Alien-FreeImage

 view release on metacpan or  search on metacpan

src/Source/FreeImageToolkit/ClassicRotate.cpp  view on Meta::CPAN

//
// Design and implementation by
// - Hervé Drolon (drolon@infonie.fr)
// - Thorsten Radde (support@IdealSoftware.com)
// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
//
// This file is part of FreeImage 3
//
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
// THIS DISCLAIMER.
//
// Use at your own risk!
// ==========================================================

/* 
 ============================================================
 References : 
 [1] Paeth A., A Fast Algorithm for General Raster Rotation. 
 Graphics Gems, p. 179, Andrew Glassner editor, Academic Press, 1990. 
 [2] Yariv E., High quality image rotation (rotate by shear). 
 [Online] http://www.codeproject.com/bitmap/rotatebyshear.asp
 [3] Treskunov A., Fast and high quality true-color bitmap rotation function.
 [Online] http://anton.treskunov.net/Software/doc/fast_and_high_quality_true_color_bitmap_rotation_function.html
 ============================================================
*/

#include "FreeImage.h"
#include "Utilities.h"

#define RBLOCK		64	// image blocks of RBLOCK*RBLOCK pixels

// --------------------------------------------------------------------------

/**
Skews a row horizontally (with filtered weights). 
Limited to 45 degree skewing only. Filters two adjacent pixels.
Parameter T can be BYTE, WORD of float. 
@param src Pointer to source image to rotate
@param dst Pointer to destination image
@param row Row index
@param iOffset Skew offset
@param dWeight Relative weight of right pixel
@param bkcolor Background color
*/
template <class T> void 
HorizontalSkewT(FIBITMAP *src, FIBITMAP *dst, int row, int iOffset, double weight, const void *bkcolor = NULL) {
	int iXPos;

	const unsigned src_width  = FreeImage_GetWidth(src);
	const unsigned dst_width  = FreeImage_GetWidth(dst);

	T pxlSrc[4], pxlLeft[4], pxlOldLeft[4];	// 4 = 4*sizeof(T) max
	
	// background
	const T pxlBlack[4] = {0, 0, 0, 0 };
	const T *pxlBkg = static_cast<const T*>(bkcolor); // assume at least bytespp and 4*sizeof(T) max
	if(!pxlBkg) {
		// default background color is black
		pxlBkg = pxlBlack;
	}

	// calculate the number of bytes per pixel
	const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
	// calculate the number of samples per pixel
	const unsigned samples = bytespp / sizeof(T);

	BYTE *src_bits = FreeImage_GetScanLine(src, row);
	BYTE *dst_bits = FreeImage_GetScanLine(dst, row);

	// fill gap left of skew with background
	if(bkcolor) {
		for(int k = 0; k < iOffset; k++) {
			memcpy(&dst_bits[k * bytespp], bkcolor, bytespp);
		}
		AssignPixel((BYTE*)&pxlOldLeft[0], (BYTE*)bkcolor, bytespp);
	} else {
		if(iOffset > 0) {
			memset(dst_bits, 0, iOffset * bytespp);
		}		
		memset(&pxlOldLeft[0], 0, bytespp);
	}

	for(unsigned i = 0; i < src_width; i++) {
		// loop through row pixels
		AssignPixel((BYTE*)&pxlSrc[0], (BYTE*)src_bits, bytespp);
		// calculate weights
		for(unsigned j = 0; j < samples; j++) {
			pxlLeft[j] = static_cast<T>(pxlBkg[j] + (pxlSrc[j] - pxlBkg[j]) * weight + 0.5);
		}
		// check boundaries 
		iXPos = i + iOffset;
		if((iXPos >= 0) && (iXPos < (int)dst_width)) {
			// update left over on source
			for(unsigned j = 0; j < samples; j++) {
				pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);
			}
			AssignPixel((BYTE*)&dst_bits[iXPos*bytespp], (BYTE*)&pxlSrc[0], bytespp);
		}
		// save leftover for next pixel in scan
		AssignPixel((BYTE*)&pxlOldLeft[0], (BYTE*)&pxlLeft[0], bytespp);

		// next pixel in scan
		src_bits += bytespp;
	}			

	// go to rightmost point of skew
	iXPos = src_width + iOffset; 

	if((iXPos >= 0) && (iXPos < (int)dst_width)) {
		dst_bits = FreeImage_GetScanLine(dst, row) + iXPos * bytespp;

		// If still in image bounds, put leftovers there
		AssignPixel((BYTE*)dst_bits, (BYTE*)&pxlOldLeft[0], bytespp);

		// clear to the right of the skewed line with background
		dst_bits += bytespp;
		if(bkcolor) {
			for(unsigned i = 0; i < dst_width - iXPos - 1; i++) {
				memcpy(&dst_bits[i * bytespp], bkcolor, bytespp);
			}
		} else {
			memset(dst_bits, 0, bytespp * (dst_width - iXPos - 1));
		}

	}
}

