1 /*
2 * Copyright 2006 The Android Open Source Project
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 "src/core/SkScalerContext.h"
9
10 #include "include/core/SkColorType.h"
11 #include "include/core/SkDrawable.h"
12 #include "include/core/SkFont.h"
13 #include "include/core/SkFontMetrics.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkMaskFilter.h"
16 #include "include/core/SkPaint.h"
17 #include "include/core/SkPath.h"
18 #include "include/core/SkPathEffect.h"
19 #include "include/core/SkPixmap.h"
20 #include "include/core/SkStrokeRec.h"
21 #include "include/private/base/SkAlign.h"
22 #include "include/private/base/SkCPUTypes.h"
23 #include "include/private/base/SkDebug.h"
24 #include "include/private/base/SkFixed.h"
25 #include "include/private/base/SkMalloc.h"
26 #include "include/private/base/SkMutex.h"
27 #include "include/private/base/SkTo.h"
28 #include "src/base/SkArenaAlloc.h"
29 #include "src/base/SkAutoMalloc.h"
30 #include "src/core/SkAutoPixmapStorage.h"
31 #include "src/core/SkBlitter_A8.h"
32 #include "src/core/SkColorData.h"
33 #include "src/core/SkDescriptor.h"
34 #include "src/core/SkDrawBase.h"
35 #include "src/core/SkFontPriv.h"
36 #include "src/core/SkGlyph.h"
37 #include "src/core/SkMaskFilterBase.h"
38 #include "src/core/SkPaintPriv.h"
39 #include "src/core/SkRasterClip.h"
40 #include "src/core/SkTextFormatParams.h"
41 #include "src/core/SkWriteBuffer.h"
42 #include "src/utils/SkMatrix22.h"
43
44 #include <algorithm>
45 #include <cstring>
46 #include <limits>
47 #include <new>
48 #include <utility>
49
50 ///////////////////////////////////////////////////////////////////////////////
51
52 namespace {
53 static inline const constexpr bool kSkShowTextBlitCoverage = false;
54 static inline const constexpr bool kSkScalerContextDumpRec = false;
55 }
56
PreprocessRec(const SkTypeface & typeface,const SkScalerContextEffects & effects,const SkDescriptor & desc)57 SkScalerContextRec SkScalerContext::PreprocessRec(const SkTypeface& typeface,
58 const SkScalerContextEffects& effects,
59 const SkDescriptor& desc) {
60 SkScalerContextRec rec =
61 *static_cast<const SkScalerContextRec*>(desc.findEntry(kRec_SkDescriptorTag, nullptr));
62
63 // Allow the typeface to adjust the rec.
64 typeface.onFilterRec(&rec);
65
66 if (effects.fMaskFilter) {
67 // Pre-blend is not currently applied to filtered text.
68 // The primary filter is blur, for which contrast makes no sense,
69 // and for which the destination guess error is more visible.
70 // Also, all existing users of blur have calibrated for linear.
71 rec.ignorePreBlend();
72 }
73
74 SkColor lumColor = rec.getLuminanceColor();
75
76 if (rec.fMaskFormat == SkMask::kA8_Format) {
77 U8CPU lum = SkComputeLuminance(SkColorGetR(lumColor),
78 SkColorGetG(lumColor),
79 SkColorGetB(lumColor));
80 lumColor = SkColorSetRGB(lum, lum, lum);
81 }
82
83 // TODO: remove CanonicalColor when we to fix up Chrome layout tests.
84 rec.setLuminanceColor(lumColor);
85
86 return rec;
87 }
88
SkScalerContext(SkTypeface & typeface,const SkScalerContextEffects & effects,const SkDescriptor * desc)89 SkScalerContext::SkScalerContext(SkTypeface& typeface, const SkScalerContextEffects& effects,
90 const SkDescriptor* desc)
91 : fRec(PreprocessRec(typeface, effects, *desc))
92 , fTypeface(typeface)
93 , fPathEffect(sk_ref_sp(effects.fPathEffect))
94 , fMaskFilter(sk_ref_sp(effects.fMaskFilter))
95 // Initialize based on our settings. Subclasses can also force this.
96 , fGenerateImageFromPath(fRec.fFrameWidth >= 0 || fPathEffect != nullptr)
97
98 , fPreBlend(fMaskFilter ? SkMaskGamma::PreBlend() : SkScalerContext::GetMaskPreBlend(fRec))
99 {
100 if constexpr (kSkScalerContextDumpRec) {
101 SkDebugf("SkScalerContext checksum %x count %u length %u\n",
102 desc->getChecksum(), desc->getCount(), desc->getLength());
103 SkDebugf("%s", fRec.dump().c_str());
104 SkDebugf(" effects %p\n", desc->findEntry(kEffects_SkDescriptorTag, nullptr));
105 }
106 }
107
~SkScalerContext()108 SkScalerContext::~SkScalerContext() {}
109
110 /**
111 * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
112 * cachedMaskGamma the caller must hold the mask_gamma_cache_mutex and continue
113 * to hold it until the returned pointer is refed or forgotten.
114 */
mask_gamma_cache_mutex()115 static SkMutex& mask_gamma_cache_mutex() {
116 static SkMutex& mutex = *(new SkMutex);
117 return mutex;
118 }
119
linear_gamma()120 static const SkMaskGamma& linear_gamma() {
121 static const SkMaskGamma kLinear;
122 return kLinear;
123 }
124
125 static SkMaskGamma* gDefaultMaskGamma = nullptr;
126 static SkMaskGamma* gMaskGamma = nullptr;
127 static uint8_t gContrast = 0;
128 static uint8_t gGamma = 0;
129
130 /**
131 * The caller must hold the mask_gamma_cache_mutex() and continue to hold it until
132 * the returned SkMaskGamma pointer is refed or forgotten.
133 */
CachedMaskGamma(uint8_t contrast,uint8_t gamma)134 const SkMaskGamma& SkScalerContextRec::CachedMaskGamma(uint8_t contrast, uint8_t gamma) {
135 mask_gamma_cache_mutex().assertHeld();
136
137 constexpr uint8_t contrast0 = InternalContrastFromExternal(0);
138 constexpr uint8_t gamma1 = InternalGammaFromExternal(1);
139 if (contrast0 == contrast && gamma1 == gamma) {
140 return linear_gamma();
141 }
142 constexpr uint8_t defaultContrast = InternalContrastFromExternal(SK_GAMMA_CONTRAST);
143 constexpr uint8_t defaultGamma = InternalGammaFromExternal(SK_GAMMA_EXPONENT);
144 if (defaultContrast == contrast && defaultGamma == gamma) {
145 if (!gDefaultMaskGamma) {
146 gDefaultMaskGamma = new SkMaskGamma(ExternalContrastFromInternal(contrast),
147 ExternalGammaFromInternal(gamma));
148 }
149 return *gDefaultMaskGamma;
150 }
151 if (!gMaskGamma || gContrast != contrast || gGamma != gamma) {
152 SkSafeUnref(gMaskGamma);
153 gMaskGamma = new SkMaskGamma(ExternalContrastFromInternal(contrast),
154 ExternalGammaFromInternal(gamma));
155 gContrast = contrast;
156 gGamma = gamma;
157 }
158 return *gMaskGamma;
159 }
160
161 /**
162 * Expands fDeviceGamma, fContrast, and fLumBits into a mask pre-blend.
163 */
GetMaskPreBlend(const SkScalerContextRec & rec)164 SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContextRec& rec) {
165 SkAutoMutexExclusive ama(mask_gamma_cache_mutex());
166
167 const SkMaskGamma& maskGamma = rec.cachedMaskGamma();
168
169 // TODO: remove CanonicalColor when we to fix up Chrome layout tests.
170 return maskGamma.preBlend(rec.getLuminanceColor());
171 }
172
GetGammaLUTSize(SkScalar contrast,SkScalar deviceGamma,int * width,int * height)173 size_t SkScalerContext::GetGammaLUTSize(SkScalar contrast, SkScalar deviceGamma,
174 int* width, int* height) {
175 SkAutoMutexExclusive ama(mask_gamma_cache_mutex());
176 const SkMaskGamma& maskGamma = SkScalerContextRec::CachedMaskGamma(
177 SkScalerContextRec::InternalContrastFromExternal(contrast),
178 SkScalerContextRec::InternalGammaFromExternal(deviceGamma));
179 maskGamma.getGammaTableDimensions(width, height);
180 return maskGamma.getGammaTableSizeInBytes();
181 }
182
GetGammaLUTData(SkScalar contrast,SkScalar deviceGamma,uint8_t * data)183 bool SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar deviceGamma, uint8_t* data) {
184 SkAutoMutexExclusive ama(mask_gamma_cache_mutex());
185 const SkMaskGamma& maskGamma = SkScalerContextRec::CachedMaskGamma(
186 SkScalerContextRec::InternalContrastFromExternal(contrast),
187 SkScalerContextRec::InternalGammaFromExternal(deviceGamma));
188 const uint8_t* gammaTables = maskGamma.getGammaTables();
189 if (!gammaTables) {
190 return false;
191 }
192
193 memcpy(data, gammaTables, maskGamma.getGammaTableSizeInBytes());
194 return true;
195 }
196
makeGlyph(SkPackedGlyphID packedID,SkArenaAlloc * alloc)197 SkGlyph SkScalerContext::makeGlyph(SkPackedGlyphID packedID, SkArenaAlloc* alloc) {
198 return internalMakeGlyph(packedID, fRec.fMaskFormat, alloc);
199 }
200
201 /** Return the closest D for the given S. Returns std::numeric_limits<D>::max() for NaN. */
sk_saturate_cast(S s)202 template <typename D, typename S> static constexpr D sk_saturate_cast(S s) {
203 static_assert(std::is_integral_v<D>);
204 s = s < std::numeric_limits<D>::max() ? s : std::numeric_limits<D>::max();
205 s = s > std::numeric_limits<D>::min() ? s : std::numeric_limits<D>::min();
206 return (D)s;
207 }
SaturateGlyphBounds(SkGlyph * glyph,SkRect && r)208 void SkScalerContext::SaturateGlyphBounds(SkGlyph* glyph, SkRect&& r) {
209 r.roundOut(&r);
210 glyph->fLeft = sk_saturate_cast<int16_t>(r.fLeft);
211 glyph->fTop = sk_saturate_cast<int16_t>(r.fTop);
212 glyph->fWidth = sk_saturate_cast<uint16_t>(r.width());
213 glyph->fHeight = sk_saturate_cast<uint16_t>(r.height());
214 }
SaturateGlyphBounds(SkGlyph * glyph,SkIRect const & r)215 void SkScalerContext::SaturateGlyphBounds(SkGlyph* glyph, SkIRect const & r) {
216 glyph->fLeft = sk_saturate_cast<int16_t>(r.fLeft);
217 glyph->fTop = sk_saturate_cast<int16_t>(r.fTop);
218 glyph->fWidth = sk_saturate_cast<uint16_t>(r.width64());
219 glyph->fHeight = sk_saturate_cast<uint16_t>(r.height64());
220 }
221
GenerateMetricsFromPath(SkGlyph * glyph,const SkPath & devPath,SkMask::Format format,const bool verticalLCD,const bool a8FromLCD,const bool hairline)222 void SkScalerContext::GenerateMetricsFromPath(
223 SkGlyph* glyph, const SkPath& devPath, SkMask::Format format,
224 const bool verticalLCD, const bool a8FromLCD, const bool hairline)
225 {
226 // Only BW, A8, and LCD16 can be produced from paths.
227 if (glyph->fMaskFormat != SkMask::kBW_Format &&
228 glyph->fMaskFormat != SkMask::kA8_Format &&
229 glyph->fMaskFormat != SkMask::kLCD16_Format)
230 {
231 glyph->fMaskFormat = SkMask::kA8_Format;
232 }
233
234 SkRect bounds = devPath.getBounds();
235 if (!bounds.isEmpty()) {
236 const bool fromLCD = (glyph->fMaskFormat == SkMask::kLCD16_Format) ||
237 (glyph->fMaskFormat == SkMask::kA8_Format && a8FromLCD);
238
239 const bool needExtraWidth = (fromLCD && !verticalLCD) || hairline;
240 const bool needExtraHeight = (fromLCD && verticalLCD) || hairline;
241 if (needExtraWidth) {
242 bounds.roundOut(&bounds);
243 bounds.outset(1, 0);
244 }
245 if (needExtraHeight) {
246 bounds.roundOut(&bounds);
247 bounds.outset(0, 1);
248 }
249 }
250 SaturateGlyphBounds(glyph, std::move(bounds));
251 }
252
internalMakeGlyph(SkPackedGlyphID packedID,SkMask::Format format,SkArenaAlloc * alloc)253 SkGlyph SkScalerContext::internalMakeGlyph(SkPackedGlyphID packedID, SkMask::Format format, SkArenaAlloc* alloc) {
254 auto zeroBounds = [](SkGlyph& glyph) {
255 glyph.fLeft = 0;
256 glyph.fTop = 0;
257 glyph.fWidth = 0;
258 glyph.fHeight = 0;
259 };
260
261 SkGlyph glyph{packedID};
262 glyph.fMaskFormat = format; // subclass may return a different value
263 GlyphMetrics mx = this->generateMetrics(glyph, alloc);
264 SkASSERT(!mx.neverRequestPath || !mx.computeFromPath);
265
266 glyph.fAdvanceX = mx.advance.fX;
267 glyph.fAdvanceY = mx.advance.fY;
268 glyph.fMaskFormat = mx.maskFormat;
269 glyph.fScalerContextBits = mx.extraBits;
270
271 if (mx.computeFromPath || (fGenerateImageFromPath && !mx.neverRequestPath)) {
272 SkDEBUGCODE(glyph.fAdvancesBoundsFormatAndInitialPathDone = true;)
273 this->internalGetPath(glyph, alloc);
274 const SkPath* devPath = glyph.path();
275 if (devPath) {
276 const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
277 const bool a8LCD = SkToBool(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag);
278 const bool hairline = glyph.pathIsHairline();
279 GenerateMetricsFromPath(&glyph, *devPath, format, doVert, a8LCD, hairline);
280 }
281 } else {
282 SaturateGlyphBounds(&glyph, std::move(mx.bounds));
283 if (mx.neverRequestPath) {
284 glyph.setPath(alloc, nullptr, false, false);
285 }
286 }
287 SkDEBUGCODE(glyph.fAdvancesBoundsFormatAndInitialPathDone = true;)
288
289 // if either dimension is empty, zap the image bounds of the glyph
290 if (0 == glyph.fWidth || 0 == glyph.fHeight) {
291 zeroBounds(glyph);
292 return glyph;
293 }
294
295 if (fMaskFilter) {
296 // only want the bounds from the filter
297 SkMask src(nullptr, glyph.iRect(), glyph.rowBytes(), glyph.maskFormat());
298 SkMaskBuilder dst;
299 SkMatrix matrix;
300
301 fRec.getMatrixFrom2x2(&matrix);
302
303 if (as_MFB(fMaskFilter)->filterMask(&dst, src, matrix, nullptr)) {
304 if (dst.fBounds.isEmpty()) {
305 zeroBounds(glyph);
306 return glyph;
307 }
308 SkASSERT(dst.fImage == nullptr);
309 SaturateGlyphBounds(&glyph, dst.fBounds);
310 glyph.fMaskFormat = dst.fFormat;
311 }
312 }
313 return glyph;
314 }
315
applyLUTToA8Mask(SkMaskBuilder & mask,const uint8_t * lut)316 static void applyLUTToA8Mask(SkMaskBuilder& mask, const uint8_t* lut) {
317 uint8_t* SK_RESTRICT dst = mask.image();
318 unsigned rowBytes = mask.fRowBytes;
319
320 for (int y = mask.fBounds.height() - 1; y >= 0; --y) {
321 for (int x = mask.fBounds.width() - 1; x >= 0; --x) {
322 dst[x] = lut[dst[x]];
323 }
324 dst += rowBytes;
325 }
326 }
327
pack4xHToMask(const SkPixmap & src,SkMaskBuilder & dst,const SkMaskGamma::PreBlend & maskPreBlend,const bool doBGR,const bool doVert)328 static void pack4xHToMask(const SkPixmap& src, SkMaskBuilder& dst,
329 const SkMaskGamma::PreBlend& maskPreBlend,
330 const bool doBGR, const bool doVert) {
331 #define SAMPLES_PER_PIXEL 4
332 #define LCD_PER_PIXEL 3
333 SkASSERT(kAlpha_8_SkColorType == src.colorType());
334
335 const bool toA8 = SkMask::kA8_Format == dst.fFormat;
336 SkASSERT(SkMask::kLCD16_Format == dst.fFormat || toA8);
337
338 // doVert in this function means swap x and y when writing to dst.
339 if (doVert) {
340 SkASSERT(src.width() == (dst.fBounds.height() - 2) * 4);
341 SkASSERT(src.height() == dst.fBounds.width());
342 } else {
343 SkASSERT(src.width() == (dst.fBounds.width() - 2) * 4);
344 SkASSERT(src.height() == dst.fBounds.height());
345 }
346
347 const int sample_width = src.width();
348 const int height = src.height();
349
350 uint8_t* dstImage = dst.image();
351 size_t dstRB = dst.fRowBytes;
352 // An N tap FIR is defined by
353 // out[n] = coeff[0]*x[n] + coeff[1]*x[n-1] + ... + coeff[N]*x[n-N]
354 // or
355 // out[n] = sum(i, 0, N, coeff[i]*x[n-i])
356
357 // The strategy is to use one FIR (different coefficients) for each of r, g, and b.
358 // This means using every 4th FIR output value of each FIR and discarding the rest.
359 // The FIRs are aligned, and the coefficients reach 5 samples to each side of their 'center'.
360 // (For r and b this is technically incorrect, but the coeffs outside round to zero anyway.)
361
362 // These are in some fixed point repesentation.
363 // Adding up to more than one simulates ink spread.
364 // For implementation reasons, these should never add up to more than two.
365
366 // Coefficients determined by a gausian where 5 samples = 3 std deviations (0x110 'contrast').
367 // Calculated using tools/generate_fir_coeff.py
368 // With this one almost no fringing is ever seen, but it is imperceptibly blurry.
369 // The lcd smoothed text is almost imperceptibly different from gray,
370 // but is still sharper on small stems and small rounded corners than gray.
371 // This also seems to be about as wide as one can get and only have a three pixel kernel.
372 // TODO: calculate these at runtime so parameters can be adjusted (esp contrast).
373 static const unsigned int coefficients[LCD_PER_PIXEL][SAMPLES_PER_PIXEL*3] = {
374 //The red subpixel is centered inside the first sample (at 1/6 pixel), and is shifted.
375 { 0x03, 0x0b, 0x1c, 0x33, 0x40, 0x39, 0x24, 0x10, 0x05, 0x01, 0x00, 0x00, },
376 //The green subpixel is centered between two samples (at 1/2 pixel), so is symetric
377 { 0x00, 0x02, 0x08, 0x16, 0x2b, 0x3d, 0x3d, 0x2b, 0x16, 0x08, 0x02, 0x00, },
378 //The blue subpixel is centered inside the last sample (at 5/6 pixel), and is shifted.
379 { 0x00, 0x00, 0x01, 0x05, 0x10, 0x24, 0x39, 0x40, 0x33, 0x1c, 0x0b, 0x03, },
380 };
381
382 size_t dstPB = toA8 ? sizeof(uint8_t) : sizeof(uint16_t);
383 for (int y = 0; y < height; ++y) {
384 uint8_t* dstP;
385 size_t dstPDelta;
386 if (doVert) {
387 dstP = SkTAddOffset<uint8_t>(dstImage, y * dstPB);
388 dstPDelta = dstRB;
389 } else {
390 dstP = SkTAddOffset<uint8_t>(dstImage, y * dstRB);
391 dstPDelta = dstPB;
392 }
393
394 const uint8_t* srcP = src.addr8(0, y);
395
396 // TODO: this fir filter implementation is straight forward, but slow.
397 // It should be possible to make it much faster.
398 for (int sample_x = -4; sample_x < sample_width + 4; sample_x += 4) {
399 int fir[LCD_PER_PIXEL] = { 0 };
400 for (int sample_index = std::max(0, sample_x - 4), coeff_index = sample_index - (sample_x - 4)
401 ; sample_index < std::min(sample_x + 8, sample_width)
402 ; ++sample_index, ++coeff_index)
403 {
404 int sample_value = srcP[sample_index];
405 for (int subpxl_index = 0; subpxl_index < LCD_PER_PIXEL; ++subpxl_index) {
406 fir[subpxl_index] += coefficients[subpxl_index][coeff_index] * sample_value;
407 }
408 }
409 for (int subpxl_index = 0; subpxl_index < LCD_PER_PIXEL; ++subpxl_index) {
410 fir[subpxl_index] /= 0x100;
411 fir[subpxl_index] = std::min(fir[subpxl_index], 255);
412 }
413
414 U8CPU r, g, b;
415 if (doBGR) {
416 r = fir[2];
417 g = fir[1];
418 b = fir[0];
419 } else {
420 r = fir[0];
421 g = fir[1];
422 b = fir[2];
423 }
424 if constexpr (kSkShowTextBlitCoverage) {
425 r = std::max(r, 10u);
426 g = std::max(g, 10u);
427 b = std::max(b, 10u);
428 }
429 if (toA8) {
430 U8CPU a = (r + g + b) / 3;
431 if (maskPreBlend.isApplicable()) {
432 a = maskPreBlend.fG[a];
433 }
434 *dstP = a;
435 } else {
436 if (maskPreBlend.isApplicable()) {
437 r = maskPreBlend.fR[r];
438 g = maskPreBlend.fG[g];
439 b = maskPreBlend.fB[b];
440 }
441 *(uint16_t*)dstP = SkPack888ToRGB16(r, g, b);
442 }
443 dstP = SkTAddOffset<uint8_t>(dstP, dstPDelta);
444 }
445 }
446 }
447
convert_8_to_1(unsigned byte)448 static inline int convert_8_to_1(unsigned byte) {
449 SkASSERT(byte <= 0xFF);
450 return byte >> 7;
451 }
452
pack_8_to_1(const uint8_t alpha[8])453 static uint8_t pack_8_to_1(const uint8_t alpha[8]) {
454 unsigned bits = 0;
455 for (int i = 0; i < 8; ++i) {
456 bits <<= 1;
457 bits |= convert_8_to_1(alpha[i]);
458 }
459 return SkToU8(bits);
460 }
461
packA8ToA1(SkMaskBuilder & dstMask,const uint8_t * src,size_t srcRB)462 static void packA8ToA1(SkMaskBuilder& dstMask, const uint8_t* src, size_t srcRB) {
463 const int height = dstMask.fBounds.height();
464 const int width = dstMask.fBounds.width();
465 const int octs = width >> 3;
466 const int leftOverBits = width & 7;
467
468 uint8_t* dst = dstMask.image();
469 const int dstPad = dstMask.fRowBytes - SkAlign8(width)/8;
470 SkASSERT(dstPad >= 0);
471
472 SkASSERT(width >= 0);
473 SkASSERT(srcRB >= (size_t)width);
474 const size_t srcPad = srcRB - width;
475
476 for (int y = 0; y < height; ++y) {
477 for (int i = 0; i < octs; ++i) {
478 *dst++ = pack_8_to_1(src);
479 src += 8;
480 }
481 if (leftOverBits > 0) {
482 unsigned bits = 0;
483 int shift = 7;
484 for (int i = 0; i < leftOverBits; ++i, --shift) {
485 bits |= convert_8_to_1(*src++) << shift;
486 }
487 *dst++ = bits;
488 }
489 src += srcPad;
490 dst += dstPad;
491 }
492 }
493
GenerateImageFromPath(SkMaskBuilder & dstMask,const SkPath & path,const SkMaskGamma::PreBlend & maskPreBlend,const bool doBGR,const bool verticalLCD,const bool a8FromLCD,const bool hairline)494 void SkScalerContext::GenerateImageFromPath(
495 SkMaskBuilder& dstMask, const SkPath& path, const SkMaskGamma::PreBlend& maskPreBlend,
496 const bool doBGR, const bool verticalLCD, const bool a8FromLCD, const bool hairline)
497 {
498 SkASSERT(dstMask.fFormat == SkMask::kBW_Format ||
499 dstMask.fFormat == SkMask::kA8_Format ||
500 dstMask.fFormat == SkMask::kLCD16_Format);
501
502 SkPaint paint;
503 SkPath strokePath;
504 const SkPath* pathToUse = &path;
505
506 int srcW = dstMask.fBounds.width();
507 int srcH = dstMask.fBounds.height();
508 int dstW = srcW;
509 int dstH = srcH;
510
511 SkMatrix matrix;
512 matrix.setTranslate(-SkIntToScalar(dstMask.fBounds.fLeft),
513 -SkIntToScalar(dstMask.fBounds.fTop));
514
515 paint.setStroke(hairline);
516 paint.setAntiAlias(SkMask::kBW_Format != dstMask.fFormat);
517
518 const bool fromLCD = (dstMask.fFormat == SkMask::kLCD16_Format) ||
519 (dstMask.fFormat == SkMask::kA8_Format && a8FromLCD);
520 const bool intermediateDst = fromLCD || dstMask.fFormat == SkMask::kBW_Format;
521 if (fromLCD) {
522 if (verticalLCD) {
523 dstW = 4*dstH - 8;
524 dstH = srcW;
525 matrix.setAll(0, 4, -SkIntToScalar(dstMask.fBounds.fTop + 1) * 4,
526 1, 0, -SkIntToScalar(dstMask.fBounds.fLeft),
527 0, 0, 1);
528 } else {
529 dstW = 4*dstW - 8;
530 matrix.setAll(4, 0, -SkIntToScalar(dstMask.fBounds.fLeft + 1) * 4,
531 0, 1, -SkIntToScalar(dstMask.fBounds.fTop),
532 0, 0, 1);
533 }
534
535 // LCD hairline doesn't line up with the pixels, so do it the expensive way.
536 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
537 if (hairline) {
538 rec.setStrokeStyle(1.0f, false);
539 rec.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 0.0f);
540 }
541 if (rec.needToApply() && rec.applyToPath(&strokePath, path)) {
542 pathToUse = &strokePath;
543 paint.setStyle(SkPaint::kFill_Style);
544 }
545 }
546
547 SkRasterClip clip;
548 clip.setRect(SkIRect::MakeWH(dstW, dstH));
549
550 const SkImageInfo info = SkImageInfo::MakeA8(dstW, dstH);
551 SkAutoPixmapStorage dst;
552
553 if (intermediateDst) {
554 if (!dst.tryAlloc(info)) {
555 // can't allocate offscreen, so empty the mask and return
556 sk_bzero(dstMask.image(), dstMask.computeImageSize());
557 return;
558 }
559 } else {
560 dst.reset(info, dstMask.image(), dstMask.fRowBytes);
561 }
562 sk_bzero(dst.writable_addr(), dst.computeByteSize());
563
564 SkDrawBase draw;
565 draw.fBlitterChooser = SkA8Blitter_Choose;
566 draw.fDst = dst;
567 draw.fRC = &clip;
568 draw.fCTM = &matrix;
569 // We can save a copy if we had to use the local strokePath
570 draw.drawPath(*pathToUse, paint, nullptr, pathToUse == &strokePath);
571
572 switch (dstMask.fFormat) {
573 case SkMask::kBW_Format:
574 packA8ToA1(dstMask, dst.addr8(0, 0), dst.rowBytes());
575 break;
576 case SkMask::kA8_Format:
577 if (fromLCD) {
578 pack4xHToMask(dst, dstMask, maskPreBlend, doBGR, verticalLCD);
579 } else if (maskPreBlend.isApplicable()) {
580 applyLUTToA8Mask(dstMask, maskPreBlend.fG);
581 }
582 break;
583 case SkMask::kLCD16_Format:
584 pack4xHToMask(dst, dstMask, maskPreBlend, doBGR, verticalLCD);
585 break;
586 default:
587 break;
588 }
589 }
590
getImage(const SkGlyph & origGlyph)591 void SkScalerContext::getImage(const SkGlyph& origGlyph) {
592 SkASSERT(origGlyph.fAdvancesBoundsFormatAndInitialPathDone);
593
594 const SkGlyph* unfilteredGlyph = &origGlyph;
595 // in case we need to call generateImage on a mask-format that is different
596 // (i.e. larger) than what our caller allocated by looking at origGlyph.
597 SkAutoMalloc tmpGlyphImageStorage;
598 SkGlyph tmpGlyph;
599 SkSTArenaAlloc<sizeof(SkGlyph::PathData)> tmpGlyphPathDataStorage;
600 if (fMaskFilter) {
601 // need the original bounds, sans our maskfilter
602 sk_sp<SkMaskFilter> mf = std::move(fMaskFilter);
603 tmpGlyph = this->makeGlyph(origGlyph.getPackedID(), &tmpGlyphPathDataStorage);
604 fMaskFilter = std::move(mf);
605
606 // Use the origGlyph storage for the temporary unfiltered mask if it will fit.
607 if (tmpGlyph.fMaskFormat == origGlyph.fMaskFormat &&
608 tmpGlyph.imageSize() <= origGlyph.imageSize())
609 {
610 tmpGlyph.fImage = origGlyph.fImage;
611 } else {
612 tmpGlyphImageStorage.reset(tmpGlyph.imageSize());
613 tmpGlyph.fImage = tmpGlyphImageStorage.get();
614 }
615 unfilteredGlyph = &tmpGlyph;
616 }
617
618 if (!fGenerateImageFromPath) {
619 generateImage(*unfilteredGlyph, unfilteredGlyph->fImage);
620 } else {
621 SkASSERT(origGlyph.setPathHasBeenCalled());
622 const SkPath* devPath = origGlyph.path();
623
624 if (!devPath) {
625 generateImage(*unfilteredGlyph, unfilteredGlyph->fImage);
626 } else {
627 SkMaskBuilder mask(static_cast<uint8_t*>(unfilteredGlyph->fImage),
628 unfilteredGlyph->iRect(), unfilteredGlyph->rowBytes(),
629 unfilteredGlyph->maskFormat());
630 SkASSERT(SkMask::kARGB32_Format != origGlyph.fMaskFormat);
631 SkASSERT(SkMask::kARGB32_Format != mask.fFormat);
632 const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
633 const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
634 const bool a8LCD = SkToBool(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag);
635 const bool hairline = origGlyph.pathIsHairline();
636 GenerateImageFromPath(mask, *devPath, fPreBlend, doBGR, doVert, a8LCD, hairline);
637 }
638 }
639
640 if (fMaskFilter) {
641 // k3D_Format should not be mask filtered.
642 SkASSERT(SkMask::k3D_Format != unfilteredGlyph->fMaskFormat);
643
644 SkMaskBuilder srcMask;
645 SkAutoMaskFreeImage srcMaskOwnedImage(nullptr);
646 SkMatrix m;
647 fRec.getMatrixFrom2x2(&m);
648
649 if (as_MFB(fMaskFilter)->filterMask(&srcMask, unfilteredGlyph->mask(), m, nullptr)) {
650 // Filter succeeded; srcMask.fImage was allocated.
651 srcMaskOwnedImage.reset(srcMask.image());
652 } else if (unfilteredGlyph->fImage == tmpGlyphImageStorage.get()) {
653 // Filter did nothing; unfiltered mask is independent of origGlyph.fImage.
654 srcMask = SkMaskBuilder(static_cast<uint8_t*>(unfilteredGlyph->fImage),
655 unfilteredGlyph->iRect(), unfilteredGlyph->rowBytes(),
656 unfilteredGlyph->maskFormat());
657 } else if (origGlyph.iRect() == unfilteredGlyph->iRect()) {
658 // Filter did nothing; the unfiltered mask is in origGlyph.fImage and matches.
659 return;
660 } else {
661 // Filter did nothing; the unfiltered mask is in origGlyph.fImage and conflicts.
662 srcMask = SkMaskBuilder(static_cast<uint8_t*>(unfilteredGlyph->fImage),
663 unfilteredGlyph->iRect(), unfilteredGlyph->rowBytes(),
664 unfilteredGlyph->maskFormat());
665 size_t imageSize = unfilteredGlyph->imageSize();
666 tmpGlyphImageStorage.reset(imageSize);
667 srcMask.image() = static_cast<uint8_t*>(tmpGlyphImageStorage.get());
668 memcpy(srcMask.image(), unfilteredGlyph->fImage, imageSize);
669 }
670
671 SkASSERT_RELEASE(srcMask.fFormat == origGlyph.fMaskFormat);
672 SkMaskBuilder dstMask = SkMaskBuilder(static_cast<uint8_t*>(origGlyph.fImage),
673 origGlyph.iRect(), origGlyph.rowBytes(),
674 origGlyph.maskFormat());
675 SkIRect origBounds = dstMask.fBounds;
676
677 // Find the intersection of src and dst while updating the fImages.
678 if (srcMask.fBounds.fTop < dstMask.fBounds.fTop) {
679 int32_t topDiff = dstMask.fBounds.fTop - srcMask.fBounds.fTop;
680 srcMask.image() += srcMask.fRowBytes * topDiff;
681 srcMask.bounds().fTop = dstMask.fBounds.fTop;
682 }
683 if (dstMask.fBounds.fTop < srcMask.fBounds.fTop) {
684 int32_t topDiff = srcMask.fBounds.fTop - dstMask.fBounds.fTop;
685 dstMask.image() += dstMask.fRowBytes * topDiff;
686 dstMask.bounds().fTop = srcMask.fBounds.fTop;
687 }
688
689 if (srcMask.fBounds.fLeft < dstMask.fBounds.fLeft) {
690 int32_t leftDiff = dstMask.fBounds.fLeft - srcMask.fBounds.fLeft;
691 srcMask.image() += leftDiff;
692 srcMask.bounds().fLeft = dstMask.fBounds.fLeft;
693 }
694 if (dstMask.fBounds.fLeft < srcMask.fBounds.fLeft) {
695 int32_t leftDiff = srcMask.fBounds.fLeft - dstMask.fBounds.fLeft;
696 dstMask.image() += leftDiff;
697 dstMask.bounds().fLeft = srcMask.fBounds.fLeft;
698 }
699
700 if (srcMask.fBounds.fBottom < dstMask.fBounds.fBottom) {
701 dstMask.bounds().fBottom = srcMask.fBounds.fBottom;
702 }
703 if (dstMask.fBounds.fBottom < srcMask.fBounds.fBottom) {
704 srcMask.bounds().fBottom = dstMask.fBounds.fBottom;
705 }
706
707 if (srcMask.fBounds.fRight < dstMask.fBounds.fRight) {
708 dstMask.bounds().fRight = srcMask.fBounds.fRight;
709 }
710 if (dstMask.fBounds.fRight < srcMask.fBounds.fRight) {
711 srcMask.bounds().fRight = dstMask.fBounds.fRight;
712 }
713
714 SkASSERT(srcMask.fBounds == dstMask.fBounds);
715 int width = srcMask.fBounds.width();
716 int height = srcMask.fBounds.height();
717 int dstRB = dstMask.fRowBytes;
718 int srcRB = srcMask.fRowBytes;
719
720 const uint8_t* src = srcMask.fImage;
721 uint8_t* dst = dstMask.image();
722
723 if (SkMask::k3D_Format == srcMask.fFormat) {
724 // we have to copy 3 times as much
725 height *= 3;
726 }
727
728 // If not filling the full original glyph, clear it out first.
729 if (dstMask.fBounds != origBounds) {
730 sk_bzero(origGlyph.fImage, origGlyph.fHeight * origGlyph.rowBytes());
731 }
732
733 while (--height >= 0) {
734 memcpy(dst, src, width);
735 src += srcRB;
736 dst += dstRB;
737 }
738 }
739 }
740
getPath(SkGlyph & glyph,SkArenaAlloc * alloc)741 void SkScalerContext::getPath(SkGlyph& glyph, SkArenaAlloc* alloc) {
742 this->internalGetPath(glyph, alloc);
743 }
744
getDrawable(SkGlyph & glyph)745 sk_sp<SkDrawable> SkScalerContext::getDrawable(SkGlyph& glyph) {
746 return this->generateDrawable(glyph);
747 }
748 //TODO: make pure virtual
generateDrawable(const SkGlyph &)749 sk_sp<SkDrawable> SkScalerContext::generateDrawable(const SkGlyph&) {
750 return nullptr;
751 }
752
getFontMetrics(SkFontMetrics * fm)753 void SkScalerContext::getFontMetrics(SkFontMetrics* fm) {
754 SkASSERT(fm);
755 this->generateFontMetrics(fm);
756 }
757
758 ///////////////////////////////////////////////////////////////////////////////
759
internalGetPath(SkGlyph & glyph,SkArenaAlloc * alloc)760 void SkScalerContext::internalGetPath(SkGlyph& glyph, SkArenaAlloc* alloc) {
761 SkASSERT(glyph.fAdvancesBoundsFormatAndInitialPathDone);
762
763 if (glyph.setPathHasBeenCalled()) {
764 return;
765 }
766
767 SkPath path;
768 SkPath devPath;
769 bool hairline = false;
770 bool pathModified = false;
771
772 SkPackedGlyphID glyphID = glyph.getPackedID();
773 if (!generatePath(glyph, &path, &pathModified)) {
774 glyph.setPath(alloc, (SkPath*)nullptr, hairline, pathModified);
775 return;
776 }
777
778 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
779 SkFixed dx = glyphID.getSubXFixed();
780 SkFixed dy = glyphID.getSubYFixed();
781 if (dx | dy) {
782 pathModified = true;
783 path.offset(SkFixedToScalar(dx), SkFixedToScalar(dy));
784 }
785 }
786
787 if (fRec.fFrameWidth < 0 && fPathEffect == nullptr) {
788 devPath.swap(path);
789 } else {
790 pathModified = true; // It could still end up the same, but it's probably going to change.
791
792 // need the path in user-space, with only the point-size applied
793 // so that our stroking and effects will operate the same way they
794 // would if the user had extracted the path themself, and then
795 // called drawPath
796 SkPath localPath;
797 SkMatrix matrix;
798 SkMatrix inverse;
799
800 fRec.getMatrixFrom2x2(&matrix);
801 if (!matrix.invert(&inverse)) {
802 glyph.setPath(alloc, &devPath, hairline, pathModified);
803 }
804 path.transform(inverse, &localPath);
805 // now localPath is only affected by the paint settings, and not the canvas matrix
806
807 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
808
809 if (fRec.fFrameWidth >= 0) {
810 rec.setStrokeStyle(fRec.fFrameWidth,
811 SkToBool(fRec.fFlags & kFrameAndFill_Flag));
812 // glyphs are always closed contours, so cap type is ignored,
813 // so we just pass something.
814 rec.setStrokeParams((SkPaint::Cap)fRec.fStrokeCap,
815 (SkPaint::Join)fRec.fStrokeJoin,
816 fRec.fMiterLimit);
817 }
818
819 if (fPathEffect) {
820 SkPath effectPath;
821 if (fPathEffect->filterPath(&effectPath, localPath, &rec, nullptr, matrix)) {
822 localPath.swap(effectPath);
823 }
824 }
825
826 if (rec.needToApply()) {
827 SkPath strokePath;
828 if (rec.applyToPath(&strokePath, localPath)) {
829 localPath.swap(strokePath);
830 }
831 }
832
833 // The path effect may have modified 'rec', so wait to here to check hairline status.
834 if (rec.isHairlineStyle()) {
835 hairline = true;
836 }
837
838 localPath.transform(matrix, &devPath);
839 }
840 glyph.setPath(alloc, &devPath, hairline, pathModified);
841 }
842
843
getMatrixFrom2x2(SkMatrix * dst) const844 void SkScalerContextRec::getMatrixFrom2x2(SkMatrix* dst) const {
845 dst->setAll(fPost2x2[0][0], fPost2x2[0][1], 0,
846 fPost2x2[1][0], fPost2x2[1][1], 0,
847 0, 0, 1);
848 }
849
getLocalMatrix(SkMatrix * m) const850 void SkScalerContextRec::getLocalMatrix(SkMatrix* m) const {
851 *m = SkFontPriv::MakeTextMatrix(fTextSize, fPreScaleX, fPreSkewX);
852 }
853
getSingleMatrix(SkMatrix * m) const854 void SkScalerContextRec::getSingleMatrix(SkMatrix* m) const {
855 this->getLocalMatrix(m);
856
857 // now concat the device matrix
858 SkMatrix deviceMatrix;
859 this->getMatrixFrom2x2(&deviceMatrix);
860 m->postConcat(deviceMatrix);
861 }
862
computeMatrices(PreMatrixScale preMatrixScale,SkVector * s,SkMatrix * sA,SkMatrix * GsA,SkMatrix * G_inv,SkMatrix * A_out)863 bool SkScalerContextRec::computeMatrices(PreMatrixScale preMatrixScale, SkVector* s, SkMatrix* sA,
864 SkMatrix* GsA, SkMatrix* G_inv, SkMatrix* A_out)
865 {
866 // A is the 'total' matrix.
867 SkMatrix A;
868 this->getSingleMatrix(&A);
869
870 // The caller may find the 'total' matrix useful when dealing directly with EM sizes.
871 if (A_out) {
872 *A_out = A;
873 }
874
875 // GA is the matrix A with rotation removed.
876 SkMatrix GA;
877 bool skewedOrFlipped = A.getSkewX() || A.getSkewY() || A.getScaleX() < 0 || A.getScaleY() < 0;
878 if (skewedOrFlipped) {
879 // QR by Givens rotations. G is Q^T and GA is R. G is rotational (no reflections).
880 // h is where A maps the horizontal baseline.
881 SkPoint h = SkPoint::Make(SK_Scalar1, 0);
882 A.mapPoints(&h, 1);
883
884 // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
885 SkMatrix G;
886 SkComputeGivensRotation(h, &G);
887
888 GA = G;
889 GA.preConcat(A);
890
891 // The 'remainingRotation' is G inverse, which is fairly simple since G is 2x2 rotational.
892 if (G_inv) {
893 G_inv->setAll(
894 G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX),
895 -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY),
896 G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2));
897 }
898 } else {
899 GA = A;
900 if (G_inv) {
901 G_inv->reset();
902 }
903 }
904
905 // If the 'total' matrix is singular, set the 'scale' to something finite and zero the matrices.
906 // All underlying ports have issues with zero text size, so use the matricies to zero.
907 // If one of the scale factors is less than 1/256 then an EM filling square will
908 // never affect any pixels.
909 // If there are any nonfinite numbers in the matrix, bail out and set the matrices to zero.
910 if (SkScalarAbs(GA.get(SkMatrix::kMScaleX)) <= SK_ScalarNearlyZero ||
911 SkScalarAbs(GA.get(SkMatrix::kMScaleY)) <= SK_ScalarNearlyZero ||
912 !GA.isFinite())
913 {
914 s->fX = SK_Scalar1;
915 s->fY = SK_Scalar1;
916 sA->setScale(0, 0);
917 if (GsA) {
918 GsA->setScale(0, 0);
919 }
920 if (G_inv) {
921 G_inv->reset();
922 }
923 return false;
924 }
925
926 // At this point, given GA, create s.
927 switch (preMatrixScale) {
928 case PreMatrixScale::kFull:
929 s->fX = SkScalarAbs(GA.get(SkMatrix::kMScaleX));
930 s->fY = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
931 break;
932 case PreMatrixScale::kVertical: {
933 SkScalar yScale = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
934 s->fX = yScale;
935 s->fY = yScale;
936 break;
937 }
938 case PreMatrixScale::kVerticalInteger: {
939 SkScalar realYScale = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
940 SkScalar intYScale = SkScalarRoundToScalar(realYScale);
941 if (intYScale == 0) {
942 intYScale = SK_Scalar1;
943 }
944 s->fX = intYScale;
945 s->fY = intYScale;
946 break;
947 }
948 }
949
950 // The 'remaining' matrix sA is the total matrix A without the scale.
951 if (!skewedOrFlipped && (
952 (PreMatrixScale::kFull == preMatrixScale) ||
953 (PreMatrixScale::kVertical == preMatrixScale && A.getScaleX() == A.getScaleY())))
954 {
955 // If GA == A and kFull, sA is identity.
956 // If GA == A and kVertical and A.scaleX == A.scaleY, sA is identity.
957 sA->reset();
958 } else if (!skewedOrFlipped && PreMatrixScale::kVertical == preMatrixScale) {
959 // If GA == A and kVertical, sA.scaleY is SK_Scalar1.
960 sA->reset();
961 sA->setScaleX(A.getScaleX() / s->fY);
962 } else {
963 // TODO: like kVertical, kVerticalInteger with int scales.
964 *sA = A;
965 sA->preScale(SkScalarInvert(s->fX), SkScalarInvert(s->fY));
966 }
967
968 // The 'remainingWithoutRotation' matrix GsA is the non-rotational part of A without the scale.
969 if (GsA) {
970 *GsA = GA;
971 // G is rotational so reorders with the scale.
972 GsA->preScale(SkScalarInvert(s->fX), SkScalarInvert(s->fY));
973 }
974
975 return true;
976 }
977
computeAxisAlignmentForHText() const978 SkAxisAlignment SkScalerContext::computeAxisAlignmentForHText() const {
979 return fRec.computeAxisAlignmentForHText();
980 }
981
computeAxisAlignmentForHText() const982 SkAxisAlignment SkScalerContextRec::computeAxisAlignmentForHText() const {
983 // Why fPost2x2 can be used here.
984 // getSingleMatrix multiplies in getLocalMatrix, which consists of
985 // * fTextSize (a scale, which has no effect)
986 // * fPreScaleX (a scale in x, which has no effect)
987 // * fPreSkewX (has no effect, but would on vertical text alignment).
988 // In other words, making the text bigger, stretching it along the
989 // horizontal axis, or fake italicizing it does not move the baseline.
990 if (!SkToBool(fFlags & SkScalerContext::kBaselineSnap_Flag)) {
991 return SkAxisAlignment::kNone;
992 }
993
994 if (0 == fPost2x2[1][0]) {
995 // The x axis is mapped onto the x axis.
996 return SkAxisAlignment::kX;
997 }
998 if (0 == fPost2x2[0][0]) {
999 // The x axis is mapped onto the y axis.
1000 return SkAxisAlignment::kY;
1001 }
1002 return SkAxisAlignment::kNone;
1003 }
1004
setLuminanceColor(SkColor c)1005 void SkScalerContextRec::setLuminanceColor(SkColor c) {
1006 fLumBits = SkMaskGamma::CanonicalColor(
1007 SkColorSetRGB(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)));
1008 }
1009
useStrokeForFakeBold()1010 void SkScalerContextRec::useStrokeForFakeBold() {
1011 if (!SkToBool(fFlags & SkScalerContext::kEmbolden_Flag)) {
1012 return;
1013 }
1014 fFlags &= ~SkScalerContext::kEmbolden_Flag;
1015
1016 SkScalar fakeBoldScale = SkScalarInterpFunc(fTextSize,
1017 kStdFakeBoldInterpKeys,
1018 kStdFakeBoldInterpValues,
1019 kStdFakeBoldInterpLength);
1020 SkScalar extra = fTextSize * fakeBoldScale;
1021
1022 if (fFrameWidth >= 0) {
1023 fFrameWidth += extra;
1024 } else {
1025 fFlags |= SkScalerContext::kFrameAndFill_Flag;
1026 fFrameWidth = extra;
1027 SkPaint paint;
1028 fMiterLimit = paint.getStrokeMiter();
1029 fStrokeJoin = SkToU8(paint.getStrokeJoin());
1030 fStrokeCap = SkToU8(paint.getStrokeCap());
1031 }
1032 }
1033
1034 /*
1035 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1036 * that vary only slightly when we create our key into the font cache, since the font scaler
1037 * typically returns the same looking resuts for tiny changes in the matrix.
1038 */
sk_relax(SkScalar x)1039 static SkScalar sk_relax(SkScalar x) {
1040 SkScalar n = SkScalarRoundToScalar(x * 1024);
1041 return n / 1024.0f;
1042 }
1043
compute_mask_format(const SkFont & font)1044 static SkMask::Format compute_mask_format(const SkFont& font) {
1045 switch (font.getEdging()) {
1046 case SkFont::Edging::kAlias:
1047 return SkMask::kBW_Format;
1048 case SkFont::Edging::kAntiAlias:
1049 return SkMask::kA8_Format;
1050 case SkFont::Edging::kSubpixelAntiAlias:
1051 return SkMask::kLCD16_Format;
1052 }
1053 SkASSERT(false);
1054 return SkMask::kA8_Format;
1055 }
1056
1057 // Beyond this size, LCD doesn't appreciably improve quality, but it always
1058 // cost more RAM and draws slower, so we set a cap.
1059 #ifndef SK_MAX_SIZE_FOR_LCDTEXT
1060 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1061 #endif
1062
1063 const SkScalar gMaxSize2ForLCDText = SK_MAX_SIZE_FOR_LCDTEXT * SK_MAX_SIZE_FOR_LCDTEXT;
1064
too_big_for_lcd(const SkScalerContextRec & rec,bool checkPost2x2)1065 static bool too_big_for_lcd(const SkScalerContextRec& rec, bool checkPost2x2) {
1066 if (checkPost2x2) {
1067 SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] -
1068 rec.fPost2x2[1][0] * rec.fPost2x2[0][1];
1069 area *= rec.fTextSize * rec.fTextSize;
1070 return area > gMaxSize2ForLCDText;
1071 } else {
1072 return rec.fTextSize > SK_MAX_SIZE_FOR_LCDTEXT;
1073 }
1074 }
1075
1076 // The only reason this is not file static is because it needs the context of SkScalerContext to
1077 // access SkPaint::computeLuminanceColor.
MakeRecAndEffects(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix,SkScalerContextRec * rec,SkScalerContextEffects * effects)1078 void SkScalerContext::MakeRecAndEffects(const SkFont& font, const SkPaint& paint,
1079 const SkSurfaceProps& surfaceProps,
1080 SkScalerContextFlags scalerContextFlags,
1081 const SkMatrix& deviceMatrix,
1082 SkScalerContextRec* rec,
1083 SkScalerContextEffects* effects) {
1084 SkASSERT(!deviceMatrix.hasPerspective());
1085
1086 sk_bzero(rec, sizeof(SkScalerContextRec));
1087
1088 SkTypeface* typeface = font.getTypeface();
1089
1090 rec->fTypefaceID = typeface->uniqueID();
1091 rec->fTextSize = font.getSize();
1092 rec->fPreScaleX = font.getScaleX();
1093 rec->fPreSkewX = font.getSkewX();
1094
1095 bool checkPost2x2 = false;
1096
1097 const SkMatrix::TypeMask mask = deviceMatrix.getType();
1098 if (mask & SkMatrix::kScale_Mask) {
1099 rec->fPost2x2[0][0] = sk_relax(deviceMatrix.getScaleX());
1100 rec->fPost2x2[1][1] = sk_relax(deviceMatrix.getScaleY());
1101 checkPost2x2 = true;
1102 } else {
1103 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1104 }
1105 if (mask & SkMatrix::kAffine_Mask) {
1106 rec->fPost2x2[0][1] = sk_relax(deviceMatrix.getSkewX());
1107 rec->fPost2x2[1][0] = sk_relax(deviceMatrix.getSkewY());
1108 checkPost2x2 = true;
1109 } else {
1110 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1111 }
1112
1113 SkPaint::Style style = paint.getStyle();
1114 SkScalar strokeWidth = paint.getStrokeWidth();
1115
1116 unsigned flags = 0;
1117
1118 if (font.isEmbolden()) {
1119 flags |= SkScalerContext::kEmbolden_Flag;
1120 }
1121
1122 if (style != SkPaint::kFill_Style && strokeWidth >= 0) {
1123 rec->fFrameWidth = strokeWidth;
1124 rec->fMiterLimit = paint.getStrokeMiter();
1125 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1126 rec->fStrokeCap = SkToU8(paint.getStrokeCap());
1127
1128 if (style == SkPaint::kStrokeAndFill_Style) {
1129 flags |= SkScalerContext::kFrameAndFill_Flag;
1130 }
1131 } else {
1132 rec->fFrameWidth = -1;
1133 rec->fMiterLimit = 0;
1134 rec->fStrokeJoin = 0;
1135 rec->fStrokeCap = 0;
1136 }
1137
1138 rec->fMaskFormat = compute_mask_format(font);
1139
1140 if (SkMask::kLCD16_Format == rec->fMaskFormat) {
1141 if (too_big_for_lcd(*rec, checkPost2x2)) {
1142 rec->fMaskFormat = SkMask::kA8_Format;
1143 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1144 } else {
1145 SkPixelGeometry geometry = surfaceProps.pixelGeometry();
1146
1147 switch (geometry) {
1148 case kUnknown_SkPixelGeometry:
1149 // eeek, can't support LCD
1150 rec->fMaskFormat = SkMask::kA8_Format;
1151 flags |= SkScalerContext::kGenA8FromLCD_Flag;
1152 break;
1153 case kRGB_H_SkPixelGeometry:
1154 // our default, do nothing.
1155 break;
1156 case kBGR_H_SkPixelGeometry:
1157 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1158 break;
1159 case kRGB_V_SkPixelGeometry:
1160 flags |= SkScalerContext::kLCD_Vertical_Flag;
1161 break;
1162 case kBGR_V_SkPixelGeometry:
1163 flags |= SkScalerContext::kLCD_Vertical_Flag;
1164 flags |= SkScalerContext::kLCD_BGROrder_Flag;
1165 break;
1166 }
1167 }
1168 }
1169
1170 if (font.isEmbeddedBitmaps()) {
1171 flags |= SkScalerContext::kEmbeddedBitmapText_Flag;
1172 }
1173 if (font.isSubpixel()) {
1174 flags |= SkScalerContext::kSubpixelPositioning_Flag;
1175 }
1176 if (font.isForceAutoHinting()) {
1177 flags |= SkScalerContext::kForceAutohinting_Flag;
1178 }
1179 if (font.isLinearMetrics()) {
1180 flags |= SkScalerContext::kLinearMetrics_Flag;
1181 }
1182 if (font.isBaselineSnap()) {
1183 flags |= SkScalerContext::kBaselineSnap_Flag;
1184 }
1185 if (typeface->glyphMaskNeedsCurrentColor()) {
1186 flags |= SkScalerContext::kNeedsForegroundColor_Flag;
1187 rec->fForegroundColor = paint.getColor();
1188 }
1189 rec->fFlags = SkToU16(flags);
1190
1191 // these modify fFlags, so do them after assigning fFlags
1192 rec->setHinting(font.getHinting());
1193 rec->setLuminanceColor(SkPaintPriv::ComputeLuminanceColor(paint));
1194
1195 // The paint color is always converted to the device colr space,
1196 // so the paint gamma is now always equal to the device gamma.
1197 // The math in SkMaskGamma can handle them being different,
1198 // but it requires superluminous masks when
1199 // Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
1200 rec->setDeviceGamma(surfaceProps.textGamma());
1201 rec->setContrast(surfaceProps.textContrast());
1202
1203 if (!SkToBool(scalerContextFlags & SkScalerContextFlags::kFakeGamma)) {
1204 rec->ignoreGamma();
1205 }
1206 if (!SkToBool(scalerContextFlags & SkScalerContextFlags::kBoostContrast)) {
1207 rec->setContrast(0);
1208 }
1209
1210 new (effects) SkScalerContextEffects{paint};
1211 }
1212
CreateDescriptorAndEffectsUsingPaint(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix,SkAutoDescriptor * ad,SkScalerContextEffects * effects)1213 SkDescriptor* SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
1214 const SkFont& font, const SkPaint& paint, const SkSurfaceProps& surfaceProps,
1215 SkScalerContextFlags scalerContextFlags, const SkMatrix& deviceMatrix, SkAutoDescriptor* ad,
1216 SkScalerContextEffects* effects)
1217 {
1218 SkScalerContextRec rec;
1219 MakeRecAndEffects(font, paint, surfaceProps, scalerContextFlags, deviceMatrix, &rec, effects);
1220 return AutoDescriptorGivenRecAndEffects(rec, *effects, ad);
1221 }
1222
calculate_size_and_flatten(const SkScalerContextRec & rec,const SkScalerContextEffects & effects,SkBinaryWriteBuffer * effectBuffer)1223 static size_t calculate_size_and_flatten(const SkScalerContextRec& rec,
1224 const SkScalerContextEffects& effects,
1225 SkBinaryWriteBuffer* effectBuffer) {
1226 size_t descSize = sizeof(rec);
1227 int entryCount = 1;
1228
1229 if (effects.fPathEffect || effects.fMaskFilter) {
1230 if (effects.fPathEffect) { effectBuffer->writeFlattenable(effects.fPathEffect); }
1231 if (effects.fMaskFilter) { effectBuffer->writeFlattenable(effects.fMaskFilter); }
1232 entryCount += 1;
1233 descSize += effectBuffer->bytesWritten();
1234 }
1235
1236 descSize += SkDescriptor::ComputeOverhead(entryCount);
1237 return descSize;
1238 }
1239
generate_descriptor(const SkScalerContextRec & rec,const SkBinaryWriteBuffer & effectBuffer,SkDescriptor * desc)1240 static void generate_descriptor(const SkScalerContextRec& rec,
1241 const SkBinaryWriteBuffer& effectBuffer,
1242 SkDescriptor* desc) {
1243 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1244
1245 if (effectBuffer.bytesWritten() > 0) {
1246 effectBuffer.writeToMemory(desc->addEntry(kEffects_SkDescriptorTag,
1247 effectBuffer.bytesWritten(),
1248 nullptr));
1249 }
1250
1251 desc->computeChecksum();
1252 }
1253
AutoDescriptorGivenRecAndEffects(const SkScalerContextRec & rec,const SkScalerContextEffects & effects,SkAutoDescriptor * ad)1254 SkDescriptor* SkScalerContext::AutoDescriptorGivenRecAndEffects(
1255 const SkScalerContextRec& rec,
1256 const SkScalerContextEffects& effects,
1257 SkAutoDescriptor* ad)
1258 {
1259 SkBinaryWriteBuffer buf({});
1260
1261 ad->reset(calculate_size_and_flatten(rec, effects, &buf));
1262 generate_descriptor(rec, buf, ad->getDesc());
1263
1264 return ad->getDesc();
1265 }
1266
DescriptorGivenRecAndEffects(const SkScalerContextRec & rec,const SkScalerContextEffects & effects)1267 std::unique_ptr<SkDescriptor> SkScalerContext::DescriptorGivenRecAndEffects(
1268 const SkScalerContextRec& rec,
1269 const SkScalerContextEffects& effects)
1270 {
1271 SkBinaryWriteBuffer buf({});
1272
1273 auto desc = SkDescriptor::Alloc(calculate_size_and_flatten(rec, effects, &buf));
1274 generate_descriptor(rec, buf, desc.get());
1275
1276 return desc;
1277 }
1278
DescriptorBufferGiveRec(const SkScalerContextRec & rec,void * buffer)1279 void SkScalerContext::DescriptorBufferGiveRec(const SkScalerContextRec& rec, void* buffer) {
1280 generate_descriptor(rec, SkBinaryWriteBuffer({}), (SkDescriptor*)buffer);
1281 }
1282
CheckBufferSizeForRec(const SkScalerContextRec & rec,const SkScalerContextEffects & effects,size_t size)1283 bool SkScalerContext::CheckBufferSizeForRec(const SkScalerContextRec& rec,
1284 const SkScalerContextEffects& effects,
1285 size_t size) {
1286 SkBinaryWriteBuffer buf({});
1287 return size >= calculate_size_and_flatten(rec, effects, &buf);
1288 }
1289
MakeEmpty(SkTypeface & typeface,const SkScalerContextEffects & effects,const SkDescriptor * desc)1290 std::unique_ptr<SkScalerContext> SkScalerContext::MakeEmpty(
1291 SkTypeface& typeface, const SkScalerContextEffects& effects,
1292 const SkDescriptor* desc) {
1293 class SkScalerContext_Empty : public SkScalerContext {
1294 public:
1295 SkScalerContext_Empty(SkTypeface& typeface, const SkScalerContextEffects& effects,
1296 const SkDescriptor* desc)
1297 : SkScalerContext(typeface, effects, desc) {}
1298
1299 protected:
1300 GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override {
1301 return {glyph.maskFormat()};
1302 }
1303 void generateImage(const SkGlyph&, void*) override {}
1304 bool generatePath(const SkGlyph& glyph, SkPath* path, bool* modified) override {
1305 path->reset();
1306 return false;
1307 }
1308 void generateFontMetrics(SkFontMetrics* metrics) override {
1309 if (metrics) {
1310 sk_bzero(metrics, sizeof(*metrics));
1311 }
1312 }
1313 };
1314
1315 return std::make_unique<SkScalerContext_Empty>(typeface, effects, desc);
1316 }
1317
1318
1319
1320
1321