Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/cairo/src/cairo-gstate.c
Line
Count
Source (jump to first uncovered line)
1
/* cairo - a vector graphics library with display and print output
2
 *
3
 * Copyright © 2002 University of Southern California
4
 * Copyright © 2005 Red Hat, Inc.
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 University of Southern
32
 * California.
33
 *
34
 * Contributor(s):
35
 *  Carl D. Worth <cworth@cworth.org>
36
 */
37
38
#include "cairoint.h"
39
40
#include "cairo-clip-inline.h"
41
#include "cairo-clip-private.h"
42
#include "cairo-error-private.h"
43
#include "cairo-list-inline.h"
44
#include "cairo-gstate-private.h"
45
#include "cairo-pattern-private.h"
46
#include "cairo-traps-private.h"
47
48
static cairo_status_t
49
_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other);
50
51
static cairo_status_t
52
_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate);
53
54
static cairo_status_t
55
_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate);
56
57
static void
58
_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate);
59
60
static void
61
_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
62
                                           const cairo_glyph_t *glyphs,
63
                                           int                  num_glyphs,
64
             const cairo_text_cluster_t *clusters,
65
             int       num_clusters,
66
             cairo_text_cluster_flags_t cluster_flags,
67
                                           cairo_glyph_t       *transformed_glyphs,
68
             int      *num_transformed_glyphs,
69
             cairo_text_cluster_t *transformed_clusters);
70
71
static void
72
_cairo_gstate_update_device_transform (cairo_observer_t *observer,
73
               void *arg)
74
0
{
75
0
    cairo_gstate_t *gstate = cairo_container_of (observer,
76
0
             cairo_gstate_t,
77
0
             device_transform_observer);
78
79
0
    gstate->is_identity = (_cairo_matrix_is_identity (&gstate->ctm) &&
80
0
         _cairo_matrix_is_identity (&gstate->target->device_transform));
81
0
}
82
83
cairo_status_t
84
_cairo_gstate_init (cairo_gstate_t  *gstate,
85
        cairo_surface_t *target)
86
8.13M
{
87
8.13M
    VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t)));
88
89
8.13M
    gstate->next = NULL;
90
91
8.13M
    gstate->op = CAIRO_GSTATE_OPERATOR_DEFAULT;
92
8.13M
    gstate->opacity = 1.;
93
94
8.13M
    gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT;
95
8.13M
    gstate->antialias = CAIRO_ANTIALIAS_DEFAULT;
96
97
8.13M
    _cairo_stroke_style_init (&gstate->stroke_style);
98
99
8.13M
    gstate->fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT;
100
101
8.13M
    gstate->font_face = NULL;
102
8.13M
    gstate->scaled_font = NULL;
103
8.13M
    gstate->previous_scaled_font = NULL;
104
105
8.13M
    cairo_matrix_init_scale (&gstate->font_matrix,
106
8.13M
           CAIRO_GSTATE_DEFAULT_FONT_SIZE,
107
8.13M
           CAIRO_GSTATE_DEFAULT_FONT_SIZE);
108
109
8.13M
    _cairo_font_options_init_default (&gstate->font_options);
110
111
8.13M
    gstate->clip = NULL;
112
113
8.13M
    gstate->target = cairo_surface_reference (target);
114
8.13M
    gstate->parent_target = NULL;
115
8.13M
    gstate->original_target = cairo_surface_reference (target);
116
117
8.13M
    gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform;
118
8.13M
    cairo_list_add (&gstate->device_transform_observer.link,
119
8.13M
        &gstate->target->device_transform_observers);
120
121
8.13M
    gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform);
122
8.13M
    cairo_matrix_init_identity (&gstate->ctm);
123
8.13M
    gstate->ctm_inverse = gstate->ctm;
124
8.13M
    gstate->source_ctm_inverse = gstate->ctm;
125
126
8.13M
    gstate->source = (cairo_pattern_t *) &_cairo_pattern_black.base;
127
128
    /* Now that the gstate is fully initialized and ready for the eventual
129
     * _cairo_gstate_fini(), we can check for errors (and not worry about
130
     * the resource deallocation). */
131
8.13M
    return target->status;
132
8.13M
}
133
134
/**
135
 * _cairo_gstate_init_copy:
136
 *
137
 * Initialize @gstate by performing a deep copy of state fields from
138
 * @other. Note that gstate->next is not copied but is set to %NULL by
139
 * this function.
140
 **/
141
static cairo_status_t
142
_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other)
143
19.4k
{
144
19.4k
    cairo_status_t status;
145
146
19.4k
    VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t)));
147
148
19.4k
    gstate->op = other->op;
149
19.4k
    gstate->opacity = other->opacity;
150
151
19.4k
    gstate->tolerance = other->tolerance;
152
19.4k
    gstate->antialias = other->antialias;
153
154
19.4k
    status = _cairo_stroke_style_init_copy (&gstate->stroke_style,
155
19.4k
              &other->stroke_style);
156
19.4k
    if (unlikely (status))
157
0
  return status;
158
159
19.4k
    gstate->fill_rule = other->fill_rule;
160
161
19.4k
    gstate->font_face = cairo_font_face_reference (other->font_face);
162
19.4k
    gstate->scaled_font = cairo_scaled_font_reference (other->scaled_font);
163
19.4k
    gstate->previous_scaled_font = cairo_scaled_font_reference (other->previous_scaled_font);
164
165
19.4k
    gstate->font_matrix = other->font_matrix;
166
167
19.4k
    _cairo_font_options_init_copy (&gstate->font_options , &other->font_options);
168
169
19.4k
    gstate->clip = _cairo_clip_copy (other->clip);
170
171
19.4k
    gstate->target = cairo_surface_reference (other->target);
172
    /* parent_target is always set to NULL; it's only ever set by redirect_target */
173
19.4k
    gstate->parent_target = NULL;
174
19.4k
    gstate->original_target = cairo_surface_reference (other->original_target);
175
176
19.4k
    gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform;
177
19.4k
    cairo_list_add (&gstate->device_transform_observer.link,
178
19.4k
        &gstate->target->device_transform_observers);
179
180
19.4k
    gstate->is_identity = other->is_identity;
181
19.4k
    gstate->ctm = other->ctm;
182
19.4k
    gstate->ctm_inverse = other->ctm_inverse;
183
19.4k
    gstate->source_ctm_inverse = other->source_ctm_inverse;
184
185
19.4k
    gstate->source = cairo_pattern_reference (other->source);
186
187
19.4k
    gstate->next = NULL;
188
189
19.4k
    return CAIRO_STATUS_SUCCESS;
190
19.4k
}
191
192
void
193
_cairo_gstate_fini (cairo_gstate_t *gstate)
194
8.15M
{
195
8.15M
    _cairo_stroke_style_fini (&gstate->stroke_style);
196
197
8.15M
    cairo_font_face_destroy (gstate->font_face);
198
8.15M
    gstate->font_face = NULL;
199
200
8.15M
    cairo_scaled_font_destroy (gstate->previous_scaled_font);
201
8.15M
    gstate->previous_scaled_font = NULL;
202
203
8.15M
    cairo_scaled_font_destroy (gstate->scaled_font);
204
8.15M
    gstate->scaled_font = NULL;
205
206
8.15M
    _cairo_clip_destroy (gstate->clip);
207
208
8.15M
    cairo_list_del (&gstate->device_transform_observer.link);
209
210
8.15M
    cairo_surface_destroy (gstate->target);
211
8.15M
    gstate->target = NULL;
212
213
8.15M
    cairo_surface_destroy (gstate->parent_target);
214
8.15M
    gstate->parent_target = NULL;
215
216
8.15M
    cairo_surface_destroy (gstate->original_target);
217
8.15M
    gstate->original_target = NULL;
218
219
8.15M
    cairo_pattern_destroy (gstate->source);
220
8.15M
    gstate->source = NULL;
221
222
8.15M
    VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t)));
223
8.15M
}
224
225
/**
226
 * _cairo_gstate_save:
227
 * @gstate: input/output gstate pointer
228
 *
229
 * Makes a copy of the current state of @gstate and saves it
230
 * to @gstate->next, then put the address of the newly allcated
231
 * copy into @gstate.  _cairo_gstate_restore() reverses this.
232
 **/
233
cairo_status_t
234
_cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist)
235
19.4k
{
236
19.4k
    cairo_gstate_t *top;
237
19.4k
    cairo_status_t status;
238
239
19.4k
    if (CAIRO_INJECT_FAULT ())
240
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
241
242
19.4k
    top = *freelist;
243
19.4k
    if (top == NULL) {
244
1
  top = _cairo_malloc (sizeof (cairo_gstate_t));
245
1
  if (unlikely (top == NULL))
246
0
      return _cairo_error (CAIRO_STATUS_NO_MEMORY);
247
1
    } else
248
19.4k
  *freelist = top->next;
249
250
19.4k
    status = _cairo_gstate_init_copy (top, *gstate);
251
19.4k
    if (unlikely (status)) {
252
0
  top->next = *freelist;
253
0
  *freelist = top;
254
0
  return status;
255
0
    }
256
257
19.4k
    top->next = *gstate;
258
19.4k
    *gstate = top;
259
260
19.4k
    return CAIRO_STATUS_SUCCESS;
261
19.4k
}
262
263
/**
264
 * _cairo_gstate_restore:
265
 * @gstate: input/output gstate pointer
266
 *
267
 * Reverses the effects of one _cairo_gstate_save() call.
268
 **/
269
cairo_status_t
270
_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist)
271
19.4k
{
272
19.4k
    cairo_gstate_t *top;
273
274
19.4k
    top = *gstate;
275
19.4k
    if (top->next == NULL)
276
0
  return _cairo_error (CAIRO_STATUS_INVALID_RESTORE);
277
278
19.4k
    *gstate = top->next;
279
280
19.4k
    _cairo_gstate_fini (top);
281
19.4k
    VG (VALGRIND_MAKE_MEM_UNDEFINED (&top->next, sizeof (cairo_gstate_t *)));
282
19.4k
    top->next = *freelist;
283
19.4k
    *freelist = top;
284
285
19.4k
    return CAIRO_STATUS_SUCCESS;
286
19.4k
}
287
288
/**
289
 * _cairo_gstate_redirect_target:
290
 * @gstate: a #cairo_gstate_t
291
 * @child: the new child target
292
 *
293
 * Redirect @gstate rendering to a "child" target. The original
294
 * "parent" target with which the gstate was created will not be
295
 * affected. See _cairo_gstate_get_target().
296
 **/
297
cairo_status_t
298
_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child)
299
0
{
300
    /* If this gstate is already redirected, this is an error; we need a
301
     * new gstate to be able to redirect */
302
0
    assert (gstate->parent_target == NULL);
303
304
    /* Set up our new parent_target based on our current target;
305
     * gstate->parent_target will take the ref that is held by gstate->target
306
     */
307
0
    gstate->parent_target = gstate->target;
308
309
    /* Now set up our new target; we overwrite gstate->target directly,
310
     * since its ref is now owned by gstate->parent_target */
311
0
    gstate->target = cairo_surface_reference (child);
312
0
    gstate->is_identity &= _cairo_matrix_is_identity (&child->device_transform);
313
0
    cairo_list_move (&gstate->device_transform_observer.link,
314
0
         &gstate->target->device_transform_observers);
315
316
    /* The clip is in surface backend coordinates for the previous target;
317
     * translate it into the child's backend coordinates. */
318
0
    _cairo_clip_destroy (gstate->clip);
319
0
    gstate->clip = _cairo_clip_copy_with_translation (gstate->next->clip,
320
0
                  child->device_transform.x0 - gstate->parent_target->device_transform.x0,
321
0
                  child->device_transform.y0 - gstate->parent_target->device_transform.y0);
322
323
0
    return CAIRO_STATUS_SUCCESS;
324
0
}
325
326
/**
327
 * _cairo_gstate_is_group:
328
 * @gstate: a #cairo_gstate_t
329
 *
330
 * Check if _cairo_gstate_redirect_target has been called on the head
331
 * of the stack.
332
 *
333
 * Return value: %TRUE if @gstate is redirected to a target different
334
 * than the previous state in the stack, %FALSE otherwise.
335
 **/
