summaryrefslogtreecommitdiff
path: root/Build/source/libs/gd/gdfx.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/gd/gdfx.c')
-rw-r--r--Build/source/libs/gd/gdfx.c509
1 files changed, 509 insertions, 0 deletions
diff --git a/Build/source/libs/gd/gdfx.c b/Build/source/libs/gd/gdfx.c
new file mode 100644
index 00000000000..ad51fa943a9
--- /dev/null
+++ b/Build/source/libs/gd/gdfx.c
@@ -0,0 +1,509 @@
+#include "gd.h"
+#include <math.h>
+
+/* In tests this is sufficient to prevent obvious artifacts */
+#define MAG 4
+
+#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])
+
+BGD_DECLARE(char *)
+gdImageStringFTCircle (gdImagePtr im,
+ int cx,
+ int cy,
+ double radius,
+ double textRadius,
+ double fillPortion,
+ char *font,
+ double points, char *top, char *bottom, int fgcolor)
+{
+ char *err;
+ int w;
+ int brect[8];
+ int sx1, sx2, sy1, sy2, sx, sy;
+ int x, y;
+ int fr, fg, fb, fa;
+ int ox, oy;
+ double prop;
+ gdImagePtr im1;
+ gdImagePtr im2;
+ gdImagePtr im3;
+ /* obtain brect so that we can size the image */
+ err = gdImageStringFT ((gdImagePtr) NULL,
+ &brect[0], 0, font, points * MAG, 0, 0, 0, bottom);
+ if (err)
+ {
+ return err;
+ }
+ sx1 = MAXX (brect) - MINX (brect) + 6;
+ sy1 = MAXY (brect) - MINY (brect) + 6;
+ err = gdImageStringFT ((gdImagePtr) NULL,
+ &brect[0], 0, font, points * MAG, 0, 0, 0, top);
+ if (err)
+ {
+ return err;
+ }
+ sx2 = MAXX (brect) - MINX (brect) + 6;
+ sy2 = MAXY (brect) - MINY (brect) + 6;
+ /* Pad by 4 pixels to allow for slight errors
+ observed in the bounding box returned by freetype */
+ if (sx1 > sx2)
+ {
+ sx = sx1 * 2 + 4;
+ }
+ else
+ {
+ sx = sx2 * 2 + 4;
+ }
+ if (sy1 > sy2)
+ {
+ sy = sy1;
+ }
+ else
+ {
+ sy = sy2;
+ }
+ im1 = gdImageCreateTrueColor (sx, sy);
+ if (!im1)
+ {
+ return "could not create first image";
+ }
+ err = gdImageStringFT (im1, 0, gdTrueColor (255, 255, 255),
+ font, points * MAG,
+ 0, ((sx / 2) - sx1) / 2, points * MAG, bottom);
+ if (err)
+ {
+ gdImageDestroy (im1);
+ return err;
+ }
+ /* We don't know the descent, which would be needed to do this
+ with the angle parameter. Instead, implement a simple
+ flip operation ourselves. */
+ err = gdImageStringFT (im1, 0, gdTrueColor (255, 255, 255),
+ font, points * MAG,
+ 0, sx / 2 + ((sx / 2) - sx2) / 2, points * MAG, top);
+ if (err)
+ {
+ gdImageDestroy (im1);
+ return err;
+ }
+ /* Flip in place is tricky, be careful not to double-swap things */
+ if (sy & 1)
+ {
+ for (y = 0; (y <= (sy / 2)); y++)
+ {
+ int xlimit = sx - 2;
+ if (y == (sy / 2))
+ {
+ /* If there is a "middle" row, be careful
+ not to swap twice! */
+ xlimit -= (sx / 4);
+ }
+ for (x = (sx / 2) + 2; (x < xlimit); x++)
+ {
+ int t;
+ int ox = sx - x + (sx / 2) - 1;
+ int oy = sy - y - 1;
+ t = im1->tpixels[oy][ox];
+ im1->tpixels[oy][ox] = im1->tpixels[y][x];
+ im1->tpixels[y][x] = t;
+ }
+ }
+ }
+ else
+ {
+ for (y = 0; (y < (sy / 2)); y++)
+ {
+ int xlimit = sx - 2;
+ for (x = (sx / 2) + 2; (x < xlimit); x++)
+ {
+ int t;
+ int ox = sx - x + (sx / 2) - 1;
+ int oy = sy - y - 1;
+ t = im1->tpixels[oy][ox];
+ im1->tpixels[oy][ox] = im1->tpixels[y][x];
+ im1->tpixels[y][x] = t;
+ }
+ }
+ }
+#if STEP_PNGS
+ {
+ FILE *out = fopen ("gdfx1.png", "wb");
+ gdImagePng (im1, out);
+ fclose (out);
+ }
+#endif /* STEP_PNGS */
+ /* Resample taller; the exact proportions of the text depend on the
+ ratio of textRadius to radius, and the value of fillPortion */
+ if (sx > sy * 10)
+ {
+ w = sx;
+ }
+ else
+ {
+ w = sy * 10;
+ }
+ im2 = gdImageCreateTrueColor (w, w);
+ if (!im2)
+ {
+ gdImageDestroy (im1);
+ return "could not create resampled image";
+ }
+ prop = textRadius / radius;
+ gdImageCopyResampled (im2, im1,
+ gdImageSX (im2) * (1.0 - fillPortion) / 4,
+ sy * 10 * (1.0 - prop),
+ 0, 0,
+ gdImageSX (im2) * fillPortion / 2, sy * 10 * prop,
+ gdImageSX (im1) / 2, gdImageSY (im1));
+ gdImageCopyResampled (im2, im1,
+ (gdImageSX (im2) / 2) +
+ gdImageSX (im2) * (1.0 - fillPortion) / 4,
+ sy * 10 * (1.0 - prop),
+ gdImageSX (im1) / 2, 0,
+ gdImageSX (im2) * fillPortion / 2, sy * 10 * prop,
+ gdImageSX (im1) / 2, gdImageSY (im1));
+#if STEP_PNGS
+ {
+ FILE *out = fopen ("gdfx2.png", "wb");
+ gdImagePng (im2, out);
+ fclose (out);
+ }
+#endif /* STEP_PNGS */
+ /* Ready to produce a circle */
+ im3 = gdImageSquareToCircle (im2, radius);
+ gdImageDestroy (im1);
+ gdImageDestroy (im2);
+ /* Now blend im3 with the destination. Cheat a little. The
+ source (im3) is white-on-black, so we can use the
+ red component as a basis for alpha as long as we're
+ careful to shift off the extra bit and invert
+ (alpha ranges from 0 to 127 where 0 is OPAQUE).
+ Also be careful to allow for an alpha component
+ in the fgcolor parameter itself (gug!) */
+ fr = gdTrueColorGetRed (fgcolor);
+ fg = gdTrueColorGetGreen (fgcolor);
+ fb = gdTrueColorGetBlue (fgcolor);
+ fa = gdTrueColorGetAlpha (fgcolor);
+ ox = cx - (im3->sx / 2);
+ oy = cy - (im3->sy / 2);
+ for (y = 0; (y < im3->sy); y++)
+ {
+ for (x = 0; (x < im3->sx); x++)
+ {
+ int a = gdTrueColorGetRed (im3->tpixels[y][x]) >> 1;
+ a *= (127 - fa);
+ a /= 127;
+ a = 127 - a;
+ gdImageSetPixel (im, x + ox, y + oy,
+ gdTrueColorAlpha (fr, fg, fb, a));
+ }
+ }
+ gdImageDestroy (im3);
+ return 0;
+}
+
+#if GDFX_MAIN
+
+int
+main (int argc, char *argv[])
+{
+ FILE *in;
+ FILE *out;
+ gdImagePtr im;
+ int radius;
+ /* Create an image of text on a circle, with an
+ alpha channel so that we can copy it onto a
+ background */
+ in = fopen ("eleanor.jpg", "rb");
+ if (!in)
+ {
+ im = gdImageCreateTrueColor (300, 300);
+ }
+ else
+ {
+ im = gdImageCreateFromJpeg (in);
+ fclose (in);
+ }
+ if (gdImageSX (im) < gdImageSY (im))
+ {
+ radius = gdImageSX (im) / 2;
+ }
+ else
+ {
+ radius = gdImageSY (im) / 2;
+ }
+ gdStringFTCircle (im,
+ gdImageSX (im) / 2,
+ gdImageSY (im) / 2,
+ radius,
+ radius / 2,
+ 0.8,
+ "arial",
+ 24,
+ "top text",
+ "bottom text", gdTrueColorAlpha (240, 240, 255, 32));
+ out = fopen ("gdfx.png", "wb");
+ if (!out)
+ {
+ fprintf (stderr, "Can't create gdfx.png\n");
+ return 1;
+ }
+ gdImagePng (im, out);
+ fclose (out);
+ gdImageDestroy (im);
+ return 0;
+}
+
+#endif /* GDFX_MAIN */
+
+/* Note: don't change these */
+#define SUPER 2
+#define SUPERBITS1 1
+#define SUPERBITS2 2
+
+BGD_DECLARE(gdImagePtr)
+gdImageSquareToCircle (gdImagePtr im, int radius)
+{
+ int x, y;
+ double c;
+ gdImagePtr im2;
+ if (im->sx != im->sy)
+ {
+ /* Source image must be square */
+ return 0;
+ }
+ im2 = gdImageCreateTrueColor (radius * 2, radius * 2);
+ /* Supersampling for a nicer result */
+ c = (im2->sx / 2) * SUPER;
+ for (y = 0; (y < im2->sy * SUPER); y++)
+ {
+ for (x = 0; (x < im2->sx * SUPER); x++)
+ {
+ double rho = sqrt ((x - c) * (x - c) + (y - c) * (y - c));
+ int pix;
+ int cpix;
+ double theta;
+ double ox;
+ double oy;
+ int red, green, blue, alpha;
+ if (rho > c)
+ {
+ continue;
+ }
+ theta = atan2 (x - c, y - c) + PI / 2;
+ if (theta < 0)
+ {
+ theta += 2 * PI;
+ }
+ /* Undo supersampling */
+ oy = (rho * im->sx) / (im2->sx * SUPER / 2);
+ ox = theta * im->sx / (3.141592653 * 2);
+ pix = gdImageGetPixel (im, ox, oy);
+ cpix = im2->tpixels[y >> SUPERBITS1][x >> SUPERBITS1];
+ red =
+ (gdImageRed (im, pix) >> SUPERBITS2) + gdTrueColorGetRed (cpix);
+ green =
+ (gdImageGreen (im, pix) >> SUPERBITS2) +
+ gdTrueColorGetGreen (cpix);
+ blue =
+ (gdImageBlue (im, pix) >> SUPERBITS2) + gdTrueColorGetBlue (cpix);
+ alpha =
+ (gdImageAlpha (im, pix) >> SUPERBITS2) +
+ gdTrueColorGetAlpha (cpix);
+ im2->tpixels[y >> SUPERBITS1][x >> SUPERBITS1] =
+ gdTrueColorAlpha (red, green, blue, alpha);
+ }
+ }
+ /* Restore full dynamic range, 0-63 yields 0-252. Replication of
+ first 2 bits in last 2 bits has the desired effect. Note
+ slightly different arithmetic for alpha which is 7-bit.
+ NOTE: only correct for SUPER == 2 */
+ for (y = 0; (y < im2->sy); y++)
+ {
+ for (x = 0; (x < im2->sx); x++)
+ {
+ /* Copy first 2 bits to last 2 bits, matching the
+ dynamic range of the original cheaply */
+ int cpix = im2->tpixels[y][x];
+
+ im2->tpixels[y][x] = gdTrueColorAlpha ((gdTrueColorGetRed (cpix) &
+ 0xFC) +
+ ((gdTrueColorGetRed (cpix) &
+ 0xC0) >> 6),
+ (gdTrueColorGetGreen (cpix) &
+ 0xFC) +
+ ((gdTrueColorGetGreen (cpix)
+ & 0xC0) >> 6),
+ (gdTrueColorGetBlue (cpix) &
+ 0xFC) +
+ ((gdTrueColorGetBlue (cpix) &
+ 0xC0) >> 6),
+ (gdTrueColorGetAlpha (cpix) &
+ 0x7C) +
+ ((gdTrueColorGetAlpha (cpix)
+ & 0x60) >> 6));
+ }
+ }
+ return im2;
+}
+
+/* 2.0.16: Called by gdImageSharpen to avoid excessive code repetition
+ Added on 2003-11-19 by
+ Paul Troughton (paul<dot>troughton<at>ieee<dot>org)
+ Given filter coefficents and colours of three adjacent pixels,
+returns new colour for centre pixel
+*/
+
+int
+gdImageSubSharpen (int pc, int c, int nc, float inner_coeff, float
+ outer_coeff)
+{
+ float red, green, blue, alpha;
+
+ red = inner_coeff * gdTrueColorGetRed (c) + outer_coeff *
+ (gdTrueColorGetRed (pc) + gdTrueColorGetRed (nc));
+ green = inner_coeff * gdTrueColorGetGreen (c) + outer_coeff *
+ (gdTrueColorGetGreen (pc) + gdTrueColorGetGreen (nc));
+ blue = inner_coeff * gdTrueColorGetBlue (c) + outer_coeff *
+ (gdTrueColorGetBlue (pc) + gdTrueColorGetBlue (nc));
+ alpha = gdTrueColorGetAlpha (c);
+
+ /* Clamping, as can overshoot bounds in either direction */
+ if (red > 255.0f)
+ {
+ red = 255.0f;
+ }
+ if (green > 255.0f)
+ {
+ green = 255.0f;
+ }
+ if (blue > 255.0f)
+ {
+ blue = 255.0f;
+ }
+ if (red < 0.0f)
+ {
+ red = 0.0f;
+ }
+ if (green < 0.0f)
+ {
+ green = 0.0f;
+ }
+ if (blue < 0.0f)
+ {
+ blue = 0.0f;
+ }
+
+ return gdTrueColorAlpha ((int) red, (int) green, (int) blue, (int) alpha);
+}
+
+/*
+ * Sharpen function added on 2003-11-19
+ * by Paul Troughton (paul<dot>troughton<at>ieee<dot>org)
+ * Simple 3x3 convolution kernel
+ * Makes use of seperability
+ * Faster, but less flexible, than full-blown unsharp masking
+ * pct is sharpening percentage, and can be greater than 100
+ * Silently does nothing to non-truecolor images
+ * Silently does nothing for pct<0, as not a useful blurring function
+ * Leaves transparency/alpha-channel untouched
+ */
+BGD_DECLARE(void)
+gdImageSharpen (gdImagePtr im, int pct)
+{
+ int x, y;
+ int sx, sy;
+ float inner_coeff, outer_coeff;
+
+ sx = im->sx;
+ sy = im->sy;
+
+ /* Must sum to 1 to avoid overall change in brightness.
+ * Scaling chosen so that pct=100 gives 1-D filter [-1 6 -1]/4,
+ * resulting in a 2-D filter [1 -6 1; -6 36 -6; 1 -6 1]/16,
+ * which gives noticeable, but not excessive, sharpening
+ */
+
+ outer_coeff = -pct / 400.0;
+ inner_coeff = 1 - 2 * outer_coeff;
+
+ /* Don't try to do anything with non-truecolor images, as
+ pointless,
+ * nor for pct<=0, as small kernel size leads to nasty
+ artefacts when blurring
+ */
+ if ((im->trueColor) && (pct > 0))
+ {
+
+ /* First pass, 1-D convolution column-wise */
+ for (x = 0; x < sx; x++)
+ {
+
+ /* pc is colour of previous pixel; c of the
+ current pixel and nc of the next */
+ int pc, c, nc;
+
+ /* Replicate edge pixel at image boundary */
+ pc = gdImageGetPixel (im, x, 0);
+
+ /* Stop looping before last pixel to avoid
+ conditional within loop */
+ for (y = 0; y < sy - 1; y++)
+ {
+
+ c = gdImageGetPixel (im, x, y);
+
+ nc = gdImageGetTrueColorPixel (im, x, y + 1);
+
+ /* Update centre pixel to new colour */
+ gdImageSetPixel (im, x, y,
+ gdImageSubSharpen (pc, c, nc, inner_coeff,
+ outer_coeff));
+
+ /* Save original colour of current
+ pixel for next time round */
+ pc = c;
+ }
+
+ /* Deal with last pixel, replicating current
+ pixel at image boundary */
+ c = gdImageGetPixel (im, x, y);
+ gdImageSetPixel (im, x, y, gdImageSubSharpen
+ (pc, c, c, inner_coeff, outer_coeff));
+ }
+
+ /* Second pass, 1-D convolution row-wise */
+ for (y = 0; y < sy; y++)
+ {
+ int pc, c;
+ pc = gdImageGetPixel (im, 0, y);
+ for (x = 0; x < sx - 1; x++)
+ {
+ int c, nc;
+ c = gdImageGetPixel (im, x, y);
+ nc = gdImageGetTrueColorPixel (im, x + 1, y);
+ gdImageSetPixel (im, x, y,
+ gdImageSubSharpen (pc, c, nc, inner_coeff,
+ outer_coeff));
+ pc = c;
+ }
+ c = gdImageGetPixel (im, x, y);
+ gdImageSetPixel (im, x, y, gdImageSubSharpen
+ (pc, c, c, inner_coeff, outer_coeff));
+ }
+ }
+}