Initial commit of the built-in libgd based on GD-2.0.1

This initial checkin has no changes to any of the libgd code so it can
be used as a basis for diffs.  It also will not build currently because
of this.  The PHP gd checks need to be incorporated along with a bit of
other config magic.  It also shouldn't break the build and will only
take effect if you use --with-gd=php right now.
This commit is contained in:
Rasmus Lerdorf 2002-04-13 02:03:09 +00:00
parent c46199f5b9
commit 7a8cade379
51 changed files with 34940 additions and 1 deletions

View File

@ -208,7 +208,17 @@ AC_DEFUN(PHP_GD_CHECK_VERSION,[
PHP_ARG_WITH(gd, for GD support,
[ --with-gd[=DIR] Include GD support (DIR is GD's install dir).])
if test "$PHP_GD" != "no"; then
if test "$PHP_GD" == "php"; then
GD_MODULE_TYPE=builtin
sources="libgd/gd.c libgd/gd_gd.c libgd/gd_gd2.c libgd/gd_io.c libgd/gd_io_dp.c \
libgd/gd_io_file.c libgd/gd_ss.c libgd/gd_io_ss.c libgd/gd_png.c libgd/gd_jpeg.c \
libgd/gdxpm.c libgd/gdfontt.c libgd/gdfonts.c libgd/gdfontmb.c libgd/gdfontl.c \
libgd/gdfontg.o libgd/gdtables.c libgd/gdft.c libgd/gdcache.c libgd/gdkanji.c \
libgd/wbmp.c libgd/gd_wbmp.c libgd/gdhelpers.c libgd/gd_topal.c"
PHP_NEW_EXTENSION(mysql, gd.c gdcache.c gdttf.c gdt1.c $sources, $ext_shared,,-I@ext_srcdir@/libgd)
PHP_ADD_BUILD_DIR($ext_builddir/libgd)
else
if test "$PHP_GD" != "no"; then
PHP_NEW_EXTENSION(gd, gd.c gdcache.c gdttf.c gdt1.c, $ext_shared)
PHP_SUBST(GD_SHARED_LIBADD)
@ -259,4 +269,5 @@ dnl SuSE 6.x package structure
PHP_EXPAND_PATH($GD_INCLUDE, GD_INCLUDE)
PHP_ADD_INCLUDE($GD_INCLUDE)
fi
fi

72
ext/gd/libgd/README Normal file
View File

@ -0,0 +1,72 @@
This directory contains the GD library available from
http://www.boutell.com/gd/
The inclusion of this library with PHP is in response to the
popularity of the GD extension and the GD library itself. We felt
it would be worthwhile to make sure everyone had access to the
features of GD and by bundling GD with PHP we have a known target to
work against.
In addition to the above, a number of motivated PHP hackers have
expressed an interest in extending the functionality of the PHP-GD
combination. By putting a version of GD into PHP CVS we have provided
a sandbox for them to throw sand at each other in. Any and all
improvements we make to the GD library will be contributed back to the
original maintainers of this library at boutell.com.
The following statement is from the original GD package:
Credits and license terms
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. _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
Portions copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
by Cold Spring Harbor Laboratory. Funded under Grant P41-RR02188 by
the National Institutes of Health.
Portions copyright 1996, 1997, 1998, 1999, 2000, 2001 by
Boutell.Com, Inc.
Portions relating to GD2 format copyright 1999, 2000 Philip Warner.
Portions relating to PNG copyright 1999, 2000 Greg Roelofs.
Portions relating to libttf copyright 1999, 2000 John Ellson
(ellson@lucent.com).
Portions relating to JPEG and to color quantization copyright 2000,
Doug Becker and copyright (C) 1994-1998, Thomas G. Lane. This
software is based in part on the work of the Independent JPEG
Group. See the file README-JPEG.TXT for more information.
Portions relating to WBMP copyright 2000 Maurice Szmurlo and Johan
Van den Brande.
_Permission has been granted to copy, distribute and modify gd in
any context without fee, including a commercial application,
provided that this notice is present in user-accessible supporting
documentation._
This does not affect your ownership of the derived work itself, and
the intent is to assure proper credit for the authors of gd, not to
interfere with your productive use of gd. If you have questions,
ask. "Derived works" includes all programs that utilize the
library. Credit must be given in user-accessible documentation.
_This software is provided "AS IS."_ The copyright holders disclaim
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.
Although their code does not appear in gd 2.0.1, the authors wish
to thank David Koblas, David Rowley, and Hutchison Avenue Software
Corporation for their prior contributions.
END OF COPYRIGHT STATEMENT

2526
ext/gd/libgd/gd.c Normal file

File diff suppressed because it is too large Load Diff

496
ext/gd/libgd/gd.h Normal file
View File

@ -0,0 +1,496 @@
#ifndef GD_H
#define GD_H 1
#ifdef __cplusplus
extern "C" {
#endif
/* default fontpath for unix systems */
#define DEFAULT_FONTPATH "/usr/share/fonts/truetype"
#define PATHSEPARATOR ":"
/* gd.h: declarations file for the graphic-draw module.
* 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." Thomas Boutell and
* Boutell.Com, Inc. disclaim 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. */
/* stdio is needed for file I/O. */
#include <stdio.h>
#include "gd_io.h"
/* The maximum number of palette entries in palette-based images.
In the wonderful new world of gd 2.0, you can of course have
many more colors when using truecolor mode. */
#define gdMaxColors 256
/* 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
read-only purposes. */
/* If 'truecolor' is set true, the image is truecolor;
pixels are represented by integers, which
must be 32 bits wide or more.
True colors are repsented as follows:
ARGB
Where 'A' (alpha channel) occupies only the
LOWER 7 BITS of the MSB. This very small
loss of alpha channel resolution allows gd 2.x
to keep backwards compatibility by allowing
signed integers to be used to represent colors,
and negative numbers to represent special cases,
just as in gd 1.x. */
#define gdAlphaMax 127
#define gdAlphaOpaque 0
#define gdAlphaTransparent 127
#define gdRedMax 255
#define gdGreenMax 255
#define gdBlueMax 255
#define gdTrueColorGetAlpha(c) (((c) & 0x7F000000) >> 24)
#define gdTrueColorGetRed(c) (((c) & 0xFF0000) >> 16)
#define gdTrueColorGetGreen(c) (((c) & 0x00FF00) >> 8)
#define gdTrueColorGetBlue(c) ((c) & 0x0000FF)
/* This function accepts truecolor pixel values only. The
source color is composited with the destination color
based on the alpha channel value of the source color.
The resulting color is opaque. */
int gdAlphaBlend(int dest, int src);
typedef struct gdImageStruct {
/* Palette-based image pixels */
unsigned char ** pixels;
int sx;
int sy;
/* These are valid in palette images only. See also
'alpha', which appears later in the structure to
preserve binary backwards compatibility */
int colorsTotal;
int red[gdMaxColors];
int green[gdMaxColors];
int blue[gdMaxColors];
int open[gdMaxColors];
/* For backwards compatibility, this is set to the
first palette entry with 100% transparency,
and is also set and reset by the
gdImageColorTransparent function. Newer
applications can allocate palette entries
with any desired level of transparency; however,
bear in mind that many viewers, notably
many web browsers, fail to implement
full alpha channel for PNG and provide
support for full opacity or transparency only. */
int transparent;
int *polyInts;
int polyAllocated;
struct gdImageStruct *brush;
struct gdImageStruct *tile;
int brushColorMap[gdMaxColors];
int tileColorMap[gdMaxColors];
int styleLength;
int stylePos;
int *style;
int interlace;
/* New in 2.0: thickness of line. Initialized to 1. */
int thick;
/* New in 2.0: alpha channel for palettes. Note that only
Macintosh Internet Explorer and (possibly) Netscape 6
really support multiple levels of transparency in
palettes, to my knowledge, as of 2/15/01. Most
common browsers will display 100% opaque and
100% transparent correctly, and do something
unpredictable and/or undesirable for levels
in between. TBB */
int alpha[gdMaxColors];
/* Truecolor flag and pixels. New 2.0 fields appear here at the
end to minimize breakage of existing object code. */
int trueColor;
int ** tpixels;
/* Should alpha channel be copied, or applied, each time a
pixel is drawn? This applies to truecolor images only.
No attempt is made to alpha-blend in palette images,
even if semitransparent palette entries exist.
To do that, build your image as a truecolor image,
then quantize down to 8 bits. */
int alphaBlendingFlag;
/* Should the alpha channel of the image be saved? This affects
PNG at the moment; other future formats may also
have that capability. JPEG doesn't. */
int saveAlphaFlag;
} gdImage;
typedef gdImage * gdImagePtr;
typedef struct {
/* # of characters in font */
int nchars;
/* First character is numbered... (usually 32 = space) */
int offset;
/* Character width and height */
int w;
int h;
/* Font data; array of characters, one row after another.
Easily included in code, also easily loaded from
data files. */
char *data;
} gdFont;
/* Text functions take these. */
typedef gdFont *gdFontPtr;
/* For backwards compatibility only. Use gdImageSetStyle()
for MUCH more flexible line drawing. Also see
gdImageSetBrush(). */
#define gdDashSize 4
/* Special colors. */
#define gdStyled (-2)
#define gdBrushed (-3)
#define gdStyledBrushed (-4)
#define gdTiled (-5)
/* NOT the same as the transparent color index.
This is used in line styles only. */
#define gdTransparent (-6)
/* Functions to manipulate images. */
/* Creates a palette-based image (up to 256 colors). */
gdImagePtr gdImageCreate(int sx, int sy);
/* An alternate name for the above (2.0). */
#define gdImageCreatePalette gdImageCreate
/* Creates a truecolor image (millions of colors). */
gdImagePtr gdImageCreateTrueColor(int sx, int sy);
/* Creates an image from various file types. These functions
return a palette or truecolor image based on the
nature of the file being loaded. Truecolor PNG
stays truecolor; palette PNG stays palette-based;
JPEG is always truecolor. */
gdImagePtr gdImageCreateFromPng(FILE *fd);
gdImagePtr gdImageCreateFromPngCtx(gdIOCtxPtr in);
gdImagePtr gdImageCreateFromWBMP(FILE *inFile);
gdImagePtr gdImageCreateFromWBMPCtx(gdIOCtx *infile);
gdImagePtr gdImageCreateFromJpeg(FILE *infile);
gdImagePtr gdImageCreateFromJpegCtx(gdIOCtx *infile);
/* A custom data source. */
/* The source function must return -1 on error, otherwise the number
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;
} gdSource, *gdSourcePtr;
gdImagePtr gdImageCreateFromPngSource(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);
/* Replaces or blends with the background depending on the
most recent call to gdImageAlphaBlending and the
alpha channel value of 'color'; default is to overwrite.
Tiling and line styling are also implemented
here. All other gd drawing functions pass through this call,
allowing for many useful effects. */
void gdImageSetPixel(gdImagePtr im, int x, int y, int color);
int gdImageGetPixel(gdImagePtr im, int x, int y);
void gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
/* For backwards compatibility only. Use gdImageSetStyle()
for much more flexible line drawing. */
void gdImageDashedLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
/* Corners specified (not width and height). Upper left first, lower right
second. */
void gdImageRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
/* Solid bar. Upper left corner first, lower right corner second. */
void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
int gdImageBoundsSafe(gdImagePtr im, int x, int y);
void gdImageChar(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color);
void gdImageCharUp(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color);
void gdImageString(gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color);
void gdImageStringUp(gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color);
void gdImageString16(gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color);
void gdImageStringUp16(gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color);
/* Calls gdImageStringFT. Provided for backwards compatibility only. */
char *gdImageStringTTF(gdImage *im, int *brect, int fg, char *fontlist,
double ptsize, double angle, int x, int y, char *string);
/* FreeType 2 text output */
char *gdImageStringFT(gdImage *im, int *brect, int fg, char *fontlist,
double ptsize, double angle, int x, int y, char *string);
/* Point type for use in polygon drawing. */
typedef struct {
int x, y;
} gdPoint, *gdPointPtr;
void gdImagePolygon(gdImagePtr im, gdPointPtr p, int n, int c);
void gdImageFilledPolygon(gdImagePtr im, gdPointPtr p, int n, int c);
/* These functions still work with truecolor images,
for which they never return error. */
int gdImageColorAllocate(gdImagePtr im, int r, int g, int b);
/* gd 2.0: palette entries with non-opaque transparency are permitted. */
int gdImageColorAllocateAlpha(gdImagePtr im, int r, int g, int b, int a);
/* Assumes opaque is the preferred alpha channel value */
int gdImageColorClosest(gdImagePtr im, int r, int g, int b);
/* Closest match taking all four parameters into account.
A slightly different color with the same transparency
beats the exact same color with radically different
transparency */
int gdImageColorClosestAlpha(gdImagePtr im, int r, int g, int b, int a);
/* Returns exact, 100% opaque matches only */
int gdImageColorExact(gdImagePtr im, int r, int g, int b);
/* Returns an exact match only, including alpha */
int gdImageColorExactAlpha(gdImagePtr im, int r, int g, int b, int a);
/* Opaque only */
int gdImageColorResolve(gdImagePtr im, int r, int g, int b);
/* Based on gdImageColorExactAlpha and gdImageColorClosestAlpha */
int gdImageColorResolveAlpha(gdImagePtr im, int r, int g, int b, int a);
/* A simpler way to obtain an opaque truecolor value for drawing on a
truecolor image. Not for use with palette images! */
#define gdTrueColor(r, g, b) (((r) << 16) + \
((g) << 8) + \
(b))
/* Returns a truecolor value with an alpha channel component.
gdAlphaMax (127, **NOT 255**) is transparent, 0 is completely
opaque. */
#define gdTrueColorAlpha(r, g, b, a) (((a) << 24) + \
((r) << 16) + \
((g) << 8) + \
(b))
void gdImageColorDeallocate(gdImagePtr im, int color);
/* Converts a truecolor image to a palette-based image,
using a high-quality two-pass quantization routine
which attempts to preserve alpha channel information
as well as R/G/B color information when creating
a palette. If ditherFlag is set, the image will be
dithered to approximate colors better, at the expense
of some obvious "speckling." colorsWanted can be
anything up to 256. If the original source image
includes photographic information or anything that
came out of a JPEG, 256 is strongly recommended.
Better yet, don't use this function -- write real
truecolor PNGs and JPEGs. The disk space gain of
conversion to palette is not great (for small images
it can be negative) and the quality loss is ugly. */
void gdImageTrueColorToPalette(gdImagePtr im, int ditherFlag, int colorsWanted);
/* Specifies a color index (if a palette image) or an
RGB color (if a truecolor image) which should be
considered 100% transparent. FOR TRUECOLOR IMAGES,
THIS IS IGNORED IF AN ALPHA CHANNEL IS BEING
SAVED. Use gdImageSaveAlpha(im, 0); to
turn off the saving of a full alpha channel in
a truecolor image. Note that gdImageColorTransparent
is usually compatible with older browsers that
do not understand full alpha channels well. TBB */
void gdImageColorTransparent(gdImagePtr im, int color);
void gdImagePaletteCopy(gdImagePtr dst, gdImagePtr src);
void gdImagePng(gdImagePtr im, FILE *out);
void gdImagePngCtx(gdImagePtr im, gdIOCtx *out);
void gdImageWBMP(gdImagePtr image, int fg, FILE *out);
void gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out);
/* Guaranteed to correctly free memory returned
by the gdImage*Ptr functions */
void gdFree(void *m);
/* Best to free this memory with gdFree(), not free() */
void *gdImageWBMPPtr(gdImagePtr im, int *size, int fg);
void gdImageJpeg(gdImagePtr im, FILE *out, int quality);
void gdImageJpegCtx(gdImagePtr im, gdIOCtx *out, int quality);
/* Best to free this memory with gdFree(), not free() */
void *gdImageJpegPtr(gdImagePtr im, int *size, int quality);
/* A custom data sink. For backwards compatibility. Use
gdIOCtx instead. */
/* The sink function must return -1 on error, otherwise the number
of bytes written, which must be equal to len. */
/* context will be passed to your sink function. */
typedef struct {
int (*sink) (void *context, const char *buffer, int len);
void *context;
} gdSink, *gdSinkPtr;
void gdImagePngToSink(gdImagePtr im, gdSinkPtr out);
void gdImageGd(gdImagePtr im, FILE *out);
void gdImageGd2(gdImagePtr im, FILE *out, int cs, int fmt);
/* Best to free this memory with gdFree(), not free() */
void* gdImagePngPtr(gdImagePtr im, int *size);
/* Best to free this memory with gdFree(), not free() */
void* gdImageGdPtr(gdImagePtr im, int *size);
/* Best to free this memory with gdFree(), not free() */
void* gdImageGd2Ptr(gdImagePtr im, int cs, int fmt, int *size);
void gdImageEllipse(gdImagePtr im, int cx, int cy, int w, int h, int color);
/* Style is a bitwise OR ( | operator ) of these.
gdArc and gdChord are mutually exclusive;
gdChord just connects the starting and ending
angles with a straight line, while gdArc produces
a rounded edge. gdPie is a synonym for gdArc.
gdNoFill indicates that the arc or chord should be
outlined, not filled. gdEdged, used together with
gdNoFill, indicates that the beginning and ending
angles should be connected to the center; this is
a good way to outline (rather than fill) a
'pie slice'. */
#define gdArc 0
#define gdPie gdArc
#define gdChord 1
#define gdNoFill 2
#define gdEdged 4
void gdImageFilledArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style);
void gdImageArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color);
void gdImageFilledEllipse(gdImagePtr im, int cx, int cy, int w, int h, 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. Does NOT attempt
to average the entire set of source pixels that scale down onto the
destination pixel. */
void gdImageCopyResized(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH);
/* gd 2.0: stretches or shrinks to fit, as needed. When called with a
truecolor destination image, this function averages the
entire set of source pixels that scale down onto the
destination pixel, taking into account what portion of the
destination pixel each source pixel represents. This is a
floating point operation, but this is not a performance issue
on modern hardware, except for some embedded devices. If the
destination is a palette image, gdImageCopyResized is
substituted automatically. */
void gdImageCopyResampled(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);
void gdImageSetTile(gdImagePtr im, gdImagePtr tile);
void gdImageSetStyle(gdImagePtr im, int *style, int noOfPixels);
/* Line thickness (defaults to 1). Affects lines, ellipses,
rectangles, polygons and so forth. */
void gdImageSetThickness(gdImagePtr im, int thickness);
/* On or off (1 or 0) for all three of these. */
void gdImageInterlace(gdImagePtr im, int interlaceArg);
void gdImageAlphaBlending(gdImagePtr im, int alphaBlendingArg);
void gdImageSaveAlpha(gdImagePtr im, int saveAlphaArg);
/* Macros to access information about images. */
/* Returns nonzero if the image is a truecolor image,
zero for a palette image. */
#define gdImageTrueColor(im) ((im)->trueColor)
#define gdImageSX(im) ((im)->sx)
#define gdImageSY(im) ((im)->sy)
#define gdImageColorsTotal(im) ((im)->colorsTotal)
#define gdImageRed(im, c) ((im)->trueColor ? gdTrueColorGetRed(c) : \
(im)->red[(c)])
#define gdImageGreen(im, c) ((im)->trueColor ? gdTrueColorGetGreen(c) : \
(im)->green[(c)])
#define gdImageBlue(im, c) ((im)->trueColor ? gdTrueColorGetBlue(c) : \
(im)->blue[(c)])
#define gdImageAlpha(im, c) ((im)->trueColor ? gdTrueColorGetAlpha(c) : \
(im)->alpha[(c)])
#define gdImageGetTransparent(im) ((im)->transparent)
#define gdImageGetInterlaced(im) ((im)->interlace)
/* These macros provide direct access to pixels in
palette-based and truecolor images, respectively.
If you use these macros, you must perform your own
bounds checking. Use of the macro for the correct type
of image is also your responsibility. */
#define gdImagePalettePixel(im, x, y) (im)->pixels[(y)][(x)]
#define gdImageTrueColorPixel(im, x, y) (im)->tpixels[(y)][(x)]
/* I/O Support routines. */
gdIOCtx* gdNewFileCtx(FILE*);
gdIOCtx* gdNewDynamicCtx(int, void*);
gdIOCtx* gdNewSSCtx(gdSourcePtr in, gdSinkPtr out);
void* gdDPExtractData(struct gdIOCtx* ctx, int *size);
#define GD2_CHUNKSIZE 128
#define GD2_CHUNKSIZE_MIN 64
#define GD2_CHUNKSIZE_MAX 4096
#define GD2_VERS 2
#define GD2_ID "gd2"
#define GD2_FMT_RAW 1
#define GD2_FMT_COMPRESSED 2
/* Image comparison definitions */
int gdImageCompare(gdImagePtr im1, gdImagePtr im2);
#define GD_CMP_IMAGE 1 /* Actual image IS different */
#define GD_CMP_NUM_COLORS 2 /* Number of Colours in pallette differ */
#define GD_CMP_COLOR 4 /* Image colours differ */
#define GD_CMP_SIZE_X 8 /* Image width differs */
#define GD_CMP_SIZE_Y 16 /* Image heights differ */
#define GD_CMP_TRANSPARENT 32 /* Transparent colour */
#define GD_CMP_BACKGROUND 64 /* Background colour */
#define GD_CMP_INTERLACE 128 /* Interlaced setting */
#define GD_CMP_TRUECOLOR 256 /* Truecolor vs palette differs */
/* resolution affects ttf font rendering, particularly hinting */
#define GD_RESOLUTION 96 /* pixels per inch */
#ifdef __cplusplus
}
#endif
#endif /* GD_H */

64
ext/gd/libgd/gd2copypal.c Normal file
View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .png 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);
return 0;
}

60
ext/gd/libgd/gd2time.c Normal file
View File

@ -0,0 +1,60 @@
#include <stdio.h>
#include <stdlib.h> /* for atoi */
#include <time.h> /* For time */
#include "gd.h"
/* A short program which converts a .png 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;
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);
return 0;
}

45
ext/gd/libgd/gd2topng.c Normal file
View File

@ -0,0 +1,45 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .png 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: gd2topng filename.gd2 filename.png\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 GD2 format!\n");
exit (1);
}
out = fopen (argv[2], "wb");
if (!out)
{
fprintf (stderr, "Output file cannot be written to!\n");
gdImageDestroy (im);
exit (1);
}
gdImagePng (im, out);
fclose (out);
gdImageDestroy (im);
return 0;
}

View File

@ -0,0 +1,741 @@
/* This is potentially great stuff, but fails against the test
program at the end. This would probably be much more
efficent than the implementation currently in gd.c if the
errors in the output were corrected. TBB */
#if 0
#include "gd.h"
#include <math.h>
/* Courtesy of F J Franklin. */
static gdPoint gdArcClosest (int width, int height, int angle);
void
gdImageFilledEllipse (gdImagePtr im, int cx, int cy, int width, int height, int color)
{
gdImageFilledArc (im, cx, cy, width, height, 0, 360, color, gdChord);
}
void
gdImageFilledArc (gdImagePtr im, int cx, int cy, int width, int height, int s, int e, int color, int style)
{
gdPoint pt[7];
gdPoint axis_pt[4];
int angle;
int have_s = 0;
int have_e = 0;
int flip_x = 0;
int flip_y = 0;
int conquer = 0;
int i;
int a;
int b;
int x;
int y;
long s_sin = 0;
long s_cos = 0;
long e_sin = 0;
long e_cos = 0;
long w; /* a * 2 */
long h; /* b * 2 */
long x2; /* x * 2 */
long y2; /* y * 2 */
long lx2; /* x * 2 (line) */
long ly2; /* y * 2 (line) */
long ws; /* (a * 2)^2 */
long hs; /* (b * 2)^2 */
long whs; /* (a * 2)^2 * (b * 2)^2 */
long g; /* decision variable */
long lg; /* decision variable (line) */
width = (width & 1) ? (width + 1) : (width);
height = (height & 1) ? (height + 1) : (height);
a = width / 2;
b = height / 2;
axis_pt[0].x = a;
axis_pt[0].y = 0;
axis_pt[1].x = 0;
axis_pt[1].y = b;
axis_pt[2].x = -a;
axis_pt[2].y = 0;
axis_pt[3].x = 0;
axis_pt[3].y = -b;
if (s == e)
return;
if ((e - s) >= 360)
{
s = 0;
e = 0;
}
while (s < 0)
s += 360;
while (s >= 360)
s -= 360;
while (e < 0)
e += 360;
while (e >= 360)
e -= 360;
if (e <= s)
e += 360;
/* I'm assuming a chord-rule at the moment. Need to add origin to get a
* pie-rule, but will need to set chord-rule before recursion...
*/
for (i = 0; i < 4; i++)
{
if ((s < (i + 1) * 90) && (e > (i + 1) * 90))
{
gdImageFilledArc (im, cx, cy, width, height, s, (i + 1) * 90, color, gdChord);
pt[0] = gdArcClosest (width, height, s);
pt[0].x += cx;
pt[0].y += cy;
pt[1].x = cx + axis_pt[(i + 1) & 3].x;
pt[1].y = cy + axis_pt[(i + 1) & 3].y;
if (e <= (i + 2) * 90)
{
gdImageFilledArc (im, cx, cy, width, height, (i + 1) * 90, e, color, gdChord);
pt[2] = gdArcClosest (width, height, e);
pt[2].x += cx;
pt[2].y += cy;
if (style == gdChord)
{
gdImageFilledPolygon (im, pt, 3, color);
gdImagePolygon (im, pt, 3, color);
}
else if (style == gdPie)
{
pt[3].x = cx;
pt[3].y = cy;
gdImageFilledPolygon (im, pt, 4, color);
gdImagePolygon (im, pt, 4, color);
}
}
else
{
gdImageFilledArc (im, cx, cy, width, height, (i + 1) * 90, (i + 2) * 90, color, gdChord);
pt[2].x = cx + axis_pt[(i + 2) & 3].x;
pt[2].y = cy + axis_pt[(i + 2) & 3].y;
if (e <= (i + 3) * 90)
{
gdImageFilledArc (im, cx, cy, width, height, (i + 2) * 90, e, color, gdChord);
pt[3] = gdArcClosest (width, height, e);
pt[3].x += cx;
pt[3].y += cy;
if (style == gdChord)
{
gdImageFilledPolygon (im, pt, 4, color);
gdImagePolygon (im, pt, 4, color);
}
else if (style == gdPie)
{
pt[4].x = cx;
pt[4].y = cy;
gdImageFilledPolygon (im, pt, 5, color);
gdImagePolygon (im, pt, 5, color);
}
}
else
{
gdImageFilledArc (im, cx, cy, width, height, (i + 2) * 90, (i + 3) * 90, color, gdChord);
pt[3].x = cx + axis_pt[(i + 3) & 3].x;
pt[3].y = cy + axis_pt[(i + 3) & 3].y;
if (e <= (i + 4) * 90)
{
gdImageFilledArc (im, cx, cy, width, height, (i + 3) * 90, e, color, gdChord);
pt[4] = gdArcClosest (width, height, e);
pt[4].x += cx;
pt[4].y += cy;
if (style == gdChord)
{
gdImageFilledPolygon (im, pt, 5, color);
gdImagePolygon (im, pt, 5, color);
}
else if (style == gdPie)
{
pt[5].x = cx;
pt[5].y = cy;
gdImageFilledPolygon (im, pt, 6, color);
gdImagePolygon (im, pt, 6, color);
}
}
else
{
gdImageFilledArc (im, cx, cy, width, height, (i + 3) * 90, (i + 4) * 90, color, gdChord);
pt[4].x = cx + axis_pt[(i + 4) & 3].x;
pt[4].y = cy + axis_pt[(i + 4) & 3].y;
gdImageFilledArc (im, cx, cy, width, height, (i + 4) * 90, e, color, gdChord);
pt[5] = gdArcClosest (width, height, e);
pt[5].x += cx;
pt[5].y += cy;
if (style == gdChord)
{
gdImageFilledPolygon (im, pt, 6, color);
gdImagePolygon (im, pt, 6, color);
}
else if (style == gdPie)
{
pt[6].x = cx;
pt[6].y = cy;
gdImageFilledPolygon (im, pt, 7, color);
gdImagePolygon (im, pt, 7, color);
}
}
}
}
return;
}
}
/* At this point we have only arcs that lies within a quadrant -
* map this to first quadrant...
*/
if ((s >= 90) && (e <= 180))
{
angle = s;
s = 180 - e;
e = 180 - angle;
flip_x = 1;
}
if ((s >= 180) && (e <= 270))
{
s = s - 180;
e = e - 180;
flip_x = 1;
flip_y = 1;
}
if ((s >= 270) && (e <= 360))
{
angle = s;
s = 360 - e;
e = 360 - angle;
flip_y = 1;
}
if (s == 0)
{
s_sin = 0;
s_cos = (long) ((double) 32768);
}
else
{
s_sin = (long) ((double) 32768 * sin ((double) s * M_PI / (double) 180));
s_cos = (long) ((double) 32768 * cos ((double) s * M_PI / (double) 180));
}
if (e == 0)
{
e_sin = (long) ((double) 32768);
e_cos = 0;
}
else
{
e_sin = (long) ((double) 32768 * sin ((double) e * M_PI / (double) 180));
e_cos = (long) ((double) 32768 * cos ((double) e * M_PI / (double) 180));
}
w = (long) width;
h = (long) height;
ws = w * w;
hs = h * h;
whs = 1;
while ((ws > 32768) || (hs > 32768))
{
ws = (ws + 1) / 2; /* Unfortunate limitations on integers makes */
hs = (hs + 1) / 2; /* drawing large ellipses problematic... */
whs *= 2;
}
while ((ws * hs) > (0x04000000L / whs))
{
ws = (ws + 1) / 2;
hs = (hs + 1) / 2;
whs *= 2;
}
whs *= ws * hs;
pt[0].x = w / 2;
pt[0].y = 0;
pt[2].x = 0;
pt[2].y = h / 2;
have_s = 0;
have_e = 0;
if (s == 0)
have_s = 1;
if (e == 90)
have_e = 1;
x2 = w;
y2 = 0; /* Starting point is exactly on ellipse */
g = x2 - 1;
g = g * g * hs + 4 * ws - whs;
while ((x2 * hs) > (y2 * ws)) /* Keep |tangent| > 1 */
{
y2 += 2;
g += ws * 4 * (y2 + 1);
if (g > 0) /* Need to drop */
{
x2 -= 2;
g -= hs * 4 * x2;
}
if ((have_s == 0) && ((s_sin * x2) <= (y2 * s_cos)))
{
pt[0].x = (int) (x2 / 2);
pt[0].y = (int) (y2 / 2);
have_s = 1;
}
if ((have_e == 0) && ((e_sin * x2) <= (y2 * e_cos)))
{
pt[2].x = (int) (x2 / 2);
pt[2].y = (int) (y2 / 2);
have_e = 1;
}
}
pt[1].x = (int) (x2 / 2);
pt[1].y = (int) (y2 / 2);
x2 = 0;
y2 = h; /* Starting point is exactly on ellipse */
g = y2 - 1;
g = g * g * ws + 4 * hs - whs;
while ((x2 * hs) < (y2 * ws))
{
x2 += 2;
g += hs * 4 * (x2 + 1);
if (g > 0) /* Need to drop */
{
y2 -= 2;
g -= ws * 4 * y2;
}
if ((have_s == 0) && ((s_sin * x2) >= (y2 * s_cos)))
{
pt[0].x = (int) (x2 / 2);
pt[0].y = (int) (y2 / 2);
have_s = 1;
}
if ((have_e == 0) && ((e_sin * x2) >= (y2 * e_cos)))
{
pt[2].x = (int) (x2 / 2);
pt[2].y = (int) (y2 / 2);
have_e = 1;
}
}
if ((have_s == 0) || (have_e == 0))
return; /* Bizarre case */
if (style == gdPie)
{
pt[3] = pt[0];
pt[4] = pt[1];
pt[5] = pt[2];
pt[0].x = cx + (flip_x ? (-pt[0].x) : pt[0].x);
pt[0].y = cy + (flip_y ? (-pt[0].y) : pt[0].y);
pt[1].x = cx;
pt[1].y = cy;
pt[2].x = cx + (flip_x ? (-pt[2].x) : pt[2].x);
pt[2].y = cy + (flip_y ? (-pt[2].y) : pt[2].y);
gdImageFilledPolygon (im, pt, 3, color);
gdImagePolygon (im, pt, 3, color);
pt[0] = pt[3];
pt[1] = pt[4];
pt[2] = pt[5];
}
if (((s_cos * hs) > (s_sin * ws)) && ((e_cos * hs) < (e_sin * ws)))
{ /* the points are on different parts of the curve...
* this is too tricky to try to handle, so divide and conquer:
*/
pt[3] = pt[0];
pt[4] = pt[1];
pt[5] = pt[2];
pt[0].x = cx + (flip_x ? (-pt[0].x) : pt[0].x);
pt[0].y = cy + (flip_y ? (-pt[0].y) : pt[0].y);
pt[1].x = cx + (flip_x ? (-pt[1].x) : pt[1].x);
pt[1].y = cy + (flip_y ? (-pt[1].y) : pt[1].y);
pt[2].x = cx + (flip_x ? (-pt[2].x) : pt[2].x);
pt[2].y = cy + (flip_y ? (-pt[2].y) : pt[2].y);
gdImageFilledPolygon (im, pt, 3, color);
gdImagePolygon (im, pt, 3, color);
pt[0] = pt[3];
pt[2] = pt[4];
conquer = 1;
}
if (conquer || (((s_cos * hs) > (s_sin * ws)) && ((e_cos * hs) > (e_sin * ws))))
{ /* This is the best bit... */
/* steep line + ellipse */
/* go up & left from pt[0] to pt[2] */
x2 = w;
y2 = 0; /* Starting point is exactly on ellipse */
g = x2 - 1;
g = g * g * hs + 4 * ws - whs;
while ((x2 * hs) > (y2 * ws)) /* Keep |tangent| > 1 */
{
if ((s_sin * x2) <= (y2 * s_cos))
break;
y2 += 2;
g += ws * 4 * (y2 + 1);
if (g > 0) /* Need to drop */
{
x2 -= 2;
g -= hs * 4 * x2;
}
}
lx2 = x2;
ly2 = y2;
lg = lx2 * (pt[0].y - pt[2].y) - ly2 * (pt[0].x - pt[2].x);
lg = (lx2 - 1) * (pt[0].y - pt[2].y) - (ly2 + 2) * (pt[0].x - pt[2].x) - lg;
while (y2 < (2 * pt[2].y))
{
y2 += 2;
g += ws * 4 * (y2 + 1);
if (g > 0) /* Need to drop */
{
x2 -= 2;
g -= hs * 4 * x2;
}
ly2 += 2;
lg -= 2 * (pt[0].x - pt[2].x);
if (lg < 0) /* Need to drop */
{
lx2 -= 2;
lg -= 2 * (pt[0].y - pt[2].y);
}
y = (int) (y2 / 2);
for (x = (int) (lx2 / 2); x <= (int) (x2 / 2); x++)
{
gdImageSetPixel (im, ((flip_x) ? (cx - x) : (cx + x)),
((flip_y) ? (cy - y) : (cy + y)), color);
}
}
}
if (conquer)
{
pt[0] = pt[4];
pt[2] = pt[5];
}
if (conquer || (((s_cos * hs) < (s_sin * ws)) && ((e_cos * hs) < (e_sin * ws))))
{ /* This is the best bit... */
/* gradual line + ellipse */
/* go down & right from pt[2] to pt[0] */
x2 = 0;
y2 = h; /* Starting point is exactly on ellipse */
g = y2 - 1;
g = g * g * ws + 4 * hs - whs;
while ((x2 * hs) < (y2 * ws))
{
x2 += 2;
g += hs * 4 * (x2 + 1);
if (g > 0) /* Need to drop */
{
y2 -= 2;
g -= ws * 4 * y2;
}
if ((e_sin * x2) >= (y2 * e_cos))
break;
}
lx2 = x2;
ly2 = y2;
lg = lx2 * (pt[0].y - pt[2].y) - ly2 * (pt[0].x - pt[2].x);
lg = (lx2 + 2) * (pt[0].y - pt[2].y) - (ly2 - 1) * (pt[0].x - pt[2].x) - lg;
while (x2 < (2 * pt[0].x))
{
x2 += 2;
g += hs * 4 * (x2 + 1);
if (g > 0) /* Need to drop */
{
y2 -= 2;
g -= ws * 4 * y2;
}
lx2 += 2;
lg += 2 * (pt[0].y - pt[2].y);
if (lg < 0) /* Need to drop */
{
ly2 -= 2;
lg += 2 * (pt[0].x - pt[2].x);
}
x = (int) (x2 / 2);
for (y = (int) (ly2 / 2); y <= (int) (y2 / 2); y++)
{
gdImageSetPixel (im, ((flip_x) ? (cx - x) : (cx + x)),
((flip_y) ? (cy - y) : (cy + y)), color);
}
}
}
}
static gdPoint
gdArcClosest (int width, int height, int angle)
{
gdPoint pt;
int flip_x = 0;
int flip_y = 0;
long a_sin = 0;
long a_cos = 0;
long w; /* a * 2 */
long h; /* b * 2 */
long x2; /* x * 2 */
long y2; /* y * 2 */
long ws; /* (a * 2)^2 */
long hs; /* (b * 2)^2 */
long whs; /* (a * 2)^2 * (b * 2)^2 */
long g; /* decision variable */
w = (long) ((width & 1) ? (width + 1) : (width));
h = (long) ((height & 1) ? (height + 1) : (height));
while (angle < 0)
angle += 360;
while (angle >= 360)
angle -= 360;
if (angle == 0)
{
pt.x = w / 2;
pt.y = 0;
return (pt);
}
if (angle == 90)
{
pt.x = 0;
pt.y = h / 2;
return (pt);
}
if (angle == 180)
{
pt.x = -w / 2;
pt.y = 0;
return (pt);
}
if (angle == 270)
{
pt.x = 0;
pt.y = -h / 2;
return (pt);
}
pt.x = 0;
pt.y = 0;
if ((angle > 90) && (angle < 180))
{
angle = 180 - angle;
flip_x = 1;
}
if ((angle > 180) && (angle < 270))
{
angle = angle - 180;
flip_x = 1;
flip_y = 1;
}
if ((angle > 270) && (angle < 360))
{
angle = 360 - angle;
flip_y = 1;
}
a_sin = (long) ((double) 32768 * sin ((double) angle * M_PI / (double) 180));
a_cos = (long) ((double) 32768 * cos ((double) angle * M_PI / (double) 180));
ws = w * w;
hs = h * h;
whs = 1;
while ((ws > 32768) || (hs > 32768))
{
ws = (ws + 1) / 2; /* Unfortunate limitations on integers makes */
hs = (hs + 1) / 2; /* drawing large ellipses problematic... */
whs *= 2;
}
while ((ws * hs) > (0x04000000L / whs))
{
ws = (ws + 1) / 2;
hs = (hs + 1) / 2;
whs *= 2;
}
whs *= ws * hs;
if ((a_cos * hs) > (a_sin * ws))
{
x2 = w;
y2 = 0; /* Starting point is exactly on ellipse */
g = x2 - 1;
g = g * g * hs + 4 * ws - whs;
while ((x2 * hs) > (y2 * ws)) /* Keep |tangent| > 1 */
{
y2 += 2;
g += ws * 4 * (y2 + 1);
if (g > 0) /* Need to drop */
{
x2 -= 2;
g -= hs * 4 * x2;
}
if ((a_sin * x2) <= (y2 * a_cos))
{
pt.x = (int) (x2 / 2);
pt.y = (int) (y2 / 2);
break;
}
}
}
else
{
x2 = 0;
y2 = h; /* Starting point is exactly on ellipse */
g = y2 - 1;
g = g * g * ws + 4 * hs - whs;
while ((x2 * hs) < (y2 * ws))
{
x2 += 2;
g += hs * 4 * (x2 + 1);
if (g > 0) /* Need to drop */
{
y2 -= 2;
g -= ws * 4 * y2;
}
if ((a_sin * x2) >= (y2 * a_cos))
{
pt.x = (int) (x2 / 2);
pt.y = (int) (y2 / 2);
break;
}
}
}
if (flip_x)
pt.x = -pt.x;
if (flip_y)
pt.y = -pt.y;
return (pt);
}
#include "gd.h"
#include <string.h>
#include <math.h>
#define WIDTH 500
#define HEIGHT 300
int
main (int argc, char *argv[])
{
gdImagePtr im = gdImageCreate (WIDTH, HEIGHT);
int white = gdImageColorResolve (im, 0xFF, 0xFF, 0xFF), black = gdImageColorResolve (im, 0, 0, 0),
red = gdImageColorResolve (im, 0xFF, 0xA0, 0xA0);
FILE *out;
/* filled arc - circle */
gdImageFilledArc (im, WIDTH / 5, HEIGHT / 4, 200, 200, 45, 90, red, gdPie);
gdImageArc (im, WIDTH / 5, HEIGHT / 4, 200, 200, 45, 90, black);
/* filled arc - ellipse */
gdImageFilledArc (im, WIDTH / 2, HEIGHT / 4, 200, 150, 45, 90, red, gdPie);
gdImageArc (im, WIDTH / 2, HEIGHT / 4, 200, 150, 45, 90, black);
/* reference lines */
gdImageLine (im, 0, HEIGHT / 4, WIDTH, HEIGHT / 4, black);
gdImageLine (im, WIDTH / 5, 0, WIDTH / 5, HEIGHT, black);
gdImageLine (im, WIDTH / 2, 0, WIDTH / 2, HEIGHT, black);
gdImageLine (im, WIDTH / 2, HEIGHT / 4, WIDTH / 2 + 300, HEIGHT / 4 + 300, black);
gdImageLine (im, WIDTH / 5, HEIGHT / 4, WIDTH / 5 + 300, HEIGHT / 4 + 300, black);
/* TBB: Write img to test/arctest.png */
out = fopen ("test/arctest.png", "wb");
if (!out)
{
fprintf (stderr, "Can't create test/arctest.png\n");
exit (1);
}
gdImagePng (im, out);
fclose (out);
fprintf (stderr, "Test image written to test/arctest.png\n");
/* Destroy it */
gdImageDestroy (im);
return 0;
}
#endif

272
ext/gd/libgd/gd_gd.c Normal file
View File

