| /* | |
| * 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 an empty region */ | |
| struct region *create_empty_region(void) | |
| { | |
| struct region *region; | |
| if (!(region = mem_alloc( sizeof(*region) ))) return NULL; | |
| if (!(region->rects = mem_alloc( RGN_DEFAULT_RECTS * sizeof(*region->rects) ))) | |
| { | |
| free( region ); | |
| return NULL; | |
| } | |
| region->size = RGN_DEFAULT_RECTS; | |
| region->num_rects = 0; | |
| region->extents.left = 0; | |
| region->extents.top = 0; | |
| region->extents.right = 0; | |
| region->extents.bottom = 0; | |
| return region; | |
| } | |
| /* create a region from request data */ | |
| struct region *create_region_from_req_data( const void *data, size_t size ) | |
| { | |
| unsigned int alloc_rects; | |
| struct region *region; | |
| 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; | |
| if (!validate_rectangles( rects, nb_rects )) | |
| { | |
| set_error( STATUS_INVALID_PARAMETER ); | |
| return NULL; | |
| } | |
| if (!(region = mem_alloc( sizeof(*region) ))) return NULL; | |
| alloc_rects = max( nb_rects, RGN_DEFAULT_RECTS ); | |
| if (!(region->rects = mem_alloc( alloc_rects * sizeof(*region->rects) ))) | |
| { | |
| free( region ); | |
| return NULL; | |
| } | |
| region->size = alloc_rects; | |
| region->num_rects = nb_rects; | |
| memcpy( region->rects, rects, nb_rects * sizeof(*rects) ); | |
| set_region_extents( region ); | |
| return region; | |
| } | |
| /* 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; | |
| if (!region->num_rects) return; | |
| 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; | |
| } |