/src/freeimage-svn/FreeImage/trunk/Source/FreeImageToolkit/ClassicRotate.cpp
Line  | Count  | Source  | 
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  | 0  | #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   | 
55  | 0  | HorizontalSkewT(FIBITMAP *src, FIBITMAP *dst, int row, int iOffset, double weight, const void *bkcolor = NULL) { | 
56  | 0  |   int iXPos;  | 
57  |  | 
  | 
58  | 0  |   const unsigned src_width  = FreeImage_GetWidth(src);  | 
59  | 0  |   const unsigned dst_width  = FreeImage_GetWidth(dst);  | 
60  |  | 
  | 
61  | 0  |   T pxlSrc[4], pxlLeft[4], pxlOldLeft[4]; // 4 = 4*sizeof(T) max  | 
62  |  |     | 
63  |  |   // background  | 
64  | 0  |   const T pxlBlack[4] = {0, 0, 0, 0 }; | 
65  | 0  |   const T *pxlBkg = static_cast<const T*>(bkcolor); // assume at least bytespp and 4*sizeof(T) max  | 
66  | 0  |   if(!pxlBkg) { | 
67  |  |     // default background color is black  | 
68  | 0  |     pxlBkg = pxlBlack;  | 
69  | 0  |   }  | 
70  |  |  | 
71  |  |   // calculate the number of bytes per pixel  | 
72  | 0  |   const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);  | 
73  |  |   // calculate the number of samples per pixel  | 
74  | 0  |   const unsigned samples = bytespp / sizeof(T);  | 
75  |  | 
  | 
76  | 0  |   BYTE *src_bits = FreeImage_GetScanLine(src, row);  | 
77  | 0  |   BYTE *dst_bits = FreeImage_GetScanLine(dst, row);  | 
78  |  |  | 
79  |  |   // fill gap left of skew with background  | 
80  | 0  |   if(bkcolor) { | 
81  | 0  |     for(int k = 0; k < iOffset; k++) { | 
82  | 0  |       memcpy(&dst_bits[k * bytespp], bkcolor, bytespp);  | 
83  | 0  |     }  | 
84  | 0  |     AssignPixel((BYTE*)&pxlOldLeft[0], (BYTE*)bkcolor, bytespp);  | 
85  | 0  |   } else { | 
86  | 0  |     if(iOffset > 0) { | 
87  | 0  |       memset(dst_bits, 0, iOffset * bytespp);  | 
88  | 0  |     }      | 
89  | 0  |     memset(&pxlOldLeft[0], 0, bytespp);  | 
90  | 0  |   }  | 
91  |  | 
  | 
92  | 0  |   for(unsigned i = 0; i < src_width; i++) { | 
93  |  |     // loop through row pixels  | 
94  | 0  |     AssignPixel((BYTE*)&pxlSrc[0], (BYTE*)src_bits, bytespp);  | 
95  |  |     // calculate weights  | 
96  | 0  |     for(unsigned j = 0; j < samples; j++) { | 
97  | 0  |       pxlLeft[j] = static_cast<T>(pxlBkg[j] + (pxlSrc[j] - pxlBkg[j]) * weight + 0.5);  | 
98  | 0  |     }  | 
99  |  |     // check boundaries   | 
100  | 0  |     iXPos = i + iOffset;  | 
101  | 0  |     if((iXPos >= 0) && (iXPos < (int)dst_width)) { | 
102  |  |       // update left over on source  | 
103  | 0  |       for(unsigned j = 0; j < samples; j++) { | 
104  | 0  |         pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);  | 
105  | 0  |       }  | 
106  | 0  |       AssignPixel((BYTE*)&dst_bits[iXPos*bytespp], (BYTE*)&pxlSrc[0], bytespp);  | 
107  | 0  |     }  | 
108  |  |     // save leftover for next pixel in scan  | 
109  | 0  |     AssignPixel((BYTE*)&pxlOldLeft[0], (BYTE*)&pxlLeft[0], bytespp);  | 
110  |  |  | 
111  |  |     // next pixel in scan  | 
112  | 0  |     src_bits += bytespp;  | 
113  | 0  |   }       | 
114  |  |  | 
115  |  |   // go to rightmost point of skew  | 
116  | 0  |   iXPos = src_width + iOffset;   | 
117  |  | 
  | 
118  | 0  |   if((iXPos >= 0) && (iXPos < (int)dst_width)) { | 
119  | 0  |     dst_bits = FreeImage_GetScanLine(dst, row) + iXPos * bytespp;  | 
120  |  |  | 
121  |  |     // If still in image bounds, put leftovers there  | 
122  | 0  |     AssignPixel((BYTE*)dst_bits, (BYTE*)&pxlOldLeft[0], bytespp);  | 
123  |  |  | 
124  |  |     // clear to the right of the skewed line with background  | 
125  | 0  |     dst_bits += bytespp;  | 
126  | 0  |     if(bkcolor) { | 
127  | 0  |       for(unsigned i = 0; i < dst_width - iXPos - 1; i++) { | 
128  | 0  |         memcpy(&dst_bits[i * bytespp], bkcolor, bytespp);  | 
129  | 0  |       }  | 
130  | 0  |     } else { | 
131  | 0  |       memset(dst_bits, 0, bytespp * (dst_width - iXPos - 1));  | 
132  | 0  |     }  | 
133  |  | 
  | 
134  | 0  |   }  | 
135  | 0  | } Unexecuted instantiation: void HorizontalSkewT<unsigned char>(FIBITMAP*, FIBITMAP*, int, int, double, void const*) Unexecuted instantiation: void HorizontalSkewT<unsigned short>(FIBITMAP*, FIBITMAP*, int, int, double, void const*) Unexecuted instantiation: void HorizontalSkewT<float>(FIBITMAP*, FIBITMAP*, int, int, double, void const*)  | 
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   | 
148  | 0  | HorizontalSkew(FIBITMAP *src, FIBITMAP *dst, int row, int iOffset, double dWeight, const void *bkcolor) { | 
149  | 0  |   FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);  | 
150  |  | 
  | 
