- sync to 1.5.0

master
pierre 2006-04-05 15:35:09 +00:00
parent 3d209b2ed9
commit 0b6804ee48
26 changed files with 7017 additions and 2915 deletions

89
src/HISTORY Normal file
View File

@ -0,0 +1,89 @@
990628 Thomas Boutell (TBB)
Removed all C++ style comments for broader compatibility.
990310 Philip Warner (pjw)
--------------------------
Broke gd.c into multiple files; gd.c now has graphics operations, and gd_<zzz> has specific
support for different formats (eg. GD, GD2, GIF).
Restructured I/O so that reading from files/streams/anything is now possible. All handled
through the IOCtx record which must define some basic I/O services. All services are defined
for files, but only some are defined for in-memory pointers. The in-memory pointer
implementation is primarily for Perl compatibility. Changed basically involved writing some
low level IO routines, and relpacing most FILE* variables with IOCtx*.
Added gdImageGdPtr, gdImageGd2Ptr, gdImageGifPtr, gdImageLzwPtr for Perl compatibility.
Broke up the GIF handling routines; gd_gif_in.c does all GIF input (this did not change when
support for LZW compression was removed). gd_gif_out.c not handles the miGif compression that
was introduced in version 1.3
Reinstated LZW support through the gdImageLzw function (defined in gd_lzw_out.c).
This is NOT built by default, and the header file must be modified to include it in the build.
Added 'GD2' support. This is a zlib-compressed internal format useful for extracting portions
of images without having to read entire image files. Informal tests suggest it is only 30% slower
than the old GD format, and file space requirements are similar to GOF. The GD2 header fields
now contain version information, so the format can probably be considered stable across versions.
Various other functions also added (see index.html).
Informal Tests
--------------
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t064.gd2 100 3000 2000 400 300
Extracting 100 times from (3000, 2000), size is 400x300
23 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t128.gd2 100 3000 2000 400 300
Extracting 100 times from (3000, 2000), size is 400x300
19 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t256.gd2 100 3000 2000 400 300
Extracting 100 times from (3000, 2000), size is 400x300
33 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t512.gd2 100 3000 2000 400 300
Extracting 100 times from (3000, 2000), size is 400x300
65 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t064.gd2 100 3000 2000 200 100
Extracting 100 times from (3000, 2000), size is 200x100
8 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t128.gd2 100 3000 2000 200 100
Extracting 100 times from (3000, 2000), size is 200x100
8 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t256.gd2 100 3000 2000 200 100
Extracting 100 times from (3000, 2000), size is 200x100
16 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t512.gd2 100 3000 2000 200 100
Extracting 100 times from (3000, 2000), size is 200x100
59 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t064.gd2 100 3000 2000 800 600
Extracting 100 times from (3000, 2000), size is 800x600
71 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t128.gd2 100 3000 2000 800 600
Extracting 100 times from (3000, 2000), size is 800x600
70 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t256.gd2 100 3000 2000 800 600
Extracting 100 times from (3000, 2000), size is 800x600
86 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t512.gd2 100 3000 2000 800 600
Extracting 100 times from (3000, 2000), size is 800x600
160 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ls -lrt t*.gd2
-rw-r--r-- 1 pjw users 1363301 Mar 10 12:03 t512.gd2
-rw-r--r-- 1 pjw users 1339003 Mar 10 12:06 t256.gd2
-rw-r--r-- 1 pjw users 1323925 Mar 10 12:09 t128.gd2
-rw-r--r-- 1 pjw users 1492096 Mar 10 12:12 t064.gd2
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t064.gd2 100 3000 2000 50 25
Extracting 100 times from (3000, 2000), size is 50x25
4 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t128.gd2 100 3000 2000 50 25
Extracting 100 times from (3000, 2000), size is 50x25
2 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t256.gd2 100 3000 2000 50 25
Extracting 100 times from (3000, 2000), size is 50x25
4 seconds to extract (& destroy) 100 times
pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t512.gd2 100 3000 2000 50 25
Extracting 100 times from (3000, 2000), size is 50x25
15 seconds to extract (& destroy) 100 times

View File

@ -9,10 +9,14 @@
CC=gcc
AR=ar
CFLAGS=-O
LIBS=-L./ -lgd -lm
#CFLAGS= -ggdb
CFLAGS= -O
LIBS=-L. -lgd -lz -lm
all: libgd.a gddemo giftogd webgif
all: libgd.a gdtest gddemo giftolzw giftogd giftogd2 gdtogif gd2togif gd2tolzw gd2copypal gdparttogif gd2time webgif
gdtest: gdtest.o libgd.a gd.h
$(CC) gdtest.o -o gdtest $(LIBS)
gddemo: gddemo.o libgd.a gd.h gdfonts.h gdfontl.h
$(CC) gddemo.o -o gddemo $(LIBS)
@ -20,15 +24,39 @@ gddemo: gddemo.o libgd.a gd.h gdfonts.h gdfontl.h
giftogd: giftogd.o libgd.a gd.h
$(CC) giftogd.o -o giftogd $(LIBS)
libgd.a: gd.o gdfontt.o gdfonts.o gdfontmb.o gdfontl.o gdfontg.o \
giftogd2: giftogd2.o libgd.a gd.h
$(CC) giftogd2.o -o giftogd2 $(LIBS)
gdtogif: gdtogif.o libgd.a gd.h
$(CC) gdtogif.o -o gdtogif $(LIBS)
giftolzw: giftolzw.o libgd.a gd.h
$(CC) giftolzw.o -o giftolzw $(LIBS)
gd2togif: gd2togif.o libgd.a gd.h
$(CC) gd2togif.o -o gd2togif $(LIBS)
gd2tolzw: gd2tolzw.o libgd.a gd.h
$(CC) gd2tolzw.o -o gd2tolzw $(LIBS)
gd2copypal: gd2copypal.o libgd.a gd.h
$(CC) gd2copypal.o -o gd2copypal $(LIBS)
gdparttogif: gdparttogif.o libgd.a gd.h
$(CC) gdparttogif.o -o gdparttogif $(LIBS)
gd2time: gd2time.o libgd.a gd.h
$(CC) gd2time.o -o gd2time $(LIBS)
libgd.a: gd.o io.o io_file.o io_dp.o io_ss.o gd_gif_in.o gd_gif_out.o gd_lzw_out.o gd_gd.o gd_gd2.o gdfontt.o gdfonts.o gdfontmb.o gdfontl.o gdfontg.o \
gd.h gdfontt.h gdfonts.h gdfontmb.h gdfontl.h gdfontg.h
rm -f libgd.a
$(AR) rc libgd.a gd.o gdfontt.o gdfonts.o gdfontmb.o \
$(AR) rc libgd.a gd.o io.o io_file.o io_dp.o gd_gif_in.o gd_gif_out.o gd_lzw_out.o gd_gd.o gd_gd2.o gdfontt.o gdfonts.o gdfontmb.o \
gdfontl.o gdfontg.o
webgif: webgif.o libgd.a gd.h
$(CC) webgif.o -o webgif $(LIBS)
clean:
rm -f *.o *.a gddemo giftogd
rm -f *.o *.a gddemo gdtest giftogd giftogd2 giftolzw gdtogif gd2togif gd2tolzw gdparttogif gd2time gd2copypal webgif

View File

