login

<     >

2018-01-21 16:45:48 (UTC-02:00)

Marcel Rodrigues <marcelgmr@gmail.com>

Fill blank sections of README.

diff --git a/README b/README
index 650fd48..70ae380 100644
--- a/README
+++ b/README
@@ -2,31 +2,227 @@ GIF decoder
 ===========
 
 This is a small C library that can be used to read GIF files.
-It's a work in progress...
 
 Features
 --------
 
-  * TODO
+  * support for all standard GIF features
+  * support for Netscape Application Extension (looping information)
+  * other extensions may be easily supported via user hooks
+  * small and portable: less than 500 lines of C99
+  * public domain
 
 
 Limitations
 -----------
 
-  * TODO
+  * no direct support for the plain text extension (rarely used)
 
 
 Documentation
 -------------
 
-TODO
+0. Essential GIF concepts
+
+GIF animations are stored in files as a series of palette-based
+compressed frames.
+
+In order to display the animation, a program must lay the frames on top
+of a fixed-size canvas, one after the other. Each frame has a size,
+position and duration. Each frame can have its own palette or use a
+global palette defined in the beginning of the file.
+
+In order to properly use extension hooks, it's necessary to understand
+how GIF files store variable-sized data. A GIF block of variable size is
+a sequence of sub-blocks. The first byte in a sub-block indicates the
+number of data bytes to follow. The end of the block is indicated by an
+empty sub-block: one byte of value 0x00. For instance, a data block of
+600 bytes is stored as 4 sub-blocks:
+  255, <255 data bytes>, 255, <255 data bytes>, 90, <90 data bytes>, 0
+
+1. Opening and closing a GIF file
+
+The function `gd_open_gif()` tries to open a GIF file for reading.
+
+    gd_GIF *gd_open_gif(const char *fname);
+
+If this function fails, it returns NULL.
+
+If `gd_open_gif()` succeeds, it returns a GIF handler (`gd_GIF *`).
+The GIF handler can be passed to the other gifdec functions to
+decode GIF metadata and frames.
+
+To close the GIF file and free memory after it has been decoded, the
+function `gd_close_gif()` must be called.
+
+    void gd_close_gif(gd_GIF *gif);
+
+2. Reading GIF attributes
+
+Once a GIF file has been successfully opened, some basic information can
+be read directly from the GIF handler:
+
+    gd_GIF *gif = gd_open_gif("animation.gif");
+    printf("canvas size: %ux%u\n", gif->width, gif->height);
+    printf("number of colors: %d\n", gif->palette->size);
+
+3. Reading frames
+
+The function `gd_get_frame()` decodes one frame from the GIF file.
+
+    int gd_get_frame(gd_GIF *gif);
+
+This function returns 0 if there are no more frames to read.
+
+The decoded frame is stored in `gif->frame`, which is a buffer of size
+`gif->width * gif->height`, in bytes. Each byte value is an index to the
+palette at `gif->palette`.
+
+Since GIF files often only store the rectangular region of a frame that
+changed from the previous frame, this function will only update the
+bytes in `gif->frame` that are in that region. For GIF files that only
+use the global palette, the whole state of the canvas is stored in
+`gif->frame` at all times, in the form of an indexed color image.
+However, when local palettes are used, it's not enough to keep color
+indices from previous frames. The color RGB values themselves need to
+be stored.
+
+For this reason, in order to get the whole state of the canvas after a
+new frame has been read, it's necessary to call the function
+`gd_render_frame()`, which writes all pixels to a given buffer.
+
+    void gd_render_frame(gd_GIF *gif, uint8_t *buffer);
+
+The buffer size must be at least `gif->width * gif->height * 3`, in
+bytes. The function `gd_render_frame()` writes the 24-bit RGB values of
+all canvas pixels in it.
+
+4. Frame duration
+
+GIF animations are not required to have a constant frame rate. Each
+frame can have a different duration, which is stored right before the
+frame in a Graphic Control Extension (GCE) block. This type of block
+is read by gifdec into a `gd_GCE` struct that is a member of the GIF
+handler. Specifically, the unsigned integer `gif->gce.delay` holds the
+current frame duration, in hundreths of a second. That means that, for
+instance, if `gif->gce.delay` is `50`, then the current frame must be
+displayed for half a second.
+
+5. Looping
+
+Most GIF animations are supposed to loop automatically, going back to
+the first frame after the last one is displayed. GIF files may contain
+looping instruction in the form of a non-negative number. If this number
+is zero, the animation must loop forever. Otherwise, this number
+indicates how many times the animation must be played. When `gifdec` is
+decoding a GIF file, this number is stored in `gif->loop_count`.
+
+The function `gd_rewind()` must be called to go back to the start of the
+GIF file without closing and reopening it.
+
+    void gd_rewind(gd_GIF *gif);
+
+6. Putting it all together
+
+gd_GIF *gif = gd_open_gif("some_animation.gif");
+char *buffer = malloc(gif->width * gif->height * 3);
+for (unsigned looped = 1;; looped++) {
+    while (gd_get_frame(gif)) {
+        gd_render_frame(gif, buffer);
+        /* insert code to render buffer to screen here */
+    }
+    if (looped == gif->loop_count)
+        break;
+    gd_rewind(gif);
+}
+free(buffer);
+gd_close_gif(gif);
+
+7. Reading streamed metadata with extension hooks
+
+Some metadata blocks may occur any number of times in GIF files in
+between frames. By default, gifdec ignore these blocks. However, it's
+possible to setup callback functions to handle each type of extension
+block, by changing some GIF handler members.
+
+Whenever a Comment Extension block is found, `gif->comment()` is called.
+
+    void (*comment)(struct gd_GIF *gif);
+
+As defined in the GIF specification, "[t]he Comment Extension contains
+textual information which is not part of the actual graphics in the GIF
+Data Stream." Encoders are recommended to only include "text using the
+7-bit ASCII character set" in GIF comments.
+
+The actual comment is stored as a variable-sized block and must be read
+from the file (using the file descriptor gif->fd) by the callback
+function. Here's an example, printing the comment to stdout:
+
+    void
+    comment(gd_GIF *gif)
+    {
+        uint8_t sub_len, byte, i;
+        do {
+            read(gif->fd, &sub_len, 1);
+            for (i = 0; i < sub_len; i++) {
+                read(gif->fd, &byte, 1);
+                printf("%c", byte);
+            }
+        } while (sub_len);
+        printf("\n");
+    }
+    
+    /* ... */
+    
+    /* Somewhere on the main path of execution. */
+    gif->comment = comment;
+
+
+Whenever a Plain Text Extension block is found, `gif->plain_text()` is
+called.
+
+
+    void (*plain_text)(
+        struct gd_GIF *gif, uint16_t tx, uint16_t ty,
+        uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch,
+        uint8_t fg, uint8_t bg
+    );
+
+According to the GIF specification, "[t]he Plain Text Extension contains
+textual data and the parameters necessary to render that data as a
+graphic [...]". This is a rarely used extension that requires the
+decoder to actually render text on the canvas. In order to support this,
+one must read the relevant specification and implement a suitable
+callback function to setup as `gif->plain_text`.
+
+The actual plain text is stored as a variable-sized block and must be
+read from the file by the callback function.
+
+
+Whenever an unknown Application Extension block is found,
+`gif->application()` is called.
+
+    void (*application)(struct gd_GIF *gif, char id[8], char auth[3]);
+
+Application Extensions are used to extend GIF with extraofficial
+features. Currently, gifdec only supports the so-called "Netscape
+Application Extension", which is commonly used to specify looping
+behavior. Other Application Extensions may be supported via this hook.
+
+The application data is stored as a variable-sized block and must be
+read from the file by the callback function.
 
 
 Example
 -------
 
-TODO
+The file "example.c" is a demo GIF player based on gifdec and SDL2. It
+can be tested like this:
+
+    $ cc `pkg-config --cflags --libs sdl2` -o gifplay gifdec.c example.c
+    $ ./gifplay animation.gif
 
+That should display the animation. Press SPACE to pause and Q to quit.
 
 Copying
 -------