Skip to content

Commit

Permalink
DynamicGifStack is now async
Browse files Browse the repository at this point in the history
  • Loading branch information
pkrumins committed Aug 7, 2010
1 parent c962843 commit 726cf38
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 58 deletions.
8 changes: 8 additions & 0 deletions src/common.h
Expand Up @@ -32,5 +32,13 @@ bool str_eq(const char *s1, const char *s2);

typedef enum { BUF_RGB, BUF_BGR, BUF_RGBA, BUF_BGRA } buffer_type;

struct encode_request {
v8::Persistent<v8::Function> callback;
void *gif_obj;
char *gif;
int gif_len;
char *error;
};

#endif

223 changes: 177 additions & 46 deletions src/dynamic_gif_stack.cpp
Expand Up @@ -6,7 +6,7 @@ using namespace v8;
using namespace node;

std::pair<Point, Point>
DynamicGifStack::OptimalDimension()
DynamicGifStack::optimal_dimension()
{
Point top(-1, -1), bottom(-1, -1);
for (GifUpdates::iterator it = gif_stack.begin(); it != gif_stack.end(); ++it) {
Expand All @@ -24,6 +24,51 @@ DynamicGifStack::OptimalDimension()
return std::make_pair(top, bottom);
}

void
DynamicGifStack::construct_gif_data(unsigned char *data, Point &top)
{
switch (buf_type) {
case BUF_RGB:
case BUF_RGBA:
for (GifUpdates::iterator it = gif_stack.begin(); it != gif_stack.end(); ++it) {
GifUpdate *gif = *it;
int start = (gif->y - top.y)*width*3 + (gif->x - top.x)*3;
unsigned char *gifdatap = gif->data;
for (int i = 0; i < gif->h; i++) {
unsigned char *datap = &data[start + i*width*3];
for (int j = 0; j < gif->w; j++) {
*datap++ = *gifdatap++;
*datap++ = *gifdatap++;
*datap++ = *gifdatap++;
if (buf_type == BUF_RGBA) gifdatap++;
}
}
}
break;

case BUF_BGR:
case BUF_BGRA:
for (GifUpdates::iterator it = gif_stack.begin(); it != gif_stack.end(); ++it) {
GifUpdate *gif = *it;
int start = (gif->y - top.y)*width*3 + (gif->x - top.x)*3;
unsigned char *gifdatap = gif->data;
for (int i = 0; i < gif->h; i++) {
unsigned char *datap = &data[start + i*width*3];
for (int j = 0; j < gif->w; j++) {
*datap++ = *(gifdatap + 2);
*datap++ = *(gifdatap + 1);
*datap++ = *gifdatap;
gifdatap += (buf_type == BUF_BGRA) ? 4 : 3;
}
}
}
break;

default:
throw "Unexpected buf_type in DynamicGifStack::GifEncode";
}
}

void
DynamicGifStack::Initialize(Handle<Object> target)
{
Expand All @@ -32,7 +77,8 @@ DynamicGifStack::Initialize(Handle<Object> target)
Local<FunctionTemplate> t = FunctionTemplate::New(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
NODE_SET_PROTOTYPE_METHOD(t, "push", Push);
NODE_SET_PROTOTYPE_METHOD(t, "encode", GifEncode);
NODE_SET_PROTOTYPE_METHOD(t, "encode", GifEncodeAsync);
NODE_SET_PROTOTYPE_METHOD(t, "encodeSync", GifEncodeSync);
NODE_SET_PROTOTYPE_METHOD(t, "dimensions", Dimensions);
target->Set(String::NewSymbol("DynamicGifStack"), t->GetFunction());
}
Expand Down Expand Up @@ -61,11 +107,11 @@ DynamicGifStack::Push(Buffer *buf, int x, int y, int w, int h)
}

Handle<Value>
DynamicGifStack::GifEncode()
DynamicGifStack::GifEncodeSync()
{
HandleScope scope;

std::pair<Point, Point> optimal = OptimalDimension();
std::pair<Point, Point> optimal = optimal_dimension();
Point top = optimal.first, bot = optimal.second;

offset = top;
Expand All @@ -82,46 +128,7 @@ DynamicGifStack::GifEncode()
*datap++ = transparency_color.b;
}

switch (buf_type) {
case BUF_RGB:
case BUF_RGBA:
for (GifUpdates::iterator it = gif_stack.begin(); it != gif_stack.end(); ++it) {
GifUpdate *gif = *it;
int start = (gif->y - top.y)*width*3 + (gif->x - top.x)*3;
unsigned char *gifdatap = gif->data;
for (int i = 0; i < gif->h; i++) {
unsigned char *datap = &data[start + i*width*3];
for (int j = 0; j < gif->w; j++) {
*datap++ = *gifdatap++;
*datap++ = *gifdatap++;
*datap++ = *gifdatap++;
if (buf_type == BUF_RGBA) gifdatap++;
}
}
}
break;

case BUF_BGR:
case BUF_BGRA:
for (GifUpdates::iterator it = gif_stack.begin(); it != gif_stack.end(); ++it) {
GifUpdate *gif = *it;
int start = (gif->y - top.y)*width*3 + (gif->x - top.x)*3;
unsigned char *gifdatap = gif->data;
for (int i = 0; i < gif->h; i++) {
unsigned char *datap = &data[start + i*width*3];
for (int j = 0; j < gif->w; j++) {
*datap++ = *(gifdatap + 2);
*datap++ = *(gifdatap + 1);
*datap++ = *gifdatap;
gifdatap += (buf_type == BUF_BGRA) ? 4 : 3;
}
}
}
break;

default:
throw "Unexpected buf_type in DynamicGifStack::GifEncode";
}
construct_gif_data(data, top);

try {
GifEncoder gif_encoder(data, width, height, BUF_RGB);
Expand Down Expand Up @@ -230,11 +237,135 @@ DynamicGifStack::Dimensions(const Arguments &args)
}

Handle<Value>
DynamicGifStack::GifEncode(const Arguments &args)
DynamicGifStack::GifEncodeSync(const Arguments &args)
{
HandleScope scope;

DynamicGifStack *gif_stack = ObjectWrap::Unwrap<DynamicGifStack>(args.This());
return scope.Close(gif_stack->GifEncode());
return scope.Close(gif_stack->GifEncodeSync());
}

int
DynamicGifStack::EIO_GifEncode(eio_req *req)
{
encode_request *enc_req = (encode_request *)req->data;
DynamicGifStack *gif = (DynamicGifStack *)enc_req->gif_obj;

std::pair<Point, Point> optimal = gif->optimal_dimension();
Point top = optimal.first, bot = optimal.second;

gif->offset = top;
gif->width = bot.x - top.x;
gif->height = bot.y - top.y;

unsigned char *data = (unsigned char*)malloc(sizeof(*data)*gif->width*gif->height*3);
if (!data) {
enc_req->error = strdup("malloc failed in DynamicGifStack::EIO_GifEncode.");
return 0;
}

unsigned char *datap = data;
for (int i = 0; i < gif->width*gif->height; i++) {
*datap++ = gif->transparency_color.r;
*datap++ = gif->transparency_color.g;
*datap++ = gif->transparency_color.b;
}

gif->construct_gif_data(data, top);

buffer_type pbt = (gif->buf_type == BUF_BGR || gif->buf_type == BUF_BGRA) ?
BUF_BGRA : BUF_RGBA;

try {
GifEncoder gif_encoder(data, gif->width, gif->height, BUF_RGB);
gif_encoder.set_transparency_color(gif->transparency_color);
gif_encoder.encode();
free(data);
enc_req->gif_len = gif_encoder.get_gif_len();
enc_req->gif = (char *)malloc(sizeof(*enc_req->gif)*enc_req->gif_len);
if (!enc_req->gif) {
enc_req->error = strdup("malloc in DynamicGifStack::EIO_GifEncode failed.");
return 0;
}
else {
memcpy(enc_req->gif, gif_encoder.get_gif(), enc_req->gif_len);
}
}
catch (const char *err) {
enc_req->error = strdup(err);
}

return 0;
}

int
DynamicGifStack::EIO_GifEncodeAfter(eio_req *req)
{
HandleScope scope;

ev_unref(EV_DEFAULT_UC);
encode_request *enc_req = (encode_request *)req->data;
DynamicGifStack *gif = (DynamicGifStack *)enc_req->gif_obj;

Handle<Value> argv[3];

if (enc_req->error) {
argv[0] = Undefined();
argv[1] = Undefined();
argv[2] = ErrorException(enc_req->error);
}
else {
argv[0] = Local<Value>::New(Encode(enc_req->gif, enc_req->gif_len, BINARY));
argv[1] = gif->Dimensions();
argv[2] = Undefined();
}

TryCatch try_catch; // don't quite see the necessity of this

enc_req->callback->Call(Context::GetCurrent()->Global(), 3, argv);

if (try_catch.HasCaught())
FatalException(try_catch);

enc_req->callback.Dispose();
free(enc_req->gif);
free(enc_req->error);

gif->Unref();
free(enc_req);

return 0;
}

Handle<Value>
DynamicGifStack::GifEncodeAsync(const Arguments &args)
{
HandleScope scope;

if (args.Length() != 1)
return VException("One argument required - callback function.");

if (!args[0]->IsFunction())
return VException("First argument must be a function.");

Local<Function> callback = Local<Function>::Cast(args[0]);
DynamicGifStack *gif = ObjectWrap::Unwrap<DynamicGifStack>(args.This());

encode_request *enc_req = (encode_request *)malloc(sizeof(*enc_req));
if (!enc_req)
return VException("malloc in DynamicGifStack::GifEncodeAsync failed.");

enc_req->callback = Persistent<Function>::New(callback);
enc_req->gif_obj = gif;
enc_req->gif = NULL;
enc_req->gif_len = 0;
enc_req->error = NULL;

eio_custom(EIO_GifEncode, EIO_PRI_DEFAULT, EIO_GifEncodeAfter, enc_req);

ev_ref(EV_DEFAULT_UC);
gif->Ref();

return Undefined();
}

11 changes: 8 additions & 3 deletions src/dynamic_gif_stack.h
Expand Up @@ -37,7 +37,11 @@ class DynamicGifStack : public node::ObjectWrap {
buffer_type buf_type;
Color transparency_color;

std::pair<Point, Point> OptimalDimension();
std::pair<Point, Point> optimal_dimension();

static int EIO_GifEncode(eio_req *req);
static int EIO_GifEncodeAfter(eio_req *req);
void construct_gif_data(unsigned char *data, Point &top);

public:
static void Initialize(v8::Handle<v8::Object> target);
Expand All @@ -46,12 +50,13 @@ class DynamicGifStack : public node::ObjectWrap {

v8::Handle<v8::Value> Push(node::Buffer *buf, int x, int y, int w, int h);
v8::Handle<v8::Value> Dimensions();
v8::Handle<v8::Value> GifEncode();
v8::Handle<v8::Value> GifEncodeSync();

static v8::Handle<v8::Value> New(const v8::Arguments &args);
static v8::Handle<v8::Value> Push(const v8::Arguments &args);
static v8::Handle<v8::Value> Dimensions(const v8::Arguments &args);
static v8::Handle<v8::Value> GifEncode(const v8::Arguments &args);
static v8::Handle<v8::Value> GifEncodeSync(const v8::Arguments &args);
static v8::Handle<v8::Value> GifEncodeAsync(const v8::Arguments &args);
};

#endif
Expand Down
8 changes: 0 additions & 8 deletions src/gif.cpp
Expand Up @@ -135,14 +135,6 @@ Gif::SetTransparencyColor(const Arguments &args)
return Undefined();
}

struct encode_request {
v8::Persistent<v8::Function> callback;
void *gif_obj;
char *gif;
int gif_len;
char *error;
};

int
Gif::EIO_GifEncode(eio_req *req)
{
Expand Down
31 changes: 31 additions & 0 deletions tests/dynamic-gif-stack-async.js
@@ -0,0 +1,31 @@
var GifLib = require('gif');
var fs = require('fs');
var sys = require('sys');
var Buffer = require('buffer').Buffer;

var gifStack = new GifLib.DynamicGifStack('rgba');
//gifStack.setTransparencyColor(0x00, 0x00, 0x00);

function rectDim(fileName) {
var m = fileName.match(/^\d+-rgba-(\d+)-(\d+)-(\d+)-(\d+).dat$/);
var dim = [m[1], m[2], m[3], m[4]].map(function (n) {
return parseInt(n, 10);
});
return { x: dim[0], y: dim[1], w: dim[2], h: dim[3] }
}

var files = fs.readdirSync('./push-data');

files.forEach(function(file) {
var dim = rectDim(file);
var rgba = fs.readFileSync('./push-data/' + file);
gifStack.push(rgba, dim.x, dim.y, dim.w, dim.h);
});

gifStack.encode(function (data, dims) {
fs.writeFileSync('dynamic-async.gif', data, 'binary');

sys.log("GIF located at (" + dims.x + "," + dims.y + ") with width " +
dims.width + " and height " + dims.height);
});

2 changes: 1 addition & 1 deletion tests/dynamic-gif-stack.js
Expand Up @@ -22,7 +22,7 @@ files.forEach(function(file) {
gifStack.push(rgba, dim.x, dim.y, dim.w, dim.h);
});

fs.writeFileSync('dynamic.gif', gifStack.encode(), 'binary');
fs.writeFileSync('dynamic.gif', gifStack.encodeSync(), 'binary');

var dims = gifStack.dimensions();

Expand Down

0 comments on commit 726cf38

Please sign in to comment.