Flutter Engine
The Flutter Engine
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
Macros | Functions | Variables
SkScalerContext.cpp File Reference
#include "src/core/SkScalerContext.h"
#include "include/core/SkColorType.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontMetrics.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkMaskFilter.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkPathEffect.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkStrokeRec.h"
#include "include/private/SkColorData.h"
#include "include/private/base/SkAlign.h"
#include "include/private/base/SkCPUTypes.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkFixed.h"
#include "include/private/base/SkMalloc.h"
#include "include/private/base/SkMutex.h"
#include "include/private/base/SkTo.h"
#include "src/base/SkArenaAlloc.h"
#include "src/base/SkAutoMalloc.h"
#include "src/core/SkAutoPixmapStorage.h"
#include "src/core/SkBlitter_A8.h"
#include "src/core/SkDescriptor.h"
#include "src/core/SkDrawBase.h"
#include "src/core/SkFontPriv.h"
#include "src/core/SkGlyph.h"
#include "src/core/SkMaskFilterBase.h"
#include "src/core/SkPaintPriv.h"
#include "src/core/SkRasterClip.h"
#include "src/core/SkTextFormatParams.h"
#include "src/core/SkWriteBuffer.h"
#include "src/utils/SkMatrix22.h"
#include <algorithm>
#include <cstring>
#include <limits>
#include <new>

Go to the source code of this file.

Macros

#define SAMPLES_PER_PIXEL   4
 
#define LCD_PER_PIXEL   3
 
#define SK_MAX_SIZE_FOR_LCDTEXT   48
 

Functions

static SkMutex & mask_gamma_cache_mutex ()
 
template<typename D , typename S >
static constexpr D sk_saturate_cast (S s)
 
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 int convert_8_to_1 (unsigned byte)
 
static uint8_t pack_8_to_1 (const uint8_t alpha[8])
 
static void packA8ToA1 (SkMaskBuilder &dstMask, const uint8_t *src, size_t srcRB)
 
static SkScalar sk_relax (SkScalar x)
 
static SkMask::Format compute_mask_format (const SkFont &font)
 
static bool too_big_for_lcd (const SkScalerContextRec &rec, bool checkPost2x2)
 
static size_t calculate_size_and_flatten (const SkScalerContextRec &rec, const SkScalerContextEffects &effects, SkBinaryWriteBuffer *effectBuffer)
 
static void generate_descriptor (const SkScalerContextRec &rec, const SkBinaryWriteBuffer &effectBuffer, SkDescriptor *desc)
 

Variables

static SkMaskGammagLinearMaskGamma = nullptr
 
static SkMaskGammagDefaultMaskGamma = nullptr
 
static SkMaskGammagMaskGamma = nullptr
 
static uint8_t gContrast = 0
 
static uint8_t gGamma = 0
 
const SkScalar gMaxSize2ForLCDText = SK_MAX_SIZE_FOR_LCDTEXT * SK_MAX_SIZE_FOR_LCDTEXT
 

Macro Definition Documentation

◆ LCD_PER_PIXEL

#define LCD_PER_PIXEL   3

◆ SAMPLES_PER_PIXEL

#define SAMPLES_PER_PIXEL   4

◆ SK_MAX_SIZE_FOR_LCDTEXT

#define SK_MAX_SIZE_FOR_LCDTEXT   48

Definition at line 1035 of file SkScalerContext.cpp.

Function Documentation

◆ applyLUTToA8Mask()

static void applyLUTToA8Mask ( SkMaskBuilder mask,
const uint8_t *  lut 
)
static

Definition at line 320 of file SkScalerContext.cpp.

320 {
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}
#define SK_RESTRICT
Definition: SkFeatures.h:42
double y
double x
dst
Definition: cp.py:12
constexpr int32_t height() const
Definition: SkRect.h:165
constexpr int32_t width() const
Definition: SkRect.h:158
uint8_t *& image()
Definition: SkMask.h:236
const uint32_t fRowBytes
Definition: SkMask.h:43
const SkIRect fBounds
Definition: SkMask.h:42

◆ calculate_size_and_flatten()

static size_t calculate_size_and_flatten ( const SkScalerContextRec rec,
const SkScalerContextEffects effects,
SkBinaryWriteBuffer effectBuffer 
)
static

Definition at line 1213 of file SkScalerContext.cpp.

1215 {
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}
void writeFlattenable(const SkFlattenable *flattenable) override
size_t bytesWritten() const
static size_t ComputeOverhead(int entryCount)
Definition: SkDescriptor.h:27
SkPathEffect * fPathEffect
SkMaskFilter * fMaskFilter

◆ compute_mask_format()

static SkMask::Format compute_mask_format ( const SkFont font)
static

Definition at line 1019 of file SkScalerContext.cpp.

