Home | History | Annotate | Download | only in ports
      1 /*
      2  * Copyright 2006-2012 The Android Open Source Project
      3  * Copyright 2012 Mozilla Foundation
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 #include "SkBitmap.h"
     10 #include "SkCanvas.h"
     11 #include "SkColor.h"
     12 #include "SkColorPriv.h"
     13 #include "SkFDot6.h"
     14 #include "SkFontHost_FreeType_common.h"
     15 #include "SkPath.h"
     16 
     17 #include <ft2build.h>
     18 #include FT_FREETYPE_H
     19 #include FT_BITMAP_H
     20 #include FT_IMAGE_H
     21 #include FT_OUTLINE_H
     22 // In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file.
     23 #include FT_SYNTHESIS_H
     24 
     25 // FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA
     26 // were introduced in FreeType 2.5.0.
     27 // The following may be removed once FreeType 2.5.0 is required to build.
     28 #ifndef FT_LOAD_COLOR
     29 #    define FT_LOAD_COLOR ( 1L << 20 )
     30 #    define FT_PIXEL_MODE_BGRA 7
     31 #endif
     32 
     33 //#define SK_SHOW_TEXT_BLIT_COVERAGE
     34 
     35 static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
     36     switch (format) {
     37         case SkMask::kBW_Format:
     38             return FT_PIXEL_MODE_MONO;
     39         case SkMask::kA8_Format:
     40         default:
     41             return FT_PIXEL_MODE_GRAY;
     42     }
     43 }
     44 
     45 ///////////////////////////////////////////////////////////////////////////////
     46 
     47 static uint16_t packTriple(U8CPU r, U8CPU g, U8CPU b) {
     48 #ifdef SK_SHOW_TEXT_BLIT_COVERAGE
     49     r = SkTMax(r, (U8CPU)0x40);
     50     g = SkTMax(g, (U8CPU)0x40);
     51     b = SkTMax(b, (U8CPU)0x40);
     52 #endif
     53     return SkPack888ToRGB16(r, g, b);
     54 }
     55 
     56 static uint16_t grayToRGB16(U8CPU gray) {
     57 #ifdef SK_SHOW_TEXT_BLIT_COVERAGE
     58     gray = SkTMax(gray, (U8CPU)0x40);
     59 #endif
     60     return SkPack888ToRGB16(gray, gray, gray);
     61 }
     62 
     63 static int bittst(const uint8_t data[], int bitOffset) {
     64     SkASSERT(bitOffset >= 0);
     65     int lowBit = data[bitOffset >> 3] >> (~bitOffset & 7);
     66     return lowBit & 1;
     67 }
     68 
     69 /**
     70  *  Copies a FT_Bitmap into an SkMask with the same dimensions.
     71  *
     72  *  FT_PIXEL_MODE_MONO
     73  *  FT_PIXEL_MODE_GRAY
     74  *  FT_PIXEL_MODE_LCD
     75  *  FT_PIXEL_MODE_LCD_V
     76  */
     77 template<bool APPLY_PREBLEND>
     78 static void copyFT2LCD16(const FT_Bitmap& bitmap, const SkMask& mask, int lcdIsBGR,
     79                          const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB)
     80 {
     81     SkASSERT(SkMask::kLCD16_Format == mask.fFormat);
     82     if (FT_PIXEL_MODE_LCD != bitmap.pixel_mode) {
     83         SkASSERT(mask.fBounds.width() == static_cast<int>(bitmap.width));
     84     }
     85     if (FT_PIXEL_MODE_LCD_V != bitmap.pixel_mode) {
     86         SkASSERT(mask.fBounds.height() == static_cast<int>(bitmap.rows));
     87     }
     88 
     89     const uint8_t* src = bitmap.buffer;
     90     uint16_t* dst = reinterpret_cast<uint16_t*>(mask.fImage);
     91     const size_t dstRB = mask.fRowBytes;
     92 
     93     const int width = mask.fBounds.width();
     94     const int height = mask.fBounds.height();
     95 
     96     switch (bitmap.pixel_mode) {
     97         case FT_PIXEL_MODE_MONO:
     98             for (int y = height; y --> 0;) {
     99                 for (int x = 0; x < width; ++x) {
    100                     dst[x] = -bittst(src, x);
    101                 }
    102                 dst = (uint16_t*)((char*)dst + dstRB);
    103                 src += bitmap.pitch;
    104             }
    105             break;
    106         case FT_PIXEL_MODE_GRAY:
    107             for (int y = height; y --> 0;) {
    108                 for (int x = 0; x < width; ++x) {
    109                     dst[x] = grayToRGB16(src[x]);
    110                 }
    111                 dst = (uint16_t*)((char*)dst + dstRB);
    112                 src += bitmap.pitch;
    113             }
    114             break;
    115         case FT_PIXEL_MODE_LCD:
    116             SkASSERT(3 * mask.fBounds.width() == static_cast<int>(bitmap.width));
    117             for (int y = height; y --> 0;) {
    118                 const uint8_t* triple = src;
    119                 if (lcdIsBGR) {
    120                     for (int x = 0; x < width; x++) {
    121                         dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR),
    122                                             sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
    123                                             sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB));
    124                         triple += 3;
    125                     }
    126                 } else {
    127                     for (int x = 0; x < width; x++) {
    128                         dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR),
    129                                             sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
    130                                             sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB));
    131                         triple += 3;
    132                     }
    133                 }
    134                 src += bitmap.pitch;
    135                 dst = (uint16_t*)((char*)dst + dstRB);
    136             }
    137             break;
    138         case FT_PIXEL_MODE_LCD_V:
    139             SkASSERT(3 * mask.fBounds.height() == static_cast<int>(bitmap.rows));
    140             for (int y = height; y --> 0;) {
    141                 const uint8_t* srcR = src;
    142                 const uint8_t* srcG = srcR + bitmap.pitch;
    143                 const uint8_t* srcB = srcG + bitmap.pitch;
    144                 if (lcdIsBGR) {
    145                     SkTSwap(srcR, srcB);
    146                 }
    147                 for (int x = 0; x < width; x++) {
    148                     dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR),
    149                                         sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG),
    150                                         sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB));
    151                 }
    152                 src += 3 * bitmap.pitch;
    153                 dst = (uint16_t*)((char*)dst + dstRB);
    154             }
    155             break;
    156         default:
    157             SkDEBUGF(("FT_Pixel_Mode %d", bitmap.pixel_mode));
    158             SkDEBUGFAIL("unsupported FT_Pixel_Mode for LCD16");
    159             break;
    160     }
    161 }
    162 
    163 /**
    164  *  Copies a FT_Bitmap into an SkMask with the same dimensions.
    165  *
    166  *  Yes, No, Never Requested, Never Produced
    167  *
    168  *                        kBW kA8 k3D kARGB32 kLCD16
    169  *  FT_PIXEL_MODE_MONO     Y   Y  NR     N       Y
    170  *  FT_PIXEL_MODE_GRAY     N   Y  NR     N       Y
    171  *  FT_PIXEL_MODE_GRAY2   NP  NP  NR    NP      NP
    172  *  FT_PIXEL_MODE_GRAY4   NP  NP  NR    NP      NP
    173  *  FT_PIXEL_MODE_LCD     NP  NP  NR    NP      NP
    174  *  FT_PIXEL_MODE_LCD_V   NP  NP  NR    NP      NP
    175  *  FT_PIXEL_MODE_BGRA     N   N  NR     Y       N
    176  *
    177  *  TODO: All of these N need to be Y or otherwise ruled out.
    178  */
    179 static void copyFTBitmap(const FT_Bitmap& srcFTBitmap, SkMask& dstMask) {
    180     SkASSERT(dstMask.fBounds.width() == static_cast<int>(srcFTBitmap.width));
    181     SkASSERT(dstMask.fBounds.height() == static_cast<int>(srcFTBitmap.rows));
    182 
    183     const uint8_t* src = reinterpret_cast<const uint8_t*>(srcFTBitmap.buffer);
    184     const FT_Pixel_Mode srcFormat = static_cast<FT_Pixel_Mode>(srcFTBitmap.pixel_mode);
    185     // FT_Bitmap::pitch is an int and allowed to be negative.
    186     const int srcPitch = srcFTBitmap.pitch;
    187     const size_t srcRowBytes = SkTAbs(srcPitch);
    188 
    189     uint8_t* dst = dstMask.fImage;
    190     const SkMask::Format dstFormat = static_cast<SkMask::Format>(dstMask.fFormat);
    191     const size_t dstRowBytes = dstMask.fRowBytes;
    192 
    193     const size_t width = srcFTBitmap.width;
    194     const size_t height = srcFTBitmap.rows;
    195 
    196     if (SkMask::kLCD16_Format == dstFormat) {
    197         copyFT2LCD16<false>(srcFTBitmap, dstMask, false, nullptr, nullptr, nullptr);
    198         return;
    199     }
    200 
    201     if ((FT_PIXEL_MODE_MONO == srcFormat && SkMask::kBW_Format == dstFormat) ||
    202         (FT_PIXEL_MODE_GRAY == srcFormat && SkMask::kA8_Format == dstFormat))
    203     {
    204         size_t commonRowBytes = SkTMin(srcRowBytes, dstRowBytes);
    205         for (size_t y = height; y --> 0;) {
    206             memcpy(dst, src, commonRowBytes);
    207             src += srcPitch;
    208             dst += dstRowBytes;
    209         }
    210     } else if (FT_PIXEL_MODE_MONO == srcFormat && SkMask::kA8_Format == dstFormat) {
    211         for (size_t y = height; y --> 0;) {
    212             uint8_t byte = 0;
    213             int bits = 0;
    214             const uint8_t* src_row = src;
    215             uint8_t* dst_row = dst;
    216             for (size_t x = width; x --> 0;) {
    217                 if (0 == bits) {
    218                     byte = *src_row++;
    219                     bits = 8;
    220                 }
    221                 *dst_row++ = byte & 0x80 ? 0xff : 0x00;
    222                 bits--;
    223                 byte <<= 1;
    224             }
    225             src += srcPitch;
    226             dst += dstRowBytes;
    227         }
    228     } else if (FT_PIXEL_MODE_BGRA == srcFormat && SkMask::kARGB32_Format == dstFormat) {
    229         // FT_PIXEL_MODE_BGRA is pre-multiplied.
    230         for (size_t y = height; y --> 0;) {
    231             const uint8_t* src_row = src;
    232             SkPMColor* dst_row = reinterpret_cast<SkPMColor*>(dst);
    233             for (size_t x = 0; x < width; ++x) {
    234                 uint8_t b = *src_row++;
    235                 uint8_t g = *src_row++;
    236                 uint8_t r = *src_row++;
    237                 uint8_t a = *src_row++;
    238                 *dst_row++ = SkPackARGB32(a, r, g, b);
    239 #ifdef SK_SHOW_TEXT_BLIT_COVERAGE
    240                 *(dst_row-1) = SkFourByteInterp256(*(dst_row-1), SK_ColorWHITE, 0x40);
    241 #endif
    242             }
    243             src += srcPitch;
    244             dst += dstRowBytes;
    245         }
    246     } else {
    247         SkDEBUGF(("FT_Pixel_Mode %d, SkMask::Format %d\n", srcFormat, dstFormat));
    248         SkDEBUGFAIL("unsupported combination of FT_Pixel_Mode and SkMask::Format");
    249     }
    250 }
    251 
    252 static inline int convert_8_to_1(unsigned byte) {
    253     SkASSERT(byte <= 0xFF);
    254     // Arbitrary decision that making the cutoff at 1/4 instead of 1/2 in general looks better.
    255     return (byte >> 6) != 0;
    256 }
    257 
    258 static uint8_t pack_8_to_1(const uint8_t alpha[8]) {
    259     unsigned bits = 0;
    260     for (int i = 0; i < 8; ++i) {
    261         bits <<= 1;
    262         bits |= convert_8_to_1(alpha[i]);
    263     }
    264     return SkToU8(bits);
    265 }
    266 
    267 static void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) {
    268     const int height = mask.fBounds.height();
    269     const int width = mask.fBounds.width();
    270     const int octs = width >> 3;
    271     const int leftOverBits = width & 7;
    272 
    273     uint8_t* dst = mask.fImage;
    274     const int dstPad = mask.fRowBytes - SkAlign8(width)/8;
    275     SkASSERT(dstPad >= 0);
    276 
    277     const int srcPad = srcRB - width;
    278     SkASSERT(srcPad >= 0);
    279 
    280     for (int y = 0; y < height; ++y) {
    281         for (int i = 0; i < octs; ++i) {
    282             *dst++ = pack_8_to_1(src);
    283             src += 8;
    284         }
    285         if (leftOverBits > 0) {
    286             unsigned bits = 0;
    287             int shift = 7;
    288             for (int i = 0; i < leftOverBits; ++i, --shift) {
    289                 bits |= convert_8_to_1(*src++) << shift;
    290             }
    291             *dst++ = bits;
    292         }
    293         src += srcPad;
    294         dst += dstPad;
    295     }
    296 }
    297 
    298 inline SkMask::Format SkMaskFormat_for_SkColorType(SkColorType colorType) {
    299     switch (colorType) {
    300         case kAlpha_8_SkColorType:
    301             return SkMask::kA8_Format;
    302         case kN32_SkColorType:
    303             return SkMask::kARGB32_Format;
    304         default:
    305             SkDEBUGFAIL("unsupported SkBitmap::Config");
    306             return SkMask::kA8_Format;
    307     }
    308 }
    309 
    310 inline SkColorType SkColorType_for_FTPixelMode(FT_Pixel_Mode pixel_mode) {
    311     switch (pixel_mode) {
    312         case FT_PIXEL_MODE_MONO:
    313         case FT_PIXEL_MODE_GRAY:
    314             return kAlpha_8_SkColorType;
    315         case FT_PIXEL_MODE_BGRA:
    316             return kN32_SkColorType;
    317         default:
    318             SkDEBUGFAIL("unsupported FT_PIXEL_MODE");
    319             return kAlpha_8_SkColorType;
    320     }
    321 }
    322 
    323 inline SkColorType SkColorType_for_SkMaskFormat(SkMask::Format format) {
    324     switch (format) {
    325         case SkMask::kBW_Format:
    326         case SkMask::kA8_Format:
    327         case SkMask::kLCD16_Format:
    328             return kAlpha_8_SkColorType;
    329         case SkMask::kARGB32_Format:
    330             return kN32_SkColorType;
    331         default:
    332             SkDEBUGFAIL("unsupported destination SkBitmap::Config");
    333             return kAlpha_8_SkColorType;
    334     }
    335 }
    336 
    337 void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGlyph& glyph) {
    338     const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
    339     const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
    340 
    341     switch ( face->glyph->format ) {
    342         case FT_GLYPH_FORMAT_OUTLINE: {
    343             FT_Outline* outline = &face->glyph->outline;
    344             FT_BBox     bbox;
    345             FT_Bitmap   target;
    346 
    347             int dx = 0, dy = 0;
    348             if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
    349                 dx = SkFixedToFDot6(glyph.getSubXFixed());
    350                 dy = SkFixedToFDot6(glyph.getSubYFixed());
    351                 // negate dy since freetype-y-goes-up and skia-y-goes-down
    352                 dy = -dy;
    353             }
    354             FT_Outline_Get_CBox(outline, &bbox);
    355             /*
    356                 what we really want to do for subpixel is
    357                     offset(dx, dy)
    358                     compute_bounds
    359                     offset(bbox & !63)
    360                 but that is two calls to offset, so we do the following, which
    361                 achieves the same thing with only one offset call.
    362             */
    363             FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
    364                                           dy - ((bbox.yMin + dy) & ~63));
    365 
    366             if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
    367                 FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD);
    368                 SkMask mask;
    369                 glyph.toMask(&mask);
    370                 if (fPreBlend.isApplicable()) {
    371                     copyFT2LCD16<true>(face->glyph->bitmap, mask, doBGR,
    372                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
    373                 } else {
    374                     copyFT2LCD16<false>(face->glyph->bitmap, mask, doBGR,
    375                                         fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
    376                 }
    377             } else {
    378                 target.width = glyph.fWidth;
    379                 target.rows = glyph.fHeight;
    380                 target.pitch = glyph.rowBytes();
    381                 target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);
    382                 target.pixel_mode = compute_pixel_mode( (SkMask::Format)fRec.fMaskFormat);
    383                 target.num_grays = 256;
    384 
    385                 memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
    386                 FT_Outline_Get_Bitmap(face->glyph->library, outline, &target);
    387             }
    388         } break;
    389 
    390         case FT_GLYPH_FORMAT_BITMAP: {
    391             FT_Pixel_Mode pixel_mode = static_cast<FT_Pixel_Mode>(face->glyph->bitmap.pixel_mode);
    392             SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
    393 
    394             // Assume that the other formats do not exist.
    395             SkASSERT(FT_PIXEL_MODE_MONO == pixel_mode ||
    396                      FT_PIXEL_MODE_GRAY == pixel_mode ||
    397                      FT_PIXEL_MODE_BGRA == pixel_mode);
    398 
    399             // These are the only formats this ScalerContext should request.
    400             SkASSERT(SkMask::kBW_Format == maskFormat ||
    401                      SkMask::kA8_Format == maskFormat ||
    402                      SkMask::kARGB32_Format == maskFormat ||
    403                      SkMask::kLCD16_Format == maskFormat);
    404 
    405             if (fRec.fFlags & SkScalerContext::kEmbolden_Flag &&
    406                 !(face->style_flags & FT_STYLE_FLAG_BOLD))
    407             {
    408                 FT_GlyphSlot_Own_Bitmap(face->glyph);
    409                 FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap,
    410                                    kBitmapEmboldenStrength, 0);
    411             }
    412 
    413             // If no scaling needed, directly copy glyph bitmap.
    414             if (glyph.fWidth == face->glyph->bitmap.width &&
    415                 glyph.fHeight == face->glyph->bitmap.rows &&
    416                 glyph.fTop == -face->glyph->bitmap_top &&
    417                 glyph.fLeft == face->glyph->bitmap_left)
    418             {
    419                 SkMask dstMask;
    420                 glyph.toMask(&dstMask);
    421                 copyFTBitmap(face->glyph->bitmap, dstMask);
    422                 break;
    423             }
    424 
    425             // Otherwise, scale the bitmap.
    426 
    427             // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB)
    428             SkBitmap unscaledBitmap;
    429             unscaledBitmap.allocPixels(SkImageInfo::Make(face->glyph->bitmap.width,
    430                                                          face->glyph->bitmap.rows,
    431                                                          SkColorType_for_FTPixelMode(pixel_mode),
    432                                                          kPremul_SkAlphaType));
    433 
    434             SkMask unscaledBitmapAlias;
    435             unscaledBitmapAlias.fImage = reinterpret_cast<uint8_t*>(unscaledBitmap.getPixels());
    436             unscaledBitmapAlias.fBounds.set(0, 0, unscaledBitmap.width(), unscaledBitmap.height());
    437             unscaledBitmapAlias.fRowBytes = unscaledBitmap.rowBytes();
    438             unscaledBitmapAlias.fFormat = SkMaskFormat_for_SkColorType(unscaledBitmap.colorType());
    439             copyFTBitmap(face->glyph->bitmap, unscaledBitmapAlias);
    440 
    441             // Wrap the glyph's mask in a bitmap, unless the glyph's mask is BW or LCD.
    442             // BW requires an A8 target for resizing, which can then be down sampled.
    443             // LCD should use a 4x A8 target, which will then be down sampled.
    444             // For simplicity, LCD uses A8 and is replicated.
    445             int bitmapRowBytes = 0;
    446             if (SkMask::kBW_Format != maskFormat && SkMask::kLCD16_Format != maskFormat) {
    447                 bitmapRowBytes = glyph.rowBytes();
    448             }
    449             SkBitmap dstBitmap;
    450             dstBitmap.setInfo(SkImageInfo::Make(glyph.fWidth, glyph.fHeight,
    451                                                 SkColorType_for_SkMaskFormat(maskFormat),
    452                                                 kPremul_SkAlphaType),
    453                               bitmapRowBytes);
    454             if (SkMask::kBW_Format == maskFormat || SkMask::kLCD16_Format == maskFormat) {
    455                 dstBitmap.allocPixels();
    456             } else {
    457                 dstBitmap.setPixels(glyph.fImage);
    458             }
    459 
    460             // Scale unscaledBitmap into dstBitmap.
    461             SkCanvas canvas(dstBitmap);
    462             canvas.clear(SK_ColorTRANSPARENT);
    463             canvas.scale(SkIntToScalar(glyph.fWidth) / SkIntToScalar(face->glyph->bitmap.width),
    464                          SkIntToScalar(glyph.fHeight) / SkIntToScalar(face->glyph->bitmap.rows));
    465             SkPaint paint;
    466             paint.setFilterQuality(kMedium_SkFilterQuality);
    467             canvas.drawBitmap(unscaledBitmap, 0, 0, &paint);
    468 
    469             // If the destination is BW or LCD, convert from A8.
    470             if (SkMask::kBW_Format == maskFormat) {
    471                 // Copy the A8 dstBitmap into the A1 glyph.fImage.
    472                 SkMask dstMask;
    473                 glyph.toMask(&dstMask);
    474                 packA8ToA1(dstMask, dstBitmap.getAddr8(0, 0), dstBitmap.rowBytes());
    475             } else if (SkMask::kLCD16_Format == maskFormat) {
    476                 // Copy the A8 dstBitmap into the LCD16 glyph.fImage.
    477                 uint8_t* src = dstBitmap.getAddr8(0, 0);
    478                 uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage);
    479                 for (int y = dstBitmap.height(); y --> 0;) {
    480                     for (int x = 0; x < dstBitmap.width(); ++x) {
    481                         dst[x] = grayToRGB16(src[x]);
    482                     }
    483                     dst = (uint16_t*)((char*)dst + glyph.rowBytes());
    484                     src += dstBitmap.rowBytes();
    485                 }
    486             }
    487 
    488         } break;
    489 
    490         default:
    491             SkDEBUGFAIL("unknown glyph format");
    492             memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
    493             return;
    494     }
    495 
    496 // We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum,
    497 // it is optional
    498 #if defined(SK_GAMMA_APPLY_TO_A8)
    499     if (SkMask::kA8_Format == glyph.fMaskFormat && fPreBlend.isApplicable()) {
    500         uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
    501         unsigned rowBytes = glyph.rowBytes();
    502 
    503         for (int y = glyph.fHeight - 1; y >= 0; --y) {
    504             for (int x = glyph.fWidth - 1; x >= 0; --x) {
    505                 dst[x] = fPreBlend.fG[dst[x]];
    506             }
    507             dst += rowBytes;
    508         }
    509     }
    510 #endif
    511 }
    512 
    513 ///////////////////////////////////////////////////////////////////////////////
    514 
    515 static int move_proc(const FT_Vector* pt, void* ctx) {
    516     SkPath* path = (SkPath*)ctx;
    517     path->close();  // to close the previous contour (if any)
    518     path->moveTo(SkFDot6ToScalar(pt->x), -SkFDot6ToScalar(pt->y));
    519     return 0;
    520 }
    521 
    522 static int line_proc(const FT_Vector* pt, void* ctx) {
    523     SkPath* path = (SkPath*)ctx;
    524     path->lineTo(SkFDot6ToScalar(pt->x), -SkFDot6ToScalar(pt->y));
    525     return 0;
    526 }
    527 
    528 static int quad_proc(const FT_Vector* pt0, const FT_Vector* pt1,
    529                      void* ctx) {
    530     SkPath* path = (SkPath*)ctx;
    531     path->quadTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y),
    532                  SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y));
    533     return 0;
    534 }
    535 
    536 static int cubic_proc(const FT_Vector* pt0, const FT_Vector* pt1,
    537                       const FT_Vector* pt2, void* ctx) {
    538     SkPath* path = (SkPath*)ctx;
    539     path->cubicTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y),
    540                   SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y),
    541                   SkFDot6ToScalar(pt2->x), -SkFDot6ToScalar(pt2->y));
    542     return 0;
    543 }
    544 
    545 void SkScalerContext_FreeType_Base::generateGlyphPath(FT_Face face,
    546                                                       SkPath* path)
    547 {
    548     FT_Outline_Funcs    funcs;
    549 
    550     funcs.move_to   = move_proc;
    551     funcs.line_to   = line_proc;
    552     funcs.conic_to  = quad_proc;
    553     funcs.cubic_to  = cubic_proc;
    554     funcs.shift     = 0;
    555     funcs.delta     = 0;
    556 
    557     FT_Error err = FT_Outline_Decompose(&face->glyph->outline, &funcs, path);
    558 
    559     if (err != 0) {
    560         path->reset();
    561         return;
    562     }
    563 
    564     path->close();
    565 }
    566