/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 | } |