GUACAMOLE-188: Use Porter-Duff "over" operator when drawing with ARGB surfaces.
This commit is contained in:
parent
4da4ce7279
commit
12d29569a6
@ -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);
|
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 surface
|
||||||
* @param x The X coordinate of the draw location.
|
* The surface to draw to.
|
||||||
* @param y The Y coordinate of the draw location.
|
*
|
||||||
* @param src The Cairo surface to retrieve data from.
|
* @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,
|
* Paints to the given guac_common_surface using the given data as a stencil,
|
||||||
|
@ -862,6 +862,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.
|
* Copies data from the given buffer to the surface at the given coordinates.
|
||||||
* The dimensions and location of the destination rectangle will be altered
|
* The dimensions and location of the destination rectangle will be altered
|
||||||
@ -906,22 +975,34 @@ static void __guac_common_surface_put(unsigned char* src_buffer, int src_stride,
|
|||||||
/* Copy row */
|
/* Copy row */
|
||||||
for (x=0; x < rect->width; x++) {
|
for (x=0; x < rect->width; x++) {
|
||||||
|
|
||||||
if (opaque || (*src_current & 0xFF000000)) {
|
uint32_t color;
|
||||||
|
|
||||||
uint32_t new_color = *src_current | 0xFF000000;
|
/* Get source and destination color values */
|
||||||
uint32_t old_color = *dst_current;
|
uint32_t src_color = *src_current;
|
||||||
|
uint32_t dst_color = *dst_current;
|
||||||
|
|
||||||
if (old_color != 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 (x < min_x) min_x = x;
|
||||||
if (y < min_y) min_y = y;
|
if (y < min_y) min_y = y;
|
||||||
if (x > max_x) max_x = x;
|
if (x > max_x) max_x = x;
|
||||||
if (y > max_y) max_y = y;
|
if (y > max_y) max_y = y;
|
||||||
*dst_current = new_color;
|
*dst_current = color;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Advance to next pixel */
|
||||||
src_current++;
|
src_current++;
|
||||||
dst_current++;
|
dst_current++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Next row */
|
/* Next row */
|
||||||
|
Loading…
Reference in New Issue
Block a user