151  | 0  |   switch(image_type) { | 
152  | 0  |     case FIT_BITMAP:  | 
153  | 0  |       switch(FreeImage_GetBPP(src)) { | 
154  | 0  |         case 8:  | 
155  | 0  |         case 24:  | 
156  | 0  |         case 32:  | 
157  | 0  |           HorizontalSkewT<BYTE>(src, dst, row, iOffset, dWeight, bkcolor);  | 
158  | 0  |         break;  | 
159  | 0  |       }  | 
160  | 0  |       break;  | 
161  | 0  |     case FIT_UINT16:  | 
162  | 0  |     case FIT_RGB16:  | 
163  | 0  |     case FIT_RGBA16:  | 
164  | 0  |       HorizontalSkewT<WORD>(src, dst, row, iOffset, dWeight, bkcolor);  | 
165  | 0  |       break;  | 
166  | 0  |     case FIT_FLOAT:  | 
167  | 0  |     case FIT_RGBF:  | 
168  | 0  |     case FIT_RGBAF:  | 
169  | 0  |       HorizontalSkewT<float>(src, dst, row, iOffset, dWeight, bkcolor);  | 
170  | 0  |       break;  | 
171  | 0  |   }  | 
172  | 0  | }  | 
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   | 
186  | 0  | VerticalSkewT(FIBITMAP *src, FIBITMAP *dst, int col, int iOffset, double weight, const void *bkcolor = NULL) { | 
187  | 0  |   int iYPos;  | 
188  |  | 
  | 
189  | 0  |   unsigned src_height = FreeImage_GetHeight(src);  | 
190  | 0  |   unsigned dst_height = FreeImage_GetHeight(dst);  | 
191  |  | 
  | 
192  | 0  |   T pxlSrc[4], pxlLeft[4], pxlOldLeft[4]; // 4 = 4*sizeof(T) max  | 
193  |  |  | 
194  |  |   // background  | 
195  | 0  |   const T pxlBlack[4] = {0, 0, 0, 0 }; | 
196  | 0  |   const T *pxlBkg = static_cast<const T*>(bkcolor); // assume at least bytespp and 4*sizeof(T) max  | 
197  | 0  |   if(!pxlBkg) { | 
198  |  |     // default background color is black  | 
199  | 0  |     pxlBkg = pxlBlack;  | 
200  | 0  |   }  | 
201  |  |  | 
202  |  |   // calculate the number of bytes per pixel  | 
203  | 0  |   const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);  | 
204  |  |   // calculate the number of samples per pixel  | 
205  | 0  |   const unsigned samples = bytespp / sizeof(T);  | 
206  |  | 
  | 
207  | 0  |   const unsigned src_pitch = FreeImage_GetPitch(src);  | 
208  | 0  |   const unsigned dst_pitch = FreeImage_GetPitch(dst);  | 
209  | 0  |   const unsigned index = col * bytespp;  | 
210  |  | 
  | 
211  | 0  |   BYTE *src_bits = FreeImage_GetBits(src) + index;  | 
212  | 0  |   BYTE *dst_bits = FreeImage_GetBits(dst) + index;  | 
213  |  |  | 
214  |  |   // fill gap above skew with background  | 
215  | 0  |   if(bkcolor) { | 
216  | 0  |     for(int k = 0; k < iOffset; k++) { | 
217  | 0  |       memcpy(dst_bits, bkcolor, bytespp);  | 
218  | 0  |       dst_bits += dst_pitch;  | 
219  | 0  |     }  | 
220  | 0  |     memcpy(&pxlOldLeft[0], bkcolor, bytespp);  | 
221  | 0  |   } else { | 
222  | 0  |     for(int k = 0; k < iOffset; k++) { | 
223  | 0  |       memset(dst_bits, 0, bytespp);  | 
224  | 0  |       dst_bits += dst_pitch;  | 
225  | 0  |     }  | 
226  | 0  |     memset(&pxlOldLeft[0], 0, bytespp);  | 
227  | 0  |   }  | 
228  |  | 
  | 
229  | 0  |   for(unsigned i = 0; i < src_height; i++) { | 
230  |  |     // loop through column pixels  | 
231  | 0  |     AssignPixel((BYTE*)(&pxlSrc[0]), src_bits, bytespp);  | 
232  |  |     // calculate weights  | 
233  | 0  |     for(unsigned j = 0; j < samples; j++) { | 
234  | 0  |       pxlLeft[j] = static_cast<T>(pxlBkg[j] + (pxlSrc[j] - pxlBkg[j]) * weight + 0.5);  | 
235  | 0  |     }  | 
236  |  |     // check boundaries  | 
237  | 0  |     iYPos = i + iOffset;  | 
238  | 0  |     if((iYPos >= 0) && (iYPos < (int)dst_height)) { | 
239  |  |       // update left over on source  | 
240  | 0  |       for(unsigned j = 0; j < samples; j++) { | 
241  | 0  |         pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);  | 
242  | 0  |       }  | 
243  | 0  |       dst_bits = FreeImage_GetScanLine(dst, iYPos) + index;  | 
244  | 0  |       AssignPixel(dst_bits, (BYTE*)(&pxlSrc[0]), bytespp);  | 
245  | 0  |     }  | 
246  |  |     // save leftover for next pixel in scan  | 
247  | 0  |     AssignPixel((BYTE*)(&pxlOldLeft[0]), (BYTE*)(&pxlLeft[0]), bytespp);  | 
248  |  |  | 
249  |  |     // next pixel in scan  | 
250  | 0  |     src_bits += src_pitch;  | 
251  | 0  |   }  | 
252  |  |   // go to bottom point of skew  | 
253  | 0  |   iYPos = src_height + iOffset;  | 
254  |  | 
  | 
