2 * Server-side region objects. Based on the X11 implementation.
4 * Copyright 1993, 1994, 1995, 2004 Alexandre Julliard
5 * Modifications and additions: Copyright 1998 Huw Davies
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * This is a simplified version of the code, without all the explanations.
24 * Check the equivalent GDI code to make sense of it.
27 /************************************************************************
29 Copyright (c) 1987, 1988 X Consortium
31 Permission is hereby granted, free of charge, to any person obtaining a copy
32 of this software and associated documentation files (the "Software"), to deal
33 in the Software without restriction, including without limitation the rights
34 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35 copies of the Software, and to permit persons to whom the Software is
36 furnished to do so, subject to the following conditions:
38 The above copyright notice and this permission notice shall be included in
39 all copies or substantial portions of the Software.
41 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
44 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
45 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
46 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48 Except as contained in this notice, the name of the X Consortium shall not be
49 used in advertising or otherwise to promote the sale, use or other dealings
50 in this Software without prior written authorization from the X Consortium.
53 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
57 Permission to use, copy, modify, and distribute this software and its
58 documentation for any purpose and without fee is hereby granted,
59 provided that the above copyright notice appear in all copies and that
60 both that copyright notice and this permission notice appear in
61 supporting documentation, and that the name of Digital not be
62 used in advertising or publicity pertaining to distribution of the
63 software without specific, written prior permission.
65 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
66 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
67 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
68 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
69 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
70 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
73 ************************************************************************/
90 #define RGN_DEFAULT_RECTS 2
92 #define EXTENTCHECK(r1, r2) \
93 ((r1)->right > (r2)->left && \
94 (r1)->left < (r2)->right && \
95 (r1)->bottom > (r2)->top && \
96 (r1)->top < (r2)->bottom)
98 typedef int (*overlap_func_t)( struct region *reg, const rectangle_t *r1, const rectangle_t *r1End,
99 const rectangle_t *r2, const rectangle_t *r2End, int top, int bottom );
100 typedef int (*non_overlap_func_t)( struct region *reg, const rectangle_t *r,
101 const rectangle_t *rEnd, int top, int bottom );
103 static const rectangle_t empty_rect; /* all-zero rectangle for empty regions */
105 /* add a rectangle to a region */
106 static inline rectangle_t *add_rect( struct region *reg )
108 if (reg->num_rects >= reg->size - 1)
110 rectangle_t *new_rect = realloc( reg->rects, 2 * sizeof(rectangle_t) * reg->size );
113 set_error( STATUS_NO_MEMORY );
116 reg->rects = new_rect;
119 return reg->rects + reg->num_rects++;
122 /* make sure all the rectangles are valid and that the region is properly y-x-banded */
123 static inline int validate_rectangles( const rectangle_t *rects, unsigned int nb_rects )
125 const rectangle_t *ptr, *end;
127 for (ptr = rects, end = rects + nb_rects; ptr < end; ptr++)
129 if (ptr->left >= ptr->right || ptr->top >= ptr->bottom) return 0; /* empty rectangle */
130 if (ptr == end - 1) break;
131 if (ptr[0].top == ptr[1].top) /* same band */
133 if (ptr[0].bottom != ptr[1].bottom) return 0; /* not same y extent */
134 if (ptr[0].right >= ptr[1].left) return 0; /* not properly x ordered */
138 if (ptr[0].bottom > ptr[1].top) return 0; /* not properly y ordered */
144 /* attempt to merge the rects in the current band with those in the */
145 /* previous one. Used only by region_op. */
146 static int coalesce_region( struct region *pReg, int prevStart, int curStart )
149 rectangle_t *pRegEnd = &pReg->rects[pReg->num_rects];
150 rectangle_t *pPrevRect = &pReg->rects[prevStart];
151 rectangle_t *pCurRect = &pReg->rects[curStart];
152 int prevNumRects = curStart - prevStart;
153 int bandtop = pCurRect->top;
155 for (curNumRects = 0;
156 (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
162 if (pCurRect != pRegEnd)
165 while (pRegEnd[-1].top == pRegEnd->top) pRegEnd--;
166 curStart = pRegEnd - pReg->rects;
167 pRegEnd = pReg->rects + pReg->num_rects;
170 if ((curNumRects == prevNumRects) && (curNumRects != 0))
172 pCurRect -= curNumRects;
173 if (pPrevRect->bottom == pCurRect->top)
177 if ((pPrevRect->left != pCurRect->left) ||
178 (pPrevRect->right != pCurRect->right)) return curStart;
182 } while (prevNumRects != 0);
184 pReg->num_rects -= curNumRects;
185 pCurRect -= curNumRects;
186 pPrevRect -= curNumRects;
190 pPrevRect->bottom = pCurRect->bottom;
194 } while (curNumRects != 0);
196 if (pCurRect == pRegEnd) curStart = prevStart;
197 else do { *pPrevRect++ = *pCurRect++; } while (pCurRect != pRegEnd);
204 /* apply an operation to two regions */
205 /* check the GDI version of the code for explanations */
206 static int region_op( struct region *newReg, const struct region *reg1, const struct region *reg2,
207 overlap_func_t overlap_func,
208 non_overlap_func_t non_overlap1_func,
209 non_overlap_func_t non_overlap2_func )
211 int ybot, ytop, top, bot, prevBand, curBand;
212 const rectangle_t *r1BandEnd, *r2BandEnd;
214 const rectangle_t *r1 = reg1->rects;
215 const rectangle_t *r2 = reg2->rects;
216 const rectangle_t *r1End = r1 + reg1->num_rects;
217 const rectangle_t *r2End = r2 + reg2->num_rects;
219 rectangle_t *new_rects, *old_rects = newReg->rects;
220 int new_size, ret = 0;
222 new_size = max( reg1->num_rects, reg2->num_rects ) * 2;
223 if (!(new_rects = mem_alloc( new_size * sizeof(*newReg->rects) ))) return 0;
225 newReg->size = new_size;
226 newReg->rects = new_rects;
227 newReg->num_rects = 0;
229 if (reg1->extents.top < reg2->extents.top)
230 ybot = reg1->extents.top;
232 ybot = reg2->extents.top;
238 curBand = newReg->num_rects;
241 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top)) r1BandEnd++;
244 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top)) r2BandEnd++;
246 if (r1->top < r2->top)
248 top = max(r1->top,ybot);
249 bot = min(r1->bottom,r2->top);
251 if ((top != bot) && non_overlap1_func)
253 if (!non_overlap1_func( newReg, r1, r1BandEnd, top, bot )) goto done;
258 else if (r2->top < r1->top)
260 top = max(r2->top,ybot);
261 bot = min(r2->bottom,r1->top);
263 if ((top != bot) && non_overlap2_func)
265 if (!non_overlap2_func( newReg, r2, r2BandEnd, top, bot )) goto done;
275 if (newReg->num_rects != curBand)
276 prevBand = coalesce_region(newReg, prevBand, curBand);
278 ybot = min(r1->bottom, r2->bottom);
279 curBand = newReg->num_rects;
282 if (!overlap_func( newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot )) goto done;
285 if (newReg->num_rects != curBand)
286 prevBand = coalesce_region(newReg, prevBand, curBand);
288 if (r1->bottom == ybot) r1 = r1BandEnd;
289 if (r2->bottom == ybot) r2 = r2BandEnd;
290 } while ((r1 != r1End) && (r2 != r2End));
292 curBand = newReg->num_rects;
295 if (non_overlap1_func)
300 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top)) r1BandEnd++;
301 if (!non_overlap1_func( newReg, r1, r1BandEnd, max(r1->top,ybot), r1->bottom ))
304 } while (r1 != r1End);
307 else if ((r2 != r2End) && non_overlap2_func)
312 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top)) r2BandEnd++;
313 if (!non_overlap2_func( newReg, r2, r2BandEnd, max(r2->top,ybot), r2->bottom ))
316 } while (r2 != r2End);
319 if (newReg->num_rects != curBand) coalesce_region(newReg, prevBand, curBand);
321 if ((newReg->num_rects < (newReg->size / 2)) && (newReg->size > 2))
323 new_size = max( newReg->num_rects, RGN_DEFAULT_RECTS );
324 if ((new_rects = realloc( newReg->rects, sizeof(*newReg->rects) * new_size )))
326 newReg->rects = new_rects;
327 newReg->size = new_size;
336 /* recalculate the extents of a region */
337 static void set_region_extents( struct region *region )
339 rectangle_t *pRect, *pRectEnd;
341 if (region->num_rects == 0)
343 region->extents.left = 0;
344 region->extents.top = 0;
345 region->extents.right = 0;
346 region->extents.bottom = 0;
350 pRect = region->rects;
351 pRectEnd = &pRect[region->num_rects - 1];
353 region->extents.left = pRect->left;
354 region->extents.top = pRect->top;
355 region->extents.right = pRectEnd->right;
356 region->extents.bottom = pRectEnd->bottom;
358 while (pRect <= pRectEnd)
360 if (pRect->left < region->extents.left) region->extents.left = pRect->left;
361 if (pRect->right > region->extents.right) region->extents.right = pRect->right;
366 /* handle an overlapping band for intersect_region */
367 static int intersect_overlapping( struct region *pReg,
368 const rectangle_t *r1, const rectangle_t *r1End,
369 const rectangle_t *r2, const rectangle_t *r2End,
370 int top, int bottom )
375 while ((r1 != r1End) && (r2 != r2End))
377 left = max(r1->left, r2->left);
378 right = min(r1->right, r2->right);
382 rectangle_t *rect = add_rect( pReg );
387 rect->bottom = bottom;
390 if (r1->right < r2->right) r1++;
391 else if (r2->right < r1->right) r2++;
401 /* handle a non-overlapping band for subtract_region */
402 static int subtract_non_overlapping( struct region *pReg, const rectangle_t *r,
403 const rectangle_t *rEnd, int top, int bottom )
407 rectangle_t *rect = add_rect( pReg );
409 rect->left = r->left;
411 rect->right = r->right;
412 rect->bottom = bottom;
418 /* handle an overlapping band for subtract_region */
419 static int subtract_overlapping( struct region *pReg,
420 const rectangle_t *r1, const rectangle_t *r1End,
421 const rectangle_t *r2, const rectangle_t *r2End,
422 int top, int bottom )
426 while ((r1 != r1End) && (r2 != r2End))
428 if (r2->right <= left) r2++;
429 else if (r2->left <= left)
432 if (left >= r1->right)
440 else if (r2->left < r1->right)
442 rectangle_t *rect = add_rect( pReg );
446 rect->right = r2->left;
447 rect->bottom = bottom;
449 if (left >= r1->right)
459 if (r1->right > left)
461 rectangle_t *rect = add_rect( pReg );
465 rect->right = r1->right;
466 rect->bottom = bottom;
475 rectangle_t *rect = add_rect( pReg );
479 rect->right = r1->right;
480 rect->bottom = bottom;
482 if (r1 != r1End) left = r1->left;
487 /* handle a non-overlapping band for union_region */
488 static int union_non_overlapping( struct region *pReg, const rectangle_t *r,
489 const rectangle_t *rEnd, int top, int bottom )
493 rectangle_t *rect = add_rect( pReg );
495 rect->left = r->left;
497 rect->right = r->right;
498 rect->bottom = bottom;
504 /* handle an overlapping band for union_region */
505 static int union_overlapping( struct region *pReg,
506 const rectangle_t *r1, const rectangle_t *r1End,
507 const rectangle_t *r2, const rectangle_t *r2End,
508 int top, int bottom )
510 #define MERGERECT(r) \
511 if ((pReg->num_rects != 0) && \
512 (pReg->rects[pReg->num_rects-1].top == top) && \
513 (pReg->rects[pReg->num_rects-1].bottom == bottom) && \
514 (pReg->rects[pReg->num_rects-1].right >= r->left)) \
516 if (pReg->rects[pReg->num_rects-1].right < r->right) \
518 pReg->rects[pReg->num_rects-1].right = r->right; \
523 rectangle_t *rect = add_rect( pReg ); \
524 if (!rect) return 0; \
526 rect->bottom = bottom; \
527 rect->left = r->left; \
528 rect->right = r->right; \
532 while ((r1 != r1End) && (r2 != r2End))
534 if (r1->left < r2->left)
549 } while (r1 != r1End);
551 else while (r2 != r2End)
560 /* create an empty region */
561 struct region *create_empty_region(void)
563 struct region *region;
565 if (!(region = mem_alloc( sizeof(*region) ))) return NULL;
566 if (!(region->rects = mem_alloc( RGN_DEFAULT_RECTS * sizeof(*region->rects) )))
571 region->size = RGN_DEFAULT_RECTS;
572 region->num_rects = 0;
573 region->extents.left = 0;
574 region->extents.top = 0;
575 region->extents.right = 0;
576 region->extents.bottom = 0;
580 /* create a region from request data */
581 struct region *create_region_from_req_data( const void *data, size_t size )
583 unsigned int alloc_rects;
584 struct region *region;
585 const rectangle_t *rects = data;
586 int nb_rects = size / sizeof(rectangle_t);
588 /* special case: empty region can be specified by a single all-zero rectangle */
589 if (nb_rects == 1 && !memcmp( rects, &empty_rect, sizeof(empty_rect) )) nb_rects = 0;
591 if (!validate_rectangles( rects, nb_rects ))
593 set_error( STATUS_INVALID_PARAMETER );
597 if (!(region = mem_alloc( sizeof(*region) ))) return NULL;
599 alloc_rects = max( nb_rects, RGN_DEFAULT_RECTS );
600 if (!(region->rects = mem_alloc( alloc_rects * sizeof(*region->rects) )))
605 region->size = alloc_rects;
606 region->num_rects = nb_rects;
607 memcpy( region->rects, rects, nb_rects * sizeof(*rects) );
608 set_region_extents( region );
613 void free_region( struct region *region )
615 free( region->rects );
619 /* set region to a simple rectangle */
620 void set_region_rect( struct region *region, const rectangle_t *rect )
622 if (rect->left < rect->right && rect->top < rect->bottom)
624 region->num_rects = 1;
625 region->rects[0] = region->extents = *rect;
629 region->num_rects = 0;
630 region->extents.left = 0;
631 region->extents.top = 0;
632 region->extents.right = 0;
633 region->extents.bottom = 0;
637 /* retrieve the region data for sending to the client */
638 rectangle_t *get_region_data( const struct region *region, size_t max_size, size_t *total_size )
640 const rectangle_t *data = region->rects;
642 if (!(*total_size = region->num_rects * sizeof(rectangle_t)))
644 /* return a single empty rect for empty regions */
645 *total_size = sizeof(empty_rect);
648 if (max_size >= *total_size) return memdup( data, *total_size );
649 set_error( STATUS_BUFFER_OVERFLOW );
653 /* retrieve the region data for sending to the client and free the region at the same time */
654 rectangle_t *get_region_data_and_free( struct region *region, size_t max_size, size_t *total_size )
656 rectangle_t *ret = region->rects;
658 if (!(*total_size = region->num_rects * sizeof(rectangle_t)))
660 /* return a single empty rect for empty regions */
661 *total_size = sizeof(empty_rect);
662 if (max_size >= sizeof(empty_rect))
664 ret = memdup( &empty_rect, sizeof(empty_rect) );
665 free( region->rects );
669 if (max_size < *total_size)
671 free( region->rects );
672 set_error( STATUS_BUFFER_OVERFLOW );
679 /* check if a given region is empty */
680 int is_region_empty( const struct region *region )
682 return region->num_rects == 0;
686 /* get the extents rect of a region */
687 void get_region_extents( const struct region *region, rectangle_t *rect )
689 *rect = region->extents;
692 /* add an offset to a region */
693 void offset_region( struct region *region, int x, int y )
695 rectangle_t *rect, *end;
697 if (!region->num_rects) return;
698 for (rect = region->rects, end = rect + region->num_rects; rect < end; rect++)
705 region->extents.left += x;
706 region->extents.right += x;
707 region->extents.top += y;
708 region->extents.bottom += y;
711 /* make a copy of a region; returns dst or NULL on error */
712 struct region *copy_region( struct region *dst, const struct region *src )
714 if (dst == src) return dst;
716 if (dst->size < src->num_rects)
718 rectangle_t *rect = realloc( dst->rects, src->num_rects * sizeof(*rect) );
721 set_error( STATUS_NO_MEMORY );
725 dst->size = src->num_rects;
727 dst->num_rects = src->num_rects;
728 dst->extents = src->extents;
729 memcpy( dst->rects, src->rects, src->num_rects * sizeof(*dst->rects) );
733 /* compute the intersection of two regions into dst, which can be one of the source regions */
734 struct region *intersect_region( struct region *dst, const struct region *src1,
735 const struct region *src2 )
737 if (!src1->num_rects || !src2->num_rects || !EXTENTCHECK(&src1->extents, &src2->extents))
740 dst->extents.left = 0;
741 dst->extents.top = 0;
742 dst->extents.right = 0;
743 dst->extents.bottom = 0;
746 if (!region_op( dst, src1, src2, intersect_overlapping, NULL, NULL )) return NULL;
747 set_region_extents( dst );
751 /* compute the subtraction of two regions into dst, which can be one of the source regions */
752 struct region *subtract_region( struct region *dst, const struct region *src1,
753 const struct region *src2 )
755 if (!src1->num_rects || !src2->num_rects || !EXTENTCHECK(&src1->extents, &src2->extents))
756 return copy_region( dst, src1 );
758 if (!region_op( dst, src1, src2, subtract_overlapping,
759 subtract_non_overlapping, NULL )) return NULL;
760 set_region_extents( dst );
764 /* compute the union of two regions into dst, which can be one of the source regions */
765 struct region *union_region( struct region *dst, const struct region *src1,
766 const struct region *src2 )
768 if (src1 == src2) return copy_region( dst, src1 );
769 if (!src1->num_rects) return copy_region( dst, src2 );
770 if (!src2->num_rects) return copy_region( dst, src1 );
772 if ((src1->num_rects == 1) &&
773 (src1->extents.left <= src2->extents.left) &&
774 (src1->extents.top <= src2->extents.top) &&
775 (src1->extents.right >= src2->extents.right) &&
776 (src1->extents.bottom >= src2->extents.bottom))
777 return copy_region( dst, src1 );
779 if ((src2->num_rects == 1) &&
780 (src2->extents.left <= src1->extents.left) &&
781 (src2->extents.top <= src1->extents.top) &&
782 (src2->extents.right >= src1->extents.right) &&
783 (src2->extents.bottom >= src1->extents.bottom))
784 return copy_region( dst, src2 );
786 if (!region_op( dst, src1, src2, union_overlapping,
787 union_non_overlapping, union_non_overlapping )) return NULL;
789 dst->extents.left = min(src1->extents.left, src2->extents.left);
790 dst->extents.top = min(src1->extents.top, src2->extents.top);
791 dst->extents.right = max(src1->extents.right, src2->extents.right);
792 dst->extents.bottom = max(src1->extents.bottom, src2->extents.bottom);
796 /* compute the exclusive or of two regions into dst, which can be one of the source regions */
797 struct region *xor_region( struct region *dst, const struct region *src1,
798 const struct region *src2 )
800 struct region *tmp = create_empty_region();
802 if (!tmp) return NULL;
804 if (!subtract_region( tmp, src1, src2 ) ||
805 !subtract_region( dst, src2, src1 ) ||
806 !union_region( dst, dst, tmp ))
813 /* check if the given point is inside the region */
814 int point_in_region( struct region *region, int x, int y )
816 const rectangle_t *ptr, *end;
818 for (ptr = region->rects, end = region->rects + region->num_rects; ptr < end; ptr++)
820 if (ptr->top > y) return 0;
821 if (ptr->bottom <= y) continue;
822 /* now we are in the correct band */
823 if (ptr->left > x) return 0;
824 if (ptr->right <= x) continue;
830 /* check if the given rectangle is (at least partially) inside the region */
831 int rect_in_region( struct region *region, const rectangle_t *rect )
833 const rectangle_t *ptr, *end;
835 for (ptr = region->rects, end = region->rects + region->num_rects; ptr < end; ptr++)
837 if (ptr->top >= rect->bottom) return 0;
838 if (ptr->bottom <= rect->top) continue;
839 if (ptr->left >= rect->right) continue;
840 if (ptr->right <= rect->left) continue;