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 (&regM->extents,&regS->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 (&reg1->extents,&reg2->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