255  | 0  |   if((iYPos >= 0) && (iYPos < (int)dst_height)) { | 
256  | 0  |     dst_bits = FreeImage_GetScanLine(dst, iYPos) + index;  | 
257  |  |  | 
258  |  |     // if still in image bounds, put leftovers there          | 
259  | 0  |     AssignPixel((BYTE*)(dst_bits), (BYTE*)(&pxlOldLeft[0]), bytespp);  | 
260  |  |  | 
261  |  |     // clear below skewed line with background  | 
262  | 0  |     if(bkcolor) { | 
263  | 0  |       while(++iYPos < (int)dst_height) {          | 
264  | 0  |         dst_bits += dst_pitch;  | 
265  | 0  |         AssignPixel((BYTE*)(dst_bits), (BYTE*)(bkcolor), bytespp);  | 
266  | 0  |       }  | 
267  | 0  |     } else { | 
268  | 0  |       while(++iYPos < (int)dst_height) {          | 
269  | 0  |         dst_bits += dst_pitch;  | 
270  | 0  |         memset(dst_bits, 0, bytespp);  | 
271  | 0  |       }  | 
272  | 0  |     }  | 
273  | 0  |   }  | 
274  | 0  | } Unexecuted instantiation: void VerticalSkewT<unsigned char>(FIBITMAP*, FIBITMAP*, int, int, double, void const*) Unexecuted instantiation: void VerticalSkewT<unsigned short>(FIBITMAP*, FIBITMAP*, int, int, double, void const*) Unexecuted instantiation: void VerticalSkewT<float>(FIBITMAP*, FIBITMAP*, int, int, double, void const*)  | 
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   | 
287  | 0  | VerticalSkew(FIBITMAP *src, FIBITMAP *dst, int col, int iOffset, double dWeight, const void *bkcolor) { | 
288  | 0  |   FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);  | 
289  |  | 
  | 
290  | 0  |   switch(image_type) { | 
291  | 0  |     case FIT_BITMAP:  | 
292  | 0  |       switch(FreeImage_GetBPP(src)) { | 
293  | 0  |         case 8:  | 
294  | 0  |         case 24:  | 
295  | 0  |         case 32:  | 
296  | 0  |           VerticalSkewT<BYTE>(src, dst, col, iOffset, dWeight, bkcolor);  | 
297  | 0  |           break;  | 
298  | 0  |       }  | 
299  | 0  |       break;  | 
300  | 0  |       case FIT_UINT16:  | 
301  | 0  |       case FIT_RGB16:  | 
302  | 0  |       case FIT_RGBA16:  | 
303  | 0  |         VerticalSkewT<WORD>(src, dst, col, iOffset, dWeight, bkcolor);  | 
304  | 0  |         break;  | 
305  | 0  |       case FIT_FLOAT:  | 
306  | 0  |       case FIT_RGBF:  | 
307  | 0  |       case FIT_RGBAF:  | 
308  | 0  |         VerticalSkewT<float>(src, dst, col, iOffset, dWeight, bkcolor);  | 
309  | 0  |         break;  | 
310  | 0  |   }  | 
311  | 0  | }   | 
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*   | 
321  | 0  | Rotate90(FIBITMAP *src) { | 
322  |  | 
  | 
323  | 0  |   const unsigned bpp = FreeImage_GetBPP(src);  | 
324  |  | 
  | 
325  | 0  |   const unsigned src_width  = FreeImage_GetWidth(src);  | 
326  | 0  |   const unsigned src_height = FreeImage_GetHeight(src);   | 
327  | 0  |   const unsigned dst_width  = src_height;  | 
328  | 0  |   const unsigned dst_height = src_width;  | 
329  |  | 
  | 
330  | 0  |   FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);  | 
331  |  |  | 
332  |  |   // allocate and clear dst image  | 
333  | 0  |   FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);  | 
334  | 0  |   if(NULL == dst) return NULL;  | 
335  |  |  | 
336  |  |   // get src and dst scan width  | 
337  | 0  |   const unsigned src_pitch  = FreeImage_GetPitch(src);  | 
338  | 0  |   const unsigned dst_pitch  = FreeImage_GetPitch(dst);  | 
339  |  | 
  | 
