PageRenderTime 106ms CodeModel.GetById 10ms app.highlight 89ms RepoModel.GetById 1ms app.codeStats 0ms

/src/FreeImage/Source/FreeImageToolkit/ClassicRotate.cpp

https://bitbucket.org/cabalistic/ogredeps/
C++ | 917 lines | 548 code | 126 blank | 243 comment | 142 complexity | f9083b42b2a1167a794c0ad964bb49a5 MD5 | raw file
  1// ==========================================================
  2// Bitmap rotation by means of 3 shears.
  3//
  4// Design and implementation by
  5// - Hervé Drolon (drolon@infonie.fr)
  6// - Thorsten Radde (support@IdealSoftware.com)
  7// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
  8//
  9// This file is part of FreeImage 3
 10//
 11// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
 12// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
 13// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
 14// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
 15// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
 16// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
 17// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
 18// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
 19// THIS DISCLAIMER.
 20//
 21// Use at your own risk!
 22// ==========================================================
 23
 24/* 
 25 ============================================================
 26 References : 
 27 [1] Paeth A., A Fast Algorithm for General Raster Rotation. 
 28 Graphics Gems, p. 179, Andrew Glassner editor, Academic Press, 1990. 
 29 [2] Yariv E., High quality image rotation (rotate by shear). 
 30 [Online] http://www.codeproject.com/bitmap/rotatebyshear.asp
 31 [3] Treskunov A., Fast and high quality true-color bitmap rotation function.
 32 [Online] http://anton.treskunov.net/Software/doc/fast_and_high_quality_true_color_bitmap_rotation_function.html
 33 ============================================================
 34*/
 35
 36#include "FreeImage.h"
 37#include "Utilities.h"
 38
 39#define RBLOCK		64	// image blocks of RBLOCK*RBLOCK pixels
 40
 41// --------------------------------------------------------------------------
 42
 43/**
 44Skews a row horizontally (with filtered weights). 
 45Limited to 45 degree skewing only. Filters two adjacent pixels.
 46Parameter T can be BYTE, WORD of float. 
 47@param src Pointer to source image to rotate
 48@param dst Pointer to destination image
 49@param row Row index
 50@param iOffset Skew offset
 51@param dWeight Relative weight of right pixel
 52@param bkcolor Background color
 53*/
 54template <class T> void 
 55HorizontalSkewT(FIBITMAP *src, FIBITMAP *dst, int row, int iOffset, double weight, const void *bkcolor = NULL) {
 56	int iXPos;
 57
 58	const unsigned src_width  = FreeImage_GetWidth(src);
 59	const unsigned dst_width  = FreeImage_GetWidth(dst);
 60
 61	T pxlSrc[4], pxlLeft[4], pxlOldLeft[4];	// 4 = 4*sizeof(T) max
 62	
 63	// background
 64	const T pxlBlack[4] = {0, 0, 0, 0 };
 65	const T *pxlBkg = static_cast<const T*>(bkcolor); // assume at least bytespp and 4*sizeof(T) max
 66	if(!pxlBkg) {
 67		// default background color is black
 68		pxlBkg = pxlBlack;
 69	}
 70
 71	// calculate the number of bytes per pixel
 72	const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
 73	// calculate the number of samples per pixel
 74	const unsigned samples = bytespp / sizeof(T);
 75
 76	BYTE *src_bits = FreeImage_GetScanLine(src, row);
 77	BYTE *dst_bits = FreeImage_GetScanLine(dst, row);
 78
 79	// fill gap left of skew with background
 80	if(bkcolor) {
 81		for(int k = 0; k < iOffset; k++) {
 82			memcpy(&dst_bits[k * bytespp], bkcolor, bytespp);
 83		}
 84		AssignPixel((BYTE*)&pxlOldLeft[0], (BYTE*)bkcolor, bytespp);
 85	} else {
 86		if(iOffset > 0) {
 87			memset(dst_bits, 0, iOffset * bytespp);
 88		}		
 89		memset(&pxlOldLeft[0], 0, bytespp);
 90	}
 91
 92	for(unsigned i = 0; i < src_width; i++) {
 93		// loop through row pixels
 94		AssignPixel((BYTE*)&pxlSrc[0], (BYTE*)src_bits, bytespp);
 95		// calculate weights
 96		for(unsigned j = 0; j < samples; j++) {
 97			pxlLeft[j] = static_cast<T>(pxlBkg[j] + (pxlSrc[j] - pxlBkg[j]) * weight + 0.5);
 98		}
 99		// check boundaries 
100		iXPos = i + iOffset;
101		if((iXPos >= 0) && (iXPos < (int)dst_width)) {
102			// update left over on source
103			for(unsigned j = 0; j < samples; j++) {
104				pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);
105			}
106			AssignPixel((BYTE*)&dst_bits[iXPos*bytespp], (BYTE*)&pxlSrc[0], bytespp);
107		}
108		// save leftover for next pixel in scan
109		AssignPixel((BYTE*)&pxlOldLeft[0], (BYTE*)&pxlLeft[0], bytespp);
110
111		// next pixel in scan
112		src_bits += bytespp;
113	}			
114
115	// go to rightmost point of skew
116	iXPos = src_width + iOffset; 
117
118	if((iXPos >= 0) && (iXPos < (int)dst_width)) {
119		dst_bits = FreeImage_GetScanLine(dst, row) + iXPos * bytespp;
120
121		// If still in image bounds, put leftovers there
122		AssignPixel((BYTE*)dst_bits, (BYTE*)&pxlOldLeft[0], bytespp);
123
124		// clear to the right of the skewed line with background
125		dst_bits += bytespp;
126		if(bkcolor) {
127			for(unsigned i = 0; i < dst_width - iXPos - 1; i++) {
128				memcpy(&dst_bits[i * bytespp], bkcolor, bytespp);
129			}
130		} else {
131			memset(dst_bits, 0, bytespp * (dst_width - iXPos - 1));
132		}
133
134	}
135}
136
137/**
138Skews a row horizontally (with filtered weights). 
139Limited to 45 degree skewing only. Filters two adjacent pixels.
140@param src Pointer to source image to rotate
141@param dst Pointer to destination image
142@param row Row index
143@param iOffset Skew offset
144@param dWeight Relative weight of right pixel
145@param bkcolor Background color
146*/
147static void 
148HorizontalSkew(FIBITMAP *src, FIBITMAP *dst, int row, int iOffset, double dWeight, const void *bkcolor) {
149	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
150
151	switch(image_type) {
152		case FIT_BITMAP:
153			switch(FreeImage_GetBPP(src)) {
154				case 8:
155				case 24:
156				case 32:
157					HorizontalSkewT<BYTE>(src, dst, row, iOffset, dWeight, bkcolor);
158				break;
159			}
160			break;
161		case FIT_UINT16:
162		case FIT_RGB16:
163		case FIT_RGBA16:
164			HorizontalSkewT<WORD>(src, dst, row, iOffset, dWeight, bkcolor);
165			break;
166		case FIT_FLOAT:
167		case FIT_RGBF:
168		case FIT_RGBAF:
169			HorizontalSkewT<float>(src, dst, row, iOffset, dWeight, bkcolor);
170			break;
171	}
172}
173
174/**
175Skews a column vertically (with filtered weights). 
176Limited to 45 degree skewing only. Filters two adjacent pixels.
177Parameter T can be BYTE, WORD of float. 
178@param src Pointer to source image to rotate
179@param dst Pointer to destination image
180@param col Column index
181@param iOffset Skew offset
182@param dWeight Relative weight of upper pixel
183@param bkcolor Background color
184*/
185template <class T> void 
186VerticalSkewT(FIBITMAP *src, FIBITMAP *dst, int col, int iOffset, double weight, const void *bkcolor = NULL) {
187	int iYPos;
188
189	unsigned src_height = FreeImage_GetHeight(src);
190	unsigned dst_height = FreeImage_GetHeight(dst);
191
192	T pxlSrc[4], pxlLeft[4], pxlOldLeft[4];	// 4 = 4*sizeof(T) max
193
194	// background
195	const T pxlBlack[4] = {0, 0, 0, 0 };
196	const T *pxlBkg = static_cast<const T*>(bkcolor); // assume at least bytespp and 4*sizeof(T) max
197	if(!pxlBkg) {
198		// default background color is black
199		pxlBkg = pxlBlack;
200	}
201
202	// calculate the number of bytes per pixel
203	const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
204	// calculate the number of samples per pixel
205	const unsigned samples = bytespp / sizeof(T);
206
207	const unsigned src_pitch = FreeImage_GetPitch(src);
208	const unsigned dst_pitch = FreeImage_GetPitch(dst);
209	const unsigned index = col * bytespp;
210
211	BYTE *src_bits = FreeImage_GetBits(src) + index;
212	BYTE *dst_bits = FreeImage_GetBits(dst) + index;
213
214	// fill gap above skew with background
215	if(bkcolor) {
216		for(int k = 0; k < iOffset; k++) {
217			memcpy(dst_bits, bkcolor, bytespp);
218			dst_bits += dst_pitch;
219		}
220		memcpy(&pxlOldLeft[0], bkcolor, bytespp);
221	} else {
222		for(int k = 0; k < iOffset; k++) {
223			memset(dst_bits, 0, bytespp);
224			dst_bits += dst_pitch;
225		}
226		memset(&pxlOldLeft[0], 0, bytespp);
227	}
228
229	for(unsigned i = 0; i < src_height; i++) {
230		// loop through column pixels
231		AssignPixel((BYTE*)(&pxlSrc[0]), src_bits, bytespp);
232		// calculate weights
233		for(unsigned j = 0; j < samples; j++) {
234			pxlLeft[j] = static_cast<T>(pxlBkg[j] + (pxlSrc[j] - pxlBkg[j]) * weight + 0.5);
235		}
236		// check boundaries
237		iYPos = i + iOffset;
238		if((iYPos >= 0) && (iYPos < (int)dst_height)) {
239			// update left over on source
240			for(unsigned j = 0; j < samples; j++) {
241				pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);
242			}
243			dst_bits = FreeImage_GetScanLine(dst, iYPos) + index;
244			AssignPixel(dst_bits, (BYTE*)(&pxlSrc[0]), bytespp);
245		}
246		// save leftover for next pixel in scan
247		AssignPixel((BYTE*)(&pxlOldLeft[0]), (BYTE*)(&pxlLeft[0]), bytespp);
248
249		// next pixel in scan
250		src_bits += src_pitch;
251	}
252	// go to bottom point of skew
253	iYPos = src_height + iOffset;
254
255	if((iYPos >= 0) && (iYPos < (int)dst_height)) {
256		dst_bits = FreeImage_GetScanLine(dst, iYPos) + index;
257
258		// if still in image bounds, put leftovers there				
259		AssignPixel((BYTE*)(dst_bits), (BYTE*)(&pxlOldLeft[0]), bytespp);
260
261		// clear below skewed line with background
262		if(bkcolor) {
263			while(++iYPos < (int)dst_height) {					
264				dst_bits += dst_pitch;
265				AssignPixel((BYTE*)(dst_bits), (BYTE*)(bkcolor), bytespp);
266			}
267		} else {
268			while(++iYPos < (int)dst_height) {					
269				dst_bits += dst_pitch;
270				memset(dst_bits, 0, bytespp);
271			}
272		}
273	}
274}
275
276/**
277Skews a column vertically (with filtered weights). 
278Limited to 45 degree skewing only. Filters two adjacent pixels.
279@param src Pointer to source image to rotate
280@param dst Pointer to destination image
281@param col Column index
282@param iOffset Skew offset
283@param dWeight Relative weight of upper pixel
284@param bkcolor Background color
285*/
286static void 
287VerticalSkew(FIBITMAP *src, FIBITMAP *dst, int col, int iOffset, double dWeight, const void *bkcolor) {
288	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
289
290	switch(image_type) {
291		case FIT_BITMAP:
292			switch(FreeImage_GetBPP(src)) {
293				case 8:
294				case 24:
295				case 32:
296					VerticalSkewT<BYTE>(src, dst, col, iOffset, dWeight, bkcolor);
297					break;
298			}
299			break;
300			case FIT_UINT16:
301			case FIT_RGB16:
302			case FIT_RGBA16:
303				VerticalSkewT<WORD>(src, dst, col, iOffset, dWeight, bkcolor);
304				break;
305			case FIT_FLOAT:
306			case FIT_RGBF:
307			case FIT_RGBAF:
308				VerticalSkewT<float>(src, dst, col, iOffset, dWeight, bkcolor);
309				break;
310	}
311} 
312
313/**
314Rotates an image by 90 degrees (counter clockwise). 
315Precise rotation, no filters required.<br>
316Code adapted from CxImage (http://www.xdp.it/cximage.htm)
317@param src Pointer to source image to rotate
318@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
319*/
320static FIBITMAP* 
321Rotate90(FIBITMAP *src) {
322
323	const unsigned bpp = FreeImage_GetBPP(src);
324
325	const unsigned src_width  = FreeImage_GetWidth(src);
326	const unsigned src_height = FreeImage_GetHeight(src);	
327	const unsigned dst_width  = src_height;
328	const unsigned dst_height = src_width;
329
330	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
331
332	// allocate and clear dst image
333	FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);
334	if(NULL == dst) return NULL;
335
336	// get src and dst scan width
337	const unsigned src_pitch  = FreeImage_GetPitch(src);
338	const unsigned dst_pitch  = FreeImage_GetPitch(dst);
339
340	switch(image_type) {
341		case FIT_BITMAP:
342			if(bpp == 1) {
343				// speedy rotate for BW images
344
345				BYTE *bsrc  = FreeImage_GetBits(src); 
346				BYTE *bdest = FreeImage_GetBits(dst);
347
348				BYTE *dbitsmax = bdest + dst_height * dst_pitch - 1;
349
350				for(unsigned y = 0; y < src_height; y++) {
351					// figure out the column we are going to be copying to
352					const div_t div_r = div(y, 8);
353					// set bit pos of src column byte
354					const BYTE bitpos = (BYTE)(128 >> div_r.rem);
355					BYTE *srcdisp = bsrc + y * src_pitch;
356					for(unsigned x = 0; x < src_pitch; x++) {
357						// get source bits
358						BYTE *sbits = srcdisp + x;
359						// get destination column
360						BYTE *nrow = bdest + (dst_height - 1 - (x * 8)) * dst_pitch + div_r.quot;
361						for (int z = 0; z < 8; z++) {
362						   // get destination byte
363							BYTE *dbits = nrow - z * dst_pitch;
364							if ((dbits < bdest) || (dbits > dbitsmax)) break;
365							if (*sbits & (128 >> z)) *dbits |= bitpos;
366						}
367					}
368				}
369			}
370			else if((bpp == 8) || (bpp == 24) || (bpp == 32)) {
371				// anything other than BW :
372				// This optimized version of rotation rotates image by smaller blocks. It is quite
373				// a bit faster than obvious algorithm, because it produces much less CPU cache misses.
374				// This optimization can be tuned by changing block size (RBLOCK). 96 is good value for current
375				// CPUs (tested on Athlon XP and Celeron D). Larger value (if CPU has enough cache) will increase
376				// speed somehow, but once you drop out of CPU's cache, things will slow down drastically.
377				// For older CPUs with less cache, lower value would yield better results.
378
379				BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels
380				BYTE *bdest = FreeImage_GetBits(dst);  // destination pixels
381
382				// calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
383				const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
384				
385				// for all image blocks of RBLOCK*RBLOCK pixels
386				
387				// x-segment
388				for(unsigned xs = 0; xs < dst_width; xs += RBLOCK) {
389					// y-segment
390					for(unsigned ys = 0; ys < dst_height; ys += RBLOCK) {
391						for(unsigned y = ys; y < MIN(dst_height, ys + RBLOCK); y++) {    // do rotation
392							const unsigned y2 = dst_height - y - 1;
393							// point to src pixel at (y2, xs)
394							BYTE *src_bits = bsrc + (xs * src_pitch) + (y2 * bytespp);
395							// point to dst pixel at (xs, y)
396							BYTE *dst_bits = bdest + (y * dst_pitch) + (xs * bytespp);
397							for(unsigned x = xs; x < MIN(dst_width, xs + RBLOCK); x++) {
398								// dst.SetPixel(x, y, src.GetPixel(y2, x));
399								AssignPixel(dst_bits, src_bits, bytespp);
400								dst_bits += bytespp;
401								src_bits += src_pitch;
402							}
403						}
404					}
405				}
406			}
407			break;
408		case FIT_UINT16:
409		case FIT_RGB16:
410		case FIT_RGBA16:
411		case FIT_FLOAT:
412		case FIT_RGBF:
413		case FIT_RGBAF:
414		{
415			BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels
416			BYTE *bdest = FreeImage_GetBits(dst);  // destination pixels
417
418			// calculate the number of bytes per pixel
419			const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
420
421			for(unsigned y = 0; y < dst_height; y++) {
422				BYTE *src_bits = bsrc + (src_width - 1 - y) * bytespp;
423				BYTE *dst_bits = bdest + (y * dst_pitch);
424				for(unsigned x = 0; x < dst_width; x++) {
425					AssignPixel(dst_bits, src_bits, bytespp);
426					src_bits += src_pitch;
427					dst_bits += bytespp;
428				}
429			}
430		}
431		break;
432	}
433
434	return dst;
435}
436
437/**
438Rotates an image by 180 degrees (counter clockwise). 
439Precise rotation, no filters required.
440@param src Pointer to source image to rotate
441@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
442*/
443static FIBITMAP* 
444Rotate180(FIBITMAP *src) {
445	int x, y, k, pos;
446
447	const int bpp = FreeImage_GetBPP(src);
448
449	const int src_width  = FreeImage_GetWidth(src);
450	const int src_height = FreeImage_GetHeight(src);
451	const int dst_width  = src_width;
452	const int dst_height = src_height;
453
454	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
455
456	FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);
457	if(NULL == dst) return NULL;
458
459	switch(image_type) {
460		case FIT_BITMAP:
461			if(bpp == 1) {
462				for(int y = 0; y < src_height; y++) {
463					BYTE *src_bits = FreeImage_GetScanLine(src, y);
464					BYTE *dst_bits = FreeImage_GetScanLine(dst, dst_height - y - 1);
465					for(int x = 0; x < src_width; x++) {
466						// get bit at (x, y)
467						k = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
468						// set bit at (dst_width - x - 1, dst_height - y - 1)
469						pos = dst_width - x - 1;
470						k ? dst_bits[pos >> 3] |= (0x80 >> (pos & 0x7)) : dst_bits[pos >> 3] &= (0xFF7F >> (pos & 0x7));
471					}			
472				}
473				break;
474			}
475			// else if((bpp == 8) || (bpp == 24) || (bpp == 32)) FALL TROUGH
476		case FIT_UINT16:
477		case FIT_RGB16:
478		case FIT_RGBA16:
479		case FIT_FLOAT:
480		case FIT_RGBF:
481		case FIT_RGBAF:
482		{
483			 // Calculate the number of bytes per pixel
484			const int bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
485
486			for(y = 0; y < src_height; y++) {
487				BYTE *src_bits = FreeImage_GetScanLine(src, y);
488				BYTE *dst_bits = FreeImage_GetScanLine(dst, dst_height - y - 1) + (dst_width - 1) * bytespp;
489				for(x = 0; x < src_width; x++) {
490					// get pixel at (x, y)
491					// set pixel at (dst_width - x - 1, dst_height - y - 1)
492					AssignPixel(dst_bits, src_bits, bytespp);
493					src_bits += bytespp;
494					dst_bits -= bytespp;					
495				}				
496			}
497		}
498		break;
499	}
500
501	return dst;
502}
503
504/**
505Rotates an image by 270 degrees (counter clockwise). 
506Precise rotation, no filters required.<br>
507Code adapted from CxImage (http://www.xdp.it/cximage.htm)
508@param src Pointer to source image to rotate
509@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
510*/
511static FIBITMAP* 
512Rotate270(FIBITMAP *src) {
513	int x2, dlineup;
514
515	const unsigned bpp = FreeImage_GetBPP(src);
516
517	const unsigned src_width  = FreeImage_GetWidth(src);
518	const unsigned src_height = FreeImage_GetHeight(src);	
519	const unsigned dst_width  = src_height;
520	const unsigned dst_height = src_width;
521
522	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
523
524	// allocate and clear dst image
525	FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);
526	if(NULL == dst) return NULL;
527
528	// get src and dst scan width
529	const unsigned src_pitch  = FreeImage_GetPitch(src);
530	const unsigned dst_pitch  = FreeImage_GetPitch(dst);
531	
532	switch(image_type) {
533		case FIT_BITMAP:
534			if(bpp == 1) {
535				// speedy rotate for BW images
536				
537				BYTE *bsrc  = FreeImage_GetBits(src); 
538				BYTE *bdest = FreeImage_GetBits(dst);
539				BYTE *dbitsmax = bdest + dst_height * dst_pitch - 1;
540				dlineup = 8 * dst_pitch - dst_width;
541
542				for(unsigned y = 0; y < src_height; y++) {
543					// figure out the column we are going to be copying to
544					const div_t div_r = div(y + dlineup, 8);
545					// set bit pos of src column byte
546					const BYTE bitpos = (BYTE)(1 << div_r.rem);
547					const BYTE *srcdisp = bsrc + y * src_pitch;
548					for(unsigned x = 0; x < src_pitch; x++) {
549						// get source bits
550						const BYTE *sbits = srcdisp + x;
551						// get destination column
552						BYTE *nrow = bdest + (x * 8) * dst_pitch + dst_pitch - 1 - div_r.quot;
553						for(unsigned z = 0; z < 8; z++) {
554						   // get destination byte
555							BYTE *dbits = nrow + z * dst_pitch;
556							if ((dbits < bdest) || (dbits > dbitsmax)) break;
557							if (*sbits & (128 >> z)) *dbits |= bitpos;
558						}
559					}
560				}
561			} 
562			else if((bpp == 8) || (bpp == 24) || (bpp == 32)) {
563				// anything other than BW :
564				// This optimized version of rotation rotates image by smaller blocks. It is quite
565				// a bit faster than obvious algorithm, because it produces much less CPU cache misses.
566				// This optimization can be tuned by changing block size (RBLOCK). 96 is good value for current
567				// CPUs (tested on Athlon XP and Celeron D). Larger value (if CPU has enough cache) will increase
568				// speed somehow, but once you drop out of CPU's cache, things will slow down drastically.
569				// For older CPUs with less cache, lower value would yield better results.
570
571				BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels
572				BYTE *bdest = FreeImage_GetBits(dst);  // destination pixels
573
574				// Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
575				const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
576
577				// for all image blocks of RBLOCK*RBLOCK pixels
578
579				// x-segment
580				for(unsigned xs = 0; xs < dst_width; xs += RBLOCK) {
581					// y-segment
582					for(unsigned ys = 0; ys < dst_height; ys += RBLOCK) {
583						for(unsigned x = xs; x < MIN(dst_width, xs + RBLOCK); x++) {    // do rotation
584							x2 = dst_width - x - 1;
585							// point to src pixel at (ys, x2)
586							BYTE *src_bits = bsrc + (x2 * src_pitch) + (ys * bytespp);
587							// point to dst pixel at (x, ys)
588							BYTE *dst_bits = bdest + (ys * dst_pitch) + (x * bytespp);
589							for(unsigned y = ys; y < MIN(dst_height, ys + RBLOCK); y++) {
590								// dst.SetPixel(x, y, src.GetPixel(y, x2));
591								AssignPixel(dst_bits, src_bits, bytespp);
592								src_bits += bytespp;
593								dst_bits += dst_pitch;
594							}
595						}
596					}
597				}
598			}
599			break;
600		case FIT_UINT16:
601		case FIT_RGB16:
602		case FIT_RGBA16:
603		case FIT_FLOAT:
604		case FIT_RGBF:
605		case FIT_RGBAF:
606		{
607			BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels
608			BYTE *bdest = FreeImage_GetBits(dst);  // destination pixels
609
610			// calculate the number of bytes per pixel
611			const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
612
613			for(unsigned y = 0; y < dst_height; y++) {
614				BYTE *src_bits = bsrc + (src_height - 1) * src_pitch + y * bytespp;
615				BYTE *dst_bits = bdest + (y * dst_pitch);
616				for(unsigned x = 0; x < dst_width; x++) {
617					AssignPixel(dst_bits, src_bits, bytespp);
618					src_bits -= src_pitch;
619					dst_bits += bytespp;
620				}
621			}
622		}
623		break;
624	}
625
626	return dst;
627}
628
629/**
630Rotates an image by a given degree in range [-45 .. +45] (counter clockwise) 
631using the 3-shear technique.
632@param src Pointer to source image to rotate
633@param dAngle Rotation angle
634@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
635*/
636static FIBITMAP* 
637Rotate45(FIBITMAP *src, double dAngle, const void *bkcolor) {
638	const double ROTATE_PI = double(3.1415926535897932384626433832795);
639
640	unsigned u;
641
642	const unsigned bpp = FreeImage_GetBPP(src);
643
644	const double dRadAngle = dAngle * ROTATE_PI / double(180); // Angle in radians
645	const double dSinE = sin(dRadAngle);
646	const double dTan = tan(dRadAngle / 2);
647
648	const unsigned src_width  = FreeImage_GetWidth(src);
649	const unsigned src_height = FreeImage_GetHeight(src);
650
651	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
652
653	// Calc first shear (horizontal) destination image dimensions 
654	const unsigned width_1  = src_width + unsigned((double)src_height * fabs(dTan) + 0.5);
655	const unsigned height_1 = src_height; 
656
657	// Perform 1st shear (horizontal)
658	// ----------------------------------------------------------------------
659
660	// Allocate image for 1st shear
661	FIBITMAP *dst1 = FreeImage_AllocateT(image_type, width_1, height_1, bpp);
662	if(NULL == dst1) {
663		return NULL;
664	}
665	
666	for(u = 0; u < height_1; u++) {  
667		double dShear;
668
669		if(dTan >= 0)	{
670			// Positive angle
671			dShear = (u + 0.5) * dTan;
672		}
673		else {
674			// Negative angle
675			dShear = (double(u) - height_1 + 0.5) * dTan;
676		}
677		int iShear = int(floor(dShear));
678		HorizontalSkew(src, dst1, u, iShear, dShear - double(iShear), bkcolor);
679	}
680
681	// Perform 2nd shear  (vertical)
682	// ----------------------------------------------------------------------
683
684	// Calc 2nd shear (vertical) destination image dimensions
685	const unsigned width_2  = width_1;
686	unsigned height_2 = unsigned((double)src_width * fabs(dSinE) + (double)src_height * cos(dRadAngle) + 0.5) + 1;
687
688	// Allocate image for 2nd shear
689	FIBITMAP *dst2 = FreeImage_AllocateT(image_type, width_2, height_2, bpp);
690	if(NULL == dst2) {
691		FreeImage_Unload(dst1);
692		return NULL;
693	}
694
695	double dOffset;     // Variable skew offset
696	if(dSinE > 0)	{   
697		// Positive angle
698		dOffset = (src_width - 1.0) * dSinE;
699	}
700	else {
701		// Negative angle
702		dOffset = -dSinE * (double(src_width) - width_2);
703	}
704
705	for(u = 0; u < width_2; u++, dOffset -= dSinE) {
706		int iShear = int(floor(dOffset));
707		VerticalSkew(dst1, dst2, u, iShear, dOffset - double(iShear), bkcolor);
708	}
709
710	// Perform 3rd shear (horizontal)
711	// ----------------------------------------------------------------------
712
713	// Free result of 1st shear
714	FreeImage_Unload(dst1);
715
716	// Calc 3rd shear (horizontal) destination image dimensions
717	const unsigned width_3  = unsigned(double(src_height) * fabs(dSinE) + double(src_width) * cos(dRadAngle) + 0.5) + 1;
718	const unsigned height_3 = height_2;
719
720	// Allocate image for 3rd shear
721	FIBITMAP *dst3 = FreeImage_AllocateT(image_type, width_3, height_3, bpp);
722	if(NULL == dst3) {
723		FreeImage_Unload(dst2);
724		return NULL;
725	}
726
727	if(dSinE >= 0) {
728		// Positive angle
729		dOffset = (src_width - 1.0) * dSinE * -dTan;
730	}
731	else {
732		// Negative angle
733		dOffset = dTan * ( (src_width - 1.0) * -dSinE + (1.0 - height_3) );
734	}
735	for(u = 0; u < height_3; u++, dOffset += dTan) {
736		int iShear = int(floor(dOffset));
737		HorizontalSkew(dst2, dst3, u, iShear, dOffset - double(iShear), bkcolor);
738	}
739	// Free result of 2nd shear    
740	FreeImage_Unload(dst2);
741
742	// Return result of 3rd shear
743	return dst3;      
744}
745
746/**
747Rotates a 1-, 8-, 24- or 32-bit image by a given angle (given in degree). 
748Angle is unlimited, except for 1-bit images (limited to integer multiples of 90 degree). 
7493-shears technique is used.
750@param src Pointer to source image to rotate
751@param dAngle Rotation angle
752@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
753*/
754static FIBITMAP* 
755RotateAny(FIBITMAP *src, double dAngle, const void *bkcolor) {
756	if(NULL == src) {
757		return NULL;
758	}
759
760	FIBITMAP *image = src;
761
762	while(dAngle >= 360) {
763		// Bring angle to range of (-INF .. 360)
764		dAngle -= 360;
765	}
766	while(dAngle < 0) {
767		// Bring angle to range of [0 .. 360) 
768		dAngle += 360;
769	}
770	if((dAngle > 45) && (dAngle <= 135)) {
771		// Angle in (45 .. 135] 
772		// Rotate image by 90 degrees into temporary image,
773		// so it requires only an extra rotation angle 
774		// of -45 .. +45 to complete rotation.
775		image = Rotate90(src);
776		dAngle -= 90;
777	}
778	else if((dAngle > 135) && (dAngle <= 225)) { 
779		// Angle in (135 .. 225] 
780		// Rotate image by 180 degrees into temporary image,
781		// so it requires only an extra rotation angle 
782		// of -45 .. +45 to complete rotation.
783		image = Rotate180(src);
784		dAngle -= 180;
785	}
786	else if((dAngle > 225) && (dAngle <= 315)) { 
787		// Angle in (225 .. 315] 
788		// Rotate image by 270 degrees into temporary image,
789		// so it requires only an extra rotation angle 
790		// of -45 .. +45 to complete rotation.
791		image = Rotate270(src);
792		dAngle -= 270;
793	}
794
795	// If we got here, angle is in (-45 .. +45]
796
797	if(NULL == image)	{
798		// Failed to allocate middle image
799		return NULL;
800	}
801
802	if(0 == dAngle) {
803		if(image == src) {
804			// Nothing to do ...
805			return FreeImage_Clone(src);
806		} else {
807			// No more rotation needed
808			return image;
809		}
810	}
811	else {
812		// Perform last rotation
813		FIBITMAP *dst = Rotate45(image, dAngle, bkcolor);
814
815		if(src != image) {
816			// Middle image was required, free it now.
817			FreeImage_Unload(image);
818		}
819
820		return dst;
821	}
822}
823
824// ==========================================================
825
826FIBITMAP *DLL_CALLCONV 
827FreeImage_Rotate(FIBITMAP *dib, double angle, const void *bkcolor) {
828	if(!FreeImage_HasPixels(dib)) return NULL;
829
830	if(0 == angle) {
831		return FreeImage_Clone(dib);
832	}
833	// DIB are stored upside down ...
834	angle *= -1;
835
836	try {
837		unsigned bpp = FreeImage_GetBPP(dib);
838		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
839		
840		switch(image_type) {
841			case FIT_BITMAP:
842				if(bpp == 1) {
843					// only rotate for integer multiples of 90 degree
844					if(fmod(angle, 90) != 0)
845						return NULL;
846
847					// perform the rotation
848					FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
849					if(!dst) throw(1);
850
851					// build a greyscale palette
852					RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
853					if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) {
854						dst_pal[0].rgbRed = dst_pal[0].rgbGreen = dst_pal[0].rgbBlue = 0;
855						dst_pal[1].rgbRed = dst_pal[1].rgbGreen = dst_pal[1].rgbBlue = 255;			
856					} else {
857						dst_pal[0].rgbRed = dst_pal[0].rgbGreen = dst_pal[0].rgbBlue = 255;
858						dst_pal[1].rgbRed = dst_pal[1].rgbGreen = dst_pal[1].rgbBlue = 0;			
859					}
860
861					// copy metadata from src to dst
862					FreeImage_CloneMetadata(dst, dib);
863
864					return dst;
865				}
866				else if((bpp == 8) || (bpp == 24) || (bpp == 32)) {
867					FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
868					if(!dst) throw(1);
869					
870					if(bpp == 8) {
871						// copy original palette to rotated bitmap
872						RGBQUAD *src_pal = FreeImage_GetPalette(dib);
873						RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
874						memcpy(&dst_pal[0], &src_pal[0], 256 * sizeof(RGBQUAD));
875
876						// copy transparency table 
877						FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
878
879						// copy background color 
880						RGBQUAD bkcolor; 
881						if( FreeImage_GetBackgroundColor(dib, &bkcolor) ) {
882							FreeImage_SetBackgroundColor(dst, &bkcolor); 
883						}
884
885					}
886
887					// copy metadata from src to dst
888					FreeImage_CloneMetadata(dst, dib);
889
890					return dst;
891				}
892				break;
893			case FIT_UINT16:
894			case FIT_RGB16:
895			case FIT_RGBA16:
896			case FIT_FLOAT:
897			case FIT_RGBF:
898			case FIT_RGBAF:
899			{
900				FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
901				if(!dst) throw(1);
902
903				// copy metadata from src to dst
904				FreeImage_CloneMetadata(dst, dib);
905
906				return dst;
907			}
908			break;
909		}
910
911	} catch(int) {
912		return NULL;
913	}
914
915	return NULL;
916}
917