/work/workdir/UnpackedTarball/cairo/src/cairo-device.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Cairo - a vector graphics library with display and print output |
2 | | * |
3 | | * Copyright © 2009 Intel Corporation |
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 Intel Corporation. |
31 | | * |
32 | | * Contributors(s): |
33 | | * Chris Wilson <chris@chris-wilson.co.uk> |
34 | | */ |
35 | | |
36 | | #include "cairoint.h" |
37 | | #include "cairo-device-private.h" |
38 | | #include "cairo-error-private.h" |
39 | | |
40 | | /** |
41 | | * SECTION:cairo-device |
42 | | * @Title: cairo_device_t |
43 | | * @Short_Description: interface to underlying rendering system |
44 | | * @See_Also: #cairo_surface_t |
45 | | * |
46 | | * Devices are the abstraction Cairo employs for the rendering system |
47 | | * used by a #cairo_surface_t. You can get the device of a surface using |
48 | | * cairo_surface_get_device(). |
49 | | * |
50 | | * Devices are created using custom functions specific to the rendering |
51 | | * system you want to use. See the documentation for the surface types |
52 | | * for those functions. |
53 | | * |
54 | | * An important function that devices fulfill is sharing access to the |
55 | | * rendering system between Cairo and your application. If you want to |
56 | | * access a device directly that you used to draw to with Cairo, you must |
57 | | * first call cairo_device_flush() to ensure that Cairo finishes all |
58 | | * operations on the device and resets it to a clean state. |
59 | | * |
60 | | * Cairo also provides the functions cairo_device_acquire() and |
61 | | * cairo_device_release() to synchronize access to the rendering system |
62 | | * in a multithreaded environment. This is done internally, but can also |
63 | | * be used by applications. |
64 | | * |
65 | | * Putting this all together, a function that works with devices should |
66 | | * look something like this: |
67 | | * <informalexample><programlisting> |
68 | | * void |
69 | | * my_device_modifying_function (cairo_device_t *device) |
70 | | * { |
71 | | * cairo_status_t status; |
72 | | * |
73 | | * // Ensure the device is properly reset |
74 | | * cairo_device_flush (device); |
75 | | * // Try to acquire the device |
76 | | * status = cairo_device_acquire (device); |
77 | | * if (status != CAIRO_STATUS_SUCCESS) { |
78 | | * printf ("Failed to acquire the device: %s\n", cairo_status_to_string (status)); |
79 | | * return; |
80 | | * } |
81 | | * |
82 | | * // Do the custom operations on the device here. |
83 | | * // But do not call any Cairo functions that might acquire devices. |
84 | | * |
85 | | * // Release the device when done. |
86 | | * cairo_device_release (device); |
87 | | * } |
88 | | * </programlisting></informalexample> |
89 | | * |
90 | | * <note><para>Please refer to the documentation of each backend for |
91 | | * additional usage requirements, guarantees provided, and |
92 | | * interactions with existing surface API of the device functions for |
93 | | * surfaces of that type. |
94 | | * </para></note> |
95 | | **/ |
96 | | |
97 | | static const cairo_device_t _nil_device = { |
98 | | CAIRO_REFERENCE_COUNT_INVALID, |
99 | | CAIRO_STATUS_NO_MEMORY, |
100 | | }; |
101 | | |
102 | | static const cairo_device_t _mismatch_device = { |
103 | | CAIRO_REFERENCE_COUNT_INVALID, |
104 | | CAIRO_STATUS_DEVICE_TYPE_MISMATCH, |
105 | | }; |
106 | | |
107 | | static const cairo_device_t _invalid_device = { |
108 | | CAIRO_REFERENCE_COUNT_INVALID, |
109 | | CAIRO_STATUS_DEVICE_ERROR, |
110 | | }; |
111 | | |
112 | | cairo_device_t * |
113 | | _cairo_device_create_in_error (cairo_status_t status) |
114 | 0 | { |
115 | 0 | switch (status) { |
116 | 0 | case CAIRO_STATUS_NO_MEMORY: |
117 | 0 | return (cairo_device_t *) &_nil_device; |
118 | 0 | case CAIRO_STATUS_DEVICE_ERROR: |
119 | 0 | return (cairo_device_t *) &_invalid_device; |
120 | 0 | case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: |
121 | 0 | return (cairo_device_t *) &_mismatch_device; |
122 | | |
123 | 0 | case CAIRO_STATUS_SUCCESS: |
124 | 0 | case CAIRO_STATUS_LAST_STATUS: |
125 | 0 | ASSERT_NOT_REACHED; |
126 | | /* fall-through */ |
127 | 0 | case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: |
128 | 0 | case CAIRO_STATUS_INVALID_STATUS: |
129 | 0 | case CAIRO_STATUS_INVALID_FORMAT: |
130 | 0 | case CAIRO_STATUS_INVALID_VISUAL: |
131 | 0 | case CAIRO_STATUS_READ_ERROR: |
132 | 0 | case CAIRO_STATUS_WRITE_ERROR: |
133 | 0 | case CAIRO_STATUS_FILE_NOT_FOUND: |
134 | 0 | case CAIRO_STATUS_TEMP_FILE_ERROR: |
135 | 0 | case CAIRO_STATUS_INVALID_STRIDE: |
136 | 0 | case CAIRO_STATUS_INVALID_SIZE: |
137 | 0 | case CAIRO_STATUS_INVALID_RESTORE: |
138 | 0 | case CAIRO_STATUS_INVALID_POP_GROUP: |
139 | 0 | case CAIRO_STATUS_NO_CURRENT_POINT: |
140 | 0 | case CAIRO_STATUS_INVALID_MATRIX: |
141 | 0 | case CAIRO_STATUS_NULL_POINTER: |
142 | 0 | case CAIRO_STATUS_INVALID_STRING: |
143 | 0 | case CAIRO_STATUS_INVALID_PATH_DATA: |
144 | 0 | case CAIRO_STATUS_SURFACE_FINISHED: |
145 | 0 | case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: |
146 | 0 | case CAIRO_STATUS_INVALID_DASH: |
147 | 0 | case CAIRO_STATUS_INVALID_DSC_COMMENT: |
148 | 0 | case CAIRO_STATUS_INVALID_INDEX: |
149 | 0 | case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: |
150 | 0 | case CAIRO_STATUS_FONT_TYPE_MISMATCH: |
151 | 0 | case CAIRO_STATUS_USER_FONT_IMMUTABLE: |
152 | 0 | case CAIRO_STATUS_USER_FONT_ERROR: |
153 | 0 | case CAIRO_STATUS_NEGATIVE_COUNT: |
154 | 0 | case CAIRO_STATUS_INVALID_CLUSTERS: |
155 | 0 | case CAIRO_STATUS_INVALID_SLANT: |
156 | 0 | case CAIRO_STATUS_INVALID_WEIGHT: |
157 | 0 | case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: |
158 | 0 | case CAIRO_STATUS_INVALID_CONTENT: |
159 | 0 | case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: |
160 | 0 | case CAIRO_STATUS_DEVICE_FINISHED: |
161 | 0 | case CAIRO_STATUS_JBIG2_GLOBAL_MISSING: |
162 | 0 | case CAIRO_STATUS_PNG_ERROR: |
163 | 0 | case CAIRO_STATUS_FREETYPE_ERROR: |
164 | 0 | case CAIRO_STATUS_WIN32_GDI_ERROR: |
165 | 0 | case CAIRO_STATUS_TAG_ERROR: |
166 | 0 | case CAIRO_STATUS_DWRITE_ERROR: |
167 | 0 | default: |
168 | 0 | _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); |
169 | 0 | return (cairo_device_t *) &_nil_device; |
170 | 0 | } |
171 | 0 | } |
172 | | |
173 | | void |
174 | | _cairo_device_init (cairo_device_t *device, |
175 | | const cairo_device_backend_t *backend) |
176 | 0 | { |
177 | 0 | CAIRO_REFERENCE_COUNT_INIT (&device->ref_count, 1); |
178 | 0 | device->status = CAIRO_STATUS_SUCCESS; |
179 | |
|
180 | 0 | device->backend = backend; |
181 | |
|
182 | 0 | CAIRO_RECURSIVE_MUTEX_INIT (device->mutex); |
183 | 0 | device->mutex_depth = 0; |
184 | |
|
185 | 0 | device->finished = FALSE; |
186 | |
|
187 | 0 | _cairo_user_data_array_init (&device->user_data); |
188 | 0 | } |
189 | | |
190 | | /** |
191 | | * cairo_device_reference: |
192 | | * @device: a #cairo_device_t |
193 | | * |
194 | | * Increases the reference count on @device by one. This prevents |
195 | | * @device from being destroyed until a matching call to |
196 | | * cairo_device_destroy() is made. |
197 | | * |
198 | | * Use cairo_device_get_reference_count() to get the number of references |
199 | | * to a #cairo_device_t. |
200 | | * |
201 | | * Return value: the referenced #cairo_device_t. |
202 | | * |
203 | | * Since: 1.10 |
204 | | **/ |
205 | | cairo_device_t * |
206 | | cairo_device_reference (cairo_device_t *device) |
207 | 2.41M | { |
208 | 2.41M | if (device == NULL || |
209 | 2.41M | CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) |
210 | 2.41M | { |
211 | 2.41M | return device; |
212 | 2.41M | } |
213 | | |
214 | 0 | assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); |
215 | 0 | _cairo_reference_count_inc (&device->ref_count); |
216 | |
|
217 | 0 | return device; |
218 | 0 | } |
219 | | slim_hidden_def (cairo_device_reference); |
220 | | |
221 | | /** |
222 | | * cairo_device_status: |
223 | | * @device: a #cairo_device_t |
224 | | * |
225 | | * Checks whether an error has previously occurred for this |
226 | | * device. |
227 | | * |
228 | | * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if |
229 | | * the device is in an error state. |
230 | | * |
231 | | * Since: 1.10 |
232 | | **/ |
233 | | cairo_status_t |
234 | | cairo_device_status (cairo_device_t *device) |
235 | 0 | { |
236 | 0 | if (device == NULL) |
237 | 0 | return CAIRO_STATUS_NULL_POINTER; |
238 | | |
239 | 0 | return device->status; |
240 | 0 | } |
241 | | |
242 | | /** |
243 | | * cairo_device_flush: |
244 | | * @device: a #cairo_device_t |
245 | | * |
246 | | * Finish any pending operations for the device and also restore any |
247 | | * temporary modifications cairo has made to the device's state. |
248 | | * This function must be called before switching from using the |
249 | | * device with Cairo to operating on it directly with native APIs. |
250 | | * If the device doesn't support direct access, then this function |
251 | | * does nothing. |
252 | | * |
253 | | * This function may acquire devices. |
254 | | * |
255 | | * Since: 1.10 |
256 | | **/ |
257 | | void |
258 | | cairo_device_flush (cairo_device_t *device) |
259 | 0 | { |
260 | 0 | cairo_status_t status; |
261 | |
|
262 | 0 | if (device == NULL || device->status) |
263 | 0 | return; |
264 | | |
265 | 0 | if (device->finished) |
266 | 0 | return; |
267 | | |
268 | 0 | if (device->backend->flush != NULL) { |
269 | 0 | status = device->backend->flush (device); |
270 | 0 | if (unlikely (status)) |
271 | 0 | status = _cairo_device_set_error (device, status); |
272 | 0 | } |
273 | 0 | } |
274 | | slim_hidden_def (cairo_device_flush); |
275 | | |
276 | | /** |
277 | | * cairo_device_finish: |
278 | | * @device: the #cairo_device_t to finish |
279 | | * |
280 | | * This function finishes the device and drops all references to |
281 | | * external resources. All surfaces, fonts and other objects created |
282 | | * for this @device will be finished, too. |
283 | | * Further operations on the @device will not affect the @device but |
284 | | * will instead trigger a %CAIRO_STATUS_DEVICE_FINISHED error. |
285 | | * |
286 | | * When the last call to cairo_device_destroy() decreases the |
287 | | * reference count to zero, cairo will call cairo_device_finish() if |
288 | | * it hasn't been called already, before freeing the resources |
289 | | * associated with the device. |
290 | | * |
291 | | * This function may acquire devices. |
292 | | * |
293 | | * Since: 1.10 |
294 | | **/ |
295 | | void |
296 | | cairo_device_finish (cairo_device_t *device) |
297 | 0 | { |
298 | 0 | if (device == NULL || |
299 | 0 | CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) |
300 | 0 | { |
301 | 0 | return; |
302 | 0 | } |
303 | | |
304 | 0 | if (device->finished) |
305 | 0 | return; |
306 | | |
307 | 0 | cairo_device_flush (device); |
308 | |
|
309 | 0 | if (device->backend->finish != NULL) |
310 | 0 | device->backend->finish (device); |
311 | | |
312 | | /* We only finish the device after the backend's callback returns because |
313 | | * the device might still be needed during the callback |
314 | | * (e.g. for cairo_device_acquire ()). |
315 | | */ |
316 | 0 | device->finished = TRUE; |
317 | 0 | } |
318 | | slim_hidden_def (cairo_device_finish); |
319 | | |
320 | | /** |
321 | | * cairo_device_destroy: |
322 | | * @device: a #cairo_device_t |
323 | | * |
324 | | * Decreases the reference count on @device by one. If the result is |
325 | | * zero, then @device and all associated resources are freed. See |
326 | | * cairo_device_reference(). |
327 | | * |
328 | | * This function may acquire devices if the last reference was dropped. |
329 | | * |
330 | | * Since: 1.10 |
331 | | **/ |
332 | | void |
333 | | cairo_device_destroy (cairo_device_t *device) |
334 | 0 | { |
335 | 0 | cairo_user_data_array_t user_data; |
336 | |
|
337 | 0 | if (device == NULL || |
338 | 0 | CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) |
339 | 0 | { |
340 | 0 | return; |
341 | 0 | } |
342 | | |
343 | 0 | assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); |
344 | 0 | if (! _cairo_reference_count_dec_and_test (&device->ref_count)) |
345 | 0 | return; |
346 | | |
347 | 0 | cairo_device_finish (device); |
348 | |
|
349 | 0 | assert (device->mutex_depth == 0); |
350 | 0 | CAIRO_MUTEX_FINI (device->mutex); |
351 | |
|
352 | 0 | user_data = device->user_data; |
353 | |
|
354 | 0 | device->backend->destroy (device); |
355 | |
|
356 | 0 | _cairo_user_data_array_fini (&user_data); |
357 | |
|
358 | 0 | } |
359 | | slim_hidden_def (cairo_device_destroy); |
360 | | |
361 | | /** |
362 | | * cairo_device_get_type: |
363 | | * @device: a #cairo_device_t |
364 | | * |
365 | | * This function returns the type of the device. See #cairo_device_type_t |
366 | | * for available types. |
367 | | * |
368 | | * Return value: The type of @device. |
369 | | * |
370 | | * Since: 1.10 |
371 | | **/ |
372 | | cairo_device_type_t |
373 | | cairo_device_get_type (cairo_device_t *device) |
374 | 0 | { |
375 | 0 | if (device == NULL || |
376 | 0 | CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) |
377 | 0 | { |
378 | 0 | return CAIRO_DEVICE_TYPE_INVALID; |
379 | 0 | } |
380 | | |
381 | 0 | return device->backend->type; |
382 | 0 | } |
383 | | |
384 | | /** |
385 | | * cairo_device_acquire: |
386 | | * @device: a #cairo_device_t |
387 | | * |
388 | | * Acquires the @device for the current thread. This function will block |
389 | | * until no other thread has acquired the device. |
390 | | * |
391 | | * If the return value is %CAIRO_STATUS_SUCCESS, you successfully acquired the |
392 | | * device. From now on your thread owns the device and no other thread will be |
393 | | * able to acquire it until a matching call to cairo_device_release(). It is |
394 | | * allowed to recursively acquire the device multiple times from the same |
395 | | * thread. |
396 | | * |
397 | | * <note><para>You must never acquire two different devices at the same time |
398 | | * unless this is explicitly allowed. Otherwise the possibility of deadlocks |
399 | | * exist. |
400 | | * |
401 | | * As various Cairo functions can acquire devices when called, these functions |
402 | | * may also cause deadlocks when you call them with an acquired device. So you |
403 | | * must not have a device acquired when calling them. These functions are |
404 | | * marked in the documentation. |
405 | | * </para></note> |
406 | | * |
407 | | * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if |
408 | | * the device is in an error state and could not be |
409 | | * acquired. After a successful call to cairo_device_acquire(), |
410 | | * a matching call to cairo_device_release() is required. |
411 | | * |
412 | | * Since: 1.10 |
413 | | **/ |
414 | | cairo_status_t |
415 | | cairo_device_acquire (cairo_device_t *device) |
416 | 0 | { |
417 | 0 | if (device == NULL) |
418 | 0 | return CAIRO_STATUS_SUCCESS; |
419 | | |
420 | 0 | if (unlikely (device->status)) |
421 | 0 | return device->status; |
422 | | |
423 | 0 | if (unlikely (device->finished)) |
424 | 0 | return _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_FINISHED); |
425 | | |
426 | 0 | CAIRO_MUTEX_LOCK (device->mutex); |
427 | 0 | if (device->mutex_depth++ == 0) { |
428 | 0 | if (device->backend->lock != NULL) |
429 | 0 | device->backend->lock (device); |
430 | 0 | } |
431 | |
|
432 | 0 | return CAIRO_STATUS_SUCCESS; |
433 | 0 | } |
434 | | slim_hidden_def (cairo_device_acquire); |
435 | | |
436 | | /** |
437 | | * cairo_device_release: |
438 | | * @device: a #cairo_device_t |
439 | | * |
440 | | * Releases a @device previously acquired using cairo_device_acquire(). See |
441 | | * that function for details. |
442 | | * |
443 | | * Since: 1.10 |
444 | | **/ |
445 | | void |
446 | | cairo_device_release (cairo_device_t *device) |
447 | 0 | { |
448 | 0 | if (device == NULL) |
449 | 0 | return; |
450 | | |
451 | 0 | assert (device->mutex_depth > 0); |
452 | | |
453 | 0 | if (--device->mutex_depth == 0) { |
454 | 0 | if (device->backend->unlock != NULL) |
455 | 0 | device->backend->unlock (device); |
456 | 0 | } |
457 | |
|
458 | 0 | CAIRO_MUTEX_UNLOCK (device->mutex); |
459 | 0 | } |
460 | | slim_hidden_def (cairo_device_release); |
461 | | |
462 | | cairo_status_t |
463 | | _cairo_device_set_error (cairo_device_t *device, |
464 | | cairo_status_t status) |
465 | 0 | { |
466 | 0 | if (status == CAIRO_STATUS_SUCCESS) |
467 | 0 | return CAIRO_STATUS_SUCCESS; |
468 | | |
469 | 0 | _cairo_status_set_error (&device->status, status); |
470 | | |
471 | 0 | return _cairo_error (status); |
472 | 0 | } |
473 | | |
474 | | /** |
475 | | * cairo_device_get_reference_count: |
476 | | * @device: a #cairo_device_t |
477 | | * |
478 | | * Returns the current reference count of @device. |
479 | | * |
480 | | * Return value: the current reference count of @device. If the |
481 | | * object is a nil object, 0 will be returned. |
482 | | * |
483 | | * Since: 1.10 |
484 | | **/ |
485 | | unsigned int |
486 | | cairo_device_get_reference_count (cairo_device_t *device) |
487 | 0 | { |
488 | 0 | if (device == NULL || |
489 | 0 | CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) |
490 | 0 | return 0; |
491 | | |
492 | 0 | return CAIRO_REFERENCE_COUNT_GET_VALUE (&device->ref_count); |
493 | 0 | } |
494 | | |
495 | | /** |
496 | | * cairo_device_get_user_data: |
497 | | * @device: a #cairo_device_t |
498 | | * @key: the address of the #cairo_user_data_key_t the user data was |
499 | | * attached to |
500 | | * |
501 | | * Return user data previously attached to @device using the |
502 | | * specified key. If no user data has been attached with the given |
503 | | * key this function returns %NULL. |
504 | | * |
505 | | * Return value: the user data previously attached or %NULL. |
506 | | * |
507 | | * Since: 1.10 |
508 | | **/ |
509 | | void * |
510 | | cairo_device_get_user_data (cairo_device_t *device, |
511 | | const cairo_user_data_key_t *key) |
512 | 0 | { |
513 | 0 | return _cairo_user_data_array_get_data (&device->user_data, |
514 | 0 | key); |
515 | 0 | } |
516 | | |
517 | | /** |
518 | | * cairo_device_set_user_data: |
519 | | * @device: a #cairo_device_t |
520 | | * @key: the address of a #cairo_user_data_key_t to attach the user data to |
521 | | * @user_data: the user data to attach to the #cairo_device_t |
522 | | * @destroy: a #cairo_destroy_func_t which will be called when the |
523 | | * #cairo_t is destroyed or when new user data is attached using the |
524 | | * same key. |
525 | | * |
526 | | * Attach user data to @device. To remove user data from a surface, |
527 | | * call this function with the key that was used to set it and %NULL |
528 | | * for @data. |
529 | | * |
530 | | * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a |
531 | | * slot could not be allocated for the user data. |
532 | | * |
533 | | * Since: 1.10 |
534 | | **/ |
535 | | cairo_status_t |
536 | | cairo_device_set_user_data (cairo_device_t *device, |
537 | | const cairo_user_data_key_t *key, |
538 | | void *user_data, |
539 | | cairo_destroy_func_t destroy) |
540 | 0 | { |
541 | 0 | if (CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) |
542 | 0 | return device->status; |
543 | | |
544 | 0 | return _cairo_user_data_array_set_data (&device->user_data, |
545 | 0 | key, user_data, destroy); |
546 | 0 | } |