forked from Mirrors/openclonk
890 lines
20 KiB
C++
890 lines
20 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 1998-2000, Matthes Bender
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2013, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
// a wrapper class to DirectDraw surfaces
|
|
|
|
#include "C4Include.h"
|
|
#include "graphics/C4Surface.h"
|
|
|
|
#include "platform/StdFile.h"
|
|
#include "c4group/CStdFile.h"
|
|
#include "platform/C4App.h"
|
|
#include "graphics/C4DrawGL.h"
|
|
#include "platform/C4Window.h"
|
|
#include "platform/StdRegistry.h"
|
|
#include "graphics/C4Draw.h"
|
|
#include "graphics/Bitmap256.h"
|
|
#include "graphics/StdPNG.h"
|
|
#include "config/C4Config.h"
|
|
#include "lib/StdColors.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, int iFlags) : fIsBackground(false)
|
|
{
|
|
Default();
|
|
// create
|
|
Create(iWdt, iHgt, iFlags);
|
|
}
|
|
|
|
C4Surface::C4Surface(C4AbstractApp * pApp, C4Window * pWindow):
|
|
Wdt(0), Hgt(0)
|
|
{
|
|
Default();
|
|
fPrimary=true;
|
|
this->pWindow=pWindow;
|
|
// create rendering context
|
|
#ifndef USE_CONSOLE
|
|
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;
|
|
pMainSfc=NULL;
|
|
pNormalSfc=NULL;
|
|
#ifndef USE_CONSOLE
|
|
pCtx=NULL;
|
|
#endif
|
|
pWindow=NULL;
|
|
ClrByOwnerClr=0;
|
|
iTexSize=0;
|
|
fIsBackground=false;
|
|
#ifdef _DEBUG
|
|
dbg_idx = 0;
|
|
#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!
|
|
texture = std::move(psfcFrom->texture);
|
|
pMainSfc=psfcFrom->pMainSfc;
|
|
pNormalSfc=psfcFrom->pNormalSfc;
|
|
ClrByOwnerClr=psfcFrom->ClrByOwnerClr;
|
|
iTexSize=psfcFrom->iTexSize;
|
|
#ifndef USE_CONSOLE
|
|
Format=psfcFrom->Format;
|
|
#endif
|
|
fIsBackground = psfcFrom->fIsBackground;
|
|
// default other sfc
|
|
psfcFrom->Default();
|
|
}
|
|
|
|
void C4Surface::Clear()
|
|
{
|
|
// Undo all locks
|
|
while (Locked) Unlock();
|
|
// release surface
|
|
#ifndef USE_CONSOLE
|
|
if (pCtx)
|
|
{
|
|
delete pCtx;
|
|
pCtx = 0;
|
|
}
|
|
#endif
|
|
texture.reset();
|
|
#ifdef _DEBUG
|
|
dbg_idx = 0;
|
|
#endif
|
|
}
|
|
|
|
bool C4Surface::IsRenderTarget()
|
|
{
|
|
// primary is always OK...
|
|
return fPrimary;
|
|
}
|
|
|
|
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=Clamp(iX,0,Wdt-1); ClipY=Clamp(iY,0,Hgt-1);
|
|
ClipX2=Clamp(iX2,0,Wdt-1); ClipY2=Clamp(iY2,0,Hgt-1);
|
|
}
|
|
|
|
bool C4Surface::Create(int iWdt, int iHgt, int iFlags)
|
|
{
|
|
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
|
|
#ifndef USE_CONSOLE
|
|
Format=pGL->sfcFmt;
|
|
#endif
|
|
// create texture
|
|
iTexSize = std::max(iWdt, iHgt);
|
|
texture = std::make_unique<C4TexRef>(iWdt, iHgt, iFlags);
|
|
// 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 (TODO: copy flags)
|
|
if (!Create(fromSfc.Wdt, fromSfc.Hgt)) return false;
|
|
// Blit copy
|
|
if (!pDraw->BlitSurface(&fromSfc, this, 0, 0, false))
|
|
{ Clear(); return false; }
|
|
// Success
|
|
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 = std::max<int>(std::max<int>(R,G),B);
|
|
cMin = std::min<int>(std::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->texture) return false;
|
|
// create in same size
|
|
if (!Create(pBySurface->Wdt, pBySurface->Hgt)) 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;
|
|
}
|
|
|
|
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;
|
|
#ifndef USE_CONSOLE
|
|
return pCtx->PageFlip();
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool C4Surface::ReadBMP(CStdStream &hGroup, int iFlags)
|
|
{
|
|
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),
|
|
std::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, iFlags)) 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::SavePNG(const char *szFilename, bool fSaveAlpha, bool fSaveOverlayOnly, bool use_background_thread)
|
|
{
|
|
// Lock - WARNING - maybe locking primary surface here...
|
|
if (!Lock()) return false;
|
|
|
|
// create png file
|
|
std::unique_ptr<CPNGFile> png(new CPNGFile());
|
|
if (!png->Create(Wdt, Hgt, fSaveAlpha)) { Unlock(); return false; }
|
|
|
|
// reset overlay if desired
|
|
C4Surface *pMainSfcBackup = NULL;
|
|
if (fSaveOverlayOnly) { pMainSfcBackup=pMainSfc; pMainSfc=NULL; }
|
|
|
|
#ifndef USE_CONSOLE
|
|
if (fPrimary)
|
|
{
|
|
// Take shortcut. FIXME: Check Endian
|
|
for (int y = 0; y < Hgt; ++y)
|
|
glReadPixels(0, Hgt - y - 1, Wdt, 1, fSaveAlpha ? GL_BGRA : GL_BGR, GL_UNSIGNED_BYTE, png->GetRow(y));
|
|
}
|
|
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);
|
|
png->SetPix(x, y, dwClr);
|
|
}
|
|
}
|
|
|
|
// reset overlay
|
|
if (fSaveOverlayOnly) pMainSfc=pMainSfcBackup;
|
|
|
|
// Unlock
|
|
Unlock();
|
|
|
|
// save png - either directly or delayed in a background thread if desired
|
|
if (use_background_thread)
|
|
{
|
|
CPNGFile::ScheduleSaving(png.release(), szFilename);
|
|
}
|
|
else
|
|
{
|
|
if (!png->Save(szFilename)) return false;
|
|
}
|
|
|
|
// 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;
|
|
// lock texture
|
|
if (!Locked && !fPrimary && !texture)
|
|
return false;
|
|
// 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 and unlock then
|
|
Locked--;
|
|
if (!Locked)
|
|
{
|
|
if (fPrimary)
|
|
{
|
|
// emulated primary locks in OpenGL
|
|
delete[] PrimarySurfaceLockBits;
|
|
PrimarySurfaceLockBits = 0;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// non-primary unlock: unlock all texture surfaces (if locked)
|
|
if (texture)
|
|
texture->Unlock();
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
#ifndef USE_CONSOLE
|
|
if (!PrimarySurfaceLockBits)
|
|
{
|
|
PrimarySurfaceLockBits = new unsigned char[Wdt*Hgt*3];
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
glReadPixels( 0, 0, Wdt, Hgt, GL_BGR, GL_UNSIGNED_BYTE, PrimarySurfaceLockBits);
|
|
PrimarySurfaceLockPitch = Wdt*3;
|
|
}
|
|
return * (DWORD *) (PrimarySurfaceLockBits+(Hgt-iY-1)*PrimarySurfaceLockPitch+iX*3);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// get+lock affected texture
|
|
if (!texture) return 0;
|
|
texture->Lock();
|
|
pBuf=(BYTE *) texture->texLock.pBits;
|
|
iPitch=texture->texLock.Pitch;
|
|
}
|
|
// get pix of surface
|
|
DWORD dwPix;
|
|
DWORD *pPix=(DWORD *) (pBuf+iY*iPitch+iX*4);
|
|
dwPix = *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::SetPixDw(int iX, int iY, DWORD dwClr)
|
|
{
|
|
// clip
|
|
if ((iX<ClipX) || (iX>ClipX2) || (iY<ClipY) || (iY>ClipY2)) return true;
|
|
// get+lock affected texture
|
|
if (!texture) return false;
|
|
texture->Lock();
|
|
// if color is fully transparent, ensure it's black
|
|
if (dwClr>>24 == 0x00) dwClr=0x00000000;
|
|
// ...and set in actual surface
|
|
texture->SetPix(iX, iY, dwClr);
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
bool C4Surface::BltPix(int iX, int iY, C4Surface *sfcSource, int iSrcX, int iSrcY, bool fTransparency)
|
|
{
|
|
// 32bit-blit. lock target
|
|
if (!texture) return false;
|
|
texture->Lock();
|
|
DWORD *pPix32 = (DWORD *)(((BYTE *)texture->texLock.pBits) + iY*texture->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);
|
|
}
|
|
// 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;
|
|
C4Rect rtClear{ iX, iY, iWdt, iHgt };
|
|
if (pMainSfc && pMainSfc->texture)
|
|
{
|
|
// assuming this is only valid as long as there's no texture management,
|
|
// organizing partially used textures together!
|
|
pMainSfc->texture->ClearRect(rtClear);
|
|
}
|
|
// clear this texture
|
|
texture->ClearRect(rtClear);
|
|
}
|
|
|
|
C4TexRef::C4TexRef(int iSizeX, int iSizeY, int iFlags)
|
|
{
|
|
// zero fields
|
|
#ifndef USE_CONSOLE
|
|
texName = 0;
|
|
#endif
|
|
texLock.pBits=NULL; fIntLock=false;
|
|
// store size
|
|
this->iSizeX=iSizeX;
|
|
this->iSizeY=iSizeY;
|
|
this->iFlags=iFlags;
|
|
// 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!
|
|
// Reserve video memory
|
|
CreateTexture();
|
|
|
|
if ((iFlags & C4SF_Unlocked) == 0 && pDraw)
|
|
{
|
|
texLock.pBits = new unsigned char[iSizeX * iSizeY * C4Draw::COLOR_DEPTH_BYTES];
|
|
texLock.Pitch = iSizeX * C4Draw::COLOR_DEPTH_BYTES;
|
|
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
|
|
#ifndef USE_CONSOLE
|
|
if (pGL && 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);
|
|
}
|
|
|
|
void C4TexRef::CreateTexture()
|
|
{
|
|
#ifndef USE_CONSOLE
|
|
assert(texName == 0);
|
|
|
|
const bool fTileable = (iFlags & C4SF_Tileable) != 0;
|
|
const bool fMipMap = (iFlags & C4SF_MipMap) != 0;
|
|
|
|
glGenTextures(1, &texName);
|
|
glBindTexture(GL_TEXTURE_2D, texName);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, fTileable ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, fTileable ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, fMipMap ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iSizeX, iSizeY, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
|
|
if (fMipMap) glGenerateMipmap(GL_TEXTURE_2D);
|
|
#endif
|
|
}
|
|
|
|
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
|
|
#ifndef USE_CONSOLE
|
|
// prepare texture data
|
|
texLock.pBits = new unsigned char[rtUpdate.Wdt * rtUpdate.Hgt * C4Draw::COLOR_DEPTH_BYTES];
|
|
texLock.Pitch = rtUpdate.Wdt * C4Draw::COLOR_DEPTH_BYTES;
|
|
LockSize = rtUpdate;
|
|
return true;
|
|
#endif
|
|
// 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
|
|
#ifndef USE_CONSOLE
|
|
if (texName)
|
|
{
|
|
if (!pGL->pCurrCtx) return false;
|
|
// get texture
|
|
texLock.pBits = new unsigned char[iSizeX * iSizeY * C4Draw::COLOR_DEPTH_BYTES];
|
|
texLock.Pitch = iSizeX * C4Draw::COLOR_DEPTH_BYTES;
|
|
glBindTexture(GL_TEXTURE_2D, texName);
|
|
glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, texLock.pBits);
|
|
return true;
|
|
}
|
|
#endif
|
|
{
|
|
// nothing to do
|
|
}
|
|
// failure
|
|
return false;
|
|
}
|
|
|
|
void C4TexRef::Unlock()
|
|
{
|
|
// locked?
|
|
if (!texLock.pBits || fIntLock) return;
|
|
#ifndef USE_CONSOLE
|
|
if (!pGL->pCurrCtx)
|
|
{
|
|
// BREAKPOINT_HERE;
|
|
assert(pGL->pMainCtx);
|
|
pGL->pMainCtx->Select();
|
|
}
|
|
|
|
const bool fTileable = (iFlags & C4SF_Tileable) != 0;
|
|
const bool fMipMap = (iFlags & C4SF_MipMap) != 0;
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
// reuse the existing texture
|
|
glBindTexture(GL_TEXTURE_2D, texName);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
LockSize.x, LockSize.y, LockSize.Wdt, LockSize.Hgt,
|
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, texLock.pBits);
|
|
|
|
delete[] static_cast<unsigned char*>(texLock.pBits); texLock.pBits=NULL;
|
|
if (fMipMap) glGenerateMipmap(GL_TEXTURE_2D);
|
|
#endif
|
|
}
|
|
|
|
bool C4TexRef::ClearRect(C4Rect &rtClear)
|
|
{
|
|
// ensure locked
|
|
if (!LockForUpdate(rtClear)) return false;
|
|
// clear pixels
|
|
int y;
|
|
for (y = rtClear.y; y < rtClear.y + rtClear.Hgt; ++y)
|
|
{
|
|
for (int x = rtClear.x; x < rtClear.x + rtClear.Wdt; ++x)
|
|
SetPix(x, y, 0x00000000);
|
|
}
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
bool C4TexRef::FillBlack()
|
|
{
|
|
// ensure locked
|
|
if (!Lock()) return false;
|
|
// clear pixels
|
|
int y;
|
|
for (y=0; y<iSizeY; ++y)
|
|
{
|
|
for (int x = 0; x < iSizeX; ++x)
|
|
SetPix(x, y, 0xff000000);
|
|
}
|
|
// 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;
|
|
#ifndef USE_CONSOLE
|
|
// 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->CreateTexture();
|
|
pRef->Unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
C4TexMgr *pTexMgr;
|