Browse Source

Merge branch 'integration'

v1.x
Tj Holowaychuk 14 years ago
parent
commit
c06ddd2228
  1. 4
      .gitignore
  2. 209
      src/Image.cc
  3. 7
      src/Image.h
  4. 7
      wscript

4
.gitignore

@ -7,3 +7,7 @@ testing
test.png
.pomo
node_modules
# Vim cruft
*.swp
*.un~

209
src/Image.cc

@ -16,6 +16,15 @@
#include <jpeglib.h>
#endif
#ifdef HAVE_GIF
#include <gif_lib.h>
typedef struct {
uint8_t *buf;
unsigned len;
unsigned pos;
} gif_data_t;
#endif
Persistent<FunctionTemplate> Image::constructor;
/*
@ -145,6 +154,9 @@ Image::SetSrc(Local<String>, Local<Value> val, const AccessorInfo &info) {
cairo_status_t
Image::loadFromBuffer(uint8_t *buf, unsigned len) {
if (isPNG(buf)) return loadPNGFromBuffer(buf);
#ifdef HAVE_GIF
if (isGIF(buf)) return loadGIFFromBuffer(buf, len);
#endif
#ifdef HAVE_JPEG
if (isJPEG(buf)) return loadJPEGFromBuffer(buf, len);
#endif
@ -313,6 +325,10 @@ Image::loadSurface() {
switch (extension(filename)) {
case Image::PNG:
return loadPNG();
#ifdef HAVE_GIF
case Image::GIF:
return loadGIF();
#endif
#ifdef HAVE_JPEG
case Image::JPEG:
return loadJPEG();
@ -332,6 +348,198 @@ Image::loadPNG() {
return cairo_surface_status(_surface);
}
// GIF support
#ifdef HAVE_GIF
/*
* Return the alpha color for `gif` at `frame`, or -1.
*/
int
get_gif_transparent_color(GifFileType *gif, int frame) {
ExtensionBlock *ext = gif->SavedImages[frame].ExtensionBlocks;
int len = gif->SavedImages[frame].ExtensionBlockCount;
for (int x = 0; x < len; ++x, ++ext) {
if ((ext->Function == GRAPHICS_EXT_FUNC_CODE) && (ext->Bytes[0] & 1)) {
return ext->Bytes[3] == 0 ? 0 : (uint8_t) ext->Bytes[3];
}
}
return -1;
}
/*
* Memory GIF reader callback.
*/
int
read_gif_from_memory(GifFileType *gif, GifByteType *buf, int len) {
gif_data_t *data = (gif_data_t *) gif->UserData;
if ((data->pos + len) > data->len) len = data->len - data->pos;
memcpy(buf, data->pos + data->buf, len);
data->pos += len;
return len;
}
/*
* Load GIF.
*/
cairo_status_t
Image::loadGIF() {
FILE *stream = fopen(filename, "r");
if (!stream) return CAIRO_STATUS_READ_ERROR;
fseek(stream, 0L, SEEK_END);
int len = ftell(stream);
fseek(stream, 0L, SEEK_SET);
uint8_t *buf = (uint8_t *) malloc(len);
if (!buf) {
fclose(stream);
return CAIRO_STATUS_NO_MEMORY;
}
size_t read = fread(buf, len, 1, stream);
fclose(stream);
cairo_status_t result = CAIRO_STATUS_READ_ERROR;
if (1 == read) result = loadGIFFromBuffer(buf, len);
free(buf);
return result;
}
/*
* Load give from `buf` and the given `len`.
*/
cairo_status_t
Image::loadGIFFromBuffer(uint8_t *buf, unsigned len) {
int i = 0;
GifFileType* gif;
gif_data_t gifd = { buf, len, 0 };
if ((gif = DGifOpen((void*) &gifd, read_gif_from_memory)) == NULL)
return CAIRO_STATUS_READ_ERROR;
if (GIF_OK != DGifSlurp(gif)) {
DGifCloseFile(gif);
return CAIRO_STATUS_READ_ERROR;
}
width = gif->SWidth;
height = gif->SHeight;
uint8_t *data = (uint8_t *) malloc(width * height * 4);
if (!data) {
DGifCloseFile(gif);
return CAIRO_STATUS_NO_MEMORY;
}
GifImageDesc *img = &gif->SavedImages[i].ImageDesc;
// local colormap takes precedence over global
ColorMapObject *colormap = img->ColorMap
? img->ColorMap
: gif->SColorMap;
int bgColor = 0;
int alphaColor = get_gif_transparent_color(gif, i);
if (gif->SColorMap) bgColor = (uint8_t) gif->SBackGroundColor;
else if(alphaColor >= 0) bgColor = alphaColor;
uint8_t *src_data = (uint8_t*) gif->SavedImages[i].RasterBits;
uint32_t *dst_data = (uint32_t*) data;
if (!gif->Image.Interlace) {
if (width == img->Width && height == img->Height) {
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
*dst_data = ((*src_data == alphaColor) ? 0 : 255) << 24
| colormap->Colors[*src_data].Red << 16
| colormap->Colors[*src_data].Green << 8
| colormap->Colors[*src_data].Blue;
dst_data++;
src_data++;
}
}
} else {
// Image does not take up whole "screen" so we need to fill-in the background
int bottom = img->Top + img->Height;
int right = img->Left + img->Width;
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
if (y < img->Top || y >= bottom || x < img->Left || x >= right) {
*dst_data = ((bgColor == alphaColor) ? 0 : 255) << 24
| colormap->Colors[bgColor].Red << 16
| colormap->Colors[bgColor].Green << 8
| colormap->Colors[bgColor].Blue;
} else {
*dst_data = ((*src_data == alphaColor) ? 0 : 255) << 24
| colormap->Colors[*src_data].Red << 16
| colormap->Colors[*src_data].Green << 8
| colormap->Colors[*src_data].Blue;
}
dst_data++;
src_data++;
}
}
}
} else {
// Image is interlaced so that it streams nice over 14.4k and 28.8k modems :)
// We first load in 1/8 of the image, followed by another 1/8, followed by
// 1/4 and finally the remaining 1/2.
int ioffs[] = { 0, 4, 2, 1 };
int ijumps[] = { 8, 8, 4, 2 };
uint8_t *src_ptr = src_data;
uint32_t *dst_ptr;
for(int z = 0; z < 4; z++) {
for(int y = ioffs[z]; y < height; y += ijumps[z]) {
dst_ptr = dst_data + width * y;
for(int x = 0; x < width; ++x) {
*dst_ptr = ((*src_ptr == alphaColor) ? 0 : 255) << 24
| (colormap->Colors[*src_ptr].Red) << 16
| (colormap->Colors[*src_ptr].Green) << 8
| (colormap->Colors[*src_ptr].Blue);
dst_ptr++;
src_ptr++;
}
}
}
}
DGifCloseFile(gif);
// 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));
cairo_status_t status = cairo_surface_status(_surface);
if (status) {
free(data);
return status;
}
return CAIRO_STATUS_SUCCESS;
}
#endif /* HAVE_GIF */
// JPEG support
#ifdef HAVE_JPEG
/*
@ -478,6 +686,7 @@ Image::extension(const char *filename) {
size_t len = strlen(filename);
filename += len;
if (len >= 5 && 0 == strcmp(".jpeg", filename - 5)) return Image::JPEG;
if (len >= 4 && 0 == strcmp(".gif", filename - 4)) return Image::GIF;
if (len >= 4 && 0 == strcmp(".jpg", filename - 4)) return Image::JPEG;
if (len >= 4 && 0 == strcmp(".png", filename - 4)) return Image::PNG;
return Image::UNKNOWN;

7
src/Image.h

@ -40,6 +40,10 @@ class Image: public node::ObjectWrap {
cairo_status_t loadFromBuffer(uint8_t *buf, unsigned len);
cairo_status_t loadPNGFromBuffer(uint8_t *buf);
cairo_status_t loadPNG();
#ifdef HAVE_GIF
cairo_status_t loadGIFFromBuffer(uint8_t *buf, unsigned len);
cairo_status_t loadGIF();
#endif
#ifdef HAVE_JPEG
cairo_status_t loadJPEGFromBuffer(uint8_t *buf, unsigned len);
cairo_status_t loadJPEG();
@ -57,12 +61,13 @@ class Image: public node::ObjectWrap {
typedef enum {
UNKNOWN
, GIF
, JPEG
, PNG
} type;
static type extension(const char *filename);
private:
cairo_surface_t *_surface;
~Image();

7
wscript

@ -16,7 +16,10 @@ def configure(conf):
conf.check_tool('compiler_cxx')
conf.check_tool('node_addon')
conf.env.append_value('CPPFLAGS', '-DNDEBUG')
if conf.check(lib='gif', uselib_store='GIF', mandatory=False):
conf.env.append_value('CPPFLAGS', '-DHAVE_GIF=1')
if conf.check(lib='jpeg', uselib_store='JPEG', mandatory=False):
conf.env.append_value('CPPFLAGS', '-DHAVE_JPEG=1')
@ -33,4 +36,4 @@ def build(bld):
obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
obj.target = 'canvas'
obj.source = bld.glob('src/*.cc')
obj.uselib = ['CAIRO', 'JPEG']
obj.uselib = ['CAIRO', 'GIF', 'JPEG']

Loading…
Cancel
Save