#include #include /* For INT16_MAX */ #include "glamor_priv.h" static void glamor_get_transform_extent_from_box(struct pixman_box32 *box, struct pixman_transform *transform); static inline glamor_pixmap_private * __glamor_large(glamor_pixmap_private *pixmap_priv) { assert(glamor_pixmap_priv_is_large(pixmap_priv)); return pixmap_priv; } /** * 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(PixmapPtr pixmap, RegionPtr region, int *n_region, int inner_block_w, int inner_block_h, int reverse, int upsidedown) { glamor_pixmap_private *pixmap_priv = glamor_get_pixmap_private(pixmap); glamor_pixmap_clipped_regions *clipped_regions, *inner_regions, *result_regions; int i, j, x, y, k, inner_n_regions; int width, height; BoxPtr box_array; BoxRec small_box; int block_w, block_h; DEBUGF("ext called \n"); if (glamor_pixmap_priv_is_small(pixmap_priv)) { 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; block_w = pixmap->drawable.width; block_h = pixmap->drawable.height; box_array = &small_box; small_box.x1 = small_box.y1 = 0; small_box.x2 = block_w; small_box.y2 = block_h; } else { glamor_pixmap_private *priv = __glamor_large(pixmap_priv); clipped_regions = __glamor_compute_clipped_regions(priv->block_w, priv->block_h, priv->block_wcnt, 0, 0, pixmap->drawable.width, pixmap->drawable.height, region, n_region, reverse, upsidedown); if (clipped_regions == NULL) { *n_region = 0; return NULL; } block_w = priv->block_w; block_h = priv->block_h; box_array = priv->box_array; } if (inner_block_w >= block_w && inner_block_h >= block_h) return clipped_regions; result_regions = calloc(*n_region * ((block_w + inner_block_w - 1) / inner_block_w) * ((block_h + inner_block_h - 1) / inner_block_h), sizeof(*result_regions)); k = 0; for (i = 0; i < *n_region; i++) { x = box_array[clipped_regions[i].block_idx].x1; y = box_array[clipped_regions[i].block_idx].y1; width = box_array[clipped_regions[i].block_idx].x2 - x; height = 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(PixmapPtr pixmap, 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 *priv; DEBUGRegionPrint(region); if (glamor_pixmap_priv_is_small(pixmap_priv)) { 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; } priv = __glamor_large(pixmap_priv); pixmap_width = pixmap->drawable.width; pixmap_height = 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, pixmap->drawable.width, 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(PixmapPtr pixmap, RegionPtr region, int *n_region, int repeat_type, int reverse, int upsidedown) { glamor_pixmap_private *priv = glamor_get_pixmap_private(pixmap); return _glamor_compute_clipped_regions(pixmap, 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? **/ static glamor_pixmap_clipped_regions * glamor_compute_transform_clipped_regions(PixmapPtr pixmap, struct pixman_transform *transform, RegionPtr region, int *n_region, int dx, int dy, int repeat_type, int reverse, int upsidedown) { glamor_pixmap_private *priv = glamor_get_pixmap_private(pixmap); 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, pixmap->drawable.width); temp_box.y2 = MIN(temp_box.y2, pixmap->drawable.height); } /* Now copy back the box32 to a box16 box, avoiding overflow. */ short_box.x1 = MIN(temp_box.x1, INT16_MAX); short_box.y1 = MIN(temp_box.y1, INT16_MAX); short_box.x2 = MIN(temp_box.x2, INT16_MAX); short_box.y2 = MIN(temp_box.y2, INT16_MAX); RegionInitBoxes(temp_region, &short_box, 1); DEBUGF("copy to temp source region \n"); DEBUGRegionPrint(temp_region); ret = _glamor_compute_clipped_regions(pixmap, 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(PixmapPtr pixmap, glamor_pixmap_private *pixmap_priv, int repeat_type, glamor_pixmap_clipped_regions *clipped_regions, int *n_regions, int *need_clean_fbo) { 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 *priv; priv = __glamor_large(pixmap_priv); pixmap_width = pixmap->drawable.width; pixmap_height =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_box = *RegionExtents(temp_region); 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(pixmap->drawable.pScreen, temp_box.x2 - temp_box.x1, temp_box.y2 - temp_box.y1, pixmap->drawable.depth, GLAMOR_CREATE_PIXMAP_FIXUP); if (temp_pixmap == NULL) { assert(0); return; } temp_priv = glamor_get_pixmap_private(temp_pixmap); assert(glamor_pixmap_priv_is_small(temp_priv)); priv->box = temp_box; if (temp_box.x1 >= 0 && temp_box.x2 <= pixmap_width && temp_box.y1 >= 0 && temp_box.y2 <= pixmap_height) { int dx, dy; copy_box.x1 = 0; copy_box.y1 = 0; copy_box.x2 = temp_box.x2 - temp_box.x1; copy_box.y2 = temp_box.y2 - temp_box.y1; dx = temp_box.x1; dy = temp_box.y1; glamor_copy(&pixmap->drawable, &temp_pixmap->drawable, NULL, ©_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(&pixmap->drawable, &temp_pixmap->drawable, NULL, ©_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->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. * **/ static 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) do {\ p.v[0] = x; \ p.v[1] = y; \ p.v[2] = 1.0; } while (0) static 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(PixmapPtr pixmap, 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(pixmap, priv, repeat_type, clipped_regions, n_regions, need_clean_fbo); } else { glamor_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, pixmap->drawable.width, rem); shift_x = (temp_box->x1 - rem) / pixmap->drawable.width; modulus(temp_box->y1, pixmap->drawable.height, rem); shift_y = (temp_box->y1 - rem) / pixmap->drawable.height; if (shift_x != 0) { __glamor_large(priv)->box.x1 += shift_x * pixmap->drawable.width; __glamor_large(priv)->box.x2 += shift_x * pixmap->drawable.width; } if (shift_y != 0) { __glamor_large(priv)->box.y1 += shift_y * pixmap->drawable.height; __glamor_large(priv)->box.y2 += shift_y * pixmap->drawable.height; } } } } Bool glamor_composite_largepixmap_region(CARD8 op, PicturePtr source, PicturePtr mask, PicturePtr dest, PixmapPtr source_pixmap, PixmapPtr mask_pixmap, PixmapPtr dest_pixmap, 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) { ScreenPtr screen = dest_pixmap->drawable.pScreen; glamor_screen_private *glamor_priv = glamor_get_screen_private(screen); glamor_pixmap_private *source_pixmap_priv = glamor_get_pixmap_private(source_pixmap); glamor_pixmap_private *mask_pixmap_priv = glamor_get_pixmap_private(mask_pixmap); glamor_pixmap_private *dest_pixmap_priv = glamor_get_pixmap_private(dest_pixmap); 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 dest_block_width, dest_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_pixmap == dest_pixmap) { glamor_fallback("source and dest pixmaps are the same\n"); return FALSE; } if (mask_pixmap == dest_pixmap) { glamor_fallback("mask and dest pixmaps are the same\n"); return FALSE; } 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; if (glamor_pixmap_priv_is_large(dest_pixmap_priv)) { dest_block_width = __glamor_large(dest_pixmap_priv)->block_w; dest_block_height = __glamor_large(dest_pixmap_priv)->block_h; } else { dest_block_width = dest_pixmap->drawable.width; dest_block_height = dest_pixmap->drawable.height; } fixed_block_width = dest_block_width; fixed_block_height = dest_block_height; /* 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 && glamor_pixmap_priv_is_large(source_pixmap_priv)) { int source_transformed_block_width, source_transformed_block_height; if (!glamor_get_transform_block_size(source->transform, __glamor_large(source_pixmap_priv)->block_w, __glamor_large(source_pixmap_priv)->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 && glamor_pixmap_priv_is_large(mask_pixmap_priv)) { int mask_transformed_block_width, mask_transformed_block_height; if (!glamor_get_transform_block_size(mask->transform, __glamor_large(mask_pixmap_priv)->block_w, __glamor_large(mask_pixmap_priv)->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_block_width || fixed_block_height < dest_block_height) clipped_dest_regions = glamor_compute_clipped_regions_ext(dest_pixmap, region, &n_dest_regions, fixed_block_width, fixed_block_height, 0, 0); else clipped_dest_regions = glamor_compute_clipped_regions(dest_pixmap, 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) && glamor_pixmap_priv_is_large(source_pixmap_priv)) { /* 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); glamor_set_pixmap_fbo_current(dest_pixmap_priv, clipped_dest_regions[i].block_idx); if (source_pixmap_priv && glamor_pixmap_priv_is_large(source_pixmap_priv)) { 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, 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, 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, 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) glamor_set_pixmap_fbo_current(source_pixmap_priv, clipped_source_regions[j].block_idx); if (mask_pixmap_priv && glamor_pixmap_priv_is_large(mask_pixmap_priv)) { 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, 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, 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 against the previous * valid region rather than the mask region. */ if (!is_normal_source_fbo) clipped_mask_regions = glamor_compute_transform_clipped_regions (mask_pixmap, 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, 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, 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, \ null_mask ? NULL : mask_pixmap, \ dest_pixmap, 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) { glamor_set_pixmap_fbo_current(mask_pixmap_priv, clipped_mask_regions[k]. block_idx); DEBUGF("mask fbo off %d %d \n", __glamor_large(mask_pixmap_priv)->box.x1, __glamor_large(mask_pixmap_priv)->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(glamor_priv, mask_pixmap_priv->fbo); mask_pixmap_priv->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(glamor_priv, source_pixmap_priv->fbo); source_pixmap_priv->fbo = NULL; need_clean_source_fbo = 0; } } else { if (mask_pixmap_priv && glamor_pixmap_priv_is_large(mask_pixmap_priv)) { 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, 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, 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, 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) { glamor_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(glamor_priv, mask_pixmap_priv->fbo); mask_pixmap_priv->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; }