 Chromium Code Reviews
 Chromium Code Reviews Issue 341343002:
  Better rendering detection with DirectWrite.  (Closed) 
  Base URL: https://skia.googlesource.com/skia.git@master
    
  
    Issue 341343002:
  Better rendering detection with DirectWrite.  (Closed) 
  Base URL: https://skia.googlesource.com/skia.git@master| OLD | NEW | 
|---|---|
| 1 /* | 1 /* | 
| 2 * Copyright 2011 Google Inc. | 2 * Copyright 2011 Google Inc. | 
| 3 * | 3 * | 
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be | 
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. | 
| 6 */ | 6 */ | 
| 7 | 7 | 
| 8 #include "SkTypes.h" | 8 #include "SkTypes.h" | 
| 9 #undef GetGlyphIndices | 9 #undef GetGlyphIndices | 
| 10 | 10 | 
| 11 #include "SkDWrite.h" | 11 #include "SkDWrite.h" | 
| 12 #include "SkDWriteGeometrySink.h" | 12 #include "SkDWriteGeometrySink.h" | 
| 13 #include "SkEndian.h" | 13 #include "SkEndian.h" | 
| 14 #include "SkGlyph.h" | 14 #include "SkGlyph.h" | 
| 15 #include "SkHRESULT.h" | 15 #include "SkHRESULT.h" | 
| 16 #include "SkMaskGamma.h" | 16 #include "SkMaskGamma.h" | 
| 17 #include "SkMatrix22.h" | 17 #include "SkMatrix22.h" | 
| 18 #include "SkOTTable_EBLC.h" | 18 #include "SkOTTable_EBLC.h" | 
| 19 #include "SkOTTable_EBSC.h" | 19 #include "SkOTTable_EBSC.h" | 
| 20 #include "SkOTTable_gasp.h" | |
| 20 #include "SkPath.h" | 21 #include "SkPath.h" | 
| 21 #include "SkScalerContext.h" | 22 #include "SkScalerContext.h" | 
| 22 #include "SkScalerContext_win_dw.h" | 23 #include "SkScalerContext_win_dw.h" | 
| 23 #include "SkTScopedComPtr.h" | 24 #include "SkTScopedComPtr.h" | 
| 24 #include "SkTypeface_win_dw.h" | 25 #include "SkTypeface_win_dw.h" | 
| 25 | 26 | 
| 26 #include <dwrite.h> | 27 #include <dwrite.h> | 
| 27 | 28 | 
| 28 static bool isLCD(const SkScalerContext::Rec& rec) { | 29 static bool isLCD(const SkScalerContext::Rec& rec) { | 
| 29 return SkMask::kLCD16_Format == rec.fMaskFormat || | 30 return SkMask::kLCD16_Format == rec.fMaskFormat || | 
| 30 SkMask::kLCD32_Format == rec.fMaskFormat; | 31 SkMask::kLCD32_Format == rec.fMaskFormat; | 
| 31 } | 32 } | 
| 32 | 33 | 
| 33 static bool hasBitmapStrike(DWriteFontTypeface* typeface, int size) { | 34 /** A PPEMRange is inclusive, [min, max]. */ | 
| 35 struct PPEMRange { | |
| 36 int min; | |
| 37 int max; | |
| 38 }; | |
| 39 | |
| 40 /** If the rendering mode for the specified 'size' is gridfit, then place | |
| 41 * the gridfit range into 'range'. Otherwise, leave 'range' alone. | |
| 42 */ | |
| 43 static void expand_range_if_gridfit_only(DWriteFontTypeface* typeface, int size, PPEMRange* range) { | |
| 44 AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFa ce.get()); | |
| 45 if (!gasp.fExists) { | |
| 46 return; | |
| 47 } | |
| 48 if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) { | |
| 49 return; | |
| 50 } | |
| 51 if (gasp->version != SkOTTableGridAndScanProcedure::version0 && | |
| 52 gasp->version != SkOTTableGridAndScanProcedure::version1) | |
| 53 { | |
| 54 return ; | |
| 55 } | |
| 56 | |
| 57 uint16_t numRanges = SkEndianSwap16(gasp->numRanges); | |
| 58 if (numRanges > 1024 || | |
| 59 gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) + | |
| 60 sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRange s) | |
| 61 { | |
| 62 return; | |
| 63 } | |
| 64 | |
| 65 const SkOTTableGridAndScanProcedure::GaspRange* rangeTable = | |
| 66 SkTAfter<const SkOTTableGridAndScanProcedure::GaspRange>(gasp.get()) ; | |
| 67 int minPPEM = -1; | |
| 68 for (uint16_t i = 0; i < numRanges; ++i, ++rangeTable) { | |
| 69 int maxPPEM = SkEndianSwap16(rangeTable->maxPPEM); | |
| 70 // Test that the size is in range and the range is gridfit only. | |
| 71 if (minPPEM < size && size <= maxPPEM && | |
| 72 rangeTable->flags.raw.value == SkOTTableGridAndScanProcedure::GaspRa nge::behavior::Raw::GridfitMask) | |
| 73 { | |
| 74 range->min = minPPEM + 1; | |
| 75 range->max = maxPPEM; | |
| 76 return; | |
| 77 } | |
| 78 minPPEM = maxPPEM; | |
| 79 } | |
| 80 | |
| 81 return; | |
| 
mtklein
2014/06/23 15:20:34
This _may_ be redundant.
 | |
