void SkScalerContext_FreeType_Base::generateGlyphImage( FT_Face face, const SkGlyph& glyph, const SkMatrix& bitmapTransform) { 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; 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; } memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); if (SkMask::kLCD16_Format == glyph.fMaskFormat) { FT_Outline_Translate(outline, dx, dy); FT_Error err = FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD); if (err) { SK_TRACEFTR(err, "Could not render glyph."); return; } SkMask mask; glyph.toMask(&mask); #ifdef SK_SHOW_TEXT_BLIT_COVERAGE memset(mask.fImage, 0x80, mask.fBounds.height() * mask.fRowBytes); #endif FT_GlyphSlotRec& ftGlyph = *face->glyph; if (!SkIRect::Intersects(mask.fBounds, SkIRect::MakeXYWH( ftGlyph.bitmap_left, -ftGlyph.bitmap_top, ftGlyph.bitmap.width, ftGlyph.bitmap.rows))) { return; } // If the FT_Bitmap extent is larger, discard bits of the bitmap outside the mask. // If the SkMask extent is larger, shrink mask to fit bitmap (clearing discarded). unsigned char* origBuffer = ftGlyph.bitmap.buffer; // First align the top left (origin). if (-ftGlyph.bitmap_top < mask.fBounds.fTop) { int32_t topDiff = mask.fBounds.fTop - (-ftGlyph.bitmap_top); ftGlyph.bitmap.buffer += ftGlyph.bitmap.pitch * topDiff; ftGlyph.bitmap.rows -= topDiff; ftGlyph.bitmap_top = -mask.fBounds.fTop; } if (ftGlyph.bitmap_left < mask.fBounds.fLeft) { int32_t leftDiff = mask.fBounds.fLeft - ftGlyph.bitmap_left; ftGlyph.bitmap.buffer += leftDiff; ftGlyph.bitmap.width -= leftDiff; ftGlyph.bitmap_left = mask.fBounds.fLeft; } if (mask.fBounds.fTop < -ftGlyph.bitmap_top) { mask.fImage += mask.fRowBytes * (-ftGlyph.bitmap_top - mask.fBounds.fTop); mask.fBounds.fTop = -ftGlyph.bitmap_top; } if (mask.fBounds.fLeft < ftGlyph.bitmap_left) { mask.fImage += sizeof(uint16_t) * (ftGlyph.bitmap_left - mask.fBounds.fLeft); mask.fBounds.fLeft = ftGlyph.bitmap_left; } // Origins aligned, clean up the width and height. int ftVertScale = (doVert ? 3 : 1); int ftHoriScale = (doVert ? 1 : 3); if (mask.fBounds.height() * ftVertScale < SkToInt(ftGlyph.bitmap.rows)) { ftGlyph.bitmap.rows = mask.fBounds.height() * ftVertScale; } if (mask.fBounds.width() * ftHoriScale < SkToInt(ftGlyph.bitmap.width)) { ftGlyph.bitmap.width = mask.fBounds.width() * ftHoriScale; } if (SkToInt(ftGlyph.bitmap.rows) < mask.fBounds.height() * ftVertScale) { mask.fBounds.fBottom = mask.fBounds.fTop + ftGlyph.bitmap.rows / ftVertScale; } if (SkToInt(ftGlyph.bitmap.width) < mask.fBounds.width() * ftHoriScale) { mask.fBounds.fRight = mask.fBounds.fLeft + ftGlyph.bitmap.width / ftHoriScale; } if (fPreBlend.isApplicable()) { copyFT2LCD16<true>(ftGlyph.bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { copyFT2LCD16<false>(ftGlyph.bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } // Restore the buffer pointer so FreeType can properly free it. ftGlyph.bitmap.buffer = origBuffer; } else { FT_BBox bbox; FT_Bitmap target; 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)); 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; FT_Outline_Get_Bitmap(face->glyph->library, outline, &target); #ifdef SK_SHOW_TEXT_BLIT_COVERAGE for (int y = 0; y < glyph.fHeight; ++y) { for (int x = 0; x < glyph.fWidth; ++x) { uint8_t& a = ((uint8_t*)glyph.fImage)[(glyph.rowBytes() * y) + x]; a = SkTMax<uint8_t>(a, 0x20); } } #endif } } break; case FT_GLYPH_FORMAT_BITMAP: { FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode); SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat); // Assume that the other formats do not exist. SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode || FT_PIXEL_MODE_GRAY == pixel_mode || FT_PIXEL_MODE_BGRA == pixel_mode); // These are the only formats this ScalerContext should request. SkASSERT(SkMask::kBW_Format == maskFormat || SkMask::kA8_Format == maskFormat || SkMask::kARGB32_Format == maskFormat || SkMask::kLCD16_Format == maskFormat); // If no scaling needed, directly copy glyph bitmap. if (bitmapTransform.isIdentity()) { SkMask dstMask; glyph.toMask(&dstMask); copyFTBitmap(face->glyph->bitmap, dstMask); break; } // Otherwise, scale the bitmap. // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB) SkBitmap unscaledBitmap; // TODO: mark this as sRGB when the blits will be sRGB. unscaledBitmap.allocPixels(SkImageInfo::Make(face->glyph->bitmap.width, face->glyph->bitmap.rows, SkColorType_for_FTPixelMode(pixel_mode), kPremul_SkAlphaType)); SkMask unscaledBitmapAlias; unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels()); unscaledBitmapAlias.fBounds.set(0, 0, unscaledBitmap.width(), unscaledBitmap.height()); unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes(); unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkColorType(unscaledBitmap.colorType()); copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias); // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD. // BW requires an A8 target for resizing, which can then be down sampled. // LCD should use a 4x A8 target, which will then be down sampled. // For simplicity, LCD uses A8 and is replicated. int bitmapRowBytes = 0; if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) { bitmapRowBytes = glyph.rowBytes(); } SkBitmap dstBitmap; // TODO: mark this as sRGB when the blits will be sRGB. dstBitmap.setInfo(SkImageInfo::Make(glyph.fWidth, glyph.fHeight, SkColorType_for_SkMaskFormat(maskFormat), kPremul_SkAlphaType), bitmapRowBytes); if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) { dstBitmap.allocPixels(); } else { dstBitmap.setPixels(glyph.fImage); } // Scale unscaledBitmap into dstBitmap. SkCanvas canvas(dstBitmap); #ifdef SK_SHOW_TEXT_BLIT_COVERAGE canvas.clear(0x33FF0000); #else canvas.clear(SK_ColorTRANSPARENT); #endif canvas.translate(-glyph.fLeft, -glyph.fTop); canvas.concat(bitmapTransform); canvas.translate(face->glyph->bitmap_left, -face->glyph->bitmap_top); SkPaint paint; paint.setFilterQuality(kMedium_SkFilterQuality); canvas.drawBitmap(unscaledBitmap, 0, 0, &paint); // If the destination is BW or LCD, convert from A8. if (SkMask::kBW_Format == maskFormat) { // Copy the A8 dstBitmap into the A1 glyph.fImage. SkMask dstMask; glyph.toMask(&dstMask); packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes()); } else if (SkMask::kLCD16_Format == maskFormat) { // Copy the A8 dstBitmap into the LCD16 glyph.fImage. uint8_t* src = dstBitmap.getAddr8(0, 0); uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage); for (int y = dstBitmap.height(); y --> 0;) { for (int x = 0; x < dstBitmap.width(); ++x) { dst[x] = grayToRGB16(src[x]); } dst = (uint16_t*)((char*)dst + glyph.rowBytes()); src += dstBitmap.rowBytes(); } } } 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 && fPreBlend.isApplicable()) { 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] = fPreBlend.fG[dst[x]]; } dst += rowBytes; } } #endif }
void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGlyph& glyph) { 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 (fPreBlend.isApplicable()) { copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } 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 (fPreBlend.isApplicable()) { copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } 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 && fPreBlend.isApplicable()) { 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] = fPreBlend.fG[dst[x]]; } dst += rowBytes; } } #endif }
SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) : SkScalerContext(desc) { SkAutoMutexAcquire ac(gFTMutex); if (gFTCount == 0) { if (!InitFreetype()) { sk_throw(); } } ++gFTCount; // load the font file fFTSize = NULL; fFace = NULL; fFaceRec = ref_ft_face(fRec.fFontID); if (NULL == fFaceRec) { return; } fFace = fFaceRec->fFace; // compute our factors from the record SkMatrix m; fRec.getSingleMatrix(&m); #ifdef DUMP_STRIKE_CREATION SkString keyString; SkFontHost::GetDescriptorKeyString(desc, &keyString); printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize), 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.getHinting(), fRec.fMaskFormat, keyString.c_str()); #endif // now compute our scale factors SkScalar sx = m.getScaleX(); SkScalar sy = m.getScaleY(); if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) { // sort of give up on hinting sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX())); sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy)); sx = sy = SkScalarAve(sx, sy); SkScalar inv = SkScalarInvert(sx); // flip the skew elements to go from our Y-down system to FreeType's fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv)); fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv)); fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv)); fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv)); } else { fMatrix22.xx = fMatrix22.yy = SK_Fixed1; fMatrix22.xy = fMatrix22.yx = 0; } fScaleX = SkScalarToFixed(sx); fScaleY = SkScalarToFixed(sy); // compute the flags we send to Load_Glyph { FT_Int32 loadFlags = FT_LOAD_DEFAULT; if (SkMask::kBW_Format == fRec.fMaskFormat) { // See http://code.google.com/p/chromium/issues/detail?id=43252#c24 loadFlags = FT_LOAD_TARGET_MONO; if (fRec.getHinting() == SkPaint::kNo_Hinting) loadFlags = FT_LOAD_NO_HINTING; } else { switch (fRec.getHinting()) { case SkPaint::kNo_Hinting: loadFlags = FT_LOAD_NO_HINTING; break; case SkPaint::kSlight_Hinting: loadFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT break; case SkPaint::kNormal_Hinting: loadFlags = FT_LOAD_TARGET_NORMAL; break; case SkPaint::kFull_Hinting: loadFlags = FT_LOAD_TARGET_NORMAL; if (SkMask::kHorizontalLCD_Format == fRec.fMaskFormat) loadFlags = FT_LOAD_TARGET_LCD; else if (SkMask::kVerticalLCD_Format == fRec.fMaskFormat) loadFlags = FT_LOAD_TARGET_LCD_V; break; default: SkDebugf("---------- UNKNOWN hinting %d\n", fRec.getHinting()); break; } } if ((fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag) == 0) loadFlags |= FT_LOAD_NO_BITMAP; fLoadGlyphFlags = loadFlags; } // now create the FT_Size { FT_Error err; err = FT_New_Size(fFace, &fFTSize); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFace = NULL; return; } err = FT_Activate_Size(fFTSize); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFTSize = NULL; } err = FT_Set_Char_Size( fFace, SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), 72, 72); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFace = NULL; return; } FT_Set_Transform( fFace, &fMatrix22, NULL); } }
void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGlyph& glyph) { 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 && !(face->style_flags & FT_STYLE_FLAG_BOLD)) { 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); SkMask mask; glyph.toMask(&mask); if (fPreBlend.isApplicable()) { copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } else { copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); } } 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: { FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode); SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat); // Assume that the other formats do not exist. SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode || FT_PIXEL_MODE_GRAY == pixel_mode || FT_PIXEL_MODE_BGRA == pixel_mode); // These are the only formats this ScalerContext should request. SkASSERT(SkMask::kBW_Format == maskFormat || SkMask::kA8_Format == maskFormat || SkMask::kARGB32_Format == maskFormat || SkMask::kLCD16_Format == maskFormat); if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && !(face->style_flags & FT_STYLE_FLAG_BOLD)) { FT_GlyphSlot_Own_Bitmap(face->glyph); FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, kBitmapEmboldenStrength, 0); } // If no scaling needed, directly copy glyph bitmap. if (glyph.fWidth == face->glyph->bitmap.width && glyph.fHeight == face->glyph->bitmap.rows && glyph.fTop == -face->glyph->bitmap_top && glyph.fLeft == face->glyph->bitmap_left) { SkMask dstMask; glyph.toMask(&dstMask); copyFTBitmap(face->glyph->bitmap, dstMask); break; } // Otherwise, scale the bitmap. // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB) SkBitmap unscaledBitmap; unscaledBitmap.setConfig(SkBitmapConfig_for_FTPixelMode(pixel_mode), face->glyph->bitmap.width, face->glyph->bitmap.rows); unscaledBitmap.allocPixels(); SkMask unscaledBitmapAlias; unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels()); unscaledBitmapAlias.fBounds.set(0, 0, unscaledBitmap.width(), unscaledBitmap.height()); unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes(); unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkBitmapConfig(unscaledBitmap.config()); copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias); // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD. // BW requires an A8 target for resizing, which can then be down sampled. // LCD should use a 4x A8 target, which will then be down sampled. // For simplicity, LCD uses A8 and is replicated. int bitmapRowBytes = 0; if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) { bitmapRowBytes = glyph.rowBytes(); } SkBitmap dstBitmap; dstBitmap.setConfig(SkBitmapConfig_for_SkMaskFormat(maskFormat), glyph.fWidth, glyph.fHeight, bitmapRowBytes); if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) { dstBitmap.allocPixels(); } else { dstBitmap.setPixels(glyph.fImage); } // Scale unscaledBitmap into dstBitmap. SkCanvas canvas(dstBitmap); canvas.clear(SK_ColorTRANSPARENT); canvas.scale(SkIntToScalar(glyph.fWidth) / SkIntToScalar(face->glyph->bitmap.width), SkIntToScalar(glyph.fHeight) / SkIntToScalar(face->glyph->bitmap.rows)); SkPaint paint; paint.setFilterLevel(SkPaint::kLow_FilterLevel); canvas.drawBitmap(unscaledBitmap, 0, 0, &paint); // If the destination is BW or LCD, convert from A8. if (SkMask::kBW_Format == maskFormat) { // Copy the A8 dstBitmap into the A1 glyph.fImage. SkMask dstMask; glyph.toMask(&dstMask); packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes()); } else if (SkMask::kLCD16_Format == maskFormat) { // Copy the A8 dstBitmap into the LCD16 glyph.fImage. uint8_t* src = dstBitmap.getAddr8(0, 0); uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage); for (int y = dstBitmap.height(); y --> 0;) { for (int x = 0; x < dstBitmap.width(); ++x) { dst[x] = grayToRGB16(src[x]); } dst = (uint16_t*)((char*)dst + glyph.rowBytes()); src += dstBitmap.rowBytes(); } } } 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 && fPreBlend.isApplicable()) { 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] = fPreBlend.fG[dst[x]]; } dst += rowBytes; } } #endif }
SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc) : SkScalerContext(desc), fFTSize(NULL) { SkAutoMutexAcquire ac(gFTMutex); FT_Error err; if (gFTCount == 0) { err = FT_Init_FreeType(&gFTLibrary); // SkDEBUGF(("FT_Init_FreeType returned %d\n", err)); SkASSERT(err == 0); } ++gFTCount; // load the font file fFaceRec = ref_ft_face(fRec.fFontID); fFace = fFaceRec ? fFaceRec->fFace : NULL; // compute our factors from the record SkMatrix m; fRec.getSingleMatrix(&m); #ifdef DUMP_STRIKE_CREATION SkString keyString; SkFontHost::GetDescriptorKeyString(desc, &keyString); printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize), 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()); #endif // now compute our scale factors SkScalar sx = m.getScaleX(); SkScalar sy = m.getScaleY(); if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) { // sort of give up on hinting sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX())); sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy)); sx = sy = SkScalarAve(sx, sy); SkScalar inv = SkScalarInvert(sx); // flip the skew elements to go from our Y-down system to FreeType's fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv)); fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv)); fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv)); fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv)); } else { fMatrix22.xx = fMatrix22.yy = SK_Fixed1; fMatrix22.xy = fMatrix22.yx = 0; } fScaleX = SkScalarToFixed(sx); fScaleY = SkScalarToFixed(sy); // 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 switch (fRec.fHints) { case kNo_Hints: flags |= FT_LOAD_NO_HINTING; break; case kSubpixel_Hints: flags |= FT_LOAD_FORCE_AUTOHINT; render_flags = FT_LOAD_TARGET_LIGHT; break; case kNormal_Hints: flags |= FT_LOAD_FORCE_AUTOHINT; #ifdef ANDROID /* 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 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; } // now create the FT_Size { FT_Error err; err = FT_New_Size(fFace, &fFTSize); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFace = NULL; return; } err = FT_Activate_Size(fFTSize); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFTSize = NULL; } err = FT_Set_Char_Size( fFace, SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), 72, 72); if (err != 0) { SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n", fFaceRec->fFontID, fScaleX, fScaleY, err)); fFace = NULL; return; } FT_Set_Transform( fFace, &fMatrix22, NULL); } }