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