Browse Source

Added Image#src= support. Closes #91

needs refactor
v1.x
Tj Holowaychuk 14 years ago
parent
commit
8179d4619e
  1. 3
      src/CanvasRenderingContext2d.cc
  2. 186
      src/Image.cc
  3. 8
      src/Image.h

3
src/CanvasRenderingContext2d.cc

@ -525,6 +525,9 @@ Context2d::DrawImage(const Arguments &args) {
// Image
if (Image::constructor->HasInstance(obj)) {
Image *img = ObjectWrap::Unwrap<Image>(obj);
if (!img->isComplete()) {
return ThrowException(Exception::Error(String::New("Image given has not completed loaded")));
}
sw = img->width;
sh = img->height;
surface = img->surface();

186
src/Image.cc

@ -10,6 +10,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <node_buffer.h>
#ifdef HAVE_JPEG
#include <jpeglib.h>
@ -17,6 +18,15 @@
Persistent<FunctionTemplate> Image::constructor;
/*
* Read closure used by loadFromBuffer.
*/
typedef struct {
unsigned len;
uint8_t *buf;
} read_closure_t;
/*
* Initialize Image.
*/
@ -74,7 +84,6 @@ Image::GetWidth(Local<String>, const AccessorInfo &info) {
Image *img = ObjectWrap::Unwrap<Image>(info.This());
return scope.Close(Number::New(img->width));
}
/*
* Get height.
*/
@ -94,7 +103,7 @@ Handle<Value>
Image::GetSrc(Local<String>, const AccessorInfo &info) {
HandleScope scope;
Image *img = ObjectWrap::Unwrap<Image>(info.This());
return scope.Close(String::New(img->filename));
return scope.Close(String::New(img->filename ? img->filename : ""));
}
/*
@ -104,20 +113,71 @@ Image::GetSrc(Local<String>, const AccessorInfo &info) {
void
Image::SetSrc(Local<String>, Local<Value> val, const AccessorInfo &info) {
HandleScope scope;
Image *img = ObjectWrap::Unwrap<Image>(info.This());
cairo_status_t status = CAIRO_STATUS_READ_ERROR;
// url string
if (val->IsString()) {
String::AsciiValue src(val);
Image *img = ObjectWrap::Unwrap<Image>(info.This());
if (img->filename) free(img->filename);
img->filename = strdup(*src);
cairo_status_t status = img->load();
// TODO: this does not work... something funky going on
if (status) {
img->error(Canvas::Error(status));
} else {
V8::AdjustAmountOfExternalAllocatedMemory(4 * img->width * img->height);
img->loaded();
}
status = img->load();
// Buffer
} else if (Buffer::HasInstance(val)) {
uint8_t *buf = (uint8_t *) Buffer::Data(val->ToObject());
unsigned len = Buffer::Length(val->ToObject());
status = img->loadFromBuffer(buf, len);
}
// check status
if (status) {
img->error(Canvas::Error(status));
} else {
img->loaded();
}
}
/*
* Load image data from `buf` by sniffing
* the bytes to determine format.
*/
cairo_status_t
Image::loadFromBuffer(uint8_t *buf, unsigned len) {
if (isPNG(buf)) return loadPNGFromBuffer(buf);
#ifdef HAVE_JPEG
if (isJPEG(buf)) return loadJPEGFromBuffer(buf, len);
#endif
return CAIRO_STATUS_READ_ERROR;
}
/*
* Load PNG data from `buf`.
*/
cairo_status_t
Image::loadPNGFromBuffer(uint8_t *buf) {
read_closure_t closure;
closure.len = 0;
closure.buf = buf;
_surface = cairo_image_surface_create_from_png_stream(readPNG, &closure);
cairo_status_t status = cairo_surface_status(_surface);
if (status) return status;
return CAIRO_STATUS_SUCCESS;
}
/*
* Read PNG data.
*/
cairo_status_t
Image::readPNG(void *c, uint8_t *data, unsigned int len) {
read_closure_t *closure = (read_closure_t *) c;
for (size_t i = 0; i < len; ++i) {
data[i] = closure->buf[i + closure->len];
}
closure->len += len;
return CAIRO_STATUS_SUCCESS;
}
/*
@ -209,6 +269,11 @@ Image::loaded() {
HandleScope scope;
state = COMPLETE;
width = cairo_image_surface_get_width(_surface);
height = cairo_image_surface_get_height(_surface);
// TODO: adjust accordingly when re-assigned src
V8::AdjustAmountOfExternalAllocatedMemory(4 * width * height);
if (!onload.IsEmpty()) {
TryCatch try_catch;
onload->Call(Context::GetCurrent()->Global(), 0, NULL);
@ -266,16 +331,76 @@ Image::loadSurface() {
cairo_status_t
Image::loadPNG() {
_surface = cairo_image_surface_create_from_png(filename);
cairo_status_t status = cairo_surface_status(_surface);
if (!status) {
width = cairo_image_surface_get_width(_surface);
height = cairo_image_surface_get_height(_surface);
}
return status;
return cairo_surface_status(_surface);
}
#ifdef HAVE_JPEG
/*
* Load jpeg from buffer.
*/
cairo_status_t
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_mem_src(&info, buf, len);
jpeg_read_header(&info, 1);
jpeg_start_decompress(&info);
width = info.output_width;
height = info.output_height;
// Data alloc
int stride = width * 4;
uint8_t *data = (uint8_t *) malloc(width * height * 4);
if (!data) return CAIRO_STATUS_NO_MEMORY;
uint8_t *src = (uint8_t *) malloc(width * 3);
if (!src) {
free(data);
return CAIRO_STATUS_NO_MEMORY;
}
// Copy RGB -> ARGB
for (int y = 0; y < height; ++y) {
jpeg_read_scanlines(&info, &src, 1);
uint32_t *row = (uint32_t *)(data + stride * y);
for (int x = 0; x < width; ++x) {
int bx = 3 * x;
uint32_t *pixel = row + x;
*pixel = 255 << 24
| src[bx + 0] << 16
| src[bx + 1] << 8
| src[bx + 2];
}
}
// New image surface
_surface = cairo_image_surface_create_for_data(
data
, CAIRO_FORMAT_ARGB32
, width
, height
, cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width));
// Cleanup
free(src);
jpeg_finish_decompress(&info);
jpeg_destroy_decompress(&info);
cairo_status_t status = cairo_surface_status(_surface);
if (status) {
free(data);
return status;
}
return CAIRO_STATUS_SUCCESS;
}
/*
* Load JPEG, convert RGB to ARGB.
*/
@ -359,3 +484,30 @@ Image::extension(const char *filename) {
if (len >= 4 && 0 == strcmp(".png", filename - 4)) return Image::PNG;
return Image::UNKNOWN;
}
/*
* Sniff bytes for JPEG's magic number ff d8.
*/
int
Image::isJPEG(uint8_t *data) {
return 0xff == data[0] && 0xd8 == data[1];
}
/*
* Sniff bytes 0..2 for "GIF".
*/
int
Image::isGIF(uint8_t *data) {
return 'G' == data[0] && 'I' == data[1] && 'F' == data[2];
}
/*
* Sniff bytes 1..3 for "PNG".
*/
int
Image::isPNG(uint8_t *data) {
return 'P' == data[1] && 'N' == data[2] && 'G' == data[3];
}

8
src/Image.h

@ -31,7 +31,15 @@ class Image: public node::ObjectWrap {
inline cairo_surface_t *surface(){ return _surface; }
inline uint8_t *data(){ return cairo_image_surface_get_data(_surface); }
inline int stride(){ return cairo_image_surface_get_stride(_surface); }
static int isPNG(unsigned char *data);
static int isJPEG(unsigned char *data);
static int isGIF(unsigned char *data);
static cairo_status_t readPNG(void *closure, unsigned char *data, unsigned len);
inline int isComplete(){ return COMPLETE == state; }
cairo_status_t loadSurface();
cairo_status_t loadFromBuffer(uint8_t *buf, unsigned len);
cairo_status_t loadJPEGFromBuffer(uint8_t *buf, unsigned len);
cairo_status_t loadPNGFromBuffer(uint8_t *buf);
cairo_status_t loadPNG();
#ifdef HAVE_JPEG
cairo_status_t loadJPEG();

Loading…
Cancel
Save