Subversion Repositories Kolibri OS

Rev

Rev 1892 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1892 serge 1
/*
2
 * Copyright © 2006 Keith Packard
3
 * Copyright © 2007 Adrian Johnson
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it either under the terms of the GNU Lesser General Public
7
 * License version 2.1 as published by the Free Software Foundation
8
 * (the "LGPL") or, at your option, under the terms of the Mozilla
9
 * Public License Version 1.1 (the "MPL"). If you do not alter this
10
 * notice, a recipient may use your version of this file under either
11
 * the MPL or the LGPL.
12
 *
13
 * You should have received a copy of the LGPL along with this library
14
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16
 * You should have received a copy of the MPL along with this library
17
 * in the file COPYING-MPL-1.1
18
 *
19
 * The contents of this file are subject to the Mozilla Public License
20
 * Version 1.1 (the "License"); you may not use this file except in
21
 * compliance with the License. You may obtain a copy of the License at
22
 * http://www.mozilla.org/MPL/
23
 *
24
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26
 * the specific language governing rights and limitations.
27
 *
28
 * The Original Code is the cairo graphics library.
29
 *
30
 * The Initial Developer of the Original Code is Keith Packard
31
 *
32
 * Contributor(s):
33
 *      Keith Packard 
34
 *      Adrian Johnson 
35
 */
36
 
37
#include "cairoint.h"
38
 
39
#include "cairo-analysis-surface-private.h"
3959 Serge 40
#include "cairo-box-inline.h"
41
#include "cairo-default-context-private.h"
1892 serge 42
#include "cairo-error-private.h"
43
#include "cairo-paginated-private.h"
3959 Serge 44
#include "cairo-recording-surface-inline.h"
45
#include "cairo-surface-snapshot-inline.h"
46
#include "cairo-surface-subsurface-inline.h"
1892 serge 47
#include "cairo-region-private.h"
48
 
49
typedef struct {
50
    cairo_surface_t base;
51
 
52
    cairo_surface_t *target;
53
 
54
    cairo_bool_t first_op;
55
    cairo_bool_t has_supported;
56
    cairo_bool_t has_unsupported;
57
 
58
    cairo_region_t supported_region;
59
    cairo_region_t fallback_region;
60
    cairo_box_t page_bbox;
61
 
62
    cairo_bool_t has_ctm;
63
    cairo_matrix_t ctm;
64
 
65
} cairo_analysis_surface_t;
66
 
67
cairo_int_status_t
68
_cairo_analysis_surface_merge_status (cairo_int_status_t status_a,
69
				      cairo_int_status_t status_b)
