Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Imaging/libImaging/GifEncode.c
diff options
context:
space:
mode:
Diffstat (limited to 'Imaging/libImaging/GifEncode.c')
-rw-r--r--Imaging/libImaging/GifEncode.c319
1 files changed, 319 insertions, 0 deletions
diff --git a/Imaging/libImaging/GifEncode.c b/Imaging/libImaging/GifEncode.c
new file mode 100644
index 0000000..fd696c1
--- /dev/null
+++ b/Imaging/libImaging/GifEncode.c
@@ -0,0 +1,319 @@
+/*
+ * The Python Imaging Library.
+ * $Id: GifEncode.c 2134 2004-10-06 08:55:20Z fredrik $
+ *
+ * encoder for uncompressed GIF data
+ *
+ * history:
+ * 97-01-05 fl created (writes uncompressed data)
+ * 97-08-27 fl fixed off-by-one error in buffer size test
+ * 98-07-09 fl added interlace write support
+ * 99-02-07 fl rewritten, now uses a run-length encoding strategy
+ * 99-02-08 fl improved run-length encoding for long runs
+ *
+ * Copyright (c) Secret Labs AB 1997-99.
+ * Copyright (c) Fredrik Lundh 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Imaging.h"
+
+#include "Gif.h"
+
+/* codes from 0 to 255 are literals */
+#define CLEAR_CODE 256
+#define EOF_CODE 257
+#define FIRST_CODE 258
+#define LAST_CODE 511
+
+enum { INIT, ENCODE, ENCODE_EOF, FLUSH, EXIT };
+
+/* to make things a little less complicated, we use a simple output
+ queue to hold completed blocks. the following inlined function
+ adds a byte to the current block. it allocates a new block if
+ necessary. */
+
+static inline int
+emit(GIFENCODERSTATE *context, int byte)
+{
+ /* write a byte to the output buffer */
+
+ if (!context->block || context->block->size == 255) {
+ GIFENCODERBLOCK* block;
+
+ /* no room in the current block (or no current block);
+ allocate a new one */
+
+ /* add current block to end of flush queue */
+ if (context->block) {
+ block = context->flush;
+ while (block && block->next)
+ block = block->next;
+ if (block)
+ block->next = context->block;
+ else
+ context->flush = context->block;
+ }
+
+ /* get a new block */
+ if (context->free) {
+ block = context->free;
+ context->free = NULL;
+ } else {
+ block = malloc(sizeof(GIFENCODERBLOCK));
+ if (!block)
+ return 0;
+ }
+
+ block->size = 0;
+ block->next = NULL;
+
+ context->block = block;
+
+ }
+
+ /* write new byte to block */
+ context->block->data[context->block->size++] = byte;
+
+ return 1;
+}
+
+/* write a code word to the current block. this is a macro to make
+ sure it's inlined on all platforms */
+
+#define EMIT(code) {\
+ context->bitbuffer |= ((INT32) (code)) << context->bitcount;\
+ context->bitcount += 9;\
+ while (context->bitcount >= 8) {\
+ if (!emit(context, (UINT8) context->bitbuffer)) {\
+ state->errcode = IMAGING_CODEC_MEMORY;\
+ return 0;\
+ }\
+ context->bitbuffer >>= 8;\
+ context->bitcount -= 8;\
+ }\
+}
+
+/* write a run. we use a combination of literals and combinations of
+ literals. this can give quite decent compression for images with
+ long stretches of identical pixels. but remember: if you want
+ really good compression, use another file format. */
+
+#define EMIT_RUN(label) {\
+label:\
+ while (context->count > 0) {\
+ int run = 2;\
+ EMIT(context->last);\
+ context->count--;\
+ if (state->count++ == LAST_CODE) {\
+ EMIT(CLEAR_CODE);\
+ state->count = FIRST_CODE;\
+ goto label;\
+ }\
+ while (context->count >= run) {\
+ EMIT(state->count - 1);\
+ context->count -= run;\
+ run++;\
+ if (state->count++ == LAST_CODE) {\
+ EMIT(CLEAR_CODE);\
+ state->count = FIRST_CODE;\
+ goto label;\
+ }\
+ }\
+ if (context->count > 1) {\
+ EMIT(state->count - 1 - (run - context->count));\
+ context->count = 0;\
+ if (state->count++ == LAST_CODE) {\
+ EMIT(CLEAR_CODE);\
+ state->count = FIRST_CODE;\
+ }\
+ break;\
+ }\
+ }\
+}
+
+int
+ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ UINT8* ptr;
+ int this;
+
+ GIFENCODERBLOCK* block;
+ GIFENCODERSTATE *context = (GIFENCODERSTATE*) state->context;
+
+ if (!state->state) {
+
+ /* place a clear code in the output buffer */
+ context->bitbuffer = CLEAR_CODE;
+ context->bitcount = 9;
+
+ state->count = FIRST_CODE;
+
+ if (context->interlace) {
+ context->interlace = 1;
+ context->step = 8;
+ } else
+ context->step = 1;
+
+ context->last = -1;
+
+ /* sanity check */
+ if (state->xsize <= 0 || state->ysize <= 0)
+ state->state = ENCODE_EOF;
+
+ }
+
+ ptr = buf;
+
+ for (;;)
+
+ switch (state->state) {
+
+ case INIT:
+ case ENCODE:
+
+ /* identify and store a run of pixels */
+
+ if (state->x == 0 || state->x >= state->xsize) {
+
+ if (!context->interlace && state->y >= state->ysize) {
+ state->state = ENCODE_EOF;
+ break;
+ }
+
+ if (context->flush) {
+ state->state = FLUSH;
+ break;
+ }
+
+ /* get another line of data */
+ state->shuffle(
+ state->buffer,
+ (UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->xsize
+ );
+
+ state->x = 0;
+
+ if (state->state == INIT) {
+ /* preload the run-length buffer and get going */
+ context->last = state->buffer[0];
+ context->count = state->x = 1;
+ state->state = ENCODE;
+ }
+
+ /* step forward, according to the interlace settings */
+ state->y += context->step;
+ while (context->interlace && state->y >= state->ysize)
+ switch (context->interlace) {
+ case 1:
+ state->y = 4;
+ context->interlace = 2;
+ break;
+ case 2:
+ context->step = 4;
+ state->y = 2;
+ context->interlace = 3;
+ break;
+ case 3:
+ context->step = 2;
+ state->y = 1;
+ context->interlace = 0;
+ break;
+ default:
+ /* just make sure we don't loop forever */
+ context->interlace = 0;
+ }
+
+ }
+
+ this = state->buffer[state->x++];
+
+ if (this == context->last)
+ context->count++;
+ else {
+ EMIT_RUN(label1);
+ context->last = this;
+ context->count = 1;
+ }
+ break;
+
+
+ case ENCODE_EOF:
+
+ /* write the final run */
+ EMIT_RUN(label2);
+
+ /* write an end of image marker */
+ EMIT(EOF_CODE);
+
+ /* empty the bit buffer */
+ while (context->bitcount > 0) {
+ if (!emit(context, (UINT8) context->bitbuffer)) {
+ state->errcode = IMAGING_CODEC_MEMORY;
+ return 0;
+ }
+ context->bitbuffer >>= 8;
+ context->bitcount -= 8;
+ }
+
+ /* flush the last block, and exit */
+ if (context->block) {
+ GIFENCODERBLOCK* block;
+ block = context->flush;
+ while (block && block->next)
+ block = block->next;
+ if (block)
+ block->next = context->block;
+ else
+ context->flush = context->block;
+ context->block = NULL;
+ }
+
+ state->state = EXIT;
+
+ /* fall through... */
+
+ case EXIT:
+ case FLUSH:
+
+ while (context->flush) {
+
+ /* get a block from the flush queue */
+ block = context->flush;
+
+ if (block->size > 0) {
+
+ /* make sure it fits into the output buffer */
+ if (bytes < block->size+1)
+ return ptr - buf;
+
+ ptr[0] = block->size;
+ memcpy(ptr+1, block->data, block->size);
+
+ ptr += block->size+1;
+ bytes -= block->size+1;
+
+ }
+
+ context->flush = block->next;
+
+ if (context->free)
+ free(context->free);
+ context->free = block;
+
+ }
+
+ if (state->state == EXIT) {
+ /* this was the last block! */
+ if (context->free)
+ free(context->free);
+ state->errcode = IMAGING_CODEC_END;
+ return ptr - buf;
+ }
+
+ state->state = ENCODE;
+ break;
+ }
+}