| 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,
|
|
|