GUAC-667: Implement clipping within common surface.
This commit is contained in:
parent
b2d74c92a2
commit
1047c6192c
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user