summaryrefslogtreecommitdiff
path: root/libs/freeimage/src/FreeImageToolkit
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2018-01-26 17:38:31 +0300
committerGeorge Hazan <ghazan@miranda.im>2018-01-26 17:38:31 +0300
commitdea9c030340e50324eba97c72a27c151bed12e1c (patch)
tree6fc156f40f52a9fc6e6b29e60001959477ee1a5e /libs/freeimage/src/FreeImageToolkit
parentc6e8f8223cab9d799593b7b2cfa22134aa9745d6 (diff)
AdvaImg:
- freeimage extracted to the separate library; - FI_INTERFACE removed, all references to it are replaced with direct calls of FreeImage_* functions; - unified project for AdvaImg
Diffstat (limited to 'libs/freeimage/src/FreeImageToolkit')
-rw-r--r--libs/freeimage/src/FreeImageToolkit/BSplineRotate.cpp727
-rw-r--r--libs/freeimage/src/FreeImageToolkit/Background.cpp894
-rw-r--r--libs/freeimage/src/FreeImageToolkit/Channels.cpp486
-rw-r--r--libs/freeimage/src/FreeImageToolkit/ClassicRotate.cpp916
-rw-r--r--libs/freeimage/src/FreeImageToolkit/Colors.cpp966
-rw-r--r--libs/freeimage/src/FreeImageToolkit/CopyPaste.cpp860
-rw-r--r--libs/freeimage/src/FreeImageToolkit/Display.cpp228
-rw-r--r--libs/freeimage/src/FreeImageToolkit/Filters.h287
-rw-r--r--libs/freeimage/src/FreeImageToolkit/Flip.cpp165
-rw-r--r--libs/freeimage/src/FreeImageToolkit/JPEGTransform.cpp622
-rw-r--r--libs/freeimage/src/FreeImageToolkit/MultigridPoissonSolver.cpp503
-rw-r--r--libs/freeimage/src/FreeImageToolkit/Rescale.cpp193
-rw-r--r--libs/freeimage/src/FreeImageToolkit/Resize.cpp2117
-rw-r--r--libs/freeimage/src/FreeImageToolkit/Resize.h196
14 files changed, 9160 insertions, 0 deletions
diff --git a/libs/freeimage/src/FreeImageToolkit/BSplineRotate.cpp b/libs/freeimage/src/FreeImageToolkit/BSplineRotate.cpp
new file mode 100644
index 0000000000..699d0ca99e
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/BSplineRotate.cpp
@@ -0,0 +1,727 @@
+// ==========================================================
+// 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 "../stdafx.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;
+}
diff --git a/libs/freeimage/src/FreeImageToolkit/Background.cpp b/libs/freeimage/src/FreeImageToolkit/Background.cpp
new file mode 100644
index 0000000000..8706cba729
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/Background.cpp
@@ -0,0 +1,894 @@
+// ==========================================================
+// Background filling routines
+//
+// Design and implementation by
+// - Carsten Klein (c.klein@datagis.com)
+//
+// 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!
+// ==========================================================
+
+#include "../stdafx.h"
+
+/** @brief Determines, whether a palletized image is visually greyscale or not.
+
+ Unlike with FreeImage_GetColorType, which returns either FIC_MINISBLACK or
+ FIC_MINISWHITE for a greyscale image with a linear ramp palette, the return
+ value of this function does not depend on the palette's order, but only on the
+ palette's individual colors.
+ @param dib The image to be tested.
+ @return Returns TRUE if the palette of the image specified contains only
+ greyscales, FALSE otherwise.
+ */
+static BOOL
+IsVisualGreyscaleImage(FIBITMAP *dib) {
+
+ switch (FreeImage_GetBPP(dib)) {
+ case 1:
+ case 4:
+ case 8: {
+ unsigned ncolors = FreeImage_GetColorsUsed(dib);
+ RGBQUAD *rgb = FreeImage_GetPalette(dib);
+ for (unsigned i = 0; i< ncolors; i++) {
+ if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ default: {
+ return (FreeImage_GetColorType(dib) == FIC_MINISBLACK);
+ }
+ }
+}
+
+/** @brief Looks up a specified color in a FIBITMAP's palette and returns the color's
+ palette index or -1 if the color was not found.
+
+ Unlike with FreeImage_GetColorType, which returns either FIC_MINISBLACK or
+ FIC_MINISWHITE for a greyscale image with a linear ramp palette, the return
+ value of this function does not depend on the palette's order, but only on the
+ palette's individual colors.
+ @param dib The image, whose palette should be searched through.
+ @param color The color to be searched in the palette.
+ @param options Options that affect the color search process.
+ @param color_type A pointer, that optionally specifies the image's color type as
+ returned by FreeImage_GetColorType. If invalid or NULL, this function determines the
+ color type with FreeImage_GetColorType.
+ @return Returns the specified color's palette index, the color's rgbReserved member
+ if option FI_COLOR_ALPHA_IS_INDEX was specified or -1, if the color was not found
+ in the image's palette or if the specified image is non-palletized.
+ */
+static int
+GetPaletteIndex(FIBITMAP *dib, const RGBQUAD *color, int options, FREE_IMAGE_COLOR_TYPE *color_type) {
+
+ int result = -1;
+
+ if ((!dib) || (!color)) {
+ return result;
+ }
+
+ int bpp = FreeImage_GetBPP(dib);
+
+ // First check trivial case: return color->rgbReserved if only
+ // FI_COLOR_ALPHA_IS_INDEX is set.
+ if ((options & FI_COLOR_ALPHA_IS_INDEX) == FI_COLOR_ALPHA_IS_INDEX) {
+ if (bpp == 1) {
+ return color->rgbReserved & 0x01;
+ } else if (bpp == 4) {
+ return color->rgbReserved & 0x0F;
+ }
+ return color->rgbReserved;
+ }
+
+ if (bpp == 8) {
+ FREE_IMAGE_COLOR_TYPE ct =
+ (color_type == NULL || *color_type < 0) ?
+ FreeImage_GetColorType(dib) : *color_type;
+ if (ct == FIC_MINISBLACK) {
+ return GREY(color->rgbRed, color->rgbGreen, color->rgbBlue);
+ }
+ if (ct == FIC_MINISWHITE) {
+ return 255 - GREY(color->rgbRed, color->rgbGreen, color->rgbBlue);
+ }
+ } else if (bpp > 8) {
+ // for palettized images only
+ return result;
+ }
+
+ if (options & FI_COLOR_FIND_EQUAL_COLOR) {
+
+ // Option FI_COLOR_ALPHA_IS_INDEX is implicit here so, set
+ // index to color->rgbReserved
+ result = color->rgbReserved;
+ if (bpp == 1) {
+ result &= 0x01;
+ } else if (bpp == 4) {
+ result &= 0x0F;
+ }
+
+ unsigned ucolor;
+ if (!IsVisualGreyscaleImage(dib)) {
+ ucolor = (*((unsigned *)color)) & 0xFFFFFF;
+ } else {
+ ucolor = GREY(color->rgbRed, color->rgbGreen, color->rgbBlue) * 0x010101;
+ //ucolor = (ucolor | (ucolor << 8) | (ucolor << 16));
+ }
+ unsigned ncolors = FreeImage_GetColorsUsed(dib);
+ unsigned *palette = (unsigned *)FreeImage_GetPalette(dib);
+ for (unsigned i = 0; i < ncolors; i++) {
+ if ((palette[i] & 0xFFFFFF) == ucolor) {
+ result = i;
+ break;
+ }
+ }
+ } else {
+ unsigned minimum = UINT_MAX;
+ unsigned ncolors = FreeImage_GetColorsUsed(dib);
+ BYTE *palette = (BYTE *)FreeImage_GetPalette(dib);
+ BYTE red, green, blue;
+ if (!IsVisualGreyscaleImage(dib)) {
+ red = color->rgbRed;
+ green = color->rgbGreen;
+ blue = color->rgbBlue;
+ } else {
+ red = GREY(color->rgbRed, color->rgbGreen, color->rgbBlue);
+ green = blue = red;
+ }
+ for (unsigned i = 0; i < ncolors; i++) {
+ unsigned m = abs(palette[FI_RGBA_BLUE] - blue)
+ + abs(palette[FI_RGBA_GREEN] - green)
+ + abs(palette[FI_RGBA_RED] - red);
+ if (m < minimum) {
+ minimum = m;
+ result = i;
+ if (m == 0) {
+ break;
+ }
+ }
+ palette += sizeof(RGBQUAD);
+ }
+ }
+ return result;
+}
+
+/** @brief Blends an alpha-transparent foreground color over an opaque background
+ color.
+
+ This function blends the alpha-transparent foreground color fgcolor over the
+ background color bgcolor. The background color is considered fully opaque,
+ whatever it's alpha value contains, whereas the foreground color is considered
+ to be a real RGBA color with an alpha value, which is used for the blend
+ operation. The resulting color is returned through the blended parameter.
+ @param bgcolor The background color for the blend operation.
+ @param fgcolor The foreground color for the blend operation. This color's alpha
+ value, stored in the rgbReserved member, is the alpha value used for the blend
+ operation.
+ @param blended This out parameter takes the blended color and so, returns it to
+ the caller. This color's alpha value will be 0xFF (255) so, the blended color
+ itself has no transparency. The this argument is not changed, if the function
+ fails.
+ @return Returns TRUE on success, FALSE otherwise. This function fails if any of
+ the color arguments is a null pointer.
+ */
+static BOOL
+GetAlphaBlendedColor(const RGBQUAD *bgcolor, const RGBQUAD *fgcolor, RGBQUAD *blended) {
+
+ if ((!bgcolor) || (!fgcolor) || (!blended)) {
+ return FALSE;
+ }
+
+ BYTE alpha = fgcolor->rgbReserved;
+ BYTE not_alpha = ~alpha;
+
+ blended->rgbRed = (BYTE)( ((WORD)fgcolor->rgbRed * alpha + not_alpha * (WORD)bgcolor->rgbRed) >> 8 );
+ blended->rgbGreen = (BYTE)( ((WORD)fgcolor->rgbGreen * alpha + not_alpha * (WORD)bgcolor->rgbGreen) >> 8) ;
+ blended->rgbBlue = (BYTE)( ((WORD)fgcolor->rgbBlue * alpha + not_alpha * (WORD)bgcolor->rgbBlue) >> 8 );
+ blended->rgbReserved = 0xFF;
+
+ return TRUE;
+}
+
+/** @brief Fills a FIT_BITMAP image with the specified color.
+
+ This function does the dirty work for FreeImage_FillBackground for FIT_BITMAP
+ images.
+ @param dib The image to be filled.
+ @param color The color, the specified image should be filled with.
+ @param options Options that affect the color search process for palletized images.
+ @return Returns TRUE on success, FALSE otherwise. This function fails if any of
+ the dib and color is NULL or the provided image is not a FIT_BITMAP image.
+ */
+static BOOL
+FillBackgroundBitmap(FIBITMAP *dib, const RGBQUAD *color, int options) {
+
+ if ((!dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+ return FALSE;;
+ }
+
+ if (!color) {
+ return FALSE;
+ }
+
+ const RGBQUAD *color_intl = color;
+ unsigned bpp = FreeImage_GetBPP(dib);
+ unsigned width = FreeImage_GetWidth(dib);
+ unsigned height = FreeImage_GetHeight(dib);
+
+ FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+
+ // get a pointer to the first scanline (bottom line)
+ BYTE *src_bits = FreeImage_GetScanLine(dib, 0);
+ BYTE *dst_bits = src_bits;
+
+ BOOL supports_alpha = ((bpp >= 24) || ((bpp == 8) && (color_type != FIC_PALETTE)));
+
+ // Check for RGBA case if bitmap supports alpha
+ // blending (8-bit greyscale, 24- or 32-bit images)
+ if (supports_alpha && (options & FI_COLOR_IS_RGBA_COLOR)) {
+
+ if (color->rgbReserved == 0) {
+ // the fill color is fully transparent; we are done
+ return TRUE;
+ }
+
+ // Only if the fill color is NOT fully opaque, draw it with
+ // the (much) slower FreeImage_DrawLine function and return.
+ // Since we do not have the FreeImage_DrawLine function in this
+ // release, just assume to have an unicolor background and fill
+ // all with an 'alpha-blended' color.
+ if (color->rgbReserved < 255) {
+
+ // If we will draw on an unicolor background, it's
+ // faster to draw opaque with an alpha blended color.
+ // So, first get the color from the first pixel in the
+ // image (bottom-left pixel).
+ RGBQUAD bgcolor;
+ if (bpp == 8) {
+ bgcolor = FreeImage_GetPalette(dib)[*src_bits];
+ } else {
+ bgcolor.rgbBlue = src_bits[FI_RGBA_BLUE];
+ bgcolor.rgbGreen = src_bits[FI_RGBA_GREEN];
+ bgcolor.rgbRed = src_bits[FI_RGBA_RED];
+ bgcolor.rgbReserved = 0xFF;
+ }
+ RGBQUAD blend;
+ GetAlphaBlendedColor(&bgcolor, color_intl, &blend);
+ color_intl = &blend;
+ }
+ }
+
+ int index = (bpp <= 8) ? GetPaletteIndex(dib, color_intl, options, &color_type) : 0;
+ if (index == -1) {
+ // No palette index found for a palletized
+ // image. This should never happen...
+ return FALSE;
+ }
+
+ // first, build the first scanline (line 0)
+ switch (bpp) {
+ case 1: {
+ unsigned bytes = (width / 8);
+ memset(dst_bits, ((index == 1) ? 0xFF : 0x00), bytes);
+ //int n = width % 8;
+ int n = width & 7;
+ if (n) {
+ if (index == 1) {
+ // set n leftmost bits
+ dst_bits[bytes] |= (0xFF << (8 - n));
+ } else {
+ // clear n leftmost bits
+ dst_bits[bytes] &= (0xFF >> n);
+ }
+ }
+ break;
+ }
+ case 4: {
+ unsigned bytes = (width / 2);
+ memset(dst_bits, (index | (index << 4)), bytes);
+ //if (bytes % 2) {
+ if (bytes & 1) {
+ dst_bits[bytes] &= 0x0F;
+ dst_bits[bytes] |= (index << 4);
+ }
+ break;
+ }
+ case 8: {
+ memset(dst_bits, index, FreeImage_GetLine(dib));
+ break;
+ }
+ case 16: {
+ WORD wcolor = RGBQUAD_TO_WORD(dib, color_intl);
+ for (unsigned x = 0; x < width; x++) {
+ ((WORD *)dst_bits)[x] = wcolor;
+ }
+ break;
+ }
+ case 24: {
+ RGBTRIPLE rgbt = *((RGBTRIPLE *)color_intl);
+ for (unsigned x = 0; x < width; x++) {
+ ((RGBTRIPLE *)dst_bits)[x] = rgbt;
+ }
+ break;
+ }
+ case 32: {
+ RGBQUAD rgbq;
+ rgbq.rgbBlue = ((RGBTRIPLE *)color_intl)->rgbtBlue;
+ rgbq.rgbGreen = ((RGBTRIPLE *)color_intl)->rgbtGreen;
+ rgbq.rgbRed = ((RGBTRIPLE *)color_intl)->rgbtRed;
+ rgbq.rgbReserved = 0xFF;
+ for (unsigned x = 0; x < width; x++) {
+ ((RGBQUAD *)dst_bits)[x] = rgbq;
+ }
+ break;
+ }
+ default:
+ return FALSE;
+ }
+
+ // Then, copy the first scanline into all following scanlines.
+ // 'src_bits' is a pointer to the first scanline and is already
+ // set up correctly.
+ if (src_bits) {
+ unsigned pitch = FreeImage_GetPitch(dib);
+ unsigned bytes = FreeImage_GetLine(dib);
+ dst_bits = src_bits + pitch;
+ for (unsigned y = 1; y < height; y++) {
+ memcpy(dst_bits, src_bits, bytes);
+ dst_bits += pitch;
+ }
+ }
+ return TRUE;
+}
+
+/** @brief Fills an image with the specified color.
+
+ This function sets all pixels of an image to the color provided through the color
+ parameter. Since this should work for all image types supported by FreeImage, the
+ pointer color must point to a memory location, which is at least as large as the
+ image's color value, if this size is greater than 4 bytes. As the color is specified
+ by an RGBQUAD structure for all images of type FIT_BITMAP (including all palletized
+ images), the smallest possible size of this memory is the size of the RGBQUAD structure,
+ which uses 4 bytes.
+
+ So, color must point to a double, if the image to be filled is of type FIT_DOUBLE and
+ point to a RGBF structure if the image is of type FIT_RGBF and so on.
+
+ However, the fill color is always specified through a RGBQUAD structure for all images
+ of type FIT_BITMAP. So, for 32- and 24-bit images, the red, green and blue members of
+ the RGBQUAD structure are directly used for the image's red, green and blue channel
+ respectively. Although alpha transparent RGBQUAD colors are supported, the alpha channel
+ of a 32-bit image never gets modified by this function. A fill color with an alpha value
+ smaller than 255 gets blended with the image's actual background color, which is determined
+ from the image's bottom-left pixel. So, currently using alpha enabled colors, assumes the
+ image to be unicolor before the fill operation. However, the RGBQUAD's rgbReserved member is
+ only taken into account, if option FI_COLOR_IS_RGBA_COLOR has been specified.
+
+ For 16-bit images, the red-, green- and blue components of the specified color are
+ transparently translated into either the 16-bit 555 or 565 representation. This depends
+ on the image's actual red- green- and blue masks.
+
+ Special attention must be payed for palletized images. Generally, the RGB color specified
+ is looked up in the image's palette. The found palette index is then used to fill the image.
+ There are some option flags, that affect this lookup process:
+
+ no option specified (0x00) Uses the color, that is nearest to the specified color.
+ This is the default behavior and should always find a
+ color in the palette. However, the visual result may
+ far from what was expected and mainly depends on the
+ image's palette.
+
+ FI_COLOR_FIND_EQUAL_COLOR (0x02) Searches the image's palette for the specified color
+ but only uses the returned palette index, if the specified
+ color exactly matches the palette entry. Of course,
+ depending on the image's actual palette entries, this
+ operation may fail. In this case, the function falls back
+ to option FI_COLOR_ALPHA_IS_INDEX and uses the RGBQUAD's
+ rgbReserved member (or its low nibble for 4-bit images
+ or its least significant bit (LSB) for 1-bit images) as
+ the palette index used for the fill operation.
+
+ FI_COLOR_ALPHA_IS_INDEX (0x04) Does not perform any color lookup from the palette, but
+ uses the RGBQUAD's alpha channel member rgbReserved as
+ the palette index to be used for the fill operation.
+ However, for 4-bit images, only the low nibble of the
+ rgbReserved member are used and for 1-bit images, only
+ the least significant bit (LSB) is used.
+
+ This function fails if any of dib and color is NULL.
+
+ @param dib The image to be filled.
+ @param color A pointer to the color value to be used for filling the image. The
+ memory pointed to by this pointer is always assumed to be at least as large as the
+ image's color value, but never smaller than the size of an RGBQUAD structure.
+ @param options Options that affect the color search process for palletized images.
+ @return Returns TRUE on success, FALSE otherwise. This function fails if any of
+ dib and color is NULL.
+ */
+BOOL DLL_CALLCONV
+FreeImage_FillBackground(FIBITMAP *dib, const void *color, int options) {
+
+ if (!FreeImage_HasPixels(dib)) {
+ return FALSE;
+ }
+
+ if (!color) {
+ return FALSE;
+ }
+
+ // handle FIT_BITMAP images with FreeImage_FillBackground()
+ if (FreeImage_GetImageType(dib) == FIT_BITMAP) {
+ return FillBackgroundBitmap(dib, (RGBQUAD *)color, options);
+ }
+
+ // first, construct the first scanline (bottom line)
+ unsigned bytespp = (FreeImage_GetBPP(dib) / 8);
+ BYTE *src_bits = FreeImage_GetScanLine(dib, 0);
+ BYTE *dst_bits = src_bits;
+ for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ memcpy(dst_bits, color, bytespp);
+ dst_bits += bytespp;
+ }
+
+ // then, copy the first scanline into all following scanlines
+ unsigned height = FreeImage_GetHeight(dib);
+ unsigned pitch = FreeImage_GetPitch(dib);
+ unsigned bytes = FreeImage_GetLine(dib);
+ dst_bits = src_bits + pitch;
+ for (unsigned y = 1; y < height; y++) {
+ memcpy(dst_bits, src_bits, bytes);
+ dst_bits += pitch;
+ }
+ return TRUE;
+}
+
+/** @brief Allocates a new image of the specified type, width, height and bit depth and
+ optionally fills it with the specified color.
+
+ This function is an extension to FreeImage_AllocateT, which additionally supports specifying
+ a palette to be set for the newly create image, as well as specifying a background color,
+ the newly created image should initially be filled with.
+
+ Basically, this function internally relies on function FreeImage_AllocateT, followed by a
+ call to FreeImage_FillBackground. This is why both parameters color and options behave the
+ same as it is documented for function FreeImage_FillBackground. So, please refer to the
+ documentation of FreeImage_FillBackground to learn more about parameters color and options.
+
+ The palette specified through parameter palette is only copied to the newly created
+ image, if its image type is FIT_BITMAP and the desired bit depth is smaller than or equal
+ to 8 bits per pixel. In other words, the palette parameter is only taken into account for
+ palletized images. However, if the preceding conditions match and if palette is not NULL,
+ the memory pointed to by the palette pointer is assumed to be at least as large as size
+ of a fully populated palette for the desired bit depth. So, for an 8-bit image, this size
+ is 256 x sizeof(RGBQUAD), for an 4-bit image it is 16 x sizeof(RGBQUAD) and it is
+ 2 x sizeof(RGBQUAD) for a 1-bit image. In other words, this function does not support
+ partial palettes.
+
+ However, specifying a palette is not necessarily needed, even for palletized images. This
+ function is capable of implicitly creating a palette, if parameter palette is NULL. If the
+ specified background color is a greyscale value (red = green = blue) or if option
+ FI_COLOR_ALPHA_IS_INDEX is specified, a greyscale palette is created. For a 1-bit image, only
+ if the specified background color is either black or white, a monochrome palette, consisting
+ of black and white only is created. In any case, the darker colors are stored at the smaller
+ palette indices.
+
+ If the specified background color is not a greyscale value, or is neither black nor white
+ for a 1-bit image, solely this single color is injected into the otherwise black-initialized
+ palette. For this operation, option FI_COLOR_ALPHA_IS_INDEX is implicit, so the specified
+ color is applied to the palette entry, specified by the background color's rgbReserved
+ member. The image is then filled with this palette index.
+
+ This function returns a newly created image as function FreeImage_AllocateT does, if both
+ parameters color and palette are NULL. If only color is NULL, the palette pointed to by
+ parameter palette is initially set for the new image, if a palletized image of type
+ FIT_BITMAP is created. However, in the latter case, this function returns an image, whose
+ pixels are all initialized with zeros so, the image will be filled with the color of the
+ first palette entry.
+
+ @param type Specifies the image type of the new image.
+ @param width The desired width in pixels of the new image.
+ @param height The desired height in pixels of the new image.
+ @param bpp The desired bit depth of the new image.
+ @param color A pointer to the color value to be used for filling the image. The
+ memory pointed to by this pointer is always assumed to be at least as large as the
+ image's color value but never smaller than the size of an RGBQUAD structure.
+ @param options Options that affect the color search process for palletized images.
+ @param red_mask Specifies the bits used to store the red components of a pixel.
+ @param green_mask Specifies the bits used to store the green components of a pixel.
+ @param blue_mask Specifies the bits used to store the blue components of a pixel.
+ @return Returns a pointer to a newly allocated image on success, NULL otherwise.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateExT(FREE_IMAGE_TYPE type, int width, int height, int bpp, const void *color, int options, const RGBQUAD *palette, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+
+ FIBITMAP *bitmap = FreeImage_AllocateT(type, width, height, bpp, red_mask, green_mask, blue_mask);
+
+ if (!color) {
+ if ((palette) && (type == FIT_BITMAP) && (bpp <= 8)) {
+ memcpy(FreeImage_GetPalette(bitmap), palette, FreeImage_GetColorsUsed(bitmap) * sizeof(RGBQUAD));
+ }
+ return bitmap;
+ }
+
+ if (bitmap != NULL) {
+
+ // Only fill the new bitmap if the specified color
+ // differs from "black", that is not all bytes of the
+ // color are equal to zero.
+ switch (bpp) {
+ case 1: {
+ // although 1-bit implies FIT_BITMAP, better get an unsigned
+ // color and palette
+ unsigned *urgb = (unsigned *)color;
+ unsigned *upal = (unsigned *)FreeImage_GetPalette(bitmap);
+ RGBQUAD rgbq = RGBQUAD();
+
+ if (palette != NULL) {
+ // clone the specified palette
+ memcpy(FreeImage_GetPalette(bitmap), palette, 2 * sizeof(RGBQUAD));
+ } else if (options & FI_COLOR_ALPHA_IS_INDEX) {
+ CREATE_GREYSCALE_PALETTE(upal, 2);
+ } else {
+ // check, whether the specified color is either black or white
+ if ((*urgb & 0xFFFFFF) == 0x000000) {
+ // in any case build a FIC_MINISBLACK palette
+ CREATE_GREYSCALE_PALETTE(upal, 2);
+ color = &rgbq;
+ } else if ((*urgb & 0xFFFFFF) == 0xFFFFFF) {
+ // in any case build a FIC_MINISBLACK palette
+ CREATE_GREYSCALE_PALETTE(upal, 2);
+ rgbq.rgbReserved = 1;
+ color = &rgbq;
+ } else {
+ // Otherwise inject the specified color into the so far
+ // black-only palette. We use color->rgbReserved as the
+ // desired palette index.
+ BYTE index = ((RGBQUAD *)color)->rgbReserved & 0x01;
+ upal[index] = *urgb & 0x00FFFFFF;
+ }
+ options |= FI_COLOR_ALPHA_IS_INDEX;
+ }
+ // and defer to FreeImage_FillBackground
+ FreeImage_FillBackground(bitmap, color, options);
+ break;
+ }
+ case 4: {
+ // 4-bit implies FIT_BITMAP so, get a RGBQUAD color
+ RGBQUAD *rgb = (RGBQUAD *)color;
+ RGBQUAD *pal = FreeImage_GetPalette(bitmap);
+ RGBQUAD rgbq = RGBQUAD();
+
+ if (palette != NULL) {
+ // clone the specified palette
+ memcpy(pal, palette, 16 * sizeof(RGBQUAD));
+ } else if (options & FI_COLOR_ALPHA_IS_INDEX) {
+ CREATE_GREYSCALE_PALETTE(pal, 16);
+ } else {
+ // check, whether the specified color is a grey one
+ if ((rgb->rgbRed == rgb->rgbGreen) && (rgb->rgbRed == rgb->rgbBlue)) {
+ // if so, build a greyscale palette
+ CREATE_GREYSCALE_PALETTE(pal, 16);
+ rgbq.rgbReserved = rgb->rgbRed >> 4;
+ color = &rgbq;
+ } else {
+ // Otherwise inject the specified color into the so far
+ // black-only palette. We use color->rgbReserved as the
+ // desired palette index.
+ BYTE index = (rgb->rgbReserved & 0x0F);
+ ((unsigned *)pal)[index] = *((unsigned *)rgb) & 0x00FFFFFF;
+ }
+ options |= FI_COLOR_ALPHA_IS_INDEX;
+ }
+ // and defer to FreeImage_FillBackground
+ FreeImage_FillBackground(bitmap, color, options);
+ break;
+ }
+ case 8: {
+ // 8-bit implies FIT_BITMAP so, get a RGBQUAD color
+ RGBQUAD *rgb = (RGBQUAD *)color;
+ RGBQUAD *pal = FreeImage_GetPalette(bitmap);
+ RGBQUAD rgbq;
+
+ if (palette != NULL) {
+ // clone the specified palette
+ memcpy(pal, palette, 256 * sizeof(RGBQUAD));
+ } else if (options & FI_COLOR_ALPHA_IS_INDEX) {
+ CREATE_GREYSCALE_PALETTE(pal, 256);
+ } else {
+ // check, whether the specified color is a grey one
+ if ((rgb->rgbRed == rgb->rgbGreen) && (rgb->rgbRed == rgb->rgbBlue)) {
+ // if so, build a greyscale palette
+ CREATE_GREYSCALE_PALETTE(pal, 256);
+ rgbq.rgbReserved = rgb->rgbRed;
+ color = &rgbq;
+ } else {
+ // Otherwise inject the specified color into the so far
+ // black-only palette. We use color->rgbReserved as the
+ // desired palette index.
+ BYTE index = rgb->rgbReserved;
+ ((unsigned *)pal)[index] = *((unsigned *)rgb) & 0x00FFFFFF;
+ }
+ options |= FI_COLOR_ALPHA_IS_INDEX;
+ }
+ // and defer to FreeImage_FillBackground
+ FreeImage_FillBackground(bitmap, color, options);
+ break;
+ }
+ case 16: {
+ WORD wcolor = (type == FIT_BITMAP) ?
+ RGBQUAD_TO_WORD(bitmap, ((RGBQUAD *)color)) : *((WORD *)color);
+ if (wcolor != 0) {
+ FreeImage_FillBackground(bitmap, color, options);
+ }
+ break;
+ }
+ default: {
+ int bytespp = bpp / 8;
+ for (int i = 0; i < bytespp; i++) {
+ if (((BYTE *)color)[i] != 0) {
+ FreeImage_FillBackground(bitmap, color, options);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return bitmap;
+}
+
+/** @brief Allocates a new image of the specified width, height and bit depth and optionally
+ fills it with the specified color.
+
+ This function is an extension to FreeImage_Allocate, which additionally supports specifying
+ a palette to be set for the newly create image, as well as specifying a background color,
+ the newly created image should initially be filled with.
+
+ Basically, this function internally relies on function FreeImage_Allocate, followed by a
+ call to FreeImage_FillBackground. This is why both parameters color and options behave the
+ same as it is documented for function FreeImage_FillBackground. So, please refer to the
+ documentation of FreeImage_FillBackground to learn more about parameters color and options.
+
+ The palette specified through parameter palette is only copied to the newly created
+ image, if the desired bit depth is smaller than or equal to 8 bits per pixel. In other words,
+ the palette parameter is only taken into account for palletized images. However, if the
+ image to be created is a palletized image and if palette is not NULL, the memory pointed to
+ by the palette pointer is assumed to be at least as large as size of a fully populated
+ palette for the desired bit depth. So, for an 8-bit image, this size is 256 x sizeof(RGBQUAD),
+ for an 4-bit image it is 16 x sizeof(RGBQUAD) and it is 2 x sizeof(RGBQUAD) for a 1-bit
+ image. In other words, this function does not support partial palettes.
+
+ However, specifying a palette is not necessarily needed, even for palletized images. This
+ function is capable of implicitly creating a palette, if parameter palette is NULL. If the
+ specified background color is a greyscale value (red = green = blue) or if option
+ FI_COLOR_ALPHA_IS_INDEX is specified, a greyscale palette is created. For a 1-bit image, only
+ if the specified background color is either black or white, a monochrome palette, consisting
+ of black and white only is created. In any case, the darker colors are stored at the smaller
+ palette indices.
+
+ If the specified background color is not a greyscale value, or is neither black nor white
+ for a 1-bit image, solely this single color is injected into the otherwise black-initialized
+ palette. For this operation, option FI_COLOR_ALPHA_IS_INDEX is implicit, so the specified
+ color is applied to the palette entry, specified by the background color's rgbReserved
+ member. The image is then filled with this palette index.
+
+ This function returns a newly created image as function FreeImage_Allocate does, if both
+ parameters color and palette are NULL. If only color is NULL, the palette pointed to by
+ parameter palette is initially set for the new image, if a palletized image of type
+ FIT_BITMAP is created. However, in the latter case, this function returns an image, whose
+ pixels are all initialized with zeros so, the image will be filled with the color of the
+ first palette entry.
+
+ @param width The desired width in pixels of the new image.
+ @param height The desired height in pixels of the new image.
+ @param bpp The desired bit depth of the new image.
+ @param color A pointer to an RGBQUAD structure, that provides the color to be used for
+ filling the image.
+ @param options Options that affect the color search process for palletized images.
+ @param red_mask Specifies the bits used to store the red components of a pixel.
+ @param green_mask Specifies the bits used to store the green components of a pixel.
+ @param blue_mask Specifies the bits used to store the blue components of a pixel.
+ @return Returns a pointer to a newly allocated image on success, NULL otherwise.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateEx(int width, int height, int bpp, const RGBQUAD *color, int options, const RGBQUAD *palette, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+ return FreeImage_AllocateExT(FIT_BITMAP, width, height, bpp, ((void *)color), options, palette, red_mask, green_mask, blue_mask);
+}
+
+/** @brief Enlarges or shrinks an image selectively per side and fills newly added areas
+ with the specified background color.
+
+ This function enlarges or shrinks an image selectively per side. The main purpose of this
+ function is to add borders to an image. To add a border to any of the image's sides, a
+ positive integer value must be passed in any of the parameters left, top, right or bottom.
+ This value represents the border's width in pixels. Newly created parts of the image (the
+ border areas) are filled with the specified color. Specifying a negative integer value for
+ a certain side, will shrink or crop the image on this side. Consequently, specifying zero
+ for a certain side will not change the image's extension on that side.
+
+ So, calling this function with all parameters left, top, right and bottom set to zero, is
+ effectively the same as calling function FreeImage_Clone; setting all parameters left, top,
+ right and bottom to value equal to or smaller than zero, my easily be substituted by a call
+ to function FreeImage_Copy. Both these cases produce a new image, which is guaranteed not to
+ be larger than the input image. Thus, since the specified color is not needed in these cases,
+ the pointer color may be NULL.
+
+ Both parameters color and options work according to function FreeImage_FillBackground. So,
+ please refer to the documentation of FreeImage_FillBackground to learn more about parameters
+ color and options. For palletized images, the palette of the input image src is
+ transparently copied to the newly created enlarged or shrunken image, so any color
+ look-ups are performed on this palette.
+
+ Here are some examples, that illustrate, how to use the parameters left, top, right and
+ bottom:
+
+ // create a white color
+ RGBQUAD c;
+ c.rgbRed = 0xFF;
+ c.rgbGreen = 0xFF;
+ c.rgbBlue = 0xFF;
+ c.rgbReserved = 0x00;
+
+ // add a white, symmetric 10 pixel wide border to the image
+ dib2 = FreeImage_EnlargeCanvas(dib, 10, 10, 10, 10, &c, FI_COLOR_IS_RGB_COLOR);
+
+ // add white, 20 pixel wide stripes to the top and bottom side of the image
+ dib3 = FreeImage_EnlargeCanvas(dib, 0, 20, 0, 20, &c, FI_COLOR_IS_RGB_COLOR);
+
+ // add white, 30 pixel wide stripes to the right side of the image and
+ // cut off the 40 leftmost pixel columns
+ dib3 = FreeImage_EnlargeCanvas(dib, -40, 0, 30, 0, &c, FI_COLOR_IS_RGB_COLOR);
+
+ This function fails if either the input image is NULL or the pointer to the color is
+ NULL, while at least on of left, top, right and bottom is greater than zero. This
+ function also returns NULL, if the new image's size will be negative in either x- or
+ y-direction.
+
+ @param dib The image to be enlarged or shrunken.
+ @param left The number of pixels, the image should be enlarged on its left side. Negative
+ values shrink the image on its left side.
+ @param top The number of pixels, the image should be enlarged on its top side. Negative
+ values shrink the image on its top side.
+ @param right The number of pixels, the image should be enlarged on its right side. Negative
+ values shrink the image on its right side.
+ @param bottom The number of pixels, the image should be enlarged on its bottom side. Negative
+ values shrink the image on its bottom side.
+ @param color The color, the enlarged sides of the image should be filled with.
+ @param options Options that affect the color search process for palletized images.
+ @return Returns a pointer to a newly allocated enlarged or shrunken image on success,
+ NULL otherwise. This function fails if either the input image is NULL or the pointer to the
+ color is NULL, while at least on of left, top, right and bottom is greater than zero. This
+ function also returns NULL, if the new image's size will be negative in either x- or
+ y-direction.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_EnlargeCanvas(FIBITMAP *src, int left, int top, int right, int bottom, const void *color, int options) {
+
+ if(!FreeImage_HasPixels(src)) return NULL;
+
+ // Just return a clone of the image, if left, top, right and bottom are
+ // all zero.
+ if ((left == 0) && (right == 0) && (top == 0) && (bottom == 0)) {
+ return FreeImage_Clone(src);
+ }
+
+ int width = FreeImage_GetWidth(src);
+ int height = FreeImage_GetHeight(src);
+
+ // Relay on FreeImage_Copy, if all parameters left, top, right and
+ // bottom are smaller than or equal zero. The color pointer may be
+ // NULL in this case.
+ if ((left <= 0) && (right <= 0) && (top <= 0) && (bottom <= 0)) {
+ return FreeImage_Copy(src, -left, -top, width + right, height + bottom);
+ }
+
+ // From here, we need a valid color, since the image will be enlarged on
+ // at least one side. So, fail if we don't have a valid color pointer.
+ if (!color) {
+ return NULL;
+ }
+
+ if (((left < 0) && (-left >= width)) || ((right < 0) && (-right >= width)) ||
+ ((top < 0) && (-top >= height)) || ((bottom < 0) && (-bottom >= height))) {
+ return NULL;
+ }
+
+ unsigned newWidth = width + left + right;
+ unsigned newHeight = height + top + bottom;
+
+ FREE_IMAGE_TYPE type = FreeImage_GetImageType(src);
+ unsigned bpp = FreeImage_GetBPP(src);
+
+ FIBITMAP *dst = FreeImage_AllocateExT(
+ type, newWidth, newHeight, bpp, color, options,
+ FreeImage_GetPalette(src),
+ FreeImage_GetRedMask(src),
+ FreeImage_GetGreenMask(src),
+ FreeImage_GetBlueMask(src));
+
+ if (!dst) {
+ return NULL;
+ }
+
+ if ((type == FIT_BITMAP) && (bpp <= 4)) {
+ FIBITMAP *copy = FreeImage_Copy(src,
+ ((left >= 0) ? 0 : -left),
+ ((top >= 0) ? 0 : -top),
+ ((width+right)>width)?width:(width+right),
+ ((height+bottom)>height)?height:(height+bottom));
+
+ if (!copy) {
+ FreeImage_Unload(dst);
+ return NULL;
+ }
+
+ if (!FreeImage_Paste(dst, copy,
+ ((left <= 0) ? 0 : left),
+ ((top <= 0) ? 0 : top), 256)) {
+ FreeImage_Unload(copy);
+ FreeImage_Unload(dst);
+ return NULL;
+ }
+
+ FreeImage_Unload(copy);
+
+ } else {
+
+ int bytespp = bpp / 8;
+ BYTE *srcPtr = FreeImage_GetScanLine(src, height - 1 - ((top >= 0) ? 0 : -top));
+ BYTE *dstPtr = FreeImage_GetScanLine(dst, newHeight - 1 - ((top <= 0) ? 0 : top));
+
+ unsigned srcPitch = FreeImage_GetPitch(src);
+ unsigned dstPitch = FreeImage_GetPitch(dst);
+
+ int lineWidth = bytespp * (width + MIN(0, left) + MIN(0, right));
+ int lines = height + MIN(0, top) + MIN(0, bottom);
+
+ if (left <= 0) {
+ srcPtr += (-left * bytespp);
+ } else {
+ dstPtr += (left * bytespp);
+ }
+
+ for (int i = 0; i < lines; i++) {
+ memcpy(dstPtr, srcPtr, lineWidth);
+ srcPtr -= srcPitch;
+ dstPtr -= dstPitch;
+ }
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ // copy transparency table
+ FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(src), FreeImage_GetTransparencyCount(src));
+
+ // copy background color
+ RGBQUAD bkcolor;
+ if( FreeImage_GetBackgroundColor(src, &bkcolor) ) {
+ FreeImage_SetBackgroundColor(dst, &bkcolor);
+ }
+
+ // clone resolution
+ FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(src));
+ FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(src));
+
+ // clone ICC profile
+ FIICCPROFILE *src_profile = FreeImage_GetICCProfile(src);
+ FIICCPROFILE *dst_profile = FreeImage_CreateICCProfile(dst, src_profile->data, src_profile->size);
+ dst_profile->flags = src_profile->flags;
+
+ return dst;
+}
+
diff --git a/libs/freeimage/src/FreeImageToolkit/Channels.cpp b/libs/freeimage/src/FreeImageToolkit/Channels.cpp
new file mode 100644
index 0000000000..b373f4046d
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/Channels.cpp
@@ -0,0 +1,486 @@
+// ==========================================================
+// Channel processing support
+//
+// Design and implementation 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!
+// ==========================================================
+
+#include "../stdafx.h"
+
+/** @brief Retrieves the red, green, blue or alpha channel of a BGR[A] image.
+@param src Input image to be processed.
+@param channel Color channel to extract
+@return Returns the extracted channel if successful, returns NULL otherwise.
+*/
+FIBITMAP * DLL_CALLCONV
+FreeImage_GetChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
+
+ if(!FreeImage_HasPixels(src)) return NULL;
+
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+ unsigned bpp = FreeImage_GetBPP(src);
+
+ // 24- or 32-bit
+ if(image_type == FIT_BITMAP && ((bpp == 24) || (bpp == 32))) {
+ int c;
+
+ // select the channel to extract
+ switch(channel) {
+ case FICC_BLUE:
+ c = FI_RGBA_BLUE;
+ break;
+ case FICC_GREEN:
+ c = FI_RGBA_GREEN;
+ break;
+ case FICC_RED:
+ c = FI_RGBA_RED;
+ break;
+ case FICC_ALPHA:
+ if(bpp != 32) return NULL;
+ c = FI_RGBA_ALPHA;
+ break;
+ default:
+ return NULL;
+ }
+
+ // allocate a 8-bit dib
+ unsigned width = FreeImage_GetWidth(src);
+ unsigned height = FreeImage_GetHeight(src);
+ FIBITMAP *dst = FreeImage_Allocate(width, height, 8) ;
+ if(!dst) return NULL;
+ // build a greyscale palette
+ RGBQUAD *pal = FreeImage_GetPalette(dst);
+ for(int i = 0; i < 256; i++) {
+ pal[i].rgbBlue = pal[i].rgbGreen = pal[i].rgbRed = (BYTE)i;
+ }
+
+ // perform extraction
+
+ int bytespp = bpp / 8; // bytes / pixel
+
+ for(unsigned y = 0; y < height; y++) {
+ BYTE *src_bits = FreeImage_GetScanLine(src, y);
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < width; x++) {
+ dst_bits[x] = src_bits[c];
+ src_bits += bytespp;
+ }
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ return dst;
+ }
+
+ // 48-bit RGB or 64-bit RGBA images
+ if((image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) {
+ int c;
+
+ // select the channel to extract (always RGB[A])
+ switch(channel) {
+ case FICC_BLUE:
+ c = 2;
+ break;
+ case FICC_GREEN:
+ c = 1;
+ break;
+ case FICC_RED:
+ c = 0;
+ break;
+ case FICC_ALPHA:
+ if(bpp != 64) return NULL;
+ c = 3;
+ break;
+ default:
+ return NULL;
+ }
+
+ // allocate a greyscale dib
+ unsigned width = FreeImage_GetWidth(src);
+ unsigned height = FreeImage_GetHeight(src);
+ FIBITMAP *dst = FreeImage_AllocateT(FIT_UINT16, width, height) ;
+ if(!dst) return NULL;
+
+ // perform extraction
+
+ int bytespp = bpp / 16; // words / pixel
+
+ for(unsigned y = 0; y < height; y++) {
+ unsigned short *src_bits = (unsigned short*)FreeImage_GetScanLine(src, y);
+ unsigned short *dst_bits = (unsigned short*)FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < width; x++) {
+ dst_bits[x] = src_bits[c];
+ src_bits += bytespp;
+ }
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ return dst;
+ }
+
+ // 96-bit RGBF or 128-bit RGBAF images
+ if((image_type == FIT_RGBF) || (image_type == FIT_RGBAF)) {
+ int c;
+
+ // select the channel to extract (always RGB[A])
+ switch(channel) {
+ case FICC_BLUE:
+ c = 2;
+ break;
+ case FICC_GREEN:
+ c = 1;
+ break;
+ case FICC_RED:
+ c = 0;
+ break;
+ case FICC_ALPHA:
+ if(bpp != 128) return NULL;
+ c = 3;
+ break;
+ default:
+ return NULL;
+ }
+
+ // allocate a greyscale dib
+ unsigned width = FreeImage_GetWidth(src);
+ unsigned height = FreeImage_GetHeight(src);
+ FIBITMAP *dst = FreeImage_AllocateT(FIT_FLOAT, width, height) ;
+ if(!dst) return NULL;
+
+ // perform extraction
+
+ int bytespp = bpp / 32; // floats / pixel
+
+ for(unsigned y = 0; y < height; y++) {
+ float *src_bits = (float*)FreeImage_GetScanLine(src, y);
+ float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < width; x++) {
+ dst_bits[x] = src_bits[c];
+ src_bits += bytespp;
+ }
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ return dst;
+ }
+
+ return NULL;
+}
+
+/** @brief Insert a greyscale dib into a RGB[A] image.
+Both src and dst must have the same width and height.
+@param dst Image to modify (RGB or RGBA)
+@param src Input greyscale image to insert
+@param channel Color channel to modify
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV
+FreeImage_SetChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
+ int c;
+
+ if(!FreeImage_HasPixels(src) || !FreeImage_HasPixels(dst)) return FALSE;
+
+ // src and dst images should have the same width and height
+ unsigned src_width = FreeImage_GetWidth(src);
+ unsigned src_height = FreeImage_GetHeight(src);
+ unsigned dst_width = FreeImage_GetWidth(dst);
+ unsigned dst_height = FreeImage_GetHeight(dst);
+ if((src_width != dst_width) || (src_height != dst_height))
+ return FALSE;
+
+ // src image should be grayscale, dst image should be RGB or RGBA
+ FREE_IMAGE_COLOR_TYPE src_type = FreeImage_GetColorType(src);
+ FREE_IMAGE_COLOR_TYPE dst_type = FreeImage_GetColorType(dst);
+ if((dst_type != FIC_RGB) && (dst_type != FIC_RGBALPHA) || (src_type != FIC_MINISBLACK)) {
+ return FALSE;
+ }
+
+ FREE_IMAGE_TYPE src_image_type = FreeImage_GetImageType(src);
+ FREE_IMAGE_TYPE dst_image_type = FreeImage_GetImageType(dst);
+
+ if((dst_image_type == FIT_BITMAP) && (src_image_type == FIT_BITMAP)) {
+
+ // src image should be grayscale, dst image should be 24- or 32-bit
+ unsigned src_bpp = FreeImage_GetBPP(src);
+ unsigned dst_bpp = FreeImage_GetBPP(dst);
+ if((src_bpp != 8) || (dst_bpp != 24) && (dst_bpp != 32))
+ return FALSE;
+
+
+ // select the channel to modify
+ switch(channel) {
+ case FICC_BLUE:
+ c = FI_RGBA_BLUE;
+ break;
+ case FICC_GREEN:
+ c = FI_RGBA_GREEN;
+ break;
+ case FICC_RED:
+ c = FI_RGBA_RED;
+ break;
+ case FICC_ALPHA:
+ if(dst_bpp != 32) return FALSE;
+ c = FI_RGBA_ALPHA;
+ break;
+ default:
+ return FALSE;
+ }
+
+ // perform insertion
+
+ int bytespp = dst_bpp / 8; // bytes / pixel
+
+ for(unsigned y = 0; y < dst_height; y++) {
+ BYTE *src_bits = FreeImage_GetScanLine(src, y);
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < dst_width; x++) {
+ dst_bits[c] = src_bits[x];
+ dst_bits += bytespp;
+ }
+ }
+
+ return TRUE;
+ }
+
+ if(((dst_image_type == FIT_RGB16) || (dst_image_type == FIT_RGBA16)) && (src_image_type == FIT_UINT16)) {
+
+ // src image should be grayscale, dst image should be 48- or 64-bit
+ unsigned src_bpp = FreeImage_GetBPP(src);
+ unsigned dst_bpp = FreeImage_GetBPP(dst);
+ if((src_bpp != 16) || (dst_bpp != 48) && (dst_bpp != 64))
+ return FALSE;
+
+
+ // select the channel to modify (always RGB[A])
+ switch(channel) {
+ case FICC_BLUE:
+ c = 2;
+ break;
+ case FICC_GREEN:
+ c = 1;
+ break;
+ case FICC_RED:
+ c = 0;
+ break;
+ case FICC_ALPHA:
+ if(dst_bpp != 64) return FALSE;
+ c = 3;
+ break;
+ default:
+ return FALSE;
+ }
+
+ // perform insertion
+
+ int bytespp = dst_bpp / 16; // words / pixel
+
+ for(unsigned y = 0; y < dst_height; y++) {
+ unsigned short *src_bits = (unsigned short*)FreeImage_GetScanLine(src, y);
+ unsigned short *dst_bits = (unsigned short*)FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < dst_width; x++) {
+ dst_bits[c] = src_bits[x];
+ dst_bits += bytespp;
+ }
+ }
+
+ return TRUE;
+ }
+
+ if(((dst_image_type == FIT_RGBF) || (dst_image_type == FIT_RGBAF)) && (src_image_type == FIT_FLOAT)) {
+
+ // src image should be grayscale, dst image should be 96- or 128-bit
+ unsigned src_bpp = FreeImage_GetBPP(src);
+ unsigned dst_bpp = FreeImage_GetBPP(dst);
+ if((src_bpp != 32) || (dst_bpp != 96) && (dst_bpp != 128))
+ return FALSE;
+
+
+ // select the channel to modify (always RGB[A])
+ switch(channel) {
+ case FICC_BLUE:
+ c = 2;
+ break;
+ case FICC_GREEN:
+ c = 1;
+ break;
+ case FICC_RED:
+ c = 0;
+ break;
+ case FICC_ALPHA:
+ if(dst_bpp != 128) return FALSE;
+ c = 3;
+ break;
+ default:
+ return FALSE;
+ }
+
+ // perform insertion
+
+ int bytespp = dst_bpp / 32; // floats / pixel
+
+ for(unsigned y = 0; y < dst_height; y++) {
+ float *src_bits = (float*)FreeImage_GetScanLine(src, y);
+ float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < dst_width; x++) {
+ dst_bits[c] = src_bits[x];
+ dst_bits += bytespp;
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/** @brief Retrieves the real part, imaginary part, magnitude or phase of a complex image.
+@param src Input image to be processed.
+@param channel Channel to extract
+@return Returns the extracted channel if successful, returns NULL otherwise.
+*/
+FIBITMAP * DLL_CALLCONV
+FreeImage_GetComplexChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
+ unsigned x, y;
+ double mag, phase;
+ FICOMPLEX *src_bits = NULL;
+ double *dst_bits = NULL;
+ FIBITMAP *dst = NULL;
+
+ if(!FreeImage_HasPixels(src)) return NULL;
+
+ if(FreeImage_GetImageType(src) == FIT_COMPLEX) {
+ // allocate a dib of type FIT_DOUBLE
+ unsigned width = FreeImage_GetWidth(src);
+ unsigned height = FreeImage_GetHeight(src);
+ dst = FreeImage_AllocateT(FIT_DOUBLE, width, height) ;
+ if(!dst) return NULL;
+
+ // perform extraction
+
+ switch(channel) {
+ case FICC_REAL: // real part
+ for(y = 0; y < height; y++) {
+ src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
+ dst_bits = (double *)FreeImage_GetScanLine(dst, y);
+ for(x = 0; x < width; x++) {
+ dst_bits[x] = src_bits[x].r;
+ }
+ }
+ break;
+
+ case FICC_IMAG: // imaginary part
+ for(y = 0; y < height; y++) {
+ src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
+ dst_bits = (double *)FreeImage_GetScanLine(dst, y);
+ for(x = 0; x < width; x++) {
+ dst_bits[x] = src_bits[x].i;
+ }
+ }
+ break;
+
+ case FICC_MAG: // magnitude
+ for(y = 0; y < height; y++) {
+ src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
+ dst_bits = (double *)FreeImage_GetScanLine(dst, y);
+ for(x = 0; x < width; x++) {
+ mag = src_bits[x].r * src_bits[x].r + src_bits[x].i * src_bits[x].i;
+ dst_bits[x] = sqrt(mag);
+ }
+ }
+ break;
+
+ case FICC_PHASE: // phase
+ for(y = 0; y < height; y++) {
+ src_bits = (FICOMPLEX *)FreeImage_GetScanLine(src, y);
+ dst_bits = (double *)FreeImage_GetScanLine(dst, y);
+ for(x = 0; x < width; x++) {
+ if((src_bits[x].r == 0) && (src_bits[x].i == 0)) {
+ phase = 0;
+ } else {
+ phase = atan2(src_bits[x].i, src_bits[x].r);
+ }
+ dst_bits[x] = phase;
+ }
+ }
+ break;
+ }
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ return dst;
+}
+
+/** @brief Set the real or imaginary part of a complex image.
+Both src and dst must have the same width and height.
+@param dst Image to modify (image of type FIT_COMPLEX)
+@param src Input image of type FIT_DOUBLE
+@param channel Channel to modify
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV
+FreeImage_SetComplexChannel(FIBITMAP *dst, FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) {
+ unsigned x, y;
+ double *src_bits = NULL;
+ FICOMPLEX *dst_bits = NULL;
+
+ if(!FreeImage_HasPixels(src) || !FreeImage_HasPixels(dst)) return FALSE;
+
+ // src image should be of type FIT_DOUBLE, dst image should be of type FIT_COMPLEX
+ const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(src);
+ const FREE_IMAGE_TYPE dst_type = FreeImage_GetImageType(dst);
+ if((src_type != FIT_DOUBLE) || (dst_type != FIT_COMPLEX))
+ return FALSE;
+
+ // src and dst images should have the same width and height
+ unsigned src_width = FreeImage_GetWidth(src);
+ unsigned src_height = FreeImage_GetHeight(src);
+ unsigned dst_width = FreeImage_GetWidth(dst);
+ unsigned dst_height = FreeImage_GetHeight(dst);
+ if((src_width != dst_width) || (src_height != dst_height))
+ return FALSE;
+
+ // select the channel to modify
+ switch(channel) {
+ case FICC_REAL: // real part
+ for(y = 0; y < dst_height; y++) {
+ src_bits = (double *)FreeImage_GetScanLine(src, y);
+ dst_bits = (FICOMPLEX *)FreeImage_GetScanLine(dst, y);
+ for(x = 0; x < dst_width; x++) {
+ dst_bits[x].r = src_bits[x];
+ }
+ }
+ break;
+ case FICC_IMAG: // imaginary part
+ for(y = 0; y < dst_height; y++) {
+ src_bits = (double *)FreeImage_GetScanLine(src, y);
+ dst_bits = (FICOMPLEX *)FreeImage_GetScanLine(dst, y);
+ for(x = 0; x < dst_width; x++) {
+ dst_bits[x].i = src_bits[x];
+ }
+ }
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/libs/freeimage/src/FreeImageToolkit/ClassicRotate.cpp b/libs/freeimage/src/FreeImageToolkit/ClassicRotate.cpp
new file mode 100644
index 0000000000..ba8985c26c
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/ClassicRotate.cpp
@@ -0,0 +1,916 @@
+// ==========================================================
+// Bitmap rotation by means of 3 shears.
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Thorsten Radde (support@IdealSoftware.com)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// 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!
+// ==========================================================
+
+/*
+ ============================================================
+ References :
+ [1] Paeth A., A Fast Algorithm for General Raster Rotation.
+ Graphics Gems, p. 179, Andrew Glassner editor, Academic Press, 1990.
+ [2] Yariv E., High quality image rotation (rotate by shear).
+ [Online] http://www.codeproject.com/bitmap/rotatebyshear.asp
+ [3] Treskunov A., Fast and high quality true-color bitmap rotation function.
+ [Online] http://anton.treskunov.net/Software/doc/fast_and_high_quality_true_color_bitmap_rotation_function.html
+ ============================================================
+*/
+
+#include "../stdafx.h"
+
+#define RBLOCK 64 // image blocks of RBLOCK*RBLOCK pixels
+
+// --------------------------------------------------------------------------
+
+/**
+Skews a row horizontally (with filtered weights).
+Limited to 45 degree skewing only. Filters two adjacent pixels.
+Parameter T can be BYTE, WORD of float.
+@param src Pointer to source image to rotate
+@param dst Pointer to destination image
+@param row Row index
+@param iOffset Skew offset
+@param dWeight Relative weight of right pixel
+@param bkcolor Background color
+*/
+template <class T> void
+HorizontalSkewT(FIBITMAP *src, FIBITMAP *dst, int row, int iOffset, double weight, const void *bkcolor = NULL) {
+ int iXPos;
+
+ const unsigned src_width = FreeImage_GetWidth(src);
+ const unsigned dst_width = FreeImage_GetWidth(dst);
+
+ T pxlSrc[4], pxlLeft[4], pxlOldLeft[4]; // 4 = 4*sizeof(T) max
+
+ // background
+ const T pxlBlack[4] = {0, 0, 0, 0 };
+ const T *pxlBkg = static_cast<const T*>(bkcolor); // assume at least bytespp and 4*sizeof(T) max
+ if(!pxlBkg) {
+ // default background color is black
+ pxlBkg = pxlBlack;
+ }
+
+ // calculate the number of bytes per pixel
+ const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+ // calculate the number of samples per pixel
+ const unsigned samples = bytespp / sizeof(T);
+
+ BYTE *src_bits = FreeImage_GetScanLine(src, row);
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, row);
+
+ // fill gap left of skew with background
+ if(bkcolor) {
+ for(int k = 0; k < iOffset; k++) {
+ memcpy(&dst_bits[k * bytespp], bkcolor, bytespp);
+ }
+ AssignPixel((BYTE*)&pxlOldLeft[0], (BYTE*)bkcolor, bytespp);
+ } else {
+ if(iOffset > 0) {
+ memset(dst_bits, 0, iOffset * bytespp);
+ }
+ memset(&pxlOldLeft[0], 0, bytespp);
+ }
+
+ for(unsigned i = 0; i < src_width; i++) {
+ // loop through row pixels
+ AssignPixel((BYTE*)&pxlSrc[0], (BYTE*)src_bits, bytespp);
+ // calculate weights
+ for(unsigned j = 0; j < samples; j++) {
+ pxlLeft[j] = static_cast<T>(pxlBkg[j] + (pxlSrc[j] - pxlBkg[j]) * weight + 0.5);
+ }
+ // check boundaries
+ iXPos = i + iOffset;
+ if((iXPos >= 0) && (iXPos < (int)dst_width)) {
+ // update left over on source
+ for(unsigned j = 0; j < samples; j++) {
+ pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);
+ }
+ AssignPixel((BYTE*)&dst_bits[iXPos*bytespp], (BYTE*)&pxlSrc[0], bytespp);
+ }
+ // save leftover for next pixel in scan
+ AssignPixel((BYTE*)&pxlOldLeft[0], (BYTE*)&pxlLeft[0], bytespp);
+
+ // next pixel in scan
+ src_bits += bytespp;
+ }
+
+ // go to rightmost point of skew
+ iXPos = src_width + iOffset;
+
+ if((iXPos >= 0) && (iXPos < (int)dst_width)) {
+ dst_bits = FreeImage_GetScanLine(dst, row) + iXPos * bytespp;
+
+ // If still in image bounds, put leftovers there
+ AssignPixel((BYTE*)dst_bits, (BYTE*)&pxlOldLeft[0], bytespp);
+
+ // clear to the right of the skewed line with background
+ dst_bits += bytespp;
+ if(bkcolor) {
+ for(unsigned i = 0; i < dst_width - iXPos - 1; i++) {
+ memcpy(&dst_bits[i * bytespp], bkcolor, bytespp);
+ }
+ } else {
+ memset(dst_bits, 0, bytespp * (dst_width - iXPos - 1));
+ }
+
+ }
+}
+
+/**
+Skews a row horizontally (with filtered weights).
+Limited to 45 degree skewing only. Filters two adjacent pixels.
+@param src Pointer to source image to rotate
+@param dst Pointer to destination image
+@param row Row index
+@param iOffset Skew offset
+@param dWeight Relative weight of right pixel
+@param bkcolor Background color
+*/
+static void
+HorizontalSkew(FIBITMAP *src, FIBITMAP *dst, int row, int iOffset, double dWeight, const void *bkcolor) {
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+ switch(image_type) {
+ case FIT_BITMAP:
+ switch(FreeImage_GetBPP(src)) {
+ case 8:
+ case 24:
+ case 32:
+ HorizontalSkewT<BYTE>(src, dst, row, iOffset, dWeight, bkcolor);
+ break;
+ }
+ break;
+ case FIT_UINT16:
+ case FIT_RGB16:
+ case FIT_RGBA16:
+ HorizontalSkewT<WORD>(src, dst, row, iOffset, dWeight, bkcolor);
+ break;
+ case FIT_FLOAT:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ HorizontalSkewT<float>(src, dst, row, iOffset, dWeight, bkcolor);
+ break;
+ }
+}
+
+/**
+Skews a column vertically (with filtered weights).
+Limited to 45 degree skewing only. Filters two adjacent pixels.
+Parameter T can be BYTE, WORD of float.
+@param src Pointer to source image to rotate
+@param dst Pointer to destination image
+@param col Column index
+@param iOffset Skew offset
+@param dWeight Relative weight of upper pixel
+@param bkcolor Background color
+*/
+template <class T> void
+VerticalSkewT(FIBITMAP *src, FIBITMAP *dst, int col, int iOffset, double weight, const void *bkcolor = NULL) {
+ int iYPos;
+
+ unsigned src_height = FreeImage_GetHeight(src);
+ unsigned dst_height = FreeImage_GetHeight(dst);
+
+ T pxlSrc[4], pxlLeft[4], pxlOldLeft[4]; // 4 = 4*sizeof(T) max
+
+ // background
+ const T pxlBlack[4] = {0, 0, 0, 0 };
+ const T *pxlBkg = static_cast<const T*>(bkcolor); // assume at least bytespp and 4*sizeof(T) max
+ if(!pxlBkg) {
+ // default background color is black
+ pxlBkg = pxlBlack;
+ }
+
+ // calculate the number of bytes per pixel
+ const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+ // calculate the number of samples per pixel
+ const unsigned samples = bytespp / sizeof(T);
+
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const unsigned dst_pitch = FreeImage_GetPitch(dst);
+ const unsigned index = col * bytespp;
+
+ BYTE *src_bits = FreeImage_GetBits(src) + index;
+ BYTE *dst_bits = FreeImage_GetBits(dst) + index;
+
+ // fill gap above skew with background
+ if(bkcolor) {
+ for(int k = 0; k < iOffset; k++) {
+ memcpy(dst_bits, bkcolor, bytespp);
+ dst_bits += dst_pitch;
+ }
+ memcpy(&pxlOldLeft[0], bkcolor, bytespp);
+ } else {
+ for(int k = 0; k < iOffset; k++) {
+ memset(dst_bits, 0, bytespp);
+ dst_bits += dst_pitch;
+ }
+ memset(&pxlOldLeft[0], 0, bytespp);
+ }
+
+ for(unsigned i = 0; i < src_height; i++) {
+ // loop through column pixels
+ AssignPixel((BYTE*)(&pxlSrc[0]), src_bits, bytespp);
+ // calculate weights
+ for(unsigned j = 0; j < samples; j++) {
+ pxlLeft[j] = static_cast<T>(pxlBkg[j] + (pxlSrc[j] - pxlBkg[j]) * weight + 0.5);
+ }
+ // check boundaries
+ iYPos = i + iOffset;
+ if((iYPos >= 0) && (iYPos < (int)dst_height)) {
+ // update left over on source
+ for(unsigned j = 0; j < samples; j++) {
+ pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);
+ }
+ dst_bits = FreeImage_GetScanLine(dst, iYPos) + index;
+ AssignPixel(dst_bits, (BYTE*)(&pxlSrc[0]), bytespp);
+ }
+ // save leftover for next pixel in scan
+ AssignPixel((BYTE*)(&pxlOldLeft[0]), (BYTE*)(&pxlLeft[0]), bytespp);
+
+ // next pixel in scan
+ src_bits += src_pitch;
+ }
+ // go to bottom point of skew
+ iYPos = src_height + iOffset;
+
+ if((iYPos >= 0) && (iYPos < (int)dst_height)) {
+ dst_bits = FreeImage_GetScanLine(dst, iYPos) + index;
+
+ // if still in image bounds, put leftovers there
+ AssignPixel((BYTE*)(dst_bits), (BYTE*)(&pxlOldLeft[0]), bytespp);
+
+ // clear below skewed line with background
+ if(bkcolor) {
+ while(++iYPos < (int)dst_height) {
+ dst_bits += dst_pitch;
+ AssignPixel((BYTE*)(dst_bits), (BYTE*)(bkcolor), bytespp);
+ }
+ } else {
+ while(++iYPos < (int)dst_height) {
+ dst_bits += dst_pitch;
+ memset(dst_bits, 0, bytespp);
+ }
+ }
+ }
+}
+
+/**
+Skews a column vertically (with filtered weights).
+Limited to 45 degree skewing only. Filters two adjacent pixels.
+@param src Pointer to source image to rotate
+@param dst Pointer to destination image
+@param col Column index
+@param iOffset Skew offset
+@param dWeight Relative weight of upper pixel
+@param bkcolor Background color
+*/
+static void
+VerticalSkew(FIBITMAP *src, FIBITMAP *dst, int col, int iOffset, double dWeight, const void *bkcolor) {
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+ switch(image_type) {
+ case FIT_BITMAP:
+ switch(FreeImage_GetBPP(src)) {
+ case 8:
+ case 24:
+ case 32:
+ VerticalSkewT<BYTE>(src, dst, col, iOffset, dWeight, bkcolor);
+ break;
+ }
+ break;
+ case FIT_UINT16:
+ case FIT_RGB16:
+ case FIT_RGBA16:
+ VerticalSkewT<WORD>(src, dst, col, iOffset, dWeight, bkcolor);
+ break;
+ case FIT_FLOAT:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ VerticalSkewT<float>(src, dst, col, iOffset, dWeight, bkcolor);
+ break;
+ }
+}
+
+/**
+Rotates an image by 90 degrees (counter clockwise).
+Precise rotation, no filters required.<br>
+Code adapted from CxImage (http://www.xdp.it/cximage.htm)
+@param src Pointer to source image to rotate
+@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
+*/
+static FIBITMAP*
+Rotate90(FIBITMAP *src) {
+
+ const unsigned bpp = FreeImage_GetBPP(src);
+
+ const unsigned src_width = FreeImage_GetWidth(src);
+ const unsigned src_height = FreeImage_GetHeight(src);
+ const unsigned dst_width = src_height;
+ const unsigned dst_height = src_width;
+
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+ // allocate and clear dst image
+ FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);
+ if(NULL == dst) return NULL;
+
+ // get src and dst scan width
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const unsigned dst_pitch = FreeImage_GetPitch(dst);
+
+ switch(image_type) {
+ case FIT_BITMAP:
+ if(bpp == 1) {
+ // speedy rotate for BW images
+
+ BYTE *bsrc = FreeImage_GetBits(src);
+ BYTE *bdest = FreeImage_GetBits(dst);
+
+ BYTE *dbitsmax = bdest + dst_height * dst_pitch - 1;
+
+ for(unsigned y = 0; y < src_height; y++) {
+ // figure out the column we are going to be copying to
+ const div_t div_r = div(y, 8);
+ // set bit pos of src column byte
+ const BYTE bitpos = (BYTE)(128 >> div_r.rem);
+ BYTE *srcdisp = bsrc + y * src_pitch;
+ for(unsigned x = 0; x < src_pitch; x++) {
+ // get source bits
+ BYTE *sbits = srcdisp + x;
+ // get destination column
+ BYTE *nrow = bdest + (dst_height - 1 - (x * 8)) * dst_pitch + div_r.quot;
+ for (int z = 0; z < 8; z++) {
+ // get destination byte
+ BYTE *dbits = nrow - z * dst_pitch;
+ if ((dbits < bdest) || (dbits > dbitsmax)) break;
+ if (*sbits & (128 >> z)) *dbits |= bitpos;
+ }
+ }
+ }
+ }
+ else if((bpp == 8) || (bpp == 24) || (bpp == 32)) {
+ // anything other than BW :
+ // This optimized version of rotation rotates image by smaller blocks. It is quite
+ // a bit faster than obvious algorithm, because it produces much less CPU cache misses.
+ // This optimization can be tuned by changing block size (RBLOCK). 96 is good value for current
+ // CPUs (tested on Athlon XP and Celeron D). Larger value (if CPU has enough cache) will increase
+ // speed somehow, but once you drop out of CPU's cache, things will slow down drastically.
+ // For older CPUs with less cache, lower value would yield better results.
+
+ BYTE *bsrc = FreeImage_GetBits(src); // source pixels
+ BYTE *bdest = FreeImage_GetBits(dst); // destination pixels
+
+ // calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
+ const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+ // for all image blocks of RBLOCK*RBLOCK pixels
+
+ // x-segment
+ for(unsigned xs = 0; xs < dst_width; xs += RBLOCK) {
+ // y-segment
+ for(unsigned ys = 0; ys < dst_height; ys += RBLOCK) {
+ for(unsigned y = ys; y < MIN(dst_height, ys + RBLOCK); y++) { // do rotation
+ const unsigned y2 = dst_height - y - 1;
+ // point to src pixel at (y2, xs)
+ BYTE *src_bits = bsrc + (xs * src_pitch) + (y2 * bytespp);
+ // point to dst pixel at (xs, y)
+ BYTE *dst_bits = bdest + (y * dst_pitch) + (xs * bytespp);
+ for(unsigned x = xs; x < MIN(dst_width, xs + RBLOCK); x++) {
+ // dst.SetPixel(x, y, src.GetPixel(y2, x));
+ AssignPixel(dst_bits, src_bits, bytespp);
+ dst_bits += bytespp;
+ src_bits += src_pitch;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case FIT_UINT16:
+ case FIT_RGB16:
+ case FIT_RGBA16:
+ case FIT_FLOAT:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ {
+ BYTE *bsrc = FreeImage_GetBits(src); // source pixels
+ BYTE *bdest = FreeImage_GetBits(dst); // destination pixels
+
+ // calculate the number of bytes per pixel
+ const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+ for(unsigned y = 0; y < dst_height; y++) {
+ BYTE *src_bits = bsrc + (src_width - 1 - y) * bytespp;
+ BYTE *dst_bits = bdest + (y * dst_pitch);
+ for(unsigned x = 0; x < dst_width; x++) {
+ AssignPixel(dst_bits, src_bits, bytespp);
+ src_bits += src_pitch;
+ dst_bits += bytespp;
+ }
+ }
+ }
+ break;
+ }
+
+ return dst;
+}
+
+/**
+Rotates an image by 180 degrees (counter clockwise).
+Precise rotation, no filters required.
+@param src Pointer to source image to rotate
+@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
+*/
+static FIBITMAP*
+Rotate180(FIBITMAP *src) {
+ int x, y, k, pos;
+
+ const int bpp = FreeImage_GetBPP(src);
+
+ const int src_width = FreeImage_GetWidth(src);
+ const int src_height = FreeImage_GetHeight(src);
+ const int dst_width = src_width;
+ const int dst_height = src_height;
+
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+ FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);
+ if(NULL == dst) return NULL;
+
+ switch(image_type) {
+ case FIT_BITMAP:
+ if(bpp == 1) {
+ for(int y = 0; y < src_height; y++) {
+ BYTE *src_bits = FreeImage_GetScanLine(src, y);
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, dst_height - y - 1);
+ for(int x = 0; x < src_width; x++) {
+ // get bit at (x, y)
+ k = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
+ // set bit at (dst_width - x - 1, dst_height - y - 1)
+ pos = dst_width - x - 1;
+ k ? dst_bits[pos >> 3] |= (0x80 >> (pos & 0x7)) : dst_bits[pos >> 3] &= (0xFF7F >> (pos & 0x7));
+ }
+ }
+ break;
+ }
+ // else if((bpp == 8) || (bpp == 24) || (bpp == 32)) FALL TROUGH
+ case FIT_UINT16:
+ case FIT_RGB16:
+ case FIT_RGBA16:
+ case FIT_FLOAT:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ {
+ // Calculate the number of bytes per pixel
+ const int bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+ for(y = 0; y < src_height; y++) {
+ BYTE *src_bits = FreeImage_GetScanLine(src, y);
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, dst_height - y - 1) + (dst_width - 1) * bytespp;
+ for(x = 0; x < src_width; x++) {
+ // get pixel at (x, y)
+ // set pixel at (dst_width - x - 1, dst_height - y - 1)
+ AssignPixel(dst_bits, src_bits, bytespp);
+ src_bits += bytespp;
+ dst_bits -= bytespp;
+ }
+ }
+ }
+ break;
+ }
+
+ return dst;
+}
+
+/**
+Rotates an image by 270 degrees (counter clockwise).
+Precise rotation, no filters required.<br>
+Code adapted from CxImage (http://www.xdp.it/cximage.htm)
+@param src Pointer to source image to rotate
+@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
+*/
+static FIBITMAP*
+Rotate270(FIBITMAP *src) {
+ int x2, dlineup;
+
+ const unsigned bpp = FreeImage_GetBPP(src);
+
+ const unsigned src_width = FreeImage_GetWidth(src);
+ const unsigned src_height = FreeImage_GetHeight(src);
+ const unsigned dst_width = src_height;
+ const unsigned dst_height = src_width;
+
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+ // allocate and clear dst image
+ FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);
+ if(NULL == dst) return NULL;
+
+ // get src and dst scan width
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const unsigned dst_pitch = FreeImage_GetPitch(dst);
+
+ switch(image_type) {
+ case FIT_BITMAP:
+ if(bpp == 1) {
+ // speedy rotate for BW images
+
+ BYTE *bsrc = FreeImage_GetBits(src);
+ BYTE *bdest = FreeImage_GetBits(dst);
+ BYTE *dbitsmax = bdest + dst_height * dst_pitch - 1;
+ dlineup = 8 * dst_pitch - dst_width;
+
+ for(unsigned y = 0; y < src_height; y++) {
+ // figure out the column we are going to be copying to
+ const div_t div_r = div(y + dlineup, 8);
+ // set bit pos of src column byte
+ const BYTE bitpos = (BYTE)(1 << div_r.rem);
+ const BYTE *srcdisp = bsrc + y * src_pitch;
+ for(unsigned x = 0; x < src_pitch; x++) {
+ // get source bits
+ const BYTE *sbits = srcdisp + x;
+ // get destination column
+ BYTE *nrow = bdest + (x * 8) * dst_pitch + dst_pitch - 1 - div_r.quot;
+ for(unsigned z = 0; z < 8; z++) {
+ // get destination byte
+ BYTE *dbits = nrow + z * dst_pitch;
+ if ((dbits < bdest) || (dbits > dbitsmax)) break;
+ if (*sbits & (128 >> z)) *dbits |= bitpos;
+ }
+ }
+ }
+ }
+ else if((bpp == 8) || (bpp == 24) || (bpp == 32)) {
+ // anything other than BW :
+ // This optimized version of rotation rotates image by smaller blocks. It is quite
+ // a bit faster than obvious algorithm, because it produces much less CPU cache misses.
+ // This optimization can be tuned by changing block size (RBLOCK). 96 is good value for current
+ // CPUs (tested on Athlon XP and Celeron D). Larger value (if CPU has enough cache) will increase
+ // speed somehow, but once you drop out of CPU's cache, things will slow down drastically.
+ // For older CPUs with less cache, lower value would yield better results.
+
+ BYTE *bsrc = FreeImage_GetBits(src); // source pixels
+ BYTE *bdest = FreeImage_GetBits(dst); // destination pixels
+
+ // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
+ const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+ // for all image blocks of RBLOCK*RBLOCK pixels
+
+ // x-segment
+ for(unsigned xs = 0; xs < dst_width; xs += RBLOCK) {
+ // y-segment
+ for(unsigned ys = 0; ys < dst_height; ys += RBLOCK) {
+ for(unsigned x = xs; x < MIN(dst_width, xs + RBLOCK); x++) { // do rotation
+ x2 = dst_width - x - 1;
+ // point to src pixel at (ys, x2)
+ BYTE *src_bits = bsrc + (x2 * src_pitch) + (ys * bytespp);
+ // point to dst pixel at (x, ys)
+ BYTE *dst_bits = bdest + (ys * dst_pitch) + (x * bytespp);
+ for(unsigned y = ys; y < MIN(dst_height, ys + RBLOCK); y++) {
+ // dst.SetPixel(x, y, src.GetPixel(y, x2));
+ AssignPixel(dst_bits, src_bits, bytespp);
+ src_bits += bytespp;
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case FIT_UINT16:
+ case FIT_RGB16:
+ case FIT_RGBA16:
+ case FIT_FLOAT:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ {
+ BYTE *bsrc = FreeImage_GetBits(src); // source pixels
+ BYTE *bdest = FreeImage_GetBits(dst); // destination pixels
+
+ // calculate the number of bytes per pixel
+ const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+ for(unsigned y = 0; y < dst_height; y++) {
+ BYTE *src_bits = bsrc + (src_height - 1) * src_pitch + y * bytespp;
+ BYTE *dst_bits = bdest + (y * dst_pitch);
+ for(unsigned x = 0; x < dst_width; x++) {
+ AssignPixel(dst_bits, src_bits, bytespp);
+ src_bits -= src_pitch;
+ dst_bits += bytespp;
+ }
+ }
+ }
+ break;
+ }
+
+ return dst;
+}
+
+/**
+Rotates an image by a given degree in range [-45 .. +45] (counter clockwise)
+using the 3-shear technique.
+@param src Pointer to source image to rotate
+@param dAngle Rotation angle
+@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
+*/
+static FIBITMAP*
+Rotate45(FIBITMAP *src, double dAngle, const void *bkcolor) {
+ const double ROTATE_PI = double(3.1415926535897932384626433832795);
+
+ unsigned u;
+
+ const unsigned bpp = FreeImage_GetBPP(src);
+
+ const double dRadAngle = dAngle * ROTATE_PI / double(180); // Angle in radians
+ const double dSinE = sin(dRadAngle);
+ const double dTan = tan(dRadAngle / 2);
+
+ const unsigned src_width = FreeImage_GetWidth(src);
+ const unsigned src_height = FreeImage_GetHeight(src);
+
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+ // Calc first shear (horizontal) destination image dimensions
+ const unsigned width_1 = src_width + unsigned((double)src_height * fabs(dTan) + 0.5);
+ const unsigned height_1 = src_height;
+
+ // Perform 1st shear (horizontal)
+ // ----------------------------------------------------------------------
+
+ // Allocate image for 1st shear
+ FIBITMAP *dst1 = FreeImage_AllocateT(image_type, width_1, height_1, bpp);
+ if(NULL == dst1) {
+ return NULL;
+ }
+
+ for(u = 0; u < height_1; u++) {
+ double dShear;
+
+ if(dTan >= 0) {
+ // Positive angle
+ dShear = (u + 0.5) * dTan;
+ }
+ else {
+ // Negative angle
+ dShear = (double(u) - height_1 + 0.5) * dTan;
+ }
+ int iShear = int(floor(dShear));
+ HorizontalSkew(src, dst1, u, iShear, dShear - double(iShear), bkcolor);
+ }
+
+ // Perform 2nd shear (vertical)
+ // ----------------------------------------------------------------------
+
+ // Calc 2nd shear (vertical) destination image dimensions
+ const unsigned width_2 = width_1;
+ unsigned height_2 = unsigned((double)src_width * fabs(dSinE) + (double)src_height * cos(dRadAngle) + 0.5) + 1;
+
+ // Allocate image for 2nd shear
+ FIBITMAP *dst2 = FreeImage_AllocateT(image_type, width_2, height_2, bpp);
+ if(NULL == dst2) {
+ FreeImage_Unload(dst1);
+ return NULL;
+ }
+
+ double dOffset; // Variable skew offset
+ if(dSinE > 0) {
+ // Positive angle
+ dOffset = (src_width - 1.0) * dSinE;
+ }
+ else {
+ // Negative angle
+ dOffset = -dSinE * (double(src_width) - width_2);
+ }
+
+ for(u = 0; u < width_2; u++, dOffset -= dSinE) {
+ int iShear = int(floor(dOffset));
+ VerticalSkew(dst1, dst2, u, iShear, dOffset - double(iShear), bkcolor);
+ }
+
+ // Perform 3rd shear (horizontal)
+ // ----------------------------------------------------------------------
+
+ // Free result of 1st shear
+ FreeImage_Unload(dst1);
+
+ // Calc 3rd shear (horizontal) destination image dimensions
+ const unsigned width_3 = unsigned(double(src_height) * fabs(dSinE) + double(src_width) * cos(dRadAngle) + 0.5) + 1;
+ const unsigned height_3 = height_2;
+
+ // Allocate image for 3rd shear
+ FIBITMAP *dst3 = FreeImage_AllocateT(image_type, width_3, height_3, bpp);
+ if(NULL == dst3) {
+ FreeImage_Unload(dst2);
+ return NULL;
+ }
+
+ if(dSinE >= 0) {
+ // Positive angle
+ dOffset = (src_width - 1.0) * dSinE * -dTan;
+ }
+ else {
+ // Negative angle
+ dOffset = dTan * ( (src_width - 1.0) * -dSinE + (1.0 - height_3) );
+ }
+ for(u = 0; u < height_3; u++, dOffset += dTan) {
+ int iShear = int(floor(dOffset));
+ HorizontalSkew(dst2, dst3, u, iShear, dOffset - double(iShear), bkcolor);
+ }
+ // Free result of 2nd shear
+ FreeImage_Unload(dst2);
+
+ // Return result of 3rd shear
+ return dst3;
+}
+
+/**
+Rotates a 1-, 8-, 24- or 32-bit image by a given angle (given in degree).
+Angle is unlimited, except for 1-bit images (limited to integer multiples of 90 degree).
+3-shears technique is used.
+@param src Pointer to source image to rotate
+@param dAngle Rotation angle
+@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
+*/
+static FIBITMAP*
+RotateAny(FIBITMAP *src, double dAngle, const void *bkcolor) {
+ if(NULL == src) {
+ return NULL;
+ }
+
+ FIBITMAP *image = src;
+
+ while(dAngle >= 360) {
+ // Bring angle to range of (-INF .. 360)
+ dAngle -= 360;
+ }
+ while(dAngle < 0) {
+ // Bring angle to range of [0 .. 360)
+ dAngle += 360;
+ }
+ if((dAngle > 45) && (dAngle <= 135)) {
+ // Angle in (45 .. 135]
+ // Rotate image by 90 degrees into temporary image,
+ // so it requires only an extra rotation angle
+ // of -45 .. +45 to complete rotation.
+ image = Rotate90(src);
+ dAngle -= 90;
+ }
+ else if((dAngle > 135) && (dAngle <= 225)) {
+ // Angle in (135 .. 225]
+ // Rotate image by 180 degrees into temporary image,
+ // so it requires only an extra rotation angle
+ // of -45 .. +45 to complete rotation.
+ image = Rotate180(src);
+ dAngle -= 180;
+ }
+ else if((dAngle > 225) && (dAngle <= 315)) {
+ // Angle in (225 .. 315]
+ // Rotate image by 270 degrees into temporary image,
+ // so it requires only an extra rotation angle
+ // of -45 .. +45 to complete rotation.
+ image = Rotate270(src);
+ dAngle -= 270;
+ }
+
+ // If we got here, angle is in (-45 .. +45]
+
+ if(NULL == image) {
+ // Failed to allocate middle image
+ return NULL;
+ }
+
+ if(0 == dAngle) {
+ if(image == src) {
+ // Nothing to do ...
+ return FreeImage_Clone(src);
+ } else {
+ // No more rotation needed
+ return image;
+ }
+ }
+ else {
+ // Perform last rotation
+ FIBITMAP *dst = Rotate45(image, dAngle, bkcolor);
+
+ if(src != image) {
+ // Middle image was required, free it now.
+ FreeImage_Unload(image);
+ }
+
+ return dst;
+ }
+}
+
+// ==========================================================
+
+FIBITMAP *DLL_CALLCONV
+FreeImage_Rotate(FIBITMAP *dib, double angle, const void *bkcolor) {
+ if(!FreeImage_HasPixels(dib)) return NULL;
+
+ if(0 == angle) {
+ return FreeImage_Clone(dib);
+ }
+ // DIB are stored upside down ...
+ angle *= -1;
+
+ try {
+ unsigned bpp = FreeImage_GetBPP(dib);
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+ switch(image_type) {
+ case FIT_BITMAP:
+ if(bpp == 1) {
+ // only rotate for integer multiples of 90 degree
+ if(fmod(angle, 90) != 0)
+ return NULL;
+
+ // perform the rotation
+ FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
+ if(!dst) throw(1);
+
+ // build a greyscale palette
+ RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
+ if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) {
+ dst_pal[0].rgbRed = dst_pal[0].rgbGreen = dst_pal[0].rgbBlue = 0;
+ dst_pal[1].rgbRed = dst_pal[1].rgbGreen = dst_pal[1].rgbBlue = 255;
+ } else {
+ dst_pal[0].rgbRed = dst_pal[0].rgbGreen = dst_pal[0].rgbBlue = 255;
+ dst_pal[1].rgbRed = dst_pal[1].rgbGreen = dst_pal[1].rgbBlue = 0;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, dib);
+
+ return dst;
+ }
+ else if((bpp == 8) || (bpp == 24) || (bpp == 32)) {
+ FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
+ if(!dst) throw(1);
+
+ if(bpp == 8) {
+ // copy original palette to rotated bitmap
+ RGBQUAD *src_pal = FreeImage_GetPalette(dib);
+ RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
+ memcpy(&dst_pal[0], &src_pal[0], 256 * sizeof(RGBQUAD));
+
+ // copy transparency table
+ FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+
+ // copy background color
+ RGBQUAD bkcolor;
+ if( FreeImage_GetBackgroundColor(dib, &bkcolor) ) {
+ FreeImage_SetBackgroundColor(dst, &bkcolor);
+ }
+
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, dib);
+
+ return dst;
+ }
+ break;
+ case FIT_UINT16:
+ case FIT_RGB16:
+ case FIT_RGBA16:
+ case FIT_FLOAT:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ {
+ FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
+ if(!dst) throw(1);
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, dib);
+
+ return dst;
+ }
+ break;
+ }
+
+ } catch(int) {
+ return NULL;
+ }
+
+ return NULL;
+}
+
diff --git a/libs/freeimage/src/FreeImageToolkit/Colors.cpp b/libs/freeimage/src/FreeImageToolkit/Colors.cpp
new file mode 100644
index 0000000000..338e5a40bc
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/Colors.cpp
@@ -0,0 +1,966 @@
+// ==========================================================
+// Color manipulation routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Carsten Klein (c.klein@datagis.com)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// 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!
+// ==========================================================
+
+#include "../stdafx.h"
+
+// ----------------------------------------------------------
+// Macros + structures
+// ----------------------------------------------------------
+
+#define GET_HI_NIBBLE(byte) ((byte) >> 4)
+#define SET_HI_NIBBLE(byte, n) byte &= 0x0F, byte |= ((n) << 4)
+#define GET_LO_NIBBLE(byte) ((byte) & 0x0F)
+#define SET_LO_NIBBLE(byte, n) byte &= 0xF0, byte |= ((n) & 0x0F)
+#define GET_NIBBLE(cn, byte) ((cn) ? (GET_HI_NIBBLE(byte)) : (GET_LO_NIBBLE(byte)))
+#define SET_NIBBLE(cn, byte, n) if (cn) SET_HI_NIBBLE(byte, n); else SET_LO_NIBBLE(byte, n)
+
+// ----------------------------------------------------------
+
+
+/** @brief Inverts each pixel data.
+
+@param src Input image to be processed.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV
+FreeImage_Invert(FIBITMAP *src) {
+
+ if (!FreeImage_HasPixels(src)) return FALSE;
+
+ unsigned i, x, y, k;
+
+ const unsigned width = FreeImage_GetWidth(src);
+ const unsigned height = FreeImage_GetHeight(src);
+ const unsigned bpp = FreeImage_GetBPP(src);
+
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+ if(image_type == FIT_BITMAP) {
+ switch(bpp) {
+ case 1 :
+ case 4 :
+ case 8 :
+ {
+ // if the dib has a colormap, just invert it
+ // else, keep the linear grayscale
+
+ if (FreeImage_GetColorType(src) == FIC_PALETTE) {
+ RGBQUAD *pal = FreeImage_GetPalette(src);
+
+ for(i = 0; i < FreeImage_GetColorsUsed(src); i++) {
+ pal[i].rgbRed = 255 - pal[i].rgbRed;
+ pal[i].rgbGreen = 255 - pal[i].rgbGreen;
+ pal[i].rgbBlue = 255 - pal[i].rgbBlue;
+ }
+ } else {
+ for(y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(src, y);
+
+ for (x = 0; x < FreeImage_GetLine(src); x++) {
+ bits[x] = ~bits[x];
+ }
+ }
+ }
+
+ break;
+ }
+
+ case 24 :
+ case 32 :
+ {
+ // Calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit)
+ const unsigned bytespp = FreeImage_GetLine(src) / width;
+
+ for(y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < width; x++) {
+ for(k = 0; k < bytespp; k++) {
+ bits[k] = ~bits[k];
+ }
+ bits += bytespp;
+ }
+ }
+
+ break;
+ }
+ default:
+ return FALSE;
+ }
+ }
+ else if((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
+
+ for(y = 0; y < height; y++) {
+ WORD *bits = (WORD*)FreeImage_GetScanLine(src, y);
+ for(x = 0; x < width; x++) {
+ for(k = 0; k < wordspp; k++) {
+ bits[k] = ~bits[k];
+ }
+ bits += wordspp;
+ }
+ }
+ }
+ else {
+ // anything else ...
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/** @brief Perfoms an histogram transformation on a 8, 24 or 32-bit image
+according to the values of a lookup table (LUT).
+
+The transformation is done as follows.<br>
+Image 8-bit : if the image has a color palette, the LUT is applied to this palette,
+otherwise, it is applied to the grey values.<br>
+Image 24-bit & 32-bit : if channel == FICC_RGB, the same LUT is applied to each color
+plane (R,G, and B). Otherwise, the LUT is applied to the specified channel only.
+@param src Input image to be processed.
+@param LUT Lookup table. <b>The size of 'LUT' is assumed to be 256.</b>
+@param channel The color channel to be processed (only used with 24 & 32-bit DIB).
+@return Returns TRUE if successful, FALSE otherwise.
+@see FREE_IMAGE_COLOR_CHANNEL
+*/
+BOOL DLL_CALLCONV
+FreeImage_AdjustCurve(FIBITMAP *src, BYTE *LUT, FREE_IMAGE_COLOR_CHANNEL channel) {
+ unsigned x, y;
+ BYTE *bits = NULL;
+
+ if(!FreeImage_HasPixels(src) || !LUT || (FreeImage_GetImageType(src) != FIT_BITMAP))
+ return FALSE;
+
+ int bpp = FreeImage_GetBPP(src);
+ if((bpp != 8) && (bpp != 24) && (bpp != 32))
+ return FALSE;
+
+ // apply the LUT
+ switch(bpp) {
+
+ case 8 :
+ {
+ // if the dib has a colormap, apply the LUT to it
+ // else, apply the LUT to pixel values
+
+ if(FreeImage_GetColorType(src) == FIC_PALETTE) {
+ RGBQUAD *rgb = FreeImage_GetPalette(src);
+ for (unsigned pal = 0; pal < FreeImage_GetColorsUsed(src); pal++) {
+ rgb->rgbRed = LUT[rgb->rgbRed];
+ rgb->rgbGreen = LUT[rgb->rgbGreen];
+ rgb->rgbBlue = LUT[rgb->rgbBlue];
+ rgb++;
+ }
+ }
+ else {
+ for(y = 0; y < FreeImage_GetHeight(src); y++) {
+ bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < FreeImage_GetWidth(src); x++) {
+ bits[x] = LUT[ bits[x] ];
+ }
+ }
+ }
+
+ break;
+ }
+
+ case 24 :
+ case 32 :
+ {
+ int bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+ switch(channel) {
+ case FICC_RGB :
+ for(y = 0; y < FreeImage_GetHeight(src); y++) {
+ bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < FreeImage_GetWidth(src); x++) {
+ bits[FI_RGBA_BLUE] = LUT[ bits[FI_RGBA_BLUE] ]; // B
+ bits[FI_RGBA_GREEN] = LUT[ bits[FI_RGBA_GREEN] ]; // G
+ bits[FI_RGBA_RED] = LUT[ bits[FI_RGBA_RED] ]; // R
+
+ bits += bytespp;
+ }
+ }
+ break;
+
+ case FICC_BLUE :
+ for(y = 0; y < FreeImage_GetHeight(src); y++) {
+ bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < FreeImage_GetWidth(src); x++) {
+ bits[FI_RGBA_BLUE] = LUT[ bits[FI_RGBA_BLUE] ]; // B
+
+ bits += bytespp;
+ }
+ }
+ break;
+
+ case FICC_GREEN :
+ for(y = 0; y < FreeImage_GetHeight(src); y++) {
+ bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < FreeImage_GetWidth(src); x++) {
+ bits[FI_RGBA_GREEN] = LUT[ bits[FI_RGBA_GREEN] ]; // G
+
+ bits += bytespp;
+ }
+ }
+ break;
+
+ case FICC_RED :
+ for(y = 0; y < FreeImage_GetHeight(src); y++) {
+ bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < FreeImage_GetWidth(src); x++) {
+ bits[FI_RGBA_RED] = LUT[ bits[FI_RGBA_RED] ]; // R
+
+ bits += bytespp;
+ }
+ }
+ break;
+
+ case FICC_ALPHA :
+ if(32 == bpp) {
+ for(y = 0; y < FreeImage_GetHeight(src); y++) {
+ bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < FreeImage_GetWidth(src); x++) {
+ bits[FI_RGBA_ALPHA] = LUT[ bits[FI_RGBA_ALPHA] ]; // A
+
+ bits += bytespp;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+/** @brief Performs gamma correction on a 8, 24 or 32-bit image.
+
+@param src Input image to be processed.
+@param gamma Gamma value to use. A value of 1.0 leaves the image alone,
+less than one darkens it, and greater than one lightens it.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV
+FreeImage_AdjustGamma(FIBITMAP *src, double gamma) {
+ BYTE LUT[256]; // Lookup table
+
+ if(!FreeImage_HasPixels(src) || (gamma <= 0))
+ return FALSE;
+
+ // Build the lookup table
+
+ double exponent = 1 / gamma;
+ double v = 255.0 * (double)pow((double)255, -exponent);
+ for(int i = 0; i < 256; i++) {
+ double color = (double)pow((double)i, exponent) * v;
+ if(color > 255)
+ color = 255;
+ LUT[i] = (BYTE)floor(color + 0.5);
+ }
+
+ // Apply the gamma correction
+ return FreeImage_AdjustCurve(src, LUT, FICC_RGB);
+}
+
+/** @brief Adjusts the brightness of a 8, 24 or 32-bit image by a certain amount.
+
+@param src Input image to be processed.
+@param percentage Where -100 <= percentage <= 100<br>
+A value 0 means no change, less than 0 will make the image darker
+and greater than 0 will make the image brighter.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV
+FreeImage_AdjustBrightness(FIBITMAP *src, double percentage) {
+ BYTE LUT[256]; // Lookup table
+ double value;
+
+ if(!FreeImage_HasPixels(src))
+ return FALSE;
+
+ // Build the lookup table
+ const double scale = (100 + percentage) / 100;
+ for(int i = 0; i < 256; i++) {
+ value = i * scale;
+ value = MAX(0.0, MIN(value, 255.0));
+ LUT[i] = (BYTE)floor(value + 0.5);
+ }
+ return FreeImage_AdjustCurve(src, LUT, FICC_RGB);
+}
+
+/** @brief Adjusts the contrast of a 8, 24 or 32-bit image by a certain amount.
+
+@param src Input image to be processed.
+@param percentage Where -100 <= percentage <= 100<br>
+A value 0 means no change, less than 0 will decrease the contrast
+and greater than 0 will increase the contrast of the image.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV
+FreeImage_AdjustContrast(FIBITMAP *src, double percentage) {
+ BYTE LUT[256]; // Lookup table
+ double value;
+
+ if(!FreeImage_HasPixels(src))
+ return FALSE;
+
+ // Build the lookup table
+ const double scale = (100 + percentage) / 100;
+ for(int i = 0; i < 256; i++) {
+ value = 128 + (i - 128) * scale;
+ value = MAX(0.0, MIN(value, 255.0));
+ LUT[i] = (BYTE)floor(value + 0.5);
+ }
+ return FreeImage_AdjustCurve(src, LUT, FICC_RGB);
+}
+
+/** @brief Computes image histogram
+
+For 24-bit and 32-bit images, histogram can be computed from red, green, blue and
+black channels. For 8-bit images, histogram is computed from the black channel. Other
+bit depth is not supported (nothing is done).
+@param src Input image to be processed.
+@param histo Histogram array to fill. <b>The size of 'histo' is assumed to be 256.</b>
+@param channel Color channel to use
+@return Returns TRUE if succesful, returns FALSE if the image bit depth isn't supported.
+*/
+BOOL DLL_CALLCONV
+FreeImage_GetHistogram(FIBITMAP *src, DWORD *histo, FREE_IMAGE_COLOR_CHANNEL channel) {
+ BYTE pixel;
+ BYTE *bits = NULL;
+ unsigned x, y;
+
+ if(!FreeImage_HasPixels(src) || !histo) return FALSE;
+
+ unsigned width = FreeImage_GetWidth(src);
+ unsigned height = FreeImage_GetHeight(src);
+ unsigned bpp = FreeImage_GetBPP(src);
+
+ if(bpp == 8) {
+ // clear histogram array
+ memset(histo, 0, 256 * sizeof(DWORD));
+ // compute histogram for black channel
+ for(y = 0; y < height; y++) {
+ bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < width; x++) {
+ // get pixel value
+ pixel = bits[x];
+ histo[pixel]++;
+ }
+ }
+ return TRUE;
+ }
+ else if((bpp == 24) || (bpp == 32)) {
+ int bytespp = bpp / 8; // bytes / pixel
+
+ // clear histogram array
+ memset(histo, 0, 256 * sizeof(DWORD));
+
+ switch(channel) {
+ case FICC_RED:
+ // compute histogram for red channel
+ for(y = 0; y < height; y++) {
+ bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < width; x++) {
+ pixel = bits[FI_RGBA_RED]; // R
+ histo[pixel]++;
+ bits += bytespp;
+ }
+ }
+ return TRUE;
+
+ case FICC_GREEN:
+ // compute histogram for green channel
+ for(y = 0; y < height; y++) {
+ bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < width; x++) {
+ pixel = bits[FI_RGBA_GREEN]; // G
+ histo[pixel]++;
+ bits += bytespp;
+ }
+ }
+ return TRUE;
+
+ case FICC_BLUE:
+ // compute histogram for blue channel
+ for(y = 0; y < height; y++) {
+ bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < width; x++) {
+ pixel = bits[FI_RGBA_BLUE]; // B
+ histo[pixel]++;
+ bits += bytespp;
+ }
+ }
+ return TRUE;
+
+ case FICC_BLACK:
+ case FICC_RGB:
+ // compute histogram for black channel
+ for(y = 0; y < height; y++) {
+ bits = FreeImage_GetScanLine(src, y);
+ for(x = 0; x < width; x++) {
+ // RGB to GREY conversion
+ pixel = GREY(bits[FI_RGBA_RED], bits[FI_RGBA_GREEN], bits[FI_RGBA_BLUE]);
+ histo[pixel]++;
+ bits += bytespp;
+ }
+ }
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+// ----------------------------------------------------------
+
+
+/** @brief Creates a lookup table to be used with FreeImage_AdjustCurve() which
+ may adjust brightness and contrast, correct gamma and invert the image with a
+ single call to FreeImage_AdjustCurve().
+
+ This function creates a lookup table to be used with FreeImage_AdjustCurve()
+ which may adjust brightness and contrast, correct gamma and invert the image
+ with a single call to FreeImage_AdjustCurve(). If more than one of these image
+ display properties need to be adjusted, using a combined lookup table should be
+ preferred over calling each adjustment function separately. That's particularly
+ true for huge images or if performance is an issue. Then, the expensive process
+ of iterating over all pixels of an image is performed only once and not up to
+ four times.
+
+ Furthermore, the lookup table created does not depend on the order, in which
+ each single adjustment operation is performed. Due to rounding and byte casting
+ issues, it actually matters in which order individual adjustment operations
+ are performed. Both of the following snippets most likely produce different
+ results:
+
+ // snippet 1: contrast, brightness
+ FreeImage_AdjustContrast(dib, 15.0);
+ FreeImage_AdjustBrightness(dib, 50.0);
+
+ // snippet 2: brightness, contrast
+ FreeImage_AdjustBrightness(dib, 50.0);
+ FreeImage_AdjustContrast(dib, 15.0);
+
+ Better and even faster would be snippet 3:
+
+ // snippet 3:
+ BYTE LUT[256];
+ FreeImage_GetAdjustColorsLookupTable(LUT, 50.0, 15.0, 1.0, FALSE);
+ FreeImage_AdjustCurve(dib, LUT, FICC_RGB);
+
+ This function is also used internally by FreeImage_AdjustColors(), which does
+ not return the lookup table, but uses it to call FreeImage_AdjustCurve() on the
+ passed image.
+
+ @param LUT Output lookup table to be used with FreeImage_AdjustCurve(). <b>The
+ size of 'LUT' is assumed to be 256.</b>
+ @param brightness Percentage brightness value where -100 <= brightness <= 100<br>
+ A value of 0 means no change, less than 0 will make the image darker and greater
+ than 0 will make the image brighter.
+ @param contrast Percentage contrast value where -100 <= contrast <= 100<br>
+ A value of 0 means no change, less than 0 will decrease the contrast
+ and greater than 0 will increase the contrast of the image.
+ @param gamma Gamma value to be used for gamma correction. A value of 1.0 leaves
+ the image alone, less than one darkens it, and greater than one lightens it.
+ This parameter must not be zero or smaller than zero. If so, it will be ignored
+ and no gamma correction will be performed using the lookup table created.
+ @param invert If set to TRUE, the image will be inverted.
+ @return Returns the number of adjustments applied to the resulting lookup table
+ compared to a blind lookup table.
+ */
+int DLL_CALLCONV
+FreeImage_GetAdjustColorsLookupTable(BYTE *LUT, double brightness, double contrast, double gamma, BOOL invert) {
+ double dblLUT[256];
+ double value;
+ int result = 0;
+
+ if ((brightness == 0.0) && (contrast == 0.0) && (gamma == 1.0) && (!invert)) {
+ // nothing to do, if all arguments have their default values
+ // return a blind LUT
+ for (int i = 0; i < 256; i++) {
+ LUT[i] = (BYTE)i;
+ }
+ return 0;
+ }
+
+ // first, create a blind LUT, which does nothing to the image
+ for (int i = 0; i < 256; i++) {
+ dblLUT[i] = i;
+ }
+
+ if (contrast != 0.0) {
+ // modify lookup table with contrast adjustment data
+ const double v = (100.0 + contrast) / 100.0;
+ for (int i = 0; i < 256; i++) {
+ value = 128 + (dblLUT[i] - 128) * v;
+ dblLUT[i] = MAX(0.0, MIN(value, 255.0));
+ }
+ result++;
+ }
+
+ if (brightness != 0.0) {
+ // modify lookup table with brightness adjustment data
+ const double v = (100.0 + brightness) / 100.0;
+ for (int i = 0; i < 256; i++) {
+ value = dblLUT[i] * v;
+ dblLUT[i] = MAX(0.0, MIN(value, 255.0));
+ }
+ result++;
+ }
+
+ if ((gamma > 0) && (gamma != 1.0)) {
+ // modify lookup table with gamma adjustment data
+ double exponent = 1 / gamma;
+ const double v = 255.0 * (double)pow((double)255, -exponent);
+ for (int i = 0; i < 256; i++) {
+ value = pow(dblLUT[i], exponent) * v;
+ dblLUT[i] = MAX(0.0, MIN(value, 255.0));
+ }
+ result++;
+ }
+
+ if (!invert) {
+ for (int i = 0; i < 256; i++) {
+ LUT[i] = (BYTE)floor(dblLUT[i] + 0.5);
+ }
+ } else {
+ for (int i = 0; i < 256; i++) {
+ LUT[i] = 255 - (BYTE)floor(dblLUT[i] + 0.5);
+ }
+ result++;
+ }
+ // return the number of adjustments made
+ return result;
+}
+
+/** @brief Adjusts an image's brightness, contrast and gamma as well as it may
+ optionally invert the image within a single operation.
+
+ This function adjusts an image's brightness, contrast and gamma as well as it
+ may optionally invert the image within a single operation. If more than one of
+ these image display properties need to be adjusted, using this function should
+ be preferred over calling each adjustment function separately. That's
+ particularly true for huge images or if performance is an issue.
+
+ This function relies on FreeImage_GetAdjustColorsLookupTable(), which creates a
+ single lookup table, that combines all adjustment operations requested.
+
+ Furthermore, the lookup table created by FreeImage_GetAdjustColorsLookupTable()
+ does not depend on the order, in which each single adjustment operation is
+ performed. Due to rounding and byte casting issues, it actually matters in which
+ order individual adjustment operations are performed. Both of the following
+ snippets most likely produce different results:
+
+ // snippet 1: contrast, brightness
+ FreeImage_AdjustContrast(dib, 15.0);
+ FreeImage_AdjustBrightness(dib, 50.0);
+
+ // snippet 2: brightness, contrast
+ FreeImage_AdjustBrightness(dib, 50.0);
+ FreeImage_AdjustContrast(dib, 15.0);
+
+ Better and even faster would be snippet 3:
+
+ // snippet 3:
+ FreeImage_AdjustColors(dib, 50.0, 15.0, 1.0, FALSE);
+
+ @param dib Input/output image to be processed.
+ @param brightness Percentage brightness value where -100 <= brightness <= 100<br>
+ A value of 0 means no change, less than 0 will make the image darker and greater
+ than 0 will make the image brighter.
+ @param contrast Percentage contrast value where -100 <= contrast <= 100<br>
+ A value of 0 means no change, less than 0 will decrease the contrast
+ and greater than 0 will increase the contrast of the image.
+ @param gamma Gamma value to be used for gamma correction. A value of 1.0 leaves
+ the image alone, less than one darkens it, and greater than one lightens it.<br>
+ This parameter must not be zero or smaller than zero. If so, it will be ignored
+ and no gamma correction will be performed on the image.
+ @param invert If set to TRUE, the image will be inverted.
+ @return Returns TRUE on success, FALSE otherwise (e.g. when the bitdeph of the
+ source dib cannot be handled).
+ */
+BOOL DLL_CALLCONV
+FreeImage_AdjustColors(FIBITMAP *dib, double brightness, double contrast, double gamma, BOOL invert) {
+ BYTE LUT[256];
+
+ if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+ return FALSE;
+ }
+
+ int bpp = FreeImage_GetBPP(dib);
+ if ((bpp != 8) && (bpp != 24) && (bpp != 32)) {
+ return FALSE;
+ }
+
+ if (FreeImage_GetAdjustColorsLookupTable(LUT, brightness, contrast, gamma, invert)) {
+ return FreeImage_AdjustCurve(dib, LUT, FICC_RGB);
+ }
+ return FALSE;
+}
+
+/** @brief Applies color mapping for one or several colors on a 1-, 4- or 8-bit
+ palletized or a 16-, 24- or 32-bit high color image.
+
+ This function maps up to <i>count</i> colors specified in <i>srccolors</i> to
+ these specified in <i>dstcolors</i>. Thereby, color <i>srccolors[N]</i>,
+ if found in the image, will be replaced by color <i>dstcolors[N]</i>. If
+ parameter <i>swap</i> is TRUE, additionally all colors specified in
+ <i>dstcolors</i> are also mapped to these specified in <i>srccolors</i>. For
+ high color images, the actual image data will be modified whereas, for
+ palletized images only the palette will be changed.<br>
+
+ The function returns the number of pixels changed or zero, if no pixels were
+ changed.
+
+ Both arrays <i>srccolors</i> and <i>dstcolors</i> are assumed not to hold less
+ than <i>count</i> colors.<br>
+
+ For 16-bit images, all colors specified are transparently converted to their
+ proper 16-bit representation (either in RGB555 or RGB565 format, which is
+ determined by the image's red- green- and blue-mask).<br>
+
+ <b>Note, that this behaviour is different from what FreeImage_ApplyPaletteIndexMapping()
+ does, which modifies the actual image data on palletized images.</b>
+
+ @param dib Input/output image to be processed.
+ @param srccolors Array of colors to be used as the mapping source.
+ @param dstcolors Array of colors to be used as the mapping destination.
+ @param count The number of colors to be mapped. This is the size of both
+ <i>srccolors</i> and <i>dstcolors</i>.
+ @param ignore_alpha If TRUE, 32-bit images and colors are treated as 24-bit.
+ @param swap If TRUE, source and destination colors are swapped, that is,
+ each destination color is also mapped to the corresponding source color.
+ @return Returns the total number of pixels changed.
+ */
+unsigned DLL_CALLCONV
+FreeImage_ApplyColorMapping(FIBITMAP *dib, RGBQUAD *srccolors, RGBQUAD *dstcolors, unsigned count, BOOL ignore_alpha, BOOL swap) {
+ unsigned result = 0;
+
+ if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+ return 0;
+ }
+
+ // validate parameters
+ if ((!srccolors) || (!dstcolors)|| (count < 1)) {
+ return 0;
+ }
+
+ int bpp = FreeImage_GetBPP(dib);
+ switch (bpp) {
+ case 1:
+ case 4:
+ case 8: {
+ unsigned size = FreeImage_GetColorsUsed(dib);
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ RGBQUAD *a, *b;
+ for (unsigned x = 0; x < size; x++) {
+ for (unsigned j = 0; j < count; j++) {
+ a = srccolors;
+ b = dstcolors;
+ for (int i = (swap ? 0 : 1); i < 2; i++) {
+ if ((pal[x].rgbBlue == a[j].rgbBlue)&&(pal[x].rgbGreen == a[j].rgbGreen) &&(pal[x].rgbRed== a[j].rgbRed)) {
+ pal[x].rgbBlue = b[j].rgbBlue;
+ pal[x].rgbGreen = b[j].rgbGreen;
+ pal[x].rgbRed = b[j].rgbRed;
+ result++;
+ j = count;
+ break;
+ }
+ a = dstcolors;
+ b = srccolors;
+ }
+ }
+ }
+ return result;
+ }
+ case 16: {
+ WORD *src16 = (WORD *)malloc(sizeof(WORD) * count);
+ if (NULL == src16) {
+ return 0;
+ }
+
+ WORD *dst16 = (WORD *)malloc(sizeof(WORD) * count);
+ if (NULL == dst16) {
+ free(src16);
+ return 0;
+ }
+
+ for (unsigned j = 0; j < count; j++) {
+ src16[j] = RGBQUAD_TO_WORD(dib, (srccolors + j));
+ dst16[j] = RGBQUAD_TO_WORD(dib, (dstcolors + j));
+ }
+
+ unsigned height = FreeImage_GetHeight(dib);
+ unsigned width = FreeImage_GetWidth(dib);
+ WORD *a, *b;
+ for (unsigned y = 0; y < height; y++) {
+ WORD *bits = (WORD *)FreeImage_GetScanLine(dib, y);
+ for (unsigned x = 0; x < width; x++, bits++) {
+ for (unsigned j = 0; j < count; j++) {
+ a = src16;
+ b = dst16;
+ for (int i = (swap ? 0 : 1); i < 2; i++) {
+ if (*bits == a[j]) {
+ *bits = b[j];
+ result++;
+ j = count;
+ break;
+ }
+ a = dst16;
+ b = src16;
+ }
+ }
+ }
+ }
+ free(src16);
+ free(dst16);
+ return result;
+ }
+ case 24: {
+ unsigned height = FreeImage_GetHeight(dib);
+ unsigned width = FreeImage_GetWidth(dib);
+ RGBQUAD *a, *b;
+ for (unsigned y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+ for (unsigned x = 0; x < width; x++, bits += 3) {
+ for (unsigned j = 0; j < count; j++) {
+ a = srccolors;
+ b = dstcolors;
+ for (int i = (swap ? 0 : 1); i < 2; i++) {
+ if ((bits[FI_RGBA_BLUE] == a[j].rgbBlue) && (bits[FI_RGBA_GREEN] == a[j].rgbGreen) &&(bits[FI_RGBA_RED] == a[j].rgbRed)) {
+ bits[FI_RGBA_BLUE] = b[j].rgbBlue;
+ bits[FI_RGBA_GREEN] = b[j].rgbGreen;
+ bits[FI_RGBA_RED] = b[j].rgbRed;
+ result++;
+ j = count;
+ break;
+ }
+ a = dstcolors;
+ b = srccolors;
+ }
+ }
+ }
+ }
+ return result;
+ }
+ case 32: {
+ unsigned height = FreeImage_GetHeight(dib);
+ unsigned width = FreeImage_GetWidth(dib);
+ RGBQUAD *a, *b;
+ for (unsigned y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+ for (unsigned x = 0; x < width; x++, bits += 4) {
+ for (unsigned j = 0; j < count; j++) {
+ a = srccolors;
+ b = dstcolors;
+ for (int i = (swap ? 0 : 1); i < 2; i++) {
+ if ((bits[FI_RGBA_BLUE] == a[j].rgbBlue) &&(bits[FI_RGBA_GREEN] == a[j].rgbGreen) &&(bits[FI_RGBA_RED] == a[j].rgbRed)
+ &&((ignore_alpha) || (bits[FI_RGBA_ALPHA] == a[j].rgbReserved))) {
+ bits[FI_RGBA_BLUE] = b[j].rgbBlue;
+ bits[FI_RGBA_GREEN] = b[j].rgbGreen;
+ bits[FI_RGBA_RED] = b[j].rgbRed;
+ if (!ignore_alpha) {
+ bits[FI_RGBA_ALPHA] = b[j].rgbReserved;
+ }
+ result++;
+ j = count;
+ break;
+ }
+ a = dstcolors;
+ b = srccolors;
+ }
+ }
+ }
+ }
+ return result;
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
+/** @brief Swaps two specified colors on a 1-, 4- or 8-bit palletized
+ or a 16-, 24- or 32-bit high color image.
+
+ This function swaps the two specified colors <i>color_a</i> and <i>color_b</i>
+ on a palletized or high color image. For high color images, the actual image
+ data will be modified whereas, for palletized images only the palette will be
+ changed.<br>
+
+ <b>Note, that this behaviour is different from what FreeImage_SwapPaletteIndices()
+ does, which modifies the actual image data on palletized images.</b><br>
+
+ This is just a thin wrapper for FreeImage_ApplyColorMapping() and resolves to:<br>
+ <i>return FreeImage_ApplyColorMapping(dib, color_a, color_b, 1, ignore_alpha, TRUE);</i>
+
+ @param dib Input/output image to be processed.
+ @param color_a On of the two colors to be swapped.
+ @param color_b The other of the two colors to be swapped.
+ @param ignore_alpha If TRUE, 32-bit images and colors are treated as 24-bit.
+ @return Returns the total number of pixels changed.
+ */
+unsigned DLL_CALLCONV
+FreeImage_SwapColors(FIBITMAP *dib, RGBQUAD *color_a, RGBQUAD *color_b, BOOL ignore_alpha) {
+ return FreeImage_ApplyColorMapping(dib, color_a, color_b, 1, ignore_alpha, TRUE);
+}
+
+/** @brief Applies palette index mapping for one or several indices on a 1-, 4-
+ or 8-bit palletized image.
+
+ This function maps up to <i>count</i> palette indices specified in
+ <i>srcindices</i> to these specified in <i>dstindices</i>. Thereby, index
+ <i>srcindices[N]</i>, if present in the image, will be replaced by index
+ <i>dstindices[N]</i>. If parameter <i>swap</i> is TRUE, additionally all indices
+ specified in <i>dstindices</i> are also mapped to these specified in
+ <i>srcindices</i>.<br>
+
+ The function returns the number of pixels changed or zero, if no pixels were
+ changed.
+
+ Both arrays <i>srcindices</i> and <i>dstindices</i> are assumed not to hold less
+ than <i>count</i> indices.<br>
+
+ <b>Note, that this behaviour is different from what FreeImage_ApplyColorMapping()
+ does, which modifies the actual image data on palletized images.</b>
+
+ @param dib Input/output image to be processed.
+ @param srcindices Array of palette indices to be used as the mapping source.
+ @param dstindices Array of palette indices to be used as the mapping destination.
+ @param count The number of palette indices to be mapped. This is the size of both
+ <i>srcindices</i> and <i>dstindices</i>.
+ @param swap If TRUE, source and destination palette indices are swapped, that is,
+ each destination index is also mapped to the corresponding source index.
+ @return Returns the total number of pixels changed.
+ */
+unsigned DLL_CALLCONV
+FreeImage_ApplyPaletteIndexMapping(FIBITMAP *dib, BYTE *srcindices, BYTE *dstindices, unsigned count, BOOL swap) {
+ unsigned result = 0;
+
+ if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+ return 0;
+ }
+
+ // validate parameters
+ if ((!srcindices) || (!dstindices)|| (count < 1)) {
+ return 0;
+ }
+
+ unsigned height = FreeImage_GetHeight(dib);
+ unsigned width = FreeImage_GetLine(dib);
+ BYTE *a, *b;
+
+ int bpp = FreeImage_GetBPP(dib);
+ switch (bpp) {
+ case 1: {
+
+ return result;
+ }
+ case 4: {
+ int skip_last = (FreeImage_GetWidth(dib) & 0x01);
+ unsigned max_x = width - 1;
+ for (unsigned y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+ for (unsigned x = 0; x < width; x++) {
+ int start = ((skip_last) && (x == max_x)) ? 1 : 0;
+ for (int cn = start; cn < 2; cn++) {
+ for (unsigned j = 0; j < count; j++) {
+ a = srcindices;
+ b = dstindices;
+ for (int i = ((swap) ? 0 : 1); i < 2; i++) {
+ if (GET_NIBBLE(cn, bits[x]) == (a[j] & 0x0F)) {
+ SET_NIBBLE(cn, bits[x], b[j]);
+ result++;
+ j = count;
+ break;
+ }
+ a = dstindices;
+ b = srcindices;
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+ case 8: {
+ for (unsigned y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+ for (unsigned x = 0; x < width; x++) {
+ for (unsigned j = 0; j < count; j++) {
+ a = srcindices;
+ b = dstindices;
+ for (int i = ((swap) ? 0 : 1); i < 2; i++) {
+ if (bits[x] == a[j]) {
+ bits[x] = b[j];
+ result++;
+ j = count;
+ break;
+ }
+ a = dstindices;
+ b = srcindices;
+ }
+ }
+ }
+ }
+ return result;
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
+/** @brief Swaps two specified palette indices on a 1-, 4- or 8-bit palletized
+ image.
+
+ This function swaps the two specified palette indices <i>index_a</i> and
+ <i>index_b</i> on a palletized image. Therefore, not the palette, but the
+ actual image data will be modified.<br>
+
+ <b>Note, that this behaviour is different from what FreeImage_SwapColors() does
+ on palletized images, which only swaps the colors in the palette.</b><br>
+
+ This is just a thin wrapper for FreeImage_ApplyColorMapping() and resolves to:<br>
+ <i>return FreeImage_ApplyPaletteIndexMapping(dib, index_a, index_b, 1, TRUE);</i>
+
+ @param dib Input/output image to be processed.
+ @param index_a On of the two palette indices to be swapped.
+ @param index_b The other of the two palette indices to be swapped.
+ @return Returns the total number of pixels changed.
+ */
+unsigned DLL_CALLCONV
+FreeImage_SwapPaletteIndices(FIBITMAP *dib, BYTE *index_a, BYTE *index_b) {
+ return FreeImage_ApplyPaletteIndexMapping(dib, index_a, index_b, 1, TRUE);
+}
+
diff --git a/libs/freeimage/src/FreeImageToolkit/CopyPaste.cpp b/libs/freeimage/src/FreeImageToolkit/CopyPaste.cpp
new file mode 100644
index 0000000000..6b23acd995
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/CopyPaste.cpp
@@ -0,0 +1,860 @@
+// ==========================================================
+// Copy / paste routines
+//
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Alexander Dymerets (sashad@te.net.ua)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Manfred Tausch (manfred.tausch@t-online.de)
+// - Riley McNiff (rmcniff@marexgroup.com)
+// - Carsten Klein (cklein05@users.sourceforge.net)
+//
+// 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!
+// ==========================================================
+
+#include "../stdafx.h"
+
+// ----------------------------------------------------------
+// Helpers
+// ----------------------------------------------------------
+
+/////////////////////////////////////////////////////////////
+// Alpha blending / combine functions
+
+// ----------------------------------------------------------
+/// 1-bit
+static BOOL Combine1(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 4-bit
+static BOOL Combine4(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 8-bit
+static BOOL Combine8(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 16-bit 555
+static BOOL Combine16_555(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 16-bit 565
+static BOOL Combine16_565(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 24-bit
+static BOOL Combine24(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+/// 32- bit
+static BOOL Combine32(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha);
+// ----------------------------------------------------------
+
+// ----------------------------------------------------------
+// 1-bit
+// ----------------------------------------------------------
+
+static BOOL
+Combine1(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+ BOOL value;
+
+ // check the bit depth of src and dst images
+ if((FreeImage_GetBPP(dst_dib) != 1) || (FreeImage_GetBPP(src_dib) != 1)) {
+ return FALSE;
+ }
+
+ // check the size of src image
+ if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+ return FALSE;
+ }
+
+ BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib));
+ BYTE *src_bits = FreeImage_GetBits(src_dib);
+
+ // combine images
+ for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+ for(unsigned cols = 0; cols < FreeImage_GetWidth(src_dib); cols++) {
+ // get bit at (rows, cols) in src image
+ value = (src_bits[cols >> 3] & (0x80 >> (cols & 0x07))) != 0;
+ // set bit at (rows, x+cols) in dst image
+ value ? dst_bits[(x + cols) >> 3] |= (0x80 >> ((x + cols) & 0x7)) : dst_bits[(x + cols) >> 3] &= (0xFF7F >> ((x + cols) & 0x7));
+ }
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+// 4-bit
+// ----------------------------------------------------------
+
+static BOOL
+Combine4(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+ int swapTable[16];
+ BOOL bOddStart, bOddEnd;
+
+ // check the bit depth of src and dst images
+ if((FreeImage_GetBPP(dst_dib) != 4) || (FreeImage_GetBPP(src_dib) != 4)) {
+ return FALSE;
+ }
+
+ // check the size of src image
+ if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+ return FALSE;
+ }
+
+ // get src and dst palettes
+ RGBQUAD *src_pal = FreeImage_GetPalette(src_dib);
+ RGBQUAD *dst_pal = FreeImage_GetPalette(dst_dib);
+ if (src_pal == NULL || dst_pal == NULL) {
+ return FALSE;
+ }
+
+ // build a swap table for the closest color match from the source palette to the destination palette
+
+ for (int i = 0; i < 16; i++) {
+ WORD min_diff = (WORD)-1;
+
+ for (int j = 0; j < 16; j++) {
+ // calculates the color difference using a Manhattan distance
+ WORD abs_diff = (WORD)(
+ abs(src_pal[i].rgbBlue - dst_pal[j].rgbBlue)
+ + abs(src_pal[i].rgbGreen - dst_pal[j].rgbGreen)
+ + abs(src_pal[i].rgbRed - dst_pal[j].rgbRed)
+ );
+
+ if (abs_diff < min_diff) {
+ swapTable[i] = j;
+ min_diff = abs_diff;
+ if (abs_diff == 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib)) + (x >> 1);
+ BYTE *src_bits = FreeImage_GetBits(src_dib);
+
+ // combine images
+
+ // allocate space for our temporary row
+ unsigned src_line = FreeImage_GetLine(src_dib);
+ unsigned src_width = FreeImage_GetWidth(src_dib);
+ unsigned src_height = FreeImage_GetHeight(src_dib);
+
+ BYTE *buffer = (BYTE *)malloc(src_line * sizeof(BYTE));
+ if (buffer == NULL) {
+ return FALSE;
+ }
+
+ bOddStart = (x & 0x01) ? TRUE : FALSE;
+
+ if ((bOddStart && !(src_width & 0x01)) || (!bOddStart && (src_width & 0x01))) {
+ bOddEnd = TRUE;
+ }
+ else {
+ bOddEnd = FALSE;
+ }
+
+ for(unsigned rows = 0; rows < src_height; rows++) {
+ memcpy(buffer, src_bits, src_line);
+
+ // change the values in the temp row to be those from the swap table
+
+ for (unsigned cols = 0; cols < src_line; cols++) {
+ buffer[cols] = (BYTE)((swapTable[HINIBBLE(buffer[cols]) >> 4] << 4) + swapTable[LOWNIBBLE(buffer[cols])]);
+ }
+
+ if (bOddStart) {
+ buffer[0] = HINIBBLE(dst_bits[0]) + LOWNIBBLE(buffer[0]);
+ }
+
+ if (bOddEnd) {
+ buffer[src_line - 1] = HINIBBLE(buffer[src_line - 1]) + LOWNIBBLE(dst_bits[src_line - 1]);
+ }
+
+ memcpy(dst_bits, buffer, src_line);
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+
+ free(buffer);
+
+ return TRUE;
+
+}
+
+// ----------------------------------------------------------
+// 8-bit
+// ----------------------------------------------------------
+
+static BOOL
+Combine8(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+ // check the bit depth of src and dst images
+ if((FreeImage_GetBPP(dst_dib) != 8) || (FreeImage_GetBPP(src_dib) != 8)) {
+ return FALSE;
+ }
+
+ // check the size of src image
+ if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+ return FALSE;
+ }
+
+ BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib)) + (x);
+ BYTE *src_bits = FreeImage_GetBits(src_dib);
+
+ if(alpha > 255) {
+ // combine images
+ for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+ memcpy(dst_bits, src_bits, FreeImage_GetLine(src_dib));
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+ } else {
+ // alpha blend images
+ for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+ for (unsigned cols = 0; cols < FreeImage_GetLine(src_dib); cols++) {
+ dst_bits[cols] = (BYTE)(((src_bits[cols] - dst_bits[cols]) * alpha + (dst_bits[cols] << 8)) >> 8);
+ }
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+// 16-bit
+// ----------------------------------------------------------
+
+static BOOL
+Combine16_555(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+ // check the bit depth of src and dst images
+ if((FreeImage_GetBPP(dst_dib) != 16) || (FreeImage_GetBPP(src_dib) != 16)) {
+ return FALSE;
+ }
+
+ // check the size of src image
+ if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+ return FALSE;
+ }
+
+ BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib)) + (x * 2);
+ BYTE *src_bits = FreeImage_GetBits(src_dib);
+
+ if (alpha > 255) {
+ for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+ memcpy(dst_bits, src_bits, FreeImage_GetLine(src_dib));
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+ } else {
+ for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+ for(unsigned cols = 0; cols < FreeImage_GetLine(src_dib); cols += 2) {
+ RGBTRIPLE color_s;
+ RGBTRIPLE color_t;
+
+ WORD *tmp1 = (WORD *)&dst_bits[cols];
+ WORD *tmp2 = (WORD *)&src_bits[cols];
+
+ // convert 16-bit colors to 24-bit
+
+ color_s.rgbtRed = (BYTE)(((*tmp1 & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) << 3);
+ color_s.rgbtGreen = (BYTE)(((*tmp1 & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) << 3);
+ color_s.rgbtBlue = (BYTE)(((*tmp1 & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) << 3);
+
+ color_t.rgbtRed = (BYTE)(((*tmp2 & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) << 3);
+ color_t.rgbtGreen = (BYTE)(((*tmp2 & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) << 3);
+ color_t.rgbtBlue = (BYTE)(((*tmp2 & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) << 3);
+
+ // alpha blend
+
+ color_s.rgbtRed = (BYTE)(((color_t.rgbtRed - color_s.rgbtRed) * alpha + (color_s.rgbtRed << 8)) >> 8);
+ color_s.rgbtGreen = (BYTE)(((color_t.rgbtGreen - color_s.rgbtGreen) * alpha + (color_s.rgbtGreen << 8)) >> 8);
+ color_s.rgbtBlue = (BYTE)(((color_t.rgbtBlue - color_s.rgbtBlue) * alpha + (color_s.rgbtBlue << 8)) >> 8);
+
+ // convert 24-bit color back to 16-bit
+
+ *tmp1 = RGB555(color_s.rgbtRed, color_s.rgbtGreen, color_s.rgbtBlue);
+ }
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+ }
+
+ return TRUE;
+}
+
+static BOOL
+Combine16_565(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+ // check the bit depth of src and dst images
+ if((FreeImage_GetBPP(dst_dib) != 16) || (FreeImage_GetBPP(src_dib) != 16)) {
+ return FALSE;
+ }
+
+ // check the size of src image
+ if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+ return FALSE;
+ }
+
+ BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib)) + (x * 2);
+ BYTE *src_bits = FreeImage_GetBits(src_dib);
+
+ if (alpha > 255) {
+ for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+ memcpy(dst_bits, src_bits, FreeImage_GetLine(src_dib));
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+ } else {
+ for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+ for(unsigned cols = 0; cols < FreeImage_GetLine(src_dib); cols += 2) {
+ RGBTRIPLE color_s;
+ RGBTRIPLE color_t;
+
+ WORD *tmp1 = (WORD *)&dst_bits[cols];
+ WORD *tmp2 = (WORD *)&src_bits[cols];
+
+ // convert 16-bit colors to 24-bit
+
+ color_s.rgbtRed = (BYTE)(((*tmp1 & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) << 3);
+ color_s.rgbtGreen = (BYTE)(((*tmp1 & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) << 2);
+ color_s.rgbtBlue = (BYTE)(((*tmp1 & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) << 3);
+
+ color_t.rgbtRed = (BYTE)(((*tmp2 & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) << 3);
+ color_t.rgbtGreen = (BYTE)(((*tmp2 & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) << 2);
+ color_t.rgbtBlue = (BYTE)(((*tmp2 & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) << 3);
+
+ // alpha blend
+
+ color_s.rgbtRed = (BYTE)(((color_t.rgbtRed - color_s.rgbtRed) * alpha + (color_s.rgbtRed << 8)) >> 8);
+ color_s.rgbtGreen = (BYTE)(((color_t.rgbtGreen - color_s.rgbtGreen) * alpha + (color_s.rgbtGreen << 8)) >> 8);
+ color_s.rgbtBlue = (BYTE)(((color_t.rgbtBlue - color_s.rgbtBlue) * alpha + (color_s.rgbtBlue << 8)) >> 8);
+
+ // convert 24-bit color back to 16-bit
+
+ *tmp1 = RGB565(color_s.rgbtRed, color_s.rgbtGreen, color_s.rgbtBlue);
+ }
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+// 24-bit
+// ----------------------------------------------------------
+
+static BOOL
+Combine24(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+ // check the bit depth of src and dst images
+ if((FreeImage_GetBPP(dst_dib) != 24) || (FreeImage_GetBPP(src_dib) != 24)) {
+ return FALSE;
+ }
+
+ // check the size of src image
+ if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+ return FALSE;
+ }
+
+ BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib)) + (x * 3);
+ BYTE *src_bits = FreeImage_GetBits(src_dib);
+
+ if(alpha > 255) {
+ // combine images
+ for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+ memcpy(dst_bits, src_bits, FreeImage_GetLine(src_dib));
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+ } else {
+ // alpha blend images
+ for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+ for (unsigned cols = 0; cols < FreeImage_GetLine(src_dib); cols++) {
+ dst_bits[cols] = (BYTE)(((src_bits[cols] - dst_bits[cols]) * alpha + (dst_bits[cols] << 8)) >> 8);
+ }
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+// 32-bit
+// ----------------------------------------------------------
+
+static BOOL
+Combine32(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) {
+ // check the bit depth of src and dst images
+ if((FreeImage_GetBPP(dst_dib) != 32) || (FreeImage_GetBPP(src_dib) != 32)) {
+ return FALSE;
+ }
+
+ // check the size of src image
+ if((x + FreeImage_GetWidth(src_dib) > FreeImage_GetWidth(dst_dib)) || (y + FreeImage_GetHeight(src_dib) > FreeImage_GetHeight(dst_dib))) {
+ return FALSE;
+ }
+
+ BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((FreeImage_GetHeight(dst_dib) - FreeImage_GetHeight(src_dib) - y) * FreeImage_GetPitch(dst_dib)) + (x * 4);
+ BYTE *src_bits = FreeImage_GetBits(src_dib);
+
+ if (alpha > 255) {
+ // combine images
+ for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+ memcpy(dst_bits, src_bits, FreeImage_GetLine(src_dib));
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+ } else {
+ // alpha blend images
+ for(unsigned rows = 0; rows < FreeImage_GetHeight(src_dib); rows++) {
+ for(unsigned cols = 0; cols < FreeImage_GetLine(src_dib); cols++) {
+ dst_bits[cols] = (BYTE)(((src_bits[cols] - dst_bits[cols]) * alpha + (dst_bits[cols] << 8)) >> 8);
+ }
+
+ dst_bits += FreeImage_GetPitch(dst_dib);
+ src_bits += FreeImage_GetPitch(src_dib);
+ }
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+// Any type other than FIBITMAP
+// ----------------------------------------------------------
+
+static BOOL
+CombineSameType(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y) {
+ // check the bit depth of src and dst images
+ if(FreeImage_GetImageType(dst_dib) != FreeImage_GetImageType(src_dib)) {
+ return FALSE;
+ }
+
+ unsigned src_width = FreeImage_GetWidth(src_dib);
+ unsigned src_height = FreeImage_GetHeight(src_dib);
+ unsigned src_pitch = FreeImage_GetPitch(src_dib);
+ unsigned src_line = FreeImage_GetLine(src_dib);
+ unsigned dst_width = FreeImage_GetWidth(dst_dib);
+ unsigned dst_height = FreeImage_GetHeight(dst_dib);
+ unsigned dst_pitch = FreeImage_GetPitch(dst_dib);
+
+ // check the size of src image
+ if((x + src_width > dst_width) || (y + src_height > dst_height)) {
+ return FALSE;
+ }
+
+ BYTE *dst_bits = FreeImage_GetBits(dst_dib) + ((dst_height - src_height - y) * dst_pitch) + (x * (src_line / src_width));
+ BYTE *src_bits = FreeImage_GetBits(src_dib);
+
+ // combine images
+ for(unsigned rows = 0; rows < src_height; rows++) {
+ memcpy(dst_bits, src_bits, src_line);
+
+ dst_bits += dst_pitch;
+ src_bits += src_pitch;
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+// FreeImage interface
+// ----------------------------------------------------------
+
+/**
+Copy a sub part of the current image and returns it as a FIBITMAP*.
+Works with any bitmap type.
+@param left Specifies the left position of the cropped rectangle.
+@param top Specifies the top position of the cropped rectangle.
+@param right Specifies the right position of the cropped rectangle.
+@param bottom Specifies the bottom position of the cropped rectangle.
+@return Returns the subimage if successful, NULL otherwise.
+*/
+FIBITMAP * DLL_CALLCONV
+FreeImage_Copy(FIBITMAP *src, int left, int top, int right, int bottom) {
+
+ if(!FreeImage_HasPixels(src))
+ return NULL;
+
+ // normalize the rectangle
+ if(right < left) {
+ INPLACESWAP(left, right);
+ }
+ if(bottom < top) {
+ INPLACESWAP(top, bottom);
+ }
+ // check the size of the sub image
+ int src_width = FreeImage_GetWidth(src);
+ int src_height = FreeImage_GetHeight(src);
+ if((left < 0) || (right > src_width) || (top < 0) || (bottom > src_height)) {
+ return NULL;
+ }
+
+ // allocate the sub image
+ unsigned bpp = FreeImage_GetBPP(src);
+ int dst_width = (right - left);
+ int dst_height = (bottom - top);
+
+ FIBITMAP *dst =
+ FreeImage_AllocateT(FreeImage_GetImageType(src),
+ dst_width,
+ dst_height,
+ bpp,
+ FreeImage_GetRedMask(src), FreeImage_GetGreenMask(src), FreeImage_GetBlueMask(src));
+
+ if(NULL == dst) return NULL;
+
+ // get the dimensions
+ int dst_line = FreeImage_GetLine(dst);
+ int dst_pitch = FreeImage_GetPitch(dst);
+ int src_pitch = FreeImage_GetPitch(src);
+
+ // get the pointers to the bits and such
+
+ BYTE *src_bits = FreeImage_GetScanLine(src, src_height - top - dst_height);
+ switch(bpp) {
+ case 1:
+ // point to x = 0
+ break;
+
+ case 4:
+ // point to x = 0
+ break;
+
+ default:
+ {
+ // calculate the number of bytes per pixel
+ unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+ // point to x = left
+ src_bits += left * bytespp;
+ }
+ break;
+ }
+
+ // point to x = 0
+ BYTE *dst_bits = FreeImage_GetBits(dst);
+
+ // copy the palette
+
+ memcpy(FreeImage_GetPalette(dst), FreeImage_GetPalette(src), FreeImage_GetColorsUsed(src) * sizeof(RGBQUAD));
+
+ // copy the bits
+ if(bpp == 1) {
+ BOOL value;
+ unsigned y_src, y_dst;
+
+ for(int y = 0; y < dst_height; y++) {
+ y_src = y * src_pitch;
+ y_dst = y * dst_pitch;
+ for(int x = 0; x < dst_width; x++) {
+ // get bit at (y, x) in src image
+ value = (src_bits[y_src + ((left+x) >> 3)] & (0x80 >> ((left+x) & 0x07))) != 0;
+ // set bit at (y, x) in dst image
+ value ? dst_bits[y_dst + (x >> 3)] |= (0x80 >> (x & 0x7)) : dst_bits[y_dst + (x >> 3)] &= (0xff7f >> (x & 0x7));
+ }
+ }
+ }
+
+ else if(bpp == 4) {
+ BYTE shift, value;
+ unsigned y_src, y_dst;
+
+ for(int y = 0; y < dst_height; y++) {
+ y_src = y * src_pitch;
+ y_dst = y * dst_pitch;
+ for(int x = 0; x < dst_width; x++) {
+ // get nibble at (y, x) in src image
+ shift = (BYTE)((1 - (left+x) % 2) << 2);
+ value = (src_bits[y_src + ((left+x) >> 1)] & (0x0F << shift)) >> shift;
+ // set nibble at (y, x) in dst image
+ shift = (BYTE)((1 - x % 2) << 2);
+ dst_bits[y_dst + (x >> 1)] &= ~(0x0F << shift);
+ dst_bits[y_dst + (x >> 1)] |= ((value & 0x0F) << shift);
+ }
+ }
+ }
+
+ else if(bpp >= 8) {
+ for(int y = 0; y < dst_height; y++) {
+ memcpy(dst_bits + (y * dst_pitch), src_bits + (y * src_pitch), dst_line);
+ }
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ // copy transparency table
+ FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(src), FreeImage_GetTransparencyCount(src));
+
+ // copy background color
+ RGBQUAD bkcolor;
+ if( FreeImage_GetBackgroundColor(src, &bkcolor) ) {
+ FreeImage_SetBackgroundColor(dst, &bkcolor);
+ }
+
+ // clone resolution
+ FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(src));
+ FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(src));
+
+ // clone ICC profile
+ FIICCPROFILE *src_profile = FreeImage_GetICCProfile(src);
+ FIICCPROFILE *dst_profile = FreeImage_CreateICCProfile(dst, src_profile->data, src_profile->size);
+ dst_profile->flags = src_profile->flags;
+
+ return dst;
+}
+
+/**
+Alpha blend or combine a sub part image with the current image.
+The bit depth of dst bitmap must be greater than or equal to the bit depth of src.
+Upper promotion of src is done internally. Supported bit depth equals to 1, 4, 8, 16, 24 or 32.
+@param src Source subimage
+@param left Specifies the left position of the sub image.
+@param top Specifies the top position of the sub image.
+@param alpha Alpha blend factor. The source and destination images are alpha blended if
+alpha = 0..255. If alpha > 255, then the source image is combined to the destination image.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV
+FreeImage_Paste(FIBITMAP *dst, FIBITMAP *src, int left, int top, int alpha) {
+ BOOL bResult = FALSE;
+
+ if(!FreeImage_HasPixels(src) || !FreeImage_HasPixels(dst)) return FALSE;
+
+ // check the size of src image
+ if((left < 0) || (top < 0)) {
+ return FALSE;
+ }
+ if((left + FreeImage_GetWidth(src) > FreeImage_GetWidth(dst)) || (top + FreeImage_GetHeight(src) > FreeImage_GetHeight(dst))) {
+ return FALSE;
+ }
+
+ // check data type
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dst);
+ if(image_type != FreeImage_GetImageType(src)) {
+ // no conversion between data type is done
+ return FALSE;
+ }
+
+ if(image_type == FIT_BITMAP) {
+ FIBITMAP *clone = NULL;
+
+ // check the bit depth of src and dst images
+ unsigned bpp_src = FreeImage_GetBPP(src);
+ unsigned bpp_dst = FreeImage_GetBPP(dst);
+ BOOL isRGB565 = FALSE;
+
+ if ((FreeImage_GetRedMask(dst) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dst) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dst) == FI16_565_BLUE_MASK)) {
+ isRGB565 = TRUE;
+ } else {
+ // includes case where all the masks are 0
+ isRGB565 = FALSE;
+ }
+
+ // perform promotion if needed
+ if(bpp_dst == bpp_src) {
+ clone = src;
+ } else if(bpp_dst > bpp_src) {
+ // perform promotion
+ switch(bpp_dst) {
+ case 4:
+ clone = FreeImage_ConvertTo4Bits(src);
+ break;
+ case 8:
+ clone = FreeImage_ConvertTo8Bits(src);
+ break;
+ case 16:
+ if (isRGB565) {
+ clone = FreeImage_ConvertTo16Bits565(src);
+ } else {
+ // includes case where all the masks are 0
+ clone = FreeImage_ConvertTo16Bits555(src);
+ }
+ break;
+ case 24:
+ clone = FreeImage_ConvertTo24Bits(src);
+ break;
+ case 32:
+ clone = FreeImage_ConvertTo32Bits(src);
+ break;
+ default:
+ return FALSE;
+ }
+ } else {
+ return FALSE;
+ }
+
+ if(!clone) return FALSE;
+
+ // paste src to dst
+ switch(FreeImage_GetBPP(dst)) {
+ case 1:
+ bResult = Combine1(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+ break;
+ case 4:
+ bResult = Combine4(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+ break;
+ case 8:
+ bResult = Combine8(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+ break;
+ case 16:
+ if (isRGB565) {
+ bResult = Combine16_565(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+ } else {
+ // includes case where all the masks are 0
+ bResult = Combine16_555(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+ }
+ break;
+ case 24:
+ bResult = Combine24(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+ break;
+ case 32:
+ bResult = Combine32(dst, clone, (unsigned)left, (unsigned)top, (unsigned)alpha);
+ break;
+ }
+
+ if(clone != src)
+ FreeImage_Unload(clone);
+
+ }
+ else { // any type other than FITBITMAP
+ bResult = CombineSameType(dst, src, (unsigned)left, (unsigned)top);
+ }
+
+ return bResult;
+}
+
+// ----------------------------------------------------------
+
+/** @brief Creates a dynamic read/write view into a FreeImage bitmap.
+
+ A dynamic view is a FreeImage bitmap with its own width and height, that,
+ however, shares its bits with another FreeImage bitmap. Typically, views
+ are used to define one or more rectangular sub-images of an existing
+ bitmap. All FreeImage operations, like saving, displaying and all the
+ toolkit functions, when applied to the view, only affect the view's
+ rectangular area.
+
+ Although the view's backing image's bits not need to be copied around,
+ which makes the view much faster than similar solutions using
+ FreeImage_Copy, a view uses some private memory that needs to be freed by
+ calling FreeImage_Unload on the view's handle to prevent memory leaks.
+
+ Only the backing image's pixels are shared by the view. For all other image
+ data, notably for the resolution, background color, color palette,
+ transparency table and for the ICC profile, the view gets a private copy
+ of the data. By default, the backing image's metadata is NOT copied to
+ the view.
+
+ As with all FreeImage functions that take a rectangle region, top and left
+ positions are included, whereas right and bottom positions are excluded
+ from the rectangle area.
+
+ Since the memory block shared by the backing image and the view must start
+ at a byte boundary, the value of parameter left must be a multiple of 8
+ for 1-bit images and a multiple of 2 for 4-bit images.
+
+ @param dib The FreeImage bitmap on which to create the view.
+ @param left The left position of the view's area.
+ @param top The top position of the view's area.
+ @param right The right position of the view's area.
+ @param bottom The bottom position of the view's area.
+ @return Returns a handle to the newly created view or NULL if the view
+ was not created.
+ */
+FIBITMAP * DLL_CALLCONV
+FreeImage_CreateView(FIBITMAP *dib, unsigned left, unsigned top, unsigned right, unsigned bottom) {
+ if (!FreeImage_HasPixels(dib)) {
+ return NULL;
+ }
+
+ // normalize the rectangle
+ if (right < left) {
+ INPLACESWAP(left, right);
+ }
+ if (bottom < top) {
+ INPLACESWAP(top, bottom);
+ }
+
+ // check the size of the sub image
+ unsigned width = FreeImage_GetWidth(dib);
+ unsigned height = FreeImage_GetHeight(dib);
+ if (left < 0 || right > width || top < 0 || bottom > height) {
+ return NULL;
+ }
+
+ unsigned bpp = FreeImage_GetBPP(dib);
+ BYTE *bits = FreeImage_GetScanLine(dib, height - bottom);
+ switch (bpp) {
+ case 1:
+ if (left % 8 != 0) {
+ // view can only start at a byte boundary
+ return NULL;
+ }
+ bits += (left / 8);
+ break;
+ case 4:
+ if (left % 2 != 0) {
+ // view can only start at a byte boundary
+ return NULL;
+ }
+ bits += (left / 2);
+ break;
+ default:
+ bits += left * (bpp / 8);
+ break;
+ }
+
+ FIBITMAP *dst = FreeImage_AllocateHeaderForBits(bits, FreeImage_GetPitch(dib), FreeImage_GetImageType(dib),
+ right - left, bottom - top,
+ bpp,
+ FreeImage_GetRedMask(dib), FreeImage_GetGreenMask(dib), FreeImage_GetBlueMask(dib));
+
+ if (dst == NULL) {
+ return NULL;
+ }
+
+ // copy some basic image properties needed for displaying and saving
+
+ // resolution
+ FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(dib));
+ FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(dib));
+
+ // background color
+ RGBQUAD bkcolor;
+ if (FreeImage_GetBackgroundColor(dib, &bkcolor)) {
+ FreeImage_SetBackgroundColor(dst, &bkcolor);
+ }
+
+ // palette
+ memcpy(FreeImage_GetPalette(dst), FreeImage_GetPalette(dib), FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD));
+
+ // transparency table
+ FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+
+ // ICC profile
+ FIICCPROFILE *src_profile = FreeImage_GetICCProfile(dib);
+ FIICCPROFILE *dst_profile = FreeImage_CreateICCProfile(dst, src_profile->data, src_profile->size);
+ dst_profile->flags = src_profile->flags;
+
+ return dst;
+}
diff --git a/libs/freeimage/src/FreeImageToolkit/Display.cpp b/libs/freeimage/src/FreeImageToolkit/Display.cpp
new file mode 100644
index 0000000000..deaefe5a39
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/Display.cpp
@@ -0,0 +1,228 @@
+// ==========================================================
+// Display routines
+//
+// Design and implementation 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!
+// ==========================================================
+
+#include "../stdafx.h"
+
+/**
+@brief Composite a foreground image against a background color or a background image.
+
+The equation for computing a composited sample value is:<br>
+output = alpha * foreground + (1-alpha) * background<br>
+where alpha and the input and output sample values are expressed as fractions in the range 0 to 1.
+For colour images, the computation is done separately for R, G, and B samples.
+
+@param fg Foreground image
+@param useFileBkg If TRUE and a file background is present, use it as the background color
+@param appBkColor If not equal to NULL, and useFileBkg is FALSE, use this color as the background color
+@param bg If not equal to NULL and useFileBkg is FALSE and appBkColor is NULL, use this as the background image
+@return Returns the composite image if successful, returns NULL otherwise
+@see FreeImage_IsTransparent, FreeImage_HasBackgroundColor
+*/
+FIBITMAP * DLL_CALLCONV
+FreeImage_Composite(FIBITMAP *fg, BOOL useFileBkg, RGBQUAD *appBkColor, FIBITMAP *bg) {
+ if(!FreeImage_HasPixels(fg)) return NULL;
+
+ int width = FreeImage_GetWidth(fg);
+ int height = FreeImage_GetHeight(fg);
+ int bpp = FreeImage_GetBPP(fg);
+
+ if((bpp != 8) && (bpp != 32))
+ return NULL;
+
+ if(bg) {
+ int bg_width = FreeImage_GetWidth(bg);
+ int bg_height = FreeImage_GetHeight(bg);
+ int bg_bpp = FreeImage_GetBPP(bg);
+ if((bg_width != width) || (bg_height != height) || (bg_bpp != 24))
+ return NULL;
+ }
+
+ int bytespp = (bpp == 8) ? 1 : 4;
+
+
+ int x, y, c;
+ BYTE alpha = 0, not_alpha;
+ BYTE index;
+ RGBQUAD fgc; // foreground color
+ RGBQUAD bkc; // background color
+
+ memset(&fgc, 0, sizeof(RGBQUAD));
+ memset(&bkc, 0, sizeof(RGBQUAD));
+
+ // allocate the composite image
+ FIBITMAP *composite = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if(!composite) return NULL;
+
+ // get the palette
+ RGBQUAD *pal = FreeImage_GetPalette(fg);
+
+ // retrieve the alpha table from the foreground image
+ BOOL bIsTransparent = FreeImage_IsTransparent(fg);
+ BYTE *trns = FreeImage_GetTransparencyTable(fg);
+
+ // retrieve the background color from the foreground image
+ BOOL bHasBkColor = FALSE;
+
+ if(useFileBkg && FreeImage_HasBackgroundColor(fg)) {
+ FreeImage_GetBackgroundColor(fg, &bkc);
+ bHasBkColor = TRUE;
+ } else {
+ // no file background color
+ // use application background color ?
+ if(appBkColor) {
+ memcpy(&bkc, appBkColor, sizeof(RGBQUAD));
+ bHasBkColor = TRUE;
+ }
+ // use background image ?
+ else if(bg) {
+ bHasBkColor = FALSE;
+ }
+ }
+
+ for(y = 0; y < height; y++) {
+ // foreground
+ BYTE *fg_bits = FreeImage_GetScanLine(fg, y);
+ // background
+ BYTE *bg_bits = FreeImage_GetScanLine(bg, y);
+ // composite image
+ BYTE *cp_bits = FreeImage_GetScanLine(composite, y);
+
+ for(x = 0; x < width; x++) {
+
+ // foreground color + alpha
+
+ if(bpp == 8) {
+ // get the foreground color
+ index = fg_bits[0];
+ memcpy(&fgc, &pal[index], sizeof(RGBQUAD));
+ // get the alpha
+ if(bIsTransparent) {
+ alpha = trns[index];
+ } else {
+ alpha = 255;
+ }
+ }
+ else if(bpp == 32) {
+ // get the foreground color
+ fgc.rgbBlue = fg_bits[FI_RGBA_BLUE];
+ fgc.rgbGreen = fg_bits[FI_RGBA_GREEN];
+ fgc.rgbRed = fg_bits[FI_RGBA_RED];
+ // get the alpha
+ alpha = fg_bits[FI_RGBA_ALPHA];
+ }
+
+ // background color
+
+ if(!bHasBkColor) {
+ if(bg) {
+ // get the background color from the background image
+ bkc.rgbBlue = bg_bits[FI_RGBA_BLUE];
+ bkc.rgbGreen = bg_bits[FI_RGBA_GREEN];
+ bkc.rgbRed = bg_bits[FI_RGBA_RED];
+ }
+ else {
+ // use a checkerboard pattern
+ c = (((y & 0x8) == 0) ^ ((x & 0x8) == 0)) * 192;
+ c = c ? c : 255;
+ bkc.rgbBlue = (BYTE)c;
+ bkc.rgbGreen = (BYTE)c;
+ bkc.rgbRed = (BYTE)c;
+ }
+ }
+
+ // composition
+
+ if(alpha == 0) {
+ // output = background
+ cp_bits[FI_RGBA_BLUE] = bkc.rgbBlue;
+ cp_bits[FI_RGBA_GREEN] = bkc.rgbGreen;
+ cp_bits[FI_RGBA_RED] = bkc.rgbRed;
+ }
+ else if(alpha == 255) {
+ // output = foreground
+ cp_bits[FI_RGBA_BLUE] = fgc.rgbBlue;
+ cp_bits[FI_RGBA_GREEN] = fgc.rgbGreen;
+ cp_bits[FI_RGBA_RED] = fgc.rgbRed;
+ }
+ else {
+ // output = alpha * foreground + (1-alpha) * background
+ not_alpha = (BYTE)~alpha;
+ cp_bits[FI_RGBA_BLUE] = (BYTE)((alpha * (WORD)fgc.rgbBlue + not_alpha * (WORD)bkc.rgbBlue) >> 8);
+ cp_bits[FI_RGBA_GREEN] = (BYTE)((alpha * (WORD)fgc.rgbGreen + not_alpha * (WORD)bkc.rgbGreen) >> 8);
+ cp_bits[FI_RGBA_RED] = (BYTE)((alpha * (WORD)fgc.rgbRed + not_alpha * (WORD)bkc.rgbRed) >> 8);
+ }
+
+ fg_bits += bytespp;
+ bg_bits += 3;
+ cp_bits += 3;
+ }
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(composite, fg);
+
+ return composite;
+}
+
+/**
+Pre-multiplies a 32-bit image's red-, green- and blue channels with it's alpha channel
+for to be used with e.g. the Windows GDI function AlphaBlend().
+The transformation changes the red-, green- and blue channels according to the following equation:
+channel(x, y) = channel(x, y) * alpha_channel(x, y) / 255
+@param dib Input/Output dib to be premultiplied
+@return Returns TRUE on success, FALSE otherwise (e.g. when the bitdepth of the source dib cannot be handled).
+*/
+BOOL DLL_CALLCONV
+FreeImage_PremultiplyWithAlpha(FIBITMAP *dib) {
+ if (!FreeImage_HasPixels(dib)) return FALSE;
+
+ if ((FreeImage_GetBPP(dib) != 32) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+ return FALSE;
+ }
+
+ int width = FreeImage_GetWidth(dib);
+ int height = FreeImage_GetHeight(dib);
+
+ for(int y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+ for (int x = 0; x < width; x++, bits += 4) {
+ const BYTE alpha = bits[FI_RGBA_ALPHA];
+ // slightly faster: care for two special cases
+ if(alpha == 0x00) {
+ // special case for alpha == 0x00
+ // color * 0x00 / 0xFF = 0x00
+ bits[FI_RGBA_BLUE] = 0x00;
+ bits[FI_RGBA_GREEN] = 0x00;
+ bits[FI_RGBA_RED] = 0x00;
+ } else if(alpha == 0xFF) {
+ // nothing to do for alpha == 0xFF
+ // color * 0xFF / 0xFF = color
+ continue;
+ } else {
+ bits[FI_RGBA_BLUE] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_BLUE] + 127) / 255 );
+ bits[FI_RGBA_GREEN] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_GREEN] + 127) / 255 );
+ bits[FI_RGBA_RED] = (BYTE)( (alpha * (WORD)bits[FI_RGBA_RED] + 127) / 255 );
+ }
+ }
+ }
+ return TRUE;
+}
+
diff --git a/libs/freeimage/src/FreeImageToolkit/Filters.h b/libs/freeimage/src/FreeImageToolkit/Filters.h
new file mode 100644
index 0000000000..970e5604ec
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/Filters.h
@@ -0,0 +1,287 @@
+// ==========================================================
+// Upsampling / downsampling filters
+//
+// Design and implementation 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!
+// ==========================================================
+
+#ifndef _FILTERS_H_
+#define _FILTERS_H_
+
+/**
+ CGenericFilter is a generic abstract filter class used to access to the filter library.<br>
+ Filters used in this library have been mainly taken from the following references : <br>
+<b>Main reference</b> : <br>
+Paul Heckbert, C code to zoom raster images up or down, with nice filtering.
+UC Berkeley, August 1989. [online] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/Web/People/ph/heckbert.html
+
+<b>Heckbert references</b> : <br>
+<ul>
+<li>Oppenheim A.V., Schafer R.W., Digital Signal Processing, Prentice-Hall, 1975
+<li>Hamming R.W., Digital Filters, Prentice-Hall, Englewood Cliffs, NJ, 1983
+<li>Pratt W.K., Digital Image Processing, John Wiley and Sons, 1978
+<li>Hou H.S., Andrews H.C., "Cubic Splines for Image Interpolation and Digital Filtering",
+IEEE Trans. Acoustics, Speech, and Signal Proc., vol. ASSP-26, no. 6, pp. 508-517, Dec. 1978.
+</ul>
+
+*/
+class CGenericFilter
+{
+protected:
+
+ #define FILTER_PI double (3.1415926535897932384626433832795)
+ #define FILTER_2PI double (2.0 * 3.1415926535897932384626433832795)
+ #define FILTER_4PI double (4.0 * 3.1415926535897932384626433832795)
+
+ /// Filter support
+ double m_dWidth;
+
+public:
+
+ /// Constructor
+ CGenericFilter (double dWidth) : m_dWidth (dWidth) {}
+ /// Destructor
+ virtual ~CGenericFilter() {}
+
+ /// Returns the filter support
+ double GetWidth() { return m_dWidth; }
+ /// Change the filter suport
+ void SetWidth (double dWidth) { m_dWidth = dWidth; }
+
+ /// Returns F(dVal) where F is the filter's impulse response
+ virtual double Filter (double dVal) = 0;
+};
+
+// -----------------------------------------------------------------------------------
+// Filters library
+// All filters are centered on 0
+// -----------------------------------------------------------------------------------
+
+/**
+ Box filter<br>
+ Box, pulse, Fourier window, 1st order (constant) b-spline.<br><br>
+
+ <b>Reference</b> : <br>
+ Glassner A.S., Principles of digital image synthesis. Morgan Kaufmann Publishers, Inc, San Francisco, Vol. 2, 1995
+*/
+class CBoxFilter : public CGenericFilter
+{
+public:
+ /**
+ Constructor<br>
+ Default fixed width = 0.5
+ */
+ CBoxFilter() : CGenericFilter(0.5) {}
+ virtual ~CBoxFilter() {}
+
+ double Filter (double dVal) { return (fabs(dVal) <= m_dWidth ? 1.0 : 0.0); }
+};
+
+/** Bilinear filter
+*/
+class CBilinearFilter : public CGenericFilter
+{
+public:
+
+ CBilinearFilter () : CGenericFilter(1) {}
+ virtual ~CBilinearFilter() {}
+
+ double Filter (double dVal) {
+ dVal = fabs(dVal);
+ return (dVal < m_dWidth ? m_dWidth - dVal : 0.0);
+ }
+};
+
+
+/**
+ Mitchell & Netravali's two-param cubic filter<br>
+
+ The parameters b and c can be used to adjust the properties of the cubic.
+ They are sometimes referred to as "blurring" and "ringing" respectively.
+ The default is b = 1/3 and c = 1/3, which were the values recommended by
+ Mitchell and Netravali as yielding the most visually pleasing results in subjective tests of human beings.
+ Larger values of b and c can produce interesting op-art effects--for example, try b = 0 and c = -5. <br><br>
+
+ <b>Reference</b> : <br>
+ Don P. Mitchell and Arun N. Netravali, Reconstruction filters in computer graphics.
+ In John Dill, editor, Computer Graphics (SIGGRAPH '88 Proceedings), Vol. 22, No. 4, August 1988, pp. 221-228.
+*/
+class CBicubicFilter : public CGenericFilter
+{
+protected:
+ // data for parameterized Mitchell filter
+ double p0, p2, p3;
+ double q0, q1, q2, q3;
+
+public:
+ /**
+ Constructor<br>
+ Default fixed width = 2
+ @param b Filter parameter (default value is 1/3)
+ @param c Filter parameter (default value is 1/3)
+ */
+ CBicubicFilter (double b = (1/(double)3), double c = (1/(double)3)) : CGenericFilter(2) {
+ p0 = (6 - 2*b) / 6;
+ p2 = (-18 + 12*b + 6*c) / 6;
+ p3 = (12 - 9*b - 6*c) / 6;
+ q0 = (8*b + 24*c) / 6;
+ q1 = (-12*b - 48*c) / 6;
+ q2 = (6*b + 30*c) / 6;
+ q3 = (-b - 6*c) / 6;
+ }
+ virtual ~CBicubicFilter() {}
+
+ double Filter(double dVal) {
+ dVal = fabs(dVal);
+ if(dVal < 1)
+ return (p0 + dVal*dVal*(p2 + dVal*p3));
+ if(dVal < 2)
+ return (q0 + dVal*(q1 + dVal*(q2 + dVal*q3)));
+ return 0;
+ }
+};
+
+/**
+ Catmull-Rom spline, Overhauser spline<br>
+
+ When using CBicubicFilter filters, you have to set parameters b and c such that <br>
+ b + 2 * c = 1<br>
+ in order to use the numerically most accurate filter.<br>
+ This gives for b = 0 the maximum value for c = 0.5, which is the Catmull-Rom
+ spline and a good suggestion for sharpness.<br><br>
+
+
+ <b>References</b> : <br>
+ <ul>
+ <li>Mitchell Don P., Netravali Arun N., Reconstruction filters in computer graphics.
+ In John Dill, editor, Computer Graphics (SIGGRAPH '88 Proceedings), Vol. 22, No. 4, August 1988, pp. 221-228.
+ <li>Keys R.G., Cubic Convolution Interpolation for Digital Image Processing.
+ IEEE Trans. Acoustics, Speech, and Signal Processing, vol. 29, no. 6, pp. 1153-1160, Dec. 1981.
+ </ul>
+
+*/
+class CCatmullRomFilter : public CGenericFilter
+{
+public:
+
+ /**
+ Constructor<br>
+ Default fixed width = 2
+ */
+ CCatmullRomFilter() : CGenericFilter(2) {}
+ virtual ~CCatmullRomFilter() {}
+
+ double Filter(double dVal) {
+ if(dVal < -2) return 0;
+ if(dVal < -1) return (0.5*(4 + dVal*(8 + dVal*(5 + dVal))));
+ if(dVal < 0) return (0.5*(2 + dVal*dVal*(-5 - 3*dVal)));
+ if(dVal < 1) return (0.5*(2 + dVal*dVal*(-5 + 3*dVal)));
+ if(dVal < 2) return (0.5*(4 + dVal*(-8 + dVal*(5 - dVal))));
+ return 0;
+ }
+};
+
+/**
+ Lanczos-windowed sinc filter<br>
+
+ Lanczos3 filter is an alternative to CBicubicFilter with high values of c about 0.6 ... 0.75
+ which produces quite strong sharpening. It usually offers better quality (fewer artifacts) and a sharp image.<br><br>
+
+*/
+class CLanczos3Filter : public CGenericFilter
+{
+public:
+ /**
+ Constructor<br>
+ Default fixed width = 3
+ */
+ CLanczos3Filter() : CGenericFilter(3) {}
+ virtual ~CLanczos3Filter() {}
+
+ double Filter(double dVal) {
+ dVal = fabs(dVal);
+ if(dVal < m_dWidth) {
+ return (sinc(dVal) * sinc(dVal / m_dWidth));
+ }
+ return 0;
+ }
+
+private:
+ double sinc(double value) {
+ if(value != 0) {
+ value *= FILTER_PI;
+ return (sin(value) / value);
+ }
+ return 1;
+ }
+};
+
+/**
+ 4th order (cubic) b-spline<br>
+
+*/
+class CBSplineFilter : public CGenericFilter
+{
+public:
+
+ /**
+ Constructor<br>
+ Default fixed width = 2
+ */
+ CBSplineFilter() : CGenericFilter(2) {}
+ virtual ~CBSplineFilter() {}
+
+ double Filter(double dVal) {
+
+ dVal = fabs(dVal);
+ if(dVal < 1) return (4 + dVal*dVal*(-6 + 3*dVal)) / 6;
+ if(dVal < 2) {
+ double t = 2 - dVal;
+ return (t*t*t / 6);
+ }
+ return 0;
+ }
+};
+
+// -----------------------------------------------------------------------------------
+// Window function library
+// -----------------------------------------------------------------------------------
+
+/**
+ Blackman window
+*/
+class CBlackmanFilter : public CGenericFilter
+{
+public:
+ /**
+ Constructor<br>
+ Default width = 0.5
+ */
+ CBlackmanFilter (double dWidth = double(0.5)) : CGenericFilter(dWidth) {}
+ virtual ~CBlackmanFilter() {}
+
+ double Filter (double dVal) {
+ if(fabs (dVal) > m_dWidth) {
+ return 0;
+ }
+ double dN = 2 * m_dWidth + 1;
+ dVal /= (dN - 1);
+ return 0.42 + 0.5*cos(FILTER_2PI*dVal) + 0.08*cos(FILTER_4PI*dVal);
+ }
+};
+
+#endif // _FILTERS_H_
diff --git a/libs/freeimage/src/FreeImageToolkit/Flip.cpp b/libs/freeimage/src/FreeImageToolkit/Flip.cpp
new file mode 100644
index 0000000000..b9cd9805aa
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/Flip.cpp
@@ -0,0 +1,165 @@
+// ==========================================================
+// Flipping routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jim Keir (jimkeir@users.sourceforge.net)
+//
+// 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!
+// ==========================================================
+
+#include "../stdafx.h"
+
+/**
+Flip the image horizontally along the vertical axis.
+@param src Input image to be processed.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+BOOL DLL_CALLCONV
+FreeImage_FlipHorizontal(FIBITMAP *src) {
+ if (!FreeImage_HasPixels(src)) return FALSE;
+
+ unsigned line = FreeImage_GetLine(src);
+ unsigned width = FreeImage_GetWidth(src);
+ unsigned height = FreeImage_GetHeight(src);
+
+ unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+ // copy between aligned memories
+ BYTE *new_bits = (BYTE*)FreeImage_Aligned_Malloc(line * sizeof(BYTE), FIBITMAP_ALIGNMENT);
+ if (!new_bits) return FALSE;
+
+ // mirror the buffer
+
+ for (unsigned y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(src, y);
+ memcpy(new_bits, bits, line);
+
+ switch (FreeImage_GetBPP(src)) {
+ case 1 :
+ {
+ for(unsigned x = 0; x < width; x++) {
+ // get pixel at (x, y)
+ BOOL value = (new_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
+ // set pixel at (new_x, y)
+ unsigned new_x = width - 1 - x;
+ value ? bits[new_x >> 3] |= (0x80 >> (new_x & 0x7)) : bits[new_x >> 3] &= (0xff7f >> (new_x & 0x7));
+ }
+ }
+ break;
+
+ case 4 :
+ {
+ for(unsigned c = 0; c < line; c++) {
+ bits[c] = new_bits[line - c - 1];
+
+ BYTE nibble = (bits[c] & 0xF0) >> 4;
+
+ bits[c] = bits[c] << 4;
+ bits[c] |= nibble;
+ }
+ }
+ break;
+
+ case 8:
+ {
+ BYTE *dst_data = (BYTE*) bits;
+ BYTE *src_data = (BYTE*) (new_bits + line - bytespp);
+ for(unsigned c = 0; c < width; c++) {
+ *dst_data++ = *src_data--;
+ }
+ }
+ break;
+
+ case 16:
+ {
+ WORD *dst_data = (WORD*) bits;
+ WORD *src_data = (WORD*) (new_bits + line - bytespp);
+ for(unsigned c = 0; c < width; c++) {
+ *dst_data++ = *src_data--;
+ }
+ }
+ break;
+
+ case 24 :
+ case 32 :
+ case 48:
+ case 64:
+ case 96:
+ case 128:
+ {
+ BYTE *dst_data = (BYTE*) bits;
+ BYTE *src_data = (BYTE*) (new_bits + line - bytespp);
+ for(unsigned c = 0; c < width; c++) {
+ for(unsigned k = 0; k < bytespp; k++) {
+ *dst_data++ = src_data[k];
+ }
+ src_data -= bytespp;
+ }
+ }
+ break;
+
+ }
+ }
+
+ FreeImage_Aligned_Free(new_bits);
+
+ return TRUE;
+}
+
+
+/**
+Flip the image vertically along the horizontal axis.
+@param src Input image to be processed.
+@return Returns TRUE if successful, FALSE otherwise.
+*/
+
+BOOL DLL_CALLCONV
+FreeImage_FlipVertical(FIBITMAP *src) {
+ BYTE *From, *Mid;
+
+ if (!FreeImage_HasPixels(src)) return FALSE;
+
+ // swap the buffer
+
+ unsigned pitch = FreeImage_GetPitch(src);
+ unsigned height = FreeImage_GetHeight(src);
+
+ // copy between aligned memories
+ Mid = (BYTE*)FreeImage_Aligned_Malloc(pitch * sizeof(BYTE), FIBITMAP_ALIGNMENT);
+ if (!Mid) return FALSE;
+
+ From = FreeImage_GetBits(src);
+
+ unsigned line_s = 0;
+ unsigned line_t = (height-1) * pitch;
+
+ for(unsigned y = 0; y < height/2; y++) {
+
+ memcpy(Mid, From + line_s, pitch);
+ memcpy(From + line_s, From + line_t, pitch);
+ memcpy(From + line_t, Mid, pitch);
+
+ line_s += pitch;
+ line_t -= pitch;
+
+ }
+
+ FreeImage_Aligned_Free(Mid);
+
+ return TRUE;
+}
+
diff --git a/libs/freeimage/src/FreeImageToolkit/JPEGTransform.cpp b/libs/freeimage/src/FreeImageToolkit/JPEGTransform.cpp
new file mode 100644
index 0000000000..16296775f1
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/JPEGTransform.cpp
@@ -0,0 +1,622 @@
+// ==========================================================
+// JPEG lossless transformations
+//
+// Design and implementation by
+// - Petr Pytelka (pyta@lightcomp.com)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// 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!
+// ==========================================================
+
+
+#include "../stdafx.h"
+
+extern "C" {
+#define XMD_H
+#undef FAR
+#include <setjmp.h>
+
+#include "../LibJPEG/jinclude.h"
+#include "../LibJPEG/jpeglib.h"
+#include "../LibJPEG/jerror.h"
+#include "../LibJPEG/transupp.h"
+}
+
+// ----------------------------------------------------------
+// Source manager & Destination manager setup
+// (see PluginJPEG.cpp)
+// ----------------------------------------------------------
+
+void jpeg_freeimage_src(j_decompress_ptr cinfo, fi_handle infile, FreeImageIO *io);
+void jpeg_freeimage_dst(j_compress_ptr cinfo, fi_handle outfile, FreeImageIO *io);
+
+// ----------------------------------------------------------
+// Error handling
+// (see also PluginJPEG.cpp)
+// ----------------------------------------------------------
+
+/**
+ Receives control for a fatal error. Information sufficient to
+ generate the error message has been stored in cinfo->err; call
+ output_message to display it. Control must NOT return to the caller;
+ generally this routine will exit() or longjmp() somewhere.
+*/
+METHODDEF(void)
+ls_jpeg_error_exit (j_common_ptr cinfo) {
+ // always display the message
+ (*cinfo->err->output_message)(cinfo);
+
+ // allow JPEG with a premature end of file
+ if((cinfo)->err->msg_parm.i[0] != 13) {
+
+ // let the memory manager delete any temp files before we die
+ jpeg_destroy(cinfo);
+
+ throw FIF_JPEG;
+ }
+}
+
+/**
+ Actual output of any JPEG message. Note that this method does not know
+ how to generate a message, only where to send it.
+*/
+METHODDEF(void)
+ls_jpeg_output_message (j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+
+ // create the message
+ (*cinfo->err->format_message)(cinfo, buffer);
+ // send it to user's message proc
+ FreeImage_OutputMessageProc(FIF_JPEG, buffer);
+}
+
+// ----------------------------------------------------------
+// Main program
+// ----------------------------------------------------------
+
+/**
+Build a crop string.
+
+@param crop Output crop string
+@param left Specifies the left position of the cropped rectangle
+@param top Specifies the top position of the cropped rectangle
+@param right Specifies the right position of the cropped rectangle
+@param bottom Specifies the bottom position of the cropped rectangle
+@param width Image width
+@param height Image height
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+getCropString(char* crop, int* left, int* top, int* right, int* bottom, int width, int height) {
+ if(!left || !top || !right || !bottom) {
+ return FALSE;
+ }
+
+ *left = CLAMP(*left, 0, width);
+ *top = CLAMP(*top, 0, height);
+
+ // negative/zero right and bottom count from the edges inwards
+
+ if(*right <= 0) {
+ *right = width + *right;
+ }
+ if(*bottom <= 0) {
+ *bottom = height + *bottom;
+ }
+
+ *right = CLAMP(*right, 0, width);
+ *bottom = CLAMP(*bottom, 0, height);
+
+ // test for empty rect
+
+ if(((*left - *right) == 0) || ((*top - *bottom) == 0)) {
+ return FALSE;
+ }
+
+ // normalize the rectangle
+
+ if(*right < *left) {
+ INPLACESWAP(*left, *right);
+ }
+ if(*bottom < *top) {
+ INPLACESWAP(*top, *bottom);
+ }
+
+ // test for "noop" rect
+
+ if(*left == 0 && *right == width && *top == 0 && *bottom == height) {
+ return FALSE;
+ }
+
+ // build the crop option
+ sprintf(crop, "%dx%d+%d+%d", *right - *left, *bottom - *top, *left, *top);
+
+ return TRUE;
+}
+
+static BOOL
+JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
+ const BOOL onlyReturnCropRect = (dst_io == NULL) || (dst_handle == NULL);
+ const long stream_start = onlyReturnCropRect ? 0 : dst_io->tell_proc(dst_handle);
+ BOOL swappedDim = FALSE;
+ BOOL trimH = FALSE;
+ BOOL trimV = FALSE;
+
+ // Set up the jpeglib structures
+ jpeg_decompress_struct srcinfo;
+ jpeg_compress_struct dstinfo;
+ jpeg_error_mgr jsrcerr, jdsterr;
+ jvirt_barray_ptr *src_coef_arrays = NULL;
+ jvirt_barray_ptr *dst_coef_arrays = NULL;
+ // Support for copying optional markers from source to destination file
+ JCOPY_OPTION copyoption;
+ // Image transformation options
+ jpeg_transform_info transfoptions;
+
+ // Initialize structures
+ memset(&srcinfo, 0, sizeof(srcinfo));
+ memset(&jsrcerr, 0, sizeof(jsrcerr));
+ memset(&jdsterr, 0, sizeof(jdsterr));
+ memset(&dstinfo, 0, sizeof(dstinfo));
+ memset(&transfoptions, 0, sizeof(transfoptions));
+
+ // Copy all extra markers from source file
+ copyoption = JCOPYOPT_ALL;
+
+ // Set up default JPEG parameters
+ transfoptions.force_grayscale = FALSE;
+ transfoptions.crop = FALSE;
+
+ // Select the transform option
+ switch(operation) {
+ case FIJPEG_OP_FLIP_H: // horizontal flip
+ transfoptions.transform = JXFORM_FLIP_H;
+ trimH = TRUE;
+ break;
+ case FIJPEG_OP_FLIP_V: // vertical flip
+ transfoptions.transform = JXFORM_FLIP_V;
+ trimV = TRUE;
+ break;
+ case FIJPEG_OP_TRANSPOSE: // transpose across UL-to-LR axis
+ transfoptions.transform = JXFORM_TRANSPOSE;
+ swappedDim = TRUE;
+ break;
+ case FIJPEG_OP_TRANSVERSE: // transpose across UR-to-LL axis
+ transfoptions.transform = JXFORM_TRANSVERSE;
+ trimH = TRUE;
+ trimV = TRUE;
+ swappedDim = TRUE;
+ break;
+ case FIJPEG_OP_ROTATE_90: // 90-degree clockwise rotation
+ transfoptions.transform = JXFORM_ROT_90;
+ trimH = TRUE;
+ swappedDim = TRUE;
+ break;
+ case FIJPEG_OP_ROTATE_180: // 180-degree rotation
+ trimH = TRUE;
+ trimV = TRUE;
+ transfoptions.transform = JXFORM_ROT_180;
+ break;
+ case FIJPEG_OP_ROTATE_270: // 270-degree clockwise (or 90 ccw)
+ transfoptions.transform = JXFORM_ROT_270;
+ trimV = TRUE;
+ swappedDim = TRUE;
+ break;
+ default:
+ case FIJPEG_OP_NONE: // no transformation
+ transfoptions.transform = JXFORM_NONE;
+ break;
+ }
+ // (perfect == TRUE) ==> fail if there is non-transformable edge blocks
+ transfoptions.perfect = (perfect == TRUE) ? TRUE : FALSE;
+ // Drop non-transformable edge blocks: trim off any partial edge MCUs that the transform can't handle.
+ transfoptions.trim = TRUE;
+
+ try {
+
+ // Initialize the JPEG decompression object with default error handling
+ srcinfo.err = jpeg_std_error(&jsrcerr);
+ srcinfo.err->error_exit = ls_jpeg_error_exit;
+ srcinfo.err->output_message = ls_jpeg_output_message;
+ jpeg_create_decompress(&srcinfo);
+
+ // Initialize the JPEG compression object with default error handling
+ dstinfo.err = jpeg_std_error(&jdsterr);
+ dstinfo.err->error_exit = ls_jpeg_error_exit;
+ dstinfo.err->output_message = ls_jpeg_output_message;
+ jpeg_create_compress(&dstinfo);
+
+ // Specify data source for decompression
+ jpeg_freeimage_src(&srcinfo, src_handle, src_io);
+
+ // Enable saving of extra markers that we want to copy
+ jcopy_markers_setup(&srcinfo, copyoption);
+
+ // Read the file header
+ jpeg_read_header(&srcinfo, TRUE);
+
+ // crop option
+ char crop[64];
+ const BOOL hasCrop = getCropString(crop, left, top, right, bottom, swappedDim ? srcinfo.image_height : srcinfo.image_width, swappedDim ? srcinfo.image_width : srcinfo.image_height);
+
+ if(hasCrop) {
+ if(!jtransform_parse_crop_spec(&transfoptions, crop)) {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop);
+ throw(1);
+ }
+ }
+
+ // Any space needed by a transform option must be requested before
+ // jpeg_read_coefficients so that memory allocation will be done right
+
+ // Prepare transformation workspace
+ // Fails right away if perfect flag is TRUE and transformation is not perfect
+ if( !jtransform_request_workspace(&srcinfo, &transfoptions) ) {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Transformation is not perfect");
+ throw(1);
+ }
+
+ if(left || top) {
+ // compute left and top offsets, it's a bit tricky, taking into account both
+ // transform, which might have trimed the image,
+ // and crop itself, which is adjusted to lie on a iMCU boundary
+
+ const int fullWidth = swappedDim ? srcinfo.image_height : srcinfo.image_width;
+ const int fullHeight = swappedDim ? srcinfo.image_width : srcinfo.image_height;
+
+ int transformedFullWidth = fullWidth;
+ int transformedFullHeight = fullHeight;
+
+ if(trimH && transformedFullWidth/transfoptions.iMCU_sample_width > 0) {
+ transformedFullWidth = (transformedFullWidth/transfoptions.iMCU_sample_width) * transfoptions.iMCU_sample_width;
+ }
+ if(trimV && transformedFullHeight/transfoptions.iMCU_sample_height > 0) {
+ transformedFullHeight = (transformedFullHeight/transfoptions.iMCU_sample_height) * transfoptions.iMCU_sample_height;
+ }
+
+ const int trimmedWidth = fullWidth - transformedFullWidth;
+ const int trimmedHeight = fullHeight - transformedFullHeight;
+
+ if(left) {
+ *left = trimmedWidth + transfoptions.x_crop_offset * transfoptions.iMCU_sample_width;
+ }
+ if(top) {
+ *top = trimmedHeight + transfoptions.y_crop_offset * transfoptions.iMCU_sample_height;
+ }
+ }
+
+ if(right) {
+ *right = (left ? *left : 0) + transfoptions.output_width;
+ }
+ if(bottom) {
+ *bottom = (top ? *top : 0) + transfoptions.output_height;
+ }
+
+ // if only the crop rect is requested, we are done
+
+ if(onlyReturnCropRect) {
+ jpeg_destroy_compress(&dstinfo);
+ jpeg_destroy_decompress(&srcinfo);
+ return TRUE;
+ }
+
+ // Read source file as DCT coefficients
+ src_coef_arrays = jpeg_read_coefficients(&srcinfo);
+
+ // Initialize destination compression parameters from source values
+ jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
+
+ // Adjust destination parameters if required by transform options;
+ // also find out which set of coefficient arrays will hold the output
+ dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
+
+ // Note: we assume that jpeg_read_coefficients consumed all input
+ // until JPEG_REACHED_EOI, and that jpeg_finish_decompress will
+ // only consume more while (! cinfo->inputctl->eoi_reached).
+ // We cannot call jpeg_finish_decompress here since we still need the
+ // virtual arrays allocated from the source object for processing.
+
+ if(src_handle == dst_handle) {
+ dst_io->seek_proc(dst_handle, stream_start, SEEK_SET);
+ }
+
+ // Specify data destination for compression
+ jpeg_freeimage_dst(&dstinfo, dst_handle, dst_io);
+
+ // Start compressor (note no image data is actually written here)
+ jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
+
+ // Copy to the output file any extra markers that we want to preserve
+ jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);
+
+ // Execute image transformation, if any
+ jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
+
+ // Finish compression and release memory
+ jpeg_finish_compress(&dstinfo);
+ jpeg_destroy_compress(&dstinfo);
+ jpeg_finish_decompress(&srcinfo);
+ jpeg_destroy_decompress(&srcinfo);
+
+ }
+ catch(...) {
+ jpeg_destroy_compress(&dstinfo);
+ jpeg_destroy_decompress(&srcinfo);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+// FreeImage interface
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
+ return JPEGTransformFromHandle(src_io, src_handle, dst_io, dst_handle, operation, left, top, right, bottom, perfect);
+}
+
+static void
+closeStdIO(fi_handle src_handle, fi_handle dst_handle) {
+ if(src_handle) {
+ fclose((FILE*)src_handle);
+ }
+ if(dst_handle && (dst_handle != src_handle)) {
+ fclose((FILE*)dst_handle);
+ }
+}
+
+static BOOL
+openStdIO(const char* src_file, const char* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
+ *src_handle = NULL;
+ *dst_handle = NULL;
+
+ FreeImageIO io;
+ SetDefaultIO (&io);
+
+ const BOOL isSameFile = (dst_file && (strcmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
+
+ FILE* srcp = NULL;
+ FILE* dstp = NULL;
+
+ if(isSameFile) {
+ srcp = fopen(src_file, "r+b");
+ dstp = srcp;
+ }
+ else {
+ srcp = fopen(src_file, "rb");
+ if(dst_file) {
+ dstp = fopen(dst_file, "wb");
+ }
+ }
+
+ if(!srcp || (dst_file && !dstp)) {
+ if(!srcp) {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for reading", src_file);
+ } else {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for writing", dst_file);
+ }
+ closeStdIO(srcp, dstp);
+ return FALSE;
+ }
+
+ if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
+ FreeImage_OutputMessageProc(FIF_JPEG, " Source file \"%s\" is not jpeg", src_file);
+ closeStdIO(srcp, dstp);
+ return FALSE;
+ }
+
+ *dst_io = io;
+ *src_handle = srcp;
+ *dst_handle = dstp;
+
+ return TRUE;
+}
+
+static BOOL
+openStdIOU(const wchar_t* src_file, const wchar_t* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
+#ifdef _WIN32
+
+ *src_handle = NULL;
+ *dst_handle = NULL;
+
+ FreeImageIO io;
+ SetDefaultIO (&io);
+
+ const BOOL isSameFile = (dst_file && (wcscmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
+
+ FILE* srcp = NULL;
+ FILE* dstp = NULL;
+
+ if(isSameFile) {
+ srcp = _wfopen(src_file, L"r+b");
+ dstp = srcp;
+ } else {
+ srcp = _wfopen(src_file, L"rb");
+ if(dst_file) {
+ dstp = _wfopen(dst_file, L"wb");
+ }
+ }
+
+ if(!srcp || (dst_file && !dstp)) {
+ if(!srcp) {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open source file for reading");
+ } else {
+ FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open destination file for writing");
+ }
+ closeStdIO(srcp, dstp);
+ return FALSE;
+ }
+
+ if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
+ FreeImage_OutputMessageProc(FIF_JPEG, " Source file is not jpeg");
+ closeStdIO(srcp, dstp);
+ return FALSE;
+ }
+
+ *dst_io = io;
+ *src_handle = srcp;
+ *dst_handle = dstp;
+
+ return TRUE;
+
+#else
+ return FALSE;
+#endif // _WIN32
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransformCombined(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransformCombinedU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
+
+ closeStdIO(src, dst);
+
+ return ret;
+}
+
+// --------------------------------------------------------------------------
+
+static BOOL
+getMemIO(FIMEMORY* src_stream, FIMEMORY* dst_stream, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
+ *src_handle = NULL;
+ *dst_handle = NULL;
+
+ FreeImageIO io;
+ SetMemoryIO (&io);
+
+ if(dst_stream) {
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(dst_stream->data);
+ if(mem_header->delete_me != TRUE) {
+ // do not save in a user buffer
+ FreeImage_OutputMessageProc(FIF_JPEG, "Destination memory buffer is read only");
+ return FALSE;
+ }
+ }
+
+ *dst_io = io;
+ *src_handle = src_stream;
+ *dst_handle = dst_stream;
+
+ return TRUE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_JPEGTransformCombinedFromMemory(FIMEMORY* src_stream, FIMEMORY* dst_stream, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
+ FreeImageIO io;
+ fi_handle src;
+ fi_handle dst;
+
+ if(!getMemIO(src_stream, dst_stream, &io, &src, &dst)) {
+ return FALSE;
+ }
+
+ return FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
+}
+
diff --git a/libs/freeimage/src/FreeImageToolkit/MultigridPoissonSolver.cpp b/libs/freeimage/src/FreeImageToolkit/MultigridPoissonSolver.cpp
new file mode 100644
index 0000000000..8852156c0e
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/MultigridPoissonSolver.cpp
@@ -0,0 +1,503 @@
+// ==========================================================
+// Poisson solver based on a full multigrid algorithm
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// Reference:
+// PRESS, W. H., TEUKOLSKY, S. A., VETTERLING, W. T., AND FLANNERY, B. P.
+// 1992. Numerical Recipes in C: The Art of Scientific Computing, 2nd ed. Cambridge University Press.
+//
+// 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!
+// ==========================================================
+
+#include "../stdafx.h"
+
+static const int NPRE = 1; // Number of relaxation sweeps before ...
+static const int NPOST = 1; // ... and after the coarse-grid correction is computed
+static const int NGMAX = 15; // Maximum number of grids
+
+/**
+Copy src into dst
+*/
+static inline void fmg_copyArray(FIBITMAP *dst, FIBITMAP *src) {
+ memcpy(FreeImage_GetBits(dst), FreeImage_GetBits(src), FreeImage_GetHeight(dst) * FreeImage_GetPitch(dst));
+}
+
+/**
+Fills src with zeros
+*/
+static inline void fmg_fillArrayWithZeros(FIBITMAP *src) {
+ memset(FreeImage_GetBits(src), 0, FreeImage_GetHeight(src) * FreeImage_GetPitch(src));
+}
+
+/**
+Half-weighting restriction. nc is the coarse-grid dimension. The fine-grid solution is input in
+uf[0..2*nc-2][0..2*nc-2], the coarse-grid solution is returned in uc[0..nc-1][0..nc-1].
+*/
+static void fmg_restrict(FIBITMAP *UC, FIBITMAP *UF, int nc) {
+ int row_uc, row_uf, col_uc, col_uf;
+
+ const int uc_pitch = FreeImage_GetPitch(UC) / sizeof(float);
+ const int uf_pitch = FreeImage_GetPitch(UF) / sizeof(float);
+
+ float *uc_bits = (float*)FreeImage_GetBits(UC);
+ const float *uf_bits = (float*)FreeImage_GetBits(UF);
+
+ // interior points
+ {
+ float *uc_scan = uc_bits + uc_pitch;
+ for (row_uc = 1, row_uf = 2; row_uc < nc-1; row_uc++, row_uf += 2) {
+ const float *uf_scan = uf_bits + row_uf * uf_pitch;
+ for (col_uc = 1, col_uf = 2; col_uc < nc-1; col_uc++, col_uf += 2) {
+ // calculate
+ // UC(row_uc, col_uc) =
+ // 0.5 * UF(row_uf, col_uf) + 0.125 * [ UF(row_uf+1, col_uf) + UF(row_uf-1, col_uf) + UF(row_uf, col_uf+1) + UF(row_uf, col_uf-1) ]
+ float *uc_pixel = uc_scan + col_uc;
+ const float *uf_center = uf_scan + col_uf;
+ *uc_pixel = 0.5F * *uf_center + 0.125F * ( *(uf_center + uf_pitch) + *(uf_center - uf_pitch) + *(uf_center + 1) + *(uf_center - 1) );
+ }
+ uc_scan += uc_pitch;
+ }
+ }
+ // boundary points
+ const int ncc = 2*nc-1;
+ {
+ /*
+ calculate the following:
+ for (row_uc = 0, row_uf = 0; row_uc < nc; row_uc++, row_uf += 2) {
+ UC(row_uc, 0) = UF(row_uf, 0);
+ UC(row_uc, nc-1) = UF(row_uf, ncc-1);
+ }
+ */
+ float *uc_scan = uc_bits;
+ for (row_uc = 0, row_uf = 0; row_uc < nc; row_uc++, row_uf += 2) {
+ const float *uf_scan = uf_bits + row_uf * uf_pitch;
+ uc_scan[0] = uf_scan[0];
+ uc_scan[nc-1] = uf_scan[ncc-1];
+ uc_scan += uc_pitch;
+ }
+ }
+ {
+ /*
+ calculate the following:
+ for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
+ UC(0, col_uc) = UF(0, col_uf);
+ UC(nc-1, col_uc) = UF(ncc-1, col_uf);
+ }
+ */
+ float *uc_scan_top = uc_bits;
+ float *uc_scan_bottom = uc_bits + (nc-1)*uc_pitch;
+ const float *uf_scan_top = uf_bits + (ncc-1)*uf_pitch;
+ const float *uf_scan_bottom = uf_bits;
+ for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
+ uc_scan_top[col_uc] = uf_scan_top[col_uf];
+ uc_scan_bottom[col_uc] = uf_scan_bottom[col_uf];
+ }
+ }
+}
+
+/**
+Solution of the model problem on the coarsest grid, where h = 1/2 .
+The right-hand side is input
+in rhs[0..2][0..2] and the solution is returned in u[0..2][0..2].
+*/
+static void fmg_solve(FIBITMAP *U, FIBITMAP *RHS) {
+ // fill U with zeros
+ fmg_fillArrayWithZeros(U);
+ // calculate U(1, 1) = -h*h*RHS(1, 1)/4.0 where h = 1/2
+ float *u_scan = (float*)FreeImage_GetScanLine(U, 1);
+ const float *rhs_scan = (float*)FreeImage_GetScanLine(RHS, 1);
+ u_scan[1] = -rhs_scan[1] / 16;
+}
+
+/**
+Coarse-to-fine prolongation by bilinear interpolation. nf is the fine-grid dimension. The coarsegrid
+solution is input as uc[0..nc-1][0..nc-1], where nc = nf/2 + 1. The fine-grid solution is
+returned in uf[0..nf-1][0..nf-1].
+*/
+static void fmg_prolongate(FIBITMAP *UF, FIBITMAP *UC, int nf) {
+ int row_uc, row_uf, col_uc, col_uf;
+
+ const int uf_pitch = FreeImage_GetPitch(UF) / sizeof(float);
+ const int uc_pitch = FreeImage_GetPitch(UC) / sizeof(float);
+
+ float *uf_bits = (float*)FreeImage_GetBits(UF);
+ const float *uc_bits = (float*)FreeImage_GetBits(UC);
+
+ // do elements that are copies
+ {
+ const int nc = nf/2 + 1;
+
+ float *uf_scan = uf_bits;
+ const float *uc_scan = uc_bits;
+ for (row_uc = 0; row_uc < nc; row_uc++) {
+ for (col_uc = 0, col_uf = 0; col_uc < nc; col_uc++, col_uf += 2) {
+ // calculate UF(2*row_uc, col_uf) = UC(row_uc, col_uc);
+ uf_scan[col_uf] = uc_scan[col_uc];
+ }
+ uc_scan += uc_pitch;
+ uf_scan += 2 * uf_pitch;
+ }
+ }
+ // do odd-numbered columns, interpolating vertically
+ {
+ for(row_uf = 1; row_uf < nf-1; row_uf += 2) {
+ float *uf_scan = uf_bits + row_uf * uf_pitch;
+ for (col_uf = 0; col_uf < nf; col_uf += 2) {
+ // calculate UF(row_uf, col_uf) = 0.5 * ( UF(row_uf+1, col_uf) + UF(row_uf-1, col_uf) )
+ uf_scan[col_uf] = 0.5F * ( *(uf_scan + uf_pitch + col_uf) + *(uf_scan - uf_pitch + col_uf) );
+ }
+ }
+ }
+ // do even-numbered columns, interpolating horizontally
+ {
+ float *uf_scan = uf_bits;
+ for(row_uf = 0; row_uf < nf; row_uf++) {
+ for (col_uf = 1; col_uf < nf-1; col_uf += 2) {
+ // calculate UF(row_uf, col_uf) = 0.5 * ( UF(row_uf, col_uf+1) + UF(row_uf, col_uf-1) )
+ uf_scan[col_uf] = 0.5F * ( uf_scan[col_uf + 1] + uf_scan[col_uf - 1] );
+ }
+ uf_scan += uf_pitch;
+ }
+ }
+}
+
+/**
+Red-black Gauss-Seidel relaxation for model problem. Updates the current value of the solution
+u[0..n-1][0..n-1], using the right-hand side function rhs[0..n-1][0..n-1].
+*/
+static void fmg_relaxation(FIBITMAP *U, FIBITMAP *RHS, int n) {
+ int row, col, ipass, isw, jsw;
+ const float h = 1.0F / (n - 1);
+ const float h2 = h*h;
+
+ const int u_pitch = FreeImage_GetPitch(U) / sizeof(float);
+ const int rhs_pitch = FreeImage_GetPitch(RHS) / sizeof(float);
+
+ float *u_bits = (float*)FreeImage_GetBits(U);
+ const float *rhs_bits = (float*)FreeImage_GetBits(RHS);
+
+ for (ipass = 0, jsw = 1; ipass < 2; ipass++, jsw = 3-jsw) { // Red and black sweeps
+ float *u_scan = u_bits + u_pitch;
+ const float *rhs_scan = rhs_bits + rhs_pitch;
+ for (row = 1, isw = jsw; row < n-1; row++, isw = 3-isw) {
+ for (col = isw; col < n-1; col += 2) {
+ // Gauss-Seidel formula
+ // calculate U(row, col) =
+ // 0.25 * [ U(row+1, col) + U(row-1, col) + U(row, col+1) + U(row, col-1) - h2 * RHS(row, col) ]
+ float *u_center = u_scan + col;
+ const float *rhs_center = rhs_scan + col;
+ *u_center = *(u_center + u_pitch) + *(u_center - u_pitch) + *(u_center + 1) + *(u_center - 1);
+ *u_center -= h2 * *rhs_center;
+ *u_center *= 0.25F;
+ }
+ u_scan += u_pitch;
+ rhs_scan += rhs_pitch;
+ }
+ }
+}
+
+/**
+Returns minus the residual for the model problem. Input quantities are u[0..n-1][0..n-1] and
+rhs[0..n-1][0..n-1], while res[0..n-1][0..n-1] is returned.
+*/
+static void fmg_residual(FIBITMAP *RES, FIBITMAP *U, FIBITMAP *RHS, int n) {
+ int row, col;
+
+ const float h = 1.0F / (n-1);
+ const float h2i = 1.0F / (h*h);
+
+ const int res_pitch = FreeImage_GetPitch(RES) / sizeof(float);
+ const int u_pitch = FreeImage_GetPitch(U) / sizeof(float);
+ const int rhs_pitch = FreeImage_GetPitch(RHS) / sizeof(float);
+
+ float *res_bits = (float*)FreeImage_GetBits(RES);
+ const float *u_bits = (float*)FreeImage_GetBits(U);
+ const float *rhs_bits = (float*)FreeImage_GetBits(RHS);
+
+ // interior points
+ {
+ float *res_scan = res_bits + res_pitch;
+ const float *u_scan = u_bits + u_pitch;
+ const float *rhs_scan = rhs_bits + rhs_pitch;
+ for (row = 1; row < n-1; row++) {
+ for (col = 1; col < n-1; col++) {
+ // calculate RES(row, col) =
+ // -h2i * [ U(row+1, col) + U(row-1, col) + U(row, col+1) + U(row, col-1) - 4 * U(row, col) ] + RHS(row, col);
+ float *res_center = res_scan + col;
+ const float *u_center = u_scan + col;
+ const float *rhs_center = rhs_scan + col;
+ *res_center = *(u_center + u_pitch) + *(u_center - u_pitch) + *(u_center + 1) + *(u_center - 1) - 4 * *u_center;
+ *res_center *= -h2i;
+ *res_center += *rhs_center;
+ }
+ res_scan += res_pitch;
+ u_scan += u_pitch;
+ rhs_scan += rhs_pitch;
+ }
+ }
+
+ // boundary points
+ {
+ memset(FreeImage_GetScanLine(RES, 0), 0, FreeImage_GetPitch(RES));
+ memset(FreeImage_GetScanLine(RES, n-1), 0, FreeImage_GetPitch(RES));
+ float *left = res_bits;
+ float *right = res_bits + (n-1);
+ for(int k = 0; k < n; k++) {
+ *left = 0;
+ *right = 0;
+ left += res_pitch;
+ right += res_pitch;
+ }
+ }
+}
+
+/**
+Does coarse-to-fine interpolation and adds result to uf. nf is the fine-grid dimension. The
+coarse-grid solution is input as uc[0..nc-1][0..nc-1], where nc = nf/2+1. The fine-grid solution
+is returned in uf[0..nf-1][0..nf-1]. res[0..nf-1][0..nf-1] is used for temporary storage.
+*/
+static void fmg_addint(FIBITMAP *UF, FIBITMAP *UC, FIBITMAP *RES, int nf) {
+ fmg_prolongate(RES, UC, nf);
+
+ const int uf_pitch = FreeImage_GetPitch(UF) / sizeof(float);
+ const int res_pitch = FreeImage_GetPitch(RES) / sizeof(float);
+
+ float *uf_bits = (float*)FreeImage_GetBits(UF);
+ const float *res_bits = (float*)FreeImage_GetBits(RES);
+
+ for(int row = 0; row < nf; row++) {
+ for(int col = 0; col < nf; col++) {
+ // calculate UF(row, col) = UF(row, col) + RES(row, col);
+ uf_bits[col] += res_bits[col];
+ }
+ uf_bits += uf_pitch;
+ res_bits += res_pitch;
+ }
+}
+
+/**
+Full Multigrid Algorithm for solution of linear elliptic equation, here the model problem (19.0.6).
+On input u[0..n-1][0..n-1] contains the right-hand side c, while on output it returns the solution.
+The dimension n must be of the form 2^j + 1 for some integer j. (j is actually the number of
+grid levels used in the solution, called ng below.) ncycle is the number of V-cycles to be
+used at each level.
+*/
+static BOOL fmg_mglin(FIBITMAP *U, int n, int ncycle) {
+ int j, jcycle, jj, jpost, jpre, nf, ngrid;
+
+ FIBITMAP **IRHO = NULL;
+ FIBITMAP **IU = NULL;
+ FIBITMAP **IRHS = NULL;
+ FIBITMAP **IRES = NULL;
+
+ int ng = 0; // number of allocated grids
+
+// --------------------------------------------------------------------------
+
+#define _CREATE_ARRAY_GRID_(array, array_size) \
+ array = (FIBITMAP**)malloc(array_size * sizeof(FIBITMAP*));\
+ if(!array) throw(1);\
+ memset(array, 0, array_size * sizeof(FIBITMAP*))
+
+#define _FREE_ARRAY_GRID_(array, array_size) \
+ if(NULL != array) {\
+ for(int k = 0; k < array_size; k++) {\
+ if(NULL != array[k]) {\
+ FreeImage_Unload(array[k]); array[k] = NULL;\
+ }\
+ }\
+ free(array);\
+ }
+
+// --------------------------------------------------------------------------
+
+ try {
+ int nn = n;
+ // check grid size and grid levels
+ while (nn >>= 1) ng++;
+ if (n != 1 + (1L << ng)) {
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, "Multigrid algorithm: n = %d, while n-1 must be a power of 2.", n);
+ throw(1);
+ }
+ if (ng > NGMAX) {
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, "Multigrid algorithm: ng = %d while NGMAX = %d, increase NGMAX.", ng, NGMAX);
+ throw(1);
+ }
+ // allocate grid arrays
+ {
+ _CREATE_ARRAY_GRID_(IRHO, ng);
+ _CREATE_ARRAY_GRID_(IU, ng);
+ _CREATE_ARRAY_GRID_(IRHS, ng);
+ _CREATE_ARRAY_GRID_(IRES, ng);
+ }
+
+ nn = n/2 + 1;
+ ngrid = ng - 2;
+
+ // allocate storage for r.h.s. on grid (ng - 2) ...
+ IRHO[ngrid] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+ if(!IRHO[ngrid]) throw(1);
+
+ // ... and fill it by restricting from the fine grid
+ fmg_restrict(IRHO[ngrid], U, nn);
+
+ // similarly allocate storage and fill r.h.s. on all coarse grids.
+ while (nn > 3) {
+ nn = nn/2 + 1;
+ ngrid--;
+ IRHO[ngrid] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+ if(!IRHO[ngrid]) throw(1);
+ fmg_restrict(IRHO[ngrid], IRHO[ngrid+1], nn);
+ }
+
+ nn = 3;
+
+ IU[0] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+ if(!IU[0]) throw(1);
+ IRHS[0] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+ if(!IRHS[0]) throw(1);
+
+ // initial solution on coarsest grid
+ fmg_solve(IU[0], IRHO[0]);
+ // irho[0] no longer needed ...
+ FreeImage_Unload(IRHO[0]); IRHO[0] = NULL;
+
+ ngrid = ng;
+
+ // nested iteration loop
+ for (j = 1; j < ngrid; j++) {
+ nn = 2*nn - 1;
+
+ IU[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+ if(!IU[j]) throw(1);
+ IRHS[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+ if(!IRHS[j]) throw(1);
+ IRES[j] = FreeImage_AllocateT(FIT_FLOAT, nn, nn);
+ if(!IRES[j]) throw(1);
+
+ fmg_prolongate(IU[j], IU[j-1], nn);
+
+ // interpolate from coarse grid to next finer grid
+
+ // set up r.h.s.
+ fmg_copyArray(IRHS[j], j != (ngrid - 1) ? IRHO[j] : U);
+
+ // V-cycle loop
+ for (jcycle = 0; jcycle < ncycle; jcycle++) {
+ nf = nn;
+ // downward stoke of the V
+ for (jj = j; jj >= 1; jj--) {
+ // pre-smoothing
+ for (jpre = 0; jpre < NPRE; jpre++) {
+ fmg_relaxation(IU[jj], IRHS[jj], nf);
+ }
+ fmg_residual(IRES[jj], IU[jj], IRHS[jj], nf);
+ nf = nf/2 + 1;
+ // restriction of the residual is the next r.h.s.
+ fmg_restrict(IRHS[jj-1], IRES[jj], nf);
+ // zero for initial guess in next relaxation
+ fmg_fillArrayWithZeros(IU[jj-1]);
+ }
+ // bottom of V: solve on coarsest grid
+ fmg_solve(IU[0], IRHS[0]);
+ nf = 3;
+ // upward stroke of V.
+ for (jj = 1; jj <= j; jj++) {
+ nf = 2*nf - 1;
+ // use res for temporary storage inside addint
+ fmg_addint(IU[jj], IU[jj-1], IRES[jj], nf);
+ // post-smoothing
+ for (jpost = 0; jpost < NPOST; jpost++) {
+ fmg_relaxation(IU[jj], IRHS[jj], nf);
+ }
+ }
+ }
+ }
+
+ // return solution in U
+ fmg_copyArray(U, IU[ngrid-1]);
+
+ // delete allocated arrays
+ _FREE_ARRAY_GRID_(IRES, ng);
+ _FREE_ARRAY_GRID_(IRHS, ng);
+ _FREE_ARRAY_GRID_(IU, ng);
+ _FREE_ARRAY_GRID_(IRHO, ng);
+
+ return TRUE;
+
+ } catch(int) {
+ // delete allocated arrays
+ _FREE_ARRAY_GRID_(IRES, ng);
+ _FREE_ARRAY_GRID_(IRHS, ng);
+ _FREE_ARRAY_GRID_(IU, ng);
+ _FREE_ARRAY_GRID_(IRHO, ng);
+
+ return FALSE;
+ }
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Poisson solver based on a multigrid algorithm.
+This routine solves a Poisson equation, remap result pixels to [0..1] and returns the solution.
+NB: The input image is first stored inside a square image whose size is (2^j + 1)x(2^j + 1) for some integer j,
+where j is such that 2^j is the nearest larger dimension corresponding to MAX(image width, image height).
+@param Laplacian Laplacian image
+@param ncycle Number of cycles in the multigrid algorithm (usually 2 or 3)
+@return Returns the solved PDE equations if successful, returns NULL otherwise
+*/
+FIBITMAP* DLL_CALLCONV
+FreeImage_MultigridPoissonSolver(FIBITMAP *Laplacian, int ncycle) {
+ if(!FreeImage_HasPixels(Laplacian)) return NULL;
+
+ int width = FreeImage_GetWidth(Laplacian);
+ int height = FreeImage_GetHeight(Laplacian);
+
+ // get nearest larger dimension length that is acceptable by the algorithm
+ int n = MAX(width, height);
+ int size = 0;
+ while((n >>= 1) > 0) size++;
+ if((1 << size) < MAX(width, height)) {
+ size++;
+ }
+ // size must be of the form 2^j + 1 for some integer j
+ size = 1 + (1 << size);
+
+ // allocate a temporary square image I
+ FIBITMAP *I = FreeImage_AllocateT(FIT_FLOAT, size, size);
+ if(!I) return NULL;
+
+ // copy Laplacian into I and shift pixels to create a boundary
+ FreeImage_Paste(I, Laplacian, 1, 1, 255);
+
+ // solve the PDE equation
+ fmg_mglin(I, size, ncycle);
+
+ // shift pixels back
+ FIBITMAP *U = FreeImage_Copy(I, 1, 1, width + 1, height + 1);
+ FreeImage_Unload(I);
+
+ // remap pixels to [0..1]
+ NormalizeY(U, 0, 1);
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(U, Laplacian);
+
+ // return the integrated image
+ return U;
+}
+
diff --git a/libs/freeimage/src/FreeImageToolkit/Rescale.cpp b/libs/freeimage/src/FreeImageToolkit/Rescale.cpp
new file mode 100644
index 0000000000..aeec14ea6a
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/Rescale.cpp
@@ -0,0 +1,193 @@
+// ==========================================================
+// Upsampling / downsampling routine
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Carsten Klein (cklein05@users.sourceforge.net)
+//
+// 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!
+// ==========================================================
+
+#include "../stdafx.h"
+#include "Resize.h"
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_RescaleRect(FIBITMAP *src, int dst_width, int dst_height, int src_left, int src_top, int src_right, int src_bottom, FREE_IMAGE_FILTER filter, unsigned flags) {
+ FIBITMAP *dst = NULL;
+
+ const int src_width = FreeImage_GetWidth(src);
+ const int src_height = FreeImage_GetHeight(src);
+
+ if (!FreeImage_HasPixels(src) || (dst_width <= 0) || (dst_height <= 0) || (src_width <= 0) || (src_height <= 0)) {
+ return NULL;
+ }
+
+ // normalize the rectangle
+ if (src_right < src_left) {
+ INPLACESWAP(src_left, src_right);
+ }
+ if (src_bottom < src_top) {
+ INPLACESWAP(src_top, src_bottom);
+ }
+
+ // check the size of the sub image
+ if((src_left < 0) || (src_right > src_width) || (src_top < 0) || (src_bottom > src_height)) {
+ return NULL;
+ }
+
+ // select the filter
+ CGenericFilter *pFilter = NULL;
+ switch (filter) {
+ case FILTER_BOX:
+ pFilter = new(std::nothrow) CBoxFilter();
+ break;
+ case FILTER_BICUBIC:
+ pFilter = new(std::nothrow) CBicubicFilter();
+ break;
+ case FILTER_BILINEAR:
+ pFilter = new(std::nothrow) CBilinearFilter();
+ break;
+ case FILTER_BSPLINE:
+ pFilter = new(std::nothrow) CBSplineFilter();
+ break;
+ case FILTER_CATMULLROM:
+ pFilter = new(std::nothrow) CCatmullRomFilter();
+ break;
+ case FILTER_LANCZOS3:
+ pFilter = new(std::nothrow) CLanczos3Filter();
+ break;
+ }
+
+ if (!pFilter) {
+ return NULL;
+ }
+
+ CResizeEngine Engine(pFilter);
+
+ dst = Engine.scale(src, dst_width, dst_height, src_left, src_top,
+ src_right - src_left, src_bottom - src_top, flags);
+
+ delete pFilter;
+
+ if ((flags & FI_RESCALE_OMIT_METADATA) != FI_RESCALE_OMIT_METADATA) {
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+ }
+
+ return dst;
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_Rescale(FIBITMAP *src, int dst_width, int dst_height, FREE_IMAGE_FILTER filter) {
+ return FreeImage_RescaleRect(src, dst_width, dst_height, 0, 0, FreeImage_GetWidth(src), FreeImage_GetHeight(src), filter, FI_RESCALE_DEFAULT);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_MakeThumbnail(FIBITMAP *dib, int max_pixel_size, BOOL convert) {
+ FIBITMAP *thumbnail = NULL;
+ int new_width, new_height;
+
+ if(!FreeImage_HasPixels(dib) || (max_pixel_size <= 0)) return NULL;
+
+ int width = FreeImage_GetWidth(dib);
+ int height = FreeImage_GetHeight(dib);
+
+ if(max_pixel_size == 0) max_pixel_size = 1;
+
+ if((width < max_pixel_size) && (height < max_pixel_size)) {
+ // image is smaller than the requested thumbnail
+ return FreeImage_Clone(dib);
+ }
+
+ if(width > height) {
+ new_width = max_pixel_size;
+ // change image height with the same ratio
+ double ratio = ((double)new_width / (double)width);
+ new_height = (int)(height * ratio + 0.5);
+ if(new_height == 0) new_height = 1;
+ } else {
+ new_height = max_pixel_size;
+ // change image width with the same ratio
+ double ratio = ((double)new_height / (double)height);
+ new_width = (int)(width * ratio + 0.5);
+ if(new_width == 0) new_width = 1;
+ }
+
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+ // perform downsampling using a bilinear interpolation
+
+ switch(image_type) {
+ case FIT_BITMAP:
+ case FIT_UINT16:
+ case FIT_RGB16:
+ case FIT_RGBA16:
+ case FIT_FLOAT:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ {
+ FREE_IMAGE_FILTER filter = FILTER_BILINEAR;
+ thumbnail = FreeImage_Rescale(dib, new_width, new_height, filter);
+ }
+ break;
+
+ case FIT_INT16:
+ case FIT_UINT32:
+ case FIT_INT32:
+ case FIT_DOUBLE:
+ case FIT_COMPLEX:
+ default:
+ // cannot rescale this kind of image
+ thumbnail = NULL;
+ break;
+ }
+
+ if((thumbnail != NULL) && (image_type != FIT_BITMAP) && convert) {
+ // convert to a standard bitmap
+ FIBITMAP *bitmap = NULL;
+ switch(image_type) {
+ case FIT_UINT16:
+ bitmap = FreeImage_ConvertTo8Bits(thumbnail);
+ break;
+ case FIT_RGB16:
+ bitmap = FreeImage_ConvertTo24Bits(thumbnail);
+ break;
+ case FIT_RGBA16:
+ bitmap = FreeImage_ConvertTo32Bits(thumbnail);
+ break;
+ case FIT_FLOAT:
+ bitmap = FreeImage_ConvertToStandardType(thumbnail, TRUE);
+ break;
+ case FIT_RGBF:
+ bitmap = FreeImage_ToneMapping(thumbnail, FITMO_DRAGO03);
+ break;
+ case FIT_RGBAF:
+ // no way to keep the transparency yet ...
+ FIBITMAP *rgbf = FreeImage_ConvertToRGBF(thumbnail);
+ bitmap = FreeImage_ToneMapping(rgbf, FITMO_DRAGO03);
+ FreeImage_Unload(rgbf);
+ break;
+ }
+ if(bitmap != NULL) {
+ FreeImage_Unload(thumbnail);
+ thumbnail = bitmap;
+ }
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(thumbnail, dib);
+
+ return thumbnail;
+}
diff --git a/libs/freeimage/src/FreeImageToolkit/Resize.cpp b/libs/freeimage/src/FreeImageToolkit/Resize.cpp
new file mode 100644
index 0000000000..82edb9da72
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/Resize.cpp
@@ -0,0 +1,2117 @@
+// ==========================================================
+// Upsampling / downsampling classes
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Carsten Klein (cklein05@users.sourceforge.net)
+//
+// 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!
+// ==========================================================
+
+#include "../stdafx.h"
+#include "Resize.h"
+
+/**
+Returns the color type of a bitmap. In contrast to FreeImage_GetColorType,
+this function optionally supports a boolean OUT parameter, that receives TRUE,
+if the specified bitmap is greyscale, that is, it consists of grey colors only.
+Although it returns the same value as returned by FreeImage_GetColorType for all
+image types, this extended function primarily is intended for palletized images,
+since the boolean pointed to by 'bIsGreyscale' remains unchanged for RGB(A/F)
+images. However, the outgoing boolean is properly maintained for palletized images,
+as well as for any non-RGB image type, like FIT_UINTxx and FIT_DOUBLE, for example.
+@param dib A pointer to a FreeImage bitmap to calculate the extended color type for
+@param bIsGreyscale A pointer to a boolean, that receives TRUE, if the specified bitmap
+is greyscale, that is, it consists of grey colors only. This parameter can be NULL.
+@return the color type of the specified bitmap
+*/
+static FREE_IMAGE_COLOR_TYPE
+GetExtendedColorType(FIBITMAP *dib, BOOL *bIsGreyscale) {
+ const unsigned bpp = FreeImage_GetBPP(dib);
+ const unsigned size = CalculateUsedPaletteEntries(bpp);
+ const RGBQUAD * const pal = FreeImage_GetPalette(dib);
+ FREE_IMAGE_COLOR_TYPE color_type = FIC_MINISBLACK;
+ BOOL bIsGrey = TRUE;
+
+ switch (bpp) {
+ case 1:
+ {
+ for (unsigned i = 0; i < size; i++) {
+ if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
+ color_type = FIC_PALETTE;
+ bIsGrey = FALSE;
+ break;
+ }
+ }
+ if (bIsGrey) {
+ if (pal[0].rgbBlue == 255 && pal[1].rgbBlue == 0) {
+ color_type = FIC_MINISWHITE;
+ } else if (pal[0].rgbBlue != 0 || pal[1].rgbBlue != 255) {
+ color_type = FIC_PALETTE;
+ }
+ }
+ break;
+ }
+
+ case 4:
+ case 8:
+ {
+ for (unsigned i = 0; i < size; i++) {
+ if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
+ color_type = FIC_PALETTE;
+ bIsGrey = FALSE;
+ break;
+ }
+ if (color_type != FIC_PALETTE && pal[i].rgbBlue != i) {
+ if ((size - i - 1) != pal[i].rgbBlue) {
+ color_type = FIC_PALETTE;
+ if (!bIsGreyscale) {
+ // exit loop if we're not setting
+ // bIsGreyscale parameter
+ break;
+ }
+ } else {
+ color_type = FIC_MINISWHITE;
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ color_type = FreeImage_GetColorType(dib);
+ bIsGrey = (color_type == FIC_MINISBLACK) ? TRUE : FALSE;
+ break;
+ }
+
+ }
+ if (bIsGreyscale) {
+ *bIsGreyscale = bIsGrey;
+ }
+
+ return color_type;
+}
+
+/**
+Returns a pointer to an RGBA palette, created from the specified bitmap.
+The RGBA palette is a copy of the specified bitmap's palette, that, additionally
+contains the bitmap's transparency information in the rgbReserved member
+of the palette's RGBQUAD elements.
+@param dib A pointer to a FreeImage bitmap to create the RGBA palette from.
+@param buffer A pointer to the buffer to store the RGBA palette.
+@return A pointer to the newly created RGBA palette or NULL, if the specified
+bitmap is no palletized standard bitmap. If non-NULL, the returned value is
+actually the pointer passed in parameter 'buffer'.
+*/
+static inline RGBQUAD *
+GetRGBAPalette(FIBITMAP *dib, RGBQUAD * const buffer) {
+ // clone the palette
+ const unsigned ncolors = FreeImage_GetColorsUsed(dib);
+ if (ncolors == 0) {
+ return NULL;
+ }
+ memcpy(buffer, FreeImage_GetPalette(dib), ncolors * sizeof(RGBQUAD));
+ // merge the transparency table
+ const unsigned ntransp = MIN(ncolors, FreeImage_GetTransparencyCount(dib));
+ const BYTE * const tt = FreeImage_GetTransparencyTable(dib);
+ for (unsigned i = 0; i < ntransp; i++) {
+ buffer[i].rgbReserved = tt[i];
+ }
+ for (unsigned i = ntransp; i < ncolors; i++) {
+ buffer[i].rgbReserved = 255;
+ }
+ return buffer;
+}
+
+// --------------------------------------------------------------------------
+
+CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) {
+ double dWidth;
+ double dFScale;
+ const double dFilterWidth = pFilter->GetWidth();
+
+ // scale factor
+ const double dScale = double(uDstSize) / double(uSrcSize);
+
+ if(dScale < 1.0) {
+ // minification
+ dWidth = dFilterWidth / dScale;
+ dFScale = dScale;
+ } else {
+ // magnification
+ dWidth = dFilterWidth;
+ dFScale = 1.0;
+ }
+
+ // allocate a new line contributions structure
+ //
+ // window size is the number of sampled pixels
+ m_WindowSize = 2 * (int)ceil(dWidth) + 1;
+ // length of dst line (no. of rows / cols)
+ m_LineLength = uDstSize;
+
+ // allocate list of contributions
+ m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
+ for(unsigned u = 0; u < m_LineLength; u++) {
+ // allocate contributions for every pixel
+ m_WeightTable[u].Weights = (double*)malloc(m_WindowSize * sizeof(double));
+ }
+
+ // offset for discrete to continuous coordinate conversion
+ const double dOffset = (0.5 / dScale);
+
+ for(unsigned u = 0; u < m_LineLength; u++) {
+ // scan through line of contributions
+
+ // inverse mapping (discrete dst 'u' to continous src 'dCenter')
+ const double dCenter = (double)u / dScale + dOffset;
+
+ // find the significant edge points that affect the pixel
+ const int iLeft = MAX(0, (int)(dCenter - dWidth + 0.5));
+ const int iRight = MIN((int)(dCenter + dWidth + 0.5), int(uSrcSize));
+
+ m_WeightTable[u].Left = iLeft;
+ m_WeightTable[u].Right = iRight;
+
+ double dTotalWeight = 0; // sum of weights (initialized to zero)
+ for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
+ // calculate weights
+ const double weight = dFScale * pFilter->Filter(dFScale * ((double)iSrc + 0.5 - dCenter));
+ // assert((iSrc-iLeft) < m_WindowSize);
+ m_WeightTable[u].Weights[iSrc-iLeft] = weight;
+ dTotalWeight += weight;
+ }
+ if((dTotalWeight > 0) && (dTotalWeight != 1)) {
+ // normalize weight of neighbouring points
+ for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
+ // normalize point
+ m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight;
+ }
+ }
+
+ // simplify the filter, discarding null weights at the right
+ {
+ int iTrailing = iRight - iLeft - 1;
+ while(m_WeightTable[u].Weights[iTrailing] == 0) {
+ m_WeightTable[u].Right--;
+ iTrailing--;
+ if(m_WeightTable[u].Right == m_WeightTable[u].Left) {
+ break;
+ }
+ }
+
+ }
+
+ } // next dst pixel
+}
+
+CWeightsTable::~CWeightsTable() {
+ for(unsigned u = 0; u < m_LineLength; u++) {
+ // free contributions for every pixel
+ free(m_WeightTable[u].Weights);
+ }
+ // free list of pixels contributions
+ free(m_WeightTable);
+}
+
+// --------------------------------------------------------------------------
+
+FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height, unsigned flags) {
+
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+ const unsigned src_bpp = FreeImage_GetBPP(src);
+
+ // determine the image's color type
+ BOOL bIsGreyscale = FALSE;
+ FREE_IMAGE_COLOR_TYPE color_type;
+ if (src_bpp <= 8) {
+ color_type = GetExtendedColorType(src, &bIsGreyscale);
+ } else {
+ color_type = FIC_RGB;
+ }
+
+ // determine the required bit depth of the destination image
+ unsigned dst_bpp;
+ unsigned dst_bpp_s1 = 0;
+ if (color_type == FIC_PALETTE && !bIsGreyscale) {
+ // non greyscale FIC_PALETTE images require a high-color destination
+ // image (24- or 32-bits depending on the image's transparent state)
+ dst_bpp = FreeImage_IsTransparent(src) ? 32 : 24;
+ } else if (src_bpp <= 8) {
+ // greyscale images require an 8-bit destination image
+ // (or a 32-bit image if the image is transparent);
+ // however, if flag FI_RESCALE_TRUE_COLOR is set, we will return
+ // a true color (24 bpp) image
+ if (FreeImage_IsTransparent(src)) {
+ dst_bpp = 32;
+ // additionally, for transparent images we always need a
+ // palette including transparency information (an RGBA palette)
+ // so, set color_type accordingly
+ color_type = FIC_PALETTE;
+ } else {
+ dst_bpp = ((flags & FI_RESCALE_TRUE_COLOR) == FI_RESCALE_TRUE_COLOR) ? 24 : 8;
+ // in any case, we use a fast 8-bit temporary image for the
+ // first filter operation (stage 1, either horizontal or
+ // vertical) and implicitly convert to 24 bpp (if requested
+ // by flag FI_RESCALE_TRUE_COLOR) during the second filter
+ // operation
+ dst_bpp_s1 = 8;
+ }
+ } else if (src_bpp == 16 && image_type == FIT_BITMAP) {
+ // 16-bit 555 and 565 RGB images require a high-color destination
+ // image (fixed to 24 bits, since 16-bit RGBs don't support
+ // transparency in FreeImage)
+ dst_bpp = 24;
+ } else {
+ // bit depth remains unchanged for all other images
+ dst_bpp = src_bpp;
+ }
+
+ // make 'stage 1' bpp a copy of the destination bpp if it
+ // was not explicitly set
+ if (dst_bpp_s1 == 0) {
+ dst_bpp_s1 = dst_bpp;
+ }
+
+ // early exit if destination size is equal to source size
+ if ((src_width == dst_width) && (src_height == dst_height)) {
+ FIBITMAP *out = src;
+ FIBITMAP *tmp = src;
+ if ((src_width != FreeImage_GetWidth(src)) || (src_height != FreeImage_GetHeight(src))) {
+ out = FreeImage_Copy(tmp, src_left, src_top, src_left + src_width, src_top + src_height);
+ tmp = out;
+ }
+ if (src_bpp != dst_bpp) {
+ switch (dst_bpp) {
+ case 8:
+ out = FreeImage_ConvertToGreyscale(tmp);
+ break;
+ case 24:
+ out = FreeImage_ConvertTo24Bits(tmp);
+ break;
+ case 32:
+ out = FreeImage_ConvertTo32Bits(tmp);
+ break;
+ default:
+ break;
+ }
+ if (tmp != src) {
+ FreeImage_Unload(tmp);
+ tmp = NULL;
+ }
+ }
+
+ return (out != src) ? out : FreeImage_Clone(src);
+ }
+
+ RGBQUAD pal_buffer[256];
+ RGBQUAD *src_pal = NULL;
+
+ // provide the source image's palette to the rescaler for
+ // FIC_PALETTE type images (this includes palletized greyscale
+ // images with an unordered palette as well as transparent images)
+ if (color_type == FIC_PALETTE) {
+ if (dst_bpp == 32) {
+ // a 32-bit destination image signals transparency, so
+ // create an RGBA palette from the source palette
+ src_pal = GetRGBAPalette(src, pal_buffer);
+ } else {
+ src_pal = FreeImage_GetPalette(src);
+ }
+ }
+
+ // allocate the dst image
+ FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, dst_bpp, 0, 0, 0);
+ if (!dst) {
+ return NULL;
+ }
+
+ if (dst_bpp == 8) {
+ RGBQUAD * const dst_pal = FreeImage_GetPalette(dst);
+ if (color_type == FIC_MINISWHITE) {
+ // build an inverted greyscale palette
+ CREATE_GREYSCALE_PALETTE_REVERSE(dst_pal, 256);
+ }
+ /*
+ else {
+ // build a default greyscale palette
+ // Currently, FreeImage_AllocateT already creates a default
+ // greyscale palette for 8 bpp images, so we can skip this here.
+ CREATE_GREYSCALE_PALETTE(dst_pal, 256);
+ }
+ */
+ }
+
+ // calculate x and y offsets; since FreeImage uses bottom-up bitmaps, the
+ // value of src_offset_y is measured from the bottom of the image
+ unsigned src_offset_x = src_left;
+ unsigned src_offset_y = FreeImage_GetHeight(src) - src_height - src_top;
+
+ /*
+ Decide which filtering order (xy or yx) is faster for this mapping.
+ --- The theory ---
+ Try to minimize calculations by counting the number of convolution multiplies
+ if(dst_width*src_height <= src_width*dst_height) {
+ // xy filtering
+ } else {
+ // yx filtering
+ }
+ --- The practice ---
+ Try to minimize calculations by counting the number of vertical convolutions (the most time consuming task)
+ if(dst_width*dst_height <= src_width*dst_height) {
+ // xy filtering
+ } else {
+ // yx filtering
+ }
+ */
+
+ if (dst_width <= src_width) {
+ // xy filtering
+ // -------------
+
+ FIBITMAP *tmp = NULL;
+
+ if (src_width != dst_width) {
+ // source and destination widths are different so, we must
+ // filter horizontally
+ if (src_height != dst_height) {
+ // source and destination heights are also different so, we need
+ // a temporary image
+ tmp = FreeImage_AllocateT(image_type, dst_width, src_height, dst_bpp_s1, 0, 0, 0);
+ if (!tmp) {
+ FreeImage_Unload(dst);
+ return NULL;
+ }
+ } else {
+ // source and destination heights are equal so, we can directly
+ // scale into destination image (second filter method will not
+ // be invoked)
+ tmp = dst;
+ }
+
+ // scale source image horizontally into temporary (or destination) image
+ horizontalFilter(src, src_height, src_width, src_offset_x, src_offset_y, src_pal, tmp, dst_width);
+
+ // set x and y offsets to zero for the second filter method
+ // invocation (the temporary image only contains the portion of
+ // the image to be rescaled with no offsets)
+ src_offset_x = 0;
+ src_offset_y = 0;
+
+ // also ensure, that the second filter method gets no source
+ // palette (the temporary image is palletized only, if it is
+ // greyscale; in that case, it is an 8-bit image with a linear
+ // palette so, the source palette is not needed or will even be
+ // mismatching, if the source palette is unordered)
+ src_pal = NULL;
+ } else {
+ // source and destination widths are equal so, just copy the
+ // image pointer
+ tmp = src;
+ }
+
+ if (src_height != dst_height) {
+ // source and destination heights are different so, scale
+ // temporary (or source) image vertically into destination image
+ verticalFilter(tmp, dst_width, src_height, src_offset_x, src_offset_y, src_pal, dst, dst_height);
+ }
+
+ // free temporary image, if not pointing to either src or dst
+ if (tmp != src && tmp != dst) {
+ FreeImage_Unload(tmp);
+ }
+
+ } else {
+ // yx filtering
+ // -------------
+
+ // Remark:
+ // The yx filtering branch could be more optimized by taking into,
+ // account that (src_width != dst_width) is always true, which
+ // follows from the above condition, which selects filtering order.
+ // Since (dst_width <= src_width) == TRUE selects xy filtering,
+ // both widths must be different when performing yx filtering.
+ // However, to make the code more robust, not depending on that
+ // condition and more symmetric to the xy filtering case, these
+ // (src_width != dst_width) conditions are still in place.
+
+ FIBITMAP *tmp = NULL;
+
+ if (src_height != dst_height) {
+ // source and destination heights are different so, we must
+ // filter vertically
+ if (src_width != dst_width) {
+ // source and destination widths are also different so, we need
+ // a temporary image
+ tmp = FreeImage_AllocateT(image_type, src_width, dst_height, dst_bpp_s1, 0, 0, 0);
+ if (!tmp) {
+ FreeImage_Unload(dst);
+ return NULL;
+ }
+ } else {
+ // source and destination widths are equal so, we can directly
+ // scale into destination image (second filter method will not
+ // be invoked)
+ tmp = dst;
+ }
+
+ // scale source image vertically into temporary (or destination) image
+ verticalFilter(src, src_width, src_height, src_offset_x, src_offset_y, src_pal, tmp, dst_height);
+
+ // set x and y offsets to zero for the second filter method
+ // invocation (the temporary image only contains the portion of
+ // the image to be rescaled with no offsets)
+ src_offset_x = 0;
+ src_offset_y = 0;
+
+ // also ensure, that the second filter method gets no source
+ // palette (the temporary image is palletized only, if it is
+ // greyscale; in that case, it is an 8-bit image with a linear
+ // palette so, the source palette is not needed or will even be
+ // mismatching, if the source palette is unordered)
+ src_pal = NULL;
+
+ } else {
+ // source and destination heights are equal so, just copy the
+ // image pointer
+ tmp = src;
+ }
+
+ if (src_width != dst_width) {
+ // source and destination heights are different so, scale
+ // temporary (or source) image horizontally into destination image
+ horizontalFilter(tmp, dst_height, src_width, src_offset_x, src_offset_y, src_pal, dst, dst_width);
+ }
+
+ // free temporary image, if not pointing to either src or dst
+ if (tmp != src && tmp != dst) {
+ FreeImage_Unload(tmp);
+ }
+ }
+
+ return dst;
+}
+
+void CResizeEngine::horizontalFilter(FIBITMAP *const src, unsigned height, unsigned src_width, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_width) {
+
+ // allocate and calculate the contributions
+ CWeightsTable weightsTable(m_pFilter, dst_width, src_width);
+
+ // step through rows
+ switch(FreeImage_GetImageType(src)) {
+ case FIT_BITMAP:
+ {
+ switch(FreeImage_GetBPP(src)) {
+ case 1:
+ {
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // transparently convert the 1-bit non-transparent greyscale image to 8 bpp
+ src_offset_x >>= 3;
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double value = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+ value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double value = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+ value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
+ }
+ value *= 0xFF;
+
+ // clamp and place result in destination pixel
+ dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 1-bit image to 24 bpp
+ src_offset_x >>= 3;
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i - iLeft);
+ const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double value = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+ value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
+ }
+ value *= 0xFF;
+
+ // clamp and place result in destination pixel
+ const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_RED] = bval;
+ dst_bits[FI_RGBA_GREEN] = bval;
+ dst_bits[FI_RGBA_BLUE] = bval;
+ dst_bits += 3;
+ }
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 1-bit image to 32 bpp;
+ // we always have got a palette here
+ src_offset_x >>= 3;
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i - iLeft);
+ const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += 4;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 4:
+ {
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // transparently convert the non-transparent 4-bit greyscale image to 8 bpp;
+ // we always have got a palette for 4-bit images
+ src_offset_x >>= 1;
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double value = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
+ value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 4-bit image to 24 bpp;
+ // we always have got a palette for 4-bit images
+ src_offset_x >>= 1;
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i - iLeft);
+ const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 4-bit image to 32 bpp;
+ // we always have got a palette for 4-bit images
+ src_offset_x >>= 1;
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i - iLeft);
+ const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += 4;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 8:
+ {
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // scale the 8-bit non-transparent greyscale image
+ // into an 8 bpp destination image
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * const pixel = src_bits + iLeft;
+ double value = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(x, i) * (double)*(BYTE *)&src_pal[pixel[i]]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * const pixel = src_bits + iLeft;
+ double value = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(x, i) * (double)pixel[i]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 8-bit image to 24 bpp
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * const pixel = src_bits + iLeft;
+ double r = 0, g = 0, b = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ const BYTE *const entry = (BYTE *)&src_pal[pixel[i]];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * const pixel = src_bits + iLeft;
+ double value = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ value += (weight * (double)pixel[i]);
+ }
+
+ // clamp and place result in destination pixel
+ const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_RED] = bval;
+ dst_bits[FI_RGBA_GREEN] = bval;
+ dst_bits[FI_RGBA_BLUE] = bval;
+ dst_bits += 3;
+ }
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 8-bit image to 32 bpp;
+ // we always have got a palette here
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * const pixel = src_bits + iLeft;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ const BYTE * const entry = (BYTE *)&src_pal[pixel[i]];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += 4;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 16:
+ {
+ // transparently convert the 16-bit non-transparent image to 24 bpp
+ if (IS_FORMAT_RGB565(src)) {
+ // image has 565 format
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const WORD *pixel = src_bits + iLeft;
+ double r = 0, g = 0, b = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
+ g += (weight * (double)((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
+ b += (weight * (double)((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
+ pixel++;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ } else {
+ // image has 555 format
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const WORD *pixel = src_bits + iLeft;
+ double r = 0, g = 0, b = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
+ g += (weight * (double)((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
+ b += (weight * (double)((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
+ pixel++;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // scale the 24-bit non-transparent image into a 24 bpp destination image
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 3;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE * pixel = src_bits + iLeft * 3;
+ double r = 0, g = 0, b = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)pixel[FI_RGBA_RED]);
+ g += (weight * (double)pixel[FI_RGBA_GREEN]);
+ b += (weight * (double)pixel[FI_RGBA_BLUE]);
+ pixel += 3;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += 3;
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // scale the 32-bit transparent image into a 32 bpp destination image
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 4;
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const BYTE *pixel = src_bits + iLeft * 4;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)pixel[FI_RGBA_RED]);
+ g += (weight * (double)pixel[FI_RGBA_GREEN]);
+ b += (weight * (double)pixel[FI_RGBA_BLUE]);
+ a += (weight * (double)pixel[FI_RGBA_ALPHA]);
+ pixel += 4;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += 4;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case FIT_UINT16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+ WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const WORD *pixel = src_bits + iLeft * wordspp;
+ double value = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ value += (weight * (double)pixel[0]);
+ pixel++;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
+ dst_bits += wordspp;
+ }
+ }
+ }
+ break;
+
+ case FIT_RGB16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+ WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const WORD *pixel = src_bits + iLeft * wordspp;
+ double r = 0, g = 0, b = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)pixel[0]);
+ g += (weight * (double)pixel[1]);
+ b += (weight * (double)pixel[2]);
+ pixel += wordspp;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+ dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+ dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+ dst_bits += wordspp;
+ }
+ }
+ }
+ break;
+
+ case FIT_RGBA16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
+
+ for (unsigned y = 0; y < height; y++) {
+ // scale each row
+ const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
+ WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+
+ for (unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
+ const WORD *pixel = src_bits + iLeft * wordspp;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ // for(i = iLeft to iRight)
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i);
+ r += (weight * (double)pixel[0]);
+ g += (weight * (double)pixel[1]);
+ b += (weight * (double)pixel[2]);
+ a += (weight * (double)pixel[3]);
+ pixel += wordspp;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+ dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+ dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+ dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
+ dst_bits += wordspp;
+ }
+ }
+ }
+ break;
+
+ case FIT_FLOAT:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ {
+ // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
+ const unsigned floatspp = (FreeImage_GetLine(src) / src_width) / sizeof(float);
+
+ for(unsigned y = 0; y < height; y++) {
+ // scale each row
+ const float *src_bits = (float*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(float);
+ float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
+
+ for(unsigned x = 0; x < dst_width; x++) {
+ // loop through row
+ const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
+ double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
+
+ for(unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(x, i-iLeft);
+
+ unsigned index = i * floatspp; // pixel index
+ for (unsigned j = 0; j < floatspp; j++) {
+ value[j] += (weight * (double)src_bits[index++]);
+ }
+ }
+
+ // place result in destination pixel
+ for (unsigned j = 0; j < floatspp; j++) {
+ dst_bits[j] = (float)value[j];
+ }
+
+ dst_bits += floatspp;
+ }
+ }
+ }
+ break;
+ }
+}
+
+/// Performs vertical image filtering
+void CResizeEngine::verticalFilter(FIBITMAP *const src, unsigned width, unsigned src_height, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_height) {
+
+ // allocate and calculate the contributions
+ CWeightsTable weightsTable(m_pFilter, dst_height, src_height);
+
+ // step through columns
+ switch(FreeImage_GetImageType(src)) {
+ case FIT_BITMAP:
+ {
+ const unsigned dst_pitch = FreeImage_GetPitch(dst);
+ BYTE * const dst_base = FreeImage_GetBits(dst);
+
+ switch(FreeImage_GetBPP(src)) {
+ case 1:
+ {
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const BYTE * const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 3);
+
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // transparently convert the 1-bit non-transparent greyscale image to 8 bpp
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x;
+ const unsigned index = x >> 3;
+ const unsigned mask = 0x80 >> (x & 0x07);
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = (*src_bits & mask) != 0;
+ value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
+ src_bits += src_pitch;
+ }
+ value *= 0xFF;
+
+ // clamp and place result in destination pixel
+ *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x;
+ const unsigned index = x >> 3;
+ const unsigned mask = 0x80 >> (x & 0x07);
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
+ src_bits += src_pitch;
+ }
+ value *= 0xFF;
+
+ // clamp and place result in destination pixel
+ *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 1-bit image to 24 bpp
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+ const unsigned index = x >> 3;
+ const unsigned mask = 0x80 >> (x & 0x07);
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const unsigned pixel = (*src_bits & mask) != 0;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+ const unsigned index = x >> 3;
+ const unsigned mask = 0x80 >> (x & 0x07);
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
+ src_bits += src_pitch;
+ }
+ value *= 0xFF;
+
+ // clamp and place result in destination pixel
+ const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_RED] = bval;
+ dst_bits[FI_RGBA_GREEN] = bval;
+ dst_bits[FI_RGBA_BLUE] = bval;
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 1-bit image to 32 bpp;
+ // we always have got a palette here
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 4;
+ const unsigned index = x >> 3;
+ const unsigned mask = 0x80 >> (x & 0x07);
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const unsigned pixel = (*src_bits & mask) != 0;
+ const BYTE * const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 4:
+ {
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 1);
+
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // transparently convert the non-transparent 4-bit greyscale image to 8 bpp;
+ // we always have got a palette for 4-bit images
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x;
+ const unsigned index = x >> 1;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
+ value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 4-bit image to 24 bpp;
+ // we always have got a palette for 4-bit images
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+ const unsigned index = x >> 1;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
+ const BYTE *const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 4-bit image to 32 bpp;
+ // we always have got a palette for 4-bit images
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 4;
+ const unsigned index = x >> 1;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
+ const BYTE *const entry = (BYTE *)&src_pal[pixel];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 8:
+ {
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
+
+ switch(FreeImage_GetBPP(dst)) {
+ case 8:
+ {
+ // scale the 8-bit non-transparent greyscale image into an 8 bpp destination image
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[*src_bits]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(y, i) * (double)*src_bits);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // transparently convert the non-transparent 8-bit image to 24 bpp
+ if (src_pal) {
+ // we have got a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ } else {
+ // we do not have a palette
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ value += (weightsTable.getWeight(y, i) * (double)*src_bits);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_RED] = bval;
+ dst_bits[FI_RGBA_GREEN] = bval;
+ dst_bits[FI_RGBA_BLUE] = bval;
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // transparently convert the transparent 8-bit image to 32 bpp;
+ // we always have got a palette here
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 4;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + x;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
+ r += (weight * (double)entry[FI_RGBA_RED]);
+ g += (weight * (double)entry[FI_RGBA_GREEN]);
+ b += (weight * (double)entry[FI_RGBA_BLUE]);
+ a += (weight * (double)entry[FI_RGBA_ALPHA]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 16:
+ {
+ // transparently convert the 16-bit non-transparent image to 24 bpp
+ const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+ const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
+
+ if (IS_FORMAT_RGB565(src)) {
+ // image has 565 format
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const WORD *src_bits = src_base + iLeft * src_pitch + x;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)((*src_bits & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
+ g += (weight * (double)((*src_bits & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
+ b += (weight * (double)((*src_bits & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ } else {
+ // image has 555 format
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ BYTE *dst_bits = dst_base + x * 3;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const WORD *src_bits = src_base + iLeft * src_pitch + x;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)((*src_bits & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
+ g += (weight * (double)((*src_bits & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
+ b += (weight * (double)((*src_bits & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ }
+ break;
+
+ case 24:
+ {
+ // scale the 24-bit transparent image into a 24 bpp destination image
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 3;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * 3;
+ BYTE *dst_bits = dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)src_bits[FI_RGBA_RED]);
+ g += (weight * (double)src_bits[FI_RGBA_GREEN]);
+ b += (weight * (double)src_bits[FI_RGBA_BLUE]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ // scale the 32-bit transparent image into a 32 bpp destination image
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 4;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * 4;
+ BYTE *dst_bits = dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const BYTE *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)src_bits[FI_RGBA_RED]);
+ g += (weight * (double)src_bits[FI_RGBA_GREEN]);
+ b += (weight * (double)src_bits[FI_RGBA_BLUE]);
+ a += (weight * (double)src_bits[FI_RGBA_ALPHA]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
+ dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int) (a + 0.5), 0, 0xFF);
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case FIT_UINT16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
+
+ const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
+ WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
+
+ const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+ const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * wordspp; // pixel index
+ WORD *dst_bits = dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const WORD *src_bits = src_base + iLeft * src_pitch + index;
+ double value = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ value += (weight * (double)src_bits[0]);
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
+
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case FIT_RGB16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
+
+ const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
+ WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
+
+ const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+ const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * wordspp; // pixel index
+ WORD *dst_bits = dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const WORD *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)src_bits[0]);
+ g += (weight * (double)src_bits[1]);
+ b += (weight * (double)src_bits[2]);
+
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+ dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+ dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case FIT_RGBA16:
+ {
+ // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
+ const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
+
+ const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
+ WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
+
+ const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
+ const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * wordspp; // pixel index
+ WORD *dst_bits = dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
+ const WORD *src_bits = src_base + iLeft * src_pitch + index;
+ double r = 0, g = 0, b = 0, a = 0;
+
+ for (unsigned i = 0; i < iLimit; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i);
+ r += (weight * (double)src_bits[0]);
+ g += (weight * (double)src_bits[1]);
+ b += (weight * (double)src_bits[2]);
+ a += (weight * (double)src_bits[3]);
+
+ src_bits += src_pitch;
+ }
+
+ // clamp and place result in destination pixel
+ dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
+ dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
+ dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
+ dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
+
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+
+ case FIT_FLOAT:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ {
+ // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
+ const unsigned floatspp = (FreeImage_GetLine(src) / width) / sizeof(float);
+
+ const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float);
+ float *const dst_base = (float *)FreeImage_GetBits(dst);
+
+ const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float);
+ const float *const src_base = (float *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * floatspp;
+
+ for (unsigned x = 0; x < width; x++) {
+ // work on column x in dst
+ const unsigned index = x * floatspp; // pixel index
+ float *dst_bits = (float *)dst_base + index;
+
+ // scale each column
+ for (unsigned y = 0; y < dst_height; y++) {
+ // loop through column
+ const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
+ const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
+ const float *src_bits = src_base + iLeft * src_pitch + index;
+ double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
+
+ for (unsigned i = iLeft; i < iRight; i++) {
+ // scan between boundaries
+ // accumulate weighted effect of each neighboring pixel
+ const double weight = weightsTable.getWeight(y, i - iLeft);
+ for (unsigned j = 0; j < floatspp; j++) {
+ value[j] += (weight * (double)src_bits[j]);
+ }
+ src_bits += src_pitch;
+ }
+
+ // place result in destination pixel
+ for (unsigned j = 0; j < floatspp; j++) {
+ dst_bits[j] = (float)value[j];
+ }
+ dst_bits += dst_pitch;
+ }
+ }
+ }
+ break;
+ }
+}
diff --git a/libs/freeimage/src/FreeImageToolkit/Resize.h b/libs/freeimage/src/FreeImageToolkit/Resize.h
new file mode 100644
index 0000000000..466fcc183d
--- /dev/null
+++ b/libs/freeimage/src/FreeImageToolkit/Resize.h
@@ -0,0 +1,196 @@
+// ==========================================================
+// Upsampling / downsampling classes
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Carsten Klein (cklein05@users.sourceforge.net)
+//
+// 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!
+// ==========================================================
+
+#ifndef _RESIZE_H_
+#define _RESIZE_H_
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "Filters.h"
+
+/**
+ Filter weights table.<br>
+ This class stores contribution information for an entire line (row or column).
+*/
+class CWeightsTable
+{
+/**
+ Sampled filter weight table.<br>
+ Contribution information for a single pixel
+*/
+typedef struct {
+ /// Normalized weights of neighboring pixels
+ double *Weights;
+ /// Bounds of source pixels window
+ unsigned Left, Right;
+} Contribution;
+
+private:
+ /// Row (or column) of contribution weights
+ Contribution *m_WeightTable;
+ /// Filter window size (of affecting source pixels)
+ unsigned m_WindowSize;
+ /// Length of line (no. of rows / cols)
+ unsigned m_LineLength;
+
+public:
+ /**
+ Constructor<br>
+ Allocate and compute the weights table
+ @param pFilter Filter used for upsampling or downsampling
+ @param uDstSize Length (in pixels) of the destination line buffer
+ @param uSrcSize Length (in pixels) of the source line buffer
+ */
+ CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize);
+
+ /**
+ Destructor<br>
+ Destroy the weights table
+ */
+ ~CWeightsTable();
+
+ /** Retrieve a filter weight, given source and destination positions
+ @param dst_pos Pixel position in destination line buffer
+ @param src_pos Pixel position in source line buffer
+ @return Returns the filter weight
+ */
+ double getWeight(unsigned dst_pos, unsigned src_pos) {
+ return m_WeightTable[dst_pos].Weights[src_pos];
+ }
+
+ /** Retrieve left boundary of source line buffer
+ @param dst_pos Pixel position in destination line buffer
+ @return Returns the left boundary of source line buffer
+ */
+ unsigned getLeftBoundary(unsigned dst_pos) {
+ return m_WeightTable[dst_pos].Left;
+ }
+
+ /** Retrieve right boundary of source line buffer
+ @param dst_pos Pixel position in destination line buffer
+ @return Returns the right boundary of source line buffer
+ */
+ unsigned getRightBoundary(unsigned dst_pos) {
+ return m_WeightTable[dst_pos].Right;
+ }
+};
+
+// ---------------------------------------------
+
+/**
+ CResizeEngine<br>
+ This class performs filtered zoom. It scales an image to the desired dimensions with
+ any of the CGenericFilter derived filter class.<br>
+ It works with FIT_BITMAP buffers, WORD buffers (FIT_UINT16, FIT_RGB16, FIT_RGBA16)
+ and float buffers (FIT_FLOAT, FIT_RGBF, FIT_RGBAF).<br><br>
+
+ <b>References</b> : <br>
+ [1] Paul Heckbert, C code to zoom raster images up or down, with nice filtering.
+ UC Berkeley, August 1989. [online] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/Web/People/ph/heckbert.html
+ [2] Eran Yariv, Two Pass Scaling using Filters. The Code Project, December 1999.
+ [online] http://www.codeproject.com/bitmap/2_pass_scaling.asp
+
+*/
+class CResizeEngine
+{
+private:
+ /// Pointer to the FIR / IIR filter
+ CGenericFilter* m_pFilter;
+
+public:
+
+ /**
+ Constructor
+ @param filter FIR /IIR filter to be used
+ */
+ CResizeEngine(CGenericFilter* filter):m_pFilter(filter) {}
+
+ /// Destructor
+ virtual ~CResizeEngine() {}
+
+ /** Scale an image to the desired dimensions.
+
+ Method CResizeEngine::scale, as well as the two filtering methods
+ CResizeEngine::horizontalFilter and CResizeEngine::verticalFilter take
+ four additional parameters, that define a rectangle in the source
+ image to be rescaled.
+
+ These are src_left, src_top, src_width and src_height and should work
+ like these of function FreeImage_Copy. However, src_left and src_top are
+ actually named src_offset_x and src_offset_y in the filtering methods.
+
+ Additionally, since src_height and dst_height are always the same for
+ method horizontalFilter as src_width and dst_width are always the same
+ for verticalFilter, these have been stripped down to a single parameter
+ height and width for horizontalFilter and verticalFilter respectively.
+
+ Currently, method scale is called with the actual size of the source
+ image. However, in a future version, we could provide a new function
+ called FreeImage_RescaleRect that rescales only part of an image.
+
+ @param src Pointer to the source image
+ @param dst_width Destination image width
+ @param dst_height Destination image height
+ @param src_left Left boundary of the source rectangle to be scaled
+ @param src_top Top boundary of the source rectangle to be scaled
+ @param src_width Width of the source rectangle to be scaled
+ @param src_height Height of the source rectangle to be scaled
+ @return Returns the scaled image if successful, returns NULL otherwise
+ */
+ FIBITMAP* scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height, unsigned flags);
+
+private:
+
+ /**
+ Performs horizontal image filtering
+
+ @param src Source image
+ @param height Source / Destination image height
+ @param src_width Source image width
+ @param src_offset_x
+ @param src_offset_y
+ @param src_pal
+ @param dst Destination image
+ @param dst_width Destination image width
+ */
+ void horizontalFilter(FIBITMAP * const src, const unsigned height, const unsigned src_width,
+ const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal,
+ FIBITMAP * const dst, const unsigned dst_width);
+
+ /**
+ Performs vertical image filtering
+ @param src Source image
+ @param width Source / Destination image width
+ @param src_height Source image height
+ @param src_offset_x
+ @param src_offset_y
+ @param src_pal
+ @param dst Destination image
+ @param dst_height Destination image height
+ */
+ void verticalFilter(FIBITMAP * const src, const unsigned width, const unsigned src_height,
+ const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal,
+ FIBITMAP * const dst, const unsigned dst_height);
+};
+
+#endif // _RESIZE_H_