336
cairo_bool_t
337
_cairo_gstate_is_group (cairo_gstate_t *gstate)
338
19.4k
{
339
19.4k
    return gstate->parent_target != NULL;
340
19.4k
}
341
342
/**
343
 * _cairo_gstate_get_target:
344
 * @gstate: a #cairo_gstate_t
345
 *
346
 * Return the current drawing target; if drawing is not redirected,
347
 * this will be the same as _cairo_gstate_get_original_target().
348
 *
349
 * Return value: the current target surface
350
 **/
351
cairo_surface_t *
352
_cairo_gstate_get_target (cairo_gstate_t *gstate)
353
0
{
354
0
    return gstate->target;
355
0
}
356
357
/**
358
 * _cairo_gstate_get_original_target:
359
 * @gstate: a #cairo_gstate_t
360
 *
361
 * Return the original target with which @gstate was created. This
362
 * function always returns the original target independent of any
363
 * child target that may have been set with
364
 * _cairo_gstate_redirect_target.
365
 *
366
 * Return value: the original target surface
367
 **/
368
cairo_surface_t *
369
_cairo_gstate_get_original_target (cairo_gstate_t *gstate)
370
8.61M
{
371
8.61M
    return gstate->original_target;
372
8.61M
}
373
374
/**
375
 * _cairo_gstate_get_clip:
376
 * @gstate: a #cairo_gstate_t
377
 *
378
 * This space left intentionally blank.
379
 *
380
 * Return value: a pointer to the gstate's #cairo_clip_t structure.
381
 **/
382
cairo_clip_t *
383
_cairo_gstate_get_clip (cairo_gstate_t *gstate)
384
0
{
385
0
    return gstate->clip;
386
0
}
387
388
cairo_status_t
389
_cairo_gstate_set_source (cairo_gstate_t  *gstate,
390
        cairo_pattern_t *source)
391
6.87M
{
392
6.87M
    if (source->status)
393
0
  return source->status;
394
395
6.87M
    source = cairo_pattern_reference (source);
396
6.87M
    cairo_pattern_destroy (gstate->source);
397
6.87M
    gstate->source = source;
398
6.87M
    gstate->source_ctm_inverse = gstate->ctm_inverse;
399
400
6.87M
    return CAIRO_STATUS_SUCCESS;
401
6.87M
}
402
403
cairo_pattern_t *
404
_cairo_gstate_get_source (cairo_gstate_t *gstate)
405
20.8k
{
406
20.8k
    if (gstate->source == &_cairo_pattern_black.base) {
407
  /* do not expose the static object to the user */
408
0
        gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK);
409
0
    }
410
411
20.8k
    return gstate->source;
412
20.8k
}
413
414
cairo_status_t
415
_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op)
416
7.62M
{
417
7.62M
    gstate->op = op;
418
419
7.62M
    return CAIRO_STATUS_SUCCESS;
420
7.62M
}
421
422
cairo_operator_t
423
_cairo_gstate_get_operator (cairo_gstate_t *gstate)
424
0
{
425
0
    return gstate->op;
426
0
}
427
428
cairo_status_t
429
_cairo_gstate_set_opacity (cairo_gstate_t *gstate, double op)
430
0
{
431
0
    gstate->opacity = op;
432
433
0
    return CAIRO_STATUS_SUCCESS;
434
0
}
435
436
double
437
_cairo_gstate_get_opacity (cairo_gstate_t *gstate)
438
0
{
439
0
    return gstate->opacity;
440
0
}
441
442
cairo_status_t
443
_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance)
444
0
{
445
0
    gstate->tolerance = tolerance;
446
447
0
    return CAIRO_STATUS_SUCCESS;
448
0
}
449
450
double
451
_cairo_gstate_get_tolerance (cairo_gstate_t *gstate)
452
677
{
453
677
    return gstate->tolerance;
454
677
}
455
456
cairo_status_t
457
_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule)
458
7.60M
{
459
7.60M
    gstate->fill_rule = fill_rule;
460
461
7.60M
    return CAIRO_STATUS_SUCCESS;
462
7.60M
}
463
464
cairo_fill_rule_t
465
_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate)
466
0
{
467
0
    return gstate->fill_rule;
468
0
}
469
470
cairo_status_t
471
_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width)
472
7.66M
{
473
7.66M
    if (gstate->stroke_style.is_hairline)
474
0
  gstate->stroke_style.pre_hairline_line_width = width;
475
7.66M
  else
476
7.66M
  gstate->stroke_style.line_width = width;
477
478
7.66M
    return CAIRO_STATUS_SUCCESS;
479
7.66M
}
480
481
double
482
_cairo_gstate_get_line_width (cairo_gstate_t *gstate)
483
0
{
484
0
    return gstate->stroke_style.line_width;
485
0
}
486
487
cairo_status_t
488
_cairo_gstate_set_hairline (cairo_gstate_t *gstate, cairo_bool_t set_hairline)
489
0
{
490
0
    if (gstate->stroke_style.is_hairline != set_hairline) {
491
0
        gstate->stroke_style.is_hairline = set_hairline;
492
493
0
        if (set_hairline) {
494
0
            gstate->stroke_style.pre_hairline_line_width = gstate->stroke_style.line_width;
495
0
            gstate->stroke_style.line_width = 0.0;
496
0
        } else {
497
0
            gstate->stroke_style.line_width = gstate->stroke_style.pre_hairline_line_width;
498
0
        }
499
0
    }
500
501
0
    return CAIRO_STATUS_SUCCESS;
502
0
}
503
504
cairo_bool_t
505
_cairo_gstate_get_hairline (cairo_gstate_t *gstate)
506
0
{
507
0
    return gstate->stroke_style.is_hairline;
508
0
}
509
510
cairo_status_t
511
_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap)
512
55.7k
{
513
55.7k
    gstate->stroke_style.line_cap = line_cap;
514
515
55.7k
    return CAIRO_STATUS_SUCCESS;
516
55.7k
}
517
518
cairo_line_cap_t
519
_cairo_gstate_get_line_cap (cairo_gstate_t *gstate)
520
0
{
521
0
    return gstate->stroke_style.line_cap;
522
0
}
523
524
cairo_status_t
525
_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join)
526
55.7k
{
527
55.7k
    gstate->stroke_style.line_join = line_join;
528
529
55.7k
    return CAIRO_STATUS_SUCCESS;
530
55.7k
}
531
532
cairo_line_join_t
533
_cairo_gstate_get_line_join (cairo_gstate_t *gstate)
534
0
{
535
0
    return gstate->stroke_style.line_join;
536
0
}
537
538
cairo_status_t
539
_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset)
540
0
{
541
0
    double dash_total, on_total, off_total;
542
0
    int i, j;
543
544
0
    free (gstate->stroke_style.dash);
545
546
0
    gstate->stroke_style.num_dashes = num_dashes;
547
548
0
    if (gstate->stroke_style.num_dashes == 0) {
549
0
  gstate->stroke_style.dash = NULL;
550
0
  gstate->stroke_style.dash_offset = 0.0;
551
0
  return CAIRO_STATUS_SUCCESS;
552
0
    }
553
554
0
    gstate->stroke_style.dash = _cairo_malloc_ab (gstate->stroke_style.num_dashes, sizeof (double));
555
0
    if (unlikely (gstate->stroke_style.dash == NULL)) {
556
0
  gstate->stroke_style.num_dashes = 0;
557
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
558
0
    }
559
560
0
    on_total = off_total = dash_total = 0.0;
561
0
    for (i = j = 0; i < num_dashes; i++) {
562
0
  if (dash[i] < 0)
563
0
      return _cairo_error (CAIRO_STATUS_INVALID_DASH);
564
565
0
  if (dash[i] == 0 && i > 0 && i < num_dashes - 1) {
566
0
      if (dash[++i] < 0)
567
0
    return _cairo_error (CAIRO_STATUS_INVALID_DASH);
568
569
0
      gstate->stroke_style.dash[j-1] += dash[i];
570
0
      gstate->stroke_style.num_dashes -= 2;
571
0
  } else
572
0
      gstate->stroke_style.dash[j++] = dash[i];
573
574
0
  if (dash[i]) {
575
0
      dash_total += dash[i];
576
0
      if ((i & 1) == 0)
577
0
    on_total += dash[i];
578
0
      else
579
0
    off_total += dash[i];
580
0
  }
581
0
    }
582
583
0
    if (dash_total == 0.0)
584
0
  return _cairo_error (CAIRO_STATUS_INVALID_DASH);
585
586
    /* An odd dash value indicate symmetric repeating, so the total
587
     * is twice as long. */
588
0
    if (gstate->stroke_style.num_dashes & 1) {
589
0
  dash_total *= 2;
590
0
  on_total += off_total;
591
0
    }
592
593
0
    if (dash_total - on_total < CAIRO_FIXED_ERROR_DOUBLE) {
594
  /* Degenerate dash -> solid line */
595
0
  free (gstate->stroke_style.dash);
596
0
  gstate->stroke_style.dash = NULL;
597
0
  gstate->stroke_style.num_dashes = 0;
598
0
  gstate->stroke_style.dash_offset = 0.0;
599
0
  return CAIRO_STATUS_SUCCESS;
600
0
    }
601
602
    /* The dashing code doesn't like a negative offset or a big positive
603
     * offset, so we compute an equivalent offset which is guaranteed to be
604
     * positive and less than twice the pattern length. */
605
0
    offset = fmod (offset, dash_total);
606
0
    if (offset < 0.0)
607
0
  offset += dash_total;
608
0
    if (offset <= 0.0)   /* Take care of -0 */
609
0
  offset = 0.0;
610
0
    gstate->stroke_style.dash_offset = offset;
611
612
0
    return CAIRO_STATUS_SUCCESS;
613
0
}
614
615
void
616
_cairo_gstate_get_dash (cairo_gstate_t *gstate,
617
      double         *dashes,
618
      int            *num_dashes,
619
      double         *offset)
620
0
{
621
0
    if (dashes) {
622
0
  memcpy (dashes,
623
0
    gstate->stroke_style.dash,
624
0
    sizeof (double) * gstate->stroke_style.num_dashes);
625
0
    }
626
627
0
    if (num_dashes)
628
0
  *num_dashes = gstate->stroke_style.num_dashes;
629
630
0
    if (offset)
631
0
  *offset = gstate->stroke_style.dash_offset;
632
0
}
633
634
cairo_status_t
635
_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit)
636
55.7k
{
637
55.7k
    gstate->stroke_style.miter_limit = limit;
638
639
55.7k
    return CAIRO_STATUS_SUCCESS;
640
55.7k
}
641
642
double
643
_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate)
644
0
{
645
0
    return gstate->stroke_style.miter_limit;
646
0
}
647
648
void
649
_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix)
650
0
{
651
0
    *matrix = gstate->ctm;
652
0
}
653
654
cairo_status_t
655
_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty)
656
20.8k
{
657
20.8k
    cairo_matrix_t tmp;
658
659
20.8k
    if (! ISFINITE (tx) || ! ISFINITE (ty))
660
0
  return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
661
662
20.8k
    _cairo_gstate_unset_scaled_font (gstate);
663
664
20.8k
    cairo_matrix_init_translate (&tmp, tx, ty);
665
20.8k
    cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm);
666
20.8k
    gstate->is_identity = FALSE;
667
668
    /* paranoid check against gradual numerical instability */
669
20.8k
    if (! _cairo_matrix_is_invertible (&gstate->ctm))
670
0
  return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
671
672
20.8k
    cairo_matrix_init_translate (&tmp, -tx, -ty);
673
20.8k
    cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