70
{
71
    /* fatal errors should be checked and propagated at source */
3959 Serge 72
    assert (! _cairo_int_status_is_error (status_a));
73
    assert (! _cairo_int_status_is_error (status_b));
1892 serge 74
 
75
    /* return the most important status */
76
    if (status_a == CAIRO_INT_STATUS_UNSUPPORTED ||
77
	status_b == CAIRO_INT_STATUS_UNSUPPORTED)
78
	return CAIRO_INT_STATUS_UNSUPPORTED;
79
 
80
    if (status_a == CAIRO_INT_STATUS_IMAGE_FALLBACK ||
81
	status_b == CAIRO_INT_STATUS_IMAGE_FALLBACK)
82
	return CAIRO_INT_STATUS_IMAGE_FALLBACK;
83
 
84
    if (status_a == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN ||
85
	status_b == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
86
	return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
87
 
88
    if (status_a == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
89
	status_b == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
90
	return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
91
 
92
    /* at this point we have checked all the valid internal codes, so... */
3959 Serge 93
    assert (status_a == CAIRO_INT_STATUS_SUCCESS &&
94
	    status_b == CAIRO_INT_STATUS_SUCCESS);
1892 serge 95
 
3959 Serge 96
    return CAIRO_INT_STATUS_SUCCESS;
97
}
98
 
99
struct proxy {
100
    cairo_surface_t base;
101
    cairo_surface_t *target;
102
};
103
 
104
static cairo_status_t
105
proxy_finish (void *abstract_surface)
106
{
1892 serge 107
    return CAIRO_STATUS_SUCCESS;
108
}
109
 
3959 Serge 110
static const cairo_surface_backend_t proxy_backend  = {
111
    CAIRO_INTERNAL_SURFACE_TYPE_NULL,
112
    proxy_finish,
113
};
114
 
115
static cairo_surface_t *
116
attach_proxy (cairo_surface_t *source,
117
	      cairo_surface_t *target)
118
{
119
    struct proxy *proxy;
120
 
121
    proxy = malloc (sizeof (*proxy));
122
    if (unlikely (proxy == NULL))
123
	return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
124
 
125
    _cairo_surface_init (&proxy->base, &proxy_backend, NULL, target->content);
126
 
127
    proxy->target = target;
128
    _cairo_surface_attach_snapshot (source, &proxy->base, NULL);
129
 
130
    return &proxy->base;
131
}
132
 
133
static void
134
detach_proxy (cairo_surface_t *proxy)
135
{
136
    cairo_surface_finish (proxy);
137
    cairo_surface_destroy (proxy);
138
}
139
 
1892 serge 140
static cairo_int_status_t
141
_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
142
				    const cairo_pattern_t    *pattern)
143
{
144
    const cairo_surface_pattern_t *surface_pattern;
3959 Serge 145
    cairo_analysis_surface_t *tmp;
146
    cairo_surface_t *source, *proxy;
147
    cairo_matrix_t p2d;
148
    cairo_status_t status, analysis_status;
1892 serge 149
 
150
    assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
151
    surface_pattern = (const cairo_surface_pattern_t *) pattern;
152
    assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING);
3959 Serge 153
    source = surface_pattern->surface;
1892 serge 154
 
3959 Serge 155
    proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
156
    if (proxy != NULL) {
157
	/* nothing untoward found so far */
158
	return CAIRO_STATUS_SUCCESS;
159
    }
1892 serge 160
 
3959 Serge 161
    tmp = (cairo_analysis_surface_t *)
162
	_cairo_analysis_surface_create (surface->target);
163
    if (unlikely (tmp->base.status))
164
	return tmp->base.status;
165
    proxy = attach_proxy (source, &tmp->base);
166
 
1892 serge 167
    p2d = pattern->matrix;
168
    status = cairo_matrix_invert (&p2d);
169
    assert (status == CAIRO_STATUS_SUCCESS);
170
 
3959 Serge 171
    cairo_matrix_multiply (&tmp->ctm, &p2d, &surface->ctm);
172
    tmp->has_ctm = ! _cairo_matrix_is_identity (&tmp->ctm);
1892 serge 173
 
3959 Serge 174
    source = _cairo_surface_get_source (source, NULL);
175
    status = _cairo_recording_surface_replay_and_create_regions (source,
176
								 &tmp->base);
177
    analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS;
178
    detach_proxy (proxy);
179
    cairo_surface_destroy (&tmp->base);
1892 serge 180
 
3959 Serge 181
    if (unlikely (status))
182
	return status;
1892 serge 183
 
3959 Serge 184
    return analysis_status;
1892 serge 185
}
186
 
187
static cairo_int_status_t
188
_add_operation (cairo_analysis_surface_t *surface,
189
		cairo_rectangle_int_t    *rect,
190
		cairo_int_status_t        backend_status)
191
{
192
    cairo_int_status_t status;
193
    cairo_box_t bbox;
194
 
195
    if (rect->width == 0 || rect->height == 0) {
196
	/* Even though the operation is not visible we must be careful
197
	 * to not allow unsupported operations to be replayed to the
198
	 * backend during CAIRO_PAGINATED_MODE_RENDER */
3959 Serge 199
	if (backend_status == CAIRO_INT_STATUS_SUCCESS ||
200
	    backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
201
	    backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO)
1892 serge 202
	{
3959 Serge 203
	    return CAIRO_INT_STATUS_SUCCESS;
1892 serge 204
	}
205
	else
206
	{
207
	    return CAIRO_INT_STATUS_IMAGE_FALLBACK;
208
	}
209
    }
210
 
211
    _cairo_box_from_rectangle (&bbox, rect);
212
 
213
    if (surface->has_ctm) {
214
	int tx, ty;
215
 
216
	if (_cairo_matrix_is_integer_translation (&surface->ctm, &tx, &ty)) {
217
	    rect->x += tx;
218
	    rect->y += ty;
3959 Serge 219
 
220
	    tx = _cairo_fixed_from_int (tx);
221
	    bbox.p1.x += tx;
222
	    bbox.p2.x += tx;
223
 
224
	    ty = _cairo_fixed_from_int (ty);
225
	    bbox.p1.y += ty;
226
	    bbox.p2.y += ty;
1892 serge 227
	} else {
228
	    _cairo_matrix_transform_bounding_box_fixed (&surface->ctm,
229
							&bbox, NULL);
230
 
231
	    if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) {
232
		/* Even though the operation is not visible we must be
233
		 * careful to not allow unsupported operations to be
234
		 * replayed to the backend during
235
		 * CAIRO_PAGINATED_MODE_RENDER */
3959 Serge 236
		if (backend_status == CAIRO_INT_STATUS_SUCCESS ||
237
		    backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
238
		    backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO)
1892 serge 239
		{
3959 Serge 240
		    return CAIRO_INT_STATUS_SUCCESS;
1892 serge 241
		}
242
		else
243
		{
244
		    return CAIRO_INT_STATUS_IMAGE_FALLBACK;
245
		}
246
	    }
247
 
248
	    _cairo_box_round_to_rectangle (&bbox, rect);
249
	}
250
    }
