Coverage Report

Created: 2023-12-08 06:53

/src/freeimage-svn/FreeImage/trunk/Source/FreeImageToolkit/ClassicRotate.cpp
Line
Count
Source (jump to first uncovered line)
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