340  | 0  |   switch(image_type) { | 
341  | 0  |     case FIT_BITMAP:  | 
342  | 0  |       if(bpp == 1) { | 
343  |  |         // speedy rotate for BW images  | 
344  |  | 
  | 
345  | 0  |         BYTE *bsrc  = FreeImage_GetBits(src);   | 
346  | 0  |         BYTE *bdest = FreeImage_GetBits(dst);  | 
347  |  | 
  | 
348  | 0  |         BYTE *dbitsmax = bdest + dst_height * dst_pitch - 1;  | 
349  |  | 
  | 
350  | 0  |         for(unsigned y = 0; y < src_height; y++) { | 
351  |  |           // figure out the column we are going to be copying to  | 
352  | 0  |           const div_t div_r = div(y, 8);  | 
353  |  |           // set bit pos of src column byte  | 
354  | 0  |           const BYTE bitpos = (BYTE)(128 >> div_r.rem);  | 
355  | 0  |           BYTE *srcdisp = bsrc + y * src_pitch;  | 
356  | 0  |           for(unsigned x = 0; x < src_pitch; x++) { | 
357  |  |             // get source bits  | 
358  | 0  |             BYTE *sbits = srcdisp + x;  | 
359  |  |             // get destination column  | 
360  | 0  |             BYTE *nrow = bdest + (dst_height - 1 - (x * 8)) * dst_pitch + div_r.quot;  | 
361  | 0  |             for (int z = 0; z < 8; z++) { | 
362  |  |                // get destination byte  | 
363  | 0  |               BYTE *dbits = nrow - z * dst_pitch;  | 
364  | 0  |               if ((dbits < bdest) || (dbits > dbitsmax)) break;  | 
365  | 0  |               if (*sbits & (128 >> z)) *dbits |= bitpos;  | 
366  | 0  |             }  | 
367  | 0  |           }  | 
368  | 0  |         }  | 
369  | 0  |       }  | 
370  | 0  |       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  | 0  |         BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels  | 
380  | 0  |         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  | 0  |         const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);  | 
384  |  |           | 
385  |  |         // for all image blocks of RBLOCK*RBLOCK pixels  | 
386  |  |           | 
387  |  |         // x-segment  | 
388  | 0  |         for(unsigned xs = 0; xs < dst_width; xs += RBLOCK) { | 
389  |  |           // y-segment  | 
390  | 0  |           for(unsigned ys = 0; ys < dst_height; ys += RBLOCK) { | 
391  | 0  |             for(unsigned y = ys; y < MIN(dst_height, ys + RBLOCK); y++) {    // do rotation | 
392  | 0  |               const unsigned y2 = dst_height - y - 1;  | 
393  |  |               // point to src pixel at (y2, xs)  | 
394  | 0  |               BYTE *src_bits = bsrc + (xs * src_pitch) + (y2 * bytespp);  | 
395  |  |               // point to dst pixel at (xs, y)  | 
396  | 0  |               BYTE *dst_bits = bdest + (y * dst_pitch) + (xs * bytespp);  | 
397  | 0  |               for(unsigned x = xs; x < MIN(dst_width, xs + RBLOCK); x++) { | 
398  |  |                 // dst.SetPixel(x, y, src.GetPixel(y2, x));  | 
399  | 0  |                 AssignPixel(dst_bits, src_bits, bytespp);  | 
400  | 0  |                 dst_bits += bytespp;  | 
401  | 0  |                 src_bits += src_pitch;  | 
402  | 0  |               }  | 
403  | 0  |             }  | 
404  | 0  |           }  | 
405  | 0  |         }  | 
406  | 0  |       }  | 
407  | 0  |       break;  | 
408  | 0  |     case FIT_UINT16:  | 
409  | 0  |     case FIT_RGB16:  | 
410  | 0  |     case FIT_RGBA16:  | 
411  | 0  |     case FIT_FLOAT:  | 
412  | 0  |     case FIT_RGBF:  | 
413  | 0  |     case FIT_RGBAF:  | 
414  | 0  |     { | 
415  | 0  |       BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels  | 
416  | 0  |       BYTE *bdest = FreeImage_GetBits(dst);  // destination pixels  | 
417  |  |  | 
418  |  |       // calculate the number of bytes per pixel  | 
419  | 0  |       const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);  | 
420  |  | 
  | 
421  | 0  |       for(unsigned y = 0; y < dst_height; y++) { | 
422  | 0  |         BYTE *src_bits = bsrc + (src_width - 1 - y) * bytespp;  | 
423  | 0  |         BYTE *dst_bits = bdest + (y * dst_pitch);  | 
424  | 0  |         for(unsigned x = 0; x < dst_width; x++) { | 
425  | 0  |           AssignPixel(dst_bits, src_bits, bytespp);  | 
426  | 0  |           src_bits += src_pitch;  | 
427  | 0  |           dst_bits += bytespp;  | 
428  | 0  |         }  | 
429  | 0  |       }  | 
430  | 0  |     }  | 
431  | 0  |     break;  | 
432  | 0  |   }  | 
433  |  |  | 
434  | 0  |   return dst;  | 
435  | 0  | }  | 
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*   | 
444  | 0  | Rotate180(FIBITMAP *src) { | 
445  | 0  |   int x, y, k, pos;  | 
446  |  | 
  | 
447  | 0  |   const int bpp = FreeImage_GetBPP(src);  | 
448  |  | 
  | 
449  | 0  |   const int src_width  = FreeImage_GetWidth(src);  | 
450  | 0  |   const int src_height = FreeImage_GetHeight(src);  | 
451  | 0  |   const int dst_width  = src_width;  | 
452  | 0  |   const int dst_height = src_height;  | 
453  |  | 
  | 
454  | 0  |   FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);  | 
455  |  | 
  | 
