Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 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 |
||
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 |
||
57 | |||
58 | #include |
||
59 | #include |
||
60 | #include |
||
61 | #include |
||
62 | #include |
||
63 | #include |
||
64 | #include |
||
65 | #include |
||
66 | #include |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 | } |
||
310 | |||
311 | return QImage::Format_Mono; |
||
312 | } |
||
313 | |||
314 | static inline QMatrix |
||
315 | _qmatrix_from_cairo_matrix (const cairo_matrix_t& m) |
||
316 | { |
||
317 | return QMatrix(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0); |
||
318 | } |
||
319 | |||
320 | /* Path conversion */ |
||
321 | typedef struct _qpainter_path_transform { |
||
322 | QPainterPath path; |
||
323 | const cairo_matrix_t *ctm_inverse; |
||
324 | } qpainter_path_data; |
||
325 | |||
326 | /* cairo path -> execute in context */ |
||
327 | static cairo_status_t |
||
328 | _cairo_path_to_qpainterpath_move_to (void *closure, const cairo_point_t *point) |
||
329 | { |
||
330 | qpainter_path_data *pdata = static_cast |
||
331 | double x = _cairo_fixed_to_double (point->x); |
||
332 | double y = _cairo_fixed_to_double (point->y); |
||
333 | |||
334 | if (pdata->ctm_inverse) |
||
335 | cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y); |
||
336 | |||
337 | pdata->path.moveTo(x, y); |
||
338 | |||
339 | return CAIRO_STATUS_SUCCESS; |
||
340 | } |
||
341 | |||
342 | static cairo_status_t |
||
343 | _cairo_path_to_qpainterpath_line_to (void *closure, const cairo_point_t *point) |
||
344 | { |
||
345 | qpainter_path_data *pdata = static_cast |
||
346 | double x = _cairo_fixed_to_double (point->x); |
||
347 | double y = _cairo_fixed_to_double (point->y); |
||
348 | |||
349 | if (pdata->ctm_inverse) |
||
350 | cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y); |
||
351 | |||
352 | pdata->path.lineTo(x, y); |
||
353 | |||
354 | return CAIRO_STATUS_SUCCESS; |
||
355 | } |
||
356 | |||
357 | static cairo_status_t |
||
358 | _cairo_path_to_qpainterpath_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2) |
||
359 | { |
||
360 | qpainter_path_data *pdata = static_cast |
||
361 | double x0 = _cairo_fixed_to_double (p0->x); |
||
362 | double y0 = _cairo_fixed_to_double (p0->y); |
||
363 | double x1 = _cairo_fixed_to_double (p1->x); |
||
364 | double y1 = _cairo_fixed_to_double (p1->y); |
||
365 | double x2 = _cairo_fixed_to_double (p2->x); |
||
366 | double y2 = _cairo_fixed_to_double (p2->y); |
||
367 | |||
368 | if (pdata->ctm_inverse) { |
||
369 | cairo_matrix_transform_point (pdata->ctm_inverse, &x0, &y0); |
||
370 | cairo_matrix_transform_point (pdata->ctm_inverse, &x1, &y1); |
||
371 | cairo_matrix_transform_point (pdata->ctm_inverse, &x2, &y2); |
||
372 | } |
||
373 | |||
374 | pdata->path.cubicTo (x0, y0, x1, y1, x2, y2); |
||
375 | |||
376 | return CAIRO_STATUS_SUCCESS; |
||
377 | } |
||
378 | |||
379 | static cairo_status_t |
||
380 | _cairo_path_to_qpainterpath_close_path (void *closure) |
||
381 | { |
||
382 | qpainter_path_data *pdata = static_cast |
||
383 | |||
384 | pdata->path.closeSubpath(); |
||
385 | |||
386 | return CAIRO_STATUS_SUCCESS; |
||
387 | } |
||
388 | |||
389 | static inline QPainterPath |
||
390 | path_to_qt (const cairo_path_fixed_t *path, |
||
391 | const cairo_matrix_t *ctm_inverse = NULL) |
||
392 | { |
||
393 | qpainter_path_data data; |
||
394 | cairo_status_t status; |
||
395 | |||
396 | if (ctm_inverse && _cairo_matrix_is_identity (ctm_inverse)) |
||
397 | ctm_inverse = NULL; |
||
398 | data.ctm_inverse = ctm_inverse; |
||
399 | |||
400 | status = _cairo_path_fixed_interpret (path, |
||
401 | _cairo_path_to_qpainterpath_move_to, |
||
402 | _cairo_path_to_qpainterpath_line_to, |
||
403 | _cairo_path_to_qpainterpath_curve_to, |
||
404 | _cairo_path_to_qpainterpath_close_path, |
||
405 | &data); |
||
406 | assert (status == CAIRO_STATUS_SUCCESS); |
||
407 | |||
408 | return data.path; |
||
409 | } |
||
410 | |||
411 | static inline QPainterPath |
||
412 | path_to_qt (const cairo_path_fixed_t *path, |
||
413 | cairo_fill_rule_t fill_rule, |
||
414 | cairo_matrix_t *ctm_inverse = NULL) |
||
415 | { |
||
416 | QPainterPath qpath = path_to_qt (path, ctm_inverse); |
||
417 | |||
418 | qpath.setFillRule (fill_rule == CAIRO_FILL_RULE_WINDING ? |
||
419 | Qt::WindingFill : |
||
420 | Qt::OddEvenFill); |
||
421 | |||
422 | return qpath; |
||
423 | } |
||
424 | |||
425 | /* |
||
426 | * Surface backend methods |
||
427 | */ |
||
428 | static cairo_surface_t * |
||
429 | _cairo_qt_surface_create_similar (void *abstract_surface, |
||
430 | cairo_content_t content, |
||
431 | int width, |
||
432 | int height) |
||
433 | { |
||
434 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
435 | bool use_pixmap; |
||
436 | |||
437 | D(fprintf(stderr, "q[%p] create_similar: %d %d [%d] -> ", abstract_surface, width, height, content)); |
||
438 | |||
439 | use_pixmap = qs->image == NULL; |
||
440 | if (use_pixmap) { |
||
441 | switch (content) { |
||
442 | case CAIRO_CONTENT_ALPHA: |
||
443 | use_pixmap = FALSE; |
||
444 | break; |
||
445 | case CAIRO_CONTENT_COLOR: |
||
446 | break; |
||
447 | case CAIRO_CONTENT_COLOR_ALPHA: |
||
448 | use_pixmap = ! _qpixmaps_have_no_alpha; |
||
449 | break; |
||
450 | } |
||
451 | } |
||
452 | |||
453 | if (use_pixmap) { |
||
454 | cairo_surface_t *result = |
||
455 | cairo_qt_surface_create_with_qpixmap (content, width, height); |
||
456 | |||
457 | /* XXX result->content is always content. ??? */ |
||
458 | if (result->content == content) { |
||
459 | D(fprintf(stderr, "qpixmap content: %d\n", content)); |
||
460 | return result; |
||
461 | } |
||
462 | |||
463 | _qpixmaps_have_no_alpha = TRUE; |
||
464 | cairo_surface_destroy (result); |
||
465 | } |
||
466 | |||
467 | D(fprintf (stderr, "qimage\n")); |
||
468 | return cairo_qt_surface_create_with_qimage |
||
469 | (_cairo_format_from_content (content), width, height); |
||
470 | } |
||
471 | |||
472 | static cairo_status_t |
||
473 | _cairo_qt_surface_finish (void *abstract_surface) |
||
474 | { |
||
475 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
476 | |||
477 | D(fprintf(stderr, "q[%p] finish\n", abstract_surface)); |
||
478 | |||
479 | /* Only delete p if we created it */ |
||
480 | if (qs->image || qs->pixmap) |
||
481 | delete qs->p; |
||
482 | else |
||
483 | qs->p->restore (); |
||
484 | |||
485 | if (qs->image_equiv) |
||
486 | cairo_surface_destroy (qs->image_equiv); |
||
487 | |||
488 | _cairo_surface_clipper_reset (&qs->clipper); |
||
489 | |||
490 | if (qs->image) |
||
491 | delete qs->image; |
||
492 | |||
493 | if (qs->pixmap) |
||
494 | delete qs->pixmap; |
||
495 | |||
496 | return CAIRO_STATUS_SUCCESS; |
||
497 | } |
||
498 | |||
499 | static void |
||
500 | _qimg_destroy (void *closure) |
||
501 | { |
||
502 | QImage *qimg = (QImage *) closure; |
||
503 | delete qimg; |
||
504 | } |
||
505 | |||
506 | static cairo_status_t |
||
507 | _cairo_qt_surface_acquire_source_image (void *abstract_surface, |
||
508 | cairo_image_surface_t **image_out, |
||
509 | void **image_extra) |
||
510 | { |
||
511 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
512 | |||
513 | D(fprintf(stderr, "q[%p] acquire_source_image\n", abstract_surface)); |
||
514 | |||
515 | *image_extra = NULL; |
||
516 | |||
517 | if (qs->image_equiv) { |
||
518 | *image_out = (cairo_image_surface_t*) |
||
519 | cairo_surface_reference (qs->image_equiv); |
||
520 | |||
521 | return CAIRO_STATUS_SUCCESS; |
||
522 | } |
||
523 | |||
524 | if (qs->pixmap) { |
||
525 | QImage *qimg = new QImage(qs->pixmap->toImage()); |
||
526 | cairo_surface_t *image; |
||
527 | cairo_status_t status; |
||
528 | |||
529 | image = cairo_image_surface_create_for_data (qimg->bits(), |
||
530 | _cairo_format_from_qimage_format (qimg->format()), |
||
531 | qimg->width(), qimg->height(), |
||
532 | qimg->bytesPerLine()); |
||
533 | |||
534 | status = _cairo_user_data_array_set_data (&image->user_data, |
||
535 | (const cairo_user_data_key_t *)&_qimg_destroy, |
||
536 | qimg, |
||
537 | _qimg_destroy); |
||
538 | if (status) { |
||
539 | cairo_surface_destroy (image); |
||
540 | return status; |
||
541 | } |
||
542 | |||
543 | *image_out = (cairo_image_surface_t *) image; |
||
544 | return CAIRO_STATUS_SUCCESS; |
||
545 | } |
||
546 | |||
547 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
548 | } |
||
549 | |||
550 | static void |
||
551 | _cairo_qt_surface_release_source_image (void *abstract_surface, |
||
552 | cairo_image_surface_t *image, |
||
553 | void *image_extra) |
||
554 | { |
||
555 | //cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
556 | |||
557 | D(fprintf(stderr, "q[%p] release_source_image\n", abstract_surface)); |
||
558 | |||
559 | cairo_surface_destroy (&image->base); |
||
560 | } |
||
561 | |||
562 | struct _qimage_surface { |
||
563 | cairo_image_surface_t image; |
||
564 | QImage *qimg; |
||
565 | }; |
||
566 | |||
567 | static cairo_surface_t * |
||
568 | map_qimage_to_image (QImage *qimg, const cairo_rectangle_int_t *extents) |
||
569 | { |
||
570 | struct _qimage_surface *surface; |
||
571 | pixman_image_t *pixman_image; |
||
572 | pixman_format_code_t pixman_format; |
||
573 | uint8_t *data; |
||
574 | |||
575 | if (qimg == NULL) |
||
576 | return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); |
||
577 | |||
578 | switch (qimg->format()) { |
||
579 | case QImage::Format_ARGB32_Premultiplied: |
||
580 | pixman_format = PIXMAN_a8r8g8b8; |
||
581 | break; |
||
582 | case QImage::Format_RGB32: |
||
583 | pixman_format = PIXMAN_x8r8g8b8; |
||
584 | break; |
||
585 | case QImage::Format_Indexed8: // XXX not quite |
||
586 | pixman_format = PIXMAN_a8; |
||
587 | break; |
||
588 | #ifdef WORDS_BIGENDIAN |
||
589 | case QImage::Format_Mono: |
||
590 | #else |
||
591 | case QImage::Format_MonoLSB: |
||
592 | #endif |
||
593 | pixman_format = PIXMAN_a1; |
||
594 | break; |
||
595 | |||
596 | case QImage::Format_Invalid: |
||
597 | #ifdef WORDS_BIGENDIAN |
||
598 | case QImage::Format_MonoLSB: |
||
599 | #else |
||
600 | case QImage::Format_Mono: |
||
601 | #endif |
||
602 | case QImage::Format_ARGB32: |
||
603 | case QImage::Format_RGB16: |
||
604 | case QImage::Format_ARGB8565_Premultiplied: |
||
605 | case QImage::Format_RGB666: |
||
606 | case QImage::Format_ARGB6666_Premultiplied: |
||
607 | case QImage::Format_RGB555: |
||
608 | case QImage::Format_ARGB8555_Premultiplied: |
||
609 | case QImage::Format_RGB888: |
||
610 | case QImage::Format_RGB444: |
||
611 | case QImage::Format_ARGB4444_Premultiplied: |
||
612 | case QImage::NImageFormats: |
||
613 | default: |
||
614 | delete qimg; |
||
615 | return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT); |
||
616 | } |
||
617 | |||
618 | data = qimg->bits(); |
||
619 | data += extents->y * qimg->bytesPerLine(); |
||
620 | data += extents->x * PIXMAN_FORMAT_BPP (pixman_format) / 8; |
||
621 | |||
622 | pixman_image = pixman_image_create_bits (pixman_format, |
||
623 | extents->width, |
||
624 | extents->height, |
||
625 | (uint32_t *)data, |
||
626 | qimg->bytesPerLine()); |
||
627 | if (pixman_image == NULL) { |
||
628 | delete qimg; |
||
629 | return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); |
||
630 | } |
||
631 | |||
632 | surface = (struct _qimage_surface *) malloc (sizeof(*surface)); |
||
633 | if (unlikely (surface == NULL)) { |
||
634 | pixman_image_unref (pixman_image); |
||
635 | delete qimg; |
||
636 | return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); |
||
637 | } |
||
638 | |||
639 | _cairo_image_surface_init (&surface->image, pixman_image, pixman_format); |
||
640 | surface->qimg = qimg; |
||
641 | |||
642 | cairo_surface_set_device_offset (&surface->image.base, |
||
643 | -extents->x, -extents->y); |
||
644 | |||
645 | return &surface->image.base; |
||
646 | } |
||
647 | |||
648 | static cairo_image_surface_t * |
||
649 | _cairo_qt_surface_map_to_image (void *abstract_surface, |
||
650 | const cairo_rectangle_int_t *extents) |
||
651 | { |
||
652 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
653 | QImage *qimg = NULL; |
||
654 | |||
655 | D(fprintf(stderr, "q[%p] acquire_dest_image\n", abstract_surface)); |
||
656 | |||
657 | if (qs->image_equiv) |
||
658 | return _cairo_image_surface_map_to_image (qs->image_equiv, |
||
659 | extents); |
||
660 | |||
661 | QPoint offset; |
||
662 | |||
663 | if (qs->pixmap) { |
||
664 | qimg = new QImage(qs->pixmap->toImage()); |
||
665 | } else { |
||
666 | // Try to figure out what kind of QPaintDevice we have, and |
||
667 | // how we can grab an image from it |
||
668 | QPaintDevice *pd = qs->p->device(); |
||
669 | if (!pd) |
||
670 | return (cairo_image_surface_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); |
||
671 | |||
672 | QPaintDevice *rpd = QPainter::redirected(pd, &offset); |
||
673 | if (rpd) |
||
674 | pd = rpd; |
||
675 | |||
676 | if (pd->devType() == QInternal::Image) { |
||
677 | qimg = new QImage(((QImage*) pd)->copy()); |
||
678 | } else if (pd->devType() == QInternal::Pixmap) { |
||
679 | qimg = new QImage(((QPixmap*) pd)->toImage()); |
||
680 | } else if (pd->devType() == QInternal::Widget) { |
||
681 | qimg = new QImage(QPixmap::grabWindow(((QWidget*)pd)->winId()).toImage()); |
||
682 | } |
||
683 | } |
||
684 | |||
685 | return (cairo_image_surface_t *) map_qimage_to_image (qimg, extents); |
||
686 | } |
||
687 | |||
688 | static cairo_int_status_t |
||
689 | _cairo_qt_surface_unmap_image (void *abstract_surface, |
||
690 | cairo_image_surface_t *image) |
||
691 | { |
||
692 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
693 | |||
694 | D(fprintf(stderr, "q[%p] release_dest_image\n", abstract_surface)); |
||
695 | |||
696 | if (!qs->image_equiv) { |
||
697 | struct _qimage_surface *qimage = (struct _qimage_surface *)image; |
||
698 | |||
699 | // XXX should I be using setBackgroundMode here instead of setCompositionMode? |
||
700 | if (qs->supports_porter_duff) |
||
701 | qs->p->setCompositionMode (QPainter::CompositionMode_Source); |
||
702 | |||
703 | qs->p->drawImage ((int)qimage->image.base.device_transform.x0, |
||
704 | (int)qimage->image.base.device_transform.y0, |
||
705 | *qimage->qimg, |
||
706 | (int)qimage->image.base.device_transform.x0, |
||
707 | (int)qimage->image.base.device_transform.y0, |
||
708 | (int)qimage->image.width, |
||
709 | (int)qimage->image.height); |
||
710 | |||
711 | if (qs->supports_porter_duff) |
||
712 | qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); |
||
713 | |||
714 | delete qimage->qimg; |
||
715 | } |
||
716 | |||
717 | cairo_surface_finish (&image->base); |
||
718 | cairo_surface_destroy (&image->base); |
||
719 | |||
720 | return CAIRO_INT_STATUS_SUCCESS; |
||
721 | } |
||
722 | |||
723 | static cairo_bool_t |
||
724 | _cairo_qt_surface_get_extents (void *abstract_surface, |
||
725 | cairo_rectangle_int_t *extents) |
||
726 | { |
||
727 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
728 | |||
729 | extents->x = qs->window.x(); |
||
730 | extents->y = qs->window.y(); |
||
731 | extents->width = qs->window.width(); |
||
732 | extents->height = qs->window.height(); |
||
733 | |||
734 | return TRUE; |
||
735 | } |
||
736 | |||
737 | static cairo_status_t |
||
738 | _cairo_qt_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, |
||
739 | cairo_path_fixed_t *path, |
||
740 | cairo_fill_rule_t fill_rule, |
||
741 | double tolerance, |
||
742 | cairo_antialias_t antialias) |
||
743 | { |
||
744 | cairo_qt_surface_t *qs = cairo_container_of (clipper, |
||
745 | cairo_qt_surface_t, |
||
746 | clipper); |
||
747 | |||
748 | if (path == NULL) { |
||
749 | if (qs->pixmap || qs->image) { |
||
750 | // we own p |
||
751 | qs->p->setClipping (false); |
||
752 | } else { |
||
753 | qs->p->restore (); |
||
754 | qs->p->save (); |
||
755 | } |
||
756 | } else { |
||
757 | // XXX Antialiasing is ignored |
||
758 | qs->p->setClipPath (path_to_qt (path, fill_rule), Qt::IntersectClip); |
||
759 | } |
||
760 | |||
761 | return CAIRO_STATUS_SUCCESS; |
||
762 | } |
||
763 | |||
764 | static void |
||
765 | _cairo_qt_surface_set_clip_region (cairo_qt_surface_t *qs, |
||
766 | const cairo_region_t *clip_region) |
||
767 | { |
||
768 | _cairo_surface_clipper_reset (&qs->clipper); |
||
769 | |||
770 | if (clip_region == NULL) { |
||
771 | // How the clip path is reset depends on whether we own p or not |
||
772 | if (qs->pixmap || qs->image) { |
||
773 | // we own p |
||
774 | qs->p->setClipping (false); |
||
775 | } else { |
||
776 | qs->p->restore (); |
||
777 | qs->p->save (); |
||
778 | } |
||
779 | } else { |
||
780 | QRegion qr; |
||
781 | int num_rects = cairo_region_num_rectangles (clip_region); |
||
782 | for (int i = 0; i < num_rects; ++i) { |
||
783 | cairo_rectangle_int_t rect; |
||
784 | |||
785 | cairo_region_get_rectangle (clip_region, i, &rect); |
||
786 | |||
787 | QRect r(rect.x, rect.y, rect.width, rect.height); |
||
788 | qr = qr.unite(r); |
||
789 | } |
||
790 | |||
791 | qs->p->setClipRegion (qr, Qt::IntersectClip); |
||
792 | } |
||
793 | } |
||
794 | |||
795 | static cairo_int_status_t |
||
796 | _cairo_qt_surface_set_clip (cairo_qt_surface_t *qs, |
||
797 | const cairo_clip_t *clip) |
||
798 | { |
||
799 | cairo_int_status_t status; |
||
800 | |||
801 | D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", abstract_surface, path ? "(path)" : "(clear)")); |
||
802 | |||
803 | if (clip == NULL) { |
||
804 | _cairo_surface_clipper_reset (&qs->clipper); |
||
805 | // How the clip path is reset depends on whether we own p or not |
||
806 | if (qs->pixmap || qs->image) { |
||
807 | // we own p |
||
808 | qs->p->setClipping (false); |
||
809 | } else { |
||
810 | qs->p->restore (); |
||
811 | qs->p->save (); |
||
812 | } |
||
813 | |||
814 | return CAIRO_INT_STATUS_SUCCESS; |
||
815 | } |
||
816 | |||
817 | #if ENABLE_FAST_CLIP |
||
818 | // Qt will implicitly enable clipping, and will use ReplaceClip |
||
819 | // instead of IntersectClip if clipping was disabled before |
||
820 | |||
821 | // Note: Qt is really bad at dealing with clip paths. It doesn't |
||
822 | // seem to usefully recognize rectangular paths, instead going down |
||
823 | // extremely slow paths whenever a clip path is set. So, |
||
824 | // we do a bunch of work here to try to get rectangles or regions |
||
825 | // down to Qt for clipping. |
||
826 | |||
827 | cairo_region_t *clip_region = NULL; |
||
828 | |||
829 | status = _cairo_clip_get_region (clip, &clip_region); |
||
830 | if (status == CAIRO_INT_STATUS_UNSUPPORTED) { |
||
831 | // We weren't able to extract a region from the traps. |
||
832 | // Just hand the path down to QPainter. |
||
833 | status = (cairo_int_status_t) |
||
834 | _cairo_surface_clipper_set_clip (&qs->clipper, clip); |
||
835 | } else if (status == CAIRO_INT_STATUS_SUCCESS) { |
||
836 | _cairo_qt_surface_set_clip_region (qs, clip_region); |
||
837 | status = CAIRO_INT_STATUS_SUCCESS; |
||
838 | } |
||
839 | #else |
||
840 | status = (cairo_int_status_t) |
||
841 | _cairo_surface_clipper_set_clip (&qs->clipper, clip); |
||
842 | #endif |
||
843 | |||
844 | return status; |
||
845 | } |
||
846 | |||
847 | /* |
||
848 | * Brush conversion |
||
849 | */ |
||
850 | |||
851 | struct PatternToBrushConverter { |
||
852 | PatternToBrushConverter (const cairo_pattern_t *pattern) : |
||
853 | mAcquiredImageParent(0), |
||
854 | mAcquiredImage(0), |
||
855 | mAcquiredImageExtra(0) |
||
856 | { |
||
857 | if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { |
||
858 | cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; |
||
859 | QColor color; |
||
860 | color.setRgbF(solid->color.red, |
||
861 | solid->color.green, |
||
862 | solid->color.blue, |
||
863 | solid->color.alpha); |
||
864 | |||
865 | mBrush = QBrush(color); |
||
866 | } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { |
||
867 | cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern; |
||
868 | cairo_surface_t *surface = spattern->surface; |
||
869 | |||
870 | if (surface->type == CAIRO_SURFACE_TYPE_QT) { |
||
871 | cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; |
||
872 | |||
873 | if (qs->image) { |
||
874 | mBrush = QBrush(*qs->image); |
||
875 | } else if (qs->pixmap) { |
||
876 | mBrush = QBrush(*qs->pixmap); |
||
877 | } else { |
||
878 | // do something smart |
||
879 | mBrush = QBrush(0xff0000ff); |
||
880 | } |
||
881 | } else { |
||
882 | cairo_image_surface_t *isurf = NULL; |
||
883 | |||
884 | if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) { |
||
885 | isurf = (cairo_image_surface_t*) surface; |
||
886 | } else { |
||
887 | void *image_extra; |
||
888 | |||
889 | if (_cairo_surface_acquire_source_image (surface, &isurf, &image_extra) == CAIRO_STATUS_SUCCESS) { |
||
890 | mAcquiredImageParent = surface; |
||
891 | mAcquiredImage = isurf; |
||
892 | mAcquiredImageExtra = image_extra; |
||
893 | } else { |
||
894 | isurf = NULL; |
||
895 | } |
||
896 | } |
||
897 | |||
898 | if (isurf) { |
||
899 | mBrush = QBrush (QImage ((const uchar *) isurf->data, |
||
900 | isurf->width, |
||
901 | isurf->height, |
||
902 | isurf->stride, |
||
903 | _qimage_format_from_cairo_format (isurf->format))); |
||
904 | } else { |
||
905 | mBrush = QBrush(0x0000ffff); |
||
906 | } |
||
907 | } |
||
908 | } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || |
||
909 | pattern->type == CAIRO_PATTERN_TYPE_RADIAL) |
||
910 | { |
||
911 | QGradient *grad; |
||
912 | cairo_bool_t reverse_stops = FALSE; |
||
913 | cairo_bool_t emulate_reflect = FALSE; |
||
914 | double offset = 0.0; |
||
915 | |||
916 | cairo_extend_t extend = pattern->extend; |
||
917 | |||
918 | cairo_gradient_pattern_t *gpat = (cairo_gradient_pattern_t *) pattern; |
||
919 | |||
920 | if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { |
||
921 | cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *) pattern; |
||
922 | grad = new QLinearGradient (lpat->pd1.x, lpat->pd1.y, |
||
923 | lpat->pd2.x, lpat->pd2.y); |
||
924 | } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { |
||
925 | cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *) pattern; |
||
926 | |||
927 | /* Based on the SVG surface code */ |
||
928 | |||
929 | cairo_circle_double_t *c0, *c1; |
||
930 | double x0, y0, r0, x1, y1, r1; |
||
931 | |||
932 | if (rpat->cd1.radius < rpat->cd2.radius) { |
||
933 | c0 = &rpat->cd1; |
||
934 | c1 = &rpat->cd2; |
||
935 | reverse_stops = FALSE; |
||
936 | } else { |
||
937 | c0 = &rpat->cd2; |
||
938 | c1 = &rpat->cd1; |
||
939 | reverse_stops = TRUE; |
||
940 | } |
||
941 | |||
942 | x0 = c0->center.x; |
||
943 | y0 = c0->center.y; |
||
944 | r0 = c0->radius; |
||
945 | x1 = c1->center.x; |
||
946 | y1 = c1->center.y; |
||
947 | r1 = c1->radius; |
||
948 | |||
949 | if (r0 == r1) { |
||
950 | grad = new QRadialGradient (x1, y1, r1, x1, y1); |
||
951 | } else { |
||
952 | double fx = (r1 * x0 - r0 * x1) / (r1 - r0); |
||
953 | double fy = (r1 * y0 - r0 * y1) / (r1 - r0); |
||
954 | |||
955 | /* QPainter doesn't support the inner circle and use instead a gradient focal. |
||
956 | * That means we need to emulate the cairo behaviour by processing the |
||
957 | * cairo gradient stops. |
||
958 | * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle, |
||
959 | * it's just a matter of stop position translation and calculation of |
||
960 | * the corresponding SVG radial gradient focal. |
||
961 | * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new |
||
962 | * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT |
||
963 | * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop |
||
964 | * list that maps to the original cairo stop list. |
||
965 | */ |
||
966 | if ((extend == CAIRO_EXTEND_REFLECT || extend == CAIRO_EXTEND_REPEAT) && r0 > 0.0) { |
||
967 | double r_org = r1; |
||
968 | double r, x, y; |
||
969 | |||
970 | if (extend == CAIRO_EXTEND_REFLECT) { |
||
971 | r1 = 2 * r1 - r0; |
||
972 | emulate_reflect = TRUE; |
||
973 | } |
||
974 | |||
975 | offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0; |
||
976 | r = r1 - r0; |
||
977 | |||
978 | /* New position of outer circle. */ |
||
979 | x = r * (x1 - fx) / r_org + fx; |
||
980 | y = r * (y1 - fy) / r_org + fy; |
||
981 | |||
982 | x1 = x; |
||
983 | y1 = y; |
||
984 | r1 = r; |
||
985 | r0 = 0.0; |
||
986 | } else { |
||
987 | offset = r0 / r1; |
||
988 | } |
||
989 | |||
990 | grad = new QRadialGradient (x1, y1, r1, fx, fy); |
||
991 | |||
992 | if (extend == CAIRO_EXTEND_NONE && r0 != 0.0) |
||
993 | grad->setColorAt (r0 / r1, Qt::transparent); |
||
994 | } |
||
995 | } |
||
996 | |||
997 | switch (extend) { |
||
998 | case CAIRO_EXTEND_NONE: |
||
999 | case CAIRO_EXTEND_PAD: |
||
1000 | grad->setSpread(QGradient::PadSpread); |
||
1001 | |||
1002 | grad->setColorAt (0.0, Qt::transparent); |
||
1003 | grad->setColorAt (1.0, Qt::transparent); |
||
1004 | break; |
||
1005 | |||
1006 | case CAIRO_EXTEND_REFLECT: |
||
1007 | grad->setSpread(QGradient::ReflectSpread); |
||
1008 | break; |
||
1009 | |||
1010 | case CAIRO_EXTEND_REPEAT: |
||
1011 | grad->setSpread(QGradient::RepeatSpread); |
||
1012 | break; |
||
1013 | } |
||
1014 | |||
1015 | for (unsigned int i = 0; i < gpat->n_stops; i++) { |
||
1016 | int index = i; |
||
1017 | if (reverse_stops) |
||
1018 | index = gpat->n_stops - i - 1; |
||
1019 | |||
1020 | double offset = gpat->stops[i].offset; |
||
1021 | QColor color; |
||
1022 | color.setRgbF (gpat->stops[i].color.red, |
||
1023 | gpat->stops[i].color.green, |
||
1024 | gpat->stops[i].color.blue, |
||
1025 | gpat->stops[i].color.alpha); |
||
1026 | |||
1027 | if (emulate_reflect) { |
||
1028 | offset = offset / 2.0; |
||
1029 | grad->setColorAt (1.0 - offset, color); |
||
1030 | } |
||
1031 | |||
1032 | grad->setColorAt (offset, color); |
||
1033 | } |
||
1034 | |||
1035 | mBrush = QBrush(*grad); |
||
1036 | |||
1037 | delete grad; |
||
1038 | } |
||
1039 | |||
1040 | if (mBrush.style() != Qt::NoBrush && |
||
1041 | pattern->type != CAIRO_PATTERN_TYPE_SOLID && |
||
1042 | ! _cairo_matrix_is_identity (&pattern->matrix)) |
||
1043 | { |
||
1044 | cairo_matrix_t pm = pattern->matrix; |
||
1045 | cairo_status_t status = cairo_matrix_invert (&pm); |
||
1046 | assert (status == CAIRO_STATUS_SUCCESS); |
||
1047 | mBrush.setMatrix (_qmatrix_from_cairo_matrix (pm)); |
||
1048 | } |
||
1049 | } |
||
1050 | |||
1051 | ~PatternToBrushConverter () { |
||
1052 | if (mAcquiredImageParent) |
||
1053 | _cairo_surface_release_source_image (mAcquiredImageParent, mAcquiredImage, mAcquiredImageExtra); |
||
1054 | } |
||
1055 | |||
1056 | operator QBrush& () { |
||
1057 | return mBrush; |
||
1058 | } |
||
1059 | |||
1060 | QBrush mBrush; |
||
1061 | |||
1062 | private: |
||
1063 | cairo_surface_t *mAcquiredImageParent; |
||
1064 | cairo_image_surface_t *mAcquiredImage; |
||
1065 | void *mAcquiredImageExtra; |
||
1066 | }; |
||
1067 | |||
1068 | struct PatternToPenConverter { |
||
1069 | PatternToPenConverter (const cairo_pattern_t *source, |
||
1070 | const cairo_stroke_style_t *style) : |
||
1071 | mBrushConverter(source) |
||
1072 | { |
||
1073 | Qt::PenJoinStyle join = Qt::MiterJoin; |
||
1074 | Qt::PenCapStyle cap = Qt::SquareCap; |
||
1075 | |||
1076 | switch (style->line_cap) { |
||
1077 | case CAIRO_LINE_CAP_BUTT: |
||
1078 | cap = Qt::FlatCap; |
||
1079 | break; |
||
1080 | case CAIRO_LINE_CAP_ROUND: |
||
1081 | cap = Qt::RoundCap; |
||
1082 | break; |
||
1083 | case CAIRO_LINE_CAP_SQUARE: |
||
1084 | cap = Qt::SquareCap; |
||
1085 | break; |
||
1086 | } |
||
1087 | |||
1088 | switch (style->line_join) { |
||
1089 | case CAIRO_LINE_JOIN_MITER: |
||
1090 | join = Qt::MiterJoin; |
||
1091 | break; |
||
1092 | case CAIRO_LINE_JOIN_ROUND: |
||
1093 | join = Qt::RoundJoin; |
||
1094 | break; |
||
1095 | case CAIRO_LINE_JOIN_BEVEL: |
||
1096 | join = Qt::BevelJoin; |
||
1097 | break; |
||
1098 | } |
||
1099 | |||
1100 | mPen = QPen(mBrushConverter, style->line_width, Qt::SolidLine, cap, join); |
||
1101 | mPen.setMiterLimit (style->miter_limit); |
||
1102 | |||
1103 | if (style->dash && style->num_dashes) { |
||
1104 | Qt::PenStyle pstyle = Qt::NoPen; |
||
1105 | |||
1106 | if (style->num_dashes == 2) { |
||
1107 | if ((style->dash[0] == style->line_width && |
||
1108 | style->dash[1] == style->line_width && style->line_width <= 2.0) || |
||
1109 | (style->dash[0] == 0.0 && |
||
1110 | style->dash[1] == style->line_width * 2 && cap == Qt::RoundCap)) |
||
1111 | { |
||
1112 | pstyle = Qt::DotLine; |
||
1113 | } else if (style->dash[0] == style->line_width * DASH_LENGTH && |
||
1114 | style->dash[1] == style->line_width * DASH_LENGTH && |
||
1115 | cap == Qt::FlatCap) |
||
1116 | { |
||
1117 | pstyle = Qt::DashLine; |
||
1118 | } |
||
1119 | } |
||
1120 | |||
1121 | if (pstyle != Qt::NoPen) { |
||
1122 | mPen.setStyle(pstyle); |
||
1123 | return; |
||
1124 | } |
||
1125 | |||
1126 | unsigned int odd_dash = style->num_dashes % 2; |
||
1127 | |||
1128 | QVector |
||
1129 | for (unsigned int i = 0; i < odd_dash+1; i++) { |
||
1130 | for (unsigned int j = 0; j < style->num_dashes; j++) { |
||
1131 | // In Qt, the dash lengths are given in units of line width, whereas |
||
1132 | // in cairo, they are in user-space units. We'll always apply the CTM, |
||
1133 | // so all we have to do here is divide cairo's dash lengths by the line |
||
1134 | // width. |
||
1135 | dashes.append (style->dash[j] / style->line_width); |
||
1136 | } |
||
1137 | } |
||
1138 | |||
1139 | mPen.setDashPattern(dashes); |
||
1140 | mPen.setDashOffset(style->dash_offset / style->line_width); |
||
1141 | } |
||
1142 | } |
||
1143 | |||
1144 | ~PatternToPenConverter() { } |
||
1145 | |||
1146 | operator QPen& () { |
||
1147 | return mPen; |
||
1148 | } |
||
1149 | |||
1150 | QPen mPen; |
||
1151 | PatternToBrushConverter mBrushConverter; |
||
1152 | }; |
||
1153 | |||
1154 | /* |
||
1155 | * Core drawing operations |
||
1156 | */ |
||
1157 | |||
1158 | static bool |
||
1159 | _cairo_qt_fast_fill (cairo_qt_surface_t *qs, |
||
1160 | const cairo_pattern_t *source, |
||
1161 | const cairo_path_fixed_t *path = NULL, |
||
1162 | cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING, |
||
1163 | double tolerance = 0.0, |
||
1164 | cairo_antialias_t antialias = CAIRO_ANTIALIAS_NONE) |
||
1165 | { |
||
1166 | #if ENABLE_FAST_FILL |
||
1167 | QImage *qsSrc_image = NULL; |
||
1168 | QPixmap *qsSrc_pixmap = NULL; |
||
1169 | std::auto_ptr |
||
1170 | |||
1171 | |||
1172 | if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { |
||
1173 | cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) source; |
||
1174 | if (spattern->surface->type == CAIRO_SURFACE_TYPE_QT) { |
||
1175 | cairo_qt_surface_t *p = (cairo_qt_surface_t*) spattern->surface; |
||
1176 | |||
1177 | qsSrc_image = p->image; |
||
1178 | qsSrc_pixmap = p->pixmap; |
||
1179 | } else if (spattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { |
||
1180 | cairo_image_surface_t *p = (cairo_image_surface_t*) spattern->surface; |
||
1181 | qsSrc_image = new QImage((const uchar*) p->data, |
||
1182 | p->width, |
||
1183 | p->height, |
||
1184 | p->stride, |
||
1185 | _qimage_format_from_cairo_format(p->format)); |
||
1186 | qsSrc_image_d.reset(qsSrc_image); |
||
1187 | } |
||
1188 | } |
||
1189 | |||
1190 | if (!qsSrc_image && !qsSrc_pixmap) |
||
1191 | return false; |
||
1192 | |||
1193 | // We can only drawTiledPixmap; there's no drawTiledImage |
||
1194 | if (! qsSrc_pixmap && |
||
1195 | (source->extend == CAIRO_EXTEND_REPEAT || |
||
1196 | source->extend == CAIRO_EXTEND_REFLECT)) |
||
1197 | { |
||
1198 | return false; |
||
1199 | } |
||
1200 | |||
1201 | QMatrix sourceMatrix = _qmatrix_from_cairo_matrix (source->matrix); |
||
1202 | |||
1203 | // We can draw this faster by clipping and calling drawImage/drawPixmap. |
||
1204 | // Use our own clipping function so that we can get the |
||
1205 | // region handling to end up with the fastest possible clip. |
||
1206 | // |
||
1207 | // XXX Antialiasing will fail pretty hard here, since we can't clip with AA |
||
1208 | // with QPainter. |
||
1209 | qs->p->save(); |
||
1210 | |||
1211 | if (path) { |
||
1212 | cairo_int_status_t status; |
||
1213 | |||
1214 | cairo_clip_t clip, old_clip = qs->clipper.clip; |
||
1215 | |||
1216 | _cairo_clip_init_copy (&clip, &qs->clipper.clip); |
||
1217 | status = (cairo_int_status_t) _cairo_clip_clip (&clip, |
||
1218 | path, |
||
1219 | fill_rule, |
||
1220 | tolerance, |
||
1221 | antialias); |
||
1222 | if (unlikely (status)) { |
||
1223 | qs->p->restore(); |
||
1224 | return false; |
||
1225 | } |
||
1226 | |||
1227 | status = _cairo_qt_surface_set_clip (qs, &clip); |
||
1228 | if (unlikely (status)) { |
||
1229 | qs->p->restore(); |
||
1230 | return false; |
||
1231 | } |
||
1232 | |||
1233 | _cairo_clip_reset (&clip); |
||
1234 | qs->clipper.clip = old_clip; |
||
1235 | } |
||
1236 | |||
1237 | qs->p->setWorldMatrix (sourceMatrix.inverted(), true); |
||
1238 | |||
1239 | switch (source->extend) { |
||
1240 | case CAIRO_EXTEND_REPEAT: |
||
1241 | // XXX handle reflect by tiling 4 times first |
||
1242 | case CAIRO_EXTEND_REFLECT: { |
||
1243 | assert (qsSrc_pixmap); |
||
1244 | |||
1245 | // Render the tiling to cover the entire destination window (because |
||
1246 | // it'll be clipped). Transform the window rect by the inverse |
||
1247 | // of the current world transform so that the device coordinates |
||
1248 | // end up as the right thing. |
||
1249 | QRectF dest = qs->p->worldTransform().inverted().mapRect(QRectF(qs->window)); |
||
1250 | QPointF origin = sourceMatrix.map(QPointF(0.0, 0.0)); |
||
1251 | |||
1252 | qs->p->drawTiledPixmap (dest, *qsSrc_pixmap, origin); |
||
1253 | } |
||
1254 | break; |
||
1255 | case CAIRO_EXTEND_NONE: |
||
1256 | case CAIRO_EXTEND_PAD: // XXX not exactly right, but good enough |
||
1257 | default: |
||
1258 | if (qsSrc_image) |
||
1259 | qs->p->drawImage (0, 0, *qsSrc_image); |
||
1260 | else if (qsSrc_pixmap) |
||
1261 | qs->p->drawPixmap (0, 0, *qsSrc_pixmap); |
||
1262 | break; |
||
1263 | } |
||
1264 | |||
1265 | qs->p->restore(); |
||
1266 | |||
1267 | return true; |
||
1268 | #else |
||
1269 | return false; |
||
1270 | #endif |
||
1271 | } |
||
1272 | |||
1273 | static cairo_int_status_t |
||
1274 | _cairo_qt_surface_paint (void *abstract_surface, |
||
1275 | cairo_operator_t op, |
||
1276 | const cairo_pattern_t *source, |
||
1277 | const cairo_clip_t *clip) |
||
1278 | { |
||
1279 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
1280 | cairo_int_status_t status; |
||
1281 | |||
1282 | D(fprintf(stderr, "q[%p] paint op:%s\n", abstract_surface, _opstr(op))); |
||
1283 | |||
1284 | if (! _op_is_supported (qs, op)) |
||
1285 | return _cairo_surface_fallback_paint (abstract_surface, op, source, clip); |
||
1286 | |||
1287 | status = _cairo_qt_surface_set_clip (qs, clip); |
||
1288 | if (unlikely (status)) |
||
1289 | return status; |
||
1290 | |||
1291 | if (qs->supports_porter_duff) |
||
1292 | qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); |
||
1293 | |||
1294 | if (! _cairo_qt_fast_fill (qs, source)) { |
||
1295 | PatternToBrushConverter brush (source); |
||
1296 | qs->p->fillRect (qs->window, brush); |
||
1297 | } |
||
1298 | |||
1299 | if (qs->supports_porter_duff) |
||
1300 | qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); |
||
1301 | |||
1302 | return CAIRO_INT_STATUS_SUCCESS; |
||
1303 | } |
||
1304 | |||
1305 | static cairo_int_status_t |
||
1306 | _cairo_qt_surface_fill (void *abstract_surface, |
||
1307 | cairo_operator_t op, |
||
1308 | const cairo_pattern_t *source, |
||
1309 | const cairo_path_fixed_t *path, |
||
1310 | cairo_fill_rule_t fill_rule, |
||
1311 | double tolerance, |
||
1312 | cairo_antialias_t antialias, |
||
1313 | const cairo_clip_t *clip) |
||
1314 | { |
||
1315 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
1316 | |||
1317 | D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op))); |
||
1318 | |||
1319 | if (! _op_is_supported (qs, op)) |
||
1320 | return _cairo_surface_fallback_fill (abstract_surface, op, |
||
1321 | source, path, fill_rule, |
||
1322 | tolerance, antialias, clip); |
||
1323 | |||
1324 | cairo_int_status_t status = _cairo_qt_surface_set_clip (qs, clip); |
||
1325 | if (unlikely (status)) |
||
1326 | return status; |
||
1327 | |||
1328 | if (qs->supports_porter_duff) |
||
1329 | qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); |
||
1330 | |||
1331 | // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is |
||
1332 | // enabled |
||
1333 | //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true); |
||
1334 | qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST); |
||
1335 | |||
1336 | if (! _cairo_qt_fast_fill (qs, source, |
||
1337 | path, fill_rule, tolerance, antialias)) |
||
1338 | { |
||
1339 | PatternToBrushConverter brush(source); |
||
1340 | qs->p->fillPath (path_to_qt (path, fill_rule), brush); |
||
1341 | } |
||
1342 | |||
1343 | if (qs->supports_porter_duff) |
||
1344 | qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); |
||
1345 | |||
1346 | return CAIRO_INT_STATUS_SUCCESS; |
||
1347 | } |
||
1348 | |||
1349 | static cairo_int_status_t |
||
1350 | _cairo_qt_surface_stroke (void *abstract_surface, |
||
1351 | cairo_operator_t op, |
||
1352 | const cairo_pattern_t *source, |
||
1353 | const cairo_path_fixed_t *path, |
||
1354 | const cairo_stroke_style_t *style, |
||
1355 | const cairo_matrix_t *ctm, |
||
1356 | const cairo_matrix_t *ctm_inverse, |
||
1357 | double tolerance, |
||
1358 | cairo_antialias_t antialias, |
||
1359 | const cairo_clip_t *clip) |
||
1360 | { |
||
1361 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
1362 | |||
1363 | D(fprintf(stderr, "q[%p] stroke op:%s\n", abstract_surface, _opstr(op))); |
||
1364 | |||
1365 | if (! _op_is_supported (qs, op)) |
||
1366 | return _cairo_surface_fallback_stroke (abstract_surface, op, |
||
1367 | source, path, style, ctm, |
||
1368 | ctm_inverse, tolerance, |
||
1369 | antialias, clip); |
||
1370 | |||
1371 | cairo_int_status_t int_status = _cairo_qt_surface_set_clip (qs, clip); |
||
1372 | if (unlikely (int_status)) |
||
1373 | return int_status; |
||
1374 | |||
1375 | QMatrix savedMatrix = qs->p->worldMatrix(); |
||
1376 | |||
1377 | if (qs->supports_porter_duff) |
||
1378 | qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); |
||
1379 | |||
1380 | qs->p->setWorldMatrix (_qmatrix_from_cairo_matrix (*ctm), true); |
||
1381 | // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is |
||
1382 | // enabled |
||
1383 | //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true); |
||
1384 | qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST); |
||
1385 | |||
1386 | PatternToPenConverter pen(source, style); |
||
1387 | |||
1388 | qs->p->setPen(pen); |
||
1389 | qs->p->drawPath(path_to_qt (path, ctm_inverse)); |
||
1390 | qs->p->setPen(Qt::black); |
||
1391 | |||
1392 | qs->p->setWorldMatrix (savedMatrix, false); |
||
1393 | |||
1394 | if (qs->supports_porter_duff) |
||
1395 | qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); |
||
1396 | |||
1397 | return CAIRO_INT_STATUS_SUCCESS; |
||
1398 | } |
||
1399 | |||
1400 | static cairo_int_status_t |
||
1401 | _cairo_qt_surface_show_glyphs (void *abstract_surface, |
||
1402 | cairo_operator_t op, |
||
1403 | const cairo_pattern_t *source, |
||
1404 | cairo_glyph_t *glyphs, |
||
1405 | int num_glyphs, |
||
1406 | cairo_scaled_font_t *scaled_font, |
||
1407 | const cairo_clip_t *clip) |
||
1408 | { |
||
1409 | #if ((QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)) && 0 |
||
1410 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
1411 | |||
1412 | // pick out the colour to use from the cairo source |
||
1413 | cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) source; |
||
1414 | cairo_scaled_glyph_t* glyph; |
||
1415 | // documentation says you have to freeze the cache, but I don't believe it |
||
1416 | _cairo_scaled_font_freeze_cache(scaled_font); |
||
1417 | |||
1418 | QColor tempColour(solid->color.red * 255, solid->color.green * 255, solid->color.blue * 255); |
||
1419 | QVarLengthArray |
||
1420 | QVarLengthArray |
||
1421 | FT_Face face = cairo_ft_scaled_font_lock_face (scaled_font); |
||
1422 | const FT_Size_Metrics& ftMetrics = face->size->metrics; |
||
1423 | QFont font(face->family_name); |
||
1424 | font.setStyleStrategy(QFont::NoFontMerging); |
||
1425 | font.setBold(face->style_flags & FT_STYLE_FLAG_BOLD); |
||
1426 | font.setItalic(face->style_flags & FT_STYLE_FLAG_ITALIC); |
||
1427 | font.setKerning(face->face_flags & FT_FACE_FLAG_KERNING); |
||
1428 | font.setPixelSize(ftMetrics.y_ppem); |
||
1429 | cairo_ft_scaled_font_unlock_face(scaled_font); |
||
1430 | qs->p->setFont(font); |
||
1431 | qs->p->setPen(tempColour); |
||
1432 | for (int currentGlyph = 0; currentGlyph < num_glyphs; currentGlyph++) { |
||
1433 | positions[currentGlyph].setX(glyphs[currentGlyph].x); |
||
1434 | positions[currentGlyph].setY(glyphs[currentGlyph].y); |
||
1435 | glyphss[currentGlyph] = glyphs[currentGlyph].index; |
||
1436 | } |
||
1437 | qt_draw_glyphs(qs->p, glyphss.data(), positions.data(), num_glyphs); |
||
1438 | _cairo_scaled_font_thaw_cache(scaled_font); |
||
1439 | return CAIRO_INT_STATUS_SUCCESS; |
||
1440 | #else |
||
1441 | return _cairo_surface_fallback_glyphs (abstract_surface, op, |
||
1442 | source, glyphs, num_glyphs, |
||
1443 | scaled_font, clip); |
||
1444 | #endif |
||
1445 | } |
||
1446 | |||
1447 | static cairo_int_status_t |
||
1448 | _cairo_qt_surface_mask (void *abstract_surface, |
||
1449 | cairo_operator_t op, |
||
1450 | const cairo_pattern_t *source, |
||
1451 | const cairo_pattern_t *mask, |
||
1452 | const cairo_clip_t *clip) |
||
1453 | { |
||
1454 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
1455 | |||
1456 | D(fprintf(stderr, "q[%p] mask op:%s\n", abstract_surface, _opstr(op))); |
||
1457 | |||
1458 | if (qs->p && mask->type == CAIRO_PATTERN_TYPE_SOLID) { |
||
1459 | cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask; |
||
1460 | cairo_int_status_t result; |
||
1461 | |||
1462 | qs->p->setOpacity (solid_mask->color.alpha); |
||
1463 | |||
1464 | result = _cairo_qt_surface_paint (abstract_surface, op, source, clip); |
||
1465 | |||
1466 | qs->p->setOpacity (1.0); |
||
1467 | |||
1468 | return result; |
||
1469 | } |
||
1470 | |||
1471 | // otherwise skip for now |
||
1472 | return _cairo_surface_fallback_mask (abstract_surface, op, source, mask, clip); |
||
1473 | } |
||
1474 | |||
1475 | static cairo_status_t |
||
1476 | _cairo_qt_surface_mark_dirty (void *abstract_surface, |
||
1477 | int x, int y, |
||
1478 | int width, int height) |
||
1479 | { |
||
1480 | cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; |
||
1481 | |||
1482 | if (qs->p && !(qs->image || qs->pixmap)) |
||
1483 | qs->p->save (); |
||
1484 | |||
1485 | return CAIRO_STATUS_SUCCESS; |
||
1486 | } |
||
1487 | |||
1488 | /* |
||
1489 | * Backend struct |
||
1490 | */ |
||
1491 | |||
1492 | static const cairo_surface_backend_t cairo_qt_surface_backend = { |
||
1493 | CAIRO_SURFACE_TYPE_QT, |
||
1494 | _cairo_qt_surface_finish, |
||
1495 | |||
1496 | _cairo_default_context_create, /* XXX */ |
||
1497 | |||
1498 | _cairo_qt_surface_create_similar, |
||
1499 | NULL, /* similar image */ |
||
1500 | _cairo_qt_surface_map_to_image, |
||
1501 | _cairo_qt_surface_unmap_image, |
||
1502 | |||
1503 | _cairo_surface_default_source, |
||
1504 | _cairo_qt_surface_acquire_source_image, |
||
1505 | _cairo_qt_surface_release_source_image, |
||
1506 | NULL, /* snapshot */ |
||
1507 | |||
1508 | NULL, /* copy_page */ |
||
1509 | NULL, /* show_page */ |
||
1510 | |||
1511 | _cairo_qt_surface_get_extents, |
||
1512 | NULL, /* get_font_options */ |
||
1513 | |||
1514 | NULL, /* flush */ |
||
1515 | _cairo_qt_surface_mark_dirty, |
||
1516 | |||
1517 | _cairo_qt_surface_paint, |
||
1518 | _cairo_qt_surface_mask, |
||
1519 | _cairo_qt_surface_stroke, |
||
1520 | _cairo_qt_surface_fill, |
||
1521 | NULL, /* fill_stroke */ |
||
1522 | _cairo_qt_surface_show_glyphs |
||
1523 | }; |
||
1524 | |||
1525 | cairo_surface_t * |
||
1526 | cairo_qt_surface_create (QPainter *painter) |
||
1527 | { |
||
1528 | cairo_qt_surface_t *qs; |
||
1529 | |||
1530 | qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); |
||
1531 | if (qs == NULL) |
||
1532 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
||
1533 | |||
1534 | memset (qs, 0, sizeof(cairo_qt_surface_t)); |
||
1535 | |||
1536 | _cairo_surface_init (&qs->base, |
||
1537 | &cairo_qt_surface_backend, |
||
1538 | NULL, /* device */ |
||
1539 | CAIRO_CONTENT_COLOR_ALPHA); |
||
1540 | |||
1541 | _cairo_surface_clipper_init (&qs->clipper, |
||
1542 | _cairo_qt_surface_clipper_intersect_clip_path); |
||
1543 | |||
1544 | qs->p = painter; |
||
1545 | if (qs->p->paintEngine()) |
||
1546 | qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); |
||
1547 | else |
||
1548 | qs->supports_porter_duff = FALSE; |
||
1549 | |||
1550 | // Save so that we can always get back to the original state |
||
1551 | qs->p->save(); |
||
1552 | |||
1553 | qs->window = painter->window(); |
||
1554 | |||
1555 | D(fprintf(stderr, "qpainter_surface_create: window: [%d %d %d %d] pd:%d\n", |
||
1556 | qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), |
||
1557 | qs->supports_porter_duff)); |
||
1558 | |||
1559 | return &qs->base; |
||
1560 | } |
||
1561 | |||
1562 | cairo_surface_t * |
||
1563 | cairo_qt_surface_create_with_qimage (cairo_format_t format, |
||
1564 | int width, |
||
1565 | int height) |
||
1566 | { |
||
1567 | cairo_qt_surface_t *qs; |
||
1568 | |||
1569 | qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); |
||
1570 | if (qs == NULL) |
||
1571 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
||
1572 | |||
1573 | memset (qs, 0, sizeof(cairo_qt_surface_t)); |
||
1574 | |||
1575 | _cairo_surface_init (&qs->base, |
||
1576 | &cairo_qt_surface_backend, |
||
1577 | NULL, /* device */ |
||
1578 | _cairo_content_from_format (format)); |
||
1579 | |||
1580 | _cairo_surface_clipper_init (&qs->clipper, |
||
1581 | _cairo_qt_surface_clipper_intersect_clip_path); |
||
1582 | |||
1583 | |||
1584 | QImage *image = new QImage (width, height, |
||
1585 | _qimage_format_from_cairo_format (format)); |
||
1586 | |||
1587 | qs->image = image; |
||
1588 | |||
1589 | if (!image->isNull()) { |
||
1590 | qs->p = new QPainter(image); |
||
1591 | qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); |
||
1592 | } |
||
1593 | |||
1594 | qs->image_equiv = cairo_image_surface_create_for_data (image->bits(), |
||
1595 | format, |
||
1596 | width, height, |
||
1597 | image->bytesPerLine()); |
||
1598 | |||
1599 | qs->window = QRect(0, 0, width, height); |
||
1600 | |||
1601 | D(fprintf(stderr, "qpainter_surface_create: qimage: [%d %d %d %d] pd:%d\n", |
||
1602 | qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), |
||
1603 | qs->supports_porter_duff)); |
||
1604 | |||
1605 | return &qs->base; |
||
1606 | } |
||
1607 | |||
1608 | cairo_surface_t * |
||
1609 | cairo_qt_surface_create_with_qpixmap (cairo_content_t content, |
||
1610 | int width, |
||
1611 | int height) |
||
1612 | { |
||
1613 | cairo_qt_surface_t *qs; |
||
1614 | |||
1615 | if ((content & CAIRO_CONTENT_COLOR) == 0) |
||
1616 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); |
||
1617 | |||
1618 | qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t)); |
||
1619 | if (qs == NULL) |
||
1620 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
||
1621 | |||
1622 | memset (qs, 0, sizeof(cairo_qt_surface_t)); |
||
1623 | |||
1624 | QPixmap *pixmap = new QPixmap (width, height); |
||
1625 | if (pixmap == NULL) { |
||
1626 | free (qs); |
||
1627 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
||
1628 | } |
||
1629 | |||
1630 | // By default, a QPixmap is opaque; however, if it's filled |
||
1631 | // with a color with a transparency component, it is converted |
||
1632 | // to a format that preserves transparency. |
||
1633 | if (content == CAIRO_CONTENT_COLOR_ALPHA) |
||
1634 | pixmap->fill(Qt::transparent); |
||
1635 | |||
1636 | _cairo_surface_init (&qs->base, |
||
1637 | &cairo_qt_surface_backend, |
||
1638 | NULL, /* device */ |
||
1639 | content); |
||
1640 | |||
1641 | _cairo_surface_clipper_init (&qs->clipper, |
||
1642 | _cairo_qt_surface_clipper_intersect_clip_path); |
||
1643 | |||
1644 | qs->pixmap = pixmap; |
||
1645 | |||
1646 | if (!pixmap->isNull()) { |
||
1647 | qs->p = new QPainter(pixmap); |
||
1648 | qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); |
||
1649 | } |
||
1650 | |||
1651 | qs->window = QRect(0, 0, width, height); |
||
1652 | |||
1653 | D(fprintf(stderr, "qpainter_surface_create: qpixmap: [%d %d %d %d] pd:%d\n", |
||
1654 | qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), |
||
1655 | qs->supports_porter_duff)); |
||
1656 | |||
1657 | return &qs->base; |
||
1658 | } |
||
1659 | |||
1660 | QPainter * |
||
1661 | cairo_qt_surface_get_qpainter (cairo_surface_t *surface) |
||
1662 | { |
||
1663 | cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; |
||
1664 | |||
1665 | if (surface->type != CAIRO_SURFACE_TYPE_QT) |
||
1666 | return NULL; |
||
1667 | |||
1668 | return qs->p; |
||
1669 | } |
||
1670 | |||
1671 | QImage * |
||
1672 | cairo_qt_surface_get_qimage (cairo_surface_t *surface) |
||
1673 | { |
||
1674 | cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; |
||
1675 | |||
1676 | if (surface->type != CAIRO_SURFACE_TYPE_QT) |
||
1677 | return NULL; |
||
1678 | |||
1679 | return qs->image; |
||
1680 | } |
||
1681 | |||
1682 | cairo_surface_t * |
||
1683 | cairo_qt_surface_get_image (cairo_surface_t *surface) |
||
1684 | { |
||
1685 | cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; |
||
1686 | |||
1687 | if (surface->type != CAIRO_SURFACE_TYPE_QT) |
||
1688 | return NULL; |
||
1689 | |||
1690 | return qs->image_equiv; |
||
1691 | } |
||
1692 | |||
1693 | /* |
||
1694 | * TODO: |
||
1695 | * |
||
1696 | * - Figure out why QBrush isn't working with non-repeated images |
||
1697 | * |
||
1698 | * - Correct repeat mode; right now, every surface source is EXTEND_REPEAT |
||
1699 | * - implement EXTEND_NONE (?? probably need to clip to the extents of the source) |
||
1700 | * - implement EXTEND_REFLECT (create temporary and copy 4x, then EXTEND_REPEAT that) |
||
1701 | * |
||
1702 | * - stroke-image failure |
||
1703 | * |
||
1704 | * - Implement mask() with non-solid masks (probably will need to use a temporary and use IN) |
||
1705 | * |
||
1706 | * - Implement gradient sources |
||
1707 | * |
||
1708 | * - Make create_similar smarter -- create QPixmaps in more circumstances |
||
1709 | * (e.g. if the pixmap can have alpha) |
||
1710 | * |
||
1711 | * - Implement show_glyphs() in terms of Qt |
||
1712 | * |
||
1713 | */>>>=>>>>> |