Blame src/gdfx.c

Packit ed3af9
#ifdef HAVE_CONFIG_H
Packit ed3af9
#include "config.h"
Packit ed3af9
#endif /* HAVE_CONFIG_H */
Packit ed3af9
Packit ed3af9
#include "gd.h"
Packit ed3af9
#include "gd_errors.h"
Packit ed3af9
#include <math.h>
Packit ed3af9
Packit ed3af9
/* In tests this is sufficient to prevent obvious artifacts */
Packit ed3af9
#define MAG 4
Packit ed3af9
Packit ed3af9
#define PI 3.141592
Packit ed3af9
#define DEG2RAD(x) ((x)*PI/180.)
Packit ed3af9
Packit ed3af9
#define MAX(x,y) ((x) > (y) ? (x) : (y))
Packit ed3af9
#define MIN(x,y) ((x) < (y) ? (x) : (y))
Packit ed3af9
Packit ed3af9
#define MAX4(x,y,z,w) \
Packit ed3af9
	((MAX((x),(y))) > (MAX((z),(w))) ? (MAX((x),(y))) : (MAX((z),(w))))
Packit ed3af9
#define MIN4(x,y,z,w) \
Packit ed3af9
	((MIN((x),(y))) < (MIN((z),(w))) ? (MIN((x),(y))) : (MIN((z),(w))))
Packit ed3af9
Packit ed3af9
#define MAXX(x) MAX4(x[0],x[2],x[4],x[6])
Packit ed3af9
#define MINX(x) MIN4(x[0],x[2],x[4],x[6])
Packit ed3af9
#define MAXY(x) MAX4(x[1],x[3],x[5],x[7])
Packit ed3af9
#define MINY(x) MIN4(x[1],x[3],x[5],x[7])
Packit ed3af9
Packit ed3af9
/**
Packit ed3af9
 * Function: gdImageStringFTCircle
Packit ed3af9
 *
Packit ed3af9
 * Draw text curved along the top and bottom of a circular area of an image.
Packit ed3af9
 *
Packit ed3af9
 * Parameters:
Packit ed3af9
 *  im          - The image to draw onto.
Packit ed3af9
 *  cx          - The x-coordinate of the center of the circular area.
Packit ed3af9
 *  cy          - The y-coordinate of the center of the circular area.
Packit ed3af9
 *  radius      - The radius of the circular area.
Packit ed3af9
 *  textRadius  - The height of each character; if textRadius is 1/2 of radius,
Packit ed3af9
 *	              characters extend halfway from the edge to the center.
Packit ed3af9
 *  fillPortion - The percentage of the 180 degrees of the circular area
Packit ed3af9
 *                assigned to each section of text, that is actually occupied
Packit ed3af9
 *                by text. The value has to be in range 0.0 to 1.0, with useful
Packit ed3af9
 *                values from about 0.4 to 0.9; 0.9 looks better than 1.0 which
Packit ed3af9
 *                is rather crowded.
Packit ed3af9
 *  font        - The fontlist that is passed to <gdImageStringFT>.
Packit ed3af9
 *  points      - The point size, which functions as a hint. Although the size
Packit ed3af9
 *                of the text is determined by radius, textRadius and
Packit ed3af9
 *                fillPortion, a point size that 'hints' appropriately should be
Packit ed3af9
 *                passed. If it's known that the text will be large, a large
Packit ed3af9
 *                point size such as 24.0 should be passed to get the best
Packit ed3af9
 *                results.
Packit ed3af9
 *  top         - The text to draw clockwise at the top of the circular area.
Packit ed3af9
 *  bottom      - The text to draw counterclockwise at the bottom of the
Packit ed3af9
 *                circular area.
Packit ed3af9
 *  fgcolor     - The font color.
Packit ed3af9
 *
Packit ed3af9
 * Returns:
Packit ed3af9
 *  NULL on success, or an error string on failure.
Packit ed3af9
 */
Packit ed3af9
BGD_DECLARE(char*)
Packit ed3af9
gdImageStringFTCircle (gdImagePtr im,
Packit ed3af9
                       int cx,
Packit ed3af9
                       int cy,
Packit ed3af9
                       double radius,
Packit ed3af9
                       double textRadius,
Packit ed3af9
                       double fillPortion,
Packit ed3af9
                       char *font,
Packit ed3af9
                       double points, char *top, char *bottom, int fgcolor)
