Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
120static SkMaskGamma* gMaskGamma = nullptr;
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 */
129static const SkMaskGamma& cached_mask_gamma(SkScalar contrast, SkScalar paintGamma,
130 SkScalar deviceGamma) {
131 mask_gamma_cache_mutex().assertHeld();
132 if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
133 if (nullptr == gLinearMaskGamma) {
135 }
136 return *gLinearMaskGamma;
137 }
138 if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
140 gMaskGamma = new SkMaskGamma(contrast, paintGamma, deviceGamma);
141 gContrast = contrast;
142 gPaintGamma = paintGamma;
143 gDeviceGamma = deviceGamma;
144 }
145 return *gMaskGamma;
146}
147
148/**
149 * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
150 */
153
154 const SkMaskGamma& maskGamma = cached_mask_gamma(rec.getContrast(),
155 rec.getPaintGamma(),
156 rec.getDeviceGamma());
157
158 // TODO: remove CanonicalColor when we to fix up Chrome layout tests.
159 return maskGamma.preBlend(rec.getLuminanceColor());
160}
161
163 SkScalar deviceGamma, int* width, int* height) {
165 const SkMaskGamma& maskGamma = cached_mask_gamma(contrast,
166 paintGamma,
167 deviceGamma);
168
170 size_t size = (*width)*(*height)*sizeof(uint8_t);
171
172 return size;
173}
174
175bool SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma,
176 uint8_t* data) {
178 const SkMaskGamma& maskGamma = cached_mask_gamma(contrast,
179 paintGamma,
180 deviceGamma);
181 const uint8_t* gammaTables = maskGamma.getGammaTables();
182 if (!gammaTables) {
183 return false;
184 }
185
186 int width, height;
188 size_t size = width*height * sizeof(uint8_t);
189 memcpy(data, gammaTables, size);
190 return true;
191}
192
194 return internalMakeGlyph(packedID, fRec.fMaskFormat, alloc);
195}
196
197/** Return the closest D for the given S. Returns std::numeric_limits<D>::max() for NaN. */
198template <typename D, typename S> static constexpr D sk_saturate_cast(S s) {
199 static_assert(std::is_integral_v<D>);
200 s = s < std::numeric_limits<D>::max() ? s : std::numeric_limits<D>::max();
201 s = s > std::numeric_limits<D>::min() ? s : std::numeric_limits<D>::min();
202 return (D)s;
203}
205 r.roundOut(&r);
206 glyph->fLeft = sk_saturate_cast<int16_t>(r.fLeft);
207 glyph->fTop = sk_saturate_cast<int16_t>(r.fTop);
208 glyph->fWidth = sk_saturate_cast<uint16_t>(r.width());
209 glyph->fHeight = sk_saturate_cast<uint16_t>(r.height());
210}
212 glyph->fLeft = sk_saturate_cast<int16_t>(r.fLeft);
213 glyph->fTop = sk_saturate_cast<int16_t>(r.fTop);
214 glyph->fWidth = sk_saturate_cast<uint16_t>(r.width64());
215 glyph->fHeight = sk_saturate_cast<uint16_t>(r.height64());
216}
217
219 SkGlyph* glyph, const SkPath& devPath, SkMask::Format format,
220 const bool verticalLCD, const bool a8FromLCD, const bool hairline)
221{
222 // Only BW, A8, and LCD16 can be produced from paths.
223 if (glyph->fMaskFormat != SkMask::kBW_Format &&
224 glyph->fMaskFormat != SkMask::kA8_Format &&
225 glyph->fMaskFormat != SkMask::kLCD16_Format)
226 {
227 glyph->fMaskFormat = SkMask::kA8_Format;
228 }
229
230 SkRect bounds = devPath.getBounds();
231 if (!bounds.isEmpty()) {
232 const bool fromLCD = (glyph->fMaskFormat == SkMask::kLCD16_Format) ||
233 (glyph->fMaskFormat == SkMask::kA8_Format && a8FromLCD);
234
235 const bool needExtraWidth = (fromLCD && !verticalLCD) || hairline;
236 const bool needExtraHeight = (fromLCD && verticalLCD) || hairline;
237 if (needExtraWidth) {
238 bounds.roundOut(&bounds);
239 bounds.outset(1, 0);
240 }
241 if (needExtraHeight) {
242 bounds.roundOut(&bounds);
243 bounds.outset(0, 1);
244 }
245 }
246 SaturateGlyphBounds(glyph, std::move(bounds));
247}
248
249SkGlyph SkScalerContext::internalMakeGlyph(SkPackedGlyphID packedID, SkMask::Format format, SkArenaAlloc* alloc) {
250 auto zeroBounds = [](SkGlyph& glyph) {
251 glyph.fLeft = 0;
252 glyph.fTop = 0;
253 glyph.fWidth = 0;
254 glyph.fHeight = 0;
255 };
256
257 SkGlyph glyph{packedID};
258 glyph.fMaskFormat = format; // subclass may return a different value
259 GlyphMetrics mx = this->generateMetrics(glyph, alloc);
260 SkASSERT(!mx.neverRequestPath || !mx.computeFromPath);
261
262 glyph.fAdvanceX = mx.advance.fX;
263 glyph.fAdvanceY = mx.advance.fY;
264 glyph.fMaskFormat = mx.maskFormat;
265 glyph.fScalerContextBits = mx.extraBits;
266
267 if (mx.computeFromPath || (fGenerateImageFromPath && !mx.neverRequestPath)) {
268 SkDEBUGCODE(glyph.fAdvancesBoundsFormatAndInitialPathDone = true;)
269 this->internalGetPath(glyph, alloc);
270 const SkPath* devPath = glyph.path();
271 if (devPath) {
274 const bool hairline = glyph.pathIsHairline();
275 GenerateMetricsFromPath(&glyph, *devPath, format, doVert, a8LCD, hairline);
276 }
277 } else {
278 SaturateGlyphBounds(&glyph, std::move(mx.bounds));
279 if (mx.neverRequestPath) {
280 glyph.setPath(alloc, nullptr, false);
281 }
282 }
283 SkDEBUGCODE(glyph.fAdvancesBoundsFormatAndInitialPathDone = true;)
284
285 // if either dimension is empty, zap the image bounds of the glyph
286 if (0 == glyph.fWidth || 0 == glyph.fHeight) {
287 zeroBounds(glyph);
288 return glyph;
289 }
290
291 if (fMaskFilter) {
292 // only want the bounds from the filter
293 SkMask src(nullptr, glyph.iRect(), glyph.rowBytes(), glyph.maskFormat());
296
297 fRec.getMatrixFrom2x2(&matrix);
298
299 if (as_MFB(fMaskFilter)->filterMask(&dst, src, matrix, nullptr)) {
300 if (dst.fBounds.isEmpty()) {
301 zeroBounds(glyph);
302 return glyph;
303 }
304 SkASSERT(dst.fImage == nullptr);
305 SaturateGlyphBounds(&glyph, dst.fBounds);
306 glyph.fMaskFormat = dst.fFormat;
307 }
308 }
309 return glyph;
310}
311
312static void applyLUTToA8Mask(SkMaskBuilder& mask, const uint8_t* lut) {
313 uint8_t* SK_RESTRICT dst = mask.image();
314 unsigned rowBytes = mask.fRowBytes;
315
316 for (int y = mask.fBounds.height() - 1; y >= 0; --y) {
317 for (int x = mask.fBounds.width() - 1; x >= 0; --x) {
318 dst[x] = lut[dst[x]];
319 }
320 dst += rowBytes;
321 }
322}
323
324static void pack4xHToMask(const SkPixmap& src, SkMaskBuilder& dst,
325 const SkMaskGamma::PreBlend& maskPreBlend,
326 const bool doBGR, const bool doVert) {
327#define SAMPLES_PER_PIXEL 4
328#define LCD_PER_PIXEL 3
329 SkASSERT(kAlpha_8_SkColorType == src.colorType());
330
331 const bool toA8 = SkMask::kA8_Format == dst.fFormat;
332 SkASSERT(SkMask::kLCD16_Format == dst.fFormat || toA8);
333
334 // doVert in this function means swap x and y when writing to dst.
335 if (doVert) {
336 SkASSERT(src.width() == (dst.fBounds.height() - 2) * 4);
337 SkASSERT(src.height() == dst.fBounds.width());
338 } else {
339 SkASSERT(src.width() == (dst.fBounds.width() - 2) * 4);
340 SkASSERT(src.height() == dst.fBounds.height());
341 }
342
343 const int sample_width = src.width();
344 const int height = src.height();
345
346 uint8_t* dstImage = dst.image();
347 size_t dstRB = dst.fRowBytes;
348 // An N tap FIR is defined by
349 // out[n] = coeff[0]*x[n] + coeff[1]*x[n-1] + ... + coeff[N]*x[n-N]
350 // or
351 // out[n] = sum(i, 0, N, coeff[i]*x[n-i])
352
353 // The strategy is to use one FIR (different coefficients) for each of r, g, and b.
354 // This means using every 4th FIR output value of each FIR and discarding the rest.
355 // The FIRs are aligned, and the coefficients reach 5 samples to each side of their 'center'.
356 // (For r and b this is technically incorrect, but the coeffs outside round to zero anyway.)
357
358 // These are in some fixed point repesentation.
359 // Adding up to more than one simulates ink spread.
360 // For implementation reasons, these should never add up to more than two.
361
362 // Coefficients determined by a gausian where 5 samples = 3 std deviations (0x110 'contrast').
363 // Calculated using tools/generate_fir_coeff.py
364 // With this one almost no fringing is ever seen, but it is imperceptibly blurry.
365 // The lcd smoothed text is almost imperceptibly different from gray,
366 // but is still sharper on small stems and small rounded corners than gray.
367 // This also seems to be about as wide as one can get and only have a three pixel kernel.
368 // TODO: calculate these at runtime so parameters can be adjusted (esp contrast).
369 static const unsigned int coefficients[LCD_PER_PIXEL][SAMPLES_PER_PIXEL*3] = {
370 //The red subpixel is centered inside the first sample (at 1/6 pixel), and is shifted.
371 { 0x03, 0x0b, 0x1c, 0x33, 0x40, 0x39, 0x24, 0x10, 0x05, 0x01, 0x00, 0x00, },
372 //The green subpixel is centered between two samples (at 1/2 pixel), so is symetric
373 { 0x00, 0x02, 0x08, 0x16, 0x2b, 0x3d, 0x3d, 0x2b, 0x16, 0x08, 0x02, 0x00, },
374 //The blue subpixel is centered inside the last sample (at 5/6 pixel), and is shifted.
375 { 0x00, 0x00, 0x01, 0x05, 0x10, 0x24, 0x39, 0x40, 0x33, 0x1c, 0x0b, 0x03, },
376 };
377
378 size_t dstPB = toA8 ? sizeof(uint8_t) : sizeof(uint16_t);
379 for (int y = 0; y < height; ++y) {
380 uint8_t* dstP;
381 size_t dstPDelta;
382 if (doVert) {
383 dstP = SkTAddOffset<uint8_t>(dstImage, y * dstPB);
384 dstPDelta = dstRB;
385 } else {
386 dstP = SkTAddOffset<uint8_t>(dstImage, y * dstRB);
387 dstPDelta = dstPB;
388 }
389
390 const uint8_t* srcP = src.addr8(0, y);
391
392 // TODO: this fir filter implementation is straight forward, but slow.
393 // It should be possible to make it much faster.
394 for (int sample_x = -4; sample_x < sample_width + 4; sample_x += 4) {
395 int fir[LCD_PER_PIXEL] = { 0 };
396 for (int sample_index = std::max(0, sample_x - 4), coeff_index = sample_index - (sample_x - 4)
397 ; sample_index < std::min(sample_x + 8, sample_width)
398 ; ++sample_index, ++coeff_index)
399 {
400 int sample_value = srcP[sample_index];
401 for (int subpxl_index = 0; subpxl_index < LCD_PER_PIXEL; ++subpxl_index) {
402 fir[subpxl_index] += coefficients[subpxl_index][coeff_index] * sample_value;
403 }
404 }
405 for (int subpxl_index = 0; subpxl_index < LCD_PER_PIXEL; ++subpxl_index) {
406 fir[subpxl_index] /= 0x100;
407 fir[subpxl_index] = std::min(fir[subpxl_index], 255);
408 }
409
410 U8CPU r, g, b;
411 if (doBGR) {
412 r = fir[2];
413 g = fir[1];
414 b = fir[0];
415 } else {
416 r = fir[0];
417 g = fir[1];
418 b = fir[2];
419 }
420 if constexpr (kSkShowTextBlitCoverage) {
421 r = std::max(r, 10u);
422 g = std::max(g, 10u);
423 b = std::max(b, 10u);
424 }
425 if (toA8) {
426 U8CPU a = (r + g + b) / 3;
427 if (maskPreBlend.isApplicable()) {
428 a = maskPreBlend.fG[a];
429 }
430 *dstP = a;
431 } else {
432 if (maskPreBlend.isApplicable()) {
433 r = maskPreBlend.fR[r];
434 g = maskPreBlend.fG[g];
435 b = maskPreBlend.fB[b];
436 }
437 *(uint16_t*)dstP = SkPack888ToRGB16(r, g, b);
438 }
439 dstP = SkTAddOffset<uint8_t>(dstP, dstPDelta);
440 }
441 }
442}
443
444static inline int convert_8_to_1(unsigned byte) {
445 SkASSERT(byte <= 0xFF);
446 return byte >> 7;
447}
448
449static uint8_t pack_8_to_1(const uint8_t alpha[8]) {
450 unsigned bits = 0;
451 for (int i = 0; i < 8; ++i) {
452 bits <<= 1;
453 bits |= convert_8_to_1(alpha[i]);
454 }
455 return SkToU8(bits);
456}
457
458static void packA8ToA1(SkMaskBuilder& dstMask, const uint8_t* src, size_t srcRB) {
459 const int height = dstMask.fBounds.height();
460 const int width = dstMask.fBounds.width();
461 const int octs = width >> 3;
462 const int leftOverBits = width & 7;
463
464 uint8_t* dst = dstMask.image();
465 const int dstPad = dstMask.fRowBytes - SkAlign8(width)/8;
466 SkASSERT(dstPad >= 0);
467
468 SkASSERT(width >= 0);
469 SkASSERT(srcRB >= (size_t)width);
470 const size_t srcPad = srcRB - width;
471
472 for (int y = 0; y < height; ++y) {
473 for (int i = 0; i < octs; ++i) {
474 *dst++ = pack_8_to_1(src);
475 src += 8;
476 }
477 if (leftOverBits > 0) {
478 unsigned bits = 0;
479 int shift = 7;
480 for (int i = 0; i < leftOverBits; ++i, --shift) {
481 bits |= convert_8_to_1(*src++) << shift;
482 }
483 *dst++ = bits;
484 }
485 src += srcPad;
486 dst += dstPad;
487 }
488}
489
491 SkMaskBuilder& dstMask, const SkPath& path, const SkMaskGamma::PreBlend& maskPreBlend,
492 const bool doBGR, const bool verticalLCD, const bool a8FromLCD, const bool hairline)
493{
495 dstMask.fFormat == SkMask::kA8_Format ||
496 dstMask.fFormat == SkMask::kLCD16_Format);
497
500 const SkPath* pathToUse = &path;
501
502 int srcW = dstMask.fBounds.width();
503 int srcH = dstMask.fBounds.height();
504 int dstW = srcW;
505 int dstH = srcH;
506
507 SkMatrix matrix;
508 matrix.setTranslate(-SkIntToScalar(dstMask.fBounds.fLeft),
509 -SkIntToScalar(dstMask.fBounds.fTop));
510
511 paint.setStroke(hairline);
512 paint.setAntiAlias(SkMask::kBW_Format != dstMask.fFormat);
513
514 const bool fromLCD = (dstMask.fFormat == SkMask::kLCD16_Format) ||
515 (dstMask.fFormat == SkMask::kA8_Format && a8FromLCD);
516 const bool intermediateDst = fromLCD || dstMask.fFormat == SkMask::kBW_Format;
517 if (fromLCD) {
518 if (verticalLCD) {
519 dstW = 4*dstH - 8;
520 dstH = srcW;
521 matrix.setAll(0, 4, -SkIntToScalar(dstMask.fBounds.fTop + 1) * 4,
522 1, 0, -SkIntToScalar(dstMask.fBounds.fLeft),
523 0, 0, 1);
524 } else {
525 dstW = 4*dstW - 8;
526 matrix.setAll(4, 0, -SkIntToScalar(dstMask.fBounds.fLeft + 1) * 4,
527 0, 1, -SkIntToScalar(dstMask.fBounds.fTop),
528 0, 0, 1);
529 }
530
531 // LCD hairline doesn't line up with the pixels, so do it the expensive way.
533 if (hairline) {
534 rec.setStrokeStyle(1.0f, false);
536 }
537 if (rec.needToApply() && rec.applyToPath(&strokePath, path)) {
538 pathToUse = &strokePath;
539 paint.setStyle(SkPaint::kFill_Style);
540 }
541 }
542
544 clip.setRect(SkIRect::MakeWH(dstW, dstH));
545
546 const SkImageInfo info = SkImageInfo::MakeA8(dstW, dstH);
548
549 if (intermediateDst) {
550 if (!dst.tryAlloc(info)) {
551 // can't allocate offscreen, so empty the mask and return
552 sk_bzero(dstMask.image(), dstMask.computeImageSize());
553 return;
554 }
555 } else {
556 dst.reset(info, dstMask.image(), dstMask.fRowBytes);
557 }
558 sk_bzero(dst.writable_addr(), dst.computeByteSize());
559
562 draw.fDst = dst;
563 draw.fRC = &clip;
564 draw.fCTM = &matrix;
565 draw.drawPath(*pathToUse, paint);
566
567 switch (dstMask.fFormat) {
569 packA8ToA1(dstMask, dst.addr8(0, 0), dst.rowBytes());
570 break;
572 if (fromLCD) {
573 pack4xHToMask(dst, dstMask, maskPreBlend, doBGR, verticalLCD);
574 } else if (maskPreBlend.isApplicable()) {
575 applyLUTToA8Mask(dstMask, maskPreBlend.fG);
576 }
577 break;
579 pack4xHToMask(dst, dstMask, maskPreBlend, doBGR, verticalLCD);
580 break;
581 default:
582 break;
583 }
584}
585
586void SkScalerContext::getImage(const SkGlyph& origGlyph) {
587 SkASSERT(origGlyph.fAdvancesBoundsFormatAndInitialPathDone);
588
589 const SkGlyph* unfilteredGlyph = &origGlyph;
590 // in case we need to call generateImage on a mask-format that is different
591 // (i.e. larger) than what our caller allocated by looking at origGlyph.
592 SkAutoMalloc tmpGlyphImageStorage;
593 SkGlyph tmpGlyph;
594 SkSTArenaAlloc<sizeof(SkGlyph::PathData)> tmpGlyphPathDataStorage;
595 if (fMaskFilter) {
596 // need the original bounds, sans our maskfilter
597 sk_sp<SkMaskFilter> mf = std::move(fMaskFilter);
598 tmpGlyph = this->makeGlyph(origGlyph.getPackedID(), &tmpGlyphPathDataStorage);
599 fMaskFilter = std::move(mf);
600
601 // Use the origGlyph storage for the temporary unfiltered mask if it will fit.
602 if (tmpGlyph.fMaskFormat == origGlyph.fMaskFormat &&
603 tmpGlyph.imageSize() <= origGlyph.imageSize())
604 {
605 tmpGlyph.fImage = origGlyph.fImage;
606 } else {
607 tmpGlyphImageStorage.reset(tmpGlyph.imageSize());
608 tmpGlyph.fImage = tmpGlyphImageStorage.get();
609 }
610 unfilteredGlyph = &tmpGlyph;
611 }
612
613 if (!fGenerateImageFromPath) {
614 generateImage(*unfilteredGlyph, unfilteredGlyph->fImage);
615 } else {
616 SkASSERT(origGlyph.setPathHasBeenCalled());
617 const SkPath* devPath = origGlyph.path();
618
619 if (!devPath) {
620 generateImage(*unfilteredGlyph, unfilteredGlyph->fImage);
621 } else {
622 SkMaskBuilder mask(static_cast<uint8_t*>(unfilteredGlyph->fImage),
623 unfilteredGlyph->iRect(), unfilteredGlyph->rowBytes(),
624 unfilteredGlyph->maskFormat());
625 SkASSERT(SkMask::kARGB32_Format != origGlyph.fMaskFormat);
630 const bool hairline = origGlyph.pathIsHairline();
631 GenerateImageFromPath(mask, *devPath, fPreBlend, doBGR, doVert, a8LCD, hairline);
632 }
633 }
634
635 if (fMaskFilter) {
636 // k3D_Format should not be mask filtered.
637 SkASSERT(SkMask::k3D_Format != unfilteredGlyph->fMaskFormat);
638
639 SkMaskBuilder srcMask;
640 SkAutoMaskFreeImage srcMaskOwnedImage(nullptr);
641 SkMatrix m;
643
644 if (as_MFB(fMaskFilter)->filterMask(&srcMask, unfilteredGlyph->mask(), m, nullptr)) {
645 // Filter succeeded; srcMask.fImage was allocated.
646 srcMaskOwnedImage.reset(srcMask.image());
647 } else if (unfilteredGlyph->fImage == tmpGlyphImageStorage.get()) {
648 // Filter did nothing; unfiltered mask is independent of origGlyph.fImage.
649 srcMask = SkMaskBuilder(static_cast<uint8_t*>(unfilteredGlyph->fImage),
650 unfilteredGlyph->iRect(), unfilteredGlyph->rowBytes(),
651 unfilteredGlyph->maskFormat());
652 } else if (origGlyph.iRect() == unfilteredGlyph->iRect()) {
653 // Filter did nothing; the unfiltered mask is in origGlyph.fImage and matches.
654 return;
655 } else {
656 // Filter did nothing; the unfiltered mask is in origGlyph.fImage and conflicts.
657 srcMask = SkMaskBuilder(static_cast<uint8_t*>(unfilteredGlyph->fImage),
658 unfilteredGlyph->iRect(), unfilteredGlyph->rowBytes(),
659 unfilteredGlyph->maskFormat());
660 size_t imageSize = unfilteredGlyph->imageSize();
661 tmpGlyphImageStorage.reset(imageSize);
662 srcMask.image() = static_cast<uint8_t*>(tmpGlyphImageStorage.get());
663 memcpy(srcMask.image(), unfilteredGlyph->fImage, imageSize);
664 }
665
666 SkASSERT_RELEASE(srcMask.fFormat == origGlyph.fMaskFormat);
667 SkMaskBuilder dstMask = SkMaskBuilder(static_cast<uint8_t*>(origGlyph.fImage),
668 origGlyph.iRect(), origGlyph.rowBytes(),
669 origGlyph.maskFormat());
670 SkIRect origBounds = dstMask.fBounds;
671
672 // Find the intersection of src and dst while updating the fImages.
673 if (srcMask.fBounds.fTop < dstMask.fBounds.fTop) {
674 int32_t topDiff = dstMask.fBounds.fTop - srcMask.fBounds.fTop;
675 srcMask.image() += srcMask.fRowBytes * topDiff;
676 srcMask.bounds().fTop = dstMask.fBounds.fTop;
677 }
678 if (dstMask.fBounds.fTop < srcMask.fBounds.fTop) {
679 int32_t topDiff = srcMask.fBounds.fTop - dstMask.fBounds.fTop;
680 dstMask.image() += dstMask.fRowBytes * topDiff;
681 dstMask.bounds().fTop = srcMask.fBounds.fTop;
682 }
683
684 if (srcMask.fBounds.fLeft < dstMask.fBounds.fLeft) {
685 int32_t leftDiff = dstMask.fBounds.fLeft - srcMask.fBounds.fLeft;
686 srcMask.image() += leftDiff;
687 srcMask.bounds().fLeft = dstMask.fBounds.fLeft;
688 }
689 if (dstMask.fBounds.fLeft < srcMask.fBounds.fLeft) {
690 int32_t leftDiff = srcMask.fBounds.fLeft - dstMask.fBounds.fLeft;
691 dstMask.image() += leftDiff;
692 dstMask.bounds().fLeft = srcMask.fBounds.fLeft;
693 }
694
695 if (srcMask.fBounds.fBottom < dstMask.fBounds.fBottom) {
696 dstMask.bounds().fBottom = srcMask.fBounds.fBottom;
697 }
698 if (dstMask.fBounds.fBottom < srcMask.fBounds.fBottom) {
699 srcMask.bounds().fBottom = dstMask.fBounds.fBottom;
700 }
701
702 if (srcMask.fBounds.fRight < dstMask.fBounds.fRight) {
703 dstMask.bounds().fRight = srcMask.fBounds.fRight;
704 }
705 if (dstMask.fBounds.fRight < srcMask.fBounds.fRight) {
706 srcMask.bounds().fRight = dstMask.fBounds.fRight;
707 }
708
709 SkASSERT(srcMask.fBounds == dstMask.fBounds);
710 int width = srcMask.fBounds.width();
711 int height = srcMask.fBounds.height();
712 int dstRB = dstMask.fRowBytes;
713 int srcRB = srcMask.fRowBytes;
714
715 const uint8_t* src = srcMask.fImage;
716 uint8_t* dst = dstMask.image();
717
718 if (SkMask::k3D_Format == srcMask.fFormat) {
719 // we have to copy 3 times as much
720 height *= 3;
721 }
722
723 // If not filling the full original glyph, clear it out first.
724 if (dstMask.fBounds != origBounds) {
725 sk_bzero(origGlyph.fImage, origGlyph.fHeight * origGlyph.rowBytes());
726 }
727
728 while (--height >= 0) {
729 memcpy(dst, src, width);
730 src += srcRB;
731 dst += dstRB;
732 }
733 }
734}
735
737 this->internalGetPath(glyph, alloc);
738}
739
743//TODO: make pure virtual
747
752
753///////////////////////////////////////////////////////////////////////////////
754
755void SkScalerContext::internalGetPath(SkGlyph& glyph, SkArenaAlloc* alloc) {
756 SkASSERT(glyph.fAdvancesBoundsFormatAndInitialPathDone);
757
758 if (glyph.setPathHasBeenCalled()) {
759 return;
760 }
761
762 SkPath path;
763 SkPath devPath;
764 bool hairline = false;
765
766 SkPackedGlyphID glyphID = glyph.getPackedID();
767 if (!generatePath(glyph, &path)) {
768 glyph.setPath(alloc, (SkPath*)nullptr, hairline);
769 return;
770 }
771
773 SkFixed dx = glyphID.getSubXFixed();
774 SkFixed dy = glyphID.getSubYFixed();
775 if (dx | dy) {
776 path.offset(SkFixedToScalar(dx), SkFixedToScalar(dy));
777 }
778 }
779
780 if (fRec.fFrameWidth < 0 && fPathEffect == nullptr) {
781 devPath.swap(path);
782 } else {
783 // need the path in user-space, with only the point-size applied
784 // so that our stroking and effects will operate the same way they
785 // would if the user had extracted the path themself, and then
786 // called drawPath
787 SkPath localPath;
789 SkMatrix inverse;
790
791 fRec.getMatrixFrom2x2(&matrix);
792 if (!matrix.invert(&inverse)) {
793 glyph.setPath(alloc, &devPath, hairline);
794 }
795 path.transform(inverse, &localPath);
796 // now localPath is only affected by the paint settings, and not the canvas matrix
797
799
800 if (fRec.fFrameWidth >= 0) {
801 rec.setStrokeStyle(fRec.fFrameWidth,
803 // glyphs are always closed contours, so cap type is ignored,
804 // so we just pass something.
805 rec.setStrokeParams((SkPaint::Cap)fRec.fStrokeCap,
806 (SkPaint::Join)fRec.fStrokeJoin,
808 }
809
810 if (fPathEffect) {
811 SkPath effectPath;
812 if (fPathEffect->filterPath(&effectPath, localPath, &rec, nullptr, matrix)) {
813 localPath.swap(effectPath);
814 }
815 }
816
817 if (rec.needToApply()) {
819 if (rec.applyToPath(&strokePath, localPath)) {
820 localPath.swap(strokePath);
821 }
822 }
823
824 // The path effect may have modified 'rec', so wait to here to check hairline status.
825 if (rec.isHairlineStyle()) {
826 hairline = true;
827 }
828
829 localPath.transform(matrix, &devPath);
830 }
831 glyph.setPath(alloc, &devPath, hairline);
832}
833
834
836 dst->setAll(fPost2x2[0][0], fPost2x2[0][1], 0,
837 fPost2x2[1][0], fPost2x2[1][1], 0,
838 0, 0, 1);
839}
840
844
846 this->getLocalMatrix(m);
847
848 // now concat the device matrix
849 SkMatrix deviceMatrix;
850 this->getMatrixFrom2x2(&deviceMatrix);
851 m->postConcat(deviceMatrix);
852}
853
855 SkMatrix* GsA, SkMatrix* G_inv, SkMatrix* A_out)
856{
857 // A is the 'total' matrix.
858 SkMatrix A;
859 this->getSingleMatrix(&A);
860
861 // The caller may find the 'total' matrix useful when dealing directly with EM sizes.
862 if (A_out) {
863 *A_out = A;
864 }
865
866 // GA is the matrix A with rotation removed.
867 SkMatrix GA;
868 bool skewedOrFlipped = A.getSkewX() || A.getSkewY() || A.getScaleX() < 0 || A.getScaleY() < 0;
869 if (skewedOrFlipped) {
870 // QR by Givens rotations. G is Q^T and GA is R. G is rotational (no reflections).
871 // h is where A maps the horizontal baseline.
873 A.mapPoints(&h, 1);
874
875 // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
876 SkMatrix G;
878
879 GA = G;
880 GA.preConcat(A);
881
882 // The 'remainingRotation' is G inverse, which is fairly simple since G is 2x2 rotational.
883 if (G_inv) {
884 G_inv->setAll(
888 }
889 } else {
890 GA = A;
891 if (G_inv) {
892 G_inv->reset();
893 }
894 }
895
896 // If the 'total' matrix is singular, set the 'scale' to something finite and zero the matrices.
897 // All underlying ports have issues with zero text size, so use the matricies to zero.
898 // If one of the scale factors is less than 1/256 then an EM filling square will
899 // never affect any pixels.
900 // If there are any nonfinite numbers in the matrix, bail out and set the matrices to zero.
903 !GA.isFinite())
904 {
905 s->fX = SK_Scalar1;
906 s->fY = SK_Scalar1;
907 sA->setScale(0, 0);
908 if (GsA) {
909 GsA->setScale(0, 0);
910 }
911 if (G_inv) {
912 G_inv->reset();
913 }
914 return false;
915 }
916
917 // At this point, given GA, create s.
918 switch (preMatrixScale) {
922 break;
925 s->fX = yScale;
926 s->fY = yScale;
927 break;
928 }
930 SkScalar realYScale = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
931 SkScalar intYScale = SkScalarRoundToScalar(realYScale);
932 if (intYScale == 0) {
933 intYScale = SK_Scalar1;
934 }
935 s->fX = intYScale;
936 s->fY = intYScale;
937 break;
938 }
939 }
940
941 // The 'remaining' matrix sA is the total matrix A without the scale.
942 if (!skewedOrFlipped && (
943 (PreMatrixScale::kFull == preMatrixScale) ||
944 (PreMatrixScale::kVertical == preMatrixScale && A.getScaleX() == A.getScaleY())))
945 {
946 // If GA == A and kFull, sA is identity.
947 // If GA == A and kVertical and A.scaleX == A.scaleY, sA is identity.
948 sA->reset();
949 } else if (!skewedOrFlipped && PreMatrixScale::kVertical == preMatrixScale) {
950 // If GA == A and kVertical, sA.scaleY is SK_Scalar1.
951 sA->reset();
952 sA->setScaleX(A.getScaleX() / s->fY);
953 } else {
954 // TODO: like kVertical, kVerticalInteger with int scales.
955 *sA = A;
956 sA->preScale(SkScalarInvert(s->fX), SkScalarInvert(s->fY));
957 }
958
959 // The 'remainingWithoutRotation' matrix GsA is the non-rotational part of A without the scale.
960 if (GsA) {
961 *GsA = GA;
962 // G is rotational so reorders with the scale.
963 GsA->preScale(SkScalarInvert(s->fX), SkScalarInvert(s->fY));
964 }
965
966 return true;
967}
968
972
974 // Why fPost2x2 can be used here.
975 // getSingleMatrix multiplies in getLocalMatrix, which consists of
976 // * fTextSize (a scale, which has no effect)
977 // * fPreScaleX (a scale in x, which has no effect)
978 // * fPreSkewX (has no effect, but would on vertical text alignment).
979 // In other words, making the text bigger, stretching it along the
980 // horizontal axis, or fake italicizing it does not move the baseline.
983 }
984
985 if (0 == fPost2x2[1][0]) {
986 // The x axis is mapped onto the x axis.
987 return SkAxisAlignment::kX;
988 }
989 if (0 == fPost2x2[0][0]) {
990 // The x axis is mapped onto the y axis.
991 return SkAxisAlignment::kY;
992 }
994}
995
1000
1001/*
1002 * Return the scalar with only limited fractional precision. Used to consolidate matrices
1003 * that vary only slightly when we create our key into the font cache, since the font scaler
1004 * typically returns the same looking resuts for tiny changes in the matrix.
1005 */
1007 SkScalar n = SkScalarRoundToScalar(x * 1024);
1008 return n / 1024.0f;
1009}
1010
1012 switch (font.getEdging()) {
1014 return SkMask::kBW_Format;
1016 return SkMask::kA8_Format;
1018 return SkMask::kLCD16_Format;
1019 }
1020 SkASSERT(false);
1021 return SkMask::kA8_Format;
1022}
1023
1024// Beyond this size, LCD doesn't appreciably improve quality, but it always
1025// cost more RAM and draws slower, so we set a cap.
1026#ifndef SK_MAX_SIZE_FOR_LCDTEXT
1027 #define SK_MAX_SIZE_FOR_LCDTEXT 48
1028#endif
1029
1031
1032static bool too_big_for_lcd(const SkScalerContextRec& rec, bool checkPost2x2) {
1033 if (checkPost2x2) {
1034 SkScalar area = rec.fPost2x2[0][0] * rec.fPost2x2[1][1] -
1035 rec.fPost2x2[1][0] * rec.fPost2x2[0][1];
1036 area *= rec.fTextSize * rec.fTextSize;
1037 return area > gMaxSize2ForLCDText;
1038 } else {
1040 }
1041}
1042
1043// The only reason this is not file static is because it needs the context of SkScalerContext to
1044// access SkPaint::computeLuminanceColor.
1046 const SkSurfaceProps& surfaceProps,
1047 SkScalerContextFlags scalerContextFlags,
1048 const SkMatrix& deviceMatrix,
1049 SkScalerContextRec* rec,
1050 SkScalerContextEffects* effects) {
1051 SkASSERT(!deviceMatrix.hasPerspective());
1052
1053 sk_bzero(rec, sizeof(SkScalerContextRec));
1054
1055 SkTypeface* typeface = font.getTypeface();
1056
1057 rec->fTypefaceID = typeface->uniqueID();
1058 rec->fTextSize = font.getSize();
1059 rec->fPreScaleX = font.getScaleX();
1060 rec->fPreSkewX = font.getSkewX();
1061
1062 bool checkPost2x2 = false;
1063
1064 const SkMatrix::TypeMask mask = deviceMatrix.getType();
1065 if (mask & SkMatrix::kScale_Mask) {
1066 rec->fPost2x2[0][0] = sk_relax(deviceMatrix.getScaleX());
1067 rec->fPost2x2[1][1] = sk_relax(deviceMatrix.getScaleY());
1068 checkPost2x2 = true;
1069 } else {
1070 rec->fPost2x2[0][0] = rec->fPost2x2[1][1] = SK_Scalar1;
1071 }
1072 if (mask & SkMatrix::kAffine_Mask) {
1073 rec->fPost2x2[0][1] = sk_relax(deviceMatrix.getSkewX());
1074 rec->fPost2x2[1][0] = sk_relax(deviceMatrix.getSkewY());
1075 checkPost2x2 = true;
1076 } else {
1077 rec->fPost2x2[0][1] = rec->fPost2x2[1][0] = 0;
1078 }
1079
1080 SkPaint::Style style = paint.getStyle();
1081 SkScalar strokeWidth = paint.getStrokeWidth();
1082
1083 unsigned flags = 0;
1084
1085 if (font.isEmbolden()) {
1086#ifdef SK_USE_FREETYPE_EMBOLDEN
1088#else
1089 SkScalar fakeBoldScale = SkScalarInterpFunc(font.getSize(),
1093 SkScalar extra = font.getSize() * fakeBoldScale;
1094
1095 if (style == SkPaint::kFill_Style) {
1097 strokeWidth = extra; // ignore paint's strokeWidth if it was "fill"
1098 } else {
1099 strokeWidth += extra;
1100 }
1101#endif
1102 }
1103
1104 if (style != SkPaint::kFill_Style && strokeWidth >= 0) {
1105 rec->fFrameWidth = strokeWidth;
1106 rec->fMiterLimit = paint.getStrokeMiter();
1107 rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
1108 rec->fStrokeCap = SkToU8(paint.getStrokeCap());
1109
1110 if (style == SkPaint::kStrokeAndFill_Style) {
1112 }
1113 } else {
1114 rec->fFrameWidth = -1;
1115 rec->fMiterLimit = 0;
1116 rec->fStrokeJoin = 0;
1117 rec->fStrokeCap = 0;
1118 }
1119
1120 rec->fMaskFormat = compute_mask_format(font);
1121
1122 if (SkMask::kLCD16_Format == rec->fMaskFormat) {
1123 if (too_big_for_lcd(*rec, checkPost2x2)) {
1126 } else {
1127 SkPixelGeometry geometry = surfaceProps.pixelGeometry();
1128
1129 switch (geometry) {
1131 // eeek, can't support LCD
1134 break;
1136 // our default, do nothing.
1137 break;
1140 break;
1143 break;
1147 break;
1148 }
1149 }
1150 }
1151
1152 if (font.isEmbeddedBitmaps()) {
1154 }
1155 if (font.isSubpixel()) {
1157 }
1158 if (font.isForceAutoHinting()) {
1160 }
1161 if (font.isLinearMetrics()) {
1163 }
1164 if (font.isBaselineSnap()) {
1166 }
1167 if (typeface->glyphMaskNeedsCurrentColor()) {
1169 rec->fForegroundColor = paint.getColor();
1170 }
1171 rec->fFlags = SkToU16(flags);
1172
1173 // these modify fFlags, so do them after assigning fFlags
1174 rec->setHinting(font.getHinting());
1176
1177 // For now always set the paint gamma equal to the device gamma.
1178 // The math in SkMaskGamma can handle them being different,
1179 // but it requires superluminous masks when
1180 // Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
1181 rec->setDeviceGamma(surfaceProps.textGamma());
1182 rec->setPaintGamma(surfaceProps.textGamma());
1183 rec->setContrast(surfaceProps.textContrast());
1184
1185 if (!SkToBool(scalerContextFlags & SkScalerContextFlags::kFakeGamma)) {
1186 rec->ignoreGamma();
1187 }
1188 if (!SkToBool(scalerContextFlags & SkScalerContextFlags::kBoostContrast)) {
1189 rec->setContrast(0);
1190 }
1191
1192 new (effects) SkScalerContextEffects{paint};
1193}
1194
1196 const SkFont& font, const SkPaint& paint, const SkSurfaceProps& surfaceProps,
1197 SkScalerContextFlags scalerContextFlags, const SkMatrix& deviceMatrix, SkAutoDescriptor* ad,
1198 SkScalerContextEffects* effects)
1199{
1201 MakeRecAndEffects(font, paint, surfaceProps, scalerContextFlags, deviceMatrix, &rec, effects);
1202 return AutoDescriptorGivenRecAndEffects(rec, *effects, ad);
1203}
1204
1206 const SkScalerContextEffects& effects,
1207 SkBinaryWriteBuffer* effectBuffer) {
1208 size_t descSize = sizeof(rec);
1209 int entryCount = 1;
1210
1211 if (effects.fPathEffect || effects.fMaskFilter) {
1212 if (effects.fPathEffect) { effectBuffer->writeFlattenable(effects.fPathEffect); }
1213 if (effects.fMaskFilter) { effectBuffer->writeFlattenable(effects.fMaskFilter); }
1214 entryCount += 1;
1215 descSize += effectBuffer->bytesWritten();
1216 }
1217
1218 descSize += SkDescriptor::ComputeOverhead(entryCount);
1219 return descSize;
1220}
1221
1223 const SkBinaryWriteBuffer& effectBuffer,
1224 SkDescriptor* desc) {
1225 desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec);
1226
1227 if (effectBuffer.bytesWritten() > 0) {
1228 effectBuffer.writeToMemory(desc->addEntry(kEffects_SkDescriptorTag,
1229 effectBuffer.bytesWritten(),
1230 nullptr));
1231 }
1232
1233 desc->computeChecksum();
1234}
1235
1237 const SkScalerContextRec& rec,
1238 const SkScalerContextEffects& effects,
1239 SkAutoDescriptor* ad)
1240{
1241 SkBinaryWriteBuffer buf({});
1242
1243 ad->reset(calculate_size_and_flatten(rec, effects, &buf));
1244 generate_descriptor(rec, buf, ad->getDesc());
1245
1246 return ad->getDesc();
1247}
1248
1250 const SkScalerContextRec& rec,
1251 const SkScalerContextEffects& effects)
1252{
1253 SkBinaryWriteBuffer buf({});
1254
1255 auto desc = SkDescriptor::Alloc(calculate_size_and_flatten(rec, effects, &buf));
1256 generate_descriptor(rec, buf, desc.get());
1257
1258 return desc;
1259}
1260
1264
1266 const SkScalerContextEffects& effects,
1267 size_t size) {
1268 SkBinaryWriteBuffer buf({});
1269 return size >= calculate_size_and_flatten(rec, effects, &buf);
1270}
1271
1272std::unique_ptr<SkScalerContext> SkScalerContext::MakeEmpty(
1273 sk_sp<SkTypeface> typeface, const SkScalerContextEffects& effects,
1274 const SkDescriptor* desc) {
1275 class SkScalerContext_Empty : public SkScalerContext {
1276 public:
1277 SkScalerContext_Empty(sk_sp<SkTypeface> typeface, const SkScalerContextEffects& effects,
1278 const SkDescriptor* desc)
1279 : SkScalerContext(std::move(typeface), effects, desc) {}
1280
1281 protected:
1282 GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override {
1283 return {glyph.maskFormat()};
1284 }
1285 void generateImage(const SkGlyph&, void*) override {}
1286 bool generatePath(const SkGlyph& glyph, SkPath* path) override {
1287 path->reset();
1288 return false;
1289 }
1290 void generateFontMetrics(SkFontMetrics* metrics) override {
1291 if (metrics) {
1292 sk_bzero(metrics, sizeof(*metrics));
1293 }
1294 }
1295 };
1296
1297 return std::make_unique<SkScalerContext_Empty>(std::move(typeface), effects, desc);
1298}
1299
1300
1301
1302
static const int strokeWidth
Definition BlurTest.cpp:60
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
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)
static U16CPU SkPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b)
@ 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 SkDEBUGCODE(...)
Definition SkDebug.h:23
#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
static void sk_bzero(void *buffer, size_t size)
Definition SkMalloc.h:105
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)
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
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_ScalarMin
Definition SkScalar.h:25
#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:10
#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 void applyLUTToA8Mask(SkMaskBuilder &mask, const uint8_t *lut)
static const SkMaskGamma & cached_mask_gamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma)
static SkScalar gPaintGamma
static SkScalar gDeviceGamma
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)
const SkScalar gMaxSize2ForLCDText
static SkMutex & mask_gamma_cache_mutex()
static SkScalar gContrast
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[]
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
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition aaclip.cpp:27
SkDescriptor * getDesc() const
void reset(size_t size)
void * reset(size_t size=0, OnShrink shrink=kAlloc_OnShrink)
void * get()
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)
BlitterChooser * fBlitterChooser
Definition SkDrawBase.h:152
static SkMatrix MakeTextMatrix(SkScalar size, SkScalar scaleX, SkScalar skewX)
Definition SkFontPriv.h:41
@ 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
const SkRect & getBounds() const
Definition SkPath.cpp:420
void swap(SkPath &other)
Definition SkPath.cpp:217
void transform(const SkMatrix &matrix, SkPath *dst, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition SkPath.cpp:1647
virtual void generateFontMetrics(SkFontMetrics *)=0
SkScalerContext(sk_sp< SkTypeface >, const SkScalerContextEffects &, const SkDescriptor *)
SkScalerContextRec fRec
static size_t GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma, int *width, int *height)
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)
SkAxisAlignment computeAxisAlignmentForHText() const
sk_sp< SkDrawable > getDrawable(SkGlyph &)
static bool GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma, uint8_t *data)
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)
void getFontMetrics(SkFontMetrics *)
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)
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
PreBlend preBlend(SkColor color) const
static SkColor CanonicalColor(SkColor color)
const uint8_t * getGammaTables() const
const uint8_t * fG
const uint8_t * fB
bool isApplicable() const
const uint8_t * fR
SkTypefaceID uniqueID() const
Definition SkTypeface.h:101
virtual void onFilterRec(SkScalerContextRec *) const =0
const Paint & paint
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct s
struct MyStruct a[10]
FlutterSemanticsFlag flags
static const uint8_t buffer[]
uint32_t uint32_t * format
static void strokePath(SkCanvas *canvas, const SkPath &path)
static float lum(float r, float g, float b)
Definition hsl.cpp:52
double y
double x
unsigned useCenter Optional< SkMatrix > matrix
Definition SkRecords.h:258
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
dst
Definition cp.py:12
Definition ref_ptr.h:256
SkScalar h
int32_t height
int32_t width
Definition SkMD5.cpp:125
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
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)
void setPaintGamma(SkScalar pg)
void getSingleMatrix(SkMatrix *) const
void getLocalMatrix(SkMatrix *) const
SkAxisAlignment computeAxisAlignmentForHText() const
void setDeviceGamma(SkScalar dg)
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)
SkScalar getPaintGamma() const
SkTypefaceID fTypefaceID
SkScalar getContrast() const
SkScalar getDeviceGamma() const
SkScalar fPost2x2[2][2]
SkColor getLuminanceColor() const
SkString dump() const
void setHinting(SkFontHinting)
void setLuminanceColor(SkColor c)