1019 {
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}
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kAntiAlias
may have transparent pixels on glyph edges
@ kAlias
no transparent pixels on glyph edges
@ kSubpixelAntiAlias
glyph positioned in pixel using transparency
font
Font Metadata and Metrics.
@ 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
@ kBW_Format
1bit per pixel mask (e.g. monochrome)
Definition: SkMask.h:27

◆ convert_8_to_1()

static int convert_8_to_1 ( unsigned  byte)
inlinestatic

Definition at line 452 of file SkScalerContext.cpp.

452 {
453 SkASSERT(byte <= 0xFF);
454 return byte >> 7;
455}

◆ generate_descriptor()

static void generate_descriptor ( const SkScalerContextRec rec,
const SkBinaryWriteBuffer effectBuffer,
SkDescriptor desc 
)
static

Definition at line 1230 of file SkScalerContext.cpp.

1232 {
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}
#define kRec_SkDescriptorTag
#define kEffects_SkDescriptorTag
void writeToMemory(void *dst) const

◆ mask_gamma_cache_mutex()

static SkMutex & mask_gamma_cache_mutex ( )
static

In order to call cachedDeviceLuminance, cachedPaintLuminance, or cachedMaskGamma the caller must hold the mask_gamma_cache_mutex and continue to hold it until the returned pointer is refed or forgotten.

Definition at line 114 of file SkScalerContext.cpp.

114 {
115 static SkMutex& mutex = *(new SkMutex);
116 return mutex;
117}

◆ pack4xHToMask()

static void pack4xHToMask ( const SkPixmap src,
SkMaskBuilder dst,
const SkMaskGamma::PreBlend maskPreBlend,
const bool  doBGR,
const bool  doVert 
)
static

Definition at line 332 of file SkScalerContext.cpp.

334 {
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}
unsigned U8CPU
Definition: SkCPUTypes.h:18
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 LCD_PER_PIXEL
#define SAMPLES_PER_PIXEL
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
static bool b
struct MyStruct a[10]
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
int32_t height

◆ pack_8_to_1()

static uint8_t pack_8_to_1 ( const uint8_t  alpha[8])
static

Definition at line 457 of file SkScalerContext.cpp.

457 {
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}
static int convert_8_to_1(unsigned byte)
constexpr uint8_t SkToU8(S x)
Definition: SkTo.h:22

◆ packA8ToA1()

static void packA8ToA1 ( SkMaskBuilder dstMask,
const uint8_t *  src,
size_t  srcRB 
)
static

Definition at line 466 of file SkScalerContext.cpp.

466 {
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}
static constexpr T SkAlign8(T x)
Definition: SkAlign.h:17
static uint8_t pack_8_to_1(const uint8_t alpha[8])
int32_t width

◆ sk_relax()

static SkScalar sk_relax ( SkScalar  x)
static

Definition at line 1014 of file SkScalerContext.cpp.

1014 {
1015 SkScalar n = SkScalarRoundToScalar(x * 1024);
1016 return n / 1024.0f;
1017}
#define SkScalarRoundToScalar(x)
Definition: SkScalar.h:32
float SkScalar
Definition: extension.cpp:12

◆ sk_saturate_cast()

template<typename D , typename S >
static constexpr D sk_saturate_cast ( s)
staticconstexpr

Return the closest D for the given S. Returns std::numeric_limits<D>::max() for NaN.

Definition at line 206 of file SkScalerContext.cpp.

206 {
207 static_assert(std::is_integral_v<D>);
210 return (D)s;
211}
struct MyStruct s

◆ too_big_for_lcd()

static bool too_big_for_lcd ( const SkScalerContextRec rec,
bool  checkPost2x2 
)
static

Definition at line 1040 of file SkScalerContext.cpp.

1040 {
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}
#define SK_MAX_SIZE_FOR_LCDTEXT
const SkScalar gMaxSize2ForLCDText
SkScalar fPost2x2[2][2]

Variable Documentation

◆ gContrast

uint8_t gContrast = 0
static

Definition at line 122 of file SkScalerContext.cpp.

◆ gDefaultMaskGamma

SkMaskGamma* gDefaultMaskGamma = nullptr
static

Definition at line 120 of file SkScalerContext.cpp.

◆ gGamma

uint8_t gGamma = 0
static

Definition at line 123 of file SkScalerContext.cpp.

◆ gLinearMaskGamma

SkMaskGamma* gLinearMaskGamma = nullptr
static

Definition at line 119 of file SkScalerContext.cpp.

◆ gMaskGamma

SkMaskGamma* gMaskGamma = nullptr
static

Definition at line 121 of file SkScalerContext.cpp.

◆ gMaxSize2ForLCDText

Definition at line 1038 of file SkScalerContext.cpp.