Mercurial > ecos
comparison packages/services/gfx/mw/current/src/engine/devrgn.c @ 208:e0c0827131d1 ecos
Merge from eCos master repository on 2002-05-20-20:11:54-BST
| author | jlarmour |
|---|---|
| date | Mon, 20 May 2002 22:19:26 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 207:74c807ddde34 | 208:e0c0827131d1 |
|---|---|
| 1 /* | |
| 2 * Portions Copyright (c) 1999, 2000 Greg Haerr <greg@censoft.com> | |
| 3 * Somewhat less shamelessly ripped from the Wine distribution | |
| 4 * | |
| 5 * Device-independent multi-rectangle clipping routines. | |
| 6 * | |
| 7 * GDI region objects. Shamelessly ripped out from the X11 distribution | |
| 8 * Thanks for the nice licence. | |
| 9 * | |
| 10 * Copyright 1993, 1994, 1995 Alexandre Julliard | |
| 11 * Modifications and additions: Copyright 1998 Huw Davies | |
| 12 */ | |
| 13 /************************************************************************ | |
| 14 | |
| 15 Copyright (c) 1987, 1988 X Consortium | |
| 16 | |
| 17 Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 18 of this software and associated documentation files (the "Software"), to deal | |
| 19 in the Software without restriction, including without limitation the rights | |
| 20 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 21 copies of the Software, and to permit persons to whom the Software is | |
| 22 furnished to do so, subject to the following conditions: | |
| 23 | |
| 24 The above copyright notice and this permission notice shall be included in | |
| 25 all copies or substantial portions of the Software. | |
| 26 | |
| 27 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 28 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 29 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 30 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | |
| 31 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| 32 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 33 | |
| 34 Except as contained in this notice, the name of the X Consortium shall not be | |
| 35 used in advertising or otherwise to promote the sale, use or other dealings | |
| 36 in this Software without prior written authorization from the X Consortium. | |
| 37 | |
| 38 | |
| 39 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. | |
| 40 | |
| 41 All Rights Reserved | |
| 42 | |
| 43 Permission to use, copy, modify, and distribute this software and its | |
| 44 documentation for any purpose and without fee is hereby granted, | |
| 45 provided that the above copyright notice appear in all copies and that | |
| 46 both that copyright notice and this permission notice appear in | |
| 47 supporting documentation, and that the name of Digital not be | |
| 48 used in advertising or publicity pertaining to distribution of the | |
| 49 software without specific, written prior permission. | |
| 50 | |
| 51 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING | |
| 52 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL | |
| 53 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR | |
| 54 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
| 55 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
| 56 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
| 57 SOFTWARE. | |
| 58 | |
| 59 ************************************************************************/ | |
| 60 /* | |
| 61 * The functions in this file implement the Region abstraction, similar to one | |
| 62 * used in the X11 sample server. A Region is simply an area, as the name | |
| 63 * implies, and is implemented as a "y-x-banded" array of rectangles. To | |
| 64 * explain: Each Region is made up of a certain number of rectangles sorted | |
| 65 * by y coordinate first, and then by x coordinate. | |
| 66 * | |
| 67 * Furthermore, the rectangles are banded such that every rectangle with a | |
| 68 * given upper-left y coordinate (y1) will have the same lower-right y | |
| 69 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it | |
| 70 * will span the entire vertical distance of the band. This means that some | |
| 71 * areas that could be merged into a taller rectangle will be represented as | |
| 72 * several shorter rectangles to account for shorter rectangles to its left | |
| 73 * or right but within its "vertical scope". | |
| 74 * | |
| 75 * An added constraint on the rectangles is that they must cover as much | |
| 76 * horizontal area as possible. E.g. no two rectangles in a band are allowed | |
| 77 * to touch. | |
| 78 * | |
| 79 * Whenever possible, bands will be merged together to cover a greater vertical | |
| 80 * distance (and thus reduce the number of rectangles). Two bands can be merged | |
| 81 * only if the bottom of one touches the top of the other and they have | |
| 82 * rectangles in the same places (of the same width, of course). This maintains | |
| 83 * the y-x-banding that's so nice to have... | |
| 84 */ | |
| 85 #include <stdio.h> | |
| 86 #include <stdlib.h> | |
| 87 #include <string.h> | |
| 88 #include "device.h" | |
| 89 | |
| 90 typedef void (*voidProcp)(); | |
| 91 | |
| 92 /* 1 if two RECTs overlap. | |
| 93 * 0 if two RECTs do not overlap. | |
| 94 */ | |
| 95 #define EXTENTCHECK(r1, r2) \ | |
| 96 ((r1)->right > (r2)->left && \ | |
| 97 (r1)->left < (r2)->right && \ | |
| 98 (r1)->bottom > (r2)->top && \ | |
| 99 (r1)->top < (r2)->bottom) | |
| 100 | |
| 101 /* | |
| 102 * Check to see if there is enough memory in the present region. | |
| 103 */ | |
| 104 #define MEMCHECK(reg, rect, firstrect){\ | |
| 105 if ((reg)->numRects >= ((reg)->size - 1)){\ | |
| 106 (firstrect) = realloc(\ | |
| 107 (firstrect), (2 * (sizeof(MWRECT)) * ((reg)->size)));\ | |
| 108 if ((firstrect) == 0)\ | |
| 109 return;\ | |
| 110 (reg)->size *= 2;\ | |
| 111 (rect) = &(firstrect)[(reg)->numRects];\ | |
| 112 }\ | |
| 113 } | |
| 114 | |
| 115 #define REGION_NOT_EMPTY(pReg) pReg->numRects | |
| 116 | |
| 117 #define EMPTY_REGION(pReg) { \ | |
| 118 (pReg)->numRects = 0; \ | |
| 119 (pReg)->extents.left = (pReg)->extents.top = 0; \ | |
| 120 (pReg)->extents.right = (pReg)->extents.bottom = 0; \ | |
| 121 (pReg)->type = MWREGION_NULL; \ | |
| 122 } | |
| 123 | |
| 124 #define INRECT(r, x, y) \ | |
| 125 ( ( ((r).right > x)) && \ | |
| 126 ( ((r).left <= x)) && \ | |
| 127 ( ((r).bottom > y)) && \ | |
| 128 ( ((r).top <= y)) ) | |
| 129 | |
| 130 /* return TRUE if point is in region*/ | |
| 131 MWBOOL | |
| 132 GdPtInRegion(MWCLIPREGION *rgn, MWCOORD x, MWCOORD y) | |
| 133 { | |
| 134 int i; | |
| 135 | |
| 136 if (rgn->numRects > 0 && INRECT(rgn->extents, x, y)) | |
| 137 for (i = 0; i < rgn->numRects; i++) | |
| 138 if (INRECT (rgn->rects[i], x, y)) | |
| 139 return TRUE; | |
| 140 return FALSE; | |
| 141 } | |
| 142 | |
| 143 /* return whether rectangle is all in, partly in, or out of region*/ | |
| 144 int | |
| 145 GdRectInRegion(MWCLIPREGION *rgn, const MWRECT *rect) | |
| 146 { | |
| 147 MWRECT * pCurRect; | |
| 148 MWRECT * pRectEnd; | |
| 149 MWCOORD rx, ry; | |
| 150 MWBOOL partIn, partOut; | |
| 151 | |
| 152 /* this is (just) a useful optimization */ | |
| 153 if (!rgn->numRects || !EXTENTCHECK(&rgn->extents, rect)) | |
| 154 return MWRECT_OUT; | |
| 155 | |
| 156 partOut = FALSE; | |
| 157 partIn = FALSE; | |
| 158 rx = rect->left; | |
| 159 ry = rect->top; | |
| 160 | |
| 161 /* | |
| 162 * can stop when both partOut and partIn are TRUE, | |
| 163 * or we reach rect->bottom | |
| 164 */ | |
| 165 for (pCurRect = rgn->rects, pRectEnd = pCurRect + rgn->numRects; | |
| 166 pCurRect < pRectEnd; pCurRect++) { | |
| 167 | |
| 168 if (pCurRect->bottom <= ry) | |
| 169 continue; /* not far enough down yet*/ | |
| 170 | |
| 171 if (pCurRect->top > ry) { | |
| 172 partOut = TRUE; /* missed part of rectangle above */ | |
| 173 if (partIn || (pCurRect->top >= rect->bottom)) | |
| 174 break; | |
| 175 ry = pCurRect->top; /* x guaranteed to be == rect->left */ | |
| 176 } | |
| 177 | |
| 178 if (pCurRect->right <= rx) | |
| 179 continue; /* not far enough over yet */ | |
| 180 | |
| 181 if (pCurRect->left > rx) { | |
| 182 partOut = TRUE; /* missed part of rectangle to left */ | |
| 183 if (partIn) | |
| 184 break; | |
| 185 } | |
| 186 | |
| 187 if (pCurRect->left < rect->right) { | |
| 188 partIn = TRUE; /* definitely overlap */ | |
| 189 if (partOut) | |
| 190 break; | |
| 191 } | |
| 192 | |
| 193 if (pCurRect->right >= rect->right) { | |
| 194 ry = pCurRect->bottom; /* finished with this band */ | |
| 195 if (ry >= rect->bottom) | |
| 196 break; | |
| 197 rx = rect->left; /* reset x out to left again */ | |
| 198 } else { | |
| 199 /* | |
| 200 * Because boxes in a band are maximal width, if the first box | |
| 201 * to overlap the rectangle doesn't completely cover it in that | |
| 202 * band, the rectangle must be partially out, since some of it | |
| 203 * will be uncovered in that band. partIn will have been set true | |
| 204 * by now... | |
| 205 */ | |
| 206 break; | |
| 207 } | |
| 208 } | |
| 209 | |
| 210 return(partIn ? ((ry < rect->bottom) ? MWRECT_PARTIN : MWRECT_ALLIN) : | |
| 211 MWRECT_OUT); | |
| 212 } | |
| 213 | |
| 214 #if 0000 | |
| 215 /* Returns TRUE if rect is at least partly inside rgn*/ | |
| 216 MWBOOL | |
| 217 GdRectInRegion(MWCLIPREGION *rgn, const MWRECT *rect) | |
| 218 { | |
| 219 MWRECT *pCurRect, *pRectEnd; | |
| 220 MWBOOL ret = FALSE; | |
| 221 | |
| 222 /* this is (just) a useful optimization */ | |
| 223 if ((rgn->numRects > 0) && EXTENTCHECK(&rgn->extents, rect)) | |
| 224 { | |
| 225 for (pCurRect = rgn->rects, pRectEnd = pCurRect + | |
| 226 rgn->numRects; pCurRect < pRectEnd; pCurRect++) | |
| 227 { | |
| 228 if (pCurRect->bottom <= rect->top) | |
| 229 continue; /* not far enough down yet */ | |
| 230 | |
| 231 if (pCurRect->top >= rect->bottom) { | |
| 232 ret = FALSE; /* too far down */ | |
| 233 break; | |
| 234 } | |
| 235 | |
| 236 if (pCurRect->right <= rect->left) | |
| 237 continue; /* not far enough over yet */ | |
| 238 | |
| 239 if (pCurRect->left >= rect->right) { | |
| 240 continue; | |
| 241 } | |
| 242 | |
| 243 ret = TRUE; | |
| 244 break; | |
| 245 } | |
| 246 } | |
| 247 return ret; | |
| 248 } | |
| 249 #endif | |
| 250 | |
| 251 static MWBOOL | |
| 252 EQUALRECT(MWRECT *r1, MWRECT *r2) | |
| 253 { | |
| 254 return ((r1->left == r2->left) && (r1->right == r2->right) && | |
| 255 (r1->top == r2->top) && (r1->bottom == r2->bottom)); | |
| 256 } | |
| 257 | |
| 258 MWBOOL | |
| 259 GdEqualRegion(MWCLIPREGION *r1, MWCLIPREGION *r2) | |
| 260 { | |
| 261 int i; | |
| 262 | |
| 263 if (r1->numRects != r2->numRects) | |
| 264 return FALSE; | |
| 265 if (r1->numRects == 0) | |
| 266 return TRUE; | |
| 267 if (!EQUALRECT(&r1->extents, &r2->extents)) | |
| 268 return FALSE; | |
| 269 for (i = 0; i < r1->numRects; i++) { | |
| 270 if (!EQUALRECT(r1->rects + i, r2->rects + i)) | |
| 271 return FALSE; | |
| 272 } | |
| 273 return TRUE; | |
| 274 } | |
| 275 | |
| 276 MWBOOL | |
| 277 GdEmptyRegion(MWCLIPREGION *rgn) | |
| 278 { | |
| 279 return rgn->numRects == 0; | |
| 280 } | |
| 281 | |
| 282 /* | |
| 283 * Create a new empty MWCLIPREGION. | |
| 284 */ | |
| 285 MWCLIPREGION * | |
| 286 GdAllocRegion(void) | |
| 287 { | |
| 288 MWCLIPREGION *rgn; | |
| 289 | |
| 290 if ((rgn = malloc(sizeof( MWCLIPREGION )))) | |
| 291 { | |
| 292 if ((rgn->rects = malloc(sizeof( MWRECT )))) | |
| 293 { | |
| 294 rgn->size = 1; | |
| 295 EMPTY_REGION(rgn); | |
| 296 return rgn; | |
| 297 } | |
| 298 free(rgn); | |
| 299 } | |
| 300 return NULL; | |
| 301 } | |
| 302 | |
| 303 MWCLIPREGION * | |
| 304 GdAllocRectRegion(MWCOORD left, MWCOORD top, MWCOORD right, MWCOORD bottom) | |
| 305 { | |
| 306 MWCLIPREGION *rgn; | |
| 307 | |
| 308 rgn = GdAllocRegion(); | |
| 309 if (rgn) | |
| 310 GdSetRectRegion(rgn, left, top, right, bottom); | |
| 311 return rgn; | |
| 312 } | |
| 313 | |
| 314 MWCLIPREGION * | |
| 315 GdAllocRectRegionIndirect(MWRECT *prc) | |
| 316 { | |
| 317 return GdAllocRectRegion(prc->left, prc->top, prc->right, prc->bottom); | |
| 318 } | |
| 319 | |
| 320 void | |
| 321 GdSetRectRegion(MWCLIPREGION *rgn, MWCOORD left, MWCOORD top, MWCOORD right, | |
| 322 MWCOORD bottom) | |
| 323 { | |
| 324 if (left != right && top != bottom) { | |
| 325 rgn->rects->left = rgn->extents.left = left; | |
| 326 rgn->rects->top = rgn->extents.top = top; | |
| 327 rgn->rects->right = rgn->extents.right = right; | |
| 328 rgn->rects->bottom = rgn->extents.bottom = bottom; | |
| 329 rgn->numRects = 1; | |
| 330 rgn->type = MWREGION_SIMPLE; | |
| 331 } else | |
| 332 EMPTY_REGION(rgn); | |
| 333 } | |
| 334 | |
| 335 void | |
| 336 GdSetRectRegionIndirect(MWCLIPREGION *rgn, MWRECT *prc) | |
| 337 { | |
| 338 GdSetRectRegion(rgn, prc->left, prc->top, prc->right, prc->bottom); | |
| 339 } | |
| 340 | |
| 341 void | |
| 342 GdDestroyRegion(MWCLIPREGION *rgn) | |
| 343 { | |
| 344 if(rgn) { | |
| 345 free(rgn->rects); | |
| 346 free(rgn); | |
| 347 } | |
| 348 } | |
| 349 | |
| 350 void | |
| 351 GdOffsetRegion(MWCLIPREGION *rgn, MWCOORD x, MWCOORD y) | |
| 352 { | |
| 353 int nbox = rgn->numRects; | |
| 354 MWRECT *pbox = rgn->rects; | |
| 355 | |
| 356 if(nbox && (x || y)) { | |
| 357 while(nbox--) { | |
| 358 pbox->left += x; | |
| 359 pbox->right += x; | |
| 360 pbox->top += y; | |
| 361 pbox->bottom += y; | |
| 362 pbox++; | |
| 363 } | |
| 364 rgn->extents.left += x; | |
| 365 rgn->extents.right += x; | |
| 366 rgn->extents.top += y; | |
| 367 rgn->extents.bottom += y; | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 /* get bounding box for region, return region type*/ | |
| 372 int | |
| 373 GdGetRegionBox(MWCLIPREGION *rgn, MWRECT *prc) | |
| 374 { | |
| 375 *prc = rgn->extents; | |
| 376 return rgn->type; | |
| 377 } | |
| 378 | |
| 379 /*********************************************************************** | |
| 380 * GdUnionRectWithRegion | |
| 381 * Adds a rectangle to a MWCLIPREGION | |
| 382 */ | |
| 383 void | |
| 384 GdUnionRectWithRegion(const MWRECT *rect, MWCLIPREGION *rgn) | |
| 385 { | |
| 386 MWCLIPREGION region; | |
| 387 | |
| 388 region.rects = ®ion.extents; | |
| 389 region.numRects = 1; | |
| 390 region.size = 1; | |
| 391 region.type = MWREGION_SIMPLE; | |
| 392 region.extents = *rect; | |
| 393 GdUnionRegion(rgn, rgn, ®ion); | |
| 394 } | |
| 395 | |
| 396 /*********************************************************************** | |
| 397 * GdSubtractRectWithRegion | |
| 398 * Subtracts a rectangle from a MWCLIPREGION | |
| 399 */ | |
| 400 void | |
| 401 GdSubtractRectFromRegion(const MWRECT *rect, MWCLIPREGION *rgn) | |
| 402 { | |
| 403 MWCLIPREGION region; | |
| 404 | |
| 405 region.rects = ®ion.extents; | |
| 406 region.numRects = 1; | |
| 407 region.size = 1; | |
| 408 region.type = MWREGION_SIMPLE; | |
| 409 region.extents = *rect; | |
| 410 GdSubtractRegion(rgn, rgn, ®ion); | |
| 411 } | |
| 412 | |
| 413 | |
| 414 /*********************************************************************** | |
| 415 * GdCopyRegion | |
| 416 */ | |
| 417 void | |
| 418 GdCopyRegion(MWCLIPREGION *dst, MWCLIPREGION *src) | |
| 419 { | |
| 420 if (dst != src) /* don't want to copy to itself */ | |
| 421 { | |
| 422 if (dst->size < src->numRects) | |
| 423 { | |
| 424 if (! (dst->rects = realloc( dst->rects, src->numRects * sizeof(MWRECT)))) | |
| 425 return; | |
| 426 dst->size = src->numRects; | |
| 427 } | |
| 428 dst->numRects = src->numRects; | |
| 429 dst->extents.left = src->extents.left; | |
| 430 dst->extents.top = src->extents.top; | |
| 431 dst->extents.right = src->extents.right; | |
| 432 dst->extents.bottom = src->extents.bottom; | |
| 433 dst->type = src->type; | |
| 434 | |
| 435 memcpy((char *) dst->rects, (char *) src->rects, | |
| 436 (int) (src->numRects * sizeof(MWRECT))); | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 | |
| 441 /*********************************************************************** | |
| 442 * REGION_SetExtents | |
| 443 * Re-calculate the extents of a region | |
| 444 */ | |
| 445 static void | |
| 446 REGION_SetExtents (MWCLIPREGION *pReg) | |
| 447 { | |
| 448 MWRECT *pRect, *pRectEnd, *pExtents; | |
| 449 | |
| 450 if (pReg->numRects == 0) | |
| 451 { | |
| 452 pReg->extents.left = 0; | |
| 453 pReg->extents.top = 0; | |
| 454 pReg->extents.right = 0; | |
| 455 pReg->extents.bottom = 0; | |
| 456 return; | |
| 457 } | |
| 458 | |
| 459 pExtents = &pReg->extents; | |
| 460 pRect = pReg->rects; | |
| 461 pRectEnd = &pRect[pReg->numRects - 1]; | |
| 462 | |
| 463 /* | |
| 464 * Since pRect is the first rectangle in the region, it must have the | |
| 465 * smallest top and since pRectEnd is the last rectangle in the region, | |
| 466 * it must have the largest bottom, because of banding. Initialize left and | |
| 467 * right from pRect and pRectEnd, resp., as good things to initialize them | |
| 468 * to... | |
| 469 */ | |
| 470 pExtents->left = pRect->left; | |
| 471 pExtents->top = pRect->top; | |
| 472 pExtents->right = pRectEnd->right; | |
| 473 pExtents->bottom = pRectEnd->bottom; | |
| 474 | |
| 475 while (pRect <= pRectEnd) | |
| 476 { | |
| 477 if (pRect->left < pExtents->left) | |
| 478 pExtents->left = pRect->left; | |
| 479 if (pRect->right > pExtents->right) | |
| 480 pExtents->right = pRect->right; | |
| 481 pRect++; | |
| 482 } | |
| 483 } | |
| 484 | |
| 485 | |
| 486 /*********************************************************************** | |
| 487 * REGION_Coalesce | |
| 488 * | |
| 489 * Attempt to merge the rects in the current band with those in the | |
| 490 * previous one. Used only by REGION_RegionOp. | |
| 491 * | |
| 492 * Results: | |
| 493 * The new index for the previous band. | |
| 494 * | |
| 495 * Side Effects: | |
| 496 * If coalescing takes place: | |
| 497 * - rectangles in the previous band will have their bottom fields | |
| 498 * altered. | |
| 499 * - pReg->numRects will be decreased. | |
| 500 * | |
| 501 */ | |
| 502 static MWCOORD | |
| 503 REGION_Coalesce ( | |
| 504 MWCLIPREGION *pReg, /* Region to coalesce */ | |
| 505 MWCOORD prevStart, /* Index of start of previous band */ | |
| 506 MWCOORD curStart /* Index of start of current band */ | |
| 507 ) { | |
| 508 MWRECT *pPrevRect; /* Current rect in previous band */ | |
| 509 MWRECT *pCurRect; /* Current rect in current band */ | |
| 510 MWRECT *pRegEnd; /* End of region */ | |
| 511 MWCOORD curNumRects; /* Number of rectangles in current band */ | |
| 512 MWCOORD prevNumRects; /* Number of rectangles in previous band */ | |
| 513 MWCOORD bandtop; /* top coordinate for current band */ | |
| 514 | |
| 515 pRegEnd = &pReg->rects[pReg->numRects]; | |
| 516 | |
| 517 pPrevRect = &pReg->rects[prevStart]; | |
| 518 prevNumRects = curStart - prevStart; | |
| 519 | |
| 520 /* | |
| 521 * Figure out how many rectangles are in the current band. Have to do | |
| 522 * this because multiple bands could have been added in REGION_RegionOp | |
| 523 * at the end when one region has been exhausted. | |
| 524 */ | |
| 525 pCurRect = &pReg->rects[curStart]; | |
| 526 bandtop = pCurRect->top; | |
| 527 for (curNumRects = 0; | |
| 528 (pCurRect != pRegEnd) && (pCurRect->top == bandtop); | |
| 529 curNumRects++) | |
| 530 { | |
| 531 pCurRect++; | |
| 532 } | |
| 533 | |
| 534 if (pCurRect != pRegEnd) | |
| 535 { | |
| 536 /* | |
| 537 * If more than one band was added, we have to find the start | |
| 538 * of the last band added so the next coalescing job can start | |
| 539 * at the right place... (given when multiple bands are added, | |
| 540 * this may be pointless -- see above). | |
| 541 */ | |
| 542 pRegEnd--; | |
| 543 while (pRegEnd[-1].top == pRegEnd->top) | |
| 544 { | |
| 545 pRegEnd--; | |
| 546 } | |
| 547 curStart = pRegEnd - pReg->rects; | |
| 548 pRegEnd = pReg->rects + pReg->numRects; | |
| 549 } | |
| 550 | |
| 551 if ((curNumRects == prevNumRects) && (curNumRects != 0)) { | |
| 552 pCurRect -= curNumRects; | |
| 553 /* | |
| 554 * The bands may only be coalesced if the bottom of the previous | |
| 555 * matches the top scanline of the current. | |
| 556 */ | |
| 557 if (pPrevRect->bottom == pCurRect->top) | |
| 558 { | |
| 559 /* | |
| 560 * Make sure the bands have rects in the same places. This | |
| 561 * assumes that rects have been added in such a way that they | |
| 562 * cover the most area possible. I.e. two rects in a band must | |
| 563 * have some horizontal space between them. | |
| 564 */ | |
| 565 do | |
| 566 { | |
| 567 if ((pPrevRect->left != pCurRect->left) || | |
| 568 (pPrevRect->right != pCurRect->right)) | |
| 569 { | |
| 570 /* | |
| 571 * The bands don't line up so they can't be coalesced. | |
| 572 */ | |
| 573 return (curStart); | |
| 574 } | |
| 575 pPrevRect++; | |
| 576 pCurRect++; | |
| 577 prevNumRects -= 1; | |
| 578 } while (prevNumRects != 0); | |
| 579 | |
| 580 pReg->numRects -= curNumRects; | |
| 581 pCurRect -= curNumRects; | |
| 582 pPrevRect -= curNumRects; | |
| 583 | |
| 584 /* | |
| 585 * The bands may be merged, so set the bottom of each rect | |
| 586 * in the previous band to that of the corresponding rect in | |
| 587 * the current band. | |
| 588 */ | |
| 589 do | |
| 590 { | |
| 591 pPrevRect->bottom = pCurRect->bottom; | |
| 592 pPrevRect++; | |
| 593 pCurRect++; | |
| 594 curNumRects -= 1; | |
| 595 } while (curNumRects != 0); | |
| 596 | |
| 597 /* | |
| 598 * If only one band was added to the region, we have to backup | |
| 599 * curStart to the start of the previous band. | |
| 600 * | |
| 601 * If more than one band was added to the region, copy the | |
| 602 * other bands down. The assumption here is that the other bands | |
| 603 * came from the same region as the current one and no further | |
| 604 * coalescing can be done on them since it's all been done | |
| 605 * already... curStart is already in the right place. | |
| 606 */ | |
| 607 if (pCurRect == pRegEnd) | |
| 608 { | |
| 609 curStart = prevStart; | |
| 610 } | |
| 611 else | |
| 612 { | |
| 613 do | |
| 614 { | |
| 615 *pPrevRect++ = *pCurRect++; | |
| 616 } while (pCurRect != pRegEnd); | |
| 617 } | |
| 618 | |
| 619 } | |
| 620 } | |
| 621 return (curStart); | |
| 622 } | |
| 623 | |
| 624 /*********************************************************************** | |
| 625 * REGION_RegionOp | |
| 626 * | |
| 627 * Apply an operation to two regions. Called by GdUnion, | |
| 628 * GdXor, GdSubtract, GdIntersect... | |
| 629 * | |
| 630 * Results: | |
| 631 * None. | |
| 632 * | |
| 633 * Side Effects: | |
| 634 * The new region is overwritten. | |
| 635 * | |
| 636 * Notes: | |
| 637 * The idea behind this function is to view the two regions as sets. | |
| 638 * Together they cover a rectangle of area that this function divides | |
| 639 * into horizontal bands where points are covered only by one region | |
| 640 * or by both. For the first case, the nonOverlapFunc is called with | |
| 641 * each the band and the band's upper and lower extents. For the | |
| 642 * second, the overlapFunc is called to process the entire band. It | |
| 643 * is responsible for clipping the rectangles in the band, though | |
| 644 * this function provides the boundaries. | |
| 645 * At the end of each band, the new region is coalesced, if possible, | |
| 646 * to reduce the number of rectangles in the region. | |
| 647 * | |
| 648 */ | |
| 649 static void | |
| 650 REGION_RegionOp( | |
| 651 MWCLIPREGION *newReg, /* Place to store result */ | |
| 652 MWCLIPREGION *reg1, /* First region in operation */ | |
| 653 MWCLIPREGION *reg2, /* 2nd region in operation */ | |
| 654 void (*overlapFunc)(), /* Function to call for over-lapping bands */ | |
| 655 void (*nonOverlap1Func)(), /* Function to call for non-overlapping bands in region 1 */ | |
| 656 void (*nonOverlap2Func)() /* Function to call for non-overlapping bands in region 2 */ | |
| 657 ) { | |
| 658 MWRECT *r1; /* Pointer into first region */ | |
| 659 MWRECT *r2; /* Pointer into 2d region */ | |
| 660 MWRECT *r1End; /* End of 1st region */ | |
| 661 MWRECT *r2End; /* End of 2d region */ | |
| 662 MWCOORD ybot; /* Bottom of intersection */ | |
| 663 MWCOORD ytop; /* Top of intersection */ | |
| 664 MWRECT *oldRects; /* Old rects for newReg */ | |
| 665 MWCOORD prevBand; /* Index of start of | |
| 666 * previous band in newReg */ | |
| 667 MWCOORD curBand; /* Index of start of current | |
| 668 * band in newReg */ | |
| 669 MWRECT *r1BandEnd; /* End of current band in r1 */ | |
| 670 MWRECT *r2BandEnd; /* End of current band in r2 */ | |
| 671 MWCOORD top; /* Top of non-overlapping band */ | |
| 672 MWCOORD bot; /* Bottom of non-overlapping band */ | |
| 673 | |
| 674 /* | |
| 675 * Initialization: | |
| 676 * set r1, r2, r1End and r2End appropriately, preserve the important | |
| 677 * parts of the destination region until the end in case it's one of | |
| 678 * the two source regions, then mark the "new" region empty, allocating | |
| 679 * another array of rectangles for it to use. | |
| 680 */ | |
| 681 r1 = reg1->rects; | |
| 682 r2 = reg2->rects; | |
| 683 r1End = r1 + reg1->numRects; | |
| 684 r2End = r2 + reg2->numRects; | |
| 685 | |
| 686 | |
| 687 /* | |
| 688 * newReg may be one of the src regions so we can't empty it. We keep a | |
| 689 * note of its rects pointer (so that we can free them later), preserve its | |
| 690 * extents and simply set numRects to zero. | |
| 691 */ | |
| 692 | |
| 693 oldRects = newReg->rects; | |
| 694 newReg->numRects = 0; | |
| 695 | |
| 696 /* | |
| 697 * Allocate a reasonable number of rectangles for the new region. The idea | |
| 698 * is to allocate enough so the individual functions don't need to | |
| 699 * reallocate and copy the array, which is time consuming, yet we don't | |
| 700 * have to worry about using too much memory. I hope to be able to | |
| 701 * nuke the Xrealloc() at the end of this function eventually. | |
| 702 */ | |
| 703 newReg->size = MWMAX(reg1->numRects,reg2->numRects) * 2; | |
| 704 | |
| 705 if (! (newReg->rects = malloc( sizeof(MWRECT) * newReg->size ))) | |
| 706 { | |
| 707 newReg->size = 0; | |
| 708 return; | |
| 709 } | |
| 710 | |
| 711 /* | |
| 712 * Initialize ybot and ytop. | |
| 713 * In the upcoming loop, ybot and ytop serve different functions depending | |
| 714 * on whether the band being handled is an overlapping or non-overlapping | |
| 715 * band. | |
| 716 * In the case of a non-overlapping band (only one of the regions | |
| 717 * has points in the band), ybot is the bottom of the most recent | |
| 718 * intersection and thus clips the top of the rectangles in that band. | |
| 719 * ytop is the top of the next intersection between the two regions and | |
| 720 * serves to clip the bottom of the rectangles in the current band. | |
| 721 * For an overlapping band (where the two regions intersect), ytop clips | |
| 722 * the top of the rectangles of both regions and ybot clips the bottoms. | |
| 723 */ | |
| 724 if (reg1->extents.top < reg2->extents.top) | |
| 725 ybot = reg1->extents.top; | |
| 726 else | |
| 727 ybot = reg2->extents.top; | |
| 728 | |
| 729 /* | |
| 730 * prevBand serves to mark the start of the previous band so rectangles | |
| 731 * can be coalesced into larger rectangles. qv. miCoalesce, above. | |
| 732 * In the beginning, there is no previous band, so prevBand == curBand | |
| 733 * (curBand is set later on, of course, but the first band will always | |
| 734 * start at index 0). prevBand and curBand must be indices because of | |
| 735 * the possible expansion, and resultant moving, of the new region's | |
| 736 * array of rectangles. | |
| 737 */ | |
| 738 prevBand = 0; | |
| 739 | |
| 740 do | |
| 741 { | |
| 742 curBand = newReg->numRects; | |
| 743 | |
| 744 /* | |
| 745 * This algorithm proceeds one source-band (as opposed to a | |
| 746 * destination band, which is determined by where the two regions | |
| 747 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the | |
| 748 * rectangle after the last one in the current band for their | |
| 749 * respective regions. | |
| 750 */ | |
| 751 r1BandEnd = r1; | |
| 752 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top)) | |
| 753 { | |
| 754 r1BandEnd++; | |
| 755 } | |
| 756 | |
| 757 r2BandEnd = r2; | |
| 758 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top)) | |
| 759 { | |
| 760 r2BandEnd++; | |
| 761 } | |
| 762 | |
| 763 /* | |
| 764 * First handle the band that doesn't intersect, if any. | |
| 765 * | |
| 766 * Note that attention is restricted to one band in the | |
| 767 * non-intersecting region at once, so if a region has n | |
| 768 * bands between the current position and the next place it overlaps | |
| 769 * the other, this entire loop will be passed through n times. | |
| 770 */ | |
| 771 if (r1->top < r2->top) | |
| 772 { | |
| 773 top = MWMAX(r1->top,ybot); | |
| 774 bot = MWMIN(r1->bottom,r2->top); | |
| 775 | |
| 776 if ((top != bot) && (nonOverlap1Func != (void (*)())NULL)) | |
| 777 { | |
| 778 (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot); | |
| 779 } | |
| 780 | |
| 781 ytop = r2->top; | |
| 782 } | |
| 783 else if (r2->top < r1->top) | |
| 784 { | |
| 785 top = MWMAX(r2->top,ybot); | |
| 786 bot = MWMIN(r2->bottom,r1->top); | |
| 787 | |
| 788 if ((top != bot) && (nonOverlap2Func != (void (*)())NULL)) | |
| 789 { | |
| 790 (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot); | |
| 791 } | |
| 792 | |
| 793 ytop = r1->top; | |
| 794 } | |
| 795 else | |
| 796 { | |
| 797 ytop = r1->top; | |
| 798 } | |
| 799 | |
| 800 /* | |
| 801 * If any rectangles got added to the region, try and coalesce them | |
| 802 * with rectangles from the previous band. Note we could just do | |
| 803 * this test in miCoalesce, but some machines incur a not | |
| 804 * inconsiderable cost for function calls, so... | |
| 805 */ | |
| 806 if (newReg->numRects != curBand) | |
| 807 { | |
| 808 prevBand = REGION_Coalesce (newReg, prevBand, curBand); | |
| 809 } | |
| 810 | |
| 811 /* | |
| 812 * Now see if we've hit an intersecting band. The two bands only | |
| 813 * intersect if ybot > ytop | |
| 814 */ | |
| 815 ybot = MWMIN(r1->bottom, r2->bottom); | |
| 816 curBand = newReg->numRects; | |
| 817 if (ybot > ytop) | |
| 818 { | |
| 819 (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot); | |
| 820 | |
| 821 } | |
| 822 | |
| 823 if (newReg->numRects != curBand) | |
| 824 { | |
| 825 prevBand = REGION_Coalesce (newReg, prevBand, curBand); | |
| 826 } | |
| 827 | |
| 828 /* | |
| 829 * If we've finished with a band (bottom == ybot) we skip forward | |
| 830 * in the region to the next band. | |
| 831 */ | |
| 832 if (r1->bottom == ybot) | |
| 833 { | |
| 834 r1 = r1BandEnd; | |
| 835 } | |
| 836 if (r2->bottom == ybot) | |
| 837 { | |
| 838 r2 = r2BandEnd; | |
| 839 } | |
| 840 } while ((r1 != r1End) && (r2 != r2End)); | |
| 841 | |
| 842 /* | |
| 843 * Deal with whichever region still has rectangles left. | |
| 844 */ | |
| 845 curBand = newReg->numRects; | |
| 846 if (r1 != r1End) | |
| 847 { | |
| 848 if (nonOverlap1Func != (void (*)())NULL) | |
| 849 { | |
| 850 do | |
| 851 { | |
| 852 r1BandEnd = r1; | |
| 853 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top)) | |
| 854 { | |
| 855 r1BandEnd++; | |
| 856 } | |
| 857 (* nonOverlap1Func) (newReg, r1, r1BandEnd, | |
| 858 MWMAX(r1->top,ybot), r1->bottom); | |
| 859 r1 = r1BandEnd; | |
| 860 } while (r1 != r1End); | |
| 861 } | |
| 862 } | |
| 863 else if ((r2 != r2End) && (nonOverlap2Func != (void (*)())NULL)) | |
| 864 { | |
| 865 do | |
| 866 { | |
| 867 r2BandEnd = r2; | |
| 868 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top)) | |
| 869 { | |
| 870 r2BandEnd++; | |
| 871 } | |
| 872 (* nonOverlap2Func) (newReg, r2, r2BandEnd, | |
| 873 MWMAX(r2->top,ybot), r2->bottom); | |
| 874 r2 = r2BandEnd; | |
| 875 } while (r2 != r2End); | |
| 876 } | |
| 877 | |
| 878 if (newReg->numRects != curBand) | |
| 879 { | |
| 880 (void) REGION_Coalesce (newReg, prevBand, curBand); | |
| 881 } | |
| 882 | |
| 883 /* | |
| 884 * A bit of cleanup. To keep regions from growing without bound, | |
| 885 * we shrink the array of rectangles to match the new number of | |
| 886 * rectangles in the region. This never goes to 0, however... | |
| 887 * | |
| 888 * Only do this stuff if the number of rectangles allocated is more than | |
| 889 * twice the number of rectangles in the region (a simple optimization...). | |
| 890 */ | |
| 891 if (newReg->numRects < (newReg->size >> 1)) | |
| 892 { | |
| 893 if (REGION_NOT_EMPTY(newReg)) | |
| 894 { | |
| 895 MWRECT *prev_rects = newReg->rects; | |
| 896 newReg->size = newReg->numRects; | |
| 897 newReg->rects = realloc( newReg->rects, sizeof(MWRECT) * newReg->size ); | |
| 898 if (! newReg->rects) | |
| 899 newReg->rects = prev_rects; | |
| 900 } | |
| 901 else | |
| 902 { | |
| 903 /* | |
| 904 * No point in doing the extra work involved in an Xrealloc if | |
| 905 * the region is empty | |
| 906 */ | |
| 907 newReg->size = 1; | |
| 908 free( newReg->rects ); | |
| 909 newReg->rects = malloc( sizeof(MWRECT) ); | |
| 910 } | |
| 911 } | |
| 912 free( oldRects ); | |
| 913 } | |
| 914 | |
| 915 /*********************************************************************** | |
| 916 * Region Intersection | |
| 917 ***********************************************************************/ | |
| 918 | |
| 919 | |
| 920 /*********************************************************************** | |
| 921 * REGION_IntersectO | |
| 922 * | |
| 923 * Handle an overlapping band for REGION_Intersect. | |
| 924 * | |
| 925 * Results: | |
| 926 * None. | |
| 927 * | |
| 928 * Side Effects: | |
| 929 * Rectangles may be added to the region. | |
| 930 * | |
| 931 */ | |
| 932 static void | |
| 933 REGION_IntersectO(MWCLIPREGION *pReg, MWRECT *r1, MWRECT *r1End, | |
| 934 MWRECT *r2, MWRECT *r2End, MWCOORD top, MWCOORD bottom) | |
| 935 | |
| 936 { | |
| 937 MWCOORD left, right; | |
| 938 MWRECT *pNextRect; | |
| 939 | |
| 940 pNextRect = &pReg->rects[pReg->numRects]; | |
| 941 | |
| 942 while ((r1 != r1End) && (r2 != r2End)) | |
| 943 { | |
| 944 left = MWMAX(r1->left, r2->left); | |
| 945 right = MWMIN(r1->right, r2->right); | |
| 946 | |
| 947 /* | |
| 948 * If there's any overlap between the two rectangles, add that | |
| 949 * overlap to the new region. | |
| 950 * There's no need to check for subsumption because the only way | |
| 951 * such a need could arise is if some region has two rectangles | |
| 952 * right next to each other. Since that should never happen... | |
| 953 */ | |
| 954 if (left < right) | |
| 955 { | |
| 956 MEMCHECK(pReg, pNextRect, pReg->rects); | |
| 957 pNextRect->left = left; | |
| 958 pNextRect->top = top; | |
| 959 pNextRect->right = right; | |
| 960 pNextRect->bottom = bottom; | |
| 961 pReg->numRects += 1; | |
| 962 pNextRect++; | |
| 963 } | |
| 964 | |
| 965 /* | |
| 966 * Need to advance the pointers. Shift the one that extends | |
| 967 * to the right the least, since the other still has a chance to | |
| 968 * overlap with that region's next rectangle, if you see what I mean. | |
| 969 */ | |
| 970 if (r1->right < r2->right) | |
| 971 { | |
| 972 r1++; | |
| 973 } | |
| 974 else if (r2->right < r1->right) | |
| 975 { | |
| 976 r2++; | |
| 977 } | |
| 978 else | |
| 979 { | |
| 980 r1++; | |
| 981 r2++; | |
| 982 } | |
| 983 } | |
| 984 } | |
| 985 | |
| 986 /*********************************************************************** | |
| 987 * GdIntersectRegion | |
| 988 */ | |
| 989 void | |
| 990 GdIntersectRegion(MWCLIPREGION *newReg, MWCLIPREGION *reg1, MWCLIPREGION *reg2) | |
| 991 { | |
| 992 /* check for trivial reject */ | |
| 993 if ( (!(reg1->numRects)) || (!(reg2->numRects)) || | |
| 994 (!EXTENTCHECK(®1->extents, ®2->extents))) | |
| 995 newReg->numRects = 0; | |
| 996 else | |
| 997 REGION_RegionOp (newReg, reg1, reg2, | |
| 998 (voidProcp) REGION_IntersectO, (voidProcp) NULL, (voidProcp) NULL); | |
| 999 | |
| 1000 /* | |
| 1001 * Can't alter newReg's extents before we call miRegionOp because | |
| 1002 * it might be one of the source regions and miRegionOp depends | |
| 1003 * on the extents of those regions being the same. Besides, this | |
| 1004 * way there's no checking against rectangles that will be nuked | |
| 1005 * due to coalescing, so we have to examine fewer rectangles. | |
| 1006 */ | |
| 1007 REGION_SetExtents(newReg); | |
| 1008 newReg->type = (newReg->numRects) ? MWREGION_COMPLEX : MWREGION_NULL ; | |
| 1009 } | |
| 1010 | |
| 1011 /*********************************************************************** | |
| 1012 * Region Union | |
| 1013 ***********************************************************************/ | |
| 1014 | |
| 1015 /*********************************************************************** | |
| 1016 * REGION_UnionNonO | |
| 1017 * | |
| 1018 * Handle a non-overlapping band for the union operation. Just | |
| 1019 * Adds the rectangles into the region. Doesn't have to check for | |
| 1020 * subsumption or anything. | |
| 1021 * | |
| 1022 * Results: | |
| 1023 * None. | |
| 1024 * | |
| 1025 * Side Effects: | |
| 1026 * pReg->numRects is incremented and the final rectangles overwritten | |
| 1027 * with the rectangles we're passed. | |
| 1028 * | |
| 1029 */ | |
| 1030 static void | |
| 1031 REGION_UnionNonO(MWCLIPREGION *pReg,MWRECT *r,MWRECT *rEnd,MWCOORD top, | |
| 1032 MWCOORD bottom) | |
| 1033 { | |
| 1034 MWRECT *pNextRect; | |
| 1035 | |
| 1036 pNextRect = &pReg->rects[pReg->numRects]; | |
| 1037 | |
| 1038 while (r != rEnd) | |
| 1039 { | |
| 1040 MEMCHECK(pReg, pNextRect, pReg->rects); | |
| 1041 pNextRect->left = r->left; | |
| 1042 pNextRect->top = top; | |
| 1043 pNextRect->right = r->right; | |
| 1044 pNextRect->bottom = bottom; | |
| 1045 pReg->numRects += 1; | |
| 1046 pNextRect++; | |
| 1047 r++; | |
| 1048 } | |
| 1049 } | |
| 1050 | |
| 1051 /*********************************************************************** | |
| 1052 * REGION_UnionO | |
| 1053 * | |
| 1054 * Handle an overlapping band for the union operation. Picks the | |
| 1055 * left-most rectangle each time and merges it into the region. | |
| 1056 * | |
| 1057 * Results: | |
| 1058 * None. | |
| 1059 * | |
| 1060 * Side Effects: | |
| 1061 * Rectangles are overwritten in pReg->rects and pReg->numRects will | |
| 1062 * be changed. | |
| 1063 * | |
| 1064 */ | |
| 1065 static void | |
| 1066 REGION_UnionO(MWCLIPREGION *pReg, MWRECT *r1, MWRECT *r1End, | |
| 1067 MWRECT *r2, MWRECT *r2End, MWCOORD top, MWCOORD bottom) | |
| 1068 { | |
| 1069 MWRECT *pNextRect; | |
| 1070 | |
| 1071 pNextRect = &pReg->rects[pReg->numRects]; | |
| 1072 | |
| 1073 #define MERGERECT(r) \ | |
| 1074 if ((pReg->numRects != 0) && \ | |
| 1075 (pNextRect[-1].top == top) && \ | |
| 1076 (pNextRect[-1].bottom == bottom) && \ | |
| 1077 (pNextRect[-1].right >= r->left)) \ | |
| 1078 { \ | |
| 1079 if (pNextRect[-1].right < r->right) \ | |
| 1080 { \ | |
| 1081 pNextRect[-1].right = r->right; \ | |
| 1082 } \ | |
| 1083 } \ | |
| 1084 else \ | |
| 1085 { \ | |
| 1086 MEMCHECK(pReg, pNextRect, pReg->rects); \ | |
| 1087 pNextRect->top = top; \ | |
| 1088 pNextRect->bottom = bottom; \ | |
| 1089 pNextRect->left = r->left; \ | |
| 1090 pNextRect->right = r->right; \ | |
| 1091 pReg->numRects += 1; \ | |
| 1092 pNextRect += 1; \ | |
| 1093 } \ | |
| 1094 r++; | |
| 1095 | |
| 1096 while ((r1 != r1End) && (r2 != r2End)) | |
| 1097 { | |
| 1098 if (r1->left < r2->left) | |
| 1099 { | |
| 1100 MERGERECT(r1); | |
| 1101 } | |
| 1102 else | |
| 1103 { | |
| 1104 MERGERECT(r2); | |
| 1105 } | |
| 1106 } | |
| 1107 | |
| 1108 if (r1 != r1End) | |
| 1109 { | |
| 1110 do | |
| 1111 { | |
| 1112 MERGERECT(r1); | |
| 1113 } while (r1 != r1End); | |
| 1114 } | |
| 1115 else while (r2 != r2End) | |
| 1116 { | |
| 1117 MERGERECT(r2); | |
| 1118 } | |
| 1119 } | |
| 1120 | |
| 1121 /*********************************************************************** | |
| 1122 * GdUnionRegion | |
| 1123 */ | |
| 1124 void | |
| 1125 GdUnionRegion(MWCLIPREGION *newReg, MWCLIPREGION *reg1, MWCLIPREGION *reg2) | |
| 1126 { | |
| 1127 /* checks all the simple cases */ | |
| 1128 | |
| 1129 /* | |
| 1130 * Region 1 and 2 are the same or region 1 is empty | |
| 1131 */ | |
| 1132 if ( (reg1 == reg2) || (!(reg1->numRects)) ) | |
| 1133 { | |
| 1134 if (newReg != reg2) | |
| 1135 GdCopyRegion(newReg, reg2); | |
| 1136 return; | |
| 1137 } | |
| 1138 | |
| 1139 /* | |
| 1140 * if nothing to union (region 2 empty) | |
| 1141 */ | |
| 1142 if (!(reg2->numRects)) | |
| 1143 { | |
| 1144 if (newReg != reg1) | |
| 1145 GdCopyRegion(newReg, reg1); | |
| 1146 return; | |
| 1147 } | |
| 1148 | |
| 1149 /* | |
| 1150 * Region 1 completely subsumes region 2 | |
| 1151 */ | |
| 1152 if ((reg1->numRects == 1) && | |
| 1153 (reg1->extents.left <= reg2->extents.left) && | |
| 1154 (reg1->extents.top <= reg2->extents.top) && | |
| 1155 (reg1->extents.right >= reg2->extents.right) && | |
| 1156 (reg1->extents.bottom >= reg2->extents.bottom)) | |
| 1157 { | |
| 1158 if (newReg != reg1) | |
| 1159 GdCopyRegion(newReg, reg1); | |
| 1160 return; | |
| 1161 } | |
| 1162 | |
| 1163 /* | |
| 1164 * Region 2 completely subsumes region 1 | |
| 1165 */ | |
| 1166 if ((reg2->numRects == 1) && | |
| 1167 (reg2->extents.left <= reg1->extents.left) && | |
| 1168 (reg2->extents.top <= reg1->extents.top) && | |
| 1169 (reg2->extents.right >= reg1->extents.right) && | |
| 1170 (reg2->extents.bottom >= reg1->extents.bottom)) | |
| 1171 { | |
| 1172 if (newReg != reg2) | |
| 1173 GdCopyRegion(newReg, reg2); | |
| 1174 return; | |
| 1175 } | |
| 1176 | |
| 1177 REGION_RegionOp (newReg, reg1, reg2, (voidProcp) REGION_UnionO, | |
| 1178 (voidProcp) REGION_UnionNonO, (voidProcp) REGION_UnionNonO); | |
| 1179 | |
| 1180 newReg->extents.left = MWMIN(reg1->extents.left, reg2->extents.left); | |
| 1181 newReg->extents.top = MWMIN(reg1->extents.top, reg2->extents.top); | |
| 1182 newReg->extents.right = MWMAX(reg1->extents.right, reg2->extents.right); | |
| 1183 newReg->extents.bottom = MWMAX(reg1->extents.bottom, reg2->extents.bottom); | |
| 1184 newReg->type = (newReg->numRects) ? MWREGION_COMPLEX : MWREGION_NULL ; | |
| 1185 } | |
| 1186 | |
| 1187 /*********************************************************************** | |
| 1188 * Region Subtraction | |
| 1189 ***********************************************************************/ | |
| 1190 | |
| 1191 /*********************************************************************** | |
| 1192 * REGION_SubtractNonO1 | |
| 1193 * | |
| 1194 * Deal with non-overlapping band for subtraction. Any parts from | |
| 1195 * region 2 we discard. Anything from region 1 we add to the region. | |
| 1196 * | |
| 1197 * Results: | |
| 1198 * None. | |
| 1199 * | |
| 1200 * Side Effects: | |
| 1201 * pReg may be affected. | |
| 1202 * | |
| 1203 */ | |
| 1204 static void | |
| 1205 REGION_SubtractNonO1(MWCLIPREGION *pReg, MWRECT *r, MWRECT *rEnd, | |
| 1206 MWCOORD top, MWCOORD bottom) | |
| 1207 { | |
| 1208 MWRECT *pNextRect; | |
| 1209 | |
| 1210 pNextRect = &pReg->rects[pReg->numRects]; | |
| 1211 | |
| 1212 while (r != rEnd) | |
| 1213 { | |
| 1214 MEMCHECK(pReg, pNextRect, pReg->rects); | |
| 1215 pNextRect->left = r->left; | |
| 1216 pNextRect->top = top; | |
| 1217 pNextRect->right = r->right; | |
| 1218 pNextRect->bottom = bottom; | |
| 1219 pReg->numRects += 1; | |
| 1220 pNextRect++; | |
| 1221 r++; | |
| 1222 } | |
| 1223 } | |
| 1224 | |
| 1225 | |
| 1226 /*********************************************************************** | |
| 1227 * REGION_SubtractO | |
| 1228 * | |
| 1229 * Overlapping band subtraction. x1 is the left-most point not yet | |
| 1230 * checked. | |
| 1231 * | |
| 1232 * Results: | |
| 1233 * None. | |
| 1234 * | |
| 1235 * Side Effects: | |
| 1236 * pReg may have rectangles added to it. | |
| 1237 * | |
| 1238 */ | |
| 1239 static void | |
| 1240 REGION_SubtractO(MWCLIPREGION *pReg, MWRECT *r1, MWRECT *r1End, | |
| 1241 MWRECT *r2, MWRECT *r2End, MWCOORD top, MWCOORD bottom) | |
| 1242 { | |
| 1243 MWRECT *pNextRect; | |
| 1244 MWCOORD left; | |
| 1245 | |
| 1246 left = r1->left; | |
| 1247 pNextRect = &pReg->rects[pReg->numRects]; | |
| 1248 | |
| 1249 while ((r1 != r1End) && (r2 != r2End)) | |
| 1250 { | |
| 1251 if (r2->right <= left) | |
| 1252 { | |
| 1253 /* | |
| 1254 * Subtrahend missed the boat: go to next subtrahend. | |
| 1255 */ | |
| 1256 r2++; | |
| 1257 } | |
| 1258 else if (r2->left <= left) | |
| 1259 { | |
| 1260 /* | |
| 1261 * Subtrahend preceeds minuend: nuke left edge of minuend. | |
| 1262 */ | |
| 1263 left = r2->right; | |
| 1264 if (left >= r1->right) | |
| 1265 { | |
| 1266 /* | |
| 1267 * Minuend completely covered: advance to next minuend and | |
| 1268 * reset left fence to edge of new minuend. | |
| 1269 */ | |
| 1270 r1++; | |
| 1271 if (r1 != r1End) | |
| 1272 left = r1->left; | |
| 1273 } | |
| 1274 else | |
| 1275 { | |
| 1276 /* | |
| 1277 * Subtrahend now used up since it doesn't extend beyond | |
| 1278 * minuend | |
| 1279 */ | |
| 1280 r2++; | |
| 1281 } | |
| 1282 } | |
| 1283 else if (r2->left < r1->right) | |
| 1284 { | |
| 1285 /* | |
| 1286 * Left part of subtrahend covers part of minuend: add uncovered | |
| 1287 * part of minuend to region and skip to next subtrahend. | |
| 1288 */ | |
| 1289 MEMCHECK(pReg, pNextRect, pReg->rects); | |
| 1290 pNextRect->left = left; | |
| 1291 pNextRect->top = top; | |
| 1292 pNextRect->right = r2->left; | |
| 1293 pNextRect->bottom = bottom; | |
| 1294 pReg->numRects += 1; | |
| 1295 pNextRect++; | |
| 1296 left = r2->right; | |
| 1297 if (left >= r1->right) | |
| 1298 { | |
| 1299 /* | |
| 1300 * Minuend used up: advance to new... | |
| 1301 */ | |
| 1302 r1++; | |
| 1303 if (r1 != r1End) | |
| 1304 left = r1->left; | |
| 1305 } | |
| 1306 else | |
| 1307 { | |
| 1308 /* | |
| 1309 * Subtrahend used up | |
| 1310 */ | |
| 1311 r2++; | |
| 1312 } | |
| 1313 } | |
| 1314 else | |
| 1315 { | |
| 1316 /* | |
| 1317 * Minuend used up: add any remaining piece before advancing. | |
| 1318 */ | |
| 1319 if (r1->right > left) | |
| 1320 { | |
| 1321 MEMCHECK(pReg, pNextRect, pReg->rects); | |
| 1322 pNextRect->left = left; | |
| 1323 pNextRect->top = top; | |
| 1324 pNextRect->right = r1->right; | |
| 1325 pNextRect->bottom = bottom; | |
| 1326 pReg->numRects += 1; | |
| 1327 pNextRect++; | |
| 1328 } | |
| 1329 r1++; | |
| 1330 left = r1->left; | |
| 1331 } | |
| 1332 } | |
| 1333 | |
| 1334 /* | |
| 1335 * Add remaining minuend rectangles to region. | |
| 1336 */ | |
| 1337 while (r1 != r1End) | |
| 1338 { | |
| 1339 MEMCHECK(pReg, pNextRect, pReg->rects); | |
| 1340 pNextRect->left = left; | |
| 1341 pNextRect->top = top; | |
| 1342 pNextRect->right = r1->right; | |
| 1343 pNextRect->bottom = bottom; | |
| 1344 pReg->numRects += 1; | |
| 1345 pNextRect++; | |
| 1346 r1++; | |
| 1347 if (r1 != r1End) | |
| 1348 { | |
| 1349 left = r1->left; | |
| 1350 } | |
| 1351 } | |
| 1352 } | |
| 1353 | |
| 1354 /*********************************************************************** | |
| 1355 * GdSubtractRegion | |
| 1356 * | |
| 1357 * Subtract regS from regM and leave the result in regD. | |
| 1358 * S stands for subtrahend, M for minuend and D for difference. | |
| 1359 * | |
| 1360 * Results: | |
| 1361 * TRUE. | |
| 1362 * | |
| 1363 * Side Effects: | |
| 1364 * regD is overwritten. | |
| 1365 * | |
| 1366 */ | |
| 1367 void | |
| 1368 GdSubtractRegion(MWCLIPREGION *regD, MWCLIPREGION *regM, MWCLIPREGION *regS ) | |
| 1369 { | |
| 1370 /* check for trivial reject */ | |
| 1371 if ( (!(regM->numRects)) || (!(regS->numRects)) || | |
| 1372 (!EXTENTCHECK(®M->extents, ®S->extents)) ) | |
| 1373 { | |
| 1374 GdCopyRegion(regD, regM); | |
| 1375 return; | |
| 1376 } | |
| 1377 | |
| 1378 REGION_RegionOp (regD, regM, regS, (voidProcp) REGION_SubtractO, | |
| 1379 (voidProcp) REGION_SubtractNonO1, (voidProcp) NULL); | |
| 1380 | |
| 1381 /* | |
| 1382 * Can't alter newReg's extents before we call miRegionOp because | |
| 1383 * it might be one of the source regions and miRegionOp depends | |
| 1384 * on the extents of those regions being the unaltered. Besides, this | |
| 1385 * way there's no checking against rectangles that will be nuked | |
| 1386 * due to coalescing, so we have to examine fewer rectangles. | |
| 1387 */ | |
| 1388 REGION_SetExtents (regD); | |
| 1389 regD->type = (regD->numRects) ? MWREGION_COMPLEX : MWREGION_NULL ; | |
| 1390 } | |
| 1391 | |
| 1392 /*********************************************************************** | |
| 1393 * GdXorRegion | |
| 1394 */ | |
| 1395 void | |
| 1396 GdXorRegion(MWCLIPREGION *dr, MWCLIPREGION *sra, MWCLIPREGION *srb) | |
| 1397 { | |
| 1398 MWCLIPREGION *tra, *trb; | |
| 1399 | |
| 1400 if ((! (tra = GdAllocRegion())) || (! (trb = GdAllocRegion()))) | |
| 1401 return; | |
| 1402 GdSubtractRegion(tra,sra,srb); | |
| 1403 GdSubtractRegion(trb,srb,sra); | |
| 1404 GdUnionRegion(dr,tra,trb); | |
| 1405 GdDestroyRegion(tra); | |
| 1406 GdDestroyRegion(trb); | |
| 1407 } | |
| 1408 | |
| 1409 #if 0 | |
| 1410 /*********************************************************************** | |
| 1411 * DumpRegion | |
| 1412 * Outputs the contents of a MWCLIPREGION | |
| 1413 */ | |
| 1414 void | |
| 1415 DumpRegion(MWCLIPREGION *pReg) | |
| 1416 { | |
| 1417 MWRECT *pRect, *pRectEnd = pReg->rects + pReg->numRects; | |
| 1418 | |
| 1419 DPRINTF("Region %p: %d,%d - %d,%d %d rects\n", pReg, | |
| 1420 pReg->extents.left, pReg->extents.top, | |
| 1421 pReg->extents.right, pReg->extents.bottom, pReg->numRects); | |
| 1422 for(pRect = pReg->rects; pRect < pRectEnd; pRect++) | |
| 1423 DPRINTF("\t%d,%d - %d,%d\n", pRect->left, pRect->top, | |
| 1424 pRect->right, pRect->bottom); | |
| 1425 } | |
| 1426 #endif |
