From c19bf1499a95bed5fb1be145eefd8e0c6ee2a634 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 5 Mar 2012 10:06:49 +0000 Subject: [PATCH] image: Add a simple inplace blitter for spans Signed-off-by: Chris Wilson --- src/cairo-image-compositor.c | 148 ++++++++++++++---- ...ecording-surface-extend-none.rgb24.ref.png | Bin 3133 -> 3128 bytes .../recording-surface-over.rgb24.ref.png | Bin 3133 -> 3128 bytes 3 files changed, 116 insertions(+), 32 deletions(-) diff --git a/src/cairo-image-compositor.c b/src/cairo-image-compositor.c index e0911dfe2..16b5a1100 100644 --- a/src/cairo-image-compositor.c +++ b/src/cairo-image-compositor.c @@ -1773,13 +1773,13 @@ mono_renderer_init (cairo_image_span_renderer_t *r, &tx, &ty) && composite->bounded.x + tx >= 0 && composite->bounded.y + ty >= 0 && - composite->bounded.x + composite->bounded.width + tx <= src->width && + composite->bounded.x + composite->bounded.width + tx <= src->width && composite->bounded.y + composite->bounded.height + ty <= src->height) { r->u.blit.stride = dst->stride; r->u.blit.data = dst->data; r->u.blit.src_stride = src->stride; - r->u.blit.src_data = src->data + src->stride * ty + tx * PIXMAN_FORMAT_BPP(src->format)/8; + r->u.blit.src_data = src->data + src->stride * ty + tx * 4; r->base.render_rows = _blit_spans; } } @@ -1812,42 +1812,38 @@ mono_renderer_init (cairo_image_span_renderer_t *r, #define RB_ONE_HALF 0x007f007f #define RB_MASK_PLUS_ONE 0x01000100 #define G_SHIFT 8 -#define UNc_rb_MUL_UNc(x, a, t) \ - do { \ - t = ((x) & RB_MASK) * (a); \ - t += RB_ONE_HALF; \ - x = (t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT; \ - x &= RB_MASK; \ - } while (0) -#define UNc_rb_ADD_UNc_rb(x, y, t) \ - do { \ - t = ((x) + (y)); \ - t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK); \ - x = (t & RB_MASK); \ - } while (0) +static inline uint32_t +mul8x2_8 (uint32_t a, uint8_t b) +{ + uint32_t t = (a & RB_MASK) * b + RB_ONE_HALF; + return ((t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT) & RB_MASK; +} + +static inline uint32_t +add8x2_8x2 (uint32_t a, uint32_t b) +{ + uint32_t t = a + b; + t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK); + return t & RB_MASK; +} + static inline uint8_t -mul8 (uint8_t a, uint8_t b) +mul8_8 (uint8_t a, uint8_t b) { uint16_t t = a * (uint16_t)b + ONE_HALF; return ((t >> G_SHIFT) + t) >> G_SHIFT; } + static inline uint32_t lerp8x4 (uint32_t src, uint8_t a, uint32_t dst) { uint8_t ia = ~a; - uint32_t r1, r2, r3, t; + uint32_t r1, r2; - r1 = src; - r2 = dst; - UNc_rb_MUL_UNc (r1, a, t); - UNc_rb_MUL_UNc (r2, ia, t); - UNc_rb_ADD_UNc_rb (r1, r2, t); - - r2 = src >> G_SHIFT; - r3 = dst >> G_SHIFT; - UNc_rb_MUL_UNc (r2, a, t); - UNc_rb_MUL_UNc (r3, ia, t); - UNc_rb_ADD_UNc_rb (r2, r3, t); + r1 = add8x2_8x2 (mul8x2_8 (src, a), + mul8x2_8 (dst, ia)); + r2 = add8x2_8x2 (mul8x2_8 (src >> G_SHIFT, a), + mul8x2_8 (dst >> G_SHIFT, ia)); return r1 | (r2 << G_SHIFT); } @@ -1995,7 +1991,7 @@ _fill_a8_lerp_spans (void *abstract_renderer, int y, int h, if (likely(h == 1)) { do { - uint8_t a = mul8 (spans[0].coverage, r->op); + uint8_t a = mul8_8 (spans[0].coverage, r->op); if (a) { int len = spans[1].x - spans[0].x; uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x; @@ -2010,7 +2006,7 @@ _fill_a8_lerp_spans (void *abstract_renderer, int y, int h, } while (--num_spans > 1); } else { do { - uint8_t a = mul8 (spans[0].coverage, r->op); + uint8_t a = mul8_8 (spans[0].coverage, r->op); if (a) { int yy = y, hh = h; uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f; @@ -2043,7 +2039,7 @@ _fill_xrgb32_lerp_spans (void *abstract_renderer, int y, int h, if (likely(h == 1)) { do { - uint8_t a = mul8 (spans[0].coverage, r->op); + uint8_t a = mul8_8 (spans[0].coverage, r->op); if (a) { int len = spans[1].x - spans[0].x; uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4); @@ -2056,7 +2052,7 @@ _fill_xrgb32_lerp_spans (void *abstract_renderer, int y, int h, } while (--num_spans > 1); } else { do { - uint8_t a = mul8 (spans[0].coverage, r->op); + uint8_t a = mul8_8 (spans[0].coverage, r->op); if (a) { int yy = y, hh = h; do { @@ -2076,6 +2072,68 @@ _fill_xrgb32_lerp_spans (void *abstract_renderer, int y, int h, return CAIRO_STATUS_SUCCESS; } +static cairo_status_t +_blit_xrgb32_lerp_spans (void *abstract_renderer, int y, int h, + const cairo_half_open_span_t *spans, unsigned num_spans) +{ + cairo_image_span_renderer_t *r = abstract_renderer; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + if (likely(h == 1)) { + uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride; + uint8_t *dst = r->u.blit.data + y*r->u.blit.stride; + do { + uint8_t a = mul8_8 (spans[0].coverage, r->op); + if (a) { + uint32_t *s = (uint32_t*)src + spans[0].x; + uint32_t *d = (uint32_t*)dst + spans[0].x; + int len = spans[1].x - spans[0].x; + if (a == 0xff) { + if (len == 1) + *d = *s; + else + memcpy(d, s, len*4); + } else { + while (len--) { + *d = lerp8x4 (*s, a, *d); + s++, d++; + } + } + } + spans++; + } while (--num_spans > 1); + } else { + do { + uint8_t a = mul8_8 (spans[0].coverage, r->op); + if (a) { + int yy = y, hh = h; + do { + uint32_t *s = (uint32_t *)(r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x * 4); + uint32_t *d = (uint32_t *)(r->u.blit.data + yy*r->u.blit.stride + spans[0].x * 4); + int len = spans[1].x - spans[0].x; + if (a == 0xff) { + if (len == 1) + *d = *s; + else + memcpy(d, s, len * 4); + } else { + while (len--) { + *d = lerp8x4 (*s, a, *d); + s++, d++; + } + } + yy++; + } while (--hh); + } + spans++; + } while (--num_spans > 1); + } + + return CAIRO_STATUS_SUCCESS; +} + static cairo_int_status_t inplace_renderer_init (cairo_image_span_renderer_t *r, const cairo_composite_rectangles_t *composite, @@ -2137,6 +2195,32 @@ inplace_renderer_init (cairo_image_span_renderer_t *r, r->u.fill.data = dst->data; r->u.fill.stride = dst->stride; } + } else if ((dst->format == CAIRO_FORMAT_ARGB32 || dst->format == CAIRO_FORMAT_RGB24) && + (composite->op == CAIRO_OPERATOR_SOURCE || + (composite->op == CAIRO_OPERATOR_OVER && + (dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) && + composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE && + composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE && + to_image_surface(composite->source_pattern.surface.surface)->format == dst->format) + { + cairo_image_surface_t *src = + to_image_surface(composite->source_pattern.surface.surface); + int tx, ty; + + if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix, + &tx, &ty) && + composite->bounded.x + tx >= 0 && + composite->bounded.y + ty >= 0 && + composite->bounded.x + composite->bounded.width + tx <= src->width && + composite->bounded.y + composite->bounded.height + ty <= src->height) { + + assert(PIXMAN_FORMAT_BPP(dst->pixman_format) == 32); + r->u.blit.stride = dst->stride; + r->u.blit.data = dst->data; + r->u.blit.src_stride = src->stride; + r->u.blit.src_data = src->data + src->stride * ty + tx * 4; + r->base.render_rows = _blit_xrgb32_lerp_spans; + } } if (r->base.render_rows == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; diff --git a/test/reference/recording-surface-extend-none.rgb24.ref.png b/test/reference/recording-surface-extend-none.rgb24.ref.png index 348167349e215ae144f90a67f49adafaa4378c02..bd84338f29fc30fe0e920a90ec9d5d1b9d9050b6 100644 GIT binary patch delta 2966 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 M07*qoM6N<$f)wWYGynhq delta 2982 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 diff --git a/test/reference/recording-surface-over.rgb24.ref.png b/test/reference/recording-surface-over.rgb24.ref.png index 348167349e215ae144f90a67f49adafaa4378c02..bd84338f29fc30fe0e920a90ec9d5d1b9d9050b6 100644 GIT binary patch delta 2966 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 M07*qoM6N<$f)wWYGynhq delta 2982 zcmV;X3t9BI7`+&fHh=O-L_t(|obBCtY*l3%$MGL+p{2bPDA2MNC=|xRu!;_a+5-}p zs3@WthG+;P5rj-wCyGmGjJrgPI|OwiD#l5SK~0QNa~KeTf*)WWln3zL{4h1(QI3V(iyJydycinYY-6N@~ zsfJ%F3#!s5p7@q{I2ZG2y;(%7-aarg&-1&CPWz z6F4uDi#stfaqQT!R;?d@{IQeCzxJQf_QM+W* z*!%Cl?_@Fvq6jSRY15`zHGTj6_fBSl;EJ^3o;7QhRg>H8-nwt(yo)3A>?E{sq1v~<@i{3; zhM~iUb^g4at~@C(*Aq|Z=br=BAAAeivjQ7Dv+vxwb8o!yhE;I1qMy~Xl1}S%a!#^U z*tTukzWeSwJK2mGqr30+aJ-~MZ@+D)Lw^ey8S39(-+mjwE@3G+Blq;_(;IvfEG{n2 zFpTo@@>8cyHI84P1qn)!lrSwpmbZ~ww{D%ygdTg$^YQq28HUcDRZ)@6p6e?vR(`(b z%~O89?EQj)|AKJM?PUkw)TvV&KZSLqMs~aY4v=JNgk`sD*RHjh(4&v~2)O@#J%96z z&CWepvqmnL9(pK%UBZJPjNERwJ0T&VSFc`H0fu2jM@Jt%eE9m0^HnpSz0C45YVY2? z+qZAGlTAj3thc7z6cr`I(6wtideqLJ(nxu^^71rehRVwWcwvuvoWADnEY;pQ`g~OO zi!Z)7^YYC8>Mu!?r16%K_Mf_KG&!LeGt{l=FFni7R>zL|cK{-{>)*Ll79>thjrM;5Pr`+fmt(LzrOiXdW#n6gAW3b3mR~!xaUhB z`VqEs=gxwHf>EPJ`PFH$w9#|RNR1q-p*yt0^4td>RCKfd?%TFCf7l*3PPw`AwLS|w zoG9*L(h14W>&ddRvRE^=Pmez8>ML}$WIbsSulPLWUC;&3&6_u?w0~3?K3_xZmF+d7 z8g1GrJ6opN%x>VB6N@zm&VJZtfhdye#hbj|PhDpeiPkr5i92c<5)q7A97eWkKeEw&u@$S3+Mp~ObUEh4u{BB^$p?U7VN!um=SFY`>IPR1F zElK;e|8srbYOwER<3@SfZjR6tv~S|vCbgF~ z)jv3K;>0`dxWnp{>lflr>8W74)*Is+Axrsjcq~hYFz<(^?f;MtDkdcuwdh}?kR*&Bu&QcKFK*x1;E z2M-1&U4Mjw5E{8tQc@m&{PD)ezyA8`UAuO9IdQm#|5mfW?{kEH_0?A^D=Y2hh;R}r zBe$fAiVD*-yLRoGoP6`)4W*@}J|AgWRA*|@G|dYaE~KZYTLq6BH!dY5WnZ&*^C9$w z(8ygYHa2#}iWS#Czwp8fRaI3sI$6Gax%aK%Yk$|SJ$dq^o%|3cLb157n>1-sRaMod zO`Cj&S{OWd@XVQBzIv%sr%tU|v&MG+2yvlX+<`K8?%dwJdwU7^;)^eS|NVEz@_}O_ zzqn<6t6J;Ag$u8|@`_{mz!@QQL)mgX-m2EswryL(FfL!deEj%vhjT;AX%!=Pt-80W zR)1LTi5RV7RX}EDX3w5I^YZeX%nAY2dPnYBOP4N9Oic6=ux{PDX6-9P zkWk!`l%AgMWw))QqN1Yr-h0ond=L<=_kXgat}|!On5LPQme$eYRc0=i%P@?Rl9JP> zPdl6&Y-8lEr(3sf3l}c*5^(9#rI%iM$+3LU9E{xcR904+rkRwK)ZmppwPIpo48y3d zu0D3`n8UfDsTjFEYumQ%(@#I`J%;6;5XZ>v+10C8P1B5uit5|9uT_j;7%rFV0DlkY z2~~{TzO7ujvP~Or|4^$}uRhOPn?esGw{M#^Z7M4(8$5WhRX}=r`i>p`JWC6XV&rau zMT-`7?%cWY@#mj^zPh@)@o@wfBX<*Q-@g6Wv17xB53hUhr=NbRePt#>3dP+tXV0EB zO|xIWe%Bwda`EEDPe1)M*hv8am4B3!l#`QV^%NlhKnSvagf~Kj6EA!U58gJtHZqZU zrZ7Np*NBoH^Di<^>MF6p8)0Uu^e102i=9XWXc_;sZo18n>IXtvmh=}-$LBbm2+%T~ zs{iBM;A8=`j6X|%l_X_Iw+1;Epam?D{CNE;5T2Md1poj500000000000F#{ygBt(< c00@8o2g&mFSyyOXHvj+t07*qoM6N<$f)Z%~UjP6A