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 ************************************************************************/
89 #define RGN_DEFAULT_RECTS 2
91 #define EXTENTCHECK(r1, r2) \
92 ((r1)->right > (r2)->left && \
93 (r1)->left < (r2)->right && \
94 (r1)->bottom > (r2)->top && \
95 (r1)->top < (r2)->bottom)
97 typedef int (*overlap_func_t
)( struct region
*reg
, const rectangle_t
*r1
, const rectangle_t
*r1End
,
98 const rectangle_t
*r2
, const rectangle_t
*r2End
, int top
, int bottom
);
99 typedef int (*non_overlap_func_t
)( struct region
*reg
, const rectangle_t
*r
,
100 const rectangle_t
*rEnd
, int top
, int bottom
);
102 static const rectangle_t empty_rect
; /* all-zero rectangle for empty regions */
104 /* add a rectangle to a region */
105 static inline rectangle_t
*add_rect( struct region
*reg
)
107 if (reg
->num_rects
>= reg
->size
- 1)
109 rectangle_t
*new_rect
= realloc( reg
->rects
, 2 * sizeof(rectangle_t
) * reg
->size
);
112 set_error( STATUS_NO_MEMORY
);
115 reg
->rects
= new_rect
;
118 return reg
->rects
+ reg
->num_rects
++;
121 /* make sure all the rectangles are valid and that the region is properly y-x-banded */
122 static inline int validate_rectangles( const rectangle_t
*rects
, unsigned int nb_rects
)
124 const rectangle_t
*ptr
, *end
;
126 for (ptr
= rects
, end
= rects
+ nb_rects
; ptr
< end
; ptr
++)
128 if (ptr
->left
>= ptr
->right
|| ptr
->top
>= ptr
->bottom
) return 0; /* empty rectangle */
129 if (ptr
== end
- 1) break;
130 if (ptr
[0].top
== ptr
[1].top
) /* same band */
132 if (ptr
[0].bottom
!= ptr
[1].bottom
) return 0; /* not same y extent */
133 if (ptr
[0].right
>= ptr
[1].left
) return 0; /* not properly x ordered */
137 if (ptr
[0].bottom
> ptr
[1].top
) return 0; /* not properly y ordered */
143 /* attempt to merge the rects in the current band with those in the */
144 /* previous one. Used only by region_op. */
145 static int coalesce_region( struct region
*pReg
, int prevStart
, int curStart
)
148 rectangle_t
*pRegEnd
= &pReg
->rects
[pReg
->num_rects
];
149 rectangle_t
*pPrevRect
= &pReg
->rects
[prevStart
];
150 rectangle_t
*pCurRect
= &pReg
->rects
[curStart
];
151 int prevNumRects
= curStart
- prevStart
;
152 int bandtop
= pCurRect
->top
;
154 for (curNumRects
= 0;
155 (pCurRect
!= pRegEnd
) && (pCurRect
->top
== bandtop
);
161 if (pCurRect
!= pRegEnd
)
164 while (pRegEnd
[-1].top
== pRegEnd
->top
) pRegEnd
--;
165 curStart
= pRegEnd
- pReg
->rects
;
166 pRegEnd
= pReg
->rects
+ pReg
->num_rects
;
169 if ((curNumRects
== prevNumRects
) && (curNumRects
!= 0))
171 pCurRect
-= curNumRects
;
172 if (pPrevRect
->bottom
== pCurRect
->top
)
176 if ((pPrevRect
->left
!= pCurRect
->left
) ||
177 (pPrevRect
->right
!= pCurRect
->right
)) return curStart
;
181 } while (prevNumRects
!= 0);
183 pReg
->num_rects
-= curNumRects
;
184 pCurRect
-= curNumRects
;
185 pPrevRect
-= curNumRects
;
189 pPrevRect
->bottom
= pCurRect
->bottom
;
193 } while (curNumRects
!= 0);
195 if (pCurRect
== pRegEnd
) curStart
= prevStart
;
196 else do { *pPrevRect
++ = *pCurRect
++; } while (pCurRect
!= pRegEnd
);
203 /* apply an operation to two regions */
204 /* check the GDI version of the code for explanations */
205 static int region_op( struct region
*newReg
, const struct region
*reg1
, const struct region
*reg2
,
206 overlap_func_t overlap_func
,
207 non_overlap_func_t non_overlap1_func
,
208 non_overlap_func_t non_overlap2_func
)
210 int ybot
, ytop
, top
, bot
, prevBand
, curBand
;
211 const rectangle_t
*r1BandEnd
, *r2BandEnd
;
213 const rectangle_t
*r1
= reg1
->rects
;
214 const rectangle_t
*r2
= reg2
->rects
;
215 const rectangle_t
*r1End
= r1
+ reg1
->num_rects
;
216 const rectangle_t
*r2End
= r2
+ reg2
->num_rects
;
218 rectangle_t
*new_rects
, *old_rects
= newReg
->rects
;
219 int new_size
, ret
= 0;
221 new_size
= max( reg1
->num_rects
, reg2
->num_rects
) * 2;
222 if (!(new_rects
= mem_alloc( new_size
* sizeof(*newReg
->rects
) ))) return 0;
224 newReg
->size
= new_size
;
225 newReg
->rects
= new_rects
;
226 newReg
->num_rects
= 0;
228 if (reg1
->extents
.top
< reg2
->extents
.top
)
229 ybot
= reg1
->extents
.top
;
231 ybot
= reg2
->extents
.top
;
237 curBand
= newReg
->num_rects
;
240 while ((r1BandEnd
!= r1End
) && (r1BandEnd
->top
== r1
->top
)) r1BandEnd
++;
243 while ((r2BandEnd
!= r2End
) && (r2BandEnd
->top
== r2
->top
)) r2BandEnd
++;
245 if (r1
->top
< r2
->top
)
247 top
= max(r1
->top
,ybot
);
248 bot
= min(r1
->bottom
,r2
->top
);
250 if ((top
!= bot
) && non_overlap1_func
)
252 if (!non_overlap1_func( newReg
, r1
, r1BandEnd
, top
, bot
)) goto done
;
257 else if (r2
->top
< r1
->top
)
259 top
= max(r2
->top
,ybot
);
260 bot
= min(r2
->bottom
,r1
->top
);
262 if ((top
!= bot
) && non_overlap2_func
)
264 if (!non_overlap2_func( newReg
, r2
, r2BandEnd
, top
, bot
)) goto done
;
274 if (newReg
->num_rects
!= curBand
)
275 prevBand
= coalesce_region(newReg
, prevBand
, curBand
);
277 ybot
= min(r1
->bottom
, r2
->bottom
);
278 curBand
= newReg
->num_rects
;
281 if (!overlap_func( newReg
, r1
, r1BandEnd
, r2
, r2BandEnd
, ytop
, ybot
)) goto done
;
284 if (newReg
->num_rects
!= curBand
)
285 prevBand
= coalesce_region(newReg
, prevBand
, curBand
);
287 if (r1
->bottom
== ybot
) r1
= r1BandEnd
;
288 if (r2
->bottom
== ybot
) r2
= r2BandEnd
;
289 } while ((r1
!= r1End
) && (r2
!= r2End
));
291 curBand
= newReg
->num_rects
;
294 if (non_overlap1_func
)
299 while ((r1BandEnd
< r1End
) && (r1BandEnd
->top
== r1
->top
)) r1BandEnd
++;
300 if (!non_overlap1_func( newReg
, r1
, r1BandEnd
, max(r1
->top
,ybot
), r1
->bottom
))
303 } while (r1
!= r1End
);
306 else if ((r2
!= r2End
) && non_overlap2_func
)
311 while ((r2BandEnd
< r2End
) && (r2BandEnd
->top
== r2
->top
)) r2BandEnd
++;
312 if (!non_overlap2_func( newReg
, r2
, r2BandEnd
, max(r2
->top
,ybot
), r2
->bottom
))
315 } while (r2
!= r2End
);
318 if (newReg
->num_rects
!= curBand
) coalesce_region(newReg
, prevBand
, curBand
);
320 if ((newReg
->num_rects
< (newReg
->size
/ 2)) && (newReg
->size
> 2))
322 new_size
= max( newReg
->num_rects
, RGN_DEFAULT_RECTS
);
323 if ((new_rects
= realloc( newReg
->rects
, sizeof(*newReg
->rects
) * new_size
)))
325 newReg
->rects
= new_rects
;
326 newReg
->size
= new_size
;
335 /* recalculate the extents of a region */
336 static void set_region_extents( struct region
*region
)
338 rectangle_t
*pRect
, *pRectEnd
;
340 if (region
->num_rects
== 0)
342 region
->extents
.left
= 0;
343 region
->extents
.top
= 0;
344 region
->extents
.right
= 0;
345 region
->extents
.bottom
= 0;
349 pRect
= region
->rects
;
350 pRectEnd
= &pRect
[region
->num_rects
- 1];
352 region
->extents
.left
= pRect
->left
;
353 region
->extents
.top
= pRect
->top
;
354 region
->extents
.right
= pRectEnd
->right
;
355 region
->extents
.bottom
= pRectEnd
->bottom
;
357 while (pRect
<= pRectEnd
)
359 if (pRect
->left
< region
->extents
.left
) region
->extents
.left
= pRect
->left
;
360 if (pRect
->right
> region
->extents
.right
) region
->extents
.right
= pRect
->right
;
365 /* handle an overlapping band for intersect_region */
366 static int intersect_overlapping( struct region
*pReg
,
367 const rectangle_t
*r1
, const rectangle_t
*r1End
,
368 const rectangle_t
*r2
, const rectangle_t
*r2End
,
369 int top
, int bottom
)
374 while ((r1
!= r1End
) && (r2
!= r2End
))
376 left
= max(r1
->left
, r2
->left
);
377 right
= min(r1
->right
, r2
->right
);
381 rectangle_t
*rect
= add_rect( pReg
);
386 rect
->bottom
= bottom
;
389 if (r1
->right
< r2
->right
) r1
++;
390 else if (r2
->right
< r1
->right
) r2
++;
400 /* handle a non-overlapping band for subtract_region */
401 static int subtract_non_overlapping( struct region
*pReg
, const rectangle_t
*r
,
402 const rectangle_t
*rEnd
, int top
, int bottom
)
406 rectangle_t
*rect
= add_rect( pReg
);
408 rect
->left
= r
->left
;
410 rect
->right
= r
->right
;
411 rect
->bottom
= bottom
;
417 /* handle an overlapping band for subtract_region */
418 static int subtract_overlapping( struct region
*pReg
,
419 const rectangle_t
*r1
, const rectangle_t
*r1End
,
420 const rectangle_t
*r2
, const rectangle_t
*r2End
,
421 int top
, int bottom
)
425 while ((r1
!= r1End
) && (r2
!= r2End
))
427 if (r2
->right
<= left
) r2
++;
428 else if (r2
->left
<= left
)
431 if (left
>= r1
->right
)
439 else if (r2
->left
< r1
->right
)
441 rectangle_t
*rect
= add_rect( pReg
);
445 rect
->right
= r2
->left
;
446 rect
->bottom
= bottom
;
448 if (left
>= r1
->right
)
458 if (r1
->right
> left
)
460 rectangle_t
*rect
= add_rect( pReg
);
464 rect
->right
= r1
->right
;
465 rect
->bottom
= bottom
;
474 rectangle_t
*rect
= add_rect( pReg
);
478 rect
->right
= r1
->right
;
479 rect
->bottom
= bottom
;
481 if (r1
!= r1End
) left
= r1
->left
;
486 /* handle a non-overlapping band for union_region */
487 static int union_non_overlapping( struct region
*pReg
, const rectangle_t
*r
,
488 const rectangle_t
*rEnd
, int top
, int bottom
)
492 rectangle_t
*rect
= add_rect( pReg
);
494 rect
->left
= r
->left
;
496 rect
->right
= r
->right
;
497 rect
->bottom
= bottom
;
503 /* handle an overlapping band for union_region */
504 static int union_overlapping( struct region
*pReg
,
505 const rectangle_t
*r1
, const rectangle_t
*r1End
,
506 const rectangle_t
*r2
, const rectangle_t
*r2End
,
507 int top
, int bottom
)
509 #define MERGERECT(r) \
510 if ((pReg->num_rects != 0) && \
511 (pReg->rects[pReg->num_rects-1].top == top) && \
512 (pReg->rects[pReg->num_rects-1].bottom == bottom) && \
513 (pReg->rects[pReg->num_rects-1].right >= r->left)) \
515 if (pReg->rects[pReg->num_rects-1].right < r->right) \
517 pReg->rects[pReg->num_rects-1].right = r->right; \
522 rectangle_t *rect = add_rect( pReg ); \
523 if (!rect) return 0; \
525 rect->bottom = bottom; \
526 rect->left = r->left; \
527 rect->right = r->right; \
531 while ((r1
!= r1End
) && (r2
!= r2End
))
533 if (r1
->left
< r2
->left
)
548 } while (r1
!= r1End
);
550 else while (r2
!= r2End
)
559 /* create a region from an array of rectangles */
560 struct region
*create_region( const rectangle_t
*rects
, unsigned int nb_rects
)
562 struct region
*region
;
563 unsigned int size
= max( nb_rects
, RGN_DEFAULT_RECTS
);
565 if (!validate_rectangles( rects
, nb_rects
))
567 set_error( STATUS_INVALID_PARAMETER
);
570 if (!(region
= mem_alloc( sizeof(*region
) ))) return NULL
;
571 if (!(region
->rects
= mem_alloc( size
* sizeof(*region
->rects
) )))
577 region
->num_rects
= nb_rects
;
578 memcpy( region
->rects
, rects
, nb_rects
* sizeof(*rects
) );
579 set_region_extents( region
);
583 /* create a region from request data */
584 struct region
*create_region_from_req_data( const void *data
, size_t size
)
586 const rectangle_t
*rects
= data
;
587 int nb_rects
= size
/ sizeof(rectangle_t
);
589 /* special case: empty region can be specified by a single all-zero rectangle */
590 if (nb_rects
== 1 && !memcmp( rects
, &empty_rect
, sizeof(empty_rect
) )) nb_rects
= 0;
591 return create_region( rects
, nb_rects
);
595 void free_region( struct region
*region
)
597 free( region
->rects
);
601 /* set region to a simple rectangle */
602 void set_region_rect( struct region
*region
, const rectangle_t
*rect
)
604 if (rect
->left
< rect
->right
&& rect
->top
< rect
->bottom
)
606 region
->num_rects
= 1;
607 region
->rects
[0] = region
->extents
= *rect
;
611 region
->num_rects
= 0;
612 region
->extents
.left
= 0;
613 region
->extents
.top
= 0;
614 region
->extents
.right
= 0;
615 region
->extents
.bottom
= 0;
619 /* retrieve the region data for sending to the client */
620 rectangle_t
*get_region_data( const struct region
*region
, size_t *total_size
)
622 if (!(*total_size
= region
->num_rects
* sizeof(rectangle_t
)))
624 /* return a single empty rect for empty regions */
625 *total_size
= sizeof(empty_rect
);
626 return memdup( &empty_rect
, sizeof(empty_rect
) );
628 return memdup( region
->rects
, *total_size
);
631 /* retrieve the region data for sending to the client and free the region at the same time */
632 rectangle_t
*get_region_data_and_free( struct region
*region
, size_t *total_size
)
634 rectangle_t
*ret
= region
->rects
;
636 if (!(*total_size
= region
->num_rects
* sizeof(rectangle_t
)))
638 /* return a single empty rect for empty regions */
639 *total_size
= sizeof(empty_rect
);
640 ret
= memdup( &empty_rect
, sizeof(empty_rect
) );
646 /* check if a given region is empty */
647 int is_region_empty( const struct region
*region
)
649 return region
->num_rects
== 0;
653 /* get the extents rect of a region */
654 void get_region_extents( const struct region
*region
, rectangle_t
*rect
)
656 *rect
= region
->extents
;
659 /* add an offset to a region */
660 void offset_region( struct region
*region
, int x
, int y
)
662 rectangle_t
*rect
, *end
;
664 for (rect
= region
->rects
, end
= rect
+ region
->num_rects
; rect
< end
; rect
++)
671 region
->extents
.left
+= x
;
672 region
->extents
.right
+= x
;
673 region
->extents
.top
+= y
;
674 region
->extents
.bottom
+= y
;
677 /* make a copy of a region; returns dst or NULL on error */
678 struct region
*copy_region( struct region
*dst
, const struct region
*src
)
680 if (dst
== src
) return dst
;
682 if (dst
->size
< src
->num_rects
)
684 rectangle_t
*rect
= realloc( dst
->rects
, src
->num_rects
* sizeof(*rect
) );
687 set_error( STATUS_NO_MEMORY
);
691 dst
->size
= src
->num_rects
;
693 dst
->num_rects
= src
->num_rects
;
694 dst
->extents
= src
->extents
;
695 memcpy( dst
->rects
, src
->rects
, src
->num_rects
* sizeof(*dst
->rects
) );
699 /* compute the intersection of two regions into dst, which can be one of the source regions */
700 struct region
*intersect_region( struct region
*dst
, const struct region
*src1
,
701 const struct region
*src2
)
703 if (!src1
->num_rects
|| !src2
->num_rects
|| !EXTENTCHECK(&src1
->extents
, &src2
->extents
))
706 dst
->extents
.left
= 0;
707 dst
->extents
.top
= 0;
708 dst
->extents
.right
= 0;
709 dst
->extents
.bottom
= 0;
712 if (!region_op( dst
, src1
, src2
, intersect_overlapping
, NULL
, NULL
)) return NULL
;
713 set_region_extents( dst
);
717 /* compute the subtraction of two regions into dst, which can be one of the source regions */
718 struct region
*subtract_region( struct region
*dst
, const struct region
*src1
,
719 const struct region
*src2
)
721 if (!src1
->num_rects
|| !src2
->num_rects
|| !EXTENTCHECK(&src1
->extents
, &src2
->extents
))
722 return copy_region( dst
, src1
);
724 if (!region_op( dst
, src1
, src2
, subtract_overlapping
,
725 subtract_non_overlapping
, NULL
)) return NULL
;
726 set_region_extents( dst
);
730 /* compute the union of two regions into dst, which can be one of the source regions */
731 struct region
*union_region( struct region
*dst
, const struct region
*src1
,
732 const struct region
*src2
)
734 if (src1
== src2
) return copy_region( dst
, src1
);
735 if (!src1
->num_rects
) return copy_region( dst
, src2
);
736 if (!src2
->num_rects
) return copy_region( dst
, src1
);
738 if ((src1
->num_rects
== 1) &&
739 (src1
->extents
.left
<= src2
->extents
.left
) &&
740 (src1
->extents
.top
<= src2
->extents
.top
) &&
741 (src1
->extents
.right
>= src2
->extents
.right
) &&
742 (src1
->extents
.bottom
>= src2
->extents
.bottom
))
743 return copy_region( dst
, src1
);
745 if ((src2
->num_rects
== 1) &&
746 (src2
->extents
.left
<= src1
->extents
.left
) &&
747 (src2
->extents
.top
<= src1
->extents
.top
) &&
748 (src2
->extents
.right
>= src1
->extents
.right
) &&
749 (src2
->extents
.bottom
>= src1
->extents
.bottom
))
750 return copy_region( dst
, src2
);
752 if (!region_op( dst
, src1
, src2
, union_overlapping
,
753 union_non_overlapping
, union_non_overlapping
)) return NULL
;
755 dst
->extents
.left
= min(src1
->extents
.left
, src2
->extents
.left
);
756 dst
->extents
.top
= min(src1
->extents
.top
, src2
->extents
.top
);
757 dst
->extents
.right
= max(src1
->extents
.right
, src2
->extents
.right
);
758 dst
->extents
.bottom
= max(src1
->extents
.bottom
, src2
->extents
.bottom
);
762 /* check if the given point is inside the region */
763 int point_in_region( struct region
*region
, int x
, int y
)
765 const rectangle_t
*ptr
, *end
;
767 for (ptr
= region
->rects
, end
= region
->rects
+ region
->num_rects
; ptr
< end
; ptr
++)
769 if (ptr
->top
> y
) return 0;
770 if (ptr
->bottom
<= y
) continue;
771 /* now we are in the correct band */
772 if (ptr
->left
> x
) return 0;
773 if (ptr
->right
<= x
) continue;