Subpixel glyph rendering support.

This patch adds support for rendering subpixel glyphs (using
Freetype). In order to control this rendering see
SkPaint::setLCDRenderText in SkPaint.h.

To setup the LCD mode, see SkFontHost::SetSubpixelOrientation and
SkFontHost::SetSubpixelOrder in SkFontHost.h.

This patch also adds more fine grained control over hinting (again,
only for Freetype currently). One can now control the hinting with
SkPaint::setHinting.


git-svn-id: http://skia.googlecode.com/svn/trunk@275 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
agl@chromium.org 2009-07-21 17:41:32 +00:00
Родитель a380ae4a9a
Коммит 309485b7b5
11 изменённых файлов: 735 добавлений и 88 удалений

Просмотреть файл

@ -187,6 +187,38 @@ public:
white (table[1]) gamma tables.
*/
static void GetGammaTables(const uint8_t* tables[2]);
///////////////////////////////////////////////////////////////////////////
/** LCDs either have their color elements arranged horizontally or
vertically. When rendering subpixel glyphs we need to know which way
round they are.
Note, if you change this after startup, you'll need to flush the glyph
cache because it'll have the wrong type of masks cached.
*/
enum LCDOrientation {
kHorizontal_LCDOrientation = 0, //!< this is the default
kVertical_LCDOrientatoin = 1,
};
static void SetSubpixelOrientation(LCDOrientation orientation);
static LCDOrientation GetSubpixelOrientation();
/** LCD color elements can vary in order. For subpixel text we need to know
the order which the LCDs uses so that the color fringes are in the
correct place.
Note, if you change this after startup, you'll need to flush the glyph
cache because it'll have the wrong type of masks cached.
*/
enum LCDOrder {
kRGB_LCDOrder = 0, //!< this is the default
kBGR_LCDOrder = 1,
};
static void SetSubpixelOrder(LCDOrder order);
static LCDOrder GetSubpixelOrder();
};
#endif

Просмотреть файл

@ -28,11 +28,28 @@ struct SkMask {
kBW_Format, //!< 1bit per pixel mask (e.g. monochrome)
kA8_Format, //!< 8bits per pixel mask (e.g. antialiasing)
k3D_Format, //!< 3 8bit per pixl planes: alpha, mul, add
kLCD_Format //!< 3 bytes/pixel: r/g/b
/* The LCD formats look like this in memory:
First, there's an A8 plane which contains the average alpha value for
each pixel. Because of this, the LCD formats can be passed directly
to functions which expect an A8 and everything will just work.
After that in memory, there's a bitmap of 32-bit values which have
been RGB order corrected for the current screen (based on the
settings in SkFontHost at the time of renderering). The alpha value
for each pixel is the maximum of the three alpha values.
kHorizontalLCD_Format has an extra column of pixels on the left and right
edges. kVerticalLCD_Format has an extra row at the top and bottom.
*/
kHorizontalLCD_Format, //!< 4 bytes/pixel: a/r/g/b
kVerticalLCD_Format, //!< 4 bytes/pixel: a/r/g/b
};
enum {
kCountMaskFormats = kLCD_Format + 1
kCountMaskFormats = kVerticalLCD_Format + 1
};
uint8_t* fImage;
@ -78,6 +95,29 @@ struct SkMask {
return fImage + x - fBounds.fLeft + (y - fBounds.fTop) * fRowBytes;
}
/** Return an address into the 32-bit plane of an LCD or VerticalLCD mask
for the given position.
*/
const uint32_t* getAddrLCD(int x, int y) const {
SkASSERT(fFormat == kHorizontanLCD_Format || fFormat == kVerticalLCD_Format);
SkASSERT(fBounds.contains(x, y));
SkASSERT(fImage != NULL);
return reinterpret_cast<const uint32_t*>(fImage + SkAlign4(fRowBytes * fBounds.height())) +
x - fBounds.fLeft + (y - fBounds.fTop) * rowWordsLCD();
}
/** Return the number of 32-bit words in a row of the 32-bit plane of an
LCD or VerticalLCD mask.
*/
const unsigned rowWordsLCD() const {
SkASSERT(fFormat == kHorizontalLCD_Format || fFormat == kVerticalLCD_Format);
if (fFormat == kHorizontalLCD_Format)
return fBounds.width() + 2;
else
return fBounds.width();
}
static uint8_t* AllocImage(size_t bytes);
static void FreeImage(void* image);

Просмотреть файл