251
 
252
    if (surface->first_op) {
253
	surface->first_op = FALSE;
254
	surface->page_bbox = bbox;
3959 Serge 255
    } else
256
	_cairo_box_add_box(&surface->page_bbox, &bbox);
1892 serge 257
 
258
    /* If the operation is completely enclosed within the fallback
259
     * region there is no benefit in emitting a native operation as
260
     * the fallback image will be painted on top.
261
     */
262
    if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN)
263
	return CAIRO_INT_STATUS_IMAGE_FALLBACK;
264
 
265
    if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) {
266
	/* A status of CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY indicates
267
	 * that the backend only supports this operation if the
268
	 * transparency removed. If the extents of this operation does
269
	 * not intersect any other native operation, the operation is
270
	 * natively supported and the backend will blend the
271
	 * transparency into the white background.
272
	 */
273
	if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT)
3959 Serge 274
	    backend_status = CAIRO_INT_STATUS_SUCCESS;
1892 serge 275
    }
276
 
3959 Serge 277
    if (backend_status == CAIRO_INT_STATUS_SUCCESS) {
1892 serge 278
	/* Add the operation to the supported region. Operations in
279
	 * this region will be emitted as native operations.
280
	 */
281
	surface->has_supported = TRUE;
282
	return cairo_region_union_rectangle (&surface->supported_region, rect);
283
    }
284
 
285
    /* Add the operation to the unsupported region. This region will
286
     * be painted as an image after all native operations have been
287
     * emitted.
288
     */
289
    surface->has_unsupported = TRUE;
290
    status = cairo_region_union_rectangle (&surface->fallback_region, rect);
291
 
292
    /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate
293
     * unsupported operations to the recording surface as using
294
     * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to
295
     * invoke the cairo-surface-fallback path then return
296
     * CAIRO_STATUS_SUCCESS.
297
     */
3959 Serge 298
    if (status == CAIRO_INT_STATUS_SUCCESS)
1892 serge 299
	return CAIRO_INT_STATUS_IMAGE_FALLBACK;
300
    else
301
	return status;
302
}
303
 
304
static cairo_status_t
305
_cairo_analysis_surface_finish (void *abstract_surface)
306
{
307
    cairo_analysis_surface_t	*surface = (cairo_analysis_surface_t *) abstract_surface;
308
 
309
    _cairo_region_fini (&surface->supported_region);
310
    _cairo_region_fini (&surface->fallback_region);
311
 
312
    cairo_surface_destroy (surface->target);
313
 
314
    return CAIRO_STATUS_SUCCESS;
315
}
316
 
317
static cairo_bool_t
318
_cairo_analysis_surface_get_extents (void			*abstract_surface,
319
				     cairo_rectangle_int_t	*rectangle)
320
{
321
    cairo_analysis_surface_t *surface = abstract_surface;
322
 
323
    return _cairo_surface_get_extents (surface->target, rectangle);
324
}
325
 
326
static void
3959 Serge 327
_rectangle_intersect_clip (cairo_rectangle_int_t *extents, const cairo_clip_t *clip)
1892 serge 328
{
329
    if (clip != NULL)
3959 Serge 330
	_cairo_rectangle_intersect (extents, _cairo_clip_get_extents (clip));
1892 serge 331
}
332
 
333
static void
334
_cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface,
335
					   cairo_operator_t op,
336
					   const cairo_pattern_t *source,
3959 Serge 337
					   const cairo_clip_t *clip,
1892 serge 338
					   cairo_rectangle_int_t *extents)
339
{
340
    cairo_bool_t is_empty;
341
 
342
    is_empty = _cairo_surface_get_extents (&surface->base, extents);
343
 
344
    if (_cairo_operator_bounded_by_source (op)) {
345
	cairo_rectangle_int_t source_extents;
346
 
347
	_cairo_pattern_get_extents (source, &source_extents);
3959 Serge 348
	_cairo_rectangle_intersect (extents, &source_extents);
1892 serge 349
    }
350
 
351
    _rectangle_intersect_clip (extents, clip);
352
}
353
 
