#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)
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);
static
void
ConvertToInterpolationCoefficients(
double
*c,
long
DataLength,
double
*z,
long
NbPoles,
double
Tolerance) {
double
Lambda = 1;
long
n, k;
if
(DataLength == 1L) {
return
;
}
for
(k = 0L; k < NbPoles; k++) {
Lambda = Lambda * (1.0 - z[k]) * (1.0 - 1.0 / z[k]);
}
for
(n = 0L; n < DataLength; n++) {
c[n] *= Lambda;
}
for
(k = 0L; k < NbPoles; k++) {
c[0] = InitialCausalCoefficient(c, DataLength, z[k], Tolerance);
for
(n = 1L; n < DataLength; n++) {
c[n] += z[k] * c[n - 1L];
}
c[DataLength - 1L] = InitialAntiCausalCoefficient(c, DataLength, z[k]);
for
(n = DataLength - 2L; 0 <= n; n--) {
c[n] = z[k] * (c[n + 1L] - c[n]);
}
}
}
static
double
InitialCausalCoefficient(
double
*c,
long
DataLength,
double
z,
double
Tolerance) {
double
Sum, zn, z2n, iz;
long
n, Horizon;
Horizon = DataLength;
if
(Tolerance > 0) {
Horizon = (
long
)
ceil
(
log
(Tolerance) /
log
(
fabs
(z)));
}
if
(Horizon < DataLength) {
zn = z;
Sum = c[0];
for
(n = 1L; n < Horizon; n++) {
Sum += zn * c[n];
zn *= z;
}
return
(Sum);
}
else
{
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));
}
}
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;
}
}
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++;
}
}
static
double
InitialAntiCausalCoefficient(
double
*c,
long
DataLength,
double
z) {
return
((z / (z * z - 1.0)) * (z * c[DataLength - 2L] + c[DataLength - 1L]));
}
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;
}
}
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];
}
}
static
bool
SamplesToCoefficients(
double
*Image,
long
Width,
long
Height,
long
spline_degree) {
double
*Line;
double
Pole[2];
long
NbPoles;
long
x, y;
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
:
return
false
;
}
Line = (
double
*)
malloc
(Width *
sizeof
(
double
));
if
(Line == NULL) {
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);
Line = (
double
*)
malloc
(Height *
sizeof
(
double
));
if
(Line == NULL) {
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
;
}
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;
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++;
}
}
switch
(spline_degree) {
case
2L:
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];
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:
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];
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:
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];
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:
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;
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
:
return
0;
}
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];
}
}
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;
}
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;
break
;
case
ROTATE_CUBIC:
spline = 3L;
break
;
case
ROTATE_QUARTIC:
spline = 4L;
break
;
case
ROTATE_QUINTIC:
spline = 5L;
break
;
default
:
spline = 3L;
}
FIBITMAP *dst = FreeImage_Allocate(width, height, bpp);
if
(!dst)
return
NULL;
RGBQUAD *pal = FreeImage_GetPalette(dst);
for
(
int
i = 0; i < 256; i++) {
pal[i].rgbRed = pal[i].rgbGreen = pal[i].rgbBlue = (
BYTE
)i;
}
ImageRasterArray = (
double
*)
malloc
(width * height *
sizeof
(
double
));
if
(!ImageRasterArray) {
FreeImage_Unload(dst);
return
NULL;
}
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];
}
}
bResult = SamplesToCoefficients(ImageRasterArray, width, height, spline);
if
(!bResult) {
FreeImage_Unload(dst);
free
(ImageRasterArray);
return
NULL;
}
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;
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);
}
dst_bits[x] = (
BYTE
)MIN(MAX((
int
)0, (
int
)(p + 0.5)), (
int
)255);
}
}
free
(ImageRasterArray);
return
dst;
}
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) {
FreeImage_CloneMetadata(dst_8, dib);
}
return
dst_8;
}
if
((bpp == 24) || (bpp == 32)) {
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);
src8 = FreeImage_Allocate(width, height, 8);
if
(!src8)
throw
(1);
nb_channels = (bpp / 8);
for
(channel = 0; channel < nb_channels; channel++) {
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;
}
}
dst8 = Rotate8Bit(src8, angle, x_shift, y_shift, x_origin, y_origin, ROTATE_CUBIC, use_mask);
if
(!dst8)
throw
(1);
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);
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;
}