From 8b8ee7f01706014f4bbc7cc0bbaa5bf1020da26c Mon Sep 17 00:00:00 2001 From: c-spencer Date: Wed, 2 May 2012 16:36:21 +0100 Subject: [PATCH] Add optional mime-data tracking to Image. --- src/CanvasRenderingContext2d.cc | 21 +--- src/Image.cc | 175 +++++++++++++++++++++++++++++--- src/Image.h | 7 ++ 3 files changed, 175 insertions(+), 28 deletions(-) diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index 97f9fcc..3952563 100644 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -538,10 +538,6 @@ Context2d::DrawImage(const Arguments &args) { if (args.Length() < 3) return ThrowException(Exception::TypeError(String::New("invalid arguments"))); -#if CAIRO_VERSION_MINOR < 10 - return ThrowException(Exception::Error(String::New("drawImage() needs cairo >= 1.10.0"))); -#else - int sx = 0 , sy = 0 , sw = 0 @@ -611,14 +607,10 @@ Context2d::DrawImage(const Arguments &args) { // Start draw cairo_save(ctx); - // Source surface - // TODO: only works with cairo >= 1.10.0 - cairo_surface_t *src = cairo_surface_create_for_rectangle( - surface - , sx - , sy - , sw - , sh); + context->savePath(); + cairo_rectangle(ctx, dx, dy, dw, dh); + cairo_clip(ctx); + context->restorePath(); // Scale src if (dw != sw || dh != sh) { @@ -630,14 +622,11 @@ Context2d::DrawImage(const Arguments &args) { } // Paint - cairo_set_source_surface(ctx, src, dx, dy); + cairo_set_source_surface(ctx, surface, dx - sx, dy - sy); cairo_pattern_set_filter(cairo_get_source(ctx), context->state->patternQuality); cairo_paint_with_alpha(ctx, context->state->globalAlpha); cairo_restore(ctx); - cairo_surface_destroy(src); - -#endif return Undefined(); } diff --git a/src/Image.cc b/src/Image.cc index f5e461b..1aafd2e 100644 --- a/src/Image.cc +++ b/src/Image.cc @@ -64,6 +64,22 @@ Handle Image::New(const Arguments &args) { HandleScope scope; Image *img = new Image; + + img->data_mode = DATA_IMAGE; + +#if CAIRO_VERSION_MINOR >= 10 + if (args[0]->IsString()) { + String::AsciiValue mode(args[0]->ToString()); + if (0 == strcmp("mime_only", *mode)) { + img->data_mode = DATA_MIME; + } else if (0 == strcmp("image_only", *mode)) { + img->data_mode = DATA_IMAGE; + } else if (0 == strcmp("image_and_mime", *mode)) { + img->data_mode = DATA_IMAGE_AND_MIME; + } + } +#endif + img->Wrap(args.This()); return args.This(); } @@ -154,7 +170,23 @@ Image::loadFromBuffer(uint8_t *buf, unsigned len) { if (isGIF(buf)) return loadGIFFromBuffer(buf, len); #endif #ifdef HAVE_JPEG - if (isJPEG(buf)) return loadJPEGFromBuffer(buf, len); +#if CAIRO_VERSION_MINOR < 10 + if (isJPEG(buf)) return loadJPEGFromBuffer(buf, len); +#else + if (isJPEG(buf)) { + switch (data_mode) { + case DATA_IMAGE: + return loadJPEGFromBuffer(buf, len); + case DATA_MIME: + return decodeJPEGBufferIntoMimeSurface(buf, len); + case DATA_IMAGE_AND_MIME: + cairo_status_t status; + status = loadJPEGFromBuffer(buf, len); + if (status) return status; + return assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG); + } + } +#endif #endif return CAIRO_STATUS_READ_ERROR; } @@ -252,7 +284,11 @@ Image::~Image() { cairo_surface_destroy(_surface); } - free(_data); + if (_data) free(_data); + if (_mime_data) { + V8::AdjustAmountOfExternalAllocatedMemory(-_mime_data_len); + free(_mime_data); + } free(filename); } @@ -596,6 +632,11 @@ static void jpeg_mem_src (j_decompress_ptr cinfo, void* buffer, long nbytes) { #endif +/* + * Takes an initialised jpeg_decompress_struct and decodes the + * data into _surface. + */ + cairo_status_t Image::decodeJPEGIntoSurface(jpeg_decompress_struct *info) { int stride = width * 4; @@ -603,7 +644,7 @@ Image::decodeJPEGIntoSurface(jpeg_decompress_struct *info) { uint8_t *data = (uint8_t *) malloc(width * height * 4); if (!data) { - jpeg_finish_decompress(info); + jpeg_abort_decompress(info); jpeg_destroy_decompress(info); return CAIRO_STATUS_NO_MEMORY; } @@ -611,7 +652,7 @@ Image::decodeJPEGIntoSurface(jpeg_decompress_struct *info) { uint8_t *src = (uint8_t *) malloc(width * 3); if (!src) { free(data); - jpeg_finish_decompress(info); + jpeg_abort_decompress(info); jpeg_destroy_decompress(info); return CAIRO_STATUS_NO_MEMORY; } @@ -647,16 +688,22 @@ Image::decodeJPEGIntoSurface(jpeg_decompress_struct *info) { } free(src); + _data = data; + _data_len = width * height * 4; + return CAIRO_STATUS_SUCCESS; } +#if CAIRO_VERSION_MINOR >= 10 + /* - * Load jpeg from buffer. + * Takes a jpeg data buffer and assigns it as mime data to a + * dummy surface */ cairo_status_t -Image::loadJPEGFromBuffer(uint8_t *buf, unsigned len) { +Image::decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len) { // TODO: remove this duplicate logic // JPEG setup struct jpeg_decompress_struct info; @@ -671,32 +718,136 @@ Image::loadJPEGFromBuffer(uint8_t *buf, unsigned len) { width = info.output_width; height = info.output_height; - return decodeJPEGIntoSurface(&info); + // Data alloc + // 8 pixels per byte using Alpha Channel format to reduce memory requirement. + int buf_size = height * cairo_format_stride_for_width(CAIRO_FORMAT_A1, width); + uint8_t *data = (uint8_t *) malloc(buf_size); + if (!data) return CAIRO_STATUS_NO_MEMORY; + + // New image surface + _surface = cairo_image_surface_create_for_data( + data + , CAIRO_FORMAT_A1 + , width + , height + , cairo_format_stride_for_width(CAIRO_FORMAT_A1, width)); + + // Cleanup + jpeg_abort_decompress(&info); + jpeg_destroy_decompress(&info); + cairo_status_t status = cairo_surface_status(_surface); + + if (status) { + free(data); + return status; + } + + _data = data; + _data_len = buf_size; + + return assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG); +} + +cairo_status_t +Image::assignDataAsMime(uint8_t *data, int len, const char *mime_type) { + _mime_data = (uint8_t *) malloc(len); + if (!_mime_data) return CAIRO_STATUS_NO_MEMORY; + + V8::AdjustAmountOfExternalAllocatedMemory(len); + + memcpy(_mime_data, data, len); + _mime_data_len = len; + + return cairo_surface_set_mime_data(_surface, mime_type, _mime_data, _mime_data_len, free, _mime_data); } +#endif + /* - * Load JPEG, convert RGB to ARGB. + * Load jpeg from buffer. */ cairo_status_t -Image::loadJPEG(FILE *stream) { +Image::loadJPEGFromBuffer(uint8_t *buf, unsigned len) { + // TODO: remove this duplicate logic // JPEG setup struct jpeg_decompress_struct info; struct jpeg_error_mgr err; info.err = jpeg_std_error(&err); jpeg_create_decompress(&info); - jpeg_stdio_src(&info, stream); + jpeg_mem_src(&info, buf, len); jpeg_read_header(&info, 1); jpeg_start_decompress(&info); width = info.output_width; height = info.output_height; + return decodeJPEGIntoSurface(&info); +} + +/* + * Load JPEG, convert RGB to ARGB. + */ + +cairo_status_t +Image::loadJPEG(FILE *stream) { cairo_status_t status; - status = decodeJPEGIntoSurface(&info); - fclose(stream); + printf("loadJPEG\n"); + + if (data_mode == DATA_IMAGE) { // Can lazily read in the JPEG. + // JPEG setup + struct jpeg_decompress_struct info; + struct jpeg_error_mgr err; + info.err = jpeg_std_error(&err); + jpeg_create_decompress(&info); + + jpeg_stdio_src(&info, stream); + + jpeg_read_header(&info, 1); + jpeg_start_decompress(&info); + width = info.output_width; + height = info.output_height; + + status = decodeJPEGIntoSurface(&info); + fclose(stream); + } else { // We'll need the actual source jpeg data, so read fully. +#if CAIRO_VERSION_MINOR >= 10 + uint8_t *buf; + unsigned len; + + fseek(stream, 0, SEEK_END); + len = ftell(stream); + fseek(stream, 0, SEEK_SET); + + buf = (uint8_t *) malloc(len); + if (!buf) return CAIRO_STATUS_NO_MEMORY; + + fread(buf, len, 1, stream); + fclose(stream); + + status = loadJPEGFromBuffer(buf, len); + if (status) { + free(buf); + return status; + } + + switch (data_mode) { + case DATA_IMAGE: // Can't be this, but compiler warning. + case DATA_IMAGE_AND_MIME: + status = loadJPEGFromBuffer(buf, len); + if (status) break; + status = assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG); + break; + case DATA_MIME: + status = decodeJPEGBufferIntoMimeSurface(buf, len); + break; + } + + free(buf); +#endif + } return status; } diff --git a/src/Image.h b/src/Image.h index 0c2dd50..463e41c 100644 --- a/src/Image.h +++ b/src/Image.h @@ -53,6 +53,10 @@ class Image: public node::ObjectWrap { cairo_status_t loadJPEGFromBuffer(uint8_t *buf, unsigned len); cairo_status_t loadJPEG(FILE *stream); cairo_status_t decodeJPEGIntoSurface(jpeg_decompress_struct *info); +#if CAIRO_VERSION_MINOR >= 10 + cairo_status_t decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len); + cairo_status_t assignDataAsMime(uint8_t *data, int len, const char *mime_type); +#endif #endif void error(Local error); void loaded(); @@ -83,6 +87,9 @@ class Image: public node::ObjectWrap { private: cairo_surface_t *_surface; uint8_t *_data; + int _data_len; + uint8_t *_mime_data; + int _mime_data_len; ~Image(); };