Subversion Repositories Kolibri OS

Rev

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  (closure);
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  (closure);
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  (closure);
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  (closure);
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 dashes (odd_dash ? style->num_dashes * 2 : style->num_dashes);
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 qsSrc_image_d;
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 positions(num_glyphs);
1420
    QVarLengthArray glyphss(num_glyphs);
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
 */