summaryrefslogtreecommitdiff
path: root/src/cairo-mime-surface.c
blob: f23c451a74e0938b3acd9d3dfbfc5b5a7d5f77a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
 *
 * Copyright © 2011 Intel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it either under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation
 * (the "LGPL") or, at your option, under the terms of the Mozilla
 * Public License Version 1.1 (the "MPL"). If you do not alter this
 * notice, a recipient may use your version of this file under either
 * the MPL or the LGPL.
 *
 * You should have received a copy of the LGPL along with this library
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
 * You should have received a copy of the MPL along with this library
 * in the file COPYING-MPL-1.1
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
 * the specific language governing rights and limitations.
 *
 * The Original Code is the cairo graphics library.
 *
 * The Initial Developer of the Original Code is Red Hat, Inc.
 *
 * Contributor(s):
 *	Chris Wilson <chris@chris-wilson.co.uk>
 */

/**
 * SECTION:cairo-mime-surface
 * @Title: Callback Surfaces
 * @Short_Description: Allows the user to provide a callback to supply image
 * data upon demand
 * @See_Also: #cairo_surface_t
 *
 * The mime surfaces provide the ability to render from arbitrary sources
 * not necessarily resident nor immediately usable by Cairo. The user is
 * given the ability to insert a placeholder surface which can be used
 * with a pattern and then later supply the actual pixel data upon demand.
 * This deferred source is given both the sample region for the operation
 * along with the destination surface such that they may be taken into
 * account when creating the actual surface to use as the source of pixel
 * data.
 *
 * The reason why it is called the mime surface is two-fold. First it came
 * about as an extension of the mime-data property to handle deferred
 * image decoding when rendering to pixel buffers (as opposed to the pass-
 * through support in PDF and friends.) And then to further rationalise
 * the name, it is a surface that mimics a real source without ever
 * holding onto to any pixels of its own - a mime surface.
 *
 * The mime-surface callback interface consists of 4 functions. The principal
 * pair are the acquire/release callbacks which are called when pixel data
 * is required for an operation (along with the target surface and the sample
 * extents). The callee must supply a surface that covers the sample area and
 * set the actual extents of the returned surface in the output rectangle
 * parameter. The surface does not necessarily have to be an image surface,
 * but it is expected that an image surface is likely to be the most
 * convenient for uploading pixel data. (Use
 * cairo_surface_create_similar_image() to create an image surface that is
 * optimised for uploading to the target.) The release callback is
 * subsequently called when the returned surface is no longer needed (before
 * the operation completes, within the lifetime of the source).
 *
 * The other pair of functions are to aide with lifetime management of the
 * surface with respect to patterns and other users. The destroy callback
 * allows for the caller to cleanup the associated data when the last
 * reference to surface is destroyed. The snapshot callback is triggered
 * when there is an immutable surface pattern referencing the mime-surface
 * and the mime-surface will be be invalidated. (Since the mime-surface is
 * read-only and a reference will be held by the pattern, this can only be
 * triggered through an explicit cairo_surface_finish().) In this
 * circumstance, we need to clone the source in order to preserve the pixel
 * data for later use (i.e. we have recorded the pattern). The snapshot
 * callback provides an interface for the caller to clone the mime-surface
 * in an efficient manner.  The returned surface may be of any type so long
 * as it holds all pixel data and remains accessible.
 */

/**
 * CAIRO_HAS_MIME_SURFACE:
 *
 * Defined if the mime surface backend is available.
 * The mime surface backend is always built in.
 *
 * @Since: 1.12
 */

#include "cairoint.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"

typedef struct _cairo_mime_surface {
    cairo_surface_t base;

    cairo_rectangle_int_t extents;

    cairo_mime_surface_acquire_t acquire;
    cairo_mime_surface_release_t release;
    cairo_mime_surface_snapshot_t snapshot;
    cairo_mime_surface_destroy_t destroy;

    /* an explicit pre-allocated member in preference to the general user-data */
    void *user_data;
} cairo_mime_surface_t;

