blob: 7884b13259c42f1ef11eaded9792394aba6d75d0 [file] [log] [blame]
bungeman@google.come8f05922012-08-16 16:13:40 +00001/*
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
bungeman@google.com72cf4fc2014-03-21 22:48:32 +000011#include "SkDWrite.h"
bungeman@google.come8f05922012-08-16 16:13:40 +000012#include "SkDWriteGeometrySink.h"
bungeman@google.come8f05922012-08-16 16:13:40 +000013#include "SkEndian.h"
bungeman@google.come8f05922012-08-16 16:13:40 +000014#include "SkGlyph.h"
15#include "SkHRESULT.h"
16#include "SkMaskGamma.h"
bungeman@google.com058670b2014-05-01 20:39:14 +000017#include "SkMatrix22.h"
bungeman@google.com058670b2014-05-01 20:39:14 +000018#include "SkOTTable_EBLC.h"
19#include "SkOTTable_EBSC.h"
bungeman740c3f12014-06-23 08:29:23 -070020#include "SkOTTable_gasp.h"
bungeman761b2502014-06-30 12:19:41 -070021#include "SkOTTable_maxp.h"
bungeman@google.come8f05922012-08-16 16:13:40 +000022#include "SkPath.h"
bungeman51daa252014-06-05 13:38:45 -070023#include "SkScalerContext.h"
24#include "SkScalerContext_win_dw.h"
bungeman@google.come8f05922012-08-16 16:13:40 +000025#include "SkTScopedComPtr.h"
bungeman51daa252014-06-05 13:38:45 -070026#include "SkTypeface_win_dw.h"
bungeman@google.come8f05922012-08-16 16:13:40 +000027
28#include <dwrite.h>
bungemanf5484442014-09-10 07:49:05 -070029#if SK_HAS_DWRITE_1_H
30# include <dwrite_1.h>
31#endif
bungeman@google.come8f05922012-08-16 16:13:40 +000032
bungeman@google.come8f05922012-08-16 16:13:40 +000033static bool isLCD(const SkScalerContext::Rec& rec) {
34 return SkMask::kLCD16_Format == rec.fMaskFormat ||
35 SkMask::kLCD32_Format == rec.fMaskFormat;
36}
37
bungeman761b2502014-06-30 12:19:41 -070038static bool is_hinted_without_gasp(DWriteFontTypeface* typeface) {
39 AutoTDWriteTable<SkOTTableMaximumProfile> maxp(typeface->fDWriteFontFace.get());
40 if (!maxp.fExists) {
41 return false;
42 }
43 if (maxp.fSize < sizeof(SkOTTableMaximumProfile::Version::TT)) {
44 return false;
45 }
46 if (maxp->version.version != SkOTTableMaximumProfile::Version::TT::VERSION) {
47 return false;
48 }
49
50 if (0 == maxp->version.tt.maxSizeOfInstructions) {
51 // No hints.
52 return false;
53 }
54
55 AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
56 return !gasp.fExists;
57}
58
bungeman740c3f12014-06-23 08:29:23 -070059/** A PPEMRange is inclusive, [min, max]. */
60struct PPEMRange {
61 int min;
62 int max;
63};
64
65/** If the rendering mode for the specified 'size' is gridfit, then place
66 * the gridfit range into 'range'. Otherwise, leave 'range' alone.
67 */
68static void expand_range_if_gridfit_only(DWriteFontTypeface* typeface, int size, PPEMRange* range) {
69 AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
70 if (!gasp.fExists) {
71 return;
72 }
73 if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) {
74 return;
75 }
76 if (gasp->version != SkOTTableGridAndScanProcedure::version0 &&
77 gasp->version != SkOTTableGridAndScanProcedure::version1)
78 {
Ben Wagnerc83780c2014-06-23 13:53:26 -040079 return;
bungeman740c3f12014-06-23 08:29:23 -070080 }
81
82 uint16_t numRanges = SkEndianSwap16(gasp->numRanges);
83 if (numRanges > 1024 ||
84 gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) +
85 sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges)
86 {
87 return;
88 }
89
90 const SkOTTableGridAndScanProcedure::GaspRange* rangeTable =
91 SkTAfter<const SkOTTableGridAndScanProcedure::GaspRange>(gasp.get());
92 int minPPEM = -1;
93 for (uint16_t i = 0; i < numRanges; ++i, ++rangeTable) {
94 int maxPPEM = SkEndianSwap16(rangeTable->maxPPEM);
95 // Test that the size is in range and the range is gridfit only.
96 if (minPPEM < size && size <= maxPPEM &&
97 rangeTable->flags.raw.value == SkOTTableGridAndScanProcedure::GaspRange::behavior::Raw::GridfitMask)
98 {
99 range->min = minPPEM + 1;
100 range->max = maxPPEM;
101 return;
102 }
103 minPPEM = maxPPEM;
104 }
bungeman740c3f12014-06-23 08:29:23 -0700105}
106
107static bool has_bitmap_strike(DWriteFontTypeface* typeface, PPEMRange range) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000108 {
109 AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWriteFontFace.get());
110 if (!eblc.fExists) {
111 return false;
112 }
113 if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) {
114 return false;
115 }
116 if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) {
117 return false;
118 }
119
120 uint32_t numSizes = SkEndianSwap32(eblc->numSizes);
bungeman740c3f12014-06-23 08:29:23 -0700121 if (numSizes > 1024 ||
122 eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) +
bungeman@google.com058670b2014-05-01 20:39:14 +0000123 sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes)
124 {
125 return false;
126 }
127
128 const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable =
129 SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable>(eblc.get());
130 for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
bungeman740c3f12014-06-23 08:29:23 -0700131 if (sizeTable->ppemX == sizeTable->ppemY &&
132 range.min <= sizeTable->ppemX && sizeTable->ppemX <= range.max)
133 {
bungeman@google.com058670b2014-05-01 20:39:14 +0000134 // TODO: determine if we should dig through IndexSubTableArray/IndexSubTable
135 // to determine the actual number of glyphs with bitmaps.
136
137 // TODO: Ensure that the bitmaps actually cover a significant portion of the strike.
138
bungeman740c3f12014-06-23 08:29:23 -0700139 // TODO: Ensure that the bitmaps are bi-level?
bungeman@google.com058670b2014-05-01 20:39:14 +0000140 if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) {
141 return true;
142 }
143 }
144 }
145 }
146
147 {
148 AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> ebsc(typeface->fDWriteFontFace.get());
149 if (!ebsc.fExists) {
150 return false;
151 }
152 if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) {
153 return false;
154 }
155 if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) {
156 return false;
157 }
158
159 uint32_t numSizes = SkEndianSwap32(ebsc->numSizes);
bungeman740c3f12014-06-23 08:29:23 -0700160 if (numSizes > 1024 ||
161 ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) +
bungeman@google.com058670b2014-05-01 20:39:14 +0000162 sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes)
163 {
164 return false;
165 }
166
167 const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable =
168 SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable>(ebsc.get());
169 for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
bungeman740c3f12014-06-23 08:29:23 -0700170 if (scaleTable->ppemX == scaleTable->ppemY &&
171 range.min <= scaleTable->ppemX && scaleTable->ppemX <= range.max) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000172 // EBSC tables are normally only found in bitmap only fonts.
173 return true;
174 }
175 }
176 }
177
178 return false;
179}
180
bungeman740c3f12014-06-23 08:29:23 -0700181static bool both_zero(SkScalar a, SkScalar b) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000182 return 0 == a && 0 == b;
183}
184
185// returns false if there is any non-90-rotation or skew
bungeman740c3f12014-06-23 08:29:23 -0700186static bool is_axis_aligned(const SkScalerContext::Rec& rec) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000187 return 0 == rec.fPreSkewX &&
bungeman740c3f12014-06-23 08:29:23 -0700188 (both_zero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
189 both_zero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
bungeman@google.com058670b2014-05-01 20:39:14 +0000190}
191
reed@google.com30ddd612013-07-30 17:47:39 +0000192SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
bungeman@google.com6eddc772014-03-31 19:18:07 +0000193 const SkDescriptor* desc)
reed@google.com0da48612013-03-19 16:06:52 +0000194 : SkScalerContext(typeface, desc)
195 , fTypeface(SkRef(typeface))
bungeman@google.come8f05922012-08-16 16:13:40 +0000196 , fGlyphCount(-1) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000197
bungeman@google.com058670b2014-05-01 20:39:14 +0000198 // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC
199 // except when bi-level rendering is requested or there are embedded
200 // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
201 //
202 // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
203 // this. As a result, determine the actual size of the text and then see if
204 // there are any embedded bi-level bitmaps of that size. If there are, then
205 // force bitmaps by requesting bi-level rendering.
206 //
207 // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes
208 // square pixels and only uses ppemY. Therefore the transform must track any
209 // non-uniform x-scale.
210 //
211 // Also, rotated glyphs should have the same absolute advance widths as
212 // horizontal glyphs and the subpixel flag should not affect glyph shapes.
bungeman@google.come8f05922012-08-16 16:13:40 +0000213
bungeman@google.com058670b2014-05-01 20:39:14 +0000214 // A is the total matrix.
215 SkMatrix A;
216 fRec.getSingleMatrix(&A);
217
218 // h is where A maps the horizontal baseline.
219 SkPoint h = SkPoint::Make(SK_Scalar1, 0);
220 A.mapPoints(&h, 1);
221
222 // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
223 SkMatrix G;
224 SkComputeGivensRotation(h, &G);
225
226 // GA is the matrix A with rotation removed.
227 SkMatrix GA(G);
228 GA.preConcat(A);
229
230 // realTextSize is the actual device size we want (as opposed to the size the user requested).
231 // gdiTextSize is the size we request when GDI compatible.
232 // If the scale is negative, this means the matrix will do the flip anyway.
233 SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
234 // Due to floating point math, the lower bits are suspect. Round carefully.
bungeman@google.com7c183512014-05-28 15:40:26 +0000235 SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
bungeman@google.com058670b2014-05-01 20:39:14 +0000236 if (gdiTextSize == 0) {
237 gdiTextSize = SK_Scalar1;
238 }
239
bungeman@google.com7c183512014-05-28 15:40:26 +0000240 bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag);
bungeman740c3f12014-06-23 08:29:23 -0700241 bool treatLikeBitmap = false;
bungeman@google.com7c183512014-05-28 15:40:26 +0000242 bool axisAlignedBitmap = false;
243 if (bitmapRequested) {
bungeman740c3f12014-06-23 08:29:23 -0700244 // When embedded bitmaps are requested, treat the entire range like
245 // a bitmap strike if the range is gridfit only and contains a bitmap.
246 int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
247 PPEMRange range = { bitmapPPEM, bitmapPPEM };
bungeman740c3f12014-06-23 08:29:23 -0700248 expand_range_if_gridfit_only(typeface, bitmapPPEM, &range);
bungeman740c3f12014-06-23 08:29:23 -0700249 treatLikeBitmap = has_bitmap_strike(typeface, range);
250
251 axisAlignedBitmap = is_axis_aligned(fRec);
bungeman@google.com7c183512014-05-28 15:40:26 +0000252 }
bungeman@google.com058670b2014-05-01 20:39:14 +0000253
bungeman@google.com7c183512014-05-28 15:40:26 +0000254 // If the user requested aliased, do so with aliased compatible metrics.
255 if (SkMask::kBW_Format == fRec.fMaskFormat) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000256 fTextSizeRender = gdiTextSize;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000257 fRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
258 fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
bungeman@google.com058670b2014-05-01 20:39:14 +0000259 fTextSizeMeasure = gdiTextSize;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000260 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
bungemandf1640d2014-06-04 15:19:47 -0700261
bungeman@google.com7c183512014-05-28 15:40:26 +0000262 // If we can use a bitmap, use gdi classic rendering and measurement.
263 // This will not always provide a bitmap, but matches expected behavior.
bungeman740c3f12014-06-23 08:29:23 -0700264 } else if (treatLikeBitmap && axisAlignedBitmap) {
bungeman@google.com7c183512014-05-28 15:40:26 +0000265 fTextSizeRender = gdiTextSize;
266 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
267 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
268 fTextSizeMeasure = gdiTextSize;
269 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
bungemandf1640d2014-06-04 15:19:47 -0700270
bungeman@google.com7c183512014-05-28 15:40:26 +0000271 // If rotated but the horizontal text could have used a bitmap,
272 // render high quality rotated glyphs but measure using bitmap metrics.
bungeman740c3f12014-06-23 08:29:23 -0700273 } else if (treatLikeBitmap) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000274 fTextSizeRender = gdiTextSize;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000275 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
276 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
bungeman@google.com058670b2014-05-01 20:39:14 +0000277 fTextSizeMeasure = gdiTextSize;
278 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
bungeman@google.com7c183512014-05-28 15:40:26 +0000279
bungeman761b2502014-06-30 12:19:41 -0700280 // Fonts that have hints but no gasp table get non-symmetric rendering.
281 // Usually such fonts have low quality hints which were never tested
282 // with anything but GDI ClearType classic. Such fonts often rely on
283 // drop out control in the y direction in order to be legible.
284 } else if (is_hinted_without_gasp(typeface)) {
285 fTextSizeRender = gdiTextSize;
286 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
287 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
288 fTextSizeMeasure = realTextSize;
289 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
290
bungeman@google.com7c183512014-05-28 15:40:26 +0000291 // The normal case is to use natural symmetric rendering and linear metrics.
bungeman@google.com058670b2014-05-01 20:39:14 +0000292 } else {
293 fTextSizeRender = realTextSize;
294 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
295 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
296 fTextSizeMeasure = realTextSize;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000297 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
298 }
299
300 if (this->isSubpixel()) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000301 fTextSizeMeasure = realTextSize;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000302 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
303 }
bungeman@google.com058670b2014-05-01 20:39:14 +0000304
305 // Remove the realTextSize, as that is the text height scale currently in A.
306 SkScalar scale = SkScalarInvert(realTextSize);
307
308 // fSkXform is the total matrix A without the text height scale.
309 fSkXform = A;
310 fSkXform.preScale(scale, scale); //remove the text height scale.
311
312 fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
313 fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
314 fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
315 fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
316 fXform.dx = 0;
317 fXform.dy = 0;
318
319 // GsA is the non-rotational part of A without the text height scale.
320 SkMatrix GsA(GA);
321 GsA.preScale(scale, scale); //remove text height scale, G is rotational so reorders with scale.
322
323 fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX));
324 fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
325 fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX));
326 fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY));
bungeman@google.come4ae0bc2014-05-21 19:44:39 +0000327 fGsA.dx = 0;
328 fGsA.dy = 0;
bungeman@google.com058670b2014-05-01 20:39:14 +0000329
330 // fG_inv is G inverse, which is fairly simple since G is 2x2 rotational.
331 fG_inv.setAll(G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX),
332 -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY),
333 G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2));
bungeman@google.come8f05922012-08-16 16:13:40 +0000334}
335
reed@google.com30ddd612013-07-30 17:47:39 +0000336SkScalerContext_DW::~SkScalerContext_DW() {
bungeman@google.come8f05922012-08-16 16:13:40 +0000337}
338
reed@google.com30ddd612013-07-30 17:47:39 +0000339unsigned SkScalerContext_DW::generateGlyphCount() {
bungeman@google.come8f05922012-08-16 16:13:40 +0000340 if (fGlyphCount < 0) {
341 fGlyphCount = fTypeface->fDWriteFontFace->GetGlyphCount();
342 }
343 return fGlyphCount;
344}
345
reed@google.com30ddd612013-07-30 17:47:39 +0000346uint16_t SkScalerContext_DW::generateCharToGlyph(SkUnichar uni) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000347 uint16_t index = 0;
348 fTypeface->fDWriteFontFace->GetGlyphIndices(reinterpret_cast<UINT32*>(&uni), 1, &index);
349 return index;
350}
351
reed@google.com30ddd612013-07-30 17:47:39 +0000352void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000353 //Delta is the difference between the right/left side bearing metric
354 //and where the right/left side bearing ends up after hinting.
355 //DirectWrite does not provide this information.
356 glyph->fRsbDelta = 0;
357 glyph->fLsbDelta = 0;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000358
bungeman@google.come8f05922012-08-16 16:13:40 +0000359 glyph->fAdvanceX = 0;
360 glyph->fAdvanceY = 0;
361
362 uint16_t glyphId = glyph->getGlyphID();
363 DWRITE_GLYPH_METRICS gm;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000364
365 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
366 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
367 {
368 HRVM(fTypeface->fDWriteFontFace->GetGdiCompatibleGlyphMetrics(
bungeman@google.com058670b2014-05-01 20:39:14 +0000369 fTextSizeMeasure,
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000370 1.0f, // pixelsPerDip
bungeman@google.com058670b2014-05-01 20:39:14 +0000371 &fGsA,
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000372 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode,
373 &glyphId, 1,
374 &gm),
375 "Could not get gdi compatible glyph metrics.");
376 } else {
377 HRVM(fTypeface->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm),
378 "Could not get design metrics.");
379 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000380
381 DWRITE_FONT_METRICS dwfm;
382 fTypeface->fDWriteFontFace->GetMetrics(&dwfm);
bungeman@google.com058670b2014-05-01 20:39:14 +0000383 SkScalar advanceX = SkScalarMulDiv(fTextSizeMeasure,
bungeman@google.come8f05922012-08-16 16:13:40 +0000384 SkIntToScalar(gm.advanceWidth),
385 SkIntToScalar(dwfm.designUnitsPerEm));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000386
bungeman@google.come8f05922012-08-16 16:13:40 +0000387 SkVector vecs[1] = { { advanceX, 0 } };
bungeman@google.com058670b2014-05-01 20:39:14 +0000388 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
389 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
390 {
bungeman761b2502014-06-30 12:19:41 -0700391 // DirectWrite produced 'compatible' metrics, but while close,
392 // the end result is not always an integer as it would be with GDI.
393 vecs[0].fX = SkScalarRoundToScalar(advanceX);
bungeman@google.com058670b2014-05-01 20:39:14 +0000394 fG_inv.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
395 } else {
396 fSkXform.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
397 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000398
399 glyph->fAdvanceX = SkScalarToFixed(vecs[0].fX);
400 glyph->fAdvanceY = SkScalarToFixed(vecs[0].fY);
401}
402
bungeman683a3762014-08-28 11:42:29 -0700403HRESULT SkScalerContext_DW::getBoundingBox(SkGlyph* glyph,
404 DWRITE_RENDERING_MODE renderingMode,
405 DWRITE_TEXTURE_TYPE textureType,
406 RECT* bbox)
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400407{
bungeman@google.come8f05922012-08-16 16:13:40 +0000408 //Measure raster size.
409 fXform.dx = SkFixedToFloat(glyph->getSubXFixed());
410 fXform.dy = SkFixedToFloat(glyph->getSubYFixed());
411
412 FLOAT advance = 0;
413
414 UINT16 glyphId = glyph->getGlyphID();
415
416 DWRITE_GLYPH_OFFSET offset;
417 offset.advanceOffset = 0.0f;
418 offset.ascenderOffset = 0.0f;
419
420 DWRITE_GLYPH_RUN run;
421 run.glyphCount = 1;
422 run.glyphAdvances = &advance;
423 run.fontFace = fTypeface->fDWriteFontFace.get();
bungeman@google.com058670b2014-05-01 20:39:14 +0000424 run.fontEmSize = SkScalarToFloat(fTextSizeRender);
bungeman@google.come8f05922012-08-16 16:13:40 +0000425 run.bidiLevel = 0;
426 run.glyphIndices = &glyphId;
427 run.isSideways = FALSE;
428 run.glyphOffsets = &offset;
429
bungeman@google.come8f05922012-08-16 16:13:40 +0000430 SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
bungeman683a3762014-08-28 11:42:29 -0700431 HRM(fTypeface->fFactory->CreateGlyphRunAnalysis(
432 &run,
433 1.0f, // pixelsPerDip,
434 &fXform,
435 renderingMode,
436 fMeasuringMode,
437 0.0f, // baselineOriginX,
438 0.0f, // baselineOriginY,
439 &glyphRunAnalysis),
440 "Could not create glyph run analysis.");
rmistry@google.comd6176b02012-08-23 18:14:13 +0000441
bungeman683a3762014-08-28 11:42:29 -0700442 HRM(glyphRunAnalysis->GetAlphaTextureBounds(textureType, bbox),
443 "Could not get texture bounds.");
444
445 return S_OK;
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400446}
447
bungeman683a3762014-08-28 11:42:29 -0700448/** GetAlphaTextureBounds succeeds but sometimes returns empty bounds like
449 * { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }
450 * for small, but not quite zero, sized glyphs.
451 * Only set as non-empty if the returned bounds are non-empty.
452 */
453static bool glyph_check_and_set_bounds(SkGlyph* glyph, const RECT& bbox) {
454 if (bbox.left >= bbox.right || bbox.top >= bbox.bottom) {
455 return false;
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400456 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000457 glyph->fWidth = SkToU16(bbox.right - bbox.left);
458 glyph->fHeight = SkToU16(bbox.bottom - bbox.top);
459 glyph->fLeft = SkToS16(bbox.left);
460 glyph->fTop = SkToS16(bbox.top);
bungeman683a3762014-08-28 11:42:29 -0700461 return true;
462}
463
464void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) {
465 glyph->fWidth = 0;
466 glyph->fHeight = 0;
467 glyph->fLeft = 0;
468 glyph->fTop = 0;
469
470 this->generateAdvance(glyph);
471
472 RECT bbox;
473 HRVM(this->getBoundingBox(glyph, fRenderingMode, fTextureType, &bbox),
474 "Requested bounding box could not be determined.");
475
476 if (glyph_check_and_set_bounds(glyph, bbox)) {
477 return;
478 }
479
480 // GetAlphaTextureBounds succeeds but returns an empty RECT if there are no
481 // glyphs of the specified texture type. When this happens, try with the
482 // alternate texture type.
483 if (DWRITE_TEXTURE_CLEARTYPE_3x1 == fTextureType) {
484 HRVM(this->getBoundingBox(glyph,
485 DWRITE_RENDERING_MODE_ALIASED,
486 DWRITE_TEXTURE_ALIASED_1x1,
487 &bbox),
488 "Fallback bounding box could not be determined.");
489 if (glyph_check_and_set_bounds(glyph, bbox)) {
490 glyph->fForceBW = 1;
491 }
492 }
493 // TODO: handle the case where a request for DWRITE_TEXTURE_ALIASED_1x1
494 // fails, and try DWRITE_TEXTURE_CLEARTYPE_3x1.
bungeman@google.come8f05922012-08-16 16:13:40 +0000495}
496
bungeman41078062014-07-07 08:16:37 -0700497void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* metrics) {
498 if (NULL == metrics) {
bungemanf73c2372014-07-23 13:31:06 -0700499 return;
bungeman41078062014-07-07 08:16:37 -0700500 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000501
bungeman41078062014-07-07 08:16:37 -0700502 sk_bzero(metrics, sizeof(*metrics));
bungeman@google.come1b9bad2013-08-27 21:51:37 +0000503
bungeman@google.come8f05922012-08-16 16:13:40 +0000504 DWRITE_FONT_METRICS dwfm;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000505 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
506 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
507 {
508 fTypeface->fDWriteFontFace->GetGdiCompatibleMetrics(
bungeman@google.com058670b2014-05-01 20:39:14 +0000509 fTextSizeRender,
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000510 1.0f, // pixelsPerDip
511 &fXform,
512 &dwfm);
513 } else {
514 fTypeface->fDWriteFontFace->GetMetrics(&dwfm);
515 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000516
bungeman@google.come1b9bad2013-08-27 21:51:37 +0000517 SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +0000518
bungemanf73c2372014-07-23 13:31:06 -0700519 metrics->fAscent = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem;
bungeman41078062014-07-07 08:16:37 -0700520 metrics->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem;
bungeman41078062014-07-07 08:16:37 -0700521 metrics->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem;
522 metrics->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem;
523 metrics->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem;
524 metrics->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem);
bungeman@google.come8f05922012-08-16 16:13:40 +0000525
bungeman41078062014-07-07 08:16:37 -0700526 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
527 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungemanf73c2372014-07-23 13:31:06 -0700528
bungemanf5484442014-09-10 07:49:05 -0700529#if SK_HAS_DWRITE_1_H
bsalomon49f085d2014-09-05 13:34:00 -0700530 if (fTypeface->fDWriteFontFace1.get()) {
bungemanf73c2372014-07-23 13:31:06 -0700531 DWRITE_FONT_METRICS1 dwfm1;
532 fTypeface->fDWriteFontFace1->GetMetrics(&dwfm1);
533 metrics->fTop = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxTop) / upem;
534 metrics->fBottom = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxBottom) / upem;
535 metrics->fXMin = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxLeft) / upem;
536 metrics->fXMax = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxRight) / upem;
537
538 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungemanf5484442014-09-10 07:49:05 -0700539 return;
bungemanf73c2372014-07-23 13:31:06 -0700540 }
bungemanf5484442014-09-10 07:49:05 -0700541#else
542# pragma message("No dwrite_1.h is available, font metrics may be affected.")
543#endif
544
545 AutoTDWriteTable<SkOTTableHead> head(fTypeface->fDWriteFontFace.get());
546 if (head.fExists &&
547 head.fSize >= sizeof(SkOTTableHead) &&
548 head->version == SkOTTableHead::version1)
549 {
550 metrics->fTop = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMax) / upem;
551 metrics->fBottom = -fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->yMin) / upem;
552 metrics->fXMin = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMin) / upem;
553 metrics->fXMax = fTextSizeRender * (int16_t)SkEndian_SwapBE16(head->xMax) / upem;
554
555 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
556 return;
557 }
558
559 metrics->fTop = metrics->fAscent;
560 metrics->fBottom = metrics->fDescent;
bungeman@google.come8f05922012-08-16 16:13:40 +0000561}
562
563///////////////////////////////////////////////////////////////////////////////
564
565#include "SkColorPriv.h"
566
567static void bilevel_to_bw(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph) {
568 const int width = glyph.fWidth;
569 const size_t dstRB = (width + 7) >> 3;
570 uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
571
572 int byteCount = width >> 3;
573 int bitCount = width & 7;
574
575 for (int y = 0; y < glyph.fHeight; ++y) {
576 if (byteCount > 0) {
577 for (int i = 0; i < byteCount; ++i) {
578 unsigned byte = 0;
579 byte |= src[0] & (1 << 7);
580 byte |= src[1] & (1 << 6);
581 byte |= src[2] & (1 << 5);
582 byte |= src[3] & (1 << 4);
583 byte |= src[4] & (1 << 3);
584 byte |= src[5] & (1 << 2);
585 byte |= src[6] & (1 << 1);
586 byte |= src[7] & (1 << 0);
587 dst[i] = byte;
588 src += 8;
589 }
590 }
591 if (bitCount > 0) {
592 unsigned byte = 0;
593 unsigned mask = 0x80;
594 for (int i = 0; i < bitCount; i++) {
595 byte |= (src[i]) & mask;
596 mask >>= 1;
597 }
598 dst[byteCount] = byte;
599 }
600 src += bitCount;
601 dst += dstRB;
602 }
603}
604
605template<bool APPLY_PREBLEND>
606static void rgb_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* table8) {
607 const size_t dstRB = glyph.rowBytes();
608 const U16CPU width = glyph.fWidth;
609 uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
610
611 for (U16CPU y = 0; y < glyph.fHeight; y++) {
612 for (U16CPU i = 0; i < width; i++) {
613 U8CPU r = *(src++);
614 U8CPU g = *(src++);
615 U8CPU b = *(src++);
616 dst[i] = sk_apply_lut_if<APPLY_PREBLEND>((r + g + b) / 3, table8);
617 }
618 dst = (uint8_t*)((char*)dst + dstRB);
619 }
620}
621
622template<bool APPLY_PREBLEND>
623static void rgb_to_lcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
624 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
625 const size_t dstRB = glyph.rowBytes();
626 const U16CPU width = glyph.fWidth;
627 uint16_t* SK_RESTRICT dst = static_cast<uint16_t*>(glyph.fImage);
628
629 for (U16CPU y = 0; y < glyph.fHeight; y++) {
630 for (U16CPU i = 0; i < width; i++) {
631 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
632 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
633 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
634 dst[i] = SkPack888ToRGB16(r, g, b);
635 }
636 dst = (uint16_t*)((char*)dst + dstRB);
637 }
638}
639
640template<bool APPLY_PREBLEND>
641static void rgb_to_lcd32(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
642 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
643 const size_t dstRB = glyph.rowBytes();
644 const U16CPU width = glyph.fWidth;
645 SkPMColor* SK_RESTRICT dst = static_cast<SkPMColor*>(glyph.fImage);
646
647 for (U16CPU y = 0; y < glyph.fHeight; y++) {
648 for (U16CPU i = 0; i < width; i++) {
649 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
650 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
651 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
652 dst[i] = SkPackARGB32(0xFF, r, g, b);
653 }
654 dst = (SkPMColor*)((char*)dst + dstRB);
655 }
656}
657
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400658const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph,
659 DWRITE_RENDERING_MODE renderingMode,
660 DWRITE_TEXTURE_TYPE textureType)
661{
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000662 int sizeNeeded = glyph.fWidth * glyph.fHeight;
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400663 if (DWRITE_RENDERING_MODE_ALIASED != renderingMode) {
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000664 sizeNeeded *= 3;
665 }
666 if (sizeNeeded > fBits.count()) {
667 fBits.setCount(sizeNeeded);
668 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000669
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000670 // erase
671 memset(fBits.begin(), 0, sizeNeeded);
672
673 fXform.dx = SkFixedToFloat(glyph.getSubXFixed());
674 fXform.dy = SkFixedToFloat(glyph.getSubYFixed());
675
676 FLOAT advance = 0.0f;
677
678 UINT16 index = glyph.getGlyphID();
679
680 DWRITE_GLYPH_OFFSET offset;
681 offset.advanceOffset = 0.0f;
682 offset.ascenderOffset = 0.0f;
683
684 DWRITE_GLYPH_RUN run;
685 run.glyphCount = 1;
686 run.glyphAdvances = &advance;
687 run.fontFace = fTypeface->fDWriteFontFace.get();
bungeman@google.com058670b2014-05-01 20:39:14 +0000688 run.fontEmSize = SkScalarToFloat(fTextSizeRender);
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000689 run.bidiLevel = 0;
690 run.glyphIndices = &index;
691 run.isSideways = FALSE;
692 run.glyphOffsets = &offset;
693
694 SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
695 HRNM(fTypeface->fFactory->CreateGlyphRunAnalysis(&run,
696 1.0f, // pixelsPerDip,
697 &fXform,
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400698 renderingMode,
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000699 fMeasuringMode,
700 0.0f, // baselineOriginX,
701 0.0f, // baselineOriginY,
702 &glyphRunAnalysis),
703 "Could not create glyph run analysis.");
704
705 //NOTE: this assumes that the glyph has already been measured
706 //with an exact same glyph run analysis.
707 RECT bbox;
708 bbox.left = glyph.fLeft;
709 bbox.top = glyph.fTop;
710 bbox.right = glyph.fLeft + glyph.fWidth;
711 bbox.bottom = glyph.fTop + glyph.fHeight;
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400712 HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType,
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000713 &bbox,
714 fBits.begin(),
715 sizeNeeded),
716 "Could not draw mask.");
717 return fBits.begin();
718}
719
720void SkScalerContext_DW::generateImage(const SkGlyph& glyph) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000721 //Create the mask.
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400722 DWRITE_RENDERING_MODE renderingMode = fRenderingMode;
723 DWRITE_TEXTURE_TYPE textureType = fTextureType;
724 if (glyph.fForceBW) {
725 renderingMode = DWRITE_RENDERING_MODE_ALIASED;
726 textureType = DWRITE_TEXTURE_ALIASED_1x1;
727 }
728 const void* bits = this->drawDWMask(glyph, renderingMode, textureType);
bungeman@google.come8f05922012-08-16 16:13:40 +0000729 if (!bits) {
730 sk_bzero(glyph.fImage, glyph.computeImageSize());
731 return;
732 }
733
734 //Copy the mask into the glyph.
bungeman@google.come8f05922012-08-16 16:13:40 +0000735 const uint8_t* src = (const uint8_t*)bits;
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400736 if (DWRITE_RENDERING_MODE_ALIASED == renderingMode) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000737 bilevel_to_bw(src, glyph);
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000738 const_cast<SkGlyph&>(glyph).fMaskFormat = SkMask::kBW_Format;
739 } else if (!isLCD(fRec)) {
bungeman@google.coma76de722012-10-26 19:35:54 +0000740 if (fPreBlend.isApplicable()) {
741 rgb_to_a8<true>(src, glyph, fPreBlend.fG);
bungeman@google.come8f05922012-08-16 16:13:40 +0000742 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +0000743 rgb_to_a8<false>(src, glyph, fPreBlend.fG);
bungeman@google.come8f05922012-08-16 16:13:40 +0000744 }
745 } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
bungeman@google.coma76de722012-10-26 19:35:54 +0000746 if (fPreBlend.isApplicable()) {
747 rgb_to_lcd16<true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
bungeman@google.come8f05922012-08-16 16:13:40 +0000748 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +0000749 rgb_to_lcd16<false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
bungeman@google.come8f05922012-08-16 16:13:40 +0000750 }
751 } else {
752 SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
bungeman@google.coma76de722012-10-26 19:35:54 +0000753 if (fPreBlend.isApplicable()) {
754 rgb_to_lcd32<true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
bungeman@google.come8f05922012-08-16 16:13:40 +0000755 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +0000756 rgb_to_lcd32<false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
bungeman@google.come8f05922012-08-16 16:13:40 +0000757 }
758 }
759}
760
reed@google.com30ddd612013-07-30 17:47:39 +0000761void SkScalerContext_DW::generatePath(const SkGlyph& glyph, SkPath* path) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000762 SkASSERT(&glyph && path);
763
764 path->reset();
765
766 SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
767 HRVM(SkDWriteGeometrySink::Create(path, &geometryToPath),
768 "Could not create geometry to path converter.");
769 uint16_t glyphId = glyph.getGlyphID();
770 //TODO: convert to<->from DIUs? This would make a difference if hinting.
771 //It may not be needed, it appears that DirectWrite only hints at em size.
bungeman@google.com058670b2014-05-01 20:39:14 +0000772 HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fTextSizeRender),
bungeman@google.come8f05922012-08-16 16:13:40 +0000773 &glyphId,
774 NULL, //advances
775 NULL, //offsets
776 1, //num glyphs
777 FALSE, //sideways
778 FALSE, //rtl
779 geometryToPath.get()),
780 "Could not create glyph outline.");
bungeman@google.com091f51b2013-01-10 18:56:18 +0000781
bungeman@google.com058670b2014-05-01 20:39:14 +0000782 path->transform(fSkXform);
bungeman@google.come8f05922012-08-16 16:13:40 +0000783}