354
static cairo_int_status_t
355
_cairo_analysis_surface_paint (void			*abstract_surface,
356
			       cairo_operator_t		op,
357
			       const cairo_pattern_t	*source,
3959 Serge 358
			       const cairo_clip_t		*clip)
1892 serge 359
{
360
    cairo_analysis_surface_t *surface = abstract_surface;
3959 Serge 361
    cairo_int_status_t	     backend_status;
1892 serge 362
    cairo_rectangle_int_t  extents;
363
 
364
    if (surface->target->backend->paint == NULL) {
365
	backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
366
    } else {
367
	backend_status =
368
	    surface->target->backend->paint (surface->target,
369
					     op, source, clip);
3959 Serge 370
	if (_cairo_int_status_is_error (backend_status))
1892 serge 371
	    return backend_status;
372
    }
373
 
374
    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
375
	backend_status = _analyze_recording_surface_pattern (surface, source);
376
 
377
    _cairo_analysis_surface_operation_extents (surface,
378
					       op, source, clip,
379
					       &extents);
380
 
381
    return _add_operation (surface, &extents, backend_status);
382
}
383
 
384
static cairo_int_status_t
385
_cairo_analysis_surface_mask (void			*abstract_surface,
386
			      cairo_operator_t		 op,
387
			      const cairo_pattern_t	*source,
388
			      const cairo_pattern_t	*mask,
3959 Serge 389
			      const cairo_clip_t		*clip)
1892 serge 390
{
391
    cairo_analysis_surface_t *surface = abstract_surface;
392
    cairo_int_status_t	      backend_status;
393
    cairo_rectangle_int_t   extents;
394
 
395
    if (surface->target->backend->mask == NULL) {
396
	backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
397
    } else {
398
	backend_status =
399
	    surface->target->backend->mask (surface->target,
400
					    op, source, mask, clip);
3959 Serge 401
	if (_cairo_int_status_is_error (backend_status))
1892 serge 402
	    return backend_status;
403
    }
404
 
405
    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) {
406
	cairo_int_status_t backend_source_status = CAIRO_STATUS_SUCCESS;
407
	cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS;
408
 
409
	if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
3959 Serge 410
	    cairo_surface_t *src_surface = ((cairo_surface_pattern_t *)source)->surface;
411
	    src_surface = _cairo_surface_get_source (src_surface, NULL);
412
	    if (_cairo_surface_is_recording (src_surface)) {
1892 serge 413
		backend_source_status =
414
		    _analyze_recording_surface_pattern (surface, source);
3959 Serge 415
		if (_cairo_int_status_is_error (backend_source_status))
1892 serge 416
		    return backend_source_status;
417
	    }
418
	}
419
 
420
	if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
3959 Serge 421
	    cairo_surface_t *mask_surface = ((cairo_surface_pattern_t *)mask)->surface;
422
	    mask_surface = _cairo_surface_get_source (mask_surface, NULL);
423
	    if (_cairo_surface_is_recording (mask_surface)) {
1892 serge 424
		backend_mask_status =
425
		    _analyze_recording_surface_pattern (surface, mask);
3959 Serge 426
		if (_cairo_int_status_is_error (backend_mask_status))
1892 serge 427
		    return backend_mask_status;
428
	    }
429
	}
430
 
431
	backend_status =
432
	    _cairo_analysis_surface_merge_status (backend_source_status,
433
						  backend_mask_status);
434
    }
435
 
436
    _cairo_analysis_surface_operation_extents (surface,
437
					       op, source, clip,
438
					       &extents);
439
 
440
    if (_cairo_operator_bounded_by_mask (op)) {
441
	cairo_rectangle_int_t mask_extents;
442
 
443
	_cairo_pattern_get_extents (mask, &mask_extents);
3959 Serge 444
	_cairo_rectangle_intersect (&extents, &mask_extents);
1892 serge 445
    }
446
 
447
    return _add_operation (surface, &extents, backend_status);
448
}
449
 
450
static cairo_int_status_t
451
_cairo_analysis_surface_stroke (void			*abstract_surface,
452
				cairo_operator_t	 op,
453
				const cairo_pattern_t	*source,
3959 Serge 454
				const cairo_path_fixed_t	*path,
1892 serge 455
				const cairo_stroke_style_t	*style,
456
				const cairo_matrix_t		*ctm,
457
				const cairo_matrix_t		*ctm_inverse,
458
				double			 tolerance,
459
				cairo_antialias_t	 antialias,
3959 Serge 460
				const cairo_clip_t		*clip)
