diff options
author | george@mozilla.com <george@mozilla.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-08-23 00:39:08 +0000 |
---|---|---|
committer | george@mozilla.com <george@mozilla.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-08-23 00:39:08 +0000 |
commit | c59b5dac9081e3613ed80d8b6d498e093c03eb87 (patch) | |
tree | 8fdc4dc1a81494510be4aea136691b45e8bbbde6 | |
parent | 55ed83db1b9d3d1187f59c9db9f225b0f1d56327 (diff) |
Split out SkFontHost_FreeType into common files
Review URL: https://codereview.appspot.com/6442092
git-svn-id: http://skia.googlecode.com/svn/trunk@5246 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | gyp/ports.gyp | 3 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType.cpp | 361 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType_common.cpp | 349 | ||||
-rw-r--r-- | src/ports/SkFontHost_FreeType_common.h | 44 |
4 files changed, 410 insertions, 347 deletions
diff --git a/gyp/ports.gyp b/gyp/ports.gyp index d9a7536693..c286603bd8 100644 --- a/gyp/ports.gyp +++ b/gyp/ports.gyp @@ -40,6 +40,7 @@ 'sources': [ '../src/ports/SkThread_pthread.cpp', '../src/ports/SkFontHost_FreeType.cpp', + '../src/ports/SkFontHost_FreeType_common.cpp', '../src/ports/SkFontHost_linux.cpp', ], }], @@ -52,6 +53,7 @@ '../src/ports/SkFontHost_mac_coretext.cpp', '../src/utils/mac/SkStream_mac.cpp', # '../src/ports/SkFontHost_FreeType.cpp', +# '../src/ports/SkFontHost_FreeType_common.cpp', # '../src/ports/SkFontHost_freetype_mac.cpp', '../src/ports/SkThread_pthread.cpp', ], @@ -106,6 +108,7 @@ '../src/ports/SkThread_pthread.cpp', '../src/ports/SkFontHost_android.cpp', '../src/ports/SkFontHost_FreeType.cpp', + '../src/ports/SkFontHost_FreeType_common.cpp', '../src/ports/FontHostConfiguration_android.cpp', #TODO: include the ports/SkImageRef_ashmem.cpp for non-NDK builds ], diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index 497a259a5b..550fae01b2 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -6,7 +6,6 @@ * found in the LICENSE file. */ - #include "SkBitmap.h" #include "SkCanvas.h" #include "SkColorPriv.h" @@ -14,6 +13,7 @@ #include "SkFDot6.h" #include "SkFloatingPoint.h" #include "SkFontHost.h" +#include "SkFontHost_FreeType_common.h" #include "SkGlyph.h" #include "SkMask.h" #include "SkMaskGamma.h" @@ -59,16 +59,6 @@ //#define SK_GAMMA_APPLY_TO_A8 -#ifdef SK_DEBUG - #define SkASSERT_CONTINUE(pred) \ - do { \ - if (!(pred)) \ - SkDebugf("file %s:%d: assert failed '" #pred "'\n", __FILE__, __LINE__); \ - } while (false) -#else - #define SkASSERT_CONTINUE(pred) -#endif - using namespace skia_advanced_typeface_metrics_utils; static bool isLCD(const SkScalerContext::Rec& rec) { @@ -95,15 +85,6 @@ static int gLCDExtra; // number of extra pixels for filtering. ///////////////////////////////////////////////////////////////////////// -// See http://freetype.sourceforge.net/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden -// This value was chosen by eyeballing the result in Firefox and trying to match it. -static const FT_Pos kBitmapEmboldenStrength = 1 << 6; - -// convert from Skia's fixed (16.16) to FreeType's fixed (26.6) representation -static inline int FixedToDot6(SkFixed x) { return x >> 10; } -// convert from FreeType's fixed (26.6) to Skia's fixed (16.16) representation -static inline SkFixed Dot6ToFixed(int x) { return x << 10; } - static bool InitFreetype() { FT_Error err = FT_Init_FreeType(&gFTLibrary); @@ -128,7 +109,7 @@ InitFreetype() { return true; } -class SkScalerContext_FreeType : public SkScalerContext { +class SkScalerContext_FreeType : public SkScalerContext_FreeType_Base { public: SkScalerContext_FreeType(const SkDescriptor* desc); virtual ~SkScalerContext_FreeType(); @@ -161,7 +142,6 @@ private: bool fLCDIsVert; FT_Error setupSize(); - void emboldenOutline(FT_Outline* outline); void getBBoxForCurrentGlyph(SkGlyph* glyph, FT_BBox* bbox, bool snapToPixelBoundary = false); void updateGlyphIfLCD(SkGlyph* glyph); @@ -684,7 +664,7 @@ uint32_t SkFontHost::GetUnitsPerEm(SkFontID fontID) { #endif SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) - : SkScalerContext(desc) { + : SkScalerContext_FreeType_Base(desc) { SkAutoMutexAcquire ac(gFTMutex); if (gFTCount == 0) { @@ -875,13 +855,6 @@ FT_Error SkScalerContext_FreeType::setupSize() { return err; } -void SkScalerContext_FreeType::emboldenOutline(FT_Outline* outline) { - FT_Pos strength; - strength = FT_MulFix(fFace->units_per_EM, fFace->size->metrics.y_scale) - / 24; - FT_Outline_Embolden(outline, strength); -} - unsigned SkScalerContext_FreeType::generateGlyphCount() { return fFace->num_glyphs; } @@ -905,16 +878,6 @@ SkUnichar SkScalerContext_FreeType::generateGlyphToChar(uint16_t glyph) { return 0; } -static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) { - switch (format) { - case SkMask::kBW_Format: - return FT_PIXEL_MODE_MONO; - case SkMask::kA8_Format: - default: - return FT_PIXEL_MODE_GRAY; - } -} - void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) { #ifdef FT_ADVANCES_H /* unhinted and light hinted text have linearly scaled advances @@ -955,8 +918,8 @@ void SkScalerContext_FreeType::getBBoxForCurrentGlyph(SkGlyph* glyph, FT_Outline_Get_CBox(&fFace->glyph->outline, bbox); if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { - int dx = FixedToDot6(glyph->getSubXFixed()); - int dy = FixedToDot6(glyph->getSubYFixed()); + int dx = SkFixedToFDot6(glyph->getSubXFixed()); + int dy = SkFixedToFDot6(glyph->getSubYFixed()); // negate dy since freetype-y-goes-up and skia-y-goes-down bbox->xMin += dx; bbox->yMin -= dy; @@ -1030,7 +993,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { } if (fRec.fFlags & kEmbolden_Flag) { - emboldenOutline(&fFace->glyph->outline); + emboldenOutline(fFace, &fFace->glyph->outline); } getBBoxForCurrentGlyph(glyph, &bbox, true); @@ -1041,8 +1004,8 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { glyph->fLeft = SkToS16(bbox.xMin >> 6); if ((fRec.fFlags & SkScalerContext::kVertical_Flag)) { - vLeft = Dot6ToFixed(bbox.xMin); - vTop = Dot6ToFixed(bbox.yMax); + vLeft = SkFDot6ToFixed(bbox.xMin); + vTop = SkFDot6ToFixed(bbox.yMax); } updateGlyphIfLCD(glyph); @@ -1102,7 +1065,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { } if (fRec.fFlags & kEmbolden_Flag) { - emboldenOutline(&fFace->glyph->outline); + emboldenOutline(fFace, &fFace->glyph->outline); } } @@ -1112,12 +1075,12 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { // compute the vertical gap above and below the glyph if the glyph were // centered within the linearVertAdvance - SkFixed vGap = (fFace->glyph->linearVertAdvance - Dot6ToFixed(bbox.yMax - bbox.yMin)) / 2; + SkFixed vGap = (fFace->glyph->linearVertAdvance - SkFDot6ToFixed(bbox.yMax - bbox.yMin)) / 2; // the origin point of the glyph when rendered vertically FT_Vector vOrigin; vOrigin.x = fFace->glyph->linearHoriAdvance / 2; - vOrigin.y = vGap + Dot6ToFixed(bbox.yMax); + vOrigin.y = vGap + SkFDot6ToFixed(bbox.yMax); // transform the vertical origin based on the matrix of the actual glyph FT_Vector_Transform(&vOrigin, &fMatrix22); @@ -1141,97 +1104,6 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { #endif } -/////////////////////////////////////////////////////////////////////////////// - -static uint16_t packTriple(unsigned r, unsigned g, unsigned b) { - return SkPackRGB16(r >> 3, g >> 2, b >> 3); -} - -static uint16_t grayToRGB16(U8CPU gray) { - SkASSERT(gray <= 255); - return SkPackRGB16(gray >> 3, gray >> 2, gray >> 3); -} - -static int bittst(const uint8_t data[], int bitOffset) { - SkASSERT(bitOffset >= 0); - int lowBit = data[bitOffset >> 3] >> (~bitOffset & 7); - return lowBit & 1; -} - -template<bool APPLY_PREBLEND> -static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap, - int lcdIsBGR, bool lcdIsVert, const uint8_t* tableR, - const uint8_t* tableG, const uint8_t* tableB) { - if (lcdIsVert) { - SkASSERT(3 * glyph.fHeight == bitmap.rows); - } else { - SkASSERT(glyph.fHeight == bitmap.rows); - } - - uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage); - const size_t dstRB = glyph.rowBytes(); - const int width = glyph.fWidth; - const uint8_t* src = bitmap.buffer; - - switch (bitmap.pixel_mode) { - case FT_PIXEL_MODE_MONO: { - for (int y = 0; y < glyph.fHeight; ++y) { - for (int x = 0; x < width; ++x) { - dst[x] = -bittst(src, x); - } - dst = (uint16_t*)((char*)dst + dstRB); - src += bitmap.pitch; - } - } break; - case FT_PIXEL_MODE_GRAY: { - for (int y = 0; y < glyph.fHeight; ++y) { - for (int x = 0; x < width; ++x) { - dst[x] = grayToRGB16(src[x]); - } - dst = (uint16_t*)((char*)dst + dstRB); - src += bitmap.pitch; - } - } break; - default: { - SkASSERT(lcdIsVert || (glyph.fWidth * 3 == bitmap.width)); - for (int y = 0; y < glyph.fHeight; y++) { - if (lcdIsVert) { // vertical stripes - const uint8_t* srcR = src; - const uint8_t* srcG = srcR + bitmap.pitch; - const uint8_t* srcB = srcG + bitmap.pitch; - if (lcdIsBGR) { - SkTSwap(srcR, srcB); - } - for (int x = 0; x < width; x++) { - dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR), - sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG), - sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB)); - } - src += 3 * bitmap.pitch; - } else { // horizontal stripes - const uint8_t* triple = src; - if (lcdIsBGR) { - for (int x = 0; x < width; x++) { - dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR), - sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), - sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB)); - triple += 3; - } - } else { - for (int x = 0; x < width; x++) { - dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR), - sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), - sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB)); - triple += 3; - } - } - src += bitmap.pitch; - } - dst = (uint16_t*)((char*)dst + dstRB); - } - } break; - } -} void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) { SkAutoMutexAcquire ac(gFTMutex); @@ -1251,192 +1123,9 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph, SkMaskGamma:: return; } - //Must be careful not to use these if maskPreBlend == NULL - const uint8_t* tableR = NULL; - const uint8_t* tableG = NULL; - const uint8_t* tableB = NULL; - if (maskPreBlend) { - tableR = maskPreBlend->fR; - tableG = maskPreBlend->fG; - tableB = maskPreBlend->fB; - } - - const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag); - const bool doVert = fLCDIsVert; - - switch ( fFace->glyph->format ) { - case FT_GLYPH_FORMAT_OUTLINE: { - FT_Outline* outline = &fFace->glyph->outline; - FT_BBox bbox; - FT_Bitmap target; - - if (fRec.fFlags & kEmbolden_Flag) { - emboldenOutline(outline); - } - - int dx = 0, dy = 0; - if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { - dx = FixedToDot6(glyph.getSubXFixed()); - dy = FixedToDot6(glyph.getSubYFixed()); - // negate dy since freetype-y-goes-up and skia-y-goes-down - dy = -dy; - } - FT_Outline_Get_CBox(outline, &bbox); - /* - what we really want to do for subpixel is - offset(dx, dy) - compute_bounds - offset(bbox & !63) - but that is two calls to offset, so we do the following, which - achieves the same thing with only one offset call. - */ - FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63), - dy - ((bbox.yMin + dy) & ~63)); - - if (SkMask::kLCD16_Format == glyph.fMaskFormat) { - FT_Render_Glyph(fFace->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD); - if (maskPreBlend) { - copyFT2LCD16<true>(glyph, fFace->glyph->bitmap, doBGR, doVert, - tableR, tableG, tableB); - } else { - copyFT2LCD16<false>(glyph, fFace->glyph->bitmap, doBGR, doVert, - tableR, tableG, tableB); - } - } else { - target.width = glyph.fWidth; - target.rows = glyph.fHeight; - target.pitch = glyph.rowBytes(); - target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); - target.pixel_mode = compute_pixel_mode( - (SkMask::Format)fRec.fMaskFormat); - target.num_grays = 256; - - memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); - FT_Outline_Get_Bitmap(gFTLibrary, outline, &target); - } - } break; - - case FT_GLYPH_FORMAT_BITMAP: { - if (fRec.fFlags & kEmbolden_Flag) { - FT_GlyphSlot_Own_Bitmap(fFace->glyph); - FT_Bitmap_Embolden(gFTLibrary, &fFace->glyph->bitmap, kBitmapEmboldenStrength, 0); - } - SkASSERT_CONTINUE(glyph.fWidth == fFace->glyph->bitmap.width); - SkASSERT_CONTINUE(glyph.fHeight == fFace->glyph->bitmap.rows); - SkASSERT_CONTINUE(glyph.fTop == -fFace->glyph->bitmap_top); - SkASSERT_CONTINUE(glyph.fLeft == fFace->glyph->bitmap_left); - - const uint8_t* src = (const uint8_t*)fFace->glyph->bitmap.buffer; - uint8_t* dst = (uint8_t*)glyph.fImage; - - if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY || - (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && - glyph.fMaskFormat == SkMask::kBW_Format)) { - unsigned srcRowBytes = fFace->glyph->bitmap.pitch; - unsigned dstRowBytes = glyph.rowBytes(); - unsigned minRowBytes = SkMin32(srcRowBytes, dstRowBytes); - unsigned extraRowBytes = dstRowBytes - minRowBytes; - - for (int y = fFace->glyph->bitmap.rows - 1; y >= 0; --y) { - memcpy(dst, src, minRowBytes); - memset(dst + minRowBytes, 0, extraRowBytes); - src += srcRowBytes; - dst += dstRowBytes; - } - } else if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && - glyph.fMaskFormat == SkMask::kA8_Format) { - for (int y = 0; y < fFace->glyph->bitmap.rows; ++y) { - uint8_t byte = 0; - int bits = 0; - const uint8_t* src_row = src; - uint8_t* dst_row = dst; - - for (int x = 0; x < fFace->glyph->bitmap.width; ++x) { - if (!bits) { - byte = *src_row++; - bits = 8; - } - - *dst_row++ = byte & 0x80 ? 0xff : 0; - bits--; - byte <<= 1; - } - - src += fFace->glyph->bitmap.pitch; - dst += glyph.rowBytes(); - } - } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) { - if (maskPreBlend) { - copyFT2LCD16<true>(glyph, fFace->glyph->bitmap, doBGR, doVert, - tableR, tableG, tableB); - } else { - copyFT2LCD16<false>(glyph, fFace->glyph->bitmap, doBGR, doVert, - tableR, tableG, tableB); - } - } else { - SkDEBUGFAIL("unknown glyph bitmap transform needed"); - } - } break; - - default: - SkDEBUGFAIL("unknown glyph format"); - goto ERROR; - } - -// We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum, -// it is optional -#if defined(SK_GAMMA_APPLY_TO_A8) - if (SkMask::kA8_Format == glyph.fMaskFormat && maskPreBlend) { - uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage; - unsigned rowBytes = glyph.rowBytes(); - - for (int y = glyph.fHeight - 1; y >= 0; --y) { - for (int x = glyph.fWidth - 1; x >= 0; --x) { - dst[x] = tableG[dst[x]]; - } - dst += rowBytes; - } - } -#endif -} - -/////////////////////////////////////////////////////////////////////////////// - -#define ft2sk(x) SkFixedToScalar(Dot6ToFixed(x)) - -#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 2 - #define CONST_PARAM const -#else // older freetype doesn't use const here - #define CONST_PARAM -#endif - -static int move_proc(CONST_PARAM FT_Vector* pt, void* ctx) { - SkPath* path = (SkPath*)ctx; - path->close(); // to close the previous contour (if any) - path->moveTo(ft2sk(pt->x), -ft2sk(pt->y)); - return 0; -} - -static int line_proc(CONST_PARAM FT_Vector* pt, void* ctx) { - SkPath* path = (SkPath*)ctx; - path->lineTo(ft2sk(pt->x), -ft2sk(pt->y)); - return 0; -} - -static int quad_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1, - void* ctx) { - SkPath* path = (SkPath*)ctx; - path->quadTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), -ft2sk(pt1->y)); - return 0; + generateGlyphImage(fFace, glyph, maskPreBlend); } -static int cubic_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1, - CONST_PARAM FT_Vector* pt2, void* ctx) { - SkPath* path = (SkPath*)ctx; - path->cubicTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), - -ft2sk(pt1->y), ft2sk(pt2->x), -ft2sk(pt2->y)); - return 0; -} void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph, SkPath* path) { @@ -1462,29 +1151,7 @@ void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph, return; } - if (fRec.fFlags & kEmbolden_Flag) { - emboldenOutline(&fFace->glyph->outline); - } - - FT_Outline_Funcs funcs; - - funcs.move_to = move_proc; - funcs.line_to = line_proc; - funcs.conic_to = quad_proc; - funcs.cubic_to = cubic_proc; - funcs.shift = 0; - funcs.delta = 0; - - err = FT_Outline_Decompose(&fFace->glyph->outline, &funcs, path); - - if (err != 0) { - SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n", - glyph.getGlyphID(fBaseGlyphCount), flags, err)); - path->reset(); - return; - } - - path->close(); + generateGlyphPath(fFace, glyph, path); } void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx, @@ -1545,7 +1212,7 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx, FT_BBox bbox; FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags); if (fRec.fFlags & kEmbolden_Flag) { - emboldenOutline(&fFace->glyph->outline); + emboldenOutline(fFace, &fFace->glyph->outline); } FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); x_height = SkFixedToScalar(SkFDot6ToFixed(bbox.yMax)); diff --git a/src/ports/SkFontHost_FreeType_common.cpp b/src/ports/SkFontHost_FreeType_common.cpp new file mode 100644 index 0000000000..91f172b440 --- /dev/null +++ b/src/ports/SkFontHost_FreeType_common.cpp @@ -0,0 +1,349 @@ +/* + * Copyright 2006-2012 The Android Open Source Project + * Copyright 2012 Mozilla Foundation + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkColorPriv.h" +#include "SkFDot6.h" +#include "SkFontHost_FreeType_common.h" +#include "SkPath.h" + +#include <ft2build.h> +#include FT_OUTLINE_H +#include FT_BITMAP_H +// In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file. +#include FT_SYNTHESIS_H + +static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) { + switch (format) { + case SkMask::kBW_Format: + return FT_PIXEL_MODE_MONO; + case SkMask::kA8_Format: + default: + return FT_PIXEL_MODE_GRAY; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint16_t packTriple(unsigned r, unsigned g, unsigned b) { + return SkPackRGB16(r >> 3, g >> 2, b >> 3); +} + +static uint16_t grayToRGB16(U8CPU gray) { + SkASSERT(gray <= 255); + return SkPackRGB16(gray >> 3, gray >> 2, gray >> 3); +} + +static int bittst(const uint8_t data[], int bitOffset) { + SkASSERT(bitOffset >= 0); + int lowBit = data[bitOffset >> 3] >> (~bitOffset & 7); + return lowBit & 1; +} + +template<bool APPLY_PREBLEND> +static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap, + int lcdIsBGR, bool lcdIsVert, const uint8_t* tableR, + const uint8_t* tableG, const uint8_t* tableB) { + if (lcdIsVert) { + SkASSERT(3 * glyph.fHeight == bitmap.rows); + } else { + SkASSERT(glyph.fHeight == bitmap.rows); + } + + uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage); + const size_t dstRB = glyph.rowBytes(); + const int width = glyph.fWidth; + const uint8_t* src = bitmap.buffer; + + switch (bitmap.pixel_mode) { + case FT_PIXEL_MODE_MONO: { + for (int y = 0; y < glyph.fHeight; ++y) { + for (int x = 0; x < width; ++x) { + dst[x] = -bittst(src, x); + } + dst = (uint16_t*)((char*)dst + dstRB); + src += bitmap.pitch; + } + } break; + case FT_PIXEL_MODE_GRAY: { + for (int y = 0; y < glyph.fHeight; ++y) { + for (int x = 0; x < width; ++x) { + dst[x] = grayToRGB16(src[x]); + } + dst = (uint16_t*)((char*)dst + dstRB); + src += bitmap.pitch; + } + } break; + default: { + SkASSERT(lcdIsVert || (glyph.fWidth * 3 == bitmap.width)); + for (int y = 0; y < glyph.fHeight; y++) { + if (lcdIsVert) { // vertical stripes + const uint8_t* srcR = src; + const uint8_t* srcG = srcR + bitmap.pitch; + const uint8_t* srcB = srcG + bitmap.pitch; + if (lcdIsBGR) { + SkTSwap(srcR, srcB); + } + for (int x = 0; x < width; x++) { + dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR), + sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG), + sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB)); + } + src += 3 * bitmap.pitch; + } else { // horizontal stripes + const uint8_t* triple = src; + if (lcdIsBGR) { + for (int x = 0; x < width; x++) { + dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR), + sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), + sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB)); + triple += 3; + } + } else { + for (int x = 0; x < width; x++) { + dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR), + sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG), + sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB)); + triple += 3; + } + } + src += bitmap.pitch; + } + dst = (uint16_t*)((char*)dst + dstRB); + } + } break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#define ft2sk(x) SkFixedToScalar(SkFDot6ToFixed(x)) + +#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 2 + #define CONST_PARAM const +#else // older freetype doesn't use const here + #define CONST_PARAM +#endif + +static int move_proc(CONST_PARAM FT_Vector* pt, void* ctx) { + SkPath* path = (SkPath*)ctx; + path->close(); // to close the previous contour (if any) + path->moveTo(ft2sk(pt->x), -ft2sk(pt->y)); + return 0; +} + +static int line_proc(CONST_PARAM FT_Vector* pt, void* ctx) { + SkPath* path = (SkPath*)ctx; + path->lineTo(ft2sk(pt->x), -ft2sk(pt->y)); + return 0; +} + +static int quad_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1, + void* ctx) { + SkPath* path = (SkPath*)ctx; + path->quadTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), -ft2sk(pt1->y)); + return 0; +} + +static int cubic_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1, + CONST_PARAM FT_Vector* pt2, void* ctx) { + SkPath* path = (SkPath*)ctx; + path->cubicTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), + -ft2sk(pt1->y), ft2sk(pt2->x), -ft2sk(pt2->y)); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, + const SkGlyph& glyph, + SkMaskGamma::PreBlend* maskPreBlend) +{ + //Must be careful not to use these if maskPreBlend == NULL + const uint8_t* tableR = NULL; + const uint8_t* tableG = NULL; + const uint8_t* tableB = NULL; + if (maskPreBlend) { + tableR = maskPreBlend->fR; + tableG = maskPreBlend->fG; + tableB = maskPreBlend->fB; + } + + const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag); + const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); + + switch ( face->glyph->format ) { + case FT_GLYPH_FORMAT_OUTLINE: { + FT_Outline* outline = &face->glyph->outline; + FT_BBox bbox; + FT_Bitmap target; + + if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { + emboldenOutline(face, outline); + } + + int dx = 0, dy = 0; + if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { + dx = SkFixedToFDot6(glyph.getSubXFixed()); + dy = SkFixedToFDot6(glyph.getSubYFixed()); + // negate dy since freetype-y-goes-up and skia-y-goes-down + dy = -dy; + } + FT_Outline_Get_CBox(outline, &bbox); + /* + what we really want to do for subpixel is + offset(dx, dy) + compute_bounds + offset(bbox & !63) + but that is two calls to offset, so we do the following, which + achieves the same thing with only one offset call. + */ + FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63), + dy - ((bbox.yMin + dy) & ~63)); + + if (SkMask::kLCD16_Format == glyph.fMaskFormat) { + FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD); + if (maskPreBlend) { + copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, + tableR, tableG, tableB); + } else { + copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, + tableR, tableG, tableB); + } + } else { + target.width = glyph.fWidth; + target.rows = glyph.fHeight; + target.pitch = glyph.rowBytes(); + target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage); + target.pixel_mode = compute_pixel_mode( + (SkMask::Format)fRec.fMaskFormat); + target.num_grays = 256; + + memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); + FT_Outline_Get_Bitmap(face->glyph->library, outline, &target); + } + } break; + + case FT_GLYPH_FORMAT_BITMAP: { + if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { + FT_GlyphSlot_Own_Bitmap(face->glyph); + FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, kBitmapEmboldenStrength, 0); + } + SkASSERT_CONTINUE(glyph.fWidth == face->glyph->bitmap.width); + SkASSERT_CONTINUE(glyph.fHeight == face->glyph->bitmap.rows); + SkASSERT_CONTINUE(glyph.fTop == -face->glyph->bitmap_top); + SkASSERT_CONTINUE(glyph.fLeft == face->glyph->bitmap_left); + + const uint8_t* src = (const uint8_t*)face->glyph->bitmap.buffer; + uint8_t* dst = (uint8_t*)glyph.fImage; + + if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY || + (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && + glyph.fMaskFormat == SkMask::kBW_Format)) { + unsigned srcRowBytes = face->glyph->bitmap.pitch; + unsigned dstRowBytes = glyph.rowBytes(); + unsigned minRowBytes = SkMin32(srcRowBytes, dstRowBytes); + unsigned extraRowBytes = dstRowBytes - minRowBytes; + + for (int y = face->glyph->bitmap.rows - 1; y >= 0; --y) { + memcpy(dst, src, minRowBytes); + memset(dst + minRowBytes, 0, extraRowBytes); + src += srcRowBytes; + dst += dstRowBytes; + } + } else if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO && + glyph.fMaskFormat == SkMask::kA8_Format) { + for (int y = 0; y < face->glyph->bitmap.rows; ++y) { + uint8_t byte = 0; + int bits = 0; + const uint8_t* src_row = src; + uint8_t* dst_row = dst; + + for (int x = 0; x < face->glyph->bitmap.width; ++x) { + if (!bits) { + byte = *src_row++; + bits = 8; + } + + *dst_row++ = byte & 0x80 ? 0xff : 0; + bits--; + byte <<= 1; + } + + src += face->glyph->bitmap.pitch; + dst += glyph.rowBytes(); + } + } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) { + if (maskPreBlend) { + copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, + tableR, tableG, tableB); + } else { + copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, + tableR, tableG, tableB); + } + } else { + SkDEBUGFAIL("unknown glyph bitmap transform needed"); + } + } break; + + default: + SkDEBUGFAIL("unknown glyph format"); + memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); + return; + } + +// We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum, +// it is optional +#if defined(SK_GAMMA_APPLY_TO_A8) + if (SkMask::kA8_Format == glyph.fMaskFormat && maskPreBlend) { + uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage; + unsigned rowBytes = glyph.rowBytes(); + + for (int y = glyph.fHeight - 1; y >= 0; --y) { + for (int x = glyph.fWidth - 1; x >= 0; --x) { + dst[x] = tableG[dst[x]]; + } + dst += rowBytes; + } + } +#endif +} + +void SkScalerContext_FreeType_Base::generateGlyphPath(FT_Face face, + const SkGlyph& glyph, + SkPath* path) +{ + if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { + emboldenOutline(face, &face->glyph->outline); + } + + FT_Outline_Funcs funcs; + + funcs.move_to = move_proc; + funcs.line_to = line_proc; + funcs.conic_to = quad_proc; + funcs.cubic_to = cubic_proc; + funcs.shift = 0; + funcs.delta = 0; + + FT_Error err = FT_Outline_Decompose(&face->glyph->outline, &funcs, path); + + if (err != 0) { + path->reset(); + return; + } + + path->close(); +} + +void SkScalerContext_FreeType_Base::emboldenOutline(FT_Face face, FT_Outline* outline) +{ + FT_Pos strength; + strength = FT_MulFix(face->units_per_EM, face->size->metrics.y_scale) + / 24; + FT_Outline_Embolden(outline, strength); +} diff --git a/src/ports/SkFontHost_FreeType_common.h b/src/ports/SkFontHost_FreeType_common.h new file mode 100644 index 0000000000..d5f2c08ab2 --- /dev/null +++ b/src/ports/SkFontHost_FreeType_common.h @@ -0,0 +1,44 @@ +/* + * Copyright 2006-2012 The Android Open Source Project + * Copyright 2012 Mozilla Foundation + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SKFONTHOST_FREETYPE_COMMON_H_ +#define SKFONTHOST_FREETYPE_COMMON_H_ + +#include "SkGlyph.h" +#include "SkScalerContext.h" +#include <ft2build.h> +#include FT_FREETYPE_H + +#ifdef SK_DEBUG + #define SkASSERT_CONTINUE(pred) \ + do { \ + if (!(pred)) \ + SkDebugf("file %s:%d: assert failed '" #pred "'\n", __FILE__, __LINE__); \ + } while (false) +#else + #define SkASSERT_CONTINUE(pred) +#endif + + +class SkScalerContext_FreeType_Base : public SkScalerContext { +public: + // See http://freetype.sourceforge.net/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden + // This value was chosen by eyeballing the result in Firefox and trying to match it. + static const FT_Pos kBitmapEmboldenStrength = 1 << 6; + + SkScalerContext_FreeType_Base(const SkDescriptor *desc) + : SkScalerContext(desc) + {} + +protected: + void generateGlyphImage(FT_Face face, const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend); + void generateGlyphPath(FT_Face face, const SkGlyph& glyph, SkPath* path); + void emboldenOutline(FT_Face face, FT_Outline* outline); +}; + +#endif // SKFONTHOST_FREETYPE_COMMON_H_ |