diff options
Diffstat (limited to 'Build/source/libs/gd/gdfx.c')
-rw-r--r-- | Build/source/libs/gd/gdfx.c | 509 |
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)); + } + } +} |