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 a region from an array of rectangles */
561 struct region
*create_region( const rectangle_t
*rects
, unsigned int nb_rects
)
563 struct region
*region
;
564 unsigned int size
= max( nb_rects
, RGN_DEFAULT_RECTS
);
566 if (!validate_rectangles( rects
, nb_rects
))
568 set_error( STATUS_INVALID_PARAMETER
);
571 if (!(region
= mem_alloc( sizeof(*region
) ))) return NULL
;
572 if (!(region
->rects
= mem_alloc( size
* sizeof(*region
->rects
) )))
578 region
->num_rects
= nb_rects
;
579 memcpy( region
->rects
, rects
, nb_rects
* sizeof(*rects
) );
580 set_region_extents( region
);
584 /* create a region from request data */
585 struct region
*create_region_from_req_data( const void *data
, size_t size
)
587 const rectangle_t
*rects
= data
;
588 int nb_rects
= size
/ sizeof(rectangle_t
);
590 /* special case: empty region can be specified by a single all-zero rectangle */
591 if (nb_rects
== 1 && !memcmp( rects
, &empty_rect
, sizeof(empty_rect
) )) nb_rects
= 0;
592 return create_region( rects
, nb_rects
);
596 void free_region( struct region
*region
)
598 free( region
->rects
);
602 /* set region to a simple rectangle */
603 void set_region_rect( struct region
*region
, const rectangle_t
*rect
)
605 if (rect
->left
< rect
->right
&& rect
->top
< rect
->bottom
)
607 region
->num_rects
= 1;
608 region
->rects
[0] = region
->extents
= *rect
;
612 region
->num_rects
= 0;
613 region
->extents
.left
= 0;
614 region
->extents
.top
= 0;
615 region
->extents
.right
= 0;
616 region
->extents
.bottom
= 0;
620 /* retrieve the region data for sending to the client */
621 rectangle_t
*get_region_data( const struct region
*region
, size_t *total_size
)
623 if (!(*total_size
= region
->num_rects
* sizeof(rectangle_t
)))
625 /* return a single empty rect for empty regions */
626 *total_size
= sizeof(empty_rect
);
627 return memdup( &empty_rect
, sizeof(empty_rect
) );
629 return memdup( region
->rects
, *total_size
);
632 /* retrieve the region data for sending to the client and free the region at the same time */
633 rectangle_t
*get_region_data_and_free( struct region
*region
, size_t *total_size
)
635 rectangle_t
*ret
= region
->rects
;
637 if (!(*total_size
= region
->num_rects
* sizeof(rectangle_t
)))
639 /* return a single empty rect for empty regions */
640 *total_size
= sizeof(empty_rect
);
641 ret
= memdup( &empty_rect
, sizeof(empty_rect
) );
647 /* check if a given region is empty */
648 int is_region_empty( const struct region
*region
)
650 return region
->num_rects
== 0;
654 /* get the extents rect of a region */
655 void get_region_extents( const struct region
*region
, rectangle_t
*rect
)
657 *rect
= region
->extents
;
660 /* add an offset to a region */
661 void offset_region( struct region
*region
, int x
, int y
)
663 rectangle_t
*rect
, *end
;
665 for (rect
= region
->rects
, end
= rect
+ region
->num_rects
; rect
< end
; rect
++)
672 region
->extents
.left
+= x
;
673 region
->extents
.right
+= x
;
674 region
->extents
.top
+= y
;
675 region
->extents
.bottom
+= y
;
678 /* make a copy of a region; returns dst or NULL on error */
679 struct region
*copy_region( struct region
*dst
, const struct region
*src
)
681 if (dst
== src
) return dst
;
683 if (dst
->size
< src
->num_rects
)
685 rectangle_t
*rect
= realloc( dst
->rects
, src
->num_rects
* sizeof(*rect
) );
688 set_error( STATUS_NO_MEMORY
);
692 dst
->size
= src
->num_rects
;
694 dst
->num_rects
= src
->num_rects
;
695 dst
->extents
= src
->extents
;
696 memcpy( dst
->rects
, src
->rects
, src
->num_rects
* sizeof(*dst
->rects
) );
700 /* compute the intersection of two regions into dst, which can be one of the source regions */
701 struct region
*intersect_region( struct region
*dst
, const struct region
*src1
,
702 const struct region
*src2
)
704 if (!src1
->num_rects
|| !src2
->num_rects
|| !EXTENTCHECK(&src1
->extents
, &src2
->extents
))
707 dst
->extents
.left
= 0;
708 dst
->extents
.top
= 0;
709 dst
->extents
.right
= 0;
710 dst
->extents
.bottom
= 0;
713 if (!region_op( dst
, src1
, src2
, intersect_overlapping
, NULL
, NULL
)) return NULL
;
714 set_region_extents( dst
);
718 /* compute the subtraction of two regions into dst, which can be one of the source regions */
719 struct region
*subtract_region( struct region
*dst
, const struct region
*src1
,
720 const struct region
*src2
)
722 if (!src1
->num_rects
|| !src2
->num_rects
|| !EXTENTCHECK(&src1
->extents
, &src2
->extents
))
723 return copy_region( dst
, src1
);
725 if (!region_op( dst
, src1
, src2
, subtract_overlapping
,
726 subtract_non_overlapping
, NULL
)) return NULL
;
727 set_region_extents( dst
);
731 /* compute the union of two regions into dst, which can be one of the source regions */
732 struct region
*union_region( struct region
*dst
, const struct region
*src1
,
733 const struct region
*src2
)
735 if (src1
== src2
) return copy_region( dst
, src1
);
736 if (!src1
->num_rects
) return copy_region( dst
, src2
);
737 if (!src2
->num_rects
) return copy_region( dst
, src1
);
739 if ((src1
->num_rects
== 1) &&
740 (src1
->extents
.left
<= src2
->extents
.left
) &&
741 (src1
->extents
.top
<= src2
->extents
.top
) &&
742 (src1
->extents
.right
>= src2
->extents
.right
) &&
743 (src1
->extents
.bottom
>= src2
->extents
.bottom
))
744 return copy_region( dst
, src1
);
746 if ((src2
->num_rects
== 1) &&
747 (src2
->extents
.left
<= src1
->extents
.left
) &&
748 (src2
->extents
.top
<= src1
->extents
.top
) &&
749 (src2
->extents
.right
>= src1
->extents
.right
) &&
750 (src2
->extents
.bottom
>= src1
->extents
.bottom
))
751 return copy_region( dst
, src2
);
753 if (!region_op( dst
, src1
, src2
, union_overlapping
,
754 union_non_overlapping
, union_non_overlapping
)) return NULL
;
756 dst
->extents
.left
= min(src1
->extents
.left
, src2
->extents
.left
);
757 dst
->extents
.top
= min(src1
->extents
.top
, src2
->extents
.top
);
758 dst
->extents
.right
= max(src1
->extents
.right
, src2
->extents
.right
);
759 dst
->extents
.bottom
= max(src1
->extents
.bottom
, src2
->extents
.bottom
);
763 /* compute the exclusive or of two regions into dst, which can be one of the source regions */
764 struct region
*xor_region( struct region
*dst
, const struct region
*src1
,
765 const struct region
*src2
)
767 struct region
*tmp
= create_empty_region();
769 if (!tmp
) return NULL
;
771 if (!subtract_region( tmp
, src1
, src2
) ||
772 !subtract_region( dst
, src2
, src1
) ||
773 !union_region( dst
, dst
, tmp
))
780 /* check if the given point is inside the region */
781 int point_in_region( struct region
*region
, int x
, int y
)
783 const rectangle_t
*ptr
, *end
;
785 for (ptr
= region
->rects
, end
= region
->rects
+ region
->num_rects
; ptr
< end
; ptr
++)
787 if (ptr
->top
> y
) return 0;
788 if (ptr
->bottom
<= y
) continue;
789 /* now we are in the correct band */
790 if (ptr
->left
> x
) return 0;
791 if (ptr
->right
<= x
) continue;
797 /* check if the given rectangle is (at least partially) inside the region */
798 int rect_in_region( struct region
*region
, const rectangle_t
*rect
)
800 const rectangle_t
*ptr
, *end
;
802 for (ptr
= region
->rects
, end
= region
->rects
+ region
->num_rects
; ptr
< end
; ptr
++)
804 if (ptr
->top
>= rect
->bottom
) return 0;
805 if (ptr
->bottom
<= rect
->top
) continue;
806 if (ptr
->left
>= rect
->right
) continue;
807 if (ptr
->right
<= rect
->left
) continue;