- sync to 1.5.0
parent
3d209b2ed9
commit
0b6804ee48
|
@ -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
|
||||
|
40
src/Makefile
40
src/Makefile
|
@ -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
|
||||
|
||||
|
|
|
@ -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"; }
|
||||
}
|
||||
|
||||
|
|
74
src/gd.h
74
src/gd.h
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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 */
|
||||
}
|
||||
}
|
||||
|
|
@ -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. | */
|
||||
/* +-------------------------------------------------------------------+ */
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
789
src/index.html
789
src/index.html
File diff suppressed because it is too large
Load Diff
|
@ -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"));
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
323
src/readme.txt
323
src/readme.txt
|
@ -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 |
|
||||
|
|
Loading…
Reference in New Issue