static cairo_status_t
_cairo_mime_surface_finish (void *abstract_surface)
{
    cairo_mime_surface_t *surface = abstract_surface;

    if (surface->destroy)
	surface->destroy (&surface->base, surface->user_data);

    return CAIRO_STATUS_SUCCESS;
}

static cairo_bool_t
_cairo_mime_surface_get_extents (void			  *abstract_surface,
				 cairo_rectangle_int_t   *rectangle)
{
    cairo_mime_surface_t *surface = abstract_surface;

    *rectangle = surface->extents;
    return TRUE;
}

static cairo_status_t
_cairo_mime_surface_acquire_source_image (void                    *abstract_surface,
					  //cairo_surface_t	  *target,
					  cairo_image_surface_t  **image_out,
					  void                   **image_extra)
{
    cairo_mime_surface_t *mime = abstract_surface;
    cairo_surface_t *acquired;
    cairo_surface_t *dummy_target;
    cairo_rectangle_int_t extents;

    if (mime->acquire == NULL)
	return CAIRO_INT_STATUS_UNSUPPORTED;

    /* Force the callee to populate the extents rectangle */
    memset (&extents, 0, sizeof (extents));

    /* Masquerade for a flexible user-interface */
    dummy_target = _cairo_image_surface_create_with_content (mime->base.content, 0, 0);
    acquired = mime->acquire (&mime->base, mime->user_data,
			      dummy_target, &mime->extents, &extents);
    cairo_surface_destroy (dummy_target);

    if (acquired == NULL)
	return CAIRO_INT_STATUS_UNSUPPORTED;

    /* The callee must have supplied us with all the image data */
    assert (extents.width == mime->extents.width && extents.height == mime->extents.height);

    if (! _cairo_surface_is_image (acquired)) {
	cairo_status_t status;
	void *extra = NULL;

	status = _cairo_surface_acquire_source_image (acquired, image_out, &extra);
	if (unlikely (status)) {
	    cairo_surface_destroy (acquired);
	    return status;
	}

	assert (extra == NULL);
	*image_extra = acquired;
    } else {
	*image_out = (cairo_image_surface_t *) acquired;
	*image_extra = NULL;
    }
    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_mime_surface_release_source_image (void                   *abstract_surface,
					  cairo_image_surface_t  *image,
					  void                   *image_extra)
{
    cairo_mime_surface_t *mime = abstract_surface;

    if (image_extra) {
	cairo_surface_destroy (&image->base);
	image = image_extra;
    }

    if (mime->release)
	mime->release (&mime->base, mime->user_data, &image->base);
}

static cairo_surface_t *
_cairo_mime_surface_snapshot (void *abstract_surface)
{
    cairo_mime_surface_t *mime = abstract_surface;

    if (mime->snapshot == NULL)
	return NULL;

    return mime->snapshot (&mime->base, mime->user_data);
}

static const cairo_surface_backend_t cairo_mime_surface_backend = {
    CAIRO_SURFACE_TYPE_MIME,
    _cairo_mime_surface_finish,

    NULL, /* Read-only */

    NULL, /* create similar */
    NULL, /* create similar image */
    NULL, /* map to image */
    NULL, /* unmap image */

    _cairo_mime_surface_acquire_source_image,
    _cairo_mime_surface_release_source_image,
    NULL, /* acquire_dest_image */
    NULL, /* release_dest_image */
    NULL, /* clone_similar */
    NULL, /* composite */
    NULL, /* fill_rectangles */
    NULL, /* composite_trapezoids */
    NULL, /* create_span_renderer */
    NULL, /* check_span_renderer */
    NULL, /* copy_page */
    NULL, /* show_page */
    _cairo_mime_surface_get_extents,
    NULL, /* old_show_glyphs */
    NULL, /* get_font_options */
    NULL, /* flush */
    NULL, /* mark_dirty_rectangle */
    NULL, /* scaled_font_fini */
    NULL, /* scaled_glyph_fini */


    NULL, /* paint */
    NULL, /* mask */
    NULL, /* stroke */
    NULL, /* fill */
    NULL, /* glyphs */

    _cairo_mime_surface_snapshot,
};

cairo_surface_t *
cairo_mime_surface_create (void *data, cairo_content_t content, int width, int height)
{
    cairo_mime_surface_t *surface;

    if (width < 0 || height < 0)
	return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);

    if (! CAIRO_CONTENT_VALID (content))
	return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT);

    surface = calloc (1, sizeof (*surface));
    if (unlikely (surface == NULL))
	return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);

    _cairo_surface_init (&surface->base,
			 &cairo_mime_surface_backend,
			 NULL, /* device */
			 content);

    surface->extents.x = 0;
    surface->extents.y = 0;
    surface->extents.width  = width;
    surface->extents.height = height;

    surface->user_data = data;

    return &surface->base;
}

