/src/FreeRDP/libfreerdp/codec/region.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /**  | 
2  |  |  * FreeRDP: A Remote Desktop Protocol Implementation  | 
3  |  |  *  | 
4  |  |  * Copyright 2014 Thincast Technologies GmbH  | 
5  |  |  * Copyright 2014 Hardening <contact@hardening-consulting.com>  | 
6  |  |  * Copyright 2017 Armin Novak <armin.novak@thincast.com>  | 
7  |  |  * Copyright 2017 Thincast Technologies GmbH  | 
8  |  |  *  | 
9  |  |  * Licensed under the Apache License, Version 2.0 (the "License");  | 
10  |  |  * you may not use this file except in compliance with the License.  | 
11  |  |  * You may obtain a copy of the License at  | 
12  |  |  *  | 
13  |  |  * http://www.apache.org/licenses/LICENSE-2.0  | 
14  |  |  *  | 
15  |  |  * Unless required by applicable law or agreed to in writing, software  | 
16  |  |  * distributed under the License is distributed on an "AS IS" BASIS,  | 
17  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  | 
18  |  |  * See the License for the specific language governing permissions and  | 
19  |  |  * limitations under the License.  | 
20  |  |  */  | 
21  |  |  | 
22  |  | #include <winpr/assert.h>  | 
23  |  | #include <winpr/memory.h>  | 
24  |  | #include <freerdp/log.h>  | 
25  |  | #include <freerdp/codec/region.h>  | 
26  |  |  | 
27  |  | #define TAG FREERDP_TAG("codec") | 
28  |  |  | 
29  |  | /*  | 
30  |  |  * The functions in this file implement the Region abstraction largely inspired from  | 
31  |  |  * pixman library. The following comment is taken from the pixman code.  | 
32  |  |  *  | 
33  |  |  * A Region is simply a set of disjoint(non-overlapping) rectangles, plus an  | 
34  |  |  * "extent" rectangle which is the smallest single rectangle that contains all  | 
35  |  |  * the non-overlapping rectangles.  | 
36  |  |  *  | 
37  |  |  * A Region is implemented as a "y-x-banded" array of rectangles.  This array  | 
38  |  |  * imposes two degrees of order.  First, all rectangles are sorted by top side  | 
39  |  |  * y coordinate first (y1), and then by left side x coordinate (x1).  | 
40  |  |  *  | 
41  |  |  * Furthermore, the rectangles are grouped into "bands".  Each rectangle in a  | 
42  |  |  * band has the same top y coordinate (y1), and each has the same bottom y  | 
43  |  |  * coordinate (y2).  Thus all rectangles in a band differ only in their left  | 
44  |  |  * and right side (x1 and x2).  Bands are implicit in the array of rectangles:  | 
45  |  |  * there is no separate list of band start pointers.  | 
46  |  |  *  | 
47  |  |  * The y-x band representation does not minimize rectangles.  In particular,  | 
48  |  |  * if a rectangle vertically crosses a band (the rectangle has scanlines in  | 
49  |  |  * the y1 to y2 area spanned by the band), then the rectangle may be broken  | 
50  |  |  * down into two or more smaller rectangles stacked one atop the other.  | 
51  |  |  *  | 
52  |  |  *  -----------                             -----------  | 
53  |  |  *  |         |                             |         |             band 0  | 
54  |  |  *  |         |  --------                   -----------  --------  | 
55  |  |  *  |         |  |      |  in y-x banded    |         |  |      |   band 1  | 
56  |  |  *  |         |  |      |  form is          |         |  |      |  | 
57  |  |  *  -----------  |      |                   -----------  --------  | 
58  |  |  *               |      |                                |      |   band 2  | 
59  |  |  *               --------                                --------  | 
60  |  |  *  | 
61  |  |  * An added constraint on the rectangles is that they must cover as much  | 
62  |  |  * horizontal area as possible: no two rectangles within a band are allowed  | 
63  |  |  * to touch.  | 
64  |  |  *  | 
65  |  |  * Whenever possible, bands will be merged together to cover a greater vertical  | 
66  |  |  * distance (and thus reduce the number of rectangles). Two bands can be merged  | 
67  |  |  * only if the bottom of one touches the top of the other and they have  | 
68  |  |  * rectangles in the same places (of the same width, of course).  | 
69  |  |  */  | 
70  |  |  | 
71  |  | struct S_REGION16_DATA  | 
72  |  | { | 
73  |  |   long size;  | 
74  |  |   long nbRects;  | 
75  |  | };  | 
76  |  |  | 
77  |  | static REGION16_DATA empty_region = { 0, 0 }; | 
78  |  |  | 
79  |  | void region16_init(REGION16* region)  | 
80  | 13.3k  | { | 
81  | 13.3k  |   WINPR_ASSERT(region);  | 
82  | 13.3k  |   ZeroMemory(region, sizeof(REGION16));  | 
83  | 13.3k  |   region->data = &empty_region;  | 
84  | 13.3k  | }  | 
85  |  |  | 
86  |  | int region16_n_rects(const REGION16* region)  | 
87  | 36  | { | 
88  | 36  |   WINPR_ASSERT(region);  | 
89  | 36  |   WINPR_ASSERT(region->data);  | 
90  | 36  |   return region->data->nbRects;  | 
91  | 36  | }  | 
92  |  |  | 
93  |  | const RECTANGLE_16* region16_rects(const REGION16* region, UINT32* nbRects)  | 
94  | 928  | { | 
95  | 928  |   REGION16_DATA* data = NULL;  | 
96  |  |  | 
97  | 928  |   if (nbRects)  | 
98  | 928  |     *nbRects = 0;  | 
99  |  |  | 
100  | 928  |   if (!region)  | 
101  | 0  |     return NULL;  | 
102  |  |  | 
103  | 928  |   data = region->data;  | 
104  |  |  | 
105  | 928  |   if (!data)  | 
106  | 0  |     return NULL;  | 
107  |  |  | 
108  | 928  |   if (nbRects)  | 
109  | 928  |     *nbRects = data->nbRects;  | 
110  |  |  | 
111  | 928  |   return (RECTANGLE_16*)(data + 1);  | 
112  | 928  | }  | 
113  |  |  | 
114  |  | static INLINE RECTANGLE_16* region16_rects_noconst(REGION16* region)  | 
115  | 9  | { | 
116  | 9  |   REGION16_DATA* data = NULL;  | 
117  | 9  |   data = region->data;  | 
118  |  |  | 
119  | 9  |   if (!data)  | 
120  | 0  |     return NULL;  | 
121  |  |  | 
122  | 9  |   return (RECTANGLE_16*)(&data[1]);  | 
123  | 9  | }  | 
124  |  |  | 
125  |  | const RECTANGLE_16* region16_extents(const REGION16* region)  | 
126  | 18  | { | 
127  | 18  |   if (!region)  | 
128  | 0  |     return NULL;  | 
129  |  |  | 
130  | 18  |   return ®ion->extents;  | 
131  | 18  | }  | 
132  |  |  | 
133  |  | static RECTANGLE_16* region16_extents_noconst(REGION16* region)  | 
134  | 18  | { | 
135  | 18  |   if (!region)  | 
136  | 0  |     return NULL;  | 
137  |  |  | 
138  | 18  |   return ®ion->extents;  | 
139  | 18  | }  | 
140  |  |  | 
141  |  | BOOL rectangle_is_empty(const RECTANGLE_16* rect)  | 
142  | 0  | { | 
143  |  |   /* A rectangle with width <= 0 or height <= 0 should be regarded  | 
144  |  |    * as empty.  | 
145  |  |    */  | 
146  | 0  |   return ((rect->left >= rect->right) || (rect->top >= rect->bottom)) ? TRUE : FALSE;  | 
147  | 0  | }  | 
148  |  |  | 
149  |  | BOOL region16_is_empty(const REGION16* region)  | 
150  | 0  | { | 
151  | 0  |   WINPR_ASSERT(region);  | 
152  | 0  |   WINPR_ASSERT(region->data);  | 
153  | 0  |   return (region->data->nbRects == 0);  | 
154  | 0  | }  | 
155  |  |  | 
156  |  | BOOL rectangles_equal(const RECTANGLE_16* r1, const RECTANGLE_16* r2)  | 
157  | 0  | { | 
158  | 0  |   return ((r1->left == r2->left) && (r1->top == r2->top) && (r1->right == r2->right) &&  | 
159  | 0  |           (r1->bottom == r2->bottom))  | 
160  | 0  |              ? TRUE  | 
161  | 0  |              : FALSE;  | 
162  | 0  | }  | 
163  |  |  | 
164  |  | BOOL rectangles_intersects(const RECTANGLE_16* r1, const RECTANGLE_16* r2)  | 
165  | 0  | { | 
166  | 0  |   RECTANGLE_16 tmp = { 0 }; | 
167  | 0  |   return rectangles_intersection(r1, r2, &tmp);  | 
168  | 0  | }  | 
169  |  |  | 
170  |  | BOOL rectangles_intersection(const RECTANGLE_16* r1, const RECTANGLE_16* r2, RECTANGLE_16* dst)  | 
171  | 0  | { | 
172  | 0  |   dst->left = MAX(r1->left, r2->left);  | 
173  | 0  |   dst->right = MIN(r1->right, r2->right);  | 
174  | 0  |   dst->top = MAX(r1->top, r2->top);  | 
175  | 0  |   dst->bottom = MIN(r1->bottom, r2->bottom);  | 
176  | 0  |   return (dst->left < dst->right) && (dst->top < dst->bottom);  | 
177  | 0  | }  | 
178  |  |  | 
179  |  | void region16_clear(REGION16* region)  | 
180  | 919  | { | 
181  | 919  |   WINPR_ASSERT(region);  | 
182  | 919  |   WINPR_ASSERT(region->data);  | 
183  |  |  | 
184  | 919  |   if ((region->data->size > 0) && (region->data != &empty_region))  | 
185  | 0  |     free(region->data);  | 
186  |  |  | 
187  | 919  |   region->data = &empty_region;  | 
188  | 919  |   ZeroMemory(®ion->extents, sizeof(region->extents));  | 
189  | 919  | }  | 
190  |  |  | 
191  |  | static INLINE REGION16_DATA* allocateRegion(long nbItems)  | 
192  | 18  | { | 
193  | 18  |   long allocSize = sizeof(REGION16_DATA) + (nbItems * sizeof(RECTANGLE_16));  | 
194  | 18  |   REGION16_DATA* ret = (REGION16_DATA*)malloc(allocSize);  | 
195  |  |  | 
196  | 18  |   if (!ret)  | 
197  | 0  |     return ret;  | 
198  |  |  | 
199  | 18  |   ret->size = allocSize;  | 
200  | 18  |   ret->nbRects = nbItems;  | 
201  | 18  |   return ret;  | 
202  | 18  | }  | 
203  |  |  | 
204  |  | BOOL region16_copy(REGION16* dst, const REGION16* src)  | 
205  | 0  | { | 
206  | 0  |   WINPR_ASSERT(dst);  | 
207  | 0  |   WINPR_ASSERT(dst->data);  | 
208  | 0  |   WINPR_ASSERT(src);  | 
209  | 0  |   WINPR_ASSERT(src->data);  | 
210  |  |  | 
211  | 0  |   if (dst == src)  | 
212  | 0  |     return TRUE;  | 
213  |  |  | 
214  | 0  |   dst->extents = src->extents;  | 
215  |  | 
  | 
216  | 0  |   if ((dst->data->size > 0) && (dst->data != &empty_region))  | 
217  | 0  |     free(dst->data);  | 
218  |  | 
  | 
219  | 0  |   if (src->data->size == 0)  | 
220  | 0  |     dst->data = &empty_region;  | 
221  | 0  |   else  | 
222  | 0  |   { | 
223  | 0  |     dst->data = allocateRegion(src->data->nbRects);  | 
224  |  | 
  | 
225  | 0  |     if (!dst->data)  | 
226  | 0  |       return FALSE;  | 
227  |  |  | 
228  | 0  |     CopyMemory(dst->data, src->data, src->data->size);  | 
229  | 0  |   }  | 
230  |  |  | 
231  | 0  |   return TRUE;  | 
232  | 0  | }  | 
233  |  |  | 
234  |  | void region16_print(const REGION16* region)  | 
235  | 919  | { | 
236  | 919  |   const RECTANGLE_16* rects = NULL;  | 
237  | 919  |   UINT32 nbRects = 0;  | 
238  | 919  |   int currentBandY = -1;  | 
239  | 919  |   rects = region16_rects(region, &nbRects);  | 
240  | 919  |   WLog_DBG(TAG, "nrects=%" PRIu32 "", nbRects);  | 
241  |  |  | 
242  | 919  |   for (UINT32 i = 0; i < nbRects; i++, rects++)  | 
243  | 0  |   { | 
244  | 0  |     if (rects->top != currentBandY)  | 
245  | 0  |     { | 
246  | 0  |       currentBandY = rects->top;  | 
247  | 0  |       WLog_DBG(TAG, "band %d: ", currentBandY);  | 
248  | 0  |     }  | 
249  |  | 
  | 
250  | 0  |     WLog_DBG(TAG, "(%" PRIu16 ",%" PRIu16 "-%" PRIu16 ",%" PRIu16 ")", rects->left, rects->top,  | 
251  | 0  |              rects->right, rects->bottom);  | 
252  | 0  |   }  | 
253  | 919  | }  | 
254  |  |  | 
255  |  | static void region16_copy_band_with_union(RECTANGLE_16* dst, const RECTANGLE_16* src,  | 
256  |  |                                           const RECTANGLE_16* end, UINT16 newTop, UINT16 newBottom,  | 
257  |  |                                           const RECTANGLE_16* unionRect, UINT32* dstCounter,  | 
258  |  |                                           const RECTANGLE_16** srcPtr, RECTANGLE_16** dstPtr)  | 
259  | 9  | { | 
260  | 9  |   UINT16 refY = src->top;  | 
261  | 9  |   const RECTANGLE_16* startOverlap = NULL;  | 
262  | 9  |   const RECTANGLE_16* endOverlap = NULL;  | 
263  |  |  | 
264  |  |   /* merges a band with the given rect  | 
265  |  |    * Input:  | 
266  |  |    *                   unionRect  | 
267  |  |    *               |               |  | 
268  |  |    *               |               |  | 
269  |  |    * ==============+===============+================================  | 
270  |  |    *   |Item1|  |Item2| |Item3|  |Item4|    |Item5|            Band  | 
271  |  |    * ==============+===============+================================  | 
272  |  |    *    before     |    overlap    |          after  | 
273  |  |    *  | 
274  |  |    * Resulting band:  | 
275  |  |    *   +-----+  +----------------------+    +-----+  | 
276  |  |    *   |Item1|  |      Item2           |    |Item3|  | 
277  |  |    *   +-----+  +----------------------+    +-----+  | 
278  |  |    *  | 
279  |  |    *  We first copy as-is items that are before Item2, the first overlapping  | 
280  |  |    *  item.  | 
281  |  |    *  Then we find the last one that overlap unionRect to agregate Item2, Item3  | 
282  |  |    *  and Item4 to create Item2.  | 
283  |  |    *  Finally Item5 is copied as Item3.  | 
284  |  |    *  | 
285  |  |    *  When no unionRect is provided, we skip the two first steps to just copy items  | 
286  |  |    */  | 
287  |  |  | 
288  | 9  |   if (unionRect)  | 
289  | 0  |   { | 
290  |  |     /* items before unionRect */  | 
291  | 0  |     while ((src < end) && (src->top == refY) && (src->right < unionRect->left))  | 
292  | 0  |     { | 
293  | 0  |       dst->top = newTop;  | 
294  | 0  |       dst->bottom = newBottom;  | 
295  | 0  |       dst->right = src->right;  | 
296  | 0  |       dst->left = src->left;  | 
297  | 0  |       src++;  | 
298  | 0  |       dst++;  | 
299  | 0  |       *dstCounter += 1;  | 
300  | 0  |     }  | 
301  |  |  | 
302  |  |     /* treat items overlapping with unionRect */  | 
303  | 0  |     startOverlap = unionRect;  | 
304  | 0  |     endOverlap = unionRect;  | 
305  |  | 
  | 
306  | 0  |     if ((src < end) && (src->top == refY) && (src->left < unionRect->left))  | 
307  | 0  |       startOverlap = src;  | 
308  |  | 
  | 
309  | 0  |     while ((src < end) && (src->top == refY) && (src->right < unionRect->right))  | 
310  | 0  |     { | 
311  | 0  |       src++;  | 
312  | 0  |     }  | 
313  |  | 
  | 
314  | 0  |     if ((src < end) && (src->top == refY) && (src->left < unionRect->right))  | 
315  | 0  |     { | 
316  | 0  |       endOverlap = src;  | 
317  | 0  |       src++;  | 
318  | 0  |     }  | 
319  |  | 
  | 
320  | 0  |     dst->bottom = newBottom;  | 
321  | 0  |     dst->top = newTop;  | 
322  | 0  |     dst->left = startOverlap->left;  | 
323  | 0  |     dst->right = endOverlap->right;  | 
324  | 0  |     dst++;  | 
325  | 0  |     *dstCounter += 1;  | 
326  | 0  |   }  | 
327  |  |  | 
328  |  |   /* treat remaining items on the same band */  | 
329  | 18  |   while ((src < end) && (src->top == refY))  | 
330  | 9  |   { | 
331  | 9  |     dst->top = newTop;  | 
332  | 9  |     dst->bottom = newBottom;  | 
333  | 9  |     dst->right = src->right;  | 
334  | 9  |     dst->left = src->left;  | 
335  | 9  |     src++;  | 
336  | 9  |     dst++;  | 
337  | 9  |     *dstCounter += 1;  | 
338  | 9  |   }  | 
339  |  |  | 
340  | 9  |   if (srcPtr)  | 
341  | 9  |     *srcPtr = src;  | 
342  |  |  | 
343  | 9  |   *dstPtr = dst;  | 
344  | 9  | }  | 
345  |  |  | 
346  |  | static RECTANGLE_16* next_band(RECTANGLE_16* band1, RECTANGLE_16* endPtr, int* nbItems)  | 
347  | 0  | { | 
348  | 0  |   UINT16 refY = band1->top;  | 
349  | 0  |   *nbItems = 0;  | 
350  |  | 
  | 
351  | 0  |   while ((band1 < endPtr) && (band1->top == refY))  | 
352  | 0  |   { | 
353  | 0  |     band1++;  | 
354  | 0  |     *nbItems += 1;  | 
355  | 0  |   }  | 
356  |  | 
  | 
357  | 0  |   return band1;  | 
358  | 0  | }  | 
359  |  |  | 
360  |  | static BOOL band_match(const RECTANGLE_16* band1, const RECTANGLE_16* band2,  | 
361  |  |                        const RECTANGLE_16* endPtr)  | 
362  | 0  | { | 
363  | 0  |   int refBand2 = band2->top;  | 
364  | 0  |   const RECTANGLE_16* band2Start = band2;  | 
365  |  | 
  | 
366  | 0  |   while ((band1 < band2Start) && (band2 < endPtr) && (band2->top == refBand2))  | 
367  | 0  |   { | 
368  | 0  |     if ((band1->left != band2->left) || (band1->right != band2->right))  | 
369  | 0  |       return FALSE;  | 
370  |  |  | 
371  | 0  |     band1++;  | 
372  | 0  |     band2++;  | 
373  | 0  |   }  | 
374  |  |  | 
375  | 0  |   if (band1 != band2Start)  | 
376  | 0  |     return FALSE;  | 
377  |  |  | 
378  | 0  |   return (band2 == endPtr) || (band2->top != refBand2);  | 
379  | 0  | }  | 
380  |  |  | 
381  |  | /** compute if the rectangle is fully included in the band  | 
382  |  |  * @param band a pointer on the beginning of the band  | 
383  |  |  * @param endPtr end of the region  | 
384  |  |  * @param rect the rectangle to test  | 
385  |  |  * @return if rect is fully included in an item of the band  | 
386  |  |  */  | 
387  |  | static BOOL rectangle_contained_in_band(const RECTANGLE_16* band, const RECTANGLE_16* endPtr,  | 
388  |  |                                         const RECTANGLE_16* rect)  | 
389  | 0  | { | 
390  | 0  |   UINT16 refY = band->top;  | 
391  |  | 
  | 
392  | 0  |   if ((band->top > rect->top) || (rect->bottom > band->bottom))  | 
393  | 0  |     return FALSE;  | 
394  |  |  | 
395  |  |   /* note: as the band is sorted from left to right, once we've seen an item  | 
396  |  |    *    that is after rect->left we're sure that the result is False.  | 
397  |  |    */  | 
398  | 0  |   while ((band < endPtr) && (band->top == refY) && (band->left <= rect->left))  | 
399  | 0  |   { | 
400  | 0  |     if (rect->right <= band->right)  | 
401  | 0  |       return TRUE;  | 
402  |  |  | 
403  | 0  |     band++;  | 
404  | 0  |   }  | 
405  |  |  | 
406  | 0  |   return FALSE;  | 
407  | 0  | }  | 
408  |  |  | 
409  |  | static BOOL region16_simplify_bands(REGION16* region)  | 
410  | 9  | { | 
411  |  |   /** Simplify consecutive bands that touch and have the same items  | 
412  |  |    *  | 
413  |  |    *  ====================          ====================  | 
414  |  |    *     | 1 |  | 2   |               |   |  |     |  | 
415  |  |    *  ====================            |   |  |     |  | 
416  |  |    *     | 1 |  | 2   |    ====>    | 1 |  |  2  |  | 
417  |  |    *  ====================            |   |  |     |  | 
418  |  |    *     | 1 |  | 2   |               |   |  |     |  | 
419  |  |    *  ====================          ====================  | 
420  |  |    *  | 
421  |  |    */  | 
422  | 9  |   RECTANGLE_16* endBand = NULL;  | 
423  | 9  |   int nbRects = 0;  | 
424  | 9  |   int finalNbRects = 0;  | 
425  | 9  |   int bandItems = 0;  | 
426  | 9  |   int toMove = 0;  | 
427  | 9  |   finalNbRects = nbRects = region16_n_rects(region);  | 
428  |  |  | 
429  | 9  |   if (nbRects < 2)  | 
430  | 9  |     return TRUE;  | 
431  |  |  | 
432  | 0  |   RECTANGLE_16* band1 = region16_rects_noconst(region);  | 
433  | 0  |   RECTANGLE_16* endPtr = band1 + nbRects;  | 
434  |  | 
  | 
435  | 0  |   do  | 
436  | 0  |   { | 
437  | 0  |     RECTANGLE_16* band2 = next_band(band1, endPtr, &bandItems);  | 
438  |  | 
  | 
439  | 0  |     if (band2 == endPtr)  | 
440  | 0  |       break;  | 
441  |  |  | 
442  | 0  |     if ((band1->bottom == band2->top) && band_match(band1, band2, endPtr))  | 
443  | 0  |     { | 
444  |  |       /* adjust the bottom of band1 items */  | 
445  | 0  |       RECTANGLE_16* tmp = band1;  | 
446  |  | 
  | 
447  | 0  |       while (tmp < band2)  | 
448  | 0  |       { | 
449  | 0  |         tmp->bottom = band2->bottom;  | 
450  | 0  |         tmp++;  | 
451  | 0  |       }  | 
452  |  |  | 
453  |  |       /* override band2, we don't move band1 pointer as the band after band2  | 
454  |  |        * may be merged too */  | 
455  | 0  |       endBand = band2 + bandItems;  | 
456  | 0  |       toMove = (endPtr - endBand) * sizeof(RECTANGLE_16);  | 
457  |  | 
  | 
458  | 0  |       if (toMove)  | 
459  | 0  |         MoveMemory(band2, endBand, toMove);  | 
460  |  | 
  | 
461  | 0  |       finalNbRects -= bandItems;  | 
462  | 0  |       endPtr -= bandItems;  | 
463  | 0  |     }  | 
464  | 0  |     else  | 
465  | 0  |     { | 
466  | 0  |       band1 = band2;  | 
467  | 0  |     }  | 
468  | 0  |   } while (TRUE);  | 
469  |  |  | 
470  | 0  |   if (finalNbRects != nbRects)  | 
471  | 0  |   { | 
472  | 0  |     size_t allocSize = sizeof(REGION16_DATA) + (finalNbRects * sizeof(RECTANGLE_16));  | 
473  | 0  |     REGION16_DATA* data = realloc(region->data, allocSize);  | 
474  | 0  |     if (!data)  | 
475  | 0  |       free(region->data);  | 
476  | 0  |     region->data = data;  | 
477  |  | 
  | 
478  | 0  |     if (!region->data)  | 
479  | 0  |     { | 
480  | 0  |       region->data = &empty_region;  | 
481  | 0  |       return FALSE;  | 
482  | 0  |     }  | 
483  |  |  | 
484  | 0  |     region->data->nbRects = finalNbRects;  | 
485  | 0  |     region->data->size = allocSize;  | 
486  | 0  |   }  | 
487  |  |  | 
488  | 0  |   return TRUE;  | 
489  | 0  | }  | 
490  |  |  | 
491  |  | BOOL region16_union_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* rect)  | 
492  | 18  | { | 
493  | 18  |   const RECTANGLE_16* srcExtents = NULL;  | 
494  | 18  |   RECTANGLE_16* dstExtents = NULL;  | 
495  | 18  |   const RECTANGLE_16* currentBand = NULL;  | 
496  | 18  |   const RECTANGLE_16* endSrcRect = NULL;  | 
497  | 18  |   const RECTANGLE_16* nextBand = NULL;  | 
498  | 18  |   REGION16_DATA* newItems = NULL;  | 
499  | 18  |   REGION16_DATA* tmpItems = NULL;  | 
500  | 18  |   RECTANGLE_16* dstRect = NULL;  | 
501  | 18  |   UINT32 usedRects = 0;  | 
502  | 18  |   UINT32 srcNbRects = 0;  | 
503  | 18  |   UINT16 topInterBand = 0;  | 
504  | 18  |   WINPR_ASSERT(src);  | 
505  | 18  |   WINPR_ASSERT(dst);  | 
506  | 18  |   srcExtents = region16_extents(src);  | 
507  | 18  |   dstExtents = region16_extents_noconst(dst);  | 
508  |  |  | 
509  | 18  |   if (!region16_n_rects(src))  | 
510  | 9  |   { | 
511  |  |     /* source is empty, so the union is rect */  | 
512  | 9  |     dst->extents = *rect;  | 
513  | 9  |     dst->data = allocateRegion(1);  | 
514  |  |  | 
515  | 9  |     if (!dst->data)  | 
516  | 0  |       return FALSE;  | 
517  |  |  | 
518  | 9  |     dstRect = region16_rects_noconst(dst);  | 
519  | 9  |     dstRect->top = rect->top;  | 
520  | 9  |     dstRect->left = rect->left;  | 
521  | 9  |     dstRect->right = rect->right;  | 
522  | 9  |     dstRect->bottom = rect->bottom;  | 
523  | 9  |     return TRUE;  | 
524  | 9  |   }  | 
525  |  |  | 
526  | 9  |   newItems = allocateRegion((1 + region16_n_rects(src)) * 4);  | 
527  |  |  | 
528  | 9  |   if (!newItems)  | 
529  | 0  |     return FALSE;  | 
530  |  |  | 
531  | 9  |   dstRect = (RECTANGLE_16*)(&newItems[1]);  | 
532  | 9  |   usedRects = 0;  | 
533  |  |  | 
534  |  |   /* adds the piece of rect that is on the top of src */  | 
535  | 9  |   if (rect->top < srcExtents->top)  | 
536  | 0  |   { | 
537  | 0  |     dstRect->top = rect->top;  | 
538  | 0  |     dstRect->left = rect->left;  | 
539  | 0  |     dstRect->right = rect->right;  | 
540  | 0  |     dstRect->bottom = MIN(srcExtents->top, rect->bottom);  | 
541  | 0  |     usedRects++;  | 
542  | 0  |     dstRect++;  | 
543  | 0  |   }  | 
544  |  |  | 
545  |  |   /* treat possibly overlapping region */  | 
546  | 9  |   currentBand = region16_rects(src, &srcNbRects);  | 
547  | 9  |   endSrcRect = currentBand + srcNbRects;  | 
548  |  |  | 
549  | 18  |   while (currentBand < endSrcRect)  | 
550  | 9  |   { | 
551  | 9  |     if ((currentBand->bottom <= rect->top) || (rect->bottom <= currentBand->top) ||  | 
552  | 9  |         rectangle_contained_in_band(currentBand, endSrcRect, rect))  | 
553  | 9  |     { | 
554  |  |       /* no overlap between rect and the band, rect is totally below or totally above  | 
555  |  |        * the current band, or rect is already covered by an item of the band.  | 
556  |  |        * let's copy all the rectangles from this band  | 
557  |  |                   +----+  | 
558  |  |                   |    |   rect (case 1)  | 
559  |  |                   +----+  | 
560  |  |  | 
561  |  |          =================  | 
562  |  |       band of srcRect  | 
563  |  |        =================  | 
564  |  |               +----+  | 
565  |  |               |    |   rect (case 2)  | 
566  |  |               +----+  | 
567  |  |       */  | 
568  | 9  |       region16_copy_band_with_union(dstRect, currentBand, endSrcRect, currentBand->top,  | 
569  | 9  |                                     currentBand->bottom, NULL, &usedRects, &nextBand,  | 
570  | 9  |                                     &dstRect);  | 
571  | 9  |       topInterBand = rect->top;  | 
572  | 9  |     }  | 
573  | 0  |     else  | 
574  | 0  |     { | 
575  |  |       /* rect overlaps the band:  | 
576  |  |                  |    |  |    |  | 
577  |  |       ====^=================|    |==|    |=========================== band  | 
578  |  |       |   top split     |    |  |    |  | 
579  |  |       v                 | 1  |  | 2  |  | 
580  |  |       ^                 |    |  |    |  +----+   +----+  | 
581  |  |       |   merge zone    |    |  |    |  |    |   | 4  |  | 
582  |  |       v                 +----+  |    |  |    |   +----+  | 
583  |  |       ^                         |    |  | 3  |  | 
584  |  |       |   bottom split          |    |  |    |  | 
585  |  |       ====v=========================|    |==|    |===================  | 
586  |  |                  |    |  |    |  | 
587  |  |  | 
588  |  |        possible cases:  | 
589  |  |        1) no top split, merge zone then a bottom split. The band will be splitted  | 
590  |  |         in two  | 
591  |  |        2) not band split, only the merge zone, band merged with rect but not splitted  | 
592  |  |        3) a top split, the merge zone and no bottom split. The band will be split  | 
593  |  |        in two  | 
594  |  |        4) a top split, the merge zone and also a bottom split. The band will be  | 
595  |  |        splitted in 3, but the coalesce algorithm may merge the created bands  | 
596  |  |        */  | 
597  | 0  |       UINT16 mergeTop = currentBand->top;  | 
598  | 0  |       UINT16 mergeBottom = currentBand->bottom;  | 
599  |  |  | 
600  |  |       /* test if we need a top split, case 3 and 4 */  | 
601  | 0  |       if (rect->top > currentBand->top)  | 
602  | 0  |       { | 
603  | 0  |         region16_copy_band_with_union(dstRect, currentBand, endSrcRect, currentBand->top,  | 
604  | 0  |                                       rect->top, NULL, &usedRects, &nextBand, &dstRect);  | 
605  | 0  |         mergeTop = rect->top;  | 
606  | 0  |       }  | 
607  |  |  | 
608  |  |       /* do the merge zone (all cases) */  | 
609  | 0  |       if (rect->bottom < currentBand->bottom)  | 
610  | 0  |         mergeBottom = rect->bottom;  | 
611  |  | 
  | 
612  | 0  |       region16_copy_band_with_union(dstRect, currentBand, endSrcRect, mergeTop, mergeBottom,  | 
613  | 0  |                                     rect, &usedRects, &nextBand, &dstRect);  | 
614  |  |  | 
615  |  |       /* test if we need a bottom split, case 1 and 4 */  | 
616  | 0  |       if (rect->bottom < currentBand->bottom)  | 
617  | 0  |       { | 
618  | 0  |         region16_copy_band_with_union(dstRect, currentBand, endSrcRect, mergeBottom,  | 
619  | 0  |                                       currentBand->bottom, NULL, &usedRects, &nextBand,  | 
620  | 0  |                                       &dstRect);  | 
621  | 0  |       }  | 
622  |  | 
  | 
623  | 0  |       topInterBand = currentBand->bottom;  | 
624  | 0  |     }  | 
625  |  |  | 
626  |  |     /* test if a piece of rect should be inserted as a new band between  | 
627  |  |      * the current band and the next one. band n and n+1 shouldn't touch.  | 
628  |  |      *  | 
629  |  |      * ==============================================================  | 
630  |  |      *                                                        band n  | 
631  |  |      *            +------+                    +------+  | 
632  |  |      * ===========| rect |====================|      |===============  | 
633  |  |      *            |      |    +------+        |      |  | 
634  |  |      *            +------+    | rect |        | rect |  | 
635  |  |      *                        +------+        |      |  | 
636  |  |      * =======================================|      |================  | 
637  |  |      *                                        +------+         band n+1  | 
638  |  |      * ===============================================================  | 
639  |  |      *  | 
640  |  |      */  | 
641  | 9  |     if ((nextBand < endSrcRect) && (nextBand->top != currentBand->bottom) &&  | 
642  | 9  |         (rect->bottom > currentBand->bottom) && (rect->top < nextBand->top))  | 
643  | 0  |     { | 
644  | 0  |       dstRect->right = rect->right;  | 
645  | 0  |       dstRect->left = rect->left;  | 
646  | 0  |       dstRect->top = topInterBand;  | 
647  | 0  |       dstRect->bottom = MIN(nextBand->top, rect->bottom);  | 
648  | 0  |       dstRect++;  | 
649  | 0  |       usedRects++;  | 
650  | 0  |     }  | 
651  |  |  | 
652  | 9  |     currentBand = nextBand;  | 
653  | 9  |   }  | 
654  |  |  | 
655  |  |   /* adds the piece of rect that is below src */  | 
656  | 9  |   if (srcExtents->bottom < rect->bottom)  | 
657  | 0  |   { | 
658  | 0  |     dstRect->top = MAX(srcExtents->bottom, rect->top);  | 
659  | 0  |     dstRect->left = rect->left;  | 
660  | 0  |     dstRect->right = rect->right;  | 
661  | 0  |     dstRect->bottom = rect->bottom;  | 
662  | 0  |     usedRects++;  | 
663  | 0  |     dstRect++;  | 
664  | 0  |   }  | 
665  |  |  | 
666  | 9  |   if ((src == dst) && (dst->data != &empty_region))  | 
667  | 9  |     free(dst->data);  | 
668  |  |  | 
669  | 9  |   dstExtents->top = MIN(rect->top, srcExtents->top);  | 
670  | 9  |   dstExtents->left = MIN(rect->left, srcExtents->left);  | 
671  | 9  |   dstExtents->bottom = MAX(rect->bottom, srcExtents->bottom);  | 
672  | 9  |   dstExtents->right = MAX(rect->right, srcExtents->right);  | 
673  | 9  |   newItems->size = sizeof(REGION16_DATA) + (usedRects * sizeof(RECTANGLE_16));  | 
674  | 9  |   tmpItems = realloc(newItems, newItems->size);  | 
675  | 9  |   if (!tmpItems)  | 
676  | 0  |     free(newItems);  | 
677  | 9  |   newItems = tmpItems;  | 
678  | 9  |   dst->data = newItems;  | 
679  |  |  | 
680  | 9  |   if (!dst->data)  | 
681  | 0  |     return FALSE;  | 
682  |  |  | 
683  | 9  |   dst->data->nbRects = usedRects;  | 
684  | 9  |   return region16_simplify_bands(dst);  | 
685  | 9  | }  | 
686  |  |  | 
687  |  | BOOL region16_intersects_rect(const REGION16* src, const RECTANGLE_16* arg2)  | 
688  | 0  | { | 
689  | 0  |   const RECTANGLE_16* rect = NULL;  | 
690  | 0  |   const RECTANGLE_16* endPtr = NULL;  | 
691  | 0  |   const RECTANGLE_16* srcExtents = NULL;  | 
692  | 0  |   UINT32 nbRects = 0;  | 
693  |  | 
  | 
694  | 0  |   if (!src || !src->data || !arg2)  | 
695  | 0  |     return FALSE;  | 
696  |  |  | 
697  | 0  |   rect = region16_rects(src, &nbRects);  | 
698  |  | 
  | 
699  | 0  |   if (!nbRects)  | 
700  | 0  |     return FALSE;  | 
701  |  |  | 
702  | 0  |   srcExtents = region16_extents(src);  | 
703  |  | 
  | 
704  | 0  |   if (nbRects == 1)  | 
705  | 0  |     return rectangles_intersects(srcExtents, arg2);  | 
706  |  |  | 
707  | 0  |   if (!rectangles_intersects(srcExtents, arg2))  | 
708  | 0  |     return FALSE;  | 
709  |  |  | 
710  | 0  |   for (endPtr = rect + nbRects; (rect < endPtr) && (arg2->bottom > rect->top); rect++)  | 
711  | 0  |   { | 
712  | 0  |     if (rectangles_intersects(rect, arg2))  | 
713  | 0  |       return TRUE;  | 
714  | 0  |   }  | 
715  |  |  | 
716  | 0  |   return FALSE;  | 
717  | 0  | }  | 
718  |  |  | 
719  |  | BOOL region16_intersect_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* rect)  | 
720  | 0  | { | 
721  | 0  |   REGION16_DATA* newItems = NULL;  | 
722  | 0  |   const RECTANGLE_16* srcPtr = NULL;  | 
723  | 0  |   const RECTANGLE_16* endPtr = NULL;  | 
724  | 0  |   const RECTANGLE_16* srcExtents = NULL;  | 
725  | 0  |   RECTANGLE_16* dstPtr = NULL;  | 
726  | 0  |   UINT32 nbRects = 0;  | 
727  | 0  |   UINT32 usedRects = 0;  | 
728  | 0  |   RECTANGLE_16 common;  | 
729  | 0  |   RECTANGLE_16 newExtents;  | 
730  | 0  |   WINPR_ASSERT(src);  | 
731  | 0  |   WINPR_ASSERT(src->data);  | 
732  | 0  |   srcPtr = region16_rects(src, &nbRects);  | 
733  |  | 
  | 
734  | 0  |   if (!nbRects)  | 
735  | 0  |   { | 
736  | 0  |     region16_clear(dst);  | 
737  | 0  |     return TRUE;  | 
738  | 0  |   }  | 
739  |  |  | 
740  | 0  |   srcExtents = region16_extents(src);  | 
741  |  | 
  | 
742  | 0  |   if (nbRects == 1)  | 
743  | 0  |   { | 
744  | 0  |     BOOL intersects = rectangles_intersection(srcExtents, rect, &common);  | 
745  | 0  |     region16_clear(dst);  | 
746  |  | 
  | 
747  | 0  |     if (intersects)  | 
748  | 0  |       return region16_union_rect(dst, dst, &common);  | 
749  |  |  | 
750  | 0  |     return TRUE;  | 
751  | 0  |   }  | 
752  |  |  | 
753  | 0  |   newItems = allocateRegion(nbRects);  | 
754  |  | 
  | 
755  | 0  |   if (!newItems)  | 
756  | 0  |     return FALSE;  | 
757  |  |  | 
758  | 0  |   dstPtr = (RECTANGLE_16*)(&newItems[1]);  | 
759  | 0  |   usedRects = 0;  | 
760  | 0  |   ZeroMemory(&newExtents, sizeof(newExtents));  | 
761  |  |  | 
762  |  |   /* accumulate intersecting rectangles, the final region16_simplify_bands() will  | 
763  |  |    * do all the bad job to recreate correct rectangles  | 
764  |  |    */  | 
765  | 0  |   for (endPtr = srcPtr + nbRects; (srcPtr < endPtr) && (rect->bottom > srcPtr->top); srcPtr++)  | 
766  | 0  |   { | 
767  | 0  |     if (rectangles_intersection(srcPtr, rect, &common))  | 
768  | 0  |     { | 
769  | 0  |       *dstPtr = common;  | 
770  | 0  |       usedRects++;  | 
771  | 0  |       dstPtr++;  | 
772  |  | 
  | 
773  | 0  |       if (rectangle_is_empty(&newExtents))  | 
774  | 0  |       { | 
775  |  |         /* Check if the existing newExtents is empty. If it is empty, use  | 
776  |  |          * new common directly. We do not need to check common rectangle  | 
777  |  |          * because the rectangles_intersection() ensures that it is not empty.  | 
778  |  |          */  | 
779  | 0  |         newExtents = common;  | 
780  | 0  |       }  | 
781  | 0  |       else  | 
782  | 0  |       { | 
783  | 0  |         newExtents.top = MIN(common.top, newExtents.top);  | 
784  | 0  |         newExtents.left = MIN(common.left, newExtents.left);  | 
785  | 0  |         newExtents.bottom = MAX(common.bottom, newExtents.bottom);  | 
786  | 0  |         newExtents.right = MAX(common.right, newExtents.right);  | 
787  | 0  |       }  | 
788  | 0  |     }  | 
789  | 0  |   }  | 
790  |  | 
  | 
791  | 0  |   newItems->nbRects = usedRects;  | 
792  | 0  |   newItems->size = sizeof(REGION16_DATA) + (usedRects * sizeof(RECTANGLE_16));  | 
793  |  | 
  | 
794  | 0  |   if ((dst->data->size > 0) && (dst->data != &empty_region))  | 
795  | 0  |     free(dst->data);  | 
796  |  | 
  | 
797  | 0  |   dst->data = realloc(newItems, newItems->size);  | 
798  |  | 
  | 
799  | 0  |   if (!dst->data)  | 
800  | 0  |   { | 
801  | 0  |     free(newItems);  | 
802  | 0  |     return FALSE;  | 
803  | 0  |   }  | 
804  |  |  | 
805  | 0  |   dst->extents = newExtents;  | 
806  | 0  |   return region16_simplify_bands(dst);  | 
807  | 0  | }  | 
808  |  |  | 
809  |  | void region16_uninit(REGION16* region)  | 
810  | 13.3k  | { | 
811  | 13.3k  |   WINPR_ASSERT(region);  | 
812  |  |  | 
813  | 13.3k  |   if (region->data)  | 
814  | 13.3k  |   { | 
815  | 13.3k  |     if ((region->data->size > 0) && (region->data != &empty_region))  | 
816  | 9  |       free(region->data);  | 
817  |  |  | 
818  | 13.3k  |     region->data = NULL;  | 
819  | 13.3k  |   }  | 
820  | 13.3k  | }  |