diff options
| author | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-10-12 10:49:29 +0000 | 
|---|---|---|
| committer | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-10-12 10:49:29 +0000 | 
| commit | 557f2816f5cd30705dec989c97a8a67c026d36d1 (patch) | |
| tree | 4900789ab5d52219bf065744a61f9ea5863b3428 /plugins/FreeImage/src/FreeImageToolkit/ClassicRotate.cpp | |
| parent | 69fe21d6e2262d6fb2b14278b7a20364468cbdcf (diff) | |
FreeImage: folders restructurization
git-svn-id: http://svn.miranda-ng.org/main/trunk@1884 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/FreeImage/src/FreeImageToolkit/ClassicRotate.cpp')
| -rw-r--r-- | plugins/FreeImage/src/FreeImageToolkit/ClassicRotate.cpp | 917 | 
1 files changed, 917 insertions, 0 deletions
diff --git a/plugins/FreeImage/src/FreeImageToolkit/ClassicRotate.cpp b/plugins/FreeImage/src/FreeImageToolkit/ClassicRotate.cpp new file mode 100644 index 0000000000..d38d8c1bcd --- /dev/null +++ b/plugins/FreeImage/src/FreeImageToolkit/ClassicRotate.cpp @@ -0,0 +1,917 @@ +// ========================================================== +// 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; +} +  | 
