Skip to content

Commit

Permalink
Add optional mime-data tracking to Image.
Browse files Browse the repository at this point in the history
  • Loading branch information
c-spencer committed May 2, 2012
1 parent d5838bc commit 8b8ee7f
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 28 deletions.
21 changes: 5 additions & 16 deletions src/CanvasRenderingContext2d.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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();
}
Expand Down
175 changes: 163 additions & 12 deletions src/Image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ Handle<Value>
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();
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -596,22 +632,27 @@ 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;
cairo_status_t status;

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

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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
7 changes: 7 additions & 0 deletions src/Image.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value> error);
void loaded();
Expand Down Expand Up @@ -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();
};

Expand Down

0 comments on commit 8b8ee7f

Please sign in to comment.