X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;ds=sidebyside;f=utilities%2FCxImage%2Fximatran.cpp;h=d3aac0182ae0e0532080d1c4c72e4abcf7c110e7;hb=fa1bed48028c3365d90cbb2c7bd43cefd35d16f0;hp=9f136a05d69ff48d8fc91237e5258727863e9309;hpb=3fd15028ab81e6746d3af96695526d7d973a26c8;p=clitk.git
diff --git a/utilities/CxImage/ximatran.cpp b/utilities/CxImage/ximatran.cpp
index 9f136a0..d3aac01 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