Index: src/ports/SkFontHost_FreeType_common.cpp |
=================================================================== |
--- src/ports/SkFontHost_FreeType_common.cpp (revision 12480) |
+++ src/ports/SkFontHost_FreeType_common.cpp (working copy) |
@@ -6,17 +6,32 @@ |
* found in the LICENSE file. |
*/ |
+#include "SkBitmap.h" |
+#include "SkCanvas.h" |
+#include "SkColor.h" |
#include "SkColorPriv.h" |
#include "SkFDot6.h" |
#include "SkFontHost_FreeType_common.h" |
#include "SkPath.h" |
#include <ft2build.h> |
+#include FT_FREETYPE_H |
+#include FT_BITMAP_H |
+#include FT_IMAGE_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 |
+// FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA |
+// were introduced in FreeType 2.5.0. |
+// The following may be removed once FreeType 2.5.0 is required to build. |
+#ifndef FT_LOAD_COLOR |
+# define FT_LOAD_COLOR ( 1L << 20 ) |
+# define FT_PIXEL_MODE_BGRA 7 |
+#endif |
+ |
+//#define SK_SHOW_TEXT_BLIT_COVERAGE |
+ |
static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) { |
switch (format) { |
case SkMask::kBW_Format: |
@@ -29,13 +44,20 @@ |
/////////////////////////////////////////////////////////////////////////////// |
-static uint16_t packTriple(unsigned r, unsigned g, unsigned b) { |
- return SkPackRGB16(r >> 3, g >> 2, b >> 3); |
+static uint16_t packTriple(U8CPU r, U8CPU g, U8CPU b) { |
+#ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
+ r = SkTMax(r, (U8CPU)0x40); |
+ g = SkTMax(g, (U8CPU)0x40); |
+ b = SkTMax(b, (U8CPU)0x40); |
+#endif |
+ return SkPack888ToRGB16(r, g, b); |
} |
static uint16_t grayToRGB16(U8CPU gray) { |
- SkASSERT(gray <= 255); |
- return SkPackRGB16(gray >> 3, gray >> 2, gray >> 3); |
+#ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
+ gray = SkTMax(gray, (U8CPU)0x40); |
+#endif |
+ return SkPack888ToRGB16(gray, gray, gray); |
} |
static int bittst(const uint8_t data[], int bitOffset) { |
@@ -44,81 +66,274 @@ |
return lowBit & 1; |
} |
+/** |
+ * Copies a FT_Bitmap into an SkMask with the same dimensions. |
+ * |
+ * FT_PIXEL_MODE_MONO |
+ * FT_PIXEL_MODE_GRAY |
+ * FT_PIXEL_MODE_LCD |
+ * FT_PIXEL_MODE_LCD_V |
+ */ |
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); |
+static void copyFT2LCD16(const FT_Bitmap& bitmap, const SkMask& mask, int lcdIsBGR, |
+ const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) |
+{ |
+ SkASSERT(SkMask::kLCD16_Format == mask.fFormat); |
+ if (FT_PIXEL_MODE_LCD != bitmap.pixel_mode) { |
+ SkASSERT(mask.fBounds.width() == bitmap.width); |
} |
+ if (FT_PIXEL_MODE_LCD_V != bitmap.pixel_mode) { |
+ SkASSERT(mask.fBounds.height() == 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; |
+ uint16_t* dst = reinterpret_cast<uint16_t*>(mask.fImage); |
+ const size_t dstRB = mask.fRowBytes; |
+ const int width = mask.fBounds.width(); |
+ const int height = mask.fBounds.height(); |
+ |
switch (bitmap.pixel_mode) { |
- case FT_PIXEL_MODE_MONO: { |
- for (int y = 0; y < glyph.fHeight; ++y) { |
+ case FT_PIXEL_MODE_MONO: |
+ for (int y = height; y --> 0;) { |
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) { |
+ break; |
+ case FT_PIXEL_MODE_GRAY: |
+ for (int y = height; y --> 0;) { |
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); |
+ break; |
+ case FT_PIXEL_MODE_LCD: |
+ SkASSERT(3 * mask.fBounds.width() == bitmap.width); |
+ for (int y = height; y --> 0;) { |
+ 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>(*srcR++, tableR), |
- sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG), |
- sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB)); |
+ 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 += 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; |
} |
+ src += bitmap.pitch; |
dst = (uint16_t*)((char*)dst + dstRB); |
} |
- } break; |
+ break; |
+ case FT_PIXEL_MODE_LCD_V: |
+ SkASSERT(3 * mask.fBounds.height() == bitmap.rows); |
+ for (int y = height; y --> 0;) { |
+ 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; |
+ dst = (uint16_t*)((char*)dst + dstRB); |
+ } |
+ break; |
+ default: |
+ SkDEBUGF(("FT_Pixel_Mode %d", bitmap.pixel_mode)); |
+ SkDEBUGFAIL("unsupported FT_Pixel_Mode for LCD16"); |
+ break; |
} |
} |
+/** |
+ * Copies a FT_Bitmap into an SkMask with the same dimensions. |
+ * |
+ * Yes, No, Never Requested, Never Produced |
+ * |
+ * kBW kA8 k3D kARGB32 kLCD16 kLCD32 |
+ * FT_PIXEL_MODE_MONO Y Y NR N Y NR |
+ * FT_PIXEL_MODE_GRAY N Y NR N Y NR |
+ * FT_PIXEL_MODE_GRAY2 NP NP NR NP NP NR |
+ * FT_PIXEL_MODE_GRAY4 NP NP NR NP NP NR |
+ * FT_PIXEL_MODE_LCD NP NP NR NP NP NR |
+ * FT_PIXEL_MODE_LCD_V NP NP NR NP NP NR |
+ * FT_PIXEL_MODE_BGRA N N NR Y N NR |
+ * |
+ * TODO: All of these N need to be Y or otherwise ruled out. |
+ */ |
+static void copyFTBitmap(const FT_Bitmap& srcFTBitmap, SkMask& dstMask) { |
+ SkASSERT(dstMask.fBounds.width() == srcFTBitmap.width); |
+ SkASSERT(dstMask.fBounds.height() == srcFTBitmap.rows); |
+ |
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(srcFTBitmap.buffer); |
+ const FT_Pixel_Mode srcFormat = static_cast<FT_Pixel_Mode>(srcFTBitmap.pixel_mode); |
+ // FT_Bitmap::pitch is an int and allowed to be negative. |
+ const int srcPitch = srcFTBitmap.pitch; |
+ const size_t srcRowBytes = SkTAbs(srcPitch); |
+ |
+ uint8_t* dst = dstMask.fImage; |
+ const SkMask::Format dstFormat = static_cast<SkMask::Format>(dstMask.fFormat); |
+ const size_t dstRowBytes = dstMask.fRowBytes; |
+ |
+ const size_t width = srcFTBitmap.width; |
+ const size_t height = srcFTBitmap.rows; |
+ |
+ if (SkMask::kLCD16_Format == dstFormat) { |
+ copyFT2LCD16<false>(srcFTBitmap, dstMask, false, NULL, NULL, NULL); |
+ return; |
+ } |
+ |
+ if ((FT_PIXEL_MODE_MONO == srcFormat && SkMask::kBW_Format == dstFormat) || |
+ (FT_PIXEL_MODE_GRAY == srcFormat && SkMask::kA8_Format == dstFormat)) |
+ { |
+ size_t commonRowBytes = SkTMin(srcRowBytes, dstRowBytes); |
+ for (size_t y = height; y --> 0;) { |
+ memcpy(dst, src, commonRowBytes); |
+ src += srcPitch; |
+ dst += dstRowBytes; |
+ } |
+ } else if (FT_PIXEL_MODE_MONO == srcFormat && SkMask::kA8_Format == dstFormat) { |
+ for (size_t y = height; y --> 0;) { |
+ uint8_t byte = 0; |
+ int bits = 0; |
+ const uint8_t* src_row = src; |
+ uint8_t* dst_row = dst; |
+ for (size_t x = width; x --> 0;) { |
+ if (0 == bits) { |
+ byte = *src_row++; |
+ bits = 8; |
+ } |
+ *dst_row++ = byte & 0x80 ? 0xff : 0x00; |
+ bits--; |
+ byte <<= 1; |
+ } |
+ src += srcPitch; |
+ dst += dstRowBytes; |
+ } |
+ } else if (FT_PIXEL_MODE_BGRA == srcFormat && SkMask::kARGB32_Format == dstFormat) { |
+ // FT_PIXEL_MODE_BGRA is pre-multiplied. |
+ for (size_t y = height; y --> 0;) { |
+ const uint8_t* src_row = src; |
+ SkPMColor* dst_row = reinterpret_cast<SkPMColor*>(dst); |
+ for (size_t x = 0; x < width; ++x) { |
+ uint8_t b = *src_row++; |
+ uint8_t g = *src_row++; |
+ uint8_t r = *src_row++; |
+ uint8_t a = *src_row++; |
+ *dst_row++ = SkPackARGB32(a, r, g, b); |
+#ifdef SK_SHOW_TEXT_BLIT_COVERAGE |
+ *(dst_row-1) = SkFourByteInterp256(*(dst_row-1), SK_ColorWHITE, 0x40); |
+#endif |
+ } |
+ src += srcPitch; |
+ dst += dstRowBytes; |
+ } |
+ } else { |
+ SkDEBUGF(("FT_Pixel_Mode %d, SkMask::Format %d\n", srcFormat, dstFormat)); |
+ SkDEBUGFAIL("unsupported combination of FT_Pixel_Mode and SkMask::Format"); |
+ } |
+} |
+ |
+static inline int convert_8_to_1(unsigned byte) { |
+ SkASSERT(byte <= 0xFF); |
+ // Arbitrary decision that making the cutoff at 1/4 instead of 1/2 in general looks better. |
+ return (byte >> 6) != 0; |
+} |
+ |
+static uint8_t pack_8_to_1(const uint8_t alpha[8]) { |
+ unsigned bits = 0; |
+ for (int i = 0; i < 8; ++i) { |
+ bits <<= 1; |
+ bits |= convert_8_to_1(alpha[i]); |
+ } |
+ return SkToU8(bits); |
+} |
+ |
+static void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) { |
+ const int height = mask.fBounds.height(); |
+ const int width = mask.fBounds.width(); |
+ const int octs = width >> 3; |
+ const int leftOverBits = width & 7; |
+ |
+ uint8_t* dst = mask.fImage; |
+ const int dstPad = mask.fRowBytes - SkAlign8(width)/8; |
+ SkASSERT(dstPad >= 0); |
+ |
+ const int srcPad = srcRB - width; |
+ SkASSERT(srcPad >= 0); |
+ |
+ for (int y = 0; y < height; ++y) { |
+ for (int i = 0; i < octs; ++i) { |
+ *dst++ = pack_8_to_1(src); |
+ src += 8; |
+ } |
+ if (leftOverBits > 0) { |
+ unsigned bits = 0; |
+ int shift = 7; |
+ for (int i = 0; i < leftOverBits; ++i, --shift) { |
+ bits |= convert_8_to_1(*src++) << shift; |
+ } |
+ *dst++ = bits; |
+ } |
+ src += srcPad; |
+ dst += dstPad; |
+ } |
+} |
+ |
+inline SkMask::Format SkMaskFormat_for_SkBitmapConfig(SkBitmap::Config config) { |
+ switch (config) { |
+ case SkBitmap::kA8_Config: |
+ return SkMask::kA8_Format; |
+ case SkBitmap::kARGB_8888_Config: |
+ return SkMask::kARGB32_Format; |
+ default: |
+ SkDEBUGFAIL("unsupported SkBitmap::Config"); |
+ return SkMask::kA8_Format; |
+ } |
+} |
+ |
+inline SkBitmap::Config SkBitmapConfig_for_FTPixelMode(FT_Pixel_Mode pixel_mode) { |
+ switch (pixel_mode) { |
+ case FT_PIXEL_MODE_MONO: |
+ case FT_PIXEL_MODE_GRAY: |
+ return SkBitmap::kA8_Config; |
+ case FT_PIXEL_MODE_BGRA: |
+ return SkBitmap::kARGB_8888_Config; |
+ default: |
+ SkDEBUGFAIL("unsupported FT_PIXEL_MODE"); |
+ return SkBitmap::kA8_Config; |
+ } |
+} |
+ |
+inline SkBitmap::Config SkBitmapConfig_for_SkMaskFormat(SkMask::Format format) { |
+ switch (format) { |
+ case SkMask::kBW_Format: |
+ case SkMask::kA8_Format: |
+ case SkMask::kLCD16_Format: |
+ return SkBitmap::kA8_Config; |
+ case SkMask::kARGB32_Format: |
+ return SkBitmap::kARGB_8888_Config; |
+ default: |
+ SkDEBUGFAIL("unsupported destination SkBitmap::Config"); |
+ return SkBitmap::kA8_Config; |
+ } |
+} |
+ |
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); |
@@ -129,7 +344,8 @@ |
FT_BBox bbox; |
FT_Bitmap target; |
- if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { |
+ if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && |
+ !(face->style_flags & FT_STYLE_FLAG_BOLD)) { |
emboldenOutline(face, outline); |
} |
@@ -154,11 +370,13 @@ |
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>(glyph, face->glyph->bitmap, doBGR, doVert, |
+ copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR, |
fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); |
} else { |
- copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert, |
+ copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR, |
fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); |
} |
} else { |
@@ -166,8 +384,7 @@ |
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.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat); |
target.num_grays = 256; |
memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); |
@@ -176,71 +393,106 @@ |
} break; |
case FT_GLYPH_FORMAT_BITMAP: { |
- if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { |
+ 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); |
+ 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 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; |
+ } |
- 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; |
+ // Otherwise, scale the bitmap. |
- 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; |
+ // 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(); |
- for (int x = 0; x < face->glyph->bitmap.width; ++x) { |
- if (!bits) { |
- byte = *src_row++; |
- bits = 8; |
- } |
+ 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); |
- *dst_row++ = byte & 0x80 ? 0xff : 0; |
- bits--; |
- byte <<= 1; |
+ // 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]); |
} |
- |
- src += face->glyph->bitmap.pitch; |
- dst += glyph.rowBytes(); |
+ dst = (uint16_t*)((char*)dst + glyph.rowBytes()); |
+ src += dstBitmap.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; |
+ 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, |