From a15a86ed005a7260732d1be8b30990025cff47e6 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 27 Feb 2016 23:31:07 -0800 Subject: [PATCH] GUAC-236: Assemble entire image data prior to decoding. Switch to simple decoder function (no struct). --- src/guacenc/image-stream.c | 66 +++++++++----- src/guacenc/image-stream.h | 172 ++++++++++++------------------------- src/guacenc/jpeg.c | 31 +------ src/guacenc/jpeg.h | 2 +- src/guacenc/png.c | 31 +------ src/guacenc/png.h | 2 +- src/guacenc/webp.c | 31 +------ src/guacenc/webp.h | 2 +- 8 files changed, 116 insertions(+), 221 deletions(-) diff --git a/src/guacenc/image-stream.c b/src/guacenc/image-stream.c index 371fa6ba..fd442e1d 100644 --- a/src/guacenc/image-stream.c +++ b/src/guacenc/image-stream.c @@ -30,14 +30,16 @@ #include "webp.h" #endif +#include + #include #include guacenc_decoder_mapping guacenc_decoder_map[] = { - {"image/png", &guacenc_png_decoder}, - {"image/jpeg", &guacenc_jpeg_decoder}, + {"image/png", guacenc_png_decoder}, + {"image/jpeg", guacenc_jpeg_decoder}, #ifdef ENABLE_WEBP - {"image/webp", &guacenc_webp_decoder}, + {"image/webp", guacenc_webp_decoder}, #endif {NULL, NULL} }; @@ -78,9 +80,12 @@ guacenc_image_stream* guacenc_image_stream_alloc(int mask, int index, stream->y = y; /* Associate with corresponding decoder */ - guacenc_decoder* decoder = stream->decoder = guacenc_get_decoder(mimetype); - if (decoder != NULL) - decoder->init_handler(stream); + stream->decoder = guacenc_get_decoder(mimetype); + + /* Allocate initial buffer */ + stream->length = 0; + stream->max_length = GUACENC_IMAGE_STREAM_INITIAL_LENGTH; + stream->buffer = (unsigned char*) malloc(stream->max_length); return stream; @@ -89,12 +94,27 @@ guacenc_image_stream* guacenc_image_stream_alloc(int mask, int index, int guacenc_image_stream_receive(guacenc_image_stream* stream, unsigned char* data, int length) { - /* Invoke data handler of corresponding decoder (if any) */ - guacenc_decoder* decoder = stream->decoder; - if (decoder != NULL) - return decoder->data_handler(stream, data, length); + /* Allocate more space if necessary */ + if (stream->max_length - stream->length < length) { - /* If there is no decoder, simply return success */ + /* Calculate a reasonable new max length guaranteed to fit buffer */ + int new_max_length = stream->max_length * 2 + length; + + /* Attempt to resize buffer */ + unsigned char* new_buffer = + (unsigned char*) realloc(stream->buffer, new_max_length); + if (new_buffer == NULL) + return 1; + + /* Store updated buffer and size */ + stream->buffer = new_buffer; + stream->max_length = new_max_length; + + } + + /* Append data */ + memcpy(stream->buffer + stream->length, data, length); + stream->length += length; return 0; } @@ -102,12 +122,20 @@ int guacenc_image_stream_receive(guacenc_image_stream* stream, int guacenc_image_stream_end(guacenc_image_stream* stream, guacenc_buffer* buffer) { - /* Invoke end handler of corresponding decoder (if any) */ - guacenc_decoder* decoder = stream->decoder; - if (decoder != NULL) - return decoder->end_handler(stream, buffer); - /* If there is no decoder, simply return success */ + guacenc_decoder* decoder = stream->decoder; + if (decoder == NULL) + return 0; + + /* Decode received data to a Cairo surface */ + cairo_surface_t* surface = stream->decoder(stream->buffer, stream->length); + if (surface == NULL) + return 1; + + /* Draw surface to buffer */ + cairo_set_source_surface(buffer->cairo, surface, stream->x, stream->y); + cairo_fill(buffer->cairo); + cairo_surface_destroy(surface); return 0; } @@ -118,10 +146,8 @@ int guacenc_image_stream_free(guacenc_image_stream* stream) { if (stream == NULL) return 0; - /* Invoke free handler for decoder (if associated) */ - guacenc_decoder* decoder = stream->decoder; - if (decoder != NULL) - decoder->free_handler(stream); + /* Free image buffer */ + free(stream->buffer); /* Free actual stream */ free(stream); diff --git a/src/guacenc/image-stream.h b/src/guacenc/image-stream.h index 22b72f1d..26660eba 100644 --- a/src/guacenc/image-stream.h +++ b/src/guacenc/image-stream.h @@ -26,11 +26,31 @@ #include "config.h" #include "buffer.h" +#include + /** - * A decoder implementation which processes arbitrary image data of a - * particular type. Image data is fed explicitly into the decoder as chunks. + * The initial number of bytes to allocate for the image data buffer. If this + * buffer is not sufficiently larged, it will be dynamically reallocated as it + * grows. */ -typedef struct guacenc_decoder guacenc_decoder; +#define GUACENC_IMAGE_STREAM_INITIAL_LENGTH 4096 + +/** + * Callback function which is provided raw, encoded image data of the given + * length. The function is expected to return a new Cairo surface which will + * later (by guacenc) be freed via cairo_surface_destroy(). + * + * @param data + * The raw encoded image data that this function must decode. + * + * @param length + * The length of the image data, in bytes. + * + * @return + * A newly-allocated Cairo surface containing the decoded image, or NULL + * or decoding fails. + */ +typedef cairo_surface_t* guacenc_decoder(unsigned char* data, int length); /** * The current state of an allocated Guacamole image stream. @@ -60,117 +80,34 @@ typedef struct guacenc_image_stream { */ int y; + /** + * Buffer of image data which will be built up over time as chunks are + * received via "blob" instructions. This will ultimately be passed in its + * entirety to the decoder function. + */ + unsigned char* buffer; + + /** + * The number of bytes currently stored in the buffer. + */ + int length; + + /** + * The maximum number of bytes that can be stored in the current buffer + * before it must be reallocated. + */ + int max_length; + /** * The decoder to use when decoding the raw data received along this * stream, or NULL if no such decoder exists. */ guacenc_decoder* decoder; - /** - * Arbitrary implementation-specific data associated with the stream. - */ - void* data; - } guacenc_image_stream; /** - * Callback function which is invoked when a decoder has been assigned to an - * image stream. - * - * @param stream - * The image stream that the decoder has been assigned to. - * - * @return - * Zero if initialization was successful, non-zero otherwise. - */ -typedef int guacenc_decoder_init_handler(guacenc_image_stream* stream); - -/** - * Callback function which is invoked when data has been received along an - * image stream with an associated decoder. - * - * @param stream - * The image stream that the decoder was assigned to. - * - * @param data - * The chunk of data received along the image stream. - * - * @param length - * The length of the chunk of data received, in bytes. - * - * @return - * Zero if the provided data was processed successfully, non-zero - * otherwise. - */ -typedef int guacenc_decoder_data_handler(guacenc_image_stream* stream, - unsigned char* data, int length); - -/** - * Callback function which is invoked when an image stream with an associated - * decoder has ended (reached end-of-stream). The image stream will contain - * the required meta-information describing the drawing operation, including - * the destination X/Y coordinates. - * - * @param stream - * The image stream that has ended. - * - * @param buffer - * The buffer to which the decoded image should be drawn. - * - * @return - * Zero if the end of the stream has been processed successfully and the - * resulting image has been rendered to the given buffer, non-zero - * otherwise. - */ -typedef int guacenc_decoder_end_handler(guacenc_image_stream* stream, - guacenc_buffer* buffer); - -/** - * Callback function which will be invoked when the data associated with an - * image stream must be freed. This may happen at any time, and will not - * necessarily occur only after the image stream has ended. It is possible - * that an image stream will be in-progress at the end of a protocol dump, thus - * the memory associated with the stream will need to be freed without ever - * ending. - * - * @param stream - * The stream whose associated data must be freed. - * - * @return - * Zero if the data was successfully freed, non-zero otherwise. - */ -typedef int guacenc_decoder_free_handler(guacenc_image_stream* stream); - -struct guacenc_decoder { - - /** - * Callback invoked when this decoder has just been assigned to an image - * stream. - */ - guacenc_decoder_init_handler* init_handler; - - /** - * Callback invoked when data has been received along an image stream to - * which this decoder has been assigned. - */ - guacenc_decoder_data_handler* data_handler; - - /** - * Callback invoked when an image stream to which this decoder has been - * assigned has ended (reached end-of-stream). - */ - guacenc_decoder_end_handler* end_handler; - - /** - * Callback invoked when data associated with an image stream by this - * decoder must be freed. - */ - guacenc_decoder_free_handler* free_handler; - -}; - -/** - * Mapping of image mimetype to corresponding decoder. + * Mapping of image mimetype to corresponding decoder function. */ typedef struct guacenc_decoder_mapping { @@ -180,8 +117,8 @@ typedef struct guacenc_decoder_mapping { const char* mimetype; /** - * The decoder to use when an image stream of the associated mimetype is - * received. + * The decoder function to use when an image stream of the associated + * mimetype is received. */ guacenc_decoder* decoder; @@ -238,9 +175,9 @@ guacenc_image_stream* guacenc_image_stream_alloc(int mask, int index, const char* mimetype, int x, int y); /** - * Signals the decoder of the given image stream that a chunk of image data - * has been received. If no decoder is associated with the given image stream, - * this function has no effect. + * Appends newly-received data to the internal buffer of the given image + * stream, such that the entire received image can be fed to the decoder as one + * buffer once the stream ends. * * @param stream * The image stream that received the data. @@ -252,18 +189,19 @@ guacenc_image_stream* guacenc_image_stream_alloc(int mask, int index, * The length of the chunk of data received, in bytes. * * @return - * Zero if the given data was handled successfully by the decoder, or - * non-zero if an error occurs. + * Zero if the given data was successfully appended to the in-progress + * image, non-zero if an error occurs. */ int guacenc_image_stream_receive(guacenc_image_stream* stream, unsigned char* data, int length); /** - * Signals the decoder of the given image stream that no more data will be - * received and the image should be written to the given buffer as-is. If no - * decoder is associated with the given image stream, this function has no - * effect. Meta-information describing the image draw operation itself is - * stored within the guacenc_image_stream. + * Marks the end of the given image stream (no more data will be received) and + * invokes the associated decoder. The decoded image will be written to the + * given buffer as-is. If no decoder is associated with the given image stream, + * this function has no effect. Meta-information describing the image draw + * operation itself is pulled from the guacenc_image_stream, having been stored + * there when the image stream was created. * * @param stream * The image stream that has ended. diff --git a/src/guacenc/jpeg.c b/src/guacenc/jpeg.c index 397589ef..706e21c7 100644 --- a/src/guacenc/jpeg.c +++ b/src/guacenc/jpeg.c @@ -21,37 +21,14 @@ */ #include "config.h" -#include "buffer.h" -#include "image-stream.h" #include "jpeg.h" +#include + #include -int guacenc_jpeg_init(guacenc_image_stream* stream) { +cairo_surface_t* guacenc_jpeg_decoder(unsigned char* data, int length) { /* STUB */ - return 0; + return NULL; } -int guacenc_jpeg_data(guacenc_image_stream* stream, unsigned char* data, - int length) { - /* STUB */ - return 0; -} - -int guacenc_jpeg_end(guacenc_image_stream* stream, guacenc_buffer* buffer) { - /* STUB */ - return 0; -} - -int guacenc_jpeg_free(guacenc_image_stream* stream) { - /* STUB */ - return 0; -} - -guacenc_decoder guacenc_jpeg_decoder = { - .init_handler = guacenc_jpeg_init, - .data_handler = guacenc_jpeg_data, - .end_handler = guacenc_jpeg_end, - .free_handler = guacenc_jpeg_free -}; - diff --git a/src/guacenc/jpeg.h b/src/guacenc/jpeg.h index 718da3ad..142fbd12 100644 --- a/src/guacenc/jpeg.h +++ b/src/guacenc/jpeg.h @@ -29,7 +29,7 @@ /** * Decoder implementation which handles "image/jpeg" images. */ -extern guacenc_decoder guacenc_jpeg_decoder; +guacenc_decoder guacenc_jpeg_decoder; #endif diff --git a/src/guacenc/png.c b/src/guacenc/png.c index d77b59eb..68b4d741 100644 --- a/src/guacenc/png.c +++ b/src/guacenc/png.c @@ -21,37 +21,14 @@ */ #include "config.h" -#include "buffer.h" -#include "image-stream.h" #include "png.h" +#include + #include -int guacenc_png_init(guacenc_image_stream* stream) { +cairo_surface_t* guacenc_png_decoder(unsigned char* data, int length) { /* STUB */ - return 0; + return NULL; } -int guacenc_png_data(guacenc_image_stream* stream, unsigned char* data, - int length) { - /* STUB */ - return 0; -} - -int guacenc_png_end(guacenc_image_stream* stream, guacenc_buffer* buffer) { - /* STUB */ - return 0; -} - -int guacenc_png_free(guacenc_image_stream* stream) { - /* STUB */ - return 0; -} - -guacenc_decoder guacenc_png_decoder = { - .init_handler = guacenc_png_init, - .data_handler = guacenc_png_data, - .end_handler = guacenc_png_end, - .free_handler = guacenc_png_free -}; - diff --git a/src/guacenc/png.h b/src/guacenc/png.h index 9e3f68ac..86307a0b 100644 --- a/src/guacenc/png.h +++ b/src/guacenc/png.h @@ -29,7 +29,7 @@ /** * Decoder implementation which handles "image/png" images. */ -extern guacenc_decoder guacenc_png_decoder; +guacenc_decoder guacenc_png_decoder; #endif diff --git a/src/guacenc/webp.c b/src/guacenc/webp.c index 2a375669..63024373 100644 --- a/src/guacenc/webp.c +++ b/src/guacenc/webp.c @@ -21,37 +21,14 @@ */ #include "config.h" -#include "buffer.h" -#include "image-stream.h" #include "webp.h" +#include + #include -int guacenc_webp_init(guacenc_image_stream* stream) { +cairo_surface_t* guacenc_webp_decoder(unsigned char* data, int length) { /* STUB */ - return 0; + return NULL; } -int guacenc_webp_data(guacenc_image_stream* stream, unsigned char* data, - int length) { - /* STUB */ - return 0; -} - -int guacenc_webp_end(guacenc_image_stream* stream, guacenc_buffer* buffer) { - /* STUB */ - return 0; -} - -int guacenc_webp_free(guacenc_image_stream* stream) { - /* STUB */ - return 0; -} - -guacenc_decoder guacenc_webp_decoder = { - .init_handler = guacenc_webp_init, - .data_handler = guacenc_webp_data, - .end_handler = guacenc_webp_end, - .free_handler = guacenc_webp_free -}; - diff --git a/src/guacenc/webp.h b/src/guacenc/webp.h index a56f67d6..a6159698 100644 --- a/src/guacenc/webp.h +++ b/src/guacenc/webp.h @@ -29,7 +29,7 @@ /** * Decoder implementation which handles "image/webp" images. */ -extern guacenc_decoder guacenc_webp_decoder; +guacenc_decoder guacenc_webp_decoder; #endif