blob: 216c7dce2c25dfbbcc4f88aaa83fd2c6444c71a3 [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>
bungemanf73c2372014-07-23 13:31:06 -070029#include <dwrite_1.h>
bungeman@google.come8f05922012-08-16 16:13:40 +000030
bungeman@google.come8f05922012-08-16 16:13:40 +000031static bool isLCD(const SkScalerContext::Rec& rec) {
32 return SkMask::kLCD16_Format == rec.fMaskFormat ||
33 SkMask::kLCD32_Format == rec.fMaskFormat;
34}
35
bungeman761b2502014-06-30 12:19:41 -070036static bool is_hinted_without_gasp(DWriteFontTypeface* typeface) {
37 AutoTDWriteTable<SkOTTableMaximumProfile> maxp(typeface->fDWriteFontFace.get());
38 if (!maxp.fExists) {
39 return false;
40 }
41 if (maxp.fSize < sizeof(SkOTTableMaximumProfile::Version::TT)) {
42 return false;
43 }
44 if (maxp->version.version != SkOTTableMaximumProfile::Version::TT::VERSION) {
45 return false;
46 }
47
48 if (0 == maxp->version.tt.maxSizeOfInstructions) {
49 // No hints.
50 return false;
51 }
52
53 AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
54 return !gasp.fExists;
55}
56
bungeman740c3f12014-06-23 08:29:23 -070057/** A PPEMRange is inclusive, [min, max]. */
58struct PPEMRange {
59 int min;
60 int max;
61};
62
63/** If the rendering mode for the specified 'size' is gridfit, then place
64 * the gridfit range into 'range'. Otherwise, leave 'range' alone.
65 */
66static void expand_range_if_gridfit_only(DWriteFontTypeface* typeface, int size, PPEMRange* range) {
67 AutoTDWriteTable<SkOTTableGridAndScanProcedure> gasp(typeface->fDWriteFontFace.get());
68 if (!gasp.fExists) {
69 return;
70 }
71 if (gasp.fSize < sizeof(SkOTTableGridAndScanProcedure)) {
72 return;
73 }
74 if (gasp->version != SkOTTableGridAndScanProcedure::version0 &&
75 gasp->version != SkOTTableGridAndScanProcedure::version1)
76 {
Ben Wagnerc83780c2014-06-23 13:53:26 -040077 return;
bungeman740c3f12014-06-23 08:29:23 -070078 }
79
80 uint16_t numRanges = SkEndianSwap16(gasp->numRanges);
81 if (numRanges > 1024 ||
82 gasp.fSize < sizeof(SkOTTableGridAndScanProcedure) +
83 sizeof(SkOTTableGridAndScanProcedure::GaspRange) * numRanges)
84 {
85 return;
86 }
87
88 const SkOTTableGridAndScanProcedure::GaspRange* rangeTable =
89 SkTAfter<const SkOTTableGridAndScanProcedure::GaspRange>(gasp.get());
90 int minPPEM = -1;
91 for (uint16_t i = 0; i < numRanges; ++i, ++rangeTable) {
92 int maxPPEM = SkEndianSwap16(rangeTable->maxPPEM);
93 // Test that the size is in range and the range is gridfit only.
94 if (minPPEM < size && size <= maxPPEM &&
95 rangeTable->flags.raw.value == SkOTTableGridAndScanProcedure::GaspRange::behavior::Raw::GridfitMask)
96 {
97 range->min = minPPEM + 1;
98 range->max = maxPPEM;
99 return;
100 }
101 minPPEM = maxPPEM;
102 }
bungeman740c3f12014-06-23 08:29:23 -0700103}
104
105static bool has_bitmap_strike(DWriteFontTypeface* typeface, PPEMRange range) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000106 {
107 AutoTDWriteTable<SkOTTableEmbeddedBitmapLocation> eblc(typeface->fDWriteFontFace.get());
108 if (!eblc.fExists) {
109 return false;
110 }
111 if (eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation)) {
112 return false;
113 }
114 if (eblc->version != SkOTTableEmbeddedBitmapLocation::version_initial) {
115 return false;
116 }
117
118 uint32_t numSizes = SkEndianSwap32(eblc->numSizes);
bungeman740c3f12014-06-23 08:29:23 -0700119 if (numSizes > 1024 ||
120 eblc.fSize < sizeof(SkOTTableEmbeddedBitmapLocation) +
bungeman@google.com058670b2014-05-01 20:39:14 +0000121 sizeof(SkOTTableEmbeddedBitmapLocation::BitmapSizeTable) * numSizes)
122 {
123 return false;
124 }
125
126 const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable* sizeTable =
127 SkTAfter<const SkOTTableEmbeddedBitmapLocation::BitmapSizeTable>(eblc.get());
128 for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
bungeman740c3f12014-06-23 08:29:23 -0700129 if (sizeTable->ppemX == sizeTable->ppemY &&
130 range.min <= sizeTable->ppemX && sizeTable->ppemX <= range.max)
131 {
bungeman@google.com058670b2014-05-01 20:39:14 +0000132 // TODO: determine if we should dig through IndexSubTableArray/IndexSubTable
133 // to determine the actual number of glyphs with bitmaps.
134
135 // TODO: Ensure that the bitmaps actually cover a significant portion of the strike.
136
bungeman740c3f12014-06-23 08:29:23 -0700137 // TODO: Ensure that the bitmaps are bi-level?
bungeman@google.com058670b2014-05-01 20:39:14 +0000138 if (sizeTable->endGlyphIndex >= sizeTable->startGlyphIndex + 3) {
139 return true;
140 }
141 }
142 }
143 }
144
145 {
146 AutoTDWriteTable<SkOTTableEmbeddedBitmapScaling> ebsc(typeface->fDWriteFontFace.get());
147 if (!ebsc.fExists) {
148 return false;
149 }
150 if (ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling)) {
151 return false;
152 }
153 if (ebsc->version != SkOTTableEmbeddedBitmapScaling::version_initial) {
154 return false;
155 }
156
157 uint32_t numSizes = SkEndianSwap32(ebsc->numSizes);
bungeman740c3f12014-06-23 08:29:23 -0700158 if (numSizes > 1024 ||
159 ebsc.fSize < sizeof(SkOTTableEmbeddedBitmapScaling) +
bungeman@google.com058670b2014-05-01 20:39:14 +0000160 sizeof(SkOTTableEmbeddedBitmapScaling::BitmapScaleTable) * numSizes)
161 {
162 return false;
163 }
164
165 const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable* scaleTable =
166 SkTAfter<const SkOTTableEmbeddedBitmapScaling::BitmapScaleTable>(ebsc.get());
167 for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
bungeman740c3f12014-06-23 08:29:23 -0700168 if (scaleTable->ppemX == scaleTable->ppemY &&
169 range.min <= scaleTable->ppemX && scaleTable->ppemX <= range.max) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000170 // EBSC tables are normally only found in bitmap only fonts.
171 return true;
172 }
173 }
174 }
175
176 return false;
177}
178
bungeman740c3f12014-06-23 08:29:23 -0700179static bool both_zero(SkScalar a, SkScalar b) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000180 return 0 == a && 0 == b;
181}
182
183// returns false if there is any non-90-rotation or skew
bungeman740c3f12014-06-23 08:29:23 -0700184static bool is_axis_aligned(const SkScalerContext::Rec& rec) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000185 return 0 == rec.fPreSkewX &&
bungeman740c3f12014-06-23 08:29:23 -0700186 (both_zero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
187 both_zero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
bungeman@google.com058670b2014-05-01 20:39:14 +0000188}
189
reed@google.com30ddd612013-07-30 17:47:39 +0000190SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
bungeman@google.com6eddc772014-03-31 19:18:07 +0000191 const SkDescriptor* desc)
reed@google.com0da48612013-03-19 16:06:52 +0000192 : SkScalerContext(typeface, desc)
193 , fTypeface(SkRef(typeface))
bungeman@google.come8f05922012-08-16 16:13:40 +0000194 , fGlyphCount(-1) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000195
bungeman@google.com058670b2014-05-01 20:39:14 +0000196 // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC
197 // except when bi-level rendering is requested or there are embedded
198 // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
199 //
200 // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
201 // this. As a result, determine the actual size of the text and then see if
202 // there are any embedded bi-level bitmaps of that size. If there are, then
203 // force bitmaps by requesting bi-level rendering.
204 //
205 // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes
206 // square pixels and only uses ppemY. Therefore the transform must track any
207 // non-uniform x-scale.
208 //
209 // Also, rotated glyphs should have the same absolute advance widths as
210 // horizontal glyphs and the subpixel flag should not affect glyph shapes.
bungeman@google.come8f05922012-08-16 16:13:40 +0000211
bungeman@google.com058670b2014-05-01 20:39:14 +0000212 // A is the total matrix.
213 SkMatrix A;
214 fRec.getSingleMatrix(&A);
215
216 // h is where A maps the horizontal baseline.
217 SkPoint h = SkPoint::Make(SK_Scalar1, 0);
218 A.mapPoints(&h, 1);
219
220 // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
221 SkMatrix G;
222 SkComputeGivensRotation(h, &G);
223
224 // GA is the matrix A with rotation removed.
225 SkMatrix GA(G);
226 GA.preConcat(A);
227
228 // realTextSize is the actual device size we want (as opposed to the size the user requested).
229 // gdiTextSize is the size we request when GDI compatible.
230 // If the scale is negative, this means the matrix will do the flip anyway.
231 SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
232 // Due to floating point math, the lower bits are suspect. Round carefully.
bungeman@google.com7c183512014-05-28 15:40:26 +0000233 SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
bungeman@google.com058670b2014-05-01 20:39:14 +0000234 if (gdiTextSize == 0) {
235 gdiTextSize = SK_Scalar1;
236 }
237
bungeman@google.com7c183512014-05-28 15:40:26 +0000238 bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag);
bungeman740c3f12014-06-23 08:29:23 -0700239 bool treatLikeBitmap = false;
bungeman@google.com7c183512014-05-28 15:40:26 +0000240 bool axisAlignedBitmap = false;
241 if (bitmapRequested) {
bungeman740c3f12014-06-23 08:29:23 -0700242 // When embedded bitmaps are requested, treat the entire range like
243 // a bitmap strike if the range is gridfit only and contains a bitmap.
244 int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
245 PPEMRange range = { bitmapPPEM, bitmapPPEM };
bungeman740c3f12014-06-23 08:29:23 -0700246 expand_range_if_gridfit_only(typeface, bitmapPPEM, &range);
bungeman740c3f12014-06-23 08:29:23 -0700247 treatLikeBitmap = has_bitmap_strike(typeface, range);
248
249 axisAlignedBitmap = is_axis_aligned(fRec);
bungeman@google.com7c183512014-05-28 15:40:26 +0000250 }
bungeman@google.com058670b2014-05-01 20:39:14 +0000251
bungeman@google.com7c183512014-05-28 15:40:26 +0000252 // If the user requested aliased, do so with aliased compatible metrics.
253 if (SkMask::kBW_Format == fRec.fMaskFormat) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000254 fTextSizeRender = gdiTextSize;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000255 fRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
256 fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
bungeman@google.com058670b2014-05-01 20:39:14 +0000257 fTextSizeMeasure = gdiTextSize;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000258 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
bungemandf1640d2014-06-04 15:19:47 -0700259
bungeman@google.com7c183512014-05-28 15:40:26 +0000260 // If we can use a bitmap, use gdi classic rendering and measurement.
261 // This will not always provide a bitmap, but matches expected behavior.
bungeman740c3f12014-06-23 08:29:23 -0700262 } else if (treatLikeBitmap && axisAlignedBitmap) {
bungeman@google.com7c183512014-05-28 15:40:26 +0000263 fTextSizeRender = gdiTextSize;
264 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
265 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
266 fTextSizeMeasure = gdiTextSize;
267 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
bungemandf1640d2014-06-04 15:19:47 -0700268
bungeman@google.com7c183512014-05-28 15:40:26 +0000269 // If rotated but the horizontal text could have used a bitmap,
270 // render high quality rotated glyphs but measure using bitmap metrics.
bungeman740c3f12014-06-23 08:29:23 -0700271 } else if (treatLikeBitmap) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000272 fTextSizeRender = gdiTextSize;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000273 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
274 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
bungeman@google.com058670b2014-05-01 20:39:14 +0000275 fTextSizeMeasure = gdiTextSize;
276 fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
bungeman@google.com7c183512014-05-28 15:40:26 +0000277
bungeman761b2502014-06-30 12:19:41 -0700278 // Fonts that have hints but no gasp table get non-symmetric rendering.
279 // Usually such fonts have low quality hints which were never tested
280 // with anything but GDI ClearType classic. Such fonts often rely on
281 // drop out control in the y direction in order to be legible.
282 } else if (is_hinted_without_gasp(typeface)) {
283 fTextSizeRender = gdiTextSize;
284 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
285 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
286 fTextSizeMeasure = realTextSize;
287 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
288
bungeman@google.com7c183512014-05-28 15:40:26 +0000289 // The normal case is to use natural symmetric rendering and linear metrics.
bungeman@google.com058670b2014-05-01 20:39:14 +0000290 } else {
291 fTextSizeRender = realTextSize;
292 fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
293 fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
294 fTextSizeMeasure = realTextSize;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000295 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
296 }
297
298 if (this->isSubpixel()) {
bungeman@google.com058670b2014-05-01 20:39:14 +0000299 fTextSizeMeasure = realTextSize;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000300 fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
301 }
bungeman@google.com058670b2014-05-01 20:39:14 +0000302
303 // Remove the realTextSize, as that is the text height scale currently in A.
304 SkScalar scale = SkScalarInvert(realTextSize);
305
306 // fSkXform is the total matrix A without the text height scale.
307 fSkXform = A;
308 fSkXform.preScale(scale, scale); //remove the text height scale.
309
310 fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
311 fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
312 fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
313 fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
314 fXform.dx = 0;
315 fXform.dy = 0;
316
317 // GsA is the non-rotational part of A without the text height scale.
318 SkMatrix GsA(GA);
319 GsA.preScale(scale, scale); //remove text height scale, G is rotational so reorders with scale.
320
321 fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX));
322 fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
323 fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX));
324 fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY));
bungeman@google.come4ae0bc2014-05-21 19:44:39 +0000325 fGsA.dx = 0;
326 fGsA.dy = 0;
bungeman@google.com058670b2014-05-01 20:39:14 +0000327
328 // fG_inv is G inverse, which is fairly simple since G is 2x2 rotational.
329 fG_inv.setAll(G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX),
330 -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY),
331 G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2));
bungeman@google.come8f05922012-08-16 16:13:40 +0000332}
333
reed@google.com30ddd612013-07-30 17:47:39 +0000334SkScalerContext_DW::~SkScalerContext_DW() {
bungeman@google.come8f05922012-08-16 16:13:40 +0000335}
336
reed@google.com30ddd612013-07-30 17:47:39 +0000337unsigned SkScalerContext_DW::generateGlyphCount() {
bungeman@google.come8f05922012-08-16 16:13:40 +0000338 if (fGlyphCount < 0) {
339 fGlyphCount = fTypeface->fDWriteFontFace->GetGlyphCount();
340 }
341 return fGlyphCount;
342}
343
reed@google.com30ddd612013-07-30 17:47:39 +0000344uint16_t SkScalerContext_DW::generateCharToGlyph(SkUnichar uni) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000345 uint16_t index = 0;
346 fTypeface->fDWriteFontFace->GetGlyphIndices(reinterpret_cast<UINT32*>(&uni), 1, &index);
347 return index;
348}
349
reed@google.com30ddd612013-07-30 17:47:39 +0000350void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000351 //Delta is the difference between the right/left side bearing metric
352 //and where the right/left side bearing ends up after hinting.
353 //DirectWrite does not provide this information.
354 glyph->fRsbDelta = 0;
355 glyph->fLsbDelta = 0;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000356
bungeman@google.come8f05922012-08-16 16:13:40 +0000357 glyph->fAdvanceX = 0;
358 glyph->fAdvanceY = 0;
359
360 uint16_t glyphId = glyph->getGlyphID();
361 DWRITE_GLYPH_METRICS gm;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000362
363 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
364 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
365 {
366 HRVM(fTypeface->fDWriteFontFace->GetGdiCompatibleGlyphMetrics(
bungeman@google.com058670b2014-05-01 20:39:14 +0000367 fTextSizeMeasure,
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000368 1.0f, // pixelsPerDip
bungeman@google.com058670b2014-05-01 20:39:14 +0000369 &fGsA,
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000370 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode,
371 &glyphId, 1,
372 &gm),
373 "Could not get gdi compatible glyph metrics.");
374 } else {
375 HRVM(fTypeface->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm),
376 "Could not get design metrics.");
377 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000378
379 DWRITE_FONT_METRICS dwfm;
380 fTypeface->fDWriteFontFace->GetMetrics(&dwfm);
bungeman@google.com058670b2014-05-01 20:39:14 +0000381 SkScalar advanceX = SkScalarMulDiv(fTextSizeMeasure,
bungeman@google.come8f05922012-08-16 16:13:40 +0000382 SkIntToScalar(gm.advanceWidth),
383 SkIntToScalar(dwfm.designUnitsPerEm));
rmistry@google.comd6176b02012-08-23 18:14:13 +0000384
bungeman@google.come8f05922012-08-16 16:13:40 +0000385 SkVector vecs[1] = { { advanceX, 0 } };
bungeman@google.com058670b2014-05-01 20:39:14 +0000386 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
387 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
388 {
bungeman761b2502014-06-30 12:19:41 -0700389 // DirectWrite produced 'compatible' metrics, but while close,
390 // the end result is not always an integer as it would be with GDI.
391 vecs[0].fX = SkScalarRoundToScalar(advanceX);
bungeman@google.com058670b2014-05-01 20:39:14 +0000392 fG_inv.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
393 } else {
394 fSkXform.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
395 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000396
397 glyph->fAdvanceX = SkScalarToFixed(vecs[0].fX);
398 glyph->fAdvanceY = SkScalarToFixed(vecs[0].fY);
399}
400
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400401void SkScalerContext_DW::getBoundingBox(SkGlyph* glyph,
402 DWRITE_RENDERING_MODE renderingMode,
403 DWRITE_TEXTURE_TYPE textureType,
404 RECT* bbox)
405{
bungeman@google.come8f05922012-08-16 16:13:40 +0000406 //Measure raster size.
407 fXform.dx = SkFixedToFloat(glyph->getSubXFixed());
408 fXform.dy = SkFixedToFloat(glyph->getSubYFixed());
409
410 FLOAT advance = 0;
411
412 UINT16 glyphId = glyph->getGlyphID();
413
414 DWRITE_GLYPH_OFFSET offset;
415 offset.advanceOffset = 0.0f;
416 offset.ascenderOffset = 0.0f;
417
418 DWRITE_GLYPH_RUN run;
419 run.glyphCount = 1;
420 run.glyphAdvances = &advance;
421 run.fontFace = fTypeface->fDWriteFontFace.get();
bungeman@google.com058670b2014-05-01 20:39:14 +0000422 run.fontEmSize = SkScalarToFloat(fTextSizeRender);
bungeman@google.come8f05922012-08-16 16:13:40 +0000423 run.bidiLevel = 0;
424 run.glyphIndices = &glyphId;
425 run.isSideways = FALSE;
426 run.glyphOffsets = &offset;
427
bungeman@google.come8f05922012-08-16 16:13:40 +0000428 SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
bungeman@google.com6eddc772014-03-31 19:18:07 +0000429 HRVM(fTypeface->fFactory->CreateGlyphRunAnalysis(
430 &run,
431 1.0f, // pixelsPerDip,
432 &fXform,
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400433 renderingMode,
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000434 fMeasuringMode,
bungeman@google.com6eddc772014-03-31 19:18:07 +0000435 0.0f, // baselineOriginX,
436 0.0f, // baselineOriginY,
437 &glyphRunAnalysis),
bungeman@google.come8f05922012-08-16 16:13:40 +0000438 "Could not create glyph run analysis.");
rmistry@google.comd6176b02012-08-23 18:14:13 +0000439
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400440 HRVM(glyphRunAnalysis->GetAlphaTextureBounds(textureType, bbox),
bungeman@google.come8f05922012-08-16 16:13:40 +0000441 "Could not get texture bounds.");
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400442}
443
444void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) {
445 glyph->fWidth = 0;
446
447 this->generateAdvance(glyph);
448
449 RECT bbox;
450 this->getBoundingBox(glyph, fRenderingMode, fTextureType, &bbox);
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 (bbox.left == bbox.right || bbox.top == bbox.bottom) {
456 if (DWRITE_TEXTURE_CLEARTYPE_3x1 == fTextureType) {
457 this->getBoundingBox(glyph,
458 DWRITE_RENDERING_MODE_ALIASED,
459 DWRITE_TEXTURE_ALIASED_1x1,
460 &bbox);
461 if (bbox.left != bbox.right && bbox.top != bbox.bottom) {
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 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000468
469 glyph->fWidth = SkToU16(bbox.right - bbox.left);
470 glyph->fHeight = SkToU16(bbox.bottom - bbox.top);
471 glyph->fLeft = SkToS16(bbox.left);
472 glyph->fTop = SkToS16(bbox.top);
473}
474
bungeman41078062014-07-07 08:16:37 -0700475void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* metrics) {
476 if (NULL == metrics) {
bungemanf73c2372014-07-23 13:31:06 -0700477 return;
bungeman41078062014-07-07 08:16:37 -0700478 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000479
bungeman41078062014-07-07 08:16:37 -0700480 sk_bzero(metrics, sizeof(*metrics));
bungeman@google.come1b9bad2013-08-27 21:51:37 +0000481
bungeman@google.come8f05922012-08-16 16:13:40 +0000482 DWRITE_FONT_METRICS dwfm;
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000483 if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
484 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
485 {
486 fTypeface->fDWriteFontFace->GetGdiCompatibleMetrics(
bungeman@google.com058670b2014-05-01 20:39:14 +0000487 fTextSizeRender,
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000488 1.0f, // pixelsPerDip
489 &fXform,
490 &dwfm);
491 } else {
492 fTypeface->fDWriteFontFace->GetMetrics(&dwfm);
493 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000494
bungeman@google.come1b9bad2013-08-27 21:51:37 +0000495 SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm);
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +0000496
bungemanf73c2372014-07-23 13:31:06 -0700497 metrics->fAscent = -fTextSizeRender * SkIntToScalar(dwfm.ascent) / upem;
bungeman41078062014-07-07 08:16:37 -0700498 metrics->fDescent = fTextSizeRender * SkIntToScalar(dwfm.descent) / upem;
bungeman41078062014-07-07 08:16:37 -0700499 metrics->fLeading = fTextSizeRender * SkIntToScalar(dwfm.lineGap) / upem;
500 metrics->fXHeight = fTextSizeRender * SkIntToScalar(dwfm.xHeight) / upem;
501 metrics->fUnderlineThickness = fTextSizeRender * SkIntToScalar(dwfm.underlineThickness) / upem;
502 metrics->fUnderlinePosition = -(fTextSizeRender * SkIntToScalar(dwfm.underlinePosition) / upem);
bungeman@google.come8f05922012-08-16 16:13:40 +0000503
bungeman41078062014-07-07 08:16:37 -0700504 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
505 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungemanf73c2372014-07-23 13:31:06 -0700506
507 if (NULL != fTypeface->fDWriteFontFace1.get()) {
508 DWRITE_FONT_METRICS1 dwfm1;
509 fTypeface->fDWriteFontFace1->GetMetrics(&dwfm1);
510 metrics->fTop = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxTop) / upem;
511 metrics->fBottom = -fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxBottom) / upem;
512 metrics->fXMin = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxLeft) / upem;
513 metrics->fXMax = fTextSizeRender * SkIntToScalar(dwfm1.glyphBoxRight) / upem;
514
515 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
516 } else {
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 } else {
529 metrics->fTop = metrics->fAscent;
530 metrics->fBottom = metrics->fDescent;
531 }
532 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000533}
534
535///////////////////////////////////////////////////////////////////////////////
536
537#include "SkColorPriv.h"
538
539static 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
577template<bool APPLY_PREBLEND>
578static 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
594template<bool APPLY_PREBLEND>
595static 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 = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
604 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
605 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
606 dst[i] = SkPack888ToRGB16(r, g, b);
607 }
608 dst = (uint16_t*)((char*)dst + dstRB);
609 }
610}
611
612template<bool APPLY_PREBLEND>
613static void rgb_to_lcd32(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph,
614 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
615 const size_t dstRB = glyph.rowBytes();
616 const U16CPU width = glyph.fWidth;
617 SkPMColor* SK_RESTRICT dst = static_cast<SkPMColor*>(glyph.fImage);
618
619 for (U16CPU y = 0; y < glyph.fHeight; y++) {
620 for (U16CPU i = 0; i < width; i++) {
621 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableR);
622 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableG);
623 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*(src++), tableB);
624 dst[i] = SkPackARGB32(0xFF, r, g, b);
625 }
626 dst = (SkPMColor*)((char*)dst + dstRB);
627 }
628}
629
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400630const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph,
631 DWRITE_RENDERING_MODE renderingMode,
632 DWRITE_TEXTURE_TYPE textureType)
633{
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000634 int sizeNeeded = glyph.fWidth * glyph.fHeight;
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400635 if (DWRITE_RENDERING_MODE_ALIASED != renderingMode) {
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000636 sizeNeeded *= 3;
637 }
638 if (sizeNeeded > fBits.count()) {
639 fBits.setCount(sizeNeeded);
640 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000641
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000642 // erase
643 memset(fBits.begin(), 0, sizeNeeded);
644
645 fXform.dx = SkFixedToFloat(glyph.getSubXFixed());
646 fXform.dy = SkFixedToFloat(glyph.getSubYFixed());
647
648 FLOAT advance = 0.0f;
649
650 UINT16 index = glyph.getGlyphID();
651
652 DWRITE_GLYPH_OFFSET offset;
653 offset.advanceOffset = 0.0f;
654 offset.ascenderOffset = 0.0f;
655
656 DWRITE_GLYPH_RUN run;
657 run.glyphCount = 1;
658 run.glyphAdvances = &advance;
659 run.fontFace = fTypeface->fDWriteFontFace.get();
bungeman@google.com058670b2014-05-01 20:39:14 +0000660 run.fontEmSize = SkScalarToFloat(fTextSizeRender);
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000661 run.bidiLevel = 0;
662 run.glyphIndices = &index;
663 run.isSideways = FALSE;
664 run.glyphOffsets = &offset;
665
666 SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
667 HRNM(fTypeface->fFactory->CreateGlyphRunAnalysis(&run,
668 1.0f, // pixelsPerDip,
669 &fXform,
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400670 renderingMode,
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000671 fMeasuringMode,
672 0.0f, // baselineOriginX,
673 0.0f, // baselineOriginY,
674 &glyphRunAnalysis),
675 "Could not create glyph run analysis.");
676
677 //NOTE: this assumes that the glyph has already been measured
678 //with an exact same glyph run analysis.
679 RECT bbox;
680 bbox.left = glyph.fLeft;
681 bbox.top = glyph.fTop;
682 bbox.right = glyph.fLeft + glyph.fWidth;
683 bbox.bottom = glyph.fTop + glyph.fHeight;
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400684 HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType,
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000685 &bbox,
686 fBits.begin(),
687 sizeNeeded),
688 "Could not draw mask.");
689 return fBits.begin();
690}
691
692void SkScalerContext_DW::generateImage(const SkGlyph& glyph) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000693 //Create the mask.
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400694 DWRITE_RENDERING_MODE renderingMode = fRenderingMode;
695 DWRITE_TEXTURE_TYPE textureType = fTextureType;
696 if (glyph.fForceBW) {
697 renderingMode = DWRITE_RENDERING_MODE_ALIASED;
698 textureType = DWRITE_TEXTURE_ALIASED_1x1;
699 }
700 const void* bits = this->drawDWMask(glyph, renderingMode, textureType);
bungeman@google.come8f05922012-08-16 16:13:40 +0000701 if (!bits) {
702 sk_bzero(glyph.fImage, glyph.computeImageSize());
703 return;
704 }
705
706 //Copy the mask into the glyph.
bungeman@google.come8f05922012-08-16 16:13:40 +0000707 const uint8_t* src = (const uint8_t*)bits;
Ben Wagnerb2f7fce2014-08-27 19:17:41 -0400708 if (DWRITE_RENDERING_MODE_ALIASED == renderingMode) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000709 bilevel_to_bw(src, glyph);
bungeman@google.comd715aaa2014-04-09 15:35:03 +0000710 const_cast<SkGlyph&>(glyph).fMaskFormat = SkMask::kBW_Format;
711 } else if (!isLCD(fRec)) {
bungeman@google.coma76de722012-10-26 19:35:54 +0000712 if (fPreBlend.isApplicable()) {
713 rgb_to_a8<true>(src, glyph, fPreBlend.fG);
bungeman@google.come8f05922012-08-16 16:13:40 +0000714 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +0000715 rgb_to_a8<false>(src, glyph, fPreBlend.fG);
bungeman@google.come8f05922012-08-16 16:13:40 +0000716 }
717 } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
bungeman@google.coma76de722012-10-26 19:35:54 +0000718 if (fPreBlend.isApplicable()) {
719 rgb_to_lcd16<true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
bungeman@google.come8f05922012-08-16 16:13:40 +0000720 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +0000721 rgb_to_lcd16<false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
bungeman@google.come8f05922012-08-16 16:13:40 +0000722 }
723 } else {
724 SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
bungeman@google.coma76de722012-10-26 19:35:54 +0000725 if (fPreBlend.isApplicable()) {
726 rgb_to_lcd32<true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
bungeman@google.come8f05922012-08-16 16:13:40 +0000727 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +0000728 rgb_to_lcd32<false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
bungeman@google.come8f05922012-08-16 16:13:40 +0000729 }
730 }
731}
732
reed@google.com30ddd612013-07-30 17:47:39 +0000733void SkScalerContext_DW::generatePath(const SkGlyph& glyph, SkPath* path) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000734 SkASSERT(&glyph && path);
735
736 path->reset();
737
738 SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
739 HRVM(SkDWriteGeometrySink::Create(path, &geometryToPath),
740 "Could not create geometry to path converter.");
741 uint16_t glyphId = glyph.getGlyphID();
742 //TODO: convert to<->from DIUs? This would make a difference if hinting.
743 //It may not be needed, it appears that DirectWrite only hints at em size.
bungeman@google.com058670b2014-05-01 20:39:14 +0000744 HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fTextSizeRender),
bungeman@google.come8f05922012-08-16 16:13:40 +0000745 &glyphId,
746 NULL, //advances
747 NULL, //offsets
748 1, //num glyphs
749 FALSE, //sideways
750 FALSE, //rtl
751 geometryToPath.get()),
752 "Could not create glyph outline.");
bungeman@google.com091f51b2013-01-10 18:56:18 +0000753
bungeman@google.com058670b2014-05-01 20:39:14 +0000754 path->transform(fSkXform);
bungeman@google.come8f05922012-08-16 16:13:40 +0000755}