1 /* libwmf ("player/region.h"): library for wmf conversion
2 Copyright (C) 2000 - various; see CREDITS, ChangeLog, and sources
3
4 The libwmf Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 The libwmf Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with the libwmf Library; see the file COPYING. If not,
16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
18
19
20 #ifndef WMFPLAYER_REGION_H
21 #define WMFPLAYER_REGION_H
22
rgn_memchk(wmfAPI * API,wmfRegion * rgn)23 static wmfD_Rect* rgn_memchk (wmfAPI* API,wmfRegion* rgn)
24 { wmfD_Rect* more = 0;
25
26 if (rgn->numRects < (rgn->size - 1)) return (rgn->rects + rgn->numRects);
27
28 more = wmf_realloc (API,rgn->rects,(rgn->size + 8) * sizeof (wmfD_Rect));
29
30 if ((more == 0) || (ERR (API))) return (0); /* The two conditions are equivalent, I think */
31
32 rgn->rects = more;
33 rgn->size += 8; /* The exponential increase in memory allocation in MEMCHECK scares me */
34
35 return (rgn->rects + rgn->numRects);
36 }
37
38 /* 1 if two RECTs overlap.
39 * 0 if two RECTs do not overlap.
40 */
41 #define EXTENTCHECK(r1,r2) \
42 ( ((r1)->BR.x > (r2)->TL.x) \
43 && ((r1)->TL.x < (r2)->BR.x) \
44 && ((r1)->BR.y > (r2)->TL.y) \
45 && ((r1)->TL.y < (r2)->BR.y) )
46
WmfSetRectRgn(wmfRegion * rgn,wmfD_Rect * rect)47 static void WmfSetRectRgn (wmfRegion* rgn,wmfD_Rect* rect)
48 { if ((rect == 0) || (rect->TL.x == rect->BR.x) || (rect->TL.y == rect->BR.y)) /* EMPTY_REGION (rgn); */
49 { rgn->extents.TL.x = 0;
50 rgn->extents.TL.y = 0;
51 rgn->extents.BR.x = 0;
52 rgn->extents.BR.y = 0;
53
54 rgn->numRects = 0;
55 rgn->type = NULLREGION;
56 }
57 else
58 { rgn->extents = (*rect);
59 (*(rgn->rects)) = (*rect);
60
61 rgn->numRects = 1;
62 rgn->type = SIMPLEREGION;
63 }
64 }
65
WmfCombineRgn(wmfAPI * API,wmfRegion * destObj,wmfRegion * src1Obj,wmfRegion * src2Obj,U16 mode)66 static void WmfCombineRgn (wmfAPI* API,wmfRegion* destObj,wmfRegion* src1Obj,wmfRegion* src2Obj,U16 mode)
67 { if ((destObj == 0) || (src1Obj == 0)) return;
68
69 if (mode == RGN_COPY)
70 { REGION_CopyRegion (API,destObj,src1Obj);
71 return;
72 }
73
74 if (src2Obj)
75 { switch (mode)
76 {
77 case RGN_OR:
78 REGION_UnionRegion (API,destObj,src1Obj,src2Obj);
79 break;
80
81 case RGN_AND:
82 REGION_IntersectRegion (API,destObj,src1Obj,src2Obj);
83 break;
84 #if 0
85 case RGN_XOR:
86 REGION_XorRegion (API,destObj,src1Obj,src2Obj);
87 break;
88 #endif
89 case RGN_DIFF:
90 REGION_SubtractRegion (API,destObj,src1Obj,src2Obj);
91 break;
92 }
93 }
94 }
95
REGION_UnionRegion(wmfAPI * API,wmfRegion * newReg,wmfRegion * reg1,wmfRegion * reg2)96 static void REGION_UnionRegion (wmfAPI* API,wmfRegion* newReg,wmfRegion* reg1,wmfRegion* reg2)
97 { /* checks all the simple cases */
98
99 /* Region 1 and 2 are the same or region 1 is empty
100 */
101 if ((reg1 == reg2) || (!(reg1->numRects)))
102 { if (newReg != reg2) REGION_CopyRegion (API,newReg,reg2);
103 return;
104 }
105
106 /* If nothing to union (region 2 empty)
107 */
108 if (reg2->numRects == 0)
109 { if (newReg != reg1) REGION_CopyRegion (API,newReg,reg1);
110 return;
111 }
112
113 /* Region 1 completely subsumes region 2
114 */
115 if ( (reg1->numRects == 1)
116 && (reg1->extents.TL.x <= reg2->extents.TL.x)
117 && (reg1->extents.TL.y <= reg2->extents.TL.y)
118 && (reg1->extents.BR.x >= reg2->extents.BR.x)
119 && (reg1->extents.BR.y >= reg2->extents.BR.y))
120 { if (newReg != reg1) REGION_CopyRegion (API,newReg,reg1);
121 return;
122 }
123
124 /* Region 2 completely subsumes region 1
125 */
126 if ( (reg2->numRects == 1)
127 && (reg2->extents.TL.x <= reg1->extents.TL.x)
128 && (reg2->extents.TL.y <= reg1->extents.TL.y)
129 && (reg2->extents.BR.x >= reg1->extents.BR.x)
130 && (reg2->extents.BR.y >= reg1->extents.BR.y))
131 { if (newReg != reg2) REGION_CopyRegion (API,newReg,reg2);
132 return;
133 }
134
135 REGION_RegionOp (API,newReg,reg1,reg2,REGION_UnionO,REGION_UnionNonO,REGION_UnionNonO);
136
137 newReg->extents.TL.x = MIN (reg1->extents.TL.x,reg2->extents.TL.x);
138 newReg->extents.TL.y = MIN (reg1->extents.TL.y,reg2->extents.TL.y);
139 newReg->extents.BR.x = MAX (reg1->extents.BR.x,reg2->extents.BR.x);
140 newReg->extents.BR.y = MAX (reg1->extents.BR.y,reg2->extents.BR.y);
141
142 newReg->type = ((newReg->numRects)?(COMPLEXREGION):(NULLREGION));
143 }
144
REGION_CopyRegion(wmfAPI * API,wmfRegion * dst,wmfRegion * src)145 static void REGION_CopyRegion (wmfAPI* API,wmfRegion* dst,wmfRegion* src)
146 { if (dst != src) /* don't want to copy to itself */
147 { if (dst->size < src->numRects)
148 { dst->rects = wmf_realloc (API,dst->rects,src->numRects * sizeof (wmfD_Rect));
149
150 if (ERR (API))
151 { WMF_DEBUG (API,"bailing...");
152 return;
153 }
154
155 dst->size = src->numRects;
156 }
157 dst->numRects = src->numRects;
158
159 dst->extents.TL.x = src->extents.TL.x;
160 dst->extents.TL.y = src->extents.TL.y;
161 dst->extents.BR.x = src->extents.BR.x;
162 dst->extents.BR.y = src->extents.BR.y;
163
164 dst->type = src->type;
165
166 memcpy ((char*) dst->rects,(char*) src->rects,(int) (src->numRects * sizeof (wmfD_Rect)));
167 }
168 }
169
170 /*****************************************************************************
171 * REGION_RegionOp
172 *
173 * Apply an operation to two regions. Called by REGION_Union,
174 * REGION_Inverse, REGION_Subtract, REGION_Intersect...
175 *
176 * Results:
177 * None.
178 *
179 * Side Effects:
180 * The new region is overwritten.
181 *
182 * Notes:
183 * The idea behind this function is to view the two regions as sets.
184 * Together they cover a rectangle of area that this function divides
185 * into horizontal bands where points are covered only by one region
186 * or by both. For the first case, the nonOverlapFunc is called with
187 * each the band and the band's upper and lower extents. For the
188 * second, the overlapFunc is called to process the entire band. It
189 * is responsible for clipping the rectangles in the band, though
190 * this function provides the boundaries.
191 * At the end of each band, the new region is coalesced, if possible,
192 * to reduce the number of rectangles in the region.
193 *
194 */
REGION_RegionOp(wmfAPI * API,wmfRegion * newReg,wmfRegion * reg1,wmfRegion * reg2,pProcO overlapFunc,pProcNonO nonOverlap1Func,pProcNonO nonOverlap2Func)195 static void REGION_RegionOp (
196 wmfAPI* API,
197 wmfRegion* newReg, /* Place to store result */
198 wmfRegion* reg1, /* First region in operation */
199 wmfRegion* reg2, /* 2nd region in operation */
200 pProcO overlapFunc, /* Function to call for over-lapping bands */
201 pProcNonO nonOverlap1Func, /* Function to call for non-overlapping bands in region 1 */
202 pProcNonO nonOverlap2Func /* Function to call for non-overlapping bands in region 2 */
203 )
204 { wmfD_Rect* r1; /* Pointer into first region */
205 wmfD_Rect* r2; /* Pointer into 2d region */
206 wmfD_Rect* r1End; /* End of 1st region */
207 wmfD_Rect* r2End; /* End of 2d region */
208 wmfD_Rect* oldRects; /* Old rects for newReg */
209 wmfD_Rect* r1BandEnd; /* End of current band in r1 */
210 wmfD_Rect* r2BandEnd; /* End of current band in r2 */
211 wmfD_Rect* prev_rects;
212
213 float ytop; /* Top of intersection */
214 float ybot; /* Bottom of intersection */
215 float top; /* Top of non-overlapping band */
216 float bot; /* Bottom of non-overlapping band */
217
218 unsigned int prevBand; /* Index of start of previous band in newReg */
219 unsigned int curBand; /* Index of start of current band in newReg */
220
221 /*Initialization:
222 *
223 * set r1, r2, r1End and r2End appropriately, preserve the important
224 * parts of the destination region until the end in case it's one of
225 * the two source regions, then mark the "new" region empty, allocating
226 * another array of rectangles for it to use.
227 */
228 r1 = reg1->rects;
229 r2 = reg2->rects;
230
231 r1End = r1 + reg1->numRects;
232 r2End = r2 + reg2->numRects;
233
234 /* newReg may be one of the src regions so we can't empty it. We keep a
235 * note of its rects pointer (so that we can free them later), preserve its
236 * extents and simply set numRects to zero.
237 */
238 oldRects = newReg->rects;
239 newReg->numRects = 0;
240
241 /* Allocate a reasonable number of rectangles for the new region. The idea
242 * is to allocate enough so the individual functions don't need to
243 * reallocate and copy the array, which is time consuming, yet we don't
244 * have to worry about using too much memory. I hope to be able to
245 * nuke the Xrealloc() at the end of this function eventually.
246 */
247 newReg->size = MAX (reg1->numRects,reg2->numRects) * 2;
248
249 if ((newReg->rects = wmf_malloc (API,sizeof (wmfD_Rect) * newReg->size)) == 0)
250 { newReg->size = 0;
251 return;
252 }
253
254 /* Initialize ybot and ytop.
255 *
256 * In the upcoming loop, ybot and ytop serve different functions depending
257 * on whether the band being handled is an overlapping or non-overlapping
258 * band.
259 *
260 * In the case of a non-overlapping band (only one of the regions
261 * has points in the band), ybot is the bottom of the most recent
262 * intersection and thus clips the top of the rectangles in that band.
263 * ytop is the top of the next intersection between the two regions and
264 * serves to clip the bottom of the rectangles in the current band.
265 *
266 * For an overlapping band (where the two regions intersect), ytop clips
267 * the top of the rectangles of both regions and ybot clips the bottoms.
268 */
269 if (reg1->extents.TL.y < reg2->extents.TL.y)
270 { ybot = reg1->extents.TL.y;
271 }
272 else
273 { ybot = reg2->extents.TL.y;
274 }
275
276 /* prevBand serves to mark the start of the previous band so rectangles
277 * can be coalesced into larger rectangles. qv. miCoalesce, above.
278 * In the beginning, there is no previous band, so prevBand == curBand
279 * (curBand is set later on, of course, but the first band will always
280 * start at index 0). prevBand and curBand must be indices because of
281 * the possible expansion, and resultant moving, of the new region's
282 * array of rectangles.
283 */
284 prevBand = 0;
285
286 do
287 { curBand = newReg->numRects;
288
289 /* This algorithm proceeds one source-band (as opposed to a
290 * destination band, which is determined by where the two regions
291 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
292 * rectangle after the last one in the current band for their
293 * respective regions.
294 */
295 r1BandEnd = r1;
296 while ((r1BandEnd != r1End) && (r1BandEnd->TL.y == r1->TL.y)) r1BandEnd++;
297
298 r2BandEnd = r2;
299 while ((r2BandEnd != r2End) && (r2BandEnd->TL.y == r2->TL.y)) r2BandEnd++;
300
301 /* First handle the band that doesn't intersect, if any.
302 *
303 * Note that attention is restricted to one band in the
304 * non-intersecting region at once, so if a region has n
305 * bands between the current position and the next place it overlaps
306 * the other, this entire loop will be passed through n times.
307 */
308 if (r1->TL.y < r2->TL.y)
309 { top = MAX (r1->TL.y,ybot);
310 bot = MIN (r1->BR.y,r2->TL.y);
311
312 if ((top != bot) && (nonOverlap1Func))
313 { (*nonOverlap1Func) (API,newReg,r1,r1BandEnd,top,bot);
314 }
315
316 ytop = r2->TL.y;
317 }
318 else if (r2->TL.y < r1->TL.y)
319 { top = MAX (r2->TL.y,ybot);
320 bot = MIN (r2->BR.y,r1->TL.y);
321
322 if ((top != bot) && (nonOverlap2Func))
323 { (*nonOverlap2Func) (API,newReg,r2,r2BandEnd,top,bot);
324 }
325
326 ytop = r1->TL.y;
327 }
328 else
329 { ytop = r1->TL.y;
330 }
331
332 /* If any rectangles got added to the region, try and coalesce them
333 * with rectangles from the previous band. Note we could just do
334 * this test in miCoalesce, but some machines incur a not
335 * inconsiderable cost for function calls, so...
336 */
337 if (newReg->numRects != curBand)
338 { prevBand = REGION_Coalesce (newReg,prevBand,curBand);
339 }
340
341 /* Now see if we've hit an intersecting band. The two bands only
342 * intersect if ybot > ytop
343 */
344 ybot = MIN (r1->BR.y,r2->BR.y);
345 curBand = newReg->numRects;
346 if ((ybot > ytop) && (overlapFunc))
347 { (*overlapFunc) (API,newReg,r1,r1BandEnd,r2,r2BandEnd,ytop,ybot);
348 }
349
350 if (newReg->numRects != curBand)
351 { prevBand = REGION_Coalesce (newReg,prevBand,curBand);
352 }
353
354 /* If we've finished with a band (bottom == ybot) we skip forward
355 * in the region to the next band.
356 */
357 if (r1->BR.y == ybot) r1 = r1BandEnd;
358 if (r2->BR.y == ybot) r2 = r2BandEnd;
359
360 } while ((r1 != r1End) && (r2 != r2End));
361
362 /* Deal with whichever region still has rectangles left.
363 */
364 curBand = newReg->numRects;
365 if (r1 != r1End)
366 { if (nonOverlap1Func)
367 { do
368 { r1BandEnd = r1;
369 while ((r1BandEnd < r1End) && (r1BandEnd->TL.y == r1->TL.y)) r1BandEnd++;
370
371 (*nonOverlap1Func) (API,newReg,r1,r1BandEnd,MAX (r1->TL.y,ybot),r1->BR.y);
372
373 r1 = r1BandEnd;
374 } while (r1 != r1End);
375 }
376 }
377 else if ((r2 != r2End) && (nonOverlap2Func))
378 { do
379 { r2BandEnd = r2;
380 while ((r2BandEnd < r2End) && (r2BandEnd->TL.y == r2->TL.y)) r2BandEnd++;
381
382 (*nonOverlap2Func) (API,newReg,r2,r2BandEnd,MAX (r2->TL.y,ybot),r2->BR.y);
383
384 r2 = r2BandEnd;
385 } while (r2 != r2End);
386 }
387
388 if (newReg->numRects != curBand)
389 { REGION_Coalesce (newReg,prevBand,curBand);
390 }
391
392 /* A bit of cleanup. To keep regions from growing without bound,
393 * we shrink the array of rectangles to match the new number of
394 * rectangles in the region. This never goes to 0, however...
395 *
396 * Only do this stuff if the number of rectangles allocated is more than
397 * twice the number of rectangles in the region (a simple optimization...).
398 */
399 if (newReg->numRects < (newReg->size >> 1))
400 { if (newReg->numRects) /* REGION_NOT_EMPTY */
401 { prev_rects = newReg->rects;
402 newReg->size = newReg->numRects;
403 newReg->rects = wmf_realloc (API,newReg->rects,sizeof(wmfD_Rect) * newReg->size);
404 if (newReg->rects == 0) newReg->rects = prev_rects;
405 }
406 else
407 { /* No point in doing the extra work involved in an Xrealloc if
408 * the region is empty
409 */
410 newReg->size = 1;
411 wmf_free (API,newReg->rects);
412 newReg->rects = wmf_malloc (API,sizeof (wmfD_Rect));
413 }
414 }
415
416 wmf_free (API,oldRects);
417 }
418
419 /*****************************************************************************
420 * REGION_Coalesce
421 *
422 * Attempt to merge the rects in the current band with those in the
423 * previous one. Used only by REGION_RegionOp.
424 *
425 * Results:
426 * The new index for the previous band.
427 *
428 * Side Effects:
429 * If coalescing takes place:
430 * - rectangles in the previous band will have their bottom fields
431 * altered.
432 * - pReg->numRects will be decreased.
433 *
434 */
REGION_Coalesce(wmfRegion * pReg,unsigned int prevStart,unsigned int curStart)435 static unsigned int REGION_Coalesce (
436 wmfRegion* pReg, /* Region to coalesce */
437 unsigned int prevStart, /* Index of start of previous band */
438 unsigned int curStart /* Index of start of current band */
439 )
440 { wmfD_Rect* pPrevRect; /* Current rect in previous band */
441 wmfD_Rect* pCurRect; /* Current rect in current band */
442 wmfD_Rect* pRegEnd; /* End of region */
443
444 unsigned int curNumRects; /* Number of rectangles in current band */
445 unsigned int prevNumRects; /* Number of rectangles in previous band */
446
447 float bandtop; /* top coordinate for current band */
448
449 pRegEnd = pReg->rects + pReg->numRects;
450 pPrevRect = pReg->rects + prevStart;
451 prevNumRects = curStart - prevStart;
452
453 /* Figure out how many rectangles are in the current band. Have to do
454 * this because multiple bands could have been added in REGION_RegionOp
455 * at the end when one region has been exhausted.
456 */
457 pCurRect = pReg->rects + curStart;
458 bandtop = pCurRect->TL.y;
459
460 for (curNumRects = 0; (pCurRect != pRegEnd) && (pCurRect->TL.y == bandtop); curNumRects++)
461 { pCurRect++;
462 }
463
464 if (pCurRect != pRegEnd)
465 { /* If more than one band was added, we have to find the start
466 * of the last band added so the next coalescing job can start
467 * at the right place... (given when multiple bands are added,
468 * this may be pointless -- see above).
469 */
470 pRegEnd--;
471 while (pRegEnd[-1].TL.y == pRegEnd->TL.y) pRegEnd--;
472
473 curStart = pRegEnd - pReg->rects;
474 pRegEnd = pReg->rects + pReg->numRects;
475 }
476
477 if ((curNumRects == prevNumRects) && (curNumRects != 0))
478 { pCurRect -= curNumRects;
479
480 /* The bands may only be coalesced if the bottom of the previous
481 * matches the top scanline of the current.
482 */
483 if (pPrevRect->BR.y == pCurRect->TL.y)
484 { /* Make sure the bands have rects in the same places. This
485 * assumes that rects have been added in such a way that they
486 * cover the most area possible. I.e. two rects in a band must
487 * have some horizontal space between them.
488 */
489 do
490 { if ( (pPrevRect->TL.x != pCurRect->TL.x )
491 || (pPrevRect->BR.x != pCurRect->BR.x))
492 { /* The bands don't line up so they can't be coalesced.
493 */
494 return (curStart);
495 }
496 pPrevRect++;
497 pCurRect++;
498 prevNumRects -= 1;
499 } while (prevNumRects != 0);
500
501 pReg->numRects -= curNumRects;
502 pCurRect -= curNumRects;
503 pPrevRect -= curNumRects;
504
505 /* The bands may be merged, so set the bottom of each rect
506 * in the previous band to that of the corresponding rect in
507 * the current band.
508 */
509 do
510 { pPrevRect->BR.y = pCurRect->BR.y;
511 pPrevRect++;
512 pCurRect++;
513 curNumRects -= 1;
514 } while (curNumRects != 0);
515
516 /* If only one band was added to the region, we have to backup
517 * curStart to the start of the previous band.
518 *
519 * If more than one band was added to the region, copy the
520 * other bands down. The assumption here is that the other bands
521 * came from the same region as the current one and no further
522 * coalescing can be done on them since it's all been done
523 * already... curStart is already in the right place.
524 */
525 if (pCurRect == pRegEnd)
526 { curStart = prevStart;
527 }
528 else
529 { do
530 { *pPrevRect++ = *pCurRect++;
531 } while (pCurRect != pRegEnd);
532 }
533 }
534 }
535
536 return (curStart);
537 }
538
539 /*****************************************************************************
540 * REGION_UnionO
541 *
542 * Handle an overlapping band for the union operation. Picks the
543 * left-most rectangle each time and merges it into the region.
544 *
545 * Results:
546 * None.
547 *
548 * Side Effects:
549 * Rectangles are overwritten in pReg->rects and pReg->numRects will
550 * be changed.
551 *
552 */
REGION_UnionO(wmfAPI * API,wmfRegion * pReg,wmfD_Rect * r1,wmfD_Rect * r1End,wmfD_Rect * r2,wmfD_Rect * r2End,float top,float bottom)553 static void REGION_UnionO (
554 wmfAPI* API,
555 wmfRegion* pReg,
556 wmfD_Rect* r1,
557 wmfD_Rect* r1End,
558 wmfD_Rect* r2,
559 wmfD_Rect* r2End,
560 float top,
561 float bottom
562 )
563 { while ((r1 != r1End) && (r2 != r2End))
564 { if (r1->TL.x < r2->TL.x)
565 { rect_merge (API,pReg,r1,top,bottom);
566 if (ERR (API)) return;
567 r1++;
568 }
569 else
570 { rect_merge (API,pReg,r2,top,bottom);
571 if (ERR (API)) return;
572 r2++;
573 }
574 }
575
576 if (r1 != r1End)
577 { do
578 { rect_merge (API,pReg,r1,top,bottom);
579 if (ERR (API)) return;
580 r1++;
581 } while (r1 != r1End);
582 }
583 else
584 { while (r2 != r2End)
585 { rect_merge (API,pReg,r2,top,bottom);
586 if (ERR (API)) return;
587 r2++;
588 }
589 }
590 }
591
rect_merge(wmfAPI * API,wmfRegion * rgn,wmfD_Rect * r,float top,float bottom)592 static void rect_merge (
593 wmfAPI* API,
594 wmfRegion* rgn,
595 wmfD_Rect* r,
596 float top,
597 float bottom
598 )
599 { wmfD_Rect* pNextRect = 0;
600 wmfD_Rect* pPrior = 0;
601
602 if ((pNextRect = rgn_memchk (API,rgn)) == 0) return;
603
604 if (rgn->numRects == 0)
605 { rgn->numRects++;
606
607 pNextRect->TL.x = r->TL.x;
608 pNextRect->TL.y = top;
609 pNextRect->BR.x = r->BR.x;
610 pNextRect->BR.y = bottom;
611
612 pNextRect++;
613 }
614 else
615 { pPrior = pNextRect - 1;
616 if ((pPrior->TL.y == top) && (pPrior->BR.y == bottom) && (pPrior->BR.x >= r->TL.x))
617 { if (pPrior->BR.x < r->BR.x) pPrior->BR.x = r->BR.x;
618 }
619 else
620 { rgn->numRects++;
621
622 pNextRect->TL.x = r->TL.x;
623 pNextRect->TL.y = top;
624 pNextRect->BR.x = r->BR.x;
625 pNextRect->BR.y = bottom;
626
627 pNextRect++;
628 }
629 }
630 }
631
632 /*****************************************************************************
633 * REGION_UnionNonO
634 *
635 * Handle a non-overlapping band for the union operation. Just
636 * Adds the rectangles into the region. Doesn't have to check for
637 * subsumption or anything.
638 *
639 * Results:
640 * None.
641 *
642 * Side Effects:
643 * pReg->numRects is incremented and the final rectangles overwritten
644 * with the rectangles we're passed.
645 *
646 */
REGION_UnionNonO(wmfAPI * API,wmfRegion * pReg,wmfD_Rect * r,wmfD_Rect * rEnd,float top,float bottom)647 static void REGION_UnionNonO (
648 wmfAPI* API,
649 wmfRegion* pReg,
650 wmfD_Rect* r,
651 wmfD_Rect* rEnd,
652 float top,
653 float bottom
654 )
655 { wmfD_Rect* pNextRect = 0;
656
657 while (r != rEnd)
658 { if ((pNextRect = rgn_memchk (API,pReg)) == 0) return;
659
660 pReg->numRects++;
661
662 pNextRect->TL.x = r->TL.x;
663 pNextRect->TL.y = top;
664 pNextRect->BR.x = r->BR.x;
665 pNextRect->BR.y = bottom;
666
667 pNextRect++;
668 r++;
669 }
670 }
671
672 /*****************************************************************************
673 * REGION_SubtractNonO1
674 *
675 * Deal with non-overlapping band for subtraction. Any parts from
676 * region 2 we discard. Anything from region 1 we add to the region.
677 *
678 * Results:
679 * None.
680 *
681 * Side Effects:
682 * pReg may be affected.
683 *
684 */
REGION_SubtractNonO1(wmfAPI * API,wmfRegion * pReg,wmfD_Rect * r,wmfD_Rect * rEnd,float top,float bottom)685 static void REGION_SubtractNonO1 (
686 wmfAPI* API,
687 wmfRegion* pReg,
688 wmfD_Rect* r,
689 wmfD_Rect* rEnd,
690 float top,
691 float bottom
692 )
693 { wmfD_Rect* pNextRect = 0;
694
695 while (r != rEnd)
696 { if ((pNextRect = rgn_memchk (API,pReg)) == 0) return;
697
698 pReg->numRects++;
699
700 pNextRect->TL.x = r->TL.x;
701 pNextRect->TL.y = top;
702 pNextRect->BR.x = r->BR.x;
703 pNextRect->BR.y = bottom;
704
705 pNextRect++;
706 r++;
707 }
708 }
709
710 /*****************************************************************************
711 * REGION_SubtractRegion
712 *
713 * Subtract regS from regM and leave the result in regD.
714 * S stands for subtrahend, M for minuend and D for difference.
715 *
716 * Results:
717 * TRUE.
718 *
719 * Side Effects:
720 * regD is overwritten.
721 *
722 */
REGION_SubtractRegion(wmfAPI * API,wmfRegion * regD,wmfRegion * regM,wmfRegion * regS)723 static void REGION_SubtractRegion (
724 wmfAPI* API,
725 wmfRegion* regD,
726 wmfRegion* regM,
727 wmfRegion* regS
728 )
729 { /* check for trivial reject
730 */
731 if ( (regM->numRects == 0)
732 || (regS->numRects == 0)
733 || (!EXTENTCHECK (®M->extents,®S->extents)) )
734 { REGION_CopyRegion (API,regD,regM);
735 return;
736 }
737
738 REGION_RegionOp (API,regD,regM,regS,REGION_SubtractO,REGION_SubtractNonO1,0);
739
740 if (ERR (API))
741 { WMF_DEBUG (API,"bailing...");
742 return;
743 }
744
745 /* Can't alter newReg's extents before we call miRegionOp because
746 * it might be one of the source regions and miRegionOp depends
747 * on the extents of those regions being the unaltered. Besides, this
748 * way there's no checking against rectangles that will be nuked
749 * due to coalescing, so we have to examine fewer rectangles.
750 */
751 REGION_SetExtents (regD);
752 regD->type = ((regD->numRects) ? COMPLEXREGION : NULLREGION);
753 }
754
755 /*****************************************************************************
756 * REGION_SubtractO
757 *
758 * Overlapping band subtraction. x1 is the left-most point not yet
759 * checked.
760 *
761 * Results:
762 * None.
763 *
764 * Side Effects:
765 * pReg may have rectangles added to it.
766 *
767 */
REGION_SubtractO(wmfAPI * API,wmfRegion * pReg,wmfD_Rect * r1,wmfD_Rect * r1End,wmfD_Rect * r2,wmfD_Rect * r2End,float top,float bottom)768 static void REGION_SubtractO (
769 wmfAPI* API,
770 wmfRegion* pReg,
771 wmfD_Rect* r1,
772 wmfD_Rect* r1End,
773 wmfD_Rect* r2,
774 wmfD_Rect* r2End,
775 float top,
776 float bottom
777 )
778 { wmfD_Rect* pNextRect = 0;
779
780 float left = r1->TL.x;
781
782 if ((pNextRect = rgn_memchk (API,pReg)) == 0) return;
783
784 while ((r1 != r1End) && (r2 != r2End))
785 { if (r2->BR.x <= left)
786 { /* Subtrahend missed the boat: go to next subtrahend.
787 */
788 r2++;
789 }
790 else if (r2->TL.x <= left)
791 { /* Subtrahend preceeds minuend: nuke left edge of minuend.
792 */
793 left = r2->BR.x;
794 if (left >= r1->BR.x)
795 { /* Minuend completely covered: advance to next minuend and
796 * reset left fence to edge of new minuend.
797 */
798 r1++;
799 if (r1 != r1End) left = r1->TL.x;
800 }
801 else
802 { /* Subtrahend now used up since it doesn't extend beyond
803 * minuend
804 */
805 r2++;
806 }
807 }
808 else if (r2->TL.x < r1->BR.x)
809 { /* Left part of subtrahend covers part of minuend: add uncovered
810 * part of minuend to region and skip to next subtrahend.
811 */
812 if ((pNextRect = rgn_memchk (API,pReg)) == 0) return;
813
814 pReg->numRects++;
815
816 pNextRect->TL.x = left;
817 pNextRect->TL.y = top;
818 pNextRect->BR.x = r2->TL.x;
819 pNextRect->BR.y = bottom;
820
821 pNextRect++;
822
823 left = r2->BR.x;
824
825 if (left >= r1->BR.x)
826 { /* Minuend used up: advance to new...
827 */
828 r1++;
829 if (r1 != r1End) left = r1->TL.x;
830 }
831 else
832 { /* Subtrahend used up
833 */
834 r2++;
835 }
836 }
837 else
838 { /* Minuend used up: add any remaining piece before advancing.
839 */
840 if (r1->BR.x > left)
841 { if ((pNextRect = rgn_memchk (API,pReg)) == 0) return;
842
843 pReg->numRects++;
844
845 pNextRect->TL.x = left;
846 pNextRect->TL.y = top;
847 pNextRect->BR.x = r1->BR.x;
848 pNextRect->BR.y = bottom;
849
850 pNextRect++;
851 }
852 r1++;
853 left = r1->TL.x;
854 }
855 }
856
857 /* Add remaining minuend rectangles to region.
858 */
859 while (r1 != r1End)
860 { if ((pNextRect = rgn_memchk (API,pReg)) == 0) return;
861
862 pReg->numRects++;
863
864 pNextRect->TL.x = left;
865 pNextRect->TL.y = top;
866 pNextRect->BR.x = r1->BR.x;
867 pNextRect->BR.y = bottom;
868
869 pNextRect++;
870 r1++;
871
872 if (r1 != r1End) left = r1->TL.x;
873 }
874 }
875
876 /*****************************************************************************
877 * REGION_SetExtents
878 * Re-calculate the extents of a region
879 */
REGION_SetExtents(wmfRegion * pReg)880 static void REGION_SetExtents (
881 wmfRegion* pReg
882 )
883 { wmfD_Rect* pRect;
884 wmfD_Rect* pRectEnd;
885 wmfD_Rect* pExtents;
886
887 if (pReg->numRects == 0)
888 { pReg->extents.TL.x = 0;
889 pReg->extents.TL.y = 0;
890 pReg->extents.BR.x = 0;
891 pReg->extents.BR.y = 0;
892
893 return;
894 }
895
896 pExtents = &pReg->extents;
897 pRect = pReg->rects;
898 pRectEnd = pRect + pReg->numRects - 1;
899
900 /* Since pRect is the first rectangle in the region, it must have the
901 * smallest top and since pRectEnd is the last rectangle in the region,
902 * it must have the largest bottom, because of banding. Initialize left and
903 * right from pRect and pRectEnd, resp., as good things to initialize them
904 * to...
905 */
906 pExtents->TL.x = pRect->TL.x;
907 pExtents->TL.y = pRect->TL.y;
908 pExtents->BR.x = pRectEnd->BR.x;
909 pExtents->BR.y = pRectEnd->BR.y;
910
911 while (pRect <= pRectEnd)
912 { if (pRect->TL.x < pExtents->TL.x) pExtents->TL.x = pRect->TL.x;
913 if (pRect->BR.x > pExtents->BR.x) pExtents->BR.x = pRect->BR.x;
914
915 pRect++;
916 }
917 }
918
919 /*****************************************************************************
920 * REGION_IntersectRegion
921 */
REGION_IntersectRegion(wmfAPI * API,wmfRegion * newReg,wmfRegion * reg1,wmfRegion * reg2)922 static void REGION_IntersectRegion (
923 wmfAPI* API,
924 wmfRegion* newReg,
925 wmfRegion* reg1,
926 wmfRegion* reg2
927 )
928 { /* check for trivial reject
929 */
930 if ( (!(reg1->numRects)) || (!(reg2->numRects)) || (!EXTENTCHECK (®1->extents,®2->extents)))
931 { newReg->numRects = 0;
932 }
933 else
934 { REGION_RegionOp (API,newReg,reg1,reg2,REGION_IntersectO,0,0);
935 }
936
937 /* Can't alter newReg's extents before we call miRegionOp because
938 * it might be one of the source regions and miRegionOp depends
939 * on the extents of those regions being the same. Besides, this
940 * way there's no checking against rectangles that will be nuked
941 * due to coalescing, so we have to examine fewer rectangles.
942 */
943 REGION_SetExtents (newReg);
944
945 newReg->type = ((newReg->numRects) ? COMPLEXREGION : NULLREGION);
946 }
947
948 /*****************************************************************************
949 * REGION_IntersectO
950 *
951 * Handle an overlapping band for REGION_Intersect.
952 *
953 * Results:
954 * None.
955 *
956 * Side Effects:
957 * Rectangles may be added to the region.
958 *
959 */
REGION_IntersectO(wmfAPI * API,wmfRegion * pReg,wmfD_Rect * r1,wmfD_Rect * r1End,wmfD_Rect * r2,wmfD_Rect * r2End,float top,float bottom)960 static void REGION_IntersectO (
961 wmfAPI* API,
962 wmfRegion* pReg,
963 wmfD_Rect* r1,
964 wmfD_Rect* r1End,
965 wmfD_Rect* r2,
966 wmfD_Rect* r2End,
967 float top,
968 float bottom
969 )
970 { float left;
971 float right;
972
973 wmfD_Rect* pNextRect;
974
975 while ((r1 != r1End) && (r2 != r2End))
976 { left = MAX (r1->TL.x,r2->TL.x);
977 right = MIN (r1->BR.x,r2->BR.x);
978
979 /* If there's any overlap between the two rectangles, add that
980 * overlap to the new region.
981 * There's no need to check for subsumption because the only way
982 * such a need could arise is if some region has two rectangles
983 * right next to each other. Since that should never happen...
984 */
985 if (left < right)
986 { if ((pNextRect = rgn_memchk (API,pReg)) == 0) return;
987
988 pReg->numRects++;
989
990 pNextRect->TL.x = left;
991 pNextRect->TL.y = top;
992 pNextRect->BR.x = right;
993 pNextRect->BR.y = bottom;
994
995 pNextRect++;
996 }
997
998 /* Need to advance the pointers. Shift the one that extends
999 * to the right the least, since the other still has a chance to
1000 * overlap with that region's next rectangle, if you see what I mean.
1001 */
1002 if (r1->BR.x < r2->BR.x)
1003 { r1++;
1004 }
1005 else if (r2->BR.x < r1->BR.x)
1006 { r2++;
1007 }
1008 else
1009 { r1++;
1010 r2++;
1011 }
1012 }
1013 }
1014
1015 #endif /* ! WMFPLAYER_REGION_H */
1016