1892 serge 461
{
462
    cairo_analysis_surface_t *surface = abstract_surface;
3959 Serge 463
    cairo_int_status_t	     backend_status;
1892 serge 464
    cairo_rectangle_int_t    extents;
465
 
466
    if (surface->target->backend->stroke == NULL) {
467
	backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
468
    } else {
469
	backend_status =
470
	    surface->target->backend->stroke (surface->target, op,
471
					      source, path, style,
472
					      ctm, ctm_inverse,
473
					      tolerance, antialias,
474
					      clip);
3959 Serge 475
	if (_cairo_int_status_is_error (backend_status))
1892 serge 476
	    return backend_status;
477
    }
478
 
479
    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
480
	backend_status = _analyze_recording_surface_pattern (surface, source);
481
 
482
    _cairo_analysis_surface_operation_extents (surface,
483
					       op, source, clip,
484
					       &extents);
485
 
486
    if (_cairo_operator_bounded_by_mask (op)) {
487
	cairo_rectangle_int_t mask_extents;
3959 Serge 488
	cairo_int_status_t status;
1892 serge 489
 
490
	status = _cairo_path_fixed_stroke_extents (path, style,
491
						   ctm, ctm_inverse,
492
						   tolerance,
493
						   &mask_extents);
494
	if (unlikely (status))
495
	    return status;
496
 
3959 Serge 497
	_cairo_rectangle_intersect (&extents, &mask_extents);
1892 serge 498
    }
499
 
500
    return _add_operation (surface, &extents, backend_status);
501
}
502
 
503
static cairo_int_status_t
504
_cairo_analysis_surface_fill (void			*abstract_surface,
505
			      cairo_operator_t		 op,
506
			      const cairo_pattern_t	*source,
3959 Serge 507
			      const cairo_path_fixed_t	*path,
1892 serge 508
			      cairo_fill_rule_t		 fill_rule,
509
			      double			 tolerance,
510
			      cairo_antialias_t		 antialias,
3959 Serge 511
			      const cairo_clip_t		*clip)
1892 serge 512
{
513
    cairo_analysis_surface_t *surface = abstract_surface;
3959 Serge 514
    cairo_int_status_t	     backend_status;
1892 serge 515
    cairo_rectangle_int_t    extents;
516
 
517
    if (surface->target->backend->fill == NULL) {
518
	backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
519
    } else {
520
	backend_status =
521
	    surface->target->backend->fill (surface->target, op,
522
					    source, path, fill_rule,
523
					    tolerance, antialias,
524
					    clip);
3959 Serge 525
	if (_cairo_int_status_is_error (backend_status))
1892 serge 526
	    return backend_status;
527
    }
528
 
529
    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
530
	backend_status = _analyze_recording_surface_pattern (surface, source);
531
 
532
    _cairo_analysis_surface_operation_extents (surface,
533
					       op, source, clip,
534
					       &extents);
535
 
536
    if (_cairo_operator_bounded_by_mask (op)) {
537
	cairo_rectangle_int_t mask_extents;
538
 
539
	_cairo_path_fixed_fill_extents (path, fill_rule, tolerance,
540
					&mask_extents);
541
 
3959 Serge 542
	_cairo_rectangle_intersect (&extents, &mask_extents);
1892 serge 543
    }
544
 
545
    return _add_operation (surface, &extents, backend_status);
546
}
547
 
548
static cairo_int_status_t
549
_cairo_analysis_surface_show_glyphs (void		  *abstract_surface,
550
				     cairo_operator_t	   op,
551
				     const cairo_pattern_t *source,
552
				     cairo_glyph_t	  *glyphs,
553
				     int		   num_glyphs,
554
				     cairo_scaled_font_t  *scaled_font,
3959 Serge 555
				     const cairo_clip_t         *clip)
