|  | /* | 
|  | * Server-side region objects. Based on the X11 implementation. | 
|  | * | 
|  | * Copyright 1993, 1994, 1995, 2004 Alexandre Julliard | 
|  | * Modifications and additions: Copyright 1998 Huw Davies | 
|  | *					  1999 Alex Korobka | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | * | 
|  | * Note: | 
|  | *  This is a simplified version of the code, without all the explanations. | 
|  | *  Check the equivalent GDI code to make sense of it. | 
|  | */ | 
|  |  | 
|  | /************************************************************************ | 
|  |  | 
|  | Copyright (c) 1987, 1988  X Consortium | 
|  |  | 
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | 
|  | of this software and associated documentation files (the "Software"), to deal | 
|  | in the Software without restriction, including without limitation the rights | 
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|  | copies of the Software, and to permit persons to whom the Software is | 
|  | furnished to do so, subject to the following conditions: | 
|  |  | 
|  | The above copyright notice and this permission notice shall be included in | 
|  | all copies or substantial portions of the Software. | 
|  |  | 
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | 
|  | X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | 
|  | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
|  | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
|  |  | 
|  | Except as contained in this notice, the name of the X Consortium shall not be | 
|  | used in advertising or otherwise to promote the sale, use or other dealings | 
|  | in this Software without prior written authorization from the X Consortium. | 
|  |  | 
|  |  | 
|  | Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. | 
|  |  | 
|  | All Rights Reserved | 
|  |  | 
|  | Permission to use, copy, modify, and distribute this software and its | 
|  | documentation for any purpose and without fee is hereby granted, | 
|  | provided that the above copyright notice appear in all copies and that | 
|  | both that copyright notice and this permission notice appear in | 
|  | supporting documentation, and that the name of Digital not be | 
|  | used in advertising or publicity pertaining to distribution of the | 
|  | software without specific, written prior permission. | 
|  |  | 
|  | DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING | 
|  | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL | 
|  | DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR | 
|  | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | 
|  | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | 
|  | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | 
|  | SOFTWARE. | 
|  |  | 
|  | ************************************************************************/ | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include "request.h" | 
|  | #include "user.h" | 
|  |  | 
|  | struct region | 
|  | { | 
|  | int size; | 
|  | int num_rects; | 
|  | rectangle_t *rects; | 
|  | rectangle_t extents; | 
|  | }; | 
|  |  | 
|  |  | 
|  | #define RGN_DEFAULT_RECTS 2 | 
|  |  | 
|  | #define EXTENTCHECK(r1, r2) \ | 
|  | ((r1)->right > (r2)->left && \ | 
|  | (r1)->left < (r2)->right && \ | 
|  | (r1)->bottom > (r2)->top && \ | 
|  | (r1)->top < (r2)->bottom) | 
|  |  | 
|  | typedef int (*overlap_func_t)( struct region *reg, const rectangle_t *r1, const rectangle_t *r1End, | 
|  | const rectangle_t *r2, const rectangle_t *r2End, int top, int bottom ); | 
|  | typedef int (*non_overlap_func_t)( struct region *reg, const rectangle_t *r, | 
|  | const rectangle_t *rEnd, int top, int bottom ); | 
|  |  | 
|  | static const rectangle_t empty_rect;  /* all-zero rectangle for empty regions */ | 
|  |  | 
|  | /* add a rectangle to a region */ | 
|  | static inline rectangle_t *add_rect( struct region *reg ) | 
|  | { | 
|  | if (reg->num_rects >= reg->size - 1) | 
|  | { | 
|  | rectangle_t *new_rect = realloc( reg->rects, 2 * sizeof(rectangle_t) * reg->size ); | 
|  | if (!new_rect) | 
|  | { | 
|  | set_error( STATUS_NO_MEMORY ); | 
|  | return 0; | 
|  | } | 
|  | reg->rects = new_rect; | 
|  | reg->size *= 2; | 
|  | } | 
|  | return reg->rects + reg->num_rects++; | 
|  | } | 
|  |  | 
|  | /* make sure all the rectangles are valid and that the region is properly y-x-banded */ | 
|  | static inline int validate_rectangles( const rectangle_t *rects, unsigned int nb_rects ) | 
|  | { | 
|  | const rectangle_t *ptr, *end; | 
|  |  | 
|  | for (ptr = rects, end = rects + nb_rects; ptr < end; ptr++) | 
|  | { | 
|  | if (ptr->left >= ptr->right || ptr->top >= ptr->bottom) return 0;  /* empty rectangle */ | 
|  | if (ptr == end - 1) break; | 
|  | if (ptr[0].top == ptr[1].top)  /* same band */ | 
|  | { | 
|  | if (ptr[0].bottom != ptr[1].bottom) return 0;  /* not same y extent */ | 
|  | if (ptr[0].right >= ptr[1].left) return 0;  /* not properly x ordered */ | 
|  | } | 
|  | else  /* new band */ | 
|  | { | 
|  | if (ptr[0].bottom > ptr[1].top) return 0;  /* not properly y ordered */ | 
|  | } | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* attempt to merge the rects in the current band with those in the */ | 
|  | /* previous one. Used only by region_op. */ | 
|  | static int coalesce_region( struct region *pReg, int prevStart, int curStart ) | 
|  | { | 
|  | int curNumRects; | 
|  | rectangle_t *pRegEnd = &pReg->rects[pReg->num_rects]; | 
|  | rectangle_t *pPrevRect = &pReg->rects[prevStart]; | 
|  | rectangle_t *pCurRect = &pReg->rects[curStart]; | 
|  | int prevNumRects = curStart - prevStart; | 
|  | int bandtop = pCurRect->top; | 
|  |  | 
|  | for (curNumRects = 0; | 
|  | (pCurRect != pRegEnd) && (pCurRect->top == bandtop); | 
|  | curNumRects++) | 
|  | { | 
|  | pCurRect++; | 
|  | } | 
|  |  | 
|  | if (pCurRect != pRegEnd) | 
|  | { | 
|  | pRegEnd--; | 
|  | while (pRegEnd[-1].top == pRegEnd->top) pRegEnd--; | 
|  | curStart = pRegEnd - pReg->rects; | 
|  | pRegEnd = pReg->rects + pReg->num_rects; | 
|  | } | 
|  |  | 
|  | if ((curNumRects == prevNumRects) && (curNumRects != 0)) | 
|  | { | 
|  | pCurRect -= curNumRects; | 
|  | if (pPrevRect->bottom == pCurRect->top) | 
|  | { | 
|  | do | 
|  | { | 
|  | if ((pPrevRect->left != pCurRect->left) || | 
|  | (pPrevRect->right != pCurRect->right)) return curStart; | 
|  | pPrevRect++; | 
|  | pCurRect++; | 
|  | prevNumRects -= 1; | 
|  | } while (prevNumRects != 0); | 
|  |  | 
|  | pReg->num_rects -= curNumRects; | 
|  | pCurRect -= curNumRects; | 
|  | pPrevRect -= curNumRects; | 
|  |  | 
|  | do | 
|  | { | 
|  | pPrevRect->bottom = pCurRect->bottom; | 
|  | pPrevRect++; | 
|  | pCurRect++; | 
|  | curNumRects -= 1; | 
|  | } while (curNumRects != 0); | 
|  |  | 
|  | if (pCurRect == pRegEnd) curStart = prevStart; | 
|  | else do { *pPrevRect++ = *pCurRect++; } while (pCurRect != pRegEnd); | 
|  |  | 
|  | } | 
|  | } | 
|  | return curStart; | 
|  | } | 
|  |  | 
|  | /* apply an operation to two regions */ | 
|  | /* check the GDI version of the code for explanations */ | 
|  | static int region_op( struct region *newReg, const struct region *reg1, const struct region *reg2, | 
|  | overlap_func_t overlap_func, | 
|  | non_overlap_func_t non_overlap1_func, | 
|  | non_overlap_func_t non_overlap2_func ) | 
|  | { | 
|  | int ybot, ytop, top, bot, prevBand, curBand; | 
|  | const rectangle_t *r1BandEnd, *r2BandEnd; | 
|  |  | 
|  | const rectangle_t *r1 = reg1->rects; | 
|  | const rectangle_t *r2 = reg2->rects; | 
|  | const rectangle_t *r1End = r1 + reg1->num_rects; | 
|  | const rectangle_t *r2End = r2 + reg2->num_rects; | 
|  |  | 
|  | rectangle_t *new_rects, *old_rects = newReg->rects; | 
|  | int new_size, ret = 0; | 
|  |  | 
|  | new_size = max( reg1->num_rects, reg2->num_rects ) * 2; | 
|  | if (!(new_rects = mem_alloc( new_size * sizeof(*newReg->rects) ))) return 0; | 
|  |  | 
|  | newReg->size = new_size; | 
|  | newReg->rects = new_rects; | 
|  | newReg->num_rects = 0; | 
|  |  | 
|  | if (reg1->extents.top < reg2->extents.top) | 
|  | ybot = reg1->extents.top; | 
|  | else | 
|  | ybot = reg2->extents.top; | 
|  |  | 
|  | prevBand = 0; | 
|  |  | 
|  | do | 
|  | { | 
|  | curBand = newReg->num_rects; | 
|  |  | 
|  | r1BandEnd = r1; | 
|  | while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top)) r1BandEnd++; | 
|  |  | 
|  | r2BandEnd = r2; | 
|  | while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top)) r2BandEnd++; | 
|  |  | 
|  | if (r1->top < r2->top) | 
|  | { | 
|  | top = max(r1->top,ybot); | 
|  | bot = min(r1->bottom,r2->top); | 
|  |  | 
|  | if ((top != bot) && non_overlap1_func) | 
|  | { | 
|  | if (!non_overlap1_func( newReg, r1, r1BandEnd, top, bot )) goto done; | 
|  | } | 
|  |  | 
|  | ytop = r2->top; | 
|  | } | 
|  | else if (r2->top < r1->top) | 
|  | { | 
|  | top = max(r2->top,ybot); | 
|  | bot = min(r2->bottom,r1->top); | 
|  |  | 
|  | if ((top != bot) && non_overlap2_func) | 
|  | { | 
|  | if (!non_overlap2_func( newReg, r2, r2BandEnd, top, bot )) goto done; | 
|  | } | 
|  |  | 
|  | ytop = r1->top; | 
|  | } | 
|  | else | 
|  | { | 
|  | ytop = r1->top; | 
|  | } | 
|  |  | 
|  | if (newReg->num_rects != curBand) | 
|  | prevBand = coalesce_region(newReg, prevBand, curBand); | 
|  |  | 
|  | ybot = min(r1->bottom, r2->bottom); | 
|  | curBand = newReg->num_rects; | 
|  | if (ybot > ytop) | 
|  | { | 
|  | if (!overlap_func( newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot )) goto done; | 
|  | } | 
|  |  | 
|  | if (newReg->num_rects != curBand) | 
|  | prevBand = coalesce_region(newReg, prevBand, curBand); | 
|  |  | 
|  | if (r1->bottom == ybot) r1 = r1BandEnd; | 
|  | if (r2->bottom == ybot) r2 = r2BandEnd; | 
|  | } while ((r1 != r1End) && (r2 != r2End)); | 
|  |  | 
|  | curBand = newReg->num_rects; | 
|  | if (r1 != r1End) | 
|  | { | 
|  | if (non_overlap1_func) | 
|  | { | 
|  | do | 
|  | { | 
|  | r1BandEnd = r1; | 
|  | while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top)) r1BandEnd++; | 
|  | if (!non_overlap1_func( newReg, r1, r1BandEnd, max(r1->top,ybot), r1->bottom )) | 
|  | goto done; | 
|  | r1 = r1BandEnd; | 
|  | } while (r1 != r1End); | 
|  | } | 
|  | } | 
|  | else if ((r2 != r2End) && non_overlap2_func) | 
|  | { | 
|  | do | 
|  | { | 
|  | r2BandEnd = r2; | 
|  | while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top)) r2BandEnd++; | 
|  | if (!non_overlap2_func( newReg, r2, r2BandEnd, max(r2->top,ybot), r2->bottom )) | 
|  | goto done; | 
|  | r2 = r2BandEnd; | 
|  | } while (r2 != r2End); | 
|  | } | 
|  |  | 
|  | if (newReg->num_rects != curBand) coalesce_region(newReg, prevBand, curBand); | 
|  |  | 
|  | if ((newReg->num_rects < (newReg->size / 2)) && (newReg->size > 2)) | 
|  | { | 
|  | new_size = max( newReg->num_rects, RGN_DEFAULT_RECTS ); | 
|  | if ((new_rects = realloc( newReg->rects, sizeof(*newReg->rects) * new_size ))) | 
|  | { | 
|  | newReg->rects = new_rects; | 
|  | newReg->size = new_size; | 
|  | } | 
|  | } | 
|  | ret = 1; | 
|  | done: | 
|  | free( old_rects ); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* recalculate the extents of a region */ | 
|  | static void set_region_extents( struct region *region ) | 
|  | { | 
|  | rectangle_t *pRect, *pRectEnd; | 
|  |  | 
|  | if (region->num_rects == 0) | 
|  | { | 
|  | region->extents.left = 0; | 
|  | region->extents.top = 0; | 
|  | region->extents.right = 0; | 
|  | region->extents.bottom = 0; | 
|  | return; | 
|  | } | 
|  |  | 
|  | pRect = region->rects; | 
|  | pRectEnd = &pRect[region->num_rects - 1]; | 
|  |  | 
|  | region->extents.left = pRect->left; | 
|  | region->extents.top = pRect->top; | 
|  | region->extents.right = pRectEnd->right; | 
|  | region->extents.bottom = pRectEnd->bottom; | 
|  |  | 
|  | while (pRect <= pRectEnd) | 
|  | { | 
|  | if (pRect->left < region->extents.left) region->extents.left = pRect->left; | 
|  | if (pRect->right > region->extents.right) region->extents.right = pRect->right; | 
|  | pRect++; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* handle an overlapping band for intersect_region */ | 
|  | static int intersect_overlapping( struct region *pReg, | 
|  | const rectangle_t *r1, const rectangle_t *r1End, | 
|  | const rectangle_t *r2, const rectangle_t *r2End, | 
|  | int top, int bottom ) | 
|  |  | 
|  | { | 
|  | int left, right; | 
|  |  | 
|  | while ((r1 != r1End) && (r2 != r2End)) | 
|  | { | 
|  | left = max(r1->left, r2->left); | 
|  | right = min(r1->right, r2->right); | 
|  |  | 
|  | if (left < right) | 
|  | { | 
|  | rectangle_t *rect = add_rect( pReg ); | 
|  | if (!rect) return 0; | 
|  | rect->left = left; | 
|  | rect->top = top; | 
|  | rect->right = right; | 
|  | rect->bottom = bottom; | 
|  | } | 
|  |  | 
|  | if (r1->right < r2->right) r1++; | 
|  | else if (r2->right < r1->right) r2++; | 
|  | else | 
|  | { | 
|  | r1++; | 
|  | r2++; | 
|  | } | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* handle a non-overlapping band for subtract_region */ | 
|  | static int subtract_non_overlapping( struct region *pReg, const rectangle_t *r, | 
|  | const rectangle_t *rEnd, int top, int bottom ) | 
|  | { | 
|  | while (r != rEnd) | 
|  | { | 
|  | rectangle_t *rect = add_rect( pReg ); | 
|  | if (!rect) return 0; | 
|  | rect->left = r->left; | 
|  | rect->top = top; | 
|  | rect->right = r->right; | 
|  | rect->bottom = bottom; | 
|  | r++; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* handle an overlapping band for subtract_region */ | 
|  | static int subtract_overlapping( struct region *pReg, | 
|  | const rectangle_t *r1, const rectangle_t *r1End, | 
|  | const rectangle_t *r2, const rectangle_t *r2End, | 
|  | int top, int bottom ) | 
|  | { | 
|  | int left = r1->left; | 
|  |  | 
|  | while ((r1 != r1End) && (r2 != r2End)) | 
|  | { | 
|  | if (r2->right <= left) r2++; | 
|  | else if (r2->left <= left) | 
|  | { | 
|  | left = r2->right; | 
|  | if (left >= r1->right) | 
|  | { | 
|  | r1++; | 
|  | if (r1 != r1End) | 
|  | left = r1->left; | 
|  | } | 
|  | else r2++; | 
|  | } | 
|  | else if (r2->left < r1->right) | 
|  | { | 
|  | rectangle_t *rect = add_rect( pReg ); | 
|  | if (!rect) return 0; | 
|  | rect->left = left; | 
|  | rect->top = top; | 
|  | rect->right = r2->left; | 
|  | rect->bottom = bottom; | 
|  | left = r2->right; | 
|  | if (left >= r1->right) | 
|  | { | 
|  | r1++; | 
|  | if (r1 != r1End) | 
|  | left = r1->left; | 
|  | } | 
|  | else r2++; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (r1->right > left) | 
|  | { | 
|  | rectangle_t *rect = add_rect( pReg ); | 
|  | if (!rect) return 0; | 
|  | rect->left = left; | 
|  | rect->top = top; | 
|  | rect->right = r1->right; | 
|  | rect->bottom = bottom; | 
|  | } | 
|  | r1++; | 
|  | left = r1->left; | 
|  | } | 
|  | } | 
|  |  | 
|  | while (r1 != r1End) | 
|  | { | 
|  | rectangle_t *rect = add_rect( pReg ); | 
|  | if (!rect) return 0; | 
|  | rect->left = left; | 
|  | rect->top = top; | 
|  | rect->right = r1->right; | 
|  | rect->bottom = bottom; | 
|  | r1++; | 
|  | if (r1 != r1End) left = r1->left; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* handle a non-overlapping band for union_region */ | 
|  | static int union_non_overlapping( struct region *pReg, const rectangle_t *r, | 
|  | const rectangle_t *rEnd, int top, int bottom ) | 
|  | { | 
|  | while (r != rEnd) | 
|  | { | 
|  | rectangle_t *rect = add_rect( pReg ); | 
|  | if (!rect) return 0; | 
|  | rect->left = r->left; | 
|  | rect->top = top; | 
|  | rect->right = r->right; | 
|  | rect->bottom = bottom; | 
|  | r++; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* handle an overlapping band for union_region */ | 
|  | static int union_overlapping( struct region *pReg, | 
|  | const rectangle_t *r1, const rectangle_t *r1End, | 
|  | const rectangle_t *r2, const rectangle_t *r2End, | 
|  | int top, int bottom ) | 
|  | { | 
|  | #define MERGERECT(r) \ | 
|  | if ((pReg->num_rects != 0) &&  \ | 
|  | (pReg->rects[pReg->num_rects-1].top == top) &&  \ | 
|  | (pReg->rects[pReg->num_rects-1].bottom == bottom) &&  \ | 
|  | (pReg->rects[pReg->num_rects-1].right >= r->left))  \ | 
|  | {  \ | 
|  | if (pReg->rects[pReg->num_rects-1].right < r->right)  \ | 
|  | {  \ | 
|  | pReg->rects[pReg->num_rects-1].right = r->right;  \ | 
|  | }  \ | 
|  | }  \ | 
|  | else  \ | 
|  | {  \ | 
|  | rectangle_t *rect = add_rect( pReg ); \ | 
|  | if (!rect) return 0; \ | 
|  | rect->top = top;  \ | 
|  | rect->bottom = bottom;  \ | 
|  | rect->left = r->left;  \ | 
|  | rect->right = r->right;  \ | 
|  | }  \ | 
|  | r++; | 
|  |  | 
|  | while ((r1 != r1End) && (r2 != r2End)) | 
|  | { | 
|  | if (r1->left < r2->left) | 
|  | { | 
|  | MERGERECT(r1); | 
|  | } | 
|  | else | 
|  | { | 
|  | MERGERECT(r2); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (r1 != r1End) | 
|  | { | 
|  | do | 
|  | { | 
|  | MERGERECT(r1); | 
|  | } while (r1 != r1End); | 
|  | } | 
|  | else while (r2 != r2End) | 
|  | { | 
|  | MERGERECT(r2); | 
|  | } | 
|  | return 1; | 
|  | #undef MERGERECT | 
|  | } | 
|  |  | 
|  |  | 
|  | /* create a region from an array of rectangles */ | 
|  | struct region *create_region( const rectangle_t *rects, unsigned int nb_rects ) | 
|  | { | 
|  | struct region *region; | 
|  | unsigned int size = max( nb_rects, RGN_DEFAULT_RECTS ); | 
|  |  | 
|  | if (!validate_rectangles( rects, nb_rects )) | 
|  | { | 
|  | set_error( STATUS_INVALID_PARAMETER ); | 
|  | return NULL; | 
|  | } | 
|  | if (!(region = mem_alloc( sizeof(*region) ))) return NULL; | 
|  | if (!(region->rects = mem_alloc( size * sizeof(*region->rects) ))) | 
|  | { | 
|  | free( region ); | 
|  | return NULL; | 
|  | } | 
|  | region->size = size; | 
|  | region->num_rects = nb_rects; | 
|  | memcpy( region->rects, rects, nb_rects * sizeof(*rects) ); | 
|  | set_region_extents( region ); | 
|  | return region; | 
|  | } | 
|  |  | 
|  | /* create a region from request data */ | 
|  | struct region *create_region_from_req_data( const void *data, size_t size ) | 
|  | { | 
|  | const rectangle_t *rects = data; | 
|  | int nb_rects = size / sizeof(rectangle_t); | 
|  |  | 
|  | /* special case: empty region can be specified by a single all-zero rectangle */ | 
|  | if (nb_rects == 1 && !memcmp( rects, &empty_rect, sizeof(empty_rect) )) nb_rects = 0; | 
|  | return create_region( rects, nb_rects ); | 
|  | } | 
|  |  | 
|  | /* free a region */ | 
|  | void free_region( struct region *region ) | 
|  | { | 
|  | free( region->rects ); | 
|  | free( region ); | 
|  | } | 
|  |  | 
|  | /* set region to a simple rectangle */ | 
|  | void set_region_rect( struct region *region, const rectangle_t *rect ) | 
|  | { | 
|  | if (rect->left < rect->right && rect->top < rect->bottom) | 
|  | { | 
|  | region->num_rects = 1; | 
|  | region->rects[0] = region->extents = *rect; | 
|  | } | 
|  | else | 
|  | { | 
|  | region->num_rects = 0; | 
|  | region->extents.left = 0; | 
|  | region->extents.top = 0; | 
|  | region->extents.right = 0; | 
|  | region->extents.bottom = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* retrieve the region data for sending to the client */ | 
|  | rectangle_t *get_region_data( const struct region *region, size_t max_size, size_t *total_size ) | 
|  | { | 
|  | const rectangle_t *data = region->rects; | 
|  |  | 
|  | if (!(*total_size = region->num_rects * sizeof(rectangle_t))) | 
|  | { | 
|  | /* return a single empty rect for empty regions */ | 
|  | *total_size = sizeof(empty_rect); | 
|  | data = &empty_rect; | 
|  | } | 
|  | if (max_size >= *total_size) return memdup( data, *total_size ); | 
|  | set_error( STATUS_BUFFER_OVERFLOW ); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* retrieve the region data for sending to the client and free the region at the same time */ | 
|  | rectangle_t *get_region_data_and_free( struct region *region, size_t max_size, size_t *total_size ) | 
|  | { | 
|  | rectangle_t *ret = region->rects; | 
|  |  | 
|  | if (!(*total_size = region->num_rects * sizeof(rectangle_t))) | 
|  | { | 
|  | /* return a single empty rect for empty regions */ | 
|  | *total_size = sizeof(empty_rect); | 
|  | if (max_size >= sizeof(empty_rect)) | 
|  | { | 
|  | ret = memdup( &empty_rect, sizeof(empty_rect) ); | 
|  | free( region->rects ); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (max_size < *total_size) | 
|  | { | 
|  | free( region->rects ); | 
|  | set_error( STATUS_BUFFER_OVERFLOW ); | 
|  | ret = NULL; | 
|  | } | 
|  | free( region ); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* check if a given region is empty */ | 
|  | int is_region_empty( const struct region *region ) | 
|  | { | 
|  | return region->num_rects == 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* get the extents rect of a region */ | 
|  | void get_region_extents( const struct region *region, rectangle_t *rect ) | 
|  | { | 
|  | *rect = region->extents; | 
|  | } | 
|  |  | 
|  | /* add an offset to a region */ | 
|  | void offset_region( struct region *region, int x, int y ) | 
|  | { | 
|  | rectangle_t *rect, *end; | 
|  |  | 
|  | for (rect = region->rects, end = rect + region->num_rects; rect < end; rect++) | 
|  | { | 
|  | rect->left += x; | 
|  | rect->right += x; | 
|  | rect->top += y; | 
|  | rect->bottom += y; | 
|  | } | 
|  | region->extents.left += x; | 
|  | region->extents.right += x; | 
|  | region->extents.top += y; | 
|  | region->extents.bottom += y; | 
|  | } | 
|  |  | 
|  | /* make a copy of a region; returns dst or NULL on error */ | 
|  | struct region *copy_region( struct region *dst, const struct region *src ) | 
|  | { | 
|  | if (dst == src) return dst; | 
|  |  | 
|  | if (dst->size < src->num_rects) | 
|  | { | 
|  | rectangle_t *rect = realloc( dst->rects, src->num_rects * sizeof(*rect) ); | 
|  | if (!rect) | 
|  | { | 
|  | set_error( STATUS_NO_MEMORY ); | 
|  | return NULL; | 
|  | } | 
|  | dst->rects = rect; | 
|  | dst->size = src->num_rects; | 
|  | } | 
|  | dst->num_rects = src->num_rects; | 
|  | dst->extents = src->extents; | 
|  | memcpy( dst->rects, src->rects, src->num_rects * sizeof(*dst->rects) ); | 
|  | return dst; | 
|  | } | 
|  |  | 
|  | /* compute the intersection of two regions into dst, which can be one of the source regions */ | 
|  | struct region *intersect_region( struct region *dst, const struct region *src1, | 
|  | const struct region *src2 ) | 
|  | { | 
|  | if (!src1->num_rects || !src2->num_rects || !EXTENTCHECK(&src1->extents, &src2->extents)) | 
|  | { | 
|  | dst->num_rects = 0; | 
|  | dst->extents.left = 0; | 
|  | dst->extents.top = 0; | 
|  | dst->extents.right = 0; | 
|  | dst->extents.bottom = 0; | 
|  | return dst; | 
|  | } | 
|  | if (!region_op( dst, src1, src2, intersect_overlapping, NULL, NULL )) return NULL; | 
|  | set_region_extents( dst ); | 
|  | return dst; | 
|  | } | 
|  |  | 
|  | /* compute the subtraction of two regions into dst, which can be one of the source regions */ | 
|  | struct region *subtract_region( struct region *dst, const struct region *src1, | 
|  | const struct region *src2 ) | 
|  | { | 
|  | if (!src1->num_rects || !src2->num_rects || !EXTENTCHECK(&src1->extents, &src2->extents)) | 
|  | return copy_region( dst, src1 ); | 
|  |  | 
|  | if (!region_op( dst, src1, src2, subtract_overlapping, | 
|  | subtract_non_overlapping, NULL )) return NULL; | 
|  | set_region_extents( dst ); | 
|  | return dst; | 
|  | } | 
|  |  | 
|  | /* compute the union of two regions into dst, which can be one of the source regions */ | 
|  | struct region *union_region( struct region *dst, const struct region *src1, | 
|  | const struct region *src2 ) | 
|  | { | 
|  | if (src1 == src2) return copy_region( dst, src1 ); | 
|  | if (!src1->num_rects) return copy_region( dst, src2 ); | 
|  | if (!src2->num_rects) return copy_region( dst, src1 ); | 
|  |  | 
|  | if ((src1->num_rects == 1) && | 
|  | (src1->extents.left <= src2->extents.left) && | 
|  | (src1->extents.top <= src2->extents.top) && | 
|  | (src1->extents.right >= src2->extents.right) && | 
|  | (src1->extents.bottom >= src2->extents.bottom)) | 
|  | return copy_region( dst, src1 ); | 
|  |  | 
|  | if ((src2->num_rects == 1) && | 
|  | (src2->extents.left <= src1->extents.left) && | 
|  | (src2->extents.top <= src1->extents.top) && | 
|  | (src2->extents.right >= src1->extents.right) && | 
|  | (src2->extents.bottom >= src1->extents.bottom)) | 
|  | return copy_region( dst, src2 ); | 
|  |  | 
|  | if (!region_op( dst, src1, src2, union_overlapping, | 
|  | union_non_overlapping, union_non_overlapping )) return NULL; | 
|  |  | 
|  | dst->extents.left = min(src1->extents.left, src2->extents.left); | 
|  | dst->extents.top = min(src1->extents.top, src2->extents.top); | 
|  | dst->extents.right = max(src1->extents.right, src2->extents.right); | 
|  | dst->extents.bottom = max(src1->extents.bottom, src2->extents.bottom); | 
|  | return dst; | 
|  | } | 
|  |  | 
|  | /* compute the exclusive or of two regions into dst, which can be one of the source regions */ | 
|  | struct region *xor_region( struct region *dst, const struct region *src1, | 
|  | const struct region *src2 ) | 
|  | { | 
|  | struct region *tmp = create_empty_region(); | 
|  |  | 
|  | if (!tmp) return NULL; | 
|  |  | 
|  | if (!subtract_region( tmp, src1, src2 ) || | 
|  | !subtract_region( dst, src2, src1 ) || | 
|  | !union_region( dst, dst, tmp )) | 
|  | dst = NULL; | 
|  |  | 
|  | free_region( tmp ); | 
|  | return dst; | 
|  | } | 
|  |  | 
|  | /* check if the given point is inside the region */ | 
|  | int point_in_region( struct region *region, int x, int y ) | 
|  | { | 
|  | const rectangle_t *ptr, *end; | 
|  |  | 
|  | for (ptr = region->rects, end = region->rects + region->num_rects; ptr < end; ptr++) | 
|  | { | 
|  | if (ptr->top > y) return 0; | 
|  | if (ptr->bottom <= y) continue; | 
|  | /* now we are in the correct band */ | 
|  | if (ptr->left > x) return 0; | 
|  | if (ptr->right <= x) continue; | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* check if the given rectangle is (at least partially) inside the region */ | 
|  | int rect_in_region( struct region *region, const rectangle_t *rect ) | 
|  | { | 
|  | const rectangle_t *ptr, *end; | 
|  |  | 
|  | for (ptr = region->rects, end = region->rects + region->num_rects; ptr < end; ptr++) | 
|  | { | 
|  | if (ptr->top >= rect->bottom) return 0; | 
|  | if (ptr->bottom <= rect->top) continue; | 
|  | if (ptr->left >= rect->right) continue; | 
|  | if (ptr->right <= rect->left) continue; | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } |