Back to index

lightning-sunbird  0.9+nobinonly
cairo-clip.c
Go to the documentation of this file.
00001 /* cairo - a vector graphics library with display and print output
00002  *
00003  * Copyright © 2002 University of Southern California
00004  * Copyright © 2005 Red Hat, Inc.
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it either under the terms of the GNU Lesser General Public
00008  * License version 2.1 as published by the Free Software Foundation
00009  * (the "LGPL") or, at your option, under the terms of the Mozilla
00010  * Public License Version 1.1 (the "MPL"). If you do not alter this
00011  * notice, a recipient may use your version of this file under either
00012  * the MPL or the LGPL.
00013  *
00014  * You should have received a copy of the LGPL along with this library
00015  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00017  * You should have received a copy of the MPL along with this library
00018  * in the file COPYING-MPL-1.1
00019  *
00020  * The contents of this file are subject to the Mozilla Public License
00021  * Version 1.1 (the "License"); you may not use this file except in
00022  * compliance with the License. You may obtain a copy of the License at
00023  * http://www.mozilla.org/MPL/
00024  *
00025  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
00026  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
00027  * the specific language governing rights and limitations.
00028  *
00029  * The Original Code is the cairo graphics library.
00030  *
00031  * The Initial Developer of the Original Code is University of Southern
00032  * California.
00033  *
00034  * Contributor(s):
00035  *     Carl D. Worth <cworth@cworth.org>
00036  *     Kristian Høgsberg <krh@redhat.com>
00037  */
00038 
00039 #include "cairoint.h"
00040 #include "cairo-clip-private.h"
00041 
00042 static cairo_clip_path_t *
00043 _cairo_clip_path_reference (cairo_clip_path_t *clip_path);
00044 
00045 static void
00046 _cairo_clip_path_destroy (cairo_clip_path_t *clip_path);
00047 
00048 void
00049 _cairo_clip_init (cairo_clip_t *clip, cairo_surface_t *target)
00050 {
00051     clip->mode = _cairo_surface_get_clip_mode (target);
00052     clip->region = NULL;
00053     clip->surface = NULL;
00054     clip->serial = 0;
00055     clip->path = NULL;
00056 }
00057 
00058 void
00059 _cairo_clip_fini (cairo_clip_t *clip)
00060 {
00061     if (clip->surface)
00062        cairo_surface_destroy (clip->surface);
00063     clip->surface = NULL;
00064 
00065     if (clip->path)
00066        _cairo_clip_path_destroy (clip->path);
00067     clip->path = NULL;
00068 
00069     if (clip->region)
00070        pixman_region_destroy (clip->region);
00071     clip->region = NULL;
00072     clip->serial = 0;
00073 }
00074 
00075 void
00076 _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
00077 {
00078     if (other->region) {
00079        clip->region = pixman_region_create ();
00080        pixman_region_copy (clip->region, other->region);
00081     }
00082 
00083     cairo_surface_reference (other->surface);
00084     clip->surface = other->surface;
00085     _cairo_clip_path_reference (other->path);
00086     clip->path = other->path;
00087 }
00088 
00089 cairo_status_t
00090 _cairo_clip_reset (cairo_clip_t *clip)
00091 {
00092     /* destroy any existing clip-region artifacts */
00093     if (clip->surface)
00094        cairo_surface_destroy (clip->surface);
00095     clip->surface = NULL;
00096 
00097     if (clip->region)
00098        pixman_region_destroy (clip->region);
00099     clip->region = NULL;
00100 
00101     if (clip->path)
00102        _cairo_clip_path_destroy (clip->path);
00103     clip->path = NULL;
00104 
00105     clip->serial = 0;
00106     
00107     return CAIRO_STATUS_SUCCESS;
00108 }
00109 
00110 cairo_status_t
00111 _cairo_clip_intersect_to_rectangle (cairo_clip_t      *clip,
00112                                 cairo_rectangle_t *rectangle)
00113 {
00114     if (clip->path) {
00115        /* Intersect path extents here. */
00116     }
00117 
00118     if (clip->region) {
00119        pixman_region16_t *intersection;
00120        cairo_status_t status = CAIRO_STATUS_SUCCESS;
00121        pixman_region_status_t pixman_status;
00122        
00123        intersection = _cairo_region_create_from_rectangle (rectangle);
00124        if (intersection == NULL)
00125            return CAIRO_STATUS_NO_MEMORY;
00126        
00127        pixman_status = pixman_region_intersect (intersection,
00128                                      clip->region,
00129                                      intersection);
00130        if (pixman_status == PIXMAN_REGION_STATUS_SUCCESS)
00131            _cairo_region_extents_rectangle (intersection, rectangle);
00132        else
00133            status = CAIRO_STATUS_NO_MEMORY;
00134 
00135        pixman_region_destroy (intersection);
00136 
00137        if (status)
00138            return status;
00139     }
00140 
00141     if (clip->surface)
00142        _cairo_rectangle_intersect (rectangle, &clip->surface_rect);
00143 
00144     return CAIRO_STATUS_SUCCESS;
00145 }
00146 
00147 cairo_status_t
00148 _cairo_clip_intersect_to_region (cairo_clip_t      *clip,
00149                              pixman_region16_t *region)
00150 {
00151     if (clip->path) {
00152        /* Intersect clip path into region. */
00153     }
00154 
00155     if (clip->region)
00156        pixman_region_intersect (region, clip->region, region);
00157 
00158     if (clip->surface) {
00159        pixman_region16_t *clip_rect;
00160        pixman_region_status_t pixman_status;
00161        cairo_status_t status = CAIRO_STATUS_SUCCESS;
00162     
00163        clip_rect = _cairo_region_create_from_rectangle (&clip->surface_rect);
00164        if (clip_rect == NULL)
00165            return CAIRO_STATUS_NO_MEMORY;
00166        
00167        pixman_status = pixman_region_intersect (region,
00168                                            clip_rect,
00169                                            region);
00170        if (pixman_status != PIXMAN_REGION_STATUS_SUCCESS)
00171            status = CAIRO_STATUS_NO_MEMORY;
00172 
00173        pixman_region_destroy (clip_rect);
00174 
00175        if (status)
00176            return status;
00177     }
00178 
00179     return CAIRO_STATUS_SUCCESS;
00180 }
00181 
00182 /* Combines the region of clip->surface given by extents in
00183  * device backend coordinates into the given temporary surface,
00184  * which has its origin at dst_x, dst_y in backend coordinates
00185  */
00186 cairo_status_t
00187 _cairo_clip_combine_to_surface (cairo_clip_t            *clip,
00188                             cairo_operator_t         operator,
00189                             cairo_surface_t         *dst,
00190                             int                      dst_x,
00191                             int                      dst_y,
00192                             const cairo_rectangle_t *extents)
00193 {
00194     cairo_pattern_union_t pattern;
00195     cairo_status_t status;
00196 
00197     _cairo_pattern_init_for_surface (&pattern.surface, clip->surface);
00198     
00199     status = _cairo_surface_composite (operator,
00200                                    &pattern.base,
00201                                    NULL,
00202                                    dst,
00203                                    extents->x - clip->surface_rect.x,
00204                                    extents->y - clip->surface_rect.y, 
00205                                    0, 0,
00206                                    extents->x - dst_x,
00207                                    extents->y - dst_y,
00208                                    extents->width, extents->height);
00209     
00210     _cairo_pattern_fini (&pattern.base);
00211 
00212     return status;
00213 }
00214 
00215 static cairo_status_t
00216 _cairo_clip_intersect_path (cairo_clip_t       *clip,
00217                          cairo_path_fixed_t *path,
00218                          cairo_fill_rule_t   fill_rule,
00219                          double              tolerance,
00220                          cairo_antialias_t   antialias,
00221                          cairo_surface_t    *target)
00222 {
00223     cairo_clip_path_t *clip_path;
00224     cairo_status_t status;
00225 
00226     if (clip->mode != CAIRO_CLIP_MODE_PATH)
00227        return CAIRO_INT_STATUS_UNSUPPORTED;
00228 
00229     clip_path = malloc (sizeof (cairo_clip_path_t));
00230     if (clip_path == NULL)
00231        return CAIRO_STATUS_NO_MEMORY;
00232 
00233     status = _cairo_path_fixed_init_copy (&clip_path->path, path);
00234     if (status)
00235        return status;
00236 
00237     clip_path->ref_count = 1;
00238     clip_path->fill_rule = fill_rule;
00239     clip_path->tolerance = tolerance;
00240     clip_path->antialias = antialias;
00241     clip_path->prev = clip->path;
00242     clip->path = clip_path;
00243     clip->serial = _cairo_surface_allocate_clip_serial (target);
00244 
00245     return CAIRO_STATUS_SUCCESS;
00246 }
00247 
00248 static cairo_clip_path_t *
00249 _cairo_clip_path_reference (cairo_clip_path_t *clip_path)
00250 {
00251     if (clip_path == NULL)
00252        return NULL;
00253 
00254     clip_path->ref_count++;
00255 
00256     return clip_path;
00257 }
00258 
00259 static void
00260 _cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
00261 {
00262     if (clip_path == NULL)
00263        return;
00264 
00265     clip_path->ref_count--;
00266     if (clip_path->ref_count)
00267        return;
00268 
00269     _cairo_path_fixed_fini (&clip_path->path);
00270     _cairo_clip_path_destroy (clip_path->prev);
00271     free (clip_path);
00272 }
00273 
00274 static cairo_status_t
00275 _cairo_clip_intersect_region (cairo_clip_t    *clip,
00276                            cairo_traps_t   *traps,
00277                            cairo_surface_t *target)
00278 {
00279     pixman_region16_t *region;
00280     cairo_status_t status;
00281 
00282     if (clip->mode != CAIRO_CLIP_MODE_REGION)
00283        return CAIRO_INT_STATUS_UNSUPPORTED;
00284     
00285     status = _cairo_traps_extract_region (traps, &region);
00286     if (status)
00287        return status;
00288        
00289     if (region == NULL)
00290        return CAIRO_INT_STATUS_UNSUPPORTED;
00291 
00292     status = CAIRO_STATUS_SUCCESS;
00293     if (clip->region == NULL) {
00294        clip->region = region;
00295     } else {
00296        pixman_region16_t *intersection = pixman_region_create();
00297     
00298        if (pixman_region_intersect (intersection, 
00299                                  clip->region, region)
00300            == PIXMAN_REGION_STATUS_SUCCESS) {
00301            pixman_region_destroy (clip->region);
00302            clip->region = intersection;
00303        } else {             
00304            status = CAIRO_STATUS_NO_MEMORY;
00305        }
00306        pixman_region_destroy (region);
00307     }
00308 
00309     clip->serial = _cairo_surface_allocate_clip_serial (target);
00310 
00311     return status;
00312 }
00313 
00314 static cairo_status_t
00315 _cairo_clip_intersect_mask (cairo_clip_t      *clip,
00316                          cairo_traps_t     *traps,
00317                          cairo_antialias_t antialias,
00318                          cairo_surface_t   *target)
00319 {
00320     cairo_pattern_union_t pattern;
00321     cairo_box_t extents;
00322     cairo_rectangle_t surface_rect, target_rect;
00323     cairo_surface_t *surface;
00324     cairo_status_t status;
00325 
00326     /* Represent the clip as a mask surface.  We create a new surface
00327      * the size of the intersection of the old mask surface and the
00328      * extents of the new clip path. */
00329 
00330     _cairo_traps_extents (traps, &extents);
00331     _cairo_box_round_to_rectangle (&extents, &surface_rect);
00332 
00333     if (clip->surface != NULL)
00334        _cairo_rectangle_intersect (&surface_rect, &clip->surface_rect);
00335 
00336     /* Intersect with the target surface rectangle so we don't use
00337      * more memory and time than we need to. */
00338 
00339     status = _cairo_surface_get_extents (target, &target_rect);
00340     if (!status)
00341        _cairo_rectangle_intersect (&surface_rect, &target_rect);
00342 
00343     surface = _cairo_surface_create_similar_solid (target,
00344                                              CAIRO_CONTENT_ALPHA,
00345                                              surface_rect.width,
00346                                              surface_rect.height,
00347                                              CAIRO_COLOR_WHITE);
00348     if (surface->status)
00349        return CAIRO_STATUS_NO_MEMORY;
00350 
00351     /* Render the new clipping path into the new mask surface. */
00352 
00353     _cairo_traps_translate (traps, -surface_rect.x, -surface_rect.y);
00354     _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE);
00355     
00356     status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN,
00357                                             &pattern.base,
00358                                             surface,
00359                                             antialias,
00360                                             0, 0,
00361                                             0, 0,
00362                                             surface_rect.width,
00363                                             surface_rect.height,
00364                                             traps->traps,
00365                                             traps->num_traps);
00366 
00367     _cairo_pattern_fini (&pattern.base);
00368 
00369     if (status) {
00370        cairo_surface_destroy (surface);
00371        return status;
00372     }
00373 
00374     /* If there was a clip surface already, combine it with the new
00375      * mask surface using the IN operator, so we get the intersection
00376      * of the old and new clipping paths. */
00377 
00378     if (clip->surface != NULL) {
00379        _cairo_pattern_init_for_surface (&pattern.surface, clip->surface);
00380 
00381        status = _cairo_surface_composite (CAIRO_OPERATOR_IN,
00382                                       &pattern.base,
00383                                       NULL,
00384                                       surface,
00385                                       surface_rect.x - clip->surface_rect.x,
00386                                       surface_rect.y - clip->surface_rect.y,
00387                                       0, 0,
00388                                       0, 0,
00389                                       surface_rect.width,
00390                                       surface_rect.height);
00391 
00392        _cairo_pattern_fini (&pattern.base);
00393 
00394        if (status) {
00395            cairo_surface_destroy (surface);
00396            return status;
00397        }
00398 
00399        cairo_surface_destroy (clip->surface);
00400     }
00401 
00402     clip->surface = surface;
00403     clip->surface_rect = surface_rect;
00404     clip->serial = _cairo_surface_allocate_clip_serial (target);
00405 
00406     return status;
00407 }
00408 
00409 cairo_status_t
00410 _cairo_clip_clip (cairo_clip_t       *clip,
00411                 cairo_path_fixed_t *path,
00412                 cairo_fill_rule_t   fill_rule,
00413                 double              tolerance,
00414                 cairo_antialias_t   antialias,
00415                 cairo_surface_t    *target)
00416 {
00417     cairo_status_t status;
00418     cairo_traps_t traps;
00419     
00420     status = _cairo_clip_intersect_path (clip,
00421                                     path, fill_rule, tolerance,
00422                                     antialias, target);
00423     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
00424        return status;
00425 
00426     _cairo_traps_init (&traps);
00427     status = _cairo_path_fixed_fill_to_traps (path,
00428                                          fill_rule,
00429                                          tolerance,
00430                                          &traps);
00431     if (status)
00432        goto bail;
00433 
00434     status = _cairo_clip_intersect_region (clip, &traps, target);
00435     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
00436        goto bail;
00437 
00438     status = _cairo_clip_intersect_mask (clip, &traps, antialias, target);
00439        
00440  bail:
00441     _cairo_traps_fini (&traps);
00442 
00443     return status;
00444 }