xenocara/xserver/glamor/glamor_largepixmap.c
matthieu 4f58590a42 Update to xserver 1.16.1.
Tested by naddy@, jsg@ & kettenis@
2014-09-27 17:52:59 +00:00

1418 lines
59 KiB
C

#include <stdlib.h>
#include "glamor_priv.h"
/**
* Clip the boxes regards to each pixmap's block array.
*
* Should translate the region to relative coords to the pixmap,
* start at (0,0).
*/
#if 0
//#define DEBUGF(str, ...) do {} while(0)
#define DEBUGF(str, ...) ErrorF(str, ##__VA_ARGS__)
//#define DEBUGRegionPrint(x) do {} while (0)
#define DEBUGRegionPrint RegionPrint
#endif
static glamor_pixmap_clipped_regions *
__glamor_compute_clipped_regions(int block_w,
int block_h,
int block_stride,
int x, int y,
int w, int h,
RegionPtr region,
int *n_region, int reverse, int upsidedown)
{
glamor_pixmap_clipped_regions *clipped_regions;
BoxPtr extent;
int start_x, start_y, end_x, end_y;
int start_block_x, start_block_y;
int end_block_x, end_block_y;
int loop_start_block_x, loop_start_block_y;
int loop_end_block_x, loop_end_block_y;
int loop_block_stride;
int i, j, delta_i, delta_j;
RegionRec temp_region;
RegionPtr current_region;
int block_idx;
int k = 0;
int temp_block_idx;
extent = RegionExtents(region);
start_x = MAX(x, extent->x1);
start_y = MAX(y, extent->y1);
end_x = MIN(x + w, extent->x2);
end_y = MIN(y + h, extent->y2);
DEBUGF("start compute clipped regions:\n");
DEBUGF("block w %d h %d x %d y %d w %d h %d, block_stride %d \n",
block_w, block_h, x, y, w, h, block_stride);
DEBUGRegionPrint(region);
DEBUGF("start_x %d start_y %d end_x %d end_y %d \n", start_x, start_y,
end_x, end_y);
if (start_x >= end_x || start_y >= end_y) {
*n_region = 0;
return NULL;
}
start_block_x = (start_x - x) / block_w;
start_block_y = (start_y - y) / block_h;
end_block_x = (end_x - x) / block_w;
end_block_y = (end_y - y) / block_h;
clipped_regions = calloc((end_block_x - start_block_x + 1)
* (end_block_y - start_block_y + 1),
sizeof(*clipped_regions));
DEBUGF("startx %d starty %d endx %d endy %d \n",
start_x, start_y, end_x, end_y);
DEBUGF("start_block_x %d end_block_x %d \n", start_block_x, end_block_x);
DEBUGF("start_block_y %d end_block_y %d \n", start_block_y, end_block_y);
if (!reverse) {
loop_start_block_x = start_block_x;
loop_end_block_x = end_block_x + 1;
delta_i = 1;
}
else {
loop_start_block_x = end_block_x;
loop_end_block_x = start_block_x - 1;
delta_i = -1;
}
if (!upsidedown) {
loop_start_block_y = start_block_y;
loop_end_block_y = end_block_y + 1;
delta_j = 1;
}
else {
loop_start_block_y = end_block_y;
loop_end_block_y = start_block_y - 1;
delta_j = -1;
}
loop_block_stride = delta_j * block_stride;
block_idx = (loop_start_block_y - delta_j) * block_stride;
for (j = loop_start_block_y; j != loop_end_block_y; j += delta_j) {
block_idx += loop_block_stride;
temp_block_idx = block_idx + loop_start_block_x;
for (i = loop_start_block_x;
i != loop_end_block_x; i += delta_i, temp_block_idx += delta_i) {
BoxRec temp_box;
temp_box.x1 = x + i * block_w;
temp_box.y1 = y + j * block_h;
temp_box.x2 = MIN(temp_box.x1 + block_w, end_x);
temp_box.y2 = MIN(temp_box.y1 + block_h, end_y);
RegionInitBoxes(&temp_region, &temp_box, 1);
DEBUGF("block idx %d \n", temp_block_idx);
DEBUGRegionPrint(&temp_region);
current_region = RegionCreate(NULL, 4);
RegionIntersect(current_region, &temp_region, region);
DEBUGF("i %d j %d region: \n", i, j);
DEBUGRegionPrint(current_region);
if (RegionNumRects(current_region)) {
clipped_regions[k].region = current_region;
clipped_regions[k].block_idx = temp_block_idx;
k++;
}
else
RegionDestroy(current_region);
RegionUninit(&temp_region);
}
}
*n_region = k;
return clipped_regions;
}
/**
* Do a two round clipping,
* first is to clip the region regard to current pixmap's
* block array. Then for each clipped region, do a inner
* block clipping. This is to make sure the final result
* will be shapped by inner_block_w and inner_block_h, and
* the final region also will not cross the pixmap's block
* boundary.
*
* This is mainly used by transformation support when do
* compositing.
*/
glamor_pixmap_clipped_regions *
glamor_compute_clipped_regions_ext(glamor_pixmap_private *pixmap_priv,
RegionPtr region,
int *n_region,
int inner_block_w, int inner_block_h,
int reverse, int upsidedown)
{
glamor_pixmap_clipped_regions *clipped_regions, *inner_regions,
*result_regions;
int i, j, x, y, k, inner_n_regions;
int width, height;
glamor_pixmap_private_large_t *priv;
priv = &pixmap_priv->large;
DEBUGF("ext called \n");
if (pixmap_priv->type != GLAMOR_TEXTURE_LARGE) {
clipped_regions = calloc(1, sizeof(*clipped_regions));
if (clipped_regions == NULL) {
*n_region = 0;
return NULL;
}
clipped_regions[0].region = RegionCreate(NULL, 1);
clipped_regions[0].block_idx = 0;
RegionCopy(clipped_regions[0].region, region);
*n_region = 1;
priv->block_w = priv->base.pixmap->drawable.width;
priv->block_h = priv->base.pixmap->drawable.height;
priv->box_array = &priv->box;
priv->box.x1 = priv->box.y1 = 0;
priv->box.x2 = priv->block_w;
priv->box.y2 = priv->block_h;
}
else {
clipped_regions = __glamor_compute_clipped_regions(priv->block_w,
priv->block_h,
priv->block_wcnt,
0, 0,
priv->base.pixmap->
drawable.width,
priv->base.pixmap->
drawable.height,
region, n_region,
reverse, upsidedown);
if (clipped_regions == NULL) {
*n_region = 0;
return NULL;
}
}
if (inner_block_w >= priv->block_w && inner_block_h >= priv->block_h)
return clipped_regions;
result_regions = calloc(*n_region
* ((priv->block_w + inner_block_w - 1) /
inner_block_w)
* ((priv->block_h + inner_block_h - 1) /
inner_block_h), sizeof(*result_regions));
k = 0;
for (i = 0; i < *n_region; i++) {
x = priv->box_array[clipped_regions[i].block_idx].x1;
y = priv->box_array[clipped_regions[i].block_idx].y1;
width = priv->box_array[clipped_regions[i].block_idx].x2 - x;
height = priv->box_array[clipped_regions[i].block_idx].y2 - y;
inner_regions = __glamor_compute_clipped_regions(inner_block_w,
inner_block_h,
0, x, y,
width,
height,
clipped_regions[i].
region,
&inner_n_regions,
reverse, upsidedown);
for (j = 0; j < inner_n_regions; j++) {
result_regions[k].region = inner_regions[j].region;
result_regions[k].block_idx = clipped_regions[i].block_idx;
k++;
}
free(inner_regions);
}
*n_region = k;
free(clipped_regions);
return result_regions;
}
/*
*
* For the repeat pad mode, we can simply convert the region and
* let the out-of-box region can cover the needed edge of the source/mask
* Then apply a normal clip we can get what we want.
*/
static RegionPtr
_glamor_convert_pad_region(RegionPtr region, int w, int h)
{
RegionPtr pad_region;
int nrect;
BoxPtr box;
int overlap;
nrect = RegionNumRects(region);
box = RegionRects(region);
pad_region = RegionCreate(NULL, 4);
if (pad_region == NULL)
return NULL;
while (nrect--) {
BoxRec pad_box;
RegionRec temp_region;
pad_box = *box;
if (pad_box.x1 < 0 && pad_box.x2 <= 0)
pad_box.x2 = 1;
else if (pad_box.x1 >= w && pad_box.x2 > w)
pad_box.x1 = w - 1;
if (pad_box.y1 < 0 && pad_box.y2 <= 0)
pad_box.y2 = 1;
else if (pad_box.y1 >= h && pad_box.y2 > h)
pad_box.y1 = h - 1;
RegionInitBoxes(&temp_region, &pad_box, 1);
RegionAppend(pad_region, &temp_region);
RegionUninit(&temp_region);
box++;
}
RegionValidate(pad_region, &overlap);
return pad_region;
}
/*
* For one type of large pixmap, its one direction is not exceed the
* size limitation, and in another word, on one direction it has only
* one block.
*
* This case of reflect repeating, we can optimize it and avoid repeat
* clip on that direction. We can just enlarge the repeat box and can
* cover all the dest region on that direction. But latter, we need to
* fixup the clipped result to get a correct coords for the subsequent
* processing. This function is to do the coords correction.
*
* */
static void
_glamor_largepixmap_reflect_fixup(short *xy1, short *xy2, int wh)
{
int odd1, odd2;
int c1, c2;
if (*xy2 - *xy1 > wh) {
*xy1 = 0;
*xy2 = wh;
return;
}
modulus(*xy1, wh, c1);
odd1 = ((*xy1 - c1) / wh) & 0x1;
modulus(*xy2, wh, c2);
odd2 = ((*xy2 - c2) / wh) & 0x1;
if (odd1 && odd2) {
*xy1 = wh - c2;
*xy2 = wh - c1;
}
else if (odd1 && !odd2) {
*xy1 = 0;
*xy2 = MAX(c2, wh - c1);
}
else if (!odd1 && odd2) {
*xy2 = wh;
*xy1 = MIN(c1, wh - c2);
}
else {
*xy1 = c1;
*xy2 = c2;
}
}
/**
* Clip the boxes regards to each pixmap's block array.
*
* Should translate the region to relative coords to the pixmap,
* start at (0,0).
*
* @is_transform: if it is set, it has a transform matrix.
*
*/
static glamor_pixmap_clipped_regions *
_glamor_compute_clipped_regions(glamor_pixmap_private *pixmap_priv,
RegionPtr region, int *n_region,
int repeat_type, int is_transform,
int reverse, int upsidedown)
{
glamor_pixmap_clipped_regions *clipped_regions;
BoxPtr extent;
int i, j;
RegionPtr current_region;
int pixmap_width, pixmap_height;
int m;
BoxRec repeat_box;
RegionRec repeat_region;
int right_shift = 0;
int down_shift = 0;
int x_center_shift = 0, y_center_shift = 0;
glamor_pixmap_private_large_t *priv;
priv = &pixmap_priv->large;
DEBUGRegionPrint(region);
if (pixmap_priv->type != GLAMOR_TEXTURE_LARGE) {
clipped_regions = calloc(1, sizeof(*clipped_regions));
clipped_regions[0].region = RegionCreate(NULL, 1);
clipped_regions[0].block_idx = 0;
RegionCopy(clipped_regions[0].region, region);
*n_region = 1;
return clipped_regions;
}
pixmap_width = priv->base.pixmap->drawable.width;
pixmap_height = priv->base.pixmap->drawable.height;
if (repeat_type == 0 || repeat_type == RepeatPad) {
RegionPtr saved_region = NULL;
if (repeat_type == RepeatPad) {
saved_region = region;
region =
_glamor_convert_pad_region(saved_region, pixmap_width,
pixmap_height);
if (region == NULL) {
*n_region = 0;
return NULL;
}
}
clipped_regions = __glamor_compute_clipped_regions(priv->block_w,
priv->block_h,
priv->block_wcnt,
0, 0,
priv->base.pixmap->
drawable.width,
priv->base.pixmap->
drawable.height,
region, n_region,
reverse, upsidedown);
if (saved_region)
RegionDestroy(region);
return clipped_regions;
}
extent = RegionExtents(region);
x_center_shift = extent->x1 / pixmap_width;
if (x_center_shift < 0)
x_center_shift--;
if (abs(x_center_shift) & 1)
x_center_shift++;
y_center_shift = extent->y1 / pixmap_height;
if (y_center_shift < 0)
y_center_shift--;
if (abs(y_center_shift) & 1)
y_center_shift++;
if (extent->x1 < 0)
right_shift = ((-extent->x1 + pixmap_width - 1) / pixmap_width);
if (extent->y1 < 0)
down_shift = ((-extent->y1 + pixmap_height - 1) / pixmap_height);
if (right_shift != 0 || down_shift != 0) {
if (repeat_type == RepeatReflect) {
right_shift = (right_shift + 1) & ~1;
down_shift = (down_shift + 1) & ~1;
}
RegionTranslate(region, right_shift * pixmap_width,
down_shift * pixmap_height);
}
extent = RegionExtents(region);
/* Tile a large pixmap to another large pixmap.
* We can't use the target large pixmap as the
* loop variable, instead we need to loop for all
* the blocks in the tile pixmap.
*
* simulate repeat each single block to cover the
* target's blocks. Two special case:
* a block_wcnt == 1 or block_hcnt ==1, then we
* only need to loop one direction as the other
* direction is fully included in the first block.
*
* For the other cases, just need to start
* from a proper shiftx/shifty, and then increase
* y by tile_height each time to walk trhough the
* target block and then walk trhough the target
* at x direction by increate tile_width each time.
*
* This way, we can consolidate all the sub blocks
* of the target boxes into one tile source's block.
*
* */
m = 0;
clipped_regions = calloc(priv->block_wcnt * priv->block_hcnt,
sizeof(*clipped_regions));
if (clipped_regions == NULL) {
*n_region = 0;
return NULL;
}
if (right_shift != 0 || down_shift != 0) {
DEBUGF("region to be repeated shifted \n");
DEBUGRegionPrint(region);
}
DEBUGF("repeat pixmap width %d height %d \n", pixmap_width, pixmap_height);
DEBUGF("extent x1 %d y1 %d x2 %d y2 %d \n", extent->x1, extent->y1,
extent->x2, extent->y2);
for (j = 0; j < priv->block_hcnt; j++) {
for (i = 0; i < priv->block_wcnt; i++) {
int dx = pixmap_width;
int dy = pixmap_height;
int idx;
int shift_x;
int shift_y;
int saved_y1, saved_y2;
int x_idx = 0, y_idx = 0, saved_y_idx = 0;
RegionRec temp_region;
BoxRec reflect_repeat_box;
BoxPtr valid_repeat_box;
shift_x = (extent->x1 / pixmap_width) * pixmap_width;
shift_y = (extent->y1 / pixmap_height) * pixmap_height;
idx = j * priv->block_wcnt + i;
if (repeat_type == RepeatReflect) {
x_idx = (extent->x1 / pixmap_width);
y_idx = (extent->y1 / pixmap_height);
}
/* Construct a rect to clip the target region. */
repeat_box.x1 = shift_x + priv->box_array[idx].x1;
repeat_box.y1 = shift_y + priv->box_array[idx].y1;
if (priv->block_wcnt == 1) {
repeat_box.x2 = extent->x2;
dx = extent->x2 - repeat_box.x1;
}
else
repeat_box.x2 = shift_x + priv->box_array[idx].x2;
if (priv->block_hcnt == 1) {
repeat_box.y2 = extent->y2;
dy = extent->y2 - repeat_box.y1;
}
else
repeat_box.y2 = shift_y + priv->box_array[idx].y2;
current_region = RegionCreate(NULL, 4);
RegionInit(&temp_region, NULL, 4);
DEBUGF("init repeat box %d %d %d %d \n",
repeat_box.x1, repeat_box.y1, repeat_box.x2, repeat_box.y2);
if (repeat_type == RepeatNormal) {
saved_y1 = repeat_box.y1;
saved_y2 = repeat_box.y2;
for (; repeat_box.x1 < extent->x2;
repeat_box.x1 += dx, repeat_box.x2 += dx) {
repeat_box.y1 = saved_y1;
repeat_box.y2 = saved_y2;
for (repeat_box.y1 = saved_y1, repeat_box.y2 = saved_y2;
repeat_box.y1 < extent->y2;
repeat_box.y1 += dy, repeat_box.y2 += dy) {
RegionInitBoxes(&repeat_region, &repeat_box, 1);
DEBUGF("Start to clip repeat region: \n");
DEBUGRegionPrint(&repeat_region);
RegionIntersect(&temp_region, &repeat_region, region);
DEBUGF("clip result:\n");
DEBUGRegionPrint(&temp_region);
RegionAppend(current_region, &temp_region);
RegionUninit(&repeat_region);
}
}
}
else if (repeat_type == RepeatReflect) {
saved_y1 = repeat_box.y1;
saved_y2 = repeat_box.y2;
saved_y_idx = y_idx;
for (;; repeat_box.x1 += dx, repeat_box.x2 += dx) {
repeat_box.y1 = saved_y1;
repeat_box.y2 = saved_y2;
y_idx = saved_y_idx;
reflect_repeat_box.x1 = (x_idx & 1) ?
((2 * x_idx + 1) * dx - repeat_box.x2) : repeat_box.x1;
reflect_repeat_box.x2 = (x_idx & 1) ?
((2 * x_idx + 1) * dx - repeat_box.x1) : repeat_box.x2;
valid_repeat_box = &reflect_repeat_box;
if (valid_repeat_box->x1 >= extent->x2)
break;
for (repeat_box.y1 = saved_y1, repeat_box.y2 = saved_y2;;
repeat_box.y1 += dy, repeat_box.y2 += dy) {
DEBUGF("x_idx %d y_idx %d dx %d dy %d\n", x_idx, y_idx,
dx, dy);
DEBUGF("repeat box %d %d %d %d \n", repeat_box.x1,
repeat_box.y1, repeat_box.x2, repeat_box.y2);
if (priv->block_hcnt > 1) {
reflect_repeat_box.y1 = (y_idx & 1) ?
((2 * y_idx + 1) * dy -
repeat_box.y2) : repeat_box.y1;
reflect_repeat_box.y2 =
(y_idx & 1) ? ((2 * y_idx + 1) * dy -
repeat_box.y1) : repeat_box.y2;
}
else {
reflect_repeat_box.y1 = repeat_box.y1;
reflect_repeat_box.y2 = repeat_box.y2;
}
DEBUGF("valid_repeat_box x1 %d y1 %d \n",
valid_repeat_box->x1, valid_repeat_box->y1);
if (valid_repeat_box->y1 >= extent->y2)
break;
RegionInitBoxes(&repeat_region, valid_repeat_box, 1);
DEBUGF("start to clip repeat[reflect] region: \n");
DEBUGRegionPrint(&repeat_region);
RegionIntersect(&temp_region, &repeat_region, region);
DEBUGF("result:\n");
DEBUGRegionPrint(&temp_region);
if (is_transform && RegionNumRects(&temp_region)) {
BoxRec temp_box;
BoxPtr temp_extent;
temp_extent = RegionExtents(&temp_region);
if (priv->block_wcnt > 1) {
if (x_idx & 1) {
temp_box.x1 =
((2 * x_idx + 1) * dx -
temp_extent->x2);
temp_box.x2 =
((2 * x_idx + 1) * dx -
temp_extent->x1);
}
else {
temp_box.x1 = temp_extent->x1;
temp_box.x2 = temp_extent->x2;
}
modulus(temp_box.x1, pixmap_width, temp_box.x1);
modulus(temp_box.x2, pixmap_width, temp_box.x2);
if (temp_box.x2 == 0)
temp_box.x2 = pixmap_width;
}
else {
temp_box.x1 = temp_extent->x1;
temp_box.x2 = temp_extent->x2;
_glamor_largepixmap_reflect_fixup(&temp_box.x1,
&temp_box.x2,
pixmap_width);
}
if (priv->block_hcnt > 1) {
if (y_idx & 1) {
temp_box.y1 =
((2 * y_idx + 1) * dy -
temp_extent->y2);
temp_box.y2 =
((2 * y_idx + 1) * dy -
temp_extent->y1);
}
else {
temp_box.y1 = temp_extent->y1;
temp_box.y2 = temp_extent->y2;
}
modulus(temp_box.y1, pixmap_height,
temp_box.y1);
modulus(temp_box.y2, pixmap_height,
temp_box.y2);
if (temp_box.y2 == 0)
temp_box.y2 = pixmap_height;
}
else {
temp_box.y1 = temp_extent->y1;
temp_box.y2 = temp_extent->y2;
_glamor_largepixmap_reflect_fixup(&temp_box.y1,
&temp_box.y2,
pixmap_height);
}
RegionInitBoxes(&temp_region, &temp_box, 1);
RegionTranslate(&temp_region,
x_center_shift * pixmap_width,
y_center_shift * pixmap_height);
DEBUGF("for transform result:\n");
DEBUGRegionPrint(&temp_region);
}
RegionAppend(current_region, &temp_region);
RegionUninit(&repeat_region);
y_idx++;
}
x_idx++;
}
}
DEBUGF("dx %d dy %d \n", dx, dy);
if (RegionNumRects(current_region)) {
if ((right_shift != 0 || down_shift != 0) &&
!(is_transform && repeat_type == RepeatReflect))
RegionTranslate(current_region, -right_shift * pixmap_width,
-down_shift * pixmap_height);
clipped_regions[m].region = current_region;
clipped_regions[m].block_idx = idx;
m++;
}
else
RegionDestroy(current_region);
RegionUninit(&temp_region);
}
}
if (right_shift != 0 || down_shift != 0)
RegionTranslate(region, -right_shift * pixmap_width,
-down_shift * pixmap_height);
*n_region = m;
return clipped_regions;
}
glamor_pixmap_clipped_regions *
glamor_compute_clipped_regions(glamor_pixmap_private *priv, RegionPtr region,
int *n_region, int repeat_type,
int reverse, int upsidedown)
{
return _glamor_compute_clipped_regions(priv, region, n_region, repeat_type,
0, reverse, upsidedown);
}
/* XXX overflow still exist. maybe we need to change to use region32.
* by default. Or just use region32 for repeat cases?
**/
glamor_pixmap_clipped_regions *
glamor_compute_transform_clipped_regions(glamor_pixmap_private *priv,
struct pixman_transform *transform,
RegionPtr region, int *n_region,
int dx, int dy, int repeat_type,
int reverse, int upsidedown)
{
BoxPtr temp_extent;
struct pixman_box32 temp_box;
struct pixman_box16 short_box;
RegionPtr temp_region;
glamor_pixmap_clipped_regions *ret;
temp_region = RegionCreate(NULL, 4);
temp_extent = RegionExtents(region);
DEBUGF("dest region \n");
DEBUGRegionPrint(region);
/* dx/dy may exceed MAX SHORT. we have to use
* a box32 to represent it.*/
temp_box.x1 = temp_extent->x1 + dx;
temp_box.x2 = temp_extent->x2 + dx;
temp_box.y1 = temp_extent->y1 + dy;
temp_box.y2 = temp_extent->y2 + dy;
DEBUGF("source box %d %d %d %d \n", temp_box.x1, temp_box.y1, temp_box.x2,
temp_box.y2);
if (transform)
glamor_get_transform_extent_from_box(&temp_box, transform);
if (repeat_type == RepeatNone) {
if (temp_box.x1 < 0)
temp_box.x1 = 0;
if (temp_box.y1 < 0)
temp_box.y1 = 0;
temp_box.x2 = MIN(temp_box.x2, priv->base.pixmap->drawable.width);
temp_box.y2 = MIN(temp_box.y2, priv->base.pixmap->drawable.height);
}
/* Now copy back the box32 to a box16 box. */
short_box.x1 = temp_box.x1;
short_box.y1 = temp_box.y1;
short_box.x2 = temp_box.x2;
short_box.y2 = temp_box.y2;
RegionInitBoxes(temp_region, &short_box, 1);
DEBUGF("copy to temp source region \n");
DEBUGRegionPrint(temp_region);
ret = _glamor_compute_clipped_regions(priv,
temp_region,
n_region,
repeat_type, 1, reverse, upsidedown);
DEBUGF("n_regions = %d \n", *n_region);
RegionDestroy(temp_region);
return ret;
}
/*
* As transform and repeatpad mode.
* We may get a clipped result which in multipe regions.
* It's not easy to do a 2nd round clipping just as we do
* without transform/repeatPad. As it's not easy to reverse
* the 2nd round clipping result with a transform/repeatPad mode,
* or even impossible for some transformation.
*
* So we have to merge the fragmental region into one region
* if the clipped result cross the region boundary.
*/
static void
glamor_merge_clipped_regions(glamor_pixmap_private *pixmap_priv,
int repeat_type,
glamor_pixmap_clipped_regions *clipped_regions,
int *n_regions, int *need_clean_fbo)
{
BoxPtr temp_extent;
BoxRec temp_box, copy_box;
RegionPtr temp_region;
glamor_pixmap_private *temp_priv;
PixmapPtr temp_pixmap;
int overlap;
int i;
int pixmap_width, pixmap_height;
glamor_pixmap_private_large_t *priv;
priv = &pixmap_priv->large;
pixmap_width = priv->base.pixmap->drawable.width;
pixmap_height = priv->base.pixmap->drawable.height;
temp_region = RegionCreate(NULL, 4);
for (i = 0; i < *n_regions; i++) {
DEBUGF("Region %d:\n", i);
DEBUGRegionPrint(clipped_regions[i].region);
RegionAppend(temp_region, clipped_regions[i].region);
}
RegionValidate(temp_region, &overlap);
DEBUGF("temp region: \n");
DEBUGRegionPrint(temp_region);
temp_extent = RegionExtents(temp_region);
temp_box = *temp_extent;
DEBUGF("need copy region: \n");
DEBUGF("%d %d %d %d \n", temp_box.x1, temp_box.y1, temp_box.x2,
temp_box.y2);
temp_pixmap =
glamor_create_pixmap(priv->base.pixmap->drawable.pScreen,
temp_box.x2 - temp_box.x1,
temp_box.y2 - temp_box.y1,
priv->base.pixmap->drawable.depth,
GLAMOR_CREATE_PIXMAP_FIXUP);
if (temp_pixmap == NULL) {
assert(0);
return;
}
temp_priv = glamor_get_pixmap_private(temp_pixmap);
assert(temp_priv->type != GLAMOR_TEXTURE_LARGE);
priv->box = temp_box;
if (temp_extent->x1 >= 0 && temp_extent->x2 <= pixmap_width
&& temp_extent->y1 >= 0 && temp_extent->y2 <= pixmap_height) {
int dx, dy;
copy_box.x1 = 0;
copy_box.y1 = 0;
copy_box.x2 = temp_extent->x2 - temp_extent->x1;
copy_box.y2 = temp_extent->y2 - temp_extent->y1;
dx = temp_extent->x1;
dy = temp_extent->y1;
glamor_copy_n_to_n(&priv->base.pixmap->drawable,
&temp_pixmap->drawable,
NULL, &copy_box, 1, dx, dy, 0, 0, 0, NULL);
// glamor_solid(temp_pixmap, 0, 0, temp_pixmap->drawable.width,
// temp_pixmap->drawable.height, GXcopy, 0xffffffff, 0xff00);
}
else {
for (i = 0; i < *n_regions; i++) {
BoxPtr box;
int nbox;
box = REGION_RECTS(clipped_regions[i].region);
nbox = REGION_NUM_RECTS(clipped_regions[i].region);
while (nbox--) {
int dx, dy, c, d;
DEBUGF("box x1 %d y1 %d x2 %d y2 %d \n",
box->x1, box->y1, box->x2, box->y2);
modulus(box->x1, pixmap_width, c);
dx = c - (box->x1 - temp_box.x1);
copy_box.x1 = box->x1 - temp_box.x1;
copy_box.x2 = box->x2 - temp_box.x1;
modulus(box->y1, pixmap_height, d);
dy = d - (box->y1 - temp_box.y1);
copy_box.y1 = box->y1 - temp_box.y1;
copy_box.y2 = box->y2 - temp_box.y1;
DEBUGF("copying box %d %d %d %d, dx %d dy %d\n",
copy_box.x1, copy_box.y1, copy_box.x2,
copy_box.y2, dx, dy);
glamor_copy_n_to_n(&priv->base.pixmap->drawable,
&temp_pixmap->drawable,
NULL, &copy_box, 1, dx, dy, 0, 0, 0, NULL);
box++;
}
}
//glamor_solid(temp_pixmap, 0, 0, temp_pixmap->drawable.width,
// temp_pixmap->drawable.height, GXcopy, 0xffffffff, 0xff);
}
/* The first region will be released at caller side. */
for (i = 1; i < *n_regions; i++)
RegionDestroy(clipped_regions[i].region);
RegionDestroy(temp_region);
priv->box = temp_box;
priv->base.fbo = glamor_pixmap_detach_fbo(temp_priv);
DEBUGF("priv box x1 %d y1 %d x2 %d y2 %d \n",
priv->box.x1, priv->box.y1, priv->box.x2, priv->box.y2);
glamor_destroy_pixmap(temp_pixmap);
*need_clean_fbo = 1;
*n_regions = 1;
}
/**
* Given an expected transformed block width and block height,
*
* This function calculate a new block width and height which
* guarantee the transform result will not exceed the given
* block width and height.
*
* For large block width and height (> 2048), we choose a
* smaller new width and height and to reduce the cross region
* boundary and can avoid some overhead.
*
**/
Bool
glamor_get_transform_block_size(struct pixman_transform *transform,
int block_w, int block_h,
int *transformed_block_w,
int *transformed_block_h)
{
double a, b, c, d, e, f, g, h;
double scale;
int width, height;
a = pixman_fixed_to_double(transform->matrix[0][0]);
b = pixman_fixed_to_double(transform->matrix[0][1]);
c = pixman_fixed_to_double(transform->matrix[1][0]);
d = pixman_fixed_to_double(transform->matrix[1][1]);
scale = pixman_fixed_to_double(transform->matrix[2][2]);
if (block_w > 2048) {
/* For large block size, we shrink it to smaller box,
* thus latter we may get less cross boundary regions and
* thus can avoid some extra copy.
*
**/
width = block_w / 4;
height = block_h / 4;
}
else {
width = block_w - 2;
height = block_h - 2;
}
e = a + b;
f = c + d;
g = a - b;
h = c - d;
e = MIN(block_w, floor(width * scale) / MAX(fabs(e), fabs(g)));
f = MIN(block_h, floor(height * scale) / MAX(fabs(f), fabs(h)));
*transformed_block_w = MIN(e, f) - 1;
*transformed_block_h = *transformed_block_w;
if (*transformed_block_w <= 0 || *transformed_block_h <= 0)
return FALSE;
DEBUGF("original block_w/h %d %d, fixed %d %d \n", block_w, block_h,
*transformed_block_w, *transformed_block_h);
return TRUE;
}
#define VECTOR_FROM_POINT(p, x, y) \
p.v[0] = x; \
p.v[1] = y; \
p.v[2] = 1.0;
void
glamor_get_transform_extent_from_box(struct pixman_box32 *box,
struct pixman_transform *transform)
{
struct pixman_f_vector p0, p1, p2, p3;
float min_x, min_y, max_x, max_y;
struct pixman_f_transform ftransform;
VECTOR_FROM_POINT(p0, box->x1, box->y1)
VECTOR_FROM_POINT(p1, box->x2, box->y1)
VECTOR_FROM_POINT(p2, box->x2, box->y2)
VECTOR_FROM_POINT(p3, box->x1, box->y2)
pixman_f_transform_from_pixman_transform(&ftransform, transform);
pixman_f_transform_point(&ftransform, &p0);
pixman_f_transform_point(&ftransform, &p1);
pixman_f_transform_point(&ftransform, &p2);
pixman_f_transform_point(&ftransform, &p3);
min_x = MIN(p0.v[0], p1.v[0]);
min_x = MIN(min_x, p2.v[0]);
min_x = MIN(min_x, p3.v[0]);
min_y = MIN(p0.v[1], p1.v[1]);
min_y = MIN(min_y, p2.v[1]);
min_y = MIN(min_y, p3.v[1]);
max_x = MAX(p0.v[0], p1.v[0]);
max_x = MAX(max_x, p2.v[0]);
max_x = MAX(max_x, p3.v[0]);
max_y = MAX(p0.v[1], p1.v[1]);
max_y = MAX(max_y, p2.v[1]);
max_y = MAX(max_y, p3.v[1]);
box->x1 = floor(min_x) - 1;
box->y1 = floor(min_y) - 1;
box->x2 = ceil(max_x) + 1;
box->y2 = ceil(max_y) + 1;
}
static void
_glamor_process_transformed_clipped_region(glamor_pixmap_private *priv,
int repeat_type,
glamor_pixmap_clipped_regions *
clipped_regions, int *n_regions,
int *need_clean_fbo)
{
int shift_x, shift_y;
if (*n_regions != 1) {
/* Merge all source regions into one region. */
glamor_merge_clipped_regions(priv, repeat_type,
clipped_regions, n_regions,
need_clean_fbo);
}
else {
SET_PIXMAP_FBO_CURRENT(priv, clipped_regions[0].block_idx);
if (repeat_type == RepeatReflect || repeat_type == RepeatNormal) {
/* The required source areas are in one region,
* we need to shift the corresponding box's coords to proper position,
* thus we can calculate the relative coords correctly.*/
BoxPtr temp_box;
int rem;
temp_box = RegionExtents(clipped_regions[0].region);
modulus(temp_box->x1, priv->base.pixmap->drawable.width, rem);
shift_x = (temp_box->x1 - rem) / priv->base.pixmap->drawable.width;
modulus(temp_box->y1, priv->base.pixmap->drawable.height, rem);
shift_y = (temp_box->y1 - rem) / priv->base.pixmap->drawable.height;
if (shift_x != 0) {
priv->large.box.x1 +=
shift_x * priv->base.pixmap->drawable.width;
priv->large.box.x2 +=
shift_x * priv->base.pixmap->drawable.width;
}
if (shift_y != 0) {
priv->large.box.y1 +=
shift_y * priv->base.pixmap->drawable.height;
priv->large.box.y2 +=
shift_y * priv->base.pixmap->drawable.height;
}
}
}
}
Bool
glamor_composite_largepixmap_region(CARD8 op,
PicturePtr source,
PicturePtr mask,
PicturePtr dest,
glamor_pixmap_private *source_pixmap_priv,
glamor_pixmap_private *mask_pixmap_priv,
glamor_pixmap_private *dest_pixmap_priv,
RegionPtr region, Bool force_clip,
INT16 x_source,
INT16 y_source,
INT16 x_mask,
INT16 y_mask,
INT16 x_dest, INT16 y_dest,
CARD16 width, CARD16 height)
{
glamor_pixmap_clipped_regions *clipped_dest_regions;
glamor_pixmap_clipped_regions *clipped_source_regions;
glamor_pixmap_clipped_regions *clipped_mask_regions;
int n_dest_regions;
int n_mask_regions;
int n_source_regions;
int i, j, k;
int need_clean_source_fbo = 0;
int need_clean_mask_fbo = 0;
int is_normal_source_fbo = 0;
int is_normal_mask_fbo = 0;
int fixed_block_width, fixed_block_height;
int null_source, null_mask;
glamor_pixmap_private *need_free_source_pixmap_priv = NULL;
glamor_pixmap_private *need_free_mask_pixmap_priv = NULL;
int source_repeat_type = 0, mask_repeat_type = 0;
int ok = TRUE;
if (source->repeat)
source_repeat_type = source->repeatType;
else
source_repeat_type = RepeatNone;
if (mask && mask->repeat)
mask_repeat_type = mask->repeatType;
else
mask_repeat_type = RepeatNone;
fixed_block_width = dest_pixmap_priv->large.block_w;
fixed_block_height = dest_pixmap_priv->large.block_h;
/* If we got an totally out-of-box region for a source or mask
* region without repeat, we need to set it as null_source and
* give it a solid color (0,0,0,0). */
null_source = 0;
null_mask = 0;
RegionTranslate(region, -dest->pDrawable->x, -dest->pDrawable->y);
/* need to transform the dest region to the correct sourcei/mask region.
* it's a little complex, as one single edge of the
* target region may be transformed to cross a block boundary of the
* source or mask. Then it's impossible to handle it as usual way.
* We may have to split the original dest region to smaller region, and
* make sure each region's transformed region can fit into one texture,
* and then continue this loop again, and each time when a transformed region
* cross the bound, we need to copy it to a single pixmap and do the composition
* with the new pixmap. If the transformed region doesn't cross a source/mask's
* boundary then we don't need to copy.
*
*/
if (source_pixmap_priv
&& source->transform
&& source_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) {
int source_transformed_block_width, source_transformed_block_height;
if (!glamor_get_transform_block_size(source->transform,
source_pixmap_priv->large.block_w,
source_pixmap_priv->large.block_h,
&source_transformed_block_width,
&source_transformed_block_height))
{
DEBUGF("source block size less than 1, fallback.\n");
RegionTranslate(region, dest->pDrawable->x, dest->pDrawable->y);
return FALSE;
}
fixed_block_width =
min(fixed_block_width, source_transformed_block_width);
fixed_block_height =
min(fixed_block_height, source_transformed_block_height);
DEBUGF("new source block size %d x %d \n", fixed_block_width,
fixed_block_height);
}
if (mask_pixmap_priv
&& mask->transform && mask_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) {
int mask_transformed_block_width, mask_transformed_block_height;
if (!glamor_get_transform_block_size(mask->transform,
mask_pixmap_priv->large.block_w,
mask_pixmap_priv->large.block_h,
&mask_transformed_block_width,
&mask_transformed_block_height)) {
DEBUGF("mask block size less than 1, fallback.\n");
RegionTranslate(region, dest->pDrawable->x, dest->pDrawable->y);
return FALSE;
}
fixed_block_width =
min(fixed_block_width, mask_transformed_block_width);
fixed_block_height =
min(fixed_block_height, mask_transformed_block_height);
DEBUGF("new mask block size %d x %d \n", fixed_block_width,
fixed_block_height);
}
/*compute the correct block width and height whose transformed source/mask
*region can fit into one texture.*/
if (force_clip || fixed_block_width < dest_pixmap_priv->large.block_w
|| fixed_block_height < dest_pixmap_priv->large.block_h)
clipped_dest_regions =
glamor_compute_clipped_regions_ext(dest_pixmap_priv, region,
&n_dest_regions,
fixed_block_width,
fixed_block_height, 0, 0);
else
clipped_dest_regions = glamor_compute_clipped_regions(dest_pixmap_priv,
region,
&n_dest_regions,
0, 0, 0);
DEBUGF("dest clipped result %d region: \n", n_dest_regions);
if (source_pixmap_priv
&& (source_pixmap_priv == dest_pixmap_priv ||
source_pixmap_priv == mask_pixmap_priv)
&& source_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) {
/* XXX self-copy... */
need_free_source_pixmap_priv = source_pixmap_priv;
source_pixmap_priv = malloc(sizeof(*source_pixmap_priv));
*source_pixmap_priv = *need_free_source_pixmap_priv;
need_free_source_pixmap_priv = source_pixmap_priv;
}
assert(mask_pixmap_priv != dest_pixmap_priv);
for (i = 0; i < n_dest_regions; i++) {
DEBUGF("dest region %d idx %d\n", i,
clipped_dest_regions[i].block_idx);
DEBUGRegionPrint(clipped_dest_regions[i].region);
SET_PIXMAP_FBO_CURRENT(dest_pixmap_priv,
clipped_dest_regions[i].block_idx);
if (source_pixmap_priv &&
source_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) {
if (!source->transform && source_repeat_type != RepeatPad) {
RegionTranslate(clipped_dest_regions[i].region,
x_source - x_dest, y_source - y_dest);
clipped_source_regions =
glamor_compute_clipped_regions(source_pixmap_priv,
clipped_dest_regions[i].
region, &n_source_regions,
source_repeat_type, 0, 0);
is_normal_source_fbo = 1;
}
else {
clipped_source_regions =
glamor_compute_transform_clipped_regions(source_pixmap_priv,
source->transform,
clipped_dest_regions
[i].region,
&n_source_regions,
x_source - x_dest,
y_source - y_dest,
source_repeat_type,
0, 0);
is_normal_source_fbo = 0;
if (n_source_regions == 0) {
/* Pad the out-of-box region to (0,0,0,0). */
null_source = 1;
n_source_regions = 1;
}
else
_glamor_process_transformed_clipped_region
(source_pixmap_priv, source_repeat_type,
clipped_source_regions, &n_source_regions,
&need_clean_source_fbo);
}
DEBUGF("source clipped result %d region: \n", n_source_regions);
for (j = 0; j < n_source_regions; j++) {
if (is_normal_source_fbo)
SET_PIXMAP_FBO_CURRENT(source_pixmap_priv,
clipped_source_regions[j].block_idx);
if (mask_pixmap_priv &&
mask_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) {
if (is_normal_mask_fbo && is_normal_source_fbo) {
/* both mask and source are normal fbo box without transform or repeatpad.
* The region is clipped against source and then we clip it against mask here.*/
DEBUGF("source region %d idx %d\n", j,
clipped_source_regions[j].block_idx);
DEBUGRegionPrint(clipped_source_regions[j].region);
RegionTranslate(clipped_source_regions[j].region,
-x_source + x_mask, -y_source + y_mask);
clipped_mask_regions =
glamor_compute_clipped_regions(mask_pixmap_priv,
clipped_source_regions
[j].region,
&n_mask_regions,
mask_repeat_type, 0,
0);
is_normal_mask_fbo = 1;
}
else if (is_normal_mask_fbo && !is_normal_source_fbo) {
assert(n_source_regions == 1);
/* The source fbo is not a normal fbo box, it has transform or repeatpad.
* the valid clip region should be the clip dest region rather than the
* clip source region.*/
RegionTranslate(clipped_dest_regions[i].region,
-x_dest + x_mask, -y_dest + y_mask);
clipped_mask_regions =
glamor_compute_clipped_regions(mask_pixmap_priv,
clipped_dest_regions
[i].region,
&n_mask_regions,
mask_repeat_type, 0,
0);
is_normal_mask_fbo = 1;
}
else {
/* This mask region has transform or repeatpad, we need clip it agains the previous
* valid region rather than the mask region. */
if (!is_normal_source_fbo)
clipped_mask_regions =
glamor_compute_transform_clipped_regions
(mask_pixmap_priv, mask->transform,
clipped_dest_regions[i].region,
&n_mask_regions, x_mask - x_dest,
y_mask - y_dest, mask_repeat_type, 0, 0);
else
clipped_mask_regions =
glamor_compute_transform_clipped_regions
(mask_pixmap_priv, mask->transform,
clipped_source_regions[j].region,
&n_mask_regions, x_mask - x_source,
y_mask - y_source, mask_repeat_type, 0, 0);
is_normal_mask_fbo = 0;
if (n_mask_regions == 0) {
/* Pad the out-of-box region to (0,0,0,0). */
null_mask = 1;
n_mask_regions = 1;
}
else
_glamor_process_transformed_clipped_region
(mask_pixmap_priv, mask_repeat_type,
clipped_mask_regions, &n_mask_regions,
&need_clean_mask_fbo);
}
DEBUGF("mask clipped result %d region: \n", n_mask_regions);
#define COMPOSITE_REGION(region) do { \
if (!glamor_composite_clipped_region(op, \
null_source ? NULL : source, \
null_mask ? NULL : mask, dest, \
null_source ? NULL : source_pixmap_priv, \
null_mask ? NULL : mask_pixmap_priv, \
dest_pixmap_priv, region, \
x_source, y_source, x_mask, y_mask, \
x_dest, y_dest)) { \
assert(0); \
} \
} while(0)
for (k = 0; k < n_mask_regions; k++) {
DEBUGF("mask region %d idx %d\n", k,
clipped_mask_regions[k].block_idx);
DEBUGRegionPrint(clipped_mask_regions[k].region);
if (is_normal_mask_fbo) {
SET_PIXMAP_FBO_CURRENT(mask_pixmap_priv,
clipped_mask_regions[k].
block_idx);
DEBUGF("mask fbo off %d %d \n",
mask_pixmap_priv->large.box.x1,
mask_pixmap_priv->large.box.y1);
DEBUGF("start composite mask hasn't transform.\n");
RegionTranslate(clipped_mask_regions[k].region,
x_dest - x_mask +
dest->pDrawable->x,
y_dest - y_mask +
dest->pDrawable->y);
COMPOSITE_REGION(clipped_mask_regions[k].region);
}
else if (!is_normal_mask_fbo && !is_normal_source_fbo) {
DEBUGF
("start composite both mask and source have transform.\n");
RegionTranslate(clipped_dest_regions[i].region,
dest->pDrawable->x,
dest->pDrawable->y);
COMPOSITE_REGION(clipped_dest_regions[i].region);
}
else {
DEBUGF
("start composite only mask has transform.\n");
RegionTranslate(clipped_source_regions[j].region,
x_dest - x_source +
dest->pDrawable->x,
y_dest - y_source +
dest->pDrawable->y);
COMPOSITE_REGION(clipped_source_regions[j].region);
}
RegionDestroy(clipped_mask_regions[k].region);
}
free(clipped_mask_regions);
if (null_mask)
null_mask = 0;
if (need_clean_mask_fbo) {
assert(is_normal_mask_fbo == 0);
glamor_destroy_fbo(mask_pixmap_priv->base.fbo);
mask_pixmap_priv->base.fbo = NULL;
need_clean_mask_fbo = 0;
}
}
else {
if (is_normal_source_fbo) {
RegionTranslate(clipped_source_regions[j].region,
-x_source + x_dest + dest->pDrawable->x,
-y_source + y_dest +
dest->pDrawable->y);
COMPOSITE_REGION(clipped_source_regions[j].region);
}
else {
/* Source has transform or repeatPad. dest regions is the right
* region to do the composite. */
RegionTranslate(clipped_dest_regions[i].region,
dest->pDrawable->x, dest->pDrawable->y);
COMPOSITE_REGION(clipped_dest_regions[i].region);
}
}
if (clipped_source_regions && clipped_source_regions[j].region)
RegionDestroy(clipped_source_regions[j].region);
}
free(clipped_source_regions);
if (null_source)
null_source = 0;
if (need_clean_source_fbo) {
assert(is_normal_source_fbo == 0);
glamor_destroy_fbo(source_pixmap_priv->base.fbo);
source_pixmap_priv->base.fbo = NULL;
need_clean_source_fbo = 0;
}
}
else {
if (mask_pixmap_priv &&
mask_pixmap_priv->type == GLAMOR_TEXTURE_LARGE) {
if (!mask->transform && mask_repeat_type != RepeatPad) {
RegionTranslate(clipped_dest_regions[i].region,
x_mask - x_dest, y_mask - y_dest);
clipped_mask_regions =
glamor_compute_clipped_regions(mask_pixmap_priv,
clipped_dest_regions[i].
region, &n_mask_regions,
mask_repeat_type, 0, 0);
is_normal_mask_fbo = 1;
}
else {
clipped_mask_regions =
glamor_compute_transform_clipped_regions
(mask_pixmap_priv, mask->transform,
clipped_dest_regions[i].region, &n_mask_regions,
x_mask - x_dest, y_mask - y_dest, mask_repeat_type, 0,
0);
is_normal_mask_fbo = 0;
if (n_mask_regions == 0) {
/* Pad the out-of-box region to (0,0,0,0). */
null_mask = 1;
n_mask_regions = 1;
}
else
_glamor_process_transformed_clipped_region
(mask_pixmap_priv, mask_repeat_type,
clipped_mask_regions, &n_mask_regions,
&need_clean_mask_fbo);
}
for (k = 0; k < n_mask_regions; k++) {
DEBUGF("mask region %d idx %d\n", k,
clipped_mask_regions[k].block_idx);
DEBUGRegionPrint(clipped_mask_regions[k].region);
if (is_normal_mask_fbo) {
SET_PIXMAP_FBO_CURRENT(mask_pixmap_priv,
clipped_mask_regions[k].
block_idx);
RegionTranslate(clipped_mask_regions[k].region,
x_dest - x_mask + dest->pDrawable->x,
y_dest - y_mask + dest->pDrawable->y);
COMPOSITE_REGION(clipped_mask_regions[k].region);
}
else {
RegionTranslate(clipped_dest_regions[i].region,
dest->pDrawable->x, dest->pDrawable->y);
COMPOSITE_REGION(clipped_dest_regions[i].region);
}
RegionDestroy(clipped_mask_regions[k].region);
}
free(clipped_mask_regions);
if (null_mask)
null_mask = 0;
if (need_clean_mask_fbo) {
glamor_destroy_fbo(mask_pixmap_priv->base.fbo);
mask_pixmap_priv->base.fbo = NULL;
need_clean_mask_fbo = 0;
}
}
else {
RegionTranslate(clipped_dest_regions[i].region,
dest->pDrawable->x, dest->pDrawable->y);
COMPOSITE_REGION(clipped_dest_regions[i].region);
}
}
RegionDestroy(clipped_dest_regions[i].region);
}
free(clipped_dest_regions);
free(need_free_source_pixmap_priv);
free(need_free_mask_pixmap_priv);
ok = TRUE;
return ok;
}