LCOV - code coverage report
Current view: top level - gfx/cairo/cairo/src - cairo-gstate.c (source / functions) Hit Total Coverage
Test: output.info Lines: 79 949 8.3 %
Date: 2017-07-14 16:53:18 Functions: 9 88 10.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13