hidefromkgb / gif_load

A slim, fast and header-only GIF loader written in C
80 stars 6 forks source link

Support GIF's with no colormap #10

Open wcout opened 5 years ago

wcout commented 5 years ago

This is just a request for a feature you may want to support:

The standard says, that both global and local color tables are no required blocks. In that case the application can use a default colormap.

Currently gif_load.h does not parse such images.

I have made some changes in my copy to support it:

(1) Continue parsing when clrs is 0. (2) Give access to the palette definition in the GIF_WHDR structure. (3) Return the value of the GIF header field color resultion. As I read it, it can be used in such a case to determine how many colors the palette has in order to supply a suitable replacement.

(1) and (2) would be nice to have, (3) could in principle also be done in the application, because the value is at a fixed position in the file.

These are my changes:

diff --git a/gif_load.h b/gif_load.h
index 12cc402..99324bb 100644
--- a/gif_load.h
+++ b/gif_load.h
@@ -48,12 +48,13 @@ extern "C" {
 #pragma pack(push, 1)
 struct GIF_WHDR {                /** ======== frame writer info: ======== **/
     long xdim, ydim, clrs,       /** global dimensions, palette size      **/
+         cres,                   /** color resolution: 2^(cres+1) = #clrs **/
          bkgd, tran,             /** background index, transparent index  **/
          intr, mode,             /** interlace flag, frame blending mode  **/
          frxd, fryd, frxo, fryo, /** current frame dimensions and offset  **/
          time, ifrm, nfrm;       /** delay, frame number, frame count     **/
     uint8_t *bptr;               /** frame pixel indices or metadata      **/
-    struct {                     /** [==== GIF RGB palette element: ====] **/
+    struct CPAL {                /** [==== GIF RGB palette element: ====] **/
         uint8_t R, G, B;         /** [color values - red, green, blue   ] **/
     } *cpal;                     /** current palette                      **/
 };
@@ -225,6 +226,7 @@ GIF_EXTR long GIF_Load(void *data, long size,
     || ((buff[4] != 55) && (buff[4] != 57)) || (buff[5] != 97) || !gwfr)
         return 0;

+    whdr.cres = (ghdr->flgs >> 4) & 7;
     buff = (uint8_t*)(ghdr + 1) /** skipping the global header and palette **/
          + _GIF_LoadHeader(ghdr->flgs, 0, 0, 0, 0, 0L) * 3L;
     if ((size -= buff - (uint8_t*)ghdr) <= 0)
@@ -238,7 +240,7 @@ GIF_EXTR long GIF_Load(void *data, long size,
         if (desc == GIF_FHDM) {
             fhdr = (struct GIF_FHDR*)whdr.bptr;
             if (_GIF_LoadHeader(ghdr->flgs, &whdr.bptr, (void**)&whdr.cpal,
-                                fhdr->flgs, &blen, sizeof(*fhdr)) <= 0)
+                                fhdr->flgs, &blen, sizeof(*fhdr)) < 0)
                 break;
             whdr.frxd = _GIF_SWAP(fhdr->frxd);
             whdr.fryd = _GIF_SWAP(fhdr->fryd);
@@ -258,7 +260,7 @@ GIF_EXTR long GIF_Load(void *data, long size,
             *(void**)&whdr.cpal = (void*)(ghdr + 1); /** interlaced? -^ **/
             whdr.clrs = _GIF_LoadHeader(ghdr->flgs, &buff, (void**)&whdr.cpal,
                                         fhdr->flgs, &size, sizeof(*fhdr));
-            if ((skip <= ++whdr.ifrm) && ((whdr.clrs <= 0)
+            if ((skip <= ++whdr.ifrm) && ((whdr.clrs < 0)
             ||  (_GIF_LoadFrame(&buff, &size,
                                  whdr.bptr, whdr.bptr + blen) < 0)))
                 size = -(whdr.ifrm--) - 1; /** failed to load the frame **/

In my program I'm doing something like that, when there are no colors:


void frame_cb(void *data_, GIF_WHDR *whdr_) {
  // ...
  if (!whdr_.clrs) {
    // no colors: use default table
    static struct GIF_WHDR::CPAL defClrs[256];
    whdr_.clrs = 1 << (whdr_.cres + 1);
    whdr_.cpal = defClrs;
    memset(defClrs, 0, sizeof(defClrs)); // Note: also sets first color to black
    defClrs[1].R = defClrs[1].G = defClrs[1].B = 0xff; // white
    for (int i = 2; i < whdr_.clrs; i++)
      defClrs[i].R = defClrs[i].G = defClrs[i].B = (uchar)(255 * i / (whdr_.clrs - 1));
  }
  // ...
}
hidefromkgb commented 5 years ago

I can modify the loader so it won`t panic on empty palettes, just boldly returning 0 in cpal. The additional field seems superfluous, since the right field to hold the number of colors requested by the GIF is definitely clrs.

wcout commented 5 years ago

That would also be an option. And you would set if from color resolution? The naming of the cpal struct would be required also.