OLD | NEW |
---|---|
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2006 The Android Open Source Project | 3 * Copyright 2006 The Android Open Source Project |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 #ifdef SK_BUILD_FOR_MAC | 9 #ifdef SK_BUILD_FOR_MAC |
10 #import <ApplicationServices/ApplicationServices.h> | 10 #import <ApplicationServices/ApplicationServices.h> |
11 #endif | 11 #endif |
12 | 12 |
13 #ifdef SK_BUILD_FOR_IOS | 13 #ifdef SK_BUILD_FOR_IOS |
14 #include <CoreText/CoreText.h> | 14 #include <CoreText/CoreText.h> |
15 #include <CoreText/CTFontManager.h> | 15 #include <CoreText/CTFontManager.h> |
16 #include <CoreGraphics/CoreGraphics.h> | 16 #include <CoreGraphics/CoreGraphics.h> |
17 #include <CoreFoundation/CoreFoundation.h> | 17 #include <CoreFoundation/CoreFoundation.h> |
18 #endif | 18 #endif |
19 | 19 |
20 #include "SkFontHost.h" | 20 #include "SkFontHost.h" |
21 #include "SkCGUtils.h" | 21 #include "SkCGUtils.h" |
22 #include "SkColorPriv.h" | 22 #include "SkColorPriv.h" |
23 #include "SkDescriptor.h" | 23 #include "SkDescriptor.h" |
24 #include "SkEndian.h" | 24 #include "SkEndian.h" |
25 #include "SkFontDescriptor.h" | 25 #include "SkFontDescriptor.h" |
26 #include "SkFloatingPoint.h" | 26 #include "SkFloatingPoint.h" |
27 #include "SkGlyph.h" | 27 #include "SkGlyph.h" |
28 #include "SkLazyFnPtr.h" | |
28 #include "SkMaskGamma.h" | 29 #include "SkMaskGamma.h" |
29 #include "SkSFNTHeader.h" | 30 #include "SkSFNTHeader.h" |
30 #include "SkOTTable_glyf.h" | 31 #include "SkOTTable_glyf.h" |
31 #include "SkOTTable_head.h" | 32 #include "SkOTTable_head.h" |
32 #include "SkOTTable_hhea.h" | 33 #include "SkOTTable_hhea.h" |
33 #include "SkOTTable_loca.h" | 34 #include "SkOTTable_loca.h" |
34 #include "SkOTUtils.h" | 35 #include "SkOTUtils.h" |
35 #include "SkPaint.h" | 36 #include "SkPaint.h" |
36 #include "SkPath.h" | 37 #include "SkPath.h" |
37 #include "SkString.h" | 38 #include "SkString.h" |
38 #include "SkStream.h" | 39 #include "SkStream.h" |
39 #include "SkThread.h" | 40 #include "SkThread.h" |
40 #include "SkTypeface_mac.h" | 41 #include "SkTypeface_mac.h" |
41 #include "SkUtils.h" | 42 #include "SkUtils.h" |
42 #include "SkTypefaceCache.h" | 43 #include "SkTypefaceCache.h" |
43 #include "SkFontMgr.h" | 44 #include "SkFontMgr.h" |
44 #include "SkUtils.h" | 45 #include "SkUtils.h" |
45 | 46 |
46 //#define HACK_COLORGLYPHS | 47 #include <dlfcn.h> |
47 | 48 |
48 class SkScalerContext_Mac; | 49 class SkScalerContext_Mac; |
49 | 50 |
50 // CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we | 51 // CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we |
51 // provide a wrapper here that will return an empty array if need be. | 52 // provide a wrapper here that will return an empty array if need be. |
52 static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() { | 53 static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() { |
53 #ifdef SK_BUILD_FOR_IOS | 54 #ifdef SK_BUILD_FOR_IOS |
54 return CFArrayCreate(NULL, NULL, 0, NULL); | 55 return CFArrayCreate(NULL, NULL, 0, NULL); |
55 #else | 56 #else |
56 return CTFontManagerCopyAvailableFontFamilyNames(); | 57 return CTFontManagerCopyAvailableFontFamilyNames(); |
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
266 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy), | 267 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy), |
267 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx), | 268 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx), |
268 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy), | 269 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy), |
269 ScalarToCG(matrix[SkMatrix::kMTransX] * sx), | 270 ScalarToCG(matrix[SkMatrix::kMTransX] * sx), |
270 ScalarToCG(matrix[SkMatrix::kMTransY] * sy)); | 271 ScalarToCG(matrix[SkMatrix::kMTransY] * sy)); |
271 } | 272 } |
272 | 273 |
273 /////////////////////////////////////////////////////////////////////////////// | 274 /////////////////////////////////////////////////////////////////////////////// |
274 | 275 |
275 #define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host) | 276 #define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host) |
276 #define BITMAP_INFO_GRAY (kCGImageAlphaNone) | |
277 | 277 |
278 /** | 278 /** |
279 * There does not appear to be a publicly accessable API for determining if lcd | 279 * There does not appear to be a publicly accessable API for determining if lcd |
280 * font smoothing will be applied if we request it. The main issue is that if | 280 * font smoothing will be applied if we request it. The main issue is that if |
281 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0. | 281 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0. |
282 */ | 282 */ |
283 static bool supports_LCD() { | 283 static bool supports_LCD() { |
284 static int gSupportsLCD = -1; | 284 static int gSupportsLCD = -1; |
285 if (gSupportsLCD >= 0) { | 285 if (gSupportsLCD >= 0) { |
286 return (bool) gSupportsLCD; | 286 return (bool) gSupportsLCD; |
(...skipping 10 matching lines...) Expand all Loading... | |
297 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1); | 297 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1); |
298 uint32_t r = (rgb >> 16) & 0xFF; | 298 uint32_t r = (rgb >> 16) & 0xFF; |
299 uint32_t g = (rgb >> 8) & 0xFF; | 299 uint32_t g = (rgb >> 8) & 0xFF; |
300 uint32_t b = (rgb >> 0) & 0xFF; | 300 uint32_t b = (rgb >> 0) & 0xFF; |
301 gSupportsLCD = (r != g || r != b); | 301 gSupportsLCD = (r != g || r != b); |
302 return (bool) gSupportsLCD; | 302 return (bool) gSupportsLCD; |
303 } | 303 } |
304 | 304 |
305 class Offscreen { | 305 class Offscreen { |
306 public: | 306 public: |
307 Offscreen(); | 307 Offscreen() |
308 : fRGBSpace(NULL) | |
309 , fCG(NULL) | |
310 , fDoAA(false) | |
311 , fDoLCD(false) | |
312 { | |
313 fSize.set(0, 0); | |
314 } | |
308 | 315 |
309 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph, | 316 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph, |
310 CGGlyph glyphID, size_t* rowBytesPtr, | 317 CGGlyph glyphID, size_t* rowBytesPtr, |
311 bool generateA8FromLCD); | 318 bool generateA8FromLCD); |
312 | 319 |
313 private: | 320 private: |
314 enum { | 321 enum { |
315 kSize = 32 * 32 * sizeof(CGRGBPixel) | 322 kSize = 32 * 32 * sizeof(CGRGBPixel) |
316 }; | 323 }; |
317 SkAutoSMalloc<kSize> fImageStorage; | 324 SkAutoSMalloc<kSize> fImageStorage; |
318 AutoCFRelease<CGColorSpaceRef> fRGBSpace; | 325 AutoCFRelease<CGColorSpaceRef> fRGBSpace; |
319 | 326 |
320 // cached state | 327 // cached state |
321 AutoCFRelease<CGContextRef> fCG; | 328 AutoCFRelease<CGContextRef> fCG; |
322 SkISize fSize; | 329 SkISize fSize; |
323 bool fDoAA; | 330 bool fDoAA; |
324 bool fDoLCD; | 331 bool fDoLCD; |
325 | 332 |
326 static int RoundSize(int dimension) { | 333 static int RoundSize(int dimension) { |
327 return SkNextPow2(dimension); | 334 return SkNextPow2(dimension); |
328 } | 335 } |
329 }; | 336 }; |
330 | 337 |
331 Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL), | |
332 fDoAA(false), fDoLCD(false) { | |
333 fSize.set(0, 0); | |
334 } | |
335 | |
336 /////////////////////////////////////////////////////////////////////////////// | 338 /////////////////////////////////////////////////////////////////////////////// |
337 | 339 |
338 static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value ) { | 340 static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value ) { |
339 CFNumberRef num; | 341 CFNumberRef num; |
340 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num) | 342 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num) |
341 && CFNumberIsFloatType(num) | 343 && CFNumberIsFloatType(num) |
342 && CFNumberGetValue(num, kCFNumberFloatType, value); | 344 && CFNumberGetValue(num, kCFNumberFloatType, value); |
343 } | 345 } |
344 | 346 |
345 static int unit_weight_to_fontstyle(float unit) { | 347 static int unit_weight_to_fontstyle(float unit) { |
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
656 | 658 |
657 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down). | 659 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down). |
658 * | 660 * |
659 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs. | 661 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs. |
660 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs. | 662 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs. |
661 */ | 663 */ |
662 SkMatrix fFUnitMatrix; | 664 SkMatrix fFUnitMatrix; |
663 | 665 |
664 Offscreen fOffscreen; | 666 Offscreen fOffscreen; |
665 AutoCFRelease<CTFontRef> fCTFont; | 667 AutoCFRelease<CTFontRef> fCTFont; |
668 CGAffineTransform fInvTransform; | |
666 | 669 |
667 /** Vertical variant of fCTFont. | 670 /** Vertical variant of fCTFont. |
668 * | 671 * |
669 * CT vertical metrics are pre-rotated (in em space, before transform) 90de g clock-wise. | 672 * CT vertical metrics are pre-rotated (in em space, before transform) 90de g clock-wise. |
670 * This makes kCTFontDefaultOrientation dangerous, because the metrics from | 673 * This makes kCTFontDefaultOrientation dangerous, because the metrics from |
671 * kCTFontHorizontalOrientation are in a different space from kCTFontVertic alOrientation. | 674 * kCTFontHorizontalOrientation are in a different space from kCTFontVertic alOrientation. |
672 * Use fCTVerticalFont with kCTFontVerticalOrientation to get metrics in th e same space. | 675 * Use fCTVerticalFont with kCTFontVerticalOrientation to get metrics in th e same space. |
673 */ | 676 */ |
674 AutoCFRelease<CTFontRef> fCTVerticalFont; | 677 AutoCFRelease<CTFontRef> fCTVerticalFont; |
675 | 678 |
(...skipping 18 matching lines...) Expand all Loading... | |
694 , fGeneratedFBoundingBoxes(false) | 697 , fGeneratedFBoundingBoxes(false) |
695 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag)) | 698 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag)) |
696 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag)) | 699 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag)) |
697 | 700 |
698 { | 701 { |
699 CTFontRef ctFont = typeface->fFontRef.get(); | 702 CTFontRef ctFont = typeface->fFontRef.get(); |
700 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont); | 703 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont); |
701 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF); | 704 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF); |
702 fGlyphCount = SkToU16(numGlyphs); | 705 fGlyphCount = SkToU16(numGlyphs); |
703 | 706 |
707 // CT on (at least) 10.9 will size color glyphs down from the requested size , but not up. | |
708 // As a result, it is necessary to know the actual device size and request t hat. | |
709 SkVector scale; | |
704 SkMatrix skTransform; | 710 SkMatrix skTransform; |
705 fRec.getSingleMatrixWithoutTextSize(&skTransform); | 711 fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale, &scale, & skTransform, |
712 NULL, NULL, &fFUnitMatrix); | |
706 CGAffineTransform transform = MatrixToCGAffineTransform(skTransform); | 713 CGAffineTransform transform = MatrixToCGAffineTransform(skTransform); |
714 fInvTransform = CGAffineTransformInvert(transform); | |
707 | 715 |
708 AutoCFRelease<CTFontDescriptorRef> ctFontDesc; | 716 AutoCFRelease<CTFontDescriptorRef> ctFontDesc; |
709 if (fVertical) { | 717 if (fVertical) { |
710 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMut able( | 718 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMut able( |
711 kCFAllocatorDefault, 0, | 719 kCFAllocatorDefault, 0, |
712 &kCFTypeDictionaryKeyCallBacks, | 720 &kCFTypeDictionaryKeyCallBacks, |
713 &kCFTypeDictionaryValueCallBacks)); | 721 &kCFTypeDictionaryValueCallBacks)); |
714 if (cfAttributes) { | 722 if (cfAttributes) { |
715 CTFontOrientation ctOrientation = kCTFontVerticalOrientation; | 723 CTFontOrientation ctOrientation = kCTFontVerticalOrientation; |
716 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate( | 724 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate( |
717 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation)); | 725 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation)); |
718 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVe rtical); | 726 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVe rtical); |
719 ctFontDesc.reset(CTFontDescriptorCreateWithAttributes(cfAttributes)) ; | 727 ctFontDesc.reset(CTFontDescriptorCreateWithAttributes(cfAttributes)) ; |
720 } | 728 } |
721 } | 729 } |
722 | 730 |
723 // The transform contains everything except the requested text size. | 731 // The transform contains everything except the requested text size. |
724 // Some properties, like 'trak', are based on the text size (before applying the matrix). | 732 // Some properties, like 'trak', are based on the text size (before applying the matrix). |
725 CGFloat textSize = ScalarToCG(fRec.fTextSize); | 733 CGFloat textSize = ScalarToCG(scale.y()); |
726 | 734 |
727 // If a text size of 0 is requested, CoreGraphics will use 12 instead. | 735 // If a text size of 0 is requested, CoreGraphics will use 12 instead. |
728 // If the text size is 0, set it to something tiny. | 736 // If the text size is 0, set it to something tiny. |
729 if (textSize < CGFLOAT_MIN) { | 737 if (textSize < CGFLOAT_MIN) { |
730 textSize = CGFLOAT_MIN; | 738 textSize = CGFLOAT_MIN; |
731 } | 739 } |
732 | 740 |
733 fCTFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize, &transform, c tFontDesc)); | 741 fCTFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize, &transform, c tFontDesc)); |
734 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont, NULL)); | 742 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont, NULL)); |
735 if (fVertical) { | 743 if (fVertical) { |
736 CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0); | 744 CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0); |
737 transform = CGAffineTransformConcat(rotateLeft, transform); | 745 transform = CGAffineTransformConcat(rotateLeft, transform); |
738 fCTVerticalFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize, & transform, NULL)); | 746 fCTVerticalFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize, & transform, NULL)); |
739 } | 747 } |
740 | 748 |
741 // The fUnitMatrix includes the text size (and em) as it is used to scale th e raw font data. | 749 // The fUnitMatrix includes the text size (and em) as it is used to scale th e raw font data. |
742 fRec.getSingleMatrix(&fFUnitMatrix); | |
743 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFo nt))); | 750 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFo nt))); |
744 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit); | 751 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit); |
745 } | 752 } |
746 | 753 |
754 extern "C" { | |
755 | |
756 /** CTFontDrawGlyphs was introduced in 10.7. */ | |
757 typedef void (*CTFontDrawGlyphsProc)(CTFontRef, const CGGlyph[], const CGPoint[] , | |
758 size_t, CGContextRef); | |
759 | |
760 /** This is an implementation of CTFontDrawGlyphs for 10.6. */ | |
761 static void sk_legacy_CTFontDrawGlyphs(CTFontRef, const CGGlyph glyphs[], const CGPoint points[], | |
762 size_t count, CGContextRef cg) | |
763 { | |
764 CGContextShowGlyphsAtPositions(cg, glyphs, points, count); | |
765 } | |
766 | |
767 } | |
768 | |
769 CTFontDrawGlyphsProc SkChooseCTFontDrawGlyphs() { | |
770 CTFontDrawGlyphsProc realCTFontDrawGlyphs; | |
771 *reinterpret_cast<void**>(&realCTFontDrawGlyphs) = dlsym(RTLD_DEFAULT, "CTFo ntDrawGlyphs"); | |
772 return realCTFontDrawGlyphs ? realCTFontDrawGlyphs : sk_legacy_CTFontDrawGly phs; | |
773 }; | |
774 | |
775 SK_DECLARE_STATIC_LAZY_FN_PTR(CTFontDrawGlyphsProc, gCTFontDrawGlyphs, SkChooseC TFontDrawGlyphs); | |
776 | |
747 CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph, | 777 CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph, |
748 CGGlyph glyphID, size_t* rowBytesPtr, | 778 CGGlyph glyphID, size_t* rowBytesPtr, |
749 bool generateA8FromLCD) { | 779 bool generateA8FromLCD) |
780 { | |
781 CTFontDrawGlyphsProc ctFontDrawGlyphs = gCTFontDrawGlyphs.get(); | |
782 | |
750 if (!fRGBSpace) { | 783 if (!fRGBSpace) { |
751 //It doesn't appear to matter what color space is specified. | 784 //It doesn't appear to matter what color space is specified. |
752 //Regular blends and antialiased text are always (s*a + d*(1-a)) | 785 //Regular blends and antialiased text are always (s*a + d*(1-a)) |
753 //and smoothed text is always g=2.0. | 786 //and smoothed text is always g=2.0. |
754 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB()); | 787 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB()); |
755 } | 788 } |
756 | 789 |
757 // default to kBW_Format | 790 // default to kBW_Format |
758 bool doAA = false; | 791 bool doAA = false; |
759 bool doLCD = false; | 792 bool doLCD = false; |
760 | 793 |
761 if (SkMask::kBW_Format != glyph.fMaskFormat) { | 794 if (SkMask::kBW_Format != glyph.fMaskFormat) { |
762 doLCD = true; | 795 doLCD = true; |
763 doAA = true; | 796 doAA = true; |
764 } | 797 } |
765 | 798 |
766 // FIXME: lcd smoothed un-hinted rasterization unsupported. | 799 // FIXME: lcd smoothed un-hinted rasterization unsupported. |
767 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) { | 800 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) { |
768 doLCD = false; | 801 doLCD = false; |
769 doAA = true; | 802 doAA = true; |
770 } | 803 } |
771 | 804 |
805 // If this font might have color glyphs, disable LCD as there's no way to su pport it. | |
806 // CoreText doesn't tell us which format it ended up using, so we can't dete ct it. | |
807 // A8 will be ugly too (white on transparent), but TODO: we can detect gray and set to A8. | |
808 if (SkMask::kARGB32_Format == glyph.fMaskFormat) { | |
809 doLCD = false; | |
810 } | |
811 | |
772 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel); | 812 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel); |
773 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) { | 813 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) { |
774 if (fSize.fWidth < glyph.fWidth) { | 814 if (fSize.fWidth < glyph.fWidth) { |
775 fSize.fWidth = RoundSize(glyph.fWidth); | 815 fSize.fWidth = RoundSize(glyph.fWidth); |
776 } | 816 } |
777 if (fSize.fHeight < glyph.fHeight) { | 817 if (fSize.fHeight < glyph.fHeight) { |
778 fSize.fHeight = RoundSize(glyph.fHeight); | 818 fSize.fHeight = RoundSize(glyph.fHeight); |
779 } | 819 } |
780 | 820 |
781 rowBytes = fSize.fWidth * sizeof(CGRGBPixel); | 821 rowBytes = fSize.fWidth * sizeof(CGRGBPixel); |
782 void* image = fImageStorage.reset(rowBytes * fSize.fHeight); | 822 void* image = fImageStorage.reset(rowBytes * fSize.fHeight); |
823 const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFor mat) | |
824 ? kCGImageAlphaPremultipliedFirst | |
825 : kCGImageAlphaNoneSkipFirst; | |
826 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha; | |
783 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8, | 827 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8, |
784 rowBytes, fRGBSpace, BITMAP_INFO_RGB)); | 828 rowBytes, fRGBSpace, bitmapInfo)); |
785 | 829 |
786 // skia handles quantization itself, so we disable this for cg to get | 830 // Skia handles quantization and subpixel positioning, |
787 // full fractional data from them. | 831 // so disable quantization and enabe subpixel positioning in CG. |
788 CGContextSetAllowsFontSubpixelQuantization(fCG, false); | 832 CGContextSetAllowsFontSubpixelQuantization(fCG, false); |
789 CGContextSetShouldSubpixelQuantizeFonts(fCG, false); | 833 CGContextSetShouldSubpixelQuantizeFonts(fCG, false); |
790 | 834 |
791 CGContextSetTextDrawingMode(fCG, kCGTextFill); | |
792 CGContextSetFont(fCG, context.fCGFont); | |
793 CGContextSetFontSize(fCG, CTFontGetSize(context.fCTFont)); | |
794 CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont)); | |
795 | |
796 // Because CG always draws from the horizontal baseline, | 835 // Because CG always draws from the horizontal baseline, |
797 // if there is a non-integral translation from the horizontal origin to the vertical origin, | 836 // if there is a non-integral translation from the horizontal origin to the vertical origin, |
798 // then CG cannot draw the glyph in the correct location without subpixe l positioning. | 837 // then CG cannot draw the glyph in the correct location without subpixe l positioning. |
799 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition || context.fVertical); | 838 CGContextSetAllowsFontSubpixelPositioning(fCG, true); |
800 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition || c ontext.fVertical); | 839 CGContextSetShouldSubpixelPositionFonts(fCG, true); |
840 | |
841 CGContextSetTextDrawingMode(fCG, kCGTextFill); | |
801 | 842 |
802 // Draw white on black to create mask. | 843 // Draw white on black to create mask. |
803 // TODO: Draw black on white and invert, CG has a special case codepath. | 844 // TODO: Draw black on white and invert, CG has a special case codepath. |
804 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f); | 845 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f); |
805 | 846 |
806 // force our checks below to happen | 847 // force our checks below to happen |
807 fDoAA = !doAA; | 848 fDoAA = !doAA; |
808 fDoLCD = !doLCD; | 849 fDoLCD = !doLCD; |
850 | |
851 if (sk_legacy_CTFontDrawGlyphs == ctFontDrawGlyphs) { | |
852 // CTFontDrawGlyphs will apply the font, font size, and font matrix to the CGContext. | |
853 // Our 'fake' one does not, so set up the CGContext here. | |
854 CGContextSetFont(fCG, context.fCGFont); | |
855 CGContextSetFontSize(fCG, CTFontGetSize(context.fCTFont)); | |
856 CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont)); | |
857 } | |
809 } | 858 } |
810 | 859 |
811 if (fDoAA != doAA) { | 860 if (fDoAA != doAA) { |
812 CGContextSetShouldAntialias(fCG, doAA); | 861 CGContextSetShouldAntialias(fCG, doAA); |
813 fDoAA = doAA; | 862 fDoAA = doAA; |
814 } | 863 } |
815 if (fDoLCD != doLCD) { | 864 if (fDoLCD != doLCD) { |
816 CGContextSetShouldSmoothFonts(fCG, doLCD); | 865 CGContextSetShouldSmoothFonts(fCG, doLCD); |
817 fDoLCD = doLCD; | 866 fDoLCD = doLCD; |
818 } | 867 } |
819 | 868 |
820 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get(); | 869 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get(); |
821 // skip rows based on the glyph's height | 870 // skip rows based on the glyph's height |
822 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth; | 871 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth; |
823 | 872 |
824 // erase to black | 873 // erase to black |
825 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes); | 874 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes); |
826 | 875 |
827 float subX = 0; | 876 float subX = 0; |
828 float subY = 0; | 877 float subY = 0; |
829 if (context.fDoSubPosition) { | 878 if (context.fDoSubPosition) { |
830 subX = SkFixedToFloat(glyph.getSubXFixed()); | 879 subX = SkFixedToFloat(glyph.getSubXFixed()); |
831 subY = SkFixedToFloat(glyph.getSubYFixed()); | 880 subY = SkFixedToFloat(glyph.getSubYFixed()); |
832 } | 881 } |
833 | 882 |
834 // CGContextShowGlyphsAtPoint always draws using the horizontal baseline ori gin. | 883 // CoreText and CoreGraphics always draw using the horizontal baseline origi n. |
835 if (context.fVertical) { | 884 if (context.fVertical) { |
836 SkPoint offset; | 885 SkPoint offset; |
837 context.getVerticalOffset(glyphID, &offset); | 886 context.getVerticalOffset(glyphID, &offset); |
838 subX += offset.fX; | 887 subX += offset.fX; |
839 subY += offset.fY; | 888 subY += offset.fY; |
840 } | 889 } |
841 | 890 |
842 CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX, | 891 // CTFontDrawGlyphs and CGContextShowGlyphsAtPositions take 'positions' whic h are in text space. |
843 glyph.fTop + glyph.fHeight - subY, | 892 // The glyph location (in device space) must be mapped into text space, so t hat CG can convert |
844 &glyphID, 1); | 893 // it back into device space. |
894 CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY); | |
895 point = CGPointApplyAffineTransform(point, context.fInvTransform); | |
896 ctFontDrawGlyphs(context.fCTFont, &glyphID, &point, 1, fCG); | |
845 | 897 |
846 SkASSERT(rowBytesPtr); | 898 SkASSERT(rowBytesPtr); |
847 *rowBytesPtr = rowBytes; | 899 *rowBytesPtr = rowBytes; |
848 return image; | 900 return image; |
849 } | 901 } |
850 | 902 |
851 void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) co nst { | 903 void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) co nst { |
852 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up). | 904 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up). |
853 // Lion and Leopard return cgVertOffset in CG units (pixels, y up). | 905 // Lion and Leopard return cgVertOffset in CG units (pixels, y up). |
854 CGSize cgVertOffset; | 906 CGSize cgVertOffset; |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1038 skBounds.roundOut(&skIBounds); | 1090 skBounds.roundOut(&skIBounds); |
1039 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing. | 1091 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing. |
1040 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset | 1092 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset |
1041 // is not currently known, as CG dilates the outlines by some percentage. | 1093 // is not currently known, as CG dilates the outlines by some percentage. |
1042 // Note that if this context is A8 and not back-forming from LCD, there is n o need to outset. | 1094 // Note that if this context is A8 and not back-forming from LCD, there is n o need to outset. |
1043 skIBounds.outset(1, 1); | 1095 skIBounds.outset(1, 1); |
1044 glyph->fLeft = SkToS16(skIBounds.fLeft); | 1096 glyph->fLeft = SkToS16(skIBounds.fLeft); |
1045 glyph->fTop = SkToS16(skIBounds.fTop); | 1097 glyph->fTop = SkToS16(skIBounds.fTop); |
1046 glyph->fWidth = SkToU16(skIBounds.width()); | 1098 glyph->fWidth = SkToU16(skIBounds.width()); |
1047 glyph->fHeight = SkToU16(skIBounds.height()); | 1099 glyph->fHeight = SkToU16(skIBounds.height()); |
1048 | |
1049 #ifdef HACK_COLORGLYPHS | |
1050 glyph->fMaskFormat = SkMask::kARGB32_Format; | |
1051 #endif | |
1052 } | 1100 } |
1053 | 1101 |
1054 #include "SkColorPriv.h" | 1102 #include "SkColorPriv.h" |
1055 | 1103 |
1056 static void build_power_table(uint8_t table[], float ee) { | 1104 static void build_power_table(uint8_t table[], float ee) { |
1057 for (int i = 0; i < 256; i++) { | 1105 for (int i = 0; i < 256; i++) { |
1058 float x = i / 255.f; | 1106 float x = i / 255.f; |
1059 x = sk_float_pow(x, ee); | 1107 x = sk_float_pow(x, ee); |
1060 int xx = SkScalarRoundToInt(x * 255); | 1108 int xx = SkScalarRoundToInt(x * 255); |
1061 table[i] = SkToU8(xx); | 1109 table[i] = SkToU8(xx); |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1133 | 1181 |
1134 for (int y = 0; y < glyph.fHeight; y++) { | 1182 for (int y = 0; y < glyph.fHeight; y++) { |
1135 for (int i = 0; i < width; i++) { | 1183 for (int i = 0; i < width; i++) { |
1136 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, t ableB); | 1184 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, t ableB); |
1137 } | 1185 } |
1138 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); | 1186 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
1139 dst = (uint16_t*)((char*)dst + dstRB); | 1187 dst = (uint16_t*)((char*)dst + dstRB); |
1140 } | 1188 } |
1141 } | 1189 } |
1142 | 1190 |
1143 #ifdef HACK_COLORGLYPHS | 1191 static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) { |
1144 // hack to colorize the output for testing kARGB32_Format | 1192 U8CPU a = (rgb >> 24) & 0xFF; |
1145 static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb, const SkGlyph& glyph, | |
1146 int x, int y) { | |
1147 U8CPU r = (rgb >> 16) & 0xFF; | 1193 U8CPU r = (rgb >> 16) & 0xFF; |
1148 U8CPU g = (rgb >> 8) & 0xFF; | 1194 U8CPU g = (rgb >> 8) & 0xFF; |
1149 U8CPU b = (rgb >> 0) & 0xFF; | 1195 U8CPU b = (rgb >> 0) & 0xFF; |
1150 unsigned a = SkComputeLuminance(r, g, b); | |
1151 | 1196 |
1152 // compute gradient from x,y | 1197 return SkPackARGB32(a, r, g, b); |
1153 r = x * 255 / glyph.fWidth; | |
1154 g = 0; | |
1155 b = (glyph.fHeight - y) * 255 / glyph.fHeight; | |
1156 return SkPreMultiplyARGB(a, r, g, b); // red | |
1157 } | 1198 } |
1158 #endif | |
1159 | 1199 |
1160 template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) { | 1200 template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) { |
1161 return (T*)((char*)ptr + byteOffset); | 1201 return (T*)((char*)ptr + byteOffset); |
1162 } | 1202 } |
1163 | 1203 |
1164 void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) { | 1204 void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) { |
1165 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(); | 1205 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(); |
1166 | 1206 |
1167 // FIXME: lcd smoothed un-hinted rasterization unsupported. | 1207 // FIXME: lcd smoothed un-hinted rasterization unsupported. |
1168 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting; | 1208 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting; |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1221 case SkMask::kBW_Format: { | 1261 case SkMask::kBW_Format: { |
1222 const int width = glyph.fWidth; | 1262 const int width = glyph.fWidth; |
1223 size_t dstRB = glyph.rowBytes(); | 1263 size_t dstRB = glyph.rowBytes(); |
1224 uint8_t* dst = (uint8_t*)glyph.fImage; | 1264 uint8_t* dst = (uint8_t*)glyph.fImage; |
1225 for (int y = 0; y < glyph.fHeight; y++) { | 1265 for (int y = 0; y < glyph.fHeight; y++) { |
1226 cgpixels_to_bits(dst, cgPixels, width); | 1266 cgpixels_to_bits(dst, cgPixels, width); |
1227 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); | 1267 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
1228 dst += dstRB; | 1268 dst += dstRB; |
1229 } | 1269 } |
1230 } break; | 1270 } break; |
1231 #ifdef HACK_COLORGLYPHS | |
1232 case SkMask::kARGB32_Format: { | 1271 case SkMask::kARGB32_Format: { |
1233 const int width = glyph.fWidth; | 1272 const int width = glyph.fWidth; |
1234 size_t dstRB = glyph.rowBytes(); | 1273 size_t dstRB = glyph.rowBytes(); |
1235 SkPMColor* dst = (SkPMColor*)glyph.fImage; | 1274 SkPMColor* dst = (SkPMColor*)glyph.fImage; |
1236 for (int y = 0; y < glyph.fHeight; y++) { | 1275 for (int y = 0; y < glyph.fHeight; y++) { |
1237 for (int x = 0; x < width; ++x) { | 1276 for (int x = 0; x < width; ++x) { |
1238 dst[x] = cgpixels_to_pmcolor(cgPixels[x], glyph, x, y); | 1277 dst[x] = cgpixels_to_pmcolor(cgPixels[x]); |
1239 } | 1278 } |
1240 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); | 1279 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
1241 dst = (SkPMColor*)((char*)dst + dstRB); | 1280 dst = (SkPMColor*)((char*)dst + dstRB); |
1242 } | 1281 } |
1243 } break; | 1282 } break; |
1244 #endif | |
1245 default: | 1283 default: |
1246 SkDEBUGFAIL("unexpected mask format"); | 1284 SkDEBUGFAIL("unexpected mask format"); |
1247 break; | 1285 break; |
1248 } | 1286 } |
1249 } | 1287 } |
1250 | 1288 |
1251 /* | 1289 /* |
1252 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4 | 1290 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4 |
1253 * seems sufficient, and possibly even correct, to allow the hinted outline | 1291 * seems sufficient, and possibly even correct, to allow the hinted outline |
1254 * to be subpixel positioned. | 1292 * to be subpixel positioned. |
(...skipping 566 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1821 if (isLCDFormat(rec->fMaskFormat)) { | 1859 if (isLCDFormat(rec->fMaskFormat)) { |
1822 if (lcdSupport) { | 1860 if (lcdSupport) { |
1823 //CoreGraphics creates 555 masks for smoothed text anyway. | 1861 //CoreGraphics creates 555 masks for smoothed text anyway. |
1824 rec->fMaskFormat = SkMask::kLCD16_Format; | 1862 rec->fMaskFormat = SkMask::kLCD16_Format; |
1825 rec->setHinting(SkPaint::kNormal_Hinting); | 1863 rec->setHinting(SkPaint::kNormal_Hinting); |
1826 } else { | 1864 } else { |
1827 rec->fMaskFormat = SkMask::kA8_Format; | 1865 rec->fMaskFormat = SkMask::kA8_Format; |
1828 } | 1866 } |
1829 } | 1867 } |
1830 | 1868 |
1869 // CoreText provides no information as to whether a glyph will be color or n ot. | |
1870 // Fonts may mix outlines and bitmaps, so information is needed on a glyph b y glyph basis. | |
1871 // If a font contains an 'sbix' table, consider it to be a color font, and d isable lcd. | |
1872 AutoCFRelease<CFArrayRef> tags(CTFontCopyAvailableTables(fFontRef,kCTFontTab leOptionNoOptions)); | |
reed1
2014/12/08 21:24:44
should we lazily do this and cache the answer in t
bungeman-skia
2014/12/08 21:33:22
This is onFilterRec, so it is being cached in the
| |
1873 if (tags) { | |
1874 int count = SkToInt(CFArrayGetCount(tags)); | |
1875 for (int i = 0; i < count; ++i) { | |
1876 uintptr_t tag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(t ags, i)); | |
1877 if ('sbix' == tag) { | |
1878 //probably color | |
1879 rec->fMaskFormat = SkMask::kARGB32_Format; | |
1880 break; | |
1881 } | |
1882 } | |
1883 } | |
1884 | |
1831 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMM A_APPLY_TO_A8. | 1885 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMM A_APPLY_TO_A8. |
1832 // All other masks can use regular gamma. | 1886 // All other masks can use regular gamma. |
1833 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hintin g) { | 1887 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hintin g) { |
1834 #ifndef SK_GAMMA_APPLY_TO_A8 | 1888 #ifndef SK_GAMMA_APPLY_TO_A8 |
1835 rec->ignorePreBlend(); | 1889 rec->ignorePreBlend(); |
1836 #endif | 1890 #endif |
1837 } else { | 1891 } else { |
1838 //CoreGraphics dialates smoothed text as needed. | 1892 //CoreGraphics dialates smoothed text as needed. |
1839 rec->setContrast(0); | 1893 rec->setContrast(0); |
1840 } | 1894 } |
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2211 } | 2265 } |
2212 return face; | 2266 return face; |
2213 } | 2267 } |
2214 }; | 2268 }; |
2215 | 2269 |
2216 /////////////////////////////////////////////////////////////////////////////// | 2270 /////////////////////////////////////////////////////////////////////////////// |
2217 | 2271 |
2218 SkFontMgr* SkFontMgr::Factory() { | 2272 SkFontMgr* SkFontMgr::Factory() { |
2219 return SkNEW(SkFontMgr_Mac); | 2273 return SkNEW(SkFontMgr_Mac); |
2220 } | 2274 } |
OLD | NEW |