Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(558)

Unified Diff: src/ports/SkFontHost_FreeType_common.cpp

Issue 23684041: improve bitmap font support (FreeType only) (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: fix silly transcription error Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« src/ports/SkFontHost_FreeType.cpp ('K') | « src/ports/SkFontHost_FreeType.cpp ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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,
reed1 2013/11/13 20:21:46 Why are we passing uint8_t for the format type, in
reed1 2013/11/13 20:21:46 Since we are copying into a bitmap, why not pass i
violets 2013/11/19 18:38:06 SkGlyph::fMaskFormat, the value being passed into
violets 2013/11/19 18:38:06 One of the two calls to this function passes in gl
reed1 2013/11/20 21:52:12 I think that may be worth it, allowing us to have
reed1 2013/11/20 21:52:12 Ah, good point. Creating a bitmap just for that do
+ 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) {
reed1 2013/11/13 20:21:46 1. Shouldn't this return SkMask::Format, instead o
violets 2013/11/19 18:38:06 1. I think that this should return the same type a
reed1 2013/11/20 21:52:12 I still vote for a named type, at least for the re
+ 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) {
reed1 2013/11/13 20:21:46 What is the type of pixel_mode? Is there an enum f
violets 2013/11/19 18:38:06 pixel_mode is type "char". There is an associated
+ 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) {
reed1 2013/11/13 20:21:46 Can we declare format to be SkMask::Format ?
violets 2013/11/19 18:38:06 We can, but the uint8_t data that we pass into thi
reed1 2013/11/20 21:52:12 I do. I'm also fine if we had a helper to SkGlyph
+ 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:
+ {
bungeman-skia 2013/11/15 00:19:48 This introduces an unnecessary amount of indentati
violets 2013/11/19 18:38:06 Done.
+ 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,
« src/ports/SkFontHost_FreeType.cpp ('K') | « src/ports/SkFontHost_FreeType.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698