[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