-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
403 additions
and
309 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
#include "gifenc.h" | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <sys/types.h> | ||
#include <sys/stat.h> | ||
#include <fcntl.h> | ||
#ifdef _WIN32 | ||
#include <io.h> | ||
#else | ||
#include <unistd.h> | ||
#endif | ||
|
||
#pragma warning(disable : 4996) | ||
|
||
/* helper to write a little-endian 16-bit number portably */ | ||
#define write_num(fd, n) write((fd), (uint8_t []) {(n) & 0xFF, (n) >> 8}, 2) | ||
|
||
static uint8_t vga[0x30] = { | ||
0x00, 0x00, 0x00, | ||
0xAA, 0x00, 0x00, | ||
0x00, 0xAA, 0x00, | ||
0xAA, 0x55, 0x00, | ||
0x00, 0x00, 0xAA, | ||
0xAA, 0x00, 0xAA, | ||
0x00, 0xAA, 0xAA, | ||
0xAA, 0xAA, 0xAA, | ||
0x55, 0x55, 0x55, | ||
0xFF, 0x55, 0x55, | ||
0x55, 0xFF, 0x55, | ||
0xFF, 0xFF, 0x55, | ||
0x55, 0x55, 0xFF, | ||
0xFF, 0x55, 0xFF, | ||
0x55, 0xFF, 0xFF, | ||
0xFF, 0xFF, 0xFF, | ||
}; | ||
|
||
struct Node { | ||
uint16_t key; | ||
struct Node *children[]; | ||
}; | ||
typedef struct Node Node; | ||
|
||
static Node * | ||
new_node(uint16_t key, int degree) | ||
{ | ||
Node *node = calloc(1, sizeof(*node) + degree * sizeof(Node *)); | ||
if (node) | ||
node->key = key; | ||
return node; | ||
} | ||
|
||
static Node * | ||
new_trie(int degree, int *nkeys) | ||
{ | ||
Node *root = new_node(0, degree); | ||
/* Create nodes for single pixels. */ | ||
for (*nkeys = 0; *nkeys < degree; (*nkeys)++) | ||
root->children[*nkeys] = new_node(*nkeys, degree); | ||
*nkeys += 2; /* skip clear code and stop code */ | ||
return root; | ||
} | ||
|
||
static void | ||
del_trie(Node *root, int degree) | ||
{ | ||
if (!root) | ||
return; | ||
for (int i = 0; i < degree; i++) | ||
del_trie(root->children[i], degree); | ||
free(root); | ||
} | ||
|
||
#define write_and_store(s, dst, fd, src, n) \ | ||
do { \ | ||
write(fd, src, n); \ | ||
if (s) { \ | ||
memcpy(dst, src, n); \ | ||
dst += n; \ | ||
} \ | ||
} while (0); | ||
|
||
static void put_loop(ge_GIF *gif, uint16_t loop); | ||
|
||
ge_GIF *ge_new_gif(const char *fname, uint16_t width, uint16_t height, int depth, int bgindex, int loop) | ||
{ | ||
ge_GIF* gif = calloc(1, sizeof(*gif) + width * height); | ||
if (!gif) | ||
goto no_gif; | ||
gif->w = width; gif->h = height; | ||
gif->bgindex = bgindex; | ||
gif->frame = (uint8_t*)&gif[1]; | ||
gif->back = &gif->frame[width*height]; | ||
gif->depth = depth; | ||
#ifdef _WIN32 | ||
gif->fd = creat(fname, S_IWRITE); | ||
#else | ||
gif->fd = creat(fname, 0666); | ||
#endif | ||
if (gif->fd == -1) | ||
goto no_fd; | ||
#ifdef _WIN32 | ||
setmode(gif->fd, O_BINARY); | ||
#endif | ||
write(gif->fd, "GIF89a", 6); | ||
write_num(gif->fd, width); | ||
write_num(gif->fd, height); | ||
unsigned char flags = 0x70; // no GCT, 2^8 color in the original image (don't care), no sort, size of GCT 2^(7+1) (don't care) | ||
write(gif->fd, (uint8_t []) {flags, (uint8_t) bgindex, 0x00}, 3); // background color = bgindex, ratio = 0 (no default ratio) | ||
put_loop(gif, (uint16_t) loop); // add the NETSCAPE 2.0 option for loop (loop = 0 infinite loop) | ||
return gif; | ||
no_fd: | ||
free(gif); | ||
no_gif: | ||
return NULL; | ||
} | ||
|
||
static void put_loop(ge_GIF *gif, uint16_t loop) | ||
{ | ||
write(gif->fd, (uint8_t []) {'!', 0xFF, 0x0B}, 3); | ||
write(gif->fd, "NETSCAPE2.0", 11); | ||
write(gif->fd, (uint8_t []) {0x03, 0x01}, 2); | ||
write_num(gif->fd, loop); | ||
write(gif->fd, "\0", 1); | ||
} | ||
|
||
/* Add packed key to buffer, updating offset and partial. | ||
* gif->offset holds position to put next *bit* | ||
* gif->partial holds bits to include in next byte */ | ||
static void put_key(ge_GIF *gif, uint16_t key, int key_size) | ||
{ | ||
int byte_offset, bit_offset, bits_to_write; | ||
byte_offset = gif->offset / 8; | ||
bit_offset = gif->offset % 8; | ||
gif->partial |= ((uint32_t) key) << bit_offset; | ||
bits_to_write = bit_offset + key_size; | ||
while (bits_to_write >= 8) { | ||
gif->buffer[byte_offset++] = gif->partial & 0xFF; | ||
if (byte_offset == 0xFF) { | ||
write(gif->fd, "\xFF", 1); | ||
write(gif->fd, gif->buffer, 0xFF); | ||
byte_offset = 0; | ||
} | ||
gif->partial >>= 8; | ||
bits_to_write -= 8; | ||
} | ||
gif->offset = (gif->offset + key_size) % (0xFF * 8); | ||
} | ||
|
||
static void end_key(ge_GIF *gif) | ||
{ | ||
int byte_offset; | ||
byte_offset = gif->offset / 8; | ||
if (gif->offset % 8) | ||
gif->buffer[byte_offset++] = gif->partial & 0xFF; | ||
if (byte_offset) { | ||
write(gif->fd, (uint8_t []) {byte_offset}, 1); | ||
write(gif->fd, gif->buffer, byte_offset); | ||
} | ||
write(gif->fd, "\0", 1); | ||
gif->offset = gif->partial = 0; | ||
} | ||
|
||
static void put_image(ge_GIF *gif, uint16_t w, uint16_t h, uint16_t x, uint16_t y, uint8_t* palette) | ||
{ | ||
int nkeys, key_size, i, j; | ||
Node *node, *child, *root; | ||
int degree = 1 << gif->depth; | ||
|
||
write(gif->fd, ",", 1); | ||
write_num(gif->fd, x); | ||
write_num(gif->fd, y); | ||
write_num(gif->fd, w); | ||
write_num(gif->fd, h); | ||
unsigned char lcd = 0x80 + gif->depth - 1; | ||
write(gif->fd, (uint8_t[]) { lcd }, 1); // gif->depth }, 2);// 0x80 because there is a local color table and there are 2^gif->depth colors in the table, we must write depth-1 | ||
// we write the local color table | ||
int ncol = 3 * degree; | ||
write(gif->fd, palette, ncol); | ||
write(gif->fd, (uint8_t[]) { gif->depth }, 1); // gif->depth }, 2);// 0x80 because there is a local color table and there are 2^gif->depth colors in the table, we must write depth-1 | ||
root = node = new_trie(degree, &nkeys); | ||
key_size = gif->depth + 1; | ||
put_key(gif, degree, key_size); /* clear code */ | ||
for (i = y; i < y+h; i++) { | ||
for (j = x; j < x+w; j++) { | ||
uint8_t pixel = gif->frame[i*gif->w+j] & (degree - 1); | ||
child = node->children[pixel]; | ||
if (child) { | ||
node = child; | ||
} else { | ||
put_key(gif, node->key, key_size); | ||
if (nkeys < 0x1000) { | ||
if (nkeys == (1 << key_size)) | ||
key_size++; | ||
node->children[pixel] = new_node(nkeys++, degree); | ||
} else { | ||
put_key(gif, degree, key_size); /* clear code */ | ||
del_trie(root, degree); | ||
root = node = new_trie(degree, &nkeys); | ||
key_size = gif->depth + 1; | ||
} | ||
node = root->children[pixel]; | ||
} | ||
} | ||
} | ||
put_key(gif, node->key, key_size); | ||
put_key(gif, degree + 1, key_size); /* stop code */ | ||
end_key(gif); | ||
del_trie(root, degree); | ||
} | ||
|
||
static void add_graphics_control_extension(ge_GIF *gif, uint16_t d) | ||
{ | ||
uint8_t flags = 0;// 1;// 2 << 2 + 1; | ||
write(gif->fd, (uint8_t []) {'!', 0xF9, 0x04, flags}, 4); | ||
write_num(gif->fd, d); | ||
write(gif->fd, (uint8_t []) {0xFF, 0x00}, 2); | ||
} | ||
|
||
void ge_add_frame(ge_GIF *gif, uint8_t* palette, uint16_t delay) | ||
{ | ||
uint8_t *tmp; | ||
if (delay || (gif->bgindex >= 0)) | ||
add_graphics_control_extension(gif, delay); | ||
put_image(gif, gif->w, gif->h, 0, 0, palette); | ||
gif->nframes++; | ||
if (gif->bgindex < 0) { | ||
tmp = gif->back; | ||
gif->back = gif->frame; | ||
gif->frame = tmp; | ||
} | ||
} | ||
|
||
void | ||
ge_close_gif(ge_GIF* gif) | ||
{ | ||
write(gif->fd, ";", 1); | ||
close(gif->fd); | ||
free(gif); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#ifndef GIFENC_H | ||
#define GIFENC_H | ||
|
||
#include <stdint.h> | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
typedef struct ge_GIF { | ||
uint16_t w, h; | ||
int depth; | ||
int bgindex; | ||
int fd; | ||
int offset; | ||
int nframes; | ||
uint8_t *frame, *back; | ||
uint32_t partial; | ||
uint8_t buffer[0xFF]; | ||
} ge_GIF; | ||
|
||
ge_GIF* ge_new_gif(const char* fname, uint16_t width, uint16_t height, int depth, int bgindex, int loop); | ||
void ge_add_frame(ge_GIF *gif, uint8_t* palette, uint16_t delay); | ||
void ge_close_gif(ge_GIF* gif); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
#endif /* GIFENC_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters