GUACAMOLE-188: Allow alpha component to be set within common surface.
This commit is contained in:
parent
ea6b094e24
commit
1a5f48961c
@ -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);
|
||||
|
||||
/**
|
||||
* 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 x The X coordinate of the upper-left corner of the rectangle.
|
||||
* @param y 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 of the rectangle.
|
||||
* @param green The green component of the color of the rectangle.
|
||||
* @param blue The blue component of the color of the rectangle.
|
||||
* @param surface
|
||||
* The surface to draw upon.
|
||||
*
|
||||
* @param x
|
||||
* The X coordinate of the upper-left corner of the rectangle.
|
||||
*
|
||||
* @param y
|
||||
* 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,
|
||||
int x, int y, int w, int h,
|
||||
int red, int green, int blue);
|
||||
void guac_common_surface_set(guac_common_surface* surface, int x, int y,
|
||||
int w, int h, int red, int green, int blue, int alpha);
|
||||
|
||||
/**
|
||||
* Given the coordinates and dimensions of a rectangle, clips all future
|
||||
|
@ -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
|
||||
* 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
|
||||
* given destination surface.
|
||||
* Assigns the given value to all pixels within a rectangle of the backing
|
||||
* 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 rect The rectangle to draw.
|
||||
* @param red The red component of the color of the rectangle.
|
||||
* @param green The green component of the color of the rectangle.
|
||||
* @param blue The blue component of the color of the rectangle.
|
||||
* @param dst
|
||||
* The destination surface.
|
||||
*
|
||||
* @param rect
|
||||
* 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,
|
||||
int red, int green, int blue) {
|
||||
static void __guac_common_surface_set(guac_common_surface* dst,
|
||||
guac_common_rect* rect, int red, int green, int blue, int alpha) {
|
||||
|
||||
int x, y;
|
||||
|
||||
int dst_stride;
|
||||
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_y = rect->height - 1;
|
||||
@ -1063,7 +1128,7 @@ guac_common_surface* guac_common_surface_alloc(guac_client* client,
|
||||
pthread_mutex_init(&surface->_lock, NULL);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Create corresponding heat map */
|
||||
@ -1126,7 +1191,7 @@ void guac_common_surface_resize(guac_common_surface* surface, int w, int h) {
|
||||
/* Re-initialize at new size */
|
||||
surface->width = w;
|
||||
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);
|
||||
__guac_common_bound_rect(surface, &surface->clip_rect, NULL, NULL);
|
||||
|
||||
@ -1368,8 +1433,8 @@ complete:
|
||||
|
||||
}
|
||||
|
||||
void guac_common_surface_rect(guac_common_surface* surface,
|
||||
int x, int y, int w, int h, int red, int green, int blue) {
|
||||
void guac_common_surface_set(guac_common_surface* surface,
|
||||
int x, int y, int w, int h, int red, int green, int blue, int alpha) {
|
||||
|
||||
pthread_mutex_lock(&surface->_lock);
|
||||
|
||||
@ -1385,19 +1450,31 @@ void guac_common_surface_rect(guac_common_surface* surface,
|
||||
goto complete;
|
||||
|
||||
/* 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)
|
||||
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 */
|
||||
if (__guac_common_should_combine(surface, &rect, 1))
|
||||
else if (__guac_common_should_combine(surface, &rect, 1))
|
||||
__guac_common_mark_dirty(surface, &rect);
|
||||
|
||||
/* Otherwise, flush and draw immediately */
|
||||
else {
|
||||
__guac_common_surface_flush(surface);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1439,8 +1516,12 @@ void guac_common_surface_reset_clip(guac_common_surface* surface) {
|
||||
*
|
||||
* @param surface
|
||||
* 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) {
|
||||
|
||||
@ -1452,9 +1533,29 @@ static void __guac_common_surface_flush_to_png(guac_common_surface* surface) {
|
||||
+ surface->dirty_rect.y * surface->stride
|
||||
+ surface->dirty_rect.x * 4;
|
||||
|
||||
cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer,
|
||||
CAIRO_FORMAT_RGB24, surface->dirty_rect.width,
|
||||
surface->dirty_rect.height, surface->stride);
|
||||
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,
|
||||
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 */
|
||||
guac_client_stream_png(surface->client, socket, GUAC_COMP_OVER,
|
||||
@ -1526,8 +1627,12 @@ static void __guac_common_surface_flush_to_jpeg(guac_common_surface* surface) {
|
||||
*
|
||||
* @param surface
|
||||
* 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) {
|
||||
|
||||
@ -1547,9 +1652,19 @@ static void __guac_common_surface_flush_to_webp(guac_common_surface* surface) {
|
||||
+ surface->dirty_rect.y * surface->stride
|
||||
+ surface->dirty_rect.x * 4;
|
||||
|
||||
cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer,
|
||||
CAIRO_FORMAT_RGB24, surface->dirty_rect.width,
|
||||
surface->dirty_rect.height, surface->stride);
|
||||
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,
|
||||
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 */
|
||||
guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer,
|
||||
@ -1679,19 +1794,22 @@ static void __guac_common_surface_flush(guac_common_surface* surface) {
|
||||
|
||||
flushed++;
|
||||
|
||||
int opaque = __guac_common_surface_is_opaque(surface,
|
||||
&surface->dirty_rect);
|
||||
|
||||
/* Prefer WebP when reasonable */
|
||||
if (__guac_common_surface_should_use_webp(surface,
|
||||
&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 */
|
||||
else if (__guac_common_surface_should_use_jpeg(surface,
|
||||
&surface->dirty_rect))
|
||||
else if (opaque && __guac_common_surface_should_use_jpeg(
|
||||
surface, &surface->dirty_rect))
|
||||
__guac_common_surface_flush_to_jpeg(surface);
|
||||
|
||||
/* Use PNG if no lossy formats are appropriate */
|
||||
else
|
||||
__guac_common_surface_flush_to_png(surface);
|
||||
__guac_common_surface_flush_to_png(surface, opaque);
|
||||
|
||||
}
|
||||
|
||||
@ -1750,7 +1868,7 @@ void guac_common_surface_dup(guac_common_surface* surface, guac_user* user,
|
||||
|
||||
/* Get entire surface */
|
||||
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);
|
||||
|
||||
/* Send PNG for rect */
|
||||
|
@ -113,7 +113,8 @@ void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) {
|
||||
case 0:
|
||||
|
||||
/* 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;
|
||||
|
||||
/* DSTINVERT */
|
||||
@ -128,7 +129,8 @@ void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) {
|
||||
|
||||
/* Whiteness */
|
||||
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;
|
||||
|
||||
/* Unsupported ROP3 */
|
||||
@ -175,7 +177,8 @@ void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
|
||||
|
||||
/* If blackness, send black rectangle */
|
||||
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;
|
||||
|
||||
/* 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 */
|
||||
case 0xCC:
|
||||
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 >> 8 ) & 0xFF,
|
||||
(patblt->foreColor ) & 0xFF);
|
||||
(patblt->foreColor ) & 0xFF,
|
||||
0xFF);
|
||||
break;
|
||||
|
||||
/* If whiteness, send white rectangle */
|
||||
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;
|
||||
|
||||
/* Otherwise, invert entire rect */
|
||||
@ -250,7 +255,8 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
||||
|
||||
/* If blackness, send black rectangle */
|
||||
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;
|
||||
|
||||
/* If NOP, do nothing */
|
||||
@ -294,7 +300,8 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
||||
|
||||
/* If whiteness, send white rectangle */
|
||||
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;
|
||||
|
||||
/* 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 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 >> 8 ) & 0xFF,
|
||||
(color ) & 0xFF);
|
||||
(color ) & 0xFF,
|
||||
0xFF);
|
||||
|
||||
}
|
||||
|
||||
|
@ -136,10 +136,12 @@ void guac_rdp_glyph_begindraw(rdpContext* context,
|
||||
/* Convert background color */
|
||||
bgcolor = guac_rdp_convert_color(context, bgcolor);
|
||||
|
||||
guac_common_surface_rect(rdp_client->current_surface, x, y, width, height,
|
||||
(bgcolor & 0xFF0000) >> 16,
|
||||
(bgcolor & 0x00FF00) >> 8,
|
||||
bgcolor & 0x0000FF);
|
||||
guac_common_surface_set(rdp_client->current_surface,
|
||||
x, y, width, height,
|
||||
(bgcolor & 0xFF0000) >> 16,
|
||||
(bgcolor & 0x00FF00) >> 8,
|
||||
(bgcolor & 0x0000FF),
|
||||
0xFF);
|
||||
|
||||
}
|
||||
|
||||
|
@ -773,13 +773,14 @@ void __guac_terminal_display_flush_clear(guac_terminal_display* display) {
|
||||
}
|
||||
|
||||
/* Send rect */
|
||||
guac_common_surface_rect(
|
||||
guac_common_surface_set(
|
||||
display->display_surface,
|
||||
col * display->char_width,
|
||||
row * display->char_height,
|
||||
rect_width * display->char_width,
|
||||
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 */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user