GUACAMOLE-188: Merge alpha component support for common surface.

This commit is contained in:
James Muehlner 2017-01-29 12:45:46 -08:00
commit d831a4b9df
5 changed files with 210 additions and 58 deletions

View File

@ -336,20 +336,43 @@ void guac_common_surface_transfer(guac_common_surface* src, int sx, int sy, int
guac_transfer_function op, guac_common_surface* dst, int dx, int dy); guac_transfer_function op, guac_common_surface* dst, int dx, int dy);
/** /**
* Draws a solid color rectangle at the given coordinates on the given surface. * Assigns the given value to all pixels within a rectangle of the given
* surface. The color of all pixels within the rectangle, including the alpha
* component, is entirely replaced.
* *
* @param surface The surface to draw upon. * @param surface
* @param x The X coordinate of the upper-left corner of the rectangle. * The surface to draw upon.
* @param y The Y coordinate of the upper-left corner of the rectangle. *
* @param w The width of the rectangle. * @param x
* @param h The height of the rectangle. * The X coordinate of the upper-left corner of the rectangle.
* @param red The red component of the color of the rectangle. *
* @param green The green component of the color of the rectangle. * @param y
* @param blue The blue component of the color of the rectangle. * The Y coordinate of the upper-left corner of the rectangle.
*
* @param w
* The width of the rectangle.
*
* @param h
* The height of the rectangle.
*
* @param red
* The red component of the color value to assign to all pixels within the
* rectangle.
*
* @param green
* The green component of the color value to assign to all pixels within
* the rectangle.
*
* @param blue
* The blue component of the color value to assign to all pixels within the
* rectangle.
*
* @param alpha
* The alpha component of the color value to assign to all pixels within
* the rectangle.
*/ */
void guac_common_surface_rect(guac_common_surface* surface, void guac_common_surface_set(guac_common_surface* surface, int x, int y,
int x, int y, int w, int h, int w, int h, int red, int green, int blue, int alpha);
int red, int green, int blue);
/** /**
* Given the coordinates and dimensions of a rectangle, clips all future * Given the coordinates and dimensions of a rectangle, clips all future

View File

@ -224,6 +224,54 @@ static void __guac_common_clip_rect(guac_common_surface* surface,
} }
/**
* Returns whether a rectangle within the given surface contains only fully
* opaque pixels.
*
* @param surface
* The surface to check.
*
* @param rect
* The rectangle to check.
*
* @return
* Non-zero if the rectangle contains only fully opaque pixels, zero
* otherwise.
*/
static int __guac_common_surface_is_opaque(guac_common_surface* surface,
guac_common_rect* rect) {
int x, y;
int stride = surface ->stride;
unsigned char* buffer =
surface->buffer + (stride * rect->y) + (4 * rect->x);
/* For each row */
for (y = 0; y < rect->height; y++) {
/* Search for a non-opaque pixel */
uint32_t* current = (uint32_t*) buffer;
for (x=0; x < rect->width; x++) {
/* Rectangle is non-opaque if a single non-opaque pixel is found */
uint32_t color = *(current++);
if ((color & 0xFF000000) != 0xFF000000)
return 0;
}
/* Next row */
buffer += stride;
}
/* Rectangle is opaque */
return 1;
}
/** /**
* Returns whether the given rectangle should be combined into the existing * Returns whether the given rectangle should be combined into the existing
* dirty rectangle, to be eventually flushed as a "png" instruction. * dirty rectangle, to be eventually flushed as a "png" instruction.
@ -730,24 +778,41 @@ static int __guac_common_surface_transfer_int(guac_transfer_function op, uint32_
} }
/** /**
* Draws a rectangle of solid color within the backing surface of the * Assigns the given value to all pixels within a rectangle of the backing
* given destination surface. * surface of the given destination surface. The color of all pixels within the
* rectangle, including the alpha component, is entirely replaced.
* *
* @param dst The destination surface. * @param dst
* @param rect The rectangle to draw. * The destination surface.
* @param red The red component of the color of the rectangle. *
* @param green The green component of the color of the rectangle. * @param rect
* @param blue The blue component of the color of the rectangle. * The rectangle to draw.
*
* @param red
* The red component of the color value to assign to all pixels within the
* rectangle.
*
* @param green
* The green component of the color value to assign to all pixels within
* the rectangle.
*
* @param blue
* The blue component of the color value to assign to all pixels within the
* rectangle.
*
* @param alpha
* The alpha component of the color value to assign to all pixels within
* the rectangle.
*/ */
static void __guac_common_surface_rect(guac_common_surface* dst, guac_common_rect* rect, static void __guac_common_surface_set(guac_common_surface* dst,
int red, int green, int blue) { guac_common_rect* rect, int red, int green, int blue, int alpha) {
int x, y; int x, y;
int dst_stride; int dst_stride;
unsigned char* dst_buffer; unsigned char* dst_buffer;
uint32_t color = 0xFF000000 | (red << 16) | (green << 8) | blue; uint32_t color = (alpha << 24) | (red << 16) | (green << 8) | blue;
int min_x = rect->width - 1; int min_x = rect->width - 1;
int min_y = rect->height - 1; int min_y = rect->height - 1;
@ -1063,7 +1128,7 @@ guac_common_surface* guac_common_surface_alloc(guac_client* client,
pthread_mutex_init(&surface->_lock, NULL); pthread_mutex_init(&surface->_lock, NULL);
/* Create corresponding Cairo surface */ /* Create corresponding Cairo surface */
surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w); surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w);
surface->buffer = calloc(h, surface->stride); surface->buffer = calloc(h, surface->stride);
/* Create corresponding heat map */ /* Create corresponding heat map */
@ -1130,7 +1195,7 @@ void guac_common_surface_resize(guac_common_surface* surface, int w, int h) {
/* Re-initialize at new size */ /* Re-initialize at new size */
surface->width = w; surface->width = w;
surface->height = h; surface->height = h;
surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w); surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w);
surface->buffer = calloc(h, surface->stride); surface->buffer = calloc(h, surface->stride);
__guac_common_bound_rect(surface, &surface->clip_rect, NULL, NULL); __guac_common_bound_rect(surface, &surface->clip_rect, NULL, NULL);
@ -1373,8 +1438,8 @@ complete:
} }
void guac_common_surface_rect(guac_common_surface* surface, void guac_common_surface_set(guac_common_surface* surface,
int x, int y, int w, int h, int red, int green, int blue) { int x, int y, int w, int h, int red, int green, int blue, int alpha) {
pthread_mutex_lock(&surface->_lock); pthread_mutex_lock(&surface->_lock);
@ -1390,19 +1455,31 @@ void guac_common_surface_rect(guac_common_surface* surface,
goto complete; goto complete;
/* Update backing surface */ /* Update backing surface */
__guac_common_surface_rect(surface, &rect, red, green, blue); __guac_common_surface_set(surface, &rect, red, green, blue, alpha);
if (rect.width <= 0 || rect.height <= 0) if (rect.width <= 0 || rect.height <= 0)
goto complete; goto complete;
/* Handle as normal draw if non-opaque */
if (alpha != 0xFF) {
/* Flush if not combining */
if (!__guac_common_should_combine(surface, &rect, 0))
__guac_common_surface_flush_deferred(surface);
/* Always defer draws */
__guac_common_mark_dirty(surface, &rect);
}
/* Defer if combining */ /* Defer if combining */
if (__guac_common_should_combine(surface, &rect, 1)) else if (__guac_common_should_combine(surface, &rect, 1))
__guac_common_mark_dirty(surface, &rect); __guac_common_mark_dirty(surface, &rect);
/* Otherwise, flush and draw immediately */ /* Otherwise, flush and draw immediately */
else { else {
__guac_common_surface_flush(surface); __guac_common_surface_flush(surface);
guac_protocol_send_rect(socket, layer, rect.x, rect.y, rect.width, rect.height); guac_protocol_send_rect(socket, layer, rect.x, rect.y, rect.width, rect.height);
guac_protocol_send_cfill(socket, GUAC_COMP_OVER, layer, red, green, blue, 0xFF); guac_protocol_send_cfill(socket, GUAC_COMP_OVER, layer, red, green, blue, alpha);
surface->realized = 1; surface->realized = 1;
} }
@ -1444,8 +1521,12 @@ void guac_common_surface_reset_clip(guac_common_surface* surface) {
* *
* @param surface * @param surface
* The surface to flush. * The surface to flush.
*
* @param opaque
* Whether the rectangle being flushed contains only fully-opaque pixels.
*/ */
static void __guac_common_surface_flush_to_png(guac_common_surface* surface) { static void __guac_common_surface_flush_to_png(guac_common_surface* surface,
int opaque) {
if (surface->dirty) { if (surface->dirty) {
@ -1457,10 +1538,30 @@ static void __guac_common_surface_flush_to_png(guac_common_surface* surface) {
+ surface->dirty_rect.y * surface->stride + surface->dirty_rect.y * surface->stride
+ surface->dirty_rect.x * 4; + surface->dirty_rect.x * 4;
cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer, cairo_surface_t* rect;
/* Use RGB24 if the image is fully opaque */
if (opaque)
rect = cairo_image_surface_create_for_data(buffer,
CAIRO_FORMAT_RGB24, surface->dirty_rect.width, CAIRO_FORMAT_RGB24, surface->dirty_rect.width,
surface->dirty_rect.height, surface->stride); surface->dirty_rect.height, surface->stride);
/* Otherwise ARGB32 is needed */
else {
rect = cairo_image_surface_create_for_data(buffer,
CAIRO_FORMAT_ARGB32, surface->dirty_rect.width,
surface->dirty_rect.height, surface->stride);
/* Clear destination rect first */
guac_protocol_send_rect(socket, layer,
surface->dirty_rect.x, surface->dirty_rect.y,
surface->dirty_rect.width, surface->dirty_rect.height);
guac_protocol_send_cfill(socket, GUAC_COMP_ROUT, layer,
0x00, 0x00, 0x00, 0xFF);
}
/* Send PNG for rect */ /* Send PNG for rect */
guac_client_stream_png(surface->client, socket, GUAC_COMP_OVER, guac_client_stream_png(surface->client, socket, GUAC_COMP_OVER,
layer, surface->dirty_rect.x, surface->dirty_rect.y, rect); layer, surface->dirty_rect.x, surface->dirty_rect.y, rect);
@ -1531,8 +1632,12 @@ static void __guac_common_surface_flush_to_jpeg(guac_common_surface* surface) {
* *
* @param surface * @param surface
* The surface to flush. * The surface to flush.
*
* @param opaque
* Whether the rectangle being flushed contains only fully-opaque pixels.
*/ */
static void __guac_common_surface_flush_to_webp(guac_common_surface* surface) { static void __guac_common_surface_flush_to_webp(guac_common_surface* surface,
int opaque) {
if (surface->dirty) { if (surface->dirty) {
@ -1552,10 +1657,20 @@ static void __guac_common_surface_flush_to_webp(guac_common_surface* surface) {
+ surface->dirty_rect.y * surface->stride + surface->dirty_rect.y * surface->stride
+ surface->dirty_rect.x * 4; + surface->dirty_rect.x * 4;
cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer, cairo_surface_t* rect;
/* Use RGB24 if the image is fully opaque */
if (opaque)
rect = cairo_image_surface_create_for_data(buffer,
CAIRO_FORMAT_RGB24, surface->dirty_rect.width, CAIRO_FORMAT_RGB24, surface->dirty_rect.width,
surface->dirty_rect.height, surface->stride); surface->dirty_rect.height, surface->stride);
/* Otherwise ARGB32 is needed */
else
rect = cairo_image_surface_create_for_data(buffer,
CAIRO_FORMAT_ARGB32, surface->dirty_rect.width,
surface->dirty_rect.height, surface->stride);
/* Send WebP for rect */ /* Send WebP for rect */
guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer, guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer,
surface->dirty_rect.x, surface->dirty_rect.y, rect, surface->dirty_rect.x, surface->dirty_rect.y, rect,
@ -1684,19 +1799,22 @@ static void __guac_common_surface_flush(guac_common_surface* surface) {
flushed++; flushed++;
int opaque = __guac_common_surface_is_opaque(surface,
&surface->dirty_rect);
/* Prefer WebP when reasonable */ /* Prefer WebP when reasonable */
if (__guac_common_surface_should_use_webp(surface, if (__guac_common_surface_should_use_webp(surface,
&surface->dirty_rect)) &surface->dirty_rect))
__guac_common_surface_flush_to_webp(surface); __guac_common_surface_flush_to_webp(surface, opaque);
/* If not WebP, JPEG is the next best (lossy) choice */ /* If not WebP, JPEG is the next best (lossy) choice */
else if (__guac_common_surface_should_use_jpeg(surface, else if (opaque && __guac_common_surface_should_use_jpeg(
&surface->dirty_rect)) surface, &surface->dirty_rect))
__guac_common_surface_flush_to_jpeg(surface); __guac_common_surface_flush_to_jpeg(surface);
/* Use PNG if no lossy formats are appropriate */ /* Use PNG if no lossy formats are appropriate */
else else
__guac_common_surface_flush_to_png(surface); __guac_common_surface_flush_to_png(surface, opaque);
} }
@ -1755,7 +1873,7 @@ void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
/* Get entire surface */ /* Get entire surface */
cairo_surface_t* rect = cairo_image_surface_create_for_data( cairo_surface_t* rect = cairo_image_surface_create_for_data(
surface->buffer, CAIRO_FORMAT_RGB24, surface->buffer, CAIRO_FORMAT_ARGB32,
surface->width, surface->height, surface->stride); surface->width, surface->height, surface->stride);
/* Send PNG for rect */ /* Send PNG for rect */

View File

@ -113,7 +113,8 @@ void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) {
case 0: case 0:
/* Send black rectangle */ /* Send black rectangle */
guac_common_surface_rect(current_surface, x, y, w, h, 0, 0, 0); guac_common_surface_set(current_surface, x, y, w, h,
0x00, 0x00, 0x00, 0xFF);
break; break;
/* DSTINVERT */ /* DSTINVERT */
@ -128,7 +129,8 @@ void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) {
/* Whiteness */ /* Whiteness */
case 0xFF: case 0xFF:
guac_common_surface_rect(current_surface, x, y, w, h, 0xFF, 0xFF, 0xFF); guac_common_surface_set(current_surface, x, y, w, h,
0xFF, 0xFF, 0xFF, 0xFF);
break; break;
/* Unsupported ROP3 */ /* Unsupported ROP3 */
@ -175,7 +177,8 @@ void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
/* If blackness, send black rectangle */ /* If blackness, send black rectangle */
case 0x00: case 0x00:
guac_common_surface_rect(current_surface, x, y, w, h, 0, 0, 0); guac_common_surface_set(current_surface, x, y, w, h,
0x00, 0x00, 0x00, 0xFF);
break; break;
/* If NOP, do nothing */ /* If NOP, do nothing */
@ -185,15 +188,17 @@ void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
/* If operation is just a copy, send foreground only */ /* If operation is just a copy, send foreground only */
case 0xCC: case 0xCC:
case 0xF0: case 0xF0:
guac_common_surface_rect(current_surface, x, y, w, h, guac_common_surface_set(current_surface, x, y, w, h,
(patblt->foreColor >> 16) & 0xFF, (patblt->foreColor >> 16) & 0xFF,
(patblt->foreColor >> 8 ) & 0xFF, (patblt->foreColor >> 8 ) & 0xFF,
(patblt->foreColor ) & 0xFF); (patblt->foreColor ) & 0xFF,
0xFF);
break; break;
/* If whiteness, send white rectangle */ /* If whiteness, send white rectangle */
case 0xFF: case 0xFF:
guac_common_surface_rect(current_surface, x, y, w, h, 0xFF, 0xFF, 0xFF); guac_common_surface_set(current_surface, x, y, w, h,
0xFF, 0xFF, 0xFF, 0xFF);
break; break;
/* Otherwise, invert entire rect */ /* Otherwise, invert entire rect */
@ -250,7 +255,8 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
/* If blackness, send black rectangle */ /* If blackness, send black rectangle */
case 0x00: case 0x00:
guac_common_surface_rect(current_surface, x, y, w, h, 0, 0, 0); guac_common_surface_set(current_surface, x, y, w, h,
0x00, 0x00, 0x00, 0xFF);
break; break;
/* If NOP, do nothing */ /* If NOP, do nothing */
@ -294,7 +300,8 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
/* If whiteness, send white rectangle */ /* If whiteness, send white rectangle */
case 0xFF: case 0xFF:
guac_common_surface_rect(current_surface, x, y, w, h, 0xFF, 0xFF, 0xFF); guac_common_surface_set(current_surface, x, y, w, h,
0xFF, 0xFF, 0xFF, 0xFF);
break; break;
/* Otherwise, use transfer */ /* Otherwise, use transfer */
@ -330,10 +337,11 @@ void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect
int w = opaque_rect->nWidth; int w = opaque_rect->nWidth;
int h = opaque_rect->nHeight; int h = opaque_rect->nHeight;
guac_common_surface_rect(current_surface, x, y, w, h, guac_common_surface_set(current_surface, x, y, w, h,
(color >> 16) & 0xFF, (color >> 16) & 0xFF,
(color >> 8 ) & 0xFF, (color >> 8 ) & 0xFF,
(color ) & 0xFF); (color ) & 0xFF,
0xFF);
} }

View File

@ -136,10 +136,12 @@ void guac_rdp_glyph_begindraw(rdpContext* context,
/* Convert background color */ /* Convert background color */
bgcolor = guac_rdp_convert_color(context, bgcolor); bgcolor = guac_rdp_convert_color(context, bgcolor);
guac_common_surface_rect(rdp_client->current_surface, x, y, width, height, guac_common_surface_set(rdp_client->current_surface,
x, y, width, height,
(bgcolor & 0xFF0000) >> 16, (bgcolor & 0xFF0000) >> 16,
(bgcolor & 0x00FF00) >> 8, (bgcolor & 0x00FF00) >> 8,
bgcolor & 0x0000FF); (bgcolor & 0x0000FF),
0xFF);
} }

View File

@ -773,13 +773,14 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display) {
} }
/* Send rect */ /* Send rect */
guac_common_surface_rect( guac_common_surface_set(
display->display_surface, display->display_surface,
col * display->char_width, col * display->char_width,
row * display->char_height, row * display->char_height,
rect_width * display->char_width, rect_width * display->char_width,
rect_height * display->char_height, rect_height * display->char_height,
guac_color->red, guac_color->green, guac_color->blue); guac_color->red, guac_color->green, guac_color->blue,
0xFF);
} /* end if clear operation */ } /* end if clear operation */