@ -136,22 +136,22 @@ $info
char ${gdname}Data[] = {
EOF
my($i);
my ($i);
for $i (0 .. 255)
{
$data[$i] = '' unless defined $data[$i];
$data[$i] = '0' x ($width * $height - length $data[$i]) . $data[$i];
print FILEC "/* Char $i */\n";
my($line);
my ($line);
for $line (0 .. $height - 1)
{ print FILEC join ',', split(//, substr($data[$i], $line * $width, $width)), "\n"; }
print FILEC "\n";
next;
my($line);
for $line (0 .. $height - 1)
for my $line (0 .. $height - 1)
{ print substr($data[$i], $line * $width, $width), "\n"; }
}

3977
src/gd.c

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,16 @@
/* stdio is needed for file I/O. */
#include <stdio.h>
#include "io.h"
/* Uncomment this line if you are licensed to use LZW compression.
Licensing LZW is strictly between you and the Unisys corporation.
The authors of GD can provide NO INFORMATION WHATSOEVER
regarding licensing of LZW compression.
*/
/* #define LZW_LICENCED */
/* This can't be changed, it's part of the GIF specification. */
@ -20,7 +30,7 @@
/* Image type. See functions below; you will not need to change
the elements directly. Use the provided macros to
access sx, sy, the color table, and colorsTotal for
access sx, sy, the color table, and colorsTotal for
read-only purposes. */
typedef struct gdImageStruct {
@ -30,13 +40,13 @@ typedef struct gdImageStruct {
int colorsTotal;
int red[gdMaxColors];
int green[gdMaxColors];
int blue[gdMaxColors];
int blue[gdMaxColors];
int open[gdMaxColors];
int transparent;
int *polyInts;
int polyAllocated;
struct gdImageStruct *brush;
struct gdImageStruct *tile;
struct gdImageStruct *tile;
int brushColorMap[gdMaxColors];
int tileColorMap[gdMaxColors];
int styleLength;
@ -84,23 +94,30 @@ typedef gdFont *gdFontPtr;
gdImagePtr gdImageCreate(int sx, int sy);
gdImagePtr gdImageCreateFromGif(FILE *fd);
gdImagePtr gdImageCreateFromGd(FILE *in);
gdImagePtr gdImageCreateFromXbm(FILE *fd);
gdImagePtr gdImageCreateFromGifCtx(gdIOCtxPtr in);
/* A custom data source. */
/* The source function must return -1 on error, otherwise the number
of bytes fetched. 0 is EOF, not an error! */
of bytes fetched. 0 is EOF, not an error! */
/* context will be passed to your source function. */
typedef struct {
int (*source) (void *context, char *buffer, int len);
void *context;
int (*source) (void *context, char *buffer, int len);
void *context;
} gdSource, *gdSourcePtr;
gdImagePtr gdImageCreateFromGifSource(
gdSourcePtr in);
gdImagePtr gdImageCreateFromGifSource(gdSourcePtr in);
gdImagePtr gdImageCreateFromGd(FILE *in);
gdImagePtr gdImageCreateFromGdCtx(gdIOCtxPtr in);
gdImagePtr gdImageCreateFromGd2(FILE *in);
gdImagePtr gdImageCreateFromGd2Ctx(gdIOCtxPtr in);
gdImagePtr gdImageCreateFromGd2Part(FILE *in, int srcx, int srcy, int w, int h);
gdImagePtr gdImageCreateFromGd2PartCtx(gdIOCtxPtr in, int srcx, int srcy, int w, int h);
gdImagePtr gdImageCreateFromXbm(FILE *fd);
void gdImageDestroy(gdImagePtr im);
void gdImageSetPixel(gdImagePtr im, int x, int y, int color);
@ -136,27 +153,36 @@ int gdImageColorClosest(gdImagePtr im, int r, int g, int b);
int gdImageColorExact(gdImagePtr im, int r, int g, int b);
void gdImageColorDeallocate(gdImagePtr im, int color);
void gdImageColorTransparent(gdImagePtr im, int color);
void gdImagePaletteCopy(gdImagePtr dst, gdImagePtr src);
void gdImageGif(gdImagePtr im, FILE *out);
/* A custom data sink. */
/* The sink function must return -1 on error, otherwise the number
of bytes written, which must be equal to len. */
of bytes written, which must be equal to len. */
/* context will be passed to your sink function. */
typedef struct {
int (*sink) (void *context, char *buffer, int len);
void *context;
int (*sink) (void *context, const char *buffer, int len);
void *context;
} gdSink, *gdSinkPtr;
void gdImageGif(gdImagePtr im, FILE *out);
void gdImageGifToSink(gdImagePtr im, gdSinkPtr out);
void gdImageGd(gdImagePtr im, FILE *out);
void gdImageGd2(gdImagePtr im, FILE *out, int cs, int fmt);
void* gdImageGifPtr(gdImagePtr im, int *size);
void gdImageLzw(gdImagePtr im, FILE *out);
void* gdImageLzwPtr(gdImagePtr im, int *size);
void* gdImageGdPtr(gdImagePtr im, int *size);
void* gdImageGd2Ptr(gdImagePtr im, int cs, int fmt, int *size);
void gdImageArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color);
void gdImageFillToBorder(gdImagePtr im, int x, int y, int border, int color);
void gdImageFill(gdImagePtr im, int x, int y, int color);
void gdImageCopy(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h);
void gdImageCopyMerge(gdImagePtr dst, gdImagePtr src, int dstX, int dstY,
int srcX, int srcY, int w, int h, int pct);
void gdImageCopyMergeGray(gdImagePtr dst, gdImagePtr src, int dstX, int dstY,
int srcX, int srcY, int w, int h, int pct);
/* Stretches or shrinks to fit, as needed */
void gdImageCopyResized(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH);
void gdImageSetBrush(gdImagePtr im, gdImagePtr brush);
@ -175,4 +201,14 @@ void gdImageInterlace(gdImagePtr im, int interlaceArg);
#define gdImageBlue(im, c) ((im)->blue[(c)])
#define gdImageGetTransparent(im) ((im)->transparent)
#define gdImageGetInterlaced(im) ((im)->interlace)
#define GD2_CHUNKSIZE 128
#define GD2_CHUNKSIZE_MIN 64
#define GD2_CHUNKSIZE_MAX 4096
#define GD2_VERS 1
#define GD2_ID "gd2"
#define GD2_FMT_RAW 1
#define GD2_FMT_COMPRESSED 2
#endif

55
src/gd2copypal.c Normal file
View File

@ -0,0 +1,55 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .gif file into a .gd file, for
your convenience in creating images on the fly from a
basis image that must be loaded quickly. The .gd format
is not intended to be a general-purpose format. */
int main(int argc, char **argv)
{
gdImagePtr im;
gdImagePtr pal;
FILE *in, *out;
if (argc != 3) {
fprintf(stderr, "Usage: gd2copypal palettefile.gd2 filename.gd2\n");
exit(1);
}
in = fopen(argv[1], "rb");
if (!in) {
fprintf(stderr, "Palette file does not exist!\n");
exit(1);
}
pal = gdImageCreateFromGd2(in);
fclose(in);
if (!pal) {
fprintf(stderr, "Palette is not in GD2 format!\n");
exit(1);
}
in = fopen(argv[2], "rb");
if (!in) {
fprintf(stderr, "Input file does not exist!\n");
exit(1);
}
im = gdImageCreateFromGd2(in);
fclose(in);
if (!im) {
fprintf(stderr, "Input is not in GD2 format!\n");
exit(1);
}
gdImagePaletteCopy(im, pal);
out = fopen(argv[2], "wb");
if (!out) {
fprintf(stderr, "Output file cannot be written to!\n");
gdImageDestroy(im);
exit(1);
}
gdImageGd2(im, out, 128, 2);
fclose(out);
gdImageDestroy(pal);
gdImageDestroy(im);
}

51
src/gd2time.c Normal file
View File

@ -0,0 +1,51 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .gif file into a .gd file, for
your convenience in creating images on the fly from a
basis image that must be loaded quickly. The .gd format
is not intended to be a general-purpose format. */
int main(int argc, char **argv)
{
gdImagePtr im;
FILE *in, *out;
int x, y, w, h;
int c;
int i;
int t0;
if (argc != 7) {
fprintf(stderr, "Usage: gd2time filename.gd count x y w h\n");
exit(1);
}
c = atoi(argv[2]);
x = atoi(argv[3]);
y = atoi(argv[4]);
w = atoi(argv[5]);
h = atoi(argv[6]);
printf("Extracting %d times from (%d, %d), size is %dx%d\n", c, x, y, w, h);
t0 = time(0);
for (i=0; i < c; i++) {
in = fopen(argv[1], "rb");
if (!in) {
fprintf(stderr, "Input file does not exist!\n");
exit(1);
}
im = gdImageCreateFromGd2Part(in, x, y, w, h);
fclose(in);
if (!im) {
fprintf(stderr, "Error reading source file!\n");
exit(1);
}
gdImageDestroy(im);
};
t0 = time(0) - t0;
printf("%d seconds to extract (& destroy) %d times\n",t0, c);
}

38
src/gd2togif.c Normal file
View File

@ -0,0 +1,38 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .gif file into a .gd file, for
your convenience in creating images on the fly from a
basis image that must be loaded quickly. The .gd format
is not intended to be a general-purpose format. */
int main(int argc, char **argv)
{
gdImagePtr im;
FILE *in, *out;
if (argc != 3) {
fprintf(stderr, "Usage: gd2togif filename.gd2 filename.gif\n");
exit(1);
}
in = fopen(argv[1], "rb");
if (!in) {
fprintf(stderr, "Input file does not exist!\n");
exit(1);
}
im = gdImageCreateFromGd2(in);
fclose(in);
if (!im) {
fprintf(stderr, "Input is not in GIF format!\n");
exit(1);
}
out = fopen(argv[2], "wb");
if (!out) {
fprintf(stderr, "Output file cannot be written to!\n");
gdImageDestroy(im);
exit(1);
}
gdImageGif(im, out);
fclose(out);
gdImageDestroy(im);
}

44
src/gd2tolzw.c Normal file
View File

@ -0,0 +1,44 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .gif file into a .gd file, for
your convenience in creating images on the fly from a
basis image that must be loaded quickly. The .gd format
is not intended to be a general-purpose format. */
int main(int argc, char **argv)
{
gdImagePtr im;
FILE *in, *out;
if (argc != 3) {
fprintf(stderr, "Usage: gd2togif filename.gd2 filename.gif\n");
exit(1);
}
in = fopen(argv[1], "rb");
if (!in) {
fprintf(stderr, "Input file does not exist!\n");
exit(1);
}
im = gdImageCreateFromGd2(in);
fclose(in);
if (!im) {
fprintf(stderr, "Input is not in GIF format!\n");
exit(1);
}
out = fopen(argv[2], "wb");
if (!out) {
fprintf(stderr, "Output file cannot be written to!\n");
gdImageDestroy(im);
exit(1);
}
#ifdef LZW_LICENCED
gdImageLzw(im, out);
#else
printf("LZW Not Licenced, using compatible GIF format\n");
gdImageGif(im, out);
#endif
fclose(out);
gdImageDestroy(im);
}

201
src/gd_gd.c Normal file
View File

@ -0,0 +1,201 @@
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "gd.h"
#include "io.h"
#define TRUE 1
#define FALSE 0
/* Exported functions: */
extern void gdImageGd(gdImagePtr im, FILE *out);
/* Use this for commenting out debug-print statements. */
/* Just use the first '#define' to allow all the prints... */
/*#define GD2_DBG(s) (s) */
#define GD2_DBG(s)
/* */
/* Shared code to read color tables from gd file. */
/* */
int _gdGetColors(gdIOCtx *in, gdImagePtr im)
{
int i;
if (!gdGetByte(&im->colorsTotal, in)) {
goto fail1;
}
if (!gdGetWord(&im->transparent, in)) {
goto fail1;
}
if (im->transparent == 257) {
im->transparent = (-1);
}
GD2_DBG(printf("Pallette had %d colours (T=%d)\n",im->colorsTotal, im->transparent));
for (i=0; (i<gdMaxColors); i++) {
if (!gdGetByte(&im->red[i], in)) {
goto fail1;
}
if (!gdGetByte(&im->green[i], in)) {
goto fail1;
}
if (!gdGetByte(&im->blue[i], in)) {
goto fail1;
}
}
for (i=0; (i < im->colorsTotal); i++) {
im->open[i] = 0;
};
return TRUE;
fail1:
return FALSE;
}
/* */
/* Use the common basic header info to make the image object. */
/* This is also called from _gd2CreateFromFile */
/* */
static
gdImagePtr _gdCreateFromFile(gdIOCtx *in, int *sx, int *sy)
{
int x, y;
int i;
gdImagePtr im;
if (!gdGetWord(sx, in)) {
goto fail1;
}
if (!gdGetWord(sy, in)) {
goto fail1;
}
GD2_DBG(printf("Image is %dx%d\n", *sx, *sy));
im = gdImageCreate(*sx, *sy);
if (!_gdGetColors(in, im)) {
goto fail2;
}
return im;
fail2:
gdImageDestroy(im);
fail1:
return 0;
}
gdImagePtr gdImageCreateFromGd(FILE *inFile)
{
gdImagePtr im;
gdIOCtx *in;
in = gdNewFileCtx(inFile);
im = gdImageCreateFromGdCtx(in);
in->free(in);
return im;
}
gdImagePtr gdImageCreateFromGdCtx(gdIOCtxPtr in)
{
int sx, sy;
int x, y;
int i;
gdImagePtr im;
/* Read the header */
im = _gdCreateFromFile(in, &sx, &sy);
if (im == NULL) {
goto fail1;
};
/* Then the data... */
for (y=0; (y<sy); y++) {
for (x=0; (x<sx); x++) {
int ch;
ch = gdGetC(in);
if (ch == EOF) {
goto fail2;
}
/* ROW-MAJOR IN GD 1.3 */
im->pixels[y][x] = ch;
}
}
return im;
fail2:
gdImageDestroy(im);
fail1:
return 0;
}
void _gdPutColors(gdImagePtr im, gdIOCtx *out)
{
int x, y;
int i;
int trans;
gdPutC((unsigned char)im->colorsTotal, out);
trans = im->transparent;
if (trans == (-1)) {
trans = 257;
}
gdPutWord(trans, out);
for (i=0; (i<gdMaxColors); i++) {
gdPutC((unsigned char)im->red[i], out);
gdPutC((unsigned char)im->green[i], out);
gdPutC((unsigned char)im->blue[i], out);
}
}
static
void _gdPutHeader(gdImagePtr im, gdIOCtx *out)
{
gdPutWord(im->sx, out);
gdPutWord(im->sy, out);
_gdPutColors(im, out);
}
static void _gdImageGd(gdImagePtr im, gdIOCtx *out)
{
int x, y;
_gdPutHeader(im, out);
for (y=0; (y < im->sy); y++) {
for (x=0; (x < im->sx); x++) {
/* ROW-MAJOR IN GD 1.3 */
gdPutC((unsigned char)im->pixels[y][x], out);
}
}
}
void gdImageGd(gdImagePtr im, FILE *outFile)
{
gdIOCtx *out = gdNewFileCtx(outFile);
_gdImageGd(im, out);
out->free(out);
}
void* gdImageGdPtr(gdImagePtr im, int *size)
{
void *rv;
gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
_gdImageGd(im, out);
rv = gdDPExtractData(out,size);
out->free(out);
return rv;
}

728
src/gd_gd2.c Normal file
View File

@ -0,0 +1,728 @@
/*
* gd_gd2.c
*
* Implements the I/O and support for the GD2 format.
*
* Changing the definition of GD2_DBG (below) will cause copious messages
* to be displayed while it processes requests.
*
* Designed, Written & Copyright 1999, Philip Warner.
*
*/
#include <stdio.h>
#include <errno.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <zlib.h>
#include "gd.h"
#include "io.h"
#define TRUE 1
#define FALSE 0
/* Use this for commenting out debug-print statements. */
/* Just use the first '#define' to allow all the prints... */
/*#define GD2_DBG(s) (s) */
#define GD2_DBG(s)
typedef struct {
int offset;
int size;
} t_chunk_info;
/* */
/* Read the extra info in the gd2 header. */
/* */
static
int _gd2GetHeader(gdIOCtxPtr in, int *sx, int *sy,
int *cs, int *vers, int *fmt, int *ncx, int *ncy, t_chunk_info **chunkIdx)
{
int i;
int ch;
char id[5];
t_chunk_info *cidx;
int sidx;
int nc, csz;
GD2_DBG(printf("Reading gd2 header info\n"));
for (i = 0; i < 4; i++) {
ch = gdGetC(in);
if (ch == EOF) {
goto fail1;
};
id[i] = ch;
};
id[4] = 0;
GD2_DBG(printf("Got file code: %s\n", id));
/* Equiv. of 'magick'. */
if (strcmp(id,GD2_ID) != 0) {
GD2_DBG(printf("Not a valid gd2 file\n"));
goto fail1;
};
/* Version */
if (gdGetWord(vers, in) != 1) {
goto fail1;
};
GD2_DBG(printf("Version: %d\n", *vers));
if (*vers != 1) {
GD2_DBG(printf("Bad version: %d\n", *vers));
goto fail1;
};
/* Image Size */
if (!gdGetWord(sx, in)) {
GD2_DBG(printf("Could not get x-size\n"));
goto fail1;
}
if (!gdGetWord(sy, in)) {
GD2_DBG(printf("Could not get y-size\n"));
goto fail1;
}
GD2_DBG(printf("Image is %dx%d\n", *sx, *sy));
/* Chunk Size */
if (gdGetWord(cs, in) != 1) {
goto fail1;
};
GD2_DBG(printf("ChunkSize: %d\n", *cs));
if ( (*cs < GD2_CHUNKSIZE_MIN) || (*cs > GD2_CHUNKSIZE_MAX)) {
GD2_DBG(printf("Bad chunk size: %d\n", *cs));
goto fail1;
};
/* Data Format */
if (gdGetWord(fmt, in) != 1) {
goto fail1;
};
GD2_DBG(printf("Format: %d\n", *fmt));
if ((*fmt != GD2_FMT_RAW) && (*fmt != GD2_FMT_COMPRESSED)) {
GD2_DBG(printf("Bad data format: %d\n", *fmt));
goto fail1;
};
/* # of chunks wide */
if (gdGetWord(ncx, in) != 1) {
goto fail1;
};
GD2_DBG(printf("%d Chunks Wide\n", *ncx));
/* # of chunks high */
if (gdGetWord(ncy, in) != 1) {
goto fail1;
};
GD2_DBG(printf("%d Chunks vertically\n", *ncy));
if ((*fmt) == GD2_FMT_COMPRESSED) {
nc = (*ncx) * (*ncy);
GD2_DBG(printf("Reading %d chunk index entries\n", nc));
sidx = sizeof(t_chunk_info) * nc;
cidx = calloc(sidx,1);
for (i = 0; i < nc; i++) {
if (gdGetInt(&cidx[i].offset, in) != 1) {
goto fail1;
};
if (gdGetInt(&cidx[i].size, in) != 1) {
goto fail1;
};
};
*chunkIdx = cidx;
};
GD2_DBG(printf("gd2 header complete\n"));
return 1;
fail1:
return 0;
}
static
gdImagePtr _gd2CreateFromFile(gdIOCtxPtr in, int *sx, int *sy,
int *cs, int *vers, int *fmt,
int *ncx, int *ncy, t_chunk_info **cidx)
{
gdImagePtr im;
if (_gd2GetHeader(in, sx, sy, cs, vers, fmt, ncx, ncy, cidx) != 1) {
GD2_DBG(printf("Bad GD2 header\n"));
goto fail1;
}
im = gdImageCreate(*sx, *sy);
if (im == NULL) {
GD2_DBG(printf("Could not create gdImage\n"));
goto fail1;
};
if (!_gdGetColors(in, im)) {
GD2_DBG(printf("Could not read color palette\n"));
goto fail2;
}
GD2_DBG(printf("Image palette completed\n"));
return im;
fail2:
gdImageDestroy(im);
return 0;
fail1:
return 0;
}
static
int _gd2ReadChunk(int offset, char *compBuf, int compSize, char *chunkBuf, uLongf *chunkLen, gdIOCtx *in)
{
int zerr;
if (gdTell(in) != offset) {
GD2_DBG(printf("Positioning in file to %d\n",offset));
gdSeek(in,offset);
} else {
GD2_DBG(printf("Already Positioned in file to %d\n",offset));
};
/* Read and uncompress an entire chunk. */
GD2_DBG(printf("Reading file\n"));
if (gdGetBuf(compBuf, compSize, in) != compSize) {
return FALSE;
};
GD2_DBG(printf("Got %d bytes. Uncompressing into buffer of %d bytes\n", compSize, *chunkLen));
zerr = uncompress(chunkBuf, chunkLen, compBuf, compSize);
if (zerr != Z_OK) {
GD2_DBG(printf("Error %d from uncompress\n",zerr));
return FALSE;
};
GD2_DBG(printf("Got chunk\n"));
return TRUE;
};
gdImagePtr gdImageCreateFromGd2(FILE *inFile)
{
gdIOCtx *in = gdNewFileCtx(inFile);
gdImagePtr im;
im = gdImageCreateFromGd2Ctx(in);
in->free(in);
return im;
}
gdImagePtr gdImageCreateFromGd2Ctx(gdIOCtxPtr in)
{
int sx, sy;
int i;
int ncx, ncy, nc, cs, cx, cy;
int x, y, ylo, yhi, xlo, xhi;
int ch, vers, fmt;
t_chunk_info *chunkIdx = NULL; /* So we can free it with impunity. */
char *chunkBuf = NULL; /* So we can free it with impunity. */
int chunkNum = 0;
int chunkMax;
uLongf chunkLen;
int chunkPos;
int compMax;
char *compBuf = NULL; /* So we can free it with impunity. */
gdImagePtr im;
/* Get the header */
im = _gd2CreateFromFile(in, &sx, &sy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx);
if (im == NULL) {
return 0;
};
nc = ncx * ncy;
if (fmt == GD2_FMT_COMPRESSED) {
/* Find the maximum compressed chunk size. */
compMax = 0;
for (i=0; (i < nc) ; i++) {
if ( chunkIdx[i].size > compMax) {
compMax = chunkIdx[i].size;
};
};
compMax++;
/* Allocate buffers */
chunkMax = cs * cs;
chunkBuf = calloc(chunkMax,1);
compBuf = calloc(compMax,1);
GD2_DBG(printf("Largest compressed chunk is %d bytes\n",compMax));
};
/* if ( (ncx != sx / cs) || (ncy != sy / cs)) { */
/* goto fail2; */
/* }; */
/* Read the data... */
for (cy=0; (cy < ncy); cy++) {
for (cx=0; (cx < ncx); cx++) {
ylo = cy * cs;
yhi = ylo + cs;
if (yhi > im->sy) {
yhi = im->sy;
};
GD2_DBG(printf("Processing Chunk %d (%d, %d), y from %d to %d\n",chunkNum, cx, cy, ylo, yhi));
if (fmt == GD2_FMT_COMPRESSED) {
chunkLen = chunkMax;
if (!_gd2ReadChunk( chunkIdx[chunkNum].offset,
compBuf,
chunkIdx[chunkNum].size,
chunkBuf, &chunkLen, in))
{
GD2_DBG(printf("Error reading comproessed chunk\n"));
goto fail2;
};
chunkPos = 0;
};
for (y= ylo ; (y < yhi); y++) {
xlo = cx * cs;
xhi = xlo + cs;
if (xhi > im->sx) {
xhi = im->sx;
};
/*GD2_DBG(printf("y=%d: ",y)); */
if (fmt == GD2_FMT_RAW) {
for (x= xlo ; x < xhi; x++) {
ch = gdGetC(in);
if (ch == EOF) {
ch = 0;
/*printf("EOF while reading\n"); */
/*gdImageDestroy(im); */
/*return 0; */
}
/*GD2_DBG(printf(" (%d, %d)", x, y)); */
im->pixels[y][x] = ch;
}
} else {
for (x= xlo ; x < xhi; x++) {
im->pixels[y][x] = chunkBuf[chunkPos++];
};
};
/*GD2_DBG(printf("\n")); */
};
chunkNum++;
};
};
GD2_DBG(printf("Freeing memory\n"));
free(chunkBuf);
free(compBuf);
free(chunkIdx);
GD2_DBG(printf("Done\n"));
return im;
fail2:
gdImageDestroy(im);
fail1:
free(chunkBuf);
free(compBuf);
free(chunkIdx);
return 0;
}
gdImagePtr gdImageCreateFromGd2Part(FILE *inFile, int srcx, int srcy, int w, int h)
{
gdImagePtr im;
gdIOCtx *in = gdNewFileCtx(inFile);
im = gdImageCreateFromGd2PartCtx(in, srcx, srcy, w, h);
in->free(in);
return im;
}
gdImagePtr gdImageCreateFromGd2PartCtx(gdIOCtx *in, int srcx, int srcy, int w, int h)
{
int sx, sy, scx, scy, ecx, ecy, fsx, fsy;
int nc, ncx, ncy, cs, cx, cy;
int x, y, ylo, yhi, xlo, xhi;
int dstart, dpos;
int i;
int ch, vers, fmt;
t_chunk_info *chunkIdx = NULL;
char *chunkBuf = NULL;
int chunkNum;
int chunkMax;
uLongf chunkLen;
int chunkPos;
int compMax;
char *compBuf = NULL;
gdImagePtr im;
/* */
/* The next few lines are basically copied from gd2CreateFromFile */
/* - we change the file size, so don't want to use the code directly. */
/* but we do need to know the file size. */
/* */
if (_gd2GetHeader(in, &fsx, &fsy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx) != 1) {
goto fail1;
}
GD2_DBG(printf("File size is %dx%d\n", fsx, fsy));
/* This is the difference - make a file based on size of chunks. */
im = gdImageCreate(w, h);
if (im == NULL) {
goto fail1;
};
if (!_gdGetColors(in, im)) {
goto fail2;
}
/* Process the header info */
nc = ncx * ncy;
if (fmt == GD2_FMT_COMPRESSED) {
/* Find the maximum compressed chunk size. */
compMax = 0;
for (i=0; (i < nc) ; i++) {
if ( chunkIdx[i].size > compMax) {
compMax = chunkIdx[i].size;
};
};
compMax++;
chunkMax = cs * cs;
chunkBuf = calloc(chunkMax,1);
compBuf = calloc(compMax,1);
};
/* Don't bother with this... */
/* if ( (ncx != sx / cs) || (ncy != sy / cs)) { */
/* goto fail2; */
/* }; */
/* Work out start/end chunks */
scx = srcx / cs;
scy = srcy / cs;
if (scx < 0) { scx = 0;};
if (scy < 0) { scy = 0;};
ecx = (srcx + w) / cs;
ecy = (srcy + h) / cs;
if (ecx >= ncx) { ecx = ncx - 1;};
if (ecy >= ncy) { ecy = ncy - 1;};
/* Remember file position of image data. */
dstart = gdTell(in);
GD2_DBG(printf("Data starts at %d\n",dstart));
/* Loop through the chunks. */
for (cy=scy ; (cy <= ecy); cy++) {
ylo = cy * cs;
yhi = ylo + cs;
if (yhi > fsy) {
yhi = fsy;
};
for (cx=scx; (cx <= ecx); cx++) {
xlo = cx * cs;
xhi = xlo + cs;
if (xhi > fsx) {
xhi = fsx;
};
GD2_DBG(printf("Processing Chunk (%d, %d), from %d to %d\n",cx, cy, ylo, yhi));
if (fmt == GD2_FMT_RAW) {
GD2_DBG(printf("Using raw format data\n"));
dpos = cy * (cs * fsx) + cx * cs * (yhi-ylo) + dstart;
if (gdSeek(in, dpos) != 0) {
printf("Error from seek: %d\n",errno);
goto fail2;
};
GD2_DBG(printf("Reading (%d, %d) from position %d\n",cx,cy,dpos-dstart));
} else {
chunkNum = cx + cy * ncx;
chunkLen = chunkMax;
if (!_gd2ReadChunk( chunkIdx[chunkNum].offset,
compBuf,
chunkIdx[chunkNum].size,
chunkBuf, &chunkLen, in))
{
printf("Error reading comproessed chunk\n");
goto fail2;
};
chunkPos= 0;
GD2_DBG(printf("Reading (%d, %d) from chunk %d\n",cx,cy,chunkNum));
};
GD2_DBG(printf(" into (%d, %d) - (%d, %d)\n", xlo, ylo, xhi, yhi));
for (y=ylo ; (y < yhi); y++) {
for (x= xlo ; x < xhi; x++) {
if (fmt == GD2_FMT_RAW) {
ch = gdGetC(in);
if (ch == EOF) {
ch = 0;
/*printf("EOF while reading file\n"); */
/*goto fail2; */
};
} else {
ch = chunkBuf[chunkPos++];
};
/* Only use a point that is in the image. */
if ( (x >= srcx) && (x < (srcx + w)) && (x < fsx) && (x >= 0)
&& (y >= srcy) && (y < (srcy + h)) && (y < fsy) && (y >= 0)
)
{
im->pixels[y-srcy][x-srcx] = ch;
}
};
};
};
};
free(chunkBuf);
free(compBuf);
free(chunkIdx);
return im;
fail2:
gdImageDestroy(im);
fail1:
free(chunkBuf);
free(compBuf);
free(chunkIdx);
return 0;
}
static
void _gd2PutHeader(gdImagePtr im, gdIOCtx *out, int cs, int fmt, int cx, int cy)
{
int i;
/* Send the gd2 id, to verify file format. */
for (i = 0; i < 4; i++) {
gdPutC((unsigned char)(GD2_ID[i]), out);
};
/* */
/* We put the version info first, so future versions can easily change header info. */
/* */
gdPutWord(GD2_VERS, out);
gdPutWord(im->sx, out);
gdPutWord(im->sy, out);
gdPutWord(cs, out);
gdPutWord(fmt, out);
gdPutWord(cx, out);
gdPutWord(cy, out);
}
static void _gdImageGd2(gdImagePtr im, gdIOCtx *out, int cs, int fmt)
{
int ncx, ncy, cx, cy;
int x, y, ylo, yhi, xlo, xhi;
int i;
int chunkLen;
int chunkNum = 0;
char *chunkData = NULL; /* So we can free it with impunity. */
char *compData = NULL; /* So we can free it with impunity. */
uLongf compLen;
int idxPos;
int idxSize;
t_chunk_info *chunkIdx = NULL;
int posSave;
int compMax;
/*printf("Trying to write GD2 file\n"); */
/* */
/* Force fmt to a valid value since we don't return anything. */
/* */
if ( (fmt == 0) || ( (fmt != GD2_FMT_RAW) && (fmt != GD2_FMT_COMPRESSED) ) ) {
fmt == GD2_FMT_COMPRESSED;
};
/* */
/* Make sure chunk size is valid. These are arbitrary values; 64 because it seems */
/* a little silly to expect performance improvements on a 64x64 bit scale, and */
/* 4096 because we buffer one chunk, and a 16MB buffer seems a little largei - it may be */
/* OK for one user, but for another to read it, they require the buffer. */
/* */
if (cs == 0) {
cs = GD2_CHUNKSIZE;
} else if (cs < GD2_CHUNKSIZE_MIN) {
cs = GD2_CHUNKSIZE_MIN;
} else if (cs > GD2_CHUNKSIZE_MAX) {
cs = GD2_CHUNKSIZE_MAX;
};
/* Work out number of chunks. */
ncx = im->sx / cs + 1;
ncy = im->sy / cs + 1;
/* Write the standard header. */
_gd2PutHeader(im, out, cs, fmt, ncx, ncy);
if (fmt == GD2_FMT_COMPRESSED) {
/* */
/* Work out size of buffer for compressed data, If CHUNKSIZE is large, */
/* then these will be large! */
/* */
/* The zlib notes say output buffer size should be (input size) * 1.01 * 12 */
/* - we'll use 1.02 to be paranoid. */
/* */
compMax = cs * cs * 1.02 + 12;
/* */
/* Allocate the buffers. */
/* */
chunkData = calloc(cs*cs,1);
compData = calloc(compMax,1);
/* */
/* Save the file position of chunk index, and allocate enough space for */
/* each chunk_info block . */
/* */
idxPos = gdTell(out);
idxSize = ncx * ncy * sizeof(t_chunk_info);
GD2_DBG(printf("Index size is %d\n", idxSize));
gdSeek(out,idxPos+idxSize);
chunkIdx = calloc(idxSize * sizeof(t_chunk_info),1);
};
_gdPutColors(im, out);
GD2_DBG(printf("Size: %dx%d\n", im->sx, im->sy));
GD2_DBG(printf("Chunks: %dx%d\n", ncx, ncy));
for (cy=0; (cy < ncy); cy++) {
for (cx=0; (cx < ncx); cx++) {
ylo = cy * cs;
yhi = ylo + cs;
if (yhi > im->sy) {
yhi = im->sy;
};
GD2_DBG(printf("Processing Chunk (%dx%d), y from %d to %d\n",cx, cy, ylo, yhi));
chunkLen = 0;
for (y= ylo ; (y < yhi); y++) {
/*GD2_DBG(printf("y=%d: ",y)); */
xlo = cx * cs;
xhi = xlo + cs;
if (xhi > im->sx) {
xhi = im->sx;
};
if (fmt == GD2_FMT_COMPRESSED) {
for (x= xlo ; x < xhi; x++) {
/*GD2_DBG(printf("%d...",x)); */
chunkData[chunkLen++] = im->pixels[y][x];
};
} else {
for (x= xlo ; x < xhi; x++) {
/*GD2_DBG(printf("%d, ",x)); */
gdPutC((unsigned char)im->pixels[y][x], out);
};
};
/*GD2_DBG(printf("y=%d done.\n",y)); */
};
if (fmt == GD2_FMT_COMPRESSED) {
compLen = compMax;
if (compress(&compData[0], &compLen, &chunkData[0], chunkLen) != Z_OK) {
printf("Error from compressing\n");
} else {
chunkIdx[chunkNum].offset = gdTell(out);
chunkIdx[chunkNum++].size = compLen;
GD2_DBG(printf("Chunk %d size %d offset %d\n",chunkNum, chunkIdx[chunkNum-1].size, chunkIdx[chunkNum-1].offset));
if (gdPutBuf(compData, compLen, out) <= 0) {
/* Any alternate suggestions for handling this? */
printf("Error %d on write\n", errno);
};
};
};
};
};
if (fmt == GD2_FMT_COMPRESSED) {
/* Save the position, write the index, restore position (paranoia). */
GD2_DBG(printf("Seeking %d to write index\n",idxPos));
posSave = gdTell(out);
gdSeek(out,idxPos);
GD2_DBG(printf("Writing index\n"));
for (x = 0; x < chunkNum; x++) {
GD2_DBG(printf("Chunk %d size %d offset %d\n", x, chunkIdx[x].size, chunkIdx[x].offset));
gdPutInt(chunkIdx[x].offset, out);
gdPutInt(chunkIdx[x].size, out);
};
/* We don't use fwrite for *endian reasons. */
/*fwrite(chunkIdx, sizeof(int)*2, chunkNum, out); */
gdSeek(out, posSave);
};
GD2_DBG(printf("Freeing memory\n"));
free(chunkData);
free(compData);
free(chunkIdx);
GD2_DBG(printf("Done\n"));
/*printf("Memory block size is %d\n",gdTell(out)); */
}
void gdImageGd2(gdImagePtr im, FILE *outFile, int cs, int fmt)
{
gdIOCtx *out = gdNewFileCtx(outFile);
_gdImageGd2(im, out, cs, fmt);
out->free(out);
}
void* gdImageGd2Ptr(gdImagePtr im, int cs, int fmt, int *size)
{
void *rv;
gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
_gdImageGd2(im, out, cs, fmt);
rv = gdDPExtractData(out, size);
out->free(out);
return rv;
}

562
src/gd_gif_in.c Normal file
View File

@ -0,0 +1,562 @@
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "gd.h"
#include "io.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
gdImageCreateFromGif(FILE *fdFile)
{
gdIOCtx *fd = gdNewFileCtx(fdFile);
gdImagePtr im = 0;
im = gdImageCreateFromGifCtx(fd);
fd->free(fd);
return im;
}
gdImagePtr
gdImageCreateFromGifCtx(gdIOCtxPtr fd)
{
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 */
}
}

813
src/gd_gif_out.c Normal file
View File

@ -0,0 +1,813 @@
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "gd.h"
#include "io.h"
/* Code drawn from ppmtogif.c, from the pbmplus package
**
** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>. A
** Lempel-Zim compression based on "compress".
**
** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl>
**
** Copyright (C) 1989 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
**
** The Graphics Interchange Format(c) is the Copyright property of
** CompuServe Incorporated. GIF(sm) is a Service Mark property of
** CompuServe Incorporated.
*
* Heavily modified by Mouse, 1998-02-12.
* Remove LZW compression.
* Added miGIF run length compression.
*
*/
/*
* a code_int must be able to hold 2**GIFBITS values of type int, and also -1
*/
typedef int code_int;
static int colorstobpp(int colors);
static void BumpPixel (void);
static int GIFNextPixel (gdImagePtr im);
static void GIFEncode (gdIOCtx *fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im);
/*static void Putword (int w, gdIOCtx *fp); */
static void GIFcompress (int, gdIOCtx *, gdImagePtr, int);
static void output (code_int code);
static void char_init (void);
static void char_out (int c);
/* Allows for reuse */
static void init_statics(void);
static void _gdImageGif(gdImagePtr im, gdIOCtx *out)
{
int interlace, transparent, BitsPerPixel;
interlace = im->interlace;
transparent = im->transparent;
BitsPerPixel = colorstobpp(im->colorsTotal);
/* Clear any old values in statics strewn through the GIF code */
init_statics();
/* All set, let's do it. */
GIFEncode(
out, im->sx, im->sy, interlace, 0, transparent, BitsPerPixel,
im->red, im->green, im->blue, im);
}
void gdImageGif(gdImagePtr im, FILE *outFile)
{
gdIOCtx *out = gdNewFileCtx(outFile);
_gdImageGif(im, out);
out->free(out);
}
void* gdImageGifPtr(gdImagePtr im, int *size)
{
void *rv;
gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
_gdImageGif(im, out);
rv = gdDPExtractData(out,size);
out->free(out);
return rv;
}
static int
colorstobpp(int colors)
{
int bpp = 0;
if ( colors <= 2 )
bpp = 1;
else if ( colors <= 4 )
bpp = 2;
else if ( colors <= 8 )
bpp = 3;
else if ( colors <= 16 )
bpp = 4;
else if ( colors <= 32 )
bpp = 5;
else if ( colors <= 64 )
bpp = 6;
else if ( colors <= 128 )
bpp = 7;
else if ( colors <= 256 )
bpp = 8;
return bpp;
}
/*****************************************************************************
*
* GIFENCODE.C - GIF Image compression interface
*
* GIFEncode( FName, GHeight, GWidth, GInterlace, Background, Transparent,
* BitsPerPixel, Red, Green, Blue, gdImagePtr )
*
*****************************************************************************/
#define TRUE 1
#define FALSE 0
static int Width, Height;
static int curx, cury;
static long CountDown;
static int Pass = 0;
static int Interlace;
/*
* Bump the 'curx' and 'cury' to point to the next pixel
*/
static void
BumpPixel(void)
{
/*
* Bump the current X position
*/
++curx;
/*
* If we are at the end of a scan line, set curx back to the beginning
* If we are interlaced, bump the cury to the appropriate spot,
* otherwise, just increment it.
*/
if( curx == Width ) {
curx = 0;
if( !Interlace )
++cury;
else {
switch( Pass ) {
case 0:
cury += 8;
if( cury >= Height ) {
++Pass;
cury = 4;
}
break;
case 1:
cury += 8;
if( cury >= Height ) {
++Pass;
cury = 2;
}
break;
case 2:
cury += 4;
if( cury >= Height ) {
++Pass;
cury = 1;
}
break;
case 3:
cury += 2;
break;
}
}
}
}
/*
* Return the next pixel from the image
*/
static int
GIFNextPixel(gdImagePtr im)
{
int r;
if( CountDown == 0 )
return EOF;
--CountDown;
r = gdImageGetPixel(im, curx, cury);
BumpPixel();
return r;
}
/* public */
static void
GIFEncode(gdIOCtx *fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im)
{
int B;
int RWidth, RHeight;
int LeftOfs, TopOfs;
int Resolution;
int ColorMapSize;
int InitCodeSize;
int i;
Interlace = GInterlace;
ColorMapSize = 1 << BitsPerPixel;
RWidth = Width = GWidth;
RHeight = Height = GHeight;
LeftOfs = TopOfs = 0;
Resolution = BitsPerPixel;
/*
* Calculate number of bits we are expecting
*/
CountDown = (long)Width * (long)Height;
/*
* Indicate which pass we are on (if interlace)
*/
Pass = 0;
/*
* The initial code size
*/
if( BitsPerPixel <= 1 )
InitCodeSize = 2;
else
InitCodeSize = BitsPerPixel;
/*
* Set up the current x and y position
*/
curx = cury = 0;
/*
* Write the Magic header
*/
gdPutBuf( Transparent < 0 ? "GIF87a" : "GIF89a", 6, fp );
/*
* Write out the screen width and height
*/
Putword( RWidth, fp );
Putword( RHeight, fp );
/*
* Indicate that there is a global colour map
*/
B = 0x80; /* Yes, there is a color map */
/*
* OR in the resolution
*/
B |= (Resolution - 1) << 4;
/*
* OR in the Bits per Pixel
*/
B |= (BitsPerPixel - 1);
/*
* Write it out
*/
gdPutC( B, fp );
/*
* Write out the Background colour
*/
gdPutC( Background, fp );
/*
* Byte of 0's (future expansion)
*/
gdPutC( 0, fp );
/*
* Write out the Global Colour Map
*/
for( i=0; i<ColorMapSize; ++i ) {
gdPutC( Red[i], fp );
gdPutC( Green[i], fp );
gdPutC( Blue[i], fp );
}
/*
* Write out extension for transparent colour index, if necessary.
*/
if ( Transparent >= 0 ) {
gdPutC( '!', fp );
gdPutC( 0xf9, fp );
gdPutC( 4, fp );
gdPutC( 1, fp );
gdPutC( 0, fp );
gdPutC( 0, fp );
gdPutC( (unsigned char) Transparent, fp );
gdPutC( 0, fp );
}
/*
* Write an Image separator
*/
gdPutC( ',', fp );
/*
* Write the Image header
*/
Putword( LeftOfs, fp );
Putword( TopOfs, fp );
Putword( Width, fp );
Putword( Height, fp );
/*
* Write out whether or not the image is interlaced
*/
if( Interlace )
gdPutC( 0x40, fp );
else
gdPutC( 0x00, fp );
/*
* Write out the initial code size
*/
gdPutC( InitCodeSize, fp );
/*
* Go and actually compress the data
*/
GIFcompress( InitCodeSize+1, fp, im, Background );
/*
* Write out a Zero-length packet (to end the series)
*/
gdPutC( 0, fp );
/*
* Write the GIF file terminator
*/
gdPutC( ';', fp );
}
/* Write out a word to the GIF file */
/*static void */
/*Putword(int w, gdIOCtx *fp) */
/*{ */
/* fputc( w & 0xff, fp ); */
/* fputc( (w / 256) & 0xff, fp ); */
/*} */
#define GIFBITS 12
/*-----------------------------------------------------------------------
*
* miGIF Compression - mouse and ivo's GIF-compatible compression
*
* -run length encoding compression routines-
*
* Copyright (C) 1998 Hutchison Avenue Software Corporation
* http://www.hasc.com
* info@hasc.com
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. This software is provided "AS IS." The Hutchison Avenue
* Software Corporation disclaims all warranties, either express or implied,
* including but not limited to implied warranties of merchantability and
* fitness for a particular purpose, with respect to this code and accompanying
* documentation.
*
* The miGIF compression routines do not, strictly speaking, generate files
* conforming to the GIF spec, since the image data is not LZW-compressed
* (this is the point: in order to avoid transgression of the Unisys patent
* on the LZW algorithm.) However, miGIF generates data streams that any
* reasonably sane LZW decompresser will decompress to what we want.
*
* miGIF compression uses run length encoding. It compresses horizontal runs
* of pixels of the same color. This type of compression gives good results
* on images with many runs, for example images with lines, text and solid
* shapes on a solid-colored background. It gives little or no compression
* on images with few runs, for example digital or scanned photos.
*
* der Mouse
* mouse@rodents.montreal.qc.ca
* 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B
*
* ivo@hasc.com
*
* The Graphics Interchange Format(c) is the Copyright property of
* CompuServe Incorporated. GIF(sm) is a Service Mark property of
* CompuServe Incorporated.
*
*/
static int rl_pixel;
static int rl_basecode;
static int rl_count;
static int rl_table_pixel;
static int rl_table_max;
static int just_cleared;
static int out_bits;
static int out_bits_init;
static int out_count;
static int out_bump;
static int out_bump_init;
static int out_clear;
static int out_clear_init;
static int max_ocodes;
static int code_clear;
static int code_eof;
static unsigned int obuf;
static int obits;
static gdIOCtx *ofile;
static unsigned char oblock[256];
static int oblen;
/* 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
static const char *binformat(unsigned int v, int nbits)
{
static char bufs[8][64];
static int bhand = 0;
unsigned int bit;
int bno;
char *bp;
bhand --;
if (bhand < 0) bhand = (sizeof(bufs)/sizeof(bufs[0]))-1;
bp = &bufs[bhand][0];
for (bno=nbits-1,bit=1U<<bno;bno>=0;bno--,bit>>=1)
{ *bp++ = (v & bit) ? '1' : '0';
if (((bno&3) == 0) && (bno != 0)) *bp++ = '.';
}
*bp = '\0';
return(&bufs[bhand][0]);
}
static void write_block(void)
{
int i;
if (VERBOSE)
{ printf("write_block %d:",oblen);
for (i=0;i<oblen;i++) printf(" %02x",oblock[i]);
printf("\n");
}
gdPutC(oblen,ofile);
gdPutBuf(&oblock[0],oblen,ofile);
oblen = 0;
}
static void block_out(unsigned char c)
{
if (VERBOSE) printf("block_out %s\n",binformat(c,8));
oblock[oblen++] = c;
if (oblen >= 255) write_block();
}
static void block_flush(void)
{
if (VERBOSE) printf("block_flush\n");
if (oblen > 0) write_block();
}
static void output(int val)
{
if (VERBOSE) printf("output %s [%s %d %d]\n",binformat(val,out_bits),binformat(obuf,obits),obits,out_bits);
obuf |= val << obits;
obits += out_bits;
while (obits >= 8)
{ block_out(obuf&0xff);
obuf >>= 8;
obits -= 8;
}
if (VERBOSE) printf("output leaving [%s %d]\n",binformat(obuf,obits),obits);
}
static void output_flush(void)
{
if (VERBOSE) printf("output_flush\n");
if (obits > 0) block_out(obuf);
block_flush();
}
static void did_clear(void)
{
if (VERBOSE) printf("did_clear\n");
out_bits = out_bits_init;
out_bump = out_bump_init;
out_clear = out_clear_init;
out_count = 0;
rl_table_max = 0;
just_cleared = 1;
}
static void output_plain(int c)
{
if (VERBOSE) printf("output_plain %s\n",binformat(c,out_bits));
just_cleared = 0;
output(c);
out_count ++;
if (out_count >= out_bump)
{ out_bits ++;
out_bump += 1 << (out_bits - 1);
}
if (out_count >= out_clear)
{ output(code_clear);
did_clear();
}
}
static unsigned int isqrt(unsigned int x)
{
unsigned int r;
unsigned int v;
if (x < 2) return(x);
for (v=x,r=1;v;v>>=2,r<<=1) ;
while (1)
{ v = ((x / r) + r) / 2;
if ((v == r) || (v == r+1)) return(r);
r = v;
}
}
static unsigned int compute_triangle_count(unsigned int count, unsigned int nrepcodes)
{
unsigned int perrep;
unsigned int cost;
cost = 0;
perrep = (nrepcodes * (nrepcodes+1)) / 2;
while (count >= perrep)
{ cost += nrepcodes;
count -= perrep;
}
if (count > 0)
{ unsigned int n;
n = isqrt(count);
while ((n*(n+1)) >= 2*count) n --;
while ((n*(n+1)) < 2*count) n ++;
cost += n;
}
return(cost);
}
static void max_out_clear(void)
{
out_clear = max_ocodes;
}
static void reset_out_clear(void)
{
out_clear = out_clear_init;
if (out_count >= out_clear)
{ output(code_clear);
did_clear();
}
}
static void rl_flush_fromclear(int count)
{
int n;
if (VERBOSE) printf("rl_flush_fromclear %d\n",count);
max_out_clear();
rl_table_pixel = rl_pixel;
n = 1;
while (count > 0)
{ if (n == 1)
{ rl_table_max = 1;
output_plain(rl_pixel);
count --;
}
else if (count >= n)
{ rl_table_max = n;
output_plain(rl_basecode+n-2);
count -= n;
}
else if (count == 1)
{ rl_table_max ++;
output_plain(rl_pixel);
count = 0;
}
else
{ rl_table_max ++;
output_plain(rl_basecode+count-2);
count = 0;
}
if (out_count == 0) n = 1; else n ++;
}
reset_out_clear();
if (VERBOSE) printf("rl_flush_fromclear leaving table_max=%d\n",rl_table_max);
}
static void rl_flush_clearorrep(int count)
{
int withclr;
if (VERBOSE) printf("rl_flush_clearorrep %d\n",count);
withclr = 1 + compute_triangle_count(count,max_ocodes);
if (withclr < count)
{ output(code_clear);
did_clear();
rl_flush_fromclear(count);
}
else
{ for (;count>0;count--) output_plain(rl_pixel);
}
}
static void rl_flush_withtable(int count)
{
int repmax;
int repleft;
int leftover;
if (VERBOSE) printf("rl_flush_withtable %d\n",count);
repmax = count / rl_table_max;
leftover = count % rl_table_max;
repleft = (leftover ? 1 : 0);
if (out_count+repmax+repleft > max_ocodes)
{ repmax = max_ocodes - out_count;
leftover = count - (repmax * rl_table_max);
repleft = 1 + compute_triangle_count(leftover,max_ocodes);
}
if (VERBOSE) printf("rl_flush_withtable repmax=%d leftover=%d repleft=%d\n",repmax,leftover,repleft);
if (1+compute_triangle_count(count,max_ocodes) < repmax+repleft)
{ output(code_clear);
did_clear();
rl_flush_fromclear(count);
return;
}
max_out_clear();
for (;repmax>0;repmax--) output_plain(rl_basecode+rl_table_max-2);
if (leftover)
{ if (just_cleared)
{ rl_flush_fromclear(leftover);
}
else if (leftover == 1)
{ output_plain(rl_pixel);
}
else
{ output_plain(rl_basecode+leftover-2);
}
}
reset_out_clear();
}
static void rl_flush(void)
{
int table_reps;
int table_extra;
if (VERBOSE) printf("rl_flush [ %d %d\n",rl_count,rl_pixel);
if (rl_count == 1)
{ output_plain(rl_pixel);
rl_count = 0;
if (VERBOSE) printf("rl_flush ]\n");
return;
}
if (just_cleared)
{ rl_flush_fromclear(rl_count);
}
else if ((rl_table_max < 2) || (rl_table_pixel != rl_pixel))
{ rl_flush_clearorrep(rl_count);
}
else
{ rl_flush_withtable(rl_count);
}
if (VERBOSE) printf("rl_flush ]\n");
rl_count = 0;
}
static void GIFcompress(int init_bits, gdIOCtx *outfile, gdImagePtr im, int background)
{
int c;
ofile = outfile;
obuf = 0;
obits = 0;
oblen = 0;
code_clear = 1 << (init_bits - 1);
code_eof = code_clear + 1;
rl_basecode = code_eof + 1;
out_bump_init = (1 << (init_bits - 1)) - 1;
/* for images with a lot of runs, making out_clear_init larger will
give better compression. */
out_clear_init = (init_bits <= 3) ? 9 : (out_bump_init-1);
#ifdef DEBUGGING_ENVARS
{ const char *ocienv;
ocienv = getenv("GIF_OUT_CLEAR_INIT");
if (ocienv)
{ out_clear_init = atoi(ocienv);
if (VERBOSE) printf("[overriding out_clear_init to %d]\n",out_clear_init);
}
}
#endif
out_bits_init = init_bits;
max_ocodes = (1 << GIFBITS) - ((1 << (out_bits_init - 1)) + 3);
did_clear();
output(code_clear);
rl_count = 0;
while (1)
{ c = GIFNextPixel(im);
if ((rl_count > 0) && (c != rl_pixel)) rl_flush();
if (c == EOF) break;
if (rl_pixel == c)
{ rl_count ++;
}
else
{ rl_pixel = c;
rl_count = 1;
}
}
output(code_eof);
output_flush();
}
/*-----------------------------------------------------------------------
*
* End of miGIF section - See copyright notice at start of section.
*
/*-----------------------------------------------------------------------
/******************************************************************************
*
* GIF Specific routines
*
******************************************************************************/
/*
* Number of characters so far in this 'packet'
*/
static int a_count;
/*
* Set up the 'byte output' routine
*/
static void
char_init(void)
{
a_count = 0;
}
/*
* Define the storage for the packet accumulator
*/
static char accum[ 256 ];
static void init_statics(void) {
/* Some of these are properly initialized later. What I'm doing
here is making sure code that depends on C's initialization
of statics doesn't break when the code gets called more
than once. */
Width = 0;
Height = 0;
curx = 0;
cury = 0;
CountDown = 0;
Pass = 0;
Interlace = 0;
a_count = 0;
}
/* +-------------------------------------------------------------------+ */
/* | Copyright 1990, 1991, 1993, David Koblas. (koblas@netcom.com) | */
/* | Permission to use, copy, modify, and distribute this software | */
/* | and its documentation for any purpose and without fee is hereby | */
/* | granted, provided that the above copyright notice appear in all | */
/* | copies and that both that copyright notice and this permission | */
/* | notice appear in supporting documentation. This software is | */
/* | provided "as is" without express or implied warranty. | */
/* +-------------------------------------------------------------------+ */

801
src/gd_lzw_out.c Normal file
View File

@ -0,0 +1,801 @@
#include <malloc.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "gd.h"
#include "io.h"
#ifdef LZW_LICENCED
/* Code drawn from ppmtogif.c, from the pbmplus package
**
** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>. A
** Lempel-Zim compression based on "compress".
**
** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl>
**
** Copyright (C) 1989 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
**
** The Graphics Interchange Format(c) is the Copyright property of
** CompuServe Incorporated. GIF(sm) is a Service Mark property of
** CompuServe Incorporated.
*/
/*
* a code_int must be able to hold 2**GIFBITS values of type int, and also -1
*/
typedef int code_int;
#ifdef SIGNED_COMPARE_SLOW
typedef unsigned long int count_int;
typedef unsigned short int count_short;
#else /*SIGNED_COMPARE_SLOW*/
typedef long int count_int;
#endif /*SIGNED_COMPARE_SLOW*/
static int colorstobpp(int colors);
static void BumpPixel (void);
static int GIFNextPixel (gdImagePtr im);
static void GIFEncode (gdIOCtx *fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im);
/*static void Putword (int w, gdIOCtx *fp); */
static void compress (int init_bits, gdIOCtx *outfile, gdImagePtr im);
static void output (code_int code);
static void cl_block (void);
static void cl_hash (register count_int hsize);
static void char_init (void);
static void char_out (int c);
static void flush_char (void);
/* Allows for reuse */
static void init_statics(void);
static void _gdImageLzw(gdImagePtr im, gdIOCtx *out)
{
int interlace, transparent, BitsPerPixel;
interlace = im->interlace;
transparent = im->transparent;
BitsPerPixel = colorstobpp(im->colorsTotal);
/* Clear any old values in statics strewn through the GIF code */
init_statics();
/* All set, let's do it. */
GIFEncode(
out, im->sx, im->sy, interlace, 0, transparent, BitsPerPixel,
im->red, im->green, im->blue, im);
}
void gdImageLzw(gdImagePtr im, FILE *outFile)
{
gdIOCtx *out = gdNewFileCtx(outFile);
_gdImageLzw(im, out);
out->free(out);
}
void* gdImageLzwPtr(gdImagePtr im, int *size)
{
void *rv;
gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
_gdImageLzw(im, out);
rv = gdDPExtractData(out,size);
out->free(out);
return rv;
}
static int
colorstobpp(int colors)
{
int bpp = 0;
if ( colors <= 2 )
bpp = 1;
else if ( colors <= 4 )
bpp = 2;
else if ( colors <= 8 )
bpp = 3;
else if ( colors <= 16 )
bpp = 4;
else if ( colors <= 32 )
bpp = 5;
else if ( colors <= 64 )
bpp = 6;
else if ( colors <= 128 )
bpp = 7;
else if ( colors <= 256 )
bpp = 8;
return bpp;
}
/*****************************************************************************
*
* GIFENCODE.C - GIF Image compression interface
*
* GIFEncode( FName, GHeight, GWidth, GInterlace, Background, Transparent,
* BitsPerPixel, Red, Green, Blue, gdImagePtr )
*
*****************************************************************************/
#define TRUE 1
#define FALSE 0
static int Width, Height;
static int curx, cury;
static long CountDown;
static int Pass = 0;
static int Interlace;
/*
* Bump the 'curx' and 'cury' to point to the next pixel
*/
static void
BumpPixel(void)
{
/*
* Bump the current X position
*/
++curx;
/*
* If we are at the end of a scan line, set curx back to the beginning
* If we are interlaced, bump the cury to the appropriate spot,
* otherwise, just increment it.
*/
if( curx == Width ) {
curx = 0;
if( !Interlace )
++cury;
else {
switch( Pass ) {
case 0:
cury += 8;
if( cury >= Height ) {
++Pass;
cury = 4;
}
break;
case 1:
cury += 8;
if( cury >= Height ) {
++Pass;
cury = 2;
}
break;
case 2:
cury += 4;
if( cury >= Height ) {
++Pass;
cury = 1;
}
break;
case 3:
cury += 2;
break;
}
}
}
}
/*
* Return the next pixel from the image
*/
static int
GIFNextPixel(gdImagePtr im)
{
int r;
if( CountDown == 0 )
return EOF;
--CountDown;
r = gdImageGetPixel(im, curx, cury);
BumpPixel();
return r;
}
/* public */
static void
GIFEncode(gdIOCtx *fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im)
{
int B;
int RWidth, RHeight;
int LeftOfs, TopOfs;
int Resolution;
int ColorMapSize;
int InitCodeSize;
int i;
Interlace = GInterlace;
ColorMapSize = 1 << BitsPerPixel;
RWidth = Width = GWidth;
RHeight = Height = GHeight;
LeftOfs = TopOfs = 0;
Resolution = BitsPerPixel;
/*
* Calculate number of bits we are expecting
*/
CountDown = (long)Width * (long)Height;
/*
* Indicate which pass we are on (if interlace)
*/
Pass = 0;
/*
* The initial code size
*/
if( BitsPerPixel <= 1 )
InitCodeSize = 2;
else
InitCodeSize = BitsPerPixel;
/*
* Set up the current x and y position
*/
curx = cury = 0;
/*
* Write the Magic header
*/
gdPutBuf( Transparent < 0 ? "GIF87a" : "GIF89a", 6, fp );
/*
* Write out the screen width and height
*/
Putword( RWidth, fp );
Putword( RHeight, fp );
/*
* Indicate that there is a global colour map
*/
B = 0x80; /* Yes, there is a color map */
/*
* OR in the resolution
*/
B |= (Resolution - 1) << 5;
/*
* OR in the Bits per Pixel
*/
B |= (BitsPerPixel - 1);
/*
* Write it out
*/
gdPutC( B, fp );
/*
* Write out the Background colour
*/
gdPutC( Background, fp );
/*
* Byte of 0's (future expansion)
*/
gdPutC( 0, fp );
/*
* Write out the Global Colour Map
*/
for( i=0; i<ColorMapSize; ++i ) {
gdPutC( Red[i], fp );
gdPutC( Green[i], fp );
gdPutC( Blue[i], fp );
}
/*
* Write out extension for transparent colour index, if necessary.
*/
if ( Transparent >= 0 ) {
gdPutC( '!', fp );
gdPutC( 0xf9, fp );
gdPutC( 4, fp );
gdPutC( 1, fp );
gdPutC( 0, fp );
gdPutC( 0, fp );
gdPutC( (unsigned char) Transparent, fp );
gdPutC( 0, fp );
}
/*
* Write an Image separator
*/
gdPutC( ',', fp );
/*
* Write the Image header
*/
Putword( LeftOfs, fp );
Putword( TopOfs, fp );
Putword( Width, fp );
Putword( Height, fp );
/*
* Write out whether or not the image is interlaced
*/
if( Interlace )
gdPutC( 0x40, fp );
else
gdPutC( 0x00, fp );
/*
* Write out the initial code size
*/
gdPutC( InitCodeSize, fp );
/*
* Go and actually compress the data
*/
compress( InitCodeSize+1, fp, im );
/*
* Write out a Zero-length packet (to end the series)
*/
gdPutC( 0, fp );
/*
* Write the GIF file terminator
*/
gdPutC( ';', fp );
}
/*/* */
/* * Write out a word to the GIF file */
/* */ */
/*static void */
/*Putword(int w, FILE *fp) */
/*{ */
/* fputc( w & 0xff, fp ); */
/* fputc( (w / 256) & 0xff, fp ); */
/*} */
/***************************************************************************
*
* GIFCOMPR.C - GIF Image compression routines
*
* Lempel-Ziv compression based on 'compress'. GIF modifications by
* David Rowley (mgardi@watdcsu.waterloo.edu)
*
***************************************************************************/
/*
* General DEFINEs
*/
#define GIFBITS 12
#define HSIZE 5003 /* 80% occupancy */
#ifdef NO_UCHAR
typedef char char_type;
#else /*NO_UCHAR*/
typedef unsigned char char_type;
#endif /*NO_UCHAR*/
/*
*
* GIF Image compression - modified 'compress'
*
* Based on: compress.c - File compression ala IEEE Computer, June 1984.
*
* By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
* Jim McKie (decvax!mcvax!jim)
* Steve Davies (decvax!vax135!petsd!peora!srd)
* Ken Turkowski (decvax!decwrl!turtlevax!ken)
* James A. Woods (decvax!ihnp4!ames!jaw)
* Joe Orost (decvax!vax135!petsd!joe)
*
*/
#include <ctype.h>
#define ARGVAL() (*++(*argv) || (--argc && *++argv))
static int n_bits; /* number of bits/code */
static int maxbits = GIFBITS; /* user settable max # bits/code */
static code_int maxcode; /* maximum code, given n_bits */
static code_int maxmaxcode = (code_int)1 << GIFBITS; /* should NEVER generate this code */
#ifdef COMPATIBLE /* But wrong! */
# define MAXCODE(n_bits) ((code_int) 1 << (n_bits) - 1)
#else /*COMPATIBLE*/
# define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1)
#endif /*COMPATIBLE*/
static count_int htab [HSIZE];
static unsigned short codetab [HSIZE];
#define HashTabOf(i) htab[i]
#define CodeTabOf(i) codetab[i]
static code_int hsize = HSIZE; /* for dynamic table sizing */
/*
* To save much memory, we overlay the table used by compress() with those
* used by decompress(). The tab_prefix table is the same size and type
* as the codetab. The tab_suffix table needs 2**GIFBITS characters. We
* get this from the beginning of htab. The output stack uses the rest
* of htab, and contains characters. There is plenty of room for any
* possible stack (stack used to be 8000 characters).
*/
#define tab_prefixof(i) CodeTabOf(i)
#define tab_suffixof(i) ((char_type*)(htab))[i]
#define de_stack ((char_type*)&tab_suffixof((code_int)1<<GIFBITS))
static code_int free_ent = 0; /* first unused entry */
/*
* block compression parameters -- after all codes are used up,
* and compression rate changes, start over.
*/
static int clear_flg = 0;
static int offset;
static long int in_count = 1; /* length of input */
static long int out_count = 0; /* # of codes output (for debugging) */
/*
* compress stdin to stdout
*
* Algorithm: use open addressing double hashing (no chaining) on the
* prefix code / next character combination. We do a variant of Knuth's
* algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
* secondary probe. Here, the modular division first probe is gives way
* to a faster exclusive-or manipulation. Also do block compression with
* an adaptive reset, whereby the code table is cleared when the compression
* ratio decreases, but after the table fills. The variable-length output
* codes are re-sized at this point, and a special CLEAR code is generated
* for the decompressor. Late addition: construct the table according to
* file size for noticeable speed improvement on small files. Please direct
* questions about this implementation to ames!jaw.
*/
static int g_init_bits;
static gdIOCtx* g_outfile;
static int ClearCode;
static int EOFCode;
static void
compress(int init_bits, gdIOCtx *outfile, gdImagePtr im)
{
register long fcode;
register code_int i /* = 0 */;
register int c;
register code_int ent;
register code_int disp;
register code_int hsize_reg;
register int hshift;
/*
* Set up the globals: g_init_bits - initial number of bits
* g_outfile - pointer to output file
*/
g_init_bits = init_bits;
g_outfile = outfile;
/*
* Set up the necessary values
*/
offset = 0;
out_count = 0;
clear_flg = 0;
in_count = 1;
maxcode = MAXCODE(n_bits = g_init_bits);
ClearCode = (1 << (init_bits - 1));
EOFCode = ClearCode + 1;
free_ent = ClearCode + 2;
char_init();
ent = GIFNextPixel( im );
hshift = 0;
for ( fcode = (long) hsize; fcode < 65536L; fcode *= 2L )
++hshift;
hshift = 8 - hshift; /* set hash code range bound */
hsize_reg = hsize;
cl_hash( (count_int) hsize_reg); /* clear hash table */
output( (code_int)ClearCode );
#ifdef SIGNED_COMPARE_SLOW
while ( (c = GIFNextPixel( im )) != (unsigned) EOF ) {
#else /*SIGNED_COMPARE_SLOW*/
while ( (c = GIFNextPixel( im )) != EOF ) { /* } */
#endif /*SIGNED_COMPARE_SLOW*/
++in_count;
fcode = (long) (((long) c << maxbits) + ent);
i = (((code_int)c << hshift) ^ ent); /* xor hashing */
if ( HashTabOf (i) == fcode ) {
ent = CodeTabOf (i);
continue;
} else if ( (long)HashTabOf (i) < 0 ) /* empty slot */
goto nomatch;
disp = hsize_reg - i; /* secondary hash (after G. Knott) */
if ( i == 0 )
disp = 1;
probe:
if ( (i -= disp) < 0 )
i += hsize_reg;
if ( HashTabOf (i) == fcode ) {
ent = CodeTabOf (i);
continue;
}
if ( (long)HashTabOf (i) > 0 )
goto probe;
nomatch:
output ( (code_int) ent );
++out_count;
ent = c;
#ifdef SIGNED_COMPARE_SLOW
if ( (unsigned) free_ent < (unsigned) maxmaxcode) {
#else /*SIGNED_COMPARE_SLOW*/
if ( free_ent < maxmaxcode ) { /* } */
#endif /*SIGNED_COMPARE_SLOW*/
CodeTabOf (i) = free_ent++; /* code -> hashtable */
HashTabOf (i) = fcode;
} else
cl_block();
}
/*
* Put out the final code.
*/
output( (code_int)ent );
++out_count;
output( (code_int) EOFCode );
}
/*****************************************************************
* TAG( output )
*
* Output the given code.
* Inputs:
* code: A n_bits-bit integer. If == -1, then EOF. This assumes
* that n_bits =< (long)wordsize - 1.
* Outputs:
* Outputs code to the file.
* Assumptions:
* Chars are 8 bits long.
* Algorithm:
* Maintain a GIFBITS character long buffer (so that 8 codes will
* fit in it exactly). Use the VAX insv instruction to insert each
* code in turn. When the buffer fills up empty it and start over.
*/
static unsigned long cur_accum = 0;
static int cur_bits = 0;
static unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
0x001F, 0x003F, 0x007F, 0x00FF,
0x01FF, 0x03FF, 0x07FF, 0x0FFF,
0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
static void
output(code_int code)
{
cur_accum &= masks[ cur_bits ];
if( cur_bits > 0 )
cur_accum |= ((long)code << cur_bits);
else
cur_accum = code;
cur_bits += n_bits;
while( cur_bits >= 8 ) {
char_out( (unsigned int)(cur_accum & 0xff) );
cur_accum >>= 8;
cur_bits -= 8;
}
/*
* If the next entry is going to be too big for the code size,
* then increase it, if possible.
*/
if ( free_ent > maxcode || clear_flg ) {
if( clear_flg ) {
maxcode = MAXCODE (n_bits = g_init_bits);
clear_flg = 0;
} else {
++n_bits;
if ( n_bits == maxbits )
maxcode = maxmaxcode;
else
maxcode = MAXCODE(n_bits);
}
}
if( code == EOFCode ) {
/*
* At EOF, write the rest of the buffer.
*/
while( cur_bits > 0 ) {
char_out( (unsigned int)(cur_accum & 0xff) );
cur_accum >>= 8;
cur_bits -= 8;
}
flush_char();
/* fflush( g_outfile ); */
/* */
/* if( ferror( g_outfile ) ) */
/* return; */
}
}
/*
* Clear out the hash table
*/
static void
cl_block (void) /* table clear for block compress */
{
cl_hash ( (count_int) hsize );
free_ent = ClearCode + 2;
clear_flg = 1;
output( (code_int)ClearCode );
}
static void
cl_hash(register count_int hsize) /* reset code table */
{
register count_int *htab_p = htab+hsize;
register long i;
register long m1 = -1;
i = hsize - 16;
do { /* might use Sys V memset(3) here */
*(htab_p-16) = m1;
*(htab_p-15) = m1;
*(htab_p-14) = m1;
*(htab_p-13) = m1;
*(htab_p-12) = m1;
*(htab_p-11) = m1;
*(htab_p-10) = m1;
*(htab_p-9) = m1;
*(htab_p-8) = m1;
*(htab_p-7) = m1;
*(htab_p-6) = m1;
*(htab_p-5) = m1;
*(htab_p-4) = m1;
*(htab_p-3) = m1;
*(htab_p-2) = m1;
*(htab_p-1) = m1;
htab_p -= 16;
} while ((i -= 16) >= 0);
for ( i += 16; i > 0; --i )
*--htab_p = m1;
}
/******************************************************************************
*
* GIF Specific routines
*
******************************************************************************/
/*
* Number of characters so far in this 'packet'
*/
static int a_count;
/*
* Set up the 'byte output' routine
*/
static void
char_init(void)
{
a_count = 0;
}
/*
* Define the storage for the packet accumulator
*/
static char accum[ 256 ];
/*
* Add a character to the end of the current packet, and if it is 254
* characters, flush the packet to disk.
*/
static void
char_out(int c)
{
accum[ a_count++ ] = c;
if( a_count >= 254 )
flush_char();
}
/*
* Flush the packet to disk, and reset the accumulator
*/
static void
flush_char(void)
{
if( a_count > 0 ) {
gdPutC( a_count, g_outfile );
gdPutBuf( accum, a_count, g_outfile );
a_count = 0;
}
}
static void init_statics(void) {
/* Some of these are properly initialized later. What I'm doing
here is making sure code that depends on C's initialization
of statics doesn't break when the code gets called more
than once. */
Width = 0;
Height = 0;
curx = 0;
cury = 0;
CountDown = 0;
Pass = 0;
Interlace = 0;
a_count = 0;
cur_accum = 0;
cur_bits = 0;
g_init_bits = 0;
g_outfile = 0;
ClearCode = 0;
EOFCode = 0;
free_ent = 0;
clear_flg = 0;
offset = 0;
in_count = 1;
out_count = 0;
hsize = HSIZE;
n_bits = 0;
maxbits = GIFBITS;
maxcode = 0;
maxmaxcode = (code_int)1 << GIFBITS;
}
/* +-------------------------------------------------------------------+ */
/* | Copyright 1990, 1991, 1993, David Koblas. (koblas@netcom.com) | */
/* | Permission to use, copy, modify, and distribute this software | */
/* | and its documentation for any purpose and without fee is hereby | */
/* | granted, provided that the above copyright notice appear in all | */
/* | copies and that both that copyright notice and this permission | */
/* | notice appear in supporting documentation. This software is | */
/* | provided "as is" without express or implied warranty. | */
/* +-------------------------------------------------------------------+ */
#endif

54
src/gdparttogif.c Normal file
View File

@ -0,0 +1,54 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .gif file into a .gd file, for
your convenience in creating images on the fly from a
basis image that must be loaded quickly. The .gd format
is not intended to be a general-purpose format. */
int main(int argc, char **argv)
{
gdImagePtr im;
FILE *in, *out;
int x, y, w, h;
if (argc != 7) {
fprintf(stderr, "Usage: gdparttogif filename.gd filename.gif x y w h\n");
exit(1);
}
in = fopen(argv[1], "rb");
if (!in) {
fprintf(stderr, "Input file does not exist!\n");
exit(1);
}
x = atoi(argv[3]);
y = atoi(argv[4]);
w = atoi(argv[5]);
h = atoi(argv[6]);
printf("Extracting from (%d, %d), size is %dx%d\n", x, y, w, h);
im = gdImageCreateFromGd2Part(in, x, y, w, h);
fclose(in);
if (!im) {
fprintf(stderr, "Input is not in GIF format!\n");
exit(1);
}
out = fopen(argv[2], "wb");
if (!out) {
fprintf(stderr, "Output file cannot be written to!\n");
gdImageDestroy(im);
exit(1);
}
#ifdef LZW_LICENCED
gdImageLzw(im, out);
#else
printf("LZW Not Licenced, using compatible GIF format\n");
gdImageGif(im, out);
#endif
fclose(out);
gdImageDestroy(im);
}

306
src/gdtest.c Normal file
View File

@ -0,0 +1,306 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .gif file into a .gd file, for
your convenience in creating images on the fly from a
basis image that must be loaded quickly. The .gd format
is not intended to be a general-purpose format. */
void CompareImages(char *msg, gdImagePtr im1, gdImagePtr im2);
int main(int argc, char **argv)
{
gdImagePtr im, ref, im2, im3;
FILE *in, *out;
int cs, fmt;
void *iptr;
int sz;
gdIOCtxPtr ctx;
char of[256];
int colRed, colBlu;
int idx;
if (argc != 2) {
fprintf(stderr, "Usage: gdtest filename.gif\n");
exit(1);
}
in = fopen(argv[1], "rb");
if (!in) {
fprintf(stderr, "Input file does not exist!\n");
exit(1);
}
im = gdImageCreateFromGif(in);
rewind(in);
ref = gdImageCreateFromGif(in);
fclose(in);
printf("Reference File has %d Palette entries\n",ref->colorsTotal);
CompareImages("Initial Versions", ref, im);
/* */
/* Send to GIF File then Ptr */
/* */
sprintf(of, "%s.gif", argv[1]);
out = fopen(of, "wb");
gdImageGif(im, out);
fclose(out);
in = fopen(of, "rb");
if (!in) {
fprintf(stderr, "GIF Output file does not exist!\n");
exit(1);
}
im2 = gdImageCreateFromGif(in);
fclose(in);
CompareImages("GD->GIF File->GD", ref, im2);
unlink(of);
gdImageDestroy(im2);
iptr = gdImageGifPtr(im,&sz);
ctx = gdNewDynamicCtx(sz,iptr);
im2 = gdImageCreateFromGifCtx(ctx);
CompareImages("GD->GIF ptr->GD", ref, im2);
gdImageDestroy(im2);
ctx->free(ctx);
/* */
/* Send to GD2 File then Ptr */
/* */
sprintf(of, "%s.gd2", argv[1]);
out = fopen(of, "wb");
gdImageGd2(im, out, 128, 2);
fclose(out);
in = fopen(of, "rb");
if (!in) {
fprintf(stderr, "GD2 Output file does not exist!\n");
exit(1);
}
im2 = gdImageCreateFromGd2(in);
fclose(in);
CompareImages("GD->GD2 File->GD", ref, im2);
unlink(of);
gdImageDestroy(im2);
iptr = gdImageGd2Ptr(im,128,2,&sz);
/*printf("Got ptr %d (size %d)\n",iptr, sz); */
ctx = gdNewDynamicCtx(sz,iptr);
/*printf("Got ctx %d\n",ctx); */
im2 = gdImageCreateFromGd2Ctx(ctx);
/*printf("Got img2 %d\n",im2); */
CompareImages("GD->GD2 ptr->GD", ref, im2);
gdImageDestroy(im2);
ctx->free(ctx);
/* */
/* Send to GD File then Ptr */
/* */
sprintf(of, "%s.gd", argv[1]);
out = fopen(of, "wb");
gdImageGd(im, out);
fclose(out);
in = fopen(of, "rb");
if (!in) {
fprintf(stderr, "GD Output file does not exist!\n");
exit(1);
}
im2 = gdImageCreateFromGd(in);
fclose(in);
CompareImages("GD->GD File->GD", ref, im2);
unlink(of);
gdImageDestroy(im2);
iptr = gdImageGdPtr(im,&sz);
/*printf("Got ptr %d (size %d)\n",iptr, sz); */
ctx = gdNewDynamicCtx(sz,iptr);
/*printf("Got ctx %d\n",ctx); */
im2 = gdImageCreateFromGdCtx(ctx);
/*printf("Got img2 %d\n",im2); */
CompareImages("GD->GD ptr->GD", ref, im2);
gdImageDestroy(im2);
ctx->free(ctx);
#ifdef LZW_LICENCED
/* */
/* Send to LZW File then Ptr */
/* */
sprintf(of, "%s.lzw", argv[1]);
out = fopen(of, "wb");
gdImageLzw(im, out);
fclose(out);
in = fopen(of, "rb");
if (!in) {
fprintf(stderr, "LZW Output file does not exist!\n");
exit(1);
}
im2 = gdImageCreateFromGif(in);
fclose(in);
CompareImages("GD->LZW File->GD", ref, im2);
unlink(of);
gdImageDestroy(im2);
iptr = gdImageLzwPtr(im,&sz);
/*printf("Got ptr %d (size %d)\n",iptr, sz); */
ctx = gdNewDynamicCtx(sz,iptr);
/*printf("Got ctx %d\n",ctx); */
im2 = gdImageCreateFromGifCtx(ctx);
/*printf("Got img2 %d\n",im2); */
CompareImages("GD->LZW ptr->GD", ref, im2);
gdImageDestroy(im2);
ctx->free(ctx);
#endif
/* */
/* Test Extraction */
/* */
in = fopen("test/gdtest_200_300_150_100.gif", "rb");
if (!in) {
fprintf(stderr, "gdtest_200_300_150_100.gif does not exist!\n");
exit(1);
}
im2 = gdImageCreateFromGif(in);
fclose(in);
in = fopen("test/gdtest.gd2", "rb");
if (!in) {
fprintf(stderr, "gdtest.gd2 does not exist!\n");
exit(1);
}
im3 = gdImageCreateFromGd2Part(in, 200, 300, 150, 100);
fclose(in);
CompareImages("GD2Part (gdtest.gd2, gdtest_200_300_150_100.gif)", im2, im3);
gdImageDestroy(im2);
gdImageDestroy(im3);
/* */
/* Copy Blend */
/* */
in = fopen("test/gdtest.gif", "rb");
if (!in) {
fprintf(stderr, "gdtest.gif does not exist!\n");
exit(1);
}
im2 = gdImageCreateFromGif(in);
fclose(in);
im3 = gdImageCreate(100, 60);
colRed = gdImageColorAllocate(im3, 255, 0, 0);
colBlu = gdImageColorAllocate(im3, 0, 0, 255);
gdImageFilledRectangle(im3, 0, 0, 49, 30, colRed);
gdImageFilledRectangle(im3, 50, 30, 99, 59, colBlu);
gdImageCopyMerge(im2, im3, 150, 200, 10, 10, 90, 50, 50);
gdImageCopyMerge(im2, im3, 180, 70, 10, 10, 90, 50, 50);
gdImageCopyMergeGray(im2, im3, 250, 160, 10, 10, 90, 50, 50);
gdImageCopyMergeGray(im2, im3, 80, 70, 10, 10, 90, 50, 50);
gdImageDestroy(im3);
in = fopen("test/gdtest_merge.gif", "rb");
if (!in) {
fprintf(stderr, "gdtest.gd2 does not exist!\n");
exit(1);
}
im3 = gdImageCreateFromGif(in);
fclose(in);
printf("[Merged Image has %d colours]\n",im2->colorsTotal);
CompareImages("Merged (gdtest.gif, gdtest_merge.gif)", im2, im3);
/*out = fopen("gdtest_merge.gif", "wb"); */
/*gdImageLzw(im2, out); */
/*fclose(out); */
gdImageDestroy(im2);
gdImageDestroy(im3);
gdImageDestroy(im);
gdImageDestroy(ref);
}
void CompareImages(char *msg, gdImagePtr im1, gdImagePtr im2)
{
int x, y;
int bad;
int p1, p2;
bad = (1==0);
if (im1->sx != im2->sx) {
printf("%s: image x-size differs\n",msg);
bad = (1==1);
}
if (im1->sy != im2->sy) {
printf("%s: image y-size differs\n",msg);
bad = (1==1);
}
if (!bad) {
if (im1->colorsTotal != im2->colorsTotal) {
printf("Mismatch in number of colours: %d Vs. %d\n",im1->colorsTotal, im2->colorsTotal);
}
for ( y = 0 ; (y<im1->sy) ; y++ ) {
for ( x = 0 ; (x < im1->sx) ; x++ ) {
p1 = im1->pixels[y][x];
p2 = im2->pixels[y][x];
if (im1->red[p1] != im2->red[p2]) {
printf("%s: image colours differ\n",msg);
bad = (1==1);
break;
}
if (im1->green[p1] != im2->green[p2]) {
printf("%s: image colours differ\n",msg);
bad = (1==1);
break;
}
if (im1->blue[p1] != im2->blue[p2]) {
printf("%s: image colours differ\n",msg);
bad = (1==1);
break;
}
}
if (bad) { break; };
}
}
if (!bad) {
printf("%s: OK\n",msg);
}
}

38
src/gdtogif.c Normal file
View File

@ -0,0 +1,38 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .gif file into a .gd file, for
your convenience in creating images on the fly from a
basis image that must be loaded quickly. The .gd format
is not intended to be a general-purpose format. */
int main(int argc, char **argv)
{
gdImagePtr im;
FILE *in, *out;
if (argc != 3) {
fprintf(stderr, "Usage: gdtogif filename.gd filename.gif\n");
exit(1);
}
in = fopen(argv[1], "rb");
if (!in) {
fprintf(stderr, "Input file does not exist!\n");
exit(1);
}
im = gdImageCreateFromGd(in);
fclose(in);
if (!im) {
fprintf(stderr, "Input is not in GIF format!\n");
exit(1);
}
out = fopen(argv[2], "wb");
if (!out) {
fprintf(stderr, "Output file cannot be written to!\n");
gdImageDestroy(im);
exit(1);
}
gdImageGif(im, out);
fclose(out);
gdImageDestroy(im);
}

44
src/giftogd2.c Normal file
View File

@ -0,0 +1,44 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .gif file into a .gd file, for
your convenience in creating images on the fly from a
basis image that must be loaded quickly. The .gd format
is not intended to be a general-purpose format. */
int main(int argc, char **argv)
{
gdImagePtr im;
FILE *in, *out;
int cs, fmt;
if (argc != 5) {
fprintf(stderr, "Usage: giftogd2 filename.gif filename.gd2 cs fmt\n");
fprintf(stderr, " where cs is the chunk size\n");
fprintf(stderr, " fmt is 1 for raw, 2 for compressed\n");
exit(1);
}
in = fopen(argv[1], "rb");
if (!in) {
fprintf(stderr, "Input file does not exist!\n");
exit(1);
}
im = gdImageCreateFromGif(in);
fclose(in);
if (!im) {
fprintf(stderr, "Input is not in GIF format!\n");
exit(1);
}
out = fopen(argv[2], "wb");
if (!out) {
fprintf(stderr, "Output file cannot be written to!\n");
gdImageDestroy(im);
exit(1);
}
cs = atoi(argv[3]);
fmt = atoi(argv[4]);
gdImageGd2(im, out, cs, fmt);
fclose(out);
gdImageDestroy(im);
}

44
src/giftolzw.c Normal file
View File

@ -0,0 +1,44 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .gif file into a .gd file, for
your convenience in creating images on the fly from a
basis image that must be loaded quickly. The .gd format
is not intended to be a general-purpose format. */
int main(int argc, char **argv)
{
gdImagePtr im;
FILE *in, *out;
if (argc != 3) {
fprintf(stderr, "Usage: giftogif filename.gif filename.gd\n");
exit(1);
}
in = fopen(argv[1], "rb");
if (!in) {
fprintf(stderr, "Input file does not exist!\n");
exit(1);
}
im = gdImageCreateFromGif(in);
fclose(in);
if (!im) {
fprintf(stderr, "Input is not in GIF format!\n");
exit(1);
}
#ifdef LZW_LICENCED
out = fopen(argv[2], "wb");
if (!out) {
fprintf(stderr, "Output file cannot be written to!\n");
gdImageDestroy(im);
exit(1);
}
gdImageLzw(im, out);
fclose(out);
#else
printf("LZW Compression Not Licenced\n");
#endif
gdImageDestroy(im);
}

File diff suppressed because it is too large Load Diff

158
src/io.c Normal file
View File

@ -0,0 +1,158 @@
/*
* io.c
*
* Implements the imple I/O 'helper' routines.
*
* Not really essential, but these routines were used extensively in GD,
* so they were moved here. They also make IOCtx calls look better...
*
* Written (or, at least, moved) 1999, Philip Warner.
*
*/
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "io.h"
#include "gd.h"
/* Use this for commenting out debug-print statements. */
/* Just use the first '#define' to allow all the prints... */
/*#define IO_DBG(s) (s) */
#define IO_DBG(s)
/*
* Write out a word to the I/O context pointer
*/
void
Putword(int w, gdIOCtx *ctx)
{
unsigned char buf[2];
buf[0] = w & 0xff;
buf[1] = (w / 256) & 0xff;
(ctx->putBuf)( ctx, (char *) buf, 2);
}
void
Putchar(int c, gdIOCtx *ctx)
{
(ctx->putC)( ctx, c & 0xff );
}
void gdPutC(const unsigned char c, gdIOCtx *ctx)
{
(ctx->putC)(ctx, c);
}
void
gdPutWord(int w, gdIOCtx *ctx)
{
IO_DBG(printf("Putting word...\n"));
(ctx->putC)(ctx, (unsigned char)(w >> 8));
(ctx->putC)(ctx, (unsigned char)(w & 0xFF));
IO_DBG(printf("put.\n"));
}
void gdPutInt(int w, gdIOCtx *ctx)
{
IO_DBG(printf("Putting int...\n"));
(ctx->putC)(ctx, (unsigned char)(w >> 24));
(ctx->putC)(ctx, (unsigned char)((w >> 16) & 0xFF));
(ctx->putC)(ctx, (unsigned char)((w >> 8) & 0xFF));
(ctx->putC)(ctx, (unsigned char)(w & 0xFF));
IO_DBG(printf("put.\n"));
}
int gdGetC(gdIOCtx *ctx)
{
return ((ctx->getC)(ctx));
}
int gdGetByte(int *result, gdIOCtx *ctx)
{
int r;
r = (ctx->getC)(ctx);
if (r == EOF) {
return 0;
}
*result = r;
return 1;
}
int gdGetWord(int *result, gdIOCtx *ctx)
{
int r;
r = (ctx->getC)(ctx);
if (r == EOF) {
return 0;
}
*result = r << 8;
r = (ctx->getC)(ctx);
if (r == EOF) {
return 0;
}
*result += r;
return 1;
}
int gdGetInt(int *result, gdIOCtx *ctx)
{
int r;
r = (ctx->getC)(ctx);
if (r == EOF) {
return 0;
}
*result = r << 24;
r = (ctx->getC)(ctx);
if (r == EOF) {
return 0;
}
*result += r << 16;
r = (ctx->getC)(ctx);
if (r == EOF) {
return 0;
}
*result += r << 8;
r = (ctx->getC)(ctx);
if (r == EOF) {
return 0;
}
*result += r;
return 1;
}
int gdPutBuf(const void *buf, int size, gdIOCtx* ctx)
{
IO_DBG(printf("Putting buf...\n"));
return (ctx->putBuf)(ctx, buf, size);
IO_DBG(printf("put.\n"));
}
int gdGetBuf(void *buf, int size, gdIOCtx* ctx)
{
return (ctx->getBuf)(ctx, buf, size);
}
int gdSeek(gdIOCtx *ctx, int pos)
{
IO_DBG(printf("Seeking...\n"));
return ((ctx->seek)(ctx, pos));
IO_DBG(printf("Done.\n"));
}
long gdTell(gdIOCtx *ctx)
{
IO_DBG(printf("Telling...\n"));
return ((ctx->tell)(ctx));
IO_DBG(printf("told.\n"));
}

43
src/io.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef GD_IO_H
#define GD_IO_H 1
#include <stdio.h>
typedef struct gdIOCtx {
int (*getC)(struct gdIOCtx*);
int (*getBuf)(struct gdIOCtx*, void*, int);
void (*putC)(struct gdIOCtx*, int);
int (*putBuf)(struct gdIOCtx*, const void*, int);
int (*seek)(struct gdIOCtx*, const int);
long (*tell)(struct gdIOCtx*);
void (*free)(struct gdIOCtx*);
} gdIOCtx;
typedef struct gdIOCtx *gdIOCtxPtr;
void Putword(int w, gdIOCtx *ctx);
void Putchar(int c, gdIOCtx *ctx);
void gdPutC(const unsigned char c, gdIOCtx *ctx);
int gdPutBuf(const void *, int, gdIOCtx*);
void gdPutWord(int w, gdIOCtx *ctx);
void gdPutInt(int w, gdIOCtx *ctx);
int gdGetC(gdIOCtx *ctx);
int gdGetBuf(void *, int, gdIOCtx*);
int gdGetByte(int *result, gdIOCtx *ctx);
int gdGetWord(int *result, gdIOCtx *ctx);
int gdGetInt(int *result, gdIOCtx *ctx);
int gdSeek(gdIOCtx *ctx, const int);
long gdTell(gdIOCtx *ctx);
gdIOCtx* gdNewFileCtx(FILE*);
gdIOCtx* gdNewDynamicCtx(int, void*);
void* gdDPExtractData(struct gdIOCtx* ctx, int *size);
#endif

376
src/io_dp.c Normal file
View File

@ -0,0 +1,376 @@
/*
* io_dp.c
*
* Implements the dynamic pointer interface.
*
* Based on GD.pm code by Lincoln Stein for interfacing to libgd.
* Added support for reading as well as support for 'tell' and 'seek'.
*
* As will all I/O modules, most functions are for local use only (called
* via function pointers in the I/O context).
*
* gdDPExtractData is the exception to this: it will return the pointer to
* the internal data, and reset the internal storage.
*
* Written/Modified 1999, Philip Warner.
*
*/
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "io.h"
#include "gd.h"
#define TRUE 1
#define FALSE 0
/* this is used for creating images in main memory*/
typedef struct dpStruct {
void* data;
int logicalSize;
int realSize;
int dataGood;
int pos;
} dynamicPtr;
typedef struct dpIOCtx {
gdIOCtx ctx;
dynamicPtr *dp;
} dpIOCtx;
typedef struct dpIOCtx *dpIOCtxPtr;
/* these functions operate on in-memory dynamic pointers */
static int allocDynamic (dynamicPtr* dp,int initialSize, void *data);
static int appendDynamic (dynamicPtr* dp, const void* src, int size);
static int reallocDynamic (dynamicPtr* dp, int required);
static int trimDynamic (dynamicPtr* dp);
static void freeDynamic (dynamicPtr* dp);
static void freeDynamicCtx(struct gdIOCtx* ctx);
static dynamicPtr* newDynamic(int initialSize, void *data);
static int dynamicPutbuf( struct gdIOCtx*, const void *, int );
static void dynamicPutchar( struct gdIOCtx*, int a );
static int dynamicGetbuf( gdIOCtxPtr ctx, void *buf, int len);
static int dynamicGetchar( gdIOCtxPtr ctx );
static int dynamicSeek(struct gdIOCtx*, const int);
static long dynamicTell(struct gdIOCtx*);
/* return data as a dynamic pointer */
gdIOCtx* gdNewDynamicCtx (int initialSize, void *data) {
dpIOCtx *ctx;
dynamicPtr* dp;
ctx = (dpIOCtx*) malloc(sizeof(dpIOCtx));
if (ctx == NULL) {
return NULL;
}
dp = newDynamic(initialSize, data);
if (!dp) {
free(ctx);
return NULL;
};
ctx->dp = dp;
ctx->ctx.getC = dynamicGetchar;
ctx->ctx.putC = dynamicPutchar;
ctx->ctx.getBuf = dynamicGetbuf;
ctx->ctx.putBuf = dynamicPutbuf;
ctx->ctx.seek = dynamicSeek;
ctx->ctx.tell = dynamicTell;
ctx->ctx.free = freeDynamicCtx;
return (gdIOCtx*)ctx;
};
void* gdDPExtractData(struct gdIOCtx* ctx, int *size)
{
dynamicPtr *dp;
dpIOCtx *dctx;
void *data;
dctx = (dpIOCtx*) ctx;
dp = dctx->dp;
/* clean up the data block and return it */
if (dp->dataGood) {
trimDynamic(dp);
*size = dp->logicalSize;
data = dp->data;
} else {
*size = 0;
data = NULL;
if (dp->data != NULL) {
free(dp->data);
}
}
dp->data = NULL;
dp->realSize=0;
dp->logicalSize=0;
return data;
}
static
void freeDynamicCtx(struct gdIOCtx* ctx)
{
dynamicPtr *dp;
dpIOCtx *dctx;
dctx = (dpIOCtx*) ctx;
dp = dctx->dp;
free(ctx);
/* clean up the data block and return it */
if (dp->data != NULL) {
free(dp->data);
}
free(dp);
}
static long dynamicTell(struct gdIOCtx* ctx)
{
dpIOCtx *dctx;
dctx = (dpIOCtx*) ctx;
return (dctx->dp->pos);
}
static int dynamicSeek(struct gdIOCtx* ctx, const int pos)
{
int bytesNeeded;
char* tmp;
dynamicPtr *dp;
dpIOCtx *dctx;
dctx = (dpIOCtx*) ctx;
dp = dctx->dp;
if (!dp->dataGood) return FALSE;
bytesNeeded = pos;
if (bytesNeeded > dp->realSize) {
if (!reallocDynamic(dp,dp->realSize*2)) {
dp->dataGood = FALSE;
return FALSE;
}
}
/* if we get here, we can be sure that we have enough bytes
to copy safely */
/* Extend the logical size if we seek beyond EOF. */
if (pos > dp->logicalSize) {
dp->logicalSize = pos;
};
dp->pos = pos;
return TRUE;
}
/* return data as a dynamic pointer */
static dynamicPtr* newDynamic (int initialSize, void *data) {
dynamicPtr* dp;
dp = (dynamicPtr*) malloc(sizeof(dynamicPtr));
if (dp == NULL) {
return NULL;
}
if (!allocDynamic(dp,initialSize, data))
return NULL;
dp->pos = 0;
return dp;
};
static int
dynamicPutbuf( struct gdIOCtx* ctx, const void *buf, int size )
{
dpIOCtx *dctx;
dctx = (dpIOCtx*) ctx;
appendDynamic(dctx->dp,buf,size);
if (dctx->dp->dataGood) {
return size;
} else {
return -1;
};
}
static void
dynamicPutchar( struct gdIOCtx* ctx, int a )
{
unsigned char b;
dpIOCtxPtr dctx;
b = a;
dctx = (dpIOCtxPtr) ctx;
appendDynamic(dctx->dp,&b,1);
}
static int
dynamicGetbuf( gdIOCtxPtr ctx, void *buf, int len)
{
int rlen, remain;
dpIOCtxPtr dctx;
dynamicPtr* dp;
dctx = (dpIOCtxPtr) ctx;
dp = dctx->dp;
remain = dp->logicalSize - dp->pos;
if (remain >= len) {
rlen = len;
} else {
if (remain == 0) {
return EOF;
}
rlen = remain;
}
memcpy(buf, (void*)((int)dp->data + dp->pos), rlen);
dp->pos += rlen;
return rlen;
}
static int
dynamicGetchar( gdIOCtxPtr ctx )
{
unsigned char b;
int rv;
rv = dynamicGetbuf(ctx, &b, 1);
if (rv != 1) {
return EOF;
} else {
return b ;/* (b & 0xff); */
}
}
/* *********************************************************************
*
* InitDynamic - Return a dynamically resizable void*
*
* *********************************************************************
*/
static int
allocDynamic (dynamicPtr* dp,int initialSize, void *data) {
if (data == NULL) {
dp->logicalSize = 0;
dp->dataGood = FALSE;
dp->data = malloc(initialSize);
} else {
dp->logicalSize = initialSize;
dp->dataGood = TRUE;
dp->data = data;
}
if (dp->data !=NULL) {
dp->realSize = initialSize;
dp->dataGood = TRUE;
dp->pos = 0;
return TRUE;
} else {
dp->realSize = 0;
return FALSE;
}
}
/* append bytes to the end of a dynamic pointer */
static int
appendDynamic (dynamicPtr* dp, const void* src, int size) {
int bytesNeeded;
char* tmp;
if (!dp->dataGood) return FALSE;
/* bytesNeeded = dp->logicalSize + size; */
bytesNeeded = dp->pos + size;
if (bytesNeeded > dp->realSize) {
if (!reallocDynamic(dp,bytesNeeded*2)) {
dp->dataGood = FALSE;
return FALSE;
}
}
/* if we get here, we can be sure that we have enough bytes
to copy safely */
/*printf("Mem OK Size: %d, Pos: %d\n", dp->realSize, dp->pos); */
tmp = (char*)dp->data;
memcpy((void*)(tmp+(dp->pos)),src,size);
dp->pos += size;
if (dp->pos > dp->logicalSize) {
dp->logicalSize = dp->pos;
};
return TRUE;
}
/* grow (or shrink) dynamic pointer */
static int
reallocDynamic (dynamicPtr* dp, int required) {
void* newPtr;
/* First try realloc(). If that doesn't work, make a new
memory block and copy. */
if (newPtr = realloc(dp->data,required)) {
dp->realSize = required;
dp->data = newPtr;
return TRUE;
}
/* create a new pointer */
newPtr = malloc(required);
if (!newPtr) {
dp->dataGood = FALSE;
return FALSE;
}
/* copy the old data into it */
memcpy(newPtr,dp->data,dp->logicalSize);
free(dp->data);
dp->data = newPtr;
dp->realSize = required;
return TRUE;
}
/* trim pointer so that its real and logical sizes match */
static int
trimDynamic (dynamicPtr* dp) {
return reallocDynamic(dp,dp->logicalSize);
}
/* dispose of the dynamic pointer */
static void
freeDynamic (dynamicPtr* dp) {
if (dp->data != NULL) {
free(dp->data);
dp->data = NULL;
}
dp->realSize=0;
dp->logicalSize=0;
}

128
src/io_file.c Normal file
View File

@ -0,0 +1,128 @@
/*
* io_file.c
*
* Implements the file interface.
*
* As will all I/O modules, most functions are for local use only (called
* via function pointers in the I/O context).
*
* Most functions are just 'wrappers' for standard file functions.
*
* Written/Modified 1999, Philip Warner.
*
*/
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "io.h"
#include "gd.h"
/* this is used for creating images in main memory*/
typedef struct fileIOCtx {
gdIOCtx ctx;
FILE *f;
} fileIOCtx;
struct fileIOCtx *fileIOCtxPtr;
gdIOCtx* newFileCtx(FILE *f);
static int fileGetbuf( gdIOCtx*, void *, int );
static int filePutbuf( gdIOCtx*, const void *, int );
static void filePutchar( gdIOCtx*, int );
static int fileGetchar( gdIOCtx* ctx);
static int fileSeek(struct gdIOCtx*, const int);
static long fileTell(struct gdIOCtx*);
static void freeFileCtx(gdIOCtx *ctx);
/* return data as a dynamic pointer */
gdIOCtx* gdNewFileCtx (FILE *f) {
fileIOCtx *ctx;
ctx = (fileIOCtx*) malloc(sizeof(fileIOCtx));
if (ctx == NULL) {
return NULL;
}
ctx->f = f;
ctx->ctx.getC = fileGetchar;
ctx->ctx.putC = filePutchar;
ctx->ctx.getBuf = fileGetbuf;
ctx->ctx.putBuf = filePutbuf;
ctx->ctx.tell = fileTell;
ctx->ctx.seek = fileSeek;
ctx->ctx.free = freeFileCtx;
return (gdIOCtx*)ctx;
};
static
void freeFileCtx(gdIOCtx *ctx)
{
free(ctx);
}
static int
filePutbuf( gdIOCtx* ctx, const void *buf, int size )
{
fileIOCtx *fctx;
fctx = (fileIOCtx*) ctx;
return fwrite(buf, 1, size, fctx->f);
}
static int
fileGetbuf( gdIOCtx* ctx, void *buf, int size )
{
fileIOCtx *fctx;
fctx = (fileIOCtx*) ctx;
return (fread(buf, 1, size, fctx->f));
}
static void
filePutchar( gdIOCtx* ctx, int a )
{
unsigned char b;
fileIOCtx *fctx;
fctx = (fileIOCtx*) ctx;
b = a;
putc(b, fctx->f);
}
static int fileGetchar( gdIOCtx* ctx)
{
fileIOCtx *fctx;
fctx = (fileIOCtx*) ctx;
return getc(fctx->f);
}
static int fileSeek(struct gdIOCtx* ctx, const int pos)
{
fileIOCtx *fctx;
fctx = (fileIOCtx*) ctx;
return (fseek(fctx->f, pos, SEEK_SET) == 0);
}
static long fileTell(struct gdIOCtx* ctx)
{
fileIOCtx *fctx;
fctx = (fileIOCtx*) ctx;
return ftell(fctx->f);
}

148
src/io_ss.c Normal file
View File

@ -0,0 +1,148 @@
/*
* io_ss.c
*
* Implements the Source/Sink interface.
*
* As will all I/O modules, most functions are for local use only (called
* via function pointers in the I/O context).
*
* The Source/Sink model is the primary 'user' interface for alternate data
* sources; the IOCtx interface is intended (at least in version 1.5) to be
* used internally until it settles down a bit.
*
* This module just layers the Source/Sink interface on top of the IOCtx; no
* support is provided for tell/seek, so GD2 writing is not possible, and
* retrieving parts of GD2 files is also not possible.
*
* A new SS context does not need to be created with both a Source and a Sink.
*
* Written/Modified 1999, Philip Warner.
*
*/
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "io.h"
#include "gd.h"
/* this is used for creating images in main memory*/
typedef struct ssIOCtx {
gdIOCtx ctx;
gdSourcePtr src;
gdSinkPtr snk;
} ssIOCtx;
typedef struct ssIOCtx *ssIOCtxPtr;
gdIOCtx* gdNewSSCtx(gdSourcePtr src, gdSinkPtr snk);
static int sourceGetbuf( gdIOCtx*, void *, int );
static int sourceGetchar( gdIOCtx* ctx);
static int sinkPutbuf( gdIOCtx* ctx, const void *buf, int size );
static void sinkPutchar( gdIOCtx* ctx, int a );
static void freeSsCtx(gdIOCtx *ctx);
/* return data as a dynamic pointer */
gdIOCtx* gdNewSSCtx (gdSourcePtr src, gdSinkPtr snk) {
ssIOCtxPtr ctx;
ctx = (ssIOCtxPtr) malloc(sizeof(ssIOCtx));
if (ctx == NULL) {
return NULL;
}
ctx->src = src;
ctx->snk = snk;
ctx->ctx.getC = sourceGetchar;
ctx->ctx.getBuf = sourceGetbuf;
ctx->ctx.putC = sinkPutchar;
ctx->ctx.putBuf = sinkPutbuf;
ctx->ctx.tell = NULL;
ctx->ctx.seek = NULL;
ctx->ctx.free = freeSsCtx;
return (gdIOCtx*)ctx;
};
static
void freeSsCtx(gdIOCtx *ctx)
{
free(ctx);
}
static int
sourceGetbuf( gdIOCtx* ctx, void *buf, int size )
{
ssIOCtx *lctx;
int res;
lctx = (ssIOCtx*) ctx;
res = ((lctx->src->source)(lctx->src, buf, size));
/*
** Translate the return values from the Source object:
** 0 is EOF, -1 is error
*/
if (res == 0) {
return EOF;
} else if (res < 0) {
return 0;
} else {
return res;
};
}
static int sourceGetchar( gdIOCtx* ctx)
{
int res;
unsigned char buf;
res = sourceGetbuf(ctx, &buf, 1);
if (res == 1) {
return buf;
} else {
return EOF;
};
}
static int
sinkPutbuf( gdIOCtx* ctx, const void *buf, int size )
{
ssIOCtxPtr lctx;
int res;
lctx = (ssIOCtx*) ctx;
res = (lctx->snk->sink)(lctx->snk, buf, size);
if (res <= 0) {
return 0;
} else {
return res;
};
}
static void
sinkPutchar( gdIOCtx* ctx, int a )
{
unsigned char b;
b = a;
sinkPutbuf(ctx, &b, 1);
}

View File

@ -4,7 +4,7 @@
SEE index.html FOR A MUCH MORE USEFUL HYPERTEXT VERSION OF THIS DOCUMENT!
SEE index.html FOR A MUCH MORE USEFUL HYPERTEXT VERSION OF THIS DOCUMENT!
gd 1.4
gd 1.5
A graphics library for fast GIF creation
@ -13,7 +13,7 @@ Follow this link to the latest version of this document.
Table of Contents
* Credits and license terms
* What's new in version 1.4?
* What's new in version 1.5?
* What is gd?
* What if I want to use another programming language?
* What else do I need to use gd?
@ -33,12 +33,9 @@ Follow this link to the latest version of this document.
In order to resolve any possible confusion regarding the authorship of
gd, the following copyright statement covers all of the authors who
have required such a statement. _Although his LZW compression code no
longer appears in gd, the authors wish to thank David Rowley for the
original LZW-based GIF compression code, which has been removed due to
patent concerns._ _If you are aware of any oversights in this
copyright notice, please contact Thomas Boutell who will be pleased to
correct them._
have required such a statement. _If you are aware of any oversights in
this copyright notice, please contact Thomas Boutell who will be
pleased to correct them._
COPYRIGHT STATEMENT FOLLOWS THIS LINE
@ -54,6 +51,14 @@ COPYRIGHT STATEMENT FOLLOWS THIS LINE
Non-LZW-based GIF compression code copyright 1998, by Hutchison
Avenue Software Corporation (http://www.hasc.com/, info@hasc.com).
LZW-based GIF compression code David Rowley. This code is compiled
only if the compiler macro LZW_LICENCED is defined when gd is
built. Obtaining a license for the Unisys LZW compression patent is
entirely between the user and Unisys. The authors of gd can provide
NO assistance in this matter.
Portions relating to GD2 format copyright 1999 Philip Warner.
_Permission has been granted to copy and distribute gd in any
context, including a commercial application, provided that this
notice is present in user-accessible supporting documentation._
@ -113,6 +118,65 @@ END OF COPYRIGHT STATEMENT
* tgd, by Bradley K. Sherman
* fly, by Martin Gleeson
What's new in version 1.5?
Version 1.5 features the following changes:
_New GD2 format_
An improvement over the GD format, the GD2 format uses the zlib
compression library to compress the image in chunks. This
results in file sizes comparable to GIFs, with the ability to
access parts of large images without having to read the entire
image into memory.
This format also supports version numbers and rudimentary
validity checks, so it should be more 'supportable' than the
previous GD format.
_Re-arranged source files_
gd.c has been broken into constituant parts: io, gif, gd, gd2
and graphics functions are now in separate files.
_Extended I/O capabilities._
The source/sink feature has been extended to support GD2 file
formats (which require seek/tell functions), and to allow more
general non-file I/O.
_Better support for Lincoln Stein's Perl Module_
The new gdImage*Ptr function returns the chosen format stored
in a block of memory. This can be directly used by the GD perl
module.
_LZW-based GIF compression code available for Unisys licensees_
Parties holding a legitimate license to use the patented LZW
compression algorithm can produce smaller GIFs if the compiler
macro LZW_LICENCED is defined when gd is built. Obtaining a
license for the Unisys LZW compression patent is entirely
between the user and Unisys. The authors of gd can provide NO
assistance in this matter.
_Added functions_
gdImageCreateFromGd2Part - allows retrieval of part of an image
(good for huge images, like maps),
gdImagePaletteCopy - Copies a palette from one image to
another, doing it's best to match the colors in the target
image to the colors in the source palette.
gdImageGd2, gdImageCreateFromGd2 - Support for new format
gdImageLzw - Optional support for LZW (for those who have a
licence). Disabled by default.
gdImageCopyMerge - Merges two images (useful to highlight part
of an image)
gdImageCopyMergeGray - Similar to gdImageCopyMerge, but tries
to preserve source image hue.
gdImageGifPtr, gdImageLzwPtr, gdImageGdPtr, gdImageGd2Ptr -
return memort blocks for each type of image.
gdImageCreateFromGifCtx, gdImageCreateFromGdCtx,
gdImageCreateFromGd2Ctx, gdImageCreateFromGd2PartCtx - Support
for new I/O context.
_NOTE:_ In fairness to Thomas Boutell, any bug/problems with any of
the above features should probably be reported to Philip Warner.
What's new in version 1.4?
Version 1.4 features the following changes:
@ -196,6 +260,9 @@ END OF COPYRIGHT STATEMENT
have gcc should get it. gcc is free, ANSI compliant and a de facto
industry standard. Ask your ISP why it is missing._
As of version 1.5, you also need the zlib compression library. It is
available for a variety of platforms from the zlib web site.
You will also want a GIF viewer, if you do not already have one for
your system, since you will need a good way to check the results of
your work. Any web browser will work, but you might be happier with a
@ -222,11 +289,11 @@ END OF COPYRIGHT STATEMENT
(Windows), please consult with an experienced user of your system.
Sorry, we cannot answer questions about basic Internet skills.
Unpacking the archive will produce a directory called "gd1.4".
Unpacking the archive will produce a directory called "gd1.5".
For Unix
cd to the gd1.4 directory and examine the Makefile, which you will
cd to the gd1.5 directory and examine the Makefile, which you will
probably need to change slightly depending on your operating system
and your needs.
@ -476,6 +543,9 @@ im = gdImageCreate(64, 64);
gdImageDestroy(im);
gdImageCreateFromGif(FILE *in) _(FUNCTION)_
gdImageCreateFromGifCtx(gdIOCtx *in) _(FUNCTION)_
gdImageCreateFromGif is called to load images from GIF format
files. Invoke gdImageCreateFromGif with an already opened
pointer to a file containing the desired image.
@ -533,6 +603,9 @@ static int freadWrapper(void *context, char *buf, int len)
}
gdImageCreateFromGd(FILE *in) _(FUNCTION)_
gdImageCreateFromGdCtx(gdIOCtx *in) _(FUNCTION)_
gdImageCreateFromGd is called to load images from gd format
files. Invoke gdImageCreateFromGd with an already opened
pointer to a file containing the desired image in the gd file
@ -556,6 +629,46 @@ fclose(in);
/* ... Use the image ... */
gdImageDestroy(im);
gdImageCreateFromGd2(FILE *in) _(FUNCTION)_
gdImageCreateFromGd2Ctx(gdIOCtx *in) _(FUNCTION)_
gdImageCreateFromGd2 is called to load images from gd2 format
files. Invoke gdImageCreateFromGd2 with an already opened
pointer to a file containing the desired image in the gd2 file
format, which is specific to gd2 and intended for fast loading
of parts of large images. (It is a compressed format, but
generally not as good a LZW compression). gdImageCreateFromGd
returns a gdImagePtr to the new image, or NULL if unable to
load the image (most often because the file is corrupt or does
not contain a gd format image). gdImageCreateFromGd2 does _not_
close the file. You can inspect the sx and sy members of the
image to determine its size. The image must eventually be
destroyed using gdImageDestroy().
... inside a function ...
gdImagePtr im;
FILE *in;
in = fopen("mygd.gd2", "rb");
im = gdImageCreateFromGd2(in);
fclose(in);
/* ... Use the image ... */
gdImageDestroy(im);
gdImageCreateFromGd2Part(FILE *in, int srcX, int srcY, int w, int h)
_(FUNCTION)_
gdImageCreateFromGd2PartCtx(gdIOCtx *in) _(FUNCTION)_
gdImageCreateFromGd2Part is called to load parts of images from
gd2 format files. Invoked in the same way as
gdImageCreateFromGd2, but with extra parameters indicating the
source (x, y) and width/height of the desired image.
gdImageCreateFromGd2Part returns a gdImagePtr to the new image,
or NULL if unable to load the image. The image must eventually
be destroyed using gdImageDestroy().
gdImageCreateFromXbm(FILE *in) _(FUNCTION)_
gdImageCreateFromXbm is called to load images from X bitmap
format files. Invoke gdImageCreateFromXbm with an already
@ -620,6 +733,12 @@ fclose(out);
/* Destroy image */
gdImageDestroy(im);
void* gdImageGifPtr(gdImagePtr im, int *size) _(FUNCTION)_
Identical to gdImageGif except that it returns a pointer to a
memory area with the GIF data. This memory must be freed by the
caller when it is no longer needed. The 'size' parameter
received the total size of the block of memory.
gdImageGifToSink(gdImagePtr im, gdSinkPtr out) _(FUNCTION)_
gdImageGifToSink is called to write a GIF to a data "sink"
(destination) other than a file. Usage is very similar to the
@ -687,6 +806,64 @@ fclose(out);
/* Destroy image */
gdImageDestroy(im);
void* gdImageGdPtr(gdImagePtr im, int *size) _(FUNCTION)_
Identical to gdImageGd except that it returns a pointer to a
memory area with the GD data. This memory must be freed by the
caller when it is no longer needed. The 'size' parameter
received the total size of the block of memory.
void gdImageGd2(gdImagePtr im, FILE *out, int chunkSize, int fmt)
_(FUNCTION)_
gdImageGd2 outputs the specified image to the specified file in
the gd2 image format. The file must be open for writing. Under
MSDOS and all versions of Windows, it is important to use "wb"
as opposed to simply "w" as the mode when opening the file, and
under Unix there is no penalty for doing so. gdImageGd2 does
_not_ close the file; your code must do so.
The gd2 image format is intended for fast reads and writes of
parts of images. It is a compressed format, and well suited to
retrieving smll sections of much larger images. The third and
fourth parameters are the 'chunk size' and format
resposectively.
The file is stored as a series of compressed subimages, and the
_Chunk Size_ determines the sub-image size - a value of zero
causes the GD library to use the default.
It is also possible to store GD2 files in an uncompressed
format, in which case the fourth parameter should be
GD2_FMT_RAW.
... inside a function ...
gdImagePtr im;
int black, white;
FILE *out;
/* Create the image */
im = gdImageCreate(100, 100);
/* Allocate background */
white = gdImageColorAllocate(im, 255, 255, 255);
/* Allocate drawing color */
black = gdImageColorAllocate(im, 0, 0, 0);
/* Draw rectangle */
gdImageRectangle(im, 0, 0, 99, 99, black);
/* Open output file in binary mode */
out = fopen("rect.gd", "wb");
/* Write gd2 format file */
gdImageGd2(im, out, 0, GD2_FMT_COMPRESSED);
/* Close file */
fclose(out);
/* Destroy image */
gdImageDestroy(im);
void* gdImageGd2Ptr(gdImagePtr im, int chunkSize, int fmt, int *size)
_(FUNCTION)_
Identical to gdImageGd2 except that it returns a pointer to a
memory area with the GD2 data. This memory must be freed by the
caller when it is no longer needed. The 'size' parameter
received the total size of the block of memory.
Drawing Functions
void gdImageSetPixel(gdImagePtr im, int x, int y, int color)
@ -1782,6 +1959,45 @@ fclose(out);
gdImageDestroy(im_in);
gdImageDestroy(im_out);
void gdImageCopyMerge(gdImagePtr dst, gdImagePtr src, int dstX,
int dstY, int srcX, int srcY, int w, int h, int pct)
_(FUNCTION)_
gdImageCopyMerge is almost identical to gdImageCopy,
except that it 'merges' the two images by an amount
specified in the last parameter. If the last parameter is
100, then it will function identically to gdImageCopy -
the source image replaces the pixels in the destination.
If, however, the _pct_ parameter is less than 100, then
the two images are merged. With pct = 0, no action is
taken.
This feature is most useful to 'highlight' sections of an
image by merging a solid color with pct = 50:
... Inside a function ...
gdImageCopyMerge(im_out, im_in, 100, 200, 0, 0, 30, 50, 50);
void gdImageCopyMergeGray(gdImagePtr dst, gdImagePtr src, int
dstX, int dstY, int srcX, int srcY, int w, int h, int
pct) _(FUNCTION)_
gdImageCopyMergeGray is almost identical to
gdImageCopyMerge, except that when merging images it
preserves the hue of the source by converting the
destination pixels to grey scale before the copy
operation.
... Inside a function ...
gdImageCopyMergeGray(im_out, im_in, 100, 200, 0, 0, 30, 50, 50);
void gdImagePaletteCopy(gdImagePtr dst, gdImagePtr src)
_(FUNCTION)_
Copies a palette from one image to another, doing it's
best to match the colors in the target image to the
colors in the source palette.
Miscellaneous Functions
gdImageInterlace(gdImagePtr im, int interlace) _(FUNCTION)_
@ -1919,6 +2135,74 @@ gdImageDestroy(im);
unless you have a need for high-speed loading of a
few frequently-used images in your program.
About the .gd2 image file format
In addition to reading and writing the GIF format
and reading the X Bitmap format, gd has the
capability to read and write its own ".gd2" format.
This format is _not_ intended for general purpose
use and should never be used to distribute images.
It is a compressed format allowing pseudo-random
access to large image files. Its purpose is solely
to allow very fast loading of _parts_ of images If
you are experiencing performance problems when
loading large, fixed GIF images your program needs
to produce its output images, you may wish to
examine the functions gdImageCreateFromGd2,
gdImageCreateFromGd2Part and gdImageGd2, which read
and write .gd2 format images.
The program "giftogd2.c" is provided as a simple
way of converting .gif files to .gd2 format.
About the gdIOCtx structure
Version 1.5 of GD incorporates a new style of I/O
based on an IOCtx structure (the most up-to-date
version can be found in io.h):
typedef struct gdIOCtx {
int (*getC)(struct gdIOCtx*);
int (*getBuf)(struct gdIOCtx*, void*, int);
void (*putC)(struct gdIOCtx*, int);
int (*putBuf)(struct gdIOCtx*, const void*, int);
int (*seek)(struct gdIOCtx*, const int);
long (*tell)(struct gdIOCtx*);
void (*free)(struct gdIOCtx*);
} gdIOCtx;
Most functions that accepted files in previous versions now
also have a counterpart that accepts an I/O
context. These functions have a 'Ctx' suffix.
The Ctx routines use the function pointers in the
I/O context pointed to by gdIOCtx to perform all
I/O. Examples of how to implement an I/O context
can be found in io_file.c (which provides a wrapper
for file routines), and io_dp.c (which implements
in-memory storage).
It is not necessary to implement all functions in
an I/O context if you know that it will only be
used in limited cirsumstances. At the time of
writing (Version 1.5, June 1999), the known
requirements are:
All Must have 'free',
Anything that reads from the context Must have
'getC' and 'getBuf',
Anything that writes to the context Must have
'putC' and 'putBuf'.
If gdCreateFromGd2Part is called Must also have
'seek' and 'tell'.
If gdImageGd2 is called Must also have 'seek' and
'tell'.
Please tell us you're using gd!
When you contact us and let us know you are using
@ -1932,8 +2216,11 @@ gdImageDestroy(im);
If you have problems
If you have any difficulties with gd, feel free to
contact the author, Thomas Boutell. Be sure to read
this manual carefully first.
contact the author, Thomas Boutell. Problems
relating to the gd2 format should be addressed to
Philip Warner.
_Be sure to read this manual carefully first. _
Alphabetical quick index
@ -1944,14 +2231,16 @@ gdImageDestroy(im);
gdImageColorDeallocate | gdImageColorExact |
gdImageColorTransparent | gdImageCopy |
gdImageCopyResized | gdImageCreate |
gdImageCreateFromGd | gdImageCreateFromGif |
gdImageCreateFromGd | gdImageCreateFromGd2 |
gdImageCreateFromGd2Part | gdImageCreateFromGif |
gdImageCreateFromGifSource | gdImageCreateFromXbm |
gdImageDashedLine | gdImageDestroy | gdImageFill |
gdImageFillToBorder | gdImageFilledRectangle |
gdImageGd | gdImageGetInterlaced | gdImageGetPixel
| gdImageGetTransparent | gdImageGif |
gdImageGifToSink | gdImageGreen | gdImageInterlace
| gdImageLine | gdImageFilledPolygon |
gdImageGd | gdImageGd2 | gdImageGetInterlaced |
gdImageGetPixel | gdImageGetTransparent |
gdImageGif | gdImageGifToSink | gdImageGreen |
gdImageInterlace | gdImageLine |
gdImageFilledPolygon | gdImagePaletteCopy |
gdImagePolygon | gdImagePtr | gdImageRectangle |
gdImageRed | gdImageSetBrush | gdImageSetPixel |
gdImageSetStyle | gdImageSetTile | gdImageString |