From 1047c6192cbf1b57e3fb84d957053afdb2d034e4 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 4 May 2014 17:00:26 -0700 Subject: [PATCH] GUAC-667: Implement clipping within common surface. --- src/common/guac_surface.c | 124 ++++++++++++++++++++++++++++++++-- src/common/guac_surface.h | 40 +++++++++++ src/protocols/rdp/client.c | 39 ----------- src/protocols/rdp/client.h | 39 ----------- src/protocols/rdp/rdp_gdi.c | 46 ++----------- src/protocols/rdp/rdp_glyph.c | 27 +++----- 6 files changed, 174 insertions(+), 141 deletions(-) diff --git a/src/common/guac_surface.c b/src/common/guac_surface.c index ab118f52..7566a393 100644 --- a/src/common/guac_surface.c +++ b/src/common/guac_surface.c @@ -67,6 +67,49 @@ #define cairo_format_stride_for_width(format, width) (width*4) #endif +/** + * Updates the coordinates of the given rectangle to be within the bounds of the given surface. + * + * @param surface The surface to use for clipping. + * @param x The X coordinate of the rectangle to clip. + * @param y The Y coordinate of the rectangle to clip. + * @param w The width of the rectangle to clip. + * @param h The height of the rectangle to clip. + * @param sx The X coordinate of the source rectangle, if any. + * @param sy The Y coordinate of the source rectangle, if any. + */ +static void __guac_common_bound_rect(guac_common_surface* surface, int* x, int* y, int* w, int* h, + int* sx, int* sy) { + + int bounds_left = surface->bounds_x; + int bounds_top = surface->bounds_y; + int bounds_right = bounds_left + surface->bounds_width; + int bounds_bottom = bounds_top + surface->bounds_height; + + /* Get rect coordinates */ + int clipped_left = *x; + int clipped_top = *y; + int clipped_right = clipped_left + *w; + int clipped_bottom = clipped_top + *h; + + /* Clip to bounds */ + if (clipped_left < bounds_left) clipped_left = bounds_left; + if (clipped_top < bounds_top) clipped_top = bounds_top; + if (clipped_right > bounds_right) clipped_right = bounds_right; + if (clipped_bottom > bounds_bottom) clipped_bottom = bounds_bottom; + + /* Update source X/Y if given */ + if (sx != NULL) *sx += clipped_left - *x; + if (sy != NULL) *sy += clipped_top - *y; + + /* Store new rect dimensions */ + *x = clipped_left; + *y = clipped_top; + *w = clipped_right - clipped_left; + *h = clipped_bottom - clipped_top; + +} + /** * Returns whether the given rectangle should be combined into the existing * dirty rectangle, to be eventually flushed as a "png" instruction. @@ -257,13 +300,16 @@ static void __guac_common_surface_transfer_int(guac_transfer_function op, uint32 static void __guac_common_surface_rect(guac_common_surface* dst, int dx, int dy, int w, int h, int red, int green, int blue) { - int dst_stride = dst->stride; - unsigned char* dst_buffer = dst->buffer + dst_stride*dy + 4*dx; - int x, y; + int dst_stride; + unsigned char* dst_buffer; + uint32_t color = 0xFF000000 | (red << 16) | (green << 8) | blue; + dst_stride = dst->stride; + dst_buffer = dst->buffer + dst_stride*dy + 4*dx; + /* For each row */ for (y=0; ystride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w); surface->buffer = malloc(surface->stride * h); + /* Reset clipping rect */ + guac_common_surface_reset_clip(surface); + /* Init with black */ __guac_common_surface_rect(surface, 0, 0, w, h, 0x00, 0x00, 0x00); @@ -430,7 +479,12 @@ void guac_common_surface_resize(guac_common_surface* surface, int w, int h) { surface->stride = stride; surface->buffer = buffer; + /* Reset clipping rect */ + guac_common_surface_reset_clip(surface); + /* Init with old data */ + if (old_width > w) old_width = w; + if (old_height > h) old_height = h; __guac_common_surface_put(old_buffer, old_stride, 0, 0, old_width, old_height, surface, 0, 0, 1); @@ -477,6 +531,14 @@ void guac_common_surface_draw(guac_common_surface* surface, int x, int y, cairo_ int w = cairo_image_surface_get_width(src); int h = cairo_image_surface_get_height(src); + int sx = 0; + int sy = 0; + + /* Clip operation */ + __guac_common_bound_rect(surface, &x, &y, &w, &h, &sx, &sy); + if (w <= 0 || h <= 0) + return; + /* Flush if not combining */ if (!__guac_common_should_combine(surface, x, y, w, h, 0)) guac_common_surface_flush(surface); @@ -485,7 +547,7 @@ void guac_common_surface_draw(guac_common_surface* surface, int x, int y, cairo_ __guac_common_mark_dirty(surface, x, y, w, h); /* Update backing surface */ - __guac_common_surface_put(buffer, stride, 0, 0, w, h, surface, x, y, format != CAIRO_FORMAT_ARGB32); + __guac_common_surface_put(buffer, stride, sx, sy, w, h, surface, x, y, format != CAIRO_FORMAT_ARGB32); } @@ -496,6 +558,11 @@ void guac_common_surface_copy(guac_common_surface* src, int sx, int sy, int w, i const guac_layer* src_layer = src->layer; const guac_layer* dst_layer = dst->layer; + /* Clip operation */ + __guac_common_bound_rect(dst, &dx, &dy, &w, &h, &sx, &sy); + if (w <= 0 || h <= 0) + return; + /* Defer if combining */ if (__guac_common_should_combine(dst, dx, dy, w, h, 1)) __guac_common_mark_dirty(dst, dx, dy, w, h); @@ -520,6 +587,11 @@ void guac_common_surface_transfer(guac_common_surface* src, int sx, int sy, int const guac_layer* src_layer = src->layer; const guac_layer* dst_layer = dst->layer; + /* Clip operation */ + __guac_common_bound_rect(dst, &dx, &dy, &w, &h, &sx, &sy); + if (w <= 0 || h <= 0) + return; + /* Defer if combining */ if (__guac_common_should_combine(dst, dx, dy, w, h, 1)) __guac_common_mark_dirty(dst, dx, dy, w, h); @@ -544,6 +616,11 @@ void guac_common_surface_rect(guac_common_surface* surface, guac_socket* socket = surface->socket; const guac_layer* layer = surface->layer; + /* Clip operation */ + __guac_common_bound_rect(surface, &x, &y, &w, &h, NULL, NULL); + if (w <= 0 || h <= 0) + return; + /* Defer if combining */ if (__guac_common_should_combine(surface, x, y, w, h, 1)) __guac_common_mark_dirty(surface, x, y, w, h); @@ -561,6 +638,43 @@ void guac_common_surface_rect(guac_common_surface* surface, } +void guac_common_surface_clip(guac_common_surface* surface, int x, int y, int w, int h) { + + /* Calculate extents of existing bounds rect */ + int bounds_left = surface->bounds_x; + int bounds_top = surface->bounds_y; + int bounds_right = bounds_left + surface->bounds_width; + int bounds_bottom = bounds_top + surface->bounds_height; + + /* Calculate missing extents of given new rect */ + int right = x + w; + int bottom = y + h; + + /* Update maximums */ + if (x > bounds_left) bounds_left = x; + if (y > bounds_top) bounds_top = y; + if (right < bounds_right) bounds_right = right; + if (bottom < bounds_bottom) bounds_bottom = bottom; + + /* Commit rect */ + surface->bounds_x = bounds_left; + surface->bounds_y = bounds_top; + surface->bounds_width = bounds_right - bounds_left; + surface->bounds_height = bounds_bottom - bounds_top; + + /* Clamp dimensions at 0x0 */ + if (surface->bounds_width < 0) surface->bounds_width = 0; + if (surface->bounds_height < 0) surface->bounds_height = 0; + +} + +void guac_common_surface_reset_clip(guac_common_surface* surface) { + surface->bounds_x = 0; + surface->bounds_y = 0; + surface->bounds_width = surface->width; + surface->bounds_height = surface->height; +} + void guac_common_surface_flush(guac_common_surface* surface) { if (surface->dirty) { diff --git a/src/common/guac_surface.h b/src/common/guac_surface.h index 45da5442..2f70ba5f 100644 --- a/src/common/guac_surface.h +++ b/src/common/guac_surface.h @@ -100,6 +100,26 @@ typedef struct guac_common_surface { */ int realized; + /** + * The X coordinate of the upper-left corner of the bounding rectangle. + */ + int bounds_x; + + /** + * The Y coordinate of the upper-left corner of the bounding rectangle. + */ + int bounds_y; + + /** + * The width of the bounding rectangle. + */ + int bounds_width; + + /** + * The height of the bounding rectangle. + */ + int bounds_height; + } guac_common_surface; /** @@ -187,6 +207,26 @@ void guac_common_surface_rect(guac_common_surface* surface, int x, int y, int w, int h, int red, int green, int blue); +/** + * Given the coordinates and dimensions of a rectangle, clips all future + * operations within that rectangle. + * + * @param surface The surface whose clipping rectangle should be changed. + * @param x The X coordinate of the upper-left corner of the bounding rectangle. + * @param y The Y coordinate of the upper-left corner of the bounding rectangle. + * @param w The width of the bounding rectangle. + * @param h The height of the bounding rectangle. + */ +void guac_common_surface_clip(guac_common_surface* surface, int x, int y, int w, int h); + +/** + * Resets the clipping rectangle, allowing drawing operations throughout the + * entire surface. + * + * @param surface The surface whose clipping rectangle should be reset. + */ +void guac_common_surface_reset_clip(guac_common_surface* surface); + /** * Flushes the given surface, drawing any pending operations on the remote * display. diff --git a/src/protocols/rdp/client.c b/src/protocols/rdp/client.c index 8b3c0cb3..cfadc2b2 100644 --- a/src/protocols/rdp/client.c +++ b/src/protocols/rdp/client.c @@ -648,7 +648,6 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Store client data */ guac_client_data->rdp_inst = rdp_inst; - guac_client_data->bounded = FALSE; guac_client_data->mouse_button_mask = 0; guac_client_data->clipboard = guac_common_clipboard_alloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH); guac_client_data->requested_clipboard_format = CB_FORMAT_TEXT; @@ -726,41 +725,3 @@ int guac_client_init(guac_client* client, int argc, char** argv) { } -int guac_rdp_clip_rect(rdp_guac_client_data* data, int* x, int* y, int* w, int* h) { - - if (data->bounded) { - - /* Get rect coordinates */ - int clipped_left = *x; - int clipped_top = *y; - int clipped_right = clipped_left + *w - 1; - int clipped_bottom = clipped_top + *h - 1; - - /* Clip left */ - if (clipped_left < data->bounds_left) clipped_left = data->bounds_left; - else if (clipped_left > data->bounds_right) return 1; - - /* Clip right */ - if (clipped_right < data->bounds_left) return 1; - else if (clipped_right > data->bounds_right) clipped_right = data->bounds_right; - - /* Clip top */ - if (clipped_top < data->bounds_top) clipped_top = data->bounds_top; - else if (clipped_top > data->bounds_bottom) return 1; - - /* Clip bottom */ - if (clipped_bottom < data->bounds_top) return 1; - else if (clipped_bottom > data->bounds_bottom) clipped_bottom = data->bounds_bottom; - - /* Store new rect dimensions */ - *x = clipped_left; - *y = clipped_top; - *w = clipped_right - clipped_left + 1; - *h = clipped_bottom - clipped_top + 1; - - } - - return 0; - -} - diff --git a/src/protocols/rdp/client.h b/src/protocols/rdp/client.h index 266ca9f0..6b1ebffe 100644 --- a/src/protocols/rdp/client.h +++ b/src/protocols/rdp/client.h @@ -131,36 +131,6 @@ typedef struct rdp_guac_client_data { */ guac_common_surface* current_surface; - /** - * Whether graphical operations are restricted to a specific bounding - * rectangle. - */ - int bounded; - - /** - * The X coordinate of the upper-left corner of the bounding rectangle, - * if any. - */ - int bounds_left; - - /** - * The Y coordinate of the upper-left corner of the bounding rectangle, - * if any. - */ - int bounds_top; - - /** - * The X coordinate of the lower-right corner of the bounding rectangle, - * if any. - */ - int bounds_right; - - /** - * The Y coordinate of the lower-right corner of the bounding rectangle, - * if any. - */ - int bounds_bottom; - /** * The keymap to use when translating keysyms into scancodes or sequences * of scancodes for RDP. @@ -241,14 +211,5 @@ typedef struct rdp_freerdp_context { } rdp_freerdp_context; -/** - * Given the coordinates and dimensions of a rectangle, clips the rectangle to be - * within the clipping bounds of the client data, if clipping is active. - * - * Returns 0 if the rectangle given is visible at all, and 1 if the entire - * rectangls is outside the clipping rectangle and this invisible. - */ -int guac_rdp_clip_rect(rdp_guac_client_data* data, int* x, int* y, int* w, int* h); - #endif diff --git a/src/protocols/rdp/rdp_gdi.c b/src/protocols/rdp/rdp_gdi.c index ba6caaeb..11b0d6e5 100644 --- a/src/protocols/rdp/rdp_gdi.c +++ b/src/protocols/rdp/rdp_gdi.c @@ -104,11 +104,6 @@ void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) { int w = dstblt->nWidth; int h = dstblt->nHeight; - /* Clip operation to bounds */ - rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; - if (guac_rdp_clip_rect(data, &x, &y, &w, &h)) - return; - switch (dstblt->bRop) { /* Blackness */ @@ -165,8 +160,6 @@ void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) { int w = patblt->nWidth; int h = patblt->nHeight; - rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; - /* * Warn that rendering is a fallback, as the server should not be sending * this order. @@ -174,10 +167,6 @@ void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) { guac_client_log_info(client, "Using fallback PATBLT (server is ignoring " "negotiated client capabilities)"); - /* Clip operation to bounds */ - if (guac_rdp_clip_rect(data, &x, &y, &w, &h)) - return; - /* Render rectangle based on ROP */ switch (patblt->bRop) { @@ -226,14 +215,7 @@ void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt) { int x_src = scrblt->nXSrc; int y_src = scrblt->nYSrc; - /* Clip operation to bounds */ rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; - if (guac_rdp_clip_rect(data, &x, &y, &w, &h)) - return; - - /* Update source coordinates */ - x_src += x - scrblt->nLeftRect; - y_src += y - scrblt->nTopRect; /* Copy screen rect to current surface */ guac_common_surface_copy(data->default_surface, x_src, y_src, w, h, @@ -255,22 +237,12 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) { int x_src = memblt->nXSrc; int y_src = memblt->nYSrc; - rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; - /* Make sure that the recieved bitmap is not NULL before processing */ if (bitmap == NULL) { guac_client_log_info(client, "NULL bitmap found in memblt instruction."); return; } - /* Clip operation to bounds */ - if (guac_rdp_clip_rect(data, &x, &y, &w, &h)) - return; - - /* Update source coordinates */ - x_src += x - memblt->nLeftRect; - y_src += y - memblt->nTopRect; - switch (memblt->bRop) { /* If blackness, send black rectangle */ @@ -352,17 +324,11 @@ void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect guac_common_surface* current_surface = ((rdp_guac_client_data*) client->data)->current_surface; - rdp_guac_client_data* data = (rdp_guac_client_data*) client->data; - int x = opaque_rect->nLeftRect; int y = opaque_rect->nTopRect; int w = opaque_rect->nWidth; int h = opaque_rect->nHeight; - /* Clip operation to bounds */ - if (guac_rdp_clip_rect(data, &x, &y, &w, &h)) - return; - guac_common_surface_rect(current_surface, x, y, w, h, (color >> 16) & 0xFF, (color >> 8 ) & 0xFF, @@ -389,16 +355,12 @@ void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) { /* If no bounds given, clear bounding rect */ if (bounds == NULL) - data->bounded = FALSE; + guac_common_surface_reset_clip(data->default_surface); /* Otherwise, set bounding rectangle */ - else { - data->bounded = TRUE; - data->bounds_left = bounds->left; - data->bounds_top = bounds->top; - data->bounds_right = bounds->right; - data->bounds_bottom = bounds->bottom; - } + else + guac_common_surface_clip(data->default_surface, bounds->left, bounds->top, + bounds->right - bounds->left + 1, bounds->bottom - bounds->top + 1); } diff --git a/src/protocols/rdp/rdp_glyph.c b/src/protocols/rdp/rdp_glyph.c index 44865aac..f104cf70 100644 --- a/src/protocols/rdp/rdp_glyph.c +++ b/src/protocols/rdp/rdp_glyph.c @@ -214,25 +214,20 @@ void guac_rdp_glyph_enddraw(rdpContext* context, if (width > max_width) width = max_width; if (height > max_height) height = max_height; - /* Clip operation to clipping region, if any */ - if (!guac_rdp_clip_rect(guac_client_data, &x, &y, &width, &height)) { + /* Ensure data is ready */ + cairo_surface_flush(glyph_surface); - /* Ensure data is ready */ - cairo_surface_flush(glyph_surface); + /* Create surface for subsection with text */ + cairo_surface_t* surface = cairo_image_surface_create_for_data( + cairo_image_surface_get_data(glyph_surface) + 4*x + y*stride, + cairo_image_surface_get_format(glyph_surface), + width, height, stride); - /* Create surface for subsection with text */ - cairo_surface_t* surface = cairo_image_surface_create_for_data( - cairo_image_surface_get_data(glyph_surface) + 4*x + y*stride, - cairo_image_surface_get_format(glyph_surface), - width, height, stride); + /* Send surface with all glyphs to current surface */ + guac_common_surface_draw(current_surface, x, y, surface); - /* Send surface with all glyphs to current surface */ - guac_common_surface_draw(current_surface, x, y, surface); - - /* Destroy surface */ - cairo_surface_destroy(surface); - - } + /* Destroy surface */ + cairo_surface_destroy(surface); /* Destroy cairo instance */ cairo_destroy(guac_client_data->glyph_cairo);