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