Jon Olick
  • Home
  • Presentations
  • Publications
  • Patents
  • Videos
  • Code
  • Games
  • Art
  • Blogspot
  • Twitter
  • WikiCoder
  • Contact
  • Links
  • Home
  • Presentations
  • Publications
  • Patents
  • Videos
  • Code
  • Games
  • Art
  • Blogspot
  • Twitter
  • WikiCoder
  • Contact
  • Links

GIF Writer

3/11/2015

9 Comments

 
Picture
jo_gif.cpp
File Size: 13 kb
File Type: cpp
Download File

This post is to announce my stb style 398 LoC animated GIF writer. 

The primary features are
1) 398 lines of code
2) No memory allocations
3) Public Domain
4) NeuQuant based quantizer

I think there is still a lot left that could be done here with it, but I feel its a good version 1.0.

Namely, missing alpha support. Easy to do, but needs to be done. Also some improvements to the color quantization. After going through NeuQuant I found quite a few things which I feel can be improved which I hope I have time to get to. 
Basic Usage:

// 4 component. RGBX format, where X is unused 
char *frame = new char[128*128*4]; 
jo_gif_t gif = jo_gif_start("foo.gif", 128, 128, 0, 32);    
jo_gif_frame(&gif, frame, 4, false); // frame 1
jo_gif_frame(&gif, frame, 4, false); // frame 2
jo_gif_frame(&gif, frame, 4, false); // frame 3, ...
jo_gif_end(&gif);


Where frame holds the RGBA pixels for a frame. You call start, then frame a bunch of times then end. 

Here is a more interesting example used to create the image above. :) Enjoy!

void hsv2rgb(float hsv[3], float rgb[3]) {

    if(hsv[1] <= 0.0) {       // < is bogus, just shuts up warnings
        rgb[0] = rgb[1] = rgb[2] = hsv[2];
        return;
    }
    float hh = hsv[0];
    if(hh >= 360) {
        hh = 0;
    }
    hh /= 60;
    long i = (long)hh;
    float ff = hh - i;
    float p = hsv[2] * (1.f - hsv[1]);
    float q = hsv[2] * (1.f - (hsv[1] * ff));
    float t = hsv[2] * (1.f - (hsv[1] * (1.f - ff)));
    switch(i) {
        case 0: rgb[0] = hsv[2]; rgb[1] = t; rgb[2] = p; break;
        case 1: rgb[0] = q; rgb[1] = hsv[2]; rgb[2] = p; break;
        case 2: rgb[0] = p; rgb[1] = hsv[2]; rgb[2] = t; break;
        case 3: rgb[0] = p; rgb[1] = q; rgb[2] = hsv[2]; break;
        case 4: rgb[0] = t; rgb[1] = p; rgb[2] = hsv[2]; break;
        case 5: rgb[0] = hsv[2]; rgb[1] = p; rgb[2] = q; break;
    }
}

int main(int argc, char **argv) {
    const int w = 256, h = 256;
    jo_gif_t gif = jo_gif_start("foo.gif", w,h,0,32);
    for(int frame = 0; frame < 360/4; ++frame) {
        unsigned char tmp[w*h*4];
        double coordX = -0.74529;
        double coordY = 0.113075;
        double zoom = 1.5E-4*0.5;
        for(int y = 0; y < h; ++y) {
            for(int x = 0; x < w; ++x) {
                double x0 = (x/double(w) * 3.5 - 2.5) * zoom + coordX;
                double y0 = (y/double(h) * 3.0 - 1.5) * zoom + coordY;
                double xx = 0, yy = 0;
                int iter = 0;
                while(xx*xx + yy*yy < 2*2 && iter++ < 4096) {
                    double xtmp = xx*xx - yy*yy + x0;
                    yy = 2*xx*yy + y0;
                    xx = xtmp;
                }
                int i = y*w*4+x*4;
                int iter2 = iter + 360 - frame*4;
                float hsv[3] = { float(iter2%360), 1, iter2 < 4096 ? 1.f : 0.f };
                float rgb[3];
                hsv2rgb(hsv, rgb);
                tmp[i+0] = (unsigned char)(rgb[0] * 255);
                tmp[i+1] = (unsigned char)(rgb[1] * 255);
                tmp[i+2] = (unsigned char)(rgb[2] * 255);
                tmp[i+3] = 255;
            }
        }
        jo_gif_frame(&gif, tmp, 4, false);
    }
    jo_gif_end(&gif);
}

9 Comments
rxi
10/23/2015 12:13:42 pm

