Skip to content

Commit

Permalink
transparent gifs now work, both stacked and regular
Browse files Browse the repository at this point in the history
  • Loading branch information
pkrumins committed Jul 28, 2010
1 parent e11d738 commit 0f9a4c6
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 12 deletions.
8 changes: 7 additions & 1 deletion readme.txt
Expand Up @@ -24,11 +24,17 @@ The third argument is integer height of the image.
The fourth argument is the quality of output image.
The fifth argument is buffer type, 'rgb', 'bgr', 'rgba' or 'bgra'.

You can set the transparent color for the image by using:

gif.setTransparencyColor(red, green, blue);

Once you have constructed Gif object, call `encode` method to encode and
produce GIF image.

var image = gif.encode();



See `tests/gif.js` for a concrete example.


Expand All @@ -44,7 +50,7 @@ dynamically computed. To create it, do:
The `buffer_type` again is 'rgb', 'bgr', 'rgba' or 'bgra', depending on what type
of buffers you're gonna push to `dynamic_gif`.

It provides three methods - `push`, `encode` and `dimensions`.
It provides several methods - `push`, `encode`, `dimensions`, `setTransparencyColor`.

The `push` method pushes the buffer to position `x`, `y` with `width`, `height`.

Expand Down
8 changes: 8 additions & 0 deletions src/common.h
Expand Up @@ -19,6 +19,14 @@ struct Rect {
bool isNull() { return x == 0 && y == 0 && w == 0 && h == 0; }
};

struct Color {
bool color_present; // true if this is really a color
unsigned char r, g, b;
Color(unsigned char rr, unsigned char gg, unsigned char bb, bool ccolor_present=true) :
r(rr), g(gg), b(bb), color_present(ccolor_present) {}
Color() : color_present(false) {}
};

bool str_eq(const char *s1, const char *s2);
unsigned char *rgba_to_rgb(const unsigned char *rgba, int rgba_size);
unsigned char *bgra_to_rgb(const unsigned char *rgba, int bgra_size);
Expand Down
103 changes: 102 additions & 1 deletion src/dynamic_gif_stack.cc
@@ -1,3 +1,4 @@
#include "common.h"
#include "gif_encoder.h"
#include "dynamic_gif_stack.h"

Expand Down Expand Up @@ -33,6 +34,7 @@ DynamicGifStack::Initialize(Handle<Object> target)
NODE_SET_PROTOTYPE_METHOD(t, "push", Push);
NODE_SET_PROTOTYPE_METHOD(t, "encode", GifEncode);
NODE_SET_PROTOTYPE_METHOD(t, "dimensions", Dimensions);
NODE_SET_PROTOTYPE_METHOD(t, "setTransparencyColor", SetTransparencyColor);
target->Set(String::NewSymbol("DynamicGifStack"), t->GetFunction());
}

Expand Down Expand Up @@ -73,14 +75,40 @@ DynamicGifStack::GifEncode()

unsigned char *data = (unsigned char*)malloc(sizeof(*data)*width*height*3);
if (!data) return VException("malloc failed in DynamicGifStack::GifEncode");
memset(data, 0xFF, width*height*3);

GifEncoder gif_encoder(data, width, height, BUF_RGB);
bool user_set_transparency = true;
if (!transparency_color.color_present) {
user_set_transparency = false;
transparency_color = Color(0xD8, 0xA8, 0x10);
gif_encoder.set_transparency_color(transparency_color);
}
unsigned char *datap = data;
for (int i = 0; i < width*height*3; i+=3) {
*datap++ = transparency_color.r;
*datap++ = transparency_color.g;
*datap++ = transparency_color.b;
}

