// xImaTran.cpp : Transformation functions /* 07/08/2001 v1.00 - ing.davide.pizzolato@libero.it * CxImage version 5.71 25/Apr/2003 */ #include "ximage.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;iinfo.pImage; for(long y=0; y < head.biHeight; y++){ memcpy(iDst,iSrc,info.dwEffWidth); iSrc-=info.dwEffWidth; iDst+=info.dwEffWidth; } #if CXIMAGE_SUPPORT_ALPHA imatmp->AlphaFlip(); #endif //CXIMAGE_SUPPORT_ALPHA Transfer(*imatmp); delete imatmp; return true; } //////////////////////////////////////////////////////////////////////////////// bool CxImage::Mirror() { if (!pDib) return false; CxImage* imatmp = new CxImage(*this,false,false,true); if (!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 CXIMAGE_SUPPORT_ALPHA imatmp->AlphaMirror(); #endif //CXIMAGE_SUPPORT_ALPHA Transfer(*imatmp); delete imatmp; return true; } //////////////////////////////////////////////////////////////////////////////// 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()); long x,x2,y,dlineup; // Speedy rotate for BW images if (head.biBitCount == 1) { BYTE *sbits, *dbits, *dbitsmax, bitpos, *nrow,*srcdisp; div_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 = div(y + dlineup, 8); // set bit pos of src column byte bitpos = 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; } } } } else { for (x = 0; x < newWidth; x++){ info.nProgress = (long)(100*x/newWidth); // x2=newWidth-x-1; for (y = 0; y < newHeight; y++){ if(head.biClrUsed==0) //RGB imgDest.SetPixelColor(x, y, GetPixelColor(y, x2)); else //PALETTE imgDest.SetPixelIndex(x, y, GetPixelIndex(y, x2)); } } } #if CXIMAGE_SUPPORT_ALPHA if (AlphaIsValid()){ imgDest.AlphaCreate(); for (x = 0; x < newWidth; x++){ x2=newWidth-x-1; for (y = 0; y < newHeight; y++){ imgDest.AlphaSet(x,y,AlphaGet(y, x2)); } } } #endif //CXIMAGE_SUPPORT_ALPHA //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()); long x,y,y2; // Speedy rotate for BW images if (head.biBitCount == 1) { BYTE *sbits, *dbits, *dbitsmax, bitpos, *nrow,*srcdisp; div_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 = div(y, 8); // set bit pos of src column byte bitpos = 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; } } } } else { for (y = 0; y < newHeight; y++){ info.nProgress = (long)(100*y/newHeight); // y2=newHeight-y-1; for (x = 0; x < newWidth; x++){ if(head.biClrUsed==0) //RGB imgDest.SetPixelColor(x, y, GetPixelColor(y2, x)); else //PALETTE imgDest.SetPixelIndex(x, y, GetPixelIndex(y2, x)); } } } #if CXIMAGE_SUPPORT_ALPHA if (AlphaIsValid()){ imgDest.AlphaCreate(); for (y = 0; y < newHeight; y++){ y2=newHeight-y-1; for (x = 0; x < newWidth; x++){ imgDest.AlphaSet(x,y,AlphaGet(y2, x)); } } } #endif //CXIMAGE_SUPPORT_ALPHA //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; // $Id: FilterRotate.cpp,v 1.10 2000/12/18 22:42:53 uzadow Exp $ // 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-1,nHeight}; POINT newP1,newP2,newP3,newP4, leftTop, rightTop, leftBottom, rightBottom; newP1.x = p1.x; newP1.y = p1.y; newP2.x = (long)(p2.x*cos_angle - p2.y*sin_angle); newP2.y = (long)(p2.x*sin_angle + p2.y*cos_angle); newP3.x = (long)(p3.x*cos_angle - p3.y*sin_angle); newP3.y = (long)(p3.x*sin_angle + p3.y*cos_angle); newP4.x = (long)(p4.x*cos_angle - p4.y*sin_angle); newP4.y = (long)(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 = rightTop.x - leftTop.x; newHeight= 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 = leftTop.y, newY = 0; y<=leftBottom.y; y++,newY++){ info.nProgress = (long)(100*newY/newHeight); if (info.nEscape) break; for (x = leftTop.x, newX = 0; x<=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 = leftTop.y, newY = 0; y<=leftBottom.y; y++,newY++){ info.nProgress = (long)(100*newY/newHeight); if (info.nEscape) break; for (x = leftTop.x, newX = 0; x<=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; } //////////////////////////////////////////////////////////////////////////////// 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, GetPixelColor(x, y)); else //PALETTE imgDest.SetPixelIndex(wid-x-1, y2, GetPixelIndex(x, y)); #if CXIMAGE_SUPPORT_ALPHA if (AlphaIsValid()) imgDest.AlphaSet(wid-x-1, y2,AlphaGet(x, y)); #endif //CXIMAGE_SUPPORT_ALPHA } } //select the destination if (iDst) iDst->Transfer(imgDest); else Transfer(imgDest); return true; } //////////////////////////////////////////////////////////////////////////////// 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()) return false; if (head.biWidth==newx && head.biHeight==newy){ Transfer(newImage); return true; } switch (mode) { case 1: // nearest pixel { for(long y=0; y=head.biWidth) xx=head.biWidth-1; if (yy>=head.biHeight) yy=head.biHeight-1; rgb = GetPixelColor(xx,yy); rr += rgb.rgbRed * r1 * r2; gg += rgb.rgbGreen * r1 * r2; bb += rgb.rgbBlue * r1 * r2; } } newImage.SetPixelColor(x,y,RGB(rr,gg,bb)); } } break; } default: // bilinear interpolation if (!(head.biWidth>newx && head.biHeight>newy && head.biBitCount==24)) { //© 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; } //////////////////////////////////////////////////////////////////////////////// bool CxImage::DecreaseBpp(DWORD nbit, bool errordiffusion, RGBQUAD* ppal) { if (!pDib) return false; if (head.biBitCount < nbit) return false; if (head.biBitCount == nbit) return true; long er,eg,eb; RGBQUAD c,ce; CxImage tmp; tmp.CopyInfo(*this); tmp.Create(head.biWidth,head.biHeight,(WORD)nbit,info.dwType); #if CXIMAGE_SUPPORT_SELECTION tmp.SelectionCopy(*this); #endif //CXIMAGE_SUPPORT_SELECTION #if CXIMAGE_SUPPORT_ALPHA tmp.AlphaCopy(*this); #endif //CXIMAGE_SUPPORT_ALPHA switch (tmp.head.biBitCount){ case 1: if (ppal) tmp.SetPalette(ppal,16); else { tmp.SetPaletteColor(0,0,0,0); tmp.SetPaletteColor(1,255,255,255); } break; case 4: if (ppal) tmp.SetPalette(ppal,16); else tmp.SetStdPalette(); break; case 8: if (ppal) tmp.SetPalette(ppal); else tmp.SetStdPalette(); break; default: return false; } for (long y=0;y4) return false; CxImage tmp; tmp.CopyInfo(*this); tmp.Create(head.biWidth,head.biHeight,4,info.dwType); tmp.SetPalette(GetPalette(),GetNumColors()); #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 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 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 * 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; } //////////////////////////////////////////////////////////////////////////////// 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); 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); for(long y=starty; y memcpy(pDest,pSrc,tmp.info.dwEffWidth); pDest+=tmp.info.dwEffWidth; pSrc+=info.dwEffWidth; } } } //select the destination if (iDst) iDst->Transfer(tmp); else Transfer(tmp); return true; } //////////////////////////////////////////////////////////////////////////////// float CxImage::b3spline(float x) { float a, b, c, d; if((x + 2.0f) <= 0.0f) a = 0.0f; else a = (float)pow((x + 2.0f), 3.0f); if((x + 1.0f) <= 0.0f) b = 0.0f; else b = (float)pow((x + 1.0f), 3.0f); if(x <= 0) c = 0.0f; else c = (float)pow(x, 3.0f); if((x - 1.0f) <= 0.0f) d = 0.0f; else d = (float)pow((x - 1.0f), 3.0f); return (0.16666666666666666667f * (a - (4.0f * b) + (6.0f * c) - (4.0f * d))); } //////////////////////////////////////////////////////////////////////////////// bool CxImage::Skew(float xgain, float ygain, long xpivot, long ypivot) { if (!pDib) return false; long nx,ny; CxImage tmp(*this,pSelection!=0,true,true); 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