@ -69,6 +69,33 @@ public:
*/
void reset();
/** Specifies the level of hinting to be performed. These names are taken
from the Gnome/Cairo names for the same. They are translated into
Freetype concepts the same as in cairo-ft-font.c:
kNo_Hinting -> FT_LOAD_NO_HINTING
kSlight_Hinting -> FT_LOAD_TARGET_LIGHT
kNormal_Hinting -> <default, no option>
kFull_Hinting -> <same as kNormalHinting, unless we are rendering
subpixel glyphs, in which case TARGET_LCD or
TARGET_LCD_V is used>
*/
enum Hinting {
kNo_Hinting = 0,
kSlight_Hinting = 1,
kNormal_Hinting = 2, //!< this is the default
kFull_Hinting = 3,
};
Hinting getHinting() const
{
return static_cast<Hinting>(fHinting);
}
void setHinting(Hinting hintingLevel)
{
fHinting = hintingLevel;
}
/** Specifies the bit values that are stored in the paint's flags.
*/
enum Flags {
@ -79,10 +106,13 @@ public:
kStrikeThruText_Flag = 0x10, //!< mask to enable strike-thru text
kFakeBoldText_Flag = 0x20, //!< mask to enable fake-bold text
kLinearText_Flag = 0x40, //!< mask to enable linear-text
kSubpixelText_Flag = 0x80, //!< mask to enable subpixel-text
kSubpixelText_Flag = 0x80, //!< mask to enable subpixel text positioning
kDevKernText_Flag = 0x100, //!< mask to enable device kerning text
kLCDRenderText_Flag = 0x200, //!< mask to enable subpixel glyph renderering
// when adding extra flags, note that the fFlags member is specified
// with a bit-width and you'll have to expand it.
kAllFlags = 0x1FF
kAllFlags = 0x3FF
};
/** Return the paint's flags. Use the Flag enum to test flag values.
@ -143,12 +173,23 @@ public:
return SkToBool(this->getFlags() & kSubpixelText_Flag);
}
/** Helper for setFlags(), setting or clearing the kSubpixelText_Flag bit
@param subpixelText true to set the subpixelText bit in the paint's
flags, false to clear it.
/** Helper for setFlags(), setting or clearing the kSubpixelText_Flag
bit @param subpixelText true to set the subpixelText bit in the paint's flags,
false to clear it.
*/
void setSubpixelText(bool subpixelText);
bool isLCDRenderText() const
{
return SkToBool(this->getFlags() & kLCDRenderText_Flag);
}
/** Helper for setFlags(), setting or clearing the kLCDRenderText_Flag bit
@param subpixelRender true to set the subpixelRenderText bit in the paint's flags,
false to clear it.
*/
void setLCDRenderText(bool subpixelRender);
/** Helper for getFlags(), returning true if kUnderlineText_Flag bit is set
@return true if the underlineText bit is set in the paint's flags.
*/
@ -749,12 +790,13 @@ private:
SkColor fColor;
SkScalar fWidth;
SkScalar fMiterLimit;
unsigned fFlags : 9;
unsigned fFlags : 10;
unsigned fTextAlign : 2;
unsigned fCapType : 2;
unsigned fJoinType : 2;
unsigned fStyle : 2;
unsigned fTextEncoding : 2; // 3 values
unsigned fHinting : 2;
SkDrawCacheProc getDrawCacheProc() const;
SkMeasureCacheProc getMeasureCacheProc(TextBufferDirection dir,

Просмотреть файл

@ -136,34 +136,47 @@ struct SkGlyph {
}
void toMask(SkMask* mask) const;
/** Given a glyph which is has a mask format of LCD or VerticalLCD, take
the A8 plane in fImage and produce a valid LCD plane from it.
*/
void expandA8ToLCD() const;
};
class SkScalerContext {
public:
enum Hints {
kNo_Hints,
kSubpixel_Hints,
kNormal_Hints
};
enum Flags {
kFrameAndFill_Flag = 0x01,
kDevKernText_Flag = 0x02,
kGammaForBlack_Flag = 0x04, // illegal to set both Gamma flags
kGammaForWhite_Flag = 0x08 // illegal to set both Gamma flags
kGammaForWhite_Flag = 0x08, // illegal to set both Gamma flags
// together, these two flags resulting in a two bit value which matches
// up with the SkPaint::Hinting enum.
kHintingBit1 = 0x10,
kHintingBit2 = 0x20,
};
struct Rec {
uint32_t fFontID;
SkScalar fTextSize, fPreScaleX, fPreSkewX;
SkScalar fPost2x2[2][2];
SkScalar fFrameWidth, fMiterLimit;
uint8_t fHints;
bool fSubpixelPositioning;
uint8_t fMaskFormat;
uint8_t fStrokeJoin;
uint8_t fFlags;
void getMatrixFrom2x2(SkMatrix*) const;
void getLocalMatrix(SkMatrix*) const;
void getSingleMatrix(SkMatrix*) const;
SkPaint::Hinting getHinting() const {
return static_cast<SkPaint::Hinting>((fFlags >> 4) & 3);
}
void setHinting(SkPaint::Hinting hinting) {
fFlags = (fFlags & ~(kHintingBit1 | kHintingBit2)) |
(static_cast<int>(hinting) << 4);
}
};
SkScalerContext(const SkDescriptor* desc);

Просмотреть файл

@ -21,6 +21,21 @@
#include "SkUtils.h"
#include "SkXfermode.h"
#if defined(SK_BUILD_SUBPIXEL)
namespace skia_blitter_support {
// subpixel helper functions from SkBlitter_ARGB32_Subpixel.cpp
extern uint32_t BlendLCDPixelWithColor(const uint32_t alphaPixel, const uint32_t originalPixel,
const uint32_t sourcePixel);
extern uint32_t BlendLCDPixelWithOpaqueColor(const uint32_t alphaPixel, const uint32_t originalPixel,
const uint32_t sourcePixel);
extern uint32_t BlendLCDPixelWithBlack(const uint32_t alphaPixel, const uint32_t originalPixel);
}
using namespace skia_blitter_support;
#endif
//////////////////////////////////////////////////////////////////////////////////////
SkARGB32_Blitter::SkARGB32_Blitter(const SkBitmap& device, const SkPaint& paint)
: INHERITED(device) {
uint32_t color = paint.getColor();
@ -201,12 +216,47 @@ void SkARGB32_Opaque_Blitter::blitMask(const SkMask& mask,
int width = clip.width();
int height = clip.height();
uint32_t* device = fDevice.getAddr32(x, y);
const uint8_t* alpha = mask.getAddr(x, y);
uint32_t srcColor = fPMColor;
unsigned devRB = fDevice.rowBytes() - (width << 2);
unsigned maskRB = mask.fRowBytes - width;
#if defined(SK_BUILD_SUBPIXEL)
const bool lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format;
const bool verticalLCDMode = mask.fFormat == SkMask::kVerticalLCD_Format;
#else
const bool lcdMode = false, verticalLCDMode = false;
#endif
// In LCD mode the masks have either an extra couple of rows or columns on the edges.
uint32_t* device = fDevice.getAddr32(x - lcdMode, y - verticalLCDMode);
uint32_t srcColor = fPMColor;
#if defined(SK_BUILD_SUBPIXEL)
if (lcdMode || verticalLCDMode) {
const uint32_t* alpha32 = mask.getAddrLCD(clip.fLeft, clip.fTop);
if (lcdMode)
width += 2; // we have extra columns on the left and right
else
height += 2; // we have extra rows at the top and bottom
unsigned devRB = fDevice.rowBytes() - (width << 2);
unsigned alphaExtraRowWords = mask.rowWordsLCD() - width;
do {
unsigned w = width;
do {
const uint32_t alphaPixel = *alpha32++;
const uint32_t originalPixel = *device;
*device++ = BlendLCDPixelWithOpaqueColor(alphaPixel, originalPixel, srcColor);
} while (--w != 0);
device = (uint32_t*)((char*)device + devRB);
alpha32 += alphaExtraRowWords;
} while (--height != 0);
return;
}
#endif
const uint8_t* alpha = mask.getAddr(x, y);
unsigned maskRB = mask.fRowBytes - width;
unsigned devRB = fDevice.rowBytes() - (width << 2);
do {
int w = width;
do {
@ -298,18 +348,53 @@ void SkARGB32_Black_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
SkARGB32_BlitBW(fDevice, mask, clip, black);
} else {
uint32_t* device = fDevice.getAddr32(clip.fLeft, clip.fTop);
const uint8_t* alpha = mask.getAddr(clip.fLeft, clip.fTop);
#if defined(SK_BUILD_SUBPIXEL)
const bool lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format;
const bool verticalLCDMode = mask.fFormat == SkMask::kVerticalLCD_Format;
#else
const bool lcdMode = false, verticalLCDMode = false;
#endif
// In LCD mode the masks have either an extra couple of rows or columns on the edges.
uint32_t* device = fDevice.getAddr32(clip.fLeft - lcdMode, clip.fTop - verticalLCDMode);
unsigned width = clip.width();
unsigned height = clip.height();
unsigned deviceRB = fDevice.rowBytes() - (width << 2);
unsigned maskRB = mask.fRowBytes - width;
const uint32_t* alpha32;
SkASSERT((int)height > 0);
SkASSERT((int)width > 0);
SkASSERT((int)deviceRB >= 0);
SkASSERT((int)maskRB >= 0);
#if defined(SK_BUILD_SUBPIXEL)
if (lcdMode || verticalLCDMode) {
const uint32_t* alpha32 = mask.getAddrLCD(clip.fLeft, clip.fTop);
if (lcdMode)
width += 2; // we have extra columns on the left and right
else
height += 2; // we have extra rows at the top and bottom
unsigned deviceRB = fDevice.rowBytes() - (width << 2);
unsigned alphaExtraRowWords = mask.rowWordsLCD() - width;
do {
unsigned w = width;
do {
const uint32_t alphaPixel = *alpha32++;
const uint32_t originalPixel = *device;
*device++ = BlendLCDPixelWithBlack(alphaPixel, originalPixel);
} while (--w != 0);
device = (uint32_t*)((char*)device + deviceRB);
alpha32 += alphaExtraRowWords;
} while (--height != 0);
return;
}
#endif
unsigned maskRB = mask.fRowBytes - width;
unsigned deviceRB = fDevice.rowBytes() - (width << 2);
const uint8_t* alpha = mask.getAddr(clip.fLeft, clip.fTop);
do {
unsigned w = width;
do {
@ -482,4 +567,3 @@ void SkARGB32_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
}
}
}

Просмотреть файл

@ -0,0 +1,102 @@
/* libs/graphics/sgl/SkBlitter_ARGB32_Subpixel.cpp
**
** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
/* LCD blend functions:
These functions take an alpha pixel of the following form:
red, green, blue -> an alpha value for the given colour component.
alpha -> the max of the red, green and blue alpha values.
These alpha pixels result from subpixel renderering. The R/G/B values have
already been corrected for RGB/BGR element ordering.
The alpha pixel is blended with an original pixel and a source colour,
resulting in a new pixel value.
*/
#include "SkColorPriv.h"
namespace skia_blitter_support {
uint32_t BlendLCDPixelWithColor(const uint32_t alphaPixel, const uint32_t originalPixel,
const uint32_t sourcePixel) {
unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel));
unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel));
unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel));
unsigned sourceRed = SkGetPackedR32(sourcePixel);
unsigned sourceGreen = SkGetPackedG32(sourcePixel);
unsigned sourceBlue = SkGetPackedB32(sourcePixel);
unsigned sourceAlpha = SkAlpha255To256(SkGetPackedA32(sourcePixel));
alphaRed = (alphaRed * sourceAlpha) >> 8;
alphaGreen = (alphaGreen * sourceAlpha) >> 8;
alphaBlue = (alphaBlue * sourceAlpha) >> 8;
unsigned alphaAlpha = SkMax32(SkMax32(alphaRed, alphaBlue), alphaGreen);
unsigned originalRed = SkGetPackedR32(originalPixel);
unsigned originalGreen = SkGetPackedG32(originalPixel);
unsigned originalBlue = SkGetPackedB32(originalPixel);
unsigned originalAlpha = SkGetPackedA32(originalPixel);
return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha),
((sourceRed * alphaRed) >> 8) + ((originalRed * (256 - alphaRed)) >> 8),
((sourceGreen * alphaGreen) >> 8) + ((originalGreen * (256 - alphaGreen)) >> 8),
((sourceBlue * alphaBlue) >> 8) + ((originalBlue * (256 - alphaBlue)) >> 8));
}
uint32_t BlendLCDPixelWithOpaqueColor(const uint32_t alphaPixel, const uint32_t originalPixel,
const uint32_t sourcePixel) {
unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel));
unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel));
unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel));
unsigned alphaAlpha = SkGetPackedA32(alphaPixel);
unsigned sourceRed = SkGetPackedR32(sourcePixel);
unsigned sourceGreen = SkGetPackedG32(sourcePixel);
unsigned sourceBlue = SkGetPackedB32(sourcePixel);
unsigned originalRed = SkGetPackedR32(originalPixel);
unsigned originalGreen = SkGetPackedG32(originalPixel);
unsigned originalBlue = SkGetPackedB32(originalPixel);
unsigned originalAlpha = SkGetPackedA32(originalPixel);
return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha),
((sourceRed * alphaRed) >> 8) + ((originalRed * (256 - alphaRed)) >> 8),
((sourceGreen * alphaGreen) >> 8) + ((originalGreen * (256 - alphaGreen)) >> 8),
((sourceBlue * alphaBlue) >> 8) + ((originalBlue * (256 - alphaBlue)) >> 8));
}
uint32_t BlendLCDPixelWithBlack(const uint32_t alphaPixel, const uint32_t originalPixel) {
unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel));
unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel));
unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel));
unsigned alphaAlpha = SkGetPackedA32(alphaPixel);
unsigned originalRed = SkGetPackedR32(originalPixel);
unsigned originalGreen = SkGetPackedG32(originalPixel);
unsigned originalBlue = SkGetPackedB32(originalPixel);
unsigned originalAlpha = SkGetPackedA32(originalPixel);
return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha),
(originalRed * (256 - alphaRed)) >> 8,
(originalGreen * (256 - alphaGreen)) >> 8,
(originalBlue * (256 - alphaBlue)) >> 8);
}
} // namespace skia_blitter_support