674
675
20.8k
    return CAIRO_STATUS_SUCCESS;
676
20.8k
}
677
678
cairo_status_t
679
_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy)
680
20.8k
{
681
20.8k
    cairo_matrix_t tmp;
682
683
20.8k
    if (sx * sy == 0.) /* either sx or sy is 0, or det == 0 due to underflow */
684
0
  return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
685
20.8k
    if (! ISFINITE (sx) || ! ISFINITE (sy))
686
0
  return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
687
688
20.8k
    _cairo_gstate_unset_scaled_font (gstate);
689
690
20.8k
    cairo_matrix_init_scale (&tmp, sx, sy);
691
20.8k
    cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm);
692
20.8k
    gstate->is_identity = FALSE;
693
694
    /* paranoid check against gradual numerical instability */
695
20.8k
    if (! _cairo_matrix_is_invertible (&gstate->ctm))
696
0
  return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
697
698
20.8k
    cairo_matrix_init_scale (&tmp, 1/sx, 1/sy);
699
20.8k
    cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
700
701
20.8k
    return CAIRO_STATUS_SUCCESS;
702
20.8k
}
703
704
cairo_status_t
705
_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle)
706
0
{
707
0
    cairo_matrix_t tmp;
708
709
0
    if (angle == 0.)
710
0
  return CAIRO_STATUS_SUCCESS;
711
712
0
    if (! ISFINITE (angle))
713
0
  return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
714
715
0
    _cairo_gstate_unset_scaled_font (gstate);
716
717
0
    cairo_matrix_init_rotate (&tmp, angle);
718
0
    cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm);
719
0
    gstate->is_identity = FALSE;
720
721
    /* paranoid check against gradual numerical instability */
722
0
    if (! _cairo_matrix_is_invertible (&gstate->ctm))
723
0
  return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
724
725
0
    cairo_matrix_init_rotate (&tmp, -angle);
726
0
    cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
727
728
0
    return CAIRO_STATUS_SUCCESS;
729
0
}
730
731
cairo_status_t
732
_cairo_gstate_transform (cairo_gstate_t       *gstate,
733
       const cairo_matrix_t *matrix)
734
0
{
735
0
    cairo_matrix_t tmp;
736
0
    cairo_status_t status;
737
738
0
    if (! _cairo_matrix_is_invertible (matrix))
739
0
  return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
740
741
0
    if (_cairo_matrix_is_identity (matrix))
742
0
  return CAIRO_STATUS_SUCCESS;
743
744
0
    tmp = *matrix;
745
0
    status = cairo_matrix_invert (&tmp);
746
0
    if (unlikely (status))
747
0
  return status;
748
749
0
    _cairo_gstate_unset_scaled_font (gstate);
750
751
0
    cairo_matrix_multiply (&gstate->ctm, matrix, &gstate->ctm);
752
0
    cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp);
753
0
    gstate->is_identity = FALSE;
754
755
    /* paranoid check against gradual numerical instability */
756
0
    if (! _cairo_matrix_is_invertible (&gstate->ctm))
757
0
  return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
758
759
0
    return CAIRO_STATUS_SUCCESS;
760
0
}
761
762
cairo_status_t
763
_cairo_gstate_set_matrix (cairo_gstate_t       *gstate,
764
        const cairo_matrix_t *matrix)
765
848k
{
766
848k
    cairo_status_t status;
767
768
848k
    if (memcmp (matrix, &gstate->ctm, sizeof (cairo_matrix_t)) == 0)
769
0
  return CAIRO_STATUS_SUCCESS;
770
771
848k
    if (! _cairo_matrix_is_invertible (matrix))
772
1.32k
  return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
773
774
846k
    if (_cairo_matrix_is_identity (matrix)) {
775
0
  _cairo_gstate_identity_matrix (gstate);
776
0
  return CAIRO_STATUS_SUCCESS;
777
0
    }
778
779
846k
    _cairo_gstate_unset_scaled_font (gstate);
780
781
846k
    gstate->ctm = *matrix;
782
846k
    gstate->ctm_inverse = *matrix;
783
846k
    status = cairo_matrix_invert (&gstate->ctm_inverse);
784
846k
    assert (status == CAIRO_STATUS_SUCCESS);
785
846k
    gstate->is_identity = FALSE;
786
787
846k
    return CAIRO_STATUS_SUCCESS;
788
846k
}
789
790
void
791
_cairo_gstate_identity_matrix (cairo_gstate_t *gstate)
792
7.60M
{
793
7.60M
    if (_cairo_matrix_is_identity (&gstate->ctm))
794
7.60M
  return;
795
796
1
    _cairo_gstate_unset_scaled_font (gstate);
797
798
1
    cairo_matrix_init_identity (&gstate->ctm);
799
1
    cairo_matrix_init_identity (&gstate->ctm_inverse);
800
1
    gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform);
801
1
}
802
803
void
804
_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y)
805
0
{
806
0
    cairo_matrix_transform_point (&gstate->ctm, x, y);
807
0
}
808
809
void
810
_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate,
811
               double *dx, double *dy)
812
0
{
813
0
    cairo_matrix_transform_distance (&gstate->ctm, dx, dy);
814
0
}
815
816
void
817
_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y)
818
0
{
819
0
    cairo_matrix_transform_point (&gstate->ctm_inverse, x, y);
820
0
}
821
822
void
823
_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate,
824
               double *dx, double *dy)
825
0
{
826
0
    cairo_matrix_transform_distance (&gstate->ctm_inverse, dx, dy);
827
0
}
828
829
void
830
_do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y)
831
6.16M
{
832
6.16M
    cairo_matrix_transform_point (&gstate->ctm, x, y);
833
6.16M
    cairo_matrix_transform_point (&gstate->target->device_transform, x, y);
834
6.16M
}
835
836
void
837
_do_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y)
838
0
{
839
0
    cairo_matrix_transform_distance (&gstate->ctm, x, y);
840
0
    cairo_matrix_transform_distance (&gstate->target->device_transform, x, y);
841
0
}
842
843
void
844
_do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y)
845
0
{
846
0
    cairo_matrix_transform_point (&gstate->target->device_transform_inverse, x, y);
847
0
    cairo_matrix_transform_point (&gstate->ctm_inverse, x, y);
848
0
}
849
850
void
851
_do_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y)
852
0
{
853
0
    cairo_matrix_transform_distance (&gstate->target->device_transform_inverse, x, y);
854
0
    cairo_matrix_transform_distance (&gstate->ctm_inverse, x, y);
855
0
}
856
857
void
858
_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate,
859
                                         double *x1, double *y1,
860
                                         double *x2, double *y2,
861
                                         cairo_bool_t *is_tight)
862
14.1M
{
863
14.1M
    cairo_matrix_t matrix_inverse;
864
865
14.1M
    if (! _cairo_matrix_is_identity (&gstate->target->device_transform_inverse) ||
866
14.1M
    ! _cairo_matrix_is_identity (&gstate->ctm_inverse))
867
1.69M
    {
868
1.69M
  cairo_matrix_multiply (&matrix_inverse,
869
1.69M
             &gstate->target->device_transform_inverse,
870
1.69M
             &gstate->ctm_inverse);
871
1.69M
  _cairo_matrix_transform_bounding_box (&matrix_inverse,
872
1.69M
                x1, y1, x2, y2, is_tight);
873
1.69M
    }
874
875
12.4M
    else
876
12.4M
    {
877
12.4M
  if (is_tight)
878
0
      *is_tight = TRUE;
879
12.4M
    }
880
14.1M
}
881
882
/* XXX: NYI
883
cairo_status_t
884
_cairo_gstate_stroke_to_path (cairo_gstate_t *gstate)
885
{
886
    cairo_status_t status;
887
888
    _cairo_pen_init (&gstate);
889
    return CAIRO_STATUS_SUCCESS;
890
}
891
*/
892
893
void
894
_cairo_gstate_path_extents (cairo_gstate_t     *gstate,
895
          cairo_path_fixed_t *path,
896
          double *x1, double *y1,
897
          double *x2, double *y2)
898
7.58M
{
899
7.58M
    cairo_box_t box;
900
7.58M
    double px1, py1, px2, py2;
901
902
7.58M
    if (_cairo_path_fixed_extents (path, &box)) {
903
6.59M
  px1 = _cairo_fixed_to_double (box.p1.x);
904
6.59M
  py1 = _cairo_fixed_to_double (box.p1.y);
905
6.59M
  px2 = _cairo_fixed_to_double (box.p2.x);
906
6.59M
  py2 = _cairo_fixed_to_double (box.p2.y);
907
908
6.59M
  _cairo_gstate_backend_to_user_rectangle (gstate,
909
6.59M
             &px1, &py1, &px2, &py2,
910
6.59M
             NULL);
911
6.59M
    } else {
912
992k
  px1 = 0.0;
913
992k
  py1 = 0.0;
914
992k
  px2 = 0.0;
915
992k
  py2 = 0.0;
916
992k
    }
917
918
7.58M
    if (x1)
919
7.58M
  *x1 = px1;
920
7.58M
    if (y1)
921
7.58M
  *y1 = py1;
922
7.58M
    if (x2)
923
7.58M
  *x2 = px2;
924
7.58M
    if (y2)
925
7.58M
  *y2 = py2;
926
7.58M
}
927
928
static void
929
_cairo_gstate_copy_pattern (cairo_pattern_t *pattern,
930
          const cairo_pattern_t *original)
931
8.49M
{
932
    /* First check if the we can replace the original with a much simpler
933
     * pattern. For example, gradients that are uniform or just have a single
934
     * stop can sometimes be replaced with a solid.
935
     */
936
937
8.49M
    if (_cairo_pattern_is_clear (original)) {
938
85
        _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern,
939
85
           CAIRO_COLOR_TRANSPARENT);
940
85
  return;
941
85
    }
942
943
8.49M
    if (original->type == CAIRO_PATTERN_TYPE_LINEAR ||
944
8.49M
  original->type == CAIRO_PATTERN_TYPE_RADIAL)
945
6.37k
    {
946
6.37k
        cairo_color_t color;
947
6.37k
  if (_cairo_gradient_pattern_is_solid ((cairo_gradient_pattern_t *) original,
948
6.37k
                NULL,
949
6.37k
                &color))
950
3.25k
  {
951
3.25k
      _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern,
952
3.25k
               &color);
953
3.25k
      return;
954
3.25k
  }
955
6.37k
    }
956
957
8.49M
    _cairo_pattern_init_static_copy (pattern, original);
958
8.49M
}
959
960
static void
961
_cairo_gstate_copy_transformed_pattern (cairo_gstate_t  *gstate,
962
          cairo_pattern_t *pattern,
963
          const cairo_pattern_t *original,
964
          const cairo_matrix_t  *ctm_inverse)
965
8.49M
{
966
    /*
967
     * What calculations below do can be described in pseudo-code (so using nonexistent fields) as (using column vectors):
968
     * pattern->matrix = surface->device_transform *
969
     *       pattern->matrix *
970
     *       ctm_inverse *
971
     *       gstate->target->device_transform_inverse
972
     *
973
     * The inverse of which is:
974
     * pattern->matrix_inverse = gstate->target->device_transform *
975
     *         ctm *
976
     *         pattern->matrix_inverse *
977
     *         surface->device_transform_inverse
978
     */
979
980
8.49M
    _cairo_gstate_copy_pattern (pattern, original);
981
982
8.49M
    if (original->type == CAIRO_PATTERN_TYPE_SURFACE) {
983
542k
  cairo_surface_pattern_t *surface_pattern;
984
542k
  cairo_surface_t *surface;
985
986
542k
        surface_pattern = (cairo_surface_pattern_t *) original;
987
542k
        surface = surface_pattern->surface;
988
989
542k
  if (_cairo_surface_has_device_transform (surface))
990
0
      _cairo_pattern_pretransform (pattern, &surface->device_transform);
991
542k
    }
992
993
8.49M
    if (! _cairo_matrix_is_identity (ctm_inverse))
994
37.5k
  _cairo_pattern_transform (pattern, ctm_inverse);
995
996
8.49M
    if (_cairo_surface_has_device_transform (gstate->target)) {
997
0
        _cairo_pattern_transform (pattern,
998
0
                                  &gstate->target->device_transform_inverse);
999
0
    }
1000
8.49M
}
1001
1002
static void
1003
_cairo_gstate_copy_transformed_source (cairo_gstate_t   *gstate,
1004
               cairo_pattern_t  *pattern)
