1 /* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkTypes.h" 9 #undef GetGlyphIndices 10 11 #include "SkDWrite.h" 12 #include "SkDWriteGeometrySink.h" 13 #include "SkEndian.h" 14 #include "SkGlyph.h" 15 #include "SkHRESULT.h" 16 #include "SkMaskGamma.h" 17 #include "SkMatrix22.h" 18 #include "SkOTTable_EBLC.h" 19 #include "SkOTTable_EBSC.h" 20 #include "SkOTTable_gasp.h" 21 #include "SkOTTable_maxp.h" 22 #include "SkPath.h" 23 #include "SkScalerContext.h" 24 #include "SkScalerContext_win_dw.h" 25 #include "SkTScopedComPtr.h" 26 #include "SkTypeface_win_dw.h" 27 28 #include <dwrite.h> 29 #if SK_HAS_DWRITE_1_H 30 # include <dwrite_1.h> 31 #endif 32 33 static bool isLCD(const SkScalerContext::Rec& rec) { 34 return SkMask::kLCD16_Format == rec.fMaskFormat; 35 } 36 37 static bool is_hinted_without_gasp(DWriteFontTypeface* typeface) { 38 AutoTDWriteTable<SkOTTableMaximumProfile> maxp(typeface->fDWriteFontFace.get()); 39 if (!maxp.fExists) { 40 return false; 41 } 42 if (maxp.fSize < sizeof(SkOTTableMaximumProfile::Version::TT)) { 43 return false; 44 } 45 if (maxp->version.version != SkOTTableMaximumProfile::Version::TT::VERSION) { 46 return false; 47 } 48 49 if (0 == maxp->version.tt.maxSizeOfInstructions) { 50 // No hints. 51 return false; 52 } 53 54 AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get()); 55 return !gasp.fExists; 56 } 57 58 /** A PPEMRange is inclusive, [min, max]. */ 59 struct PPEMRange { 60 int min; 61 int max; 62 }; 63 64 /** If the rendering mode for the specified 'size' is gridfit, then place 65 * the gridfit range into 'range'. Otherwise, leave 'range' alone. 66 */ 67 static void expand_range_if_gridfit_only(DWriteFontTypeface* typeface, int size, PPEMRange* range) { 68 AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get()); 69 if (!gasp.fExists) { 70 return; 71 } 72 if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) { 73 return; 74 } 75 if (gasp->version != SkOTTableGridAndScanProcedure::version0 && 76 gasp->version != SkOTTableGridAndScanProcedure::version1) 77 { 78 return; 79 } 80 81 uint16_t numRanges = SkEndianSwap16(gasp->numRanges); 82 if (numRanges > 1024 || 83 gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) + 84 sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges) 85 { 86 return; 87 } 88 89 const SkOTTableGridAndScanProcedure::GaspRange* rangeTable = 90 SkTAfter<const SkOTTableGridAndScanProcedure::GaspRange>(gasp.get()); 91 int minPPEM = -1; 92 for (uint16_t i = 0; i < numRanges; ++i, ++rangeTable) { 93 int maxPPEM = SkEndianSwap16(rangeTable->maxPPEM); 94 // Test that the size is in range and the range is gridfit only. 95 if (minPPEM < size && size <= maxPPEM && 96 rangeTable->flags.raw.value == SkOTTableGridAndScanProcedure::GaspRange::behavior::Raw::GridfitMask) 97 { 98 range->min = minPPEM + 1; 99 range->max = maxPPEM; 100 return; 101 } 102 minPPEM = maxPPEM; 103 } 104 } 105 106 static bool has_bitmap_strike(DWriteFontTypeface* typeface, PPEMRange range) { 107 { 108 AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWriteFontFace.get()); 109 if (!eblc.fExists) { 110 return false; 111 } 112 if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) { 113 return false; 114 } 115 if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) { 116 return false; 117 } 118 119 uint32_t numSizes = SkEndianSwap32(eblc->numSizes); 120 if (numSizes > 1024 || 121 eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) + 122 sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes) 123 { 124 return false; 125 } 126 127 const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable = 128 SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable>(eblc.get()); 129 for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) { 130 if (sizeTable->ppemX == sizeTable->ppemY && 131 range.min <= sizeTable->ppemX && sizeTable->ppemX <= range.max) 132 { 133 // TODO: determine if we should dig through IndexSubTableArray/IndexSubTable 134 // to determine the actual number of glyphs with bitmaps. 135 136 // TODO: Ensure that the bitmaps actually cover a significant portion of the strike. 137 138 // TODO: Ensure that the bitmaps are bi-level? 139 if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) { 140 return true; 141 } 142 } 143 } 144 } 145 146 { 147 AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> ebsc(typeface->fDWriteFontFace.get()); 148 if (!ebsc.fExists) { 149 return false; 150 } 151 if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) { 152 return false; 153 } 154 if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) { 155 return false; 156 } 157 158 uint32_t numSizes = SkEndianSwap32(ebsc->numSizes); 159 if (numSizes > 1024 || 160 ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) + 161 sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes) 162 { 163 return false; 164 } 165 166 const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable = 167 SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable>(ebsc.get()); 168 for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) { 169 if (scaleTable->ppemX == scaleTable->ppemY && 170 range.min <= scaleTable->ppemX && scaleTable->ppemX <= range.max) { 171 // EBSC tables are normally only found in bitmap only fonts. 172 return true; 173 } 174 } 175 } 176 177 return false; 178 } 179 180 static bool both_zero(SkScalar a, SkScalar b) { 181 return 0 == a && 0 == b; 182 } 183 184 // returns false if there is any non-90-rotation or skew 185 static bool is_axis_aligned(const SkScalerContext::Rec& rec) { 186 return 0 == rec.fPreSkewX && 187 (both_zero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) || 188 both_zero(rec.fPost2x2[0][0], rec.fPost2x2[1][1])); 189 } 190 191 SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface, 192 const SkDescriptor* desc) 193 : SkScalerContext(typeface, desc) 194 , fTypeface(SkRef(typeface)) 195 , fGlyphCount(-1) { 196 197 // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC 198 // except when bi-level rendering is requested or there are embedded 199 // bi-level bitmaps (and the embedded bitmap flag is set and no rotation). 200 // 201 // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do 202 // this. As a result, determine the actual size of the text and then see if 203 // there are any embedded bi-level bitmaps of that size. If there are, then 204 // force bitmaps by requesting bi-level rendering. 205 // 206 // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes 207 // square pixels and only uses ppemY. Therefore the transform must track any 208 // non-uniform x-scale. 209 // 210 // Also, rotated glyphs should have the same absolute advance widths as 211 // horizontal glyphs and the subpixel flag should not affect glyph shapes. 212 213 SkVector scale; 214 SkMatrix GsA; 215 fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale, 216 &scale, &fSkXform, &GsA, &fG_inv); 217 218 fXform.m11 = SkScalarToFloat(fSkXform.getScaleX()); 219 fXform.m12 = SkScalarToFloat(fSkXform.getSkewY()); 220 fXform.m21 = SkScalarToFloat(fSkXform.getSkewX()); 221 fXform.m22 = SkScalarToFloat(fSkXform.getScaleY()); 222 fXform.dx = 0; 223 fXform.dy = 0; 224 225 fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX)); 226 fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0. 227 fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX)); 228 fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY)); 229 fGsA.dx = 0; 230 fGsA.dy = 0; 231 232 // realTextSize is the actual device size we want (as opposed to the size the user requested). 233 // gdiTextSize is the size we request when GDI compatible. 234 // If the scale is negative, this means the matrix will do the flip anyway. 235 const SkScalar realTextSize = scale.fY; 236 // Due to floating point math, the lower bits are suspect. Round carefully. 237 SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f; 238 if (gdiTextSize == 0) { 239 gdiTextSize = SK_Scalar1; 240 } 241 242 bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag); 243 bool treatLikeBitmap = false; 244 bool axisAlignedBitmap = false; 245 if (bitmapRequested) { 246 // When embedded bitmaps are requested, treat the entire range like 247 // a bitmap strike if the range is gridfit only and contains a bitmap. 248 int bitmapPPEM = SkScalarTruncToInt(gdiTextSize); 249 PPEMRange range = { bitmapPPEM, bitmapPPEM }; 250 expand_range_if_gridfit_only(typeface, bitmapPPEM, &range); 251 treatLikeBitmap = has_bitmap_strike(typeface, range); 252 253 axisAlignedBitmap = is_axis_aligned(fRec); 254 } 255 256 // If the user requested aliased, do so with aliased compatible metrics. 257 if (SkMask::kBW_Format == fRec.fMaskFormat) { 258 fTextSizeRender = gdiTextSize; 259 fRenderingMode = DWRITE_RENDERING_MODE_ALIASED; 260 fTextureType = DWRITE_TEXTURE_ALIASED_1x1; 261 fTextSizeMeasure = gdiTextSize; 262 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; 263 264 // If we can use a bitmap, use gdi classic rendering and measurement. 265 // This will not always provide a bitmap, but matches expected behavior. 266 } else if (treatLikeBitmap && axisAlignedBitmap) { 267 fTextSizeRender = gdiTextSize; 268 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC; 269 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; 270 fTextSizeMeasure = gdiTextSize; 271 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; 272 273 // If rotated but the horizontal text could have used a bitmap, 274 // render high quality rotated glyphs but measure using bitmap metrics. 275 } else if (treatLikeBitmap) { 276 fTextSizeRender = gdiTextSize; 277 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; 278 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; 279 fTextSizeMeasure = gdiTextSize; 280 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; 281 282 // Fonts that have hints but no gasp table get non-symmetric rendering. 283 // Usually such fonts have low quality hints which were never tested 284 // with anything but GDI ClearType classic. Such fonts often rely on 285 // drop out control in the y direction in order to be legible. 286 } else if (is_hinted_without_gasp(typeface)) { 287 fTextSizeRender = gdiTextSize; 288 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; 289 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; 290 fTextSizeMeasure = realTextSize; 291 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; 292 293 // The normal case is to use natural symmetric rendering and linear metrics. 294 } else { 295 fTextSizeRender = realTextSize; 296 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; 297 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; 298 fTextSizeMeasure = realTextSize; 299 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; 300 } 301 302 if (this->isSubpixel()) { 303 fTextSizeMeasure = realTextSize; 304 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL; 305 } 306 } 307 308 SkScalerContext_DW::~SkScalerContext_DW() { 309 } 310 311 unsigned SkScalerContext_DW::generateGlyphCount() { 312 if (fGlyphCount < 0) { 313 fGlyphCount = fTypeface->fDWriteFontFace->GetGlyphCount(); 314 } 315 return fGlyphCount; 316 } 317 318 uint16_t SkScalerContext_DW::generateCharToGlyph(SkUnichar uni) { 319 uint16_t index = 0; 320 fTypeface->fDWriteFontFace->GetGlyphIndices(reinterpret_cast<UINT32*>(&uni), 1, &index); 321 return index; 322 } 323 324 void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) { 325 //Delta is the difference between the right/left side bearing metric 326 //and where the right/left side bearing ends up after hinting. 327 //DirectWrite does not provide this information. 328 glyph->fRsbDelta = 0; 329 glyph->fLsbDelta = 0; 330 331 glyph->fAdvanceX = 0; 332 glyph->fAdvanceY = 0; 333 334 uint16_t glyphId = glyph->getGlyphID(); 335 DWRITE_GLYPH_METRICS gm; 336 337 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode || 338 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) 339 { 340 HRVM(fTypeface->fDWriteFontFace->GetGdiCompatibleGlyphMetrics( 341 fTextSizeMeasure, 342 1.0f, // pixelsPerDip 343 &fGsA, 344 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode, 345 &glyphId, 1, 346 &gm), 347 "Could not get gdi compatible glyph metrics."); 348 } else { 349 HRVM(fTypeface->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm), 350 "Could not get design metrics."); 351 } 352 353 DWRITE_FONT_METRICS dwfm; 354 fTypeface->fDWriteFontFace->GetMetrics(&dwfm); 355 SkScalar advanceX = SkScalarMulDiv(fTextSizeMeasure, 356 SkIntToScalar(gm.advanceWidth), 357 SkIntToScalar(dwfm.designUnitsPerEm)); 358 359 SkVector vecs[1] = { { advanceX, 0 } }; 360 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode || 361 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) 362 { 363 // DirectWrite produced 'compatible' metrics, but while close, 364 // the end result is not always an integer as it would be with GDI. 365 vecs[0].fX = SkScalarRoundToScalar(advanceX); 366 fG_inv.mapVectors(vecs, SK_ARRAY_COUNT(vecs)); 367 } else { 368 fSkXform.mapVectors(vecs, SK_ARRAY_COUNT(vecs)); 369 } 370 371 glyph->fAdvanceX = SkScalarToFixed(vecs[0].fX); 372 glyph->fAdvanceY = SkScalarToFixed(vecs[0].fY); 373 } 374 375 HRESULT SkScalerContext_DW::getBoundingBox(SkGlyph* glyph, 376 DWRITE_RENDERING_MODE renderingMode, 377 DWRITE_TEXTURE_TYPE textureType, 378 RECT* bbox) 379 { 380 //Measure raster size. 381 fXform.dx = SkFixedToFloat(glyph->getSubXFixed()); 382 fXform.dy = SkFixedToFloat(glyph->getSubYFixed()); 383 384 FLOAT advance = 0; 385 386 UINT16 glyphId = glyph->getGlyphID(); 387 388 DWRITE_GLYPH_OFFSET offset; 389 offset.advanceOffset = 0.0f; 390 offset.ascenderOffset = 0.0f; 391 392 DWRITE_GLYPH_RUN run; 393 run.glyphCount = 1; 394 run.glyphAdvances = &advance; 395 run.fontFace = fTypeface->fDWriteFontFace.get(); 396 run.fontEmSize = SkScalarToFloat(fTextSizeRender); 397 run.bidiLevel = 0; 398 run.glyphIndices = &glyphId; 399 run.isSideways = FALSE; 400 run.glyphOffsets = &offset; 401 402 SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis; 403 HRM(fTypeface->fFactory->CreateGlyphRunAnalysis( 404 &run, 405 1.0f, // pixelsPerDip, 406 &fXform, 407 renderingMode, 408 fMeasuringMode, 409 0.0f, // baselineOriginX, 410 0.0f, // baselineOriginY, 411 &glyphRunAnalysis), 412 "Could not create glyph run analysis."); 413 414 HRM(glyphRunAnalysis->GetAlphaTextureBounds(textureType, bbox), 415 "Could not get texture bounds."); 416 417 return S_OK; 418 } 419 420 /** GetAlphaTextureBounds succeeds but sometimes returns empty bounds like 421 * { 0x80000000, 0x80000000, 0x80000000, 0x80000000 } 422 * for small, but not quite zero, sized glyphs. 423 * Only set as non-empty if the returned bounds are non-empty. 424 */ 425 static bool glyph_check_and_set_bounds(SkGlyph* glyph, const RECT& bbox) { 426 if (bbox.left >= bbox.right || bbox.top >= bbox.bottom) { 427 return false; 428 } 429 glyph->fWidth = SkToU16(bbox.right - bbox.left); 430 glyph->fHeight = SkToU16(bbox.bottom - bbox.top); 431 glyph->fLeft = SkToS16(bbox.left); 432 glyph->fTop = SkToS16(bbox.top); 433 return true; 434 } 435 436 void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) { 437 glyph->fWidth = 0; 438 glyph->fHeight = 0; 439 glyph->fLeft = 0; 440 glyph->fTop = 0; 441 442 this->generateAdvance(glyph); 443 444 RECT bbox; 445 HRVM(this->getBoundingBox(glyph, fRenderingMode, fTextureType, &bbox), 446 "Requested bounding box could not be determined."); 447 448 if (glyph_check_and_set_bounds(glyph, bbox)) { 449 return; 450 } 451 452 // GetAlphaTextureBounds succeeds but returns an empty RECT if there are no 453 // glyphs of the specified texture type. When this happens, try with the 454 // alternate texture type. 455 if (DWRITE_TEXTURE_CLEARTYPE_3x1 == fTextureType) { 456 HRVM(this->getBoundingBox(glyph, 457 DWRITE_RENDERING_MODE_ALIASED, 458 DWRITE_TEXTURE_ALIASED_1x1, 459 &bbox), 460 "Fallback bounding box could not be determined."); 461 if (glyph_check_and_set_bounds(glyph, bbox)) { 462 glyph->fForceBW = 1; 463 } 464 } 465 // TODO: handle the case where a request for DWRITE_TEXTURE_ALIASED_1x1 466 // fails, and try DWRITE_TEXTURE_CLEARTYPE_3x1. 467 } 468 469 void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* metrics) { 470 if (NULL == metrics) { 471 return; 472 } 473 474 sk_bzero(metrics, sizeof(*metrics)); 475 476 DWRITE_FONT_METRICS dwfm; 477 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode || 478 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode) 479 { 480 fTypeface->fDWriteFontFace->GetGdiCompatibleMetrics( 481 fTextSizeRender, 482 1.0f, // pixelsPerDip 483 &fXform, 484 &dwfm); 485 } else { 486 fTypeface->fDWriteFontFace->GetMetrics(&dwfm); 487 } 488 489 SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm); 490 491 metrics->fAscent = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem; 492 metrics->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem; 493 metrics->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem; 494 metrics->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem; 495 metrics->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem; 496 metrics->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem); 497 498 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag; 499 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag; 500 501 #if SK_HAS_DWRITE_1_H 502 if (fTypeface->fDWriteFontFace1.get()) { 503 DWRITE_FONT_METRICS1 dwfm1; 504 fTypeface->fDWriteFontFace1->GetMetrics(&dwfm1); 505 metrics->fTop = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxTop) / upem; 506 metrics->fBottom = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxBottom) / upem; 507 metrics->fXMin = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxLeft) / upem; 508 metrics->fXMax = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxRight) / upem; 509 510 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin; 511 return; 512 } 513 #else 514 # pragma message("No dwrite_1.h is available, font metrics may be affected.") 515 #endif 516 517 AutoTDWriteTable<SkOTTableHead> head(fTypeface->fDWriteFontFace.get()); 518 if (head.fExists && 519 head.fSize >= sizeof(SkOTTableHead) && 520 head->version == SkOTTableHead::version1) 521 { 522 metrics->fTop = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMax) / upem; 523 metrics->fBottom = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMin) / upem; 524 metrics->fXMin = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMin) / upem; 525 metrics->fXMax = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMax) / upem; 526 527 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin; 528 return; 529 } 530 531 metrics->fTop = metrics->fAscent; 532 metrics->fBottom = metrics->fDescent; 533 } 534 535 /////////////////////////////////////////////////////////////////////////////// 536 537 #include "SkColorPriv.h" 538 539 static void bilevel_to_bw(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph) { 540 const int width = glyph.fWidth; 541 const size_t dstRB = (width + 7) >> 3; 542 uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage); 543 544 int byteCount = width >> 3; 545 int bitCount = width & 7; 546 547 for (int y = 0; y < glyph.fHeight; ++y) { 548 if (byteCount > 0) { 549 for (int i = 0; i < byteCount; ++i) { 550 unsigned byte = 0; 551 byte |= src[0] & (1 << 7); 552 byte |= src[1] & (1 << 6); 553 byte |= src[2] & (1 << 5); 554 byte |= src[3] & (1 << 4); 555 byte |= src[4] & (1 << 3); 556 byte |= src[5] & (1 << 2); 557 byte |= src[6] & (1 << 1); 558 byte |= src[7] & (1 << 0); 559 dst[i] = byte; 560 src += 8; 561 } 562 } 563 if (bitCount > 0) { 564 unsigned byte = 0; 565 unsigned mask = 0x80; 566 for (int i = 0; i < bitCount; i++) { 567 byte |= (src[i]) & mask; 568 mask >>= 1; 569 } 570 dst[byteCount] = byte; 571 } 572 src += bitCount; 573 dst += dstRB; 574 } 575 } 576 577 template<bool APPLY_PREBLEND> 578 static void rgb_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* table8) { 579 const size_t dstRB = glyph.rowBytes(); 580 const U16CPU width = glyph.fWidth; 581 uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage); 582 583 for (U16CPU y = 0; y < glyph.fHeight; y++) { 584 for (U16CPU i = 0; i < width; i++) { 585 U8CPU r = *(src++); 586 U8CPU g = *(src++); 587 U8CPU b = *(src++); 588 dst[i] = sk_apply_lut_if<APPLY_PREBLEND>((r + g + b) / 3, table8); 589 } 590 dst = (uint8_t*)((char*)dst + dstRB); 591 } 592 } 593 594 template<bool APPLY_PREBLEND, bool RGB> 595 static void rgb_to_lcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, 596 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) { 597 const size_t dstRB = glyph.rowBytes(); 598 const U16CPU width = glyph.fWidth; 599 uint16_t* SK_RESTRICT dst = static_cast<uint16_t*>(glyph.fImage); 600 601 for (U16CPU y = 0; y < glyph.fHeight; y++) { 602 for (U16CPU i = 0; i < width; i++) { 603 U8CPU r, g, b; 604 if (RGB) { 605 r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR); 606 g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG); 607 b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB); 608 } else { 609 b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB); 610 g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG); 611 r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR); 612 } 613 dst[i] = SkPack888ToRGB16(r, g, b); 614 } 615 dst = (uint16_t*)((char*)dst + dstRB); 616 } 617 } 618 619 const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph, 620 DWRITE_RENDERING_MODE renderingMode, 621 DWRITE_TEXTURE_TYPE textureType) 622 { 623 int sizeNeeded = glyph.fWidth * glyph.fHeight; 624 if (DWRITE_RENDERING_MODE_ALIASED != renderingMode) { 625 sizeNeeded *= 3; 626 } 627 if (sizeNeeded > fBits.count()) { 628 fBits.setCount(sizeNeeded); 629 } 630 631 // erase 632 memset(fBits.begin(), 0, sizeNeeded); 633 634 fXform.dx = SkFixedToFloat(glyph.getSubXFixed()); 635 fXform.dy = SkFixedToFloat(glyph.getSubYFixed()); 636 637 FLOAT advance = 0.0f; 638 639 UINT16 index = glyph.getGlyphID(); 640 641 DWRITE_GLYPH_OFFSET offset; 642 offset.advanceOffset = 0.0f; 643 offset.ascenderOffset = 0.0f; 644 645 DWRITE_GLYPH_RUN run; 646 run.glyphCount = 1; 647 run.glyphAdvances = &advance; 648 run.fontFace = fTypeface->fDWriteFontFace.get(); 649 run.fontEmSize = SkScalarToFloat(fTextSizeRender); 650 run.bidiLevel = 0; 651 run.glyphIndices = &index; 652 run.isSideways = FALSE; 653 run.glyphOffsets = &offset; 654 655 SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis; 656 HRNM(fTypeface->fFactory->CreateGlyphRunAnalysis(&run, 657 1.0f, // pixelsPerDip, 658 &fXform, 659 renderingMode, 660 fMeasuringMode, 661 0.0f, // baselineOriginX, 662 0.0f, // baselineOriginY, 663 &glyphRunAnalysis), 664 "Could not create glyph run analysis."); 665 666 //NOTE: this assumes that the glyph has already been measured 667 //with an exact same glyph run analysis. 668 RECT bbox; 669 bbox.left = glyph.fLeft; 670 bbox.top = glyph.fTop; 671 bbox.right = glyph.fLeft + glyph.fWidth; 672 bbox.bottom = glyph.fTop + glyph.fHeight; 673 HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType, 674 &bbox, 675 fBits.begin(), 676 sizeNeeded), 677 "Could not draw mask."); 678 return fBits.begin(); 679 } 680 681 void SkScalerContext_DW::generateImage(const SkGlyph& glyph) { 682 //Create the mask. 683 DWRITE_RENDERING_MODE renderingMode = fRenderingMode; 684 DWRITE_TEXTURE_TYPE textureType = fTextureType; 685 if (glyph.fForceBW) { 686 renderingMode = DWRITE_RENDERING_MODE_ALIASED; 687 textureType = DWRITE_TEXTURE_ALIASED_1x1; 688 } 689 const void* bits = this->drawDWMask(glyph, renderingMode, textureType); 690 if (!bits) { 691 sk_bzero(glyph.fImage, glyph.computeImageSize()); 692 return; 693 } 694 695 //Copy the mask into the glyph. 696 const uint8_t* src = (const uint8_t*)bits; 697 if (DWRITE_RENDERING_MODE_ALIASED == renderingMode) { 698 bilevel_to_bw(src, glyph); 699 const_cast<SkGlyph&>(glyph).fMaskFormat = SkMask::kBW_Format; 700 } else if (!isLCD(fRec)) { 701 if (fPreBlend.isApplicable()) { 702 rgb_to_a8<true>(src, glyph, fPreBlend.fG); 703 } else { 704 rgb_to_a8<false>(src, glyph, fPreBlend.fG); 705 } 706 } else { 707 SkASSERT(SkMask::kLCD16_Format == glyph.fMaskFormat); 708 if (fPreBlend.isApplicable()) { 709 if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) { 710 rgb_to_lcd16<true, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); 711 } else { 712 rgb_to_lcd16<true, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); 713 } 714 } else { 715 if (fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag) { 716 rgb_to_lcd16<false, false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); 717 } else { 718 rgb_to_lcd16<false, true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); 719 } 720 } 721 } 722 } 723 724 void SkScalerContext_DW::generatePath(const SkGlyph& glyph, SkPath* path) { 725 SkASSERT(&glyph && path); 726 727 path->reset(); 728 729 SkTScopedComPtr<IDWriteGeometrySink> geometryToPath; 730 HRVM(SkDWriteGeometrySink::Create(path, &geometryToPath), 731 "Could not create geometry to path converter."); 732 uint16_t glyphId = glyph.getGlyphID(); 733 //TODO: convert to<->from DIUs? This would make a difference if hinting. 734 //It may not be needed, it appears that DirectWrite only hints at em size. 735 HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fTextSizeRender), 736 &glyphId, 737 NULL, //advances 738 NULL, //offsets 739 1, //num glyphs 740 FALSE, //sideways 741 FALSE, //rtl 742 geometryToPath.get()), 743 "Could not create glyph outline."); 744 745 path->transform(fSkXform); 746 } 747