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 /**
44 Skews a row horizontally (with filtered weights).
45 Limited to 45 degree skewing only. Filters two adjacent pixels.
46 Parameter 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 */
54 template <class T> void
HorizontalSkewT(FIBITMAP * src,FIBITMAP * dst,int row,int iOffset,double weight,const void * bkcolor=NULL)55 HorizontalSkewT(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 /**
138 Skews a row horizontally (with filtered weights).
139 Limited 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 */
147 static void
HorizontalSkew(FIBITMAP * src,FIBITMAP * dst,int row,int iOffset,double dWeight,const void * bkcolor)148 HorizontalSkew(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 /**
175 Skews a column vertically (with filtered weights).
176 Limited to 45 degree skewing only. Filters two adjacent pixels.
177 Parameter 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 */
185 template <class T> void
VerticalSkewT(FIBITMAP * src,FIBITMAP * dst,int col,int iOffset,double weight,const void * bkcolor=NULL)186 VerticalSkewT(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 /**
277 Skews a column vertically (with filtered weights).
278 Limited 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 */
286 static void
VerticalSkew(FIBITMAP * src,FIBITMAP * dst,int col,int iOffset,double dWeight,const void * bkcolor)287 VerticalSkew(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 /**
314 Rotates an image by 90 degrees (counter clockwise).
315 Precise rotation, no filters required.<br>
316 Code 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 */
320 static FIBITMAP*
Rotate90(FIBITMAP * src)321 Rotate90(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 /**
438 Rotates an image by 180 degrees (counter clockwise).
439 Precise 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 */
443 static FIBITMAP*
Rotate180(FIBITMAP * src)444 Rotate180(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 /**
505 Rotates an image by 270 degrees (counter clockwise).
506 Precise rotation, no filters required.<br>
507 Code 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 */
511 static FIBITMAP*
Rotate270(FIBITMAP * src)512 Rotate270(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 /**
630 Rotates an image by a given degree in range [-45 .. +45] (counter clockwise)
631 using 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 */
636 static FIBITMAP*
Rotate45(FIBITMAP * src,double dAngle,const void * bkcolor)637 Rotate45(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 /**
747 Rotates a 1-, 8-, 24- or 32-bit image by a given angle (given in degree).
748 Angle is unlimited, except for 1-bit images (limited to integer multiples of 90 degree).
749 3-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 */
754 static FIBITMAP*
RotateAny(FIBITMAP * src,double dAngle,const void * bkcolor)755 RotateAny(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 
826 FIBITMAP *DLL_CALLCONV
FreeImage_Rotate(FIBITMAP * dib,double angle,const void * bkcolor)827 FreeImage_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 
918