Add a region component
This patch adds a pixman_region like component in the utility components of FreeRdp. The data structure is exactly the same as in pixman_region but the implementation differs as we need fewer methods. The patch contains the corresponding unitary tests.
This commit is contained in:
parent
1c0e874b5b
commit
d1e75efb8c
3
.gitignore
vendored
3
.gitignore
vendored
@ -102,6 +102,9 @@ server/Sample/sfreerdp-server
|
||||
server/X11/xfreerdp-server
|
||||
xcode
|
||||
|
||||
# Generated test code
|
||||
libfreerdp/utils/test/TestFreeRDPUtils.c
|
||||
|
||||
# Other
|
||||
*~
|
||||
*.dir
|
||||
|
127
include/freerdp/utils/region.h
Normal file
127
include/freerdp/utils/region.h
Normal file
@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright © 2014 Thincast Technologies GmbH
|
||||
* Copyright © 2014 Hardening <contact@hardening-consulting.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee, provided
|
||||
* that the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation, and that the name of the copyright holders not be used in
|
||||
* advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The copyright holders make
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __REGION_H___
|
||||
#define __REGION_H___
|
||||
|
||||
#include <freerdp/types.h>
|
||||
|
||||
struct _REGION16_DATA;
|
||||
typedef struct _REGION16_DATA REGION16_DATA;
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*/
|
||||
struct _REGION16 {
|
||||
RECTANGLE_16 extents;
|
||||
REGION16_DATA *data;
|
||||
};
|
||||
typedef struct _REGION16 REGION16;
|
||||
|
||||
/** computes if two rectangles intersect
|
||||
* @param r1 first rectangle
|
||||
* @param r2 second rectangle
|
||||
* @return if the two rectangles intersect
|
||||
*/
|
||||
BOOL rectangles_intersects(const RECTANGLE_16 *r1, const RECTANGLE_16 *r2);
|
||||
|
||||
/** computes the intersection of two rectangles
|
||||
* @param r1 first rectangle
|
||||
* @param r2 second rectangle
|
||||
* @param dst resulting intersection
|
||||
* @return if the two rectangles intersect
|
||||
*/
|
||||
BOOL rectangles_intersection(const RECTANGLE_16 *r1, const RECTANGLE_16 *r2, RECTANGLE_16 *dst);
|
||||
|
||||
/** initialize a region16
|
||||
* @param region the region to initialise
|
||||
*/
|
||||
void region16_init(REGION16 *region);
|
||||
|
||||
/** @return the number of rectangles of this region16 */
|
||||
int region16_n_rects(const REGION16 *region);
|
||||
|
||||
/** returns a pointer on rectangles and the number of rectangles in this region.
|
||||
* nbRect can be set to NULL if not interested by the numnber of rectangles.
|
||||
* @param region the input region
|
||||
* @param nbRects a pointer that will be filled with the number of rectangles
|
||||
* @return a pointer on the rectangles
|
||||
*/
|
||||
const RECTANGLE_16 *region16_rects(const REGION16 *region, int *nbRects);
|
||||
|
||||
/** @return the extents rectangle of this region */
|
||||
const RECTANGLE_16 *region16_extents(const REGION16 *region);
|
||||
|
||||
/** returns if the region is empty
|
||||
* @param region
|
||||
* @return if the region is empty
|
||||
*/
|
||||
BOOL region16_is_empty(const REGION16 *region);
|
||||
|
||||
/** clears the region, the region is resetted to a (0,0,0,0) region
|
||||
* @param region
|
||||
*/
|
||||
void region16_clear(REGION16 *region);
|
||||
|
||||
/** dumps the region on stderr
|
||||
* @param region the region to dump
|
||||
*/
|
||||
void region16_print(const REGION16 *region);
|
||||
|
||||
/** copies the region to another region
|
||||
* @param dst destination region
|
||||
* @param src source region
|
||||
* @return if the operation was successful (false meaning out-of-memory)
|
||||
*/
|
||||
BOOL region16_copy(REGION16 *dst, const REGION16 *src);
|
||||
|
||||
/** adds a rectangle in src and stores the resulting region in dst
|
||||
* @param dst destination region
|
||||
* @param src source region
|
||||
* @param rect the rectangle to add
|
||||
* @return if the operation was successful (false meaning out-of-memory)
|
||||
*/
|
||||
BOOL region16_union_rect(REGION16 *dst, const REGION16 *src, const RECTANGLE_16 *rect);
|
||||
|
||||
/** returns if a rectangle intersects the region
|
||||
* @param src the region
|
||||
* @param arg2 the rectangle
|
||||
* @return if region and rectangle intersect
|
||||
*/
|
||||
BOOL region16_intersects_rect(const REGION16 *src, const RECTANGLE_16 *arg2);
|
||||
|
||||
/** computes the intersection between a region and a rectangle
|
||||
* @param dst destination region
|
||||
* @param src the source region
|
||||
* @param arg2 the rectangle that intersects
|
||||
* @return if the operation was successful (false meaning out-of-memory)
|
||||
*/
|
||||
BOOL region16_intersect_rect(REGION16 *dst, const REGION16 *src, const RECTANGLE_16 *arg2);
|
||||
|
||||
/** release internal data associated with this region
|
||||
* @param region the region to release
|
||||
*/
|
||||
void region16_uninit(REGION16 *region);
|
||||
|
||||
|
||||
#endif /* __REGION_H___ */
|
@ -26,6 +26,7 @@ set(${MODULE_PREFIX}_SRCS
|
||||
pcap.c
|
||||
profiler.c
|
||||
rail.c
|
||||
region.c
|
||||
signal.c
|
||||
stopwatch.c
|
||||
svc_plugin.c
|
||||
@ -68,3 +69,7 @@ else()
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/libfreerdp")
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
753
libfreerdp/utils/region.c
Normal file
753
libfreerdp/utils/region.c
Normal file
@ -0,0 +1,753 @@
|
||||
/**
|
||||
* Copyright © 2014 Thincast Technologies GmbH
|
||||
* Copyright © 2014 Hardening <contact@hardening-consulting.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee, provided
|
||||
* that the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation, and that the name of the copyright holders not be used in
|
||||
* advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The copyright holders make
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <winpr/memory.h>
|
||||
#include <freerdp/utils/region.h>
|
||||
|
||||
/*
|
||||
* The functions in this file implement the Region abstraction largely inspired from
|
||||
* pixman library. The following comment is taken from the pixman code.
|
||||
*
|
||||
* A Region is simply a set of disjoint(non-overlapping) rectangles, plus an
|
||||
* "extent" rectangle which is the smallest single rectangle that contains all
|
||||
* the non-overlapping rectangles.
|
||||
*
|
||||
* A Region is implemented as a "y-x-banded" array of rectangles. This array
|
||||
* imposes two degrees of order. First, all rectangles are sorted by top side
|
||||
* y coordinate first (y1), and then by left side x coordinate (x1).
|
||||
*
|
||||
* Furthermore, the rectangles are grouped into "bands". Each rectangle in a
|
||||
* band has the same top y coordinate (y1), and each has the same bottom y
|
||||
* coordinate (y2). Thus all rectangles in a band differ only in their left
|
||||
* and right side (x1 and x2). Bands are implicit in the array of rectangles:
|
||||
* there is no separate list of band start pointers.
|
||||
*
|
||||
* The y-x band representation does not minimize rectangles. In particular,
|
||||
* if a rectangle vertically crosses a band (the rectangle has scanlines in
|
||||
* the y1 to y2 area spanned by the band), then the rectangle may be broken
|
||||
* down into two or more smaller rectangles stacked one atop the other.
|
||||
*
|
||||
* ----------- -----------
|
||||
* | | | | band 0
|
||||
* | | -------- ----------- --------
|
||||
* | | | | in y-x banded | | | | band 1
|
||||
* | | | | form is | | | |
|
||||
* ----------- | | ----------- --------
|
||||
* | | | | band 2
|
||||
* -------- --------
|
||||
*
|
||||
* An added constraint on the rectangles is that they must cover as much
|
||||
* horizontal area as possible: no two rectangles within a band are allowed
|
||||
* to touch.
|
||||
*
|
||||
* Whenever possible, bands will be merged together to cover a greater vertical
|
||||
* distance (and thus reduce the number of rectangles). Two bands can be merged
|
||||
* only if the bottom of one touches the top of the other and they have
|
||||
* rectangles in the same places (of the same width, of course).
|
||||
*/
|
||||
|
||||
struct _REGION16_DATA {
|
||||
long size;
|
||||
long nbRects;
|
||||
};
|
||||
typedef struct _REGION16_DATA REGION16_DATA;
|
||||
|
||||
static REGION16_DATA empty_region = { 0, 0 };
|
||||
|
||||
void region16_init(REGION16 *region)
|
||||
{
|
||||
assert(region);
|
||||
|
||||
ZeroMemory(region, sizeof(REGION16));
|
||||
region->data = &empty_region;
|
||||
}
|
||||
|
||||
int region16_n_rects(const REGION16 *region)
|
||||
{
|
||||
assert(region);
|
||||
assert(region->data);
|
||||
|
||||
return region->data->nbRects;
|
||||
}
|
||||
|
||||
const RECTANGLE_16 *region16_rects(const REGION16 *region, int *nbRects)
|
||||
{
|
||||
REGION16_DATA *data;
|
||||
|
||||
assert(region);
|
||||
assert(region->data);
|
||||
|
||||
data = region->data;
|
||||
if (!data)
|
||||
{
|
||||
if (nbRects)
|
||||
*nbRects = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*nbRects = data->nbRects;
|
||||
return (RECTANGLE_16 *)(data + 1);
|
||||
}
|
||||
|
||||
static inline RECTANGLE_16 *region16_rects_noconst(REGION16 *region)
|
||||
{
|
||||
REGION16_DATA *data;
|
||||
|
||||
data = region->data;
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
return (RECTANGLE_16 *)(data + 1);
|
||||
}
|
||||
|
||||
const RECTANGLE_16 *region16_extents(const REGION16 *region)
|
||||
{
|
||||
return ®ion->extents;
|
||||
}
|
||||
|
||||
static RECTANGLE_16 *region16_extents_noconst(REGION16 *region)
|
||||
{
|
||||
return ®ion->extents;
|
||||
}
|
||||
|
||||
BOOL region16_is_empty(const REGION16 *region)
|
||||
{
|
||||
assert(region);
|
||||
assert(region->data);
|
||||
|
||||
return (region->data->nbRects == 0);
|
||||
}
|
||||
|
||||
BOOL rectangles_intersects(const RECTANGLE_16 *r1, const RECTANGLE_16 *r2)
|
||||
{
|
||||
RECTANGLE_16 tmp;
|
||||
return rectangles_intersection(r1, r2, &tmp);
|
||||
}
|
||||
|
||||
BOOL rectangles_intersection(const RECTANGLE_16 *r1, const RECTANGLE_16 *r2,
|
||||
RECTANGLE_16 *dst)
|
||||
{
|
||||
dst->left = MAX(r1->left, r2->left);
|
||||
dst->right = MIN(r1->right, r2->right);
|
||||
dst->top = MAX(r1->top, r2->top);
|
||||
dst->bottom = MIN(r1->bottom, r2->bottom);
|
||||
|
||||
return (dst->left < dst->right) && (dst->top < dst->bottom);
|
||||
}
|
||||
|
||||
void region16_clear(REGION16 *region)
|
||||
{
|
||||
assert(region);
|
||||
assert(region->data);
|
||||
|
||||
if (region->data->size)
|
||||
free(region->data);
|
||||
region->data = &empty_region;
|
||||
|
||||
ZeroMemory(®ion->extents, sizeof(region->extents));
|
||||
}
|
||||
|
||||
static inline REGION16_DATA *allocateRegion(long nbItems)
|
||||
{
|
||||
long allocSize = sizeof(REGION16_DATA) + nbItems * sizeof(RECTANGLE_16);
|
||||
REGION16_DATA *ret = (REGION16_DATA *)malloc(allocSize);
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
ret->size = allocSize;
|
||||
ret->nbRects = nbItems;
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL region16_copy(REGION16 *dst, const REGION16 *src)
|
||||
{
|
||||
assert(dst);
|
||||
assert(dst->data);
|
||||
assert(src);
|
||||
assert(src->data);
|
||||
|
||||
if (dst == src)
|
||||
return TRUE;
|
||||
|
||||
dst->extents = src->extents;
|
||||
if (dst->data->size)
|
||||
free(dst->data);
|
||||
|
||||
if (!src->data->size)
|
||||
{
|
||||
dst->data = &empty_region;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst->data = allocateRegion(src->data->nbRects);
|
||||
if (!dst->data)
|
||||
return FALSE;
|
||||
|
||||
memcpy(dst->data, src->data, src->data->size);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void region16_print(const REGION16 *region)
|
||||
{
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects, i;
|
||||
int currentBandY = -1;
|
||||
|
||||
rects = region16_rects(region, &nbRects);
|
||||
fprintf(stderr, "nrects=%d", nbRects);
|
||||
|
||||
for (i = 0; i < nbRects; i++, rects++)
|
||||
{
|
||||
if (rects->top != currentBandY)
|
||||
{
|
||||
currentBandY = rects->top;
|
||||
fprintf(stderr, "\nband %d: ", currentBandY);
|
||||
}
|
||||
|
||||
fprintf(stderr, "(%d,%d-%d,%d)", rects->left, rects->top, rects->right, rects->bottom);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void region16_copy_band_with_union(RECTANGLE_16 *dst,
|
||||
const RECTANGLE_16 *src, const RECTANGLE_16 *end,
|
||||
UINT16 newTop, UINT16 newBottom,
|
||||
const RECTANGLE_16 *unionRect,
|
||||
int *dstCounter,
|
||||
const RECTANGLE_16 **srcPtr, RECTANGLE_16 **dstPtr)
|
||||
{
|
||||
UINT16 refY = src->top;
|
||||
const RECTANGLE_16 *startOverlap, *endOverlap;
|
||||
|
||||
/* merges a band with the given rect
|
||||
* Input:
|
||||
* unionRect
|
||||
* | |
|
||||
* | |
|
||||
* ==============+===============+================================
|
||||
* |Item1| |Item2| |Item3| |Item4| |Item5| Band
|
||||
* ==============+===============+================================
|
||||
* before | overlap | after
|
||||
*
|
||||
* Resulting band:
|
||||
* +-----+ +----------------------+ +-----+
|
||||
* |Item1| | Item2 | |Item3|
|
||||
* +-----+ +----------------------+ +-----+
|
||||
*
|
||||
* We first copy as-is items that are before Item2, the first overlapping
|
||||
* item.
|
||||
* Then we find the last one that overlap unionRect to agregate Item2, Item3
|
||||
* and Item4 to create Item2.
|
||||
* Finally Item5 is copied as Item3.
|
||||
*
|
||||
* When no unionRect is provided, we skip the two first steps to just copy items
|
||||
*/
|
||||
|
||||
if (unionRect)
|
||||
{
|
||||
/* items before unionRect */
|
||||
while ((src < end) && (src->top == refY) && (src->right < unionRect->left))
|
||||
{
|
||||
dst->top = newTop;
|
||||
dst->bottom = newBottom;
|
||||
dst->right = src->right;
|
||||
dst->left = src->left;
|
||||
|
||||
src++; dst++; *dstCounter += 1;
|
||||
}
|
||||
|
||||
/* treat items overlapping with unionRect */
|
||||
startOverlap = unionRect;
|
||||
endOverlap = unionRect;
|
||||
|
||||
if ((src < end) && (src->top == refY) && (src->left < unionRect->left))
|
||||
startOverlap = src;
|
||||
|
||||
while ((src < end) && (src->top == refY) && (src->right < unionRect->right))
|
||||
{
|
||||
src++;
|
||||
}
|
||||
|
||||
if ((src < end) && (src->top == refY) && (src->left < unionRect->right))
|
||||
{
|
||||
endOverlap = src;
|
||||
src++;
|
||||
}
|
||||
|
||||
dst->bottom = newBottom;
|
||||
dst->top = newTop;
|
||||
dst->left = startOverlap->left;
|
||||
dst->right = endOverlap->right;
|
||||
dst++; *dstCounter += 1;
|
||||
}
|
||||
|
||||
/* treat remaining items on the same band */
|
||||
while ((src < end) && (src->top == refY))
|
||||
{
|
||||
dst->top = newTop;
|
||||
dst->bottom = newBottom;
|
||||
dst->right = src->right;
|
||||
dst->left = src->left;
|
||||
|
||||
src++; dst++; *dstCounter += 1;
|
||||
}
|
||||
|
||||
if(srcPtr)
|
||||
*srcPtr = src;
|
||||
*dstPtr = dst;
|
||||
}
|
||||
|
||||
static RECTANGLE_16 *next_band(RECTANGLE_16 *band1, RECTANGLE_16 *endPtr, int *nbItems)
|
||||
{
|
||||
UINT16 refY = band1->top;
|
||||
|
||||
*nbItems = 0;
|
||||
while((band1 < endPtr) && (band1->top == refY)) {
|
||||
band1++;
|
||||
*nbItems += 1;
|
||||
}
|
||||
return band1;
|
||||
}
|
||||
|
||||
static BOOL band_match(const RECTANGLE_16 *band1, const RECTANGLE_16 *band2, RECTANGLE_16 *endPtr)
|
||||
{
|
||||
int refBand2 = band2->top;
|
||||
const RECTANGLE_16 *band2Start = band2;
|
||||
|
||||
while ((band1 < band2Start) && (band2 < endPtr) && (band2->top == refBand2)) {
|
||||
if ((band1->left != band2->left) || (band1->right != band2->right))
|
||||
return FALSE;
|
||||
|
||||
band1++;
|
||||
band2++;
|
||||
}
|
||||
|
||||
if (band1 != band2Start)
|
||||
return FALSE;
|
||||
|
||||
return (band2 == endPtr) || (band2->top != refBand2);
|
||||
}
|
||||
|
||||
/** compute if the rectangle is fully included in the band
|
||||
* @param band a pointer on the beginning of the band
|
||||
* @param endPtr end of the region
|
||||
* @param rect the rectangle to test
|
||||
* @return if rect is fully included in an item of the band
|
||||
*/
|
||||
static BOOL rectangle_contained_in_band(const RECTANGLE_16 *band, const RECTANGLE_16 *endPtr,
|
||||
const RECTANGLE_16 *rect)
|
||||
{
|
||||
UINT16 refY = band->top;
|
||||
|
||||
if ((band->top > rect->top) || (rect->bottom > band->bottom))
|
||||
return FALSE;
|
||||
|
||||
/* note: as the band is sorted from left to right, once we've seen an item
|
||||
* that is after rect->left we're sure that the result is False.
|
||||
*/
|
||||
while( (band < endPtr) && (band->top == refY) && (band->left <= rect->left))
|
||||
{
|
||||
if (rect->right <= band->right)
|
||||
return TRUE;
|
||||
|
||||
band++;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL region16_simplify_bands(REGION16 *region)
|
||||
{
|
||||
/** Simplify bands consecutive band that touch and have the same items
|
||||
*
|
||||
* ==================== ====================
|
||||
* | 1 | | 2 | | | | |
|
||||
* ==================== | | | |
|
||||
* | 1 | | 2 | ====> | 1 | | 2 |
|
||||
* ==================== | | | |
|
||||
* | 1 | | 2 | | | | |
|
||||
* ==================== ====================
|
||||
*
|
||||
*/
|
||||
RECTANGLE_16 *band1, *band2, *endPtr, *endBand, *tmp;
|
||||
int nbRects, finalNbRects;
|
||||
int bandItems, toMove;
|
||||
|
||||
finalNbRects = nbRects = region16_n_rects(region);
|
||||
if (nbRects < 2)
|
||||
return TRUE;
|
||||
|
||||
band1 = region16_rects_noconst(region);
|
||||
endPtr = band1 + nbRects;
|
||||
|
||||
do {
|
||||
band2 = next_band(band1, endPtr, &bandItems);
|
||||
if (band2 == endPtr)
|
||||
break;
|
||||
|
||||
if ((band1->bottom == band2->top) && band_match(band1, band2, endPtr))
|
||||
{
|
||||
/* adjust the bottom of band1 items */
|
||||
tmp = band1;
|
||||
while (tmp < band2)
|
||||
{
|
||||
tmp->bottom = band2->bottom;
|
||||
tmp++;
|
||||
}
|
||||
|
||||
/* override band2, we don't move band1 pointer as the band after band2
|
||||
* may be merged too */
|
||||
endBand = band2 + bandItems;
|
||||
toMove = (endPtr - endBand) * sizeof(RECTANGLE_16);
|
||||
if (toMove)
|
||||
memmove(band2, endBand, toMove);
|
||||
finalNbRects -= bandItems;
|
||||
endPtr -= bandItems;
|
||||
} else {
|
||||
band1 = band2;
|
||||
}
|
||||
} while(TRUE);
|
||||
|
||||
if (finalNbRects != nbRects)
|
||||
{
|
||||
int allocSize = sizeof(REGION16_DATA) + finalNbRects * sizeof(RECTANGLE_16);
|
||||
region->data = realloc(region->data, allocSize);
|
||||
region->data->nbRects = finalNbRects;
|
||||
region->data->size = allocSize;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
BOOL region16_union_rect(REGION16 *dst, const REGION16 *src, const RECTANGLE_16 *rect)
|
||||
{
|
||||
const RECTANGLE_16 *srcExtents;
|
||||
RECTANGLE_16 *dstExtents;
|
||||
const RECTANGLE_16 *currentBand, *endSrcRect, *nextBand;
|
||||
REGION16_DATA *newItems;
|
||||
RECTANGLE_16 *dstRect;
|
||||
int usedRects, srcNbRects;
|
||||
UINT16 topInterBand;
|
||||
|
||||
assert(src);
|
||||
assert(src->data);
|
||||
assert(dst);
|
||||
|
||||
srcExtents = region16_extents(src);
|
||||
dstExtents = region16_extents_noconst(dst);
|
||||
|
||||
if (!region16_n_rects(src))
|
||||
{
|
||||
/* source is empty, so the union is rect */
|
||||
dst->extents = *rect;
|
||||
dst->data = allocateRegion(1);
|
||||
if (!dst->data)
|
||||
return FALSE;
|
||||
|
||||
dstRect = region16_rects_noconst(dst);
|
||||
*dstRect = *rect;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
newItems = allocateRegion((1 + region16_n_rects(src)) * 2);
|
||||
if (!newItems)
|
||||
return FALSE;
|
||||
|
||||
dstRect = (RECTANGLE_16 *)(newItems + 1);
|
||||
usedRects = 0;
|
||||
|
||||
/* adds the piece of rect that is on the top of src */
|
||||
if (rect->top < srcExtents->top)
|
||||
{
|
||||
dstRect->top = rect->top;
|
||||
dstRect->left = rect->left;
|
||||
dstRect->right = rect->right;
|
||||
dstRect->bottom = srcExtents->top;
|
||||
|
||||
usedRects++;
|
||||
dstRect++;
|
||||
}
|
||||
|
||||
/* treat possibly overlapping region */
|
||||
currentBand = region16_rects(src, &srcNbRects);
|
||||
endSrcRect = currentBand + srcNbRects;
|
||||
|
||||
while (currentBand < endSrcRect)
|
||||
{
|
||||
if ((currentBand->bottom <= rect->top) || (rect->bottom <= currentBand->top) ||
|
||||
rectangle_contained_in_band(currentBand, endSrcRect, rect)
|
||||
)
|
||||
{
|
||||
/* no overlap between rect and the band, rect is totally below or totally above
|
||||
* the current band, or rect is already covered by an item of the band.
|
||||
* let's copy all the rectangles from this band
|
||||
+----+
|
||||
| | rect (case 1)
|
||||
+----+
|
||||
|
||||
=================
|
||||
band of srcRect
|
||||
=================
|
||||
+----+
|
||||
| | rect (case 2)
|
||||
+----+
|
||||
*/
|
||||
region16_copy_band_with_union(dstRect,
|
||||
currentBand, endSrcRect,
|
||||
currentBand->top, currentBand->bottom,
|
||||
NULL, &usedRects,
|
||||
&nextBand, &dstRect);
|
||||
topInterBand = rect->top;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* rect overlaps the band:
|
||||
| | | |
|
||||
====^=================| |==| |=========================== band
|
||||
| top split | | | |
|
||||
v | 1 | | 2 |
|
||||
^ | | | | +----+ +----+
|
||||
| merge zone | | | | | | | 4 |
|
||||
v +----+ | | | | +----+
|
||||
^ | | | 3 |
|
||||
| bottom split | | | |
|
||||
====v=========================| |==| |===================
|
||||
| | | |
|
||||
|
||||
possible cases:
|
||||
1) no top split, merge zone then a bottom split. The band will be splitted
|
||||
in two
|
||||
2) not band split, only the merge zone, band merged with rect but not splitted
|
||||
3) a top split, the merge zone and no bottom split. The band will be split
|
||||
in two
|
||||
4) a top split, the merge zone and also a bottom split. The band will be
|
||||
splitted in 3, but the coalesce algorithm may merge the created bands
|
||||
*/
|
||||
UINT16 mergeTop = currentBand->top;
|
||||
UINT16 mergeBottom = currentBand->bottom;
|
||||
|
||||
/* test if we need a top split, case 3 and 4 */
|
||||
if (rect->top > currentBand->top)
|
||||
{
|
||||
region16_copy_band_with_union(dstRect,
|
||||
currentBand, endSrcRect,
|
||||
currentBand->top, rect->top,
|
||||
NULL, &usedRects,
|
||||
&nextBand, &dstRect);
|
||||
mergeTop = rect->top;
|
||||
}
|
||||
|
||||
/* do the merge zone (all cases ) */
|
||||
if (rect->bottom < currentBand->bottom)
|
||||
mergeBottom = rect->bottom;
|
||||
region16_copy_band_with_union(dstRect,
|
||||
currentBand, endSrcRect,
|
||||
mergeTop, mergeBottom,
|
||||
rect, &usedRects,
|
||||
&nextBand, &dstRect);
|
||||
|
||||
/* test if we need a bottom split, case 1 and 4 */
|
||||
if (rect->bottom < currentBand->bottom)
|
||||
{
|
||||
region16_copy_band_with_union(dstRect,
|
||||
currentBand, endSrcRect,
|
||||
mergeBottom, currentBand->bottom,
|
||||
NULL, &usedRects,
|
||||
&nextBand, &dstRect);
|
||||
}
|
||||
topInterBand = currentBand->bottom;
|
||||
}
|
||||
|
||||
/* test if a piece of rect should be inserted as a new band between
|
||||
* the current band and the next one. band n and n+1 shouldn't touch.
|
||||
*
|
||||
* ==============================================================
|
||||
* band n
|
||||
* +------+ +------+
|
||||
* ===========| rect |====================| |===============
|
||||
* | | +------+ | |
|
||||
* +------+ | rect | | rect |
|
||||
* +------+ | |
|
||||
* =======================================| |================
|
||||
* +------+ band n+1
|
||||
* ===============================================================
|
||||
*
|
||||
*/
|
||||
if ((nextBand < endSrcRect) && (nextBand->top != currentBand->bottom) &&
|
||||
(rect->bottom > currentBand->bottom) && (rect->top < nextBand->top))
|
||||
{
|
||||
dstRect->right = rect->right;
|
||||
dstRect->left = rect->left;
|
||||
dstRect->top = topInterBand;
|
||||
dstRect->bottom = MIN(nextBand->top, rect->bottom);
|
||||
dstRect++; usedRects++;
|
||||
}
|
||||
|
||||
currentBand = nextBand;
|
||||
}
|
||||
|
||||
/* adds the piece of rect that is below src */
|
||||
if (srcExtents->bottom < rect->bottom)
|
||||
{
|
||||
dstRect->top = MAX(srcExtents->bottom, rect->top);
|
||||
dstRect->left = rect->left;
|
||||
dstRect->right = rect->right;
|
||||
dstRect->bottom = rect->bottom;
|
||||
|
||||
usedRects++;
|
||||
dstRect++;
|
||||
}
|
||||
|
||||
if ((src == dst) && (src->data->size))
|
||||
free(src->data);
|
||||
|
||||
dstExtents->top = MIN(rect->top, srcExtents->top);
|
||||
dstExtents->left = MIN(rect->left, srcExtents->left);
|
||||
dstExtents->bottom = MAX(rect->bottom, srcExtents->bottom);
|
||||
dstExtents->right = MAX(rect->right, srcExtents->right);
|
||||
|
||||
newItems->size = sizeof(REGION16_DATA) + usedRects * sizeof(RECTANGLE_16);
|
||||
dst->data = realloc(newItems, newItems->size);
|
||||
if (!dst->data)
|
||||
{
|
||||
free(newItems);
|
||||
return FALSE;
|
||||
}
|
||||
dst->data->nbRects = usedRects;
|
||||
|
||||
return region16_simplify_bands(dst);
|
||||
}
|
||||
|
||||
|
||||
BOOL region16_intersects_rect(const REGION16 *src, const RECTANGLE_16 *arg2) {
|
||||
assert(src);
|
||||
assert(src->data);
|
||||
|
||||
const RECTANGLE_16 *rect, *endPtr, *srcExtents;;
|
||||
int nbRects;
|
||||
|
||||
rect = region16_rects(src, &nbRects);
|
||||
if (!nbRects)
|
||||
return FALSE;
|
||||
|
||||
srcExtents = region16_extents(src);
|
||||
if (nbRects == 1)
|
||||
return rectangles_intersects(srcExtents, arg2);
|
||||
|
||||
if (!rectangles_intersects(srcExtents, arg2))
|
||||
return FALSE;
|
||||
|
||||
endPtr = rect + nbRects;
|
||||
|
||||
for (endPtr = rect + nbRects; rect < endPtr; rect++)
|
||||
{
|
||||
if (rectangles_intersects(rect, arg2))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL region16_intersect_rect(REGION16 *dst, const REGION16 *src, const RECTANGLE_16 *rect)
|
||||
{
|
||||
assert(src);
|
||||
assert(src->data);
|
||||
|
||||
REGION16_DATA *newItems;
|
||||
const RECTANGLE_16 *srcPtr, *endPtr, *srcExtents;
|
||||
RECTANGLE_16 *dstPtr;
|
||||
int nbRects, usedRects;
|
||||
RECTANGLE_16 common, newExtents;
|
||||
|
||||
srcPtr = region16_rects(src, &nbRects);
|
||||
if (!nbRects)
|
||||
{
|
||||
region16_clear(dst);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
srcExtents = region16_extents(src);
|
||||
if (nbRects == 1)
|
||||
{
|
||||
BOOL intersects = rectangles_intersection(srcExtents, rect, &common);
|
||||
|
||||
region16_clear(dst);
|
||||
if (intersects)
|
||||
return region16_union_rect(dst, dst, &common);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
newItems = allocateRegion(nbRects);
|
||||
if (!newItems)
|
||||
return FALSE;
|
||||
dstPtr = (RECTANGLE_16 *)(newItems + 1);
|
||||
usedRects = 0;
|
||||
ZeroMemory(&newExtents, sizeof(newExtents));
|
||||
|
||||
/* accumulate intersecting rectangles, the final region16_simplify_bands() will
|
||||
* do all the bad job to recreate correct rectangles
|
||||
*/
|
||||
for(endPtr = srcPtr + nbRects; srcPtr < endPtr; srcPtr++)
|
||||
{
|
||||
if (rectangles_intersection(srcPtr, rect, &common))
|
||||
{
|
||||
*dstPtr = common;
|
||||
usedRects++;
|
||||
dstPtr++;
|
||||
|
||||
newExtents.top = MIN(common.top, newExtents.top);
|
||||
newExtents.left = MIN(common.left, newExtents.left);
|
||||
newExtents.bottom = MAX(common.bottom, newExtents.bottom);
|
||||
newExtents.right = MIN(common.right, newExtents.right);
|
||||
}
|
||||
}
|
||||
|
||||
newItems->nbRects = usedRects;
|
||||
newItems->size = sizeof(REGION16_DATA) + usedRects * sizeof(RECTANGLE_16);
|
||||
|
||||
if (dst->data->size)
|
||||
free(dst->data);
|
||||
|
||||
dst->data = realloc(newItems, newItems->size);
|
||||
if (!dst->data)
|
||||
return FALSE;
|
||||
|
||||
dst->extents = newExtents;
|
||||
return region16_simplify_bands(dst);
|
||||
}
|
||||
|
||||
void region16_uninit(REGION16 *region) {
|
||||
assert(region);
|
||||
assert(region->data);
|
||||
|
||||
if(region->data->size)
|
||||
free(region->data);
|
||||
region->data = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
48
libfreerdp/utils/test/CMakeLists.txt
Normal file
48
libfreerdp/utils/test/CMakeLists.txt
Normal file
@ -0,0 +1,48 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# tests for utils cmake build script
|
||||
#
|
||||
# Copyright © 2014 Thincast Technologies GmbH
|
||||
# Copyright © 2014 Hardening <contact@hardening-consulting.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set(MODULE_NAME "TestFreeRDPUtils")
|
||||
set(MODULE_PREFIX "TEST_FREERDP_UTILS")
|
||||
|
||||
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
|
||||
|
||||
set(${MODULE_PREFIX}_TESTS
|
||||
TestFreeRDPRegion.c)
|
||||
|
||||
create_test_sourcelist(${MODULE_PREFIX}_SRCS
|
||||
${${MODULE_PREFIX}_DRIVER}
|
||||
${${MODULE_PREFIX}_TESTS})
|
||||
|
||||
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
|
||||
|
||||
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MONOLITHIC ${MONOLITHIC_BUILD}
|
||||
MODULE freerdp
|
||||
MODULES freerdp-utils)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
|
||||
|
||||
foreach(test ${${MODULE_PREFIX}_TESTS})
|
||||
get_filename_component(TestName ${test} NAME_WE)
|
||||
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
|
||||
endforeach()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Test")
|
||||
|
662
libfreerdp/utils/test/TestFreeRDPRegion.c
Normal file
662
libfreerdp/utils/test/TestFreeRDPRegion.c
Normal file
@ -0,0 +1,662 @@
|
||||
/**
|
||||
* Copyright © 2014 Thincast Technologies GmbH
|
||||
* Copyright © 2014 Hardening <contact@hardening-consulting.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee, provided
|
||||
* that the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation, and that the name of the copyright holders not be used in
|
||||
* advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. The copyright holders make
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/print.h>
|
||||
|
||||
#include <freerdp/utils/region.h>
|
||||
|
||||
|
||||
static BOOL compareRectangles(const RECTANGLE_16 *src1, const RECTANGLE_16 *src2, int nb)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i< nb; i++, src1++, src2++)
|
||||
{
|
||||
if (memcmp(src1, src2, sizeof(RECTANGLE_16)))
|
||||
{
|
||||
fprintf(stderr, "expecting rect %d (%d,%d-%d,%d) and have (%d,%d-%d,%d)\n",
|
||||
i, src2->left, src2->top, src2->right, src2->bottom,
|
||||
src1->left, src1->top, src1->right, src1->bottom
|
||||
);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static int test_basic() {
|
||||
REGION16 region;
|
||||
int retCode = -1;
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects;
|
||||
|
||||
/* R1 + R2 ==> disjointed rects */
|
||||
RECTANGLE_16 r1 = { 0, 101, 200, 201};
|
||||
RECTANGLE_16 r2 = {150, 301, 250, 401};
|
||||
|
||||
RECTANGLE_16 r1_r2[] = {
|
||||
{0, 101, 200, 201},
|
||||
{150, 301, 250, 401}
|
||||
};
|
||||
|
||||
/* r1 */
|
||||
region16_init(®ion);
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 1 || memcmp(rects, &r1, sizeof(RECTANGLE_16)))
|
||||
goto out;
|
||||
|
||||
/* r1 + r2 */
|
||||
if (!region16_union_rect(®ion, ®ion, &r2))
|
||||
goto out;;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 2 || !compareRectangles(rects, r1_r2, nbRects))
|
||||
goto out;
|
||||
|
||||
|
||||
/* clear region */
|
||||
region16_clear(®ion);
|
||||
region16_rects(®ion, &nbRects);
|
||||
if (nbRects)
|
||||
goto out;
|
||||
|
||||
retCode = 0;
|
||||
out:
|
||||
region16_uninit(®ion);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
|
||||
static int test_r1_r3() {
|
||||
REGION16 region;
|
||||
int retCode = -1;
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects;
|
||||
|
||||
RECTANGLE_16 r1 = { 0, 101, 200, 201};
|
||||
RECTANGLE_16 r3 = {150, 151, 250, 251};
|
||||
RECTANGLE_16 r1_r3[] = {
|
||||
{ 0, 101, 200, 151},
|
||||
{ 0, 151, 250, 201},
|
||||
{150, 201, 250, 251}
|
||||
};
|
||||
|
||||
region16_init(®ion);
|
||||
/*
|
||||
* +===============================================================
|
||||
* |
|
||||
* |+-----+ +-----+
|
||||
* || r1 | | |
|
||||
* || +-+------+ +-----+--------+
|
||||
* || | r3 | | |
|
||||
* |+---+ | ====> +-----+--------+
|
||||
* | | | | |
|
||||
* | +--------+ +--------+
|
||||
*/
|
||||
|
||||
/* R1 + R3 */
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r3))
|
||||
goto out;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 3 || !compareRectangles(rects, r1_r3, nbRects))
|
||||
goto out;
|
||||
|
||||
|
||||
/* R3 + R1 */
|
||||
region16_clear(®ion);
|
||||
if (!region16_union_rect(®ion, ®ion, &r3))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 3 || !compareRectangles(rects, r1_r3, nbRects))
|
||||
goto out;
|
||||
|
||||
retCode = 0;
|
||||
out:
|
||||
region16_uninit(®ion);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
|
||||
static int test_r9_r10() {
|
||||
REGION16 region;
|
||||
int retCode = -1;
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects;
|
||||
|
||||
/*
|
||||
* +===============================================================
|
||||
* |
|
||||
* | +---+ +---+
|
||||
* |+--|r10|-+ +--+---+-+
|
||||
* ||r9| | | | |
|
||||
* || | | | | |
|
||||
* || | | | =====> | |
|
||||
* || | | | | |
|
||||
* || | | | | |
|
||||
* |+--| |-+ +--+---+-+
|
||||
* | +---+ +---+
|
||||
*/
|
||||
RECTANGLE_16 r9 = { 0, 100, 400, 200};
|
||||
RECTANGLE_16 r10 = {200, 0, 300, 300};
|
||||
RECTANGLE_16 r9_r10[] = {
|
||||
{200, 0, 300, 100},
|
||||
{ 0, 100, 400, 200},
|
||||
{200, 200, 300, 300},
|
||||
};
|
||||
|
||||
region16_init(®ion);
|
||||
if (!region16_union_rect(®ion, ®ion, &r9))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r10))
|
||||
goto out;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 3 || !compareRectangles(rects, r9_r10, nbRects))
|
||||
goto out;
|
||||
|
||||
retCode = 0;
|
||||
out:
|
||||
region16_uninit(®ion);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
|
||||
static int test_r1_r5() {
|
||||
REGION16 region;
|
||||
int retCode = -1;
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects;
|
||||
|
||||
RECTANGLE_16 r1 = { 0, 101, 200, 201};
|
||||
RECTANGLE_16 r5 = {150, 121, 300, 131};
|
||||
|
||||
RECTANGLE_16 r1_r5[] = {
|
||||
{ 0, 101, 200, 121},
|
||||
{ 0, 121, 300, 131},
|
||||
{ 0, 131, 200, 201}
|
||||
};
|
||||
|
||||
region16_init(®ion);
|
||||
/*
|
||||
* +===============================================================
|
||||
* |
|
||||
* |+--------+ +--------+
|
||||
* || r1 | | |
|
||||
* || +--+----+ +--------+----+
|
||||
* || | r5 | =====> | |
|
||||
* || +-------+ +--------+----+
|
||||
* || | | |
|
||||
* |+--------+ +--------+
|
||||
* |
|
||||
*
|
||||
*/
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r5))
|
||||
goto out;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 3 || !compareRectangles(rects, r1_r5, nbRects))
|
||||
goto out;
|
||||
|
||||
|
||||
retCode = 0;
|
||||
out:
|
||||
region16_uninit(®ion);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
static int test_r1_r6() {
|
||||
REGION16 region;
|
||||
int retCode = -1;
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects;
|
||||
|
||||
RECTANGLE_16 r1 = { 0, 101, 200, 201};
|
||||
RECTANGLE_16 r6 = {150, 121, 170, 131};
|
||||
|
||||
region16_init(®ion);
|
||||
/*
|
||||
* +===============================================================
|
||||
* |
|
||||
* |+--------+ +--------+
|
||||
* || r1 | | |
|
||||
* || +--+ | | |
|
||||
* || |r6| | =====> | |
|
||||
* || +--+ | | |
|
||||
* || | | |
|
||||
* |+--------+ +--------+
|
||||
* |
|
||||
*/
|
||||
region16_clear(®ion);
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r6))
|
||||
goto out;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 1 || !compareRectangles(rects, &r1, nbRects))
|
||||
goto out;
|
||||
|
||||
retCode = 0;
|
||||
out:
|
||||
region16_uninit(®ion);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
|
||||
static int test_r1_r2_r4() {
|
||||
REGION16 region;
|
||||
int retCode = -1;
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects;
|
||||
RECTANGLE_16 r1 = { 0, 101, 200, 201};
|
||||
RECTANGLE_16 r2 = {150, 301, 250, 401};
|
||||
RECTANGLE_16 r4 = {150, 251, 250, 301};
|
||||
RECTANGLE_16 r1_r2_r4[] = {
|
||||
{ 0, 101, 200, 201},
|
||||
{150, 251, 250, 401}
|
||||
};
|
||||
|
||||
/*
|
||||
* +===============================================================
|
||||
* |
|
||||
* |+-----+ +-----+
|
||||
* || r1 | | |
|
||||
* || | | |
|
||||
* || | | |
|
||||
* |+-----+ ====> +-----+
|
||||
* |
|
||||
* | +--------+ +--------+
|
||||
* | | r4 | | |
|
||||
* | +--------+ | |
|
||||
* | | r2 | | |
|
||||
* | | | | |
|
||||
* | +--------+ +--------+
|
||||
*
|
||||
*/
|
||||
region16_init(®ion);
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r2))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r4))
|
||||
goto out;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 2 || !compareRectangles(rects, r1_r2_r4, nbRects))
|
||||
goto out;
|
||||
|
||||
retCode = 0;
|
||||
out:
|
||||
region16_uninit(®ion);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
|
||||
static int test_r1_r7_r8() {
|
||||
REGION16 region;
|
||||
int retCode = -1;
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects;
|
||||
RECTANGLE_16 r1 = { 0, 101, 200, 201};
|
||||
RECTANGLE_16 r7 = {300, 101, 500, 201};
|
||||
RECTANGLE_16 r8 = {150, 121, 400, 131};
|
||||
|
||||
RECTANGLE_16 r1_r7_r8[] = {
|
||||
{ 0, 101, 200, 121},
|
||||
{300, 101, 500, 121},
|
||||
{ 0, 121, 500, 131},
|
||||
{ 0, 131, 200, 201},
|
||||
{300, 131, 500, 201},
|
||||
};
|
||||
|
||||
/*
|
||||
* +===============================================================
|
||||
* |
|
||||
* |+--------+ +--------+ +--------+ +--------+
|
||||
* || r1 | | r7 | | | | |
|
||||
* || +------------+ | +--------+---+--------+
|
||||
* || | r8 | | =====> | |
|
||||
* || +------------+ | +--------+---+--------+
|
||||
* || | | | | | | |
|
||||
* |+--------+ +--------+ +--------+ +--------+
|
||||
* |
|
||||
*/
|
||||
region16_init(®ion);
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r7))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r8))
|
||||
goto out;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 5 || !compareRectangles(rects, r1_r7_r8, nbRects))
|
||||
goto out;
|
||||
|
||||
region16_clear(®ion);
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r8))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r7))
|
||||
goto out;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 5 || !compareRectangles(rects, r1_r7_r8, nbRects))
|
||||
goto out;
|
||||
|
||||
region16_clear(®ion);
|
||||
if (!region16_union_rect(®ion, ®ion, &r8))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r7))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 5 || !compareRectangles(rects, r1_r7_r8, nbRects))
|
||||
goto out;
|
||||
|
||||
retCode = 0;
|
||||
out:
|
||||
region16_uninit(®ion);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
|
||||
static int test_r1_r2_r3_r4() {
|
||||
REGION16 region;
|
||||
int retCode = -1;
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects;
|
||||
RECTANGLE_16 r1 = { 0, 101, 200, 201};
|
||||
RECTANGLE_16 r2 = {150, 301, 250, 401};
|
||||
RECTANGLE_16 r3 = {150, 151, 250, 251};
|
||||
RECTANGLE_16 r4 = {150, 251, 250, 301};
|
||||
|
||||
RECTANGLE_16 r1_r2_r3[] = {
|
||||
{ 0, 101, 200, 151},
|
||||
{ 0, 151, 250, 201},
|
||||
{150, 201, 250, 251},
|
||||
{150, 301, 250, 401}
|
||||
};
|
||||
|
||||
RECTANGLE_16 r1_r2_r3_r4[] = {
|
||||
{ 0, 101, 200, 151},
|
||||
{ 0, 151, 250, 201},
|
||||
{150, 201, 250, 401}
|
||||
};
|
||||
|
||||
region16_init(®ion);
|
||||
/*
|
||||
* +===============================================================
|
||||
* |
|
||||
* |+-----+ +-----+
|
||||
* || r1 | | |
|
||||
* || +-+------+ +-----+--------+
|
||||
* || | r3 | | |
|
||||
* |+---+ | ====> +-----+--------+
|
||||
* | | | | |
|
||||
* | +--------+ +--------+
|
||||
* | +--------+ +--------+
|
||||
* | | r2 | | |
|
||||
* | | | | |
|
||||
* | +--------+ +--------+
|
||||
*/
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r2))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r3))
|
||||
goto out;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 4 || !compareRectangles(rects, r1_r2_r3, 4))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* +===============================================================
|
||||
* |
|
||||
* |+-----+ +-----+
|
||||
* || | | |
|
||||
* |+-----+--------+ +-----+--------+
|
||||
* || | ==> | |
|
||||
* |+-----+--------+ +-----+--------+
|
||||
* | | | | |
|
||||
* | +--------+ | |
|
||||
* | | + r4 | | |
|
||||
* | +--------+ | |
|
||||
* | | | | |
|
||||
* | | | | |
|
||||
* | +--------+ +--------+
|
||||
*/
|
||||
if (!region16_union_rect(®ion, ®ion, &r4))
|
||||
goto out;
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 3 || !compareRectangles(rects, r1_r2_r3_r4, 3))
|
||||
goto out;
|
||||
|
||||
retCode = 0;
|
||||
out:
|
||||
region16_uninit(®ion);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
|
||||
static int test_from_weston()
|
||||
{
|
||||
/*
|
||||
* 0: 0,0 -> 640,32 (w=640 h=32)
|
||||
* 1: 236,169 -> 268,201 (w=32 h=32)
|
||||
* 2: 246,258 -> 278,290 (w=32 h=32)
|
||||
*/
|
||||
REGION16 region;
|
||||
int retCode = -1;
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects;
|
||||
RECTANGLE_16 r1 = { 0, 0, 640, 32};
|
||||
RECTANGLE_16 r2 = {236, 169, 268, 201};
|
||||
RECTANGLE_16 r3 = {246, 258, 278, 290};
|
||||
|
||||
RECTANGLE_16 r1_r2_r3[] = {
|
||||
{ 0, 0, 640, 32},
|
||||
{236, 169, 268, 201},
|
||||
{246, 258, 278, 290}
|
||||
};
|
||||
|
||||
region16_init(®ion);
|
||||
/*
|
||||
* +===============================================================
|
||||
* |+-------------------------------------------------------------+
|
||||
* || r1 |
|
||||
* |+-------------------------------------------------------------+
|
||||
* |
|
||||
* | +---------------+
|
||||
* | | r2 |
|
||||
* | +---------------+
|
||||
* |
|
||||
* | +---------------+
|
||||
* | | r3 |
|
||||
* | +---------------+
|
||||
* |
|
||||
*/
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r2))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r3))
|
||||
goto out;
|
||||
|
||||
rects = region16_rects(®ion, &nbRects);
|
||||
if (!rects || nbRects != 3 || !compareRectangles(rects, r1_r2_r3, 3))
|
||||
goto out;
|
||||
|
||||
retCode = 0;
|
||||
out:
|
||||
region16_uninit(®ion);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
static int test_r1_inter_r3() {
|
||||
REGION16 region, intersection;
|
||||
int retCode = -1;
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects;
|
||||
RECTANGLE_16 r1 = { 0, 101, 200, 201};
|
||||
RECTANGLE_16 r3 = {150, 151, 250, 251};
|
||||
|
||||
RECTANGLE_16 r1_inter_r3[] = {
|
||||
{150, 151, 200, 201},
|
||||
};
|
||||
|
||||
region16_init(®ion);
|
||||
region16_init(&intersection);
|
||||
|
||||
/*
|
||||
* +===============================================================
|
||||
* |
|
||||
* |+-----+
|
||||
* || r1 |
|
||||
* || +-+------+ +-+
|
||||
* || | r3 | r1&r3 | |
|
||||
* |+---+ | ====> +-+
|
||||
* | | |
|
||||
* | +--------+
|
||||
*/
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
if (!region16_intersects_rect(®ion, &r3))
|
||||
goto out;
|
||||
|
||||
if (!region16_intersect_rect(&intersection, ®ion, &r3))
|
||||
goto out;
|
||||
rects = region16_rects(&intersection, &nbRects);
|
||||
if (!rects || nbRects != 1 || !compareRectangles(rects, r1_inter_r3, nbRects))
|
||||
goto out;
|
||||
|
||||
|
||||
retCode = 0;
|
||||
out:
|
||||
region16_uninit(®ion);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
static int test_r1_r3_inter_r11() {
|
||||
REGION16 region, intersection;
|
||||
int retCode = -1;
|
||||
const RECTANGLE_16 *rects;
|
||||
int nbRects;
|
||||
RECTANGLE_16 r1 = { 0, 101, 200, 201};
|
||||
RECTANGLE_16 r3 = {150, 151, 250, 251};
|
||||
RECTANGLE_16 r11 ={170, 151, 600, 301};
|
||||
|
||||
RECTANGLE_16 r1_r3_inter_r11[] = {
|
||||
{170, 151, 250, 251},
|
||||
};
|
||||
|
||||
region16_init(®ion);
|
||||
region16_init(&intersection);
|
||||
|
||||
/*
|
||||
* +===============================================================
|
||||
* |
|
||||
* |+-----+
|
||||
* || |
|
||||
* || +------+
|
||||
* || r1+r3 | (r1+r3) & r11
|
||||
* || +----------------+ +--------+
|
||||
* |+---+ | | | ====> | |
|
||||
* | | | | | | |
|
||||
* | | | | | | |
|
||||
* | +-|------+ | +--------+
|
||||
* | | r11 |
|
||||
* | +----------------+
|
||||
*
|
||||
*
|
||||
* R1+R3 is made of 3 bands, R11 overlap the second and the third band. The
|
||||
* intersection is made of two band that must be reassembled to give only
|
||||
* one
|
||||
*/
|
||||
if (!region16_union_rect(®ion, ®ion, &r1))
|
||||
goto out;
|
||||
if (!region16_union_rect(®ion, ®ion, &r3))
|
||||
goto out;
|
||||
|
||||
if (!region16_intersects_rect(®ion, &r11))
|
||||
goto out;
|
||||
|
||||
if (!region16_intersect_rect(&intersection, ®ion, &r11))
|
||||
goto out;
|
||||
rects = region16_rects(&intersection, &nbRects);
|
||||
if (!rects || nbRects != 1 || !compareRectangles(rects, r1_r3_inter_r11, nbRects))
|
||||
goto out;
|
||||
|
||||
retCode = 0;
|
||||
out:
|
||||
region16_uninit(&intersection);
|
||||
region16_uninit(®ion);
|
||||
return retCode;
|
||||
}
|
||||
|
||||
|
||||
typedef int (*TestFunction)();
|
||||
struct UnitaryTest {
|
||||
const char *name;
|
||||
TestFunction func;
|
||||
};
|
||||
|
||||
struct UnitaryTest tests[] = {
|
||||
{"Basic trivial tests", test_basic},
|
||||
{"R1+R3 and R3+R1", test_r1_r3},
|
||||
{"R1+R5", test_r1_r5},
|
||||
{"R1+R6", test_r1_r6},
|
||||
{"R9+R10", test_r9_r10},
|
||||
{"R1+R2+R4", test_r1_r2_r4},
|
||||
{"R1+R7+R8 in many orders", test_r1_r7_r8},
|
||||
{"R1+R2+R3+R4", test_r1_r2_r3_r4},
|
||||
{"data from weston", test_from_weston},
|
||||
{"R1 & R3", test_r1_inter_r3},
|
||||
{"(R1+R3)&R11 (band merge)",test_r1_r3_inter_r11},
|
||||
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
int TestFreeRDPRegion(int argc, char* argv[])
|
||||
{
|
||||
int i, testNb;
|
||||
int retCode = -1;
|
||||
|
||||
for (i = 0; tests[i].func; i++)
|
||||
{
|
||||
testNb++;
|
||||
fprintf(stderr, "%d: %s\n", testNb, tests[i].name);
|
||||
retCode = tests[i].func();
|
||||
if (retCode < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (retCode < 0)
|
||||
fprintf(stderr, "failed for test %d\n", testNb);
|
||||
|
||||
return retCode;
|
||||
}
|
Loading…
Reference in New Issue
Block a user