2006-04-05 08:35:53 -07:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <zlib.h>
|
|
|
|
#include "gd.h"
|
2006-04-05 08:41:55 -07:00
|
|
|
#include "gdhelpers.h"
|
2006-04-05 08:37:05 -07:00
|
|
|
|
2006-04-05 08:41:22 -07:00
|
|
|
#ifdef _OSD_POSIX /* BS2000 uses the EBCDIC char set instead of ASCII */
|
|
|
|
#define CHARSET_EBCDIC
|
|
|
|
#define __attribute__(any) /*nothing*/
|
|
|
|
#endif /*_OSD_POSIX*/
|
|
|
|
|
|
|
|
#ifndef CHARSET_EBCDIC
|
|
|
|
#define ASC(ch) ch
|
|
|
|
#else /*CHARSET_EBCDIC*/
|
|
|
|
#define ASC(ch) gd_toascii[(unsigned char)ch]
|
|
|
|
static const unsigned char gd_toascii[256] = {
|
|
|
|
/*00*/ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f,
|
|
|
|
0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/
|
|
|
|
/*10*/ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97,
|
|
|
|
0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/
|
|
|
|
/*20*/ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b,
|
|
|
|
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /*................*/
|
|
|
|
/*30*/ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04,
|
|
|
|
0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /*................*/
|
|
|
|
/*40*/ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5,
|
|
|
|
0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+|*/
|
|
|
|
/*50*/ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef,
|
|
|
|
0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /*&.........!$*);.*/
|
|
|
|
/*60*/ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5,
|
|
|
|
0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /*-/........^,%_>?*/
|
|
|
|
/*70*/ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf,
|
|
|
|
0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /*..........:#@'="*/
|
|
|
|
/*80*/ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
|
|
|
0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /*.abcdefghi......*/
|
|
|
|
/*90*/ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
|
|
|
|
0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /*.jklmnopqr......*/
|
|
|
|
/*a0*/ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
|
|
|
0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /*..stuvwxyz......*/
|
|
|
|
/*b0*/ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc,
|
|
|
|
0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /*...........[\]..*/
|
|
|
|
/*c0*/ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
|
|
|
0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /*.ABCDEFGHI......*/
|
|
|
|
/*d0*/ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
|
|
|
|
0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /*.JKLMNOPQR......*/
|
|
|
|
/*e0*/ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
|
|
|
0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /*..STUVWXYZ......*/
|
|
|
|
/*f0*/ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
|
|
|
0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e /*0123456789.{.}.~*/
|
|
|
|
};
|
|
|
|
#endif /*CHARSET_EBCDIC*/
|
|
|
|
|
2006-04-05 08:41:55 -07:00
|
|
|
extern int gdCosT[];
|
|
|
|
extern int gdSinT[];
|
2006-04-05 08:35:53 -07:00
|
|
|
|
|
|
|
static void gdImageBrushApply(gdImagePtr im, int x, int y);
|
|
|
|
static void gdImageTileApply(gdImagePtr im, int x, int y);
|
|
|
|
|
|
|
|
gdImagePtr gdImageCreate(int sx, int sy)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
gdImagePtr im;
|
2006-04-05 08:41:55 -07:00
|
|
|
im = (gdImage *) gdMalloc(sizeof(gdImage));
|
2006-04-05 08:42:22 -07:00
|
|
|
memset(im, 0, sizeof(gdImage));
|
|
|
|
/* Row-major ever since gd 1.3 */
|
2006-04-05 08:41:55 -07:00
|
|
|
im->pixels = (unsigned char **) gdMalloc(sizeof(unsigned char *) * sy);
|
2006-04-05 08:35:53 -07:00
|
|
|
im->polyInts = 0;
|
|
|
|
im->polyAllocated = 0;
|
|
|
|
im->brush = 0;
|
|
|
|
im->tile = 0;
|
|
|
|
im->style = 0;
|
|
|
|
for (i=0; (i<sy); i++) {
|
2006-04-05 08:42:22 -07:00
|
|
|
/* Row-major ever since gd 1.3 */
|
2006-04-05 08:41:55 -07:00
|
|
|
im->pixels[i] = (unsigned char *) gdCalloc(
|
2006-04-05 08:35:53 -07:00
|
|
|
sx, sizeof(unsigned char));
|
|
|
|
}
|
|
|
|
im->sx = sx;
|
|
|
|
im->sy = sy;
|
|
|
|
im->colorsTotal = 0;
|
|
|
|
im->transparent = (-1);
|
|
|
|
im->interlace = 0;
|
2006-04-05 08:42:22 -07:00
|
|
|
im->thick = 1;
|
2006-04-05 08:35:53 -07:00
|
|
|
for (i=0; (i < gdMaxColors); i++) {
|
|
|
|
im->open[i] = 1;
|
|
|
|
im->red[i] = 0;
|
|
|
|
im->green[i] = 0;
|
|
|
|
im->blue[i] = 0;
|
|
|
|
};
|
2006-04-05 08:42:22 -07:00
|
|
|
im->trueColor = 0;
|
|
|
|
im->tpixels = 0;
|
|
|
|
return im;
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
gdImagePtr gdImageCreateTrueColor(int sx, int sy)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
gdImagePtr im;
|
|
|
|
im = (gdImage *) gdMalloc(sizeof(gdImage));
|
|
|
|
memset(im, 0, sizeof(gdImage));
|
|
|
|
im->tpixels = (int **) gdMalloc(sizeof(int *) * sy);
|
|
|
|
im->polyInts = 0;
|
|
|
|
im->polyAllocated = 0;
|
|
|
|
im->brush = 0;
|
|
|
|
im->tile = 0;
|
|
|
|
im->style = 0;
|
|
|
|
for (i=0; (i<sy); i++) {
|
|
|
|
im->tpixels[i] = (int *) gdCalloc(
|
|
|
|
sx, sizeof(int));
|
|
|
|
}
|
|
|
|
im->sx = sx;
|
|
|
|
im->sy = sy;
|
|
|
|
im->transparent = (-1);
|
|
|
|
im->interlace = 0;
|
|
|
|
im->trueColor = 1;
|
|
|
|
im->saveAlphaFlag = 1;
|
|
|
|
im->alphaBlendingFlag = 0;
|
|
|
|
im->thick = 1;
|
2006-04-05 08:35:53 -07:00
|
|
|
return im;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageDestroy(gdImagePtr im)
|
|
|
|
{
|
|
|
|
int i;
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im->pixels) {
|
|
|
|
for (i=0; (i<im->sy); i++) {
|
|
|
|
gdFree(im->pixels[i]);
|
|
|
|
}
|
|
|
|
gdFree(im->pixels);
|
|
|
|
}
|
|
|
|
if (im->tpixels) {
|
|
|
|
for (i=0; (i<im->sy); i++) {
|
|
|
|
gdFree(im->tpixels[i]);
|
|
|
|
}
|
|
|
|
gdFree(im->tpixels);
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
if (im->polyInts) {
|
2006-04-05 08:41:55 -07:00
|
|
|
gdFree(im->polyInts);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
if (im->style) {
|
2006-04-05 08:41:55 -07:00
|
|
|
gdFree(im->style);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
2006-04-05 08:41:55 -07:00
|
|
|
gdFree(im);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int gdImageColorClosest(gdImagePtr im, int r, int g, int b)
|
2006-04-05 08:42:22 -07:00
|
|
|
{
|
|
|
|
return gdImageColorClosestAlpha(im, r, g, b, gdAlphaOpaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
int gdImageColorClosestAlpha(gdImagePtr im, int r, int g, int b, int a)
|
2006-04-05 08:35:53 -07:00
|
|
|
{
|
|
|
|
int i;
|
2006-04-05 08:42:22 -07:00
|
|
|
long rd, gd, bd, ad;
|
2006-04-05 08:35:53 -07:00
|
|
|
int ct = (-1);
|
|
|
|
int first = 1;
|
|
|
|
long mindist = 0;
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im->trueColor) {
|
|
|
|
return gdTrueColorAlpha(r, g, b, a);
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
for (i=0; (i<(im->colorsTotal)); i++) {
|
|
|
|
long dist;
|
|
|
|
if (im->open[i]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
rd = (im->red[i] - r);
|
|
|
|
gd = (im->green[i] - g);
|
|
|
|
bd = (im->blue[i] - b);
|
2006-04-05 08:42:22 -07:00
|
|
|
ad = (im->blue[i] - b);
|
|
|
|
dist = rd * rd + gd * gd + bd * bd + ad * ad;
|
2006-04-05 08:35:53 -07:00
|
|
|
if (first || (dist < mindist)) {
|
|
|
|
mindist = dist;
|
|
|
|
ct = i;
|
|
|
|
first = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ct;
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:40:28 -07:00
|
|
|
/* This code is taken from http://www.acm.org/jgt/papers/SmithLyons96/hwb_rgb.html, an article
|
|
|
|
* on colour conversion to/from RBG and HWB colour systems.
|
|
|
|
* It has been modified to return the converted value as a * parameter.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define RETURN_HWB(h, w, b) {HWB->H = h; HWB->W = w; HWB->B = b; return HWB;}
|
|
|
|
#define RETURN_RGB(r, g, b) {RGB->R = r; RGB->G = g; RGB->B = b; return RGB;}
|
|
|
|
#define HWB_UNDEFINED -1
|
|
|
|
#define SETUP_RGB(s, r, g, b) {s.R = r/255.0; s.G = g/255.0; s.B = b/255.0;}
|
|
|
|
|
|
|
|
#define MIN(a,b) ((a)<(b)?(a):(b))
|
|
|
|
#define MIN3(a,b,c) ((a)<(b)?(MIN(a,c)):(MIN(b,c)))
|
|
|
|
#define MAX(a,b) ((a)<(b)?(b):(a))
|
|
|
|
#define MAX3(a,b,c) ((a)<(b)?(MAX(b,c)):(MAX(a,c)))
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Theoretically, hue 0 (pure red) is identical to hue 6 in these transforms. Pure
|
|
|
|
* red always maps to 6 in this implementation. Therefore UNDEFINED can be
|
|
|
|
* defined as 0 in situations where only unsigned numbers are desired.
|
|
|
|
*/
|
|
|
|
typedef struct {float R, G, B;} RGBType;
|
|
|
|
typedef struct {float H, W, B;} HWBType;
|
|
|
|
|
|
|
|
static HWBType* RGB_to_HWB( RGBType RGB, HWBType* HWB) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RGB are each on [0, 1]. W and B are returned on [0, 1] and H is
|
|
|
|
* returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B.
|
|
|
|
*/
|
|
|
|
|
|
|
|
float R = RGB.R, G = RGB.G, B = RGB.B, w, v, b, f;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
w = MIN3(R, G, B);
|
|
|
|
v = MAX3(R, G, B);
|
|
|
|
b = 1 - v;
|
|
|
|
if (v == w) RETURN_HWB(HWB_UNDEFINED, w, b);
|
|
|
|
f = (R == w) ? G - B : ((G == w) ? B - R : R - G);
|
|
|
|
i = (R == w) ? 3 : ((G == w) ? 5 : 1);
|
|
|
|
RETURN_HWB(i - f /(v - w), w, b);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static float HWB_Diff(int r1, int g1, int b1, int r2, int g2, int b2) {
|
|
|
|
RGBType RGB1, RGB2;
|
|
|
|
HWBType HWB1, HWB2;
|
|
|
|
float diff;
|
|
|
|
|
|
|
|
SETUP_RGB(RGB1, r1, g1, b1);
|
|
|
|
SETUP_RGB(RGB2, r2, g2, b2);
|
|
|
|
|
|
|
|
RGB_to_HWB(RGB1, &HWB1);
|
|
|
|
RGB_to_HWB(RGB2, &HWB2);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* I made this bit up; it seems to produce OK results, and it is certainly
|
|
|
|
* more visually correct than the current RGB metric. (PJW)
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ( (HWB1.H == HWB_UNDEFINED) || (HWB2.H == HWB_UNDEFINED) ) {
|
|
|
|
diff = 0; /* Undefined hues always match... */
|
|
|
|
} else {
|
|
|
|
diff = abs(HWB1.H - HWB2.H);
|
|
|
|
if (diff > 3) {
|
|
|
|
diff = 6 - diff; /* Remember, it's a colour circle */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
diff = diff*diff + (HWB1.W - HWB2.W)*(HWB1.W - HWB2.W) + (HWB1.B - HWB2.B)*(HWB1.B - HWB2.B);
|
|
|
|
|
|
|
|
return diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is not actually used, but is here for completeness, in case someone wants to
|
|
|
|
* use the HWB stuff for anything else...
|
|
|
|
*/
|
|
|
|
static RGBType* HWB_to_RGB( HWBType HWB, RGBType* RGB ) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* H is given on [0, 6] or UNDEFINED. W and B are given on [0, 1].
|
|
|
|
* RGB are each returned on [0, 1].
|
|
|
|
*/
|
|
|
|
|
|
|
|
float h = HWB.H, w = HWB.W, b = HWB.B, v, n, f;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
v = 1 - b;
|
|
|
|
if (h == HWB_UNDEFINED) RETURN_RGB(v, v, v);
|
|
|
|
i = floor(h);
|
|
|
|
f = h - i;
|
|
|
|
if (i & 1) f = 1 - f; /* if i is odd */
|
|
|
|
n = w + f * (v - w); /* linear interpolation between w and v */
|
|
|
|
switch (i) {
|
|
|
|
case 6:
|
|
|
|
case 0: RETURN_RGB(v, n, w);
|
|
|
|
case 1: RETURN_RGB(n, v, w);
|
|
|
|
case 2: RETURN_RGB(w, v, n);
|
|
|
|
case 3: RETURN_RGB(w, n, v);
|
|
|
|
case 4: RETURN_RGB(n, w, v);
|
|
|
|
case 5: RETURN_RGB(v, w, n);
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:41:55 -07:00
|
|
|
return RGB;
|
|
|
|
|
2006-04-05 08:40:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int gdImageColorClosestHWB(gdImagePtr im, int r, int g, int b)
|
|
|
|
{
|
|
|
|
int i;
|
2006-04-05 08:41:55 -07:00
|
|
|
/* long rd, gd, bd; */
|
2006-04-05 08:40:28 -07:00
|
|
|
int ct = (-1);
|
|
|
|
int first = 1;
|
|
|
|
float mindist = 0;
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im->trueColor) {
|
|
|
|
return gdTrueColor(r, g, b);
|
|
|
|
}
|
2006-04-05 08:40:28 -07:00
|
|
|
for (i=0; (i<(im->colorsTotal)); i++) {
|
|
|
|
float dist;
|
|
|
|
if (im->open[i]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
dist = HWB_Diff(im->red[i], im->green[i], im->blue[i], r, g, b);
|
|
|
|
if (first || (dist < mindist)) {
|
|
|
|
mindist = dist;
|
|
|
|
ct = i;
|
|
|
|
first = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ct;
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
int gdImageColorExact(gdImagePtr im, int r, int g, int b)
|
2006-04-05 08:42:22 -07:00
|
|
|
{
|
|
|
|
return gdImageColorExactAlpha(im, r, g, b, gdAlphaOpaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
int gdImageColorExactAlpha(gdImagePtr im, int r, int g, int b, int a)
|
2006-04-05 08:35:53 -07:00
|
|
|
{
|
|
|
|
int i;
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im->trueColor) {
|
|
|
|
return gdTrueColorAlpha(r, g, b, a);
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
for (i=0; (i<(im->colorsTotal)); i++) {
|
|
|
|
if (im->open[i]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((im->red[i] == r) &&
|
|
|
|
(im->green[i] == g) &&
|
2006-04-05 08:42:22 -07:00
|
|
|
(im->blue[i] == b) &&
|
|
|
|
(im->alpha[i] == a))
|
|
|
|
{
|
2006-04-05 08:35:53 -07:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int gdImageColorAllocate(gdImagePtr im, int r, int g, int b)
|
2006-04-05 08:42:22 -07:00
|
|
|
{
|
|
|
|
return gdImageColorAllocateAlpha(im, r, g, b, gdAlphaOpaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
int gdImageColorAllocateAlpha(gdImagePtr im, int r, int g, int b, int a)
|
2006-04-05 08:35:53 -07:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int ct = (-1);
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im->trueColor) {
|
|
|
|
return gdTrueColorAlpha(r, g, b, a);
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
for (i=0; (i<(im->colorsTotal)); i++) {
|
|
|
|
if (im->open[i]) {
|
|
|
|
ct = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ct == (-1)) {
|
|
|
|
ct = im->colorsTotal;
|
|
|
|
if (ct == gdMaxColors) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
im->colorsTotal++;
|
|
|
|
}
|
|
|
|
im->red[ct] = r;
|
|
|
|
im->green[ct] = g;
|
|
|
|
im->blue[ct] = b;
|
2006-04-05 08:42:22 -07:00
|
|
|
im->alpha[ct] = a;
|
2006-04-05 08:35:53 -07:00
|
|
|
im->open[ct] = 0;
|
|
|
|
return ct;
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:37:05 -07:00
|
|
|
/*
|
|
|
|
* gdImageColorResolve is an alternative for the code fragment:
|
|
|
|
*
|
|
|
|
* if ((color=gdImageColorExact(im,R,G,B)) < 0)
|
|
|
|
* if ((color=gdImageColorAllocate(im,R,G,B)) < 0)
|
|
|
|
* color=gdImageColorClosest(im,R,G,B);
|
|
|
|
*
|
|
|
|
* in a single function. Its advantage is that it is guaranteed to
|
|
|
|
* return a color index in one search over the color table.
|
|
|
|
*/
|
2006-04-05 08:42:22 -07:00
|
|
|
|
2006-04-05 08:37:05 -07:00
|
|
|
int gdImageColorResolve(gdImagePtr im, int r, int g, int b)
|
2006-04-05 08:42:22 -07:00
|
|
|
{
|
|
|
|
return gdImageColorResolveAlpha(im, r, g, b, gdAlphaOpaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
int gdImageColorResolveAlpha(gdImagePtr im, int r, int g, int b, int a)
|
2006-04-05 08:37:05 -07:00
|
|
|
{
|
|
|
|
int c;
|
|
|
|
int ct = -1;
|
|
|
|
int op = -1;
|
2006-04-05 08:42:22 -07:00
|
|
|
long rd, gd, bd, ad, dist;
|
|
|
|
long mindist = 4*255*255; /* init to max poss dist */
|
|
|
|
if (im->trueColor) {
|
|
|
|
return gdTrueColorAlpha(r, g, b, a);
|
|
|
|
}
|
2006-04-05 08:37:05 -07:00
|
|
|
|
|
|
|
for (c = 0; c < im->colorsTotal; c++) {
|
|
|
|
if (im->open[c]) {
|
|
|
|
op = c; /* Save open slot */
|
|
|
|
continue; /* Color not in use */
|
|
|
|
}
|
|
|
|
rd = (long)(im->red [c] - r);
|
|
|
|
gd = (long)(im->green[c] - g);
|
|
|
|
bd = (long)(im->blue [c] - b);
|
2006-04-05 08:42:22 -07:00
|
|
|
ad = (long)(im->alpha [c] - a);
|
|
|
|
dist = rd * rd + gd * gd + bd * bd + ad * ad;
|
2006-04-05 08:37:05 -07:00
|
|
|
if (dist < mindist) {
|
|
|
|
if (dist == 0) {
|
|
|
|
return c; /* Return exact match color */
|
|
|
|
}
|
|
|
|
mindist = dist;
|
|
|
|
ct = c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* no exact match. We now know closest, but first try to allocate exact */
|
|
|
|
if (op == -1) {
|
|
|
|
op = im->colorsTotal;
|
|
|
|
if (op == gdMaxColors) { /* No room for more colors */
|
|
|
|
return ct; /* Return closest available color */
|
|
|
|
}
|
|
|
|
im->colorsTotal++;
|
|
|
|
}
|
|
|
|
im->red [op] = r;
|
|
|
|
im->green[op] = g;
|
|
|
|
im->blue [op] = b;
|
2006-04-05 08:42:22 -07:00
|
|
|
im->alpha [op] = a;
|
2006-04-05 08:37:05 -07:00
|
|
|
im->open [op] = 0;
|
|
|
|
return op; /* Return newly allocated color */
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
void gdImageColorDeallocate(gdImagePtr im, int color)
|
|
|
|
{
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im->trueColor) {
|
|
|
|
return;
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
/* Mark it open. */
|
|
|
|
im->open[color] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageColorTransparent(gdImagePtr im, int color)
|
|
|
|
{
|
2006-04-05 08:42:22 -07:00
|
|
|
if (!im->trueColor) {
|
|
|
|
if (im->transparent != -1) {
|
|
|
|
im->alpha[im->transparent] = gdAlphaOpaque;
|
|
|
|
}
|
|
|
|
if (color != -1) {
|
|
|
|
im->alpha[color] = gdAlphaTransparent;
|
|
|
|
}
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
im->transparent = color;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImagePaletteCopy(gdImagePtr to, gdImagePtr from)
|
|
|
|
{
|
2006-04-05 08:42:22 -07:00
|
|
|
int i;
|
2006-04-05 08:35:53 -07:00
|
|
|
int x, y, p;
|
|
|
|
int xlate[256];
|
2006-04-05 08:42:22 -07:00
|
|
|
if (to->trueColor) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (from->trueColor) {
|
|
|
|
return;
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
|
|
|
|
for (i=0; i < 256 ; i++) {
|
|
|
|
xlate[i] = -1;
|
|
|
|
};
|
|
|
|
|
|
|
|
for (x=0 ; x < (to->sx) ; x++) {
|
|
|
|
for (y=0 ; y < (to->sy) ; y++) {
|
|
|
|
p = gdImageGetPixel(to, x, y);
|
|
|
|
if (xlate[p] == -1) {
|
2006-04-05 08:42:22 -07:00
|
|
|
/* This ought to use HWB, but we don't have an alpha-aware
|
|
|
|
version of that yet. */
|
|
|
|
xlate[p] = gdImageColorClosestAlpha(from, to->red[p], to->green[p], to->blue[p], to->alpha[p]);
|
|
|
|
/*printf("Mapping %d (%d, %d, %d, %d) to %d (%d, %d, %d, %d)\n", */
|
|
|
|
/* p, to->red[p], to->green[p], to->blue[p], to->alpha[p], */
|
|
|
|
/* xlate[p], from->red[xlate[p]], from->green[xlate[p]], from->blue[xlate[p]], from->alpha[xlate[p]]); */
|
2006-04-05 08:35:53 -07:00
|
|
|
};
|
|
|
|
gdImageSetPixel(to, x, y, xlate[p]);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
for (i=0; (i < (from->colorsTotal) ) ; i++) {
|
2006-04-05 08:42:22 -07:00
|
|
|
/*printf("Copying color %d (%d, %d, %d, %d)\n", i, from->red[i], from->blue[i], from->green[i], from->alpha[i]); */
|
2006-04-05 08:35:53 -07:00
|
|
|
to->red[i] = from->red[i];
|
|
|
|
to->blue[i] = from->blue[i];
|
|
|
|
to->green[i] = from->green[i];
|
2006-04-05 08:42:22 -07:00
|
|
|
to->alpha[i] = from->alpha[i];
|
2006-04-05 08:35:53 -07:00
|
|
|
to->open[i] = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
for (i=from->colorsTotal ; (i < to->colorsTotal) ; i++) {
|
|
|
|
to->open[i] = 1;
|
|
|
|
};
|
|
|
|
|
|
|
|
to->colorsTotal = from->colorsTotal;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageSetPixel(gdImagePtr im, int x, int y, int color)
|
|
|
|
{
|
|
|
|
int p;
|
|
|
|
switch(color) {
|
|
|
|
case gdStyled:
|
|
|
|
if (!im->style) {
|
|
|
|
/* Refuse to draw if no style is set. */
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
p = im->style[im->stylePos++];
|
|
|
|
}
|
|
|
|
if (p != (gdTransparent)) {
|
|
|
|
gdImageSetPixel(im, x, y, p);
|
|
|
|
}
|
|
|
|
im->stylePos = im->stylePos % im->styleLength;
|
|
|
|
break;
|
|
|
|
case gdStyledBrushed:
|
|
|
|
if (!im->style) {
|
|
|
|
/* Refuse to draw if no style is set. */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
p = im->style[im->stylePos++];
|
|
|
|
if ((p != gdTransparent) && (p != 0)) {
|
|
|
|
gdImageSetPixel(im, x, y, gdBrushed);
|
|
|
|
}
|
|
|
|
im->stylePos = im->stylePos % im->styleLength;
|
|
|
|
break;
|
|
|
|
case gdBrushed:
|
|
|
|
gdImageBrushApply(im, x, y);
|
|
|
|
break;
|
|
|
|
case gdTiled:
|
|
|
|
gdImageTileApply(im, x, y);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (gdImageBoundsSafe(im, x, y)) {
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im->trueColor) {
|
|
|
|
if (im->alphaBlendingFlag) {
|
|
|
|
im->tpixels[y][x] =
|
|
|
|
gdAlphaBlend(im->tpixels[y][x],
|
|
|
|
color);
|
|
|
|
} else {
|
|
|
|
im->tpixels[y][x] = color;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
im->pixels[y][x] = color;
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gdImageBrushApply(gdImagePtr im, int x, int y)
|
|
|
|
{
|
|
|
|
int lx, ly;
|
|
|
|
int hy;
|
|
|
|
int hx;
|
|
|
|
int x1, y1, x2, y2;
|
|
|
|
int srcx, srcy;
|
|
|
|
if (!im->brush) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
hy = gdImageSY(im->brush)/2;
|
|
|
|
y1 = y - hy;
|
|
|
|
y2 = y1 + gdImageSY(im->brush);
|
|
|
|
hx = gdImageSX(im->brush)/2;
|
|
|
|
x1 = x - hx;
|
|
|
|
x2 = x1 + gdImageSX(im->brush);
|
|
|
|
srcy = 0;
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im->trueColor) {
|
|
|
|
for (ly = y1; (ly < y2); ly++) {
|
|
|
|
srcx = 0;
|
|
|
|
for (lx = x1; (lx < x2); lx++) {
|
|
|
|
int p;
|
|
|
|
p = gdImageGetTrueColorPixel(
|
|
|
|
im->brush, srcx, srcy);
|
2006-04-05 08:35:53 -07:00
|
|
|
gdImageSetPixel(im, lx, ly,
|
2006-04-05 08:42:22 -07:00
|
|
|
p);
|
|
|
|
srcx++;
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
srcy++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (ly = y1; (ly < y2); ly++) {
|
|
|
|
srcx = 0;
|
|
|
|
for (lx = x1; (lx < x2); lx++) {
|
|
|
|
int p;
|
|
|
|
p = gdImageGetPixel(im->brush, srcx, srcy);
|
|
|
|
/* Allow for non-square brushes! */
|
|
|
|
if (p != gdImageGetTransparent(im->brush)) {
|
|
|
|
/* Truecolor brush. Very slow
|
|
|
|
on a palette destination. */
|
|
|
|
if (im->brush->trueColor) {
|
|
|
|
gdImageSetPixel(im, lx, ly,
|
|
|
|
gdImageColorResolveAlpha(
|
|
|
|
im,
|
|
|
|
gdTrueColorGetRed(p),
|
|
|
|
gdTrueColorGetGreen(p),
|
|
|
|
gdTrueColorGetBlue(p),
|
|
|
|
gdTrueColorGetAlpha(p)));
|
|
|
|
} else {
|
|
|
|
gdImageSetPixel(im, lx, ly,
|
|
|
|
im->brushColorMap[p]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
srcx++;
|
|
|
|
}
|
|
|
|
srcy++;
|
|
|
|
}
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gdImageTileApply(gdImagePtr im, int x, int y)
|
|
|
|
{
|
|
|
|
int srcx, srcy;
|
|
|
|
int p;
|
|
|
|
if (!im->tile) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
srcx = x % gdImageSX(im->tile);
|
|
|
|
srcy = y % gdImageSY(im->tile);
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im->trueColor) {
|
|
|
|
p = gdImageGetTrueColorPixel(im->tile, srcx, srcy);
|
|
|
|
gdImageSetPixel(im, x, y, p);
|
|
|
|
} else {
|
|
|
|
/* Allow for transparency */
|
|
|
|
if (p != gdImageGetTransparent(im->tile)) {
|
|
|
|
if (im->tile->trueColor) {
|
|
|
|
/* Truecolor tile. Very slow
|
|
|
|
on a palette destination. */
|
|
|
|
gdImageSetPixel(im, x, y,
|
|
|
|
gdImageColorResolveAlpha(
|
|
|
|
im,
|
|
|
|
gdTrueColorGetRed(p),
|
|
|
|
gdTrueColorGetGreen(p),
|
|
|
|
gdTrueColorGetBlue(p),
|
|
|
|
gdTrueColorGetAlpha(p)));
|
|
|
|
} else {
|
|
|
|
gdImageSetPixel(im, x, y,
|
|
|
|
im->tileColorMap[p]);
|
|
|
|
}
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int gdImageGetPixel(gdImagePtr im, int x, int y)
|
|
|
|
{
|
|
|
|
if (gdImageBoundsSafe(im, x, y)) {
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im->trueColor) {
|
|
|
|
return im->tpixels[y][x];
|
|
|
|
} else {
|
|
|
|
return im->pixels[y][x];
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
int gdImageGetTrueColorPixel(gdImagePtr im, int x, int y)
|
|
|
|
{
|
|
|
|
int p = gdImageGetPixel(im, x, y);
|
|
|
|
if (!im->trueColor) {
|
|
|
|
return gdTrueColorAlpha(im->red[p], im->green[p], im->blue[p],
|
|
|
|
(im->transparent == p) ? gdAlphaTransparent :
|
|
|
|
gdAlphaOpaque);
|
|
|
|
} else {
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
/* Bresenham as presented in Foley & Van Dam */
|
2006-04-05 08:35:53 -07:00
|
|
|
void gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
|
|
|
|
{
|
|
|
|
int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
|
2006-04-05 08:42:22 -07:00
|
|
|
int wid;
|
|
|
|
int w, wstart;
|
|
|
|
int thick = im->thick;
|
2006-04-05 08:35:53 -07:00
|
|
|
dx = abs(x2-x1);
|
|
|
|
dy = abs(y2-y1);
|
|
|
|
if (dy <= dx) {
|
2006-04-05 08:42:22 -07:00
|
|
|
/* More-or-less horizontal. use wid for vertical stroke */
|
|
|
|
wid = thick * cos ( atan2(dy, dx) );
|
|
|
|
if (wid==0) wid=1;
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
d = 2*dy - dx;
|
|
|
|
incr1 = 2*dy;
|
|
|
|
incr2 = 2 * (dy - dx);
|
|
|
|
if (x1 > x2) {
|
|
|
|
x = x2;
|
|
|
|
y = y2;
|
|
|
|
ydirflag = (-1);
|
|
|
|
xend = x1;
|
|
|
|
} else {
|
|
|
|
x = x1;
|
|
|
|
y = y1;
|
|
|
|
ydirflag = 1;
|
|
|
|
xend = x2;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
|
|
|
|
/* Set up line thickness */
|
|
|
|
wstart = y - wid/2;
|
|
|
|
for ( w = wstart; w < wstart + wid; w++ )
|
|
|
|
gdImageSetPixel(im, x, w, color);
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
if (((y2 - y1) * ydirflag) > 0) {
|
|
|
|
while (x < xend) {
|
|
|
|
x++;
|
|
|
|
if (d <0) {
|
|
|
|
d+=incr1;
|
|
|
|
} else {
|
|
|
|
y++;
|
|
|
|
d+=incr2;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
wstart = y - wid/2;
|
|
|
|
for ( w = wstart; w < wstart + wid; w++)
|
|
|
|
gdImageSetPixel(im, x, w, color);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
} else {
|
2006-04-05 08:42:22 -07:00
|
|
|
while (x < xend) {
|
2006-04-05 08:35:53 -07:00
|
|
|
x++;
|
|
|
|
if (d <0) {
|
|
|
|
d+=incr1;
|
|
|
|
} else {
|
|
|
|
y--;
|
|
|
|
d+=incr2;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
wstart = y - wid / 2;
|
|
|
|
for ( w=wstart; w < wstart+wid; w++)
|
|
|
|
gdImageSetPixel(im, x, w, color);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2006-04-05 08:42:22 -07:00
|
|
|
/* More-or-less vertical. use wid for horizontal stroke */
|
|
|
|
wid = thick * sin ( atan2(dy, dx) );
|
|
|
|
if (wid == 0 ) wid=1;
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
d = 2*dx - dy;
|
|
|
|
incr1 = 2*dx;
|
|
|
|
incr2 = 2 * (dx - dy);
|
|
|
|
if (y1 > y2) {
|
|
|
|
y = y2;
|
|
|
|
x = x2;
|
|
|
|
yend = y1;
|
|
|
|
xdirflag = (-1);
|
|
|
|
} else {
|
|
|
|
y = y1;
|
|
|
|
x = x1;
|
|
|
|
yend = y2;
|
|
|
|
xdirflag = 1;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
|
|
|
|
/* Set up line thickness */
|
|
|
|
wstart = x - wid/2;
|
|
|
|
for ( w = wstart; w < wstart + wid; w++ )
|
|
|
|
gdImageSetPixel(im, w, y, color);
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
if (((x2 - x1) * xdirflag) > 0) {
|
|
|
|
while (y < yend) {
|
|
|
|
y++;
|
|
|
|
if (d <0) {
|
|
|
|
d+=incr1;
|
|
|
|
} else {
|
|
|
|
x++;
|
|
|
|
d+=incr2;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
wstart = x - wid/2;
|
|
|
|
for ( w=wstart; w < wstart + wid; w++)
|
|
|
|
gdImageSetPixel(im, w, y, color);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (y < yend) {
|
|
|
|
y++;
|
|
|
|
if (d <0) {
|
|
|
|
d+=incr1;
|
|
|
|
} else {
|
|
|
|
x--;
|
|
|
|
d+=incr2;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
wstart = x - wid/2;
|
|
|
|
for ( w = wstart; w < wstart+wid; w++)
|
|
|
|
gdImageSetPixel(im, w, y, color);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void dashedSet(gdImagePtr im, int x, int y, int color,
|
2006-04-05 08:42:22 -07:00
|
|
|
int *onP, int *dashStepP, int wid, int vert);
|
2006-04-05 08:35:53 -07:00
|
|
|
|
|
|
|
void gdImageDashedLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
|
|
|
|
{
|
|
|
|
int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
|
|
|
|
int dashStep = 0;
|
|
|
|
int on = 1;
|
2006-04-05 08:42:22 -07:00
|
|
|
int wid;
|
|
|
|
int w, wstart, vert;
|
|
|
|
int thick = im->thick;
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
dx = abs(x2-x1);
|
|
|
|
dy = abs(y2-y1);
|
|
|
|
if (dy <= dx) {
|
2006-04-05 08:42:22 -07:00
|
|
|
/* More-or-less horizontal. use wid for vertical stroke */
|
|
|
|
wid = thick * sin ( atan2(dy, dx) );
|
|
|
|
vert=1;
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
d = 2*dy - dx;
|
|
|
|
incr1 = 2*dy;
|
|
|
|
incr2 = 2 * (dy - dx);
|
|
|
|
if (x1 > x2) {
|
|
|
|
x = x2;
|
|
|
|
y = y2;
|
|
|
|
ydirflag = (-1);
|
|
|
|
xend = x1;
|
|
|
|
} else {
|
|
|
|
x = x1;
|
|
|
|
y = y1;
|
|
|
|
ydirflag = 1;
|
|
|
|
xend = x2;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
|
2006-04-05 08:35:53 -07:00
|
|
|
if (((y2 - y1) * ydirflag) > 0) {
|
|
|
|
while (x < xend) {
|
|
|
|
x++;
|
|
|
|
if (d <0) {
|
|
|
|
d+=incr1;
|
|
|
|
} else {
|
|
|
|
y++;
|
|
|
|
d+=incr2;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (x < xend) {
|
|
|
|
x++;
|
|
|
|
if (d <0) {
|
|
|
|
d+=incr1;
|
|
|
|
} else {
|
|
|
|
y--;
|
|
|
|
d+=incr2;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2006-04-05 08:42:22 -07:00
|
|
|
/* More-or-less vertical. use wid for horizontal stroke */
|
|
|
|
wid = thick * sin ( atan2(dy, dx) );
|
|
|
|
vert = 0;
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
d = 2*dx - dy;
|
|
|
|
incr1 = 2*dx;
|
|
|
|
incr2 = 2 * (dx - dy);
|
|
|
|
if (y1 > y2) {
|
|
|
|
y = y2;
|
|
|
|
x = x2;
|
|
|
|
yend = y1;
|
|
|
|
xdirflag = (-1);
|
|
|
|
} else {
|
|
|
|
y = y1;
|
|
|
|
x = x1;
|
|
|
|
yend = y2;
|
|
|
|
xdirflag = 1;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
|
2006-04-05 08:35:53 -07:00
|
|
|
if (((x2 - x1) * xdirflag) > 0) {
|
|
|
|
while (y < yend) {
|
|
|
|
y++;
|
|
|
|
if (d <0) {
|
|
|
|
d+=incr1;
|
|
|
|
} else {
|
|
|
|
x++;
|
|
|
|
d+=incr2;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while (y < yend) {
|
|
|
|
y++;
|
|
|
|
if (d <0) {
|
|
|
|
d+=incr1;
|
|
|
|
} else {
|
|
|
|
x--;
|
|
|
|
d+=incr2;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dashedSet(gdImagePtr im, int x, int y, int color,
|
2006-04-05 08:42:22 -07:00
|
|
|
int *onP, int *dashStepP, int wid, int vert)
|
2006-04-05 08:35:53 -07:00
|
|
|
{
|
|
|
|
int dashStep = *dashStepP;
|
|
|
|
int on = *onP;
|
2006-04-05 08:42:22 -07:00
|
|
|
int w, wstart;
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
dashStep++;
|
|
|
|
if (dashStep == gdDashSize) {
|
|
|
|
dashStep = 0;
|
|
|
|
on = !on;
|
|
|
|
}
|
|
|
|
if (on) {
|
2006-04-05 08:42:22 -07:00
|
|
|
if (vert) {
|
|
|
|
wstart = y - wid / 2;
|
|
|
|
for (w=wstart; w < wstart+wid;w++)
|
|
|
|
gdImageSetPixel(im, x, w, color);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wstart = x - wid / 2;
|
|
|
|
for (w=wstart; w < wstart+wid;w++)
|
|
|
|
gdImageSetPixel(im, w, y, color);
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
*dashStepP = dashStep;
|
|
|
|
*onP = on;
|
|
|
|
}
|
|
|
|
|
|
|
|
int gdImageBoundsSafe(gdImagePtr im, int x, int y)
|
|
|
|
{
|
|
|
|
return (!(((y < 0) || (y >= im->sy)) ||
|
|
|
|
((x < 0) || (x >= im->sx))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageChar(gdImagePtr im, gdFontPtr f, int x, int y,
|
|
|
|
int c, int color)
|
|
|
|
{
|
|
|
|
int cx, cy;
|
|
|
|
int px, py;
|
|
|
|
int fline;
|
|
|
|
cx = 0;
|
|
|
|
cy = 0;
|
2006-04-05 08:41:22 -07:00
|
|
|
#ifdef CHARSET_EBCDIC
|
|
|
|
c = ASC(c);
|
|
|
|
#endif /*CHARSET_EBCDIC*/
|
2006-04-05 08:35:53 -07:00
|
|
|
if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fline = (c - f->offset) * f->h * f->w;
|
|
|
|
for (py = y; (py < (y + f->h)); py++) {
|
|
|
|
for (px = x; (px < (x + f->w)); px++) {
|
|
|
|
if (f->data[fline + cy * f->w + cx]) {
|
|
|
|
gdImageSetPixel(im, px, py, color);
|
|
|
|
}
|
|
|
|
cx++;
|
|
|
|
}
|
|
|
|
cx = 0;
|
|
|
|
cy++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageCharUp(gdImagePtr im, gdFontPtr f,
|
|
|
|
int x, int y, int c, int color)
|
|
|
|
{
|
|
|
|
int cx, cy;
|
|
|
|
int px, py;
|
|
|
|
int fline;
|
|
|
|
cx = 0;
|
|
|
|
cy = 0;
|
2006-04-05 08:41:22 -07:00
|
|
|
#ifdef CHARSET_EBCDIC
|
|
|
|
c = ASC(c);
|
|
|
|
#endif /*CHARSET_EBCDIC*/
|
2006-04-05 08:35:53 -07:00
|
|
|
if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fline = (c - f->offset) * f->h * f->w;
|
|
|
|
for (py = y; (py > (y - f->w)); py--) {
|
|
|
|
for (px = x; (px < (x + f->h)); px++) {
|
|
|
|
if (f->data[fline + cy * f->w + cx]) {
|
|
|
|
gdImageSetPixel(im, px, py, color);
|
|
|
|
}
|
|
|
|
cy++;
|
|
|
|
}
|
|
|
|
cy = 0;
|
|
|
|
cx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageString(gdImagePtr im, gdFontPtr f,
|
|
|
|
int x, int y, unsigned char *s, int color)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int l;
|
2006-04-05 08:38:05 -07:00
|
|
|
l = strlen((char *) s);
|
2006-04-05 08:35:53 -07:00
|
|
|
for (i=0; (i<l); i++) {
|
|
|
|
gdImageChar(im, f, x, y, s[i], color);
|
|
|
|
x += f->w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageStringUp(gdImagePtr im, gdFontPtr f,
|
|
|
|
int x, int y, unsigned char *s, int color)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int l;
|
2006-04-05 08:38:05 -07:00
|
|
|
l = strlen((char *) s);
|
2006-04-05 08:35:53 -07:00
|
|
|
for (i=0; (i<l); i++) {
|
|
|
|
gdImageCharUp(im, f, x, y, s[i], color);
|
|
|
|
y -= f->w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int strlen16(unsigned short *s);
|
|
|
|
|
|
|
|
void gdImageString16(gdImagePtr im, gdFontPtr f,
|
|
|
|
int x, int y, unsigned short *s, int color)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int l;
|
|
|
|
l = strlen16(s);
|
|
|
|
for (i=0; (i<l); i++) {
|
|
|
|
gdImageChar(im, f, x, y, s[i], color);
|
|
|
|
x += f->w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageStringUp16(gdImagePtr im, gdFontPtr f,
|
|
|
|
int x, int y, unsigned short *s, int color)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int l;
|
|
|
|
l = strlen16(s);
|
|
|
|
for (i=0; (i<l); i++) {
|
|
|
|
gdImageCharUp(im, f, x, y, s[i], color);
|
|
|
|
y -= f->w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int strlen16(unsigned short *s)
|
|
|
|
{
|
|
|
|
int len = 0;
|
|
|
|
while (*s) {
|
|
|
|
s++;
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
#ifndef HAVE_LSQRT
|
|
|
|
/* If you don't have a nice square root function for longs, you can use
|
|
|
|
** this hack
|
|
|
|
*/
|
|
|
|
long lsqrt(long n)
|
|
|
|
{
|
|
|
|
long result = (long)sqrt((double)n);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void gdImageEllipse (gdImagePtr im, int cx, int cy, int w, int h, int color)
|
|
|
|
{
|
|
|
|
long d, b_sq, b_sq_4, b_sq_6;
|
|
|
|
long a_sq, a_sq_4, a_sq_6;
|
|
|
|
int x, y, switchem;
|
|
|
|
long lsqrt(long);
|
|
|
|
int pix, half, pstart;
|
|
|
|
int thick = im->thick;
|
|
|
|
|
|
|
|
half = thick/2;
|
|
|
|
w /= 2; /* ImageArc uses diameter, not radius */
|
|
|
|
h /= 2;
|
|
|
|
|
|
|
|
d = 2 * (long) h * h + (long) w * w - 2 * (long)w * w * h;
|
|
|
|
b_sq = (long)h * h;
|
|
|
|
b_sq_4 = 4 * (long) h * h;
|
|
|
|
b_sq_6 = 6 * (long) h * h;
|
|
|
|
a_sq = (long) w * w;
|
|
|
|
a_sq_4 = 4 * (long)w * w;
|
|
|
|
a_sq_6 = 6 * (long)w * w;
|
|
|
|
|
|
|
|
x = 0;
|
|
|
|
y = -h;
|
|
|
|
switchem = a_sq / lsqrt( a_sq + b_sq );
|
|
|
|
|
|
|
|
while ( x <= switchem )
|
|
|
|
{
|
|
|
|
pstart = y - half;
|
|
|
|
for (pix = pstart; pix < pstart + thick; pix++)
|
|
|
|
{
|
|
|
|
gdImageSetPixel(im, cx+x, cy+pix, color);
|
|
|
|
gdImageSetPixel(im, cx-x, cy+pix, color);
|
|
|
|
gdImageSetPixel(im, cx+x, cy-pix, color);
|
|
|
|
gdImageSetPixel(im, cx-x, cy-pix, color);
|
|
|
|
}
|
|
|
|
if ( d < 0 )
|
|
|
|
d += b_sq_4 * x++ + b_sq_6;
|
|
|
|
else
|
|
|
|
d += b_sq_4*x++ + b_sq_6 + a_sq_4*(++y);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Middlesplat!
|
|
|
|
** Go a little further if the thickness is not nominal...
|
|
|
|
*/
|
|
|
|
if ( thick > 1 )
|
|
|
|
{
|
|
|
|
int xp = x;
|
|
|
|
int yp = y;
|
|
|
|
int dp = d;
|
|
|
|
int thick2 = thick + 2;
|
|
|
|
int half2 = half + 1;
|
|
|
|
|
|
|
|
while ( xp <= switchem + half )
|
|
|
|
{
|
|
|
|
pstart = yp - half2;
|
|
|
|
for (pix = pstart; pix < pstart + thick2; pix++)
|
|
|
|
{
|
|
|
|
gdImageSetPixel(im, cx+xp, cy+pix, color);
|
|
|
|
gdImageSetPixel(im, cx-xp, cy+pix, color);
|
|
|
|
gdImageSetPixel(im, cx+xp, cy-pix, color);
|
|
|
|
gdImageSetPixel(im, cx-xp, cy-pix, color);
|
|
|
|
}
|
|
|
|
if ( dp < 0 )
|
|
|
|
dp += b_sq_4 * xp++ + b_sq_6;
|
|
|
|
else
|
|
|
|
dp += b_sq_4*xp++ + b_sq_6 + a_sq_4*(++yp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d += -2*(long)b_sq + 2*(long)a_sq - 2*(long)b_sq*(x-1) + 2*(long)a_sq*(y-1);
|
|
|
|
|
|
|
|
while ( y <= 0 )
|
|
|
|
{
|
|
|
|
pstart = x - half;
|
|
|
|
for (pix = pstart; pix < pstart + thick; pix++)
|
|
|
|
{
|
|
|
|
gdImageSetPixel(im, cx+pix, cy+y, color);
|
|
|
|
gdImageSetPixel(im, cx-pix, cy+y, color);
|
|
|
|
gdImageSetPixel(im, cx+pix, cy-y, color);
|
|
|
|
gdImageSetPixel(im, cx-pix, cy-y, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d < 0)
|
|
|
|
d += a_sq_4*y++ + a_sq_6 + b_sq_4*(++x);
|
|
|
|
else
|
|
|
|
d += a_sq_4*y++ + a_sq_6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
/* s and e are integers modulo 360 (degrees), with 0 degrees
|
|
|
|
being the rightmost extreme and degrees changing clockwise.
|
|
|
|
cx and cy are the center in pixels; w and h are the horizontal
|
|
|
|
and vertical diameter in pixels. Nice interface, but slow, since
|
|
|
|
I don't yet use Bresenham (I'm using an inefficient but
|
|
|
|
simple solution with too much work going on in it; generalizing
|
|
|
|
Bresenham to ellipses and partial arcs of ellipses is non-trivial,
|
|
|
|
at least for me) and there are other inefficiencies (small circles
|
|
|
|
do far too much work). */
|
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
static int lineside ( int x, int y, int dx, int dy, int x0, int y0 )
|
2006-04-05 08:35:53 -07:00
|
|
|
{
|
2006-04-05 08:42:22 -07:00
|
|
|
return ( y - y0) * dx - dy * ( x - x0 ) < 0;
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
void gdImageArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
double w2, h2;
|
|
|
|
int xr0, xr1, yr0, yr1; /* starting and ending coordinates */
|
|
|
|
int dx, dy; /* slope of the resulting line */
|
|
|
|
int end;
|
|
|
|
long d, b_sq, b_sq_4, b_sq_6;
|
|
|
|
long a_sq, a_sq_4, a_sq_6;
|
|
|
|
int x, y, switchem;
|
|
|
|
long lsqrt(long);
|
|
|
|
int pix, half, pstart;
|
|
|
|
int thick = im->thick;
|
|
|
|
|
|
|
|
half = thick/2;
|
|
|
|
|
|
|
|
w2 = w / 2.0;
|
|
|
|
h2 = h / 2.0;
|
|
|
|
while (s > 360) s -= 360;
|
|
|
|
while (s < 0 ) s += 360;
|
|
|
|
while (e < s ) e += 360;
|
|
|
|
|
|
|
|
if ( e - s >= 359 )
|
|
|
|
{
|
|
|
|
gdImageEllipse (im, cx, cy, w, h, color);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
xr0 = xr1 = cx;
|
|
|
|
yr0 = yr1 = cy;
|
|
|
|
xr0 += w2 * cos ( (s * 2.0 * 3.14159265) / 360.0 );
|
|
|
|
yr0 += h2 * sin ( (s * 2.0 * 3.14159265) / 360.0 );
|
|
|
|
xr1 += w2 * cos ( (e * 2.0 * 3.14159265 ) / 360.0 );
|
|
|
|
yr1 += h2 * sin ( (e * 2.0 * 3.14159265 ) / 360.0 );
|
|
|
|
dx = xr1 - xr0;
|
|
|
|
dy = yr1 - yr0;
|
|
|
|
|
|
|
|
/* From here it should be just like the ellipse, but qualified to be on
|
|
|
|
** ONE side of the chord from xr0,yr0 to xr1, yr1 (positive)
|
|
|
|
*/
|
|
|
|
d = 2 * (long) h2 * h2 + (long) w2 * w2 - 2 * (long)w2 * w2 * h2;
|
|
|
|
b_sq = (long)h2 * h2;
|
|
|
|
b_sq_4 = 4 * (long) h2 * h2;
|
|
|
|
b_sq_6 = 6 * (long) h2 * h2;
|
|
|
|
a_sq = (long) w2 * w2;
|
|
|
|
a_sq_4 = 4 * (long)w2 * w2;
|
|
|
|
a_sq_6 = 6 * (long)w2 * w2;
|
|
|
|
|
|
|
|
x = 0;
|
|
|
|
y = -h2;
|
|
|
|
switchem = a_sq / lsqrt( a_sq + b_sq );
|
|
|
|
|
|
|
|
while ( x <= switchem )
|
|
|
|
{
|
|
|
|
pstart = y - half;
|
|
|
|
for (pix = pstart; pix < pstart + thick; pix++)
|
|
|
|
{
|
|
|
|
int xx, yy;
|
|
|
|
|
|
|
|
xx = cx+x; yy = cy+pix;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
|
|
|
|
xx = cx - x;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
|
|
|
|
xx = cx+x; yy = cy-pix;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
|
|
|
|
xx = cx-x;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
}
|
|
|
|
if ( d < 0 )
|
|
|
|
d += b_sq_4 * x++ + b_sq_6;
|
|
|
|
else
|
|
|
|
d += b_sq_4*x++ + b_sq_6 + a_sq_4*(++y);
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
/* Middlesplat!
|
|
|
|
** Go a little further if the thickness is not nominal...
|
|
|
|
*/
|
|
|
|
if ( thick > 1 )
|
|
|
|
{
|
|
|
|
int xp = x;
|
|
|
|
int yp = y;
|
|
|
|
int dp = d;
|
|
|
|
int thick2 = thick + 2;
|
|
|
|
int half2 = half + 1;
|
|
|
|
|
|
|
|
while ( xp <= switchem + half )
|
|
|
|
{
|
|
|
|
pstart = yp - half2;
|
|
|
|
for (pix = pstart; pix < pstart + thick2; pix++)
|
|
|
|
{
|
|
|
|
int xx, yy;
|
|
|
|
|
|
|
|
xx = cx+xp; yy = cy+pix;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
|
|
|
|
xx = cx - xp;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
|
|
|
|
xx = cx+xp; yy = cy-pix;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
|
|
|
|
xx = cx-xp;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
}
|
|
|
|
if ( dp < 0 )
|
|
|
|
dp += b_sq_4 * xp++ + b_sq_6;
|
|
|
|
else
|
|
|
|
dp += b_sq_4*xp++ + b_sq_6 + a_sq_4*(++yp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
d += -2*(long)b_sq + 2*(long)a_sq - 2*(long)b_sq*(x-1) + 2*(long)a_sq*(y-1);
|
|
|
|
|
|
|
|
while ( y <= 0 )
|
|
|
|
{
|
|
|
|
pstart = x - half;
|
|
|
|
for (pix = pstart; pix < pstart + thick; pix++)
|
|
|
|
{
|
|
|
|
int xx, yy;
|
|
|
|
|
|
|
|
xx = cx+pix; yy = cy+y;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
|
|
|
|
xx = cx - pix;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
|
|
|
|
xx = cx+pix; yy = cy-y;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
|
|
|
|
xx = cx-pix;
|
|
|
|
if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
|
|
|
|
gdImageSetPixel(im, xx, yy, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d < 0)
|
|
|
|
d += a_sq_4*y++ + a_sq_6 + b_sq_4*(++x);
|
|
|
|
else
|
|
|
|
d += a_sq_4*y++ + a_sq_6;
|
|
|
|
}
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
|
|
|
|
void gdImageFillToBorder(gdImagePtr im, int x, int y, int border, int color)
|
|
|
|
{
|
|
|
|
int lastBorder;
|
|
|
|
/* Seek left */
|
|
|
|
int leftLimit, rightLimit;
|
|
|
|
int i;
|
|
|
|
leftLimit = (-1);
|
|
|
|
if (border < 0) {
|
|
|
|
/* Refuse to fill to a non-solid border */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (i = x; (i >= 0); i--) {
|
|
|
|
if (gdImageGetPixel(im, i, y) == border) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gdImageSetPixel(im, i, y, color);
|
|
|
|
leftLimit = i;
|
|
|
|
}
|
|
|
|
if (leftLimit == (-1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Seek right */
|
|
|
|
rightLimit = x;
|
|
|
|
for (i = (x+1); (i < im->sx); i++) {
|
|
|
|
if (gdImageGetPixel(im, i, y) == border) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gdImageSetPixel(im, i, y, color);
|
|
|
|
rightLimit = i;
|
|
|
|
}
|
|
|
|
/* Look at lines above and below and start paints */
|
|
|
|
/* Above */
|
|
|
|
if (y > 0) {
|
|
|
|
lastBorder = 1;
|
|
|
|
for (i = leftLimit; (i <= rightLimit); i++) {
|
|
|
|
int c;
|
|
|
|
c = gdImageGetPixel(im, i, y-1);
|
|
|
|
if (lastBorder) {
|
|
|
|
if ((c != border) && (c != color)) {
|
|
|
|
gdImageFillToBorder(im, i, y-1,
|
|
|
|
border, color);
|
|
|
|
lastBorder = 0;
|
|
|
|
}
|
|
|
|
} else if ((c == border) || (c == color)) {
|
|
|
|
lastBorder = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Below */
|
|
|
|
if (y < ((im->sy) - 1)) {
|
|
|
|
lastBorder = 1;
|
|
|
|
for (i = leftLimit; (i <= rightLimit); i++) {
|
|
|
|
int c;
|
|
|
|
c = gdImageGetPixel(im, i, y+1);
|
|
|
|
if (lastBorder) {
|
|
|
|
if ((c != border) && (c != color)) {
|
|
|
|
gdImageFillToBorder(im, i, y+1,
|
|
|
|
border, color);
|
|
|
|
lastBorder = 0;
|
|
|
|
}
|
|
|
|
} else if ((c == border) || (c == color)) {
|
|
|
|
lastBorder = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageFill(gdImagePtr im, int x, int y, int color)
|
|
|
|
{
|
|
|
|
int lastBorder;
|
|
|
|
int old;
|
|
|
|
int leftLimit, rightLimit;
|
|
|
|
int i;
|
|
|
|
old = gdImageGetPixel(im, x, y);
|
|
|
|
if (color == gdTiled) {
|
|
|
|
/* Tile fill -- got to watch out! */
|
|
|
|
int p, tileColor;
|
|
|
|
int srcx, srcy;
|
|
|
|
if (!im->tile) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Refuse to flood-fill with a transparent pattern --
|
|
|
|
I can't do it without allocating another image */
|
|
|
|
if (gdImageGetTransparent(im->tile) != (-1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
srcx = x % gdImageSX(im->tile);
|
|
|
|
srcy = y % gdImageSY(im->tile);
|
|
|
|
p = gdImageGetPixel(im->tile, srcx, srcy);
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im->trueColor) {
|
|
|
|
tileColor = p;
|
|
|
|
} else {
|
|
|
|
if (im->tile->trueColor) {
|
|
|
|
tileColor = gdImageColorResolveAlpha(im,
|
|
|
|
gdTrueColorGetRed(p),
|
|
|
|
gdTrueColorGetGreen(p),
|
|
|
|
gdTrueColorGetBlue(p),
|
|
|
|
gdTrueColorGetAlpha(p));
|
|
|
|
} else {
|
|
|
|
tileColor = im->tileColorMap[p];
|
|
|
|
}
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
if (old == tileColor) {
|
|
|
|
/* Nothing to be done */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (old == color) {
|
|
|
|
/* Nothing to be done */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Seek left */
|
|
|
|
leftLimit = (-1);
|
|
|
|
for (i = x; (i >= 0); i--) {
|
|
|
|
if (gdImageGetPixel(im, i, y) != old) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gdImageSetPixel(im, i, y, color);
|
|
|
|
leftLimit = i;
|
|
|
|
}
|
|
|
|
if (leftLimit == (-1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Seek right */
|
|
|
|
rightLimit = x;
|
|
|
|
for (i = (x+1); (i < im->sx); i++) {
|
|
|
|
if (gdImageGetPixel(im, i, y) != old) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gdImageSetPixel(im, i, y, color);
|
|
|
|
rightLimit = i;
|
|
|
|
}
|
|
|
|
/* Look at lines above and below and start paints */
|
|
|
|
/* Above */
|
|
|
|
if (y > 0) {
|
|
|
|
lastBorder = 1;
|
|
|
|
for (i = leftLimit; (i <= rightLimit); i++) {
|
|
|
|
int c;
|
|
|
|
c = gdImageGetPixel(im, i, y-1);
|
|
|
|
if (lastBorder) {
|
|
|
|
if (c == old) {
|
|
|
|
gdImageFill(im, i, y-1, color);
|
|
|
|
lastBorder = 0;
|
|
|
|
}
|
|
|
|
} else if (c != old) {
|
|
|
|
lastBorder = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Below */
|
|
|
|
if (y < ((im->sy) - 1)) {
|
|
|
|
lastBorder = 1;
|
|
|
|
for (i = leftLimit; (i <= rightLimit); i++) {
|
|
|
|
int c;
|
|
|
|
c = gdImageGetPixel(im, i, y+1);
|
|
|
|
if (lastBorder) {
|
|
|
|
if (c == old) {
|
|
|
|
gdImageFill(im, i, y+1, color);
|
|
|
|
lastBorder = 0;
|
|
|
|
}
|
|
|
|
} else if (c != old) {
|
|
|
|
lastBorder = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
|
|
|
|
{
|
2006-04-05 08:42:22 -07:00
|
|
|
int x1h=x1, x1v=x1, y1h=y1, y1v=y1, x2h=x2, x2v=x2, y2h=y2, y2v=y2;
|
|
|
|
int thick = im->thick;
|
|
|
|
if (thick > 1)
|
|
|
|
{
|
|
|
|
int half = thick/2;
|
|
|
|
int half1 = thick - half;
|
|
|
|
|
|
|
|
if ( y1 < y2 )
|
|
|
|
{
|
|
|
|
y1v = y1h - half;
|
|
|
|
y2v = y2h + half1 - 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
y1v = y1h + half1 - 1;
|
|
|
|
y2v = y2h - half;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gdImageLine(im, x1h, y1h, x2h, y1h, color);
|
|
|
|
gdImageLine(im, x1h, y2h, x2h, y2h, color);
|
|
|
|
gdImageLine(im, x1v, y1v, x1v, y2v, color);
|
|
|
|
gdImageLine(im, x2v, y1v, x2v, y2v, color);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
for (y=y1; (y<=y2); y++) {
|
|
|
|
for (x=x1; (x<=x2); x++) {
|
|
|
|
gdImageSetPixel(im, x, y, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageCopy(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
int x, y;
|
|
|
|
int tox, toy;
|
|
|
|
int i;
|
|
|
|
int colorMap[gdMaxColors];
|
2006-04-05 08:42:22 -07:00
|
|
|
if (dst->trueColor) {
|
|
|
|
/* 2.0: much easier when the destination is truecolor. */
|
|
|
|
for (y = 0; (y < h); y++) {
|
|
|
|
for (x = 0; (x < w); x++) {
|
|
|
|
int c = gdImageGetTrueColorPixel(src, srcX + x,
|
|
|
|
srcY + y);
|
|
|
|
if (c != src->transparent) {
|
|
|
|
gdImageSetPixel(dst,
|
|
|
|
dstX + x,
|
|
|
|
dstY + y,
|
|
|
|
c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
for (i=0; (i<gdMaxColors); i++) {
|
|
|
|
colorMap[i] = (-1);
|
|
|
|
}
|
|
|
|
toy = dstY;
|
|
|
|
for (y=srcY; (y < (srcY + h)); y++) {
|
|
|
|
tox = dstX;
|
|
|
|
for (x=srcX; (x < (srcX + w)); x++) {
|
|
|
|
int nc;
|
|
|
|
c = gdImageGetPixel(src, x, y);
|
|
|
|
/* Added 7/24/95: support transparent copies */
|
|
|
|
if (gdImageGetTransparent(src) == c) {
|
|
|
|
tox++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Have we established a mapping for this color? */
|
|
|
|
if (colorMap[c] == (-1)) {
|
|
|
|
/* If it's the same image, mapping is trivial */
|
|
|
|
if (dst == src) {
|
|
|
|
nc = c;
|
|
|
|
} else {
|
2006-04-05 08:42:22 -07:00
|
|
|
/* Get best match possible. This
|
|
|
|
function never returns error. */
|
|
|
|
nc = gdImageColorResolveAlpha(
|
|
|
|
dst,
|
2006-04-05 08:35:53 -07:00
|
|
|
src->red[c], src->green[c],
|
2006-04-05 08:42:22 -07:00
|
|
|
src->blue[c], src->alpha[c]);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
colorMap[c] = nc;
|
|
|
|
}
|
|
|
|
gdImageSetPixel(dst, tox, toy, colorMap[c]);
|
|
|
|
tox++;
|
|
|
|
}
|
|
|
|
toy++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
/* This function is a substitute for real alpha channel operations,
|
|
|
|
so it doesn't pay attention to the alpha channel. */
|
2006-04-05 08:35:53 -07:00
|
|
|
void gdImageCopyMerge(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
|
|
|
|
{
|
|
|
|
|
|
|
|
int c, dc;
|
|
|
|
int x, y;
|
|
|
|
int tox, toy;
|
|
|
|
int ncR, ncG, ncB;
|
|
|
|
toy = dstY;
|
|
|
|
for (y=srcY; (y < (srcY + h)); y++) {
|
|
|
|
tox = dstX;
|
|
|
|
for (x=srcX; (x < (srcX + w)); x++) {
|
|
|
|
int nc;
|
|
|
|
c = gdImageGetPixel(src, x, y);
|
|
|
|
/* Added 7/24/95: support transparent copies */
|
|
|
|
if (gdImageGetTransparent(src) == c) {
|
|
|
|
tox++;
|
|
|
|
continue;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
/* If it's the same image, mapping is trivial */
|
|
|
|
if (dst == src) {
|
|
|
|
nc = c;
|
|
|
|
} else {
|
|
|
|
dc = gdImageGetPixel(dst, tox, toy);
|
|
|
|
|
|
|
|
ncR = gdImageRed(src, c) * (pct/100.0)
|
|
|
|
+ gdImageRed(dst, dc) * ((100-pct)/100.0);
|
|
|
|
ncG = gdImageGreen(src, c) * (pct/100.0)
|
|
|
|
+ gdImageGreen(dst, dc) * ((100-pct)/100.0);
|
|
|
|
ncB = gdImageBlue(src, c) * (pct/100.0)
|
|
|
|
+ gdImageBlue(dst, dc) * ((100-pct)/100.0);
|
|
|
|
|
|
|
|
/* Find a reasonable color */
|
|
|
|
nc = gdImageColorResolve(dst,ncR, ncG, ncB);
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
gdImageSetPixel(dst, tox, toy, nc);
|
|
|
|
tox++;
|
|
|
|
}
|
|
|
|
toy++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
/* This function is a substitute for real alpha channel operations,
|
|
|
|
so it doesn't pay attention to the alpha channel. */
|
2006-04-05 08:35:53 -07:00
|
|
|
void gdImageCopyMergeGray(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
|
|
|
|
{
|
|
|
|
|
|
|
|
int c, dc;
|
|
|
|
int x, y;
|
|
|
|
int tox, toy;
|
|
|
|
int ncR, ncG, ncB;
|
|
|
|
float g;
|
|
|
|
toy = dstY;
|
|
|
|
for (y=srcY; (y < (srcY + h)); y++) {
|
|
|
|
tox = dstX;
|
|
|
|
for (x=srcX; (x < (srcX + w)); x++) {
|
|
|
|
int nc;
|
|
|
|
c = gdImageGetPixel(src, x, y);
|
|
|
|
/* Added 7/24/95: support transparent copies */
|
|
|
|
if (gdImageGetTransparent(src) == c) {
|
|
|
|
tox++;
|
|
|
|
continue;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
/* If it's the same image, mapping is trivial */
|
|
|
|
if (dst == src) {
|
|
|
|
nc = c;
|
|
|
|
} else {
|
|
|
|
dc = gdImageGetPixel(dst, tox, toy);
|
|
|
|
g = 0.29900*dst->red[dc]
|
|
|
|
+ 0.58700*dst->green[dc]
|
|
|
|
+ 0.11400*dst->blue[dc];
|
|
|
|
|
|
|
|
ncR = gdImageRed(src, c) * (pct/100.0)
|
|
|
|
+ gdImageRed(dst, dc) * g *
|
|
|
|
((100-pct)/100.0);
|
|
|
|
ncG = gdImageGreen(src, c) * (pct/100.0)
|
|
|
|
+ gdImageGreen(dst, dc) * g *
|
|
|
|
((100-pct)/100.0);
|
|
|
|
ncB = gdImageBlue(src, c) * (pct/100.0)
|
|
|
|
+ gdImageBlue(dst, dc) * g *
|
|
|
|
((100-pct)/100.0);
|
|
|
|
|
|
|
|
/* First look for an exact match */
|
|
|
|
nc = gdImageColorExact(dst,ncR, ncG, ncB);
|
2006-04-05 08:35:53 -07:00
|
|
|
if (nc == (-1)) {
|
2006-04-05 08:42:22 -07:00
|
|
|
/* No, so try to allocate it */
|
|
|
|
nc = gdImageColorAllocate(dst, ncR, ncG, ncB);
|
|
|
|
/* If we're out of colors, go for the
|
|
|
|
closest color */
|
|
|
|
if (nc == (-1)) {
|
|
|
|
nc = gdImageColorClosest(dst, ncR, ncG, ncB);
|
|
|
|
}
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
gdImageSetPixel(dst, tox, toy, nc);
|
|
|
|
tox++;
|
|
|
|
}
|
|
|
|
toy++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageCopyResized(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
int x, y;
|
|
|
|
int tox, toy;
|
|
|
|
int ydest;
|
|
|
|
int i;
|
|
|
|
int colorMap[gdMaxColors];
|
|
|
|
/* Stretch vectors */
|
|
|
|
int *stx;
|
|
|
|
int *sty;
|
|
|
|
/* We only need to use floating point to determine the correct
|
|
|
|
stretch vector for one line's worth. */
|
|
|
|
double accum;
|
2006-04-05 08:41:55 -07:00
|
|
|
stx = (int *) gdMalloc(sizeof(int) * srcW);
|
|
|
|
sty = (int *) gdMalloc(sizeof(int) * srcH);
|
2006-04-05 08:35:53 -07:00
|
|
|
accum = 0;
|
|
|
|
for (i=0; (i < srcW); i++) {
|
|
|
|
int got;
|
|
|
|
accum += (double)dstW/(double)srcW;
|
|
|
|
got = (int) floor(accum);
|
|
|
|
stx[i] = got;
|
|
|
|
accum -= got;
|
|
|
|
}
|
|
|
|
accum = 0;
|
|
|
|
for (i=0; (i < srcH); i++) {
|
|
|
|
int got;
|
|
|
|
accum += (double)dstH/(double)srcH;
|
|
|
|
got = (int) floor(accum);
|
|
|
|
sty[i] = got;
|
|
|
|
accum -= got;
|
|
|
|
}
|
|
|
|
for (i=0; (i<gdMaxColors); i++) {
|
|
|
|
colorMap[i] = (-1);
|
|
|
|
}
|
|
|
|
toy = dstY;
|
|
|
|
for (y=srcY; (y < (srcY + srcH)); y++) {
|
|
|
|
for (ydest=0; (ydest < sty[y-srcY]); ydest++) {
|
|
|
|
tox = dstX;
|
|
|
|
for (x=srcX; (x < (srcX + srcW)); x++) {
|
|
|
|
int nc;
|
2006-04-05 08:42:22 -07:00
|
|
|
int mapTo;
|
2006-04-05 08:35:53 -07:00
|
|
|
if (!stx[x - srcX]) {
|
|
|
|
continue;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
if (dst->trueColor) {
|
|
|
|
int d;
|
|
|
|
mapTo = gdImageGetTrueColorPixel(src, x, y);
|
|
|
|
/* Added 7/24/95: support transparent copies */
|
|
|
|
if (gdImageGetTransparent(src) == mapTo) {
|
|
|
|
tox++;
|
|
|
|
continue;
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
} else {
|
|
|
|
c = gdImageGetPixel(src, x, y);
|
|
|
|
/* Added 7/24/95: support transparent copies */
|
|
|
|
if (gdImageGetTransparent(src) == c) {
|
|
|
|
tox += stx[x-srcX];
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (src->trueColor) {
|
|
|
|
/* Remap to the palette available in the
|
|
|
|
destination image. This is slow and
|
|
|
|
works badly. */
|
|
|
|
mapTo = gdImageColorResolveAlpha(dst,
|
|
|
|
gdTrueColorGetRed(c),
|
|
|
|
gdTrueColorGetGreen(c),
|
|
|
|
gdTrueColorGetBlue(c),
|
|
|
|
gdTrueColorGetAlpha(c));
|
|
|
|
} else {
|
|
|
|
/* Have we established a mapping for this color? */
|
|
|
|
if (colorMap[c] == (-1)) {
|
|
|
|
/* If it's the same image, mapping is trivial */
|
|
|
|
if (dst == src) {
|
|
|
|
nc = c;
|
|
|
|
} else {
|
|
|
|
/* Find or create the best match */
|
|
|
|
mapTo = gdImageColorResolveAlpha(dst,
|
|
|
|
gdTrueColorGetRed(c),
|
|
|
|
gdTrueColorGetGreen(c),
|
|
|
|
gdTrueColorGetBlue(c),
|
|
|
|
gdTrueColorGetAlpha(c));
|
|
|
|
}
|
|
|
|
colorMap[c] = nc;
|
|
|
|
}
|
|
|
|
mapTo = colorMap[c];
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i=0; (i < stx[x - srcX]); i++) {
|
2006-04-05 08:42:22 -07:00
|
|
|
gdImageSetPixel(dst, tox, toy, mapTo);
|
2006-04-05 08:35:53 -07:00
|
|
|
tox++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
toy++;
|
|
|
|
}
|
|
|
|
}
|
2006-04-05 08:41:55 -07:00
|
|
|
gdFree(stx);
|
|
|
|
gdFree(sty);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
/* When gd 1.x was first created, floating point was to be avoided.
|
|
|
|
These days it is often faster than table lookups or integer
|
|
|
|
arithmetic. The routine below is shamelessly, gloriously
|
|
|
|
floating point. TBB */
|
|
|
|
|
|
|
|
void gdImageCopyResampled(gdImagePtr dst,
|
|
|
|
gdImagePtr src,
|
|
|
|
int dstX, int dstY,
|
|
|
|
int srcX, int srcY,
|
|
|
|
int dstW, int dstH,
|
|
|
|
int srcW, int srcH)
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
float sx, sy;
|
|
|
|
if (!dst->trueColor) {
|
|
|
|
return gdImageCopyResized(
|
|
|
|
dst, src, dstX, dstY, srcX, srcY, dstW, dstH,
|
|
|
|
srcW, srcH);
|
|
|
|
}
|
|
|
|
for (y = dstY; (y < dstY + dstH); y++) {
|
|
|
|
for (x = dstX; (x < dstX + dstW); x++) {
|
|
|
|
int pd = gdImageGetPixel(dst, x, y);
|
|
|
|
float sy1, sy2, sx1, sx2;
|
|
|
|
float sx, sy;
|
|
|
|
float spixels = 0;
|
|
|
|
float red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
|
|
|
|
sy1 = ((float) y - (float) dstY) * (float) srcH /
|
|
|
|
(float) dstH;
|
|
|
|
sy2 = ((float) (y + 1) - (float) dstY) * (float) srcH /
|
|
|
|
(float) dstH;
|
|
|
|
sy = sy1;
|
|
|
|
do {
|
|
|
|
float yportion;
|
|
|
|
if (floor(sy) == floor(sy1)) {
|
|
|
|
yportion = 1.0 - (sy - floor(sy));
|
|
|
|
if (yportion > sy2 - sy1) {
|
|
|
|
yportion = sy2 - sy1;
|
|
|
|
}
|
|
|
|
sy = floor(sy);
|
|
|
|
} else if (sy == floor(sy2)) {
|
|
|
|
yportion = sy2 - floor(sy2);
|
|
|
|
} else {
|
|
|
|
yportion = 1.0;
|
|
|
|
}
|
|
|
|
sx1 = ((float) x - (float) dstX) * (float) srcW /
|
|
|
|
dstW;
|
|
|
|
sx2 = ((float) (x + 1) - (float) dstX) * (float) srcW /
|
|
|
|
dstW;
|
|
|
|
sx = sx1;
|
|
|
|
do {
|
|
|
|
float xportion;
|
|
|
|
float pcontribution;
|
|
|
|
int p;
|
|
|
|
float yportion;
|
|
|
|
if (floor(sx) == floor(sx1)) {
|
|
|
|
xportion = 1.0 - (sx - floor(sx));
|
|
|
|
if (xportion > sx2 - sx1) {
|
|
|
|
xportion = sx2 - sx1;
|
|
|
|
}
|
|
|
|
sx = floor(sx);
|
|
|
|
} else if (sx == floor(sx2)) {
|
|
|
|
xportion = sx2 - floor(sx2);
|
|
|
|
} else {
|
|
|
|
xportion = 1.0;
|
|
|
|
}
|
|
|
|
pcontribution = xportion * yportion;
|
|
|
|
p = gdImageGetTrueColorPixel(
|
|
|
|
src,
|
|
|
|
(int) sx,
|
|
|
|
(int) sy);
|
|
|
|
red += gdTrueColorGetRed(p) * pcontribution;
|
|
|
|
green += gdTrueColorGetGreen(p) * pcontribution;
|
|
|
|
blue += gdTrueColorGetBlue(p) * pcontribution;
|
|
|
|
alpha += gdTrueColorGetAlpha(p) * pcontribution;
|
|
|
|
spixels += xportion * yportion;
|
|
|
|
sx += 1.0;
|
|
|
|
} while (sx < sx2);
|
|
|
|
sy += 1.0;
|
|
|
|
} while (sy < sy2);
|
|
|
|
if (spixels != 0.0) {
|
|
|
|
red /= spixels;
|
|
|
|
green /= spixels;
|
|
|
|
blue /= spixels;
|
|
|
|
alpha /= spixels;
|
|
|
|
}
|
|
|
|
/* Clamping to allow for rounding errors above */
|
|
|
|
if (red > 255.0) {
|
|
|
|
red = 255.0;
|
|
|
|
}
|
|
|
|
if (green > 255.0) {
|
|
|
|
green = 255.0;
|
|
|
|
}
|
|
|
|
if (blue > 255.0) {
|
|
|
|
blue = 255.0;
|
|
|
|
}
|
|
|
|
if (alpha > gdAlphaMax) {
|
|
|
|
alpha = gdAlphaMax;
|
|
|
|
}
|
|
|
|
gdImageSetPixel(dst,
|
|
|
|
x, y,
|
|
|
|
gdTrueColorAlpha((int) red,
|
|
|
|
(int) green,
|
|
|
|
(int) blue,
|
|
|
|
(int) alpha));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
gdImagePtr
|
|
|
|
gdImageCreateFromXbm(FILE *fd)
|
|
|
|
{
|
|
|
|
gdImagePtr im;
|
|
|
|
int bit;
|
|
|
|
int w, h;
|
|
|
|
int bytes;
|
|
|
|
int ch;
|
|
|
|
int i, x, y;
|
|
|
|
char *sp;
|
|
|
|
char s[161];
|
|
|
|
if (!fgets(s, 160, fd)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
sp = &s[0];
|
|
|
|
/* Skip #define */
|
|
|
|
sp = strchr(sp, ' ');
|
|
|
|
if (!sp) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Skip width label */
|
|
|
|
sp++;
|
|
|
|
sp = strchr(sp, ' ');
|
|
|
|
if (!sp) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Get width */
|
|
|
|
w = atoi(sp + 1);
|
|
|
|
if (!w) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!fgets(s, 160, fd)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
sp = s;
|
|
|
|
/* Skip #define */
|
|
|
|
sp = strchr(sp, ' ');
|
|
|
|
if (!sp) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Skip height label */
|
|
|
|
sp++;
|
|
|
|
sp = strchr(sp, ' ');
|
|
|
|
if (!sp) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Get height */
|
|
|
|
h = atoi(sp + 1);
|
|
|
|
if (!h) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Skip declaration line */
|
|
|
|
if (!fgets(s, 160, fd)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
bytes = (w * h / 8) + 1;
|
|
|
|
im = gdImageCreate(w, h);
|
|
|
|
gdImageColorAllocate(im, 255, 255, 255);
|
|
|
|
gdImageColorAllocate(im, 0, 0, 0);
|
|
|
|
x = 0;
|
|
|
|
y = 0;
|
|
|
|
for (i=0; (i < bytes); i++) {
|
|
|
|
char h[3];
|
|
|
|
int b;
|
|
|
|
/* Skip spaces, commas, CRs, 0x */
|
|
|
|
while(1) {
|
|
|
|
ch = getc(fd);
|
|
|
|
if (ch == EOF) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (ch == 'x') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Get hex value */
|
|
|
|
ch = getc(fd);
|
|
|
|
if (ch == EOF) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
h[0] = ch;
|
|
|
|
ch = getc(fd);
|
|
|
|
if (ch == EOF) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
h[1] = ch;
|
|
|
|
h[2] = '\0';
|
|
|
|
sscanf(h, "%x", &b);
|
|
|
|
for (bit = 1; (bit <= 128); (bit = bit << 1)) {
|
|
|
|
gdImageSetPixel(im, x++, y, (b & bit) ? 1 : 0);
|
|
|
|
if (x == im->sx) {
|
|
|
|
x = 0;
|
|
|
|
y++;
|
|
|
|
if (y == im->sy) {
|
|
|
|
return im;
|
|
|
|
}
|
|
|
|
/* Fix 8/8/95 */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Shouldn't happen */
|
|
|
|
fprintf(stderr, "Error: bug in gdImageCreateFromXbm!\n");
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
|
|
gdImageDestroy(im);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImagePolygon(gdImagePtr im, gdPointPtr p, int n, int c)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int lx, ly;
|
|
|
|
if (!n) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
lx = p->x;
|
|
|
|
ly = p->y;
|
|
|
|
gdImageLine(im, lx, ly, p[n-1].x, p[n-1].y, c);
|
|
|
|
for (i=1; (i < n); i++) {
|
|
|
|
p++;
|
|
|
|
gdImageLine(im, lx, ly, p->x, p->y, c);
|
|
|
|
lx = p->x;
|
|
|
|
ly = p->y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int gdCompareInt(const void *a, const void *b);
|
|
|
|
|
|
|
|
/* THANKS to Kirsten Schulz for the polygon fixes! */
|
|
|
|
|
|
|
|
/* The intersection finding technique of this code could be improved */
|
2006-04-05 08:42:22 -07:00
|
|
|
/* by remembering the previous intertersection, and by using the slope. */
|
2006-04-05 08:35:53 -07:00
|
|
|
/* That could help to adjust intersections to produce a nice */
|
|
|
|
/* interior_extrema. */
|
|
|
|
|
|
|
|
void gdImageFilledPolygon(gdImagePtr im, gdPointPtr p, int n, int c)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int y;
|
|
|
|
int miny, maxy;
|
|
|
|
int x1, y1;
|
|
|
|
int x2, y2;
|
|
|
|
int ind1, ind2;
|
|
|
|
int ints;
|
|
|
|
if (!n) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!im->polyAllocated) {
|
2006-04-05 08:41:55 -07:00
|
|
|
im->polyInts = (int *) gdMalloc(sizeof(int) * n);
|
2006-04-05 08:35:53 -07:00
|
|
|
im->polyAllocated = n;
|
|
|
|
}
|
|
|
|
if (im->polyAllocated < n) {
|
|
|
|
while (im->polyAllocated < n) {
|
|
|
|
im->polyAllocated *= 2;
|
|
|
|
}
|
2006-04-05 08:41:55 -07:00
|
|
|
im->polyInts = (int *) gdRealloc(im->polyInts,
|
2006-04-05 08:35:53 -07:00
|
|
|
sizeof(int) * im->polyAllocated);
|
|
|
|
}
|
|
|
|
miny = p[0].y;
|
|
|
|
maxy = p[0].y;
|
|
|
|
for (i=1; (i < n); i++) {
|
|
|
|
if (p[i].y < miny) {
|
|
|
|
miny = p[i].y;
|
|
|
|
}
|
|
|
|
if (p[i].y > maxy) {
|
|
|
|
maxy = p[i].y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Fix in 1.3: count a vertex only once */
|
2006-04-05 08:37:05 -07:00
|
|
|
for (y=miny; (y <= maxy); y++) {
|
2006-04-05 08:35:53 -07:00
|
|
|
/*1.4 int interLast = 0; */
|
|
|
|
/* int dirLast = 0; */
|
|
|
|
/* int interFirst = 1; */
|
|
|
|
ints = 0;
|
|
|
|
for (i=0; (i < n); i++) {
|
|
|
|
if (!i) {
|
|
|
|
ind1 = n-1;
|
|
|
|
ind2 = 0;
|
|
|
|
} else {
|
|
|
|
ind1 = i-1;
|
|
|
|
ind2 = i;
|
|
|
|
}
|
|
|
|
y1 = p[ind1].y;
|
|
|
|
y2 = p[ind2].y;
|
|
|
|
if (y1 < y2) {
|
|
|
|
x1 = p[ind1].x;
|
|
|
|
x2 = p[ind2].x;
|
|
|
|
} else if (y1 > y2) {
|
|
|
|
y2 = p[ind1].y;
|
|
|
|
y1 = p[ind2].y;
|
|
|
|
x2 = p[ind1].x;
|
|
|
|
x1 = p[ind2].x;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((y >= y1) && (y < y2)) {
|
|
|
|
im->polyInts[ints++] = (y-y1) * (x2-x1) / (y2-y1) + x1;
|
2006-04-05 08:37:05 -07:00
|
|
|
} else if ((y == maxy) && (y > y1) && (y <= y2)) {
|
|
|
|
im->polyInts[ints++] = (y-y1) * (x2-x1) / (y2-y1) + x1;
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
qsort(im->polyInts, ints, sizeof(int), gdCompareInt);
|
|
|
|
|
|
|
|
for (i=0; (i < (ints)); i+=2) {
|
|
|
|
gdImageLine(im, im->polyInts[i], y,
|
|
|
|
im->polyInts[i+1], y, c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int gdCompareInt(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
return (*(const int *)a) - (*(const int *)b);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageSetStyle(gdImagePtr im, int *style, int noOfPixels)
|
|
|
|
{
|
|
|
|
if (im->style) {
|
2006-04-05 08:41:55 -07:00
|
|
|
gdFree(im->style);
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
im->style = (int *)
|
2006-04-05 08:41:55 -07:00
|
|
|
gdMalloc(sizeof(int) * noOfPixels);
|
2006-04-05 08:35:53 -07:00
|
|
|
memcpy(im->style, style, sizeof(int) * noOfPixels);
|
|
|
|
im->styleLength = noOfPixels;
|
|
|
|
im->stylePos = 0;
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
void gdImageSetThickness(gdImagePtr im, int thickness)
|
|
|
|
{
|
|
|
|
im->thick = thickness;
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:35:53 -07:00
|
|
|
void gdImageSetBrush(gdImagePtr im, gdImagePtr brush)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
im->brush = brush;
|
2006-04-05 08:42:22 -07:00
|
|
|
if ((!im->trueColor) && (!im->brush->trueColor)) {
|
|
|
|
for (i=0; (i < gdImageColorsTotal(brush)); i++) {
|
|
|
|
int index;
|
|
|
|
index = gdImageColorResolveAlpha(im,
|
2006-04-05 08:35:53 -07:00
|
|
|
gdImageRed(brush, i),
|
|
|
|
gdImageGreen(brush, i),
|
2006-04-05 08:42:22 -07:00
|
|
|
gdImageBlue(brush, i),
|
|
|
|
gdImageAlpha(brush, i));
|
|
|
|
im->brushColorMap[i] = index;
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageSetTile(gdImagePtr im, gdImagePtr tile)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
im->tile = tile;
|
2006-04-05 08:42:22 -07:00
|
|
|
if ((!im->trueColor) && (!im->tile->trueColor)) {
|
|
|
|
for (i=0; (i < gdImageColorsTotal(tile)); i++) {
|
|
|
|
int index;
|
|
|
|
index = gdImageColorResolveAlpha(im,
|
2006-04-05 08:35:53 -07:00
|
|
|
gdImageRed(tile, i),
|
|
|
|
gdImageGreen(tile, i),
|
2006-04-05 08:42:22 -07:00
|
|
|
gdImageBlue(tile, i),
|
|
|
|
gdImageAlpha(tile, i));
|
|
|
|
im->tileColorMap[i] = index;
|
2006-04-05 08:35:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageInterlace(gdImagePtr im, int interlaceArg)
|
|
|
|
{
|
|
|
|
im->interlace = interlaceArg;
|
|
|
|
}
|
2006-04-05 08:37:05 -07:00
|
|
|
|
|
|
|
int gdImageCompare(gdImagePtr im1, gdImagePtr im2)
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
int p1, p2;
|
|
|
|
int cmpStatus = 0;
|
|
|
|
int sx, sy;
|
|
|
|
|
|
|
|
if (im1->interlace != im2->interlace) {
|
|
|
|
cmpStatus |= GD_CMP_INTERLACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (im1->transparent != im2->transparent) {
|
|
|
|
cmpStatus |= GD_CMP_TRANSPARENT;
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
if (im1->trueColor != im2->trueColor) {
|
|
|
|
cmpStatus |= GD_CMP_TRUECOLOR;
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:41:55 -07:00
|
|
|
sx = im1->sx;
|
2006-04-05 08:37:05 -07:00
|
|
|
if (im1->sx != im2->sx) {
|
|
|
|
cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE;
|
2006-04-05 08:41:55 -07:00
|
|
|
if (im2->sx < im1->sx) {
|
2006-04-05 08:37:05 -07:00
|
|
|
sx = im2->sx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:41:55 -07:00
|
|
|
sy = im1->sy;
|
2006-04-05 08:37:05 -07:00
|
|
|
if (im1->sy != im2->sy) {
|
|
|
|
cmpStatus |= GD_CMP_SIZE_Y + GD_CMP_IMAGE;
|
2006-04-05 08:41:55 -07:00
|
|
|
if (im2->sy < im1->sy) {
|
2006-04-05 08:37:05 -07:00
|
|
|
sy = im2->sy;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (im1->colorsTotal != im2->colorsTotal) {
|
|
|
|
cmpStatus |= GD_CMP_NUM_COLORS;
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:41:55 -07:00
|
|
|
for ( y = 0 ; (y < sy) ; y++ ) {
|
|
|
|
for ( x = 0 ; (x < sx) ; x++ ) {
|
2006-04-05 08:37:05 -07:00
|
|
|
p1 = im1->pixels[y][x];
|
|
|
|
p2 = im2->pixels[y][x];
|
2006-04-05 08:42:22 -07:00
|
|
|
if (gdImageRed(im1, p1) != gdImageRed(im2, p2)) {
|
2006-04-05 08:37:05 -07:00
|
|
|
cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
|
|
|
|
break;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
if (gdImageGreen(im1, p1) != gdImageGreen(im2, p2)) {
|
2006-04-05 08:37:05 -07:00
|
|
|
cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
|
|
|
|
break;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
if (gdImageBlue(im1, p1) != gdImageBlue(im2, p2)) {
|
|
|
|
cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
/* Soon we'll add alpha channel to palettes */
|
|
|
|
if (gdImageAlpha(im1, p1) != gdImageAlpha(im2, p2)) {
|
2006-04-05 08:37:05 -07:00
|
|
|
cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
|
|
|
|
break;
|
|
|
|
}
|
2006-04-05 08:42:22 -07:00
|
|
|
#endif
|
2006-04-05 08:37:05 -07:00
|
|
|
}
|
|
|
|
if (cmpStatus & GD_CMP_COLOR) { break; };
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmpStatus;
|
|
|
|
}
|
|
|
|
|
2006-04-05 08:42:22 -07:00
|
|
|
int gdAlphaBlend(int dst, int src)
|
|
|
|
{
|
|
|
|
return (((((gdAlphaTransparent - gdTrueColorGetAlpha(src)) *
|
|
|
|
gdTrueColorGetRed(src) / gdAlphaMax) +
|
|
|
|
(gdTrueColorGetAlpha(src) *
|
|
|
|
gdTrueColorGetRed(dst)) / gdAlphaMax) << 16) +
|
|
|
|
((((gdAlphaTransparent - gdTrueColorGetAlpha(src)) *
|
|
|
|
gdTrueColorGetGreen(src) / gdAlphaMax) +
|
|
|
|
(gdTrueColorGetAlpha(src) *
|
|
|
|
gdTrueColorGetGreen(dst)) / gdAlphaMax) << 8) +
|
|
|
|
(((gdAlphaTransparent - gdTrueColorGetAlpha(src)) *
|
|
|
|
gdTrueColorGetBlue(src) / gdAlphaMax) +
|
|
|
|
(gdTrueColorGetAlpha(src) *
|
|
|
|
gdTrueColorGetBlue(dst)) / gdAlphaMax));
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageAlphaBlending(gdImagePtr im, int alphaBlendingArg)
|
|
|
|
{
|
|
|
|
im->alphaBlendingFlag = alphaBlendingArg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gdImageSaveAlpha(gdImagePtr im, int saveAlphaArg)
|
|
|
|
{
|
|
|
|
im->saveAlphaFlag = saveAlphaArg;
|
|
|
|
}
|
|
|
|
|