|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include <tier0/platform.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "bitmap/float_bm.h"
#include <tier2/tier2.h>
#include "bitmap/imageformat.h"
#include "bitmap/tgaloader.h"
#include "tier1/strtools.h"
#include "filesystem.h"
#define SQ(x) ((x)*(x))
// linear interpolate between 2 control points (L,R)
inline float LinInterp(float frac, float L, float R) { return (((R-L) * frac) + L); }
// bilinear interpolate between 4 control points (UL,UR,LL,LR)
inline float BiLinInterp(float Xfrac, float Yfrac, float UL, float UR, float LL, float LR) { float iu = LinInterp(Xfrac, UL, UR); float il = LinInterp(Xfrac, LL, LR);
return( LinInterp(Yfrac, iu, il) ); }
FloatBitMap_t::FloatBitMap_t(int width, int height) { RGBAData=0; AllocateRGB(width,height); }
FloatBitMap_t::FloatBitMap_t(FloatBitMap_t const *orig) { RGBAData=0; AllocateRGB(orig->Width,orig->Height); memcpy(RGBAData,orig->RGBAData,Width*Height*sizeof(float)*4); }
static char GetChar(FileHandle_t &f) { char a; g_pFullFileSystem->Read(&a,1,f); return a; }
static int GetInt(FileHandle_t &f) { char buf[100]; char *bout=buf; for(;;) { char c=GetChar(f); if ((c<'0') || (c>'9')) break; *(bout++)=c; } *(bout++)=0; return atoi(buf);
}
#define PFM_MAX_XSIZE 2048
bool FloatBitMap_t::LoadFromPFM(char const *fname) { FileHandle_t f = g_pFullFileSystem->Open(fname, "rb"); if (f) { if( ( GetChar(f) == 'P' ) && (GetChar(f) == 'F' ) && ( GetChar(f) == '\n' )) { Width=GetInt(f); Height=GetInt(f);
// eat crap until the next newline
while( GetChar(f) != '\n') { }
// printf("file %s w=%d h=%d\n",fname,Width,Height);
AllocateRGB(Width,Height);
for( int y = Height-1; y >= 0; y-- ) { float linebuffer[PFM_MAX_XSIZE*3]; g_pFullFileSystem->Read(linebuffer,3*Width*sizeof(float),f); for(int x=0;x<Width;x++) { for(int c=0;c<3;c++) { Pixel(x,y,c)=linebuffer[x*3+c]; } } } } g_pFullFileSystem->Close( f ); // close file after reading
} return (RGBAData!=0); }
bool FloatBitMap_t::WritePFM(char const *fname) { FileHandle_t f = g_pFullFileSystem->Open(fname, "wb");
if ( f ) { g_pFullFileSystem->FPrintf(f,"PF\n%d %d\n-1.000000\n",Width,Height); for( int y = Height-1; y >= 0; y-- ) { float linebuffer[PFM_MAX_XSIZE*3]; for(int x=0;x<Width;x++) { for(int c=0;c<3;c++) { linebuffer[x*3+c]=Pixel(x,y,c); } } g_pFullFileSystem->Write(linebuffer,3*Width*sizeof(float),f); } g_pFullFileSystem->Close(f);
return true; }
return false; }
float FloatBitMap_t::InterpolatedPixel(float x, float y, int comp) const { int Top= floor(y); float Yfrac= y - Top; int Bot= min(Height-1,Top+1); int Left= floor(x); float Xfrac= x - Left; int Right= min(Width-1,Left+1); return BiLinInterp(Xfrac, Yfrac, Pixel(Left, Top, comp), Pixel(Right, Top, comp), Pixel(Left, Bot, comp), Pixel(Right, Bot, comp));
}
//-----------------------------------------------------------------
// resize (with bilinear filter) truecolor bitmap in place
void FloatBitMap_t::ReSize(int NewWidth, int NewHeight) { float XRatio= (float)Width / (float)NewWidth; float YRatio= (float)Height / (float)NewHeight; float SourceX, SourceY, Xfrac, Yfrac; int Top, Bot, Left, Right;
float *newrgba=new float[NewWidth * NewHeight * 4];
SourceY= 0; for(int y=0;y<NewHeight;y++) { Yfrac= SourceY - floor(SourceY); Top= SourceY; Bot= SourceY+1; if (Bot>=Height) Bot= Height-1; SourceX= 0; for(int x=0;x<NewWidth;x++) { Xfrac= SourceX - floor(SourceX); Left= SourceX; Right= SourceX+1; if (Right>=Width) Right= Width-1; for(int c=0;c<4;c++) { newrgba[4*(y*NewWidth+x)+c] = BiLinInterp(Xfrac, Yfrac, Pixel(Left, Top, c), Pixel(Right, Top, c), Pixel(Left, Bot, c), Pixel(Right, Bot, c)); } SourceX+= XRatio; } SourceY+= YRatio; }
delete[] RGBAData; RGBAData=newrgba;
Width=NewWidth; Height=NewHeight; }
struct TGAHeader_t { unsigned char id_length, colormap_type, image_type; unsigned char colormap_index0,colormap_index1, colormap_length0,colormap_length1; unsigned char colormap_size; unsigned char x_origin0,x_origin1, y_origin0,y_origin1, width0, width1,height0,height1; unsigned char pixel_size, attributes; };
bool FloatBitMap_t::WriteTGAFile(char const *filename) const { FileHandle_t f = g_pFullFileSystem->Open(filename, "wb"); if (f) { TGAHeader_t myheader; memset(&myheader,0,sizeof(myheader)); myheader.image_type=2; myheader.pixel_size=32; myheader.width0= Width & 0xff; myheader.width1= (Width>>8); myheader.height0= Height & 0xff; myheader.height1= (Height>>8); myheader.attributes=0x20; g_pFullFileSystem->Write(&myheader,sizeof(myheader),f); // now, write the pixels
for(int y=0;y<Height;y++) { for(int x=0;x<Width;x++) { PixRGBAF fpix = PixelRGBAF( x, y ); PixRGBA8 pix8 = PixRGBAF_to_8( fpix );
g_pFullFileSystem->Write(&pix8.Blue,1,f); g_pFullFileSystem->Write(&pix8.Green,1,f); g_pFullFileSystem->Write(&pix8.Red,1,f); g_pFullFileSystem->Write(&pix8.Alpha,1,f); } } g_pFullFileSystem->Close( f ); // close file after reading
return true; } return false; }
FloatBitMap_t::FloatBitMap_t(char const *tgafilename) { RGBAData=0;
// load from a tga or pfm
if (Q_stristr(tgafilename, ".pfm")) { LoadFromPFM(tgafilename); return; }
int width1, height1; ImageFormat imageFormat1; float gamma1;
if( !TGALoader::GetInfo( tgafilename, &width1, &height1, &imageFormat1, &gamma1 ) ) { printf( "error loading %s\n", tgafilename); exit( -1 ); } AllocateRGB(width1,height1);
uint8 *pImage1Tmp = new uint8 [ImageLoader::GetMemRequired( width1, height1, 1, imageFormat1, false )];
if( !TGALoader::Load( pImage1Tmp, tgafilename, width1, height1, imageFormat1, 2.2f, false ) ) { printf( "error loading %s\n", tgafilename); exit( -1 ); } uint8 *pImage1 = new uint8 [ImageLoader::GetMemRequired( width1, height1, 1, IMAGE_FORMAT_ABGR8888, false )];
ImageLoader::ConvertImageFormat( pImage1Tmp, imageFormat1, pImage1, IMAGE_FORMAT_ABGR8888, width1, height1, 0, 0 );
for(int y=0;y<height1;y++) { for(int x=0;x<width1;x++) { for(int c=0;c<4;c++) { Pixel(x,y,3-c)=pImage1[c+4*(x+(y*width1))]/255.0; } } }
delete[] pImage1; delete[] pImage1Tmp; }
FloatBitMap_t::~FloatBitMap_t(void) { if (RGBAData) delete[] RGBAData; }
FloatBitMap_t *FloatBitMap_t::QuarterSize(void) const { // generate a new bitmap half on each axis
FloatBitMap_t *newbm=new FloatBitMap_t(Width/2,Height/2); for(int y=0;y<Height/2;y++) for(int x=0;x<Width/2;x++) { for(int c=0;c<4;c++) newbm->Pixel(x,y,c)=((Pixel(x*2,y*2,c)+Pixel(x*2+1,y*2,c)+ Pixel(x*2,y*2+1,c)+Pixel(x*2+1,y*2+1,c))/4); } return newbm; }
FloatBitMap_t *FloatBitMap_t::QuarterSizeBlocky(void) const { // generate a new bitmap half on each axis
FloatBitMap_t *newbm=new FloatBitMap_t(Width/2,Height/2); for(int y=0;y<Height/2;y++) for(int x=0;x<Width/2;x++) { for(int c=0;c<4;c++) newbm->Pixel(x,y,c)=Pixel(x*2,y*2,c); } return newbm; }
Vector FloatBitMap_t::AverageColor(void) { Vector ret(0,0,0); for(int y=0;y<Height;y++) for(int x=0;x<Width;x++) for(int c=0;c<3;c++) ret[c]+=Pixel(x,y,c); ret*=1.0/(Width*Height); return ret; }
float FloatBitMap_t::BrightestColor(void) { float ret=0.0; for(int y=0;y<Height;y++) for(int x=0;x<Width;x++) { Vector v(Pixel(x,y,0),Pixel(x,y,1),Pixel(x,y,2)); ret=max(ret,v.Length()); } return ret; }
template <class T> static inline void SWAP(T & a, T & b) { T temp=a; a=b; b=temp; }
void FloatBitMap_t::RaiseToPower(float power) { for(int y=0;y<Height;y++) for(int x=0;x<Width;x++) for(int c=0;c<3;c++) Pixel(x,y,c)=pow((float)MAX(0.0,Pixel(x,y,c)),(float)power);
}
void FloatBitMap_t::Logize(void) { for(int y=0;y<Height;y++) for(int x=0;x<Width;x++) for(int c=0;c<3;c++) Pixel(x,y,c)=log(1.0+Pixel(x,y,c));
}
void FloatBitMap_t::UnLogize(void) { for(int y=0;y<Height;y++) for(int x=0;x<Width;x++) for(int c=0;c<3;c++) Pixel(x,y,c)=exp(Pixel(x,y,c))-1; }
void FloatBitMap_t::Clear(float r, float g, float b, float alpha) { for(int y=0;y<Height;y++) for(int x=0;x<Width;x++) { Pixel(x,y,0)=r; Pixel(x,y,1)=g; Pixel(x,y,2)=b; Pixel(x,y,3)=alpha; } }
void FloatBitMap_t::ScaleRGB(float scale_factor) { for(int y=0;y<Height;y++) for(int x=0;x<Width;x++) for(int c=0;c<3;c++) Pixel(x,y,c)*=scale_factor; }
static int dx[4]={0,-1,1,0}; static int dy[4]={-1,0,0,1};
#define NDELTAS 4
void FloatBitMap_t::SmartPaste(FloatBitMap_t const &b, int xofs, int yofs, uint32 Flags) { // now, need to make Difference map
FloatBitMap_t DiffMap0(this); FloatBitMap_t DiffMap1(this); FloatBitMap_t DiffMap2(this); FloatBitMap_t DiffMap3(this); FloatBitMap_t *deltas[4]={&DiffMap0,&DiffMap1,&DiffMap2,&DiffMap3}; for(int x=0;x<Width;x++) for(int y=0;y<Height;y++) for(int c=0;c<3;c++) { for(int i=0;i<NDELTAS;i++) { int x1=x+dx[i]; int y1=y+dy[i]; x1=MAX(0,x1); x1=MIN(Width-1,x1); y1=MAX(0,y1); y1=MIN(Height-1,y1); float dx1=Pixel(x,y,c)-Pixel(x1,y1,c); deltas[i]->Pixel(x,y,c)=dx1; } } for(int x=1;x<b.Width-1;x++) for(int y=1;y<b.Height-1;y++) for(int c=0;c<3;c++) { for(int i=0;i<NDELTAS;i++) { float diff=b.Pixel(x,y,c)-b.Pixel(x+dx[i],y+dy[i],c); deltas[i]->Pixel(x+xofs,y+yofs,c)=diff; if (Flags & SPFLAGS_MAXGRADIENT) { float dx1=Pixel(x+xofs,y+yofs,c)-Pixel(x+dx[i]+xofs,y+dy[i]+yofs,c); if (fabs(dx1)>fabs(diff)) deltas[i]->Pixel(x+xofs,y+yofs,c)=dx1; } } }
// now, calculate modifiability
for(int x=0;x<Width;x++) for(int y=0;y<Height;y++) { float modify=0; if ( (x>xofs+1) && (x<=xofs+b.Width-2) && (y>yofs+1) && (y<=yofs+b.Height-2)) modify=1; Alpha(x,y)=modify; }
// // now, force a fex pixels in center to be constant
// int midx=xofs+b.Width/2;
// int midy=yofs+b.Height/2;
// for(x=midx-10;x<midx+10;x++)
// for(int y=midy-10;y<midy+10;y++)
// {
// Alpha(x,y)=0;
// for(int c=0;c<3;c++)
// Pixel(x,y,c)=b.Pixel(x-xofs,y-yofs,c);
// }
Poisson(deltas,6000,Flags); }
void FloatBitMap_t::ScaleGradients(void) { // now, need to make Difference map
FloatBitMap_t DiffMap0(this); FloatBitMap_t DiffMap1(this); FloatBitMap_t DiffMap2(this); FloatBitMap_t DiffMap3(this); FloatBitMap_t *deltas[4]={&DiffMap0,&DiffMap1,&DiffMap2,&DiffMap3}; double gsum=0.0; for(int x=0;x<Width;x++) for(int y=0;y<Height;y++) for(int c=0;c<3;c++) { for(int i=0;i<NDELTAS;i++) { int x1=x+dx[i]; int y1=y+dy[i]; x1=MAX(0,x1); x1=MIN(Width-1,x1); y1=MAX(0,y1); y1=MIN(Height-1,y1); float dx1=Pixel(x,y,c)-Pixel(x1,y1,c); deltas[i]->Pixel(x,y,c)=dx1; gsum+=fabs(dx1); } } // now, reduce gradient changes
// float gavg=gsum/(Width*Height);
for(int x=0;x<Width;x++) for(int y=0;y<Height;y++) for(int c=0;c<3;c++) { for(int i=0;i<NDELTAS;i++) { float norml=1.1*deltas[i]->Pixel(x,y,c); // if (norml<0.0)
// norml=-pow(-norml,1.2);
// else
// norml=pow(norml,1.2);
deltas[i]->Pixel(x,y,c)=norml; } }
// now, calculate modifiability
for(int x=0;x<Width;x++) for(int y=0;y<Height;y++) { float modify=0; if ( (x>0) && (x<Width-1) && (y) && (y<Height-1)) { modify=1; Alpha(x,y)=modify; } }
Poisson(deltas,2200,0); }
void FloatBitMap_t::MakeTileable(void) { FloatBitMap_t rslta(this); // now, need to make Difference map
FloatBitMap_t DiffMapX(this); FloatBitMap_t DiffMapY(this); // set each pixel=avg-pixel
FloatBitMap_t *cursrc=&rslta; for(int x=1;x<Width-1;x++) for(int y=1;y<Height-1;y++) for(int c=0;c<3;c++) { DiffMapX.Pixel(x,y,c)=Pixel(x,y,c)-Pixel(x+1,y,c); DiffMapY.Pixel(x,y,c)=Pixel(x,y,c)-Pixel(x,y+1,c); } // initialize edge conditions
for(int x=0;x<Width;x++) { for(int c=0;c<3;c++) { float a=0.5*(Pixel(x,Height-1,c)+=Pixel(x,0,c)); rslta.Pixel(x,Height-1,c)=a; rslta.Pixel(x,0,c)=a; } } for(int y=0;y<Height;y++) { for(int c=0;c<3;c++) { float a=0.5*(Pixel(Width-1,y,c)+Pixel(0,y,c)); rslta.Pixel(Width-1,y,c)=a; rslta.Pixel(0,y,c)=a; } } FloatBitMap_t rsltb(&rslta); FloatBitMap_t *curdst=&rsltb;
// now, ready to iterate
for(int pass=0;pass<10;pass++) { float error=0.0; for(int x=1;x<Width-1;x++) for(int y=1;y<Height-1;y++) for(int c=0;c<3;c++) { float desiredx=DiffMapX.Pixel(x,y,c)+cursrc->Pixel(x+1,y,c); float desiredy=DiffMapY.Pixel(x,y,c)+cursrc->Pixel(x,y+1,c); float desired=0.5*(desiredy+desiredx); curdst->Pixel(x,y,c)=FLerp(cursrc->Pixel(x,y,c),desired,0.5); error+=SQ(desired-cursrc->Pixel(x,y,c)); } SWAP(cursrc,curdst); } // paste result
for(int x=0;x<Width;x++) for(int y=0;y<Height;y++) for(int c=0;c<3;c++) Pixel(x,y,c)=curdst->Pixel(x,y,c); }
void FloatBitMap_t::GetAlphaBounds(int &minx, int &miny, int &maxx,int &maxy) { for(minx=0;minx<Width;minx++) { int y; for(y=0;y<Height;y++) if (Alpha(minx,y)) break; if (y!=Height) break; } for(maxx=Width-1;maxx>=0;maxx--) { int y; for(y=0;y<Height;y++) if (Alpha(maxx,y)) break; if (y!=Height) break; } for(miny=0;minx<Height;miny++) { int x; for(x=minx;x<=maxx;x++) if (Alpha(x,miny)) break; if (x<maxx) break; } for(maxy=Height-1;maxy>=0;maxy--) { int x; for(x=minx;x<=maxx;x++) if (Alpha(x,maxy)) break; if (x<maxx) break; } }
void FloatBitMap_t::Poisson(FloatBitMap_t *deltas[4], int n_iters, uint32 flags // SPF_xxx
) { int minx,miny,maxx,maxy; GetAlphaBounds(minx,miny,maxx,maxy); minx=MAX(1,minx); miny=MAX(1,miny); maxx=MIN(Width-2,maxx); maxy=MIN(Height-2,maxy); if (((maxx-minx)>25) && (maxy-miny)>25) { // perform at low resolution
FloatBitMap_t *lowdeltas[NDELTAS]; for(int i=0;i<NDELTAS;i++) lowdeltas[i]=deltas[i]->QuarterSize(); FloatBitMap_t *tmp=QuarterSize(); tmp->Poisson(lowdeltas,n_iters*4,flags); // now, propagate results from tmp to us
for(int x=0;x<tmp->Width;x++) for(int y=0;y<tmp->Height;y++) for(int xi=0;xi<2;xi++) for(int yi=0;yi<2;yi++) if (Alpha(x*2+xi,y*2+yi)) { for(int c=0;c<3;c++) Pixel(x*2+xi,y*2+yi,c)= FLerp(Pixel(x*2+xi,y*2+yi,c),tmp->Pixel(x,y,c),Alpha(x*2+xi,y*2+yi)); } char fname[80]; sprintf(fname,"sub%dx%d.tga",tmp->Width,tmp->Height); tmp->WriteTGAFile(fname); sprintf(fname,"submrg%dx%d.tga",tmp->Width,tmp->Height); WriteTGAFile(fname); delete tmp; for(int i=0;i<NDELTAS;i++) delete lowdeltas[i]; } FloatBitMap_t work1(this); FloatBitMap_t work2(this); FloatBitMap_t *curdst=&work1; FloatBitMap_t *cursrc=&work2; // now, ready to iterate
while(n_iters--) { float error=0.0; for(int x=minx;x<=maxx;x++) { for(int y=miny;y<=maxy;y++) { if (Alpha(x,y)) { for(int c=0;c<3;c++) { float desired=0.0; for(int i=0;i<NDELTAS;i++) desired+=deltas[i]->Pixel(x,y,c)+cursrc->Pixel(x+dx[i],y+dy[i],c); desired*=(1.0/NDELTAS); // desired=FLerp(Pixel(x,y,c),desired,Alpha(x,y));
curdst->Pixel(x,y,c)=FLerp(cursrc->Pixel(x,y,c),desired,0.5); error+=SQ(desired-cursrc->Pixel(x,y,c)); } } SWAP(cursrc,curdst); } } } // paste result
for(int x=0;x<Width;x++) { for(int y=0;y<Height;y++) { for(int c=0;c<3;c++) { Pixel(x,y,c)=curdst->Pixel(x,y,c); } } } }
|