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

https://bitbucket.org/cabalistic/ogredeps/ · C++ · 917 lines · 548 code · 126 blank · 243 comment · 154 complexity · f9083b42b2a1167a794c0ad964bb49a5 MD5 · raw file

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