Chromium Code Reviews| Index: src/ports/SkFontHost_FreeType_common.cpp |
| =================================================================== |
| --- src/ports/SkFontHost_FreeType_common.cpp (revision 12380) |
| +++ src/ports/SkFontHost_FreeType_common.cpp (working copy) |
| @@ -6,6 +6,9 @@ |
| * 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" |
| @@ -17,6 +20,14 @@ |
| // 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 |
| + |
| static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) { |
| switch (format) { |
| case SkMask::kBW_Format: |
| @@ -60,7 +71,7 @@ |
| const uint8_t* src = bitmap.buffer; |
| switch (bitmap.pixel_mode) { |
| - case FT_PIXEL_MODE_MONO: { |
| + 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); |
| @@ -68,8 +79,8 @@ |
| dst = (uint16_t*)((char*)dst + dstRB); |
| src += bitmap.pitch; |
| } |
| - } break; |
| - case FT_PIXEL_MODE_GRAY: { |
| + 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]); |
| @@ -77,8 +88,8 @@ |
| dst = (uint16_t*)((char*)dst + dstRB); |
| src += bitmap.pitch; |
| } |
| - } break; |
| - default: { |
| + break; |
| + default: |
| SkASSERT(lcdIsVert || (glyph.fWidth * 3 == bitmap.width)); |
| for (int y = 0; y < glyph.fHeight; y++) { |
| if (lcdIsVert) { // vertical stripes |
| @@ -115,10 +126,121 @@ |
| } |
| dst = (uint16_t*)((char*)dst + dstRB); |
| } |
| - } break; |
| + break; |
| } |
| } |
| +// copies an FT_Bitmap's pixel data into a buffer with identical dimensions |
| +static void copyFTBitmap(const FT_Bitmap& srcFTBitmap, SkMask& dstMask) { |
| + const uint8_t* src = reinterpret_cast<const uint8_t*>(srcFTBitmap.buffer); |
| + uint8_t* dst = dstMask.fImage; |
| + const SkMask::Format dstFormat = static_cast<SkMask::Format>(dstMask.fFormat); |
| + size_t dstRowBytes = dstMask.fRowBytes; |
| + size_t width = srcFTBitmap.width; |
| + size_t height = srcFTBitmap.rows; |
| + size_t srcRowBytes = srcFTBitmap.pitch; |
| + |
| + if ((SkMask::kA8_Format == dstFormat && |
| + FT_PIXEL_MODE_GRAY == srcFTBitmap.pixel_mode) || |
| + (SkMask::kBW_Format == dstFormat && |
| + FT_PIXEL_MODE_MONO == srcFTBitmap.pixel_mode)) { |
| + // TODO: test 1bpp bitmap font into kBW_Format glyph |
| + size_t minRowBytes = SkMin32(srcRowBytes, dstRowBytes); |
| + size_t extraRowBytes = dstRowBytes - minRowBytes; |
| + for (int y = height - 1; y >= 0; --y) { |
| + memcpy(dst, src, minRowBytes); |
| + memset(dst + minRowBytes, 0, extraRowBytes); |
| + src += srcRowBytes; |
| + dst += dstRowBytes; |
| + } |
| + } else if (SkMask::kA8_Format == dstFormat && |
| + FT_PIXEL_MODE_MONO == srcFTBitmap.pixel_mode) { |
| + // TODO: test 1bpp bitmap font into kA8_Format glyph |
| + for (size_t y = 0; y < height; ++y) { |
| + uint8_t byte = 0; |
| + int bits = 0; |
| + const uint8_t* src_row = src; |
| + uint8_t* dst_row = dst; |
| + for (size_t x = 0; x < width; ++x) { |
| + if (!bits) { |
| + byte = *src_row++; |
| + bits = 8; |
| + } |
| + *dst_row++ = byte & 0x80 ? 0xff : 0; |
| + bits--; |
| + byte <<= 1; |
| + } |
| + src += srcRowBytes; |
| + dst += dstRowBytes; |
| + } |
| + } else if (SkMask::kARGB32_Format == dstFormat && |
| + FT_PIXEL_MODE_BGRA == srcFTBitmap.pixel_mode) { |
| + size_t minWidth = SkMin32(width, SkMin32(srcRowBytes, dstRowBytes) / 4); |
| + size_t extraRowBytes = dstRowBytes - (4 * minWidth); |
| + for (size_t y = 0; y < height; ++y) { |
| + const uint8_t* src_row = src; |
| + uint8_t* dst_row = dst; |
| + for (size_t x = 0; x < minWidth; ++x) { |
| + uint8_t blue = *src_row++; |
| + uint8_t green = *src_row++; |
| + uint8_t red = *src_row++; |
| + uint8_t alpha = *src_row++; |
| + *dst_row++ = red; |
| + *dst_row++ = green; |
| + *dst_row++ = blue; |
| + *dst_row++ = alpha; |
| + } |
| + memset(dst_row, 0, extraRowBytes); |
| + src += srcRowBytes; |
| + dst += dstRowBytes; |
| + } |
| + } else { |
| + SkDEBUGFAIL("unsupported combination of FT_PIXEL_MODE and SkMask::Format"); |
| + } |
| +} |
| + |
| +inline SkMask::Format skFormatForFTPixelMode(FT_Pixel_Mode pixel_mode) { |
| + switch (pixel_mode) { |
| + case FT_PIXEL_MODE_GRAY: |
| + return SkMask::kA8_Format; |
| + case FT_PIXEL_MODE_MONO: |
| + return SkMask::kBW_Format; |
| + case FT_PIXEL_MODE_BGRA: |
| + return SkMask::kARGB32_Format; |
| + default: |
| + SkDEBUGFAIL("unsupported FT_PIXEL_MODE"); |
| + return SkMask::kA8_Format; |
| + } |
| +} |
| + |
| +inline SkBitmap::Config skConfigForFTPixelMode(FT_Pixel_Mode pixel_mode) { |
| + switch (pixel_mode) { |
| + case FT_PIXEL_MODE_GRAY: |
| + return SkBitmap::kA8_Config; |
| + case FT_PIXEL_MODE_MONO: |
| + return SkBitmap::kA1_Config; |
| + case FT_PIXEL_MODE_BGRA: |
| + return SkBitmap::kARGB_8888_Config; |
| + default: |
| + SkDEBUGFAIL("unsupported FT_PIXEL_MODE"); |
| + return SkBitmap::kA8_Config; |
| + } |
| +} |
| + |
| +inline SkBitmap::Config skConfigForFormat(SkMask::Format format) { |
| + switch (format) { |
| + case SkMask::kA8_Format: |
| + return SkBitmap::kA8_Config; |
| + case SkMask::kBW_Format: |
| + return SkBitmap::kA1_Config; |
| + case SkMask::kARGB32_Format: |
| + return SkBitmap::kARGB_8888_Config; |
| + default: |
| + SkDEBUGFAIL("unsupported FT_PIXEL_MODE"); |
| + 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 +251,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); |
| } |
| @@ -166,65 +289,29 @@ |
| 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); |
| FT_Outline_Get_Bitmap(face->glyph->library, outline, &target); |
| } |
| - } break; |
| + } |
| + break; |
| - case FT_GLYPH_FORMAT_BITMAP: { |
| - if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) { |
| + case FT_GLYPH_FORMAT_BITMAP: |
| + 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 (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 (SkMask::kLCD16_Format == glyph.fMaskFormat) { |
| + // special-case kLCD16_Format - no scaling currently supported |
| + 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); |
| if (fPreBlend.isApplicable()) { |
| copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert, |
| fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); |
| @@ -233,14 +320,53 @@ |
| fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); |
| } |
| } else { |
| - SkDEBUGFAIL("unknown glyph bitmap transform needed"); |
| + 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) { |
| + // glyph image needs scaling |
| + // start by copying FT2 image into an SkBitmap |
| + SkBitmap unscaledBitmap; |
|
bungeman-skia
2013/11/27 00:04:49
This scaling code is incorrect for two reasons. Fi
|
| + FT_Pixel_Mode pixel_mode = |
| + static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode); |
| + unscaledBitmap.setConfig(skConfigForFTPixelMode(pixel_mode), |
| + face->glyph->bitmap.width, |
| + face->glyph->bitmap.rows); |
| + unscaledBitmap.allocPixels(); |
| + SkMask dstMask; |
| + dstMask.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels()); |
| + dstMask.fRowBytes = unscaledBitmap.rowBytes(); |
| + dstMask.fFormat = skFormatForFTPixelMode(pixel_mode); |
| + copyFTBitmap(face->glyph->bitmap, dstMask); |
| + // wrap the destination SkGlyph's image data into a bitmap |
| + SkBitmap dstBitmap; |
| + SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat); |
| + dstBitmap.setConfig(skConfigForFormat(maskFormat), |
| + glyph.fWidth, glyph.fHeight, glyph.rowBytes()); |
| + 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); |
| + } else { |
| + // no scaling needed - directly copy glyph data |
| + SkMask dstMask; |
| + glyph.toMask(&dstMask); |
| + copyFTBitmap(face->glyph->bitmap, dstMask); |
| + } |
| } |
| - } break; |
| + 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, |