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