1005
8.49M
{
1006
8.49M
    _cairo_gstate_copy_transformed_pattern (gstate, pattern,
1007
8.49M
              gstate->source,
1008
8.49M
              &gstate->source_ctm_inverse);
1009
8.49M
}
1010
1011
static void
1012
_cairo_gstate_copy_transformed_mask (cairo_gstate_t   *gstate,
1013
             cairo_pattern_t  *pattern,
1014
             cairo_pattern_t  *mask)
1015
4
{
1016
4
    _cairo_gstate_copy_transformed_pattern (gstate, pattern,
1017
4
              mask,
1018
4
              &gstate->ctm_inverse);
1019
4
}
1020
1021
static cairo_operator_t
1022
_reduce_op (cairo_gstate_t *gstate)
1023
7.66M
{
1024
7.66M
    cairo_operator_t op;
1025
7.66M
    const cairo_pattern_t *pattern;
1026
1027
7.66M
    op = gstate->op;
1028
7.66M
    if (op != CAIRO_OPERATOR_SOURCE)
1029
7.64M
  return op;
1030
1031
18.3k
    pattern = gstate->source;
1032
18.3k
    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
1033
0
  const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
1034
0
  if (solid->color.alpha_short <= 0x00ff) {
1035
0
      op = CAIRO_OPERATOR_CLEAR;
1036
0
  } else if ((gstate->target->content & CAIRO_CONTENT_ALPHA) == 0) {
1037
0
      if ((solid->color.red_short |
1038
0
     solid->color.green_short |
1039
0
     solid->color.blue_short) <= 0x00ff)
1040
0
      {
1041
0
    op = CAIRO_OPERATOR_CLEAR;
1042
0
      }
1043
0
  }
1044
18.3k
    } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
1045
18.3k
  const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern;
1046
18.3k
  if (surface->surface->is_clear &&
1047
18.3k
      surface->surface->content & CAIRO_CONTENT_ALPHA)
1048
0
  {
1049
0
      op = CAIRO_OPERATOR_CLEAR;
1050
0
  }
1051
18.3k
    } else {
1052
0
  const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
1053
0
  if (gradient->n_stops == 0)
1054
0
      op = CAIRO_OPERATOR_CLEAR;
1055
0
    }
1056
1057
18.3k
    return op;
1058
7.66M
}
1059
1060
static cairo_status_t
1061
_cairo_gstate_get_pattern_status (const cairo_pattern_t *pattern)
1062
10.6M
{
1063
10.6M
    if (unlikely (pattern->type == CAIRO_PATTERN_TYPE_MESH &&
1064
10.6M
      ((const cairo_mesh_pattern_t *) pattern)->current_patch))
1065
0
    {
1066
  /* If current patch != NULL, the pattern is under construction
1067
   * and cannot be used as a source */
1068
0
  return CAIRO_STATUS_INVALID_MESH_CONSTRUCTION;
1069
0
    }
1070
1071
10.6M
    return pattern->status;
1072
10.6M
}
1073
1074
cairo_status_t
1075
_cairo_gstate_paint (cairo_gstate_t *gstate)
1076
543k
{
1077
543k
    cairo_pattern_union_t source_pattern;
1078
543k
    const cairo_pattern_t *pattern;
1079
543k
    cairo_status_t status;
1080
543k
    cairo_operator_t op;
1081
1082
543k
    status = _cairo_gstate_get_pattern_status (gstate->source);
1083
543k
    if (unlikely (status))
1084
0
  return status;
1085
1086
543k
    if (gstate->op == CAIRO_OPERATOR_DEST)
1087
0
  return CAIRO_STATUS_SUCCESS;
1088
1089
543k
    if (_cairo_clip_is_all_clipped (gstate->clip))
1090
505
  return CAIRO_STATUS_SUCCESS;
1091
1092
542k
    op = _reduce_op (gstate);
1093
542k
    if (op == CAIRO_OPERATOR_CLEAR) {
1094
0
  pattern = &_cairo_pattern_clear.base;
1095
542k
    } else {
1096
542k
  _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
1097
542k
  pattern = &source_pattern.base;
1098
542k
    }
1099
1100
542k
    return _cairo_surface_paint (gstate->target,
1101
542k
         op, pattern,
1102
542k
         gstate->clip);
1103
543k
}
1104
1105
cairo_status_t
1106
_cairo_gstate_mask (cairo_gstate_t  *gstate,
1107
        cairo_pattern_t *mask)
1108
4
{
1109
4
    cairo_pattern_union_t source_pattern, mask_pattern;
1110
4
    const cairo_pattern_t *source;
1111
4
    cairo_operator_t op;
1112
4
    cairo_status_t status;
1113
1114
4
    status = _cairo_gstate_get_pattern_status (mask);
1115
4
    if (unlikely (status))
1116
0
  return status;
1117
1118
4
    status = _cairo_gstate_get_pattern_status (gstate->source);
1119
4
    if (unlikely (status))
1120
0
  return status;
1121
1122
4
    if (gstate->op == CAIRO_OPERATOR_DEST)
1123
0
  return CAIRO_STATUS_SUCCESS;
1124
1125
4
    if (_cairo_clip_is_all_clipped (gstate->clip))
1126
0
  return CAIRO_STATUS_SUCCESS;
1127
1128
4
    assert (gstate->opacity == 1.0);
1129
1130
4
    if (_cairo_pattern_is_opaque (mask, NULL))
1131
0
  return _cairo_gstate_paint (gstate);
1132
1133
4
    if (_cairo_pattern_is_clear (mask) &&
1134
4
  _cairo_operator_bounded_by_mask (gstate->op))
1135
0
    {
1136
0
  return CAIRO_STATUS_SUCCESS;
1137
0
    }
1138
1139
4
    op = _reduce_op (gstate);
1140
4
    if (op == CAIRO_OPERATOR_CLEAR) {
1141
0
  source = &_cairo_pattern_clear.base;
1142
4
    } else {
1143
4
  _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
1144
4
  source = &source_pattern.base;
1145
4
    }
1146
4
    _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask);
1147
1148
4
    if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
1149
4
  mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
1150
4
  _cairo_operator_bounded_by_source (op))
1151
0
    {
1152
0
  const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
1153
0
  cairo_color_t combined;
1154
1155
0
  if (mask_pattern.base.has_component_alpha) {
1156
0
#define M(R, A, B, c) R.c = A.c * B.c
1157
0
      M(combined, solid->color, mask_pattern.solid.color, red);
1158
0
      M(combined, solid->color, mask_pattern.solid.color, green);
1159
0
      M(combined, solid->color, mask_pattern.solid.color, blue);
1160
0
      M(combined, solid->color, mask_pattern.solid.color, alpha);
1161
0
#undef M
1162
0
  } else {
1163
0
      combined = solid->color;
1164
0
      _cairo_color_multiply_alpha (&combined, mask_pattern.solid.color.alpha);
1165
0
  }
1166
1167
0
  _cairo_pattern_init_solid (&source_pattern.solid, &combined);
1168
1169
0
  status = _cairo_surface_paint (gstate->target, op,
1170
0
               &source_pattern.base,
1171
0
               gstate->clip);
1172
0
    }
1173
4
    else
1174
4
    {
1175
4
  status = _cairo_surface_mask (gstate->target, op,
1176
4
              source,
1177
4
              &mask_pattern.base,
1178
4
              gstate->clip);
1179
4
    }
1180
1181
4
    return status;
1182
4
}
1183
1184
cairo_status_t
1185
_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
1186
835k
{
1187
835k
    cairo_pattern_union_t source_pattern;
1188
835k
    cairo_stroke_style_t style;
1189
835k
    double dash[2];
1190
835k
    cairo_status_t status;
1191
835k
    cairo_matrix_t aggregate_transform;
1192
835k
    cairo_matrix_t aggregate_transform_inverse;
1193
1194
835k
    status = _cairo_gstate_get_pattern_status (gstate->source);
1195
835k
    if (unlikely (status))
1196
0
  return status;
1197
1198
835k
    if (gstate->op == CAIRO_OPERATOR_DEST)
1199
0
  return CAIRO_STATUS_SUCCESS;
1200
1201
835k
    if (gstate->stroke_style.line_width <= 0.0 && !gstate->stroke_style.is_hairline)
1202
259
  return CAIRO_STATUS_SUCCESS;
1203
1204
834k
    if (_cairo_clip_is_all_clipped (gstate->clip))
1205
0
  return CAIRO_STATUS_SUCCESS;
1206
1207
834k
    assert (gstate->opacity == 1.0);
1208
1209
834k
    cairo_matrix_multiply (&aggregate_transform,
1210
834k
                           &gstate->ctm,
1211
834k
                           &gstate->target->device_transform);
1212
834k
    cairo_matrix_multiply (&aggregate_transform_inverse,
1213
834k
                           &gstate->target->device_transform_inverse,
1214
834k
                           &gstate->ctm_inverse);
1215
1216
834k
    memcpy (&style, &gstate->stroke_style, sizeof (gstate->stroke_style));
1217
834k
    if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &aggregate_transform, gstate->tolerance)) {
1218
0
        style.dash = dash;
1219
0
        _cairo_stroke_style_dash_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance,
1220
0
                &style.dash_offset,
1221
0
                style.dash,
1222
0
                &style.num_dashes);
1223
0
    }
1224
1225
834k
    _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
1226
1227
834k
    return _cairo_surface_stroke (gstate->target,
1228
834k
          gstate->op,
1229
834k
          &source_pattern.base,
1230
834k
          path,
1231
834k
          &style,
1232
834k
          &aggregate_transform,
1233
834k
          &aggregate_transform_inverse,
1234
834k
          gstate->tolerance,
1235
834k
          gstate->antialias,
1236
834k
          gstate->clip);
1237
834k
}
1238
1239
cairo_status_t
1240
_cairo_gstate_in_stroke (cairo_gstate_t     *gstate,
1241
       cairo_path_fixed_t *path,
1242
       double        x,
1243
       double        y,
1244
       cairo_bool_t     *inside_ret)