456  | 0  |   FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);  | 
457  | 0  |   if(NULL == dst) return NULL;  | 
458  |  |  | 
459  | 0  |   switch(image_type) { | 
460  | 0  |     case FIT_BITMAP:  | 
461  | 0  |       if(bpp == 1) { | 
462  | 0  |         for(int y = 0; y < src_height; y++) { | 
463  | 0  |           BYTE *src_bits = FreeImage_GetScanLine(src, y);  | 
464  | 0  |           BYTE *dst_bits = FreeImage_GetScanLine(dst, dst_height - y - 1);  | 
465  | 0  |           for(int x = 0; x < src_width; x++) { | 
466  |  |             // get bit at (x, y)  | 
467  | 0  |             k = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;  | 
468  |  |             // set bit at (dst_width - x - 1, dst_height - y - 1)  | 
469  | 0  |             pos = dst_width - x - 1;  | 
470  | 0  |             k ? dst_bits[pos >> 3] |= (0x80 >> (pos & 0x7)) : dst_bits[pos >> 3] &= (0xFF7F >> (pos & 0x7));  | 
471  | 0  |           }       | 
472  | 0  |         }  | 
473  | 0  |         break;  | 
474  | 0  |       }  | 
475  |  |       // else if((bpp == 8) || (bpp == 24) || (bpp == 32)) FALL TROUGH  | 
476  | 0  |     case FIT_UINT16:  | 
477  | 0  |     case FIT_RGB16:  | 
478  | 0  |     case FIT_RGBA16:  | 
479  | 0  |     case FIT_FLOAT:  | 
480  | 0  |     case FIT_RGBF:  | 
481  | 0  |     case FIT_RGBAF:  | 
482  | 0  |     { | 
483  |  |        // Calculate the number of bytes per pixel  | 
484  | 0  |       const int bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);  | 
485  |  | 
  | 
486  | 0  |       for(y = 0; y < src_height; y++) { | 
487  | 0  |         BYTE *src_bits = FreeImage_GetScanLine(src, y);  | 
488  | 0  |         BYTE *dst_bits = FreeImage_GetScanLine(dst, dst_height - y - 1) + (dst_width - 1) * bytespp;  | 
489  | 0  |         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  | 0  |           AssignPixel(dst_bits, src_bits, bytespp);  | 
493  | 0  |           src_bits += bytespp;  | 
494  | 0  |           dst_bits -= bytespp;            | 
495  | 0  |         }         | 
496  | 0  |       }  | 
497  | 0  |     }  | 
498  | 0  |     break;  | 
499  | 0  |   }  | 
500  |  |  | 
501  | 0  |   return dst;  | 
502  | 0  | }  | 
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*   | 
512  | 0  | Rotate270(FIBITMAP *src) { | 
513  | 0  |   int x2, dlineup;  | 
514  |  | 
  | 
515  | 0  |   const unsigned bpp = FreeImage_GetBPP(src);  | 
516  |  | 
  | 
517  | 0  |   const unsigned src_width  = FreeImage_GetWidth(src);  | 
518  | 0  |   const unsigned src_height = FreeImage_GetHeight(src);   | 
519  | 0  |   const unsigned dst_width  = src_height;  | 
520  | 0  |   const unsigned dst_height = src_width;  | 
521  |  | 
  | 
522  | 0  |   FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);  | 
523  |  |  | 
524  |  |   // allocate and clear dst image  | 
525  | 0  |   FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp);  | 
526  | 0  |   if(NULL == dst) return NULL;  | 
527  |  |  | 
528  |  |   // get src and dst scan width  | 
529  | 0  |   const unsigned src_pitch  = FreeImage_GetPitch(src);  | 
530  | 0  |   const unsigned dst_pitch  = FreeImage_GetPitch(dst);  | 
531  |  |     | 
532  | 0  |   switch(image_type) { | 
533  | 0  |     case FIT_BITMAP:  | 
534  | 0  |       if(bpp == 1) { | 
535  |  |         // speedy rotate for BW images  | 
536  |  |           | 
537  | 0  |         BYTE *bsrc  = FreeImage_GetBits(src);   | 
538  | 0  |         BYTE *bdest = FreeImage_GetBits(dst);  | 
539  | 0  |         BYTE *dbitsmax = bdest + dst_height * dst_pitch - 1;  | 
540  | 0  |         dlineup = 8 * dst_pitch - dst_width;  | 
541  |  | 
  | 
542  | 0  |         for(unsigned y = 0; y < src_height; y++) { | 
543  |  |           // figure out the column we are going to be copying to  | 
544  | 0  |           const div_t div_r = div(y + dlineup, 8);  | 
545  |  |           // set bit pos of src column byte  | 
546  | 0  |           const BYTE bitpos = (BYTE)(1 << div_r.rem);  | 
547  | 0  |           const BYTE *srcdisp = bsrc + y * src_pitch;  | 
548  | 0  |           for(unsigned x = 0; x < src_pitch; x++) { | 
549  |  |             // get source bits  | 
550  | 0  |             const BYTE *sbits = srcdisp + x;  | 
551  |  |             // get destination column  | 
552  | 0  |             BYTE *nrow = bdest + (x * 8) * dst_pitch + dst_pitch - 1 - div_r.quot;  | 
553  | 0  |             for(unsigned z = 0; z < 8; z++) { | 
554  |  |                // get destination byte  | 
555  | 0  |               BYTE *dbits = nrow + z * dst_pitch;  | 
556  | 0  |               if ((dbits < bdest) || (dbits > dbitsmax)) break;  | 
557  | 0  |               if (*sbits & (128 >> z)) *dbits |= bitpos;  | 
558  | 0  |             }  | 
559  | 0  |           }  | 
560  | 0  |         }  | 
561  | 0  |       }   | 
562  | 0  |       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  | 0  |         BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels  | 
572  | 0  |         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  | 0  |         const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);  | 
576  |  |  | 
577  |  |         // for all image blocks of RBLOCK*RBLOCK pixels  | 
578  |  |  | 
579  |  |         // x-segment  | 
580  | 0  |         for(unsigned xs = 0; xs < dst_width; xs += RBLOCK) { | 
581  |  |           // y-segment  | 
582  | 0  |           for(unsigned ys = 0; ys < dst_height; ys += RBLOCK) { | 
583  | 0  |             for(unsigned x = xs; x < MIN(dst_width, xs + RBLOCK); x++) {    // do rotation | 
584  | 0  |               x2 = dst_width - x - 1;  | 
585  |  |               // point to src pixel at (ys, x2)  | 
586  | 0  |               BYTE *src_bits = bsrc + (x2 * src_pitch) + (ys * bytespp);  | 
587  |  |               // point to dst pixel at (x, ys)  | 
588  | 0  |               BYTE *dst_bits = bdest + (ys * dst_pitch) + (x * bytespp);  | 
589  | 0  |               for(unsigned y = ys; y < MIN(dst_height, ys + RBLOCK); y++) { | 
590  |  |                 // dst.SetPixel(x, y, src.GetPixel(y, x2));  | 
591  | 0  |                 AssignPixel(dst_bits, src_bits, bytespp);  | 
592  | 0  |                 src_bits += bytespp;  | 
593  | 0  |                 dst_bits += dst_pitch;  | 
594  | 0  |               }  | 
595  | 0  |             }  | 
596  | 0  |           }  | 
597  | 0  |         }  | 
598  | 0  |       }  | 
599  | 0  |       break;  | 
600  | 0  |     case FIT_UINT16:  | 
601  | 0  |     case FIT_RGB16:  | 
602  | 0  |     case FIT_RGBA16:  | 
603  | 0  |     case FIT_FLOAT:  | 
604  | 0  |     case FIT_RGBF:  | 
605  | 0  |     case FIT_RGBAF:  | 
606  | 0  |     { | 
607  | 0  |       BYTE *bsrc  = FreeImage_GetBits(src);  // source pixels  | 
608  | 0  |       BYTE *bdest = FreeImage_GetBits(dst);  // destination pixels  | 
609  |  |  | 
610  |  |       // calculate the number of bytes per pixel  | 
611  | 0  |       const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);  | 
612  |  | 
  | 