void
cairo_mime_surface_set_callback_data (cairo_surface_t *surface,
				      void *data)
{
    cairo_mime_surface_t *mime;

    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
	return;

    if (surface->backend != &cairo_mime_surface_backend)
	return;

    mime = (cairo_mime_surface_t *)surface;
    mime->user_data = data;
}

void *
cairo_mime_surface_get_callback_data (cairo_surface_t *surface)
{
    cairo_mime_surface_t *mime;

    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
	return NULL;

    if (surface->backend != &cairo_mime_surface_backend)
	return NULL;

    mime = (cairo_mime_surface_t *)surface;
    return mime->user_data;
}

void
cairo_mime_surface_set_acquire (cairo_surface_t *surface,
				cairo_mime_surface_acquire_t acquire,
				cairo_mime_surface_release_t release)
{
    cairo_mime_surface_t *mime;

    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
	return;

    if (surface->backend != &cairo_mime_surface_backend)
	return;

    mime = (cairo_mime_surface_t *)surface;
    mime->acquire = acquire;
    mime->release = release;
}

void
cairo_mime_surface_get_acquire (cairo_surface_t *surface,
				cairo_mime_surface_acquire_t *acquire,
				cairo_mime_surface_release_t *release)
{
    cairo_mime_surface_t *mime;

    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
	return;

    if (surface->backend != &cairo_mime_surface_backend)
	return;

    mime = (cairo_mime_surface_t *)surface;
    if (acquire)
	*acquire = mime->acquire;
    if (release)
	*release = mime->release;
}

void
cairo_mime_surface_set_snapshot (cairo_surface_t *surface,
				 cairo_mime_surface_snapshot_t snapshot)
{
    cairo_mime_surface_t *mime;

    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
	return;

    if (surface->backend != &cairo_mime_surface_backend)
	return;

    mime = (cairo_mime_surface_t *)surface;
    mime->snapshot = snapshot;
}

cairo_mime_surface_snapshot_t
cairo_mime_surface_get_snapshot (cairo_surface_t *surface)
{
    cairo_mime_surface_t *mime;

    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
	return NULL;

    if (surface->backend != &cairo_mime_surface_backend)
	return NULL;

    mime = (cairo_mime_surface_t *)surface;
    return mime->snapshot;
}

void
cairo_mime_surface_set_destroy (cairo_surface_t *surface,
				cairo_mime_surface_destroy_t destroy)
{
    cairo_mime_surface_t *mime;

    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
	return;

    if (surface->backend != &cairo_mime_surface_backend)
	return;

    mime = (cairo_mime_surface_t *)surface;
    mime->destroy = destroy;
}

cairo_mime_surface_destroy_t
cairo_mime_surface_get_destroy (cairo_surface_t *surface)
{
    cairo_mime_surface_t *mime;

    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
	return NULL;

    if (surface->backend != &cairo_mime_surface_backend)
	return NULL;

    mime = (cairo_mime_surface_t *)surface;
    return mime->destroy;
}