GUACAMOLE-188: Use Porter-Duff "over" operator when drawing with ARGB surfaces.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/12d29569 Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/12d29569 Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/12d29569 Branch: refs/heads/master Commit: 12d29569a6a52227704149b62935dfc2032100ca Parents: 4da4ce7 Author: Michael Jumper <[email protected]> Authored: Sat Feb 25 16:13:56 2017 -0800 Committer: Michael Jumper <[email protected]> Committed: Sat Feb 25 19:57:10 2017 -0800 ---------------------------------------------------------------------- src/common/common/surface.h | 23 ++++++--- src/common/surface.c | 101 +++++++++++++++++++++++++++++++++++---- 2 files changed, 108 insertions(+), 16 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/12d29569/src/common/common/surface.h ---------------------------------------------------------------------- diff --git a/src/common/common/surface.h b/src/common/common/surface.h index 964f8d3..c778883 100644 --- a/src/common/common/surface.h +++ b/src/common/common/surface.h @@ -279,14 +279,25 @@ void guac_common_surface_free(guac_common_surface* surface); void guac_common_surface_resize(guac_common_surface* surface, int w, int h); /** - * Draws the given data to the given guac_common_surface. + * Draws the given data to the given guac_common_surface. If the source surface + * is ARGB, the draw operation will be performed using the Porter-Duff "over" + * composite operator. If the source surface is RGB (no alpha channel), no + * compositing is performed and destination pixels are ignored. * - * @param surface The surface to draw to. - * @param x The X coordinate of the draw location. - * @param y The Y coordinate of the draw location. - * @param src The Cairo surface to retrieve data from. + * @param surface + * The surface to draw to. + * + * @param x + * The X coordinate of the draw location. + * + * @param y + * The Y coordinate of the draw location. + * + * @param src + * The Cairo surface to retrieve data from. */ -void guac_common_surface_draw(guac_common_surface* surface, int x, int y, cairo_surface_t* src); +void guac_common_surface_draw(guac_common_surface* surface, int x, int y, + cairo_surface_t* src); /** * Paints to the given guac_common_surface using the given data as a stencil, http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/12d29569/src/common/surface.c ---------------------------------------------------------------------- diff --git a/src/common/surface.c b/src/common/surface.c index ac66a81..bc0c693 100644 --- a/src/common/surface.c +++ b/src/common/surface.c @@ -863,6 +863,75 @@ static void __guac_common_surface_set(guac_common_surface* dst, } /** + * Applies the Porter-Duff "over" composite operator, blending the two given + * color components using the given alpha value. + * + * @param dst + * The destination color component. + * + * @param src + * The source color component. + * + * @param alpha + * The alpha value which applies to the blending operation. + * + * @return + * The result of applying the Porter-Duff "over" composite operator to the + * given source and destination components. + */ +static int guac_common_surface_blend_component(int dst, int src, int alpha) { + return src + dst * (0xFF - alpha); +} + +/** + * Applies the Porter-Duff "over" composite operator, blending each component + * of the two given ARGB colors. + * + * @param dst + * The destination ARGB color. + * + * @param src + * The source ARGB color. + * + * @return + * The result of applying the Porter-Duff "over" composite operator to the + * given source and destination colors. + */ +static uint32_t guac_common_surface_argb_blend(uint32_t dst, uint32_t src) { + + /* Separate destination ARGB color into its components */ + int dst_a = (dst >> 24) & 0xFF; + int dst_r = (dst >> 16) & 0xFF; + int dst_g = (dst >> 8) & 0xFF; + int dst_b = dst & 0xFF; + + /* Separate source ARGB color into its components */ + int src_a = (src >> 24) & 0xFF; + int src_r = (src >> 16) & 0xFF; + int src_g = (src >> 8) & 0xFF; + int src_b = src & 0xFF; + + /* If source is fully opaque (or destination is fully transparent), the + * blended result is the source */ + if (src_a == 0xFF || dst_a == 0x00) + return src; + + /* If source is fully transparent, the blended result is the destination */ + if (src_a == 0x00) + return dst; + + /* Otherwise, blend each ARGB component, assuming pre-multiplied alpha */ + int r = guac_common_surface_blend_component(dst_r, src_r, src_a); + int g = guac_common_surface_blend_component(dst_g, src_g, src_a); + int b = guac_common_surface_blend_component(dst_b, src_b, src_a); + int a = guac_common_surface_blend_component(dst_a, src_a, src_a); + + /* Recombine blended components */ + return (a << 24) | (r << 16) | (g << 8) | b; + +} + +/** * Copies data from the given buffer to the surface at the given coordinates. * The dimensions and location of the destination rectangle will be altered * to remove as many unchanged pixels as possible. @@ -906,22 +975,34 @@ static void __guac_common_surface_put(unsigned char* src_buffer, int src_stride, /* Copy row */ for (x=0; x < rect->width; x++) { - if (opaque || (*src_current & 0xFF000000)) { + uint32_t color; - uint32_t new_color = *src_current | 0xFF000000; - uint32_t old_color = *dst_current; + /* Get source and destination color values */ + uint32_t src_color = *src_current; + uint32_t dst_color = *dst_current; - if (old_color != new_color) { - if (x < min_x) min_x = x; - if (y < min_y) min_y = y; - if (x > max_x) max_x = x; - if (y > max_y) max_y = y; - *dst_current = new_color; - } + /* Ignore alpha channel if opaque */ + if (opaque) + color = src_color | 0xFF000000; + + /* Otherwise, perform alpha blending operation */ + else + color = guac_common_surface_argb_blend(dst_color, src_color); + + /* If the destination color is changing, update rectangle bounds + * and store the new color */ + if (dst_color != color) { + if (x < min_x) min_x = x; + if (y < min_y) min_y = y; + if (x > max_x) max_x = x; + if (y > max_y) max_y = y; + *dst_current = color; } + /* Advance to next pixel */ src_current++; dst_current++; + } /* Next row */
