/src/FreeRDP/libfreerdp/codec/region.c
Line | Count | Source |
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 | | size_t nbRects; |
74 | | RECTANGLE_16* rects; |
75 | | }; |
76 | | |
77 | | void region16_init(REGION16* region) |
78 | 13.4k | { |
79 | 13.4k | WINPR_ASSERT(region); |
80 | | |
81 | 13.4k | const REGION16 empty = WINPR_C_ARRAY_INIT; |
82 | 13.4k | *region = empty; |
83 | 13.4k | } |
84 | | |
85 | | int region16_n_rects(const REGION16* region) |
86 | 20.3k | { |
87 | 20.3k | WINPR_ASSERT(region); |
88 | 20.3k | if (!region->data) |
89 | 301 | return 0; |
90 | | |
91 | 20.0k | return WINPR_ASSERTING_INT_CAST(int, region->data->nbRects); |
92 | 20.3k | } |
93 | | |
94 | | const RECTANGLE_16* region16_rects(const REGION16* region, UINT32* nbRects) |
95 | 10.9k | { |
96 | 10.9k | if (nbRects) |
97 | 10.9k | *nbRects = 0; |
98 | | |
99 | 10.9k | if (!region) |
100 | 0 | return nullptr; |
101 | | |
102 | 10.9k | REGION16_DATA* data = region->data; |
103 | 10.9k | if (!data) |
104 | 875 | return nullptr; |
105 | | |
106 | 10.0k | if (nbRects) |
107 | 10.0k | *nbRects = WINPR_ASSERTING_INT_CAST(UINT32, data->nbRects); |
108 | | |
109 | 10.0k | return data->rects; |
110 | 10.9k | } |
111 | | |
112 | | static inline RECTANGLE_16* region16_rects_noconst(REGION16* region) |
113 | 9.97k | { |
114 | 9.97k | WINPR_ASSERT(region); |
115 | | |
116 | 9.97k | REGION16_DATA* data = region->data; |
117 | | |
118 | 9.97k | if (!data) |
119 | 0 | return nullptr; |
120 | | |
121 | 9.97k | return data->rects; |
122 | 9.97k | } |
123 | | |
124 | | const RECTANGLE_16* region16_extents(const REGION16* region) |
125 | 10.3k | { |
126 | 10.3k | if (!region) |
127 | 0 | return nullptr; |
128 | | |
129 | 10.3k | return ®ion->extents; |
130 | 10.3k | } |
131 | | |
132 | | static RECTANGLE_16* region16_extents_noconst(REGION16* region) |
133 | 10.3k | { |
134 | 10.3k | if (!region) |
135 | 0 | return nullptr; |
136 | | |
137 | 10.3k | return ®ion->extents; |
138 | 10.3k | } |
139 | | |
140 | | BOOL rectangle_is_empty(const RECTANGLE_16* rect) |
141 | 0 | { |
142 | 0 | WINPR_ASSERT(rect); |
143 | | |
144 | | /* A rectangle with width <= 0 or height <= 0 should be regarded |
145 | | * as empty. |
146 | | */ |
147 | 0 | return ((rect->left >= rect->right) || (rect->top >= rect->bottom)); |
148 | 0 | } |
149 | | |
150 | | BOOL region16_is_empty(const REGION16* region) |
151 | 0 | { |
152 | 0 | WINPR_ASSERT(region); |
153 | 0 | if (!region->data) |
154 | 0 | return TRUE; |
155 | 0 | return (region->data->nbRects == 0); |
156 | 0 | } |
157 | | |
158 | | BOOL rectangles_equal(const RECTANGLE_16* r1, const RECTANGLE_16* r2) |
159 | 0 | { |
160 | 0 | WINPR_ASSERT(r1); |
161 | 0 | WINPR_ASSERT(r2); |
162 | |
|
163 | 0 | return ((r1->left == r2->left) && (r1->top == r2->top) && (r1->right == r2->right) && |
164 | 0 | (r1->bottom == r2->bottom)); |
165 | 0 | } |
166 | | |
167 | | BOOL rectangles_intersects(const RECTANGLE_16* r1, const RECTANGLE_16* r2) |
168 | 0 | { |
169 | 0 | RECTANGLE_16 tmp = WINPR_C_ARRAY_INIT; |
170 | 0 | return rectangles_intersection(r1, r2, &tmp); |
171 | 0 | } |
172 | | |
173 | | BOOL rectangles_intersection(const RECTANGLE_16* r1, const RECTANGLE_16* r2, RECTANGLE_16* dst) |
174 | 0 | { |
175 | 0 | WINPR_ASSERT(r1); |
176 | 0 | WINPR_ASSERT(r2); |
177 | 0 | WINPR_ASSERT(dst); |
178 | |
|
179 | 0 | dst->left = MAX(r1->left, r2->left); |
180 | 0 | dst->right = MIN(r1->right, r2->right); |
181 | 0 | dst->top = MAX(r1->top, r2->top); |
182 | 0 | dst->bottom = MIN(r1->bottom, r2->bottom); |
183 | 0 | return (dst->left < dst->right) && (dst->top < dst->bottom); |
184 | 0 | } |
185 | | |
186 | | const char* rectangle_to_string(const RECTANGLE_16* rect, char* buffer, size_t length) |
187 | 0 | { |
188 | 0 | if (!buffer || (length < 2)) |
189 | 0 | return nullptr; |
190 | 0 | if (!rect) |
191 | 0 | (void)_snprintf(buffer, length - 1, "{ nullptr }"); |
192 | 0 | else |
193 | 0 | (void)_snprintf(buffer, length - 1, "{%" PRIu16 "x%" PRIu16 "-%" PRIu16 "x%" PRIu16 "}", |
194 | 0 | rect->left, rect->top, rect->right, rect->bottom); |
195 | 0 | return buffer; |
196 | 0 | } |
197 | | |
198 | | RECTANGLE_16* rectangles_clone(const RECTANGLE_16* rects, size_t count) |
199 | 0 | { |
200 | 0 | if (count == 0) |
201 | 0 | return nullptr; |
202 | 0 | if (count > SIZE_MAX / sizeof(RECTANGLE_16)) |
203 | 0 | return nullptr; |
204 | | |
205 | 0 | WINPR_ASSERT(rects); |
206 | |
|
207 | 0 | RECTANGLE_16* clone = calloc(count, sizeof(RECTANGLE_16)); |
208 | 0 | if (!clone) |
209 | 0 | return nullptr; |
210 | | |
211 | 0 | memcpy(clone, rects, sizeof(RECTANGLE_16) * count); |
212 | 0 | return clone; |
213 | 0 | } |
214 | | |
215 | | static void freeRegion(REGION16_DATA* data) |
216 | 24.3k | { |
217 | 24.3k | if (data) |
218 | 10.3k | free(data->rects); |
219 | 24.3k | free(data); |
220 | 24.3k | } |
221 | | |
222 | | void region16_clear(REGION16* region) |
223 | 875 | { |
224 | 875 | WINPR_ASSERT(region); |
225 | | |
226 | 875 | freeRegion(region->data); |
227 | 875 | region->data = nullptr; |
228 | | |
229 | 875 | const RECTANGLE_16 empty = WINPR_C_ARRAY_INIT; |
230 | 875 | region->extents = empty; |
231 | 875 | } |
232 | | |
233 | | WINPR_ATTR_MALLOC(freeRegion, 1) |
234 | | WINPR_ATTR_NODISCARD |
235 | | static REGION16_DATA* allocateRegion(size_t nbItems) |
236 | 10.3k | { |
237 | 10.3k | REGION16_DATA* data = calloc(1, sizeof(REGION16_DATA)); |
238 | 10.3k | if (!data) |
239 | 0 | return nullptr; |
240 | | |
241 | 10.3k | if (nbItems > 0) |
242 | 10.3k | { |
243 | 10.3k | data->rects = calloc(nbItems, sizeof(RECTANGLE_16)); |
244 | 10.3k | if (!data->rects) |
245 | 0 | { |
246 | 0 | free(data); |
247 | 0 | return nullptr; |
248 | 0 | } |
249 | 10.3k | } |
250 | | |
251 | 10.3k | data->nbRects = nbItems; |
252 | 10.3k | return data; |
253 | 10.3k | } |
254 | | |
255 | | static inline RECTANGLE_16* nextRect(REGION16_DATA* data, size_t index) |
256 | 38.2M | { |
257 | 38.2M | WINPR_ASSERT(data); |
258 | 38.2M | if (index + 1 > data->nbRects) |
259 | 533k | { |
260 | 533k | RECTANGLE_16* rects = realloc(data->rects, (index + 1) * sizeof(RECTANGLE_16)); |
261 | 533k | if (!rects) |
262 | 0 | { |
263 | 0 | freeRegion(data); |
264 | 0 | return nullptr; |
265 | 0 | } |
266 | | |
267 | 533k | const RECTANGLE_16 empty = WINPR_C_ARRAY_INIT; |
268 | 533k | rects[index] = empty; |
269 | 533k | data->nbRects = index + 1; |
270 | 533k | data->rects = rects; |
271 | 533k | } |
272 | 38.2M | return &data->rects[index]; |
273 | 38.2M | } |
274 | | |
275 | | static BOOL resizeRegion(REGION16* region, size_t nbItems) |
276 | 3.51k | { |
277 | 3.51k | WINPR_ASSERT(region); |
278 | 3.51k | if (nbItems == 0) |
279 | 0 | { |
280 | 0 | freeRegion(region->data); |
281 | 0 | region->data = nullptr; |
282 | 0 | return TRUE; |
283 | 0 | } |
284 | | |
285 | 3.51k | if (!region->data) |
286 | 301 | { |
287 | 301 | region->data = allocateRegion(nbItems); |
288 | 301 | return region->data != nullptr; |
289 | 301 | } |
290 | | |
291 | 3.21k | RECTANGLE_16* rects = realloc(region->data->rects, nbItems * sizeof(RECTANGLE_16)); |
292 | 3.21k | if (!rects) |
293 | 0 | { |
294 | 0 | free(region->data->rects); |
295 | 0 | region->data->nbRects = 0; |
296 | 0 | region->data->rects = nullptr; |
297 | 0 | return FALSE; |
298 | 0 | } |
299 | | |
300 | 3.21k | for (size_t x = region->data->nbRects; x < nbItems; x++) |
301 | 0 | { |
302 | 0 | const RECTANGLE_16 empty = WINPR_C_ARRAY_INIT; |
303 | 0 | rects[x] = empty; |
304 | 0 | } |
305 | 3.21k | region->data->rects = rects; |
306 | 3.21k | region->data->nbRects = nbItems; |
307 | 3.21k | return TRUE; |
308 | 3.21k | } |
309 | | |
310 | | static inline BOOL region16_copy_data(REGION16* dst, const REGION16* src) |
311 | 0 | { |
312 | 0 | WINPR_ASSERT(dst); |
313 | 0 | WINPR_ASSERT(src); |
314 | |
|
315 | 0 | freeRegion(dst->data); |
316 | 0 | dst->data = nullptr; |
317 | |
|
318 | 0 | if (src->data && (src->data->nbRects > 0)) |
319 | 0 | { |
320 | 0 | dst->data = allocateRegion(src->data->nbRects); |
321 | 0 | if (!dst->data || !dst->data->rects) |
322 | 0 | return FALSE; |
323 | 0 | memcpy(dst->data->rects, src->data->rects, dst->data->nbRects * sizeof(RECTANGLE_16)); |
324 | 0 | } |
325 | 0 | return TRUE; |
326 | 0 | } |
327 | | |
328 | | BOOL region16_copy(REGION16* dst, const REGION16* src) |
329 | 0 | { |
330 | 0 | if (dst == src) |
331 | 0 | return TRUE; |
332 | | |
333 | 0 | WINPR_ASSERT(dst); |
334 | 0 | WINPR_ASSERT(src); |
335 | |
|
336 | 0 | dst->extents = src->extents; |
337 | |
|
338 | 0 | return region16_copy_data(dst, src); |
339 | 0 | } |
340 | | |
341 | | void region16_print(const REGION16* region) |
342 | 875 | { |
343 | 875 | UINT32 nbRects = 0; |
344 | 875 | int currentBandY = -1; |
345 | 875 | const RECTANGLE_16* rects = region16_rects(region, &nbRects); |
346 | | |
347 | 875 | WLog_DBG(TAG, "nrects=%" PRIu32 "", nbRects); |
348 | | |
349 | 875 | for (UINT32 i = 0; i < nbRects; i++) |
350 | 0 | { |
351 | 0 | const RECTANGLE_16* rect = &rects[i]; |
352 | |
|
353 | 0 | if (rect->top != currentBandY) |
354 | 0 | { |
355 | 0 | currentBandY = rect->top; |
356 | 0 | WLog_DBG(TAG, "band %d: ", currentBandY); |
357 | 0 | } |
358 | |
|
359 | 0 | WLog_DBG(TAG, "(%" PRIu16 ",%" PRIu16 "-%" PRIu16 ",%" PRIu16 ")", rect->left, rect->top, |
360 | 0 | rect->right, rect->bottom); |
361 | 0 | } |
362 | 875 | } |
363 | | |
364 | | char* region16_to_string(const REGION16* region) |
365 | 0 | { |
366 | 0 | if (!region) |
367 | 0 | return _strdup("REGION16{nullptr}"); |
368 | | |
369 | 0 | UINT32 nbRects = 0; |
370 | 0 | const RECTANGLE_16* rects = region16_rects(region, &nbRects); |
371 | |
|
372 | 0 | char* str = nullptr; |
373 | 0 | size_t slen = 0; |
374 | 0 | winpr_asprintf(&str, &slen, "REGION16{nrects=%" PRIu32 " [", nbRects); |
375 | |
|
376 | 0 | UINT16 currentBandY = 0; |
377 | 0 | for (UINT32 i = 0; i < nbRects; i++) |
378 | 0 | { |
379 | 0 | const RECTANGLE_16* rect = &rects[i]; |
380 | |
|
381 | 0 | if (rect->top != currentBandY) |
382 | 0 | { |
383 | 0 | currentBandY = rect->top; |
384 | 0 | char* tmp = nullptr; |
385 | 0 | winpr_asprintf(&tmp, &slen, "%sband %" PRIu16 ":", str, currentBandY); |
386 | 0 | free(str); |
387 | 0 | str = tmp; |
388 | 0 | } |
389 | |
|
390 | 0 | char buffer[64] = WINPR_C_ARRAY_INIT; |
391 | 0 | char* tmp = nullptr; |
392 | 0 | winpr_asprintf(&tmp, &slen, "%s, %s", str, |
393 | 0 | rectangle_to_string(rect, buffer, sizeof(buffer))); |
394 | 0 | free(str); |
395 | 0 | str = tmp; |
396 | 0 | } |
397 | |
|
398 | 0 | char* tmp = nullptr; |
399 | 0 | winpr_asprintf(&tmp, &slen, "%s]}", str); |
400 | 0 | free(str); |
401 | 0 | return tmp; |
402 | 0 | } |
403 | | |
404 | | static BOOL region16_copy_band_with_union(REGION16_DATA* region, const RECTANGLE_16* src, |
405 | | const RECTANGLE_16* end, UINT16 newTop, UINT16 newBottom, |
406 | | const RECTANGLE_16* unionRect, UINT32* dstCounter, |
407 | | const RECTANGLE_16** srcPtr) |
408 | 2.47M | { |
409 | 2.47M | WINPR_ASSERT(region); |
410 | 2.47M | WINPR_ASSERT(src); |
411 | 2.47M | WINPR_ASSERT(end); |
412 | 2.47M | WINPR_ASSERT(dstCounter); |
413 | | |
414 | 2.47M | UINT16 refY = src->top; |
415 | | |
416 | | /* merges a band with the given rect |
417 | | * Input: |
418 | | * unionRect |
419 | | * | | |
420 | | * | | |
421 | | * ==============+===============+================================ |
422 | | * |Item1| |Item2| |Item3| |Item4| |Item5| Band |
423 | | * ==============+===============+================================ |
424 | | * before | overlap | after |
425 | | * |
426 | | * Resulting band: |
427 | | * +-----+ +----------------------+ +-----+ |
428 | | * |Item1| | Item2 | |Item3| |
429 | | * +-----+ +----------------------+ +-----+ |
430 | | * |
431 | | * We first copy as-is items that are before Item2, the first overlapping |
432 | | * item. |
433 | | * Then we find the last one that overlap unionRect to aggregate Item2, Item3 |
434 | | * and Item4 to create Item2. |
435 | | * Finally Item5 is copied as Item3. |
436 | | * |
437 | | * When no unionRect is provided, we skip the two first steps to just copy items |
438 | | */ |
439 | | |
440 | 2.47M | if (unionRect) |
441 | 470k | { |
442 | | /* items before unionRect */ |
443 | 5.97M | while ((src < end) && (src->top == refY) && (src->right < unionRect->left)) |
444 | 5.50M | { |
445 | 5.50M | RECTANGLE_16* dst = nextRect(region, (*dstCounter)++); |
446 | 5.50M | if (!dst) |
447 | 0 | return FALSE; |
448 | 5.50M | dst->top = newTop; |
449 | 5.50M | dst->bottom = newBottom; |
450 | 5.50M | dst->right = src->right; |
451 | 5.50M | dst->left = src->left; |
452 | 5.50M | src++; |
453 | 5.50M | } |
454 | | |
455 | | /* treat items overlapping with unionRect */ |
456 | 470k | const RECTANGLE_16* startOverlap = unionRect; |
457 | 470k | const RECTANGLE_16* endOverlap = unionRect; |
458 | | |
459 | 470k | if ((src < end) && (src->top == refY) && (src->left < unionRect->left)) |
460 | 257k | startOverlap = src; |
461 | | |
462 | 721k | while ((src < end) && (src->top == refY) && (src->right < unionRect->right)) |
463 | 251k | { |
464 | 251k | src++; |
465 | 251k | } |
466 | | |
467 | 470k | if ((src < end) && (src->top == refY) && (src->left < unionRect->right)) |
468 | 230k | { |
469 | 230k | endOverlap = src; |
470 | 230k | src++; |
471 | 230k | } |
472 | | |
473 | 470k | { |
474 | 470k | RECTANGLE_16* dst = nextRect(region, (*dstCounter)++); |
475 | 470k | if (!dst) |
476 | 0 | return FALSE; |
477 | 470k | dst->bottom = newBottom; |
478 | 470k | dst->top = newTop; |
479 | 470k | dst->left = startOverlap->left; |
480 | 470k | dst->right = endOverlap->right; |
481 | 470k | } |
482 | 470k | } |
483 | | |
484 | | /* treat remaining items on the same band */ |
485 | 34.7M | while ((src < end) && (src->top == refY)) |
486 | 32.3M | { |
487 | 32.3M | RECTANGLE_16* dst = nextRect(region, (*dstCounter)++); |
488 | 32.3M | if (!dst) |
489 | 0 | return FALSE; |
490 | | |
491 | 32.3M | dst->top = newTop; |
492 | 32.3M | dst->bottom = newBottom; |
493 | 32.3M | dst->right = src->right; |
494 | 32.3M | dst->left = src->left; |
495 | 32.3M | src++; |
496 | 32.3M | } |
497 | | |
498 | 2.47M | if (srcPtr) |
499 | 2.47M | *srcPtr = src; |
500 | | |
501 | 2.47M | return TRUE; |
502 | 2.47M | } |
503 | | |
504 | | static RECTANGLE_16* next_band(RECTANGLE_16* band1, RECTANGLE_16* endPtr, int* nbItems) |
505 | 2.47M | { |
506 | 2.47M | WINPR_ASSERT(band1); |
507 | 2.47M | WINPR_ASSERT(endPtr); |
508 | 2.47M | WINPR_ASSERT(nbItems); |
509 | | |
510 | 2.47M | UINT16 refY = band1->top; |
511 | 2.47M | *nbItems = 0; |
512 | | |
513 | 40.7M | while ((band1 < endPtr) && (band1->top == refY)) |
514 | 38.2M | { |
515 | 38.2M | band1++; |
516 | 38.2M | *nbItems += 1; |
517 | 38.2M | } |
518 | | |
519 | 2.47M | return band1; |
520 | 2.47M | } |
521 | | |
522 | | static BOOL band_match(const RECTANGLE_16* band1, const RECTANGLE_16* band2, |
523 | | const RECTANGLE_16* endPtr) |
524 | 2.33M | { |
525 | 2.33M | int refBand2 = band2->top; |
526 | 2.33M | const RECTANGLE_16* band2Start = band2; |
527 | | |
528 | 10.9M | while ((band1 < band2Start) && (band2 < endPtr) && (band2->top == refBand2)) |
529 | 10.6M | { |
530 | 10.6M | if ((band1->left != band2->left) || (band1->right != band2->right)) |
531 | 2.09M | return FALSE; |
532 | | |
533 | 8.59M | band1++; |
534 | 8.59M | band2++; |
535 | 8.59M | } |
536 | | |
537 | 236k | if (band1 != band2Start) |
538 | 60.5k | return FALSE; |
539 | | |
540 | 175k | return (band2 == endPtr) || (band2->top != refBand2); |
541 | 236k | } |
542 | | |
543 | | /** compute if the rectangle is fully included in the band |
544 | | * @param band a pointer on the beginning of the band |
545 | | * @param endPtr end of the region |
546 | | * @param rect the rectangle to test |
547 | | * @return if rect is fully included in an item of the band |
548 | | */ |
549 | | static BOOL rectangle_contained_in_band(const RECTANGLE_16* band, const RECTANGLE_16* endPtr, |
550 | | const RECTANGLE_16* rect) |
551 | 474k | { |
552 | 474k | WINPR_ASSERT(band); |
553 | 474k | WINPR_ASSERT(endPtr); |
554 | 474k | WINPR_ASSERT(rect); |
555 | | |
556 | 474k | UINT16 refY = band->top; |
557 | | |
558 | 474k | if ((band->top > rect->top) || (rect->bottom > band->bottom)) |
559 | 463k | return FALSE; |
560 | | |
561 | | /* note: as the band is sorted from left to right, once we've seen an item |
562 | | * that is after rect->left we're sure that the result is False. |
563 | | */ |
564 | 19.8k | while ((band < endPtr) && (band->top == refY) && (band->left <= rect->left)) |
565 | 12.1k | { |
566 | 12.1k | if (rect->right <= band->right) |
567 | 3.67k | return TRUE; |
568 | | |
569 | 8.45k | band++; |
570 | 8.45k | } |
571 | | |
572 | 7.73k | return FALSE; |
573 | 11.4k | } |
574 | | |
575 | | static BOOL region16_simplify_bands(REGION16* region) |
576 | 10.0k | { |
577 | | /** Simplify consecutive bands that touch and have the same items |
578 | | * |
579 | | * ==================== ==================== |
580 | | * | 1 | | 2 | | | | | |
581 | | * ==================== | | | | |
582 | | * | 1 | | 2 | ====> | 1 | | 2 | |
583 | | * ==================== | | | | |
584 | | * | 1 | | 2 | | | | | |
585 | | * ==================== ==================== |
586 | | * |
587 | | */ |
588 | | |
589 | 10.0k | const int nbRects = region16_n_rects(region); |
590 | 10.0k | int finalNbRects = nbRects; |
591 | | |
592 | 10.0k | if (nbRects < 2) |
593 | 356 | return TRUE; |
594 | | |
595 | 9.66k | RECTANGLE_16* band1 = region16_rects_noconst(region); |
596 | 9.66k | RECTANGLE_16* endPtr = band1 + nbRects; |
597 | | |
598 | 9.66k | do |
599 | 2.47M | { |
600 | 2.47M | int bandItems = 0; |
601 | 2.47M | RECTANGLE_16* band2 = next_band(band1, endPtr, &bandItems); |
602 | | |
603 | 2.47M | if (band2 == endPtr) |
604 | 9.66k | break; |
605 | | |
606 | 2.46M | if ((band1->bottom == band2->top) && band_match(band1, band2, endPtr)) |
607 | 48.9k | { |
608 | | /* adjust the bottom of band1 items */ |
609 | 48.9k | RECTANGLE_16* tmp = band1; |
610 | | |
611 | 276k | while (tmp < band2) |
612 | 227k | { |
613 | 227k | tmp->bottom = band2->bottom; |
614 | 227k | tmp++; |
615 | 227k | } |
616 | | |
617 | | /* override band2, we don't move band1 pointer as the band after band2 |
618 | | * may be merged too */ |
619 | 48.9k | const RECTANGLE_16* endBand = band2 + bandItems; |
620 | 48.9k | const size_t toMove = |
621 | 48.9k | WINPR_ASSERTING_INT_CAST(size_t, (endPtr - endBand)) * sizeof(RECTANGLE_16); |
622 | | |
623 | 48.9k | if (toMove) |
624 | 48.6k | MoveMemory(band2, endBand, toMove); |
625 | | |
626 | 48.9k | finalNbRects -= bandItems; |
627 | 48.9k | endPtr -= bandItems; |
628 | 48.9k | } |
629 | 2.41M | else |
630 | 2.41M | { |
631 | 2.41M | band1 = band2; |
632 | 2.41M | } |
633 | 2.46M | } while (TRUE); |
634 | | |
635 | 9.66k | if (finalNbRects != nbRects) |
636 | 3.21k | { |
637 | 3.21k | if (!resizeRegion(region, WINPR_ASSERTING_INT_CAST(size_t, finalNbRects))) |
638 | 0 | return FALSE; |
639 | 3.21k | } |
640 | | |
641 | 9.66k | return TRUE; |
642 | 9.66k | } |
643 | | |
644 | | BOOL region16_union_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* rect) |
645 | 10.3k | { |
646 | 10.3k | const RECTANGLE_16* nextBand = nullptr; |
647 | 10.3k | UINT32 srcNbRects = 0; |
648 | 10.3k | UINT16 topInterBand = 0; |
649 | 10.3k | WINPR_ASSERT(src); |
650 | 10.3k | WINPR_ASSERT(dst); |
651 | | |
652 | 10.3k | const RECTANGLE_16* srcExtents = region16_extents(src); |
653 | 10.3k | RECTANGLE_16* dstExtents = region16_extents_noconst(dst); |
654 | | |
655 | 10.3k | const int nrSrcRects = region16_n_rects(src); |
656 | 10.3k | if (nrSrcRects == 0) |
657 | 301 | { |
658 | | /* source is empty, so the union is rect */ |
659 | 301 | dst->extents = *rect; |
660 | | |
661 | 301 | if (!resizeRegion(dst, 1)) |
662 | 0 | return FALSE; |
663 | | |
664 | 301 | RECTANGLE_16* dstRect = region16_rects_noconst(dst); |
665 | 301 | WINPR_ASSERT(dstRect); |
666 | | |
667 | 301 | dstRect->top = rect->top; |
668 | 301 | dstRect->left = rect->left; |
669 | 301 | dstRect->right = rect->right; |
670 | 301 | dstRect->bottom = rect->bottom; |
671 | 301 | dst->data->nbRects = 1; |
672 | 301 | return TRUE; |
673 | 301 | } |
674 | | |
675 | 10.0k | REGION16_DATA* newItems = allocateRegion(WINPR_ASSERTING_INT_CAST(size_t, nrSrcRects + 1)); |
676 | | |
677 | 10.0k | if (!newItems) |
678 | 0 | return FALSE; |
679 | | |
680 | 10.0k | UINT32 usedRects = 0; |
681 | | |
682 | | /* adds the piece of rect that is on the top of src */ |
683 | 10.0k | if (rect->top < srcExtents->top) |
684 | 508 | { |
685 | 508 | RECTANGLE_16* dstRect = nextRect(newItems, usedRects++); |
686 | 508 | if (!dstRect) |
687 | 0 | return FALSE; |
688 | | |
689 | 508 | dstRect->top = rect->top; |
690 | 508 | dstRect->left = rect->left; |
691 | 508 | dstRect->right = rect->right; |
692 | 508 | dstRect->bottom = MIN(srcExtents->top, rect->bottom); |
693 | 508 | } |
694 | | |
695 | | /* treat possibly overlapping region */ |
696 | 10.0k | const RECTANGLE_16* currentBand = region16_rects(src, &srcNbRects); |
697 | 10.0k | const RECTANGLE_16* endSrcRect = currentBand + srcNbRects; |
698 | | |
699 | 2.41M | while (currentBand < endSrcRect) |
700 | 2.40M | { |
701 | 2.40M | if ((currentBand->bottom <= rect->top) || (rect->bottom <= currentBand->top) || |
702 | 474k | rectangle_contained_in_band(currentBand, endSrcRect, rect)) |
703 | 1.93M | { |
704 | | /* no overlap between rect and the band, rect is totally below or totally above |
705 | | * the current band, or rect is already covered by an item of the band. |
706 | | * let's copy all the rectangles from this band |
707 | | +----+ |
708 | | | | rect (case 1) |
709 | | +----+ |
710 | | |
711 | | ================= |
712 | | band of srcRect |
713 | | ================= |
714 | | +----+ |
715 | | | | rect (case 2) |
716 | | +----+ |
717 | | */ |
718 | 1.93M | if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect, currentBand->top, |
719 | 1.93M | currentBand->bottom, nullptr, &usedRects, &nextBand)) |
720 | 0 | return FALSE; |
721 | 1.93M | topInterBand = rect->top; |
722 | 1.93M | } |
723 | 470k | else |
724 | 470k | { |
725 | | /* rect overlaps the band: |
726 | | | | | | |
727 | | ====^=================| |==| |=========================== band |
728 | | | top split | | | | |
729 | | v | 1 | | 2 | |
730 | | ^ | | | | +----+ +----+ |
731 | | | merge zone | | | | | | | 4 | |
732 | | v +----+ | | | | +----+ |
733 | | ^ | | | 3 | |
734 | | | bottom split | | | | |
735 | | ====v=========================| |==| |=================== |
736 | | | | | | |
737 | | |
738 | | possible cases: |
739 | | 1) no top split, merge zone then a bottom split. The band will be split |
740 | | in two |
741 | | 2) not band split, only the merge zone, band merged with rect but not split |
742 | | 3) a top split, the merge zone and no bottom split. The band will be split |
743 | | in two |
744 | | 4) a top split, the merge zone and also a bottom split. The band will be |
745 | | split in 3, but the coalesce algorithm may merge the created bands |
746 | | */ |
747 | 470k | UINT16 mergeTop = currentBand->top; |
748 | 470k | UINT16 mergeBottom = currentBand->bottom; |
749 | | |
750 | | /* test if we need a top split, case 3 and 4 */ |
751 | 470k | if (rect->top > currentBand->top) |
752 | 36.5k | { |
753 | 36.5k | if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect, |
754 | 36.5k | currentBand->top, rect->top, nullptr, &usedRects, |
755 | 36.5k | &nextBand)) |
756 | 0 | return FALSE; |
757 | 36.5k | mergeTop = rect->top; |
758 | 36.5k | } |
759 | | |
760 | | /* do the merge zone (all cases) */ |
761 | 470k | if (rect->bottom < currentBand->bottom) |
762 | 27.2k | mergeBottom = rect->bottom; |
763 | | |
764 | 470k | if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect, mergeTop, |
765 | 470k | mergeBottom, rect, &usedRects, &nextBand)) |
766 | 0 | return FALSE; |
767 | | |
768 | | /* test if we need a bottom split, case 1 and 4 */ |
769 | 470k | if (rect->bottom < currentBand->bottom) |
770 | 27.2k | { |
771 | 27.2k | if (!region16_copy_band_with_union(newItems, currentBand, endSrcRect, mergeBottom, |
772 | 27.2k | currentBand->bottom, nullptr, &usedRects, |
773 | 27.2k | &nextBand)) |
774 | 0 | return FALSE; |
775 | 27.2k | } |
776 | | |
777 | 470k | topInterBand = currentBand->bottom; |
778 | 470k | } |
779 | | |
780 | | /* test if a piece of rect should be inserted as a new band between |
781 | | * the current band and the next one. band n and n+1 shouldn't touch. |
782 | | * |
783 | | * ============================================================== |
784 | | * band n |
785 | | * +------+ +------+ |
786 | | * ===========| rect |====================| |=============== |
787 | | * | | +------+ | | |
788 | | * +------+ | rect | | rect | |
789 | | * +------+ | | |
790 | | * =======================================| |================ |
791 | | * +------+ band n+1 |
792 | | * =============================================================== |
793 | | * |
794 | | */ |
795 | 2.40M | if ((nextBand < endSrcRect) && (nextBand->top != currentBand->bottom) && |
796 | 130k | (rect->bottom > currentBand->bottom) && (rect->top < nextBand->top)) |
797 | 20.0k | { |
798 | 20.0k | RECTANGLE_16* dstRect = nextRect(newItems, usedRects++); |
799 | 20.0k | if (!dstRect) |
800 | 0 | return FALSE; |
801 | | |
802 | 20.0k | dstRect->right = rect->right; |
803 | 20.0k | dstRect->left = rect->left; |
804 | 20.0k | dstRect->top = topInterBand; |
805 | 20.0k | dstRect->bottom = MIN(nextBand->top, rect->bottom); |
806 | 20.0k | } |
807 | | |
808 | 2.40M | currentBand = nextBand; |
809 | 2.40M | } |
810 | | |
811 | | /* adds the piece of rect that is below src */ |
812 | 10.0k | if (srcExtents->bottom < rect->bottom) |
813 | 680 | { |
814 | 680 | RECTANGLE_16* dstRect = nextRect(newItems, usedRects++); |
815 | 680 | if (!dstRect) |
816 | 0 | return FALSE; |
817 | | |
818 | 680 | dstRect->top = MAX(srcExtents->bottom, rect->top); |
819 | 680 | dstRect->left = rect->left; |
820 | 680 | dstRect->right = rect->right; |
821 | 680 | dstRect->bottom = rect->bottom; |
822 | 680 | } |
823 | | |
824 | 10.0k | dstExtents->top = MIN(rect->top, srcExtents->top); |
825 | 10.0k | dstExtents->left = MIN(rect->left, srcExtents->left); |
826 | 10.0k | dstExtents->bottom = MAX(rect->bottom, srcExtents->bottom); |
827 | 10.0k | dstExtents->right = MAX(rect->right, srcExtents->right); |
828 | | |
829 | 10.0k | newItems->nbRects = usedRects; |
830 | 10.0k | freeRegion(dst->data); |
831 | 10.0k | dst->data = newItems; |
832 | | |
833 | 10.0k | return region16_simplify_bands(dst); |
834 | 10.0k | } |
835 | | |
836 | | BOOL region16_intersects_rect(const REGION16* src, const RECTANGLE_16* arg2) |
837 | 0 | { |
838 | 0 | const RECTANGLE_16* endPtr = nullptr; |
839 | 0 | UINT32 nbRects = 0; |
840 | |
|
841 | 0 | if (!src || !src->data || !arg2) |
842 | 0 | return FALSE; |
843 | | |
844 | 0 | const RECTANGLE_16* rect = region16_rects(src, &nbRects); |
845 | |
|
846 | 0 | if (!nbRects) |
847 | 0 | return FALSE; |
848 | | |
849 | 0 | const RECTANGLE_16* srcExtents = region16_extents(src); |
850 | |
|
851 | 0 | if (nbRects == 1) |
852 | 0 | return rectangles_intersects(srcExtents, arg2); |
853 | | |
854 | 0 | if (!rectangles_intersects(srcExtents, arg2)) |
855 | 0 | return FALSE; |
856 | | |
857 | 0 | for (endPtr = rect + nbRects; (rect < endPtr) && (arg2->bottom > rect->top); rect++) |
858 | 0 | { |
859 | 0 | if (rectangles_intersects(rect, arg2)) |
860 | 0 | return TRUE; |
861 | 0 | } |
862 | | |
863 | 0 | return FALSE; |
864 | 0 | } |
865 | | |
866 | | BOOL region16_intersect_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* rect) |
867 | 0 | { |
868 | 0 | const RECTANGLE_16* endPtr = nullptr; |
869 | 0 | UINT32 nbRects = 0; |
870 | 0 | RECTANGLE_16 common = WINPR_C_ARRAY_INIT; |
871 | |
|
872 | 0 | WINPR_ASSERT(dst); |
873 | 0 | WINPR_ASSERT(src); |
874 | |
|
875 | 0 | const RECTANGLE_16* srcPtr = region16_rects(src, &nbRects); |
876 | |
|
877 | 0 | if (!nbRects) |
878 | 0 | { |
879 | 0 | region16_clear(dst); |
880 | 0 | return TRUE; |
881 | 0 | } |
882 | | |
883 | 0 | const RECTANGLE_16* srcExtents = region16_extents(src); |
884 | |
|
885 | 0 | if (nbRects == 1) |
886 | 0 | { |
887 | 0 | BOOL intersects = rectangles_intersection(srcExtents, rect, &common); |
888 | 0 | region16_clear(dst); |
889 | |
|
890 | 0 | if (intersects) |
891 | 0 | return region16_union_rect(dst, dst, &common); |
892 | | |
893 | 0 | return TRUE; |
894 | 0 | } |
895 | | |
896 | 0 | REGION16_DATA* newItems = allocateRegion(nbRects); |
897 | |
|
898 | 0 | if (!newItems) |
899 | 0 | return FALSE; |
900 | | |
901 | 0 | RECTANGLE_16* dstPtr = newItems->rects; |
902 | 0 | UINT32 usedRects = 0; |
903 | 0 | RECTANGLE_16 newExtents = WINPR_C_ARRAY_INIT; |
904 | | |
905 | | /* accumulate intersecting rectangles, the final region16_simplify_bands() will |
906 | | * do all the bad job to recreate correct rectangles |
907 | | */ |
908 | 0 | for (endPtr = srcPtr + nbRects; (srcPtr < endPtr) && (rect->bottom > srcPtr->top); srcPtr++) |
909 | 0 | { |
910 | 0 | if (usedRects > nbRects) |
911 | 0 | { |
912 | 0 | freeRegion(newItems); |
913 | 0 | return FALSE; |
914 | 0 | } |
915 | | |
916 | 0 | if (rectangles_intersection(srcPtr, rect, &common)) |
917 | 0 | { |
918 | 0 | *dstPtr = common; |
919 | 0 | usedRects++; |
920 | 0 | dstPtr++; |
921 | |
|
922 | 0 | if (rectangle_is_empty(&newExtents)) |
923 | 0 | { |
924 | | /* Check if the existing newExtents is empty. If it is empty, use |
925 | | * new common directly. We do not need to check common rectangle |
926 | | * because the rectangles_intersection() ensures that it is not empty. |
927 | | */ |
928 | 0 | newExtents = common; |
929 | 0 | } |
930 | 0 | else |
931 | 0 | { |
932 | 0 | newExtents.top = MIN(common.top, newExtents.top); |
933 | 0 | newExtents.left = MIN(common.left, newExtents.left); |
934 | 0 | newExtents.bottom = MAX(common.bottom, newExtents.bottom); |
935 | 0 | newExtents.right = MAX(common.right, newExtents.right); |
936 | 0 | } |
937 | 0 | } |
938 | 0 | } |
939 | | |
940 | 0 | newItems->nbRects = usedRects; |
941 | |
|
942 | 0 | freeRegion(dst->data); |
943 | 0 | dst->data = newItems; |
944 | 0 | dst->extents = newExtents; |
945 | 0 | return region16_simplify_bands(dst); |
946 | 0 | } |
947 | | |
948 | | void region16_uninit(REGION16* region) |
949 | 13.4k | { |
950 | 13.4k | WINPR_ASSERT(region); |
951 | | |
952 | 13.4k | freeRegion(region->data); |
953 | 13.4k | region->data = nullptr; |
954 | 13.4k | } |