forked from Mirrors/openclonk
1632 lines
37 KiB
C++
1632 lines
37 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 1998-2000, 2003, 2008 Matthes Bender
|
|
* Copyright (c) 2002, 2004-2008 Sven Eberhardt
|
|
* Copyright (c) 2005, 2007-2011 Günther Brammer
|
|
* Copyright (c) 2005 Peter Wortmann
|
|
* Copyright (c) 2009-2010 Armin Burgmeier
|
|
* Copyright (c) 2009 Nicolas Hake
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
|
*
|
|
* Portions might be copyrighted by other authors who have contributed
|
|
* to OpenClonk.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
* See isc_license.txt for full license and disclaimer.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender.
|
|
* See clonk_trademark_license.txt for full license.
|
|
*/
|
|
// a wrapper class to DirectDraw surfaces
|
|
|
|
#include "C4Include.h"
|
|
#include <C4Surface.h>
|
|
|
|
#include <StdFile.h>
|
|
#include <CStdFile.h>
|
|
#include "C4App.h"
|
|
#include <StdGL.h>
|
|
#include <C4Window.h>
|
|
#include <StdRegistry.h>
|
|
#include <StdResStr.h>
|
|
#include <StdDDraw2.h>
|
|
#include <StdD3D.h>
|
|
#include <Bitmap256.h>
|
|
#include <StdPNG.h>
|
|
#include <C4Config.h>
|
|
|
|
|
|
#ifdef HAVE_IO_H
|
|
#include <io.h>
|
|
#endif
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <limits.h>
|
|
#include <list>
|
|
|
|
C4Surface::C4Surface() : fIsBackground(false)
|
|
{
|
|
Default();
|
|
}
|
|
|
|
C4Surface::C4Surface(int iWdt, int iHgt) : fIsBackground(false)
|
|
{
|
|
Default();
|
|
// create
|
|
Create(iWdt, iHgt);
|
|
}
|
|
|
|
C4Surface::C4Surface(C4AbstractApp * pApp, C4Window * pWindow):
|
|
Wdt(0), Hgt(0)
|
|
{
|
|
Default();
|
|
fPrimary=true;
|
|
this->pWindow=pWindow;
|
|
// create rendering context
|
|
#ifdef USE_GL
|
|
if (pGL)
|
|
pCtx = pGL->CreateContext(pWindow, pApp);
|
|
#endif
|
|
// reset clipping
|
|
NoClip();
|
|
}
|
|
|
|
C4Surface::~C4Surface()
|
|
{
|
|
/* for (C4ObjectLink *lnk = ::Objects.First; lnk; lnk=lnk->Next)
|
|
if (lnk->Obj->Menu)
|
|
lnk->Obj->Menu->AssertSurfaceNotUsed(this);*/
|
|
Clear();
|
|
}
|
|
|
|
void C4Surface::Default()
|
|
{
|
|
Wdt=Hgt=0;
|
|
Scale=1;
|
|
PrimarySurfaceLockPitch=0; PrimarySurfaceLockBits=NULL;
|
|
ClipX=ClipY=ClipX2=ClipY2=0;
|
|
Locked=0;
|
|
Attached=false;
|
|
fPrimary=false;
|
|
#ifdef USE_DIRECTX
|
|
pSfc=NULL;
|
|
#endif
|
|
ppTex=NULL;
|
|
pMainSfc=NULL;
|
|
#ifdef USE_GL
|
|
pCtx=NULL;
|
|
#endif
|
|
pWindow=NULL;
|
|
ClrByOwnerClr=0;
|
|
iTexSize=iTexX=iTexY=0;
|
|
fIsRenderTarget=false;
|
|
fIsBackground=false;
|
|
#ifdef _DEBUG
|
|
dbg_idx = NULL;
|
|
#endif
|
|
}
|
|
|
|
void C4Surface::MoveFrom(C4Surface *psfcFrom)
|
|
{
|
|
// clear own
|
|
Clear();
|
|
// safety
|
|
if (!psfcFrom) return;
|
|
// grab data from other sfc
|
|
#ifdef _DEBUG
|
|
dbg_idx = psfcFrom->dbg_idx;
|
|
#endif
|
|
Wdt=psfcFrom->Wdt; Hgt=psfcFrom->Hgt;
|
|
PrimarySurfaceLockPitch=psfcFrom->PrimarySurfaceLockPitch;
|
|
PrimarySurfaceLockBits=psfcFrom->PrimarySurfaceLockBits;
|
|
psfcFrom->PrimarySurfaceLockBits=NULL;
|
|
ClipX=psfcFrom->ClipX; ClipY=psfcFrom->ClipY;
|
|
ClipX2=psfcFrom->ClipX2; ClipY2=psfcFrom->ClipY2;
|
|
Locked=psfcFrom->Locked;
|
|
Attached=psfcFrom->Attached;
|
|
fPrimary=psfcFrom->fPrimary; // shouldn't be true!
|
|
ppTex=psfcFrom->ppTex;
|
|
pMainSfc=psfcFrom->pMainSfc;
|
|
ClrByOwnerClr=psfcFrom->ClrByOwnerClr;
|
|
iTexSize=psfcFrom->iTexSize;
|
|
iTexX=psfcFrom->iTexX; iTexY=psfcFrom->iTexY;
|
|
byBytesPP=psfcFrom->byBytesPP;
|
|
#ifdef USE_DIRECTX
|
|
dwClrFormat=psfcFrom->dwClrFormat;
|
|
pSfc=psfcFrom->pSfc;
|
|
#endif
|
|
#ifdef USE_GL
|
|
Format=psfcFrom->Format;
|
|
#endif
|
|
fIsBackground = psfcFrom->fIsBackground;
|
|
// default other sfc
|
|
psfcFrom->Default();
|
|
}
|
|
|
|
void C4Surface::Clear()
|
|
{
|
|
// Undo all locks
|
|
while (Locked) Unlock();
|
|
// release surface
|
|
#ifdef USE_DIRECTX
|
|
if (pD3D)
|
|
{
|
|
if (pSfc) pSfc->Release();
|
|
}
|
|
pSfc=NULL;
|
|
#endif
|
|
#ifdef USE_GL
|
|
if (pCtx)
|
|
{
|
|
delete pCtx;
|
|
pCtx = 0;
|
|
}
|
|
#endif
|
|
FreeTextures();
|
|
ppTex=NULL;
|
|
#ifdef _DEBUG
|
|
delete dbg_idx;
|
|
dbg_idx = NULL;
|
|
#endif
|
|
}
|
|
|
|
bool C4Surface::IsRenderTarget()
|
|
{
|
|
// primary is always OK...
|
|
return fPrimary
|
|
// other surfaces may be used as render targets, if offscreen rendertargets are not disabled by config,
|
|
// or the surface is split (large sfcs) or locked (landscape)
|
|
// (only D3D for now)
|
|
#ifdef USE_DIRECTX
|
|
|| (!Locked && !Config.Graphics.NoOffscreenBlits && pD3D && fIsRenderTarget)
|
|
#endif
|
|
;
|
|
}
|
|
|
|
void C4Surface::NoClip()
|
|
{
|
|
ClipX=0; ClipY=0; ClipX2=Wdt-1; ClipY2=Hgt-1;
|
|
}
|
|
|
|
void C4Surface::Clip(int iX, int iY, int iX2, int iY2)
|
|
{
|
|
ClipX=BoundBy(iX,0,Wdt-1); ClipY=BoundBy(iY,0,Hgt-1);
|
|
ClipX2=BoundBy(iX2,0,Wdt-1); ClipY2=BoundBy(iY2,0,Hgt-1);
|
|
}
|
|
|
|
bool C4Surface::Create(int iWdt, int iHgt, bool, bool fIsRenderTarget, int MaxTextureSize)
|
|
{
|
|
Clear(); Default();
|
|
// check size
|
|
if (!iWdt || !iHgt) return false;
|
|
Wdt=iWdt; Hgt=iHgt;
|
|
// create texture: check gfx system
|
|
if (!pDraw) return false;
|
|
if (!pDraw->DeviceReady()) return false;
|
|
|
|
// store color format that will be used
|
|
#ifdef USE_DIRECTX
|
|
if (pD3D)
|
|
dwClrFormat=pD3D->dwSurfaceType;
|
|
else
|
|
#endif
|
|
#ifdef USE_GL
|
|
if (pGL)
|
|
Format=pGL->sfcFmt;
|
|
else
|
|
#endif
|
|
{/* nothing to do */}
|
|
byBytesPP=pDraw->byByteCnt;
|
|
this->fIsRenderTarget = fIsRenderTarget;
|
|
// create textures
|
|
if (!CreateTextures(MaxTextureSize)) { Clear(); return false; }
|
|
// update clipping
|
|
NoClip();
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
bool C4Surface::Copy(C4Surface &fromSfc)
|
|
{
|
|
// Clear anything old
|
|
Clear();
|
|
// Default to other surface's color depth
|
|
Default();
|
|
// Create surface
|
|
if (!Create(fromSfc.Wdt, fromSfc.Hgt)) return false;
|
|
// Blit copy
|
|
if (!pDraw->BlitSurface(&fromSfc, this, 0, 0, false))
|
|
{ Clear(); return false; }
|
|
// Success
|
|
return true;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
int GetNeedTexSize(int Size)
|
|
{
|
|
int iNeedSize = Size;
|
|
|
|
#ifdef USE_GL
|
|
if (!pGL || !GLEW_ARB_texture_non_power_of_two)
|
|
#endif
|
|
{
|
|
int n=0;
|
|
while ((1<<++n) < iNeedSize) {}
|
|
iNeedSize = 1<<n;
|
|
}
|
|
|
|
return iNeedSize;
|
|
}
|
|
}
|
|
|
|
bool C4Surface::CreateTextures(int MaxTextureSize)
|
|
{
|
|
// free previous
|
|
FreeTextures();
|
|
iTexSize=Min(GetNeedTexSize(Max(Wdt, Hgt)), pDraw->MaxTexSize);
|
|
if (MaxTextureSize)
|
|
iTexSize=Min(iTexSize, MaxTextureSize);
|
|
// get the number of textures needed for this size
|
|
iTexX=(Wdt-1)/iTexSize +1;
|
|
iTexY=(Hgt-1)/iTexSize +1;
|
|
// get mem for texture array
|
|
ppTex = new C4TexRef * [iTexX*iTexY];
|
|
memset(ppTex, 0, iTexX*iTexY*sizeof(C4TexRef *));
|
|
// cvan't be render target if it's not a single surface
|
|
if (!IsSingleSurface()) fIsRenderTarget = false;
|
|
// create textures
|
|
C4TexRef **ppCTex=ppTex;
|
|
for (int y = 0; y < iTexY; ++y)
|
|
{
|
|
for(int x = 0; x < iTexX; ++x)
|
|
{
|
|
int sizeX = iTexSize;
|
|
int sizeY = iTexSize;
|
|
if(x == iTexX-1) sizeX = GetNeedTexSize( (Wdt - 1) % iTexSize + 1);
|
|
if(y == iTexY-1) sizeY = GetNeedTexSize( (Hgt - 1) % iTexSize + 1);
|
|
|
|
*ppCTex = new C4TexRef(sizeX, sizeY, fIsRenderTarget);
|
|
|
|
if (fIsBackground && ppCTex) (*ppCTex)->FillBlack();
|
|
|
|
#ifdef USE_DIRECTX
|
|
if (!(*ppCTex)->pTex && pD3D)
|
|
{
|
|
// error creating texture
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
++ppCTex;
|
|
}
|
|
}
|
|
#if 0
|
|
for (int i=iTexX*iTexY; i; --i,++ppCTex)
|
|
{
|
|
// regular textures or if last texture fits exactly into the space by Wdt or Hgt
|
|
if (i-1 || !(Wdt%iTexSize) || !(Hgt%iTexSize))
|
|
*ppCTex = new C4TexRef(iTexSize, fIsRenderTarget);
|
|
else
|
|
{
|
|
// last texture might be smaller
|
|
iNeedSize=Max(Wdt%iTexSize, Hgt%iTexSize);
|
|
#ifdef USE_GL
|
|
if (!pGL || !GLEW_ARB_texture_non_power_of_two)
|
|
#endif
|
|
{
|
|
int n=0;
|
|
while ((1<<++n) < iNeedSize) {}
|
|
iNeedSize=1<<n;
|
|
}
|
|
*ppCTex = new C4TexRef(iNeedSize, fIsRenderTarget);
|
|
}
|
|
if (fIsBackground && ppCTex) (*ppCTex)->FillBlack();
|
|
#ifdef USE_DIRECTX
|
|
if (!(*ppCTex)->pTex && pD3D)
|
|
{
|
|
// error creating texture
|
|
return false;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
static int dbg_counter = 0;
|
|
if (dbg_idx) delete dbg_idx;
|
|
dbg_idx = new int;
|
|
*dbg_idx = dbg_counter++;
|
|
#endif
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
void C4Surface::FreeTextures()
|
|
{
|
|
if (ppTex)
|
|
{
|
|
// clear all textures
|
|
C4TexRef **ppTx=ppTex;
|
|
for (int i=0; i<iTexX*iTexY; ++i,++ppTx)
|
|
if (*ppTx) delete *ppTx;
|
|
// clear texture list
|
|
delete [] ppTex;
|
|
ppTex=NULL;
|
|
}
|
|
}
|
|
|
|
/*bool ClrByOwner(DWORD &rClr) old style...
|
|
{
|
|
// red value must be approx. same to green
|
|
BYTE byR=GetBValue(rClr), byG=GetGValue(rClr);
|
|
int diff=Abs(byR-byG);
|
|
if (diff>byR/8) return false;
|
|
// get blue value; mustn't be 0 or equal to R/G (grey)
|
|
BYTE byB=GetRValue(rClr); if (!byB || Inside(byB, Min(byR, byG), Max(byR, byG))) return false;
|
|
// medium r/g and blue is very close (additional gray shade check)
|
|
if ((byR > 50) && (Abs(byB - byR) < 20)) return false;
|
|
// if blue is not fully lit, red and green should be very low
|
|
if (byB<240 && byR>15) return false;
|
|
// so, the color seems to be truly blue-ish
|
|
rClr=RGB(byB, byB, byB) | (rClr&0xff000000);
|
|
return true;
|
|
}*/
|
|
|
|
#define RANGE 255
|
|
#define HLSMAX RANGE
|
|
#define RGBMAX 255
|
|
|
|
bool ClrByOwner(DWORD &dwClr) // new style, based on Microsoft Knowledge Base Article - 29240
|
|
{
|
|
int H,L,S;
|
|
WORD R,G,B;
|
|
BYTE cMax,cMin;
|
|
WORD Rdelta,Gdelta,Bdelta;
|
|
// get RGB
|
|
R = GetRedValue(dwClr);
|
|
G = GetGreenValue(dwClr);
|
|
B = GetBlueValue(dwClr);
|
|
// calculate lightness
|
|
cMax = Max<int>(Max<int>(R,G),B);
|
|
cMin = Min<int>(Min<int>(R,G),B);
|
|
L = ( ((cMax+cMin)*HLSMAX) + RGBMAX )/(2*RGBMAX);
|
|
// achromatic case
|
|
if (cMax == cMin)
|
|
{
|
|
S = 0;
|
|
H = (HLSMAX*2/3);
|
|
}
|
|
// chromatic case
|
|
else
|
|
{
|
|
// saturation
|
|
if (L <= (HLSMAX/2))
|
|
S = ( ((cMax-cMin)*HLSMAX) + ((cMax+cMin)/2) ) / (cMax+cMin);
|
|
else
|
|
S = ( ((cMax-cMin)*HLSMAX) + ((2*RGBMAX-cMax-cMin)/2) )
|
|
/ (2*RGBMAX-cMax-cMin);
|
|
// hue
|
|
Rdelta = ( ((cMax-R)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
|
|
Gdelta = ( ((cMax-G)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
|
|
Bdelta = ( ((cMax-B)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
|
|
if (R == cMax)
|
|
H = Bdelta - Gdelta;
|
|
else if (G == cMax)
|
|
H = (HLSMAX/3) + Rdelta - Bdelta;
|
|
else
|
|
H = ((2*HLSMAX)/3) + Gdelta - Rdelta;
|
|
if (H < 0)
|
|
H += HLSMAX;
|
|
if (H > HLSMAX)
|
|
H -= HLSMAX;
|
|
}
|
|
// Not blue
|
|
if (!(Inside(H, 145, 175) && (S > 100))) return false;
|
|
// It's blue: make it gray
|
|
BYTE b = GetBlueValue(dwClr);
|
|
dwClr = RGBA(b, b, b, 0) | (dwClr & 0xff000000);
|
|
return true;
|
|
}
|
|
|
|
bool C4Surface::CreateColorByOwner(C4Surface *pBySurface)
|
|
{
|
|
// safety
|
|
if (!pBySurface) return false;
|
|
if (!pBySurface->ppTex) return false;
|
|
// create in same size
|
|
if (!Create(pBySurface->Wdt, pBySurface->Hgt, false)) return false;
|
|
// copy scale
|
|
Scale = pBySurface->Scale;
|
|
// set main surface
|
|
pMainSfc=pBySurface;
|
|
// lock it
|
|
if (!pMainSfc->Lock()) return false;
|
|
if (!Lock()) { pMainSfc->Unlock(); return false; }
|
|
// set ColorByOwner-pixels
|
|
for (int iY=0; iY<Hgt; ++iY)
|
|
for (int iX=0; iX<Wdt; ++iX)
|
|
{
|
|
// get pixel
|
|
DWORD dwPix=pMainSfc->GetPixDw(iX, iY, false);
|
|
// is it a ClrByOwner-px?
|
|
if (!ClrByOwner(dwPix)) continue;
|
|
// set in this surface
|
|
SetPixDw(iX, iY, dwPix);
|
|
// clear in the other
|
|
pMainSfc->SetPixDw(iX, iY, 0x00ffffff);
|
|
}
|
|
// unlock
|
|
Unlock();
|
|
pMainSfc->Unlock();
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
bool C4Surface::SetAsClrByOwnerOf(C4Surface *pOfSurface)
|
|
{
|
|
// safety
|
|
if (!pOfSurface) return false;
|
|
if (Wdt != pOfSurface->Wdt || Hgt != pOfSurface->Hgt)
|
|
return false;
|
|
// set main surface
|
|
pMainSfc=pOfSurface;
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
#ifdef USE_GL
|
|
/*bool C4Surface::CreatePrimaryGLTextures()
|
|
{
|
|
if (!pGL) return false;
|
|
// primary OpenGL-surface: ensure context is selected
|
|
if (!pGL->pCurrCtx) if (!pGL->MainCtx.Select()) return false;
|
|
// create texture array
|
|
CreateTextures();
|
|
// get from framebuffer
|
|
C4TexRef **ppTexRef = ppTex;
|
|
for (int iY=0; iY<Hgt; iY+=iTexSize)
|
|
for (int iX=0; iX<Wdt; iX+=iTexSize)
|
|
{
|
|
// get tex size
|
|
int txWdt=Min(Wdt-iX, iTexSize), txHgt=Min(Hgt-iY, iTexSize);
|
|
// copy surface into texture
|
|
glBindTexture(GL_TEXTURE_2D, (*ppTexRef)->texName);
|
|
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, iX, iY, txWdt, txHgt, 0);
|
|
// next texture reference
|
|
++ppTexRef;
|
|
}
|
|
// done, success
|
|
return true;
|
|
}*/
|
|
#endif
|
|
|
|
bool C4Surface::UpdateSize(int wdt, int hgt)
|
|
{
|
|
assert(fPrimary);
|
|
if (!fPrimary)
|
|
return false;
|
|
this->Wdt = wdt; this->Hgt = hgt;
|
|
return true;
|
|
}
|
|
|
|
bool C4Surface::PageFlip(C4Rect *pSrcRt, C4Rect *pDstRt)
|
|
{
|
|
assert(fPrimary);
|
|
if (!fPrimary)
|
|
return false;
|
|
// call from gfx thread only!
|
|
if (!pDraw->pApp || !pDraw->pApp->AssertMainThread()) return false;
|
|
#ifdef USE_GL
|
|
if (pGL)
|
|
return pCtx->PageFlip();
|
|
#endif
|
|
#ifdef USE_DIRECTX
|
|
if (pD3D)
|
|
return pD3D->PageFlip(pSrcRt, pDstRt);
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
#ifdef USE_DIRECTX
|
|
IDirect3DSurface9 *C4Surface::GetSurface()
|
|
{
|
|
// direct surface?
|
|
if (pSfc)
|
|
{
|
|
pSfc->AddRef();
|
|
return pSfc;
|
|
}
|
|
// surface by texture?
|
|
if (fIsRenderTarget && ppTex)
|
|
{
|
|
IDirect3DTexture9 *pTex = (*ppTex)->pTex;
|
|
IDirect3DSurface9 *pSfcResult=NULL;
|
|
if (pTex) pTex->GetSurfaceLevel(0, &pSfcResult);
|
|
return pSfcResult;
|
|
}
|
|
// split surfaces: Won't work; we're no render target anyway
|
|
return NULL;
|
|
}
|
|
#endif //USE_DIRECTX
|
|
|
|
bool C4Surface::ReadBMP(CStdStream &hGroup)
|
|
{
|
|
int lcnt;
|
|
C4BMP256Info BitmapInfo;
|
|
// read bmpinfo-header
|
|
if (!hGroup.Read(&BitmapInfo,sizeof(C4BMPInfo))) return false;
|
|
// is it 8bpp?
|
|
if (BitmapInfo.Info.biBitCount == 8)
|
|
{
|
|
if (!hGroup.Read(((BYTE *) &BitmapInfo)+sizeof(C4BMPInfo),
|
|
Min(sizeof(BitmapInfo)-sizeof(C4BMPInfo),sizeof(BitmapInfo)-sizeof(C4BMPInfo)+BitmapInfo.FileBitsOffset())))
|
|
return false;
|
|
if (!hGroup.Advance(BitmapInfo.FileBitsOffset())) return false;
|
|
}
|
|
else
|
|
{
|
|
// read 24bpp
|
|
if (BitmapInfo.Info.biBitCount != 24) return false;
|
|
if (!hGroup.Advance(((C4BMPInfo) BitmapInfo).FileBitsOffset())) return false;
|
|
}
|
|
|
|
// Create and lock surface
|
|
if (!Create(BitmapInfo.Info.biWidth,BitmapInfo.Info.biHeight)) return false;
|
|
if (!Lock()) { Clear(); return false; }
|
|
|
|
// create line buffer
|
|
int iBufSize=DWordAligned(BitmapInfo.Info.biWidth*BitmapInfo.Info.biBitCount/8);
|
|
BYTE *pBuf = new BYTE[iBufSize];
|
|
// Read lines
|
|
for (lcnt=Hgt-1; lcnt>=0; lcnt--)
|
|
{
|
|
if (!hGroup.Read(pBuf, iBufSize))
|
|
{ Clear(); delete [] pBuf; return false; }
|
|
BYTE *pPix=pBuf;
|
|
for (int x=0; x<BitmapInfo.Info.biWidth; ++x)
|
|
switch (BitmapInfo.Info.biBitCount)
|
|
{
|
|
case 8:
|
|
SetPixDw(x, lcnt, C4RGB(
|
|
BitmapInfo.Colors[*pPix].rgbRed,
|
|
BitmapInfo.Colors[*pPix].rgbGreen,
|
|
BitmapInfo.Colors[*pPix].rgbBlue));
|
|
++pPix;
|
|
break;
|
|
case 24:
|
|
SetPixDw(x, lcnt, C4RGB(pPix[0], pPix[1], pPix[2]));
|
|
pPix+=3;
|
|
break;
|
|
}
|
|
}
|
|
// free buffer again
|
|
delete [] pBuf;
|
|
|
|
Unlock();
|
|
|
|
return true;
|
|
}
|
|
|
|
/*bool C4Surface::Save(const char *szFilename)
|
|
{
|
|
C4BMPInfo BitmapInfo2;
|
|
C4BMP256Info BitmapInfo;
|
|
// Set bitmap info
|
|
if (fPrimary)
|
|
{
|
|
if (byBytesPP==4)
|
|
BitmapInfo2.Set(Wdt,Hgt,32);
|
|
else
|
|
BitmapInfo2.Set(Wdt,Hgt,16);
|
|
}
|
|
else
|
|
BitmapInfo.Set(Wdt,Hgt,pPal->Colors);
|
|
|
|
// Lock - WARNING - maybe locking primary surface here...
|
|
if (!Lock()) return false;
|
|
|
|
// Create file & write info
|
|
CStdFile hFile;
|
|
|
|
if(fPrimary)
|
|
{
|
|
if ( !hFile.Create(szFilename)
|
|
|| !hFile.Write(&BitmapInfo2,sizeof(BitmapInfo2)) )
|
|
{ Unlock(); return false; }
|
|
|
|
// write lines
|
|
char bpEmpty[4]; int iEmpty = DWordAligned(Wdt*byBytesPP)-Wdt*byBytesPP;
|
|
for (int cnt=Hgt-1; cnt>=0; cnt--)
|
|
{
|
|
if (!hFile.Write(Bits+(Pitch*cnt),Wdt*byBytesPP))
|
|
{ Unlock(); return false; }
|
|
if (iEmpty)
|
|
if (!hFile.Write(bpEmpty,iEmpty))
|
|
{ Unlock(); return false; }
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if ( !hFile.Create(szFilename)
|
|
|| !hFile.Write(&BitmapInfo,sizeof(BitmapInfo)) )
|
|
{ Unlock(); return false; }
|
|
|
|
// Write lines
|
|
char bpEmpty[4]; int iEmpty = DWordAligned(Wdt)-Wdt;
|
|
for (int cnt=Hgt-1; cnt>=0; cnt--)
|
|
{
|
|
if (!hFile.Write(Bits+(Pitch*cnt),Wdt))
|
|
{ Unlock(); return false; }
|
|
if (iEmpty)
|
|
if (!hFile.Write(bpEmpty,iEmpty))
|
|
{ Unlock(); return false; }
|
|
}
|
|
}
|
|
|
|
// Close file
|
|
hFile.Close();
|
|
|
|
// Unlock
|
|
Unlock();
|
|
|
|
// Success
|
|
return true;
|
|
}
|
|
*/
|
|
bool C4Surface::SavePNG(const char *szFilename, bool fSaveAlpha, bool fApplyGamma, bool fSaveOverlayOnly)
|
|
{
|
|
// Lock - WARNING - maybe locking primary surface here...
|
|
if (!Lock()) return false;
|
|
|
|
// create png file
|
|
CPNGFile png;
|
|
if (!png.Create(Wdt, Hgt, fSaveAlpha)) { Unlock(); return false; }
|
|
|
|
// reset overlay if desired
|
|
C4Surface *pMainSfcBackup = NULL;
|
|
if (fSaveOverlayOnly) { pMainSfcBackup=pMainSfc; pMainSfc=NULL; }
|
|
|
|
#ifdef USE_GL
|
|
if (fPrimary && pGL)
|
|
{
|
|
// Take shortcut. FIXME: Check Endian
|
|
for (int y = 0; y < Hgt; ++y)
|
|
glReadPixels(0, Hgt - y, Wdt, 1, fSaveAlpha ? GL_BGRA : GL_BGR, GL_UNSIGNED_BYTE, png.GetImageData() + y * Wdt * (3 + fSaveAlpha));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// write pixel values
|
|
for (int y=0; y<Hgt; ++y)
|
|
for (int x=0; x<Wdt; ++x)
|
|
{
|
|
DWORD dwClr = GetPixDw(x, y, false);
|
|
if (fApplyGamma) dwClr = pDraw->Gamma.ApplyTo(dwClr);
|
|
png.SetPix(x, y, dwClr);
|
|
}
|
|
}
|
|
|
|
// reset overlay
|
|
if (fSaveOverlayOnly) pMainSfc=pMainSfcBackup;
|
|
|
|
// save png
|
|
if (!png.Save(szFilename)) { Unlock(); return false; }
|
|
|
|
// Unlock
|
|
Unlock();
|
|
|
|
// Success
|
|
return true;
|
|
}
|
|
|
|
|
|
bool C4Surface::AttachPalette()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
double ColorDistance(BYTE *bpRGB1, BYTE *bpRGB2)
|
|
{
|
|
return (double) (Abs(bpRGB1[0]-bpRGB2[0]) + Abs(bpRGB1[1]-bpRGB2[1]) + Abs(bpRGB1[2]-bpRGB2[2])) / 6.0;
|
|
}
|
|
|
|
bool C4Surface::GetSurfaceSize(int &irX, int &irY)
|
|
{
|
|
// simply assign stored values
|
|
irX=Wdt;
|
|
irY=Hgt;
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
bool C4Surface::Lock()
|
|
{
|
|
// lock main sfc
|
|
if (pMainSfc) if (!pMainSfc->Lock()) return false;
|
|
// not yet locked?
|
|
if (!Locked)
|
|
{
|
|
if (fPrimary)
|
|
{
|
|
#ifdef USE_DIRECTX
|
|
if (pD3D)
|
|
{
|
|
D3DLOCKED_RECT lock;
|
|
// locking primary
|
|
if (!pSfc) return false;
|
|
// lock it
|
|
if (pSfc->LockRect(&lock, NULL, 0) != D3D_OK)
|
|
return false;
|
|
pDraw->LockingPrimary();
|
|
// store pitch and pointer
|
|
PrimarySurfaceLockPitch=lock.Pitch;
|
|
PrimarySurfaceLockBits=(BYTE*) lock.pBits;
|
|
}
|
|
#endif //USE_DIRECTX
|
|
|
|
// OpenGL:
|
|
// cannot really lock primary surface, but Get/SetPix will emulate it
|
|
}
|
|
else
|
|
{
|
|
if (!ppTex) return false;
|
|
// lock texture
|
|
// textures will be locked when needed
|
|
}
|
|
}
|
|
// count lock
|
|
Locked++; return true;
|
|
}
|
|
|
|
bool C4Surface::Unlock()
|
|
{
|
|
// unlock main sfc
|
|
if (pMainSfc) pMainSfc->Unlock();
|
|
// locked?
|
|
if (!Locked) return false;
|
|
// decrease lock counter; check if zeroed
|
|
Locked--;
|
|
if (!Locked)
|
|
{
|
|
// zeroed: unlock
|
|
if (fPrimary)
|
|
{
|
|
#ifdef USE_DIRECTX
|
|
if (pD3D)
|
|
{
|
|
if (!pSfc) return false;
|
|
// unlocking primary?
|
|
if (pSfc->UnlockRect() != D3D_OK)
|
|
return false;
|
|
pDraw->PrimaryUnlocked();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// if tex refs exist, free them
|
|
/*FreeTextures();*/
|
|
// otherwise, emulated primary locks in OpenGL
|
|
delete[] PrimarySurfaceLockBits;
|
|
PrimarySurfaceLockBits = 0;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// non-primary unlock: unlock all texture surfaces (if locked)
|
|
C4TexRef **ppTx=ppTex;
|
|
for (int i=0; i<iTexX*iTexY; ++i,++ppTx)
|
|
(*ppTx)->Unlock();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool C4Surface::GetTexAt(C4TexRef **ppTexRef, int &rX, int &rY)
|
|
{
|
|
// texture present?
|
|
if (!ppTex) return false;
|
|
// get pos
|
|
int iX=rX/iTexSize;
|
|
int iY=rY/iTexSize;
|
|
// clip
|
|
if (iX<0 || iY<0 || iX>=iTexX || iY>=iTexY) return false;
|
|
// get texture by pos
|
|
*ppTexRef=*(ppTex+iY*iTexX+iX);
|
|
// adjust pos
|
|
rX-=iX*iTexSize;
|
|
rY-=iY*iTexSize;
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
bool C4Surface::GetLockTexAt(C4TexRef **ppTexRef, int &rX, int &rY)
|
|
{
|
|
// texture present?
|
|
if (!GetTexAt(ppTexRef, rX, rY)) return false;
|
|
// Already partially locked
|
|
if ((*ppTexRef)->texLock.pBits)
|
|
{
|
|
// But not for the requested pixel
|
|
C4Rect & r = (*ppTexRef)->LockSize;
|
|
if (r.x > rX || r.y > rY || (r.x + r.Wdt) < rX || (r.y + r.Hgt) < rY)
|
|
// Unlock, then relock the whole thing
|
|
(*ppTexRef)->Unlock();
|
|
else return true;
|
|
}
|
|
// ensure it's locked
|
|
if (!(*ppTexRef)->Lock()) return false;
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
DWORD C4Surface::GetPixDw(int iX, int iY, bool fApplyModulation)
|
|
{
|
|
BYTE *pBuf = NULL; int iPitch = 0; // TODO: are those initialised to something sensible?
|
|
// backup pos
|
|
int iX2=iX; int iY2=iY;
|
|
// primary?
|
|
if (fPrimary)
|
|
{
|
|
#ifdef USE_GL
|
|
// OpenGL?
|
|
if (pGL)
|
|
{
|
|
if (!PrimarySurfaceLockBits)
|
|
{
|
|
PrimarySurfaceLockBits = new unsigned char[Wdt*Hgt*3 + 1];
|
|
glReadPixels( 0, 0, Wdt, Hgt, GL_BGR, GL_UNSIGNED_BYTE, PrimarySurfaceLockBits);
|
|
PrimarySurfaceLockPitch = Wdt*3;
|
|
}
|
|
return * (DWORD *) (PrimarySurfaceLockBits+(Hgt-iY-1)*PrimarySurfaceLockPitch+iX*3);
|
|
|
|
// copy content into textures
|
|
/*if (!ppTex) if (!CreatePrimaryGLTextures()) return 0;
|
|
// get+lock affected texture - inverse Y as primary is locked upside down!
|
|
iY = Hgt-iY-1;
|
|
C4TexRef *pTexRef;
|
|
if (!GetLockTexAt(&pTexRef, iX, iY)) return 0;
|
|
pBuf=(BYTE *) pTexRef->texLock.pBits;
|
|
iPitch=pTexRef->texLock.Pitch;
|
|
// get pixel
|
|
return *(DWORD *)(pBuf+iY*iPitch+iX*4);*/
|
|
}
|
|
#endif
|
|
#ifdef USE_DIRECTX
|
|
if (!PrimarySurfaceLockBits)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
// clip
|
|
if (iX<0 || iY<0 || iX>=Wdt || iY>=Hgt) return 0;
|
|
// get pixel from primary surface
|
|
WORD pix16;
|
|
switch (dwClrFormat)
|
|
{
|
|
case D3DFMT_X1R5G5B5:
|
|
// 16 bit 5-5-5
|
|
pix16= * (WORD *) (((BYTE *) PrimarySurfaceLockBits)+iY*PrimarySurfaceLockPitch+iX*2);
|
|
return ((pix16 & 0x001f) << 3)
|
|
| ((pix16 & 0x03e0) << 6)
|
|
| ((pix16 & 0x7c00) << 9);
|
|
|
|
case D3DFMT_R5G6B5:
|
|
// 16 bit 5-6-5
|
|
pix16= * (WORD *) (((BYTE *) PrimarySurfaceLockBits)+iY*PrimarySurfaceLockPitch+iX*2);
|
|
return ((pix16 & 0x001f) << 3)
|
|
| ((pix16 & 0x07e0) << 5)
|
|
| ((pix16 & 0xf800) << 8);
|
|
break;
|
|
|
|
case D3DFMT_X8R8G8B8:
|
|
// 32 bit
|
|
return * (DWORD *) (((BYTE *) PrimarySurfaceLockBits)+iY*PrimarySurfaceLockPitch+iX*4);
|
|
default: assert(false); return 0; // should not happen
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// get+lock affected texture
|
|
if (!ppTex) return 0;
|
|
C4TexRef *pTexRef;
|
|
if (!GetLockTexAt(&pTexRef, iX, iY)) return 0;
|
|
pBuf=(BYTE *) pTexRef->texLock.pBits;
|
|
iPitch=pTexRef->texLock.Pitch;
|
|
}
|
|
// get pix of surface
|
|
DWORD dwPix;
|
|
if (byBytesPP == 4)
|
|
{
|
|
// 32 bit
|
|
DWORD *pPix=(DWORD *) (pBuf+iY*iPitch+iX*4);
|
|
dwPix = *pPix;
|
|
}
|
|
else
|
|
{
|
|
// 16 bit
|
|
WORD *pPix=(WORD *) (pBuf+iY*iPitch+iX*2);
|
|
dwPix = ClrW2Dw(*pPix);
|
|
}
|
|
// this is a ColorByOwner-surface?
|
|
if (pMainSfc)
|
|
{
|
|
BYTE byAlpha=BYTE(dwPix>>24);
|
|
// pix is fully transparent?
|
|
if (byAlpha==0x00)
|
|
// then get the main surfaces's pixel
|
|
dwPix = pMainSfc->GetPixDw(iX2, iY2, fApplyModulation);
|
|
else
|
|
{
|
|
// otherwise, it's a ColorByOwner-pixel: adjust the color
|
|
if (fApplyModulation)
|
|
{
|
|
if (pDraw->dwBlitMode & C4GFXBLIT_CLRSFC_MOD2)
|
|
ModulateClrMOD2(dwPix, ClrByOwnerClr);
|
|
else
|
|
ModulateClr(dwPix, ClrByOwnerClr);
|
|
if (pDraw->BlitModulated && !(pDraw->dwBlitMode & C4GFXBLIT_CLRSFC_OWNCLR))
|
|
ModulateClr(dwPix, pDraw->BlitModulateClr);
|
|
}
|
|
else
|
|
ModulateClr(dwPix, ClrByOwnerClr);
|
|
// does it contain transparency? then blit on main sfc
|
|
if (byAlpha)
|
|
{
|
|
DWORD dwMainPix = pMainSfc->GetPixDw(iX2, iY2, fApplyModulation);
|
|
BltAlpha(dwMainPix, dwPix); dwPix=dwMainPix;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// single main surface
|
|
// apply color modulation if desired
|
|
if (fApplyModulation && pDraw->BlitModulated)
|
|
{
|
|
if (pDraw->dwBlitMode & C4GFXBLIT_MOD2)
|
|
ModulateClrMOD2(dwPix, pDraw->BlitModulateClr);
|
|
else
|
|
ModulateClr(dwPix, pDraw->BlitModulateClr);
|
|
}
|
|
}
|
|
// return pixel value
|
|
return dwPix;
|
|
}
|
|
|
|
bool C4Surface::IsPixTransparent(int iX, int iY)
|
|
{
|
|
// get pixel value
|
|
DWORD dwPix=GetPixDw(iX, iY, false);
|
|
// get alpha value
|
|
return (dwPix>>24) < 128;
|
|
}
|
|
|
|
/*bool C4Surface::SetPixEx(int iX, int iY, BYTE byCol, DWORD dwClr)
|
|
{
|
|
// clip
|
|
if ((iX<ClipX) || (iX>ClipX2) || (iY<ClipY) || (iY>ClipY2)) return true;
|
|
// primary?
|
|
if (fPrimary)
|
|
{
|
|
#ifdef USE_GL
|
|
// OpenGL: Use OpenGL API
|
|
if (pGL)
|
|
{
|
|
pGL->DrawPixInt(this, iX, iY, dwClr);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef USE_DIRECTX
|
|
// must be locked!
|
|
if (!Bits) return false;
|
|
// set according to pixel format
|
|
DWORD *pPix32; WORD *pPix16;
|
|
switch (dwClrFormat)
|
|
{
|
|
case D3DFMT_X1R5G5B5:
|
|
// 16 bit 5-5-5
|
|
pPix16=(WORD *) (((BYTE *) Bits)+iY*Pitch+iX*2);
|
|
*pPix16=WORD((dwClr & 0x000000f8) >> 3)
|
|
| WORD((dwClr & 0x0000f800) >> 6)
|
|
| WORD((dwClr & 0x00f80000) >> 9);
|
|
break;
|
|
|
|
case D3DFMT_R5G6B5:
|
|
// 16 bit 5-6-5
|
|
pPix16=(WORD *) (((BYTE *) Bits)+iY*Pitch+iX*2);
|
|
*pPix16=WORD((dwClr & 0x000000f8) >> 3)
|
|
| WORD((dwClr & 0x0000fc00) >> 5)
|
|
| WORD((dwClr & 0x00f80000) >> 8);
|
|
break;
|
|
|
|
case D3DFMT_X8R8G8B8:
|
|
// 32 bit
|
|
pPix32=(DWORD *) (((BYTE *) Bits)+iY*Pitch+iX*4);
|
|
*pPix32=dwClr;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
SetPixDw(iX, iY, dwClr);
|
|
}
|
|
return true;
|
|
}*/
|
|
|
|
bool C4Surface::SetPixDw(int iX, int iY, DWORD dwClr)
|
|
{
|
|
// clip
|
|
if ((iX<ClipX) || (iX>ClipX2) || (iY<ClipY) || (iY>ClipY2)) return true;
|
|
// get+lock affected texture
|
|
if (!ppTex) return false;
|
|
// if color is fully transparent, ensure it's black
|
|
if (dwClr>>24 == 0x00) dwClr=0x00000000;
|
|
C4TexRef *pTexRef;
|
|
#ifdef USE_GL
|
|
// openGL: use glTexSubImage2D
|
|
// This optimization was moved to LockForUpdate, as it only slows down mass updates here
|
|
// Keep this code in case there is a need for fast single pixel updates again
|
|
if (0 && pGL && pGL->pCurrCtx)
|
|
{
|
|
if (!GetTexAt(&pTexRef, iX, iY))
|
|
return false;
|
|
// If the texture is not copied into system memory, modify it directly in the video memory
|
|
if (!pTexRef->texLock.pBits)
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, pTexRef->texName);
|
|
if (byBytesPP == 4)
|
|
{
|
|
// 32 Bit
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, iX, iY, 1, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &dwClr);
|
|
}
|
|
else
|
|
{
|
|
// 16 bit
|
|
uint16_t wClr=ClrDw2W(dwClr);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, iX, iY, 1, 1, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV, &wClr);
|
|
}
|
|
return true;
|
|
}
|
|
// Otherwise, make sure that the texlock covers the new pixel
|
|
C4Rect & r = pTexRef->LockSize;
|
|
if (r.x > iX || r.y > iY || (r.x + r.Wdt) < iX || (r.y + r.Hgt) < iY)
|
|
{
|
|
// Unlock, then relock the whole thing
|
|
pTexRef->Unlock();
|
|
if (!pTexRef->Lock()) return false;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (!GetLockTexAt(&pTexRef, iX, iY)) return false;
|
|
}
|
|
// ...and set in actual surface
|
|
if (byBytesPP == 4)
|
|
{
|
|
// 32 bit
|
|
pTexRef->SetPix4(iX, iY, dwClr);
|
|
}
|
|
else
|
|
{
|
|
// 16 bit
|
|
pTexRef->SetPix2(iX, iY, ClrDw2W(dwClr));
|
|
}
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
bool C4Surface::SetPixAlpha(int iX, int iY, BYTE byAlpha)
|
|
{
|
|
// clip
|
|
if ((iX<ClipX) || (iX>ClipX2) || (iY<ClipY) || (iY>ClipY2)) return true;
|
|
// get+lock affected texture
|
|
if (!ppTex) return false;
|
|
C4TexRef *pTexRef;
|
|
if (!GetLockTexAt(&pTexRef, iX, iY)) return false;
|
|
// set alpha value of pix in surface
|
|
if (byBytesPP == 4)
|
|
// 32 bit
|
|
*(((BYTE *) pTexRef->texLock.pBits)+iY*pTexRef->texLock.Pitch+iX*4+3)=byAlpha;
|
|
else
|
|
{
|
|
// 16 bit
|
|
BYTE *pPix=((BYTE *) pTexRef->texLock.pBits)+iY*pTexRef->texLock.Pitch+iX*2+1;
|
|
*pPix = (*pPix & 0x0f) | (byAlpha & 0xf0);
|
|
}
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
bool C4Surface::BltPix(int iX, int iY, C4Surface *sfcSource, int iSrcX, int iSrcY, bool fTransparency)
|
|
{
|
|
// 16- or 32bit-blit. lock target
|
|
C4TexRef *pTexRef;
|
|
if (!GetLockTexAt(&pTexRef, iX, iY)) return false;
|
|
if (byBytesPP == 4)
|
|
{
|
|
// 32 bit
|
|
DWORD *pPix32=(DWORD *) (((BYTE *) pTexRef->texLock.pBits)+iY*pTexRef->texLock.Pitch+iX*4);
|
|
// get source pix as dword
|
|
DWORD srcPix=sfcSource->GetPixDw(iSrcX, iSrcY, true);
|
|
// merge
|
|
if (!fTransparency)
|
|
{
|
|
// set it
|
|
*pPix32=srcPix;
|
|
}
|
|
else
|
|
{
|
|
if (pDraw->dwBlitMode & C4GFXBLIT_ADDITIVE)
|
|
BltAlphaAdd(*pPix32, srcPix);
|
|
else
|
|
BltAlpha(*pPix32, srcPix);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 16 bit
|
|
WORD *pPix16=(WORD *) (((BYTE *) pTexRef->texLock.pBits)+iY*pTexRef->texLock.Pitch+iX*2);
|
|
// get source pix as dword
|
|
DWORD srcPix=sfcSource->GetPixDw(iSrcX, iSrcY, true);
|
|
if (!fTransparency)
|
|
{
|
|
// set it
|
|
*pPix16=ClrDw2W(srcPix);
|
|
}
|
|
else
|
|
{
|
|
// merge in 32 bit
|
|
DWORD dwDst=ClrW2Dw(*pPix16);
|
|
if (pDraw->dwBlitMode & C4GFXBLIT_ADDITIVE)
|
|
BltAlphaAdd(dwDst, srcPix);
|
|
else
|
|
BltAlpha(dwDst, srcPix);
|
|
// set
|
|
*pPix16=ClrDw2W(dwDst);
|
|
}
|
|
}
|
|
// done
|
|
return true;
|
|
}
|
|
|
|
void C4Surface::ClearBoxDw(int iX, int iY, int iWdt, int iHgt)
|
|
{
|
|
// lock
|
|
if (!Locked) return;
|
|
// clip to target size
|
|
if (iX<0) { iWdt+=iX; iX=0; }
|
|
if (iY<0) { iHgt+=iY; iY=0; }
|
|
int iOver;
|
|
iOver=Wdt-(iX+iWdt); if (iOver<0) iWdt+=iOver;
|
|
iOver=Hgt-(iY+iHgt); if (iOver<0) iHgt+=iOver;
|
|
// get textures involved
|
|
int iTexX1=iX/iTexSize;
|
|
int iTexY1=iY/iTexSize;
|
|
int iTexX2=Min((iX+iWdt-1)/iTexSize +1, iTexX);
|
|
int iTexY2=Min((iY+iHgt-1)/iTexSize +1, iTexY);
|
|
// clear basesfc?
|
|
bool fBaseSfc=false;
|
|
if (pMainSfc) if (pMainSfc->ppTex) fBaseSfc=true;
|
|
// clear all these textures
|
|
for (int y=iTexY1; y<iTexY2; ++y)
|
|
{
|
|
for (int x=iTexX1; x<iTexX2; ++x)
|
|
{
|
|
C4TexRef *pTex = *(ppTex + y * iTexX + x);
|
|
// get current offset in texture
|
|
int iBlitX=iTexSize*x;
|
|
int iBlitY=iTexSize*y;
|
|
// get clearing bounds in texture
|
|
C4Rect rtClear = C4Rect(0, 0, pTex->iSizeX, pTex->iSizeY);
|
|
rtClear.Intersect(C4Rect(iX - iBlitX, iY - iBlitY, iWdt, iHgt));
|
|
// is there a base-surface to be cleared first?
|
|
if (fBaseSfc)
|
|
{
|
|
// then get this surface as same offset as from other surface
|
|
// assuming this is only valid as long as there's no texture management,
|
|
// organizing partially used textures together!
|
|
C4TexRef *pBaseTex = *(pMainSfc->ppTex + y * iTexX + x);
|
|
pBaseTex->ClearRect(rtClear);
|
|
}
|
|
// clear this texture
|
|
pTex->ClearRect(rtClear);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool C4Surface::CopyBytes(BYTE *pImageData)
|
|
{
|
|
// copy image data directly into textures
|
|
C4TexRef **ppCurrTex = ppTex, *pTex = *ppTex;
|
|
int iSrcPitch = Wdt * byBytesPP; int iLineTotal = 0;
|
|
for (int iY=0; iY<iTexY; ++iY)
|
|
{
|
|
BYTE *pSource = pImageData + iSrcPitch * iLineTotal;
|
|
int iLastHeight=pTex->iSizeY; int iXImgPos=0;
|
|
for (int iX=0; iX<iTexX; ++iX)
|
|
{
|
|
pTex = *ppCurrTex++;
|
|
if (!pTex->Lock()) return false;
|
|
BYTE *pTarget = (BYTE*)pTex->texLock.pBits;
|
|
int iCpyNum = Min(pTex->iSizeX, Wdt-iXImgPos)*byBytesPP;
|
|
int iYMax = Min(pTex->iSizeY, Hgt-iLineTotal);
|
|
for (int iLine = 0; iLine < iYMax; ++iLine)
|
|
{
|
|
memcpy(pTarget, pSource, iCpyNum);
|
|
pSource += iSrcPitch;
|
|
// FIXME: use pTex->texLock.Pitch here?
|
|
pTarget += pTex->iSizeX*byBytesPP;
|
|
}
|
|
pSource += iCpyNum - iSrcPitch*iYMax;
|
|
iXImgPos += pTex->iSizeX;
|
|
}
|
|
iLineTotal += iLastHeight;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
C4TexRef::C4TexRef(int iSizeX, int iSizeY, bool fSingle)
|
|
{
|
|
// zero fields
|
|
#ifdef USE_DIRECTX
|
|
pTex=NULL;
|
|
#endif
|
|
#ifdef USE_GL
|
|
texName=0;
|
|
#endif
|
|
texLock.pBits=NULL; fIntLock=false;
|
|
// store size
|
|
this->iSizeX=iSizeX;
|
|
this->iSizeY=iSizeY;
|
|
// add to texture manager
|
|
if (!pTexMgr) pTexMgr = new C4TexMgr();
|
|
pTexMgr->RegTex(this);
|
|
// create texture: check ddraw
|
|
if (!pDraw) return;
|
|
if (!pDraw->DeviceReady()) return;
|
|
// create it!
|
|
#ifdef USE_DIRECTX
|
|
if (pD3D)
|
|
{
|
|
// Direct3D
|
|
bool fRenderTarget = fSingle && !Config.Graphics.NoOffscreenBlits;
|
|
if (pD3D->lpDevice->CreateTexture(iSizeX, iSizeY, 1, fRenderTarget ? D3DUSAGE_RENDERTARGET : 0, pD3D->dwSurfaceType, fRenderTarget ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED, &pTex, NULL) != D3D_OK)
|
|
{
|
|
pDraw->Error("Error creating surface");
|
|
return;
|
|
}
|
|
// empty texture
|
|
if (!Lock()) return;
|
|
FillMemory(texLock.pBits, texLock.Pitch*iSizeY, 0x00);
|
|
Unlock();
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef USE_GL
|
|
if (pGL)
|
|
{
|
|
// OpenGL
|
|
// create mem array for texture creation
|
|
texLock.pBits = new unsigned char[iSizeX*iSizeY*pGL->byByteCnt];
|
|
texLock.Pitch = iSizeX*pGL->byByteCnt;
|
|
memset(texLock.pBits, 0x00, texLock.Pitch*iSizeY);
|
|
// turn mem array into texture
|
|
Unlock();
|
|
}
|
|
else
|
|
#endif
|
|
if (pDraw)
|
|
{
|
|
texLock.pBits = new unsigned char[iSizeX*iSizeY*pDraw->byByteCnt];
|
|
texLock.Pitch = iSizeX*pDraw->byByteCnt;
|
|
memset(texLock.pBits, 0x00, texLock.Pitch*iSizeY);
|
|
// Always locked
|
|
LockSize.x = LockSize.y = 0;
|
|
LockSize.Wdt = iSizeX; LockSize.Hgt = iSizeY;
|
|
}
|
|
}
|
|
|
|
C4TexRef::~C4TexRef()
|
|
{
|
|
fIntLock=false;
|
|
// free texture
|
|
#ifdef USE_DIRECTX
|
|
if (pD3D)
|
|
{
|
|
if (texLock.pBits) Unlock();
|
|
if (pTex) pTex->Release();
|
|
}
|
|
#endif
|
|
#ifdef USE_GL
|
|
if (pGL)
|
|
{
|
|
if (texName && pGL->pCurrCtx) glDeleteTextures(1, &texName);
|
|
}
|
|
#endif
|
|
if (pDraw) delete [] static_cast<unsigned char*>(texLock.pBits); texLock.pBits = 0;
|
|
// remove from texture manager
|
|
pTexMgr->UnregTex(this);
|
|
}
|
|
|
|
bool C4TexRef::LockForUpdate(C4Rect & rtUpdate)
|
|
{
|
|
// already locked?
|
|
if (texLock.pBits)
|
|
{
|
|
// fully locked
|
|
if (LockSize.x == 0 && LockSize.Wdt == iSizeX && LockSize.y == 0 && LockSize.Hgt == iSizeY)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Commit previous changes to the texture
|
|
Unlock();
|
|
}
|
|
}
|
|
// lock
|
|
#ifdef USE_DIRECTX
|
|
if (pD3D)
|
|
{
|
|
RECT r;
|
|
r.left = rtUpdate.x;
|
|
r.top = rtUpdate.y;
|
|
r.right = rtUpdate.x + rtUpdate.Wdt;
|
|
r.bottom = rtUpdate.y + rtUpdate.Hgt;
|
|
if (pTex)
|
|
if (pTex->LockRect(0, &texLock, &r, D3DLOCK_DISCARD) == D3D_OK)
|
|
{
|
|
LockSize = rtUpdate;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef USE_GL
|
|
if (pGL)
|
|
{
|
|
if (texName)
|
|
{
|
|
// prepare texture data
|
|
texLock.pBits = new unsigned char[rtUpdate.Wdt * rtUpdate.Hgt * pGL->byByteCnt];
|
|
texLock.Pitch = rtUpdate.Wdt * pGL->byByteCnt;
|
|
LockSize = rtUpdate;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// nothing to do
|
|
}
|
|
// failure
|
|
return false;
|
|
}
|
|
|
|
bool C4TexRef::Lock()
|
|
{
|
|
// already locked?
|
|
if (texLock.pBits) return true;
|
|
LockSize.Wdt = iSizeX; LockSize.Hgt = iSizeY;
|
|
LockSize.x = LockSize.y = 0;
|
|
// lock
|
|
#ifdef USE_DIRECTX
|
|
if (pD3D)
|
|
{
|
|
if (pTex)
|
|
if (pTex->LockRect(0, &texLock, NULL, 0) == D3D_OK) return true;
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef USE_GL
|
|
if (pGL)
|
|
{
|
|
if (texName)
|
|
{
|
|
if (!pGL->pCurrCtx) return false;
|
|
// get texture
|
|
texLock.pBits = new unsigned char[iSizeX*iSizeY*pGL->byByteCnt];
|
|
texLock.Pitch = iSizeX * pGL->byByteCnt;
|
|
glBindTexture(GL_TEXTURE_2D, texName);
|
|
glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, pDraw->byByteCnt == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV, texLock.pBits);
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// nothing to do
|
|
}
|
|
// failure
|
|
return false;
|
|
}
|
|
|
|
void C4TexRef::Unlock()
|
|
{
|
|
// locked?
|
|
if (!texLock.pBits || fIntLock) return;
|
|
#ifdef USE_DIRECTX
|
|
if (pD3D)
|
|
{
|
|
// unlock
|
|
if (pTex) pTex->UnlockRect(0);
|
|
texLock.pBits=NULL;
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef USE_GL
|
|
if (pGL)
|
|
{
|
|
if (!pGL->pCurrCtx)
|
|
{
|
|
// BREAKPOINT_HERE;
|
|
assert(pGL->pMainCtx);
|
|
pGL->pMainCtx->Select();
|
|
}
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
if (!texName)
|
|
{
|
|
// create a new texture
|
|
glGenTextures(1, &texName);
|
|
glBindTexture(GL_TEXTURE_2D, texName);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
// Default, changed in PerformBlt if necessary
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, 4, iSizeX, iSizeY, 0, GL_BGRA, pDraw->byByteCnt == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV, texLock.pBits);
|
|
}
|
|
else
|
|
{
|
|
// reuse the existing texture
|
|
glBindTexture(GL_TEXTURE_2D, texName);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
LockSize.x, LockSize.y, LockSize.Wdt, LockSize.Hgt,
|
|
GL_BGRA, pDraw->byByteCnt == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV, texLock.pBits);
|
|
}
|
|
delete[] static_cast<unsigned char*>(texLock.pBits); texLock.pBits=NULL;
|
|
// switch back to original context
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// nothing to do
|
|
}
|
|
}
|
|
|
|
bool C4TexRef::ClearRect(C4Rect &rtClear)
|
|
{
|
|
// ensure locked
|
|
if (!LockForUpdate(rtClear)) return false;
|
|
// clear pixels
|
|
int y;
|
|
switch (pDraw->byByteCnt)
|
|
{
|
|
case 2:
|
|
for (y = rtClear.y; y < rtClear.y + rtClear.Hgt; ++y)
|
|
{
|
|
for (int x = rtClear.x; x < rtClear.x + rtClear.Wdt; ++x)
|
|
SetPix2(x, y, 0x0000);
|
|
}
|
|
break;
|
|
case 4:
|
|
for (y = rtClear.y; y < rtClear.y + rtClear.Hgt; ++y)
|
|
{
|
|
for (int x = rtClear.x; x < rtClear.x + rtClear.Wdt; ++x)
|
|
SetPix4(x, y, 0x00000000);
|
|
}
|
|
break;
|
|
}
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
bool C4TexRef::FillBlack()
|
|
{
|
|
// ensure locked
|
|
if (!Lock()) return false;
|
|
// clear pixels
|
|
int y;
|
|
switch (pDraw->byByteCnt)
|
|
{
|
|
case 2:
|
|
for (y=0; y<iSizeY; ++y)
|
|
{
|
|
for (int x = 0; x < iSizeX; ++x)
|
|
SetPix2(x, y, 0xf000);
|
|
}
|
|
break;
|
|
case 4:
|
|
for (y=0; y<iSizeY; ++y)
|
|
{
|
|
for (int x = 0; x < iSizeX; ++x)
|
|
SetPix4(x, y, 0xff000000);
|
|
}
|
|
break;
|
|
}
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
// texture manager
|
|
|
|
C4TexMgr::C4TexMgr()
|
|
{
|
|
// clear textures
|
|
Textures.clear();
|
|
}
|
|
|
|
C4TexMgr::~C4TexMgr()
|
|
{
|
|
// unlock all textures
|
|
IntUnlock();
|
|
}
|
|
|
|
void C4TexMgr::RegTex(C4TexRef *pTex)
|
|
{
|
|
// add texture to list
|
|
Textures.push_front(pTex);
|
|
}
|
|
|
|
void C4TexMgr::UnregTex(C4TexRef *pTex)
|
|
{
|
|
// remove texture from list
|
|
Textures.remove(pTex);
|
|
// if list is empty, remove self
|
|
if (Textures.empty()) { delete this; pTexMgr=NULL; }
|
|
}
|
|
|
|
void C4TexMgr::IntLock()
|
|
{
|
|
// lock all textures
|
|
int j=Textures.size();
|
|
for (std::list<C4TexRef *>::iterator i=Textures.begin(); j--; ++i)
|
|
{
|
|
C4TexRef *pRef = *i;
|
|
if (pRef->Lock() && pRef->texLock.pBits)
|
|
{
|
|
pRef->fIntLock = true;
|
|
#ifdef USE_GL
|
|
// Release the underlying texture with GL and recreate
|
|
// it on unlock, so that the texture survives
|
|
// context recreation.
|
|
if(pGL)
|
|
{
|
|
glDeleteTextures(1, &pRef->texName);
|
|
pRef->texName = 0;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void C4TexMgr::IntUnlock()
|
|
{
|
|
// unlock all internally locked textures
|
|
int j=Textures.size();
|
|
for (std::list<C4TexRef *>::iterator i=Textures.begin(); j--; ++i)
|
|
{
|
|
C4TexRef *pRef = *i;
|
|
if (pRef->fIntLock) { pRef->fIntLock = false; pRef->Unlock(); }
|
|
}
|
|
}
|
|
|
|
C4TexMgr *pTexMgr;
|