[cairo-commit] 4 commits - src/cairo-quartz-surface.c test/Makefile.am test/text-antialias-none-quartz-ref.png test/text-antialias-subpixel-quartz-ref.png
Vladimir Vukicevic
vladimir at kemper.freedesktop.org
Sat Mar 8 16:06:50 PST 2008
src/cairo-quartz-surface.c | 267 +++++++++++++++++++++++-----
test/Makefile.am | 2
test/text-antialias-none-quartz-ref.png |binary
test/text-antialias-subpixel-quartz-ref.png |binary
4 files changed, 230 insertions(+), 39 deletions(-)
New commits:
commit 08e38d3f204911a793d3aa24bf2636caf3e58e20
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date: Sat Mar 8 16:06:01 2008 -0800
[quartz] align strides to 16 bytes, as per apple docs
The docs indicate that doing so can yield a peformance win, so we align
ARGB32/RGB24 surface strides to 16 bytes.
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index c6134d7..4fe3f25 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -2164,16 +2164,23 @@ cairo_quartz_surface_create (cairo_format_t format,
width, height);
}
- if (format == CAIRO_FORMAT_ARGB32) {
+ if (format == CAIRO_FORMAT_ARGB32 ||
+ format == CAIRO_FORMAT_RGB24)
+ {
cgColorspace = CGColorSpaceCreateDeviceRGB();
- stride = width * 4;
- bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
+ bitinfo = kCGBitmapByteOrder32Host;
+ if (format == CAIRO_FORMAT_ARGB32)
+ bitinfo |= kCGImageAlphaPremultipliedFirst;
+ else
+ bitinfo |= kCGImageAlphaNoneSkipFirst;
bitsPerComponent = 8;
- } else if (format == CAIRO_FORMAT_RGB24) {
- cgColorspace = CGColorSpaceCreateDeviceRGB();
+
+ /* 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 = width * 4;
- bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
- bitsPerComponent = 8;
+ stride += (16 - (stride & 15)) & 15;
} else if (format == CAIRO_FORMAT_A8) {
cgColorspace = CGColorSpaceCreateDeviceGray();
if (width % 4 == 0)
commit 73444f16fa82b63a19aa938eb42001b5731326da
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date: Sat Mar 8 15:31:15 2008 -0800
[quartz] Don't try to call CGBitmapContextCreateImage on non-bitmap contexts
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 9f57595..c6134d7 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -549,9 +549,11 @@ _cairo_surface_to_cgimage (cairo_surface_t *target,
if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
- image = CGBitmapContextCreateImage (surface->cgContext);
- if (image)
- return image;
+ if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
+ image = CGBitmapContextCreateImage (surface->cgContext);
+ if (image)
+ return image;
+ }
}
if (stype != CAIRO_SURFACE_TYPE_IMAGE) {
commit 091df2c59b6dbd53a748955db359443d5d445ba4
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date: Sat Mar 8 15:16:05 2008 -0800
[quartz] properly honor text antialiasing modes in show_glyphs
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 183347d..9f57595 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -107,6 +107,10 @@ static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL;
/* Only present in 10.5+ */
static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
+static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
+static void (*CGContextSetShouldSmoothFontsPtr) (CGContextRef, bool) = NULL;
+static void (*CGContextGetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
+static void (*CGContextGetShouldSmoothFontsPtr) (CGContextRef, bool) = NULL;
static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
@@ -134,6 +138,10 @@ static void quartz_ensure_symbols(void)
CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask");
CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage");
CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType");
+ CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts");
+ CGContextSetShouldSmoothFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldSmoothFonts");
+ CGContextGetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextGetShouldAntialiasFonts");
+ CGContextGetShouldSmoothFontsPtr = dlsym(RTLD_DEFAULT, "CGContextGetShouldSmoothFonts");
_cairo_quartz_symbol_lookup_done = TRUE;
}
@@ -1614,11 +1622,11 @@ _cairo_quartz_surface_stroke (void *abstract_surface,
#if CAIRO_HAS_ATSUI_FONT
static cairo_int_status_t
_cairo_quartz_surface_show_glyphs (void *abstract_surface,
- cairo_operator_t op,
- cairo_pattern_t *source,
- cairo_glyph_t *glyphs,
- int num_glyphs,
- cairo_scaled_font_t *scaled_font)
+ cairo_operator_t op,
+ cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font)
{
CGAffineTransform textTransform, ctm;
#define STATIC_BUF_SIZE 64
@@ -1669,6 +1677,23 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
CGContextSetFont (surface->cgContext, cgfref);
CGContextSetFontSize (surface->cgContext, 1.0);
+ if (CGContextSetShouldAntialiasFontsPtr) {
+ switch (scaled_font->options.antialias) {
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ CGContextSetShouldAntialiasFontsPtr (surface->cgContext, TRUE);
+ CGContextSetShouldSmoothFontsPtr (surface->cgContext, TRUE);
+ break;
+ case CAIRO_ANTIALIAS_NONE:
+ CGContextSetShouldAntialiasFontsPtr (surface->cgContext, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_GRAY:
+ case CAIRO_ANTIALIAS_DEFAULT:
+ CGContextSetShouldAntialiasFontsPtr (surface->cgContext, TRUE);
+ CGContextSetShouldSmoothFontsPtr (surface->cgContext, FALSE);
+ break;
+ }
+ }
+
if (num_glyphs > STATIC_BUF_SIZE) {
cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph));
if (cg_glyphs == NULL) {
diff --git a/test/Makefile.am b/test/Makefile.am
index 61a5ef2..b457f39 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -570,7 +570,9 @@ REFERENCE_IMAGES = \
text-antialias-gray-ref.png \
text-antialias-gray-quartz-ref.png \
text-antialias-none-ref.png \
+ text-antialias-none-quartz-ref.png \
text-antialias-subpixel-ref.png \
+ text-antialias-subpixel-quartz-ref.png \
text-glyph-range-ref.png \
text-glyph-range-rgb24-ref.png \
text-pattern-ps-argb32-ref.png \
diff --git a/test/text-antialias-none-quartz-ref.png b/test/text-antialias-none-quartz-ref.png
new file mode 100644
index 0000000..32537fc
Binary files /dev/null and b/test/text-antialias-none-quartz-ref.png differ
diff --git a/test/text-antialias-subpixel-quartz-ref.png b/test/text-antialias-subpixel-quartz-ref.png
new file mode 100644
index 0000000..bab8a72
Binary files /dev/null and b/test/text-antialias-subpixel-quartz-ref.png differ
commit 4f7ac14b8fdafd9fa85e797fb6e4c974788f70fc
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date: Sat Mar 8 19:22:13 2008 -0800
[quartz] implement REPEAT/REFLECT for gradients
Implement REPEAT/REFLECT for gradients in Quartz: for linear gradients,
they're implemented natively (by extending the gradient region); for
radial gradients, we generate a fallback image using pixman and render
that.
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index b452549..183347d 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -376,11 +376,25 @@ _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
static void
ComputeGradientValue (void *info, const float *in, float *out)
{
- float fdist = *in; /* 0.0 .. 1.0 */
- cairo_fixed_t fdist_fix = _cairo_fixed_from_double(*in);
+ double fdist = *in;
+ cairo_fixed_t fdist_fix;
cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
unsigned int i;
+ /* Put fdist back in the 0.0..1.0 range if we're doing
+ * REPEAT/REFLECT
+ */
+ if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
+ fdist = fdist - floor(fdist);
+ } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
+ fdist = fmod(fabs(fdist), 2.0);
+ if (fdist > 1.0) {
+ fdist = 2.0 - fdist;
+ }
+ }
+
+ fdist_fix = _cairo_fixed_from_double(fdist);
+
for (i = 0; i < grad->n_stops; i++) {
if (grad->stops[i].x > fdist_fix)
break;
@@ -417,9 +431,9 @@ ComputeGradientValue (void *info, const float *in, float *out)
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 = {
+ float input_value_range[2] = { 0.f, 1.f };
+ float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
+ CGFunctionCallbacks callbacks = {
0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
};
@@ -431,6 +445,84 @@ CreateGradientFunction (cairo_gradient_pattern_t *gpat)
&callbacks);
}
+static CGFunctionRef
+CreateRepeatingGradientFunction (cairo_quartz_surface_t *surface,
+ cairo_gradient_pattern_t *gpat,
+ CGPoint *start, CGPoint *end,
+ CGAffineTransform matrix)
+{
+ float input_value_range[2];
+ float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
+ CGFunctionCallbacks callbacks = {
+ 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
+ };
+
+ CGPoint mstart, mend;
+
+ double dx, dy;
+ int x_rep_start = 0, x_rep_end = 0;
+ int y_rep_start = 0, y_rep_end = 0;
+
+ int rep_start, rep_end;
+
+ // figure out how many times we'd need to repeat the gradient pattern
+ // to cover the whole (transformed) surface area
+ mstart = CGPointApplyAffineTransform (*start, matrix);
+ mend = CGPointApplyAffineTransform (*end, matrix);
+
+ dx = fabs (mend.x - mstart.x);
+ dy = fabs (mend.y - mstart.y);
+
+ if (dx > 1e-6) {
+ x_rep_start = (int) ceil(MIN(mstart.x, mend.x) / dx);
+ x_rep_end = (int) ceil((surface->extents.width - MAX(mstart.x, mend.x)) / dx);
+
+ if (mend.x < mstart.x) {
+ int swap = x_rep_end;
+ x_rep_end = x_rep_start;
+ x_rep_start = swap;
+ }
+ }
+
+ if (dy > 1e-6) {
+ y_rep_start = (int) ceil(MIN(mstart.y, mend.y) / dy);
+ y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y)) / dy);
+
+ if (mend.y < mstart.y) {
+ int swap = y_rep_end;
+ y_rep_end = y_rep_start;
+ y_rep_start = swap;
+ }
+ }
+
+ rep_start = MAX(x_rep_start, y_rep_start);
+ rep_end = MAX(x_rep_end, y_rep_end);
+
+ // extend the line between start and end by rep_start times from the start
+ // and rep_end times from the end
+
+ dx = end->x - start->x;
+ dy = end->y - start->y;
+
+ start->x = start->x - dx * rep_start;
+ start->y = start->y - dy * rep_start;
+
+ end->x = end->x + dx * rep_end;
+ end->y = end->y + dy * rep_end;
+
+ // set the input range for the function -- the function knows how to
+ // map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT.
+ input_value_range[0] = 0.0 - 1.0 * rep_start;
+ input_value_range[1] = 1.0 + 1.0 * rep_end;
+
+ return CGFunctionCreate (gpat,
+ 1,
+ input_value_range,
+ 4,
+ output_value_ranges,
+ &callbacks);
+}
+
/* Obtain a CGImageRef from a #cairo_surface_t * */
static CGImageRef
@@ -638,6 +730,57 @@ typedef enum {
} cairo_quartz_action_t;
static cairo_quartz_action_t
+_cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
+ cairo_pattern_t *source)
+{
+ CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
+ CGAffineTransform ctm;
+ double x0, y0, w, h;
+
+ cairo_surface_t *fallback;
+ cairo_t *fallback_cr;
+ CGImageRef img;
+
+ if (clipBox.size.width == 0.0f ||
+ clipBox.size.height == 0.0f)
+ return DO_NOTHING;
+
+ // the clipBox is in userspace, so:
+ ctm = CGContextGetCTM (surface->cgContext);
+ ctm = CGAffineTransformInvert (ctm);
+ clipBox = CGRectApplyAffineTransform (clipBox, ctm);
+
+ // get the Y flip right -- the CTM will always have a Y flip in place
+ clipBox.origin.y = surface->extents.height - (clipBox.origin.y + clipBox.size.height);
+
+ x0 = floor(clipBox.origin.x);
+ y0 = floor(clipBox.origin.y);
+ w = ceil(clipBox.origin.x + clipBox.size.width) - x0;
+ h = ceil(clipBox.origin.y + clipBox.size.height) - y0;
+
+ /* Create a temporary the size of the clip surface, and position
+ * it so that the device origin coincides with the original surface */
+ fallback = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, (int) w, (int) h);
+ cairo_surface_set_device_offset (fallback, -x0, -y0);
+
+ /* Paint the source onto our temporary */
+ fallback_cr = cairo_create (fallback);
+ cairo_set_operator (fallback_cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source (fallback_cr, source);
+ cairo_paint (fallback_cr);
+ cairo_destroy (fallback_cr);
+
+ img = _cairo_surface_to_cgimage ((cairo_surface_t*) surface, fallback);
+
+ surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
+ surface->sourceImage = img;
+ surface->sourceImageSurface = fallback;
+ surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
+
+ return DO_IMAGE;
+}
+
+static cairo_quartz_action_t
_cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
cairo_linear_pattern_t *lpat)
{
@@ -648,13 +791,6 @@ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
CGColorSpaceRef rgb;
bool extend = abspat->extend == CAIRO_EXTEND_PAD;
- /* bandaid for mozilla bug 379321, also visible in the
- * linear-gradient-reflect test.
- */
- if (abspat->extend == CAIRO_EXTEND_REFLECT ||
- abspat->extend == CAIRO_EXTEND_REPEAT)
- return DO_UNSUPPORTED;
-
if (lpat->base.n_stops == 0) {
CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
@@ -672,12 +808,24 @@ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
_cairo_fixed_to_double (lpat->p2.y));
- cairo_pattern_reference (abspat);
- gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) lpat);
+ // ref will be released by the CGShading's destructor
+ cairo_pattern_reference ((cairo_pattern_t*) lpat);
+
+ if (abspat->extend == CAIRO_EXTEND_NONE ||
+ abspat->extend == CAIRO_EXTEND_PAD)
+ {
+ gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) lpat);
+ } else {
+ gradFunc = CreateRepeatingGradientFunction (surface,
+ (cairo_gradient_pattern_t*) lpat,
+ &start, &end, surface->sourceTransform);
+ }
+
surface->sourceShading = CGShadingCreateAxial (rgb,
start, end,
gradFunc,
extend, extend);
+
CGColorSpaceRelease(rgb);
CGFunctionRelease(gradFunc);
@@ -690,25 +838,28 @@ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
{
cairo_pattern_t *abspat = (cairo_pattern_t *)rpat;
cairo_matrix_t mat;
- CGAffineTransform cgmat;
CGPoint start, end;
CGFunctionRef gradFunc;
- CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
+ CGColorSpaceRef rgb;
bool extend = abspat->extend == CAIRO_EXTEND_PAD;
- /* bandaid for mozilla bug 379321, also visible in the
- * linear-gradient-reflect test.
- */
- if (abspat->extend == CAIRO_EXTEND_REFLECT ||
- abspat->extend == CAIRO_EXTEND_REPEAT)
- return DO_UNSUPPORTED;
-
if (rpat->base.n_stops == 0) {
CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
return DO_SOLID;
}
+ if (abspat->extend == CAIRO_EXTEND_REPEAT ||
+ abspat->extend == CAIRO_EXTEND_REFLECT)
+ {
+ /* I started trying to map these to Quartz, but it's much harder
+ * then the linear case (I think it would involve doing multiple
+ * Radial shadings). So, instead, let's just render an image
+ * for pixman to draw the shading into, and use that.
+ */
+ return _cairo_quartz_setup_fallback_source (surface, (cairo_pattern_t*) rpat);
+ }
+
cairo_pattern_get_matrix (abspat, &mat);
cairo_matrix_invert (&mat);
_cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
@@ -720,8 +871,11 @@ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
end = CGPointMake (_cairo_fixed_to_double (rpat->c2.x),
_cairo_fixed_to_double (rpat->c2.y));
- cairo_pattern_reference (abspat);
+ // ref will be released by the CGShading's destructor
+ cairo_pattern_reference ((cairo_pattern_t*) rpat);
+
gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) rpat);
+
surface->sourceShading = CGShadingCreateRadial (rgb,
start,
_cairo_fixed_to_double (rpat->r1),
@@ -729,6 +883,7 @@ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
_cairo_fixed_to_double (rpat->r2),
gradFunc,
extend, extend);
+
CGColorSpaceRelease(rgb);
CGFunctionRelease(gradFunc);
More information about the cairo-commit
mailing list