From 9347abfc489da41a1b2ab99f6e75924b9e9e3e6f Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 19 Jul 2015 11:37:46 +0200 Subject: [PATCH] quartz-surface: extend and scale the back buffer in case the CGContext it is created from has a device scale set. This fixes pixelated drawing in case cairo_surface_create_similar() is used on a hidpi quartz surface. --- src/cairo-quartz-surface.c | 211 +++++++++++++++++++++++++-------------------- 1 file changed, 118 insertions(+), 93 deletions(-) diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index 1116ff9..3900c4a 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -1568,6 +1568,114 @@ _cairo_quartz_surface_release_source_image (void *abstract_surface, } static cairo_surface_t * +cairo_qcuartz_surface_create_for_similar_cg_context (CGContextRef context, + cairo_format_t format, + unsigned int width, + unsigned int height) +{ + cairo_quartz_surface_t *surf; + CGContextRef cgc; + CGColorSpaceRef cgColorspace; + CGBitmapInfo bitinfo; + void *imageData; + int stride; + int bitsPerComponent; + CGFloat xs = 1.0, ys = 1.0; + unsigned int real_height = height, real_width = width; + + if (context != NULL) { + CGAffineTransform tform = CGContextGetUserSpaceToDeviceSpaceTransform (context); + ys= tform.d; + xs = tform.a; + width *= xs; + height *= ys; + } + + if (!_cairo_quartz_verify_surface_size (width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + + if (width == 0 || height == 0) { + return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format), + width, height)->base; + } + + 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; + } else if (format == CAIRO_FORMAT_A8) { + cgColorspace = NULL; + stride = width; + bitinfo = kCGImageAlphaOnly; + bitsPerComponent = 8; + } else if (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; + + imageData = _cairo_malloc_ab (height, stride); + if (unlikely (!imageData)) { + CGColorSpaceRelease (cgColorspace); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + /* zero the memory to match the image surface behaviour */ + memset (imageData, 0, height * stride); + + cgc = CGBitmapContextCreate (imageData, + width, + height, + bitsPerComponent, + stride, + cgColorspace, + bitinfo); + CGColorSpaceRelease (cgColorspace); + + 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, xs, -ys); + + surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format), + real_width, real_height); + if (surf->base.status) { + CGContextRelease (cgc); + free (imageData); + // create_internal will have set an error + return &surf->base; + } + + surf->base.is_clear = TRUE; + + surf->imageData = imageData; + surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride); + + return &surf->base; +} + +static cairo_surface_t * _cairo_quartz_surface_create_similar (void *abstract_surface, cairo_content_t content, int width, @@ -1592,11 +1700,11 @@ _cairo_quartz_surface_create_similar (void *abstract_surface, (CAIRO_STATUS_INVALID_SIZE)); } - similar = cairo_quartz_surface_create (format, width, height); + surface = (cairo_quartz_surface_t *)abstract_surface; + similar = cairo_qcuartz_surface_create_for_similar_cg_context (surface->cgContext, format, width, height); if (unlikely (similar->status)) return similar; - surface = (cairo_quartz_surface_t *) abstract_surface; similar_quartz = (cairo_quartz_surface_t *) similar; similar_quartz->virtual_extents = surface->virtual_extents; @@ -1648,6 +1756,7 @@ _cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents, CGAffineTransform mask_matrix; cairo_quartz_drawing_state_t state; cairo_format_t format = _cairo_format_from_content (extents->surface->content); + cairo_quartz_surface_t *source = (cairo_quartz_surface_t *)mask_surf; cairo_rectangle_int_t dest_extents; cairo_matrix_t m = *mask_mat; @@ -1661,7 +1770,12 @@ _cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents, if (unlikely (status)) goto BAIL; - rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img)); + /* In case of a quartz surface depend on the extends as the content could + * be a scaled back buffer (CGBitmap) */ + if(_cairo_surface_is_quartz(mask_surf)) + rect = CGRectMake (0.0, 0.0, source->extents.width, source->extents.height); + else + rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img)); _cairo_quartz_cairo_matrix_to_quartz (&m, &mask_matrix); /* ClipToMask is essentially drawing an image, so we need to flip the CTM @@ -2342,96 +2456,7 @@ cairo_quartz_surface_create (cairo_format_t format, unsigned int width, unsigned int 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 (width == 0 || height == 0) { - return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format), - width, height)->base; - } - - 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; - } else if (format == CAIRO_FORMAT_A8) { - cgColorspace = NULL; - stride = width; - bitinfo = kCGImageAlphaOnly; - bitsPerComponent = 8; - } else if (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; - - imageData = _cairo_malloc_ab (height, stride); - if (unlikely (!imageData)) { - CGColorSpaceRelease (cgColorspace); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - /* zero the memory to match the image surface behaviour */ - memset (imageData, 0, height * stride); - - cgc = CGBitmapContextCreate (imageData, - width, - height, - bitsPerComponent, - stride, - cgColorspace, - bitinfo); - CGColorSpaceRelease (cgColorspace); - - 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; - } - - surf->base.is_clear = TRUE; - - surf->imageData = imageData; - surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride); - - return &surf->base; + return cairo_qcuartz_surface_create_for_similar_cg_context (NULL, format, width, height); } /** -- 2.1.4