/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright � 2006, 2007 Mozilla Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Mozilla Foundation. * * Contributor(s): * Vladimir Vukicevic <vladimir@mozilla.com>
*/
#define _GNU_SOURCE /* required for RTLD_DEFAULT */ #include"cairoint.h"
/** * SECTION:cairo-quartz * @Title: Quartz Surfaces * @Short_Description: Rendering to Quartz surfaces * @See_Also: #cairo_surface_t * * The Quartz surface is used to render cairo graphics targeting the * Apple OS X Quartz rendering system.
**/
/** * CAIRO_HAS_QUARTZ_SURFACE: * * Defined if the Quartz surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * 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))
}; typedefenum 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.5 */ staticvoid (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
/* public since 10.6 */ static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; staticvoid (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
/* not yet public */ staticunsignedint (*CGContextGetTypePtr) (CGContextRef) = NULL; staticbool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
/* CTFontDrawGlyphs is not available until 10.7 */ staticvoid (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL;
#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)
{ switch (op) { case CAIRO_OPERATOR_MULTIPLY: return kCGBlendModeMultiply; case CAIRO_OPERATOR_SCREEN: return kCGBlendModeScreen; case CAIRO_OPERATOR_OVERLAY: return kCGBlendModeOverlay; case CAIRO_OPERATOR_DARKEN: return kCGBlendModeDarken; case CAIRO_OPERATOR_LIGHTEN: return kCGBlendModeLighten; case CAIRO_OPERATOR_COLOR_DODGE: return kCGBlendModeColorDodge; case CAIRO_OPERATOR_COLOR_BURN: return kCGBlendModeColorBurn; case CAIRO_OPERATOR_HARD_LIGHT: return kCGBlendModeHardLight; case CAIRO_OPERATOR_SOFT_LIGHT: return kCGBlendModeSoftLight; case CAIRO_OPERATOR_DIFFERENCE: return kCGBlendModeDifference; case CAIRO_OPERATOR_EXCLUSION: return kCGBlendModeExclusion; case CAIRO_OPERATOR_HSL_HUE: return kCGBlendModeHue; case CAIRO_OPERATOR_HSL_SATURATION: return kCGBlendModeSaturation; case CAIRO_OPERATOR_HSL_COLOR: return kCGBlendModeColor; 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: return kCGBlendModeCopy; case CAIRO_OPERATOR_OVER: return kCGBlendModeNormal; case CAIRO_OPERATOR_IN: return kCGBlendModeSourceIn; case CAIRO_OPERATOR_OUT: return kCGBlendModeSourceOut; case CAIRO_OPERATOR_ATOP: return kCGBlendModeSourceAtop; case CAIRO_OPERATOR_DEST_OVER: return kCGBlendModeDestinationOver; case CAIRO_OPERATOR_DEST_IN: return kCGBlendModeDestinationIn; case CAIRO_OPERATOR_DEST_OUT: return kCGBlendModeDestinationOut; case CAIRO_OPERATOR_DEST_ATOP: return kCGBlendModeDestinationAtop; case CAIRO_OPERATOR_XOR: 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:
ASSERT_NOT_REACHED;
} return kCGBlendModeNormal; /* just to silence clang warning [-Wreturn-type] */
}
/* Quartz doesn't support SATURATE at all. COLOR_DODGE and * COLOR_BURN in Quartz follow the ISO32000 definition, but cairo * uses the definition from the Adobe Supplement. Also fallback * on SOFT_LIGHT and HSL_HUE, because their results are * significantly different from those provided by pixman.
*/ if (op == CAIRO_OPERATOR_SATURATE ||
op == CAIRO_OPERATOR_SOFT_LIGHT ||
op == CAIRO_OPERATOR_HSL_HUE ||
op == CAIRO_OPERATOR_COLOR_DODGE ||
op == CAIRO_OPERATOR_COLOR_BURN)
{ return CAIRO_INT_STATUS_UNSUPPORTED;
}
/* When the destination has no color components, we can avoid some * fallbacks, but we have to workaround operators which behave
* differently in Quartz. */ if (surface->base.content == CAIRO_CONTENT_ALPHA) {
assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */
if (op == CAIRO_OPERATOR_SOURCE ||
op == CAIRO_OPERATOR_IN ||
op == CAIRO_OPERATOR_OUT ||
op == CAIRO_OPERATOR_DEST_IN ||
op == CAIRO_OPERATOR_DEST_ATOP ||
op == CAIRO_OPERATOR_XOR)
{ return CAIRO_INT_STATUS_UNSUPPORTED;
}
if (op == CAIRO_OPERATOR_DEST_OVER)
op = CAIRO_OPERATOR_OVER; elseif (op == CAIRO_OPERATOR_SATURATE)
op = CAIRO_OPERATOR_ADD; elseif (op == CAIRO_OPERATOR_COLOR_DODGE)
op = CAIRO_OPERATOR_OVER; elseif (op == CAIRO_OPERATOR_COLOR_BURN)
op = CAIRO_OPERATOR_OVER;
}
/* Quartz computes a small number of samples of the gradient color * function. On MacOS X 10.5 it apparently computes only 1024
* samples. */ #define MAX_GRADIENT_RANGE 1024
if (gradient->base.extend == CAIRO_EXTEND_PAD) {
t[0] = MAX (t[0], -0.5);
t[1] = MIN (t[1], 1.5);
} elseif (t[1] - t[0] > MAX_GRADIENT_RANGE) return NULL;
/* set the input range for the function -- the function knows how
to map values outside of 0.0 .. 1.0 to the correct color */
input_value_range[0] = t[0];
input_value_range[1] = t[1];
} else {
input_value_range[0] = 0;
input_value_range[1] = 1;
}
image_data = _cairo_malloc_ab (image_surface->height, image_surface->stride); if (unlikely (!image_data))
{ if (acquired)
_cairo_surface_release_source_image (source, image_surface, image_extra); else
cairo_surface_destroy (&image_surface->base);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
// The last row of data may have less than stride bytes so make sure we // only copy the minimum amount required from that row.
memcpy (image_data, image_surface->data,
(image_surface->height - 1) * image_surface->stride +
cairo_format_stride_for_width (image_surface->format,
image_surface->width));
*image_out = CairoQuartzCreateCGImage (image_surface->format,
image_surface->width,
image_surface->height,
image_surface->stride,
image_data, TRUE,
NULL,
DataProviderReleaseCallback,
image_data);
/* TODO: differentiate memory error and unsupported surface type */ if (unlikely (*image_out == NULL))
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (acquired)
_cairo_surface_release_source_image (source, image_surface, image_extra); else
cairo_surface_destroy (&image_surface->base);
return status;
}
/* Generic #cairo_pattern_t -> CGPattern function */
CGContextDrawImage (context, info->imageBounds, info->image); if (info->do_reflect) { /* draw 3 more copies of the image, flipped. * DrawImage draws the image according to the current Y-direction into the rectangle given * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left * of the base image position, and the Y axis is extending upwards.
*/
/* Make the y axis extend downwards, and draw a flipped image below */
CGContextScaleCTM (context, 1, -1);
CGContextDrawImage (context, info->imageBounds, info->image);
/* Shift over to the right, and flip vertically (translation is 2x, * since we'll be flipping and thus rendering the rectangle "backwards"
*/
CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
CGContextScaleCTM (context, -1, 1);
CGContextDrawImage (context, info->imageBounds, info->image);
/* Then unflip the Y-axis again, and draw the image above the point. */
CGContextScaleCTM (context, 1, -1);
CGContextDrawImage (context, info->imageBounds, info->image);
}
}
m = spattern->base.matrix;
status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
&m, clip, &image); if (unlikely (status)) return status;
info = _cairo_malloc (sizeof (SurfacePatternDrawInfo)); if (unlikely (!info)) return CAIRO_STATUS_NO_MEMORY;
/* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure * that the data will stick around for this image when the printer gets to it. * Otherwise, the underlying data store may disappear from under us! * * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces, * since the Quartz surfaces have a higher chance of sticking around. If the * source is a quartz image surface, then it's set up to retain a ref to the * image surface that it's backed by.
*/
info->image = image;
info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
info->do_reflect = FALSE;
/* The pattern matrix is relative to the bottom left, again; the * incoming cairo pattern matrix is relative to the upper left. * So we take the pattern matrix and the original context matrix, * which gives us the correct base translation/y flip.
*/
ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
/* Source layer to be rendered when using DO_LAYER. Unlike 'layer' above, this is not owned by the drawing state
but by the source surface. */
CGLayerRef sourceLayer;
} cairo_quartz_drawing_state_t;
/* Quartz does not support repeating radients. We handle repeating gradients by manually extending the gradient and repeating color stops. We need to minimize the number of repetitions since Quartz seems to sample our color function across the entire range, even if part of that range is not needed for the visible area of the gradient, and it samples with some fixed resolution, so if the gradient range is too large it samples with very low resolution and the gradient is very coarse. _cairo_quartz_create_gradient_function computes the number of repetitions needed based on the extents.
*/ static cairo_int_status_t
_cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state, const cairo_gradient_pattern_t *gradient, const cairo_rectangle_int_t *extents)
{
cairo_matrix_t mat;
cairo_circle_double_t start, end;
CGFunctionRef gradFunc;
CGColorSpaceRef rgb; bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
assert (gradient->n_stops > 0);
mat = gradient->base.matrix;
cairo_matrix_invert (&mat);
_cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); if (unlikely (status)) return status;
status = _cairo_quartz_surface_set_cairo_operator (surface, op); if (unlikely (status)) return status;
/* Save before we change the pattern, colorspace, etc. so that * we can restore and make sure that quartz releases our * pattern (which may be stack allocated)
*/
/* * To implement mask unbounded operations Quartz needs a temporary * surface which will be composited entirely (ignoring the mask). * To implement source unbounded operations Quartz needs a * temporary surface which allows extending the source to a size * covering the whole mask, but there are some optimization * opportunities: * * - CLEAR completely ignores the source, thus we can just use a * solid color fill. * * - SOURCE can be implemented by drawing the source and clearing * outside of the source as long as the two regions have no * intersection. This happens when the source is a pixel-aligned * rectangle. If the source is at least as big as the * intersection between the clip rectangle and the mask * rectangle, no clear operation is needed.
*/
needs_temp = ! _cairo_operator_bounded_by_mask (op);
/* Quartz seems to tile images at pixel-aligned regions only -- this * leads to seams if the image doesn't end up scaling to fill the * space exactly. The CGPattern tiling approach doesn't have this * problem. Check if we're going to fill up the space (within some * epsilon), and if not, fall back to the CGPattern type.
*/
if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
(fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
{ /* We're good to use DrawTiledImage, but ensure that
* the math works out */
/* Quartz likes to munge the pattern phase (as yet unexplained * why); force it to 0,0 as we've already baked in the correct * pattern translation into the pattern matrix
*/
CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
if (state->action == DO_LAYER) { /* Note that according to Apple docs it's completely legal to draw a CGLayer * to any CGContext, even one it wasn't created for.
*/
assert (state->sourceLayer);
CGContextDrawLayerAtPoint (state->cgDrawContext, state->rect.origin,
state->sourceLayer);
} elseif (state->action == DO_IMAGE) {
CGContextDrawImage (state->cgDrawContext, state->rect, state->image); if (op == CAIRO_OPERATOR_SOURCE &&
state->cgDrawContext == state->cgMaskContext)
{
CGContextBeginPath (state->cgDrawContext);
CGContextAddRect (state->cgDrawContext, state->rect);
// let's hope they don't add YUV under us
colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
color_comps = CGColorSpaceGetNumberOfComponents(colorspace);
// XXX TODO: We can handle all of these by converting to // pixman masks, including non-native-endian masks if (bpc != 8) return CAIRO_INT_STATUS_UNSUPPORTED;
if (bpp != 32 && bpp != 8) return CAIRO_INT_STATUS_UNSUPPORTED;
if (color_comps != 3 && color_comps != 1) return CAIRO_INT_STATUS_UNSUPPORTED;
if (IS_EMPTY (surface)) return CAIRO_STATUS_SUCCESS;
/* Restore our saved gstate that we use to reset clipping */
CGContextRestoreGState (surface->cgContext);
_cairo_surface_clipper_reset (&surface->clipper);
/* ClipToMask is essentially drawing an image, so we need to flip the CTM
* to get the image to appear oriented the right way */
CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix));
CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height);
CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
if (! need_temp) {
mask_surf = extents->mask_pattern.surface.surface;
/* When an opaque surface used as a mask in Quartz, its * luminosity is used as the alpha value, so we con only use
* surfaces with alpha without creating a temporary mask. */
need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
}
if (! need_temp) {
CGInterpolationQuality mask_filter;
cairo_bool_t simple_transform;
/* Quartz only allows one interpolation to be set for mask and * source, so we can skip the temp surface only if the source
* filtering makes the mask look correct. */ if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
need_temp = ! (simple_transform || filter == mask_filter); else
filter = mask_filter;
}
if (need_temp) { /* Render the mask to a surface */
mask_surf = _cairo_quartz_surface_create_similar (surface,
CAIRO_CONTENT_ALPHA,
surface->extents.width,
surface->extents.height);
status = mask_surf->status; if (unlikely (status)) goto BAIL;
/* mask_surf is clear, so use OVER instead of SOURCE to avoid a
* temporary layer or fallback to cairo-image. */
status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL); if (unlikely (status)) goto BAIL;
cairo_matrix_init_identity (&matrix);
}
status = _cairo_quartz_cg_mask_with_surface (extents,
mask_surf, &matrix, filter);
rv = _cairo_quartz_setup_state (&state, extents); if (unlikely (rv)) goto BAIL;
// Turning antialiasing off used to cause misrendering with // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels). // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
CGContextSetLineWidth (state.cgMaskContext, style->line_width);
CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit);
/* * CGContextShowGlyphsWithAdvances does not transform the advances * by the text matrix, if the drawing mode is kCGTextClip. Instead * of trying to recompute the advances, make sure that the text * matrix is the identity and rely on the CTM for the text * transform.
*/
CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
CGContextConcatCTM (state.cgMaskContext, textTransform);
/* Convert glyph positions to glyph advances. */
cg_glyphs[0] = glyphs[0].index; for (i = 1; i < num_glyphs; i++) {
CGSize advance = CGSizeMake (glyphs[i].x - glyphs[i-1].x,
glyphs[i].y - glyphs[i-1].y);
cg_advances[i] = CGSizeApplyAffineTransform (advance, invTextTransform);
cg_glyphs[i] = glyphs[i].index;
}
if (CTFontDrawGlyphsPtr) { /* If CTFontDrawGlyphs is available, we want to use that * instead of the deprecated CGContextShowGlyphsWithAdvances * so that colored-bitmap fonts like Apple Color Emoji will
* render properly. */
/* Accumulate the glyph advances into glyph positions, * overwriting them. Start at (0,0) because the CTM already
* takes into account the position of the first glyph. */
CGPoint pos = CGPointMake (0, 0);
cg_positions = (CGPoint *) cg_advances;
cg_positions[0] = pos; for (i = 1; i < num_glyphs; i++) {
pos.x += cg_advances[i].width;
pos.y += cg_advances[i].height;
cg_positions[i] = pos;
}
/* Revert the changes to the CTM. This fragment cannot rely on
* CG{Save,Restore}GState, as that would reset the clip. */
CGContextConcatCTM (state.cgMaskContext, invTextTransform);
CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
if (state.action != DO_DIRECT)
_cairo_quartz_draw_source (&state, extents->op);
BAIL: if (didForceFontSmoothing)
CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
_cairo_quartz_teardown_state (&state, extents);
if (cg_advances != cg_advances_static)
free (cg_advances);
if (IS_EMPTY (surface)) return CAIRO_STATUS_SUCCESS;
if (path == NULL) { /* If we're being asked to reset the clip, we can only do it * by restoring the gstate to our previous saved one, and * saving it again. * * Note that this assumes that ALL quartz surface creation * functions will do a SaveGState first; we do this in create_internal.
*/
CGContextRestoreGState (surface->cgContext);
CGContextSaveGState (surface->cgContext);
} else {
CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
if (fill_rule == CAIRO_FILL_RULE_WINDING)
CGContextClip (surface->cgContext); else
CGContextEOClip (surface->cgContext);
}
ND ((stderr, "-- intersect_clip_path\n"));
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_quartz_surface_link (cairo_quartz_surface_t *surface,
cairo_bool_t begin, constchar *attributes)
{
cairo_link_attrs_t link_attrs;
cairo_int_status_t status = CAIRO_STATUS_SUCCESS; int i, num_rects;
/* We only process the 'begin' tag, and expect a rect attribute; using the extents of the drawing operations enclosed by the begin/end
link tags to define the clickable area is not implemented. */ if (!begin) return status;
status = _cairo_tag_parse_link_attributes (attributes, &link_attrs); if (unlikely (status)) return status;
num_rects = _cairo_array_num_elements (&link_attrs.rects); if (num_rects > 0) { /* Create either a named destination or a URL, depending which is present
in the link attributes. */
CFURLRef url = NULL;
CFStringRef name = NULL; if (link_attrs.uri && *link_attrs.uri)
url = CFURLCreateWithBytes (NULL,
(const UInt8 *) link_attrs.uri,
strlen (link_attrs.uri),
kCFStringEncodingUTF8,
NULL); elseif (link_attrs.dest && *link_attrs.dest)
name = CFStringCreateWithBytes (kCFAllocatorDefault,
(const UInt8 *) link_attrs.dest,
strlen (link_attrs.dest),
kCFStringEncodingUTF8, FALSE); else/* silently ignore link that doesn't have a usable target */ goto cleanup;
for (i = 0; i < num_rects; i++) {
CGRect link_rect;
cairo_rectangle_t rectf;
_cairo_array_copy_element (&link_attrs.rects, i, &rectf);
/* Currently the only tags we support are CAIRO_TAG_LINK and CAIRO_TAG_DEST */ if (!strcmp (tag_name, CAIRO_TAG_LINK)) return _cairo_quartz_surface_link (surface, begin, attributes);
if (!strcmp (tag_name, CAIRO_TAG_DEST)) return _cairo_quartz_surface_dest (surface, begin, attributes);
/* Unknown tag names are silently ignored here. */ return CAIRO_INT_STATUS_SUCCESS;
}
// XXXtodo implement show_page; need to figure out how to handle begin/end
/* Save so we can always get back to a known-good CGContext -- this is * required for proper behaviour of intersect_clip_path(NULL)
*/
CGContextSaveGState (cgContext);
/** * cairo_quartz_surface_create_for_cg_context: * @cgContext: the existing CGContext for which to create the surface * @width: width of the surface, in pixels * @height: height of the surface, in pixels * * Creates a Quartz surface that wraps the given CGContext. The * CGContext is assumed to be in the standard Cairo coordinate space * (that is, with the origin at the upper left and the Y axis * increasing downward). If the CGContext is in the Quartz coordinate * space (with the origin at the bottom left), then it should be * flipped before this function is called. The flip can be accomplished * using a translate and a scale; for example: * * <informalexample><programlisting> * CGContextTranslateCTM (cgContext, 0.0, height); * CGContextScaleCTM (cgContext, 1.0, -1.0); * </programlisting></informalexample> * * All Cairo operations are implemented in terms of Quartz operations, * as long as Quartz-compatible elements are used (such as Quartz fonts). * * Return value: the newly created Cairo surface. * * Since: 1.6
**/
/** * cairo_quartz_surface_create_cg_layer * @surface: The returned surface can be efficiently drawn into this * destination surface (if tiling is not used)." * @content: the content type of the surface * @width: width of the surface, in pixels * @height: height of the surface, in pixels * * Creates a Quartz surface backed by a CGLayer, if the given surface * is a Quartz surface; the CGLayer is created to match the surface's * Quartz context. Otherwise just calls cairo_surface_create_similar. * The returned surface can be efficiently blitted to the given surface, * but tiling and 'extend' modes other than NONE are not so efficient. * * Return value: the newly created surface. * * Since: 1.10
**/
cairo_surface_t *
cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
cairo_content_t content, unsignedint width, unsignedint height)
{
cairo_quartz_surface_t *surf;
CGLayerRef layer;
CGContextRef ctx;
CGContextRef cgContext;
if (!_cairo_quartz_verify_surface_size(width, height)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
/* If we pass zero width or height into CGLayerCreateWithContext below, * it will fail.
*/ if (width == 0 || height == 0) { return (cairo_surface_t*)
_cairo_quartz_surface_create_internal (NULL, content,
width, height);
}
ctx = CGLayerGetContext (layer); /* Flip it when we draw into it, so that when we finally composite it * to a flipped target, the directions match and Quartz will optimize * the composition properly
*/
CGContextTranslateCTM (ctx, 0, height);
CGContextScaleCTM (ctx, 1, -1);
CGContextRetain (ctx);
surf = _cairo_quartz_surface_create_internal (ctx, content,
width, height); if (surf->base.status) {
CGLayerRelease (layer); // create_internal will have set an error return (cairo_surface_t*) surf;
}
surf->cgLayer = layer;
return (cairo_surface_t *) surf;
}
/** * cairo_quartz_surface_create: * @format: format of pixels in the surface to create * @width: width of the surface, in pixels * @height: height of the surface, in pixels * * Creates a Quartz surface backed by a CGBitmap. The surface is * created using the Device RGB (or Device Gray, for A8) color space. * All Cairo operations, including those that require software * rendering, will succeed on this surface. * * Return value: the newly created surface. * * Since: 1.6
**/
cairo_surface_t *
cairo_quartz_surface_create (cairo_format_t format, unsignedint width, unsignedint height)
{
cairo_quartz_surface_t *surf;
CGContextRef cgc;
CGColorSpaceRef cgColorspace;
CGBitmapInfo bitinfo; void *imageData; int stride; int bitsPerComponent;
if (!_cairo_quartz_verify_surface_size (width, height)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
if (format == CAIRO_FORMAT_ARGB32 ||
format == CAIRO_FORMAT_RGB24)
{
cgColorspace = CGColorSpaceCreateDeviceRGB ();
bitinfo = kCGBitmapByteOrder32Host; if (format == CAIRO_FORMAT_ARGB32)
bitinfo |= kCGImageAlphaPremultipliedFirst; else
bitinfo |= kCGImageAlphaNoneSkipFirst;
bitsPerComponent = 8;
stride = width * 4;
} elseif (format == CAIRO_FORMAT_A8) {
cgColorspace = NULL;
stride = width;
bitinfo = kCGImageAlphaOnly;
bitsPerComponent = 8;
} elseif (format == CAIRO_FORMAT_A1) { /* I don't think we can usefully support this, as defined by * cairo_format_t -- these are 1-bit pixels stored in 32-bit * quantities.
*/ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
} else { return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
}
/* The Apple docs say that for best performance, the stride and the data * pointer should be 16-byte aligned. malloc already aligns to 16-bytes, * so we don't have to anything special on allocation.
*/
stride = (stride + 15) & ~15;
if (!cgc) {
free (imageData); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
/* flip the Y axis */
CGContextTranslateCTM (cgc, 0.0, height);
CGContextScaleCTM (cgc, 1.0, -1.0);
surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
width, height); if (surf->base.status) {
CGContextRelease (cgc);
free (imageData); // create_internal will have set an error return &surf->base;
}
// We created this data, so we can delete it.
surf->ownsData = TRUE;
return &surf->base;
}
/** * cairo_quartz_surface_get_cg_context: * @surface: the Cairo Quartz surface * * Returns the CGContextRef that the given Quartz surface is backed * by. * * A call to cairo_surface_flush() is required before using the * CGContextRef to ensure that all pending drawing operations are * finished and to restore any temporary modification cairo has made * to its state. A call to cairo_surface_mark_dirty() is required * after the state or the content of the CGContextRef has been * modified. * * Return value: the CGContextRef for the given surface. * * Since: 1.6
**/
CGContextRef
cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
{ if (surface && _cairo_surface_is_quartz (surface)) {
cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface; return quartz->cgContext;
} else return NULL;
}
/** * _cairo_surface_is_quartz: * @surface: a #cairo_surface_t * * Checks if a surface is a #cairo_quartz_surface_t * * Return value: True if the surface is an quartz surface
**/
cairo_bool_t
_cairo_surface_is_quartz (const cairo_surface_t *surface)
{ return surface->backend == &cairo_quartz_surface_backend;
}
GraphicsExportComponent grex = 0; unsignedlong sizeWritten;
ComponentResult result;
// create the data reference
result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle,
0, &dataRef, &dataRefType);
if (NULL != dataRef && noErr == result) { // get the PNG exporter
result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG,
&grex);
if (grex) { // tell the exporter where to find its source image
result = GraphicsExportSetInputCGImage (grex, inImageRef);
if (noErr == result) { // tell the exporter where to save the exporter image
result = GraphicsExportSetOutputDataReference (grex, dataRef,
dataRefType);
if (noErr == result) { // write the PNG file
result = GraphicsExportDoExport (grex, &sizeWritten);
}
}
// remember to close the component
CloseComponent (grex);
}
// remember to dispose of the data reference handle
DisposeHandle (dataRef);
}
}
if (dest == NULL) {
fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
sctr++;
dest = sptr;
}
CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext); if (imgref == NULL) {
fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq); return;
}
ExportCGImageToPNGFile (imgref, dest);
CGImageRelease (imgref);
}
#endif/* QUARTZ_DEBUG */
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.36 Sekunden
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.