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:
Hardening 2014-01-31 17:02:50 +01:00
parent 1c0e874b5b
commit d1e75efb8c
6 changed files with 1598 additions and 0 deletions

3
.gitignore vendored
View File

@ -102,6 +102,9 @@ server/Sample/sfreerdp-server
server/X11/xfreerdp-server
xcode
# Generated test code
libfreerdp/utils/test/TestFreeRDPUtils.c
# Other
*~
*.dir

View 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___ */

View File

@ -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
View 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 &region->extents;
}
static RECTANGLE_16 *region16_extents_noconst(REGION16 *region)
{
return &region->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(&region->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;
}

View 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")

View 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(&region);
if (!region16_union_rect(&region, &region, &r1))
goto out;;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 1 || memcmp(rects, &r1, sizeof(RECTANGLE_16)))
goto out;
/* r1 + r2 */
if (!region16_union_rect(&region, &region, &r2))
goto out;;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 2 || !compareRectangles(rects, r1_r2, nbRects))
goto out;
/* clear region */
region16_clear(&region);
region16_rects(&region, &nbRects);
if (nbRects)
goto out;
retCode = 0;
out:
region16_uninit(&region);
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(&region);
/*
* +===============================================================
* |
* |+-----+ +-----+
* || r1 | | |
* || +-+------+ +-----+--------+
* || | r3 | | |
* |+---+ | ====> +-----+--------+
* | | | | |
* | +--------+ +--------+
*/
/* R1 + R3 */
if (!region16_union_rect(&region, &region, &r1))
goto out;
if (!region16_union_rect(&region, &region, &r3))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 3 || !compareRectangles(rects, r1_r3, nbRects))
goto out;
/* R3 + R1 */
region16_clear(&region);
if (!region16_union_rect(&region, &region, &r3))
goto out;
if (!region16_union_rect(&region, &region, &r1))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 3 || !compareRectangles(rects, r1_r3, nbRects))
goto out;
retCode = 0;
out:
region16_uninit(&region);
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(&region);
if (!region16_union_rect(&region, &region, &r9))
goto out;
if (!region16_union_rect(&region, &region, &r10))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 3 || !compareRectangles(rects, r9_r10, nbRects))
goto out;
retCode = 0;
out:
region16_uninit(&region);
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(&region);
/*
* +===============================================================
* |
* |+--------+ +--------+
* || r1 | | |
* || +--+----+ +--------+----+
* || | r5 | =====> | |
* || +-------+ +--------+----+
* || | | |
* |+--------+ +--------+
* |
*
*/
if (!region16_union_rect(&region, &region, &r1))
goto out;
if (!region16_union_rect(&region, &region, &r5))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 3 || !compareRectangles(rects, r1_r5, nbRects))
goto out;
retCode = 0;
out:
region16_uninit(&region);
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(&region);
/*
* +===============================================================
* |
* |+--------+ +--------+
* || r1 | | |
* || +--+ | | |
* || |r6| | =====> | |
* || +--+ | | |
* || | | |
* |+--------+ +--------+
* |
*/
region16_clear(&region);
if (!region16_union_rect(&region, &region, &r1))
goto out;
if (!region16_union_rect(&region, &region, &r6))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 1 || !compareRectangles(rects, &r1, nbRects))
goto out;
retCode = 0;
out:
region16_uninit(&region);
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(&region);
if (!region16_union_rect(&region, &region, &r1))
goto out;
if (!region16_union_rect(&region, &region, &r2))
goto out;
if (!region16_union_rect(&region, &region, &r4))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 2 || !compareRectangles(rects, r1_r2_r4, nbRects))
goto out;
retCode = 0;
out:
region16_uninit(&region);
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(&region);
if (!region16_union_rect(&region, &region, &r1))
goto out;
if (!region16_union_rect(&region, &region, &r7))
goto out;
if (!region16_union_rect(&region, &region, &r8))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 5 || !compareRectangles(rects, r1_r7_r8, nbRects))
goto out;
region16_clear(&region);
if (!region16_union_rect(&region, &region, &r1))
goto out;
if (!region16_union_rect(&region, &region, &r8))
goto out;
if (!region16_union_rect(&region, &region, &r7))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 5 || !compareRectangles(rects, r1_r7_r8, nbRects))
goto out;
region16_clear(&region);
if (!region16_union_rect(&region, &region, &r8))
goto out;
if (!region16_union_rect(&region, &region, &r7))
goto out;
if (!region16_union_rect(&region, &region, &r1))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 5 || !compareRectangles(rects, r1_r7_r8, nbRects))
goto out;
retCode = 0;
out:
region16_uninit(&region);
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(&region);
/*
* +===============================================================
* |
* |+-----+ +-----+
* || r1 | | |
* || +-+------+ +-----+--------+
* || | r3 | | |
* |+---+ | ====> +-----+--------+
* | | | | |
* | +--------+ +--------+
* | +--------+ +--------+
* | | r2 | | |
* | | | | |
* | +--------+ +--------+
*/
if (!region16_union_rect(&region, &region, &r1))
goto out;
if (!region16_union_rect(&region, &region, &r2))
goto out;
if (!region16_union_rect(&region, &region, &r3))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 4 || !compareRectangles(rects, r1_r2_r3, 4))
goto out;
/*
* +===============================================================
* |
* |+-----+ +-----+
* || | | |
* |+-----+--------+ +-----+--------+
* || | ==> | |
* |+-----+--------+ +-----+--------+
* | | | | |
* | +--------+ | |
* | | + r4 | | |
* | +--------+ | |
* | | | | |
* | | | | |
* | +--------+ +--------+
*/
if (!region16_union_rect(&region, &region, &r4))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 3 || !compareRectangles(rects, r1_r2_r3_r4, 3))
goto out;
retCode = 0;
out:
region16_uninit(&region);
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(&region);
/*
* +===============================================================
* |+-------------------------------------------------------------+
* || r1 |
* |+-------------------------------------------------------------+
* |
* | +---------------+
* | | r2 |
* | +---------------+
* |
* | +---------------+
* | | r3 |
* | +---------------+
* |
*/
if (!region16_union_rect(&region, &region, &r1))
goto out;
if (!region16_union_rect(&region, &region, &r2))
goto out;
if (!region16_union_rect(&region, &region, &r3))
goto out;
rects = region16_rects(&region, &nbRects);
if (!rects || nbRects != 3 || !compareRectangles(rects, r1_r2_r3, 3))
goto out;
retCode = 0;
out:
region16_uninit(&region);
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(&region);
region16_init(&intersection);
/*
* +===============================================================
* |
* |+-----+
* || r1 |
* || +-+------+ +-+
* || | r3 | r1&r3 | |
* |+---+ | ====> +-+
* | | |
* | +--------+
*/
if (!region16_union_rect(&region, &region, &r1))
goto out;
if (!region16_intersects_rect(&region, &r3))
goto out;
if (!region16_intersect_rect(&intersection, &region, &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(&region);
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(&region);
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(&region, &region, &r1))
goto out;
if (!region16_union_rect(&region, &region, &r3))
goto out;
if (!region16_intersects_rect(&region, &r11))
goto out;
if (!region16_intersect_rect(&intersection, &region, &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(&region);
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;
}