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