Merge branch 'quartz-core-text' into 'master'

quartz: support rendering colored bitmap fonts

See merge request cairo/cairo!289
This commit is contained in:
Adrian Johnson 2022-04-27 00:04:43 +00:00
commit 455a4cca54
16 changed files with 358 additions and 511 deletions

View file

@ -193,11 +193,8 @@ dnl ===========================================================================
CAIRO_ENABLE_SURFACE_BACKEND(quartz, Quartz, auto, [
dnl There is no pkgconfig for quartz; lets do a header check
AC_CHECK_HEADER(ApplicationServices/ApplicationServices.h, , [use_quartz="no (requires ApplicationServices framework)"])
if test "x$use_quartz" != "xyes" ; then
dnl check for CoreGraphics as a separate framework
AC_CHECK_HEADER(CoreGraphics/CoreGraphics.h, , [use_quartz="no (requires CoreGraphics framework)"])
quartz_LIBS="-Xlinker -framework -Xlinker CoreGraphics"
else
AC_CHECK_FUNC([CTFontDrawGlyphs],,[use_quartz_fonts="no (requires Mac OS X 10.7 or later)"])
if test "x$use_quartz" = "xyes" ; then
quartz_LIBS="-Xlinker -framework -Xlinker ApplicationServices"
fi
])

View file

@ -455,7 +455,6 @@ if host_machine.system() == 'darwin' and not get_option('quartz').disabled()
deps += [quartz_deps]
feature_conf.set('CAIRO_HAS_QUARTZ_SURFACE', 1)
feature_conf.set('CAIRO_HAS_QUARTZ_FONT', 1)
feature_conf.set('CAIRO_HAS_QUARTZ_IMAGE_SURFACE', 1)
built_features += [
@ -468,13 +467,18 @@ if host_machine.system() == 'darwin' and not get_option('quartz').disabled()
'name': 'cairo-quartz-image',
'description': 'Quartz Image surface backend',
'deps': quartz_deps,
},
{
'name': 'cairo-quartz-font',
'description': 'Quartz font backend',
'deps': quartz_deps,
},
]
}]
compiler = meson.get_compiler('c')
if compiler.has_function('CTFontDrawGlyphs', prefix: '#include <ApplicationServices/ApplicationServices.h>',
dependencies: quartz_deps)
built_features += [
{
'name': 'cairo-quartz-font',
'description': 'Quartz font backend',
'deps': quartz_deps,
}]
feature_conf.set('CAIRO_HAS_QUARTZ_FONT', 1)
endif
endif
endif

View file

