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> |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
108 { } | 108 { } |
109 | 109 |
110 const T* operator->() const { return fData; } | 110 const T* operator->() const { return fData; } |
111 | 111 |
112 private: | 112 private: |
113 AutoCFRelease<CFDataRef> fCFData; | 113 AutoCFRelease<CFDataRef> fCFData; |
114 public: | 114 public: |
115 const T* fData; | 115 const T* fData; |
116 }; | 116 }; |
117 | 117 |
118 static bool CTFontIsAppleColorEmoji(CTFontRef font) { | |
119 AutoCFRelease<CFStringRef> name(CTFontCopyFamilyName(font)); | |
120 return CFStringCompare(name.get(), CFSTR("Apple Color Emoji"), 0) == kCFComp areEqualTo; | |
bungeman-skia
2014/12/04 14:47:14
No way we're doing this (testing by name).
| |
121 } | |
122 | |
118 // inline versions of these rect helpers | 123 // inline versions of these rect helpers |
119 | 124 |
120 static bool CGRectIsEmpty_inline(const CGRect& rect) { | 125 static bool CGRectIsEmpty_inline(const CGRect& rect) { |
121 return rect.size.width <= 0 || rect.size.height <= 0; | 126 return rect.size.width <= 0 || rect.size.height <= 0; |
122 } | 127 } |
123 | 128 |
124 static CGFloat CGRectGetMinX_inline(const CGRect& rect) { | 129 static CGFloat CGRectGetMinX_inline(const CGRect& rect) { |
125 return rect.origin.x; | 130 return rect.origin.x; |
126 } | 131 } |
127 | 132 |
(...skipping 545 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
673 */ | 678 */ |
674 AutoCFRelease<CTFontRef> fCTVerticalFont; | 679 AutoCFRelease<CTFontRef> fCTVerticalFont; |
675 | 680 |
676 AutoCFRelease<CGFontRef> fCGFont; | 681 AutoCFRelease<CGFontRef> fCGFont; |
677 SkAutoTMalloc<GlyphRect> fFBoundingBoxes; | 682 SkAutoTMalloc<GlyphRect> fFBoundingBoxes; |
678 uint16_t fFBoundingBoxesGlyphOffset; | 683 uint16_t fFBoundingBoxesGlyphOffset; |
679 uint16_t fGlyphCount; | 684 uint16_t fGlyphCount; |
680 bool fGeneratedFBoundingBoxes; | 685 bool fGeneratedFBoundingBoxes; |
681 const bool fDoSubPosition; | 686 const bool fDoSubPosition; |
682 const bool fVertical; | 687 const bool fVertical; |
688 bool fIsColorEmoji; | |
683 | 689 |
684 friend class Offscreen; | 690 friend class Offscreen; |
685 | 691 |
686 typedef SkScalerContext INHERITED; | 692 typedef SkScalerContext INHERITED; |
687 }; | 693 }; |
688 | 694 |
689 SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface, | 695 SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface, |
690 const SkDescriptor* desc) | 696 const SkDescriptor* desc) |
691 : INHERITED(typeface, desc) | 697 : INHERITED(typeface, desc) |
692 , fFBoundingBoxes() | 698 , fFBoundingBoxes() |
693 , fFBoundingBoxesGlyphOffset(0) | 699 , fFBoundingBoxesGlyphOffset(0) |
694 , fGeneratedFBoundingBoxes(false) | 700 , fGeneratedFBoundingBoxes(false) |
695 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag)) | 701 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag)) |
696 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag)) | 702 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag)) |
697 | 703 |
698 { | 704 { |
699 CTFontRef ctFont = typeface->fFontRef.get(); | 705 CTFontRef ctFont = typeface->fFontRef.get(); |
700 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont); | 706 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont); |
701 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF); | 707 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF); |
702 fGlyphCount = SkToU16(numGlyphs); | 708 fGlyphCount = SkToU16(numGlyphs); |
709 fIsColorEmoji = CTFontIsAppleColorEmoji(ctFont); | |
703 | 710 |
704 SkMatrix skTransform; | 711 SkMatrix skTransform; |
705 fRec.getSingleMatrixWithoutTextSize(&skTransform); | 712 fRec.getSingleMatrixWithoutTextSize(&skTransform); |
706 CGAffineTransform transform = MatrixToCGAffineTransform(skTransform); | 713 CGAffineTransform transform = MatrixToCGAffineTransform(skTransform); |
707 | 714 |
708 AutoCFRelease<CTFontDescriptorRef> ctFontDesc; | 715 AutoCFRelease<CTFontDescriptorRef> ctFontDesc; |
709 if (fVertical) { | 716 if (fVertical) { |
710 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMut able( | 717 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMut able( |
711 kCFAllocatorDefault, 0, | 718 kCFAllocatorDefault, 0, |
712 &kCFTypeDictionaryKeyCallBacks, | 719 &kCFTypeDictionaryKeyCallBacks, |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
781 rowBytes = fSize.fWidth * sizeof(CGRGBPixel); | 788 rowBytes = fSize.fWidth * sizeof(CGRGBPixel); |
782 void* image = fImageStorage.reset(rowBytes * fSize.fHeight); | 789 void* image = fImageStorage.reset(rowBytes * fSize.fHeight); |
783 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8, | 790 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8, |
784 rowBytes, fRGBSpace, BITMAP_INFO_RGB)); | 791 rowBytes, fRGBSpace, BITMAP_INFO_RGB)); |
785 | 792 |
786 // skia handles quantization itself, so we disable this for cg to get | 793 // skia handles quantization itself, so we disable this for cg to get |
787 // full fractional data from them. | 794 // full fractional data from them. |
788 CGContextSetAllowsFontSubpixelQuantization(fCG, false); | 795 CGContextSetAllowsFontSubpixelQuantization(fCG, false); |
789 CGContextSetShouldSubpixelQuantizeFonts(fCG, false); | 796 CGContextSetShouldSubpixelQuantizeFonts(fCG, false); |
790 | 797 |
791 CGContextSetTextDrawingMode(fCG, kCGTextFill); | 798 if (!context.fIsColorEmoji) { |
792 CGContextSetFont(fCG, context.fCGFont); | 799 CGContextSetTextDrawingMode(fCG, kCGTextFill); |
793 CGContextSetFontSize(fCG, CTFontGetSize(context.fCTFont)); | 800 CGContextSetFont(fCG, context.fCGFont); |
794 CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont)); | 801 CGContextSetFontSize(fCG, CTFontGetSize(context.fCTFont)); |
802 CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont)); | |
803 } | |
795 | 804 |
796 // Because CG always draws from the horizontal baseline, | 805 // Because CG always draws from the horizontal baseline, |
797 // if there is a non-integral translation from the horizontal origin to the vertical origin, | 806 // 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. | 807 // then CG cannot draw the glyph in the correct location without subpixe l positioning. |
799 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition || context.fVertical); | 808 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition || context.fVertical); |
800 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition || c ontext.fVertical); | 809 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition || c ontext.fVertical); |
801 | 810 |
802 // Draw white on black to create mask. | 811 // Draw white on black to create mask. |
803 // TODO: Draw black on white and invert, CG has a special case codepath. | 812 // TODO: Draw black on white and invert, CG has a special case codepath. |
804 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f); | 813 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f); |
805 | 814 |
806 // force our checks below to happen | 815 // force our checks below to happen |
807 fDoAA = !doAA; | 816 fDoAA = !doAA; |
808 fDoLCD = !doLCD; | 817 fDoLCD = !doLCD; |
809 } | 818 } |
810 | 819 |
811 if (fDoAA != doAA) { | 820 if (fDoAA != doAA) { |
812 CGContextSetShouldAntialias(fCG, doAA); | 821 CGContextSetShouldAntialias(fCG, doAA); |
813 fDoAA = doAA; | 822 fDoAA = doAA; |
814 } | 823 } |
815 if (fDoLCD != doLCD) { | 824 if (fDoLCD != doLCD) { |
816 CGContextSetShouldSmoothFonts(fCG, doLCD); | 825 CGContextSetShouldSmoothFonts(fCG, doLCD); |
817 fDoLCD = doLCD; | 826 fDoLCD = doLCD; |
818 } | 827 } |
819 | 828 |
820 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get(); | 829 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get(); |
821 // skip rows based on the glyph's height | 830 // skip rows based on the glyph's height |
822 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth; | 831 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth; |
823 | 832 |
824 // erase to black | 833 // erase to black or white. |
825 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes); | 834 sk_memset_rect32(image, context.fIsColorEmoji ? 0xFFFFFFFF : 0, glyph.fWidth , glyph.fHeight, rowBytes); |
bungeman-skia
2014/12/04 14:47:13
We cannot make the background opaque.
| |
826 | 835 |
827 float subX = 0; | 836 float subX = 0; |
828 float subY = 0; | 837 float subY = 0; |
829 if (context.fDoSubPosition) { | 838 if (context.fDoSubPosition) { |
830 subX = SkFixedToFloat(glyph.getSubXFixed()); | 839 subX = SkFixedToFloat(glyph.getSubXFixed()); |
831 subY = SkFixedToFloat(glyph.getSubYFixed()); | 840 subY = SkFixedToFloat(glyph.getSubYFixed()); |
832 } | 841 } |
833 | 842 |
834 // CGContextShowGlyphsAtPoint always draws using the horizontal baseline ori gin. | 843 // CGContextShowGlyphsAtPoint always draws using the horizontal baseline ori gin. |
835 if (context.fVertical) { | 844 if (context.fVertical) { |
836 SkPoint offset; | 845 SkPoint offset; |
837 context.getVerticalOffset(glyphID, &offset); | 846 context.getVerticalOffset(glyphID, &offset); |
838 subX += offset.fX; | 847 subX += offset.fX; |
839 subY += offset.fY; | 848 subY += offset.fY; |
840 } | 849 } |
841 | 850 |
842 CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX, | 851 CGPoint pos = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY); |
843 glyph.fTop + glyph.fHeight - subY, | 852 if (context.fIsColorEmoji) { |
844 &glyphID, 1); | 853 CGContextSaveGState(fCG); |
854 // CGContextSetTextMatrix does not work with color glyphs, so we use the | |
bungeman-skia
2014/12/04 14:47:13
This isn't quite true, what happens is CoreText wo
| |
855 // CTM instead. This means we must translate the CTM as well, to set the | |
856 // glyph position, instead of using CGContextSetTextPosition. | |
857 CGContextConcatCTM(fCG, CTFontGetMatrix(context.fCTFont)); | |
858 CGContextTranslateCTM(fCG, pos.x, pos.y); | |
859 CTFontDrawGlyphs(context.fCTFont, &glyphID, &CGPointZero, 1, fCG); | |
860 CGContextRestoreGState(fCG); | |
861 } else { | |
862 CGContextShowGlyphsAtPoint(fCG, pos.x, pos.y, &glyphID, 1); | |
863 } | |
845 | 864 |
846 SkASSERT(rowBytesPtr); | 865 SkASSERT(rowBytesPtr); |
847 *rowBytesPtr = rowBytes; | 866 *rowBytesPtr = rowBytes; |
848 return image; | 867 return image; |
849 } | 868 } |
850 | 869 |
851 void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) co nst { | 870 void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) co nst { |
852 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up). | 871 // 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). | 872 // Lion and Leopard return cgVertOffset in CG units (pixels, y up). |
854 CGSize cgVertOffset; | 873 CGSize cgVertOffset; |
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1133 | 1152 |
1134 for (int y = 0; y < glyph.fHeight; y++) { | 1153 for (int y = 0; y < glyph.fHeight; y++) { |
1135 for (int i = 0; i < width; i++) { | 1154 for (int i = 0; i < width; i++) { |
1136 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, t ableB); | 1155 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, t ableB); |
1137 } | 1156 } |
1138 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); | 1157 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
1139 dst = (uint16_t*)((char*)dst + dstRB); | 1158 dst = (uint16_t*)((char*)dst + dstRB); |
1140 } | 1159 } |
1141 } | 1160 } |
1142 | 1161 |
1143 #ifdef HACK_COLORGLYPHS | 1162 static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) { |
1144 // hack to colorize the output for testing kARGB32_Format | |
1145 static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb, const SkGlyph& glyph, | |
1146 int x, int y) { | |
1147 U8CPU r = (rgb >> 16) & 0xFF; | 1163 U8CPU r = (rgb >> 16) & 0xFF; |
1148 U8CPU g = (rgb >> 8) & 0xFF; | 1164 U8CPU g = (rgb >> 8) & 0xFF; |
1149 U8CPU b = (rgb >> 0) & 0xFF; | 1165 U8CPU b = (rgb >> 0) & 0xFF; |
1150 unsigned a = SkComputeLuminance(r, g, b); | 1166 return SkPreMultiplyARGB(0xFF, r, g, b); // red |
bungeman-skia
2014/12/04 14:47:13
We cannot make these opaque, transparency must be
| |
1151 | |
1152 // compute gradient from x,y | |
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 } | 1167 } |
1158 #endif | |
1159 | 1168 |
1160 template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) { | 1169 template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) { |
1161 return (T*)((char*)ptr + byteOffset); | 1170 return (T*)((char*)ptr + byteOffset); |
1162 } | 1171 } |
1163 | 1172 |
1164 void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) { | 1173 void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) { |
1165 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(); | 1174 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(); |
1166 | 1175 |
1167 // FIXME: lcd smoothed un-hinted rasterization unsupported. | 1176 // FIXME: lcd smoothed un-hinted rasterization unsupported. |
1168 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting; | 1177 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: { | 1230 case SkMask::kBW_Format: { |
1222 const int width = glyph.fWidth; | 1231 const int width = glyph.fWidth; |
1223 size_t dstRB = glyph.rowBytes(); | 1232 size_t dstRB = glyph.rowBytes(); |
1224 uint8_t* dst = (uint8_t*)glyph.fImage; | 1233 uint8_t* dst = (uint8_t*)glyph.fImage; |
1225 for (int y = 0; y < glyph.fHeight; y++) { | 1234 for (int y = 0; y < glyph.fHeight; y++) { |
1226 cgpixels_to_bits(dst, cgPixels, width); | 1235 cgpixels_to_bits(dst, cgPixels, width); |
1227 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); | 1236 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
1228 dst += dstRB; | 1237 dst += dstRB; |
1229 } | 1238 } |
1230 } break; | 1239 } break; |
1231 #ifdef HACK_COLORGLYPHS | |
1232 case SkMask::kARGB32_Format: { | 1240 case SkMask::kARGB32_Format: { |
1233 const int width = glyph.fWidth; | 1241 const int width = glyph.fWidth; |
1234 size_t dstRB = glyph.rowBytes(); | 1242 size_t dstRB = glyph.rowBytes(); |
1235 SkPMColor* dst = (SkPMColor*)glyph.fImage; | 1243 SkPMColor* dst = (SkPMColor*)glyph.fImage; |
1236 for (int y = 0; y < glyph.fHeight; y++) { | 1244 for (int y = 0; y < glyph.fHeight; y++) { |
1237 for (int x = 0; x < width; ++x) { | 1245 for (int x = 0; x < width; ++x) { |
1238 dst[x] = cgpixels_to_pmcolor(cgPixels[x], glyph, x, y); | 1246 dst[x] = cgpixels_to_pmcolor(cgPixels[x]); |
1239 } | 1247 } |
1240 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); | 1248 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
1241 dst = (SkPMColor*)((char*)dst + dstRB); | 1249 dst = (SkPMColor*)((char*)dst + dstRB); |
1242 } | 1250 } |
1243 } break; | 1251 } break; |
1244 #endif | |
1245 default: | 1252 default: |
1246 SkDEBUGFAIL("unexpected mask format"); | 1253 SkDEBUGFAIL("unexpected mask format"); |
1247 break; | 1254 break; |
1248 } | 1255 } |
1249 } | 1256 } |
1250 | 1257 |
1251 /* | 1258 /* |
1252 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4 | 1259 * 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 | 1260 * seems sufficient, and possibly even correct, to allow the hinted outline |
1254 * to be subpixel positioned. | 1261 * to be subpixel positioned. |
(...skipping 566 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1821 if (isLCDFormat(rec->fMaskFormat)) { | 1828 if (isLCDFormat(rec->fMaskFormat)) { |
1822 if (lcdSupport) { | 1829 if (lcdSupport) { |
1823 //CoreGraphics creates 555 masks for smoothed text anyway. | 1830 //CoreGraphics creates 555 masks for smoothed text anyway. |
1824 rec->fMaskFormat = SkMask::kLCD16_Format; | 1831 rec->fMaskFormat = SkMask::kLCD16_Format; |
1825 rec->setHinting(SkPaint::kNormal_Hinting); | 1832 rec->setHinting(SkPaint::kNormal_Hinting); |
1826 } else { | 1833 } else { |
1827 rec->fMaskFormat = SkMask::kA8_Format; | 1834 rec->fMaskFormat = SkMask::kA8_Format; |
1828 } | 1835 } |
1829 } | 1836 } |
1830 | 1837 |
1838 if (CTFontIsAppleColorEmoji(fFontRef.get())) | |
1839 rec->fMaskFormat = SkMask::kARGB32_Format; | |
1840 | |
1831 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMM A_APPLY_TO_A8. | 1841 // 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. | 1842 // All other masks can use regular gamma. |
1833 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hintin g) { | 1843 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hintin g) { |
1834 #ifndef SK_GAMMA_APPLY_TO_A8 | 1844 #ifndef SK_GAMMA_APPLY_TO_A8 |
1835 rec->ignorePreBlend(); | 1845 rec->ignorePreBlend(); |
1836 #endif | 1846 #endif |
1837 } else { | 1847 } else { |
1838 //CoreGraphics dialates smoothed text as needed. | 1848 //CoreGraphics dialates smoothed text as needed. |
1839 rec->setContrast(0); | 1849 rec->setContrast(0); |
1840 } | 1850 } |
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2211 } | 2221 } |
2212 return face; | 2222 return face; |
2213 } | 2223 } |
2214 }; | 2224 }; |
2215 | 2225 |
2216 /////////////////////////////////////////////////////////////////////////////// | 2226 /////////////////////////////////////////////////////////////////////////////// |
2217 | 2227 |
2218 SkFontMgr* SkFontMgr::Factory() { | 2228 SkFontMgr* SkFontMgr::Factory() { |
2219 return SkNEW(SkFontMgr_Mac); | 2229 return SkNEW(SkFontMgr_Mac); |
2220 } | 2230 } |
OLD | NEW |