@ -0,0 +1,272 @@
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "gd.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 gd2xFlag)
{
int i;
if (gd2xFlag)
{
if (!gdGetByte (&im->trueColor, in))
{
goto fail1;
}
/* This should have been a word all along */
if (!im->trueColor)
{
if (!gdGetWord (&im->colorsTotal, in))
{
goto fail1;
}
}
/* Int to accommodate truecolor single-color transparency */
if (!gdGetInt (&im->transparent, in))
{
goto fail1;
}
}
else
{
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;
}
if (gd2xFlag)
{
if (!gdGetByte (&im->alpha[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)
{
gdImagePtr im;
int gd2xFlag = 0;
if (!gdGetWord (sx, in))
{
goto fail1;
}
if (*sx == 65535)
{
/* This is a gd 2.0 .gd file */
gd2xFlag = 1;
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, gd2xFlag))
{
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;
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 i;
int trans;
gdPutC (im->trueColor, out);
if (!im->trueColor)
{
gdPutWord (im->colorsTotal, out);
}
gdPutInt (im->transparent, out);
if (!im->trueColor)
{
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);
gdPutC ((unsigned char) im->alpha[i], out);
}
}
}
static
void
_gdPutHeader (gdImagePtr im, gdIOCtx * out)
{
/* 65535 indicates this is a gd 2.x .gd file. */
gdPutWord (65535, 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 */
if (im->trueColor)
{
gdPutInt (im->tpixels[y][x], out);
}
else
{
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;
}

923
ext/gd/libgd/gd_gd2.c Normal file
View File

@ -0,0 +1,923 @@
/*
* 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 "gdhelpers.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;
extern int _gdGetColors (gdIOCtx * in, gdImagePtr im, int gd2xFlag);
extern void _gdPutColors (gdImagePtr im, gdIOCtx * out);
/* */
/* 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;
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) && (*vers != 2))
{
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 (pixels, not bytes!) */
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 = gdCalloc (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, (*vers) == 2))
{
GD2_DBG (printf ("Could not read color palette\n"));
goto fail2;
}
GD2_DBG (printf ("Image palette completed: %d colours\n", im->colorsTotal));
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 ((unsigned char *) chunkBuf, chunkLen,
(unsigned char *) 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 gdFree it with impunity. */
unsigned char *chunkBuf = NULL; /* So we can gdFree it with impunity. */
int chunkNum = 0;
int chunkMax;
uLongf chunkLen;
int chunkPos;
int compMax;
int bytesPerPixel;
char *compBuf = NULL; /* So we can gdFree it with impunity. */
gdImagePtr im;
/* Get the header */
im = _gd2CreateFromFile (in, &sx, &sy, &cs, &vers, &fmt, &ncx, &ncy, &chunkIdx);
if (im == NULL)
{
return 0;
};
bytesPerPixel = im->trueColor ? 4 : 1;
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 * bytesPerPixel * cs;
chunkBuf = gdCalloc (chunkMax, 1);
compBuf = gdCalloc (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++)
{
if (im->trueColor)
{
if (!gdGetInt (&im->tpixels[y][x], in))
{
/*printf("EOF while reading\n"); */
/*gdImageDestroy(im); */
/*return 0; */
im->tpixels[y][x] = 0;
}
}
else
{
int ch;
if (!gdGetByte (&ch, in))
{
/*printf("EOF while reading\n"); */
/*gdImageDestroy(im); */
/*return 0; */
ch = 0;
}
im->pixels[y][x] = ch;
}
}
}
else
{
for (x = xlo; x < xhi; x++)
{
if (im->trueColor)
{
/* 2.0.1: work around a gcc bug by being verbose.
TBB */
int a = chunkBuf[chunkPos++] << 24;
int r = chunkBuf[chunkPos++] << 16;
int g = chunkBuf[chunkPos++] << 8;
int b = chunkBuf[chunkPos++];
im->pixels[y][x] = a + r + g + b;
}
else
{
im->pixels[y][x] = chunkBuf[chunkPos++];
}
};
};
/*GD2_DBG(printf("\n")); */
};
chunkNum++;
};
};
GD2_DBG (printf ("Freeing memory\n"));
gdFree (chunkBuf);
gdFree (compBuf);
gdFree (chunkIdx);
GD2_DBG (printf ("Done\n"));
return im;
fail2:
gdImageDestroy (im);
gdFree (chunkBuf);
gdFree (compBuf);
gdFree (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 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, vers == 2))
{
goto fail2;
}
GD2_DBG (printf ("Image palette completed: %d colours\n", im->colorsTotal));
/* 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++;
if (im->trueColor)
{
chunkMax = cs * cs * 4;
}
else
{
chunkMax = cs * cs;
}
chunkBuf = gdCalloc (chunkMax, 1);
compBuf = gdCalloc (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"));
if (im->trueColor)
{
dpos = (cy * (cs * fsx) + cx * cs * (yhi - ylo) * 4) + dstart;
}
else
{
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)
{
if (im->trueColor)
{
if (!gdGetInt (&ch, in))
{
ch = 0;
/*printf("EOF while reading file\n"); */
/*goto fail2; */
}
}
else
{
ch = gdGetC (in);
if (ch == EOF)
{
ch = 0;
/*printf("EOF while reading file\n"); */
/*goto fail2; */
}
}
}
else
{
if (im->trueColor)
{
ch = chunkBuf[chunkPos++] << 24 +
chunkBuf[chunkPos++] << 16 +
chunkBuf[chunkPos++] << 8 +
chunkBuf[chunkPos++];
}
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;
}
};
};
};
};
gdFree (chunkBuf);
gdFree (compBuf);
gdFree (chunkIdx);
return im;
fail2:
gdImageDestroy (im);
fail1:
gdFree (chunkBuf);
gdFree (compBuf);
gdFree (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 chunkLen;
int chunkNum = 0;
char *chunkData = NULL; /* So we can gdFree it with impunity. */
char *compData = NULL; /* So we can gdFree it with impunity. */
uLongf compLen;
int idxPos;
int idxSize;
t_chunk_info *chunkIdx = NULL;
int posSave;
int bytesPerPixel = im->trueColor ? 4 : 1;
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 * bytesPerPixel * cs * 1.02 + 12;
/* */
/* Allocate the buffers. */
/* */
chunkData = gdCalloc (cs * bytesPerPixel * cs, 1);
compData = gdCalloc (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 = gdCalloc (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++)
{
int p = im->pixels[y][x];
/*GD2_DBG(printf("%d...",x)); */
if (im->trueColor)
{
chunkData[chunkLen++] = gdTrueColorGetAlpha (p);
chunkData[chunkLen++] = gdTrueColorGetRed (p);
chunkData[chunkLen++] = gdTrueColorGetGreen (p);
chunkData[chunkLen++] = gdTrueColorGetBlue (p);
}
else
{
chunkData[chunkLen++] = p;
}
};
}
else
{
for (x = xlo; x < xhi; x++)
{
/*GD2_DBG(printf("%d, ",x)); */
if (im->trueColor)
{
gdPutInt (im->tpixels[y][x], out);
}
else
{
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 ((unsigned char *)
&compData[0], &compLen,
(unsigned char *) &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"));
gdFree (chunkData);
gdFree (compData);
gdFree (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;
}

175
ext/gd/libgd/gd_io.c Normal file
View File

@ -0,0 +1,175 @@
/*
* 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 "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, const 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"));
}

39
ext/gd/libgd/gd_io.h Normal file
View File

@ -0,0 +1,39 @@
#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);
#endif

419
ext/gd/libgd/gd_io_dp.c Normal file
View File

@ -0,0 +1,419 @@
/*
* 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 "gd.h"
#include "gdhelpers.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 gdReallocDynamic (dynamicPtr * dp, int required);
static int trimDynamic (dynamicPtr * dp);
static void gdFreeDynamicCtx (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 *) gdMalloc (sizeof (dpIOCtx));
if (ctx == NULL)
{
return NULL;
}
dp = newDynamic (initialSize, data);
if (!dp)
{
gdFree (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 = gdFreeDynamicCtx;
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)
{
gdFree (dp->data);
}
}
dp->data = NULL;
dp->realSize = 0;
dp->logicalSize = 0;
return data;
}
static
void
gdFreeDynamicCtx (struct gdIOCtx *ctx)
{
dynamicPtr *dp;
dpIOCtx *dctx;
dctx = (dpIOCtx *) ctx;
dp = dctx->dp;
gdFree (ctx);
/* clean up the data block and return it */
if (dp->data != NULL)
{
gdFree (dp->data);
dp->data = NULL;
}
dp->realSize = 0;
dp->logicalSize = 0;
gdFree (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;
dynamicPtr *dp;
dpIOCtx *dctx;
dctx = (dpIOCtx *) ctx;
dp = dctx->dp;
if (!dp->dataGood)
return FALSE;
bytesNeeded = pos;
if (bytesNeeded > dp->realSize)
{
if (!gdReallocDynamic (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 *) gdMalloc (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 *) ((char *) 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 = gdMalloc (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 (!gdReallocDynamic (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
gdReallocDynamic (dynamicPtr * dp, int required)
{
void *newPtr;
/* First try gdRealloc(). If that doesn't work, make a new
memory block and copy. */
if ((newPtr = gdRealloc (dp->data, required)))
{
dp->realSize = required;
dp->data = newPtr;
return TRUE;
}
/* create a new pointer */
newPtr = gdMalloc (required);
if (!newPtr)
{
dp->dataGood = FALSE;
return FALSE;
}
/* copy the old data into it */
memcpy (newPtr, dp->data, dp->logicalSize);
gdFree (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 gdReallocDynamic (dp, dp->logicalSize);
}

145
ext/gd/libgd/gd_io_file.c Normal file
View File

@ -0,0 +1,145 @@
/*
* 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.
*
*/
/* For platforms with incomplete ANSI defines. Fortunately,
SEEK_SET is defined to be zero by the standard. */
#ifndef SEEK_SET
#define SEEK_SET 0
#endif /* SEEK_SET */
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "gd.h"
#include "gdhelpers.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 gdFreeFileCtx (gdIOCtx * ctx);
/* return data as a dynamic pointer */
gdIOCtx *
gdNewFileCtx (FILE * f)
{
fileIOCtx *ctx;
ctx = (fileIOCtx *) gdMalloc (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 = gdFreeFileCtx;
return (gdIOCtx *) ctx;
}
static
void
gdFreeFileCtx (gdIOCtx * ctx)
{
gdFree (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);
}

165
ext/gd/libgd/gd_io_ss.c Normal file
View File

@ -0,0 +1,165 @@
/*
* 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 "gd.h"
#include "gdhelpers.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 gdFreeSsCtx (gdIOCtx * ctx);
/* return data as a dynamic pointer */
gdIOCtx *
gdNewSSCtx (gdSourcePtr src, gdSinkPtr snk)
{
ssIOCtxPtr ctx;
ctx = (ssIOCtxPtr) gdMalloc (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 = gdFreeSsCtx;
return (gdIOCtx *) ctx;
}
static
void
gdFreeSsCtx (gdIOCtx * ctx)
{
gdFree (ctx);
}
static int
sourceGetbuf (gdIOCtx * ctx, void *buf, int size)
{
ssIOCtx *lctx;
int res;
lctx = (ssIOCtx *) ctx;
res = ((lctx->src->source) (lctx->src->context, 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->context, 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);
}

852
ext/gd/libgd/gd_jpeg.c Normal file
View File

@ -0,0 +1,852 @@
/*
* gd_jpeg.c: Read and write JPEG (JFIF) format image files using the
* gd graphics library (http://www.boutell.com/gd/).
*
* This software is based in part on the work of the Independent JPEG
* Group. For more information on the IJG JPEG software (and JPEG
* documentation, etc.), see ftp://ftp.uu.net/graphics/jpeg/.
*
* NOTE: IJG 12-bit JSAMPLE (BITS_IN_JSAMPLE == 12) mode is not
* supported at all on read in gd 2.0, and is not supported on write
* except for palette images, which is sort of pointless (TBB). Even that
* has never been tested according to DB.
*
* Copyright 2000 Doug Becker, mailto:thebeckers@home.com
*
* Modification 4/18/00 TBB: JPEG_DEBUG rather than just DEBUG,
* so VC++ builds don't spew to standard output, causing
* major CGI brain damage
*/
/* TBB: move this up so include files are not brought in */
#ifdef HAVE_LIBJPEG
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <limits.h>
#include <string.h>
/* 1.8.1: remove dependency on jinclude.h */
#include "jpeglib.h"
#include "jerror.h"
#include "gd.h"
#include "gdhelpers.h"
static const char *const GD_JPEG_VERSION = "1.0";
typedef struct _jmpbuf_wrapper
{
jmp_buf jmpbuf;
}
jmpbuf_wrapper;
/* Called by the IJG JPEG library upon encountering a fatal error */
static void
fatal_jpeg_error (j_common_ptr cinfo)
{
jmpbuf_wrapper *jmpbufw;
fprintf (stderr, "gd-jpeg: JPEG library reports unrecoverable error: ");
(*cinfo->err->output_message) (cinfo);
fflush (stderr);
jmpbufw = (jmpbuf_wrapper *) cinfo->client_data;
jpeg_destroy (cinfo);
if (jmpbufw != 0)
{
longjmp (jmpbufw->jmpbuf, 1);
fprintf (stderr, "gd-jpeg: EXTREMELY fatal error: longjmp"
" returned control; terminating\n");
}
else
{
fprintf (stderr, "gd-jpeg: EXTREMELY fatal error: jmpbuf"
" unrecoverable; terminating\n");
}
fflush (stderr);
exit (99);
}
/*
* Write IM to OUTFILE as a JFIF-formatted JPEG image, using quality
* QUALITY. If QUALITY is in the range 0-100, increasing values
* represent higher quality but also larger image size. If QUALITY is
* negative, the IJG JPEG library's default quality is used (which
* should be near optimal for many applications). See the IJG JPEG
* library documentation for more details. */
void
gdImageJpeg (gdImagePtr im, FILE * outFile, int quality)
{
gdIOCtx *out = gdNewFileCtx (outFile);
gdImageJpegCtx (im, out, quality);
out->free (out);
}
void *
gdImageJpegPtr (gdImagePtr im, int *size, int quality)
{
void *rv;
gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
gdImageJpegCtx (im, out, quality);
rv = gdDPExtractData (out, size);
out->free (out);
return rv;
}
void jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile);
void
gdImageJpegCtx (gdImagePtr im, gdIOCtx * outfile, int quality)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
int i, j, jidx;
/* volatile so we can gdFree it on return from longjmp */
volatile JSAMPROW row = 0;
JSAMPROW rowptr[1];
jmpbuf_wrapper jmpbufw;
JDIMENSION nlines;
char comment[255];
#ifdef JPEG_DEBUG
printf ("gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION);
printf ("gd-jpeg: JPEG library version %d, %d-bit sample values\n",
JPEG_LIB_VERSION, BITS_IN_JSAMPLE);
if (!im->trueColor)
{
for (i = 0; i < im->colorsTotal; i++)
{
if (!im->open[i])
printf ("gd-jpeg: gd colormap index %d: (%d, %d, %d)\n", i,
im->red[i], im->green[i], im->blue[i]);
}
}
#endif /* JPEG_DEBUG */
memset (&cinfo, 0, sizeof (cinfo));
memset (&jerr, 0, sizeof (jerr));
cinfo.err = jpeg_std_error (&jerr);
cinfo.client_data = &jmpbufw;
if (setjmp (jmpbufw.jmpbuf) != 0)
{
/* we're here courtesy of longjmp */
if (row)
gdFree (row);
return;
}
cinfo.err->error_exit = fatal_jpeg_error;
jpeg_create_compress (&cinfo);
cinfo.image_width = im->sx;
cinfo.image_height = im->sy;
cinfo.input_components = 3; /* # of color components per pixel */
cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
jpeg_set_defaults (&cinfo);
if (quality >= 0)
jpeg_set_quality (&cinfo, quality, TRUE);
/* If user requests interlace, translate that to progressive JPEG */
if (gdImageGetInterlaced (im))
{
#ifdef JPEG_DEBUG
printf ("gd-jpeg: interlace set, outputting progressive"
" JPEG image\n");
#endif
jpeg_simple_progression (&cinfo);
}
jpeg_gdIOCtx_dest (&cinfo, outfile);
row = (JSAMPROW) gdCalloc (1, cinfo.image_width * cinfo.input_components
* sizeof (JSAMPLE));
if (row == 0)
{
fprintf (stderr, "gd-jpeg: error: unable to allocate JPEG row "
"structure: gdCalloc returns NULL\n");
jpeg_destroy_compress (&cinfo);
return;
}
rowptr[0] = row;
jpeg_start_compress (&cinfo, TRUE);
sprintf (comment, "CREATOR: gd-jpeg v%s (using IJG JPEG v%d),",
GD_JPEG_VERSION, JPEG_LIB_VERSION);
if (quality >= 0)
sprintf (comment + strlen (comment), " quality = %d\n",
quality);
else
strcat (comment + strlen (comment), " default quality\n");
jpeg_write_marker (&cinfo, JPEG_COM, (unsigned char *) comment,
(unsigned int) strlen (comment));
if (im->trueColor)
{
#if BITS_IN_JSAMPLE == 12
fprintf (stderr, "gd-jpeg: error: jpeg library was compiled for 12-bit\n"
"precision. This is mostly useless, because JPEGs on the web are\n"
"8-bit and such versions of the jpeg library won't read or write\n"
"them. GD doesn't support these unusual images. Edit your\n"
"jmorecfg.h file to specify the correct precision and completely\n"
"'make clean' and 'make install' libjpeg again. Sorry.\n");
goto error;
#endif /* BITS_IN_JSAMPLE == 12 */
for (i = 0; i < im->sy; i++)
{
for (jidx = 0, j = 0; j < im->sx; j++)
{
int val = im->tpixels[i][j];
row[jidx++] = gdTrueColorGetRed (val);
row[jidx++] = gdTrueColorGetGreen (val);
row[jidx++] = gdTrueColorGetBlue (val);
}
nlines = jpeg_write_scanlines (&cinfo, rowptr, 1);
if (nlines != 1)
fprintf (stderr, "gd_jpeg: warning: jpeg_write_scanlines"
" returns %u -- expected 1\n", nlines);
}
}
else
{
for (i = 0; i < im->sy; i++)
{
for (jidx = 0, j = 0; j < im->sx; j++)
{
int idx = im->pixels[i][j];
/*
* NB: Although gd RGB values are ints, their max value is
* 255 (see the documentation for gdImageColorAllocate())
* -- perfect for 8-bit JPEG encoding (which is the norm)
*/
#if BITS_IN_JSAMPLE == 8
row[jidx++] = im->red[idx];
row[jidx++] = im->green[idx];
row[jidx++] = im->blue[idx];
#elif BITS_IN_JSAMPLE == 12
row[jidx++] = im->red[idx] << 4;
row[jidx++] = im->green[idx] << 4;
row[jidx++] = im->blue[idx] << 4;
#else
#error IJG JPEG library BITS_IN_JSAMPLE value must be 8 or 12
#endif
}
nlines = jpeg_write_scanlines (&cinfo, rowptr, 1);
if (nlines != 1)
fprintf (stderr, "gd_jpeg: warning: jpeg_write_scanlines"
" returns %u -- expected 1\n", nlines);
}
}
jpeg_finish_compress (&cinfo);
error:
jpeg_destroy_compress (&cinfo);
gdFree (row);
}
gdImagePtr
gdImageCreateFromJpeg (FILE * inFile)
{
gdImagePtr im;
gdIOCtx *in = gdNewFileCtx (inFile);
im = gdImageCreateFromJpegCtx (in);
in->free (in);
return im;
}
void
jpeg_gdIOCtx_src (j_decompress_ptr cinfo,
gdIOCtx * infile);
/*
* Create a gd-format image from the JPEG-format INFILE. Returns the
* image, or NULL upon error.
*/
gdImagePtr
gdImageCreateFromJpegCtx (gdIOCtx * infile)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
jmpbuf_wrapper jmpbufw;
/* volatile so we can gdFree them after longjmp */
volatile JSAMPROW row = 0;
volatile gdImagePtr im = 0;
JSAMPROW rowptr[1];
int i, j, retval;
JDIMENSION nrows;
#ifdef JPEG_DEBUG
printf ("gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION);
printf ("gd-jpeg: JPEG library version %d, %d-bit sample values\n",
JPEG_LIB_VERSION, BITS_IN_JSAMPLE);
#endif
memset (&cinfo, 0, sizeof (cinfo));
memset (&jerr, 0, sizeof (jerr));
cinfo.err = jpeg_std_error (&jerr);
cinfo.client_data = &jmpbufw;
if (setjmp (jmpbufw.jmpbuf) != 0)
{
/* we're here courtesy of longjmp */
if (row)
gdFree (row);
if (im)
gdImageDestroy (im);
return 0;
}
cinfo.err->error_exit = fatal_jpeg_error;
jpeg_create_decompress (&cinfo);
jpeg_gdIOCtx_src (&cinfo, infile);
retval = jpeg_read_header (&cinfo, TRUE);
if (retval != JPEG_HEADER_OK)
fprintf (stderr, "gd-jpeg: warning: jpeg_read_header returns"
" %d, expected %d\n", retval, JPEG_HEADER_OK);
if (cinfo.image_height > INT_MAX)
fprintf (stderr, "gd-jpeg: warning: JPEG image height (%u) is"
" greater than INT_MAX (%d) (and thus greater than"
" gd can handle)", cinfo.image_height,
INT_MAX);
if (cinfo.image_width > INT_MAX)
fprintf (stderr, "gd-jpeg: warning: JPEG image width (%u) is"
" greater than INT_MAX (%d) (and thus greater than"
" gd can handle)\n", cinfo.image_width, INT_MAX);
im = gdImageCreateTrueColor ((int) cinfo.image_width,
(int) cinfo.image_height);
if (im == 0)
{
fprintf (stderr, "gd-jpeg error: cannot allocate gdImage"
" struct\n");
goto error;
}
/*
* Force the image into RGB colorspace, but don't
* reduce the number of colors anymore (GD 2.0)
*/
cinfo.out_color_space = JCS_RGB;
if (jpeg_start_decompress (&cinfo) != TRUE)
fprintf (stderr, "gd-jpeg: warning: jpeg_start_decompress"
" reports suspended data source\n");
#ifdef JPEG_DEBUG
printf ("gd-jpeg: JPEG image information:");
if (cinfo.saw_JFIF_marker)
printf (" JFIF version %d.%.2d",
(int) cinfo.JFIF_major_version,
(int) cinfo.JFIF_minor_version);
else if (cinfo.saw_Adobe_marker)
printf (" Adobe format");
else
printf (" UNKNOWN format");
printf (" %ux%u (raw) / %ux%u (scaled) %d-bit", cinfo.image_width,
cinfo.image_height, cinfo.output_width,
cinfo.output_height, cinfo.data_precision);
printf (" %s", (cinfo.progressive_mode ? "progressive" :
"baseline"));
printf (" image, %d quantized colors, ",
cinfo.actual_number_of_colors);
switch (cinfo.jpeg_color_space)
{
case JCS_GRAYSCALE:
printf ("grayscale");
break;
case JCS_RGB:
printf ("RGB");
break;
case JCS_YCbCr:
printf ("YCbCr (a.k.a. YUV)");
break;
case JCS_CMYK:
printf ("CMYK");
break;
case JCS_YCCK:
printf ("YCbCrK");
break;
default:
printf ("UNKNOWN (value: %d)", (int) cinfo.jpeg_color_space);
break;
}
printf (" colorspace\n");
fflush (stdout);
#endif /* JPEG_DEBUG */
/* REMOVED by TBB 2/12/01. This field of the structure is
documented as private, and sure enough it's gone in the
latest libjpeg, replaced by something else. Unfortunately
there is still no right way to find out if the file was
progressive or not; just declare your intent before you
write one by calling gdImageInterlace(im, 1) yourself.
After all, we're not really supposed to rework JPEGs and
write them out again anyway. Lossy compression, remember? */
#if 0
gdImageInterlace (im, cinfo.progressive_mode != 0);
#endif
if (cinfo.output_components != 3)
{
fprintf (stderr, "gd-jpeg: error: JPEG color quantization"
" request resulted in output_components == %d"
" (expected 3)\n", cinfo.output_components);
goto error;
}
#if BITS_IN_JSAMPLE == 12
fprintf (stderr, "gd-jpeg: error: jpeg library was compiled for 12-bit\n"
"precision. This is mostly useless, because JPEGs on the web are\n"
"8-bit and such versions of the jpeg library won't read or write\n"
"them. GD doesn't support these unusual images. Edit your\n"
"jmorecfg.h file to specify the correct precision and completely\n"
"'make clean' and 'make install' libjpeg again. Sorry.\n");
goto error;
#endif /* BITS_IN_JSAMPLE == 12 */
row = gdCalloc (cinfo.output_width * 3, sizeof (JSAMPLE));
if (row == 0)
{
fprintf (stderr, "gd-jpeg: error: unable to allocate row for"
" JPEG scanline: gdCalloc returns NULL\n");
goto error;
}
rowptr[0] = row;
for (i = 0; i < cinfo.output_height; i++)
{
nrows = jpeg_read_scanlines (&cinfo, rowptr, 1);
if (nrows != 1)
{
fprintf (stderr, "gd-jpeg: error: jpeg_read_scanlines"
" returns %u, expected 1\n", nrows);
goto error;
}
for (j = 0; j < cinfo.output_width; j++)
im->tpixels[i][j] = gdTrueColor (row[j * 3], row[j * 3 + 1],
row[j * 3 + 2]);
}
if (jpeg_finish_decompress (&cinfo) != TRUE)
fprintf (stderr, "gd-jpeg: warning: jpeg_finish_decompress"
" reports suspended data source\n");
jpeg_destroy_decompress (&cinfo);
gdFree (row);
return im;
error:
jpeg_destroy_decompress (&cinfo);
if (row)
gdFree (row);
if (im)
gdImageDestroy (im);
return 0;
}
/*
* gdIOCtx JPEG data sources and sinks, T. Boutell
* almost a simple global replace from T. Lane's stdio versions.
*
*/
/* Different versions of libjpeg use either 'jboolean' or 'boolean', and
some platforms define 'boolean', and so forth. Deal with this
madness by typedeffing 'safeboolean' to 'boolean' if HAVE_BOOLEAN
is already set, because this is the test that libjpeg uses.
Otherwise, typedef it to int, because that's what libjpeg does
if HAVE_BOOLEAN is not defined. -TBB */
#ifdef HAVE_BOOLEAN
typedef boolean safeboolean;
#else
typedef int safeboolean;
#endif /* HAVE_BOOLEAN */
/* Expanded data source object for gdIOCtx input */
typedef struct
{
struct jpeg_source_mgr pub; /* public fields */
gdIOCtx *infile; /* source stream */
unsigned char *buffer; /* start of buffer */
safeboolean start_of_file; /* have we gotten any data yet? */
}
my_source_mgr;
typedef my_source_mgr *my_src_ptr;
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
/*
* Initialize source --- called by jpeg_read_header
* before any data is actually read.
*/
void
init_source (j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
/* We reset the empty-input-file flag for each image,
* but we don't clear the input buffer.
* This is correct behavior for reading a series of images from one source.
*/
src->start_of_file = TRUE;
}
/*
* Fill the input buffer --- called whenever buffer is emptied.
*
* In typical applications, this should read fresh data into the buffer
* (ignoring the current state of next_input_byte & bytes_in_buffer),
* reset the pointer & count to the start of the buffer, and return TRUE
* indicating that the buffer has been reloaded. It is not necessary to
* fill the buffer entirely, only to obtain at least one more byte.
*
* There is no such thing as an EOF return. If the end of the file has been
* reached, the routine has a choice of ERREXIT() or inserting fake data into
* the buffer. In most cases, generating a warning message and inserting a
* fake EOI marker is the best course of action --- this will allow the
* decompressor to output however much of the image is there. However,
* the resulting error message is misleading if the real problem is an empty
* input file, so we handle that case specially.
*
* In applications that need to be able to suspend compression due to input
* not being available yet, a FALSE return indicates that no more data can be
* obtained right now, but more may be forthcoming later. In this situation,
* the decompressor will return to its caller (with an indication of the
* number of scanlines it has read, if any). The application should resume
* decompression after it has loaded more data into the input buffer. Note
* that there are substantial restrictions on the use of suspension --- see
* the documentation.
*
* When suspending, the decompressor will back up to a convenient restart point
* (typically the start of the current MCU). next_input_byte & bytes_in_buffer
* indicate where the restart point will be if the current call returns FALSE.
* Data beyond this point must be rescanned after resumption, so move it to
* the front of the buffer rather than discarding it.
*/
#define END_JPEG_SEQUENCE "\r\n[*]--:END JPEG:--[*]\r\n"
safeboolean
fill_input_buffer (j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
size_t nbytes = 0;
/* size_t got; */
/* char *s; */
memset (src->buffer, 0, INPUT_BUF_SIZE);
while (nbytes < INPUT_BUF_SIZE)
{
int got = gdGetBuf (src->buffer + nbytes,
INPUT_BUF_SIZE - nbytes,
src->infile);
if ((got == EOF) || (got == 0))
{
/* EOF or error. If we got any data, don't worry about it.
If we didn't, then this is unexpected. */
if (!nbytes)
{
nbytes = -1;
}
break;
}
nbytes += got;
}
if (nbytes <= 0)
{
if (src->start_of_file) /* Treat empty input file as fatal error */
ERREXIT (cinfo, JERR_INPUT_EMPTY);
WARNMS (cinfo, JWRN_JPEG_EOF);
/* Insert a fake EOI marker */
src->buffer[0] = (unsigned char) 0xFF;
src->buffer[1] = (unsigned char) JPEG_EOI;
nbytes = 2;
}
src->pub.next_input_byte = src->buffer;
src->pub.bytes_in_buffer = nbytes;
src->start_of_file = FALSE;
return TRUE;
}
/*
* Skip data --- used to skip over a potentially large amount of
* uninteresting data (such as an APPn marker).
*
* Writers of suspendable-input applications must note that skip_input_data
* is not granted the right to give a suspension return. If the skip extends
* beyond the data currently in the buffer, the buffer can be marked empty so
* that the next read will cause a fill_input_buffer call that can suspend.
* Arranging for additional bytes to be discarded before reloading the input
* buffer is the application writer's problem.
*/
void
skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
/* Just a dumb implementation for now. Not clear that being smart is worth
* any trouble anyway --- large skips are infrequent.
*/
if (num_bytes > 0)
{
while (num_bytes > (long) src->pub.bytes_in_buffer)
{
num_bytes -= (long) src->pub.bytes_in_buffer;
(void) fill_input_buffer (cinfo);
/* note we assume that fill_input_buffer will never return FALSE,
* so suspension need not be handled.
*/
}
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
}
}
/*
* An additional method that can be provided by data source modules is the
* resync_to_restart method for error recovery in the presence of RST markers.
* For the moment, this source module just uses the default resync method
* provided by the JPEG library. That method assumes that no backtracking
* is possible.
*/
/*
* Terminate source --- called by jpeg_finish_decompress
* after all data has been read. Often a no-op.
*
* NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
* application must deal with any cleanup that should happen even
* for error exit.
*/
void
term_source (j_decompress_ptr cinfo)
{
#if 0
/* never used */
my_src_ptr src = (my_src_ptr) cinfo->src;
#endif
}
/*
* Prepare for input from a gdIOCtx stream.
* The caller must have already opened the stream, and is responsible
* for closing it after finishing decompression.
*/
void
jpeg_gdIOCtx_src (j_decompress_ptr cinfo,
gdIOCtx * infile)
{
my_src_ptr src;
/* The source object and input buffer are made permanent so that a series
* of JPEG images can be read from the same file by calling jpeg_gdIOCtx_src
* only before the first one. (If we discarded the buffer at the end of
* one image, we'd likely lose the start of the next one.)
* This makes it unsafe to use this manager and a different source
* manager serially with the same JPEG object. Caveat programmer.
*/
if (cinfo->src == NULL)
{ /* first time for this JPEG object? */
cinfo->src = (struct jpeg_source_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
sizeof (my_source_mgr));
src = (my_src_ptr) cinfo->src;
src->buffer = (unsigned char *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
INPUT_BUF_SIZE * sizeof (unsigned char));
}
src = (my_src_ptr) cinfo->src;
src->pub.init_source = init_source;
src->pub.fill_input_buffer = fill_input_buffer;
src->pub.skip_input_data = skip_input_data;
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
src->pub.term_source = term_source;
src->infile = infile;
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
src->pub.next_input_byte = NULL; /* until buffer loaded */
}
/* Expanded data destination object for stdio output */
typedef struct
{
struct jpeg_destination_mgr pub; /* public fields */
gdIOCtx *outfile; /* target stream */
unsigned char *buffer; /* start of buffer */
}
my_destination_mgr;
typedef my_destination_mgr *my_dest_ptr;
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
/*
* Initialize destination --- called by jpeg_start_compress
* before any data is actually written.
*/
void
init_destination (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
/* Allocate the output buffer --- it will be released when done with image */
dest->buffer = (unsigned char *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
OUTPUT_BUF_SIZE * sizeof (unsigned char));
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
}
/*
* Empty the output buffer --- called whenever buffer fills up.
*
* In typical applications, this should write the entire output buffer
* (ignoring the current state of next_output_byte & free_in_buffer),
* reset the pointer & count to the start of the buffer, and return TRUE
* indicating that the buffer has been dumped.
*
* In applications that need to be able to suspend compression due to output
* overrun, a FALSE return indicates that the buffer cannot be emptied now.
* In this situation, the compressor will return to its caller (possibly with
* an indication that it has not accepted all the supplied scanlines). The
* application should resume compression after it has made more room in the
* output buffer. Note that there are substantial restrictions on the use of
* suspension --- see the documentation.
*
* When suspending, the compressor will back up to a convenient restart point
* (typically the start of the current MCU). next_output_byte & free_in_buffer
* indicate where the restart point will be if the current call returns FALSE.
* Data beyond this point will be regenerated after resumption, so do not
* write it out when emptying the buffer externally.
*/
safeboolean
empty_output_buffer (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
if (gdPutBuf (dest->buffer, OUTPUT_BUF_SIZE, dest->outfile) !=
(size_t) OUTPUT_BUF_SIZE)
ERREXIT (cinfo, JERR_FILE_WRITE);
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
return TRUE;
}
/*
* Terminate destination --- called by jpeg_finish_compress
* after all data has been written. Usually needs to flush buffer.
*
* NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
* application must deal with any cleanup that should happen even
* for error exit.
*/
void
term_destination (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
/* Write any data remaining in the buffer */
if (datacount > 0)
{
if (gdPutBuf (dest->buffer, datacount, dest->outfile) != datacount)
ERREXIT (cinfo, JERR_FILE_WRITE);
}
}
/*
* Prepare for output to a stdio stream.
* The caller must have already opened the stream, and is responsible
* for closing it after finishing compression.
*/
void
jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile)
{
my_dest_ptr dest;
/* The destination object is made permanent so that multiple JPEG images
* can be written to the same file without re-executing jpeg_stdio_dest.
* This makes it dangerous to use this manager and a different destination
* manager serially with the same JPEG object, because their private object
* sizes may be different. Caveat programmer.
*/
if (cinfo->dest == NULL)
{ /* first time for this JPEG object? */
cinfo->dest = (struct jpeg_destination_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
sizeof (my_destination_mgr));
}
dest = (my_dest_ptr) cinfo->dest;
dest->pub.init_destination = init_destination;
dest->pub.empty_output_buffer = empty_output_buffer;
dest->pub.term_destination = term_destination;
dest->outfile = outfile;
}
#endif /* HAVE_JPEG */

753
ext/gd/libgd/gd_png.c Normal file
View File

@ -0,0 +1,753 @@
#ifdef HAVE_LIBPNG
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "gd.h"
#include "gdhelpers.h"
#include "png.h" /* includes zlib.h and setjmp.h */
#define TRUE 1
#define FALSE 0
/*---------------------------------------------------------------------------
gd_png.c Copyright 1999 Greg Roelofs and Thomas Boutell
The routines in this file, gdImagePng*() and gdImageCreateFromPng*(),
are drop-in replacements for gdImageGif*() and gdImageCreateFromGif*(),
except that these functions are noisier in the case of errors (comment
out all fprintf() statements to disable that).
GD 2.0 supports RGBA truecolor and will read and write truecolor PNGs.
GD 2.0 supports 8 bits of color resolution per channel and
7 bits of alpha channel resolution. Images with more than 8 bits
per channel are reduced to 8 bits. Images with an alpha channel are
only able to resolve down to '1/128th opaque' instead of '1/256th',
and this conversion is also automatic. I very much doubt you can see it.
Both tRNS and true alpha are supported.
Gamma is ignored, and there is no support for text annotations.
Last updated: 9 February 2001
---------------------------------------------------------------------------*/
#ifndef PNG_SETJMP_NOT_SUPPORTED
typedef struct _jmpbuf_wrapper
{
jmp_buf jmpbuf;
}
jmpbuf_wrapper;
static jmpbuf_wrapper gdPngJmpbufStruct;
static void
gdPngErrorHandler (png_structp png_ptr, png_const_charp msg)
{
jmpbuf_wrapper *jmpbuf_ptr;
/* This function, aside from the extra step of retrieving the "error
* pointer" (below) and the fact that it exists within the application
* rather than within libpng, is essentially identical to libpng's
* default error handler. The second point is critical: since both
* setjmp() and longjmp() are called from the same code, they are
* guaranteed to have compatible notions of how big a jmp_buf is,
* regardless of whether _BSD_SOURCE or anything else has (or has not)
* been defined. */
fprintf (stderr, "gd-png: fatal libpng error: %s\n", msg);
fflush (stderr);
jmpbuf_ptr = png_get_error_ptr (png_ptr);
if (jmpbuf_ptr == NULL)
{ /* we are completely hosed now */
fprintf (stderr,
"gd-png: EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n");
fflush (stderr);
exit (99);
}
longjmp (jmpbuf_ptr->jmpbuf, 1);
}
#endif
static void
gdPngReadData (png_structp png_ptr,
png_bytep data, png_size_t length)
{
gdGetBuf (data, length, (gdIOCtx *)
png_get_io_ptr (png_ptr));
}
static void
gdPngWriteData (png_structp png_ptr,
png_bytep data, png_size_t length)
{
gdPutBuf (data, length, (gdIOCtx *)
png_get_io_ptr (png_ptr));
}
static void
gdPngFlushData (png_structp png_ptr)
{
}
gdImagePtr
gdImageCreateFromPng (FILE * inFile)
{
gdImagePtr im;
gdIOCtx *in = gdNewFileCtx (inFile);
im = gdImageCreateFromPngCtx (in);
in->free (in);
return im;
}
/* This routine is based in part on the Chapter 13 demo code in "PNG: The
* Definitive Guide" (http://www.cdrom.com/pub/png/pngbook.html).
*/
gdImagePtr
gdImageCreateFromPngCtx (gdIOCtx * infile)
{
png_byte sig[8];
png_structp png_ptr;
png_infop info_ptr;
png_uint_32 width, height, rowbytes;
int bit_depth, color_type, interlace_type;
int num_palette, num_trans;
png_colorp palette;
png_color_16p trans_gray_rgb;
png_color_16p trans_color_rgb;
png_bytep trans;
png_bytep image_data = NULL;
png_bytepp row_pointers = NULL;
gdImagePtr im = NULL;
int i, j, *open;
volatile int transparent = -1;
volatile int palette_allocated = FALSE;
/* Make sure the signature can't match by dumb luck -- TBB */
/* GRR: isn't sizeof(infile) equal to the size of the pointer? */
memset (infile, 0, sizeof (infile));
/* first do a quick check that the file really is a PNG image; could
* have used slightly more general png_sig_cmp() function instead */
gdGetBuf (sig, 8, infile);
if (!png_check_sig (sig, 8))
return NULL; /* bad signature */
#ifndef PNG_SETJMP_NOT_SUPPORTED
png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, &gdPngJmpbufStruct,
gdPngErrorHandler, NULL);
#else
png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
#endif
if (png_ptr == NULL)
{
fprintf (stderr, "gd-png error: cannot allocate libpng main struct\n");
return NULL;
}
info_ptr = png_create_info_struct (png_ptr);
if (info_ptr == NULL)
{
fprintf (stderr, "gd-png error: cannot allocate libpng info struct\n");
png_destroy_read_struct (&png_ptr, NULL, NULL);
return NULL;
}
/* we could create a second info struct here (end_info), but it's only
* useful if we want to keep pre- and post-IDAT chunk info separated
* (mainly for PNG-aware image editors and converters) */
/* setjmp() must be called in every non-callback function that calls a
* PNG-reading libpng function */
#ifndef PNG_SETJMP_NOT_SUPPORTED
if (setjmp (gdPngJmpbufStruct.jmpbuf))
{
fprintf (stderr, "gd-png error: setjmp returns error condition\n");
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return NULL;
}
#endif
png_set_sig_bytes (png_ptr, 8); /* we already read the 8 signature bytes */
png_set_read_fn (png_ptr, (void *) infile, gdPngReadData);
png_read_info (png_ptr, info_ptr); /* read all PNG info up to image data */
png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_type, NULL, NULL);
if ((color_type == PNG_COLOR_TYPE_RGB) ||
(color_type == PNG_COLOR_TYPE_RGB_ALPHA))
{
im = gdImageCreateTrueColor ((int) width, (int) height);
}
else
{
im = gdImageCreate ((int) width, (int) height);
}
if (im == NULL)
{
fprintf (stderr, "gd-png error: cannot allocate gdImage struct\n");
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
gdFree (image_data);
gdFree (row_pointers);
return NULL;
}
if (bit_depth == 16)
png_set_strip_16 (png_ptr);
else if (bit_depth < 8)
png_set_packing (png_ptr); /* expand to 1 byte per pixel */
switch (color_type)
{
case PNG_COLOR_TYPE_PALETTE:
png_get_PLTE (png_ptr, info_ptr, &palette, &num_palette);
#ifdef DEBUG
fprintf (stderr, "gd-png color_type is palette, colors: %d\n",
num_palette);
#endif /* DEBUG */
if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
{
/* gd 2.0: we support this rather thoroughly now. Grab the
first fully transparent entry, if any, as the value of
the simple-transparency index, mostly for backwards
binary compatibility. The alpha channel is where it's
really at these days. */
int firstZero = 1;
png_get_tRNS (png_ptr, info_ptr, &trans, &num_trans, NULL);
for (i = 0; i < num_trans; ++i)
{
im->alpha[i] = gdAlphaMax - (trans[i] >> 1);
if ((trans[i] == 0) && (firstZero))
{
im->transparent = i;
firstZero = 0;
}
}
}
break;
case PNG_COLOR_TYPE_GRAY:
case PNG_COLOR_TYPE_GRAY_ALPHA:
/* create a fake palette and check for single-shade transparency */
if ((palette = (png_colorp) gdMalloc (256 * sizeof (png_color))) == NULL)
{
fprintf (stderr, "gd-png error: cannot allocate gray palette\n");
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return NULL;
}
palette_allocated = TRUE;
if (bit_depth < 8)
{
num_palette = 1 << bit_depth;
for (i = 0; i < 256; ++i)
{
j = (255 * i) / (num_palette - 1);
palette[i].red = palette[i].green = palette[i].blue = j;
}
}
else
{
num_palette = 256;
for (i = 0; i < 256; ++i)
{
palette[i].red = palette[i].green = palette[i].blue = i;
}
}
if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
{
png_get_tRNS (png_ptr, info_ptr, NULL, NULL, &trans_gray_rgb);
if (bit_depth == 16) /* png_set_strip_16() not yet in effect */
transparent = trans_gray_rgb->gray >> 8;
else
transparent = trans_gray_rgb->gray;
/* Note slight error in 16-bit case: up to 256 16-bit shades
* may get mapped to a single 8-bit shade, and only one of them
* is supposed to be transparent. IOW, both opaque pixels and
* transparent pixels will be mapped into the transparent entry.
* There is no particularly good way around this in the case
* that all 256 8-bit shades are used, but one could write some
* custom 16-bit code to handle the case where there are gdFree
* palette entries. This error will be extremely rare in
* general, though. (Quite possibly there is only one such
* image in existence.) */
}
break;
case PNG_COLOR_TYPE_RGB:
case PNG_COLOR_TYPE_RGB_ALPHA:
/* gd 2.0: we now support truecolor. See the comment above
for a rare situation in which the transparent pixel may not
work properly with 16-bit channels. */
if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
{
png_get_tRNS (png_ptr, info_ptr, NULL, NULL, &trans_color_rgb);
if (bit_depth == 16) /* png_set_strip_16() not yet in effect */
transparent = gdTrueColor (trans_color_rgb->red >> 8,
trans_color_rgb->green >> 8,
trans_color_rgb->blue >> 8);
else
transparent = gdTrueColor (trans_color_rgb->red,
trans_color_rgb->green,
trans_color_rgb->blue);
}
break;
}
png_read_update_info (png_ptr, info_ptr);
/* allocate space for the PNG image data */
rowbytes = png_get_rowbytes (png_ptr, info_ptr);
if ((image_data = (png_bytep) gdMalloc (rowbytes * height)) == NULL)
{
fprintf (stderr, "gd-png error: cannot allocate image data\n");
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return NULL;
}
if ((row_pointers = (png_bytepp) gdMalloc (height * sizeof (png_bytep))) == NULL)
{
fprintf (stderr, "gd-png error: cannot allocate row pointers\n");
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
gdFree (image_data);
return NULL;
}
/* set the individual row_pointers to point at the correct offsets */
for (j = 0; j < height; ++j)
{
row_pointers[j] = image_data + j * rowbytes;
}
png_read_image (png_ptr, row_pointers); /* read whole image... */
png_read_end (png_ptr, NULL); /* ...done! */
if (!im->trueColor)
{
im->colorsTotal = num_palette;
im->transparent = transparent;
/* load the palette and mark all entries "open" (unused) for now */
open = im->open;
for (i = 0; i < num_palette; ++i)
{
im->red[i] = palette[i].red;
im->green[i] = palette[i].green;
im->blue[i] = palette[i].blue;
open[i] = 1;
}
for (i = num_palette; i < gdMaxColors; ++i)
{
open[i] = 1;
}
}
im->interlace = (interlace_type == PNG_INTERLACE_ADAM7);
/* can't nuke structs until done with palette */
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
switch (color_type)
{
case PNG_COLOR_TYPE_RGB:
for (j = 0; j < height; j++)
{
int boffset = 0;
for (i = 0; i < width; i++)
{
register png_byte r = row_pointers[j][boffset++];
register png_byte g = row_pointers[j][boffset++];
register png_byte b = row_pointers[j][boffset++];
im->tpixels[j][i] = gdTrueColor (r, g, b);
}
}
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
for (j = 0; j < height; j++)
{
int boffset = 0;
for (i = 0; i < width; i++)
{
register png_byte r = row_pointers[j][boffset++];
register png_byte g = row_pointers[j][boffset++];
register png_byte b = row_pointers[j][boffset++];
/* gd has only 7 bits of alpha channel resolution, and
127 is transparent, 0 opaque. A moment of convenience,
a lifetime of compatibility. */
register png_byte a = gdAlphaMax -
(row_pointers[j][boffset++] >> 1);
im->tpixels[j][i] = gdTrueColorAlpha (r, g, b, a);
}
}
break;
default:
/* Palette image, or something coerced to be one */
for (j = 0; j < height; ++j)
{
for (i = 0; i < width; ++i)
{
register png_byte idx = row_pointers[j][i];
im->pixels[j][i] = idx;
open[idx] = 0;
}
}
}
#ifdef DEBUG
if (!im->trueColor)
{
for (i = num_palette; i < gdMaxColors; ++i)
{
if (!open[i])
{
fprintf (stderr, "gd-png warning: image data references out-of-range"
" color index (%d)\n", i);
}
}
}
#endif
if (palette_allocated)
gdFree (palette);
gdFree (image_data);
gdFree (row_pointers);
return im;
}
void
gdImagePng (gdImagePtr im, FILE * outFile)
{
gdIOCtx *out = gdNewFileCtx (outFile);
gdImagePngCtx (im, out);
out->free (out);
}
void *
gdImagePngPtr (gdImagePtr im, int *size)
{
void *rv;
gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
gdImagePngCtx (im, out);
rv = gdDPExtractData (out, size);
out->free (out);
return rv;
}
/* This routine is based in part on code from Dale Lutz (Safe Software Inc.)
* and in part on demo code from Chapter 15 of "PNG: The Definitive Guide"
* (http://www.cdrom.com/pub/png/pngbook.html).
*/
void
gdImagePngCtx (gdImagePtr im, gdIOCtx * outfile)
{
int i, j, bit_depth, interlace_type;
int width = im->sx;
int height = im->sy;
int colors = im->colorsTotal;
int *open = im->open;
int mapping[gdMaxColors]; /* mapping[gd_index] == png_index */
png_byte trans_values[256];
png_color_16 trans_rgb_value;
png_color palette[gdMaxColors];
png_structp png_ptr;
png_infop info_ptr;
volatile int transparent = im->transparent;
volatile int remap = FALSE;
#ifndef PNG_SETJMP_NOT_SUPPORTED
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
&gdPngJmpbufStruct, gdPngErrorHandler, NULL);
#else
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
#endif
if (png_ptr == NULL)
{
fprintf (stderr, "gd-png error: cannot allocate libpng main struct\n");
return;
}
info_ptr = png_create_info_struct (png_ptr);
if (info_ptr == NULL)
{
fprintf (stderr, "gd-png error: cannot allocate libpng info struct\n");
png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
return;
}
#ifndef PNG_SETJMP_NOT_SUPPORTED
if (setjmp (gdPngJmpbufStruct.jmpbuf))
{
fprintf (stderr, "gd-png error: setjmp returns error condition\n");
png_destroy_write_struct (&png_ptr, &info_ptr);
return;
}
#endif
png_set_write_fn (png_ptr, (void *) outfile, gdPngWriteData, gdPngFlushData);
/* This is best for palette images, and libpng defaults to it for
palette images anyway, so we don't need to do it explicitly.
What to ideally do for truecolor images depends, alas, on the image.
gd is intentionally imperfect and doesn't spend a lot of time
fussing with such things. */
/* png_set_filter(png_ptr, 0, PNG_FILTER_NONE); */
/* may want to force maximum compression, but time penalty is large */
/* png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); */
/* can set this to a smaller value without compromising compression if all
* image data is 16K or less; will save some decoder memory [min == 8] */
/* png_set_compression_window_bits(png_ptr, 15); */
if (!im->trueColor)
{
if (transparent >= im->colorsTotal ||
(transparent >= 0 && open[transparent]))
transparent = -1;
}
if (!im->trueColor)
{
for (i = 0; i < gdMaxColors; ++i)
mapping[i] = -1;
}
if (!im->trueColor)
{
/* count actual number of colors used (colorsTotal == high-water mark) */
colors = 0;
for (i = 0; i < im->colorsTotal; ++i)
{
if (!open[i])
{
mapping[i] = colors;
++colors;
}
}
if (colors < im->colorsTotal)
{
remap = TRUE;
}
if (colors <= 2)
bit_depth = 1;
else if (colors <= 4)
bit_depth = 2;
else if (colors <= 16)
bit_depth = 4;
else
bit_depth = 8;
}
interlace_type = im->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
if (im->trueColor)
{
if (im->saveAlphaFlag)
{
png_set_IHDR (png_ptr, info_ptr, width, height, 8,
PNG_COLOR_TYPE_RGB_ALPHA, interlace_type,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
}
else
{
png_set_IHDR (png_ptr, info_ptr, width, height, 8,
PNG_COLOR_TYPE_RGB, interlace_type,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
}
}
else
{
png_set_IHDR (png_ptr, info_ptr, width, height, bit_depth,
PNG_COLOR_TYPE_PALETTE, interlace_type,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
}
if (im->trueColor && (!im->saveAlphaFlag) && (transparent >= 0))
{
trans_rgb_value.red = gdTrueColorGetRed (im->trueColor);
trans_rgb_value.green = gdTrueColorGetGreen (im->trueColor);
trans_rgb_value.blue = gdTrueColorGetBlue (im->trueColor);
png_set_tRNS (png_ptr, info_ptr, 0, 0, &trans_rgb_value);
}
if (!im->trueColor)
{
/* Oy veh. Remap the PNG palette to put the
entries with interesting alpha channel
values first. This minimizes the size
of the tRNS chunk and thus the size
of the PNG file as a whole. */
int tc = 0;
int i;
int j;
int k;
int highTrans = -1;
for (i = 0; (i < im->colorsTotal); i++)
{
if ((!im->open[i]) &&
(im->alpha[i] != gdAlphaOpaque))
{
tc++;
}
}
if (tc)
{
#if 0
for (i = 0; (i < im->colorsTotal); i++)
{
trans_values[i] = 255 -
((im->alpha[i] << 1) +
(im->alpha[i] >> 7));
}
png_set_tRNS (png_ptr, info_ptr, trans_values, 256, NULL);
#endif
if (!remap)
{
remap = TRUE;
}
/* (Semi-)transparent indexes come up from the bottom
of the list of real colors; opaque
indexes come down from the top */
j = 0;
k = colors - 1;
for (i = 0; (i < im->colorsTotal); i++)
{
if (!im->open[i])
{
if (im->alpha[i] != gdAlphaOpaque)
{
trans_values[j] = 255 -
((im->alpha[i] << 1) +
(im->alpha[i] >> 7));
mapping[i] = j++;
}
else
{
mapping[i] = k--;
}
}
}
png_set_tRNS (png_ptr, info_ptr, trans_values, tc, NULL);
}
}
/* convert palette to libpng layout */
if (!im->trueColor)
{
if (remap)
for (i = 0; i < im->colorsTotal; ++i)
{
if (mapping[i] < 0)
continue;
palette[mapping[i]].red = im->red[i];
palette[mapping[i]].green = im->green[i];
palette[mapping[i]].blue = im->blue[i];
}
else
for (i = 0; i < colors; ++i)
{
palette[i].red = im->red[i];
palette[i].green = im->green[i];
palette[i].blue = im->blue[i];
}
png_set_PLTE (png_ptr, info_ptr, palette, colors);
}
/* write out the PNG header info (everything up to first IDAT) */
png_write_info (png_ptr, info_ptr);
/* make sure < 8-bit images are packed into pixels as tightly as possible */
png_set_packing (png_ptr);
/* This code allocates a set of row buffers and copies the gd image data
* into them only in the case that remapping is necessary; in gd 1.3 and
* later, the im->pixels array is laid out identically to libpng's row
* pointers and can be passed to png_write_image() function directly.
* The remapping case could be accomplished with less memory for non-
* interlaced images, but interlacing causes some serious complications. */
if (im->trueColor)
{
int channels = im->saveAlphaFlag ? 4 : 3;
/* Our little 7-bit alpha channel trick costs us a bit here. */
png_bytep *row_pointers;
row_pointers = gdMalloc (sizeof (png_bytep) * height);
if (row_pointers == NULL)
{
fprintf (stderr, "gd-png error: unable to allocate row_pointers\n");
}
for (j = 0; j < height; ++j)
{
int bo = 0;
if ((row_pointers[j] = (png_bytep) gdMalloc (width * channels)) == NULL)
{
fprintf (stderr, "gd-png error: unable to allocate rows\n");
for (i = 0; i < j; ++i)
gdFree (row_pointers[i]);
return;
}
for (i = 0; i < width; ++i)
{
unsigned char a;
row_pointers[j][bo++] = gdTrueColorGetRed (im->tpixels[j][i]);
row_pointers[j][bo++] = gdTrueColorGetGreen (im->tpixels[j][i]);
row_pointers[j][bo++] = gdTrueColorGetBlue (im->tpixels[j][i]);
if (im->saveAlphaFlag)
{
/* convert the 7-bit alpha channel to an 8-bit alpha channel.
We do a little bit-flipping magic, repeating the MSB
as the LSB, to ensure that 0 maps to 0 and
127 maps to 255. We also have to invert to match
PNG's convention in which 255 is opaque. */
a = gdTrueColorGetAlpha (im->tpixels[j][i]);
row_pointers[j][bo++] = 255 - ((a << 1) + (a >> 7));
}
}
}
png_write_image (png_ptr, row_pointers);
png_write_end (png_ptr, info_ptr);
for (j = 0; j < height; ++j)
gdFree (row_pointers[j]);
gdFree (row_pointers);
}
else
{
if (remap)
{
png_bytep *row_pointers;
row_pointers = gdMalloc (sizeof (png_bytep) * height);
if (row_pointers == NULL)
{
fprintf (stderr, "gd-png error: unable to allocate row_pointers\n");
}
for (j = 0; j < height; ++j)
{
if ((row_pointers[j] = (png_bytep) gdMalloc (width)) == NULL)
{
fprintf (stderr, "gd-png error: unable to allocate rows\n");
for (i = 0; i < j; ++i)
gdFree (row_pointers[i]);
return;
}
for (i = 0; i < width; ++i)
row_pointers[j][i] = mapping[im->pixels[j][i]];
}
png_write_image (png_ptr, row_pointers);
png_write_end (png_ptr, info_ptr);
for (j = 0; j < height; ++j)
gdFree (row_pointers[j]);
gdFree (row_pointers);
}
else
{
png_write_image (png_ptr, im->pixels);
png_write_end (png_ptr, info_ptr);
}
}
/* 1.6.3: maybe we should give that memory BACK! TBB */
png_destroy_write_struct (&png_ptr, &info_ptr);
}
#endif /* HAVE_LIBPNG */

38
ext/gd/libgd/gd_ss.c Normal file
View File

@ -0,0 +1,38 @@
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "gd.h"
#define TRUE 1
#define FALSE 0
/* Exported functions: */
extern void gdImagePngToSink (gdImagePtr im, gdSinkPtr out);
extern gdImagePtr gdImageCreateFromPngSource (gdSourcePtr inSource);
/* Use this for commenting out debug-print statements. */
/* Just use the first '#define' to allow all the prints... */
/*#define GD_SS_DBG(s) (s) */
#define GD_SS_DBG(s)
void
gdImagePngToSink (gdImagePtr im, gdSinkPtr outSink)
{
gdIOCtx *out = gdNewSSCtx (NULL, outSink);
gdImagePngCtx (im, out);
out->free (out);
}
gdImagePtr
gdImageCreateFromPngSource (gdSourcePtr inSource)
{
gdIOCtx *in = gdNewSSCtx (inSource, NULL);
gdImagePtr im;
im = gdImageCreateFromPngCtx (in);
in->free (in);
return im;
}

1688
ext/gd/libgd/gd_topal.c Normal file

File diff suppressed because it is too large Load Diff

218
ext/gd/libgd/gd_wbmp.c Normal file
View File

@ -0,0 +1,218 @@
/*
WBMP: Wireless Bitmap Type 0: B/W, Uncompressed Bitmap
Specification of the WBMP format can be found in the file:
SPEC-WAESpec-19990524.pdf
You can download the WAP specification on: http://www.wapforum.com/
gd_wbmp.c
Copyright (C) Johan Van den Brande (johan@vandenbrande.com)
Fixed: gdImageWBMPPtr, gdImageWBMP
Recoded: gdImageWBMPCtx for use with my wbmp library
(wbmp library included, but you can find the latest distribution
at http://www.vandenbrande.com/wbmp)
Implemented: gdImageCreateFromWBMPCtx, gdImageCreateFromWBMP
---------------------------------------------------------------------------
Parts of this code are from Maurice Smurlo.
** Copyright (C) Maurice Szmurlo --- T-SIT --- January 2000
** (Maurice.Szmurlo@info.unicaen.fr)
** 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.
---------------------------------------------------------------------------
Parts od this code are inspired by 'pbmtowbmp.c' and 'wbmptopbm.c' by
Terje Sannum <terje@looplab.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.
**
---------------------------------------------------------------------------
Todo:
gdCreateFromWBMP function for reading WBMP files
----------------------------------------------------------------------------
*/
#include <gd.h>
#include <gdfonts.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include "wbmp.h"
/* gd_putout
** ---------
** Wrapper around gdPutC for use with writewbmp
**
*/
void
gd_putout (int i, void *out)
{
gdPutC (i, (gdIOCtx *) out);
}
/* gd_getin
** --------
** Wrapper around gdGetC for use with readwbmp
**
*/
int
gd_getin (void *in)
{
return (gdGetC ((gdIOCtx *) in));
}
/* gdImageWBMPCtx
** --------------
** Write the image as a wbmp file
** Parameters are:
** image: gd image structure;
** fg: the index of the foreground color. any other value will be
** considered as background and will not be written
** out: the stream where to write
*/
void
gdImageWBMPCtx (gdImagePtr image, int fg, gdIOCtx * out)
{
int x, y, pos;
Wbmp *wbmp;
/* create the WBMP */
if ((wbmp = createwbmp (gdImageSX (image), gdImageSY (image), WBMP_WHITE)) == NULL)
fprintf (stderr, "Could not create WBMP\n");
/* fill up the WBMP structure */
pos = 0;
for (y = 0; y < gdImageSY (image); y++)
{
for (x = 0; x < gdImageSX (image); x++)
{
if (gdImageGetPixel (image, x, y) == fg)
{
wbmp->bitmap[pos] = WBMP_BLACK;
}
pos++;
}
}
/* write the WBMP to a gd file descriptor */
if (writewbmp (wbmp, &gd_putout, out))
fprintf (stderr, "Could not save WBMP\n");
/* des submitted this bugfix: gdFree the memory. */
freewbmp (wbmp);
}
/* gdImageCreateFromWBMPCtx
** ------------------------
** Create a gdImage from a WBMP file input from an gdIOCtx
*/
gdImagePtr
gdImageCreateFromWBMPCtx (gdIOCtx * infile)
{
/* FILE *wbmp_file; */
Wbmp *wbmp;
gdImagePtr im = NULL;
int black, white;
int col, row, pos;
if (readwbmp (&gd_getin, infile, &wbmp))
return (NULL);
if (!(im = gdImageCreate (wbmp->width, wbmp->height)))
{
freewbmp (wbmp);
return (NULL);
}
/* create the background color */
white = gdImageColorAllocate (im, 255, 255, 255);
/* create foreground color */
black = gdImageColorAllocate (im, 0, 0, 0);
/* fill in image (in a wbmp 1 = white/ 0 = black) */
pos = 0;
for (row = 0; row < wbmp->height; row++)
{
for (col = 0; col < wbmp->width; col++)
{
if (wbmp->bitmap[pos++] == WBMP_WHITE)
{
gdImageSetPixel (im, col, row, white);
}
else
{
gdImageSetPixel (im, col, row, black);
}
}
}
freewbmp (wbmp);
return (im);
}
/* gdImageCreateFromWBMP
** ---------------------
*/
gdImagePtr
gdImageCreateFromWBMP (FILE * inFile)
{
gdImagePtr im;
gdIOCtx *in = gdNewFileCtx (inFile);
im = gdImageCreateFromWBMPCtx (in);
in->free (in);
return (im);
}
/* gdImageWBMP
** -----------
*/
void
gdImageWBMP (gdImagePtr im, int fg, FILE * outFile)
{
gdIOCtx *out = gdNewFileCtx (outFile);
gdImageWBMPCtx (im, fg, out);
out->free (out);
}
/* gdImageWBMPPtr
** --------------
*/
void *
gdImageWBMPPtr (gdImagePtr im, int *size, int fg)
{
void *rv;
gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
gdImageWBMPCtx (im, fg, out);
rv = gdDPExtractData (out, size);
out->free (out);
return rv;
}

214
ext/gd/libgd/gdcache.c Normal file
View File

@ -0,0 +1,214 @@
#include "gd.h"
#include "gdhelpers.h"
#ifdef HAVE_LIBTTF
#define NEED_CACHE 1
#else
#ifdef HAVE_LIBFREETYPE
#define NEED_CACHE 1
#endif
#endif
#ifdef NEED_CACHE
/*
* gdcache.c
*
* Caches of pointers to user structs in which the least-recently-used
* element is replaced in the event of a cache miss after the cache has
* reached a given size.
*
* John Ellson (ellson@lucent.com) Oct 31, 1997
*
* Test this with:
* gcc -o gdcache -g -Wall -DTEST gdcache.c
*
* The cache is implemented by a singly-linked list of elements
* each containing a pointer to a user struct that is being managed by
* the cache.
*
* The head structure has a pointer to the most-recently-used
* element, and elements are moved to this position in the list each
* time they are used. The head also contains pointers to three
* user defined functions:
* - a function to test if a cached userdata matches some keydata
* - a function to provide a new userdata struct to the cache
* if there has been a cache miss.
* - a function to release a userdata struct when it is
* no longer being managed by the cache
*
* In the event of a cache miss the cache is allowed to grow up to
* a specified maximum size. After the maximum size is reached then
* the least-recently-used element is discarded to make room for the
* new. The most-recently-returned value is always left at the
* beginning of the list after retrieval.
*
* In the current implementation the cache is traversed by a linear
* search from most-recent to least-recent. This linear search
* probably limits the usefulness of this implementation to cache
* sizes of a few tens of elements.
*/
#include "gdcache.h"
/*********************************************************/
/* implementation */
/*********************************************************/
/* create a new cache */
gdCache_head_t *
gdCacheCreate (
int size,
gdCacheTestFn_t gdCacheTest,
gdCacheFetchFn_t gdCacheFetch,
gdCacheReleaseFn_t gdCacheRelease)
{
gdCache_head_t *head;
head = (gdCache_head_t *) gdMalloc (sizeof (gdCache_head_t));
head->mru = NULL;
head->size = size;
head->gdCacheTest = gdCacheTest;
head->gdCacheFetch = gdCacheFetch;
head->gdCacheRelease = gdCacheRelease;
return head;
}
void
gdCacheDelete (gdCache_head_t * head)
{
gdCache_element_t *elem, *prev;
elem = head->mru;
while (elem)
{
(*(head->gdCacheRelease)) (elem->userdata);
prev = elem;
elem = elem->next;
gdFree ((char *) prev);
}
gdFree ((char *) head);
}
void *
gdCacheGet (gdCache_head_t * head, void *keydata)
{
int i = 0;
gdCache_element_t *elem, *prev = NULL, *prevprev = NULL;
void *userdata;
elem = head->mru;
while (elem)
{
if ((*(head->gdCacheTest)) (elem->userdata, keydata))
{
if (i)
{ /* if not already most-recently-used */
/* relink to top of list */
prev->next = elem->next;
elem->next = head->mru;
head->mru = elem;
}
return elem->userdata;
}
prevprev = prev;
prev = elem;
elem = elem->next;
i++;
}
userdata = (*(head->gdCacheFetch)) (&(head->error), keydata);
if (!userdata)
{
/* if there was an error in the fetch then don't cache */
return NULL;
}
if (i < head->size)
{ /* cache still growing - add new elem */
elem = (gdCache_element_t *) gdMalloc (sizeof (gdCache_element_t));
}
else
{ /* cache full - replace least-recently-used */
/* preveprev becomes new end of list */
prevprev->next = NULL;
elem = prev;
(*(head->gdCacheRelease)) (elem->userdata);
}
/* relink to top of list */
elem->next = head->mru;
head->mru = elem;
elem->userdata = userdata;
return userdata;
}
/*********************************************************/
/* test stub */
/*********************************************************/
#ifdef TEST
#include <stdio.h>
typedef struct
{
int key;
int value;
}
key_value_t;
static int
cacheTest (void *map, void *key)
{
return (((key_value_t *) map)->key == *(int *) key);
}
static void *
cacheFetch (char **error, void *key)
{
key_value_t *map;
map = (key_value_t *) gdMalloc (sizeof (key_value_t));
map->key = *(int *) key;
map->value = 3;
*error = NULL;
return (void *) map;
}
static void
cacheRelease (void *map)
{
gdFree ((char *) map);
}
int
main (char *argv[], int argc)
{
gdCache_head_t *cacheTable;
int elem, key;
cacheTable = gdCacheCreate (3, cacheTest, cacheFetch, cacheRelease);
key = 20;
elem = *(int *) gdCacheGet (cacheTable, &key);
key = 30;
elem = *(int *) gdCacheGet (cacheTable, &key);
key = 40;
elem = *(int *) gdCacheGet (cacheTable, &key);
key = 50;
elem = *(int *) gdCacheGet (cacheTable, &key);
key = 30;
elem = *(int *) gdCacheGet (cacheTable, &key);
key = 30;
elem = *(int *) gdCacheGet (cacheTable, &key);
gdCacheDelete (cacheTable);
return 0;
}
#endif /* TEST */
#endif /* HAVE_LIBTTF */

83
ext/gd/libgd/gdcache.h Normal file
View File

@ -0,0 +1,83 @@
/*
* gdcache.h
*
* Caches of pointers to user structs in which the least-recently-used
* element is replaced in the event of a cache miss after the cache has
* reached a given size.
*
* John Ellson (ellson@lucent.com) Oct 31, 1997
*
* Test this with:
* gcc -o gdcache -g -Wall -DTEST gdcache.c
*
* The cache is implemented by a singly-linked list of elements
* each containing a pointer to a user struct that is being managed by
* the cache.
*
* The head structure has a pointer to the most-recently-used
* element, and elements are moved to this position in the list each
* time they are used. The head also contains pointers to three
* user defined functions:
* - a function to test if a cached userdata matches some keydata
* - a function to provide a new userdata struct to the cache
* if there has been a cache miss.
* - a function to release a userdata struct when it is
* no longer being managed by the cache
*
* In the event of a cache miss the cache is allowed to grow up to
* a specified maximum size. After the maximum size is reached then
* the least-recently-used element is discarded to make room for the
* new. The most-recently-returned value is always left at the
* beginning of the list after retrieval.
*
* In the current implementation the cache is traversed by a linear
* search from most-recent to least-recent. This linear search
* probably limits the usefulness of this implementation to cache
* sizes of a few tens of elements.
*/
/*********************************************************/
/* header */
/*********************************************************/
#include <malloc.h>
#ifndef NULL
#define NULL (void *)0
#endif
/* user defined function templates */
typedef int (*gdCacheTestFn_t)(void *userdata, void *keydata);
typedef void *(*gdCacheFetchFn_t)(char **error, void *keydata);
typedef void (*gdCacheReleaseFn_t)(void *userdata);
/* element structure */
typedef struct gdCache_element_s gdCache_element_t;
struct gdCache_element_s {
gdCache_element_t *next;
void *userdata;
};
/* head structure */
typedef struct gdCache_head_s gdCache_head_t;
struct gdCache_head_s {
gdCache_element_t *mru;
int size;
char *error;
gdCacheTestFn_t gdCacheTest;
gdCacheFetchFn_t gdCacheFetch;
gdCacheReleaseFn_t gdCacheRelease;
};
/* function templates */
gdCache_head_t *
gdCacheCreate(
int size,
gdCacheTestFn_t gdCacheTest,
gdCacheFetchFn_t gdCacheFetch,
gdCacheReleaseFn_t gdCacheRelease );
void
gdCacheDelete( gdCache_head_t *head );
void *
gdCacheGet( gdCache_head_t *head, void *keydata );

118
ext/gd/libgd/gddemo.c Normal file
View File

@ -0,0 +1,118 @@
#include <stdio.h>
#include "gd.h"
#include "gdfontg.h"
#include "gdfonts.h"
int
main (void)
{
/* Input and output files */
FILE *in;
FILE *out;
/* Input and output images */
gdImagePtr im_in = 0, im_out = 0;
/* Brush image */
gdImagePtr brush;
/* Color indexes */
int white;
int blue;
int red;
int green;
/* Points for polygon */
gdPoint points[3];
/* Create output image, 256 by 256 pixels, true color. */
im_out = gdImageCreateTrueColor (256, 256);
/* First color allocated is background. */
white = gdImageColorAllocate (im_out, 255, 255, 255);
/* Set transparent color. */
gdImageColorTransparent (im_out, white);
/* Try to load demoin.png and paste part of it into the
output image. */
in = fopen ("demoin.png", "rb");
if (!in)
{
fprintf (stderr, "Can't load source image; this demo\n");
fprintf (stderr, "is much more impressive if demoin.png\n");
fprintf (stderr, "is available.\n");
im_in = 0;
}
else
{
im_in = gdImageCreateFromPng (in);
fclose (in);
/* Now copy, and magnify as we do so */
gdImageCopyResized (im_out, im_in,
32, 32, 0, 0, 192, 192, 255, 255);
}
red = gdImageColorAllocate (im_out, 255, 0, 0);
green = gdImageColorAllocate (im_out, 0, 255, 0);
blue = gdImageColorAllocate (im_out, 0, 0, 255);
/* Rectangle */
gdImageLine (im_out, 16, 16, 240, 16, green);
gdImageLine (im_out, 240, 16, 240, 240, green);
gdImageLine (im_out, 240, 240, 16, 240, green);
gdImageLine (im_out, 16, 240, 16, 16, green);
/* Circle */
gdImageArc (im_out, 128, 128, 60, 20, 0, 720, blue);
/* Arc */
gdImageArc (im_out, 128, 128, 40, 40, 90, 270, blue);
/* Flood fill: doesn't do much on a continuously
variable tone jpeg original. */
gdImageFill (im_out, 8, 8, blue);
/* Polygon */
points[0].x = 64;
points[0].y = 0;
points[1].x = 0;
points[1].y = 128;
points[2].x = 128;
points[2].y = 128;
gdImageFilledPolygon (im_out, points, 3, green);
/* Brush. A fairly wild example also involving a line style! */
if (im_in)
{
int style[8];
brush = gdImageCreateTrueColor (16, 16);
gdImageCopyResized (brush, im_in,
0, 0, 0, 0,
gdImageSX (brush), gdImageSY (brush),
gdImageSX (im_in), gdImageSY (im_in));
gdImageSetBrush (im_out, brush);
/* With a style, so they won't overprint each other.
Normally, they would, yielding a fat-brush effect. */
style[0] = 0;
style[1] = 0;
style[2] = 0;
style[3] = 0;
style[4] = 0;
style[5] = 0;
style[6] = 0;
style[7] = 1;
gdImageSetStyle (im_out, style, 8);
/* Draw the styled, brushed line */
gdImageLine (im_out, 0, 255, 255, 0, gdStyledBrushed);
}
/* Text */
gdImageString (im_out, gdFontGiant, 32, 32,
(unsigned char *) "hi", red);
gdImageStringUp (im_out, gdFontSmall, 64, 64,
(unsigned char *) "hi", red);
/* Make output image interlaced (progressive, in the case of JPEG) */
gdImageInterlace (im_out, 1);
out = fopen ("demoout.png", "wb");
/* Write PNG */
gdImagePng (im_out, out);
fclose (out);
gdImageDestroy (im_out);
if (im_in)
{
gdImageDestroy (im_in);
}
return 0;
}

4384
ext/gd/libgd/gdfontg.c Normal file

File diff suppressed because it is too large Load Diff

29
ext/gd/libgd/gdfontg.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef _GDFONTG_H_
#define _GDFONTG_H_ 1
#ifdef __cplusplus
extern "C" {
#endif
/*
This is a header file for gd font, generated using
bdftogd version 0.51 by Jan Pazdziora, adelton@fi.muni.cz
from bdf font
-Misc-Fixed-Bold-R-Normal-Sans-15-140-75-75-C-90-ISO8859-2
at Mon Jan 26 14:45:58 1998.
The original bdf was holding following copyright:
"Libor Skarvada, libor@informatics.muni.cz"
*/
#include "gd.h"
extern gdFontPtr gdFontGiant;
#ifdef __cplusplus
}
#endif
#endif

4641
ext/gd/libgd/gdfontl.c Normal file

File diff suppressed because it is too large Load Diff

30
ext/gd/libgd/gdfontl.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef _GDFONTL_H_
#define _GDFONTL_H_ 1
#ifdef __cplusplus
extern "C" {
#endif
/*
This is a header file for gd font, generated using
bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz
from bdf font
-misc-fixed-medium-r-normal--16-140-75-75-c-80-iso8859-2
at Tue Jan 6 19:39:27 1998.
The original bdf was holding following copyright:
"Libor Skarvada, libor@informatics.muni.cz"
*/
#include "gd.h"
extern gdFontPtr gdFontLarge;
#ifdef __cplusplus
}
#endif
#endif

3871
ext/gd/libgd/gdfontmb.c Normal file

File diff suppressed because it is too large Load Diff

28
ext/gd/libgd/gdfontmb.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _GDFONTMB_H_
#define _GDFONTMB_H_ 1
#ifdef __cplusplus
extern "C" {
#endif
/*
This is a header file for gd font, generated using
bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz
from bdf font
-misc-fixed-bold-r-normal-sans-13-94-100-100-c-70-iso8859-2
at Thu Jan 8 13:54:57 1998.
No copyright info was found in the original bdf.
*/
#include "gd.h"
extern gdFontPtr gdFontMediumBold;
#ifdef __cplusplus
}
#endif
#endif

3871
ext/gd/libgd/gdfonts.c Normal file

File diff suppressed because it is too large Load Diff

28
ext/gd/libgd/gdfonts.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _GDFONTS_H_
#define _GDFONTS_H_ 1
#ifdef __cplusplus
extern "C" {
#endif
/*
This is a header file for gd font, generated using
bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz
from bdf font
-misc-fixed-medium-r-semicondensed-sans-12-116-75-75-c-60-iso8859-2
at Thu Jan 8 14:13:20 1998.
No copyright info was found in the original bdf.
*/
#include "gd.h"
extern gdFontPtr gdFontSmall;
#ifdef __cplusplus
}
#endif
#endif

2592
ext/gd/libgd/gdfontt.c Normal file

File diff suppressed because it is too large Load Diff

29
ext/gd/libgd/gdfontt.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef _GDFONTT_H_
#define _GDFONTT_H_ 1
#ifdef __cplusplus
extern "C" {
#endif
/*
This is a header file for gd font, generated using
bdftogd version 0.5 by Jan Pazdziora, adelton@fi.muni.cz
from bdf font
-Misc-Fixed-Medium-R-Normal--8-80-75-75-C-50-ISO8859-2
at Thu Jan 8 13:49:54 1998.
The original bdf was holding following copyright:
"Libor Skarvada, libor@informatics.muni.cz"
*/
#include "gd.h"
extern gdFontPtr gdFontTiny;
#ifdef __cplusplus
}
#endif
#endif

936
ext/gd/libgd/gdft.c Normal file
View File

@ -0,0 +1,936 @@
/********************************************/
/* gd interface to freetype library */
/* */
/* John Ellson ellson@lucent.com */
/********************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "gd.h"
#include "gdhelpers.h"
#ifndef MSWIN32
#include <unistd.h>
#else
#define R_OK 2
#endif
/* number of antialised colors for indexed bitmaps */
#define NUMCOLORS 8
char *
gdImageStringTTF (gdImage * im, int *brect, int fg, char *fontlist,
double ptsize, double angle, int x, int y, char *string)
{
gdImageStringFT (im, brect, fg, fontlist, ptsize,
angle, x, y, string);
}
#ifndef HAVE_LIBFREETYPE
char *
gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
double ptsize, double angle, int x, int y, char *string)
{
return "libgd was not built with FreeType font support\n";
}
#else
#include "gdcache.h"
#include "freetype/freetype.h"
#include "freetype/ftglyph.h"
/* number of fonts cached before least recently used is replaced */
#define FONTCACHESIZE 6
/* number of antialias color lookups cached */
#define TWEENCOLORCACHESIZE 32
/*
* Line separation as a factor of font height.
* No space between if LINESPACE = 1.00
* Line separation will be rounded up to next pixel row.
*/
#define LINESPACE 1.05
/*
* The character (space) used to separate alternate fonts in the
* fontlist parameter to gdImageStringFT.
*/
#define LISTSEPARATOR " "
/*
* DEFAULT_FONTPATH and PATHSEPARATOR are host type dependent and
* are normally set by configure in gvconfig.h. These are just
* some last resort values that might match some Un*x system
* if building this version of gd separate from graphviz.
*/
#ifndef DEFAULT_FONTPATH
#define DEFAULT_FONTPATH "/usr/share/fonts/truetype"
#endif
#ifndef PATHSEPARATOR
#define PATHSEPARATOR ":"
#endif
#ifndef TRUE
#define FALSE 0
#define TRUE !FALSE
#endif
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
typedef struct
{
char *fontlist; /* key */
FT_Library *library;
FT_Face face;
FT_Bool have_char_map_unicode, have_char_map_big5, have_char_map_sjis,
have_char_map_apple_roman;
gdCache_head_t *glyphCache;
}
font_t;
typedef struct
{
char *fontlist; /* key */
FT_Library *library;
}
fontkey_t;
typedef struct
{
int pixel; /* key */
int bgcolor; /* key */
int fgcolor; /* key *//* -ve means no antialias */
gdImagePtr im; /* key */
int tweencolor;
}
tweencolor_t;
typedef struct
{
int pixel; /* key */
int bgcolor; /* key */
int fgcolor; /* key *//* -ve means no antialias */
gdImagePtr im; /* key */
}
tweencolorkey_t;
/********************************************************************
* gdTcl_UtfToUniChar is borrowed from Tcl ...
*/
/*
* tclUtf.c --
*
* Routines for manipulating UTF-8 strings.
*
* Copyright (c) 1997-1998 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* SCCS: @(#) tclUtf.c 1.25 98/01/28 18:02:43
*/
/*
*---------------------------------------------------------------------------
*
* gdTcl_UtfToUniChar --
*
* Extract the Tcl_UniChar represented by the UTF-8 string. Bad
* UTF-8 sequences are converted to valid Tcl_UniChars and processing
* continues. Equivalent to Plan 9 chartorune().
*
* The caller must ensure that the source buffer is long enough that
* this routine does not run off the end and dereference non-existent
* memory looking for trail bytes. If the source buffer is known to
* be '\0' terminated, this cannot happen. Otherwise, the caller
* should call Tcl_UtfCharComplete() before calling this routine to
* ensure that enough bytes remain in the string.
*
* Results:
* *chPtr is filled with the Tcl_UniChar, and the return value is the
* number of bytes from the UTF-8 string that were consumed.
*
* Side effects:
* None.
*
*---------------------------------------------------------------------------
*/
#ifdef JISX0208
#include "jisx0208.h"
#endif
#define Tcl_UniChar int
#define TCL_UTF_MAX 3
static int
gdTcl_UtfToUniChar (char *str, Tcl_UniChar * chPtr)
/* str is the UTF8 next character pointer */
/* chPtr is the int for the result */
{
int byte;
/* HTML4.0 entities in decimal form, e.g. &#197; */
byte = *((unsigned char *) str);
if (byte == '&')
{
int i, n = 0;
byte = *((unsigned char *) (str + 1));
if (byte == '#')
{
for (i = 2; i < 8; i++)
{
byte = *((unsigned char *) (str + i));
if (byte >= '0' && byte <= '9')
{
n = (n * 10) + (byte - '0');
}
else
break;
}
if (byte == ';')
{
*chPtr = (Tcl_UniChar) n;
return ++i;
}
}
}
/*
* Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones.
*/
byte = *((unsigned char *) str);
#ifdef JISX0208
if (0xA1 <= byte && byte <= 0xFE)
{
int ku, ten;
ku = (byte & 0x7F) - 0x20;
ten = (str[1] & 0x7F) - 0x20;
if ((ku < 1 || ku > 92) || (ten < 1 || ten > 94))
{
*chPtr = (Tcl_UniChar) byte;
return 1;
}
*chPtr = (Tcl_UniChar) UnicodeTbl[ku - 1][ten - 1];
return 2;
}
else
#endif /* JISX0208 */
if (byte < 0xC0)
{
/*
* Handles properly formed UTF-8 characters between
* 0x01 and 0x7F. Also treats \0 and naked trail
* bytes 0x80 to 0xBF as valid characters representing
* themselves.
*/
*chPtr = (Tcl_UniChar) byte;
return 1;
}
else if (byte < 0xE0)
{
if ((str[1] & 0xC0) == 0x80)
{
/*
* Two-byte-character lead-byte followed
* by a trail-byte.
*/
*chPtr = (Tcl_UniChar) (((byte & 0x1F) << 6)
| (str[1] & 0x3F));
return 2;
}
/*
* A two-byte-character lead-byte not followed by trail-byte
* represents itself.
*/
*chPtr = (Tcl_UniChar) byte;
return 1;
}
else if (byte < 0xF0)
{
if (((str[1] & 0xC0) == 0x80) && ((str[2] & 0xC0) == 0x80))
{
/*
* Three-byte-character lead byte followed by
* two trail bytes.
*/
*chPtr = (Tcl_UniChar) (((byte & 0x0F) << 12)
| ((str[1] & 0x3F) << 6) | (str[2] & 0x3F));
return 3;
}
/*
* A three-byte-character lead-byte not followed by
* two trail-bytes represents itself.
*/
*chPtr = (Tcl_UniChar) byte;
return 1;
}
#if TCL_UTF_MAX > 3
else
{
int ch, total, trail;
total = totalBytes[byte];
trail = total - 1;
if (trail > 0)
{
ch = byte & (0x3F >> trail);
do
{
str++;
if ((*str & 0xC0) != 0x80)
{
*chPtr = byte;
return 1;
}
ch <<= 6;
ch |= (*str & 0x3F);
trail--;
}
while (trail > 0);
*chPtr = ch;
return total;
}
}
#endif
*chPtr = (Tcl_UniChar) byte;
return 1;
}
/********************************************************************/
/* font cache functions */
static int
fontTest (void *element, void *key)
{
font_t *a = (font_t *) element;
fontkey_t *b = (fontkey_t *) key;
return (strcmp (a->fontlist, b->fontlist) == 0);
}
static void *
fontFetch (char **error, void *key)
{
font_t *a;
fontkey_t *b = (fontkey_t *) key;
int n;
int font_found = 0;
unsigned short platform, encoding;
char *fontsearchpath, *fontpath, *fontlist;
char *fullname = NULL;
char *name, *path, *dir;
char *strtok_ptr;
FT_Error err;
FT_CharMap found = 0;
FT_CharMap charmap;
a = (font_t *) gdMalloc (sizeof (font_t));
a->fontlist = strdup (b->fontlist);
a->library = b->library;
/*
* Search the pathlist for any of a list of font names.
*/
fontsearchpath = getenv ("GDFONTPATH");
if (!fontsearchpath)
fontsearchpath = DEFAULT_FONTPATH;
path = strdup (fontsearchpath);
fontlist = strdup (a->fontlist);
/*
* Must use gd_strtok_r else pointer corrupted by strtok in nested loop.
*/
for (name = gd_strtok_r (fontlist, LISTSEPARATOR, &strtok_ptr); name;
name = gd_strtok_r (0, LISTSEPARATOR, &strtok_ptr))
{
/*
* Allocate an oversized buffer that is guaranteed to be
* big enough for all paths to be tested.
*/
fullname = gdRealloc (fullname,
strlen (fontsearchpath) + strlen (name) + 6);
/* if name is an absolute filename then test directly */
if (*name == '/')
{
sprintf (fullname, "%s", name);
if (access (fullname, R_OK) == 0)
{
font_found++;
break;
}
}
for (dir = strtok (path, PATHSEPARATOR); dir;
dir = strtok (0, PATHSEPARATOR))
{
sprintf (fullname, "%s/%s.ttf", dir, name);
if (access (fullname, R_OK) == 0)
{
font_found++;
break;
}
}
if (font_found)
break;
}
gdFree (path);
gdFree (fontlist);
if (!font_found)
{
*error = "Could not find/open font";
return NULL;
}
err = FT_New_Face (*b->library, fullname, 0, &a->face);
if (err)
{
*error = "Could not read font";
return NULL;
}
gdFree (fullname);
/* FIXME - This mapping stuff is imcomplete - where is the spec? */
a->have_char_map_unicode = 0;
a->have_char_map_big5 = 0;
a->have_char_map_sjis = 0;
a->have_char_map_apple_roman = 0;
for (n = 0; n < a->face->num_charmaps; n++)
{
charmap = a->face->charmaps[n];
platform = charmap->platform_id;
encoding = charmap->encoding_id;
if ((platform == 3 && encoding == 1) /* Windows Unicode */
|| (platform == 3 && encoding == 0) /* Windows Symbol */
|| (platform == 2 && encoding == 1) /* ISO Unicode */
|| (platform == 0))
{ /* Apple Unicode */
a->have_char_map_unicode = 1;
found = charmap;
}
else if (platform == 3 && encoding == 4)
{ /* Windows Big5 */
a->have_char_map_big5 = 1;
found = charmap;
}
else if (platform == 3 && encoding == 2)
{ /* Windows Sjis */
a->have_char_map_sjis = 1;
found = charmap;
}
else if ((platform == 1 && encoding == 0) /* Apple Roman */
|| (platform == 2 && encoding == 0))
{ /* ISO ASCII */
a->have_char_map_apple_roman = 1;
found = charmap;
}
}
if (!found)
{
*error = "Unable to find a CharMap that I can handle";
return NULL;
}
return (void *) a;
}
static void
fontRelease (void *element)
{
font_t *a = (font_t *) element;
FT_Done_Face (a->face);
gdFree (a->fontlist);
gdFree ((char *) element);
}
/********************************************************************/
/* tweencolor cache functions */
static int
tweenColorTest (void *element, void *key)
{
tweencolor_t *a = (tweencolor_t *) element;
tweencolorkey_t *b = (tweencolorkey_t *) key;
return (a->pixel == b->pixel
&& a->bgcolor == b->bgcolor
&& a->fgcolor == b->fgcolor
&& a->im == b->im);
}
/*
* Computes a color in im's color table that is part way between
* the background and foreground colors proportional to the gray
* pixel value in the range 0-NUMCOLORS. The fg and bg colors must already
* be in the color table.
*/
static void *
tweenColorFetch (char **error, void *key)
{
tweencolor_t *a;
tweencolorkey_t *b = (tweencolorkey_t *) key;
int pixel, npixel, bg, fg;
gdImagePtr im;
a = (tweencolor_t *) gdMalloc (sizeof (tweencolor_t));
pixel = a->pixel = b->pixel;
bg = a->bgcolor = b->bgcolor;
fg = a->fgcolor = b->fgcolor;
im = b->im;
/* if fg is specified by a negative color idx, then don't antialias */
if (fg < 0)
{
a->tweencolor = -fg;
}
else
{
npixel = NUMCOLORS - pixel;
if (im->trueColor)
{
/* 2.0.1: use gdImageSetPixel to do the alpha blending work,
or to just store the alpha level. All we have to do here
is incorporate our knowledge of the percentage of this
pixel that is really "lit" by pushing the alpha value
up toward transparency in edge regions. */
a->tweencolor = gdTrueColorAlpha (
gdTrueColorGetRed (fg),
gdTrueColorGetGreen (fg),
gdTrueColorGetBlue (fg),
gdAlphaMax - (gdTrueColorGetAlpha (fg) * pixel / NUMCOLORS));
}
else
{
a->tweencolor = gdImageColorResolve (im,
(pixel * im->red[fg] + npixel * im->red[bg]) / NUMCOLORS,
(pixel * im->green[fg] + npixel * im->green[bg]) / NUMCOLORS,
(pixel * im->blue[fg] + npixel * im->blue[bg]) / NUMCOLORS);
}
}
return (void *) a;
}
static void
tweenColorRelease (void *element)
{
gdFree ((char *) element);
}
/* draw_bitmap - transfers glyph bitmap to GD image */
static char *
gdft_draw_bitmap (gdImage * im, int fg, FT_Bitmap bitmap, int pen_x, int pen_y)
{
unsigned char *pixel;
int *tpixel;
int x, y, row, col, pc;
tweencolor_t *tc_elem;
tweencolorkey_t tc_key;
/* initialize tweenColorCache on first call */
static gdCache_head_t *tc_cache;
if (!tc_cache)
{
tc_cache = gdCacheCreate (TWEENCOLORCACHESIZE,
tweenColorTest, tweenColorFetch, tweenColorRelease);
}
/* copy to image, mapping colors */
tc_key.fgcolor = fg;
tc_key.im = im;
for (row = 0; row < bitmap.rows; row++)
{
pc = row * bitmap.pitch;
y = pen_y + row;
/* clip if out of bounds */
if (y >= im->sy || y < 0)
continue;
for (col = 0; col < bitmap.width; col++, pc++)
{
if (bitmap.pixel_mode == ft_pixel_mode_grays)
{
/*
* Round to NUMCOLORS levels of antialiasing for
* index color images since only 256 colors are
* available.
*/
tc_key.pixel = ((bitmap.buffer[pc] * NUMCOLORS)
+ bitmap.num_grays / 2)
/ (bitmap.num_grays - 1);
}
else if (bitmap.pixel_mode == ft_pixel_mode_mono)
{
tc_key.pixel = ((bitmap.buffer[pc / 8]
<< (pc % 8)) & 128) ? NUMCOLORS : 0;
}
else
{
return "Unsupported ft_pixel_mode";
}
if (tc_key.pixel > 0)
{ /* if not background */
x = pen_x + col;
/* clip if out of bounds */
if (x >= im->sx || x < 0)
continue;
/* get pixel location in gd buffer */
if (im->trueColor)
{
tpixel = &im->tpixels[y][x];
}
else
{
pixel = &im->pixels[y][x];
}
if (tc_key.pixel == NUMCOLORS)
{
/* use fg color directly */
if (im->trueColor)
{
*tpixel = fg;
}
else
{
*pixel = fg;
}
}
else
{
/* find antialised color */
if (im->trueColor)
{
tc_key.bgcolor = *tpixel;
}
else
{
tc_key.bgcolor = *pixel;
}
tc_elem = (tweencolor_t *) gdCacheGet (
tc_cache, &tc_key);
if (im->trueColor)
{
*tpixel = tc_elem->tweencolor;
}
else
{
*pixel = tc_elem->tweencolor;
}
}
}
}
}
return (char *) NULL;
}
extern int any2eucjp (char *, char *, unsigned int);
/********************************************************************/
/* gdImageStringFT - render a utf8 string onto a gd image */
char *
gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist,
double ptsize, double angle, int x, int y, char *string)
{
FT_BBox bbox, glyph_bbox;
FT_Matrix matrix;
FT_Vector pen, delta, penf;
FT_Face face;
FT_Glyph image;
FT_GlyphSlot slot;
FT_Error err;
FT_Bool use_kerning;
FT_UInt glyph_index, previous;
double sin_a = sin (angle);
double cos_a = cos (angle);
int len, i = 0, ch;
int x1 = 0, y1 = 0;
font_t *font;
fontkey_t fontkey;
char *next;
char *tmpstr = 0;
int render = (im && (im->trueColor || (fg <= 255 && fg >= -255)));
FT_BitmapGlyph bm;
/***** initialize font library and font cache on first call ******/
static gdCache_head_t *fontCache;
static FT_Library library;
if (!fontCache)
{
if (FT_Init_FreeType (&library))
{
return "Failure to initialize font library";
}
fontCache = gdCacheCreate (FONTCACHESIZE,
fontTest, fontFetch, fontRelease);
}
/*****/
/* get the font (via font cache) */
fontkey.fontlist = fontlist;
fontkey.library = &library;
font = (font_t *) gdCacheGet (fontCache, &fontkey);
if (!font)
{
return fontCache->error;
}
face = font->face; /* shortcut */
slot = face->glyph; /* shortcut */
if (FT_Set_Char_Size (face, 0, (FT_F26Dot6) (ptsize * 64),
GD_RESOLUTION, GD_RESOLUTION))
{
return "Could not set character size";
}
matrix.xx = (FT_Fixed) (cos_a * (1 << 16));
matrix.yx = (FT_Fixed) (sin_a * (1 << 16));
matrix.xy = -matrix.yx;
matrix.yy = matrix.xx;
penf.x = penf.y = 0; /* running position of non-rotated string */
pen.x = pen.y = 0; /* running position of rotated string */
bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0;
use_kerning = FT_HAS_KERNING (face);
previous = 0;
#ifndef JISX0208
if (font->have_char_map_sjis)
{
#endif
if (tmpstr = (char *) gdMalloc (BUFSIZ))
{
any2eucjp (tmpstr, string, BUFSIZ);
next = tmpstr;
}
else
{
next = string;
}
#ifndef JISX0208
}
else
{
next = string;
}
#endif
while (*next)
{
ch = *next;
/* carriage returns */
if (ch == '\r')
{
penf.x = 0;
x1 = (penf.x * cos_a - penf.y * sin_a + 32) / 64;
y1 = (penf.x * sin_a + penf.y * cos_a + 32) / 64;
pen.x = pen.y = 0;
previous = 0; /* clear kerning flag */
next++;
continue;
}
/* newlines */
if (ch == '\n')
{
penf.y -= face->size->metrics.height * LINESPACE;
penf.y = (penf.y - 32) & -64; /* round to next pixel row */
x1 = (penf.x * cos_a - penf.y * sin_a + 32) / 64;
y1 = (penf.x * sin_a + penf.y * cos_a + 32) / 64;
pen.x = pen.y = 0;
previous = 0; /* clear kerning flag */
next++;
continue;
}
if (font->have_char_map_unicode)
{
/* use UTF-8 mapping from ASCII */
len = gdTcl_UtfToUniChar (next, &ch);
next += len;
}
else if (font->have_char_map_sjis)
{
unsigned char c;
int jiscode;
c = *next;
if (0xA1 <= c && c <= 0xFE)
{
next++;
jiscode = 0x100 * (c & 0x7F) + ((*next) & 0x7F);
ch = (jiscode >> 8) & 0xFF;
jiscode &= 0xFF;
if (ch & 1)
jiscode += 0x40 - 0x21;
else
jiscode += 0x9E - 0x21;
if (jiscode >= 0x7F)
jiscode++;
ch = (ch - 0x21) / 2 + 0x81;
if (ch >= 0xA0)
ch += 0x40;
ch = (ch << 8) + jiscode;
}
else
{
ch = c & 0xFF; /* don't extend sign */
}
next++;
}
else
{
/*
* Big 5 mapping:
* use "JIS-8 half-width katakana" coding from 8-bit characters. Ref:
* ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs
*/
ch = (*next) & 0xFF; /* don't extend sign */
next++;
if (ch >= 161 /* first code of JIS-8 pair */
&& *next)
{ /* don't advance past '\0' */
/* TBB: Fix from Kwok Wah On: & 255 needed */
ch = (ch * 256) + ((*next) & 255);
next++;
}
}
/* Convert character code to glyph index */
glyph_index = FT_Get_Char_Index (face, ch);
/* retrieve kerning distance and move pen position */
if (use_kerning && previous && glyph_index)
{
FT_Get_Kerning (face, previous, glyph_index,
ft_kerning_default, &delta);
pen.x += delta.x;
}
/* load glyph image into the slot (erase previous one) */
err = FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT);
if (err)
return "Problem loading glyph";
/* transform glyph image */
FT_Get_Glyph (slot, &image);
if (brect)
{ /* only if need brect */
FT_Glyph_Get_CBox (image, ft_glyph_bbox_gridfit, &glyph_bbox);
if (!i)
{ /* if first character, init BB corner values */
bbox.xMin = bbox.yMin = (1 << 30) - 1;
bbox.xMax = bbox.yMax = -bbox.xMin;
}
glyph_bbox.xMin += penf.x;
glyph_bbox.yMin += penf.y;
glyph_bbox.xMax += penf.x;
glyph_bbox.yMax += penf.y;
if (bbox.xMin > glyph_bbox.xMin)
bbox.xMin = glyph_bbox.xMin;
if (bbox.yMin > glyph_bbox.yMin)
bbox.yMin = glyph_bbox.yMin;
if (bbox.xMax < glyph_bbox.xMax)
bbox.xMax = glyph_bbox.xMax;
if (bbox.yMax < glyph_bbox.yMax)
bbox.yMax = glyph_bbox.yMax;
i++;
}
/* transform glyph image */
FT_Glyph_Transform (image, &matrix, 0);
if (render)
{
if (image->format != ft_glyph_format_bitmap)
{
err = FT_Glyph_To_Bitmap (&image, ft_render_mode_normal, 0, 1);
if (err)
return "Problem rendering glyph";
}
/* now, draw to our target surface */
bm = (FT_BitmapGlyph) image;
gdft_draw_bitmap (im, fg, bm->bitmap,
x + x1 + ((pen.x + 31) >> 6) + bm->left,
y - y1 + ((pen.y + 31) >> 6) - bm->top);
}
/* record current glyph index for kerning */
previous = glyph_index;
/* increment pen position */
pen.x += image->advance.x >> 10;
pen.y -= image->advance.y >> 10;
penf.x += slot->metrics.horiAdvance;
FT_Done_Glyph (image);
}
if (brect)
{ /* only if need brect */
/* For perfect rounding, must get sin(a + pi/4) and sin(a - pi/4). */
double d1 = sin (angle + 0.78539816339744830962);
double d2 = sin (angle - 0.78539816339744830962);
/* rotate bounding rectangle */
brect[0] = (int) (bbox.xMin * cos_a - bbox.yMin * sin_a);
brect[1] = (int) (bbox.xMin * sin_a + bbox.yMin * cos_a);
brect[2] = (int) (bbox.xMax * cos_a - bbox.yMin * sin_a);
brect[3] = (int) (bbox.xMax * sin_a + bbox.yMin * cos_a);
brect[4] = (int) (bbox.xMax * cos_a - bbox.yMax * sin_a);
brect[5] = (int) (bbox.xMax * sin_a + bbox.yMax * cos_a);
brect[6] = (int) (bbox.xMin * cos_a - bbox.yMax * sin_a);
brect[7] = (int) (bbox.xMin * sin_a + bbox.yMax * cos_a);
/* scale, round and offset brect */
brect[0] = x + gdroundupdown (brect[0], d2 > 0);
brect[1] = y - gdroundupdown (brect[1], d1 < 0);
brect[2] = x + gdroundupdown (brect[2], d1 > 0);
brect[3] = y - gdroundupdown (brect[3], d2 > 0);
brect[4] = x + gdroundupdown (brect[4], d2 < 0);
brect[5] = y - gdroundupdown (brect[5], d1 > 0);
brect[6] = x + gdroundupdown (brect[6], d1 < 0);
brect[7] = y - gdroundupdown (brect[7], d2 < 0);
}
if (tmpstr)
gdFree (tmpstr);
return (char *) NULL;
}
int
gdroundupdown (FT_F26Dot6 v1, int updown)
{
return (!updown)
? (v1 < 0 ? ((v1 - 63) >> 6) : v1 >> 6)
: (v1 > 0 ? ((v1 + 63) >> 6) : v1 >> 6);
}
#endif /* HAVE_LIBFREETYPE */

95
ext/gd/libgd/gdhelpers.c Normal file
View File

@ -0,0 +1,95 @@
#include "gd.h"
#include "gdhelpers.h"
#include <stdlib.h>
/* TBB: gd_strtok_r is not portable; provide an implementation */
#define SEP_TEST (separators[*((unsigned char *) s)])
char *
gd_strtok_r (char *s, char *sep, char **state)
{
char separators[256];
char *start;
char *result = 0;
memset (separators, 0, sizeof (separators));
while (*sep)
{
separators[*((unsigned char *) sep)] = 1;
sep++;
}
if (!s)
{
/* Pick up where we left off */
s = *state;
}
start = s;
/* 1. EOS */
if (!(*s))
{
*state = s;
return 0;
}
/* 2. Leading separators, if any */
if (SEP_TEST)
{
do
{
s++;
}
while (SEP_TEST);
/* 2a. EOS after separators only */
if (!(*s))
{
*state = s;
return 0;
}
}
/* 3. A token */
result = s;
do
{
/* 3a. Token at end of string */
if (!(*s))
{
*state = s;
return result;
}
s++;
}
while (!SEP_TEST);
/* 4. Terminate token and skip trailing separators */
*s = '\0';
do
{
s++;
}
while (SEP_TEST);
/* 5. Return token */
*state = s;
return result;
}
void *
gdCalloc (size_t nmemb, size_t size)
{
return calloc (nmemb, size);
}
void *
gdMalloc (size_t size)
{
return malloc (size);
}
void *
gdRealloc (void *ptr, size_t size)
{
return realloc (ptr, size);
}
void
gdFree (void *ptr)
{
free (ptr);
}

17
ext/gd/libgd/gdhelpers.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef GDHELPERS_H
#define GDHELPERS_H 1
/* TBB: strtok_r is not universal; provide an implementation of it. */
extern char *gd_strtok_r(char *s, char *sep, char **state);
/* These functions wrap memory management. gdFree is
in gd.h, where callers can utilize it to correctly
free memory allocated by these functions with the
right version of free(). */
void *gdCalloc(size_t nmemb, size_t size);
void *gdMalloc(size_t size);
void *gdRealloc(void *ptr, size_t size);
#endif /* GDHELPERS_H */

626
ext/gd/libgd/gdkanji.c Normal file
View File

@ -0,0 +1,626 @@
/* gdkanji.c (Kanji code converter) */
/* written by Masahito Yamaga (ma@yama-ga.com) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gd.h"
#include "gdhelpers.h"
#include <stdarg.h>
#if defined(HAVE_ICONV_H) || defined(HAVE_ICONV)
#include <iconv.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#endif
#if defined(HAVE_ICONV_H) && !defined(HAVE_ICONV)
#define HAVE_ICONV 1
#endif
#define LIBNAME "any2eucjp()"
#if defined(__MSC__) || defined(__BORLANDC__) || defined(__TURBOC__) || defined(_Windows) || defined(MSDOS)
#ifndef SJISPRE
#define SJISPRE 1
#endif
#endif
#ifdef TRUE
#undef TRUE
#endif
#ifdef FALSE
#undef FALSE
#endif
#define TRUE 1
#define FALSE 0
#define NEW 1
#define OLD 2
#define ESCI 3
#define NEC 4
#define EUC 5
#define SJIS 6
#define EUCORSJIS 7
#define ASCII 8
#define NEWJISSTR "JIS7"
#define OLDJISSTR "jis"
#define EUCSTR "eucJP"
#define SJISSTR "SJIS"
#define ESC 27
#define SS2 142
static void
debug (const char *format,...)
{
#ifdef DEBUG
va_list args;
va_start (args, format);
fprintf (stdout, "%s: ", LIBNAME);
vfprintf (stdout, format, args);
fprintf (stdout, "\n");
va_end (args);
#endif
}
static void
error (const char *format,...)
{
va_list args;
va_start (args, format);
fprintf (stderr, "%s: ", LIBNAME);
vfprintf (stderr, format, args);
fprintf (stderr, "\n");
va_end (args);
}
/* DetectKanjiCode() derived from DetectCodeType() by Ken Lunde. */
static int
DetectKanjiCode (unsigned char *str)
{
static int whatcode = ASCII;
int oldcode = ASCII;
int c, i;
char *lang = NULL;
c = '\1';
i = 0;
if (whatcode != EUCORSJIS && whatcode != ASCII)
{
oldcode = whatcode;
whatcode = ASCII;
}
while ((whatcode == EUCORSJIS || whatcode == ASCII) && c != '\0')
{
if ((c = str[i++]) != '\0')
{
if (c == ESC)
{
c = str[i++];
if (c == '$')
{
c = str[i++];
if (c == 'B')
whatcode = NEW;
else if (c == '@')
whatcode = OLD;
}
else if (c == '(')
{
c = str[i++];
if (c == 'I')
whatcode = ESCI;
}
else if (c == 'K')
whatcode = NEC;
}
else if ((c >= 129 && c <= 141) || (c >= 143 && c <= 159))
whatcode = SJIS;
else if (c == SS2)
{
c = str[i++];
if ((c >= 64 && c <= 126) || (c >= 128 && c <= 160) || (c >= 224 && c <= 252))
whatcode = SJIS;
else if (c >= 161 && c <= 223)
whatcode = EUCORSJIS;
}
else if (c >= 161 && c <= 223)
{
c = str[i++];
if (c >= 240 && c <= 254)
whatcode = EUC;
else if (c >= 161 && c <= 223)
whatcode = EUCORSJIS;
else if (c >= 224 && c <= 239)
{
whatcode = EUCORSJIS;
while (c >= 64 && c != '\0' && whatcode == EUCORSJIS)
{
if (c >= 129)
{
if (c <= 141 || (c >= 143 && c <= 159))
whatcode = SJIS;
else if (c >= 253 && c <= 254)
whatcode = EUC;
}
c = str[i++];
}
}
else if (c <= 159)
whatcode = SJIS;
}
else if (c >= 240 && c <= 254)
whatcode = EUC;
else if (c >= 224 && c <= 239)
{
c = str[i++];
if ((c >= 64 && c <= 126) || (c >= 128 && c <= 160))
whatcode = SJIS;
else if (c >= 253 && c <= 254)
whatcode = EUC;
else if (c >= 161 && c <= 252)
whatcode = EUCORSJIS;
}
}
}
#ifdef DEBUG
if (whatcode == ASCII)
debug ("Kanji code not included.");
else if (whatcode == EUCORSJIS)
debug ("Kanji code not detected.");
else
debug ("Kanji code detected at %d byte.", i);
#endif
if (whatcode == EUCORSJIS && oldcode != ASCII)
whatcode = oldcode;
if (whatcode == EUCORSJIS)
{
if (getenv ("LC_ALL"))
lang = getenv ("LC_ALL");
else if (getenv ("LC_CTYPE"))
lang = getenv ("LC_CTYPE");
else if (getenv ("LANG"))
lang = getenv ("LANG");
if (lang)
{
if (strcmp (lang, "ja_JP.SJIS") == 0 ||
#ifdef hpux
strcmp (lang, "japanese") == 0 ||
#endif
strcmp (lang, "ja_JP.mscode") == 0 ||
strcmp (lang, "ja_JP.PCK") == 0)
whatcode = SJIS;
else if (strncmp (lang, "ja", 2) == 0)
#ifdef SJISPRE
whatcode = SJIS;
#else
whatcode = EUC;
#endif
}
}
if (whatcode == EUCORSJIS)
#ifdef SJISPRE
whatcode = SJIS;
#else
whatcode = EUC;
#endif
return whatcode;
}
/* SJIStoJIS() is sjis2jis() by Ken Lunde. */
static void
SJIStoJIS (int *p1, int *p2)
{
register unsigned char c1 = *p1;
register unsigned char c2 = *p2;
register int adjust = c2 < 159;
register int rowOffset = c1 < 160 ? 112 : 176;
register int cellOffset = adjust ? (31 + (c2 > 127)) : 126;
*p1 = ((c1 - rowOffset) << 1) - adjust;
*p2 -= cellOffset;
}
/* han2zen() was derived from han2zen() written by Ken Lunde. */
#define IS_DAKU(c) ((c >= 182 && c <= 196) || (c >= 202 && c <= 206) || (c == 179))
#define IS_HANDAKU(c) (c >= 202 && c <= 206)
static void
han2zen (int *p1, int *p2)
{
int c = *p1;
int daku = FALSE;
int handaku = FALSE;
int mtable[][2] =
{
{129, 66},
{129, 117},
{129, 118},
{129, 65},
{129, 69},
{131, 146},
{131, 64},
{131, 66},
{131, 68},
{131, 70},
{131, 72},
{131, 131},
{131, 133},
{131, 135},
{131, 98},
{129, 91},
{131, 65},
{131, 67},
{131, 69},
{131, 71},
{131, 73},
{131, 74},
{131, 76},
{131, 78},
{131, 80},
{131, 82},
{131, 84},
{131, 86},
{131, 88},
{131, 90},
{131, 92},
{131, 94},
{131, 96},
{131, 99},
{131, 101},
{131, 103},
{131, 105},
{131, 106},
{131, 107},
{131, 108},
{131, 109},
{131, 110},
{131, 113},
{131, 116},
{131, 119},
{131, 122},
{131, 125},
{131, 126},
{131, 128},
{131, 129},
{131, 130},
{131, 132},
{131, 134},
{131, 136},
{131, 137},
{131, 138},
{131, 139},
{131, 140},
{131, 141},
{131, 143},
{131, 147},
{129, 74},
{129, 75}
};
if (*p2 == 222 && IS_DAKU (*p1))
daku = TRUE; /* Daku-ten */
else if (*p2 == 223 && IS_HANDAKU (*p1))
handaku = TRUE; /* Han-daku-ten */
*p1 = mtable[c - 161][0];
*p2 = mtable[c - 161][1];
if (daku)
{
if ((*p2 >= 74 && *p2 <= 103) || (*p2 >= 110 && *p2 <= 122))
(*p2)++;
else if (*p2 == 131 && *p2 == 69)
*p2 = 148;
}
else if (handaku && *p2 >= 110 && *p2 <= 122)
(*p2) += 2;
}
/* Recast strcpy to handle unsigned chars used below. */
#define ustrcpy(A,B) (strcpy((char*)(A),(const char*)(B)))
static void
do_convert (unsigned char *to, unsigned char *from, const char *code)
{
#ifdef HAVE_ICONV
iconv_t cd;
size_t from_len, to_len;
if ((cd = iconv_open (EUCSTR, code)) == (iconv_t) - 1)
{
error ("iconv_open() error");
#ifdef HAVE_ERRNO_H
if (errno == EINVAL)
error ("invalid code specification: \"%s\" or \"%s\"",
EUCSTR, code);
#endif
strcpy ((char *) to, (const char *) from);
return;
}
from_len = strlen ((const char *) from) + 1;
to_len = BUFSIZ;
if (iconv (cd, (const char **) &from, &from_len,
(char **) &to, &to_len) == -1)
{
#ifdef HAVE_ERRNO_H
if (errno == EINVAL)
error ("invalid end of input string");
else if (errno == EILSEQ)
error ("invalid code in input string");
else if (errno == E2BIG)
error ("output buffer overflow at do_convert()");
else
#endif
error ("something happen");
strcpy ((char *) to, (const char *) from);
return;
}
if (iconv_close (cd) != 0)
{
error ("iconv_close() error");
}
#else
int p1, p2, i, j;
int jisx0208 = FALSE;
int hankaku = FALSE;
j = 0;
if (strcmp (code, NEWJISSTR) == 0 || strcmp (code, OLDJISSTR) == 0)
{
for (i = 0; from[i] != '\0' && j < BUFSIZ; i++)
{
if (from[i] == ESC)
{
i++;
if (from[i] == '$')
{
jisx0208 = TRUE;
hankaku = FALSE;
i++;
}
else if (from[i] == '(')
{
jisx0208 = FALSE;
i++;
if (from[i] == 'I') /* Hankaku Kana */
hankaku = TRUE;
else
hankaku = FALSE;
}
}
else
{
if (jisx0208)
to[j++] = from[i] + 128;
else if (hankaku)
{
to[j++] = SS2;
to[j++] = from[i] + 128;
}
else
to[j++] = from[i];
}
}
}
else if (strcmp (code, SJISSTR) == 0)
{
for (i = 0; from[i] != '\0' && j < BUFSIZ; i++)
{
p1 = from[i];
if (p1 < 127)
to[j++] = p1;
else if ((p1 >= 161) && (p1 <= 223))
{ /* Hankaku Kana */
to[j++] = SS2;
to[j++] = p1;
}
else
{
p2 = from[++i];
SJIStoJIS (&p1, &p2);
to[j++] = p1 + 128;
to[j++] = p2 + 128;
}
}
}
else
{
error ("invalid code specification: \"%s\"", code);
return;
}
if (j >= BUFSIZ)
{
error ("output buffer overflow at do_convert()");
ustrcpy (to, from);
}
else
to[j] = '\0';
#endif /* HAVE_ICONV */
}
static int
do_check_and_conv (unsigned char *to, unsigned char *from)
{
static unsigned char tmp[BUFSIZ];
int p1, p2, i, j;
int kanji = TRUE;
switch (DetectKanjiCode (from))
{
case NEW:
debug ("Kanji code is New JIS.");
do_convert (tmp, from, NEWJISSTR);
break;
case OLD:
debug ("Kanji code is Old JIS.");
do_convert (tmp, from, OLDJISSTR);
break;
case ESCI:
debug ("This string includes Hankaku-Kana (jisx0201) escape sequence [ESC] + ( + I.");
do_convert (tmp, from, NEWJISSTR);
break;
case NEC:
debug ("Kanji code is NEC Kanji.");
error ("cannot convert NEC Kanji.");
ustrcpy (tmp, from);
kanji = FALSE;
break;
case EUC:
debug ("Kanji code is EUC.");
ustrcpy (tmp, from);
break;
case SJIS:
debug ("Kanji code is SJIS.");
do_convert (tmp, from, SJISSTR);
break;
case EUCORSJIS:
debug ("Kanji code is EUC or SJIS.");
ustrcpy (tmp, from);
kanji = FALSE;
break;
case ASCII:
debug ("This is ASCII string.");
ustrcpy (tmp, from);
kanji = FALSE;
break;
default:
debug ("This string includes unknown code.");
ustrcpy (tmp, from);
kanji = FALSE;
break;
}
/* Hankaku Kana ---> Zenkaku Kana */
if (kanji)
{
j = 0;
for (i = 0; tmp[i] != '\0' && j < BUFSIZ; i++)
{
if (tmp[i] == SS2)
{
p1 = tmp[++i];
if (tmp[i + 1] == SS2)
{
p2 = tmp[i + 2];
if (p2 == 222 || p2 == 223)
i += 2;
else
p2 = 0;
}
else
p2 = 0;
han2zen (&p1, &p2);
SJIStoJIS (&p1, &p2);
to[j++] = p1 + 128;
to[j++] = p2 + 128;
}
else
to[j++] = tmp[i];
}
if (j >= BUFSIZ)
{
error ("output buffer overflow at Hankaku --> Zenkaku");
ustrcpy (to, tmp);
}
else
to[j] = '\0';
}
else
ustrcpy (to, tmp);
return kanji;
}
int
any2eucjp (unsigned char *dest, unsigned char *src, unsigned int dest_max)
{
static unsigned char tmp_dest[BUFSIZ];
int ret;
if (strlen ((const char *) src) >= BUFSIZ)
{
error ("input string too large");
return -1;
}
if (dest_max > BUFSIZ)
{
error ("invalid maximum size of destination\nit should be less than %d.", BUFSIZ);
return -1;
}
ret = do_check_and_conv (tmp_dest, src);
if (strlen ((const char *) tmp_dest) >= dest_max)
{
error ("output buffer overflow");
ustrcpy (dest, src);
return -1;
}
ustrcpy (dest, tmp_dest);
return ret;
}
#if 0
unsigned int
strwidth (unsigned char *s)
{
unsigned char *t;
unsigned int i;
t = (unsigned char *) gdMalloc (BUFSIZ);
any2eucjp (t, s, BUFSIZ);
i = strlen (t);
gdFree (t);
return i;
}
#ifdef DEBUG
int
main ()
{
unsigned char input[BUFSIZ];
unsigned char *output;
unsigned char *str;
int c, i = 0;
while ((c = fgetc (stdin)) != '\n' && i < BUFSIZ)
input[i++] = c;
input[i] = '\0';
printf ("input : %d bytes\n", strlen ((const char *) input));
printf ("output: %d bytes\n", strwidth (input));
output = (unsigned char *) gdMalloc (BUFSIZ);
any2eucjp (output, input, BUFSIZ);
str = output;
while (*str != '\0')
putchar (*(str++));
putchar ('\n');
gdFree (output);
return 0;
}
#endif
#endif

View File

@ -0,0 +1,55 @@
#include <stdio.h>
#include <stdlib.h> /* For atoi */
#include "gd.h"
/* A short program which converts a .png 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: gdparttopng filename.gd filename.png 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 PNG format!\n");
exit (1);
}
out = fopen (argv[2], "wb");
if (!out)
{
fprintf (stderr, "Output file cannot be written to!\n");
gdImageDestroy (im);
exit (1);
}
gdImagePng (im, out);
fclose (out);
gdImageDestroy (im);
return 0;
}

728
ext/gd/libgd/gdtables.c Normal file
View File

@ -0,0 +1,728 @@
int gdCosT[] =
{
1024,
1023,
1023,
1022,
1021,
1020,
1018,
1016,
1014,
1011,
1008,
1005,
1001,
997,
993,
989,
984,
979,
973,
968,
962,
955,
949,
942,
935,
928,
920,
912,
904,
895,
886,
877,
868,
858,
848,
838,
828,
817,
806,
795,
784,
772,
760,
748,
736,
724,
711,
698,
685,
671,
658,
644,
630,
616,
601,
587,
572,
557,
542,
527,
512,
496,
480,
464,
448,
432,
416,
400,
383,
366,
350,
333,
316,
299,
282,
265,
247,
230,
212,
195,
177,
160,
142,
124,
107,
89,
71,
53,
35,
17,
0,
-17,
-35,
-53,
-71,
-89,
-107,
-124,
-142,
-160,
-177,
-195,
-212,
-230,
-247,
-265,
-282,
-299,
-316,
-333,
-350,
-366,
-383,
-400,
-416,
-432,
-448,
-464,
-480,
-496,
-512,
-527,
-542,
-557,
-572,
-587,
-601,
-616,
-630,
-644,
-658,
-671,
-685,
-698,
-711,
-724,
-736,
-748,
-760,
-772,
-784,
-795,
-806,
-817,
-828,
-838,
-848,
-858,
-868,
-877,
-886,
-895,
-904,
-912,
-920,
-928,
-935,
-942,
-949,
-955,
-962,
-968,
-973,
-979,
-984,
-989,
-993,
-997,
-1001,
-1005,
-1008,
-1011,
-1014,
-1016,
-1018,
-1020,
-1021,
-1022,
-1023,
-1023,
-1024,
-1023,
-1023,
-1022,
-1021,
-1020,
-1018,
-1016,
-1014,
-1011,
-1008,
-1005,
-1001,
-997,
-993,
-989,
-984,
-979,
-973,
-968,
-962,
-955,
-949,
-942,
-935,
-928,
-920,
-912,
-904,
-895,
-886,
-877,
-868,
-858,
-848,
-838,
-828,
-817,
-806,
-795,
-784,
-772,
-760,
-748,
-736,
-724,
-711,
-698,
-685,
-671,
-658,
-644,
-630,
-616,
-601,
-587,
-572,
-557,
-542,
-527,
-512,
-496,
-480,
-464,
-448,
-432,
-416,
-400,
-383,
-366,
-350,
-333,
-316,
-299,
-282,
-265,
-247,
-230,
-212,
-195,
-177,
-160,
-142,
-124,
-107,
-89,
-71,
-53,
-35,
-17,
0,
17,
35,
53,
71,
89,
107,
124,
142,
160,
177,
195,
212,
230,
247,
265,
282,
299,
316,
333,
350,
366,
383,
400,
416,
432,
448,
464,
480,
496,
512,
527,
542,
557,
572,
587,
601,
616,
630,
644,
658,
671,
685,
698,
711,
724,
736,
748,
760,
772,
784,
795,
806,
817,
828,
838,
848,
858,
868,
877,
886,
895,
904,
912,
920,
928,
935,
942,
949,
955,
962,
968,
973,
979,
984,
989,
993,
997,
1001,
1005,
1008,
1011,
1014,
1016,
1018,
1020,
1021,
1022,
1023,
1023
};
int gdSinT[] =
{
0,
17,
35,
53,
71,
89,
107,
124,
142,
160,
177,
195,
212,
230,
247,
265,
282,
299,
316,
333,
350,
366,
383,
400,
416,
432,
448,
464,
480,
496,
512,
527,
542,
557,
572,
587,
601,
616,
630,
644,
658,
671,
685,
698,
711,
724,
736,
748,
760,
772,
784,
795,
806,
817,
828,
838,
848,
858,
868,
877,
886,
895,
904,
912,
920,
928,
935,
942,
949,
955,
962,
968,
973,
979,
984,
989,
993,
997,
1001,
1005,
1008,
1011,
1014,
1016,
1018,
1020,
1021,
1022,
1023,
1023,
1024,
1023,
1023,
1022,
1021,
1020,
1018,
1016,
1014,
1011,
1008,
1005,
1001,
997,
993,
989,
984,
979,
973,
968,
962,
955,
949,
942,
935,
928,
920,
912,
904,
895,
886,
877,
868,
858,
848,
838,
828,
817,
806,
795,
784,
772,
760,
748,
736,
724,
711,
698,
685,
671,
658,
644,
630,
616,
601,
587,
572,
557,
542,
527,
512,
496,
480,
464,
448,
432,
416,
400,
383,
366,
350,
333,
316,
299,
282,
265,
247,
230,
212,
195,
177,
160,
142,
124,
107,
89,
71,
53,
35,
17,
0,
-17,
-35,
-53,
-71,
-89,
-107,
-124,
-142,
-160,
-177,
-195,
-212,
-230,
-247,
-265,
-282,
-299,
-316,
-333,
-350,
-366,
-383,
-400,
-416,
-432,
-448,
-464,
-480,
-496,
-512,
-527,
-542,
-557,
-572,
-587,
-601,
-616,
-630,
-644,
-658,
-671,
-685,
-698,
-711,
-724,
-736,
-748,
-760,
-772,
-784,
-795,
-806,
-817,
-828,
-838,
-848,
-858,
-868,
-877,
-886,
-895,
-904,
-912,
-920,
-928,
-935,
-942,
-949,
-955,
-962,
-968,
-973,
-979,
-984,
-989,
-993,
-997,
-1001,
-1005,
-1008,
-1011,
-1014,
-1016,
-1018,
-1020,
-1021,
-1022,
-1023,
-1023,
-1024,
-1023,
-1023,
-1022,
-1021,
-1020,
-1018,
-1016,
-1014,
-1011,
-1008,
-1005,
-1001,
-997,
-993,
-989,
-984,
-979,
-973,
-968,
-962,
-955,
-949,
-942,
-935,
-928,
-920,
-912,
-904,
-895,
-886,
-877,
-868,
-858,
-848,
-838,
-828,
-817,
-806,
-795,
-784,
-772,
-760,
-748,
-736,
-724,
-711,
-698,
-685,
-671,
-658,
-644,
-630,
-616,
-601,
-587,
-572,
-557,
-542,
-527,
-512,
-496,
-480,
-464,
-448,
-432,
-416,
-400,
-383,
-366,
-350,
-333,
-316,
-299,
-282,
-265,
-247,
-230,
-212,
-195,
-177,
-160,
-142,
-124,
-107,
-89,
-71,
-53,
-35,
-17
};

409
ext/gd/libgd/gdtest.c Normal file
View File

@ -0,0 +1,409 @@
#include <stdio.h>
#ifdef _WIN32
#include <process.h>
int
unlink (const char *filename)
{
return _unlink (filename);
}
#else
#include <unistd.h> /* for getpid(), unlink() */
#endif
#include "gd.h"
void CompareImages (char *msg, gdImagePtr im1, gdImagePtr im2);
static int freadWrapper (void *context, char *buf, int len);
static int fwriteWrapper (void *context, const char *buffer, int len);
int
main (int argc, char **argv)
{
gdImagePtr im, ref, im2, im3;
FILE *in, *out;
void *iptr;
int sz;
gdIOCtxPtr ctx;
char of[256];
int colRed, colBlu;
gdSource imgsrc;
gdSink imgsnk;
int foreground;
int i;
if (argc != 2)
{
fprintf (stderr, "Usage: gdtest filename.png\n");
exit (1);
}
in = fopen (argv[1], "rb");
if (!in)
{
fprintf (stderr, "Input file does not exist!\n");
exit (1);
}
im = gdImageCreateFromPng (in);
rewind (in);
ref = gdImageCreateFromPng (in);
fclose (in);
printf ("Reference File has %d Palette entries\n", ref->colorsTotal);
CompareImages ("Initial Versions", ref, im);
/* */
/* Send to PNG File then Ptr */
/* */
sprintf (of, "%s.png", argv[1]);
out = fopen (of, "wb");
gdImagePng (im, out);
fclose (out);
in = fopen (of, "rb");
if (!in)
{
fprintf (stderr, "PNG Output file does not exist!\n");
exit (1);
}
im2 = gdImageCreateFromPng (in);
fclose (in);
CompareImages ("GD->PNG File->GD", ref, im2);
unlink (of);
gdImageDestroy (im2);
iptr = gdImagePngPtr (im, &sz);
ctx = gdNewDynamicCtx (sz, iptr);
im2 = gdImageCreateFromPngCtx (ctx);
CompareImages ("GD->PNG 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);
/*
** Test gdImageCreateFromPngSource'
* */
in = fopen (argv[1], "rb");
imgsrc.source = freadWrapper;
imgsrc.context = in;
im2 = gdImageCreateFromPngSource (&imgsrc);
fclose (in);
if (im2 == NULL)
{
printf ("GD Source: ERROR Null returned by gdImageCreateFromPngSource\n");
}
else
{
CompareImages ("GD Source", ref, im2);
gdImageDestroy (im2);
};
/*
** Test gdImagePngToSink'
* */
sprintf (of, "%s.snk", argv[1]);
out = fopen (of, "wb");
imgsnk.sink = fwriteWrapper;
imgsnk.context = out;
gdImagePngToSink (im, &imgsnk);
fclose (out);
in = fopen (of, "rb");
if (!in)
{
fprintf (stderr, "GD Sink: ERROR - GD Sink Output file does not exist!\n");
}
else
{
im2 = gdImageCreateFromPng (in);
fclose (in);
CompareImages ("GD Sink", ref, im2);
gdImageDestroy (im2);
};
unlink (of);
/* */
/* Test Extraction */
/* */
in = fopen ("test/gdtest_200_300_150_100.png", "rb");
if (!in)
{
fprintf (stderr, "gdtest_200_300_150_100.png does not exist!\n");
exit (1);
}
im2 = gdImageCreateFromPng (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_200_300_150_100.png, gdtest.gd2(part))", im2, im3);
gdImageDestroy (im2);
gdImageDestroy (im3);
/* */
/* Copy Blend */
/* */
in = fopen ("test/gdtest.png", "rb");
if (!in)
{
fprintf (stderr, "gdtest.png does not exist!\n");
exit (1);
}
im2 = gdImageCreateFromPng (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.png", "rb");
if (!in)
{
fprintf (stderr, "gdtest_merge.png does not exist!\n");
exit (1);
}
im3 = gdImageCreateFromPng (in);
fclose (in);
printf ("[Merged Image has %d colours]\n", im2->colorsTotal);
CompareImages ("Merged (gdtest.png, gdtest_merge.png)", im2, im3);
gdImageDestroy (im2);
gdImageDestroy (im3);
#ifdef HAVE_JPEG
out = fopen ("test/gdtest.jpg", "wb");
if (!out)
{
fprintf (stderr, "Can't create file test/gdtest.jpg.\n");
exit (1);
}
gdImageJpeg (im, out, -1);
fclose (out);
in = fopen ("test/gdtest.jpg", "rb");
if (!in)
{
fprintf (stderr, "Can't open file test/gdtest.jpg.\n");
exit (1);
}
im2 = gdImageCreateFromJpeg (in);
fclose (in);
if (!im2)
{
fprintf (stderr, "gdImageCreateFromJpeg failed.\n");
exit (1);
}
gdImageDestroy (im2);
printf ("Created test/gdtest.jpg successfully. Compare this image\n"
"to the input image manually. Some difference must be\n"
"expected as JPEG is a lossy file format.\n");
#endif /* HAVE_JPEG */
/* Assume the color closest to black is the foreground
color for the B&W wbmp image. */
fprintf (stderr, "NOTE: the WBMP output image will NOT match the original unless the original\n"
"is also black and white. This is OK!\n");
foreground = gdImageColorClosest (im, 0, 0, 0);
fprintf (stderr, "Foreground index is %d\n", foreground);
if (foreground == -1)
{
fprintf (stderr, "Source image has no colors, skipping wbmp test.\n");
}
else
{
out = fopen ("test/gdtest.wbmp", "wb");
if (!out)
{
fprintf (stderr, "Can't create file test/gdtest.wbmp.\n");
exit (1);
}
gdImageWBMP (im, foreground, out);
fclose (out);
in = fopen ("test/gdtest.wbmp", "rb");
if (!in)
{
fprintf (stderr, "Can't open file test/gdtest.wbmp.\n");
exit (1);
}
im2 = gdImageCreateFromWBMP (in);
fprintf (stderr, "WBMP has %d colors\n", gdImageColorsTotal (im2));
fprintf (stderr, "WBMP colors are:\n");
for (i = 0; (i < gdImageColorsTotal (im2)); i++)
{
fprintf (stderr, "%02X%02X%02X\n",
gdImageRed (im2, i),
gdImageGreen (im2, i),
gdImageBlue (im2, i));
}
fclose (in);
if (!im2)
{
fprintf (stderr, "gdImageCreateFromWBMP failed.\n");
exit (1);
}
CompareImages ("WBMP test (gdtest.png, gdtest.wbmp)", ref, im2);
out = fopen ("test/gdtest_wbmp_to_png.png", "wb");
if (!out)
{
fprintf (stderr, "Can't create file test/gdtest_wbmp_to_png.png.\n");
exit (1);
}
gdImagePng (im2, out);
fclose (out);
gdImageDestroy (im2);
}
gdImageDestroy (im);
gdImageDestroy (ref);
return 0;
}
void
CompareImages (char *msg, gdImagePtr im1, gdImagePtr im2)
{
int cmpRes;
cmpRes = gdImageCompare (im1, im2);
if (cmpRes & GD_CMP_IMAGE)
{
printf ("%%%s: ERROR images differ: BAD\n", msg);
}
else if (cmpRes != 0)
{
printf ("%%%s: WARNING images differ: WARNING - Probably OK\n", msg);
}
else
{
printf ("%%%s: OK\n", msg);
return;
}
if (cmpRes & (GD_CMP_SIZE_X + GD_CMP_SIZE_Y))
{
printf ("-%s: INFO image sizes differ\n", msg);
}
if (cmpRes & GD_CMP_NUM_COLORS)
{
printf ("-%s: INFO number of pallette entries differ %d Vs. %d\n", msg,
im1->colorsTotal, im2->colorsTotal);
}
if (cmpRes & GD_CMP_COLOR)
{
printf ("-%s: INFO actual colours of pixels differ\n", msg);
}
}
static int
freadWrapper (void *context, char *buf, int len)
{
int got = fread (buf, 1, len, (FILE *) context);
return got;
}
static int
fwriteWrapper (void *context, const char *buffer, int len)
{
return fwrite (buffer, 1, len, (FILE *) context);
}

108
ext/gd/libgd/gdtestft.c Normal file
View File

@ -0,0 +1,108 @@
#include "gd.h"
#include <string.h>
#define PI 3.141592
#define DEG2RAD(x) ((x)*PI/180.)
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX4(x,y,z,w) \
((MAX((x),(y))) > (MAX((z),(w))) ? (MAX((x),(y))) : (MAX((z),(w))))
#define MIN4(x,y,z,w) \
((MIN((x),(y))) < (MIN((z),(w))) ? (MIN((x),(y))) : (MIN((z),(w))))
#define MAXX(x) MAX4(x[0],x[2],x[4],x[6])
#define MINX(x) MIN4(x[0],x[2],x[4],x[6])
#define MAXY(x) MAX4(x[1],x[3],x[5],x[7])
#define MINY(x) MIN4(x[1],x[3],x[5],x[7])
int
main (int argc, char *argv[])
{
#ifndef HAVE_LIBFREETYPE
fprintf (stderr, "gd was not compiled with HAVE_LIBFREETYPE defined.\n");
fprintf (stderr, "Install the FreeType library, including the\n");
fprintf (stderr, "header files. Then edit the gd Makefile, type\n");
fprintf (stderr, "make clean, and type make again.\n");
return 1;
#else
gdImagePtr im;
int black;
int white;
int brect[8];
int x, y;
char *err;
FILE *out;
#ifdef JISX0208
char *s = "Hello. ‚±‚ñ‚É‚¿‚Í Qyjpqg,"; /* String to draw. */
#else
char *s = "Hello. Qyjpqg,"; /* String to draw. */
#endif
double sz = 40.;
#if 0
double angle = 0.;
#else
double angle = DEG2RAD (-90);
#endif
#ifdef JISX0208
char *f = "/usr/openwin/lib/locale/ja/X11/fonts/TT/HG-MinchoL.ttf"; /* UNICODE */
/* char *f = "/usr/local/lib/fonts/truetype/DynaFont/dfpop1.ttf"; *//* SJIS */
#else
char *f = "times"; /* TrueType font */
#endif
/* obtain brect so that we can size the image */
err = gdImageStringFT ((gdImagePtr) NULL, &brect[0], 0, f, sz, angle, 0, 0, s);
if (err)
{
fprintf (stderr, err);
return 1;
}
/* create an image just big enough for the string */
x = MAXX (brect) - MINX (brect) + 6;
y = MAXY (brect) - MINY (brect) + 6;
#if 0
im = gdImageCreate (500, 500);
#else
/* gd 2.0: true color images can use freetype too */
im = gdImageCreateTrueColor (x, y);
#endif
/* Background color. gd 2.0: fill the image with it; truecolor
images have a black background otherwise. */
white = gdImageColorResolve (im, 255, 255, 255);
gdImageFilledRectangle (im, 0, 0, x, y, white);
black = gdImageColorResolve (im, 0, 0, 0);
/* render the string, offset origin to center string */
x = 0 - MINX (brect) + 3;
y = 0 - MINY (brect) + 3;
err = gdImageStringFT (im, NULL, black, f, sz, angle, x, y, s);
if (err)
{
fprintf (stderr, err);
return 1;
}
/* TBB: Write img to test/fttest.png */
out = fopen ("test/fttest.png", "wb");
if (!out)
{
fprintf (stderr, "Can't create test/fttest.png\n");
exit (1);
}
gdImagePng (im, out);
fclose (out);
fprintf (stderr, "Test image written to test/fttest.png\n");
/* Destroy it */
gdImageDestroy (im);
return 0;
#endif /* HAVE_FREETYPE */
}

44
ext/gd/libgd/gdtopng.c Normal file
View File

@ -0,0 +1,44 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .png 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: gdtopng filename.gd filename.png\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 PNG format!\n");
exit (1);
}
out = fopen (argv[2], "wb");
if (!out)
{
fprintf (stderr, "Output file cannot be written to!\n");
gdImageDestroy (im);
exit (1);
}
gdImagePng (im, out);
fclose (out);
gdImageDestroy (im);
return 0;
}

148
ext/gd/libgd/gdxpm.c Normal file
View File

@ -0,0 +1,148 @@
/*
add ability to load xpm files to gd, requires the xpm
library.
Caolan.McNamara@ul.ie
http://www.csn.ul.ie/~caolan
*/
#include <stdio.h>
#include <stdlib.h>
#include "gd.h"
#include "gdhelpers.h"
#ifndef HAVE_XPM
gdImagePtr
gdImageCreateFromXpm (char *filename)
{
fprintf (stderr, "libgd was not built with xpm support\n");
return (NULL);
}
#else
#include "xpm.h"
gdImagePtr
gdImageCreateFromXpm (char *filename)
{
XpmInfo info;
XpmImage image;
int i, j, k, number;
char buf[5];
gdImagePtr im = 0;
char *apixel;
int *pointer;
int red = 0, green = 0, blue = 0, color = 0;
int *colors;
int ret;
ret = XpmReadFileToXpmImage (filename, &image, &info);
if (ret != XpmSuccess)
return 0;
if (!(im = gdImageCreate (image.width, image.height)))
return 0;
number = image.ncolors;
colors = (int *) gdMalloc (sizeof (int) * number);
if (colors == NULL)
return (0);
for (i = 0; i < number; i++)
{
switch (strlen (image.colorTable[i].c_color))
{
case 4:
buf[1] = '\0';
buf[0] = image.colorTable[i].c_color[1];
red = strtol (buf, NULL, 16);
buf[0] = image.colorTable[i].c_color[3];
green = strtol (buf, NULL, 16);
buf[0] = image.colorTable[i].c_color[5];
blue = strtol (buf, NULL, 16);
break;
case 7:
buf[2] = '\0';
buf[0] = image.colorTable[i].c_color[1];
buf[1] = image.colorTable[i].c_color[2];
red = strtol (buf, NULL, 16);
buf[0] = image.colorTable[i].c_color[3];
buf[1] = image.colorTable[i].c_color[4];
green = strtol (buf, NULL, 16);
buf[0] = image.colorTable[i].c_color[5];
buf[1] = image.colorTable[i].c_color[6];
blue = strtol (buf, NULL, 16);
break;
case 10:
buf[3] = '\0';
buf[0] = image.colorTable[i].c_color[1];
buf[1] = image.colorTable[i].c_color[2];
buf[2] = image.colorTable[i].c_color[3];
red = strtol (buf, NULL, 16);
red /= 64;
buf[0] = image.colorTable[i].c_color[4];
buf[1] = image.colorTable[i].c_color[5];
buf[2] = image.colorTable[i].c_color[6];
green = strtol (buf, NULL, 16);
green /= 64;
buf[0] = image.colorTable[i].c_color[7];
buf[1] = image.colorTable[i].c_color[8];
buf[2] = image.colorTable[i].c_color[9];
blue = strtol (buf, NULL, 16);
blue /= 64;
break;
case 13:
buf[4] = '\0';
buf[0] = image.colorTable[i].c_color[1];
buf[1] = image.colorTable[i].c_color[2];
buf[2] = image.colorTable[i].c_color[3];
buf[3] = image.colorTable[i].c_color[4];
red = strtol (buf, NULL, 16);
red /= 256;
buf[0] = image.colorTable[i].c_color[5];
buf[1] = image.colorTable[i].c_color[6];
buf[2] = image.colorTable[i].c_color[7];
buf[3] = image.colorTable[i].c_color[8];
green = strtol (buf, NULL, 16);
green /= 256;
buf[0] = image.colorTable[i].c_color[9];
buf[1] = image.colorTable[i].c_color[10];
buf[2] = image.colorTable[i].c_color[11];
buf[3] = image.colorTable[i].c_color[12];
blue = strtol (buf, NULL, 16);
blue /= 256;
break;
}
colors[i] = gdImageColorResolve (im, red, green, blue);
if (colors[i] == -1)
fprintf (stderr, "ARRRGH\n");
}
apixel = (char *) gdMalloc (image.cpp + 1);
if (apixel == NULL)
return (0);
apixel[image.cpp] = '\0';
pointer = image.data;
for (i = 0; i < image.height; i++)
{
for (j = 0; j < image.width; j++)
{
k = *pointer++;
gdImageSetPixel (im, j, i, colors[k]);
}
}
gdFree (apixel);
gdFree (colors);
return (im);
}
#endif

1205
ext/gd/libgd/jisx0208.h Normal file

File diff suppressed because it is too large Load Diff

52
ext/gd/libgd/mathmake.c Normal file
View File

@ -0,0 +1,52 @@
#include <stdio.h>
#include <math.h>
#define scale 1024
int basis[91];
int cost[360];
main (void)
{
int i;
printf ("#define costScale %d\n", scale);
printf ("int cost[] = {\n ");
for (i = 0; (i <= 90); i++)
{
basis[i] = cos ((double) i * .0174532925) * scale;
}
for (i = 0; (i < 90); i++)
{
printf ("%d,\n ", cost[i] = basis[i]);
}
for (i = 90; (i < 180); i++)
{
printf ("%d,\n ", cost[i] = -basis[180 - i]);
}
for (i = 180; (i < 270); i++)
{
printf ("%d,\n ", cost[i] = -basis[i - 180]);
}
for (i = 270; (i < 359); i++)
{
printf ("%d,\n ", cost[i] = basis[360 - i]);
}
printf ("%d\n", cost[359] = basis[1]);
printf ("};\n");
printf ("#define sintScale %d\n", scale);
printf ("int sint[] = {\n ");
for (i = 0; (i < 360); i++)
{
int val;
val = cost[(i + 270) % 360];
if (i != 359)
{
printf ("%d,\n ", val);
}
else
{
printf ("%d\n", val);
}
}
printf ("};\n");
}

45
ext/gd/libgd/pngtogd.c Normal file
View File

@ -0,0 +1,45 @@
#include <stdio.h>
#include "gd.h"
/* A short program which converts a .png 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: pngtogd filename.png filename.gd\n");
exit (1);
}
in = fopen (argv[1], "rb");
if (!in)
{
fprintf (stderr, "Input file does not exist!\n");
exit (1);
}
im = gdImageCreateFromPng (in);
fclose (in);
if (!im)
{
fprintf (stderr, "Input is not in PNG format!\n");
exit (1);
}
out = fopen (argv[2], "wb");
if (!out)
{
fprintf (stderr, "Output file cannot be written to!\n");
gdImageDestroy (im);
exit (1);
}
gdImageGd (im, out);
fclose (out);
gdImageDestroy (im);
return 0;
}

52
ext/gd/libgd/pngtogd2.c Normal file
View File

@ -0,0 +1,52 @@
#include <stdio.h>
#include <stdlib.h>
#include "gd.h"
/* A short program which converts a .png 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: pngtogd2 filename.png 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 = gdImageCreateFromPng (in);
fclose (in);
if (!im)
{
fprintf (stderr, "Input is not in PNG 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);
return 0;
}

129
ext/gd/libgd/testac.c Normal file
View File

@ -0,0 +1,129 @@
#include <stdio.h>
#include "gd.h"
/* If palette is true, we convert from truecolor to palette at the end,
to test gdImageTrueColorToPalette and see file size/
quality tradeoffs. */
void testDrawing (
gdImagePtr im_in,
double scale,
int blending,
int palette,
char *filename);
int
main (int argc, char *argv[])
{
/* Input and output files */
FILE *in;
FILE *out;
/* Input image */
gdImagePtr im_in = 0;
/* Colors */
int lightBlue;
if (argc != 2)
{
fprintf (stderr, "Usage: testac filename.png\n");
exit (1);
}
/* Load original PNG, which should contain alpha channel
information. We will use it in two ways: preserving it
literally, for use with compatible browsers, and
compositing it ourselves against a background of our
choosing (alpha blending). We'll change its size
and try creating palette versions of it. */
in = fopen (argv[1], "rb");
if (!in)
{
fprintf (stderr, "Can't load %s.\n", argv[1]);
exit (1);
}
else
{
im_in = gdImageCreateFromPng (in);
fclose (in);
}
testDrawing (im_in, 1.0, 0, 0, "noblending-fullsize-truecolor.png");
testDrawing (im_in, 1.0, 1, 0, "blending-fullsize-truecolor.png");
testDrawing (im_in, 0.5, 0, 0, "noblending-halfsize-truecolor.png");
testDrawing (im_in, 0.5, 1, 0, "blending-halfsize-truecolor.png");
testDrawing (im_in, 2.0, 0, 0, "noblending-doublesize-truecolor.png");
testDrawing (im_in, 2.0, 1, 0, "blending-doublesize-truecolor.png");
testDrawing (im_in, 1.0, 0, 1, "noblending-fullsize-palette.png");
testDrawing (im_in, 1.0, 1, 1, "blending-fullsize-palette.png");
testDrawing (im_in, 0.5, 0, 1, "noblending-halfsize-palette.png");
testDrawing (im_in, 0.5, 1, 1, "blending-halfsize-palette.png");
testDrawing (im_in, 2.0, 0, 1, "noblending-doublesize-palette.png");
testDrawing (im_in, 2.0, 1, 1, "blending-doublesize-palette.png");
gdImageDestroy (im_in);
return 0;
}
/* If palette is true, we convert from truecolor to palette at the end,
to test gdImageTrueColorToPalette and see file size/
quality tradeoffs. */
void
testDrawing (
gdImagePtr im_in,
double scale,
int blending,
int palette,
char *filename)
{
gdImagePtr im_out;
FILE *out;
/* Create output image. */
im_out = gdImageCreateTrueColor ((int) (gdImageSX (im_in) * scale),
(int) (gdImageSY (im_in) * scale));
/*
Request alpha blending. This causes future
drawing operations to perform alpha channel blending
with the background, resulting in an opaque image.
Without this call, pixels in the foreground color are
copied literally, *including* the alpha channel value,
resulting in an output image which is potentially
not opaque. This flag can be set and cleared as often
as desired. */
gdImageAlphaBlending (im_out, blending);
/* Flood with light blue. */
gdImageFill (im_out, (int) (gdImageSX (im_in) * scale / 2),
(int) (gdImageSY (im_in) * scale / 2),
gdTrueColor (192, 192, 255));
/* Copy the source image. Alpha blending should result in
compositing against red. With blending turned off, the
browser or viewer will composite against its preferred
background, or, if it does not support an alpha channel,
we will see the original colors for the pixels that
ought to be transparent or semitransparent. */
gdImageCopyResampled (im_out, im_in,
0, 0,
0, 0,
(int) (gdImageSX (im_in) * scale), (int) (gdImageSY (im_in) * scale),
gdImageSX (im_in), gdImageSY (im_in));
/* Write PNG */
out = fopen (filename, "wb");
/* If this image is the result of alpha channel blending,
it will not contain an interesting alpha channel itself.
Save a little file size by not saving the alpha channel.
Otherwise the file would typically be slightly larger. */
gdImageSaveAlpha (im_out, !blending);
/* If requested, convert from truecolor to palette. */
if (palette)
{
/* Dithering, 256 colors. */
gdImageTrueColorToPalette (im_out, 1, 256);
}
gdImagePng (im_out, out);
fclose (out);
gdImageDestroy (im_out);
}

369
ext/gd/libgd/wbmp.c Normal file
View File

@ -0,0 +1,369 @@
/* WBMP
** ----
** WBMP Level 0: B/W, Uncompressed
** This implements the WBMP format as specified in WAPSpec 1.1 and 1.2.
** It does not support ExtHeaders as defined in the spec. The spec states
** that a WAP client does not need to implement ExtHeaders.
**
** (c) 2000 Johan Van den Brande <johan@vandenbrande.com>
*/
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "wbmp.h"
#include "gd.h"
#include "gdhelpers.h"
#ifdef NOTDEF
#define __TEST /* Compile with main function */
#define __DEBUG /* Extra verbose when with __TEST */
#define __WRITE /* readwbmp and writewbmp(stdout) */
#define __VIEW /* view the wbmp on stdout */
#endif
/* getmbi
** ------
** Get a multibyte integer from a generic getin function
** 'getin' can be getc, with in = NULL
** you can find getin as a function just above the main function
** This way you gain a lot of flexibilty about how this package
** reads a wbmp file.
*/
int
getmbi (int (*getin) (void *in), void *in)
{
int i, mbi = 0;
do
{
i = getin (in);
if (i < 0)
return (-1);
mbi = mbi << 7 | i & 0x7f;
}
while (i & 0x80);
return (mbi);
}
/* putmbi
** ------
** Put a multibyte intgerer in some kind of output stream
** I work here with a function pointer, to make it as generic
** as possible. Look at this function as an iterator on the
** mbi integers it spits out.
**
*/
void
putmbi (int i, void (*putout) (int c, void *out), void *out)
{
int cnt, l, accu;
/* Get number of septets */
cnt = 0;
accu = 0;
while (accu != i)
accu += i & 0x7f << 7 * cnt++;
/* Produce the multibyte output */
for (l = cnt - 1; l > 0; l--)
putout (0x80 | (i & 0x7f << 7 * l) >> 7 * l, out);
putout (i & 0x7f, out);
}
/* skipheader
** ----------
** Skips the ExtHeader. Not needed for the moment
**
*/
int
skipheader (int (*getin) (void *in), void *in)
{
int i;
do
{
i = getin (in);
if (i < 0)
return (-1);
}
while (i & 0x80);
return (0);
}
/* create wbmp
** -----------
** create an empty wbmp
**
*/
Wbmp *
createwbmp (int width, int height, int color)
{
int i;
Wbmp *wbmp;
if ((wbmp = (Wbmp *) gdMalloc (sizeof (Wbmp))) == NULL)
return (NULL);
if ((wbmp->bitmap = (int *) gdMalloc (sizeof (int) * width * height)) == NULL)
{
gdFree (wbmp);
return (NULL);
}
wbmp->width = width;
wbmp->height = height;
for (i = 0; i < width * height; wbmp->bitmap[i++] = color);
return (wbmp);
}
/* readwbmp
** -------
** Actually reads the WBMP format from an open file descriptor
** It goes along by returning a pointer to a WBMP struct.
**
*/
int
readwbmp (int (*getin) (void *in), void *in, Wbmp ** return_wbmp)
{
int row, col, byte, pel, pos;
Wbmp *wbmp;
if ((wbmp = (Wbmp *) gdMalloc (sizeof (Wbmp))) == NULL)
return (-1);
wbmp->type = getin (in);
if (wbmp->type != 0)
{
gdFree (wbmp);
return (-1);
}
if (skipheader (getin, in))
return (-1);
wbmp->width = getmbi (getin, in);
if (wbmp->width == -1)
{
gdFree (wbmp);
return (-1);
}
wbmp->height = getmbi (getin, in);
if (wbmp->height == -1)
{
gdFree (wbmp);
return (-1);
}
#ifdef __DEBUG
printf ("W: %d, H: %d\n", wbmp->width, wbmp->height);
#endif
if ((wbmp->bitmap = (int *) gdMalloc (sizeof (int) * wbmp->width * wbmp->height)) == NULL)
{
gdFree (wbmp);
return (-1);
}
#ifdef __DEBUG
printf ("DATA CONSTRUCTED\n");
#endif
pos = 0;
for (row = 0; row < wbmp->height; row++)
{
for (col = 0; col < wbmp->width;)
{
byte = getin (in);
for (pel = 7; pel >= 0; pel--)
{
if (col++ < wbmp->width)
{
if (byte & 1 << pel)
{
wbmp->bitmap[pos] = WBMP_WHITE;
}
else
{
wbmp->bitmap[pos] = WBMP_BLACK;
}
pos++;
}
}
}
}
*return_wbmp = wbmp;
return (0);
}
/* writewbmp
** ---------
** Write a wbmp to a file descriptor
**
** Why not just giving a filedescriptor to this function?
** Well, the incentive to write this function was the complete
** integration in gd library from www.boutell.com. They use
** their own io functions, so the passing of a function seemed to be
** a logic(?) decision ...
**
*/
int
writewbmp (Wbmp * wbmp, void (*putout) (int c, void *out), void *out)
{
int row, col;
int bitpos, octet;
/* Generate the header */
putout (0, out); /* WBMP Type 0: B/W, Uncompressed bitmap */
putout (0, out); /* FixHeaderField */
/* Size of the image */
putmbi (wbmp->width, putout, out); /* width */
putmbi (wbmp->height, putout, out); /* height */
/* Image data */
for (row = 0; row < wbmp->height; row++)
{
bitpos = 8;
octet = 0;
for (col = 0; col < wbmp->width; col++)
{
octet |= ((wbmp->bitmap[row * wbmp->width + col] == 1) ? WBMP_WHITE : WBMP_BLACK) << --bitpos;
if (bitpos == 0)
{
bitpos = 8;
putout (octet, out);
octet = 0;
}
}
if (bitpos != 8)
putout (octet, out);
}
return (0);
}
/* freewbmp
** --------
** gdFrees up memory occupied by a WBMP structure
**
*/
void
freewbmp (Wbmp * wbmp)
{
gdFree (wbmp->bitmap);
gdFree (wbmp);
}
/* printwbmp
** ---------
** print a WBMP to stdout for visualisation
**
*/
void
printwbmp (Wbmp * wbmp)
{
int row, col;
for (row = 0; row < wbmp->height; row++)
{
for (col = 0; col < wbmp->width; col++)
{
if (wbmp->bitmap[wbmp->width * row + col] == WBMP_BLACK)
{
putchar ('#');
}
else
{
putchar (' ');
}
}
putchar ('\n');
}
}
#ifdef __TEST
/* putout to file descriptor
** -------------------------
*/
int
putout (int c, void *out)
{
return (putc (c, (FILE *) out));
}
/* getin from file descriptor
** --------------------------
*/
int
getin (void *in)
{
return (getc ((FILE *) in));
}
/* Main function
** -------------
**
*/
int
main (int argc, char *argv[])
{
FILE *wbmp_file;
Wbmp *wbmp;
wbmp_file = fopen (argv[1], "rb");
if (wbmp_file)
{
readwbmp (&getin, wbmp_file, &wbmp);
#ifdef __VIEW
#ifdef __DEBUG
printf ("\nVIEWING IMAGE\n");
#endif
printwbmp (wbmp);
#endif
#ifdef __WRITE
#ifdef __DEBUG
printf ("\nDUMPING WBMP to STDOUT\n");
#endif
writewbmp (wbmp, &putout, stdout);
#endif
freewbmp (wbmp);
fclose (wbmp_file);
}
}
#endif

47
ext/gd/libgd/wbmp.h Normal file
View File

@ -0,0 +1,47 @@
/* WBMP
** ----
** WBMP Level 0: B/W, Uncompressed
** This implements the WBMP format as specified in WAPSpec 1.1 and 1.2.
** It does not support ExtHeaders as defined in the spec. The spec states
** that a WAP client does not need to implement ExtHeaders.
**
** (c) 2000 Johan Van den Brande <johan@vandenbrande.com>
**
** Header file
*/
#ifndef __WBMP_H
#define __WBMP_H 1
/* WBMP struct
** -----------
** A Wireless bitmap structure
**
*/
typedef struct Wbmp_
{
int type; /* type of the wbmp */
int width; /* width of the image */
int height; /* height of the image */
int *bitmap; /* pointer to data: 0 = WHITE , 1 = BLACK */
} Wbmp;
#define WBMP_WHITE 1
#define WBMP_BLACK 0
/* Proto's
** -------
**
*/
void putmbi( int i, void (*putout)(int c, void *out), void *out);
int getmbi ( int (*getin)(void *in), void *in );
int skipheader( int (*getin)(void *in), void *in );
Wbmp *createwbmp( int width, int height, int color );
int readwbmp( int (*getin)(void *in), void *in, Wbmp **wbmp );
int writewbmp( Wbmp *wbmp, void (*putout)( int c, void *out), void *out);
void freewbmp( Wbmp *wbmp );
void printwbmp( Wbmp *wbmp );
#endif

254
ext/gd/libgd/webpng.c Normal file
View File

@ -0,0 +1,254 @@
/* Bring in the gd library functions */
#include "gd.h"
/* Bring in standard I/O and string manipulation functions */
#include <stdio.h>
#include <stdlib.h> /* for atoi() */
#include <string.h>
#ifdef _WIN32
#include <process.h>
int
getpid ()
{
return _getpid ();
}
#else
#include <unistd.h> /* for getpid(), unlink() */
#endif
int
main (int argc, char **argv)
{
FILE *in;
FILE *out;
char outFn[20];
int useStdinStdout = 0;
/* Declare our image pointer */
gdImagePtr im = 0;
int i;
/* We'll clear 'no' once we know the user has made a
reasonable request. */
int no = 1;
/* We'll set 'write' once we know the user's request
requires that the image be written back to disk. */
int write = 0;
/* C programs always get at least one argument; we want at
least one more (the image), more in practice. */
if (argc < 2 || !strcmp (argv[1], "--help"))
{
no = 1;
goto usage;
}
/* The last argument should be the image. Open the file. */
if (strcmp ("-", argv[argc - 1]) == 0)
{ /* - is synonymous with STDIN */
useStdinStdout = 1;
in = stdin;
}
else
{
in = fopen (argv[argc - 1], "rb");
}
if (!in)
{
fprintf (stderr,
"Error: can't open file %s.\n", argv[argc - 1]);
exit (1);
}
/* Now load the image. */
im = gdImageCreateFromPng (in);
fclose (in);
/* If the load failed, it must not be a PNG file. */
if (!im)
{
fprintf (stderr,
"Error: %s is not a valid PNG file.\n", argv[argc - 1]);
exit (1);
}
/* Consider each argument in turn. */
for (i = 1; (i < (argc - 1)); i++)
{
/* -i turns on and off interlacing. */
if (!strcmp (argv[i], "--help"))
{
/* Every program should use this for help! :) */
no = 1;
goto usage;
}
else if (!strcmp (argv[i], "-i"))
{
if (i == (argc - 2))
{
fprintf (stderr,
"Error: -i specified without y or n.\n");
no = 1;
goto usage;
}
if (!strcmp (argv[i + 1], "y"))
{
/* Set interlace. */
gdImageInterlace (im, 1);
}
else if (!strcmp (argv[i + 1], "n"))
{
/* Clear interlace. */
gdImageInterlace (im, 0);
}
else
{
fprintf (stderr,
"Error: -i specified without y or n.\n");
no = 1;
goto usage;
}
i++;
no = 0;
write = 1;
}
else if (!strcmp (argv[i], "-t"))
{
/* Set transparent index (or none). */
int index;
if (i == (argc - 2))
{
fprintf (stderr,
"Error: -t specified without a color table index.\n");
no = 1;
goto usage;
}
if (!strcmp (argv[i + 1], "none"))
{
/* -1 means not transparent. */
gdImageColorTransparent (im, -1);
}
else
{
/* OK, get an integer and set the index. */
index = atoi (argv[i + 1]);
gdImageColorTransparent (im, index);
}
i++;
write = 1;
no = 0;
}
else if (!strcmp (argv[i], "-l"))
{
/* List the colors in the color table. */
int j;
if (!im->trueColor)
{
/* Tabs used below. */
printf ("Index Red Green Blue Alpha\n");
for (j = 0; (j < gdImageColorsTotal (im)); j++)
{
/* Use access macros to learn colors. */
printf ("%d %d %d %d %d\n",
j,
gdImageRed (im, j),
gdImageGreen (im, j),
gdImageBlue (im, j),
gdImageAlpha (im, j));
}
}
else
{
printf ("Truecolor image, no palette entries to list.\n");
}
no = 0;
}
else if (!strcmp (argv[i], "-d"))
{
/* Output dimensions, etc. */
int t;
printf ("Width: %d Height: %d Colors: %d\n",
gdImageSX (im), gdImageSY (im),
gdImageColorsTotal (im));
t = gdImageGetTransparent (im);
if (t != (-1))
{
printf ("First 100% transparent index: %d\n", t);
}
else
{
/* -1 means the image is not transparent. */
printf ("First 100% transparent index: none\n");
}
if (gdImageGetInterlaced (im))
{
printf ("Interlaced: yes\n");
}
else
{
printf ("Interlaced: no\n");
}
no = 0;
}
else
{
fprintf (stderr, "Unknown argument: %s\n", argv[i]);
break;
}
}
usage:
if (no)
{
/* If the command failed, output an explanation. */
fprintf (stderr,
"Usage: webpng [-i y|n ] [-l] [-t index|none ] [-d] pngname.png\n"
" -i [y|n] Turns on/off interlace\n"
" -l Prints the table of color indexes\n"
" -t [index] Set the transparent color to the specified index (0-255 or \"none\")\n"
" -d Reports the dimensions and other characteristics of the image.\n"
"\n"
"If you specify '-' as the input file, stdin/stdout will be used input/output.\n"
);
}
if (write)
{
if (useStdinStdout)
{
out = stdout;
}
else
{
/* Open a temporary file. */
/* "temp.tmp" is not good temporary filename. */
sprintf (outFn, "webpng.tmp%d", getpid ());
out = fopen (outFn, "wb");
if (!out)
{
fprintf (stderr,
"Unable to write to %s -- exiting\n", outFn);
exit (1);
}
}
/* Write the new PNG. */
gdImagePng (im, out);
if (!useStdinStdout)
{
fclose (out);
/* Erase the old PNG. */
unlink (argv[argc - 1]);
/* Rename the new to the old. */
if (rename (outFn, argv[argc - 1]) != 0)
{
perror ("rename");
exit (1);
}
}
}
/* Delete the image from memory. */
if (im)
{
gdImageDestroy (im);
}
/* All's well that ends well. */
return 0;
}