• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2006 The Android Open Source Project
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkTypes.h"
9 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
10 
11 #ifdef SK_BUILD_FOR_MAC
12 #import <ApplicationServices/ApplicationServices.h>
13 #endif
14 
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>
20 #endif
21 
22 #include "include/core/SkColor.h"
23 #include "include/core/SkColorPriv.h"
24 #include "include/core/SkFontMetrics.h"
25 #include "include/core/SkFontTypes.h"
26 #include "include/core/SkMatrix.h"
27 #include "include/core/SkPathBuilder.h"
28 #include "include/core/SkPoint.h"
29 #include "include/core/SkRect.h"
30 #include "include/core/SkScalar.h"
31 #include "include/core/SkTypeface.h"
32 #include "include/private/SkColorData.h"
33 #include "include/private/base/SkFixed.h"
34 #include "include/private/base/SkTemplates.h"
35 #include "include/private/base/SkTo.h"
36 #include "src/base/SkAutoMalloc.h"
37 #include "src/base/SkMathPriv.h"
38 #include "src/core/SkEndian.h"
39 #include "src/core/SkGlyph.h"
40 #include "src/core/SkMask.h"
41 #include "src/core/SkMaskGamma.h"
42 #include "src/core/SkOpts.h"
43 #include "src/ports/SkScalerContext_mac_ct.h"
44 #include "src/ports/SkTypeface_mac_ct.h"
45 #include "src/sfnt/SkOTTableTypes.h"
46 #include "src/sfnt/SkOTTable_OS_2.h"
47 #include "src/utils/mac/SkCGBase.h"
48 #include "src/utils/mac/SkCGGeometry.h"
49 #include "src/utils/mac/SkCTFont.h"
50 #include "src/utils/mac/SkUniqueCFRef.h"
51 
52 #include <algorithm>
53 
54 class SkDescriptor;
55 
56 
57 namespace {
58 static inline const constexpr bool kSkShowTextBlitCoverage = false;
59 }
60 
sk_memset_rect32(uint32_t * ptr,uint32_t value,int width,int height,size_t rowBytes)61 static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
62                              int width, int height, size_t rowBytes) {
63     SkASSERT(width);
64     SkASSERT(width * sizeof(uint32_t) <= rowBytes);
65 
66     if (width >= 32) {
67         while (height) {
68             SkOpts::memset32(ptr, value, width);
69             ptr = (uint32_t*)((char*)ptr + rowBytes);
70             height -= 1;
71         }
72         return;
73     }
74 
75     rowBytes -= width * sizeof(uint32_t);
76 
77     if (width >= 8) {
78         while (height) {
79             int w = width;
80             do {
81                 *ptr++ = value; *ptr++ = value;
82                 *ptr++ = value; *ptr++ = value;
83                 *ptr++ = value; *ptr++ = value;
84                 *ptr++ = value; *ptr++ = value;
85                 w -= 8;
86             } while (w >= 8);
87             while (--w >= 0) {
88                 *ptr++ = value;
89             }
90             ptr = (uint32_t*)((char*)ptr + rowBytes);
91             height -= 1;
92         }
93     } else {
94         while (height) {
95             int w = width;
96             do {
97                 *ptr++ = value;
98             } while (--w > 0);
99             ptr = (uint32_t*)((char*)ptr + rowBytes);
100             height -= 1;
101         }
102     }
103 }
104 
CGRGBPixel_getAlpha(CGRGBPixel pixel)105 static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
106     return pixel & 0xFF;
107 }
108 
MatrixToCGAffineTransform(const SkMatrix & matrix)109 static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix) {
110     return CGAffineTransformMake( SkScalarToCGFloat(matrix[SkMatrix::kMScaleX]),
111                                  -SkScalarToCGFloat(matrix[SkMatrix::kMSkewY] ),
112                                  -SkScalarToCGFloat(matrix[SkMatrix::kMSkewX] ),
113                                   SkScalarToCGFloat(matrix[SkMatrix::kMScaleY]),
114                                   SkScalarToCGFloat(matrix[SkMatrix::kMTransX]),
115                                   SkScalarToCGFloat(matrix[SkMatrix::kMTransY]));
116 }
117 
SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,const SkScalerContextEffects & effects,const SkDescriptor * desc)118 SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
119                                          const SkScalerContextEffects& effects,
120                                          const SkDescriptor* desc)
121         : INHERITED(std::move(typeface), effects, desc)
122         , fOffscreen(fRec.fForegroundColor)
123         , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
124 
125 {
126     CTFontRef ctFont = (CTFontRef)this->getTypeface()->internal_private_getCTFontRef();
127 
128     // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
129     // As a result, it is necessary to know the actual device size and request that.
130     SkVector scale;
131     SkMatrix skTransform;
132     bool invertible = fRec.computeMatrices(SkScalerContextRec::PreMatrixScale::kVertical,
133                                            &scale, &skTransform, nullptr, nullptr, nullptr);
134     fTransform = MatrixToCGAffineTransform(skTransform);
135     // CGAffineTransformInvert documents that if the transform is non-invertible it will return the
136     // passed transform unchanged. It does so, but then also prints a message to stdout. Avoid this.
137     if (invertible) {
138         fInvTransform = CGAffineTransformInvert(fTransform);
139     } else {
140         fInvTransform = fTransform;
141     }
142 
143     // The transform contains everything except the requested text size.
144     // Some properties, like 'trak', are based on the optical text size.
145     CGFloat textSize = SkScalarToCGFloat(scale.y());
146     fCTFont = SkCTFontCreateExactCopy(ctFont, textSize,
147                                       ((SkTypeface_Mac*)this->getTypeface())->fOpszVariation);
148     fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(), nullptr));
149 }
150 
RoundSize(int dimension)151 static int RoundSize(int dimension) {
152     return SkNextPow2(dimension);
153 }
154 
CGColorForSkColor(CGColorSpaceRef rgbcs,SkColor bgra)155 static CGColorRef CGColorForSkColor(CGColorSpaceRef rgbcs, SkColor bgra) {
156     CGFloat components[4];
157     components[0] = (CGFloat)SkColorGetR(bgra) * (1/255.0f);
158     components[1] = (CGFloat)SkColorGetG(bgra) * (1/255.0f);
159     components[2] = (CGFloat)SkColorGetB(bgra) * (1/255.0f);
160     // CoreText applies the CGContext fill color as the COLR foreground color.
161     // However, the alpha is applied to the whole glyph drawing (and Skia will do that as well).
162     // For now, cannot really support COLR foreground color alpha.
163     components[3] = 1.0f;
164     return CGColorCreate(rgbcs, components);
165 }
166 
Offscreen(SkColor foregroundColor)167 SkScalerContext_Mac::Offscreen::Offscreen(SkColor foregroundColor)
168     : fCG(nullptr)
169     , fSKForegroundColor(foregroundColor)
170     , fDoAA(false)
171     , fDoLCD(false)
172 {
173     fSize.set(0, 0);
174 }
175 
getCG(const SkScalerContext_Mac & context,const SkGlyph & glyph,CGGlyph glyphID,size_t * rowBytesPtr,bool generateA8FromLCD)176 CGRGBPixel* SkScalerContext_Mac::Offscreen::getCG(const SkScalerContext_Mac& context,
177                                                   const SkGlyph& glyph, CGGlyph glyphID,
178                                                   size_t* rowBytesPtr,
179                                                   bool generateA8FromLCD) {
180     if (!fRGBSpace) {
181         //It doesn't appear to matter what color space is specified.
182         //Regular blends and antialiased text are always (s*a + d*(1-a))
183         //and subpixel antialiased text is always g=2.0.
184         fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
185         fCGForegroundColor.reset(CGColorForSkColor(fRGBSpace.get(), fSKForegroundColor));
186     }
187 
188     // default to kBW_Format
189     bool doAA = false;
190     bool doLCD = false;
191 
192     if (SkMask::kBW_Format != glyph.maskFormat()) {
193         doLCD = true;
194         doAA = true;
195     }
196 
197     // FIXME: lcd smoothed un-hinted rasterization unsupported.
198     if (!generateA8FromLCD && SkMask::kA8_Format == glyph.maskFormat()) {
199         doLCD = false;
200         doAA = true;
201     }
202 
203     // If this font might have color glyphs, disable LCD as there's no way to support it.
204     // CoreText doesn't tell us which format it ended up using, so we can't detect it.
205     // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
206     if (SkMask::kARGB32_Format == glyph.maskFormat()) {
207         doLCD = false;
208     }
209 
210     size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
211     if (!fCG || fSize.fWidth < glyph.width() || fSize.fHeight < glyph.height()) {
212         if (fSize.fWidth < glyph.width()) {
213             fSize.fWidth = RoundSize(glyph.width());
214         }
215         if (fSize.fHeight < glyph.height()) {
216             fSize.fHeight = RoundSize(glyph.height());
217         }
218 
219         rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
220         void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
221         const CGImageAlphaInfo alpha = (glyph.isColor())
222                                      ? kCGImageAlphaPremultipliedFirst
223                                      : kCGImageAlphaNoneSkipFirst;
224         const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | (CGBitmapInfo)alpha;
225         fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
226                                         rowBytes, fRGBSpace.get(), bitmapInfo));
227 
228         // Skia handles quantization and subpixel positioning,
229         // so disable quantization and enable subpixel positioning in CG.
230         CGContextSetAllowsFontSubpixelQuantization(fCG.get(), false);
231         CGContextSetShouldSubpixelQuantizeFonts(fCG.get(), false);
232 
233         // Because CG always draws from the horizontal baseline,
234         // if there is a non-integral translation from the horizontal origin to the vertical origin,
235         // then CG cannot draw the glyph in the correct location without subpixel positioning.
236         CGContextSetAllowsFontSubpixelPositioning(fCG.get(), true);
237         CGContextSetShouldSubpixelPositionFonts(fCG.get(), true);
238 
239         CGContextSetTextDrawingMode(fCG.get(), kCGTextFill);
240 
241         if (SkMask::kARGB32_Format != glyph.maskFormat()) {
242             // Draw black on white to create mask. (Special path exists to speed this up in CG.)
243             CGContextSetGrayFillColor(fCG.get(), 0.0f, 1.0f);
244         } else {
245             CGContextSetFillColorWithColor(fCG.get(), fCGForegroundColor.get());
246         }
247 
248         // force our checks below to happen
249         fDoAA = !doAA;
250         fDoLCD = !doLCD;
251 
252         CGContextSetTextMatrix(fCG.get(), context.fTransform);
253     }
254 
255     if (fDoAA != doAA) {
256         CGContextSetShouldAntialias(fCG.get(), doAA);
257         fDoAA = doAA;
258     }
259     if (fDoLCD != doLCD) {
260         CGContextSetShouldSmoothFonts(fCG.get(), doLCD);
261         fDoLCD = doLCD;
262     }
263 
264     CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
265     // skip rows based on the glyph's height
266     image += (fSize.fHeight - glyph.height()) * fSize.fWidth;
267 
268     // Erase to white (or transparent black if it's a color glyph, to not composite against white).
269     uint32_t bgColor = (!glyph.isColor()) ? 0xFFFFFFFF : 0x00000000;
270     sk_memset_rect32(image, bgColor, glyph.width(), glyph.height(), rowBytes);
271 
272     float subX = 0;
273     float subY = 0;
274     if (context.fDoSubPosition) {
275         subX = SkFixedToFloat(glyph.getSubXFixed());
276         subY = SkFixedToFloat(glyph.getSubYFixed());
277     }
278 
279     CGPoint point = CGPointMake(-glyph.left() + subX, glyph.top() + glyph.height() - subY);
280     // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
281     // 'positions' which are in text space. The glyph location (in device space) must be
282     // mapped into text space, so that CG can convert it back into device space.
283     // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
284     //
285     // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
286     // So always make the font transform identity and place the transform on the context.
287     point = CGPointApplyAffineTransform(point, context.fInvTransform);
288 
289     CTFontDrawGlyphs(context.fCTFont.get(), &glyphID, &point, 1, fCG.get());
290 
291     SkASSERT(rowBytesPtr);
292     *rowBytesPtr = rowBytes;
293     return image;
294 }
295 
generateAdvance(SkGlyph * glyph)296 bool SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
297     return false;
298 }
299 
generateMetrics(SkGlyph * glyph,SkArenaAlloc * alloc)300 void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) {
301     glyph->fMaskFormat = fRec.fMaskFormat;
302 
303     if (((SkTypeface_Mac*)this->getTypeface())->fHasColorGlyphs) {
304         glyph->setPath(alloc, nullptr, false);
305     }
306 
307     const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
308     glyph->zeroMetrics();
309 
310     // The following block produces cgAdvance in CG units (pixels, y up).
311     CGSize cgAdvance;
312     CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
313                                &cgGlyph, &cgAdvance, 1);
314     cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
315     glyph->fAdvanceX =  SkFloatFromCGFloat(cgAdvance.width);
316     glyph->fAdvanceY = -SkFloatFromCGFloat(cgAdvance.height);
317 
318     // The following produces skBounds in SkGlyph units (pixels, y down),
319     // or returns early if skBounds would be empty.
320     SkRect skBounds;
321 
322     // Glyphs are always drawn from the horizontal origin. The caller must manually use the result
323     // of CTFontGetVerticalTranslationsForGlyphs to calculate where to draw the glyph for vertical
324     // glyphs. As a result, always get the horizontal bounds of a glyph and translate it if the
325     // glyph is vertical. This avoids any diagreement between the various means of retrieving
326     // vertical metrics.
327     {
328         // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
329         CGRect cgBounds;
330         CTFontGetBoundingRectsForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
331                                         &cgGlyph, &cgBounds, 1);
332         cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
333 
334         // BUG?
335         // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
336         // it should be empty. So, if we see a zero-advance, we check if it has an
337         // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
338         // is rare, so we won't incur a big performance cost for this extra check.
339         if (0 == cgAdvance.width && 0 == cgAdvance.height) {
340             SkUniqueCFRef<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph,nullptr));
341             if (!path || CGPathIsEmpty(path.get())) {
342                 return;
343             }
344         }
345 
346         if (SkCGRectIsEmpty(cgBounds)) {
347             return;
348         }
349 
350         // Convert cgBounds to SkGlyph units (pixels, y down).
351         skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
352                                     cgBounds.size.width, cgBounds.size.height);
353     }
354 
355     // Currently the bounds are based on being rendered at (0,0).
356     // The top left must not move, since that is the base from which subpixel positioning is offset.
357     if (fDoSubPosition) {
358         skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
359         skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
360     }
361 
362     // We're trying to pack left and top into int16_t,
363     // and width and height into uint16_t, after outsetting by 1.
364     if (!SkRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(skBounds)) {
365         return;
366     }
367 
368     SkIRect skIBounds;
369     skBounds.roundOut(&skIBounds);
370     // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
371     // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
372     // is not currently known, as CG dilates the outlines by some percentage.
373     // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
374     skIBounds.outset(1, 1);
375     glyph->fLeft = SkToS16(skIBounds.fLeft);
376     glyph->fTop = SkToS16(skIBounds.fTop);
377     glyph->fWidth = SkToU16(skIBounds.width());
378     glyph->fHeight = SkToU16(skIBounds.height());
379 }
380 
sk_pow2_table(size_t i)381 static constexpr uint8_t sk_pow2_table(size_t i) {
382     return SkToU8(((i * i + 128) / 255));
383 }
384 
385 /**
386  *  This will invert the gamma applied by CoreGraphics, so we can get linear
387  *  values.
388  *
389  *  CoreGraphics obscurely defaults to 2.0 as the subpixel coverage gamma value.
390  *  The color space used does not appear to affect this choice.
391  */
392 static constexpr auto gLinearCoverageFromCGLCDValue = SkMakeArray<256>(sk_pow2_table);
393 
cgpixels_to_bits(uint8_t dst[],const CGRGBPixel src[],int count)394 static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
395     while (count > 0) {
396         uint8_t mask = 0;
397         for (int i = 7; i >= 0; --i) {
398             mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
399             if (0 == --count) {
400                 break;
401             }
402         }
403         *dst++ = mask;
404     }
405 }
406 
407 template<bool APPLY_PREBLEND>
rgb_to_a8(CGRGBPixel rgb,const uint8_t * table8)408 static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
409     U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
410     U8CPU g = 0xFF - ((rgb >>  8) & 0xFF);
411     U8CPU b = 0xFF - ((rgb >>  0) & 0xFF);
412     U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
413     if constexpr (kSkShowTextBlitCoverage) {
414         lum = std::max(lum, (U8CPU)0x30);
415     }
416     return lum;
417 }
418 
419 template<bool APPLY_PREBLEND>
RGBToA8(const CGRGBPixel * SK_RESTRICT cgPixels,size_t cgRowBytes,const SkGlyph & glyph,void * glyphImage,const uint8_t * table8)420 static void RGBToA8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
421                     const SkGlyph& glyph, void* glyphImage, const uint8_t* table8) {
422     const int width = glyph.width();
423     const int height = glyph.height();
424     size_t dstRB = glyph.rowBytes();
425     uint8_t* SK_RESTRICT dst = (uint8_t*)glyphImage;
426 
427     for (int y = 0; y < height; y++) {
428         for (int i = 0; i < width; ++i) {
429             dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
430         }
431         cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
432         dst = SkTAddOffset<uint8_t>(dst, dstRB);
433     }
434 }
435 
436 template<bool APPLY_PREBLEND>
RGBToLcd16(CGRGBPixel rgb,const uint8_t * tableR,const uint8_t * tableG,const uint8_t * tableB)437 static uint16_t RGBToLcd16(CGRGBPixel rgb,
438                            const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
439     U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
440     U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >>  8) & 0xFF), tableG);
441     U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >>  0) & 0xFF), tableB);
442     if constexpr (kSkShowTextBlitCoverage) {
443         r = std::max(r, (U8CPU)0x30);
444         g = std::max(g, (U8CPU)0x30);
445         b = std::max(b, (U8CPU)0x30);
446     }
447     return SkPack888ToRGB16(r, g, b);
448 }
449 
450 template<bool APPLY_PREBLEND>
RGBToLcd16(const CGRGBPixel * SK_RESTRICT cgPixels,size_t cgRowBytes,const SkGlyph & glyph,void * glyphImage,const uint8_t * tableR,const uint8_t * tableG,const uint8_t * tableB)451 static void RGBToLcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
452                        const SkGlyph& glyph, void* glyphImage,
453                        const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
454     const int width = glyph.width();
455     const int height = glyph.height();
456     size_t dstRB = glyph.rowBytes();
457     uint16_t* SK_RESTRICT dst = (uint16_t*)glyphImage;
458 
459     for (int y = 0; y < height; y++) {
460         for (int i = 0; i < width; i++) {
461             dst[i] = RGBToLcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
462         }
463         cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
464         dst = SkTAddOffset<uint16_t>(dst, dstRB);
465     }
466 }
467 
cgpixels_to_pmcolor(CGRGBPixel rgb)468 static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
469     U8CPU a = (rgb >> 24) & 0xFF;
470     U8CPU r = (rgb >> 16) & 0xFF;
471     U8CPU g = (rgb >>  8) & 0xFF;
472     U8CPU b = (rgb >>  0) & 0xFF;
473     if constexpr (kSkShowTextBlitCoverage) {
474         a = std::max(a, (U8CPU)0x30);
475     }
476     return SkPackARGB32(a, r, g, b);
477 }
478 
generateImage(const SkGlyph & glyph)479 void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
480     CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
481 
482     // FIXME: lcd smoothed un-hinted rasterization unsupported.
483     bool requestSmooth = fRec.getHinting() != SkFontHinting::kNone;
484 
485     // Draw the glyph
486     size_t cgRowBytes;
487     CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, requestSmooth);
488     if (cgPixels == nullptr) {
489         return;
490     }
491 
492     // Fix the glyph
493     if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
494         (glyph.fMaskFormat == SkMask::kA8_Format
495          && requestSmooth
496          && SkCTFontGetSmoothBehavior() != SkCTFontSmoothBehavior::none))
497     {
498         const uint8_t* linear = gLinearCoverageFromCGLCDValue.data();
499 
500         //Note that the following cannot really be integrated into the
501         //pre-blend, since we may not be applying the pre-blend; when we aren't
502         //applying the pre-blend it means that a filter wants linear anyway.
503         //Other code may also be applying the pre-blend, so we'd need another
504         //one with this and one without.
505         CGRGBPixel* addr = cgPixels;
506         for (int y = 0; y < glyph.fHeight; ++y) {
507             for (int x = 0; x < glyph.fWidth; ++x) {
508                 int r = (addr[x] >> 16) & 0xFF;
509                 int g = (addr[x] >>  8) & 0xFF;
510                 int b = (addr[x] >>  0) & 0xFF;
511                 addr[x] = (linear[r] << 16) | (linear[g] << 8) | linear[b];
512             }
513             addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
514         }
515     }
516 
517     // Convert glyph to mask
518     switch (glyph.fMaskFormat) {
519         case SkMask::kLCD16_Format: {
520             if (fPreBlend.isApplicable()) {
521                 RGBToLcd16<true>(cgPixels, cgRowBytes, glyph, glyph.fImage,
522                                  fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
523             } else {
524                 RGBToLcd16<false>(cgPixels, cgRowBytes, glyph, glyph.fImage,
525                                   fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
526             }
527         } break;
528         case SkMask::kA8_Format: {
529             if (fPreBlend.isApplicable()) {
530                 RGBToA8<true>(cgPixels, cgRowBytes, glyph, glyph.fImage, fPreBlend.fG);
531             } else {
532                 RGBToA8<false>(cgPixels, cgRowBytes, glyph, glyph.fImage, fPreBlend.fG);
533             }
534         } break;
535         case SkMask::kBW_Format: {
536             const int width = glyph.fWidth;
537             size_t dstRB = glyph.rowBytes();
538             uint8_t* dst = (uint8_t*)glyph.fImage;
539             for (int y = 0; y < glyph.fHeight; y++) {
540                 cgpixels_to_bits(dst, cgPixels, width);
541                 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
542                 dst = SkTAddOffset<uint8_t>(dst, dstRB);
543             }
544         } break;
545         case SkMask::kARGB32_Format: {
546             const int width = glyph.fWidth;
547             size_t dstRB = glyph.rowBytes();
548             SkPMColor* dst = (SkPMColor*)glyph.fImage;
549             for (int y = 0; y < glyph.fHeight; y++) {
550                 for (int x = 0; x < width; ++x) {
551                     dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
552                 }
553                 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
554                 dst = SkTAddOffset<SkPMColor>(dst, dstRB);
555             }
556         } break;
557         default:
558             SkDEBUGFAIL("unexpected mask format");
559             break;
560     }
561 }
562 
563 namespace {
564 class SkCTPathGeometrySink {
565     SkPathBuilder fBuilder;
566     bool fStarted;
567     CGPoint fCurrent;
568 
goingTo(const CGPoint pt)569     void goingTo(const CGPoint pt) {
570         if (!fStarted) {
571             fStarted = true;
572             fBuilder.moveTo(fCurrent.x, -fCurrent.y);
573         }
574         fCurrent = pt;
575     }
576 
currentIsNot(const CGPoint pt)577     bool currentIsNot(const CGPoint pt) {
578         return fCurrent.x != pt.x || fCurrent.y != pt.y;
579     }
580 
581 public:
SkCTPathGeometrySink()582     SkCTPathGeometrySink() : fStarted{false}, fCurrent{0,0} {}
583 
detach()584     SkPath detach() { return fBuilder.detach(); }
585 
ApplyElement(void * ctx,const CGPathElement * element)586     static void ApplyElement(void *ctx, const CGPathElement *element) {
587         SkCTPathGeometrySink& self = *(SkCTPathGeometrySink*)ctx;
588         CGPoint* points = element->points;
589 
590         switch (element->type) {
591             case kCGPathElementMoveToPoint:
592                 self.fStarted = false;
593                 self.fCurrent = points[0];
594                 break;
595 
596             case kCGPathElementAddLineToPoint:
597                 if (self.currentIsNot(points[0])) {
598                     self.goingTo(points[0]);
599                     self.fBuilder.lineTo(points[0].x, -points[0].y);
600                 }
601                 break;
602 
603             case kCGPathElementAddQuadCurveToPoint:
604                 if (self.currentIsNot(points[0]) || self.currentIsNot(points[1])) {
605                     self.goingTo(points[1]);
606                     self.fBuilder.quadTo(points[0].x, -points[0].y,
607                                          points[1].x, -points[1].y);
608                 }
609                 break;
610 
611             case kCGPathElementAddCurveToPoint:
612                 if (self.currentIsNot(points[0]) ||
613                     self.currentIsNot(points[1]) ||
614                     self.currentIsNot(points[2]))
615                 {
616                     self.goingTo(points[2]);
617                     self.fBuilder.cubicTo(points[0].x, -points[0].y,
618                                           points[1].x, -points[1].y,
619                                           points[2].x, -points[2].y);
620                 }
621                 break;
622 
623             case kCGPathElementCloseSubpath:
624                 if (self.fStarted) {
625                     self.fBuilder.close();
626                 }
627                 break;
628 
629             default:
630                 SkDEBUGFAIL("Unknown path element!");
631                 break;
632             }
633     }
634 };
635 } // namespace
636 
637 /*
638  *  Our subpixel resolution is only 2 bits in each direction, so a scale of 4
639  *  seems sufficient, and possibly even correct, to allow the hinted outline
640  *  to be subpixel positioned.
641  */
642 #define kScaleForSubPixelPositionHinting (4.0f)
643 
generatePath(const SkGlyph & glyph,SkPath * path)644 bool SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
645     SkScalar scaleX = SK_Scalar1;
646     SkScalar scaleY = SK_Scalar1;
647 
648     CGAffineTransform xform = fTransform;
649     /*
650      *  For subpixel positioning, we want to return an unhinted outline, so it
651      *  can be positioned nicely at fractional offsets. However, we special-case
652      *  if the baseline of the (horizontal) text is axis-aligned. In those cases
653      *  we want to retain hinting in the direction orthogonal to the baseline.
654      *  e.g. for horizontal baseline, we want to retain hinting in Y.
655      *  The way we remove hinting is to scale the font by some value (4) in that
656      *  direction, ask for the path, and then scale the path back down.
657      */
658     if (fDoSubPosition) {
659         // start out by assuming that we want no hining in X and Y
660         scaleX = scaleY = kScaleForSubPixelPositionHinting;
661         // now see if we need to restore hinting for axis-aligned baselines
662         switch (this->computeAxisAlignmentForHText()) {
663             case SkAxisAlignment::kX:
664                 scaleY = SK_Scalar1; // want hinting in the Y direction
665                 break;
666             case SkAxisAlignment::kY:
667                 scaleX = SK_Scalar1; // want hinting in the X direction
668                 break;
669             default:
670                 break;
671         }
672 
673         CGAffineTransform scale(CGAffineTransformMakeScale(SkScalarToCGFloat(scaleX),
674                                                            SkScalarToCGFloat(scaleY)));
675         xform = CGAffineTransformConcat(fTransform, scale);
676     }
677 
678     CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
679     SkUniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
680 
681     path->reset();
682     if (!cgPath) {
683         return false;
684     }
685 
686     SkCTPathGeometrySink sink;
687     CGPathApply(cgPath.get(), &sink, SkCTPathGeometrySink::ApplyElement);
688     *path = sink.detach();
689     if (fDoSubPosition) {
690         SkMatrix m;
691         m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
692         path->transform(m);
693     }
694     return true;
695 }
696 
generateFontMetrics(SkFontMetrics * metrics)697 void SkScalerContext_Mac::generateFontMetrics(SkFontMetrics* metrics) {
698     if (nullptr == metrics) {
699         return;
700     }
701 
702     CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
703 
704     metrics->fTop          = SkScalarFromCGFloat(-SkCGRectGetMaxY(theBounds));
705     metrics->fAscent       = SkScalarFromCGFloat(-CTFontGetAscent(fCTFont.get()));
706     metrics->fDescent      = SkScalarFromCGFloat( CTFontGetDescent(fCTFont.get()));
707     metrics->fBottom       = SkScalarFromCGFloat(-SkCGRectGetMinY(theBounds));
708     metrics->fLeading      = SkScalarFromCGFloat( CTFontGetLeading(fCTFont.get()));
709     metrics->fAvgCharWidth = SkScalarFromCGFloat( SkCGRectGetWidth(theBounds));
710     metrics->fXMin         = SkScalarFromCGFloat( SkCGRectGetMinX(theBounds));
711     metrics->fXMax         = SkScalarFromCGFloat( SkCGRectGetMaxX(theBounds));
712     metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
713     metrics->fXHeight      = SkScalarFromCGFloat( CTFontGetXHeight(fCTFont.get()));
714     metrics->fCapHeight    = SkScalarFromCGFloat( CTFontGetCapHeight(fCTFont.get()));
715     metrics->fUnderlineThickness = SkScalarFromCGFloat( CTFontGetUnderlineThickness(fCTFont.get()));
716     metrics->fUnderlinePosition = -SkScalarFromCGFloat( CTFontGetUnderlinePosition(fCTFont.get()));
717     metrics->fStrikeoutThickness = 0;
718     metrics->fStrikeoutPosition = 0;
719 
720     metrics->fFlags = 0;
721     metrics->fFlags |= SkFontMetrics::kUnderlineThicknessIsValid_Flag;
722     metrics->fFlags |= SkFontMetrics::kUnderlinePositionIsValid_Flag;
723 
724     CFArrayRef ctAxes = ((SkTypeface_Mac*)this->getTypeface())->getVariationAxes();
725     if ((ctAxes && CFArrayGetCount(ctAxes) > 0) ||
726         ((SkTypeface_Mac*)this->getTypeface())->fHasColorGlyphs)
727     {
728         // The bounds are only valid for the default outline variation.
729         // In particular `sbix` and `SVG ` data may draw outside these bounds.
730         metrics->fFlags |= SkFontMetrics::kBoundsInvalid_Flag;
731     }
732 
733     sk_sp<SkData> os2 = this->getTypeface()->copyTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG));
734     if (os2) {
735         // 'fontSize' is correct because the entire resolved size is set by the constructor.
736         const CGFloat fontSize = CTFontGetSize(fCTFont.get());
737         const unsigned int upem = CTFontGetUnitsPerEm(fCTFont.get());
738         const unsigned int maxSaneHeight = upem * 2;
739 
740         // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
741         // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent
742         // and the cap-height is always 0.8888 of the ascent. It appears that the values from the
743         // 'OS/2' table are read, but then overwritten if the font is not a system font. As a
744         // result, if there is a valid 'OS/2' table available use the values from the table if they
745         // aren't too strange.
746         if (sizeof(SkOTTableOS2_V2) <= os2->size()) {
747             const SkOTTableOS2_V2* os2v2 = static_cast<const SkOTTableOS2_V2*>(os2->data());
748             uint16_t xHeight = SkEndian_SwapBE16(os2v2->sxHeight);
749             if (xHeight && xHeight < maxSaneHeight) {
750                 metrics->fXHeight = SkScalarFromCGFloat(xHeight * fontSize / upem);
751             }
752             uint16_t capHeight = SkEndian_SwapBE16(os2v2->sCapHeight);
753             if (capHeight && capHeight < maxSaneHeight) {
754                 metrics->fCapHeight = SkScalarFromCGFloat(capHeight * fontSize / upem);
755             }
756         }
757 
758         // CoreText does not provide the strikeout metrics, which are available in OS/2 version 0.
759         if (sizeof(SkOTTableOS2_V0) <= os2->size()) {
760             const SkOTTableOS2_V0* os2v0 = static_cast<const SkOTTableOS2_V0*>(os2->data());
761             uint16_t strikeoutSize = SkEndian_SwapBE16(os2v0->yStrikeoutSize);
762             if (strikeoutSize && strikeoutSize < maxSaneHeight) {
763                 metrics->fStrikeoutThickness = SkScalarFromCGFloat(strikeoutSize * fontSize / upem);
764                 metrics->fFlags |= SkFontMetrics::kStrikeoutThicknessIsValid_Flag;
765             }
766             uint16_t strikeoutPos = SkEndian_SwapBE16(os2v0->yStrikeoutPosition);
767             if (strikeoutPos && strikeoutPos < maxSaneHeight) {
768                 metrics->fStrikeoutPosition = -SkScalarFromCGFloat(strikeoutPos * fontSize / upem);
769                 metrics->fFlags |= SkFontMetrics::kStrikeoutPositionIsValid_Flag;
770             }
771         }
772     }
773 }
774 
775 #endif
776