1892 serge 556
{
557
    cairo_analysis_surface_t *surface = abstract_surface;
3959 Serge 558
    cairo_int_status_t	     status, backend_status;
1892 serge 559
    cairo_rectangle_int_t    extents, glyph_extents;
560
 
561
    /* Adapted from _cairo_surface_show_glyphs */
562
    if (surface->target->backend->show_glyphs != NULL) {
563
	backend_status =
564
	    surface->target->backend->show_glyphs (surface->target, op,
565
						   source,
566
						   glyphs, num_glyphs,
567
						   scaled_font,
3959 Serge 568
						   clip);
569
	if (_cairo_int_status_is_error (backend_status))
1892 serge 570
	    return backend_status;
571
    }
572
    else if (surface->target->backend->show_text_glyphs != NULL)
573
    {
574
	backend_status =
575
	    surface->target->backend->show_text_glyphs (surface->target, op,
576
							source,
577
							NULL, 0,
578
							glyphs, num_glyphs,
579
							NULL, 0,
580
							FALSE,
581
							scaled_font,
582
							clip);
3959 Serge 583
	if (_cairo_int_status_is_error (backend_status))
1892 serge 584
	    return backend_status;
585
    }
586
    else
587
    {
588
	backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
589
    }
590
 
591
    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
592
	backend_status = _analyze_recording_surface_pattern (surface, source);
593
 
594
    _cairo_analysis_surface_operation_extents (surface,
595
					       op, source, clip,
596
					       &extents);
597
 
598
    if (_cairo_operator_bounded_by_mask (op)) {
599
	status = _cairo_scaled_font_glyph_device_extents (scaled_font,
600
							  glyphs,
601
							  num_glyphs,
602
							  &glyph_extents,
603
							  NULL);
604
	if (unlikely (status))
605
	    return status;
606
 
3959 Serge 607
	_cairo_rectangle_intersect (&extents, &glyph_extents);
1892 serge 608
    }
609
 
610
    return _add_operation (surface, &extents, backend_status);
611
}
612
 
613
static cairo_bool_t
614
_cairo_analysis_surface_has_show_text_glyphs (void *abstract_surface)
615
{
616
    cairo_analysis_surface_t *surface = abstract_surface;
617
 
618
    return cairo_surface_has_show_text_glyphs (surface->target);
619
}
620
 
621
static cairo_int_status_t
622
_cairo_analysis_surface_show_text_glyphs (void			    *abstract_surface,
623
					  cairo_operator_t	     op,
624
					  const cairo_pattern_t	    *source,
625
					  const char		    *utf8,
626
					  int			     utf8_len,
627
					  cairo_glyph_t		    *glyphs,
628
					  int			     num_glyphs,
629
					  const cairo_text_cluster_t *clusters,
630
					  int			     num_clusters,
631
					  cairo_text_cluster_flags_t cluster_flags,
632
					  cairo_scaled_font_t	    *scaled_font,
3959 Serge 633
					  const cairo_clip_t		    *clip)
1892 serge 634
{
635
    cairo_analysis_surface_t *surface = abstract_surface;
3959 Serge 636
    cairo_int_status_t	     status, backend_status;
1892 serge 637
    cairo_rectangle_int_t    extents, glyph_extents;
638
 
639
    /* Adapted from _cairo_surface_show_glyphs */
640
    backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
641
    if (surface->target->backend->show_text_glyphs != NULL) {
642
	backend_status =
643
	    surface->target->backend->show_text_glyphs (surface->target, op,
644
							source,
645
							utf8, utf8_len,
646
							glyphs, num_glyphs,
647
							clusters, num_clusters,
648
							cluster_flags,
649
							scaled_font,
650
							clip);
3959 Serge 651
	if (_cairo_int_status_is_error (backend_status))
1892 serge 652
	    return backend_status;
653
    }
654
    if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED &&
655
	surface->target->backend->show_glyphs != NULL)
656
    {
657
	backend_status =
658
	    surface->target->backend->show_glyphs (surface->target, op,
659
						   source,
660
						   glyphs, num_glyphs,
661
						   scaled_font,
3959 Serge 662
						   clip);
663
	if (_cairo_int_status_is_error (backend_status))
1892 serge 664
	    return backend_status;
665
    }
666
 
667
    if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
668
	backend_status = _analyze_recording_surface_pattern (surface, source);
669
 
670
    _cairo_analysis_surface_operation_extents (surface,
671
					       op, source, clip,
672
					       &extents);
673
 
674
    if (_cairo_operator_bounded_by_mask (op)) {
675
	status = _cairo_scaled_font_glyph_device_extents (scaled_font,
676
							  glyphs,
677
							  num_glyphs,
678
							  &glyph_extents,
679
							  NULL);
680
	if (unlikely (status))
681
	    return status;
682
 
3959 Serge 683
	_cairo_rectangle_intersect (&extents, &glyph_extents);
1892 serge 684
    }
685
 
686
    return _add_operation (surface, &extents, backend_status);
687
}
688
 
