Coverage Report

Created: 2025-07-07 10:01

/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
}