X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=utilities%2FCxImage%2Fximatran.cpp;h=dd635cf1185368b1a81311688bd02b540c21916f;hb=b07ea516b22fea80f721f2f984c4ae9e3c33ef0a;hp=9f136a05d69ff48d8fc91237e5258727863e9309;hpb=19d5db17f1c0e98cf84a6cb83643404a550a12a4;p=clitk.git diff --git a/utilities/CxImage/ximatran.cpp b/utilities/CxImage/ximatran.cpp index 9f136a0..dd635cf 100644 --- a/utilities/CxImage/ximatran.cpp +++ b/utilities/CxImage/ximatran.cpp @@ -1,2624 +1,2624 @@ -// xImaTran.cpp : Transformation functions -/* 07/08/2001 v1.00 - Davide Pizzolato - www.xdp.it - * CxImage version 6.0.0 02/Feb/2008 - */ - -#include "ximage.h" -#include "ximath.h" - -#if CXIMAGE_SUPPORT_BASICTRANSFORMATIONS -//////////////////////////////////////////////////////////////////////////////// -bool CxImage::GrayScale() -{ - if (!pDib) return false; - if (head.biBitCount<=8){ - RGBQUAD* ppal=GetPalette(); - int gray; - //converts the colors to gray, use the blue channel only - for(DWORD i=0;i= 0) info.nBkgndIndex = ppal[info.nBkgndIndex].rgbBlue; - //create a "real" 8 bit gray scale image - if (head.biBitCount==8){ - BYTE *img=info.pImage; - for(DWORD i=0;i> 1]&((BYTE)0x0F<> pos)].rgbBlue; - } else { - BYTE pos = (BYTE)(7-x%8); - iDst[x]= ppal[(BYTE)((iSrc[x >> 3]&((BYTE)0x01<> pos)].rgbBlue; - } - } - } - Transfer(ima); - } - } else { //from RGB to 8 bit gray scale - BYTE *iSrc=info.pImage; - CxImage ima; - ima.CopyInfo(*this); - if (!ima.Create(head.biWidth,head.biHeight,8,info.dwType)) return false; - ima.SetGrayPalette(); -#if CXIMAGE_SUPPORT_SELECTION - ima.SelectionCopy(*this); -#endif //CXIMAGE_SUPPORT_SELECTION -#if CXIMAGE_SUPPORT_ALPHA - ima.AlphaCopy(*this); -#endif //CXIMAGE_SUPPORT_ALPHA - BYTE *img=ima.GetBits(); - long l8=ima.GetEffWidth(); - long l=head.biWidth * 3; - for(long y=0; y < head.biHeight; y++) { - for(long x=0,x8=0; x < l; x+=3,x8++) { - img[x8+y*l8]=(BYTE)RGB2GRAY(*(iSrc+x+2),*(iSrc+x+1),*(iSrc+x+0)); - } - iSrc+=info.dwEffWidth; - } - Transfer(ima); - } - return true; -} -//////////////////////////////////////////////////////////////////////////////// -/** - * \sa Mirror - * \author [qhbo] - */ -bool CxImage::Flip(bool bFlipSelection, bool bFlipAlpha) -{ - if (!pDib) return false; - - BYTE *buff = (BYTE*)malloc(info.dwEffWidth); - if (!buff) return false; - - BYTE *iSrc,*iDst; - iSrc = GetBits(head.biHeight-1); - iDst = GetBits(0); - for (long i=0; i<(head.biHeight/2); ++i) - { - memcpy(buff, iSrc, info.dwEffWidth); - memcpy(iSrc, iDst, info.dwEffWidth); - memcpy(iDst, buff, info.dwEffWidth); - iSrc-=info.dwEffWidth; - iDst+=info.dwEffWidth; - } - - free(buff); - - if (bFlipSelection){ -#if CXIMAGE_SUPPORT_SELECTION - SelectionFlip(); -#endif //CXIMAGE_SUPPORT_SELECTION - } - - if (bFlipAlpha){ -#if CXIMAGE_SUPPORT_ALPHA - AlphaFlip(); -#endif //CXIMAGE_SUPPORT_ALPHA - } - - return true; -} -//////////////////////////////////////////////////////////////////////////////// -/** - * \sa Flip - */ -bool CxImage::Mirror(bool bMirrorSelection, bool bMirrorAlpha) -{ - if (!pDib) return false; - - CxImage* imatmp = new CxImage(*this,false,true,true); - if (!imatmp) return false; - if (!imatmp->IsValid()){ - delete imatmp; - return false; - } - - BYTE *iSrc,*iDst; - long wdt=(head.biWidth-1) * (head.biBitCount==24 ? 3:1); - iSrc=info.pImage + wdt; - iDst=imatmp->info.pImage; - long x,y; - switch (head.biBitCount){ - case 24: - for(y=0; y < head.biHeight; y++){ - for(x=0; x <= wdt; x+=3){ - *(iDst+x)=*(iSrc-x); - *(iDst+x+1)=*(iSrc-x+1); - *(iDst+x+2)=*(iSrc-x+2); - } - iSrc+=info.dwEffWidth; - iDst+=info.dwEffWidth; - } - break; - case 8: - for(y=0; y < head.biHeight; y++){ - for(x=0; x <= wdt; x++) - *(iDst+x)=*(iSrc-x); - iSrc+=info.dwEffWidth; - iDst+=info.dwEffWidth; - } - break; - default: - for(y=0; y < head.biHeight; y++){ - for(x=0; x <= wdt; x++) - imatmp->SetPixelIndex(x,y,GetPixelIndex(wdt-x,y)); - } - } - - if (bMirrorSelection){ -#if CXIMAGE_SUPPORT_SELECTION - imatmp->SelectionMirror(); -#endif //CXIMAGE_SUPPORT_SELECTION - } - - if (bMirrorAlpha){ -#if CXIMAGE_SUPPORT_ALPHA - imatmp->AlphaMirror(); -#endif //CXIMAGE_SUPPORT_ALPHA - } - - Transfer(*imatmp); - delete imatmp; - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -#define RBLOCK 64 - -//////////////////////////////////////////////////////////////////////////////// -bool CxImage::RotateLeft(CxImage* iDst) -{ - if (!pDib) return false; - - long newWidth = GetHeight(); - long newHeight = GetWidth(); - - CxImage imgDest; - imgDest.CopyInfo(*this); - imgDest.Create(newWidth,newHeight,GetBpp(),GetType()); - imgDest.SetPalette(GetPalette()); - -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()) imgDest.AlphaCreate(); -#endif - -#if CXIMAGE_SUPPORT_SELECTION - if (SelectionIsValid()) imgDest.SelectionCreate(); -#endif - - long x,x2,y,dlineup; - - // Speedy rotate for BW images - if (head.biBitCount == 1) { - - BYTE *sbits, *dbits, *dbitsmax, bitpos, *nrow,*srcdisp; - ldiv_t div_r; - - BYTE *bsrc = GetBits(), *bdest = imgDest.GetBits(); - dbitsmax = bdest + imgDest.head.biSizeImage - 1; - dlineup = 8 * imgDest.info.dwEffWidth - imgDest.head.biWidth; - - imgDest.Clear(0); - for (y = 0; y < head.biHeight; y++) { - // Figure out the Column we are going to be copying to - div_r = ldiv(y + dlineup, (long)8); - // set bit pos of src column byte - bitpos = (BYTE)(1 << div_r.rem); - srcdisp = bsrc + y * info.dwEffWidth; - for (x = 0; x < (long)info.dwEffWidth; x++) { - // Get Source Bits - sbits = srcdisp + x; - // Get destination column - nrow = bdest + (x * 8) * imgDest.info.dwEffWidth + imgDest.info.dwEffWidth - 1 - div_r.quot; - for (long z = 0; z < 8; z++) { - // Get Destination Byte - dbits = nrow + z * imgDest.info.dwEffWidth; - if ((dbits < bdest) || (dbits > dbitsmax)) break; - if (*sbits & (128 >> z)) *dbits |= bitpos; - } - } - }//for y - -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()) { - for (x = 0; x < newWidth; x++){ - x2=newWidth-x-1; - for (y = 0; y < newHeight; y++){ - imgDest.AlphaSet(x,y,BlindAlphaGet(y, x2)); - }//for y - }//for x - } -#endif //CXIMAGE_SUPPORT_ALPHA - -#if CXIMAGE_SUPPORT_SELECTION - if (SelectionIsValid()) { - imgDest.info.rSelectionBox.left = newWidth-info.rSelectionBox.top; - imgDest.info.rSelectionBox.right = newWidth-info.rSelectionBox.bottom; - imgDest.info.rSelectionBox.bottom = info.rSelectionBox.left; - imgDest.info.rSelectionBox.top = info.rSelectionBox.right; - for (x = 0; x < newWidth; x++){ - x2=newWidth-x-1; - for (y = 0; y < newHeight; y++){ - imgDest.SelectionSet(x,y,BlindSelectionGet(y, x2)); - }//for y - }//for x - } -#endif //CXIMAGE_SUPPORT_SELECTION - - } else { - //anything other than BW: - //bd, 10. 2004: This optimized version of rotation rotates image by smaller blocks. It is quite - //a bit faster than obvious algorithm, because it produces much less CPU cache misses. - //This optimization can be tuned by changing block size (RBLOCK). 96 is good value for current - //CPUs (tested on Athlon XP and Celeron D). Larger value (if CPU has enough cache) will increase - //speed somehow, but once you drop out of CPU's cache, things will slow down drastically. - //For older CPUs with less cache, lower value would yield better results. - - BYTE *srcPtr, *dstPtr; //source and destionation for 24-bit version - int xs, ys; //x-segment and y-segment - for (xs = 0; xs < newWidth; xs+=RBLOCK) { //for all image blocks of RBLOCK*RBLOCK pixels - for (ys = 0; ys < newHeight; ys+=RBLOCK) { - if (head.biBitCount==24) { - //RGB24 optimized pixel access: - for (x = xs; x < min(newWidth, xs+RBLOCK); x++){ //do rotation - info.nProgress = (long)(100*x/newWidth); - x2=newWidth-x-1; - dstPtr = (BYTE*) imgDest.BlindGetPixelPointer(x,ys); - srcPtr = (BYTE*) BlindGetPixelPointer(ys, x2); - for (y = ys; y < min(newHeight, ys+RBLOCK); y++){ - //imgDest.SetPixelColor(x, y, GetPixelColor(y, x2)); - *(dstPtr) = *(srcPtr); - *(dstPtr+1) = *(srcPtr+1); - *(dstPtr+2) = *(srcPtr+2); - srcPtr += 3; - dstPtr += imgDest.info.dwEffWidth; - }//for y - }//for x - } else { - //anything else than 24bpp (and 1bpp): palette - for (x = xs; x < min(newWidth, xs+RBLOCK); x++){ - info.nProgress = (long)(100*x/newWidth); // - x2=newWidth-x-1; - for (y = ys; y < min(newHeight, ys+RBLOCK); y++){ - imgDest.SetPixelIndex(x, y, BlindGetPixelIndex(y, x2)); - }//for y - }//for x - }//if (version selection) -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()) { - for (x = xs; x < min(newWidth, xs+RBLOCK); x++){ - x2=newWidth-x-1; - for (y = ys; y < min(newHeight, ys+RBLOCK); y++){ - imgDest.AlphaSet(x,y,BlindAlphaGet(y, x2)); - }//for y - }//for x - }//if (alpha channel) -#endif //CXIMAGE_SUPPORT_ALPHA - -#if CXIMAGE_SUPPORT_SELECTION - if (SelectionIsValid()) { - imgDest.info.rSelectionBox.left = newWidth-info.rSelectionBox.top; - imgDest.info.rSelectionBox.right = newWidth-info.rSelectionBox.bottom; - imgDest.info.rSelectionBox.bottom = info.rSelectionBox.left; - imgDest.info.rSelectionBox.top = info.rSelectionBox.right; - for (x = xs; x < min(newWidth, xs+RBLOCK); x++){ - x2=newWidth-x-1; - for (y = ys; y < min(newHeight, ys+RBLOCK); y++){ - imgDest.SelectionSet(x,y,BlindSelectionGet(y, x2)); - }//for y - }//for x - }//if (selection) -#endif //CXIMAGE_SUPPORT_SELECTION - }//for ys - }//for xs - }//if - - //select the destination - if (iDst) iDst->Transfer(imgDest); - else Transfer(imgDest); - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -bool CxImage::RotateRight(CxImage* iDst) -{ - if (!pDib) return false; - - long newWidth = GetHeight(); - long newHeight = GetWidth(); - - CxImage imgDest; - imgDest.CopyInfo(*this); - imgDest.Create(newWidth,newHeight,GetBpp(),GetType()); - imgDest.SetPalette(GetPalette()); - -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()) imgDest.AlphaCreate(); -#endif - -#if CXIMAGE_SUPPORT_SELECTION - if (SelectionIsValid()) imgDest.SelectionCreate(); -#endif - - long x,y,y2; - // Speedy rotate for BW images - if (head.biBitCount == 1) { - - BYTE *sbits, *dbits, *dbitsmax, bitpos, *nrow,*srcdisp; - ldiv_t div_r; - - BYTE *bsrc = GetBits(), *bdest = imgDest.GetBits(); - dbitsmax = bdest + imgDest.head.biSizeImage - 1; - - imgDest.Clear(0); - for (y = 0; y < head.biHeight; y++) { - // Figure out the Column we are going to be copying to - div_r = ldiv(y, (long)8); - // set bit pos of src column byte - bitpos = (BYTE)(128 >> div_r.rem); - srcdisp = bsrc + y * info.dwEffWidth; - for (x = 0; x < (long)info.dwEffWidth; x++) { - // Get Source Bits - sbits = srcdisp + x; - // Get destination column - nrow = bdest + (imgDest.head.biHeight-1-(x*8)) * imgDest.info.dwEffWidth + div_r.quot; - for (long z = 0; z < 8; z++) { - // Get Destination Byte - dbits = nrow - z * imgDest.info.dwEffWidth; - if ((dbits < bdest) || (dbits > dbitsmax)) break; - if (*sbits & (128 >> z)) *dbits |= bitpos; - } - } - } - -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()){ - for (y = 0; y < newHeight; y++){ - y2=newHeight-y-1; - for (x = 0; x < newWidth; x++){ - imgDest.AlphaSet(x,y,BlindAlphaGet(y2, x)); - } - } - } -#endif //CXIMAGE_SUPPORT_ALPHA - -#if CXIMAGE_SUPPORT_SELECTION - if (SelectionIsValid()){ - imgDest.info.rSelectionBox.left = info.rSelectionBox.bottom; - imgDest.info.rSelectionBox.right = info.rSelectionBox.top; - imgDest.info.rSelectionBox.bottom = newHeight-info.rSelectionBox.right; - imgDest.info.rSelectionBox.top = newHeight-info.rSelectionBox.left; - for (y = 0; y < newHeight; y++){ - y2=newHeight-y-1; - for (x = 0; x < newWidth; x++){ - imgDest.SelectionSet(x,y,BlindSelectionGet(y2, x)); - } - } - } -#endif //CXIMAGE_SUPPORT_SELECTION - - } else { - //anything else but BW - BYTE *srcPtr, *dstPtr; //source and destionation for 24-bit version - int xs, ys; //x-segment and y-segment - for (xs = 0; xs < newWidth; xs+=RBLOCK) { - for (ys = 0; ys < newHeight; ys+=RBLOCK) { - if (head.biBitCount==24) { - //RGB24 optimized pixel access: - for (y = ys; y < min(newHeight, ys+RBLOCK); y++){ - info.nProgress = (long)(100*y/newHeight); // - y2=newHeight-y-1; - dstPtr = (BYTE*) imgDest.BlindGetPixelPointer(xs,y); - srcPtr = (BYTE*) BlindGetPixelPointer(y2, xs); - for (x = xs; x < min(newWidth, xs+RBLOCK); x++){ - //imgDest.SetPixelColor(x, y, GetPixelColor(y2, x)); - *(dstPtr) = *(srcPtr); - *(dstPtr+1) = *(srcPtr+1); - *(dstPtr+2) = *(srcPtr+2); - dstPtr += 3; - srcPtr += info.dwEffWidth; - }//for x - }//for y - } else { - //anything else than BW & RGB24: palette - for (y = ys; y < min(newHeight, ys+RBLOCK); y++){ - info.nProgress = (long)(100*y/newHeight); // - y2=newHeight-y-1; - for (x = xs; x < min(newWidth, xs+RBLOCK); x++){ - imgDest.SetPixelIndex(x, y, BlindGetPixelIndex(y2, x)); - }//for x - }//for y - }//if -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()){ - for (y = ys; y < min(newHeight, ys+RBLOCK); y++){ - y2=newHeight-y-1; - for (x = xs; x < min(newWidth, xs+RBLOCK); x++){ - imgDest.AlphaSet(x,y,BlindAlphaGet(y2, x)); - }//for x - }//for y - }//if (has alpha) -#endif //CXIMAGE_SUPPORT_ALPHA - -#if CXIMAGE_SUPPORT_SELECTION - if (SelectionIsValid()){ - imgDest.info.rSelectionBox.left = info.rSelectionBox.bottom; - imgDest.info.rSelectionBox.right = info.rSelectionBox.top; - imgDest.info.rSelectionBox.bottom = newHeight-info.rSelectionBox.right; - imgDest.info.rSelectionBox.top = newHeight-info.rSelectionBox.left; - for (y = ys; y < min(newHeight, ys+RBLOCK); y++){ - y2=newHeight-y-1; - for (x = xs; x < min(newWidth, xs+RBLOCK); x++){ - imgDest.SelectionSet(x,y,BlindSelectionGet(y2, x)); - }//for x - }//for y - }//if (has alpha) -#endif //CXIMAGE_SUPPORT_SELECTION - }//for ys - }//for xs - }//if - - //select the destination - if (iDst) iDst->Transfer(imgDest); - else Transfer(imgDest); - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -bool CxImage::Negative() -{ - if (!pDib) return false; - - if (head.biBitCount<=8){ - if (IsGrayScale()){ //GRAYSCALE, selection - if (pSelection){ - for(long y=info.rSelectionBox.bottom; y invert transparent color too - info.nBkgndColor.rgbBlue = (BYTE)(255-info.nBkgndColor.rgbBlue); - info.nBkgndColor.rgbGreen = (BYTE)(255-info.nBkgndColor.rgbGreen); - info.nBkgndColor.rgbRed = (BYTE)(255-info.nBkgndColor.rgbRed); - } - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -#endif //CXIMAGE_SUPPORT_BASICTRANSFORMATIONS -//////////////////////////////////////////////////////////////////////////////// -#if CXIMAGE_SUPPORT_TRANSFORMATION -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -bool CxImage::Rotate(float angle, CxImage* iDst) -{ - if (!pDib) return false; - - // Copyright (c) 1996-1998 Ulrich von Zadow - - // Negative the angle, because the y-axis is negative. - double ang = -angle*acos((float)0)/90; - int newWidth, newHeight; - int nWidth = GetWidth(); - int nHeight= GetHeight(); - double cos_angle = cos(ang); - double sin_angle = sin(ang); - - // Calculate the size of the new bitmap - POINT p1={0,0}; - POINT p2={nWidth,0}; - POINT p3={0,nHeight}; - POINT p4={nWidth,nHeight}; - CxPoint2 newP1,newP2,newP3,newP4, leftTop, rightTop, leftBottom, rightBottom; - - newP1.x = (float)p1.x; - newP1.y = (float)p1.y; - newP2.x = (float)(p2.x*cos_angle - p2.y*sin_angle); - newP2.y = (float)(p2.x*sin_angle + p2.y*cos_angle); - newP3.x = (float)(p3.x*cos_angle - p3.y*sin_angle); - newP3.y = (float)(p3.x*sin_angle + p3.y*cos_angle); - newP4.x = (float)(p4.x*cos_angle - p4.y*sin_angle); - newP4.y = (float)(p4.x*sin_angle + p4.y*cos_angle); - - leftTop.x = min(min(newP1.x,newP2.x),min(newP3.x,newP4.x)); - leftTop.y = min(min(newP1.y,newP2.y),min(newP3.y,newP4.y)); - rightBottom.x = max(max(newP1.x,newP2.x),max(newP3.x,newP4.x)); - rightBottom.y = max(max(newP1.y,newP2.y),max(newP3.y,newP4.y)); - leftBottom.x = leftTop.x; - leftBottom.y = rightBottom.y; - rightTop.x = rightBottom.x; - rightTop.y = leftTop.y; - - newWidth = (int) floor(0.5f + rightTop.x - leftTop.x); - newHeight= (int) floor(0.5f + leftBottom.y - leftTop.y); - CxImage imgDest; - imgDest.CopyInfo(*this); - imgDest.Create(newWidth,newHeight,GetBpp(),GetType()); - imgDest.SetPalette(GetPalette()); - -#if CXIMAGE_SUPPORT_ALPHA - if(AlphaIsValid()) //MTA: Fix for rotation problem when the image has an alpha channel - { - imgDest.AlphaCreate(); - imgDest.AlphaClear(); - } -#endif //CXIMAGE_SUPPORT_ALPHA - - int x,y,newX,newY,oldX,oldY; - - if (head.biClrUsed==0){ //RGB - for (y = (int)leftTop.y, newY = 0; y<=(int)leftBottom.y; y++,newY++){ - info.nProgress = (long)(100*newY/newHeight); - if (info.nEscape) break; - for (x = (int)leftTop.x, newX = 0; x<=(int)rightTop.x; x++,newX++){ - oldX = (long)(x*cos_angle + y*sin_angle + 0.5); - oldY = (long)(y*cos_angle - x*sin_angle + 0.5); - imgDest.SetPixelColor(newX,newY,GetPixelColor(oldX,oldY)); -#if CXIMAGE_SUPPORT_ALPHA - imgDest.AlphaSet(newX,newY,AlphaGet(oldX,oldY)); //MTA: copy the alpha value -#endif //CXIMAGE_SUPPORT_ALPHA - } - } - } else { //PALETTE - for (y = (int)leftTop.y, newY = 0; y<=(int)leftBottom.y; y++,newY++){ - info.nProgress = (long)(100*newY/newHeight); - if (info.nEscape) break; - for (x = (int)leftTop.x, newX = 0; x<=(int)rightTop.x; x++,newX++){ - oldX = (long)(x*cos_angle + y*sin_angle + 0.5); - oldY = (long)(y*cos_angle - x*sin_angle + 0.5); - imgDest.SetPixelIndex(newX,newY,GetPixelIndex(oldX,oldY)); -#if CXIMAGE_SUPPORT_ALPHA - imgDest.AlphaSet(newX,newY,AlphaGet(oldX,oldY)); //MTA: copy the alpha value -#endif //CXIMAGE_SUPPORT_ALPHA - } - } - } - //select the destination - if (iDst) iDst->Transfer(imgDest); - else Transfer(imgDest); - - return true; -} -//////////////////////////////////////////////////////////////////////////////// -/** - * Rotates image around it's center. - * Method can use interpolation with paletted images, but does not change pallete, so results vary. - * (If you have only four colours in a palette, there's not much room for interpolation.) - * - * \param angle - angle in degrees (positive values rotate clockwise) - * \param *iDst - destination image (if null, this image is changed) - * \param inMethod - interpolation method used - * (IM_NEAREST_NEIGHBOUR produces aliasing (fast), IM_BILINEAR softens picture a bit (slower) - * IM_SHARPBICUBIC is slower and produces some halos...) - * \param ofMethod - overflow method (how to choose colour of pixels that have no source) - * \param replColor - replacement colour to use (OM_COLOR, OM_BACKGROUND with no background colour...) - * \param optimizeRightAngles - call faster methods for 90, 180, and 270 degree rotations. Faster methods - * are called for angles, where error (in location of corner pixels) is less - * than 0.25 pixels. - * \param bKeepOriginalSize - rotates the image without resizing. - * - * \author ***bd*** 2.2004 - */ -bool CxImage::Rotate2(float angle, - CxImage *iDst, - InterpolationMethod inMethod, - OverflowMethod ofMethod, - RGBQUAD *replColor, - bool const optimizeRightAngles, - bool const bKeepOriginalSize) -{ - if (!pDib) return false; //no dib no go - - double ang = -angle*acos(0.0f)/90.0f; //convert angle to radians and invert (positive angle performs clockwise rotation) - float cos_angle = (float) cos(ang); //these two are needed later (to rotate) - float sin_angle = (float) sin(ang); - - //Calculate the size of the new bitmap (rotate corners of image) - CxPoint2 p[4]; //original corners of the image - p[0]=CxPoint2(-0.5f,-0.5f); - p[1]=CxPoint2(GetWidth()-0.5f,-0.5f); - p[2]=CxPoint2(-0.5f,GetHeight()-0.5f); - p[3]=CxPoint2(GetWidth()-0.5f,GetHeight()-0.5f); - CxPoint2 newp[4]; //rotated positions of corners - //(rotate corners) - if (bKeepOriginalSize){ - for (int i=0; i<4; i++) { - newp[i].x = p[i].x; - newp[i].y = p[i].y; - }//for - } else { - for (int i=0; i<4; i++) { - newp[i].x = (p[i].x*cos_angle - p[i].y*sin_angle); - newp[i].y = (p[i].x*sin_angle + p[i].y*cos_angle); - }//for i - - if (optimizeRightAngles) { - //For rotations of 90, -90 or 180 or 0 degrees, call faster routines - if (newp[3].Distance(CxPoint2(GetHeight()-0.5f, 0.5f-GetWidth())) < 0.25) - //rotation right for circa 90 degrees (diagonal pixels less than 0.25 pixel away from 90 degree rotation destination) - return RotateRight(iDst); - if (newp[3].Distance(CxPoint2(0.5f-GetHeight(), -0.5f+GetWidth())) < 0.25) - //rotation left for ~90 degrees - return RotateLeft(iDst); - if (newp[3].Distance(CxPoint2(0.5f-GetWidth(), 0.5f-GetHeight())) < 0.25) - //rotation left for ~180 degrees - return Rotate180(iDst); - if (newp[3].Distance(p[3]) < 0.25) { - //rotation not significant - if (iDst) iDst->Copy(*this); //copy image to iDst, if required - return true; //and we're done - }//if - }//if - }//if - - //(read new dimensions from location of corners) - float minx = (float) min(min(newp[0].x,newp[1].x),min(newp[2].x,newp[3].x)); - float miny = (float) min(min(newp[0].y,newp[1].y),min(newp[2].y,newp[3].y)); - float maxx = (float) max(max(newp[0].x,newp[1].x),max(newp[2].x,newp[3].x)); - float maxy = (float) max(max(newp[0].y,newp[1].y),max(newp[2].y,newp[3].y)); - int newWidth = (int) floor(maxx-minx+0.5f); - int newHeight= (int) floor(maxy-miny+0.5f); - float ssx=((maxx+minx)- ((float) newWidth-1))/2.0f; //start for x - float ssy=((maxy+miny)- ((float) newHeight-1))/2.0f; //start for y - - float newxcenteroffset = 0.5f * newWidth; - float newycenteroffset = 0.5f * newHeight; - if (bKeepOriginalSize){ - ssx -= 0.5f * GetWidth(); - ssy -= 0.5f * GetHeight(); - } - - //create destination image - CxImage imgDest; - imgDest.CopyInfo(*this); - imgDest.Create(newWidth,newHeight,GetBpp(),GetType()); - imgDest.SetPalette(GetPalette()); -#if CXIMAGE_SUPPORT_ALPHA - if(AlphaIsValid()) imgDest.AlphaCreate(); //MTA: Fix for rotation problem when the image has an alpha channel -#endif //CXIMAGE_SUPPORT_ALPHA - - RGBQUAD rgb; //pixel colour - RGBQUAD rc; - if (replColor!=0) - rc=*replColor; - else { - rc.rgbRed=255; rc.rgbGreen=255; rc.rgbBlue=255; rc.rgbReserved=0; - }//if - float x,y; //destination location (float, with proper offset) - float origx, origy; //origin location - int destx, desty; //destination location - - y=ssy; //initialize y - if (!IsIndexed()){ //RGB24 - //optimized RGB24 implementation (direct write to destination): - BYTE *pxptr; -#if CXIMAGE_SUPPORT_ALPHA - BYTE *pxptra=0; -#endif //CXIMAGE_SUPPORT_ALPHA - for (desty=0; destyTransfer(imgDest); - else Transfer(imgDest); - - return true; -} -//////////////////////////////////////////////////////////////////////////////// -bool CxImage::Rotate180(CxImage* iDst) -{ - if (!pDib) return false; - - long wid = GetWidth(); - long ht = GetHeight(); - - CxImage imgDest; - imgDest.CopyInfo(*this); - imgDest.Create(wid,ht,GetBpp(),GetType()); - imgDest.SetPalette(GetPalette()); - -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()) imgDest.AlphaCreate(); -#endif //CXIMAGE_SUPPORT_ALPHA - - long x,y,y2; - for (y = 0; y < ht; y++){ - info.nProgress = (long)(100*y/ht); // - y2=ht-y-1; - for (x = 0; x < wid; x++){ - if(head.biClrUsed==0)//RGB - imgDest.SetPixelColor(wid-x-1, y2, BlindGetPixelColor(x, y)); - else //PALETTE - imgDest.SetPixelIndex(wid-x-1, y2, BlindGetPixelIndex(x, y)); - -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()) imgDest.AlphaSet(wid-x-1, y2,BlindAlphaGet(x, y)); -#endif //CXIMAGE_SUPPORT_ALPHA - - } - } - - //select the destination - if (iDst) iDst->Transfer(imgDest); - else Transfer(imgDest); - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/** - * Resizes the image. mode can be 0 for slow (bilinear) method , - * 1 for fast (nearest pixel) method, or 2 for accurate (bicubic spline interpolation) method. - * The function is faster with 24 and 1 bpp images, slow for 4 bpp images and slowest for 8 bpp images. - */ -bool CxImage::Resample(long newx, long newy, int mode, CxImage* iDst) -{ - if (newx==0 || newy==0) return false; - - if (head.biWidth==newx && head.biHeight==newy){ - if (iDst) iDst->Copy(*this); - return true; - } - - float xScale, yScale, fX, fY; - xScale = (float)head.biWidth / (float)newx; - yScale = (float)head.biHeight / (float)newy; - - CxImage newImage; - newImage.CopyInfo(*this); - newImage.Create(newx,newy,head.biBitCount,GetType()); - newImage.SetPalette(GetPalette()); - if (!newImage.IsValid()){ - strcpy(info.szLastError,newImage.GetLastError()); - return false; - } - - switch (mode) { - case 1: // nearest pixel - { - for(long y=0; y=head.biHeight) yy = head.biHeight-1; - for(int n=-1; n<3; n++) { - r2 = r1 * KernelBSpline(b - (float)n); - xx = i_x+n; - if (xx<0) xx=0; - if (xx>=head.biWidth) xx=head.biWidth-1; - - if (head.biClrUsed){ - rgb = GetPixelColor(xx,yy); - } else { - iDst = info.pImage + yy*info.dwEffWidth + xx*3; - rgb.rgbBlue = *iDst++; - rgb.rgbGreen= *iDst++; - rgb.rgbRed = *iDst; - } - - rr += rgb.rgbRed * r2; - gg += rgb.rgbGreen * r2; - bb += rgb.rgbBlue * r2; - } - } - - if (head.biClrUsed) - newImage.SetPixelColor(x,y,RGB(rr,gg,bb)); - else { - iDst = newImage.info.pImage + y*newImage.info.dwEffWidth + x*3; - *iDst++ = (BYTE)bb; - *iDst++ = (BYTE)gg; - *iDst = (BYTE)rr; - } - - } - } - break; - } - default: // bilinear interpolation - if (!(head.biWidth>newx && head.biHeight>newy && head.biBitCount==24)) { - // (c) 1999 Steve McMahon (steve@dogma.demon.co.uk) - long ifX, ifY, ifX1, ifY1, xmax, ymax; - float ir1, ir2, ig1, ig2, ib1, ib2, dx, dy; - BYTE r,g,b; - RGBQUAD rgb1, rgb2, rgb3, rgb4; - xmax = head.biWidth-1; - ymax = head.biHeight-1; - for(long y=0; y - const long ACCURACY = 1000; - long i,j; // index for faValue - long x,y; // coordinates in source image - BYTE* pSource; - BYTE* pDest = newImage.info.pImage; - long* naAccu = new long[3 * newx + 3]; - long* naCarry = new long[3 * newx + 3]; - long* naTemp; - long nWeightX,nWeightY; - float fEndX; - long nScale = (long)(ACCURACY * xScale * yScale); - - memset(naAccu, 0, sizeof(long) * 3 * newx); - memset(naCarry, 0, sizeof(long) * 3 * newx); - - int u, v = 0; // coordinates in dest image - float fEndY = yScale - 1.0f; - for (y = 0; y < head.biHeight; y++){ - info.nProgress = (long)(100*y/head.biHeight); // - if (info.nEscape) break; - pSource = info.pImage + y * info.dwEffWidth; - u = i = 0; - fEndX = xScale - 1.0f; - if ((float)y < fEndY) { // complete source row goes into dest row - for (x = 0; x < head.biWidth; x++){ - if ((float)x < fEndX){ // complete source pixel goes into dest pixel - for (j = 0; j < 3; j++) naAccu[i + j] += (*pSource++) * ACCURACY; - } else { // source pixel is splitted for 2 dest pixels - nWeightX = (long)(((float)x - fEndX) * ACCURACY); - for (j = 0; j < 3; j++){ - naAccu[i] += (ACCURACY - nWeightX) * (*pSource); - naAccu[3 + i++] += nWeightX * (*pSource++); - } - fEndX += xScale; - u++; - } - } - } else { // source row is splitted for 2 dest rows - nWeightY = (long)(((float)y - fEndY) * ACCURACY); - for (x = 0; x < head.biWidth; x++){ - if ((float)x < fEndX){ // complete source pixel goes into 2 pixel - for (j = 0; j < 3; j++){ - naAccu[i + j] += ((ACCURACY - nWeightY) * (*pSource)); - naCarry[i + j] += nWeightY * (*pSource++); - } - } else { // source pixel is splitted for 4 dest pixels - nWeightX = (int)(((float)x - fEndX) * ACCURACY); - for (j = 0; j < 3; j++) { - naAccu[i] += ((ACCURACY - nWeightY) * (ACCURACY - nWeightX)) * (*pSource) / ACCURACY; - *pDest++ = (BYTE)(naAccu[i] / nScale); - naCarry[i] += (nWeightY * (ACCURACY - nWeightX) * (*pSource)) / ACCURACY; - naAccu[i + 3] += ((ACCURACY - nWeightY) * nWeightX * (*pSource)) / ACCURACY; - naCarry[i + 3] = (nWeightY * nWeightX * (*pSource)) / ACCURACY; - i++; - pSource++; - } - fEndX += xScale; - u++; - } - } - if (u < newx){ // possibly not completed due to rounding errors - for (j = 0; j < 3; j++) *pDest++ = (BYTE)(naAccu[i++] / nScale); - } - naTemp = naCarry; - naCarry = naAccu; - naAccu = naTemp; - memset(naCarry, 0, sizeof(int) * 3); // need only to set first pixel zero - pDest = newImage.info.pImage + (++v * newImage.info.dwEffWidth); - fEndY += yScale; - } - } - if (v < newy){ // possibly not completed due to rounding errors - for (i = 0; i < 3 * newx; i++) *pDest++ = (BYTE)(naAccu[i] / nScale); - } - delete [] naAccu; - delete [] naCarry; - } - } - -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()){ - newImage.AlphaCreate(); - for(long y=0; yTransfer(newImage); - else Transfer(newImage); - - return true; -} -//////////////////////////////////////////////////////////////////////////////// -/** - * New simpler resample. Adds new interpolation methods and simplifies code (using GetPixelColorInterpolated - * and GetAreaColorInterpolated). It also (unlike old method) interpolates alpha layer. - * - * \param newx, newy - size of resampled image - * \param inMethod - interpolation method to use (see comments at GetPixelColorInterpolated) - * If image size is being reduced, averaging is used instead (or simultaneously with) inMethod. - * \param ofMethod - what to replace outside pixels by (only significant for bordering pixels of enlarged image) - * \param iDst - pointer to destination CxImage or NULL. - * \param disableAveraging - force no averaging when shrinking images (Produces aliasing. - * You probably just want to leave this off...) - * - * \author ***bd*** 2.2004 - */ -bool CxImage::Resample2( - long newx, long newy, - InterpolationMethod const inMethod, - OverflowMethod const ofMethod, - CxImage* const iDst, - bool const disableAveraging) -{ - if (newx<=0 || newy<=0 || !pDib) return false; - - if (head.biWidth==newx && head.biHeight==newy) { - //image already correct size (just copy and return) - if (iDst) iDst->Copy(*this); - return true; - }//if - - //calculate scale of new image (less than 1 for enlarge) - float xScale, yScale; - xScale = (float)head.biWidth / (float)newx; - yScale = (float)head.biHeight / (float)newy; - - //create temporary destination image - CxImage newImage; - newImage.CopyInfo(*this); - newImage.Create(newx,newy,head.biBitCount,GetType()); - newImage.SetPalette(GetPalette()); - if (!newImage.IsValid()){ - strcpy(info.szLastError,newImage.GetLastError()); - return false; - } - - //and alpha channel if required -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()) newImage.AlphaCreate(); - BYTE *pxptra = 0; // destination alpha data -#endif - - float sX, sY; //source location - long dX,dY; //destination pixel (int value) - if ((xScale<=1 && yScale<=1) || disableAveraging) { - //image is being enlarged (or interpolation on demand) - if (!IsIndexed()) { - //RGB24 image (optimized version with direct writes) - RGBQUAD q; //pixel colour - BYTE *pxptr; //pointer to destination pixel - for(dY=0; dYTransfer(newImage); - else - Transfer(newImage); - return true; -} -//////////////////////////////////////////////////////////////////////////////// -/** - * Reduces the number of bits per pixel to nbit (1, 4 or 8). - * ppal points to a valid palette for the final image; if not supplied the function will use a standard palette. - * ppal is not necessary for reduction to 1 bpp. - */ -bool CxImage::DecreaseBpp(DWORD nbit, bool errordiffusion, RGBQUAD* ppal, DWORD clrimportant) -{ - if (!pDib) return false; - if (head.biBitCount < nbit){ - strcpy(info.szLastError,"DecreaseBpp: target BPP greater than source BPP"); - return false; - } - if (head.biBitCount == nbit){ - if (clrimportant==0) return true; - if (head.biClrImportant && (head.biClrImportant4) return false; - - CxImage tmp; - tmp.CopyInfo(*this); - tmp.Create(head.biWidth,head.biHeight,4,info.dwType); - tmp.SetPalette(GetPalette(),GetNumColors()); - if (!tmp.IsValid()){ - strcpy(info.szLastError,tmp.GetLastError()); - return false; - } - - -#if CXIMAGE_SUPPORT_SELECTION - tmp.SelectionCopy(*this); -#endif //CXIMAGE_SUPPORT_SELECTION - -#if CXIMAGE_SUPPORT_ALPHA - tmp.AlphaCopy(*this); -#endif //CXIMAGE_SUPPORT_ALPHA - - for (long y=0;y8) return false; - - CxImage tmp; - tmp.CopyInfo(*this); - tmp.Create(head.biWidth,head.biHeight,8,info.dwType); - tmp.SetPalette(GetPalette(),GetNumColors()); - if (!tmp.IsValid()){ - strcpy(info.szLastError,tmp.GetLastError()); - return false; - } - -#if CXIMAGE_SUPPORT_SELECTION - tmp.SelectionCopy(*this); -#endif //CXIMAGE_SUPPORT_SELECTION - -#if CXIMAGE_SUPPORT_ALPHA - tmp.AlphaCopy(*this); -#endif //CXIMAGE_SUPPORT_ALPHA - - for (long y=0;y24) return false; - - CxImage tmp; - tmp.CopyInfo(*this); - tmp.Create(head.biWidth,head.biHeight,24,info.dwType); - if (!tmp.IsValid()){ - strcpy(info.szLastError,tmp.GetLastError()); - return false; - } - - if (info.nBkgndIndex>=0) //translate transparency - tmp.info.nBkgndColor=GetPaletteColor((BYTE)info.nBkgndIndex); - -#if CXIMAGE_SUPPORT_SELECTION - tmp.SelectionCopy(*this); -#endif //CXIMAGE_SUPPORT_SELECTION - -#if CXIMAGE_SUPPORT_ALPHA - tmp.AlphaCopy(*this); - if (AlphaPaletteIsValid() && !AlphaIsValid()) tmp.AlphaCreate(); -#endif //CXIMAGE_SUPPORT_ALPHA - - for (long y=0;y 128) { - tmp.SetPixelIndex(x, y, 1); - error = level - 255; - } else { - tmp.SetPixelIndex(x, y, 0); - error = level; - } - - nlevel = GetPixelIndex(x + 1, y) + (error * 8) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + 1, y, level); - nlevel = GetPixelIndex(x + 2, y) + (error * 4) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + 2, y, level); - int i; - for (i = -2; i < 3; i++) { - switch (i) { - case -2: - coeff = 2; - break; - case -1: - coeff = 4; - break; - case 0: - coeff = 8; - break; - case 1: - coeff = 4; - break; - case 2: - coeff = 2; - break; - } - nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + i, y + 1, level); - } - } - } - break; - } - case 3: - { - //Stucki error diffusion (Thanks to Franco Gerevini) - int TotalCoeffSum = 42; - long error, nlevel, coeff=1; - BYTE level; - - for (long y = 0; y < head.biHeight; y++) { - info.nProgress = (long)(100 * y / head.biHeight); - if (info.nEscape) - break; - for (long x = 0; x < head.biWidth; x++) { - level = BlindGetPixelIndex(x, y); - if (level > 128) { - tmp.SetPixelIndex(x, y, 1); - error = level - 255; - } else { - tmp.SetPixelIndex(x, y, 0); - error = level; - } - - nlevel = GetPixelIndex(x + 1, y) + (error * 8) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + 1, y, level); - nlevel = GetPixelIndex(x + 2, y) + (error * 4) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + 2, y, level); - int i; - for (i = -2; i < 3; i++) { - switch (i) { - case -2: - coeff = 2; - break; - case -1: - coeff = 4; - break; - case 0: - coeff = 8; - break; - case 1: - coeff = 4; - break; - case 2: - coeff = 2; - break; - } - nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + i, y + 1, level); - } - for (i = -2; i < 3; i++) { - switch (i) { - case -2: - coeff = 1; - break; - case -1: - coeff = 2; - break; - case 0: - coeff = 4; - break; - case 1: - coeff = 2; - break; - case 2: - coeff = 1; - break; - } - nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + i, y + 2, level); - } - } - } - break; - } - case 4: - { - //Jarvis, Judice and Ninke error diffusion (Thanks to Franco Gerevini) - int TotalCoeffSum = 48; - long error, nlevel, coeff=1; - BYTE level; - - for (long y = 0; y < head.biHeight; y++) { - info.nProgress = (long)(100 * y / head.biHeight); - if (info.nEscape) - break; - for (long x = 0; x < head.biWidth; x++) { - level = BlindGetPixelIndex(x, y); - if (level > 128) { - tmp.SetPixelIndex(x, y, 1); - error = level - 255; - } else { - tmp.SetPixelIndex(x, y, 0); - error = level; - } - - nlevel = GetPixelIndex(x + 1, y) + (error * 7) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + 1, y, level); - nlevel = GetPixelIndex(x + 2, y) + (error * 5) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + 2, y, level); - int i; - for (i = -2; i < 3; i++) { - switch (i) { - case -2: - coeff = 3; - break; - case -1: - coeff = 5; - break; - case 0: - coeff = 7; - break; - case 1: - coeff = 5; - break; - case 2: - coeff = 3; - break; - } - nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + i, y + 1, level); - } - for (i = -2; i < 3; i++) { - switch (i) { - case -2: - coeff = 1; - break; - case -1: - coeff = 3; - break; - case 0: - coeff = 5; - break; - case 1: - coeff = 3; - break; - case 2: - coeff = 1; - break; - } - nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + i, y + 2, level); - } - } - } - break; - } - case 5: - { - //Sierra error diffusion (Thanks to Franco Gerevini) - int TotalCoeffSum = 32; - long error, nlevel, coeff=1; - BYTE level; - - for (long y = 0; y < head.biHeight; y++) { - info.nProgress = (long)(100 * y / head.biHeight); - if (info.nEscape) - break; - for (long x = 0; x < head.biWidth; x++) { - level = BlindGetPixelIndex(x, y); - if (level > 128) { - tmp.SetPixelIndex(x, y, 1); - error = level - 255; - } else { - tmp.SetPixelIndex(x, y, 0); - error = level; - } - - nlevel = GetPixelIndex(x + 1, y) + (error * 5) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + 1, y, level); - nlevel = GetPixelIndex(x + 2, y) + (error * 3) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + 2, y, level); - int i; - for (i = -2; i < 3; i++) { - switch (i) { - case -2: - coeff = 2; - break; - case -1: - coeff = 4; - break; - case 0: - coeff = 5; - break; - case 1: - coeff = 4; - break; - case 2: - coeff = 2; - break; - } - nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + i, y + 1, level); - } - for (i = -1; i < 2; i++) { - switch (i) { - case -1: - coeff = 2; - break; - case 0: - coeff = 3; - break; - case 1: - coeff = 2; - break; - } - nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(x + i, y + 2, level); - } - } - } - break; - } - case 6: - { - //Stevenson and Arce error diffusion (Thanks to Franco Gerevini) - int TotalCoeffSum = 200; - long error, nlevel; - BYTE level; - - for (long y = 0; y < head.biHeight; y++) { - info.nProgress = (long)(100 * y / head.biHeight); - if (info.nEscape) - break; - for (long x = 0; x < head.biWidth; x++) { - level = BlindGetPixelIndex(x, y); - if (level > 128) { - tmp.SetPixelIndex(x, y, 1); - error = level - 255; - } else { - tmp.SetPixelIndex(x, y, 0); - error = level; - } - - int tmp_index_x = x + 2; - int tmp_index_y = y; - int tmp_coeff = 32; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - - tmp_index_x = x - 3; - tmp_index_y = y + 1; - tmp_coeff = 12; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - - tmp_index_x = x - 1; - tmp_coeff = 26; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - - tmp_index_x = x + 1; - tmp_coeff = 30; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - - tmp_index_x = x + 3; - tmp_coeff = 16; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - - tmp_index_x = x - 2; - tmp_index_y = y + 2; - tmp_coeff = 12; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - - tmp_index_x = x; - tmp_coeff = 26; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - - tmp_index_x = x + 2; - tmp_coeff = 12; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - - tmp_index_x = x - 3; - tmp_index_y = y + 3; - tmp_coeff = 5; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - - tmp_index_x = x - 1; - tmp_coeff = 12; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - - tmp_index_x = x + 1; - tmp_coeff = 12; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - - tmp_index_x = x + 3; - tmp_coeff = 5; - nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; - level = (BYTE)min(255, max(0, (int)nlevel)); - SetPixelIndex(tmp_index_x, tmp_index_y, level); - } - } - break; - } - case 7: - { - // Bayer ordered dither - int order = 4; - //create Bayer matrix - if (order>4) order = 4; - int size = (1 << (2*order)); - BYTE* Bmatrix = (BYTE*) malloc(size * sizeof(BYTE)); - for(int i = 0; i < size; i++) { - int n = order; - int x = i / n; - int y = i % n; - int dither = 0; - while (n-- > 0){ - dither = (((dither<<1)|((x&1) ^ (y&1)))<<1) | (y&1); - x >>= 1; - y >>= 1; - } - Bmatrix[i] = (BYTE)(dither); - } - - int scale = max(0,(8-2*order)); - int level; - for (long y=0;y> scale; - if(level > Bmatrix[ (x % order) + order * (y % order) ]){ - tmp.SetPixelIndex(x,y,1); - } else { - tmp.SetPixelIndex(x,y,0); - } - } - } - - free(Bmatrix); - - break; - } - default: - { - // Floyd-Steinberg error diffusion (Thanks to Steve McMahon) - long error,nlevel,coeff=1; - BYTE level; - - for (long y=0;y 128){ - tmp.SetPixelIndex(x,y,1); - error = level-255; - } else { - tmp.SetPixelIndex(x,y,0); - error = level; - } - - nlevel = GetPixelIndex(x+1,y) + (error * 7)/16; - level = (BYTE)min(255,max(0,(int)nlevel)); - SetPixelIndex(x+1,y,level); - for(int i=-1; i<2; i++){ - switch(i){ - case -1: - coeff=3; break; - case 0: - coeff=5; break; - case 1: - coeff=1; break; - } - nlevel = GetPixelIndex(x+i,y+1) + (error * coeff)/16; - level = (BYTE)min(255,max(0,(int)nlevel)); - SetPixelIndex(x+i,y+1,level); - } - } - } - } - } - - tmp.SetPaletteColor(0,0,0,0); - tmp.SetPaletteColor(1,255,255,255); - Transfer(tmp); - - return true; -} -//////////////////////////////////////////////////////////////////////////////// -/** - * CropRotatedRectangle - * \param topx,topy : topmost and leftmost point of the rectangle - (topmost, and if there are 2 topmost points, the left one) - * \param width : size of the right hand side of rect, from (topx,topy) roundwalking clockwise - * \param height : size of the left hand side of rect, from (topx,topy) roundwalking clockwise - * \param angle : angle of the right hand side of rect, from (topx,topy) - * \param iDst : pointer to destination image (if 0, this image is modified) - * \author [VATI] - */ -bool CxImage::CropRotatedRectangle( long topx, long topy, long width, long height, float angle, CxImage* iDst) -{ - if (!pDib) return false; - - - long startx,starty,endx,endy; - double cos_angle = cos(angle/*/57.295779513082320877*/); - double sin_angle = sin(angle/*/57.295779513082320877*/); - - // if there is nothing special, call the original Crop(): - if ( fabs(angle)<0.0002 ) - return Crop( topx, topy, topx+width, topy+height, iDst); - - startx = min(topx, topx - (long)(sin_angle*(double)height)); - endx = topx + (long)(cos_angle*(double)width); - endy = topy + (long)(cos_angle*(double)height + sin_angle*(double)width); - // check: corners of the rectangle must be inside - if ( IsInside( startx, topy )==false || - IsInside( endx, endy ) == false ) - return false; - - // first crop to bounding rectangle - CxImage tmp(*this, true, false, true); - // tmp.Copy(*this, true, false, true); - if (!tmp.IsValid()){ - strcpy(info.szLastError,tmp.GetLastError()); - return false; - } - if (!tmp.Crop( startx, topy, endx, endy)){ - strcpy(info.szLastError,tmp.GetLastError()); - return false; - } - - // the midpoint of the image now became the same as the midpoint of the rectangle - // rotate new image with minus angle amount - if ( false == tmp.Rotate( (float)(-angle*57.295779513082320877) ) ) // Rotate expects angle in degrees - return false; - - // crop rotated image to the original selection rectangle - endx = (tmp.head.biWidth+width)/2; - startx = (tmp.head.biWidth-width)/2; - starty = (tmp.head.biHeight+height)/2; - endy = (tmp.head.biHeight-height)/2; - if ( false == tmp.Crop( startx, starty, endx, endy ) ) - return false; - - if (iDst) iDst->Transfer(tmp); - else Transfer(tmp); - - return true; -} -//////////////////////////////////////////////////////////////////////////////// -bool CxImage::Crop(const RECT& rect, CxImage* iDst) -{ - return Crop(rect.left, rect.top, rect.right, rect.bottom, iDst); -} -//////////////////////////////////////////////////////////////////////////////// -bool CxImage::Crop(long left, long top, long right, long bottom, CxImage* iDst) -{ - if (!pDib) return false; - - long startx = max(0L,min(left,head.biWidth)); - long endx = max(0L,min(right,head.biWidth)); - long starty = head.biHeight - max(0L,min(top,head.biHeight)); - long endy = head.biHeight - max(0L,min(bottom,head.biHeight)); - - if (startx==endx || starty==endy) return false; - - if (startx>endx) {long tmp=startx; startx=endx; endx=tmp;} - if (starty>endy) {long tmp=starty; starty=endy; endy=tmp;} - - CxImage tmp(endx-startx,endy-starty,head.biBitCount,info.dwType); - if (!tmp.IsValid()){ - strcpy(info.szLastError,tmp.GetLastError()); - return false; - } - - tmp.SetPalette(GetPalette(),head.biClrUsed); - tmp.info.nBkgndIndex = info.nBkgndIndex; - tmp.info.nBkgndColor = info.nBkgndColor; - - switch (head.biBitCount) { - case 1: - case 4: - { - for(long y=starty, yd=0; y - for(long x=startx, xd=0; x> 3; - BYTE* pDest = tmp.info.pImage; - BYTE* pSrc = info.pImage + starty * info.dwEffWidth + (startx*head.biBitCount >> 3); - for(long y=starty; y - memcpy(pDest,pSrc,linelen); - pDest+=tmp.info.dwEffWidth; - pSrc+=info.dwEffWidth; - } - } - } - -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()){ // - tmp.AlphaCreate(); - if (!tmp.AlphaIsValid()) return false; - BYTE* pDest = tmp.pAlpha; - BYTE* pSrc = pAlpha + startx + starty*head.biWidth; - for (long y=starty; yTransfer(tmp); - else Transfer(tmp); - - return true; -} -//////////////////////////////////////////////////////////////////////////////// -/** - * \param xgain, ygain : can be from 0 to 1. - * \param xpivot, ypivot : is the center of the transformation. - * \param bEnableInterpolation : if true, enables bilinear interpolation. - * \return true if everything is ok - */ -bool CxImage::Skew(float xgain, float ygain, long xpivot, long ypivot, bool bEnableInterpolation) -{ - if (!pDib) return false; - float nx,ny; - - CxImage tmp(*this); - if (!tmp.IsValid()){ - strcpy(info.szLastError,tmp.GetLastError()); - return false; - } - - long xmin,xmax,ymin,ymax; - if (pSelection){ - xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; - ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; - } else { - xmin = ymin = 0; - xmax = head.biWidth; ymax=head.biHeight; - } - for(long y=ymin; y top) || (x < left) || (x > right)) { - tmp.SetPixelIndex(x,y, pixel); - } else { - tmp.SetPixelIndex(x,y,GetPixelIndex(x-left,y-bottom)); - } - } - } - break; - } - case 8: - case 24: - { - if (head.biBitCount == 8) { - BYTE pixel = tmp.GetNearestIndex( canvascolor); - memset(tmp.info.pImage, pixel, + (tmp.info.dwEffWidth * newHeight)); - } else { - for (long y = 0; y < newHeight; ++y) { - BYTE *pDest = tmp.info.pImage + (y * tmp.info.dwEffWidth); - for (long x = 0; x < newWidth; ++x) { - *pDest++ = canvascolor.rgbBlue; - *pDest++ = canvascolor.rgbGreen; - *pDest++ = canvascolor.rgbRed; - } - } - } - - BYTE* pDest = tmp.info.pImage + (tmp.info.dwEffWidth * bottom) + (left*(head.biBitCount >> 3)); - BYTE* pSrc = info.pImage; - for(long y=bottom; y <= top; y++){ - info.nProgress = (long)(100*y/(1 + top - bottom)); - memcpy(pDest,pSrc,(head.biBitCount >> 3) * (right - left + 1)); - pDest+=tmp.info.dwEffWidth; - pSrc+=info.dwEffWidth; - } - } - } - -#if CXIMAGE_SUPPORT_SELECTION - if (SelectionIsValid()){ - if (!tmp.SelectionCreate()) - return false; - BYTE* pSrc = SelectionGetPointer(); - BYTE* pDst = tmp.SelectionGetPointer(left,bottom); - for(long y=bottom; y <= top; y++){ - memcpy(pDst,pSrc, (right - left + 1)); - pSrc+=head.biWidth; - pDst+=tmp.head.biWidth; - } - tmp.info.rSelectionBox.left = info.rSelectionBox.left + left; - tmp.info.rSelectionBox.right = info.rSelectionBox.right + left; - tmp.info.rSelectionBox.top = info.rSelectionBox.top + bottom; - tmp.info.rSelectionBox.bottom = info.rSelectionBox.bottom + bottom; - } -#endif //CXIMAGE_SUPPORT_SELECTION - -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()){ - if (!tmp.AlphaCreate()) - return false; - tmp.AlphaSet(canvascolor.rgbReserved); - BYTE* pSrc = AlphaGetPointer(); - BYTE* pDst = tmp.AlphaGetPointer(left,bottom); - for(long y=bottom; y <= top; y++){ - memcpy(pDst,pSrc, (right - left + 1)); - pSrc+=head.biWidth; - pDst+=tmp.head.biWidth; - } - } -#endif //CXIMAGE_SUPPORT_ALPHA - - //select the destination - if (iDst) iDst->Transfer(tmp); - else Transfer(tmp); - - return true; -} -//////////////////////////////////////////////////////////////////////////////// -bool CxImage::Expand(long newx, long newy, RGBQUAD canvascolor, CxImage* iDst) -{ - //thanks to - - if (!pDib) return false; - - if ((newx < head.biWidth) || (newy < head.biHeight)) return false; - - int nAddLeft = (newx - head.biWidth) / 2; - int nAddTop = (newy - head.biHeight) / 2; - - return Expand(nAddLeft, nAddTop, newx - (head.biWidth + nAddLeft), newy - (head.biHeight + nAddTop), canvascolor, iDst); -} -//////////////////////////////////////////////////////////////////////////////// -/** - * Resamples the image with the correct aspect ratio, and fills the borders. - * \param newx, newy = thumbnail size. - * \param canvascolor = border color. - * \param iDst = pointer to destination image (if it's 0, this image is modified). - * \return true if everything is ok. - * \author [Colin Urquhart] - */ -bool CxImage::Thumbnail(long newx, long newy, RGBQUAD canvascolor, CxImage* iDst) -{ - if (!pDib) return false; - - if ((newx <= 0) || (newy <= 0)) return false; - - CxImage tmp(*this); - if (!tmp.IsValid()){ - strcpy(info.szLastError,tmp.GetLastError()); - return false; - } - - // determine whether we need to shrink the image - if ((head.biWidth > newx) || (head.biHeight > newy)) { - float fScale; - float fAspect = (float) newx / (float) newy; - if (fAspect * head.biHeight > head.biWidth) { - fScale = (float) newy / head.biHeight; - } else { - fScale = (float) newx / head.biWidth; - } - tmp.Resample((long) (fScale * head.biWidth), (long) (fScale * head.biHeight), 0); - } - - // expand the frame - tmp.Expand(newx, newy, canvascolor, iDst); - - //select the destination - if (iDst) iDst->Transfer(tmp); - else Transfer(tmp); - return true; -} -//////////////////////////////////////////////////////////////////////////////// -/** - * Perform circle_based transformations. - * \param type - for different transformations - * - 0 for normal (proturberant) FishEye - * - 1 for reverse (concave) FishEye - * - 2 for Swirle - * - 3 for Cilinder mirror - * - 4 for bathroom - * - * \param rmax - effect radius. If 0, the whole image is processed - * \param Koeff - only for swirle - * \author Arkadiy Olovyannikov ark(at)msun(dot)ru - */ -bool CxImage::CircleTransform(int type,long rmax,float Koeff) -{ - if (!pDib) return false; - - long nx,ny; - double angle,radius,rnew; - - CxImage tmp(*this); - if (!tmp.IsValid()){ - strcpy(info.szLastError,tmp.GetLastError()); - return false; - } - - long xmin,xmax,ymin,ymax,xmid,ymid; - if (pSelection){ - xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; - ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; - } else { - xmin = ymin = 0; - xmax = head.biWidth; ymax=head.biHeight; - } - - xmid = (long) (tmp.GetWidth()/2); - ymid = (long) (tmp.GetHeight()/2); - - if (!rmax) rmax=(long)sqrt((float)((xmid-xmin)*(xmid-xmin)+(ymid-ymin)*(ymid-ymin))); - if (Koeff==0.0f) Koeff=1.0f; - - for(long y=ymin; yhead.biWidth || newy>head.biHeight) { - //let me repeat... this method can't enlarge image - strcpy(info.szLastError,"QIShrink can't enlarge image"); - return false; - } - - if (newx==head.biWidth && newy==head.biHeight) { - //image already correct size (just copy and return) - if (iDst) iDst->Copy(*this); - return true; - }//if - - //create temporary destination image - CxImage newImage; - newImage.CopyInfo(*this); - newImage.Create(newx,newy,(bChangeBpp)?24:head.biBitCount,GetType()); - newImage.SetPalette(GetPalette()); - if (!newImage.IsValid()){ - strcpy(info.szLastError,newImage.GetLastError()); - return false; - } - - //and alpha channel if required -#if CXIMAGE_SUPPORT_ALPHA - if (AlphaIsValid()) newImage.AlphaCreate(); -#endif - - const int oldx = head.biWidth; - const int oldy = head.biHeight; - - int accuCellSize = 4; -#if CXIMAGE_SUPPORT_ALPHA - BYTE *alphaPtr; - if (AlphaIsValid()) accuCellSize=5; -#endif - - unsigned int *accu = new unsigned int[newx*accuCellSize]; //array for suming pixels... one pixel for every destination column - unsigned int *accuPtr; //pointer for walking through accu - //each cell consists of blue, red, green component and count of pixels summed in this cell - memset(accu, 0, newx * accuCellSize * sizeof(unsigned int)); //clear accu - - if (!IsIndexed()) { - //RGB24 version with pointers - BYTE *destPtr, *srcPtr, *destPtrS, *srcPtrS; //destination and source pixel, and beginnings of current row - srcPtrS=(BYTE*)BlindGetPixelPointer(0,0); - destPtrS=(BYTE*)newImage.BlindGetPixelPointer(0,0); - int ex=0, ey=0; //ex and ey replace division... - int dy=0; - //(we just add pixels, until by adding newx or newy we get a number greater than old size... then - // it's time to move to next pixel) - - for(int y=0; yoldx) { //when we reach oldx, it's time to move to new slot - accuPtr += accuCellSize; - ex -= oldx; //(substract oldx from ex and resume from there on) - }//if (ex overflow) - }//for x - - if (ey>=oldy) { //now when this happens - ey -= oldy; //it's time to move to new destination row - destPtr = destPtrS; //reset pointers to proper initial values - accuPtr = accu; -#if CXIMAGE_SUPPORT_ALPHA - alphaPtr = newImage.AlphaGetPointer(0, dy++); -#endif - for (int k=0; koldx) { //when we reach oldx, it's time to move to new slot - accuPtr += accuCellSize; - ex -= oldx; //(substract oldx from ex and resume from there on) - }//if (ex overflow) - }//for x - - if (ey>=oldy) { //now when this happens - ey -= oldy; //it's time to move to new destination row - accuPtr = accu; - for (int dx=0; dxTransfer(newImage); - else - Transfer(newImage); - return true; - -} - -//////////////////////////////////////////////////////////////////////////////// -#endif //CXIMAGE_SUPPORT_TRANSFORMATION +// xImaTran.cpp : Transformation functions +/* 07/08/2001 v1.00 - Davide Pizzolato - www.xdp.it + * CxImage version 6.0.0 02/Feb/2008 + */ + +#include "ximage.h" +#include "ximath.h" + +#if CXIMAGE_SUPPORT_BASICTRANSFORMATIONS +//////////////////////////////////////////////////////////////////////////////// +bool CxImage::GrayScale() +{ + if (!pDib) return false; + if (head.biBitCount<=8){ + RGBQUAD* ppal=GetPalette(); + int gray; + //converts the colors to gray, use the blue channel only + for(DWORD i=0;i= 0) info.nBkgndIndex = ppal[info.nBkgndIndex].rgbBlue; + //create a "real" 8 bit gray scale image + if (head.biBitCount==8){ + BYTE *img=info.pImage; + for(DWORD i=0;i> 1]&((BYTE)0x0F<> pos)].rgbBlue; + } else { + BYTE pos = (BYTE)(7-x%8); + iDst[x]= ppal[(BYTE)((iSrc[x >> 3]&((BYTE)0x01<> pos)].rgbBlue; + } + } + } + Transfer(ima); + } + } else { //from RGB to 8 bit gray scale + BYTE *iSrc=info.pImage; + CxImage ima; + ima.CopyInfo(*this); + if (!ima.Create(head.biWidth,head.biHeight,8,info.dwType)) return false; + ima.SetGrayPalette(); +#if CXIMAGE_SUPPORT_SELECTION + ima.SelectionCopy(*this); +#endif //CXIMAGE_SUPPORT_SELECTION +#if CXIMAGE_SUPPORT_ALPHA + ima.AlphaCopy(*this); +#endif //CXIMAGE_SUPPORT_ALPHA + BYTE *img=ima.GetBits(); + long l8=ima.GetEffWidth(); + long l=head.biWidth * 3; + for(long y=0; y < head.biHeight; y++) { + for(long x=0,x8=0; x < l; x+=3,x8++) { + img[x8+y*l8]=(BYTE)RGB2GRAY(*(iSrc+x+2),*(iSrc+x+1),*(iSrc+x+0)); + } + iSrc+=info.dwEffWidth; + } + Transfer(ima); + } + return true; +} +//////////////////////////////////////////////////////////////////////////////// +/** + * \sa Mirror + * \author [qhbo] + */ +bool CxImage::Flip(bool bFlipSelection, bool bFlipAlpha) +{ + if (!pDib) return false; + + BYTE *buff = (BYTE*)malloc(info.dwEffWidth); + if (!buff) return false; + + BYTE *iSrc,*iDst; + iSrc = GetBits(head.biHeight-1); + iDst = GetBits(0); + for (long i=0; i<(head.biHeight/2); ++i) + { + memcpy(buff, iSrc, info.dwEffWidth); + memcpy(iSrc, iDst, info.dwEffWidth); + memcpy(iDst, buff, info.dwEffWidth); + iSrc-=info.dwEffWidth; + iDst+=info.dwEffWidth; + } + + free(buff); + + if (bFlipSelection){ +#if CXIMAGE_SUPPORT_SELECTION + SelectionFlip(); +#endif //CXIMAGE_SUPPORT_SELECTION + } + + if (bFlipAlpha){ +#if CXIMAGE_SUPPORT_ALPHA + AlphaFlip(); +#endif //CXIMAGE_SUPPORT_ALPHA + } + + return true; +} +//////////////////////////////////////////////////////////////////////////////// +/** + * \sa Flip + */ +bool CxImage::Mirror(bool bMirrorSelection, bool bMirrorAlpha) +{ + if (!pDib) return false; + + CxImage* imatmp = new CxImage(*this,false,true,true); + if (!imatmp) return false; + if (!imatmp->IsValid()){ + delete imatmp; + return false; + } + + BYTE *iSrc,*iDst; + long wdt=(head.biWidth-1) * (head.biBitCount==24 ? 3:1); + iSrc=info.pImage + wdt; + iDst=imatmp->info.pImage; + long x,y; + switch (head.biBitCount){ + case 24: + for(y=0; y < head.biHeight; y++){ + for(x=0; x <= wdt; x+=3){ + *(iDst+x)=*(iSrc-x); + *(iDst+x+1)=*(iSrc-x+1); + *(iDst+x+2)=*(iSrc-x+2); + } + iSrc+=info.dwEffWidth; + iDst+=info.dwEffWidth; + } + break; + case 8: + for(y=0; y < head.biHeight; y++){ + for(x=0; x <= wdt; x++) + *(iDst+x)=*(iSrc-x); + iSrc+=info.dwEffWidth; + iDst+=info.dwEffWidth; + } + break; + default: + for(y=0; y < head.biHeight; y++){ + for(x=0; x <= wdt; x++) + imatmp->SetPixelIndex(x,y,GetPixelIndex(wdt-x,y)); + } + } + + if (bMirrorSelection){ +#if CXIMAGE_SUPPORT_SELECTION + imatmp->SelectionMirror(); +#endif //CXIMAGE_SUPPORT_SELECTION + } + + if (bMirrorAlpha){ +#if CXIMAGE_SUPPORT_ALPHA + imatmp->AlphaMirror(); +#endif //CXIMAGE_SUPPORT_ALPHA + } + + Transfer(*imatmp); + delete imatmp; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +#define RBLOCK 64 + +//////////////////////////////////////////////////////////////////////////////// +bool CxImage::RotateLeft(CxImage* iDst) +{ + if (!pDib) return false; + + long newWidth = GetHeight(); + long newHeight = GetWidth(); + + CxImage imgDest; + imgDest.CopyInfo(*this); + imgDest.Create(newWidth,newHeight,GetBpp(),GetType()); + imgDest.SetPalette(GetPalette()); + +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()) imgDest.AlphaCreate(); +#endif + +#if CXIMAGE_SUPPORT_SELECTION + if (SelectionIsValid()) imgDest.SelectionCreate(); +#endif + + long x,x2,y,dlineup; + + // Speedy rotate for BW images + if (head.biBitCount == 1) { + + BYTE *sbits, *dbits, *dbitsmax, bitpos, *nrow,*srcdisp; + ldiv_t div_r; + + BYTE *bsrc = GetBits(), *bdest = imgDest.GetBits(); + dbitsmax = bdest + imgDest.head.biSizeImage - 1; + dlineup = 8 * imgDest.info.dwEffWidth - imgDest.head.biWidth; + + imgDest.Clear(0); + for (y = 0; y < head.biHeight; y++) { + // Figure out the Column we are going to be copying to + div_r = ldiv(y + dlineup, (long)8); + // set bit pos of src column byte + bitpos = (BYTE)(1 << div_r.rem); + srcdisp = bsrc + y * info.dwEffWidth; + for (x = 0; x < (long)info.dwEffWidth; x++) { + // Get Source Bits + sbits = srcdisp + x; + // Get destination column + nrow = bdest + (x * 8) * imgDest.info.dwEffWidth + imgDest.info.dwEffWidth - 1 - div_r.quot; + for (long z = 0; z < 8; z++) { + // Get Destination Byte + dbits = nrow + z * imgDest.info.dwEffWidth; + if ((dbits < bdest) || (dbits > dbitsmax)) break; + if (*sbits & (128 >> z)) *dbits |= bitpos; + } + } + }//for y + +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()) { + for (x = 0; x < newWidth; x++){ + x2=newWidth-x-1; + for (y = 0; y < newHeight; y++){ + imgDest.AlphaSet(x,y,BlindAlphaGet(y, x2)); + }//for y + }//for x + } +#endif //CXIMAGE_SUPPORT_ALPHA + +#if CXIMAGE_SUPPORT_SELECTION + if (SelectionIsValid()) { + imgDest.info.rSelectionBox.left = newWidth-info.rSelectionBox.top; + imgDest.info.rSelectionBox.right = newWidth-info.rSelectionBox.bottom; + imgDest.info.rSelectionBox.bottom = info.rSelectionBox.left; + imgDest.info.rSelectionBox.top = info.rSelectionBox.right; + for (x = 0; x < newWidth; x++){ + x2=newWidth-x-1; + for (y = 0; y < newHeight; y++){ + imgDest.SelectionSet(x,y,BlindSelectionGet(y, x2)); + }//for y + }//for x + } +#endif //CXIMAGE_SUPPORT_SELECTION + + } else { + //anything other than BW: + //bd, 10. 2004: This optimized version of rotation rotates image by smaller blocks. It is quite + //a bit faster than obvious algorithm, because it produces much less CPU cache misses. + //This optimization can be tuned by changing block size (RBLOCK). 96 is good value for current + //CPUs (tested on Athlon XP and Celeron D). Larger value (if CPU has enough cache) will increase + //speed somehow, but once you drop out of CPU's cache, things will slow down drastically. + //For older CPUs with less cache, lower value would yield better results. + + BYTE *srcPtr, *dstPtr; //source and destionation for 24-bit version + int xs, ys; //x-segment and y-segment + for (xs = 0; xs < newWidth; xs+=RBLOCK) { //for all image blocks of RBLOCK*RBLOCK pixels + for (ys = 0; ys < newHeight; ys+=RBLOCK) { + if (head.biBitCount==24) { + //RGB24 optimized pixel access: + for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){ //do rotation + info.nProgress = (long)(100*x/newWidth); + x2=newWidth-x-1; + dstPtr = (BYTE*) imgDest.BlindGetPixelPointer(x,ys); + srcPtr = (BYTE*) BlindGetPixelPointer(ys, x2); + for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){ + //imgDest.SetPixelColor(x, y, GetPixelColor(y, x2)); + *(dstPtr) = *(srcPtr); + *(dstPtr+1) = *(srcPtr+1); + *(dstPtr+2) = *(srcPtr+2); + srcPtr += 3; + dstPtr += imgDest.info.dwEffWidth; + }//for y + }//for x + } else { + //anything else than 24bpp (and 1bpp): palette + for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){ + info.nProgress = (long)(100*x/newWidth); // + x2=newWidth-x-1; + for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){ + imgDest.SetPixelIndex(x, y, BlindGetPixelIndex(y, x2)); + }//for y + }//for x + }//if (version selection) +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()) { + for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){ + x2=newWidth-x-1; + for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){ + imgDest.AlphaSet(x,y,BlindAlphaGet(y, x2)); + }//for y + }//for x + }//if (alpha channel) +#endif //CXIMAGE_SUPPORT_ALPHA + +#if CXIMAGE_SUPPORT_SELECTION + if (SelectionIsValid()) { + imgDest.info.rSelectionBox.left = newWidth-info.rSelectionBox.top; + imgDest.info.rSelectionBox.right = newWidth-info.rSelectionBox.bottom; + imgDest.info.rSelectionBox.bottom = info.rSelectionBox.left; + imgDest.info.rSelectionBox.top = info.rSelectionBox.right; + for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){ + x2=newWidth-x-1; + for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){ + imgDest.SelectionSet(x,y,BlindSelectionGet(y, x2)); + }//for y + }//for x + }//if (selection) +#endif //CXIMAGE_SUPPORT_SELECTION + }//for ys + }//for xs + }//if + + //select the destination + if (iDst) iDst->Transfer(imgDest); + else Transfer(imgDest); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +bool CxImage::RotateRight(CxImage* iDst) +{ + if (!pDib) return false; + + long newWidth = GetHeight(); + long newHeight = GetWidth(); + + CxImage imgDest; + imgDest.CopyInfo(*this); + imgDest.Create(newWidth,newHeight,GetBpp(),GetType()); + imgDest.SetPalette(GetPalette()); + +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()) imgDest.AlphaCreate(); +#endif + +#if CXIMAGE_SUPPORT_SELECTION + if (SelectionIsValid()) imgDest.SelectionCreate(); +#endif + + long x,y,y2; + // Speedy rotate for BW images + if (head.biBitCount == 1) { + + BYTE *sbits, *dbits, *dbitsmax, bitpos, *nrow,*srcdisp; + ldiv_t div_r; + + BYTE *bsrc = GetBits(), *bdest = imgDest.GetBits(); + dbitsmax = bdest + imgDest.head.biSizeImage - 1; + + imgDest.Clear(0); + for (y = 0; y < head.biHeight; y++) { + // Figure out the Column we are going to be copying to + div_r = ldiv(y, (long)8); + // set bit pos of src column byte + bitpos = (BYTE)(128 >> div_r.rem); + srcdisp = bsrc + y * info.dwEffWidth; + for (x = 0; x < (long)info.dwEffWidth; x++) { + // Get Source Bits + sbits = srcdisp + x; + // Get destination column + nrow = bdest + (imgDest.head.biHeight-1-(x*8)) * imgDest.info.dwEffWidth + div_r.quot; + for (long z = 0; z < 8; z++) { + // Get Destination Byte + dbits = nrow - z * imgDest.info.dwEffWidth; + if ((dbits < bdest) || (dbits > dbitsmax)) break; + if (*sbits & (128 >> z)) *dbits |= bitpos; + } + } + } + +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()){ + for (y = 0; y < newHeight; y++){ + y2=newHeight-y-1; + for (x = 0; x < newWidth; x++){ + imgDest.AlphaSet(x,y,BlindAlphaGet(y2, x)); + } + } + } +#endif //CXIMAGE_SUPPORT_ALPHA + +#if CXIMAGE_SUPPORT_SELECTION + if (SelectionIsValid()){ + imgDest.info.rSelectionBox.left = info.rSelectionBox.bottom; + imgDest.info.rSelectionBox.right = info.rSelectionBox.top; + imgDest.info.rSelectionBox.bottom = newHeight-info.rSelectionBox.right; + imgDest.info.rSelectionBox.top = newHeight-info.rSelectionBox.left; + for (y = 0; y < newHeight; y++){ + y2=newHeight-y-1; + for (x = 0; x < newWidth; x++){ + imgDest.SelectionSet(x,y,BlindSelectionGet(y2, x)); + } + } + } +#endif //CXIMAGE_SUPPORT_SELECTION + + } else { + //anything else but BW + BYTE *srcPtr, *dstPtr; //source and destionation for 24-bit version + int xs, ys; //x-segment and y-segment + for (xs = 0; xs < newWidth; xs+=RBLOCK) { + for (ys = 0; ys < newHeight; ys+=RBLOCK) { + if (head.biBitCount==24) { + //RGB24 optimized pixel access: + for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){ + info.nProgress = (long)(100*y/newHeight); // + y2=newHeight-y-1; + dstPtr = (BYTE*) imgDest.BlindGetPixelPointer(xs,y); + srcPtr = (BYTE*) BlindGetPixelPointer(y2, xs); + for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){ + //imgDest.SetPixelColor(x, y, GetPixelColor(y2, x)); + *(dstPtr) = *(srcPtr); + *(dstPtr+1) = *(srcPtr+1); + *(dstPtr+2) = *(srcPtr+2); + dstPtr += 3; + srcPtr += info.dwEffWidth; + }//for x + }//for y + } else { + //anything else than BW & RGB24: palette + for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){ + info.nProgress = (long)(100*y/newHeight); // + y2=newHeight-y-1; + for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){ + imgDest.SetPixelIndex(x, y, BlindGetPixelIndex(y2, x)); + }//for x + }//for y + }//if +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()){ + for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){ + y2=newHeight-y-1; + for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){ + imgDest.AlphaSet(x,y,BlindAlphaGet(y2, x)); + }//for x + }//for y + }//if (has alpha) +#endif //CXIMAGE_SUPPORT_ALPHA + +#if CXIMAGE_SUPPORT_SELECTION + if (SelectionIsValid()){ + imgDest.info.rSelectionBox.left = info.rSelectionBox.bottom; + imgDest.info.rSelectionBox.right = info.rSelectionBox.top; + imgDest.info.rSelectionBox.bottom = newHeight-info.rSelectionBox.right; + imgDest.info.rSelectionBox.top = newHeight-info.rSelectionBox.left; + for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){ + y2=newHeight-y-1; + for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){ + imgDest.SelectionSet(x,y,BlindSelectionGet(y2, x)); + }//for x + }//for y + }//if (has alpha) +#endif //CXIMAGE_SUPPORT_SELECTION + }//for ys + }//for xs + }//if + + //select the destination + if (iDst) iDst->Transfer(imgDest); + else Transfer(imgDest); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +bool CxImage::Negative() +{ + if (!pDib) return false; + + if (head.biBitCount<=8){ + if (IsGrayScale()){ //GRAYSCALE, selection + if (pSelection){ + for(long y=info.rSelectionBox.bottom; y invert transparent color too + info.nBkgndColor.rgbBlue = (BYTE)(255-info.nBkgndColor.rgbBlue); + info.nBkgndColor.rgbGreen = (BYTE)(255-info.nBkgndColor.rgbGreen); + info.nBkgndColor.rgbRed = (BYTE)(255-info.nBkgndColor.rgbRed); + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +#endif //CXIMAGE_SUPPORT_BASICTRANSFORMATIONS +//////////////////////////////////////////////////////////////////////////////// +#if CXIMAGE_SUPPORT_TRANSFORMATION +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +bool CxImage::Rotate(float angle, CxImage* iDst) +{ + if (!pDib) return false; + + // Copyright (c) 1996-1998 Ulrich von Zadow + + // Negative the angle, because the y-axis is negative. + double ang = -angle*acos((float)0)/90; + int newWidth, newHeight; + int nWidth = GetWidth(); + int nHeight= GetHeight(); + double cos_angle = cos(ang); + double sin_angle = sin(ang); + + // Calculate the size of the new bitmap + POINT p1={0,0}; + POINT p2={nWidth,0}; + POINT p3={0,nHeight}; + POINT p4={nWidth,nHeight}; + CxPoint2 newP1,newP2,newP3,newP4, leftTop, rightTop, leftBottom, rightBottom; + + newP1.x = (float)p1.x; + newP1.y = (float)p1.y; + newP2.x = (float)(p2.x*cos_angle - p2.y*sin_angle); + newP2.y = (float)(p2.x*sin_angle + p2.y*cos_angle); + newP3.x = (float)(p3.x*cos_angle - p3.y*sin_angle); + newP3.y = (float)(p3.x*sin_angle + p3.y*cos_angle); + newP4.x = (float)(p4.x*cos_angle - p4.y*sin_angle); + newP4.y = (float)(p4.x*sin_angle + p4.y*cos_angle); + + leftTop.x = __min(__min(newP1.x,newP2.x),__min(newP3.x,newP4.x)); + leftTop.y = __min(__min(newP1.y,newP2.y),__min(newP3.y,newP4.y)); + rightBottom.x = __max(__max(newP1.x,newP2.x),__max(newP3.x,newP4.x)); + rightBottom.y = __max(__max(newP1.y,newP2.y),__max(newP3.y,newP4.y)); + leftBottom.x = leftTop.x; + leftBottom.y = rightBottom.y; + rightTop.x = rightBottom.x; + rightTop.y = leftTop.y; + + newWidth = (int) floor(0.5f + rightTop.x - leftTop.x); + newHeight= (int) floor(0.5f + leftBottom.y - leftTop.y); + CxImage imgDest; + imgDest.CopyInfo(*this); + imgDest.Create(newWidth,newHeight,GetBpp(),GetType()); + imgDest.SetPalette(GetPalette()); + +#if CXIMAGE_SUPPORT_ALPHA + if(AlphaIsValid()) //MTA: Fix for rotation problem when the image has an alpha channel + { + imgDest.AlphaCreate(); + imgDest.AlphaClear(); + } +#endif //CXIMAGE_SUPPORT_ALPHA + + int x,y,newX,newY,oldX,oldY; + + if (head.biClrUsed==0){ //RGB + for (y = (int)leftTop.y, newY = 0; y<=(int)leftBottom.y; y++,newY++){ + info.nProgress = (long)(100*newY/newHeight); + if (info.nEscape) break; + for (x = (int)leftTop.x, newX = 0; x<=(int)rightTop.x; x++,newX++){ + oldX = (long)(x*cos_angle + y*sin_angle + 0.5); + oldY = (long)(y*cos_angle - x*sin_angle + 0.5); + imgDest.SetPixelColor(newX,newY,GetPixelColor(oldX,oldY)); +#if CXIMAGE_SUPPORT_ALPHA + imgDest.AlphaSet(newX,newY,AlphaGet(oldX,oldY)); //MTA: copy the alpha value +#endif //CXIMAGE_SUPPORT_ALPHA + } + } + } else { //PALETTE + for (y = (int)leftTop.y, newY = 0; y<=(int)leftBottom.y; y++,newY++){ + info.nProgress = (long)(100*newY/newHeight); + if (info.nEscape) break; + for (x = (int)leftTop.x, newX = 0; x<=(int)rightTop.x; x++,newX++){ + oldX = (long)(x*cos_angle + y*sin_angle + 0.5); + oldY = (long)(y*cos_angle - x*sin_angle + 0.5); + imgDest.SetPixelIndex(newX,newY,GetPixelIndex(oldX,oldY)); +#if CXIMAGE_SUPPORT_ALPHA + imgDest.AlphaSet(newX,newY,AlphaGet(oldX,oldY)); //MTA: copy the alpha value +#endif //CXIMAGE_SUPPORT_ALPHA + } + } + } + //select the destination + if (iDst) iDst->Transfer(imgDest); + else Transfer(imgDest); + + return true; +} +//////////////////////////////////////////////////////////////////////////////// +/** + * Rotates image around it's center. + * Method can use interpolation with paletted images, but does not change pallete, so results vary. + * (If you have only four colours in a palette, there's not much room for interpolation.) + * + * \param angle - angle in degrees (positive values rotate clockwise) + * \param *iDst - destination image (if null, this image is changed) + * \param inMethod - interpolation method used + * (IM_NEAREST_NEIGHBOUR produces aliasing (fast), IM_BILINEAR softens picture a bit (slower) + * IM_SHARPBICUBIC is slower and produces some halos...) + * \param ofMethod - overflow method (how to choose colour of pixels that have no source) + * \param replColor - replacement colour to use (OM_COLOR, OM_BACKGROUND with no background colour...) + * \param optimizeRightAngles - call faster methods for 90, 180, and 270 degree rotations. Faster methods + * are called for angles, where error (in location of corner pixels) is less + * than 0.25 pixels. + * \param bKeepOriginalSize - rotates the image without resizing. + * + * \author ***bd*** 2.2004 + */ +bool CxImage::Rotate2(float angle, + CxImage *iDst, + InterpolationMethod inMethod, + OverflowMethod ofMethod, + RGBQUAD *replColor, + bool const optimizeRightAngles, + bool const bKeepOriginalSize) +{ + if (!pDib) return false; //no dib no go + + double ang = -angle*acos(0.0f)/90.0f; //convert angle to radians and invert (positive angle performs clockwise rotation) + float cos_angle = (float) cos(ang); //these two are needed later (to rotate) + float sin_angle = (float) sin(ang); + + //Calculate the size of the new bitmap (rotate corners of image) + CxPoint2 p[4]; //original corners of the image + p[0]=CxPoint2(-0.5f,-0.5f); + p[1]=CxPoint2(GetWidth()-0.5f,-0.5f); + p[2]=CxPoint2(-0.5f,GetHeight()-0.5f); + p[3]=CxPoint2(GetWidth()-0.5f,GetHeight()-0.5f); + CxPoint2 newp[4]; //rotated positions of corners + //(rotate corners) + if (bKeepOriginalSize){ + for (int i=0; i<4; i++) { + newp[i].x = p[i].x; + newp[i].y = p[i].y; + }//for + } else { + for (int i=0; i<4; i++) { + newp[i].x = (p[i].x*cos_angle - p[i].y*sin_angle); + newp[i].y = (p[i].x*sin_angle + p[i].y*cos_angle); + }//for i + + if (optimizeRightAngles) { + //For rotations of 90, -90 or 180 or 0 degrees, call faster routines + if (newp[3].Distance(CxPoint2(GetHeight()-0.5f, 0.5f-GetWidth())) < 0.25) + //rotation right for circa 90 degrees (diagonal pixels less than 0.25 pixel away from 90 degree rotation destination) + return RotateRight(iDst); + if (newp[3].Distance(CxPoint2(0.5f-GetHeight(), -0.5f+GetWidth())) < 0.25) + //rotation left for ~90 degrees + return RotateLeft(iDst); + if (newp[3].Distance(CxPoint2(0.5f-GetWidth(), 0.5f-GetHeight())) < 0.25) + //rotation left for ~180 degrees + return Rotate180(iDst); + if (newp[3].Distance(p[3]) < 0.25) { + //rotation not significant + if (iDst) iDst->Copy(*this); //copy image to iDst, if required + return true; //and we're done + }//if + }//if + }//if + + //(read new dimensions from location of corners) + float minx = (float) __min(__min(newp[0].x,newp[1].x),__min(newp[2].x,newp[3].x)); + float miny = (float) __min(__min(newp[0].y,newp[1].y),__min(newp[2].y,newp[3].y)); + float maxx = (float) __max(__max(newp[0].x,newp[1].x),__max(newp[2].x,newp[3].x)); + float maxy = (float) __max(__max(newp[0].y,newp[1].y),__max(newp[2].y,newp[3].y)); + int newWidth = (int) floor(maxx-minx+0.5f); + int newHeight= (int) floor(maxy-miny+0.5f); + float ssx=((maxx+minx)- ((float) newWidth-1))/2.0f; //start for x + float ssy=((maxy+miny)- ((float) newHeight-1))/2.0f; //start for y + + float newxcenteroffset = 0.5f * newWidth; + float newycenteroffset = 0.5f * newHeight; + if (bKeepOriginalSize){ + ssx -= 0.5f * GetWidth(); + ssy -= 0.5f * GetHeight(); + } + + //create destination image + CxImage imgDest; + imgDest.CopyInfo(*this); + imgDest.Create(newWidth,newHeight,GetBpp(),GetType()); + imgDest.SetPalette(GetPalette()); +#if CXIMAGE_SUPPORT_ALPHA + if(AlphaIsValid()) imgDest.AlphaCreate(); //MTA: Fix for rotation problem when the image has an alpha channel +#endif //CXIMAGE_SUPPORT_ALPHA + + RGBQUAD rgb; //pixel colour + RGBQUAD rc; + if (replColor!=0) + rc=*replColor; + else { + rc.rgbRed=255; rc.rgbGreen=255; rc.rgbBlue=255; rc.rgbReserved=0; + }//if + float x,y; //destination location (float, with proper offset) + float origx, origy; //origin location + int destx, desty; //destination location + + y=ssy; //initialize y + if (!IsIndexed()){ //RGB24 + //optimized RGB24 implementation (direct write to destination): + BYTE *pxptr; +#if CXIMAGE_SUPPORT_ALPHA + BYTE *pxptra=0; +#endif //CXIMAGE_SUPPORT_ALPHA + for (desty=0; destyTransfer(imgDest); + else Transfer(imgDest); + + return true; +} +//////////////////////////////////////////////////////////////////////////////// +bool CxImage::Rotate180(CxImage* iDst) +{ + if (!pDib) return false; + + long wid = GetWidth(); + long ht = GetHeight(); + + CxImage imgDest; + imgDest.CopyInfo(*this); + imgDest.Create(wid,ht,GetBpp(),GetType()); + imgDest.SetPalette(GetPalette()); + +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()) imgDest.AlphaCreate(); +#endif //CXIMAGE_SUPPORT_ALPHA + + long x,y,y2; + for (y = 0; y < ht; y++){ + info.nProgress = (long)(100*y/ht); // + y2=ht-y-1; + for (x = 0; x < wid; x++){ + if(head.biClrUsed==0)//RGB + imgDest.SetPixelColor(wid-x-1, y2, BlindGetPixelColor(x, y)); + else //PALETTE + imgDest.SetPixelIndex(wid-x-1, y2, BlindGetPixelIndex(x, y)); + +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()) imgDest.AlphaSet(wid-x-1, y2,BlindAlphaGet(x, y)); +#endif //CXIMAGE_SUPPORT_ALPHA + + } + } + + //select the destination + if (iDst) iDst->Transfer(imgDest); + else Transfer(imgDest); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/** + * Resizes the image. mode can be 0 for slow (bilinear) method , + * 1 for fast (nearest pixel) method, or 2 for accurate (bicubic spline interpolation) method. + * The function is faster with 24 and 1 bpp images, slow for 4 bpp images and slowest for 8 bpp images. + */ +bool CxImage::Resample(long newx, long newy, int mode, CxImage* iDst) +{ + if (newx==0 || newy==0) return false; + + if (head.biWidth==newx && head.biHeight==newy){ + if (iDst) iDst->Copy(*this); + return true; + } + + float xScale, yScale, fX, fY; + xScale = (float)head.biWidth / (float)newx; + yScale = (float)head.biHeight / (float)newy; + + CxImage newImage; + newImage.CopyInfo(*this); + newImage.Create(newx,newy,head.biBitCount,GetType()); + newImage.SetPalette(GetPalette()); + if (!newImage.IsValid()){ + strcpy(info.szLastError,newImage.GetLastError()); + return false; + } + + switch (mode) { + case 1: // nearest pixel + { + for(long y=0; y=head.biHeight) yy = head.biHeight-1; + for(int n=-1; n<3; n++) { + r2 = r1 * KernelBSpline(b - (float)n); + xx = i_x+n; + if (xx<0) xx=0; + if (xx>=head.biWidth) xx=head.biWidth-1; + + if (head.biClrUsed){ + rgb = GetPixelColor(xx,yy); + } else { + iDst = info.pImage + yy*info.dwEffWidth + xx*3; + rgb.rgbBlue = *iDst++; + rgb.rgbGreen= *iDst++; + rgb.rgbRed = *iDst; + } + + rr += rgb.rgbRed * r2; + gg += rgb.rgbGreen * r2; + bb += rgb.rgbBlue * r2; + } + } + + if (head.biClrUsed) + newImage.SetPixelColor(x,y,RGB(rr,gg,bb)); + else { + iDst = newImage.info.pImage + y*newImage.info.dwEffWidth + x*3; + *iDst++ = (BYTE)bb; + *iDst++ = (BYTE)gg; + *iDst = (BYTE)rr; + } + + } + } + break; + } + default: // bilinear interpolation + if (!(head.biWidth>newx && head.biHeight>newy && head.biBitCount==24)) { + // (c) 1999 Steve McMahon (steve@dogma.demon.co.uk) + long ifX, ifY, ifX1, ifY1, xmax, ymax; + float ir1, ir2, ig1, ig2, ib1, ib2, dx, dy; + BYTE r,g,b; + RGBQUAD rgb1, rgb2, rgb3, rgb4; + xmax = head.biWidth-1; + ymax = head.biHeight-1; + for(long y=0; y + const long ACCURACY = 1000; + long i,j; // index for faValue + long x,y; // coordinates in source image + BYTE* pSource; + BYTE* pDest = newImage.info.pImage; + long* naAccu = new long[3 * newx + 3]; + long* naCarry = new long[3 * newx + 3]; + long* naTemp; + long nWeightX,nWeightY; + float fEndX; + long nScale = (long)(ACCURACY * xScale * yScale); + + memset(naAccu, 0, sizeof(long) * 3 * newx); + memset(naCarry, 0, sizeof(long) * 3 * newx); + + int u, v = 0; // coordinates in dest image + float fEndY = yScale - 1.0f; + for (y = 0; y < head.biHeight; y++){ + info.nProgress = (long)(100*y/head.biHeight); // + if (info.nEscape) break; + pSource = info.pImage + y * info.dwEffWidth; + u = i = 0; + fEndX = xScale - 1.0f; + if ((float)y < fEndY) { // complete source row goes into dest row + for (x = 0; x < head.biWidth; x++){ + if ((float)x < fEndX){ // complete source pixel goes into dest pixel + for (j = 0; j < 3; j++) naAccu[i + j] += (*pSource++) * ACCURACY; + } else { // source pixel is splitted for 2 dest pixels + nWeightX = (long)(((float)x - fEndX) * ACCURACY); + for (j = 0; j < 3; j++){ + naAccu[i] += (ACCURACY - nWeightX) * (*pSource); + naAccu[3 + i++] += nWeightX * (*pSource++); + } + fEndX += xScale; + u++; + } + } + } else { // source row is splitted for 2 dest rows + nWeightY = (long)(((float)y - fEndY) * ACCURACY); + for (x = 0; x < head.biWidth; x++){ + if ((float)x < fEndX){ // complete source pixel goes into 2 pixel + for (j = 0; j < 3; j++){ + naAccu[i + j] += ((ACCURACY - nWeightY) * (*pSource)); + naCarry[i + j] += nWeightY * (*pSource++); + } + } else { // source pixel is splitted for 4 dest pixels + nWeightX = (int)(((float)x - fEndX) * ACCURACY); + for (j = 0; j < 3; j++) { + naAccu[i] += ((ACCURACY - nWeightY) * (ACCURACY - nWeightX)) * (*pSource) / ACCURACY; + *pDest++ = (BYTE)(naAccu[i] / nScale); + naCarry[i] += (nWeightY * (ACCURACY - nWeightX) * (*pSource)) / ACCURACY; + naAccu[i + 3] += ((ACCURACY - nWeightY) * nWeightX * (*pSource)) / ACCURACY; + naCarry[i + 3] = (nWeightY * nWeightX * (*pSource)) / ACCURACY; + i++; + pSource++; + } + fEndX += xScale; + u++; + } + } + if (u < newx){ // possibly not completed due to rounding errors + for (j = 0; j < 3; j++) *pDest++ = (BYTE)(naAccu[i++] / nScale); + } + naTemp = naCarry; + naCarry = naAccu; + naAccu = naTemp; + memset(naCarry, 0, sizeof(int) * 3); // need only to set first pixel zero + pDest = newImage.info.pImage + (++v * newImage.info.dwEffWidth); + fEndY += yScale; + } + } + if (v < newy){ // possibly not completed due to rounding errors + for (i = 0; i < 3 * newx; i++) *pDest++ = (BYTE)(naAccu[i] / nScale); + } + delete [] naAccu; + delete [] naCarry; + } + } + +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()){ + newImage.AlphaCreate(); + for(long y=0; yTransfer(newImage); + else Transfer(newImage); + + return true; +} +//////////////////////////////////////////////////////////////////////////////// +/** + * New simpler resample. Adds new interpolation methods and simplifies code (using GetPixelColorInterpolated + * and GetAreaColorInterpolated). It also (unlike old method) interpolates alpha layer. + * + * \param newx, newy - size of resampled image + * \param inMethod - interpolation method to use (see comments at GetPixelColorInterpolated) + * If image size is being reduced, averaging is used instead (or simultaneously with) inMethod. + * \param ofMethod - what to replace outside pixels by (only significant for bordering pixels of enlarged image) + * \param iDst - pointer to destination CxImage or NULL. + * \param disableAveraging - force no averaging when shrinking images (Produces aliasing. + * You probably just want to leave this off...) + * + * \author ***bd*** 2.2004 + */ +bool CxImage::Resample2( + long newx, long newy, + InterpolationMethod const inMethod, + OverflowMethod const ofMethod, + CxImage* const iDst, + bool const disableAveraging) +{ + if (newx<=0 || newy<=0 || !pDib) return false; + + if (head.biWidth==newx && head.biHeight==newy) { + //image already correct size (just copy and return) + if (iDst) iDst->Copy(*this); + return true; + }//if + + //calculate scale of new image (less than 1 for enlarge) + float xScale, yScale; + xScale = (float)head.biWidth / (float)newx; + yScale = (float)head.biHeight / (float)newy; + + //create temporary destination image + CxImage newImage; + newImage.CopyInfo(*this); + newImage.Create(newx,newy,head.biBitCount,GetType()); + newImage.SetPalette(GetPalette()); + if (!newImage.IsValid()){ + strcpy(info.szLastError,newImage.GetLastError()); + return false; + } + + //and alpha channel if required +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()) newImage.AlphaCreate(); + BYTE *pxptra = 0; // destination alpha data +#endif + + float sX, sY; //source location + long dX,dY; //destination pixel (int value) + if ((xScale<=1 && yScale<=1) || disableAveraging) { + //image is being enlarged (or interpolation on demand) + if (!IsIndexed()) { + //RGB24 image (optimized version with direct writes) + RGBQUAD q; //pixel colour + BYTE *pxptr; //pointer to destination pixel + for(dY=0; dYTransfer(newImage); + else + Transfer(newImage); + return true; +} +//////////////////////////////////////////////////////////////////////////////// +/** + * Reduces the number of bits per pixel to nbit (1, 4 or 8). + * ppal points to a valid palette for the final image; if not supplied the function will use a standard palette. + * ppal is not necessary for reduction to 1 bpp. + */ +bool CxImage::DecreaseBpp(DWORD nbit, bool errordiffusion, RGBQUAD* ppal, DWORD clrimportant) +{ + if (!pDib) return false; + if (head.biBitCount < nbit){ + strcpy(info.szLastError,"DecreaseBpp: target BPP greater than source BPP"); + return false; + } + if (head.biBitCount == nbit){ + if (clrimportant==0) return true; + if (head.biClrImportant && (head.biClrImportant4) return false; + + CxImage tmp; + tmp.CopyInfo(*this); + tmp.Create(head.biWidth,head.biHeight,4,info.dwType); + tmp.SetPalette(GetPalette(),GetNumColors()); + if (!tmp.IsValid()){ + strcpy(info.szLastError,tmp.GetLastError()); + return false; + } + + +#if CXIMAGE_SUPPORT_SELECTION + tmp.SelectionCopy(*this); +#endif //CXIMAGE_SUPPORT_SELECTION + +#if CXIMAGE_SUPPORT_ALPHA + tmp.AlphaCopy(*this); +#endif //CXIMAGE_SUPPORT_ALPHA + + for (long y=0;y8) return false; + + CxImage tmp; + tmp.CopyInfo(*this); + tmp.Create(head.biWidth,head.biHeight,8,info.dwType); + tmp.SetPalette(GetPalette(),GetNumColors()); + if (!tmp.IsValid()){ + strcpy(info.szLastError,tmp.GetLastError()); + return false; + } + +#if CXIMAGE_SUPPORT_SELECTION + tmp.SelectionCopy(*this); +#endif //CXIMAGE_SUPPORT_SELECTION + +#if CXIMAGE_SUPPORT_ALPHA + tmp.AlphaCopy(*this); +#endif //CXIMAGE_SUPPORT_ALPHA + + for (long y=0;y24) return false; + + CxImage tmp; + tmp.CopyInfo(*this); + tmp.Create(head.biWidth,head.biHeight,24,info.dwType); + if (!tmp.IsValid()){ + strcpy(info.szLastError,tmp.GetLastError()); + return false; + } + + if (info.nBkgndIndex>=0) //translate transparency + tmp.info.nBkgndColor=GetPaletteColor((BYTE)info.nBkgndIndex); + +#if CXIMAGE_SUPPORT_SELECTION + tmp.SelectionCopy(*this); +#endif //CXIMAGE_SUPPORT_SELECTION + +#if CXIMAGE_SUPPORT_ALPHA + tmp.AlphaCopy(*this); + if (AlphaPaletteIsValid() && !AlphaIsValid()) tmp.AlphaCreate(); +#endif //CXIMAGE_SUPPORT_ALPHA + + for (long y=0;y 128) { + tmp.SetPixelIndex(x, y, 1); + error = level - 255; + } else { + tmp.SetPixelIndex(x, y, 0); + error = level; + } + + nlevel = GetPixelIndex(x + 1, y) + (error * 8) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + 1, y, level); + nlevel = GetPixelIndex(x + 2, y) + (error * 4) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + 2, y, level); + int i; + for (i = -2; i < 3; i++) { + switch (i) { + case -2: + coeff = 2; + break; + case -1: + coeff = 4; + break; + case 0: + coeff = 8; + break; + case 1: + coeff = 4; + break; + case 2: + coeff = 2; + break; + } + nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + i, y + 1, level); + } + } + } + break; + } + case 3: + { + //Stucki error diffusion (Thanks to Franco Gerevini) + int TotalCoeffSum = 42; + long error, nlevel, coeff=1; + BYTE level; + + for (long y = 0; y < head.biHeight; y++) { + info.nProgress = (long)(100 * y / head.biHeight); + if (info.nEscape) + break; + for (long x = 0; x < head.biWidth; x++) { + level = BlindGetPixelIndex(x, y); + if (level > 128) { + tmp.SetPixelIndex(x, y, 1); + error = level - 255; + } else { + tmp.SetPixelIndex(x, y, 0); + error = level; + } + + nlevel = GetPixelIndex(x + 1, y) + (error * 8) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + 1, y, level); + nlevel = GetPixelIndex(x + 2, y) + (error * 4) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + 2, y, level); + int i; + for (i = -2; i < 3; i++) { + switch (i) { + case -2: + coeff = 2; + break; + case -1: + coeff = 4; + break; + case 0: + coeff = 8; + break; + case 1: + coeff = 4; + break; + case 2: + coeff = 2; + break; + } + nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + i, y + 1, level); + } + for (i = -2; i < 3; i++) { + switch (i) { + case -2: + coeff = 1; + break; + case -1: + coeff = 2; + break; + case 0: + coeff = 4; + break; + case 1: + coeff = 2; + break; + case 2: + coeff = 1; + break; + } + nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + i, y + 2, level); + } + } + } + break; + } + case 4: + { + //Jarvis, Judice and Ninke error diffusion (Thanks to Franco Gerevini) + int TotalCoeffSum = 48; + long error, nlevel, coeff=1; + BYTE level; + + for (long y = 0; y < head.biHeight; y++) { + info.nProgress = (long)(100 * y / head.biHeight); + if (info.nEscape) + break; + for (long x = 0; x < head.biWidth; x++) { + level = BlindGetPixelIndex(x, y); + if (level > 128) { + tmp.SetPixelIndex(x, y, 1); + error = level - 255; + } else { + tmp.SetPixelIndex(x, y, 0); + error = level; + } + + nlevel = GetPixelIndex(x + 1, y) + (error * 7) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + 1, y, level); + nlevel = GetPixelIndex(x + 2, y) + (error * 5) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + 2, y, level); + int i; + for (i = -2; i < 3; i++) { + switch (i) { + case -2: + coeff = 3; + break; + case -1: + coeff = 5; + break; + case 0: + coeff = 7; + break; + case 1: + coeff = 5; + break; + case 2: + coeff = 3; + break; + } + nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + i, y + 1, level); + } + for (i = -2; i < 3; i++) { + switch (i) { + case -2: + coeff = 1; + break; + case -1: + coeff = 3; + break; + case 0: + coeff = 5; + break; + case 1: + coeff = 3; + break; + case 2: + coeff = 1; + break; + } + nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + i, y + 2, level); + } + } + } + break; + } + case 5: + { + //Sierra error diffusion (Thanks to Franco Gerevini) + int TotalCoeffSum = 32; + long error, nlevel, coeff=1; + BYTE level; + + for (long y = 0; y < head.biHeight; y++) { + info.nProgress = (long)(100 * y / head.biHeight); + if (info.nEscape) + break; + for (long x = 0; x < head.biWidth; x++) { + level = BlindGetPixelIndex(x, y); + if (level > 128) { + tmp.SetPixelIndex(x, y, 1); + error = level - 255; + } else { + tmp.SetPixelIndex(x, y, 0); + error = level; + } + + nlevel = GetPixelIndex(x + 1, y) + (error * 5) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + 1, y, level); + nlevel = GetPixelIndex(x + 2, y) + (error * 3) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + 2, y, level); + int i; + for (i = -2; i < 3; i++) { + switch (i) { + case -2: + coeff = 2; + break; + case -1: + coeff = 4; + break; + case 0: + coeff = 5; + break; + case 1: + coeff = 4; + break; + case 2: + coeff = 2; + break; + } + nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + i, y + 1, level); + } + for (i = -1; i < 2; i++) { + switch (i) { + case -1: + coeff = 2; + break; + case 0: + coeff = 3; + break; + case 1: + coeff = 2; + break; + } + nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(x + i, y + 2, level); + } + } + } + break; + } + case 6: + { + //Stevenson and Arce error diffusion (Thanks to Franco Gerevini) + int TotalCoeffSum = 200; + long error, nlevel; + BYTE level; + + for (long y = 0; y < head.biHeight; y++) { + info.nProgress = (long)(100 * y / head.biHeight); + if (info.nEscape) + break; + for (long x = 0; x < head.biWidth; x++) { + level = BlindGetPixelIndex(x, y); + if (level > 128) { + tmp.SetPixelIndex(x, y, 1); + error = level - 255; + } else { + tmp.SetPixelIndex(x, y, 0); + error = level; + } + + int tmp_index_x = x + 2; + int tmp_index_y = y; + int tmp_coeff = 32; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + + tmp_index_x = x - 3; + tmp_index_y = y + 1; + tmp_coeff = 12; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + + tmp_index_x = x - 1; + tmp_coeff = 26; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + + tmp_index_x = x + 1; + tmp_coeff = 30; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + + tmp_index_x = x + 3; + tmp_coeff = 16; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + + tmp_index_x = x - 2; + tmp_index_y = y + 2; + tmp_coeff = 12; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + + tmp_index_x = x; + tmp_coeff = 26; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + + tmp_index_x = x + 2; + tmp_coeff = 12; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + + tmp_index_x = x - 3; + tmp_index_y = y + 3; + tmp_coeff = 5; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + + tmp_index_x = x - 1; + tmp_coeff = 12; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + + tmp_index_x = x + 1; + tmp_coeff = 12; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + + tmp_index_x = x + 3; + tmp_coeff = 5; + nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum; + level = (BYTE)__min(255, __max(0, (int)nlevel)); + SetPixelIndex(tmp_index_x, tmp_index_y, level); + } + } + break; + } + case 7: + { + // Bayer ordered dither + int order = 4; + //create Bayer matrix + if (order>4) order = 4; + int size = (1 << (2*order)); + BYTE* Bmatrix = (BYTE*) malloc(size * sizeof(BYTE)); + for(int i = 0; i < size; i++) { + int n = order; + int x = i / n; + int y = i % n; + int dither = 0; + while (n-- > 0){ + dither = (((dither<<1)|((x&1) ^ (y&1)))<<1) | (y&1); + x >>= 1; + y >>= 1; + } + Bmatrix[i] = (BYTE)(dither); + } + + int scale = __max(0,(8-2*order)); + int level; + for (long y=0;y> scale; + if(level > Bmatrix[ (x % order) + order * (y % order) ]){ + tmp.SetPixelIndex(x,y,1); + } else { + tmp.SetPixelIndex(x,y,0); + } + } + } + + free(Bmatrix); + + break; + } + default: + { + // Floyd-Steinberg error diffusion (Thanks to Steve McMahon) + long error,nlevel,coeff=1; + BYTE level; + + for (long y=0;y 128){ + tmp.SetPixelIndex(x,y,1); + error = level-255; + } else { + tmp.SetPixelIndex(x,y,0); + error = level; + } + + nlevel = GetPixelIndex(x+1,y) + (error * 7)/16; + level = (BYTE)__min(255,__max(0,(int)nlevel)); + SetPixelIndex(x+1,y,level); + for(int i=-1; i<2; i++){ + switch(i){ + case -1: + coeff=3; break; + case 0: + coeff=5; break; + case 1: + coeff=1; break; + } + nlevel = GetPixelIndex(x+i,y+1) + (error * coeff)/16; + level = (BYTE)__min(255,__max(0,(int)nlevel)); + SetPixelIndex(x+i,y+1,level); + } + } + } + } + } + + tmp.SetPaletteColor(0,0,0,0); + tmp.SetPaletteColor(1,255,255,255); + Transfer(tmp); + + return true; +} +//////////////////////////////////////////////////////////////////////////////// +/** + * CropRotatedRectangle + * \param topx,topy : topmost and leftmost point of the rectangle + (topmost, and if there are 2 topmost points, the left one) + * \param width : size of the right hand side of rect, from (topx,topy) roundwalking clockwise + * \param height : size of the left hand side of rect, from (topx,topy) roundwalking clockwise + * \param angle : angle of the right hand side of rect, from (topx,topy) + * \param iDst : pointer to destination image (if 0, this image is modified) + * \author [VATI] + */ +bool CxImage::CropRotatedRectangle( long topx, long topy, long width, long height, float angle, CxImage* iDst) +{ + if (!pDib) return false; + + + long startx,starty,endx,endy; + double cos_angle = cos(angle/*/57.295779513082320877*/); + double sin_angle = sin(angle/*/57.295779513082320877*/); + + // if there is nothing special, call the original Crop(): + if ( fabs(angle)<0.0002 ) + return Crop( topx, topy, topx+width, topy+height, iDst); + + startx = __min(topx, topx - (long)(sin_angle*(double)height)); + endx = topx + (long)(cos_angle*(double)width); + endy = topy + (long)(cos_angle*(double)height + sin_angle*(double)width); + // check: corners of the rectangle must be inside + if ( IsInside( startx, topy )==false || + IsInside( endx, endy ) == false ) + return false; + + // first crop to bounding rectangle + CxImage tmp(*this, true, false, true); + // tmp.Copy(*this, true, false, true); + if (!tmp.IsValid()){ + strcpy(info.szLastError,tmp.GetLastError()); + return false; + } + if (!tmp.Crop( startx, topy, endx, endy)){ + strcpy(info.szLastError,tmp.GetLastError()); + return false; + } + + // the midpoint of the image now became the same as the midpoint of the rectangle + // rotate new image with minus angle amount + if ( false == tmp.Rotate( (float)(-angle*57.295779513082320877) ) ) // Rotate expects angle in degrees + return false; + + // crop rotated image to the original selection rectangle + endx = (tmp.head.biWidth+width)/2; + startx = (tmp.head.biWidth-width)/2; + starty = (tmp.head.biHeight+height)/2; + endy = (tmp.head.biHeight-height)/2; + if ( false == tmp.Crop( startx, starty, endx, endy ) ) + return false; + + if (iDst) iDst->Transfer(tmp); + else Transfer(tmp); + + return true; +} +//////////////////////////////////////////////////////////////////////////////// +bool CxImage::Crop(const RECT& rect, CxImage* iDst) +{ + return Crop(rect.left, rect.top, rect.right, rect.bottom, iDst); +} +//////////////////////////////////////////////////////////////////////////////// +bool CxImage::Crop(long left, long top, long right, long bottom, CxImage* iDst) +{ + if (!pDib) return false; + + long startx = __max(0L,__min(left,head.biWidth)); + long endx = __max(0L,__min(right,head.biWidth)); + long starty = head.biHeight - __max(0L,__min(top,head.biHeight)); + long endy = head.biHeight - __max(0L,__min(bottom,head.biHeight)); + + if (startx==endx || starty==endy) return false; + + if (startx>endx) {long tmp=startx; startx=endx; endx=tmp;} + if (starty>endy) {long tmp=starty; starty=endy; endy=tmp;} + + CxImage tmp(endx-startx,endy-starty,head.biBitCount,info.dwType); + if (!tmp.IsValid()){ + strcpy(info.szLastError,tmp.GetLastError()); + return false; + } + + tmp.SetPalette(GetPalette(),head.biClrUsed); + tmp.info.nBkgndIndex = info.nBkgndIndex; + tmp.info.nBkgndColor = info.nBkgndColor; + + switch (head.biBitCount) { + case 1: + case 4: + { + for(long y=starty, yd=0; y + for(long x=startx, xd=0; x> 3; + BYTE* pDest = tmp.info.pImage; + BYTE* pSrc = info.pImage + starty * info.dwEffWidth + (startx*head.biBitCount >> 3); + for(long y=starty; y + memcpy(pDest,pSrc,linelen); + pDest+=tmp.info.dwEffWidth; + pSrc+=info.dwEffWidth; + } + } + } + +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()){ // + tmp.AlphaCreate(); + if (!tmp.AlphaIsValid()) return false; + BYTE* pDest = tmp.pAlpha; + BYTE* pSrc = pAlpha + startx + starty*head.biWidth; + for (long y=starty; yTransfer(tmp); + else Transfer(tmp); + + return true; +} +//////////////////////////////////////////////////////////////////////////////// +/** + * \param xgain, ygain : can be from 0 to 1. + * \param xpivot, ypivot : is the center of the transformation. + * \param bEnableInterpolation : if true, enables bilinear interpolation. + * \return true if everything is ok + */ +bool CxImage::Skew(float xgain, float ygain, long xpivot, long ypivot, bool bEnableInterpolation) +{ + if (!pDib) return false; + float nx,ny; + + CxImage tmp(*this); + if (!tmp.IsValid()){ + strcpy(info.szLastError,tmp.GetLastError()); + return false; + } + + long xmin,xmax,ymin,ymax; + if (pSelection){ + xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; + ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; + } else { + xmin = ymin = 0; + xmax = head.biWidth; ymax=head.biHeight; + } + for(long y=ymin; y top) || (x < left) || (x > right)) { + tmp.SetPixelIndex(x,y, pixel); + } else { + tmp.SetPixelIndex(x,y,GetPixelIndex(x-left,y-bottom)); + } + } + } + break; + } + case 8: + case 24: + { + if (head.biBitCount == 8) { + BYTE pixel = tmp.GetNearestIndex( canvascolor); + memset(tmp.info.pImage, pixel, + (tmp.info.dwEffWidth * newHeight)); + } else { + for (long y = 0; y < newHeight; ++y) { + BYTE *pDest = tmp.info.pImage + (y * tmp.info.dwEffWidth); + for (long x = 0; x < newWidth; ++x) { + *pDest++ = canvascolor.rgbBlue; + *pDest++ = canvascolor.rgbGreen; + *pDest++ = canvascolor.rgbRed; + } + } + } + + BYTE* pDest = tmp.info.pImage + (tmp.info.dwEffWidth * bottom) + (left*(head.biBitCount >> 3)); + BYTE* pSrc = info.pImage; + for(long y=bottom; y <= top; y++){ + info.nProgress = (long)(100*y/(1 + top - bottom)); + memcpy(pDest,pSrc,(head.biBitCount >> 3) * (right - left + 1)); + pDest+=tmp.info.dwEffWidth; + pSrc+=info.dwEffWidth; + } + } + } + +#if CXIMAGE_SUPPORT_SELECTION + if (SelectionIsValid()){ + if (!tmp.SelectionCreate()) + return false; + BYTE* pSrc = SelectionGetPointer(); + BYTE* pDst = tmp.SelectionGetPointer(left,bottom); + for(long y=bottom; y <= top; y++){ + memcpy(pDst,pSrc, (right - left + 1)); + pSrc+=head.biWidth; + pDst+=tmp.head.biWidth; + } + tmp.info.rSelectionBox.left = info.rSelectionBox.left + left; + tmp.info.rSelectionBox.right = info.rSelectionBox.right + left; + tmp.info.rSelectionBox.top = info.rSelectionBox.top + bottom; + tmp.info.rSelectionBox.bottom = info.rSelectionBox.bottom + bottom; + } +#endif //CXIMAGE_SUPPORT_SELECTION + +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()){ + if (!tmp.AlphaCreate()) + return false; + tmp.AlphaSet(canvascolor.rgbReserved); + BYTE* pSrc = AlphaGetPointer(); + BYTE* pDst = tmp.AlphaGetPointer(left,bottom); + for(long y=bottom; y <= top; y++){ + memcpy(pDst,pSrc, (right - left + 1)); + pSrc+=head.biWidth; + pDst+=tmp.head.biWidth; + } + } +#endif //CXIMAGE_SUPPORT_ALPHA + + //select the destination + if (iDst) iDst->Transfer(tmp); + else Transfer(tmp); + + return true; +} +//////////////////////////////////////////////////////////////////////////////// +bool CxImage::Expand(long newx, long newy, RGBQUAD canvascolor, CxImage* iDst) +{ + //thanks to + + if (!pDib) return false; + + if ((newx < head.biWidth) || (newy < head.biHeight)) return false; + + int nAddLeft = (newx - head.biWidth) / 2; + int nAddTop = (newy - head.biHeight) / 2; + + return Expand(nAddLeft, nAddTop, newx - (head.biWidth + nAddLeft), newy - (head.biHeight + nAddTop), canvascolor, iDst); +} +//////////////////////////////////////////////////////////////////////////////// +/** + * Resamples the image with the correct aspect ratio, and fills the borders. + * \param newx, newy = thumbnail size. + * \param canvascolor = border color. + * \param iDst = pointer to destination image (if it's 0, this image is modified). + * \return true if everything is ok. + * \author [Colin Urquhart] + */ +bool CxImage::Thumbnail(long newx, long newy, RGBQUAD canvascolor, CxImage* iDst) +{ + if (!pDib) return false; + + if ((newx <= 0) || (newy <= 0)) return false; + + CxImage tmp(*this); + if (!tmp.IsValid()){ + strcpy(info.szLastError,tmp.GetLastError()); + return false; + } + + // determine whether we need to shrink the image + if ((head.biWidth > newx) || (head.biHeight > newy)) { + float fScale; + float fAspect = (float) newx / (float) newy; + if (fAspect * head.biHeight > head.biWidth) { + fScale = (float) newy / head.biHeight; + } else { + fScale = (float) newx / head.biWidth; + } + tmp.Resample((long) (fScale * head.biWidth), (long) (fScale * head.biHeight), 0); + } + + // expand the frame + tmp.Expand(newx, newy, canvascolor, iDst); + + //select the destination + if (iDst) iDst->Transfer(tmp); + else Transfer(tmp); + return true; +} +//////////////////////////////////////////////////////////////////////////////// +/** + * Perform circle_based transformations. + * \param type - for different transformations + * - 0 for normal (proturberant) FishEye + * - 1 for reverse (concave) FishEye + * - 2 for Swirle + * - 3 for Cilinder mirror + * - 4 for bathroom + * + * \param rmax - effect radius. If 0, the whole image is processed + * \param Koeff - only for swirle + * \author Arkadiy Olovyannikov ark(at)msun(dot)ru + */ +bool CxImage::CircleTransform(int type,long rmax,float Koeff) +{ + if (!pDib) return false; + + long nx,ny; + double angle,radius,rnew; + + CxImage tmp(*this); + if (!tmp.IsValid()){ + strcpy(info.szLastError,tmp.GetLastError()); + return false; + } + + long xmin,xmax,ymin,ymax,xmid,ymid; + if (pSelection){ + xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; + ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; + } else { + xmin = ymin = 0; + xmax = head.biWidth; ymax=head.biHeight; + } + + xmid = (long) (tmp.GetWidth()/2); + ymid = (long) (tmp.GetHeight()/2); + + if (!rmax) rmax=(long)sqrt((float)((xmid-xmin)*(xmid-xmin)+(ymid-ymin)*(ymid-ymin))); + if (Koeff==0.0f) Koeff=1.0f; + + for(long y=ymin; yhead.biWidth || newy>head.biHeight) { + //let me repeat... this method can't enlarge image + strcpy(info.szLastError,"QIShrink can't enlarge image"); + return false; + } + + if (newx==head.biWidth && newy==head.biHeight) { + //image already correct size (just copy and return) + if (iDst) iDst->Copy(*this); + return true; + }//if + + //create temporary destination image + CxImage newImage; + newImage.CopyInfo(*this); + newImage.Create(newx,newy,(bChangeBpp)?24:head.biBitCount,GetType()); + newImage.SetPalette(GetPalette()); + if (!newImage.IsValid()){ + strcpy(info.szLastError,newImage.GetLastError()); + return false; + } + + //and alpha channel if required +#if CXIMAGE_SUPPORT_ALPHA + if (AlphaIsValid()) newImage.AlphaCreate(); +#endif + + const int oldx = head.biWidth; + const int oldy = head.biHeight; + + int accuCellSize = 4; +#if CXIMAGE_SUPPORT_ALPHA + BYTE *alphaPtr; + if (AlphaIsValid()) accuCellSize=5; +#endif + + unsigned int *accu = new unsigned int[newx*accuCellSize]; //array for suming pixels... one pixel for every destination column + unsigned int *accuPtr; //pointer for walking through accu + //each cell consists of blue, red, green component and count of pixels summed in this cell + memset(accu, 0, newx * accuCellSize * sizeof(unsigned int)); //clear accu + + if (!IsIndexed()) { + //RGB24 version with pointers + BYTE *destPtr, *srcPtr, *destPtrS, *srcPtrS; //destination and source pixel, and beginnings of current row + srcPtrS=(BYTE*)BlindGetPixelPointer(0,0); + destPtrS=(BYTE*)newImage.BlindGetPixelPointer(0,0); + int ex=0, ey=0; //ex and ey replace division... + int dy=0; + //(we just add pixels, until by adding newx or newy we get a number greater than old size... then + // it's time to move to next pixel) + + for(int y=0; yoldx) { //when we reach oldx, it's time to move to new slot + accuPtr += accuCellSize; + ex -= oldx; //(substract oldx from ex and resume from there on) + }//if (ex overflow) + }//for x + + if (ey>=oldy) { //now when this happens + ey -= oldy; //it's time to move to new destination row + destPtr = destPtrS; //reset pointers to proper initial values + accuPtr = accu; +#if CXIMAGE_SUPPORT_ALPHA + alphaPtr = newImage.AlphaGetPointer(0, dy++); +#endif + for (int k=0; koldx) { //when we reach oldx, it's time to move to new slot + accuPtr += accuCellSize; + ex -= oldx; //(substract oldx from ex and resume from there on) + }//if (ex overflow) + }//for x + + if (ey>=oldy) { //now when this happens + ey -= oldy; //it's time to move to new destination row + accuPtr = accu; + for (int dx=0; dxTransfer(newImage); + else + Transfer(newImage); + return true; + +} + +//////////////////////////////////////////////////////////////////////////////// +#endif //CXIMAGE_SUPPORT_TRANSFORMATION