689
static const cairo_surface_backend_t cairo_analysis_surface_backend = {
690
    CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
3959 Serge 691
 
692
    _cairo_analysis_surface_finish,
693
    NULL,
694
 
1892 serge 695
    NULL, /* create_similar */
3959 Serge 696
    NULL, /* create_similar_image */
697
    NULL, /* map_to_image */
698
    NULL, /* unmap */
699
 
700
    NULL, /* source */
1892 serge 701
    NULL, /* acquire_source_image */
702
    NULL, /* release_source_image */
3959 Serge 703
    NULL, /* snapshot */
704
 
1892 serge 705
    NULL, /* copy_page */
706
    NULL, /* show_page */
3959 Serge 707
 
1892 serge 708
    _cairo_analysis_surface_get_extents,
709
    NULL, /* get_font_options */
3959 Serge 710
 
1892 serge 711
    NULL, /* flush */
712
    NULL, /* mark_dirty_rectangle */
3959 Serge 713
 
1892 serge 714
    _cairo_analysis_surface_paint,
715
    _cairo_analysis_surface_mask,
716
    _cairo_analysis_surface_stroke,
717
    _cairo_analysis_surface_fill,
3959 Serge 718
    NULL, /* fill_stroke */
1892 serge 719
    _cairo_analysis_surface_show_glyphs,
720
    _cairo_analysis_surface_has_show_text_glyphs,
721
    _cairo_analysis_surface_show_text_glyphs
722
};
723
 
724
cairo_surface_t *
725
_cairo_analysis_surface_create (cairo_surface_t		*target)
726
{
727
    cairo_analysis_surface_t *surface;
728
    cairo_status_t status;
729
 
730
    status = target->status;
731
    if (unlikely (status))
732
	return _cairo_surface_create_in_error (status);
733
 
734
    surface = malloc (sizeof (cairo_analysis_surface_t));
735
    if (unlikely (surface == NULL))
736
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
737
 
738
    /* I believe the content type here is truly arbitrary. I'm quite
739
     * sure nothing will ever use this value. */
740
    _cairo_surface_init (&surface->base,
741
			 &cairo_analysis_surface_backend,
742
			 NULL, /* device */
743
			 CAIRO_CONTENT_COLOR_ALPHA);
744
 
745
    cairo_matrix_init_identity (&surface->ctm);
746
    surface->has_ctm = FALSE;
747
 
748
    surface->target = cairo_surface_reference (target);
749
    surface->first_op  = TRUE;
750
    surface->has_supported = FALSE;
751
    surface->has_unsupported = FALSE;
752
 
753
    _cairo_region_init (&surface->supported_region);
754
    _cairo_region_init (&surface->fallback_region);
755
 
756
    surface->page_bbox.p1.x = 0;
757
    surface->page_bbox.p1.y = 0;
758
    surface->page_bbox.p2.x = 0;
759
    surface->page_bbox.p2.y = 0;
760
 
761
    return &surface->base;
762
}
763
 
764
void
765
_cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface,
766
				 const cairo_matrix_t  *ctm)
767
{
768
    cairo_analysis_surface_t	*surface;
769
 
770
    if (abstract_surface->status)
771
	return;
772
 
773
    surface = (cairo_analysis_surface_t *) abstract_surface;
774
 
775
    surface->ctm = *ctm;
776
    surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm);
777
}
778
 
779
void
780
_cairo_analysis_surface_get_ctm (cairo_surface_t *abstract_surface,
781
				 cairo_matrix_t  *ctm)
782
{
783
    cairo_analysis_surface_t	*surface = (cairo_analysis_surface_t *) abstract_surface;
784
 
785
    *ctm = surface->ctm;
786
}
787
 
788
 
789
cairo_region_t *
790
_cairo_analysis_surface_get_supported (cairo_surface_t *abstract_surface)
791
{
792
    cairo_analysis_surface_t	*surface = (cairo_analysis_surface_t *) abstract_surface;
793
 
794
    return &surface->supported_region;
795
}
796
 
797
cairo_region_t *
798
_cairo_analysis_surface_get_unsupported (cairo_surface_t *abstract_surface)
799
{
800
    cairo_analysis_surface_t	*surface = (cairo_analysis_surface_t *) abstract_surface;
801
 
802
    return &surface->fallback_region;
803
}
804
 
805
cairo_bool_t
806
_cairo_analysis_surface_has_supported (cairo_surface_t *abstract_surface)
807
{
808
    cairo_analysis_surface_t	*surface = (cairo_analysis_surface_t *) abstract_surface;
809
 
810
    return surface->has_supported;
811
}
812
 
813
cairo_bool_t
814
_cairo_analysis_surface_has_unsupported (cairo_surface_t *abstract_surface)
815
{
816
    cairo_analysis_surface_t	*surface = (cairo_analysis_surface_t *) abstract_surface;
817
 
818
    return surface->has_unsupported;
819
}
820
 
821
void
822
_cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface,
823
					  cairo_box_t     *bbox)
