login

<     >

2017-10-13 14:40:51 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

First commit: parse most things except actual images.

diff --git a/gifdec.c b/gifdec.c
new file mode 100644
index 0000000..6331f89
--- /dev/null
+++ b/gifdec.c
@@ -0,0 +1,241 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+typedef struct Palette {
+    int size;
+    uint8_t colors[0x100 * 3];
+} Palette;
+
+typedef struct GCE {
+    uint16_t delay;
+    uint8_t tindex;
+    uint8_t disposal;
+    int input;
+    int transparency;
+} GCE;
+
+typedef struct GIF {
+    int fd;
+    uint16_t width, height;
+    uint16_t loop_count;
+    GCE gce;
+    Palette *palette;
+    Palette lct, gct;
+    uint8_t *frame;
+} GIF;
+
+uint16_t
+read_num(int fd)
+{
+    uint8_t bytes[2];
+
+    read(fd, bytes, 2);
+    return bytes[0] + (((uint16_t) bytes[1]) << 8);
+}
+
+GIF *
+open_gif(const char *fname)
+{
+    int fd;
+    uint8_t sigver[3];
+    uint16_t width, height;
+    uint8_t fdsz, bgidx, aspect;
+    int gct_sz;
+    GIF *gif;
+
+    fd = open(fname, O_RDONLY);
+    if (fd == -1) return NULL;
+    /* Header */
+    read(fd, sigver, 3);
+    if (memcmp(sigver, "GIF", 3) != 0) {
+        fprintf(stderr, "invalid signature\n");
+        goto fail;
+    }
+    /* Version */
+    read(fd, sigver, 3);
+    if (memcmp(sigver, "89a", 3) != 0) {
+        fprintf(stderr, "invalid version\n");
+        goto fail;
+    }
+    /* Width x Height */
+    width  = read_num(fd);
+    height = read_num(fd);
+    /* FDSZ */
+    read(fd, &fdsz, 1);
+    /* Presence of GCT */
+    if (!(fdsz & 0x80)) {
+        fprintf(stderr, "no global color table");
+        goto fail;
+    }
+    /* Color Space's Depth */
+    if (((fdsz >> 4) & 7) != 7) {
+        fprintf(stderr, "depth of color space is not 8 bits");
+        goto fail;
+    }
+    /* Ignore Sort Flag. */
+    /* GCT Size */
+    gct_sz = 1 << ((fdsz & 0x07) + 1);
+    /* Background Color Index */
+    read(fd, &bgidx, 1);
+    /* Aspect Ratio */
+    read(fd, &aspect, 1);
+    /* Create GIF Structure. */
+    gif = calloc(1, sizeof(*gif) + width * height);
+    if (!gif) goto fail;
+    gif->fd = fd;
+    gif->width  = width;
+    gif->height = height;
+    /* Read GCT */
+    gif->gct.size = gct_sz;
+    read(fd, gif->gct.colors, 3 * gif->gct.size);
+    gif->palette = &gif->gct;
+    gif->frame = (uint8_t *) &gif[1];
+    goto ok;
+fail:
+    close(fd);
+ok:
+    return gif;
+}
+
+void
+discard_sub_blocks(GIF *gif)
+{
+    uint8_t size;
+
+    do {
+        read(gif->fd, &size, 1);
+        lseek(gif->fd, size, SEEK_CUR);
+    } while (size);
+}
+
+/* Ignored extension. */
+void
+read_plain_text_ext(GIF *gif)
+{
+    fprintf(stderr, "ignoring plain text extension\n");
+    /* Discard plain text metadata. */
+    lseek(gif->fd, 13, SEEK_CUR);
+    /* Discard plain text sub-blocks. */
+    discard_sub_blocks(gif);
+}
+
+void
+read_graphic_control_ext(GIF *gif)
+{
+    uint8_t rdit;
+
+    /* Discard block size (always 0x04). */
+    lseek(gif->fd, 1, SEEK_CUR);
+    read(gif->fd, &rdit, 1);
+    gif->gce.disposal = (rdit >> 2) & 3;
+    gif->gce.input = rdit & 2;
+    gif->gce.transparency = rdit & 1;
+    gif->gce.delay = read_num(gif->fd);
+    read(gif->fd, &gif->gce.tindex, 1);
+    /* Skip block terminator. */
+    lseek(gif->fd, 1, SEEK_CUR);
+}
+
+/* Ignored extension. */
+void
+read_comment_ext(GIF *gif)
+{
+    fprintf(stderr, "ignoring comment extension\n");
+    /* Discard comment sub-blocks. */
+    discard_sub_blocks(gif);
+}
+
+void
+read_application_ext(GIF *gif)
+{
+    char app_id[8];
+    char app_auth_code[3];
+
+    /* Discard block size (always 0x0B). */
+    lseek(gif->fd, 1, SEEK_CUR);
+    /* Application Identifier. */
+    read(gif->fd, app_id, 8);
+    /* Application Authentication Code. */
+    read(gif->fd, app_auth_code, 3);
+    if (!strncmp(app_id, "NETSCAPE", sizeof(app_id))) {
+        /* Discard block size (0x03) and constant byte (0x01). */
+        lseek(gif->fd, 2, SEEK_CUR);
+        gif->loop_count = read_num(gif->fd);
+        /* Skip block terminator. */
+        lseek(gif->fd, 1, SEEK_CUR);
+    } else {
+        fprintf(stderr, "ignoring application extension: %.*s\n",
+                (int) sizeof(app_id), app_id);
+        discard_sub_blocks(gif);
+    }
+}
+
+void
+read_ext(GIF *gif)
+{
+    uint8_t label;
+
+    read(gif->fd, &label, 1);
+    switch (label) {
+    case 0x01:
+        read_plain_text_ext(gif);
+        break;
+    case 0xF9:
+        read_graphic_control_ext(gif);
+        break;
+    case 0xFE:
+        read_comment_ext(gif);
+        break;
+    case 0xFF:
+        read_application_ext(gif);
+        break;
+    default:
+        fprintf(stderr, "unknown extension: %02X\n", label);
+    }
+}
+
+void
+read_image(GIF *gif)
+{
+    /* TODO */
+}
+
+/* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
+int
+get_frame(GIF *gif)
+{
+    char sep;
+
+    read(gif->fd, &sep, 1);
+    while (sep != ',') {
+        if (sep == ';')
+            return 0;
+        if (sep == '!')
+            read_ext(gif);
+        else return -1;
+        read(gif->fd, &sep, 1);
+    }
+    read_image(gif);
+    return 1;
+}
+
+int
+main()
+{
+    GIF *gif = open_gif("example.gif");
+    if (gif) {
+        printf("%ux%u\n", gif->width, gif->height);
+        printf("%d colors\n", gif->palette->size);
+        get_frame(gif);
+        close(gif->fd);
+        free(gif);
+    }
+    return 0;
+}