1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /* cairo - a vector graphics library with display and print output
3 *
4 * Copyright © 2008 Mozilla Corporation
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it either under the terms of the GNU Lesser General Public
8 * License version 2.1 as published by the Free Software Foundation
9 * (the "LGPL") or, at your option, under the terms of the Mozilla
10 * Public License Version 1.1 (the "MPL"). If you do not alter this
11 * notice, a recipient may use your version of this file under either
12 * the MPL or the LGPL.
13 *
14 * You should have received a copy of the LGPL along with this library
15 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17 * You should have received a copy of the MPL along with this library
18 * in the file COPYING-MPL-1.1
19 *
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License at
23 * http://www.mozilla.org/MPL/
24 *
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27 * the specific language governing rights and limitations.
28 *
29 * The Original Code is the cairo graphics library.
30 *
31 * The Initial Developer of the Original Code is Mozilla Corporation.
32 *
33 * Contributor(s):
34 * Vladimir Vukicevic <vladimir@mozilla.com>
35 */
36
37 /* Get INT16_MIN etc. as per C99 */
38 #define __STDC_LIMIT_MACROS
39
40 #include "cairoint.h"
41
42 #include "cairo-clip-private.h"
43 #include "cairo-default-context-private.h"
44 #include "cairo-error-private.h"
45 #include "cairo-region-private.h"
46 #include "cairo-surface-clipper-private.h"
47 #include "cairo-types-private.h"
48 #include "cairo-image-surface-private.h"
49 #include "cairo-pattern-private.h"
50 #include "cairo-surface-backend-private.h"
51 #include "cairo-surface-fallback-private.h"
52
53 #include "cairo-ft.h"
54 #include "cairo-qt.h"
55
56 #include <memory>
57
58 #include <QtGui/QPainter>
59 #include <QtGui/QPaintEngine>
60 #include <QtGui/QPaintDevice>
61 #include <QtGui/QImage>
62 #include <QtGui/QPixmap>
63 #include <QtGui/QBrush>
64 #include <QtGui/QPen>
65 #include <QWidget>
66 #include <QtCore/QVarLengthArray>
67
68 #if ((QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)) && 0
69 extern void qt_draw_glyphs(QPainter *, const quint32 *glyphs, const QPointF *positions, int count);
70 #endif
71
72 #include <sys/time.h>
73
74 /* Enable workaround slow regional Qt paths */
75 #define ENABLE_FAST_FILL 0
76 #define ENABLE_FAST_CLIP 0
77
78 #if 0
79 #define D(x) x
80 static const char *
81 _opstr (cairo_operator_t op)
82 {
83 const char *ops[] = {
84 "CLEAR",
85 "SOURCE",
86 "OVER",
87 "IN",
88 "OUT",
89 "ATOP",
90 "DEST",
91 "DEST_OVER",
92 "DEST_IN",
93 "DEST_OUT",
94 "DEST_ATOP",
95 "XOR",
96 "ADD",
97 "SATURATE"
98 };
99
100 if (op < CAIRO_OPERATOR_CLEAR || op > CAIRO_OPERATOR_SATURATE)
101 return "(\?\?\?)";
102
103 return ops[op];
104 }
105 #else
106 #define D(x) do { } while(0)
107 #endif
108
109 #ifndef CAIRO_INT_STATUS_SUCCESS
110 #define CAIRO_INT_STATUS_SUCCESS ((cairo_int_status_t) CAIRO_STATUS_SUCCESS)
111 #endif
112
113 /* Qt::PenStyle optimization based on the assumption that dots are 1*w and dashes are 3*w. */
114 #define DOT_LENGTH 1.0
115 #define DASH_LENGTH 3.0
116
117 struct cairo_qt_surface_t {
118 cairo_surface_t base;
119
120 cairo_bool_t supports_porter_duff;
121
122 QPainter *p;
123
124 /* The pixmap/image constructors will store their objects here */
125 QPixmap *pixmap;
126 QImage *image;
127
128 QRect window;
129
130 cairo_surface_clipper_t clipper;
131
132 cairo_surface_t *image_equiv;
133 };
134
135 /* Will be true if we ever try to create a QPixmap and end
136 * up with one without an alpha channel.
137 */
138 static cairo_bool_t _qpixmaps_have_no_alpha = FALSE;
139
140 /*
141 * Helper methods
142 */
143
144 static QPainter::CompositionMode
_qpainter_compositionmode_from_cairo_op(cairo_operator_t op)145 _qpainter_compositionmode_from_cairo_op (cairo_operator_t op)
146 {
147 switch (op) {
148 case CAIRO_OPERATOR_CLEAR:
149 return QPainter::CompositionMode_Clear;
150
151 case CAIRO_OPERATOR_SOURCE:
152 return QPainter::CompositionMode_Source;
153 case CAIRO_OPERATOR_OVER:
154 return QPainter::CompositionMode_SourceOver;
155 case CAIRO_OPERATOR_IN:
156 return QPainter::CompositionMode_SourceIn;
157 case CAIRO_OPERATOR_OUT:
158 return QPainter::CompositionMode_SourceOut;
159 case CAIRO_OPERATOR_ATOP:
160 return QPainter::CompositionMode_SourceAtop;
161
162 case CAIRO_OPERATOR_DEST:
163 return QPainter::CompositionMode_Destination;
164 case CAIRO_OPERATOR_DEST_OVER:
165 return QPainter::CompositionMode_DestinationOver;
166 case CAIRO_OPERATOR_DEST_IN:
167 return QPainter::CompositionMode_DestinationIn;
168 case CAIRO_OPERATOR_DEST_OUT:
169 return QPainter::CompositionMode_DestinationOut;
170 case CAIRO_OPERATOR_DEST_ATOP:
171 return QPainter::CompositionMode_DestinationAtop;
172
173 case CAIRO_OPERATOR_XOR:
174 return QPainter::CompositionMode_Xor;
175
176 default:
177 case CAIRO_OPERATOR_ADD:
178 case CAIRO_OPERATOR_SATURATE:
179 case CAIRO_OPERATOR_MULTIPLY:
180 case CAIRO_OPERATOR_SCREEN:
181 case CAIRO_OPERATOR_OVERLAY:
182 case CAIRO_OPERATOR_DARKEN:
183 case CAIRO_OPERATOR_LIGHTEN:
184 case CAIRO_OPERATOR_COLOR_DODGE:
185 case CAIRO_OPERATOR_COLOR_BURN:
186 case CAIRO_OPERATOR_HARD_LIGHT:
187 case CAIRO_OPERATOR_SOFT_LIGHT:
188 case CAIRO_OPERATOR_DIFFERENCE:
189 case CAIRO_OPERATOR_EXCLUSION:
190 case CAIRO_OPERATOR_HSL_HUE:
191 case CAIRO_OPERATOR_HSL_SATURATION:
192 case CAIRO_OPERATOR_HSL_COLOR:
193 case CAIRO_OPERATOR_HSL_LUMINOSITY:
194 ASSERT_NOT_REACHED;
195 }
196 }
197
198 static bool
_op_is_supported(cairo_qt_surface_t * qs,cairo_operator_t op)199 _op_is_supported (cairo_qt_surface_t *qs, cairo_operator_t op)
200 {
201 if (qs->p == NULL)
202 return false;
203
204 if (qs->supports_porter_duff) {
205 switch (op) {
206 case CAIRO_OPERATOR_CLEAR:
207 case CAIRO_OPERATOR_SOURCE:
208 case CAIRO_OPERATOR_OVER:
209 case CAIRO_OPERATOR_IN:
210 case CAIRO_OPERATOR_OUT:
211 case CAIRO_OPERATOR_ATOP:
212
213 case CAIRO_OPERATOR_DEST:
214 case CAIRO_OPERATOR_DEST_OVER:
215 case CAIRO_OPERATOR_DEST_IN:
216 case CAIRO_OPERATOR_DEST_OUT:
217 case CAIRO_OPERATOR_DEST_ATOP:
218
219 case CAIRO_OPERATOR_XOR:
220 return TRUE;
221
222 default:
223 ASSERT_NOT_REACHED;
224 case CAIRO_OPERATOR_ADD:
225 case CAIRO_OPERATOR_SATURATE:
226 case CAIRO_OPERATOR_MULTIPLY:
227 case CAIRO_OPERATOR_SCREEN:
228 case CAIRO_OPERATOR_OVERLAY:
229 case CAIRO_OPERATOR_DARKEN:
230 case CAIRO_OPERATOR_LIGHTEN:
231 case CAIRO_OPERATOR_COLOR_DODGE:
232 case CAIRO_OPERATOR_COLOR_BURN:
233 case CAIRO_OPERATOR_HARD_LIGHT:
234 case CAIRO_OPERATOR_SOFT_LIGHT:
235 case CAIRO_OPERATOR_DIFFERENCE:
236 case CAIRO_OPERATOR_EXCLUSION:
237 case CAIRO_OPERATOR_HSL_HUE:
238 case CAIRO_OPERATOR_HSL_SATURATION:
239 case CAIRO_OPERATOR_HSL_COLOR:
240 case CAIRO_OPERATOR_HSL_LUMINOSITY:
241 return FALSE;
242
243 }
244 } else {
245 return op == CAIRO_OPERATOR_OVER;
246 }
247 }
248
249 static cairo_format_t
_cairo_format_from_qimage_format(QImage::Format fmt)250 _cairo_format_from_qimage_format (QImage::Format fmt)
251 {
252 switch (fmt) {
253 case QImage::Format_ARGB32_Premultiplied:
254 return CAIRO_FORMAT_ARGB32;
255 case QImage::Format_RGB32:
256 return CAIRO_FORMAT_RGB24;
257 case QImage::Format_Indexed8: // XXX not quite
258 return CAIRO_FORMAT_A8;
259 #ifdef WORDS_BIGENDIAN
260 case QImage::Format_Mono:
261 #else
262 case QImage::Format_MonoLSB:
263 #endif
264 return CAIRO_FORMAT_A1;
265
266 case QImage::Format_Invalid:
267 #ifdef WORDS_BIGENDIAN
268 case QImage::Format_MonoLSB:
269 #else
270 case QImage::Format_Mono:
271 #endif
272 case QImage::Format_ARGB32:
273 case QImage::Format_RGB16:
274 case QImage::Format_ARGB8565_Premultiplied:
275 case QImage::Format_RGB666:
276 case QImage::Format_ARGB6666_Premultiplied:
277 case QImage::Format_RGB555:
278 case QImage::Format_ARGB8555_Premultiplied:
279 case QImage::Format_RGB888:
280 case QImage::Format_RGB444:
281 case QImage::Format_ARGB4444_Premultiplied:
282 case QImage::NImageFormats:
283 default:
284 ASSERT_NOT_REACHED;
285 return (cairo_format_t) -1;
286 }
287 }
288
289 static QImage::Format
_qimage_format_from_cairo_format(cairo_format_t fmt)290 _qimage_format_from_cairo_format (cairo_format_t fmt)
291 {
292 switch (fmt) {
293 case CAIRO_FORMAT_INVALID:
294 ASSERT_NOT_REACHED;
295 case CAIRO_FORMAT_ARGB32:
296 return QImage::Format_ARGB32_Premultiplied;
297 case CAIRO_FORMAT_RGB24:
298 return QImage::Format_RGB32;
299 case CAIRO_FORMAT_RGB16_565:
300 return QImage::Format_RGB16;
301 case CAIRO_FORMAT_A8:
302 return QImage::Format_Indexed8; // XXX not quite
303 case CAIRO_FORMAT_A1:
304 #ifdef WORDS_BIGENDIAN
305 return QImage::Format_Mono; // XXX think we need to choose between this and LSB
306 #else
307 return QImage::Format_MonoLSB;
308 #endif
309 case CAIRO_FORMAT_RGB30:
310 return QImage::Format_Mono;
311 }
312
313 return QImage::Format_Mono;
314 }
315
316 static inline QMatrix
_qmatrix_from_cairo_matrix(const cairo_matrix_t & m)317 _qmatrix_from_cairo_matrix (const cairo_matrix_t& m)
318 {
319 return QMatrix(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0);
320 }
321
322 /* Path conversion */
323 typedef struct _qpainter_path_transform {
324 QPainterPath path;
325 const cairo_matrix_t *ctm_inverse;
326 } qpainter_path_data;
327
328 /* cairo path -> execute in context */
329 static cairo_status_t
_cairo_path_to_qpainterpath_move_to(void * closure,const cairo_point_t * point)330 _cairo_path_to_qpainterpath_move_to (void *closure, const cairo_point_t *point)
331 {
332 qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
333 double x = _cairo_fixed_to_double (point->x);
334 double y = _cairo_fixed_to_double (point->y);
335
336 if (pdata->ctm_inverse)
337 cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y);
338
339 pdata->path.moveTo(x, y);
340
341 return CAIRO_STATUS_SUCCESS;
342 }
343
344 static cairo_status_t
_cairo_path_to_qpainterpath_line_to(void * closure,const cairo_point_t * point)345 _cairo_path_to_qpainterpath_line_to (void *closure, const cairo_point_t *point)
346 {
347 qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
348 double x = _cairo_fixed_to_double (point->x);
349 double y = _cairo_fixed_to_double (point->y);
350
351 if (pdata->ctm_inverse)
352 cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y);
353
354 pdata->path.lineTo(x, y);
355
356 return CAIRO_STATUS_SUCCESS;
357 }
358
359 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)360 _cairo_path_to_qpainterpath_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2)
361 {
362 qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
363 double x0 = _cairo_fixed_to_double (p0->x);
364 double y0 = _cairo_fixed_to_double (p0->y);
365 double x1 = _cairo_fixed_to_double (p1->x);
366 double y1 = _cairo_fixed_to_double (p1->y);
367 double x2 = _cairo_fixed_to_double (p2->x);
368 double y2 = _cairo_fixed_to_double (p2->y);
369
370 if (pdata->ctm_inverse) {
371 cairo_matrix_transform_point (pdata->ctm_inverse, &x0, &y0);
372 cairo_matrix_transform_point (pdata->ctm_inverse, &x1, &y1);
373 cairo_matrix_transform_point (pdata->ctm_inverse, &x2, &y2);
374 }
375
376 pdata->path.cubicTo (x0, y0, x1, y1, x2, y2);
377
378 return CAIRO_STATUS_SUCCESS;
379 }
380
381 static cairo_status_t
_cairo_path_to_qpainterpath_close_path(void * closure)382 _cairo_path_to_qpainterpath_close_path (void *closure)
383 {
384 qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
385
386 pdata->path.closeSubpath();
387
388 return CAIRO_STATUS_SUCCESS;
389 }
390
391 static QPainterPath
path_to_qt(const cairo_path_fixed_t * path,const cairo_matrix_t * ctm_inverse=NULL)392 path_to_qt (const cairo_path_fixed_t *path,
393 const cairo_matrix_t *ctm_inverse = NULL)
394 {
395 qpainter_path_data data;
396 cairo_status_t status;
397
398 if (ctm_inverse && _cairo_matrix_is_identity (ctm_inverse))
399 ctm_inverse = NULL;
400 data.ctm_inverse = ctm_inverse;
401
402 status = _cairo_path_fixed_interpret (path,
403 _cairo_path_to_qpainterpath_move_to,
404 _cairo_path_to_qpainterpath_line_to,
405 _cairo_path_to_qpainterpath_curve_to,
406 _cairo_path_to_qpainterpath_close_path,
407 &data);
408 assert (status == CAIRO_STATUS_SUCCESS);
409
410 return data.path;
411 }
412
413 static inline QPainterPath
path_to_qt(const cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,cairo_matrix_t * ctm_inverse=NULL)414 path_to_qt (const cairo_path_fixed_t *path,
415 cairo_fill_rule_t fill_rule,
416 cairo_matrix_t *ctm_inverse = NULL)
417 {
418 QPainterPath qpath = path_to_qt (path, ctm_inverse);
419
420 qpath.setFillRule (fill_rule == CAIRO_FILL_RULE_WINDING ?
421 Qt::WindingFill :
422 Qt::OddEvenFill);
423
424 return qpath;
425 }
426
427 /*
428 * Surface backend methods
429 */
430 static cairo_surface_t *
_cairo_qt_surface_create_similar(void * abstract_surface,cairo_content_t content,int width,int height)431 _cairo_qt_surface_create_similar (void *abstract_surface,
432 cairo_content_t content,
433 int width,
434 int height)
435 {
436 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
437 bool use_pixmap;
438
439 D(fprintf(stderr, "q[%p] create_similar: %d %d [%d] -> ", abstract_surface, width, height, content));
440
441 use_pixmap = qs->image == NULL;
442 if (use_pixmap) {
443 switch (content) {
444 case CAIRO_CONTENT_ALPHA:
445 use_pixmap = FALSE;
446 break;
447 case CAIRO_CONTENT_COLOR:
448 break;
449 case CAIRO_CONTENT_COLOR_ALPHA:
450 use_pixmap = ! _qpixmaps_have_no_alpha;
451 break;
452 }
453 }
454
455 if (use_pixmap) {
456 cairo_surface_t *result =
457 cairo_qt_surface_create_with_qpixmap (content, width, height);
458
459 /* XXX result->content is always content. ??? */
460 if (result->content == content) {
461 D(fprintf(stderr, "qpixmap content: %d\n", content));
462 return result;
463 }
464
465 _qpixmaps_have_no_alpha = TRUE;
466 cairo_surface_destroy (result);
467 }
468
469 D(fprintf (stderr, "qimage\n"));
470 return cairo_qt_surface_create_with_qimage
471 (_cairo_format_from_content (content), width, height);
472 }
473
474 static cairo_status_t
_cairo_qt_surface_finish(void * abstract_surface)475 _cairo_qt_surface_finish (void *abstract_surface)
476 {
477 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
478
479 D(fprintf(stderr, "q[%p] finish\n", abstract_surface));
480
481 /* Only delete p if we created it */
482 if (qs->image || qs->pixmap)
483 delete qs->p;
484 else
485 qs->p->restore ();
486
487 if (qs->image_equiv)
488 cairo_surface_destroy (qs->image_equiv);
489
490 _cairo_surface_clipper_reset (&qs->clipper);
491
492 if (qs->image)
493 delete qs->image;
494
495 if (qs->pixmap)
496 delete qs->pixmap;
497
498 return CAIRO_STATUS_SUCCESS;
499 }
500
501 static void
_qimg_destroy(void * closure)502 _qimg_destroy (void *closure)
503 {
504 QImage *qimg = (QImage *) closure;
505 delete qimg;
506 }
507
508 static cairo_status_t
_cairo_qt_surface_acquire_source_image(void * abstract_surface,cairo_image_surface_t ** image_out,void ** image_extra)509 _cairo_qt_surface_acquire_source_image (void *abstract_surface,
510 cairo_image_surface_t **image_out,
511 void **image_extra)
512 {
513 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
514
515 D(fprintf(stderr, "q[%p] acquire_source_image\n", abstract_surface));
516
517 *image_extra = NULL;
518
519 if (qs->image_equiv) {
520 *image_out = (cairo_image_surface_t*)
521 cairo_surface_reference (qs->image_equiv);
522
523 return CAIRO_STATUS_SUCCESS;
524 }
525
526 if (qs->pixmap) {
527 QImage *qimg = new QImage(qs->pixmap->toImage());
528 cairo_surface_t *image;
529 cairo_status_t status;
530
531 image = cairo_image_surface_create_for_data (qimg->bits(),
532 _cairo_format_from_qimage_format (qimg->format()),
533 qimg->width(), qimg->height(),
534 qimg->bytesPerLine());
535
536 status = _cairo_user_data_array_set_data (&image->user_data,
537 (const cairo_user_data_key_t *)&_qimg_destroy,
538 qimg,
539 _qimg_destroy);
540 if (status) {
541 cairo_surface_destroy (image);
542 return status;
543 }
544
545 *image_out = (cairo_image_surface_t *) image;
546 return CAIRO_STATUS_SUCCESS;
547 }
548
549 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
550 }
551
552 static void
_cairo_qt_surface_release_source_image(void * abstract_surface,cairo_image_surface_t * image,void * image_extra)553 _cairo_qt_surface_release_source_image (void *abstract_surface,
554 cairo_image_surface_t *image,
555 void *image_extra)
556 {
557 //cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
558
559 D(fprintf(stderr, "q[%p] release_source_image\n", abstract_surface));
560
561 cairo_surface_destroy (&image->base);
562 }
563
564 struct _qimage_surface {
565 cairo_image_surface_t image;
566 QImage *qimg;
567 };
568
569 static cairo_surface_t *
map_qimage_to_image(QImage * qimg,const cairo_rectangle_int_t * extents)570 map_qimage_to_image (QImage *qimg, const cairo_rectangle_int_t *extents)
571 {
572 struct _qimage_surface *surface;
573 pixman_image_t *pixman_image;
574 pixman_format_code_t pixman_format;
575 uint8_t *data;
576
577 if (qimg == NULL)
578 return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
579
580 switch (qimg->format()) {
581 case QImage::Format_ARGB32_Premultiplied:
582 pixman_format = PIXMAN_a8r8g8b8;
583 break;
584 case QImage::Format_RGB32:
585 pixman_format = PIXMAN_x8r8g8b8;
586 break;
587 case QImage::Format_Indexed8: // XXX not quite
588 pixman_format = PIXMAN_a8;
589 break;
590 #ifdef WORDS_BIGENDIAN
591 case QImage::Format_Mono:
592 #else
593 case QImage::Format_MonoLSB:
594 #endif
595 pixman_format = PIXMAN_a1;
596 break;
597
598 case QImage::Format_Invalid:
599 #ifdef WORDS_BIGENDIAN
600 case QImage::Format_MonoLSB:
601 #else
602 case QImage::Format_Mono:
603 #endif
604 case QImage::Format_ARGB32:
605 case QImage::Format_RGB16:
606 case QImage::Format_ARGB8565_Premultiplied:
607 case QImage::Format_RGB666:
608 case QImage::Format_ARGB6666_Premultiplied:
609 case QImage::Format_RGB555:
610 case QImage::Format_ARGB8555_Premultiplied:
611 case QImage::Format_RGB888:
612 case QImage::Format_RGB444:
613 case QImage::Format_ARGB4444_Premultiplied:
614 case QImage::NImageFormats:
615 default:
616 delete qimg;
617 return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT);
618 }
619
620 data = qimg->bits();
621 data += extents->y * qimg->bytesPerLine();
622 data += extents->x * PIXMAN_FORMAT_BPP (pixman_format) / 8;
623
624 pixman_image = pixman_image_create_bits (pixman_format,
625 extents->width,
626 extents->height,
627 (uint32_t *)data,
628 qimg->bytesPerLine());
629 if (pixman_image == NULL) {
630 delete qimg;
631 return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
632 }
633
634 surface = (struct _qimage_surface *) _cairo_malloc (sizeof(*surface));
635 if (unlikely (surface == NULL)) {
636 pixman_image_unref (pixman_image);
637 delete qimg;
638 return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
639 }
640
641 _cairo_image_surface_init (&surface->image, pixman_image, pixman_format);
642 surface->qimg = qimg;
643
644 cairo_surface_set_device_offset (&surface->image.base,
645 -extents->x, -extents->y);
646
647 return &surface->image.base;
648 }
649
650 static cairo_image_surface_t *
_cairo_qt_surface_map_to_image(void * abstract_surface,const cairo_rectangle_int_t * extents)651 _cairo_qt_surface_map_to_image (void *abstract_surface,
652 const cairo_rectangle_int_t *extents)
653 {
654 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
655 QImage *qimg = NULL;
656
657 D(fprintf(stderr, "q[%p] acquire_dest_image\n", abstract_surface));
658
659 if (qs->image_equiv)
660 return _cairo_image_surface_map_to_image (qs->image_equiv,
661 extents);
662
663 QPoint offset;
664
665 if (qs->pixmap) {
666 qimg = new QImage(qs->pixmap->toImage());
667 } else {
668 // Try to figure out what kind of QPaintDevice we have, and
669 // how we can grab an image from it
670 QPaintDevice *pd = qs->p->device();
671 if (!pd)
672 return (cairo_image_surface_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
673
674 QPaintDevice *rpd = QPainter::redirected(pd, &offset);
675 if (rpd)
676 pd = rpd;
677
678 if (pd->devType() == QInternal::Image) {
679 qimg = new QImage(((QImage*) pd)->copy());
680 } else if (pd->devType() == QInternal::Pixmap) {
681 qimg = new QImage(((QPixmap*) pd)->toImage());
682 } else if (pd->devType() == QInternal::Widget) {
683 qimg = new QImage(QPixmap::grabWindow(((QWidget*)pd)->winId()).toImage());
684 }
685 }
686
687 return (cairo_image_surface_t *) map_qimage_to_image (qimg, extents);
688 }
689
690 static cairo_int_status_t
_cairo_qt_surface_unmap_image(void * abstract_surface,cairo_image_surface_t * image)691 _cairo_qt_surface_unmap_image (void *abstract_surface,
692 cairo_image_surface_t *image)
693 {
694 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
695
696 D(fprintf(stderr, "q[%p] release_dest_image\n", abstract_surface));
697
698 if (!qs->image_equiv) {
699 struct _qimage_surface *qimage = (struct _qimage_surface *)image;
700
701 // XXX should I be using setBackgroundMode here instead of setCompositionMode?
702 if (qs->supports_porter_duff)
703 qs->p->setCompositionMode (QPainter::CompositionMode_Source);
704
705 qs->p->drawImage ((int)qimage->image.base.device_transform.x0,
706 (int)qimage->image.base.device_transform.y0,
707 *qimage->qimg,
708 (int)qimage->image.base.device_transform.x0,
709 (int)qimage->image.base.device_transform.y0,
710 (int)qimage->image.width,
711 (int)qimage->image.height);
712
713 if (qs->supports_porter_duff)
714 qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
715
716 delete qimage->qimg;
717 }
718
719 cairo_surface_finish (&image->base);
720 cairo_surface_destroy (&image->base);
721
722 return CAIRO_INT_STATUS_SUCCESS;
723 }
724
725 static cairo_bool_t
_cairo_qt_surface_get_extents(void * abstract_surface,cairo_rectangle_int_t * extents)726 _cairo_qt_surface_get_extents (void *abstract_surface,
727 cairo_rectangle_int_t *extents)
728 {
729 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
730
731 extents->x = qs->window.x();
732 extents->y = qs->window.y();
733 extents->width = qs->window.width();
734 extents->height = qs->window.height();
735
736 return TRUE;
737 }
738
739 static cairo_status_t
_cairo_qt_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)740 _cairo_qt_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
741 cairo_path_fixed_t *path,
742 cairo_fill_rule_t fill_rule,
743 double tolerance,
744 cairo_antialias_t antialias)
745 {
746 cairo_qt_surface_t *qs = cairo_container_of (clipper,
747 cairo_qt_surface_t,
748 clipper);
749
750 if (path == NULL) {
751 if (qs->pixmap || qs->image) {
752 // we own p
753 qs->p->setClipping (false);
754 } else {
755 qs->p->restore ();
756 qs->p->save ();
757 }
758 } else {
759 // XXX Antialiasing is ignored
760 qs->p->setClipPath (path_to_qt (path, fill_rule), Qt::IntersectClip);
761 }
762
763 return CAIRO_STATUS_SUCCESS;
764 }
765
766 static void
_cairo_qt_surface_set_clip_region(cairo_qt_surface_t * qs,const cairo_region_t * clip_region)767 _cairo_qt_surface_set_clip_region (cairo_qt_surface_t *qs,
768 const cairo_region_t *clip_region)
769 {
770 _cairo_surface_clipper_reset (&qs->clipper);
771
772 if (clip_region == NULL) {
773 // How the clip path is reset depends on whether we own p or not
774 if (qs->pixmap || qs->image) {
775 // we own p
776 qs->p->setClipping (false);
777 } else {
778 qs->p->restore ();
779 qs->p->save ();
780 }
781 } else {
782 QRegion qr;
783 int num_rects = cairo_region_num_rectangles (clip_region);
784 for (int i = 0; i < num_rects; ++i) {
785 cairo_rectangle_int_t rect;
786
787 cairo_region_get_rectangle (clip_region, i, &rect);
788
789 QRect r(rect.x, rect.y, rect.width, rect.height);
790 qr = qr.unite(r);
791 }
792
793 qs->p->setClipRegion (qr, Qt::IntersectClip);
794 }
795 }
796
797 static cairo_int_status_t
_cairo_qt_surface_set_clip(cairo_qt_surface_t * qs,const cairo_clip_t * clip)798 _cairo_qt_surface_set_clip (cairo_qt_surface_t *qs,
799 const cairo_clip_t *clip)
800 {
801 cairo_int_status_t status;
802
803 D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", abstract_surface, path ? "(path)" : "(clear)"));
804
805 if (clip == NULL) {
806 _cairo_surface_clipper_reset (&qs->clipper);
807 // How the clip path is reset depends on whether we own p or not
808 if (qs->pixmap || qs->image) {
809 // we own p
810 qs->p->setClipping (false);
811 } else {
812 qs->p->restore ();
813 qs->p->save ();
814 }
815
816 return CAIRO_INT_STATUS_SUCCESS;
817 }
818
819 #if ENABLE_FAST_CLIP
820 // Qt will implicitly enable clipping, and will use ReplaceClip
821 // instead of IntersectClip if clipping was disabled before
822
823 // Note: Qt is really bad at dealing with clip paths. It doesn't
824 // seem to usefully recognize rectangular paths, instead going down
825 // extremely slow paths whenever a clip path is set. So,
826 // we do a bunch of work here to try to get rectangles or regions
827 // down to Qt for clipping.
828
829 cairo_region_t *clip_region = NULL;
830
831 status = _cairo_clip_get_region (clip, &clip_region);
832 if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
833 // We weren't able to extract a region from the traps.
834 // Just hand the path down to QPainter.
835 status = (cairo_int_status_t)
836 _cairo_surface_clipper_set_clip (&qs->clipper, clip);
837 } else if (status == CAIRO_INT_STATUS_SUCCESS) {
838 _cairo_qt_surface_set_clip_region (qs, clip_region);
839 status = CAIRO_INT_STATUS_SUCCESS;
840 }
841 #else
842 status = (cairo_int_status_t)
843 _cairo_surface_clipper_set_clip (&qs->clipper, clip);
844 #endif
845
846 return status;
847 }
848
849 /*
850 * Brush conversion
851 */
852
853 struct PatternToBrushConverter {
PatternToBrushConverterPatternToBrushConverter854 PatternToBrushConverter (const cairo_pattern_t *pattern)
855 __attribute__ ((noinline)) :
856 mAcquiredImageParent(0),
857 mAcquiredImage(0),
858 mAcquiredImageExtra(0)
859 {
860 if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
861 cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern;
862 QColor color;
863 color.setRgbF(solid->color.red,
864 solid->color.green,
865 solid->color.blue,
866 solid->color.alpha);
867
868 mBrush = QBrush(color);
869 } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
870 cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern;
871 cairo_surface_t *surface = spattern->surface;
872
873 if (surface->type == CAIRO_SURFACE_TYPE_QT) {
874 cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
875
876 if (qs->image) {
877 mBrush = QBrush(*qs->image);
878 } else if (qs->pixmap) {
879 mBrush = QBrush(*qs->pixmap);
880 } else {
881 // do something smart
882 mBrush = QBrush(0xff0000ff);
883 }
884 } else {
885 cairo_image_surface_t *isurf = NULL;
886
887 if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
888 isurf = (cairo_image_surface_t*) surface;
889 } else {
890 void *image_extra;
891
892 if (_cairo_surface_acquire_source_image (surface, &isurf, &image_extra) == CAIRO_STATUS_SUCCESS) {
893 mAcquiredImageParent = surface;
894 mAcquiredImage = isurf;
895 mAcquiredImageExtra = image_extra;
896 } else {
897 isurf = NULL;
898 }
899 }
900
901 if (isurf) {
902 mBrush = QBrush (QImage ((const uchar *) isurf->data,
903 isurf->width,
904 isurf->height,
905 isurf->stride,
906 _qimage_format_from_cairo_format (isurf->format)));
907 } else {
908 mBrush = QBrush(0x0000ffff);
909 }
910 }
911 } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
912 pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
913 {
914 QGradient *grad;
915 cairo_bool_t reverse_stops = FALSE;
916 cairo_bool_t emulate_reflect = FALSE;
917 double offset = 0.0;
918
919 cairo_extend_t extend = pattern->extend;
920
921 cairo_gradient_pattern_t *gpat = (cairo_gradient_pattern_t *) pattern;
922
923 if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
924 cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *) pattern;
925 grad = new QLinearGradient (lpat->pd1.x, lpat->pd1.y,
926 lpat->pd2.x, lpat->pd2.y);
927 } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
928 cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *) pattern;
929
930 /* Based on the SVG surface code */
931
932 cairo_circle_double_t *c0, *c1;
933 double x0, y0, r0, x1, y1, r1;
934
935 if (rpat->cd1.radius < rpat->cd2.radius) {
936 c0 = &rpat->cd1;
937 c1 = &rpat->cd2;
938 reverse_stops = FALSE;
939 } else {
940 c0 = &rpat->cd2;
941 c1 = &rpat->cd1;
942 reverse_stops = TRUE;
943 }
944
945 x0 = c0->center.x;
946 y0 = c0->center.y;
947 r0 = c0->radius;
948 x1 = c1->center.x;
949 y1 = c1->center.y;
950 r1 = c1->radius;
951
952 if (r0 == r1) {
953 grad = new QRadialGradient (x1, y1, r1, x1, y1);
954 } else {
955 double fx = (r1 * x0 - r0 * x1) / (r1 - r0);
956 double fy = (r1 * y0 - r0 * y1) / (r1 - r0);
957
958 /* QPainter doesn't support the inner circle and use instead a gradient focal.
959 * That means we need to emulate the cairo behaviour by processing the
960 * cairo gradient stops.
961 * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
962 * it's just a matter of stop position translation and calculation of
963 * the corresponding SVG radial gradient focal.
964 * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
965 * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
966 * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
967 * list that maps to the original cairo stop list.
968 */
969 if ((extend == CAIRO_EXTEND_REFLECT || extend == CAIRO_EXTEND_REPEAT) && r0 > 0.0) {
970 double r_org = r1;
971 double r, x, y;
972
973 if (extend == CAIRO_EXTEND_REFLECT) {
974 r1 = 2 * r1 - r0;
975 emulate_reflect = TRUE;
976 }
977
978 offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
979 r = r1 - r0;
980
981 /* New position of outer circle. */
982 x = r * (x1 - fx) / r_org + fx;
983 y = r * (y1 - fy) / r_org + fy;
984
985 x1 = x;
986 y1 = y;
987 r1 = r;
988 r0 = 0.0;
989 } else {
990 offset = r0 / r1;
991 }
992
993 grad = new QRadialGradient (x1, y1, r1, fx, fy);
994
995 if (extend == CAIRO_EXTEND_NONE && r0 != 0.0)
996 grad->setColorAt (r0 / r1, Qt::transparent);
997 }
998 }
999
1000 switch (extend) {
1001 case CAIRO_EXTEND_NONE:
1002 case CAIRO_EXTEND_PAD:
1003 grad->setSpread(QGradient::PadSpread);
1004
1005 grad->setColorAt (0.0, Qt::transparent);
1006 grad->setColorAt (1.0, Qt::transparent);
1007 break;
1008
1009 case CAIRO_EXTEND_REFLECT:
1010 grad->setSpread(QGradient::ReflectSpread);
1011 break;
1012
1013 case CAIRO_EXTEND_REPEAT:
1014 grad->setSpread(QGradient::RepeatSpread);
1015 break;
1016 }
1017
1018 for (unsigned int i = 0; i < gpat->n_stops; i++) {
1019 int index = i;
1020 if (reverse_stops)
1021 index = gpat->n_stops - i - 1;
1022
1023 double offset = gpat->stops[i].offset;
1024 QColor color;
1025 color.setRgbF (gpat->stops[i].color.red,
1026 gpat->stops[i].color.green,
1027 gpat->stops[i].color.blue,
1028 gpat->stops[i].color.alpha);
1029
1030 if (emulate_reflect) {
1031 offset = offset / 2.0;
1032 grad->setColorAt (1.0 - offset, color);
1033 }
1034
1035 grad->setColorAt (offset, color);
1036 }
1037
1038 mBrush = QBrush(*grad);
1039
1040 delete grad;
1041 }
1042
1043 if (mBrush.style() != Qt::NoBrush &&
1044 pattern->type != CAIRO_PATTERN_TYPE_SOLID &&
1045 ! _cairo_matrix_is_identity (&pattern->matrix))
1046 {
1047 cairo_matrix_t pm = pattern->matrix;
1048 cairo_status_t status = cairo_matrix_invert (&pm);
1049 assert (status == CAIRO_STATUS_SUCCESS);
1050 mBrush.setMatrix (_qmatrix_from_cairo_matrix (pm));
1051 }
1052 }
1053
~PatternToBrushConverterPatternToBrushConverter1054 ~PatternToBrushConverter () __attribute__ ((noinline)){
1055 if (mAcquiredImageParent)
1056 _cairo_surface_release_source_image (mAcquiredImageParent, mAcquiredImage, mAcquiredImageExtra);
1057 }
1058
operator QBrush&PatternToBrushConverter1059 operator QBrush& () {
1060 return mBrush;
1061 }
1062
1063 QBrush mBrush;
1064
1065 private:
1066 cairo_surface_t *mAcquiredImageParent;
1067 cairo_image_surface_t *mAcquiredImage;
1068 void *mAcquiredImageExtra;
1069 };
1070
1071 struct PatternToPenConverter {
PatternToPenConverterPatternToPenConverter1072 PatternToPenConverter (const cairo_pattern_t *source,
1073 const cairo_stroke_style_t *style) :
1074 mBrushConverter(source)
1075 {
1076 Qt::PenJoinStyle join = Qt::MiterJoin;
1077 Qt::PenCapStyle cap = Qt::SquareCap;
1078
1079 switch (style->line_cap) {
1080 case CAIRO_LINE_CAP_BUTT:
1081 cap = Qt::FlatCap;
1082 break;
1083 case CAIRO_LINE_CAP_ROUND:
1084 cap = Qt::RoundCap;
1085 break;
1086 case CAIRO_LINE_CAP_SQUARE:
1087 cap = Qt::SquareCap;
1088 break;
1089 }
1090
1091 switch (style->line_join) {
1092 case CAIRO_LINE_JOIN_MITER:
1093 join = Qt::MiterJoin;
1094 break;
1095 case CAIRO_LINE_JOIN_ROUND:
1096 join = Qt::RoundJoin;
1097 break;
1098 case CAIRO_LINE_JOIN_BEVEL:
1099 join = Qt::BevelJoin;
1100 break;
1101 }
1102
1103 mPen = QPen(mBrushConverter, style->line_width, Qt::SolidLine, cap, join);
1104 mPen.setMiterLimit (style->miter_limit);
1105
1106 if (style->dash && style->num_dashes) {
1107 Qt::PenStyle pstyle = Qt::NoPen;
1108
1109 if (style->num_dashes == 2) {
1110 if ((style->dash[0] == style->line_width &&
1111 style->dash[1] == style->line_width && style->line_width <= 2.0) ||
1112 (style->dash[0] == 0.0 &&
1113 style->dash[1] == style->line_width * 2 && cap == Qt::RoundCap))
1114 {
1115 pstyle = Qt::DotLine;
1116 } else if (style->dash[0] == style->line_width * DASH_LENGTH &&
1117 style->dash[1] == style->line_width * DASH_LENGTH &&
1118 cap == Qt::FlatCap)
1119 {
1120 pstyle = Qt::DashLine;
1121 }
1122 }
1123
1124 if (pstyle != Qt::NoPen) {
1125 mPen.setStyle(pstyle);
1126 return;
1127 }
1128
1129 unsigned int odd_dash = style->num_dashes % 2;
1130
1131 QVector<qreal> dashes (odd_dash ? style->num_dashes * 2 : style->num_dashes);
1132 for (unsigned int i = 0; i < odd_dash+1; i++) {
1133 for (unsigned int j = 0; j < style->num_dashes; j++) {
1134 // In Qt, the dash lengths are given in units of line width, whereas
1135 // in cairo, they are in user-space units. We'll always apply the CTM,
1136 // so all we have to do here is divide cairo's dash lengths by the line
1137 // width.
1138 dashes.append (style->dash[j] / style->line_width);
1139 }
1140 }
1141
1142 mPen.setDashPattern(dashes);
1143 mPen.setDashOffset(style->dash_offset / style->line_width);
1144 }
1145 }
1146
~PatternToPenConverterPatternToPenConverter1147 ~PatternToPenConverter() { }
1148
operator QPen&PatternToPenConverter1149 operator QPen& () {
1150 return mPen;
1151 }
1152
1153 QPen mPen;
1154 PatternToBrushConverter mBrushConverter;
1155 };
1156
1157 /*
1158 * Core drawing operations
1159 */
1160
1161 static bool
_cairo_qt_fast_fill(cairo_qt_surface_t * qs,const cairo_pattern_t * source,const 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)1162 _cairo_qt_fast_fill (cairo_qt_surface_t *qs,
1163 const cairo_pattern_t *source,
1164 const cairo_path_fixed_t *path = NULL,
1165 cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING,
1166 double tolerance = 0.0,
1167 cairo_antialias_t antialias = CAIRO_ANTIALIAS_NONE)
1168 {
1169 #if ENABLE_FAST_FILL
1170 QImage *qsSrc_image = NULL;
1171 QPixmap *qsSrc_pixmap = NULL;
1172 std::auto_ptr<QImage> qsSrc_image_d;
1173
1174
1175 if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
1176 cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) source;
1177 if (spattern->surface->type == CAIRO_SURFACE_TYPE_QT) {
1178 cairo_qt_surface_t *p = (cairo_qt_surface_t*) spattern->surface;
1179
1180 qsSrc_image = p->image;
1181 qsSrc_pixmap = p->pixmap;
1182 } else if (spattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
1183 cairo_image_surface_t *p = (cairo_image_surface_t*) spattern->surface;
1184 qsSrc_image = new QImage((const uchar*) p->data,
1185 p->width,
1186 p->height,
1187 p->stride,
1188 _qimage_format_from_cairo_format(p->format));
1189 qsSrc_image_d.reset(qsSrc_image);
1190 }
1191 }
1192
1193 if (!qsSrc_image && !qsSrc_pixmap)
1194 return false;
1195
1196 // We can only drawTiledPixmap; there's no drawTiledImage
1197 if (! qsSrc_pixmap &&
1198 (source->extend == CAIRO_EXTEND_REPEAT ||
1199 source->extend == CAIRO_EXTEND_REFLECT))
1200 {
1201 return false;
1202 }
1203
1204 QMatrix sourceMatrix = _qmatrix_from_cairo_matrix (source->matrix);
1205
1206 // We can draw this faster by clipping and calling drawImage/drawPixmap.
1207 // Use our own clipping function so that we can get the
1208 // region handling to end up with the fastest possible clip.
1209 //
1210 // XXX Antialiasing will fail pretty hard here, since we can't clip with AA
1211 // with QPainter.
1212 qs->p->save();
1213
1214 if (path) {
1215 cairo_int_status_t status;
1216
1217 cairo_clip_t clip, old_clip = qs->clipper.clip;
1218
1219 qs->clipper.clip = _cairo_clip_copy (&clip);
1220 status = (cairo_int_status_t) _cairo_clip_clip (&clip,
1221 path,
1222 fill_rule,
1223 tolerance,
1224 antialias);
1225 if (unlikely (status)) {
1226 qs->p->restore();
1227 return false;
1228 }
1229
1230 status = _cairo_qt_surface_set_clip (qs, &clip);
1231 if (unlikely (status)) {
1232 qs->p->restore();
1233 return false;
1234 }
1235
1236 _cairo_clip_reset (&clip);
1237 qs->clipper.clip = old_clip;
1238 }
1239
1240 qs->p->setWorldMatrix (sourceMatrix.inverted(), true);
1241
1242 switch (source->extend) {
1243 case CAIRO_EXTEND_REPEAT:
1244 // XXX handle reflect by tiling 4 times first
1245 case CAIRO_EXTEND_REFLECT: {
1246 assert (qsSrc_pixmap);
1247
1248 // Render the tiling to cover the entire destination window (because
1249 // it'll be clipped). Transform the window rect by the inverse
1250 // of the current world transform so that the device coordinates
1251 // end up as the right thing.
1252 QRectF dest = qs->p->worldTransform().inverted().mapRect(QRectF(qs->window));
1253 QPointF origin = sourceMatrix.map(QPointF(0.0, 0.0));
1254
1255 qs->p->drawTiledPixmap (dest, *qsSrc_pixmap, origin);
1256 }
1257 break;
1258 case CAIRO_EXTEND_NONE:
1259 case CAIRO_EXTEND_PAD: // XXX not exactly right, but good enough
1260 default:
1261 if (qsSrc_image)
1262 qs->p->drawImage (0, 0, *qsSrc_image);
1263 else if (qsSrc_pixmap)
1264 qs->p->drawPixmap (0, 0, *qsSrc_pixmap);
1265 break;
1266 }
1267
1268 qs->p->restore();
1269
1270 return true;
1271 #else
1272 return false;
1273 #endif
1274 }
1275
1276 static cairo_int_status_t
_cairo_qt_surface_paint(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_clip_t * clip)1277 _cairo_qt_surface_paint (void *abstract_surface,
1278 cairo_operator_t op,
1279 const cairo_pattern_t *source,
1280 const cairo_clip_t *clip)
1281 {
1282 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
1283 cairo_int_status_t status;
1284
1285 D(fprintf(stderr, "q[%p] paint op:%s\n", abstract_surface, _opstr(op)));
1286
1287 if (! _op_is_supported (qs, op))
1288 return _cairo_surface_fallback_paint (abstract_surface, op, source, clip);
1289
1290 status = _cairo_qt_surface_set_clip (qs, clip);
1291 if (unlikely (status))
1292 return status;
1293
1294 if (qs->supports_porter_duff)
1295 qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
1296
1297 if (! _cairo_qt_fast_fill (qs, source)) {
1298 PatternToBrushConverter brush (source);
1299 qs->p->fillRect (qs->window, brush);
1300 }
1301
1302 if (qs->supports_porter_duff)
1303 qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
1304
1305 return CAIRO_INT_STATUS_SUCCESS;
1306 }
1307
1308 static cairo_int_status_t
_cairo_qt_surface_fill(void * abstract_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)1309 _cairo_qt_surface_fill (void *abstract_surface,
1310 cairo_operator_t op,
1311 const cairo_pattern_t *source,
1312 const cairo_path_fixed_t *path,
1313 cairo_fill_rule_t fill_rule,
1314 double tolerance,
1315 cairo_antialias_t antialias,
1316 const cairo_clip_t *clip)
1317 {
1318 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
1319
1320 D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op)));
1321
1322 if (! _op_is_supported (qs, op))
1323 return _cairo_surface_fallback_fill (abstract_surface, op,
1324 source, path, fill_rule,
1325 tolerance, antialias, clip);
1326
1327 cairo_int_status_t status = _cairo_qt_surface_set_clip (qs, clip);
1328 if (unlikely (status))
1329 return status;
1330
1331 if (qs->supports_porter_duff)
1332 qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
1333
1334 // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is
1335 // enabled
1336 //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true);
1337 qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST);
1338
1339 if (! _cairo_qt_fast_fill (qs, source,
1340 path, fill_rule, tolerance, antialias))
1341 {
1342 PatternToBrushConverter brush(source);
1343 qs->p->fillPath (path_to_qt (path, fill_rule), brush);
1344 }
1345
1346 if (qs->supports_porter_duff)
1347 qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
1348
1349 return CAIRO_INT_STATUS_SUCCESS;
1350 }
1351
1352 static cairo_int_status_t
_cairo_qt_surface_stroke(void * abstract_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)1353 _cairo_qt_surface_stroke (void *abstract_surface,
1354 cairo_operator_t op,
1355 const cairo_pattern_t *source,
1356 const cairo_path_fixed_t *path,
1357 const cairo_stroke_style_t *style,
1358 const cairo_matrix_t *ctm,
1359 const cairo_matrix_t *ctm_inverse,
1360 double tolerance,
1361 cairo_antialias_t antialias,
1362 const cairo_clip_t *clip)
1363 {
1364 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
1365
1366 D(fprintf(stderr, "q[%p] stroke op:%s\n", abstract_surface, _opstr(op)));
1367
1368 if (! _op_is_supported (qs, op))
1369 return _cairo_surface_fallback_stroke (abstract_surface, op,
1370 source, path, style, ctm,
1371 ctm_inverse, tolerance,
1372 antialias, clip);
1373
1374 cairo_int_status_t int_status = _cairo_qt_surface_set_clip (qs, clip);
1375 if (unlikely (int_status))
1376 return int_status;
1377
1378 QMatrix savedMatrix = qs->p->worldMatrix();
1379
1380 if (qs->supports_porter_duff)
1381 qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
1382
1383 qs->p->setWorldMatrix (_qmatrix_from_cairo_matrix (*ctm), true);
1384 // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is
1385 // enabled
1386 //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true);
1387 qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST);
1388
1389 PatternToPenConverter pen(source, style);
1390
1391 qs->p->setPen(pen);
1392 qs->p->drawPath(path_to_qt (path, ctm_inverse));
1393 qs->p->setPen(Qt::black);
1394
1395 qs->p->setWorldMatrix (savedMatrix, false);
1396
1397 if (qs->supports_porter_duff)
1398 qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
1399
1400 return CAIRO_INT_STATUS_SUCCESS;
1401 }
1402
1403 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,const cairo_clip_t * clip)1404 _cairo_qt_surface_show_glyphs (void *abstract_surface,
1405 cairo_operator_t op,
1406 const cairo_pattern_t *source,
1407 cairo_glyph_t *glyphs,
1408 int num_glyphs,
1409 cairo_scaled_font_t *scaled_font,
1410 const cairo_clip_t *clip)
1411 {
1412 #if ((QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)) && 0
1413 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
1414
1415 // pick out the colour to use from the cairo source
1416 cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) source;
1417 cairo_scaled_glyph_t* glyph;
1418 // documentation says you have to freeze the cache, but I don't believe it
1419 _cairo_scaled_font_freeze_cache(scaled_font);
1420
1421 QColor tempColour(solid->color.red * 255, solid->color.green * 255, solid->color.blue * 255);
1422 QVarLengthArray<QPointF> positions(num_glyphs);
1423 QVarLengthArray<unsigned int> glyphss(num_glyphs);
1424 FT_Face face = cairo_ft_scaled_font_lock_face (scaled_font);
1425 const FT_Size_Metrics& ftMetrics = face->size->metrics;
1426 QFont font(face->family_name);
1427 font.setStyleStrategy(QFont::NoFontMerging);
1428 font.setBold(face->style_flags & FT_STYLE_FLAG_BOLD);
1429 font.setItalic(face->style_flags & FT_STYLE_FLAG_ITALIC);
1430 font.setKerning(face->face_flags & FT_FACE_FLAG_KERNING);
1431 font.setPixelSize(ftMetrics.y_ppem);
1432 cairo_ft_scaled_font_unlock_face(scaled_font);
1433 qs->p->setFont(font);
1434 qs->p->setPen(tempColour);
1435 for (int currentGlyph = 0; currentGlyph < num_glyphs; currentGlyph++) {
1436 positions[currentGlyph].setX(glyphs[currentGlyph].x);
1437 positions[currentGlyph].setY(glyphs[currentGlyph].y);
1438 glyphss[currentGlyph] = glyphs[currentGlyph].index;
1439 }
1440 qt_draw_glyphs(qs->p, glyphss.data(), positions.data(), num_glyphs);
1441 _cairo_scaled_font_thaw_cache(scaled_font);
1442 return CAIRO_INT_STATUS_SUCCESS;
1443 #else
1444 return _cairo_surface_fallback_glyphs (abstract_surface, op,
1445 source, glyphs, num_glyphs,
1446 scaled_font, clip);
1447 #endif
1448 }
1449
1450 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,const cairo_clip_t * clip)1451 _cairo_qt_surface_mask (void *abstract_surface,
1452 cairo_operator_t op,
1453 const cairo_pattern_t *source,
1454 const cairo_pattern_t *mask,
1455 const cairo_clip_t *clip)
1456 {
1457 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
1458
1459 D(fprintf(stderr, "q[%p] mask op:%s\n", abstract_surface, _opstr(op)));
1460
1461 if (qs->p && mask->type == CAIRO_PATTERN_TYPE_SOLID) {
1462 cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
1463 cairo_int_status_t result;
1464
1465 qs->p->setOpacity (solid_mask->color.alpha);
1466
1467 result = _cairo_qt_surface_paint (abstract_surface, op, source, clip);
1468
1469 qs->p->setOpacity (1.0);
1470
1471 return result;
1472 }
1473
1474 // otherwise skip for now
1475 return _cairo_surface_fallback_mask (abstract_surface, op, source, mask, clip);
1476 }
1477
1478 static cairo_status_t
_cairo_qt_surface_mark_dirty(void * abstract_surface,int x,int y,int width,int height)1479 _cairo_qt_surface_mark_dirty (void *abstract_surface,
1480 int x, int y,
1481 int width, int height)
1482 {
1483 cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
1484
1485 if (qs->p && !(qs->image || qs->pixmap))
1486 qs->p->save ();
1487
1488 return CAIRO_STATUS_SUCCESS;
1489 }
1490
1491 /*
1492 * Backend struct
1493 */
1494
1495 static const cairo_surface_backend_t cairo_qt_surface_backend = {
1496 CAIRO_SURFACE_TYPE_QT,
1497 _cairo_qt_surface_finish,
1498
1499 _cairo_default_context_create, /* XXX */
1500
1501 _cairo_qt_surface_create_similar,
1502 NULL, /* similar image */
1503 _cairo_qt_surface_map_to_image,
1504 _cairo_qt_surface_unmap_image,
1505
1506 _cairo_surface_default_source,
1507 _cairo_qt_surface_acquire_source_image,
1508 _cairo_qt_surface_release_source_image,
1509 NULL, /* snapshot */
1510
1511 NULL, /* copy_page */
1512 NULL, /* show_page */
1513
1514 _cairo_qt_surface_get_extents,
1515 NULL, /* get_font_options */
1516
1517 NULL, /* flush */
1518 _cairo_qt_surface_mark_dirty,
1519
1520 _cairo_qt_surface_paint,
1521 _cairo_qt_surface_mask,
1522 _cairo_qt_surface_stroke,
1523 _cairo_qt_surface_fill,
1524 NULL, /* fill_stroke */
1525 _cairo_qt_surface_show_glyphs
1526 };
1527
1528 cairo_surface_t *
cairo_qt_surface_create(QPainter * painter)1529 cairo_qt_surface_create (QPainter *painter)
1530 {
1531 cairo_qt_surface_t *qs;
1532
1533 qs = (cairo_qt_surface_t *) _cairo_malloc (sizeof(cairo_qt_surface_t));
1534 if (qs == NULL)
1535 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1536
1537 memset (qs, 0, sizeof(cairo_qt_surface_t));
1538
1539 _cairo_surface_init (&qs->base,
1540 &cairo_qt_surface_backend,
1541 NULL, /* device */
1542 CAIRO_CONTENT_COLOR_ALPHA,
1543 FALSE); /* is_vector */
1544
1545 _cairo_surface_clipper_init (&qs->clipper,
1546 _cairo_qt_surface_clipper_intersect_clip_path);
1547
1548 qs->p = painter;
1549 if (qs->p->paintEngine())
1550 qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
1551 else
1552 qs->supports_porter_duff = FALSE;
1553
1554 // Save so that we can always get back to the original state
1555 qs->p->save();
1556
1557 qs->window = painter->window();
1558
1559 D(fprintf(stderr, "qpainter_surface_create: window: [%d %d %d %d] pd:%d\n",
1560 qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
1561 qs->supports_porter_duff));
1562
1563 return &qs->base;
1564 }
1565
1566 cairo_surface_t *
cairo_qt_surface_create_with_qimage(cairo_format_t format,int width,int height)1567 cairo_qt_surface_create_with_qimage (cairo_format_t format,
1568 int width,
1569 int height)
1570 {
1571 cairo_qt_surface_t *qs;
1572
1573 qs = (cairo_qt_surface_t *) _cairo_malloc (sizeof(cairo_qt_surface_t));
1574 if (qs == NULL)
1575 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1576
1577 memset (qs, 0, sizeof(cairo_qt_surface_t));
1578
1579 _cairo_surface_init (&qs->base,
1580 &cairo_qt_surface_backend,
1581 NULL, /* device */
1582 _cairo_content_from_format (format),
1583 FALSE); /* is_vector */
1584
1585 _cairo_surface_clipper_init (&qs->clipper,
1586 _cairo_qt_surface_clipper_intersect_clip_path);
1587
1588
1589 QImage *image = new QImage (width, height,
1590 _qimage_format_from_cairo_format (format));
1591
1592 qs->image = image;
1593
1594 if (!image->isNull()) {
1595 qs->p = new QPainter(image);
1596 qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
1597 }
1598
1599 qs->image_equiv = cairo_image_surface_create_for_data (image->bits(),
1600 format,
1601 width, height,
1602 image->bytesPerLine());
1603
1604 qs->window = QRect(0, 0, width, height);
1605
1606 D(fprintf(stderr, "qpainter_surface_create: qimage: [%d %d %d %d] pd:%d\n",
1607 qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
1608 qs->supports_porter_duff));
1609
1610 return &qs->base;
1611 }
1612
1613 cairo_surface_t *
cairo_qt_surface_create_with_qpixmap(cairo_content_t content,int width,int height)1614 cairo_qt_surface_create_with_qpixmap (cairo_content_t content,
1615 int width,
1616 int height)
1617 {
1618 cairo_qt_surface_t *qs;
1619
1620 if ((content & CAIRO_CONTENT_COLOR) == 0)
1621 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
1622
1623 qs = (cairo_qt_surface_t *) _cairo_malloc (sizeof(cairo_qt_surface_t));
1624 if (qs == NULL)
1625 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1626
1627 memset (qs, 0, sizeof(cairo_qt_surface_t));
1628
1629 QPixmap *pixmap = new QPixmap (width, height);
1630 if (pixmap == NULL) {
1631 free (qs);
1632 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1633 }
1634
1635 // By default, a QPixmap is opaque; however, if it's filled
1636 // with a color with a transparency component, it is converted
1637 // to a format that preserves transparency.
1638 if (content == CAIRO_CONTENT_COLOR_ALPHA)
1639 pixmap->fill(Qt::transparent);
1640
1641 _cairo_surface_init (&qs->base,
1642 &cairo_qt_surface_backend,
1643 NULL, /* device */
1644 content,
1645 FALSE); /* is_vector */
1646
1647 _cairo_surface_clipper_init (&qs->clipper,
1648 _cairo_qt_surface_clipper_intersect_clip_path);
1649
1650 qs->pixmap = pixmap;
1651
1652 if (!pixmap->isNull()) {
1653 qs->p = new QPainter(pixmap);
1654 qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
1655 }
1656
1657 qs->window = QRect(0, 0, width, height);
1658
1659 D(fprintf(stderr, "qpainter_surface_create: qpixmap: [%d %d %d %d] pd:%d\n",
1660 qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
1661 qs->supports_porter_duff));
1662
1663 return &qs->base;
1664 }
1665
1666 /**
1667 * _cairo_surface_is_qt:
1668 * @surface: a #cairo_surface_t
1669 *
1670 * Checks if a surface is a #cairo_qt_surface_t
1671 *
1672 * Return value: True if the surface is an qt surface
1673 **/
1674 static inline cairo_bool_t
_cairo_surface_is_qt(cairo_surface_t * surface)1675 _cairo_surface_is_qt (cairo_surface_t *surface)
1676 {
1677 return surface->backend == &cairo_qt_surface_backend;
1678 }
1679
1680 QPainter *
cairo_qt_surface_get_qpainter(cairo_surface_t * surface)1681 cairo_qt_surface_get_qpainter (cairo_surface_t *surface)
1682 {
1683 cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
1684
1685 /* Throw an error for a non-qt surface */
1686 if (! _cairo_surface_is_qt (surface)) {
1687 _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1688 return NULL;
1689 }
1690
1691 return qs->p;
1692 }
1693
1694 QImage *
cairo_qt_surface_get_qimage(cairo_surface_t * surface)1695 cairo_qt_surface_get_qimage (cairo_surface_t *surface)
1696 {
1697 cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
1698
1699 /* Throw an error for a non-qt surface */
1700 if (! _cairo_surface_is_qt (surface)) {
1701 _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
1702 return NULL;
1703 }
1704
1705 return qs->image;
1706 }
1707
1708 cairo_surface_t *
cairo_qt_surface_get_image(cairo_surface_t * surface)1709 cairo_qt_surface_get_image (cairo_surface_t *surface)
1710 {
1711 cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
1712
1713 /* Throw an error for a non-qt surface */
1714 if (! _cairo_surface_is_qt (surface)) {
1715 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
1716 }
1717
1718 return qs->image_equiv;
1719 }
1720
1721 /*
1722 * TODO:
1723 *
1724 * - Figure out why QBrush isn't working with non-repeated images
1725 *
1726 * - Correct repeat mode; right now, every surface source is EXTEND_REPEAT
1727 * - implement EXTEND_NONE (?? probably need to clip to the extents of the source)
1728 * - implement EXTEND_REFLECT (create temporary and copy 4x, then EXTEND_REPEAT that)
1729 *
1730 * - stroke-image failure
1731 *
1732 * - Implement mask() with non-solid masks (probably will need to use a temporary and use IN)
1733 *
1734 * - Implement gradient sources
1735 *
1736 * - Make create_similar smarter -- create QPixmaps in more circumstances
1737 * (e.g. if the pixmap can have alpha)
1738 *
1739 * - Implement show_glyphs() in terms of Qt
1740 *
1741 */
1742