| 82 } | |
| 83 | |
| 84 static bool has_bitmap_strike(DWriteFontTypeface* typeface, PPEMRange range) { | |
| 34 { | 85 { | 
| 35 AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWrite FontFace.get()); | 86 AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWrite FontFace.get()); | 
| 36 if (!eblc.fExists) { | 87 if (!eblc.fExists) { | 
| 37 return false; | 88 return false; | 
| 38 } | 89 } | 
| 39 if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) { | 90 if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) { | 
| 40 return false; | 91 return false; | 
| 41 } | 92 } | 
| 42 if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) { | 93 if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) { | 
| 43 return false; | 94 return false; | 
| 44 } | 95 } | 
| 45 | 96 | 
| 46 uint32_t numSizes = SkEndianSwap32(eblc->numSizes); | 97 uint32_t numSizes = SkEndianSwap32(eblc->numSizes); | 
| 47 if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) + | 98 if (numSizes > 1024 || | 
| 99 eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) + | |
| 48 sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable ) * numSizes) | 100 sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable ) * numSizes) | 
| 49 { | 101 { | 
| 50 return false; | 102 return false; | 
| 51 } | 103 } | 
| 52 | 104 | 
| 53 const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable = | 105 const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable = | 
| 54 SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable> (eblc.get()); | 106 SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable> (eblc.get()); | 
| 55 for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) { | 107 for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) { | 
| 56 if (sizeTable->ppemX == size && sizeTable->ppemY == size) { | 108 if (sizeTable->ppemX == sizeTable->ppemY && | 
| 109 range.min <= sizeTable->ppemX && sizeTable->ppemX <= range.max) | |
| 110 { | |
| 57 // TODO: determine if we should dig through IndexSubTableArray/I ndexSubTable | 111 // TODO: determine if we should dig through IndexSubTableArray/I ndexSubTable | 
| 58 // to determine the actual number of glyphs with bitmaps. | 112 // to determine the actual number of glyphs with bitmaps. | 
| 59 | 113 | 
| 60 // TODO: Ensure that the bitmaps actually cover a significant po rtion of the strike. | 114 // TODO: Ensure that the bitmaps actually cover a significant po rtion of the strike. | 
| 61 | 115 | 
| 62 //TODO: Endure that the bitmaps are bi-level. | 116 // TODO: Ensure that the bitmaps are bi-level? | 
| 63 if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) { | 117 if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) { | 
| 64 return true; | 118 return true; | 
| 65 } | 119 } | 
| 66 } | 120 } | 
| 67 } | 121 } | 
| 68 } | 122 } | 
| 69 | 123 | 
| 70 { | 124 { | 
| 71 AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> ebsc(typeface->fDWriteF ontFace.get()); | 125 AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> ebsc(typeface->fDWriteF ontFace.get()); | 
| 72 if (!ebsc.fExists) { | 126 if (!ebsc.fExists) { | 
| 73 return false; | 127 return false; | 
| 74 } | 128 } | 
| 75 if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) { | 129 if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) { | 
| 76 return false; | 130 return false; | 
| 77 } | 131 } | 
| 78 if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) { | 132 if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) { | 
| 79 return false; | 133 return false; | 
| 80 } | 134 } | 
| 81 | 135 | 
| 82 uint32_t numSizes = SkEndianSwap32(ebsc->numSizes); | 136 uint32_t numSizes = SkEndianSwap32(ebsc->numSizes); | 
| 83 if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) + | 137 if (numSizes > 1024 || | 
| 138 ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) + | |
| 84 sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable ) * numSizes) | 139 sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable ) * numSizes) | 
| 85 { | 140 { | 
| 86 return false; | 141 return false; | 
| 87 } | 142 } | 
| 88 | 143 | 
| 89 const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable = | 144 const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable = | 
| 90 SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable> (ebsc.get()); | 145 SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable> (ebsc.get()); | 
| 91 for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) { | 146 for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) { | 
| 92 if (scaleTable->ppemX == size && scaleTable->ppemY == size) { | 147 if (scaleTable->ppemX == scaleTable->ppemY && | 
| 148 range.min <= scaleTable->ppemX && scaleTable->ppemX <= range.max ) { | |
| 93 // EBSC tables are normally only found in bitmap only fonts. | 149 // EBSC tables are normally only found in bitmap only fonts. | 
| 94 return true; | 150 return true; | 
| 95 } | 151 } | 
| 96 } | 152 } | 
| 97 } | 153 } | 
| 98 | 154 | 
| 99 return false; | 155 return false; | 
| 100 } | 156 } | 
| 101 | 157 | 
| 102 static bool bothZero(SkScalar a, SkScalar b) { | 158 static bool both_zero(SkScalar a, SkScalar b) { | 
| 103 return 0 == a && 0 == b; | 159 return 0 == a && 0 == b; | 
| 104 } | 160 } | 
| 105 | 161 | 
| 106 // returns false if there is any non-90-rotation or skew | 162 // returns false if there is any non-90-rotation or skew | 
| 107 static bool isAxisAligned(const SkScalerContext::Rec& rec) { | 163 static bool is_axis_aligned(const SkScalerContext::Rec& rec) { | 
| 108 return 0 == rec.fPreSkewX && | 164 return 0 == rec.fPreSkewX && | 
| 109 (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) || | 165 (both_zero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) || | 
| 110 bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1])); | 166 both_zero(rec.fPost2x2[0][0], rec.fPost2x2[1][1])); | 
| 111 } | 167 } | 
| 112 | 168 | 
| 113 SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface, | 169 SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface, | 
| 114 const SkDescriptor* desc) | 170 const SkDescriptor* desc) | 
| 115 : SkScalerContext(typeface, desc) | 171 : SkScalerContext(typeface, desc) | 
| 116 , fTypeface(SkRef(typeface)) | 172 , fTypeface(SkRef(typeface)) | 
| 117 , fGlyphCount(-1) { | 173 , fGlyphCount(-1) { | 
| 118 | 174 | 
| 119 // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC | 175 // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC | 
| 120 // except when bi-level rendering is requested or there are embedded | 176 // except when bi-level rendering is requested or there are embedded | 
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 152 // gdiTextSize is the size we request when GDI compatible. | 208 // gdiTextSize is the size we request when GDI compatible. | 
| 153 // If the scale is negative, this means the matrix will do the flip anyway. | 209 // If the scale is negative, this means the matrix will do the flip anyway. | 
| 154 SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY)); | 210 SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY)); | 
| 155 // Due to floating point math, the lower bits are suspect. Round carefully. | 211 // Due to floating point math, the lower bits are suspect. Round carefully. | 
| 156 SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f; | 212 SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f; | 
| 157 if (gdiTextSize == 0) { | 213 if (gdiTextSize == 0) { | 
| 158 gdiTextSize = SK_Scalar1; | 214 gdiTextSize = SK_Scalar1; | 
| 159 } | 215 } | 
| 160 | 216 | 
| 161 bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitm apText_Flag); | 217 bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitm apText_Flag); | 
| 162 bool hasBitmap = false; | 218 bool treatLikeBitmap = false; | 
| 163 bool axisAlignedBitmap = false; | 219 bool axisAlignedBitmap = false; | 
| 164 if (bitmapRequested) { | 220 if (bitmapRequested) { | 
| 165 hasBitmap = hasBitmapStrike(typeface, SkScalarTruncToInt(gdiTextSize)); | 221 // When embedded bitmaps are requested, treat the entire range like | 
| 166 axisAlignedBitmap = isAxisAligned(fRec); | 222 // a bitmap strike if the range is gridfit only and contains a bitmap. | 
| 223 int bitmapPPEM = SkScalarTruncToInt(gdiTextSize); | |
| 224 PPEMRange range = { bitmapPPEM, bitmapPPEM }; | |
| 225 #ifndef SK_IGNORE_DWRITE_RENDERING_FIX | |
| 226 expand_range_if_gridfit_only(typeface, bitmapPPEM, &range); | |
| 227 #endif | |
| 228 treatLikeBitmap = has_bitmap_strike(typeface, range); | |
| 229 | |
| 230 axisAlignedBitmap = is_axis_aligned(fRec); | |
| 167 } | 231 } | 
| 168 | 232 | 
| 169 // If the user requested aliased, do so with aliased compatible metrics. | 233 // If the user requested aliased, do so with aliased compatible metrics. | 
| 170 if (SkMask::kBW_Format == fRec.fMaskFormat) { | 234 if (SkMask::kBW_Format == fRec.fMaskFormat) { | 
| 171 fTextSizeRender = gdiTextSize; | 235 fTextSizeRender = gdiTextSize; | 
| 172 fRenderingMode = DWRITE_RENDERING_MODE_ALIASED; | 236 fRenderingMode = DWRITE_RENDERING_MODE_ALIASED; | 
| 173 fTextureType = DWRITE_TEXTURE_ALIASED_1x1; | 237 fTextureType = DWRITE_TEXTURE_ALIASED_1x1; | 
| 174 fTextSizeMeasure = gdiTextSize; | 238 fTextSizeMeasure = gdiTextSize; | 
| 175 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; | 239 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; | 
| 176 | 240 | 
| 177 // If we can use a bitmap, use gdi classic rendering and measurement. | 241 // If we can use a bitmap, use gdi classic rendering and measurement. | 
| 178 // This will not always provide a bitmap, but matches expected behavior. | 242 // This will not always provide a bitmap, but matches expected behavior. | 
| 179 } else if (hasBitmap && axisAlignedBitmap) { | 243 } else if (treatLikeBitmap && axisAlignedBitmap) { | 
| 180 fTextSizeRender = gdiTextSize; | 244 fTextSizeRender = gdiTextSize; | 
| 181 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC; | 245 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC; | 
| 182 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; | 246 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; | 
| 183 fTextSizeMeasure = gdiTextSize; | 247 fTextSizeMeasure = gdiTextSize; | 
| 184 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; | 248 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; | 
| 185 | 249 | 
| 186 // If rotated but the horizontal text could have used a bitmap, | 250 // If rotated but the horizontal text could have used a bitmap, | 
| 187 // render high quality rotated glyphs but measure using bitmap metrics. | 251 // render high quality rotated glyphs but measure using bitmap metrics. | 
| 188 } else if (hasBitmap) { | 252 } else if (treatLikeBitmap) { | 
| 189 fTextSizeRender = gdiTextSize; | 253 fTextSizeRender = gdiTextSize; | 
| 190 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; | 254 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; | 
| 191 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; | 255 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1; | 
| 192 fTextSizeMeasure = gdiTextSize; | 256 fTextSizeMeasure = gdiTextSize; | 
| 193 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; | 257 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; | 
| 194 | 258 | 
| 195 // The normal case is to use natural symmetric rendering and linear metrics. | 259 // The normal case is to use natural symmetric rendering and linear metrics. | 
| 196 } else { | 260 } else { | 
| 197 fTextSizeRender = realTextSize; | 261 fTextSizeRender = realTextSize; | 
| 198 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; | 262 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; | 
| (...skipping 411 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 610 NULL, //advances | 674 NULL, //advances | 
| 611 NULL, //offsets | 675 NULL, //offsets | 
| 612 1, //num glyphs | 676 1, //num glyphs | 
| 613 FALSE, //sideways | 677 FALSE, //sideways | 
| 614 FALSE, //rtl | 678 FALSE, //rtl | 
| 615 geometryToPath.get()), | 679 geometryToPath.get()), | 
| 616 "Could not create glyph outline."); | 680 "Could not create glyph outline."); | 
| 617 | 681 | 
| 618 path->transform(fSkXform); | 682 path->transform(fSkXform); | 
| 619 } | 683 } | 
| OLD | NEW |