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>
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
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"
|
2022-04-26 12:20:04 -07:00
|
|
|
|
#include "cairo-quartz-image.h"
|
2010-01-18 16:58:40 +00:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
#include "cairo-composite-rectangles-private.h"
|
|
|
|
|
|
#include "cairo-compositor-private.h"
|
2011-07-19 07:50:47 +02:00
|
|
|
|
#include "cairo-default-context-private.h"
|
2010-01-18 16:58:40 +00:00
|
|
|
|
#include "cairo-error-private.h"
|
2012-01-06 19:47:08 +01:00
|
|
|
|
#include "cairo-image-surface-inline.h"
|
2011-07-19 07:50:47 +02:00
|
|
|
|
#include "cairo-pattern-private.h"
|
2011-07-30 17:28:21 +01:00
|
|
|
|
#include "cairo-surface-backend-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"
|
2012-07-23 16:28:28 +00:00
|
|
|
|
#include "cairo-recording-surface-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>
|
2022-05-01 13:30:21 -07:00
|
|
|
|
#include <assert.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
|
|
|
|
|
|
|
2022-04-21 17:19:15 -07:00
|
|
|
|
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080
|
|
|
|
|
|
#define FONT_ORIENTATION_HORIZONTAL kCTFontHorizontalOrientation
|
|
|
|
|
|
#else
|
|
|
|
|
|
#define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2022-04-26 12:20:04 -07:00
|
|
|
|
static inline cairo_bool_t
|
2022-05-01 13:30:21 -07:00
|
|
|
|
_is_quartz_surface (cairo_surface_t *surface) {
|
|
|
|
|
|
return _cairo_surface_is_quartz (surface) || _cairo_surface_is_quartz_image (surface);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline cairo_bool_t
|
|
|
|
|
|
_cairo_quartz_surface_is_zero (cairo_quartz_surface_t* surface) {
|
|
|
|
|
|
return surface->extents.width == 0 || surface->extents.height == 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline cairo_bool_t
|
|
|
|
|
|
_cairo_quartz_is_zero_surface (cairo_surface_t* surface) {
|
|
|
|
|
|
assert (_is_quartz_surface (surface));
|
|
|
|
|
|
if (_cairo_surface_is_quartz (surface))
|
|
|
|
|
|
return (_cairo_quartz_surface_is_zero ((cairo_quartz_surface_t*) surface));
|
|
|
|
|
|
else
|
|
|
|
|
|
return (_cairo_quartz_image_surface_is_zero ((cairo_quartz_image_surface_t*) surface));
|
2022-04-26 12:20:04 -07:00
|
|
|
|
}
|
2007-12-04 13:49:59 -08:00
|
|
|
|
|
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.
|
2012-02-16 00:31:47 +01:00
|
|
|
|
**/
|
2010-07-08 13:05:18 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* CAIRO_HAS_QUARTZ_SURFACE:
|
|
|
|
|
|
*
|
|
|
|
|
|
* Defined if the Quartz surface backend is available.
|
|
|
|
|
|
* This macro can be used to conditionally compile backend-specific code.
|
doc: Add "since" tag to documentation
The following Python script was used to compute "Since: 1.X" tags,
based on the first version where a symbol became officially supported.
This script requires a concatenation of the the cairo public headers
for the officially supported beckends to be available as
"../../includes/1.X.0.h".
from sys import argv
import re
syms = {}
def stripcomments(text):
def replacer(match):
s = match.group(0)
if s.startswith('/'):
return ""
else:
return s
pattern = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE
)
return re.sub(pattern, replacer, text)
for minor in range(12,-2,-2):
version = "1.%d" % minor
names = re.split('([A-Za-z0-9_]+)', stripcomments(open("../../includes/%s.0.h" % version).read()))
for s in names: syms[s] = version
for filename in argv[1:]:
is_public = False
lines = open(filename, "r").read().split("\n")
newlines = []
for i in range(len(lines)):
if lines[i] == "/**":
last_sym = lines[i+1][2:].strip().replace(":", "")
is_public = last_sym.lower().startswith("cairo")
elif is_public and lines[i] == " **/":
if last_sym in syms:
v = syms[last_sym]
if re.search("Since", newlines[-1]): newlines = newlines[:-1]
if newlines[-1].strip() != "*": newlines.append(" *")
newlines.append(" * Since: %s" % v)
else:
print "%s (%d): Cannot determine the version in which '%s' was introduced" % (filename, i, last_sym)
newlines.append(lines[i])
out = open(filename, "w")
out.write("\n".join(newlines))
out.close()
2012-03-27 11:48:19 +02:00
|
|
|
|
*
|
|
|
|
|
|
* Since: 1.6
|
2012-02-16 00:31:47 +01:00
|
|
|
|
**/
|
2010-07-08 13:05:18 +02:00
|
|
|
|
|
2007-02-27 20:03:26 -05:00
|
|
|
|
/*
|
2022-04-07 16:51:16 -07:00
|
|
|
|
* macOS Private functions
|
2007-02-27 20:03:26 -05:00
|
|
|
|
*/
|
2023-02-07 09:48:16 -08:00
|
|
|
|
typedef enum {
|
|
|
|
|
|
kCGContextTypeUnknown,
|
|
|
|
|
|
kCGContextTypePDF,
|
|
|
|
|
|
kCGContextTypePostScript,
|
|
|
|
|
|
kCGContextTypeWindow,
|
|
|
|
|
|
kCGContextTypeBitmap,
|
|
|
|
|
|
kCGContextTypeGL,
|
|
|
|
|
|
kCGContextTypeDisplayList,
|
|
|
|
|
|
kCGContextTypeKSeparation,
|
|
|
|
|
|
kCGContextTypeIOSurface,
|
|
|
|
|
|
kCGContextTypeCount
|
|
|
|
|
|
} CGContextType;
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-04-07 16:51:16 -07:00
|
|
|
|
static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
|
|
|
|
|
|
static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
|
|
|
|
|
|
static void
|
|
|
|
|
|
quartz_ensure_symbols()
|
|
|
|
|
|
{
|
|
|
|
|
|
static cairo_bool_t symbol_lookup_done = FALSE;
|
|
|
|
|
|
if (!symbol_lookup_done) {
|
|
|
|
|
|
CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
|
|
|
|
|
|
CGContextGetAllowsFontSmoothingPtr =
|
|
|
|
|
|
dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
|
|
|
|
|
|
symbol_lookup_done = TRUE;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2022-02-10 12:54:46 -08:00
|
|
|
|
typedef struct
|
|
|
|
|
|
{
|
|
|
|
|
|
cairo_surface_t base;
|
|
|
|
|
|
CGImageRef image;
|
|
|
|
|
|
} cairo_quartz_snapshot_t;
|
|
|
|
|
|
|
2022-04-26 12:20:04 -07:00
|
|
|
|
static cairo_surface_t* _cairo_quartz_snapshot_create (cairo_surface_t *surface);
|
|
|
|
|
|
static cairo_status_t _cairo_quartz_snapshot_finish (void *surface);
|
|
|
|
|
|
static CGImageRef _cairo_quartz_surface_snapshot_get_image (cairo_surface_t *surface);
|
2022-02-10 12:54:46 -08:00
|
|
|
|
|
|
|
|
|
|
static const cairo_surface_backend_t cairo_quartz_snapshot_backend = {
|
|
|
|
|
|
CAIRO_INTERNAL_SURFACE_TYPE_QUARTZ_SNAPSHOT,
|
|
|
|
|
|
_cairo_quartz_snapshot_finish,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGColorSpaceRef
|
|
|
|
|
|
_cairo_quartz_create_color_space (CGContextRef context)
|
|
|
|
|
|
{
|
|
|
|
|
|
CGColorSpaceRef color_space = NULL;
|
|
|
|
|
|
CGContextType cgtype = kCGContextTypeUnknown;
|
|
|
|
|
|
|
|
|
|
|
|
if (context)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (CGBitmapContextGetBitsPerPixel (context) < 24)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
quartz_ensure_symbols();
|
|
|
|
|
|
cgtype = CGContextGetTypePtr (context);
|
|
|
|
|
|
switch (cgtype)
|
|
|
|
|
|
{
|
|
|
|
|
|
case kCGContextTypeUnknown:
|
|
|
|
|
|
break;
|
|
|
|
|
|
case kCGContextTypePDF:
|
|
|
|
|
|
color_space = CGColorSpaceCreateDeviceRGB ();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case kCGContextTypePostScript:
|
|
|
|
|
|
case kCGContextTypeWindow:
|
|
|
|
|
|
break;
|
|
|
|
|
|
case kCGContextTypeBitmap:
|
|
|
|
|
|
color_space = CGBitmapContextGetColorSpace (context);
|
|
|
|
|
|
color_space = CGColorSpaceRetain (color_space);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case kCGContextTypeGL:
|
|
|
|
|
|
case kCGContextTypeDisplayList:
|
|
|
|
|
|
case kCGContextTypeKSeparation:
|
|
|
|
|
|
case kCGContextTypeIOSurface:
|
|
|
|
|
|
case kCGContextTypeCount:
|
|
|
|
|
|
default:
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (color_space)
|
|
|
|
|
|
return color_space;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!color_space)
|
|
|
|
|
|
color_space = CGDisplayCopyColorSpace (CGMainDisplayID ());
|
|
|
|
|
|
|
|
|
|
|
|
if (!color_space)
|
|
|
|
|
|
color_space = CGColorSpaceCreateDeviceRGB ();
|
|
|
|
|
|
|
|
|
|
|
|
return color_space;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static CGColorRef
|
|
|
|
|
|
_cairo_quartz_create_cgcolor (CGColorSpaceRef cs, CGFloat red, CGFloat green,
|
|
|
|
|
|
CGFloat blue, CGFloat alpha)
|
|
|
|
|
|
{
|
|
|
|
|
|
CGFloat colors[4] = { red, green, blue, alpha };
|
|
|
|
|
|
CGColorRef cgc;
|
|
|
|
|
|
if (!CGColorSpaceRetain(cs))
|
|
|
|
|
|
{
|
|
|
|
|
|
cs = _cairo_quartz_create_color_space (NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
cgc = CGColorCreate (cs, colors);
|
|
|
|
|
|
CGColorSpaceRelease (cs);
|
|
|
|
|
|
return cgc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static CGColorRef
|
|
|
|
|
|
_cairo_quartz_black (CGColorSpaceRef cs)
|
|
|
|
|
|
{
|
|
|
|
|
|
return _cairo_quartz_create_cgcolor (cs, 0.0, 0.0, 0.0, 1.0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
2022-04-07 16:51:16 -07:00
|
|
|
|
quartz_ensure_symbols ();
|
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 */
|
2023-02-07 09:48:16 -08:00
|
|
|
|
return CGContextGetTypePtr (cgc) == kCGContextTypeBitmap;
|
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
|
2011-07-26 20:51:30 +02:00
|
|
|
|
_cairo_quartz_cairo_path_to_quartz_context (const 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_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
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2009-11-09 21:38:00 +01:00
|
|
|
|
|
2010-07-23 08:20:52 +02:00
|
|
|
|
static CGBlendMode
|
|
|
|
|
|
_cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
|
2009-11-09 21:38:00 +01:00
|
|
|
|
{
|
2010-07-23 08:20:52 +02:00
|
|
|
|
switch (op) {
|
|
|
|
|
|
case CAIRO_OPERATOR_MULTIPLY:
|
|
|
|
|
|
return kCGBlendModeMultiply;
|
|
|
|
|
|
case CAIRO_OPERATOR_SCREEN:
|
|
|
|
|
|
return kCGBlendModeScreen;
|
|
|
|
|
|
case CAIRO_OPERATOR_OVERLAY:
|
|
|
|
|
|
return kCGBlendModeOverlay;
|
|
|
|
|
|
case CAIRO_OPERATOR_DARKEN:
|
|
|
|
|
|
return kCGBlendModeDarken;
|
|
|
|
|
|
case CAIRO_OPERATOR_LIGHTEN:
|
|
|
|
|
|
return kCGBlendModeLighten;
|
|
|
|
|
|
case CAIRO_OPERATOR_COLOR_DODGE:
|
|
|
|
|
|
return kCGBlendModeColorDodge;
|
|
|
|
|
|
case CAIRO_OPERATOR_COLOR_BURN:
|
|
|
|
|
|
return kCGBlendModeColorBurn;
|
|
|
|
|
|
case CAIRO_OPERATOR_HARD_LIGHT:
|
|
|
|
|
|
return kCGBlendModeHardLight;
|
|
|
|
|
|
case CAIRO_OPERATOR_SOFT_LIGHT:
|
|
|
|
|
|
return kCGBlendModeSoftLight;
|
|
|
|
|
|
case CAIRO_OPERATOR_DIFFERENCE:
|
|
|
|
|
|
return kCGBlendModeDifference;
|
|
|
|
|
|
case CAIRO_OPERATOR_EXCLUSION:
|
|
|
|
|
|
return kCGBlendModeExclusion;
|
|
|
|
|
|
case CAIRO_OPERATOR_HSL_HUE:
|
|
|
|
|
|
return kCGBlendModeHue;
|
|
|
|
|
|
case CAIRO_OPERATOR_HSL_SATURATION:
|
|
|
|
|
|
return kCGBlendModeSaturation;
|
|
|
|
|
|
case CAIRO_OPERATOR_HSL_COLOR:
|
|
|
|
|
|
return kCGBlendModeColor;
|
|
|
|
|
|
case CAIRO_OPERATOR_HSL_LUMINOSITY:
|
|
|
|
|
|
return kCGBlendModeLuminosity;
|
2009-11-09 21:38:00 +01:00
|
|
|
|
|
2010-07-23 08:20:52 +02:00
|
|
|
|
case CAIRO_OPERATOR_CLEAR:
|
|
|
|
|
|
return kCGBlendModeClear;
|
|
|
|
|
|
case CAIRO_OPERATOR_SOURCE:
|
|
|
|
|
|
return kCGBlendModeCopy;
|
|
|
|
|
|
case CAIRO_OPERATOR_OVER:
|
|
|
|
|
|
return kCGBlendModeNormal;
|
|
|
|
|
|
case CAIRO_OPERATOR_IN:
|
|
|
|
|
|
return kCGBlendModeSourceIn;
|
|
|
|
|
|
case CAIRO_OPERATOR_OUT:
|
|
|
|
|
|
return kCGBlendModeSourceOut;
|
|
|
|
|
|
case CAIRO_OPERATOR_ATOP:
|
|
|
|
|
|
return kCGBlendModeSourceAtop;
|
|
|
|
|
|
case CAIRO_OPERATOR_DEST_OVER:
|
|
|
|
|
|
return kCGBlendModeDestinationOver;
|
|
|
|
|
|
case CAIRO_OPERATOR_DEST_IN:
|
|
|
|
|
|
return kCGBlendModeDestinationIn;
|
|
|
|
|
|
case CAIRO_OPERATOR_DEST_OUT:
|
|
|
|
|
|
return kCGBlendModeDestinationOut;
|
|
|
|
|
|
case CAIRO_OPERATOR_DEST_ATOP:
|
|
|
|
|
|
return kCGBlendModeDestinationAtop;
|
|
|
|
|
|
case CAIRO_OPERATOR_XOR:
|
|
|
|
|
|
return kCGBlendModeXOR;
|
|
|
|
|
|
case CAIRO_OPERATOR_ADD:
|
|
|
|
|
|
return kCGBlendModePlusLighter;
|
|
|
|
|
|
case CAIRO_OPERATOR_DEST:
|
|
|
|
|
|
case CAIRO_OPERATOR_SATURATE:
|
|
|
|
|
|
default:
|
|
|
|
|
|
ASSERT_NOT_REACHED;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2009-11-09 21:38:00 +01:00
|
|
|
|
|
2010-07-23 08:20:52 +02:00
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op)
|
|
|
|
|
|
{
|
|
|
|
|
|
CGBlendMode blendmode;
|
2009-11-09 21:38:00 +01:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
assert (op != CAIRO_OPERATOR_DEST);
|
2009-11-09 21:38:00 +01:00
|
|
|
|
|
2010-07-23 08:20:52 +02:00
|
|
|
|
/* Quartz doesn't support SATURATE at all. COLOR_DODGE and
|
|
|
|
|
|
* COLOR_BURN in Quartz follow the ISO32000 definition, but cairo
|
2015-04-09 00:17:24 +02:00
|
|
|
|
* uses the definition from the Adobe Supplement. Also fallback
|
|
|
|
|
|
* on SOFT_LIGHT and HSL_HUE, because their results are
|
|
|
|
|
|
* significantly different from those provided by pixman.
|
2010-07-23 08:20:52 +02:00
|
|
|
|
*/
|
|
|
|
|
|
if (op == CAIRO_OPERATOR_SATURATE ||
|
2015-04-09 00:17:24 +02:00
|
|
|
|
op == CAIRO_OPERATOR_SOFT_LIGHT ||
|
|
|
|
|
|
op == CAIRO_OPERATOR_HSL_HUE ||
|
2010-07-23 08:20:52 +02:00
|
|
|
|
op == CAIRO_OPERATOR_COLOR_DODGE ||
|
|
|
|
|
|
op == CAIRO_OPERATOR_COLOR_BURN)
|
|
|
|
|
|
{
|
|
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
|
|
}
|
2009-11-09 21:38:00 +01:00
|
|
|
|
|
2010-07-23 08:20:52 +02:00
|
|
|
|
blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op);
|
|
|
|
|
|
CGContextSetBlendMode (context, blendmode);
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
|
|
|
|
|
|
{
|
|
|
|
|
|
ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
|
|
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
/* When the destination has no color components, we can avoid some
|
|
|
|
|
|
* fallbacks, but we have to workaround operators which behave
|
|
|
|
|
|
* differently in Quartz. */
|
2010-07-23 08:20:52 +02:00
|
|
|
|
if (surface->base.content == CAIRO_CONTENT_ALPHA) {
|
2011-07-30 17:28:21 +01:00
|
|
|
|
assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */
|
2011-01-05 16:12:34 +01:00
|
|
|
|
|
|
|
|
|
|
if (op == CAIRO_OPERATOR_SOURCE ||
|
|
|
|
|
|
op == CAIRO_OPERATOR_IN ||
|
|
|
|
|
|
op == CAIRO_OPERATOR_OUT ||
|
|
|
|
|
|
op == CAIRO_OPERATOR_DEST_IN ||
|
|
|
|
|
|
op == CAIRO_OPERATOR_DEST_ATOP ||
|
2010-07-23 08:20:52 +02:00
|
|
|
|
op == CAIRO_OPERATOR_XOR)
|
2011-01-05 16:12:34 +01:00
|
|
|
|
{
|
2010-07-23 08:20:52 +02:00
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
2011-01-05 16:12:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (op == CAIRO_OPERATOR_DEST_OVER)
|
|
|
|
|
|
op = CAIRO_OPERATOR_OVER;
|
2010-07-23 08:20:52 +02:00
|
|
|
|
else if (op == CAIRO_OPERATOR_SATURATE)
|
|
|
|
|
|
op = CAIRO_OPERATOR_ADD;
|
2011-01-05 16:12:34 +01:00
|
|
|
|
else if (op == CAIRO_OPERATOR_COLOR_DODGE)
|
|
|
|
|
|
op = CAIRO_OPERATOR_OVER;
|
|
|
|
|
|
else if (op == CAIRO_OPERATOR_COLOR_BURN)
|
|
|
|
|
|
op = CAIRO_OPERATOR_OVER;
|
2009-11-09 21:38:00 +01:00
|
|
|
|
}
|
2010-07-23 08:20:52 +02:00
|
|
|
|
|
|
|
|
|
|
return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op);
|
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)
|
|
|
|
|
|
{
|
2015-04-08 17:10:32 +02:00
|
|
|
|
/* The CGInterpolationQuality enumeration values seem to have the
|
|
|
|
|
|
* following meaning:
|
|
|
|
|
|
* - kCGInterpolationNone: nearest neighbor
|
|
|
|
|
|
* - kCGInterpolationLow: bilinear
|
|
|
|
|
|
* - kCGInterpolationHigh: bicubic? Lanczos?
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2008-02-25 21:06:21 -05:00
|
|
|
|
switch (filter) {
|
2010-07-27 14:43:15 +02:00
|
|
|
|
case CAIRO_FILTER_NEAREST:
|
|
|
|
|
|
case CAIRO_FILTER_FAST:
|
2011-01-06 17:40:05 +01:00
|
|
|
|
return kCGInterpolationNone;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2010-07-27 14:43:15 +02:00
|
|
|
|
case CAIRO_FILTER_BEST:
|
2015-04-08 17:10:32 +02:00
|
|
|
|
return kCGInterpolationHigh;
|
|
|
|
|
|
|
2010-07-27 14:43:15 +02:00
|
|
|
|
case CAIRO_FILTER_GOOD:
|
|
|
|
|
|
case CAIRO_FILTER_BILINEAR:
|
2015-04-08 17:10:32 +02:00
|
|
|
|
return kCGInterpolationLow;
|
|
|
|
|
|
|
2010-07-27 14:43:15 +02:00
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
|
|
};
|
2008-03-08 19:22:13 -08:00
|
|
|
|
|
quartz: Make huge domain handling more stable
Quartz cannot correctly handle arbitrary domains. Falling back is
needed to get correct results on very large (in parameter space)
gradients.
For PAD extended gradients, limiting the domain to (at most) [-0.5,
1.5] is sufficient to guarantee that it will correctly sample the
extreme stops and improves the accuracy (over having a much bigger
domain).
Fixes radial-gradient, radial-gradient-mask, radial-gradient-source,
radial-gradient-mask-source, radial-gradient-one-stop.
Improves the quality of the linear gradients in linear-gradient,
linear-gradient-subset, mask, operator-source, trap-clip.
2011-01-05 12:15:06 +01:00
|
|
|
|
/* Quartz computes a small number of samples of the gradient color
|
|
|
|
|
|
* function. On MacOS X 10.5 it apparently computes only 1024
|
|
|
|
|
|
* samples. */
|
|
|
|
|
|
#define MAX_GRADIENT_RANGE 1024
|
|
|
|
|
|
|
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
|
2011-07-29 11:33:46 +02:00
|
|
|
|
CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient,
|
|
|
|
|
|
const cairo_rectangle_int_t *extents,
|
|
|
|
|
|
cairo_circle_double_t *start,
|
|
|
|
|
|
cairo_circle_double_t *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
|
|
|
|
{
|
|
|
|
|
|
cairo_pattern_t *pat;
|
|
|
|
|
|
cairo_quartz_float_t input_value_range[2];
|
2010-05-11 13:58:10 -04:00
|
|
|
|
|
2010-11-17 22:07:09 +01:00
|
|
|
|
if (gradient->base.extend != CAIRO_EXTEND_NONE) {
|
|
|
|
|
|
double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
|
2011-01-03 16:41:34 +01:00
|
|
|
|
double t[2], tolerance;
|
|
|
|
|
|
|
|
|
|
|
|
tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix));
|
|
|
|
|
|
tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1);
|
2010-11-17 22:07:09 +01: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 (&gradient->base.matrix,
|
|
|
|
|
|
&bounds_x1, &bounds_y1,
|
|
|
|
|
|
&bounds_x2, &bounds_y2,
|
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
2011-01-03 16:41:34 +01:00
|
|
|
|
_cairo_gradient_pattern_box_to_parameter (gradient,
|
|
|
|
|
|
bounds_x1, bounds_y1,
|
|
|
|
|
|
bounds_x2, bounds_y2,
|
|
|
|
|
|
tolerance,
|
|
|
|
|
|
t);
|
2010-11-17 22:07:09 +01:00
|
|
|
|
|
quartz: Make huge domain handling more stable
Quartz cannot correctly handle arbitrary domains. Falling back is
needed to get correct results on very large (in parameter space)
gradients.
For PAD extended gradients, limiting the domain to (at most) [-0.5,
1.5] is sufficient to guarantee that it will correctly sample the
extreme stops and improves the accuracy (over having a much bigger
domain).
Fixes radial-gradient, radial-gradient-mask, radial-gradient-source,
radial-gradient-mask-source, radial-gradient-one-stop.
Improves the quality of the linear gradients in linear-gradient,
linear-gradient-subset, mask, operator-source, trap-clip.
2011-01-05 12:15:06 +01:00
|
|
|
|
if (gradient->base.extend == CAIRO_EXTEND_PAD) {
|
|
|
|
|
|
t[0] = MAX (t[0], -0.5);
|
|
|
|
|
|
t[1] = MIN (t[1], 1.5);
|
|
|
|
|
|
} else if (t[1] - t[0] > MAX_GRADIENT_RANGE)
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
2010-11-17 22:07:09 +01:00
|
|
|
|
/* set the input range for the function -- the function knows how
|
|
|
|
|
|
to map values outside of 0.0 .. 1.0 to the correct color */
|
|
|
|
|
|
input_value_range[0] = t[0];
|
|
|
|
|
|
input_value_range[1] = t[1];
|
2010-05-11 13:58:10 -04:00
|
|
|
|
} else {
|
2010-11-17 22:07:09 +01:00
|
|
|
|
input_value_range[0] = 0;
|
|
|
|
|
|
input_value_range[1] = 1;
|
2010-05-11 13:58:10 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-11-17 22:07:09 +01:00
|
|
|
|
_cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start);
|
|
|
|
|
|
_cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end);
|
2010-05-11 13:58:10 -04:00
|
|
|
|
|
2010-11-17 22:07:09 +01:00
|
|
|
|
if (_cairo_pattern_create_copy (&pat, &gradient->base))
|
2010-07-27 17:24:08 +02:00
|
|
|
|
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-03-17 17:37:22 -07:00
|
|
|
|
static cairo_status_t
|
2012-07-23 16:28:28 +00:00
|
|
|
|
_cairo_surface_to_cgimage (cairo_surface_t *source,
|
|
|
|
|
|
cairo_rectangle_int_t *extents,
|
|
|
|
|
|
cairo_format_t format,
|
2012-07-24 10:21:08 +02:00
|
|
|
|
cairo_matrix_t *matrix,
|
2012-07-23 16:28:28 +00:00
|
|
|
|
const cairo_clip_t *clip,
|
|
|
|
|
|
CGImageRef *image_out)
|
2008-02-25 21:06:21 -05:00
|
|
|
|
{
|
2010-01-24 21:59:32 +01:00
|
|
|
|
cairo_status_t status;
|
2022-04-26 12:20:04 -07:00
|
|
|
|
cairo_quartz_image_surface_t *image_surface;
|
|
|
|
|
|
void *image_extra;
|
2020-12-05 11:44:19 -08:00
|
|
|
|
cairo_bool_t acquired = FALSE;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2022-05-01 13:30:21 -07:00
|
|
|
|
if (_is_quartz_surface (source)) {
|
2022-04-26 12:20:04 -07:00
|
|
|
|
CGContextRef cgContext = cairo_quartz_surface_get_cg_context(source);
|
2022-05-01 13:30:21 -07:00
|
|
|
|
if (_cairo_quartz_is_zero_surface (source)) {
|
2008-03-17 17:37:22 -07:00
|
|
|
|
*image_out = NULL;
|
2011-07-30 17:28:21 +01:00
|
|
|
|
return CAIRO_INT_STATUS_NOTHING_TO_DO;
|
2008-03-17 17:37:22 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-26 12:20:04 -07:00
|
|
|
|
if (_cairo_quartz_is_cgcontext_bitmap_context (cgContext)) {
|
|
|
|
|
|
*image_out = _cairo_quartz_surface_snapshot_get_image (source);
|
2022-02-10 12:54:46 -08:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2008-03-08 15:31:15 -08:00
|
|
|
|
}
|
2022-02-10 12:54:46 -08:00
|
|
|
|
|
|
|
|
|
|
*image_out = NULL;
|
|
|
|
|
|
return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2012-07-23 16:28:28 +00:00
|
|
|
|
if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
|
2022-04-26 12:20:04 -07:00
|
|
|
|
cairo_image_surface_t *surface =
|
|
|
|
|
|
(cairo_image_surface_t*)cairo_image_surface_create (format, extents->width,
|
|
|
|
|
|
extents->height);
|
|
|
|
|
|
if (unlikely (surface->base.status)) {
|
|
|
|
|
|
status = surface->base.status;
|
|
|
|
|
|
cairo_surface_destroy (&surface->base);
|
2012-07-23 16:28:28 +00:00
|
|
|
|
return status;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
status = _cairo_recording_surface_replay_with_clip (source,
|
2012-07-24 10:21:08 +02:00
|
|
|
|
matrix,
|
2022-04-26 12:20:04 -07:00
|
|
|
|
&surface->base,
|
2023-02-19 21:10:58 +10:30
|
|
|
|
NULL);
|
2012-07-23 16:28:28 +00:00
|
|
|
|
if (unlikely (status)) {
|
2022-04-26 12:20:04 -07:00
|
|
|
|
cairo_surface_destroy (&surface->base);
|
2012-07-23 16:28:28 +00:00
|
|
|
|
return status;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-26 12:20:04 -07:00
|
|
|
|
image_surface =
|
|
|
|
|
|
(cairo_quartz_image_surface_t*)cairo_quartz_image_surface_create (&surface->base);
|
2012-07-24 10:21:08 +02:00
|
|
|
|
cairo_matrix_init_identity (matrix);
|
2012-07-23 16:28:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
else {
|
2022-04-26 12:20:04 -07:00
|
|
|
|
cairo_image_surface_t *surface;
|
|
|
|
|
|
status = _cairo_surface_acquire_source_image (source, &surface,
|
2020-11-30 17:16:00 -08:00
|
|
|
|
&image_extra);
|
|
|
|
|
|
if (unlikely (status))
|
2012-07-23 16:28:28 +00:00
|
|
|
|
return status;
|
2022-04-26 12:20:04 -07:00
|
|
|
|
image_surface =
|
|
|
|
|
|
(cairo_quartz_image_surface_t*)cairo_quartz_image_surface_create (&surface->base);
|
|
|
|
|
|
status = image_surface->base.status;
|
|
|
|
|
|
if (status)
|
|
|
|
|
|
_cairo_surface_release_source_image (source, surface, image_extra);
|
2020-12-05 11:44:19 -08:00
|
|
|
|
else
|
2022-04-26 12:20:04 -07:00
|
|
|
|
acquired = TRUE;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2022-04-26 12:20:04 -07:00
|
|
|
|
*image_out = NULL;
|
|
|
|
|
|
if (image_surface->width > 0 && image_surface->height > 0) {
|
|
|
|
|
|
*image_out = _cairo_quartz_surface_snapshot_get_image (&image_surface->base);
|
|
|
|
|
|
status = CAIRO_STATUS_SUCCESS;
|
2020-12-05 11:44:19 -08:00
|
|
|
|
}
|
2020-11-30 15:13:41 -08:00
|
|
|
|
|
2022-04-26 12:20:04 -07:00
|
|
|
|
if (acquired) {
|
|
|
|
|
|
_cairo_surface_release_source_image (source, image_surface->imageSurface, image_extra);
|
|
|
|
|
|
image_surface->imageSurface = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
cairo_surface_destroy (&image_surface->base);
|
2020-12-05 11:44:19 -08:00
|
|
|
|
|
|
|
|
|
|
/* TODO: differentiate memory error and unsupported surface type */
|
|
|
|
|
|
if (unlikely (*image_out == NULL))
|
|
|
|
|
|
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
|
|
|
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
|
2022-02-24 14:58:57 -08:00
|
|
|
|
_cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *surface,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
2012-07-23 16:28:28 +00:00
|
|
|
|
const cairo_clip_t *clip,
|
2007-10-20 23:41:46 +01:00
|
|
|
|
CGPatternRef *cgpat)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2022-02-24 14:58:57 -08:00
|
|
|
|
cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t *) source;
|
2022-02-19 18:38:29 -08:00
|
|
|
|
cairo_surface_t *pat_surf = spattern->surface;
|
2007-06-18 16:56:24 -07:00
|
|
|
|
cairo_rectangle_int_t extents;
|
2022-02-24 14:58:57 -08:00
|
|
|
|
cairo_format_t format = _cairo_format_from_content (surface->base.content);
|
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;
|
2022-02-19 18:38:29 -08:00
|
|
|
|
cairo_bool_t is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
|
|
|
|
|
|
cairo_matrix_t m = spattern->base.matrix;
|
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 */
|
2022-02-24 14:58:57 -08:00
|
|
|
|
assert (source->type == CAIRO_PATTERN_TYPE_SURFACE);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2022-02-19 18:38:29 -08:00
|
|
|
|
if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING)
|
2012-07-23 16:28:28 +00:00
|
|
|
|
assert (is_bounded);
|
2007-06-18 00:07:33 +01:00
|
|
|
|
|
2012-07-23 16:28:28 +00:00
|
|
|
|
status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
|
2012-07-24 10:21:08 +02:00
|
|
|
|
&m, clip, &image);
|
2022-02-19 18:38:29 -08:00
|
|
|
|
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (status))
|
2010-01-02 13:23:53 +01:00
|
|
|
|
return status;
|
2008-03-17 17:37:22 -07:00
|
|
|
|
|
2024-06-21 10:03:52 +09:30
|
|
|
|
info = _cairo_calloc (sizeof (SurfacePatternDrawInfo));
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (!info))
|
2022-02-19 18:38:29 -08:00
|
|
|
|
{
|
|
|
|
|
|
CGImageRelease (image);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
return CAIRO_STATUS_NO_MEMORY;
|
2022-02-19 18:38:29 -08:00
|
|
|
|
}
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2022-02-24 14:58:57 -08:00
|
|
|
|
switch (spattern->base.extend) {
|
|
|
|
|
|
case CAIRO_EXTEND_NONE:
|
2022-10-19 14:59:45 +01:00
|
|
|
|
case CAIRO_EXTEND_PAD:
|
2022-02-24 14:58:57 -08:00
|
|
|
|
case CAIRO_EXTEND_REPEAT:
|
|
|
|
|
|
pbounds.size.width = extents.width;
|
|
|
|
|
|
pbounds.size.height = extents.height;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case CAIRO_EXTEND_REFLECT:
|
2008-02-25 21:06:21 -05:00
|
|
|
|
pbounds.size.width = 2.0 * extents.width;
|
|
|
|
|
|
pbounds.size.height = 2.0 * extents.height;
|
|
|
|
|
|
info->do_reflect = TRUE;
|
2022-02-24 14:58:57 -08:00
|
|
|
|
break;
|
2007-06-07 23:22:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
rw = pbounds.size.width;
|
|
|
|
|
|
rh = pbounds.size.height;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
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.
|
|
|
|
|
|
*/
|
2022-02-24 14:58:57 -08:00
|
|
|
|
ptransform = CGAffineTransformConcat (stransform, surface->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));
|
2022-02-24 14:58:57 -08:00
|
|
|
|
CGAffineTransform xform = CGContextGetCTM (surface->cgContext);
|
2010-07-27 15:59:31 +02:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2010-07-29 16:48:30 +02:00
|
|
|
|
/* State used during a drawing operation. */
|
|
|
|
|
|
typedef struct {
|
2011-01-05 16:12:34 +01:00
|
|
|
|
/* The destination of the mask */
|
|
|
|
|
|
CGContextRef cgMaskContext;
|
|
|
|
|
|
|
|
|
|
|
|
/* The destination of the drawing of the source */
|
|
|
|
|
|
CGContextRef cgDrawContext;
|
|
|
|
|
|
|
2011-01-06 17:40:05 +01:00
|
|
|
|
/* The filter to be used when drawing the source */
|
|
|
|
|
|
CGInterpolationQuality filter;
|
|
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
/* Action type */
|
2010-07-29 16:48:30 +02:00
|
|
|
|
cairo_quartz_action_t action;
|
|
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
/* Destination rect */
|
|
|
|
|
|
CGRect rect;
|
|
|
|
|
|
|
2022-02-24 14:58:57 -08:00
|
|
|
|
/* Used with DO_SHADING, DO_IMAGE */
|
2010-07-29 16:48:30 +02:00
|
|
|
|
CGAffineTransform transform;
|
|
|
|
|
|
|
2022-02-24 14:58:57 -08:00
|
|
|
|
/* Used with DO_IMAGE */
|
2010-07-29 16:48:30 +02:00
|
|
|
|
CGImageRef image;
|
|
|
|
|
|
|
|
|
|
|
|
/* Used with DO_SHADING */
|
|
|
|
|
|
CGShadingRef shading;
|
|
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
/* Temporary destination for unbounded operations */
|
|
|
|
|
|
CGLayerRef layer;
|
|
|
|
|
|
CGRect clipRect;
|
2010-07-29 16:48:30 +02:00
|
|
|
|
} cairo_quartz_drawing_state_t;
|
|
|
|
|
|
|
2022-02-19 18:37:16 -08:00
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_setup_pattern_source (cairo_quartz_drawing_state_t *state,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
cairo_quartz_surface_t *surface,
|
|
|
|
|
|
const cairo_clip_t *clip,
|
|
|
|
|
|
cairo_operator_t op)
|
|
|
|
|
|
{
|
|
|
|
|
|
const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
|
|
|
|
|
|
cairo_surface_t *pat_surf = spat->surface;
|
|
|
|
|
|
cairo_matrix_t m = spat->base.matrix;
|
|
|
|
|
|
cairo_format_t format = _cairo_format_from_content (surface->base.content);
|
2022-02-24 14:58:57 -08:00
|
|
|
|
cairo_rectangle_int_t extents, pattern_extents;
|
2022-02-19 18:37:16 -08:00
|
|
|
|
CGImageRef img;
|
|
|
|
|
|
|
|
|
|
|
|
cairo_quartz_float_t patternAlpha = 1.0f;
|
|
|
|
|
|
CGColorSpaceRef patternSpace;
|
2022-02-24 14:58:57 -08:00
|
|
|
|
CGPatternRef cgpat = NULL;
|
2022-02-19 18:37:16 -08:00
|
|
|
|
cairo_int_status_t status;
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGColorRef black = _cairo_quartz_black (_cairo_quartz_create_color_space (state->cgDrawContext));
|
2022-02-19 18:37:16 -08:00
|
|
|
|
|
|
|
|
|
|
_cairo_surface_get_extents (&surface->base, &extents);
|
|
|
|
|
|
|
|
|
|
|
|
if (pat_surf->backend->type == CAIRO_SURFACE_TYPE_QUARTZ) {
|
|
|
|
|
|
cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf;
|
|
|
|
|
|
|
|
|
|
|
|
if (quartz_surf->cgLayer && source->extend == CAIRO_EXTEND_NONE) {
|
|
|
|
|
|
cairo_matrix_invert (&m);
|
|
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
|
|
|
|
|
|
state->rect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
|
|
|
|
|
|
state->layer = quartz_surf->cgLayer;
|
|
|
|
|
|
state->action = DO_LAYER;
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
|
|
|
|
|
|
&m, clip, &img); // Note that only pat_surf will get used!
|
|
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
return status;
|
|
|
|
|
|
|
|
|
|
|
|
state->image = img;
|
|
|
|
|
|
|
2022-02-24 14:58:57 -08:00
|
|
|
|
if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
|
2022-02-19 18:37:16 -08:00
|
|
|
|
m.x0 = -ceil (m.x0 - 0.5);
|
|
|
|
|
|
m.y0 = -ceil (m.y0 - 0.5);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
cairo_matrix_invert (&m);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
|
|
|
|
|
|
|
|
|
|
|
|
if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
|
2022-02-24 14:58:57 -08:00
|
|
|
|
cairo_bool_t is_bounded = _cairo_surface_get_extents (pat_surf, &pattern_extents);
|
2022-02-19 18:37:16 -08:00
|
|
|
|
assert (is_bounded);
|
2022-02-24 14:58:57 -08:00
|
|
|
|
} else {
|
|
|
|
|
|
_cairo_surface_get_extents (&surface->base, &pattern_extents);
|
2022-02-19 18:37:16 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (source->extend == CAIRO_EXTEND_NONE) {
|
|
|
|
|
|
int x, y;
|
2022-02-24 14:58:57 -08:00
|
|
|
|
|
2022-02-19 18:37:16 -08:00
|
|
|
|
if (op == CAIRO_OPERATOR_SOURCE &&
|
|
|
|
|
|
(pat_surf->content == CAIRO_CONTENT_ALPHA ||
|
|
|
|
|
|
! _cairo_matrix_is_integer_translation (&m, &x, &y)))
|
|
|
|
|
|
{
|
|
|
|
|
|
state->layer = CGLayerCreateWithContext (surface->cgContext,
|
|
|
|
|
|
state->clipRect.size,
|
|
|
|
|
|
NULL);
|
|
|
|
|
|
state->cgDrawContext = CGLayerGetContext (state->layer);
|
|
|
|
|
|
CGContextTranslateCTM (state->cgDrawContext,
|
|
|
|
|
|
-state->clipRect.origin.x,
|
|
|
|
|
|
-state->clipRect.origin.y);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGContextSetFillColorWithColor (state->cgDrawContext, black);
|
2022-02-19 18:37:16 -08:00
|
|
|
|
|
2022-02-24 14:58:57 -08:00
|
|
|
|
state->rect = CGRectMake (0, 0, pattern_extents.width, pattern_extents.height);
|
2022-02-19 18:37:16 -08:00
|
|
|
|
state->action = DO_IMAGE;
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-24 14:58:57 -08:00
|
|
|
|
if (source->extend == CAIRO_EXTEND_REPEAT)
|
|
|
|
|
|
{
|
|
|
|
|
|
CGAffineTransform xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
|
|
|
|
|
|
state->transform);
|
|
|
|
|
|
CGRect srcRect = CGRectMake (0, 0, extents.width, extents.height);
|
2022-02-19 18:37:16 -08:00
|
|
|
|
srcRect = CGRectApplyAffineTransform (srcRect, xform);
|
2022-02-24 14:58:57 -08:00
|
|
|
|
xform = CGAffineTransformInvert (xform);
|
|
|
|
|
|
srcRect = CGRectApplyAffineTransform (srcRect, xform);
|
|
|
|
|
|
state->rect = srcRect;
|
2022-02-19 18:37:16 -08:00
|
|
|
|
}
|
2022-02-24 14:58:57 -08:00
|
|
|
|
|
|
|
|
|
|
status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, clip, &cgpat);
|
2022-02-19 18:37:16 -08:00
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
return status;
|
|
|
|
|
|
|
|
|
|
|
|
patternSpace = CGColorSpaceCreatePattern (NULL);
|
2022-02-24 14:58:57 -08:00
|
|
|
|
/* To pass pthread-same-source. */
|
|
|
|
|
|
if (source->extend == CAIRO_EXTEND_REPEAT)
|
|
|
|
|
|
CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
|
2022-02-19 18:37:16 -08:00
|
|
|
|
CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
|
2022-02-24 14:58:57 -08:00
|
|
|
|
CGContextSetFillPattern (state->cgDrawContext, cgpat, &patternAlpha);
|
2022-02-19 18:37:16 -08:00
|
|
|
|
CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
|
2022-02-24 14:58:57 -08:00
|
|
|
|
CGContextSetStrokePattern (state->cgDrawContext, cgpat, &patternAlpha);
|
2022-02-19 18:37:16 -08:00
|
|
|
|
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
|
|
|
|
|
|
*/
|
|
|
|
|
|
CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
|
|
|
|
|
|
|
2022-02-24 14:58:57 -08:00
|
|
|
|
CGPatternRelease (cgpat);
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGColorRelease (black);
|
2022-02-19 18:37:16 -08:00
|
|
|
|
|
|
|
|
|
|
state->action = DO_DIRECT;
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
/*
|
2022-02-23 13:12:35 +00:00
|
|
|
|
Quartz does not support repeating gradients. We handle repeating gradients
|
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
|
|
|
|
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
|
2010-11-17 22:07:09 +01:00
|
|
|
|
the gradient is very coarse. _cairo_quartz_create_gradient_function computes
|
|
|
|
|
|
the number of repetitions needed based on the 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
|
|
|
|
*/
|
2010-10-08 09:49:51 +02:00
|
|
|
|
static cairo_int_status_t
|
2010-11-17 22:07:09 +01:00
|
|
|
|
_cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
|
|
|
|
|
|
const cairo_gradient_pattern_t *gradient,
|
|
|
|
|
|
const cairo_rectangle_int_t *extents)
|
2007-12-06 21:31:14 +00:00
|
|
|
|
{
|
|
|
|
|
|
cairo_matrix_t mat;
|
2010-11-17 22:07:09 +01:00
|
|
|
|
cairo_circle_double_t start, end;
|
2007-12-06 21:31:14 +00:00
|
|
|
|
CGFunctionRef gradFunc;
|
2008-03-08 19:22:13 -08:00
|
|
|
|
CGColorSpaceRef rgb;
|
2010-11-17 22:07:09 +01:00
|
|
|
|
bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
|
2007-12-06 21:31:14 +00:00
|
|
|
|
|
2010-11-17 22:07:09 +01:00
|
|
|
|
assert (gradient->n_stops > 0);
|
2007-12-06 21:31:15 +00:00
|
|
|
|
|
2010-11-17 22:07:09 +01:00
|
|
|
|
mat = gradient->base.matrix;
|
2008-03-07 16:46:15 -08:00
|
|
|
|
cairo_matrix_invert (&mat);
|
2010-07-29 16:48:30 +02:00
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
|
2008-03-07 16:46:15 -08:00
|
|
|
|
|
2011-07-29 11:33:46 +02:00
|
|
|
|
gradFunc = CairoQuartzCreateGradientFunction (gradient, extents,
|
|
|
|
|
|
&start, &end);
|
2010-11-17 22:07:09 +01:00
|
|
|
|
|
quartz: Make huge domain handling more stable
Quartz cannot correctly handle arbitrary domains. Falling back is
needed to get correct results on very large (in parameter space)
gradients.
For PAD extended gradients, limiting the domain to (at most) [-0.5,
1.5] is sufficient to guarantee that it will correctly sample the
extreme stops and improves the accuracy (over having a much bigger
domain).
Fixes radial-gradient, radial-gradient-mask, radial-gradient-source,
radial-gradient-mask-source, radial-gradient-one-stop.
Improves the quality of the linear gradients in linear-gradient,
linear-gradient-subset, mask, operator-source, trap-clip.
2011-01-05 12:15:06 +01:00
|
|
|
|
if (unlikely (gradFunc == NULL))
|
|
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
|
|
|
2023-02-07 09:48:16 -08:00
|
|
|
|
rgb = _cairo_quartz_create_color_space (NULL);
|
quartz: Make huge domain handling more stable
Quartz cannot correctly handle arbitrary domains. Falling back is
needed to get correct results on very large (in parameter space)
gradients.
For PAD extended gradients, limiting the domain to (at most) [-0.5,
1.5] is sufficient to guarantee that it will correctly sample the
extreme stops and improves the accuracy (over having a much bigger
domain).
Fixes radial-gradient, radial-gradient-mask, radial-gradient-source,
radial-gradient-mask-source, radial-gradient-one-stop.
Improves the quality of the linear gradients in linear-gradient,
linear-gradient-subset, mask, operator-source, trap-clip.
2011-01-05 12:15:06 +01:00
|
|
|
|
|
2010-11-17 22:07:09 +01:00
|
|
|
|
if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
|
|
|
|
|
|
state->shading = CGShadingCreateAxial (rgb,
|
|
|
|
|
|
CGPointMake (start.center.x,
|
|
|
|
|
|
start.center.y),
|
|
|
|
|
|
CGPointMake (end.center.x,
|
|
|
|
|
|
end.center.y),
|
|
|
|
|
|
gradFunc,
|
|
|
|
|
|
extend, extend);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
state->shading = CGShadingCreateRadial (rgb,
|
|
|
|
|
|
CGPointMake (start.center.x,
|
|
|
|
|
|
start.center.y),
|
|
|
|
|
|
MAX (start.radius, 0),
|
|
|
|
|
|
CGPointMake (end.center.x,
|
|
|
|
|
|
end.center.y),
|
|
|
|
|
|
MAX (end.radius, 0),
|
|
|
|
|
|
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-07-29 16:48:30 +02:00
|
|
|
|
state->action = DO_SHADING;
|
2010-10-08 09:49:51 +02:00
|
|
|
|
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
|
2011-01-05 16:12:34 +01:00
|
|
|
|
_cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
|
2012-07-23 16:28:28 +00:00
|
|
|
|
cairo_composite_rectangles_t *composite)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2012-07-23 16:28:28 +00:00
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) composite->surface;
|
|
|
|
|
|
cairo_operator_t op = composite->op;
|
|
|
|
|
|
const cairo_pattern_t *source = &composite->source_pattern.base;
|
|
|
|
|
|
const cairo_clip_t *clip = composite->clip;
|
2011-01-05 16:12:34 +01:00
|
|
|
|
cairo_bool_t needs_temp;
|
2010-07-28 19:03:13 +02:00
|
|
|
|
cairo_status_t status;
|
|
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
state->layer = NULL;
|
2010-07-29 16:48:30 +02:00
|
|
|
|
state->image = NULL;
|
|
|
|
|
|
state->shading = NULL;
|
2011-01-05 16:12:34 +01:00
|
|
|
|
state->cgDrawContext = NULL;
|
|
|
|
|
|
state->cgMaskContext = NULL;
|
2022-02-14 17:23:12 -08:00
|
|
|
|
state->layer = NULL;
|
2011-01-05 16:12:34 +01:00
|
|
|
|
|
|
|
|
|
|
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
|
|
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
return status;
|
|
|
|
|
|
|
|
|
|
|
|
status = _cairo_quartz_surface_set_cairo_operator (surface, op);
|
|
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
return status;
|
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);
|
2011-01-05 16:12:34 +01:00
|
|
|
|
state->clipRect = CGContextGetClipBoundingBox (surface->cgContext);
|
|
|
|
|
|
state->clipRect = CGRectIntegral (state->clipRect);
|
|
|
|
|
|
state->rect = state->clipRect;
|
2010-07-28 19:03:13 +02:00
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
state->cgMaskContext = surface->cgContext;
|
|
|
|
|
|
state->cgDrawContext = state->cgMaskContext;
|
2010-07-28 19:03:13 +02:00
|
|
|
|
|
2011-01-06 17:40:05 +01:00
|
|
|
|
state->filter = _cairo_quartz_filter_to_quartz (source->filter);
|
|
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
if (op == CAIRO_OPERATOR_CLEAR) {
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGColorRef black = _cairo_quartz_black (_cairo_quartz_create_color_space (surface->cgContext));
|
|
|
|
|
|
CGContextSetFillColorWithColor (state->cgDrawContext, black);
|
2011-01-05 16:12:34 +01:00
|
|
|
|
|
|
|
|
|
|
state->action = DO_DIRECT;
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGColorRelease (black);
|
2011-01-05 16:12:34 +01:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* To implement mask unbounded operations Quartz needs a temporary
|
|
|
|
|
|
* surface which will be composited entirely (ignoring the mask).
|
|
|
|
|
|
* To implement source unbounded operations Quartz needs a
|
|
|
|
|
|
* temporary surface which allows extending the source to a size
|
|
|
|
|
|
* covering the whole mask, but there are some optimization
|
|
|
|
|
|
* opportunities:
|
|
|
|
|
|
*
|
|
|
|
|
|
* - CLEAR completely ignores the source, thus we can just use a
|
|
|
|
|
|
* solid color fill.
|
|
|
|
|
|
*
|
|
|
|
|
|
* - SOURCE can be implemented by drawing the source and clearing
|
|
|
|
|
|
* outside of the source as long as the two regions have no
|
|
|
|
|
|
* intersection. This happens when the source is a pixel-aligned
|
|
|
|
|
|
* rectangle. If the source is at least as big as the
|
|
|
|
|
|
* intersection between the clip rectangle and the mask
|
|
|
|
|
|
* rectangle, no clear operation is needed.
|
|
|
|
|
|
*/
|
|
|
|
|
|
needs_temp = ! _cairo_operator_bounded_by_mask (op);
|
|
|
|
|
|
|
|
|
|
|
|
if (needs_temp) {
|
|
|
|
|
|
state->layer = CGLayerCreateWithContext (surface->cgContext,
|
|
|
|
|
|
state->clipRect.size,
|
|
|
|
|
|
NULL);
|
|
|
|
|
|
state->cgDrawContext = CGLayerGetContext (state->layer);
|
|
|
|
|
|
state->cgMaskContext = state->cgDrawContext;
|
|
|
|
|
|
CGContextTranslateCTM (state->cgDrawContext,
|
|
|
|
|
|
-state->clipRect.origin.x,
|
|
|
|
|
|
-state->clipRect.origin.y);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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;
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGColorRef color = _cairo_quartz_create_cgcolor (_cairo_quartz_create_color_space (state->cgDrawContext),
|
|
|
|
|
|
solid->color.red,
|
|
|
|
|
|
solid->color.green,
|
|
|
|
|
|
solid->color.blue,
|
|
|
|
|
|
solid->color.alpha);
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGContextSetStrokeColorWithColor (state->cgDrawContext, color);
|
|
|
|
|
|
CGContextSetFillColorWithColor (state->cgDrawContext, color);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGColorRelease (color);
|
2010-07-29 16:48:30 +02:00
|
|
|
|
state->action = DO_DIRECT;
|
2010-10-08 09:49:51 +02:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2008-01-27 16:52:39 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-11-17 22:07:09 +01:00
|
|
|
|
if (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
|
|
|
|
|
|
source->type == CAIRO_PATTERN_TYPE_RADIAL)
|
|
|
|
|
|
{
|
|
|
|
|
|
const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source;
|
2011-01-01 23:16:11 +01:00
|
|
|
|
cairo_rectangle_int_t extents;
|
|
|
|
|
|
|
|
|
|
|
|
extents = surface->virtual_extents;
|
|
|
|
|
|
extents.x -= surface->base.device_transform.x0;
|
|
|
|
|
|
extents.y -= surface->base.device_transform.y0;
|
|
|
|
|
|
_cairo_rectangle_union (&extents, &surface->extents);
|
|
|
|
|
|
|
|
|
|
|
|
return _cairo_quartz_setup_gradient_source (state, gpat, &extents);
|
2008-01-27 16:52:39 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-02-19 18:37:16 -08:00
|
|
|
|
if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
|
|
|
|
|
|
return _cairo_quartz_setup_pattern_source (state, source, surface, clip, op);
|
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
|
2011-01-05 16:12:34 +01:00
|
|
|
|
_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
|
2011-09-18 07:22:19 -07:00
|
|
|
|
cairo_composite_rectangles_t *extents)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2011-09-18 07:22:19 -07:00
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
|
2011-07-30 17:28:21 +01:00
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
if (state->layer) {
|
2022-02-14 17:23:12 -08:00
|
|
|
|
if (state->action != DO_LAYER)
|
|
|
|
|
|
CGContextDrawLayerInRect (surface->cgContext,
|
|
|
|
|
|
state->clipRect,
|
|
|
|
|
|
state->layer);
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGLayerRelease (state->layer);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (state->cgMaskContext)
|
|
|
|
|
|
CGContextRestoreGState (surface->cgContext);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2010-07-29 16:48:30 +02:00
|
|
|
|
if (state->image)
|
|
|
|
|
|
CGImageRelease (state->image);
|
2007-06-08 01:17:09 +01:00
|
|
|
|
|
2010-07-29 16:48:30 +02:00
|
|
|
|
if (state->shading)
|
|
|
|
|
|
CGShadingRelease (state->shading);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-14 17:23:12 -08:00
|
|
|
|
static inline void
|
|
|
|
|
|
_cairo_quartz_draw_cgcontext (cairo_quartz_drawing_state_t *state,
|
|
|
|
|
|
cairo_operator_t op)
|
|
|
|
|
|
{
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGColorSpaceRef cs = _cairo_quartz_create_color_space (state->cgDrawContext);
|
|
|
|
|
|
CGColorRef transparent = _cairo_quartz_create_cgcolor (cs, 0.0, 0.0, 0.0, 0.0); //releases cs
|
|
|
|
|
|
|
2022-02-14 17:23:12 -08:00
|
|
|
|
if (! (op == CAIRO_OPERATOR_SOURCE &&
|
|
|
|
|
|
state->cgDrawContext == state->cgMaskContext))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
CGContextBeginPath (state->cgDrawContext);
|
|
|
|
|
|
CGContextAddRect (state->cgDrawContext, state->rect);
|
|
|
|
|
|
|
|
|
|
|
|
CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
|
|
|
|
|
|
CGContextScaleCTM (state->cgDrawContext, 1, -1);
|
|
|
|
|
|
CGContextConcatCTM (state->cgDrawContext,
|
|
|
|
|
|
CGAffineTransformInvert (state->transform));
|
|
|
|
|
|
|
|
|
|
|
|
CGContextAddRect (state->cgDrawContext, state->clipRect);
|
|
|
|
|
|
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGContextSetFillColorWithColor (state->cgDrawContext, transparent);
|
2022-02-14 17:23:12 -08:00
|
|
|
|
CGContextEOFillPath (state->cgDrawContext);
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGColorRelease (transparent);
|
2022-02-14 17:23:12 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-11-09 14:21:40 +01:00
|
|
|
|
static void
|
2011-01-05 16:12:34 +01:00
|
|
|
|
_cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
|
|
|
|
|
|
cairo_operator_t op)
|
2009-11-09 14:21:40 +01:00
|
|
|
|
{
|
2011-01-06 17:40:05 +01:00
|
|
|
|
CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone);
|
|
|
|
|
|
CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
|
|
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
if (state->action == DO_DIRECT) {
|
|
|
|
|
|
CGContextFillRect (state->cgDrawContext, state->rect);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CGContextConcatCTM (state->cgDrawContext, state->transform);
|
2010-07-29 15:50:21 +02:00
|
|
|
|
|
2010-07-29 16:48:30 +02:00
|
|
|
|
if (state->action == DO_SHADING) {
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextDrawShading (state->cgDrawContext, state->shading);
|
2010-07-29 15:50:21 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
|
|
|
|
|
|
CGContextScaleCTM (state->cgDrawContext, 1, -1);
|
2009-11-09 14:21:40 +01:00
|
|
|
|
|
2010-07-29 16:48:30 +02:00
|
|
|
|
if (state->action == DO_IMAGE) {
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
|
2022-02-14 17:23:12 -08:00
|
|
|
|
_cairo_quartz_draw_cgcontext (state, op);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-02-24 14:58:57 -08:00
|
|
|
|
|
2022-02-14 17:23:12 -08:00
|
|
|
|
if (state->action == DO_LAYER) {
|
|
|
|
|
|
CGContextDrawLayerInRect (state->cgDrawContext, state->rect, state->layer);
|
|
|
|
|
|
_cairo_quartz_draw_cgcontext (state, op);
|
|
|
|
|
|
return;
|
2011-01-05 16:12:34 +01:00
|
|
|
|
}
|
2022-02-14 17:23:12 -08:00
|
|
|
|
assert (FALSE); // Unreachable
|
2009-11-09 14:21:40 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-01-06 19:47:08 +01:00
|
|
|
|
static cairo_image_surface_t *
|
|
|
|
|
|
_cairo_quartz_surface_map_to_image (void *abstract_surface,
|
|
|
|
|
|
const cairo_rectangle_int_t *extents)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2012-01-06 19:47:08 +01:00
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
2022-02-13 13:50:57 -08:00
|
|
|
|
cairo_surface_t *return_surface = NULL;
|
2012-01-06 19:47:08 +01:00
|
|
|
|
unsigned int stride, bitinfo, bpp, color_comps;
|
|
|
|
|
|
CGColorSpaceRef colorspace;
|
2022-04-17 10:47:10 -07:00
|
|
|
|
unsigned char *imageData;
|
2012-01-06 19:47:08 +01:00
|
|
|
|
cairo_format_t format;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2022-05-01 13:30:21 -07:00
|
|
|
|
if (_cairo_quartz_is_zero_surface (&surface->base))
|
2012-01-06 19:47:08 +01:00
|
|
|
|
return (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2012-01-06 19:47:08 +01:00
|
|
|
|
if (! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext))
|
2022-02-13 13:50:57 -08:00
|
|
|
|
return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2012-01-06 19:47:08 +01:00
|
|
|
|
bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
|
|
|
|
|
|
bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2012-01-06 19:47:08 +01:00
|
|
|
|
// let's hope they don't add YUV under us
|
2023-02-07 09:48:16 -08:00
|
|
|
|
colorspace = _cairo_quartz_create_color_space (surface->cgContext);
|
2012-01-06 19:47:08 +01:00
|
|
|
|
color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
|
2023-02-07 09:48:16 -08:00
|
|
|
|
CGColorSpaceRelease (colorspace);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2012-01-06 19:47:08 +01:00
|
|
|
|
/* XXX TODO: We can handle many more data formats by
|
|
|
|
|
|
* converting to pixman_format_t */
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2012-01-06 19:47:08 +01:00
|
|
|
|
if (bpp == 32 && color_comps == 3 &&
|
|
|
|
|
|
(bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
|
|
|
|
|
|
(bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
|
|
|
|
|
|
{
|
|
|
|
|
|
format = CAIRO_FORMAT_ARGB32;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (bpp == 32 && color_comps == 3 &&
|
|
|
|
|
|
(bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
|
|
|
|
|
|
(bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
|
|
|
|
|
|
{
|
|
|
|
|
|
format = CAIRO_FORMAT_RGB24;
|
|
|
|
|
|
}
|
2022-02-13 13:50:57 -08:00
|
|
|
|
else if (bpp == 8 && color_comps == 0)
|
2012-01-06 19:47:08 +01:00
|
|
|
|
{
|
2022-02-13 13:50:57 -08:00
|
|
|
|
format = CAIRO_FORMAT_A8;
|
2012-01-06 19:47:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2022-02-13 13:50:57 -08:00
|
|
|
|
return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
|
2012-01-06 19:47:08 +01:00
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2012-01-06 19:47:08 +01:00
|
|
|
|
imageData = CGBitmapContextGetData (surface->cgContext);
|
|
|
|
|
|
stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2022-02-13 13:50:57 -08:00
|
|
|
|
imageData += extents->y * stride + extents->x * bpp / 8;
|
|
|
|
|
|
return_surface = cairo_image_surface_create_for_data (imageData,
|
|
|
|
|
|
format,
|
|
|
|
|
|
extents->width,
|
|
|
|
|
|
extents->height,
|
|
|
|
|
|
stride);
|
|
|
|
|
|
|
|
|
|
|
|
return (cairo_image_surface_t *) return_surface;
|
2012-01-06 19:47:08 +01:00
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2012-01-06 19:47:08 +01:00
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_surface_unmap_image (void *abstract_surface,
|
|
|
|
|
|
cairo_image_surface_t *image)
|
|
|
|
|
|
{
|
|
|
|
|
|
cairo_surface_finish (&image->base);
|
|
|
|
|
|
cairo_surface_destroy (&image->base);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-01-06 19:47:08 +01:00
|
|
|
|
|
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
|
|
|
|
|
2022-05-01 13:30:21 -07:00
|
|
|
|
if (_cairo_quartz_is_zero_surface (&surface->base))
|
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;
|
|
|
|
|
|
|
2022-02-14 17:23:12 -08:00
|
|
|
|
if (surface->cgLayer)
|
|
|
|
|
|
{
|
|
|
|
|
|
CGLayerRelease (surface->cgLayer);
|
|
|
|
|
|
surface->cgLayer = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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-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
|
|
|
|
*image_extra = NULL;
|
|
|
|
|
|
|
2012-01-06 19:47:08 +01:00
|
|
|
|
*image_out = _cairo_quartz_surface_map_to_image (surface, &surface->extents);
|
|
|
|
|
|
if (unlikely (cairo_surface_status(&(*image_out)->base))) {
|
|
|
|
|
|
cairo_surface_destroy (&(*image_out)->base);
|
|
|
|
|
|
*image_out = NULL;
|
|
|
|
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
|
|
}
|
2008-11-25 20:15:15 +01:00
|
|
|
|
|
2012-01-06 19:47:08 +01:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2008-11-25 20:15:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2012-01-06 19:47:08 +01:00
|
|
|
|
_cairo_quartz_surface_unmap_image (abstract_surface, image);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-14 17:23:12 -08:00
|
|
|
|
static cairo_surface_t*
|
|
|
|
|
|
_cairo_quartz_surface_create_with_cglayer (cairo_quartz_surface_t *surface,
|
|
|
|
|
|
cairo_content_t content,
|
|
|
|
|
|
int width, int height)
|
|
|
|
|
|
{
|
|
|
|
|
|
CGAffineTransform xform;
|
|
|
|
|
|
CGContextRef context;
|
|
|
|
|
|
CGLayerRef layer;
|
|
|
|
|
|
cairo_quartz_surface_t* new_surface;
|
|
|
|
|
|
|
|
|
|
|
|
if (surface->cgContext == NULL || surface->cgLayer != NULL)
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
|
|
if (width <= 0 || height <= 0)
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
|
|
xform = CGContextGetUserSpaceToDeviceSpaceTransform (surface->cgContext);
|
|
|
|
|
|
layer = CGLayerCreateWithContext (surface->cgContext,
|
|
|
|
|
|
CGSizeMake (width * xform.a,
|
|
|
|
|
|
height * xform.d),
|
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
|
|
context = CGLayerGetContext (layer);
|
|
|
|
|
|
CGContextTranslateCTM (context, 0.0, height);
|
|
|
|
|
|
CGContextScaleCTM (context, xform.a, -xform.d);
|
|
|
|
|
|
new_surface = _cairo_quartz_surface_create_internal (context, content,
|
|
|
|
|
|
width, height);
|
|
|
|
|
|
if (unlikely (new_surface->base.status))
|
|
|
|
|
|
{
|
|
|
|
|
|
CGContextRelease (context);
|
|
|
|
|
|
CGLayerRelease (layer);
|
|
|
|
|
|
return &new_surface->base;
|
|
|
|
|
|
}
|
|
|
|
|
|
new_surface->cgLayer = CGLayerRetain(layer);
|
|
|
|
|
|
CGContextRetain(context);
|
|
|
|
|
|
new_surface->virtual_extents = surface->virtual_extents;
|
|
|
|
|
|
|
|
|
|
|
|
return &new_surface->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
|
|
|
|
|
2022-02-14 17:23:12 -08:00
|
|
|
|
// verify width and height of surface
|
|
|
|
|
|
if (!_cairo_quartz_verify_surface_size (width, height)) {
|
|
|
|
|
|
return _cairo_surface_create_in_error (_cairo_error
|
|
|
|
|
|
(CAIRO_STATUS_INVALID_SIZE));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
surface = (cairo_quartz_surface_t *) abstract_surface;
|
|
|
|
|
|
if (surface->cgContext && !surface->cgLayer && !(width && height))
|
|
|
|
|
|
_cairo_quartz_surface_create_with_cglayer (surface, content,
|
|
|
|
|
|
width, height);
|
|
|
|
|
|
|
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
|
|
|
|
|
2010-10-12 22:52:54 +02:00
|
|
|
|
similar = cairo_quartz_surface_create (format, width, height);
|
|
|
|
|
|
if (unlikely (similar->status))
|
|
|
|
|
|
return similar;
|
|
|
|
|
|
|
|
|
|
|
|
similar_quartz = (cairo_quartz_surface_t *) similar;
|
|
|
|
|
|
similar_quartz->virtual_extents = surface->virtual_extents;
|
|
|
|
|
|
|
|
|
|
|
|
return similar;
|
2005-01-16 08:35:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
static cairo_bool_t
|
|
|
|
|
|
_cairo_quartz_surface_get_extents (void *abstract_surface,
|
|
|
|
|
|
cairo_rectangle_int_t *extents)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2011-07-30 17:28:21 +01:00
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
|
2007-12-04 13:54:32 -08:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
*extents = surface->extents;
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
}
|
2007-11-06 15:40:30 -08:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_cg_paint (const cairo_compositor_t *compositor,
|
2011-09-18 07:22:19 -07:00
|
|
|
|
cairo_composite_rectangles_t *extents)
|
2011-07-30 17:28:21 +01:00
|
|
|
|
{
|
|
|
|
|
|
cairo_quartz_drawing_state_t state;
|
|
|
|
|
|
cairo_int_status_t rv;
|
2006-06-06 15:25:49 -07:00
|
|
|
|
|
2012-01-06 17:17:30 +01:00
|
|
|
|
ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n",
|
|
|
|
|
|
extents->surface, extents->op, extents->source_pattern.base.type));
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
rv = _cairo_quartz_setup_state (&state, extents);
|
|
|
|
|
|
if (unlikely (rv))
|
|
|
|
|
|
goto BAIL;
|
2007-12-04 13:49:59 -08:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_draw_source (&state, extents->op);
|
|
|
|
|
|
|
|
|
|
|
|
BAIL:
|
|
|
|
|
|
_cairo_quartz_teardown_state (&state, extents);
|
|
|
|
|
|
|
|
|
|
|
|
ND ((stderr, "-- paint\n"));
|
|
|
|
|
|
return rv;
|
|
|
|
|
|
}
|
2005-01-16 08:35:14 +00:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
static cairo_int_status_t
|
2011-09-18 07:22:19 -07:00
|
|
|
|
_cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents,
|
|
|
|
|
|
cairo_surface_t *mask_surf,
|
|
|
|
|
|
const cairo_matrix_t *mask_mat,
|
|
|
|
|
|
CGInterpolationQuality filter)
|
2011-07-30 17:28:21 +01:00
|
|
|
|
{
|
|
|
|
|
|
CGRect rect;
|
|
|
|
|
|
CGImageRef img;
|
|
|
|
|
|
cairo_status_t status;
|
|
|
|
|
|
CGAffineTransform mask_matrix;
|
|
|
|
|
|
cairo_quartz_drawing_state_t state;
|
2012-07-23 16:28:28 +00:00
|
|
|
|
cairo_format_t format = _cairo_format_from_content (extents->surface->content);
|
|
|
|
|
|
cairo_rectangle_int_t dest_extents;
|
2012-07-24 10:21:08 +02:00
|
|
|
|
cairo_matrix_t m = *mask_mat;
|
2011-07-30 17:28:21 +01:00
|
|
|
|
|
2012-07-23 16:28:28 +00:00
|
|
|
|
_cairo_surface_get_extents (extents->surface, &dest_extents);
|
|
|
|
|
|
status = _cairo_surface_to_cgimage (mask_surf, &dest_extents, format,
|
2012-07-24 10:21:08 +02:00
|
|
|
|
&m, extents->clip, &img);
|
2010-07-27 16:51:30 +02:00
|
|
|
|
if (unlikely (status))
|
2011-07-30 17:28:21 +01:00
|
|
|
|
return status;
|
2005-03-29 11:24:10 +00:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
status = _cairo_quartz_setup_state (&state, extents);
|
|
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
goto BAIL;
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img));
|
2012-07-24 10:21:08 +02:00
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (&m, &mask_matrix);
|
2008-03-17 17:37:22 -07:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
/* ClipToMask is essentially drawing an image, so we need to flip the CTM
|
|
|
|
|
|
* to get the image to appear oriented the right way */
|
|
|
|
|
|
CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix));
|
|
|
|
|
|
CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height);
|
|
|
|
|
|
CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
|
2008-03-17 17:37:22 -07:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
state.filter = filter;
|
2006-04-10 22:24:02 +02:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
CGContextSetInterpolationQuality (state.cgMaskContext, filter);
|
|
|
|
|
|
CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
CGContextClipToMask (state.cgMaskContext, rect, img);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
|
|
|
|
|
|
CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height);
|
|
|
|
|
|
CGContextConcatCTM (state.cgMaskContext, mask_matrix);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_draw_source (&state, extents->op);
|
2008-02-25 21:06:21 -05:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
BAIL:
|
|
|
|
|
|
_cairo_quartz_teardown_state (&state, extents);
|
2008-03-17 17:37:22 -07:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
CGImageRelease (img);
|
2005-03-29 11:24:10 +00:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
return status;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface,
|
|
|
|
|
|
cairo_composite_rectangles_t *extents)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2011-07-30 17:28:21 +01:00
|
|
|
|
cairo_quartz_drawing_state_t state;
|
|
|
|
|
|
double alpha = extents->mask_pattern.solid.color.alpha;
|
|
|
|
|
|
cairo_status_t status;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
status = _cairo_quartz_setup_state (&state, extents);
|
|
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
return status;
|
|
|
|
|
|
|
|
|
|
|
|
CGContextSetAlpha (surface->cgContext, alpha);
|
|
|
|
|
|
_cairo_quartz_draw_source (&state, extents->op);
|
|
|
|
|
|
|
|
|
|
|
|
_cairo_quartz_teardown_state (&state, extents);
|
|
|
|
|
|
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2005-01-16 08:35:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
static cairo_int_status_t
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_cg_mask (const cairo_compositor_t *compositor,
|
2011-09-18 07:22:19 -07:00
|
|
|
|
cairo_composite_rectangles_t *extents)
|
2006-04-10 22:24:02 +02:00
|
|
|
|
{
|
2011-07-30 17:28:21 +01:00
|
|
|
|
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface;
|
|
|
|
|
|
const cairo_pattern_t *source = &extents->source_pattern.base;
|
|
|
|
|
|
const cairo_pattern_t *mask = &extents->mask_pattern.base;
|
|
|
|
|
|
cairo_surface_t *mask_surf;
|
|
|
|
|
|
cairo_matrix_t matrix;
|
|
|
|
|
|
cairo_status_t status;
|
|
|
|
|
|
cairo_bool_t need_temp;
|
|
|
|
|
|
CGInterpolationQuality filter;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2012-01-06 17:17:30 +01:00
|
|
|
|
ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n",
|
|
|
|
|
|
extents->surface, extents->op, extents->source_pattern.base.type,
|
|
|
|
|
|
extents->mask_pattern.base.type));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
|
|
|
|
|
|
return _cairo_quartz_cg_mask_with_solid (surface, extents);
|
2007-12-04 13:49:59 -08:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
need_temp = (mask->type != CAIRO_PATTERN_TYPE_SURFACE ||
|
|
|
|
|
|
mask->extend != CAIRO_EXTEND_NONE);
|
2006-06-06 15:25:49 -07:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
filter = _cairo_quartz_filter_to_quartz (source->filter);
|
2006-04-10 22:24:02 +02:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
if (! need_temp) {
|
|
|
|
|
|
mask_surf = extents->mask_pattern.surface.surface;
|
2006-06-06 15:25:49 -07:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
/* When an opaque surface used as a mask in Quartz, its
|
2022-02-26 09:17:28 +00:00
|
|
|
|
* luminosity is used as the alpha value, so we can only use
|
2011-07-30 17:28:21 +01:00
|
|
|
|
* surfaces with alpha without creating a temporary mask. */
|
|
|
|
|
|
need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
|
|
|
|
|
|
}
|
2005-01-16 08:35:14 +00:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
if (! need_temp) {
|
|
|
|
|
|
CGInterpolationQuality mask_filter;
|
|
|
|
|
|
cairo_bool_t simple_transform;
|
2010-01-29 12:06:13 +01:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
matrix = mask->matrix;
|
2010-01-29 12:06:13 +01:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
mask_filter = _cairo_quartz_filter_to_quartz (mask->filter);
|
|
|
|
|
|
if (mask_filter == kCGInterpolationNone) {
|
|
|
|
|
|
simple_transform = _cairo_matrix_is_translation (&matrix);
|
|
|
|
|
|
if (simple_transform) {
|
|
|
|
|
|
matrix.x0 = ceil (matrix.x0 - 0.5);
|
|
|
|
|
|
matrix.y0 = ceil (matrix.y0 - 0.5);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
simple_transform = _cairo_matrix_is_integer_translation (&matrix,
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
NULL);
|
|
|
|
|
|
}
|
2010-01-29 12:06:13 +01:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
/* Quartz only allows one interpolation to be set for mask and
|
|
|
|
|
|
* source, so we can skip the temp surface only if the source
|
|
|
|
|
|
* filtering makes the mask look correct. */
|
|
|
|
|
|
if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
|
|
|
|
|
|
need_temp = ! (simple_transform || filter == mask_filter);
|
|
|
|
|
|
else
|
|
|
|
|
|
filter = mask_filter;
|
2010-01-29 12:06:13 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
if (need_temp) {
|
|
|
|
|
|
/* Render the mask to a surface */
|
|
|
|
|
|
mask_surf = _cairo_quartz_surface_create_similar (surface,
|
|
|
|
|
|
CAIRO_CONTENT_ALPHA,
|
|
|
|
|
|
surface->extents.width,
|
|
|
|
|
|
surface->extents.height);
|
|
|
|
|
|
status = mask_surf->status;
|
|
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
goto BAIL;
|
|
|
|
|
|
|
|
|
|
|
|
/* mask_surf is clear, so use OVER instead of SOURCE to avoid a
|
|
|
|
|
|
* temporary layer or fallback to cairo-image. */
|
|
|
|
|
|
status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL);
|
|
|
|
|
|
if (unlikely (status))
|
|
|
|
|
|
goto BAIL;
|
|
|
|
|
|
|
|
|
|
|
|
cairo_matrix_init_identity (&matrix);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
status = _cairo_quartz_cg_mask_with_surface (extents,
|
|
|
|
|
|
mask_surf, &matrix, filter);
|
|
|
|
|
|
|
|
|
|
|
|
BAIL:
|
|
|
|
|
|
|
|
|
|
|
|
if (need_temp)
|
|
|
|
|
|
cairo_surface_destroy (mask_surf);
|
|
|
|
|
|
|
|
|
|
|
|
return status;
|
2010-01-29 12:06:13 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static cairo_int_status_t
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_cg_fill (const cairo_compositor_t *compositor,
|
2011-09-18 07:22:19 -07:00
|
|
|
|
cairo_composite_rectangles_t *extents,
|
2011-07-30 17:28:21 +01:00
|
|
|
|
const cairo_path_fixed_t *path,
|
|
|
|
|
|
cairo_fill_rule_t fill_rule,
|
|
|
|
|
|
double tolerance,
|
|
|
|
|
|
cairo_antialias_t antialias)
|
2005-01-16 08:35:14 +00:00
|
|
|
|
{
|
2010-07-29 16:48:30 +02:00
|
|
|
|
cairo_quartz_drawing_state_t state;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
|
2012-01-06 17:17:30 +01:00
|
|
|
|
ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n",
|
|
|
|
|
|
extents->surface, extents->op, extents->source_pattern.base.type));
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
rv = _cairo_quartz_setup_state (&state, 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
|
|
|
|
if (unlikely (rv))
|
2011-01-05 16:12:34 +01:00
|
|
|
|
goto BAIL;
|
2005-11-14 12:57:31 +00:00
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
|
2006-06-06 15:25:49 -07:00
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
_cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
|
2010-07-29 16:48:30 +02:00
|
|
|
|
if (state.action == DO_DIRECT) {
|
2011-01-05 16:12:34 +01:00
|
|
|
|
assert (state.cgDrawContext == state.cgMaskContext);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (fill_rule == CAIRO_FILL_RULE_WINDING)
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextFillPath (state.cgMaskContext);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
else
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextEOFillPath (state.cgMaskContext);
|
2010-07-29 15:50:21 +02:00
|
|
|
|
} else {
|
2007-05-29 23:11:49 +01:00
|
|
|
|
if (fill_rule == CAIRO_FILL_RULE_WINDING)
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextClip (state.cgMaskContext);
|
2007-05-29 23:11:49 +01:00
|
|
|
|
else
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextEOClip (state.cgMaskContext);
|
2007-07-01 16:11:07 +01:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_draw_source (&state, extents->op);
|
2005-03-29 11:24:10 +00:00
|
|
|
|
}
|
2006-06-06 15:25:49 -07:00
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
BAIL:
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_teardown_state (&state, extents);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
|
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
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_cg_stroke (const cairo_compositor_t *compositor,
|
2011-09-18 07:22:19 -07:00
|
|
|
|
cairo_composite_rectangles_t *extents,
|
2011-07-30 17:28:21 +01:00
|
|
|
|
const 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)
|
2005-01-16 08:35:14 +00:00
|
|
|
|
{
|
2010-07-29 16:48:30 +02:00
|
|
|
|
cairo_quartz_drawing_state_t state;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
|
2011-01-07 18:00:37 +01:00
|
|
|
|
CGAffineTransform strokeTransform, invStrokeTransform;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2012-01-06 17:17:30 +01:00
|
|
|
|
ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n",
|
|
|
|
|
|
extents->surface, extents->op, extents->source_pattern.base.type));
|
2005-01-16 08:35:14 +00:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
rv = _cairo_quartz_setup_state (&state, 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
|
|
|
|
if (unlikely (rv))
|
2011-01-05 16:12:34 +01:00
|
|
|
|
goto BAIL;
|
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
|
|
|
|
|
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.
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
|
|
|
|
|
|
CGContextSetLineWidth (state.cgMaskContext, style->line_width);
|
|
|
|
|
|
CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
|
|
|
|
|
|
CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
|
|
|
|
|
|
CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit);
|
2008-03-25 11:17:57 -07:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
if (style->dash && style->num_dashes) {
|
2011-01-01 22:57:27 +01:00
|
|
|
|
cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)];
|
2009-12-29 08:47:09 +01:00
|
|
|
|
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;
|
2011-01-01 22:57:27 +01:00
|
|
|
|
if (max_dashes > ARRAY_LENGTH (sdash))
|
2009-12-29 08:47:09 +01:00
|
|
|
|
fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
|
2011-01-05 16:12:34 +01:00
|
|
|
|
if (unlikely (fdash == NULL)) {
|
|
|
|
|
|
rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
|
|
goto BAIL;
|
|
|
|
|
|
}
|
2010-01-30 09:29:48 +01:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextSetLineDash (state.cgMaskContext, 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
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0);
|
2010-10-08 09:49:51 +02:00
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
_cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
|
2008-03-12 18:11:57 -07:00
|
|
|
|
|
2010-04-07 18:34:25 +02:00
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextConcatCTM (state.cgMaskContext, strokeTransform);
|
2010-04-07 18:34:25 +02:00
|
|
|
|
|
2010-07-29 16:48:30 +02:00
|
|
|
|
if (state.action == DO_DIRECT) {
|
2011-01-05 16:12:34 +01:00
|
|
|
|
assert (state.cgDrawContext == state.cgMaskContext);
|
|
|
|
|
|
CGContextStrokePath (state.cgMaskContext);
|
2010-07-29 15:50:21 +02:00
|
|
|
|
} else {
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextReplacePathWithStrokedPath (state.cgMaskContext);
|
|
|
|
|
|
CGContextClip (state.cgMaskContext);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2011-01-07 18:00:37 +01:00
|
|
|
|
_cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform);
|
|
|
|
|
|
CGContextConcatCTM (state.cgMaskContext, invStrokeTransform);
|
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_draw_source (&state, extents->op);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
BAIL:
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_teardown_state (&state, extents);
|
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
|
|
|
|
}
|
|
|
|
|
|
|
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
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
|
2011-09-18 07:22:19 -07:00
|
|
|
|
cairo_composite_rectangles_t *extents,
|
|
|
|
|
|
cairo_scaled_font_t *scaled_font,
|
2011-07-30 17:28:21 +01:00
|
|
|
|
cairo_glyph_t *glyphs,
|
|
|
|
|
|
int num_glyphs,
|
|
|
|
|
|
cairo_bool_t overlap)
|
2005-04-19 16:29:04 +00:00
|
|
|
|
{
|
2011-01-07 18:00:37 +01:00
|
|
|
|
CGAffineTransform textTransform, invTextTransform;
|
2022-11-17 10:49:18 -08:00
|
|
|
|
CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGPoint)];
|
2022-04-21 17:19:15 -07:00
|
|
|
|
CGPoint cg_positions_static[CAIRO_STACK_ARRAY_LENGTH (CGPoint)];
|
2007-06-13 01:27:36 +01:00
|
|
|
|
CGGlyph *cg_glyphs = &glyphs_static[0];
|
2022-04-21 17:19:15 -07:00
|
|
|
|
CGPoint *cg_positions = &cg_positions_static[0];
|
2007-06-13 01:04:54 +01:00
|
|
|
|
|
2010-07-29 16:48:30 +02:00
|
|
|
|
cairo_quartz_drawing_state_t state;
|
2011-07-30 17:28:21 +01:00
|
|
|
|
cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
|
2022-04-21 17:19:15 -07:00
|
|
|
|
CGPoint origin;
|
|
|
|
|
|
CTFontRef ctFont = NULL;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2008-03-14 23:23:16 -07:00
|
|
|
|
cairo_bool_t didForceFontSmoothing = FALSE;
|
2008-03-06 16:52:54 -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;
|
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
rv = _cairo_quartz_setup_state (&state, 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
|
|
|
|
if (unlikely (rv))
|
2011-01-05 16:12:34 +01:00
|
|
|
|
goto BAIL;
|
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 16:48:30 +02:00
|
|
|
|
if (state.action == DO_DIRECT) {
|
2011-01-05 16:12:34 +01:00
|
|
|
|
assert (state.cgDrawContext == state.cgMaskContext);
|
|
|
|
|
|
CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill);
|
2010-07-29 15:50:21 +02:00
|
|
|
|
} else {
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-01-15 14:27:14 -08:00
|
|
|
|
/* this doesn't addref */
|
2022-04-21 17:19:15 -07:00
|
|
|
|
ctFont = _cairo_quartz_scaled_font_get_ct_font (scaled_font);
|
|
|
|
|
|
_cairo_quartz_set_antialiasing (state.cgMaskContext, scaled_font->options.antialias);
|
2008-03-08 15:16:05 -08:00
|
|
|
|
|
2011-01-01 22:57:27 +01:00
|
|
|
|
if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
|
2022-04-21 17:19:15 -07:00
|
|
|
|
cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGPoint));
|
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
|
|
|
|
|
2022-04-21 17:19:15 -07:00
|
|
|
|
cg_positions = (CGPoint*) (cg_glyphs + num_glyphs);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-03-16 17:12:59 +01:00
|
|
|
|
/* scale(1,-1) * scaled_font->scale */
|
2010-06-24 15:26:03 +02:00
|
|
|
|
textTransform = CGAffineTransformMake (scaled_font->scale.xx,
|
|
|
|
|
|
scaled_font->scale.yx,
|
2011-03-16 17:12:59 +01:00
|
|
|
|
-scaled_font->scale.xy,
|
|
|
|
|
|
-scaled_font->scale.yy,
|
2011-01-07 18:00:37 +01:00
|
|
|
|
0.0, 0.0);
|
2008-03-06 16:52:54 -08:00
|
|
|
|
|
2011-03-16 17:12:59 +01:00
|
|
|
|
/* scaled_font->scale_inverse * scale(1,-1) */
|
2011-01-07 18:00:37 +01:00
|
|
|
|
invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
|
2011-03-16 17:12:59 +01:00
|
|
|
|
-scaled_font->scale_inverse.yx,
|
2011-01-07 18:00:37 +01:00
|
|
|
|
scaled_font->scale_inverse.xy,
|
2011-03-16 17:12:59 +01:00
|
|
|
|
-scaled_font->scale_inverse.yy,
|
2011-01-07 18:00:37 +01:00
|
|
|
|
0.0, 0.0);
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2022-04-21 17:19:15 -07:00
|
|
|
|
origin = CGPointMake (glyphs[0].x, glyphs[0].y);
|
|
|
|
|
|
for (int i = 0; i < num_glyphs; ++i)
|
|
|
|
|
|
{
|
2007-06-13 01:27:36 +01:00
|
|
|
|
cg_glyphs[i] = glyphs[i].index;
|
2022-04-21 17:19:15 -07:00
|
|
|
|
cg_positions[i] = CGPointMake (glyphs[i].x - origin.x, glyphs[i].y - origin.y);
|
|
|
|
|
|
cg_positions[i] = CGPointApplyAffineTransform (cg_positions[i], invTextTransform);
|
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 */
|
2022-04-21 17:19:15 -07:00
|
|
|
|
CGContextTranslateCTM (state.cgMaskContext, origin.x, origin.y);
|
2011-01-05 16:12:34 +01:00
|
|
|
|
CGContextConcatCTM (state.cgMaskContext, textTransform);
|
2008-03-06 16:52:54 -08:00
|
|
|
|
|
2022-04-21 17:19:15 -07:00
|
|
|
|
CTFontDrawGlyphs (ctFont, cg_glyphs, cg_positions, num_glyphs, state.cgMaskContext);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2011-01-07 18:00:37 +01:00
|
|
|
|
CGContextConcatCTM (state.cgMaskContext, invTextTransform);
|
2022-04-21 17:19:15 -07:00
|
|
|
|
CGContextTranslateCTM (state.cgMaskContext, -origin.x, -origin.y);
|
2008-03-06 16:52:54 -08:00
|
|
|
|
|
2010-07-29 16:48:30 +02:00
|
|
|
|
if (state.action != DO_DIRECT)
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_draw_source (&state, extents->op);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2007-11-12 23:56:01 +00:00
|
|
|
|
BAIL:
|
2008-03-12 18:19:59 -07:00
|
|
|
|
if (didForceFontSmoothing)
|
2022-04-07 16:51:16 -07:00
|
|
|
|
CGContextSetAllowsFontSmoothing (state.cgMaskContext, FALSE);
|
2008-03-12 18:19:59 -07:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_teardown_state (&state, extents);
|
2008-03-12 18:11:57 -07: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
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
static const cairo_compositor_t _cairo_quartz_cg_compositor = {
|
|
|
|
|
|
&_cairo_fallback_compositor,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_cg_paint,
|
|
|
|
|
|
_cairo_quartz_cg_mask,
|
|
|
|
|
|
_cairo_quartz_cg_stroke,
|
|
|
|
|
|
_cairo_quartz_cg_fill,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
#if CAIRO_HAS_QUARTZ_FONT
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_cg_glyphs,
|
|
|
|
|
|
#else
|
|
|
|
|
|
NULL,
|
2010-01-29 12:06:13 +01:00
|
|
|
|
#endif
|
2011-07-30 17:28:21 +01:00
|
|
|
|
};
|
2010-01-29 12:06:13 +01:00
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
|
static cairo_int_status_t
|
|
|
|
|
|
_cairo_quartz_surface_paint (void *surface,
|
|
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
const cairo_clip_t *clip)
|
|
|
|
|
|
{
|
|
|
|
|
|
return _cairo_compositor_paint (&_cairo_quartz_cg_compositor,
|
|
|
|
|
|
surface, op, source, clip);
|
2010-01-29 12:06:13 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-10-20 23:41:32 +01:00
|
|
|
|
static cairo_int_status_t
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_surface_mask (void *surface,
|
|
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
const cairo_pattern_t *mask,
|
|
|
|
|
|
const cairo_clip_t *clip)
|
2007-07-01 14:37:57 +01:00
|
|
|
|
{
|
2011-07-30 17:28:21 +01:00
|
|
|
|
return _cairo_compositor_mask (&_cairo_quartz_cg_compositor,
|
|
|
|
|
|
surface, op, source, mask,
|
|
|
|
|
|
clip);
|
2007-07-01 14:37:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-01-05 16:12:34 +01:00
|
|
|
|
static cairo_int_status_t
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_surface_fill (void *surface,
|
|
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
const cairo_path_fixed_t *path,
|
|
|
|
|
|
cairo_fill_rule_t fill_rule,
|
|
|
|
|
|
double tolerance,
|
|
|
|
|
|
cairo_antialias_t antialias,
|
|
|
|
|
|
const cairo_clip_t *clip)
|
2011-01-05 16:12:34 +01:00
|
|
|
|
{
|
2011-07-30 17:28:21 +01:00
|
|
|
|
return _cairo_compositor_fill (&_cairo_quartz_cg_compositor,
|
|
|
|
|
|
surface, op, source, path,
|
|
|
|
|
|
fill_rule, tolerance, antialias,
|
|
|
|
|
|
clip);
|
2011-01-05 16:12:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
static cairo_int_status_t
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_surface_stroke (void *surface,
|
|
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
const 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,
|
|
|
|
|
|
const cairo_clip_t *clip)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2011-07-30 17:28:21 +01:00
|
|
|
|
return _cairo_compositor_stroke (&_cairo_quartz_cg_compositor,
|
|
|
|
|
|
surface, op, source, path,
|
|
|
|
|
|
style, ctm,ctm_inverse,
|
|
|
|
|
|
tolerance, antialias, clip);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-29 12:06:13 +01:00
|
|
|
|
static cairo_int_status_t
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_surface_glyphs (void *surface,
|
|
|
|
|
|
cairo_operator_t op,
|
|
|
|
|
|
const cairo_pattern_t *source,
|
|
|
|
|
|
cairo_glyph_t *glyphs,
|
|
|
|
|
|
int num_glyphs,
|
|
|
|
|
|
cairo_scaled_font_t *scaled_font,
|
2011-09-18 07:22:19 -07:00
|
|
|
|
const cairo_clip_t *clip)
|
2010-01-29 12:06:13 +01:00
|
|
|
|
{
|
2011-07-30 17:28:21 +01:00
|
|
|
|
return _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor,
|
|
|
|
|
|
surface, op, source,
|
|
|
|
|
|
glyphs, num_glyphs, scaled_font,
|
2011-09-18 07:22:19 -07:00
|
|
|
|
clip);
|
2010-01-29 12:06:13 +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
|
|
|
|
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
|
|
|
|
|
2022-05-01 13:30:21 -07:00
|
|
|
|
if (_cairo_quartz_is_zero_surface (&surface->base))
|
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
|
|
|
|
|
|
|
2022-02-10 12:54:46 -08:00
|
|
|
|
static const cairo_surface_backend_t cairo_quartz_surface_backend = {
|
2006-04-07 17:40:30 +02:00
|
|
|
|
CAIRO_SURFACE_TYPE_QUARTZ,
|
2011-07-26 20:48:55 +02:00
|
|
|
|
_cairo_quartz_surface_finish,
|
|
|
|
|
|
|
2011-07-19 07:50:47 +02:00
|
|
|
|
_cairo_default_context_create,
|
|
|
|
|
|
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_create_similar,
|
2011-07-26 20:48:55 +02:00
|
|
|
|
NULL, /* similar image */
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_surface_map_to_image,
|
|
|
|
|
|
_cairo_quartz_surface_unmap_image,
|
2011-07-26 20:48:55 +02:00
|
|
|
|
|
2012-02-10 09:20:21 +00:00
|
|
|
|
_cairo_surface_default_source,
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_acquire_source_image,
|
2007-05-14 23:46:14 -05:00
|
|
|
|
_cairo_quartz_surface_release_source_image,
|
2012-01-06 19:47:08 +01:00
|
|
|
|
NULL, /* snapshot */
|
2011-07-30 17:28:21 +01:00
|
|
|
|
|
2005-04-07 14:25:00 +00:00
|
|
|
|
NULL, /* copy_page */
|
|
|
|
|
|
NULL, /* show_page */
|
2011-07-30 17:28:21 +01:00
|
|
|
|
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_get_extents,
|
2007-02-20 12:15:35 -08:00
|
|
|
|
NULL, /* get_font_options */
|
2011-07-30 17:28:21 +01:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
NULL, /* flush */
|
|
|
|
|
|
NULL, /* mark_dirty_rectangle */
|
|
|
|
|
|
|
2007-03-06 23:24:33 +00:00
|
|
|
|
_cairo_quartz_surface_paint,
|
|
|
|
|
|
_cairo_quartz_surface_mask,
|
|
|
|
|
|
_cairo_quartz_surface_stroke,
|
|
|
|
|
|
_cairo_quartz_surface_fill,
|
2011-09-18 07:22:19 -07:00
|
|
|
|
NULL, /* fill-stroke */
|
2011-07-30 17:28:21 +01:00
|
|
|
|
_cairo_quartz_surface_glyphs,
|
2022-02-10 12:54:46 -08:00
|
|
|
|
NULL, /* has_show_text_glyphs */
|
|
|
|
|
|
NULL, /* show_text_glyphs */
|
|
|
|
|
|
NULL, /* get_supported_mime_types */
|
|
|
|
|
|
NULL, /* tag */
|
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
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/* Init the base surface */
|
2024-06-21 10:03:52 +09:30
|
|
|
|
surface = _cairo_calloc (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 */
|
2015-10-17 22:02:11 +10:30
|
|
|
|
content,
|
|
|
|
|
|
FALSE); /* is_vector */
|
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
|
|
|
|
|
2022-05-01 13:30:21 -07:00
|
|
|
|
if (_cairo_quartz_is_zero_surface (&surface->base)) {
|
2007-12-04 13:49:59 -08:00
|
|
|
|
surface->cgContext = NULL;
|
|
|
|
|
|
surface->cgContextBaseCTM = CGAffineTransformIdentity;
|
2012-01-06 20:13:39 +01:00
|
|
|
|
surface->base.is_clear = TRUE;
|
2007-12-04 13:49:59 -08:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
return surface;
|
|
|
|
|
|
}
|
2007-12-04 13:54:32 -08:00
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
/**
|
2012-03-24 17:48:07 +01:00
|
|
|
|
* cairo_quartz_surface_create_for_cg_context:
|
2007-02-20 12:15:35 -08:00
|
|
|
|
* @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.
|
|
|
|
|
|
*
|
2012-03-27 11:47:11 +02:00
|
|
|
|
* Since: 1.6
|
2007-02-20 12:15:35 -08:00
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
|
|
|
cairo_surface_t *
|
|
|
|
|
|
cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
|
|
|
|
|
|
unsigned int width,
|
|
|
|
|
|
unsigned int height)
|
|
|
|
|
|
{
|
2023-02-07 09:48:16 -08:00
|
|
|
|
cairo_quartz_surface_t *surf =
|
|
|
|
|
|
_cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2012-03-24 17:48:07 +01:00
|
|
|
|
* cairo_quartz_surface_create:
|
2007-02-20 12:15:35 -08:00
|
|
|
|
* @format: format of pixels in the surface to create
|
|
|
|
|
|
* @width: width of the surface, in pixels
|
|
|
|
|
|
* @height: height of the surface, in pixels
|
|
|
|
|
|
*
|
2023-02-07 09:48:16 -08:00
|
|
|
|
* Creates a Quartz surface backed by a CGBitmapContext using the main
|
|
|
|
|
|
* display's colorspace to avoid an expensive colorspace transform
|
|
|
|
|
|
* done serially on the CPU. This may produce slightly different
|
|
|
|
|
|
* colors from what's intended. Programs for which color management is
|
|
|
|
|
|
* important should create their own CGBitmapContext with a
|
|
|
|
|
|
* device-independent color space; most will expect Cairo to draw in
|
|
|
|
|
|
* sRGB and would use CGColorSpaceCreateWithName(kCGColorSpaceSRGB).
|
|
|
|
|
|
*
|
2007-02-20 12:15:35 -08:00
|
|
|
|
* All Cairo operations, including those that require software
|
|
|
|
|
|
* rendering, will succeed on this surface.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Return value: the newly created surface.
|
|
|
|
|
|
*
|
2012-03-27 11:47:11 +02:00
|
|
|
|
* Since: 1.6
|
2007-02-20 12:15:35 -08:00
|
|
|
|
**/
|
|
|
|
|
|
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;
|
2022-02-13 13:50:57 -08:00
|
|
|
|
void *imageData = NULL;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
int stride;
|
|
|
|
|
|
int bitsPerComponent;
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2023-02-07 09:48:16 -08:00
|
|
|
|
cgColorspace = _cairo_quartz_create_color_space (NULL);
|
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
|
|
|
|
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) {
|
2022-02-13 13:50:57 -08:00
|
|
|
|
if (imageData)
|
|
|
|
|
|
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);
|
2022-02-13 13:50:57 -08:00
|
|
|
|
|
|
|
|
|
|
if (imageData)
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-13 13:50:57 -08:00
|
|
|
|
surf->base.is_clear = TRUE;
|
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
|
|
|
|
/**
|
2012-03-24 17:48:07 +01: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.
|
|
|
|
|
|
*
|
2011-01-16 18:40:42 +01:00
|
|
|
|
* A call to cairo_surface_flush() is required before using the
|
|
|
|
|
|
* CGContextRef to ensure that all pending drawing operations are
|
|
|
|
|
|
* finished and to restore any temporary modification cairo has made
|
|
|
|
|
|
* to its state. A call to cairo_surface_mark_dirty() is required
|
|
|
|
|
|
* after the state or the content of the CGContextRef has been
|
|
|
|
|
|
* modified.
|
|
|
|
|
|
*
|
2007-02-20 12:15:35 -08:00
|
|
|
|
* Return value: the CGContextRef for the given surface.
|
|
|
|
|
|
*
|
2012-03-27 11:47:11 +02:00
|
|
|
|
* Since: 1.6
|
2007-02-20 12:15:35 -08:00
|
|
|
|
**/
|
|
|
|
|
|
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;
|
2022-04-26 12:20:04 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (surface && _cairo_surface_is_quartz_image (surface)) {
|
|
|
|
|
|
cairo_quartz_image_surface_t *quartz = (cairo_quartz_image_surface_t *) surface;
|
|
|
|
|
|
return quartz->cgContext;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return NULL;
|
2006-01-10 05:28:59 +00:00
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2014-10-02 18:10:00 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* _cairo_surface_is_quartz:
|
|
|
|
|
|
* @surface: a #cairo_surface_t
|
|
|
|
|
|
*
|
|
|
|
|
|
* Checks if a surface is a #cairo_quartz_surface_t
|
|
|
|
|
|
*
|
|
|
|
|
|
* Return value: True if the surface is an quartz surface
|
|
|
|
|
|
**/
|
2014-10-03 09:56:24 +02:00
|
|
|
|
cairo_bool_t
|
2010-07-27 18:04:36 +02:00
|
|
|
|
_cairo_surface_is_quartz (const cairo_surface_t *surface)
|
|
|
|
|
|
{
|
|
|
|
|
|
return surface->backend == &cairo_quartz_surface_backend;
|
|
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2022-02-10 12:54:46 -08:00
|
|
|
|
cairo_surface_t*
|
2022-04-26 12:20:04 -07:00
|
|
|
|
_cairo_quartz_snapshot_create (cairo_surface_t *surface)
|
2022-02-10 12:54:46 -08:00
|
|
|
|
{
|
|
|
|
|
|
cairo_quartz_snapshot_t *snapshot = NULL;
|
2022-04-26 12:20:04 -07:00
|
|
|
|
CGContextRef cgContext;
|
2022-05-01 13:30:21 -07:00
|
|
|
|
if (!surface || ! _is_quartz_surface (surface) || _cairo_quartz_is_zero_surface (surface))
|
2022-04-26 12:20:04 -07:00
|
|
|
|
return NULL;
|
2022-02-10 12:54:46 -08:00
|
|
|
|
|
2022-04-26 12:20:04 -07:00
|
|
|
|
if (_cairo_surface_is_quartz (surface) &&
|
|
|
|
|
|
! _cairo_quartz_is_cgcontext_bitmap_context (((cairo_quartz_surface_t*)surface)->cgContext))
|
2022-02-10 12:54:46 -08:00
|
|
|
|
return NULL;
|
|
|
|
|
|
|
2024-06-21 10:03:52 +09:30
|
|
|
|
snapshot = _cairo_calloc (sizeof (cairo_quartz_snapshot_t));
|
2022-02-10 12:54:46 -08:00
|
|
|
|
|
|
|
|
|
|
if (unlikely (surface == NULL))
|
|
|
|
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
|
|
|
|
|
|
|
|
|
|
memset (snapshot, 0, sizeof (cairo_quartz_snapshot_t));
|
2022-04-26 12:20:04 -07:00
|
|
|
|
cgContext = cairo_quartz_surface_get_cg_context (surface);
|
2022-02-10 12:54:46 -08:00
|
|
|
|
_cairo_surface_init (&snapshot->base,
|
|
|
|
|
|
&cairo_quartz_snapshot_backend,
|
|
|
|
|
|
NULL, CAIRO_CONTENT_COLOR_ALPHA, FALSE);
|
2022-04-26 12:20:04 -07:00
|
|
|
|
snapshot->image = CGBitmapContextCreateImage (cgContext);
|
|
|
|
|
|
_cairo_surface_attach_snapshot (surface, &snapshot->base, NULL);
|
|
|
|
|
|
cairo_surface_destroy (&snapshot->base); // The surface has reffed the snapshot so we must unref it here.
|
2022-02-10 12:54:46 -08:00
|
|
|
|
|
|
|
|
|
|
return &snapshot->base;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cairo_status_t
|
|
|
|
|
|
_cairo_quartz_snapshot_finish (void *surface)
|
|
|
|
|
|
{
|
|
|
|
|
|
cairo_quartz_snapshot_t *snapshot = (cairo_quartz_snapshot_t *)surface;
|
|
|
|
|
|
if (snapshot->image)
|
|
|
|
|
|
CGImageRelease (snapshot->image);
|
|
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CGImageRef
|
2022-04-26 12:20:04 -07:00
|
|
|
|
_cairo_quartz_surface_snapshot_get_image (cairo_surface_t *surface)
|
2022-02-10 12:54:46 -08:00
|
|
|
|
{
|
2022-04-26 12:20:04 -07:00
|
|
|
|
cairo_surface_t *snapshot;
|
2022-05-01 13:30:21 -07:00
|
|
|
|
assert (_is_quartz_surface (surface));
|
2022-04-26 12:20:04 -07:00
|
|
|
|
snapshot =
|
|
|
|
|
|
_cairo_surface_has_snapshot (surface, &cairo_quartz_snapshot_backend);
|
2022-02-10 12:54:46 -08:00
|
|
|
|
|
|
|
|
|
|
if (unlikely (!snapshot))
|
|
|
|
|
|
{
|
|
|
|
|
|
snapshot = _cairo_quartz_snapshot_create (surface);
|
|
|
|
|
|
if (unlikely (!snapshot || cairo_surface_status (snapshot)))
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return CGImageRetain (((cairo_quartz_snapshot_t*)snapshot)->image);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-02-20 12:15:35 -08:00
|
|
|
|
void
|
2023-02-07 09:48:16 -08:00
|
|
|
|
_cairo_quartz_image_to_png (CGImageRef image, const char *dest)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2022-02-19 18:34:01 -08:00
|
|
|
|
CFStringRef png_utti = CFSTR("public.png");
|
|
|
|
|
|
CFStringRef path;
|
|
|
|
|
|
CFURLRef url;
|
|
|
|
|
|
CGImageDestinationRef image_dest;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2023-02-07 09:48:16 -08:00
|
|
|
|
if (!dest)
|
|
|
|
|
|
return;
|
|
|
|
|
|
path = CFStringCreateWithCString (NULL, dest, kCFStringEncodingUTF8);
|
2022-02-19 18:34:01 -08:00
|
|
|
|
url = CFURLCreateWithFileSystemPath (NULL, path, kCFURLPOSIXPathStyle, FALSE);
|
|
|
|
|
|
image_dest = CGImageDestinationCreateWithURL (url, png_utti, 1, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
CGImageDestinationAddImage (image_dest, image, NULL);
|
|
|
|
|
|
CGImageDestinationFinalize (image_dest);
|
|
|
|
|
|
|
|
|
|
|
|
CFRelease (url);
|
|
|
|
|
|
CFRelease (path);
|
|
|
|
|
|
}
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2023-02-07 09:48:16 -08:00
|
|
|
|
cairo_status_t
|
|
|
|
|
|
_cairo_quartz_surface_to_png (cairo_surface_t *abstract_surface, const char *dest)
|
2007-02-20 12:15:35 -08:00
|
|
|
|
{
|
2022-02-19 18:34:01 -08:00
|
|
|
|
CGImageRef image;
|
2023-02-07 09:48:16 -08:00
|
|
|
|
cairo_quartz_surface_t *surface;
|
|
|
|
|
|
if (!(_cairo_surface_is_quartz (abstract_surface) &&
|
|
|
|
|
|
_cairo_quartz_is_cgcontext_bitmap_context (((cairo_quartz_surface_t*)abstract_surface)->cgContext))) {
|
|
|
|
|
|
return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-07 09:48:16 -08:00
|
|
|
|
surface = (cairo_quartz_surface_t*)abstract_surface;
|
|
|
|
|
|
image = CGBitmapContextCreateImage (surface->cgContext);
|
|
|
|
|
|
_cairo_quartz_image_to_png (image, dest);
|
2007-02-20 12:15:35 -08:00
|
|
|
|
|
2022-02-19 18:34:01 -08:00
|
|
|
|
CGImageRelease (image);
|
2023-02-07 09:48:16 -08:00
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
2007-02-20 12:15:35 -08:00
|
|
|
|
}
|