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/ClassicRotate.cpp | |
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/ClassicRotate.cpp')
-rw-r--r-- | plugins/FreeImage/Source/FreeImageToolkit/ClassicRotate.cpp | 1873 |
1 files changed, 917 insertions, 956 deletions
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; +} + |