aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ports
diff options
context:
space:
mode:
authorGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-12-10 18:09:36 +0000
committerGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-12-10 18:09:36 +0000
commitc9a8a7e23de576ac91e9b34a221382f7c0e69813 (patch)
tree765f14b890730d1f25546ec8a03b96d88a94c4f6 /src/ports
parent45dfe6bb55828b8e819933279edc815f2fd6229a (diff)
improve bitmap font support (FreeType only)
This commit improves SkFontHost_FreeType's support for bitmap fonts, adding a number of features: - Intelligent bitmap strike selection. - Inter-strike bitmap font scaling. - Colour bitmap font support (FreeType 2.5.0+). BUG= R=reed@google.com Review URL: https://codereview.chromium.org/23684041 git-svn-id: http://skia.googlecode.com/svn/trunk@12607 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/ports')
-rw-r--r--src/ports/SkFontConfigInterface_direct.cpp2
-rw-r--r--src/ports/SkFontHost_FreeType.cpp334
-rw-r--r--src/ports/SkFontHost_FreeType_common.cpp484
3 files changed, 596 insertions, 224 deletions
diff --git a/src/ports/SkFontConfigInterface_direct.cpp b/src/ports/SkFontConfigInterface_direct.cpp
index f1ac7342dc..13993f10c0 100644
--- a/src/ports/SkFontConfigInterface_direct.cpp
+++ b/src/ports/SkFontConfigInterface_direct.cpp
@@ -330,11 +330,13 @@ bool IsFallbackFontAllowed(const std::string& family) {
}
static bool valid_pattern(FcPattern* pattern) {
+#ifdef SK_FONT_CONFIG_ONLY_ALLOW_SCALABLE_FONTS
FcBool is_scalable;
if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch
|| !is_scalable) {
return false;
}
+#endif
// fontconfig can also return fonts which are unreadable
const char* c_filename = get_name(pattern, FC_FILE);
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index fd87a66dc6..745a58a996 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -58,6 +58,21 @@
#include <freetype/ftsynth.h>
#endif
+// 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
+
+// FT_HAS_COLOR and the corresponding FT_FACE_FLAG_COLOR
+// were introduced in FreeType 2.5.1
+// The following may be removed once FreeType 2.5.1 is required to build.
+#ifndef FT_HAS_COLOR
+# define FT_HAS_COLOR(face) false
+#endif
+
//#define ENABLE_GLYPH_SPEW // for tracing calls
//#define DUMP_STRIKE_CREATION
@@ -184,6 +199,7 @@ private:
SkFaceRec* fFaceRec;
FT_Face fFace; // reference to shared face in gFaceRecHead
FT_Size fFTSize; // our own copy
+ FT_Int fStrikeIndex;
SkFixed fScaleX, fScaleY;
FT_Matrix fMatrix22;
uint32_t fLoadGlyphFlags;
@@ -751,6 +767,50 @@ bool SkTypeface_FreeType::onGetKerningPairAdjustments(const uint16_t glyphs[],
return true;
}
+static FT_Int chooseBitmapStrike(FT_Face face, SkFixed scaleY) {
+ // early out if face is bad
+ if (face == NULL) {
+ SkDEBUGF(("chooseBitmapStrike aborted due to NULL face\n"));
+ return -1;
+ }
+ // determine target ppem
+ FT_Pos targetPPEM = SkFixedToFDot6(scaleY);
+ // find a bitmap strike equal to or just larger than the requested size
+ FT_Int chosenStrikeIndex = -1;
+ FT_Pos chosenPPEM = 0;
+ for (FT_Int strikeIndex = 0; strikeIndex < face->num_fixed_sizes; ++strikeIndex) {
+ FT_Pos thisPPEM = face->available_sizes[strikeIndex].y_ppem;
+ if (thisPPEM == targetPPEM) {
+ // exact match - our search stops here
+ chosenPPEM = thisPPEM;
+ chosenStrikeIndex = strikeIndex;
+ break;
+ } else if (chosenPPEM < targetPPEM) {
+ // attempt to increase chosenPPEM
+ if (thisPPEM > chosenPPEM) {
+ chosenPPEM = thisPPEM;
+ chosenStrikeIndex = strikeIndex;
+ }
+ } else {
+ // attempt to decrease chosenPPEM, but not below targetPPEM
+ if (thisPPEM < chosenPPEM && thisPPEM > targetPPEM) {
+ chosenPPEM = thisPPEM;
+ chosenStrikeIndex = strikeIndex;
+ }
+ }
+ }
+ if (chosenStrikeIndex != -1) {
+ // use the chosen strike
+ FT_Error err = FT_Select_Size(face, chosenStrikeIndex);
+ if (err != 0) {
+ SkDEBUGF(("FT_Select_Size(%s, %d) returned 0x%x\n", face->family_name,
+ chosenStrikeIndex, err));
+ chosenStrikeIndex = -1;
+ }
+ }
+ return chosenStrikeIndex;
+}
+
SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface,
const SkDescriptor* desc)
: SkScalerContext_FreeType_Base(typeface, desc) {
@@ -764,6 +824,7 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface,
++gFTCount;
// load the font file
+ fStrikeIndex = -1;
fFTSize = NULL;
fFace = NULL;
fFaceRec = ref_ft_face(typeface);
@@ -823,9 +884,9 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface,
fLCDIsVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
// compute the flags we send to Load_Glyph
+ bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag);
{
FT_Int32 loadFlags = FT_LOAD_DEFAULT;
- bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag);
if (SkMask::kBW_Format == fRec.fMaskFormat) {
// See http://code.google.com/p/chromium/issues/detail?id=43252#c24
@@ -883,42 +944,57 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface,
loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
}
+ loadFlags |= FT_LOAD_COLOR;
+
fLoadGlyphFlags = loadFlags;
- fDoLinearMetrics = linearMetrics;
}
- // now create the FT_Size
-
- {
- FT_Error err;
+ FT_Error err = FT_New_Size(fFace, &fFTSize);
+ if (err != 0) {
+ SkDEBUGF(("FT_New_Size returned %x for face %s\n", err, fFace->family_name));
+ fFace = NULL;
+ return;
+ }
+ err = FT_Activate_Size(fFTSize);
+ if (err != 0) {
+ SkDEBUGF(("FT_Activate_Size(%08x, 0x%x, 0x%x) returned 0x%x\n", fFace, fScaleX, fScaleY,
+ err));
+ fFTSize = NULL;
+ return;
+ }
- err = FT_New_Size(fFace, &fFTSize);
+ if (FT_IS_SCALABLE(fFace)) {
+ err = FT_Set_Char_Size(fFace, SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), 72, 72);
if (err != 0) {
- SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n",
- fFaceRec->fFontID, fScaleX, fScaleY, err));
+ SkDEBUGF(("FT_Set_CharSize(%08x, 0x%x, 0x%x) returned 0x%x\n",
+ fFace, fScaleX, fScaleY, err));
fFace = NULL;
return;
}
-
- err = FT_Activate_Size(fFTSize);
- if (err != 0) {
- SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
- fFaceRec->fFontID, fScaleX, fScaleY, err));
- fFTSize = NULL;
- }
-
- err = FT_Set_Char_Size( fFace,
- SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY),
- 72, 72);
- if (err != 0) {
- SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
- fFaceRec->fFontID, fScaleX, fScaleY, err));
- fFace = NULL;
- return;
+ FT_Set_Transform(fFace, &fMatrix22, NULL);
+ } else if (FT_HAS_FIXED_SIZES(fFace)) {
+ fStrikeIndex = chooseBitmapStrike(fFace, fScaleY);
+ if (fStrikeIndex == -1) {
+ SkDEBUGF(("no glyphs for font \"%s\" size %f?\n",
+ fFace->family_name, SkFixedToScalar(fScaleY)));
+ } else {
+ // FreeType does no provide linear metrics for bitmap fonts.
+ linearMetrics = false;
+
+ // FreeType documentation says:
+ // FT_LOAD_NO_BITMAP -- Ignore bitmap strikes when loading.
+ // Bitmap-only fonts ignore this flag.
+ //
+ // However, in FreeType 2.5.1 color bitmap only fonts do not ignore this flag.
+ // Force this flag off for bitmap only fonts.
+ fLoadGlyphFlags &= ~FT_LOAD_NO_BITMAP;
}
-
- FT_Set_Transform( fFace, &fMatrix22, NULL);
+ } else {
+ SkDEBUGF(("unknown kind of font \"%s\" size %f?\n",
+ fFace->family_name, SkFixedToScalar(fScaleY)));
}
+
+ fDoLinearMetrics = linearMetrics;
}
SkScalerContext_FreeType::~SkScalerContext_FreeType() {
@@ -932,7 +1008,6 @@ SkScalerContext_FreeType::~SkScalerContext_FreeType() {
unref_ft_face(fFace);
}
if (--gFTCount == 0) {
-// SkDEBUGF(("FT_Done_FreeType\n"));
FT_Done_FreeType(gFTLibrary);
SkDEBUGCODE(gFTLibrary = NULL;)
}
@@ -942,18 +1017,18 @@ SkScalerContext_FreeType::~SkScalerContext_FreeType() {
this face with other context (at different sizes).
*/
FT_Error SkScalerContext_FreeType::setupSize() {
- FT_Error err = FT_Activate_Size(fFTSize);
-
+ FT_Error err = FT_Activate_Size(fFTSize);
if (err != 0) {
SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n",
- fFaceRec->fFontID, fScaleX, fScaleY, err));
+ fFaceRec->fFontID, fScaleX, fScaleY, err));
fFTSize = NULL;
- } else {
- // seems we need to reset this every time (not sure why, but without it
- // I get random italics from some other fFTSize)
- FT_Set_Transform( fFace, &fMatrix22, NULL);
+ return err;
}
- return err;
+
+ // seems we need to reset this every time (not sure why, but without it
+ // I get random italics from some other fFTSize)
+ FT_Set_Transform(fFace, &fMatrix22, NULL);
+ return 0;
}
unsigned SkScalerContext_FreeType::generateGlyphCount() {
@@ -1063,6 +1138,17 @@ void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) {
}
}
+inline void scaleGlyphMetrics(SkGlyph& glyph, SkScalar scale) {
+ glyph.fWidth *= scale;
+ glyph.fHeight *= scale;
+ glyph.fTop *= scale;
+ glyph.fLeft *= scale;
+
+ SkFixed fixedScale = SkScalarToFixed(scale);
+ glyph.fAdvanceX = SkFixedMul(glyph.fAdvanceX, fixedScale);
+ glyph.fAdvanceY = SkFixedMul(glyph.fAdvanceY, fixedScale);
+}
+
void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
SkAutoMutexAcquire ac(gFTMutex);
@@ -1087,32 +1173,28 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
}
switch ( fFace->glyph->format ) {
- case FT_GLYPH_FORMAT_OUTLINE: {
- FT_BBox bbox;
-
+ case FT_GLYPH_FORMAT_OUTLINE:
if (0 == fFace->glyph->outline.n_contours) {
glyph->fWidth = 0;
glyph->fHeight = 0;
glyph->fTop = 0;
glyph->fLeft = 0;
- break;
- }
-
- if (fRec.fFlags & kEmbolden_Flag) {
- emboldenOutline(fFace, &fFace->glyph->outline);
- }
-
- getBBoxForCurrentGlyph(glyph, &bbox, true);
+ } else {
+ if (fRec.fFlags & kEmbolden_Flag && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
+ emboldenOutline(fFace, &fFace->glyph->outline);
+ }
- glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin));
- glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin));
- glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax));
- glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin));
+ FT_BBox bbox;
+ getBBoxForCurrentGlyph(glyph, &bbox, true);
- updateGlyphIfLCD(glyph);
+ glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin));
+ glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin));
+ glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax));
+ glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin));
+ updateGlyphIfLCD(glyph);
+ }
break;
- }
case FT_GLYPH_FORMAT_BITMAP:
if (fRec.fFlags & kEmbolden_Flag) {
@@ -1129,6 +1211,10 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
fFace->glyph->bitmap_top += SkFDot6Floor(vector.y);
}
+ if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
+ glyph->fMaskFormat = SkMask::kARGB32_Format;
+ }
+
glyph->fWidth = SkToU16(fFace->glyph->bitmap.width);
glyph->fHeight = SkToU16(fFace->glyph->bitmap.rows);
glyph->fTop = -SkToS16(fFace->glyph->bitmap_top);
@@ -1163,6 +1249,11 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
}
}
+ if (fFace->glyph->format == FT_GLYPH_FORMAT_BITMAP && fScaleY && fFace->size->metrics.y_ppem) {
+ // NOTE: both dimensions are scaled by y_ppem. this is WAI.
+ scaleGlyphMetrics(*glyph, SkScalarDiv(SkFixedToScalar(fScaleY),
+ SkIntToScalar(fFace->size->metrics.y_ppem)));
+ }
#ifdef ENABLE_GLYPH_SPEW
SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY));
@@ -1250,77 +1341,104 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
}
FT_Face face = fFace;
- int upem = face->units_per_EM;
- if (upem <= 0) {
- goto ERROR;
- }
-
- SkPoint pts[6];
- SkFixed ys[6];
+ SkScalar scaleX = fScale.x();
SkScalar scaleY = fScale.y();
- SkScalar mxy = fMatrix22Scalar.getSkewX();
- SkScalar myy = fMatrix22Scalar.getScaleY();
- SkScalar xmin = SkIntToScalar(face->bbox.xMin) / upem;
- SkScalar xmax = SkIntToScalar(face->bbox.xMax) / upem;
-
- int leading = face->height - (face->ascender + -face->descender);
- if (leading < 0) {
- leading = 0;
+ SkScalar mxy = fMatrix22Scalar.getSkewX() * scaleY;
+ SkScalar myy = fMatrix22Scalar.getScaleY() * scaleY;
+
+ // fetch units/EM from "head" table if needed (ie for bitmap fonts)
+ SkScalar upem = SkIntToScalar(face->units_per_EM);
+ if (!upem) {
+ TT_Header* ttHeader = (TT_Header*)FT_Get_Sfnt_Table(face, ft_sfnt_head);
+ if (ttHeader) {
+ upem = SkIntToScalar(ttHeader->Units_Per_EM);
+ }
}
- // Try to get the OS/2 table from the font. This contains the specific
- // average font width metrics which Windows uses.
+ // use the os/2 table as a source of reasonable defaults.
+ SkScalar x_height = 0.0f;
+ SkScalar avgCharWidth = 0.0f;
TT_OS2* os2 = (TT_OS2*) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
-
- ys[0] = -face->bbox.yMax;
- ys[1] = -face->ascender;
- ys[2] = -face->descender;
- ys[3] = -face->bbox.yMin;
- ys[4] = leading;
- ys[5] = os2 ? os2->xAvgCharWidth : 0;
-
- SkScalar x_height;
- if (os2 && os2->sxHeight) {
- x_height = fScale.x() * os2->sxHeight / upem;
- } else {
- const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x');
- if (x_glyph) {
- FT_BBox bbox;
- FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags);
- if (fRec.fFlags & kEmbolden_Flag) {
- emboldenOutline(fFace, &fFace->glyph->outline);
+ if (os2) {
+ x_height = scaleX * SkIntToScalar(os2->sxHeight) / upem;
+ avgCharWidth = SkIntToScalar(os2->xAvgCharWidth) / upem;
+ }
+
+ // pull from format-specific metrics as needed
+ SkScalar ascent, descent, leading, xmin, xmax, ymin, ymax;
+ if (face->face_flags & FT_FACE_FLAG_SCALABLE) { // scalable outline font
+ ascent = -SkIntToScalar(face->ascender) / upem;
+ descent = -SkIntToScalar(face->descender) / upem;
+ leading = SkIntToScalar(face->height + (face->descender - face->ascender)) / upem;
+ xmin = SkIntToScalar(face->bbox.xMin) / upem;
+ xmax = SkIntToScalar(face->bbox.xMax) / upem;
+ ymin = -SkIntToScalar(face->bbox.yMin) / upem;
+ ymax = -SkIntToScalar(face->bbox.yMax) / upem;
+ // we may be able to synthesize x_height from outline
+ if (!x_height) {
+ const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x');
+ if (x_glyph) {
+ FT_BBox bbox;
+ FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags);
+ if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
+ emboldenOutline(fFace, &fFace->glyph->outline);
+ }
+ FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
+ x_height = SkIntToScalar(bbox.yMax) / 64.0f;
}
- FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
- x_height = bbox.yMax / 64.0f;
- } else {
- x_height = 0;
}
+ } else if (fStrikeIndex != -1) { // bitmap strike metrics
+ SkScalar xppem = SkIntToScalar(face->size->metrics.x_ppem);
+ SkScalar yppem = SkIntToScalar(face->size->metrics.y_ppem);
+ ascent = -SkIntToScalar(face->size->metrics.ascender) / (yppem * 64.0f);
+ descent = -SkIntToScalar(face->size->metrics.descender) / (yppem * 64.0f);
+ leading = (SkIntToScalar(face->size->metrics.height) / (yppem * 64.0f))
+ + ascent - descent;
+ xmin = 0.0f;
+ xmax = SkIntToScalar(face->available_sizes[fStrikeIndex].width) / xppem;
+ ymin = descent + leading;
+ ymax = ascent - descent;
+ if (!x_height) {
+ x_height = -ascent;
+ }
+ if (!avgCharWidth) {
+ avgCharWidth = xmax - xmin;
+ }
+ } else {
+ goto ERROR;
+ }
+
+ // synthesize elements that were not provided by the os/2 table or format-specific metrics
+ if (!x_height) {
+ x_height = -ascent;
+ }
+ if (!avgCharWidth) {
+ avgCharWidth = xmax - xmin;
}
- // convert upem-y values into scalar points
- for (int i = 0; i < 6; i++) {
- SkScalar y = scaleY * ys[i] / upem;
- pts[i].set(y * mxy, y * myy);
+ // disallow negative linespacing
+ if (leading < 0.0f) {
+ leading = 0.0f;
}
if (mx) {
- mx->fTop = pts[0].fX;
- mx->fAscent = pts[1].fX;
- mx->fDescent = pts[2].fX;
- mx->fBottom = pts[3].fX;
- mx->fLeading = pts[4].fX;
- mx->fAvgCharWidth = pts[5].fX;
+ mx->fTop = ymax * mxy;
+ mx->fAscent = ascent * mxy;
+ mx->fDescent = descent * mxy;
+ mx->fBottom = ymin * mxy;
+ mx->fLeading = leading * mxy;
+ mx->fAvgCharWidth = avgCharWidth * mxy;
mx->fXMin = xmin;
mx->fXMax = xmax;
mx->fXHeight = x_height;
}
if (my) {
- my->fTop = pts[0].fY;
- my->fAscent = pts[1].fY;
- my->fDescent = pts[2].fY;
- my->fBottom = pts[3].fY;
- my->fLeading = pts[4].fY;
- my->fAvgCharWidth = pts[5].fY;
+ my->fTop = ymax * myy;
+ my->fAscent = ascent * myy;
+ my->fDescent = descent * myy;
+ my->fBottom = ymin * myy;
+ my->fLeading = leading * myy;
+ my->fAvgCharWidth = avgCharWidth * myy;
my->fXMin = xmin;
my->fXMax = xmax;
my->fXHeight = x_height;
diff --git a/src/ports/SkFontHost_FreeType_common.cpp b/src/ports/SkFontHost_FreeType_common.cpp
index 2c486847b6..065a83a41c 100644
--- a/src/ports/SkFontHost_FreeType_common.cpp
+++ b/src/ports/SkFontHost_FreeType_common.cpp
@@ -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_OUTLINE_H
+#include FT_FREETYPE_H
#include FT_BITMAP_H
+#include FT_IMAGE_H
+#include FT_OUTLINE_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 FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
///////////////////////////////////////////////////////////////////////////////
-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,78 +66,271 @@ static int bittst(const uint8_t data[], int bitOffset) {
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>(*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[2], tableR),
+ sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
+ sk_apply_lut_if<APPLY_PREBLEND>(triple[0], 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;
- }
+ } 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;
}
}
@@ -129,7 +344,8 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly
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 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly
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 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly
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 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly
} 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 (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 (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);
- }
+ // 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;
+ }
+
+ // Otherwise, scale the bitmap.
+
+ // 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();
+
+ 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);
+
+ // 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 {
- SkDEBUGFAIL("unknown glyph bitmap transform needed");
+ 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]);
+ }
+ dst = (uint16_t*)((char*)dst + glyph.rowBytes());
+ src += dstBitmap.rowBytes();
+ }
+ }
+
} 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,