summaryrefslogtreecommitdiff
path: root/plugins/AdvaImg/src/FreeImageToolkit/BSplineRotate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/AdvaImg/src/FreeImageToolkit/BSplineRotate.cpp')
-rw-r--r--plugins/AdvaImg/src/FreeImageToolkit/BSplineRotate.cpp730
1 files changed, 730 insertions, 0 deletions
diff --git a/plugins/AdvaImg/src/FreeImageToolkit/BSplineRotate.cpp b/plugins/AdvaImg/src/FreeImageToolkit/BSplineRotate.cpp
new file mode 100644
index 0000000000..e4be1d0bd2
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImageToolkit/BSplineRotate.cpp
@@ -0,0 +1,730 @@
+// ==========================================================
+// Bitmap rotation using B-Splines
+//
+// Design and implementation by
+// - Philippe Thévenaz (philippe.thevenaz@epfl.ch)
+// Adaptation for FreeImage by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+/*
+==========================================================
+This code was taken and adapted from the following reference :
+
+[1] Philippe Thévenaz, Spline interpolation, a C source code
+implementation. http://bigwww.epfl.ch/thevenaz/
+
+It implements ideas described in the following papers :
+
+[2] Unser M., Splines: A Perfect Fit for Signal and Image Processing.
+IEEE Signal Processing Magazine, vol. 16, no. 6, pp. 22-38, November 1999.
+
+[3] Unser M., Aldroubi A., Eden M., B-Spline Signal Processing: Part I--Theory.
+IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 821-832, February 1993.
+
+[4] Unser M., Aldroubi A., Eden M., B-Spline Signal Processing: Part II--Efficient Design and Applications.
+IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 834-848, February 1993.
+
+==========================================================
+*/
+
+
+#include <float.h>
+#include "FreeImage.h"
+#include "Utilities.h"
+
+#define PI ((double)3.14159265358979323846264338327950288419716939937510)
+
+#define ROTATE_QUADRATIC 2L // Use B-splines of degree 2 (quadratic interpolation)
+#define ROTATE_CUBIC 3L // Use B-splines of degree 3 (cubic interpolation)
+#define ROTATE_QUARTIC 4L // Use B-splines of degree 4 (quartic interpolation)
+#define ROTATE_QUINTIC 5L // Use B-splines of degree 5 (quintic interpolation)
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Prototypes definition
+
+static void ConvertToInterpolationCoefficients(double *c, long DataLength, double *z, long NbPoles, double Tolerance);
+static double InitialCausalCoefficient(double *c, long DataLength, double z, double Tolerance);
+static void GetColumn(double *Image, long Width, long x, double *Line, long Height);
+static void GetRow(double *Image, long y, double *Line, long Width);
+static double InitialAntiCausalCoefficient(double *c, long DataLength, double z);
+static void PutColumn(double *Image, long Width, long x, double *Line, long Height);
+static void PutRow(double *Image, long y, double *Line, long Width);
+static bool SamplesToCoefficients(double *Image, long Width, long Height, long spline_degree);
+static double InterpolatedValue(double *Bcoeff, long Width, long Height, double x, double y, long spline_degree);
+
+static FIBITMAP * Rotate8Bit(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, long spline_degree, BOOL use_mask);
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Coefficients routines
+
+/**
+ ConvertToInterpolationCoefficients
+
+ @param c Input samples --> output coefficients
+ @param DataLength Number of samples or coefficients
+ @param z Poles
+ @param NbPoles Number of poles
+ @param Tolerance Admissible relative error
+*/
+static void
+ConvertToInterpolationCoefficients(double *c, long DataLength, double *z, long NbPoles, double Tolerance) {
+ double Lambda = 1;
+ long n, k;
+
+ // special case required by mirror boundaries
+ if(DataLength == 1L) {
+ return;
+ }
+ // compute the overall gain
+ for(k = 0L; k < NbPoles; k++) {
+ Lambda = Lambda * (1.0 - z[k]) * (1.0 - 1.0 / z[k]);
+ }
+ // apply the gain
+ for (n = 0L; n < DataLength; n++) {
+ c[n] *= Lambda;
+ }
+ // loop over all poles
+ for (k = 0L; k < NbPoles; k++) {
+ // causal initialization
+ c[0] = InitialCausalCoefficient(c, DataLength, z[k], Tolerance);
+ // causal recursion
+ for (n = 1L; n < DataLength; n++) {
+ c[n] += z[k] * c[n - 1L];
+ }
+ // anticausal initialization
+ c[DataLength - 1L] = InitialAntiCausalCoefficient(c, DataLength, z[k]);
+ // anticausal recursion
+ for (n = DataLength - 2L; 0 <= n; n--) {
+ c[n] = z[k] * (c[n + 1L] - c[n]);
+ }
+ }
+}
+
+/**
+ InitialCausalCoefficient
+
+ @param c Coefficients
+ @param DataLength Number of coefficients
+ @param z Actual pole
+ @param Tolerance Admissible relative error
+ @return
+*/
+static double
+InitialCausalCoefficient(double *c, long DataLength, double z, double Tolerance) {
+ double Sum, zn, z2n, iz;
+ long n, Horizon;
+
+ // this initialization corresponds to mirror boundaries
+ Horizon = DataLength;
+ if(Tolerance > 0) {
+ Horizon = (long)ceil(log(Tolerance) / log(fabs(z)));
+ }
+ if(Horizon < DataLength) {
+ // accelerated loop
+ zn = z;
+ Sum = c[0];
+ for (n = 1L; n < Horizon; n++) {
+ Sum += zn * c[n];
+ zn *= z;
+ }
+ return(Sum);
+ }
+ else {
+ // full loop
+ zn = z;
+ iz = 1.0 / z;
+ z2n = pow(z, (double)(DataLength - 1L));
+ Sum = c[0] + z2n * c[DataLength - 1L];
+ z2n *= z2n * iz;
+ for (n = 1L; n <= DataLength - 2L; n++) {
+ Sum += (zn + z2n) * c[n];
+ zn *= z;
+ z2n *= iz;
+ }
+ return(Sum / (1.0 - zn * zn));
+ }
+}
+
+/**
+ GetColumn
+
+ @param Image Input image array
+ @param Width Width of the image
+ @param x x coordinate of the selected line
+ @param Line Output linear array
+ @param Height Length of the line
+*/
+static void
+GetColumn(double *Image, long Width, long x, double *Line, long Height) {
+ long y;
+
+ Image = Image + x;
+ for(y = 0L; y < Height; y++) {
+ Line[y] = (double)*Image;
+ Image += Width;
+ }
+}
+
+/**
+ GetRow
+
+ @param Image Input image array
+ @param y y coordinate of the selected line
+ @param Line Output linear array
+ @param Width Length of the line
+*/
+static void
+GetRow(double *Image, long y, double *Line, long Width) {
+ long x;
+
+ Image = Image + (y * Width);
+ for(x = 0L; x < Width; x++) {
+ Line[x] = (double)*Image++;
+ }
+}
+
+/**
+ InitialAntiCausalCoefficient
+
+ @param c Coefficients
+ @param DataLength Number of samples or coefficients
+ @param z Actual pole
+ @return
+*/
+static double
+InitialAntiCausalCoefficient(double *c, long DataLength, double z) {
+ // this initialization corresponds to mirror boundaries
+ return((z / (z * z - 1.0)) * (z * c[DataLength - 2L] + c[DataLength - 1L]));
+}
+
+/**
+ PutColumn
+
+ @param Image Output image array
+ @param Width Width of the image
+ @param x x coordinate of the selected line
+ @param Line Input linear array
+ @param Height Length of the line and height of the image
+*/
+static void
+PutColumn(double *Image, long Width, long x, double *Line, long Height) {
+ long y;
+
+ Image = Image + x;
+ for(y = 0L; y < Height; y++) {
+ *Image = (double)Line[y];
+ Image += Width;
+ }
+}
+
+/**
+ PutRow
+
+ @param Image Output image array
+ @param y y coordinate of the selected line
+ @param Line Input linear array
+ @param Width length of the line and width of the image
+*/
+static void
+PutRow(double *Image, long y, double *Line, long Width) {
+ long x;
+
+ Image = Image + (y * Width);
+ for(x = 0L; x < Width; x++) {
+ *Image++ = (double)Line[x];
+ }
+}
+
+/**
+ SamplesToCoefficients.<br>
+ Implement the algorithm that converts the image samples into B-spline coefficients.
+ This efficient procedure essentially relies on the three papers cited above;
+ data are processed in-place.
+ Even though this algorithm is robust with respect to quantization,
+ we advocate the use of a floating-point format for the data.
+
+ @param Image Input / Output image (in-place processing)
+ @param Width Width of the image
+ @param Height Height of the image
+ @param spline_degree Degree of the spline model
+ @return Returns true if success, false otherwise
+*/
+static bool
+SamplesToCoefficients(double *Image, long Width, long Height, long spline_degree) {
+ double *Line;
+ double Pole[2];
+ long NbPoles;
+ long x, y;
+
+ // recover the poles from a lookup table
+ switch (spline_degree) {
+ case 2L:
+ NbPoles = 1L;
+ Pole[0] = sqrt(8.0) - 3.0;
+ break;
+ case 3L:
+ NbPoles = 1L;
+ Pole[0] = sqrt(3.0) - 2.0;
+ break;
+ case 4L:
+ NbPoles = 2L;
+ Pole[0] = sqrt(664.0 - sqrt(438976.0)) + sqrt(304.0) - 19.0;
+ Pole[1] = sqrt(664.0 + sqrt(438976.0)) - sqrt(304.0) - 19.0;
+ break;
+ case 5L:
+ NbPoles = 2L;
+ Pole[0] = sqrt(135.0 / 2.0 - sqrt(17745.0 / 4.0)) + sqrt(105.0 / 4.0)
+ - 13.0 / 2.0;
+ Pole[1] = sqrt(135.0 / 2.0 + sqrt(17745.0 / 4.0)) - sqrt(105.0 / 4.0)
+ - 13.0 / 2.0;
+ break;
+ default:
+ // Invalid spline degree
+ return false;
+ }
+
+ // convert the image samples into interpolation coefficients
+
+ // in-place separable process, along x
+ Line = (double *)malloc(Width * sizeof(double));
+ if (Line == NULL) {
+ // Row allocation failed
+ return false;
+ }
+ for (y = 0L; y < Height; y++) {
+ GetRow(Image, y, Line, Width);
+ ConvertToInterpolationCoefficients(Line, Width, Pole, NbPoles, DBL_EPSILON);
+ PutRow(Image, y, Line, Width);
+ }
+ free(Line);
+
+ // in-place separable process, along y
+ Line = (double *)malloc(Height * sizeof(double));
+ if (Line == NULL) {
+ // Column allocation failed
+ return false;
+ }
+ for (x = 0L; x < Width; x++) {
+ GetColumn(Image, Width, x, Line, Height);
+ ConvertToInterpolationCoefficients(Line, Height, Pole, NbPoles, DBL_EPSILON);
+ PutColumn(Image, Width, x, Line, Height);
+ }
+ free(Line);
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interpolation routines
+
+/**
+Perform the bidimensional interpolation of an image.
+Given an array of spline coefficients, return the value of
+the underlying continuous spline model, sampled at the location (x, y).
+The model degree can be 2 (quadratic), 3 (cubic), 4 (quartic), or 5 (quintic).
+
+@param Bcoeff Input B-spline array of coefficients
+@param Width Width of the image
+@param Height Height of the image
+@param x x coordinate where to interpolate
+@param y y coordinate where to interpolate
+@param spline_degree Degree of the spline model
+@return Returns the value of the underlying continuous spline model,
+sampled at the location (x, y)
+*/
+static double
+InterpolatedValue(double *Bcoeff, long Width, long Height, double x, double y, long spline_degree) {
+ double *p;
+ double xWeight[6], yWeight[6];
+ double interpolated;
+ double w, w2, w4, t, t0, t1;
+ long xIndex[6], yIndex[6];
+ long Width2 = 2L * Width - 2L, Height2 = 2L * Height - 2L;
+ long i, j, k;
+
+ // compute the interpolation indexes
+ if (spline_degree & 1L) {
+ i = (long)floor(x) - spline_degree / 2L;
+ j = (long)floor(y) - spline_degree / 2L;
+ for(k = 0; k <= spline_degree; k++) {
+ xIndex[k] = i++;
+ yIndex[k] = j++;
+ }
+ }
+ else {
+ i = (long)floor(x + 0.5) - spline_degree / 2L;
+ j = (long)floor(y + 0.5) - spline_degree / 2L;
+ for (k = 0; k <= spline_degree; k++) {
+ xIndex[k] = i++;
+ yIndex[k] = j++;
+ }
+ }
+
+ // compute the interpolation weights
+ switch (spline_degree) {
+ case 2L:
+ /* x */
+ w = x - (double)xIndex[1];
+ xWeight[1] = 3.0 / 4.0 - w * w;
+ xWeight[2] = (1.0 / 2.0) * (w - xWeight[1] + 1.0);
+ xWeight[0] = 1.0 - xWeight[1] - xWeight[2];
+ /* y */
+ w = y - (double)yIndex[1];
+ yWeight[1] = 3.0 / 4.0 - w * w;
+ yWeight[2] = (1.0 / 2.0) * (w - yWeight[1] + 1.0);
+ yWeight[0] = 1.0 - yWeight[1] - yWeight[2];
+ break;
+ case 3L:
+ /* x */
+ w = x - (double)xIndex[1];
+ xWeight[3] = (1.0 / 6.0) * w * w * w;
+ xWeight[0] = (1.0 / 6.0) + (1.0 / 2.0) * w * (w - 1.0) - xWeight[3];
+ xWeight[2] = w + xWeight[0] - 2.0 * xWeight[3];
+ xWeight[1] = 1.0 - xWeight[0] - xWeight[2] - xWeight[3];
+ /* y */
+ w = y - (double)yIndex[1];
+ yWeight[3] = (1.0 / 6.0) * w * w * w;
+ yWeight[0] = (1.0 / 6.0) + (1.0 / 2.0) * w * (w - 1.0) - yWeight[3];
+ yWeight[2] = w + yWeight[0] - 2.0 * yWeight[3];
+ yWeight[1] = 1.0 - yWeight[0] - yWeight[2] - yWeight[3];
+ break;
+ case 4L:
+ /* x */
+ w = x - (double)xIndex[2];
+ w2 = w * w;
+ t = (1.0 / 6.0) * w2;
+ xWeight[0] = 1.0 / 2.0 - w;
+ xWeight[0] *= xWeight[0];
+ xWeight[0] *= (1.0 / 24.0) * xWeight[0];
+ t0 = w * (t - 11.0 / 24.0);
+ t1 = 19.0 / 96.0 + w2 * (1.0 / 4.0 - t);
+ xWeight[1] = t1 + t0;
+ xWeight[3] = t1 - t0;
+ xWeight[4] = xWeight[0] + t0 + (1.0 / 2.0) * w;
+ xWeight[2] = 1.0 - xWeight[0] - xWeight[1] - xWeight[3] - xWeight[4];
+ /* y */
+ w = y - (double)yIndex[2];
+ w2 = w * w;
+ t = (1.0 / 6.0) * w2;
+ yWeight[0] = 1.0 / 2.0 - w;
+ yWeight[0] *= yWeight[0];
+ yWeight[0] *= (1.0 / 24.0) * yWeight[0];
+ t0 = w * (t - 11.0 / 24.0);
+ t1 = 19.0 / 96.0 + w2 * (1.0 / 4.0 - t);
+ yWeight[1] = t1 + t0;
+ yWeight[3] = t1 - t0;
+ yWeight[4] = yWeight[0] + t0 + (1.0 / 2.0) * w;
+ yWeight[2] = 1.0 - yWeight[0] - yWeight[1] - yWeight[3] - yWeight[4];
+ break;
+ case 5L:
+ /* x */
+ w = x - (double)xIndex[2];
+ w2 = w * w;
+ xWeight[5] = (1.0 / 120.0) * w * w2 * w2;
+ w2 -= w;
+ w4 = w2 * w2;
+ w -= 1.0 / 2.0;
+ t = w2 * (w2 - 3.0);
+ xWeight[0] = (1.0 / 24.0) * (1.0 / 5.0 + w2 + w4) - xWeight[5];
+ t0 = (1.0 / 24.0) * (w2 * (w2 - 5.0) + 46.0 / 5.0);
+ t1 = (-1.0 / 12.0) * w * (t + 4.0);
+ xWeight[2] = t0 + t1;
+ xWeight[3] = t0 - t1;
+ t0 = (1.0 / 16.0) * (9.0 / 5.0 - t);
+ t1 = (1.0 / 24.0) * w * (w4 - w2 - 5.0);
+ xWeight[1] = t0 + t1;
+ xWeight[4] = t0 - t1;
+ /* y */
+ w = y - (double)yIndex[2];
+ w2 = w * w;
+ yWeight[5] = (1.0 / 120.0) * w * w2 * w2;
+ w2 -= w;
+ w4 = w2 * w2;
+ w -= 1.0 / 2.0;
+ t = w2 * (w2 - 3.0);
+ yWeight[0] = (1.0 / 24.0) * (1.0 / 5.0 + w2 + w4) - yWeight[5];
+ t0 = (1.0 / 24.0) * (w2 * (w2 - 5.0) + 46.0 / 5.0);
+ t1 = (-1.0 / 12.0) * w * (t + 4.0);
+ yWeight[2] = t0 + t1;
+ yWeight[3] = t0 - t1;
+ t0 = (1.0 / 16.0) * (9.0 / 5.0 - t);
+ t1 = (1.0 / 24.0) * w * (w4 - w2 - 5.0);
+ yWeight[1] = t0 + t1;
+ yWeight[4] = t0 - t1;
+ break;
+ default:
+ // Invalid spline degree
+ return 0;
+ }
+
+ // apply the mirror boundary conditions
+ for(k = 0; k <= spline_degree; k++) {
+ xIndex[k] = (Width == 1L) ? (0L) : ((xIndex[k] < 0L) ?
+ (-xIndex[k] - Width2 * ((-xIndex[k]) / Width2))
+ : (xIndex[k] - Width2 * (xIndex[k] / Width2)));
+ if (Width <= xIndex[k]) {
+ xIndex[k] = Width2 - xIndex[k];
+ }
+ yIndex[k] = (Height == 1L) ? (0L) : ((yIndex[k] < 0L) ?
+ (-yIndex[k] - Height2 * ((-yIndex[k]) / Height2))
+ : (yIndex[k] - Height2 * (yIndex[k] / Height2)));
+ if (Height <= yIndex[k]) {
+ yIndex[k] = Height2 - yIndex[k];
+ }
+ }
+
+ // perform interpolation
+ interpolated = 0.0;
+ for(j = 0; j <= spline_degree; j++) {
+ p = Bcoeff + (yIndex[j] * Width);
+ w = 0.0;
+ for(i = 0; i <= spline_degree; i++) {
+ w += xWeight[i] * p[xIndex[i]];
+ }
+ interpolated += yWeight[j] * w;
+ }
+
+ return interpolated;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// FreeImage implementation
+
+
+/**
+ Image translation and rotation using B-Splines.
+
+ @param dib Input 8-bit greyscale image
+ @param angle Output image rotation in degree
+ @param x_shift Output image horizontal shift
+ @param y_shift Output image vertical shift
+ @param x_origin Output origin of the x-axis
+ @param y_origin Output origin of the y-axis
+ @param spline_degree Output degree of the B-spline model
+ @param use_mask Whether or not to mask the image
+ @return Returns the translated & rotated dib if successful, returns NULL otherwise
+*/
+static FIBITMAP *
+Rotate8Bit(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, long spline_degree, BOOL use_mask) {
+ double *ImageRasterArray;
+ double p;
+ double a11, a12, a21, a22;
+ double x0, y0, x1, y1;
+ long x, y;
+ long spline;
+ bool bResult;
+
+ int bpp = FreeImage_GetBPP(dib);
+ if(bpp != 8) {
+ return NULL;
+ }
+
+ int width = FreeImage_GetWidth(dib);
+ int height = FreeImage_GetHeight(dib);
+ switch(spline_degree) {
+ case ROTATE_QUADRATIC:
+ spline = 2L; // Use splines of degree 2 (quadratic interpolation)
+ break;
+ case ROTATE_CUBIC:
+ spline = 3L; // Use splines of degree 3 (cubic interpolation)
+ break;
+ case ROTATE_QUARTIC:
+ spline = 4L; // Use splines of degree 4 (quartic interpolation)
+ break;
+ case ROTATE_QUINTIC:
+ spline = 5L; // Use splines of degree 5 (quintic interpolation)
+ break;
+ default:
+ spline = 3L;
+ }
+
+ // allocate output image
+ FIBITMAP *dst = FreeImage_Allocate(width, height, bpp);
+ if (!dst)
+ return NULL;
+ // buid a grey scale palette
+ RGBQUAD *pal = FreeImage_GetPalette(dst);
+ for(int i = 0; i < 256; i++) {
+ pal[i].rgbRed = pal[i].rgbGreen = pal[i].rgbBlue = (BYTE)i;
+ }
+
+ // allocate a temporary array
+ ImageRasterArray = (double*)malloc(width * height * sizeof(double));
+ if (!ImageRasterArray) {
+ FreeImage_Unload(dst);
+ return NULL;
+ }
+ // copy data samples
+ for(y = 0; y < height; y++) {
+ double *pImage = &ImageRasterArray[y*width];
+ BYTE *src_bits = FreeImage_GetScanLine(dib, height-1-y);
+
+ for(x = 0; x < width; x++) {
+ pImage[x] = (double)src_bits[x];
+ }
+ }
+
+ // convert between a representation based on image samples
+ // and a representation based on image B-spline coefficients
+ bResult = SamplesToCoefficients(ImageRasterArray, width, height, spline);
+ if (!bResult) {
+ FreeImage_Unload(dst);
+ free(ImageRasterArray);
+ return NULL;
+ }
+
+ // prepare the geometry
+ angle *= PI / 180.0;
+ a11 = cos(angle);
+ a12 = -sin(angle);
+ a21 = sin(angle);
+ a22 = cos(angle);
+ x0 = a11 * (x_shift + x_origin) + a12 * (y_shift + y_origin);
+ y0 = a21 * (x_shift + x_origin) + a22 * (y_shift + y_origin);
+ x_shift = x_origin - x0;
+ y_shift = y_origin - y0;
+
+ // visit all pixels of the output image and assign their value
+ for(y = 0; y < height; y++) {
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, height-1-y);
+
+ x0 = a12 * (double)y + x_shift;
+ y0 = a22 * (double)y + y_shift;
+
+ for(x = 0; x < width; x++) {
+ x1 = x0 + a11 * (double)x;
+ y1 = y0 + a21 * (double)x;
+ if(use_mask) {
+ if ((x1 <= -0.5) || (((double)width - 0.5) <= x1) || (y1 <= -0.5) || (((double)height - 0.5) <= y1)) {
+ p = 0;
+ }
+ else {
+ p = (double)InterpolatedValue(ImageRasterArray, width, height, x1, y1, spline);
+ }
+ }
+ else {
+ p = (double)InterpolatedValue(ImageRasterArray, width, height, x1, y1, spline);
+ }
+ // clamp and convert to BYTE
+ dst_bits[x] = (BYTE)MIN(MAX((int)0, (int)(p + 0.5)), (int)255);
+ }
+ }
+
+ // free working array and return
+ free(ImageRasterArray);
+
+ return dst;
+}
+
+/**
+ Image rotation using a 3rd order (cubic) B-Splines.
+
+ @param dib Input dib (8, 24 or 32-bit)
+ @param angle Output image rotation
+ @param x_shift Output image horizontal shift
+ @param y_shift Output image vertical shift
+ @param x_origin Output origin of the x-axis
+ @param y_origin Output origin of the y-axis
+ @param use_mask Whether or not to mask the image
+ @return Returns the translated & rotated dib if successful, returns NULL otherwise
+*/
+FIBITMAP * DLL_CALLCONV
+FreeImage_RotateEx(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask) {
+
+ int x, y, bpp;
+ int channel, nb_channels;
+ BYTE *src_bits, *dst_bits;
+ FIBITMAP *src8 = NULL, *dst8 = NULL, *dst = NULL;
+
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ try {
+
+ bpp = FreeImage_GetBPP(dib);
+
+ if(bpp == 8) {
+ FIBITMAP *dst_8 = Rotate8Bit(dib, angle, x_shift, y_shift, x_origin, y_origin, ROTATE_CUBIC, use_mask);
+ if(dst_8) {
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst_8, dib);
+ }
+ return dst_8;
+ }
+ if ((bpp == 24) || (bpp == 32)) {
+ // allocate dst image
+ int width = FreeImage_GetWidth(dib);
+ int height = FreeImage_GetHeight(dib);
+ if ( bpp == 24 ) {
+ dst = FreeImage_Allocate(width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ } else {
+ dst = FreeImage_Allocate(width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ }
+ if (!dst) throw(1);
+
+ // allocate a temporary 8-bit dib (no need to build a palette)
+ src8 = FreeImage_Allocate(width, height, 8);
+ if (!src8) throw(1);
+
+ // process each channel separately
+ // -------------------------------
+ nb_channels = (bpp / 8);
+
+ for(channel = 0; channel < nb_channels; channel++) {
+ // extract channel from source dib
+ for(y = 0; y < height; y++) {
+ src_bits = FreeImage_GetScanLine(dib, y);
+ dst_bits = FreeImage_GetScanLine(src8, y);
+ for(x = 0; x < width; x++) {
+ dst_bits[x] = src_bits[channel];
+ src_bits += nb_channels;
+ }
+ }
+
+ // process channel
+ dst8 = Rotate8Bit(src8, angle, x_shift, y_shift, x_origin, y_origin, ROTATE_CUBIC, use_mask);
+ if (!dst8) throw(1);
+
+ // insert channel to destination dib
+ for(y = 0; y < height; y++) {
+ src_bits = FreeImage_GetScanLine(dst8, y);
+ dst_bits = FreeImage_GetScanLine(dst, y);
+ for(x = 0; x < width; x++) {
+ dst_bits[channel] = src_bits[x];
+ dst_bits += nb_channels;
+ }
+ }
+
+ FreeImage_Unload(dst8);
+ }
+
+ FreeImage_Unload(src8);
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, dib);
+
+ return dst;
+ }
+ } catch(int) {
+ if(src8) FreeImage_Unload(src8);
+ if(dst8) FreeImage_Unload(dst8);
+ if(dst) FreeImage_Unload(dst);
+ }
+
+ return NULL;
+}