1245
0
{
1246
0
    cairo_status_t status;
1247
0
    cairo_rectangle_int_t extents;
1248
0
    cairo_box_t limit;
1249
0
    cairo_traps_t traps;
1250
1251
0
    if (gstate->stroke_style.line_width <= 0.0) {
1252
0
  *inside_ret = FALSE;
1253
0
  return CAIRO_STATUS_SUCCESS;
1254
0
    }
1255
1256
0
    _cairo_gstate_user_to_backend (gstate, &x, &y);
1257
1258
    /* Before we perform the expensive stroke analysis,
1259
     * check whether the point is within the extents of the path.
1260
     */
1261
0
    _cairo_path_fixed_approximate_stroke_extents (path,
1262
0
              &gstate->stroke_style,
1263
0
              &gstate->ctm,
1264
0
              gstate->target->is_vector,
1265
0
              &extents);
1266
0
    if (x < extents.x || x > extents.x + extents.width ||
1267
0
  y < extents.y || y > extents.y + extents.height)
1268
0
    {
1269
0
  *inside_ret = FALSE;
1270
0
  return CAIRO_STATUS_SUCCESS;
1271
0
    }
1272
1273
0
    limit.p1.x = _cairo_fixed_from_double (x) - 1;
1274
0
    limit.p1.y = _cairo_fixed_from_double (y) - 1;
1275
0
    limit.p2.x = limit.p1.x + 2;
1276
0
    limit.p2.y = limit.p1.y + 2;
1277
1278
0
    _cairo_traps_init (&traps);
1279
0
    _cairo_traps_limit (&traps, &limit, 1);
1280
1281
0
    status = _cairo_path_fixed_stroke_polygon_to_traps (path,
1282
0
              &gstate->stroke_style,
1283
0
              &gstate->ctm,
1284
0
              &gstate->ctm_inverse,
1285
0
              gstate->tolerance,
1286
0
              &traps);
1287
0
    if (unlikely (status))
1288
0
  goto BAIL;
1289
1290
0
    *inside_ret = _cairo_traps_contain (&traps, x, y);
1291
1292
0
BAIL:
1293
0
    _cairo_traps_fini (&traps);
1294
1295
0
    return status;
1296
0
}
1297
1298
cairo_status_t
1299
_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
1300
6.72M
{
1301
6.72M
    cairo_status_t status;
1302
1303
6.72M
    status = _cairo_gstate_get_pattern_status (gstate->source);
1304
6.72M
    if (unlikely (status))
1305
0
  return status;
1306
1307
6.72M
    if (gstate->op == CAIRO_OPERATOR_DEST)
1308
0
  return CAIRO_STATUS_SUCCESS;
1309
1310
6.72M
    if (_cairo_clip_is_all_clipped (gstate->clip))
1311
0
  return CAIRO_STATUS_SUCCESS;
1312
1313
6.72M
    assert (gstate->opacity == 1.0);
1314
1315
6.72M
    if (_cairo_path_fixed_fill_is_empty (path)) {
1316
6.22k
  if (_cairo_operator_bounded_by_mask (gstate->op))
1317
6.22k
      return CAIRO_STATUS_SUCCESS;
1318
1319
0
  status = _cairo_surface_paint (gstate->target,
1320
0
               CAIRO_OPERATOR_CLEAR,
1321
0
               &_cairo_pattern_clear.base,
1322
0
               gstate->clip);
1323
6.72M
    } else {
1324
6.72M
  cairo_pattern_union_t source_pattern;
1325
6.72M
  const cairo_pattern_t *pattern;
1326
6.72M
  cairo_operator_t op;
1327
6.72M
  cairo_rectangle_int_t extents;
1328
6.72M
  cairo_box_t box;
1329
1330
6.72M
  op = _reduce_op (gstate);
1331
6.72M
  if (op == CAIRO_OPERATOR_CLEAR) {
1332
0
      pattern = &_cairo_pattern_clear.base;
1333
6.72M
  } else {
1334
6.72M
      _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
1335
6.72M
      pattern = &source_pattern.base;
1336
6.72M
  }
1337
1338
  /* Toolkits often paint the entire background with a fill */
1339
6.72M
  if (_cairo_surface_get_extents (gstate->target, &extents) &&
1340
6.72M
      _cairo_path_fixed_is_box (path, &box) &&
1341
6.72M
      box.p1.x <= _cairo_fixed_from_int (extents.x) &&
1342
6.72M
      box.p1.y <= _cairo_fixed_from_int (extents.y) &&
1343
6.72M
      box.p2.x >= _cairo_fixed_from_int (extents.x + extents.width) &&
1344
6.72M
      box.p2.y >= _cairo_fixed_from_int (extents.y + extents.height))
1345
146k
  {
1346
146k
      status = _cairo_surface_paint (gstate->target, op, pattern,
1347
146k
             gstate->clip);
1348
146k
  }
1349
6.57M
  else
1350
6.57M
  {
1351
6.57M
      status = _cairo_surface_fill (gstate->target, op, pattern,
1352
6.57M
            path,
1353
6.57M
            gstate->fill_rule,
1354
6.57M
            gstate->tolerance,
1355
6.57M
            gstate->antialias,
1356
6.57M
            gstate->clip);
1357
6.57M
  }
1358
6.72M
    }
1359
1360
6.72M
    return status;
1361
6.72M
}
1362
1363
cairo_bool_t
1364
_cairo_gstate_in_fill (cairo_gstate_t   *gstate,
1365
           cairo_path_fixed_t *path,
1366
           double      x,
1367
           double      y)
1368
0
{
1369
0
    _cairo_gstate_user_to_backend (gstate, &x, &y);
1370
1371
0
    return _cairo_path_fixed_in_fill (path,
1372
0
              gstate->fill_rule,
1373
0
              gstate->tolerance,
1374
0
              x, y);
1375
0
}
1376
1377
cairo_bool_t
1378
_cairo_gstate_in_clip (cairo_gstate_t   *gstate,
1379
           double      x,
1380
           double      y)
1381
0
{
1382
0
    cairo_clip_t *clip = gstate->clip;
1383
0
    int i;
1384
1385
0
    if (_cairo_clip_is_all_clipped (clip))
1386
0
  return FALSE;
1387
1388
0
    if (clip == NULL)
1389
0
  return TRUE;
1390
1391
0
    _cairo_gstate_user_to_backend (gstate, &x, &y);
1392
1393
0
    if (x <  clip->extents.x ||
1394
0
  x >= clip->extents.x + clip->extents.width ||
1395
0
  y <  clip->extents.y ||
1396
0
  y >= clip->extents.y + clip->extents.height)
1397
0
    {
1398
0
  return FALSE;
1399
0
    }
1400
1401
0
    if (clip->num_boxes) {
1402
0
  int fx, fy;
1403
1404
0
  fx = _cairo_fixed_from_double (x);
1405
0
  fy = _cairo_fixed_from_double (y);
1406
0
  for (i = 0; i < clip->num_boxes; i++) {
1407
0
      if (fx >= clip->boxes[i].p1.x && fx <= clip->boxes[i].p2.x &&
1408
0
    fy >= clip->boxes[i].p1.y && fy <= clip->boxes[i].p2.y)
1409
0
    break;
1410
0
  }
1411
0
  if (i == clip->num_boxes)
1412
0
      return FALSE;
1413
0
    }
1414
1415
0
    if (clip->path) {
1416
0
  cairo_clip_path_t *clip_path = clip->path;
1417
0
  do {
1418
0
      if (! _cairo_path_fixed_in_fill (&clip_path->path,
1419
0
               clip_path->fill_rule,
1420
0
               clip_path->tolerance,
1421
0
               x, y))
1422
0
    return FALSE;
1423
0
  } while ((clip_path = clip_path->prev) != NULL);
1424
0
    }
1425
1426
0
    return TRUE;
1427
0
}
1428
1429
cairo_status_t
1430
_cairo_gstate_copy_page (cairo_gstate_t *gstate)
1431
0
{
1432
0
    cairo_surface_copy_page (gstate->target);
1433
0
    return cairo_surface_status (gstate->target);
1434
0
}
1435
1436
cairo_status_t
1437
_cairo_gstate_show_page (cairo_gstate_t *gstate)
1438
0
{
1439
0
    cairo_surface_show_page (gstate->target);
1440
0
    return cairo_surface_status (gstate->target);
1441
0
}
1442
1443
static void
1444
_cairo_gstate_extents_to_user_rectangle (cairo_gstate_t   *gstate,
1445
           const cairo_box_t *extents,
1446
           double *x1, double *y1,
1447
           double *x2, double *y2)
1448
0
{
1449
0
    double px1, py1, px2, py2;
1450
1451
0
    px1 = _cairo_fixed_to_double (extents->p1.x);
1452
0
    py1 = _cairo_fixed_to_double (extents->p1.y);
1453
0
    px2 = _cairo_fixed_to_double (extents->p2.x);
1454
0
    py2 = _cairo_fixed_to_double (extents->p2.y);
1455
1456
0
    _cairo_gstate_backend_to_user_rectangle (gstate,
1457
0
               &px1, &py1, &px2, &py2,
1458
0
               NULL);
1459
0
    if (x1)
1460
0
  *x1 = px1;
1461
0
    if (y1)
1462
0
  *y1 = py1;
1463
0
    if (x2)
1464
0
  *x2 = px2;
1465
0
    if (y2)
1466
0
  *y2 = py2;
1467
0
}
1468
1469
cairo_status_t
1470
_cairo_gstate_stroke_extents (cairo_gstate_t   *gstate,
1471
            cairo_path_fixed_t *path,
1472
                              double *x1, double *y1,
1473
            double *x2, double *y2)
1474
0
{
1475
0
    cairo_int_status_t status;
1476
0
    cairo_box_t extents;
1477
0
    cairo_bool_t empty;
1478
1479
0
    if (x1)
1480
0
  *x1 = 0.0;
1481
0
    if (y1)
1482
0
  *y1 = 0.0;
1483
0
    if (x2)
1484
0
  *x2 = 0.0;
1485
0
    if (y2)
1486
0
  *y2 = 0.0;
1487
1488
0
    if (gstate->stroke_style.line_width <= 0.0)
1489
0
  return CAIRO_STATUS_SUCCESS;
1490
1491
0
    status = CAIRO_INT_STATUS_UNSUPPORTED;
1492
0
    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
1493
0
  cairo_boxes_t boxes;
1494
1495
0
  _cairo_boxes_init (&boxes);
1496
0
  status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
1497
0
                &gstate->stroke_style,
1498
0
                &gstate->ctm,
1499
0
                gstate->antialias,
1500
0
                &boxes);
1501
0
  empty = boxes.num_boxes == 0;
1502
0
  if (! empty)
1503
0
      _cairo_boxes_extents (&boxes, &extents);
1504
0
  _cairo_boxes_fini (&boxes);
1505
0
    }
1506
1507
0
    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
1508
0
  cairo_polygon_t polygon;
1509
1510
0
  _cairo_polygon_init (&polygon, NULL, 0);
1511
0
  status = _cairo_path_fixed_stroke_to_polygon (path,
1512
0
                  &gstate->stroke_style,
1513
0
                  &gstate->ctm,
1514
0
                  &gstate->ctm_inverse,
1515
0
                  gstate->tolerance,
1516
0
                  &polygon);
1517
0
  empty = polygon.num_edges == 0;
1518
0
  if (! empty)
1519
0
      extents = polygon.extents;
1520
0
  _cairo_polygon_fini (&polygon);
1521
0
    }
1522
0
    if (! empty) {
1523
0
  _cairo_gstate_extents_to_user_rectangle (gstate, &extents,
1524
0
             x1, y1, x2, y2);
1525
0
    }
1526
1527
0
    return status;
1528
0
}
1529
1530
cairo_status_t
1531
_cairo_gstate_fill_extents (cairo_gstate_t     *gstate,
1532
          cairo_path_fixed_t *path,
1533
                            double *x1, double *y1,
1534
          double *x2, double *y2)
1535
0
{
1536
0
    cairo_status_t status;
1537
0
    cairo_box_t extents;
1538
0
    cairo_bool_t empty;
1539
1540
0
    if (x1)
1541
0
  *x1 = 0.0;
1542
0
    if (y1)
1543
0
  *y1 = 0.0;
1544
0
    if (x2)
1545
0
  *x2 = 0.0;
1546
0
    if (y2)
1547
0
  *y2 = 0.0;
1548
1549
0
    if (_cairo_path_fixed_fill_is_empty (path))
1550
0
  return CAIRO_STATUS_SUCCESS;
1551
1552
0
    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
1553
0
  cairo_boxes_t boxes;
1554
1555
0
  _cairo_boxes_init (&boxes);
1556
0
  status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
1557
0
                    gstate->fill_rule,
1558
0
                    gstate->antialias,
1559
0
                    &boxes);
1560
0
  empty = boxes.num_boxes == 0;
1561
0
  if (! empty)
1562
0
      _cairo_boxes_extents (&boxes, &extents);
1563
1564
0
  _cairo_boxes_fini (&boxes);
