Back to index

lightning-sunbird  0.9+nobinonly
cairo-pattern.c
Go to the documentation of this file.
00001 /* cairo - a vector graphics library with display and print output
00002  *
00003  * Copyright © 2004 David Reveman
00004  * Copyright © 2005 Red Hat, Inc.
00005  *
00006  * Permission to use, copy, modify, distribute, and sell this software
00007  * and its documentation for any purpose is hereby granted without
00008  * fee, provided that the above copyright notice appear in all copies
00009  * and that both that copyright notice and this permission notice
00010  * appear in supporting documentation, and that the name of David
00011  * Reveman not be used in advertising or publicity pertaining to
00012  * distribution of the software without specific, written prior
00013  * permission. David Reveman makes no representations about the
00014  * suitability of this software for any purpose.  It is provided "as
00015  * is" without express or implied warranty.
00016  *
00017  * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
00018  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
00019  * FITNESS, IN NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL,
00020  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
00021  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
00022  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
00023  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00024  *
00025  * Author: David Reveman <davidr@novell.com>
00026  */
00027 
00028 #include "cairoint.h"
00029 
00030 typedef struct _cairo_shader_color_stop {
00031     cairo_fixed_t    offset;
00032     cairo_fixed_48_16_t scale;
00033     int                     id;
00034     unsigned char    color_char[4];
00035 } cairo_shader_color_stop_t;
00036 
00037 typedef struct _cairo_shader_op {
00038     cairo_shader_color_stop_t *stops;
00039     int                           n_stops;
00040     cairo_extend_t         extend;
00041 } cairo_shader_op_t;
00042 
00043 #define MULTIPLY_COLORCOMP(c1, c2) \
00044     ((unsigned char) \
00045      ((((unsigned char) (c1)) * (int) ((unsigned char) (c2))) / 0xff))
00046 
00047 const cairo_solid_pattern_t cairo_pattern_nil = {
00048     { CAIRO_PATTERN_SOLID,  /* type */
00049       (unsigned int)-1,            /* ref_count */
00050       CAIRO_STATUS_NO_MEMORY,      /* status */
00051       { 1., 0., 0., 1., 0., 0., }, /* matrix */
00052       CAIRO_FILTER_DEFAULT, /* filter */
00053       CAIRO_EXTEND_DEFAULT },      /* extend */
00054 };
00055 
00056 static const cairo_solid_pattern_t cairo_pattern_nil_null_pointer = {
00057     { CAIRO_PATTERN_SOLID,  /* type */
00058       (unsigned int)-1,            /* ref_count */
00059       CAIRO_STATUS_NULL_POINTER,/* status */
00060       { 1., 0., 0., 1., 0., 0., }, /* matrix */
00061       CAIRO_FILTER_DEFAULT, /* filter */
00062       CAIRO_EXTEND_DEFAULT },      /* extend */
00063 };
00064 
00065 static const cairo_solid_pattern_t cairo_pattern_nil_file_not_found = {
00066     { CAIRO_PATTERN_SOLID,  /* type */
00067       (unsigned int)-1,            /* ref_count */
00068       CAIRO_STATUS_FILE_NOT_FOUND, /* status */
00069       { 1., 0., 0., 1., 0., 0., }, /* matrix */
00070       CAIRO_FILTER_DEFAULT, /* filter */
00071       CAIRO_EXTEND_DEFAULT },      /* extend */
00072 };
00073 
00074 static const cairo_solid_pattern_t cairo_pattern_nil_read_error = {
00075     { CAIRO_PATTERN_SOLID,  /* type */
00076       (unsigned int)-1,            /* ref_count */
00077       CAIRO_STATUS_READ_ERROR,     /* status */
00078       { 1., 0., 0., 1., 0., 0., }, /* matrix */
00079       CAIRO_FILTER_DEFAULT, /* filter */
00080       CAIRO_EXTEND_DEFAULT },      /* extend */
00081 };
00082 
00083 static const cairo_pattern_t *
00084 _cairo_pattern_nil_for_status (cairo_status_t status)
00085 {
00086     switch (status) {
00087     case CAIRO_STATUS_NULL_POINTER:
00088        return &cairo_pattern_nil_null_pointer.base;
00089     case CAIRO_STATUS_FILE_NOT_FOUND:
00090        return &cairo_pattern_nil_file_not_found.base;
00091     case CAIRO_STATUS_READ_ERROR:
00092        return &cairo_pattern_nil_read_error.base;
00093     default:
00094     case CAIRO_STATUS_NO_MEMORY:
00095        return &cairo_pattern_nil.base;
00096     }
00097 }
00098 
00115 static void
00116 _cairo_pattern_set_error (cairo_pattern_t *pattern,
00117                        cairo_status_t status)
00118 {
00119     /* Don't overwrite an existing error. This preserves the first
00120      * error, which is the most significant. It also avoids attempting
00121      * to write to read-only data (eg. from a nil pattern). */
00122     if (pattern->status == CAIRO_STATUS_SUCCESS)
00123        pattern->status = status;
00124 
00125     _cairo_error (status);
00126 }
00127 
00128 static void
00129 _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
00130 {
00131     pattern->type      = type; 
00132     pattern->ref_count = 1;
00133     pattern->status    = CAIRO_STATUS_SUCCESS;
00134     pattern->extend    = CAIRO_EXTEND_DEFAULT;
00135     pattern->filter    = CAIRO_FILTER_DEFAULT;
00136 
00137     cairo_matrix_init_identity (&pattern->matrix);
00138 }
00139 
00140 static void
00141 _cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t      *pattern,
00142                                const cairo_gradient_pattern_t *other)
00143 {
00144     if (other->base.type == CAIRO_PATTERN_LINEAR)
00145     {
00146        cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern;
00147        cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other;
00148        
00149        *dst = *src;
00150     }
00151     else
00152     {
00153        cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern;
00154        cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other;
00155        
00156        *dst = *src;
00157     }
00158 
00159     if (other->n_stops)
00160     {
00161        pattern->stops = malloc (other->n_stops * sizeof (cairo_color_stop_t));
00162        if (pattern->stops == NULL) {
00163            _cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY);
00164            return;
00165        }
00166        
00167        memcpy (pattern->stops, other->stops,
00168               other->n_stops * sizeof (cairo_color_stop_t));
00169     }
00170 }
00171 
00172 void
00173 _cairo_pattern_init_copy (cairo_pattern_t *pattern,
00174                        const cairo_pattern_t *other)
00175 {
00176     if (other->status) {
00177        _cairo_pattern_set_error (pattern, other->status);
00178        return;
00179     }
00180 
00181     switch (other->type) {
00182     case CAIRO_PATTERN_SOLID: {
00183        cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern;
00184        cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other;
00185 
00186        *dst = *src;
00187     } break;
00188     case CAIRO_PATTERN_SURFACE: {
00189        cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern;
00190        cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other;
00191        
00192        *dst = *src;
00193        cairo_surface_reference (dst->surface);
00194     } break;
00195     case CAIRO_PATTERN_LINEAR:
00196     case CAIRO_PATTERN_RADIAL: {
00197        cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern;
00198        cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other;
00199        
00200        _cairo_gradient_pattern_init_copy (dst, src);
00201     } break;
00202     }
00203     
00204     pattern->ref_count = 1;
00205 }
00206 
00207 void
00208 _cairo_pattern_fini (cairo_pattern_t *pattern)
00209 {
00210     switch (pattern->type) {
00211     case CAIRO_PATTERN_SOLID:
00212        break;
00213     case CAIRO_PATTERN_SURFACE: {
00214        cairo_surface_pattern_t *surface_pattern =
00215            (cairo_surface_pattern_t *) pattern;
00216        
00217        cairo_surface_destroy (surface_pattern->surface);
00218     } break;
00219     case CAIRO_PATTERN_LINEAR:
00220     case CAIRO_PATTERN_RADIAL: {
00221        cairo_gradient_pattern_t *gradient =
00222            (cairo_gradient_pattern_t *) pattern;
00223        
00224        if (gradient->stops)
00225            free (gradient->stops);
00226     } break;
00227     }
00228 }
00229 
00230 void
00231 _cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
00232                         const cairo_color_t       *color)
00233 {
00234     _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SOLID);
00235     pattern->color = *color;
00236 }
00237 
00238 void
00239 _cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
00240                              cairo_surface_t      *surface)
00241 {
00242     if (surface->status) {
00243        /* Force to solid to simplify the pattern_fini process. */
00244        pattern->base.type = CAIRO_PATTERN_SOLID;
00245        _cairo_pattern_set_error (&pattern->base, surface->status);
00246        return;
00247     }
00248 
00249     _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SURFACE);
00250     
00251     pattern->surface = cairo_surface_reference (surface);
00252 }
00253 
00254 static void
00255 _cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern,
00256                            cairo_pattern_type_t     type)
00257 {
00258     _cairo_pattern_init (&pattern->base, type);
00259 
00260     pattern->stops   = NULL;
00261     pattern->n_stops = 0;
00262 }
00263 
00264 void
00265 _cairo_pattern_init_linear (cairo_linear_pattern_t *pattern,
00266                          double x0, double y0, double x1, double y1)
00267 {
00268     _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_LINEAR);
00269     
00270     pattern->point0.x = x0;
00271     pattern->point0.y = y0;
00272     pattern->point1.x = x1;
00273     pattern->point1.y = y1;
00274 }
00275 
00276 void
00277 _cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
00278                          double cx0, double cy0, double radius0,
00279                          double cx1, double cy1, double radius1)
00280 {
00281     _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_RADIAL);
00282 
00283     pattern->center0.x = cx0;
00284     pattern->center0.y = cy0;
00285     pattern->radius0   = fabs (radius0);
00286     pattern->center1.x = cx1;
00287     pattern->center1.y = cy1;
00288     pattern->radius1   = fabs (radius1);
00289 }
00290 
00291 cairo_pattern_t *
00292 _cairo_pattern_create_solid (const cairo_color_t *color)
00293 {
00294     cairo_solid_pattern_t *pattern;
00295 
00296     pattern = malloc (sizeof (cairo_solid_pattern_t));
00297     if (pattern == NULL)
00298        return (cairo_pattern_t *) &cairo_pattern_nil.base;
00299 
00300     _cairo_pattern_init_solid (pattern, color);
00301 
00302     return &pattern->base;
00303 }
00304 
00325 cairo_pattern_t *
00326 cairo_pattern_create_rgb (double red, double green, double blue)
00327 {
00328     cairo_pattern_t *pattern;
00329     cairo_color_t color;
00330 
00331     _cairo_restrict_value (&red,   0.0, 1.0);
00332     _cairo_restrict_value (&green, 0.0, 1.0);
00333     _cairo_restrict_value (&blue,  0.0, 1.0);
00334 
00335     _cairo_color_init_rgb (&color, red, green, blue);
00336 
00337     pattern = _cairo_pattern_create_solid (&color);
00338     if (pattern->status)
00339        _cairo_error (pattern->status);
00340 
00341     return pattern;
00342 }
00343 
00365 cairo_pattern_t *
00366 cairo_pattern_create_rgba (double red, double green, double blue,
00367                         double alpha)
00368 {
00369     cairo_pattern_t *pattern;
00370     cairo_color_t color;
00371 
00372     _cairo_restrict_value (&red,   0.0, 1.0);
00373     _cairo_restrict_value (&green, 0.0, 1.0);
00374     _cairo_restrict_value (&blue,  0.0, 1.0);
00375     _cairo_restrict_value (&alpha, 0.0, 1.0);
00376 
00377     _cairo_color_init_rgba (&color, red, green, blue, alpha);
00378 
00379     pattern = _cairo_pattern_create_solid (&color);
00380     if (pattern->status)
00381        _cairo_error (pattern->status);
00382 
00383     return pattern;
00384 }
00385 
00401 cairo_pattern_t *
00402 cairo_pattern_create_for_surface (cairo_surface_t *surface)
00403 {
00404     cairo_surface_pattern_t *pattern;
00405 
00406     if (surface == NULL)
00407        return (cairo_pattern_t*) _cairo_pattern_nil_for_status (CAIRO_STATUS_NULL_POINTER);
00408 
00409     if (surface->status)
00410        return (cairo_pattern_t*) _cairo_pattern_nil_for_status (surface->status);
00411 
00412     pattern = malloc (sizeof (cairo_surface_pattern_t));
00413     if (pattern == NULL) {
00414        _cairo_error (CAIRO_STATUS_NO_MEMORY);
00415        return (cairo_pattern_t *)&cairo_pattern_nil.base;
00416     }
00417 
00418     _cairo_pattern_init_for_surface (pattern, surface);
00419 
00420     return &pattern->base;
00421 }
00422 
00449 cairo_pattern_t *
00450 cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
00451 {
00452     cairo_linear_pattern_t *pattern;
00453 
00454     pattern = malloc (sizeof (cairo_linear_pattern_t));
00455     if (pattern == NULL) {
00456        _cairo_error (CAIRO_STATUS_NO_MEMORY);
00457        return (cairo_pattern_t *) &cairo_pattern_nil.base;
00458     }
00459 
00460     _cairo_pattern_init_linear (pattern, x0, y0, x1, y1);
00461 
00462     return &pattern->base.base;
00463 }
00464 
00493 cairo_pattern_t *
00494 cairo_pattern_create_radial (double cx0, double cy0, double radius0,
00495                           double cx1, double cy1, double radius1)
00496 {
00497     cairo_radial_pattern_t *pattern;
00498     
00499     pattern = malloc (sizeof (cairo_radial_pattern_t));
00500     if (pattern == NULL) {
00501        _cairo_error (CAIRO_STATUS_NO_MEMORY);
00502        return (cairo_pattern_t *) &cairo_pattern_nil.base;
00503     }
00504 
00505     _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1);
00506 
00507     return &pattern->base.base;
00508 }
00509 
00520 cairo_pattern_t *
00521 cairo_pattern_reference (cairo_pattern_t *pattern)
00522 {
00523     if (pattern == NULL)
00524        return NULL;
00525 
00526     if (pattern->ref_count == (unsigned int)-1)
00527        return pattern;
00528 
00529     assert (pattern->ref_count > 0);
00530 
00531     pattern->ref_count++;
00532 
00533     return pattern;
00534 }
00535 
00546 cairo_status_t
00547 cairo_pattern_status (cairo_pattern_t *pattern)
00548 {
00549     return pattern->status;
00550 }
00551 
00560 void
00561 cairo_pattern_destroy (cairo_pattern_t *pattern)
00562 {
00563     if (pattern == NULL)
00564        return;
00565 
00566     if (pattern->ref_count == (unsigned int)-1)
00567        return;
00568 
00569     assert (pattern->ref_count > 0);
00570 
00571     pattern->ref_count--;
00572     if (pattern->ref_count)
00573        return;
00574 
00575     _cairo_pattern_fini (pattern);
00576     free (pattern);
00577 }
00578 
00579 static void
00580 _cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern,
00581                             double                offset,
00582                             cairo_color_t        *color)
00583 {
00584     cairo_color_stop_t *stop;
00585     cairo_color_stop_t *new_stops;
00586 
00587     pattern->n_stops++;
00588     new_stops = realloc (pattern->stops,
00589                       pattern->n_stops * sizeof (cairo_color_stop_t));
00590     if (new_stops == NULL) {
00591        _cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY);
00592        return;
00593     }
00594     
00595     pattern->stops = new_stops;
00596 
00597     stop = &pattern->stops[pattern->n_stops - 1];
00598 
00599     stop->offset = _cairo_fixed_from_double (offset);
00600     stop->color = *color;
00601 }
00602 
00623 void
00624 cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern,
00625                               double         offset,
00626                               double         red,
00627                               double         green,
00628                               double         blue)
00629 {
00630     cairo_color_t color;
00631 
00632     if (pattern->status)
00633        return;
00634 
00635     if (pattern->type != CAIRO_PATTERN_LINEAR &&
00636        pattern->type != CAIRO_PATTERN_RADIAL)
00637     {
00638        _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
00639        return;
00640     }
00641 
00642     _cairo_restrict_value (&offset, 0.0, 1.0);
00643     _cairo_restrict_value (&red,    0.0, 1.0);
00644     _cairo_restrict_value (&green,  0.0, 1.0);
00645     _cairo_restrict_value (&blue,   0.0, 1.0);
00646 
00647     _cairo_color_init_rgb (&color, red, green, blue);
00648     _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
00649                                offset,
00650                                &color);
00651 }
00652 
00674 void
00675 cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern,
00676                                double        offset,
00677                                double        red,
00678                                double        green,
00679                                double        blue,
00680                                double        alpha)
00681 {
00682     cairo_color_t color;
00683 
00684     if (pattern->status)
00685        return;
00686 
00687     if (pattern->type != CAIRO_PATTERN_LINEAR &&
00688        pattern->type != CAIRO_PATTERN_RADIAL)
00689     {
00690        _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
00691        return;
00692     }
00693 
00694     _cairo_restrict_value (&offset, 0.0, 1.0);
00695     _cairo_restrict_value (&red,    0.0, 1.0);
00696     _cairo_restrict_value (&green,  0.0, 1.0);
00697     _cairo_restrict_value (&blue,   0.0, 1.0);
00698     _cairo_restrict_value (&alpha,  0.0, 1.0);
00699 
00700     _cairo_color_init_rgba (&color, red, green, blue, alpha);
00701     _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
00702                                offset,
00703                                &color);
00704 }
00705 
00738 void
00739 cairo_pattern_set_matrix (cairo_pattern_t      *pattern,
00740                        const cairo_matrix_t *matrix)
00741 {
00742     if (pattern->status)
00743        return;
00744 
00745     pattern->matrix = *matrix;
00746 }
00747 
00755 void
00756 cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix)
00757 {
00758     *matrix = pattern->matrix;
00759 }
00760 
00761 void
00762 cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
00763 {
00764     if (pattern->status)
00765        return;
00766 
00767     pattern->filter = filter;
00768 }
00769 
00770 cairo_filter_t
00771 cairo_pattern_get_filter (cairo_pattern_t *pattern)
00772 {
00773     return pattern->filter;
00774 }
00775 
00776 void
00777 cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
00778 {
00779     if (pattern->status)
00780        return;
00781 
00782     pattern->extend = extend;
00783 }
00784 
00785 cairo_extend_t
00786 cairo_pattern_get_extend (cairo_pattern_t *pattern)
00787 {
00788     return pattern->extend;
00789 }
00790 
00791 void
00792 _cairo_pattern_transform (cairo_pattern_t *pattern,
00793                        const cairo_matrix_t  *ctm_inverse)
00794 {
00795     assert (pattern->status == CAIRO_STATUS_SUCCESS);
00796 
00797     cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix);
00798 }
00799 
00800 #define INTERPOLATE_COLOR_LINEAR(c1, c2, factor) \
00801   (((c2 * factor) + (c1 * (65536 - factor))) / 65536)
00802 
00803 static void
00804 _cairo_pattern_shader_linear (unsigned char *color0,
00805                            unsigned char *color1,
00806                            cairo_fixed_t factor,
00807                            uint32_t           *pixel)
00808 {
00809     *pixel = ((INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor) << 24) |
00810              (INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor) << 16) |
00811              (INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor) << 8) |
00812              (INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor) << 0));
00813 }
00814 
00815 #undef INTERPOLATE_COLOR_LINEAR
00816 
00817 static int
00818 _cairo_shader_color_stop_compare (const void *elem1, const void *elem2)
00819 {
00820     cairo_shader_color_stop_t *s1 = (cairo_shader_color_stop_t *) elem1;
00821     cairo_shader_color_stop_t *s2 = (cairo_shader_color_stop_t *) elem2;
00822        
00823     return
00824         (s1->offset == s2->offset) ?
00825         /* equal offsets, sort on id */
00826         ((s1->id < s2->id) ? -1 : 1) :
00827         /* sort on offset */
00828         ((s1->offset < s2->offset) ? -1 : 1);
00829 }
00830 
00831 static cairo_status_t
00832 _cairo_pattern_shader_init (cairo_gradient_pattern_t *pattern,
00833                          cairo_shader_op_t            *op)
00834 {
00835     int i;
00836 
00837     op->stops = malloc (pattern->n_stops * sizeof (cairo_shader_color_stop_t));
00838     if (!op->stops)
00839        return CAIRO_STATUS_NO_MEMORY;
00840 
00841     for (i = 0; i < pattern->n_stops; i++)
00842     {
00843        op->stops[i].color_char[0] = pattern->stops[i].color.red * 0xff;
00844        op->stops[i].color_char[1] = pattern->stops[i].color.green * 0xff;
00845        op->stops[i].color_char[2] = pattern->stops[i].color.blue * 0xff;
00846        op->stops[i].color_char[3] = pattern->stops[i].color.alpha * 0xff;
00847        op->stops[i].offset = pattern->stops[i].offset;
00848        op->stops[i].id = i;
00849     }
00850 
00851     /* sort stops in ascending order */
00852     qsort (op->stops, pattern->n_stops, sizeof (cairo_shader_color_stop_t),
00853           _cairo_shader_color_stop_compare);
00854 
00855     /* this scale value is used only when computing gradient values
00856      * before the defined range, in which case stop 0 is used for both
00857      * ends of the interpolation, making the value of 'scale' not
00858      * actually matter, except that valgrind notices we're using
00859      * an undefined value.
00860      */
00861     op->stops[0].scale = 0;
00862     for (i = 0; i < pattern->n_stops - 1; i++)
00863     {
00864        op->stops[i + 1].scale = op->stops[i + 1].offset - op->stops[i].offset;
00865        if (op->stops[i + 1].scale == 65536)
00866            op->stops[i + 1].scale = 0;
00867     }
00868 
00869     op->n_stops = pattern->n_stops;
00870     op->extend = pattern->base.extend;
00871 
00872     return CAIRO_STATUS_SUCCESS;
00873 }
00874 
00875 static void
00876 _cairo_pattern_shader_fini (cairo_shader_op_t *op)
00877 {
00878     if (op->stops)
00879        free (op->stops);
00880 }
00881 
00882 /* Find two color stops bounding the given offset. If the given offset
00883  * is before the first or after the last stop offset, the nearest
00884  * offset is returned twice.
00885  */
00886 static void
00887 _cairo_shader_op_find_color_stops (cairo_shader_op_t         *op,
00888                                cairo_fixed_t          offset,
00889                                cairo_shader_color_stop_t *stops[2])
00890 {
00891     int i;
00892 
00893     /* Before first stop. */
00894     if (offset <= op->stops[0].offset) {
00895        stops[0] = &op->stops[0];
00896        stops[1] = &op->stops[0];
00897        return;
00898     }
00899 
00900     /* Between two stops. */
00901     for (i = 0; i < op->n_stops - 1; i++) {
00902        if (offset <= op->stops[i + 1].offset) {
00903            stops[0] = &op->stops[i];
00904            stops[1] = &op->stops[i + 1];
00905            return;
00906        }
00907     }
00908 
00909     /* After last stop. */
00910     stops[0] = &op->stops[op->n_stops - 1];
00911     stops[1] = &op->stops[op->n_stops - 1];
00912 }
00913 
00914 static void
00915 _cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op,
00916                                 cairo_fixed_t     factor,
00917                                 uint32_t        *pixel)
00918 {
00919     cairo_shader_color_stop_t *stops[2];
00920 
00921     switch (op->extend) {
00922     case CAIRO_EXTEND_REPEAT:
00923        factor -= factor & 0xffff0000;
00924        break;
00925     case CAIRO_EXTEND_REFLECT:
00926        if (factor < 0 || factor > 65536) {
00927            if ((factor >> 16) % 2)
00928               factor = 65536 - (factor - (factor & 0xffff0000));
00929            else
00930               factor -= factor & 0xffff0000;
00931        }
00932        break;
00933     case CAIRO_EXTEND_NONE:
00934        break;
00935     }
00936 
00937     _cairo_shader_op_find_color_stops (op, factor, stops);
00938 
00939     /* take offset as new 0 of coordinate system */
00940     factor -= stops[0]->offset;
00941            
00942     /* difference between two offsets == 0, abrubt change */
00943     if (stops[1]->scale)
00944        factor = ((cairo_fixed_48_16_t) factor << 16) /
00945            stops[1]->scale;
00946 
00947     _cairo_pattern_shader_linear (stops[0]->color_char,
00948                               stops[1]->color_char,
00949                               factor, pixel);
00950            
00951     /* multiply alpha */
00952     if (((unsigned char) (*pixel >> 24)) != 0xff) {
00953        *pixel = (*pixel & 0xff000000) |
00954            (MULTIPLY_COLORCOMP (*pixel >> 16, *pixel >> 24) << 16) |
00955            (MULTIPLY_COLORCOMP (*pixel >> 8, *pixel >> 24) << 8) |
00956            (MULTIPLY_COLORCOMP (*pixel >> 0, *pixel >> 24) << 0);
00957     }
00958 }
00959 
00960 static cairo_status_t
00961 _cairo_image_data_set_linear (cairo_linear_pattern_t *pattern,
00962                            double              offset_x,
00963                            double              offset_y,
00964                            uint32_t                   *pixels,
00965                            int                 width,
00966                            int                 height)
00967 {
00968     int x, y;
00969     cairo_point_double_t point0, point1;
00970     double a, b, c, d, tx, ty;
00971     double scale, start, dx, dy, factor;
00972     cairo_shader_op_t op;
00973     cairo_status_t status;
00974 
00975     status = _cairo_pattern_shader_init (&pattern->base, &op);
00976     if (status)
00977        return status;
00978 
00979     /* We compute the position in the linear gradient for
00980      * a point q as:
00981      *
00982      *  [q . (p1 - p0) - p0 . (p1 - p0)] / (p1 - p0) ^ 2
00983      *
00984      * The computation is done in pattern space. The 
00985      * calculation could be heavily optimized by using the
00986      * fact that 'factor' increases linearly in both
00987      * directions.
00988      */
00989     point0.x = pattern->point0.x;
00990     point0.y = pattern->point0.y;
00991     point1.x = pattern->point1.x;
00992     point1.y = pattern->point1.y;
00993 
00994     _cairo_matrix_get_affine (&pattern->base.base.matrix,
00995                            &a, &b, &c, &d, &tx, &ty);
00996 
00997     dx = point1.x - point0.x;
00998     dy = point1.y - point0.y;
00999     scale = dx * dx + dy * dy;
01000     scale = (scale) ? 1.0 / scale : 1.0;
01001 
01002     start = dx * point0.x + dy * point0.y;
01003 
01004     for (y = 0; y < height; y++) {
01005        for (x = 0; x < width; x++) {
01006            double qx_device = x + offset_x;
01007            double qy_device = y + offset_y;
01008               
01009            /* transform fragment into pattern space */
01010            double qx = a * qx_device + c * qy_device + tx;
01011            double qy = b * qx_device + d * qy_device + ty;
01012            
01013            factor = ((dx * qx + dy * qy) - start) * scale;
01014 
01015            _cairo_pattern_calc_color_at_pixel (&op, _cairo_fixed_from_double (factor), pixels++);
01016        }
01017     }
01018 
01019     _cairo_pattern_shader_fini (&op);
01020     
01021     return CAIRO_STATUS_SUCCESS;
01022 }
01023 
01024 static void
01025 _cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern,
01026                             double               offset_x,
01027                             double               offset_y,
01028                             int                  width,
01029                             int                  height,
01030                             cairo_bool_t           *is_horizontal,
01031                             cairo_bool_t           *is_vertical)
01032 {
01033     cairo_point_double_t point0, point1;
01034     double a, b, c, d, tx, ty;
01035     double scale, start, dx, dy;
01036     cairo_fixed_t factors[3];
01037     int i;
01038 
01039     /* To classidy a pattern as horizontal or vertical, we first
01040      * compute the (fixed point) factors at the corners of the
01041      * pattern. We actually only need 3/4 corners, so we skip the
01042      * fourth.
01043      */
01044     point0.x = pattern->point0.x;
01045     point0.y = pattern->point0.y;
01046     point1.x = pattern->point1.x;
01047     point1.y = pattern->point1.y;
01048 
01049     _cairo_matrix_get_affine (&pattern->base.base.matrix,
01050                            &a, &b, &c, &d, &tx, &ty);
01051 
01052     dx = point1.x - point0.x;
01053     dy = point1.y - point0.y;
01054     scale = dx * dx + dy * dy;
01055     scale = (scale) ? 1.0 / scale : 1.0;
01056 
01057     start = dx * point0.x + dy * point0.y;
01058 
01059     for (i = 0; i < 3; i++) {
01060        double qx_device = (i % 2) * (width - 1) + offset_x;
01061        double qy_device = (i / 2) * (height - 1) + offset_y;
01062 
01063        /* transform fragment into pattern space */
01064        double qx = a * qx_device + c * qy_device + tx;
01065        double qy = b * qx_device + d * qy_device + ty;
01066        
01067        factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale);
01068     }
01069 
01070     /* We consider a pattern to be vertical if the fixed point factor
01071      * at the two upper corners is the same. We could accept a small
01072      * change, but determining what change is acceptable would require
01073      * sorting the stops in the pattern and looking at the differences.
01074      *
01075      * Horizontal works the same way with the two left corners.
01076      */
01077 
01078     *is_vertical = factors[1] == factors[0];
01079     *is_horizontal = factors[2] == factors[0];
01080 }
01081 
01082 static cairo_status_t
01083 _cairo_image_data_set_radial (cairo_radial_pattern_t *pattern,
01084                            double              offset_x,
01085                            double              offset_y,
01086                            uint32_t                   *pixels,
01087                            int                 width,
01088                            int                 height)
01089 {
01090     int x, y, aligned_circles;
01091     cairo_point_double_t c0, c1;
01092     double px, py, ex, ey;
01093     double a, b, c, d, tx, ty;
01094     double r0, r1, c0_e_x, c0_e_y, c0_e, c1_e_x, c1_e_y, c1_e,
01095        c0_c1_x, c0_c1_y, c0_c1, angle_c0, c1_y, y_x, c0_y, c0_x, r1_2,
01096        denumerator, fraction, factor;
01097     cairo_shader_op_t op;
01098     cairo_status_t status;
01099 
01100     status = _cairo_pattern_shader_init (&pattern->base, &op);
01101     if (status)
01102        return status;
01103 
01104     c0.x = pattern->center0.x;
01105     c0.y = pattern->center0.y;
01106     r0 = pattern->radius0;
01107     c1.x = pattern->center1.x;
01108     c1.y = pattern->center1.y;
01109     r1 =  pattern->radius1;
01110 
01111     if (c0.x != c1.x || c0.y != c1.y) {
01112        aligned_circles = 0;
01113        c0_c1_x = c1.x - c0.x;
01114        c0_c1_y = c1.y - c0.y;
01115        c0_c1 = sqrt (c0_c1_x * c0_c1_x + c0_c1_y * c0_c1_y);
01116        r1_2 = r1 * r1;
01117     } else {
01118        aligned_circles = 1;
01119        r1 = 1.0 / (r1 - r0);
01120        r1_2 = c0_c1 = 0.0; /* shut up compiler */
01121     }
01122 
01123     _cairo_matrix_get_affine (&pattern->base.base.matrix,
01124                            &a, &b, &c, &d, &tx, &ty);
01125 
01126     for (y = 0; y < height; y++) {
01127        for (x = 0; x < width; x++) {
01128            px = x + offset_x;
01129            py = y + offset_y;
01130               
01131            /* transform fragment */
01132            ex = a * px + c * py + tx;
01133            ey = b * px + d * py + ty;
01134 
01135            if (aligned_circles) {
01136               ex = ex - c1.x;
01137               ey = ey - c1.y;
01138 
01139               factor = (sqrt (ex * ex + ey * ey) - r0) * r1;
01140            } else {
01141            /*
01142                        y         (ex, ey)
01143                c0 -------------------+---------- x
01144                   \     |                  __--
01145                    \    |              __--
01146                     \   |          __--
01147                      \  |      __-- r1
01148                       \ |  __--
01149                       c1 --
01150 
01151               We need to calulate distance c0->x; the distance from
01152               the inner circle center c0, through fragment position
01153               (ex, ey) to point x where it crosses the outer circle.
01154 
01155               From points c0, c1 and (ex, ey) we get angle C0. With
01156               angle C0 we calculate distance c1->y and c0->y and by
01157               knowing c1->y and r1, we also know y->x. Adding y->x to
01158               c0->y gives us c0->x. The gradient offset can then be
01159               calculated as:
01160               
01161               offset = (c0->e - r0) / (c0->x - r0)
01162               
01163               */
01164 
01165               c0_e_x = ex - c0.x;
01166               c0_e_y = ey - c0.y;
01167               c0_e = sqrt (c0_e_x * c0_e_x + c0_e_y * c0_e_y);
01168 
01169               c1_e_x = ex - c1.x;
01170               c1_e_y = ey - c1.y;
01171               c1_e = sqrt (c1_e_x * c1_e_x + c1_e_y * c1_e_y);
01172 
01173               denumerator = -2.0 * c0_e * c0_c1;
01174               
01175               if (denumerator != 0.0) {
01176                   fraction = (c1_e * c1_e - c0_e * c0_e - c0_c1 * c0_c1) /
01177                      denumerator;
01178 
01179                   if (fraction > 1.0)
01180                      fraction = 1.0;
01181                   else if (fraction < -1.0)
01182                      fraction = -1.0;
01183                   
01184                   angle_c0 = acos (fraction);
01185                   
01186                   c0_y = cos (angle_c0) * c0_c1;
01187                   c1_y = sin (angle_c0) * c0_c1;
01188                   
01189                   y_x = sqrt (r1_2 - c1_y * c1_y);
01190                   c0_x = y_x + c0_y;
01191                   
01192                   factor = (c0_e - r0) / (c0_x - r0);
01193               } else {
01194                   factor = -r0;
01195               }
01196            }
01197 
01198            _cairo_pattern_calc_color_at_pixel (&op, _cairo_fixed_from_double (factor), pixels++);
01199        }
01200     }
01201 
01202     _cairo_pattern_shader_fini (&op);
01203 
01204     return CAIRO_STATUS_SUCCESS;
01205 }
01206 
01207 static cairo_int_status_t
01208 _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern,
01209                                         cairo_surface_t         *dst,
01210                                         int                    x,
01211                                         int                    y,
01212                                         unsigned int           width,
01213                                         unsigned int            height,
01214                                         cairo_surface_t         **out,
01215                                         cairo_surface_attributes_t *attr)
01216 {
01217     cairo_image_surface_t *image;
01218     cairo_status_t status;
01219     uint32_t *data;
01220     cairo_bool_t repeat = FALSE;
01221 
01222     if (pattern->base.type == CAIRO_PATTERN_LINEAR) {
01223        cairo_bool_t is_horizontal;
01224        cairo_bool_t is_vertical;
01225 
01226        _cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern,
01227                                    x, y, width, height,
01228                                    &is_horizontal, &is_vertical);
01229        if (is_horizontal) {
01230            height = 1;
01231            repeat = TRUE;
01232        }
01233        /* width-1 repeating patterns are quite slow with scan-line based
01234         * compositing code, so we use a wider strip and spend some extra
01235         * expense in computing the gradient. It's possible that for narrow
01236         * gradients we'd be better off using a 2 or 4 pixel strip; the
01237         * wider the gradient, the more it's worth spending extra time
01238         * computing a sample.
01239         */
01240        if (is_vertical && width > 8) {
01241            width = 8;
01242            repeat = TRUE;
01243        }
01244     }
01245     
01246     data = malloc (width * height * 4);
01247     if (data == NULL)
01248        return CAIRO_STATUS_NO_MEMORY;
01249     
01250     if (pattern->base.type == CAIRO_PATTERN_LINEAR)
01251     {
01252        cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
01253        
01254        status = _cairo_image_data_set_linear (linear, x, y, data,
01255                                           width, height);
01256     }
01257     else
01258     {
01259        cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
01260        
01261        status = _cairo_image_data_set_radial (radial, x, y, data,
01262                                           width, height);
01263     }
01264 
01265     if (status) {
01266        free (data);
01267        return status;
01268     }
01269 
01270     image = (cairo_image_surface_t *)
01271        cairo_image_surface_create_for_data ((unsigned char *) data,
01272                                         CAIRO_FORMAT_ARGB32,
01273                                         width, height,
01274                                         width * 4);
01275     
01276     if (image->base.status) {
01277        free (data);
01278        return CAIRO_STATUS_NO_MEMORY;
01279     }
01280 
01281     _cairo_image_surface_assume_ownership_of_data (image);
01282     
01283     status = _cairo_surface_clone_similar (dst, &image->base, out);
01284        
01285     cairo_surface_destroy (&image->base);
01286 
01287     attr->x_offset = -x;
01288     attr->y_offset = -y;
01289     cairo_matrix_init_identity (&attr->matrix);
01290     attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE;
01291     attr->filter = CAIRO_FILTER_NEAREST;
01292     attr->acquired = FALSE;
01293     
01294     return status;
01295 }
01296 
01297 static cairo_int_status_t
01298 _cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t            *pattern,
01299                                      cairo_surface_t         *dst,
01300                                      int                     x,
01301                                      int                     y,
01302                                      unsigned int                   width,
01303                                      unsigned int                   height,
01304                                      cairo_surface_t         **out,
01305                                      cairo_surface_attributes_t *attribs)
01306 {
01307     *out = _cairo_surface_create_similar_solid (dst,
01308                                           CAIRO_CONTENT_COLOR_ALPHA,
01309                                           1, 1,
01310                                           &pattern->color);
01311     if ((*out)->status)
01312        return CAIRO_STATUS_NO_MEMORY;
01313 
01314     attribs->x_offset = attribs->y_offset = 0;
01315     cairo_matrix_init_identity (&attribs->matrix);
01316     attribs->extend = CAIRO_EXTEND_REPEAT;
01317     attribs->filter = CAIRO_FILTER_NEAREST;
01318     attribs->acquired = FALSE;
01319     
01320     return CAIRO_STATUS_SUCCESS;
01321 }
01322 
01323 
01335 cairo_bool_t 
01336 _cairo_pattern_is_opaque_solid (cairo_pattern_t *pattern)
01337 {
01338     cairo_solid_pattern_t *solid;
01339 
01340     if (pattern->type != CAIRO_PATTERN_SOLID)
01341        return FALSE;
01342 
01343     solid = (cairo_solid_pattern_t *) pattern;
01344 
01345     return CAIRO_ALPHA_IS_OPAQUE (solid->color.alpha);
01346 }
01347 
01348 static cairo_int_status_t
01349 _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t   *pattern,
01350                                        cairo_surface_t         *dst,
01351                                        int                            x,
01352                                        int                            y,
01353                                        unsigned int            width,
01354                                        unsigned int            height,
01355                                        cairo_surface_t         **out,
01356                                        cairo_surface_attributes_t *attr)
01357 {
01358     cairo_int_status_t status;
01359     int tx, ty;
01360 
01361     attr->acquired = FALSE;
01362            
01363     if (_cairo_surface_is_image (dst))
01364     {
01365        cairo_image_surface_t *image;
01366        
01367        status = _cairo_surface_acquire_source_image (pattern->surface,
01368                                                 &image,
01369                                                 &attr->extra);
01370        if (status)
01371            return status;
01372 
01373        *out = &image->base;
01374        attr->acquired = TRUE;
01375     }
01376     else
01377     {
01378        status = _cairo_surface_clone_similar (dst, pattern->surface, out);
01379     }
01380     
01381     attr->extend = pattern->base.extend;
01382     attr->filter = pattern->base.filter;
01383     if (_cairo_matrix_is_integer_translation (&pattern->base.matrix,
01384                                          &tx, &ty))
01385     {
01386        cairo_matrix_init_identity (&attr->matrix);
01387        attr->x_offset = tx;
01388        attr->y_offset = ty;
01389        attr->filter = CAIRO_FILTER_NEAREST;
01390     }
01391     else
01392     {
01393        attr->matrix = pattern->base.matrix;
01394        attr->x_offset = attr->y_offset = 0;
01395     }
01396     
01397     return status;
01398 }
01399 
01417 cairo_int_status_t
01418 _cairo_pattern_acquire_surface (cairo_pattern_t            *pattern,
01419                             cairo_surface_t                *dst,
01420                             int                     x,
01421                             int                     y,
01422                             unsigned int            width,
01423                             unsigned int            height,
01424                             cairo_surface_t                **surface_out,
01425                             cairo_surface_attributes_t *attributes)
01426 {
01427     cairo_status_t status;
01428     
01429     if (pattern->status) {
01430        *surface_out = NULL;
01431        attributes->acquired = FALSE;
01432        return pattern->status;
01433     }
01434 
01435     switch (pattern->type) {
01436     case CAIRO_PATTERN_SOLID: {
01437        cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) pattern;
01438        
01439        status = _cairo_pattern_acquire_surface_for_solid (src, dst,
01440                                                     x, y, width, height,
01441                                                     surface_out,
01442                                                     attributes);
01443        } break;
01444     case CAIRO_PATTERN_LINEAR:
01445     case CAIRO_PATTERN_RADIAL: {
01446        cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern;
01447 
01448        /* fast path for gradients with less than 2 color stops */
01449        if (src->n_stops < 2)
01450        {
01451            const cairo_color_t *color;
01452            cairo_solid_pattern_t solid;
01453 
01454            if (src->n_stops)
01455               color = &src->stops->color;
01456            else
01457               color = CAIRO_COLOR_TRANSPARENT;
01458 
01459            _cairo_pattern_init_solid (&solid, color);
01460 
01461            status = _cairo_pattern_acquire_surface_for_solid (&solid, dst,
01462                                                         x, y,
01463                                                         width, height,
01464                                                         surface_out,
01465                                                         attributes);
01466        }
01467        else
01468        {
01469            status = _cairo_pattern_acquire_surface_for_gradient (src, dst,
01470                                                           x, y,
01471                                                           width, height,
01472                                                           surface_out,
01473                                                           attributes);
01474        }
01475     } break;
01476     case CAIRO_PATTERN_SURFACE: {
01477        cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) pattern;
01478        
01479        status = _cairo_pattern_acquire_surface_for_surface (src, dst,
01480                                                       x, y, width, height,
01481                                                       surface_out,
01482                                                       attributes);
01483     } break;
01484     default:
01485        status = CAIRO_INT_STATUS_UNSUPPORTED;
01486     }
01487 
01488     return status;
01489 }
01490 
01499 void
01500 _cairo_pattern_release_surface (cairo_pattern_t            *pattern,
01501                             cairo_surface_t                *surface,
01502                             cairo_surface_attributes_t *attributes)
01503 {
01504     if (attributes->acquired)
01505     {
01506        cairo_surface_pattern_t *surface_pattern;
01507 
01508        assert (pattern->type == CAIRO_PATTERN_SURFACE);
01509        surface_pattern = (cairo_surface_pattern_t *) pattern;
01510 
01511        _cairo_surface_release_source_image (surface_pattern->surface,
01512                                         (cairo_image_surface_t *) surface,
01513                                         attributes->extra);
01514     }
01515     else
01516     {
01517        cairo_surface_destroy (surface);
01518     }
01519 }
01520 
01521 cairo_int_status_t
01522 _cairo_pattern_acquire_surfaces (cairo_pattern_t     *src,
01523                              cairo_pattern_t         *mask,
01524                              cairo_surface_t         *dst,
01525                              int                     src_x,
01526                              int                     src_y,
01527                              int                     mask_x,
01528                              int                     mask_y,
01529                              unsigned int            width,
01530                              unsigned int            height,
01531                              cairo_surface_t         **src_out,
01532                              cairo_surface_t         **mask_out,
01533                              cairo_surface_attributes_t *src_attributes,
01534                              cairo_surface_attributes_t *mask_attributes)
01535 {
01536     cairo_int_status_t        status;
01537     cairo_pattern_union_t src_tmp, mask_tmp;
01538 
01539     if (src->status)
01540        return src->status;
01541     if (mask && mask->status)
01542        return mask->status;
01543 
01544     /* If src and mask are both solid, then the mask alpha can be
01545      * combined into src and mask can be ignored. */
01546 
01547     /* XXX: This optimization assumes that there is no color
01548      * information in mask, so this will need to change when we
01549      * support RENDER-style 4-channel masks. */
01550     if (src->type == CAIRO_PATTERN_SOLID &&
01551        mask && mask->type == CAIRO_PATTERN_SOLID)
01552     {
01553        cairo_color_t combined;
01554        cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src;
01555        cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask;
01556 
01557        combined = src_solid->color;
01558        _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha);
01559 
01560        _cairo_pattern_init_solid (&src_tmp.solid, &combined);
01561 
01562        mask = NULL;
01563     }
01564     else
01565     {
01566        _cairo_pattern_init_copy (&src_tmp.base, src);
01567     }
01568 
01569     status = _cairo_pattern_acquire_surface (&src_tmp.base, dst,
01570                                         src_x, src_y,
01571                                         width, height,
01572                                         src_out, src_attributes);
01573     if (status) {
01574        _cairo_pattern_fini (&src_tmp.base);
01575        return status;
01576     }
01577 
01578     if (mask == NULL)
01579     {
01580        _cairo_pattern_fini (&src_tmp.base);
01581        *mask_out = NULL;
01582        return CAIRO_STATUS_SUCCESS;
01583     }
01584 
01585     _cairo_pattern_init_copy (&mask_tmp.base, mask);
01586        
01587     status = _cairo_pattern_acquire_surface (&mask_tmp.base, dst,
01588                                         mask_x, mask_y,
01589                                         width, height,
01590                                         mask_out, mask_attributes);
01591     
01592     if (status)
01593        _cairo_pattern_release_surface (&src_tmp.base,
01594                                    *src_out, src_attributes);
01595 
01596     _cairo_pattern_fini (&src_tmp.base);
01597     _cairo_pattern_fini (&mask_tmp.base);
01598 
01599     return status;
01600 }