I was curious to whether you were planning on hosting the project on something like github? As it is it, assuming I haven't overlooked something, it seems difficult for one to report issues or contribute bug fixes, though I appreciate that this might be intentional.

Really liking the library so far -- thanks!

Reply
Jon Olick
3/13/2018 12:46:40 pm

I have it on a local git, but I haven't yet made an official github.com repo yet. on the TODO,

Reply
kmatze
5/17/2017 03:06:50 am

Hi Jon, I've found your formidable tiny gif library. I use These with tcltk und wrote a tcl package in c. That's works fine.
One question i have to mpeg_writer:
The RGBX input stream in gif_writer is the same like as in jo_mpeg? I tried this, but I get a pixeled mpeg Video with wrong Colors. I think the compression algorithm in mpeg to do this.
Thanks and greeting - kmatze

Reply
Jon Olick
3/13/2018 12:45:48 pm

Should be the same, yes. Send me an email (via the contact form). I can take a look!

Reply
Robinson Bruginski
11/1/2017 09:49:30 am

I made some changes to work with a different quantiser.
For present color palete. the diff is bellow:

Also changing bool to char will make it compile in any C compiler.



diff -r
44c44
< extern void jo_gif_frame(jo_gif_t *gif, unsigned char *rgba, short delayCsec, bool localPalette);
---
> extern void jo_gif_frame(jo_gif_t *gif, unsigned char *rgba, short delayCsec, char localPalette);
200a201,229
> // Based on Last Color
> //this one is better for computer generated graphics. , when no dither will be used.
> static void suns_gif_quantize(unsigned char *rgba, int rgbaSize, int sample, unsigned char *map, int numColors) {
> char found=0;
> int colors=0;
> unsigned char r,g,b;
> //search color
> for (int x = 0; x < rgbaSize;) {
> r=rgba[x++];
> g=rgba[x++];
> b=rgba[x++];
> x++;
> for (int i = 0; i < numColors; i++) {
> if(map[i*3+0] == r && map[i*3+1] == g && map[i*3+2] == b) {
> //color found
> found=1;
> }
> }
> if(found==0) {
> //ADD new color to table
> if(colors>numColors) colors=0; //if full wrap around
> map[colors*3+0]=r; //fill palete
> map[colors*3+1]=g;
> map[colors*3+2]=b;
> colors++;
> //printf("NEWCOLOR R=%d G=%d B=%d\n",r,g,b);
> }
> found=0;
> }
200a231
> }
309c342
< void jo_gif_frame(jo_gif_t *gif, unsigned char * rgba, short delayCsec, bool localPalette) {
---
> void jo_gif_frame(jo_gif_t *gif, unsigned char * rgba, short delayCsec, char localPalette) {
322c355,357
<
---
> if(localPalette==2) {
> suns_gif_quantize(rgba, size*4, 1, palette, gif->numColors);
> }
375c410
< fwrite("\x2c\x00\x00\x00\x00", 5, 1, gif->fp); // header, x,y
---
> fwrite("\x2c\x00\x00\x00\x00", 5, 1, gif->fp); // header, x,y //todo check what changed from the previous frame and only save the diference

Reply
Jon Olick
3/13/2018 12:47:15 pm

I'll try it out and maybe see about integrating this official!

Reply
Tony Wang link
3/20/2018 12:36:48 am

Would be nice if could add a `paletteFilled` parameter to indicate the palette is already filled, instead of using `jo_gif_quantize` to generate it:

void jo_gif_frame(jo_gif_t *gif, unsigned char * rgba, short delayCsec, bool localPalette, bool paletteFilled) {
...
unsigned char localPalTbl[0x300];
unsigned char *palette = gif->frame == 0 || !localPalette ? gif->palette : localPalTbl;
if(!paletteFilled && (gif->frame == 0 || localPalette)) {
jo_gif_quantize(rgba, size*4, 1, palette, gif->numColors);
}
...
}

I also look forward to make contribution in a public repo.

Reply
kdrnic link
12/6/2020 04:16:12 pm

I have made an improved derivative, although much increased in complexity, and wrote about it at https://kdrnic.github.io/index_gifcap.html

Reply
Johnny link
12/23/2020 12:05:27 pm

Hello nice blog

Reply



Leave a Reply.

    Archives

    November 2021
    October 2021
    September 2021
    April 2021
    February 2021
    January 2021
    December 2020
    June 2020
    May 2020
    April 2020
    November 2019
    April 2019
    August 2018
    April 2017
    March 2017
    January 2017
    November 2016
    October 2016
    September 2016
    January 2016
    March 2015
    August 2013
    July 2013
    December 2012

    Categories

    All
    Compression
    Dxt

    RSS Feed