GUAC-667: Implement clipping within common surface.

This commit is contained in:
Michael Jumper 2014-05-04 17:00:26 -07:00
parent b2d74c92a2
commit 1047c6192c
6 changed files with 174 additions and 141 deletions

View File

@ -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; y<h; y++) {
@ -305,7 +351,7 @@ static void __guac_common_surface_put(unsigned char* src_buffer, int src_stride,
for (x=0; x<w; x++) {
if (opaque || (*src_current & 0xFF000000))
*dst_current = *src_current;
*dst_current = *src_current | 0xFF000000;
src_current++;
dst_current++;
@ -382,6 +428,9 @@ guac_common_surface* guac_common_surface_alloc(guac_socket* socket, const guac_l
surface->stride = 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) {

View File

@ -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.

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);