/**
Skews a row horizontally (with filtered weights). 
Limited to 45 degree skewing only. Filters two adjacent pixels.
@param src Pointer to source image to rotate
@param dst Pointer to destination image
@param row Row index
@param iOffset Skew offset
@param dWeight Relative weight of right pixel
@param bkcolor Background color
*/
static void 
HorizontalSkew(FIBITMAP *src, FIBITMAP *dst, int row, int iOffset, double dWeight, const void *bkcolor) {
	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);

	switch(image_type) {
		case FIT_BITMAP:
			switch(FreeImage_GetBPP(src)) {
				case 8:
				case 24:
				case 32:
					HorizontalSkewT<BYTE>(src, dst, row, iOffset, dWeight, bkcolor);
				break;
			}
			break;
		case FIT_UINT16:
		case FIT_RGB16:
		case FIT_RGBA16:
			HorizontalSkewT<WORD>(src, dst, row, iOffset, dWeight, bkcolor);
			break;
		case FIT_FLOAT:
		case FIT_RGBF:
		case FIT_RGBAF:
			HorizontalSkewT<float>(src, dst, row, iOffset, dWeight, bkcolor);
			break;
	}
}

/**
Skews a column vertically (with filtered weights). 
Limited to 45 degree skewing only. Filters two adjacent pixels.
Parameter T can be BYTE, WORD of float. 
@param src Pointer to source image to rotate
@param dst Pointer to destination image
@param col Column index
@param iOffset Skew offset
@param dWeight Relative weight of upper pixel
@param bkcolor Background color
*/
template <class T> void 
VerticalSkewT(FIBITMAP *src, FIBITMAP *dst, int col, int iOffset, double weight, const void *bkcolor = NULL) {
	int iYPos;

	unsigned src_height = FreeImage_GetHeight(src);
	unsigned dst_height = FreeImage_GetHeight(dst);

	T pxlSrc[4], pxlLeft[4], pxlOldLeft[4];	// 4 = 4*sizeof(T) max

	// background
	const T pxlBlack[4] = {0, 0, 0, 0 };
	const T *pxlBkg = static_cast<const T*>(bkcolor); // assume at least bytespp and 4*sizeof(T) max
	if(!pxlBkg) {
		// default background color is black
		pxlBkg = pxlBlack;
	}

	// calculate the number of bytes per pixel
	const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
	// calculate the number of samples per pixel
	const unsigned samples = bytespp / sizeof(T);

	const unsigned src_pitch = FreeImage_GetPitch(src);
	const unsigned dst_pitch = FreeImage_GetPitch(dst);
	const unsigned index = col * bytespp;

	BYTE *src_bits = FreeImage_GetBits(src) + index;
	BYTE *dst_bits = FreeImage_GetBits(dst) + index;

	// fill gap above skew with background
	if(bkcolor) {
		for(int k = 0; k < iOffset; k++) {
			memcpy(dst_bits, bkcolor, bytespp);
			dst_bits += dst_pitch;
		}
		memcpy(&pxlOldLeft[0], bkcolor, bytespp);
	} else {
		for(int k = 0; k < iOffset; k++) {
			memset(dst_bits, 0, bytespp);
			dst_bits += dst_pitch;
		}
		memset(&pxlOldLeft[0], 0, bytespp);
	}

	for(unsigned i = 0; i < src_height; i++) {
		// loop through column pixels
		AssignPixel((BYTE*)(&pxlSrc[0]), src_bits, bytespp);
		// calculate weights
		for(unsigned j = 0; j < samples; j++) {
			pxlLeft[j] = static_cast<T>(pxlBkg[j] + (pxlSrc[j] - pxlBkg[j]) * weight + 0.5);
		}
		// check boundaries
		iYPos = i + iOffset;
		if((iYPos >= 0) && (iYPos < (int)dst_height)) {
			// update left over on source
			for(unsigned j = 0; j < samples; j++) {
				pxlSrc[j] = pxlSrc[j] - (pxlLeft[j] - pxlOldLeft[j]);
			}
			dst_bits = FreeImage_GetScanLine(dst, iYPos) + index;
			AssignPixel(dst_bits, (BYTE*)(&pxlSrc[0]), bytespp);
		}
		// save leftover for next pixel in scan
		AssignPixel((BYTE*)(&pxlOldLeft[0]), (BYTE*)(&pxlLeft[0]), bytespp);

		// next pixel in scan
		src_bits += src_pitch;
	}
	// go to bottom point of skew
	iYPos = src_height + iOffset;

	if((iYPos >= 0) && (iYPos < (int)dst_height)) {
		dst_bits = FreeImage_GetScanLine(dst, iYPos) + index;

		// if still in image bounds, put leftovers there				
		AssignPixel((BYTE*)(dst_bits), (BYTE*)(&pxlOldLeft[0]), bytespp);

		// clear below skewed line with background
		if(bkcolor) {
			while(++iYPos < (int)dst_height) {					
				dst_bits += dst_pitch;
				AssignPixel((BYTE*)(dst_bits), (BYTE*)(bkcolor), bytespp);
			}
		} else {
			while(++iYPos < (int)dst_height) {					
				dst_bits += dst_pitch;
				memset(dst_bits, 0, bytespp);
			}
		}
	}
}

