2007-02-20 12:15:35 -08:00
|
|
|
|
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
|
2005-01-16 08:35:14 +00:00
|
|
|
|
/* cairo - a vector graphics library with display and print output
|
|
|
|
|
|
*
|
2007-12-06 21:31:14 +00:00
|
|
|
|
* Copyright <EFBFBD> 2006, 2007 Mozilla Corporation
|
2005-01-16 08:35:14 +00:00
|
|
|
|
*
|
|
|
|
|
|
* 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
|
2010-04-27 10:17:23 +02:00
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
|
2005-01-16 08:35:14 +00:00
|
|
|
|
* 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.
|
|
|
|
|
|
*
|
2010-05-06 16:07:43 -04:00
|
|
|
|
* The Initial Developer of the Original Code is Mozilla Foundation.
|
2005-01-16 08:35:14 +00:00
|
|
|
|
*
|
|
|
|
|
|
* Contributor(s):
|
2007-02-20 12:15:35 -08:00
|
|
|
|
* Vladimir Vukicevic <vladimir@mozilla.com>
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2009-01-03 21:50:55 +00:00
|
|
|
|
#define _GNU_SOURCE /* required for RTLD_DEFAULT */
|
2005-01-16 08:35:14 +00:00
|
|
|
|
#include "cairoint.h"
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2006-01-10 05:28:59 +00:00
|
|
|
|
#include "cairo-quartz-private.h"
|
2010-01-18 16:58:40 +00:00
|
|
|
|
|
|
|
|
|
|
#include "cairo-error-private.h"
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
#include "cairo-surface-clipper-private.h"
|
2005-01-16 08:35:14 +00:00
|
|
|
|
|
2008-02-26 13:04:33 +00:00
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
|
|
2009-01-03 21:50:55 +00:00
|
|
|
|
#ifndef RTLD_DEFAULT
|
|
|
|
|
|
#define RTLD_DEFAULT ((void *) 0)
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2007-11-06 15:40:30 -08:00
|
|
|
|
#include <limits.h>
|
2007-04-03 20:25:30 -04:00
|
|
|
|
|
2007-02-20 12:54:03 -08:00
|
|
|
|
#undef QUARTZ_DEBUG
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2007-02-20 12:54:03 -08:00
|
|
|
|
#ifdef QUARTZ_DEBUG
|
2007-02-20 12:15:35 -08:00
|
|
|
|
#define ND(_x) fprintf _x
|
|
|
|
|
|
#else
|
|
|
|
|
|
#define ND(_x) do {} while(0)
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2007-12-04 13:49:59 -08:00
|
|
|
|
#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
|
|
|
|
|
|
|
2010-07-08 13:05:18 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/* 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))
|
|
|
|
|
|
};
|
|
|
|
|
|
typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
|
|
|
|
|
|
CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
|
|
|
|
|
|
CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform);
|
|
|
|
|
|
|
|
|
|
|
|
/* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9
|
|
|
|
|
|
* has all the stuff we care about, just some of it isn't exported in the SDK.
|
2007-12-04 13:54:32 -08:00
|
|
|
|
*/
|
2007-02-20 12:15:35 -08:00
|
|
|
|
#ifndef kCGBitmapByteOrder32Host
|
|
|
|
|
|
#define USE_10_3_WORKAROUNDS
|
|
|
|
|
|
#define kCGBitmapAlphaInfoMask 0x1F
|
|
|
|
|
|
#define kCGBitmapByteOrderMask 0x7000
|
|
|
|
|
|
#define kCGBitmapByteOrder32Host 0
|
|
|
|
|
|
|
|
|
|
|
|
typedef uint32_t CGBitmapInfo;
|
|
|
|
|
|
|
|
|
|
|
|
/* public in 10.4, present in 10.3.9 */
|
|
|
|
|
|
CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef);
|
|
|
|
|
|
CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2008-04-01 12:44:32 -07:00
|
|
|
|
/* Some of these are present in earlier versions of the OS than where
|
|
|
|
|
|
* they are public; others are not public at all (CGContextCopyPath,
|
|
|
|
|
|
* CGContextReplacePathWithClipPath, many of the getters, etc.)
|
|
|
|
|
|
*/
|
2008-01-24 11:48:02 -08:00
|
|
|
|
static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL;
|
|
|
|
|
|
static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
|
2008-03-03 17:40:21 -08:00
|
|
|
|
static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
|
2008-03-08 15:16:05 -08:00
|
|
|
|
static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
|
2008-03-12 18:19:59 -07:00
|
|
|
|
static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
|
|
|
|
|
|
static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
|
2008-03-12 18:11:57 -07:00
|
|
|
|
static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-01-24 11:48:02 -08:00
|
|
|
|
static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
|
2007-12-04 13:53:03 -08:00
|
|
|
|
|
2007-02-27 20:03:26 -05:00
|
|
|
|
/*
|
|
|
|
|
|
* Utility functions
|
|
|
|
|
|
*/
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-03-07 14:49:15 -08:00
|
|
|
|
#ifdef QUARTZ_DEBUG
|
2007-03-06 23:24:33 +00:00
|
|
|
|
static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
|
|
|
|
|
|
static void quartz_image_to_png (CGImageRef, char *dest);
|
2008-03-07 14:49:15 -08:00
|
|
|
|
#endif
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2007-12-04 13:49:59 -08:00
|
|
|
|
static cairo_quartz_surface_t *
|
|
|
|
|
|
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
|
|
|
|
|
|
cairo_content_t content,
|
|
|
|
|
|
unsigned int width,
|
|
|
|
|
|
unsigned int height);
|
|
|
|
|
|
|
2010-07-27 18:04:36 +02:00
|
|
|
|
static cairo_bool_t
|
|
|
|
|
|
_cairo_surface_is_quartz (const cairo_surface_t *surface);
|
|
|
|
|
|
|
2008-01-24 11:48:02 -08:00
|
|
|
|
/* Load all extra symbols */
|
2010-07-27 15:59:31 +02:00
|
|
|
|
static void quartz_ensure_symbols (void)
|
2008-01-24 11:48:02 -08:00
|
|
|
|
{
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (likely (_cairo_quartz_symbol_lookup_done))
|
2008-01-24 11:48:02 -08:00
|
|
|
|
return;
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CGContextClipToMaskPtr = dlsym (RTLD_DEFAULT, "CGContextClipToMask");
|
|
|
|
|
|
CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage");
|
|
|
|
|
|
CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
|
|
|
|
|
|
CGContextSetShouldAntialiasFontsPtr = dlsym (RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts");
|
|
|
|
|
|
CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
|
|
|
|
|
|
CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
|
|
|
|
|
|
CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
|
2008-01-24 11:48:02 -08:00
|
|
|
|
|
|
|
|
|
|
_cairo_quartz_symbol_lookup_done = TRUE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-09-03 15:34:40 -07:00
|
|
|
|
CGImageRef
|
|
|
|
|
|
_cairo_quartz_create_cgimage (cairo_format_t format,
|
|
|
|
|
|
unsigned int width,
|
|
|
|
|
|
unsigned int height,
|
|
|
|
|
|
unsigned int stride,
|
|
|
|
|
|
void *data,
|
|
|
|
|
|
cairo_bool_t interpolate,
|
|
|
|
|
|
CGColorSpaceRef colorSpaceOverride,
|
|
|
|
|
|
CGDataProviderReleaseDataCallback releaseCallback,
|
|
|
|
|
|
void *releaseInfo)
|
|
|
|
|
|
{
|
|
|
|
|
|
CGImageRef image = NULL;
|
|
|
|
|
|
CGDataProviderRef dataProvider = NULL;
|
|
|
|
|
|
CGColorSpaceRef colorSpace = colorSpaceOverride;
|
2010-01-25 15:09:51 +01:00
|
|
|
|
CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host;
|
2008-09-03 15:34:40 -07:00
|
|
|
|
int bitsPerComponent, bitsPerPixel;
|
|
|
|
|
|
|
|
|
|
|
|
switch (format) {
|
|
|
|
|
|
case CAIRO_FORMAT_ARGB32:
|
|
|
|
|
|
if (colorSpace == NULL)
|
2010-07-27 15:59:31 +02:00
|
|
|
|
colorSpace = CGColorSpaceCreateDeviceRGB ();
|
2010-01-25 15:09:51 +01:00
|
|
|
|
bitinfo |= kCGImageAlphaPremultipliedFirst;
|
2008-09-03 15:34:40 -07:00
|
|
|
|
bitsPerComponent = 8;
|
|
|
|
|
|
bitsPerPixel = 32;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_FORMAT_RGB24:
|
|
|
|
|
|
if (colorSpace == NULL)
|
2010-07-27 15:59:31 +02:00
|
|
|
|
colorSpace = CGColorSpaceCreateDeviceRGB ();
|
2010-01-25 15:09:51 +01:00
|
|
|
|
bitinfo |= kCGImageAlphaNoneSkipFirst;
|
2008-09-03 15:34:40 -07:00
|
|
|
|
bitsPerComponent = 8;
|
|
|
|
|
|
bitsPerPixel = 32;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_FORMAT_A8:
|
|
|
|
|
|
bitsPerComponent = 8;
|
|
|
|
|
|
bitsPerPixel = 8;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_FORMAT_A1:
|
2010-05-14 19:33:15 +02:00
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
2010-01-02 13:23:53 +01:00
|
|
|
|
bitsPerComponent = 1;
|
|
|
|
|
|
bitsPerPixel = 1;
|
|
|
|
|
|
break;
|
2010-05-14 19:33:15 +02:00
|
|
|
|
#endif
|
2010-01-02 13:23:53 +01:00
|
|
|
|
|
2010-05-14 21:03:19 +02:00
|
|
|
|
case CAIRO_FORMAT_RGB16_565:
|
|
|
|
|
|
case CAIRO_FORMAT_INVALID:
|
2008-09-03 15:34:40 -07:00
|
|
|
|
default:
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dataProvider = CGDataProviderCreateWithData (releaseInfo,
|
|
|
|
|
|
data,
|
|
|
|
|
|
height * stride,
|
|
|
|
|
|
releaseCallback);
|
|
|
|
|
|
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (!dataProvider)) {
|
2008-09-03 15:34:40 -07:00
|
|
|
|
// manually release
|
|
|
|
|
|
if (releaseCallback)
|
|
|
|
|
|
releaseCallback (releaseInfo, data, height * stride);
|
|
|
|
|
|
goto FINISH;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-02 13:23:53 +01:00
|
|
|
|
if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) {
|
2009-12-29 08:47:09 +01:00
|
|
|
|
cairo_quartz_float_t decode[] = {1.0, 0.0};
|
2010-01-02 13:23:53 +01:00
|
|
|
|
image = CGImageMaskCreate (width, height,
|
|
|
|
|
|
bitsPerComponent,
|
|
|
|
|
|
bitsPerPixel,
|
|
|
|
|
|
stride,
|
|
|
|
|
|
dataProvider,
|
|
|
|
|
|
decode,
|
|
|
|
|
|
interpolate);
|
|
|
|
|
|
} else
|
|
|
|
|
|
image = CGImageCreate (width, height,
|
|
|
|
|
|
bitsPerComponent,
|
|
|
|
|
|
bitsPerPixel,
|
|
|
|
|
|
stride,
|
|
|
|
|
|
colorSpace,
|
|
|
|
|
|
bitinfo,
|
|
|
|
|
|
dataProvider,
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
interpolate,
|
|
|
|
|
|
kCGRenderingIntentDefault);
|
2008-09-03 15:34:40 -07:00
|
|
|
|
|
|
|
|
|
|
FINISH:
|
|
|
|
|
|
|
|
|
|
|
|
CGDataProviderRelease (dataProvider);
|
|
|
|
|
|
|
|
|
|
|
|
if (colorSpace != colorSpaceOverride)
|
|
|
|
|
|
CGColorSpaceRelease (colorSpace);
|
|
|
|
|
|
|
|
|
|
|
|
return image;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-03-03 17:40:21 -08:00
|
|
|
|
static inline cairo_bool_t
|
2010-07-27 16:51:30 +02:00
|
|
|
|
_cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (unlikely (cgc == NULL))
|
2008-03-17 17:37:22 -07:00
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (likely (CGContextGetTypePtr)) {
|
2008-03-03 17:40:21 -08:00
|
|
|
|
/* 4 is the type value of a bitmap context */
|
2010-07-27 16:51:30 +02:00
|
|
|
|
return CGContextGetTypePtr (cgc) == 4;
|
2008-03-03 17:40:21 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
|
2010-07-27 15:59:31 +02:00
|
|
|
|
return CGBitmapContextGetBitsPerPixel (cgc) != 0;
|
2008-03-03 17:40:21 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-11-06 15:40:30 -08:00
|
|
|
|
/* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
|
|
|
|
|
|
|
|
|
|
|
|
#define CG_MAX_HEIGHT SHRT_MAX
|
|
|
|
|
|
#define CG_MAX_WIDTH USHRT_MAX
|
|
|
|
|
|
|
|
|
|
|
|
/* is the desired size of the surface within bounds? */
|
2008-02-25 21:06:21 -05:00
|
|
|
|
cairo_bool_t
|
2010-07-27 15:59:31 +02:00
|
|
|
|
_cairo_quartz_verify_surface_size (int width, int height)
|
2007-11-06 15:40:30 -08:00
|
|
|
|
{
|
|
|
|
|
|
/* hmmm, allow width, height == 0 ? */
|
2010-07-29 16:05:51 +02:00
|
|
|
|
if (width < 0 || height < 0)
|
2007-11-06 15:40:30 -08:00
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
2010-07-29 16:05:51 +02:00
|
|
|
|
if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT)
|
2007-11-06 15:40:30 -08:00
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/*
|
|
|
|
|
|
* Cairo path -> Quartz path conversion helpers
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* cairo path -> execute in context */
|
|
|
|
|
|
static cairo_status_t
|
2008-12-09 20:44:25 +00:00
|
|
|
|
_cairo_path_to_quartz_context_move_to (void *closure,
|
|
|
|
|
|
const cairo_point_t *point)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2010-07-27 15:59:31 +02:00
|
|
|
|
//ND ((stderr, "moveto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
|
2007-04-01 00:04:24 +01:00
|
|
|
|
double x = _cairo_fixed_to_double (point->x);
|
|
|
|
|
|
double y = _cairo_fixed_to_double (point->y);
|
|
|
|
|
|
|
2010-04-04 11:50:41 +02:00
|
|
|
|
CGContextMoveToPoint (closure, x, y);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static cairo_status_t
|
2008-12-09 20:44:25 +00:00
|
|
|
|
_cairo_path_to_quartz_context_line_to (void *closure,
|
|
|
|
|
|
const cairo_point_t *point)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2010-07-27 15:59:31 +02:00
|
|
|
|
//ND ((stderr, "lineto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
|
2007-04-01 00:04:24 +01:00
|
|
|
|
double x = _cairo_fixed_to_double (point->x);
|
|
|
|
|
|
double y = _cairo_fixed_to_double (point->y);
|
2007-12-04 13:54:32 -08:00
|
|
|
|
|
2010-04-04 11:50:41 +02:00
|
|
|
|
CGContextAddLineToPoint (closure, x, y);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static cairo_status_t
|
2008-12-09 20:44:25 +00:00
|
|
|
|
_cairo_path_to_quartz_context_curve_to (void *closure,
|
|
|
|
|
|
const cairo_point_t *p0,
|
|
|
|
|
|
const cairo_point_t *p1,
|
|
|
|
|
|
const cairo_point_t *p2)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2010-07-27 15:59:31 +02:00
|
|
|
|
//ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n",
|
|
|
|
|
|
// _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)));
|
2007-04-01 00:04:24 +01:00
|
|
|
|
double x0 = _cairo_fixed_to_double (p0->x);
|
|
|
|
|
|
double y0 = _cairo_fixed_to_double (p0->y);
|
|
|
|
|
|
double x1 = _cairo_fixed_to_double (p1->x);
|
|
|
|
|
|
double y1 = _cairo_fixed_to_double (p1->y);
|
|
|
|
|
|
double x2 = _cairo_fixed_to_double (p2->x);
|
|
|
|
|
|
double y2 = _cairo_fixed_to_double (p2->y);
|
|
|
|
|
|
|
2010-07-27 17:24:08 +02:00
|
|
|
|
CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static cairo_status_t
|
|
|
|
|
|
_cairo_path_to_quartz_context_close_path (void *closure)
|
|
|
|
|
|
{
|
2010-07-27 15:59:31 +02:00
|
|
|
|
//ND ((stderr, "closepath\n"));
|
2010-04-04 11:50:41 +02:00
|
|
|
|
CGContextClosePath (closure);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-04-27 10:59:09 +02:00
|
|
|
|
static void
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path,
|
2010-04-04 11:50:41 +02:00
|
|
|
|
CGContextRef closure)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2010-04-27 10:59:09 +02:00
|
|
|
|
cairo_status_t status;
|
|
|
|
|
|
|
2010-04-04 11:50:41 +02:00
|
|
|
|
CGContextBeginPath (closure);
|
2010-04-27 10:59:09 +02:00
|
|
|
|
status = _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,
|
|
|
|
|
|
closure);
|
|
|
|
|
|
|
|
|
|
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Misc helpers/callbacks
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static PrivateCGCompositeMode
|
2009-11-09 21:38:00 +01:00
|
|
|
|
_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
|
|
|
|
|
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:
|
2009-11-09 21:38:00 +01:00
|
|
|
|
return kPrivateCGCompositeXOR;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
case CAIRO_OPERATOR_ADD:
|
|
|
|
|
|
return kPrivateCGCompositePlusLighter;
|
2009-11-09 21:38:00 +01:00
|
|
|
|
|
|
|
|
|
|
case CAIRO_OPERATOR_DEST:
|
2007-02-20 12:15:35 -08:00
|
|
|
|
case CAIRO_OPERATOR_SATURATE:
|
2009-11-09 21:38:00 +01:00
|
|
|
|
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 (0);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
2009-11-09 21:38:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
|
|
|
|
|
|
{
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
|
2009-11-09 21:38:00 +01:00
|
|
|
|
|
|
|
|
|
|
if (surface->base.content == CAIRO_CONTENT_ALPHA) {
|
|
|
|
|
|
/* For some weird reason, some compositing operators are
|
|
|
|
|
|
swapped when operating on masks */
|
|
|
|
|
|
switch (op) {
|
|
|
|
|
|
case CAIRO_OPERATOR_CLEAR:
|
|
|
|
|
|
case CAIRO_OPERATOR_SOURCE:
|
|
|
|
|
|
case CAIRO_OPERATOR_OVER:
|
|
|
|
|
|
case CAIRO_OPERATOR_DEST_IN:
|
|
|
|
|
|
case CAIRO_OPERATOR_DEST_OUT:
|
|
|
|
|
|
case CAIRO_OPERATOR_ADD:
|
|
|
|
|
|
CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op));
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2009-11-09 21:38:00 +01:00
|
|
|
|
case CAIRO_OPERATOR_IN:
|
|
|
|
|
|
CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationAtop);
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_OPERATOR_DEST_OVER:
|
|
|
|
|
|
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:
|
|
|
|
|
|
CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceOver);
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_OPERATOR_DEST_ATOP:
|
|
|
|
|
|
CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceIn);
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_OPERATOR_SATURATE:
|
|
|
|
|
|
CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositePlusLighter);
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_OPERATOR_ATOP:
|
|
|
|
|
|
/*
|
|
|
|
|
|
CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationOver);
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
*/
|
|
|
|
|
|
case CAIRO_OPERATOR_DEST:
|
|
|
|
|
|
return CAIRO_INT_STATUS_NOTHING_TO_DO;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_OPERATOR_OUT:
|
|
|
|
|
|
case CAIRO_OPERATOR_XOR:
|
|
|
|
|
|
default:
|
|
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
switch (op) {
|
|
|
|
|
|
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:
|
|
|
|
|
|
CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op));
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2009-11-09 21:38:00 +01:00
|
|
|
|
case CAIRO_OPERATOR_DEST:
|
|
|
|
|
|
return CAIRO_INT_STATUS_NOTHING_TO_DO;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_OPERATOR_SATURATE:
|
|
|
|
|
|
/* TODO: the following are mostly supported by CGContextSetBlendMode*/
|
|
|
|
|
|
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:
|
|
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
static inline CGLineCap
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
|
|
|
|
|
switch (ccap) {
|
2010-07-27 14:43:15 +02:00
|
|
|
|
default:
|
|
|
|
|
|
ASSERT_NOT_REACHED;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_LINE_CAP_BUTT:
|
|
|
|
|
|
return kCGLineCapButt;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_LINE_CAP_ROUND:
|
|
|
|
|
|
return kCGLineCapRound;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 14:43:15 +02:00
|
|
|
|
case CAIRO_LINE_CAP_SQUARE:
|
|
|
|
|
|
return kCGLineCapSquare;
|
|
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
static inline CGLineJoin
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
|
|
|
|
|
switch (cjoin) {
|
2010-07-27 14:43:15 +02:00
|
|
|
|
default:
|
|
|
|
|
|
ASSERT_NOT_REACHED;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 14:43:15 +02:00
|
|
|
|
case CAIRO_LINE_JOIN_MITER:
|
|
|
|
|
|
return kCGLineJoinMiter;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_LINE_JOIN_ROUND:
|
|
|
|
|
|
return kCGLineJoinRound;
|
|
|
|
|
|
|
|
|
|
|
|
case CAIRO_LINE_JOIN_BEVEL:
|
|
|
|
|
|
return kCGLineJoinBevel;
|
|
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
static inline CGInterpolationQuality
|
|
|
|
|
|
_cairo_quartz_filter_to_quartz (cairo_filter_t filter)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (filter) {
|
2010-07-27 14:43:15 +02:00
|
|
|
|
case CAIRO_FILTER_NEAREST:
|
|
|
|
|
|
return kCGInterpolationNone;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2010-07-27 14:43:15 +02:00
|
|
|
|
case CAIRO_FILTER_FAST:
|
|
|
|
|
|
return kCGInterpolationLow;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2010-07-27 14:43:15 +02:00
|
|
|
|
case CAIRO_FILTER_BEST:
|
|
|
|
|
|
case CAIRO_FILTER_GOOD:
|
|
|
|
|
|
case CAIRO_FILTER_BILINEAR:
|
|
|
|
|
|
case CAIRO_FILTER_GAUSSIAN:
|
|
|
|
|
|
return kCGInterpolationDefault;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2010-07-27 14:43:15 +02:00
|
|
|
|
default:
|
|
|
|
|
|
ASSERT_NOT_REACHED;
|
|
|
|
|
|
return kCGInterpolationDefault;
|
|
|
|
|
|
}
|
2008-02-25 21:06:21 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline void
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
|
2008-02-25 21:06:25 -05:00
|
|
|
|
CGAffineTransform *dst)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
|
|
|
|
|
dst->a = src->xx;
|
2007-03-25 23:31:40 +01:00
|
|
|
|
dst->b = src->yx;
|
|
|
|
|
|
dst->c = src->xy;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
dst->d = src->yy;
|
|
|
|
|
|
dst->tx = src->x0;
|
|
|
|
|
|
dst->ty = src->y0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-03-12 18:11:57 -07:00
|
|
|
|
typedef struct {
|
|
|
|
|
|
bool isClipping;
|
|
|
|
|
|
CGGlyph *cg_glyphs;
|
|
|
|
|
|
CGSize *cg_advances;
|
|
|
|
|
|
size_t nglyphs;
|
|
|
|
|
|
CGAffineTransform textTransform;
|
|
|
|
|
|
CGFontRef font;
|
|
|
|
|
|
CGPoint origin;
|
|
|
|
|
|
} unbounded_show_glyphs_t;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
|
CGPathRef cgPath;
|
|
|
|
|
|
cairo_fill_rule_t fill_rule;
|
|
|
|
|
|
} unbounded_stroke_fill_t;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
|
CGImageRef mask;
|
|
|
|
|
|
CGAffineTransform maskTransform;
|
|
|
|
|
|
} unbounded_mask_t;
|
|
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
|
UNBOUNDED_STROKE_FILL,
|
|
|
|
|
|
UNBOUNDED_SHOW_GLYPHS,
|
|
|
|
|
|
UNBOUNDED_MASK
|
|
|
|
|
|
} unbounded_op_t;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
|
unbounded_op_t op;
|
|
|
|
|
|
union {
|
|
|
|
|
|
unbounded_stroke_fill_t stroke_fill;
|
|
|
|
|
|
unbounded_show_glyphs_t show_glyphs;
|
|
|
|
|
|
unbounded_mask_t mask;
|
|
|
|
|
|
} u;
|
|
|
|
|
|
} unbounded_op_data_t;
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
_cairo_quartz_fixup_unbounded_operation (cairo_quartz_surface_t *surface,
|
|
|
|
|
|
unbounded_op_data_t *op,
|
|
|
|
|
|
cairo_antialias_t antialias)
|
|
|
|
|
|
{
|
|
|
|
|
|
CGRect clipBox, clipBoxRound;
|
|
|
|
|
|
CGContextRef cgc;
|
|
|
|
|
|
CGImageRef maskImage;
|
|
|
|
|
|
|
2010-01-06 18:53:54 +01:00
|
|
|
|
/* TODO: handle failure */
|
2008-03-12 18:11:57 -07:00
|
|
|
|
if (!CGContextClipToMaskPtr)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
clipBox = CGContextGetClipBoundingBox (surface->cgContext);
|
|
|
|
|
|
clipBoxRound = CGRectIntegral (clipBox);
|
|
|
|
|
|
|
|
|
|
|
|
cgc = CGBitmapContextCreate (NULL,
|
|
|
|
|
|
clipBoxRound.size.width,
|
|
|
|
|
|
clipBoxRound.size.height,
|
|
|
|
|
|
8,
|
2009-10-19 18:38:57 +02:00
|
|
|
|
(((size_t) clipBoxRound.size.width) + 15) & (~15),
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
kCGImageAlphaOnly);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
|
|
|
|
|
|
if (!cgc)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2009-10-19 18:38:57 +02:00
|
|
|
|
CGContextSetCompositeOperation (cgc, kPrivateCGCompositeCopy);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
/* We want to mask out whatever we just rendered, so we fill the
|
2009-10-19 18:38:57 +02:00
|
|
|
|
* surface opaque, and then we'll render transparent.
|
2008-03-12 18:11:57 -07:00
|
|
|
|
*/
|
2009-10-19 18:38:57 +02:00
|
|
|
|
CGContextSetAlpha (cgc, 1.0f);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGContextFillRect (cgc, CGRectMake (0, 0, clipBoxRound.size.width, clipBoxRound.size.height));
|
|
|
|
|
|
|
2010-01-06 18:53:54 +01:00
|
|
|
|
CGContextSetCompositeOperation (cgc, kPrivateCGCompositeClear);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGContextSetShouldAntialias (cgc, (antialias != CAIRO_ANTIALIAS_NONE));
|
|
|
|
|
|
|
|
|
|
|
|
CGContextTranslateCTM (cgc, -clipBoxRound.origin.x, -clipBoxRound.origin.y);
|
|
|
|
|
|
|
|
|
|
|
|
/* We need to either render the path that was given to us, or the glyph op */
|
|
|
|
|
|
if (op->op == UNBOUNDED_STROKE_FILL) {
|
|
|
|
|
|
CGContextBeginPath (cgc);
|
|
|
|
|
|
CGContextAddPath (cgc, op->u.stroke_fill.cgPath);
|
|
|
|
|
|
|
|
|
|
|
|
if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING)
|
|
|
|
|
|
CGContextFillPath (cgc);
|
|
|
|
|
|
else
|
|
|
|
|
|
CGContextEOFillPath (cgc);
|
|
|
|
|
|
} else if (op->op == UNBOUNDED_SHOW_GLYPHS) {
|
|
|
|
|
|
CGContextSetFont (cgc, op->u.show_glyphs.font);
|
|
|
|
|
|
CGContextSetFontSize (cgc, 1.0);
|
2010-06-24 15:26:03 +02:00
|
|
|
|
CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
|
2010-06-24 15:26:03 +02:00
|
|
|
|
CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
|
|
|
|
|
|
if (op->u.show_glyphs.isClipping) {
|
2008-04-01 12:44:32 -07:00
|
|
|
|
/* Note that the comment in show_glyphs about kCGTextClip
|
|
|
|
|
|
* and the text transform still applies here; however, the
|
|
|
|
|
|
* cg_advances we have were already transformed, so we
|
|
|
|
|
|
* don't have to do anything. */
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGContextSetTextDrawingMode (cgc, kCGTextClip);
|
|
|
|
|
|
CGContextSaveGState (cgc);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CGContextShowGlyphsWithAdvances (cgc,
|
|
|
|
|
|
op->u.show_glyphs.cg_glyphs,
|
|
|
|
|
|
op->u.show_glyphs.cg_advances,
|
|
|
|
|
|
op->u.show_glyphs.nglyphs);
|
|
|
|
|
|
|
|
|
|
|
|
if (op->u.show_glyphs.isClipping) {
|
2010-01-06 18:53:54 +01:00
|
|
|
|
CGContextClearRect (cgc, clipBoxRound);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGContextRestoreGState (cgc);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (op->op == UNBOUNDED_MASK) {
|
2010-01-06 18:53:54 +01:00
|
|
|
|
CGAffineTransform ctm = CGContextGetCTM (cgc);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGContextSaveGState (cgc);
|
|
|
|
|
|
CGContextConcatCTM (cgc, op->u.mask.maskTransform);
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CGContextClipToMask (cgc,
|
|
|
|
|
|
CGRectMake (0.0,
|
|
|
|
|
|
0.0,
|
|
|
|
|
|
CGImageGetWidth (op->u.mask.mask),
|
|
|
|
|
|
CGImageGetHeight (op->u.mask.mask)),
|
2008-03-12 18:11:57 -07:00
|
|
|
|
op->u.mask.mask);
|
2010-01-06 18:53:54 +01:00
|
|
|
|
CGContextSetCTM (cgc, ctm);
|
|
|
|
|
|
CGContextClearRect (cgc, clipBoxRound);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGContextRestoreGState (cgc);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Also mask out the portion of the clipbox that we rounded out, if any */
|
|
|
|
|
|
if (!CGRectEqualToRect (clipBox, clipBoxRound)) {
|
|
|
|
|
|
CGContextBeginPath (cgc);
|
2010-01-06 18:53:54 +01:00
|
|
|
|
CGContextAddRect (cgc, clipBoxRound);
|
|
|
|
|
|
CGContextAddRect (cgc, clipBox);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGContextEOFillPath (cgc);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
maskImage = CGBitmapContextCreateImage (cgc);
|
|
|
|
|
|
CGContextRelease (cgc);
|
|
|
|
|
|
|
|
|
|
|
|
if (!maskImage)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
/* Then render with the mask */
|
|
|
|
|
|
CGContextSaveGState (surface->cgContext);
|
|
|
|
|
|
|
|
|
|
|
|
CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy);
|
|
|
|
|
|
CGContextClipToMaskPtr (surface->cgContext, clipBoxRound, maskImage);
|
|
|
|
|
|
CGImageRelease (maskImage);
|
|
|
|
|
|
|
|
|
|
|
|
/* Finally, clear out the entire clipping region through our mask */
|
|
|
|
|
|
CGContextClearRect (surface->cgContext, clipBoxRound);
|
|
|
|
|
|
|
|
|
|
|
|
CGContextRestoreGState (surface->cgContext);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-27 20:03:26 -05:00
|
|
|
|
/*
|
|
|
|
|
|
* Source -> Quartz setup and finish functions
|
|
|
|
|
|
*/
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
static void
|
2009-12-29 08:47:09 +01:00
|
|
|
|
ComputeGradientValue (void *info,
|
|
|
|
|
|
const cairo_quartz_float_t *in,
|
|
|
|
|
|
cairo_quartz_float_t *out)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2008-03-08 19:22:13 -08:00
|
|
|
|
double fdist = *in;
|
2008-11-11 08:31:23 -05:00
|
|
|
|
const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
2008-03-08 19:22:13 -08:00
|
|
|
|
/* Put fdist back in the 0.0..1.0 range if we're doing
|
|
|
|
|
|
* REPEAT/REFLECT
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
|
2010-07-27 15:59:31 +02:00
|
|
|
|
fdist = fdist - floor (fdist);
|
2008-03-08 19:22:13 -08:00
|
|
|
|
} else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
|
2010-07-27 15:59:31 +02:00
|
|
|
|
fdist = fmod (fabs (fdist), 2.0);
|
2010-07-29 16:05:51 +02:00
|
|
|
|
if (fdist > 1.0)
|
2008-03-08 19:22:13 -08:00
|
|
|
|
fdist = 2.0 - fdist;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-07-29 16:05:51 +02:00
|
|
|
|
for (i = 0; i < grad->n_stops; i++)
|
2008-03-26 11:31:04 -07:00
|
|
|
|
if (grad->stops[i].offset > fdist)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
if (i == 0 || i == grad->n_stops) {
|
|
|
|
|
|
if (i == grad->n_stops)
|
|
|
|
|
|
--i;
|
2007-11-15 11:56:56 -08:00
|
|
|
|
out[0] = grad->stops[i].color.red;
|
|
|
|
|
|
out[1] = grad->stops[i].color.green;
|
|
|
|
|
|
out[2] = grad->stops[i].color.blue;
|
|
|
|
|
|
out[3] = grad->stops[i].color.alpha;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
} else {
|
2009-12-29 08:47:09 +01:00
|
|
|
|
cairo_quartz_float_t ax = grad->stops[i-1].offset;
|
|
|
|
|
|
cairo_quartz_float_t bx = grad->stops[i].offset - ax;
|
|
|
|
|
|
cairo_quartz_float_t bp = (fdist - ax)/bx;
|
|
|
|
|
|
cairo_quartz_float_t ap = 1.0 - bp;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
out[0] =
|
2007-11-15 11:56:56 -08:00
|
|
|
|
grad->stops[i-1].color.red * ap +
|
|
|
|
|
|
grad->stops[i].color.red * bp;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
out[1] =
|
2007-11-15 11:56:56 -08:00
|
|
|
|
grad->stops[i-1].color.green * ap +
|
|
|
|
|
|
grad->stops[i].color.green * bp;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
out[2] =
|
2007-11-15 11:56:56 -08:00
|
|
|
|
grad->stops[i-1].color.blue * ap +
|
|
|
|
|
|
grad->stops[i].color.blue * bp;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
out[3] =
|
2007-11-15 11:56:56 -08:00
|
|
|
|
grad->stops[i-1].color.alpha * ap +
|
|
|
|
|
|
grad->stops[i].color.alpha * bp;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-05-11 13:59:43 -04:00
|
|
|
|
static const cairo_quartz_float_t gradient_output_value_ranges[8] = {
|
|
|
|
|
|
0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f
|
|
|
|
|
|
};
|
|
|
|
|
|
static const CGFunctionCallbacks gradient_callbacks = {
|
|
|
|
|
|
0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
|
|
|
|
|
|
};
|
2010-05-11 13:59:58 -04:00
|
|
|
|
/* Quartz will clamp input values to the input range.
|
|
|
|
|
|
|
|
|
|
|
|
Our stops are all in the range 0.0 to 1.0. However, the color before the
|
|
|
|
|
|
beginning of the gradient line is obtained by Quartz computing a negative
|
|
|
|
|
|
position on the gradient line, clamping it to the input range we specified
|
|
|
|
|
|
for our color function, and then calling our color function (actually it
|
|
|
|
|
|
pre-samples the color function into an array, but that doesn't matter just
|
|
|
|
|
|
here). Therefore if we set the lower bound to 0.0, a negative position
|
|
|
|
|
|
on the gradient line will pass 0.0 to ComputeGradientValue, which will
|
|
|
|
|
|
select the last color stop with position 0, although it should select
|
|
|
|
|
|
the first color stop (this matters when there are multiple color stops with
|
|
|
|
|
|
position 0).
|
|
|
|
|
|
|
|
|
|
|
|
Therefore we pass a small negative number as the lower bound of the input
|
|
|
|
|
|
range, so this value gets passed into ComputeGradientValue, which will
|
|
|
|
|
|
return the color of the first stop. The number should be small because
|
|
|
|
|
|
as far as I can tell, Quartz pre-samples the entire input range of the color
|
|
|
|
|
|
function into an array of fixed size, so if the input range is larger
|
|
|
|
|
|
than needed, the resolution of the gradient will be unnecessarily low.
|
|
|
|
|
|
*/
|
2010-09-07 08:49:05 +02:00
|
|
|
|
static const cairo_quartz_float_t nonrepeating_gradient_input_value_range[2] = { 0., 1. };
|
2010-05-11 13:59:43 -04:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
static CGFunctionRef
|
2008-12-10 17:54:59 -05:00
|
|
|
|
CreateGradientFunction (const cairo_gradient_pattern_t *gpat)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2008-12-11 10:34:37 -05:00
|
|
|
|
cairo_pattern_t *pat;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-12-11 10:34:37 -05:00
|
|
|
|
if (_cairo_pattern_create_copy (&pat, &gpat->base))
|
|
|
|
|
|
/* quartz doesn't deal very well with malloc failing, so there's
|
|
|
|
|
|
* not much point in us trying either */
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
|
|
return CGFunctionCreate (pat,
|
2007-02-20 12:15:35 -08:00
|
|
|
|
1,
|
2010-05-11 13:59:58 -04:00
|
|
|
|
nonrepeating_gradient_input_value_range,
|
2007-02-20 12:15:35 -08:00
|
|
|
|
4,
|
2010-05-11 13:59:43 -04:00
|
|
|
|
gradient_output_value_ranges,
|
|
|
|
|
|
&gradient_callbacks);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
static void
|
2010-07-27 15:59:31 +02:00
|
|
|
|
UpdateLinearParametersToIncludePoint (double *min_t, double *max_t, CGPoint *start,
|
|
|
|
|
|
double dx, double dy,
|
|
|
|
|
|
double x, double y)
|
2008-03-08 19:22:13 -08:00
|
|
|
|
{
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
/* Compute a parameter t such that a line perpendicular to the (dx,dy)
|
|
|
|
|
|
vector, passing through (start->x + dx*t, start->y + dy*t), also
|
|
|
|
|
|
passes through (x,y).
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
Let px = x - start->x, py = y - start->y.
|
|
|
|
|
|
t is given by
|
|
|
|
|
|
(px - dx*t)*dx + (py - dy*t)*dy = 0
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
Solving for t we get
|
|
|
|
|
|
numerator = dx*px + dy*py
|
|
|
|
|
|
denominator = dx^2 + dy^2
|
|
|
|
|
|
t = numerator/denominator
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
In CreateRepeatingLinearGradientFunction we know the length of (dx,dy)
|
|
|
|
|
|
is not zero. (This is checked in _cairo_quartz_setup_linear_source.)
|
|
|
|
|
|
*/
|
|
|
|
|
|
double px = x - start->x;
|
|
|
|
|
|
double py = y - start->y;
|
|
|
|
|
|
double numerator = dx*px + dy*py;
|
|
|
|
|
|
double denominator = dx*dx + dy*dy;
|
|
|
|
|
|
double t = numerator/denominator;
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
2010-07-29 16:05:51 +02:00
|
|
|
|
if (*min_t > t)
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
*min_t = t;
|
2010-07-29 16:05:51 +02:00
|
|
|
|
|
|
|
|
|
|
if (*max_t < t)
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
*max_t = t;
|
|
|
|
|
|
}
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
static CGFunctionRef
|
|
|
|
|
|
CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
|
|
|
|
|
|
const cairo_gradient_pattern_t *gpat,
|
|
|
|
|
|
CGPoint *start, CGPoint *end,
|
2010-10-12 22:52:54 +02:00
|
|
|
|
const cairo_rectangle_int_t *extents)
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
{
|
|
|
|
|
|
cairo_pattern_t *pat;
|
|
|
|
|
|
cairo_quartz_float_t input_value_range[2];
|
|
|
|
|
|
double t_min = 0.;
|
|
|
|
|
|
double t_max = 0.;
|
|
|
|
|
|
double dx = end->x - start->x;
|
|
|
|
|
|
double dy = end->y - start->y;
|
|
|
|
|
|
double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
|
|
|
|
|
|
|
|
|
|
|
|
bounds_x1 = extents->x;
|
|
|
|
|
|
bounds_y1 = extents->y;
|
|
|
|
|
|
bounds_x2 = extents->x + extents->width;
|
|
|
|
|
|
bounds_y2 = extents->y + extents->height;
|
|
|
|
|
|
_cairo_matrix_transform_bounding_box (&gpat->base.matrix,
|
|
|
|
|
|
&bounds_x1, &bounds_y1,
|
|
|
|
|
|
&bounds_x2, &bounds_y2,
|
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
UpdateLinearParametersToIncludePoint (&t_min, &t_max, start, dx, dy,
|
|
|
|
|
|
bounds_x1, bounds_y1);
|
|
|
|
|
|
UpdateLinearParametersToIncludePoint (&t_min, &t_max, start, dx, dy,
|
|
|
|
|
|
bounds_x2, bounds_y1);
|
|
|
|
|
|
UpdateLinearParametersToIncludePoint (&t_min, &t_max, start, dx, dy,
|
|
|
|
|
|
bounds_x2, bounds_y2);
|
|
|
|
|
|
UpdateLinearParametersToIncludePoint (&t_min, &t_max, start, dx, dy,
|
|
|
|
|
|
bounds_x1, bounds_y2);
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
|
|
|
|
|
|
end->x = start->x + dx*t_max;
|
|
|
|
|
|
end->y = start->y + dy*t_max;
|
|
|
|
|
|
start->x = start->x + dx*t_min;
|
|
|
|
|
|
start->y = start->y + dy*t_min;
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
|
|
|
|
|
// 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.
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
input_value_range[0] = t_min;
|
|
|
|
|
|
input_value_range[1] = t_max;
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
2008-12-11 10:34:37 -05:00
|
|
|
|
if (_cairo_pattern_create_copy (&pat, &gpat->base))
|
|
|
|
|
|
/* quartz doesn't deal very well with malloc failing, so there's
|
|
|
|
|
|
* not much point in us trying either */
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
|
|
return CGFunctionCreate (pat,
|
2008-03-08 19:22:13 -08:00
|
|
|
|
1,
|
|
|
|
|
|
input_value_range,
|
|
|
|
|
|
4,
|
2010-05-11 13:59:43 -04:00
|
|
|
|
gradient_output_value_ranges,
|
|
|
|
|
|
&gradient_callbacks);
|
2008-03-08 19:22:13 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-05-11 13:58:10 -04:00
|
|
|
|
static void
|
2010-07-27 15:59:31 +02:00
|
|
|
|
UpdateRadialParameterToIncludePoint (double *max_t, CGPoint *center,
|
|
|
|
|
|
double dr, double dx, double dy,
|
|
|
|
|
|
double x, double y)
|
2010-05-11 13:58:10 -04:00
|
|
|
|
{
|
|
|
|
|
|
/* Compute a parameter t such that a circle centered at
|
|
|
|
|
|
(center->x + dx*t, center->y + dy*t) with radius dr*t contains the
|
|
|
|
|
|
point (x,y).
|
|
|
|
|
|
|
|
|
|
|
|
Let px = x - center->x, py = y - center->y.
|
|
|
|
|
|
Parameter values for which t is on the circle are given by
|
|
|
|
|
|
(px - dx*t)^2 + (py - dy*t)^2 = (t*dr)^2
|
|
|
|
|
|
|
|
|
|
|
|
Solving for t using the quadratic formula, and simplifying, we get
|
|
|
|
|
|
numerator = dx*px + dy*py +-
|
|
|
|
|
|
sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
|
|
|
|
|
|
denominator = dx^2 + dy^2 - dr^2
|
|
|
|
|
|
t = numerator/denominator
|
|
|
|
|
|
|
|
|
|
|
|
In CreateRepeatingRadialGradientFunction we know the outer circle
|
|
|
|
|
|
contains the inner circle. Therefore the distance between the circle
|
|
|
|
|
|
centers plus the radius of the inner circle is less than the radius of
|
|
|
|
|
|
the outer circle. (This is checked in _cairo_quartz_setup_radial_source.)
|
|
|
|
|
|
Therefore
|
|
|
|
|
|
dx^2 + dy^2 < dr^2
|
|
|
|
|
|
So the denominator is negative and the larger solution for t is given by
|
|
|
|
|
|
numerator = dx*px + dy*py -
|
|
|
|
|
|
sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
|
|
|
|
|
|
denominator = dx^2 + dy^2 - dr^2
|
|
|
|
|
|
t = numerator/denominator
|
|
|
|
|
|
dx^2 + dy^2 < dr^2 also ensures that the operand of sqrt is positive.
|
|
|
|
|
|
*/
|
|
|
|
|
|
double px = x - center->x;
|
|
|
|
|
|
double py = y - center->y;
|
|
|
|
|
|
double dx_py_minus_dy_px = dx*py - dy*px;
|
|
|
|
|
|
double numerator = dx*px + dy*py -
|
|
|
|
|
|
sqrt (dr*dr*(px*px + py*py) - dx_py_minus_dy_px*dx_py_minus_dy_px);
|
|
|
|
|
|
double denominator = dx*dx + dy*dy - dr*dr;
|
|
|
|
|
|
double t = numerator/denominator;
|
|
|
|
|
|
|
2010-07-29 16:05:51 +02:00
|
|
|
|
if (*max_t < t)
|
2010-05-11 13:58:10 -04:00
|
|
|
|
*max_t = t;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This must only be called when one of the circles properly contains the other */
|
|
|
|
|
|
static CGFunctionRef
|
|
|
|
|
|
CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface,
|
|
|
|
|
|
const cairo_gradient_pattern_t *gpat,
|
|
|
|
|
|
CGPoint *start, double *start_radius,
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
CGPoint *end, double *end_radius,
|
2010-10-12 22:52:54 +02:00
|
|
|
|
const cairo_rectangle_int_t *extents)
|
2010-05-11 13:58:10 -04:00
|
|
|
|
{
|
|
|
|
|
|
cairo_pattern_t *pat;
|
2010-05-14 21:04:22 +02:00
|
|
|
|
cairo_quartz_float_t input_value_range[2];
|
2010-05-11 13:58:10 -04:00
|
|
|
|
CGPoint *inner;
|
|
|
|
|
|
double *inner_radius;
|
|
|
|
|
|
CGPoint *outer;
|
|
|
|
|
|
double *outer_radius;
|
|
|
|
|
|
/* minimum and maximum t-parameter values that will make our gradient
|
|
|
|
|
|
cover the clipBox */
|
|
|
|
|
|
double t_min, t_max, t_temp;
|
|
|
|
|
|
/* outer minus inner */
|
|
|
|
|
|
double dr, dx, dy;
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
|
2010-05-11 13:58:10 -04:00
|
|
|
|
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
bounds_x1 = extents->x;
|
|
|
|
|
|
bounds_y1 = extents->y;
|
|
|
|
|
|
bounds_x2 = extents->x + extents->width;
|
|
|
|
|
|
bounds_y2 = extents->y + extents->height;
|
|
|
|
|
|
_cairo_matrix_transform_bounding_box (&gpat->base.matrix,
|
|
|
|
|
|
&bounds_x1, &bounds_y1,
|
|
|
|
|
|
&bounds_x2, &bounds_y2,
|
|
|
|
|
|
NULL);
|
2010-05-11 13:58:10 -04:00
|
|
|
|
|
|
|
|
|
|
if (*start_radius < *end_radius) {
|
|
|
|
|
|
/* end circle contains start circle */
|
|
|
|
|
|
inner = start;
|
|
|
|
|
|
outer = end;
|
|
|
|
|
|
inner_radius = start_radius;
|
|
|
|
|
|
outer_radius = end_radius;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
/* start circle contains end circle */
|
|
|
|
|
|
inner = end;
|
|
|
|
|
|
outer = start;
|
|
|
|
|
|
inner_radius = end_radius;
|
|
|
|
|
|
outer_radius = start_radius;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dr = *outer_radius - *inner_radius;
|
|
|
|
|
|
dx = outer->x - inner->x;
|
|
|
|
|
|
dy = outer->y - inner->y;
|
|
|
|
|
|
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
/* We can't round or fudge t_min here, it has to be as accurate as possible. */
|
2010-05-11 13:58:10 -04:00
|
|
|
|
t_min = -(*inner_radius/dr);
|
|
|
|
|
|
inner->x += t_min*dx;
|
|
|
|
|
|
inner->y += t_min*dy;
|
|
|
|
|
|
*inner_radius = 0.;
|
|
|
|
|
|
|
|
|
|
|
|
t_temp = 0.;
|
2010-07-27 15:59:31 +02:00
|
|
|
|
UpdateRadialParameterToIncludePoint (&t_temp, inner, dr, dx, dy,
|
|
|
|
|
|
bounds_x1, bounds_y1);
|
|
|
|
|
|
UpdateRadialParameterToIncludePoint (&t_temp, inner, dr, dx, dy,
|
|
|
|
|
|
bounds_x2, bounds_y1);
|
|
|
|
|
|
UpdateRadialParameterToIncludePoint (&t_temp, inner, dr, dx, dy,
|
|
|
|
|
|
bounds_x2, bounds_y2);
|
|
|
|
|
|
UpdateRadialParameterToIncludePoint (&t_temp, inner, dr, dx, dy,
|
|
|
|
|
|
bounds_x1, bounds_y2);
|
2010-05-11 13:58:10 -04:00
|
|
|
|
/* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0.
|
2010-10-08 16:57:27 +02:00
|
|
|
|
But for the parameter values we use with Quartz, t_min means radius 0. */
|
2010-05-11 13:58:10 -04:00
|
|
|
|
t_max = t_min + t_temp;
|
|
|
|
|
|
outer->x = inner->x + t_temp*dx;
|
|
|
|
|
|
outer->y = inner->y + t_temp*dy;
|
|
|
|
|
|
*outer_radius = t_temp*dr;
|
|
|
|
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
|
if (*start_radius < *end_radius) {
|
|
|
|
|
|
input_value_range[0] = t_min;
|
|
|
|
|
|
input_value_range[1] = t_max;
|
|
|
|
|
|
} else {
|
2010-07-26 11:21:48 +02:00
|
|
|
|
input_value_range[0] = 1 - t_max;
|
|
|
|
|
|
input_value_range[1] = 1 - t_min;
|
2010-05-11 13:58:10 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (_cairo_pattern_create_copy (&pat, &gpat->base))
|
2010-07-27 17:24:08 +02:00
|
|
|
|
/* quartz doesn't deal very well with malloc failing, so there's
|
|
|
|
|
|
* not much point in us trying either */
|
|
|
|
|
|
return NULL;
|
2010-05-11 13:58:10 -04:00
|
|
|
|
|
|
|
|
|
|
return CGFunctionCreate (pat,
|
2010-07-27 17:24:08 +02:00
|
|
|
|
1,
|
|
|
|
|
|
input_value_range,
|
|
|
|
|
|
4,
|
|
|
|
|
|
gradient_output_value_ranges,
|
|
|
|
|
|
&gradient_callbacks);
|
2010-05-11 13:58:10 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-26 13:04:33 +00:00
|
|
|
|
/* Obtain a CGImageRef from a #cairo_surface_t * */
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-01-24 21:59:32 +01:00
|
|
|
|
typedef struct {
|
|
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
|
|
cairo_image_surface_t *image_out;
|
|
|
|
|
|
void *image_extra;
|
|
|
|
|
|
} quartz_source_image_t;
|
|
|
|
|
|
|
2008-07-09 13:20:53 -07:00
|
|
|
|
static void
|
|
|
|
|
|
DataProviderReleaseCallback (void *info, const void *data, size_t size)
|
|
|
|
|
|
{
|
2010-01-24 21:59:32 +01:00
|
|
|
|
quartz_source_image_t *source_img = info;
|
|
|
|
|
|
_cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
|
|
|
|
|
|
free (source_img);
|
2008-07-09 13:20:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-03-17 17:37:22 -07:00
|
|
|
|
static cairo_status_t
|
2010-07-27 14:38:28 +02:00
|
|
|
|
_cairo_surface_to_cgimage (cairo_surface_t *source,
|
2008-03-17 17:37:22 -07:00
|
|
|
|
CGImageRef *image_out)
|
2008-02-25 21:06:21 -05:00
|
|
|
|
{
|
2010-01-24 21:59:32 +01:00
|
|
|
|
cairo_status_t status;
|
|
|
|
|
|
quartz_source_image_t *source_img;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 18:04:36 +02:00
|
|
|
|
if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
|
2008-02-25 21:06:21 -05:00
|
|
|
|
cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
|
2008-03-17 17:37:22 -07:00
|
|
|
|
*image_out = CGImageRetain (surface->image);
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
}
|
2007-06-18 00:07:33 +01:00
|
|
|
|
|
2010-07-27 18:04:36 +02:00
|
|
|
|
if (_cairo_surface_is_quartz (source)) {
|
2008-02-25 21:06:21 -05:00
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (IS_EMPTY (surface)) {
|
2008-03-17 17:37:22 -07:00
|
|
|
|
*image_out = NULL;
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-03-08 15:31:15 -08:00
|
|
|
|
if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
|
2008-03-17 17:37:22 -07:00
|
|
|
|
*image_out = CGBitmapContextCreateImage (surface->cgContext);
|
|
|
|
|
|
if (*image_out)
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2008-03-08 15:31:15 -08:00
|
|
|
|
}
|
2008-02-25 21:06:21 -05:00
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-01-24 21:59:32 +01:00
|
|
|
|
source_img = malloc (sizeof (quartz_source_image_t));
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (source_img == NULL))
|
2010-01-24 21:59:32 +01:00
|
|
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
|
|
|
|
|
|
|
|
source_img->surface = source;
|
|
|
|
|
|
|
|
|
|
|
|
status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (status)) {
|
2010-01-24 21:59:32 +01:00
|
|
|
|
free (source_img);
|
|
|
|
|
|
return status;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-01-24 21:59:32 +01:00
|
|
|
|
if (source_img->image_out->width == 0 || source_img->image_out->height == 0) {
|
2008-03-17 17:37:22 -07:00
|
|
|
|
*image_out = NULL;
|
2010-01-24 21:59:32 +01:00
|
|
|
|
DataProviderReleaseCallback (source_img,
|
|
|
|
|
|
source_img->image_out->data,
|
|
|
|
|
|
source_img->image_out->height * source_img->image_out->stride);
|
2008-03-17 17:37:22 -07:00
|
|
|
|
} else {
|
2010-01-24 21:59:32 +01:00
|
|
|
|
*image_out = _cairo_quartz_create_cgimage (source_img->image_out->format,
|
|
|
|
|
|
source_img->image_out->width,
|
|
|
|
|
|
source_img->image_out->height,
|
|
|
|
|
|
source_img->image_out->stride,
|
|
|
|
|
|
source_img->image_out->data,
|
|
|
|
|
|
TRUE,
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
DataProviderReleaseCallback,
|
|
|
|
|
|
source_img);
|
|
|
|
|
|
|
|
|
|
|
|
/* TODO: differentiate memory error and unsupported surface type */
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (*image_out == NULL))
|
2010-01-02 13:23:53 +01:00
|
|
|
|
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
2008-03-17 17:37:22 -07:00
|
|
|
|
}
|
2007-11-06 15:40:30 -08:00
|
|
|
|
|
2010-01-02 13:23:53 +01:00
|
|
|
|
return status;
|
2007-06-07 23:22:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-01-28 21:49:57 -05:00
|
|
|
|
/* Generic #cairo_pattern_t -> CGPattern function */
|
2007-06-07 23:22:05 +01:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
typedef struct {
|
|
|
|
|
|
CGImageRef image;
|
2007-06-13 01:04:54 +01:00
|
|
|
|
CGRect imageBounds;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
cairo_bool_t do_reflect;
|
|
|
|
|
|
} SurfacePatternDrawInfo;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
static void
|
|
|
|
|
|
SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
|
|
|
|
|
|
{
|
|
|
|
|
|
SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
|
2007-06-18 00:07:33 +01:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
|
|
|
|
|
|
CGContextScaleCTM (context, 1, -1);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
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.
|
|
|
|
|
|
*/
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
/* Make the y axis extend downwards, and draw a flipped image below */
|
2007-06-07 23:22:27 +01:00
|
|
|
|
CGContextScaleCTM (context, 1, -1);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
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);
|
2007-06-07 23:22:27 +01:00
|
|
|
|
CGContextScaleCTM (context, -1, 1);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
CGContextDrawImage (context, info->imageBounds, info->image);
|
|
|
|
|
|
|
|
|
|
|
|
/* Then unflip the Y-axis again, and draw the image above the point. */
|
2007-06-07 23:22:27 +01:00
|
|
|
|
CGContextScaleCTM (context, 1, -1);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
CGContextDrawImage (context, info->imageBounds, info->image);
|
2007-06-07 23:22:27 +01:00
|
|
|
|
}
|
2008-02-25 21:06:21 -05:00
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
static void
|
|
|
|
|
|
SurfacePatternReleaseInfoFunc (void *ainfo)
|
|
|
|
|
|
{
|
|
|
|
|
|
SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
CGImageRelease (info->image);
|
|
|
|
|
|
free (info);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-10-20 23:41:46 +01:00
|
|
|
|
static cairo_int_status_t
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
|
2008-11-11 08:31:23 -05:00
|
|
|
|
const cairo_pattern_t *apattern,
|
2007-10-20 23:41:46 +01:00
|
|
|
|
CGPatternRef *cgpat)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2008-02-25 21:06:21 -05:00
|
|
|
|
cairo_surface_pattern_t *spattern;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_surface_t *pat_surf;
|
2007-06-18 16:56:24 -07:00
|
|
|
|
cairo_rectangle_int_t extents;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
CGImageRef image;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGRect pbounds;
|
|
|
|
|
|
CGAffineTransform ptransform, stransform;
|
|
|
|
|
|
CGPatternCallbacks cb = { 0,
|
|
|
|
|
|
SurfacePatternDrawFunc,
|
2008-02-25 21:06:21 -05:00
|
|
|
|
SurfacePatternReleaseInfoFunc };
|
|
|
|
|
|
SurfacePatternDrawInfo *info;
|
2009-12-29 08:47:09 +01:00
|
|
|
|
cairo_quartz_float_t rw, rh;
|
2007-06-18 00:07:33 +01:00
|
|
|
|
cairo_status_t status;
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
cairo_bool_t is_bounded;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
cairo_matrix_t m;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/* SURFACE is the only type we'll handle here */
|
2010-07-27 14:44:17 +02:00
|
|
|
|
assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
spattern = (cairo_surface_pattern_t *) apattern;
|
|
|
|
|
|
pat_surf = spattern->surface;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
|
|
|
|
|
|
assert (is_bounded);
|
2007-06-18 00:07:33 +01:00
|
|
|
|
|
2010-07-27 14:38:28 +02:00
|
|
|
|
status = _cairo_surface_to_cgimage (pat_surf, &image);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (status))
|
2010-01-02 13:23:53 +01:00
|
|
|
|
return status;
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (image == NULL))
|
2008-03-17 17:37:22 -07:00
|
|
|
|
return CAIRO_INT_STATUS_NOTHING_TO_DO;
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
info = malloc (sizeof (SurfacePatternDrawInfo));
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (!info))
|
2008-02-25 21:06:21 -05:00
|
|
|
|
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);
|
2008-10-07 10:18:41 -07:00
|
|
|
|
info->do_reflect = FALSE;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
pbounds.origin.x = 0;
|
|
|
|
|
|
pbounds.origin.y = 0;
|
2007-06-07 23:22:27 +01:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
|
|
|
|
|
|
pbounds.size.width = 2.0 * extents.width;
|
|
|
|
|
|
pbounds.size.height = 2.0 * extents.height;
|
|
|
|
|
|
info->do_reflect = TRUE;
|
2007-06-07 23:22:27 +01:00
|
|
|
|
} else {
|
|
|
|
|
|
pbounds.size.width = extents.width;
|
|
|
|
|
|
pbounds.size.height = extents.height;
|
|
|
|
|
|
}
|
|
|
|
|
|
rw = pbounds.size.width;
|
|
|
|
|
|
rh = pbounds.size.height;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
m = spattern->base.matrix;
|
2010-07-27 15:59:31 +02:00
|
|
|
|
cairo_matrix_invert (&m);
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
|
|
*/
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2007-02-20 12:54:03 -08:00
|
|
|
|
#ifdef QUARTZ_DEBUG
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
|
|
|
|
|
|
ND ((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d));
|
|
|
|
|
|
CGAffineTransform xform = CGContextGetCTM (dest->cgContext);
|
|
|
|
|
|
ND ((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
#endif
|
|
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
*cgpat = CGPatternCreate (info,
|
|
|
|
|
|
pbounds,
|
|
|
|
|
|
ptransform,
|
|
|
|
|
|
rw, rh,
|
|
|
|
|
|
kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
|
|
|
|
|
|
TRUE,
|
|
|
|
|
|
&cb);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2007-10-20 23:41:46 +01:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
/*
|
|
|
|
|
|
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. CreateRepeatingLinearGradientFunction and
|
|
|
|
|
|
CreateRepeatingRadialGradientFunction compute the number of repetitions needed
|
|
|
|
|
|
based on the extents of the object (the clip region cannot be used here since
|
|
|
|
|
|
we don't want the rasterization of the entire gradient to depend on the
|
|
|
|
|
|
clip region).
|
|
|
|
|
|
*/
|
2010-10-08 09:49:51 +02:00
|
|
|
|
static cairo_int_status_t
|
2007-12-06 21:31:14 +00:00
|
|
|
|
_cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
const cairo_linear_pattern_t *lpat,
|
2010-10-12 22:52:54 +02:00
|
|
|
|
const cairo_rectangle_int_t *extents)
|
2007-12-06 21:31:14 +00:00
|
|
|
|
{
|
2008-12-10 17:54:59 -05:00
|
|
|
|
const cairo_pattern_t *abspat = &lpat->base.base;
|
2007-12-06 21:31:14 +00:00
|
|
|
|
cairo_matrix_t mat;
|
|
|
|
|
|
CGPoint start, end;
|
|
|
|
|
|
CGFunctionRef gradFunc;
|
|
|
|
|
|
CGColorSpaceRef rgb;
|
2010-09-07 08:49:05 +02:00
|
|
|
|
bool extend = abspat->extend != CAIRO_EXTEND_NONE;
|
2007-12-06 21:31:14 +00:00
|
|
|
|
|
2010-07-27 14:44:17 +02:00
|
|
|
|
assert (lpat->base.n_stops > 0);
|
2007-12-06 21:31:15 +00:00
|
|
|
|
|
2008-12-10 17:54:59 -05:00
|
|
|
|
mat = abspat->matrix;
|
2008-03-07 16:46:15 -08:00
|
|
|
|
cairo_matrix_invert (&mat);
|
|
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
rgb = CGColorSpaceCreateDeviceRGB ();
|
2007-12-06 21:31:14 +00:00
|
|
|
|
|
2008-03-07 16:46:15 -08:00
|
|
|
|
start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
|
|
|
|
|
|
_cairo_fixed_to_double (lpat->p1.y));
|
|
|
|
|
|
end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
|
|
|
|
|
|
_cairo_fixed_to_double (lpat->p2.y));
|
2007-12-06 21:31:14 +00:00
|
|
|
|
|
2010-09-07 08:49:05 +02:00
|
|
|
|
if (!extend)
|
2008-12-09 12:03:11 -05:00
|
|
|
|
gradFunc = CreateGradientFunction (&lpat->base);
|
2010-09-07 08:49:05 +02:00
|
|
|
|
else
|
2010-05-11 13:58:10 -04:00
|
|
|
|
gradFunc = CreateRepeatingLinearGradientFunction (surface,
|
|
|
|
|
|
&lpat->base,
|
|
|
|
|
|
&start, &end,
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
extents);
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
2007-12-06 21:31:14 +00:00
|
|
|
|
surface->sourceShading = CGShadingCreateAxial (rgb,
|
|
|
|
|
|
start, end,
|
|
|
|
|
|
gradFunc,
|
|
|
|
|
|
extend, extend);
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CGColorSpaceRelease (rgb);
|
|
|
|
|
|
CGFunctionRelease (gradFunc);
|
2007-12-06 21:31:14 +00:00
|
|
|
|
|
2010-10-08 09:49:51 +02:00
|
|
|
|
surface->action = DO_SHADING;
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2007-12-06 21:31:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-10-08 09:49:51 +02:00
|
|
|
|
static cairo_int_status_t
|
2007-12-06 21:31:14 +00:00
|
|
|
|
_cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
const cairo_radial_pattern_t *rpat,
|
2010-10-12 22:52:54 +02:00
|
|
|
|
const cairo_rectangle_int_t *extents)
|
2007-12-06 21:31:14 +00:00
|
|
|
|
{
|
2008-12-10 17:54:59 -05:00
|
|
|
|
const cairo_pattern_t *abspat = &rpat->base.base;
|
2007-12-06 21:31:14 +00:00
|
|
|
|
cairo_matrix_t mat;
|
|
|
|
|
|
CGPoint start, end;
|
|
|
|
|
|
CGFunctionRef gradFunc;
|
2008-03-08 19:22:13 -08:00
|
|
|
|
CGColorSpaceRef rgb;
|
2010-09-07 08:49:05 +02:00
|
|
|
|
bool extend = abspat->extend != CAIRO_EXTEND_NONE;
|
2010-05-11 13:58:10 -04:00
|
|
|
|
double c1x = _cairo_fixed_to_double (rpat->c1.x);
|
|
|
|
|
|
double c1y = _cairo_fixed_to_double (rpat->c1.y);
|
|
|
|
|
|
double c2x = _cairo_fixed_to_double (rpat->c2.x);
|
|
|
|
|
|
double c2y = _cairo_fixed_to_double (rpat->c2.y);
|
|
|
|
|
|
double r1 = _cairo_fixed_to_double (rpat->r1);
|
|
|
|
|
|
double r2 = _cairo_fixed_to_double (rpat->r2);
|
2007-12-06 21:31:14 +00:00
|
|
|
|
|
2010-07-27 14:44:17 +02:00
|
|
|
|
assert (rpat->base.n_stops > 0);
|
2007-12-06 21:31:15 +00:00
|
|
|
|
|
2008-12-10 17:54:59 -05:00
|
|
|
|
mat = abspat->matrix;
|
2008-03-07 16:46:15 -08:00
|
|
|
|
cairo_matrix_invert (&mat);
|
|
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
rgb = CGColorSpaceCreateDeviceRGB ();
|
2007-12-06 21:31:14 +00:00
|
|
|
|
|
2010-05-11 13:58:10 -04:00
|
|
|
|
start = CGPointMake (c1x, c1y);
|
|
|
|
|
|
end = CGPointMake (c2x, c2y);
|
2007-12-06 21:31:14 +00:00
|
|
|
|
|
2010-09-07 08:49:05 +02:00
|
|
|
|
if (!extend)
|
2010-05-11 13:58:10 -04:00
|
|
|
|
gradFunc = CreateGradientFunction (&rpat->base);
|
2010-09-07 08:49:05 +02:00
|
|
|
|
else
|
2010-05-11 13:58:10 -04:00
|
|
|
|
gradFunc = CreateRepeatingRadialGradientFunction (surface,
|
|
|
|
|
|
&rpat->base,
|
|
|
|
|
|
&start, &r1,
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
&end, &r2,
|
|
|
|
|
|
extents);
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
2007-12-06 21:31:14 +00:00
|
|
|
|
surface->sourceShading = CGShadingCreateRadial (rgb,
|
|
|
|
|
|
start,
|
2010-05-11 13:58:10 -04:00
|
|
|
|
r1,
|
2007-12-06 21:31:14 +00:00
|
|
|
|
end,
|
2010-05-11 13:58:10 -04:00
|
|
|
|
r2,
|
2007-12-06 21:31:14 +00:00
|
|
|
|
gradFunc,
|
|
|
|
|
|
extend, extend);
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CGColorSpaceRelease (rgb);
|
|
|
|
|
|
CGFunctionRelease (gradFunc);
|
2007-12-06 21:31:14 +00:00
|
|
|
|
|
2010-10-08 09:49:51 +02:00
|
|
|
|
surface->action = DO_SHADING;
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2007-12-06 21:31:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-10-08 09:49:51 +02:00
|
|
|
|
static cairo_int_status_t
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
|
2010-07-28 19:03:13 +02:00
|
|
|
|
cairo_operator_t op,
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
const cairo_pattern_t *source,
|
2010-10-12 22:52:54 +02:00
|
|
|
|
const cairo_rectangle_int_t *extents)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2010-07-28 19:03:13 +02:00
|
|
|
|
cairo_status_t status;
|
|
|
|
|
|
|
2010-07-29 15:06:39 +02:00
|
|
|
|
assert (!(surface->sourceImage || surface->sourceShading));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-28 19:03:13 +02:00
|
|
|
|
/* 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)
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
CGContextSaveGState (surface->cgContext);
|
|
|
|
|
|
|
|
|
|
|
|
status = _cairo_quartz_surface_set_cairo_operator (surface, op);
|
|
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
return status;
|
|
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
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);
|
|
|
|
|
|
|
2010-07-29 15:06:39 +02:00
|
|
|
|
surface->action = DO_DIRECT;
|
2010-10-08 09:49:51 +02:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2008-01-27 16:52:39 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
|
2008-12-10 17:54:59 -05:00
|
|
|
|
const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
return _cairo_quartz_setup_linear_source (surface, lpat, extents);
|
2008-01-27 16:52:39 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
|
2008-12-10 17:54:59 -05:00
|
|
|
|
const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
return _cairo_quartz_setup_radial_source (surface, rpat, extents);
|
2008-01-27 16:52:39 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
|
|
|
|
|
|
(source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
|
2007-06-08 01:17:09 +01:00
|
|
|
|
{
|
2008-12-10 17:54:59 -05:00
|
|
|
|
const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
|
2008-01-27 16:52:39 -08:00
|
|
|
|
cairo_surface_t *pat_surf = spat->surface;
|
|
|
|
|
|
CGImageRef img;
|
|
|
|
|
|
cairo_matrix_t m = spat->base.matrix;
|
|
|
|
|
|
cairo_rectangle_int_t extents;
|
|
|
|
|
|
CGAffineTransform xform;
|
|
|
|
|
|
CGRect srcRect;
|
|
|
|
|
|
cairo_fixed_t fw, fh;
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
cairo_bool_t is_bounded;
|
2007-05-29 23:11:49 +01:00
|
|
|
|
|
2010-07-27 14:38:28 +02:00
|
|
|
|
status = _cairo_surface_to_cgimage (pat_surf, &img);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (status))
|
2010-10-08 09:49:51 +02:00
|
|
|
|
return status;
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (img == NULL))
|
2010-10-08 09:49:51 +02:00
|
|
|
|
return CAIRO_INT_STATUS_NOTHING_TO_DO;
|
2007-05-29 23:11:49 +01:00
|
|
|
|
|
2009-11-09 14:24:13 +01:00
|
|
|
|
CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
|
|
|
|
|
|
|
2008-01-27 16:52:39 -08:00
|
|
|
|
surface->sourceImage = img;
|
2007-05-29 23:11:49 +01:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
cairo_matrix_invert (&m);
|
2008-03-07 16:46:15 -08:00
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
|
2007-06-08 01:17:09 +01:00
|
|
|
|
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
|
|
|
|
|
|
assert (is_bounded);
|
2007-06-18 00:07:33 +01:00
|
|
|
|
|
2008-01-27 16:52:39 -08:00
|
|
|
|
if (source->extend == CAIRO_EXTEND_NONE) {
|
2007-06-08 01:17:09 +01:00
|
|
|
|
surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
|
2010-10-08 09:49:51 +02:00
|
|
|
|
surface->action = DO_IMAGE;
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2008-01-27 16:52:39 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext),
|
2008-03-07 16:46:15 -08:00
|
|
|
|
surface->sourceTransform);
|
2008-01-27 16:52:39 -08:00
|
|
|
|
|
|
|
|
|
|
srcRect = CGRectMake (0, 0, extents.width, extents.height);
|
|
|
|
|
|
srcRect = CGRectApplyAffineTransform (srcRect, xform);
|
2007-06-08 01:17:09 +01:00
|
|
|
|
|
2008-01-27 16:52:39 -08:00
|
|
|
|
fw = _cairo_fixed_from_double (srcRect.size.width);
|
|
|
|
|
|
fh = _cairo_fixed_from_double (srcRect.size.height);
|
|
|
|
|
|
|
|
|
|
|
|
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 */
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
srcRect.size.width = round (srcRect.size.width);
|
|
|
|
|
|
srcRect.size.height = round (srcRect.size.height);
|
2008-01-27 16:52:39 -08:00
|
|
|
|
|
|
|
|
|
|
xform = CGAffineTransformInvert (xform);
|
|
|
|
|
|
|
|
|
|
|
|
srcRect = CGRectApplyAffineTransform (srcRect, xform);
|
|
|
|
|
|
|
|
|
|
|
|
surface->sourceImageRect = srcRect;
|
2010-10-08 09:49:51 +02:00
|
|
|
|
surface->action = DO_TILED_IMAGE;
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2008-01-27 16:52:39 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Fall through to generic SURFACE case */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
|
2009-12-29 08:47:09 +01:00
|
|
|
|
cairo_quartz_float_t patternAlpha = 1.0f;
|
2007-06-13 01:04:54 +01:00
|
|
|
|
CGColorSpaceRef patternSpace;
|
2010-10-08 09:49:51 +02:00
|
|
|
|
CGPatternRef pattern = NULL;
|
2007-10-20 23:41:46 +01:00
|
|
|
|
cairo_int_status_t status;
|
2007-06-13 01:04:54 +01:00
|
|
|
|
|
2007-10-20 23:41:46 +01:00
|
|
|
|
status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (status))
|
2010-10-08 09:49:51 +02:00
|
|
|
|
return status;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
patternSpace = CGColorSpaceCreatePattern (NULL);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextSetFillColorSpace (surface->cgContext, patternSpace);
|
|
|
|
|
|
CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
|
|
|
|
|
|
CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
|
|
|
|
|
|
CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
|
|
|
|
|
|
CGColorSpaceRelease (patternSpace);
|
|
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
|
*/
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CGContextSetPatternPhase (surface->cgContext, CGSizeMake (0, 0));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-29 15:06:39 +02:00
|
|
|
|
CGPatternRelease (pattern);
|
|
|
|
|
|
|
|
|
|
|
|
surface->action = DO_DIRECT;
|
2010-10-08 09:49:51 +02:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-10-08 09:49:51 +02:00
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
|
2008-11-11 08:31:23 -05:00
|
|
|
|
const cairo_pattern_t *source)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2010-07-28 19:03:13 +02:00
|
|
|
|
CGContextRestoreGState (surface->cgContext);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (surface->sourceImage) {
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CGImageRelease (surface->sourceImage);
|
2007-05-29 23:11:49 +01:00
|
|
|
|
surface->sourceImage = NULL;
|
2007-06-08 01:17:09 +01:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
cairo_surface_destroy (surface->sourceImageSurface);
|
2007-06-08 01:17:09 +01:00
|
|
|
|
surface->sourceImageSurface = NULL;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (surface->sourceShading) {
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CGShadingRelease (surface->sourceShading);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
surface->sourceShading = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-10-08 09:49:51 +02:00
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_setup_source_safe (cairo_quartz_surface_t *surface,
|
2010-07-28 19:03:13 +02:00
|
|
|
|
cairo_operator_t op,
|
2010-10-08 09:49:51 +02:00
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
const cairo_rectangle_int_t *extents)
|
|
|
|
|
|
{
|
|
|
|
|
|
cairo_int_status_t status;
|
|
|
|
|
|
|
2010-07-28 19:03:13 +02:00
|
|
|
|
status = _cairo_quartz_setup_source (surface, op, source, extents);
|
2010-10-08 09:49:51 +02:00
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
_cairo_quartz_teardown_source (surface, source);
|
|
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
|
}
|
2009-11-09 14:21:40 +01:00
|
|
|
|
|
|
|
|
|
|
static void
|
2010-07-29 15:50:21 +02:00
|
|
|
|
_cairo_quartz_draw_source (cairo_quartz_surface_t *surface, cairo_operator_t op)
|
2009-11-09 14:21:40 +01:00
|
|
|
|
{
|
|
|
|
|
|
CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
|
2010-07-29 15:50:21 +02:00
|
|
|
|
|
|
|
|
|
|
if (surface->action == DO_SHADING) {
|
|
|
|
|
|
CGContextDrawShading (surface->cgContext, surface->sourceShading);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-09 14:21:40 +01:00
|
|
|
|
CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
|
|
|
|
|
|
CGContextScaleCTM (surface->cgContext, 1, -1);
|
|
|
|
|
|
|
2010-10-08 09:49:51 +02:00
|
|
|
|
if (surface->action == DO_IMAGE) {
|
2009-11-09 14:21:40 +01:00
|
|
|
|
CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (!_cairo_operator_bounded_by_source (op)) {
|
2009-11-09 14:21:40 +01:00
|
|
|
|
CGContextBeginPath (surface->cgContext);
|
|
|
|
|
|
CGContextAddRect (surface->cgContext, surface->sourceImageRect);
|
|
|
|
|
|
CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
|
|
|
|
|
|
CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 0);
|
|
|
|
|
|
CGContextEOFillPath (surface->cgContext);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else
|
|
|
|
|
|
CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-02-27 20:03:26 -05:00
|
|
|
|
/*
|
|
|
|
|
|
* get source/dest image implementation
|
|
|
|
|
|
*/
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
/* Read the image from the surface's front buffer */
|
|
|
|
|
|
static cairo_int_status_t
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_get_image (cairo_quartz_surface_t *surface,
|
2008-01-23 21:30:42 -08:00
|
|
|
|
cairo_image_surface_t **image_out)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
|
|
|
|
|
unsigned char *imageData;
|
|
|
|
|
|
cairo_image_surface_t *isurf;
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (IS_EMPTY (surface)) {
|
2007-12-04 13:49:59 -08:00
|
|
|
|
*image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
if (surface->imageSurfaceEquiv) {
|
2010-07-27 15:59:31 +02:00
|
|
|
|
*image_out = (cairo_image_surface_t*) cairo_surface_reference (surface->imageSurfaceEquiv);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
|
2007-02-20 12:15:35 -08:00
|
|
|
|
unsigned int stride;
|
|
|
|
|
|
unsigned int bitinfo;
|
|
|
|
|
|
unsigned int bpc, bpp;
|
|
|
|
|
|
CGColorSpaceRef colorspace;
|
|
|
|
|
|
unsigned int color_comps;
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
imageData = (unsigned char *) CGBitmapContextGetData (surface->cgContext);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
#ifdef USE_10_3_WORKAROUNDS
|
|
|
|
|
|
bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext);
|
|
|
|
|
|
#else
|
|
|
|
|
|
bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
|
|
|
|
|
|
bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
|
|
|
|
|
|
bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext);
|
|
|
|
|
|
|
|
|
|
|
|
// let's hope they don't add YUV under us
|
|
|
|
|
|
colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
|
2010-07-27 15:59:31 +02:00
|
|
|
|
color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
// 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 (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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-27 20:03:26 -05:00
|
|
|
|
/*
|
|
|
|
|
|
* Cairo surface backend implementations
|
|
|
|
|
|
*/
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
static cairo_status_t
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_finish (void *abstract_surface)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2007-03-06 23:24:33 +00:00
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
|
2005-01-16 08:35:14 +00:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (IS_EMPTY (surface))
|
2007-12-04 13:49:59 -08:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/* Restore our saved gstate that we use to reset clipping */
|
|
|
|
|
|
CGContextRestoreGState (surface->cgContext);
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
_cairo_surface_clipper_reset (&surface->clipper);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
CGContextRelease (surface->cgContext);
|
|
|
|
|
|
|
|
|
|
|
|
surface->cgContext = NULL;
|
|
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
if (surface->imageSurfaceEquiv) {
|
|
|
|
|
|
cairo_surface_destroy (surface->imageSurfaceEquiv);
|
|
|
|
|
|
surface->imageSurfaceEquiv = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (surface->imageData) {
|
|
|
|
|
|
free (surface->imageData);
|
|
|
|
|
|
surface->imageData = NULL;
|
|
|
|
|
|
}
|
2006-06-06 15:25:49 -07:00
|
|
|
|
|
2005-05-11 15:39:26 +00:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2005-01-16 08:35:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2005-03-29 11:24:10 +00:00
|
|
|
|
static cairo_status_t
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_acquire_source_image (void *abstract_surface,
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_image_surface_t **image_out,
|
|
|
|
|
|
void **image_extra)
|
|
|
|
|
|
{
|
2007-06-23 12:34:24 +01:00
|
|
|
|
cairo_int_status_t status;
|
2007-03-06 23:24:33 +00:00
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
//ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-01-23 21:30:42 -08:00
|
|
|
|
status = _cairo_quartz_get_image (surface, image_out);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (status))
|
2007-06-23 12:34:24 +01:00
|
|
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
|
|
|
2008-01-23 21:30:42 -08:00
|
|
|
|
*image_extra = NULL;
|
|
|
|
|
|
|
2007-06-23 12:34:24 +01:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-25 20:15:15 +01:00
|
|
|
|
static cairo_surface_t *
|
|
|
|
|
|
_cairo_quartz_surface_snapshot (void *abstract_surface)
|
|
|
|
|
|
{
|
|
|
|
|
|
cairo_int_status_t status;
|
|
|
|
|
|
cairo_quartz_surface_t *surface = abstract_surface;
|
|
|
|
|
|
cairo_image_surface_t *image;
|
|
|
|
|
|
|
|
|
|
|
|
if (surface->imageSurfaceEquiv)
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
|
|
status = _cairo_quartz_get_image (surface, &image);
|
|
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
|
|
|
|
|
|
|
|
return &image->base;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-05-14 23:46:14 -05:00
|
|
|
|
static void
|
|
|
|
|
|
_cairo_quartz_surface_release_source_image (void *abstract_surface,
|
2010-07-27 16:06:27 +02:00
|
|
|
|
cairo_image_surface_t *image,
|
|
|
|
|
|
void *image_extra)
|
2007-05-14 23:46:14 -05:00
|
|
|
|
{
|
2010-07-27 16:06:27 +02:00
|
|
|
|
cairo_surface_destroy (&image->base);
|
2007-05-14 23:46:14 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
static cairo_status_t
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_acquire_dest_image (void *abstract_surface,
|
2007-06-18 16:56:24 -07:00
|
|
|
|
cairo_rectangle_int_t *interest_rect,
|
|
|
|
|
|
cairo_image_surface_t **image_out,
|
|
|
|
|
|
cairo_rectangle_int_t *image_rect,
|
|
|
|
|
|
void **image_extra)
|
2005-01-16 08:35:14 +00:00
|
|
|
|
{
|
2007-03-06 23:24:33 +00:00
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_int_status_t status;
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-01-23 21:30:42 -08:00
|
|
|
|
status = _cairo_quartz_get_image (surface, image_out);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (status))
|
2007-06-23 12:34:24 +01:00
|
|
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-01-23 21:30:42 -08:00
|
|
|
|
*image_rect = surface->extents;
|
|
|
|
|
|
*image_extra = NULL;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_release_dest_image (void *abstract_surface,
|
2007-06-18 16:56:24 -07:00
|
|
|
|
cairo_rectangle_int_t *interest_rect,
|
|
|
|
|
|
cairo_image_surface_t *image,
|
|
|
|
|
|
cairo_rectangle_int_t *image_rect,
|
|
|
|
|
|
void *image_extra)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2008-01-23 21:30:42 -08:00
|
|
|
|
//cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
//ND ((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 16:06:27 +02:00
|
|
|
|
cairo_surface_destroy (&image->base);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static cairo_surface_t *
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_create_similar (void *abstract_surface,
|
2010-07-27 17:24:08 +02:00
|
|
|
|
cairo_content_t content,
|
|
|
|
|
|
int width,
|
|
|
|
|
|
int height)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2010-10-12 22:52:54 +02:00
|
|
|
|
cairo_quartz_surface_t *surface, *similar_quartz;
|
|
|
|
|
|
cairo_surface_t *similar;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_format_t format;
|
2005-03-29 11:24:10 +00:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
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;
|
2007-12-04 13:54:32 -08:00
|
|
|
|
|
2007-11-06 15:40:30 -08:00
|
|
|
|
// verify width and height of surface
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (!_cairo_quartz_verify_surface_size (width, height)) {
|
2008-10-16 12:19:09 +01:00
|
|
|
|
return _cairo_surface_create_in_error (_cairo_error
|
2008-12-15 09:32:43 +01:00
|
|
|
|
(CAIRO_STATUS_INVALID_SIZE));
|
2007-11-06 15:40:30 -08:00
|
|
|
|
}
|
2005-01-16 08:35:14 +00:00
|
|
|
|
|
2010-10-12 22:52:54 +02:00
|
|
|
|
similar = cairo_quartz_surface_create (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;
|
|
|
|
|
|
|
|
|
|
|
|
return similar;
|
2005-01-16 08:35:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static cairo_status_t
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_clone_similar (void *abstract_surface,
|
2008-02-25 21:06:21 -05:00
|
|
|
|
cairo_surface_t *src,
|
|
|
|
|
|
int src_x,
|
|
|
|
|
|
int src_y,
|
|
|
|
|
|
int width,
|
|
|
|
|
|
int height,
|
2008-09-27 17:24:57 +01:00
|
|
|
|
int *clone_offset_x,
|
|
|
|
|
|
int *clone_offset_y,
|
2008-02-25 21:06:21 -05:00
|
|
|
|
cairo_surface_t **clone_out)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2007-03-06 23:24:33 +00:00
|
|
|
|
cairo_quartz_surface_t *new_surface = NULL;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_format_t new_format;
|
2007-12-04 13:49:59 -08:00
|
|
|
|
CGImageRef quartz_image = NULL;
|
2008-03-17 17:37:22 -07:00
|
|
|
|
cairo_status_t status;
|
2007-12-04 13:54:32 -08:00
|
|
|
|
|
2007-11-06 15:40:30 -08:00
|
|
|
|
*clone_out = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
// verify width and height of surface
|
2010-07-29 16:05:51 +02:00
|
|
|
|
if (!_cairo_quartz_verify_surface_size (width, height))
|
2007-11-06 15:40:30 -08:00
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
2006-06-06 15:25:49 -07:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
if (width == 0 || height == 0) {
|
2010-07-27 16:06:27 +02:00
|
|
|
|
*clone_out = &_cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
|
|
|
|
|
|
width, height)->base;
|
2008-09-27 17:24:57 +01:00
|
|
|
|
*clone_offset_x = 0;
|
|
|
|
|
|
*clone_offset_y = 0;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-07-27 18:04:36 +02:00
|
|
|
|
if (_cairo_surface_is_quartz (src)) {
|
2007-03-06 23:24:33 +00:00
|
|
|
|
cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src;
|
2007-12-04 13:49:59 -08:00
|
|
|
|
|
2010-07-27 16:06:27 +02:00
|
|
|
|
if (IS_EMPTY (qsurf)) {
|
|
|
|
|
|
*clone_out = &_cairo_quartz_surface_create_internal (NULL,
|
|
|
|
|
|
CAIRO_CONTENT_COLOR_ALPHA,
|
|
|
|
|
|
qsurf->extents.width,
|
|
|
|
|
|
qsurf->extents.height)->base;
|
2008-09-27 17:24:57 +01:00
|
|
|
|
*clone_offset_x = 0;
|
|
|
|
|
|
*clone_offset_y = 0;
|
2007-12-04 13:49:59 -08:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
2006-04-10 22:24:02 +02:00
|
|
|
|
}
|
2005-01-16 08:35:14 +00:00
|
|
|
|
|
2010-07-27 14:38:28 +02:00
|
|
|
|
status = _cairo_surface_to_cgimage (src, &quartz_image);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (status))
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
2005-03-29 11:24:10 +00:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
new_format = CAIRO_FORMAT_ARGB32; /* assumed */
|
2010-07-29 16:05:51 +02:00
|
|
|
|
if (_cairo_surface_is_image (src))
|
2008-02-25 21:06:21 -05:00
|
|
|
|
new_format = ((cairo_image_surface_t *) src)->format;
|
|
|
|
|
|
|
2007-03-06 23:24:33 +00:00
|
|
|
|
new_surface = (cairo_quartz_surface_t *)
|
2008-02-25 21:06:21 -05:00
|
|
|
|
cairo_quartz_surface_create (new_format, width, height);
|
2008-03-17 17:37:22 -07:00
|
|
|
|
|
|
|
|
|
|
if (quartz_image == NULL)
|
|
|
|
|
|
goto FINISH;
|
|
|
|
|
|
|
2007-11-06 15:40:30 -08:00
|
|
|
|
if (!new_surface || new_surface->base.status) {
|
|
|
|
|
|
CGImageRelease (quartz_image);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
2007-11-06 15:40:30 -08:00
|
|
|
|
}
|
2006-04-10 22:24:02 +02:00
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
CGContextSaveGState (new_surface->cgContext);
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextSetCompositeOperation (new_surface->cgContext,
|
|
|
|
|
|
kPrivateCGCompositeCopy);
|
|
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
CGContextTranslateCTM (new_surface->cgContext, -src_x, -src_y);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextDrawImage (new_surface->cgContext,
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CGRectMake (0, 0, CGImageGetWidth (quartz_image), CGImageGetHeight (quartz_image)),
|
2007-02-20 12:15:35 -08:00
|
|
|
|
quartz_image);
|
|
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
CGContextRestoreGState (new_surface->cgContext);
|
|
|
|
|
|
|
|
|
|
|
|
CGImageRelease (quartz_image);
|
2008-03-17 17:37:22 -07:00
|
|
|
|
|
2008-09-27 00:18:07 +01:00
|
|
|
|
FINISH:
|
2008-09-27 17:24:57 +01:00
|
|
|
|
*clone_offset_x = src_x;
|
|
|
|
|
|
*clone_offset_y = src_y;
|
2010-07-27 16:06:27 +02:00
|
|
|
|
*clone_out = &new_surface->base;
|
2005-03-29 11:24:10 +00:00
|
|
|
|
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
static cairo_bool_t
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_get_extents (void *abstract_surface,
|
2007-06-18 16:56:24 -07:00
|
|
|
|
cairo_rectangle_int_t *extents)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2007-03-06 23:24:33 +00:00
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
*extents = surface->extents;
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
return TRUE;
|
2005-01-16 08:35:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
static cairo_int_status_t
|
2010-07-28 10:26:50 +02:00
|
|
|
|
_cairo_quartz_surface_paint_cg (cairo_quartz_surface_t *surface,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
cairo_clip_t *clip)
|
2006-04-10 22:24:02 +02:00
|
|
|
|
{
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
|
2010-10-12 22:52:54 +02:00
|
|
|
|
cairo_rectangle_int_t extents;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (IS_EMPTY (surface))
|
2010-07-27 18:48:00 +02:00
|
|
|
|
return CAIRO_INT_STATUS_NOTHING_TO_DO;
|
2007-12-04 13:49:59 -08:00
|
|
|
|
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
|
|
|
|
|
if (unlikely (rv))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
2010-10-12 22:52:54 +02:00
|
|
|
|
extents = surface->virtual_extents;
|
|
|
|
|
|
extents.x -= surface->base.device_transform.x0;
|
|
|
|
|
|
extents.y -= surface->base.device_transform.y0;
|
|
|
|
|
|
_cairo_rectangle_union (&extents, &surface->extents);
|
2010-07-28 19:03:13 +02:00
|
|
|
|
rv = _cairo_quartz_setup_source_safe (surface, op, source, &extents);
|
2010-10-08 09:49:51 +02:00
|
|
|
|
if (unlikely (rv))
|
|
|
|
|
|
return rv;
|
2006-06-06 15:25:49 -07:00
|
|
|
|
|
2010-07-29 15:06:39 +02:00
|
|
|
|
if (surface->action == DO_DIRECT) {
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CGContextFillRect (surface->cgContext, CGRectMake (surface->extents.x,
|
|
|
|
|
|
surface->extents.y,
|
|
|
|
|
|
surface->extents.width,
|
|
|
|
|
|
surface->extents.height));
|
2010-07-29 15:50:21 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
_cairo_quartz_draw_source (surface, op);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
2006-04-10 22:24:02 +02:00
|
|
|
|
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_teardown_source (surface, source);
|
2006-06-06 15:25:49 -07:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "-- paint\n"));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return rv;
|
2006-04-10 22:24:02 +02:00
|
|
|
|
}
|
2005-01-16 08:35:14 +00:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
static cairo_int_status_t
|
2010-01-29 12:06:13 +01:00
|
|
|
|
_cairo_quartz_surface_paint (void *abstract_surface,
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_operator_t op,
|
2008-11-11 08:31:23 -05:00
|
|
|
|
const cairo_pattern_t *source,
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
cairo_clip_t *clip)
|
2010-01-29 12:06:13 +01:00
|
|
|
|
{
|
|
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
|
|
|
|
|
cairo_int_status_t rv;
|
|
|
|
|
|
cairo_image_surface_t *image;
|
|
|
|
|
|
|
2010-07-28 10:26:50 +02:00
|
|
|
|
rv = _cairo_quartz_surface_paint_cg (surface,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
op,
|
|
|
|
|
|
source,
|
|
|
|
|
|
clip);
|
|
|
|
|
|
|
|
|
|
|
|
if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
|
|
|
|
rv = _cairo_quartz_get_image (surface, &image);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (likely (rv == CAIRO_STATUS_SUCCESS)) {
|
2010-01-29 12:06:13 +01:00
|
|
|
|
rv = _cairo_surface_paint (&image->base, op, source, clip);
|
|
|
|
|
|
cairo_surface_destroy (&image->base);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static cairo_int_status_t
|
2010-07-28 10:26:50 +02:00
|
|
|
|
_cairo_quartz_surface_fill_cg (cairo_quartz_surface_t *surface,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
cairo_path_fixed_t *path,
|
|
|
|
|
|
cairo_fill_rule_t fill_rule,
|
|
|
|
|
|
double tolerance,
|
|
|
|
|
|
cairo_antialias_t antialias,
|
|
|
|
|
|
cairo_clip_t *clip)
|
2005-01-16 08:35:14 +00:00
|
|
|
|
{
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGPathRef path_for_unbounded = NULL;
|
2010-10-12 22:52:54 +02:00
|
|
|
|
cairo_rectangle_int_t extents;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (IS_EMPTY (surface))
|
2010-07-27 18:48:00 +02:00
|
|
|
|
return CAIRO_INT_STATUS_NOTHING_TO_DO;
|
2007-12-04 13:49:59 -08:00
|
|
|
|
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
|
|
|
|
|
if (unlikely (rv))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
2010-10-12 22:52:54 +02:00
|
|
|
|
extents = surface->virtual_extents;
|
|
|
|
|
|
extents.x -= surface->base.device_transform.x0;
|
|
|
|
|
|
extents.y -= surface->base.device_transform.y0;
|
|
|
|
|
|
_cairo_rectangle_union (&extents, &surface->extents);
|
2010-07-28 19:03:13 +02:00
|
|
|
|
rv = _cairo_quartz_setup_source_safe (surface, op, source, &extents);
|
2010-10-08 09:49:51 +02:00
|
|
|
|
if (unlikely (rv))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
|
|
|
|
CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
|
2005-11-14 12:57:31 +00:00
|
|
|
|
|
2010-04-27 10:59:09 +02:00
|
|
|
|
_cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
|
2006-06-06 15:25:49 -07:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
|
2008-03-12 18:11:57 -07:00
|
|
|
|
path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
|
|
|
|
|
|
|
2010-07-29 15:06:39 +02:00
|
|
|
|
if (surface->action == DO_DIRECT) {
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (fill_rule == CAIRO_FILL_RULE_WINDING)
|
|
|
|
|
|
CGContextFillPath (surface->cgContext);
|
|
|
|
|
|
else
|
|
|
|
|
|
CGContextEOFillPath (surface->cgContext);
|
2010-07-29 15:50:21 +02:00
|
|
|
|
} else {
|
2007-05-29 23:11:49 +01:00
|
|
|
|
if (fill_rule == CAIRO_FILL_RULE_WINDING)
|
|
|
|
|
|
CGContextClip (surface->cgContext);
|
|
|
|
|
|
else
|
|
|
|
|
|
CGContextEOClip (surface->cgContext);
|
2007-07-01 16:11:07 +01:00
|
|
|
|
|
2010-07-29 15:50:21 +02:00
|
|
|
|
_cairo_quartz_draw_source (surface, op);
|
2005-03-29 11:24:10 +00:00
|
|
|
|
}
|
2006-06-06 15:25:49 -07:00
|
|
|
|
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_teardown_source (surface, source);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-03-12 18:11:57 -07:00
|
|
|
|
if (path_for_unbounded) {
|
|
|
|
|
|
unbounded_op_data_t ub;
|
|
|
|
|
|
ub.op = UNBOUNDED_STROKE_FILL;
|
|
|
|
|
|
ub.u.stroke_fill.cgPath = path_for_unbounded;
|
|
|
|
|
|
ub.u.stroke_fill.fill_rule = fill_rule;
|
|
|
|
|
|
|
|
|
|
|
|
_cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
|
|
|
|
|
|
CGPathRelease (path_for_unbounded);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "-- fill\n"));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return rv;
|
2005-01-16 08:35:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static cairo_int_status_t
|
2010-01-29 12:06:13 +01:00
|
|
|
|
_cairo_quartz_surface_fill (void *abstract_surface,
|
|
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
cairo_path_fixed_t *path,
|
|
|
|
|
|
cairo_fill_rule_t fill_rule,
|
|
|
|
|
|
double tolerance,
|
|
|
|
|
|
cairo_antialias_t antialias,
|
|
|
|
|
|
cairo_clip_t *clip)
|
|
|
|
|
|
{
|
|
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
|
|
|
|
|
cairo_int_status_t rv;
|
|
|
|
|
|
cairo_image_surface_t *image;
|
|
|
|
|
|
|
2010-07-28 10:26:50 +02:00
|
|
|
|
rv = _cairo_quartz_surface_fill_cg (surface,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
op,
|
|
|
|
|
|
source,
|
|
|
|
|
|
path,
|
|
|
|
|
|
fill_rule,
|
|
|
|
|
|
tolerance,
|
|
|
|
|
|
antialias,
|
|
|
|
|
|
clip);
|
|
|
|
|
|
|
|
|
|
|
|
if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
|
|
|
|
rv = _cairo_quartz_get_image (surface, &image);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (likely (rv == CAIRO_STATUS_SUCCESS)) {
|
2010-01-29 12:06:13 +01:00
|
|
|
|
rv = _cairo_surface_fill (&image->base, op, source,
|
|
|
|
|
|
path, fill_rule, tolerance, antialias,
|
|
|
|
|
|
clip);
|
|
|
|
|
|
cairo_surface_destroy (&image->base);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static cairo_int_status_t
|
2010-07-28 10:26:50 +02:00
|
|
|
|
_cairo_quartz_surface_stroke_cg (cairo_quartz_surface_t *surface,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
cairo_path_fixed_t *path,
|
|
|
|
|
|
const cairo_stroke_style_t *style,
|
|
|
|
|
|
const cairo_matrix_t *ctm,
|
|
|
|
|
|
const cairo_matrix_t *ctm_inverse,
|
|
|
|
|
|
double tolerance,
|
|
|
|
|
|
cairo_antialias_t antialias,
|
|
|
|
|
|
cairo_clip_t *clip)
|
2005-01-16 08:35:14 +00:00
|
|
|
|
{
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
|
2008-03-25 11:17:57 -07:00
|
|
|
|
CGAffineTransform origCTM, strokeTransform;
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGPathRef path_for_unbounded = NULL;
|
2010-10-12 22:52:54 +02:00
|
|
|
|
cairo_rectangle_int_t extents;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
|
2005-01-16 08:35:14 +00:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (IS_EMPTY (surface))
|
2010-07-27 18:48:00 +02:00
|
|
|
|
return CAIRO_INT_STATUS_NOTHING_TO_DO;
|
2007-12-04 13:49:59 -08:00
|
|
|
|
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
|
|
|
|
|
if (unlikely (rv))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
2008-03-07 15:35:31 -08:00
|
|
|
|
// 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 (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextSetLineWidth (surface->cgContext, style->line_width);
|
2007-03-06 23:24:33 +00:00
|
|
|
|
CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
|
|
|
|
|
|
CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
|
2008-03-25 11:17:57 -07:00
|
|
|
|
|
|
|
|
|
|
origCTM = CGContextGetCTM (surface->cgContext);
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (style->dash && style->num_dashes) {
|
|
|
|
|
|
#define STATIC_DASH 32
|
2009-12-29 08:47:09 +01:00
|
|
|
|
cairo_quartz_float_t sdash[STATIC_DASH];
|
|
|
|
|
|
cairo_quartz_float_t *fdash = sdash;
|
2007-06-24 23:53:47 +01:00
|
|
|
|
unsigned int max_dashes = style->num_dashes;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
unsigned int k;
|
2007-06-24 23:53:47 +01:00
|
|
|
|
|
2010-01-30 09:29:48 +01:00
|
|
|
|
if (style->num_dashes%2)
|
|
|
|
|
|
max_dashes *= 2;
|
|
|
|
|
|
if (max_dashes > STATIC_DASH)
|
2009-12-29 08:47:09 +01:00
|
|
|
|
fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (fdash == NULL))
|
2010-01-30 09:29:48 +01:00
|
|
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
|
|
|
|
|
|
|
|
for (k = 0; k < max_dashes; k++)
|
2009-12-29 08:47:09 +01:00
|
|
|
|
fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
|
2010-01-30 09:29:48 +01:00
|
|
|
|
|
|
|
|
|
|
CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (fdash != sdash)
|
|
|
|
|
|
free (fdash);
|
2009-11-09 20:17:35 +01:00
|
|
|
|
} else
|
|
|
|
|
|
CGContextSetLineDash (surface->cgContext, 0, NULL, 0);
|
|
|
|
|
|
|
2010-10-12 22:52:54 +02:00
|
|
|
|
extents = surface->virtual_extents;
|
|
|
|
|
|
extents.x -= surface->base.device_transform.x0;
|
|
|
|
|
|
extents.y -= surface->base.device_transform.y0;
|
|
|
|
|
|
_cairo_rectangle_union (&extents, &surface->extents);
|
2010-07-28 19:03:13 +02:00
|
|
|
|
rv = _cairo_quartz_setup_source_safe (surface, op, source, &extents);
|
2010-10-08 09:49:51 +02:00
|
|
|
|
if (unlikely (rv))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
2010-04-27 10:59:09 +02:00
|
|
|
|
_cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-03-12 18:11:57 -07:00
|
|
|
|
if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
|
|
|
|
|
|
path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
|
|
|
|
|
|
|
2010-04-07 18:34:25 +02:00
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
|
|
|
|
|
|
CGContextConcatCTM (surface->cgContext, strokeTransform);
|
|
|
|
|
|
|
2010-07-29 15:06:39 +02:00
|
|
|
|
if (surface->action == DO_DIRECT) {
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextStrokePath (surface->cgContext);
|
2010-07-29 15:50:21 +02:00
|
|
|
|
} else {
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextReplacePathWithStrokedPath (surface->cgContext);
|
|
|
|
|
|
CGContextClip (surface->cgContext);
|
|
|
|
|
|
|
2008-03-25 11:17:57 -07:00
|
|
|
|
CGContextSetCTM (surface->cgContext, origCTM);
|
2010-07-29 15:50:21 +02:00
|
|
|
|
_cairo_quartz_draw_source (surface, op);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_teardown_source (surface, source);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-03-12 18:11:57 -07:00
|
|
|
|
if (path_for_unbounded) {
|
2010-07-28 19:33:53 +02:00
|
|
|
|
unbounded_op_data_t ub;
|
|
|
|
|
|
ub.op = UNBOUNDED_STROKE_FILL;
|
|
|
|
|
|
ub.u.stroke_fill.fill_rule = CAIRO_FILL_RULE_WINDING;
|
2008-03-12 18:11:57 -07:00
|
|
|
|
|
|
|
|
|
|
CGContextBeginPath (surface->cgContext);
|
|
|
|
|
|
CGContextAddPath (surface->cgContext, path_for_unbounded);
|
|
|
|
|
|
CGPathRelease (path_for_unbounded);
|
|
|
|
|
|
|
2010-07-28 19:33:53 +02:00
|
|
|
|
CGContextSaveGState (surface->cgContext);
|
2010-04-07 18:34:25 +02:00
|
|
|
|
CGContextConcatCTM (surface->cgContext, strokeTransform);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGContextReplacePathWithStrokedPath (surface->cgContext);
|
2010-07-28 19:33:53 +02:00
|
|
|
|
CGContextRestoreGState (surface->cgContext);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
|
2010-07-28 19:33:53 +02:00
|
|
|
|
ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (surface->cgContext);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
|
2010-07-28 19:33:53 +02:00
|
|
|
|
_cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
|
|
|
|
|
|
CGPathRelease (ub.u.stroke_fill.cgPath);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "-- stroke\n"));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return rv;
|
2005-01-16 08:35:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-29 12:06:13 +01:00
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_surface_stroke (void *abstract_surface,
|
|
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
cairo_path_fixed_t *path,
|
|
|
|
|
|
const cairo_stroke_style_t *style,
|
|
|
|
|
|
const cairo_matrix_t *ctm,
|
|
|
|
|
|
const cairo_matrix_t *ctm_inverse,
|
|
|
|
|
|
double tolerance,
|
|
|
|
|
|
cairo_antialias_t antialias,
|
|
|
|
|
|
cairo_clip_t *clip)
|
|
|
|
|
|
{
|
|
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
|
|
|
|
|
cairo_int_status_t rv;
|
|
|
|
|
|
cairo_image_surface_t *image;
|
|
|
|
|
|
|
2010-07-28 10:26:50 +02:00
|
|
|
|
rv = _cairo_quartz_surface_stroke_cg (surface, op, source,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
path, style, ctm, ctm_inverse,
|
|
|
|
|
|
tolerance, antialias,
|
|
|
|
|
|
clip);
|
|
|
|
|
|
|
|
|
|
|
|
if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
|
|
|
|
rv = _cairo_quartz_get_image (surface, &image);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (likely (rv == CAIRO_STATUS_SUCCESS)) {
|
2010-01-29 12:06:13 +01:00
|
|
|
|
rv = _cairo_surface_stroke (&image->base, op, source,
|
|
|
|
|
|
path, style, ctm, ctm_inverse,
|
|
|
|
|
|
tolerance, antialias,
|
|
|
|
|
|
clip);
|
|
|
|
|
|
cairo_surface_destroy (&image->base);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-03-17 17:37:19 -07:00
|
|
|
|
#if CAIRO_HAS_QUARTZ_FONT
|
2005-05-11 15:39:26 +00:00
|
|
|
|
static cairo_int_status_t
|
2010-07-28 10:26:50 +02:00
|
|
|
|
_cairo_quartz_surface_show_glyphs_cg (cairo_quartz_surface_t *surface,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
cairo_glyph_t *glyphs,
|
|
|
|
|
|
int num_glyphs,
|
|
|
|
|
|
cairo_scaled_font_t *scaled_font,
|
|
|
|
|
|
cairo_clip_t *clip,
|
|
|
|
|
|
int *remaining_glyphs)
|
2005-04-19 16:29:04 +00:00
|
|
|
|
{
|
2010-06-24 15:26:03 +02:00
|
|
|
|
CGAffineTransform textTransform, ctm, invTextTransform;
|
2007-06-13 01:27:36 +01:00
|
|
|
|
#define STATIC_BUF_SIZE 64
|
|
|
|
|
|
CGGlyph glyphs_static[STATIC_BUF_SIZE];
|
|
|
|
|
|
CGSize cg_advances_static[STATIC_BUF_SIZE];
|
|
|
|
|
|
CGGlyph *cg_glyphs = &glyphs_static[0];
|
|
|
|
|
|
CGSize *cg_advances = &cg_advances_static[0];
|
2007-06-13 01:04:54 +01:00
|
|
|
|
|
2010-10-12 22:52:54 +02:00
|
|
|
|
cairo_rectangle_int_t extents;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
|
2009-12-29 08:47:09 +01:00
|
|
|
|
cairo_quartz_float_t xprev, yprev;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
int i;
|
2008-03-12 18:11:57 -07:00
|
|
|
|
CGFontRef cgfref = NULL;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-03-06 16:52:54 -08:00
|
|
|
|
cairo_bool_t isClipping = FALSE;
|
2008-03-14 23:23:16 -07:00
|
|
|
|
cairo_bool_t didForceFontSmoothing = FALSE;
|
2008-03-06 16:52:54 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (IS_EMPTY (surface))
|
2010-07-27 18:48:00 +02:00
|
|
|
|
return CAIRO_INT_STATUS_NOTHING_TO_DO;
|
2007-12-04 13:49:59 -08:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (num_glyphs <= 0)
|
2010-07-27 18:48:00 +02:00
|
|
|
|
return CAIRO_INT_STATUS_NOTHING_TO_DO;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-03-17 17:37:19 -07:00
|
|
|
|
if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
|
|
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
|
|
|
|
|
if (unlikely (rv))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
2010-10-12 22:52:54 +02:00
|
|
|
|
extents = surface->virtual_extents;
|
|
|
|
|
|
extents.x -= surface->base.device_transform.x0;
|
|
|
|
|
|
extents.y -= surface->base.device_transform.y0;
|
|
|
|
|
|
_cairo_rectangle_union (&extents, &surface->extents);
|
2010-07-28 19:03:13 +02:00
|
|
|
|
rv = _cairo_quartz_setup_source_safe (surface, op, source, &extents);
|
2010-10-08 09:49:51 +02:00
|
|
|
|
if (unlikely (rv))
|
|
|
|
|
|
return rv;
|
Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops.
-- Add a parameter to _cairo_quartz_setup_source so we can pass down the
extents of the object we're drawing
-- Compute fill/stroke/glyph extents and pass them down in the cases we need to
(repeating/reflecting gradients)
-- Pass those extents on down to where we set up the gradients
-- Make _cairo_quartz_setup_linear_source fall back to pixman for the
degenerate case where the linear gradient vector has no length
-- In CreateRepeatingRadialGradientFunction and
CreateRepeatingLinearGradientFunction, use the object extents (or surface
extents, for the paint() case) instead of the clip box to calculate the
parameters for the gradient
-- I've changed the way CreateRepeatingLinearGradientFunction calculates the
repetition count. The new approach gives much more precise bounds on the number
of repetitions needed (and is very similar to what we do for radial gradients).
This is important because if we specify a much larger input range than we
really need for our gradient color function, Quartz samples it too coarsely
over the range we actually care about, and the gradients look bad.
For example, suppose start = (5,0), end = (6,10), the CTM is identity and the
bounds we want to cover is (0,0)-(10,10). I think the current approach sets up
the gradient to be repeated 10 times. In fact only 3 repetitions are needed.
Also, using 'width' here didn't look right:
- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y))
/ dy
From https://bugzilla.mozilla.org/show_bug.cgi?id=508730
2010-05-11 13:59:52 -04:00
|
|
|
|
|
2010-07-29 15:06:39 +02:00
|
|
|
|
if (surface->action == DO_DIRECT) {
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
|
2010-07-29 15:50:21 +02:00
|
|
|
|
} else {
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
|
2008-03-06 16:52:54 -08:00
|
|
|
|
isClipping = TRUE;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-01-15 14:27:14 -08:00
|
|
|
|
/* this doesn't addref */
|
2008-03-17 17:37:19 -07:00
|
|
|
|
cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextSetFont (surface->cgContext, cgfref);
|
|
|
|
|
|
CGContextSetFontSize (surface->cgContext, 1.0);
|
|
|
|
|
|
|
2008-04-01 12:44:32 -07:00
|
|
|
|
switch (scaled_font->options.antialias) {
|
|
|
|
|
|
case CAIRO_ANTIALIAS_SUBPIXEL:
|
|
|
|
|
|
CGContextSetShouldAntialias (surface->cgContext, TRUE);
|
|
|
|
|
|
CGContextSetShouldSmoothFonts (surface->cgContext, TRUE);
|
|
|
|
|
|
if (CGContextSetAllowsFontSmoothingPtr &&
|
|
|
|
|
|
!CGContextGetAllowsFontSmoothingPtr (surface->cgContext))
|
|
|
|
|
|
{
|
|
|
|
|
|
didForceFontSmoothing = TRUE;
|
|
|
|
|
|
CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case CAIRO_ANTIALIAS_NONE:
|
|
|
|
|
|
CGContextSetShouldAntialias (surface->cgContext, FALSE);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case CAIRO_ANTIALIAS_GRAY:
|
|
|
|
|
|
CGContextSetShouldAntialias (surface->cgContext, TRUE);
|
|
|
|
|
|
CGContextSetShouldSmoothFonts (surface->cgContext, FALSE);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case CAIRO_ANTIALIAS_DEFAULT:
|
|
|
|
|
|
/* Don't do anything */
|
|
|
|
|
|
break;
|
2008-03-08 15:16:05 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (num_glyphs > STATIC_BUF_SIZE) {
|
2010-07-27 15:59:31 +02:00
|
|
|
|
cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph));
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (cg_glyphs == NULL)) {
|
2007-11-12 23:56:01 +00:00
|
|
|
|
rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
|
|
goto BAIL;
|
|
|
|
|
|
}
|
2007-10-04 00:38:12 +01:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
cg_advances = (CGSize*) _cairo_malloc_ab (num_glyphs, sizeof (CGSize));
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (cg_advances == NULL)) {
|
2007-11-12 23:56:01 +00:00
|
|
|
|
rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
|
|
goto BAIL;
|
2007-10-04 00:38:12 +01:00
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-06-24 15:26:03 +02:00
|
|
|
|
textTransform = CGAffineTransformMake (scaled_font->scale.xx,
|
|
|
|
|
|
scaled_font->scale.yx,
|
|
|
|
|
|
-scaled_font->scale.xy,
|
|
|
|
|
|
-scaled_font->scale.yy,
|
|
|
|
|
|
0, 0);
|
|
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (&scaled_font->scale_inverse, &invTextTransform);
|
2008-03-06 16:52:54 -08:00
|
|
|
|
|
2010-06-24 15:26:03 +02:00
|
|
|
|
CGContextSetTextMatrix (surface->cgContext, CGAffineTransformIdentity);
|
2008-03-06 16:52:54 -08:00
|
|
|
|
|
|
|
|
|
|
/* Convert our glyph positions to glyph advances. We need n-1 advances,
|
|
|
|
|
|
* since the advance at index 0 is applied after glyph 0. */
|
2007-06-13 01:27:36 +01:00
|
|
|
|
xprev = glyphs[0].x;
|
|
|
|
|
|
yprev = glyphs[0].y;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
cg_glyphs[0] = glyphs[0].index;
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 1; i < num_glyphs; i++) {
|
2009-12-29 08:47:09 +01:00
|
|
|
|
cairo_quartz_float_t xf = glyphs[i].x;
|
|
|
|
|
|
cairo_quartz_float_t yf = glyphs[i].y;
|
2007-06-13 01:27:36 +01:00
|
|
|
|
cg_glyphs[i] = glyphs[i].index;
|
2010-07-27 15:59:31 +02:00
|
|
|
|
cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
|
2007-04-06 00:00:33 +01:00
|
|
|
|
xprev = xf;
|
|
|
|
|
|
yprev = yf;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-03-06 16:52:54 -08:00
|
|
|
|
/* Translate to the first glyph's position before drawing */
|
|
|
|
|
|
ctm = CGContextGetCTM (surface->cgContext);
|
|
|
|
|
|
CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y);
|
2010-06-24 15:26:03 +02:00
|
|
|
|
CGContextConcatCTM (surface->cgContext, textTransform);
|
2008-03-06 16:52:54 -08:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextShowGlyphsWithAdvances (surface->cgContext,
|
|
|
|
|
|
cg_glyphs,
|
|
|
|
|
|
cg_advances,
|
|
|
|
|
|
num_glyphs);
|
|
|
|
|
|
|
2008-03-06 16:52:54 -08:00
|
|
|
|
CGContextSetCTM (surface->cgContext, ctm);
|
|
|
|
|
|
|
2010-07-29 15:50:21 +02:00
|
|
|
|
if (surface->action != DO_DIRECT)
|
|
|
|
|
|
_cairo_quartz_draw_source (surface, op);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2007-11-12 23:56:01 +00:00
|
|
|
|
BAIL:
|
2008-03-12 18:11:57 -07:00
|
|
|
|
_cairo_quartz_teardown_source (surface, source);
|
|
|
|
|
|
|
2008-03-12 18:19:59 -07:00
|
|
|
|
if (didForceFontSmoothing)
|
|
|
|
|
|
CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE);
|
|
|
|
|
|
|
2008-03-12 18:11:57 -07:00
|
|
|
|
if (rv == CAIRO_STATUS_SUCCESS &&
|
|
|
|
|
|
cgfref &&
|
|
|
|
|
|
!_cairo_operator_bounded_by_mask (op))
|
|
|
|
|
|
{
|
|
|
|
|
|
unbounded_op_data_t ub;
|
|
|
|
|
|
ub.op = UNBOUNDED_SHOW_GLYPHS;
|
|
|
|
|
|
|
|
|
|
|
|
ub.u.show_glyphs.isClipping = isClipping;
|
|
|
|
|
|
ub.u.show_glyphs.cg_glyphs = cg_glyphs;
|
|
|
|
|
|
ub.u.show_glyphs.cg_advances = cg_advances;
|
|
|
|
|
|
ub.u.show_glyphs.nglyphs = num_glyphs;
|
|
|
|
|
|
ub.u.show_glyphs.textTransform = textTransform;
|
|
|
|
|
|
ub.u.show_glyphs.font = cgfref;
|
|
|
|
|
|
ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
|
|
|
|
|
|
|
|
|
|
|
|
_cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-07-29 16:05:51 +02:00
|
|
|
|
if (cg_advances != cg_advances_static)
|
2007-11-12 23:56:01 +00:00
|
|
|
|
free (cg_advances);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-29 16:05:51 +02:00
|
|
|
|
if (cg_glyphs != glyphs_static)
|
2007-11-12 23:56:01 +00:00
|
|
|
|
free (cg_glyphs);
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return rv;
|
|
|
|
|
|
}
|
2008-03-17 17:37:19 -07:00
|
|
|
|
#endif /* CAIRO_HAS_QUARTZ_FONT */
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-01-29 12:06:13 +01:00
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_surface_show_glyphs (void *abstract_surface,
|
|
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
cairo_glyph_t *glyphs,
|
|
|
|
|
|
int num_glyphs,
|
|
|
|
|
|
cairo_scaled_font_t *scaled_font,
|
|
|
|
|
|
cairo_clip_t *clip,
|
|
|
|
|
|
int *remaining_glyphs)
|
|
|
|
|
|
{
|
|
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
|
|
|
|
|
cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
|
|
cairo_image_surface_t *image;
|
|
|
|
|
|
|
|
|
|
|
|
#if CAIRO_HAS_QUARTZ_FONT
|
2010-07-28 10:26:50 +02:00
|
|
|
|
rv = _cairo_quartz_surface_show_glyphs_cg (surface, op, source,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
glyphs, num_glyphs,
|
|
|
|
|
|
scaled_font, clip, remaining_glyphs);
|
|
|
|
|
|
|
|
|
|
|
|
if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
rv = _cairo_quartz_get_image (surface, &image);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (likely (rv == CAIRO_STATUS_SUCCESS)) {
|
2010-01-29 12:06:13 +01:00
|
|
|
|
rv = _cairo_surface_show_text_glyphs (&image->base, op, source,
|
|
|
|
|
|
NULL, 0,
|
|
|
|
|
|
glyphs, num_glyphs,
|
|
|
|
|
|
NULL, 0, 0,
|
|
|
|
|
|
scaled_font, clip);
|
|
|
|
|
|
cairo_surface_destroy (&image->base);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-10-20 23:41:32 +01:00
|
|
|
|
static cairo_int_status_t
|
2007-07-01 14:37:57 +01:00
|
|
|
|
_cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
|
|
|
|
|
|
cairo_operator_t op,
|
2008-11-11 08:31:23 -05:00
|
|
|
|
const cairo_pattern_t *source,
|
2008-11-11 08:41:31 -05:00
|
|
|
|
const cairo_surface_pattern_t *mask,
|
2009-08-09 21:12:36 +01:00
|
|
|
|
cairo_clip_t *clip)
|
2007-07-01 14:37:57 +01:00
|
|
|
|
{
|
|
|
|
|
|
CGRect rect;
|
|
|
|
|
|
CGImageRef img;
|
|
|
|
|
|
cairo_surface_t *pat_surf = mask->surface;
|
|
|
|
|
|
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
2008-02-25 21:06:25 -05:00
|
|
|
|
CGAffineTransform ctm, mask_matrix;
|
2007-07-01 14:37:57 +01:00
|
|
|
|
|
2010-07-27 14:38:28 +02:00
|
|
|
|
status = _cairo_surface_to_cgimage (pat_surf, &img);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (status))
|
2008-03-17 17:37:22 -07:00
|
|
|
|
return status;
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (img == NULL)) {
|
2010-01-06 18:53:54 +01:00
|
|
|
|
if (!_cairo_operator_bounded_by_mask (op))
|
|
|
|
|
|
CGContextClearRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
|
2010-01-02 13:23:53 +01:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2010-01-06 18:53:54 +01:00
|
|
|
|
}
|
2007-07-01 14:37:57 +01:00
|
|
|
|
|
2010-01-15 16:28:12 +01:00
|
|
|
|
rect = CGRectMake (0.0f, 0.0f, CGImageGetWidth (img) , CGImageGetHeight (img));
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2007-07-01 14:37:57 +01:00
|
|
|
|
CGContextSaveGState (surface->cgContext);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
|
|
|
|
|
/* ClipToMask is essentially drawing an image, so we need to flip the CTM
|
|
|
|
|
|
* to get the image to appear oriented the right way */
|
2008-02-25 21:06:25 -05:00
|
|
|
|
ctm = CGContextGetCTM (surface->cgContext);
|
|
|
|
|
|
|
|
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (&mask->base.matrix, &mask_matrix);
|
2010-07-27 15:59:31 +02:00
|
|
|
|
mask_matrix = CGAffineTransformInvert (mask_matrix);
|
2010-01-06 18:53:54 +01:00
|
|
|
|
mask_matrix = CGAffineTransformTranslate (mask_matrix, 0.0, CGImageGetHeight (img));
|
|
|
|
|
|
mask_matrix = CGAffineTransformScale (mask_matrix, 1.0, -1.0);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2010-01-06 18:53:54 +01:00
|
|
|
|
CGContextConcatCTM (surface->cgContext, mask_matrix);
|
2008-01-24 11:48:02 -08:00
|
|
|
|
CGContextClipToMaskPtr (surface->cgContext, rect, img);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
|
|
|
|
|
CGContextSetCTM (surface->cgContext, ctm);
|
|
|
|
|
|
|
2010-02-03 11:19:26 +01:00
|
|
|
|
status = _cairo_quartz_surface_paint_cg (surface, op, source, clip);
|
2007-07-01 14:37:57 +01:00
|
|
|
|
|
|
|
|
|
|
CGContextRestoreGState (surface->cgContext);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
|
|
|
|
|
|
if (!_cairo_operator_bounded_by_mask (op)) {
|
|
|
|
|
|
unbounded_op_data_t ub;
|
|
|
|
|
|
ub.op = UNBOUNDED_MASK;
|
|
|
|
|
|
ub.u.mask.mask = img;
|
2010-01-06 18:53:54 +01:00
|
|
|
|
ub.u.mask.maskTransform = mask_matrix;
|
2008-03-12 18:11:57 -07:00
|
|
|
|
_cairo_quartz_fixup_unbounded_operation (surface, &ub, CAIRO_ANTIALIAS_NONE);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-07-01 14:37:57 +01:00
|
|
|
|
CGImageRelease (img);
|
2008-03-17 17:37:22 -07:00
|
|
|
|
|
2007-07-01 14:37:57 +01:00
|
|
|
|
return status;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-25 21:06:36 -05:00
|
|
|
|
/* This is somewhat less than ideal, but it gets the job done;
|
|
|
|
|
|
* it would be better to avoid calling back into cairo. This
|
|
|
|
|
|
* creates a temporary surface to use as the mask.
|
|
|
|
|
|
*/
|
|
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface,
|
|
|
|
|
|
cairo_operator_t op,
|
2008-11-11 08:31:23 -05:00
|
|
|
|
const cairo_pattern_t *source,
|
2008-11-11 08:41:31 -05:00
|
|
|
|
const cairo_pattern_t *mask,
|
2009-08-09 21:12:36 +01:00
|
|
|
|
cairo_clip_t *clip)
|
2008-02-25 21:06:36 -05:00
|
|
|
|
{
|
2010-07-27 19:05:25 +02:00
|
|
|
|
cairo_surface_t *gradient_surf;
|
2008-05-28 15:42:09 +01:00
|
|
|
|
cairo_surface_pattern_t surface_pattern;
|
2008-02-25 21:06:36 -05:00
|
|
|
|
cairo_int_status_t status;
|
|
|
|
|
|
|
|
|
|
|
|
/* Render the gradient to a surface */
|
2010-10-12 22:52:54 +02:00
|
|
|
|
gradient_surf = _cairo_quartz_surface_create_similar (surface,
|
|
|
|
|
|
CAIRO_CONTENT_ALPHA,
|
|
|
|
|
|
surface->extents.width,
|
|
|
|
|
|
surface->extents.height);
|
2010-10-07 18:59:22 +02:00
|
|
|
|
status = gradient_surf->status;
|
|
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
goto BAIL;
|
2008-02-25 21:06:36 -05:00
|
|
|
|
|
2010-01-06 18:53:54 +01:00
|
|
|
|
status = _cairo_quartz_surface_paint (gradient_surf, CAIRO_OPERATOR_SOURCE, mask, NULL);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (status))
|
2008-02-25 21:06:36 -05:00
|
|
|
|
goto BAIL;
|
|
|
|
|
|
|
2008-05-28 15:42:09 +01:00
|
|
|
|
_cairo_pattern_init_for_surface (&surface_pattern, gradient_surf);
|
2009-08-09 21:12:36 +01:00
|
|
|
|
status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern, clip);
|
2008-02-25 21:06:36 -05:00
|
|
|
|
_cairo_pattern_fini (&surface_pattern.base);
|
|
|
|
|
|
|
2010-07-27 19:05:25 +02:00
|
|
|
|
BAIL:
|
|
|
|
|
|
cairo_surface_destroy (gradient_surf);
|
2008-02-25 21:06:36 -05:00
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
static cairo_int_status_t
|
2010-07-28 10:26:50 +02:00
|
|
|
|
_cairo_quartz_surface_mask_cg (cairo_quartz_surface_t *surface,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
const cairo_pattern_t *mask,
|
|
|
|
|
|
cairo_clip_t *clip)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
|
|
|
|
|
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (IS_EMPTY (surface))
|
2010-07-27 18:48:00 +02:00
|
|
|
|
return CAIRO_INT_STATUS_NOTHING_TO_DO;
|
2007-12-04 13:49:59 -08:00
|
|
|
|
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
|
|
|
|
|
if (unlikely (rv))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
|
|
|
|
|
|
/* This is easy; we just need to paint with the alpha. */
|
|
|
|
|
|
cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
|
|
|
|
|
|
|
|
|
|
|
|
CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha);
|
2010-07-27 18:50:55 +02:00
|
|
|
|
rv = _cairo_quartz_surface_paint_cg (surface, op, source, clip);
|
2008-02-25 21:06:36 -05:00
|
|
|
|
CGContextSetAlpha (surface->cgContext, 1.0);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-02-25 21:06:36 -05:00
|
|
|
|
return rv;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-25 21:06:36 -05:00
|
|
|
|
/* If we have CGContextClipToMask, we can do more complex masks */
|
|
|
|
|
|
if (CGContextClipToMaskPtr) {
|
|
|
|
|
|
/* For these, we can skip creating a temporary surface, since we already have one */
|
|
|
|
|
|
if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE)
|
2009-08-09 21:12:36 +01:00
|
|
|
|
return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2009-08-09 21:12:36 +01:00
|
|
|
|
return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-02-25 21:06:36 -05:00
|
|
|
|
/* So, CGContextClipToMask is not present in 10.3.9, so we're
|
|
|
|
|
|
* doomed; if we have imageData, we can do fallback, otherwise
|
|
|
|
|
|
* just pretend success.
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (surface->imageData)
|
|
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-02-25 21:06:36 -05:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-29 12:06:13 +01:00
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_surface_mask (void *abstract_surface,
|
|
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
const cairo_pattern_t *mask,
|
|
|
|
|
|
cairo_clip_t *clip)
|
|
|
|
|
|
{
|
|
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
|
|
|
|
|
cairo_int_status_t rv;
|
|
|
|
|
|
cairo_image_surface_t *image;
|
|
|
|
|
|
|
2010-07-28 10:26:50 +02:00
|
|
|
|
rv = _cairo_quartz_surface_mask_cg (surface,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
op,
|
|
|
|
|
|
source,
|
|
|
|
|
|
mask,
|
|
|
|
|
|
clip);
|
|
|
|
|
|
|
|
|
|
|
|
if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED))
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
|
|
|
|
rv = _cairo_quartz_get_image (surface, &image);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (likely (rv == CAIRO_STATUS_SUCCESS)) {
|
2010-01-29 12:06:13 +01:00
|
|
|
|
rv = _cairo_surface_mask (&image->base, op, source, mask, clip);
|
|
|
|
|
|
cairo_surface_destroy (&image->base);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
static cairo_status_t
|
|
|
|
|
|
_cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
|
|
|
|
|
|
cairo_path_fixed_t *path,
|
|
|
|
|
|
cairo_fill_rule_t fill_rule,
|
|
|
|
|
|
double tolerance,
|
|
|
|
|
|
cairo_antialias_t antialias)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
cairo_quartz_surface_t *surface =
|
|
|
|
|
|
cairo_container_of (clipper, cairo_quartz_surface_t, clipper);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (IS_EMPTY (surface))
|
2007-12-04 13:49:59 -08:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
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.
|
|
|
|
|
|
*
|
2007-03-06 23:24:33 +00:00
|
|
|
|
* Note that this assumes that ALL quartz surface creation
|
2007-02-20 12:15:35 -08:00
|
|
|
|
* functions will do a SaveGState first; we do this in create_internal.
|
|
|
|
|
|
*/
|
|
|
|
|
|
CGContextRestoreGState (surface->cgContext);
|
|
|
|
|
|
CGContextSaveGState (surface->cgContext);
|
|
|
|
|
|
} else {
|
2008-03-07 15:35:31 -08:00
|
|
|
|
CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
|
|
|
|
|
|
|
2010-04-27 10:59:09 +02:00
|
|
|
|
_cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
|
2007-06-18 00:07:33 +01:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (fill_rule == CAIRO_FILL_RULE_WINDING)
|
|
|
|
|
|
CGContextClip (surface->cgContext);
|
|
|
|
|
|
else
|
|
|
|
|
|
CGContextEOClip (surface->cgContext);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ND ((stderr, "-- intersect_clip_path\n"));
|
2005-05-11 15:39:26 +00:00
|
|
|
|
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2005-04-19 16:29:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
// XXXtodo implement show_page; need to figure out how to handle begin/end
|
|
|
|
|
|
|
2007-03-06 23:24:33 +00:00
|
|
|
|
static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
|
2006-04-07 17:40:30 +02:00
|
|
|
|
CAIRO_SURFACE_TYPE_QUARTZ,
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_create_similar,
|
|
|
|
|
|
_cairo_quartz_surface_finish,
|
|
|
|
|
|
_cairo_quartz_surface_acquire_source_image,
|
2007-05-14 23:46:14 -05:00
|
|
|
|
_cairo_quartz_surface_release_source_image,
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_acquire_dest_image,
|
|
|
|
|
|
_cairo_quartz_surface_release_dest_image,
|
|
|
|
|
|
_cairo_quartz_surface_clone_similar,
|
2005-04-07 14:25:00 +00:00
|
|
|
|
NULL, /* composite */
|
|
|
|
|
|
NULL, /* fill_rectangles */
|
|
|
|
|
|
NULL, /* composite_trapezoids */
|
2008-07-11 00:59:47 +03:00
|
|
|
|
NULL, /* create_span_renderer */
|
|
|
|
|
|
NULL, /* check_span_renderer */
|
2005-04-07 14:25:00 +00:00
|
|
|
|
NULL, /* copy_page */
|
|
|
|
|
|
NULL, /* show_page */
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_get_extents,
|
2007-02-20 12:15:35 -08:00
|
|
|
|
NULL, /* old_show_glyphs */
|
|
|
|
|
|
NULL, /* get_font_options */
|
|
|
|
|
|
NULL, /* flush */
|
|
|
|
|
|
NULL, /* mark_dirty_rectangle */
|
|
|
|
|
|
NULL, /* scaled_font_fini */
|
|
|
|
|
|
NULL, /* scaled_glyph_fini */
|
|
|
|
|
|
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_paint,
|
|
|
|
|
|
_cairo_quartz_surface_mask,
|
|
|
|
|
|
_cairo_quartz_surface_stroke,
|
|
|
|
|
|
_cairo_quartz_surface_fill,
|
|
|
|
|
|
_cairo_quartz_surface_show_glyphs,
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-11-25 20:15:15 +01:00
|
|
|
|
_cairo_quartz_surface_snapshot,
|
2007-11-06 15:40:30 -08:00
|
|
|
|
NULL, /* is_similar */
|
|
|
|
|
|
NULL /* fill_stroke */
|
2005-01-16 08:35:14 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
2007-12-04 13:49:59 -08:00
|
|
|
|
cairo_quartz_surface_t *
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
|
2010-07-27 17:24:08 +02:00
|
|
|
|
cairo_content_t content,
|
|
|
|
|
|
unsigned int width,
|
|
|
|
|
|
unsigned int height)
|
2005-01-16 08:35:14 +00:00
|
|
|
|
{
|
2007-03-06 23:24:33 +00:00
|
|
|
|
cairo_quartz_surface_t *surface;
|
2005-03-29 11:24:10 +00:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
quartz_ensure_symbols ();
|
2008-01-24 11:48:02 -08:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/* Init the base surface */
|
2010-07-27 15:59:31 +02:00
|
|
|
|
surface = malloc (sizeof (cairo_quartz_surface_t));
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (surface == NULL))
|
2008-01-23 21:30:42 -08:00
|
|
|
|
return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
memset (surface, 0, sizeof (cairo_quartz_surface_t));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-01-18 21:53:42 +00:00
|
|
|
|
_cairo_surface_init (&surface->base,
|
|
|
|
|
|
&cairo_quartz_surface_backend,
|
|
|
|
|
|
NULL, /* device */
|
|
|
|
|
|
content);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
_cairo_surface_clipper_init (&surface->clipper,
|
2009-08-09 21:12:36 +01:00
|
|
|
|
_cairo_quartz_surface_clipper_intersect_clip_path);
|
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of
the operation state, is cumbersome and a hindrance to providing true proxy
surface support. For example, the clip must be copied from the surface
onto the fallback image, but this was forgotten causing undue hassle in
each backend. Another example is the contortion the meta surface
endures to ensure the clip is correctly recorded. By contrast passing the
clip along with the operation is quite simple and enables us to write
generic handlers for providing surface wrappers. (And in the future, we
should be able to write more esoteric wrappers, e.g. automatic 2x FSAA,
trivially.)
In brief, instead of the surface automatically applying the clip before
calling the backend, the backend can call into a generic helper to apply
clipping. For raster surfaces, clip regions are handled automatically as
part of the composite interface. For vector surfaces, a clip helper is
introduced to replay and callback into an intersect_clip_path() function
as necessary.
Whilst this is not primarily a performance related change (the change
should just move the computation of the clip from the moment it is applied
by the user to the moment it is required by the backend), it is important
to track any potential regression:
ppc:
Speedups
========
image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup
▌
image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup
▎
image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup
▏
image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup
▏
Slowdowns
=========
image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown
▏
2009-07-23 15:32:13 +01:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/* Save our extents */
|
|
|
|
|
|
surface->extents.x = surface->extents.y = 0;
|
|
|
|
|
|
surface->extents.width = width;
|
|
|
|
|
|
surface->extents.height = height;
|
2010-10-12 22:52:54 +02:00
|
|
|
|
surface->virtual_extents = surface->extents;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (IS_EMPTY (surface)) {
|
2007-12-04 13:49:59 -08:00
|
|
|
|
surface->cgContext = NULL;
|
|
|
|
|
|
surface->cgContextBaseCTM = CGAffineTransformIdentity;
|
|
|
|
|
|
surface->imageData = NULL;
|
|
|
|
|
|
return surface;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
|
|
|
|
surface->cgContext = cgContext;
|
|
|
|
|
|
surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
|
|
|
|
|
|
|
|
|
|
|
|
surface->imageData = NULL;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
surface->imageSurfaceEquiv = NULL;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
return surface;
|
|
|
|
|
|
}
|
2007-12-04 13:54:32 -08:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* 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
|
2008-04-01 13:26:00 -07:00
|
|
|
|
* 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:
|
2007-02-20 12:15:35 -08:00
|
|
|
|
*
|
|
|
|
|
|
* <informalexample><programlisting>
|
2008-01-11 14:36:05 -08:00
|
|
|
|
* CGContextTranslateCTM (cgContext, 0.0, height);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
* CGContextScaleCTM (cgContext, 1.0, -1.0);
|
|
|
|
|
|
* </programlisting></informalexample>
|
|
|
|
|
|
*
|
2008-04-01 13:26:00 -07:00
|
|
|
|
* All Cairo operations are implemented in terms of Quartz operations,
|
|
|
|
|
|
* as long as Quartz-compatible elements are used (such as Quartz fonts).
|
2007-02-20 12:15:35 -08:00
|
|
|
|
*
|
|
|
|
|
|
* Return value: the newly created Cairo surface.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Since: 1.4
|
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
|
|
|
cairo_surface_t *
|
|
|
|
|
|
cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
|
|
|
|
|
|
unsigned int width,
|
|
|
|
|
|
unsigned int height)
|
|
|
|
|
|
{
|
2007-03-06 23:24:33 +00:00
|
|
|
|
cairo_quartz_surface_t *surf;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2007-03-06 23:24:33 +00:00
|
|
|
|
surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
|
2007-12-04 13:49:59 -08:00
|
|
|
|
width, height);
|
2010-07-27 18:55:38 +02:00
|
|
|
|
if (likely (!surf->base.status))
|
|
|
|
|
|
CGContextRetain (cgContext);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 16:06:27 +02:00
|
|
|
|
return &surf->base;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 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.4
|
|
|
|
|
|
**/
|
|
|
|
|
|
cairo_surface_t *
|
|
|
|
|
|
cairo_quartz_surface_create (cairo_format_t format,
|
|
|
|
|
|
unsigned int width,
|
|
|
|
|
|
unsigned int height)
|
|
|
|
|
|
{
|
2007-03-06 23:24:33 +00:00
|
|
|
|
cairo_quartz_surface_t *surf;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextRef cgc;
|
|
|
|
|
|
CGColorSpaceRef cgColorspace;
|
|
|
|
|
|
CGBitmapInfo bitinfo;
|
|
|
|
|
|
void *imageData;
|
|
|
|
|
|
int stride;
|
|
|
|
|
|
int bitsPerComponent;
|
|
|
|
|
|
|
2007-11-06 15:40:30 -08:00
|
|
|
|
// verify width and height of surface
|
2010-07-27 15:59:31 +02:00
|
|
|
|
if (!_cairo_quartz_verify_surface_size (width, height))
|
2008-12-15 09:32:43 +01:00
|
|
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
|
2007-11-06 15:40:30 -08:00
|
|
|
|
|
2007-12-04 13:49:59 -08:00
|
|
|
|
if (width == 0 || height == 0) {
|
2010-07-27 16:06:27 +02:00
|
|
|
|
return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
|
|
|
|
|
|
width, height)->base;
|
2007-12-04 13:49:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-03-08 16:06:01 -08:00
|
|
|
|
if (format == CAIRO_FORMAT_ARGB32 ||
|
|
|
|
|
|
format == CAIRO_FORMAT_RGB24)
|
|
|
|
|
|
{
|
2010-07-27 15:59:31 +02:00
|
|
|
|
cgColorspace = CGColorSpaceCreateDeviceRGB ();
|
2008-03-08 16:06:01 -08:00
|
|
|
|
bitinfo = kCGBitmapByteOrder32Host;
|
|
|
|
|
|
if (format == CAIRO_FORMAT_ARGB32)
|
|
|
|
|
|
bitinfo |= kCGImageAlphaPremultipliedFirst;
|
|
|
|
|
|
else
|
|
|
|
|
|
bitinfo |= kCGImageAlphaNoneSkipFirst;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
bitsPerComponent = 8;
|
|
|
|
|
|
stride = width * 4;
|
|
|
|
|
|
} else if (format == CAIRO_FORMAT_A8) {
|
2009-10-19 17:33:10 +02:00
|
|
|
|
cgColorspace = NULL;
|
|
|
|
|
|
stride = width;
|
|
|
|
|
|
bitinfo = kCGImageAlphaOnly;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
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.
|
|
|
|
|
|
*/
|
2008-01-16 16:23:23 +00:00
|
|
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
} else {
|
2008-01-16 16:23:23 +00:00
|
|
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
2009-10-19 17:33:10 +02:00
|
|
|
|
|
|
|
|
|
|
/* 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;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2007-06-19 13:15:21 -07:00
|
|
|
|
imageData = _cairo_malloc_ab (height, stride);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (!imageData)) {
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGColorSpaceRelease (cgColorspace);
|
2008-01-16 16:23:23 +00:00
|
|
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
2005-07-27 15:39:34 +00:00
|
|
|
|
}
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2007-11-18 20:30:49 +00:00
|
|
|
|
/* zero the memory to match the image surface behaviour */
|
|
|
|
|
|
memset (imageData, 0, height * stride);
|
2005-03-29 11:24:10 +00:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cgc = CGBitmapContextCreate (imageData,
|
|
|
|
|
|
width,
|
|
|
|
|
|
height,
|
|
|
|
|
|
bitsPerComponent,
|
|
|
|
|
|
stride,
|
|
|
|
|
|
cgColorspace,
|
|
|
|
|
|
bitinfo);
|
|
|
|
|
|
CGColorSpaceRelease (cgColorspace);
|
2005-01-16 08:35:14 +00:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (!cgc) {
|
2007-11-06 15:40:30 -08:00
|
|
|
|
free (imageData);
|
2008-01-16 16:23:23 +00:00
|
|
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
2005-03-29 11:24:10 +00:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/* flip the Y axis */
|
|
|
|
|
|
CGContextTranslateCTM (cgc, 0.0, height);
|
|
|
|
|
|
CGContextScaleCTM (cgc, 1.0, -1.0);
|
2005-01-16 08:35:14 +00:00
|
|
|
|
|
2007-03-06 23:24:33 +00:00
|
|
|
|
surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
|
2007-12-04 13:49:59 -08:00
|
|
|
|
width, height);
|
2008-01-16 16:23:23 +00:00
|
|
|
|
if (surf->base.status) {
|
2007-02-20 12:15:35 -08:00
|
|
|
|
CGContextRelease (cgc);
|
2007-11-06 15:40:30 -08:00
|
|
|
|
free (imageData);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
// create_internal will have set an error
|
2010-07-27 16:06:27 +02:00
|
|
|
|
return &surf->base;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
surf->imageData = imageData;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 16:06:27 +02:00
|
|
|
|
return &surf->base;
|
2005-03-29 11:24:10 +00:00
|
|
|
|
}
|
2006-01-10 05:28:59 +00:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/**
|
|
|
|
|
|
* cairo_quartz_surface_get_cg_context
|
2007-02-20 16:16:18 -08:00
|
|
|
|
* @surface: the Cairo Quartz surface
|
2007-02-20 12:15:35 -08:00
|
|
|
|
*
|
|
|
|
|
|
* Returns the CGContextRef that the given Quartz surface is backed
|
|
|
|
|
|
* by.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Return value: the CGContextRef for the given surface.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Since: 1.4
|
|
|
|
|
|
**/
|
|
|
|
|
|
CGContextRef
|
2007-02-20 16:16:18 -08:00
|
|
|
|
cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
|
2006-01-10 05:28:59 +00:00
|
|
|
|
{
|
2010-07-27 18:04:36 +02:00
|
|
|
|
if (surface && _cairo_surface_is_quartz (surface)) {
|
|
|
|
|
|
cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface;
|
|
|
|
|
|
return quartz->cgContext;
|
|
|
|
|
|
} else
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return NULL;
|
2006-01-10 05:28:59 +00:00
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 18:04:36 +02:00
|
|
|
|
static cairo_bool_t
|
|
|
|
|
|
_cairo_surface_is_quartz (const cairo_surface_t *surface)
|
|
|
|
|
|
{
|
|
|
|
|
|
return surface->backend == &cairo_quartz_surface_backend;
|
|
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
/* Debug stuff */
|
|
|
|
|
|
|
2007-02-20 12:54:03 -08:00
|
|
|
|
#ifdef QUARTZ_DEBUG
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
#include <Movies.h>
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
void ExportCGImageToPNGFile (CGImageRef inImageRef, char* dest)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
|
|
|
|
|
Handle dataRef = NULL;
|
|
|
|
|
|
OSType dataRefType;
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
GraphicsExportComponent grex = 0;
|
|
|
|
|
|
unsigned long sizeWritten;
|
|
|
|
|
|
|
|
|
|
|
|
ComponentResult result;
|
|
|
|
|
|
|
|
|
|
|
|
// create the data reference
|
2010-07-27 15:59:31 +02:00
|
|
|
|
result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle,
|
|
|
|
|
|
0, &dataRef, &dataRefType);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
if (NULL != dataRef && noErr == result) {
|
|
|
|
|
|
// get the PNG exporter
|
2010-07-27 15:59:31 +02:00
|
|
|
|
result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG,
|
|
|
|
|
|
&grex);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
if (grex) {
|
|
|
|
|
|
// tell the exporter where to find its source image
|
2010-07-27 15:59:31 +02:00
|
|
|
|
result = GraphicsExportSetInputCGImage (grex, inImageRef);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
if (noErr == result) {
|
|
|
|
|
|
// tell the exporter where to save the exporter image
|
2010-07-27 15:59:31 +02:00
|
|
|
|
result = GraphicsExportSetOutputDataReference (grex, dataRef,
|
|
|
|
|
|
dataRefType);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
if (noErr == result) {
|
|
|
|
|
|
// write the PNG file
|
2010-07-27 15:59:31 +02:00
|
|
|
|
result = GraphicsExportDoExport (grex, &sizeWritten);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// remember to close the component
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CloseComponent (grex);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// remember to dispose of the data reference handle
|
2010-07-27 15:59:31 +02:00
|
|
|
|
DisposeHandle (dataRef);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2008-03-06 16:52:54 -08:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
void
|
2007-03-06 23:24:33 +00:00
|
|
|
|
quartz_image_to_png (CGImageRef imgref, char *dest)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
|
|
|
|
|
static int sctr = 0;
|
|
|
|
|
|
char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
|
|
|
|
|
|
|
|
|
|
|
|
if (dest == NULL) {
|
|
|
|
|
|
fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
|
|
|
|
|
|
sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
|
|
|
|
|
|
sctr++;
|
|
|
|
|
|
dest = sptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ExportCGImageToPNGFile (imgref, dest);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2007-03-06 23:24:33 +00:00
|
|
|
|
quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
|
|
|
|
|
static int sctr = 0;
|
|
|
|
|
|
char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
|
|
|
|
|
|
|
|
|
|
|
|
if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
|
2007-03-06 23:24:33 +00:00
|
|
|
|
fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
2007-03-06 23:24:33 +00:00
|
|
|
|
fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
ExportCGImageToPNGFile (imgref, dest);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2010-07-27 15:59:31 +02:00
|
|
|
|
CGImageRelease (imgref);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
2008-03-07 14:49:15 -08:00
|
|
|
|
|
|
|
|
|
|
#endif /* QUARTZ_DEBUG */
|