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