1565
0
    } else {
1566
0
  cairo_traps_t traps;
1567
1568
0
  _cairo_traps_init (&traps);
1569
1570
0
  status = _cairo_path_fixed_fill_to_traps (path,
1571
0
              gstate->fill_rule,
1572
0
              gstate->tolerance,
1573
0
              &traps);
1574
0
  empty = traps.num_traps == 0;
1575
0
  if (! empty)
1576
0
      _cairo_traps_extents (&traps, &extents);
1577
1578
0
  _cairo_traps_fini (&traps);
1579
0
    }
1580
1581
0
    if (! empty) {
1582
0
  _cairo_gstate_extents_to_user_rectangle (gstate, &extents,
1583
0
             x1, y1, x2, y2);
1584
0
    }
1585
1586
0
    return status;
1587
0
}
1588
1589
cairo_status_t
1590
_cairo_gstate_reset_clip (cairo_gstate_t *gstate)
1591
1
{
1592
1
    _cairo_clip_destroy (gstate->clip);
1593
1
    gstate->clip = NULL;
1594
1595
1
    return CAIRO_STATUS_SUCCESS;
1596
1
}
1597
1598
cairo_status_t
1599
_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
1600
267k
{
1601
267k
    gstate->clip =
1602
267k
  _cairo_clip_intersect_path (gstate->clip,
1603
267k
            path,
1604
267k
            gstate->fill_rule,
1605
267k
            gstate->tolerance,
1606
267k
            gstate->antialias);
1607
    /* XXX */
1608
267k
    return CAIRO_STATUS_SUCCESS;
1609
267k
}
1610
1611
static cairo_bool_t
1612
_cairo_gstate_int_clip_extents (cairo_gstate_t        *gstate,
1613
        cairo_rectangle_int_t *extents)
1614
10.1M
{
1615
10.1M
    cairo_bool_t is_bounded;
1616
1617
10.1M
    is_bounded = _cairo_surface_get_extents (gstate->target, extents);
1618
1619
10.1M
    if (gstate->clip) {
1620
395k
  _cairo_rectangle_intersect (extents,
1621
395k
            _cairo_clip_get_extents (gstate->clip));
1622
395k
  is_bounded = TRUE;
1623
395k
    }
1624
1625
10.1M
    return is_bounded;
1626
10.1M
}
1627
1628
cairo_bool_t
1629
_cairo_gstate_clip_extents (cairo_gstate_t *gstate,
1630
                double         *x1,
1631
                double         *y1,
1632
          double         *x2,
1633
          double         *y2)
1634
7.58M
{
1635
7.58M
    cairo_rectangle_int_t extents;
1636
7.58M
    double px1, py1, px2, py2;
1637
1638
7.58M
    if (! _cairo_gstate_int_clip_extents (gstate, &extents))
1639
0
  return FALSE;
1640
1641
7.58M
    px1 = extents.x;
1642
7.58M
    py1 = extents.y;
1643
7.58M
    px2 = extents.x + (int) extents.width;
1644
7.58M
    py2 = extents.y + (int) extents.height;
1645
1646
7.58M
    _cairo_gstate_backend_to_user_rectangle (gstate,
1647
7.58M
               &px1, &py1, &px2, &py2,
1648
7.58M
               NULL);
1649
1650
7.58M
    if (x1)
1651
7.58M
  *x1 = px1;
1652
7.58M
    if (y1)
1653
7.58M
  *y1 = py1;
1654
7.58M
    if (x2)
1655
7.58M
  *x2 = px2;
1656
7.58M
    if (y2)
1657
7.58M
  *y2 = py2;
1658
1659
7.58M
    return TRUE;
1660
7.58M
}
1661
1662
cairo_rectangle_list_t*
1663
_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate)
1664
0
{
1665
0
    cairo_rectangle_int_t extents;
1666
0
    cairo_rectangle_list_t *list;
1667
0
    cairo_clip_t *clip;
1668
1669
0
    if (_cairo_surface_get_extents (gstate->target, &extents))
1670
0
  clip = _cairo_clip_copy_intersect_rectangle (gstate->clip, &extents);
1671
0
    else
1672
0
  clip = gstate->clip;
1673
1674
0
    list = _cairo_clip_copy_rectangle_list (clip, gstate);
1675
1676
0
    if (clip != gstate->clip)
1677
0
  _cairo_clip_destroy (clip);
1678
1679
0
    return list;
1680
0
}
1681
1682
cairo_status_t
1683
_cairo_gstate_tag_begin (cairo_gstate_t *gstate,
1684
       const char *tag_name, const char *attributes)
1685
0
{
1686
0
    return _cairo_surface_tag (gstate->target,
1687
0
             TRUE, /* begin */
1688
0
             tag_name,
1689
0
             attributes ? attributes : "");
1690
0
}
1691
1692
cairo_status_t
1693
_cairo_gstate_tag_end (cairo_gstate_t *gstate,
1694
           const char *tag_name)
1695
0
{
1696
0
    return _cairo_surface_tag (gstate->target,
1697
0
             FALSE, /* begin */
1698
0
             tag_name,
1699
0
             NULL); /* attributes */
1700
0
}
1701
1702
static void
1703
_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate)
1704
6.02M
{
1705
6.02M
    if (gstate->scaled_font == NULL)
1706
3.49M
  return;
1707
1708
2.52M
    if (gstate->previous_scaled_font != NULL)
1709
2.52M
  cairo_scaled_font_destroy (gstate->previous_scaled_font);
1710
1711
2.52M
    gstate->previous_scaled_font = gstate->scaled_font;
1712
2.52M
    gstate->scaled_font = NULL;
1713
2.52M
}
1714
1715
cairo_status_t
1716
_cairo_gstate_set_font_size (cairo_gstate_t *gstate,
1717
           double          size)
1718
2.55M
{
1719
2.55M
    _cairo_gstate_unset_scaled_font (gstate);
1720
1721
2.55M
    cairo_matrix_init_scale (&gstate->font_matrix, size, size);
1722
1723
2.55M
    return CAIRO_STATUS_SUCCESS;
1724
2.55M
}
1725
1726
cairo_status_t
1727
_cairo_gstate_set_font_matrix (cairo_gstate_t     *gstate,
1728
             const cairo_matrix_t *matrix)
1729
2.55M
{
1730
2.55M
    if (memcmp (matrix, &gstate->font_matrix, sizeof (cairo_matrix_t)) == 0)
1731
21.8k
  return CAIRO_STATUS_SUCCESS;
1732
1733
2.53M
    _cairo_gstate_unset_scaled_font (gstate);
1734
1735
2.53M
    gstate->font_matrix = *matrix;
1736
1737
2.53M
    return CAIRO_STATUS_SUCCESS;
1738
2.55M
}
1739
1740
void
1741
_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate,
1742
             cairo_matrix_t *matrix)
1743
0
{
1744
0
    *matrix = gstate->font_matrix;
1745
0
}
1746
1747
void
1748
_cairo_gstate_set_font_options (cairo_gstate_t             *gstate,
1749
        const cairo_font_options_t *options)
1750
23.4k
{
1751
23.4k
    if (memcmp (options, &gstate->font_options, sizeof (cairo_font_options_t)) == 0)
1752
0
  return;
1753
1754
23.4k
    _cairo_gstate_unset_scaled_font (gstate);
1755
1756
23.4k
    _cairo_font_options_init_copy (&gstate->font_options, options);
1757
23.4k
}
1758
1759
void
1760
_cairo_gstate_get_font_options (cairo_gstate_t       *gstate,
1761
        cairo_font_options_t *options)
1762
0
{
1763
0
    *options = gstate->font_options;
1764
0
}
1765
1766
cairo_status_t
1767
_cairo_gstate_get_font_face (cairo_gstate_t     *gstate,
1768
           cairo_font_face_t **font_face)
1769
0
{
1770
0
    cairo_status_t status;
1771
1772
0
    status = _cairo_gstate_ensure_font_face (gstate);
1773
0
    if (unlikely (status))
1774
0
  return status;
1775
1776
0
    *font_face = gstate->font_face;
1777
1778
0
    return CAIRO_STATUS_SUCCESS;
1779
0
}
1780
1781
cairo_status_t
1782
_cairo_gstate_get_scaled_font (cairo_gstate_t       *gstate,
1783
             cairo_scaled_font_t **scaled_font)
1784
0
{
1785
0
    cairo_status_t status;
1786
1787
0
    status = _cairo_gstate_ensure_scaled_font (gstate);
1788
0
    if (unlikely (status))
1789
0
  return status;
1790
1791
0
    *scaled_font = gstate->scaled_font;
1792
1793
0
    return CAIRO_STATUS_SUCCESS;
1794
0
}
1795
1796
/*
1797
 * Like everything else in this file, fonts involve Too Many Coordinate Spaces;
1798
 * it is easy to get confused about what's going on.
1799
 *
1800
 * The user's view
1801
 * ---------------
1802
 *
1803
 * Users ask for things in user space. When cairo starts, a user space unit
1804
 * is about 1/96 inch, which is similar to (but importantly different from)
1805
 * the normal "point" units most users think in terms of. When a user
1806
 * selects a font, its scale is set to "one user unit". The user can then
1807
 * independently scale the user coordinate system *or* the font matrix, in
1808
 * order to adjust the rendered size of the font.
1809
 *
1810
 * Metrics are returned in user space, whether they are obtained from
1811
 * the currently selected font in a  #cairo_t or from a #cairo_scaled_font_t
1812
 * which is a font specialized to a particular scale matrix, CTM, and target
1813
 * surface.
1814
 *
1815
 * The font's view
1816
 * ---------------
1817
 *
1818
 * Fonts are designed and stored (in say .ttf files) in "font space", which
1819
 * describes an "EM Square" (a design tile) and has some abstract number
1820
 * such as 1000, 1024, or 2048 units per "EM". This is basically an
1821
 * uninteresting space for us, but we need to remember that it exists.
1822
 *
1823
 * Font resources (from libraries or operating systems) render themselves
1824
 * to a particular device. Since they do not want to make most programmers
1825
 * worry about the font design space, the scaling API is simplified to
1826
 * involve just telling the font the required pixel size of the EM square
1827
 * (that is, in device space).
1828
 *
1829
 *
1830
 * Cairo's gstate view
1831
 * -------------------
1832
 *
1833
 * In addition to the CTM and CTM inverse, we keep a matrix in the gstate
1834
 * called the "font matrix" which describes the user's most recent
1835
 * font-scaling or font-transforming request. This is kept in terms of an
1836
 * abstract scale factor, composed with the CTM and used to set the font's
1837
 * pixel size. So if the user asks to "scale the font by 12", the matrix
1838
 * is:
1839
 *
1840
 *   [ 12.0, 0.0, 0.0, 12.0, 0.0, 0.0 ]
1841
 *
1842
 * It is an affine matrix, like all cairo matrices, where its tx and ty
1843
 * components are used to "nudging" fonts around and are handled in gstate
1844
 * and then ignored by the "scaled-font" layer.
1845
 *
1846
 * In order to perform any action on a font, we must build an object
1847
 * called a #cairo_font_scale_t; this contains the central 2x2 matrix
1848
 * resulting from "font matrix * CTM" (sans the font matrix translation
1849
 * components as stated in the previous paragraph).
1850
 *
1851
 * We pass this to the font when making requests of it, which causes it to
1852
 * reply for a particular [user request, device] combination, under the CTM
1853
 * (to accommodate the "zoom in" == "bigger fonts" issue above).
1854
 *
1855
 * The other terms in our communication with the font are therefore in
1856
 * device space. When we ask it to perform text->glyph conversion, it will
1857
 * produce a glyph string in device space. Glyph vectors we pass to it for
1858
 * measuring or rendering should be in device space. The metrics which we
1859
 * get back from the font will be in device space. The contents of the
1860
 * global glyph image cache will be in device space.
1861
 *
1862
 *
1863
 * Cairo's public view
1864
 * -------------------
1865
 *
1866
 * Since the values entering and leaving via public API calls are in user
1867
 * space, the gstate functions typically need to multiply arguments by the
1868
 * CTM (for user-input glyph vectors), and return values by the CTM inverse
1869
 * (for font responses such as metrics or glyph vectors).
1870
 *
1871
 */
