1 /*
2 * OpenClonk, http://www.openclonk.org
3 *
4 * Copyright (c) 1998-2000, Matthes Bender
5 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6 * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17 // a wrapper class to DirectDraw surfaces
18
19 #include "C4Include.h"
20 #include "C4ForbidLibraryCompilation.h"
21 #include "graphics/C4Surface.h"
22
23 #include "c4group/CStdFile.h"
24 #include "graphics/Bitmap256.h"
25 #include "graphics/C4Draw.h"
26 #include "graphics/C4DrawGL.h"
27 #include "graphics/StdPNG.h"
28 #include "lib/StdColors.h"
29 #include "platform/C4App.h"
30 #include "platform/C4Window.h"
31 #include "platform/StdRegistry.h"
32
33 #ifdef HAVE_IO_H
34 #include <io.h>
35 #endif
36
C4Surface()37 C4Surface::C4Surface()
38 {
39 Default();
40 }
41
C4Surface(int iWdt,int iHgt,int iFlags)42 C4Surface::C4Surface(int iWdt, int iHgt, int iFlags)
43 {
44 Default();
45 // create
46 Create(iWdt, iHgt, iFlags);
47 }
48
C4Surface(C4AbstractApp * pApp,C4Window * pWindow)49 C4Surface::C4Surface(C4AbstractApp * pApp, C4Window * pWindow):
50 Wdt(0), Hgt(0)
51 {
52 Default();
53 fPrimary=true;
54 this->pWindow=pWindow;
55 // create rendering context
56 #ifndef USE_CONSOLE
57 pCtx = pGL->CreateContext(pWindow, pApp);
58 #endif
59 // reset clipping
60 NoClip();
61 }
62
~C4Surface()63 C4Surface::~C4Surface()
64 {
65 /* for (C4ObjectLink *lnk = ::Objects.First; lnk; lnk=lnk->Next)
66 if (lnk->Obj->Menu)
67 lnk->Obj->Menu->AssertSurfaceNotUsed(this);*/
68 Clear();
69 }
70
Default()71 void C4Surface::Default()
72 {
73 Wdt=Hgt=0;
74 Scale=1;
75 PrimarySurfaceLockPitch=0; PrimarySurfaceLockBits=nullptr;
76 ClipX=ClipY=ClipX2=ClipY2=0;
77 Locked=0;
78 Attached=false;
79 fPrimary=false;
80 pMainSfc=nullptr;
81 pNormalSfc=nullptr;
82 #ifndef USE_CONSOLE
83 pCtx=nullptr;
84 #endif
85 pWindow=nullptr;
86 ClrByOwnerClr=0;
87 iTexSize=0;
88 fIsBackground=false;
89 #ifdef _DEBUG
90 dbg_idx = 0;
91 #endif
92 }
93
MoveFrom(C4Surface * psfcFrom)94 void C4Surface::MoveFrom(C4Surface *psfcFrom)
95 {
96 // clear own
97 Clear();
98 // safety
99 if (!psfcFrom) return;
100 // grab data from other sfc
101 #ifdef _DEBUG
102 dbg_idx = psfcFrom->dbg_idx;
103 #endif
104 Wdt=psfcFrom->Wdt; Hgt=psfcFrom->Hgt;
105 PrimarySurfaceLockPitch=psfcFrom->PrimarySurfaceLockPitch;
106 PrimarySurfaceLockBits=psfcFrom->PrimarySurfaceLockBits;
107 psfcFrom->PrimarySurfaceLockBits=nullptr;
108 ClipX=psfcFrom->ClipX; ClipY=psfcFrom->ClipY;
109 ClipX2=psfcFrom->ClipX2; ClipY2=psfcFrom->ClipY2;
110 Locked=psfcFrom->Locked;
111 Attached=psfcFrom->Attached;
112 fPrimary=psfcFrom->fPrimary; // shouldn't be true!
113 texture = std::move(psfcFrom->texture);
114 pMainSfc=psfcFrom->pMainSfc;
115 pNormalSfc=psfcFrom->pNormalSfc;
116 ClrByOwnerClr=psfcFrom->ClrByOwnerClr;
117 iTexSize=psfcFrom->iTexSize;
118 #ifndef USE_CONSOLE
119 Format=psfcFrom->Format;
120 #endif
121 fIsBackground = psfcFrom->fIsBackground;
122 // default other sfc
123 psfcFrom->Default();
124 }
125
Clear()126 void C4Surface::Clear()
127 {
128 // Undo all locks
129 while (Locked) Unlock();
130 // release surface
131 #ifndef USE_CONSOLE
132 if (pCtx)
133 {
134 delete pCtx;
135 pCtx = nullptr;
136 }
137 #endif
138 texture.reset();
139 #ifdef _DEBUG
140 dbg_idx = 0;
141 #endif
142 }
143
IsRenderTarget()144 bool C4Surface::IsRenderTarget()
145 {
146 // primary is always OK...
147 return fPrimary;
148 }
149
NoClip()150 void C4Surface::NoClip()
151 {
152 ClipX=0; ClipY=0; ClipX2=Wdt-1; ClipY2=Hgt-1;
153 }
154
Clip(int iX,int iY,int iX2,int iY2)155 void C4Surface::Clip(int iX, int iY, int iX2, int iY2)
156 {
157 ClipX=Clamp(iX,0,Wdt-1); ClipY=Clamp(iY,0,Hgt-1);
158 ClipX2=Clamp(iX2,0,Wdt-1); ClipY2=Clamp(iY2,0,Hgt-1);
159 }
160
Create(int iWdt,int iHgt,int iFlags)161 bool C4Surface::Create(int iWdt, int iHgt, int iFlags)
162 {
163 Clear(); Default();
164 // check size
165 if (!iWdt || !iHgt) return false;
166 Wdt=iWdt; Hgt=iHgt;
167 // create texture: check gfx system
168 if (!pDraw) return false;
169 if (!pDraw->DeviceReady()) return false;
170
171 // store color format that will be used
172 #ifndef USE_CONSOLE
173 Format=pGL->sfcFmt;
174 #endif
175 // create texture
176 iTexSize = std::max(iWdt, iHgt);
177 texture = std::make_unique<C4TexRef>(iWdt, iHgt, iFlags);
178 // update clipping
179 NoClip();
180 // success
181 return true;
182 }
183
Copy(C4Surface & fromSfc)184 bool C4Surface::Copy(C4Surface &fromSfc)
185 {
186 // Clear anything old
187 Clear();
188 // Default to other surface's color depth
189 Default();
190 // Create surface (TODO: copy flags)
191 if (!Create(fromSfc.Wdt, fromSfc.Hgt)) return false;
192 // Blit copy
193 if (!pDraw->BlitSurface(&fromSfc, this, 0, 0, false))
194 { Clear(); return false; }
195 // Success
196 return true;
197 }
198
199 #define RANGE 255
200 #define HLSMAX RANGE
201 #define RGBMAX 255
202
ClrByOwner(DWORD & dwClr)203 bool ClrByOwner(DWORD &dwClr) // new style, based on Microsoft Knowledge Base Article - 29240
204 {
205 int H,L,S;
206 WORD R,G,B;
207 BYTE cMax,cMin;
208 WORD Rdelta,Gdelta,Bdelta;
209 // get RGB
210 R = GetRedValue(dwClr);
211 G = GetGreenValue(dwClr);
212 B = GetBlueValue(dwClr);
213 // calculate lightness
214 cMax = std::max<int>(std::max<int>(R,G),B);
215 cMin = std::min<int>(std::min<int>(R,G),B);
216 L = ( ((cMax+cMin)*HLSMAX) + RGBMAX )/(2*RGBMAX);
217 // achromatic case
218 if (cMax == cMin)
219 {
220 S = 0;
221 H = (HLSMAX*2/3);
222 }
223 // chromatic case
224 else
225 {
226 // saturation
227 if (L <= (HLSMAX/2))
228 S = ( ((cMax-cMin)*HLSMAX) + ((cMax+cMin)/2) ) / (cMax+cMin);
229 else
230 S = ( ((cMax-cMin)*HLSMAX) + ((2*RGBMAX-cMax-cMin)/2) )
231 / (2*RGBMAX-cMax-cMin);
232 // hue
233 Rdelta = ( ((cMax-R)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
234 Gdelta = ( ((cMax-G)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
235 Bdelta = ( ((cMax-B)*(HLSMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin);
236 if (R == cMax)
237 H = Bdelta - Gdelta;
238 else if (G == cMax)
239 H = (HLSMAX/3) + Rdelta - Bdelta;
240 else
241 H = ((2*HLSMAX)/3) + Gdelta - Rdelta;
242 if (H < 0)
243 H += HLSMAX;
244 if (H > HLSMAX)
245 H -= HLSMAX;
246 }
247 // Not blue
248 if (!(Inside(H, 145, 175) && (S > 100))) return false;
249 // It's blue: make it gray
250 BYTE b = GetBlueValue(dwClr);
251 dwClr = RGBA(b, b, b, 0) | (dwClr & 0xff000000);
252 return true;
253 }
254
CreateColorByOwner(C4Surface * pBySurface)255 bool C4Surface::CreateColorByOwner(C4Surface *pBySurface)
256 {
257 // safety
258 if (!pBySurface) return false;
259 if (!pBySurface->texture) return false;
260 // create in same size
261 if (!Create(pBySurface->Wdt, pBySurface->Hgt)) return false;
262 // copy scale
263 Scale = pBySurface->Scale;
264 // set main surface
265 pMainSfc=pBySurface;
266 // lock it
267 if (!pMainSfc->Lock()) return false;
268 if (!Lock()) { pMainSfc->Unlock(); return false; }
269 // set ColorByOwner-pixels
270 for (int iY=0; iY<Hgt; ++iY)
271 for (int iX=0; iX<Wdt; ++iX)
272 {
273 // get pixel
274 DWORD dwPix=pMainSfc->GetPixDw(iX, iY, false);
275 // is it a ClrByOwner-px?
276 if (!ClrByOwner(dwPix)) continue;
277 // set in this surface
278 SetPixDw(iX, iY, dwPix);
279 // clear in the other
280 pMainSfc->SetPixDw(iX, iY, 0x00ffffff);
281 }
282 // unlock
283 Unlock();
284 pMainSfc->Unlock();
285 // success
286 return true;
287 }
288
SetAsClrByOwnerOf(C4Surface * pOfSurface)289 bool C4Surface::SetAsClrByOwnerOf(C4Surface *pOfSurface)
290 {
291 // safety
292 if (!pOfSurface) return false;
293 if (Wdt != pOfSurface->Wdt || Hgt != pOfSurface->Hgt)
294 return false;
295 // set main surface
296 pMainSfc=pOfSurface;
297 // success
298 return true;
299 }
300
UpdateSize(int wdt,int hgt)301 bool C4Surface::UpdateSize(int wdt, int hgt)
302 {
303 assert(fPrimary);
304 if (!fPrimary)
305 return false;
306 this->Wdt = wdt; this->Hgt = hgt;
307 return true;
308 }
309
PageFlip(C4Rect * pSrcRt,C4Rect * pDstRt)310 bool C4Surface::PageFlip(C4Rect *pSrcRt, C4Rect *pDstRt)
311 {
312 assert(fPrimary);
313 if (!fPrimary)
314 return false;
315 // call from gfx thread only!
316 if (!pDraw->pApp || !pDraw->pApp->AssertMainThread()) return false;
317 #ifndef USE_CONSOLE
318 return pCtx->PageFlip();
319 #endif
320 return true;
321 }
322
ReadBMP(CStdStream & hGroup,int iFlags)323 bool C4Surface::ReadBMP(CStdStream &hGroup, int iFlags)
324 {
325 int lcnt;
326 C4BMP256Info BitmapInfo;
327 // read bmpinfo-header
328 if (!hGroup.Read(&BitmapInfo,sizeof(C4BMPInfo))) return false;
329 // is it 8bpp?
330 if (BitmapInfo.Info.biBitCount == 8)
331 {
332 if (!hGroup.Read(((BYTE *) &BitmapInfo)+sizeof(C4BMPInfo),
333 std::min(sizeof(BitmapInfo)-sizeof(C4BMPInfo),sizeof(BitmapInfo)-sizeof(C4BMPInfo)+BitmapInfo.FileBitsOffset())))
334 return false;
335 if (!hGroup.Advance(BitmapInfo.FileBitsOffset())) return false;
336 }
337 else
338 {
339 // read 24bpp
340 if (BitmapInfo.Info.biBitCount != 24) return false;
341 if (!hGroup.Advance(((C4BMPInfo) BitmapInfo).FileBitsOffset())) return false;
342 }
343
344 // Create and lock surface
345 if (!Create(BitmapInfo.Info.biWidth,BitmapInfo.Info.biHeight, iFlags)) return false;
346 if (!Lock()) { Clear(); return false; }
347
348 // create line buffer
349 int iBufSize=DWordAligned(BitmapInfo.Info.biWidth*BitmapInfo.Info.biBitCount/8);
350 BYTE *pBuf = new BYTE[iBufSize];
351 // Read lines
352 for (lcnt=Hgt-1; lcnt>=0; lcnt--)
353 {
354 if (!hGroup.Read(pBuf, iBufSize))
355 { Clear(); delete [] pBuf; return false; }
356 BYTE *pPix=pBuf;
357 for (int x=0; x<BitmapInfo.Info.biWidth; ++x)
358 switch (BitmapInfo.Info.biBitCount)
359 {
360 case 8:
361 SetPixDw(x, lcnt, C4RGB(
362 BitmapInfo.Colors[*pPix].rgbRed,
363 BitmapInfo.Colors[*pPix].rgbGreen,
364 BitmapInfo.Colors[*pPix].rgbBlue));
365 ++pPix;
366 break;
367 case 24:
368 SetPixDw(x, lcnt, C4RGB(pPix[0], pPix[1], pPix[2]));
369 pPix+=3;
370 break;
371 }
372 }
373 // free buffer again
374 delete [] pBuf;
375
376 Unlock();
377
378 return true;
379 }
380
SavePNG(const char * szFilename,bool fSaveAlpha,bool fSaveOverlayOnly,bool use_background_thread)381 bool C4Surface::SavePNG(const char *szFilename, bool fSaveAlpha, bool fSaveOverlayOnly, bool use_background_thread)
382 {
383 // Lock - WARNING - maybe locking primary surface here...
384 if (!Lock()) return false;
385
386 // create png file
387 std::unique_ptr<CPNGFile> png(new CPNGFile());
388 if (!png->Create(Wdt, Hgt, fSaveAlpha)) { Unlock(); return false; }
389
390 // reset overlay if desired
391 C4Surface *pMainSfcBackup = nullptr;
392 if (fSaveOverlayOnly) { pMainSfcBackup=pMainSfc; pMainSfc=nullptr; }
393
394 #ifndef USE_CONSOLE
395 if (fPrimary)
396 {
397 // Take shortcut. FIXME: Check Endian
398 for (int y = 0; y < Hgt; ++y)
399 glReadPixels(0, Hgt - y - 1, Wdt, 1, fSaveAlpha ? GL_BGRA : GL_BGR, GL_UNSIGNED_BYTE, png->GetRow(y));
400 }
401 else
402 #endif
403 {
404 // write pixel values
405 for (int y=0; y<Hgt; ++y)
406 for (int x=0; x<Wdt; ++x)
407 {
408 DWORD dwClr = GetPixDw(x, y, false);
409 png->SetPix(x, y, dwClr);
410 }
411 }
412
413 // reset overlay
414 if (fSaveOverlayOnly) pMainSfc=pMainSfcBackup;
415
416 // Unlock
417 Unlock();
418
419 // save png - either directly or delayed in a background thread if desired
420 if (use_background_thread)
421 {
422 CPNGFile::ScheduleSaving(png.release(), szFilename);
423 }
424 else
425 {
426 if (!png->Save(szFilename)) return false;
427 }
428
429 // Success
430 return true;
431 }
432
433
AttachPalette()434 bool C4Surface::AttachPalette()
435 {
436 return true;
437 }
438
ColorDistance(BYTE * bpRGB1,BYTE * bpRGB2)439 double ColorDistance(BYTE *bpRGB1, BYTE *bpRGB2)
440 {
441 return (double) (Abs(bpRGB1[0]-bpRGB2[0]) + Abs(bpRGB1[1]-bpRGB2[1]) + Abs(bpRGB1[2]-bpRGB2[2])) / 6.0;
442 }
443
GetSurfaceSize(int & irX,int & irY)444 bool C4Surface::GetSurfaceSize(int &irX, int &irY)
445 {
446 // simply assign stored values
447 irX=Wdt;
448 irY=Hgt;
449 // success
450 return true;
451 }
452
Lock()453 bool C4Surface::Lock()
454 {
455 // lock main sfc
456 if (pMainSfc) if (!pMainSfc->Lock()) return false;
457 // lock texture
458 if (!Locked && !fPrimary && !texture)
459 return false;
460 // count lock
461 Locked++; return true;
462 }
463
Unlock()464 bool C4Surface::Unlock()
465 {
466 // unlock main sfc
467 if (pMainSfc) pMainSfc->Unlock();
468 // locked?
469 if (!Locked) return false;
470 // decrease lock counter; check if zeroed and unlock then
471 Locked--;
472 if (!Locked)
473 {
474 if (fPrimary)
475 {
476 // emulated primary locks in OpenGL
477 delete[] PrimarySurfaceLockBits;
478 PrimarySurfaceLockBits = nullptr;
479 return true;
480 }
481 else
482 {
483 // non-primary unlock: unlock all texture surfaces (if locked)
484 if (texture)
485 texture->Unlock();
486 }
487 }
488 return true;
489 }
490
GetPixDw(int iX,int iY,bool fApplyModulation)491 DWORD C4Surface::GetPixDw(int iX, int iY, bool fApplyModulation)
492 {
493 BYTE *pBuf = nullptr; int iPitch = 0; // TODO: are those initialised to something sensible?
494 // backup pos
495 int iX2=iX; int iY2=iY;
496 // primary?
497 if (fPrimary)
498 {
499 #ifndef USE_CONSOLE
500 if (!PrimarySurfaceLockBits)
501 {
502 PrimarySurfaceLockBits = new unsigned char[Wdt*Hgt*3];
503 glPixelStorei(GL_PACK_ALIGNMENT, 1);
504 glReadPixels( 0, 0, Wdt, Hgt, GL_BGR, GL_UNSIGNED_BYTE, PrimarySurfaceLockBits);
505 PrimarySurfaceLockPitch = Wdt*3;
506 }
507 return * (DWORD *) (PrimarySurfaceLockBits+(Hgt-iY-1)*PrimarySurfaceLockPitch+iX*3);
508 #endif
509 }
510 else
511 {
512 // get+lock affected texture
513 if (!texture) return 0;
514 texture->Lock();
515 pBuf=(BYTE *) texture->texLock.pBits.get();
516 iPitch=texture->texLock.Pitch;
517 }
518 // get pix of surface
519 DWORD dwPix;
520 DWORD *pPix=(DWORD *) (pBuf+iY*iPitch+iX*4);
521 dwPix = *pPix;
522 // this is a ColorByOwner-surface?
523 if (pMainSfc)
524 {
525 BYTE byAlpha=BYTE(dwPix>>24);
526 // pix is fully transparent?
527 if (byAlpha==0x00)
528 // then get the main surfaces's pixel
529 dwPix = pMainSfc->GetPixDw(iX2, iY2, fApplyModulation);
530 else
531 {
532 // otherwise, it's a ColorByOwner-pixel: adjust the color
533 if (fApplyModulation)
534 {
535 if (pDraw->dwBlitMode & C4GFXBLIT_CLRSFC_MOD2)
536 ModulateClrMOD2(dwPix, ClrByOwnerClr);
537 else
538 ModulateClr(dwPix, ClrByOwnerClr);
539 if (pDraw->BlitModulated && !(pDraw->dwBlitMode & C4GFXBLIT_CLRSFC_OWNCLR))
540 ModulateClr(dwPix, pDraw->BlitModulateClr);
541 }
542 else
543 ModulateClr(dwPix, ClrByOwnerClr);
544 // does it contain transparency? then blit on main sfc
545 if (byAlpha)
546 {
547 DWORD dwMainPix = pMainSfc->GetPixDw(iX2, iY2, fApplyModulation);
548 BltAlpha(dwMainPix, dwPix); dwPix=dwMainPix;
549 }
550 }
551 }
552 else
553 {
554 // single main surface
555 // apply color modulation if desired
556 if (fApplyModulation && pDraw->BlitModulated)
557 {
558 if (pDraw->dwBlitMode & C4GFXBLIT_MOD2)
559 ModulateClrMOD2(dwPix, pDraw->BlitModulateClr);
560 else
561 ModulateClr(dwPix, pDraw->BlitModulateClr);
562 }
563 }
564 // return pixel value
565 return dwPix;
566 }
567
IsPixTransparent(int iX,int iY)568 bool C4Surface::IsPixTransparent(int iX, int iY)
569 {
570 // get pixel value
571 DWORD dwPix=GetPixDw(iX, iY, false);
572 // get alpha value
573 return (dwPix>>24) < 128;
574 }
575
SetPixDw(int iX,int iY,DWORD dwClr)576 bool C4Surface::SetPixDw(int iX, int iY, DWORD dwClr)
577 {
578 // clip
579 if ((iX<ClipX) || (iX>ClipX2) || (iY<ClipY) || (iY>ClipY2)) return true;
580 // get+lock affected texture
581 if (!texture) return false;
582 texture->Lock();
583 // if color is fully transparent, ensure it's black
584 if (dwClr>>24 == 0x00) dwClr=0x00000000;
585 // ...and set in actual surface
586 texture->SetPix(iX, iY, dwClr);
587 // success
588 return true;
589 }
590
BltPix(int iX,int iY,C4Surface * sfcSource,int iSrcX,int iSrcY,bool fTransparency)591 bool C4Surface::BltPix(int iX, int iY, C4Surface *sfcSource, int iSrcX, int iSrcY, bool fTransparency)
592 {
593 // 32bit-blit. lock target
594 if (!texture) return false;
595 texture->Lock();
596 DWORD *pPix32 = (DWORD *)(((BYTE *)texture->texLock.pBits.get()) + iY*texture->texLock.Pitch + iX * 4);
597 // get source pix as dword
598 DWORD srcPix=sfcSource->GetPixDw(iSrcX, iSrcY, true);
599 // merge
600 if (!fTransparency)
601 {
602 // set it
603 *pPix32=srcPix;
604 }
605 else
606 {
607 if (pDraw->dwBlitMode & C4GFXBLIT_ADDITIVE)
608 BltAlphaAdd(*pPix32, srcPix);
609 else
610 BltAlpha(*pPix32, srcPix);
611 }
612 // done
613 return true;
614 }
615
ClearBoxDw(int iX,int iY,int iWdt,int iHgt)616 void C4Surface::ClearBoxDw(int iX, int iY, int iWdt, int iHgt)
617 {
618 // lock
619 if (!Locked) return;
620 // clip to target size
621 if (iX<0) { iWdt+=iX; iX=0; }
622 if (iY<0) { iHgt+=iY; iY=0; }
623 int iOver;
624 iOver=Wdt-(iX+iWdt); if (iOver<0) iWdt+=iOver;
625 iOver=Hgt-(iY+iHgt); if (iOver<0) iHgt+=iOver;
626 C4Rect rtClear{ iX, iY, iWdt, iHgt };
627 if (pMainSfc && pMainSfc->texture)
628 {
629 // assuming this is only valid as long as there's no texture management,
630 // organizing partially used textures together!
631 pMainSfc->texture->ClearRect(rtClear);
632 }
633 // clear this texture
634 texture->ClearRect(rtClear);
635 }
636
C4TexRef(int iSizeX,int iSizeY,int iFlags)637 C4TexRef::C4TexRef(int iSizeX, int iSizeY, int iFlags)
638 {
639 // zero fields
640 #ifndef USE_CONSOLE
641 texName = 0;
642 #endif
643 texLock.pBits.reset(); fIntLock=false;
644 // store size
645 this->iSizeX=iSizeX;
646 this->iSizeY=iSizeY;
647 this->iFlags=iFlags;
648 // add to texture manager
649 if (!pTexMgr) pTexMgr = new C4TexMgr();
650 pTexMgr->RegTex(this);
651 // create texture: check ddraw
652 if (!pDraw) return;
653 if (!pDraw->DeviceReady()) return;
654 // create it!
655 // Reserve video memory
656 CreateTexture();
657
658 if ((iFlags & C4SF_Unlocked) == 0 && pDraw)
659 {
660 texLock.pBits = std::make_unique<unsigned char[]>(iSizeX * iSizeY * C4Draw::COLOR_DEPTH_BYTES);
661 texLock.Pitch = iSizeX * C4Draw::COLOR_DEPTH_BYTES;
662 memset(texLock.pBits.get(), 0x00, texLock.Pitch*iSizeY);
663 // Always locked
664 LockSize.x = LockSize.y = 0;
665 LockSize.Wdt = iSizeX; LockSize.Hgt = iSizeY;
666 }
667 }
668
~C4TexRef()669 C4TexRef::~C4TexRef()
670 {
671 fIntLock=false;
672 // free texture
673 #ifndef USE_CONSOLE
674 if (pGL && pGL->pCurrCtx) glDeleteTextures(1, &texName);
675 #endif
676 if (pDraw) texLock.pBits = nullptr;
677 // remove from texture manager
678 pTexMgr->UnregTex(this);
679 }
680
CreateTexture()681 void C4TexRef::CreateTexture()
682 {
683 #ifndef USE_CONSOLE
684 assert(texName == 0);
685
686 const bool fTileable = (iFlags & C4SF_Tileable) != 0;
687 const bool fMipMap = (iFlags & C4SF_MipMap) != 0;
688
689 glGenTextures(1, &texName);
690 glBindTexture(GL_TEXTURE_2D, texName);
691 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, fTileable ? GL_REPEAT : GL_CLAMP_TO_EDGE);
692 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, fTileable ? GL_REPEAT : GL_CLAMP_TO_EDGE);
693 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
694 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, fMipMap ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR);
695 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iSizeX, iSizeY, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
696 if (fMipMap) glGenerateMipmap(GL_TEXTURE_2D);
697 #endif
698 }
699
LockForUpdate(C4Rect & rtUpdate)700 bool C4TexRef::LockForUpdate(C4Rect & rtUpdate)
701 {
702 // already locked?
703 if (texLock.pBits)
704 {
705 // fully locked
706 if (LockSize.x == 0 && LockSize.Wdt == iSizeX && LockSize.y == 0 && LockSize.Hgt == iSizeY)
707 {
708 return true;
709 }
710 else
711 {
712 // Commit previous changes to the texture
713 Unlock();
714 }
715 }
716 // lock
717 #ifndef USE_CONSOLE
718 // prepare texture data
719 texLock.pBits = std::make_unique<unsigned char[]>(rtUpdate.Wdt * rtUpdate.Hgt * C4Draw::COLOR_DEPTH_BYTES);
720 texLock.Pitch = rtUpdate.Wdt * C4Draw::COLOR_DEPTH_BYTES;
721 LockSize = rtUpdate;
722 return true;
723 #endif
724 // failure
725 return false;
726 }
727
Lock()728 bool C4TexRef::Lock()
729 {
730 // already locked?
731 if (texLock.pBits) return true;
732 LockSize.Wdt = iSizeX; LockSize.Hgt = iSizeY;
733 LockSize.x = LockSize.y = 0;
734 // lock
735 #ifndef USE_CONSOLE
736 if (texName)
737 {
738 if (!pGL->pCurrCtx) return false;
739 // get texture
740 texLock.pBits = std::make_unique<unsigned char[]>(iSizeX * iSizeY * C4Draw::COLOR_DEPTH_BYTES);
741 texLock.Pitch = iSizeX * C4Draw::COLOR_DEPTH_BYTES;
742 glBindTexture(GL_TEXTURE_2D, texName);
743 glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, texLock.pBits.get());
744 return true;
745 }
746 #endif
747 {
748 // nothing to do
749 }
750 // failure
751 return false;
752 }
753
Unlock()754 void C4TexRef::Unlock()
755 {
756 // locked?
757 if (!texLock.pBits || fIntLock) return;
758 #ifndef USE_CONSOLE
759 if (!pGL->pCurrCtx)
760 {
761 // BREAKPOINT_HERE;
762 assert(pGL->pMainCtx);
763 pGL->pMainCtx->Select();
764 }
765
766 const bool fTileable = (iFlags & C4SF_Tileable) != 0;
767 const bool fMipMap = (iFlags & C4SF_MipMap) != 0;
768
769 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
770
771 // reuse the existing texture
772 glBindTexture(GL_TEXTURE_2D, texName);
773 glTexSubImage2D(GL_TEXTURE_2D, 0,
774 LockSize.x, LockSize.y, LockSize.Wdt, LockSize.Hgt,
775 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, texLock.pBits.get());
776
777 texLock.pBits.reset();
778 if (fMipMap) glGenerateMipmap(GL_TEXTURE_2D);
779 #endif
780 }
781
ClearRect(C4Rect & rtClear)782 bool C4TexRef::ClearRect(C4Rect &rtClear)
783 {
784 // ensure locked
785 if (!LockForUpdate(rtClear)) return false;
786 // clear pixels
787 int y;
788 for (y = rtClear.y; y < rtClear.y + rtClear.Hgt; ++y)
789 {
790 for (int x = rtClear.x; x < rtClear.x + rtClear.Wdt; ++x)
791 SetPix(x, y, 0x00000000);
792 }
793 // success
794 return true;
795 }
796
FillBlack()797 bool C4TexRef::FillBlack()
798 {
799 // ensure locked
800 if (!Lock()) return false;
801 // clear pixels
802 int y;
803 for (y=0; y<iSizeY; ++y)
804 {
805 for (int x = 0; x < iSizeX; ++x)
806 SetPix(x, y, 0xff000000);
807 }
808 // success
809 return true;
810 }
811
812 // texture manager
813
C4TexMgr()814 C4TexMgr::C4TexMgr()
815 {
816 // clear textures
817 Textures.clear();
818 }
819
~C4TexMgr()820 C4TexMgr::~C4TexMgr()
821 {
822 // unlock all textures
823 IntUnlock();
824 }
825
RegTex(C4TexRef * pTex)826 void C4TexMgr::RegTex(C4TexRef *pTex)
827 {
828 // add texture to list
829 Textures.push_front(pTex);
830 }
831
UnregTex(C4TexRef * pTex)832 void C4TexMgr::UnregTex(C4TexRef *pTex)
833 {
834 // remove texture from list
835 Textures.remove(pTex);
836 // if list is empty, remove self
837 if (Textures.empty()) { delete this; pTexMgr=nullptr; }
838 }
839
IntLock()840 void C4TexMgr::IntLock()
841 {
842 // lock all textures
843 int j=Textures.size();
844 for (std::list<C4TexRef *>::iterator i=Textures.begin(); j--; ++i)
845 {
846 C4TexRef *pRef = *i;
847 if (pRef->Lock() && pRef->texLock.pBits)
848 {
849 pRef->fIntLock = true;
850 #ifndef USE_CONSOLE
851 // Release the underlying texture with GL and recreate
852 // it on unlock, so that the texture survives
853 // context recreation.
854 if(pGL)
855 {
856 glDeleteTextures(1, &pRef->texName);
857 pRef->texName = 0;
858 }
859 #endif
860 }
861 }
862 }
863
IntUnlock()864 void C4TexMgr::IntUnlock()
865 {
866 // unlock all internally locked textures
867 int j=Textures.size();
868 for (std::list<C4TexRef *>::iterator i=Textures.begin(); j--; ++i)
869 {
870 C4TexRef *pRef = *i;
871 if (pRef->fIntLock)
872 {
873 pRef->fIntLock = false;
874 pRef->CreateTexture();
875 pRef->Unlock();
876 }
877 }
878 }
879
880 C4TexMgr *pTexMgr;
881