613  | 0  |       for(unsigned y = 0; y < dst_height; y++) { | 
614  | 0  |         BYTE *src_bits = bsrc + (src_height - 1) * src_pitch + y * bytespp;  | 
615  | 0  |         BYTE *dst_bits = bdest + (y * dst_pitch);  | 
616  | 0  |         for(unsigned x = 0; x < dst_width; x++) { | 
617  | 0  |           AssignPixel(dst_bits, src_bits, bytespp);  | 
618  | 0  |           src_bits -= src_pitch;  | 
619  | 0  |           dst_bits += bytespp;  | 
620  | 0  |         }  | 
621  | 0  |       }  | 
622  | 0  |     }  | 
623  | 0  |     break;  | 
624  | 0  |   }  | 
625  |  |  | 
626  | 0  |   return dst;  | 
627  | 0  | }  | 
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*   | 
637  | 0  | Rotate45(FIBITMAP *src, double dAngle, const void *bkcolor) { | 
638  | 0  |   const double ROTATE_PI = double(3.1415926535897932384626433832795);  | 
639  |  | 
  | 
640  | 0  |   unsigned u;  | 
641  |  | 
  | 
642  | 0  |   const unsigned bpp = FreeImage_GetBPP(src);  | 
643  |  | 
  | 
644  | 0  |   const double dRadAngle = dAngle * ROTATE_PI / double(180); // Angle in radians  | 
645  | 0  |   const double dSinE = sin(dRadAngle);  | 
646  | 0  |   const double dTan = tan(dRadAngle / 2);  | 
647  |  | 
  | 
648  | 0  |   const unsigned src_width  = FreeImage_GetWidth(src);  | 
649  | 0  |   const unsigned src_height = FreeImage_GetHeight(src);  | 
650  |  | 
  | 
651  | 0  |   FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);  | 
652  |  |  | 
653  |  |   // Calc first shear (horizontal) destination image dimensions   | 
654  | 0  |   const unsigned width_1  = src_width + unsigned((double)src_height * fabs(dTan) + 0.5);  | 
655  | 0  |   const unsigned height_1 = src_height;   | 
656  |  |  | 
657  |  |   // Perform 1st shear (horizontal)  | 
658  |  |   // ----------------------------------------------------------------------  | 
659  |  |  | 
660  |  |   // Allocate image for 1st shear  | 
661  | 0  |   FIBITMAP *dst1 = FreeImage_AllocateT(image_type, width_1, height_1, bpp);  | 
662  | 0  |   if(NULL == dst1) { | 
663  | 0  |     return NULL;  | 
664  | 0  |   }  | 
665  |  |     | 
666  | 0  |   for(u = 0; u < height_1; u++) {   | 
667  | 0  |     double dShear;  | 
668  |  | 
  | 
669  | 0  |     if(dTan >= 0) { | 
670  |  |       // Positive angle  | 
671  | 0  |       dShear = (u + 0.5) * dTan;  | 
672  | 0  |     }  | 
673  | 0  |     else { | 
674  |  |       // Negative angle  | 
675  | 0  |       dShear = (double(u) - height_1 + 0.5) * dTan;  | 
676  | 0  |     }  | 
677  | 0  |     int iShear = int(floor(dShear));  | 
678  | 0  |     HorizontalSkew(src, dst1, u, iShear, dShear - double(iShear), bkcolor);  | 
679  | 0  |   }  | 
680  |  |  | 
681  |  |   // Perform 2nd shear  (vertical)  | 
682  |  |   // ----------------------------------------------------------------------  | 
683  |  |  | 
684  |  |   // Calc 2nd shear (vertical) destination image dimensions  | 
685  | 0  |   const unsigned width_2  = width_1;  | 
686  | 0  |   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  | 0  |   FIBITMAP *dst2 = FreeImage_AllocateT(image_type, width_2, height_2, bpp);  | 
690  | 0  |   if(NULL == dst2) { | 
691  | 0  |     FreeImage_Unload(dst1);  | 
692  | 0  |     return NULL;  | 
693  | 0  |   }  | 
694  |  |  | 
695  | 0  |   double dOffset;     // Variable skew offset  | 
696  | 0  |   if(dSinE > 0) {    | 
697  |  |     // Positive angle  | 
698  | 0  |     dOffset = (src_width - 1.0) * dSinE;  | 
699  | 0  |   }  | 
700  | 0  |   else { | 
701  |  |     // Negative angle  | 
702  | 0  |     dOffset = -dSinE * (double(src_width) - width_2);  | 
703  | 0  |   }  | 
704  |  | 
  | 
