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
35static 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
47static 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
56static 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
63static 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 */
77template<bool APPLY_PREBLEND>
78static 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() == bitmap.width);
84    }
85    if (FT_PIXEL_MODE_LCD_V != bitmap.pixel_mode) {
86        SkASSERT(mask.fBounds.height() == 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() == 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() == 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 kLCD32
169 *  FT_PIXEL_MODE_MONO     Y   Y  NR     N       Y     NR
170 *  FT_PIXEL_MODE_GRAY     N   Y  NR     N       Y     NR
171 *  FT_PIXEL_MODE_GRAY2   NP  NP  NR    NP      NP     NR
172 *  FT_PIXEL_MODE_GRAY4   NP  NP  NR    NP      NP     NR
173 *  FT_PIXEL_MODE_LCD     NP  NP  NR    NP      NP     NR
174 *  FT_PIXEL_MODE_LCD_V   NP  NP  NR    NP      NP     NR
175 *  FT_PIXEL_MODE_BGRA     N   N  NR     Y       N     NR
176 *
177 *  TODO: All of these N need to be Y or otherwise ruled out.
178 */
179static void copyFTBitmap(const FT_Bitmap& srcFTBitmap, SkMask& dstMask) {
180    SkASSERT(dstMask.fBounds.width() == srcFTBitmap.width);
181    SkASSERT(dstMask.fBounds.height() == 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, NULL, NULL, NULL);
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
252static 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
258static 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
267static 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
298inline 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
310inline 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
323inline 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
337void 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.setFilterLevel(SkPaint::kMedium_FilterLevel);
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
515static 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
522static 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
528static 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
536static 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
545void 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