/**
Skews a column vertically (with filtered weights). 
Limited to 45 degree skewing only. Filters two adjacent pixels.
@param src Pointer to source image to rotate
@param dst Pointer to destination image
@param col Column index
@param iOffset Skew offset
@param dWeight Relative weight of upper pixel
@param bkcolor Background color
*/
static void 
VerticalSkew(FIBITMAP *src, FIBITMAP *dst, int col, int iOffset, double dWeight, const void *bkcolor) {
	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);

	switch(image_type) {
		case FIT_BITMAP:
			switch(FreeImage_GetBPP(src)) {
				case 8:
				case 24:
				case 32:
					VerticalSkewT<BYTE>(src, dst, col, iOffset, dWeight, bkcolor);
					break;
			}
			break;
			case FIT_UINT16:
			case FIT_RGB16:
			case FIT_RGBA16:
				VerticalSkewT<WORD>(src, dst, col, iOffset, dWeight, bkcolor);
				break;
			case FIT_FLOAT:
			case FIT_RGBF:
			case FIT_RGBAF:
				VerticalSkewT<float>(src, dst, col, iOffset, dWeight, bkcolor);
				break;
	}
} 

/**
Rotates an image by 90 degrees (counter clockwise). 
Precise rotation, no filters required.<br>
Code adapted from CxImage (http://www.xdp.it/cximage.htm)
@param src Pointer to source image to rotate
@return Returns a pointer to a newly allocated rotated image if successful, returns NULL otherwise
*/
static FIBITMAP* 
Rotate90(FIBITMAP *src) {

src/Source/FreeImageToolkit/ClassicRotate.cpp  view on Meta::CPAN


		return dst;
	}
}

// ==========================================================

FIBITMAP *DLL_CALLCONV 
FreeImage_Rotate(FIBITMAP *dib, double angle, const void *bkcolor) {
	if(!FreeImage_HasPixels(dib)) return NULL;

	if(0 == angle) {
		return FreeImage_Clone(dib);
	}
	// DIB are stored upside down ...
	angle *= -1;

	try {
		unsigned bpp = FreeImage_GetBPP(dib);
		FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
		
		switch(image_type) {
			case FIT_BITMAP:
				if(bpp == 1) {
					// only rotate for integer multiples of 90 degree
					if(fmod(angle, 90) != 0)
						return NULL;

					// perform the rotation
					FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
					if(!dst) throw(1);

					// build a greyscale palette
					RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
					if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) {
						dst_pal[0].rgbRed = dst_pal[0].rgbGreen = dst_pal[0].rgbBlue = 0;
						dst_pal[1].rgbRed = dst_pal[1].rgbGreen = dst_pal[1].rgbBlue = 255;			
					} else {
						dst_pal[0].rgbRed = dst_pal[0].rgbGreen = dst_pal[0].rgbBlue = 255;
						dst_pal[1].rgbRed = dst_pal[1].rgbGreen = dst_pal[1].rgbBlue = 0;			
					}

					// copy metadata from src to dst
					FreeImage_CloneMetadata(dst, dib);

					return dst;
				}
				else if((bpp == 8) || (bpp == 24) || (bpp == 32)) {
					FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
					if(!dst) throw(1);
					
					if(bpp == 8) {
						// copy original palette to rotated bitmap
						RGBQUAD *src_pal = FreeImage_GetPalette(dib);
						RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
						memcpy(&dst_pal[0], &src_pal[0], 256 * sizeof(RGBQUAD));

						// copy transparency table 
						FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));

						// copy background color 
						RGBQUAD bkcolor; 
						if( FreeImage_GetBackgroundColor(dib, &bkcolor) ) {
							FreeImage_SetBackgroundColor(dst, &bkcolor); 
						}

					}

					// copy metadata from src to dst
					FreeImage_CloneMetadata(dst, dib);

					return dst;
				}
				break;
			case FIT_UINT16:
			case FIT_RGB16:
			case FIT_RGBA16:
			case FIT_FLOAT:
			case FIT_RGBF:
			case FIT_RGBAF:
			{
				FIBITMAP *dst = RotateAny(dib, angle, bkcolor);
				if(!dst) throw(1);

				// copy metadata from src to dst
				FreeImage_CloneMetadata(dst, dib);

				return dst;
			}
			break;
		}

	} catch(int) {
		return NULL;
	}

	return NULL;
}



( run in 0.499 second using v1.01-cache-2.11-cpan-0d23b851a93 )