705  | 0  |   for(u = 0; u < width_2; u++, dOffset -= dSinE) { | 
706  | 0  |     int iShear = int(floor(dOffset));  | 
707  | 0  |     VerticalSkew(dst1, dst2, u, iShear, dOffset - double(iShear), bkcolor);  | 
708  | 0  |   }  | 
709  |  |  | 
710  |  |   // Perform 3rd shear (horizontal)  | 
711  |  |   // ----------------------------------------------------------------------  | 
712  |  |  | 
713  |  |   // Free result of 1st shear  | 
714  | 0  |   FreeImage_Unload(dst1);  | 
715  |  |  | 
716  |  |   // Calc 3rd shear (horizontal) destination image dimensions  | 
717  | 0  |   const unsigned width_3  = unsigned(double(src_height) * fabs(dSinE) + double(src_width) * cos(dRadAngle) + 0.5) + 1;  | 
718  | 0  |   const unsigned height_3 = height_2;  | 
719  |  |  | 
720  |  |   // Allocate image for 3rd shear  | 
721  | 0  |   FIBITMAP *dst3 = FreeImage_AllocateT(image_type, width_3, height_3, bpp);  | 
722  | 0  |   if(NULL == dst3) { | 
723  | 0  |     FreeImage_Unload(dst2);  | 
724  | 0  |     return NULL;  | 
725  | 0  |   }  | 
726  |  |  | 
727  | 0  |   if(dSinE >= 0) { | 
728  |  |     // Positive angle  | 
729  | 0  |     dOffset = (src_width - 1.0) * dSinE * -dTan;  | 
730  | 0  |   }  | 
731  | 0  |   else { | 
732  |  |     // Negative angle  | 
733  | 0  |     dOffset = dTan * ( (src_width - 1.0) * -dSinE + (1.0 - height_3) );  | 
734  | 0  |   }  | 
735  | 0  |   for(u = 0; u < height_3; u++, dOffset += dTan) { | 
736  | 0  |     int iShear = int(floor(dOffset));  | 
737  | 0  |     HorizontalSkew(dst2, dst3, u, iShear, dOffset - double(iShear), bkcolor);  | 
738  | 0  |   }  | 
739  |  |   // Free result of 2nd shear      | 
740  | 0  |   FreeImage_Unload(dst2);  | 
741  |  |  | 
742  |  |   // Return result of 3rd shear  | 
743  | 0  |   return dst3;        | 
744  | 0  | }  | 
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*   | 
755  | 0  | RotateAny(FIBITMAP *src, double dAngle, const void *bkcolor) { | 
756  | 0  |   if(NULL == src) { | 
757  | 0  |     return NULL;  | 
758  | 0  |   }  | 
759  |  |  | 
760  | 0  |   FIBITMAP *image = src;  | 
761  |  | 
  | 
762  | 0  |   while(dAngle >= 360) { | 
763  |  |     // Bring angle to range of (-INF .. 360)  | 
764  | 0  |     dAngle -= 360;  | 
765  | 0  |   }  | 
766  | 0  |   while(dAngle < 0) { | 
767  |  |     // Bring angle to range of [0 .. 360)   | 
768  | 0  |     dAngle += 360;  | 
769  | 0  |   }  | 
770  | 0  |   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  | 0  |     image = Rotate90(src);  | 
776  | 0  |     dAngle -= 90;  | 
777  | 0  |   }  | 
778  | 0  |   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  | 0  |     image = Rotate180(src);  | 
784  | 0  |     dAngle -= 180;  | 
785  | 0  |   }  | 
786  | 0  |   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  | 0  |     image = Rotate270(src);  | 
792  | 0  |     dAngle -= 270;  | 
793  | 0  |   }  | 
794  |  |  | 
795  |  |   // If we got here, angle is in (-45 .. +45]  | 
796  |  | 
  | 
