| Index: src/ports/SkFontHost_FreeType_common.cpp
|
| diff --git a/src/ports/SkFontHost_FreeType_common.cpp b/src/ports/SkFontHost_FreeType_common.cpp
|
| index 2c486847b6d8f4e0fe9c0f108707bf5662756e1a..2b5f90d990eee36ba7860baeabbca76e850891ca 100644
|
| --- a/src/ports/SkFontHost_FreeType_common.cpp
|
| +++ b/src/ports/SkFontHost_FreeType_common.cpp
|
| @@ -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"
|
| @@ -60,7 +63,7 @@ static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap,
|
| 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 +71,8 @@ static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap,
|
| 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 +80,8 @@ static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap,
|
| 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,7 +118,124 @@ static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap,
|
| }
|
| 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, uint8_t* dst, uint8_t dstFormat,
|
| + size_t dstRowBytes) {
|
| + const uint8_t* src = reinterpret_cast<const uint8_t*>(srcFTBitmap.buffer);
|
| + 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;
|
| + }
|
| +#ifdef FT_LOAD_COLOR
|
| + } 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;
|
| + }
|
| +#endif
|
| + } else {
|
| + SkDEBUGFAIL("unsupported combination of FT_PIXEL_MODE and SkMask::Format");
|
| + }
|
| +}
|
| +
|
| +inline uint8_t skFormatForFTPixelMode(char pixel_mode) {
|
| + switch (pixel_mode) {
|
| + case FT_PIXEL_MODE_GRAY:
|
| + return SkMask::kA8_Format;
|
| + case FT_PIXEL_MODE_MONO:
|
| + return SkMask::kBW_Format;
|
| +#ifdef FT_LOAD_COLOR
|
| + case FT_PIXEL_MODE_BGRA:
|
| + return SkMask::kARGB32_Format;
|
| +#endif
|
| + default:
|
| + SkDEBUGFAIL("unsupported FT_PIXEL_MODE");
|
| + return SkMask::kA8_Format;
|
| + }
|
| +}
|
| +
|
| +inline SkBitmap::Config skConfigForFTPixelMode(char pixel_mode) {
|
| + switch (pixel_mode) {
|
| + case FT_PIXEL_MODE_GRAY:
|
| + return SkBitmap::kA8_Config;
|
| + case FT_PIXEL_MODE_MONO:
|
| + return SkBitmap::kA1_Config;
|
| +#ifdef FT_LOAD_COLOR
|
| + case FT_PIXEL_MODE_BGRA:
|
| + return SkBitmap::kARGB_8888_Config;
|
| +#endif
|
| + default:
|
| + SkDEBUGFAIL("unsupported FT_PIXEL_MODE");
|
| + return SkBitmap::kA8_Config;
|
| + }
|
| +}
|
| +
|
| +inline SkBitmap::Config skConfigForFormat(uint8_t format) {
|
| + switch (format) {
|
| + case SkMask::kA8_Format:
|
| + return SkBitmap::kA8_Config;
|
| + case SkMask::kBW_Format:
|
| + return SkBitmap::kA1_Config;
|
| +#ifdef FT_LOAD_COLOR
|
| + case SkMask::kARGB32_Format:
|
| + return SkBitmap::kARGB_8888_Config;
|
| +#endif
|
| + default:
|
| + SkDEBUGFAIL("unsupported FT_PIXEL_MODE");
|
| + return SkBitmap::kA8_Config;
|
| }
|
| }
|
|
|
| @@ -124,107 +244,74 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly
|
| 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));
|
| + 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);
|
| + }
|
|
|
| - 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);
|
| + 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 {
|
| - copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert,
|
| - fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
|
| + 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);
|
| }
|
| - } 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;
|
| + 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);
|
| }
|
| - 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,48 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly
|
| 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;
|
| + unscaledBitmap.setConfig(skConfigForFTPixelMode(face->glyph->bitmap.pixel_mode),
|
| + face->glyph->bitmap.width,
|
| + face->glyph->bitmap.rows);
|
| + unscaledBitmap.allocPixels();
|
| + copyFTBitmap(face->glyph->bitmap,
|
| + reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels()),
|
| + skFormatForFTPixelMode(face->glyph->bitmap.pixel_mode),
|
| + unscaledBitmap.rowBytes());
|
| + // wrap the destination SkGlyph's image data into a bitmap
|
| + SkBitmap dstBitmap;
|
| + dstBitmap.setConfig(skConfigForFormat(glyph.fMaskFormat),
|
| + 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
|
| + copyFTBitmap(face->glyph->bitmap, reinterpret_cast<uint8_t*>(glyph.fImage),
|
| + glyph.fMaskFormat, glyph.rowBytes());
|
| + }
|
| }
|
| - } 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,
|
|
|