Split out coretext fontmgr factory.
There can be only one SkFontMgr::Factory, so each should go in its own
translation unit. In addition, the name SkFontHost_mac is now rather out
of date and should now be SkFontMgr_mac_ct. Since the additional burden
of additional build changes after the first is minimal, also split out
SkTypeface_mac_ct and SkScalerContext_mac_ct. The original
SkFontHost_mac.cpp is kept as a shell which #includes the cpp files
which are replacing it. Once references to it are removed from all
builds it can be removed.
This is intended to be a reorganization without much code change. Most
changes are simple renaming of functions which are now shared between
translation units. However, there are a few behavior changes here.
* Drop SkTypefaceCache global for SkTypeface_Mac 'local' global.
* SkCTFontCTWidthForCSSWidth returns CGFloat instead of 'int'.
* SkFontMgr_New_CoreText takes a CTFontCollectionRef.
Change-Id: I897f69f2f5ea06819f5daa964c09cdd36490af85
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/294456
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/294704
diff --git a/BUILD.gn b/BUILD.gn
index 8c09b76..9e3b3b7 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -378,7 +378,16 @@
optional("fontmgr_mac") {
enabled = skia_use_fonthost_mac
- sources = [ "src/ports/SkFontHost_mac.cpp" ]
+ public = [ "include/ports/SkFontMgr_mac_ct.h" ]
+ sources = [
+ #"src/ports/SkFontHost_mac.cpp",
+ "src/ports/SkFontMgr_mac_ct.cpp",
+ "src/ports/SkFontMgr_mac_ct_factory.cpp",
+ "src/ports/SkScalerContext_mac_ct.cpp",
+ "src/ports/SkScalerContext_mac_ct.h",
+ "src/ports/SkTypeface_mac_ct.cpp",
+ "src/ports/SkTypeface_mac_ct.h",
+ ]
if (is_mac) {
libs = [
diff --git a/gn/utils.gni b/gn/utils.gni
index 1b968fc..9b181dc 100644
--- a/gn/utils.gni
+++ b/gn/utils.gni
@@ -81,6 +81,10 @@
"$_src/utils/SkWhitelistTypefaces.cpp",
#mac
+ "$_src/utils/mac/SkCGBase.h",
+ "$_src/utils/mac/SkCGGeometry.h",
+ "$_src/utils/mac/SkCTFontSmoothBehavior.cpp",
+ "$_src/utils/mac/SkCTFontSmoothBehavior.h",
"$_src/utils/mac/SkCreateCGImageRef.cpp",
"$_src/utils/mac/SkUniqueCFRef.h",
diff --git a/include/ports/SkFontMgr_mac_ct.h b/include/ports/SkFontMgr_mac_ct.h
new file mode 100644
index 0000000..45cba65
--- /dev/null
+++ b/include/ports/SkFontMgr_mac_ct.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkFontMgr_mac_ct_DEFINED
+#define SkFontMgr_mac_ct_DEFINED
+
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkTypes.h"
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#endif
+
+class SkFontMgr;
+
+/** Create a font manager for CoreText. If the collection is nullptr the system default will be used. */
+SK_API extern sk_sp<SkFontMgr> SkFontMgr_New_CoreText(CTFontCollectionRef);
+
+#endif // SkFontMgr_mac_ct_DEFINED
diff --git a/public.bzl b/public.bzl
index 0db0487..2d5b201 100644
--- a/public.bzl
+++ b/public.bzl
@@ -347,6 +347,7 @@
"src/ports/*mozalloc*",
"src/ports/*nacl*",
"src/ports/*win*",
+ "src/ports/SkFontHost_mac.cpp",
"src/ports/SkFontMgr_custom.cpp",
"src/ports/SkFontMgr_custom_directory.cpp",
"src/ports/SkFontMgr_custom_embedded.cpp",
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index 9e29b5c..b2f2b0b 100644
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -1,3226 +1,13 @@
/*
- * Copyright 2006 The Android Open Source Project
+ * Copyright 2020 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-#include "include/core/SkTypes.h"
-#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
-
-#ifdef SK_BUILD_FOR_MAC
-#import <ApplicationServices/ApplicationServices.h>
-#endif
-
-#ifdef SK_BUILD_FOR_IOS
-#include <CoreText/CoreText.h>
-#include <CoreText/CTFontManager.h>
-#include <CoreGraphics/CoreGraphics.h>
-#include <CoreFoundation/CoreFoundation.h>
-#endif
-
-#include "include/core/SkFontMetrics.h"
-#include "include/core/SkFontMgr.h"
-#include "include/core/SkPaint.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkStream.h"
-#include "include/core/SkString.h"
-#include "include/ports/SkTypeface_mac.h"
-#include "include/private/SkColorData.h"
-#include "include/private/SkFloatingPoint.h"
-#include "include/private/SkMutex.h"
-#include "include/private/SkOnce.h"
-#include "include/private/SkTemplates.h"
-#include "include/private/SkTo.h"
-#include "include/utils/mac/SkCGUtils.h"
-#include "src/core/SkAdvancedTypefaceMetrics.h"
-#include "src/core/SkAutoMalloc.h"
-#include "src/core/SkDescriptor.h"
-#include "src/core/SkEndian.h"
-#include "src/core/SkFontDescriptor.h"
-#include "src/core/SkGlyph.h"
-#include "src/core/SkMaskGamma.h"
-#include "src/core/SkMathPriv.h"
-#include "src/core/SkTypefaceCache.h"
-#include "src/core/SkUtils.h"
-#include "src/sfnt/SkOTTable_OS_2.h"
-#include "src/sfnt/SkOTUtils.h"
-#include "src/sfnt/SkSFNTHeader.h"
-#include "src/utils/SkUTF.h"
-#include "src/utils/mac/SkUniqueCFRef.h"
-
-#include <dlfcn.h>
-
-#include <utility>
-
-// Set to make glyph bounding boxes visible.
-#define SK_SHOW_TEXT_BLIT_COVERAGE 0
-
-CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
- return face ? (CTFontRef)face->internal_private_getCTFontRef() : nullptr;
-}
-
-class SkScalerContext_Mac;
-
-static SkUniqueCFRef<CFStringRef> make_CFString(const char s[]) {
- return SkUniqueCFRef<CFStringRef>(CFStringCreateWithCString(nullptr, s, kCFStringEncodingUTF8));
-}
-
-// inline versions of these rect helpers
-
-static bool CGRectIsEmpty_inline(const CGRect& rect) {
- return rect.size.width <= 0 || rect.size.height <= 0;
-}
-
-static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
- return rect.origin.x;
-}
-
-static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
- return rect.origin.x + rect.size.width;
-}
-
-static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
- return rect.origin.y;
-}
-
-static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
- return rect.origin.y + rect.size.height;
-}
-
-static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
- return rect.size.width;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
- int width, int height, size_t rowBytes) {
- SkASSERT(width);
- SkASSERT(width * sizeof(uint32_t) <= rowBytes);
-
- if (width >= 32) {
- while (height) {
- sk_memset32(ptr, value, width);
- ptr = (uint32_t*)((char*)ptr + rowBytes);
- height -= 1;
- }
- return;
- }
-
- rowBytes -= width * sizeof(uint32_t);
-
- if (width >= 8) {
- while (height) {
- int w = width;
- do {
- *ptr++ = value; *ptr++ = value;
- *ptr++ = value; *ptr++ = value;
- *ptr++ = value; *ptr++ = value;
- *ptr++ = value; *ptr++ = value;
- w -= 8;
- } while (w >= 8);
- while (--w >= 0) {
- *ptr++ = value;
- }
- ptr = (uint32_t*)((char*)ptr + rowBytes);
- height -= 1;
- }
- } else {
- while (height) {
- int w = width;
- do {
- *ptr++ = value;
- } while (--w > 0);
- ptr = (uint32_t*)((char*)ptr + rowBytes);
- height -= 1;
- }
- }
-}
-
-typedef uint32_t CGRGBPixel;
-
-static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
- return pixel & 0xFF;
-}
-
-static CGFloat ScalarToCG(SkScalar scalar) {
- if (sizeof(CGFloat) == sizeof(float)) {
- return SkScalarToFloat(scalar);
- } else {
- SkASSERT(sizeof(CGFloat) == sizeof(double));
- return (CGFloat) SkScalarToDouble(scalar);
- }
-}
-
-static SkScalar CGToScalar(CGFloat cgFloat) {
- if (sizeof(CGFloat) == sizeof(float)) {
- return SkFloatToScalar(cgFloat);
- } else {
- SkASSERT(sizeof(CGFloat) == sizeof(double));
- return SkDoubleToScalar(cgFloat);
- }
-}
-
-static float CGToFloat(CGFloat cgFloat) {
- if (sizeof(CGFloat) == sizeof(float)) {
- return cgFloat;
- } else {
- SkASSERT(sizeof(CGFloat) == sizeof(double));
- return static_cast<float>(cgFloat);
- }
-}
-
-static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix) {
- return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX]),
- -ScalarToCG(matrix[SkMatrix::kMSkewY] ),
- -ScalarToCG(matrix[SkMatrix::kMSkewX] ),
- ScalarToCG(matrix[SkMatrix::kMScaleY]),
- ScalarToCG(matrix[SkMatrix::kMTransX]),
- ScalarToCG(matrix[SkMatrix::kMTransY]));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
-
-/** Drawn in FontForge, reduced with fonttools ttx, converted by xxd -i,
- * this TrueType font contains a glyph of the spider.
- *
- * To re-forge the original bytes of the TrueType font file,
- * remove all ',|( +0x)' from this definition,
- * copy the data to the clipboard,
- * run 'pbpaste | xxd -p -r - spider.ttf'.
- */
-static constexpr const uint8_t kSpiderSymbol_ttf[] = {
- 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x80, 0x00, 0x03, 0x00, 0x40,
- 0x47, 0x44, 0x45, 0x46, 0x00, 0x14, 0x00, 0x14, 0x00, 0x00, 0x07, 0xa8,
- 0x00, 0x00, 0x00, 0x18, 0x4f, 0x53, 0x2f, 0x32, 0x8a, 0xf4, 0xfb, 0xdb,
- 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x00, 0x60, 0x63, 0x6d, 0x61, 0x70,
- 0xe0, 0x7f, 0x10, 0x7e, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x54,
- 0x67, 0x61, 0x73, 0x70, 0xff, 0xff, 0x00, 0x03, 0x00, 0x00, 0x07, 0xa0,
- 0x00, 0x00, 0x00, 0x08, 0x67, 0x6c, 0x79, 0x66, 0x97, 0x0b, 0x6a, 0xf6,
- 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x03, 0x40, 0x68, 0x65, 0x61, 0x64,
- 0x0f, 0xa2, 0x24, 0x1a, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x36,
- 0x68, 0x68, 0x65, 0x61, 0x0e, 0xd3, 0x07, 0x3f, 0x00, 0x00, 0x01, 0x04,
- 0x00, 0x00, 0x00, 0x24, 0x68, 0x6d, 0x74, 0x78, 0x10, 0x03, 0x00, 0x44,
- 0x00, 0x00, 0x01, 0xa8, 0x00, 0x00, 0x00, 0x0e, 0x6c, 0x6f, 0x63, 0x61,
- 0x01, 0xb4, 0x00, 0x28, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x0a,
- 0x6d, 0x61, 0x78, 0x70, 0x00, 0x4a, 0x01, 0x4d, 0x00, 0x00, 0x01, 0x28,
- 0x00, 0x00, 0x00, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0xc3, 0xe5, 0x39, 0xd4,
- 0x00, 0x00, 0x05, 0x58, 0x00, 0x00, 0x02, 0x28, 0x70, 0x6f, 0x73, 0x74,
- 0xff, 0x03, 0x00, 0x67, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x20,
- 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0b, 0x0f, 0x08, 0x1d,
- 0x5f, 0x0f, 0x3c, 0xf5, 0x00, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xd1, 0x97, 0xa8, 0x5a, 0x00, 0x00, 0x00, 0x00, 0xd6, 0xe8, 0x32, 0x33,
- 0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x55, 0x00, 0x00, 0x00, 0x08,
- 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
- 0x05, 0x55, 0xff, 0x3b, 0x01, 0x79, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00,
- 0x00, 0x04, 0x01, 0x1c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x2e,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x01, 0x90, 0x00, 0x05,
- 0x00, 0x00, 0x05, 0x33, 0x05, 0x99, 0x00, 0x00, 0x01, 0x1e, 0x05, 0x33,
- 0x05, 0x99, 0x00, 0x00, 0x03, 0xd7, 0x00, 0x66, 0x02, 0x12, 0x00, 0x00,
- 0x05, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x73, 0x6b, 0x69, 0x61, 0x00, 0xc0, 0x00, 0x00, 0xf0, 0x21,
- 0x06, 0x66, 0xfe, 0x66, 0x01, 0x79, 0x05, 0x55, 0x00, 0xc5, 0x80, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x20, 0x00, 0x01, 0x08, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x48,
- 0x00, 0x00, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00,
- 0x00, 0x09, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21, 0xf0, 0x21, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21,
- 0xf0, 0x21, 0xff, 0xff, 0x00, 0x01, 0xff, 0xf9, 0xff, 0xf5, 0xff, 0xe4,
- 0xff, 0xe2, 0x0f, 0xe2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
- 0x00, 0x14, 0x00, 0x14, 0x01, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x44,
- 0x00, 0x00, 0x02, 0x64, 0x05, 0x55, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00,
- 0x33, 0x11, 0x21, 0x11, 0x25, 0x21, 0x11, 0x21, 0x44, 0x02, 0x20, 0xfe,
- 0x24, 0x01, 0x98, 0xfe, 0x68, 0x05, 0x55, 0xfa, 0xab, 0x44, 0x04, 0xcd,
- 0x00, 0x04, 0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x4c, 0x00, 0x15,
- 0x00, 0x1d, 0x00, 0x25, 0x01, 0x1b, 0x00, 0x00, 0x01, 0x36, 0x37, 0x36,
- 0x27, 0x26, 0x07, 0x06, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27, 0x26, 0x07,
- 0x06, 0x17, 0x16, 0x17, 0x16, 0x32, 0x37, 0x32, 0x35, 0x34, 0x23, 0x22,
- 0x15, 0x14, 0x27, 0x32, 0x35, 0x34, 0x23, 0x22, 0x15, 0x14, 0x03, 0x32,
- 0x17, 0x30, 0x17, 0x31, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33,
- 0x32, 0x33, 0x16, 0x33, 0x32, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27,
- 0x26, 0x27, 0x26, 0x23, 0x22, 0x07, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
- 0x1f, 0x02, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32, 0x17, 0x17, 0x16, 0x33,
- 0x16, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27, 0x27, 0x26, 0x23, 0x22,
- 0x07, 0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x33, 0x32, 0x33, 0x32,
- 0x37, 0x36, 0x37, 0x36, 0x17, 0x16, 0x1f, 0x02, 0x16, 0x17, 0x16, 0x15,
- 0x14, 0x23, 0x22, 0x27, 0x27, 0x26, 0x27, 0x27, 0x26, 0x27, 0x26, 0x07,
- 0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x07,
- 0x06, 0x23, 0x22, 0x27, 0x26, 0x07, 0x06, 0x07, 0x06, 0x15, 0x14, 0x17,
- 0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27,
- 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x34, 0x27, 0x26, 0x07,
- 0x06, 0x07, 0x06, 0x0f, 0x02, 0x06, 0x23, 0x22, 0x27, 0x26, 0x35, 0x34,
- 0x37, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x27, 0x26, 0x27,
- 0x26, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x07, 0x06, 0x23, 0x22,
- 0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x37, 0x36, 0x37, 0x37, 0x36,
- 0x37, 0x36, 0x37, 0x36, 0x35, 0x34, 0x27, 0x26, 0x27, 0x26, 0x27, 0x26,
- 0x23, 0x22, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x27, 0x26, 0x27, 0x26,
- 0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32,
- 0x17, 0x16, 0x33, 0x32, 0x37, 0x36, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36,
- 0x33, 0x04, 0xf5, 0x23, 0x13, 0x11, 0x14, 0x16, 0x1d, 0x1b, 0x4c, 0x1f,
- 0x0e, 0x2d, 0x23, 0x14, 0x2c, 0x13, 0x18, 0x25, 0x2c, 0x10, 0x3c, 0x71,
- 0x1d, 0x5c, 0x5c, 0x3f, 0xae, 0x5c, 0x5c, 0x3f, 0x6a, 0x27, 0x31, 0x5b,
- 0x09, 0x27, 0x36, 0x03, 0x0a, 0x26, 0x35, 0x2e, 0x09, 0x08, 0xc6, 0x13,
- 0x81, 0x17, 0x20, 0x18, 0x21, 0x1e, 0x04, 0x04, 0x15, 0x5c, 0x22, 0x26,
- 0x48, 0x56, 0x3b, 0x10, 0x21, 0x01, 0x0c, 0x06, 0x06, 0x0f, 0x31, 0x44,
- 0x3c, 0x52, 0x4a, 0x1d, 0x11, 0x3f, 0xb4, 0x71, 0x01, 0x26, 0x06, 0x0d,
- 0x15, 0x1a, 0x2a, 0x13, 0x53, 0xaa, 0x42, 0x1d, 0x0a, 0x33, 0x20, 0x21,
- 0x2b, 0x01, 0x02, 0x3e, 0x21, 0x09, 0x02, 0x02, 0x0f, 0x2d, 0x4b, 0x0a,
- 0x22, 0x15, 0x20, 0x1f, 0x72, 0x8b, 0x2d, 0x2f, 0x1d, 0x1f, 0x0e, 0x25,
- 0x3f, 0x4d, 0x1b, 0x63, 0x2a, 0x2c, 0x14, 0x22, 0x18, 0x1c, 0x0f, 0x08,
- 0x2a, 0x08, 0x08, 0x0d, 0x3b, 0x4c, 0x52, 0x74, 0x27, 0x71, 0x2e, 0x01,
- 0x0c, 0x10, 0x15, 0x0d, 0x06, 0x0d, 0x05, 0x01, 0x06, 0x2c, 0x28, 0x14,
- 0x1b, 0x05, 0x04, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x16, 0x27, 0x03, 0x0d,
- 0x30, 0x4c, 0x4c, 0x4b, 0x1f, 0x0b, 0x22, 0x26, 0x0d, 0x15, 0x0d, 0x2d,
- 0x68, 0x34, 0x14, 0x3c, 0x25, 0x12, 0x04, 0x10, 0x18, 0x0b, 0x09, 0x30,
- 0x2b, 0x44, 0x66, 0x14, 0x47, 0x47, 0x59, 0x73, 0x25, 0x05, 0x03, 0x1f,
- 0x01, 0x08, 0x3f, 0x48, 0x4b, 0x4b, 0x76, 0x2f, 0x49, 0x2d, 0x22, 0x24,
- 0x0c, 0x15, 0x08, 0x0e, 0x33, 0x03, 0x44, 0x4c, 0x10, 0x46, 0x13, 0x1f,
- 0x27, 0x1b, 0x1d, 0x13, 0x02, 0x24, 0x08, 0x02, 0x42, 0x0e, 0x4d, 0x3c,
- 0x19, 0x1b, 0x40, 0x2b, 0x2b, 0x1e, 0x16, 0x11, 0x04, 0x1f, 0x11, 0x04,
- 0x18, 0x11, 0x35, 0x01, 0xa3, 0x13, 0x24, 0x1f, 0x0b, 0x0c, 0x19, 0x19,
- 0x18, 0x13, 0x0f, 0x0c, 0x1a, 0x18, 0x1f, 0x19, 0x1e, 0x07, 0x1a, 0xc3,
- 0x54, 0x51, 0x54, 0x51, 0x04, 0x53, 0x51, 0x54, 0x50, 0x02, 0x48, 0x1a,
- 0x31, 0x18, 0x55, 0x74, 0x04, 0x0e, 0x09, 0x0d, 0x06, 0x10, 0x16, 0x1b,
- 0x24, 0x01, 0x04, 0x0b, 0x04, 0x10, 0x3f, 0x0a, 0x41, 0x02, 0x41, 0x20,
- 0x06, 0x12, 0x16, 0x21, 0x17, 0x2a, 0x1e, 0x15, 0x40, 0x27, 0x11, 0x0e,
- 0x1e, 0x11, 0x15, 0x1f, 0x43, 0x13, 0x1a, 0x10, 0x15, 0x1b, 0x04, 0x09,
- 0x4d, 0x2a, 0x0f, 0x19, 0x0a, 0x0a, 0x03, 0x05, 0x15, 0x3c, 0x64, 0x21,
- 0x4b, 0x2e, 0x21, 0x28, 0x13, 0x47, 0x44, 0x19, 0x3f, 0x11, 0x18, 0x0b,
- 0x0a, 0x07, 0x18, 0x0d, 0x07, 0x24, 0x2c, 0x2b, 0x21, 0x32, 0x10, 0x48,
- 0x2a, 0x2d, 0x1e, 0x1a, 0x01, 0x0c, 0x43, 0x59, 0x28, 0x4e, 0x1c, 0x0d,
- 0x5d, 0x24, 0x14, 0x0a, 0x05, 0x1f, 0x24, 0x32, 0x46, 0x3e, 0x5f, 0x3e,
- 0x44, 0x1a, 0x30, 0x15, 0x0d, 0x07, 0x18, 0x2b, 0x03, 0x0d, 0x1a, 0x28,
- 0x28, 0x57, 0xb2, 0x29, 0x27, 0x40, 0x2c, 0x23, 0x16, 0x63, 0x58, 0x1a,
- 0x0a, 0x18, 0x11, 0x23, 0x08, 0x1b, 0x29, 0x05, 0x04, 0x0b, 0x15, 0x0d,
- 0x14, 0x0b, 0x2a, 0x29, 0x5a, 0x62, 0x01, 0x19, 0x1e, 0x05, 0x05, 0x26,
- 0x42, 0x42, 0x2a, 0x2a, 0x3f, 0x0d, 0x0f, 0x09, 0x05, 0x07, 0x01, 0x0b,
- 0x25, 0x3e, 0x0d, 0x17, 0x11, 0x01, 0x03, 0x0d, 0x13, 0x20, 0x19, 0x11,
- 0x03, 0x02, 0x01, 0x04, 0x11, 0x04, 0x05, 0x1b, 0x3d, 0x10, 0x29, 0x20,
- 0x04, 0x04, 0x0a, 0x07, 0x04, 0x1f, 0x15, 0x20, 0x3e, 0x0f, 0x2a, 0x1e,
- 0x00, 0x00, 0x00, 0x1b, 0x01, 0x4a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x00, 0x07, 0x00, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x03, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x04, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x05, 0x00, 0x02, 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x06, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x0d, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
- 0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
- 0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
- 0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
- 0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
- 0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
- 0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
- 0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
- 0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
- 0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
- 0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
- 0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
- 0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
- 0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
- 0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
- 0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
- 0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
- 0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
- 0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69,
- 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x35,
- 0x2c, 0x20, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x53, 0x70, 0x69,
- 0x64, 0x65, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x52, 0x65, 0x67,
- 0x75, 0x6c, 0x61, 0x72, 0x56, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
- 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x2e, 0x73, 0x69, 0x6c,
- 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x4f, 0x46, 0x4c, 0x00, 0x43, 0x00, 0x6f,
- 0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68,
- 0x00, 0x74, 0x00, 0x20, 0x00, 0x28, 0x00, 0x63, 0x00, 0x29, 0x00, 0x20,
- 0x00, 0x32, 0x00, 0x30, 0x00, 0x31, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x20,
- 0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65,
- 0x00, 0x2e, 0x00, 0x53, 0x00, 0x70, 0x00, 0x69, 0x00, 0x64, 0x00, 0x65,
- 0x00, 0x72, 0x00, 0x53, 0x00, 0x79, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x6f,
- 0x00, 0x6c, 0x00, 0x52, 0x00, 0x65, 0x00, 0x67, 0x00, 0x75, 0x00, 0x6c,
- 0x00, 0x61, 0x00, 0x72, 0x00, 0x56, 0x00, 0x31, 0x00, 0x68, 0x00, 0x74,
- 0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x73,
- 0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70, 0x00, 0x74, 0x00, 0x73,
- 0x00, 0x2e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x2e, 0x00, 0x6f,
- 0x00, 0x72, 0x00, 0x67, 0x00, 0x2f, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x4c,
- 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x66,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0xff, 0xff, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
- 0x00, 0x02, 0x00, 0x00
-};
-
-enum class SmoothBehavior {
- none, // SmoothFonts produces no effect.
- some, // SmoothFonts produces some effect, but not subpixel coverage.
- subpixel, // SmoothFonts produces some effect and provides subpixel coverage.
-};
-
-/**
- * There does not appear to be a publicly accessable API for determining if lcd
- * font smoothing will be applied if we request it. The main issue is that if
- * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
- */
-static SmoothBehavior smooth_behavior() {
- static SmoothBehavior gSmoothBehavior = []{
- uint32_t noSmoothBitmap[16][16] = {};
- uint32_t smoothBitmap[16][16] = {};
-
- SkUniqueCFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
- SkUniqueCFRef<CGContextRef> noSmoothContext(
- CGBitmapContextCreate(&noSmoothBitmap, 16, 16, 8, 16*4,
- colorspace.get(), BITMAP_INFO_RGB));
- SkUniqueCFRef<CGContextRef> smoothContext(
- CGBitmapContextCreate(&smoothBitmap, 16, 16, 8, 16*4,
- colorspace.get(), BITMAP_INFO_RGB));
-
- SkUniqueCFRef<CFDataRef> data(CFDataCreateWithBytesNoCopy(
- kCFAllocatorDefault, kSpiderSymbol_ttf, SK_ARRAY_COUNT(kSpiderSymbol_ttf),
- kCFAllocatorNull));
- SkUniqueCFRef<CTFontDescriptorRef> desc(
- CTFontManagerCreateFontDescriptorFromData(data.get()));
- SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc.get(), 16, nullptr));
- SkASSERT(ctFont);
-
- CGContextSetShouldSmoothFonts(noSmoothContext.get(), false);
- CGContextSetShouldAntialias(noSmoothContext.get(), true);
- CGContextSetTextDrawingMode(noSmoothContext.get(), kCGTextFill);
- CGContextSetGrayFillColor(noSmoothContext.get(), 1, 1);
-
- CGContextSetShouldSmoothFonts(smoothContext.get(), true);
- CGContextSetShouldAntialias(smoothContext.get(), true);
- CGContextSetTextDrawingMode(smoothContext.get(), kCGTextFill);
- CGContextSetGrayFillColor(smoothContext.get(), 1, 1);
-
- CGPoint point = CGPointMake(0, 3);
- CGGlyph spiderGlyph = 3;
- CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, noSmoothContext.get());
- CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, smoothContext.get());
-
- // For debugging.
- //SkUniqueCFRef<CGImageRef> image(CGBitmapContextCreateImage(noSmoothContext()));
- //SkUniqueCFRef<CGImageRef> image(CGBitmapContextCreateImage(smoothContext()));
-
- SmoothBehavior smoothBehavior = SmoothBehavior::none;
- for (int x = 0; x < 16; ++x) {
- for (int y = 0; y < 16; ++y) {
- uint32_t smoothPixel = smoothBitmap[x][y];
- uint32_t r = (smoothPixel >> 16) & 0xFF;
- uint32_t g = (smoothPixel >> 8) & 0xFF;
- uint32_t b = (smoothPixel >> 0) & 0xFF;
- if (r != g || r != b) {
- return SmoothBehavior::subpixel;
- }
- if (noSmoothBitmap[x][y] != smoothPixel) {
- smoothBehavior = SmoothBehavior::some;
- }
- }
- }
- return smoothBehavior;
- }();
- return gSmoothBehavior;
-}
-
-class Offscreen {
-public:
- Offscreen()
- : fRGBSpace(nullptr)
- , fCG(nullptr)
- , fDoAA(false)
- , fDoLCD(false)
- {
- fSize.set(0, 0);
- }
-
- CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
- CGGlyph glyphID, size_t* rowBytesPtr, bool generateA8FromLCD);
-
-private:
- enum {
- kSize = 32 * 32 * sizeof(CGRGBPixel)
- };
- SkAutoSMalloc<kSize> fImageStorage;
- SkUniqueCFRef<CGColorSpaceRef> fRGBSpace;
-
- // cached state
- SkUniqueCFRef<CGContextRef> fCG;
- SkISize fSize;
- bool fDoAA;
- bool fDoLCD;
-
- static int RoundSize(int dimension) {
- return SkNextPow2(dimension);
- }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
- CFNumberRef num;
- return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
- && CFNumberIsFloatType(num)
- && CFNumberGetValue(num, kCFNumberCGFloatType, value);
-}
-
-template <typename S, typename D, typename C> struct LinearInterpolater {
- struct Mapping {
- S src_val;
- D dst_val;
- };
- constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
- : fMapping(mapping), fMappingCount(mappingCount) {}
-
- static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
- SkASSERT(src_min < src_max);
- SkASSERT(dst_min <= dst_max);
- return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
- }
-
- D map(S val) const {
- // -Inf to [0]
- if (val < fMapping[0].src_val) {
- return fMapping[0].dst_val;
- }
-
- // Linear from [i] to [i+1]
- for (int i = 0; i < fMappingCount - 1; ++i) {
- if (val < fMapping[i+1].src_val) {
- return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
- fMapping[i].dst_val, fMapping[i+1].dst_val);
- }
- }
-
- // From [n] to +Inf
- // if (fcweight < Inf)
- return fMapping[fMappingCount - 1].dst_val;
- }
-
- Mapping const * fMapping;
- int fMappingCount;
-};
-
-struct RoundCGFloatToInt {
- int operator()(CGFloat s) { return s + 0.5; }
-};
-struct CGFloatIdentity {
- CGFloat operator()(CGFloat s) { return s; }
-};
-
-/** Returns the [-1, 1] CTFontDescriptor weights for the
- * <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
- *
- * It is assumed that the values will be interpolated linearly between these points.
- * NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
- * The actual values appear to be stable, but they may change in the future without notice.
- */
-static CGFloat(&get_NSFontWeight_mapping())[11] {
-
- // Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
-#ifdef SK_BUILD_FOR_MAC
-# define SK_KIT_FONT_WEIGHT_PREFIX "NS"
-#endif
-#ifdef SK_BUILD_FOR_IOS
-# define SK_KIT_FONT_WEIGHT_PREFIX "UI"
-#endif
- static constexpr struct {
- CGFloat defaultValue;
- const char* name;
- } nsFontWeightLoaderInfos[] = {
- { -0.80f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight" },
- { -0.60f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin" },
- { -0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight" },
- { 0.00f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular" },
- { 0.23f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium" },
- { 0.30f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold" },
- { 0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold" },
- { 0.56f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy" },
- { 0.62f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack" },
- };
-
- static_assert(SK_ARRAY_COUNT(nsFontWeightLoaderInfos) == 9, "");
- static CGFloat nsFontWeights[11];
- static SkOnce once;
- once([&] {
- size_t i = 0;
- nsFontWeights[i++] = -1.00;
- for (const auto& nsFontWeightLoaderInfo : nsFontWeightLoaderInfos) {
- void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightLoaderInfo.name);
- if (nsFontWeightValuePtr) {
- nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
- } else {
- nsFontWeights[i++] = nsFontWeightLoaderInfo.defaultValue;
- }
- }
- nsFontWeights[i++] = 1.00;
- });
- return nsFontWeights;
-}
-
-/** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
- *
- * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
- * CTFont is native or created from a CGDataProvider.
- */
-static CGFloat fontstyle_to_ct_weight(int fontstyleWeight) {
- using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
-
- // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
- // However, on this end we can't tell, so this is ignored.
-
- static Interpolator::Mapping nativeWeightMappings[11];
- static SkOnce once;
- once([&] {
- CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
- for (int i = 0; i < 11; ++i) {
- nativeWeightMappings[i].src_val = i * 100;
- nativeWeightMappings[i].dst_val = nsFontWeights[i];
- }
- });
- static constexpr Interpolator nativeInterpolator(
- nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
-
- return nativeInterpolator.map(fontstyleWeight);
-}
-
-
-/** Convert the [-1, 1] CTFontDescriptor weight to [0, 1000] CSS weight.
- *
- * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
- * CTFont is native or created from a CGDataProvider.
- */
-static int ct_weight_to_fontstyle(CGFloat cgWeight, bool fromDataProvider) {
- using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
-
- // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
- // However, on this end we can't tell, so this is ignored.
-
- /** This mapping for CGDataProvider created fonts is determined by creating font data with every
- * weight, creating a CTFont, and asking the CTFont for its weight. See the TypefaceStyle test
- * in tests/TypefaceTest.cpp for the code used to determine these values.
- */
- static constexpr Interpolator::Mapping dataProviderWeightMappings[] = {
- { -1.00, 0 },
- { -0.70, 100 },
- { -0.50, 200 },
- { -0.23, 300 },
- { 0.00, 400 },
- { 0.20, 500 },
- { 0.30, 600 },
- { 0.40, 700 },
- { 0.60, 800 },
- { 0.80, 900 },
- { 1.00, 1000 },
- };
- static constexpr Interpolator dataProviderInterpolator(
- dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
-
- static Interpolator::Mapping nativeWeightMappings[11];
- static SkOnce once;
- once([&] {
- CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
- for (int i = 0; i < 11; ++i) {
- nativeWeightMappings[i].src_val = nsFontWeights[i];
- nativeWeightMappings[i].dst_val = i * 100;
- }
- });
- static constexpr Interpolator nativeInterpolator(
- nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
-
- return fromDataProvider ? dataProviderInterpolator.map(cgWeight)
- : nativeInterpolator.map(cgWeight);
-}
-
-/** Convert the [0, 10] CSS weight to [-1, 1] CTFontDescriptor width. */
-static int fontstyle_to_ct_width(int fontstyleWidth) {
- using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
-
- // Values determined by creating font data with every width, creating a CTFont,
- // and asking the CTFont for its width. See TypefaceStyle test for basics.
- static constexpr Interpolator::Mapping widthMappings[] = {
- { 0, -0.5 },
- { 10, 0.5 },
- };
- static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
- return interpolator.map(fontstyleWidth);
-}
-
-/** Convert the [-1, 1] CTFontDescriptor width to [0, 10] CSS weight. */
-static int ct_width_to_fontstyle(CGFloat cgWidth) {
- using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
-
- // Values determined by creating font data with every width, creating a CTFont,
- // and asking the CTFont for its width. See TypefaceStyle test for basics.
- static constexpr Interpolator::Mapping widthMappings[] = {
- { -0.5, 0 },
- { 0.5, 10 },
- };
- static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
- return interpolator.map(cgWidth);
-}
-
-static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc, bool fromDataProvider) {
- SkUniqueCFRef<CFTypeRef> traits(CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
- if (!traits || CFDictionaryGetTypeID() != CFGetTypeID(traits.get())) {
- return SkFontStyle();
- }
- SkUniqueCFRef<CFDictionaryRef> fontTraitsDict(static_cast<CFDictionaryRef>(traits.release()));
-
- CGFloat weight, width, slant;
- if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWeightTrait, &weight)) {
- weight = 0;
- }
- if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWidthTrait, &width)) {
- width = 0;
- }
- if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontSlantTrait, &slant)) {
- slant = 0;
- }
-
- return SkFontStyle(ct_weight_to_fontstyle(weight, fromDataProvider),
- ct_width_to_fontstyle(width),
- slant ? SkFontStyle::kItalic_Slant
- : SkFontStyle::kUpright_Slant);
-}
-
-struct OpszVariation {
- bool isSet = false;
- double value = 0;
-};
-
-class SkTypeface_Mac : public SkTypeface {
-public:
- SkTypeface_Mac(SkUniqueCFRef<CTFontRef> fontRef, const SkFontStyle& fs, bool isFixedPitch,
- OpszVariation opszVariation, std::unique_ptr<SkStreamAsset> providedData)
- : SkTypeface(fs, isFixedPitch)
- , fFontRef(std::move(fontRef))
- , fOpszVariation(opszVariation)
- , fHasColorGlyphs(
- SkToBool(CTFontGetSymbolicTraits(fFontRef.get()) & kCTFontColorGlyphsTrait))
- , fStream(std::move(providedData))
- , fIsFromStream(fStream)
- {
- SkASSERT(fFontRef);
- }
-
- SkUniqueCFRef<CTFontRef> fFontRef;
- const OpszVariation fOpszVariation;
- const bool fHasColorGlyphs;
-
-protected:
- int onGetUPEM() const override;
- std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override;
- std::unique_ptr<SkFontData> onMakeFontData() const override;
- int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
- int coordinateCount) const override;
- void onGetFamilyName(SkString* familyName) const override;
- SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
- int onGetTableTags(SkFontTableTag tags[]) const override;
- size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
- sk_sp<SkData> onCopyTableData(SkFontTableTag) const override;
- SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
- const SkDescriptor*) const override;
- void onFilterRec(SkScalerContextRec*) const override;
- void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
- void getGlyphToUnicodeMap(SkUnichar*) const override;
- std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
- void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
- int onCountGlyphs() const override;
- void getPostScriptGlyphNames(SkString*) const override {}
- int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
- int parameterCount) const override;
- sk_sp<SkTypeface> onMakeClone(const SkFontArguments&) const override;
-
- void* onGetCTFontRef() const override { return (void*)fFontRef.get(); }
-
-private:
- mutable std::unique_ptr<SkStreamAsset> fStream;
- bool fIsFromStream;
- mutable SkOnce fInitStream;
-
- typedef SkTypeface INHERITED;
-};
-
-static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
- CTFontRef self = (CTFontRef)context;
- CTFontRef other = (CTFontRef)cached->internal_private_getCTFontRef();
-
- return CFEqual(self, other);
-}
-
-/** Creates a typeface, searching the cache if isLocalStream is false. */
-static sk_sp<SkTypeface> create_from_CTFontRef(SkUniqueCFRef<CTFontRef> font,
- OpszVariation opszVariation,
- std::unique_ptr<SkStreamAsset> providedData) {
- SkASSERT(font);
- const bool isFromStream(providedData);
-
- if (!isFromStream) {
- sk_sp<SkTypeface> face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef,
- (void*)font.get());
- if (face) {
- return face;
- }
- }
-
- SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(font.get()));
- SkFontStyle style = fontstyle_from_descriptor(desc.get(), isFromStream);
- CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font.get());
- bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
-
- sk_sp<SkTypeface> face(new SkTypeface_Mac(std::move(font), style,
- isFixedPitch, opszVariation,
- std::move(providedData)));
- if (!isFromStream) {
- SkTypefaceCache::Add(face);
- }
- return face;
-}
-
-/** Creates a typeface from a descriptor, searching the cache. */
-static sk_sp<SkTypeface> create_from_desc(CTFontDescriptorRef desc) {
- SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
- if (!ctFont) {
- return nullptr;
- }
-
- return create_from_CTFontRef(std::move(ctFont), OpszVariation(), nullptr);
-}
-
-static SkUniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[],
- const SkFontStyle& style) {
- SkUniqueCFRef<CFMutableDictionaryRef> cfAttributes(
- CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
-
- SkUniqueCFRef<CFMutableDictionaryRef> cfTraits(
- CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
-
- if (!cfAttributes || !cfTraits) {
- return nullptr;
- }
-
- // TODO(crbug.com/1018581) Some CoreText versions have errant behavior when
- // certain traits set. Temporary workaround to omit specifying trait for
- // those versions.
- // Long term solution will involve serializing typefaces instead of relying
- // upon this to match between processes.
- //
- // Compare CoreText.h in an up to date SDK for where these values come from.
- static const uint32_t kSkiaLocalCTVersionNumber10_14 = 0x000B0000;
- static const uint32_t kSkiaLocalCTVersionNumber10_15 = 0x000C0000;
-
- // CTFontTraits (symbolic)
- // macOS 14 and iOS 12 seem to behave badly when kCTFontSymbolicTrait is set.
- // macOS 15 yields LastResort font instead of a good default font when
- // kCTFontSymbolicTrait is set.
- if (!(&CTGetCoreTextVersion && CTGetCoreTextVersion() >= kSkiaLocalCTVersionNumber10_14)) {
- CTFontSymbolicTraits ctFontTraits = 0;
- if (style.weight() >= SkFontStyle::kBold_Weight) {
- ctFontTraits |= kCTFontBoldTrait;
- }
- if (style.slant() != SkFontStyle::kUpright_Slant) {
- ctFontTraits |= kCTFontItalicTrait;
- }
- SkUniqueCFRef<CFNumberRef> cfFontTraits(
- CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
- if (cfFontTraits) {
- CFDictionaryAddValue(cfTraits.get(), kCTFontSymbolicTrait, cfFontTraits.get());
- }
- }
-
- // CTFontTraits (weight)
- CGFloat ctWeight = fontstyle_to_ct_weight(style.weight());
- SkUniqueCFRef<CFNumberRef> cfFontWeight(
- CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWeight));
- if (cfFontWeight) {
- CFDictionaryAddValue(cfTraits.get(), kCTFontWeightTrait, cfFontWeight.get());
- }
- // CTFontTraits (width)
- CGFloat ctWidth = fontstyle_to_ct_width(style.width());
- SkUniqueCFRef<CFNumberRef> cfFontWidth(
- CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWidth));
- if (cfFontWidth) {
- CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get());
- }
- // CTFontTraits (slant)
- // macOS 15 behaves badly when kCTFontSlantTrait is set.
- if (!(&CTGetCoreTextVersion && CTGetCoreTextVersion() == kSkiaLocalCTVersionNumber10_15)) {
- CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : 1;
- SkUniqueCFRef<CFNumberRef> cfFontSlant(
- CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant));
- if (cfFontSlant) {
- CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get());
- }
- }
- // CTFontTraits
- CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get());
-
- // CTFontFamilyName
- if (familyName) {
- SkUniqueCFRef<CFStringRef> cfFontName = make_CFString(familyName);
- if (cfFontName) {
- CFDictionaryAddValue(cfAttributes.get(), kCTFontFamilyNameAttribute, cfFontName.get());
- }
- }
-
- return SkUniqueCFRef<CTFontDescriptorRef>(
- CTFontDescriptorCreateWithAttributes(cfAttributes.get()));
-}
-
-// Same as the above function except style is included so we can
-// compare whether the created font conforms to the style. If not, we need
-// to recreate the font with symbolic traits. This is needed due to MacOS 10.11
-// font creation problem https://bugs.chromium.org/p/skia/issues/detail?id=8447.
-static sk_sp<SkTypeface> create_from_desc_and_style(CTFontDescriptorRef desc,
- const SkFontStyle& style) {
- SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
- if (!ctFont) {
- return nullptr;
- }
-
- const CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctFont.get());
- CTFontSymbolicTraits expected_traits = traits;
- if (style.slant() != SkFontStyle::kUpright_Slant) {
- expected_traits |= kCTFontItalicTrait;
- }
- if (style.weight() >= SkFontStyle::kBold_Weight) {
- expected_traits |= kCTFontBoldTrait;
- }
-
- if (expected_traits != traits) {
- SkUniqueCFRef<CTFontRef> ctNewFont(CTFontCreateCopyWithSymbolicTraits(
- ctFont.get(), 0, nullptr, expected_traits, expected_traits));
- if (ctNewFont) {
- ctFont = std::move(ctNewFont);
- }
- }
-
- return create_from_CTFontRef(std::move(ctFont), OpszVariation(), nullptr);
-}
-
-/** Creates a typeface from a name, searching the cache. */
-static sk_sp<SkTypeface> create_from_name(const char familyName[], const SkFontStyle& style) {
- SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
- if (!desc) {
- return nullptr;
- }
- return create_from_desc_and_style(desc.get(), style);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-/* This function is visible on the outside. It first searches the cache, and if
- * not found, returns a new entry (after adding it to the cache).
- */
-sk_sp<SkTypeface> SkMakeTypefaceFromCTFont(CTFontRef font) {
- CFRetain(font);
- return create_from_CTFontRef(SkUniqueCFRef<CTFontRef>(font),
- OpszVariation(),
- nullptr);
-}
-
-static const char* map_css_names(const char* name) {
- static const struct {
- const char* fFrom; // name the caller specified
- const char* fTo; // "canonical" name we map to
- } gPairs[] = {
- { "sans-serif", "Helvetica" },
- { "serif", "Times" },
- { "monospace", "Courier" }
- };
-
- for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
- if (strcmp(name, gPairs[i].fFrom) == 0) {
- return gPairs[i].fTo;
- }
- }
- return name; // no change
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class SkScalerContext_Mac : public SkScalerContext {
-public:
- SkScalerContext_Mac(sk_sp<SkTypeface_Mac>, const SkScalerContextEffects&, const SkDescriptor*);
-
-protected:
- unsigned generateGlyphCount(void) override;
- bool generateAdvance(SkGlyph* glyph) override;
- void generateMetrics(SkGlyph* glyph) override;
- void generateImage(const SkGlyph& glyph) override;
- bool generatePath(SkGlyphID glyph, SkPath* path) override;
- void generateFontMetrics(SkFontMetrics*) override;
-
-private:
- template<bool APPLY_PREBLEND>
- static void RGBToA8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
- const SkGlyph& glyph, const uint8_t* table8);
- template<bool APPLY_PREBLEND>
- static uint16_t RGBToLcd16(CGRGBPixel rgb, const uint8_t* tableR,
- const uint8_t* tableG,
- const uint8_t* tableB);
- template<bool APPLY_PREBLEND>
- static void RGBToLcd16(const CGRGBPixel* SK_RESTRICT cgPixels,
- size_t cgRowBytes,
- const SkGlyph& glyph,
- const uint8_t* tableR,
- const uint8_t* tableG,
- const uint8_t* tableB);
-
- Offscreen fOffscreen;
-
- /** Unrotated variant of fCTFont.
- *
- * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
- * advances, but always sets the height to 0. This font is used to get the advances of the
- * unrotated glyph, and then the rotation is applied separately.
- *
- * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
- * This makes kCTFontOrientationDefault dangerous, because the metrics from
- * kCTFontOrientationHorizontal are in a different space from kCTFontOrientationVertical.
- * With kCTFontOrientationVertical the advances must be unrotated.
- *
- * Sometimes, creating a copy of a CTFont with the same size but different trasform will select
- * different underlying font data. As a result, avoid ever creating more than one CTFont per
- * SkScalerContext to ensure that only one CTFont is used.
- *
- * As a result of the above (and other constraints) this font contains the size, but not the
- * transform. The transform must always be applied separately.
- */
- SkUniqueCFRef<CTFontRef> fCTFont;
-
- /** The transform without the font size. */
- CGAffineTransform fTransform;
- CGAffineTransform fInvTransform;
-
- SkUniqueCFRef<CGFontRef> fCGFont;
- uint16_t fGlyphCount;
- const bool fDoSubPosition;
-
- friend class Offscreen;
-
- typedef SkScalerContext INHERITED;
-};
-
-// In macOS 10.12 and later any variation on the CGFont which has default axis value will be
-// dropped when creating the CTFont. Unfortunately, in macOS 10.15 the priority of setting
-// the optical size (and opsz variation) is
-// 1. the value of kCTFontOpticalSizeAttribute in the CTFontDescriptor (undocumented)
-// 2. the opsz axis default value if kCTFontOpticalSizeAttribute is 'none' (undocumented)
-// 3. the opsz variation on the nascent CTFont from the CGFont (was dropped if default)
-// 4. the opsz variation in kCTFontVariationAttribute in CTFontDescriptor (crashes 10.10)
-// 5. the size requested (can fudge in SkTypeface but not SkScalerContext)
-// The first one which is found will be used to set the opsz variation (after clamping).
-static void add_opsz_attr(CFMutableDictionaryRef attr, double opsz) {
- SkUniqueCFRef<CFNumberRef> opszValueNumber(
- CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &opsz));
- // Avoid using kCTFontOpticalSizeAttribute directly
- CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
- CFDictionarySetValue(attr, SkCTFontOpticalSizeAttribute, opszValueNumber.get());
-}
-
-// This turns off application of the 'trak' table to advances, but also all other tracking.
-static void add_notrak_attr(CFMutableDictionaryRef attr) {
- int zero = 0;
- SkUniqueCFRef<CFNumberRef> unscaledTrackingNumber(
- CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero));
- CFStringRef SkCTFontUnscaledTrackingAttribute = CFSTR("NSCTFontUnscaledTrackingAttribute");
- CFDictionarySetValue(attr, SkCTFontUnscaledTrackingAttribute, unscaledTrackingNumber.get());
-}
-
-static SkUniqueCFRef<CTFontRef> ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
- OpszVariation opsz)
-{
- SkUniqueCFRef<CFMutableDictionaryRef> attr(
- CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
-
- if (opsz.isSet) {
- add_opsz_attr(attr.get(), opsz.value);
-#if !defined(SK_IGNORE_MAC_OPSZ_FORCE)
- } else {
- // On (at least) 10.10 though 10.14 the default system font was SFNSText/SFNSDisplay.
- // The CTFont is backed by both; optical size < 20 means SFNSText else SFNSDisplay.
- // On at least 10.11 the glyph ids in these fonts became non-interchangable.
- // To keep glyph ids stable over size changes, preserve the optical size.
- // In 10.15 this was replaced with use of variable fonts with an opsz axis.
- // A CTFont backed by multiple fonts picked by opsz where the multiple backing fonts are
- // variable fonts with opsz axis and non-interchangeable glyph ids would break the
- // opsz.isSet branch above, but hopefully that never happens.
- // See https://crbug.com/524646 .
- CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
- SkUniqueCFRef<CFTypeRef> opsz(CTFontCopyAttribute(baseFont, SkCTFontOpticalSizeAttribute));
- double opsz_val;
- if (!opsz ||
- CFGetTypeID(opsz.get()) != CFNumberGetTypeID() ||
- !CFNumberGetValue(static_cast<CFNumberRef>(opsz.get()),kCFNumberDoubleType,&opsz_val) ||
- opsz_val <= 0)
- {
- opsz_val = CTFontGetSize(baseFont);
- }
- add_opsz_attr(attr.get(), opsz_val);
-#endif
- }
- add_notrak_attr(attr.get());
-
- SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontDescriptorCreateWithAttributes(attr.get()));
-
-#if !defined(SK_IGNORE_MAC_OPSZ_FORCE)
- return SkUniqueCFRef<CTFontRef>(
- CTFontCreateCopyWithAttributes(baseFont, textSize, nullptr, desc.get()));
-#else
- SkUniqueCFRef<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
- return SkUniqueCFRef<CTFontRef>(
- CTFontCreateWithGraphicsFont(baseCGFont.get(), textSize, nullptr, desc.get()));
-
-#endif
-}
-
-SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
- const SkScalerContextEffects& effects,
- const SkDescriptor* desc)
- : INHERITED(std::move(typeface), effects, desc)
- , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
-
-{
- CTFontRef ctFont = (CTFontRef)this->getTypeface()->internal_private_getCTFontRef();
- CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
- SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
- fGlyphCount = SkToU16(numGlyphs);
-
- // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
- // As a result, it is necessary to know the actual device size and request that.
- SkVector scale;
- SkMatrix skTransform;
- bool invertible = fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
- &scale, &skTransform, nullptr, nullptr, nullptr);
- fTransform = MatrixToCGAffineTransform(skTransform);
- // CGAffineTransformInvert documents that if the transform is non-invertible it will return the
- // passed transform unchanged. It does so, but then also prints a message to stdout. Avoid this.
- if (invertible) {
- fInvTransform = CGAffineTransformInvert(fTransform);
- } else {
- fInvTransform = fTransform;
- }
-
- // The transform contains everything except the requested text size.
- // Some properties, like 'trak', are based on the optical text size.
- CGFloat textSize = ScalarToCG(scale.y());
- fCTFont = ctfont_create_exact_copy(ctFont, textSize,
- ((SkTypeface_Mac*)this->getTypeface())->fOpszVariation);
- fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(), nullptr));
-}
-
-CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
- CGGlyph glyphID, size_t* rowBytesPtr,
- bool generateA8FromLCD) {
- if (!fRGBSpace) {
- //It doesn't appear to matter what color space is specified.
- //Regular blends and antialiased text are always (s*a + d*(1-a))
- //and subpixel antialiased text is always g=2.0.
- fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
- }
-
- // default to kBW_Format
- bool doAA = false;
- bool doLCD = false;
-
- if (SkMask::kBW_Format != glyph.maskFormat()) {
- doLCD = true;
- doAA = true;
- }
-
- // FIXME: lcd smoothed un-hinted rasterization unsupported.
- if (!generateA8FromLCD && SkMask::kA8_Format == glyph.maskFormat()) {
- doLCD = false;
- doAA = true;
- }
-
- // If this font might have color glyphs, disable LCD as there's no way to support it.
- // CoreText doesn't tell us which format it ended up using, so we can't detect it.
- // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
- if (SkMask::kARGB32_Format == glyph.maskFormat()) {
- doLCD = false;
- }
-
- size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
- if (!fCG || fSize.fWidth < glyph.width() || fSize.fHeight < glyph.height()) {
- if (fSize.fWidth < glyph.width()) {
- fSize.fWidth = RoundSize(glyph.width());
- }
- if (fSize.fHeight < glyph.height()) {
- fSize.fHeight = RoundSize(glyph.height());
- }
-
- rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
- void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
- const CGImageAlphaInfo alpha = (glyph.isColor())
- ? kCGImageAlphaPremultipliedFirst
- : kCGImageAlphaNoneSkipFirst;
- const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
- fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
- rowBytes, fRGBSpace.get(), bitmapInfo));
-
- // Skia handles quantization and subpixel positioning,
- // so disable quantization and enabe subpixel positioning in CG.
- CGContextSetAllowsFontSubpixelQuantization(fCG.get(), false);
- CGContextSetShouldSubpixelQuantizeFonts(fCG.get(), false);
-
- // Because CG always draws from the horizontal baseline,
- // if there is a non-integral translation from the horizontal origin to the vertical origin,
- // then CG cannot draw the glyph in the correct location without subpixel positioning.
- CGContextSetAllowsFontSubpixelPositioning(fCG.get(), true);
- CGContextSetShouldSubpixelPositionFonts(fCG.get(), true);
-
- CGContextSetTextDrawingMode(fCG.get(), kCGTextFill);
-
- // Draw black on white to create mask. (Special path exists to speed this up in CG.)
- CGContextSetGrayFillColor(fCG.get(), 0.0f, 1.0f);
-
- // force our checks below to happen
- fDoAA = !doAA;
- fDoLCD = !doLCD;
-
- CGContextSetTextMatrix(fCG.get(), context.fTransform);
- }
-
- if (fDoAA != doAA) {
- CGContextSetShouldAntialias(fCG.get(), doAA);
- fDoAA = doAA;
- }
- if (fDoLCD != doLCD) {
- CGContextSetShouldSmoothFonts(fCG.get(), doLCD);
- fDoLCD = doLCD;
- }
-
- CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
- // skip rows based on the glyph's height
- image += (fSize.fHeight - glyph.height()) * fSize.fWidth;
-
- // Erase to white (or transparent black if it's a color glyph, to not composite against white).
- uint32_t bgColor = (!glyph.isColor()) ? 0xFFFFFFFF : 0x00000000;
- sk_memset_rect32(image, bgColor, glyph.width(), glyph.height(), rowBytes);
-
- float subX = 0;
- float subY = 0;
- if (context.fDoSubPosition) {
- subX = SkFixedToFloat(glyph.getSubXFixed());
- subY = SkFixedToFloat(glyph.getSubYFixed());
- }
-
- CGPoint point = CGPointMake(-glyph.left() + subX, glyph.top() + glyph.height() - subY);
- // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
- // 'positions' which are in text space. The glyph location (in device space) must be
- // mapped into text space, so that CG can convert it back into device space.
- // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
- //
- // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
- // So always make the font transform identity and place the transform on the context.
- point = CGPointApplyAffineTransform(point, context.fInvTransform);
-
- CTFontDrawGlyphs(context.fCTFont.get(), &glyphID, &point, 1, fCG.get());
-
- SkASSERT(rowBytesPtr);
- *rowBytesPtr = rowBytes;
- return image;
-}
-
-unsigned SkScalerContext_Mac::generateGlyphCount(void) {
- return fGlyphCount;
-}
-
-bool SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
- return false;
-}
-
-void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
- glyph->fMaskFormat = fRec.fMaskFormat;
-
- const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
- glyph->zeroMetrics();
-
- // The following block produces cgAdvance in CG units (pixels, y up).
- CGSize cgAdvance;
- CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
- &cgGlyph, &cgAdvance, 1);
- cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
- glyph->fAdvanceX = CGToFloat(cgAdvance.width);
- glyph->fAdvanceY = -CGToFloat(cgAdvance.height);
-
- // The following produces skBounds in SkGlyph units (pixels, y down),
- // or returns early if skBounds would be empty.
- SkRect skBounds;
-
- // Glyphs are always drawn from the horizontal origin. The caller must manually use the result
- // of CTFontGetVerticalTranslationsForGlyphs to calculate where to draw the glyph for vertical
- // glyphs. As a result, always get the horizontal bounds of a glyph and translate it if the
- // glyph is vertical. This avoids any diagreement between the various means of retrieving
- // vertical metrics.
- {
- // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
- CGRect cgBounds;
- CTFontGetBoundingRectsForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
- &cgGlyph, &cgBounds, 1);
- cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
-
- // BUG?
- // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
- // it should be empty. So, if we see a zero-advance, we check if it has an
- // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
- // is rare, so we won't incur a big performance cost for this extra check.
- if (0 == cgAdvance.width && 0 == cgAdvance.height) {
- SkUniqueCFRef<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph,nullptr));
- if (!path || CGPathIsEmpty(path.get())) {
- return;
- }
- }
-
- if (CGRectIsEmpty_inline(cgBounds)) {
- return;
- }
-
- // Convert cgBounds to SkGlyph units (pixels, y down).
- skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
- cgBounds.size.width, cgBounds.size.height);
- }
-
- // Currently the bounds are based on being rendered at (0,0).
- // The top left must not move, since that is the base from which subpixel positioning is offset.
- if (fDoSubPosition) {
- skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
- skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
- }
-
- // We're trying to pack left and top into int16_t,
- // and width and height into uint16_t, after outsetting by 1.
- if (!SkRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(skBounds)) {
- return;
- }
-
- SkIRect skIBounds;
- skBounds.roundOut(&skIBounds);
- // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
- // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
- // is not currently known, as CG dilates the outlines by some percentage.
- // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
- skIBounds.outset(1, 1);
- glyph->fLeft = SkToS16(skIBounds.fLeft);
- glyph->fTop = SkToS16(skIBounds.fTop);
- glyph->fWidth = SkToU16(skIBounds.width());
- glyph->fHeight = SkToU16(skIBounds.height());
-}
-
-static constexpr uint8_t sk_pow2_table(size_t i) {
- return SkToU8(((i * i + 128) / 255));
-}
-
-/**
- * This will invert the gamma applied by CoreGraphics, so we can get linear
- * values.
- *
- * CoreGraphics obscurely defaults to 2.0 as the subpixel coverage gamma value.
- * The color space used does not appear to affect this choice.
- */
-static constexpr auto gLinearCoverageFromCGLCDValue = SkMakeArray<256>(sk_pow2_table);
-
-static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
- while (count > 0) {
- uint8_t mask = 0;
- for (int i = 7; i >= 0; --i) {
- mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
- if (0 == --count) {
- break;
- }
- }
- *dst++ = mask;
- }
-}
-
-template<bool APPLY_PREBLEND>
-static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
- U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
- U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
- U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
- U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
-#if SK_SHOW_TEXT_BLIT_COVERAGE
- lum = std::max(lum, (U8CPU)0x30);
-#endif
- return lum;
-}
-
-template<bool APPLY_PREBLEND>
-void SkScalerContext_Mac::RGBToA8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
- const SkGlyph& glyph, const uint8_t* table8) {
- const int width = glyph.fWidth;
- size_t dstRB = glyph.rowBytes();
- uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
-
- for (int y = 0; y < glyph.fHeight; y++) {
- for (int i = 0; i < width; ++i) {
- dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
- }
- cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
- dst = SkTAddOffset<uint8_t>(dst, dstRB);
- }
-}
-
-template<bool APPLY_PREBLEND>
-uint16_t SkScalerContext_Mac::RGBToLcd16(CGRGBPixel rgb, const uint8_t* tableR,
- const uint8_t* tableG,
- const uint8_t* tableB) {
- U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
- U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
- U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
-#if SK_SHOW_TEXT_BLIT_COVERAGE
- r = std::max(r, (U8CPU)0x30);
- g = std::max(g, (U8CPU)0x30);
- b = std::max(b, (U8CPU)0x30);
-#endif
- return SkPack888ToRGB16(r, g, b);
-}
-
-template<bool APPLY_PREBLEND>
-void SkScalerContext_Mac::RGBToLcd16(const CGRGBPixel* SK_RESTRICT cgPixels,
- size_t cgRowBytes,
- const SkGlyph& glyph,
- const uint8_t* tableR,
- const uint8_t* tableG,
- const uint8_t* tableB) {
- const int width = glyph.fWidth;
- size_t dstRB = glyph.rowBytes();
- uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
-
- for (int y = 0; y < glyph.fHeight; y++) {
- for (int i = 0; i < width; i++) {
- dst[i] = RGBToLcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
- }
- cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
- dst = SkTAddOffset<uint16_t>(dst, dstRB);
- }
-}
-
-static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
- U8CPU a = (rgb >> 24) & 0xFF;
- U8CPU r = (rgb >> 16) & 0xFF;
- U8CPU g = (rgb >> 8) & 0xFF;
- U8CPU b = (rgb >> 0) & 0xFF;
-#if SK_SHOW_TEXT_BLIT_COVERAGE
- a = std::max(a, (U8CPU)0x30);
-#endif
- return SkPackARGB32(a, r, g, b);
-}
-
-void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
- CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
-
- // FIXME: lcd smoothed un-hinted rasterization unsupported.
- bool requestSmooth = fRec.getHinting() != SkFontHinting::kNone;
-
- // Draw the glyph
- size_t cgRowBytes;
- CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, requestSmooth);
- if (cgPixels == nullptr) {
- return;
- }
-
- // Fix the glyph
- if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
- (glyph.fMaskFormat == SkMask::kA8_Format
- && requestSmooth
- && smooth_behavior() != SmoothBehavior::none))
- {
- const uint8_t* linear = gLinearCoverageFromCGLCDValue.data();
-
- //Note that the following cannot really be integrated into the
- //pre-blend, since we may not be applying the pre-blend; when we aren't
- //applying the pre-blend it means that a filter wants linear anyway.
- //Other code may also be applying the pre-blend, so we'd need another
- //one with this and one without.
- CGRGBPixel* addr = cgPixels;
- for (int y = 0; y < glyph.fHeight; ++y) {
- for (int x = 0; x < glyph.fWidth; ++x) {
- int r = (addr[x] >> 16) & 0xFF;
- int g = (addr[x] >> 8) & 0xFF;
- int b = (addr[x] >> 0) & 0xFF;
- addr[x] = (linear[r] << 16) | (linear[g] << 8) | linear[b];
- }
- addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
- }
- }
-
- // Convert glyph to mask
- switch (glyph.fMaskFormat) {
- case SkMask::kLCD16_Format: {
- if (fPreBlend.isApplicable()) {
- RGBToLcd16<true>(cgPixels, cgRowBytes, glyph,
- fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
- } else {
- RGBToLcd16<false>(cgPixels, cgRowBytes, glyph,
- fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
- }
- } break;
- case SkMask::kA8_Format: {
- if (fPreBlend.isApplicable()) {
- RGBToA8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
- } else {
- RGBToA8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
- }
- } break;
- case SkMask::kBW_Format: {
- const int width = glyph.fWidth;
- size_t dstRB = glyph.rowBytes();
- uint8_t* dst = (uint8_t*)glyph.fImage;
- for (int y = 0; y < glyph.fHeight; y++) {
- cgpixels_to_bits(dst, cgPixels, width);
- cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
- dst = SkTAddOffset<uint8_t>(dst, dstRB);
- }
- } break;
- case SkMask::kARGB32_Format: {
- const int width = glyph.fWidth;
- size_t dstRB = glyph.rowBytes();
- SkPMColor* dst = (SkPMColor*)glyph.fImage;
- for (int y = 0; y < glyph.fHeight; y++) {
- for (int x = 0; x < width; ++x) {
- dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
- }
- cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
- dst = SkTAddOffset<SkPMColor>(dst, dstRB);
- }
- } break;
- default:
- SkDEBUGFAIL("unexpected mask format");
- break;
- }
-}
-
-namespace {
-class SkCTPathGeometrySink {
- SkPath* fPath;
- bool fStarted;
- CGPoint fCurrent;
-
- void goingTo(const CGPoint pt) {
- if (!fStarted) {
- fStarted = true;
- fPath->moveTo(fCurrent.x, -fCurrent.y);
- }
- fCurrent = pt;
- }
-
- bool currentIsNot(const CGPoint pt) {
- return fCurrent.x != pt.x || fCurrent.y != pt.y;
- }
-
-public:
- SkCTPathGeometrySink(SkPath* path) : fPath{path}, fStarted{false}, fCurrent{0,0} {}
- static void ApplyElement(void *ctx, const CGPathElement *element) {
- SkCTPathGeometrySink& self = *(SkCTPathGeometrySink*)ctx;
- CGPoint* points = element->points;
-
- switch (element->type) {
- case kCGPathElementMoveToPoint:
- self.fStarted = false;
- self.fCurrent = points[0];
- break;
-
- case kCGPathElementAddLineToPoint:
- if (self.currentIsNot(points[0])) {
- self.goingTo(points[0]);
- self.fPath->lineTo(points[0].x, -points[0].y);
- }
- break;
-
- case kCGPathElementAddQuadCurveToPoint:
- if (self.currentIsNot(points[0]) || self.currentIsNot(points[1])) {
- self.goingTo(points[1]);
- self.fPath->quadTo(points[0].x, -points[0].y,
- points[1].x, -points[1].y);
- }
- break;
-
- case kCGPathElementAddCurveToPoint:
- if (self.currentIsNot(points[0]) ||
- self.currentIsNot(points[1]) ||
- self.currentIsNot(points[2]))
- {
- self.goingTo(points[2]);
- self.fPath->cubicTo(points[0].x, -points[0].y,
- points[1].x, -points[1].y,
- points[2].x, -points[2].y);
- }
- break;
-
- case kCGPathElementCloseSubpath:
- if (self.fStarted) {
- self.fPath->close();
- }
- break;
-
- default:
- SkDEBUGFAIL("Unknown path element!");
- break;
- }
- }
-};
-} // namespace
-
-/*
- * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
- * seems sufficient, and possibly even correct, to allow the hinted outline
- * to be subpixel positioned.
- */
-#define kScaleForSubPixelPositionHinting (4.0f)
-
-bool SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
- SkScalar scaleX = SK_Scalar1;
- SkScalar scaleY = SK_Scalar1;
-
- CGAffineTransform xform = fTransform;
- /*
- * For subpixel positioning, we want to return an unhinted outline, so it
- * can be positioned nicely at fractional offsets. However, we special-case
- * if the baseline of the (horizontal) text is axis-aligned. In those cases
- * we want to retain hinting in the direction orthogonal to the baseline.
- * e.g. for horizontal baseline, we want to retain hinting in Y.
- * The way we remove hinting is to scale the font by some value (4) in that
- * direction, ask for the path, and then scale the path back down.
- */
- if (fDoSubPosition) {
- // start out by assuming that we want no hining in X and Y
- scaleX = scaleY = kScaleForSubPixelPositionHinting;
- // now see if we need to restore hinting for axis-aligned baselines
- switch (this->computeAxisAlignmentForHText()) {
- case kX_SkAxisAlignment:
- scaleY = SK_Scalar1; // want hinting in the Y direction
- break;
- case kY_SkAxisAlignment:
- scaleX = SK_Scalar1; // want hinting in the X direction
- break;
- default:
- break;
- }
-
- CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
- xform = CGAffineTransformConcat(fTransform, scale);
- }
-
- CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
- SkUniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
-
- path->reset();
- if (!cgPath) {
- return false;
- }
-
- SkCTPathGeometrySink sink(path);
- CGPathApply(cgPath.get(), &sink, SkCTPathGeometrySink::ApplyElement);
- if (fDoSubPosition) {
- SkMatrix m;
- m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
- path->transform(m);
- }
- return true;
-}
-
-void SkScalerContext_Mac::generateFontMetrics(SkFontMetrics* metrics) {
- if (nullptr == metrics) {
- return;
- }
-
- CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
-
- metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
- metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont.get()));
- metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont.get()));
- metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
- metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont.get()));
- metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
- metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
- metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
- metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
- metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont.get()));
- metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont.get()));
- metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont.get()));
- metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont.get()));
-
- metrics->fFlags = 0;
- metrics->fFlags |= SkFontMetrics::kUnderlineThicknessIsValid_Flag;
- metrics->fFlags |= SkFontMetrics::kUnderlinePositionIsValid_Flag;
-
- // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
- // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent and
- // the cap-height is always 0.8888 of the ascent. It appears that the values from the 'OS/2'
- // table are read, but then overwritten if the font is not a system font. As a result, if there
- // is a valid 'OS/2' table available use the values from the table if they aren't too strange.
- struct OS2HeightMetrics {
- SK_OT_SHORT sxHeight;
- SK_OT_SHORT sCapHeight;
- } heights;
- size_t bytesRead = this->getTypeface()->getTableData(
- SkTEndian_SwapBE32(SkOTTableOS2::TAG), offsetof(SkOTTableOS2, version.v2.sxHeight),
- sizeof(heights), &heights);
- if (bytesRead == sizeof(heights)) {
- // 'fontSize' is correct because the entire resolved size is set by the constructor.
- CGFloat fontSize = CTFontGetSize(this->fCTFont.get());
- unsigned upem = CTFontGetUnitsPerEm(this->fCTFont.get());
- unsigned maxSaneHeight = upem * 2;
- uint16_t xHeight = SkEndian_SwapBE16(heights.sxHeight);
- if (xHeight && xHeight < maxSaneHeight) {
- metrics->fXHeight = CGToScalar(xHeight * fontSize / upem);
- }
- uint16_t capHeight = SkEndian_SwapBE16(heights.sCapHeight);
- if (capHeight && capHeight < maxSaneHeight) {
- metrics->fCapHeight = CGToScalar(capHeight * fontSize / upem);
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-// Web fonts added to the CTFont registry do not return their character set.
-// Iterate through the font in this case. The existing caller caches the result,
-// so the performance impact isn't too bad.
-static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
- SkUnichar* out) {
- sk_bzero(out, glyphCount * sizeof(SkUnichar));
- UniChar unichar = 0;
- while (glyphCount > 0) {
- CGGlyph glyph;
- if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
- if (out[glyph] == 0) {
- out[glyph] = unichar;
- --glyphCount;
- }
- }
- if (++unichar == 0) {
- break;
- }
- }
-}
-
-static constexpr uint16_t kPlaneSize = 1 << 13;
-
-static void get_plane_glyph_map(const uint8_t* bits,
- CTFontRef ctFont,
- CFIndex glyphCount,
- SkUnichar* glyphToUnicode,
- uint8_t planeIndex) {
- SkUnichar planeOrigin = (SkUnichar)planeIndex << 16; // top half of codepoint.
- for (uint16_t i = 0; i < kPlaneSize; i++) {
- uint8_t mask = bits[i];
- if (!mask) {
- continue;
- }
- for (uint8_t j = 0; j < 8; j++) {
- if (0 == (mask & ((uint8_t)1 << j))) {
- continue;
- }
- uint16_t planeOffset = (i << 3) | j;
- SkUnichar codepoint = planeOrigin | (SkUnichar)planeOffset;
- uint16_t utf16[2] = {planeOffset, 0};
- size_t count = 1;
- if (planeOrigin != 0) {
- count = SkUTF::ToUTF16(codepoint, utf16);
- }
- CGGlyph glyphs[2] = {0, 0};
- if (CTFontGetGlyphsForCharacters(ctFont, utf16, glyphs, count)) {
- SkASSERT(glyphs[1] == 0);
- SkASSERT(glyphs[0] < glyphCount);
- // CTFontCopyCharacterSet and CTFontGetGlyphsForCharacters seem to add 'support'
- // for characters 0x9, 0xA, and 0xD mapping them to the glyph for character 0x20?
- // Prefer mappings to codepoints at or above 0x20.
- if (glyphToUnicode[glyphs[0]] < 0x20) {
- glyphToUnicode[glyphs[0]] = codepoint;
- }
- }
- }
- }
-}
-// Construct Glyph to Unicode table.
-static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
- SkUnichar* glyphToUnicode) {
- sk_bzero(glyphToUnicode, sizeof(SkUnichar) * glyphCount);
- SkUniqueCFRef<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
- if (!charSet) {
- populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
- return;
- }
-
- SkUniqueCFRef<CFDataRef> bitmap(
- CFCharacterSetCreateBitmapRepresentation(nullptr, charSet.get()));
- if (!bitmap) {
- return;
- }
- CFIndex dataLength = CFDataGetLength(bitmap.get());
- if (!dataLength) {
- return;
- }
- SkASSERT(dataLength >= kPlaneSize);
- const UInt8* bits = CFDataGetBytePtr(bitmap.get());
-
- get_plane_glyph_map(bits, ctFont, glyphCount, glyphToUnicode, 0);
- /*
- A CFData object that specifies the bitmap representation of the Unicode
- character points the for the new character set. The bitmap representation could
- contain all the Unicode character range starting from BMP to Plane 16. The
- first 8KiB (8192 bytes) of the data represent the BMP range. The BMP range 8KiB
- can be followed by zero to sixteen 8KiB bitmaps, each prepended with the plane
- index byte. For example, the bitmap representing the BMP and Plane 2 has the
- size of 16385 bytes (8KiB for BMP, 1 byte index, and a 8KiB bitmap for Plane
- 2). The plane index byte, in this case, contains the integer value two.
- */
-
- if (dataLength <= kPlaneSize) {
- return;
- }
- int extraPlaneCount = (dataLength - kPlaneSize) / (1 + kPlaneSize);
- SkASSERT(dataLength == kPlaneSize + extraPlaneCount * (1 + kPlaneSize));
- while (extraPlaneCount-- > 0) {
- bits += kPlaneSize;
- uint8_t planeIndex = *bits++;
- SkASSERT(planeIndex >= 1);
- SkASSERT(planeIndex <= 16);
- get_plane_glyph_map(bits, ctFont, glyphCount, glyphToUnicode, planeIndex);
- }
-}
-
-/** Assumes src and dst are not nullptr. */
-static void CFStringToSkString(CFStringRef src, SkString* dst) {
- // Reserve enough room for the worst-case string,
- // plus 1 byte for the trailing null.
- CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
- kCFStringEncodingUTF8) + 1;
- dst->resize(length);
- CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
- // Resize to the actual UTF-8 length used, stripping the null character.
- dst->resize(strlen(dst->c_str()));
-}
-
-void SkTypeface_Mac::getGlyphToUnicodeMap(SkUnichar* dstArray) const {
- SkUniqueCFRef<CTFontRef> ctFont =
- ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
- fOpszVariation);
- CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
- populate_glyph_to_unicode(ctFont.get(), glyphCount, dstArray);
-}
-
-std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
-
- SkUniqueCFRef<CTFontRef> ctFont =
- ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
- fOpszVariation);
-
- std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
-
- {
- SkUniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
- if (fontName.get()) {
- CFStringToSkString(fontName.get(), &info->fPostScriptName);
- info->fFontName = info->fPostScriptName;
- }
- }
-
- // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
- // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
- // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
- // CGFontCopyVariations here until support for 10.10 and earlier is removed.
- SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont.get(), nullptr));
- if (cgFont) {
- SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
- if (cgAxes && CFArrayGetCount(cgAxes.get()) > 0) {
- info->fFlags |= SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag;
- }
- }
-
- SkOTTableOS2_V4::Type fsType;
- if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
- offsetof(SkOTTableOS2_V4, fsType),
- sizeof(fsType),
- &fsType)) {
- SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
- }
-
- // If it's not a truetype font, mark it as 'other'. Assume that TrueType
- // fonts always have both glyf and loca tables. At the least, this is what
- // sfntly needs to subset the font. CTFontCopyAttribute() does not always
- // succeed in determining this directly.
- if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
- return info;
- }
-
- info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
- CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont.get());
- if (symbolicTraits & kCTFontMonoSpaceTrait) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
- }
- if (symbolicTraits & kCTFontItalicTrait) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
- }
- CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
- if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
- } else if (stylisticClass & kCTFontScriptsClass) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
- }
- info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont.get());
- info->fAscent = (int16_t) CTFontGetAscent(ctFont.get());
- info->fDescent = (int16_t) CTFontGetDescent(ctFont.get());
- info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont.get());
- CGRect bbox = CTFontGetBoundingBox(ctFont.get());
-
- SkRect r;
- r.setLTRB(CGToScalar(CGRectGetMinX_inline(bbox)), // Left
- CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
- CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
- CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
-
- r.roundOut(&(info->fBBox));
-
- // Figure out a good guess for StemV - Min width of i, I, !, 1.
- // This probably isn't very good with an italic font.
- int16_t min_width = SHRT_MAX;
- info->fStemV = 0;
- static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
- const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
- CGGlyph glyphs[count];
- CGRect boundingRects[count];
- if (CTFontGetGlyphsForCharacters(ctFont.get(), stem_chars, glyphs, count)) {
- CTFontGetBoundingRectsForGlyphs(ctFont.get(), kCTFontOrientationHorizontal,
- glyphs, boundingRects, count);
- for (size_t i = 0; i < count; i++) {
- int16_t width = (int16_t) boundingRects[i].size.width;
- if (width > 0 && width < min_width) {
- min_width = width;
- info->fStemV = min_width;
- }
- }
- }
- return info;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SK_SFNT_ULONG get_font_type_tag(CTFontRef ctFont) {
- SkUniqueCFRef<CFNumberRef> fontFormatRef(
- static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
- if (!fontFormatRef) {
- return 0;
- }
-
- SInt32 fontFormatValue;
- if (!CFNumberGetValue(fontFormatRef.get(), kCFNumberSInt32Type, &fontFormatValue)) {
- return 0;
- }
-
- switch (fontFormatValue) {
- case kCTFontFormatOpenTypePostScript:
- return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
- case kCTFontFormatOpenTypeTrueType:
- return SkSFNTHeader::fontType_WindowsTrueType::TAG;
- case kCTFontFormatTrueType:
- return SkSFNTHeader::fontType_MacTrueType::TAG;
- case kCTFontFormatPostScript:
- return SkSFNTHeader::fontType_PostScript::TAG;
- case kCTFontFormatBitmap:
- return SkSFNTHeader::fontType_MacTrueType::TAG;
- case kCTFontFormatUnrecognized:
- default:
- return 0;
- }
-}
-
-std::unique_ptr<SkStreamAsset> SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
- *ttcIndex = 0;
-
- fInitStream([this]{
- if (fStream) {
- return;
- }
-
- SK_SFNT_ULONG fontType = get_font_type_tag(fFontRef.get());
-
- // get table tags
- int numTables = this->countTables();
- SkTDArray<SkFontTableTag> tableTags;
- tableTags.setCount(numTables);
- this->getTableTags(tableTags.begin());
-
- // CT seems to be unreliable in being able to obtain the type,
- // even if all we want is the first four bytes of the font resource.
- // Just the presence of the FontForge 'FFTM' table seems to throw it off.
- if (fontType == 0) {
- fontType = SkSFNTHeader::fontType_WindowsTrueType::TAG;
-
- // see https://skbug.com/7630#c7
- bool couldBeCFF = false;
- constexpr SkFontTableTag CFFTag = SkSetFourByteTag('C', 'F', 'F', ' ');
- constexpr SkFontTableTag CFF2Tag = SkSetFourByteTag('C', 'F', 'F', '2');
- for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
- if (CFFTag == tableTags[tableIndex] || CFF2Tag == tableTags[tableIndex]) {
- couldBeCFF = true;
- }
- }
- if (couldBeCFF) {
- fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
- }
- }
-
- // Sometimes CoreGraphics incorrectly thinks a font is kCTFontFormatPostScript.
- // It is exceedingly unlikely that this is the case, so double check
- // (see https://crbug.com/809763 ).
- if (fontType == SkSFNTHeader::fontType_PostScript::TAG) {
- // see if there are any required 'typ1' tables (see Adobe Technical Note #5180)
- bool couldBeTyp1 = false;
- constexpr SkFontTableTag TYPE1Tag = SkSetFourByteTag('T', 'Y', 'P', '1');
- constexpr SkFontTableTag CIDTag = SkSetFourByteTag('C', 'I', 'D', ' ');
- for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
- if (TYPE1Tag == tableTags[tableIndex] || CIDTag == tableTags[tableIndex]) {
- couldBeTyp1 = true;
- }
- }
- if (!couldBeTyp1) {
- fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
- }
- }
-
- // get the table sizes and accumulate the total size of the font
- SkTDArray<size_t> tableSizes;
- size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
- for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
- size_t tableSize = this->getTableSize(tableTags[tableIndex]);
- totalSize += (tableSize + 3) & ~3;
- *tableSizes.append() = tableSize;
- }
-
- // reserve memory for stream, and zero it (tables must be zero padded)
- fStream.reset(new SkMemoryStream(totalSize));
- char* dataStart = (char*)fStream->getMemoryBase();
- sk_bzero(dataStart, totalSize);
- char* dataPtr = dataStart;
-
- // compute font header entries
- uint16_t entrySelector = 0;
- uint16_t searchRange = 1;
- while (searchRange < numTables >> 1) {
- entrySelector++;
- searchRange <<= 1;
- }
- searchRange <<= 4;
- uint16_t rangeShift = (numTables << 4) - searchRange;
-
- // write font header
- SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
- header->fontType = fontType;
- header->numTables = SkEndian_SwapBE16(numTables);
- header->searchRange = SkEndian_SwapBE16(searchRange);
- header->entrySelector = SkEndian_SwapBE16(entrySelector);
- header->rangeShift = SkEndian_SwapBE16(rangeShift);
- dataPtr += sizeof(SkSFNTHeader);
-
- // write tables
- SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
- dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
- for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
- size_t tableSize = tableSizes[tableIndex];
- this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
- entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
- entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
- tableSize));
- entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
- entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
-
- dataPtr += (tableSize + 3) & ~3;
- ++entry;
- }
- });
- return fStream->duplicate();
-}
-
-struct NonDefaultAxesContext {
- SkFixed* axisValue;
- CFArrayRef cgAxes;
-};
-static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
- NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
-
- if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
- return;
- }
-
- // The key is a CFString which is a string from the 'name' table.
- // Search the cgAxes for an axis with this name, and use its index to store the value.
- CFIndex keyIndex = -1;
- CFStringRef keyString = static_cast<CFStringRef>(key);
- for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
- CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
- if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
- continue;
- }
-
- CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
- CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
- if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
- continue;
- }
- CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
- if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
- keyIndex = i;
- break;
- }
- }
- if (keyIndex == -1) {
- return;
- }
-
- CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
- double valueDouble;
- if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
- valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
- {
- return;
- }
- self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
-}
-static bool get_variations(CTFontRef ctFont, CFIndex* cgAxisCount,
- SkAutoSTMalloc<4, SkFixed>* axisValues)
-{
- // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
- // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
- // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
- // CGFontCopyVariations here until support for 10.10 and earlier is removed.
- SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
- if (!cgFont) {
- return false;
- }
-
- SkUniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
- // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
- if (!cgVariations) {
- return false;
- }
-
- SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
- if (!cgAxes) {
- return false;
- }
- *cgAxisCount = CFArrayGetCount(cgAxes.get());
- axisValues->reset(*cgAxisCount);
-
- // Set all of the axes to their default values.
- // Fail if any default value cannot be determined.
- for (CFIndex i = 0; i < *cgAxisCount; ++i) {
- CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes.get(), i);
- if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
- return false;
- }
-
- CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
- CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
- kCGFontVariationAxisDefaultValue);
- if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
- return false;
- }
- CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
- double axisDefaultValueDouble;
- if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
- {
- return false;
- }
- if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
- SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
- {
- return false;
- }
- (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
- }
-
- // Override the default values with the given font's stated axis values.
- NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
- CFDictionaryApplyFunction(cgVariations.get(), set_non_default_axes, &c);
-
- return true;
-}
-std::unique_ptr<SkFontData> SkTypeface_Mac::onMakeFontData() const {
- int index;
- std::unique_ptr<SkStreamAsset> stream(this->onOpenStream(&index));
-
- CFIndex cgAxisCount;
- SkAutoSTMalloc<4, SkFixed> axisValues;
- if (get_variations(fFontRef.get(), &cgAxisCount, &axisValues)) {
- return std::make_unique<SkFontData>(std::move(stream), index,
- axisValues.get(), cgAxisCount);
- }
- return std::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
-}
-
-/** Creates a CT variation dictionary {tag, value} from a CG variation dictionary {name, value}. */
-static SkUniqueCFRef<CFDictionaryRef> ct_variation_from_cg_variation(CFDictionaryRef cgVariations,
- CFArrayRef ctAxes) {
-
- SkUniqueCFRef<CFMutableDictionaryRef> ctVariation(
- CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
-
- CFIndex axisCount = CFArrayGetCount(ctAxes);
- for (CFIndex i = 0; i < axisCount; ++i) {
- CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes, i);
- if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
- return nullptr;
- }
- CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
-
- // The assumption is that values produced by kCTFontVariationAxisNameKey and
- // kCGFontVariationAxisName will always be equal.
- CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
- if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
- return nullptr;
- }
-
- CFTypeRef axisValue = CFDictionaryGetValue(cgVariations, axisName);
- if (!axisValue || CFGetTypeID(axisValue) != CFNumberGetTypeID()) {
- return nullptr;
- }
-
- CFTypeRef axisTag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
- if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) {
- return nullptr;
- }
-
- CFDictionaryAddValue(ctVariation.get(), axisTag, axisValue);
- }
- return std::move(ctVariation);
-}
-
-int SkTypeface_Mac::onGetVariationDesignPosition(
- SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
-{
- // The CGFont variation data does not contain the tag.
-
- // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
- // macOS 10.10 and iOS 9 or earlier. When this happens, there is no API to provide the tag.
- SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
- if (!ctAxes) {
- return -1;
- }
- CFIndex axisCount = CFArrayGetCount(ctAxes.get());
- if (!coordinates || coordinateCount < axisCount) {
- return axisCount;
- }
-
- // This call always returns nullptr on 10.11 and under for CGFontCreateWithDataProvider fonts.
- // When this happens, try converting the CG variation to a CT variation.
- // On 10.12 and later, this only returns non-default variations.
- SkUniqueCFRef<CFDictionaryRef> ctVariation(CTFontCopyVariation(fFontRef.get()));
- if (!ctVariation) {
- // When 10.11 and earlier are no longer supported, the following code can be replaced with
- // return -1 and ct_variation_from_cg_variation can be removed.
- SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
- if (!cgFont) {
- return -1;
- }
- SkUniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
- if (!cgVariations) {
- return -1;
- }
- ctVariation = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get());
- if (!ctVariation) {
- return -1;
- }
- }
-
- for (int i = 0; i < axisCount; ++i) {
- CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
- if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
- return -1;
- }
- CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
-
- CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
- if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
- return -1;
- }
- CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
- int64_t tagLong;
- if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
- return -1;
- }
- coordinates[i].axis = tagLong;
-
- CGFloat variationCGFloat;
- CFTypeRef variationValue = CFDictionaryGetValue(ctVariation.get(), tagNumber);
- if (variationValue) {
- if (CFGetTypeID(variationValue) != CFNumberGetTypeID()) {
- return -1;
- }
- CFNumberRef variationNumber = static_cast<CFNumberRef>(variationValue);
- if (!CFNumberGetValue(variationNumber, kCFNumberCGFloatType, &variationCGFloat)) {
- return -1;
- }
- } else {
- CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
- if (!def || CFGetTypeID(def) != CFNumberGetTypeID()) {
- return -1;
- }
- CFNumberRef defNumber = static_cast<CFNumberRef>(def);
- if (!CFNumberGetValue(defNumber, kCFNumberCGFloatType, &variationCGFloat)) {
- return -1;
- }
- }
- coordinates[i].value = CGToScalar(variationCGFloat);
-
- }
- return axisCount;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-int SkTypeface_Mac::onGetUPEM() const {
- SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
- return CGFontGetUnitsPerEm(cgFont.get());
-}
-
-SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
- sk_sp<SkTypeface::LocalizedStrings> nameIter =
- SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(*this);
- if (!nameIter) {
- CFStringRef cfLanguageRaw;
- SkUniqueCFRef<CFStringRef> cfFamilyName(
- CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
- SkUniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
-
- SkString skLanguage;
- SkString skFamilyName;
- if (cfLanguage) {
- CFStringToSkString(cfLanguage.get(), &skLanguage);
- } else {
- skLanguage = "und"; //undetermined
- }
- if (cfFamilyName) {
- CFStringToSkString(cfFamilyName.get(), &skFamilyName);
- }
-
- nameIter = sk_make_sp<SkOTUtils::LocalizedStrings_SingleName>(skFamilyName, skLanguage);
- }
- return nameIter.release();
-}
-
-int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
- SkUniqueCFRef<CFArrayRef> cfArray(
- CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
- if (!cfArray) {
- return 0;
- }
- int count = SkToInt(CFArrayGetCount(cfArray.get()));
- if (tags) {
- for (int i = 0; i < count; ++i) {
- uintptr_t fontTag = reinterpret_cast<uintptr_t>(
- CFArrayGetValueAtIndex(cfArray.get(), i));
- tags[i] = static_cast<SkFontTableTag>(fontTag);
- }
- }
- return count;
-}
-
-// If, as is the case with web fonts, the CTFont data isn't available,
-// the CGFont data may work. While the CGFont may always provide the
-// right result, leave the CTFont code path to minimize disruption.
-static SkUniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
- SkUniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
- kCTFontTableOptionNoOptions));
- if (!data) {
- SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
- data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
- }
- return data;
-}
-
-size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
- size_t length, void* dstData) const {
- SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
- if (!srcData) {
- return 0;
- }
-
- size_t srcSize = CFDataGetLength(srcData.get());
- if (offset >= srcSize) {
- return 0;
- }
- if (length > srcSize - offset) {
- length = srcSize - offset;
- }
- if (dstData) {
- memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
- }
- return length;
-}
-
-sk_sp<SkData> SkTypeface_Mac::onCopyTableData(SkFontTableTag tag) const {
- SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
- if (!srcData) {
- return nullptr;
- }
- const UInt8* data = CFDataGetBytePtr(srcData.get());
- CFIndex length = CFDataGetLength(srcData.get());
- return SkData::MakeWithProc(data, length,
- [](const void*, void* ctx) {
- CFRelease((CFDataRef)ctx);
- }, (void*)srcData.release());
-}
-
-SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkScalerContextEffects& effects,
- const SkDescriptor* desc) const {
- return new SkScalerContext_Mac(sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
-}
-
-void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
- if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
- rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
- {
- rec->fMaskFormat = SkMask::kA8_Format;
- // Render the glyphs as close as possible to what was requested.
- // The above turns off subpixel rendering, but the user requested it.
- // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
- // See comments below for more details.
- rec->setHinting(SkFontHinting::kNormal);
- }
-
- unsigned flagsWeDontSupport = SkScalerContext::kForceAutohinting_Flag |
- SkScalerContext::kLCD_BGROrder_Flag |
- SkScalerContext::kLCD_Vertical_Flag;
-
- rec->fFlags &= ~flagsWeDontSupport;
-
- const SmoothBehavior smoothBehavior = smooth_behavior();
-
- // Only two levels of hinting are supported.
- // kNo_Hinting means avoid CoreGraphics outline dilation (smoothing).
- // kNormal_Hinting means CoreGraphics outline dilation (smoothing) is allowed.
- if (rec->getHinting() != SkFontHinting::kNone) {
- rec->setHinting(SkFontHinting::kNormal);
- }
- // If smoothing has no effect, don't request it.
- if (smoothBehavior == SmoothBehavior::none) {
- rec->setHinting(SkFontHinting::kNone);
- }
-
- // FIXME: lcd smoothed un-hinted rasterization unsupported.
- // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
- // There is no current means to honor a request for unhinted lcd,
- // so arbitrarilly ignore the hinting request and honor lcd.
-
- // Hinting and smoothing should be orthogonal, but currently they are not.
- // CoreGraphics has no API to influence hinting. However, its lcd smoothed
- // output is drawn from auto-dilated outlines (the amount of which is
- // determined by AppleFontSmoothing). Its regular anti-aliased output is
- // drawn from un-dilated outlines.
-
- // The behavior of Skia is as follows:
- // [AA][no-hint]: generate AA using CoreGraphic's AA output.
- // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
- // channel. This matches [LCD][yes-hint] in weight.
- // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
- // Currenly side with LCD, effectively ignoring the hinting setting.
- // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
- if (rec->fMaskFormat == SkMask::kLCD16_Format) {
- if (smoothBehavior == SmoothBehavior::subpixel) {
- //CoreGraphics creates 555 masks for smoothed text anyway.
- rec->fMaskFormat = SkMask::kLCD16_Format;
- rec->setHinting(SkFontHinting::kNormal);
- } else {
- rec->fMaskFormat = SkMask::kA8_Format;
- if (smoothBehavior != SmoothBehavior::none) {
- rec->setHinting(SkFontHinting::kNormal);
- }
- }
- }
-
- // CoreText provides no information as to whether a glyph will be color or not.
- // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
- // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
- if (fHasColorGlyphs) {
- rec->fMaskFormat = SkMask::kARGB32_Format;
- }
-
- // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
- // All other masks can use regular gamma.
- if (SkMask::kA8_Format == rec->fMaskFormat && SkFontHinting::kNone == rec->getHinting()) {
-#ifndef SK_GAMMA_APPLY_TO_A8
- // SRGBTODO: Is this correct? Do we want contrast boost?
- rec->ignorePreBlend();
-#endif
- } else {
- SkColor color = rec->getLuminanceColor();
- if (smoothBehavior == SmoothBehavior::some) {
- // CoreGraphics smoothed text without subpixel coverage blitting goes from a gamma of
- // 2.0 for black foreground to a gamma of 1.0 for white foreground. Emulate this
- // through the mask gamma by reducing the color values to 1/2.
- color = SkColorSetRGB(SkColorGetR(color) * 1/2,
- SkColorGetG(color) * 1/2,
- SkColorGetB(color) * 1/2);
- } else if (smoothBehavior == SmoothBehavior::subpixel) {
- // CoreGraphics smoothed text with subpixel coverage blitting goes from a gamma of
- // 2.0 for black foreground to a gamma of ~1.4? for white foreground. Emulate this
- // through the mask gamma by reducing the color values to 3/4.
- color = SkColorSetRGB(SkColorGetR(color) * 3/4,
- SkColorGetG(color) * 3/4,
- SkColorGetB(color) * 3/4);
- }
- rec->setLuminanceColor(color);
-
- // CoreGraphics dialates smoothed text to provide contrast.
- rec->setContrast(0);
- }
-}
-
-/** Takes ownership of the CFStringRef. */
-static const char* get_str(CFStringRef ref, SkString* str) {
- if (nullptr == ref) {
- return nullptr;
- }
- CFStringToSkString(ref, str);
- CFRelease(ref);
- return str->c_str();
-}
-
-void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
- get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
-}
-
-void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
- bool* isLocalStream) const {
- SkString tmpStr;
-
- desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
- desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
- desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
- desc->setStyle(this->fontStyle());
- *isLocalStream = fIsFromStream;
-}
-
-void SkTypeface_Mac::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
- // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
- // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
- // It is documented that if a mapping is unavailable, the glyph will be set to 0.
-
- SkAutoSTMalloc<1024, UniChar> charStorage;
- const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
- int srcCount;
- const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(uni);
- UniChar* utf16 = charStorage.reset(2 * count);
- src = utf16;
- for (int i = 0; i < count; ++i) {
- utf16 += SkUTF::ToUTF16(utf32[i], utf16);
- }
- srcCount = SkToInt(utf16 - src);
-
- // If there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
- SkAutoSTMalloc<1024, uint16_t> glyphStorage;
- uint16_t* macGlyphs = glyphs;
- if (srcCount > count) {
- macGlyphs = glyphStorage.reset(srcCount);
- }
-
- CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
-
- // If there were any non-bmp, then copy and compact.
- // If all are bmp, 'glyphs' already contains the compact glyphs.
- // If some are non-bmp, copy and compact into 'glyphs'.
- if (srcCount > count) {
- SkASSERT(glyphs != macGlyphs);
- int extra = 0;
- for (int i = 0; i < count; ++i) {
- glyphs[i] = macGlyphs[i + extra];
- if (SkUTF16_IsLeadingSurrogate(src[i + extra])) {
- ++extra;
- }
- }
- } else {
- SkASSERT(glyphs == macGlyphs);
- }
-}
-
-int SkTypeface_Mac::onCountGlyphs() const {
- return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-namespace {
-
-struct CTFontVariation {
- SkUniqueCFRef<CFDictionaryRef> dict;
- OpszVariation opsz;
-};
-
-/** Creates a dictionary suitable for setting the axes on a CTFont. */
-static CTFontVariation ctvariation_from_skfontarguments(CTFontRef ct,
- const SkFontArguments& args)
-{
- OpszVariation opsz;
- constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
-
- SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
- if (!ctAxes) {
- return CTFontVariation();
- }
- CFIndex axisCount = CFArrayGetCount(ctAxes.get());
-
- const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
-
- SkUniqueCFRef<CFMutableDictionaryRef> dict(
- CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
-
- for (int i = 0; i < axisCount; ++i) {
- CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
- if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
- return CTFontVariation();
- }
- CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
-
- CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
- if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
- return CTFontVariation();
- }
- CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
- int64_t tagLong;
- if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
- return CTFontVariation();
- }
-
- // The variation axes can be set to any value, but cg will effectively pin them.
- // Pin them here to normalize.
- CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
- CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
- CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
- if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
- !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
- !def || CFGetTypeID(def) != CFNumberGetTypeID())
- {
- return CTFontVariation();
- }
- CFNumberRef minNumber = static_cast<CFNumberRef>(min);
- CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
- CFNumberRef defNumber = static_cast<CFNumberRef>(def);
- double minDouble;
- double maxDouble;
- double defDouble;
- if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
- !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
- !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
- {
- return CTFontVariation();
- }
-
- double value = defDouble;
- // The position may be over specified. If there are multiple values for a given axis,
- // use the last one since that's what css-fonts-4 requires.
- for (int j = position.coordinateCount; j --> 0;) {
- if (position.coordinates[j].axis == tagLong) {
- value = SkTPin(SkScalarToDouble(position.coordinates[j].value),
- minDouble, maxDouble);
- if (tagLong == opszTag) {
- opsz.isSet = true;
- }
- break;
- }
- }
- if (tagLong == opszTag) {
- opsz.value = value;
- }
- SkUniqueCFRef<CFNumberRef> valueNumber(
- CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
- CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
- }
- return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
-}
-
-/** Creates a dictionary suitable for setting the axes on a CTFont. */
-static CTFontVariation ctvariation_from_skfontdata(CTFontRef ct, SkFontData* fontData) {
- // In macOS 10.15 CTFontCreate* overrides any 'opsz' variation with the 'size'.
- // Track the 'opsz' and return it, since it is an out of band axis.
- OpszVariation opsz;
- constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
-
- SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
- if (!ctAxes) {
- return CTFontVariation();
- }
-
- CFIndex axisCount = CFArrayGetCount(ctAxes.get());
- if (0 == axisCount || axisCount != fontData->getAxisCount()) {
- return CTFontVariation();
- }
-
- SkUniqueCFRef<CFMutableDictionaryRef> dict(
- CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
-
- for (int i = 0; i < fontData->getAxisCount(); ++i) {
- CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
- if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
- return CTFontVariation();
- }
- CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
-
- CFTypeRef tag = CFDictionaryGetValue(axisInfoDict,
- kCTFontVariationAxisIdentifierKey);
- if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
- return CTFontVariation();
- }
- CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
- int64_t tagLong;
- if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
- return CTFontVariation();
- }
-
- // The variation axes can be set to any value, but cg will effectively pin them.
- // Pin them here to normalize.
- CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
- CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
- if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
- !max || CFGetTypeID(max) != CFNumberGetTypeID())
- {
- return CTFontVariation();
- }
- CFNumberRef minNumber = static_cast<CFNumberRef>(min);
- CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
- double minDouble;
- double maxDouble;
- if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
- !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
- {
- return CTFontVariation();
- }
- double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
-
- if (tagLong == opszTag) {
- opsz.isSet = true;
- opsz.value = value;
- }
-
- SkUniqueCFRef<CFNumberRef> valueNumber(
- CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
- CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
- }
- return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
-}
-
-static sk_sp<SkData> skdata_from_skstreamasset(std::unique_ptr<SkStreamAsset> stream) {
- size_t size = stream->getLength();
- if (const void* base = stream->getMemoryBase()) {
- return SkData::MakeWithProc(base, size,
- [](const void*, void* ctx) -> void {
- delete (SkStreamAsset*)ctx;
- }, stream.release());
- }
- return SkData::MakeFromStream(stream.get(), size);
-}
-
-static SkUniqueCFRef<CFDataRef> cfdata_from_skdata(sk_sp<SkData> data) {
- void const * const addr = data->data();
- size_t const size = data->size();
-
- CFAllocatorContext ctx = {
- 0, // CFIndex version
- data.release(), // void* info
- nullptr, // const void *(*retain)(const void *info);
- nullptr, // void (*release)(const void *info);
- nullptr, // CFStringRef (*copyDescription)(const void *info);
- nullptr, // void * (*allocate)(CFIndex size, CFOptionFlags hint, void *info);
- nullptr, // void*(*reallocate)(void* ptr,CFIndex newsize,CFOptionFlags hint,void* info);
- [](void*,void* info) -> void { // void (*deallocate)(void *ptr, void *info);
- SkASSERT(info);
- ((SkData*)info)->unref();
- },
- nullptr, // CFIndex (*preferredSize)(CFIndex size, CFOptionFlags hint, void *info);
- };
- SkUniqueCFRef<CFAllocatorRef> alloc(CFAllocatorCreate(kCFAllocatorDefault, &ctx));
- return SkUniqueCFRef<CFDataRef>(CFDataCreateWithBytesNoCopy(
- kCFAllocatorDefault, (const UInt8 *)addr, size, alloc.get()));
-}
-
-static SkUniqueCFRef<CTFontRef> ctfont_from_skdata(sk_sp<SkData> data, int ttcIndex) {
- // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
- if (ttcIndex != 0) {
- return nullptr;
- }
-
- SkUniqueCFRef<CFDataRef> cfData(cfdata_from_skdata(std::move(data)));
-
- SkUniqueCFRef<CTFontDescriptorRef> desc(
- CTFontManagerCreateFontDescriptorFromData(cfData.get()));
- if (!desc) {
- return nullptr;
- }
- return SkUniqueCFRef<CTFontRef>(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
-}
-
-static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
- SkUniqueCFRef<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
- if (!ref) {
- return false;
- }
- CFStringToSkString(ref.get(), value);
- return true;
-}
-
-static inline int sqr(int value) {
- SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
- return value * value;
-}
-
-// We normalize each axis (weight, width, italic) to be base-900
-static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
- return sqr(a.weight() - b.weight()) +
- sqr((a.width() - b.width()) * 100) +
- sqr((a.slant() != b.slant()) * 900);
-}
-
-class SkFontStyleSet_Mac : public SkFontStyleSet {
-public:
- SkFontStyleSet_Mac(CTFontDescriptorRef desc)
- : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
- , fCount(0)
- {
- if (!fArray) {
- fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr));
- }
- fCount = SkToInt(CFArrayGetCount(fArray.get()));
- }
-
- int count() override {
- return fCount;
- }
-
- void getStyle(int index, SkFontStyle* style, SkString* name) override {
- SkASSERT((unsigned)index < (unsigned)fCount);
- CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
- if (style) {
- *style = fontstyle_from_descriptor(desc, false);
- }
- if (name) {
- if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
- name->reset();
- }
- }
- }
-
- SkTypeface* createTypeface(int index) override {
- SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get()));
- CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
-
- return create_from_desc(desc).release();
- }
-
- SkTypeface* matchStyle(const SkFontStyle& pattern) override {
- if (0 == fCount) {
- return nullptr;
- }
- return create_from_desc(findMatchingDesc(pattern)).release();
- }
-
-private:
- SkUniqueCFRef<CFArrayRef> fArray;
- int fCount;
-
- CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
- int bestMetric = SK_MaxS32;
- CTFontDescriptorRef bestDesc = nullptr;
-
- for (int i = 0; i < fCount; ++i) {
- CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i);
- int metric = compute_metric(pattern, fontstyle_from_descriptor(desc, false));
- if (0 == metric) {
- return desc;
- }
- if (metric < bestMetric) {
- bestMetric = metric;
- bestDesc = desc;
- }
- }
- SkASSERT(bestDesc);
- return bestDesc;
- }
-};
-
-} // namespace
-
-sk_sp<SkTypeface> SkTypeface_Mac::onMakeClone(const SkFontArguments& args) const {
- CTFontVariation ctVariation = ctvariation_from_skfontarguments(fFontRef.get(), args);
-
- SkUniqueCFRef<CTFontRef> ctVariant;
- if (ctVariation.dict) {
- SkUniqueCFRef<CFMutableDictionaryRef> attributes(
- CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
- CFDictionaryAddValue(attributes.get(),
- kCTFontVariationAttribute, ctVariation.dict.get());
- SkUniqueCFRef<CTFontDescriptorRef> varDesc(
- CTFontDescriptorCreateWithAttributes(attributes.get()));
- ctVariant.reset(CTFontCreateCopyWithAttributes(fFontRef.get(), 0, nullptr, varDesc.get()));
- } else {
- ctVariant.reset((CTFontRef)CFRetain(fFontRef.get()));
- }
- if (!ctVariant) {
- return nullptr;
- }
-
- return create_from_CTFontRef(std::move(ctVariant), ctVariation.opsz,
- fStream ? fStream->duplicate() : nullptr);
-}
-
-int SkTypeface_Mac::onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
- int parameterCount) const
-{
- SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
- if (!ctAxes) {
- return -1;
- }
- CFIndex axisCount = CFArrayGetCount(ctAxes.get());
-
- if (!parameters || parameterCount < axisCount) {
- return axisCount;
- }
-
- // Added in 10.13
- CFStringRef* kCTFontVariationAxisHiddenKeyPtr =
- static_cast<CFStringRef*>(dlsym(RTLD_DEFAULT, "kCTFontVariationAxisHiddenKey"));
-
- for (int i = 0; i < axisCount; ++i) {
- CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
- if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
- return -1;
- }
- CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
-
- CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
- if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
- return -1;
- }
- CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
- int64_t tagLong;
- if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
- return -1;
- }
-
- CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
- CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
- CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
- if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
- !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
- !def || CFGetTypeID(def) != CFNumberGetTypeID())
- {
- return -1;
- }
- CFNumberRef minNumber = static_cast<CFNumberRef>(min);
- CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
- CFNumberRef defNumber = static_cast<CFNumberRef>(def);
- double minDouble;
- double maxDouble;
- double defDouble;
- if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
- !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
- !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
- {
- return -1;
- }
-
- SkFontParameters::Variation::Axis& skAxis = parameters[i];
- skAxis.tag = tagLong;
- skAxis.min = minDouble;
- skAxis.max = maxDouble;
- skAxis.def = defDouble;
- skAxis.setHidden(false);
- if (kCTFontVariationAxisHiddenKeyPtr) {
- CFTypeRef hidden = CFDictionaryGetValue(axisInfoDict,*kCTFontVariationAxisHiddenKeyPtr);
- if (hidden) {
- if (CFGetTypeID(hidden) != CFBooleanGetTypeID()) {
- return -1;
- }
- CFBooleanRef hiddenBoolean = static_cast<CFBooleanRef>(hidden);
- skAxis.setHidden(CFBooleanGetValue(hiddenBoolean));
- }
- }
- }
- return axisCount;
-}
-
-class SkFontMgr_Mac : public SkFontMgr {
- SkUniqueCFRef<CFArrayRef> fNames;
- int fCount;
-
- CFStringRef getFamilyNameAt(int index) const {
- SkASSERT((unsigned)index < (unsigned)fCount);
- return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index);
- }
-
- static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
- SkUniqueCFRef<CFMutableDictionaryRef> cfAttr(
- CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
-
- CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName);
-
- SkUniqueCFRef<CTFontDescriptorRef> desc(
- CTFontDescriptorCreateWithAttributes(cfAttr.get()));
- return new SkFontStyleSet_Mac(desc.get());
- }
-
- /** CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
- * provide a wrapper here that will return an empty array if need be.
- */
- static SkUniqueCFRef<CFArrayRef> CopyAvailableFontFamilyNames() {
-#ifdef SK_BUILD_FOR_IOS
- return SkUniqueCFRef<CFArrayRef>(CFArrayCreate(nullptr, nullptr, 0, nullptr));
-#else
- return SkUniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames());
-#endif
- }
-
-public:
- SkFontMgr_Mac()
- : fNames(CopyAvailableFontFamilyNames())
- , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0) {}
-
-protected:
- int onCountFamilies() const override {
- return fCount;
- }
-
- void onGetFamilyName(int index, SkString* familyName) const override {
- if ((unsigned)index < (unsigned)fCount) {
- CFStringToSkString(this->getFamilyNameAt(index), familyName);
- } else {
- familyName->reset();
- }
- }
-
- SkFontStyleSet* onCreateStyleSet(int index) const override {
- if ((unsigned)index >= (unsigned)fCount) {
- return nullptr;
- }
- return CreateSet(this->getFamilyNameAt(index));
- }
-
- SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
- if (!familyName) {
- return nullptr;
- }
- SkUniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
- return CreateSet(cfName.get());
- }
-
- SkTypeface* onMatchFamilyStyle(const char familyName[],
- const SkFontStyle& style) const override {
- SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
- return create_from_desc(desc.get()).release();
- }
-
- SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
- const SkFontStyle& style,
- const char* bcp47[], int bcp47Count,
- SkUnichar character) const override {
- SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
- SkUniqueCFRef<CTFontRef> familyFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
-
- // kCFStringEncodingUTF32 is BE unless there is a BOM.
- // Since there is no machine endian option, explicitly state machine endian.
-#ifdef SK_CPU_LENDIAN
- constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
-#else
- constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
-#endif
- SkUniqueCFRef<CFStringRef> string(CFStringCreateWithBytes(
- kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
- encoding, false));
- // If 0xD800 <= codepoint <= 0xDFFF || 0x10FFFF < codepoint 'string' may be nullptr.
- // No font should be covering such codepoints (even the magic fallback font).
- if (!string) {
- return nullptr;
- }
- CFRange range = CFRangeMake(0, CFStringGetLength(string.get())); // in UniChar units.
- SkUniqueCFRef<CTFontRef> fallbackFont(
- CTFontCreateForString(familyFont.get(), string.get(), range));
- return create_from_CTFontRef(std::move(fallbackFont), OpszVariation(), nullptr).release();
- }
-
- SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
- const SkFontStyle&) const override {
- return nullptr;
- }
-
- sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
- if (ttcIndex != 0) {
- return nullptr;
- }
-
- SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(data, ttcIndex);
- if (!ct) {
- return nullptr;
- }
-
- return create_from_CTFontRef(std::move(ct), OpszVariation(),
- SkMemoryStream::Make(std::move(data)));
- }
-
- sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
- int ttcIndex) const override {
- if (ttcIndex != 0) {
- return nullptr;
- }
-
- sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate());
- if (!data) {
- return nullptr;
- }
- SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex);
- if (!ct) {
- return nullptr;
- }
-
- return create_from_CTFontRef(std::move(ct), OpszVariation(), std::move(stream));
- }
-
- sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
- const SkFontArguments& args) const override
- {
- // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
- int ttcIndex = args.getCollectionIndex();
- if (ttcIndex != 0) {
- return nullptr;
- }
-
- sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate());
- if (!data) {
- return nullptr;
- }
- SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex);
- if (!ct) {
- return nullptr;
- }
-
- CTFontVariation ctVariation = ctvariation_from_skfontarguments(ct.get(), args);
-
- SkUniqueCFRef<CTFontRef> ctVariant;
- if (ctVariation.dict) {
- SkUniqueCFRef<CFMutableDictionaryRef> attributes(
- CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
- CFDictionaryAddValue(attributes.get(),
- kCTFontVariationAttribute, ctVariation.dict.get());
- SkUniqueCFRef<CTFontDescriptorRef> varDesc(
- CTFontDescriptorCreateWithAttributes(attributes.get()));
- ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get()));
- } else {
- ctVariant.reset(ct.release());
- }
- if (!ctVariant) {
- return nullptr;
- }
-
- return create_from_CTFontRef(std::move(ctVariant), ctVariation.opsz,
- std::move(stream));
- }
-
- sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData> fontData) const override {
- // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
- if (fontData->getIndex() != 0) {
- return nullptr;
- }
-
- sk_sp<SkData> data = skdata_from_skstreamasset(fontData->getStream()->duplicate());
- if (!data) {
- return nullptr;
- }
- SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), fontData->getIndex());
- if (!ct) {
- return nullptr;
- }
-
- CTFontVariation ctVariation = ctvariation_from_skfontdata(ct.get(), fontData.get());
-
- SkUniqueCFRef<CTFontRef> ctVariant;
- if (ctVariation.dict) {
- SkUniqueCFRef<CFMutableDictionaryRef> attributes(
- CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
- CFDictionaryAddValue(attributes.get(),
- kCTFontVariationAttribute, ctVariation.dict.get());
- SkUniqueCFRef<CTFontDescriptorRef> varDesc(
- CTFontDescriptorCreateWithAttributes(attributes.get()));
- ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get()));
- } else {
- ctVariant.reset(ct.release());
- }
- if (!ctVariant) {
- return nullptr;
- }
-
- return create_from_CTFontRef(std::move(ctVariant), ctVariation.opsz,
- fontData->detachStream());
- }
-
- sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
- if (ttcIndex != 0) {
- return nullptr;
- }
-
- sk_sp<SkData> data = SkData::MakeFromFileName(path);
- if (!data) {
- return nullptr;
- }
-
- return this->onMakeFromData(std::move(data), ttcIndex);
- }
-
- sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
- if (familyName) {
- familyName = map_css_names(familyName);
- }
-
- sk_sp<SkTypeface> face = create_from_name(familyName, style);
- if (face) {
- return face;
- }
-
- static SkTypeface* gDefaultFace;
- static SkOnce lookupDefault;
- static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
- lookupDefault([]{
- gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle()).release();
- });
- return sk_ref_sp(gDefaultFace);
- }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkFontMgr> SkFontMgr::Factory() { return sk_make_sp<SkFontMgr_Mac>(); }
-
-#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+// All prior contents of this file have been divided among the following files.
+// This file remains while builds are migrated.
+#include "src/ports/SkFontMgr_mac_ct.cpp"
+#include "src/ports/SkTypeface_mac_ct.cpp"
+#include "src/ports/SkScalerContext_mac_ct.cpp"
+#include "src/ports/SkFontMgr_mac_ct_factory.cpp"
diff --git a/src/ports/SkFontMgr_mac_ct.cpp b/src/ports/SkFontMgr_mac_ct.cpp
new file mode 100644
index 0000000..c362b16
--- /dev/null
+++ b/src/ports/SkFontMgr_mac_ct.cpp
@@ -0,0 +1,669 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include "include/core/SkData.h"
+#include "include/core/SkFontArguments.h"
+#include "include/core/SkFontMgr.h"
+#include "include/core/SkFontStyle.h"
+#include "include/core/SkStream.h"
+#include "include/core/SkString.h"
+#include "include/core/SkTypeface.h"
+#include "include/ports/SkFontMgr_mac_ct.h"
+#include "include/private/SkFixed.h"
+#include "include/private/SkOnce.h"
+#include "include/private/SkTemplates.h"
+#include "include/private/SkTo.h"
+#include "src/core/SkFontDescriptor.h"
+#include "src/ports/SkTypeface_mac_ct.h"
+#include "src/utils/SkUTF.h"
+
+#include <string.h>
+#include <memory>
+
+static SkUniqueCFRef<CFStringRef> make_CFString(const char s[]) {
+ return SkUniqueCFRef<CFStringRef>(CFStringCreateWithCString(nullptr, s, kCFStringEncodingUTF8));
+}
+
+/** Creates a typeface from a descriptor, searching the cache. */
+static sk_sp<SkTypeface> create_from_desc(CTFontDescriptorRef desc) {
+ SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
+ if (!ctFont) {
+ return nullptr;
+ }
+
+ return SkTypeface_Mac::Make(std::move(ctFont), OpszVariation(), nullptr);
+}
+
+static SkUniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[],
+ const SkFontStyle& style) {
+ SkUniqueCFRef<CFMutableDictionaryRef> cfAttributes(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ SkUniqueCFRef<CFMutableDictionaryRef> cfTraits(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ if (!cfAttributes || !cfTraits) {
+ return nullptr;
+ }
+
+ // TODO(crbug.com/1018581) Some CoreText versions have errant behavior when
+ // certain traits set. Temporary workaround to omit specifying trait for
+ // those versions.
+ // Long term solution will involve serializing typefaces instead of relying
+ // upon this to match between processes.
+ //
+ // Compare CoreText.h in an up to date SDK for where these values come from.
+ static const uint32_t kSkiaLocalCTVersionNumber10_14 = 0x000B0000;
+ static const uint32_t kSkiaLocalCTVersionNumber10_15 = 0x000C0000;
+
+ // CTFontTraits (symbolic)
+ // macOS 14 and iOS 12 seem to behave badly when kCTFontSymbolicTrait is set.
+ // macOS 15 yields LastResort font instead of a good default font when
+ // kCTFontSymbolicTrait is set.
+ if (!(&CTGetCoreTextVersion && CTGetCoreTextVersion() >= kSkiaLocalCTVersionNumber10_14)) {
+ CTFontSymbolicTraits ctFontTraits = 0;
+ if (style.weight() >= SkFontStyle::kBold_Weight) {
+ ctFontTraits |= kCTFontBoldTrait;
+ }
+ if (style.slant() != SkFontStyle::kUpright_Slant) {
+ ctFontTraits |= kCTFontItalicTrait;
+ }
+ SkUniqueCFRef<CFNumberRef> cfFontTraits(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
+ if (cfFontTraits) {
+ CFDictionaryAddValue(cfTraits.get(), kCTFontSymbolicTrait, cfFontTraits.get());
+ }
+ }
+
+ // CTFontTraits (weight)
+ CGFloat ctWeight = SkCTFontCTWeightForCSSWeight(style.weight());
+ SkUniqueCFRef<CFNumberRef> cfFontWeight(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWeight));
+ if (cfFontWeight) {
+ CFDictionaryAddValue(cfTraits.get(), kCTFontWeightTrait, cfFontWeight.get());
+ }
+ // CTFontTraits (width)
+ CGFloat ctWidth = SkCTFontCTWidthForCSSWidth(style.width());
+ SkUniqueCFRef<CFNumberRef> cfFontWidth(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWidth));
+ if (cfFontWidth) {
+ CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get());
+ }
+ // CTFontTraits (slant)
+ // macOS 15 behaves badly when kCTFontSlantTrait is set.
+ if (!(&CTGetCoreTextVersion && CTGetCoreTextVersion() == kSkiaLocalCTVersionNumber10_15)) {
+ CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : 1;
+ SkUniqueCFRef<CFNumberRef> cfFontSlant(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant));
+ if (cfFontSlant) {
+ CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get());
+ }
+ }
+ // CTFontTraits
+ CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get());
+
+ // CTFontFamilyName
+ if (familyName) {
+ SkUniqueCFRef<CFStringRef> cfFontName = make_CFString(familyName);
+ if (cfFontName) {
+ CFDictionaryAddValue(cfAttributes.get(), kCTFontFamilyNameAttribute, cfFontName.get());
+ }
+ }
+
+ return SkUniqueCFRef<CTFontDescriptorRef>(
+ CTFontDescriptorCreateWithAttributes(cfAttributes.get()));
+}
+
+// Same as the above function except style is included so we can
+// compare whether the created font conforms to the style. If not, we need
+// to recreate the font with symbolic traits. This is needed due to MacOS 10.11
+// font creation problem https://bugs.chromium.org/p/skia/issues/detail?id=8447.
+static sk_sp<SkTypeface> create_from_desc_and_style(CTFontDescriptorRef desc,
+ const SkFontStyle& style) {
+ SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
+ if (!ctFont) {
+ return nullptr;
+ }
+
+ const CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctFont.get());
+ CTFontSymbolicTraits expected_traits = traits;
+ if (style.slant() != SkFontStyle::kUpright_Slant) {
+ expected_traits |= kCTFontItalicTrait;
+ }
+ if (style.weight() >= SkFontStyle::kBold_Weight) {
+ expected_traits |= kCTFontBoldTrait;
+ }
+
+ if (expected_traits != traits) {
+ SkUniqueCFRef<CTFontRef> ctNewFont(CTFontCreateCopyWithSymbolicTraits(
+ ctFont.get(), 0, nullptr, expected_traits, expected_traits));
+ if (ctNewFont) {
+ ctFont = std::move(ctNewFont);
+ }
+ }
+
+ return SkTypeface_Mac::Make(std::move(ctFont), OpszVariation(), nullptr);
+}
+
+/** Creates a typeface from a name, searching the cache. */
+static sk_sp<SkTypeface> create_from_name(const char familyName[], const SkFontStyle& style) {
+ SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
+ if (!desc) {
+ return nullptr;
+ }
+ return create_from_desc_and_style(desc.get(), style);
+}
+
+static const char* map_css_names(const char* name) {
+ static const struct {
+ const char* fFrom; // name the caller specified
+ const char* fTo; // "canonical" name we map to
+ } gPairs[] = {
+ { "sans-serif", "Helvetica" },
+ { "serif", "Times" },
+ { "monospace", "Courier" }
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
+ if (strcmp(name, gPairs[i].fFrom) == 0) {
+ return gPairs[i].fTo;
+ }
+ }
+ return name; // no change
+}
+
+namespace {
+
+/** Creates a dictionary suitable for setting the axes on a CTFont. */
+static CTFontVariation ctvariation_from_skfontdata(CTFontRef ct, SkFontData* fontData) {
+ // In macOS 10.15 CTFontCreate* overrides any 'opsz' variation with the 'size'.
+ // Track the 'opsz' and return it, since it is an out of band axis.
+ OpszVariation opsz;
+ constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
+
+ SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
+ if (!ctAxes) {
+ return CTFontVariation();
+ }
+
+ CFIndex axisCount = CFArrayGetCount(ctAxes.get());
+ if (0 == axisCount || axisCount != fontData->getAxisCount()) {
+ return CTFontVariation();
+ }
+
+ SkUniqueCFRef<CFMutableDictionaryRef> dict(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ for (int i = 0; i < fontData->getAxisCount(); ++i) {
+ CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
+ if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
+ return CTFontVariation();
+ }
+ CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
+
+ CFTypeRef tag = CFDictionaryGetValue(axisInfoDict,
+ kCTFontVariationAxisIdentifierKey);
+ if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
+ return CTFontVariation();
+ }
+ CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
+ int64_t tagLong;
+ if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
+ return CTFontVariation();
+ }
+
+ // The variation axes can be set to any value, but cg will effectively pin them.
+ // Pin them here to normalize.
+ CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
+ CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
+ if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
+ !max || CFGetTypeID(max) != CFNumberGetTypeID())
+ {
+ return CTFontVariation();
+ }
+ CFNumberRef minNumber = static_cast<CFNumberRef>(min);
+ CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
+ double minDouble;
+ double maxDouble;
+ if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
+ !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
+ {
+ return CTFontVariation();
+ }
+ double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
+
+ if (tagLong == opszTag) {
+ opsz.isSet = true;
+ opsz.value = value;
+ }
+
+ SkUniqueCFRef<CFNumberRef> valueNumber(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
+ CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
+ }
+ return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
+}
+
+static sk_sp<SkData> skdata_from_skstreamasset(std::unique_ptr<SkStreamAsset> stream) {
+ size_t size = stream->getLength();
+ if (const void* base = stream->getMemoryBase()) {
+ return SkData::MakeWithProc(base, size,
+ [](const void*, void* ctx) -> void {
+ delete (SkStreamAsset*)ctx;
+ }, stream.release());
+ }
+ return SkData::MakeFromStream(stream.get(), size);
+}
+
+static SkUniqueCFRef<CFDataRef> cfdata_from_skdata(sk_sp<SkData> data) {
+ void const * const addr = data->data();
+ size_t const size = data->size();
+
+ CFAllocatorContext ctx = {
+ 0, // CFIndex version
+ data.release(), // void* info
+ nullptr, // const void *(*retain)(const void *info);
+ nullptr, // void (*release)(const void *info);
+ nullptr, // CFStringRef (*copyDescription)(const void *info);
+ nullptr, // void * (*allocate)(CFIndex size, CFOptionFlags hint, void *info);
+ nullptr, // void*(*reallocate)(void* ptr,CFIndex newsize,CFOptionFlags hint,void* info);
+ [](void*,void* info) -> void { // void (*deallocate)(void *ptr, void *info);
+ SkASSERT(info);
+ ((SkData*)info)->unref();
+ },
+ nullptr, // CFIndex (*preferredSize)(CFIndex size, CFOptionFlags hint, void *info);
+ };
+ SkUniqueCFRef<CFAllocatorRef> alloc(CFAllocatorCreate(kCFAllocatorDefault, &ctx));
+ return SkUniqueCFRef<CFDataRef>(CFDataCreateWithBytesNoCopy(
+ kCFAllocatorDefault, (const UInt8 *)addr, size, alloc.get()));
+}
+
+static SkUniqueCFRef<CTFontRef> ctfont_from_skdata(sk_sp<SkData> data, int ttcIndex) {
+ // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
+ if (ttcIndex != 0) {
+ return nullptr;
+ }
+
+ SkUniqueCFRef<CFDataRef> cfData(cfdata_from_skdata(std::move(data)));
+
+ SkUniqueCFRef<CTFontDescriptorRef> desc(
+ CTFontManagerCreateFontDescriptorFromData(cfData.get()));
+ if (!desc) {
+ return nullptr;
+ }
+ return SkUniqueCFRef<CTFontRef>(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
+}
+
+static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
+ SkUniqueCFRef<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
+ if (!ref) {
+ return false;
+ }
+ SkStringFromCFString(ref.get(), value);
+ return true;
+}
+
+static inline int sqr(int value) {
+ SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
+ return value * value;
+}
+
+// We normalize each axis (weight, width, italic) to be base-900
+static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
+ return sqr(a.weight() - b.weight()) +
+ sqr((a.width() - b.width()) * 100) +
+ sqr((a.slant() != b.slant()) * 900);
+}
+
+class SkFontStyleSet_Mac : public SkFontStyleSet {
+public:
+ SkFontStyleSet_Mac(CTFontDescriptorRef desc)
+ : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
+ , fCount(0)
+ {
+ if (!fArray) {
+ fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr));
+ }
+ fCount = SkToInt(CFArrayGetCount(fArray.get()));
+ }
+
+ int count() override {
+ return fCount;
+ }
+
+ void getStyle(int index, SkFontStyle* style, SkString* name) override {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+ CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
+ if (style) {
+ *style = SkCTFontDescriptorGetSkFontStyle(desc, false);
+ }
+ if (name) {
+ if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
+ name->reset();
+ }
+ }
+ }
+
+ SkTypeface* createTypeface(int index) override {
+ SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get()));
+ CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
+
+ return create_from_desc(desc).release();
+ }
+
+ SkTypeface* matchStyle(const SkFontStyle& pattern) override {
+ if (0 == fCount) {
+ return nullptr;
+ }
+ return create_from_desc(findMatchingDesc(pattern)).release();
+ }
+
+private:
+ SkUniqueCFRef<CFArrayRef> fArray;
+ int fCount;
+
+ CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
+ int bestMetric = SK_MaxS32;
+ CTFontDescriptorRef bestDesc = nullptr;
+
+ for (int i = 0; i < fCount; ++i) {
+ CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i);
+ int metric = compute_metric(pattern, SkCTFontDescriptorGetSkFontStyle(desc, false));
+ if (0 == metric) {
+ return desc;
+ }
+ if (metric < bestMetric) {
+ bestMetric = metric;
+ bestDesc = desc;
+ }
+ }
+ SkASSERT(bestDesc);
+ return bestDesc;
+ }
+};
+
+} // namespace
+
+class SkFontMgr_Mac : public SkFontMgr {
+ SkUniqueCFRef<CFArrayRef> fNames;
+ int fCount;
+
+ CFStringRef getFamilyNameAt(int index) const {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+ return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index);
+ }
+
+ static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
+ SkUniqueCFRef<CFMutableDictionaryRef> cfAttr(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName);
+
+ SkUniqueCFRef<CTFontDescriptorRef> desc(
+ CTFontDescriptorCreateWithAttributes(cfAttr.get()));
+ return new SkFontStyleSet_Mac(desc.get());
+ }
+
+ /** CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
+ * provide a wrapper here that will return an empty array if need be.
+ */
+ static SkUniqueCFRef<CFArrayRef> CopyAvailableFontFamilyNames() {
+#ifdef SK_BUILD_FOR_IOS
+ return SkUniqueCFRef<CFArrayRef>(CFArrayCreate(nullptr, nullptr, 0, nullptr));
+#else
+ return SkUniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames());
+#endif
+ }
+
+public:
+ SkUniqueCFRef<CTFontCollectionRef> fFontCollection;
+ SkFontMgr_Mac(CTFontCollectionRef fontCollection)
+ : fNames(CopyAvailableFontFamilyNames())
+ , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0)
+ , fFontCollection(fontCollection ? (CTFontCollectionRef)CFRetain(fontCollection)
+ : CTFontCollectionCreateFromAvailableFonts(nullptr))
+ {}
+
+protected:
+ int onCountFamilies() const override {
+ return fCount;
+ }
+
+ void onGetFamilyName(int index, SkString* familyName) const override {
+ if ((unsigned)index < (unsigned)fCount) {
+ SkStringFromCFString(this->getFamilyNameAt(index), familyName);
+ } else {
+ familyName->reset();
+ }
+ }
+
+ SkFontStyleSet* onCreateStyleSet(int index) const override {
+ if ((unsigned)index >= (unsigned)fCount) {
+ return nullptr;
+ }
+ return CreateSet(this->getFamilyNameAt(index));
+ }
+
+ SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
+ if (!familyName) {
+ return nullptr;
+ }
+ SkUniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
+ return CreateSet(cfName.get());
+ }
+
+ SkTypeface* onMatchFamilyStyle(const char familyName[],
+ const SkFontStyle& style) const override {
+ SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
+ return create_from_desc(desc.get()).release();
+ }
+
+ SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
+ const SkFontStyle& style,
+ const char* bcp47[], int bcp47Count,
+ SkUnichar character) const override {
+ SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
+ SkUniqueCFRef<CTFontRef> familyFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
+
+ // kCFStringEncodingUTF32 is BE unless there is a BOM.
+ // Since there is no machine endian option, explicitly state machine endian.
+#ifdef SK_CPU_LENDIAN
+ constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
+#else
+ constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
+#endif
+ SkUniqueCFRef<CFStringRef> string(CFStringCreateWithBytes(
+ kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
+ encoding, false));
+ // If 0xD800 <= codepoint <= 0xDFFF || 0x10FFFF < codepoint 'string' may be nullptr.
+ // No font should be covering such codepoints (even the magic fallback font).
+ if (!string) {
+ return nullptr;
+ }
+ CFRange range = CFRangeMake(0, CFStringGetLength(string.get())); // in UniChar units.
+ SkUniqueCFRef<CTFontRef> fallbackFont(
+ CTFontCreateForString(familyFont.get(), string.get(), range));
+ return SkTypeface_Mac::Make(std::move(fallbackFont), OpszVariation(), nullptr).release();
+ }
+
+ SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
+ const SkFontStyle&) const override {
+ return nullptr;
+ }
+
+ sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
+ if (ttcIndex != 0) {
+ return nullptr;
+ }
+
+ SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(data, ttcIndex);
+ if (!ct) {
+ return nullptr;
+ }
+
+ return SkTypeface_Mac::Make(std::move(ct), OpszVariation(),
+ SkMemoryStream::Make(std::move(data)));
+ }
+
+ sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
+ int ttcIndex) const override {
+ if (ttcIndex != 0) {
+ return nullptr;
+ }
+
+ sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate());
+ if (!data) {
+ return nullptr;
+ }
+ SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex);
+ if (!ct) {
+ return nullptr;
+ }
+
+ return SkTypeface_Mac::Make(std::move(ct), OpszVariation(), std::move(stream));
+ }
+
+ sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
+ const SkFontArguments& args) const override
+ {
+ // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
+ int ttcIndex = args.getCollectionIndex();
+ if (ttcIndex != 0) {
+ return nullptr;
+ }
+
+ sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate());
+ if (!data) {
+ return nullptr;
+ }
+ SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex);
+ if (!ct) {
+ return nullptr;
+ }
+
+ CTFontVariation ctVariation = SkCTVariationFromSkFontArguments(ct.get(), args);
+
+ SkUniqueCFRef<CTFontRef> ctVariant;
+ if (ctVariation.dict) {
+ SkUniqueCFRef<CFMutableDictionaryRef> attributes(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ CFDictionaryAddValue(attributes.get(),
+ kCTFontVariationAttribute, ctVariation.dict.get());
+ SkUniqueCFRef<CTFontDescriptorRef> varDesc(
+ CTFontDescriptorCreateWithAttributes(attributes.get()));
+ ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get()));
+ } else {
+ ctVariant.reset(ct.release());
+ }
+ if (!ctVariant) {
+ return nullptr;
+ }
+
+ return SkTypeface_Mac::Make(std::move(ctVariant), ctVariation.opsz, std::move(stream));
+ }
+
+ sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData> fontData) const override {
+ // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
+ if (fontData->getIndex() != 0) {
+ return nullptr;
+ }
+
+ sk_sp<SkData> data = skdata_from_skstreamasset(fontData->getStream()->duplicate());
+ if (!data) {
+ return nullptr;
+ }
+ SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), fontData->getIndex());
+ if (!ct) {
+ return nullptr;
+ }
+
+ CTFontVariation ctVariation = ctvariation_from_skfontdata(ct.get(), fontData.get());
+
+ SkUniqueCFRef<CTFontRef> ctVariant;
+ if (ctVariation.dict) {
+ SkUniqueCFRef<CFMutableDictionaryRef> attributes(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ CFDictionaryAddValue(attributes.get(),
+ kCTFontVariationAttribute, ctVariation.dict.get());
+ SkUniqueCFRef<CTFontDescriptorRef> varDesc(
+ CTFontDescriptorCreateWithAttributes(attributes.get()));
+ ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get()));
+ } else {
+ ctVariant.reset(ct.release());
+ }
+ if (!ctVariant) {
+ return nullptr;
+ }
+
+ return SkTypeface_Mac::Make(std::move(ctVariant), ctVariation.opsz,
+ fontData->detachStream());
+ }
+
+ sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
+ if (ttcIndex != 0) {
+ return nullptr;
+ }
+
+ sk_sp<SkData> data = SkData::MakeFromFileName(path);
+ if (!data) {
+ return nullptr;
+ }
+
+ return this->onMakeFromData(std::move(data), ttcIndex);
+ }
+
+ sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
+ if (familyName) {
+ familyName = map_css_names(familyName);
+ }
+
+ sk_sp<SkTypeface> face = create_from_name(familyName, style);
+ if (face) {
+ return face;
+ }
+
+ static SkTypeface* gDefaultFace;
+ static SkOnce lookupDefault;
+ static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
+ lookupDefault([]{
+ gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle()).release();
+ });
+ return sk_ref_sp(gDefaultFace);
+ }
+};
+
+sk_sp<SkFontMgr> SkFontMgr_New_CoreText(CTFontCollectionRef fontCollection) {
+ return sk_make_sp<SkFontMgr_Mac>(fontCollection);
+}
+
+#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
diff --git a/src/ports/SkFontMgr_mac_ct_factory.cpp b/src/ports/SkFontMgr_mac_ct_factory.cpp
new file mode 100644
index 0000000..ef834e4
--- /dev/null
+++ b/src/ports/SkFontMgr_mac_ct_factory.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#include "include/core/SkFontMgr.h"
+#include "include/ports/SkFontMgr_mac_ct.h"
+
+sk_sp<SkFontMgr> SkFontMgr::Factory() {
+ return SkFontMgr_New_CoreText(nullptr);
+}
+
+#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
diff --git a/src/ports/SkScalerContext_mac_ct.cpp b/src/ports/SkScalerContext_mac_ct.cpp
new file mode 100644
index 0000000..ad01dc6
--- /dev/null
+++ b/src/ports/SkScalerContext_mac_ct.cpp
@@ -0,0 +1,721 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include "include/core/SkColor.h"
+#include "include/core/SkColorPriv.h"
+#include "include/core/SkFontMetrics.h"
+#include "include/core/SkFontTypes.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkTypeface.h"
+#include "include/private/SkColorData.h"
+#include "include/private/SkFixed.h"
+#include "include/private/SkTemplates.h"
+#include "include/private/SkTo.h"
+#include "src/core/SkAutoMalloc.h"
+#include "src/core/SkEndian.h"
+#include "src/core/SkGlyph.h"
+#include "src/core/SkMask.h"
+#include "src/core/SkMaskGamma.h"
+#include "src/core/SkMathPriv.h"
+#include "src/core/SkUtils.h"
+#include "src/ports/SkScalerContext_mac_ct.h"
+#include "src/ports/SkTypeface_mac_ct.h"
+#include "src/sfnt/SkOTTableTypes.h"
+#include "src/sfnt/SkOTTable_OS_2.h"
+#include "src/utils/mac/SkCGBase.h"
+#include "src/utils/mac/SkCGGeometry.h"
+#include "src/utils/mac/SkCTFontSmoothBehavior.h"
+#include "src/utils/mac/SkUniqueCFRef.h"
+
+#include <algorithm>
+
+class SkDescriptor;
+
+
+// Set to make glyph bounding boxes visible.
+#define SK_SHOW_TEXT_BLIT_COVERAGE 0
+
+static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
+ int width, int height, size_t rowBytes) {
+ SkASSERT(width);
+ SkASSERT(width * sizeof(uint32_t) <= rowBytes);
+
+ if (width >= 32) {
+ while (height) {
+ sk_memset32(ptr, value, width);
+ ptr = (uint32_t*)((char*)ptr + rowBytes);
+ height -= 1;
+ }
+ return;
+ }
+
+ rowBytes -= width * sizeof(uint32_t);
+
+ if (width >= 8) {
+ while (height) {
+ int w = width;
+ do {
+ *ptr++ = value; *ptr++ = value;
+ *ptr++ = value; *ptr++ = value;
+ *ptr++ = value; *ptr++ = value;
+ *ptr++ = value; *ptr++ = value;
+ w -= 8;
+ } while (w >= 8);
+ while (--w >= 0) {
+ *ptr++ = value;
+ }
+ ptr = (uint32_t*)((char*)ptr + rowBytes);
+ height -= 1;
+ }
+ } else {
+ while (height) {
+ int w = width;
+ do {
+ *ptr++ = value;
+ } while (--w > 0);
+ ptr = (uint32_t*)((char*)ptr + rowBytes);
+ height -= 1;
+ }
+ }
+}
+
+static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
+ return pixel & 0xFF;
+}
+
+static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix) {
+ return CGAffineTransformMake( SkScalarToCGFloat(matrix[SkMatrix::kMScaleX]),
+ -SkScalarToCGFloat(matrix[SkMatrix::kMSkewY] ),
+ -SkScalarToCGFloat(matrix[SkMatrix::kMSkewX] ),
+ SkScalarToCGFloat(matrix[SkMatrix::kMScaleY]),
+ SkScalarToCGFloat(matrix[SkMatrix::kMTransX]),
+ SkScalarToCGFloat(matrix[SkMatrix::kMTransY]));
+}
+
+SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
+ const SkScalerContextEffects& effects,
+ const SkDescriptor* desc)
+ : INHERITED(std::move(typeface), effects, desc)
+ , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
+
+{
+ CTFontRef ctFont = (CTFontRef)this->getTypeface()->internal_private_getCTFontRef();
+ CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
+ SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
+ fGlyphCount = SkToU16(numGlyphs);
+
+ // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
+ // As a result, it is necessary to know the actual device size and request that.
+ SkVector scale;
+ SkMatrix skTransform;
+ bool invertible = fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
+ &scale, &skTransform, nullptr, nullptr, nullptr);
+ fTransform = MatrixToCGAffineTransform(skTransform);
+ // CGAffineTransformInvert documents that if the transform is non-invertible it will return the
+ // passed transform unchanged. It does so, but then also prints a message to stdout. Avoid this.
+ if (invertible) {
+ fInvTransform = CGAffineTransformInvert(fTransform);
+ } else {
+ fInvTransform = fTransform;
+ }
+
+ // The transform contains everything except the requested text size.
+ // Some properties, like 'trak', are based on the optical text size.
+ CGFloat textSize = SkScalarToCGFloat(scale.y());
+ fCTFont = SkCTFontCreateExactCopy(ctFont, textSize,
+ ((SkTypeface_Mac*)this->getTypeface())->fOpszVariation);
+ fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(), nullptr));
+}
+
+static int RoundSize(int dimension) {
+ return SkNextPow2(dimension);
+}
+
+CGRGBPixel* SkScalerContext_Mac::Offscreen::getCG(const SkScalerContext_Mac& context,
+ const SkGlyph& glyph, CGGlyph glyphID,
+ size_t* rowBytesPtr,
+ bool generateA8FromLCD) {
+ if (!fRGBSpace) {
+ //It doesn't appear to matter what color space is specified.
+ //Regular blends and antialiased text are always (s*a + d*(1-a))
+ //and subpixel antialiased text is always g=2.0.
+ fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
+ }
+
+ // default to kBW_Format
+ bool doAA = false;
+ bool doLCD = false;
+
+ if (SkMask::kBW_Format != glyph.maskFormat()) {
+ doLCD = true;
+ doAA = true;
+ }
+
+ // FIXME: lcd smoothed un-hinted rasterization unsupported.
+ if (!generateA8FromLCD && SkMask::kA8_Format == glyph.maskFormat()) {
+ doLCD = false;
+ doAA = true;
+ }
+
+ // If this font might have color glyphs, disable LCD as there's no way to support it.
+ // CoreText doesn't tell us which format it ended up using, so we can't detect it.
+ // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
+ if (SkMask::kARGB32_Format == glyph.maskFormat()) {
+ doLCD = false;
+ }
+
+ size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
+ if (!fCG || fSize.fWidth < glyph.width() || fSize.fHeight < glyph.height()) {
+ if (fSize.fWidth < glyph.width()) {
+ fSize.fWidth = RoundSize(glyph.width());
+ }
+ if (fSize.fHeight < glyph.height()) {
+ fSize.fHeight = RoundSize(glyph.height());
+ }
+
+ rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
+ void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
+ const CGImageAlphaInfo alpha = (glyph.isColor())
+ ? kCGImageAlphaPremultipliedFirst
+ : kCGImageAlphaNoneSkipFirst;
+ const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
+ fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
+ rowBytes, fRGBSpace.get(), bitmapInfo));
+
+ // Skia handles quantization and subpixel positioning,
+ // so disable quantization and enable subpixel positioning in CG.
+ CGContextSetAllowsFontSubpixelQuantization(fCG.get(), false);
+ CGContextSetShouldSubpixelQuantizeFonts(fCG.get(), false);
+
+ // Because CG always draws from the horizontal baseline,
+ // if there is a non-integral translation from the horizontal origin to the vertical origin,
+ // then CG cannot draw the glyph in the correct location without subpixel positioning.
+ CGContextSetAllowsFontSubpixelPositioning(fCG.get(), true);
+ CGContextSetShouldSubpixelPositionFonts(fCG.get(), true);
+
+ CGContextSetTextDrawingMode(fCG.get(), kCGTextFill);
+
+ // Draw black on white to create mask. (Special path exists to speed this up in CG.)
+ CGContextSetGrayFillColor(fCG.get(), 0.0f, 1.0f);
+
+ // force our checks below to happen
+ fDoAA = !doAA;
+ fDoLCD = !doLCD;
+
+ CGContextSetTextMatrix(fCG.get(), context.fTransform);
+ }
+
+ if (fDoAA != doAA) {
+ CGContextSetShouldAntialias(fCG.get(), doAA);
+ fDoAA = doAA;
+ }
+ if (fDoLCD != doLCD) {
+ CGContextSetShouldSmoothFonts(fCG.get(), doLCD);
+ fDoLCD = doLCD;
+ }
+
+ CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
+ // skip rows based on the glyph's height
+ image += (fSize.fHeight - glyph.height()) * fSize.fWidth;
+
+ // Erase to white (or transparent black if it's a color glyph, to not composite against white).
+ uint32_t bgColor = (!glyph.isColor()) ? 0xFFFFFFFF : 0x00000000;
+ sk_memset_rect32(image, bgColor, glyph.width(), glyph.height(), rowBytes);
+
+ float subX = 0;
+ float subY = 0;
+ if (context.fDoSubPosition) {
+ subX = SkFixedToFloat(glyph.getSubXFixed());
+ subY = SkFixedToFloat(glyph.getSubYFixed());
+ }
+
+ CGPoint point = CGPointMake(-glyph.left() + subX, glyph.top() + glyph.height() - subY);
+ // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
+ // 'positions' which are in text space. The glyph location (in device space) must be
+ // mapped into text space, so that CG can convert it back into device space.
+ // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
+ //
+ // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
+ // So always make the font transform identity and place the transform on the context.
+ point = CGPointApplyAffineTransform(point, context.fInvTransform);
+
+ CTFontDrawGlyphs(context.fCTFont.get(), &glyphID, &point, 1, fCG.get());
+
+ SkASSERT(rowBytesPtr);
+ *rowBytesPtr = rowBytes;
+ return image;
+}
+
+unsigned SkScalerContext_Mac::generateGlyphCount(void) {
+ return fGlyphCount;
+}
+
+bool SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
+ return false;
+}
+
+void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
+ glyph->fMaskFormat = fRec.fMaskFormat;
+
+ const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
+ glyph->zeroMetrics();
+
+ // The following block produces cgAdvance in CG units (pixels, y up).
+ CGSize cgAdvance;
+ CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
+ &cgGlyph, &cgAdvance, 1);
+ cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
+ glyph->fAdvanceX = SkFloatFromCGFloat(cgAdvance.width);
+ glyph->fAdvanceY = -SkFloatFromCGFloat(cgAdvance.height);
+
+ // The following produces skBounds in SkGlyph units (pixels, y down),
+ // or returns early if skBounds would be empty.
+ SkRect skBounds;
+
+ // Glyphs are always drawn from the horizontal origin. The caller must manually use the result
+ // of CTFontGetVerticalTranslationsForGlyphs to calculate where to draw the glyph for vertical
+ // glyphs. As a result, always get the horizontal bounds of a glyph and translate it if the
+ // glyph is vertical. This avoids any diagreement between the various means of retrieving
+ // vertical metrics.
+ {
+ // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
+ CGRect cgBounds;
+ CTFontGetBoundingRectsForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
+ &cgGlyph, &cgBounds, 1);
+ cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
+
+ // BUG?
+ // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
+ // it should be empty. So, if we see a zero-advance, we check if it has an
+ // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
+ // is rare, so we won't incur a big performance cost for this extra check.
+ if (0 == cgAdvance.width && 0 == cgAdvance.height) {
+ SkUniqueCFRef<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph,nullptr));
+ if (!path || CGPathIsEmpty(path.get())) {
+ return;
+ }
+ }
+
+ if (SkCGRectIsEmpty(cgBounds)) {
+ return;
+ }
+
+ // Convert cgBounds to SkGlyph units (pixels, y down).
+ skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
+ cgBounds.size.width, cgBounds.size.height);
+ }
+
+ // Currently the bounds are based on being rendered at (0,0).
+ // The top left must not move, since that is the base from which subpixel positioning is offset.
+ if (fDoSubPosition) {
+ skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
+ skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
+ }
+
+ // We're trying to pack left and top into int16_t,
+ // and width and height into uint16_t, after outsetting by 1.
+ if (!SkRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(skBounds)) {
+ return;
+ }
+
+ SkIRect skIBounds;
+ skBounds.roundOut(&skIBounds);
+ // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
+ // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
+ // is not currently known, as CG dilates the outlines by some percentage.
+ // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
+ skIBounds.outset(1, 1);
+ glyph->fLeft = SkToS16(skIBounds.fLeft);
+ glyph->fTop = SkToS16(skIBounds.fTop);
+ glyph->fWidth = SkToU16(skIBounds.width());
+ glyph->fHeight = SkToU16(skIBounds.height());
+}
+
+static constexpr uint8_t sk_pow2_table(size_t i) {
+ return SkToU8(((i * i + 128) / 255));
+}
+
+/**
+ * This will invert the gamma applied by CoreGraphics, so we can get linear
+ * values.
+ *
+ * CoreGraphics obscurely defaults to 2.0 as the subpixel coverage gamma value.
+ * The color space used does not appear to affect this choice.
+ */
+static constexpr auto gLinearCoverageFromCGLCDValue = SkMakeArray<256>(sk_pow2_table);
+
+static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
+ while (count > 0) {
+ uint8_t mask = 0;
+ for (int i = 7; i >= 0; --i) {
+ mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
+ if (0 == --count) {
+ break;
+ }
+ }
+ *dst++ = mask;
+ }
+}
+
+template<bool APPLY_PREBLEND>
+static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
+ U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
+ U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
+ U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
+ U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
+#if SK_SHOW_TEXT_BLIT_COVERAGE
+ lum = std::max(lum, (U8CPU)0x30);
+#endif
+ return lum;
+}
+
+template<bool APPLY_PREBLEND>
+static void RGBToA8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
+ const SkGlyph& glyph, void* glyphImage, const uint8_t* table8) {
+ const int width = glyph.width();
+ const int height = glyph.height();
+ size_t dstRB = glyph.rowBytes();
+ uint8_t* SK_RESTRICT dst = (uint8_t*)glyphImage;
+
+ for (int y = 0; y < height; y++) {
+ for (int i = 0; i < width; ++i) {
+ dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
+ }
+ cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
+ dst = SkTAddOffset<uint8_t>(dst, dstRB);
+ }
+}
+
+template<bool APPLY_PREBLEND>
+static uint16_t RGBToLcd16(CGRGBPixel rgb,
+ const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
+ U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
+ U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
+ U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
+#if SK_SHOW_TEXT_BLIT_COVERAGE
+ r = std::max(r, (U8CPU)0x30);
+ g = std::max(g, (U8CPU)0x30);
+ b = std::max(b, (U8CPU)0x30);
+#endif
+ return SkPack888ToRGB16(r, g, b);
+}
+
+template<bool APPLY_PREBLEND>
+static void 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) {
+ const int width = glyph.width();
+ const int height = glyph.height();
+ size_t dstRB = glyph.rowBytes();
+ uint16_t* SK_RESTRICT dst = (uint16_t*)glyphImage;
+
+ for (int y = 0; y < height; y++) {
+ for (int i = 0; i < width; i++) {
+ dst[i] = RGBToLcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
+ }
+ cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
+ dst = SkTAddOffset<uint16_t>(dst, dstRB);
+ }
+}
+
+static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
+ U8CPU a = (rgb >> 24) & 0xFF;
+ U8CPU r = (rgb >> 16) & 0xFF;
+ U8CPU g = (rgb >> 8) & 0xFF;
+ U8CPU b = (rgb >> 0) & 0xFF;
+#if SK_SHOW_TEXT_BLIT_COVERAGE
+ a = std::max(a, (U8CPU)0x30);
+#endif
+ return SkPackARGB32(a, r, g, b);
+}
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
+ CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
+
+ // FIXME: lcd smoothed un-hinted rasterization unsupported.
+ bool requestSmooth = fRec.getHinting() != SkFontHinting::kNone;
+
+ // Draw the glyph
+ size_t cgRowBytes;
+ CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, requestSmooth);
+ if (cgPixels == nullptr) {
+ return;
+ }
+
+ // Fix the glyph
+ if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
+ (glyph.fMaskFormat == SkMask::kA8_Format
+ && requestSmooth
+ && SkCTFontGetSmoothBehavior() != SkCTFontSmoothBehavior::none))
+ {
+ const uint8_t* linear = gLinearCoverageFromCGLCDValue.data();
+
+ //Note that the following cannot really be integrated into the
+ //pre-blend, since we may not be applying the pre-blend; when we aren't
+ //applying the pre-blend it means that a filter wants linear anyway.
+ //Other code may also be applying the pre-blend, so we'd need another
+ //one with this and one without.
+ CGRGBPixel* addr = cgPixels;
+ for (int y = 0; y < glyph.fHeight; ++y) {
+ for (int x = 0; x < glyph.fWidth; ++x) {
+ int r = (addr[x] >> 16) & 0xFF;
+ int g = (addr[x] >> 8) & 0xFF;
+ int b = (addr[x] >> 0) & 0xFF;
+ addr[x] = (linear[r] << 16) | (linear[g] << 8) | linear[b];
+ }
+ addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
+ }
+ }
+
+ // Convert glyph to mask
+ switch (glyph.fMaskFormat) {
+ case SkMask::kLCD16_Format: {
+ if (fPreBlend.isApplicable()) {
+ RGBToLcd16<true>(cgPixels, cgRowBytes, glyph, glyph.fImage,
+ fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+ } else {
+ RGBToLcd16<false>(cgPixels, cgRowBytes, glyph, glyph.fImage,
+ fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+ }
+ } break;
+ case SkMask::kA8_Format: {
+ if (fPreBlend.isApplicable()) {
+ RGBToA8<true>(cgPixels, cgRowBytes, glyph, glyph.fImage, fPreBlend.fG);
+ } else {
+ RGBToA8<false>(cgPixels, cgRowBytes, glyph, glyph.fImage, fPreBlend.fG);
+ }
+ } break;
+ case SkMask::kBW_Format: {
+ const int width = glyph.fWidth;
+ size_t dstRB = glyph.rowBytes();
+ uint8_t* dst = (uint8_t*)glyph.fImage;
+ for (int y = 0; y < glyph.fHeight; y++) {
+ cgpixels_to_bits(dst, cgPixels, width);
+ cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
+ dst = SkTAddOffset<uint8_t>(dst, dstRB);
+ }
+ } break;
+ case SkMask::kARGB32_Format: {
+ const int width = glyph.fWidth;
+ size_t dstRB = glyph.rowBytes();
+ SkPMColor* dst = (SkPMColor*)glyph.fImage;
+ for (int y = 0; y < glyph.fHeight; y++) {
+ for (int x = 0; x < width; ++x) {
+ dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
+ }
+ cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
+ dst = SkTAddOffset<SkPMColor>(dst, dstRB);
+ }
+ } break;
+ default:
+ SkDEBUGFAIL("unexpected mask format");
+ break;
+ }
+}
+
+namespace {
+class SkCTPathGeometrySink {
+ SkPath* fPath;
+ bool fStarted;
+ CGPoint fCurrent;
+
+ void goingTo(const CGPoint pt) {
+ if (!fStarted) {
+ fStarted = true;
+ fPath->moveTo(fCurrent.x, -fCurrent.y);
+ }
+ fCurrent = pt;
+ }
+
+ bool currentIsNot(const CGPoint pt) {
+ return fCurrent.x != pt.x || fCurrent.y != pt.y;
+ }
+
+public:
+ SkCTPathGeometrySink(SkPath* path) : fPath{path}, fStarted{false}, fCurrent{0,0} {}
+ static void ApplyElement(void *ctx, const CGPathElement *element) {
+ SkCTPathGeometrySink& self = *(SkCTPathGeometrySink*)ctx;
+ CGPoint* points = element->points;
+
+ switch (element->type) {
+ case kCGPathElementMoveToPoint:
+ self.fStarted = false;
+ self.fCurrent = points[0];
+ break;
+
+ case kCGPathElementAddLineToPoint:
+ if (self.currentIsNot(points[0])) {
+ self.goingTo(points[0]);
+ self.fPath->lineTo(points[0].x, -points[0].y);
+ }
+ break;
+
+ case kCGPathElementAddQuadCurveToPoint:
+ if (self.currentIsNot(points[0]) || self.currentIsNot(points[1])) {
+ self.goingTo(points[1]);
+ self.fPath->quadTo(points[0].x, -points[0].y,
+ points[1].x, -points[1].y);
+ }
+ break;
+
+ case kCGPathElementAddCurveToPoint:
+ if (self.currentIsNot(points[0]) ||
+ self.currentIsNot(points[1]) ||
+ self.currentIsNot(points[2]))
+ {
+ self.goingTo(points[2]);
+ self.fPath->cubicTo(points[0].x, -points[0].y,
+ points[1].x, -points[1].y,
+ points[2].x, -points[2].y);
+ }
+ break;
+
+ case kCGPathElementCloseSubpath:
+ if (self.fStarted) {
+ self.fPath->close();
+ }
+ break;
+
+ default:
+ SkDEBUGFAIL("Unknown path element!");
+ break;
+ }
+ }
+};
+} // namespace
+
+/*
+ * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
+ * seems sufficient, and possibly even correct, to allow the hinted outline
+ * to be subpixel positioned.
+ */
+#define kScaleForSubPixelPositionHinting (4.0f)
+
+bool SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
+ SkScalar scaleX = SK_Scalar1;
+ SkScalar scaleY = SK_Scalar1;
+
+ CGAffineTransform xform = fTransform;
+ /*
+ * For subpixel positioning, we want to return an unhinted outline, so it
+ * can be positioned nicely at fractional offsets. However, we special-case
+ * if the baseline of the (horizontal) text is axis-aligned. In those cases
+ * we want to retain hinting in the direction orthogonal to the baseline.
+ * e.g. for horizontal baseline, we want to retain hinting in Y.
+ * The way we remove hinting is to scale the font by some value (4) in that
+ * direction, ask for the path, and then scale the path back down.
+ */
+ if (fDoSubPosition) {
+ // start out by assuming that we want no hining in X and Y
+ scaleX = scaleY = kScaleForSubPixelPositionHinting;
+ // now see if we need to restore hinting for axis-aligned baselines
+ switch (this->computeAxisAlignmentForHText()) {
+ case kX_SkAxisAlignment:
+ scaleY = SK_Scalar1; // want hinting in the Y direction
+ break;
+ case kY_SkAxisAlignment:
+ scaleX = SK_Scalar1; // want hinting in the X direction
+ break;
+ default:
+ break;
+ }
+
+ CGAffineTransform scale(CGAffineTransformMakeScale(SkScalarToCGFloat(scaleX),
+ SkScalarToCGFloat(scaleY)));
+ xform = CGAffineTransformConcat(fTransform, scale);
+ }
+
+ CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
+ SkUniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
+
+ path->reset();
+ if (!cgPath) {
+ return false;
+ }
+
+ SkCTPathGeometrySink sink(path);
+ CGPathApply(cgPath.get(), &sink, SkCTPathGeometrySink::ApplyElement);
+ if (fDoSubPosition) {
+ SkMatrix m;
+ m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
+ path->transform(m);
+ }
+ return true;
+}
+
+void SkScalerContext_Mac::generateFontMetrics(SkFontMetrics* metrics) {
+ if (nullptr == metrics) {
+ return;
+ }
+
+ CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
+
+ metrics->fTop = SkScalarFromCGFloat(-SkCGRectGetMaxY(theBounds));
+ metrics->fAscent = SkScalarFromCGFloat(-CTFontGetAscent(fCTFont.get()));
+ metrics->fDescent = SkScalarFromCGFloat( CTFontGetDescent(fCTFont.get()));
+ metrics->fBottom = SkScalarFromCGFloat(-SkCGRectGetMinY(theBounds));
+ metrics->fLeading = SkScalarFromCGFloat( CTFontGetLeading(fCTFont.get()));
+ metrics->fAvgCharWidth = SkScalarFromCGFloat( SkCGRectGetWidth(theBounds));
+ metrics->fXMin = SkScalarFromCGFloat( SkCGRectGetMinX(theBounds));
+ metrics->fXMax = SkScalarFromCGFloat( SkCGRectGetMaxX(theBounds));
+ metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
+ metrics->fXHeight = SkScalarFromCGFloat( CTFontGetXHeight(fCTFont.get()));
+ metrics->fCapHeight = SkScalarFromCGFloat( CTFontGetCapHeight(fCTFont.get()));
+ metrics->fUnderlineThickness = SkScalarFromCGFloat( CTFontGetUnderlineThickness(fCTFont.get()));
+ metrics->fUnderlinePosition = -SkScalarFromCGFloat( CTFontGetUnderlinePosition(fCTFont.get()));
+
+ metrics->fFlags = 0;
+ metrics->fFlags |= SkFontMetrics::kUnderlineThicknessIsValid_Flag;
+ metrics->fFlags |= SkFontMetrics::kUnderlinePositionIsValid_Flag;
+
+ // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
+ // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent and
+ // the cap-height is always 0.8888 of the ascent. It appears that the values from the 'OS/2'
+ // table are read, but then overwritten if the font is not a system font. As a result, if there
+ // is a valid 'OS/2' table available use the values from the table if they aren't too strange.
+ struct OS2HeightMetrics {
+ SK_OT_SHORT sxHeight;
+ SK_OT_SHORT sCapHeight;
+ } heights;
+ size_t bytesRead = this->getTypeface()->getTableData(
+ SkTEndian_SwapBE32(SkOTTableOS2::TAG), offsetof(SkOTTableOS2, version.v2.sxHeight),
+ sizeof(heights), &heights);
+ if (bytesRead == sizeof(heights)) {
+ // 'fontSize' is correct because the entire resolved size is set by the constructor.
+ CGFloat fontSize = CTFontGetSize(this->fCTFont.get());
+ unsigned upem = CTFontGetUnitsPerEm(this->fCTFont.get());
+ unsigned maxSaneHeight = upem * 2;
+ uint16_t xHeight = SkEndian_SwapBE16(heights.sxHeight);
+ if (xHeight && xHeight < maxSaneHeight) {
+ metrics->fXHeight = SkScalarFromCGFloat(xHeight * fontSize / upem);
+ }
+ uint16_t capHeight = SkEndian_SwapBE16(heights.sCapHeight);
+ if (capHeight && capHeight < maxSaneHeight) {
+ metrics->fCapHeight = SkScalarFromCGFloat(capHeight * fontSize / upem);
+ }
+ }
+}
+
+#endif
diff --git a/src/ports/SkScalerContext_mac_ct.h b/src/ports/SkScalerContext_mac_ct.h
new file mode 100644
index 0000000..4b7f548
--- /dev/null
+++ b/src/ports/SkScalerContext_mac_ct.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkScalerContext_mac_ct_DEFINED
+#define SkScalerContext_mac_ct_DEFINED
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSize.h"
+#include "src/core/SkAutoMalloc.h"
+#include "src/core/SkScalerContext.h"
+#include "src/utils/mac/SkUniqueCFRef.h"
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include <memory>
+
+class SkDescriptor;
+class SkGlyph;
+class SkPath;
+class SkTypeface_Mac;
+struct SkFontMetrics;
+
+
+typedef uint32_t CGRGBPixel;
+
+class SkScalerContext_Mac : public SkScalerContext {
+public:
+ SkScalerContext_Mac(sk_sp<SkTypeface_Mac>, const SkScalerContextEffects&, const SkDescriptor*);
+
+protected:
+ unsigned generateGlyphCount(void) override;
+ bool generateAdvance(SkGlyph* glyph) override;
+ void generateMetrics(SkGlyph* glyph) override;
+ void generateImage(const SkGlyph& glyph) override;
+ bool generatePath(SkGlyphID glyph, SkPath* path) override;
+ void generateFontMetrics(SkFontMetrics*) override;
+
+private:
+ class Offscreen {
+ public:
+ Offscreen()
+ : fRGBSpace(nullptr)
+ , fCG(nullptr)
+ , fDoAA(false)
+ , fDoLCD(false)
+ {
+ fSize.set(0, 0);
+ }
+
+ CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
+ CGGlyph glyphID, size_t* rowBytesPtr, bool generateA8FromLCD);
+
+ private:
+ enum {
+ kSize = 32 * 32 * sizeof(CGRGBPixel)
+ };
+ SkAutoSMalloc<kSize> fImageStorage;
+ SkUniqueCFRef<CGColorSpaceRef> fRGBSpace;
+
+ // cached state
+ SkUniqueCFRef<CGContextRef> fCG;
+ SkISize fSize;
+ bool fDoAA;
+ bool fDoLCD;
+ };
+ Offscreen fOffscreen;
+
+ /** Unrotated variant of fCTFont.
+ *
+ * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
+ * advances, but always sets the height to 0. This font is used to get the advances of the
+ * unrotated glyph, and then the rotation is applied separately.
+ *
+ * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
+ * This makes kCTFontOrientationDefault dangerous, because the metrics from
+ * kCTFontOrientationHorizontal are in a different space from kCTFontOrientationVertical.
+ * With kCTFontOrientationVertical the advances must be unrotated.
+ *
+ * Sometimes, creating a copy of a CTFont with the same size but different trasform will select
+ * different underlying font data. As a result, avoid ever creating more than one CTFont per
+ * SkScalerContext to ensure that only one CTFont is used.
+ *
+ * As a result of the above (and other constraints) this font contains the size, but not the
+ * transform. The transform must always be applied separately.
+ */
+ SkUniqueCFRef<CTFontRef> fCTFont;
+
+ /** The transform without the font size. */
+ CGAffineTransform fTransform;
+ CGAffineTransform fInvTransform;
+
+ SkUniqueCFRef<CGFontRef> fCGFont;
+ uint16_t fGlyphCount;
+ const bool fDoSubPosition;
+
+ friend class Offscreen;
+
+ typedef SkScalerContext INHERITED;
+};
+
+#endif
+#endif //SkScalerContext_mac_ct_DEFINED
diff --git a/src/ports/SkTypeface_mac_ct.cpp b/src/ports/SkTypeface_mac_ct.cpp
new file mode 100644
index 0000000..8055f6b
--- /dev/null
+++ b/src/ports/SkTypeface_mac_ct.cpp
@@ -0,0 +1,1470 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include "include/core/SkColor.h"
+#include "include/core/SkData.h"
+#include "include/core/SkFontArguments.h"
+#include "include/core/SkFontParameters.h"
+#include "include/core/SkFontStyle.h"
+#include "include/core/SkFontTypes.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkStream.h"
+#include "include/core/SkString.h"
+#include "include/core/SkTypeface.h"
+#include "include/ports/SkTypeface_mac.h"
+#include "include/private/SkFixed.h"
+#include "include/private/SkMalloc.h"
+#include "include/private/SkMutex.h"
+#include "include/private/SkOnce.h"
+#include "include/private/SkTDArray.h"
+#include "include/private/SkTemplates.h"
+#include "include/private/SkTo.h"
+#include "src/core/SkAdvancedTypefaceMetrics.h"
+#include "src/core/SkEndian.h"
+#include "src/core/SkFontDescriptor.h"
+#include "src/core/SkMask.h"
+#include "src/core/SkScalerContext.h"
+#include "src/core/SkTypefaceCache.h"
+#include "src/core/SkUtils.h"
+#include "src/ports/SkScalerContext_mac_ct.h"
+#include "src/ports/SkTypeface_mac_ct.h"
+#include "src/sfnt/SkOTTableTypes.h"
+#include "src/sfnt/SkOTTable_OS_2.h"
+#include "src/sfnt/SkOTTable_OS_2_V4.h"
+#include "src/sfnt/SkOTUtils.h"
+#include "src/sfnt/SkSFNTHeader.h"
+#include "src/utils/SkUTF.h"
+#include "src/utils/mac/SkCGBase.h"
+#include "src/utils/mac/SkCGGeometry.h"
+#include "src/utils/mac/SkCTFontSmoothBehavior.h"
+#include "src/utils/mac/SkUniqueCFRef.h"
+
+#include <dlfcn.h>
+#include <limits.h>
+#include <string.h>
+#include <memory>
+
+// In macOS 10.12 and later any variation on the CGFont which has default axis value will be
+// dropped when creating the CTFont. Unfortunately, in macOS 10.15 the priority of setting
+// the optical size (and opsz variation) is
+// 1. the value of kCTFontOpticalSizeAttribute in the CTFontDescriptor (undocumented)
+// 2. the opsz axis default value if kCTFontOpticalSizeAttribute is 'none' (undocumented)
+// 3. the opsz variation on the nascent CTFont from the CGFont (was dropped if default)
+// 4. the opsz variation in kCTFontVariationAttribute in CTFontDescriptor (crashes 10.10)
+// 5. the size requested (can fudge in SkTypeface but not SkScalerContext)
+// The first one which is found will be used to set the opsz variation (after clamping).
+static void add_opsz_attr(CFMutableDictionaryRef attr, double opsz) {
+ SkUniqueCFRef<CFNumberRef> opszValueNumber(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &opsz));
+ // Avoid using kCTFontOpticalSizeAttribute directly
+ CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
+ CFDictionarySetValue(attr, SkCTFontOpticalSizeAttribute, opszValueNumber.get());
+}
+
+// This turns off application of the 'trak' table to advances, but also all other tracking.
+static void add_notrak_attr(CFMutableDictionaryRef attr) {
+ int zero = 0;
+ SkUniqueCFRef<CFNumberRef> unscaledTrackingNumber(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &zero));
+ CFStringRef SkCTFontUnscaledTrackingAttribute = CFSTR("NSCTFontUnscaledTrackingAttribute");
+ CFDictionarySetValue(attr, SkCTFontUnscaledTrackingAttribute, unscaledTrackingNumber.get());
+}
+
+SkUniqueCFRef<CTFontRef> SkCTFontCreateExactCopy(CTFontRef baseFont, CGFloat textSize,
+ OpszVariation opsz)
+{
+ SkUniqueCFRef<CFMutableDictionaryRef> attr(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ if (opsz.isSet) {
+ add_opsz_attr(attr.get(), opsz.value);
+#if !defined(SK_IGNORE_MAC_OPSZ_FORCE)
+ } else {
+ // On (at least) 10.10 though 10.14 the default system font was SFNSText/SFNSDisplay.
+ // The CTFont is backed by both; optical size < 20 means SFNSText else SFNSDisplay.
+ // On at least 10.11 the glyph ids in these fonts became non-interchangable.
+ // To keep glyph ids stable over size changes, preserve the optical size.
+ // In 10.15 this was replaced with use of variable fonts with an opsz axis.
+ // A CTFont backed by multiple fonts picked by opsz where the multiple backing fonts are
+ // variable fonts with opsz axis and non-interchangeable glyph ids would break the
+ // opsz.isSet branch above, but hopefully that never happens.
+ // See https://crbug.com/524646 .
+ CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
+ SkUniqueCFRef<CFTypeRef> opsz(CTFontCopyAttribute(baseFont, SkCTFontOpticalSizeAttribute));
+ double opsz_val;
+ if (!opsz ||
+ CFGetTypeID(opsz.get()) != CFNumberGetTypeID() ||
+ !CFNumberGetValue(static_cast<CFNumberRef>(opsz.get()),kCFNumberDoubleType,&opsz_val) ||
+ opsz_val <= 0)
+ {
+ opsz_val = CTFontGetSize(baseFont);
+ }
+ add_opsz_attr(attr.get(), opsz_val);
+#endif
+ }
+ add_notrak_attr(attr.get());
+
+ SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontDescriptorCreateWithAttributes(attr.get()));
+
+#if !defined(SK_IGNORE_MAC_OPSZ_FORCE)
+ return SkUniqueCFRef<CTFontRef>(
+ CTFontCreateCopyWithAttributes(baseFont, textSize, nullptr, desc.get()));
+#else
+ SkUniqueCFRef<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
+ return SkUniqueCFRef<CTFontRef>(
+ CTFontCreateWithGraphicsFont(baseCGFont.get(), textSize, nullptr, desc.get()));
+
+#endif
+}
+
+CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
+ return face ? (CTFontRef)face->internal_private_getCTFontRef() : nullptr;
+}
+
+static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
+ CTFontRef self = (CTFontRef)context;
+ CTFontRef other = (CTFontRef)cached->internal_private_getCTFontRef();
+
+ return CFEqual(self, other);
+}
+
+/** Creates a typeface, searching the cache if isLocalStream is false. */
+sk_sp<SkTypeface> SkTypeface_Mac::Make(SkUniqueCFRef<CTFontRef> font,
+ OpszVariation opszVariation,
+ std::unique_ptr<SkStreamAsset> providedData) {
+ static SkMutex gTFCacheMutex;
+ static SkTypefaceCache gTFCache;
+
+ SkASSERT(font);
+ const bool isFromStream(providedData);
+
+ if (!isFromStream) {
+ SkAutoMutexExclusive ama(gTFCacheMutex);
+ sk_sp<SkTypeface> face = gTFCache.findByProcAndRef(find_by_CTFontRef, (void*)font.get());
+ if (face) {
+ return face;
+ }
+ }
+
+ SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(font.get()));
+ SkFontStyle style = SkCTFontDescriptorGetSkFontStyle(desc.get(), isFromStream);
+ CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font.get());
+ bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
+
+ sk_sp<SkTypeface> face(new SkTypeface_Mac(std::move(font), style,
+ isFixedPitch, opszVariation,
+ std::move(providedData)));
+ if (!isFromStream) {
+ SkAutoMutexExclusive ama(gTFCacheMutex);
+ gTFCache.add(face);
+ }
+ return face;
+}
+
+/* This function is visible on the outside. It first searches the cache, and if
+ * not found, returns a new entry (after adding it to the cache).
+ */
+sk_sp<SkTypeface> SkMakeTypefaceFromCTFont(CTFontRef font) {
+ CFRetain(font);
+ return SkTypeface_Mac::Make(SkUniqueCFRef<CTFontRef>(font),
+ OpszVariation(),
+ nullptr);
+}
+
+static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
+ CFNumberRef num;
+ return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
+ && CFNumberIsFloatType(num)
+ && CFNumberGetValue(num, kCFNumberCGFloatType, value);
+}
+
+template <typename S, typename D, typename C> struct LinearInterpolater {
+ struct Mapping {
+ S src_val;
+ D dst_val;
+ };
+ constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
+ : fMapping(mapping), fMappingCount(mappingCount) {}
+
+ static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
+ SkASSERT(src_min < src_max);
+ SkASSERT(dst_min <= dst_max);
+ return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
+ }
+
+ D map(S val) const {
+ // -Inf to [0]
+ if (val < fMapping[0].src_val) {
+ return fMapping[0].dst_val;
+ }
+
+ // Linear from [i] to [i+1]
+ for (int i = 0; i < fMappingCount - 1; ++i) {
+ if (val < fMapping[i+1].src_val) {
+ return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
+ fMapping[i].dst_val, fMapping[i+1].dst_val);
+ }
+ }
+
+ // From [n] to +Inf
+ // if (fcweight < Inf)
+ return fMapping[fMappingCount - 1].dst_val;
+ }
+
+ Mapping const * fMapping;
+ int fMappingCount;
+};
+
+struct RoundCGFloatToInt {
+ int operator()(CGFloat s) { return s + 0.5; }
+};
+struct CGFloatIdentity {
+ CGFloat operator()(CGFloat s) { return s; }
+};
+
+/** Returns the [-1, 1] CTFontDescriptor weights for the
+ * <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
+ *
+ * It is assumed that the values will be interpolated linearly between these points.
+ * NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
+ * The actual values appear to be stable, but they may change in the future without notice.
+ */
+static CGFloat(&get_NSFontWeight_mapping())[11] {
+
+ // Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
+#ifdef SK_BUILD_FOR_MAC
+# define SK_KIT_FONT_WEIGHT_PREFIX "NS"
+#endif
+#ifdef SK_BUILD_FOR_IOS
+# define SK_KIT_FONT_WEIGHT_PREFIX "UI"
+#endif
+ static constexpr struct {
+ CGFloat defaultValue;
+ const char* name;
+ } nsFontWeightLoaderInfos[] = {
+ { -0.80f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight" },
+ { -0.60f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin" },
+ { -0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight" },
+ { 0.00f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular" },
+ { 0.23f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium" },
+ { 0.30f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold" },
+ { 0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold" },
+ { 0.56f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy" },
+ { 0.62f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack" },
+ };
+
+ static_assert(SK_ARRAY_COUNT(nsFontWeightLoaderInfos) == 9, "");
+ static CGFloat nsFontWeights[11];
+ static SkOnce once;
+ once([&] {
+ size_t i = 0;
+ nsFontWeights[i++] = -1.00;
+ for (const auto& nsFontWeightLoaderInfo : nsFontWeightLoaderInfos) {
+ void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightLoaderInfo.name);
+ if (nsFontWeightValuePtr) {
+ nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
+ } else {
+ nsFontWeights[i++] = nsFontWeightLoaderInfo.defaultValue;
+ }
+ }
+ nsFontWeights[i++] = 1.00;
+ });
+ return nsFontWeights;
+}
+
+/** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
+ *
+ * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
+ * CTFont is native or created from a CGDataProvider.
+ */
+CGFloat SkCTFontCTWeightForCSSWeight(int fontstyleWeight) {
+ using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
+
+ // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
+ // However, on this end we can't tell, so this is ignored.
+
+ static Interpolator::Mapping nativeWeightMappings[11];
+ static SkOnce once;
+ once([&] {
+ CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
+ for (int i = 0; i < 11; ++i) {
+ nativeWeightMappings[i].src_val = i * 100;
+ nativeWeightMappings[i].dst_val = nsFontWeights[i];
+ }
+ });
+ static constexpr Interpolator nativeInterpolator(
+ nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
+
+ return nativeInterpolator.map(fontstyleWeight);
+}
+
+
+/** Convert the [-1, 1] CTFontDescriptor weight to [0, 1000] CSS weight.
+ *
+ * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
+ * CTFont is native or created from a CGDataProvider.
+ */
+static int ct_weight_to_fontstyle(CGFloat cgWeight, bool fromDataProvider) {
+ using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
+
+ // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
+ // However, on this end we can't tell, so this is ignored.
+
+ /** This mapping for CGDataProvider created fonts is determined by creating font data with every
+ * weight, creating a CTFont, and asking the CTFont for its weight. See the TypefaceStyle test
+ * in tests/TypefaceTest.cpp for the code used to determine these values.
+ */
+ static constexpr Interpolator::Mapping dataProviderWeightMappings[] = {
+ { -1.00, 0 },
+ { -0.70, 100 },
+ { -0.50, 200 },
+ { -0.23, 300 },
+ { 0.00, 400 },
+ { 0.20, 500 },
+ { 0.30, 600 },
+ { 0.40, 700 },
+ { 0.60, 800 },
+ { 0.80, 900 },
+ { 1.00, 1000 },
+ };
+ static constexpr Interpolator dataProviderInterpolator(
+ dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
+
+ static Interpolator::Mapping nativeWeightMappings[11];
+ static SkOnce once;
+ once([&] {
+ CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
+ for (int i = 0; i < 11; ++i) {
+ nativeWeightMappings[i].src_val = nsFontWeights[i];
+ nativeWeightMappings[i].dst_val = i * 100;
+ }
+ });
+ static constexpr Interpolator nativeInterpolator(
+ nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
+
+ return fromDataProvider ? dataProviderInterpolator.map(cgWeight)
+ : nativeInterpolator.map(cgWeight);
+}
+
+/** Convert the [0, 10] CSS weight to [-1, 1] CTFontDescriptor width. */
+CGFloat SkCTFontCTWidthForCSSWidth(int fontstyleWidth) {
+ using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
+
+ // Values determined by creating font data with every width, creating a CTFont,
+ // and asking the CTFont for its width. See TypefaceStyle test for basics.
+ static constexpr Interpolator::Mapping widthMappings[] = {
+ { 0, -0.5 },
+ { 10, 0.5 },
+ };
+ static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
+ return interpolator.map(fontstyleWidth);
+}
+
+/** Convert the [-1, 1] CTFontDescriptor width to [0, 10] CSS weight. */
+static int ct_width_to_fontstyle(CGFloat cgWidth) {
+ using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
+
+ // Values determined by creating font data with every width, creating a CTFont,
+ // and asking the CTFont for its width. See TypefaceStyle test for basics.
+ static constexpr Interpolator::Mapping widthMappings[] = {
+ { -0.5, 0 },
+ { 0.5, 10 },
+ };
+ static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
+ return interpolator.map(cgWidth);
+}
+
+SkFontStyle SkCTFontDescriptorGetSkFontStyle(CTFontDescriptorRef desc, bool fromDataProvider) {
+ SkUniqueCFRef<CFTypeRef> traits(CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
+ if (!traits || CFDictionaryGetTypeID() != CFGetTypeID(traits.get())) {
+ return SkFontStyle();
+ }
+ SkUniqueCFRef<CFDictionaryRef> fontTraitsDict(static_cast<CFDictionaryRef>(traits.release()));
+
+ CGFloat weight, width, slant;
+ if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWeightTrait, &weight)) {
+ weight = 0;
+ }
+ if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWidthTrait, &width)) {
+ width = 0;
+ }
+ if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontSlantTrait, &slant)) {
+ slant = 0;
+ }
+
+ return SkFontStyle(ct_weight_to_fontstyle(weight, fromDataProvider),
+ ct_width_to_fontstyle(width),
+ slant ? SkFontStyle::kItalic_Slant
+ : SkFontStyle::kUpright_Slant);
+}
+
+
+// Web fonts added to the CTFont registry do not return their character set.
+// Iterate through the font in this case. The existing caller caches the result,
+// so the performance impact isn't too bad.
+static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
+ SkUnichar* out) {
+ sk_bzero(out, glyphCount * sizeof(SkUnichar));
+ UniChar unichar = 0;
+ while (glyphCount > 0) {
+ CGGlyph glyph;
+ if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
+ if (out[glyph] == 0) {
+ out[glyph] = unichar;
+ --glyphCount;
+ }
+ }
+ if (++unichar == 0) {
+ break;
+ }
+ }
+}
+
+static constexpr uint16_t kPlaneSize = 1 << 13;
+
+static void get_plane_glyph_map(const uint8_t* bits,
+ CTFontRef ctFont,
+ CFIndex glyphCount,
+ SkUnichar* glyphToUnicode,
+ uint8_t planeIndex) {
+ SkUnichar planeOrigin = (SkUnichar)planeIndex << 16; // top half of codepoint.
+ for (uint16_t i = 0; i < kPlaneSize; i++) {
+ uint8_t mask = bits[i];
+ if (!mask) {
+ continue;
+ }
+ for (uint8_t j = 0; j < 8; j++) {
+ if (0 == (mask & ((uint8_t)1 << j))) {
+ continue;
+ }
+ uint16_t planeOffset = (i << 3) | j;
+ SkUnichar codepoint = planeOrigin | (SkUnichar)planeOffset;
+ uint16_t utf16[2] = {planeOffset, 0};
+ size_t count = 1;
+ if (planeOrigin != 0) {
+ count = SkUTF::ToUTF16(codepoint, utf16);
+ }
+ CGGlyph glyphs[2] = {0, 0};
+ if (CTFontGetGlyphsForCharacters(ctFont, utf16, glyphs, count)) {
+ SkASSERT(glyphs[1] == 0);
+ SkASSERT(glyphs[0] < glyphCount);
+ // CTFontCopyCharacterSet and CTFontGetGlyphsForCharacters seem to add 'support'
+ // for characters 0x9, 0xA, and 0xD mapping them to the glyph for character 0x20?
+ // Prefer mappings to codepoints at or above 0x20.
+ if (glyphToUnicode[glyphs[0]] < 0x20) {
+ glyphToUnicode[glyphs[0]] = codepoint;
+ }
+ }
+ }
+ }
+}
+// Construct Glyph to Unicode table.
+static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
+ SkUnichar* glyphToUnicode) {
+ sk_bzero(glyphToUnicode, sizeof(SkUnichar) * glyphCount);
+ SkUniqueCFRef<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
+ if (!charSet) {
+ populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
+ return;
+ }
+
+ SkUniqueCFRef<CFDataRef> bitmap(
+ CFCharacterSetCreateBitmapRepresentation(nullptr, charSet.get()));
+ if (!bitmap) {
+ return;
+ }
+ CFIndex dataLength = CFDataGetLength(bitmap.get());
+ if (!dataLength) {
+ return;
+ }
+ SkASSERT(dataLength >= kPlaneSize);
+ const UInt8* bits = CFDataGetBytePtr(bitmap.get());
+
+ get_plane_glyph_map(bits, ctFont, glyphCount, glyphToUnicode, 0);
+ /*
+ A CFData object that specifies the bitmap representation of the Unicode
+ character points the for the new character set. The bitmap representation could
+ contain all the Unicode character range starting from BMP to Plane 16. The
+ first 8KiB (8192 bytes) of the data represent the BMP range. The BMP range 8KiB
+ can be followed by zero to sixteen 8KiB bitmaps, each prepended with the plane
+ index byte. For example, the bitmap representing the BMP and Plane 2 has the
+ size of 16385 bytes (8KiB for BMP, 1 byte index, and a 8KiB bitmap for Plane
+ 2). The plane index byte, in this case, contains the integer value two.
+ */
+
+ if (dataLength <= kPlaneSize) {
+ return;
+ }
+ int extraPlaneCount = (dataLength - kPlaneSize) / (1 + kPlaneSize);
+ SkASSERT(dataLength == kPlaneSize + extraPlaneCount * (1 + kPlaneSize));
+ while (extraPlaneCount-- > 0) {
+ bits += kPlaneSize;
+ uint8_t planeIndex = *bits++;
+ SkASSERT(planeIndex >= 1);
+ SkASSERT(planeIndex <= 16);
+ get_plane_glyph_map(bits, ctFont, glyphCount, glyphToUnicode, planeIndex);
+ }
+}
+
+/** Assumes src and dst are not nullptr. */
+void SkStringFromCFString(CFStringRef src, SkString* dst) {
+ // Reserve enough room for the worst-case string,
+ // plus 1 byte for the trailing null.
+ CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
+ kCFStringEncodingUTF8) + 1;
+ dst->resize(length);
+ CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
+ // Resize to the actual UTF-8 length used, stripping the null character.
+ dst->resize(strlen(dst->c_str()));
+}
+
+void SkTypeface_Mac::getGlyphToUnicodeMap(SkUnichar* dstArray) const {
+ SkUniqueCFRef<CTFontRef> ctFont =
+ SkCTFontCreateExactCopy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
+ fOpszVariation);
+ CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
+ populate_glyph_to_unicode(ctFont.get(), glyphCount, dstArray);
+}
+
+std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
+
+ SkUniqueCFRef<CTFontRef> ctFont =
+ SkCTFontCreateExactCopy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
+ fOpszVariation);
+
+ std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
+
+ {
+ SkUniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
+ if (fontName.get()) {
+ SkStringFromCFString(fontName.get(), &info->fPostScriptName);
+ info->fFontName = info->fPostScriptName;
+ }
+ }
+
+ // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
+ // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
+ // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
+ // CGFontCopyVariations here until support for 10.10 and earlier is removed.
+ SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont.get(), nullptr));
+ if (cgFont) {
+ SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
+ if (cgAxes && CFArrayGetCount(cgAxes.get()) > 0) {
+ info->fFlags |= SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag;
+ }
+ }
+
+ SkOTTableOS2_V4::Type fsType;
+ if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
+ offsetof(SkOTTableOS2_V4, fsType),
+ sizeof(fsType),
+ &fsType)) {
+ SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
+ }
+
+ // If it's not a truetype font, mark it as 'other'. Assume that TrueType
+ // fonts always have both glyf and loca tables. At the least, this is what
+ // sfntly needs to subset the font. CTFontCopyAttribute() does not always
+ // succeed in determining this directly.
+ if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
+ return info;
+ }
+
+ info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+ CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont.get());
+ if (symbolicTraits & kCTFontMonoSpaceTrait) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+ }
+ if (symbolicTraits & kCTFontItalicTrait) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+ }
+ CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
+ if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
+ } else if (stylisticClass & kCTFontScriptsClass) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
+ }
+ info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont.get());
+ info->fAscent = (int16_t) CTFontGetAscent(ctFont.get());
+ info->fDescent = (int16_t) CTFontGetDescent(ctFont.get());
+ info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont.get());
+ CGRect bbox = CTFontGetBoundingBox(ctFont.get());
+
+ SkRect r;
+ r.setLTRB(SkScalarFromCGFloat(SkCGRectGetMinX(bbox)), // Left
+ SkScalarFromCGFloat(SkCGRectGetMaxY(bbox)), // Top
+ SkScalarFromCGFloat(SkCGRectGetMaxX(bbox)), // Right
+ SkScalarFromCGFloat(SkCGRectGetMinY(bbox))); // Bottom
+
+ r.roundOut(&(info->fBBox));
+
+ // Figure out a good guess for StemV - Min width of i, I, !, 1.
+ // This probably isn't very good with an italic font.
+ int16_t min_width = SHRT_MAX;
+ info->fStemV = 0;
+ static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
+ const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
+ CGGlyph glyphs[count];
+ CGRect boundingRects[count];
+ if (CTFontGetGlyphsForCharacters(ctFont.get(), stem_chars, glyphs, count)) {
+ CTFontGetBoundingRectsForGlyphs(ctFont.get(), kCTFontOrientationHorizontal,
+ glyphs, boundingRects, count);
+ for (size_t i = 0; i < count; i++) {
+ int16_t width = (int16_t) boundingRects[i].size.width;
+ if (width > 0 && width < min_width) {
+ min_width = width;
+ info->fStemV = min_width;
+ }
+ }
+ }
+ return info;
+}
+
+static SK_SFNT_ULONG get_font_type_tag(CTFontRef ctFont) {
+ SkUniqueCFRef<CFNumberRef> fontFormatRef(
+ static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
+ if (!fontFormatRef) {
+ return 0;
+ }
+
+ SInt32 fontFormatValue;
+ if (!CFNumberGetValue(fontFormatRef.get(), kCFNumberSInt32Type, &fontFormatValue)) {
+ return 0;
+ }
+
+ switch (fontFormatValue) {
+ case kCTFontFormatOpenTypePostScript:
+ return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
+ case kCTFontFormatOpenTypeTrueType:
+ return SkSFNTHeader::fontType_WindowsTrueType::TAG;
+ case kCTFontFormatTrueType:
+ return SkSFNTHeader::fontType_MacTrueType::TAG;
+ case kCTFontFormatPostScript:
+ return SkSFNTHeader::fontType_PostScript::TAG;
+ case kCTFontFormatBitmap:
+ return SkSFNTHeader::fontType_MacTrueType::TAG;
+ case kCTFontFormatUnrecognized:
+ default:
+ return 0;
+ }
+}
+
+std::unique_ptr<SkStreamAsset> SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
+ *ttcIndex = 0;
+
+ fInitStream([this]{
+ if (fStream) {
+ return;
+ }
+
+ SK_SFNT_ULONG fontType = get_font_type_tag(fFontRef.get());
+
+ // get table tags
+ int numTables = this->countTables();
+ SkTDArray<SkFontTableTag> tableTags;
+ tableTags.setCount(numTables);
+ this->getTableTags(tableTags.begin());
+
+ // CT seems to be unreliable in being able to obtain the type,
+ // even if all we want is the first four bytes of the font resource.
+ // Just the presence of the FontForge 'FFTM' table seems to throw it off.
+ if (fontType == 0) {
+ fontType = SkSFNTHeader::fontType_WindowsTrueType::TAG;
+
+ // see https://skbug.com/7630#c7
+ bool couldBeCFF = false;
+ constexpr SkFontTableTag CFFTag = SkSetFourByteTag('C', 'F', 'F', ' ');
+ constexpr SkFontTableTag CFF2Tag = SkSetFourByteTag('C', 'F', 'F', '2');
+ for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+ if (CFFTag == tableTags[tableIndex] || CFF2Tag == tableTags[tableIndex]) {
+ couldBeCFF = true;
+ }
+ }
+ if (couldBeCFF) {
+ fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
+ }
+ }
+
+ // Sometimes CoreGraphics incorrectly thinks a font is kCTFontFormatPostScript.
+ // It is exceedingly unlikely that this is the case, so double check
+ // (see https://crbug.com/809763 ).
+ if (fontType == SkSFNTHeader::fontType_PostScript::TAG) {
+ // see if there are any required 'typ1' tables (see Adobe Technical Note #5180)
+ bool couldBeTyp1 = false;
+ constexpr SkFontTableTag TYPE1Tag = SkSetFourByteTag('T', 'Y', 'P', '1');
+ constexpr SkFontTableTag CIDTag = SkSetFourByteTag('C', 'I', 'D', ' ');
+ for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+ if (TYPE1Tag == tableTags[tableIndex] || CIDTag == tableTags[tableIndex]) {
+ couldBeTyp1 = true;
+ }
+ }
+ if (!couldBeTyp1) {
+ fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
+ }
+ }
+
+ // get the table sizes and accumulate the total size of the font
+ SkTDArray<size_t> tableSizes;
+ size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
+ for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+ size_t tableSize = this->getTableSize(tableTags[tableIndex]);
+ totalSize += (tableSize + 3) & ~3;
+ *tableSizes.append() = tableSize;
+ }
+
+ // reserve memory for stream, and zero it (tables must be zero padded)
+ fStream.reset(new SkMemoryStream(totalSize));
+ char* dataStart = (char*)fStream->getMemoryBase();
+ sk_bzero(dataStart, totalSize);
+ char* dataPtr = dataStart;
+
+ // compute font header entries
+ uint16_t entrySelector = 0;
+ uint16_t searchRange = 1;
+ while (searchRange < numTables >> 1) {
+ entrySelector++;
+ searchRange <<= 1;
+ }
+ searchRange <<= 4;
+ uint16_t rangeShift = (numTables << 4) - searchRange;
+
+ // write font header
+ SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
+ header->fontType = fontType;
+ header->numTables = SkEndian_SwapBE16(numTables);
+ header->searchRange = SkEndian_SwapBE16(searchRange);
+ header->entrySelector = SkEndian_SwapBE16(entrySelector);
+ header->rangeShift = SkEndian_SwapBE16(rangeShift);
+ dataPtr += sizeof(SkSFNTHeader);
+
+ // write tables
+ SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
+ dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
+ for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+ size_t tableSize = tableSizes[tableIndex];
+ this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
+ entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
+ entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
+ tableSize));
+ entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
+ entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
+
+ dataPtr += (tableSize + 3) & ~3;
+ ++entry;
+ }
+ });
+ return fStream->duplicate();
+}
+
+struct NonDefaultAxesContext {
+ SkFixed* axisValue;
+ CFArrayRef cgAxes;
+};
+static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
+ NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
+
+ if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
+ return;
+ }
+
+ // The key is a CFString which is a string from the 'name' table.
+ // Search the cgAxes for an axis with this name, and use its index to store the value.
+ CFIndex keyIndex = -1;
+ CFStringRef keyString = static_cast<CFStringRef>(key);
+ for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
+ CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
+ if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
+ continue;
+ }
+
+ CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
+ CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
+ if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
+ continue;
+ }
+ CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
+ if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
+ keyIndex = i;
+ break;
+ }
+ }
+ if (keyIndex == -1) {
+ return;
+ }
+
+ CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
+ double valueDouble;
+ if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
+ valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
+ {
+ return;
+ }
+ self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
+}
+static bool get_variations(CTFontRef ctFont, CFIndex* cgAxisCount,
+ SkAutoSTMalloc<4, SkFixed>* axisValues)
+{
+ // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
+ // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
+ // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
+ // CGFontCopyVariations here until support for 10.10 and earlier is removed.
+ SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
+ if (!cgFont) {
+ return false;
+ }
+
+ SkUniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
+ // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
+ if (!cgVariations) {
+ return false;
+ }
+
+ SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
+ if (!cgAxes) {
+ return false;
+ }
+ *cgAxisCount = CFArrayGetCount(cgAxes.get());
+ axisValues->reset(*cgAxisCount);
+
+ // Set all of the axes to their default values.
+ // Fail if any default value cannot be determined.
+ for (CFIndex i = 0; i < *cgAxisCount; ++i) {
+ CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes.get(), i);
+ if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
+ return false;
+ }
+
+ CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
+ CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
+ kCGFontVariationAxisDefaultValue);
+ if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
+ return false;
+ }
+ CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
+ double axisDefaultValueDouble;
+ if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
+ {
+ return false;
+ }
+ if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
+ SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
+ {
+ return false;
+ }
+ (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
+ }
+
+ // Override the default values with the given font's stated axis values.
+ NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
+ CFDictionaryApplyFunction(cgVariations.get(), set_non_default_axes, &c);
+
+ return true;
+}
+std::unique_ptr<SkFontData> SkTypeface_Mac::onMakeFontData() const {
+ int index;
+ std::unique_ptr<SkStreamAsset> stream(this->onOpenStream(&index));
+
+ CFIndex cgAxisCount;
+ SkAutoSTMalloc<4, SkFixed> axisValues;
+ if (get_variations(fFontRef.get(), &cgAxisCount, &axisValues)) {
+ return std::make_unique<SkFontData>(std::move(stream), index,
+ axisValues.get(), cgAxisCount);
+ }
+ return std::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
+}
+
+/** Creates a CT variation dictionary {tag, value} from a CG variation dictionary {name, value}. */
+static SkUniqueCFRef<CFDictionaryRef> ct_variation_from_cg_variation(CFDictionaryRef cgVariations,
+ CFArrayRef ctAxes) {
+
+ SkUniqueCFRef<CFMutableDictionaryRef> ctVariation(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ CFIndex axisCount = CFArrayGetCount(ctAxes);
+ for (CFIndex i = 0; i < axisCount; ++i) {
+ CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes, i);
+ if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
+ return nullptr;
+ }
+ CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
+
+ // The assumption is that values produced by kCTFontVariationAxisNameKey and
+ // kCGFontVariationAxisName will always be equal.
+ CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
+ if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
+ return nullptr;
+ }
+
+ CFTypeRef axisValue = CFDictionaryGetValue(cgVariations, axisName);
+ if (!axisValue || CFGetTypeID(axisValue) != CFNumberGetTypeID()) {
+ return nullptr;
+ }
+
+ CFTypeRef axisTag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
+ if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) {
+ return nullptr;
+ }
+
+ CFDictionaryAddValue(ctVariation.get(), axisTag, axisValue);
+ }
+ return std::move(ctVariation);
+}
+
+int SkTypeface_Mac::onGetVariationDesignPosition(
+ SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
+{
+ // The CGFont variation data does not contain the tag.
+
+ // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
+ // macOS 10.10 and iOS 9 or earlier. When this happens, there is no API to provide the tag.
+ SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
+ if (!ctAxes) {
+ return -1;
+ }
+ CFIndex axisCount = CFArrayGetCount(ctAxes.get());
+ if (!coordinates || coordinateCount < axisCount) {
+ return axisCount;
+ }
+
+ // This call always returns nullptr on 10.11 and under for CGFontCreateWithDataProvider fonts.
+ // When this happens, try converting the CG variation to a CT variation.
+ // On 10.12 and later, this only returns non-default variations.
+ SkUniqueCFRef<CFDictionaryRef> ctVariation(CTFontCopyVariation(fFontRef.get()));
+ if (!ctVariation) {
+ // When 10.11 and earlier are no longer supported, the following code can be replaced with
+ // return -1 and ct_variation_from_cg_variation can be removed.
+ SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
+ if (!cgFont) {
+ return -1;
+ }
+ SkUniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
+ if (!cgVariations) {
+ return -1;
+ }
+ ctVariation = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get());
+ if (!ctVariation) {
+ return -1;
+ }
+ }
+
+ for (int i = 0; i < axisCount; ++i) {
+ CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
+ if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
+ return -1;
+ }
+ CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
+
+ CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
+ if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
+ return -1;
+ }
+ CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
+ int64_t tagLong;
+ if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
+ return -1;
+ }
+ coordinates[i].axis = tagLong;
+
+ CGFloat variationCGFloat;
+ CFTypeRef variationValue = CFDictionaryGetValue(ctVariation.get(), tagNumber);
+ if (variationValue) {
+ if (CFGetTypeID(variationValue) != CFNumberGetTypeID()) {
+ return -1;
+ }
+ CFNumberRef variationNumber = static_cast<CFNumberRef>(variationValue);
+ if (!CFNumberGetValue(variationNumber, kCFNumberCGFloatType, &variationCGFloat)) {
+ return -1;
+ }
+ } else {
+ CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
+ if (!def || CFGetTypeID(def) != CFNumberGetTypeID()) {
+ return -1;
+ }
+ CFNumberRef defNumber = static_cast<CFNumberRef>(def);
+ if (!CFNumberGetValue(defNumber, kCFNumberCGFloatType, &variationCGFloat)) {
+ return -1;
+ }
+ }
+ coordinates[i].value = SkScalarFromCGFloat(variationCGFloat);
+
+ }
+ return axisCount;
+}
+
+int SkTypeface_Mac::onGetUPEM() const {
+ SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
+ return CGFontGetUnitsPerEm(cgFont.get());
+}
+
+SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
+ sk_sp<SkTypeface::LocalizedStrings> nameIter =
+ SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(*this);
+ if (!nameIter) {
+ CFStringRef cfLanguageRaw;
+ SkUniqueCFRef<CFStringRef> cfFamilyName(
+ CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
+ SkUniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
+
+ SkString skLanguage;
+ SkString skFamilyName;
+ if (cfLanguage) {
+ SkStringFromCFString(cfLanguage.get(), &skLanguage);
+ } else {
+ skLanguage = "und"; //undetermined
+ }
+ if (cfFamilyName) {
+ SkStringFromCFString(cfFamilyName.get(), &skFamilyName);
+ }
+
+ nameIter = sk_make_sp<SkOTUtils::LocalizedStrings_SingleName>(skFamilyName, skLanguage);
+ }
+ return nameIter.release();
+}
+
+int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
+ SkUniqueCFRef<CFArrayRef> cfArray(
+ CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
+ if (!cfArray) {
+ return 0;
+ }
+ int count = SkToInt(CFArrayGetCount(cfArray.get()));
+ if (tags) {
+ for (int i = 0; i < count; ++i) {
+ uintptr_t fontTag = reinterpret_cast<uintptr_t>(
+ CFArrayGetValueAtIndex(cfArray.get(), i));
+ tags[i] = static_cast<SkFontTableTag>(fontTag);
+ }
+ }
+ return count;
+}
+
+// If, as is the case with web fonts, the CTFont data isn't available,
+// the CGFont data may work. While the CGFont may always provide the
+// right result, leave the CTFont code path to minimize disruption.
+static SkUniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
+ SkUniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
+ kCTFontTableOptionNoOptions));
+ if (!data) {
+ SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
+ data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
+ }
+ return data;
+}
+
+size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
+ size_t length, void* dstData) const {
+ SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
+ if (!srcData) {
+ return 0;
+ }
+
+ size_t srcSize = CFDataGetLength(srcData.get());
+ if (offset >= srcSize) {
+ return 0;
+ }
+ if (length > srcSize - offset) {
+ length = srcSize - offset;
+ }
+ if (dstData) {
+ memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
+ }
+ return length;
+}
+
+sk_sp<SkData> SkTypeface_Mac::onCopyTableData(SkFontTableTag tag) const {
+ SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
+ if (!srcData) {
+ return nullptr;
+ }
+ const UInt8* data = CFDataGetBytePtr(srcData.get());
+ CFIndex length = CFDataGetLength(srcData.get());
+ return SkData::MakeWithProc(data, length,
+ [](const void*, void* ctx) {
+ CFRelease((CFDataRef)ctx);
+ }, (void*)srcData.release());
+}
+
+SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkScalerContextEffects& effects,
+ const SkDescriptor* desc) const {
+ return new SkScalerContext_Mac(sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
+}
+
+void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
+ if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
+ rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
+ {
+ rec->fMaskFormat = SkMask::kA8_Format;
+ // Render the glyphs as close as possible to what was requested.
+ // The above turns off subpixel rendering, but the user requested it.
+ // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
+ // See comments below for more details.
+ rec->setHinting(SkFontHinting::kNormal);
+ }
+
+ unsigned flagsWeDontSupport = SkScalerContext::kForceAutohinting_Flag |
+ SkScalerContext::kLCD_BGROrder_Flag |
+ SkScalerContext::kLCD_Vertical_Flag;
+
+ rec->fFlags &= ~flagsWeDontSupport;
+
+ const SkCTFontSmoothBehavior smoothBehavior = SkCTFontGetSmoothBehavior();
+
+ // Only two levels of hinting are supported.
+ // kNo_Hinting means avoid CoreGraphics outline dilation (smoothing).
+ // kNormal_Hinting means CoreGraphics outline dilation (smoothing) is allowed.
+ if (rec->getHinting() != SkFontHinting::kNone) {
+ rec->setHinting(SkFontHinting::kNormal);
+ }
+ // If smoothing has no effect, don't request it.
+ if (smoothBehavior == SkCTFontSmoothBehavior::none) {
+ rec->setHinting(SkFontHinting::kNone);
+ }
+
+ // FIXME: lcd smoothed un-hinted rasterization unsupported.
+ // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
+ // There is no current means to honor a request for unhinted lcd,
+ // so arbitrarilly ignore the hinting request and honor lcd.
+
+ // Hinting and smoothing should be orthogonal, but currently they are not.
+ // CoreGraphics has no API to influence hinting. However, its lcd smoothed
+ // output is drawn from auto-dilated outlines (the amount of which is
+ // determined by AppleFontSmoothing). Its regular anti-aliased output is
+ // drawn from un-dilated outlines.
+
+ // The behavior of Skia is as follows:
+ // [AA][no-hint]: generate AA using CoreGraphic's AA output.
+ // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
+ // channel. This matches [LCD][yes-hint] in weight.
+ // [LCD][no-hint]: currently unable to honor, and must pick which to respect.
+ // Currently side with LCD, effectively ignoring the hinting setting.
+ // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
+ if (rec->fMaskFormat == SkMask::kLCD16_Format) {
+ if (smoothBehavior == SkCTFontSmoothBehavior::subpixel) {
+ //CoreGraphics creates 555 masks for smoothed text anyway.
+ rec->fMaskFormat = SkMask::kLCD16_Format;
+ rec->setHinting(SkFontHinting::kNormal);
+ } else {
+ rec->fMaskFormat = SkMask::kA8_Format;
+ if (smoothBehavior != SkCTFontSmoothBehavior::none) {
+ rec->setHinting(SkFontHinting::kNormal);
+ }
+ }
+ }
+
+ // CoreText provides no information as to whether a glyph will be color or not.
+ // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
+ // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
+ if (fHasColorGlyphs) {
+ rec->fMaskFormat = SkMask::kARGB32_Format;
+ }
+
+ // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
+ // All other masks can use regular gamma.
+ if (SkMask::kA8_Format == rec->fMaskFormat && SkFontHinting::kNone == rec->getHinting()) {
+#ifndef SK_GAMMA_APPLY_TO_A8
+ // SRGBTODO: Is this correct? Do we want contrast boost?
+ rec->ignorePreBlend();
+#endif
+ } else {
+ SkColor color = rec->getLuminanceColor();
+ if (smoothBehavior == SkCTFontSmoothBehavior::some) {
+ // CoreGraphics smoothed text without subpixel coverage blitting goes from a gamma of
+ // 2.0 for black foreground to a gamma of 1.0 for white foreground. Emulate this
+ // through the mask gamma by reducing the color values to 1/2.
+ color = SkColorSetRGB(SkColorGetR(color) * 1/2,
+ SkColorGetG(color) * 1/2,
+ SkColorGetB(color) * 1/2);
+ } else if (smoothBehavior == SkCTFontSmoothBehavior::subpixel) {
+ // CoreGraphics smoothed text with subpixel coverage blitting goes from a gamma of
+ // 2.0 for black foreground to a gamma of ~1.4? for white foreground. Emulate this
+ // through the mask gamma by reducing the color values to 3/4.
+ color = SkColorSetRGB(SkColorGetR(color) * 3/4,
+ SkColorGetG(color) * 3/4,
+ SkColorGetB(color) * 3/4);
+ }
+ rec->setLuminanceColor(color);
+
+ // CoreGraphics dialates smoothed text to provide contrast.
+ rec->setContrast(0);
+ }
+}
+
+/** Takes ownership of the CFStringRef. */
+static const char* get_str(CFStringRef ref, SkString* str) {
+ if (nullptr == ref) {
+ return nullptr;
+ }
+ SkStringFromCFString(ref, str);
+ CFRelease(ref);
+ return str->c_str();
+}
+
+void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
+ get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
+}
+
+void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
+ bool* isLocalStream) const {
+ SkString tmpStr;
+
+ desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
+ desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
+ desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
+ desc->setStyle(this->fontStyle());
+ *isLocalStream = fIsFromStream;
+}
+
+void SkTypeface_Mac::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const {
+ // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
+ // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
+ // It is documented that if a mapping is unavailable, the glyph will be set to 0.
+
+ SkAutoSTMalloc<1024, UniChar> charStorage;
+ const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
+ int srcCount;
+ const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(uni);
+ UniChar* utf16 = charStorage.reset(2 * count);
+ src = utf16;
+ for (int i = 0; i < count; ++i) {
+ utf16 += SkUTF::ToUTF16(utf32[i], utf16);
+ }
+ srcCount = SkToInt(utf16 - src);
+
+ // If there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
+ SkAutoSTMalloc<1024, uint16_t> glyphStorage;
+ uint16_t* macGlyphs = glyphs;
+ if (srcCount > count) {
+ macGlyphs = glyphStorage.reset(srcCount);
+ }
+
+ CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
+
+ // If there were any non-bmp, then copy and compact.
+ // If all are bmp, 'glyphs' already contains the compact glyphs.
+ // If some are non-bmp, copy and compact into 'glyphs'.
+ if (srcCount > count) {
+ SkASSERT(glyphs != macGlyphs);
+ int extra = 0;
+ for (int i = 0; i < count; ++i) {
+ glyphs[i] = macGlyphs[i + extra];
+ if (SkUTF16_IsLeadingSurrogate(src[i + extra])) {
+ ++extra;
+ }
+ }
+ } else {
+ SkASSERT(glyphs == macGlyphs);
+ }
+}
+
+int SkTypeface_Mac::onCountGlyphs() const {
+ return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
+}
+
+/** Creates a dictionary suitable for setting the axes on a CTFont. */
+CTFontVariation SkCTVariationFromSkFontArguments(CTFontRef ct, const SkFontArguments& args) {
+ OpszVariation opsz;
+ constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
+
+ SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
+ if (!ctAxes) {
+ return CTFontVariation();
+ }
+ CFIndex axisCount = CFArrayGetCount(ctAxes.get());
+
+ const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
+
+ SkUniqueCFRef<CFMutableDictionaryRef> dict(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ for (int i = 0; i < axisCount; ++i) {
+ CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
+ if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
+ return CTFontVariation();
+ }
+ CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
+
+ CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
+ if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
+ return CTFontVariation();
+ }
+ CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
+ int64_t tagLong;
+ if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
+ return CTFontVariation();
+ }
+
+ // The variation axes can be set to any value, but cg will effectively pin them.
+ // Pin them here to normalize.
+ CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
+ CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
+ CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
+ if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
+ !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
+ !def || CFGetTypeID(def) != CFNumberGetTypeID())
+ {
+ return CTFontVariation();
+ }
+ CFNumberRef minNumber = static_cast<CFNumberRef>(min);
+ CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
+ CFNumberRef defNumber = static_cast<CFNumberRef>(def);
+ double minDouble;
+ double maxDouble;
+ double defDouble;
+ if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
+ !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
+ !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
+ {
+ return CTFontVariation();
+ }
+
+ double value = defDouble;
+ // The position may be over specified. If there are multiple values for a given axis,
+ // use the last one since that's what css-fonts-4 requires.
+ for (int j = position.coordinateCount; j --> 0;) {
+ if (position.coordinates[j].axis == tagLong) {
+ value = SkTPin(SkScalarToDouble(position.coordinates[j].value),
+ minDouble, maxDouble);
+ if (tagLong == opszTag) {
+ opsz.isSet = true;
+ }
+ break;
+ }
+ }
+ if (tagLong == opszTag) {
+ opsz.value = value;
+ }
+ SkUniqueCFRef<CFNumberRef> valueNumber(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
+ CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
+ }
+ return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
+}
+
+sk_sp<SkTypeface> SkTypeface_Mac::onMakeClone(const SkFontArguments& args) const {
+ CTFontVariation ctVariation = SkCTVariationFromSkFontArguments(fFontRef.get(), args);
+
+ SkUniqueCFRef<CTFontRef> ctVariant;
+ if (ctVariation.dict) {
+ SkUniqueCFRef<CFMutableDictionaryRef> attributes(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ CFDictionaryAddValue(attributes.get(),
+ kCTFontVariationAttribute, ctVariation.dict.get());
+ SkUniqueCFRef<CTFontDescriptorRef> varDesc(
+ CTFontDescriptorCreateWithAttributes(attributes.get()));
+ ctVariant.reset(CTFontCreateCopyWithAttributes(fFontRef.get(), 0, nullptr, varDesc.get()));
+ } else {
+ ctVariant.reset((CTFontRef)CFRetain(fFontRef.get()));
+ }
+ if (!ctVariant) {
+ return nullptr;
+ }
+
+ return SkTypeface_Mac::Make(std::move(ctVariant), ctVariation.opsz,
+ fStream ? fStream->duplicate() : nullptr);
+}
+
+int SkTypeface_Mac::onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
+ int parameterCount) const
+{
+ SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
+ if (!ctAxes) {
+ return -1;
+ }
+ CFIndex axisCount = CFArrayGetCount(ctAxes.get());
+
+ if (!parameters || parameterCount < axisCount) {
+ return axisCount;
+ }
+
+ // Added in 10.13
+ CFStringRef* kCTFontVariationAxisHiddenKeyPtr =
+ static_cast<CFStringRef*>(dlsym(RTLD_DEFAULT, "kCTFontVariationAxisHiddenKey"));
+
+ for (int i = 0; i < axisCount; ++i) {
+ CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
+ if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
+ return -1;
+ }
+ CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
+
+ CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
+ if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
+ return -1;
+ }
+ CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
+ int64_t tagLong;
+ if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
+ return -1;
+ }
+
+ CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
+ CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
+ CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
+ if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
+ !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
+ !def || CFGetTypeID(def) != CFNumberGetTypeID())
+ {
+ return -1;
+ }
+ CFNumberRef minNumber = static_cast<CFNumberRef>(min);
+ CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
+ CFNumberRef defNumber = static_cast<CFNumberRef>(def);
+ double minDouble;
+ double maxDouble;
+ double defDouble;
+ if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
+ !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
+ !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
+ {
+ return -1;
+ }
+
+ SkFontParameters::Variation::Axis& skAxis = parameters[i];
+ skAxis.tag = tagLong;
+ skAxis.min = minDouble;
+ skAxis.max = maxDouble;
+ skAxis.def = defDouble;
+ skAxis.setHidden(false);
+ if (kCTFontVariationAxisHiddenKeyPtr) {
+ CFTypeRef hidden = CFDictionaryGetValue(axisInfoDict,*kCTFontVariationAxisHiddenKeyPtr);
+ if (hidden) {
+ if (CFGetTypeID(hidden) != CFBooleanGetTypeID()) {
+ return -1;
+ }
+ CFBooleanRef hiddenBoolean = static_cast<CFBooleanRef>(hidden);
+ skAxis.setHidden(CFBooleanGetValue(hiddenBoolean));
+ }
+ }
+ }
+ return axisCount;
+}
+
+#endif
diff --git a/src/ports/SkTypeface_mac_ct.h b/src/ports/SkTypeface_mac_ct.h
new file mode 100644
index 0000000..49f55b3
--- /dev/null
+++ b/src/ports/SkTypeface_mac_ct.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkTypeface_mac_ct_DEFINED
+#define SkTypeface_mac_ct_DEFINED
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#include "include/core/SkFontArguments.h"
+#include "include/core/SkFontParameters.h"
+#include "include/core/SkFontStyle.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkStream.h"
+#include "include/core/SkTypeface.h"
+#include "include/private/SkOnce.h"
+#include "src/utils/mac/SkUniqueCFRef.h"
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include <memory>
+
+class SkData;
+class SkDescriptor;
+class SkFontData;
+class SkFontDescriptor;
+class SkScalerContext;
+class SkString;
+struct SkAdvancedTypefaceMetrics;
+struct SkScalerContextEffects;
+struct SkScalerContextRec;
+
+struct OpszVariation {
+ bool isSet = false;
+ double value = 0;
+};
+
+struct CTFontVariation {
+ SkUniqueCFRef<CFDictionaryRef> dict;
+ OpszVariation opsz;
+};
+
+CTFontVariation SkCTVariationFromSkFontArguments(CTFontRef ct, const SkFontArguments& args);
+
+SkUniqueCFRef<CTFontRef> SkCTFontCreateExactCopy(CTFontRef baseFont, CGFloat textSize,
+ OpszVariation opsz);
+
+SkFontStyle SkCTFontDescriptorGetSkFontStyle(CTFontDescriptorRef desc, bool fromDataProvider);
+
+CGFloat SkCTFontCTWeightForCSSWeight(int fontstyleWeight);
+CGFloat SkCTFontCTWidthForCSSWidth(int fontstyleWidth);
+
+void SkStringFromCFString(CFStringRef src, SkString* dst);
+
+class SkTypeface_Mac : public SkTypeface {
+private:
+ SkTypeface_Mac(SkUniqueCFRef<CTFontRef> fontRef, const SkFontStyle& fs, bool isFixedPitch,
+ OpszVariation opszVariation, std::unique_ptr<SkStreamAsset> providedData)
+ : SkTypeface(fs, isFixedPitch)
+ , fFontRef(std::move(fontRef))
+ , fOpszVariation(opszVariation)
+ , fHasColorGlyphs(
+ SkToBool(CTFontGetSymbolicTraits(fFontRef.get()) & kCTFontColorGlyphsTrait))
+ , fStream(std::move(providedData))
+ , fIsFromStream(fStream)
+ {
+ SkASSERT(fFontRef);
+ }
+
+public:
+ static sk_sp<SkTypeface> Make(SkUniqueCFRef<CTFontRef> font,
+ OpszVariation opszVariation,
+ std::unique_ptr<SkStreamAsset> providedData);
+
+ SkUniqueCFRef<CTFontRef> fFontRef;
+ const OpszVariation fOpszVariation;
+ const bool fHasColorGlyphs;
+
+protected:
+ int onGetUPEM() const override;
+ std::unique_ptr<SkStreamAsset> onOpenStream(int* ttcIndex) const override;
+ std::unique_ptr<SkFontData> onMakeFontData() const override;
+ int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
+ int coordinateCount) const override;
+ void onGetFamilyName(SkString* familyName) const override;
+ SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
+ int onGetTableTags(SkFontTableTag tags[]) const override;
+ size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
+ sk_sp<SkData> onCopyTableData(SkFontTableTag) const override;
+ SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
+ const SkDescriptor*) const override;
+ void onFilterRec(SkScalerContextRec*) const override;
+ void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
+ void getGlyphToUnicodeMap(SkUnichar*) const override;
+ std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
+ void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override;
+ int onCountGlyphs() const override;
+ void getPostScriptGlyphNames(SkString*) const override {}
+ int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
+ int parameterCount) const override;
+ sk_sp<SkTypeface> onMakeClone(const SkFontArguments&) const override;
+
+ void* onGetCTFontRef() const override { return (void*)fFontRef.get(); }
+
+private:
+ mutable std::unique_ptr<SkStreamAsset> fStream;
+ bool fIsFromStream;
+ mutable SkOnce fInitStream;
+
+ typedef SkTypeface INHERITED;
+};
+
+#endif
+#endif //SkTypeface_mac_ct_DEFINED
diff --git a/src/utils/mac/SkCGBase.h b/src/utils/mac/SkCGBase.h
new file mode 100644
index 0000000..4874a72f
--- /dev/null
+++ b/src/utils/mac/SkCGBase.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCGBase_DEFINED
+#define SkCGBase_DEFINED
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreGraphics/CoreGraphics.h>
+#endif
+
+// Skia extensions for types in CGBase.h
+
+static inline CGFloat SkScalarToCGFloat(SkScalar scalar) {
+ if (sizeof(CGFloat) == sizeof(float)) {
+ return SkScalarToFloat(scalar);
+ } else {
+ SkASSERT(sizeof(CGFloat) == sizeof(double));
+ return (CGFloat) SkScalarToDouble(scalar);
+ }
+}
+
+static inline SkScalar SkScalarFromCGFloat(CGFloat cgFloat) {
+ if (sizeof(CGFloat) == sizeof(float)) {
+ return SkFloatToScalar(cgFloat);
+ } else {
+ SkASSERT(sizeof(CGFloat) == sizeof(double));
+ return SkDoubleToScalar(cgFloat);
+ }
+}
+
+static inline float SkFloatFromCGFloat(CGFloat cgFloat) {
+ if (sizeof(CGFloat) == sizeof(float)) {
+ return cgFloat;
+ } else {
+ SkASSERT(sizeof(CGFloat) == sizeof(double));
+ return static_cast<float>(cgFloat);
+ }
+}
+
+#endif
+#endif //SkCGBase_DEFINED
diff --git a/src/utils/mac/SkCGGeometry.h b/src/utils/mac/SkCGGeometry.h
new file mode 100644
index 0000000..04b1263
--- /dev/null
+++ b/src/utils/mac/SkCGGeometry.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCGGeometry_DEFINED
+#define SkCGGeometry_DEFINED
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreGraphics/CoreGraphics.h>
+#endif
+
+// Skia extensions for types in CGGeometry.h
+
+// Inline versions of these CGRect helpers.
+// The CG versions require making a call and a copy of the CGRect on the stack.
+
+static inline bool SkCGRectIsEmpty(const CGRect& rect) {
+ return rect.size.width <= 0 || rect.size.height <= 0;
+}
+
+static inline CGFloat SkCGRectGetMinX(const CGRect& rect) {
+ return rect.origin.x;
+}
+
+static inline CGFloat SkCGRectGetMaxX(const CGRect& rect) {
+ return rect.origin.x + rect.size.width;
+}
+
+static inline CGFloat SkCGRectGetMinY(const CGRect& rect) {
+ return rect.origin.y;
+}
+
+static inline CGFloat SkCGRectGetMaxY(const CGRect& rect) {
+ return rect.origin.y + rect.size.height;
+}
+
+static inline CGFloat SkCGRectGetWidth(const CGRect& rect) {
+ return rect.size.width;
+}
+
+#endif
+#endif //SkCGGeometry_DEFINED
diff --git a/src/utils/mac/SkCTFontSmoothBehavior.cpp b/src/utils/mac/SkCTFontSmoothBehavior.cpp
new file mode 100644
index 0000000..7836706
--- /dev/null
+++ b/src/utils/mac/SkCTFontSmoothBehavior.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypes.h"
+
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+#include "src/utils/mac/SkCTFontSmoothBehavior.h"
+#include "src/utils/mac/SkUniqueCFRef.h"
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreText/CTFontManager.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
+
+/** Drawn in FontForge, reduced with fonttools ttx, converted by xxd -i,
+ * this TrueType font contains a glyph of the spider.
+ *
+ * To re-forge the original bytes of the TrueType font file,
+ * remove all ',|( +0x)' from this definition,
+ * copy the data to the clipboard,
+ * run 'pbpaste | xxd -p -r - spider.ttf'.
+ */
+static constexpr const uint8_t kSpiderSymbol_ttf[] = {
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x80, 0x00, 0x03, 0x00, 0x40,
+ 0x47, 0x44, 0x45, 0x46, 0x00, 0x14, 0x00, 0x14, 0x00, 0x00, 0x07, 0xa8,
+ 0x00, 0x00, 0x00, 0x18, 0x4f, 0x53, 0x2f, 0x32, 0x8a, 0xf4, 0xfb, 0xdb,
+ 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x00, 0x60, 0x63, 0x6d, 0x61, 0x70,
+ 0xe0, 0x7f, 0x10, 0x7e, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x54,
+ 0x67, 0x61, 0x73, 0x70, 0xff, 0xff, 0x00, 0x03, 0x00, 0x00, 0x07, 0xa0,
+ 0x00, 0x00, 0x00, 0x08, 0x67, 0x6c, 0x79, 0x66, 0x97, 0x0b, 0x6a, 0xf6,
+ 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x03, 0x40, 0x68, 0x65, 0x61, 0x64,
+ 0x0f, 0xa2, 0x24, 0x1a, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x36,
+ 0x68, 0x68, 0x65, 0x61, 0x0e, 0xd3, 0x07, 0x3f, 0x00, 0x00, 0x01, 0x04,
+ 0x00, 0x00, 0x00, 0x24, 0x68, 0x6d, 0x74, 0x78, 0x10, 0x03, 0x00, 0x44,
+ 0x00, 0x00, 0x01, 0xa8, 0x00, 0x00, 0x00, 0x0e, 0x6c, 0x6f, 0x63, 0x61,
+ 0x01, 0xb4, 0x00, 0x28, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x0a,
+ 0x6d, 0x61, 0x78, 0x70, 0x00, 0x4a, 0x01, 0x4d, 0x00, 0x00, 0x01, 0x28,
+ 0x00, 0x00, 0x00, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0xc3, 0xe5, 0x39, 0xd4,
+ 0x00, 0x00, 0x05, 0x58, 0x00, 0x00, 0x02, 0x28, 0x70, 0x6f, 0x73, 0x74,
+ 0xff, 0x03, 0x00, 0x67, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0b, 0x0f, 0x08, 0x1d,
+ 0x5f, 0x0f, 0x3c, 0xf5, 0x00, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd1, 0x97, 0xa8, 0x5a, 0x00, 0x00, 0x00, 0x00, 0xd6, 0xe8, 0x32, 0x33,
+ 0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x55, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x05, 0x55, 0xff, 0x3b, 0x01, 0x79, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x04, 0x01, 0x1c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x2e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x01, 0x90, 0x00, 0x05,
+ 0x00, 0x00, 0x05, 0x33, 0x05, 0x99, 0x00, 0x00, 0x01, 0x1e, 0x05, 0x33,
+ 0x05, 0x99, 0x00, 0x00, 0x03, 0xd7, 0x00, 0x66, 0x02, 0x12, 0x00, 0x00,
+ 0x05, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x73, 0x6b, 0x69, 0x61, 0x00, 0xc0, 0x00, 0x00, 0xf0, 0x21,
+ 0x06, 0x66, 0xfe, 0x66, 0x01, 0x79, 0x05, 0x55, 0x00, 0xc5, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x01, 0x08, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x48,
+ 0x00, 0x00, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00,
+ 0x00, 0x09, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21, 0xf0, 0x21, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21,
+ 0xf0, 0x21, 0xff, 0xff, 0x00, 0x01, 0xff, 0xf9, 0xff, 0xf5, 0xff, 0xe4,
+ 0xff, 0xe2, 0x0f, 0xe2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
+ 0x00, 0x14, 0x00, 0x14, 0x01, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x44,
+ 0x00, 0x00, 0x02, 0x64, 0x05, 0x55, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00,
+ 0x33, 0x11, 0x21, 0x11, 0x25, 0x21, 0x11, 0x21, 0x44, 0x02, 0x20, 0xfe,
+ 0x24, 0x01, 0x98, 0xfe, 0x68, 0x05, 0x55, 0xfa, 0xab, 0x44, 0x04, 0xcd,
+ 0x00, 0x04, 0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x4c, 0x00, 0x15,
+ 0x00, 0x1d, 0x00, 0x25, 0x01, 0x1b, 0x00, 0x00, 0x01, 0x36, 0x37, 0x36,
+ 0x27, 0x26, 0x07, 0x06, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27, 0x26, 0x07,
+ 0x06, 0x17, 0x16, 0x17, 0x16, 0x32, 0x37, 0x32, 0x35, 0x34, 0x23, 0x22,
+ 0x15, 0x14, 0x27, 0x32, 0x35, 0x34, 0x23, 0x22, 0x15, 0x14, 0x03, 0x32,
+ 0x17, 0x30, 0x17, 0x31, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33,
+ 0x32, 0x33, 0x16, 0x33, 0x32, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27,
+ 0x26, 0x27, 0x26, 0x23, 0x22, 0x07, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
+ 0x1f, 0x02, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32, 0x17, 0x17, 0x16, 0x33,
+ 0x16, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27, 0x27, 0x26, 0x23, 0x22,
+ 0x07, 0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x33, 0x32, 0x33, 0x32,
+ 0x37, 0x36, 0x37, 0x36, 0x17, 0x16, 0x1f, 0x02, 0x16, 0x17, 0x16, 0x15,
+ 0x14, 0x23, 0x22, 0x27, 0x27, 0x26, 0x27, 0x27, 0x26, 0x27, 0x26, 0x07,
+ 0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x07,
+ 0x06, 0x23, 0x22, 0x27, 0x26, 0x07, 0x06, 0x07, 0x06, 0x15, 0x14, 0x17,
+ 0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27,
+ 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x34, 0x27, 0x26, 0x07,
+ 0x06, 0x07, 0x06, 0x0f, 0x02, 0x06, 0x23, 0x22, 0x27, 0x26, 0x35, 0x34,
+ 0x37, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x27, 0x26, 0x27,
+ 0x26, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x07, 0x06, 0x23, 0x22,
+ 0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x37, 0x36, 0x37, 0x37, 0x36,
+ 0x37, 0x36, 0x37, 0x36, 0x35, 0x34, 0x27, 0x26, 0x27, 0x26, 0x27, 0x26,
+ 0x23, 0x22, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x27, 0x26, 0x27, 0x26,
+ 0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32,
+ 0x17, 0x16, 0x33, 0x32, 0x37, 0x36, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36,
+ 0x33, 0x04, 0xf5, 0x23, 0x13, 0x11, 0x14, 0x16, 0x1d, 0x1b, 0x4c, 0x1f,
+ 0x0e, 0x2d, 0x23, 0x14, 0x2c, 0x13, 0x18, 0x25, 0x2c, 0x10, 0x3c, 0x71,
+ 0x1d, 0x5c, 0x5c, 0x3f, 0xae, 0x5c, 0x5c, 0x3f, 0x6a, 0x27, 0x31, 0x5b,
+ 0x09, 0x27, 0x36, 0x03, 0x0a, 0x26, 0x35, 0x2e, 0x09, 0x08, 0xc6, 0x13,
+ 0x81, 0x17, 0x20, 0x18, 0x21, 0x1e, 0x04, 0x04, 0x15, 0x5c, 0x22, 0x26,
+ 0x48, 0x56, 0x3b, 0x10, 0x21, 0x01, 0x0c, 0x06, 0x06, 0x0f, 0x31, 0x44,
+ 0x3c, 0x52, 0x4a, 0x1d, 0x11, 0x3f, 0xb4, 0x71, 0x01, 0x26, 0x06, 0x0d,
+ 0x15, 0x1a, 0x2a, 0x13, 0x53, 0xaa, 0x42, 0x1d, 0x0a, 0x33, 0x20, 0x21,
+ 0x2b, 0x01, 0x02, 0x3e, 0x21, 0x09, 0x02, 0x02, 0x0f, 0x2d, 0x4b, 0x0a,
+ 0x22, 0x15, 0x20, 0x1f, 0x72, 0x8b, 0x2d, 0x2f, 0x1d, 0x1f, 0x0e, 0x25,
+ 0x3f, 0x4d, 0x1b, 0x63, 0x2a, 0x2c, 0x14, 0x22, 0x18, 0x1c, 0x0f, 0x08,
+ 0x2a, 0x08, 0x08, 0x0d, 0x3b, 0x4c, 0x52, 0x74, 0x27, 0x71, 0x2e, 0x01,
+ 0x0c, 0x10, 0x15, 0x0d, 0x06, 0x0d, 0x05, 0x01, 0x06, 0x2c, 0x28, 0x14,
+ 0x1b, 0x05, 0x04, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x16, 0x27, 0x03, 0x0d,
+ 0x30, 0x4c, 0x4c, 0x4b, 0x1f, 0x0b, 0x22, 0x26, 0x0d, 0x15, 0x0d, 0x2d,
+ 0x68, 0x34, 0x14, 0x3c, 0x25, 0x12, 0x04, 0x10, 0x18, 0x0b, 0x09, 0x30,
+ 0x2b, 0x44, 0x66, 0x14, 0x47, 0x47, 0x59, 0x73, 0x25, 0x05, 0x03, 0x1f,
+ 0x01, 0x08, 0x3f, 0x48, 0x4b, 0x4b, 0x76, 0x2f, 0x49, 0x2d, 0x22, 0x24,
+ 0x0c, 0x15, 0x08, 0x0e, 0x33, 0x03, 0x44, 0x4c, 0x10, 0x46, 0x13, 0x1f,
+ 0x27, 0x1b, 0x1d, 0x13, 0x02, 0x24, 0x08, 0x02, 0x42, 0x0e, 0x4d, 0x3c,
+ 0x19, 0x1b, 0x40, 0x2b, 0x2b, 0x1e, 0x16, 0x11, 0x04, 0x1f, 0x11, 0x04,
+ 0x18, 0x11, 0x35, 0x01, 0xa3, 0x13, 0x24, 0x1f, 0x0b, 0x0c, 0x19, 0x19,
+ 0x18, 0x13, 0x0f, 0x0c, 0x1a, 0x18, 0x1f, 0x19, 0x1e, 0x07, 0x1a, 0xc3,
+ 0x54, 0x51, 0x54, 0x51, 0x04, 0x53, 0x51, 0x54, 0x50, 0x02, 0x48, 0x1a,
+ 0x31, 0x18, 0x55, 0x74, 0x04, 0x0e, 0x09, 0x0d, 0x06, 0x10, 0x16, 0x1b,
+ 0x24, 0x01, 0x04, 0x0b, 0x04, 0x10, 0x3f, 0x0a, 0x41, 0x02, 0x41, 0x20,
+ 0x06, 0x12, 0x16, 0x21, 0x17, 0x2a, 0x1e, 0x15, 0x40, 0x27, 0x11, 0x0e,
+ 0x1e, 0x11, 0x15, 0x1f, 0x43, 0x13, 0x1a, 0x10, 0x15, 0x1b, 0x04, 0x09,
+ 0x4d, 0x2a, 0x0f, 0x19, 0x0a, 0x0a, 0x03, 0x05, 0x15, 0x3c, 0x64, 0x21,
+ 0x4b, 0x2e, 0x21, 0x28, 0x13, 0x47, 0x44, 0x19, 0x3f, 0x11, 0x18, 0x0b,
+ 0x0a, 0x07, 0x18, 0x0d, 0x07, 0x24, 0x2c, 0x2b, 0x21, 0x32, 0x10, 0x48,
+ 0x2a, 0x2d, 0x1e, 0x1a, 0x01, 0x0c, 0x43, 0x59, 0x28, 0x4e, 0x1c, 0x0d,
+ 0x5d, 0x24, 0x14, 0x0a, 0x05, 0x1f, 0x24, 0x32, 0x46, 0x3e, 0x5f, 0x3e,
+ 0x44, 0x1a, 0x30, 0x15, 0x0d, 0x07, 0x18, 0x2b, 0x03, 0x0d, 0x1a, 0x28,
+ 0x28, 0x57, 0xb2, 0x29, 0x27, 0x40, 0x2c, 0x23, 0x16, 0x63, 0x58, 0x1a,
+ 0x0a, 0x18, 0x11, 0x23, 0x08, 0x1b, 0x29, 0x05, 0x04, 0x0b, 0x15, 0x0d,
+ 0x14, 0x0b, 0x2a, 0x29, 0x5a, 0x62, 0x01, 0x19, 0x1e, 0x05, 0x05, 0x26,
+ 0x42, 0x42, 0x2a, 0x2a, 0x3f, 0x0d, 0x0f, 0x09, 0x05, 0x07, 0x01, 0x0b,
+ 0x25, 0x3e, 0x0d, 0x17, 0x11, 0x01, 0x03, 0x0d, 0x13, 0x20, 0x19, 0x11,
+ 0x03, 0x02, 0x01, 0x04, 0x11, 0x04, 0x05, 0x1b, 0x3d, 0x10, 0x29, 0x20,
+ 0x04, 0x04, 0x0a, 0x07, 0x04, 0x1f, 0x15, 0x20, 0x3e, 0x0f, 0x2a, 0x1e,
+ 0x00, 0x00, 0x00, 0x1b, 0x01, 0x4a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x07, 0x00, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x05, 0x00, 0x02, 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x06, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0d, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+ 0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+ 0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+ 0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+ 0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+ 0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+ 0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+ 0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+ 0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
+ 0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+ 0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+ 0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+ 0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+ 0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+ 0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+ 0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+ 0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+ 0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
+ 0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69,
+ 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x35,
+ 0x2c, 0x20, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x53, 0x70, 0x69,
+ 0x64, 0x65, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x52, 0x65, 0x67,
+ 0x75, 0x6c, 0x61, 0x72, 0x56, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x2e, 0x73, 0x69, 0x6c,
+ 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x4f, 0x46, 0x4c, 0x00, 0x43, 0x00, 0x6f,
+ 0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68,
+ 0x00, 0x74, 0x00, 0x20, 0x00, 0x28, 0x00, 0x63, 0x00, 0x29, 0x00, 0x20,
+ 0x00, 0x32, 0x00, 0x30, 0x00, 0x31, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x20,
+ 0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65,
+ 0x00, 0x2e, 0x00, 0x53, 0x00, 0x70, 0x00, 0x69, 0x00, 0x64, 0x00, 0x65,
+ 0x00, 0x72, 0x00, 0x53, 0x00, 0x79, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x6f,
+ 0x00, 0x6c, 0x00, 0x52, 0x00, 0x65, 0x00, 0x67, 0x00, 0x75, 0x00, 0x6c,
+ 0x00, 0x61, 0x00, 0x72, 0x00, 0x56, 0x00, 0x31, 0x00, 0x68, 0x00, 0x74,
+ 0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x73,
+ 0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70, 0x00, 0x74, 0x00, 0x73,
+ 0x00, 0x2e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x2e, 0x00, 0x6f,
+ 0x00, 0x72, 0x00, 0x67, 0x00, 0x2f, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x4c,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x66,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0xff, 0xff, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00
+};
+
+/**
+ * There does not appear to be a publicly accessible API for determining if lcd
+ * font smoothing will be applied if we request it. The main issue is that if
+ * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
+ */
+SkCTFontSmoothBehavior SkCTFontGetSmoothBehavior() {
+ static SkCTFontSmoothBehavior gSmoothBehavior = []{
+ uint32_t noSmoothBitmap[16][16] = {};
+ uint32_t smoothBitmap[16][16] = {};
+
+ SkUniqueCFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
+ SkUniqueCFRef<CGContextRef> noSmoothContext(
+ CGBitmapContextCreate(&noSmoothBitmap, 16, 16, 8, 16*4,
+ colorspace.get(), BITMAP_INFO_RGB));
+ SkUniqueCFRef<CGContextRef> smoothContext(
+ CGBitmapContextCreate(&smoothBitmap, 16, 16, 8, 16*4,
+ colorspace.get(), BITMAP_INFO_RGB));
+
+ SkUniqueCFRef<CFDataRef> data(CFDataCreateWithBytesNoCopy(
+ kCFAllocatorDefault, kSpiderSymbol_ttf, SK_ARRAY_COUNT(kSpiderSymbol_ttf),
+ kCFAllocatorNull));
+ SkUniqueCFRef<CTFontDescriptorRef> desc(
+ CTFontManagerCreateFontDescriptorFromData(data.get()));
+ SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc.get(), 16, nullptr));
+ SkASSERT(ctFont);
+
+ CGContextSetShouldSmoothFonts(noSmoothContext.get(), false);
+ CGContextSetShouldAntialias(noSmoothContext.get(), true);
+ CGContextSetTextDrawingMode(noSmoothContext.get(), kCGTextFill);
+ CGContextSetGrayFillColor(noSmoothContext.get(), 1, 1);
+
+ CGContextSetShouldSmoothFonts(smoothContext.get(), true);
+ CGContextSetShouldAntialias(smoothContext.get(), true);
+ CGContextSetTextDrawingMode(smoothContext.get(), kCGTextFill);
+ CGContextSetGrayFillColor(smoothContext.get(), 1, 1);
+
+ CGPoint point = CGPointMake(0, 3);
+ CGGlyph spiderGlyph = 3;
+ CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, noSmoothContext.get());
+ CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, smoothContext.get());
+
+ // For debugging.
+ //SkUniqueCFRef<CGImageRef> image(CGBitmapContextCreateImage(noSmoothContext()));
+ //SkUniqueCFRef<CGImageRef> image(CGBitmapContextCreateImage(smoothContext()));
+
+ SkCTFontSmoothBehavior smoothBehavior = SkCTFontSmoothBehavior::none;
+ for (int x = 0; x < 16; ++x) {
+ for (int y = 0; y < 16; ++y) {
+ uint32_t smoothPixel = smoothBitmap[x][y];
+ uint32_t r = (smoothPixel >> 16) & 0xFF;
+ uint32_t g = (smoothPixel >> 8) & 0xFF;
+ uint32_t b = (smoothPixel >> 0) & 0xFF;
+ if (r != g || r != b) {
+ return SkCTFontSmoothBehavior::subpixel;
+ }
+ if (noSmoothBitmap[x][y] != smoothPixel) {
+ smoothBehavior = SkCTFontSmoothBehavior::some;
+ }
+ }
+ }
+ return smoothBehavior;
+ }();
+ return gSmoothBehavior;
+}
+
+#endif
diff --git a/src/utils/mac/SkCTFontSmoothBehavior.h b/src/utils/mac/SkCTFontSmoothBehavior.h
new file mode 100644
index 0000000..126cdb3
--- /dev/null
+++ b/src/utils/mac/SkCTFontSmoothBehavior.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCTFontSmoothBehavior_DEFINED
+#define SkCTFontSmoothBehavior_DEFINED
+
+#include "include/core/SkTypes.h"
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+
+enum class SkCTFontSmoothBehavior {
+ none, // SmoothFonts produces no effect.
+ some, // SmoothFonts produces some effect, but not subpixel coverage.
+ subpixel, // SmoothFonts produces some effect and provides subpixel coverage.
+};
+
+SkCTFontSmoothBehavior SkCTFontGetSmoothBehavior();
+
+#endif
+#endif // SkCTFontSmoothBehavior_DEFINED