diff options
author | George Hazan <george.hazan@gmail.com> | 2012-05-30 17:27:49 +0000 |
---|---|---|
committer | George Hazan <george.hazan@gmail.com> | 2012-05-30 17:27:49 +0000 |
commit | 88708cffa15662dcd2755fce699112d24a10a087 (patch) | |
tree | 55d362220b42a52b52eaef2254f894c9a61fb4fd /plugins/FreeImage/Source/FreeImageToolkit | |
parent | 7fa5563a954339f3feeb156285ef56bfde7cbec8 (diff) |
update for zlib & FreeImage
git-svn-id: http://svn.miranda-ng.org/main/trunk@238 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/FreeImage/Source/FreeImageToolkit')
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/BSplineRotate.cpp | 1460 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/Channels.cpp | 976 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/ClassicRotate.cpp | 1873 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/Colors.cpp | 1934 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/CopyPaste.cpp | 1494 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/Display.cpp | 460 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/Filters.h | 574 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/Flip.cpp | 332 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/JPEGTransform.cpp | 820 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp | 1010 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/Rescale.cpp | 462 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/Resize.cpp | 1302 | ||||
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/Resize.h | 290 |
13 files changed, 6479 insertions, 6508 deletions
diff --git a/plugins/FreeImage/Source/FreeImageToolkit/BSplineRotate.cpp b/plugins/FreeImage/Source/FreeImageToolkit/BSplineRotate.cpp index ed468b4dac..690db87d8c 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/BSplineRotate.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/BSplineRotate.cpp @@ -1,730 +1,730 @@ -// ==========================================================
-// Bitmap rotation using B-Splines
-//
-// Design and implementation by
-// - Philippe Thévenaz (philippe.thevenaz@epfl.ch)
-// Adaptation for FreeImage by
-// - Hervé Drolon (drolon@infonie.fr)
-//
-// This file is part of FreeImage 3
-//
-// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
-// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
-// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
-// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
-// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
-// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
-// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
-// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
-// THIS DISCLAIMER.
-//
-// Use at your own risk!
-// ==========================================================
-
-/*
-==========================================================
-This code was taken and adapted from the following reference :
-
-[1] Philippe Thévenaz, Spline interpolation, a C source code
-implementation. http://bigwww.epfl.ch/thevenaz/
-
-It implements ideas described in the following papers :
-
-[2] Unser M., Splines: A Perfect Fit for Signal and Image Processing.
-IEEE Signal Processing Magazine, vol. 16, no. 6, pp. 22-38, November 1999.
-
-[3] Unser M., Aldroubi A., Eden M., B-Spline Signal Processing: Part I--Theory.
-IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 821-832, February 1993.
-
-[4] Unser M., Aldroubi A., Eden M., B-Spline Signal Processing: Part II--Efficient Design and Applications.
-IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 834-848, February 1993.
-
-==========================================================
-*/
-
-
-#include <float.h>
-#include "FreeImage.h"
-#include "Utilities.h"
-
-#define PI ((double)3.14159265358979323846264338327950288419716939937510)
-
-#define ROTATE_QUADRATIC 2L // Use B-splines of degree 2 (quadratic interpolation)
-#define ROTATE_CUBIC 3L // Use B-splines of degree 3 (cubic interpolation)
-#define ROTATE_QUARTIC 4L // Use B-splines of degree 4 (quartic interpolation)
-#define ROTATE_QUINTIC 5L // Use B-splines of degree 5 (quintic interpolation)
-
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Prototypes definition
-
-static void ConvertToInterpolationCoefficients(double *c, long DataLength, double *z, long NbPoles, double Tolerance);
-static double InitialCausalCoefficient(double *c, long DataLength, double z, double Tolerance);
-static void GetColumn(double *Image, long Width, long x, double *Line, long Height);
-static void GetRow(double *Image, long y, double *Line, long Width);
-static double InitialAntiCausalCoefficient(double *c, long DataLength, double z);
-static void PutColumn(double *Image, long Width, long x, double *Line, long Height);
-static void PutRow(double *Image, long y, double *Line, long Width);
-static bool SamplesToCoefficients(double *Image, long Width, long Height, long spline_degree);
-static double InterpolatedValue(double *Bcoeff, long Width, long Height, double x, double y, long spline_degree);
-
-static FIBITMAP * Rotate8Bit(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, long spline_degree, BOOL use_mask);
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Coefficients routines
-
-/**
- ConvertToInterpolationCoefficients
-
- @param c Input samples --> output coefficients
- @param DataLength Number of samples or coefficients
- @param z Poles
- @param NbPoles Number of poles
- @param Tolerance Admissible relative error
-*/
-static void
-ConvertToInterpolationCoefficients(double *c, long DataLength, double *z, long NbPoles, double Tolerance) {
- double Lambda = 1;
- long n, k;
-
- // special case required by mirror boundaries
- if(DataLength == 1L) {
- return;
- }
- // compute the overall gain
- for(k = 0L; k < NbPoles; k++) {
- Lambda = Lambda * (1.0 - z[k]) * (1.0 - 1.0 / z[k]);
- }
- // apply the gain
- for (n = 0L; n < DataLength; n++) {
- c[n] *= Lambda;
- }
- // loop over all poles
- for (k = 0L; k < NbPoles; k++) {
- // causal initialization
- c[0] = InitialCausalCoefficient(c, DataLength, z[k], Tolerance);
- // causal recursion
- for (n = 1L; n < DataLength; n++) {
- c[n] += z[k] * c[n - 1L];
- }
- // anticausal initialization
- c[DataLength - 1L] = InitialAntiCausalCoefficient(c, DataLength, z[k]);
- // anticausal recursion
- for (n = DataLength - 2L; 0 <= n; n--) {
- c[n] = z[k] * (c[n + 1L] - c[n]);
- }
- }
-}
-
-/**
- InitialCausalCoefficient
-
- @param c Coefficients
- @param DataLength Number of coefficients
- @param z Actual pole
- @param Tolerance Admissible relative error
- @return
-*/
-static double
-InitialCausalCoefficient(double *c, long DataLength, double z, double Tolerance) {
- double Sum, zn, z2n, iz;
- long n, Horizon;
-
- // this initialization corresponds to mirror boundaries
- Horizon = DataLength;
- if(Tolerance > 0) {
- Horizon = (long)ceil(log(Tolerance) / log(fabs(z)));
- }
- if(Horizon < DataLength) {
- // accelerated loop
- zn = z;
- Sum = c[0];
- for (n = 1L; n < Horizon; n++) {
- Sum += zn * c[n];
- zn *= z;
- }
- return(Sum);
- }
- else {
- // full loop
- zn = z;
- iz = 1.0 / z;
- z2n = pow(z, (double)(DataLength - 1L));
- Sum = c[0] + z2n * c[DataLength - 1L];
- z2n *= z2n * iz;
- for (n = 1L; n <= DataLength - 2L; n++) {
- Sum += (zn + z2n) * c[n];
- zn *= z;
- z2n *= iz;
- }
- return(Sum / (1.0 - zn * zn));
- }
-}
-
-/**
- GetColumn
-
- @param Image Input image array
- @param Width Width of the image
- @param x x coordinate of the selected line
- @param Line Output linear array
- @param Height Length of the line
-*/
-static void
-GetColumn(double *Image, long Width, long x, double *Line, long Height) {
- long y;
-
- Image = Image + x;
- for(y = 0L; y < Height; y++) {
- Line[y] = (double)*Image;
- Image += Width;
- }
-}
-
-/**
- GetRow
-
- @param Image Input image array
- @param y y coordinate of the selected line
- @param Line Output linear array
- @param Width Length of the line
-*/
-static void
-GetRow(double *Image, long y, double *Line, long Width) {
- long x;
-
- Image = Image + (y * Width);
- for(x = 0L; x < Width; x++) {
- Line[x] = (double)*Image++;
- }
-}
-
-/**
- InitialAntiCausalCoefficient
-
- @param c Coefficients
- @param DataLength Number of samples or coefficients
- @param z Actual pole
- @return
-*/
-static double
-InitialAntiCausalCoefficient(double *c, long DataLength, double z) {
- // this initialization corresponds to mirror boundaries
- return((z / (z * z - 1.0)) * (z * c[DataLength - 2L] + c[DataLength - 1L]));
-}
-
-/**
- PutColumn
-
- @param Image Output image array
- @param Width Width of the image
- @param x x coordinate of the selected line
- @param Line Input linear array
- @param Height Length of the line and height of the image
-*/
-static void
-PutColumn(double *Image, long Width, long x, double *Line, long Height) {
- long y;
-
- Image = Image + x;
- for(y = 0L; y < Height; y++) {
- *Image = (double)Line[y];
- Image += Width;
- }
-}
-
-/**
- PutRow
-
- @param Image Output image array
- @param y y coordinate of the selected line
- @param Line Input linear array
- @param Width length of the line and width of the image
-*/
-static void
-PutRow(double *Image, long y, double *Line, long Width) {
- long x;
-
- Image = Image + (y * Width);
- for(x = 0L; x < Width; x++) {
- *Image++ = (double)Line[x];
- }
-}
-
-/**
- SamplesToCoefficients.<br>
- Implement the algorithm that converts the image samples into B-spline coefficients.
- This efficient procedure essentially relies on the three papers cited above;
- data are processed in-place.
- Even though this algorithm is robust with respect to quantization,
- we advocate the use of a floating-point format for the data.
-
- @param Image Input / Output image (in-place processing)
- @param Width Width of the image
- @param Height Height of the image
- @param spline_degree Degree of the spline model
- @return Returns true if success, false otherwise
-*/
-static bool
-SamplesToCoefficients(double *Image, long Width, long Height, long spline_degree) {
- double *Line;
- double Pole[2];
- long NbPoles;
- long x, y;
-
- // recover the poles from a lookup table
- switch (spline_degree) {
- case 2L:
- NbPoles = 1L;
- Pole[0] = sqrt(8.0) - 3.0;
- break;
- case 3L:
- NbPoles = 1L;
- Pole[0] = sqrt(3.0) - 2.0;
- break;
- case 4L:
- NbPoles = 2L;
- Pole[0] = sqrt(664.0 - sqrt(438976.0)) + sqrt(304.0) - 19.0;
- Pole[1] = sqrt(664.0 + sqrt(438976.0)) - sqrt(304.0) - 19.0;
- break;
- case 5L:
- NbPoles = 2L;
- Pole[0] = sqrt(135.0 / 2.0 - sqrt(17745.0 / 4.0)) + sqrt(105.0 / 4.0)
- - 13.0 / 2.0;
- Pole[1] = sqrt(135.0 / 2.0 + sqrt(17745.0 / 4.0)) - sqrt(105.0 / 4.0)
- - 13.0 / 2.0;
- break;
- default:
- // Invalid spline degree
- return false;
- }
-
- // convert the image samples into interpolation coefficients
-
- // in-place separable process, along x
- Line = (double *)malloc(Width * sizeof(double));
- if (Line == NULL) {
- // Row allocation failed
- return false;
- }
- for (y = 0L; y < Height; y++) {
- GetRow(Image, y, Line, Width);
- ConvertToInterpolationCoefficients(Line, Width, Pole, NbPoles, DBL_EPSILON);
- PutRow(Image, y, Line, Width);
- }
- free(Line);
-
- // in-place separable process, along y
- Line = (double *)malloc(Height * sizeof(double));
- if (Line == NULL) {
- // Column allocation failed
- return false;
- }
- for (x = 0L; x < Width; x++) {
- GetColumn(Image, Width, x, Line, Height);
- ConvertToInterpolationCoefficients(Line, Height, Pole, NbPoles, DBL_EPSILON);
- PutColumn(Image, Width, x, Line, Height);
- }
- free(Line);
-
- return true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Interpolation routines
-
-/**
-Perform the bidimensional interpolation of an image.
-Given an array of spline coefficients, return the value of
-the underlying continuous spline model, sampled at the location (x, y).
-The model degree can be 2 (quadratic), 3 (cubic), 4 (quartic), or 5 (quintic).
-
-@param Bcoeff Input B-spline array of coefficients
-@param Width Width of the image
-@param Height Height of the image
-@param x x coordinate where to interpolate
-@param y y coordinate where to interpolate
-@param spline_degree Degree of the spline model
-@return Returns the value of the underlying continuous spline model,
-sampled at the location (x, y)
-*/
-static double
-InterpolatedValue(double *Bcoeff, long Width, long Height, double x, double y, long spline_degree) {
- double *p;
- double xWeight[6], yWeight[6];
- double interpolated;
- double w, w2, w4, t, t0, t1;
- long xIndex[6], yIndex[6];
- long Width2 = 2L * Width - 2L, Height2 = 2L * Height - 2L;
- long i, j, k;
-
- // compute the interpolation indexes
- if (spline_degree & 1L) {
- i = (long)floor(x) - spline_degree / 2L;
- j = (long)floor(y) - spline_degree / 2L;
- for(k = 0; k <= spline_degree; k++) {
- xIndex[k] = i++;
- yIndex[k] = j++;
- }
- }
- else {
- i = (long)floor(x + 0.5) - spline_degree / 2L;
- j = (long)floor(y + 0.5) - spline_degree / 2L;
- for (k = 0; k <= spline_degree; k++) {
- xIndex[k] = i++;
- yIndex[k] = j++;
- }
- }
-
- // compute the interpolation weights
- switch (spline_degree) {
- case 2L:
- /* x */
- w = x - (double)xIndex[1];
- xWeight[1] = 3.0 / 4.0 - w * w;
- xWeight[2] = (1.0 / 2.0) * (w - xWeight[1] + 1.0);
- xWeight[0] = 1.0 - xWeight[1] - xWeight[2];
- /* y */
- w = y - (double)yIndex[1];
- yWeight[1] = 3.0 / 4.0 - w * w;
- yWeight[2] = (1.0 / 2.0) * (w - yWeight[1] + 1.0);
- yWeight[0] = 1.0 - yWeight[1] - yWeight[2];
- break;
- case 3L:
- /* x */
- w = x - (double)xIndex[1];
- xWeight[3] = (1.0 / 6.0) * w * w * w;
- xWeight[0] = (1.0 / 6.0) + (1.0 / 2.0) * w * (w - 1.0) - xWeight[3];
- xWeight[2] = w + xWeight[0] - 2.0 * xWeight[3];
- xWeight[1] = 1.0 - xWeight[0] - xWeight[2] - xWeight[3];
- /* y */
- w = y - (double)yIndex[1];
- yWeight[3] = (1.0 / 6.0) * w * w * w;
- yWeight[0] = (1.0 / 6.0) + (1.0 / 2.0) * w * (w - 1.0) - yWeight[3];
- yWeight[2] = w + yWeight[0] - 2.0 * yWeight[3];
- yWeight[1] = 1.0 - yWeight[0] - yWeight[2] - yWeight[3];
- break;
- case 4L:
- /* x */
- w = x - (double)xIndex[2];
- w2 = w * w;
- t = (1.0 / 6.0) * w2;
- xWeight[0] = 1.0 / 2.0 - w;
- xWeight[0] *= xWeight[0];
- xWeight[0] *= (1.0 / 24.0) * xWeight[0];
- t0 = w * (t - 11.0 / 24.0);
- t1 = 19.0 / 96.0 + w2 * (1.0 / 4.0 - t);
- xWeight[1] = t1 + t0;
- xWeight[3] = t1 - t0;
- xWeight[4] = xWeight[0] + t0 + (1.0 / 2.0) * w;
- xWeight[2] = 1.0 - xWeight[0] - xWeight[1] - xWeight[3] - xWeight[4];
- /* y */
- w = y - (double)yIndex[2];
- w2 = w * w;
- t = (1.0 / 6.0) * w2;
- yWeight[0] = 1.0 / 2.0 - w;
- yWeight[0] *= yWeight[0];
- yWeight[0] *= (1.0 / 24.0) * yWeight[0];
- t0 = w * (t - 11.0 / 24.0);
- t1 = 19.0 / 96.0 + w2 * (1.0 / 4.0 - t);
- yWeight[1] = t1 + t0;
- yWeight[3] = t1 - t0;
- yWeight[4] = yWeight[0] + t0 + (1.0 / 2.0) * w;
- yWeight[2] = 1.0 - yWeight[0] - yWeight[1] - yWeight[3] - yWeight[4];
- break;
- case 5L:
- /* x */
- w = x - (double)xIndex[2];
- w2 = w * w;
- xWeight[5] = (1.0 / 120.0) * w * w2 * w2;
- w2 -= w;
- w4 = w2 * w2;
- w -= 1.0 / 2.0;
- t = w2 * (w2 - 3.0);
- xWeight[0] = (1.0 / 24.0) * (1.0 / 5.0 + w2 + w4) - xWeight[5];
- t0 = (1.0 / 24.0) * (w2 * (w2 - 5.0) + 46.0 / 5.0);
- t1 = (-1.0 / 12.0) * w * (t + 4.0);
- xWeight[2] = t0 + t1;
- xWeight[3] = t0 - t1;
- t0 = (1.0 / 16.0) * (9.0 / 5.0 - t);
- t1 = (1.0 / 24.0) * w * (w4 - w2 - 5.0);
- xWeight[1] = t0 + t1;
- xWeight[4] = t0 - t1;
- /* y */
- w = y - (double)yIndex[2];
- w2 = w * w;
- yWeight[5] = (1.0 / 120.0) * w * w2 * w2;
- w2 -= w;
- w4 = w2 * w2;
- w -= 1.0 / 2.0;
- t = w2 * (w2 - 3.0);
- yWeight[0] = (1.0 / 24.0) * (1.0 / 5.0 + w2 + w4) - yWeight[5];
- t0 = (1.0 / 24.0) * (w2 * (w2 - 5.0) + 46.0 / 5.0);
- t1 = (-1.0 / 12.0) * w * (t + 4.0);
- yWeight[2] = t0 + t1;
- yWeight[3] = t0 - t1;
- t0 = (1.0 / 16.0) * (9.0 / 5.0 - t);
- t1 = (1.0 / 24.0) * w * (w4 - w2 - 5.0);
- yWeight[1] = t0 + t1;
- yWeight[4] = t0 - t1;
- break;
- default:
- // Invalid spline degree
- return 0;
- }
-
- // apply the mirror boundary conditions
- for(k = 0; k <= spline_degree; k++) {
- xIndex[k] = (Width == 1L) ? (0L) : ((xIndex[k] < 0L) ?
- (-xIndex[k] - Width2 * ((-xIndex[k]) / Width2))
- : (xIndex[k] - Width2 * (xIndex[k] / Width2)));
- if (Width <= xIndex[k]) {
- xIndex[k] = Width2 - xIndex[k];
- }
- yIndex[k] = (Height == 1L) ? (0L) : ((yIndex[k] < 0L) ?
- (-yIndex[k] - Height2 * ((-yIndex[k]) / Height2))
- : (yIndex[k] - Height2 * (yIndex[k] / Height2)));
- if (Height <= yIndex[k]) {
- yIndex[k] = Height2 - yIndex[k];
- }
- }
-
- // perform interpolation
- interpolated = 0.0;
- for(j = 0; j <= spline_degree; j++) {
- p = Bcoeff + (yIndex[j] * Width);
- w = 0.0;
- for(i = 0; i <= spline_degree; i++) {
- w += xWeight[i] * p[xIndex[i]];
- }
- interpolated += yWeight[j] * w;
- }
-
- return interpolated;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// FreeImage implementation
-
-
-/**
- Image translation and rotation using B-Splines.
-
- @param dib Input 8-bit greyscale image
- @param angle Output image rotation in degree
- @param x_shift Output image horizontal shift
- @param y_shift Output image vertical shift
- @param x_origin Output origin of the x-axis
- @param y_origin Output origin of the y-axis
- @param spline_degree Output degree of the B-spline model
- @param use_mask Whether or not to mask the image
- @return Returns the translated & rotated dib if successful, returns NULL otherwise
-*/
-static FIBITMAP *
-Rotate8Bit(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, long spline_degree, BOOL use_mask) {
- double *ImageRasterArray;
- double p;
- double a11, a12, a21, a22;
- double x0, y0, x1, y1;
- long x, y;
- long spline;
- bool bResult;
-
- int bpp = FreeImage_GetBPP(dib);
- if(bpp != 8) {
- return NULL;
- }
-
- int width = FreeImage_GetWidth(dib);
- int height = FreeImage_GetHeight(dib);
- switch(spline_degree) {
- case ROTATE_QUADRATIC:
- spline = 2L; // Use splines of degree 2 (quadratic interpolation)
- break;
- case ROTATE_CUBIC:
- spline = 3L; // Use splines of degree 3 (cubic interpolation)
- break;
- case ROTATE_QUARTIC:
- spline = 4L; // Use splines of degree 4 (quartic interpolation)
- break;
- case ROTATE_QUINTIC:
- spline = 5L; // Use splines of degree 5 (quintic interpolation)
- break;
- default:
- spline = 3L;
- }
-
- // allocate output image
- FIBITMAP *dst = FreeImage_Allocate(width, height, bpp);
- if(!dst)
- return NULL;
- // buid a grey scale palette
- RGBQUAD *pal = FreeImage_GetPalette(dst);
- for(int i = 0; i < 256; i++) {
- pal[i].rgbRed = pal[i].rgbGreen = pal[i].rgbBlue = (BYTE)i;
- }
-
- // allocate a temporary array
- ImageRasterArray = (double*)malloc(width * height * sizeof(double));
- if(!ImageRasterArray) {
- FreeImage_Unload(dst);
- return NULL;
- }
- // copy data samples
- for(y = 0; y < height; y++) {
- double *pImage = &ImageRasterArray[y*width];
- BYTE *src_bits = FreeImage_GetScanLine(dib, height-1-y);
-
- for(x = 0; x < width; x++) {
- pImage[x] = (double)src_bits[x];
- }
- }
-
- // convert between a representation based on image samples
- // and a representation based on image B-spline coefficients
- bResult = SamplesToCoefficients(ImageRasterArray, width, height, spline);
- if(!bResult) {
- FreeImage_Unload(dst);
- free(ImageRasterArray);
- return NULL;
- }
-
- // prepare the geometry
- angle *= PI / 180.0;
- a11 = cos(angle);
- a12 = -sin(angle);
- a21 = sin(angle);
- a22 = cos(angle);
- x0 = a11 * (x_shift + x_origin) + a12 * (y_shift + y_origin);
- y0 = a21 * (x_shift + x_origin) + a22 * (y_shift + y_origin);
- x_shift = x_origin - x0;
- y_shift = y_origin - y0;
-
- // visit all pixels of the output image and assign their value
- for(y = 0; y < height; y++) {
- BYTE *dst_bits = FreeImage_GetScanLine(dst, height-1-y);
-
- x0 = a12 * (double)y + x_shift;
- y0 = a22 * (double)y + y_shift;
-
- for(x = 0; x < width; x++) {
- x1 = x0 + a11 * (double)x;
- y1 = y0 + a21 * (double)x;
- if(use_mask) {
- if((x1 <= -0.5) || (((double)width - 0.5) <= x1) || (y1 <= -0.5) || (((double)height - 0.5) <= y1)) {
- p = 0;
- }
- else {
- p = (double)InterpolatedValue(ImageRasterArray, width, height, x1, y1, spline);
- }
- }
- else {
- p = (double)InterpolatedValue(ImageRasterArray, width, height, x1, y1, spline);
- }
- // clamp and convert to BYTE
- dst_bits[x] = (BYTE)MIN(MAX((int)0, (int)(p + 0.5)), (int)255);
- }
- }
-
- // free working array and return
- free(ImageRasterArray);
-
- return dst;
-}
-
-/**
- Image rotation using a 3rd order (cubic) B-Splines.
-
- @param dib Input dib (8, 24 or 32-bit)
- @param angle Output image rotation
- @param x_shift Output image horizontal shift
- @param y_shift Output image vertical shift
- @param x_origin Output origin of the x-axis
- @param y_origin Output origin of the y-axis
- @param use_mask Whether or not to mask the image
- @return Returns the translated & rotated dib if successful, returns NULL otherwise
-*/
-FIBITMAP * DLL_CALLCONV
-FreeImage_RotateEx(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask) {
-
- int x, y, bpp;
- int channel, nb_channels;
- BYTE *src_bits, *dst_bits;
- FIBITMAP *src8 = NULL, *dst8 = NULL, *dst = NULL;
-
- if(!FreeImage_HasPixels(dib)) return NULL;
-
- try {
-
- bpp = FreeImage_GetBPP(dib);
-
- if(bpp == 8) {
- FIBITMAP *dst_8 = Rotate8Bit(dib, angle, x_shift, y_shift, x_origin, y_origin, ROTATE_CUBIC, use_mask);
- if(dst_8) {
- // copy metadata from src to dst
- FreeImage_CloneMetadata(dst_8, dib);
- }
- return dst_8;
- }
- if((bpp == 24) || (bpp == 32)) {
- // allocate dst image
- int width = FreeImage_GetWidth(dib);
- int height = FreeImage_GetHeight(dib);
- if( bpp == 24 ) {
- dst = FreeImage_Allocate(width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
- } else {
- dst = FreeImage_Allocate(width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
- }
- if(!dst) throw(1);
-
- // allocate a temporary 8-bit dib (no need to build a palette)
- src8 = FreeImage_Allocate(width, height, 8);
- if(!src8) throw(1);
-
- // process each channel separately
- // -------------------------------
- nb_channels = (bpp / 8);
-
- for(channel = 0; channel < nb_channels; channel++) {
- // extract channel from source dib
- for(y = 0; y < height; y++) {
- src_bits = FreeImage_GetScanLine(dib, y);
- dst_bits = FreeImage_GetScanLine(src8, y);
- for(x = 0; x < width; x++) {
- dst_bits[x] = src_bits[channel];
- src_bits += nb_channels;
- }
- }
-
- // process channel
- dst8 = Rotate8Bit(src8, angle, x_shift, y_shift, x_origin, y_origin, ROTATE_CUBIC, use_mask);
- if(!dst8) throw(1);
-
- // insert channel to destination dib
- for(y = 0; y < height; y++) {
- src_bits = FreeImage_GetScanLine(dst8, y);
- dst_bits = FreeImage_GetScanLine(dst, y);
- for(x = 0; x < width; x++) {
- dst_bits[channel] = src_bits[x];
- dst_bits += nb_channels;
- }
- }
-
- FreeImage_Unload(dst8);
- }
-
- FreeImage_Unload(src8);
-
- // copy metadata from src to dst
- FreeImage_CloneMetadata(dst, dib);
-
- return dst;
- }
- } catch(int) {
- if(src8) FreeImage_Unload(src8);
- if(dst8) FreeImage_Unload(dst8);
- if(dst) FreeImage_Unload(dst);
- }
-
- return NULL;
-}
+// ========================================================== +// Bitmap rotation using B-Splines +// +// Design and implementation by +// - Philippe Thévenaz (philippe.thevenaz@epfl.ch) +// Adaptation for FreeImage by +// - Hervé Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +/* +========================================================== +This code was taken and adapted from the following reference : + +[1] Philippe Thévenaz, Spline interpolation, a C source code +implementation. http://bigwww.epfl.ch/thevenaz/ + +It implements ideas described in the following papers : + +[2] Unser M., Splines: A Perfect Fit for Signal and Image Processing. +IEEE Signal Processing Magazine, vol. 16, no. 6, pp. 22-38, November 1999. + +[3] Unser M., Aldroubi A., Eden M., B-Spline Signal Processing: Part I--Theory. +IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 821-832, February 1993. + +[4] Unser M., Aldroubi A., Eden M., B-Spline Signal Processing: Part II--Efficient Design and Applications. +IEEE Transactions on Signal Processing, vol. 41, no. 2, pp. 834-848, February 1993. + +========================================================== +*/ + + +#include <float.h> +#include "FreeImage.h" +#include "Utilities.h" + +#define PI ((double)3.14159265358979323846264338327950288419716939937510) + +#define ROTATE_QUADRATIC 2L // Use B-splines of degree 2 (quadratic interpolation) +#define ROTATE_CUBIC 3L // Use B-splines of degree 3 (cubic interpolation) +#define ROTATE_QUARTIC 4L // Use B-splines of degree 4 (quartic interpolation) +#define ROTATE_QUINTIC 5L // Use B-splines of degree 5 (quintic interpolation) + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Prototypes definition + +static void ConvertToInterpolationCoefficients(double *c, long DataLength, double *z, long NbPoles, double Tolerance); +static double InitialCausalCoefficient(double *c, long DataLength, double z, double Tolerance); +static void GetColumn(double *Image, long Width, long x, double *Line, long Height); +static void GetRow(double *Image, long y, double *Line, long Width); +static double InitialAntiCausalCoefficient(double *c, long DataLength, double z); +static void PutColumn(double *Image, long Width, long x, double *Line, long Height); +static void PutRow(double *Image, long y, double *Line, long Width); +static bool SamplesToCoefficients(double *Image, long Width, long Height, long spline_degree); +static double InterpolatedValue(double *Bcoeff, long Width, long Height, double x, double y, long spline_degree); + +static FIBITMAP * Rotate8Bit(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, long spline_degree, BOOL use_mask); + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Coefficients routines + +/** + ConvertToInterpolationCoefficients + + @param c Input samples --> output coefficients + @param DataLength Number of samples or coefficients + @param z Poles + @param NbPoles Number of poles + @param Tolerance Admissible relative error +*/ +static void +ConvertToInterpolationCoefficients(double *c, long DataLength, double *z, long NbPoles, double Tolerance) { + double Lambda = 1; + long n, k; + + // special case required by mirror boundaries + if(DataLength == 1L) { + return; + } + // compute the overall gain + for(k = 0L; k < NbPoles; k++) { + Lambda = Lambda * (1.0 - z[k]) * (1.0 - 1.0 / z[k]); + } + // apply the gain + for (n = 0L; n < DataLength; n++) { + c[n] *= Lambda; + } + // loop over all poles + for (k = 0L; k < NbPoles; k++) { + // causal initialization + c[0] = InitialCausalCoefficient(c, DataLength, z[k], Tolerance); + // causal recursion + for (n = 1L; n < DataLength; n++) { + c[n] += z[k] * c[n - 1L]; + } + // anticausal initialization + c[DataLength - 1L] = InitialAntiCausalCoefficient(c, DataLength, z[k]); + // anticausal recursion + for (n = DataLength - 2L; 0 <= n; n--) { + c[n] = z[k] * (c[n + 1L] - c[n]); + } + } +} + +/** + InitialCausalCoefficient + + @param c Coefficients + @param DataLength Number of coefficients + @param z Actual pole + @param Tolerance Admissible relative error + @return +*/ +static double +InitialCausalCoefficient(double *c, long DataLength, double z, double Tolerance) { + double Sum, zn, z2n, iz; + long n, Horizon; + + // this initialization corresponds to mirror boundaries + Horizon = DataLength; + if(Tolerance > 0) { + Horizon = (long)ceil(log(Tolerance) / log(fabs(z))); + } + if(Horizon < DataLength) { + // accelerated loop + zn = z; + Sum = c[0]; + for (n = 1L; n < Horizon; n++) { + Sum += zn * c[n]; + zn *= z; + } + return(Sum); + } + else { + // full loop + zn = z; + iz = 1.0 / z; + z2n = pow(z, (double)(DataLength - 1L)); + Sum = c[0] + z2n * c[DataLength - 1L]; + z2n *= z2n * iz; + for (n = 1L; n <= DataLength - 2L; n++) { + Sum += (zn + z2n) * c[n]; + zn *= z; + z2n *= iz; + } + return(Sum / (1.0 - zn * zn)); + } +} + +/** + GetColumn + + @param Image Input image array + @param Width Width of the image + @param x x coordinate of the selected line + @param Line Output linear array + @param Height Length of the line +*/ +static void +GetColumn(double *Image, long Width, long x, double *Line, long Height) { + long y; + + Image = Image + x; + for(y = 0L; y < Height; y++) { + Line[y] = (double)*Image; + Image += Width; + } +} + +/** + GetRow + + @param Image Input image array + @param y y coordinate of the selected line + @param Line Output linear array + @param Width Length of the line +*/ +static void +GetRow(double *Image, long y, double *Line, long Width) { + long x; + + Image = Image + (y * Width); + for(x = 0L; x < Width; x++) { + Line[x] = (double)*Image++; + } +} + +/** + InitialAntiCausalCoefficient + + @param c Coefficients + @param DataLength Number of samples or coefficients + @param z Actual pole + @return +*/ +static double +InitialAntiCausalCoefficient(double *c, long DataLength, double z) { + // this initialization corresponds to mirror boundaries + return((z / (z * z - 1.0)) * (z * c[DataLength - 2L] + c[DataLength - 1L])); +} + +/** + PutColumn + + @param Image Output image array + @param Width Width of the image + @param x x coordinate of the selected line + @param Line Input linear array + @param Height Length of the line and height of the image +*/ +static void +PutColumn(double *Image, long Width, long x, double *Line, long Height) { + long y; + + Image = Image + x; + for(y = 0L; y < Height; y++) { + *Image = (double)Line[y]; + Image += Width; + } +} + +/** + PutRow + + @param Image Output image array + @param y y coordinate of the selected line + @param Line Input linear array + @param Width length of the line and width of the image +*/ +static void +PutRow(double *Image, long y, double *Line, long Width) { + long x; + + Image = Image + (y * Width); + for(x = 0L; x < Width; x++) { + *Image++ = (double)Line[x]; + } +} + +/** + SamplesToCoefficients.<br> + Implement the algorithm that converts the image samples into B-spline coefficients. + This efficient procedure essentially relies on the three papers cited above; + data are processed in-place. + Even though this algorithm is robust with respect to quantization, + we advocate the use of a floating-point format for the data. + + @param Image Input / Output image (in-place processing) + @param Width Width of the image + @param Height Height of the image + @param spline_degree Degree of the spline model + @return Returns true if success, false otherwise +*/ +static bool +SamplesToCoefficients(double *Image, long Width, long Height, long spline_degree) { + double *Line; + double Pole[2]; + long NbPoles; + long x, y; + + // recover the poles from a lookup table + switch (spline_degree) { + case 2L: + NbPoles = 1L; + Pole[0] = sqrt(8.0) - 3.0; + break; + case 3L: + NbPoles = 1L; + Pole[0] = sqrt(3.0) - 2.0; + break; + case 4L: + NbPoles = 2L; + Pole[0] = sqrt(664.0 - sqrt(438976.0)) + sqrt(304.0) - 19.0; + Pole[1] = sqrt(664.0 + sqrt(438976.0)) - sqrt(304.0) - 19.0; + break; + case 5L: + NbPoles = 2L; + Pole[0] = sqrt(135.0 / 2.0 - sqrt(17745.0 / 4.0)) + sqrt(105.0 / 4.0) + - 13.0 / 2.0; + Pole[1] = sqrt(135.0 / 2.0 + sqrt(17745.0 / 4.0)) - sqrt(105.0 / 4.0) + - 13.0 / 2.0; + break; + default: + // Invalid spline degree + return false; + } + + // convert the image samples into interpolation coefficients + + // in-place separable process, along x + Line = (double *)malloc(Width * sizeof(double)); + if (Line == NULL) { + // Row allocation failed + return false; + } + for (y = 0L; y < Height; y++) { + GetRow(Image, y, Line, Width); + ConvertToInterpolationCoefficients(Line, Width, Pole, NbPoles, DBL_EPSILON); + PutRow(Image, y, Line, Width); + } + free(Line); + + // in-place separable process, along y + Line = (double *)malloc(Height * sizeof(double)); + if (Line == NULL) { + // Column allocation failed + return false; + } + for (x = 0L; x < Width; x++) { + GetColumn(Image, Width, x, Line, Height); + ConvertToInterpolationCoefficients(Line, Height, Pole, NbPoles, DBL_EPSILON); + PutColumn(Image, Width, x, Line, Height); + } + free(Line); + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Interpolation routines + +/** +Perform the bidimensional interpolation of an image. +Given an array of spline coefficients, return the value of +the underlying continuous spline model, sampled at the location (x, y). +The model degree can be 2 (quadratic), 3 (cubic), 4 (quartic), or 5 (quintic). + +@param Bcoeff Input B-spline array of coefficients +@param Width Width of the image +@param Height Height of the image +@param x x coordinate where to interpolate +@param y y coordinate where to interpolate +@param spline_degree Degree of the spline model +@return Returns the value of the underlying continuous spline model, +sampled at the location (x, y) +*/ +static double +InterpolatedValue(double *Bcoeff, long Width, long Height, double x, double y, long spline_degree) { + double *p; + double xWeight[6], yWeight[6]; + double interpolated; + double w, w2, w4, t, t0, t1; + long xIndex[6], yIndex[6]; + long Width2 = 2L * Width - 2L, Height2 = 2L * Height - 2L; + long i, j, k; + + // compute the interpolation indexes + if (spline_degree & 1L) { + i = (long)floor(x) - spline_degree / 2L; + j = (long)floor(y) - spline_degree / 2L; + for(k = 0; k <= spline_degree; k++) { + xIndex[k] = i++; + yIndex[k] = j++; + } + } + else { + i = (long)floor(x + 0.5) - spline_degree / 2L; + j = (long)floor(y + 0.5) - spline_degree / 2L; + for (k = 0; k <= spline_degree; k++) { + xIndex[k] = i++; + yIndex[k] = j++; + } + } + + // compute the interpolation weights + switch (spline_degree) { + case 2L: + /* x */ + w = x - (double)xIndex[1]; + xWeight[1] = 3.0 / 4.0 - w * w; + xWeight[2] = (1.0 / 2.0) * (w - xWeight[1] + 1.0); + xWeight[0] = 1.0 - xWeight[1] - xWeight[2]; + /* y */ + w = y - (double)yIndex[1]; + yWeight[1] = 3.0 / 4.0 - w * w; + yWeight[2] = (1.0 / 2.0) * (w - yWeight[1] + 1.0); + yWeight[0] = 1.0 - yWeight[1] - yWeight[2]; + break; + case 3L: + /* x */ + w = x - (double)xIndex[1]; + xWeight[3] = (1.0 / 6.0) * w * w * w; + xWeight[0] = (1.0 / 6.0) + (1.0 / 2.0) * w * (w - 1.0) - xWeight[3]; + xWeight[2] = w + xWeight[0] - 2.0 * xWeight[3]; + xWeight[1] = 1.0 - xWeight[0] - xWeight[2] - xWeight[3]; + /* y */ + w = y - (double)yIndex[1]; + yWeight[3] = (1.0 / 6.0) * w * w * w; + yWeight[0] = (1.0 / 6.0) + (1.0 / 2.0) * w * (w - 1.0) - yWeight[3]; + yWeight[2] = w + yWeight[0] - 2.0 * yWeight[3]; + yWeight[1] = 1.0 - yWeight[0] - yWeight[2] - yWeight[3]; + break; + case 4L: + /* x */ + w = x - (double)xIndex[2]; + w2 = w * w; + t = (1.0 / 6.0) * w2; + xWeight[0] = 1.0 / 2.0 - w; + xWeight[0] *= xWeight[0]; + xWeight[0] *= (1.0 / 24.0) * xWeight[0]; + t0 = w * (t - 11.0 / 24.0); + t1 = 19.0 / 96.0 + w2 * (1.0 / 4.0 - t); + xWeight[1] = t1 + t0; + xWeight[3] = t1 - t0; + xWeight[4] = xWeight[0] + t0 + (1.0 / 2.0) * w; + xWeight[2] = 1.0 - xWeight[0] - xWeight[1] - xWeight[3] - xWeight[4]; + /* y */ + w = y - (double)yIndex[2]; + w2 = w * w; + t = (1.0 / 6.0) * w2; + yWeight[0] = 1.0 / 2.0 - w; + yWeight[0] *= yWeight[0]; + yWeight[0] *= (1.0 / 24.0) * yWeight[0]; + t0 = w * (t - 11.0 / 24.0); + t1 = 19.0 / 96.0 + w2 * (1.0 / 4.0 - t); + yWeight[1] = t1 + t0; + yWeight[3] = t1 - t0; + yWeight[4] = yWeight[0] + t0 + (1.0 / 2.0) * w; + yWeight[2] = 1.0 - yWeight[0] - yWeight[1] - yWeight[3] - yWeight[4]; + break; + case 5L: + /* x */ + w = x - (double)xIndex[2]; + w2 = w * w; + xWeight[5] = (1.0 / 120.0) * w * w2 * w2; + w2 -= w; + w4 = w2 * w2; + w -= 1.0 / 2.0; + t = w2 * (w2 - 3.0); + xWeight[0] = (1.0 / 24.0) * (1.0 / 5.0 + w2 + w4) - xWeight[5]; + t0 = (1.0 / 24.0) * (w2 * (w2 - 5.0) + 46.0 / 5.0); + t1 = (-1.0 / 12.0) * w * (t + 4.0); + xWeight[2] = t0 + t1; + xWeight[3] = t0 - t1; + t0 = (1.0 / 16.0) * (9.0 / 5.0 - t); + t1 = (1.0 / 24.0) * w * (w4 - w2 - 5.0); + xWeight[1] = t0 + t1; + xWeight[4] = t0 - t1; + /* y */ + w = y - (double)yIndex[2]; + w2 = w * w; + yWeight[5] = (1.0 / 120.0) * w * w2 * w2; + w2 -= w; + w4 = w2 * w2; + w -= 1.0 / 2.0; + t = w2 * (w2 - 3.0); + yWeight[0] = (1.0 / 24.0) * (1.0 / 5.0 + w2 + w4) - yWeight[5]; + t0 = (1.0 / 24.0) * (w2 * (w2 - 5.0) + 46.0 / 5.0); + t1 = (-1.0 / 12.0) * w * (t + 4.0); + yWeight[2] = t0 + t1; + yWeight[3] = t0 - t1; + t0 = (1.0 / 16.0) * (9.0 / 5.0 - t); + t1 = (1.0 / 24.0) * w * (w4 - w2 - 5.0); + yWeight[1] = t0 + t1; + yWeight[4] = t0 - t1; + break; + default: + // Invalid spline degree + return 0; + } + + // apply the mirror boundary conditions + for(k = 0; k <= spline_degree; k++) { + xIndex[k] = (Width == 1L) ? (0L) : ((xIndex[k] < 0L) ? + (-xIndex[k] - Width2 * ((-xIndex[k]) / Width2)) + : (xIndex[k] - Width2 * (xIndex[k] / Width2))); + if (Width <= xIndex[k]) { + xIndex[k] = Width2 - xIndex[k]; + } + yIndex[k] = (Height == 1L) ? (0L) : ((yIndex[k] < 0L) ? + (-yIndex[k] - Height2 * ((-yIndex[k]) / Height2)) + : (yIndex[k] - Height2 * (yIndex[k] / Height2))); + if (Height <= yIndex[k]) { + yIndex[k] = Height2 - yIndex[k]; + } + } + + // perform interpolation + interpolated = 0.0; + for(j = 0; j <= spline_degree; j++) { + p = Bcoeff + (yIndex[j] * Width); + w = 0.0; + for(i = 0; i <= spline_degree; i++) { + w += xWeight[i] * p[xIndex[i]]; + } + interpolated += yWeight[j] * w; + } + + return interpolated; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// FreeImage implementation + + +/** + Image translation and rotation using B-Splines. + + @param dib Input 8-bit greyscale image + @param angle Output image rotation in degree + @param x_shift Output image horizontal shift + @param y_shift Output image vertical shift + @param x_origin Output origin of the x-axis + @param y_origin Output origin of the y-axis + @param spline_degree Output degree of the B-spline model + @param use_mask Whether or not to mask the image + @return Returns the translated & rotated dib if successful, returns NULL otherwise +*/ +static FIBITMAP * +Rotate8Bit(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, long spline_degree, BOOL use_mask) { + double *ImageRasterArray; + double p; + double a11, a12, a21, a22; + double x0, y0, x1, y1; + long x, y; + long spline; + bool bResult; + + int bpp = FreeImage_GetBPP(dib); + if(bpp != 8) { + return NULL; + } + + int width = FreeImage_GetWidth(dib); + int height = FreeImage_GetHeight(dib); + switch(spline_degree) { + case ROTATE_QUADRATIC: + spline = 2L; // Use splines of degree 2 (quadratic interpolation) + break; + case ROTATE_CUBIC: + spline = 3L; // Use splines of degree 3 (cubic interpolation) + break; + case ROTATE_QUARTIC: + spline = 4L; // Use splines of degree 4 (quartic interpolation) + break; + case ROTATE_QUINTIC: + spline = 5L; // Use splines of degree 5 (quintic interpolation) + break; + default: + spline = 3L; + } + + // allocate output image + FIBITMAP *dst = FreeImage_Allocate(width, height, bpp); + if(!dst) + return NULL; + // buid a grey scale palette + RGBQUAD *pal = FreeImage_GetPalette(dst); + for(int i = 0; i < 256; i++) { + pal[i].rgbRed = pal[i].rgbGreen = pal[i].rgbBlue = (BYTE)i; + } + + // allocate a temporary array + ImageRasterArray = (double*)malloc(width * height * sizeof(double)); + if(!ImageRasterArray) { + FreeImage_Unload(dst); + return NULL; + } + // copy data samples + for(y = 0; y < height; y++) { + double *pImage = &ImageRasterArray[y*width]; + BYTE *src_bits = FreeImage_GetScanLine(dib, height-1-y); + + for(x = 0; x < width; x++) { + pImage[x] = (double)src_bits[x]; + } + } + + // convert between a representation based on image samples + // and a representation based on image B-spline coefficients + bResult = SamplesToCoefficients(ImageRasterArray, width, height, spline); + if(!bResult) { + FreeImage_Unload(dst); + free(ImageRasterArray); + return NULL; + } + + // prepare the geometry + angle *= PI / 180.0; + a11 = cos(angle); + a12 = -sin(angle); + a21 = sin(angle); + a22 = cos(angle); + x0 = a11 * (x_shift + x_origin) + a12 * (y_shift + y_origin); + y0 = a21 * (x_shift + x_origin) + a22 * (y_shift + y_origin); + x_shift = x_origin - x0; + y_shift = y_origin - y0; + + // visit all pixels of the output image and assign their value + for(y = 0; y < height; y++) { + BYTE *dst_bits = FreeImage_GetScanLine(dst, height-1-y); + + x0 = a12 * (double)y + x_shift; + y0 = a22 * (double)y + y_shift; + + for(x = 0; x < width; x++) { + x1 = x0 + a11 * (double)x; + y1 = y0 + a21 * (double)x; + if(use_mask) { + if((x1 <= -0.5) || (((double)width - 0.5) <= x1) || (y1 <= -0.5) || (((double)height - 0.5) <= y1)) { + p = 0; + } + else { + p = (double)InterpolatedValue(ImageRasterArray, width, height, x1, y1, spline); + } + } + else { + p = (double)InterpolatedValue(ImageRasterArray, width, height, x1, y1, spline); + } + // clamp and convert to BYTE + dst_bits[x] = (BYTE)MIN(MAX((int)0, (int)(p + 0.5)), (int)255); + } + } + + // free working array and return + free(ImageRasterArray); + + return dst; +} + +/** + Image rotation using a 3rd order (cubic) B-Splines. + + @param dib Input dib (8, 24 or 32-bit) + @param angle Output image rotation + @param x_shift Output image horizontal shift + @param y_shift Output image vertical shift + @param x_origin Output origin of the x-axis + @param y_origin Output origin of the y-axis + @param use_mask Whether or not to mask the image + @return Returns the translated & rotated dib if successful, returns NULL otherwise +*/ +FIBITMAP * DLL_CALLCONV +FreeImage_RotateEx(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, BOOL use_mask) { + + int x, y, bpp; + int channel, nb_channels; + BYTE *src_bits, *dst_bits; + FIBITMAP *src8 = NULL, *dst8 = NULL, *dst = NULL; + + if(!FreeImage_HasPixels(dib)) return NULL; + + try { + + bpp = FreeImage_GetBPP(dib); + + if(bpp == 8) { + FIBITMAP *dst_8 = Rotate8Bit(dib, angle, x_shift, y_shift, x_origin, y_origin, ROTATE_CUBIC, use_mask); + if(dst_8) { + // copy metadata from src to dst + FreeImage_CloneMetadata(dst_8, dib); + } + return dst_8; + } + if((bpp == 24) || (bpp == 32)) { + // allocate dst image + int width = FreeImage_GetWidth(dib); + int height = FreeImage_GetHeight(dib); + if( bpp == 24 ) { + dst = FreeImage_Allocate(width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + } else { + dst = FreeImage_Allocate(width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + } + if(!dst) throw(1); + + // allocate a temporary 8-bit dib (no need to build a palette) + src8 = FreeImage_Allocate(width, height, 8); + if(!src8) throw(1); + + // process each channel separately + // ------------------------------- + nb_channels = (bpp / 8); + + for(channel = 0; channel < nb_channels; channel++) { + // extract channel from source dib + for(y = 0; y < height; y++) { + src_bits = FreeImage_GetScanLine(dib, y); + dst_bits = FreeImage_GetScanLine(src8, y); + for(x = 0; x < width; x++) { + dst_bits[x] = src_bits[channel]; + src_bits += nb_channels; + } + } + + // process channel + dst8 = Rotate8Bit(src8, angle, x_shift, y_shift, x_origin, y_origin, ROTATE_CUBIC, use_mask); + if(!dst8) throw(1); + + // insert channel to destination dib + for(y = 0; y < height; y++) { + src_bits = FreeImage_GetScanLine(dst8, y); + dst_bits = FreeImage_GetScanLine(dst, y); + for(x = 0; x < width; x++) { + dst_bits[channel] = src_bits[x]; + dst_bits += nb_channels; + } + } + + FreeImage_Unload(dst8); + } + + FreeImage_Unload(src8); + + // copy metadata from src to dst + FreeImage_CloneMetadata(dst, dib); + + return dst; + } + } catch(int) { + if(src8) FreeImage_Unload(src8); + if(dst8) FreeImage_Unload(dst8); + if(dst) FreeImage_Unload(dst); + } + + return NULL; +} diff --git a/plugins/FreeImage/Source/FreeImageToolkit/Channels.cpp b/plugins/FreeImage/Source/FreeImageToolkit/Channels.cpp index 36475f5aba..5f01ad815f 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/Channels.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/Channels.cpp @@ -1,488 +1,488 @@ -// ==========================================================
-// 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 "FreeImage.h"
-#include "Utilities.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;
-}
+// ========================================================== +// 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 "FreeImage.h" +#include "Utilities.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/plugins/FreeImage/Source/FreeImageToolkit/ClassicRotate.cpp b/plugins/FreeImage/Source/FreeImageToolkit/ClassicRotate.cpp index 9e5ea7c9c3..83c2f92123 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/ClassicRotate.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/ClassicRotate.cpp @@ -1,956 +1,917 @@ -// ==========================================================
-// Bitmap rotation by means of 3 shears.
-//
-// Design and implementation by
-// - Hervé Drolon (drolon@infonie.fr)
-// - Thorsten Radde (support@IdealSoftware.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!
-// ==========================================================
-
-/*
- ============================================================
- 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 "FreeImage.h"
-#include "Utilities.h"
-
-#define RBLOCK 64 // image blocks of RBLOCK*RBLOCK pixels
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Prototypes definition
-
-static void HorizontalSkew(FIBITMAP *src, FIBITMAP *dst, int row, int iOffset, double dWeight, const void *bkcolor);
-static void VerticalSkew(FIBITMAP *src, FIBITMAP *dst, int col, int iOffset, double dWeight, const void *bkcolor);
-static FIBITMAP* Rotate90(FIBITMAP *src);
-static FIBITMAP* Rotate180(FIBITMAP *src);
-static FIBITMAP* Rotate270(FIBITMAP *src);
-static FIBITMAP* Rotate45(FIBITMAP *src, double dAngle, const void *bkcolor);
-static FIBITMAP* RotateAny(FIBITMAP *src, double dAngle, const void *bkcolor);
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
-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) {
- unsigned i, j;
- int iXPos;
-
- unsigned src_width = FreeImage_GetWidth(src);
- 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
- unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
- // calculate the number of samples per pixel
- 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);
- }
- memcpy(&pxlOldLeft[0], bkcolor, bytespp);
- } else {
- if(iOffset > 0) {
- memset(dst_bits, 0, iOffset * bytespp);
- }
- memset(&pxlOldLeft[0], 0, bytespp);
- }
-
- for(i = 0; i < src_width; i++) {
- // loop through row pixels
- memcpy(&pxlSrc[0], src_bits, bytespp);
- // calculate weights
- for(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(j = 0; j < samples; j++) {
- pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);
- }
- memcpy(&dst_bits[iXPos*bytespp], &pxlSrc[0], bytespp);
- }
- // save leftover for next pixel in scan
- memcpy(&pxlOldLeft[0], &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
- memcpy(dst_bits, &pxlOldLeft[0], bytespp);
-
- // clear to the right of the skewed line with background
- dst_bits += bytespp;
- if(bkcolor) {
- for(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) {
- unsigned i, j;
- 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
- unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
- // calculate the number of samples per pixel
- unsigned samples = bytespp / sizeof(T);
-
- unsigned src_pitch = FreeImage_GetPitch(src);
- unsigned dst_pitch = FreeImage_GetPitch(dst);
- 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(i = 0; i < src_height; i++) {
- // loop through column pixels
- memcpy(&pxlSrc[0], src_bits, bytespp);
- // calculate weights
- for(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(j = 0; j < samples; j++) {
- pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);
- }
- dst_bits = FreeImage_GetScanLine(dst, iYPos) + index;
- memcpy(dst_bits, &pxlSrc[0], bytespp);
- }
- // save leftover for next pixel in scan
- memcpy(&pxlOldLeft[0], &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
- memcpy(dst_bits, &pxlOldLeft[0], bytespp);
-
- // clear below skewed line with background
- if(bkcolor) {
- while(++iYPos < (int)dst_height) {
- dst_bits += dst_pitch;
- memcpy(dst_bits, 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) {
- int x, y, y2;
-
- int bpp = FreeImage_GetBPP(src);
-
- int src_width = FreeImage_GetWidth(src);
- int src_height = FreeImage_GetHeight(src);
- int dst_width = src_height;
- int 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
- int src_pitch = FreeImage_GetPitch(src);
- int dst_pitch = FreeImage_GetPitch(dst);
-
- switch(image_type) {
- case FIT_BITMAP:
- if(bpp == 1) {
- // speedy rotate for BW images
-
- BYTE *sbits, *dbits, *dbitsmax, bitpos, *nrow, *srcdisp;
- div_t div_r;
-
- BYTE *bsrc = FreeImage_GetBits(src);
- BYTE *bdest = FreeImage_GetBits(dst);
- dbitsmax = bdest + dst_height * dst_pitch - 1;
-
- for(y = 0; y < src_height; y++) {
- // figure out the column we are going to be copying to
- div_r = div(y, 8);
- // set bit pos of src column byte
- bitpos = (BYTE)(128 >> div_r.rem);
- srcdisp = bsrc + y * src_pitch;
- for (x = 0; x < src_pitch; x++) {
- // get source bits
- sbits = srcdisp + x;
- // get destination column
- nrow = bdest + (dst_height - 1 - (x * 8)) * dst_pitch + div_r.quot;
- for (int z = 0; z < 8; z++) {
- // get destination 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.
-
- int xs, ys; // x-segment and y-segment
- 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)
- int bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
-
- for(xs = 0; xs < dst_width; xs += RBLOCK) { // for all image blocks of RBLOCK*RBLOCK pixels
- for(ys = 0; ys < dst_height; ys += RBLOCK) {
- for(y = ys; y < MIN(dst_height, ys + RBLOCK); y++) { // do rotation
- 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 (x = xs; x < MIN(dst_width, xs + RBLOCK); x++) {
- // dst.SetPixel(x, y, src.GetPixel(y2, x));
- for(int j = 0; j < bytespp; j++) {
- dst_bits[j] = src_bits[j];
- }
- 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
- int bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
-
- for(y = 0; y < dst_height; y++) {
- BYTE *src_bits = bsrc + (src_width - 1 - y) * bytespp;
- BYTE *dst_bits = bdest + (y * dst_pitch);
- for(x = 0; x < dst_width; x++) {
- for(int j = 0; j < bytespp; j++) {
- dst_bits[j] = src_bits[j];
- }
- 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;
-
- int bpp = FreeImage_GetBPP(src);
-
- int src_width = FreeImage_GetWidth(src);
- int src_height = FreeImage_GetHeight(src);
- int dst_width = src_width;
- 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));
- }
- }
- }
- else if((bpp == 8) || (bpp == 24) || (bpp == 32)) {
- // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
- 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)
- for(k = 0; k < bytespp; k++) {
- dst_bits[k] = src_bits[k];
- }
- src_bits += bytespp;
- dst_bits -= bytespp;
- }
- }
- }
- break;
- 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
- 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)
- for(k = 0; k < bytespp; k++) {
- dst_bits[k] = src_bits[k];
- }
- 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 x, x2, y, dlineup;
-
- int bpp = FreeImage_GetBPP(src);
-
- int src_width = FreeImage_GetWidth(src);
- int src_height = FreeImage_GetHeight(src);
- int dst_width = src_height;
- int 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
- int src_pitch = FreeImage_GetPitch(src);
- int dst_pitch = FreeImage_GetPitch(dst);
-
- switch(image_type) {
- case FIT_BITMAP:
- if(bpp == 1) {
- // speedy rotate for BW images
-
- BYTE *sbits, *dbits, *dbitsmax, bitpos, *nrow, *srcdisp;
- div_t div_r;
-
- BYTE *bsrc = FreeImage_GetBits(src);
- BYTE *bdest = FreeImage_GetBits(dst);
- dbitsmax = bdest + dst_height * dst_pitch - 1;
- dlineup = 8 * dst_pitch - dst_width;
-
- for(y = 0; y < src_height; y++) {
- // figure out the column we are going to be copying to
- div_r = div(y + dlineup, 8);
- // set bit pos of src column byte
- bitpos = (BYTE)(1 << div_r.rem);
- srcdisp = bsrc + y * src_pitch;
- for (x = 0; x < src_pitch; x++) {
- // get source bits
- sbits = srcdisp + x;
- // get destination column
- nrow = bdest + (x * 8) * dst_pitch + dst_pitch - 1 - div_r.quot;
- for (int z = 0; z < 8; z++) {
- // get destination 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.
-
- int xs, ys; // x-segment and y-segment
- 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)
- int bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
-
- for(xs = 0; xs < dst_width; xs += RBLOCK) { // for all image blocks of RBLOCK*RBLOCK pixels
- for(ys = 0; ys < dst_height; ys += RBLOCK) {
- for(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 (y = ys; y < MIN(dst_height, ys + RBLOCK); y++) {
- // dst.SetPixel(x, y, src.GetPixel(y, x2));
- for(int j = 0; j < bytespp; j++) {
- dst_bits[j] = src_bits[j];
- }
- 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
- int bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
-
- for(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(x = 0; x < dst_width; x++) {
- for(int j = 0; j < bytespp; j++) {
- dst_bits[j] = src_bits[j];
- }
- 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;
-
- unsigned bpp = FreeImage_GetBPP(src);
-
- double dRadAngle = dAngle * ROTATE_PI / double(180); // Angle in radians
- double dSinE = sin(dRadAngle);
- double dTan = tan(dRadAngle / 2);
-
- unsigned src_width = FreeImage_GetWidth(src);
- unsigned src_height = FreeImage_GetHeight(src);
-
- FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
-
- // Calc first shear (horizontal) destination image dimensions
- unsigned width_1 = src_width + unsigned((double)src_height * fabs(dTan) + 0.5);
- 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
- 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
- unsigned width_3 = unsigned(double(src_height) * fabs(dSinE) + double(src_width) * cos(dRadAngle) + 0.5) + 1;
- 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;
-}
-
+// ========================================================== +// 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 "FreeImage.h" +#include "Utilities.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/plugins/FreeImage/Source/FreeImageToolkit/Colors.cpp b/plugins/FreeImage/Source/FreeImageToolkit/Colors.cpp index 13b61195ba..67191768bb 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/Colors.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/Colors.cpp @@ -1,967 +1,967 @@ -// ==========================================================
-// 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 "FreeImage.h"
-#include "Utilities.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);
-}
-
+// ========================================================== +// 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 "FreeImage.h" +#include "Utilities.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/plugins/FreeImage/Source/FreeImageToolkit/CopyPaste.cpp b/plugins/FreeImage/Source/FreeImageToolkit/CopyPaste.cpp index b66efbf9e8..e4b8155739 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/CopyPaste.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/CopyPaste.cpp @@ -1,747 +1,747 @@ -// ==========================================================
-// 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)
-//
-// 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 "FreeImage.h"
-#include "Utilities.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;
-}
-
-
+// ========================================================== +// 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) +// +// 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 "FreeImage.h" +#include "Utilities.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; +} + + diff --git a/plugins/FreeImage/Source/FreeImageToolkit/Display.cpp b/plugins/FreeImage/Source/FreeImageToolkit/Display.cpp index f7e9aa7689..245c5c3a18 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/Display.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/Display.cpp @@ -1,230 +1,230 @@ -// ==========================================================
-// 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 "FreeImage.h"
-#include "Utilities.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;
-}
-
+// ========================================================== +// 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 "FreeImage.h" +#include "Utilities.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/plugins/FreeImage/Source/FreeImageToolkit/Filters.h b/plugins/FreeImage/Source/FreeImageToolkit/Filters.h index 548997ae1e..7a45c493f4 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/Filters.h +++ b/plugins/FreeImage/Source/FreeImageToolkit/Filters.h @@ -1,287 +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_
+// ========================================================== +// 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/plugins/FreeImage/Source/FreeImageToolkit/Flip.cpp b/plugins/FreeImage/Source/FreeImageToolkit/Flip.cpp index 2f0f7d52e4..c7898a7813 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/Flip.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/Flip.cpp @@ -1,166 +1,166 @@ -// ==========================================================
-// 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 "FreeImage.h"
-#include "Utilities.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;
-}
-
+// ========================================================== +// 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 "FreeImage.h" +#include "Utilities.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/plugins/FreeImage/Source/FreeImageToolkit/JPEGTransform.cpp b/plugins/FreeImage/Source/FreeImageToolkit/JPEGTransform.cpp index 5b5fcefe2b..5822f748a6 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/JPEGTransform.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/JPEGTransform.cpp @@ -1,410 +1,410 @@ -// ==========================================================
-// JPEG lossless transformations
-//
-// Design and implementation by
-// - Petr Pytelka (pyta@lightcomp.com)
-// - 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!
-// ==========================================================
-
-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"
-}
-
-#include "FreeImage.h"
-#include "Utilities.h"
-
-// ----------------------------------------------------------
-// IO filename handling
-// ----------------------------------------------------------
-
-typedef struct tagFilenameIO {
- const char *src_file;
- const char *dst_file;
- const wchar_t *wsrc_file;
- const wchar_t *wdst_file;
-} FilenameIO;
-
-// ----------------------------------------------------------
-// Error handling
-// ----------------------------------------------------------
-
-/**
- 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
-// ----------------------------------------------------------
-
-static BOOL
-LosslessTransform(const FilenameIO *filenameIO, FREE_IMAGE_JPEG_OPERATION operation, const char *crop, BOOL perfect) {
- // We assume all-in-memory processing and can therefore use only a
- // single file pointer for sequential input and output operation
- FILE *fp = NULL;
-
- // check for UNICODE filenames - previous structure filling was done before
- bool bUseUnicode = filenameIO && filenameIO->wsrc_file && filenameIO->wdst_file;
-
- // 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;
- break;
- case FIJPEG_OP_FLIP_V: // vertical flip
- transfoptions.transform = JXFORM_FLIP_V;
- break;
- case FIJPEG_OP_TRANSPOSE: // transpose across UL-to-LR axis
- transfoptions.transform = JXFORM_TRANSPOSE;
- break;
- case FIJPEG_OP_TRANSVERSE: // transpose across UR-to-LL axis
- transfoptions.transform = JXFORM_TRANSVERSE;
- break;
- case FIJPEG_OP_ROTATE_90: // 90-degree clockwise rotation
- transfoptions.transform = JXFORM_ROT_90;
- break;
- case FIJPEG_OP_ROTATE_180: // 180-degree rotation
- transfoptions.transform = JXFORM_ROT_180;
- break;
- case FIJPEG_OP_ROTATE_270: // 270-degree clockwise (or 90 ccw)
- transfoptions.transform = JXFORM_ROT_270;
- 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);
-
- // crop option
- if(crop != NULL) {
- if(!jtransform_parse_crop_spec(&transfoptions, crop)) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop);
- throw(1);
- }
- }
-
- // Open the input file
- if(bUseUnicode) {
-#ifdef _WIN32
- if((fp = _wfopen(filenameIO->wsrc_file, L"rb")) == NULL) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open input file for reading");
- }
-#else
- fp = NULL;
-#endif // _WIN32
- } else {
- if((fp = fopen(filenameIO->src_file, "rb")) == NULL) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open %s for reading", filenameIO->src_file);
- }
- }
- if(fp == NULL) {
- jpeg_destroy_compress(&dstinfo);
- jpeg_destroy_decompress(&srcinfo);
- return FALSE;
- }
-
- // Specify data source for decompression
- jpeg_stdio_src(&srcinfo, fp);
-
- // Enable saving of extra markers that we want to copy
- jcopy_markers_setup(&srcinfo, copyoption);
-
- // Read the file header
- jpeg_read_header(&srcinfo, TRUE);
-
- // 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);
- }
-
- // 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);
-
- // Close the input file.
- // 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.
- fclose(fp);
-
- // Open the output file
- if(bUseUnicode) {
-#ifdef _WIN32
- if((fp = _wfopen(filenameIO->wdst_file, L"wb")) == NULL) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open output file for writing");
- }
-#else
- fp = NULL;
-#endif // _WIN32
- } else {
- if((fp = fopen(filenameIO->dst_file, "wb")) == NULL) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open %s for writing", filenameIO->dst_file);
- }
- }
- if(fp == NULL) {
- throw(1);
- }
-
- // Specify data destination for compression
- jpeg_stdio_dest(&dstinfo, fp);
-
- // 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);
-
- // Close output file and return
- fclose(fp);
- }
- catch(...) {
- if(fp) fclose(fp);
- jpeg_destroy_compress(&dstinfo);
- jpeg_destroy_decompress(&srcinfo);
- return FALSE;
- }
-
- return TRUE;
-}
-
-// ----------------------------------------------------------
-// FreeImage interface
-// ----------------------------------------------------------
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
- try {
- // check the src file format
- if(FreeImage_GetFileType(src_file) != FIF_JPEG) {
- throw FI_MSG_ERROR_MAGIC_NUMBER;
- }
-
- // setup IO
- FilenameIO filenameIO;
- memset(&filenameIO, 0, sizeof(FilenameIO));
- filenameIO.src_file = src_file;
- filenameIO.dst_file = dst_file;
-
- // perform the transformation
- return LosslessTransform(&filenameIO, operation, NULL, perfect);
-
- } catch(const char *text) {
- FreeImage_OutputMessageProc(FIF_JPEG, text);
- return FALSE;
- }
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) {
- char crop[64];
-
- try {
- // check the src file format
- if(FreeImage_GetFileType(src_file) != FIF_JPEG) {
- throw FI_MSG_ERROR_MAGIC_NUMBER;
- }
-
- // normalize the rectangle
- if(right < left) {
- INPLACESWAP(left, right);
- }
- if(bottom < top) {
- INPLACESWAP(top, bottom);
- }
-
- // build the crop option
- sprintf(crop, "%dx%d+%d+%d", right - left, bottom - top, left, top);
-
- // setup IO
- FilenameIO filenameIO;
- memset(&filenameIO, 0, sizeof(FilenameIO));
- filenameIO.src_file = src_file;
- filenameIO.dst_file = dst_file;
-
- // perform the transformation
- return LosslessTransform(&filenameIO, FIJPEG_OP_NONE, crop, FALSE);
-
- } catch(const char *text) {
- FreeImage_OutputMessageProc(FIF_JPEG, text);
- return FALSE;
- }
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
-#ifdef _WIN32
- try {
- // check the src file format
- if(FreeImage_GetFileTypeU(src_file) != FIF_JPEG) {
- throw FI_MSG_ERROR_MAGIC_NUMBER;
- }
-
- // setup IO
- FilenameIO filenameIO;
- memset(&filenameIO, 0, sizeof(FilenameIO));
- filenameIO.wsrc_file = src_file;
- filenameIO.wdst_file = dst_file;
-
- // perform the transformation
- return LosslessTransform(&filenameIO, operation, NULL, perfect);
-
- } catch(const char *text) {
- FreeImage_OutputMessageProc(FIF_JPEG, text);
- }
-#endif /// _WIN32
- return FALSE;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom) {
-#ifdef _WIN32
- char crop[64];
-
- try {
- // check the src file format
- if(FreeImage_GetFileTypeU(src_file) != FIF_JPEG) {
- throw FI_MSG_ERROR_MAGIC_NUMBER;
- }
-
- // normalize the rectangle
- if(right < left) {
- INPLACESWAP(left, right);
- }
- if(bottom < top) {
- INPLACESWAP(top, bottom);
- }
-
- // build the crop option
- sprintf(crop, "%dx%d+%d+%d", right - left, bottom - top, left, top);
-
- // setup IO
- FilenameIO filenameIO;
- memset(&filenameIO, 0, sizeof(FilenameIO));
- filenameIO.wsrc_file = src_file;
- filenameIO.wdst_file = dst_file;
-
- // perform the transformation
- return LosslessTransform(&filenameIO, FIJPEG_OP_NONE, crop, FALSE);
-
- } catch(const char *text) {
- FreeImage_OutputMessageProc(FIF_JPEG, text);
- }
-#endif // _WIN32
- return FALSE;
-}
+// ========================================================== +// JPEG lossless transformations +// +// Design and implementation by +// - Petr Pytelka (pyta@lightcomp.com) +// - 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! +// ========================================================== + +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" +} + +#include "FreeImage.h" +#include "Utilities.h" + +// ---------------------------------------------------------- +// IO filename handling +// ---------------------------------------------------------- + +typedef struct tagFilenameIO { + const char *src_file; + const char *dst_file; + const wchar_t *wsrc_file; + const wchar_t *wdst_file; +} FilenameIO; + +// ---------------------------------------------------------- +// Error handling +// ---------------------------------------------------------- + +/** + 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 +// ---------------------------------------------------------- + +static BOOL +LosslessTransform(const FilenameIO *filenameIO, FREE_IMAGE_JPEG_OPERATION operation, const char *crop, BOOL perfect) { + // We assume all-in-memory processing and can therefore use only a + // single file pointer for sequential input and output operation + FILE *fp = NULL; + + // check for UNICODE filenames - previous structure filling was done before + bool bUseUnicode = filenameIO && filenameIO->wsrc_file && filenameIO->wdst_file; + + // 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; + break; + case FIJPEG_OP_FLIP_V: // vertical flip + transfoptions.transform = JXFORM_FLIP_V; + break; + case FIJPEG_OP_TRANSPOSE: // transpose across UL-to-LR axis + transfoptions.transform = JXFORM_TRANSPOSE; + break; + case FIJPEG_OP_TRANSVERSE: // transpose across UR-to-LL axis + transfoptions.transform = JXFORM_TRANSVERSE; + break; + case FIJPEG_OP_ROTATE_90: // 90-degree clockwise rotation + transfoptions.transform = JXFORM_ROT_90; + break; + case FIJPEG_OP_ROTATE_180: // 180-degree rotation + transfoptions.transform = JXFORM_ROT_180; + break; + case FIJPEG_OP_ROTATE_270: // 270-degree clockwise (or 90 ccw) + transfoptions.transform = JXFORM_ROT_270; + 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); + + // crop option + if(crop != NULL) { + if(!jtransform_parse_crop_spec(&transfoptions, crop)) { + FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop); + throw(1); + } + } + + // Open the input file + if(bUseUnicode) { +#ifdef _WIN32 + if((fp = _wfopen(filenameIO->wsrc_file, L"rb")) == NULL) { + FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open input file for reading"); + } +#else + fp = NULL; +#endif // _WIN32 + } else { + if((fp = fopen(filenameIO->src_file, "rb")) == NULL) { + FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open %s for reading", filenameIO->src_file); + } + } + if(fp == NULL) { + jpeg_destroy_compress(&dstinfo); + jpeg_destroy_decompress(&srcinfo); + return FALSE; + } + + // Specify data source for decompression + jpeg_stdio_src(&srcinfo, fp); + + // Enable saving of extra markers that we want to copy + jcopy_markers_setup(&srcinfo, copyoption); + + // Read the file header + jpeg_read_header(&srcinfo, TRUE); + + // 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); + } + + // 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); + + // Close the input file. + // 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. + fclose(fp); + + // Open the output file + if(bUseUnicode) { +#ifdef _WIN32 + if((fp = _wfopen(filenameIO->wdst_file, L"wb")) == NULL) { + FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open output file for writing"); + } +#else + fp = NULL; +#endif // _WIN32 + } else { + if((fp = fopen(filenameIO->dst_file, "wb")) == NULL) { + FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open %s for writing", filenameIO->dst_file); + } + } + if(fp == NULL) { + throw(1); + } + + // Specify data destination for compression + jpeg_stdio_dest(&dstinfo, fp); + + // 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); + + // Close output file and return + fclose(fp); + } + catch(...) { + if(fp) fclose(fp); + jpeg_destroy_compress(&dstinfo); + jpeg_destroy_decompress(&srcinfo); + return FALSE; + } + + return TRUE; +} + +// ---------------------------------------------------------- +// FreeImage interface +// ---------------------------------------------------------- + +BOOL DLL_CALLCONV +FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) { + try { + // check the src file format + if(FreeImage_GetFileType(src_file) != FIF_JPEG) { + throw FI_MSG_ERROR_MAGIC_NUMBER; + } + + // setup IO + FilenameIO filenameIO; + memset(&filenameIO, 0, sizeof(FilenameIO)); + filenameIO.src_file = src_file; + filenameIO.dst_file = dst_file; + + // perform the transformation + return LosslessTransform(&filenameIO, operation, NULL, perfect); + + } catch(const char *text) { + FreeImage_OutputMessageProc(FIF_JPEG, text); + return FALSE; + } +} + +BOOL DLL_CALLCONV +FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) { + char crop[64]; + + try { + // check the src file format + if(FreeImage_GetFileType(src_file) != FIF_JPEG) { + throw FI_MSG_ERROR_MAGIC_NUMBER; + } + + // normalize the rectangle + if(right < left) { + INPLACESWAP(left, right); + } + if(bottom < top) { + INPLACESWAP(top, bottom); + } + + // build the crop option + sprintf(crop, "%dx%d+%d+%d", right - left, bottom - top, left, top); + + // setup IO + FilenameIO filenameIO; + memset(&filenameIO, 0, sizeof(FilenameIO)); + filenameIO.src_file = src_file; + filenameIO.dst_file = dst_file; + + // perform the transformation + return LosslessTransform(&filenameIO, FIJPEG_OP_NONE, crop, FALSE); + + } catch(const char *text) { + FreeImage_OutputMessageProc(FIF_JPEG, text); + return FALSE; + } +} + +BOOL DLL_CALLCONV +FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) { +#ifdef _WIN32 + try { + // check the src file format + if(FreeImage_GetFileTypeU(src_file) != FIF_JPEG) { + throw FI_MSG_ERROR_MAGIC_NUMBER; + } + + // setup IO + FilenameIO filenameIO; + memset(&filenameIO, 0, sizeof(FilenameIO)); + filenameIO.wsrc_file = src_file; + filenameIO.wdst_file = dst_file; + + // perform the transformation + return LosslessTransform(&filenameIO, operation, NULL, perfect); + + } catch(const char *text) { + FreeImage_OutputMessageProc(FIF_JPEG, text); + } +#endif /// _WIN32 + return FALSE; +} + +BOOL DLL_CALLCONV +FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom) { +#ifdef _WIN32 + char crop[64]; + + try { + // check the src file format + if(FreeImage_GetFileTypeU(src_file) != FIF_JPEG) { + throw FI_MSG_ERROR_MAGIC_NUMBER; + } + + // normalize the rectangle + if(right < left) { + INPLACESWAP(left, right); + } + if(bottom < top) { + INPLACESWAP(top, bottom); + } + + // build the crop option + sprintf(crop, "%dx%d+%d+%d", right - left, bottom - top, left, top); + + // setup IO + FilenameIO filenameIO; + memset(&filenameIO, 0, sizeof(FilenameIO)); + filenameIO.wsrc_file = src_file; + filenameIO.wdst_file = dst_file; + + // perform the transformation + return LosslessTransform(&filenameIO, FIJPEG_OP_NONE, crop, FALSE); + + } catch(const char *text) { + FreeImage_OutputMessageProc(FIF_JPEG, text); + } +#endif // _WIN32 + return FALSE; +} diff --git a/plugins/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp b/plugins/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp index 03281a16b5..3b577cbd0a 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/MultigridPoissonSolver.cpp @@ -1,505 +1,505 @@ -// ==========================================================
-// 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 "FreeImage.h"
-#include "Utilities.h"
-#include "ToneMapping.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 ñ, 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;
-}
-
+// ========================================================== +// 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 "FreeImage.h" +#include "Utilities.h" +#include "ToneMapping.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 ñ, 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/plugins/FreeImage/Source/FreeImageToolkit/Rescale.cpp b/plugins/FreeImage/Source/FreeImageToolkit/Rescale.cpp index 99a31862b9..d957d9cb5e 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/Rescale.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/Rescale.cpp @@ -1,231 +1,231 @@ -// ==========================================================
-// Upsampling / downsampling routine
-//
-// 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 "Resize.h"
-
-FIBITMAP * DLL_CALLCONV
-FreeImage_Rescale(FIBITMAP *src, int dst_width, int dst_height, FREE_IMAGE_FILTER filter) {
- FIBITMAP *dst = NULL;
-
- if (!FreeImage_HasPixels(src) || (dst_width <= 0) || (dst_height <= 0) || (FreeImage_GetWidth(src) <= 0) || (FreeImage_GetHeight(src) <= 0)) {
- 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);
-
- // perform upsampling or downsampling
-
- if((FreeImage_GetBPP(src) == 4) || (FreeImage_GetColorType(src) == FIC_PALETTE)) {
- // special case for 4-bit images or color map indexed images ...
- if(FreeImage_IsTransparent(src) == FALSE) {
- FIBITMAP *src24 = NULL;
- FIBITMAP *dst24 = NULL;
- try {
- // transparent conversion to 24-bit (any transparency table will be destroyed)
- src24 = FreeImage_ConvertTo24Bits(src);
- if(!src24) throw(1);
- // perform upsampling or downsampling
- dst24 = Engine.scale(src24, dst_width, dst_height);
- if(!dst24) throw(1);
- // color quantize to 8-bit
- dst = FreeImage_ColorQuantize(dst24, FIQ_NNQUANT);
- // free and return
- FreeImage_Unload(src24);
- FreeImage_Unload(dst24);
- } catch(int) {
- if(src24) FreeImage_Unload(src24);
- if(dst24) FreeImage_Unload(dst24);
- }
- } else {
- FIBITMAP *src32 = NULL;
- try {
- // transparent conversion to 32-bit (keep transparency)
- src32 = FreeImage_ConvertTo32Bits(src);
- if(!src32) throw(1);
- // perform upsampling or downsampling
- dst = Engine.scale(src32, dst_width, dst_height);
- if(!dst) throw(1);
- // free and return
- FreeImage_Unload(src32);
- } catch(int) {
- if(src32) FreeImage_Unload(src32);
- if(dst) FreeImage_Unload(dst);
- }
- }
- }
- else if((FreeImage_GetBPP(src) == 16) && (FreeImage_GetImageType(src) == FIT_BITMAP)) {
- // convert 16-bit RGB to 24-bit
- FIBITMAP *src24 = NULL;
- try {
- // transparent conversion to 24-bit (any transparency table will be destroyed)
- src24 = FreeImage_ConvertTo24Bits(src);
- if(!src24) throw(1);
- // perform upsampling or downsampling
- dst = Engine.scale(src24, dst_width, dst_height);
- if(!dst) throw(1);
- // free and return
- FreeImage_Unload(src24);
- } catch(int) {
- if(src24) FreeImage_Unload(src24);
- if(dst) FreeImage_Unload(dst);
- }
- }
- else {
- // normal case :
- // 1- or 8-bit greyscale, 24- or 32-bit RGB(A) images
- // 16-bit greyscale, 48- or 64-bit RGB(A) images
- // 32-bit float, 96- or 128-bit RGB(A) float images
- dst = Engine.scale(src, dst_width, dst_height);
- }
-
-
- delete pFilter;
-
- // copy metadata from src to dst
- FreeImage_CloneMetadata(dst, src);
-
- return dst;
-}
-
-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;
-}
+// ========================================================== +// Upsampling / downsampling routine +// +// 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 "Resize.h" + +FIBITMAP * DLL_CALLCONV +FreeImage_Rescale(FIBITMAP *src, int dst_width, int dst_height, FREE_IMAGE_FILTER filter) { + FIBITMAP *dst = NULL; + + if (!FreeImage_HasPixels(src) || (dst_width <= 0) || (dst_height <= 0) || (FreeImage_GetWidth(src) <= 0) || (FreeImage_GetHeight(src) <= 0)) { + 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); + + // perform upsampling or downsampling + + if((FreeImage_GetBPP(src) == 4) || (FreeImage_GetColorType(src) == FIC_PALETTE)) { + // special case for 4-bit images or color map indexed images ... + if(FreeImage_IsTransparent(src) == FALSE) { + FIBITMAP *src24 = NULL; + FIBITMAP *dst24 = NULL; + try { + // transparent conversion to 24-bit (any transparency table will be destroyed) + src24 = FreeImage_ConvertTo24Bits(src); + if(!src24) throw(1); + // perform upsampling or downsampling + dst24 = Engine.scale(src24, dst_width, dst_height); + if(!dst24) throw(1); + FreeImage_Unload(src24); src24 = NULL; + // color quantize to 8-bit + dst = FreeImage_ColorQuantize(dst24, FIQ_NNQUANT); + // free and return + FreeImage_Unload(dst24); + } catch(int) { + if(src24) FreeImage_Unload(src24); + if(dst24) FreeImage_Unload(dst24); + } + } else { + FIBITMAP *src32 = NULL; + try { + // transparent conversion to 32-bit (keep transparency) + src32 = FreeImage_ConvertTo32Bits(src); + if(!src32) throw(1); + // perform upsampling or downsampling + dst = Engine.scale(src32, dst_width, dst_height); + if(!dst) throw(1); + // free and return + FreeImage_Unload(src32); + } catch(int) { + if(src32) FreeImage_Unload(src32); + if(dst) FreeImage_Unload(dst); + } + } + } + else if((FreeImage_GetBPP(src) == 16) && (FreeImage_GetImageType(src) == FIT_BITMAP)) { + // convert 16-bit RGB to 24-bit + FIBITMAP *src24 = NULL; + try { + // transparent conversion to 24-bit (any transparency table will be destroyed) + src24 = FreeImage_ConvertTo24Bits(src); + if(!src24) throw(1); + // perform upsampling or downsampling + dst = Engine.scale(src24, dst_width, dst_height); + if(!dst) throw(1); + // free and return + FreeImage_Unload(src24); + } catch(int) { + if(src24) FreeImage_Unload(src24); + if(dst) FreeImage_Unload(dst); + } + } + else { + // normal case : + // 1- or 8-bit greyscale, 24- or 32-bit RGB(A) images + // 16-bit greyscale, 48- or 64-bit RGB(A) images + // 32-bit float, 96- or 128-bit RGB(A) float images + dst = Engine.scale(src, dst_width, dst_height); + } + + + delete pFilter; + + // copy metadata from src to dst + FreeImage_CloneMetadata(dst, src); + + return dst; +} + +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/plugins/FreeImage/Source/FreeImageToolkit/Resize.cpp b/plugins/FreeImage/Source/FreeImageToolkit/Resize.cpp index c98fe97391..5dcec62af0 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/Resize.cpp +++ b/plugins/FreeImage/Source/FreeImageToolkit/Resize.cpp @@ -1,646 +1,656 @@ -// ==========================================================
-// Upsampling / downsampling classes
-//
-// Design and implementation by
-// - Hervé Drolon (drolon@infonie.fr)
-// - Detlev Vendt (detlev.vendt@brillit.de)
-//
-// 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 "Resize.h"
-
-/**
- Filter weights table.
- This class stores contribution information for an entire line (row or column).
-*/
-CWeightsTable::CWeightsTable(CGenericFilter *pFilter, DWORD uDstSize, DWORD uSrcSize) {
- DWORD u;
- double dWidth;
- double dFScale = 1.0;
- double dFilterWidth = pFilter->GetWidth();
-
- // scale factor
- double dScale = double(uDstSize) / double(uSrcSize);
-
- if(dScale < 1.0) {
- // minification
- dWidth = dFilterWidth / dScale;
- dFScale = dScale;
- } else {
- // magnification
- dWidth= dFilterWidth;
- }
-
- // allocate a new line contributions structure
- //
- // window size is the number of sampled pixels
- m_WindowSize = 2 * (int)ceil(dWidth) + 1;
- m_LineLength = uDstSize;
- // allocate list of contributions
- m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
- for(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
- double dOffset = (0.5 / dScale) - 0.5;
-
-
- for(u = 0; u < m_LineLength; u++) {
- // scan through line of contributions
- double dCenter = (double)u / dScale + dOffset; // reverse mapping
- // find the significant edge points that affect the pixel
- int iLeft = MAX (0, (int)floor (dCenter - dWidth));
- int iRight = MIN ((int)ceil (dCenter + dWidth), int(uSrcSize) - 1);
-
- // cut edge points to fit in filter window in case of spill-off
- if((iRight - iLeft + 1) > int(m_WindowSize)) {
- if(iLeft < (int(uSrcSize) - 1 / 2)) {
- iLeft++;
- } else {
- iRight--;
- }
- }
-
- m_WeightTable[u].Left = iLeft;
- m_WeightTable[u].Right = iRight;
-
- int iSrc = 0;
- double dTotalWeight = 0; // zero sum of weights
- for(iSrc = iLeft; iSrc <= iRight; iSrc++) {
- // calculate weights
- double weight = dFScale * pFilter->Filter(dFScale * (dCenter - (double)iSrc));
- m_WeightTable[u].Weights[iSrc-iLeft] = weight;
- dTotalWeight += weight;
- }
- if((dTotalWeight > 0) && (dTotalWeight != 1)) {
- // normalize weight of neighbouring points
- for(iSrc = iLeft; iSrc <= iRight; iSrc++) {
- // normalize point
- m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight;
- }
- // simplify the filter, discarding null weights at the right
- iSrc = iRight - iLeft;
- while(m_WeightTable[u].Weights[iSrc] == 0){
- m_WeightTable[u].Right--;
- iSrc--;
- if(m_WeightTable[u].Right == m_WeightTable[u].Left)
- break;
- }
-
- }
- }
-}
-
-CWeightsTable::~CWeightsTable() {
- for(DWORD u = 0; u < m_LineLength; u++) {
- // free contributions for every pixel
- free(m_WeightTable[u].Weights);
- }
- // free list of pixels contributions
- free(m_WeightTable);
-}
-
-// ---------------------------------------------
-
-/**
- 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 8-, 24- and 32-bit buffers.<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
-
-*/
-
-FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height) {
- DWORD src_width = FreeImage_GetWidth(src);
- DWORD src_height = FreeImage_GetHeight(src);
-
- unsigned redMask = FreeImage_GetRedMask(src);
- unsigned greenMask = FreeImage_GetGreenMask(src);
- unsigned blueMask = FreeImage_GetBlueMask(src);
-
- unsigned bpp = FreeImage_GetBPP(src);
- if(bpp == 1) {
- // convert output to 8-bit
- bpp = 8;
- }
-
- FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
-
- // allocate the dst image
- FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp, redMask, greenMask, blueMask);
- if(!dst) return NULL;
-
- if(bpp == 8) {
- if(FreeImage_GetColorType(src) == FIC_MINISWHITE) {
- // build an inverted greyscale palette
- RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
- for(int i = 0; i < 256; i++) {
- dst_pal[i].rgbRed = dst_pal[i].rgbGreen =
- dst_pal[i].rgbBlue = (BYTE)(255 - i);
- }
- } else {
- // build a greyscale palette
- RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
- for(int i = 0; i < 256; i++) {
- dst_pal[i].rgbRed = dst_pal[i].rgbGreen =
- dst_pal[i].rgbBlue = (BYTE)i;
- }
- }
- }
-
- // decide which filtering order (xy or yx) is faster for this mapping by
- // counting convolution multiplies
-
- if(dst_width*src_height <= dst_height*src_width) {
- // xy filtering
- // -------------
-
- // allocate a temporary image
- FIBITMAP *tmp = FreeImage_AllocateT(image_type, dst_width, src_height, bpp, redMask, greenMask, blueMask);
- if(!tmp) {
- FreeImage_Unload(dst);
- return NULL;
- }
-
- // scale source image horizontally into temporary image
- horizontalFilter(src, src_width, src_height, tmp, dst_width, src_height);
-
- // scale temporary image vertically into result image
- verticalFilter(tmp, dst_width, src_height, dst, dst_width, dst_height);
-
- // free temporary image
- FreeImage_Unload(tmp);
-
- } else {
- // yx filtering
- // -------------
-
- // allocate a temporary image
- FIBITMAP *tmp = FreeImage_AllocateT(image_type, src_width, dst_height, bpp, redMask, greenMask, blueMask);
- if(!tmp) {
- FreeImage_Unload(dst);
- return NULL;
- }
-
- // scale source image vertically into temporary image
- verticalFilter(src, src_width, src_height, tmp, src_width, dst_height);
-
- // scale temporary image horizontally into result image
- horizontalFilter(tmp, src_width, dst_height, dst, dst_width, dst_height);
-
- // free temporary image
- FreeImage_Unload(tmp);
- }
-
- return dst;
-}
-
-
-/// Performs horizontal image filtering
-void CResizeEngine::horizontalFilter(FIBITMAP *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height) {
- if(dst_width == src_width) {
- // no scaling required, just copy
- switch(FreeImage_GetBPP(src)) {
- case 1:
- {
- if(FreeImage_GetBPP(dst) != 8) break;
- for(unsigned y = 0; y < dst_height; y++) {
- // convert each row
- BYTE *src_bits = FreeImage_GetScanLine(src, y);
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
- FreeImage_ConvertLine1To8(dst_bits, src_bits, dst_width);
- }
- }
- break;
-
- default:
- {
- BYTE *src_bits = FreeImage_GetBits(src);
- BYTE *dst_bits = FreeImage_GetBits(dst);
- memcpy(dst_bits, src_bits, dst_height * FreeImage_GetPitch(dst));
- }
- break;
- }
- }
- else {
- unsigned index; // pixel index
-
- // 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:
- {
- // scale and convert to 8-bit
- if(FreeImage_GetBPP(dst) != 8) break;
-
- for(unsigned y = 0; y < dst_height; y++) {
- // scale each row
- BYTE *src_bits = FreeImage_GetScanLine(src, y);
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for(unsigned x = 0; x < dst_width; x++) {
- // loop through row
- double value = 0;
- int iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- int iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
-
- for(int i = iLeft; i <= iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- double weight = weightsTable.getWeight(x, i-iLeft);
-
- BYTE pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
- value += (weight * (double)pixel);
- }
- value *= 255;
-
- // clamp and place result in destination pixel
- dst_bits[x] = (BYTE)MIN(MAX((int)0, (int)(value + 0.5)), (int)255);
- }
- }
- }
- break;
-
- case 8:
- case 24:
- case 32:
- {
- // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
- unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
-
- for(unsigned y = 0; y < dst_height; y++) {
- // scale each row
- BYTE *src_bits = FreeImage_GetScanLine(src, y);
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for(unsigned x = 0; x < dst_width; x++) {
- // loop through row
- double value[4] = {0, 0, 0, 0}; // 4 = 32bpp max
- int iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- int iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
-
- for(int i = iLeft; i <= iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- double weight = weightsTable.getWeight(x, i-iLeft);
-
- index = i * bytespp;
- for (unsigned j = 0; j < bytespp; j++) {
- value[j] += (weight * (double)src_bits[index++]);
- }
- }
-
- // clamp and place result in destination pixel
- for (unsigned j = 0; j < bytespp; j++) {
- dst_bits[j] = (BYTE)MIN(MAX((int)0, (int)(value[j] + 0.5)), (int)255);
- }
-
- dst_bits += bytespp;
- }
- }
- }
- break;
- }
- }
- break;
-
- case FIT_UINT16:
- case FIT_RGB16:
- case FIT_RGBA16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- unsigned wordspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(WORD);
-
- for(unsigned y = 0; y < dst_height; y++) {
- // scale each row
- WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y);
- WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
-
- for(unsigned x = 0; x < dst_width; x++) {
- // loop through row
- double value[4] = {0, 0, 0, 0}; // 4 = 64bpp max
- int iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- int iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
-
- for(int i = iLeft; i <= iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- double weight = weightsTable.getWeight(x, i-iLeft);
-
- index = i * wordspp;
- for (unsigned j = 0; j < wordspp; j++) {
- value[j] += (weight * (double)src_bits[index++]);
- }
- }
-
- // clamp and place result in destination pixel
- for (unsigned j = 0; j < wordspp; j++) {
- dst_bits[j] = (WORD)MIN(MAX((int)0, (int)(value[j] + 0.5)), (int)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)
- unsigned floatspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(float);
-
- for(unsigned y = 0; y < dst_height; y++) {
- // scale each row
- float *src_bits = (float*)FreeImage_GetScanLine(src, y);
- float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
-
- for(unsigned x = 0; x < dst_width; x++) {
- // loop through row
- double value[4] = {0, 0, 0, 0}; // 4 = 64bpp max
- int iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- int iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
-
- for(int i = iLeft; i <= iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- double weight = weightsTable.getWeight(x, i-iLeft);
-
- index = i * floatspp;
- 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 *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height) {
- if(src_height == dst_height) {
- // no scaling required, just copy
- switch(FreeImage_GetBPP(src)) {
- case 1:
- {
- if(FreeImage_GetBPP(dst) != 8) break;
- for(unsigned y = 0; y < dst_height; y++) {
- // convert each row
- BYTE *src_bits = FreeImage_GetScanLine(src, y);
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
- FreeImage_ConvertLine1To8(dst_bits, src_bits, dst_width);
- }
- }
- break;
-
- default:
- {
- BYTE *src_bits = FreeImage_GetBits(src);
- BYTE *dst_bits = FreeImage_GetBits(dst);
- memcpy(dst_bits, src_bits, dst_height * FreeImage_GetPitch(dst));
- }
- break;
- }
-
- }
- else {
- unsigned index; // pixel index
-
- // allocate and calculate the contributions
- CWeightsTable weightsTable(m_pFilter, dst_height, src_height);
-
-
- // step through columns
- switch(FreeImage_GetImageType(src)) {
- case FIT_BITMAP:
- {
- switch(FreeImage_GetBPP(src)) {
- case 1:
- {
- // scale and convert to 8-bit
- if(FreeImage_GetBPP(dst) != 8) break;
-
- unsigned src_pitch = FreeImage_GetPitch(src);
- unsigned dst_pitch = FreeImage_GetPitch(dst);
-
- for(unsigned x = 0; x < dst_width; x++) {
-
- // work on column x in dst
- BYTE *dst_bits = FreeImage_GetBits(dst) + x;
-
- // scale each column
- for(unsigned y = 0; y < dst_height; y++) {
- // loop through column
- double value = 0;
- int iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- int iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
-
- BYTE *src_bits = FreeImage_GetScanLine(src, iLeft);
-
- for(int i = iLeft; i <= iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- double weight = weightsTable.getWeight(y, i-iLeft);
-
- BYTE pixel = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
- value += (weight * (double)pixel);
-
- src_bits += src_pitch;
- }
- value *= 255;
-
- // clamp and place result in destination pixel
- *dst_bits = (BYTE)MIN(MAX((int)0, (int)(value + 0.5)), (int)255);
-
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case 8:
- case 24:
- case 32:
- {
- // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
- unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
-
- unsigned src_pitch = FreeImage_GetPitch(src);
- unsigned dst_pitch = FreeImage_GetPitch(dst);
-
- for(unsigned x = 0; x < dst_width; x++) {
- index = x * bytespp;
-
- // work on column x in dst
- BYTE *dst_bits = FreeImage_GetBits(dst) + index;
-
- // scale each column
- for(unsigned y = 0; y < dst_height; y++) {
- // loop through column
- double value[4] = {0, 0, 0, 0}; // 4 = 32bpp max
- int iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- int iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
-
- BYTE *src_bits = FreeImage_GetScanLine(src, iLeft) + index;
-
- for(int i = iLeft; i <= iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- double weight = weightsTable.getWeight(y, i-iLeft);
- for (unsigned j = 0; j < bytespp; j++) {
- value[j] += (weight * (double)src_bits[j]);
- }
-
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- for (unsigned j = 0; j < bytespp; j++) {
- dst_bits[j] = (BYTE)MIN(MAX((int)0, (int)(value[j] + 0.5)), (int)255);
- }
-
- dst_bits += dst_pitch;
- }
- }
- }
- break;
- }
- }
- break;
-
- case FIT_UINT16:
- case FIT_RGB16:
- case FIT_RGBA16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- unsigned wordspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(WORD);
-
- unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
- unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
-
- for(unsigned x = 0; x < dst_width; x++) {
- index = x * wordspp;
-
- // work on column x in dst
- WORD *dst_bits = (WORD*)FreeImage_GetBits(dst) + index;
-
- // scale each column
- for(unsigned y = 0; y < dst_height; y++) {
- // loop through column
- double value[4] = {0, 0, 0, 0}; // 4 = 64bpp max
- int iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- int iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
-
- WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, iLeft) + index;
-
- for(int i = iLeft; i <= iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- double weight = weightsTable.getWeight(y, i-iLeft);
- for (unsigned j = 0; j < wordspp; j++) {
- value[j] += (weight * (double)src_bits[j]);
- }
-
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- for (unsigned j = 0; j < wordspp; j++) {
- dst_bits[j] = (WORD)MIN(MAX((int)0, (int)(value[j] + 0.5)), (int)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)
- unsigned floatspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(float);
-
- unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float);
- unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float);
-
- for(unsigned x = 0; x < dst_width; x++) {
- index = x * floatspp;
-
- // work on column x in dst
- float *dst_bits = (float*)FreeImage_GetBits(dst) + index;
-
- // scale each column
- for(unsigned y = 0; y < dst_height; y++) {
- // loop through column
- double value[4] = {0, 0, 0, 0}; // 4 = 64bpp max
- int iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- int iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
-
- float *src_bits = (float*)FreeImage_GetScanLine(src, iLeft) + index;
-
- for(int i = iLeft; i <= iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- 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;
- }
-
- // clamp and place result in destination pixel
- for (unsigned j = 0; j < floatspp; j++) {
- dst_bits[j] = (float)value[j];
- }
-
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- }
- }
-}
-
+// ========================================================== +// Upsampling / downsampling classes +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// - Detlev Vendt (detlev.vendt@brillit.de) +// +// 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 "Resize.h" + +/** + Filter weights table. + This class stores contribution information for an entire line (row or column). +*/ +CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) { + unsigned u; + double dWidth; + double dFScale = 1.0; + 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; + } + + // allocate a new line contributions structure + // + // window size is the number of sampled pixels + m_WindowSize = 2 * (int)ceil(dWidth) + 1; + m_LineLength = uDstSize; + // allocate list of contributions + m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution)); + for(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) - 0.5; + + + for(u = 0; u < m_LineLength; u++) { + // scan through line of contributions + const double dCenter = (double)u / dScale + dOffset; // reverse mapping + // find the significant edge points that affect the pixel + int iLeft = MAX (0, (int)floor (dCenter - dWidth)); + int iRight = MIN ((int)ceil (dCenter + dWidth), int(uSrcSize) - 1); + + // cut edge points to fit in filter window in case of spill-off + if((iRight - iLeft + 1) > int(m_WindowSize)) { + if(iLeft < (int(uSrcSize) - 1 / 2)) { + iLeft++; + } else { + iRight--; + } + } + + m_WeightTable[u].Left = iLeft; + m_WeightTable[u].Right = iRight; + + int iSrc = 0; + double dTotalWeight = 0; // zero sum of weights + for(iSrc = iLeft; iSrc <= iRight; iSrc++) { + // calculate weights + const double weight = dFScale * pFilter->Filter(dFScale * (dCenter - (double)iSrc)); + m_WeightTable[u].Weights[iSrc-iLeft] = weight; + dTotalWeight += weight; + } + if((dTotalWeight > 0) && (dTotalWeight != 1)) { + // normalize weight of neighbouring points + for(iSrc = iLeft; iSrc <= iRight; iSrc++) { + // normalize point + m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight; + } + // simplify the filter, discarding null weights at the right + iSrc = iRight - iLeft; + while(m_WeightTable[u].Weights[iSrc] == 0){ + m_WeightTable[u].Right--; + iSrc--; + if(m_WeightTable[u].Right == m_WeightTable[u].Left) + break; + } + + } + } +} + +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); +} + +// --------------------------------------------- + +/** + 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 8-, 24- and 32-bit buffers.<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 + +*/ + +FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height) { + unsigned src_width = FreeImage_GetWidth(src); + unsigned src_height = FreeImage_GetHeight(src); + + unsigned redMask = FreeImage_GetRedMask(src); + unsigned greenMask = FreeImage_GetGreenMask(src); + unsigned blueMask = FreeImage_GetBlueMask(src); + + unsigned bpp = FreeImage_GetBPP(src); + if(bpp == 1) { + // convert output to 8-bit + bpp = 8; + } + + FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src); + + // allocate the dst image + FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp, redMask, greenMask, blueMask); + if(!dst) return NULL; + + if(bpp == 8) { + if(FreeImage_GetColorType(src) == FIC_MINISWHITE) { + // build an inverted greyscale palette + RGBQUAD *dst_pal = FreeImage_GetPalette(dst); + for(unsigned i = 0; i < 256; i++) { + dst_pal[i].rgbRed = dst_pal[i].rgbGreen = dst_pal[i].rgbBlue = (BYTE)(255 - i); + } + } else { + // build a greyscale palette + RGBQUAD *dst_pal = FreeImage_GetPalette(dst); + for(unsigned i = 0; i < 256; i++) { + dst_pal[i].rgbRed = dst_pal[i].rgbGreen = dst_pal[i].rgbBlue = (BYTE)i; + } + } + } + + /** + 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 + // ------------- + + // allocate a temporary image + FIBITMAP *tmp = FreeImage_AllocateT(image_type, dst_width, src_height, bpp, redMask, greenMask, blueMask); + if(!tmp) { + FreeImage_Unload(dst); + return NULL; + } + + // scale source image horizontally into temporary image + horizontalFilter(src, src_width, src_height, tmp, dst_width, src_height); + + // scale temporary image vertically into result image + verticalFilter(tmp, dst_width, src_height, dst, dst_width, dst_height); + + // free temporary image + FreeImage_Unload(tmp); + + } else { + // yx filtering + // ------------- + + // allocate a temporary image + FIBITMAP *tmp = FreeImage_AllocateT(image_type, src_width, dst_height, bpp, redMask, greenMask, blueMask); + if(!tmp) { + FreeImage_Unload(dst); + return NULL; + } + + // scale source image vertically into temporary image + verticalFilter(src, src_width, src_height, tmp, src_width, dst_height); + + // scale temporary image horizontally into result image + horizontalFilter(tmp, src_width, dst_height, dst, dst_width, dst_height); + + // free temporary image + FreeImage_Unload(tmp); + } + + return dst; +} + + +/// Performs horizontal image filtering +void CResizeEngine::horizontalFilter(FIBITMAP *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height) { + if(dst_width == src_width) { + // no scaling required, just copy + switch(FreeImage_GetBPP(src)) { + case 1: + { + if(FreeImage_GetBPP(dst) != 8) break; + for(unsigned y = 0; y < dst_height; y++) { + // convert each row + BYTE *src_bits = FreeImage_GetScanLine(src, y); + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + FreeImage_ConvertLine1To8(dst_bits, src_bits, dst_width); + } + } + break; + + default: + { + const BYTE *src_bits = FreeImage_GetBits(src); + BYTE *dst_bits = FreeImage_GetBits(dst); + memcpy(dst_bits, src_bits, dst_height * FreeImage_GetPitch(dst)); + } + break; + } + } + else { + + // 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: + { + // scale and convert to 8-bit + if(FreeImage_GetBPP(dst) != 8) break; + + for(unsigned y = 0; y < dst_height; y++) { + // scale each row + const BYTE *src_bits = FreeImage_GetScanLine(src, y); + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for(unsigned x = 0; x < dst_width; x++) { + // loop through row + double value = 0; + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + + 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 BYTE pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0; + value += (weight * (double)pixel); + } + value *= 255; + + // clamp and place result in destination pixel + dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 255); + } + } + } + break; + + case 8: + case 24: + case 32: + { + // 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(unsigned y = 0; y < dst_height; y++) { + // scale each row + const BYTE *src_bits = FreeImage_GetScanLine(src, y); + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for(unsigned x = 0; x < dst_width; x++) { + // loop through row + double value[4] = {0, 0, 0, 0}; // 4 = 32 bpp max + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + + 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 * bytespp; // pixel index + for (unsigned j = 0; j < bytespp; j++) { + value[j] += (weight * (double)src_bits[index++]); + } + } + + // clamp and place result in destination pixel + for (unsigned j = 0; j < bytespp; j++) { + dst_bits[j] = (BYTE)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFF); + } + + dst_bits += bytespp; + } + } + } + break; + } + } + break; + + case FIT_UINT16: + case FIT_RGB16: + 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) / FreeImage_GetWidth(src)) / sizeof(WORD); + + for(unsigned y = 0; y < dst_height; y++) { + // scale each row + const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y); + WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); + + for(unsigned x = 0; x < dst_width; x++) { + // loop through row + double value[4] = {0, 0, 0, 0}; // 4 = 64 bpp max + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + + 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 * wordspp; // pixel index + for (unsigned j = 0; j < wordspp; j++) { + value[j] += (weight * (double)src_bits[index++]); + } + } + + // clamp and place result in destination pixel + for (unsigned j = 0; j < wordspp; j++) { + dst_bits[j] = (WORD)CLAMP<int>((int)(value[j] + 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) / FreeImage_GetWidth(src)) / sizeof(float); + + for(unsigned y = 0; y < dst_height; y++) { + // scale each row + const float *src_bits = (float*)FreeImage_GetScanLine(src, y); + float *dst_bits = (float*)FreeImage_GetScanLine(dst, y); + + for(unsigned x = 0; x < dst_width; x++) { + // loop through row + double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + + 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 *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height) { + if(src_height == dst_height) { + // no scaling required, just copy + switch(FreeImage_GetBPP(src)) { + case 1: + { + if(FreeImage_GetBPP(dst) != 8) break; + for(unsigned y = 0; y < dst_height; y++) { + // convert each row + BYTE *src_bits = FreeImage_GetScanLine(src, y); + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + FreeImage_ConvertLine1To8(dst_bits, src_bits, dst_width); + } + } + break; + + default: + { + const BYTE *src_bits = FreeImage_GetBits(src); + BYTE *dst_bits = FreeImage_GetBits(dst); + memcpy(dst_bits, src_bits, dst_height * FreeImage_GetPitch(dst)); + } + break; + } + + } + else { + + // allocate and calculate the contributions + CWeightsTable weightsTable(m_pFilter, dst_height, src_height); + + // step through columns + switch(FreeImage_GetImageType(src)) { + case FIT_BITMAP: + { + switch(FreeImage_GetBPP(src)) { + case 1: + { + // scale and convert to 8-bit + if(FreeImage_GetBPP(dst) != 8) break; + + const unsigned src_pitch = FreeImage_GetPitch(src); + const unsigned dst_pitch = FreeImage_GetPitch(dst); + + for(unsigned x = 0; x < dst_width; x++) { + + // work on column x in dst + BYTE *dst_bits = FreeImage_GetBits(dst) + x; + + // scale each column + for(unsigned y = 0; y < dst_height; y++) { + // loop through column + double value = 0; + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary + + BYTE *src_bits = FreeImage_GetScanLine(src, iLeft); + + 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); + + const BYTE pixel = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0; + value += (weight * (double)pixel); + + src_bits += src_pitch; + } + value *= 255; + + // clamp and place result in destination pixel + *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + + dst_bits += dst_pitch; + } + } + } + break; + + case 8: + case 24: + case 32: + { + // 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); + + const unsigned src_pitch = FreeImage_GetPitch(src); + const unsigned dst_pitch = FreeImage_GetPitch(dst); + + for(unsigned x = 0; x < dst_width; x++) { + const unsigned index = x * bytespp; // pixel index + + // work on column x in dst + BYTE *dst_bits = FreeImage_GetBits(dst) + index; + + // scale each column + for(unsigned y = 0; y < dst_height; y++) { + // loop through column + double value[4] = {0, 0, 0, 0}; // 4 = 32 bpp max + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary + + const BYTE *src_bits = FreeImage_GetScanLine(src, iLeft) + index; + + 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 < bytespp; j++) { + value[j] += (weight * (double)src_bits[j]); + } + + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + for (unsigned j = 0; j < bytespp; j++) { + dst_bits[j] = (BYTE)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFF); + } + + dst_bits += dst_pitch; + } + } + } + break; + } + } + break; + + case FIT_UINT16: + case FIT_RGB16: + 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) / FreeImage_GetWidth(src)) / sizeof(WORD); + + const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD); + const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD); + + for(unsigned x = 0; x < dst_width; x++) { + const unsigned index = x * wordspp; // pixel index + + // work on column x in dst + WORD *dst_bits = (WORD*)FreeImage_GetBits(dst) + index; + + // scale each column + for(unsigned y = 0; y < dst_height; y++) { + // loop through column + double value[4] = {0, 0, 0, 0}; // 4 = 64 bpp max + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary + + const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, iLeft) + index; + + 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 < wordspp; j++) { + value[j] += (weight * (double)src_bits[j]); + } + + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + for (unsigned j = 0; j < wordspp; j++) { + dst_bits[j] = (WORD)CLAMP<int>((int)(value[j] + 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) / FreeImage_GetWidth(src)) / sizeof(float); + + const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float); + const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float); + + for(unsigned x = 0; x < dst_width; x++) { + const unsigned index = x * floatspp; // pixel index + + // work on column x in dst + float *dst_bits = (float*)FreeImage_GetBits(dst) + index; + + // scale each column + for(unsigned y = 0; y < dst_height; y++) { + // loop through column + double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary + + const float *src_bits = (float*)FreeImage_GetScanLine(src, iLeft) + index; + + 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; + } + + // clamp and 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/plugins/FreeImage/Source/FreeImageToolkit/Resize.h b/plugins/FreeImage/Source/FreeImageToolkit/Resize.h index 8ecaca4caa..0d5d0b451a 100644 --- a/plugins/FreeImage/Source/FreeImageToolkit/Resize.h +++ b/plugins/FreeImage/Source/FreeImageToolkit/Resize.h @@ -1,145 +1,145 @@ -// ==========================================================
-// Upsampling / downsampling classes
-//
-// Design and implementation by
-// - Hervé Drolon (drolon@infonie.fr)
-// - Detlev Vendt (detlev.vendt@brillit.de)
-//
-// 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
- int Left, Right;
-} Contribution;
-
-private:
- /// Row (or column) of contribution weights
- Contribution *m_WeightTable;
- /// Filter window size (of affecting source pixels)
- DWORD m_WindowSize;
- /// Length of line (no. of rows / cols)
- DWORD m_LineLength;
-
-public:
- /**
- Constructor<br>
- Allocate and compute the weights table
- @param pFilter Filter used for upsampling or downsampling
- @param uLineSize Length (in pixels) of the destination line buffer
- @param uSrcSize Length (in pixels) of the source line buffer
- */
- CWeightsTable(CGenericFilter *pFilter, DWORD uDstSize, DWORD 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(int dst_pos, int 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
- */
- int getLeftBoundary(int 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
- */
- int getRightBoundary(int 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 8-, 24- and 32-bit buffers.<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
- CResizeEngine(CGenericFilter* filter):m_pFilter(filter) {}
-
- /// Destructor
- virtual ~CResizeEngine() {}
-
- /** Scale an image to the desired dimensions
- @param src Pointer to the source image
- @param dst_width Destination image width
- @param dst_height Destination image height
- @return Returns the scaled image if successful, returns NULL otherwise
- */
- FIBITMAP* scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height);
-
-private:
-
- /// Performs horizontal image filtering
- void horizontalFilter(FIBITMAP *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height);
-
- /// Performs vertical image filtering
- void verticalFilter(FIBITMAP *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height);
-};
-
-
-
-#endif // _RESIZE_H_
+// ========================================================== +// Upsampling / downsampling classes +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// - Detlev Vendt (detlev.vendt@brillit.de) +// +// 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 8-, 24- and 32-bit buffers.<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 + CResizeEngine(CGenericFilter* filter):m_pFilter(filter) {} + + /// Destructor + virtual ~CResizeEngine() {} + + /** Scale an image to the desired dimensions + @param src Pointer to the source image + @param dst_width Destination image width + @param dst_height Destination image height + @return Returns the scaled image if successful, returns NULL otherwise + */ + FIBITMAP* scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height); + +private: + + /// Performs horizontal image filtering + void horizontalFilter(FIBITMAP *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height); + + /// Performs vertical image filtering + void verticalFilter(FIBITMAP *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height); +}; + + + +#endif // _RESIZE_H_ |