summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Vukicevic <vladimir@pobox.com>2009-06-14 20:43:05 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2009-06-16 11:03:46 +0100
commit22587f57bd5d1b4440d936cd4655a7e8fcebdf36 (patch)
treef7f4224e598d8a23b1a643016c880b7c91d5ff84
parent7d3881114add18d5934073d0b04755d343ea38c6 (diff)
Import Qt backend by Mozilla
Written by Vladimir Vukicevic to enable integration with Qt embedded devices, this backend allows cairo code to target QPainter, and use it as a source for other cairo backends. This imports the sources from mozilla-central: http://mxr.mozilla.org/mozilla-central/find?text=&kind=text&string=cairo-qpainter renames them from cairo-qpainter to cairo-qt, and integrates the patch by Oleg Romashin: https://bugs.freedesktop.org/attachment.cgi?id=18953 And then attempts to restore 'make check' to full functionality. However: - C++ does not play well with the PLT symbol hiding, and leaks into the global namespace. 'make check' fails at check-plt.sh - Qt embeds a GUI into QApplication which it requires to construct any QPainter drawable, i.e. used by the boilerplate to create a cairo-qt surface, and this leaks fonts (cairo-ft-fonts no less) causing assertion failures that all cairo objects are accounted for upon destruction. [Updated by Chris Wilson] Acked-by: Jeff Muizelaar <jeff@infidigm.net> Acked-by: Carl Worth <cworth@cworth.org>
-rw-r--r--boilerplate/Makefile.sources3
-rw-r--r--boilerplate/Makefile.win32.features10
-rw-r--r--boilerplate/cairo-boilerplate-qt-private.h60
-rw-r--r--boilerplate/cairo-boilerplate-qt.cpp90
-rw-r--r--boilerplate/cairo-boilerplate.c23
-rw-r--r--boilerplate/cairo-boilerplate.h3
-rw-r--r--build/Makefile.win32.features1
-rw-r--r--build/Makefile.win32.features-h3
-rw-r--r--build/configure.ac.features1
-rw-r--r--configure.ac11
-rw-r--r--perf/cairo-perf-trace.c1
-rw-r--r--perf/cairo-perf.c3
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.sources3
-rw-r--r--src/Makefile.win32.features14
-rw-r--r--src/cairo-debug.c42
-rw-r--r--src/cairo-qt-surface.cpp1876
-rw-r--r--src/cairo-qt.h89
-rw-r--r--src/cairo-xlib-surface.c6
-rw-r--r--src/cairo.h4
-rwxr-xr-xsrc/check-def.sh7
-rwxr-xr-xsrc/check-preprocessor-syntax.sh3
22 files changed, 2247 insertions, 8 deletions
diff --git a/boilerplate/Makefile.sources b/boilerplate/Makefile.sources
index 1b82bbda..1cccedad 100644
--- a/boilerplate/Makefile.sources
+++ b/boilerplate/Makefile.sources
@@ -37,6 +37,9 @@ cairo_boilerplate_pdf_sources = cairo-boilerplate-pdf.c
cairo_boilerplate_ps_private = cairo-boilerplate-ps-private.h
cairo_boilerplate_ps_sources = cairo-boilerplate-ps.c
+cairo_boilerplate_qt_private = cairo-boilerplate-qt-private.h
+cairo_boilerplate_qt_sources = cairo-boilerplate-qt.cpp
+
cairo_boilerplate_quartz_private = cairo-boilerplate-quartz-private.h
cairo_boilerplate_quartz_sources = cairo-boilerplate-quartz.c
diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index d6d50650..e80bf034 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -49,6 +49,16 @@ enabled_cairo_boilerplate_private += $(cairo_boilerplate_xcb_private)
enabled_cairo_boilerplate_sources += $(cairo_boilerplate_xcb_sources)
endif
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_qt_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_qt_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_qt_private)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_qt_sources)
+ifeq ($(CAIRO_HAS_QT_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_qt_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_qt_private)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_qt_sources)
+endif
+
supported_cairo_boilerplate_headers += $(cairo_boilerplate_quartz_headers)
all_cairo_boilerplate_headers += $(cairo_boilerplate_quartz_headers)
all_cairo_boilerplate_private += $(cairo_boilerplate_quartz_private)
diff --git a/boilerplate/cairo-boilerplate-qt-private.h b/boilerplate/cairo-boilerplate-qt-private.h
new file mode 100644
index 00000000..4ac87642
--- /dev/null
+++ b/boilerplate/cairo-boilerplate-qt-private.h
@@ -0,0 +1,60 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 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.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#ifndef CAIRO_BOILERPLATE_QT_PRIVATE_H
+#define CAIRO_BOILERPLATE_QT_PRIVATE_H
+
+#include <cairo.h>
+
+CAIRO_BEGIN_DECLS
+
+extern cairo_surface_t *
+_cairo_boilerplate_qt_create_surface (const char *name,
+ cairo_content_t content,
+ int width,
+ int height,
+ int max_width,
+ int max_height,
+ cairo_boilerplate_mode_t mode,
+ int id,
+ void **closure);
+
+extern void
+_cairo_boilerplate_qt_cleanup (void* closure);
+
+extern void
+_cairo_boilerplate_qt_synchronize (void *closure);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_BOILERPLATE_QT_PRIVATE_H */
+
diff --git a/boilerplate/cairo-boilerplate-qt.cpp b/boilerplate/cairo-boilerplate-qt.cpp
new file mode 100644
index 00000000..0e0aa7db
--- /dev/null
+++ b/boilerplate/cairo-boilerplate-qt.cpp
@@ -0,0 +1,90 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 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.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairo-boilerplate.h"
+#include "cairo-boilerplate-qt-private.h"
+
+#include <cairo-qt.h>
+
+#include <qapplication.h>
+#include <X11/Xlib.h>
+
+typedef struct _qt_closure {
+ Display *dpy;
+ QApplication *app;
+} qt_closure_t;
+
+void
+_cairo_boilerplate_qt_cleanup (void *closure)
+{
+ qt_closure_t *qtc = (qt_closure_t *) closure;
+
+ delete qtc->app;
+ XCloseDisplay (qtc->dpy);
+ free (qtc);
+}
+
+cairo_surface_t *
+_cairo_boilerplate_qt_create_surface (const char *name,
+ cairo_content_t content,
+ int width,
+ int height,
+ int max_width,
+ int max_height,
+ cairo_boilerplate_mode_t mode,
+ int id,
+ void **closure)
+{
+ qt_closure_t *qtc;
+
+ qtc = (qt_closure_t *) xcalloc (1, sizeof (qt_closure_t));
+ qtc->dpy = XOpenDisplay (NULL);
+ if (qtc->dpy == NULL) {
+ free (qtc);
+ return NULL;
+ }
+
+ if (mode == CAIRO_BOILERPLATE_MODE_TEST)
+ XSynchronize (qtc->dpy, True);
+
+ qtc->app = new QApplication (qtc->dpy);
+ *closure = qtc;
+ return cairo_qt_surface_create_with_qpixmap (content, width, height);
+}
+
+void
+_cairo_boilerplate_qt_synchronize (void *closure)
+{
+ qt_closure_t *qtc = (qt_closure_t *) closure;
+
+ qtc->app->flush (); /* not sure if this is sufficient */
+}
diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
index 6da28228..11c0833c 100644
--- a/boilerplate/cairo-boilerplate.c
+++ b/boilerplate/cairo-boilerplate.c
@@ -44,6 +44,9 @@
#if CAIRO_HAS_PS_SURFACE
#include "cairo-boilerplate-ps-private.h"
#endif
+#if CAIRO_HAS_QT_SURFACE
+#include "cairo-boilerplate-qt-private.h"
+#endif
#if CAIRO_HAS_QUARTZ_SURFACE
#include "cairo-boilerplate-quartz-private.h"
#endif
@@ -446,6 +449,26 @@ static const cairo_boilerplate_target_t targets[] =
},
#endif
#endif /* CAIRO_HAS_GLITZ_SURFACE */
+#if CAIRO_HAS_QT_SURFACE
+ {
+ "qt", "qt", NULL,
+ CAIRO_SURFACE_TYPE_QT, CAIRO_CONTENT_COLOR_ALPHA, 0,
+ _cairo_boilerplate_qt_create_surface, NULL,
+ NULL,
+ _cairo_boilerplate_get_image_surface,
+ cairo_surface_write_to_png,
+ _cairo_boilerplate_qt_cleanup
+ },
+ {
+ "qt", "qt", NULL,
+ CAIRO_SURFACE_TYPE_QT, CAIRO_CONTENT_COLOR, 0,
+ _cairo_boilerplate_qt_create_surface, NULL,
+ NULL,
+ _cairo_boilerplate_get_image_surface,
+ cairo_surface_write_to_png,
+ _cairo_boilerplate_qt_cleanup
+ },
+#endif
#if CAIRO_HAS_QUARTZ_SURFACE
{
"quartz", "quartz", NULL,
diff --git a/boilerplate/cairo-boilerplate.h b/boilerplate/cairo-boilerplate.h
index a2763bbf..aa96ab7b 100644
--- a/boilerplate/cairo-boilerplate.h
+++ b/boilerplate/cairo-boilerplate.h
@@ -93,6 +93,7 @@
#define M_PI 3.14159265358979323846
#endif
+CAIRO_BEGIN_DECLS
/* A fake format we use for the flattened ARGB output of the PS and
* PDF surfaces. */
@@ -210,4 +211,6 @@ cairo_boilerplate_version_string (void);
#include "cairo-boilerplate-system.h"
+CAIRO_END_DECLS
+
#endif
diff --git a/build/Makefile.win32.features b/build/Makefile.win32.features
index 764639ce..3029c50f 100644
--- a/build/Makefile.win32.features
+++ b/build/Makefile.win32.features
@@ -3,6 +3,7 @@
CAIRO_HAS_XLIB_SURFACE=0
CAIRO_HAS_XLIB_XRENDER_SURFACE=0
CAIRO_HAS_XCB_SURFACE=0
+CAIRO_HAS_QT_SURFACE=0
CAIRO_HAS_QUARTZ_SURFACE=0
CAIRO_HAS_QUARTZ_FONT=0
CAIRO_HAS_QUARTZ_IMAGE_SURFACE=0
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index dcefdbc0..3cfc5424 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -14,6 +14,9 @@ endif
ifeq ($(CAIRO_HAS_XCB_SURFACE),1)
@echo "#define CAIRO_HAS_XCB_SURFACE 1" >> src/cairo-features.h
endif
+ifeq ($(CAIRO_HAS_QT_SURFACE),1)
+ @echo "#define CAIRO_HAS_QT_SURFACE 1" >> src/cairo-features.h
+endif
ifeq ($(CAIRO_HAS_QUARTZ_SURFACE),1)
@echo "#define CAIRO_HAS_QUARTZ_SURFACE 1" >> src/cairo-features.h
endif
diff --git a/build/configure.ac.features b/build/configure.ac.features
index 1901a18d..d3d321fd 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -363,6 +363,7 @@ AC_DEFUN([CAIRO_REPORT],
echo " Image: yes (always builtin)"
echo " Xlib: $use_xlib"
echo " Xlib Xrender: $use_xlib_xrender"
+ echo " Qt: $use_qt"
echo " Quartz: $use_quartz"
echo " Quartz-image: $use_quartz_image"
echo " XCB: $use_xcb"
diff --git a/configure.ac b/configure.ac
index 7c406e7a..90843576 100644
--- a/configure.ac
+++ b/configure.ac
@@ -82,6 +82,17 @@ CAIRO_ENABLE_SURFACE_BACKEND(xcb, XCB, no, [
dnl ===========================================================================
+CAIRO_ENABLE_SURFACE_BACKEND(qt, Qt, no, [
+ qt_REQUIRES="QtGui >= 4.4.0"
+ PKG_CHECK_MODULES(qt, $qt_REQUIRES, ,
+ [AC_MSG_RESULT(no)
+ qt_REQUIRES=""
+ use_qt="no (requires Qt4 development libraries)"
+ ])
+])
+
+dnl ===========================================================================
+
CAIRO_ENABLE_SURFACE_BACKEND(quartz, Quartz, auto, [
dnl There is no pkgconfig for quartz; lets do a header check
AC_CHECK_HEADER(ApplicationServices/ApplicationServices.h, , [use_quartz="no (requires ApplicationServices framework)"])
diff --git a/perf/cairo-perf-trace.c b/perf/cairo-perf-trace.c
index 9edd4f23..3ed2bb87 100644
--- a/perf/cairo-perf-trace.c
+++ b/perf/cairo-perf-trace.c
@@ -99,6 +99,7 @@ target_is_measurable (const cairo_boilerplate_target_t *target)
case CAIRO_SURFACE_TYPE_BEOS:
case CAIRO_SURFACE_TYPE_DIRECTFB:
case CAIRO_SURFACE_TYPE_OS2:
+ case CAIRO_SURFACE_TYPE_QT:
case CAIRO_INTERNAL_SURFACE_TYPE_NULL:
return TRUE;
case CAIRO_SURFACE_TYPE_PDF:
diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c
index be436813..4965e577 100644
--- a/perf/cairo-perf.c
+++ b/perf/cairo-perf.c
@@ -92,9 +92,8 @@ target_is_measurable (const cairo_boilerplate_target_t *target)
case CAIRO_SURFACE_TYPE_WIN32:
case CAIRO_SURFACE_TYPE_BEOS:
case CAIRO_SURFACE_TYPE_DIRECTFB:
-#if CAIRO_VERSION_MAJOR > 1 || (CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR > 2)
case CAIRO_SURFACE_TYPE_OS2:
-#endif
+ case CAIRO_SURFACE_TYPE_QT:
return TRUE;
case CAIRO_SURFACE_TYPE_PDF:
case CAIRO_SURFACE_TYPE_PS:
diff --git a/src/Makefile.am b/src/Makefile.am
index b017c17a..f34d79c4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -50,7 +50,7 @@ cairo.def: cairo-features.h $(enabled_cairo_headers)
@(echo EXPORTS; \
(cd $(srcdir); cat $(enabled_cairo_headers) || echo 'cairo_ERROR ()' ) | \
grep -v -E '^# *include' | \
- ( cat cairo-features.h - | $(CPP) - || echo 'cairo_ERROR ()' ) | \
+ ( cat cairo-features.h - | $(CPP) -D__cplusplus - || echo 'cairo_ERROR ()' ) | \
grep -E '^cairo_.* \(' | \
sed -e 's/[ ].*//' | \
sort; \
diff --git a/src/Makefile.sources b/src/Makefile.sources
index ec58b2d8..6da3937f 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -223,6 +223,9 @@ cairo_xlib_xrender_headers = cairo-xlib-xrender.h
cairo_xcb_headers = cairo-xcb.h cairo-xcb-xrender.h
cairo_xcb_sources = cairo-xcb-surface.c
+cairo_qt_headers = cairo-qt.h
+cairo_qt_sources = cairo-qt-surface.cpp
+
cairo_quartz_headers = cairo-quartz.h
cairo_quartz_private = cairo-quartz-private.h
cairo_quartz_sources = cairo-quartz-surface.c
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index bed181fd..ac8d570e 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -63,6 +63,20 @@ ifeq ($(CAIRO_HAS_XCB_SURFACE),1)
enabled_cairo_pkgconf += cairo-xcb.pc
endif
+unsupported_cairo_headers += $(cairo_qt_headers)
+all_cairo_headers += $(cairo_qt_headers)
+all_cairo_private += $(cairo_qt_private)
+all_cairo_sources += $(cairo_qt_sources)
+ifeq ($(CAIRO_HAS_QT_SURFACE),1)
+enabled_cairo_headers += $(cairo_qt_headers)
+enabled_cairo_private += $(cairo_qt_private)
+enabled_cairo_sources += $(cairo_qt_sources)
+endif
+all_cairo_pkgconf += cairo-qt.pc
+ifeq ($(CAIRO_HAS_QT_SURFACE),1)
+enabled_cairo_pkgconf += cairo-qt.pc
+endif
+
supported_cairo_headers += $(cairo_quartz_headers)
all_cairo_headers += $(cairo_quartz_headers)
all_cairo_private += $(cairo_quartz_private)
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index 8d310a51..d4548f15 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -117,3 +117,45 @@ _cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface)
}
}
#endif
+
+
+#if 0
+void
+_cairo_image_surface_write_to_ppm (cairo_image_surface_t *isurf, const char *fn)
+{
+ char *fmt;
+ if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24)
+ fmt = "P6";
+ else if (isurf->format == CAIRO_FORMAT_A8)
+ fmt = "P5";
+ else
+ return;
+
+ FILE *fp = fopen(fn, "wb");
+ if (!fp)
+ return;
+
+ fprintf (fp, "%s %d %d 255\n", fmt,isurf->width, isurf->height);
+ for (int j = 0; j < isurf->height; j++) {
+ unsigned char *row = isurf->data + isurf->stride * j;
+ for (int i = 0; i < isurf->width; i++) {
+ if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24) {
+ unsigned char r = *row++;
+ unsigned char g = *row++;
+ unsigned char b = *row++;
+ *row++;
+ putc(r, fp);
+ putc(g, fp);
+ putc(b, fp);
+ } else {
+ unsigned char a = *row++;
+ putc(a, fp);
+ }
+ }
+ }
+
+ fclose (fp);
+
+ fprintf (stderr, "Wrote %s\n", fn);
+}
+#endif
diff --git a/src/cairo-qt-surface.cpp b/src/cairo-qt-surface.cpp
new file mode 100644
index 00000000..5542abd6
--- /dev/null
+++ b/src/cairo-qt-surface.cpp
@@ -0,0 +1,1876 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Mozilla Corporation
+ *
+ * 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 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.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ * Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+/* Get INT16_MIN etc. as per C99 */
+#define __STDC_LIMIT_MACROS
+
+#include "cairoint.h"
+#include "cairo-types-private.h"
+
+#include "cairo-qt.h"
+
+#include <memory>
+
+#include <QtGui/QPainter>
+#include <QtGui/QPaintEngine>
+#include <QtGui/QPaintDevice>
+#include <QtGui/QImage>
+#include <QtGui/QPixmap>
+#include <QtGui/QBrush>
+#include <QtGui/QPen>
+#include <QtGui/QWidget>
+#include <QtGui/QX11Info>
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+#include "cairo-xlib.h"
+#include "cairo-xlib-xrender.h"
+// I hate X
+#undef Status
+#undef CursorShape
+#undef Bool
+#endif
+
+#include <sys/time.h>
+
+#if 0
+#define D(x) x
+#else
+#define D(x) do { } while(0)
+#endif
+
+#ifndef CAIRO_INT_STATUS_SUCCESS
+#define CAIRO_INT_STATUS_SUCCESS ((cairo_int_status_t) CAIRO_STATUS_SUCCESS)
+#endif
+
+/* Qt::PenStyle optimization based on the assumption that dots are 1*w and dashes are 3*w. */
+#define DOT_LENGTH 1.0
+#define DASH_LENGTH 3.0
+
+typedef struct {
+ cairo_surface_t base;
+
+ QPainter *p;
+
+ /* The pixmap/image constructors will store their objects here */
+ QPixmap *pixmap;
+ QImage *image;
+
+ QRect window;
+
+ bool has_clipping;
+ QRect clip_bounds;
+
+ // if this is true, calls to intersect_clip_path won't
+ // update the clip_bounds rect
+ bool no_update_clip_bounds;
+
+ cairo_surface_t *image_equiv;
+
+#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+ /* temporary, so that we can share the xlib surface's glyphs code */
+ cairo_surface_t *xlib_equiv;
+ bool xlib_has_clipping;
+ QRect xlib_clip_bounds;
+ int xlib_clip_serial;
+ QPoint redir_offset;
+#endif
+
+ cairo_bool_t supports_porter_duff;
+} cairo_qt_surface_t;
+
+/* Will be true if we ever try to create a QPixmap and end
+ * up with one without an alpha channel.
+ */
+static cairo_bool_t _qpixmaps_have_no_alpha = FALSE;
+
+#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+slim_hidden_proto (cairo_xlib_surface_create);
+slim_hidden_proto (cairo_xlib_surface_create_with_xrender_format);
+#endif
+
+/**
+ ** Helper methods
+ **/
+static const char *
+_opstr (cairo_operator_t op)
+{
+ const char *ops[] = {
+ "CLEAR",
+ "SOURCE",
+ "OVER",
+ "IN",
+ "OUT",
+ "ATOP",
+ "DEST",
+ "DEST_OVER",
+ "DEST_IN",
+ "DEST_OUT",
+ "DEST_ATOP",
+ "XOR",
+ "ADD",
+ "SATURATE"
+ };
+
+ if (op < CAIRO_OPERATOR_CLEAR || op > CAIRO_OPERATOR_SATURATE)
+ return "(\?\?\?)";
+
+ return ops[op];
+}
+
+static QPainter::CompositionMode
+_qpainter_compositionmode_from_cairo_op (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ return QPainter::CompositionMode_Clear;
+
+ case CAIRO_OPERATOR_SOURCE:
+ return QPainter::CompositionMode_Source;
+ case CAIRO_OPERATOR_OVER:
+ return QPainter::CompositionMode_SourceOver;
+ case CAIRO_OPERATOR_IN:
+ return QPainter::CompositionMode_SourceIn;
+ case CAIRO_OPERATOR_OUT:
+ return QPainter::CompositionMode_SourceOut;
+ case CAIRO_OPERATOR_ATOP:
+ return QPainter::CompositionMode_SourceAtop;
+
+ case CAIRO_OPERATOR_DEST:
+ return QPainter::CompositionMode_Destination;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return QPainter::CompositionMode_DestinationOver;
+ case CAIRO_OPERATOR_DEST_IN:
+ return QPainter::CompositionMode_DestinationIn;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return QPainter::CompositionMode_DestinationOut;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return QPainter::CompositionMode_DestinationAtop;
+
+ case CAIRO_OPERATOR_XOR:
+ return QPainter::CompositionMode_Xor;
+
+ default:
+ case CAIRO_OPERATOR_ADD:
+ case CAIRO_OPERATOR_SATURATE:
+ ASSERT_NOT_REACHED;
+ }
+}
+
+static bool
+_op_is_supported (cairo_qt_surface_t *qs, cairo_operator_t op)
+{
+ if (qs->supports_porter_duff) {
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_ATOP:
+
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_DEST_ATOP:
+
+ case CAIRO_OPERATOR_XOR:
+ return TRUE;
+
+ case CAIRO_OPERATOR_ADD:
+ case CAIRO_OPERATOR_SATURATE:
+ return FALSE;
+
+ default:
+ ASSERT_NOT_REACHED;
+ }
+ } else {
+ return op == CAIRO_OPERATOR_OVER;
+ }
+}
+
+static cairo_format_t
+_cairo_format_from_qimage_format (QImage::Format fmt)
+{
+ switch (fmt) {
+ case QImage::Format_ARGB32_Premultiplied:
+ return CAIRO_FORMAT_ARGB32;
+ case QImage::Format_RGB32:
+ return CAIRO_FORMAT_RGB24;
+ case QImage::Format_Indexed8: // XXX not quite
+ return CAIRO_FORMAT_A8;
+#ifdef WORDS_BIGENDIAN
+ case QImage::Format_Mono:
+#else
+ case QImage::Format_MonoLSB:
+#endif
+ return CAIRO_FORMAT_A1;
+
+ case QImage::Format_Invalid:
+#ifdef WORDS_BIGENDIAN
+ case QImage::Format_MonoLSB:
+#else
+ case QImage::Format_Mono:
+#endif
+ case QImage::Format_ARGB32:
+ case QImage::Format_RGB16:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_RGB666:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_RGB555:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_RGB888:
+ case QImage::Format_RGB444:
+ case QImage::Format_ARGB4444_Premultiplied:
+ case QImage::NImageFormats:
+ default:
+ ASSERT_NOT_REACHED;
+ return (cairo_format_t) -1;
+ }
+}
+
+static QImage::Format
+_qimage_format_from_cairo_format (cairo_format_t fmt)
+{
+ switch (fmt) {
+ case CAIRO_FORMAT_ARGB32:
+ return QImage::Format_ARGB32_Premultiplied;
+ case CAIRO_FORMAT_RGB24:
+ return QImage::Format_RGB32;
+ case CAIRO_FORMAT_A8:
+ return QImage::Format_Indexed8; // XXX not quite
+ case CAIRO_FORMAT_A1:
+#ifdef WORDS_BIGENDIAN
+ return QImage::Format_Mono; // XXX think we need to choose between this and LSB
+#else
+ return QImage::Format_MonoLSB;
+#endif
+ }
+
+ return QImage::Format_Mono;
+}
+
+static inline QMatrix
+_qmatrix_from_cairo_matrix (const cairo_matrix_t& m)
+{
+ return QMatrix(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0);
+}
+
+/** Path conversion **/
+typedef struct _qpainter_path_transform {
+ QPainterPath *path;
+ cairo_matrix_t *ctm_inverse;
+} qpainter_path_data;
+
+/* cairo path -> execute in context */
+static cairo_status_t
+_cairo_path_to_qpainterpath_move_to (void *closure, const cairo_point_t *point)
+{
+ qpainter_path_data *pdata = (qpainter_path_data *)closure;
+ double x = _cairo_fixed_to_double (point->x);
+ double y = _cairo_fixed_to_double (point->y);
+
+ if (pdata->ctm_inverse)
+ cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y);
+
+ pdata->path->moveTo(x, y);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_qpainterpath_line_to (void *closure, const cairo_point_t *point)
+{
+ qpainter_path_data *pdata = (qpainter_path_data *)closure;
+ double x = _cairo_fixed_to_double (point->x);
+ double y = _cairo_fixed_to_double (point->y);
+
+ if (pdata->ctm_inverse)
+ cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y);
+
+ pdata->path->lineTo(x, y);
+
+ if (pdata->path->isEmpty())
+ pdata->path->moveTo(x, y);
+ else
+ pdata->path->lineTo(x, y);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_qpainterpath_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2)
+{
+ qpainter_path_data *pdata = (qpainter_path_data *)closure;
+ 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);
+
+ if (pdata->ctm_inverse) {
+ cairo_matrix_transform_point (pdata->ctm_inverse, &x0, &y0);
+ cairo_matrix_transform_point (pdata->ctm_inverse, &x1, &y1);
+ cairo_matrix_transform_point (pdata->ctm_inverse, &x2, &y2);
+ }
+
+ pdata->path->cubicTo (x0, y0, x1, y1, x2, y2);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_to_qpainterpath_close_path (void *closure)
+{
+ qpainter_path_data *pdata = (qpainter_path_data *)closure;
+
+ pdata->path->closeSubpath();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_quartz_cairo_path_to_qpainterpath (cairo_path_fixed_t *path,
+ QPainterPath *qpath,
+ cairo_fill_rule_t fill_rule,
+ cairo_matrix_t *ctm_inverse = NULL)
+{
+ qpainter_path_data pdata = { qpath, ctm_inverse };
+
+ qpath->setFillRule (fill_rule == CAIRO_FILL_RULE_WINDING ?
+ Qt::WindingFill :
+ Qt::OddEvenFill);
+
+ return _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_path_to_qpainterpath_move_to,
+ _cairo_path_to_qpainterpath_line_to,
+ _cairo_path_to_qpainterpath_curve_to,
+ _cairo_path_to_qpainterpath_close_path,
+ &pdata);
+}
+
+static cairo_status_t
+_cairo_quartz_cairo_path_to_qpainterpath (cairo_path_fixed_t *path,
+ QPainterPath *qpath,
+ cairo_matrix_t *ctm_inverse)
+{
+ return _cairo_quartz_cairo_path_to_qpainterpath (path, qpath,
+ CAIRO_FILL_RULE_WINDING,
+ ctm_inverse);
+}
+
+/**
+ ** Surface backend methods
+ **/
+static cairo_surface_t *
+_cairo_qt_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+ bool use_pixmap;
+
+ D(fprintf(stderr, "q[%p] create_similar: %d %d [%d] -> ", abstract_surface, width, height, content));
+
+ use_pixmap = qs->image == NULL;
+ if (use_pixmap) {
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA:
+ use_pixmap = FALSE;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ break;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ use_pixmap = ! _qpixmaps_have_no_alpha;
+ break;
+ }
+ }
+
+ if (use_pixmap) {
+ cairo_surface_t *result =
+ cairo_qt_surface_create_with_qpixmap (content, width, height);
+
+ /* XXX result->content is always content. ??? */
+ if (result->content == content) {
+ D(fprintf(stderr, "qpixmap content: %d\n", content));
+ return result;
+ }
+
+ _qpixmaps_have_no_alpha = TRUE;
+ cairo_surface_destroy (result);
+ }
+
+ D(fprintf (stderr, "qimage\n"));
+ return cairo_qt_surface_create_with_qimage
+ (_cairo_format_from_content (content), width, height);
+}
+
+static cairo_status_t
+_cairo_qt_surface_finish (void *abstract_surface)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] finish\n", abstract_surface));
+
+ /* Only delete p if we created it */
+ if (qs->image || qs->pixmap)
+ delete qs->p;
+ else
+ qs->p->restore();
+
+ if (qs->image_equiv)
+ cairo_surface_destroy (qs->image_equiv);
+
+#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+ if (qs->xlib_equiv)
+ cairo_surface_destroy (qs->xlib_equiv);
+#endif
+
+ if (qs->image)
+ delete qs->image;
+
+ if (qs->pixmap)
+ delete qs->pixmap;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_qimg_destroy (void *closure)
+{
+ QImage *qimg = (QImage *) closure;
+ delete qimg;
+}
+
+static cairo_status_t
+_cairo_qt_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] acquire_source_image\n", abstract_surface));
+
+ *image_extra = NULL;
+
+ if (qs->image_equiv) {
+ *image_out = (cairo_image_surface_t*)
+ cairo_surface_reference (qs->image_equiv);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (qs->pixmap) {
+ QImage *qimg = new QImage(qs->pixmap->toImage());
+ cairo_surface_t *image;
+ cairo_status_t status;
+
+ image = cairo_image_surface_create_for_data (qimg->bits(),
+ _cairo_format_from_qimage_format (qimg->format()),
+ qimg->width(), qimg->height(),
+ qimg->bytesPerLine());
+
+ status = _cairo_user_data_array_set_data (&image->user_data,
+ (const cairo_user_data_key_t *)&_qimg_destroy,
+ qimg,
+ _qimg_destroy);
+ if (status) {
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ *image_out = (cairo_image_surface_t *) image;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+}
+
+static void
+_cairo_qt_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ //cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] release_source_image\n", abstract_surface));
+
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_qt_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+ QImage *qimg = NULL;
+
+ D(fprintf(stderr, "q[%p] acquire_dest_image\n", abstract_surface));
+
+ *image_extra = NULL;
+
+ if (qs->image_equiv) {
+ *image_out = (cairo_image_surface_t*)
+ cairo_surface_reference (qs->image_equiv);
+
+ image_rect->x = qs->window.x();
+ image_rect->y = qs->window.y();
+ image_rect->width = qs->window.width();
+ image_rect->height = qs->window.height();
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ QPoint offset;
+
+ if (qs->pixmap) {
+ qimg = new QImage(qs->pixmap->toImage());
+ } else {
+ // Try to figure out what kind of QPaintDevice we have, and
+ // how we can grab an image from it
+ QPaintDevice *pd = qs->p->device();
+ if (!pd)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ QPaintDevice *rpd = QPainter::redirected(pd, &offset);
+ if (rpd)
+ pd = rpd;
+
+ if (pd->devType() == QInternal::Image) {
+ qimg = new QImage(((QImage*) pd)->copy());
+ } else if (pd->devType() == QInternal::Pixmap) {
+ qimg = new QImage(((QPixmap*) pd)->toImage());
+ } else if (pd->devType() == QInternal::Widget) {
+ qimg = new QImage(QPixmap::grabWindow(((QWidget*)pd)->winId()).toImage());
+ }
+ }
+
+ if (qimg == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ *image_out = (cairo_image_surface_t*)
+ cairo_image_surface_create_for_data (qimg->bits(),
+ _cairo_format_from_qimage_format (qimg->format()),
+ qimg->width(), qimg->height(),
+ qimg->bytesPerLine());
+ *image_extra = qimg;
+
+ image_rect->x = qs->window.x() + offset.x();
+ image_rect->y = qs->window.y() + offset.y();
+ image_rect->width = qs->window.width() - offset.x();
+ image_rect->height = qs->window.height() - offset.y();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_qt_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+ D(fprintf(stderr, "q[%p] release_dest_image\n", abstract_surface));
+
+ cairo_surface_destroy (&image->base);
+
+ if (image_extra) {
+ QImage *qimg = (QImage*) image_extra;
+
+ // XXX should I be using setBackgroundMode here instead of setCompositionMode?
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_Source);
+
+ qs->p->drawImage (image_rect->x, image_rect->y, *qimg);
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+ delete qimg;
+ }
+}
+
+static cairo_status_t
+_cairo_qt_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ cairo_content_t content,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ if (src->backend == qs->base.backend) {
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ *clone_out = cairo_surface_reference (src);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return (cairo_status_t) CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ extents->x = qs->window.x();
+ extents->y = qs->window.y();
+ extents->width = qs->window.width();
+ extents->height = qs->window.height();
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_intersect_clip_path (void *abstract_surface,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", abstract_surface, path ? "(path)" : "(clear)"));
+
+ if (!qs->p)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (path == NULL) {
+ //fprintf (stderr, "clip clear\n");
+ // How the clip path is reset depends on whether we own p or not
+ if (qs->pixmap || qs->image) {
+ // we own p
+ qs->p->setClipping (false);
+ } else {
+ qs->p->restore ();
+ qs->p->save ();
+ }
+
+ if (!qs->no_update_clip_bounds) {
+ qs->clip_bounds.setRect(0, 0, 0, 0);
+ qs->has_clipping = false;
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ // Qt will implicitly enable clipping, and will use ReplaceClip
+ // instead of IntersectClip if clipping was disabled before
+
+ // Note: Qt is really bad at dealing with clip paths. It doesn't
+ // seem to usefully recognize rectangular paths, instead going down
+ // extremely slow paths whenever a clip path is set. So,
+ // we do a bunch of work here to try to get rectangles or regions
+ // down to Qt for clipping.
+
+ QRect clip_bounds;
+
+ // First check if it's an integer-aligned single rectangle
+ cairo_box_t box;
+ if (_cairo_path_fixed_is_box (path, &box) &&
+ _cairo_fixed_is_integer (box.p1.x) &&
+ _cairo_fixed_is_integer (box.p1.y) &&
+ _cairo_fixed_is_integer (box.p2.x) &&
+ _cairo_fixed_is_integer (box.p2.y))
+ {
+ QRect r(_cairo_fixed_integer_part(box.p1.x),
+ _cairo_fixed_integer_part(box.p1.y),
+ _cairo_fixed_integer_part(box.p2.x - box.p1.x),
+ _cairo_fixed_integer_part(box.p2.y - box.p1.y));
+
+ r = r.normalized();
+
+ clip_bounds = r;
+
+ qs->p->setClipRect (r, Qt::IntersectClip);
+ } else {
+ // Then if it's not an integer-aligned rectangle, check
+ // if we can extract a region (a set of rectangles) out.
+ // We use cairo to convert the path to traps.
+
+ cairo_traps_t traps;
+ cairo_int_status_t status;
+ cairo_region_t *region = NULL;
+
+ _cairo_traps_init (&traps);
+ status = (cairo_int_status_t)
+ _cairo_path_fixed_fill_to_traps (path,
+ fill_rule, tolerance, &traps);
+ if (status) {
+ _cairo_traps_fini (&traps);
+ return status;
+ }
+
+ status = _cairo_traps_extract_region (&traps, &region);
+ _cairo_traps_fini (&traps);
+
+ if (_cairo_status_is_error ((cairo_status_t) status))
+ return status;
+
+ if (status == CAIRO_INT_STATUS_SUCCESS) {
+#if 0
+ cairo_box_int_t *boxes;
+ int n_boxes;
+
+ QRegion qr;
+
+ _cairo_region_get_boxes (&region, &n_boxes, &boxes);
+
+ for (int i = 0; i < n_boxes; i++) {
+ QRect r(boxes[i].p1.x,
+ boxes[i].p1.y,
+ boxes[i].p2.x - boxes[i].p1.x,
+ boxes[i].p2.y - boxes[i].p1.y);
+
+ if (i == 0)
+ clip_bounds = r;
+ else
+ clip_bounds = clip_bounds.united(r);
+
+ qr = qr.unite(r);
+ }
+ _cairo_region_boxes_fini (&region, boxes);
+#else
+ int n_boxes;
+
+ QRegion qr;
+
+ n_boxes = cairo_region_num_rectangles (region);
+ for (int i = 0; i < n_boxes; ++i) {
+ cairo_rectangle_int_t box;
+ cairo_region_get_rectangle (region, i, &box);
+ QRect r(box.x, box.y, box.width, box.height);
+
+ if (0 == i)
+ clip_bounds = r;
+ else
+ clip_bounds = clip_bounds.united(r);
+
+ qr = qr.unite(r);
+ }
+#endif
+ _cairo_region_fini (region);
+
+ qs->p->setClipRegion (qr, Qt::IntersectClip);
+ } else {
+ // We weren't able to extract a region from the traps.
+ // Just hand the path down to QPainter.
+ QPainterPath qpath;
+ cairo_status_t status;
+
+ status = _cairo_quartz_cairo_path_to_qpainterpath (path,
+ &qpath,
+ fill_rule);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ clip_bounds = qpath.boundingRect().toAlignedRect();
+
+ // XXX Antialiasing is ignored
+ qs->p->setClipPath (qpath, Qt::IntersectClip);
+ }
+ }
+
+ if (!qs->no_update_clip_bounds) {
+ clip_bounds = qs->p->worldTransform().mapRect(clip_bounds);
+
+ if (qs->has_clipping) {
+ qs->clip_bounds = qs->clip_bounds.intersect(clip_bounds);
+ } else {
+ qs->clip_bounds = clip_bounds;
+ qs->has_clipping = true;
+ }
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+/**
+ ** Brush conversion
+ **/
+
+struct PatternToBrushConverter {
+ PatternToBrushConverter (const cairo_pattern_t *pattern)
+ : mBrush(0),
+ mAcquiredImageParent(0)
+ {
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern;
+ QColor color;
+ color.setRgbF(solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+
+ mBrush = new QBrush(color);
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern;
+ cairo_surface_t *surface = spattern->surface;
+
+ if (surface->type == CAIRO_SURFACE_TYPE_QT) {
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
+
+ if (qs->image) {
+ mBrush = new QBrush(*qs->image);
+ } else if (qs->pixmap) {
+ mBrush = new QBrush(*qs->pixmap);
+ } else {
+ // do something smart
+ mBrush = new QBrush(0xff0000ff);
+ }
+ } else {
+ cairo_image_surface_t *isurf = NULL;
+
+ if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ isurf = (cairo_image_surface_t*) surface;
+ } else {
+ void *image_extra;
+
+ if (_cairo_surface_acquire_source_image (surface, &isurf, &image_extra) == CAIRO_STATUS_SUCCESS) {
+ mAcquiredImageParent = surface;
+ mAcquiredImage = isurf;
+ mAcquiredImageExtra = image_extra;
+ } else {
+ isurf = NULL;
+ }
+ }
+
+ if (isurf) {
+ mBrush = new QBrush (QImage ((const uchar *) isurf->data,
+ isurf->width,
+ isurf->height,
+ isurf->stride,
+ _qimage_format_from_cairo_format (isurf->format)));
+ } else {
+ mBrush = new QBrush(0x0000ffff);
+ }
+ }
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
+ pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+ {
+ QGradient *grad;
+ cairo_bool_t reverse_stops = FALSE;
+ cairo_bool_t emulate_reflect = FALSE;
+ double offset = 0.0;
+
+ cairo_extend_t extend = pattern->extend;
+
+ cairo_gradient_pattern_t *gpat = (cairo_gradient_pattern_t *) pattern;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *) pattern;
+ grad = new QLinearGradient (_cairo_fixed_to_double (lpat->p1.x),
+ _cairo_fixed_to_double (lpat->p1.y),
+ _cairo_fixed_to_double (lpat->p2.x),
+ _cairo_fixed_to_double (lpat->p2.y));
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *) pattern;
+
+ /* Based on the SVG surface code */
+
+ cairo_point_t *c0, *c1;
+ cairo_fixed_t radius0, radius1;
+
+ if (rpat->r1 < rpat->r2) {
+ c0 = &rpat->c1;
+ c1 = &rpat->c2;
+ radius0 = rpat->r1;
+ radius1 = rpat->r2;
+ reverse_stops = FALSE;
+ } else {
+ c0 = &rpat->c2;
+ c1 = &rpat->c1;
+ radius0 = rpat->r2;
+ radius1 = rpat->r1;
+ reverse_stops = TRUE;
+ }
+
+ double x0 = _cairo_fixed_to_double (c0->x);
+ double y0 = _cairo_fixed_to_double (c0->y);
+ double r0 = _cairo_fixed_to_double (radius0);
+ double x1 = _cairo_fixed_to_double (c1->x);
+ double y1 = _cairo_fixed_to_double (c1->y);
+ double r1 = _cairo_fixed_to_double (radius1);
+
+ if (rpat->r1 == rpat->r2) {
+ grad = new QRadialGradient (x1, y1, r1, x1, y1);
+ } else {
+ double fx = (r1 * x0 - r0 * x1) / (r1 - r0);
+ double fy = (r1 * y0 - r0 * y1) / (r1 - r0);
+
+ /* QPainter doesn't support the inner circle and use instead a gradient focal.
+ * That means we need to emulate the cairo behaviour by processing the
+ * cairo gradient stops.
+ * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
+ * it's just a matter of stop position translation and calculation of
+ * the corresponding SVG radial gradient focal.
+ * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
+ * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
+ * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
+ * list that maps to the original cairo stop list.
+ */
+ if ((extend == CAIRO_EXTEND_REFLECT || extend == CAIRO_EXTEND_REPEAT) && r0 > 0.0) {
+ double r_org = r1;
+ double r, x, y;
+
+ if (extend == CAIRO_EXTEND_REFLECT) {
+ r1 = 2 * r1 - r0;
+ emulate_reflect = TRUE;
+ }
+
+ offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
+ r = r1 - r0;
+
+ /* New position of outer circle. */
+ x = r * (x1 - fx) / r_org + fx;
+ y = r * (y1 - fy) / r_org + fy;
+
+ x1 = x;
+ y1 = y;
+ r1 = r;
+ r0 = 0.0;
+ } else {
+ offset = r0 / r1;
+ }
+
+ grad = new QRadialGradient (x1, y1, r1, fx, fy);
+
+ if (extend == CAIRO_EXTEND_NONE && r0 != 0.0)
+ grad->setColorAt (r0 / r1, Qt::transparent);
+ }
+ }
+
+ switch (extend) {
+ case CAIRO_EXTEND_NONE:
+ case CAIRO_EXTEND_PAD:
+ grad->setSpread(QGradient::PadSpread);
+
+ grad->setColorAt (0.0, Qt::transparent);
+ grad->setColorAt (1.0, Qt::transparent);
+ break;
+
+ case CAIRO_EXTEND_REFLECT:
+ grad->setSpread(QGradient::ReflectSpread);
+ break;
+
+ case CAIRO_EXTEND_REPEAT:
+ grad->setSpread(QGradient::RepeatSpread);
+ break;
+ }
+
+ for (unsigned int i = 0; i < gpat->n_stops; i++) {
+ int index = i;
+ if (reverse_stops)
+ index = gpat->n_stops - i - 1;
+
+ double offset = gpat->stops[i].offset;
+ QColor color;
+ color.setRgbF (gpat->stops[i].color.red,
+ gpat->stops[i].color.green,
+ gpat->stops[i].color.blue,
+ gpat->stops[i].color.alpha);
+
+ if (emulate_reflect) {
+ offset = offset / 2.0;
+ grad->setColorAt (1.0 - offset, color);
+ }
+
+ grad->setColorAt (offset, color);
+ }
+
+ mBrush = new QBrush(*grad);
+
+ delete grad;
+ }
+
+ if (mBrush &&
+ pattern->type != CAIRO_PATTERN_TYPE_SOLID &&
+ ! _cairo_matrix_is_identity (&pattern->matrix))
+ {
+ cairo_matrix_t pm = pattern->matrix;
+ cairo_status_t status = cairo_matrix_invert (&pm);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ mBrush->setMatrix (_qmatrix_from_cairo_matrix (pm));
+ }
+ }
+
+ ~PatternToBrushConverter () {
+ delete mBrush;
+
+ if (mAcquiredImageParent)
+ _cairo_surface_release_source_image (mAcquiredImageParent, mAcquiredImage, mAcquiredImageExtra);
+ }
+
+ operator QBrush& () {
+ return *mBrush;
+ }
+
+ QBrush *mBrush;
+
+ cairo_surface_t *mAcquiredImageParent;
+ cairo_image_surface_t *mAcquiredImage;
+ void *mAcquiredImageExtra;
+};
+
+struct PatternToPenConverter {
+ PatternToPenConverter (const cairo_pattern_t *source,
+ cairo_stroke_style_t *style)
+ : mBrushConverter(source)
+ {
+ Qt::PenJoinStyle join = Qt::MiterJoin;
+ Qt::PenCapStyle cap = Qt::SquareCap;
+
+ switch (style->line_cap) {
+ case CAIRO_LINE_CAP_BUTT:
+ cap = Qt::FlatCap;
+ break;
+ case CAIRO_LINE_CAP_ROUND:
+ cap = Qt::RoundCap;
+ break;
+ case CAIRO_LINE_CAP_SQUARE:
+ cap = Qt::SquareCap;
+ break;
+ }
+
+ switch (style->line_join) {
+ case CAIRO_LINE_JOIN_MITER:
+ join = Qt::MiterJoin;
+ break;
+ case CAIRO_LINE_JOIN_ROUND:
+ join = Qt::RoundJoin;
+ break;
+ case CAIRO_LINE_JOIN_BEVEL:
+ join = Qt::BevelJoin;
+ break;
+ }
+
+ mPen = new QPen (mBrushConverter, style->line_width, Qt::SolidLine, cap, join);
+ mPen->setMiterLimit (style->miter_limit);
+
+ if (style->dash && style->num_dashes) {
+ Qt::PenStyle pstyle = Qt::NoPen;
+
+ if (style->num_dashes == 2) {
+ if ((style->dash[0] == style->line_width &&
+ style->dash[1] == style->line_width && style->line_width <= 2.0) ||
+ (style->dash[0] == 0.0 &&
+ style->dash[1] == style->line_width * 2 && cap == Qt::RoundCap))
+ {
+ pstyle = Qt::DotLine;
+ } else if (style->dash[0] == style->line_width * DASH_LENGTH &&
+ style->dash[1] == style->line_width * DASH_LENGTH &&
+ cap == Qt::FlatCap)
+ {
+ pstyle = Qt::DashLine;
+ }
+ }
+
+ if (pstyle != Qt::NoPen) {
+ mPen->setStyle(pstyle);
+ return;
+ }
+
+ unsigned int odd_dash = style->num_dashes % 2;
+
+ QVector<qreal> dashes (odd_dash ? style->num_dashes * 2 : style->num_dashes);
+ for (unsigned int i = 0; i < odd_dash+1; i++) {
+ for (unsigned int j = 0; j < style->num_dashes; j++) {
+ // In Qt, the dash lengths are given in units of line width, whereas
+ // in cairo, they are in user-space units. We'll always apply the CTM,
+ // so all we have to do here is divide cairo's dash lengths by the line
+ // width.
+ dashes.append (style->dash[j] / style->line_width);
+ }
+ }
+
+ mPen->setDashPattern (dashes);
+ mPen->setDashOffset (style->dash_offset / style->line_width);
+ }
+ }
+
+ ~PatternToPenConverter() {
+ delete mPen;
+ }
+
+ operator QPen& () {
+ return *mPen;
+ }
+
+ QPen *mPen;
+ PatternToBrushConverter mBrushConverter;
+};
+
+/**
+ ** Core drawing operations
+ **/
+
+static bool
+_cairo_qt_fast_fill (cairo_qt_surface_t *qs,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path = NULL,
+ cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING,
+ double tolerance = 0.0,
+ cairo_antialias_t antialias = CAIRO_ANTIALIAS_NONE)
+{
+ QImage *qsSrc_image = NULL;
+ QPixmap *qsSrc_pixmap = NULL;
+ std::auto_ptr<QImage> qsSrc_image_d;
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) source;
+ if (spattern->surface->type == CAIRO_SURFACE_TYPE_QT) {
+ cairo_qt_surface_t *p = (cairo_qt_surface_t*) spattern->surface;
+
+ qsSrc_image = p->image;
+ qsSrc_pixmap = p->pixmap;
+ } else if (spattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ cairo_image_surface_t *p = (cairo_image_surface_t*) spattern->surface;
+ qsSrc_image = new QImage((const uchar*) p->data,
+ p->width,
+ p->height,
+ p->stride,
+ _qimage_format_from_cairo_format(p->format));
+ qsSrc_image_d.reset(qsSrc_image);
+ }
+ }
+
+ if (!qsSrc_image && !qsSrc_pixmap)
+ return false;
+
+ // We can only drawTiledPixmap; there's no drawTiledImage
+ if (! qsSrc_pixmap &&
+ (source->extend == CAIRO_EXTEND_REPEAT ||
+ source->extend == CAIRO_EXTEND_REFLECT))
+ {
+ return false;
+ }
+
+ QMatrix sourceMatrix = _qmatrix_from_cairo_matrix (source->matrix);
+ cairo_int_status_t status;
+
+ // We can draw this faster by clipping and calling drawImage/drawPixmap.
+ // Use our own clipping function so that we can get the
+ // region handling to end up with the fastest possible clip.
+ //
+ // XXX Antialiasing will fail pretty hard here, since we can't clip with AA
+ // with QPainter.
+ qs->p->save();
+
+ if (path) {
+ qs->no_update_clip_bounds = true;
+ status = _cairo_qt_surface_intersect_clip_path (qs, path, fill_rule, tolerance, antialias);
+ qs->no_update_clip_bounds = false;
+
+ if (status != CAIRO_INT_STATUS_SUCCESS) {
+ qs->p->restore();
+ return false;
+ }
+ }
+
+ qs->p->setWorldMatrix (sourceMatrix.inverted(), true);
+
+ switch (source->extend) {
+ case CAIRO_EXTEND_REPEAT:
+ // XXX handle reflect by tiling 4 times first
+ case CAIRO_EXTEND_REFLECT: {
+ assert (qsSrc_pixmap);
+
+ // Render the tiling to cover the entire destination window (because
+ // it'll be clipped). Transform the window rect by the inverse
+ // of the current world transform so that the device coordinates
+ // end up as the right thing.
+ QRectF dest = qs->p->worldTransform().inverted().mapRect(QRectF(qs->window));
+ QPointF origin = sourceMatrix.map(QPointF(0.0, 0.0));
+
+ qs->p->drawTiledPixmap (dest, *qsSrc_pixmap, origin);
+ }
+ break;
+ case CAIRO_EXTEND_NONE:
+ case CAIRO_EXTEND_PAD: // XXX not exactly right, but good enough
+ default:
+ if (qsSrc_image)
+ qs->p->drawImage (0, 0, *qsSrc_image);
+ else if (qsSrc_pixmap)
+ qs->p->drawPixmap (0, 0, *qsSrc_pixmap);
+ break;
+ }
+
+ qs->p->restore();
+
+ return true;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_rectangle_int_t *extents)
+{
+ Q_UNUSED(extents);
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] paint op:%s\n", abstract_surface, _opstr(op)));
+
+ if (!qs->p)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _op_is_supported (qs, op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+ if (! _cairo_qt_fast_fill (qs, source)) {
+ PatternToBrushConverter brush (source);
+ qs->p->fillRect (qs->window, brush);
+ }
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_rectangle_int_t * extents)
+{
+ Q_UNUSED(extents);
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op)));
+
+ if (!qs->p)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _op_is_supported (qs, op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+ // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is
+ // enabled
+ //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true);
+ qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST);
+
+ if (! _cairo_qt_fast_fill (qs, source,
+ path, fill_rule, tolerance, antialias))
+ {
+ QPainterPath qpath;
+ cairo_status_t status;
+
+ status = _cairo_quartz_cairo_path_to_qpainterpath (path, &qpath, fill_rule);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ PatternToBrushConverter brush(source);
+ qs->p->fillPath (qpath, brush);
+ }
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_stroke_style_t *style,
+ cairo_matrix_t *ctm,
+ cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_rectangle_int_t *extents)
+{
+ Q_UNUSED(extents);
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] stroke op:%s\n", abstract_surface, _opstr(op)));
+
+ if (!qs->p)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _op_is_supported (qs, op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ QPainterPath qpath;
+ cairo_status_t status;
+
+ if (_cairo_matrix_is_identity (ctm_inverse))
+ ctm_inverse = NULL;
+ status = _cairo_quartz_cairo_path_to_qpainterpath (path, &qpath,
+ ctm_inverse);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ QMatrix savedMatrix = qs->p->worldMatrix();
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+ qs->p->setWorldMatrix (_qmatrix_from_cairo_matrix (*ctm), true);
+ // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is
+ // enabled
+ //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true);
+ qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST);
+
+ PatternToPenConverter pen(source, style);
+
+ qs->p->setPen(pen);
+ qs->p->drawPath(qpath);
+ qs->p->setPen(Qt::black);
+
+ qs->p->setWorldMatrix (savedMatrix, false);
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_show_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ int *remaining_glyphs,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+ /* If we have an equivalent X surface, let the xlib surface handle this
+ * until we figure out how to do this natively with Qt.
+ */
+ if (qs->xlib_equiv) {
+ D(fprintf(stderr, "q[%p] show_glyphs (x11 equiv) op:%s nglyphs: %d\n", abstract_surface, _opstr(op), num_glyphs));
+
+ for (int i = 0; i < num_glyphs; i++) {
+ glyphs[i].x -= qs->redir_offset.x();
+ glyphs[i].y -= qs->redir_offset.y();
+ }
+
+ if (qs->has_clipping != qs->xlib_has_clipping ||
+ qs->clip_bounds != qs->xlib_clip_bounds)
+ {
+ cairo_status_t status;
+
+ status = _cairo_surface_reset_clip (qs->xlib_equiv);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ if (qs->has_clipping) {
+ cairo_region_t region;
+ cairo_rectangle_int_t rect = {
+ qs->clip_bounds.x() - qs->redir_offset.x(),
+ qs->clip_bounds.y() - qs->redir_offset.y(),
+ qs->clip_bounds.width(),
+ qs->clip_bounds.height()
+ };
+
+ _cairo_region_init_rectangle (&region, &rect);
+ status = _cairo_surface_set_clip_region (qs->xlib_equiv,
+ &region,
+ ++qs->xlib_clip_serial);
+ _cairo_region_fini (&region);
+
+ if (status)
+ return (cairo_int_status_t) status;
+ }
+
+ qs->xlib_has_clipping = qs->has_clipping;
+ qs->xlib_clip_bounds = qs->clip_bounds;
+ }
+
+ return (cairo_int_status_t)
+ _cairo_surface_show_text_glyphs (qs->xlib_equiv,
+ op, source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0,
+ (cairo_text_cluster_flags_t) 0,
+ scaled_font,
+ extents);
+ }
+#endif
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_rectangle_int_t *extents)
+{
+ Q_UNUSED(extents);
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] mask op:%s\n", abstract_surface, _opstr(op)));
+
+ if (!qs->p)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
+ cairo_int_status_t result;
+
+ qs->p->setOpacity (solid_mask->color.alpha);
+
+ result = _cairo_qt_surface_paint (abstract_surface, op, source, 0);
+
+ qs->p->setOpacity (1.0);
+
+ return result;
+ }
+
+ // otherwise skip for now
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+/**
+ ** XXX this will go away! it's only implemented for now so that we
+ ** can get some text without show_glyphs being available.
+ **/
+static cairo_int_status_t
+_cairo_qt_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const cairo_pattern_t *mask_pattern,
+ void *abstract_surface,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ if (mask_pattern)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _op_is_supported (qs, op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ D(fprintf(stderr, "q[%p] composite op:%s src:%p [%d %d] dst [%d %d] dim [%d %d]\n",
+ abstract_surface, _opstr(op), (void*)pattern,
+ src_x, src_y, dst_x, dst_y, width, height));
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern;
+
+ QColor color;
+ color.setRgbF(solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+ qs->p->fillRect (dst_x, dst_y, width, height, color);
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern;
+ cairo_surface_t *surface = spattern->surface;
+
+ QImage *qimg = NULL;
+ QPixmap *qpixmap = NULL;
+ std::auto_ptr<QImage> qimg_d;
+
+ if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ cairo_image_surface_t *isurf = (cairo_image_surface_t*) surface;
+ qimg = new QImage ((const uchar *) isurf->data,
+ isurf->width,
+ isurf->height,
+ isurf->stride,
+ _qimage_format_from_cairo_format (isurf->format));
+ qimg_d.reset(qimg);
+ }
+
+ if (surface->type == CAIRO_SURFACE_TYPE_QT) {
+ cairo_qt_surface_t *qsrc = (cairo_qt_surface_t*) surface;
+
+ if (qsrc->image)
+ qimg = qsrc->image;
+ else if (qsrc->pixmap)
+ qpixmap = qsrc->pixmap;
+ }
+
+ if (!qimg && !qpixmap)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ QMatrix savedMatrix = qs->p->worldMatrix();
+ if (! _cairo_matrix_is_identity (&pattern->matrix)) {
+ cairo_matrix_t pm = pattern->matrix;
+ cairo_status_t status;
+
+ status = cairo_matrix_invert (&pm);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ qs->p->setWorldMatrix(_qmatrix_from_cairo_matrix (pm), true);
+ }
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+ if (qimg)
+ qs->p->drawImage (dst_x, dst_y, *qimg, src_x, src_y, width, height);
+ else if (qpixmap)
+ qs->p->drawPixmap (dst_x, dst_y, *qpixmap, src_x, src_y, width, height);
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+ } else {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+/**
+ ** Backend struct
+ **/
+
+static const cairo_surface_backend_t cairo_qt_surface_backend = {
+ CAIRO_SURFACE_TYPE_QT,
+ _cairo_qt_surface_create_similar,
+ _cairo_qt_surface_finish,
+ _cairo_qt_surface_acquire_source_image,
+ _cairo_qt_surface_release_source_image,
+ _cairo_qt_surface_acquire_dest_image,
+ _cairo_qt_surface_release_dest_image,
+ _cairo_qt_surface_clone_similar,
+
+ _cairo_qt_surface_composite,
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ NULL, /* set_clip_region */
+ _cairo_qt_surface_intersect_clip_path,
+ _cairo_qt_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ _cairo_qt_surface_paint,
+ _cairo_qt_surface_mask,
+ _cairo_qt_surface_stroke,
+ _cairo_qt_surface_fill,
+ _cairo_qt_surface_show_glyphs,
+
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL, /* reset */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+ NULL, /* has_show_text_glyphs */
+ NULL, /* show_text_glyphs */
+};
+
+#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+static cairo_surface_t *
+_cairo_qt_create_xlib_surface (cairo_qt_surface_t *qs)
+{
+ if (!qs->p)
+ return NULL;
+
+ QPaintDevice *pd = qs->p->device();
+ if (!pd)
+ return NULL;
+
+ QPoint offs;
+ QPaintDevice *rpd = QPainter::redirected(pd, &offs);
+ if (rpd) {
+ pd = rpd;
+ qs->redir_offset = offs;
+ }
+
+ if (pd->devType() == QInternal::Widget) {
+ QWidget *w = (QWidget*) pd;
+ QX11Info xinfo = w->x11Info();
+
+ return cairo_xlib_surface_create (xinfo.display(),
+ (Drawable) w->handle (),
+ (Visual *) xinfo.visual (),
+ w->width (), w->height ());
+ } else if (pd->devType() == QInternal::Pixmap) {
+ QPixmap *pixmap = (QPixmap*) pd;
+ QX11Info xinfo = pixmap->x11Info ();
+ XRenderPictFormat *xrender_format;
+ int pict_format;
+
+ switch (pixmap->depth ()) {
+ case 1:
+ pict_format = PictStandardA1; break;
+ case 8:
+ pict_format = PictStandardA8; break;
+ case 24:
+ pict_format = PictStandardRGB24; break;
+ default:
+ ASSERT_NOT_REACHED;
+ case 32:
+ pict_format = PictStandardARGB32; break;
+ }
+ xrender_format = XRenderFindStandardFormat (xinfo.display (),
+ pict_format);
+
+ return cairo_xlib_surface_create_with_xrender_format (xinfo.display(),
+ (Drawable) pixmap->handle (),
+ ScreenOfDisplay (xinfo.display (),
+ xinfo.screen ()),
+ xrender_format,
+ pixmap->width (), pixmap->height ());
+ } else
+ return NULL;
+}
+#endif
+
+cairo_surface_t *
+cairo_qt_surface_create (QPainter *painter)
+{
+ cairo_qt_surface_t *qs;
+
+ qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t));
+ if (qs == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ memset (qs, 0, sizeof(cairo_qt_surface_t));
+
+ _cairo_surface_init (&qs->base,
+ &cairo_qt_surface_backend,
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ qs->p = painter;
+ if (qs->p->paintEngine())
+ qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
+ else
+ qs->supports_porter_duff = FALSE;
+
+ // Save so that we can always get back to the original state
+ qs->p->save();
+
+ qs->window = painter->window();
+
+#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+ qs->xlib_equiv = _cairo_qt_create_xlib_surface (qs);
+#endif
+
+ D(fprintf(stderr, "qpainter_surface_create: window: [%d %d %d %d] pd:%d\n",
+ qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
+ qs->supports_porter_duff));
+
+ return &qs->base;
+}
+
+cairo_surface_t *
+cairo_qt_surface_create_with_qimage (cairo_format_t format,
+ int width,
+ int height)
+{
+ cairo_qt_surface_t *qs;
+
+ qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t));
+ if (qs == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ memset (qs, 0, sizeof(cairo_qt_surface_t));
+
+ _cairo_surface_init (&qs->base,
+ &cairo_qt_surface_backend,
+ _cairo_content_from_format (format));
+
+ QImage *image = new QImage (width, height,
+ _qimage_format_from_cairo_format (format));
+
+ qs->image = image;
+
+ if (!image->isNull()) {
+ qs->p = new QPainter(image);
+ qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
+ }
+
+ qs->image_equiv = cairo_image_surface_create_for_data (image->bits(),
+ format,
+ width, height,
+ image->bytesPerLine());
+
+ qs->window = QRect(0, 0, width, height);
+
+ D(fprintf(stderr, "qpainter_surface_create: qimage: [%d %d %d %d] pd:%d\n",
+ qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
+ qs->supports_porter_duff));
+
+ return &qs->base;
+}
+
+cairo_surface_t *
+cairo_qt_surface_create_with_qpixmap (cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_qt_surface_t *qs;
+
+ if ((content & CAIRO_CONTENT_COLOR) == 0)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+
+ qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t));
+ if (qs == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ memset (qs, 0, sizeof(cairo_qt_surface_t));
+
+ QPixmap *pixmap = new QPixmap (width, height);
+ if (pixmap == NULL) {
+ free (qs);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ // By default, a QPixmap is opaque; however, if it's filled
+ // with a color with a transparency component, it is converted
+ // to a format that preserves transparency.
+ if (content == CAIRO_CONTENT_COLOR_ALPHA)
+ pixmap->fill(Qt::transparent);
+
+ _cairo_surface_init (&qs->base, &cairo_qt_surface_backend, content);
+
+ qs->pixmap = pixmap;
+
+ if (!pixmap->isNull()) {
+ qs->p = new QPainter(pixmap);
+ qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
+ }
+
+ qs->window = QRect(0, 0, width, height);
+
+#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+ qs->xlib_equiv = _cairo_qt_create_xlib_surface (qs);
+#endif
+
+ D(fprintf(stderr, "qpainter_surface_create: qpixmap: [%d %d %d %d] pd:%d\n",
+ qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
+ qs->supports_porter_duff));
+
+ return &qs->base;
+}
+
+QPainter *
+cairo_qt_surface_get_qpainter (cairo_surface_t *surface)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
+
+ if (surface->type != CAIRO_SURFACE_TYPE_QT)
+ return NULL;
+
+ return qs->p;
+}
+
+QImage *
+cairo_qt_surface_get_qimage (cairo_surface_t *surface)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
+
+ if (surface->type != CAIRO_SURFACE_TYPE_QT)
+ return NULL;
+
+ return qs->image;
+}
+
+cairo_surface_t *
+cairo_qt_surface_get_image (cairo_surface_t *surface)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
+
+ if (surface->type != CAIRO_SURFACE_TYPE_QT)
+ return NULL;
+
+ return qs->image_equiv;
+}
+
+/*
+ * TODO:
+ *
+ * - Figure out why QBrush isn't working with non-repeated images
+ *
+ * - Correct repeat mode; right now, every surface source is EXTEND_REPEAT
+ * - implement EXTEND_NONE (?? probably need to clip to the extents of the source)
+ * - implement EXTEND_REFLECT (create temporary and copy 4x, then EXTEND_REPEAT that)
+ *
+ * - stroke-image failure
+ *
+ * - Implement mask() with non-solid masks (probably will need to use a temporary and use IN)
+ *
+ * - Implement gradient sources
+ *
+ * - Make create_similar smarter -- create QPixmaps in more circumstances
+ * (e.g. if the pixmap can have alpha)
+ *
+ * - Implement show_glyphs() in terms of Qt
+ *
+ */
diff --git a/src/cairo-qt.h b/src/cairo-qt.h
new file mode 100644
index 00000000..d16087dd
--- /dev/null
+++ b/src/cairo-qt.h
@@ -0,0 +1,89 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Mozilla Corporation
+ *
+ * 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 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.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ * Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#ifndef CAIRO_QT_H
+#define CAIRO_QT_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_QT_SURFACE
+
+#if defined(__cplusplus)
+
+class QPainter;
+class QImage;
+
+cairo_public cairo_surface_t *
+cairo_qt_surface_create (QPainter *painter);
+
+cairo_public cairo_surface_t *
+cairo_qt_surface_create_with_qimage (cairo_format_t format,
+ int width,
+ int height);
+
+cairo_public cairo_surface_t *
+cairo_qt_surface_create_with_qpixmap (cairo_content_t content,
+ int width,
+ int height);
+
+cairo_public QPainter *
+cairo_qt_surface_get_qpainter (cairo_surface_t *surface);
+
+/* XXX needs hooking to generic surface layer, my vote is for
+cairo_public cairo_surface_t *
+cairo_surface_map_image (cairo_surface_t *surface);
+cairo_public void
+cairo_surface_unmap_image (cairo_surface_t *surface, cairo_surface_t *image);
+*/
+cairo_public cairo_surface_t *
+cairo_qt_surface_get_image (cairo_surface_t *surface);
+
+cairo_public QImage *
+cairo_qt_surface_get_qimage (cairo_surface_t *surface);
+
+#else /* ! __cplusplus */
+
+# warning cairo-qt only exports a C++ interface
+
+#endif /* __cplusplus */
+
+#else /* CAIRO_HAS_QT_SURFACE */
+
+# error Cairo was not compiled with support for the Qt backend
+
+#endif /* CAIRO_HAS_QT_SURFACE */
+
+#endif /* CAIRO_QT_H */
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 67b4ca6f..4fa5d2a7 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -89,6 +89,10 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst,
int *remaining_glyphs,
cairo_rectangle_int_t *extents);
+/* XXX temporarily used by cairo-qt-surface.c */
+slim_hidden_proto (cairo_xlib_surface_create);
+slim_hidden_proto (cairo_xlib_surface_create_with_xrender_format);
+
/*
* Instead of taking two round trips for each blending request,
* assume that if a particular drawable fails GetImage that it will
@@ -2816,6 +2820,7 @@ cairo_xlib_surface_create (Display *dpy,
return _cairo_xlib_surface_create_internal (dpy, drawable, screen,
visual, NULL, width, height, 0);
}
+slim_hidden_def (cairo_xlib_surface_create);
/**
* cairo_xlib_surface_create_for_bitmap:
@@ -2873,6 +2878,7 @@ cairo_xlib_surface_create_with_xrender_format (Display *dpy,
return _cairo_xlib_surface_create_internal (dpy, drawable, screen,
NULL, format, width, height, 0);
}
+slim_hidden_def (cairo_xlib_surface_create_with_xrender_format);
/**
* cairo_xlib_surface_get_xrender_format:
diff --git a/src/cairo.h b/src/cairo.h
index e21cc8ae..37ccdc3c 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1889,6 +1889,7 @@ cairo_surface_status (cairo_surface_t *surface);
* @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface
* @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image
* @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10
+ * @CAIRO_SURFACE_TYPE_QT: The surface is of type Qt, since 1.10
*
* #cairo_surface_type_t is used to describe the type of a given
* surface. The surface types are also known as "backends" or "surface
@@ -1928,7 +1929,8 @@ typedef enum _cairo_surface_type {
CAIRO_SURFACE_TYPE_OS2,
CAIRO_SURFACE_TYPE_WIN32_PRINTING,
CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
- CAIRO_SURFACE_TYPE_SCRIPT
+ CAIRO_SURFACE_TYPE_SCRIPT,
+ CAIRO_SURFACE_TYPE_QT
} cairo_surface_type_t;
cairo_public cairo_surface_type_t
diff --git a/src/check-def.sh b/src/check-def.sh
index 96f343e0..a33e71e6 100755
--- a/src/check-def.sh
+++ b/src/check-def.sh
@@ -19,9 +19,10 @@ if tail -1 check-has-hidden-symbols.i | grep CAIRO_HAS_HIDDEN_SYMBOLS >/dev/null
exit 0
fi
-get_cairo_syms='nm "$so" | grep " [BCDGINRSTVW] " | cut -d" " -f3'
if [ "`uname -s`" = "Linux" ]; then
- get_cairo_syms='( objdump -t "$so" | grep "^[^ ]* [^l.*]*[.]"; objdump -t "$so" | grep "[.]hidden.*\\<cairo"; ) | sed "s/.* //"'
+ get_cairo_syms='( objdump -t "$so" | grep "^[^ ]* [^l.*]*[.]"; objdump -t "$so" | grep "[.]hidden.*\\<cairo"; ) | sed "s/.* //"'
+else
+ get_cairo_syms='nm "$so" | grep " [BCDGINRSTVW] " | cut -d" " -f3'
fi
defs="cairo.def"
@@ -37,7 +38,7 @@ for def in $defs; do
{
echo EXPORTS
- eval $get_cairo_syms | grep -v '^_cairo_test_\|^_fini\|^_init\|^_save[fg]pr\|^_rest[fg]pr' | sort -u
+ eval $get_cairo_syms | c++filt --no-params | grep -v '^_cairo_test_\|^_fini\|^_init\|^_save[fg]pr\|^_rest[fg]pr\|^_Z' | sort -u
# cheat: copy the last line from the def file!
tail -n1 "$def"
} | diff "$def" - >&2 || stat=1
diff --git a/src/check-preprocessor-syntax.sh b/src/check-preprocessor-syntax.sh
index d5735498..4e8efaa3 100755
--- a/src/check-preprocessor-syntax.sh
+++ b/src/check-preprocessor-syntax.sh
@@ -52,6 +52,7 @@ grep '#.*\<include\>.*<.*cairo' $ALL >&2 && stat=1
echo 'Checking that feature conditionals are used with #if only (not #ifdef)'
-grep '#if.*CAIRO_HAS_' $ALL | grep def >&2 && stat=1
+grep '#ifdef CAIRO_HAS_' $ALL && stat=1
+grep '#if.*defined[ ]*(CAIRO_HAS_' $ALL && stat=1
exit $stat