90
src/core/SkFontHost.cpp Normal file
Просмотреть файл

@ -0,0 +1,90 @@
/* libs/graphics/sgl/SkFontHost.cpp
**
** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include "SkFontHost.h"
static SkFontHost::LCDOrientation gLCDOrientation = SkFontHost::kHorizontal_LCDOrientation;
static SkFontHost::LCDOrder gLCDOrder = SkFontHost::kRGB_LCDOrder;
// static
SkFontHost::LCDOrientation SkFontHost::GetSubpixelOrientation()
{
return gLCDOrientation;
}
// static
void SkFontHost::SetSubpixelOrientation(LCDOrientation orientation)
{
gLCDOrientation = orientation;
}
// static
SkFontHost::LCDOrder SkFontHost::GetSubpixelOrder()
{
return gLCDOrder;
}
// static
void SkFontHost::SetSubpixelOrder(LCDOrder order)
{
gLCDOrder = order;
}
/* libs/graphics/sgl/SkFontHost.cpp
**
** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include "SkFontHost.h"
static SkFontHost::LCDOrientation gLCDOrientation = SkFontHost::kHorizontal_LCDOrientation;
static SkFontHost::LCDOrder gLCDOrder = SkFontHost::kRGB_LCDOrder;
// static
SkFontHost::LCDOrientation SkFontHost::GetSubpixelOrientation()
{
return gLCDOrientation;
}
// static
void SkFontHost::SetSubpixelOrientation(LCDOrientation orientation)
{
gLCDOrientation = orientation;
}
// static
SkFontHost::LCDOrder SkFontHost::GetSubpixelOrder()
{
return gLCDOrder;
}
// static
void SkFontHost::SetSubpixelOrder(LCDOrder order)
{
gLCDOrder = order;
}

Просмотреть файл

@ -57,6 +57,7 @@ SkPaint::SkPaint()
fTextAlign = kLeft_Align;
fStyle = kFill_Style;
fTextEncoding = kUTF8_TextEncoding;
fHinting = kNormal_Hinting;
}
SkPaint::SkPaint(const SkPaint& src)
@ -144,6 +145,11 @@ void SkPaint::setSubpixelText(bool doSubpixel)
this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
}
void SkPaint::setLCDRenderText(bool doLCDRender)
{
this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
}
void SkPaint::setLinearText(bool doLinearText)
{
this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
@ -1118,20 +1124,17 @@ static const SkScalar multipliers[] = { SK_Scalar1/24, SK_Scalar1/32 };
static SkMask::Format computeMaskFormat(const SkPaint& paint)
{
uint32_t flags = paint.getFlags();
return (flags & SkPaint::kAntiAlias_Flag) ? SkMask::kA8_Format : SkMask::kBW_Format;
}
static SkScalerContext::Hints computeScalerHints(const SkPaint& paint)
{
uint32_t flags = paint.getFlags();
if (flags & SkPaint::kLinearText_Flag)
return SkScalerContext::kNo_Hints;
else if (flags & SkPaint::kSubpixelText_Flag)
return SkScalerContext::kSubpixel_Hints;
else
return SkScalerContext::kNormal_Hints;
if (flags & SkPaint::kLCDRenderText_Flag)
#if defined(SK_BUILD_SUBPIXEL)
return SkFontHost::GetSubpixelOrientation() == SkFontHost::kHorizontal_LCDOrientation ?
SkMask::kHorizontalLCD_Format : SkMask::kVerticalLCD_Format;
#else
return SkMask::kA8_Format;
#endif
if (flags & SkPaint::kAntiAlias_Flag)
return SkMask::kA8_Format;
return SkMask::kBW_Format;
}
void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix, Rec* rec)
@ -1194,9 +1197,10 @@ void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix
rec->fStrokeJoin = 0;
}
rec->fHints = SkToU8(computeScalerHints(paint));
rec->fSubpixelPositioning = paint.isSubpixelText();
rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
rec->fFlags = SkToU8(flags);
rec->setHinting(paint.getHinting());
}
#define MIN_SIZE_FOR_EFFECT_BUFFER 1024

Просмотреть файл

@ -16,6 +16,7 @@
*/
#include "SkScalerContext.h"
#include "SkColorPriv.h"
#include "SkDescriptor.h"
#include "SkDraw.h"
#include "SkFontHost.h"
@ -45,11 +46,18 @@ void SkGlyph::toMask(SkMask* mask) const {
}
size_t SkGlyph::computeImageSize() const {
size_t size = this->rowBytes() * fHeight;
if (fMaskFormat == SkMask::k3D_Format) {
size *= 3;
const size_t size = this->rowBytes() * fHeight;
switch (fMaskFormat) {
case SkMask::kHorizontalLCD_Format:
return SkAlign4(size) + sizeof(uint32_t) * ((fWidth + 2) * fHeight);
case SkMask::kVerticalLCD_Format:
return SkAlign4(size) + sizeof(uint32_t) * (fWidth * (fHeight + 2));
case SkMask::k3D_Format:
return 3 * size;
default:
return size;
}
return size;
}
void SkGlyph::zeroMetrics() {
@ -63,6 +71,48 @@ void SkGlyph::zeroMetrics() {
fLsbDelta = 0;
}
void SkGlyph::expandA8ToLCD() const {
SkASSERT(fMaskFormat == SkMask::kHorizontalLCD_Format ||
fMaskFormat == SkMask::kVerticalLCD_Format);
#if defined(SK_BUILD_SUBPIXEL)
uint8_t* input = reinterpret_cast<uint8_t*>(fImage);
uint32_t* output = reinterpret_cast<uint32_t*>(input + SkAlign4(rowBytes() * fHeight));
if (fMaskFormat == SkMask::kHorizontalLCD_Format) {
for (unsigned y = 0; y < fHeight; ++y) {
const uint8_t* inputRow = input;
*output++ = 0; // make the extra column on the left clear
for (unsigned x = 0; x < fWidth; ++x) {
const uint8_t alpha = *inputRow++;
*output++ = SkPackARGB32(alpha, alpha, alpha, alpha);
}
*output++ = 0;
input += rowBytes();
}
} else {
const unsigned outputRowBytes = sizeof(uint32_t) * fWidth;
memset(output, 0, outputRowBytes);
output += fWidth;
for (unsigned y = 0; y < fHeight; ++y) {
const uint8_t* inputRow = input;
for (unsigned x = 0; x < fWidth; ++x) {
const uint8_t alpha = *inputRow++;
*output++ = SkPackARGB32(alpha, alpha, alpha, alpha);
}
input += rowBytes();
}
memset(output, 0, outputRowBytes);
output += fWidth;
}
#else
#endif
}
///////////////////////////////////////////////////////////////////////////////
#ifdef SK_DEBUG
@ -329,6 +379,9 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
const bool lcdMode = fRec.fMaskFormat == SkMask::kHorizontalLCD_Format ||
fRec.fMaskFormat == SkMask::kVerticalLCD_Format;
if (fRasterizer) {
SkMask mask;
@ -349,7 +402,7 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
SkPaint paint;
SkDraw draw;
if (SkMask::kA8_Format == fRec.fMaskFormat) {
if (SkMask::kA8_Format == fRec.fMaskFormat || lcdMode) {
config = SkBitmap::kA8_Config;
paint.setAntiAlias(true);
} else {
@ -372,6 +425,9 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
draw.fBounder = NULL;
draw.drawPath(devPath, paint);
}
if (lcdMode)
glyph->expandA8ToLCD();
} else {
this->getGlyphContext(*glyph)->generateImage(*glyph);
}

Просмотреть файл

@ -15,6 +15,7 @@
** limitations under the License.
*/
#include "SkColorPriv.h"
#include "SkScalerContext.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
@ -32,6 +33,11 @@
#include FT_OUTLINE_H
#include FT_SIZES_H
#include FT_TRUETYPE_TABLES_H
#if defined(SK_BUILD_SUBPIXEL)
#include FT_LCD_FILTER_H
#endif
#ifdef FT_ADVANCES_H
#include FT_ADVANCES_H
#endif
@ -43,6 +49,7 @@
#include <freetype/ftsizes.h>
#include <freetype/tttables.h>
#include <freetype/ftadvanc.h>
#include <freetype/ftlcdfil.h>
#endif
//#define ENABLE_GLYPH_SPEW // for tracing calls
@ -69,6 +76,21 @@ static SkFaceRec* gFaceRecHead;
/////////////////////////////////////////////////////////////////////////
static bool
InitFreetype() {
FT_Error err = FT_Init_FreeType(&gFTLibrary);
if (err)
return false;
#if defined(SK_BUILD_SUBPIXEL)
// Setup LCD filtering. This reduces colour fringes for LCD rendered
// glyphs.
err = FT_Library_SetLcdFilter(gFTLibrary, FT_LCD_FILTER_DEFAULT);
#endif
return true;
}
class SkScalerContext_FreeType : public SkScalerContext {
public:
SkScalerContext_FreeType(const SkDescriptor* desc);
@ -247,9 +269,8 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
FT_Error err;
if (gFTCount == 0) {
err = FT_Init_FreeType(&gFTLibrary);
// SkDEBUGF(("FT_Init_FreeType returned %d\n", err));
SkASSERT(err == 0);
const bool success = InitFreetype();
SkASSERT(success);
}
++gFTCount;
@ -275,7 +296,7 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX),
SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]),
SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]),
fRec.fHints, fRec.fMaskFormat, keyString.c_str());
fRec.getHinting(), fRec.fMaskFormat, keyString.c_str());
#endif
// now compute our scale factors
@ -305,42 +326,27 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
// compute the flags we send to Load_Glyph
{
uint32_t flags = FT_LOAD_DEFAULT;
uint32_t render_flags = FT_LOAD_TARGET_NORMAL;
FT_Int32 hintingFlags = FT_LOAD_DEFAULT;
// we force autohinting at the moment
switch (fRec.fHints) {
case kNo_Hints:
flags |= FT_LOAD_NO_HINTING;
switch (fRec.getHinting()) {
case SkPaint::kNo_Hinting:
hintingFlags = FT_LOAD_NO_HINTING;
break;
case kSubpixel_Hints:
flags |= FT_LOAD_FORCE_AUTOHINT;
render_flags = FT_LOAD_TARGET_LIGHT;
case SkPaint::kSlight_Hinting:
hintingFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT
break;
case kNormal_Hints:
#ifdef ANDROID
// The following line disables the font's hinting tables. For
// Chromium we want font hinting on so that we can generate
// baselines that look at little like Firefox. It's expected that
// Mike Reed will rework this code sometime soon so we don't wish
// to make more extensive changes.
flags |= FT_LOAD_FORCE_AUTOHINT;
/* Switch to light hinting (vertical only) to address some chars
that behaved poorly with NORMAL. In the future we could consider
making this choice exposed at runtime to the caller.
*/
render_flags = FT_LOAD_TARGET_LIGHT;
#endif
case SkPaint::kNormal_Hinting:
hintingFlags |= FT_LOAD_TARGET_NORMAL;
break;
case SkPaint::kFull_Hinting:
if (SkMask::kHorizontalLCD_Format == fRec.fMaskFormat)
hintingFlags = FT_LOAD_TARGET_LCD;
else if (SkMask::kVerticalLCD_Format == fRec.fMaskFormat)
hintingFlags = FT_LOAD_TARGET_LCD_V;
break;
}
if (SkMask::kBW_Format == fRec.fMaskFormat)
render_flags = FT_LOAD_TARGET_MONO;
else if (SkMask::kLCD_Format == fRec.fMaskFormat)
render_flags = FT_LOAD_TARGET_LCD;
fLoadGlyphFlags = flags | render_flags;
fLoadGlyphFlags = hintingFlags;
}
// now create the FT_Size
@ -433,10 +439,12 @@ uint16_t SkScalerContext_FreeType::generateCharToGlyph(SkUnichar uni) {
static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
switch (format) {
case SkMask::kHorizontalLCD_Format:
case SkMask::kVerticalLCD_Format:
SkASSERT(!"An LCD format should never be passed here");
return FT_PIXEL_MODE_GRAY;
case SkMask::kBW_Format:
return FT_PIXEL_MODE_MONO;
case SkMask::kLCD_Format:
return FT_PIXEL_MODE_LCD;
case SkMask::kA8_Format:
default:
return FT_PIXEL_MODE_GRAY;
@ -503,7 +511,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
if (kSubpixel_Hints == fRec.fHints) {
if (fRec.fSubpixelPositioning) {
int dx = glyph->getSubXFixed() >> 10;
int dy = glyph->getSubYFixed() >> 10;
// negate dy since freetype-y-goes-up and skia-y-goes-down
@ -536,7 +544,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
goto ERROR;
}
if (kNormal_Hints == fRec.fHints) {
if (!fRec.fSubpixelPositioning) {
glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x);
glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y);
if (fRec.fFlags & kDevKernText_Flag) {
@ -554,6 +562,16 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
#endif
}
#if defined(SK_BUILD_SUBPIXEL)
namespace skia_freetype_support {
// extern functions from SkFontHost_FreeType_Subpixel
extern void CopyFreetypeBitmapToLCDMask(const SkGlyph& dest, const FT_Bitmap& source);
extern void CopyFreetypeBitmapToVerticalLCDMask(const SkGlyph& dest, const FT_Bitmap& source);
}
using namespace skia_freetype_support;
#endif
void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
SkAutoMutexAcquire ac(gFTMutex);
@ -572,6 +590,9 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
return;
}
const bool lcdRenderMode = fRec.fMaskFormat == SkMask::kHorizontalLCD_Format ||
fRec.fMaskFormat == SkMask::kVerticalLCD_Format;
switch ( fFace->glyph->format ) {
case FT_GLYPH_FORMAT_OUTLINE: {
FT_Outline* outline = &fFace->glyph->outline;
@ -579,7 +600,7 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
FT_Bitmap target;
int dx = 0, dy = 0;
if (kSubpixel_Hints == fRec.fHints) {
if (fRec.fSubpixelPositioning) {
dx = glyph.getSubXFixed() >> 10;
dy = glyph.getSubYFixed() >> 10;
// negate dy since freetype-y-goes-up and skia-y-goes-down
@ -597,6 +618,23 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
dy - ((bbox.yMin + dy) & ~63));
#if defined(SK_BUILD_SUBPIXEL)
if (lcdRenderMode) {
// FT_Outline_Get_Bitmap cannot render LCD glyphs. In this case
// we have to call FT_Render_Glyph and memcpy the image out.
const bool isVertical = fRec.fMaskFormat == SkMask::kVerticalLCD_Format;
FT_Render_Mode mode = isVertical ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD;
FT_Render_Glyph(fFace->glyph, mode);
if (isVertical)
CopyFreetypeBitmapToVerticalLCDMask(glyph, fFace->glyph->bitmap);
else
CopyFreetypeBitmapToLCDMask(glyph, fFace->glyph->bitmap);
break;
}
#endif
target.width = glyph.fWidth;
target.rows = glyph.fHeight;
target.pitch = glyph.rowBytes();
@ -652,6 +690,10 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
dst += glyph.rowBytes();
}
}
if (lcdRenderMode)
glyph.expandA8ToLCD();
} break;
default:
@ -858,7 +900,7 @@ SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
*/
SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name) {
FT_Library library;
if (FT_Init_FreeType(&library)) {
if (!InitFreetype()) {
name->set(NULL);
return SkTypeface::kNormal;
}
@ -905,4 +947,3 @@ SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name) {
FT_Done_FreeType(library);
return (SkTypeface::Style)style;
}

Просмотреть файл

@ -0,0 +1,143 @@
/* libs/graphics/ports/SkFontHost_FreeType_Subpixel.cpp
**
** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
/* This file contains functions for converting Freetype's subpixel output
formats into the format used by SkMask for subpixel masks. See the comments
in SkMask.h for details on the format.
*/
#include "SkColorPriv.h"
#include "SkFontHost.h"
#include "SkMask.h"
#include "SkScalerContext.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#if 0
// Also include the files by name for build tools which require this.
#include <freetype/freetype.h>
#endif
namespace skia_freetype_support {
void CopyFreetypeBitmapToLCDMask(const SkGlyph& dest, const FT_Bitmap& source)
{
// |source| has three alpha values per pixel and has an extra column at the
// left and right edges.
// ----- <--- a single pixel in the output
// source .oOo.. |.oO|o..
// .OOO.. -----
// .oOo.. --> .OO O..
// .oO o..
uint8_t* output = reinterpret_cast<uint8_t*>(dest.fImage);
const unsigned outputPitch = SkAlign4((source.width / 3) - 2);
const uint8_t* input = source.buffer;
// First we calculate the A8 mask.
for (int y = 0; y < source.rows; ++y) {
const uint8_t* inputRow = input;
uint8_t* outputRow = output;
inputRow += 3; // skip the extra column on the left
for (int x = 3; x < source.width - 3; x += 3) {
const uint8_t averageAlpha = (static_cast<unsigned>(inputRow[0]) + inputRow[1] + inputRow[2] + 1) / 3;
*outputRow++ = averageAlpha;
inputRow += 3;
}
input += source.pitch;
output += outputPitch;
}
// Align the 32-bit plane on a word boundary
uint32_t* output32 = (uint32_t*) SkAlign4((uintptr_t) output);
// Now we build the 32-bit alpha mask and RGB order correct.
const int isBGR = SkFontHost::GetSubpixelOrder() == SkFontHost::kBGR_LCDOrder;
input = source.buffer;
for (int y = 0; y < source.rows; ++y) {
const uint8_t* inputRow = input;
for (int x = 0; x < source.width; x += 3) {
const uint8_t alphaRed = isBGR ? inputRow[2] : inputRow[0];
const uint8_t alphaGreen = inputRow[1];
const uint8_t alphaBlue = isBGR ? inputRow[0] : inputRow[2];
const uint8_t maxAlpha = SkMax32(alphaRed, SkMax32(alphaGreen, alphaBlue));
*output32++ = SkPackARGB32(maxAlpha, alphaRed, alphaGreen, alphaBlue);
inputRow += 3;
}
input += source.pitch;
}
}
void CopyFreetypeBitmapToVerticalLCDMask(const SkGlyph& dest, const FT_Bitmap& source)
{
// |source| has three times as many rows as normal, and an extra triple on the
// top and bottom.
// source .oOo.. |.|oOo..
// .OOO.. --> |.|OOO..
// .oOo.. |.|oOo..
// ^
// |-------- A single pixel in the output
uint8_t* output = reinterpret_cast<uint8_t*>(dest.fImage);
const unsigned outputPitch = SkAlign4(source.rows);
const uint8_t* input = source.buffer;
// First we calculate the A8 mask.
input += 3 * source.pitch; // skip the extra at the beginning
for (int y = 3; y < source.rows - 3; y += 3) {
const uint8_t* inputRow = input;
uint8_t* outputRow = output;
for (int x = 0; x < source.width; ++x) {
const uint8_t averageAlpha = (static_cast<unsigned>(*inputRow) + inputRow[source.pitch] + inputRow[source.pitch * 2] + 1) / 3;
*outputRow++ = averageAlpha;
inputRow++;
}
input += source.pitch * 3;
output += outputPitch;
}
// Align the 32-bit plane on a word boundary
uint32_t* output32 = (uint32_t*) SkAlign4((uintptr_t) output);
// Now we build the 32-bit alpha mask and RGB order correct.
const int isBGR = SkFontHost::GetSubpixelOrder() == SkFontHost::kBGR_LCDOrder;
input = source.buffer;
for (int y = 0; y < source.rows; y += 3) {
const uint8_t* inputRow = input;
for (int x = 0; x < source.width; ++x) {
const uint8_t alphaRed = isBGR ? inputRow[source.pitch * 2] : inputRow[0];
const uint8_t alphaGreen = inputRow[source.pitch];
const uint8_t alphaBlue = isBGR ? inputRow[0] : inputRow[2 * source.pitch];
const uint8_t maxAlpha = SkMax32(alphaRed, SkMax32(alphaGreen, alphaBlue));
*output32++ = SkPackARGB32(maxAlpha, alphaRed, alphaGreen, alphaBlue);
inputRow++;
}
input += source.pitch * 3;
}
}
} // namespace skia_freetype_support