cairo-quartz2-surface.c [plain text]
#include <Carbon/Carbon.h>
#include <AGL/agl.h>
#include <OpenGL/gl.h>
#include "cairoint.h"
#include "cairo-private.h"
#include "cairo-quartz2.h"
#include "cairo-quartz-private.h"
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, kPrivateCGCompositePlusLighter = 12, };
typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
CG_EXTERN void CGContextResetCTM (CGContextRef);
CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform);
CG_EXTERN void CGContextResetClip (CGContextRef);
CG_EXTERN CGSize CGContextGetPatternPhase (CGContextRef);
typedef struct cairo_quartzgl_surface {
cairo_surface_t base;
void *imageData;
cairo_bool_t y_grows_down;
AGLContext aglContext;
CGContextRef cgContext;
cairo_rectangle_t extents;
CGImageRef sourceImage;
CGShadingRef sourceShading;
CGPatternRef sourcePattern;
} cairo_quartzgl_surface_t;
static cairo_status_t
_cairo_path_to_quartz_path_move_to (void *closure, cairo_point_t *point)
{
CGPathMoveToPoint ((CGMutablePathRef) closure, NULL, _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_path_to_quartz_path_line_to (void *closure, cairo_point_t *point)
{
CGPathAddLineToPoint ((CGMutablePathRef) closure, NULL, _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_path_to_quartz_path_curve_to (void *closure, cairo_point_t *p0, cairo_point_t *p1, cairo_point_t *p2)
{
CGPathAddCurveToPoint ((CGMutablePathRef) closure, NULL,
_cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y),
_cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y),
_cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_path_to_quartz_path_close_path (void *closure)
{
CGPathCloseSubpath ((CGMutablePathRef) closure);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_path_to_quartz_context_move_to (void *closure, cairo_point_t *point)
{
CGContextMoveToPoint ((CGContextRef) closure, _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_path_to_quartz_context_line_to (void *closure, cairo_point_t *point)
{
if (CGContextIsPathEmpty ((CGContextRef) closure))
CGContextMoveToPoint ((CGContextRef) closure, _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y));
else
CGContextAddLineToPoint ((CGContextRef) closure, _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_path_to_quartz_context_curve_to (void *closure, cairo_point_t *p0, cairo_point_t *p1, cairo_point_t *p2)
{
CGContextAddCurveToPoint ((CGContextRef) closure,
_cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y),
_cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y),
_cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y));
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_path_to_quartz_context_close_path (void *closure)
{
CGContextClosePath ((CGContextRef) closure);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_quartzgl_cairo_path_to_quartz_path (cairo_path_fixed_t *path,
CGMutablePathRef cgPath)
{
return _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_path_to_quartz_path_move_to,
_cairo_path_to_quartz_path_line_to,
_cairo_path_to_quartz_path_curve_to,
_cairo_path_to_quartz_path_close_path,
cgPath);
}
static cairo_status_t
_cairo_quartzgl_cairo_path_to_quartz_context (cairo_path_fixed_t *path,
CGContextRef cgc)
{
return _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_path_to_quartz_context_move_to,
_cairo_path_to_quartz_context_line_to,
_cairo_path_to_quartz_context_curve_to,
_cairo_path_to_quartz_context_close_path,
cgc);
}
static PrivateCGCompositeMode
_cairo_quartzgl_cairo_operator_to_quartz (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:
return kPrivateCGCompositeCopy;
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_SATURATE:
return kPrivateCGCompositePlusDarker;
}
return kPrivateCGCompositeCopy;
}
static CGLineCap
_cairo_quartzgl_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
{
switch (ccap) {
case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; break;
case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; break;
case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; break;
}
return kCGLineCapButt;
}
static CGLineJoin
_cairo_quartzgl_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
{
switch (cjoin) {
case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; break;
case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; break;
case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; break;
}
return kCGLineJoinMiter;
}
static void
_cairo_quartzgl_cairo_matrix_to_quartz (const cairo_matrix_t *src,
CGAffineTransform *dst)
{
dst->a = src->xx;
dst->b = src->xy;
dst->c = src->yx;
dst->d = src->yy;
dst->tx = src->x0;
dst->ty = src->y0;
}
static void
ComputeGradientValue (void *info, const float *in, float *out)
{
float fdist = *in;
cairo_fixed_16_16_t fdist_fix = _cairo_fixed_from_double(*in);
cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
int i;
for (i = 0; i < grad->n_stops; i++) {
if (grad->stops[i].x > fdist_fix)
break;
}
if (i == 0 || i == grad->n_stops) {
if (i == grad->n_stops)
--i;
out[0] = grad->stops[i].color.red / 65535.;
out[1] = grad->stops[i].color.green / 65535.;
out[2] = grad->stops[i].color.blue / 65535.;
out[3] = grad->stops[i].color.alpha / 65535.;
} else {
float ax = _cairo_fixed_to_double(grad->stops[i-1].x);
float bx = _cairo_fixed_to_double(grad->stops[i].x) - ax;
fdist -= ax;
float bp = fdist/bx;
float ap = 1.0 - bp;
out[0] =
(grad->stops[i-1].color.red / 65535.) * ap +
(grad->stops[i].color.red / 65535.) * bp;
out[1] =
(grad->stops[i-1].color.green / 65535.) * ap +
(grad->stops[i].color.green / 65535.) * bp;
out[2] =
(grad->stops[i-1].color.blue / 65535.) * ap +
(grad->stops[i].color.blue / 65535.) * bp;
out[3] =
(grad->stops[i-1].color.alpha / 65535.) * ap +
(grad->stops[i].color.alpha / 65535.) * bp;
}
}
static CGFunctionRef
CreateGradientFunction (cairo_gradient_pattern_t *gpat)
{
static const float input_value_range[2] = { 0.f, 1.f };
static const float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
static const CGFunctionCallbacks callbacks = {
0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
};
return CGFunctionCreate (gpat,
1,
input_value_range,
4,
output_value_ranges,
&callbacks);
}
static CGShadingRef
_cairo_quartzgl_cairo_gradient_pattern_to_quartz (cairo_pattern_t *abspat)
{
cairo_matrix_t mat;
double x0, y0;
if (abspat->type != CAIRO_PATTERN_TYPE_LINEAR &&
abspat->type != CAIRO_PATTERN_TYPE_RADIAL)
return NULL;
cairo_pattern_get_matrix (abspat, &mat);
if (mat.xx != 1.0 || mat.yy != 1.0 || mat.xy != 0.0 || mat.yx != 0.0)
return NULL;
x0 = mat.x0;
y0 = mat.y0;
if (abspat->type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t*) abspat;
CGShadingRef shading;
CGPoint start, end;
CGFunctionRef gradFunc;
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
start = CGPointMake (_cairo_fixed_to_double (lpat->gradient.p1.x) - x0,
_cairo_fixed_to_double (lpat->gradient.p1.y) - y0);
end = CGPointMake (_cairo_fixed_to_double (lpat->gradient.p2.x) - x0,
_cairo_fixed_to_double (lpat->gradient.p2.y) - y0);
cairo_pattern_reference (abspat);
gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) lpat);
shading = CGShadingCreateAxial (rgb,
start, end,
gradFunc,
true, true);
CGColorSpaceRelease(rgb);
CGFunctionRelease(gradFunc);
return shading;
}
if (abspat->type == CAIRO_PATTERN_TYPE_RADIAL) {
cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t*) abspat;
CGShadingRef shading;
CGPoint start, end;
CGFunctionRef gradFunc;
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
start = CGPointMake (_cairo_fixed_to_double (rpat->gradient.inner.x) - x0,
_cairo_fixed_to_double (rpat->gradient.inner.y) - y0);
end = CGPointMake (_cairo_fixed_to_double (rpat->gradient.outer.x) - x0,
_cairo_fixed_to_double (rpat->gradient.outer.y) - y0);
cairo_pattern_reference (abspat);
gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) rpat);
shading = CGShadingCreateRadial (rgb,
start,
_cairo_fixed_to_double (rpat->gradient.inner.radius),
end,
_cairo_fixed_to_double (rpat->gradient.outer.radius),
gradFunc,
true, true);
CGColorSpaceRelease(rgb);
CGFunctionRelease(gradFunc);
return shading;
}
return NULL;
}
static void
SurfacePatternDrawFunc (void *info, CGContextRef context)
{
cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) info;
cairo_surface_t *pat_surf = spat->surface;
cairo_rectangle_t extents;
cairo_status_t status;
cairo_quartzgl_surface_t *quartz_surf = NULL;
CGImageRef img;
if (!cairo_surface_is_quartzgl (pat_surf)) {
cairo_surface_t *dummy = cairo_quartzgl_surface_create (CAIRO_FORMAT_ARGB32, 1, 1, TRUE);
cairo_rectangle_t rect;
_cairo_surface_get_extents (pat_surf, &rect);
cairo_surface_t *new_surf = NULL;
_cairo_surface_clone_similar (dummy, pat_surf, &new_surf);
cairo_surface_destroy(dummy);
quartz_surf = (cairo_quartzgl_surface_t *) new_surf;
} else {
cairo_surface_reference (pat_surf);
quartz_surf = (cairo_quartzgl_surface_t*) pat_surf;
}
img = CGBitmapContextCreateImage (quartz_surf->cgContext);
if (!img)
{
CGAffineTransform xform = CGContextGetCTM(context);
}
CGRect imageBounds;
imageBounds.size = CGSizeMake (CGImageGetWidth(img), CGImageGetHeight(img));
imageBounds.origin.x = 0;
imageBounds.origin.y = 0;
CGContextDrawImage (context, imageBounds, img);
CGImageRelease (img);
cairo_surface_destroy ((cairo_surface_t*) quartz_surf);
}
static CGPatternRef
_cairo_quartzgl_cairo_repeating_surface_pattern_to_quartz (cairo_quartzgl_surface_t *dest,
cairo_pattern_t *abspat)
{
cairo_surface_pattern_t *spat;
cairo_surface_t *pat_surf;
cairo_rectangle_t extents;
CGRect pbounds;
CGAffineTransform ptransform, stransform;
CGPatternCallbacks cb = { 0,
SurfacePatternDrawFunc,
(CGFunctionReleaseInfoCallback) cairo_pattern_destroy };
CGPatternRef cgpat;
float rw, rh;
if (abspat->type != CAIRO_PATTERN_TYPE_SURFACE)
return NULL;
spat = (cairo_surface_pattern_t *) abspat;
pat_surf = spat->surface;
_cairo_surface_get_extents (pat_surf, &extents);
pbounds.origin.x = 0;
pbounds.origin.y = 0;
pbounds.size.width = extents.width;
pbounds.size.height = extents.height;
cairo_matrix_t m = spat->base.matrix;
cairo_matrix_invert(&m);
_cairo_quartzgl_cairo_matrix_to_quartz (&m, &stransform);
ptransform = CGContextGetCTM (dest->cgContext);
ptransform = CGAffineTransformConcat (stransform, ptransform);
#if 0
if (spat->base.extend == CAIRO_EXTEND_NONE) {
rw = 0;
rh = 0;
} else if (spat->base.extend == CAIRO_EXTEND_REPEAT) {
rw = extents.width;
rh = extents.height;
} else if (spat->base.extend == CAIRO_EXTEND_REFLECT) {
rw = extents.width;
rh = extents.height;
} else {
rw = 0;
rh = 0;
}
#else
rw = extents.width;
rh = extents.height;
#endif
cairo_pattern_reference (abspat);
cgpat = CGPatternCreate (abspat,
pbounds,
ptransform,
rw, rh,
kCGPatternTilingConstantSpacing,
TRUE,
&cb);
return cgpat;
}
typedef enum {
DO_SOLID,
DO_SHADING,
DO_PATTERN,
DO_UNSUPPORTED
} cairo_quartzgl_action_t;
static cairo_quartzgl_action_t
_cairo_quartzgl_setup_source (cairo_quartzgl_surface_t *surface,
cairo_pattern_t *source)
{
assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
CGContextSetRGBStrokeColor (surface->cgContext,
solid->color.red,
solid->color.green,
solid->color.blue,
solid->color.alpha);
CGContextSetRGBFillColor (surface->cgContext,
solid->color.red,
solid->color.green,
solid->color.blue,
solid->color.alpha);
} else if (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
source->type == CAIRO_PATTERN_TYPE_RADIAL)
{
CGShadingRef shading = _cairo_quartzgl_cairo_gradient_pattern_to_quartz (source);
if (!shading)
return CAIRO_INT_STATUS_UNSUPPORTED;
surface->sourceShading = shading;
} else if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
CGPatternRef pattern = _cairo_quartzgl_cairo_repeating_surface_pattern_to_quartz (surface, source);
if (!pattern)
return CAIRO_INT_STATUS_UNSUPPORTED;
float patternAlpha = 1.0;
CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
CGContextSetFillColorSpace (surface->cgContext, patternSpace);
CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
CGColorSpaceRelease (patternSpace);
CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
surface->sourcePattern = pattern;
} else {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_quartzgl_teardown_source (cairo_quartzgl_surface_t *surface,
cairo_pattern_t *source)
{
if (surface->sourceImage) {
}
if (surface->sourceShading) {
CGShadingRelease(surface->sourceShading);
surface->sourceShading = NULL;
}
if (surface->sourcePattern) {
CGPatternRelease(surface->sourcePattern);
surface->sourcePattern = NULL;
}
}
static void
ImageDataReleaseFunc(void *info, const void *data, size_t size)
{
if (data != NULL) {
free((void *) data);
}
}
static cairo_int_status_t
_cairo_quartzgl_get_image (cairo_quartzgl_surface_t *surface,
cairo_image_surface_t **image_out,
unsigned char **data_out)
{
unsigned char *imageData;
cairo_image_surface_t *isurf;
if (surface->aglContext) {
AGLContext oldContext;
cairo_format_masks_t masks = { 32, 0xff << 24, 0xff << 16, 0xff << 8, 0xff << 0 };
unsigned int i;
oldContext = aglGetCurrentContext();
if (oldContext != surface->aglContext)
aglSetCurrentContext(surface->aglContext);
imageData = (unsigned char *) malloc (surface->extents.width * surface->extents.height * 4);
if (!imageData)
return CAIRO_STATUS_NO_MEMORY;
glReadBuffer (GL_FRONT);
glReadPixels (0, 0, surface->extents.width, surface->extents.height,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
imageData);
unsigned int lineSize = surface->extents.width * 4;
unsigned char *tmpLine = malloc(lineSize);
for (i = 0; i < surface->extents.height / 2; i++) {
unsigned char *l0 = imageData + lineSize * i;
unsigned char *l1 = imageData + (lineSize * (surface->extents.height - i - 1));
memcpy (tmpLine, l0, lineSize);
memcpy (l0, l1, lineSize);
memcpy (l1, tmpLine, lineSize);
}
free (tmpLine);
if (oldContext && oldContext != surface->aglContext)
aglSetCurrentContext(oldContext);
isurf = (cairo_image_surface_t *)_cairo_image_surface_create_with_masks
(imageData,
&masks,
surface->extents.width,
surface->extents.height,
surface->extents.width * 4);
if (data_out)
*data_out = imageData;
else
_cairo_image_surface_assume_ownership_of_data (isurf);
} else if (CGBitmapContextGetBitmapInfo(surface->cgContext) != 0) {
unsigned int stride;
unsigned int bitinfo;
unsigned int bpc, bpp;
CGColorSpaceRef colorspace;
unsigned int color_comps;
imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext);
bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext);
colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
color_comps = CGColorSpaceGetNumberOfComponents(colorspace);
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 (bpp == 32 && color_comps == 3 &&
(bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
(bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
{
isurf = (cairo_image_surface_t *)
cairo_image_surface_create_for_data (imageData,
CAIRO_FORMAT_ARGB32,
surface->extents.width,
surface->extents.height,
stride);
} else if (bpp == 32 && color_comps == 3 &&
(bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
(bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
{
isurf = (cairo_image_surface_t *)
cairo_image_surface_create_for_data (imageData,
CAIRO_FORMAT_RGB24,
surface->extents.width,
surface->extents.height,
stride);
} else if (bpp == 8 && color_comps == 1)
{
isurf = (cairo_image_surface_t *)
cairo_image_surface_create_for_data (imageData,
CAIRO_FORMAT_A8,
surface->extents.width,
surface->extents.height,
stride);
} else {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
} else {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
*image_out = isurf;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_quartzgl_surface_finish (void *abstract_surface)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
if (surface->aglContext)
aglSetCurrentContext(surface->aglContext);
CGContextFlush (surface->cgContext);
CGContextRelease (surface->cgContext);
surface->cgContext = NULL;
if (surface->aglContext)
glFlush();
surface->aglContext = NULL;
if (surface->imageData) {
free (surface->imageData);
surface->imageData = NULL;
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_quartzgl_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
*image_extra = NULL;
return _cairo_quartzgl_get_image (surface, image_out, NULL);
}
static cairo_status_t
_cairo_quartzgl_surface_acquire_dest_image (void *abstract_surface,
cairo_rectangle_t *interest_rect,
cairo_image_surface_t **image_out,
cairo_rectangle_t *image_rect,
void **image_extra)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
cairo_int_status_t status;
unsigned char *data;
*image_rect = surface->extents;
status = _cairo_quartzgl_get_image (surface, image_out, &data);
if (status)
return status;
*image_extra = data;
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_quartzgl_surface_release_dest_image (void *abstract_surface,
cairo_rectangle_t *interest_rect,
cairo_image_surface_t *image,
cairo_rectangle_t *image_rect,
void *image_extra)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
unsigned char *imageData = (unsigned char *) image_extra;
if (!CGBitmapContextGetData (surface->cgContext)) {
CGDataProviderRef dataProvider;
CGImageRef img;
dataProvider = CGDataProviderCreateWithData (NULL, imageData,
surface->extents.width * surface->extents.height * 4,
ImageDataReleaseFunc);
img = CGImageCreate (surface->extents.width, surface->extents.height,
8, 32,
surface->extents.width * 4,
CGColorSpaceCreateDeviceRGB(),
kCGImageAlphaPremultipliedFirst,
dataProvider,
NULL,
false,
kCGRenderingIntentDefault);
if (surface->y_grows_down) {
CGContextSaveGState (surface->cgContext);
CGContextScaleCTM (surface->cgContext, 1.0, -1.0);
CGContextTranslateCTM (surface->cgContext, 0.0, -surface->extents.height);
}
CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy);
CGContextDrawImage (surface->cgContext,
CGRectMake (0, 0, surface->extents.width, surface->extents.height),
img);
if (surface->y_grows_down) {
CGContextRestoreGState (surface->cgContext);
}
CGImageRelease (img);
CGDataProviderRelease (dataProvider);
}
cairo_surface_destroy ((cairo_surface_t *) image);
}
static cairo_surface_t *
_cairo_quartzgl_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
cairo_format_t format;
if (content == CAIRO_CONTENT_COLOR_ALPHA)
format = CAIRO_FORMAT_ARGB32;
else if (content == CAIRO_CONTENT_COLOR)
format = CAIRO_FORMAT_RGB24;
else if (content == CAIRO_CONTENT_ALPHA)
format = CAIRO_FORMAT_A8;
else
return NULL;
return cairo_quartzgl_surface_create (format, width, height, surface->y_grows_down);
}
static cairo_status_t
_cairo_quartzgl_surface_clone_similar (void *abstract_surface,
cairo_surface_t *src,
cairo_surface_t **clone_out)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
cairo_quartzgl_surface_t *new_surface = NULL;
cairo_format_t new_format;
CGImageRef quartz_image = NULL;
cairo_surface_t *surface_to_release = NULL;
if (cairo_surface_is_quartzgl (src)) {
cairo_quartzgl_surface_t *qsurf = (cairo_quartzgl_surface_t *) src;
quartz_image = CGBitmapContextCreateImage (qsurf->cgContext);
new_format = CAIRO_FORMAT_ARGB32;
} else if (_cairo_surface_is_image (src)) {
cairo_image_surface_t *isurf = (cairo_image_surface_t *) src;
CGDataProviderRef dataProvider;
CGColorSpaceRef cgColorspace;
CGBitmapInfo bitinfo;
int bitsPerComponent, bitsPerPixel;
if (isurf->format == CAIRO_FORMAT_ARGB32) {
cgColorspace = CGColorSpaceCreateDeviceRGB();
bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
bitsPerComponent = 8;
bitsPerPixel = 32;
} else if (isurf->format == CAIRO_FORMAT_RGB24) {
cgColorspace = CGColorSpaceCreateDeviceRGB();
bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
bitsPerComponent = 8;
bitsPerPixel = 32;
} else if (isurf->format == CAIRO_FORMAT_A8) {
cgColorspace = CGColorSpaceCreateDeviceGray();
bitinfo = kCGImageAlphaNone;
bitsPerComponent = 8;
bitsPerPixel = 8;
} else {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
new_format = isurf->format;
dataProvider = CGDataProviderCreateWithData (NULL,
isurf->data,
isurf->height * isurf->stride,
NULL);
quartz_image = CGImageCreate (isurf->width, isurf->height,
bitsPerComponent,
bitsPerPixel,
isurf->stride,
cgColorspace,
bitinfo,
dataProvider,
NULL,
false,
kCGRenderingIntentDefault);
CGDataProviderRelease (dataProvider);
CGColorSpaceRelease (cgColorspace);
} else {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
if (!quartz_image)
return CAIRO_INT_STATUS_UNSUPPORTED;
new_surface = (cairo_quartzgl_surface_t *)
cairo_quartzgl_surface_create (new_format,
CGImageGetWidth (quartz_image),
CGImageGetHeight (quartz_image),
surface->y_grows_down);
if (!new_surface || new_surface->base.status)
return CAIRO_INT_STATUS_UNSUPPORTED;
CGContextSetCompositeOperation (new_surface->cgContext,
kPrivateCGCompositeCopy);
CGContextTranslateCTM (new_surface->cgContext, 0.0, new_surface->extents.height);
CGContextScaleCTM (new_surface->cgContext, 1.0, -1.0);
CGContextDrawImage (new_surface->cgContext,
CGRectMake (0, 0, CGImageGetWidth (quartz_image), CGImageGetHeight (quartz_image)),
quartz_image);
CGImageRelease (quartz_image);
*clone_out = (cairo_surface_t*) new_surface;
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_quartzgl_surface_get_extents (void *abstract_surface,
cairo_rectangle_t *extents)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
*extents = surface->extents;
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_quartzgl_surface_paint (void *abstract_surface,
cairo_operator_t op,
cairo_pattern_t *source)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
cairo_quartzgl_action_t action;
if (op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
CGContextSetCompositeOperation (surface->cgContext, _cairo_quartzgl_cairo_operator_to_quartz (op));
CGRect bounds = CGContextGetClipBoundingBox (surface->cgContext);
action = _cairo_quartzgl_setup_source (surface, source);
if (action == DO_SOLID || action == DO_PATTERN) {
CGContextFillRect (surface->cgContext, bounds);
} else if (action == DO_SHADING) {
CGContextDrawShading (surface->cgContext, surface->sourceShading);
} else {
rv = CAIRO_INT_STATUS_UNSUPPORTED;
}
_cairo_quartzgl_teardown_source (surface, source);
return rv;
}
static cairo_int_status_t
_cairo_quartzgl_surface_fill (void *abstract_surface,
cairo_operator_t op,
cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
cairo_quartzgl_action_t action;
if (op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
CGContextSaveGState (surface->cgContext);
CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
CGContextSetCompositeOperation (surface->cgContext, _cairo_quartzgl_cairo_operator_to_quartz (op));
action = _cairo_quartzgl_setup_source (surface, source);
if (action == DO_UNSUPPORTED) {
CGContextRestoreGState (surface->cgContext);
return CAIRO_INT_STATUS_UNSUPPORTED;
}
CGContextBeginPath (surface->cgContext);
_cairo_quartzgl_cairo_path_to_quartz_context (path, surface->cgContext);
if (action == DO_SOLID || action == DO_PATTERN) {
if (fill_rule == CAIRO_FILL_RULE_WINDING)
CGContextFillPath (surface->cgContext);
else
CGContextEOFillPath (surface->cgContext);
} else if (action == DO_SHADING) {
if (fill_rule == CAIRO_FILL_RULE_WINDING)
CGContextClip (surface->cgContext);
else
CGContextEOClip (surface->cgContext);
CGContextDrawShading (surface->cgContext, surface->sourceShading);
} else {
rv = CAIRO_INT_STATUS_UNSUPPORTED;
}
_cairo_quartzgl_teardown_source (surface, source);
CGContextRestoreGState (surface->cgContext);
return rv;
}
static cairo_int_status_t
_cairo_quartzgl_surface_stroke (void *abstract_surface,
cairo_operator_t op,
cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_stroke_style_t *style,
cairo_matrix_t *ctm,
cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
cairo_quartzgl_action_t action;
if (op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
CGContextSaveGState (surface->cgContext);
CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
CGContextSetLineWidth (surface->cgContext, style->line_width);
CGContextSetLineCap (surface->cgContext, _cairo_quartzgl_cairo_line_cap_to_quartz (style->line_cap));
CGContextSetLineJoin (surface->cgContext, _cairo_quartzgl_cairo_line_join_to_quartz (style->line_join));
CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
if (style->dash && style->num_dashes) {
#define STATIC_DASH 32
float sdash[STATIC_DASH];
float *fdash = sdash;
int k;
if (style->num_dashes > STATIC_DASH)
fdash = malloc (sizeof(float)*style->num_dashes);
for (k = 0; k < style->num_dashes; k++)
fdash[k] = (float) style->dash[k];
CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, style->num_dashes);
if (fdash != sdash)
free (fdash);
}
CGContextSetCompositeOperation (surface->cgContext, _cairo_quartzgl_cairo_operator_to_quartz (op));
action = _cairo_quartzgl_setup_source (surface, source);
if (action == DO_UNSUPPORTED) {
CGContextRestoreGState (surface->cgContext);
return CAIRO_INT_STATUS_UNSUPPORTED;
}
CGContextBeginPath (surface->cgContext);
_cairo_quartzgl_cairo_path_to_quartz_context (path, surface->cgContext);
if (action == DO_SOLID || action == DO_PATTERN) {
CGContextStrokePath (surface->cgContext);
} else if (action == DO_SHADING) {
CGContextReplacePathWithStrokedPath (surface->cgContext);
CGContextClip (surface->cgContext);
CGContextDrawShading (surface->cgContext, surface->sourceShading);
} else {
rv = CAIRO_INT_STATUS_UNSUPPORTED;
}
CGContextRestoreGState (surface->cgContext);
return rv;
}
static cairo_int_status_t
_cairo_quartzgl_surface_show_glyphs (void *abstract_surface,
cairo_operator_t op,
cairo_pattern_t *source,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
cairo_quartzgl_action_t action;
if (num_glyphs <= 0)
return CAIRO_STATUS_SUCCESS;
if (op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
if (!_cairo_scaled_font_is_atsui (scaled_font))
return CAIRO_INT_STATUS_UNSUPPORTED;
CGContextSaveGState (surface->cgContext);
action = _cairo_quartzgl_setup_source (surface, source);
if (action == DO_SOLID || action == DO_PATTERN) {
CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
} else if (action == DO_SHADING) {
CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
} else {
CGContextRestoreGState (surface->cgContext);
return CAIRO_INT_STATUS_UNSUPPORTED;
}
double y_height = surface->extents.height;
#if 0
if (surface->y_grows_down) {
CGContextScaleCTM (surface->cgContext, 1.0, -1.0);
CGContextTranslateCTM (surface->cgContext, 0.0, - y_height);
}
#endif
CGContextSetCompositeOperation (surface->cgContext, _cairo_quartzgl_cairo_operator_to_quartz (op));
ATSUFontID fid = _cairo_atsui_scaled_font_get_atsu_font_id (scaled_font);
ATSFontRef atsfref = FMGetATSFontRefFromFont (fid);
CGFontRef cgfref = CGFontCreateWithPlatformFont (&atsfref);
CGContextSetFont (surface->cgContext, cgfref);
CGFontRelease (cgfref);
CGAffineTransform textTransform;
_cairo_quartzgl_cairo_matrix_to_quartz (&scaled_font->font_matrix, &textTransform);
if (surface->y_grows_down) {
textTransform = CGAffineTransformTranslate (textTransform, glyphs[0].x, y_height - glyphs[0].y);
} else {
textTransform = CGAffineTransformTranslate (textTransform, glyphs[0].x, glyphs[0].y);
textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0);
}
CGContextSetTextMatrix (surface->cgContext, textTransform);
CGContextSetFontSize (surface->cgContext, 1.0);
#if 1
if (surface->y_grows_down) {
int i;
for (i = 0; i < num_glyphs; i++) {
CGGlyph g = glyphs[i].index;
CGContextShowGlyphsAtPoint (surface->cgContext,
glyphs[i].x, y_height - glyphs[i].y,
&g,
1);
}
} else {
int i;
for (i = 0; i < num_glyphs; i++) {
CGGlyph g = glyphs[i].index;
CGContextShowGlyphsAtPoint (surface->cgContext,
glyphs[i].x, glyphs[i].y,
&g,
1);
}
}
#else
CGGlyph *cg_glyphs = (CGGlyph*) malloc(sizeof(CGGlyph) * num_glyphs);
CGSize *cg_advances = (CGSize*) malloc(sizeof(CGSize) * num_glyphs);
double xprev = 0.0, yprev = 0.0;
for (i = 0; i < num_glyphs; i++) {
cg_glyphs[i] = glyphs[i].index;
if (i) {
cg_advances[i-1].width = glyphs[i].x - xprev;
cg_advances[i-1].height = - (glyphs[i].y - yprev);
} else {
cg_advances[i].width = 0;
cg_advances[i].height = 0;
}
xprev = glyphs[i].x;
yprev = glyphs[i].y;
}
cg_advances[num_glyphs-1].width = 0;
cg_advances[num_glyphs-1].height = 0;
for (i = 0; i < num_glyphs; i++) {
}
CGContextShowGlyphsWithAdvances (surface->cgContext,
cg_glyphs,
cg_advances,
num_glyphs);
free (cg_glyphs);
free (cg_advances);
#endif
if (action == DO_SHADING)
CGContextDrawShading (surface->cgContext, surface->sourceShading);
_cairo_quartzgl_teardown_source (surface, source);
CGContextRestoreGState (surface->cgContext);
return rv;
}
static cairo_int_status_t
_cairo_quartzgl_surface_mask (void *abstract_surface,
cairo_operator_t op,
cairo_pattern_t *source,
cairo_pattern_t *mask)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_quartzgl_surface_intersect_clip_path (void *abstract_surface,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface;
if (path == NULL) {
CGContextRestoreGState (surface->cgContext);
CGContextSaveGState (surface->cgContext);
return CAIRO_STATUS_SUCCESS;
}
#if 0
CGContextSetRGBStrokeColor (surface->cgContext, 1.0, 0.0, 0.0, 1.0);
CGContextBeginPath (surface->cgContext);
_cairo_quartzgl_cairo_path_to_quartz_context (path, surface->cgContext);
CGContextStrokePath (surface->cgContext);
#endif
CGContextBeginPath (surface->cgContext);
_cairo_quartzgl_cairo_path_to_quartz_context (path, surface->cgContext);
if (fill_rule == CAIRO_FILL_RULE_WINDING)
CGContextClip (surface->cgContext);
else
CGContextEOClip (surface->cgContext);
return CAIRO_STATUS_SUCCESS;
}
static const struct _cairo_surface_backend cairo_quartzgl_surface_backend = {
CAIRO_SURFACE_TYPE_QUARTZ2,
_cairo_quartzgl_surface_create_similar,
_cairo_quartzgl_surface_finish,
_cairo_quartzgl_surface_acquire_source_image,
NULL,
_cairo_quartzgl_surface_acquire_dest_image,
_cairo_quartzgl_surface_release_dest_image,
_cairo_quartzgl_surface_clone_similar,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
_cairo_quartzgl_surface_intersect_clip_path,
_cairo_quartzgl_surface_get_extents,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
_cairo_quartzgl_surface_paint,
_cairo_quartzgl_surface_mask,
_cairo_quartzgl_surface_stroke,
_cairo_quartzgl_surface_fill,
_cairo_quartzgl_surface_show_glyphs,
NULL,
};
cairo_bool_t
cairo_surface_is_quartzgl (cairo_surface_t *surf)
{
return (surf->backend == &cairo_quartzgl_surface_backend);
}
static cairo_quartzgl_surface_t *
_cairo_quartzgl_surface_create_internal (CGContextRef cgContext,
AGLContext aglContext,
unsigned int width,
unsigned int height,
cairo_bool_t y_grows_down)
{
cairo_quartzgl_surface_t *surface;
surface = malloc(sizeof(cairo_quartzgl_surface_t));
if (surface == NULL) {
_cairo_error (CAIRO_STATUS_NO_MEMORY);
return NULL;
}
memset(surface, 0, sizeof(cairo_quartzgl_surface_t));
_cairo_surface_init(&surface->base, &cairo_quartzgl_surface_backend);
surface->extents.x = surface->extents.y = 0;
surface->extents.width = width;
surface->extents.height = height;
if (y_grows_down) {
CGContextTranslateCTM (cgContext, 0.0, surface->extents.height);
CGContextScaleCTM (cgContext, 1.0, -1.0);
}
CGContextSaveGState (cgContext);
surface->y_grows_down = y_grows_down;
surface->aglContext = aglContext;
surface->cgContext = cgContext;
surface->imageData = NULL;
return surface;
}
cairo_surface_t *
cairo_quartzgl_surface_create_for_agl_context (AGLContext aglContext,
unsigned int width,
unsigned int height,
cairo_bool_t y_grows_down)
{
cairo_quartzgl_surface_t *surf;
CGSize sz;
sz.width = width;
sz.height = height;
CGContextRef cgc = CGGLContextCreate (aglContext, sz, NULL );
fprintf (stderr, "cgc: %p\n", cgc);
surf = _cairo_quartzgl_surface_create_internal (cgc, aglContext, width, height, y_grows_down);
if (!surf) {
CGContextRelease (cgc);
return (cairo_surface_t*) &_cairo_surface_nil;
}
return (cairo_surface_t *) surf;
}
cairo_surface_t *
cairo_quartzgl_surface_create_for_cg_context (CGContextRef cgContext,
unsigned int width,
unsigned int height,
cairo_bool_t y_grows_down)
{
cairo_quartzgl_surface_t *surf;
CGContextRetain (cgContext);
surf = _cairo_quartzgl_surface_create_internal (cgContext, NULL, width, height, y_grows_down);
if (!surf) {
CGContextRelease (cgContext);
return (cairo_surface_t*) &_cairo_surface_nil;
}
return (cairo_surface_t *) surf;
}
cairo_surface_t *
cairo_quartzgl_surface_create (cairo_format_t format,
unsigned int width,
unsigned int height,
cairo_bool_t y_grows_down)
{
cairo_quartzgl_surface_t *surf;
CGContextRef cgc;
CGColorSpaceRef cgColorspace;
CGBitmapInfo bitinfo;
void *imageData;
int stride;
int bitsPerComponent;
if (format == CAIRO_FORMAT_ARGB32) {
cgColorspace = CGColorSpaceCreateDeviceRGB();
stride = width * 4;
bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
bitsPerComponent = 8;
} else if (format == CAIRO_FORMAT_RGB24) {
cgColorspace = CGColorSpaceCreateDeviceRGB();
stride = width * 4;
bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
bitsPerComponent = 8;
} else if (format == CAIRO_FORMAT_A8) {
cgColorspace = CGColorSpaceCreateDeviceGray();
if (width % 4 == 0)
stride = width;
else
stride = (width & 3) + 1;
bitinfo = kCGImageAlphaNone;
bitsPerComponent = 8;
} else if (format == CAIRO_FORMAT_A1) {
_cairo_error (CAIRO_STATUS_INVALID_FORMAT);
return (cairo_surface_t*) &_cairo_surface_nil;
} else {
_cairo_error (CAIRO_STATUS_INVALID_FORMAT);
return (cairo_surface_t*) &_cairo_surface_nil;
}
imageData = malloc (height * stride);
if (!imageData) {
CGColorSpaceRelease (cgColorspace);
_cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_surface_t*) &_cairo_surface_nil;
}
cgc = CGBitmapContextCreate (imageData,
width,
height,
bitsPerComponent,
stride,
cgColorspace,
bitinfo);
CGColorSpaceRelease (cgColorspace);
surf = _cairo_quartzgl_surface_create_internal (cgc, NULL, width, height, y_grows_down);
if (!surf) {
CGContextRelease (cgc);
return (cairo_surface_t*) &_cairo_surface_nil;
}
surf->imageData = imageData;
return (cairo_surface_t *) surf;
}