mirror of
https://github.com/php/php-src.git
synced 2024-10-06 01:06:14 +00:00
72d90fc41c
LZW patent violation so we can distribute read-only GIF support with our bundled GD2. The patent also expires in 2003, which is quickly approaching, so we can roll in write support soon unless things change.
574 lines
16 KiB
C
574 lines
16 KiB
C
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "gd.h"
|
|
|
|
/* Used only when debugging GIF compression code */
|
|
/* #define DEBUGGING_ENVARS */
|
|
|
|
#ifdef DEBUGGING_ENVARS
|
|
|
|
static int verbose_set = 0;
|
|
static int verbose;
|
|
#define VERBOSE (verbose_set?verbose:set_verbose())
|
|
|
|
static int set_verbose(void)
|
|
{
|
|
verbose = !!getenv("GIF_VERBOSE");
|
|
verbose_set = 1;
|
|
return(verbose);
|
|
}
|
|
|
|
#else
|
|
|
|
#define VERBOSE 0
|
|
|
|
#endif
|
|
|
|
|
|
#define MAXCOLORMAPSIZE 256
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
#define CM_RED 0
|
|
#define CM_GREEN 1
|
|
#define CM_BLUE 2
|
|
|
|
#define MAX_LWZ_BITS 12
|
|
|
|
#define INTERLACE 0x40
|
|
#define LOCALCOLORMAP 0x80
|
|
#define BitSet(byte, bit) (((byte) & (bit)) == (bit))
|
|
|
|
#define ReadOK(file,buffer,len) (gdGetBuf(buffer, len, file) != 0)
|
|
|
|
#define LM_to_uint(a,b) (((b)<<8)|(a))
|
|
|
|
/* We may eventually want to use this information, but def it out for now */
|
|
#if 0
|
|
static struct {
|
|
unsigned int Width;
|
|
unsigned int Height;
|
|
unsigned char ColorMap[3][MAXCOLORMAPSIZE];
|
|
unsigned int BitPixel;
|
|
unsigned int ColorResolution;
|
|
unsigned int Background;
|
|
unsigned int AspectRatio;
|
|
} GifScreen;
|
|
#endif
|
|
|
|
static struct {
|
|
int transparent;
|
|
int delayTime;
|
|
int inputFlag;
|
|
int disposal;
|
|
} Gif89 = { -1, -1, -1, 0 };
|
|
|
|
static int ReadColorMap (gdIOCtx *fd, int number, unsigned char (*buffer)[256]);
|
|
static int DoExtension (gdIOCtx *fd, int label, int *Transparent);
|
|
static int GetDataBlock (gdIOCtx *fd, unsigned char *buf);
|
|
static int GetCode (gdIOCtx *fd, int code_size, int flag);
|
|
static int LWZReadByte (gdIOCtx *fd, int flag, int input_code_size);
|
|
|
|
static void ReadImage (gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap)[256], int interlace); /*1.4//, int ignore); */
|
|
|
|
int ZeroDataBlock;
|
|
|
|
gdImagePtr gdImageCreateFromGifSource(gdSourcePtr inSource)
|
|
{
|
|
gdIOCtx *in = gdNewSSCtx(inSource, NULL);
|
|
gdImagePtr im;
|
|
|
|
im = gdImageCreateFromGifCtx(in);
|
|
|
|
in->free(in);
|
|
|
|
return im;
|
|
}
|
|
|
|
gdImagePtr
|
|
gdImageCreateFromGif(FILE *fdFile)
|
|
{
|
|
gdIOCtx *fd = gdNewFileCtx(fdFile);
|
|
gdImagePtr im = 0;
|
|
|
|
im = gdImageCreateFromGifCtx(fd);
|
|
|
|
fd->free(fd);
|
|
|
|
return im;
|
|
}
|
|
|
|
gdImagePtr
|
|
gdImageCreateFromGifCtx(gdIOCtxPtr fd)
|
|
{
|
|
/* 1.4 int imageNumber; */
|
|
int BitPixel;
|
|
int ColorResolution;
|
|
int Background;
|
|
int AspectRatio;
|
|
int Transparent = (-1);
|
|
unsigned char buf[16];
|
|
unsigned char c;
|
|
unsigned char ColorMap[3][MAXCOLORMAPSIZE];
|
|
unsigned char localColorMap[3][MAXCOLORMAPSIZE];
|
|
int imw, imh;
|
|
int useGlobalColormap;
|
|
int bitPixel;
|
|
int i;
|
|
/*1.4//int imageCount = 0; */
|
|
char version[4];
|
|
|
|
gdImagePtr im = 0;
|
|
ZeroDataBlock = FALSE;
|
|
|
|
/*1.4//imageNumber = 1; */
|
|
if (! ReadOK(fd,buf,6)) {
|
|
return 0;
|
|
}
|
|
if (strncmp((char *)buf,"GIF",3) != 0) {
|
|
return 0;
|
|
}
|
|
strncpy(version, (char *)buf + 3, 3);
|
|
version[3] = '\0';
|
|
|
|
if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
|
|
return 0;
|
|
}
|
|
if (! ReadOK(fd,buf,7)) {
|
|
return 0;
|
|
}
|
|
BitPixel = 2<<(buf[4]&0x07);
|
|
ColorResolution = (int) (((buf[4]&0x70)>>3)+1);
|
|
Background = buf[5];
|
|
AspectRatio = buf[6];
|
|
|
|
if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */
|
|
if (ReadColorMap(fd, BitPixel, ColorMap)) {
|
|
return 0;
|
|
}
|
|
}
|
|
for (;;) {
|
|
if (! ReadOK(fd,&c,1)) {
|
|
return 0;
|
|
}
|
|
if (c == ';') { /* GIF terminator */
|
|
goto terminated;
|
|
}
|
|
|
|
if (c == '!') { /* Extension */
|
|
if (! ReadOK(fd,&c,1)) {
|
|
return 0;
|
|
}
|
|
DoExtension(fd, c, &Transparent);
|
|
continue;
|
|
}
|
|
|
|
if (c != ',') { /* Not a valid start character */
|
|
continue;
|
|
}
|
|
|
|
/*1.4//++imageCount; */
|
|
|
|
if (! ReadOK(fd,buf,9)) {
|
|
return 0;
|
|
}
|
|
|
|
useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP);
|
|
|
|
bitPixel = 1<<((buf[8]&0x07)+1);
|
|
|
|
imw = LM_to_uint(buf[4],buf[5]);
|
|
imh = LM_to_uint(buf[6],buf[7]);
|
|
if (!(im = gdImageCreate(imw, imh))) {
|
|
return 0;
|
|
}
|
|
im->interlace = BitSet(buf[8], INTERLACE);
|
|
if (! useGlobalColormap) {
|
|
if (ReadColorMap(fd, bitPixel, localColorMap)) {
|
|
return 0;
|
|
}
|
|
ReadImage(im, fd, imw, imh, localColorMap,
|
|
BitSet(buf[8], INTERLACE));
|
|
/*1.4//imageCount != imageNumber); */
|
|
} else {
|
|
ReadImage(im, fd, imw, imh,
|
|
ColorMap,
|
|
BitSet(buf[8], INTERLACE));
|
|
/*1.4//imageCount != imageNumber); */
|
|
}
|
|
if (Transparent != (-1)) {
|
|
gdImageColorTransparent(im, Transparent);
|
|
}
|
|
goto terminated;
|
|
}
|
|
|
|
terminated:
|
|
/* Terminator before any image was declared! */
|
|
if (!im) {
|
|
return 0;
|
|
}
|
|
/* Check for open colors at the end, so
|
|
we can reduce colorsTotal and ultimately
|
|
BitsPerPixel */
|
|
for (i=((im->colorsTotal-1)); (i>=0); i--) {
|
|
if (im->open[i]) {
|
|
im->colorsTotal--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return im;
|
|
}
|
|
|
|
static int
|
|
ReadColorMap(gdIOCtx *fd, int number, unsigned char (*buffer)[256])
|
|
{
|
|
int i;
|
|
unsigned char rgb[3];
|
|
|
|
|
|
for (i = 0; i < number; ++i) {
|
|
if (! ReadOK(fd, rgb, sizeof(rgb))) {
|
|
return TRUE;
|
|
}
|
|
buffer[CM_RED][i] = rgb[0] ;
|
|
buffer[CM_GREEN][i] = rgb[1] ;
|
|
buffer[CM_BLUE][i] = rgb[2] ;
|
|
}
|
|
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
DoExtension(gdIOCtx *fd, int label, int *Transparent)
|
|
{
|
|
static unsigned char buf[256];
|
|
|
|
switch (label) {
|
|
case 0xf9: /* Graphic Control Extension */
|
|
(void) GetDataBlock(fd, (unsigned char*) buf);
|
|
Gif89.disposal = (buf[0] >> 2) & 0x7;
|
|
Gif89.inputFlag = (buf[0] >> 1) & 0x1;
|
|
Gif89.delayTime = LM_to_uint(buf[1],buf[2]);
|
|
if ((buf[0] & 0x1) != 0)
|
|
*Transparent = buf[3];
|
|
|
|
while (GetDataBlock(fd, (unsigned char*) buf) != 0)
|
|
;
|
|
return FALSE;
|
|
default:
|
|
break;
|
|
}
|
|
while (GetDataBlock(fd, (unsigned char*) buf) != 0)
|
|
;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
GetDataBlock_(gdIOCtx *fd, unsigned char *buf)
|
|
{
|
|
unsigned char count;
|
|
|
|
if (! ReadOK(fd,&count,1)) {
|
|
return -1;
|
|
}
|
|
|
|
ZeroDataBlock = count == 0;
|
|
|
|
if ((count != 0) && (! ReadOK(fd, buf, count))) {
|
|
return -1;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static int
|
|
GetDataBlock(gdIOCtx *fd, unsigned char *buf)
|
|
{
|
|
int rv;
|
|
int i;
|
|
|
|
rv = GetDataBlock_(fd,buf);
|
|
if (VERBOSE)
|
|
{ printf("[GetDataBlock returning %d",rv);
|
|
if (rv > 0)
|
|
{ printf(":");
|
|
for (i=0;i<rv;i++) printf(" %02x",buf[i]);
|
|
}
|
|
printf("]\n");
|
|
}
|
|
return(rv);
|
|
}
|
|
|
|
static int
|
|
GetCode_(gdIOCtx *fd, int code_size, int flag)
|
|
{
|
|
static unsigned char buf[280];
|
|
static int curbit, lastbit, done, last_byte;
|
|
int i, j, ret;
|
|
unsigned char count;
|
|
|
|
if (flag) {
|
|
curbit = 0;
|
|
lastbit = 0;
|
|
done = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
if ( (curbit+code_size) >= lastbit) {
|
|
if (done) {
|
|
if (curbit >= lastbit) {
|
|
/* Oh well */
|
|
}
|
|
return -1;
|
|
}
|
|
buf[0] = buf[last_byte-2];
|
|
buf[1] = buf[last_byte-1];
|
|
|
|
if ((count = GetDataBlock(fd, &buf[2])) == 0)
|
|
done = TRUE;
|
|
|
|
last_byte = 2 + count;
|
|
curbit = (curbit - lastbit) + 16;
|
|
lastbit = (2+count)*8 ;
|
|
}
|
|
|
|
ret = 0;
|
|
for (i = curbit, j = 0; j < code_size; ++i, ++j)
|
|
ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
|
|
|
|
curbit += code_size;
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
GetCode(gdIOCtx *fd, int code_size, int flag)
|
|
{
|
|
int rv;
|
|
|
|
rv = GetCode_(fd,code_size,flag);
|
|
if (VERBOSE) printf("[GetCode(,%d,%d) returning %d]\n",code_size,flag,rv);
|
|
return(rv);
|
|
}
|
|
|
|
#define STACK_SIZE ((1<<(MAX_LWZ_BITS))*2)
|
|
static int
|
|
LWZReadByte_(gdIOCtx *fd, int flag, int input_code_size)
|
|
{
|
|
static int fresh = FALSE;
|
|
int code, incode;
|
|
static int code_size, set_code_size;
|
|
static int max_code, max_code_size;
|
|
static int firstcode, oldcode;
|
|
static int clear_code, end_code;
|
|
static int table[2][(1<< MAX_LWZ_BITS)];
|
|
static int stack[STACK_SIZE], *sp;
|
|
register int i;
|
|
|
|
if (flag) {
|
|
set_code_size = input_code_size;
|
|
code_size = set_code_size+1;
|
|
clear_code = 1 << set_code_size ;
|
|
end_code = clear_code + 1;
|
|
max_code_size = 2*clear_code;
|
|
max_code = clear_code+2;
|
|
|
|
GetCode(fd, 0, TRUE);
|
|
|
|
fresh = TRUE;
|
|
|
|
for (i = 0; i < clear_code; ++i) {
|
|
table[0][i] = 0;
|
|
table[1][i] = i;
|
|
}
|
|
for (; i < (1<<MAX_LWZ_BITS); ++i)
|
|
table[0][i] = table[1][0] = 0;
|
|
|
|
sp = stack;
|
|
|
|
return 0;
|
|
} else if (fresh) {
|
|
fresh = FALSE;
|
|
do {
|
|
firstcode = oldcode =
|
|
GetCode(fd, code_size, FALSE);
|
|
} while (firstcode == clear_code);
|
|
return firstcode;
|
|
}
|
|
|
|
if (sp > stack)
|
|
return *--sp;
|
|
|
|
while ((code = GetCode(fd, code_size, FALSE)) >= 0) {
|
|
if (code == clear_code) {
|
|
for (i = 0; i < clear_code; ++i) {
|
|
table[0][i] = 0;
|
|
table[1][i] = i;
|
|
}
|
|
for (; i < (1<<MAX_LWZ_BITS); ++i)
|
|
table[0][i] = table[1][i] = 0;
|
|
code_size = set_code_size+1;
|
|
max_code_size = 2*clear_code;
|
|
max_code = clear_code+2;
|
|
sp = stack;
|
|
firstcode = oldcode =
|
|
GetCode(fd, code_size, FALSE);
|
|
return firstcode;
|
|
} else if (code == end_code) {
|
|
int count;
|
|
unsigned char buf[260];
|
|
|
|
if (ZeroDataBlock)
|
|
return -2;
|
|
|
|
while ((count = GetDataBlock(fd, buf)) > 0)
|
|
;
|
|
|
|
if (count != 0)
|
|
return -2;
|
|
}
|
|
|
|
incode = code;
|
|
|
|
if (sp == (stack + STACK_SIZE)) {
|
|
/* Bad compressed data stream */
|
|
return -1;
|
|
}
|
|
|
|
if (code >= max_code) {
|
|
*sp++ = firstcode;
|
|
code = oldcode;
|
|
}
|
|
|
|
while (code >= clear_code) {
|
|
if (sp == (stack + STACK_SIZE)) {
|
|
/* Bad compressed data stream */
|
|
return -1;
|
|
}
|
|
*sp++ = table[1][code];
|
|
if (code == table[0][code]) {
|
|
/* Oh well */
|
|
}
|
|
code = table[0][code];
|
|
}
|
|
|
|
*sp++ = firstcode = table[1][code];
|
|
|
|
if ((code = max_code) <(1<<MAX_LWZ_BITS)) {
|
|
table[0][code] = oldcode;
|
|
table[1][code] = firstcode;
|
|
++max_code;
|
|
if ((max_code >= max_code_size) &&
|
|
(max_code_size < (1<<MAX_LWZ_BITS))) {
|
|
max_code_size *= 2;
|
|
++code_size;
|
|
}
|
|
}
|
|
|
|
oldcode = incode;
|
|
|
|
if (sp > stack)
|
|
return *--sp;
|
|
}
|
|
return code;
|
|
}
|
|
|
|
static int
|
|
LWZReadByte(gdIOCtx *fd, int flag, int input_code_size)
|
|
{
|
|
int rv;
|
|
|
|
rv = LWZReadByte_(fd,flag,input_code_size);
|
|
if (VERBOSE) printf("[LWZReadByte(,%d,%d) returning %d]\n",flag,input_code_size,rv);
|
|
return(rv);
|
|
}
|
|
|
|
static void
|
|
ReadImage(gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap)[256], int interlace) /*1.4//, int ignore) */
|
|
{
|
|
unsigned char c;
|
|
int v;
|
|
int xpos = 0, ypos = 0, pass = 0;
|
|
int i;
|
|
/* Stash the color map into the image */
|
|
for (i=0; (i<gdMaxColors); i++) {
|
|
im->red[i] = cmap[CM_RED][i];
|
|
im->green[i] = cmap[CM_GREEN][i];
|
|
im->blue[i] = cmap[CM_BLUE][i];
|
|
im->open[i] = 1;
|
|
}
|
|
/* Many (perhaps most) of these colors will remain marked open. */
|
|
im->colorsTotal = gdMaxColors;
|
|
/*
|
|
** Initialize the Compression routines
|
|
*/
|
|
if (! ReadOK(fd,&c,1)) {
|
|
return;
|
|
}
|
|
if (LWZReadByte(fd, TRUE, c) < 0) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
** If this is an "uninteresting picture" ignore it.
|
|
** REMOVED For 1.4
|
|
*/
|
|
/*if (ignore) { */
|
|
/* while (LWZReadByte(fd, FALSE, c) >= 0) */
|
|
/* ; */
|
|
/* return; */
|
|
/*} */
|
|
|
|
while ((v = LWZReadByte(fd,FALSE,c)) >= 0 ) {
|
|
/* This how we recognize which colors are actually used. */
|
|
if (im->open[v]) {
|
|
im->open[v] = 0;
|
|
}
|
|
gdImageSetPixel(im, xpos, ypos, v);
|
|
++xpos;
|
|
if (xpos == len) {
|
|
xpos = 0;
|
|
if (interlace) {
|
|
switch (pass) {
|
|
case 0:
|
|
case 1:
|
|
ypos += 8; break;
|
|
case 2:
|
|
ypos += 4; break;
|
|
case 3:
|
|
ypos += 2; break;
|
|
}
|
|
|
|
if (ypos >= height) {
|
|
++pass;
|
|
switch (pass) {
|
|
case 1:
|
|
ypos = 4; break;
|
|
case 2:
|
|
ypos = 2; break;
|
|
case 3:
|
|
ypos = 1; break;
|
|
default:
|
|
goto fini;
|
|
}
|
|
}
|
|
} else {
|
|
++ypos;
|
|
}
|
|
}
|
|
if (ypos >= height)
|
|
break;
|
|
}
|
|
|
|
fini:
|
|
if (LWZReadByte(fd,FALSE,c)>=0) {
|
|
/* Ignore extra */
|
|
}
|
|
}
|
|
|