From 960ee263e8757ca009a9bf407d89b0b16233ddd6 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 28 Feb 2016 15:45:04 -0800 Subject: [PATCH] GUAC-236: Split massive display code into more reasonable files. --- src/guacenc/Makefile.am | 51 ++-- src/guacenc/buffer.c | 1 - src/guacenc/display-buffers.c | 110 +++++++++ src/guacenc/display-flatten.c | 160 ++++++++++++ src/guacenc/display-image-streams.c | 84 +++++++ src/guacenc/display-layers.c | 102 ++++++++ src/guacenc/display-sync.c | 66 +++++ src/guacenc/display.c | 361 ---------------------------- src/guacenc/display.h | 16 ++ 9 files changed, 566 insertions(+), 385 deletions(-) create mode 100644 src/guacenc/display-buffers.c create mode 100644 src/guacenc/display-flatten.c create mode 100644 src/guacenc/display-image-streams.c create mode 100644 src/guacenc/display-layers.c create mode 100644 src/guacenc/display-sync.c diff --git a/src/guacenc/Makefile.am b/src/guacenc/Makefile.am index 2baa6003..54f1df24 100644 --- a/src/guacenc/Makefile.am +++ b/src/guacenc/Makefile.am @@ -35,29 +35,34 @@ noinst_HEADERS = \ log.h \ png.h -guacenc_SOURCES = \ - buffer.c \ - display.c \ - encode.c \ - guacenc.c \ - image-stream.c \ - instructions.c \ - instruction-blob.c \ - instruction-cfill.c \ - instruction-copy.c \ - instruction-cursor.c \ - instruction-dispose.c \ - instruction-end.c \ - instruction-img.c \ - instruction-move.c \ - instruction-rect.c \ - instruction-shade.c \ - instruction-size.c \ - instruction-sync.c \ - instruction-transfer.c \ - jpeg.c \ - layer.c \ - log.c \ +guacenc_SOURCES = \ + buffer.c \ + display.c \ + display-buffers.c \ + display-image-streams.c \ + display-flatten.c \ + display-layers.c \ + display-sync.c \ + encode.c \ + guacenc.c \ + image-stream.c \ + instructions.c \ + instruction-blob.c \ + instruction-cfill.c \ + instruction-copy.c \ + instruction-cursor.c \ + instruction-dispose.c \ + instruction-end.c \ + instruction-img.c \ + instruction-move.c \ + instruction-rect.c \ + instruction-shade.c \ + instruction-size.c \ + instruction-sync.c \ + instruction-transfer.c \ + jpeg.c \ + layer.c \ + log.c \ png.c # Compile WebP support if available diff --git a/src/guacenc/buffer.c b/src/guacenc/buffer.c index a4776289..f7387997 100644 --- a/src/guacenc/buffer.c +++ b/src/guacenc/buffer.c @@ -26,7 +26,6 @@ #include #include -#include #include guacenc_buffer* guacenc_buffer_alloc() { diff --git a/src/guacenc/display-buffers.c b/src/guacenc/display-buffers.c new file mode 100644 index 00000000..9fd8663d --- /dev/null +++ b/src/guacenc/display-buffers.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016 Glyptodon, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" +#include "display.h" +#include "buffer.h" +#include "layer.h" +#include "log.h" + +#include + +#include + +guacenc_buffer* guacenc_display_get_buffer(guacenc_display* display, + int index) { + + /* Transform index to buffer space */ + int internal_index = -index - 1; + + /* Do not lookup / allocate if index is invalid */ + if (internal_index < 0 || internal_index > GUACENC_DISPLAY_MAX_BUFFERS) { + guacenc_log(GUAC_LOG_WARNING, "Buffer index out of bounds: %i", index); + return NULL; + } + + /* Lookup buffer, allocating a new buffer if necessary */ + guacenc_buffer* buffer = display->buffers[internal_index]; + if (buffer == NULL) { + + /* Attempt to allocate buffer */ + buffer = guacenc_buffer_alloc(); + if (buffer == NULL) { + guacenc_log(GUAC_LOG_WARNING, "Buffer allocation failed"); + return NULL; + } + + /* All non-layer buffers must autosize */ + buffer->autosize = true; + + /* Store buffer within display for future retrieval / management */ + display->buffers[internal_index] = buffer; + + } + + return buffer; + +} + +int guacenc_display_free_buffer(guacenc_display* display, + int index) { + + /* Transform index to buffer space */ + int internal_index = -index - 1; + + /* Do not lookup / allocate if index is invalid */ + if (internal_index < 0 || internal_index > GUACENC_DISPLAY_MAX_BUFFERS) { + guacenc_log(GUAC_LOG_WARNING, "Buffer index out of bounds: %i", index); + return 1; + } + + /* Free buffer (if allocated) */ + guacenc_buffer_free(display->buffers[internal_index]); + + /* Mark buffer as freed */ + display->buffers[internal_index] = NULL; + + return 0; + +} + +guacenc_buffer* guacenc_display_get_related_buffer(guacenc_display* display, + int index) { + + /* Retrieve underlying buffer of layer if a layer is requested */ + if (index >= 0) { + + /* Retrieve / allocate layer (if possible */ + guacenc_layer* layer = guacenc_display_get_layer(display, index); + if (layer == NULL) + return NULL; + + /* Return underlying buffer */ + return layer->buffer; + + } + + /* Otherwise retrieve buffer directly */ + return guacenc_display_get_buffer(display, index); + +} + diff --git a/src/guacenc/display-flatten.c b/src/guacenc/display-flatten.c new file mode 100644 index 00000000..e49bc704 --- /dev/null +++ b/src/guacenc/display-flatten.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2016 Glyptodon, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" +#include "display.h" +#include "layer.h" + +#include + +#include +#include + +/** + * The Guacamole video encoder display related to the current qsort() + * operation. As qsort() does not provide a means of passing arbitrary data to + * the comparitor, this value must be set prior to invoking qsort() with + * guacenc_display_layer_comparator. + */ +guacenc_display* __qsort_display; + +/** + * Comparator which orders layer pointers such that (1) NULL pointers are last, + * (2) layers with the same parent_index are adjacent, and (3) layers with the + * same parent_index are ordered by Z. + * + * @see qsort() + */ +static int guacenc_display_layer_comparator(const void* a, const void* b) { + + guacenc_layer* layer_a = *((guacenc_layer**) a); + guacenc_layer* layer_b = *((guacenc_layer**) b); + + /* If a is NULL, sort it to bottom */ + if (layer_a == NULL) { + + /* ... unless b is also NULL, in which case they are equal */ + if (layer_b == NULL) + return 0; + + return 1; + } + + /* If b is NULL (and a is not NULL), sort it to bottom */ + if (layer_b == NULL) + return -1; + + /* Order such that the deepest layers are first */ + int a_depth = guacenc_display_get_depth(__qsort_display, layer_a); + int b_depth = guacenc_display_get_depth(__qsort_display, layer_b); + if (b_depth != a_depth) + return b_depth - a_depth; + + /* Order such that sibling layers are adjacent */ + if (layer_b->parent_index != layer_a->parent_index) + return layer_b->parent_index - layer_a->parent_index; + + /* Order sibling layers according to descending Z */ + return layer_b->z - layer_a->z; + +} + +int guacenc_display_flatten(guacenc_display* display) { + + int i; + guacenc_layer* render_order[GUACENC_DISPLAY_MAX_LAYERS]; + + /* Copy list of layers within display */ + memcpy(render_order, display->layers, sizeof(render_order)); + + /* Sort layers by depth, parent, and Z */ + __qsort_display = display; + qsort(render_order, GUACENC_DISPLAY_MAX_LAYERS, sizeof(guacenc_layer*), + guacenc_display_layer_comparator); + + /* Reset layer frame buffers */ + for (i = 0; i < GUACENC_DISPLAY_MAX_LAYERS; i++) { + + /* Pull current layer, ignoring unallocated layers */ + guacenc_layer* layer = render_order[i]; + if (layer == NULL) + continue; + + /* Get source buffer and destination frame buffer */ + guacenc_buffer* buffer = layer->buffer; + guacenc_buffer* frame = layer->frame; + + /* Reset frame contents */ + guacenc_buffer_copy(frame, buffer); + + } + + /* Render each layer, in order */ + for (i = 0; i < GUACENC_DISPLAY_MAX_LAYERS; i++) { + + /* Pull current layer, ignoring unallocated layers */ + guacenc_layer* layer = render_order[i]; + if (layer == NULL) + continue; + + /* Skip fully-transparent layers */ + if (layer->opacity == 0) + continue; + + /* Ignore layers without a parent */ + int parent_index = layer->parent_index; + if (parent_index == GUACENC_LAYER_NO_PARENT) + continue; + + /* Retrieve parent layer, ignoring layers with invalid parents */ + guacenc_layer* parent = guacenc_display_get_layer(display, parent_index); + if (parent == NULL) + continue; + + /* Get source and destination frame buffer */ + guacenc_buffer* src = layer->frame; + guacenc_buffer* dst = parent->frame; + + /* Ignore layers with empty buffers */ + cairo_surface_t* surface = src->surface; + if (surface == NULL) + continue; + + /* Ignore if parent has no pixels */ + cairo_t* cairo = dst->cairo; + if (cairo == NULL) + continue; + + /* Render buffer to layer */ + cairo_reset_clip(cairo); + cairo_rectangle(cairo, layer->x, layer->y, src->width, src->height); + cairo_clip(cairo); + + cairo_set_source_surface(cairo, surface, layer->x, layer->y); + cairo_paint_with_alpha(cairo, layer->opacity / 255.0); + + } + + return 0; + +} + diff --git a/src/guacenc/display-image-streams.c b/src/guacenc/display-image-streams.c new file mode 100644 index 00000000..46e4b867 --- /dev/null +++ b/src/guacenc/display-image-streams.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 Glyptodon, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" +#include "display.h" +#include "image-stream.h" +#include "log.h" + +#include + +#include + +int guacenc_display_create_image_stream(guacenc_display* display, int index, + int mask, int layer_index, const char* mimetype, int x, int y) { + + /* Do not lookup / allocate if index is invalid */ + if (index < 0 || index > GUACENC_DISPLAY_MAX_STREAMS) { + guacenc_log(GUAC_LOG_WARNING, "Stream index out of bounds: %i", index); + return 1; + } + + /* Free existing stream (if any) */ + guacenc_image_stream_free(display->image_streams[index]); + + /* Associate new stream */ + guacenc_image_stream* stream = display->image_streams[index] = + guacenc_image_stream_alloc(mask, layer_index, mimetype, x, y); + + /* Return zero only if stream is not NULL */ + return stream == NULL; + +} + +guacenc_image_stream* guacenc_display_get_image_stream( + guacenc_display* display, int index) { + + /* Do not lookup / allocate if index is invalid */ + if (index < 0 || index > GUACENC_DISPLAY_MAX_STREAMS) { + guacenc_log(GUAC_LOG_WARNING, "Stream index out of bounds: %i", index); + return NULL; + } + + /* Return existing stream (if any) */ + return display->image_streams[index]; + +} + +int guacenc_display_free_image_stream(guacenc_display* display, int index) { + + /* Do not lookup / allocate if index is invalid */ + if (index < 0 || index > GUACENC_DISPLAY_MAX_STREAMS) { + guacenc_log(GUAC_LOG_WARNING, "Stream index out of bounds: %i", index); + return 1; + } + + /* Free stream (if allocated) */ + guacenc_image_stream_free(display->image_streams[index]); + + /* Mark stream as freed */ + display->image_streams[index] = NULL; + + return 0; + +} + diff --git a/src/guacenc/display-layers.c b/src/guacenc/display-layers.c new file mode 100644 index 00000000..23b43e75 --- /dev/null +++ b/src/guacenc/display-layers.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 Glyptodon, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" +#include "display.h" +#include "layer.h" +#include "log.h" + +#include + +#include + +guacenc_layer* guacenc_display_get_layer(guacenc_display* display, + int index) { + + /* Do not lookup / allocate if index is invalid */ + if (index < 0 || index > GUACENC_DISPLAY_MAX_LAYERS) { + guacenc_log(GUAC_LOG_WARNING, "Layer index out of bounds: %i", index); + return NULL; + } + + /* Lookup layer, allocating a new layer if necessary */ + guacenc_layer* layer = display->layers[index]; + if (layer == NULL) { + + /* Attempt to allocate layer */ + layer = guacenc_layer_alloc(); + if (layer == NULL) { + guacenc_log(GUAC_LOG_WARNING, "Layer allocation failed"); + return NULL; + } + + /* The default layer has no parent */ + if (index == 0) + layer->parent_index = GUACENC_LAYER_NO_PARENT; + + /* Store layer within display for future retrieval / management */ + display->layers[index] = layer; + + } + + return layer; + +} + +int guacenc_display_get_depth(guacenc_display* display, guacenc_layer* layer) { + + /* Non-existent layers have a depth of 0 */ + if (layer == NULL) + return 0; + + /* Layers with no parent have a depth of 0 */ + if (layer->parent_index == GUACENC_LAYER_NO_PARENT) + return 0; + + /* Retrieve parent layer */ + guacenc_layer* parent = + guacenc_display_get_layer(display, layer->parent_index); + + /* Current layer depth is the depth of the parent + 1 */ + return guacenc_display_get_depth(display, parent) + 1; + +} + +int guacenc_display_free_layer(guacenc_display* display, + int index) { + + /* Do not lookup / allocate if index is invalid */ + if (index < 0 || index > GUACENC_DISPLAY_MAX_LAYERS) { + guacenc_log(GUAC_LOG_WARNING, "Layer index out of bounds: %i", index); + return 1; + } + + /* Free layer (if allocated) */ + guacenc_layer_free(display->layers[index]); + + /* Mark layer as freed */ + display->layers[index] = NULL; + + return 0; + +} + diff --git a/src/guacenc/display-sync.c b/src/guacenc/display-sync.c new file mode 100644 index 00000000..33a358b5 --- /dev/null +++ b/src/guacenc/display-sync.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 Glyptodon, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config.h" +#include "display.h" +#include "layer.h" +#include "log.h" + +#include +#include +#include + +#include +#include +#include +#include + +int guacenc_display_sync(guacenc_display* display, guac_timestamp timestamp) { + + /* Verify timestamp is not decreasing */ + if (timestamp < display->last_sync) { + guacenc_log(GUAC_LOG_WARNING, "Decreasing sync timestamp"); + return 1; + } + + /* Update timestamp of display */ + display->last_sync = timestamp; + + /* Flatten display to default layer */ + if (guacenc_display_flatten(display)) + return 1; + + /* Retrieve default layer (guaranteed to not be NULL) */ + guacenc_layer* def_layer = guacenc_display_get_layer(display, 0); + assert(def_layer != NULL); + + /* STUB: Write frame as PNG */ + char filename[256]; + sprintf(filename, "frame-%" PRId64 ".png", timestamp); + cairo_surface_t* surface = def_layer->frame->surface; + if (surface != NULL) + cairo_surface_write_to_png(surface, filename); + + return 0; + +} + diff --git a/src/guacenc/display.c b/src/guacenc/display.c index 86398253..fdfb93a0 100644 --- a/src/guacenc/display.c +++ b/src/guacenc/display.c @@ -22,371 +22,10 @@ #include "config.h" #include "display.h" -#include "layer.h" -#include "log.h" #include -#include -#include -#include -#include -#include -#include -#include #include -#include - -/** - * The Guacamole video encoder display related to the current qsort() - * operation. As qsort() does not provide a means of passing arbitrary data to - * the comparitor, this value must be set prior to invoking qsort() with - * guacenc_display_layer_comparator. - */ -guacenc_display* __qsort_display; - -/** - * Comparator which orders layer pointers such that (1) NULL pointers are last, - * (2) layers with the same parent_index are adjacent, and (3) layers with the - * same parent_index are ordered by Z. - * - * @see qsort() - */ -static int guacenc_display_layer_comparator(const void* a, const void* b) { - - guacenc_layer* layer_a = *((guacenc_layer**) a); - guacenc_layer* layer_b = *((guacenc_layer**) b); - - /* If a is NULL, sort it to bottom */ - if (layer_a == NULL) { - - /* ... unless b is also NULL, in which case they are equal */ - if (layer_b == NULL) - return 0; - - return 1; - } - - /* If b is NULL (and a is not NULL), sort it to bottom */ - if (layer_b == NULL) - return -1; - - /* Order such that the deepest layers are first */ - int a_depth = guacenc_display_get_depth(__qsort_display, layer_a); - int b_depth = guacenc_display_get_depth(__qsort_display, layer_b); - if (b_depth != a_depth) - return b_depth - a_depth; - - /* Order such that sibling layers are adjacent */ - if (layer_b->parent_index != layer_a->parent_index) - return layer_b->parent_index - layer_a->parent_index; - - /* Order sibling layers according to descending Z */ - return layer_b->z - layer_a->z; - -} - -int guacenc_display_sync(guacenc_display* display, guac_timestamp timestamp) { - - /* Verify timestamp is not decreasing */ - if (timestamp < display->last_sync) { - guacenc_log(GUAC_LOG_WARNING, "Decreasing sync timestamp"); - return 1; - } - - /* Update timestamp of display */ - display->last_sync = timestamp; - - int i; - guacenc_layer* render_order[GUACENC_DISPLAY_MAX_LAYERS]; - - /* Copy list of layers within display */ - memcpy(render_order, display->layers, sizeof(render_order)); - - /* Sort layers by depth, parent, and Z */ - __qsort_display = display; - qsort(render_order, GUACENC_DISPLAY_MAX_LAYERS, sizeof(guacenc_layer*), - guacenc_display_layer_comparator); - - /* Reset layer frame buffers */ - for (i = 0; i < GUACENC_DISPLAY_MAX_LAYERS; i++) { - - /* Pull current layer, ignoring unallocated layers */ - guacenc_layer* layer = render_order[i]; - if (layer == NULL) - continue; - - /* Get source buffer and destination frame buffer */ - guacenc_buffer* buffer = layer->buffer; - guacenc_buffer* frame = layer->frame; - - /* Reset frame contents */ - guacenc_buffer_copy(frame, buffer); - - } - - /* Render each layer, in order */ - for (i = 0; i < GUACENC_DISPLAY_MAX_LAYERS; i++) { - - /* Pull current layer, ignoring unallocated layers */ - guacenc_layer* layer = render_order[i]; - if (layer == NULL) - continue; - - /* Skip fully-transparent layers */ - if (layer->opacity == 0) - continue; - - /* Ignore layers without a parent */ - int parent_index = layer->parent_index; - if (parent_index == GUACENC_LAYER_NO_PARENT) - continue; - - /* Retrieve parent layer, ignoring layers with invalid parents */ - guacenc_layer* parent = guacenc_display_get_layer(display, parent_index); - if (parent == NULL) - continue; - - /* Get source and destination frame buffer */ - guacenc_buffer* src = layer->frame; - guacenc_buffer* dst = parent->frame; - - /* Ignore layers with empty buffers */ - cairo_surface_t* surface = src->surface; - if (surface == NULL) - continue; - - /* Ignore if parent has no pixels */ - cairo_t* cairo = dst->cairo; - if (cairo == NULL) - continue; - - /* Render buffer to layer */ - cairo_reset_clip(cairo); - cairo_rectangle(cairo, layer->x, layer->y, src->width, src->height); - cairo_clip(cairo); - - cairo_set_source_surface(cairo, surface, layer->x, layer->y); - cairo_paint_with_alpha(cairo, layer->opacity / 255.0); - - } - - /* Retrieve default layer (guaranteed to not be NULL) */ - guacenc_layer* def_layer = guacenc_display_get_layer(display, 0); - assert(def_layer != NULL); - - /* STUB: Write frame as PNG */ - char filename[256]; - sprintf(filename, "frame-%" PRId64 ".png", timestamp); - cairo_surface_t* surface = def_layer->frame->surface; - if (surface != NULL) - cairo_surface_write_to_png(surface, filename); - - return 0; - -} - -guacenc_layer* guacenc_display_get_layer(guacenc_display* display, - int index) { - - /* Do not lookup / allocate if index is invalid */ - if (index < 0 || index > GUACENC_DISPLAY_MAX_LAYERS) { - guacenc_log(GUAC_LOG_WARNING, "Layer index out of bounds: %i", index); - return NULL; - } - - /* Lookup layer, allocating a new layer if necessary */ - guacenc_layer* layer = display->layers[index]; - if (layer == NULL) { - - /* Attempt to allocate layer */ - layer = guacenc_layer_alloc(); - if (layer == NULL) { - guacenc_log(GUAC_LOG_WARNING, "Layer allocation failed"); - return NULL; - } - - /* The default layer has no parent */ - if (index == 0) - layer->parent_index = GUACENC_LAYER_NO_PARENT; - - /* Store layer within display for future retrieval / management */ - display->layers[index] = layer; - - } - - return layer; - -} - -int guacenc_display_get_depth(guacenc_display* display, guacenc_layer* layer) { - - /* Non-existent layers have a depth of 0 */ - if (layer == NULL) - return 0; - - /* Layers with no parent have a depth of 0 */ - if (layer->parent_index == GUACENC_LAYER_NO_PARENT) - return 0; - - /* Retrieve parent layer */ - guacenc_layer* parent = - guacenc_display_get_layer(display, layer->parent_index); - - /* Current layer depth is the depth of the parent + 1 */ - return guacenc_display_get_depth(display, parent) + 1; - -} - -int guacenc_display_free_layer(guacenc_display* display, - int index) { - - /* Do not lookup / allocate if index is invalid */ - if (index < 0 || index > GUACENC_DISPLAY_MAX_LAYERS) { - guacenc_log(GUAC_LOG_WARNING, "Layer index out of bounds: %i", index); - return 1; - } - - /* Free layer (if allocated) */ - guacenc_layer_free(display->layers[index]); - - /* Mark layer as freed */ - display->layers[index] = NULL; - - return 0; - -} - -guacenc_buffer* guacenc_display_get_buffer(guacenc_display* display, - int index) { - - /* Transform index to buffer space */ - int internal_index = -index - 1; - - /* Do not lookup / allocate if index is invalid */ - if (internal_index < 0 || internal_index > GUACENC_DISPLAY_MAX_BUFFERS) { - guacenc_log(GUAC_LOG_WARNING, "Buffer index out of bounds: %i", index); - return NULL; - } - - /* Lookup buffer, allocating a new buffer if necessary */ - guacenc_buffer* buffer = display->buffers[internal_index]; - if (buffer == NULL) { - - /* Attempt to allocate buffer */ - buffer = guacenc_buffer_alloc(); - if (buffer == NULL) { - guacenc_log(GUAC_LOG_WARNING, "Buffer allocation failed"); - return NULL; - } - - /* All non-layer buffers must autosize */ - buffer->autosize = true; - - /* Store buffer within display for future retrieval / management */ - display->buffers[internal_index] = buffer; - - } - - return buffer; - -} - -int guacenc_display_free_buffer(guacenc_display* display, - int index) { - - /* Transform index to buffer space */ - int internal_index = -index - 1; - - /* Do not lookup / allocate if index is invalid */ - if (internal_index < 0 || internal_index > GUACENC_DISPLAY_MAX_BUFFERS) { - guacenc_log(GUAC_LOG_WARNING, "Buffer index out of bounds: %i", index); - return 1; - } - - /* Free buffer (if allocated) */ - guacenc_buffer_free(display->buffers[internal_index]); - - /* Mark buffer as freed */ - display->buffers[internal_index] = NULL; - - return 0; - -} - -guacenc_buffer* guacenc_display_get_related_buffer(guacenc_display* display, - int index) { - - /* Retrieve underlying buffer of layer if a layer is requested */ - if (index >= 0) { - - /* Retrieve / allocate layer (if possible */ - guacenc_layer* layer = guacenc_display_get_layer(display, index); - if (layer == NULL) - return NULL; - - /* Return underlying buffer */ - return layer->buffer; - - } - - /* Otherwise retrieve buffer directly */ - return guacenc_display_get_buffer(display, index); - -} - -int guacenc_display_create_image_stream(guacenc_display* display, int index, - int mask, int layer_index, const char* mimetype, int x, int y) { - - /* Do not lookup / allocate if index is invalid */ - if (index < 0 || index > GUACENC_DISPLAY_MAX_STREAMS) { - guacenc_log(GUAC_LOG_WARNING, "Stream index out of bounds: %i", index); - return 1; - } - - /* Free existing stream (if any) */ - guacenc_image_stream_free(display->image_streams[index]); - - /* Associate new stream */ - guacenc_image_stream* stream = display->image_streams[index] = - guacenc_image_stream_alloc(mask, layer_index, mimetype, x, y); - - /* Return zero only if stream is not NULL */ - return stream == NULL; - -} - -guacenc_image_stream* guacenc_display_get_image_stream( - guacenc_display* display, int index) { - - /* Do not lookup / allocate if index is invalid */ - if (index < 0 || index > GUACENC_DISPLAY_MAX_STREAMS) { - guacenc_log(GUAC_LOG_WARNING, "Stream index out of bounds: %i", index); - return NULL; - } - - /* Return existing stream (if any) */ - return display->image_streams[index]; - -} - -int guacenc_display_free_image_stream(guacenc_display* display, int index) { - - /* Do not lookup / allocate if index is invalid */ - if (index < 0 || index > GUACENC_DISPLAY_MAX_STREAMS) { - guacenc_log(GUAC_LOG_WARNING, "Stream index out of bounds: %i", index); - return 1; - } - - /* Free stream (if allocated) */ - guacenc_image_stream_free(display->image_streams[index]); - - /* Mark stream as freed */ - display->image_streams[index] = NULL; - - return 0; - -} cairo_operator_t guacenc_display_cairo_operator(guac_composite_mode mask) { diff --git a/src/guacenc/display.h b/src/guacenc/display.h index a67b861f..cfbc5377 100644 --- a/src/guacenc/display.h +++ b/src/guacenc/display.h @@ -102,6 +102,22 @@ typedef struct guacenc_display { */ int guacenc_display_sync(guacenc_display* display, guac_timestamp timestamp); +/** + * Flattens the given display, rendering all child layers to the frame buffers + * of their parent layers. The frame buffer of the default layer of the display + * will thus contain the flattened, composited rendering of the entire display + * state after this function succeeds. The contents of the frame buffers of + * each layer are replaced by this function. + * + * @param display + * The display to flatten. + * + * @return + * Zero if the flatten operation succeeds, non-zero if an error occurs + * preventing proper rendering. + */ +int guacenc_display_flatten(guacenc_display* display); + /** * Allocates a new Guacamole video encoder display. This display serves as the * representation of encoding state, as well as the state of the Guacamole