824
{
825
    cairo_analysis_surface_t	*surface = (cairo_analysis_surface_t *) abstract_surface;
826
 
827
    *bbox = surface->page_bbox;
828
}
829
 
830
/* null surface type: a surface that does nothing (has no side effects, yay!) */
831
 
832
static cairo_int_status_t
833
_return_success (void)
834
{
835
    return CAIRO_STATUS_SUCCESS;
836
}
837
 
838
/* These typedefs are just to silence the compiler... */
839
typedef cairo_int_status_t
840
(*_paint_func)			(void			*surface,
841
			         cairo_operator_t	 op,
842
				 const cairo_pattern_t	*source,
3959 Serge 843
				 const cairo_clip_t		*clip);
1892 serge 844
 
845
typedef cairo_int_status_t
846
(*_mask_func)			(void			*surface,
847
			         cairo_operator_t	 op,
848
				 const cairo_pattern_t	*source,
849
				 const cairo_pattern_t	*mask,
3959 Serge 850
				 const cairo_clip_t		*clip);
1892 serge 851
 
852
typedef cairo_int_status_t
853
(*_stroke_func)			(void			*surface,
854
			         cairo_operator_t	 op,
855
				 const cairo_pattern_t	*source,
3959 Serge 856
				 const cairo_path_fixed_t	*path,
1892 serge 857
				 const cairo_stroke_style_t	*style,
858
				 const cairo_matrix_t		*ctm,
859
				 const cairo_matrix_t		*ctm_inverse,
860
				 double			 tolerance,
861
				 cairo_antialias_t	 antialias,
3959 Serge 862
				 const cairo_clip_t		*clip);
1892 serge 863
 
864
typedef cairo_int_status_t
865
(*_fill_func)			(void			*surface,
866
			         cairo_operator_t	 op,
867
				 const cairo_pattern_t	*source,
3959 Serge 868
				 const cairo_path_fixed_t	*path,
1892 serge 869
				 cairo_fill_rule_t	 fill_rule,
870
				 double			 tolerance,
871
				 cairo_antialias_t	 antialias,
3959 Serge 872
				 const cairo_clip_t		*clip);
1892 serge 873
 
874
typedef cairo_int_status_t
875
(*_show_glyphs_func)		(void			*surface,
876
			         cairo_operator_t	 op,
877
				 const cairo_pattern_t	*source,
878
				 cairo_glyph_t		*glyphs,
879
				 int			 num_glyphs,
880
				 cairo_scaled_font_t	*scaled_font,
3959 Serge 881
				 const cairo_clip_t		*clip);
1892 serge 882
 
883
static const cairo_surface_backend_t cairo_null_surface_backend = {
884
    CAIRO_INTERNAL_SURFACE_TYPE_NULL,
3959 Serge 885
    NULL, /* finish */
1892 serge 886
 
3959 Serge 887
    NULL, /* only accessed through the surface functions */
888
 
1892 serge 889
    NULL, /* create_similar */
3959 Serge 890
    NULL, /* create similar image */
891
    NULL, /* map to image */
892
    NULL, /* unmap image*/
893
 
894
    NULL, /* source */
1892 serge 895
    NULL, /* acquire_source_image */
896
    NULL, /* release_source_image */
3959 Serge 897
    NULL, /* snapshot */
898
 
1892 serge 899
    NULL, /* copy_page */
900
    NULL, /* show_page */
3959 Serge 901
 
1892 serge 902
    NULL, /* get_extents */
903
    NULL, /* get_font_options */
3959 Serge 904
 
1892 serge 905
    NULL, /* flush */
906
    NULL, /* mark_dirty_rectangle */
3959 Serge 907
 
1892 serge 908
    (_paint_func) _return_success,	    /* paint */
909
    (_mask_func) _return_success,	    /* mask */
910
    (_stroke_func) _return_success,	    /* stroke */
911
    (_fill_func) _return_success,	    /* fill */
3959 Serge 912
    NULL, /* fill_stroke */
1892 serge 913
    (_show_glyphs_func) _return_success,    /* show_glyphs */
914
    NULL, /* has_show_text_glyphs */
915
    NULL  /* show_text_glyphs */
916
};
917
 
918
cairo_surface_t *
919
_cairo_null_surface_create (cairo_content_t content)
920
{
921
    cairo_surface_t *surface;
922
 
923
    surface = malloc (sizeof (cairo_surface_t));
924
    if (unlikely (surface == NULL)) {
925
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
926
    }
927
 
928
    _cairo_surface_init (surface,
929
			 &cairo_null_surface_backend,
930
			 NULL, /* device */
931
			 content);
932
 
933
    return surface;
934
}