1872
1873
static cairo_status_t
1874
_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate)
1875
2.55M
{
1876
2.55M
    cairo_font_face_t *font_face;
1877
1878
2.55M
    if (gstate->font_face != NULL)
1879
2.55M
  return gstate->font_face->status;
1880
1881
1882
0
    font_face = cairo_toy_font_face_create (CAIRO_FONT_FAMILY_DEFAULT,
1883
0
              CAIRO_FONT_SLANT_DEFAULT,
1884
0
              CAIRO_FONT_WEIGHT_DEFAULT);
1885
0
    if (font_face->status)
1886
0
  return font_face->status;
1887
1888
0
    gstate->font_face = font_face;
1889
1890
0
    return CAIRO_STATUS_SUCCESS;
1891
0
}
1892
1893
static cairo_status_t
1894
_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate)
1895
2.55M
{
1896
2.55M
    cairo_status_t status;
1897
2.55M
    cairo_font_options_t options;
1898
2.55M
    cairo_scaled_font_t *scaled_font;
1899
2.55M
    cairo_matrix_t font_ctm;
1900
1901
2.55M
    if (gstate->scaled_font != NULL)
1902
0
  return gstate->scaled_font->status;
1903
1904
2.55M
    status = _cairo_gstate_ensure_font_face (gstate);
1905
2.55M
    if (unlikely (status))
1906
0
  return status;
1907
1908
2.55M
    cairo_surface_get_font_options (gstate->target, &options);
1909
2.55M
    cairo_font_options_merge (&options, &gstate->font_options);
1910
1911
2.55M
    cairo_matrix_multiply (&font_ctm,
1912
2.55M
         &gstate->ctm,
1913
2.55M
         &gstate->target->device_transform);
1914
1915
2.55M
    scaled_font = cairo_scaled_font_create (gstate->font_face,
1916
2.55M
                    &gstate->font_matrix,
1917
2.55M
              &font_ctm,
1918
2.55M
              &options);
1919
1920
2.55M
    status = cairo_scaled_font_status (scaled_font);
1921
2.55M
    if (unlikely (status))
1922
0
  return status;
1923
1924
2.55M
    gstate->scaled_font = scaled_font;
1925
1926
2.55M
    return CAIRO_STATUS_SUCCESS;
1927
2.55M
}
1928
1929
cairo_status_t
1930
_cairo_gstate_get_font_extents (cairo_gstate_t *gstate,
1931
        cairo_font_extents_t *extents)
1932
0
{
1933
0
    cairo_status_t status = _cairo_gstate_ensure_scaled_font (gstate);
1934
0
    if (unlikely (status))
1935
0
  return status;
1936
1937
0
    cairo_scaled_font_extents (gstate->scaled_font, extents);
1938
1939
0
    return cairo_scaled_font_status (gstate->scaled_font);
1940
0
}
1941
1942
cairo_status_t
1943
_cairo_gstate_set_font_face (cairo_gstate_t    *gstate,
1944
           cairo_font_face_t *font_face)
1945
2.55M
{
1946
2.55M
    if (font_face && font_face->status)
1947
0
  return _cairo_error (font_face->status);
1948
1949
2.55M
    if (font_face == gstate->font_face)
1950
2.52M
  return CAIRO_STATUS_SUCCESS;
1951
1952
23.4k
    cairo_font_face_destroy (gstate->font_face);
1953
23.4k
    gstate->font_face = cairo_font_face_reference (font_face);
1954
1955
23.4k
    _cairo_gstate_unset_scaled_font (gstate);
1956
1957
23.4k
    return CAIRO_STATUS_SUCCESS;
1958
2.55M
}
1959
1960
cairo_status_t
1961
_cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
1962
           const cairo_glyph_t *glyphs,
1963
           int num_glyphs,
1964
           cairo_text_extents_t *extents)
1965
0
{
1966
0
    cairo_status_t status;
1967
1968
0
    status = _cairo_gstate_ensure_scaled_font (gstate);
1969
0
    if (unlikely (status))
1970
0
  return status;
1971
1972
0
    cairo_scaled_font_glyph_extents (gstate->scaled_font,
1973
0
             glyphs, num_glyphs,
1974
0
             extents);
1975
1976
0
    return cairo_scaled_font_status (gstate->scaled_font);
1977
0
}
1978
1979
cairo_status_t
1980
_cairo_gstate_show_text_glyphs (cairo_gstate_t       *gstate,
1981
        const cairo_glyph_t    *glyphs,
1982
        int         num_glyphs,
1983
        cairo_glyph_text_info_t    *info)
1984
2.55M
{
1985
2.55M
    cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
1986
2.55M
    cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
1987
2.55M
    cairo_pattern_union_t source_pattern;
1988
2.55M
    cairo_glyph_t *transformed_glyphs;
1989
2.55M
    const cairo_pattern_t *pattern;
1990
2.55M
    cairo_text_cluster_t *transformed_clusters;
1991
2.55M
    cairo_operator_t op;
1992
2.55M
    cairo_status_t status;
1993
1994
2.55M
    status = _cairo_gstate_get_pattern_status (gstate->source);
1995
2.55M
    if (unlikely (status))
1996
0
  return status;
1997
1998
2.55M
    if (gstate->op == CAIRO_OPERATOR_DEST)
1999
0
  return CAIRO_STATUS_SUCCESS;
2000
2001
2.55M
    if (_cairo_clip_is_all_clipped (gstate->clip))
2002
0
  return CAIRO_STATUS_SUCCESS;
2003
2004
2.55M
    status = _cairo_gstate_ensure_scaled_font (gstate);
2005
2.55M
    if (unlikely (status))
2006
0
  return status;
2007
2008
2.55M
    transformed_glyphs = stack_transformed_glyphs;
2009
2.55M
    transformed_clusters = stack_transformed_clusters;
2010
2011
2.55M
    if (num_glyphs > ARRAY_LENGTH (stack_transformed_glyphs)) {
2012
6.32k
  transformed_glyphs = cairo_glyph_allocate (num_glyphs);
2013
6.32k
  if (unlikely (transformed_glyphs == NULL))
2014
0
      return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2015
6.32k
    }
2016
2017
2.55M
    if (info != NULL) {
2018
0
  if (info->num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) {
2019
0
      transformed_clusters = cairo_text_cluster_allocate (info->num_clusters);
2020
0
      if (unlikely (transformed_clusters == NULL)) {
2021
0
    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2022
0
    goto CLEANUP_GLYPHS;
2023
0
      }
2024
0
  }
2025
2026
0
  _cairo_gstate_transform_glyphs_to_backend (gstate,
2027
0
               glyphs, num_glyphs,
2028
0
               info->clusters,
2029
0
               info->num_clusters,
2030
0
               info->cluster_flags,
2031
0
               transformed_glyphs,
2032
0
               &num_glyphs,
2033
0
               transformed_clusters);
2034
2.55M
    } else {
2035
2.55M
  _cairo_gstate_transform_glyphs_to_backend (gstate,
2036
2.55M
               glyphs, num_glyphs,
2037
2.55M
               NULL, 0, 0,
2038
2.55M
               transformed_glyphs,
2039
2.55M
               &num_glyphs,
2040
2.55M
               NULL);
2041
2.55M
    }
2042
2043
2.55M
    if (num_glyphs == 0)
2044
2.15M
  goto CLEANUP_GLYPHS;
2045
2046
396k
    op = _reduce_op (gstate);
2047
396k
    if (op == CAIRO_OPERATOR_CLEAR) {
2048
0
  pattern = &_cairo_pattern_clear.base;
2049
396k
    } else {
2050
396k
  _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
2051
396k
  pattern = &source_pattern.base;
2052
396k
    }
2053
2054
    /* For really huge font sizes, we can just do path;fill instead of
2055
     * show_glyphs, as show_glyphs would put excess pressure on the cache,
2056
     * and moreover, not all components below us correctly handle huge font
2057
     * sizes.  I wanted to set the limit at 256.  But alas, seems like cairo's
2058
     * rasterizer is something like ten times slower than freetype's for huge
2059
     * sizes.  So, no win just yet.  For now, do it for insanely-huge sizes,
2060
     * just to make sure we don't make anyone unhappy.  When we get a really
2061
     * fast rasterizer in cairo, we may want to readjust this.
2062
     *
2063
     * Needless to say, do this only if show_text_glyphs is not available. */
2064
396k
    if (cairo_surface_has_show_text_glyphs (gstate->target) ||
2065
396k
  _cairo_scaled_font_get_max_scale (gstate->scaled_font) <= 10240)
2066
396k
    {
2067
2068
396k
  if (info != NULL) {
2069
0
      status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern,
2070
0
                  info->utf8, info->utf8_len,
2071
0
                  transformed_glyphs, num_glyphs,
2072
0
                  transformed_clusters, info->num_clusters,
2073
0
                  info->cluster_flags,
2074
0
                  gstate->scaled_font,
2075
0
                  gstate->clip);
2076
396k
  } else {
2077
396k
      status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern,
2078
396k
                  NULL, 0,
2079
396k
                  transformed_glyphs, num_glyphs,
2080
396k
                  NULL, 0, 0,
2081
396k
                  gstate->scaled_font,
2082
396k
                  gstate->clip);
2083
396k
  }
2084
396k
    }
2085
0
    else
2086
0
    {
2087
0
  cairo_path_fixed_t path;
2088
2089
0
  _cairo_path_fixed_init (&path);
2090
2091
0
  status = _cairo_scaled_font_glyph_path (gstate->scaled_font,
2092
0
            transformed_glyphs, num_glyphs,
2093
0
            &path);
2094
2095
0
  if (status == CAIRO_STATUS_SUCCESS) {
2096
0
      status = _cairo_surface_fill (gstate->target, op, pattern,
2097
0
            &path,
2098
0
            CAIRO_FILL_RULE_WINDING,
2099
0
            gstate->tolerance,
2100
0
            gstate->scaled_font->options.antialias,
2101
0
            gstate->clip);
2102
0
  }
2103
2104
0
  _cairo_path_fixed_fini (&path);
2105
0
    }
2106
2107
2.55M
CLEANUP_GLYPHS:
2108
2.55M
    if (transformed_glyphs != stack_transformed_glyphs)
2109
6.32k
      cairo_glyph_free (transformed_glyphs);
2110
2.55M
    if (transformed_clusters != stack_transformed_clusters)
2111
0
      cairo_text_cluster_free (transformed_clusters);
2112
2113
2.55M
    return status;
2114
396k
}
2115
2116
cairo_status_t
2117
_cairo_gstate_glyph_path (cairo_gstate_t      *gstate,
2118
        const cairo_glyph_t *glyphs,
2119
        int          num_glyphs,
2120
        cairo_path_fixed_t  *path)
2121
0
{
2122
0
    cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
2123
0
    cairo_glyph_t *transformed_glyphs;
2124
0
    cairo_status_t status;
2125
2126
0
    status = _cairo_gstate_ensure_scaled_font (gstate);
2127
0
    if (unlikely (status))
2128
0
  return status;
2129
2130
0
    if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs)) {
2131
0
  transformed_glyphs = stack_transformed_glyphs;
2132
0
    } else {
2133
0
  transformed_glyphs = cairo_glyph_allocate (num_glyphs);
2134
0
  if (unlikely (transformed_glyphs == NULL))
2135
0
      return _cairo_error (CAIRO_STATUS_NO_MEMORY);
2136
0
    }
2137
2138
0
    _cairo_gstate_transform_glyphs_to_backend (gstate,
2139
0
                 glyphs, num_glyphs,
2140
0
                 NULL, 0, 0,
2141
0
                 transformed_glyphs,
2142
0
                 &num_glyphs, NULL);
