9#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
11#ifdef SK_BUILD_FOR_MAC
12#import <ApplicationServices/ApplicationServices.h>
15#ifdef SK_BUILD_FOR_IOS
16#include <CoreText/CoreText.h>
17#include <CoreText/CTFontManager.h>
18#include <CoreGraphics/CoreGraphics.h>
19#include <CoreFoundation/CoreFoundation.h>
59static inline const constexpr bool kSkShowTextBlitCoverage =
false;
62static void sk_memset_rect32(uint32_t* ptr, uint32_t
value,
70 ptr = (uint32_t*)((
char*)ptr + rowBytes);
76 rowBytes -=
width *
sizeof(uint32_t);
91 ptr = (uint32_t*)((
char*)ptr + rowBytes);
100 ptr = (uint32_t*)((
char*)ptr + rowBytes);
106static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
110static CGAffineTransform MatrixToCGAffineTransform(
const SkMatrix&
matrix) {
123 , fOffscreen(fRec.fForegroundColor)
124 , fDoSubPosition(
SkToBool(fRec.
fFlags & kSubpixelPositioning_Flag))
127 CTFontRef ctFont = (CTFontRef)this->getTypeface()->internal_private_getCTFontRef();
134 &
scale, &skTransform,
nullptr,
nullptr,
nullptr);
135 fTransform = MatrixToCGAffineTransform(skTransform);
139 fInvTransform = CGAffineTransformInvert(fTransform);
141 fInvTransform = fTransform;
146 CGFloat textSize = SkScalarToCGFloat(
scale.y());
147 fCTFont = SkCTFontCreateExactCopy(ctFont, textSize,
148 ((SkTypeface_Mac*)this->getTypeface())->fOpszVariation);
149 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(),
nullptr));
152static int RoundSize(
int dimension) {
156static CGColorRef CGColorForSkColor(CGColorSpaceRef rgbcs,
SkColor bgra) {
157 CGFloat components[4];
164 components[3] = 1.0f;
165 return CGColorCreate(rgbcs, components);
168SkScalerContext_Mac::Offscreen::Offscreen(
SkColor foregroundColor)
170 , fSKForegroundColor(foregroundColor)
177CGRGBPixel* SkScalerContext_Mac::Offscreen::getCG(
const SkScalerContext_Mac& context,
178 const SkGlyph& glyph, CGGlyph glyphID,
180 bool generateA8FromLCD) {
185 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
186 fCGForegroundColor.reset(CGColorForSkColor(fRGBSpace.get(), fSKForegroundColor));
211 size_t rowBytes = fSize.fWidth *
sizeof(CGRGBPixel);
212 if (!fCG || fSize.fWidth < glyph.
width() || fSize.fHeight < glyph.
height()) {
213 if (fSize.fWidth < glyph.
width()) {
214 fSize.fWidth = RoundSize(glyph.
width());
216 if (fSize.fHeight < glyph.
height()) {
217 fSize.fHeight = RoundSize(glyph.
height());
220 rowBytes = fSize.fWidth *
sizeof(CGRGBPixel);
221 void*
image = fImageStorage.
reset(rowBytes * fSize.fHeight);
222 const CGImageAlphaInfo alpha = (glyph.
isColor())
223 ? kCGImageAlphaPremultipliedFirst
224 : kCGImageAlphaNoneSkipFirst;
225 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | (CGBitmapInfo)alpha;
226 fCG.reset(CGBitmapContextCreate(
image, fSize.fWidth, fSize.fHeight, 8,
227 rowBytes, fRGBSpace.get(), bitmapInfo));
231 CGContextSetAllowsFontSubpixelQuantization(fCG.get(),
false);
232 CGContextSetShouldSubpixelQuantizeFonts(fCG.get(),
false);
237 CGContextSetAllowsFontSubpixelPositioning(fCG.get(),
true);
238 CGContextSetShouldSubpixelPositionFonts(fCG.get(),
true);
240 CGContextSetTextDrawingMode(fCG.get(), kCGTextFill);
244 CGContextSetGrayFillColor(fCG.get(), 0.0f, 1.0f);
246 CGContextSetFillColorWithColor(fCG.get(), fCGForegroundColor.get());
253 CGContextSetTextMatrix(fCG.get(), context.fTransform);
257 CGContextSetShouldAntialias(fCG.get(), doAA);
260 if (fDoLCD != doLCD) {
261 CGContextSetShouldSmoothFonts(fCG.get(), doLCD);
265 CGRGBPixel*
image = (CGRGBPixel*)fImageStorage.
get();
267 image += (fSize.fHeight - glyph.
height()) * fSize.fWidth;
275 if (context.fDoSubPosition) {
280 CGPoint point = CGPointMake(-glyph.
left() + subX, glyph.
top() + glyph.
height() - subY);
288 point = CGPointApplyAffineTransform(point, context.fInvTransform);
290 CTFontDrawGlyphs(context.fCTFont.get(), &glyphID, &point, 1, fCG.get());
293 *rowBytesPtr = rowBytes;
301 mx.neverRequestPath = ((SkTypeface_Mac*)this->getTypeface())->fHasColorGlyphs;
303 const CGGlyph cgGlyph = (CGGlyph)glyph.
getGlyphID();
307 CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
308 &cgGlyph, &cgAdvance, 1);
309 cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
310 mx.advance.fX = SkFloatFromCGFloat(cgAdvance.width);
311 mx.advance.fY = -SkFloatFromCGFloat(cgAdvance.height);
325 CTFontGetBoundingRectsForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
326 &cgGlyph, &cgBounds, 1);
327 cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
334 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
335 SkUniqueCFRef<CGPathRef>
path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph,
nullptr));
336 if (!
path || CGPathIsEmpty(
path.get())) {
341 if (SkCGRectIsEmpty(cgBounds)) {
346 skBounds =
SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
347 cgBounds.size.width, cgBounds.size.height);
352 if (fDoSubPosition) {
362 mx.bounds.outset(1, 1);
366static constexpr uint8_t sk_pow2_table(
size_t i) {
367 return SkToU8(((
i *
i + 128) / 255));
377static constexpr auto gLinearCoverageFromCGLCDValue = SkMakeArray<256>(sk_pow2_table);
379static void cgpixels_to_bits(uint8_t
dst[],
const CGRGBPixel
src[],
int count) {
382 for (
int i = 7;
i >= 0; --
i) {
383 mask |= ((CGRGBPixel_getAlpha(*
src++) >> 7) ^ 0x1) <<
i;
392template<
bool APPLY_PREBLEND>
393static inline uint8_t rgb_to_a8(CGRGBPixel rgb,
const uint8_t* table8) {
394 U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
395 U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
396 U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
398 if constexpr (kSkShowTextBlitCoverage) {
404template<
bool APPLY_PREBLEND>
405static void RGBToA8(
const CGRGBPixel*
SK_RESTRICT cgPixels,
size_t cgRowBytes,
406 const SkGlyph& glyph,
void* glyphImage,
const uint8_t* table8) {
414 dst[
i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[
i], table8);
416 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
417 dst = SkTAddOffset<uint8_t>(
dst, dstRB);
421template<
bool APPLY_PREBLEND>
422static uint16_t RGBToLcd16(CGRGBPixel rgb,
423 const uint8_t* tableR,
const uint8_t* tableG,
const uint8_t* tableB) {
424 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
425 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
426 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
427 if constexpr (kSkShowTextBlitCoverage) {
435template<
bool APPLY_PREBLEND>
436static void RGBToLcd16(
const CGRGBPixel*
SK_RESTRICT cgPixels,
size_t cgRowBytes,
437 const SkGlyph& glyph,
void* glyphImage,
438 const uint8_t* tableR,
const uint8_t* tableG,
const uint8_t* tableB) {
446 dst[
i] = RGBToLcd16<APPLY_PREBLEND>(cgPixels[
i], tableR, tableG, tableB);
448 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
449 dst = SkTAddOffset<uint16_t>(
dst, dstRB);
453static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
454 U8CPU a = (rgb >> 24) & 0xFF;
455 U8CPU r = (rgb >> 16) & 0xFF;
456 U8CPU g = (rgb >> 8) & 0xFF;
457 U8CPU b = (rgb >> 0) & 0xFF;
458 if constexpr (kSkShowTextBlitCoverage) {
464void SkScalerContext_Mac::generateImage(
const SkGlyph& glyph,
void* imageBuffer) {
465 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.
getGlyphID());
472 CGRGBPixel* cgPixels = fOffscreen.getCG(*
this, glyph, cgGlyph, &cgRowBytes, requestSmooth);
473 if (cgPixels ==
nullptr) {
481 && SkCTFontGetSmoothBehavior() != SkCTFontSmoothBehavior::none))
483 const uint8_t*
linear = gLinearCoverageFromCGLCDValue.data();
490 CGRGBPixel*
addr = cgPixels;
491 for (
int y = 0;
y < glyph.
height(); ++
y) {
492 for (
int x = 0;
x < glyph.
width(); ++
x) {
493 int r = (
addr[
x] >> 16) & 0xFF;
494 int g = (
addr[
x] >> 8) & 0xFF;
495 int b = (
addr[
x] >> 0) & 0xFF;
498 addr = SkTAddOffset<CGRGBPixel>(
addr, cgRowBytes);
505 if (fPreBlend.isApplicable()) {
506 RGBToLcd16<true>(cgPixels, cgRowBytes, glyph, imageBuffer,
507 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
509 RGBToLcd16<false>(cgPixels, cgRowBytes, glyph, imageBuffer,
510 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
514 if (fPreBlend.isApplicable()) {
515 RGBToA8<true>(cgPixels, cgRowBytes, glyph, imageBuffer, fPreBlend.fG);
517 RGBToA8<false>(cgPixels, cgRowBytes, glyph, imageBuffer, fPreBlend.fG);
523 uint8_t*
dst = (uint8_t*)imageBuffer;
524 for (
int y = 0;
y < glyph.
height();
y++) {
525 cgpixels_to_bits(
dst, cgPixels,
width);
526 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
527 dst = SkTAddOffset<uint8_t>(
dst, dstRB);
534 for (
int y = 0;
y < glyph.
height();
y++) {
536 dst[
x] = cgpixels_to_pmcolor(cgPixels[
x]);
538 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
539 dst = SkTAddOffset<SkPMColor>(
dst, dstRB);
549class SkCTPathGeometrySink {
554 void goingTo(
const CGPoint pt) {
557 fBuilder.
moveTo(fCurrent.x, -fCurrent.y);
562 bool currentIsNot(
const CGPoint pt) {
563 return fCurrent.x != pt.x || fCurrent.y != pt.y;
567 SkCTPathGeometrySink() : fStarted{
false}, fCurrent{0,0} {}
571 static void ApplyElement(
void *ctx,
const CGPathElement *element) {
572 SkCTPathGeometrySink&
self = *(SkCTPathGeometrySink*)ctx;
573 CGPoint*
points = element->points;
575 switch (element->type) {
576 case kCGPathElementMoveToPoint:
577 self.fStarted =
false;
581 case kCGPathElementAddLineToPoint:
588 case kCGPathElementAddQuadCurveToPoint:
596 case kCGPathElementAddCurveToPoint:
608 case kCGPathElementCloseSubpath:
610 self.fBuilder.close();
627#define kScaleForSubPixelPositionHinting (4.0f)
633 CGAffineTransform xform = fTransform;
643 if (fDoSubPosition) {
645 scaleX = scaleY = kScaleForSubPixelPositionHinting;
647 switch (this->computeAxisAlignmentForHText()) {
658 CGAffineTransform
scale(CGAffineTransformMakeScale(SkScalarToCGFloat(scaleX),
659 SkScalarToCGFloat(scaleY)));
660 xform = CGAffineTransformConcat(fTransform,
scale);
663 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.
getGlyphID());
664 SkUniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
671 SkCTPathGeometrySink sink;
672 CGPathApply(cgPath.get(), &sink, SkCTPathGeometrySink::ApplyElement);
673 *
path = sink.detach();
674 if (fDoSubPosition) {
682void SkScalerContext_Mac::generateFontMetrics(
SkFontMetrics* metrics) {
683 if (
nullptr == metrics) {
687 CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
689 metrics->
fTop = SkScalarFromCGFloat(-SkCGRectGetMaxY(theBounds));
690 metrics->
fAscent = SkScalarFromCGFloat(-CTFontGetAscent(fCTFont.get()));
691 metrics->
fDescent = SkScalarFromCGFloat( CTFontGetDescent(fCTFont.get()));
692 metrics->
fBottom = SkScalarFromCGFloat(-SkCGRectGetMinY(theBounds));
693 metrics->
fLeading = SkScalarFromCGFloat( CTFontGetLeading(fCTFont.get()));
694 metrics->
fAvgCharWidth = SkScalarFromCGFloat( SkCGRectGetWidth(theBounds));
695 metrics->
fXMin = SkScalarFromCGFloat( SkCGRectGetMinX(theBounds));
696 metrics->
fXMax = SkScalarFromCGFloat( SkCGRectGetMaxX(theBounds));
698 metrics->
fXHeight = SkScalarFromCGFloat( CTFontGetXHeight(fCTFont.get()));
699 metrics->
fCapHeight = SkScalarFromCGFloat( CTFontGetCapHeight(fCTFont.get()));
700 metrics->
fUnderlineThickness = SkScalarFromCGFloat( CTFontGetUnderlineThickness(fCTFont.get()));
701 metrics->
fUnderlinePosition = -SkScalarFromCGFloat( CTFontGetUnderlinePosition(fCTFont.get()));
709 CFArrayRef ctAxes = ((SkTypeface_Mac*)this->getTypeface())->getVariationAxes();
710 if ((ctAxes && CFArrayGetCount(ctAxes) > 0) ||
711 ((SkTypeface_Mac*)this->getTypeface())->fHasColorGlyphs)
721 const CGFloat fontSize = CTFontGetSize(fCTFont.get());
722 const unsigned int upem = CTFontGetUnitsPerEm(fCTFont.get());
723 const unsigned int maxSaneHeight = upem * 2;
734 if (xHeight && xHeight < maxSaneHeight) {
735 metrics->
fXHeight = SkScalarFromCGFloat(xHeight * fontSize / upem);
738 if (capHeight && capHeight < maxSaneHeight) {
739 metrics->
fCapHeight = SkScalarFromCGFloat(capHeight * fontSize / upem);
747 if (strikeoutSize && strikeoutSize < maxSaneHeight) {
752 if (strikeoutPos && strikeoutPos < maxSaneHeight) {
static const SkColor bgColor
static const int points[]
static const uint32_t bgra[kNumPixels]
#define SkDEBUGFAIL(message)
static U8CPU SkComputeLuminance(U8CPU r, U8CPU g, U8CPU b)
static U16CPU SkPack888ToRGB16(U8CPU r, U8CPU g, U8CPU b)
static SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
#define SkColorGetR(color)
#define SkColorGetG(color)
#define SkColorGetB(color)
#define SkEndian_SwapBE16(n)
#define SkTEndian_SwapBE32(n)
#define SkFixedToFloat(x)
@ kNone
glyph outlines unchanged
static int SkNextPow2(int value)
#define INHERITED(method,...)
#define SkScalarInvert(x)
static constexpr bool SkToBool(const T &x)
constexpr uint8_t SkToU8(S x)
const void * data() const
SkGlyphID getGlyphID() const
SkMask::Format maskFormat() const
SkFixed getSubYFixed() const
SkFixed getSubXFixed() const
static constexpr int kMScaleX
horizontal scale factor
static constexpr int kMTransY
vertical translation
static constexpr int kMTransX
horizontal translation
static constexpr int kMSkewY
vertical skew factor
static constexpr int kMScaleY
vertical scale factor
static constexpr int kMSkewX
horizontal skew factor
SkPathBuilder & moveTo(SkPoint pt)
void reset(T *ptr=nullptr)
static float max(float r, float g, float b)
static float lum(float r, float g, float b)
void(* memset32)(uint32_t[], uint32_t, int)
unsigned useCenter Optional< SkMatrix > matrix
sk_sp< const SkImage > image
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
SkScalar fTop
greatest extent above origin of any glyph bounding box, typically negative; deprecated with variable ...
SkScalar fLeading
distance to add between lines, typically positive or zero
SkScalar fAvgCharWidth
average character width, zero if unknown
SkScalar fStrikeoutPosition
distance from baseline to bottom of stroke, typically negative
SkScalar fStrikeoutThickness
strikeout thickness
SkScalar fMaxCharWidth
maximum character width, zero if unknown
SkScalar fBottom
greatest extent below origin of any glyph bounding box, typically positive; deprecated with variable ...
uint32_t fFlags
FontMetricsFlags indicating which metrics are valid.
SkScalar fAscent
distance to reserve above baseline, typically negative
SkScalar fXHeight
height of lower-case 'x', zero if unknown, typically negative
SkScalar fUnderlineThickness
underline thickness
@ kStrikeoutPositionIsValid_Flag
set if fStrikeoutPosition is valid
@ kStrikeoutThicknessIsValid_Flag
set if fStrikeoutThickness is valid
@ kUnderlinePositionIsValid_Flag
set if fUnderlinePosition is valid
@ kUnderlineThicknessIsValid_Flag
set if fUnderlineThickness is valid
@ kBoundsInvalid_Flag
set if fTop, fBottom, fXMin, fXMax invalid
SkScalar fDescent
distance to reserve below baseline, typically positive
SkScalar fCapHeight
height of an upper-case letter, zero if unknown, typically negative
SkScalar fXMin
greatest extent to left of origin of any glyph bounding box, typically negative; deprecated with vari...
SkScalar fUnderlinePosition
distance from baseline to top of stroke, typically positive
SkScalar fXMax
greatest extent to right of origin of any glyph bounding box, typically positive; deprecated with var...
@ kA8_Format
8bits per pixel mask (e.g. antialiasing)
@ kLCD16_Format
565 alpha for r/g/b
@ kARGB32_Format
SkPMColor.
@ kBW_Format
1bit per pixel mask (e.g. monochrome)
SK_OT_SHORT yStrikeoutSize
SK_OT_SHORT yStrikeoutPosition
static constexpr SK_OT_ULONG TAG
SkScalar fBottom
larger y-axis bounds
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
SkScalar fRight
larger x-axis bounds
void roundOut(SkIRect *dst) const
static sk_sp< SkShader > linear(sk_sp< SkShader > shader)