@ -44,15 +44,13 @@
#include "cairo-error-private.h"
//#define DEBUG /* Uncomment this to get debug messages on the console. */
/**
* SECTION:cairo-quartz-fonts
* @Title: Quartz (CGFont) Fonts
* @Short_Description: Font support via CGFont on OS X
* @Short_Description: Font support via Core Text on Apple operating systems.
* @See_Also: #cairo_font_face_t
*
* The Quartz font backend is primarily used to render text on Apple
* MacOS X systems. The CGFont API is used for the internal
* implementation of the font backend methods.
**/
/**
@ -64,103 +62,37 @@
* Since: 1.6
**/
static CFDataRef (*CGFontCopyTableForTagPtr) (CGFontRef font, uint32_t tag) = NULL;
/* CreateWithFontName exists in 10.5, but not in 10.4; CreateWithName isn't public in 10.4 */
static CGFontRef (*CGFontCreateWithFontNamePtr) (CFStringRef) = NULL;
static CGFontRef (*CGFontCreateWithNamePtr) (const char *) = NULL;
/* These aren't public before 10.5, and some have different names in 10.4 */
static int (*CGFontGetUnitsPerEmPtr) (CGFontRef) = NULL;
static bool (*CGFontGetGlyphAdvancesPtr) (CGFontRef, const CGGlyph[], size_t, int[]) = NULL;
static bool (*CGFontGetGlyphBBoxesPtr) (CGFontRef, const CGGlyph[], size_t, CGRect[]) = NULL;
static CGRect (*CGFontGetFontBBoxPtr) (CGFontRef) = NULL;
/* Not public, but present */
static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const CGGlyph[], size_t) = NULL;
static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
/* These are private functions */
static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
/* Not public in the least bit */
static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = NULL;
/* CTFontCreateWithGraphicsFont is not available until 10.5 */
typedef const struct __CTFontDescriptor *CTFontDescriptorRef;
static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform*, CTFontDescriptorRef) = NULL;
static CGPathRef (*CTFontCreatePathForGlyphPtr) (CTFontRef, CGGlyph, CGAffineTransform *) = NULL;
/* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */
typedef struct {
int ascent;
int descent;
int leading;
} quartz_CGFontMetrics;
static quartz_CGFontMetrics* (*CGFontGetHMetricsPtr) (CGFontRef fontRef) = NULL;
static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL;
static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL;
static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL;
/* Not public anymore in 64-bits nor in 10.7 */
static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL;
static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
/* Cairo's transformations assume a unit-scaled font. */
static const CGFloat font_scale = 1.0;
/* Defined in 10.11 */
#define CGGLYPH_MAX ((CGGlyph) 0xFFFE) /* kCGFontIndexMax */
#define CGGLYPH_INVALID ((CGGlyph) 0xFFFF) /* kCGFontIndexInvalid */
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
#define FONT_ORIENTATION_HORIZONTAL kCTFontHorizontalOrientation
#define FONT_COLOR_GLYPHS kCTFontTraitColorGlyphs
#else
#define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal
#define FONT_COLOR_GLYPHS kCTFontColorGlyphsTrait
#endif
static void
quartz_font_ensure_symbols(void)
{
if (_cairo_quartz_font_symbol_lookup_done)
return;
CGFontCopyTableForTagPtr = dlsym(RTLD_DEFAULT, "CGFontCopyTableForTag");
/* Look for the 10.5 versions first */
CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBBoxes");
if (!CGFontGetGlyphBBoxesPtr)
CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBoundingBoxes");
CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnichars");
if (!CGFontGetGlyphsForUnicharsPtr)
CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes");
CGFontGetFontBBoxPtr = dlsym(RTLD_DEFAULT, "CGFontGetFontBBox");
/* We just need one of these two */
CGFontCreateWithFontNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithFontName");
CGFontCreateWithNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithName");
/* These have the same name in 10.4 and 10.5 */
CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm");
CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances");
CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
CTFontCreatePathForGlyphPtr = dlsym(RTLD_DEFAULT, "CTFontCreatePathForGlyph");
if (!CTFontCreateWithGraphicsFontPtr || !CTFontCreatePathForGlyphPtr)
CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath");
CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics");
CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent");
CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent");
CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading");
CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
CGContextGetAllowsFontSmoothingPtr =
dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
CGFontGetGlyphBBoxesPtr &&
CGFontGetGlyphsForUnicharsPtr &&
CGFontGetUnitsPerEmPtr &&
CGFontGetGlyphAdvancesPtr &&
((CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) || CGFontGetGlyphPathPtr) &&
(CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr)))
_cairo_quartz_font_symbols_present = TRUE;
_cairo_quartz_font_symbol_lookup_done = TRUE;
}
@ -169,6 +101,7 @@ typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t;
struct _cairo_quartz_scaled_font {
cairo_scaled_font_t base;
CTFontRef ctFont;
};
struct _cairo_quartz_font_face {
@ -187,14 +120,10 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
{
const char *family;
char *full_name;
CFStringRef cgFontName = NULL;
CFStringRef FontName = NULL;
CGFontRef cgFont = NULL;
int loop;
quartz_font_ensure_symbols();
if (! _cairo_quartz_font_symbols_present)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
family = toy_face->family;
full_name = _cairo_malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc.
/* handle CSS-ish faces */
@ -221,23 +150,19 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
if (loop < 3 && (loop & 1) == 0) {
if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD)
strcat (full_name, " Bold");
strcat (full_name, "-Bold");
}
if (loop < 3 && (loop & 2) == 0) {
if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC)
strcat (full_name, " Italic");
strcat (full_name, "-Italic");
else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE)
strcat (full_name, " Oblique");
strcat (full_name, "-Oblique");
}
if (CGFontCreateWithFontNamePtr) {
cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
cgFont = CGFontCreateWithFontNamePtr (cgFontName);
CFRelease (cgFontName);
} else {
cgFont = CGFontCreateWithNamePtr (full_name);
}
FontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
cgFont = CGFontCreateWithFontName (FontName);
CFRelease (FontName);
if (cgFont)
break;
@ -249,7 +174,7 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
}
*font_face = cairo_quartz_font_face_create_for_cgfont (cgFont);
CGFontRelease (cgFont);
CFRelease (cgFont);
return CAIRO_STATUS_SUCCESS;
}
@ -265,6 +190,51 @@ _cairo_quartz_font_face_destroy (void *abstract_face)
static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend;
#ifdef DEBUG
static void
_cairo_quartz_debug_font_characteristics (cairo_quartz_scaled_font_t *font)
{
CGRect ct_bbox = CTFontGetBoundingBox (font->ctFont);
CGFloat ct_ascent = CTFontGetAscent (font->ctFont);
CGFloat ct_descent = CTFontGetDescent (font->ctFont);
CGFloat ct_leading = CTFontGetLeading (font->ctFont);
CGFloat ct_capheight = CTFontGetCapHeight (font->ctFont);
CGFloat ct_xheight = CTFontGetXHeight (font->ctFont);
char chars[] = "ymMW";
CGGlyph glyphs[4];
UniChar *utf16 = NULL;
CGSize ct_advances[4];
CGRect ct_gbbox[4], ct_gobox[4], ct_rbbox, ct_robox;
double ct_radvance;
int converted;
cairo_status_t rv;
rv = _cairo_utf8_to_utf16 (chars, 4, &utf16, &converted);
if (rv) return;
CTFontGetGlyphsForCharacters (font->ctFont, utf16, glyphs, 4);
free (utf16);
ct_rbbox = CTFontGetBoundingRectsForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, glyphs, ct_gbbox, 4);
ct_robox = CTFontGetOpticalBoundsForGlyphs (font->ctFont, glyphs, ct_gobox, 4, 0);
ct_radvance = CTFontGetAdvancesForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, glyphs, ct_advances, 4);
fprintf (stderr, "\nCTFont Bounding Box: %f %f %f %f\nAscent %f Descent %f Leading %f Cap Height %f X-Height %f\n",
ct_bbox.origin.x, ct_bbox.origin.y, ct_bbox.size.width, ct_bbox.size.height, ct_ascent, ct_descent,
ct_leading, ct_capheight, ct_xheight);
fprintf (stderr, "CTFont string\n\t bounding box %f %f %f %f advance %f\n\toptical box %f %f %f %f\n\n",
ct_rbbox.origin.x, ct_rbbox.origin.y, ct_rbbox.size.width, ct_rbbox.size.height, ct_radvance,
ct_robox.origin.x, ct_robox.origin.y, ct_robox.size.width, ct_robox.size.height);
for (int i = 0; i < 4; ++i)
{
fprintf (stderr, "Character %c\n", chars[i]);
fprintf (stderr, "\tbox %f %f %f %f\n\toptical %f %f %f %f advance %f %f\n",
ct_gbbox[i].origin.x, ct_gbbox[i].origin.y, ct_gbbox[i].size.width, ct_gbbox[i].size.height,
ct_advances[i].width, ct_advances[i].height,
ct_gobox[i].origin.x, ct_gobox[i].origin.y, ct_gobox[i].size.width, ct_gobox[i].size.height);
}
fprintf (stderr, "\n");
}
#endif
static cairo_status_t
_cairo_quartz_font_face_scaled_font_create (void *abstract_face,
const cairo_matrix_t *font_matrix,
@ -276,13 +246,9 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face,
cairo_quartz_scaled_font_t *font = NULL;
cairo_status_t status;
cairo_font_extents_t fs_metrics;
double ems;
CTFontRef ctFont;
CGRect bbox;
quartz_font_ensure_symbols();
if (!_cairo_quartz_font_symbols_present)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
font = _cairo_malloc (sizeof(cairo_quartz_scaled_font_t));
if (font == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
@ -295,48 +261,29 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face,
if (status)
goto FINISH;
ems = CGFontGetUnitsPerEmPtr (font_face->cgFont);
ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, font_scale, NULL, NULL);
/* initialize metrics */
if (CGFontGetFontBBoxPtr && CGFontGetAscentPtr) {
fs_metrics.ascent = (CGFontGetAscentPtr (font_face->cgFont) / ems);
fs_metrics.descent = - (CGFontGetDescentPtr (font_face->cgFont) / ems);
fs_metrics.height = fs_metrics.ascent + fs_metrics.descent +
(CGFontGetLeadingPtr (font_face->cgFont) / ems);
bbox = CGFontGetFontBBoxPtr (font_face->cgFont);
fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems;
fs_metrics.max_y_advance = 0.0;
} else {
CGGlyph wGlyph;
UniChar u;
quartz_CGFontMetrics *m;
m = CGFontGetHMetricsPtr (font_face->cgFont);
/* On OX 10.4, GetHMetricsPtr sometimes returns NULL for unknown reasons */
if (!m) {
status = _cairo_error(CAIRO_STATUS_NULL_POINTER);
goto FINISH;
}
fs_metrics.ascent = (m->ascent / ems);
fs_metrics.descent = - (m->descent / ems);
fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + (m->leading / ems);
/* We kind of have to guess here; W's big, right? */
u = (UniChar) 'W';
CGFontGetGlyphsForUnicharsPtr (font_face->cgFont, &u, &wGlyph, 1);
if (wGlyph && CGFontGetGlyphBBoxesPtr (font_face->cgFont, &wGlyph, 1, &bbox)) {
fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems;
fs_metrics.max_y_advance = 0.0;
} else {
fs_metrics.max_x_advance = 0.0;
fs_metrics.max_y_advance = 0.0;
}
}
fs_metrics.ascent = CTFontGetAscent (ctFont);
fs_metrics.descent = CTFontGetDescent (ctFont);
fs_metrics.height = fs_metrics.ascent + fs_metrics.descent +
CTFontGetLeading (ctFont);
bbox = CTFontGetBoundingBox (ctFont);
fs_metrics.max_x_advance = CGRectGetMaxX(bbox);
fs_metrics.max_y_advance = 0.0;
font->ctFont = CFRetain (ctFont);
status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics);
#ifdef DEBUG
{
CFStringRef fontFullName = CTFontCopyFullName (ctFont);
const char* font_full_name = CFStringGetCStringPtr(fontFullName, kCFStringEncodingUTF8);
fprintf (stderr, "Create scaled font %s with scale %f ascent %f, descent %f, height %f, x-advance %f\n",
font_full_name, fs_metrics.ascent, fs_metrics.descent, fs_metrics.height,
fs_metrics.max_x_advance);
_cairo_quartz_debug_font_characteristics (font);
}
#endif
FINISH:
if (status != CAIRO_STATUS_SUCCESS) {
@ -355,6 +302,23 @@ const cairo_font_face_backend_t _cairo_quartz_font_face_backend = {
_cairo_quartz_font_face_scaled_font_create
};
static inline cairo_quartz_font_face_t*
_cairo_quartz_font_face_create ()
{
cairo_quartz_font_face_t *font_face =
_cairo_malloc (sizeof (cairo_quartz_font_face_t));
if (!font_face) {
cairo_status_t ignore_status;
ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_quartz_font_face_t *)&_cairo_font_face_nil;
}
_cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
return font_face;
}
/**
* cairo_quartz_font_face_create_for_cgfont:
* @font: a #CGFontRef obtained through a method external to cairo.
@ -371,21 +335,13 @@ const cairo_font_face_backend_t _cairo_quartz_font_face_backend = {
cairo_font_face_t *
cairo_quartz_font_face_create_for_cgfont (CGFontRef font)
{
cairo_quartz_font_face_t *font_face;
cairo_quartz_font_face_t* font_face = _cairo_quartz_font_face_create ();
quartz_font_ensure_symbols();
font_face = _cairo_malloc (sizeof (cairo_quartz_font_face_t));
if (!font_face) {
cairo_status_t ignore_status;
ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_font_face_t *)&_cairo_font_face_nil;
}
if (cairo_font_face_status (&font_face->base))
return &font_face->base;
font_face->cgFont = CGFontRetain (font);
_cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
return &font_face->base;
}
@ -405,6 +361,8 @@ _cairo_quartz_scaled_to_face (void *abstract_font)
static void
_cairo_quartz_scaled_font_fini(void *abstract_font)
{
cairo_quartz_scaled_font_t* font = (cairo_quartz_scaled_font_t*)abstract_font;
CFRelease (font->ctFont);
}
static inline CGGlyph
@ -419,21 +377,17 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font,
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0};
CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
int advance;
CGSize advance;
CGRect bbox;
double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
double xmin, ymin, xmax, ymax;
if (unlikely (glyph == CGGLYPH_INVALID))
goto FAIL;
if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
!CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
goto FAIL;
CTFontGetAdvancesForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, &glyph, &advance, 1);
CTFontGetBoundingRectsForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, &glyph, &bbox, 1);
/* broken fonts like Al Bayan return incorrect bounds for some null characters,
see https://bugzilla.mozilla.org/show_bug.cgi?id=534260 */
if (unlikely (bbox.origin.x == -32767 &&
@ -444,31 +398,9 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font,
bbox.size.width = bbox.size.height = 0;
}
bbox = CGRectMake (bbox.origin.x / emscale,
bbox.origin.y / emscale,
bbox.size.width / emscale,
bbox.size.height / emscale);
/* Should we want to always integer-align glyph extents, we can do so in this way */
#if 0
{
CGAffineTransform textMatrix;
textMatrix = CGAffineTransformMake (font->base.scale.xx,
-font->base.scale.yx,
-font->base.scale.xy,
font->base.scale.yy,
0.0f, 0.0f);
bbox = CGRectApplyAffineTransform (bbox, textMatrix);
bbox = CGRectIntegral (bbox);
bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix));
}
#endif
#if 0
fprintf (stderr, "[0x%04x] bbox: %f %f %f %f\n", glyph,
bbox.origin.x / emscale, bbox.origin.y / emscale,
bbox.size.width / emscale, bbox.size.height / emscale);
#ifdef DEBUG
fprintf (stderr, "[0x%04x] bbox: x %f y %f width %f height %f\n", glyph,
bbox.origin.x, bbox.origin.y, bbox.size.width, bbox.size.height);
#endif
xmin = CGRectGetMinX(bbox);
@ -480,13 +412,17 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font,
extents.y_bearing = - ymax;
extents.width = xmax - xmin;
extents.height = ymax - ymin;
/* At the necessary 1.0pt ctFont size some glyphs get a reduced
* advance that causes overlaps when scaled up. We can avoid that by
* using the width instead if it's wider. Since cairo doesn't support
* vertical font layout we don't do the same for y_advance.
*/
extents.x_advance = MAX(extents.width, advance.width);
extents.y_advance = advance.height;
extents.x_advance = (double) advance / emscale;
extents.y_advance = 0.0;
#if 0
fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph,
extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance);
#ifdef DEBUG
fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f %f\n\n", glyph,
extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance, extents.y_advance);
#endif
FAIL:
@ -542,7 +478,7 @@ _cairo_quartz_path_apply_func (void *info, const CGPathElement *el)
_cairo_fixed_from_double(el->points[1].y),
_cairo_fixed_from_double(el->points[2].x),
_cairo_fixed_from_double(el->points[2].y));
assert(!status);
assert(!status);
break;
case kCGPathElementCloseSubpath:
status = _cairo_path_fixed_close_path (path);
@ -555,7 +491,6 @@ static cairo_int_status_t
_cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font,
cairo_scaled_glyph_t *scaled_glyph)
{
cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
CGAffineTransform textMatrix;
CGPathRef glyphPath;
@ -573,13 +508,7 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font,
-font->base.scale.yy,
0, 0);
if (CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) {
CTFontRef ctFont = CTFontCreateWithGraphicsFontPtr (font_face->cgFont, 1.0, NULL, NULL);
glyphPath = CTFontCreatePathForGlyphPtr (ctFont, glyph, &textMatrix);
CFRelease (ctFont);
} else {
glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph);
}
glyphPath = CTFontCreatePathForGlyph (font->ctFont, glyph, &textMatrix);
if (!glyphPath)
return CAIRO_INT_STATUS_UNSUPPORTED;
@ -599,30 +528,42 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font,
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
_cairo_quartz_font_has_color_glyphs (void *abstract_font)
{
cairo_quartz_scaled_font_t *face = (cairo_quartz_scaled_font_t*)abstract_font;
CTFontSymbolicTraits traits = CTFontGetSymbolicTraits (face->ctFont);
return traits & FONT_COLOR_GLYPHS;
}
static cairo_int_status_t
_cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
cairo_scaled_glyph_t *scaled_glyph)
cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_glyph_info_t info,
const cairo_color_t *fg_color)
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
cairo_image_surface_t *surface = NULL;
CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
int advance;
CGRect bbox;
cairo_text_extents_t metrics = scaled_glyph->fs_metrics;
CGRect bbox = CGRectMake (metrics.x_bearing, -(metrics.y_bearing + metrics.height),
metrics.width, metrics.height);
double width, height;
double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
CGContextRef cgContext = NULL;
CGAffineTransform textMatrix;
CGRect glyphRect, glyphRectInt;
CGPoint glyphOrigin;
cairo_bool_t is_color = info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE;
cairo_format_t format = is_color ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_A8;
//fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface);
#ifdef DEBUG
fprintf (stderr, "[0x%04x] bearing: %f %f width %f height %f advances %f %f\n",
glyph, metrics.x_bearing, metrics.y_bearing, metrics.width, metrics.height,
metrics.x_advance, metrics.y_advance);
fprintf (stderr, "[0x%04x] bounds: origin %f %f, size %f %f\n", glyph, bbox.origin.x,
bbox.origin.y, bbox.size.width, bbox.size.height);
#endif
/* Create blank 2x2 image if we don't have this character.
* Maybe we should draw a better missing-glyph slug or something,
* but this is ok for now.
@ -639,31 +580,33 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
return CAIRO_STATUS_SUCCESS;
}
if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
!CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
{
/* Note: Certain opentype color fonts have the ability to provide a
* mixture of color and not-color glyphs. The Core Text API doesn't
* expose a way to query individual glyphs and at the level that that
* API is written it's not supposed to matter. The following code will
* cheerfully render any glyph requested onto the image surface. If
* the font is capable of color and
* COLOR_SCALED_GLYPH_INFO_COLOR_SURFACE is set then you get back a
* CAIRO_FORMAT_ARGB32 surface. If a foreground color is provided then
* the glyph will be drawn in that color, otherwise it will be black.
*/
if (unlikely (is_color && ! _cairo_quartz_font_has_color_glyphs (font)))
return CAIRO_INT_STATUS_UNSUPPORTED;
}
/* scale(1,-1) * font->base.scale * scale(1,-1) */
textMatrix = CGAffineTransformMake (font->base.scale.xx,
-font->base.scale.yx,
-font->base.scale.xy,
font->base.scale.yy,
0, -0);
glyphRect = CGRectMake (bbox.origin.x / emscale,
bbox.origin.y / emscale,
bbox.size.width / emscale,
bbox.size.height / emscale);
glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix);
0, 0);
glyphRect = CGRectApplyAffineTransform (bbox, textMatrix);
/* Round the rectangle outwards, so that we don't have to deal
* with non-integer-pixel origins or dimensions.
*/
glyphRectInt = CGRectIntegral (glyphRect);
#if 0
#ifdef DEBUG
fprintf (stderr, "glyphRect[o]: %f %f %f %f\n",
glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
fprintf (stderr, "glyphRectInt: %f %f %f %f\n",
@ -672,70 +615,50 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
glyphOrigin = glyphRectInt.origin;
//textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm));
width = glyphRectInt.size.width;
height = glyphRectInt.size.height;
//fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
surface = (cairo_image_surface_t*) cairo_image_surface_create (format, width, height);
if (surface->base.status)
return surface->base.status;
if (surface->width != 0 && surface->height != 0) {
cgContext = CGBitmapContextCreate (surface->data,
surface->width,
surface->height,
8,
surface->stride,
NULL,
kCGImageAlphaOnly);
CGColorSpaceRef colorspace = is_color ? CGColorSpaceCreateDeviceRGB () : NULL;
CGBitmapInfo bitinfo = is_color ? kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst : kCGImageAlphaOnly;
CGContextRef cgContext = CGBitmapContextCreate (surface->data,
surface->width,
surface->height,
8,
surface->stride,
colorspace,
bitinfo);
if (cgContext == NULL) {
cairo_surface_destroy (&surface->base);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
CGContextSetFont (cgContext, font_face->cgFont);
CGContextSetFontSize (cgContext, 1.0);
CGContextSetTextMatrix (cgContext, textMatrix);
switch (font->base.options.antialias) {
case CAIRO_ANTIALIAS_SUBPIXEL:
case CAIRO_ANTIALIAS_BEST:
CGContextSetShouldAntialias (cgContext, TRUE);
CGContextSetShouldSmoothFonts (cgContext, TRUE);
if (CGContextSetAllowsFontSmoothingPtr &&
!CGContextGetAllowsFontSmoothingPtr (cgContext))
CGContextSetAllowsFontSmoothingPtr (cgContext, TRUE);
break;
case CAIRO_ANTIALIAS_NONE:
CGContextSetShouldAntialias (cgContext, FALSE);
break;
case CAIRO_ANTIALIAS_GRAY:
case CAIRO_ANTIALIAS_GOOD:
case CAIRO_ANTIALIAS_FAST:
CGContextSetShouldAntialias (cgContext, TRUE);
CGContextSetShouldSmoothFonts (cgContext, FALSE);
break;
case CAIRO_ANTIALIAS_DEFAULT:
default:
/* Don't do anything */
break;
}
if (fg_color)
CGContextSetRGBFillColor (cgContext, fg_color->red, fg_color->green, fg_color->blue, fg_color->alpha);
_cairo_quartz_set_antialiasing (cgContext, font->base.options.antialias);
CGContextSetAlpha (cgContext, 1.0);
CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1);
CGContextTranslateCTM (cgContext, -glyphOrigin.x, -glyphOrigin.y);
CGContextConcatCTM (cgContext, textMatrix);
CTFontDrawGlyphs (font->ctFont, &glyph, &CGPointZero, 1, cgContext);
CGContextRelease (cgContext);
CGColorSpaceRelease (colorspace);
}
cairo_surface_set_device_offset (&surface->base,
- glyphOrigin.x,
height + glyphOrigin.y);
cairo_surface_mark_dirty (&surface->base);
_cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);
if (is_color)
_cairo_scaled_glyph_set_color_surface (scaled_glyph, &font->base, surface, fg_color != NULL);
else
_cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);
return status;
}
@ -755,8 +678,10 @@ _cairo_quartz_scaled_glyph_init (void *abstract_font,
if (!status && (info & CAIRO_SCALED_GLYPH_INFO_PATH))
status = _cairo_quartz_init_glyph_path (font, scaled_glyph);
if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE))
status = _cairo_quartz_init_glyph_surface (font, scaled_glyph);
if (!status && (info & (CAIRO_SCALED_GLYPH_INFO_SURFACE |
CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE )))
status = _cairo_quartz_init_glyph_surface (font, scaled_glyph,
info, foreground_color);
return status;
}
@ -766,13 +691,11 @@ _cairo_quartz_ucs4_to_index (void *abstract_font,
uint32_t ucs4)
{
cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font;
cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font);
CGGlyph glyph[2];
UniChar utf16[2];
int len = _cairo_ucs4_to_utf16 (ucs4, utf16);
CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, utf16, glyph, len);
CTFontGetGlyphsForCharacters (font->ctFont, utf16, glyph, len);
return glyph[0];
}
@ -783,11 +706,8 @@ _cairo_quartz_load_truetype_table (void *abstract_font,
unsigned char *buffer,
unsigned long *length)
{
cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face (abstract_font);
CFDataRef data = NULL;
if (likely (CGFontCopyTableForTagPtr))
data = CGFontCopyTableForTagPtr (font_face->cgFont, tag);
cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font;
CFDataRef data = CTFontCopyTable (font->ctFont, tag, kCTFontTableOptionNoOptions);
if (!data)
return CAIRO_INT_STATUS_UNSUPPORTED;
@ -816,9 +736,12 @@ static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = {
NULL, /* text_to_glyphs */
_cairo_quartz_ucs4_to_index,
_cairo_quartz_load_truetype_table,
NULL, /* map_glyphs_to_unicode */
NULL, /*index_to_ucs4*/
NULL, /* is_synthetic */
NULL, /* index_to_glyph_name */
NULL, /* load_type1_data */
_cairo_quartz_font_has_color_glyphs
};
/*
* private methods that the quartz surface uses
*/
@ -831,6 +754,43 @@ _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font)
return ffont->cgFont;
}
CTFontRef
_cairo_quartz_scaled_font_get_ct_font (cairo_scaled_font_t *abstract_font)
{
cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font;
return font->ctFont;
}
void
_cairo_quartz_set_antialiasing (CGContextRef cgContext, cairo_antialias_t antialias)
{
switch (antialias) {
case CAIRO_ANTIALIAS_SUBPIXEL:
case CAIRO_ANTIALIAS_BEST:
CGContextSetShouldAntialias (cgContext, TRUE);
CGContextSetShouldSmoothFonts (cgContext, TRUE);
quartz_font_ensure_symbols ();
if (CGContextGetAllowsFontSmoothingPtr &&
!CGContextGetAllowsFontSmoothingPtr (cgContext))
CGContextSetAllowsFontSmoothing (cgContext, TRUE);
break;
case CAIRO_ANTIALIAS_NONE:
CGContextSetShouldAntialias (cgContext, FALSE);
break;
case CAIRO_ANTIALIAS_GRAY:
case CAIRO_ANTIALIAS_GOOD:
case CAIRO_ANTIALIAS_FAST:
CGContextSetShouldAntialias (cgContext, TRUE);
CGContextSetShouldSmoothFonts (cgContext, FALSE);
break;
case CAIRO_ANTIALIAS_DEFAULT:
default:
/* Don't do anything */
break;
}
}
/*
* compat with old ATSUI backend
*/