797  | 0  |   if(NULL == image) { | 
798  |  |     // Failed to allocate middle image  | 
799  | 0  |     return NULL;  | 
800  | 0  |   }  | 
801  |  |  | 
802  | 0  |   if(0 == dAngle) { | 
803  | 0  |     if(image == src) { | 
804  |  |       // Nothing to do ...  | 
805  | 0  |       return FreeImage_Clone(src);  | 
806  | 0  |     } else { | 
807  |  |       // No more rotation needed  | 
808  | 0  |       return image;  | 
809  | 0  |     }  | 
810  | 0  |   }  | 
811  | 0  |   else { | 
812  |  |     // Perform last rotation  | 
813  | 0  |     FIBITMAP *dst = Rotate45(image, dAngle, bkcolor);  | 
814  |  | 
  | 
815  | 0  |     if(src != image) { | 
816  |  |       // Middle image was required, free it now.  | 
817  | 0  |       FreeImage_Unload(image);  | 
818  | 0  |     }  | 
819  |  | 
  | 
820  | 0  |     return dst;  | 
821  | 0  |   }  | 
822  | 0  | }  | 
823  |  |  | 
824  |  | // ==========================================================  | 
825  |  |  | 
826  |  | FIBITMAP *DLL_CALLCONV   | 
827  | 0  | FreeImage_Rotate(FIBITMAP *dib, double angle, const void *bkcolor) { | 
828  | 0  |   if(!FreeImage_HasPixels(dib)) return NULL;  | 
829  |  |  | 
830  | 0  |   if(0 == angle) { | 
831  | 0  |     return FreeImage_Clone(dib);  | 
832  | 0  |   }  | 
833  |  |   // DIB are stored upside down ...  | 
834  | 0  |   angle *= -1;  | 
835  |  | 
  | 
836  | 0  |   try { | 
837  | 0  |     unsigned bpp = FreeImage_GetBPP(dib);  | 
838  | 0  |     FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);  | 
839  |  |       | 
840  | 0  |     switch(image_type) { | 
841  | 0  |       case FIT_BITMAP:  | 
842  | 0  |         if(bpp == 1) { | 
843  |  |           // only rotate for integer multiples of 90 degree  | 
844  | 0  |           if(fmod(angle, 90) != 0)  | 
845  | 0  |             return NULL;  | 
846  |  |  | 
847  |  |           // perform the rotation  | 
848  | 0  |           FIBITMAP *dst = RotateAny(dib, angle, bkcolor);  | 
849  | 0  |           if(!dst) throw(1);  | 
850  |  |  | 
851  |  |           // build a greyscale palette  | 
852  | 0  |           RGBQUAD *dst_pal = FreeImage_GetPalette(dst);  | 
853  | 0  |           if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) { | 
854  | 0  |             dst_pal[0].rgbRed = dst_pal[0].rgbGreen = dst_pal[0].rgbBlue = 0;  | 
855  | 0  |             dst_pal[1].rgbRed = dst_pal[1].rgbGreen = dst_pal[1].rgbBlue = 255;       | 
856  | 0  |           } else { | 
857  | 0  |             dst_pal[0].rgbRed = dst_pal[0].rgbGreen = dst_pal[0].rgbBlue = 255;  | 
858  | 0  |             dst_pal[1].rgbRed = dst_pal[1].rgbGreen = dst_pal[1].rgbBlue = 0;       | 
859  | 0  |           }  | 
860  |  |  | 
861  |  |           // copy metadata from src to dst  | 
862  | 0  |           FreeImage_CloneMetadata(dst, dib);  | 
863  |  | 
  | 
864  | 0  |           return dst;  | 
865  | 0  |         }  | 
866  | 0  |         else if((bpp == 8) || (bpp == 24) || (bpp == 32)) { | 
867  | 0  |           FIBITMAP *dst = RotateAny(dib, angle, bkcolor);  | 
868  | 0  |           if(!dst) throw(1);  | 
869  |  |             | 
870  | 0  |           if(bpp == 8) { | 
871  |  |             // copy original palette to rotated bitmap  | 
872  | 0  |             RGBQUAD *src_pal = FreeImage_GetPalette(dib);  | 
873  | 0  |             RGBQUAD *dst_pal = FreeImage_GetPalette(dst);  | 
874  | 0  |             memcpy(&dst_pal[0], &src_pal[0], 256 * sizeof(RGBQUAD));  | 
875  |  |  | 
876  |  |             // copy transparency table   | 
877  | 0  |             FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));  | 
878  |  |  | 
879  |  |             // copy background color   | 
880  | 0  |             RGBQUAD bkcolor;   | 
881  | 0  |             if( FreeImage_GetBackgroundColor(dib, &bkcolor) ) { | 
882  | 0  |               FreeImage_SetBackgroundColor(dst, &bkcolor);   | 
883  | 0  |             }  | 
884  |  | 
  | 
885  | 0  |           }  | 
886  |  |  | 
887  |  |           // copy metadata from src to dst  | 
888  | 0  |           FreeImage_CloneMetadata(dst, dib);  | 
889  |  | 
  | 
890  | 0  |           return dst;  | 
891  | 0  |         }  | 
892  | 0  |         break;  | 
893  | 0  |       case FIT_UINT16:  | 
894  | 0  |       case FIT_RGB16:  | 
895  | 0  |       case FIT_RGBA16:  | 
896  | 0  |       case FIT_FLOAT:  | 
897  | 0  |       case FIT_RGBF:  | 
898  | 0  |       case FIT_RGBAF:  | 
899  | 0  |       { | 
900  | 0  |         FIBITMAP *dst = RotateAny(dib, angle, bkcolor);  | 
901  | 0  |         if(!dst) throw(1);  | 
902  |  |  | 
903  |  |         // copy metadata from src to dst  | 
904  | 0  |         FreeImage_CloneMetadata(dst, dib);  | 
905  |  | 
  | 
906  | 0  |         return dst;  | 
907  | 0  |       }  | 
908  | 0  |       break;  | 
909  | 0  |     }  | 
910  |  | 
  | 
911  | 0  |   } catch(int) { | 
912  | 0  |     return NULL;  | 
913  | 0  |   }  | 
914  |  |  | 
915  | 0  |   return NULL;  | 
916  | 0  | }  | 
917  |  |  |