/* * Copyright (c) 2007 David Turner * Copyright (c) 2008 M Joonas Pihlaja * Copyright (c) 2011 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Chris Wilson <chris@chris-wilson.co.uk> * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sna.h" #include "sna_render.h" #include "sna_render_inline.h" #include "sna_trapezoids.h" #include "fb/fbpict.h" #include <mipict.h> /* TODO: Emit unantialiased and MSAA triangles. */ #ifndef MAX #define MAX(x,y) ((x) >= (y) ? (x) : (y)) #endif #ifndef MIN #define MIN(x,y) ((x) <= (y) ? (x) : (y)) #endif #define region_count(r) ((r)->data ? (r)->data->numRects : 1) #define region_boxes(r) ((r)->data ? (BoxPtr)((r)->data + 1) : &(r)->extents) #if HAS_DEBUG_FULL static void _assert_pixmap_contains_box(PixmapPtr pixmap, BoxPtr box, const char *function) { if (box->x1 < 0 || box->y1 < 0 || box->x2 > pixmap->drawable.width || box->y2 > pixmap->drawable.height) { FatalError("%s: damage box is beyond the pixmap: box=(%d, %d), (%d, %d), pixmap=(%d, %d)\n", function, box->x1, box->y1, box->x2, box->y2, pixmap->drawable.width, pixmap->drawable.height); } } #define assert_pixmap_contains_box(p, b) _assert_pixmap_contains_box(p, b, __FUNCTION__) #else #define assert_pixmap_contains_box(p, b) #endif static void apply_damage(struct sna_composite_op *op, RegionPtr region) { DBG(("%s: damage=%p, region=%dx[(%d, %d), (%d, %d)]\n", __FUNCTION__, op->damage, region_num_rects(region), region->extents.x1, region->extents.y1, region->extents.x2, region->extents.y2)); if (op->damage == NULL) return; RegionTranslate(region, op->dst.x, op->dst.y); assert_pixmap_contains_box(op->dst.pixmap, RegionExtents(region)); if (sna_damage_add_to_pixmap(op->damage, region, op->dst.pixmap)) op->damage = NULL; } static void _apply_damage_box(struct sna_composite_op *op, const BoxRec *box) { BoxRec r; r.x1 = box->x1 + op->dst.x; r.x2 = box->x2 + op->dst.x; r.y1 = box->y1 + op->dst.y; r.y2 = box->y2 + op->dst.y; assert_pixmap_contains_box(op->dst.pixmap, &r); sna_damage_add_box(op->damage, &r); } inline static void apply_damage_box(struct sna_composite_op *op, const BoxRec *box) { if (op->damage) _apply_damage_box(op, box); } bool composite_aligned_boxes(struct sna *sna, CARD8 op, PicturePtr src, PicturePtr dst, PictFormatPtr maskFormat, INT16 src_x, INT16 src_y, int ntrap, const xTrapezoid *traps, bool force_fallback) { BoxRec stack_boxes[64], *boxes; pixman_region16_t region, clip; struct sna_composite_op tmp; int16_t dst_x, dst_y; bool ret = true; int dx, dy, n, num_boxes; if (NO_ALIGNED_BOXES) return false; DBG(("%s: pixmap=%ld, nboxes=%d, dx=(%d, %d)\n", __FUNCTION__, get_drawable_pixmap(dst->pDrawable)->drawable.serialNumber, ntrap, dst->pDrawable->x, dst->pDrawable->y)); boxes = stack_boxes; if (ntrap > (int)ARRAY_SIZE(stack_boxes)) { boxes = malloc(sizeof(BoxRec)*ntrap); if (boxes == NULL) return false; } dx = dst->pDrawable->x; dy = dst->pDrawable->y; region.extents.x1 = region.extents.y1 = 32767; region.extents.x2 = region.extents.y2 = -32767; num_boxes = 0; for (n = 0; n < ntrap; n++) { boxes[num_boxes].x1 = dx + pixman_fixed_to_int(traps[n].left.p1.x + pixman_fixed_1_minus_e/2); boxes[num_boxes].y1 = dy + pixman_fixed_to_int(traps[n].top + pixman_fixed_1_minus_e/2); boxes[num_boxes].x2 = dx + pixman_fixed_to_int(traps[n].right.p2.x + pixman_fixed_1_minus_e/2); boxes[num_boxes].y2 = dy + pixman_fixed_to_int(traps[n].bottom + pixman_fixed_1_minus_e/2); if (boxes[num_boxes].x1 >= boxes[num_boxes].x2) continue; if (boxes[num_boxes].y1 >= boxes[num_boxes].y2) continue; if (boxes[num_boxes].x1 < region.extents.x1) region.extents.x1 = boxes[num_boxes].x1; if (boxes[num_boxes].x2 > region.extents.x2) region.extents.x2 = boxes[num_boxes].x2; if (boxes[num_boxes].y1 < region.extents.y1) region.extents.y1 = boxes[num_boxes].y1; if (boxes[num_boxes].y2 > region.extents.y2) region.extents.y2 = boxes[num_boxes].y2; num_boxes++; } if (num_boxes == 0) goto free_boxes; trapezoid_origin(&traps[0].left, &dst_x, &dst_y); DBG(("%s: extents (%d, %d), (%d, %d) offset of (%d, %d), origin (%d, %d)\n", __FUNCTION__, region.extents.x1, region.extents.y1, region.extents.x2, region.extents.y2, region.extents.x1 - boxes[0].x1, region.extents.y1 - boxes[0].y1, dst_x, dst_y)); if (!sna_compute_composite_region(&clip, src, NULL, dst, src_x + region.extents.x1 - dst_x - dx, src_y + region.extents.y1 - dst_y - dy, 0, 0, region.extents.x1 - dx, region.extents.y1 - dy, region.extents.x2 - region.extents.x1, region.extents.y2 - region.extents.y1)) { DBG(("%s: trapezoids do not intersect drawable clips\n", __FUNCTION__)) ; goto done; } if (op == PictOpClear && sna->clear) src = sna->clear; DBG(("%s: clipped extents (%d, %d), (%d, %d); now offset by (%d, %d), origin (%d, %d)\n", __FUNCTION__, clip.extents.x1, clip.extents.y1, clip.extents.x2, clip.extents.y2, clip.extents.x1 - boxes[0].x1, clip.extents.y1 - boxes[0].y1, dst_x, dst_y)); if (force_fallback || !sna->render.composite(sna, op, src, NULL, dst, src_x + clip.extents.x1 - dst_x, src_y + clip.extents.y1 - dst_y, 0, 0, clip.extents.x1, clip.extents.y1, clip.extents.x2 - clip.extents.x1, clip.extents.y2 - clip.extents.y1, (clip.data || num_boxes > 1) ? COMPOSITE_PARTIAL : 0, memset(&tmp, 0, sizeof(tmp)))) { unsigned int flags; const pixman_box16_t *b; int i, count; DBG(("%s: composite render op not supported\n", __FUNCTION__)); flags = MOVE_READ | MOVE_WRITE; if (op <= PictOpSrc) { flags |= MOVE_INPLACE_HINT; if (n == 1) flags &= ~MOVE_READ; } if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &clip, flags)) goto done; if (dst->alphaMap && !sna_drawable_move_to_cpu(dst->alphaMap->pDrawable, MOVE_READ | MOVE_WRITE)) goto done; if (src->pDrawable) { if (!sna_drawable_move_to_cpu(src->pDrawable, MOVE_READ)) goto done; if (src->alphaMap && !sna_drawable_move_to_cpu(src->alphaMap->pDrawable, MOVE_READ)) goto done; } DBG(("%s: fbComposite()\n", __FUNCTION__)); src_x -= dst_x - dx; src_y -= dst_y - dy; if (maskFormat) { pixman_region_init_rects(®ion, boxes, num_boxes); RegionIntersect(®ion, ®ion, &clip); if (sigtrap_get() == 0) { b = region_rects(®ion); count = region_num_rects(®ion); for (i = 0; i < count; i++) { fbComposite(op, src, NULL, dst, src_x + b[i].x1, src_y + b[i].y1, 0, 0, b[i].x1, b[i].y1, b[i].x2 - b[i].x1, b[i].y2 - b[i].y1); } sigtrap_put(); } pixman_region_fini(®ion); } else { for (n = 0; n < num_boxes; n++) { pixman_region_init_rects(®ion, &boxes[n], 1); RegionIntersect(®ion, ®ion, &clip); b = region_rects(®ion); count = region_num_rects(®ion); if (sigtrap_get() == 0) { for (i = 0; i < count; i++) { fbComposite(op, src, NULL, dst, src_x + b[i].x1, src_y + b[i].y1, 0, 0, b[i].x1, b[i].y1, b[i].x2 - b[i].x1, b[i].y2 - b[i].y1); } sigtrap_put(); } pixman_region_fini(®ion); pixman_region_fini(®ion); } } ret = true; goto done; } if (maskFormat || (op == PictOpSrc || op == PictOpClear) || num_boxes == 1) { pixman_region_init_rects(®ion, boxes, num_boxes); RegionIntersect(®ion, ®ion, &clip); if (region_num_rects(®ion)) { tmp.boxes(sna, &tmp, region_rects(®ion), region_num_rects(®ion)); apply_damage(&tmp, ®ion); } pixman_region_fini(®ion); } else { for (n = 0; n < num_boxes; n++) { pixman_region_init_rects(®ion, &boxes[n], 1); RegionIntersect(®ion, ®ion, &clip); if (region_num_rects(®ion)) { tmp.boxes(sna, &tmp, region_rects(®ion), region_num_rects(®ion)); apply_damage(&tmp, ®ion); } pixman_region_fini(®ion); } } tmp.done(sna, &tmp); done: REGION_UNINIT(NULL, &clip); free_boxes: if (boxes != stack_boxes) free(boxes); return ret; } static inline int grid_coverage(int samples, pixman_fixed_t f) { return (samples * pixman_fixed_frac(f) + pixman_fixed_1/2) / pixman_fixed_1; } inline static void composite_unaligned_box(struct sna *sna, struct sna_composite_spans_op *tmp, const BoxRec *box, float opacity, pixman_region16_t *clip) { assert(opacity != 0.); if (clip) { pixman_region16_t region; pixman_region_init_rects(®ion, box, 1); RegionIntersect(®ion, ®ion, clip); if (region_num_rects(®ion)) tmp->boxes(sna, tmp, region_rects(®ion), region_num_rects(®ion), opacity); pixman_region_fini(®ion); } else tmp->box(sna, tmp, box, opacity); } inline static void composite_unaligned_trap_row(struct sna *sna, struct sna_composite_spans_op *tmp, const xTrapezoid *trap, int dx, int y1, int y2, int covered, pixman_region16_t *clip) { BoxRec box; int opacity; int x1, x2; #define u8_to_float(x) ((x) * (1.f/255)) if (covered == 0) return; x1 = dx + pixman_fixed_to_int(trap->left.p1.x); x2 = dx + pixman_fixed_to_int(trap->right.p1.x); if (clip) { if (y2 > clip->extents.y2) y2 = clip->extents.y2; if (y1 < clip->extents.y1) y1 = clip->extents.y1; if (y1 >= y2) return; if (x2 < clip->extents.x1 || x1 > clip->extents.x2) return; } box.y1 = y1; box.y2 = y2; if (x1 == x2) { box.x1 = x1; box.x2 = x2 + 1; opacity = covered; opacity *= grid_coverage(SAMPLES_X, trap->right.p1.x) - grid_coverage(SAMPLES_X, trap->left.p1.x); if (opacity) composite_unaligned_box(sna, tmp, &box, u8_to_float(opacity), clip); } else { if (pixman_fixed_frac(trap->left.p1.x)) { box.x1 = x1; box.x2 = ++x1; opacity = covered; opacity *= SAMPLES_X - grid_coverage(SAMPLES_X, trap->left.p1.x); if (opacity) composite_unaligned_box(sna, tmp, &box, u8_to_float(opacity), clip); } if (x2 > x1) { box.x1 = x1; box.x2 = x2; composite_unaligned_box(sna, tmp, &box, covered == SAMPLES_Y ? 1. : u8_to_float(covered*SAMPLES_X), clip); } if (pixman_fixed_frac(trap->right.p1.x)) { box.x1 = x2; box.x2 = x2 + 1; opacity = covered; opacity *= grid_coverage(SAMPLES_X, trap->right.p1.x); if (opacity) composite_unaligned_box(sna, tmp, &box, u8_to_float(opacity), clip); } } } flatten static void composite_unaligned_trap(struct sna *sna, struct sna_composite_spans_op *tmp, const xTrapezoid *trap, int dx, int dy, pixman_region16_t *clip) { int y1, y2; y1 = dy + pixman_fixed_to_int(trap->top); y2 = dy + pixman_fixed_to_int(trap->bottom); DBG(("%s: y1=%d, y2=%d\n", __FUNCTION__, y1, y2)); if (y1 == y2) { composite_unaligned_trap_row(sna, tmp, trap, dx, y1, y1 + 1, grid_coverage(SAMPLES_Y, trap->bottom) - grid_coverage(SAMPLES_Y, trap->top), clip); } else { if (pixman_fixed_frac(trap->top)) { composite_unaligned_trap_row(sna, tmp, trap, dx, y1, y1 + 1, SAMPLES_Y - grid_coverage(SAMPLES_Y, trap->top), clip); y1++; } if (y2 > y1) composite_unaligned_trap_row(sna, tmp, trap, dx, y1, y2, SAMPLES_Y, clip); if (pixman_fixed_frac(trap->bottom)) composite_unaligned_trap_row(sna, tmp, trap, dx, y2, y2 + 1, grid_coverage(SAMPLES_Y, trap->bottom), clip); } if (tmp->base.damage) { BoxRec box; box.x1 = dx + pixman_fixed_to_int(trap->left.p1.x); box.x2 = dx + pixman_fixed_to_int(trap->right.p1.x + pixman_fixed_1_minus_e); box.y1 = dy + pixman_fixed_to_int(trap->top); box.y2 = dy + pixman_fixed_to_int(trap->bottom + pixman_fixed_1_minus_e); if (clip) { pixman_region16_t region; pixman_region_init_rects(®ion, &box, 1); RegionIntersect(®ion, ®ion, clip); if (region_num_rects(®ion)) apply_damage(&tmp->base, ®ion); RegionUninit(®ion); } else apply_damage_box(&tmp->base, &box); } } inline static void blt_opacity(PixmapPtr scratch, int x1, int x2, int y, int h, uint8_t opacity) { uint8_t *ptr; if (opacity == 0xff) return; if (x1 < 0) x1 = 0; if (x2 > scratch->drawable.width) x2 = scratch->drawable.width; if (x1 >= x2) return; x2 -= x1; ptr = scratch->devPrivate.ptr; ptr += scratch->devKind * y; ptr += x1; do { if (x2 == 1) *ptr = opacity; else memset(ptr, opacity, x2); ptr += scratch->devKind; } while (--h); } static void blt_unaligned_box_row(PixmapPtr scratch, BoxPtr extents, const xTrapezoid *trap, int y1, int y2, int covered) { int x1, x2; if (y2 > scratch->drawable.height) y2 = scratch->drawable.height; if (y1 < 0) y1 = 0; if (y1 >= y2) return; y2 -= y1; x1 = pixman_fixed_to_int(trap->left.p1.x); x2 = pixman_fixed_to_int(trap->right.p1.x); x1 -= extents->x1; x2 -= extents->x1; if (x1 == x2) { blt_opacity(scratch, x1, x1+1, y1, y2, covered * (grid_coverage(SAMPLES_X, trap->right.p1.x) - grid_coverage(SAMPLES_X, trap->left.p1.x))); } else { if (pixman_fixed_frac(trap->left.p1.x)) { blt_opacity(scratch, x1, x1 + 1, y1, y2, covered * (SAMPLES_X - grid_coverage(SAMPLES_X, trap->left.p1.x))); x1++; } if (x2 > x1) { blt_opacity(scratch, x1, x2, y1, y2, covered*SAMPLES_X); } if (pixman_fixed_frac(trap->right.p1.x)) blt_opacity(scratch, x2, x2 + 1, y1, y2, covered * grid_coverage(SAMPLES_X, trap->right.p1.x)); } } inline static void lerp32_opacity(PixmapPtr scratch, uint32_t color, int16_t x, int16_t w, int16_t y, int16_t h, uint8_t opacity) { uint32_t *ptr; int stride, i; sigtrap_assert_active(); ptr = (uint32_t*)((uint8_t *)scratch->devPrivate.ptr + scratch->devKind * y); ptr += x; stride = scratch->devKind / 4; if (opacity == 0xff) { if ((w | h) == 1) { *ptr = color; } else { if (w < 16) { do { for (i = 0; i < w; i++) ptr[i] = color; ptr += stride; } while (--h); } else { pixman_fill(ptr, stride, 32, 0, 0, w, h, color); } } } else { if ((w | h) == 1) { *ptr = lerp8x4(color, opacity, *ptr); } else if (w == 1) { do { *ptr = lerp8x4(color, opacity, *ptr); ptr += stride; } while (--h); } else{ do { for (i = 0; i < w; i++) ptr[i] = lerp8x4(color, opacity, ptr[i]); ptr += stride; } while (--h); } } } static void lerp32_unaligned_box_row(PixmapPtr scratch, uint32_t color, const BoxRec *extents, const xTrapezoid *trap, int16_t dx, int16_t y, int16_t h, uint8_t covered) { int16_t x1 = pixman_fixed_to_int(trap->left.p1.x) + dx; uint16_t fx1 = grid_coverage(SAMPLES_X, trap->left.p1.x); int16_t x2 = pixman_fixed_to_int(trap->right.p2.x) + dx; uint16_t fx2 = grid_coverage(SAMPLES_X, trap->right.p2.x); if (x1 < extents->x1) x1 = extents->x1, fx1 = 0; if (x2 >= extents->x2) x2 = extents->x2, fx2 = 0; DBG(("%s: x=(%d.%d, %d.%d), y=%dx%d, covered=%d\n", __FUNCTION__, x1, fx1, x2, fx2, y, h, covered)); if (x1 < x2) { if (fx1) { lerp32_opacity(scratch, color, x1, 1, y, h, covered * (SAMPLES_X - fx1)); x1++; } if (x2 > x1) { lerp32_opacity(scratch, color, x1, x2-x1, y, h, covered*SAMPLES_X); } if (fx2) { lerp32_opacity(scratch, color, x2, 1, y, h, covered * fx2); } } else if (x1 == x2 && fx2 > fx1) { lerp32_opacity(scratch, color, x1, 1, y, h, covered * (fx2 - fx1)); } } struct pixman_inplace { pixman_image_t *image, *source, *mask; uint32_t color; uint32_t *bits; int dx, dy; int sx, sy; uint8_t op; }; inline static void pixsolid_opacity(struct pixman_inplace *pi, int16_t x, int16_t w, int16_t y, int16_t h, uint8_t opacity) { if (opacity == 0xff) *pi->bits = pi->color; else *pi->bits = mul_4x8_8(pi->color, opacity); sna_image_composite(pi->op, pi->source, NULL, pi->image, 0, 0, 0, 0, pi->dx + x, pi->dy + y, w, h); } static void pixsolid_unaligned_box_row(struct pixman_inplace *pi, const BoxRec *extents, const xTrapezoid *trap, int16_t y, int16_t h, uint8_t covered) { int16_t x1 = pixman_fixed_to_int(trap->left.p1.x); uint16_t fx1 = grid_coverage(SAMPLES_X, trap->left.p1.x); int16_t x2 = pixman_fixed_to_int(trap->right.p1.x); uint16_t fx2 = grid_coverage(SAMPLES_X, trap->right.p1.x); if (x1 < extents->x1) x1 = extents->x1, fx1 = 0; if (x2 >= extents->x2) x2 = extents->x2, fx2 = 0; if (x1 < x2) { if (fx1) { pixsolid_opacity(pi, x1, 1, y, h, covered * (SAMPLES_X - fx1)); x1++; } if (x2 > x1) pixsolid_opacity(pi, x1, x2-x1, y, h, covered*SAMPLES_X); if (fx2) pixsolid_opacity(pi, x2, 1, y, h, covered * fx2); } else if (x1 == x2 && fx2 > fx1) { pixsolid_opacity(pi, x1, 1, y, h, covered * (fx2 - fx1)); } } static bool composite_unaligned_boxes_inplace__solid(struct sna *sna, CARD8 op, uint32_t color, PicturePtr dst, int n, const xTrapezoid *t, bool force_fallback) { PixmapPtr pixmap; int16_t dx, dy; DBG(("%s: force=%d, is_gpu=%d, op=%d, color=%x\n", __FUNCTION__, force_fallback, is_gpu(sna, dst->pDrawable, PREFER_GPU_SPANS), op, color)); if (!force_fallback && is_gpu(sna, dst->pDrawable, PREFER_GPU_SPANS)) { DBG(("%s: fallback -- can not perform operation in place, destination busy\n", __FUNCTION__)); return false; } /* XXX a8 boxes */ if (!(dst->format == PICT_a8r8g8b8 || dst->format == PICT_x8r8g8b8)) { DBG(("%s: fallback -- can not perform operation in place, unhanbled format %08lx\n", __FUNCTION__, (long)dst->format)); goto pixman; } pixmap = get_drawable_pixmap(dst->pDrawable); get_drawable_deltas(dst->pDrawable, pixmap, &dx, &dy); if (op == PictOpOver && (color >> 24) == 0xff) op = PictOpSrc; if (op == PictOpOver || op == PictOpAdd) { struct sna_pixmap *priv = sna_pixmap(pixmap); if (priv && priv->clear && priv->clear_color == 0) op = PictOpSrc; } switch (op) { case PictOpSrc: break; default: DBG(("%s: fallback -- can not perform op [%d] in place\n", __FUNCTION__, op)); goto pixman; } DBG(("%s: inplace operation on argb32 destination x %d\n", __FUNCTION__, n)); do { RegionRec clip; const BoxRec *extents; int count; clip.extents.x1 = pixman_fixed_to_int(t->left.p1.x); clip.extents.x2 = pixman_fixed_to_int(t->right.p1.x + pixman_fixed_1_minus_e); clip.extents.y1 = pixman_fixed_to_int(t->top); clip.extents.y2 = pixman_fixed_to_int(t->bottom + pixman_fixed_1_minus_e); clip.data = NULL; if (!sna_compute_composite_region(&clip, NULL, NULL, dst, 0, 0, 0, 0, clip.extents.x1, clip.extents.y1, clip.extents.x2 - clip.extents.x1, clip.extents.y2 - clip.extents.y1)) continue; if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &clip, MOVE_WRITE | MOVE_READ)) { RegionUninit(&clip); continue; } if (sigtrap_get() == 0) { RegionTranslate(&clip, dx, dy); count = region_num_rects(&clip); extents = region_rects(&clip); while (count--) { int16_t y1 = dy + pixman_fixed_to_int(t->top); uint16_t fy1 = pixman_fixed_frac(t->top); int16_t y2 = dy + pixman_fixed_to_int(t->bottom); uint16_t fy2 = pixman_fixed_frac(t->bottom); DBG(("%s: t=(%d, %d), (%d, %d), extents (%d, %d), (%d, %d)\n", __FUNCTION__, pixman_fixed_to_int(t->left.p1.x), pixman_fixed_to_int(t->top), pixman_fixed_to_int(t->right.p2.x), pixman_fixed_to_int(t->bottom), extents->x1, extents->y1, extents->x2, extents->y2)); if (y1 < extents->y1) y1 = extents->y1, fy1 = 0; if (y2 >= extents->y2) y2 = extents->y2, fy2 = 0; if (y1 < y2) { if (fy1) { lerp32_unaligned_box_row(pixmap, color, extents, t, dx, y1, 1, SAMPLES_Y - grid_coverage(SAMPLES_Y, fy1)); y1++; } if (y2 > y1) lerp32_unaligned_box_row(pixmap, color, extents, t, dx, y1, y2 - y1, SAMPLES_Y); if (fy2) lerp32_unaligned_box_row(pixmap, color, extents, t, dx, y2, 1, grid_coverage(SAMPLES_Y, fy2)); } else if (y1 == y2 && fy2 > fy1) { lerp32_unaligned_box_row(pixmap, color, extents, t, dx, y1, 1, grid_coverage(SAMPLES_Y, fy2) - grid_coverage(SAMPLES_Y, fy1)); } extents++; } sigtrap_put(); } RegionUninit(&clip); } while (--n && t++); return true; pixman: do { struct pixman_inplace pi; RegionRec clip; const BoxRec *extents; int count; clip.extents.x1 = pixman_fixed_to_int(t->left.p1.x); clip.extents.x2 = pixman_fixed_to_int(t->right.p1.x + pixman_fixed_1_minus_e); clip.extents.y1 = pixman_fixed_to_int(t->top); clip.extents.y2 = pixman_fixed_to_int(t->bottom + pixman_fixed_1_minus_e); clip.data = NULL; if (!sna_compute_composite_region(&clip, NULL, NULL, dst, 0, 0, 0, 0, clip.extents.x1, clip.extents.y1, clip.extents.x2 - clip.extents.x1, clip.extents.y2 - clip.extents.y1)) continue; if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &clip, MOVE_WRITE | MOVE_READ)) { RegionUninit(&clip); continue; } pi.image = image_from_pict(dst, false, &pi.dx, &pi.dy); pi.source = pixman_image_create_bits(PIXMAN_a8r8g8b8, 1, 1, NULL, 0); pixman_image_set_repeat(pi.source, PIXMAN_REPEAT_NORMAL); pi.bits = pixman_image_get_data(pi.source); pi.color = color; pi.op = op; if (sigtrap_get() == 0) { count = region_num_rects(&clip); extents = region_rects(&clip); while (count--) { int16_t y1 = pixman_fixed_to_int(t->top); uint16_t fy1 = pixman_fixed_frac(t->top); int16_t y2 = pixman_fixed_to_int(t->bottom); uint16_t fy2 = pixman_fixed_frac(t->bottom); if (y1 < extents->y1) y1 = extents->y1, fy1 = 0; if (y2 >= extents->y2) y2 = extents->y2, fy2 = 0; if (y1 < y2) { if (fy1) { pixsolid_unaligned_box_row(&pi, extents, t, y1, 1, SAMPLES_Y - grid_coverage(SAMPLES_Y, fy1)); y1++; } if (y2 > y1) pixsolid_unaligned_box_row(&pi, extents, t, y1, y2 - y1, SAMPLES_Y); if (fy2) pixsolid_unaligned_box_row(&pi, extents, t, y2, 1, grid_coverage(SAMPLES_Y, fy2)); } else if (y1 == y2 && fy2 > fy1) { pixsolid_unaligned_box_row(&pi, extents, t, y1, 1, grid_coverage(SAMPLES_Y, fy2) - grid_coverage(SAMPLES_Y, fy1)); } extents++; } sigtrap_put(); } RegionUninit(&clip); pixman_image_unref(pi.image); pixman_image_unref(pi.source); } while (--n && t++); return true; } inline static void pixmask_opacity(struct pixman_inplace *pi, int16_t x, int16_t w, int16_t y, int16_t h, uint8_t opacity) { if (opacity == 0xff) { pixman_image_composite(pi->op, pi->source, NULL, pi->image, pi->sx + x, pi->sy + y, 0, 0, pi->dx + x, pi->dy + y, w, h); } else { *pi->bits = opacity; pixman_image_composite(pi->op, pi->source, pi->mask, pi->image, pi->sx + x, pi->sy + y, 0, 0, pi->dx + x, pi->dy + y, w, h); } } static void pixmask_unaligned_box_row(struct pixman_inplace *pi, const BoxRec *extents, const xTrapezoid *trap, int16_t y, int16_t h, uint8_t covered) { int16_t x1 = pixman_fixed_to_int(trap->left.p1.x); uint16_t fx1 = grid_coverage(SAMPLES_X, trap->left.p1.x); int16_t x2 = pixman_fixed_to_int(trap->right.p1.x); uint16_t fx2 = grid_coverage(SAMPLES_X, trap->right.p1.x); if (x1 < extents->x1) x1 = extents->x1, fx1 = 0; if (x2 >= extents->x2) x2 = extents->x2, fx2 = 0; if (x1 < x2) { if (fx1) { pixmask_opacity(pi, x1, 1, y, h, covered * (SAMPLES_X - fx1)); x1++; } if (x2 > x1) pixmask_opacity(pi, x1, x2-x1, y, h, covered*SAMPLES_X); if (fx2) pixmask_opacity(pi, x2, 1, y, h, covered * fx2); } else if (x1 == x2 && fx2 > fx1) { pixmask_opacity(pi, x1, 1, y, h, covered * (fx2 - fx1)); } } struct rectilinear_inplace_thread { pixman_image_t *dst, *src; const RegionRec *clip; const xTrapezoid *trap; int dx, dy, sx, sy; int y1, y2; CARD8 op; }; static void rectilinear_inplace_thread(void *arg) { struct rectilinear_inplace_thread *thread = arg; const xTrapezoid *t = thread->trap; struct pixman_inplace pi; const BoxRec *extents; int count; pi.image = thread->dst; pi.dx = thread->dx; pi.dy = thread->dy; pi.source = thread->src; pi.sx = thread->sx; pi.sy = thread->sy; pi.mask = pixman_image_create_bits(PIXMAN_a8, 1, 1, &pi.color, 4); pixman_image_set_repeat(pi.mask, PIXMAN_REPEAT_NORMAL); pi.bits = pixman_image_get_data(pi.mask); pi.op = thread->op; count = region_count(thread->clip); extents = region_boxes(thread->clip); while (count--) { int16_t y1 = pixman_fixed_to_int(t->top); uint16_t fy1 = pixman_fixed_frac(t->top); int16_t y2 = pixman_fixed_to_int(t->bottom); uint16_t fy2 = pixman_fixed_frac(t->bottom); if (y1 < MAX(thread->y1, extents->y1)) y1 = MAX(thread->y1, extents->y1), fy1 = 0; if (y2 > MIN(thread->y2, extents->y2)) y2 = MIN(thread->y2, extents->y2), fy2 = 0; if (y1 < y2) { if (fy1) { pixmask_unaligned_box_row(&pi, extents, t, y1, 1, SAMPLES_Y - grid_coverage(SAMPLES_Y, fy1)); y1++; } if (y2 > y1) pixmask_unaligned_box_row(&pi, extents, t, y1, y2 - y1, SAMPLES_Y); if (fy2) pixmask_unaligned_box_row(&pi, extents, t, y2, 1, grid_coverage(SAMPLES_Y, fy2)); } else if (y1 == y2 && fy2 > fy1) { pixmask_unaligned_box_row(&pi, extents, t, y1, 1, grid_coverage(SAMPLES_Y, fy2) - grid_coverage(SAMPLES_Y, fy1)); } extents++; } pixman_image_unref(pi.mask); } static bool composite_unaligned_boxes_inplace(struct sna *sna, CARD8 op, PicturePtr src, int16_t src_x, int16_t src_y, PicturePtr dst, int n, const xTrapezoid *t, bool force_fallback) { if (!force_fallback && (is_gpu(sna, dst->pDrawable, PREFER_GPU_SPANS) || picture_is_gpu(sna, src, PREFER_GPU_SPANS))) { DBG(("%s: fallback -- not forcing\n", __FUNCTION__)); return false; } DBG(("%s\n", __FUNCTION__)); src_x -= pixman_fixed_to_int(t[0].left.p1.x); src_y -= pixman_fixed_to_int(t[0].left.p1.y); do { RegionRec clip; const BoxRec *extents; int count; int num_threads; clip.extents.x1 = pixman_fixed_to_int(t->left.p1.x); clip.extents.x2 = pixman_fixed_to_int(t->right.p1.x + pixman_fixed_1_minus_e); clip.extents.y1 = pixman_fixed_to_int(t->top); clip.extents.y2 = pixman_fixed_to_int(t->bottom + pixman_fixed_1_minus_e); clip.data = NULL; if (!sna_compute_composite_region(&clip, src, NULL, dst, clip.extents.x1 + src_x, clip.extents.y1 + src_y, 0, 0, clip.extents.x1, clip.extents.y1, clip.extents.x2 - clip.extents.x1, clip.extents.y2 - clip.extents.y1)) continue; if (!sna_drawable_move_region_to_cpu(dst->pDrawable, &clip, MOVE_WRITE | MOVE_READ)) { RegionUninit(&clip); continue; } if (src->pDrawable) { if (!sna_drawable_move_to_cpu(src->pDrawable, MOVE_READ)) { RegionUninit(&clip); continue; } if (src->alphaMap) { if (!sna_drawable_move_to_cpu(src->alphaMap->pDrawable, MOVE_READ)) { RegionUninit(&clip); continue; } } } num_threads = sna_use_threads(clip.extents.x2 - clip.extents.x1, clip.extents.y2 - clip.extents.y1, 32); if (num_threads == 1) { struct pixman_inplace pi; pi.image = image_from_pict(dst, false, &pi.dx, &pi.dy); pi.source = image_from_pict(src, false, &pi.sx, &pi.sy); pi.sx += src_x; pi.sy += src_y; pi.mask = pixman_image_create_bits(PIXMAN_a8, 1, 1, &pi.color, 4); pixman_image_set_repeat(pi.mask, PIXMAN_REPEAT_NORMAL); pi.bits = pixman_image_get_data(pi.mask); pi.op = op; if (sigtrap_get() == 0) { count = region_num_rects(&clip); extents = region_rects(&clip); while (count--) { int16_t y1 = pixman_fixed_to_int(t->top); uint16_t fy1 = pixman_fixed_frac(t->top); int16_t y2 = pixman_fixed_to_int(t->bottom); uint16_t fy2 = pixman_fixed_frac(t->bottom); if (y1 < extents->y1) y1 = extents->y1, fy1 = 0; if (y2 > extents->y2) y2 = extents->y2, fy2 = 0; if (y1 < y2) { if (fy1) { pixmask_unaligned_box_row(&pi, extents, t, y1, 1, SAMPLES_Y - grid_coverage(SAMPLES_Y, fy1)); y1++; } if (y2 > y1) pixmask_unaligned_box_row(&pi, extents, t, y1, y2 - y1, SAMPLES_Y); if (fy2) pixmask_unaligned_box_row(&pi, extents, t, y2, 1, grid_coverage(SAMPLES_Y, fy2)); } else if (y1 == y2 && fy2 > fy1) { pixmask_unaligned_box_row(&pi, extents, t, y1, 1, grid_coverage(SAMPLES_Y, fy2) - grid_coverage(SAMPLES_Y, fy1)); } extents++; } sigtrap_put(); } pixman_image_unref(pi.image); pixman_image_unref(pi.source); pixman_image_unref(pi.mask); } else { struct rectilinear_inplace_thread thread[num_threads]; int i, y, dy; thread[0].trap = t; thread[0].dst = image_from_pict(dst, false, &thread[0].dx, &thread[0].dy); thread[0].src = image_from_pict(src, false, &thread[0].sx, &thread[0].sy); thread[0].sx += src_x; thread[0].sy += src_y; thread[0].clip = &clip; thread[0].op = op; y = clip.extents.y1; dy = (clip.extents.y2 - clip.extents.y1 + num_threads - 1) / num_threads; num_threads = (clip.extents.y2 - clip.extents.y1 + dy - 1) / dy; if (sigtrap_get() == 0) { for (i = 1; i < num_threads; i++) { thread[i] = thread[0]; thread[i].y1 = y; thread[i].y2 = y += dy; sna_threads_run(i, rectilinear_inplace_thread, &thread[i]); } assert(y < clip.extents.y2); thread[0].y1 = y; thread[0].y2 = clip.extents.y2; rectilinear_inplace_thread(&thread[0]); sna_threads_wait(); sigtrap_put(); } else sna_threads_kill(); pixman_image_unref(thread[0].dst); pixman_image_unref(thread[0].src); } RegionUninit(&clip); } while (--n && t++); return true; } static bool composite_unaligned_boxes_fallback(struct sna *sna, CARD8 op, PicturePtr src, PicturePtr dst, INT16 src_x, INT16 src_y, int ntrap, const xTrapezoid *traps, bool force_fallback) { ScreenPtr screen = dst->pDrawable->pScreen; uint32_t color; int16_t dst_x, dst_y; int16_t dx, dy; int n; if (sna_picture_is_solid(src, &color) && composite_unaligned_boxes_inplace__solid(sna, op, color, dst, ntrap, traps, force_fallback)) return true; if (composite_unaligned_boxes_inplace(sna, op, src, src_x, src_y, dst, ntrap, traps, force_fallback)) return true; trapezoid_origin(&traps[0].left, &dst_x, &dst_y); dx = dst->pDrawable->x; dy = dst->pDrawable->y; for (n = 0; n < ntrap; n++) { const xTrapezoid *t = &traps[n]; PixmapPtr scratch; PicturePtr mask; BoxRec extents; int error; int y1, y2; extents.x1 = pixman_fixed_to_int(t->left.p1.x); extents.x2 = pixman_fixed_to_int(t->right.p1.x + pixman_fixed_1_minus_e); extents.y1 = pixman_fixed_to_int(t->top); extents.y2 = pixman_fixed_to_int(t->bottom + pixman_fixed_1_minus_e); if (!sna_compute_composite_extents(&extents, src, NULL, dst, src_x, src_y, 0, 0, extents.x1, extents.y1, extents.x2 - extents.x1, extents.y2 - extents.y1)) continue; if (force_fallback) scratch = sna_pixmap_create_unattached(screen, extents.x2 - extents.x1, extents.y2 - extents.y1, 8); else scratch = sna_pixmap_create_upload(screen, extents.x2 - extents.x1, extents.y2 - extents.y1, 8, KGEM_BUFFER_WRITE_INPLACE); if (!scratch) continue; memset(scratch->devPrivate.ptr, 0xff, scratch->devKind * (extents.y2 - extents.y1)); extents.x1 -= dx; extents.x2 -= dx; extents.y1 -= dy; extents.y2 -= dy; y1 = pixman_fixed_to_int(t->top) - extents.y1; y2 = pixman_fixed_to_int(t->bottom) - extents.y1; if (y1 == y2) { blt_unaligned_box_row(scratch, &extents, t, y1, y1 + 1, grid_coverage(SAMPLES_Y, t->bottom) - grid_coverage(SAMPLES_Y, t->top)); } else { if (pixman_fixed_frac(t->top)) { blt_unaligned_box_row(scratch, &extents, t, y1, y1 + 1, SAMPLES_Y - grid_coverage(SAMPLES_Y, t->top)); y1++; } if (y2 > y1) blt_unaligned_box_row(scratch, &extents, t, y1, y2, SAMPLES_Y); if (pixman_fixed_frac(t->bottom)) blt_unaligned_box_row(scratch, &extents, t, y2, y2+1, grid_coverage(SAMPLES_Y, t->bottom)); } mask = CreatePicture(0, &scratch->drawable, PictureMatchFormat(screen, 8, PICT_a8), 0, 0, serverClient, &error); if (mask) { CompositePicture(op, src, mask, dst, src_x + extents.x1 - dst_x, src_y + extents.y1 - dst_y, 0, 0, extents.x1, extents.y1, extents.x2 - extents.x1, extents.y2 - extents.y1); FreePicture(mask, 0); } sna_pixmap_destroy(scratch); } return true; } bool composite_unaligned_boxes(struct sna *sna, CARD8 op, PicturePtr src, PicturePtr dst, PictFormatPtr maskFormat, INT16 src_x, INT16 src_y, int ntrap, const xTrapezoid *traps, bool force_fallback) { BoxRec extents; struct sna_composite_spans_op tmp; struct sna_pixmap *priv; pixman_region16_t clip, *c; int16_t dst_x, dst_y; int dx, dy, n; if (NO_UNALIGNED_BOXES) return false; DBG(("%s: force_fallback=%d, mask=%x, n=%d, op=%d\n", __FUNCTION__, force_fallback, maskFormat ? (int)maskFormat->format : 0, ntrap, op)); /* need a span converter to handle overlapping traps */ if (ntrap > 1 && maskFormat) return false; if (force_fallback || !sna->render.check_composite_spans(sna, op, src, dst, 0, 0, COMPOSITE_SPANS_RECTILINEAR)) { fallback: return composite_unaligned_boxes_fallback(sna, op, src, dst, src_x, src_y, ntrap, traps, force_fallback); } trapezoid_origin(&traps[0].left, &dst_x, &dst_y); extents.x1 = pixman_fixed_to_int(traps[0].left.p1.x); extents.x2 = pixman_fixed_to_int(traps[0].right.p1.x + pixman_fixed_1_minus_e); extents.y1 = pixman_fixed_to_int(traps[0].top); extents.y2 = pixman_fixed_to_int(traps[0].bottom + pixman_fixed_1_minus_e); DBG(("%s: src=(%d, %d), dst=(%d, %d)\n", __FUNCTION__, src_x, src_y, dst_x, dst_y)); for (n = 1; n < ntrap; n++) { int x1 = pixman_fixed_to_int(traps[n].left.p1.x); int x2 = pixman_fixed_to_int(traps[n].right.p1.x + pixman_fixed_1_minus_e); int y1 = pixman_fixed_to_int(traps[n].top); int y2 = pixman_fixed_to_int(traps[n].bottom + pixman_fixed_1_minus_e); if (x1 < extents.x1) extents.x1 = x1; if (x2 > extents.x2) extents.x2 = x2; if (y1 < extents.y1) extents.y1 = y1; if (y2 > extents.y2) extents.y2 = y2; } DBG(("%s: extents (%d, %d), (%d, %d)\n", __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2)); if (!sna_compute_composite_region(&clip, src, NULL, dst, src_x + extents.x1 - dst_x, src_y + extents.y1 - dst_y, 0, 0, extents.x1, extents.y1, extents.x2 - extents.x1, extents.y2 - extents.y1)) { DBG(("%s: trapezoids do not intersect drawable clips\n", __FUNCTION__)) ; return true; } if (!sna->render.check_composite_spans(sna, op, src, dst, clip.extents.x2 - clip.extents.x1, clip.extents.y2 - clip.extents.y1, COMPOSITE_SPANS_RECTILINEAR)) { DBG(("%s: fallback -- composite spans not supported\n", __FUNCTION__)); goto fallback; } c = NULL; if (extents.x2 - extents.x1 > clip.extents.x2 - clip.extents.x1 || extents.y2 - extents.y1 > clip.extents.y2 - clip.extents.y1) { DBG(("%s: forcing clip\n", __FUNCTION__)); c = &clip; } extents = *RegionExtents(&clip); dx = dst->pDrawable->x; dy = dst->pDrawable->y; DBG(("%s: after clip -- extents (%d, %d), (%d, %d), delta=(%d, %d) src -> (%d, %d)\n", __FUNCTION__, extents.x1, extents.y1, extents.x2, extents.y2, dx, dy, src_x + extents.x1 - dst_x - dx, src_y + extents.y1 - dst_y - dy)); switch (op) { case PictOpAdd: case PictOpOver: priv = sna_pixmap(get_drawable_pixmap(dst->pDrawable)); assert(priv != NULL); if (priv->clear && priv->clear_color == 0) { DBG(("%s: converting %d to PictOpSrc\n", __FUNCTION__, op)); op = PictOpSrc; } break; case PictOpIn: priv = sna_pixmap(get_drawable_pixmap(dst->pDrawable)); assert(priv != NULL); if (priv->clear && priv->clear_color == 0) { DBG(("%s: clear destination using In, skipping\n", __FUNCTION__)); return true; } break; } if (!sna->render.composite_spans(sna, op, src, dst, src_x + extents.x1 - dst_x - dx, src_y + extents.y1 - dst_y - dy, extents.x1, extents.y1, extents.x2 - extents.x1, extents.y2 - extents.y1, COMPOSITE_SPANS_RECTILINEAR, memset(&tmp, 0, sizeof(tmp)))) { DBG(("%s: composite spans render op not supported\n", __FUNCTION__)); REGION_UNINIT(NULL, &clip); goto fallback; } for (n = 0; n < ntrap; n++) composite_unaligned_trap(sna, &tmp, &traps[n], dx, dy, c); tmp.done(sna, &tmp); REGION_UNINIT(NULL, &clip); return true; }