if (buf_type == BUF_RGB) {
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;
for (int i = 0; i < gif->h; i++) {
for (int j = 0; j < 3*gif->w; j+=3) {
if (!user_set_transparency) {
if (gif->data[i*gif->w*3 + j] == transparency_color.r &&
gif->data[i*gif->w*3 + j + 1] == transparency_color.g &&
gif->data[i*gif->w*3 + j + 2] == transparency_color.b)
{
if (gif->data[i*gif->w*3 + j + 2] < 0xFF) {
gif->data[i*gif->w*3 + j + 2]++;
}
else {
gif->data[i*gif->w*3 + j + 2]--;
}
}
}
data[start + i*width*3 + j] = gif->data[i*gif->w*3 + j];
data[start + i*width*3 + j + 1] = gif->data[i*gif->w*3 + j + 1];
data[start + i*width*3 + j + 2] = gif->data[i*gif->w*3 + j + 2];
Expand All @@ -94,6 +122,19 @@ DynamicGifStack::GifEncode()
int start = (gif->y - top.y)*width*3 + (gif->x - top.x)*3;
for (int i = 0; i < gif->h; i++) {
for (int j = 0; j < 3*gif->w; j+=3) {
if (!user_set_transparency) {
if (gif->data[i*gif->w*3 + j + 2] == transparency_color.r &&
gif->data[i*gif->w*3 + j + 1] == transparency_color.g &&
gif->data[i*gif->w*3 + j] == transparency_color.b)
{
if (gif->data[i*gif->w*3 + j] < 0xFF) {
gif->data[i*gif->w*3 + j]++;
}
else {
gif->data[i*gif->w*3 + j]--;
}
}
}
data[start + i*width*3 + j] = gif->data[i*gif->w*3 + j + 2];
data[start + i*width*3 + j + 1] = gif->data[i*gif->w*3 + j + 1];
data[start + i*width*3 + j + 2] = gif->data[i*gif->w*3 + j];
Expand All @@ -107,6 +148,19 @@ DynamicGifStack::GifEncode()
int start = (gif->y - top.y)*width*3 + (gif->x - top.x)*3;
for (int i = 0; i < gif->h; i++) {
for (int j = 0, k = 0; j < 3*gif->w; j+=3, k+=4) {
if (!user_set_transparency) {
if (gif->data[i*gif->w*4 + k] == transparency_color.r &&
gif->data[i*gif->w*4 + k + 1] == transparency_color.g &&
gif->data[i*gif->w*4 + k + 2] == transparency_color.b)
{
if (gif->data[i*gif->w*4 + k + 2] < 0xFF) {
gif->data[i*gif->w*4 + k + 2]++;
}
else {
gif->data[i*gif->w*3 + k + 2]--;
}
}
}
data[start + i*width*3 + j] = gif->data[i*gif->w*4 + k];
data[start + i*width*3 + j + 1] = gif->data[i*gif->w*4 + k + 1];
data[start + i*width*3 + j + 2] = gif->data[i*gif->w*4 + k + 2];
Expand All @@ -120,6 +174,19 @@ DynamicGifStack::GifEncode()
int start = (gif->y - top.y)*width*3 + (gif->x - top.x)*3;
for (int i = 0; i < gif->h; i++) {
for (int j = 0, k = 0; j < 3*gif->w; j+=3, k+=4) {
if (!user_set_transparency) {
if (gif->data[i*gif->w*4 + k + 2] == transparency_color.r &&
gif->data[i*gif->w*4 + k + 1] == transparency_color.g &&
gif->data[i*gif->w*4 + k] == transparency_color.b)
{
if (gif->data[i*gif->w*4 + k] < 0xFF) {
gif->data[i*gif->w*4 + k]++;
}
else {
gif->data[i*gif->w*3 + k]--;
}
}
}
data[start + i*width*3 + j] = gif->data[i*gif->w*4 + k + 2];
data[start + i*width*3 + j + 1] = gif->data[i*gif->w*4 + k + 1];
data[start + i*width*3 + j + 2] = gif->data[i*gif->w*4 + k];
Expand All @@ -130,6 +197,9 @@ DynamicGifStack::GifEncode()

try {
GifEncoder gif_encoder(data, width, height, BUF_RGB);
if (transparency_color.color_present) {
gif_encoder.set_transparency_color(transparency_color);
}
gif_encoder.encode();
free(data);
return scope.Close(
Expand All @@ -141,6 +211,12 @@ DynamicGifStack::GifEncode()
}
}

void
DynamicGifStack::SetTransparencyColor(unsigned char r, unsigned char g, unsigned char b)
{
transparency_color = Color(r, g, b, true);
}

Handle<Value>
DynamicGifStack::Dimensions()
{
Expand Down Expand Up @@ -242,3 +318,28 @@ DynamicGifStack::GifEncode(const Arguments &args)
return scope.Close(gif_stack->GifEncode());
}

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

if (args.Length() != 3)
return VException("Three arguments required - r, g, b");

if (!args[0]->IsInt32())
return VException("First argument must be integer red.");
if (!args[1]->IsInt32())
return VException("Second argument must be integer green.");
if (!args[2]->IsInt32())
return VException("Third argument must be integer blue.");

unsigned char r = args[0]->Int32Value();
unsigned char g = args[1]->Int32Value();
unsigned char b = args[2]->Int32Value();

DynamicGifStack *gif = ObjectWrap::Unwrap<DynamicGifStack>(args.This());
gif->SetTransparencyColor(r, g, b);

return Undefined();
}

3 changes: 3 additions & 0 deletions src/dynamic_gif_stack.h
Expand Up @@ -35,6 +35,7 @@ class DynamicGifStack : public node::ObjectWrap {
Point offset;
int width, height;
buffer_type buf_type;
Color transparency_color;

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

Expand All @@ -46,11 +47,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();
void SetTransparencyColor(unsigned char r, unsigned char g, unsigned char b);

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> SetTransparencyColor(const v8::Arguments &args);
};

#endif
Expand Down
36 changes: 36 additions & 0 deletions src/gif.cc
Expand Up @@ -13,6 +13,7 @@ Gif::Initialize(Handle<Object> target)
Local<FunctionTemplate> t = FunctionTemplate::New(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
NODE_SET_PROTOTYPE_METHOD(t, "encode", GifEncode);
NODE_SET_PROTOTYPE_METHOD(t, "setTransparencyColor", SetTransparencyColor);
target->Set(String::NewSymbol("Gif"), t->GetFunction());
}

Expand All @@ -28,6 +29,9 @@ Gif::GifEncode()
GifEncoder encoder(
(unsigned char *)data->data(), width, height, buf_type
);
if (transparency_color.color_present) {
encoder.set_transparency_color(transparency_color);
}
encoder.encode();
return scope.Close(
Encode((char *)encoder.get_gif(), encoder.get_gif_len(), BINARY)
Expand All @@ -38,6 +42,12 @@ Gif::GifEncode()
}
}

void
Gif::SetTransparencyColor(unsigned char r, unsigned char g, unsigned char b)
{
transparency_color = Color(r, g, b, true);
}

Handle<Value>
Gif::New(const Arguments &args)
{
Expand Down Expand Up @@ -94,7 +104,33 @@ Handle<Value>
Gif::GifEncode(const Arguments &args)
{
HandleScope scope;

Gif *gif = ObjectWrap::Unwrap<Gif>(args.This());
return gif->GifEncode();
}

Handle<Value>
Gif::SetTransparencyColor(const Arguments &args)
{
HandleScope scope;

if (args.Length() != 3)
return VException("Three arguments required - r, g, b");

if (!args[0]->IsInt32())
return VException("First argument must be integer red.");
if (!args[1]->IsInt32())
return VException("Second argument must be integer green.");
if (!args[2]->IsInt32())
return VException("Third argument must be integer blue.");

unsigned char r = args[0]->Int32Value();
unsigned char g = args[1]->Int32Value();
unsigned char b = args[2]->Int32Value();

Gif *gif = ObjectWrap::Unwrap<Gif>(args.This());
gif->SetTransparencyColor(r, g, b);

return Undefined();
}

3 changes: 3 additions & 0 deletions src/gif.h
Expand Up @@ -10,14 +10,17 @@ class Gif : public node::ObjectWrap {
int width, height;
node::Buffer *data;
buffer_type buf_type;
Color transparency_color;

public:
static void Initialize(v8::Handle<v8::Object> target);
Gif(node::Buffer *ddata, int wwidth, int hheight, buffer_type bbuf_type);
v8::Handle<v8::Value> GifEncode();
void SetTransparencyColor(unsigned char r, unsigned char g, unsigned char b);

static v8::Handle<v8::Value> New(const v8::Arguments &args);
static v8::Handle<v8::Value> GifEncode(const v8::Arguments &args);
static v8::Handle<v8::Value> SetTransparencyColor(const v8::Arguments &args);
};

#endif
Expand Down
45 changes: 37 additions & 8 deletions src/gif_encoder.cc
Expand Up @@ -122,14 +122,27 @@ GifEncoder::encode()
throw "EGifPutScreenDesc in GifEncoder::encode failed";
}

/*
char moo[] = {
1, // enable transparency
0, 0, // no time delay,
0xFF // transparency color index
};
EGifPutExtension(gif_file, GRAPHICS_EXT_FUNC_CODE, 4, moo);
*/
if (transparent_color.color_present) {
// find color's index in color map
for (int i = 0; i < color_map_size; i++) {
/*
printf("%d: %02x %02x %02x\n", i, output_color_map->Colors[i].Red,
output_color_map->Colors[i].Green, output_color_map->Colors[i].Blue);
*/
if (output_color_map->Colors[i].Red == transparent_color.r &&
output_color_map->Colors[i].Green == transparent_color.g &&
output_color_map->Colors[i].Blue == transparent_color.b)
{
char extension[] = {
1, // enable transparency
0, 0, // no time delay
i // transparency color index
};
EGifPutExtension(gif_file, GRAPHICS_EXT_FUNC_CODE, 4, extension);
break;
}
}
}

if (EGifPutImageDesc(gif_file, 0, 0, width, height, FALSE, NULL)
== GIF_ERROR)
Expand All @@ -156,11 +169,27 @@ GifEncoder::encode()
EGifCloseFile(gif_file);
}

void
GifEncoder::set_transparency_color(unsigned char r, unsigned char g, unsigned char b)
{
transparent_color.r = r;
transparent_color.r = g;
transparent_color.r = b;
transparent_color.color_present = true;
}

void
GifEncoder::set_transparency_color(const Color &c)
{
transparent_color = c;
}

const unsigned char *
GifEncoder::get_gif() const
{
return gif.gif;
}

const int
GifEncoder::get_gif_len() const
{
Expand Down

0 comments on commit 0f9a4c6

Please sign in to comment.