// 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;i<head.biClrUsed;i++){
gray=(int)RGB2GRAY(ppal[i].rgbRed,ppal[i].rgbGreen,ppal[i].rgbBlue);
ppal[i].rgbBlue = (BYTE)gray;
}
// preserve transparency
if (info.nBkgndIndex != -1) 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<head.biSizeImage;i++) img[i]=ppal[img[i]].rgbBlue;
SetGrayPalette();
}
//transform to 8 bit gray scale
if (head.biBitCount==4 || head.biBitCount==1){
CxImage ima(head.biWidth,head.biHeight,8,info.dwType);
ima.SetGrayPalette();
BYTE *img=ima.GetBits();
long l=ima.GetEffWidth();
for (long y=0;y<head.biHeight;y++){
for (long x=0;x<head.biWidth; x++){
img[x+y*l]=ppal[GetPixelIndex(x,y)].rgbBlue;
}
}
Transfer(ima);
}
} else { //from RGB to 8 bit gray scale
BYTE *iSrc=info.pImage;
CxImage ima(head.biWidth,head.biHeight,8,info.dwType);
ima.SetGrayPalette();
#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;
}
bool CxImage::Flip()
{
if (!pDib) return false;
CxImage* imatmp = new CxImage(*this,false,false,true);
if (!imatmp) return false;
BYTE *iSrc,*iDst;
iSrc=info.pImage + (head.biHeight-1)*info.dwEffWidth;
iDst=imatmp->info.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 <Robert Abram>
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); //<Anatoly Ivasyuk>
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 <Robert Abram>
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); //<Anatoly Ivasyuk>
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<info.rSelectionBox.top; y++){
for(long x=info.rSelectionBox.left; x<info.rSelectionBox.right; x++){
#if CXIMAGE_SUPPORT_SELECTION
if (SelectionIsInside(x,y))
#endif //CXIMAGE_SUPPORT_SELECTION
{
SetPixelIndex(x,y,(BYTE)(255-GetPixelIndex(x,y)));
}
}
}
} else {
for(long y=0; y<head.biHeight; y++){
for(long x=0; x<head.biWidth; x++){
SetPixelIndex(x,y,(BYTE)(255-GetPixelIndex(x,y)));
}
}
}
} else { //PALETTE, full image
RGBQUAD* ppal=GetPalette();
for(DWORD i=0;i<head.biClrUsed;i++){
ppal[i].rgbBlue =(BYTE)(255-ppal[i].rgbBlue);
ppal[i].rgbGreen =(BYTE)(255-ppal[i].rgbGreen);
ppal[i].rgbRed =(BYTE)(255-ppal[i].rgbRed);
}
}
} else {
if (pSelection==NULL){ //RGB, full image
BYTE *iSrc=info.pImage;
for(unsigned long i=0; i < head.biSizeImage ; i++){
*iSrc=(BYTE)~(*(iSrc));
iSrc++;
}
} else { // RGB with selection
RGBQUAD color;
for(long y=info.rSelectionBox.bottom; y<info.rSelectionBox.top; y++){
for(long x=info.rSelectionBox.left; x<info.rSelectionBox.right; x++){
#if CXIMAGE_SUPPORT_SELECTION
if (SelectionIsInside(x,y))
#endif //CXIMAGE_SUPPORT_SELECTION
{
color = GetPixelColor(x,y);
color.rgbRed = (BYTE)(255-color.rgbRed);
color.rgbGreen = (BYTE)(255-color.rgbGreen);
color.rgbBlue = (BYTE)(255-color.rgbBlue);
SetPixelColor(x,y,color);
}
}
}
}
//<DP> 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); //<Anatoly Ivasyuk>
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<newy; y++){
info.nProgress = (long)(100*y/newy);
if (info.nEscape) break;
fY = y * yScale;
for(long x=0; x<newx; x++){
fX = x * xScale;
newImage.SetPixelColor(x,y,GetPixelColor((long)fX,(long)fY));
}
}
break;
}
case 2: // bicubic interpolation by Blake L. Carlson <blake-carlson(at)uiowa(dot)edu
{
float f_x, f_y, a, b, rr, gg, bb, r1, r2;
int i_x, i_y, xx, yy;
RGBQUAD rgb;
for(long y=0; y<newy; y++){
info.nProgress = (long)(100*y/newy);
if (info.nEscape) break;
f_y = (float) y * yScale;
i_y = (int) floor(f_y);
a = f_y - (float)floor(f_y);
for(long x=0; x<newx; x++){
f_x = (float) x * xScale;
i_x = (int) floor(f_x);
b = f_x - (float)floor(f_x);
rr = gg = bb = 0.0F;
for(int m=-1; m<3; m++) {
r1 = b3spline((float) m - a);
for(int n=-1; n<3; n++) {
r2 = b3spline(-1.0F*((float)n - b));
xx = i_x+n+2;
yy = i_y+m+2;
if (xx<0) xx=0;
if (yy<0) yy=0;
if (xx>=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<newy; y++){
info.nProgress = (long)(100*y/newy);
if (info.nEscape) break;
fY = y * yScale;
ifY = (int)fY;
ifY1 = min(ymax, ifY+1);
dy = fY - ifY;
for(long x=0; x<newx; x++){
fX = x * xScale;
ifX = (int)fX;
ifX1 = min(xmax, ifX+1);
dx = fX - ifX;
// Interpolate using the four nearest pixels in the source
if (head.biClrUsed){
rgb1=GetPaletteColor(GetPixelIndex(ifX,ifY));
rgb2=GetPaletteColor(GetPixelIndex(ifX1,ifY));
rgb3=GetPaletteColor(GetPixelIndex(ifX,ifY1));
rgb4=GetPaletteColor(GetPixelIndex(ifX1,ifY1));
}
else {
BYTE* iDst;
iDst = info.pImage + ifY*info.dwEffWidth + ifX*3;
rgb1.rgbBlue = *iDst++; rgb1.rgbGreen= *iDst++; rgb1.rgbRed =*iDst;
iDst = info.pImage + ifY*info.dwEffWidth + ifX1*3;
rgb2.rgbBlue = *iDst++; rgb2.rgbGreen= *iDst++; rgb2.rgbRed =*iDst;
iDst = info.pImage + ifY1*info.dwEffWidth + ifX*3;
rgb3.rgbBlue = *iDst++; rgb3.rgbGreen= *iDst++; rgb3.rgbRed =*iDst;
iDst = info.pImage + ifY1*info.dwEffWidth + ifX1*3;
rgb4.rgbBlue = *iDst++; rgb4.rgbGreen= *iDst++; rgb4.rgbRed =*iDst;
}
// Interplate in x direction:
ir1 = rgb1.rgbRed * (1 - dy) + rgb3.rgbRed * dy;
ig1 = rgb1.rgbGreen * (1 - dy) + rgb3.rgbGreen * dy;
ib1 = rgb1.rgbBlue * (1 - dy) + rgb3.rgbBlue * dy;
ir2 = rgb2.rgbRed * (1 - dy) + rgb4.rgbRed * dy;
ig2 = rgb2.rgbGreen * (1 - dy) + rgb4.rgbGreen * dy;
ib2 = rgb2.rgbBlue * (1 - dy) + rgb4.rgbBlue * dy;
// Interpolate in y:
r = (BYTE)(ir1 * (1 - dx) + ir2 * dx);
g = (BYTE)(ig1 * (1 - dx) + ig2 * dx);
b = (BYTE)(ib1 * (1 - dx) + ib2 * dx);
// Set output
newImage.SetPixelColor(x,y,RGB(r,g,b));
}
}
} else {
//high resolution shrink, thanks to Henrik Stellmann <henrik.stellmann@volleynet.de>
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); //<Anatoly Ivasyuk>
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; y<newy; y++){
fY = y * yScale;
for(long x=0; x<newx; x++){
fX = x * xScale;
newImage.AlphaSet(x,y,AlphaGet((long)fX,(long)fY));
}
}
}
#endif //CXIMAGE_SUPPORT_ALPHA
//select the destination
if (iDst) iDst->Transfer(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;y<head.biHeight;y++){
if (info.nEscape) break;
info.nProgress = (long)(100*y/head.biHeight);
for (long x=0;x<head.biWidth;x++){
if (!errordiffusion){
tmp.SetPixelColor(x,y,GetPixelColor(x,y));
} else {
c=GetPixelColor(x,y);
tmp.SetPixelColor(x,y,c);
ce=tmp.GetPixelColor(x,y);
er=(long)c.rgbRed - (long)ce.rgbRed;
eg=(long)c.rgbGreen - (long)ce.rgbGreen;
eb=(long)c.rgbBlue - (long)ce.rgbBlue;
c = GetPixelColor(x+1,y);
c.rgbRed = (BYTE)min(255L,max(0L,(long)c.rgbRed + ((er*7)/16)));
c.rgbGreen = (BYTE)min(255L,max(0L,(long)c.rgbGreen + ((eg*7)/16)));
c.rgbBlue = (BYTE)min(255L,max(0L,(long)c.rgbBlue + ((eb*7)/16)));
SetPixelColor(x+1,y,c);
int coeff;
for(int i=-1; i<2; i++){
switch(i){
case -1:
coeff=2; break;
case 0:
coeff=4; break;
case 1:
coeff=1; break;
}
c = GetPixelColor(x+i,y+1);
c.rgbRed = (BYTE)min(255L,max(0L,(long)c.rgbRed + ((er * coeff)/16)));
c.rgbGreen = (BYTE)min(255L,max(0L,(long)c.rgbGreen + ((eg * coeff)/16)));
c.rgbBlue = (BYTE)min(255L,max(0L,(long)c.rgbBlue + ((eb * coeff)/16)));
SetPixelColor(x+i,y+1,c);
}
}
}
}
if (head.biBitCount==1){
tmp.SetPaletteColor(0,0,0,0);
tmp.SetPaletteColor(1,255,255,255);
}
Transfer(tmp);
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool CxImage::IncreaseBpp(DWORD nbit)
{
if (!pDib) return false;
switch (nbit){
case 4:
{
if (head.biBitCount==4) return true;
if (head.biBitCount>4) 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;y<head.biHeight;y++){
if (info.nEscape) break;
for (long x=0;x<head.biWidth;x++){
tmp.SetPixelIndex(x,y,GetPixelIndex(x,y));
}
}
Transfer(tmp);
return true;
}
case 8:
{
if (head.biBitCount==8) return true;
if (head.biBitCount>8) 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;y<head.biHeight;y++){
if (info.nEscape) break;
for (long x=0;x<head.biWidth;x++){
tmp.SetPixelIndex(x,y,GetPixelIndex(x,y));
}
}
Transfer(tmp);
return true;
}
case 24:
{
if (head.biBitCount==24) return true;
if (head.biBitCount>24) 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<head.biHeight;y++){
if (info.nEscape) break;
for (long x=0;x<head.biWidth;x++){
tmp.SetPixelColor(x,y,GetPixelColor(x,y),true);
}
}
Transfer(tmp);
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool CxImage::Dither(long method)
{
if (!pDib) return false;
if (head.biBitCount == 1) return true;
switch (method){
case 1:
{
// Multi-Level Ordered-Dithering by Kenny Hoff (Oct. 12, 1995)
#define NumRows 4
#define NumCols 4
#define NumIntensityLevels 2
#define NumRowsLessOne (NumRows-1)
#define NumColsLessOne (NumCols-1)
#define RowsXCols (NumRows*NumCols)
#define MaxIntensityVal 255
#define MaxDitherIntensityVal (NumRows*NumCols*(NumIntensityLevels-1))
int DitherMatrix[NumRows][NumCols] = {{0,8,2,10}, {12,4,14,6}, {3,11,1,9}, {15,7,13,5} };
unsigned char Intensity[NumIntensityLevels] = { 0,1 }; // 2 LEVELS B/W
//unsigned char Intensity[NumIntensityLevels] = { 0,255 }; // 2 LEVELS
//unsigned char Intensity[NumIntensityLevels] = { 0,127,255 }; // 3 LEVELS
//unsigned char Intensity[NumIntensityLevels] = { 0,85,170,255 }; // 4 LEVELS
//unsigned char Intensity[NumIntensityLevels] = { 0,63,127,191,255 }; // 5 LEVELS
//unsigned char Intensity[NumIntensityLevels] = { 0,51,102,153,204,255 }; // 6 LEVELS
//unsigned char Intensity[NumIntensityLevels] = { 0,42,85,127,170,213,255 }; // 7 LEVELS
//unsigned char Intensity[NumIntensityLevels] = { 0,36,73,109,145,182,219,255 }; // 8 LEVELS
int DitherIntensity, DitherMatrixIntensity, Offset, DeviceIntensity;
unsigned char DitherValue;
GrayScale();
CxImage tmp(head.biWidth,head.biHeight,1,info.dwType);
#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;y<head.biHeight;y++){
info.nProgress = (long)(100*y/head.biHeight);
if (info.nEscape) break;
for (long x=0;x<head.biWidth;x++){
DeviceIntensity = GetPixelIndex(x,y);
DitherIntensity = DeviceIntensity*MaxDitherIntensityVal/MaxIntensityVal;
DitherMatrixIntensity = DitherIntensity % RowsXCols;
Offset = DitherIntensity / RowsXCols;
if (DitherMatrix[y&NumRowsLessOne][x&NumColsLessOne] < DitherMatrixIntensity)
DitherValue = Intensity[1+Offset];
else
DitherValue = Intensity[0+Offset];
tmp.SetPixelIndex(x,y,DitherValue);
}
}
tmp.SetPaletteColor(0,0,0,0);
tmp.SetPaletteColor(1,255,255,255);
Transfer(tmp);
break;
}
default:
{
// Floyd-Steinberg error diffusion (Thanks to Steve McMahon)
long error,nlevel,coeff;
BYTE level;
GrayScale();
CxImage tmp(head.biWidth,head.biHeight,1,info.dwType);
#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;y<head.biHeight;y++){
info.nProgress = (long)(100*y/head.biHeight);
if (info.nEscape) break;
for (long x=0;x<head.biWidth;x++){
level=GetPixelIndex(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)/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<endy; y++, yd++){
info.nProgress = (long)(100*y/endy); //<Anatoly Ivasyuk>
for(long x=startx, xd=0; x<endx; x++, xd++){
tmp.SetPixelIndex(xd,yd,GetPixelIndex(x,y));
}
}
break;
}
case 8:
case 24:
{
BYTE* pDest = tmp.info.pImage;
BYTE* pSrc = info.pImage + starty * info.dwEffWidth + (startx*head.biBitCount >> 3);
for(long y=starty; y<endy; y++){
info.nProgress = (long)(100*y/endy); //<Anatoly Ivasyuk>
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<ymax; y++){
info.nProgress = (long)(100*y/head.biHeight);
if (info.nEscape) break;
for(long x=xmin; x<xmax; x++){
#if CXIMAGE_SUPPORT_SELECTION
if (SelectionIsInside(x,y))
#endif //CXIMAGE_SUPPORT_SELECTION
{
nx = x + (long)(xgain*(y - ypivot));
ny = y + (long)(ygain*(x - xpivot));
if (head.biClrUsed==0){
tmp.SetPixelColor(x,y,GetPixelColor(nx,ny));
} else {
tmp.SetPixelIndex(x,y,GetPixelIndex(nx,ny));
}
#if CXIMAGE_SUPPORT_ALPHA
tmp.AlphaSet(x,y,AlphaGet(nx,ny));
#endif //CXIMAGE_SUPPORT_ALPHA
}
}
}
Transfer(tmp);
return true;
}
////////////////////////////////////////////////////////////////////////////////
#endif //CXIMAGE_SUPPORT_TRANSFORMATION