Browse Source

Add optional mime-data tracking to Image.

v1.x
c-spencer 13 years ago
parent
commit
8b8ee7f017
  1. 21
      src/CanvasRenderingContext2d.cc
  2. 175
      src/Image.cc
  3. 7
      src/Image.h

21
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();
}

175
src/Image.cc

@ -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();
}
@ -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;
}

7
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<Value> 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();
};

Loading…
Cancel
Save