summaryrefslogtreecommitdiff
path: root/ext/gd/libgd
diff options
context:
space:
mode:
Diffstat (limited to 'ext/gd/libgd')
-rw-r--r--ext/gd/libgd/gd.c86
-rw-r--r--ext/gd/libgd/gd.h208
-rw-r--r--ext/gd/libgd/gd_compat.c35
-rw-r--r--ext/gd/libgd/gd_compat.h66
-rw-r--r--ext/gd/libgd/gd_crop.c369
-rw-r--r--ext/gd/libgd/gd_interpolation.c2550
-rw-r--r--ext/gd/libgd/gd_jpeg.c25
-rw-r--r--ext/gd/libgd/gd_matrix.c334
-rw-r--r--ext/gd/libgd/gd_png.c2
-rw-r--r--ext/gd/libgd/gd_transform.c73
-rw-r--r--ext/gd/libgd/gd_webp.c33
11 files changed, 3653 insertions, 128 deletions
diff --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c
index fa75898ddb..54890bc177 100644
--- a/ext/gd/libgd/gd.c
+++ b/ext/gd/libgd/gd.c
@@ -168,6 +168,8 @@ gdImagePtr gdImageCreate (int sx, int sy)
im->cy1 = 0;
im->cx2 = im->sx - 1;
im->cy2 = im->sy - 1;
+ im->interpolation = NULL;
+ im->interpolation_id = GD_BILINEAR_FIXED;
return im;
}
@@ -183,7 +185,7 @@ gdImagePtr gdImageCreateTrueColor (int sx, int sy)
if (overflow2(sizeof(unsigned char *), sy)) {
return NULL;
}
-
+
if (overflow2(sizeof(int), sx)) {
return NULL;
}
@@ -221,6 +223,8 @@ gdImagePtr gdImageCreateTrueColor (int sx, int sy)
im->cy1 = 0;
im->cx2 = im->sx - 1;
im->cy2 = im->sy - 1;
+ im->interpolation = NULL;
+ im->interpolation_id = GD_BILINEAR_FIXED;
return im;
}
@@ -1954,7 +1958,6 @@ static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc)
{
int i, l, x1, x2, dy;
int oc; /* old pixel value */
- int tiled;
int wx2,wy2;
/* stack of filled segments */
struct seg *stack;
@@ -1966,7 +1969,6 @@ static void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc)
}
wx2=im->sx;wy2=im->sy;
- tiled = nc==gdTiled;
nc = gdImageTileGet(im,x,y);
@@ -2031,7 +2033,6 @@ void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
{
int x1h = x1, x1v = x1, y1h = y1, y1v = y1, x2h = x2, x2v = x2, y2h = y2, y2v = y2;
int thick = im->thick;
- int half1 = 1;
int t;
if (x1 == x2 && y1 == y2 && thick == 1) {
@@ -2053,7 +2054,7 @@ void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
if (thick > 1) {
int cx, cy, x1ul, y1ul, x2lr, y2lr;
int half = thick >> 1;
- half1 = thick - half;
+
x1ul = x1 - half;
y1ul = y1 - half;
@@ -2351,8 +2352,6 @@ void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int
int colorMap[gdMaxColors];
/* Stretch vectors */
int *stx, *sty;
- /* We only need to use floating point to determine the correct stretch vector for one line's worth. */
- double accum;
if (overflow2(sizeof(int), srcW)) {
return;
@@ -2363,7 +2362,6 @@ void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int
stx = (int *) gdMalloc (sizeof (int) * srcW);
sty = (int *) gdMalloc (sizeof (int) * srcH);
- accum = 0;
/* Fixed by Mao Morimoto 2.0.16 */
for (i = 0; (i < srcW); i++) {
@@ -3009,3 +3007,75 @@ void gdImageGetClip (gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P)
*y2P = im->cy2;
}
+/* convert a palette image to true color */
+int gdImagePaletteToTrueColor(gdImagePtr src)
+{
+ unsigned int y;
+ unsigned int yy;
+
+ if (src == NULL) {
+ return 0;
+ }
+
+ if (src->trueColor == 1) {
+ return 1;
+ } else {
+ unsigned int x;
+ const unsigned int sy = gdImageSY(src);
+ const unsigned int sx = gdImageSX(src);
+
+ src->tpixels = (int **) gdMalloc(sizeof(int *) * sy);
+ if (src->tpixels == NULL) {
+ return 0;
+ }
+
+ for (y = 0; y < sy; y++) {
+ const unsigned char *src_row = src->pixels[y];
+ int * dst_row;
+
+ /* no need to calloc it, we overwrite all pxl anyway */
+ src->tpixels[y] = (int *) gdMalloc(sx * sizeof(int));
+ if (src->tpixels[y] == NULL) {
+ goto clean_on_error;
+ }
+
+ dst_row = src->tpixels[y];
+ for (x = 0; x < sx; x++) {
+ const unsigned char c = *(src_row + x);
+ if (c == src->transparent) {
+ *(dst_row + x) = gdTrueColorAlpha(0, 0, 0, 127);
+ } else {
+ *(dst_row + x) = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
+ }
+ }
+ }
+ }
+
+ /* free old palette buffer */
+ for (yy = y - 1; yy >= yy - 1; yy--) {
+ gdFree(src->pixels[yy]);
+ }
+ gdFree(src->pixels);
+ src->trueColor = 1;
+ src->pixels = NULL;
+ src->alphaBlendingFlag = 0;
+ src->saveAlphaFlag = 1;
+
+ if (src->transparent >= 0) {
+ const unsigned char c = src->transparent;
+ src->transparent = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]);
+ }
+
+ return 1;
+
+clean_on_error:
+ if (y > 0) {
+
+ for (yy = y; yy >= yy - 1; y--) {
+ gdFree(src->tpixels[y]);
+ }
+ gdFree(src->tpixels);
+ }
+ return 0;
+}
+
diff --git a/ext/gd/libgd/gd.h b/ext/gd/libgd/gd.h
index 8aedc2c38c..72515108d6 100644
--- a/ext/gd/libgd/gd.h
+++ b/ext/gd/libgd/gd.h
@@ -93,6 +93,10 @@ void php_gd_error(const char *format, ...);
#define gdEffectNormal 2
#define gdEffectOverlay 3
+#define GD_TRUE 1
+#define GD_FALSE 0
+
+#define GD_EPSILON 1e-6
/* This function accepts truecolor pixel values only. The
source color is composited with the destination color
@@ -101,6 +105,67 @@ void php_gd_error(const char *format, ...);
int gdAlphaBlend(int dest, int src);
+/**
+ * Group: Transform
+ *
+ * Constants: gdInterpolationMethod
+
+ * GD_BELL - Bell
+ * GD_BESSEL - Bessel
+ * GD_BILINEAR_FIXED - fixed point bilinear
+ * GD_BICUBIC - Bicubic
+ * GD_BICUBIC_FIXED - fixed point bicubic integer
+ * GD_BLACKMAN - Blackman
+ * GD_BOX - Box
+ * GD_BSPLINE - BSpline
+ * GD_CATMULLROM - Catmullrom
+ * GD_GAUSSIAN - Gaussian
+ * GD_GENERALIZED_CUBIC - Generalized cubic
+ * GD_HERMITE - Hermite
+ * GD_HAMMING - Hamming
+ * GD_HANNING - Hannig
+ * GD_MITCHELL - Mitchell
+ * GD_NEAREST_NEIGHBOUR - Nearest neighbour interpolation
+ * GD_POWER - Power
+ * GD_QUADRATIC - Quadratic
+ * GD_SINC - Sinc
+ * GD_TRIANGLE - Triangle
+ * GD_WEIGHTED4 - 4 pixels weighted bilinear interpolation
+ *
+ * See also:
+ * <gdSetInterpolationMethod>
+ **/
+typedef enum {
+ GD_DEFAULT = 0,
+ GD_BELL,
+ GD_BESSEL,
+ GD_BILINEAR_FIXED,
+ GD_BICUBIC,
+ GD_BICUBIC_FIXED,
+ GD_BLACKMAN,
+ GD_BOX,
+ GD_BSPLINE,
+ GD_CATMULLROM,
+ GD_GAUSSIAN,
+ GD_GENERALIZED_CUBIC,
+ GD_HERMITE,
+ GD_HAMMING,
+ GD_HANNING,
+ GD_MITCHELL,
+ GD_NEAREST_NEIGHBOUR,
+ GD_POWER,
+ GD_QUADRATIC,
+ GD_SINC,
+ GD_TRIANGLE,
+ GD_WEIGHTED4,
+ GD_METHOD_COUNT = 21
+} gdInterpolationMethod;
+
+/* define struct with name and func ptr and add it to gdImageStruct gdInterpolationMethod interpolation; */
+
+/* Interpolation function ptr */
+typedef double (* interpolation_method )(double);
+
typedef struct gdImageStruct {
/* Palette-based image pixels */
unsigned char ** pixels;
@@ -188,10 +253,35 @@ typedef struct gdImageStruct {
int cy1;
int cx2;
int cy2;
+ gdInterpolationMethod interpolation_id;
+ interpolation_method interpolation;
} gdImage;
typedef gdImage * gdImagePtr;
+/* Point type for use in polygon drawing. */
+
+/**
+ * Group: Types
+ *
+ * typedef: gdPointF
+ * Defines a point in a 2D coordinate system using floating point
+ * values.
+ * x - Floating point position (increase from left to right)
+ * y - Floating point Row position (increase from top to bottom)
+ *
+ * typedef: gdPointFPtr
+ * Pointer to a <gdPointF>
+ *
+ * See also:
+ * <gdImageCreate>, <gdImageCreateTrueColor>,
+ **/
+typedef struct
+{
+ double x, y;
+}
+gdPointF, *gdPointFPtr;
+
typedef struct {
/* # of characters in font */
int nchars;
@@ -209,6 +299,31 @@ typedef struct {
/* Text functions take these. */
typedef gdFont *gdFontPtr;
+
+/**
+ * Group: Types
+ *
+ * typedef: gdRect
+ * Defines a rectilinear region.
+ *
+ * x - left position
+ * y - right position
+ * width - Rectangle width
+ * height - Rectangle height
+ *
+ * typedef: gdRectPtr
+ * Pointer to a <gdRect>
+ *
+ * See also:
+ * <gdSetInterpolationMethod>
+ **/
+typedef struct
+{
+ int x, y;
+ int width, height;
+}
+gdRect, *gdRectPtr;
+
/* For backwards compatibility only. Use gdImageSetStyle()
for MUCH more flexible line drawing. Also see
gdImageSetBrush(). */
@@ -247,8 +362,12 @@ gdImagePtr gdImageCreateFromPng(FILE *fd);
gdImagePtr gdImageCreateFromPngCtx(gdIOCtxPtr in);
gdImagePtr gdImageCreateFromWBMP(FILE *inFile);
gdImagePtr gdImageCreateFromWBMPCtx(gdIOCtx *infile);
-gdImagePtr gdImageCreateFromJpeg(FILE *infile, int ignore_warning);
-gdImagePtr gdImageCreateFromJpegCtx(gdIOCtx *infile, int ignore_warning);
+gdImagePtr gdImageCreateFromJpeg(FILE *infile);
+gdImagePtr gdImageCreateFromJpegEx(FILE *infile, int ignore_warning);
+gdImagePtr gdImageCreateFromJpegCtx(gdIOCtx *infile);
+gdImagePtr gdImageCreateFromJpegCtxEx(gdIOCtx *infile, int ignore_warning);
+gdImagePtr gdImageCreateFromJpegPtr (int size, void *data);
+gdImagePtr gdImageCreateFromJpegPtrEx (int size, void *data, int ignore_warning);
gdImagePtr gdImageCreateFromWebp(FILE *fd);
gdImagePtr gdImageCreateFromWebpCtx(gdIOCtxPtr in);
gdImagePtr gdImageCreateFromWebpPtr (int size, void *data);
@@ -444,7 +563,7 @@ void gdImageColorDeallocate(gdImagePtr im, int color);
gdImagePtr gdImageCreatePaletteFromTrueColor (gdImagePtr im, int ditherFlag, int colorsWanted);
void gdImageTrueColorToPalette(gdImagePtr im, int ditherFlag, int colorsWanted);
-
+int gdImagePaletteToTrueColor(gdImagePtr src);
/* An attempt at getting the results of gdImageTrueColorToPalette
to look a bit more like the original (im1 is the original
@@ -575,6 +694,7 @@ gdImagePtr gdImageRotate180(gdImagePtr src, int ignoretransparent);
gdImagePtr gdImageRotate270(gdImagePtr src, int ignoretransparent);
gdImagePtr gdImageRotate45(gdImagePtr src, double dAngle, int clrBack, int ignoretransparent);
gdImagePtr gdImageRotate (gdImagePtr src, double dAngle, int clrBack, int ignoretransparent);
+gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor);
void gdImageSetBrush(gdImagePtr im, gdImagePtr brush);
void gdImageSetTile(gdImagePtr im, gdImagePtr tile);
@@ -682,6 +802,88 @@ int gdImageSmooth(gdImagePtr im, float weight);
/* Image comparison definitions */
int gdImageCompare(gdImagePtr im1, gdImagePtr im2);
+void gdImageFlipHorizontal(gdImagePtr im);
+void gdImageFlipVertical(gdImagePtr im);
+void gdImageFlipBoth(gdImagePtr im);
+
+#define GD_FLIP_HORINZONTAL 1
+#define GD_FLIP_VERTICAL 2
+#define GD_FLIP_BOTH 3
+
+/**
+ * Group: Crop
+ *
+ * Constants: gdCropMode
+ * GD_CROP_DEFAULT - Default crop mode (4 corners or background)
+ * GD_CROP_TRANSPARENT - Crop using the transparent color
+ * GD_CROP_BLACK - Crop black borders
+ * GD_CROP_WHITE - Crop white borders
+ * GD_CROP_SIDES - Crop using colors of the 4 corners
+ *
+ * See also:
+ * <gdImageAutoCrop>
+ **/
+enum gdCropMode {
+ GD_CROP_DEFAULT = 0,
+ GD_CROP_TRANSPARENT,
+ GD_CROP_BLACK,
+ GD_CROP_WHITE,
+ GD_CROP_SIDES,
+ GD_CROP_THRESHOLD
+};
+
+gdImagePtr gdImageCrop(gdImagePtr src, const gdRectPtr crop);
+gdImagePtr gdImageCropAuto(gdImagePtr im, const unsigned int mode);
+gdImagePtr gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold);
+
+int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id);
+
+gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height);
+gdImagePtr gdImageScaleBicubic(gdImagePtr src_img, const unsigned int new_width, const unsigned int new_height);
+gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height);
+gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, const unsigned int height);
+gdImagePtr gdImageScaleTwoPass(const gdImagePtr pOrigImage, const unsigned int uOrigWidth, const unsigned int uOrigHeight, const unsigned int uNewWidth, const unsigned int uNewHeight);
+gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height);
+
+gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor);
+gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int bgColor);
+gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const int bgColor);
+gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor);
+
+
+
+typedef enum {
+ GD_AFFINE_TRANSLATE = 0,
+ GD_AFFINE_SCALE,
+ GD_AFFINE_ROTATE,
+ GD_AFFINE_SHEAR_HORIZONTAL,
+ GD_AFFINE_SHEAR_VERTICAL,
+} gdAffineStandardMatrix;
+
+int gdAffineApplyToPointF (gdPointFPtr dst, const gdPointFPtr src, const double affine[6]);
+int gdAffineInvert (double dst[6], const double src[6]);
+int gdAffineFlip (double dst_affine[6], const double src_affine[6], const int flip_h, const int flip_v);
+int gdAffineConcat (double dst[6], const double m1[6], const double m2[6]);
+
+int gdAffineIdentity (double dst[6]);
+int gdAffineScale (double dst[6], const double scale_x, const double scale_y);
+int gdAffineRotate (double dst[6], const double angle);
+int gdAffineShearHorizontal (double dst[6], const double angle);
+int gdAffineShearVertical(double dst[6], const double angle);
+int gdAffineTranslate (double dst[6], const double offset_x, const double offset_y);
+double gdAffineExpansion (const double src[6]);
+int gdAffineRectilinear (const double src[6]);
+int gdAffineEqual (const double matrix1[6], const double matrix2[6]);
+int gdTransformAffineGetImage(gdImagePtr *dst, const gdImagePtr src, gdRectPtr src_area, const double affine[6]);
+int gdTransformAffineCopy(gdImagePtr dst, int dst_x, int dst_y, const gdImagePtr src, gdRectPtr src_region, const double affine[6]);
+/*
+gdTransformAffineCopy(gdImagePtr dst, int x0, int y0, int x1, int y1,
+ const gdImagePtr src, int src_width, int src_height,
+ const double affine[6]);
+*/
+int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox);
+
+
#define GD_CMP_IMAGE 1 /* Actual image IS different */
#define GD_CMP_NUM_COLORS 2 /* Number of Colours in pallette differ */
#define GD_CMP_COLOR 4 /* Image colours differ */
diff --git a/ext/gd/libgd/gd_compat.c b/ext/gd/libgd/gd_compat.c
deleted file mode 100644
index 473ea203e5..0000000000
--- a/ext/gd/libgd/gd_compat.c
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "php_config.h"
-#ifdef HAVE_GD_PNG
-/* needs to be first */
-# include <png.h>
-#endif
-
-#ifdef HAVE_GD_JPG
-# include <jpeglib.h>
-#endif
-
-#ifdef HAVE_GD_JPG
-int gdJpegGetVersionInt()
-{
- return JPEG_LIB_VERSION;
-}
-
-const char * gdJpegGetVersionString()
-{
- switch(JPEG_LIB_VERSION) {
- case 62:
- return "6b";
- break;
- default:
- return "unknown";
- }
-}
-#endif
-
-#ifdef HAVE_GD_PNG
-const char * gdPngGetVersionString()
-{
- return PNG_LIBPNG_VER_STRING;
-}
-#endif
-
diff --git a/ext/gd/libgd/gd_compat.h b/ext/gd/libgd/gd_compat.h
deleted file mode 100644
index 779e709a63..0000000000
--- a/ext/gd/libgd/gd_compat.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef GD_COMPAT_H
-#define GD_COMPAT_H 1
-
-#if HAVE_GD_BUNDLED
-# include "gd.h"
-#else
-# include <gd.h>
-#endif
-
-const char * gdPngGetVersionString();
-const char * gdJpegGetVersionString();
-int gdJpegGetVersionInt();
-int overflow2(int a, int b);
-
-/* filters section
- *
- * Negate the imag src, white becomes black,
- * The red, green, and blue intensities of an image are negated.
- * White becomes black, yellow becomes blue, etc.
- */
-int gdImageNegate(gdImagePtr src);
-
-/* Convert the image src to a grayscale image */
-int gdImageGrayScale(gdImagePtr src);
-
-/* Set the brightness level <brightness> for the image src */
-int gdImageBrightness(gdImagePtr src, int brightness);
-
-/* Set the contrast level <contrast> for the image <src> */
-int gdImageContrast(gdImagePtr src, double contrast);
-
-/* Simply adds or substracts respectively red, green or blue to a pixel */
-int gdImageColor(gdImagePtr src, const int red, const int green, const int blue, const int alpha);
-
-#if !defined(HAVE_GD_IMAGE_CONVOLUTION)
-/* Image convolution by a 3x3 custom matrix */
-int gdImageConvolution(gdImagePtr src, float ft[3][3], float filter_div, float offset);
-int gdImageEdgeDetectQuick(gdImagePtr src);
-int gdImageGaussianBlur(gdImagePtr im);
-int gdImageSelectiveBlur( gdImagePtr src);
-int gdImageEmboss(gdImagePtr im);
-int gdImageMeanRemoval(gdImagePtr im);
-int gdImageSmooth(gdImagePtr im, float weight);
-#endif
-
-#if !defined(HAVE_GD_IMAGE_PIXELATE)
-enum gdPixelateMode {
- GD_PIXELATE_UPPERLEFT,
- GD_PIXELATE_AVERAGE
-};
-
-int gdImagePixelate(gdImagePtr im, int block_size, const unsigned int mode);
-#endif
-
-int gdImagePixelate(gdImagePtr im, int block_size, const unsigned int mode);
-
-#if !HAVE_GD_IMAGEELLIPSE
-void gdImageEllipse(gdImagePtr im, int cx, int cy, int w, int h, int c);
-#endif
-
-gdImagePtr gdImageRotate (gdImagePtr src, double dAngle, int clrBack, int ignoretransparent);
-
-int gdImageColorMatch (gdImagePtr im1, gdImagePtr im2);
-
-#endif
-
diff --git a/ext/gd/libgd/gd_crop.c b/ext/gd/libgd/gd_crop.c
new file mode 100644
index 0000000000..bba425d0e3
--- /dev/null
+++ b/ext/gd/libgd/gd_crop.c
@@ -0,0 +1,369 @@
+/**
+ * Title: Crop
+ *
+ * A couple of functions to crop images, automatically (auto detection of
+ * the borders color), using a given color (with or without tolerance)
+ * or using a selection.
+ *
+ * The threshold method works relatively well but it can be improved.
+ * Maybe L*a*b* and Delta-E will give better results (and a better
+ * granularity).
+ *
+ * Example:
+ * (start code)
+ * im2 = gdImageAutoCrop(im, GD_CROP_SIDES);
+ * if (im2) {
+
+ * }
+ * gdImageDestroy(im2);
+ * (end code)
+ **/
+
+#include <gd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color);
+static int gdColorMatch(gdImagePtr im, int col1, int col2, float threshold);
+
+/**
+ * Function: gdImageCrop
+ * Crops the src image using the area defined by the <crop> rectangle.
+ * The result is returned as a new image.
+ *
+ *
+ * Parameters:
+ * src - Source image
+ * crop - Rectangular region to crop
+ *
+ * Returns:
+ * <gdImagePtr> on success or NULL
+ */
+gdImagePtr gdImageCrop(gdImagePtr src, const gdRectPtr crop)
+{
+ gdImagePtr dst;
+ int y;
+
+ /* check size */
+ if (crop->width<=0 || crop->height<=0) {
+ return NULL;
+ }
+
+ /* allocate the requested size (could be only partially filled) */
+ if (src->trueColor) {
+ dst = gdImageCreateTrueColor(crop->width, crop->height);
+ gdImageSaveAlpha(dst, 1);
+ } else {
+ dst = gdImageCreate(crop->width, crop->height);
+ gdImagePaletteCopy(dst, src);
+ }
+ if (dst == NULL) {
+ return NULL;
+ }
+ dst->transparent = src->transparent;
+
+ /* check position in the src image */
+ if (crop->x < 0 || crop->x>=src->sx || crop->y<0 || crop->y>=src->sy) {
+ return dst;
+ }
+
+ /* reduce size if needed */
+ if ((src->sx - crop->width) < crop->x) {
+ crop->width = src->sx - crop->x;
+ }
+ if ((src->sy - crop->height) < crop->y) {
+ crop->height = src->sy - crop->y;
+ }
+
+#if 0
+printf("rect->x: %i\nrect->y: %i\nrect->width: %i\nrect->height: %i\n", crop->x, crop->y, crop->width, crop->height);
+#endif
+ y = crop->y;
+ if (src->trueColor) {
+ unsigned int dst_y = 0;
+ while (y < (crop->y + (crop->height - 1))) {
+ /* TODO: replace 4 w/byte per channel||pitch once available */
+ memcpy(dst->tpixels[dst_y++], src->tpixels[y++] + crop->x, crop->width * 4);
+ }
+ } else {
+ int x;
+ for (y = crop->y; y < (crop->y + (crop->height - 1)); y++) {
+ for (x = crop->x; x < (crop->x + (crop->width - 1)); x++) {
+ dst->pixels[y - crop->y][x - crop->x] = src->pixels[y][x];
+ }
+ }
+ }
+ return dst;
+}
+
+/**
+ * Function: gdImageAutoCrop
+ * Automatic croping of the src image using the given mode
+ * (see <gdCropMode>)
+ *
+ *
+ * Parameters:
+ * im - Source image
+ * mode - crop mode
+ *
+ * Returns:
+ * <gdImagePtr> on success or NULL
+ *
+ * See also:
+ * <gdCropMode>
+ */
+gdImagePtr gdImageCropAuto(gdImagePtr im, const unsigned int mode)
+{
+ const int width = gdImageSX(im);
+ const int height = gdImageSY(im);
+
+ int x,y;
+ int color, corners, match;
+ gdRect crop;
+
+ crop.x = 0;
+ crop.y = 0;
+ crop.width = 0;
+ crop.height = 0;
+
+ switch (mode) {
+ case GD_CROP_TRANSPARENT:
+ color = gdImageGetTransparent(im);
+ break;
+
+ case GD_CROP_BLACK:
+ color = gdImageColorClosestAlpha(im, 0, 0, 0, 0);
+ break;
+
+ case GD_CROP_WHITE:
+ color = gdImageColorClosestAlpha(im, 255, 255, 255, 0);
+ break;
+
+ case GD_CROP_SIDES:
+ corners = gdGuessBackgroundColorFromCorners(im, &color);
+ break;
+
+ case GD_CROP_DEFAULT:
+ default:
+ color = gdImageGetTransparent(im);
+ if (color == -1) {
+ corners = gdGuessBackgroundColorFromCorners(im, &color);
+ }
+ break;
+ }
+
+ /* TODO: Add gdImageGetRowPtr and works with ptr at the row level
+ * for the true color and palette images
+ * new formats will simply work with ptr
+ */
+ match = 1;
+ for (y = 0; match && y < height; y++) {
+ for (x = 0; match && x < width; x++) {
+ int c2 = gdImageGetPixel(im, x, y);
+ match = (color == c2);
+ }
+ }
+
+ /* Nothing to do > bye
+ * Duplicate the image?
+ */
+ if (y == height - 1) {
+ return NULL;
+ }
+
+ crop.y = y -1;
+ match = 1;
+ for (y = height - 1; match && y >= 0; y--) {
+ for (x = 0; match && x < width; x++) {
+ match = (color == gdImageGetPixel(im, x,y));
+ }
+ }
+
+ if (y == 0) {
+ crop.height = height - crop.y + 1;
+ } else {
+ crop.height = y - crop.y + 2;
+ }
+
+ match = 1;
+ for (x = 0; match && x < width; x++) {
+ for (y = 0; match && y < crop.y + crop.height - 1; y++) {
+ match = (color == gdImageGetPixel(im, x,y));
+ }
+ }
+ crop.x = x - 1;
+
+ match = 1;
+ for (x = width - 1; match && x >= 0; x--) {
+ for (y = 0; match && y < crop.y + crop.height - 1; y++) {
+ match = (color == gdImageGetPixel(im, x,y));
+ }
+ }
+ crop.width = x - crop.x + 2;
+ if (crop.x <= 0 || crop.y <= 0 || crop.width <= 0 || crop.height <= 0) {
+ return NULL;
+ }
+ return gdImageCrop(im, &crop);
+}
+/*TODOs: Implement DeltaE instead, way better perceptual differences */
+/**
+ * Function: gdImageThresholdCrop
+ * Crop an image using a given color. The threshold argument defines
+ * the tolerance to be used while comparing the image color and the
+ * color to crop. The method used to calculate the color difference
+ * is based on the color distance in the RGB(a) cube.
+ *
+ *
+ * Parameters:
+ * im - Source image
+ * color - color to crop
+ * threshold - tolerance (0..100)
+ *
+ * Returns:
+ * <gdImagePtr> on success or NULL
+ *
+ * See also:
+ * <gdCropMode>, <gdImageAutoCrop> or <gdImageCrop>
+ */
+gdImagePtr gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold)
+{
+ const int width = gdImageSX(im);
+ const int height = gdImageSY(im);
+
+ int x,y;
+ int match;
+ gdRect crop;
+
+ crop.x = 0;
+ crop.y = 0;
+ crop.width = 0;
+ crop.height = 0;
+
+ /* Pierre: crop everything sounds bad */
+ if (threshold > 1.0) {
+ return NULL;
+ }
+
+ /* TODO: Add gdImageGetRowPtr and works with ptr at the row level
+ * for the true color and palette images
+ * new formats will simply work with ptr
+ */
+ match = 1;
+ for (y = 0; match && y < height; y++) {
+ for (x = 0; match && x < width; x++) {
+ match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
+ }
+ }
+
+ /* Pierre
+ * Nothing to do > bye
+ * Duplicate the image?
+ */
+ if (y == height - 1) {
+ return NULL;
+ }
+
+ crop.y = y -1;
+ match = 1;
+ for (y = height - 1; match && y >= 0; y--) {
+ for (x = 0; match && x < width; x++) {
+ match = (gdColorMatch(im, color, gdImageGetPixel(im, x, y), threshold)) > 0;
+ }
+ }
+
+ if (y == 0) {
+ crop.height = height - crop.y + 1;
+ } else {
+ crop.height = y - crop.y + 2;
+ }
+
+ match = 1;
+ for (x = 0; match && x < width; x++) {
+ for (y = 0; match && y < crop.y + crop.height - 1; y++) {
+ match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
+ }
+ }
+ crop.x = x - 1;
+
+ match = 1;
+ for (x = width - 1; match && x >= 0; x--) {
+ for (y = 0; match && y < crop.y + crop.height - 1; y++) {
+ match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
+ }
+ }
+ crop.width = x - crop.x + 2;
+
+ return gdImageCrop(im, &crop);
+}
+
+/* This algorithm comes from pnmcrop (http://netpbm.sourceforge.net/)
+ * Three steps:
+ * - if 3 corners are equal.
+ * - if two are equal.
+ * - Last solution: average the colors
+ */
+static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color)
+{
+ const int tl = gdImageGetPixel(im, 0, 0);
+ const int tr = gdImageGetPixel(im, gdImageSX(im) - 1, 0);
+ const int bl = gdImageGetPixel(im, 0, gdImageSY(im) -1);
+ const int br = gdImageGetPixel(im, gdImageSX(im) - 1, gdImageSY(im) -1);
+
+ if (tr == bl && tr == br) {
+ *color = tr;
+ return 3;
+ } else if (tl == bl && tl == br) {
+ *color = tl;
+ return 3;
+ } else if (tl == tr && tl == br) {
+ *color = tl;
+ return 3;
+ } else if (tl == tr && tl == bl) {
+ *color = tl;
+ return 3;
+ } else if (tl == tr || tl == bl || tl == br) {
+ *color = tl;
+ return 2;
+ } else if (tr == bl) {
+ *color = tr;
+ return 2;
+ } else if (br == bl) {
+ *color = bl;
+ return 2;
+ } else {
+ register int r,b,g,a;
+
+ r = (int)(0.5f + (gdImageRed(im, tl) + gdImageRed(im, tr) + gdImageRed(im, bl) + gdImageRed(im, br)) / 4);
+ g = (int)(0.5f + (gdImageGreen(im, tl) + gdImageGreen(im, tr) + gdImageGreen(im, bl) + gdImageGreen(im, br)) / 4);
+ b = (int)(0.5f + (gdImageBlue(im, tl) + gdImageBlue(im, tr) + gdImageBlue(im, bl) + gdImageBlue(im, br)) / 4);
+ a = (int)(0.5f + (gdImageAlpha(im, tl) + gdImageAlpha(im, tr) + gdImageAlpha(im, bl) + gdImageAlpha(im, br)) / 4);
+ *color = gdImageColorClosestAlpha(im, r, g, b, a);
+ return 0;
+ }
+}
+
+static int gdColorMatch(gdImagePtr im, int col1, int col2, float threshold)
+{
+ const int dr = gdImageRed(im, col1) - gdImageRed(im, col2);
+ const int dg = gdImageGreen(im, col1) - gdImageGreen(im, col2);
+ const int db = gdImageBlue(im, col1) - gdImageBlue(im, col2);
+ const int da = gdImageAlpha(im, col1) - gdImageAlpha(im, col2);
+ const double dist = sqrt(dr * dr + dg * dg + db * db + da * da);
+ const double dist_perc = sqrt(dist / (255^2 + 255^2 + 255^2));
+ return (dist_perc <= threshold);
+ //return (100.0 * dist / 195075) < threshold;
+}
+
+/*
+ * To be implemented when we have more image formats.
+ * Buffer like gray8 gray16 or rgb8 will require some tweak
+ * and can be done in this function (called from the autocrop
+ * function. (Pierre)
+ */
+#if 0
+static int colors_equal (const int col1, const in col2)
+{
+
+}
+#endif
diff --git a/ext/gd/libgd/gd_interpolation.c b/ext/gd/libgd/gd_interpolation.c
new file mode 100644
index 0000000000..3643535f2e
--- /dev/null
+++ b/ext/gd/libgd/gd_interpolation.c
@@ -0,0 +1,2550 @@
+/*
+ * Filtered Image Rescaling
+ * Based on Gems III
+ * - Schumacher general filtered image rescaling
+ * (pp. 414-424)
+ * by Dale Schumacher
+ *
+ * Additional changes by Ray Gardener, Daylon Graphics Ltd.
+ * December 4, 1999
+ *
+ * Ported to libgd by Pierre Joye. Support for multiple channels
+ * added (argb for now).
+ *
+ * Initial sources code is avaibable in the Gems Source Code Packages:
+ * http://www.acm.org/pubs/tog/GraphicsGems/GGemsIII.tar.gz
+ */
+
+/*
+ Summary:
+
+ - Horizontal filter contributions are calculated on the fly,
+ as each column is mapped from src to dst image. This lets
+ us omit having to allocate a temporary full horizontal stretch
+ of the src image.
+
+ - If none of the src pixels within a sampling region differ,
+ then the output pixel is forced to equal (any of) the source pixel.
+ This ensures that filters do not corrupt areas of constant color.
+
+ - Filter weight contribution results, after summing, are
+ rounded to the nearest pixel color value instead of
+ being casted to ILubyte (usually an int or char). Otherwise,
+ artifacting occurs.
+
+*/
+
+/*
+ Additional functions are available for simple rotation or up/downscaling.
+ downscaling using the fixed point implementations are usually much faster
+ than the existing gdImageCopyResampled while having a similar or better
+ quality.
+
+ For image rotations, the optimized versions have a lazy antialiasing for
+ the edges of the images. For a much better antialiased result, the affine
+ function is recommended.
+*/
+
+/*
+TODO:
+ - Optimize pixel accesses and loops once we have continuous buffer
+ - Add scale support for a portion only of an image (equivalent of copyresized/resampled)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <gd.h>
+#include "gdhelpers.h"
+
+#ifdef _MSC_VER
+# pragma optimize("t", on)
+# include <emmintrin.h>
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+#define MIN3(a,b,c) ((a)<(b)?(MIN(a,c)):(MIN(b,c)))
+#ifndef MAX
+#define MAX(a,b) ((a)<(b)?(b):(a))
+#endif
+#define MAX3(a,b,c) ((a)<(b)?(MAX(b,c)):(MAX(a,c)))
+
+#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+
+/* only used here, let do a generic fixed point integers later if required by other
+ part of GD */
+typedef long gdFixed;
+/* Integer to fixed point */
+#define gd_itofx(x) ((x) << 8)
+
+/* Float to fixed point */
+#define gd_ftofx(x) (long)((x) * 256)
+
+/* Double to fixed point */
+#define gd_dtofx(x) (long)((x) * 256)
+
+/* Fixed point to integer */
+#define gd_fxtoi(x) ((x) >> 8)
+
+/* Fixed point to float */
+# define gd_fxtof(x) ((float)(x) / 256)
+
+/* Fixed point to double */
+#define gd_fxtod(x) ((double)(x) / 256)
+
+/* Multiply a fixed by a fixed */
+#define gd_mulfx(x,y) (((x) * (y)) >> 8)
+
+/* Divide a fixed by a fixed */
+#define gd_divfx(x,y) (((x) << 8) / (y))
+
+typedef struct
+{
+ double *Weights; /* Normalized weights of neighboring pixels */
+ int Left,Right; /* Bounds of source pixels window */
+} ContributionType; /* Contirbution information for a single pixel */
+
+typedef struct
+{
+ ContributionType *ContribRow; /* Row (or column) of contribution weights */
+ unsigned int WindowSize, /* Filter window size (of affecting source pixels) */
+ LineLength; /* Length of line (no. or rows / cols) */
+} LineContribType;
+
+/* Each core filter has its own radius */
+#define DEFAULT_FILTER_BICUBIC 3.0
+#define DEFAULT_FILTER_BOX 0.5
+#define DEFAULT_FILTER_GENERALIZED_CUBIC 0.5
+#define DEFAULT_FILTER_RADIUS 1.0
+#define DEFAULT_LANCZOS8_RADIUS 8.0
+#define DEFAULT_LANCZOS3_RADIUS 3.0
+#define DEFAULT_HERMITE_RADIUS 1.0
+#define DEFAULT_BOX_RADIUS 0.5
+#define DEFAULT_TRIANGLE_RADIUS 1.0
+#define DEFAULT_BELL_RADIUS 1.5
+#define DEFAULT_CUBICSPLINE_RADIUS 2.0
+#define DEFAULT_MITCHELL_RADIUS 2.0
+#define DEFAULT_COSINE_RADIUS 1.0
+#define DEFAULT_CATMULLROM_RADIUS 2.0
+#define DEFAULT_QUADRATIC_RADIUS 1.5
+#define DEFAULT_QUADRATICBSPLINE_RADIUS 1.5
+#define DEFAULT_CUBICCONVOLUTION_RADIUS 3.0
+#define DEFAULT_GAUSSIAN_RADIUS 1.0
+#define DEFAULT_HANNING_RADIUS 1.0
+#define DEFAULT_HAMMING_RADIUS 1.0
+#define DEFAULT_SINC_RADIUS 1.0
+#define DEFAULT_WELSH_RADIUS 1.0
+
+enum GD_RESIZE_FILTER_TYPE{
+ FILTER_DEFAULT = 0,
+ FILTER_BELL,
+ FILTER_BESSEL,
+ FILTER_BLACKMAN,
+ FILTER_BOX,
+ FILTER_BSPLINE,
+ FILTER_CATMULLROM,
+ FILTER_COSINE,
+ FILTER_CUBICCONVOLUTION,
+ FILTER_CUBICSPLINE,
+ FILTER_HERMITE,
+ FILTER_LANCZOS3,
+ FILTER_LANCZOS8,
+ FILTER_MITCHELL,
+ FILTER_QUADRATIC,
+ FILTER_QUADRATICBSPLINE,
+ FILTER_TRIANGLE,
+ FILTER_GAUSSIAN,
+ FILTER_HANNING,
+ FILTER_HAMMING,
+ FILTER_SINC,
+ FILTER_WELSH,
+
+ FILTER_CALLBACK = 999
+};
+
+typedef enum GD_RESIZE_FILTER_TYPE gdResizeFilterType;
+
+static double KernelBessel_J1(const double x)
+{
+ double p, q;
+
+ register long i;
+
+ static const double
+ Pone[] =
+ {
+ 0.581199354001606143928050809e+21,
+ -0.6672106568924916298020941484e+20,
+ 0.2316433580634002297931815435e+19,
+ -0.3588817569910106050743641413e+17,
+ 0.2908795263834775409737601689e+15,
+ -0.1322983480332126453125473247e+13,
+ 0.3413234182301700539091292655e+10,
+ -0.4695753530642995859767162166e+7,
+ 0.270112271089232341485679099e+4
+ },
+ Qone[] =
+ {
+ 0.11623987080032122878585294e+22,
+ 0.1185770712190320999837113348e+20,
+ 0.6092061398917521746105196863e+17,
+ 0.2081661221307607351240184229e+15,
+ 0.5243710262167649715406728642e+12,
+ 0.1013863514358673989967045588e+10,
+ 0.1501793594998585505921097578e+7,
+ 0.1606931573481487801970916749e+4,
+ 0.1e+1
+ };
+
+ p = Pone[8];
+ q = Qone[8];
+ for (i=7; i >= 0; i--)
+ {
+ p = p*x*x+Pone[i];
+ q = q*x*x+Qone[i];
+ }
+ return (double)(p/q);
+}
+
+static double KernelBessel_P1(const double x)
+{
+ double p, q;
+
+ register long i;
+
+ static const double
+ Pone[] =
+ {
+ 0.352246649133679798341724373e+5,
+ 0.62758845247161281269005675e+5,
+ 0.313539631109159574238669888e+5,
+ 0.49854832060594338434500455e+4,
+ 0.2111529182853962382105718e+3,
+ 0.12571716929145341558495e+1
+ },
+ Qone[] =
+ {
+ 0.352246649133679798068390431e+5,
+ 0.626943469593560511888833731e+5,
+ 0.312404063819041039923015703e+5,
+ 0.4930396490181088979386097e+4,
+ 0.2030775189134759322293574e+3,
+ 0.1e+1
+ };
+
+ p = Pone[5];
+ q = Qone[5];
+ for (i=4; i >= 0; i--)
+ {
+ p = p*(8.0/x)*(8.0/x)+Pone[i];
+ q = q*(8.0/x)*(8.0/x)+Qone[i];
+ }
+ return (double)(p/q);
+}
+
+static double KernelBessel_Q1(const double x)
+{
+ double p, q;
+
+ register long i;
+
+ static const double
+ Pone[] =
+ {
+ 0.3511751914303552822533318e+3,
+ 0.7210391804904475039280863e+3,
+ 0.4259873011654442389886993e+3,
+ 0.831898957673850827325226e+2,
+ 0.45681716295512267064405e+1,
+ 0.3532840052740123642735e-1
+ },
+ Qone[] =
+ {
+ 0.74917374171809127714519505e+4,
+ 0.154141773392650970499848051e+5,
+ 0.91522317015169922705904727e+4,
+ 0.18111867005523513506724158e+4,
+ 0.1038187585462133728776636e+3,
+ 0.1e+1
+ };
+
+ p = Pone[5];
+ q = Qone[5];
+ for (i=4; i >= 0; i--)
+ {
+ p = p*(8.0/x)*(8.0/x)+Pone[i];
+ q = q*(8.0/x)*(8.0/x)+Qone[i];
+ }
+ return (double)(p/q);
+}
+
+static double KernelBessel_Order1(double x)
+{
+ double p, q;
+
+ if (x == 0.0)
+ return (0.0f);
+ p = x;
+ if (x < 0.0)
+ x=(-x);
+ if (x < 8.0)
+ return (p*KernelBessel_J1(x));
+ q = (double)sqrt(2.0f/(M_PI*x))*(double)(KernelBessel_P1(x)*(1.0f/sqrt(2.0f)*(sin(x)-cos(x)))-8.0f/x*KernelBessel_Q1(x)*
+ (-1.0f/sqrt(2.0f)*(sin(x)+cos(x))));
+ if (p < 0.0f)
+ q = (-q);
+ return (q);
+}
+
+static double filter_bessel(const double x)
+{
+ if (x == 0.0f)
+ return (double)(M_PI/4.0f);
+ return (KernelBessel_Order1((double)M_PI*x)/(2.0f*x));
+}
+
+
+static double filter_blackman(const double x)
+{
+ return (0.42f+0.5f*(double)cos(M_PI*x)+0.08f*(double)cos(2.0f*M_PI*x));
+}
+
+/**
+ * Bicubic interpolation kernel (a=-1):
+ \verbatim
+ /
+ | 1-2|t|**2+|t|**3 , if |t| < 1
+ h(t) = | 4-8|t|+5|t|**2-|t|**3 , if 1<=|t|<2
+ | 0 , otherwise
+ \
+ \endverbatim
+ * ***bd*** 2.2004
+ */
+static double filter_bicubic(const double t)
+{
+ const double abs_t = (double)fabs(t);
+ const double abs_t_sq = abs_t * abs_t;
+ if (abs_t<1) return 1-2*abs_t_sq+abs_t_sq*abs_t;
+ if (abs_t<2) return 4 - 8*abs_t +5*abs_t_sq - abs_t_sq*abs_t;
+ return 0;
+}
+
+/**
+ * Generalized cubic kernel (for a=-1 it is the same as BicubicKernel):
+ \verbatim
+ /
+ | (a+2)|t|**3 - (a+3)|t|**2 + 1 , |t| <= 1
+ h(t) = | a|t|**3 - 5a|t|**2 + 8a|t| - 4a , 1 < |t| <= 2
+ | 0 , otherwise
+ \
+ \endverbatim
+ * Often used values for a are -1 and -1/2.
+ */
+static double filter_generalized_cubic(const double t)
+{
+ const double a = -DEFAULT_FILTER_GENERALIZED_CUBIC;
+ double abs_t = (double)fabs(t);
+ double abs_t_sq = abs_t * abs_t;
+ if (abs_t < 1) return (a + 2) * abs_t_sq * abs_t - (a + 3) * abs_t_sq + 1;
+ if (abs_t < 2) return a * abs_t_sq * abs_t - 5 * a * abs_t_sq + 8 * a * abs_t - 4 * a;
+ return 0;
+}
+
+/* CubicSpline filter, default radius 2 */
+static double filter_cubic_spline(const double x1)
+{
+ const double x = x1 < 0.0 ? -x1 : x1;
+
+ if (x < 1.0 ) {
+ const double x2 = x*x;
+
+ return (0.5 * x2 * x - x2 + 2.0 / 3.0);
+ }
+ if (x < 2.0) {
+ return (pow(2.0 - x, 3.0)/6.0);
+ }
+ return 0;
+}
+
+/* CubicConvolution filter, default radius 3 */
+static double filter_cubic_convolution(const double x1)
+{
+ const double x = x1 < 0.0 ? -x1 : x1;
+ const double x2 = x1 * x1;
+ const double x2_x = x2 * x;
+
+ if (x <= 1.0) return ((4.0 / 3.0)* x2_x - (7.0 / 3.0) * x2 + 1.0);
+ if (x <= 2.0) return (- (7.0 / 12.0) * x2_x + 3 * x2 - (59.0 / 12.0) * x + 2.5);
+ if (x <= 3.0) return ( (1.0/12.0) * x2_x - (2.0 / 3.0) * x2 + 1.75 * x - 1.5);
+ return 0;
+}
+
+static double filter_box(double x) {
+ if (x < - DEFAULT_FILTER_BOX)
+ return 0.0f;
+ if (x < DEFAULT_FILTER_BOX)
+ return 1.0f;
+ return 0.0f;
+}
+
+static double filter_catmullrom(const double x)
+{
+ if (x < -2.0)
+ return(0.0f);
+ if (x < -1.0)
+ return(0.5f*(4.0f+x*(8.0f+x*(5.0f+x))));
+ if (x < 0.0)
+ return(0.5f*(2.0f+x*x*(-5.0f-3.0f*x)));
+ if (x < 1.0)
+ return(0.5f*(2.0f+x*x*(-5.0f+3.0f*x)));
+ if (x < 2.0)
+ return(0.5f*(4.0f+x*(-8.0f+x*(5.0f-x))));
+ return(0.0f);
+}
+
+static double filter_filter(double t)
+{
+ /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */
+ if(t < 0.0) t = -t;
+ if(t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0);
+ return(0.0);
+}
+
+
+/* Lanczos8 filter, default radius 8 */
+static double filter_lanczos8(const double x1)
+{
+ const double x = x1 < 0.0 ? -x1 : x1;
+#define R DEFAULT_LANCZOS8_RADIUS
+
+ if ( x == 0.0) return 1;
+
+ if ( x < R) {
+ return R * sin(x*M_PI) * sin(x * M_PI/ R) / (x * M_PI * x * M_PI);
+ }
+ return 0.0;
+#undef R
+}
+
+
+/* Lanczos3 filter, default radius 3 */
+static double filter_lanczos3(const double x1)
+{
+ const double x = x1 < 0.0 ? -x1 : x1;
+#define R DEFAULT_LANCZOS3_RADIUS
+
+ if ( x == 0.0) return 1;
+
+ if ( x < R)
+ {
+ return R * sin(x*M_PI) * sin(x * M_PI / R) / (x * M_PI * x * M_PI);
+ }
+ return 0.0;
+#undef R
+}
+
+/* Hermite filter, default radius 1 */
+static double filter_hermite(const double x1)
+{
+ const double x = x1 < 0.0 ? -x1 : x1;
+
+ if (x < 1.0) return ((2.0 * x - 3) * x * x + 1.0 );
+
+ return 0.0;
+}
+
+/* Trangle filter, default radius 1 */
+static double filter_triangle(const double x1)
+{
+ const double x = x1 < 0.0 ? -x1 : x1;
+ if (x < 1.0) return (1.0 - x);
+ return 0.0;
+}
+
+/* Bell filter, default radius 1.5 */
+static double filter_bell(const double x1)
+{
+ const double x = x1 < 0.0 ? -x1 : x1;
+
+ if (x < 0.5) return (0.75 - x*x);
+ if (x < 1.5) return (0.5 * pow(x - 1.5, 2.0));
+ return 0.0;
+}
+
+/* Mitchell filter, default radius 2.0 */
+static double filter_mitchell(const double x)
+{
+#define KM_B (1.0f/3.0f)
+#define KM_C (1.0f/3.0f)
+#define KM_P0 (( 6.0f - 2.0f * KM_B ) / 6.0f)
+#define KM_P2 ((-18.0f + 12.0f * KM_B + 6.0f * KM_C) / 6.0f)
+#define KM_P3 (( 12.0f - 9.0f * KM_B - 6.0f * KM_C) / 6.0f)
+#define KM_Q0 (( 8.0f * KM_B + 24.0f * KM_C) / 6.0f)
+#define KM_Q1 ((-12.0f * KM_B - 48.0f * KM_C) / 6.0f)
+#define KM_Q2 (( 6.0f * KM_B + 30.0f * KM_C) / 6.0f)
+#define KM_Q3 (( -1.0f * KM_B - 6.0f * KM_C) / 6.0f)
+
+ if (x < -2.0)
+ return(0.0f);
+ if (x < -1.0)
+ return(KM_Q0-x*(KM_Q1-x*(KM_Q2-x*KM_Q3)));
+ if (x < 0.0f)
+ return(KM_P0+x*x*(KM_P2-x*KM_P3));
+ if (x < 1.0f)
+ return(KM_P0+x*x*(KM_P2+x*KM_P3));
+ if (x < 2.0f)
+ return(KM_Q0+x*(KM_Q1+x*(KM_Q2+x*KM_Q3)));
+ return(0.0f);
+}
+
+
+
+/* Cosine filter, default radius 1 */
+static double filter_cosine(const double x)
+{
+ if ((x >= -1.0) && (x <= 1.0)) return ((cos(x * M_PI) + 1.0)/2.0);
+
+ return 0;
+}
+
+/* Quadratic filter, default radius 1.5 */
+static double filter_quadratic(const double x1)
+{
+ const double x = x1 < 0.0 ? -x1 : x1;
+
+ if (x <= 0.5) return (- 2.0 * x * x + 1);
+ if (x <= 1.5) return (x * x - 2.5* x + 1.5);
+ return 0.0;
+}
+
+static double filter_bspline(const double x)
+{
+ if (x>2.0f) {
+ return 0.0f;
+ } else {
+ double a, b, c, d;
+ /* Was calculated anyway cause the "if((x-1.0f) < 0)" */
+ const double xm1 = x - 1.0f;
+ const double xp1 = x + 1.0f;
+ const double xp2 = x + 2.0f;
+
+ if ((xp2) <= 0.0f) a = 0.0f; else a = xp2*xp2*xp2;
+ if ((xp1) <= 0.0f) b = 0.0f; else b = xp1*xp1*xp1;
+ if (x <= 0) c = 0.0f; else c = x*x*x;
+ if ((xm1) <= 0.0f) d = 0.0f; else d = xm1*xm1*xm1;
+
+ return (0.16666666666666666667f * (a - (4.0f * b) + (6.0f * c) - (4.0f * d)));
+ }
+}
+
+/* QuadraticBSpline filter, default radius 1.5 */
+static double filter_quadratic_bspline(const double x1)
+{
+ const double x = x1 < 0.0 ? -x1 : x1;
+
+ if (x <= 0.5) return (- x * x + 0.75);
+ if (x <= 1.5) return (0.5 * x * x - 1.5 * x + 1.125);
+ return 0.0;
+}
+
+static double filter_gaussian(const double x)
+{
+ /* return(exp((double) (-2.0 * x * x)) * sqrt(2.0 / M_PI)); */
+ return (double)(exp(-2.0f * x * x) * 0.79788456080287f);
+}
+
+static double filter_hanning(const double x)
+{
+ /* A Cosine windowing function */
+ return(0.5 + 0.5 * cos(M_PI * x));
+}
+
+static double filter_hamming(const double x)
+{
+ /* should be
+ (0.54+0.46*cos(M_PI*(double) x));
+ but this approximation is sufficient */
+ if (x < -1.0f)
+ return 0.0f;
+ if (x < 0.0f)
+ return 0.92f*(-2.0f*x-3.0f)*x*x+1.0f;
+ if (x < 1.0f)
+ return 0.92f*(2.0f*x-3.0f)*x*x+1.0f;
+ return 0.0f;
+}
+
+static double filter_power(const double x)
+{
+ const double a = 2.0f;
+ if (fabs(x)>1) return 0.0f;
+ return (1.0f - (double)fabs(pow(x,a)));
+}
+
+static double filter_sinc(const double x)
+{
+ /* X-scaled Sinc(x) function. */
+ if (x == 0.0) return(1.0);
+ return (sin(M_PI * (double) x) / (M_PI * (double) x));
+}
+
+static double filter_welsh(const double x)
+{
+ /* Welsh parabolic windowing filter */
+ if (x < 1.0)
+ return(1 - x*x);
+ return(0.0);
+}
+
+
+/* Copied from upstream's libgd */
+static inline int _color_blend (const int dst, const int src)
+{
+ const int src_alpha = gdTrueColorGetAlpha(src);
+
+ if( src_alpha == gdAlphaOpaque ) {
+ return src;
+ } else {
+ const int dst_alpha = gdTrueColorGetAlpha(dst);
+
+ if( src_alpha == gdAlphaTransparent ) return dst;
+ if( dst_alpha == gdAlphaTransparent ) {
+ return src;
+ } else {
+ register int alpha, red, green, blue;
+ const int src_weight = gdAlphaTransparent - src_alpha;
+ const int dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax;
+ const int tot_weight = src_weight + dst_weight;
+
+ alpha = src_alpha * dst_alpha / gdAlphaMax;
+
+ red = (gdTrueColorGetRed(src) * src_weight
+ + gdTrueColorGetRed(dst) * dst_weight) / tot_weight;
+ green = (gdTrueColorGetGreen(src) * src_weight
+ + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight;
+ blue = (gdTrueColorGetBlue(src) * src_weight
+ + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight;
+
+ return ((alpha << 24) + (red << 16) + (green << 8) + blue);
+ }
+ }
+}
+
+static inline int _setEdgePixel(const gdImagePtr src, unsigned int x, unsigned int y, gdFixed coverage, const int bgColor)
+{
+ const gdFixed f_127 = gd_itofx(127);
+ register int c = src->tpixels[y][x];
+ c = c | (( (int) (gd_fxtof(gd_mulfx(coverage, f_127)) + 50.5f)) << 24);
+ return _color_blend(bgColor, c);
+}
+
+static inline int getPixelOverflowTC(gdImagePtr im, const int x, const int y, const int bgColor)
+{
+ if (gdImageBoundsSafe(im, x, y)) {
+ const int c = im->tpixels[y][x];
+ if (c == im->transparent) {
+ return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor;
+ }
+ return c;
+ } else {
+ register int border = 0;
+
+ if (y < im->cy1) {
+ border = im->tpixels[0][im->cx1];
+ goto processborder;
+ }
+
+ if (y < im->cy1) {
+ border = im->tpixels[0][im->cx1];
+ goto processborder;
+ }
+
+ if (y > im->cy2) {
+ if (x >= im->cx1 && x <= im->cx1) {
+ border = im->tpixels[im->cy2][x];
+ goto processborder;
+ } else {
+ return gdTrueColorAlpha(0, 0, 0, 127);
+ }
+ }
+
+ /* y is bound safe at this point */
+ if (x < im->cx1) {
+ border = im->tpixels[y][im->cx1];
+ goto processborder;
+ }
+
+ if (x > im->cx2) {
+ border = im->tpixels[y][im->cx2];
+ }
+
+processborder:
+ if (border == im->transparent) {
+ return gdTrueColorAlpha(0, 0, 0, 127);
+ } else{
+ return gdTrueColorAlpha(gdTrueColorGetRed(border), gdTrueColorGetGreen(border), gdTrueColorGetBlue(border), 127);
+ }
+ }
+}
+
+#define colorIndex2RGBA(c) gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(c)])
+#define colorIndex2RGBcustomA(c, a) gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(a)])
+static inline int getPixelOverflowPalette(gdImagePtr im, const int x, const int y, const int bgColor)
+{
+ if (gdImageBoundsSafe(im, x, y)) {
+ const int c = im->pixels[y][x];
+ if (c == im->transparent) {
+ return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor;
+ }
+ return colorIndex2RGBA(c);
+ } else {
+ register int border = 0;
+ if (y < im->cy1) {
+ border = gdImageGetPixel(im, im->cx1, 0);
+ goto processborder;
+ }
+
+ if (y < im->cy1) {
+ border = gdImageGetPixel(im, im->cx1, 0);
+ goto processborder;
+ }
+
+ if (y > im->cy2) {
+ if (x >= im->cx1 && x <= im->cx1) {
+ border = gdImageGetPixel(im, x, im->cy2);
+ goto processborder;
+ } else {
+ return gdTrueColorAlpha(0, 0, 0, 127);
+ }
+ }
+
+ /* y is bound safe at this point */
+ if (x < im->cx1) {
+ border = gdImageGetPixel(im, im->cx1, y);
+ goto processborder;
+ }
+
+ if (x > im->cx2) {
+ border = gdImageGetPixel(im, im->cx2, y);
+ }
+
+processborder:
+ if (border == im->transparent) {
+ return gdTrueColorAlpha(0, 0, 0, 127);
+ } else{
+ return colorIndex2RGBcustomA(border, 127);
+ }
+ }
+}
+
+static int getPixelInterpolateWeight(gdImagePtr im, const double x, const double y, const int bgColor)
+{
+ /* Closest pixel <= (xf,yf) */
+ int sx = (int)(x);
+ int sy = (int)(y);
+ const double xf = x - (double)sx;
+ const double yf = y - (double)sy;
+ const double nxf = (double) 1.0 - xf;
+ const double nyf = (double) 1.0 - yf;
+ const double m1 = xf * yf;
+ const double m2 = nxf * yf;
+ const double m3 = xf * nyf;
+ const double m4 = nxf * nyf;
+
+ /* get color values of neighbouring pixels */
+ const int c1 = im->trueColor == 1 ? getPixelOverflowTC(im, sx, sy, bgColor) : getPixelOverflowPalette(im, sx, sy, bgColor);
+ const int c2 = im->trueColor == 1 ? getPixelOverflowTC(im, sx - 1, sy, bgColor) : getPixelOverflowPalette(im, sx - 1, sy, bgColor);
+ const int c3 = im->trueColor == 1 ? getPixelOverflowTC(im, sx, sy - 1, bgColor) : getPixelOverflowPalette(im, sx, sy - 1, bgColor);
+ const int c4 = im->trueColor == 1 ? getPixelOverflowTC(im, sx - 1, sy - 1, bgColor) : getPixelOverflowPalette(im, sx, sy - 1, bgColor);
+ int r, g, b, a;
+
+ if (x < 0) sx--;
+ if (y < 0) sy--;
+
+ /* component-wise summing-up of color values */
+ if (im->trueColor) {
+ r = (int)(m1*gdTrueColorGetRed(c1) + m2*gdTrueColorGetRed(c2) + m3*gdTrueColorGetRed(c3) + m4*gdTrueColorGetRed(c4));
+ g = (int)(m1*gdTrueColorGetGreen(c1) + m2*gdTrueColorGetGreen(c2) + m3*gdTrueColorGetGreen(c3) + m4*gdTrueColorGetGreen(c4));
+ b = (int)(m1*gdTrueColorGetBlue(c1) + m2*gdTrueColorGetBlue(c2) + m3*gdTrueColorGetBlue(c3) + m4*gdTrueColorGetBlue(c4));
+ a = (int)(m1*gdTrueColorGetAlpha(c1) + m2*gdTrueColorGetAlpha(c2) + m3*gdTrueColorGetAlpha(c3) + m4*gdTrueColorGetAlpha(c4));
+ } else {
+ r = (int)(m1*im->red[(c1)] + m2*im->red[(c2)] + m3*im->red[(c3)] + m4*im->red[(c4)]);
+ g = (int)(m1*im->green[(c1)] + m2*im->green[(c2)] + m3*im->green[(c3)] + m4*im->green[(c4)]);
+ b = (int)(m1*im->blue[(c1)] + m2*im->blue[(c2)] + m3*im->blue[(c3)] + m4*im->blue[(c4)]);
+ a = (int)(m1*im->alpha[(c1)] + m2*im->alpha[(c2)] + m3*im->alpha[(c3)] + m4*im->alpha[(c4)]);
+ }
+
+ r = CLAMP(r, 0, 255);
+ g = CLAMP(g, 0, 255);
+ b = CLAMP(b, 0, 255);
+ a = CLAMP(a, 0, gdAlphaMax);
+ return gdTrueColorAlpha(r, g, b, a);
+}
+
+/**
+ * Function: getPixelInterpolated
+ * Returns the interpolated color value using the default interpolation
+ * method. The returned color is always in the ARGB format (truecolor).
+ *
+ * Parameters:
+ * im - Image to set the default interpolation method
+ * y - X value of the ideal position
+ * y - Y value of the ideal position
+ * method - Interpolation method <gdInterpolationMethod>
+ *
+ * Returns:
+ * GD_TRUE if the affine is rectilinear or GD_FALSE
+ *
+ * See also:
+ * <gdSetInterpolationMethod>
+ */
+int getPixelInterpolated(gdImagePtr im, const double x, const double y, const int bgColor)
+{
+ const int xi=(int)((x) < 0 ? x - 1: x);
+ const int yi=(int)((y) < 0 ? y - 1: y);
+ int yii;
+ int i;
+ double kernel, kernel_cache_y;
+ double kernel_x[12], kernel_y[4];
+ double new_r = 0.0f, new_g = 0.0f, new_b = 0.0f, new_a = 0.0f;
+
+ /* These methods use special implementations */
+ if (im->interpolation_id == GD_BILINEAR_FIXED || im->interpolation_id == GD_BICUBIC_FIXED || im->interpolation_id == GD_NEAREST_NEIGHBOUR) {
+ return -1;
+ }
+
+ /* Default to full alpha */
+ if (bgColor == -1) {
+ }
+
+ if (im->interpolation_id == GD_WEIGHTED4) {
+ return getPixelInterpolateWeight(im, x, y, bgColor);
+ }
+
+ if (im->interpolation_id == GD_NEAREST_NEIGHBOUR) {
+ if (im->trueColor == 1) {
+ return getPixelOverflowTC(im, xi, yi, bgColor);
+ } else {
+ return getPixelOverflowPalette(im, xi, yi, bgColor);
+ }
+ }
+ if (im->interpolation) {
+ for (i=0; i<4; i++) {
+ kernel_x[i] = (double) im->interpolation((double)(xi+i-1-x));
+ kernel_y[i] = (double) im->interpolation((double)(yi+i-1-y));
+ }
+ } else {
+ return -1;
+ }
+
+ /*
+ * TODO: use the known fast rgba multiplication implementation once
+ * the new formats are in place
+ */
+ for (yii = yi-1; yii < yi+3; yii++) {
+ int xii;
+ kernel_cache_y = kernel_y[yii-(yi-1)];
+ if (im->trueColor) {
+ for (xii=xi-1; xii<xi+3; xii++) {
+ const int rgbs = getPixelOverflowTC(im, xii, yii, bgColor);
+
+ kernel = kernel_cache_y * kernel_x[xii-(xi-1)];
+ new_r += kernel * gdTrueColorGetRed(rgbs);
+ new_g += kernel * gdTrueColorGetGreen(rgbs);
+ new_b += kernel * gdTrueColorGetBlue(rgbs);
+ new_a += kernel * gdTrueColorGetAlpha(rgbs);
+ }
+ } else {
+ for (xii=xi-1; xii<xi+3; xii++) {
+ const int rgbs = getPixelOverflowPalette(im, xii, yii, bgColor);
+
+ kernel = kernel_cache_y * kernel_x[xii-(xi-1)];
+ new_r += kernel * gdTrueColorGetRed(rgbs);
+ new_g += kernel * gdTrueColorGetGreen(rgbs);
+ new_b += kernel * gdTrueColorGetBlue(rgbs);
+ new_a += kernel * gdTrueColorGetAlpha(rgbs);
+ }
+ }
+ }
+
+ new_r = CLAMP(new_r, 0, 255);
+ new_g = CLAMP(new_g, 0, 255);
+ new_b = CLAMP(new_b, 0, 255);
+ new_a = CLAMP(new_a, 0, gdAlphaMax);
+
+ return gdTrueColorAlpha(((int)new_r), ((int)new_g), ((int)new_b), ((int)new_a));
+}
+
+static inline LineContribType * _gdContributionsAlloc(unsigned int line_length, unsigned int windows_size)
+{
+ unsigned int u = 0;
+ LineContribType *res;
+
+ res = (LineContribType *) gdMalloc(sizeof(LineContribType));
+ if (!res) {
+ return NULL;
+ }
+ res->WindowSize = windows_size;
+ res->LineLength = line_length;
+ res->ContribRow = (ContributionType *) gdMalloc(line_length * sizeof(ContributionType));
+
+ for (u = 0 ; u < line_length ; u++) {
+ res->ContribRow[u].Weights = (double *) gdMalloc(windows_size * sizeof(double));
+ }
+ return res;
+}
+
+static inline void _gdContributionsFree(LineContribType * p)
+{
+ unsigned int u;
+ for (u = 0; u < p->LineLength; u++) {
+ gdFree(p->ContribRow[u].Weights);
+ }
+ gdFree(p->ContribRow);
+ gdFree(p);
+}
+
+static inline LineContribType *_gdContributionsCalc(unsigned int line_size, unsigned int src_size, double scale_d, const interpolation_method pFilter)
+{
+ double width_d;
+ double scale_f_d = 1.0;
+ const double filter_width_d = DEFAULT_BOX_RADIUS;
+ int windows_size;
+ unsigned int u;
+ LineContribType *res;
+
+ if (scale_d < 1.0) {
+ width_d = filter_width_d / scale_d;
+ scale_f_d = scale_d;
+ } else {
+ width_d= filter_width_d;
+ }
+
+ windows_size = 2 * (int)ceil(width_d) + 1;
+ res = _gdContributionsAlloc(line_size, windows_size);
+
+ for (u = 0; u < line_size; u++) {
+ const double dCenter = (double)u / scale_d;
+ /* get the significant edge points affecting the pixel */
+ register int iLeft = MAX(0, (int)floor (dCenter - width_d));
+ int iRight = MIN((int)ceil(dCenter + width_d), (int)src_size - 1);
+ double dTotalWeight = 0.0;
+ int iSrc;
+
+ res->ContribRow[u].Left = iLeft;
+ res->ContribRow[u].Right = iRight;
+
+ /* Cut edge points to fit in filter window in case of spill-off */
+ if (iRight - iLeft + 1 > windows_size) {
+ if (iLeft < ((int)src_size - 1 / 2)) {
+ iLeft++;
+ } else {
+ iRight--;
+ }
+ }
+
+ for (iSrc = iLeft; iSrc <= iRight; iSrc++) {
+ dTotalWeight += (res->ContribRow[u].Weights[iSrc-iLeft] = scale_f_d * (*pFilter)(scale_f_d * (dCenter - (double)iSrc)));
+ }
+
+ if (dTotalWeight < 0.0) {
+ _gdContributionsFree(res);
+ return NULL;
+ }
+
+ if (dTotalWeight > 0.0) {
+ for (iSrc = iLeft; iSrc <= iRight; iSrc++) {
+ res->ContribRow[u].Weights[iSrc-iLeft] /= dTotalWeight;
+ }
+ }
+ }
+ return res;
+}
+
+static inline void _gdScaleRow(gdImagePtr pSrc, unsigned int src_width, gdImagePtr dst, unsigned int dst_width, unsigned int row, LineContribType *contrib)
+{
+ int *p_src_row = pSrc->tpixels[row];
+ int *p_dst_row = dst->tpixels[row];
+ unsigned int x;
+
+ for (x = 0; x < dst_width - 1; x++) {
+ register unsigned char r = 0, g = 0, b = 0, a = 0;
+ const int left = contrib->ContribRow[x].Left;
+ const int right = contrib->ContribRow[x].Right;
+ int i;
+
+ /* Accumulate each channel */
+ for (i = left; i <= right; i++) {
+ const int left_channel = i - left;
+ r += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetRed(p_src_row[i])));
+ g += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetGreen(p_src_row[i])));
+ b += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetBlue(p_src_row[i])));
+ a += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetAlpha(p_src_row[i])));
+ }
+ p_dst_row[x] = gdTrueColorAlpha(r, g, b, a);
+ }
+}
+
+static inline void _gdScaleHoriz(gdImagePtr pSrc, unsigned int src_width, unsigned int src_height, gdImagePtr pDst, unsigned int dst_width, unsigned int dst_height)
+{
+ unsigned int u;
+ LineContribType * contrib;
+
+ /* same width, just copy it */
+ if (dst_width == src_width) {
+ unsigned int y;
+ for (y = 0; y < src_height - 1; ++y) {
+ memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width);
+ }
+ }
+
+ contrib = _gdContributionsCalc(dst_width, src_width, (double)dst_width / (double)src_width, pSrc->interpolation);
+ if (contrib == NULL) {
+ return;
+ }
+ /* Scale each row */
+ for (u = 0; u < dst_height - 1; u++) {
+ _gdScaleRow(pSrc, src_width, pDst, dst_width, u, contrib);
+ }
+ _gdContributionsFree (contrib);
+}
+
+static inline void _gdScaleCol (gdImagePtr pSrc, unsigned int src_width, gdImagePtr pRes, unsigned int dst_width, unsigned int dst_height, unsigned int uCol, LineContribType *contrib)
+{
+ unsigned int y;
+ for (y = 0; y < dst_height - 1; y++) {
+ register unsigned char r = 0, g = 0, b = 0, a = 0;
+ const int iLeft = contrib->ContribRow[y].Left;
+ const int iRight = contrib->ContribRow[y].Right;
+ int i;
+ int *row = pRes->tpixels[y];
+
+ /* Accumulate each channel */
+ for (i = iLeft; i <= iRight; i++) {
+ const int pCurSrc = pSrc->tpixels[i][uCol];
+ const int i_iLeft = i - iLeft;
+ r += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetRed(pCurSrc)));
+ g += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetGreen(pCurSrc)));
+ b += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetBlue(pCurSrc)));
+ a += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetAlpha(pCurSrc)));
+ }
+ pRes->tpixels[y][uCol] = gdTrueColorAlpha(r, g, b, a);
+ }
+}
+
+static inline void _gdScaleVert (const gdImagePtr pSrc, const unsigned int src_width, const unsigned int src_height, const gdImagePtr pDst, const unsigned int dst_width, const unsigned int dst_height)
+{
+ unsigned int u;
+ LineContribType * contrib;
+
+ /* same height, copy it */
+ if (src_height == dst_height) {
+ unsigned int y;
+ for (y = 0; y < src_height - 1; ++y) {
+ memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width);
+ }
+ }
+
+ contrib = _gdContributionsCalc(dst_height, src_height, (double)(dst_height) / (double)(src_height), pSrc->interpolation);
+ /* scale each column */
+ for (u = 0; u < dst_width - 1; u++) {
+ _gdScaleCol(pSrc, src_width, pDst, dst_width, dst_height, u, contrib);
+ }
+ _gdContributionsFree(contrib);
+}
+
+gdImagePtr gdImageScaleTwoPass(const gdImagePtr src, const unsigned int src_width, const unsigned int src_height, const unsigned int new_width, const unsigned int new_height)
+{
+ gdImagePtr tmp_im;
+ gdImagePtr dst;
+
+ tmp_im = gdImageCreateTrueColor(new_width, src_height);
+ if (tmp_im == NULL) {
+ return NULL;
+ }
+ gdImageSetInterpolationMethod(tmp_im, src->interpolation_id);
+ _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height);
+
+ dst = gdImageCreateTrueColor(new_width, new_height);
+ if (dst == NULL) {
+ gdFree(tmp_im);
+ return NULL;
+ }
+ gdImageSetInterpolationMethod(dst, src->interpolation_id);
+ _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height);
+ gdFree(tmp_im);
+
+ return dst;
+}
+
+gdImagePtr Scale(const gdImagePtr src, const unsigned int src_width, const unsigned int src_height, const gdImagePtr dst, const unsigned int new_width, const unsigned int new_height)
+{
+ gdImagePtr tmp_im;
+
+ tmp_im = gdImageCreateTrueColor(new_width, src_height);
+ if (tmp_im == NULL) {
+ return NULL;
+ }
+ gdImageSetInterpolationMethod(tmp_im, src->interpolation_id);
+
+ _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height);
+ _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height);
+
+ gdFree(tmp_im);
+ return dst;
+}
+
+/*
+ BilinearFixed, BicubicFixed and nearest implementations are rewamped versions of the implementation in CBitmapEx
+ http://www.codeproject.com/Articles/29121/CBitmapEx-Free-C-Bitmap-Manipulation-Class
+ Integer only implementation, good to have for common usages like pre scale very large
+ images before using another interpolation methods for the last step.
+*/
+gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, const unsigned int height)
+{
+ const unsigned long new_width = MAX(1, width);
+ const unsigned long new_height = MAX(1, height);
+ const float dx = (float)im->sx / (float)new_width;
+ const float dy = (float)im->sy / (float)new_height;
+ const gdFixed f_dx = gd_ftofx(dx);
+ const gdFixed f_dy = gd_ftofx(dy);
+
+ gdImagePtr dst_img;
+ unsigned long dst_offset_x;
+ unsigned long dst_offset_y = 0;
+ unsigned int i;
+
+ dst_img = gdImageCreateTrueColor(new_width, new_height);
+
+ if (dst_img == NULL) {
+ return NULL;
+ }
+
+ for (i=0; i<new_height; i++) {
+ unsigned int j;
+ dst_offset_x = 0;
+ if (im->trueColor) {
+ for (j=0; j<new_width; j++) {
+ const gdFixed f_i = gd_itofx(i);
+ const gdFixed f_j = gd_itofx(j);
+ const gdFixed f_a = gd_mulfx(f_i, f_dy);
+ const gdFixed f_b = gd_mulfx(f_j, f_dx);
+ const long m = gd_fxtoi(f_a);
+ const long n = gd_fxtoi(f_b);
+
+ dst_img->tpixels[dst_offset_y][dst_offset_x++] = im->tpixels[m][n];
+ }
+ } else {
+ for (j=0; j<new_width; j++) {
+ const gdFixed f_i = gd_itofx(i);
+ const gdFixed f_j = gd_itofx(j);
+ const gdFixed f_a = gd_mulfx(f_i, f_dy);
+ const gdFixed f_b = gd_mulfx(f_j, f_dx);
+ const long m = gd_fxtoi(f_a);
+ const long n = gd_fxtoi(f_b);
+
+ dst_img->tpixels[dst_offset_y][dst_offset_x++] = colorIndex2RGBA(im->pixels[m][n]);
+ }
+ }
+ dst_offset_y++;
+ }
+ return dst_img;
+}
+
+static inline int getPixelOverflowColorTC(gdImagePtr im, const int x, const int y, const int color)
+{
+ if (gdImageBoundsSafe(im, x, y)) {
+ const int c = im->tpixels[y][x];
+ if (c == im->transparent) {
+ return gdTrueColorAlpha(0, 0, 0, 127);
+ }
+ return c;
+ } else {
+ register int border = 0;
+ if (y < im->cy1) {
+ border = im->tpixels[0][im->cx1];
+ goto processborder;
+ }
+
+ if (y < im->cy1) {
+ border = im->tpixels[0][im->cx1];
+ goto processborder;
+ }
+
+ if (y > im->cy2) {
+ if (x >= im->cx1 && x <= im->cx1) {
+ border = im->tpixels[im->cy2][x];
+ goto processborder;
+ } else {
+ return gdTrueColorAlpha(0, 0, 0, 127);
+ }
+ }
+
+ /* y is bound safe at this point */
+ if (x < im->cx1) {
+ border = im->tpixels[y][im->cx1];
+ goto processborder;
+ }
+
+ if (x > im->cx2) {
+ border = im->tpixels[y][im->cx2];
+ }
+
+processborder:
+ if (border == im->transparent) {
+ return gdTrueColorAlpha(0, 0, 0, 127);
+ } else{
+ return gdTrueColorAlpha(gdTrueColorGetRed(border), gdTrueColorGetGreen(border), gdTrueColorGetBlue(border), 127);
+ }
+ }
+}
+
+static gdImagePtr gdImageScaleBilinearPalette(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
+{
+ long _width = MAX(1, new_width);
+ long _height = MAX(1, new_height);
+ float dx = (float)gdImageSX(im) / (float)_width;
+ float dy = (float)gdImageSY(im) / (float)_height;
+ gdFixed f_dx = gd_ftofx(dx);
+ gdFixed f_dy = gd_ftofx(dy);
+ gdFixed f_1 = gd_itofx(1);
+
+ int dst_offset_h;
+ int dst_offset_v = 0;
+ long i;
+ gdImagePtr new_img;
+ const int transparent = im->transparent;
+
+ new_img = gdImageCreateTrueColor(new_width, new_height);
+ if (new_img == NULL) {
+ return NULL;
+ }
+ new_img->transparent = gdTrueColorAlpha(im->red[transparent], im->green[transparent], im->blue[transparent], im->alpha[transparent]);
+
+ for (i=0; i < _height; i++) {
+ long j;
+ const gdFixed f_i = gd_itofx(i);
+ const gdFixed f_a = gd_mulfx(f_i, f_dy);
+ register long m = gd_fxtoi(f_a);
+
+ dst_offset_h = 0;
+
+ for (j=0; j < _width; j++) {
+ /* Update bitmap */
+ gdFixed f_j = gd_itofx(j);
+ gdFixed f_b = gd_mulfx(f_j, f_dx);
+
+ const long n = gd_fxtoi(f_b);
+ gdFixed f_f = f_a - gd_itofx(m);
+ gdFixed f_g = f_b - gd_itofx(n);
+
+ const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
+ const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
+ const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
+ const gdFixed f_w4 = gd_mulfx(f_f, f_g);
+ unsigned int pixel1;
+ unsigned int pixel2;
+ unsigned int pixel3;
+ unsigned int pixel4;
+ register gdFixed f_r1, f_r2, f_r3, f_r4,
+ f_g1, f_g2, f_g3, f_g4,
+ f_b1, f_b2, f_b3, f_b4,
+ f_a1, f_a2, f_a3, f_a4;
+
+ /* zero for the background color, nothig gets outside anyway */
+ pixel1 = getPixelOverflowPalette(im, n, m, 0);
+ pixel2 = getPixelOverflowPalette(im, n + 1, m, 0);
+ pixel3 = getPixelOverflowPalette(im, n, m + 1, 0);
+ pixel4 = getPixelOverflowPalette(im, n + 1, m + 1, 0);
+
+ f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
+ f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
+ f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
+ f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
+ f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
+ f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
+ f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
+ f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
+ f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
+ f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
+ f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
+ f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
+ f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
+ f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
+ f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
+ f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
+
+ {
+ const char red = (char) gd_fxtoi(gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4));
+ const char green = (char) gd_fxtoi(gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4));
+ const char blue = (char) gd_fxtoi(gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4));
+ const char alpha = (char) gd_fxtoi(gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4));
+
+ new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha);
+ }
+
+ dst_offset_h++;
+ }
+
+ dst_offset_v++;
+ }
+ return new_img;
+}
+
+static gdImagePtr gdImageScaleBilinearTC(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
+{
+ long dst_w = MAX(1, new_width);
+ long dst_h = MAX(1, new_height);
+ float dx = (float)gdImageSX(im) / (float)dst_w;
+ float dy = (float)gdImageSY(im) / (float)dst_h;
+ gdFixed f_dx = gd_ftofx(dx);
+ gdFixed f_dy = gd_ftofx(dy);
+ gdFixed f_1 = gd_itofx(1);
+
+ int dst_offset_h;
+ int dst_offset_v = 0;
+ int dwSrcTotalOffset;
+ long i;
+ gdImagePtr new_img;
+
+ new_img = gdImageCreateTrueColor(new_width, new_height);
+ if (!new_img){
+ return NULL;
+ }
+
+ for (i=0; i < dst_h; i++) {
+ long j;
+ dst_offset_h = 0;
+ for (j=0; j < dst_w; j++) {
+ /* Update bitmap */
+ gdFixed f_i = gd_itofx(i);
+ gdFixed f_j = gd_itofx(j);
+ gdFixed f_a = gd_mulfx(f_i, f_dy);
+ gdFixed f_b = gd_mulfx(f_j, f_dx);
+ const long m = gd_fxtoi(f_a);
+ const long n = gd_fxtoi(f_b);
+ gdFixed f_f = f_a - gd_itofx(m);
+ gdFixed f_g = f_b - gd_itofx(n);
+
+ const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
+ const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
+ const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
+ const gdFixed f_w4 = gd_mulfx(f_f, f_g);
+ unsigned int pixel1;
+ unsigned int pixel2;
+ unsigned int pixel3;
+ unsigned int pixel4;
+ register gdFixed f_r1, f_r2, f_r3, f_r4,
+ f_g1, f_g2, f_g3, f_g4,
+ f_b1, f_b2, f_b3, f_b4,
+ f_a1, f_a2, f_a3, f_a4;
+ dwSrcTotalOffset = m + n;
+ /* 0 for bgColor, nothing gets outside anyway */
+ pixel1 = getPixelOverflowTC(im, n, m, 0);
+ pixel2 = getPixelOverflowTC(im, n + 1, m, 0);
+ pixel3 = getPixelOverflowTC(im, n, m + 1, 0);
+ pixel4 = getPixelOverflowTC(im, n + 1, m + 1, 0);
+
+ f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
+ f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
+ f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
+ f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
+ f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
+ f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
+ f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
+ f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
+ f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
+ f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
+ f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
+ f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
+ f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
+ f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
+ f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
+ f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
+ {
+ const char red = (char) gd_fxtoi(gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4));
+ const char green = (char) gd_fxtoi(gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4));
+ const char blue = (char) gd_fxtoi(gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4));
+ const char alpha = (char) gd_fxtoi(gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4));
+
+ new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha);
+ }
+
+ dst_offset_h++;
+ }
+
+ dst_offset_v++;
+ }
+ return new_img;
+}
+
+gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height)
+{
+ if (im->trueColor) {
+ return gdImageScaleBilinearTC(im, new_width, new_height);
+ } else {
+ return gdImageScaleBilinearPalette(im, new_width, new_height);
+ }
+}
+
+gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height)
+{
+ const long new_width = MAX(1, width);
+ const long new_height = MAX(1, height);
+ const int src_w = gdImageSX(src);
+ const int src_h = gdImageSY(src);
+ const gdFixed f_dx = gd_ftofx((float)src_w / (float)new_width);
+ const gdFixed f_dy = gd_ftofx((float)src_h / (float)new_height);
+ const gdFixed f_1 = gd_itofx(1);
+ const gdFixed f_2 = gd_itofx(2);
+ const gdFixed f_4 = gd_itofx(4);
+ const gdFixed f_6 = gd_itofx(6);
+ const gdFixed f_gamma = gd_ftofx(1.04f);
+ gdImagePtr dst;
+
+ unsigned int dst_offset_x;
+ unsigned int dst_offset_y = 0;
+ long i;
+
+ /* impact perf a bit, but not that much. Implementation for palette
+ images can be done at a later point.
+ */
+ if (src->trueColor == 0) {
+ gdImagePaletteToTrueColor(src);
+ }
+
+ dst = gdImageCreateTrueColor(new_width, new_height);
+ if (!dst) {
+ return NULL;
+ }
+
+ dst->saveAlphaFlag = 1;
+
+ for (i=0; i < new_height; i++) {
+ long j;
+ dst_offset_x = 0;
+
+ for (j=0; j < new_width; j++) {
+ const gdFixed f_a = gd_mulfx(gd_itofx(i), f_dy);
+ const gdFixed f_b = gd_mulfx(gd_itofx(j), f_dx);
+ const long m = gd_fxtoi(f_a);
+ const long n = gd_fxtoi(f_b);
+ const gdFixed f_f = f_a - gd_itofx(m);
+ const gdFixed f_g = f_b - gd_itofx(n);
+ unsigned int src_offset_x[16], src_offset_y[16];
+ long k;
+ register gdFixed f_red = 0, f_green = 0, f_blue = 0, f_alpha = 0;
+ unsigned char red, green, blue, alpha = 0;
+ int *dst_row = dst->tpixels[dst_offset_y];
+
+ if ((m < 1) || (n < 1)) {
+ src_offset_x[0] = n;
+ src_offset_y[0] = m;
+ } else {
+ src_offset_x[0] = n - 1;
+ src_offset_y[0] = m;
+ }
+
+ if (m < 1) {
+ src_offset_x[1] = n;
+ src_offset_y[1] = m;
+ } else {
+ src_offset_x[1] = n;
+ src_offset_y[1] = m;
+ }
+
+ if ((m < 1) || (n >= src_w - 1)) {
+ src_offset_x[2] = n;
+ src_offset_y[2] = m;
+ } else {
+ src_offset_x[2] = n + 1;
+ src_offset_y[2] = m;
+ }
+
+ if ((m < 1) || (n >= src_w - 2)) {
+ src_offset_x[3] = n;
+ src_offset_y[3] = m;
+ } else {
+ src_offset_x[3] = n + 1 + 1;
+ src_offset_y[3] = m;
+ }
+
+ if (n < 1) {
+ src_offset_x[4] = n;
+ src_offset_y[4] = m;
+ } else {
+ src_offset_x[4] = n - 1;
+ src_offset_y[4] = m;
+ }
+
+ src_offset_x[5] = n;
+ src_offset_y[5] = m;
+ if (n >= src_w-1) {
+ src_offset_x[6] = n;
+ src_offset_y[6] = m;
+ } else {
+ src_offset_x[6] = n + 1;
+ src_offset_y[6] = m;
+ }
+
+ if (n >= src_w - 2) {
+ src_offset_x[7] = n;
+ src_offset_y[7] = m;
+ } else {
+ src_offset_x[7] = n + 1 + 1;
+ src_offset_y[7] = m;
+ }
+
+ if ((m >= src_h - 1) || (n < 1)) {
+ src_offset_x[8] = n;
+ src_offset_y[8] = m;
+ } else {
+ src_offset_x[8] = n - 1;
+ src_offset_y[8] = m;
+ }
+
+ if (m >= src_h - 1) {
+ src_offset_x[8] = n;
+ src_offset_y[8] = m;
+ } else {
+ src_offset_x[9] = n;
+ src_offset_y[9] = m;
+ }
+
+ if ((m >= src_h-1) || (n >= src_w-1)) {
+ src_offset_x[10] = n;
+ src_offset_y[10] = m;
+ } else {
+ src_offset_x[10] = n + 1;
+ src_offset_y[10] = m;
+ }
+
+ if ((m >= src_h - 1) || (n >= src_w - 2)) {
+ src_offset_x[11] = n;
+ src_offset_y[11] = m;
+ } else {
+ src_offset_x[11] = n + 1 + 1;
+ src_offset_y[11] = m;
+ }
+
+ if ((m >= src_h - 2) || (n < 1)) {
+ src_offset_x[12] = n;
+ src_offset_y[12] = m;
+ } else {
+ src_offset_x[12] = n - 1;
+ src_offset_y[12] = m;
+ }
+
+ if (m >= src_h - 2) {
+ src_offset_x[13] = n;
+ src_offset_y[13] = m;
+ } else {
+ src_offset_x[13] = n;
+ src_offset_y[13] = m;
+ }
+
+ if ((m >= src_h - 2) || (n >= src_w - 1)) {
+ src_offset_x[14] = n;
+ src_offset_y[14] = m;
+ } else {
+ src_offset_x[14] = n + 1;
+ src_offset_y[14] = m;
+ }
+
+ if ((m >= src_h - 2) || (n >= src_w - 2)) {
+ src_offset_x[15] = n;
+ src_offset_y[15] = m;
+ } else {
+ src_offset_x[15] = n + 1 + 1;
+ src_offset_y[15] = m;
+ }
+
+ for (k = -1; k < 3; k++) {
+ const gdFixed f = gd_itofx(k)-f_f;
+ const gdFixed f_fm1 = f - f_1;
+ const gdFixed f_fp1 = f + f_1;
+ const gdFixed f_fp2 = f + f_2;
+ register gdFixed f_a = 0, f_b = 0, f_d = 0, f_c = 0;
+ register gdFixed f_RY;
+ int l;
+
+ if (f_fp2 > 0) f_a = gd_mulfx(f_fp2, gd_mulfx(f_fp2,f_fp2));
+ if (f_fp1 > 0) f_b = gd_mulfx(f_fp1, gd_mulfx(f_fp1,f_fp1));
+ if (f > 0) f_c = gd_mulfx(f, gd_mulfx(f,f));
+ if (f_fm1 > 0) f_d = gd_mulfx(f_fm1, gd_mulfx(f_fm1,f_fm1));
+
+ f_RY = gd_divfx((f_a - gd_mulfx(f_4,f_b) + gd_mulfx(f_6,f_c) - gd_mulfx(f_4,f_d)),f_6);
+
+ for (l = -1; l < 3; l++) {
+ const gdFixed f = gd_itofx(l) - f_g;
+ const gdFixed f_fm1 = f - f_1;
+ const gdFixed f_fp1 = f + f_1;
+ const gdFixed f_fp2 = f + f_2;
+ register gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0;
+ register gdFixed f_RX, f_R, f_rs, f_gs, f_bs, f_ba;
+ register int c;
+ const int _k = ((k+1)*4) + (l+1);
+
+ if (f_fp2 > 0) f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
+
+ if (f_fp1 > 0) f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
+
+ if (f > 0) f_c = gd_mulfx(f,gd_mulfx(f,f));
+
+ if (f_fm1 > 0) f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
+
+ f_RX = gd_divfx((f_a-gd_mulfx(f_4,f_b)+gd_mulfx(f_6,f_c)-gd_mulfx(f_4,f_d)),f_6);
+ f_R = gd_mulfx(f_RY,f_RX);
+
+ c = src->tpixels[*(src_offset_y + _k)][*(src_offset_x + _k)];
+ f_rs = gd_itofx(gdTrueColorGetRed(c));
+ f_gs = gd_itofx(gdTrueColorGetGreen(c));
+ f_bs = gd_itofx(gdTrueColorGetBlue(c));
+ f_ba = gd_itofx(gdTrueColorGetAlpha(c));
+
+ f_red += gd_mulfx(f_rs,f_R);
+ f_green += gd_mulfx(f_gs,f_R);
+ f_blue += gd_mulfx(f_bs,f_R);
+ f_alpha += gd_mulfx(f_ba,f_R);
+ }
+ }
+
+ red = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gamma)), 0, 255);
+ green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gamma)), 0, 255);
+ blue = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gamma)), 0, 255);
+ alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gamma)), 0, 127);
+
+ *(dst_row + dst_offset_x) = gdTrueColorAlpha(red, green, blue, alpha);
+
+ dst_offset_x++;
+ }
+ dst_offset_y++;
+ }
+ return dst;
+}
+
+gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height)
+{
+ gdImagePtr im_scaled = NULL;
+
+ if (src == NULL || src->interpolation_id < 0 || src->interpolation_id > GD_METHOD_COUNT) {
+ return 0;
+ }
+
+ switch (src->interpolation_id) {
+ /*Special cases, optimized implementations */
+ case GD_NEAREST_NEIGHBOUR:
+ im_scaled = gdImageScaleNearestNeighbour(src, new_width, new_height);
+ break;
+
+ case GD_BILINEAR_FIXED:
+ im_scaled = gdImageScaleBilinear(src, new_width, new_height);
+ break;
+
+ case GD_BICUBIC_FIXED:
+ im_scaled = gdImageScaleBicubicFixed(src, new_width, new_height);
+ break;
+
+ /* generic */
+ default:
+ if (src->interpolation == NULL) {
+ return NULL;
+ }
+ im_scaled = gdImageScaleTwoPass(src, src->sx, src->sy, new_width, new_height);
+ break;
+ }
+ return im_scaled;
+}
+
+gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor)
+{
+ float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
+ const int src_w = gdImageSX(src);
+ const int src_h = gdImageSY(src);
+ const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f);
+ const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f);
+ const gdFixed f_0_5 = gd_ftofx(0.5f);
+ const gdFixed f_H = gd_itofx(src_h/2);
+ const gdFixed f_W = gd_itofx(src_w/2);
+ const gdFixed f_cos = gd_ftofx(cos(-_angle));
+ const gdFixed f_sin = gd_ftofx(sin(-_angle));
+
+ unsigned int dst_offset_x;
+ unsigned int dst_offset_y = 0;
+ unsigned int i;
+ gdImagePtr dst;
+
+ dst = gdImageCreateTrueColor(new_width, new_height);
+ if (!dst) {
+ return NULL;
+ }
+ dst->saveAlphaFlag = 1;
+ for (i = 0; i < new_height; i++) {
+ unsigned int j;
+ dst_offset_x = 0;
+ for (j = 0; j < new_width; j++) {
+ gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
+ gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
+ gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
+ gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
+ long m = gd_fxtoi(f_m);
+ long n = gd_fxtoi(f_n);
+
+ if ((m > 0) && (m < src_h-1) && (n > 0) && (n < src_w-1)) {
+ if (dst_offset_y < new_height) {
+ dst->tpixels[dst_offset_y][dst_offset_x++] = src->tpixels[m][n];
+ }
+ } else {
+ if (dst_offset_y < new_height) {
+ dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
+ }
+ }
+ }
+ dst_offset_y++;
+ }
+ return dst;
+}
+
+gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor)
+{
+ float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
+ const int src_w = gdImageSX(src);
+ const int src_h = gdImageSY(src);
+ const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f);
+ const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f);
+ const gdFixed f_0_5 = gd_ftofx(0.5f);
+ const gdFixed f_H = gd_itofx(src_h/2);
+ const gdFixed f_W = gd_itofx(src_w/2);
+ const gdFixed f_cos = gd_ftofx(cos(-_angle));
+ const gdFixed f_sin = gd_ftofx(sin(-_angle));
+
+ unsigned int dst_offset_x;
+ unsigned int dst_offset_y = 0;
+ unsigned int i;
+ gdImagePtr dst;
+
+ const gdFixed f_slop_y = f_sin;
+ const gdFixed f_slop_x = f_cos;
+ const gdFixed f_slop = f_slop_x > 0 && f_slop_x > 0 ?
+ f_slop_x > f_slop_y ? gd_divfx(f_slop_y, f_slop_x) : gd_divfx(f_slop_x, f_slop_y)
+ : 0;
+
+
+ dst = gdImageCreateTrueColor(new_width, new_height);
+ if (!dst) {
+ return NULL;
+ }
+ dst->saveAlphaFlag = 1;
+
+ for (i = 0; i < new_height; i++) {
+ unsigned int j;
+ dst_offset_x = 0;
+ for (j = 0; j < new_width; j++) {
+ gdFixed f_i = gd_itofx((int)i - (int)new_height/ 2);
+ gdFixed f_j = gd_itofx((int)j - (int)new_width / 2);
+ gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
+ gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
+ long m = gd_fxtoi(f_m);
+ long n = gd_fxtoi(f_n);
+
+ if ((n <= 0) || (m <= 0) || (m >= src_h) || (n >= src_w)) {
+ dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
+ } else if ((n <= 1) || (m <= 1) || (m >= src_h - 1) || (n >= src_w - 1)) {
+ gdFixed f_127 = gd_itofx(127);
+ register int c = getPixelInterpolated(src, n, m, bgColor);
+ c = c | (( gdTrueColorGetAlpha(c) + ((int)(127* gd_fxtof(f_slop)))) << 24);
+
+ dst->tpixels[dst_offset_y][dst_offset_x++] = _color_blend(bgColor, c);
+ } else {
+ dst->tpixels[dst_offset_y][dst_offset_x++] = getPixelInterpolated(src, n, m, bgColor);
+ }
+ }
+ dst_offset_y++;
+ }
+ return dst;
+}
+
+gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int bgColor)
+{
+ float _angle = (float)((- degrees / 180.0f) * M_PI);
+ const unsigned int src_w = gdImageSX(src);
+ const unsigned int src_h = gdImageSY(src);
+ unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f));
+ unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f));
+ const gdFixed f_0_5 = gd_ftofx(0.5f);
+ const gdFixed f_H = gd_itofx(src_h/2);
+ const gdFixed f_W = gd_itofx(src_w/2);
+ const gdFixed f_cos = gd_ftofx(cos(-_angle));
+ const gdFixed f_sin = gd_ftofx(sin(-_angle));
+ const gdFixed f_1 = gd_itofx(1);
+ unsigned int i;
+ unsigned int dst_offset_x;
+ unsigned int dst_offset_y = 0;
+ unsigned int src_offset_x, src_offset_y;
+ gdImagePtr dst;
+
+ dst = gdImageCreateTrueColor(new_width, new_height);
+ if (dst == NULL) {
+ return NULL;
+ }
+ dst->saveAlphaFlag = 1;
+
+ for (i = 0; i < new_height; i++) {
+ unsigned int j;
+ dst_offset_x = 0;
+
+ for (j=0; j < new_width; j++) {
+ const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
+ const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
+ const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
+ const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
+ const unsigned int m = gd_fxtoi(f_m);
+ const unsigned int n = gd_fxtoi(f_n);
+
+ if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w - 1)) {
+ const gdFixed f_f = f_m - gd_itofx(m);
+ const gdFixed f_g = f_n - gd_itofx(n);
+ const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
+ const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
+ const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
+ const gdFixed f_w4 = gd_mulfx(f_f, f_g);
+
+ if (n < src_w - 1) {
+ src_offset_x = n + 1;
+ src_offset_y = m;
+ }
+
+ if (m < src_h-1) {
+ src_offset_x = n;
+ src_offset_y = m + 1;
+ }
+
+ if (!((n >= src_w-1) || (m >= src_h-1))) {
+ src_offset_x = n + 1;
+ src_offset_y = m + 1;
+ }
+ {
+ const int pixel1 = src->tpixels[src_offset_y][src_offset_x];
+ register int pixel2, pixel3, pixel4;
+
+ if (src_offset_y + 1 >= src_h) {
+ pixel2 = bgColor;
+ pixel3 = bgColor;
+ pixel4 = bgColor;
+ } else if (src_offset_x + 1 >= src_w) {
+ pixel2 = bgColor;
+ pixel3 = bgColor;
+ pixel4 = bgColor;
+ } else {
+ pixel2 = src->tpixels[src_offset_y][src_offset_x + 1];
+ pixel3 = src->tpixels[src_offset_y + 1][src_offset_x];
+ pixel4 = src->tpixels[src_offset_y + 1][src_offset_x + 1];
+ }
+ {
+ const gdFixed f_r1 = gd_itofx(gdTrueColorGetRed(pixel1));
+ const gdFixed f_r2 = gd_itofx(gdTrueColorGetRed(pixel2));
+ const gdFixed f_r3 = gd_itofx(gdTrueColorGetRed(pixel3));
+ const gdFixed f_r4 = gd_itofx(gdTrueColorGetRed(pixel4));
+ const gdFixed f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1));
+ const gdFixed f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2));
+ const gdFixed f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3));
+ const gdFixed f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4));
+ const gdFixed f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1));
+ const gdFixed f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2));
+ const gdFixed f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3));
+ const gdFixed f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4));
+ const gdFixed f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1));
+ const gdFixed f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2));
+ const gdFixed f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3));
+ const gdFixed f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4));
+ const gdFixed f_red = gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4);
+ const gdFixed f_green = gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4);
+ const gdFixed f_blue = gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4);
+ const gdFixed f_alpha = gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4);
+
+ const unsigned char red = (unsigned char) CLAMP(gd_fxtoi(f_red), 0, 255);
+ const unsigned char green = (unsigned char) CLAMP(gd_fxtoi(f_green), 0, 255);
+ const unsigned char blue = (unsigned char) CLAMP(gd_fxtoi(f_blue), 0, 255);
+ const unsigned char alpha = (unsigned char) CLAMP(gd_fxtoi(f_alpha), 0, 127);
+
+ dst->tpixels[dst_offset_y][dst_offset_x++] = gdTrueColorAlpha(red, green, blue, alpha);
+ }
+ }
+ } else {
+ dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor;
+ }
+ }
+ dst_offset_y++;
+ }
+ return dst;
+}
+
+gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const int bgColor)
+{
+ const float _angle = (float)((- degrees / 180.0f) * M_PI);
+ const int src_w = gdImageSX(src);
+ const int src_h = gdImageSY(src);
+ const unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f));
+ const unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f));
+ const gdFixed f_0_5 = gd_ftofx(0.5f);
+ const gdFixed f_H = gd_itofx(src_h/2);
+ const gdFixed f_W = gd_itofx(src_w/2);
+ const gdFixed f_cos = gd_ftofx(cos(-_angle));
+ const gdFixed f_sin = gd_ftofx(sin(-_angle));
+ const gdFixed f_1 = gd_itofx(1);
+ const gdFixed f_2 = gd_itofx(2);
+ const gdFixed f_4 = gd_itofx(4);
+ const gdFixed f_6 = gd_itofx(6);
+ const gdFixed f_gama = gd_ftofx(1.04f);
+
+ unsigned int dst_offset_x;
+ unsigned int dst_offset_y = 0;
+ unsigned int i;
+ gdImagePtr dst;
+
+ dst = gdImageCreateTrueColor(new_width, new_height);
+
+ if (dst == NULL) {
+ return NULL;
+ }
+ dst->saveAlphaFlag = 1;
+
+ for (i=0; i < new_height; i++) {
+ unsigned int j;
+ dst_offset_x = 0;
+
+ for (j=0; j < new_width; j++) {
+ const gdFixed f_i = gd_itofx((int)i - (int)new_height/2);
+ const gdFixed f_j = gd_itofx((int)j - (int)new_width/2);
+ const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H;
+ const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W;
+ const int m = gd_fxtoi(f_m);
+ const int n = gd_fxtoi(f_n);
+
+ if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w-1)) {
+ const gdFixed f_f = f_m - gd_itofx(m);
+ const gdFixed f_g = f_n - gd_itofx(n);
+ unsigned int src_offset_x[16], src_offset_y[16];
+ unsigned char red, green, blue, alpha;
+ gdFixed f_red=0, f_green=0, f_blue=0, f_alpha=0;
+ int k;
+
+ if ((m < 1) || (n < 1)) {
+ src_offset_x[0] = n;
+ src_offset_y[0] = m;
+ } else {
+ src_offset_x[0] = n - 1;
+ src_offset_y[0] = m;
+ }
+
+ if (m < 1) {
+ src_offset_x[1] = n;
+ src_offset_y[1] = m;
+ } else {
+ src_offset_x[1] = n;
+ src_offset_y[1] = m ;
+ }
+
+ if ((m < 1) || (n >= src_w-1)) {
+ src_offset_x[2] = - 1;
+ src_offset_y[2] = - 1;
+ } else {
+ src_offset_x[2] = n + 1;
+ src_offset_y[2] = m ;
+ }
+
+ if ((m < 1) || (n >= src_w-2)) {
+ src_offset_x[3] = - 1;
+ src_offset_y[3] = - 1;
+ } else {
+ src_offset_x[3] = n + 1 + 1;
+ src_offset_y[3] = m ;
+ }
+
+ if (n < 1) {
+ src_offset_x[4] = - 1;
+ src_offset_y[4] = - 1;
+ } else {
+ src_offset_x[4] = n - 1;
+ src_offset_y[4] = m;
+ }
+
+ src_offset_x[5] = n;
+ src_offset_y[5] = m;
+ if (n >= src_w-1) {
+ src_offset_x[6] = - 1;
+ src_offset_y[6] = - 1;
+ } else {
+ src_offset_x[6] = n + 1;
+ src_offset_y[6] = m;
+ }
+
+ if (n >= src_w-2) {
+ src_offset_x[7] = - 1;
+ src_offset_y[7] = - 1;
+ } else {
+ src_offset_x[7] = n + 1 + 1;
+ src_offset_y[7] = m;
+ }
+
+ if ((m >= src_h-1) || (n < 1)) {
+ src_offset_x[8] = - 1;
+ src_offset_y[8] = - 1;
+ } else {
+ src_offset_x[8] = n - 1;
+ src_offset_y[8] = m;
+ }
+
+ if (m >= src_h-1) {
+ src_offset_x[8] = - 1;
+ src_offset_y[8] = - 1;
+ } else {
+ src_offset_x[9] = n;
+ src_offset_y[9] = m;
+ }
+
+ if ((m >= src_h-1) || (n >= src_w-1)) {
+ src_offset_x[10] = - 1;
+ src_offset_y[10] = - 1;
+ } else {
+ src_offset_x[10] = n + 1;
+ src_offset_y[10] = m;
+ }
+
+ if ((m >= src_h-1) || (n >= src_w-2)) {
+ src_offset_x[11] = - 1;
+ src_offset_y[11] = - 1;
+ } else {
+ src_offset_x[11] = n + 1 + 1;
+ src_offset_y[11] = m;
+ }
+
+ if ((m >= src_h-2) || (n < 1)) {
+ src_offset_x[12] = - 1;
+ src_offset_y[12] = - 1;
+ } else {
+ src_offset_x[12] = n - 1;
+ src_offset_y[12] = m;
+ }
+
+ if (m >= src_h-2) {
+ src_offset_x[13] = - 1;
+ src_offset_y[13] = - 1;
+ } else {
+ src_offset_x[13] = n;
+ src_offset_y[13] = m;
+ }
+
+ if ((m >= src_h-2) || (n >= src_w - 1)) {
+ src_offset_x[14] = - 1;
+ src_offset_y[14] = - 1;
+ } else {
+ src_offset_x[14] = n + 1;
+ src_offset_y[14] = m;
+ }
+
+ if ((m >= src_h-2) || (n >= src_w-2)) {
+ src_offset_x[15] = - 1;
+ src_offset_y[15] = - 1;
+ } else {
+ src_offset_x[15] = n + 1 + 1;
+ src_offset_y[15] = m;
+ }
+
+ for (k=-1; k<3; k++) {
+ const gdFixed f = gd_itofx(k)-f_f;
+ const gdFixed f_fm1 = f - f_1;
+ const gdFixed f_fp1 = f + f_1;
+ const gdFixed f_fp2 = f + f_2;
+ gdFixed f_a = 0, f_b = 0,f_c = 0, f_d = 0;
+ gdFixed f_RY;
+ int l;
+
+ if (f_fp2 > 0) {
+ f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
+ }
+
+ if (f_fp1 > 0) {
+ f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
+ }
+
+ if (f > 0) {
+ f_c = gd_mulfx(f,gd_mulfx(f,f));
+ }
+
+ if (f_fm1 > 0) {
+ f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
+ }
+ f_RY = gd_divfx((f_a-gd_mulfx(f_4,f_b)+gd_mulfx(f_6,f_c)-gd_mulfx(f_4,f_d)),f_6);
+
+ for (l=-1; l< 3; l++) {
+ const gdFixed f = gd_itofx(l) - f_g;
+ const gdFixed f_fm1 = f - f_1;
+ const gdFixed f_fp1 = f + f_1;
+ const gdFixed f_fp2 = f + f_2;
+ gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0;
+ gdFixed f_RX, f_R;
+ const int _k = ((k + 1) * 4) + (l + 1);
+ register gdFixed f_rs, f_gs, f_bs, f_as;
+ register int c;
+
+ if (f_fp2 > 0) {
+ f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2));
+ }
+
+ if (f_fp1 > 0) {
+ f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1));
+ }
+
+ if (f > 0) {
+ f_c = gd_mulfx(f,gd_mulfx(f,f));
+ }
+
+ if (f_fm1 > 0) {
+ f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1));
+ }
+
+ f_RX = gd_divfx((f_a - gd_mulfx(f_4, f_b) + gd_mulfx(f_6, f_c) - gd_mulfx(f_4, f_d)), f_6);
+ f_R = gd_mulfx(f_RY, f_RX);
+
+ if ((src_offset_x[_k] <= 0) || (src_offset_y[_k] <= 0) || (src_offset_y[_k] >= src_h) || (src_offset_x[_k] >= src_w)) {
+ c = bgColor;
+ } else if ((src_offset_x[_k] <= 1) || (src_offset_y[_k] <= 1) || (src_offset_y[_k] >= (int)src_h - 1) || (src_offset_x[_k] >= (int)src_w - 1)) {
+ gdFixed f_127 = gd_itofx(127);
+ c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
+ c = c | (( (int) (gd_fxtof(gd_mulfx(f_R, f_127)) + 50.5f)) << 24);
+ c = _color_blend(bgColor, c);
+ } else {
+ c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]];
+ }
+
+ f_rs = gd_itofx(gdTrueColorGetRed(c));
+ f_gs = gd_itofx(gdTrueColorGetGreen(c));
+ f_bs = gd_itofx(gdTrueColorGetBlue(c));
+ f_as = gd_itofx(gdTrueColorGetAlpha(c));
+
+ f_red += gd_mulfx(f_rs, f_R);
+ f_green += gd_mulfx(f_gs, f_R);
+ f_blue += gd_mulfx(f_bs, f_R);
+ f_alpha += gd_mulfx(f_as, f_R);
+ }
+ }
+
+ red = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gama)), 0, 255);
+ green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gama)), 0, 255);
+ blue = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gama)), 0, 255);
+ alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gama)), 0, 127);
+
+ dst->tpixels[dst_offset_y][dst_offset_x] = gdTrueColorAlpha(red, green, blue, alpha);
+ } else {
+ dst->tpixels[dst_offset_y][dst_offset_x] = bgColor;
+ }
+ dst_offset_x++;
+ }
+
+ dst_offset_y++;
+ }
+ return dst;
+}
+
+gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor)
+{
+ const int angle_rounded = (int)floor(angle * 100);
+
+ if (bgcolor < 0) {
+ return NULL;
+ }
+
+ /* impact perf a bit, but not that much. Implementation for palette
+ images can be done at a later point.
+ */
+ if (src->trueColor == 0) {
+ if (bgcolor >= 0) {
+ bgcolor = gdTrueColorAlpha(src->red[bgcolor], src->green[bgcolor], src->blue[bgcolor], src->alpha[bgcolor]);
+ }
+ gdImagePaletteToTrueColor(src);
+ }
+
+ /* no interpolation needed here */
+ switch (angle_rounded) {
+ case 9000:
+ return gdImageRotate90(src, 0);
+ case 18000:
+ return gdImageRotate180(src, 0);
+ case 27000:
+ return gdImageRotate270(src, 0);
+ }
+
+ if (src == NULL || src->interpolation_id < 1 || src->interpolation_id > GD_METHOD_COUNT) {
+ return NULL;
+ }
+
+ switch (src->interpolation_id) {
+ case GD_NEAREST_NEIGHBOUR:
+ return gdImageRotateNearestNeighbour(src, angle, bgcolor);
+ break;
+
+ case GD_BILINEAR_FIXED:
+ return gdImageRotateBilinear(src, angle, bgcolor);
+ break;
+
+ case GD_BICUBIC_FIXED:
+ return gdImageRotateBicubicFixed(src, angle, bgcolor);
+ break;
+
+ default:
+ return gdImageRotateGeneric(src, angle, bgcolor);
+ }
+ return NULL;
+}
+
+/**
+ * Title: Affine transformation
+ **/
+
+/**
+ * Group: Transform
+ **/
+
+ static void gdImageClipRectangle(gdImagePtr im, gdRectPtr r)
+{
+ int c1x, c1y, c2x, c2y;
+ int x1,y1;
+
+ gdImageGetClip(im, &c1x, &c1y, &c2x, &c2y);
+ x1 = r->x + r->width - 1;
+ y1 = r->y + r->height - 1;
+ r->x = CLAMP(r->x, c1x, c2x);
+ r->y = CLAMP(r->y, c1y, c2y);
+ r->width = CLAMP(x1, c1x, c2x) - r->x + 1;
+ r->height = CLAMP(y1, c1y, c2y) - r->y + 1;
+}
+
+void gdDumpRect(const char *msg, gdRectPtr r)
+{
+ printf("%s (%i, %i) (%i, %i)\n", msg, r->x, r->y, r->width, r->height);
+}
+
+/**
+ * Function: gdTransformAffineGetImage
+ * Applies an affine transformation to a region and return an image
+ * containing the complete transformation.
+ *
+ * Parameters:
+ * dst - Pointer to a gdImagePtr to store the created image, NULL when
+ * the creation or the transformation failed
+ * src - Source image
+ * src_area - rectangle defining the source region to transform
+ * dstY - Y position in the destination image
+ * affine - The desired affine transformation
+ *
+ * Returns:
+ * GD_TRUE if the affine is rectilinear or GD_FALSE
+ */
+int gdTransformAffineGetImage(gdImagePtr *dst,
+ const gdImagePtr src,
+ gdRectPtr src_area,
+ const double affine[6])
+{
+ int res;
+ double m[6];
+ gdRect bbox;
+ gdRect area_full;
+
+ if (src_area == NULL) {
+ area_full.x = 0;
+ area_full.y = 0;
+ area_full.width = gdImageSX(src);
+ area_full.height = gdImageSY(src);
+ src_area = &area_full;
+ }
+
+ gdTransformAffineBoundingBox(src_area, affine, &bbox);
+
+ *dst = gdImageCreateTrueColor(bbox.width, bbox.height);
+ if (*dst == NULL) {
+ return GD_FALSE;
+ }
+ (*dst)->saveAlphaFlag = 1;
+
+ if (!src->trueColor) {
+ gdImagePaletteToTrueColor(src);
+ }
+
+ /* Translate to dst origin (0,0) */
+ gdAffineTranslate(m, -bbox.x, -bbox.y);
+ gdAffineConcat(m, affine, m);
+
+ gdImageAlphaBlending(*dst, 0);
+
+ res = gdTransformAffineCopy(*dst,
+ 0,0,
+ src,
+ src_area,
+ m);
+
+ if (res != GD_TRUE) {
+ gdImageDestroy(*dst);
+ dst = NULL;
+ return GD_FALSE;
+ } else {
+ return GD_TRUE;
+ }
+}
+
+/**
+ * Function: gdTransformAffineCopy
+ * Applies an affine transformation to a region and copy the result
+ * in a destination to the given position.
+ *
+ * Parameters:
+ * dst - Image to draw the transformed image
+ * src - Source image
+ * dstX - X position in the destination image
+ * dstY - Y position in the destination image
+ * src_area - Rectangular region to rotate in the src image
+ *
+ * Returns:
+ * GD_TRUE if the affine is rectilinear or GD_FALSE
+ */
+int gdTransformAffineCopy(gdImagePtr dst,
+ int dst_x, int dst_y,
+ const gdImagePtr src,
+ gdRectPtr src_region,
+ const double affine[6])
+{
+ int c1x,c1y,c2x,c2y;
+ int backclip = 0;
+ int backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2;
+ register int x, y, src_offset_x, src_offset_y;
+ double inv[6];
+ int *dst_p;
+ gdPointF pt, src_pt;
+ gdRect bbox;
+ int end_x, end_y;
+ gdInterpolationMethod interpolation_id_bak = GD_DEFAULT;
+ interpolation_method interpolation_bak;
+
+ /* These methods use special implementations */
+ if (src->interpolation_id == GD_BILINEAR_FIXED || src->interpolation_id == GD_BICUBIC_FIXED || src->interpolation_id == GD_NEAREST_NEIGHBOUR) {
+ interpolation_id_bak = src->interpolation_id;
+ interpolation_bak = src->interpolation;
+
+ gdImageSetInterpolationMethod(src, GD_BICUBIC);
+ }
+
+
+ gdImageClipRectangle(src, src_region);
+
+ if (src_region->x > 0 || src_region->y > 0
+ || src_region->width < gdImageSX(src)
+ || src_region->height < gdImageSY(src)) {
+ backclip = 1;
+
+ gdImageGetClip(src, &backup_clipx1, &backup_clipy1,
+ &backup_clipx2, &backup_clipy2);
+
+ gdImageSetClip(src, src_region->x, src_region->y,
+ src_region->x + src_region->width - 1,
+ src_region->y + src_region->height - 1);
+ }
+
+ if (!gdTransformAffineBoundingBox(src_region, affine, &bbox)) {
+ if (backclip) {
+ gdImageSetClip(src, backup_clipx1, backup_clipy1,
+ backup_clipx2, backup_clipy2);
+ }
+ gdImageSetInterpolationMethod(src, interpolation_id_bak);
+ return GD_FALSE;
+ }
+
+ gdImageGetClip(dst, &c1x, &c1y, &c2x, &c2y);
+
+ end_x = bbox.width + (int) fabs(bbox.x);
+ end_y = bbox.height + (int) fabs(bbox.y);
+
+ /* Get inverse affine to let us work with destination -> source */
+ gdAffineInvert(inv, affine);
+
+ src_offset_x = src_region->x;
+ src_offset_y = src_region->y;
+
+ if (dst->alphaBlendingFlag) {
+ for (y = bbox.y; y <= end_y; y++) {
+ pt.y = y + 0.5;
+ for (x = 0; x <= end_x; x++) {
+ pt.x = x + 0.5;
+ gdAffineApplyToPointF(&src_pt, &pt, inv);
+ gdImageSetPixel(dst, dst_x + x, dst_y + y, getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, 0));
+ }
+ }
+ } else {
+ for (y = 0; y <= end_y; y++) {
+ pt.y = y + 0.5 + bbox.y;
+ if ((dst_y + y) < 0 || ((dst_y + y) > gdImageSY(dst) -1)) {
+ continue;
+ }
+ dst_p = dst->tpixels[dst_y + y] + dst_x;
+
+ for (x = 0; x <= end_x; x++) {
+ pt.x = x + 0.5 + bbox.x;
+ gdAffineApplyToPointF(&src_pt, &pt, inv);
+
+ if ((dst_x + x) < 0 || (dst_x + x) > (gdImageSX(dst) - 1)) {
+ break;
+ }
+ *(dst_p++) = getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, -1);
+ }
+ }
+ }
+
+ /* Restore clip if required */
+ if (backclip) {
+ gdImageSetClip(src, backup_clipx1, backup_clipy1,
+ backup_clipx2, backup_clipy2);
+ }
+
+ gdImageSetInterpolationMethod(src, interpolation_id_bak);
+ return GD_TRUE;
+}
+
+/**
+ * Function: gdTransformAffineBoundingBox
+ * Returns the bounding box of an affine transformation applied to a
+ * rectangular area <gdRect>
+ *
+ * Parameters:
+ * src - Rectangular source area for the affine transformation
+ * affine - the affine transformation
+ * bbox - the resulting bounding box
+ *
+ * Returns:
+ * GD_TRUE if the affine is rectilinear or GD_FALSE
+ */
+int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox)
+{
+ gdPointF extent[4], min, max, point;
+ int i;
+
+ extent[0].x=0.0;
+ extent[0].y=0.0;
+ extent[1].x=(double) src->width;
+ extent[1].y=0.0;
+ extent[2].x=(double) src->width;
+ extent[2].y=(double) src->height;
+ extent[3].x=0.0;
+ extent[3].y=(double) src->height;
+
+ for (i=0; i < 4; i++) {
+ point=extent[i];
+ if (gdAffineApplyToPointF(&extent[i], &point, affine) != GD_TRUE) {
+ return GD_FALSE;
+ }
+ }
+ min=extent[0];
+ max=extent[0];
+
+ for (i=1; i < 4; i++) {
+ if (min.x > extent[i].x)
+ min.x=extent[i].x;
+ if (min.y > extent[i].y)
+ min.y=extent[i].y;
+ if (max.x < extent[i].x)
+ max.x=extent[i].x;
+ if (max.y < extent[i].y)
+ max.y=extent[i].y;
+ }
+ bbox->x = (int) min.x;
+ bbox->y = (int) min.y;
+ bbox->width = (int) floor(max.x - min.x) - 1;
+ bbox->height = (int) floor(max.y - min.y);
+ return GD_TRUE;
+}
+
+int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id)
+{
+ if (im == NULL || id < 0 || id > GD_METHOD_COUNT) {
+ return 0;
+ }
+
+ switch (id) {
+ case GD_DEFAULT:
+ im->interpolation_id = GD_BILINEAR_FIXED;
+ im->interpolation = NULL;
+ break;
+
+ /* Optimized versions */
+ case GD_BILINEAR_FIXED:
+ case GD_BICUBIC_FIXED:
+ case GD_NEAREST_NEIGHBOUR:
+ case GD_WEIGHTED4:
+ im->interpolation = NULL;
+ break;
+
+ /* generic versions*/
+ case GD_BELL:
+ im->interpolation = filter_bell;
+ break;
+ case GD_BESSEL:
+ im->interpolation = filter_bessel;
+ break;
+ case GD_BICUBIC:
+ im->interpolation = filter_bicubic;
+ break;
+ case GD_BLACKMAN:
+ im->interpolation = filter_blackman;
+ break;
+ case GD_BOX:
+ im->interpolation = filter_box;
+ break;
+ case GD_BSPLINE:
+ im->interpolation = filter_bspline;
+ break;
+ case GD_CATMULLROM:
+ im->interpolation = filter_catmullrom;
+ break;
+ case GD_GAUSSIAN:
+ im->interpolation = filter_gaussian;
+ break;
+ case GD_GENERALIZED_CUBIC:
+ im->interpolation = filter_generalized_cubic;
+ break;
+ case GD_HERMITE:
+ im->interpolation = filter_hermite;
+ break;
+ case GD_HAMMING:
+ im->interpolation = filter_hamming;
+ break;
+ case GD_HANNING:
+ im->interpolation = filter_hanning;
+ break;
+ case GD_MITCHELL:
+ im->interpolation = filter_mitchell;
+ break;
+ case GD_POWER:
+ im->interpolation = filter_power;
+ break;
+ case GD_QUADRATIC:
+ im->interpolation = filter_quadratic;
+ break;
+ case GD_SINC:
+ im->interpolation = filter_sinc;
+ break;
+ case GD_TRIANGLE:
+ im->interpolation = filter_triangle;
+ break;
+
+ default:
+ return 0;
+ break;
+ }
+ im->interpolation_id = id;
+ return 1;
+}
+
+#ifdef _MSC_VER
+# pragma optimize("", on)
+#endif
diff --git a/ext/gd/libgd/gd_jpeg.c b/ext/gd/libgd/gd_jpeg.c
index 175c5b85fd..a882b28c88 100644
--- a/ext/gd/libgd/gd_jpeg.c
+++ b/ext/gd/libgd/gd_jpeg.c
@@ -269,21 +269,31 @@ void gdImageJpegCtx (gdImagePtr im, gdIOCtx * outfile, int quality)
gdFree (row);
}
-gdImagePtr gdImageCreateFromJpeg (FILE * inFile, int ignore_warning)
+gdImagePtr gdImageCreateFromJpeg (FILE * inFile)
+{
+ return gdImageCreateFromJpegEx(inFile, 1);
+}
+
+gdImagePtr gdImageCreateFromJpegEx (FILE * inFile, int ignore_warning)
{
gdImagePtr im;
gdIOCtx *in = gdNewFileCtx(inFile);
- im = gdImageCreateFromJpegCtx(in, ignore_warning);
+ im = gdImageCreateFromJpegCtxEx(in, ignore_warning);
in->gd_free (in);
return im;
}
-gdImagePtr gdImageCreateFromJpegPtr (int size, void *data, int ignore_warning)
+gdImagePtr gdImageCreateFromJpegPtr (int size, void *data)
+{
+ return gdImageCreateFromJpegPtrEx(size, data, 1);
+}
+
+gdImagePtr gdImageCreateFromJpegPtrEx (int size, void *data, int ignore_warning)
{
gdImagePtr im;
gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
- im = gdImageCreateFromJpegCtx(in, ignore_warning);
+ im = gdImageCreateFromJpegCtxEx(in, ignore_warning);
in->gd_free(in);
return im;
@@ -298,7 +308,12 @@ static int CMYKToRGB(int c, int m, int y, int k, int inverted);
* Create a gd-format image from the JPEG-format INFILE. Returns the
* image, or NULL upon error.
*/
-gdImagePtr gdImageCreateFromJpegCtx (gdIOCtx * infile, int ignore_warning)
+gdImagePtr gdImageCreateFromJpegCtx (gdIOCtx * infile)
+{
+ return gdImageCreateFromJpegCtxEx(infile, 1);
+}
+
+gdImagePtr gdImageCreateFromJpegCtxEx (gdIOCtx * infile, int ignore_warning)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
diff --git a/ext/gd/libgd/gd_matrix.c b/ext/gd/libgd/gd_matrix.c
new file mode 100644
index 0000000000..83438bdbe3
--- /dev/null
+++ b/ext/gd/libgd/gd_matrix.c
@@ -0,0 +1,334 @@
+#include "gd.h"
+#include <math.h>
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+
+/**
+ * Title: Matrix
+ * Group: Affine Matrix
+ */
+
+/**
+ * Function: gdAffineApplyToPointF
+ * Applies an affine transformation to a point (floating point
+ * gdPointF)
+ *
+ *
+ * Parameters:
+ * dst - Where to store the resulting point
+ * affine - Source Point
+ * flip_horz - affine matrix
+ *
+ * Returns:
+ * GD_TRUE if the affine is rectilinear or GD_FALSE
+ */
+int gdAffineApplyToPointF (gdPointFPtr dst, const gdPointFPtr src,
+ const double affine[6])
+{
+ double x = src->x;
+ double y = src->y;
+ x = src->x;
+ y = src->y;
+ dst->x = x * affine[0] + y * affine[2] + affine[4];
+ dst->y = x * affine[1] + y * affine[3] + affine[5];
+ return GD_TRUE;
+}
+
+/**
+ * Function: gdAffineInvert
+ * Find the inverse of an affine transformation.
+ *
+ * All non-degenerate affine transforms are invertible. Applying the
+ * inverted matrix will restore the original values. Multiplying <src>
+ * by <dst> (commutative) will return the identity affine (rounding
+ * error possible).
+ *
+ * Parameters:
+ * dst - Where to store the resulting affine transform
+ * src_affine - Original affine matrix
+ * flip_horz - Whether or not to flip horizontally
+ * flip_vert - Whether or not to flip vertically
+ *
+ * See also:
+ * <gdAffineIdentity>
+ *
+ * Returns:
+ * GD_TRUE if the affine is rectilinear or GD_FALSE
+ */
+int gdAffineInvert (double dst[6], const double src[6])
+{
+ double r_det = (src[0] * src[3] - src[1] * src[2]);
+
+ if (r_det <= 0.0) {
+ return GD_FALSE;
+ }
+
+ r_det = 1.0 / r_det;
+ dst[0] = src[3] * r_det;
+ dst[1] = -src[1] * r_det;
+ dst[2] = -src[2] * r_det;
+ dst[3] = src[0] * r_det;
+ dst[4] = -src[4] * dst[0] - src[5] * dst[2];
+ dst[5] = -src[4] * dst[1] - src[5] * dst[3];
+ return GD_TRUE;
+}
+
+/**
+ * Function: gdAffineFlip
+ * Flip an affine transformation horizontally or vertically.
+ *
+ * Flips the affine transform, giving GD_FALSE for <flip_horz> and
+ * <flip_vert> will clone the affine matrix. GD_TRUE for both will
+ * copy a 180° rotation.
+ *
+ * Parameters:
+ * dst - Where to store the resulting affine transform
+ * src_affine - Original affine matrix
+ * flip_h - Whether or not to flip horizontally
+ * flip_v - Whether or not to flip vertically
+ *
+ * Returns:
+ * GD_SUCCESS on success or GD_FAILURE
+ */
+int gdAffineFlip (double dst[6], const double src[6], const int flip_h, const int flip_v)
+{
+ dst[0] = flip_h ? - src[0] : src[0];
+ dst[1] = flip_h ? - src[1] : src[1];
+ dst[2] = flip_v ? - src[2] : src[2];
+ dst[3] = flip_v ? - src[3] : src[3];
+ dst[4] = flip_h ? - src[4] : src[4];
+ dst[5] = flip_v ? - src[5] : src[5];
+ return GD_TRUE;
+}
+
+/**
+ * Function: gdAffineConcat
+ * Concat (Multiply) two affine transformation matrices.
+ *
+ * Concats two affine transforms together, i.e. the result
+ * will be the equivalent of doing first the transformation m1 and then
+ * m2. All parameters can be the same matrix (safe to call using
+ * the same array for all three arguments).
+ *
+ * Parameters:
+ * dst - Where to store the resulting affine transform
+ * m1 - First affine matrix
+ * m2 - Second affine matrix
+ *
+ * Returns:
+ * GD_SUCCESS on success or GD_FAILURE
+ */
+int gdAffineConcat (double dst[6], const double m1[6], const double m2[6])
+{
+ double dst0, dst1, dst2, dst3, dst4, dst5;
+
+ dst0 = m1[0] * m2[0] + m1[1] * m2[2];
+ dst1 = m1[0] * m2[1] + m1[1] * m2[3];
+ dst2 = m1[2] * m2[0] + m1[3] * m2[2];
+ dst3 = m1[2] * m2[1] + m1[3] * m2[3];
+ dst4 = m1[4] * m2[0] + m1[5] * m2[2] + m2[4];
+ dst5 = m1[4] * m2[1] + m1[5] * m2[3] + m2[5];
+ dst[0] = dst0;
+ dst[1] = dst1;
+ dst[2] = dst2;
+ dst[3] = dst3;
+ dst[4] = dst4;
+ dst[5] = dst5;
+ return GD_TRUE;
+}
+
+/**
+ * Function: gdAffineIdentity
+ * Set up the identity matrix.
+ *
+ * Parameters:
+ * dst - Where to store the resulting affine transform
+ *
+ * Returns:
+ * GD_SUCCESS on success or GD_FAILURE
+ */
+int gdAffineIdentity (double dst[6])
+{
+ dst[0] = 1;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = 1;
+ dst[4] = 0;
+ dst[5] = 0;
+ return GD_TRUE;
+}
+
+/**
+ * Function: gdAffineScale
+ * Set up a scaling matrix.
+ *
+ * Parameters:
+ * scale_x - X scale factor
+ * scale_y - Y scale factor
+ *
+ * Returns:
+ * GD_SUCCESS on success or GD_FAILURE
+ */
+int gdAffineScale (double dst[6], const double scale_x, const double scale_y)
+{
+ dst[0] = scale_x;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = scale_y;
+ dst[4] = 0;
+ dst[5] = 0;
+ return GD_TRUE;
+}
+
+/**
+ * Function: gdAffineRotate
+ * Set up a rotation affine transform.
+ *
+ * Like the other angle in libGD, in which increasing y moves
+ * downward, this is a counterclockwise rotation.
+ *
+ * Parameters:
+ * dst - Where to store the resulting affine transform
+ * angle - Rotation angle in degrees
+ *
+ * Returns:
+ * GD_SUCCESS on success or GD_FAILURE
+ */
+int gdAffineRotate (double dst[6], const double angle)
+{
+ const double sin_t = sin (angle * M_PI / 180.0);
+ const double cos_t = cos (angle * M_PI / 180.0);
+
+ dst[0] = cos_t;
+ dst[1] = sin_t;
+ dst[2] = -sin_t;
+ dst[3] = cos_t;
+ dst[4] = 0;
+ dst[5] = 0;
+ return GD_TRUE;
+}
+
+/**
+ * Function: gdAffineShearHorizontal
+ * Set up a horizontal shearing matrix || becomes \\.
+ *
+ * Parameters:
+ * dst - Where to store the resulting affine transform
+ * angle - Shear angle in degrees
+ *
+ * Returns:
+ * GD_SUCCESS on success or GD_FAILURE
+ */
+int gdAffineShearHorizontal(double dst[6], const double angle)
+{
+ dst[0] = 1;
+ dst[1] = 0;
+ dst[2] = tan(angle * M_PI / 180.0);
+ dst[3] = 1;
+ dst[4] = 0;
+ dst[5] = 0;
+ return GD_TRUE;
+}
+
+/**
+ * Function: gdAffineShearVertical
+ * Set up a vertical shearing matrix, columns are untouched.
+ *
+ * Parameters:
+ * dst - Where to store the resulting affine transform
+ * angle - Shear angle in degrees
+ *
+ * Returns:
+ * GD_SUCCESS on success or GD_FAILURE
+ */
+int gdAffineShearVertical(double dst[6], const double angle)
+{
+ dst[0] = 1;
+ dst[1] = tan(angle * M_PI / 180.0);;
+ dst[2] = 0;
+ dst[3] = 1;
+ dst[4] = 0;
+ dst[5] = 0;
+ return GD_TRUE;
+}
+
+/**
+ * Function: gdAffineTranslate
+ * Set up a translation matrix.
+ *
+ * Parameters:
+ * dst - Where to store the resulting affine transform
+ * offset_x - Horizontal translation amount
+ * offset_y - Vertical translation amount
+ *
+ * Returns:
+ * GD_SUCCESS on success or GD_FAILURE
+ */
+int gdAffineTranslate (double dst[6], const double offset_x, const double offset_y)
+{
+ dst[0] = 1;
+ dst[1] = 0;
+ dst[2] = 0;
+ dst[3] = 1;
+ dst[4] = offset_x;
+ dst[5] = offset_y;
+ return GD_TRUE;
+}
+
+/**
+ * gdAffineexpansion: Find the affine's expansion factor.
+ * @src: The affine transformation.
+ *
+ * Finds the expansion factor, i.e. the square root of the factor
+ * by which the affine transform affects area. In an affine transform
+ * composed of scaling, rotation, shearing, and translation, returns
+ * the amount of scaling.
+ *
+ * GD_SUCCESS on success or GD_FAILURE
+ **/
+double gdAffineExpansion (const double src[6])
+{
+ return sqrt (fabs (src[0] * src[3] - src[1] * src[2]));
+}
+
+/**
+ * Function: gdAffineRectilinear
+ * Determines whether the affine transformation is axis aligned. A
+ * tolerance has been implemented using GD_EPSILON.
+ *
+ * Parameters:
+ * m - The affine transformation
+ *
+ * Returns:
+ * GD_TRUE if the affine is rectilinear or GD_FALSE
+ */
+int gdAffineRectilinear (const double m[6])
+{
+ return ((fabs (m[1]) < GD_EPSILON && fabs (m[2]) < GD_EPSILON) ||
+ (fabs (m[0]) < GD_EPSILON && fabs (m[3]) < GD_EPSILON));
+}
+
+/**
+ * Function: gdAffineEqual
+ * Determines whether two affine transformations are equal. A tolerance
+ * has been implemented using GD_EPSILON.
+ *
+ * Parameters:
+ * m1 - The first affine transformation
+ * m2 - The first affine transformation
+ *
+ * Returns:
+ * GD_SUCCESS on success or GD_FAILURE
+ */
+int gdAffineEqual (const double m1[6], const double m2[6])
+{
+ return (fabs (m1[0] - m2[0]) < GD_EPSILON &&
+ fabs (m1[1] - m2[1]) < GD_EPSILON &&
+ fabs (m1[2] - m2[2]) < GD_EPSILON &&
+ fabs (m1[3] - m2[3]) < GD_EPSILON &&
+ fabs (m1[4] - m2[4]) < GD_EPSILON &&
+ fabs (m1[5] - m2[5]) < GD_EPSILON);
+}
+
diff --git a/ext/gd/libgd/gd_png.c b/ext/gd/libgd/gd_png.c
index bdbb7ee7d3..a012cc63b8 100644
--- a/ext/gd/libgd/gd_png.c
+++ b/ext/gd/libgd/gd_png.c
@@ -134,6 +134,7 @@ gdImagePtr gdImageCreateFromPngCtx (gdIOCtx * infile)
volatile int transparent = -1;
volatile int palette_allocated = FALSE;
+
/* Make sure the signature can't match by dumb luck -- TBB */
/* GRR: isn't sizeof(infile) equal to the size of the pointer? */
memset (sig, 0, sizeof(sig));
@@ -345,6 +346,7 @@ gdImagePtr gdImageCreateFromPngCtx (gdIOCtx * infile)
open[i] = 1;
}
}
+
/* 2.0.12: Slaven Rezic: palette images are not the only images
* with a simple transparent color setting.
*/
diff --git a/ext/gd/libgd/gd_transform.c b/ext/gd/libgd/gd_transform.c
new file mode 100644
index 0000000000..9051525eec
--- /dev/null
+++ b/ext/gd/libgd/gd_transform.c
@@ -0,0 +1,73 @@
+#include "gd.h"
+
+void gdImageFlipVertical(gdImagePtr im)
+{
+ register int x, y;
+
+ if (im->trueColor) {
+ for (y = 0; y < im->sy / 2; y++) {
+ int *row_dst = im->tpixels[y];
+ int *row_src = im->tpixels[im->sy - 1 - y];
+ for (x = 0; x < im->sx; x++) {
+ register int p;
+ p = row_dst[x];
+ row_dst[x] = im->tpixels[im->sy - 1 - y][x];
+ row_src[x] = p;
+ }
+ }
+ } else {
+ unsigned char p;
+ for (y = 0; y < im->sy / 2; y++) {
+ for (x = 0; x < im->sx; x++) {
+ p = im->pixels[y][x];
+ im->pixels[y][x] = im->pixels[im->sy - 1 - y][x];
+ im->pixels[im->sy - 1 - y][x] = p;
+ }
+ }
+ }
+ return;
+}
+
+void gdImageFlipHorizontal(gdImagePtr im)
+{
+
+ int x, y;
+
+ if (im->trueColor) {
+ int *px1, *px2, tmp;
+
+ for (y = 0; y < im->sy; y++) {
+ px1 = im->tpixels[y];
+ px2 = im->tpixels[y] + im->sx - 1;
+ for (x = 0; x < (im->sx >> 1); x++) {
+ tmp = *px1;
+ *px1 = *px2;
+ *px2 = tmp;
+ px1++;
+ px2--;
+ }
+ }
+ } else {
+ unsigned char *px1, *px2, tmp;
+
+ for (y = 0; y < im->sy; y++) {
+ px1 = im->pixels[y];
+ px2 = im->pixels[y] + im->sx - 1;
+ for (x = 0; x < (im->sx >> 1); x++) {
+ tmp = *px1;
+ *px1 = *px2;
+ *px2 = tmp;
+ px1++;
+ px2--;
+ }
+ }
+ }
+}
+
+void gdImageFlipBoth(gdImagePtr im)
+{
+ gdImageFlipVertical(im);
+ gdImageFlipHorizontal(im);
+}
+
+
diff --git a/ext/gd/libgd/gd_webp.c b/ext/gd/libgd/gd_webp.c
index 889f5f10a6..bf9ac9dd0e 100644
--- a/ext/gd/libgd/gd_webp.c
+++ b/ext/gd/libgd/gd_webp.c
@@ -58,11 +58,13 @@ gdImagePtr gdImageCreateFromWebpPtr (int size, void *data)
return im;
}
+#define GD_WEBP_ALLOC_STEP (4*1024)
+
gdImagePtr gdImageCreateFromWebpCtx (gdIOCtx * infile)
{
int width, height, ret;
- unsigned char *filedata;
- unsigned char dummy[1024];
+ unsigned char *filedata = NULL;
+ unsigned char *read, *temp;
unsigned char *Y = NULL;
unsigned char *U = NULL;
unsigned char *V = NULL;
@@ -70,16 +72,25 @@ gdImagePtr gdImageCreateFromWebpCtx (gdIOCtx * infile)
gdImagePtr im;
do {
- n = gdGetBuf(dummy, 1024, infile);
- size += n;
- } while (n != EOF);
+ temp = gdRealloc(filedata, size+GD_WEBP_ALLOC_STEP);
+ if (temp) {
+ filedata = temp;
+ read = temp + size;
+ } else {
+ if (filedata) {
+ gdFree(filedata);
+ }
+ php_gd_error("WebP decode: realloc failed");
+ return NULL;
+ }
+
+ n = gdGetBuf(read, GD_WEBP_ALLOC_STEP, infile);
+ /* differs from upstream where gdGetBuf return 0 instead of EOF */
+ if (n>0 && n!=EOF) {
+ size += n;
+ }
+ } while (n>0 && n!=EOF);
- filedata = gdMalloc(size);
- if (!filedata) {
- php_gd_error("WebP decode: alloc failed");
- return NULL;
- }
- gdGetBuf(filedata, size, infile);
ret = WebPDecode(filedata, size, &Y, &U, &V, &width, &height);
gdFree(filedata);
if (ret != webp_success) {