2143
2144
0
    status = _cairo_scaled_font_glyph_path (gstate->scaled_font,
2145
0
              transformed_glyphs, num_glyphs,
2146
0
              path);
2147
2148
0
    if (transformed_glyphs != stack_transformed_glyphs)
2149
0
      cairo_glyph_free (transformed_glyphs);
2150
2151
0
    return status;
2152
0
}
2153
2154
cairo_status_t
2155
_cairo_gstate_set_antialias (cairo_gstate_t *gstate,
2156
           cairo_antialias_t antialias)
2157
7.60M
{
2158
7.60M
    gstate->antialias = antialias;
2159
2160
7.60M
    return CAIRO_STATUS_SUCCESS;
2161
7.60M
}
2162
2163
cairo_antialias_t
2164
_cairo_gstate_get_antialias (cairo_gstate_t *gstate)
2165
0
{
2166
0
    return gstate->antialias;
2167
0
}
2168
2169
/**
2170
 * _cairo_gstate_transform_glyphs_to_backend:
2171
 * @gstate: a #cairo_gstate_t
2172
 * @glyphs: the array of #cairo_glyph_t objects to be transformed
2173
 * @num_glyphs: the number of elements in @glyphs
2174
 * @transformed_glyphs: a pre-allocated array of at least @num_glyphs
2175
 * #cairo_glyph_t objects
2176
 * @num_transformed_glyphs: the number of elements in @transformed_glyphs
2177
 * after dropping out of bounds glyphs, or %NULL if glyphs shouldn't be
2178
 * dropped
2179
 *
2180
 * Transform an array of glyphs to backend space by first adding the offset
2181
 * of the font matrix, then transforming from user space to backend space.
2182
 * The result of the transformation is placed in @transformed_glyphs.
2183
 *
2184
 * This also uses information from the scaled font and the surface to
2185
 * cull/drop glyphs that will not be visible.
2186
 **/
2187
static void
2188
_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate,
2189
                                           const cairo_glyph_t  *glyphs,
2190
                                           int       num_glyphs,
2191
             const cairo_text_cluster_t *clusters,
2192
             int       num_clusters,
2193
             cairo_text_cluster_flags_t cluster_flags,
2194
                                           cairo_glyph_t  *transformed_glyphs,
2195
             int      *num_transformed_glyphs,
2196
             cairo_text_cluster_t *transformed_clusters)
2197
2.55M
{
2198
2.55M
    cairo_rectangle_int_t surface_extents;
2199
2.55M
    cairo_matrix_t *ctm = &gstate->ctm;
2200
2.55M
    cairo_matrix_t *font_matrix = &gstate->font_matrix;
2201
2.55M
    cairo_matrix_t *device_transform = &gstate->target->device_transform;
2202
2.55M
    cairo_bool_t drop = FALSE;
2203
2.55M
    double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
2204
2.55M
    int i, j, k;
2205
2206
2.55M
    drop = TRUE;
2207
2.55M
    if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) {
2208
0
  drop = FALSE; /* unbounded surface */
2209
2.55M
    } else {
2210
2.55M
  double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font);
2211
2.55M
  if (surface_extents.width == 0 || surface_extents.height == 0) {
2212
    /* No visible area.  Don't draw anything */
2213
0
    *num_transformed_glyphs = 0;
2214
0
    return;
2215
0
  }
2216
  /* XXX We currently drop any glyphs that have their position outside
2217
   * of the surface boundaries by a safety margin depending on the
2218
   * font scale.  This however can fail in extreme cases where the
2219
   * font has really long swashes for example...  We can correctly
2220
   * handle that by looking the glyph up and using its device bbox
2221
   * to device if it's going to be visible, but I'm not inclined to
2222
   * do that now.
2223
   */
2224
2.55M
  x1 = surface_extents.x - scale10;
2225
2.55M
  y1 = surface_extents.y - scale10;
2226
2.55M
  x2 = surface_extents.x + (int) surface_extents.width  + scale10;
2227
2.55M
  y2 = surface_extents.y + (int) surface_extents.height + scale10;
2228
2.55M
    }
2229
2230
2.55M
    if (!drop)
2231
0
  *num_transformed_glyphs = num_glyphs;
2232
2233
13.0M
#define KEEP_GLYPH(glyph) (x1 <= glyph.x && glyph.x <= x2 && y1 <= glyph.y && glyph.y <= y2)
2234
2235
2.55M
    j = 0;
2236
2.55M
    if (_cairo_matrix_is_identity (ctm) &&
2237
2.55M
        _cairo_matrix_is_identity (device_transform) &&
2238
2.55M
  font_matrix->x0 == 0 && font_matrix->y0 == 0)
2239
2.55M
    {
2240
2.55M
  if (! drop) {
2241
0
      memcpy (transformed_glyphs, glyphs,
2242
0
        num_glyphs * sizeof (cairo_glyph_t));
2243
0
      memcpy (transformed_clusters, clusters,
2244
0
        num_clusters * sizeof (cairo_text_cluster_t));
2245
0
      j = num_glyphs;
2246
2.55M
  } else if (num_clusters == 0) {
2247
15.5M
      for (i = 0; i < num_glyphs; i++) {
2248
13.0M
    transformed_glyphs[j].index = glyphs[i].index;
2249
13.0M
    transformed_glyphs[j].x = glyphs[i].x;
2250
13.0M
    transformed_glyphs[j].y = glyphs[i].y;
2251
13.0M
    if (KEEP_GLYPH (transformed_glyphs[j]))
2252
2.77M
        j++;
2253
13.0M
      }
2254
2.55M
  } else {
2255
0
      const cairo_glyph_t *cur_glyph;
2256
2257
0
      if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
2258
0
    cur_glyph = glyphs + num_glyphs - 1;
2259
0
      else
2260
0
    cur_glyph = glyphs;
2261
2262
0
      for (i = 0; i < num_clusters; i++) {
2263
0
    cairo_bool_t cluster_visible = FALSE;
2264
2265
0
    for (k = 0; k < clusters[i].num_glyphs; k++) {
2266
0
        transformed_glyphs[j+k].index = cur_glyph->index;
2267
0
        transformed_glyphs[j+k].x = cur_glyph->x;
2268
0
        transformed_glyphs[j+k].y = cur_glyph->y;
2269
0
        if (KEEP_GLYPH (transformed_glyphs[j+k]))
2270
0
      cluster_visible = TRUE;
2271
2272
0
        if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
2273
0
      cur_glyph--;
2274
0
        else
2275
0
      cur_glyph++;
2276
0
    }
2277
2278
0
    transformed_clusters[i] = clusters[i];
2279
0
    if (cluster_visible)
2280
0
        j += k;
2281
0
    else
2282
0
        transformed_clusters[i].num_glyphs = 0;
2283
0
      }
2284
0
  }
2285
2.55M
    }
2286
0
    else if (_cairo_matrix_is_translation (ctm) &&
2287
0
             _cairo_matrix_is_translation (device_transform))
2288
0
    {
2289
0
        double tx = font_matrix->x0 + ctm->x0 + device_transform->x0;
2290
0
        double ty = font_matrix->y0 + ctm->y0 + device_transform->y0;
2291
2292
0
  if (! drop || num_clusters == 0) {
2293
0
      for (i = 0; i < num_glyphs; i++) {
2294
0
    transformed_glyphs[j].index = glyphs[i].index;
2295
0
    transformed_glyphs[j].x = glyphs[i].x + tx;
2296
0
    transformed_glyphs[j].y = glyphs[i].y + ty;
2297
0
    if (!drop || KEEP_GLYPH (transformed_glyphs[j]))
2298
0
        j++;
2299
0
      }
2300
0
      if (num_clusters != 0) memcpy (transformed_clusters, clusters,
2301
0
        num_clusters * sizeof (cairo_text_cluster_t));
2302
0
  } else {
2303
0
      const cairo_glyph_t *cur_glyph;
2304
2305
0
      if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
2306
0
    cur_glyph = glyphs + num_glyphs - 1;
2307
0
      else
2308
0
    cur_glyph = glyphs;
2309
2310
0
      for (i = 0; i < num_clusters; i++) {
2311
0
    cairo_bool_t cluster_visible = FALSE;
2312
2313
0
    for (k = 0; k < clusters[i].num_glyphs; k++) {
2314
0
        transformed_glyphs[j+k].index = cur_glyph->index;
2315
0
        transformed_glyphs[j+k].x = cur_glyph->x + tx;
2316
0
        transformed_glyphs[j+k].y = cur_glyph->y + ty;
2317
0
        if (KEEP_GLYPH (transformed_glyphs[j+k]))
2318
0
      cluster_visible = TRUE;
2319
2320
0
        if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
2321
0
      cur_glyph--;
2322
0
        else
2323
0
      cur_glyph++;
2324
0
    }
2325
2326
0
    transformed_clusters[i] = clusters[i];
2327
0
    if (cluster_visible)
2328
0
        j += k;
2329
0
    else
2330
0
        transformed_clusters[i].num_glyphs = 0;
2331
0
      }
2332
0
  }
2333
0
    }
2334
0
    else
2335
0
    {
2336
0
        cairo_matrix_t aggregate_transform;
2337
2338
0
        cairo_matrix_init_translate (&aggregate_transform,
2339
0
                                     gstate->font_matrix.x0,
2340
0
                                     gstate->font_matrix.y0);
2341
0
        cairo_matrix_multiply (&aggregate_transform,
2342
0
                               &aggregate_transform, ctm);
2343
0
        cairo_matrix_multiply (&aggregate_transform,
2344
0
                               &aggregate_transform, device_transform);
2345
2346
0
  if (! drop || num_clusters == 0) {
2347
0
      for (i = 0; i < num_glyphs; i++) {
2348
0
    transformed_glyphs[j] = glyphs[i];
2349
0
    cairo_matrix_transform_point (&aggregate_transform,
2350
0
                &transformed_glyphs[j].x,
2351
0
                &transformed_glyphs[j].y);
2352
0
    if (! drop || KEEP_GLYPH (transformed_glyphs[j]))
2353
0
        j++;
2354
0
      }
2355
0
      if (num_clusters != 0) memcpy (transformed_clusters, clusters,
2356
0
        num_clusters * sizeof (cairo_text_cluster_t));
2357
0
  } else {
2358
0
      const cairo_glyph_t *cur_glyph;
2359
2360
0
      if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
2361
0
    cur_glyph = glyphs + num_glyphs - 1;
2362
0
      else
2363
0
    cur_glyph = glyphs;
2364
2365
0
      for (i = 0; i < num_clusters; i++) {
2366
0
    cairo_bool_t cluster_visible = FALSE;
2367
0
    for (k = 0; k < clusters[i].num_glyphs; k++) {
2368
0
        transformed_glyphs[j+k] = *cur_glyph;
2369
0
        cairo_matrix_transform_point (&aggregate_transform,
2370
0
              &transformed_glyphs[j+k].x,
2371
0
              &transformed_glyphs[j+k].y);
2372
0
        if (KEEP_GLYPH (transformed_glyphs[j+k]))
2373
0
      cluster_visible = TRUE;
2374
2375
0
        if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
2376
0
      cur_glyph--;
2377
0
        else
2378
0
      cur_glyph++;
2379
0
    }
2380
2381
0
    transformed_clusters[i] = clusters[i];
2382
0
    if (cluster_visible)
2383
0
        j += k;
2384
0
    else
2385
0
        transformed_clusters[i].num_glyphs = 0;
2386
0
      }
2387
0
  }
2388
0
    }
2389
2.55M
    *num_transformed_glyphs = j;
2390
2391
2.55M
    if (num_clusters != 0 && cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) {
2392
0
  for (i = 0; i < --j; i++) {
2393
0
      cairo_glyph_t tmp;
2394
2395
0
      tmp = transformed_glyphs[i];
2396
0
      transformed_glyphs[i] = transformed_glyphs[j];
2397
0
      transformed_glyphs[j] = tmp;
2398
0
  }
2399
0
    }
2400
2.55M
}