Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright © 2004 Carl Worth
  3.  * Copyright © 2006 Red Hat, Inc.
  4.  * Copyright © 2009 Chris Wilson
  5.  * Copyright © 2011 Intel Corporation
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it either under the terms of the GNU Lesser General Public
  9.  * License version 2.1 as published by the Free Software Foundation
  10.  * (the "LGPL") or, at your option, under the terms of the Mozilla
  11.  * Public License Version 1.1 (the "MPL"). If you do not alter this
  12.  * notice, a recipient may use your version of this file under either
  13.  * the MPL or the LGPL.
  14.  *
  15.  * You should have received a copy of the LGPL along with this library
  16.  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
  17.  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
  18.  * You should have received a copy of the MPL along with this library
  19.  * in the file COPYING-MPL-1.1
  20.  *
  21.  * The contents of this file are subject to the Mozilla Public License
  22.  * Version 1.1 (the "License"); you may not use this file except in
  23.  * compliance with the License. You may obtain a copy of the License at
  24.  * http://www.mozilla.org/MPL/
  25.  *
  26.  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
  27.  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
  28.  * the specific language governing rights and limitations.
  29.  *
  30.  * The Original Code is the cairo graphics library.
  31.  *
  32.  * The Initial Developer of the Original Code is Carl Worth
  33.  *
  34.  * Contributor(s):
  35.  *      Carl D. Worth <cworth@cworth.org>
  36.  *      Chris Wilson <chris@chris-wilson.co.uk>
  37.  */
  38.  
  39. /* Provide definitions for standalone compilation */
  40. #include "cairoint.h"
  41.  
  42. #include "cairo-boxes-private.h"
  43. #include "cairo-error-private.h"
  44. #include "cairo-combsort-inline.h"
  45. #include "cairo-list-private.h"
  46.  
  47. #include <setjmp.h>
  48.  
  49. typedef struct _rectangle rectangle_t;
  50. typedef struct _edge edge_t;
  51.  
  52. struct _edge {
  53.     edge_t *next, *prev;
  54.     edge_t *right;
  55.     cairo_fixed_t x, top;
  56.     int a_or_b;
  57.     int dir;
  58. };
  59.  
  60. struct _rectangle {
  61.     edge_t left, right;
  62.     int32_t top, bottom;
  63. };
  64.  
  65. #define UNROLL3(x) x x x
  66.  
  67. /* the parent is always given by index/2 */
  68. #define PQ_PARENT_INDEX(i) ((i) >> 1)
  69. #define PQ_FIRST_ENTRY 1
  70.  
  71. /* left and right children are index * 2 and (index * 2) +1 respectively */
  72. #define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
  73.  
  74. typedef struct _pqueue {
  75.     int size, max_size;
  76.  
  77.     rectangle_t **elements;
  78.     rectangle_t *elements_embedded[1024];
  79. } pqueue_t;
  80.  
  81. typedef struct _sweep_line {
  82.     rectangle_t **rectangles;
  83.     pqueue_t pq;
  84.     edge_t head, tail;
  85.     edge_t *insert_left, *insert_right;
  86.     int32_t current_y;
  87.     int32_t last_y;
  88.  
  89.     jmp_buf unwind;
  90. } sweep_line_t;
  91.  
  92. #define DEBUG_TRAPS 0
  93.  
  94. #if DEBUG_TRAPS
  95. static void
  96. dump_traps (cairo_traps_t *traps, const char *filename)
  97. {
  98.     FILE *file;
  99.     int n;
  100.  
  101.     if (getenv ("CAIRO_DEBUG_TRAPS") == NULL)
  102.         return;
  103.  
  104.     file = fopen (filename, "a");
  105.     if (file != NULL) {
  106.         for (n = 0; n < traps->num_traps; n++) {
  107.             fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n",
  108.                      traps->traps[n].top,
  109.                      traps->traps[n].bottom,
  110.                      traps->traps[n].left.p1.x,
  111.                      traps->traps[n].left.p1.y,
  112.                      traps->traps[n].left.p2.x,
  113.                      traps->traps[n].left.p2.y,
  114.                      traps->traps[n].right.p1.x,
  115.                      traps->traps[n].right.p1.y,
  116.                      traps->traps[n].right.p2.x,
  117.                      traps->traps[n].right.p2.y);
  118.         }
  119.         fprintf (file, "\n");
  120.         fclose (file);
  121.     }
  122. }
  123. #else
  124. #define dump_traps(traps, filename)
  125. #endif
  126.  
  127. static inline int
  128. rectangle_compare_start (const rectangle_t *a,
  129.                          const rectangle_t *b)
  130. {
  131.     return a->top - b->top;
  132. }
  133.  
  134. static inline int
  135. rectangle_compare_stop (const rectangle_t *a,
  136.                          const rectangle_t *b)
  137. {
  138.     return a->bottom - b->bottom;
  139. }
  140.  
  141. static inline void
  142. pqueue_init (pqueue_t *pq)
  143. {
  144.     pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
  145.     pq->size = 0;
  146.  
  147.     pq->elements = pq->elements_embedded;
  148.     pq->elements[PQ_FIRST_ENTRY] = NULL;
  149. }
  150.  
  151. static inline void
  152. pqueue_fini (pqueue_t *pq)
  153. {
  154.     if (pq->elements != pq->elements_embedded)
  155.         free (pq->elements);
  156. }
  157.  
  158. static cairo_bool_t
  159. pqueue_grow (pqueue_t *pq)
  160. {
  161.     rectangle_t **new_elements;
  162.     pq->max_size *= 2;
  163.  
  164.     if (pq->elements == pq->elements_embedded) {
  165.         new_elements = _cairo_malloc_ab (pq->max_size,
  166.                                          sizeof (rectangle_t *));
  167.         if (unlikely (new_elements == NULL))
  168.             return FALSE;
  169.  
  170.         memcpy (new_elements, pq->elements_embedded,
  171.                 sizeof (pq->elements_embedded));
  172.     } else {
  173.         new_elements = _cairo_realloc_ab (pq->elements,
  174.                                           pq->max_size,
  175.                                           sizeof (rectangle_t *));
  176.         if (unlikely (new_elements == NULL))
  177.             return FALSE;
  178.     }
  179.  
  180.     pq->elements = new_elements;
  181.     return TRUE;
  182. }
  183.  
  184. static inline void
  185. pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle)
  186. {
  187.     rectangle_t **elements;
  188.     int i, parent;
  189.  
  190.     if (unlikely (sweep->pq.size + 1 == sweep->pq.max_size)) {
  191.         if (unlikely (! pqueue_grow (&sweep->pq))) {
  192.             longjmp (sweep->unwind,
  193.                      _cairo_error (CAIRO_STATUS_NO_MEMORY));
  194.         }
  195.     }
  196.  
  197.     elements = sweep->pq.elements;
  198.     for (i = ++sweep->pq.size;
  199.          i != PQ_FIRST_ENTRY &&
  200.          rectangle_compare_stop (rectangle,
  201.                                  elements[parent = PQ_PARENT_INDEX (i)]) < 0;
  202.          i = parent)
  203.     {
  204.         elements[i] = elements[parent];
  205.     }
  206.  
  207.     elements[i] = rectangle;
  208. }
  209.  
  210. static inline void
  211. pqueue_pop (pqueue_t *pq)
  212. {
  213.     rectangle_t **elements = pq->elements;
  214.     rectangle_t *tail;
  215.     int child, i;
  216.  
  217.     tail = elements[pq->size--];
  218.     if (pq->size == 0) {
  219.         elements[PQ_FIRST_ENTRY] = NULL;
  220.         return;
  221.     }
  222.  
  223.     for (i = PQ_FIRST_ENTRY;
  224.          (child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
  225.          i = child)
  226.     {
  227.         if (child != pq->size &&
  228.             rectangle_compare_stop (elements[child+1],
  229.                                     elements[child]) < 0)
  230.         {
  231.             child++;
  232.         }
  233.  
  234.         if (rectangle_compare_stop (elements[child], tail) >= 0)
  235.             break;
  236.  
  237.         elements[i] = elements[child];
  238.     }
  239.     elements[i] = tail;
  240. }
  241.  
  242. static inline rectangle_t *
  243. rectangle_pop_start (sweep_line_t *sweep_line)
  244. {
  245.     return *sweep_line->rectangles++;
  246. }
  247.  
  248. static inline rectangle_t *
  249. rectangle_peek_stop (sweep_line_t *sweep_line)
  250. {
  251.     return sweep_line->pq.elements[PQ_FIRST_ENTRY];
  252. }
  253.  
  254. CAIRO_COMBSORT_DECLARE (_rectangle_sort,
  255.                         rectangle_t *,
  256.                         rectangle_compare_start)
  257.  
  258. static void
  259. sweep_line_init (sweep_line_t    *sweep_line,
  260.                  rectangle_t    **rectangles,
  261.                  int              num_rectangles)
  262. {
  263.     _rectangle_sort (rectangles, num_rectangles);
  264.     rectangles[num_rectangles] = NULL;
  265.     sweep_line->rectangles = rectangles;
  266.  
  267.     sweep_line->head.x = INT32_MIN;
  268.     sweep_line->head.right = NULL;
  269.     sweep_line->head.dir = 0;
  270.     sweep_line->head.next = &sweep_line->tail;
  271.     sweep_line->tail.x = INT32_MAX;
  272.     sweep_line->tail.right = NULL;
  273.     sweep_line->tail.dir = 0;
  274.     sweep_line->tail.prev = &sweep_line->head;
  275.  
  276.     sweep_line->insert_left = &sweep_line->tail;
  277.     sweep_line->insert_right = &sweep_line->tail;
  278.  
  279.     sweep_line->current_y = INT32_MIN;
  280.     sweep_line->last_y = INT32_MIN;
  281.  
  282.     pqueue_init (&sweep_line->pq);
  283. }
  284.  
  285. static void
  286. sweep_line_fini (sweep_line_t *sweep_line)
  287. {
  288.     pqueue_fini (&sweep_line->pq);
  289. }
  290.  
  291. static void
  292. end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot, cairo_boxes_t *out)
  293. {
  294.     if (likely (left->top < bot)) {
  295.         cairo_status_t status;
  296.         cairo_box_t box;
  297.  
  298.         box.p1.x = left->x;
  299.         box.p1.y = left->top;
  300.         box.p2.x = left->right->x;
  301.         box.p2.y = bot;
  302.  
  303.         status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box);
  304.         if (unlikely (status))
  305.             longjmp (sweep_line->unwind, status);
  306.     }
  307.  
  308.     left->right = NULL;
  309. }
  310.  
  311. /* Start a new trapezoid at the given top y coordinate, whose edges
  312.  * are `edge' and `edge->next'. If `edge' already has a trapezoid,
  313.  * then either add it to the traps in `traps', if the trapezoid's
  314.  * right edge differs from `edge->next', or do nothing if the new
  315.  * trapezoid would be a continuation of the existing one. */
  316. static inline void
  317. start_or_continue_box (sweep_line_t *sweep_line,
  318.                        edge_t   *left,
  319.                        edge_t   *right,
  320.                        int               top,
  321.                        cairo_boxes_t *out)
  322. {
  323.     if (left->right == right)
  324.         return;
  325.  
  326.     if (left->right != NULL) {
  327.         if (right != NULL && left->right->x == right->x) {
  328.             /* continuation on right, so just swap edges */
  329.             left->right = right;
  330.             return;
  331.         }
  332.  
  333.         end_box (sweep_line, left, top, out);
  334.     }
  335.  
  336.     if (right != NULL && left->x != right->x) {
  337.         left->top = top;
  338.         left->right = right;
  339.     }
  340. }
  341.  
  342. static inline int is_zero(const int *winding)
  343. {
  344.     return winding[0] == 0 || winding[1] == 0;
  345. }
  346.  
  347. static inline void
  348. active_edges (sweep_line_t *sweep, cairo_boxes_t *out)
  349. {
  350.     int top = sweep->current_y;
  351.     int winding[2] = { 0 };
  352.     edge_t *pos;
  353.  
  354.     if (sweep->last_y == sweep->current_y)
  355.         return;
  356.  
  357.     pos = sweep->head.next;
  358.     if (pos == &sweep->tail)
  359.         return;
  360.  
  361.     do {
  362.         edge_t *left, *right;
  363.  
  364.         left = pos;
  365.         do {
  366.             winding[left->a_or_b] += left->dir;
  367.             if (!is_zero (winding))
  368.                 break;
  369.             if (left->next == &sweep->tail)
  370.                 goto out;
  371.  
  372.             if (unlikely (left->right != NULL))
  373.                 end_box (sweep, left, top, out);
  374.  
  375.             left = left->next;
  376.         } while (1);
  377.  
  378.         right = left->next;
  379.         do {
  380.             if (unlikely (right->right != NULL))
  381.                 end_box (sweep, right, top, out);
  382.  
  383.             winding[right->a_or_b] += right->dir;
  384.             if (is_zero (winding)) {
  385.                 /* skip co-linear edges */
  386.                 if (likely (right->x != right->next->x))
  387.                     break;
  388.             }
  389.  
  390.             right = right->next;
  391.         } while (TRUE);
  392.  
  393.         start_or_continue_box (sweep, left, right, top, out);
  394.  
  395.         pos = right->next;
  396.     } while (pos != &sweep->tail);
  397.  
  398. out:
  399.     sweep->last_y = sweep->current_y;
  400. }
  401.  
  402. static inline void
  403. sweep_line_delete_edge (sweep_line_t *sweep_line, edge_t *edge, cairo_boxes_t *out)
  404. {
  405.     if (edge->right != NULL) {
  406.         edge_t *next = edge->next;
  407.         if (next->x == edge->x) {
  408.             next->top = edge->top;
  409.             next->right = edge->right;
  410.         } else {
  411.             end_box (sweep_line, edge, sweep_line->current_y, out);
  412.         }
  413.     }
  414.  
  415.     if (sweep_line->insert_left == edge)
  416.         sweep_line->insert_left = edge->next;
  417.     if (sweep_line->insert_right == edge)
  418.         sweep_line->insert_right = edge->next;
  419.  
  420.     edge->prev->next = edge->next;
  421.     edge->next->prev = edge->prev;
  422. }
  423.  
  424. static inline void
  425. sweep_line_delete (sweep_line_t *sweep,
  426.                    rectangle_t  *rectangle,
  427.                    cairo_boxes_t *out)
  428. {
  429.     sweep_line_delete_edge (sweep, &rectangle->left, out);
  430.     sweep_line_delete_edge (sweep, &rectangle->right, out);
  431.  
  432.     pqueue_pop (&sweep->pq);
  433. }
  434.  
  435. static inline void
  436. insert_edge (edge_t *edge, edge_t *pos)
  437. {
  438.     if (pos->x != edge->x) {
  439.         if (pos->x > edge->x) {
  440.             do {
  441.                 UNROLL3({
  442.                     if (pos->prev->x <= edge->x)
  443.                         break;
  444.                     pos = pos->prev;
  445.                 })
  446.             } while (TRUE);
  447.         } else {
  448.             do {
  449.                 UNROLL3({
  450.                     pos = pos->next;
  451.                     if (pos->x >= edge->x)
  452.                         break;
  453.                 })
  454.             } while (TRUE);
  455.         }
  456.     }
  457.  
  458.     pos->prev->next = edge;
  459.     edge->prev = pos->prev;
  460.     edge->next = pos;
  461.     pos->prev = edge;
  462. }
  463.  
  464. static inline void
  465. sweep_line_insert (sweep_line_t *sweep, rectangle_t     *rectangle)
  466. {
  467.     edge_t *pos;
  468.  
  469.     /* right edge */
  470.     pos = sweep->insert_right;
  471.     insert_edge (&rectangle->right, pos);
  472.     sweep->insert_right = &rectangle->right;
  473.  
  474.     /* left edge */
  475.     pos = sweep->insert_left;
  476.     if (pos->x > sweep->insert_right->x)
  477.         pos = sweep->insert_right->prev;
  478.     insert_edge (&rectangle->left, pos);
  479.     sweep->insert_left = &rectangle->left;
  480.  
  481.     pqueue_push (sweep, rectangle);
  482. }
  483.  
  484. static cairo_status_t
  485. intersect (rectangle_t **rectangles, int num_rectangles, cairo_boxes_t *out)
  486. {
  487.     sweep_line_t sweep_line;
  488.     rectangle_t *rectangle;
  489.     cairo_status_t status;
  490.  
  491.     sweep_line_init (&sweep_line, rectangles, num_rectangles);
  492.     if ((status = setjmp (sweep_line.unwind)))
  493.         goto unwind;
  494.  
  495.     rectangle = rectangle_pop_start (&sweep_line);
  496.     do {
  497.         if (rectangle->top != sweep_line.current_y) {
  498.             rectangle_t *stop;
  499.  
  500.             stop = rectangle_peek_stop (&sweep_line);
  501.             while (stop != NULL && stop->bottom < rectangle->top) {
  502.                 if (stop->bottom != sweep_line.current_y) {
  503.                     active_edges (&sweep_line, out);
  504.                     sweep_line.current_y = stop->bottom;
  505.                 }
  506.  
  507.                 sweep_line_delete (&sweep_line, stop, out);
  508.  
  509.                 stop = rectangle_peek_stop (&sweep_line);
  510.             }
  511.  
  512.             active_edges (&sweep_line, out);
  513.             sweep_line.current_y = rectangle->top;
  514.         }
  515.  
  516.         sweep_line_insert (&sweep_line, rectangle);
  517.     } while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL);
  518.  
  519.     while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) {
  520.         if (rectangle->bottom != sweep_line.current_y) {
  521.             active_edges (&sweep_line, out);
  522.             sweep_line.current_y = rectangle->bottom;
  523.         }
  524.  
  525.         sweep_line_delete (&sweep_line, rectangle, out);
  526.     }
  527.  
  528. unwind:
  529.     sweep_line_fini (&sweep_line);
  530.     return status;
  531. }
  532.  
  533. static cairo_status_t
  534. _cairo_boxes_intersect_with_box (const cairo_boxes_t *boxes,
  535.                                  const cairo_box_t *box,
  536.                                  cairo_boxes_t *out)
  537. {
  538.     cairo_status_t status;
  539.     int i, j;
  540.  
  541.     if (out == boxes) { /* inplace update */
  542.         struct _cairo_boxes_chunk *chunk;
  543.  
  544.         out->num_boxes = 0;
  545.         for (chunk = &out->chunks; chunk != NULL; chunk = chunk->next) {
  546.             for (i = j = 0; i < chunk->count; i++) {
  547.                 cairo_box_t *b = &chunk->base[i];
  548.  
  549.                 b->p1.x = MAX (b->p1.x, box->p1.x);
  550.                 b->p1.y = MAX (b->p1.y, box->p1.y);
  551.                 b->p2.x = MIN (b->p2.x, box->p2.x);
  552.                 b->p2.y = MIN (b->p2.y, box->p2.y);
  553.                 if (b->p1.x < b->p2.x && b->p1.y < b->p2.y) {
  554.                     if (i != j)
  555.                         chunk->base[j] = *b;
  556.                     j++;
  557.                 }
  558.             }
  559.             /* XXX unlink empty chains? */
  560.             chunk->count = j;
  561.             out->num_boxes += j;
  562.         }
  563.     } else {
  564.         const struct _cairo_boxes_chunk *chunk;
  565.  
  566.         _cairo_boxes_clear (out);
  567.         _cairo_boxes_limit (out, box, 1);
  568.         for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
  569.             for (i = 0; i < chunk->count; i++) {
  570.                 status = _cairo_boxes_add (out,
  571.                                            CAIRO_ANTIALIAS_DEFAULT,
  572.                                            &chunk->base[i]);
  573.                 if (unlikely (status))
  574.                     return status;
  575.             }
  576.         }
  577.     }
  578.  
  579.     return CAIRO_STATUS_SUCCESS;
  580. }
  581.  
  582. cairo_status_t
  583. _cairo_boxes_intersect (const cairo_boxes_t *a,
  584.                         const cairo_boxes_t *b,
  585.                         cairo_boxes_t *out)
  586. {
  587.     rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)];
  588.     rectangle_t *rectangles;
  589.     rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1];
  590.     rectangle_t **rectangles_ptrs;
  591.     const struct _cairo_boxes_chunk *chunk;
  592.     cairo_status_t status;
  593.     int i, j, count;
  594.  
  595.     if (unlikely (a->num_boxes == 0 || b->num_boxes == 0)) {
  596.         _cairo_boxes_clear (out);
  597.         return CAIRO_STATUS_SUCCESS;
  598.     }
  599.  
  600.     if (a->num_boxes == 1) {
  601.         cairo_box_t box = a->chunks.base[0];
  602.         return _cairo_boxes_intersect_with_box (b, &box, out);
  603.     }
  604.     if (b->num_boxes == 1) {
  605.         cairo_box_t box = b->chunks.base[0];
  606.         return _cairo_boxes_intersect_with_box (a, &box, out);
  607.     }
  608.  
  609.     rectangles = stack_rectangles;
  610.     rectangles_ptrs = stack_rectangles_ptrs;
  611.     count = a->num_boxes + b->num_boxes;
  612.     if (count > ARRAY_LENGTH (stack_rectangles)) {
  613.         rectangles = _cairo_malloc_ab_plus_c (count,
  614.                                               sizeof (rectangle_t) +
  615.                                               sizeof (rectangle_t *),
  616.                                               sizeof (rectangle_t *));
  617.         if (unlikely (rectangles == NULL))
  618.             return _cairo_error (CAIRO_STATUS_NO_MEMORY);
  619.  
  620.         rectangles_ptrs = (rectangle_t **) (rectangles + count);
  621.     }
  622.  
  623.     j = 0;
  624.     for (chunk = &a->chunks; chunk != NULL; chunk = chunk->next) {
  625.         const cairo_box_t *box = chunk->base;
  626.         for (i = 0; i < chunk->count; i++) {
  627.             if (box[i].p1.x < box[i].p2.x) {
  628.                 rectangles[j].left.x = box[i].p1.x;
  629.                 rectangles[j].left.dir = 1;
  630.  
  631.                 rectangles[j].right.x = box[i].p2.x;
  632.                 rectangles[j].right.dir = -1;
  633.             } else {
  634.                 rectangles[j].right.x = box[i].p1.x;
  635.                 rectangles[j].right.dir = 1;
  636.  
  637.                 rectangles[j].left.x = box[i].p2.x;
  638.                 rectangles[j].left.dir = -1;
  639.             }
  640.  
  641.             rectangles[j].left.a_or_b = 0;
  642.             rectangles[j].left.right = NULL;
  643.             rectangles[j].right.a_or_b = 0;
  644.             rectangles[j].right.right = NULL;
  645.  
  646.             rectangles[j].top = box[i].p1.y;
  647.             rectangles[j].bottom = box[i].p2.y;
  648.  
  649.             rectangles_ptrs[j] = &rectangles[j];
  650.             j++;
  651.         }
  652.     }
  653.     for (chunk = &b->chunks; chunk != NULL; chunk = chunk->next) {
  654.         const cairo_box_t *box = chunk->base;
  655.         for (i = 0; i < chunk->count; i++) {
  656.             if (box[i].p1.x < box[i].p2.x) {
  657.                 rectangles[j].left.x = box[i].p1.x;
  658.                 rectangles[j].left.dir = 1;
  659.  
  660.                 rectangles[j].right.x = box[i].p2.x;
  661.                 rectangles[j].right.dir = -1;
  662.             } else {
  663.                 rectangles[j].right.x = box[i].p1.x;
  664.                 rectangles[j].right.dir = 1;
  665.  
  666.                 rectangles[j].left.x = box[i].p2.x;
  667.                 rectangles[j].left.dir = -1;
  668.             }
  669.  
  670.             rectangles[j].left.a_or_b = 1;
  671.             rectangles[j].left.right = NULL;
  672.             rectangles[j].right.a_or_b = 1;
  673.             rectangles[j].right.right = NULL;
  674.  
  675.             rectangles[j].top = box[i].p1.y;
  676.             rectangles[j].bottom = box[i].p2.y;
  677.  
  678.             rectangles_ptrs[j] = &rectangles[j];
  679.             j++;
  680.         }
  681.     }
  682.     assert (j == count);
  683.  
  684.     _cairo_boxes_clear (out);
  685.     status = intersect (rectangles_ptrs, j, out);
  686.     if (rectangles != stack_rectangles)
  687.         free (rectangles);
  688.  
  689.     return status;
  690. }
  691.