View file

@ -105,6 +105,12 @@ CairoQuartzCreateCGImage (cairo_format_t format,
cairo_private CGFontRef
_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
cairo_private CTFontRef
_cairo_quartz_scaled_font_get_ct_font (cairo_scaled_font_t *sfont);
cairo_private cairo_font_face_t*
_cairo_quartz_font_face_create_for_ctfont (CTFontRef ctFont);
cairo_private void
_cairo_quartz_set_antialiasing (CGContextRef context, cairo_antialias_t antialias);
#else

View file

@ -65,6 +65,12 @@
#endif
#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
#define FONT_ORIENTATION_HORIZONTAL kCTFontHorizontalOrientation
#else
#define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal
#endif
/**
* SECTION:cairo-quartz
@ -85,46 +91,22 @@
* Since: 1.6
**/
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
/* This method is private, but it exists. Its params are are exposed
* as args to the NS* method, but not as CG.
*/
enum PrivateCGCompositeMode {
kPrivateCGCompositeClear = 0,
kPrivateCGCompositeCopy = 1,
kPrivateCGCompositeSourceOver = 2,
kPrivateCGCompositeSourceIn = 3,
kPrivateCGCompositeSourceOut = 4,
kPrivateCGCompositeSourceAtop = 5,
kPrivateCGCompositeDestinationOver = 6,
kPrivateCGCompositeDestinationIn = 7,
kPrivateCGCompositeDestinationOut = 8,
kPrivateCGCompositeDestinationAtop = 9,
kPrivateCGCompositeXOR = 10,
kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s)))
kPrivateCGCompositePlusLighter = 12, // (min (1, s + d))
};
typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
#endif
/* Some of these are present in earlier versions of the OS than where
* they are public; other are not public at all
*/
/* public since 10.6 */
static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
/* not yet public */
static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
/*
* Utility functions
* macOS Private functions
*/
static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
static void
quartz_ensure_symbols()
{
static cairo_bool_t symbol_lookup_done = FALSE;
if (!symbol_lookup_done) {
CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
CGContextGetAllowsFontSmoothingPtr =
dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
symbol_lookup_done = TRUE;
}
}
#ifdef QUARTZ_DEBUG
static void quartz_surface_to_png (cairo_quartz_surface_t *nq, const char *dest);
@ -152,20 +134,6 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
unsigned int width,
unsigned int height);
/* Load all extra symbols */
static void quartz_ensure_symbols (void)
{
if (likely (_cairo_quartz_symbol_lookup_done))
return;
CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
_cairo_quartz_symbol_lookup_done = TRUE;
}
CGImageRef
CairoQuartzCreateCGImage (cairo_format_t format,
unsigned int width,
@ -270,6 +238,7 @@ _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc)
if (unlikely (cgc == NULL))
return FALSE;
quartz_ensure_symbols ();
if (likely (CGContextGetTypePtr)) {
/* 4 is the type value of a bitmap context */
return CGContextGetTypePtr (cgc) == 4;
@ -377,58 +346,6 @@ _cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path,
* Misc helpers/callbacks
*/
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
static PrivateCGCompositeMode
_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
{
switch (op) {
case CAIRO_OPERATOR_CLEAR:
return kPrivateCGCompositeClear;
case CAIRO_OPERATOR_SOURCE:
return kPrivateCGCompositeCopy;
case CAIRO_OPERATOR_OVER:
return kPrivateCGCompositeSourceOver;
case CAIRO_OPERATOR_IN:
return kPrivateCGCompositeSourceIn;
case CAIRO_OPERATOR_OUT:
return kPrivateCGCompositeSourceOut;
case CAIRO_OPERATOR_ATOP:
return kPrivateCGCompositeSourceAtop;
case CAIRO_OPERATOR_DEST_OVER:
return kPrivateCGCompositeDestinationOver;
case CAIRO_OPERATOR_DEST_IN:
return kPrivateCGCompositeDestinationIn;
case CAIRO_OPERATOR_DEST_OUT:
return kPrivateCGCompositeDestinationOut;
case CAIRO_OPERATOR_DEST_ATOP:
return kPrivateCGCompositeDestinationAtop;
case CAIRO_OPERATOR_XOR:
return kPrivateCGCompositeXOR;
case CAIRO_OPERATOR_ADD:
return kPrivateCGCompositePlusLighter;
case CAIRO_OPERATOR_DEST:
case CAIRO_OPERATOR_SATURATE:
case CAIRO_OPERATOR_MULTIPLY:
case CAIRO_OPERATOR_SCREEN:
case CAIRO_OPERATOR_OVERLAY:
case CAIRO_OPERATOR_DARKEN:
case CAIRO_OPERATOR_LIGHTEN:
case CAIRO_OPERATOR_COLOR_DODGE:
case CAIRO_OPERATOR_COLOR_BURN:
case CAIRO_OPERATOR_HARD_LIGHT:
case CAIRO_OPERATOR_SOFT_LIGHT:
case CAIRO_OPERATOR_DIFFERENCE:
case CAIRO_OPERATOR_EXCLUSION:
case CAIRO_OPERATOR_HSL_HUE:
case CAIRO_OPERATOR_HSL_SATURATION:
case CAIRO_OPERATOR_HSL_COLOR:
case CAIRO_OPERATOR_HSL_LUMINOSITY:
default:
ASSERT_NOT_REACHED;
}
}
#endif
static CGBlendMode
_cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
@ -465,7 +382,6 @@ _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
case CAIRO_OPERATOR_HSL_LUMINOSITY:
return kCGBlendModeLuminosity;
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
case CAIRO_OPERATOR_CLEAR:
return kCGBlendModeClear;
case CAIRO_OPERATOR_SOURCE:
@ -490,21 +406,6 @@ _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
return kCGBlendModeXOR;
case CAIRO_OPERATOR_ADD:
return kCGBlendModePlusLighter;
#else
case CAIRO_OPERATOR_CLEAR:
case CAIRO_OPERATOR_SOURCE:
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_IN:
case CAIRO_OPERATOR_OUT:
case CAIRO_OPERATOR_ATOP:
case CAIRO_OPERATOR_DEST_OVER:
case CAIRO_OPERATOR_DEST_IN:
case CAIRO_OPERATOR_DEST_OUT:
case CAIRO_OPERATOR_DEST_ATOP:
case CAIRO_OPERATOR_XOR:
case CAIRO_OPERATOR_ADD:
#endif
case CAIRO_OPERATOR_DEST:
case CAIRO_OPERATOR_SATURATE:
default:
@ -534,16 +435,6 @@ _cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op)
return CAIRO_INT_STATUS_UNSUPPORTED;
}
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
if (op <= CAIRO_OPERATOR_ADD) {
PrivateCGCompositeMode compmode;
compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op);
CGContextSetCompositeOperation (context, compmode);
return CAIRO_STATUS_SUCCESS;
}
#endif
blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op);
CGContextSetBlendMode (context, blendmode);
return CAIRO_STATUS_SUCCESS;
@ -1472,7 +1363,7 @@ _cairo_quartz_surface_map_to_image (void *abstract_surface,
cairo_surface_t *return_surface = NULL;
unsigned int stride, bitinfo, bpp, color_comps;
CGColorSpaceRef colorspace;
void *imageData;
unsigned char *imageData;
cairo_format_t format;
if (IS_EMPTY (surface))
@ -1558,13 +1449,6 @@ _cairo_quartz_surface_finish (void *abstract_surface)
surface->cgContext = NULL;
#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600
if (surface->imageData) {
free (surface->imageData);
surface->imageData = NULL;
}
#endif
if (surface->cgLayer)
{
CGLayerRelease (surface->cgLayer);
@ -2016,17 +1900,15 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
cairo_bool_t overlap)
{
CGAffineTransform textTransform, invTextTransform;
CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGGlyph)];
CGPoint cg_positions_static[CAIRO_STACK_ARRAY_LENGTH (CGPoint)];
CGGlyph *cg_glyphs = &glyphs_static[0];
CGSize *cg_advances = &cg_advances_static[0];
COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize));
CGPoint *cg_positions = &cg_positions_static[0];
cairo_quartz_drawing_state_t state;
cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
cairo_quartz_float_t xprev, yprev;
int i;
CGFontRef cgfref = NULL;
CGPoint origin;
CTFontRef ctFont = NULL;
cairo_bool_t didForceFontSmoothing = FALSE;
@ -2045,44 +1927,17 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
}
/* this doesn't addref */
cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
CGContextSetFont (state.cgMaskContext, cgfref);
CGContextSetFontSize (state.cgMaskContext, 1.0);
switch (scaled_font->options.antialias) {
case CAIRO_ANTIALIAS_SUBPIXEL:
case CAIRO_ANTIALIAS_BEST:
CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE);
if (CGContextSetAllowsFontSmoothingPtr &&
!CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext))
{
didForceFontSmoothing = TRUE;
CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE);
}
break;
case CAIRO_ANTIALIAS_NONE:
CGContextSetShouldAntialias (state.cgMaskContext, FALSE);
break;
case CAIRO_ANTIALIAS_GRAY:
case CAIRO_ANTIALIAS_GOOD:
case CAIRO_ANTIALIAS_FAST:
CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE);
break;
case CAIRO_ANTIALIAS_DEFAULT:
/* Don't do anything */
break;
}
ctFont = _cairo_quartz_scaled_font_get_ct_font (scaled_font);
_cairo_quartz_set_antialiasing (state.cgMaskContext, scaled_font->options.antialias);
if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize));
cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGPoint));
if (unlikely (cg_glyphs == NULL)) {
rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
cg_advances = (CGSize*) (cg_glyphs + num_glyphs);
cg_positions = (CGPoint*) (cg_glyphs + num_glyphs);
}
/* scale(1,-1) * scaled_font->scale */
@ -2099,43 +1954,30 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
-scaled_font->scale_inverse.yy,
0.0, 0.0);
CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0);
CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
/* Convert our glyph positions to glyph advances. We need n-1 advances,
* since the advance at index 0 is applied after glyph 0. */
xprev = glyphs[0].x;
yprev = glyphs[0].y;
cg_glyphs[0] = glyphs[0].index;
for (i = 1; i < num_glyphs; i++) {
cairo_quartz_float_t xf = glyphs[i].x;
cairo_quartz_float_t yf = glyphs[i].y;
origin = CGPointMake (glyphs[0].x, glyphs[0].y);
for (int i = 0; i < num_glyphs; ++i)
{
cg_glyphs[i] = glyphs[i].index;
cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
xprev = xf;
yprev = yf;
cg_positions[i] = CGPointMake (glyphs[i].x - origin.x, glyphs[i].y - origin.y);
cg_positions[i] = CGPointApplyAffineTransform (cg_positions[i], invTextTransform);
}
/* Translate to the first glyph's position before drawing */
CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
CGContextTranslateCTM (state.cgMaskContext, origin.x, origin.y);
CGContextConcatCTM (state.cgMaskContext, textTransform);
CGContextShowGlyphsWithAdvances (state.cgMaskContext,
cg_glyphs,
cg_advances,
num_glyphs);
CTFontDrawGlyphs (ctFont, cg_glyphs, cg_positions, num_glyphs, state.cgMaskContext);
CGContextConcatCTM (state.cgMaskContext, invTextTransform);
CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
CGContextTranslateCTM (state.cgMaskContext, -origin.x, -origin.y);
if (state.action != DO_DIRECT)
_cairo_quartz_draw_source (&state, extents->op);
BAIL:
if (didForceFontSmoothing)
CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
CGContextSetAllowsFontSmoothing (state.cgMaskContext, FALSE);
_cairo_quartz_teardown_state (&state, extents);
@ -2319,8 +2161,6 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
{
cairo_quartz_surface_t *surface;
quartz_ensure_symbols ();
/* Init the base surface */
surface = _cairo_malloc (sizeof (cairo_quartz_surface_t));
if (unlikely (surface == NULL))
@ -2342,9 +2182,6 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
surface->extents.width = width;
surface->extents.height = height;
surface->virtual_extents = surface->extents;
#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600
surface->imageData = NULL;
#endif
if (IS_EMPTY (surface)) {
surface->cgContext = NULL;
@ -2473,16 +2310,6 @@ cairo_quartz_surface_create (cairo_format_t format,
* so we don't have to anything special on allocation.
*/
stride = (stride + 15) & ~15;
#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600
imageData = _cairo_malloc_ab (height, stride);
if (unlikely (!imageData)) {
CGColorSpaceRelease (cgColorspace);
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
/* zero the memory to match the image surface behavior */
memset (imageData, 0, height * stride);
#endif /* For newer macOS versions let Core Graphics manage the buffer. */
cgc = CGBitmapContextCreate (imageData,
width,
height,
@ -2515,9 +2342,6 @@ cairo_quartz_surface_create (cairo_format_t format,
return &surf->base;
}
#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600
surf->imageData = imageData;
#endif
surf->base.is_clear = TRUE;
return &surf->base;
@ -2633,7 +2457,7 @@ quartz_image_to_png (CGImageRef image, const char *dest)
memset (pathbuf, 0, sizeof (pathbuf));
dest = dest ? dest : image_name;
snprintf (pathbuf, sizeof (pathbuf), "%s/Desktop/%s%d.png",getenv ("HOME"), dest, sctr++, ext);
snprintf (pathbuf, sizeof (pathbuf), "%s/Desktop/%s%d.png",getenv ("HOME"), dest, sctr++);
path = CFStringCreateWithCString (NULL, pathbuf, kCFStringEncodingUTF8);
url = CFURLCreateWithFileSystemPath (NULL, path, kCFURLPOSIXPathStyle, FALSE);
image_dest = CGImageDestinationCreateWithURL (url, png_utti, 1, NULL);
@ -2648,7 +2472,6 @@ quartz_image_to_png (CGImageRef image, const char *dest)
void
quartz_surface_to_png (cairo_quartz_surface_t *nq, const char *dest)
{
static int sctr = 0;
CGImageRef image;
if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {

View file

@ -27,7 +27,7 @@ endif
# Need to add quartz-surface-source
if CAIRO_HAS_QUARTZ_SURFACE
test_sources += $(quartz_surface_test_sources)
test_sources += "$(quartz_surface_test_sources) $(quartz_color_font_test_sources)"
endif
if CAIRO_HAS_PDF_SURFACE

View file

@ -434,6 +434,8 @@ egl_surface_test_sources = \
quartz_surface_test_sources = quartz-surface-source.c
quartz_color_font_test_sources = quartz-color-font.c
pdf_surface_test_sources = \
pdf-features.c \
pdf-mime-data.c \

View file

@ -438,6 +438,7 @@ test_egl_sources = [
test_quartz_sources = [
'quartz-surface-source.c',
'quartz-color-font.c',
]
test_pdf_sources = [

54
test/quartz-color-font.c Normal file
View file

@ -0,0 +1,54 @@
/*
* Copyright © 2022 John Ralls <jralls@ceridwen.us>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#include "cairo-test.h"
#include <cairo-quartz.h>
#define WIDTH 100
#define HEIGHT 50
#define FONT "Apple Color Emoji"
static const char smiley_face_utf8[] = { 0xf0, 0x9f, 0x99, 0x82, 0x00 }; /* U+1F642 */
static cairo_test_status_t
draw (cairo_t *cr, int width, int height)
{
cairo_select_font_face(cr, FONT, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size (cr, HEIGHT/2);
cairo_move_to (cr, width/5, 2*height/3);
cairo_show_text(cr, smiley_face_utf8);
cairo_show_text(cr, smiley_face_utf8);
return CAIRO_TEST_SUCCESS;
}
CAIRO_TEST (quartz_color_font,
"Test color font",
"quartz, font", /* keywords */
NULL, /* requirements */
WIDTH, HEIGHT,
NULL, draw)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB