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