diff options
author | agl@chromium.org <agl@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2009-07-21 17:41:32 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2009-07-21 17:41:32 +0000 |
commit | 309485b7b51f4cae4c0361ab4da00fe9cc89515c (patch) | |
tree | a6c987c01d54a52545b210552b8a52bd834477f0 /src | |
parent | a380ae4a9ac209f5676c06aeaceacc1b08817eda (diff) |
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
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkBlitter_ARGB32.cpp | 102 | ||||
-rw-r--r-- | src/core/SkBlitter_ARGB32_Subpixel.cpp | 102 | ||||
-rw-r--r-- | src/core/SkFontHost.cpp | 90 | ||||
-rw-r--r-- | src/core/SkPaint.cpp | 32 | ||||
-rw-r--r-- | src/core/SkScalerContext.cpp | 66 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType.cpp | 123 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType_Subpixel.cpp | 143 |
7 files changed, 589 insertions, 69 deletions
diff --git a/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp index ed2fc0ec9a..ecafc3355b 100644 --- a/src/core/SkBlitter_ARGB32.cpp +++ b/src/core/SkBlitter_ARGB32.cpp @@ -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); +#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; - unsigned devRB = fDevice.rowBytes() - (width << 2); - unsigned maskRB = mask.fRowBytes - width; +#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[], } } } - diff --git a/src/core/SkBlitter_ARGB32_Subpixel.cpp b/src/core/SkBlitter_ARGB32_Subpixel.cpp new file mode 100644 index 0000000000..668ccf5c5c --- /dev/null +++ b/src/core/SkBlitter_ARGB32_Subpixel.cpp @@ -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 diff --git a/src/core/SkFontHost.cpp b/src/core/SkFontHost.cpp new file mode 100644 index 0000000000..fe8e6ea89f --- /dev/null +++ b/src/core/SkFontHost.cpp @@ -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; +} diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 8b2a21b5bd..95448c959a 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -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 diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp index 7d66c2a5e6..57a3b3548d 100644 --- a/src/core/SkScalerContext.cpp +++ b/src/core/SkScalerContext.cpp @@ -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); } diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index b30aba53fb..5367439705 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -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; - - // we force autohinting at the moment + FT_Int32 hintingFlags = FT_LOAD_DEFAULT; - 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; } - diff --git a/src/ports/SkFontHost_FreeType_Subpixel.cpp b/src/ports/SkFontHost_FreeType_Subpixel.cpp new file mode 100644 index 0000000000..2087ba8023 --- /dev/null +++ b/src/ports/SkFontHost_FreeType_Subpixel.cpp @@ -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 |