Packit ed3af9
{
Packit ed3af9
	char *err;
Packit ed3af9
	int w;
Packit ed3af9
	int brect[8];
Packit ed3af9
	int sx1, sx2, sy1, sy2, sx, sy;
Packit ed3af9
	int x, y;
Packit ed3af9
	int fr, fg, fb, fa;
Packit ed3af9
	int ox, oy;
Packit ed3af9
	double prop;
Packit ed3af9
	gdImagePtr im1;
Packit ed3af9
	gdImagePtr im2;
Packit ed3af9
	gdImagePtr im3;
Packit ed3af9
	/* obtain brect so that we can size the image */
Packit ed3af9
	err = gdImageStringFT ((gdImagePtr) NULL,
Packit ed3af9
	                       &brect[0], 0, font, points * MAG, 0, 0, 0, bottom);
Packit ed3af9
	if (err) {
Packit ed3af9
		return err;
Packit ed3af9
	}
Packit ed3af9
	sx1 = MAXX (brect) - MINX (brect) + 6;
Packit ed3af9
	sy1 = MAXY (brect) - MINY (brect) + 6;
Packit ed3af9
	err = gdImageStringFT ((gdImagePtr) NULL,
Packit ed3af9
	                       &brect[0], 0, font, points * MAG, 0, 0, 0, top);
Packit ed3af9
	if (err) {
Packit ed3af9
		return err;
Packit ed3af9
	}
Packit ed3af9
	sx2 = MAXX (brect) - MINX (brect) + 6;
Packit ed3af9
	sy2 = MAXY (brect) - MINY (brect) + 6;
Packit ed3af9
	/* Pad by 4 pixels to allow for slight errors
Packit ed3af9
	   observed in the bounding box returned by freetype */
Packit ed3af9
	if (sx1 > sx2) {
Packit ed3af9
		sx = sx1 * 2 + 4;
Packit ed3af9
	} else {
Packit ed3af9
		sx = sx2 * 2 + 4;
Packit ed3af9
	}
Packit ed3af9
	if (sy1 > sy2) {
Packit ed3af9
		sy = sy1;
Packit ed3af9
	} else {
Packit ed3af9
		sy = sy2;
Packit ed3af9
	}
Packit ed3af9
	im1 = gdImageCreateTrueColor (sx, sy);
Packit ed3af9
	if (!im1) {
Packit ed3af9
		return "could not create first image";
Packit ed3af9
	}
Packit ed3af9
	err = gdImageStringFT (im1, 0, gdTrueColor (255, 255, 255),
Packit ed3af9
	                       font, points * MAG,
Packit ed3af9
	                       0, ((sx / 2) - sx1) / 2, points * MAG, bottom);
Packit ed3af9
	if (err) {
Packit ed3af9
		gdImageDestroy (im1);
Packit ed3af9
		return err;
Packit ed3af9
	}
Packit ed3af9
	/* We don't know the descent, which would be needed to do this
Packit ed3af9
	   with the angle parameter. Instead, implement a simple
Packit ed3af9
	   flip operation ourselves. */
Packit ed3af9
	err = gdImageStringFT (im1, 0, gdTrueColor (255, 255, 255),
Packit ed3af9
	                       font, points * MAG,
Packit ed3af9
	                       0, sx / 2 + ((sx / 2) - sx2) / 2, points * MAG, top);
Packit ed3af9
	if (err) {
Packit ed3af9
		gdImageDestroy (im1);
Packit ed3af9
		return err;
Packit ed3af9
	}
Packit ed3af9
	/* Flip in place is tricky, be careful not to double-swap things */
Packit ed3af9
	if (sy & 1) {
Packit ed3af9
		for (y = 0; (y <= (sy / 2)); y++) {
Packit ed3af9
			int xlimit = sx - 2;
Packit ed3af9
			if (y == (sy / 2)) {
Packit ed3af9
				/* If there is a "middle" row, be careful
Packit ed3af9
				   not to swap twice! */
Packit ed3af9
				xlimit -= (sx / 4);
Packit ed3af9
			}
Packit ed3af9
			for (x = (sx / 2) + 2; (x < xlimit); x++) {
Packit ed3af9
				int t;
Packit ed3af9
				int ox = sx - x + (sx / 2) - 1;
Packit ed3af9
				int oy = sy - y - 1;
Packit ed3af9
				t = im1->tpixels[oy][ox];
Packit ed3af9
				im1->tpixels[oy][ox] = im1->tpixels[y][x];
Packit ed3af9
				im1->tpixels[y][x] = t;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	} else {
Packit ed3af9
		for (y = 0; (y < (sy / 2)); y++) {
Packit ed3af9
			int xlimit = sx - 2;
Packit ed3af9
			for (x = (sx / 2) + 2; (x < xlimit); x++) {
Packit ed3af9
				int t;
Packit ed3af9
				int ox = sx - x + (sx / 2) - 1;
Packit ed3af9
				int oy = sy - y - 1;
Packit ed3af9
				t = im1->tpixels[oy][ox];
Packit ed3af9
				im1->tpixels[oy][ox] = im1->tpixels[y][x];
Packit ed3af9
				im1->tpixels[y][x] = t;
Packit ed3af9
			}
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
#if STEP_PNGS
Packit ed3af9
	{
Packit ed3af9
		FILE *out = fopen ("gdfx1.png", "wb");
Packit ed3af9
		gdImagePng (im1, out);
Packit ed3af9
		fclose (out);
Packit ed3af9
	}
Packit ed3af9
#endif /* STEP_PNGS */
Packit ed3af9
	/* Resample taller; the exact proportions of the text depend on the
Packit ed3af9
	   ratio of textRadius to radius, and the value of fillPortion */
Packit ed3af9
	if (sx > sy * 10) {
Packit ed3af9
		w = sx;
Packit ed3af9
	} else {
Packit ed3af9
		w = sy * 10;
Packit ed3af9
	}
Packit ed3af9
	im2 = gdImageCreateTrueColor (w, w);
Packit ed3af9
	if (!im2) {
Packit ed3af9
		gdImageDestroy (im1);
Packit ed3af9
		return "could not create resampled image";
Packit ed3af9
	}
Packit ed3af9
	prop = textRadius / radius;
Packit ed3af9
	gdImageCopyResampled (im2, im1,
Packit ed3af9
	                      gdImageSX (im2) * (1.0 - fillPortion) / 4,
Packit ed3af9
	                      sy * 10 * (1.0 - prop),
Packit ed3af9
	                      0, 0,
Packit ed3af9
	                      gdImageSX (im2) * fillPortion / 2, sy * 10 * prop,
Packit ed3af9
	                      gdImageSX (im1) / 2, gdImageSY (im1));
Packit ed3af9
	gdImageCopyResampled (im2, im1,
Packit ed3af9
	                      (gdImageSX (im2) / 2) +
Packit ed3af9
	                      gdImageSX (im2) * (1.0 - fillPortion) / 4,
Packit ed3af9
	                      sy * 10 * (1.0 - prop),
Packit ed3af9
	                      gdImageSX (im1) / 2, 0,
Packit ed3af9
	                      gdImageSX (im2) * fillPortion / 2, sy * 10 * prop,
Packit ed3af9
	                      gdImageSX (im1) / 2, gdImageSY (im1));
Packit ed3af9
#if STEP_PNGS
Packit ed3af9
	{
Packit ed3af9
		FILE *out = fopen ("gdfx2.png", "wb");
Packit ed3af9
		gdImagePng (im2, out);
Packit ed3af9
		fclose (out);
Packit ed3af9
	}
Packit ed3af9
#endif /* STEP_PNGS */
Packit ed3af9
Packit ed3af9
	gdImageDestroy (im1);
Packit ed3af9
Packit ed3af9
	/* Ready to produce a circle */
Packit ed3af9
	im3 = gdImageSquareToCircle (im2, radius);
Packit ed3af9
	if (im3 == NULL) {
Packit ed3af9
		gdImageDestroy(im2);
Packit ed3af9
		return 0;
Packit ed3af9
	}
Packit ed3af9
	gdImageDestroy (im2);
Packit ed3af9
	/* Now blend im3 with the destination. Cheat a little. The
Packit ed3af9
	   source (im3) is white-on-black, so we can use the
Packit ed3af9
	   red component as a basis for alpha as long as we're
Packit ed3af9
	   careful to shift off the extra bit and invert
Packit ed3af9
	   (alpha ranges from 0 to 127 where 0 is OPAQUE).
Packit ed3af9
	   Also be careful to allow for an alpha component
Packit ed3af9
	   in the fgcolor parameter itself (gug!) */
Packit ed3af9
	fr = gdTrueColorGetRed (fgcolor);
Packit ed3af9
	fg = gdTrueColorGetGreen (fgcolor);
Packit ed3af9
	fb = gdTrueColorGetBlue (fgcolor);
Packit ed3af9
	fa = gdTrueColorGetAlpha (fgcolor);
Packit ed3af9
	ox = cx - (im3->sx / 2);
Packit ed3af9
	oy = cy - (im3->sy / 2);
Packit ed3af9
	for (y = 0; (y < im3->sy); y++) {
Packit ed3af9
		for (x = 0; (x < im3->sx); x++) {
Packit ed3af9
			int a = gdTrueColorGetRed (im3->tpixels[y][x]) >> 1;
Packit ed3af9
			a *= (127 - fa);
Packit ed3af9
			a /= 127;
Packit ed3af9
			a = 127 - a;
Packit ed3af9
			gdImageSetPixel (im, x + ox, y + oy,
Packit ed3af9
			                 gdTrueColorAlpha (fr, fg, fb, a));
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	gdImageDestroy (im3);
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
#if GDFX_MAIN
Packit ed3af9
Packit ed3af9
int
Packit ed3af9
main (int argc, char *argv[])
Packit ed3af9
{
Packit ed3af9
	FILE *in;
Packit ed3af9
	FILE *out;
Packit ed3af9
	gdImagePtr im;
Packit ed3af9
	int radius;
Packit ed3af9
	/* Create an image of text on a circle, with an
Packit ed3af9
	   alpha channel so that we can copy it onto a
Packit ed3af9
	   background */
Packit ed3af9
	in = fopen ("eleanor.jpg", "rb");
Packit ed3af9
	if (!in) {
Packit ed3af9
		im = gdImageCreateTrueColor (300, 300);
Packit ed3af9
	} else {
Packit ed3af9
		im = gdImageCreateFromJpeg (in);
Packit ed3af9
		fclose (in);
Packit ed3af9
	}
Packit ed3af9
	if (gdImageSX (im) < gdImageSY (im)) {
Packit ed3af9
		radius = gdImageSX (im) / 2;
Packit ed3af9
	} else {
Packit ed3af9
		radius = gdImageSY (im) / 2;
Packit ed3af9
	}
Packit ed3af9
	gdImageStringFTCircle (im,
Packit ed3af9
	                       gdImageSX (im) / 2,
Packit ed3af9
	                       gdImageSY (im) / 2,
Packit ed3af9
	                       radius,
Packit ed3af9
	                       radius / 2,
Packit ed3af9
	                       0.8,
Packit ed3af9
	                       "arial",
Packit ed3af9
	                       24,
Packit ed3af9
	                       "top text",
Packit ed3af9
	                       "bottom text", gdTrueColorAlpha (240, 240, 255, 32));
Packit ed3af9
	out = fopen ("gdfx.png", "wb");
Packit ed3af9
	if (!out) {
Packit ed3af9
		gd_error("Can't create gdfx.png\n");
Packit ed3af9
		return 1;
Packit ed3af9
	}
Packit ed3af9
	gdImagePng (im, out);
Packit ed3af9
	fclose (out);
Packit ed3af9
	gdImageDestroy (im);
Packit ed3af9
	return 0;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
#endif /* GDFX_MAIN */
Packit ed3af9
Packit ed3af9
/* Note: don't change these */
Packit ed3af9
#define SUPER 2
Packit ed3af9
#define SUPERBITS1 1
Packit ed3af9
#define SUPERBITS2 2
Packit ed3af9
Packit ed3af9
/**
Packit ed3af9
 * Function: gdImageSquareToCircle
Packit ed3af9
 *
Packit ed3af9
 * Apply polar coordinate transformation to an image.
Packit ed3af9
 *
Packit ed3af9
 * The X axis of the original will be remapped to theta (angle) and the Y axis
Packit ed3af9
 * of the original will be remapped to rho (distance from center).
Packit ed3af9
 *
Packit ed3af9
 * Parameters:
Packit ed3af9
 *  im     - The image, which must be square, i.e. width == height.
Packit ed3af9
 *  radius - The radius of the new image, i.e. width == height == radius * 2.
Packit ed3af9
 *
Packit ed3af9
 * Returns:
Packit ed3af9
 *  The transformed image, or NULL on failure.
Packit ed3af9
 */
Packit ed3af9
BGD_DECLARE(gdImagePtr)
Packit ed3af9
gdImageSquareToCircle (gdImagePtr im, int radius)
Packit ed3af9
{
Packit ed3af9
	int x, y;
Packit ed3af9
	double c;
Packit ed3af9
	gdImagePtr im2;
Packit ed3af9
	if (im->sx != im->sy) {
Packit ed3af9
		/* Source image must be square */
Packit ed3af9
		return 0;
Packit ed3af9
	}
Packit ed3af9
	im2 = gdImageCreateTrueColor (radius * 2, radius * 2);
Packit ed3af9
	if (!im2) {
Packit ed3af9
		return 0;
Packit ed3af9
	}
Packit ed3af9
	/* Supersampling for a nicer result */
Packit ed3af9
	c = (im2->sx / 2) * SUPER;
Packit ed3af9
	for (y = 0; (y < im2->sy * SUPER); y++) {
Packit ed3af9
		for (x = 0; (x < im2->sx * SUPER); x++) {
Packit ed3af9
			double rho = sqrt ((x - c) * (x - c) + (y - c) * (y - c));
Packit ed3af9
			int pix;
Packit ed3af9
			int cpix;
Packit ed3af9
			double theta;
Packit ed3af9
			double ox;
Packit ed3af9
			double oy;
Packit ed3af9
			int red, green, blue, alpha;
Packit ed3af9
			if (rho > c) {
Packit ed3af9
				continue;
Packit ed3af9
			}
Packit ed3af9
			theta = atan2 (x - c, y - c) + PI / 2;
Packit ed3af9
			if (theta < 0) {
Packit ed3af9
				theta += 2 * PI;
Packit ed3af9
			}
Packit ed3af9
			/* Undo supersampling */
Packit ed3af9
			oy = (rho * im->sx) / (im2->sx * SUPER / 2);
Packit ed3af9
			ox = theta * im->sx / (3.141592653 * 2);
Packit ed3af9
			pix = gdImageGetPixel (im, ox, oy);
Packit ed3af9
			cpix = im2->tpixels[y >> SUPERBITS1][x >> SUPERBITS1];
Packit ed3af9
			red =
Packit ed3af9
			    (gdImageRed (im, pix) >> SUPERBITS2) + gdTrueColorGetRed (cpix);
Packit ed3af9
			green =
Packit ed3af9
			    (gdImageGreen (im, pix) >> SUPERBITS2) +
Packit ed3af9
			    gdTrueColorGetGreen (cpix);
Packit ed3af9
			blue =
Packit ed3af9
			    (gdImageBlue (im, pix) >> SUPERBITS2) + gdTrueColorGetBlue (cpix);
Packit ed3af9
			alpha =
Packit ed3af9
			    (gdImageAlpha (im, pix) >> SUPERBITS2) +
Packit ed3af9
			    gdTrueColorGetAlpha (cpix);
Packit ed3af9
			im2->tpixels[y >> SUPERBITS1][x >> SUPERBITS1] =
Packit ed3af9
			    gdTrueColorAlpha (red, green, blue, alpha);
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	/* Restore full dynamic range, 0-63 yields 0-252. Replication of
Packit ed3af9
	   first 2 bits in last 2 bits has the desired effect. Note
Packit ed3af9
	   slightly different arithmetic for alpha which is 7-bit.
Packit ed3af9
	   NOTE: only correct for SUPER == 2 */
Packit ed3af9
	for (y = 0; (y < im2->sy); y++) {
Packit ed3af9
		for (x = 0; (x < im2->sx); x++) {
Packit ed3af9
			/* Copy first 2 bits to last 2 bits, matching the
Packit ed3af9
			   dynamic range of the original cheaply */
Packit ed3af9
			int cpix = im2->tpixels[y][x];
Packit ed3af9
Packit ed3af9
			im2->tpixels[y][x] = gdTrueColorAlpha ((gdTrueColorGetRed (cpix) &
Packit ed3af9
			                                        0xFC) +
Packit ed3af9
			                                       ((gdTrueColorGetRed (cpix) &
Packit ed3af9
			                                               0xC0) >> 6),
Packit ed3af9
			                                       (gdTrueColorGetGreen (cpix) &
Packit ed3af9
			                                        0xFC) +
Packit ed3af9
			                                       ((gdTrueColorGetGreen (cpix)
Packit ed3af9
			                                               & 0xC0) >> 6),
Packit ed3af9
			                                       (gdTrueColorGetBlue (cpix) &
Packit ed3af9
			                                        0xFC) +
Packit ed3af9
			                                       ((gdTrueColorGetBlue (cpix) &
Packit ed3af9
			                                               0xC0) >> 6),
Packit ed3af9
			                                       (gdTrueColorGetAlpha (cpix) &
Packit ed3af9
			                                        0x7C) +
Packit ed3af9
			                                       ((gdTrueColorGetAlpha (cpix)
Packit ed3af9
			                                               & 0x60) >> 6));
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
	return im2;
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/* 2.0.16: Called by gdImageSharpen to avoid excessive code repetition
Packit ed3af9
    Added on 2003-11-19 by
Packit ed3af9
    Paul Troughton (paul<dot>troughton<at>ieee<dot>org)
Packit ed3af9
    Given filter coefficents and colours of three adjacent pixels,
Packit ed3af9
returns new colour for centre pixel
Packit ed3af9
*/
Packit ed3af9
Packit ed3af9
int
Packit ed3af9
gdImageSubSharpen (int pc, int c, int nc, float inner_coeff, float
Packit ed3af9
                   outer_coeff)
Packit ed3af9
{
Packit ed3af9
	float red, green, blue, alpha;
Packit ed3af9
Packit ed3af9
	red = inner_coeff * gdTrueColorGetRed (c) + outer_coeff *
Packit ed3af9
	      (gdTrueColorGetRed (pc) + gdTrueColorGetRed (nc));
Packit ed3af9
	green = inner_coeff * gdTrueColorGetGreen (c) + outer_coeff *
Packit ed3af9
	        (gdTrueColorGetGreen (pc) + gdTrueColorGetGreen (nc));
Packit ed3af9
	blue = inner_coeff * gdTrueColorGetBlue (c) + outer_coeff *
Packit ed3af9
	       (gdTrueColorGetBlue (pc) + gdTrueColorGetBlue (nc));
Packit ed3af9
	alpha = gdTrueColorGetAlpha (c);
Packit ed3af9
Packit ed3af9
	/* Clamping, as can overshoot bounds in either direction */
Packit ed3af9
	if (red > 255.0f) {
Packit ed3af9
		red = 255.0f;
Packit ed3af9
	}
Packit ed3af9
	if (green > 255.0f) {
Packit ed3af9
		green = 255.0f;
Packit ed3af9
	}
Packit ed3af9
	if (blue > 255.0f) {
Packit ed3af9
		blue = 255.0f;
Packit ed3af9
	}
Packit ed3af9
	if (red < 0.0f) {
Packit ed3af9
		red = 0.0f;
Packit ed3af9
	}
Packit ed3af9
	if (green < 0.0f) {
Packit ed3af9
		green = 0.0f;
Packit ed3af9
	}
Packit ed3af9
	if (blue < 0.0f) {
Packit ed3af9
		blue = 0.0f;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	return gdTrueColorAlpha ((int) red, (int) green, (int) blue, (int) alpha);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
/**
Packit ed3af9
 * Function: gdImageSharpen
Packit ed3af9
 *
Packit ed3af9
 * Sharpen an image.
Packit ed3af9
 *
Packit ed3af9
 * Uses a simple 3x3 convolution kernel and makes use of separability.
Packit ed3af9
 * It's faster, but less flexible, than full-blown unsharp masking.
Packit ed3af9
 * Silently does nothing to non-truecolor images and for pct<0, as it's not a useful blurring function.
Packit ed3af9
 *
Packit ed3af9
 * Parameters:
Packit ed3af9
 *  pct - The sharpening percentage, which can be greater than 100.
Packit ed3af9
 *
Packit ed3af9
 * Author:
Packit ed3af9
 *  Paul Troughton (paul<dot>troughton<at>ieee<dot>org)
Packit ed3af9
 */
Packit ed3af9
BGD_DECLARE(void)
Packit ed3af9
gdImageSharpen (gdImagePtr im, int pct)
Packit ed3af9
{
Packit ed3af9
	int x, y;
Packit ed3af9
	int sx, sy;
Packit ed3af9
	float inner_coeff, outer_coeff;
Packit ed3af9
Packit ed3af9
	sx = im->sx;
Packit ed3af9
	sy = im->sy;
Packit ed3af9
Packit ed3af9
	/* Must sum to 1 to avoid overall change in brightness.
Packit ed3af9
	 * Scaling chosen so that pct=100 gives 1-D filter [-1 6 -1]/4,
Packit ed3af9
	 * resulting in a 2-D filter [1 -6 1; -6 36 -6; 1 -6 1]/16,
Packit ed3af9
	 * which gives noticeable, but not excessive, sharpening
Packit ed3af9
	 */
Packit ed3af9
Packit ed3af9
	outer_coeff = -pct / 400.0;
Packit ed3af9
	inner_coeff = 1 - 2 * outer_coeff;
Packit ed3af9
Packit ed3af9
	/* Don't try to do anything with non-truecolor images, as
Packit ed3af9
	   pointless,
Packit ed3af9
	   * nor for pct<=0, as small kernel size leads to nasty
Packit ed3af9
	   artefacts when blurring
Packit ed3af9
	 */
Packit ed3af9
	if ((im->trueColor) && (pct > 0)) {
Packit ed3af9
Packit ed3af9
		/* First pass, 1-D convolution column-wise */
Packit ed3af9
		for (x = 0; x < sx; x++) {
Packit ed3af9
Packit ed3af9
			/* pc is colour of previous pixel; c of the
Packit ed3af9
			   current pixel and nc of the next */
Packit ed3af9
			int pc, c, nc;
Packit ed3af9
Packit ed3af9
			/* Replicate edge pixel at image boundary */
Packit ed3af9
			pc = gdImageGetPixel (im, x, 0);
Packit ed3af9
Packit ed3af9
			/* Stop looping before last pixel to avoid
Packit ed3af9
			   conditional within loop */
Packit ed3af9
			for (y = 0; y < sy - 1; y++) {
Packit ed3af9
Packit ed3af9
				c = gdImageGetPixel (im, x, y);
Packit ed3af9
Packit ed3af9
				nc = gdImageGetTrueColorPixel (im, x, y + 1);
Packit ed3af9
Packit ed3af9
				/* Update centre pixel to new colour */
Packit ed3af9
				gdImageSetPixel (im, x, y,
Packit ed3af9
				                 gdImageSubSharpen (pc, c, nc, inner_coeff,
Packit ed3af9
				                                    outer_coeff));
Packit ed3af9
Packit ed3af9
				/* Save original colour of current
Packit ed3af9
				   pixel for next time round */
Packit ed3af9
				pc = c;
Packit ed3af9
			}
Packit ed3af9
Packit ed3af9
			/* Deal with last pixel, replicating current
Packit ed3af9
			   pixel at image boundary */
Packit ed3af9
			c = gdImageGetPixel (im, x, y);
Packit ed3af9
			gdImageSetPixel (im, x, y, gdImageSubSharpen
Packit ed3af9
			                 (pc, c, c, inner_coeff, outer_coeff));
Packit ed3af9
		}
Packit ed3af9
Packit ed3af9
		/* Second pass, 1-D convolution row-wise */
Packit ed3af9
		for (y = 0; y < sy; y++) {
Packit ed3af9
			int pc, c;
Packit ed3af9
			pc = gdImageGetPixel (im, 0, y);
Packit ed3af9
			for (x = 0; x < sx - 1; x++) {
Packit ed3af9
				int c, nc;
Packit ed3af9
				c = gdImageGetPixel (im, x, y);
Packit ed3af9
				nc = gdImageGetTrueColorPixel (im, x + 1, y);
Packit ed3af9
				gdImageSetPixel (im, x, y,
Packit ed3af9
				                 gdImageSubSharpen (pc, c, nc, inner_coeff,
Packit ed3af9
				                                    outer_coeff));
Packit ed3af9
				pc = c;
Packit ed3af9
			}
Packit ed3af9
			c = gdImageGetPixel (im, x, y);
Packit ed3af9
			gdImageSetPixel (im, x, y, gdImageSubSharpen
Packit ed3af9
			                 (pc, c, c, inner_coeff, outer_coeff));
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
}
Packit ed3af9