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
18 /* NewGfx interfaces */
19 #include "C4Include.h"
20 #include "C4ForbidLibraryCompilation.h"
21 #include "graphics/C4Draw.h"
22
23 #include "graphics/C4DrawGL.h"
24 #include "graphics/C4DrawT.h"
25 #include "graphics/C4FontLoader.h"
26 #include "graphics/CSurface8.h"
27 #include "lib/C4Markup.h"
28 #include "lib/C4Rect.h"
29 #include "lib/StdColors.h"
30 #include "lib/StdMesh.h"
31 #include "platform/C4App.h"
32 #include "platform/C4Window.h"
33
34 // Instruct Optimus laptops to use nVidia GPU instead of integrated GPU
35 #if defined(_WIN32) && !defined(USE_CONSOLE)
36 extern "C" {
37 __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
38 }
39 #endif
40
41 // Global access pointer
42 C4Draw *pDraw=nullptr;
43
SetRotate(float iAngle,float fOffX,float fOffY)44 void C4BltTransform::SetRotate(float iAngle, float fOffX, float fOffY) // set by angle and rotation offset
45 {
46 // iAngle is in degrees (cycling from 0 to 360)
47 // determine sine and cos of reversed angle in radians
48 // fAngle = -iAngle * pi/180 = iAngle * -pi/180
49 float fAngle = iAngle * -0.0174532925f;
50 float fsin = sinf(fAngle); float fcos = cosf(fAngle);
51 // set matrix values
52 mat[0] = +fcos; mat[1] = +fsin; mat[2] = (1-fcos)*fOffX - fsin*fOffY;
53 mat[3] = -fsin; mat[4] = +fcos; mat[5] = (1-fcos)*fOffY + fsin*fOffX;
54 mat[6] = 0; mat[7] = 0; mat[8] = 1;
55 /* calculation of rotation matrix:
56 x2 = fcos*(x1-fOffX) + fsin*(y1-fOffY) + fOffX
57 = fcos*x1 - fcos*fOffX + fsin*y1 - fsin*fOffY + fOffX
58 = x1*fcos + y1*fsin + (1-fcos)*fOffX - fsin*fOffY
59
60 y2 = -fsin*(x1-fOffX) + fcos*(y1-fOffY) + fOffY
61 = x1*-fsin + fsin*fOffX + y1*fcos - fcos*fOffY + fOffY
62 = x1*-fsin + y1*fcos + fsin*fOffX + (1-fcos)*fOffY */
63 }
64
SetAsInv(C4BltTransform & r)65 bool C4BltTransform::SetAsInv(C4BltTransform &r)
66 {
67 // calc inverse of matrix
68 float det = r.mat[0]*r.mat[4]*r.mat[8] + r.mat[1]*r.mat[5]*r.mat[6]
69 + r.mat[2]*r.mat[3]*r.mat[7] - r.mat[2]*r.mat[4]*r.mat[6]
70 - r.mat[0]*r.mat[5]*r.mat[7] - r.mat[1]*r.mat[3]*r.mat[8];
71 if (!det) { Set(1,0,0,0,1,0,0,0,1); return false; }
72 mat[0] = (r.mat[4] * r.mat[8] - r.mat[5] * r.mat[7]) / det;
73 mat[1] = (r.mat[2] * r.mat[7] - r.mat[1] * r.mat[8]) / det;
74 mat[2] = (r.mat[1] * r.mat[5] - r.mat[2] * r.mat[4]) / det;
75 mat[3] = (r.mat[5] * r.mat[6] - r.mat[3] * r.mat[8]) / det;
76 mat[4] = (r.mat[0] * r.mat[8] - r.mat[2] * r.mat[6]) / det;
77 mat[5] = (r.mat[2] * r.mat[3] - r.mat[0] * r.mat[5]) / det;
78 mat[6] = (r.mat[3] * r.mat[7] - r.mat[4] * r.mat[6]) / det;
79 mat[7] = (r.mat[1] * r.mat[6] - r.mat[0] * r.mat[7]) / det;
80 mat[8] = (r.mat[0] * r.mat[4] - r.mat[1] * r.mat[3]) / det;
81 return true;
82 }
83
TransformPoint(float & rX,float & rY) const84 void C4BltTransform::TransformPoint(float &rX, float &rY) const
85 {
86 // apply matrix
87 float fW = mat[6] * rX + mat[7] * rY + mat[8];
88 // store in temp, so original rX is used for calculation of rY
89 float fX = (mat[0] * rX + mat[1] * rY + mat[2]) / fW;
90 rY = (mat[3] * rX + mat[4] * rY + mat[5]) / fW;
91 rX = fX; // apply temp
92 }
93
operator =(const C4Pattern & nPattern)94 C4Pattern& C4Pattern::operator=(const C4Pattern& nPattern)
95 {
96 sfcPattern32 = nPattern.sfcPattern32;
97 if (sfcPattern32) sfcPattern32->Lock();
98 delete [] CachedPattern;
99 if (nPattern.CachedPattern)
100 {
101 CachedPattern = new uint32_t[sfcPattern32->Wdt * sfcPattern32->Hgt];
102 memcpy(CachedPattern, nPattern.CachedPattern, sfcPattern32->Wdt * sfcPattern32->Hgt * 4);
103 }
104 else
105 {
106 CachedPattern = nullptr;
107 }
108 Wdt = nPattern.Wdt;
109 Hgt = nPattern.Hgt;
110 Zoom = nPattern.Zoom;
111 return *this;
112 }
113
Set(C4Surface * sfcSource,int iZoom)114 bool C4Pattern::Set(C4Surface * sfcSource, int iZoom)
115 {
116 // Safety
117 if (!sfcSource) return false;
118 // Clear existing pattern
119 Clear();
120 // new style: simply store pattern for modulation or shifting, which will be decided upon use
121 sfcPattern32=sfcSource;
122 sfcPattern32->Lock();
123 Wdt = sfcPattern32->Wdt;
124 Hgt = sfcPattern32->Hgt;
125 // set zoom
126 Zoom=iZoom;
127 // set flags
128 CachedPattern = new uint32_t[Wdt * Hgt];
129 if (!CachedPattern) return false;
130 for (int y = 0; y < Hgt; ++y)
131 for (int x = 0; x < Wdt; ++x)
132 {
133 CachedPattern[y * Wdt + x] = sfcPattern32->GetPixDw(x, y, false);
134 }
135 return true;
136 }
137
C4Pattern()138 C4Pattern::C4Pattern()
139 {
140 // disable
141 sfcPattern32=nullptr;
142 CachedPattern = nullptr;
143 Zoom=0;
144 }
145
Clear()146 void C4Pattern::Clear()
147 {
148 // pattern assigned
149 if (sfcPattern32)
150 {
151 // unlock it
152 sfcPattern32->Unlock();
153 // clear field
154 sfcPattern32=nullptr;
155 }
156 delete[] CachedPattern; CachedPattern = nullptr;
157 }
158
PatternClr(unsigned int iX,unsigned int iY) const159 DWORD C4Pattern::PatternClr(unsigned int iX, unsigned int iY) const
160 {
161 if (!CachedPattern) return 0;
162 // wrap position
163 iX %= Wdt; iY %= Hgt;
164 return CachedPattern[iY * Wdt + iX];
165 }
166
167 //--------------------------------------------------------------------
168
Default()169 void C4Draw::Default()
170 {
171 RenderTarget=nullptr;
172 ClipAll=false;
173 Active=false;
174 BlitModulated=false;
175 dwBlitMode = 0;
176 ResetGamma();
177 pFoW = nullptr;
178 ZoomX = 0; ZoomY = 0; Zoom = 1;
179 MeshTransform = nullptr;
180 fUsePerspective = false;
181 scriptUniform.Clear();
182 }
183
Clear()184 void C4Draw::Clear()
185 {
186 ResetGamma();
187 Active=BlitModulated=false;
188 dwBlitMode = 0;
189 }
190
GetSurfaceSize(C4Surface * sfcSurface,int & iWdt,int & iHgt)191 bool C4Draw::GetSurfaceSize(C4Surface * sfcSurface, int &iWdt, int &iHgt)
192 {
193 return sfcSurface->GetSurfaceSize(iWdt, iHgt);
194 }
195
SubPrimaryClipper(int iX1,int iY1,int iX2,int iY2)196 bool C4Draw::SubPrimaryClipper(int iX1, int iY1, int iX2, int iY2)
197 {
198 // Set sub primary clipper
199 SetPrimaryClipper(std::max(iX1,iClipX1),std::max(iY1,iClipY1),std::min(iX2,iClipX2),std::min(iY2,iClipY2));
200 return true;
201 }
202
StorePrimaryClipper()203 bool C4Draw::StorePrimaryClipper()
204 {
205 // Store current primary clipper
206 fStClipX1=fClipX1; fStClipY1=fClipY1; fStClipX2=fClipX2; fStClipY2=fClipY2;
207 return true;
208 }
209
RestorePrimaryClipper()210 bool C4Draw::RestorePrimaryClipper()
211 {
212 // Restore primary clipper
213 SetPrimaryClipper(fStClipX1, fStClipY1, fStClipX2, fStClipY2);
214 return true;
215 }
216
SetPrimaryClipper(int iX1,int iY1,int iX2,int iY2)217 bool C4Draw::SetPrimaryClipper(int iX1, int iY1, int iX2, int iY2)
218 {
219 // set clipper
220 fClipX1=iX1; fClipY1=iY1; fClipX2=iX2; fClipY2=iY2;
221 iClipX1=iX1; iClipY1=iY1; iClipX2=iX2; iClipY2=iY2;
222 UpdateClipper();
223 // Done
224 return true;
225 }
226
ApplyPrimaryClipper(C4Surface * sfcSurface)227 bool C4Draw::ApplyPrimaryClipper(C4Surface * sfcSurface)
228 {
229 return true;
230 }
231
DetachPrimaryClipper(C4Surface * sfcSurface)232 bool C4Draw::DetachPrimaryClipper(C4Surface * sfcSurface)
233 {
234 return true;
235 }
236
NoPrimaryClipper()237 bool C4Draw::NoPrimaryClipper()
238 {
239 // apply maximum clipper
240 SetPrimaryClipper(0,0,439832,439832);
241 // Done
242 return true;
243 }
244
BlitLandscape(C4Surface * sfcSource,float fx,float fy,C4Surface * sfcTarget,float tx,float ty,float wdt,float hgt)245 void C4Draw::BlitLandscape(C4Surface * sfcSource, float fx, float fy,
246 C4Surface * sfcTarget, float tx, float ty, float wdt, float hgt)
247 {
248 Blit(sfcSource, fx, fy, wdt, hgt, sfcTarget, tx, ty, wdt, hgt, false);
249 }
250
Blit8Fast(CSurface8 * sfcSource,int fx,int fy,C4Surface * sfcTarget,int tx,int ty,int wdt,int hgt)251 void C4Draw::Blit8Fast(CSurface8 * sfcSource, int fx, int fy,
252 C4Surface * sfcTarget, int tx, int ty, int wdt, int hgt)
253 {
254 // blit 8bit-sfc
255 // lock surfaces
256 assert(sfcTarget->fPrimary);
257 bool fRender = sfcTarget->IsRenderTarget();
258 if (!fRender) if (!sfcTarget->Lock())
259 { return; }
260
261 float tfx = tx, tfy = ty, twdt = wdt, thgt = hgt;
262 if (Zoom != 1.0)
263 {
264 ApplyZoom(tfx, tfy);
265 twdt *= Zoom;
266 thgt *= Zoom;
267 }
268
269 // blit 8 bit pix in batches of 1024 pixels
270 static const int BUF_SIZE = 1024;
271 C4BltVertex* vertices = new C4BltVertex[BUF_SIZE];
272 int bufcnt = 0;
273
274 for (int ycnt=0; ycnt<thgt; ++ycnt)
275 {
276 for (int xcnt=0; xcnt<twdt; ++xcnt)
277 {
278 BYTE byPix = sfcSource->GetPix(fx+wdt*xcnt/twdt, fy+hgt*ycnt/thgt);
279 DWORD dwClr = byPix ? sfcSource->pPal->GetClr(byPix) : 0x00000000;
280
281 vertices[bufcnt].ftx = (float)(tx + xcnt / Zoom);
282 vertices[bufcnt].fty = (float)(ty + ycnt / Zoom);
283 DwTo4UB(dwClr, vertices[bufcnt].color);
284 ++bufcnt;
285
286 if(bufcnt == BUF_SIZE)
287 {
288 PerformMultiPix(sfcTarget, vertices, BUF_SIZE, nullptr);
289 bufcnt = 0;
290 }
291 }
292
293 }
294 if(bufcnt > 0)
295 PerformMultiPix(sfcTarget, vertices, bufcnt, nullptr);
296 delete[] vertices;
297 // unlock
298 if (!fRender) sfcTarget->Unlock();
299 }
300
Blit(C4Surface * sfcSource,float fx,float fy,float fwdt,float fhgt,C4Surface * sfcTarget,float tx,float ty,float twdt,float thgt,bool fSrcColKey,const C4BltTransform * pTransform)301 bool C4Draw::Blit(C4Surface * sfcSource, float fx, float fy, float fwdt, float fhgt,
302 C4Surface * sfcTarget, float tx, float ty, float twdt, float thgt,
303 bool fSrcColKey, const C4BltTransform *pTransform)
304 {
305 return BlitUnscaled(sfcSource, fx * sfcSource->Scale, fy * sfcSource->Scale, fwdt * sfcSource->Scale, fhgt * sfcSource->Scale,
306 sfcTarget, tx, ty, twdt, thgt, fSrcColKey, pTransform);
307 }
308
BlitUnscaled(C4Surface * sfcSource,float fx,float fy,float fwdt,float fhgt,C4Surface * sfcTarget,float tx,float ty,float twdt,float thgt,bool fSrcColKey,const C4BltTransform * pTransform)309 bool C4Draw::BlitUnscaled(C4Surface * sfcSource, float fx, float fy, float fwdt, float fhgt,
310 C4Surface * sfcTarget, float tx, float ty, float twdt, float thgt,
311 bool fSrcColKey, const C4BltTransform *pTransform)
312 {
313 // safety
314 if (!sfcSource || !sfcTarget || !twdt || !thgt || !fwdt || !fhgt) return false;
315 // emulated blit?
316 if (!sfcTarget->IsRenderTarget())
317 {
318 C4BltTransform t;
319 if(pTransform && Zoom != 1.0)
320 {
321 t.Set(pTransform->mat[0]*Zoom, pTransform->mat[1]*Zoom, pTransform->mat[2]*Zoom + ZoomX*(1-Zoom),
322 pTransform->mat[3]*Zoom, pTransform->mat[4]*Zoom, pTransform->mat[5]*Zoom + ZoomY*(1-Zoom),
323 pTransform->mat[6], pTransform->mat[7], pTransform->mat[8]);
324 pTransform = &t;
325 }
326 else if(Zoom != 1.0)
327 {
328 ApplyZoom(tx, ty);
329 twdt *= Zoom;
330 thgt *= Zoom;
331 }
332
333 return Blit8(sfcSource, int(fx), int(fy), int(fwdt), int(fhgt), sfcTarget, int(tx), int(ty), int(twdt), int(thgt), fSrcColKey, pTransform);
334 }
335
336 // calc stretch
337 float scaleX = twdt/fwdt;
338 float scaleY = thgt/fhgt;
339 // bound
340 if (ClipAll) return true;
341 // inside screen?
342 if (twdt<=0 || thgt<=0) return false;
343 // prepare rendering to surface
344 if (!PrepareRendering(sfcTarget)) return false;
345 // texture present?
346 if (!sfcSource->texture)
347 {
348 // primary surface?
349 if (sfcSource->fPrimary)
350 {
351 // blit emulated
352 return Blit8(sfcSource, int(fx), int(fy), int(fwdt), int(fhgt), sfcTarget, int(tx), int(ty), int(twdt), int(thgt), fSrcColKey);
353 }
354 return false;
355 }
356 // blit with basesfc?
357 bool fBaseSfc=false;
358 if (sfcSource->pMainSfc) if (sfcSource->pMainSfc->texture) fBaseSfc = true;
359 // get involved texture offsets
360 int iTexSizeX=sfcSource->iTexSize;
361 int iTexSizeY=sfcSource->iTexSize;
362
363 C4TexRef *pTex = sfcSource->texture.get();
364 // set up blit data
365 C4BltVertex vertices[6];
366 vertices[0].ftx = tx; vertices[0].fty = ty;
367 vertices[1].ftx = tx + twdt; vertices[1].fty = ty;
368 vertices[2].ftx = tx + twdt; vertices[2].fty = ty + thgt;
369 vertices[3].ftx = tx; vertices[3].fty = ty + thgt;
370 vertices[0].tx = fx / pTex->iSizeX; vertices[0].ty = fy / pTex->iSizeY;
371 vertices[1].tx = (fx + fwdt) / pTex->iSizeX; vertices[1].ty = fy / pTex->iSizeY;
372 vertices[2].tx = (fx + fwdt) / pTex->iSizeX; vertices[2].ty = (fy + fhgt) / pTex->iSizeY;
373 vertices[3].tx = fx / pTex->iSizeX; vertices[3].ty = (fy + fhgt) / pTex->iSizeY;
374 DwTo4UB(0xffffffff, vertices[0].color);
375 DwTo4UB(0xffffffff, vertices[1].color);
376 DwTo4UB(0xffffffff, vertices[2].color);
377 DwTo4UB(0xffffffff, vertices[3].color);
378
379 // duplicate vertices
380 vertices[4] = vertices[0]; vertices[5] = vertices[2];
381
382 C4TexRef * pBaseTex = pTex;
383 // is there a base-surface to be blitted first?
384 if (fBaseSfc)
385 {
386 // then get this surface as same offset as from other surface
387 // assuming this is only valid as long as there's no texture management,
388 // organizing partially used textures together!
389 pBaseTex = sfcSource->pMainSfc->texture.get();
390 }
391
392 C4TexRef* pNormalTex = nullptr;
393 if (sfcSource->pNormalSfc)
394 pNormalTex = sfcSource->pNormalSfc->texture.get();
395
396 // ClrByOwner is always fully opaque
397 const DWORD dwOverlayClrMod = 0xff000000 | sfcSource->ClrByOwnerClr;
398 PerformMultiTris(sfcTarget, vertices, 6, pTransform, pBaseTex, fBaseSfc ? pTex : nullptr, pNormalTex, dwOverlayClrMod, nullptr);
399 // success
400 return true;
401 }
402
RenderMesh(StdMeshInstance & instance,C4Surface * sfcTarget,float tx,float ty,float twdt,float thgt,DWORD dwPlayerColor,C4BltTransform * pTransform)403 bool C4Draw::RenderMesh(StdMeshInstance &instance, C4Surface * sfcTarget, float tx, float ty, float twdt, float thgt, DWORD dwPlayerColor, C4BltTransform* pTransform)
404 {
405 // TODO: Emulate rendering
406 if (!sfcTarget->IsRenderTarget()) return false;
407
408 // TODO: Clip
409
410 // prepare rendering to surface
411 if (!PrepareRendering(sfcTarget)) return false;
412 // Update bone matrices and vertex data (note this also updates attach transforms and child transforms)
413 instance.UpdateBoneTransforms();
414 // Order faces according to MeshTransformation (note pTransform does not affect Z coordinate, so does not need to be taken into account for correct ordering)
415 StdMeshMatrix mat = StdMeshMatrix::Identity();
416 if(MeshTransform) mat = *MeshTransform * mat;
417 instance.ReorderFaces(&mat);
418 // Render mesh
419 PerformMesh(instance, tx, ty, twdt, thgt, dwPlayerColor, pTransform);
420 // success
421 return true;
422 }
423
Blit8(C4Surface * sfcSource,int fx,int fy,int fwdt,int fhgt,C4Surface * sfcTarget,int tx,int ty,int twdt,int thgt,bool fSrcColKey,const C4BltTransform * pTransform)424 bool C4Draw::Blit8(C4Surface * sfcSource, int fx, int fy, int fwdt, int fhgt,
425 C4Surface * sfcTarget, int tx, int ty, int twdt, int thgt,
426 bool fSrcColKey, const C4BltTransform *pTransform)
427 {
428 if (!pTransform) return BlitSimple(sfcSource, fx, fy, fwdt, fhgt, sfcTarget, tx, ty, twdt, thgt, fSrcColKey!=false);
429 // safety
430 if (!fwdt || !fhgt) return true;
431 // Lock the surfaces
432 if (!sfcSource->Lock())
433 return false;
434 if (!sfcTarget->Lock())
435 { sfcSource->Unlock(); return false; }
436 // transformed, emulated blit
437 // Calculate transform target rect
438 C4BltTransform Transform;
439 Transform.SetMoveScale(tx-(float)fx*twdt/fwdt, ty-(float)fy*thgt/fhgt, (float) twdt/fwdt, (float) thgt/fhgt);
440 Transform *=* pTransform;
441 C4BltTransform TransformBack;
442 TransformBack.SetAsInv(Transform);
443 auto ttx0=(float)tx, tty0=(float)ty, ttx1=(float)(tx+twdt), tty1=(float)(ty+thgt);
444 auto ttx2=(float)ttx0, tty2=(float)tty1, ttx3=(float)ttx1, tty3=(float)tty0;
445 pTransform->TransformPoint(ttx0, tty0);
446 pTransform->TransformPoint(ttx1, tty1);
447 pTransform->TransformPoint(ttx2, tty2);
448 pTransform->TransformPoint(ttx3, tty3);
449 int ttxMin = std::max<int>((int)floor(std::min(std::min(ttx0, ttx1), std::min(ttx2, ttx3))), 0);
450 int ttxMax = std::min<int>((int)ceil(std::max(std::max(ttx0, ttx1), std::max(ttx2, ttx3))), sfcTarget->Wdt);
451 int ttyMin = std::max<int>((int)floor(std::min(std::min(tty0, tty1), std::min(tty2, tty3))), 0);
452 int ttyMax = std::min<int>((int)ceil(std::max(std::max(tty0, tty1), std::max(tty2, tty3))), sfcTarget->Hgt);
453 // blit within target rect
454 for (int y = ttyMin; y < ttyMax; ++y)
455 for (int x = ttxMin; x < ttxMax; ++x)
456 {
457 float ffx=(float)x, ffy=(float)y;
458 TransformBack.TransformPoint(ffx, ffy);
459 int ifx=static_cast<int>(ffx), ify=static_cast<int>(ffy);
460 if (ifx<fx || ify<fy || ifx>=fx+fwdt || ify>=fy+fhgt) continue;
461 sfcTarget->BltPix(x,y, sfcSource, ifx,ify, !!fSrcColKey);
462 }
463 // Unlock the surfaces
464 sfcSource->Unlock();
465 sfcTarget->Unlock();
466 return true;
467 }
468
BlitSimple(C4Surface * sfcSource,int fx,int fy,int fwdt,int fhgt,C4Surface * sfcTarget,int tx,int ty,int twdt,int thgt,bool fTransparency)469 bool C4Draw::BlitSimple(C4Surface * sfcSource, int fx, int fy, int fwdt, int fhgt,
470 C4Surface * sfcTarget, int tx, int ty, int twdt, int thgt,
471 bool fTransparency)
472 {
473 // rendertarget?
474 if (sfcTarget->IsRenderTarget())
475 {
476 return Blit(sfcSource, float(fx), float(fy), float(fwdt), float(fhgt), sfcTarget, float(tx), float(ty), float(twdt), float(thgt), true);
477 }
478 // Object is first stretched to dest rect
479 int xcnt,ycnt,tcx,tcy,cpcx,cpcy;
480 if (!fwdt || !fhgt || !twdt || !thgt) return false;
481 // Lock the surfaces
482 if (!sfcSource->Lock())
483 return false;
484 if (!sfcTarget->Lock())
485 { sfcSource->Unlock(); return false; }
486 // Rectangle centers
487 tcx=twdt/2; tcy=thgt/2;
488 for (ycnt=0; ycnt<thgt; ycnt++)
489 if (Inside(cpcy=ty+tcy-thgt/2+ycnt,0,sfcTarget->Hgt-1))
490 for (xcnt=0; xcnt<twdt; xcnt++)
491 if (Inside(cpcx=tx+tcx-twdt/2+xcnt,0,sfcTarget->Wdt-1))
492 sfcTarget->BltPix(cpcx, cpcy, sfcSource, xcnt*fwdt/twdt+fx, ycnt*fhgt/thgt+fy, fTransparency);
493 // Unlock the surfaces
494 sfcSource->Unlock();
495 sfcTarget->Unlock();
496 return true;
497 }
498
499
Error(const char * szMsg)500 bool C4Draw::Error(const char *szMsg)
501 {
502 if (pApp) pApp->Error(szMsg);
503 Log(szMsg); return false;
504 }
505
506
CreatePrimaryClipper(unsigned int iXRes,unsigned int iYRes)507 bool C4Draw::CreatePrimaryClipper(unsigned int iXRes, unsigned int iYRes)
508 {
509 // simply setup primary viewport
510 // assume no zoom has been set yet
511 assert(Zoom==1.0f);
512 SetPrimaryClipper(0, 0, iXRes - 1, iYRes - 1);
513 StorePrimaryClipper();
514 return true;
515 }
516
BlitSurface(C4Surface * sfcSurface,C4Surface * sfcTarget,int tx,int ty,bool fBlitBase)517 bool C4Draw::BlitSurface(C4Surface * sfcSurface, C4Surface * sfcTarget, int tx, int ty, bool fBlitBase)
518 {
519 if (fBlitBase)
520 {
521 Blit(sfcSurface, 0.0f, 0.0f, (float)sfcSurface->Wdt, (float)sfcSurface->Hgt, sfcTarget, float(tx), float(ty), float(sfcSurface->Wdt), float(sfcSurface->Hgt), false);
522 return true;
523 }
524 else
525 {
526 if (!sfcSurface) return false;
527 C4Surface *pSfcBase = sfcSurface->pMainSfc;
528 sfcSurface->pMainSfc = nullptr;
529 Blit(sfcSurface, 0.0f, 0.0f, (float)sfcSurface->Wdt, (float)sfcSurface->Hgt, sfcTarget, float(tx), float(ty), float(sfcSurface->Wdt), float(sfcSurface->Hgt), false);
530 sfcSurface->pMainSfc = pSfcBase;
531 return true;
532 }
533 }
534
BlitSurfaceTile(C4Surface * sfcSurface,C4Surface * sfcTarget,float iToX,float iToY,float iToWdt,float iToHgt,float iOffsetX,float iOffsetY,C4ShaderCall * shader_call)535 bool C4Draw::BlitSurfaceTile(C4Surface * sfcSurface, C4Surface * sfcTarget, float iToX, float iToY, float iToWdt, float iToHgt, float iOffsetX, float iOffsetY, C4ShaderCall* shader_call)
536 {
537 // Only direct rendering from single, tileable, texture
538 if (!sfcTarget->IsRenderTarget()) return false;
539 if ((sfcSurface->texture->iFlags & C4SF_Tileable) == 0) return false;
540
541 // source surface dimensions
542 const float sourceWdt = sfcSurface->Wdt;
543 const float sourceHgt = sfcSurface->Hgt;
544
545 // vertex positions
546 C4BltVertex vertices[6];
547 vertices[0].ftx = iToX; vertices[0].fty = iToY; vertices[0].ftz = 0.0f;
548 vertices[0].tx = (0.0f + iOffsetX) / sourceWdt; vertices[0].ty = (0.0f + iOffsetY) / sourceHgt;
549 DwTo4UB(0xffffffff, vertices[0].color);
550 vertices[1].ftx = iToX + iToWdt; vertices[1].fty = iToY; vertices[1].ftz = 0.0f;
551 vertices[1].tx = (iToWdt + iOffsetX) / sourceWdt; vertices[1].ty = (0.0f + iOffsetY) / sourceHgt;
552 DwTo4UB(0xffffffff, vertices[1].color);
553 vertices[2].ftx = iToX + iToWdt; vertices[2].fty = iToY + iToHgt; vertices[2].ftz = 0.0f;
554 vertices[2].tx = (iToWdt + iOffsetX) / sourceWdt; vertices[2].ty = (iToHgt + iOffsetY) / sourceHgt;
555 DwTo4UB(0xffffffff, vertices[2].color);
556 vertices[3].ftx = iToX; vertices[3].fty = iToY + iToHgt; vertices[3].ftz = 0.0f;
557 vertices[3].tx = (0.0f + iOffsetX) / sourceWdt; vertices[3].ty = (iToHgt + iOffsetY) / sourceHgt;
558 DwTo4UB(0xffffffff, vertices[3].color);
559 // duplicate vertices
560 vertices[4] = vertices[0]; vertices[5] = vertices[2];
561
562 // Draw
563 PerformMultiTris(sfcTarget, vertices, 6, nullptr, sfcSurface->texture.get(), nullptr, nullptr, 0, shader_call);
564 return true;
565 }
566
TextOut(const char * szText,CStdFont & rFont,float fZoom,C4Surface * sfcDest,float iTx,float iTy,DWORD dwFCol,BYTE byForm,bool fDoMarkup)567 bool C4Draw::TextOut(const char *szText, CStdFont &rFont, float fZoom, C4Surface * sfcDest, float iTx, float iTy, DWORD dwFCol, BYTE byForm, bool fDoMarkup)
568 {
569 C4Markup Markup(true);
570 static char szLinebuf[2500+1];
571 for (int cnt=0; SCopySegmentEx(szText,cnt,szLinebuf,fDoMarkup ? '|' : '\n','\n',2500); cnt++,iTy+=int(fZoom*rFont.GetLineHeight()))
572 if (!StringOut(szLinebuf,sfcDest,iTx,iTy,dwFCol,byForm,fDoMarkup,Markup,&rFont,fZoom)) return false;
573 return true;
574 }
575
StringOut(const char * szText,CStdFont & rFont,float fZoom,C4Surface * sfcDest,float iTx,float iTy,DWORD dwFCol,BYTE byForm,bool fDoMarkup)576 bool C4Draw::StringOut(const char *szText, CStdFont &rFont, float fZoom, C4Surface * sfcDest, float iTx, float iTy, DWORD dwFCol, BYTE byForm, bool fDoMarkup)
577 {
578 // init markup
579 C4Markup Markup(true);
580 // output string
581 return StringOut(szText, sfcDest, iTx, iTy, dwFCol, byForm, fDoMarkup, Markup, &rFont, fZoom);
582 }
583
StringOut(const char * szText,C4Surface * sfcDest,float iTx,float iTy,DWORD dwFCol,BYTE byForm,bool fDoMarkup,C4Markup & Markup,CStdFont * pFont,float fZoom)584 bool C4Draw::StringOut(const char *szText, C4Surface * sfcDest, float iTx, float iTy, DWORD dwFCol, BYTE byForm, bool fDoMarkup, C4Markup &Markup, CStdFont *pFont, float fZoom)
585 {
586 // clip
587 if (ClipAll) return true;
588 // safety
589 if (!PrepareRendering(sfcDest)) return false;
590 // convert align
591 int iFlags=0;
592 switch (byForm)
593 {
594 case ACenter: iFlags |= STDFONT_CENTERED; break;
595 case ARight: iFlags |= STDFONT_RIGHTALGN; break;
596 }
597 if (!fDoMarkup) iFlags|=STDFONT_NOMARKUP;
598 // draw text
599 pFont->DrawText(sfcDest, iTx , iTy , dwFCol, szText, iFlags, Markup, fZoom);
600 // done, success
601 return true;
602 }
603
DrawPix(C4Surface * sfcDest,float tx,float ty,DWORD dwClr)604 void C4Draw::DrawPix(C4Surface * sfcDest, float tx, float ty, DWORD dwClr)
605 {
606 // Draw
607 C4BltVertex vtx;
608 vtx.ftx = tx;
609 vtx.fty = ty;
610 DwTo4UB(dwClr, vtx.color);
611 PerformMultiPix(sfcDest, &vtx, 1, nullptr);
612 }
613
DrawLineDw(C4Surface * sfcTarget,float x1,float y1,float x2,float y2,DWORD dwClr,float width)614 void C4Draw::DrawLineDw(C4Surface * sfcTarget, float x1, float y1, float x2, float y2, DWORD dwClr, float width)
615 {
616 C4BltVertex vertices[2];
617 vertices[0].ftx = x1; vertices[0].fty = y1;
618 vertices[1].ftx = x2; vertices[1].fty = y2;
619 DwTo4UB(dwClr, vertices[0].color);
620 DwTo4UB(dwClr, vertices[1].color);
621 PerformMultiLines(sfcTarget, vertices, 2, width, nullptr);
622 }
623
DrawCircleDw(C4Surface * sfcTarget,float cx,float cy,float r,DWORD dwClr,float width)624 void C4Draw::DrawCircleDw(C4Surface * sfcTarget, float cx, float cy, float r, DWORD dwClr, float width)
625 {
626 // Draw as line segments
627 int32_t num_lines = 12 + int32_t(r / 10);
628 std::unique_ptr<C4BltVertex[]> vertices(new C4BltVertex[num_lines * 2]);
629 for (int32_t i = 0; i < num_lines; ++i)
630 {
631 float ang = float(i) * 2 * M_PI / num_lines;
632 int32_t iv = i * 2 + 1;
633 vertices[iv].ftx = cx + sin(ang) * r;
634 vertices[iv].fty = cy + cos(ang) * r;
635 DwTo4UB(dwClr, vertices[iv].color);
636 vertices[(iv + 1) % (num_lines * 2)] = vertices[iv];
637 }
638 PerformMultiLines(sfcTarget, vertices.get(), num_lines * 2, width, nullptr);
639 }
640
DrawFrameDw(C4Surface * sfcDest,int x1,int y1,int x2,int y2,DWORD dwClr,float width)641 void C4Draw::DrawFrameDw(C4Surface * sfcDest, int x1, int y1, int x2, int y2, DWORD dwClr, float width) // make these parameters float...?
642 {
643 C4BltVertex vertices[8];
644 vertices[0].ftx = x1; vertices[0].fty = y1;
645 vertices[1].ftx = x2; vertices[1].fty = y1;
646 vertices[2] = vertices[1];
647 vertices[3].ftx = x2; vertices[3].fty = y2;
648 vertices[4] = vertices[3];
649 vertices[5].ftx = x1; vertices[5].fty = y2;
650 vertices[6] = vertices[5];
651 vertices[7] = vertices[0];
652
653 for(auto & vertex : vertices)
654 DwTo4UB(dwClr, vertex.color);
655
656 PerformMultiLines(sfcDest, vertices, 8, width, nullptr);
657 }
658
DrawQuadDw(C4Surface * sfcTarget,float * ipVtx,DWORD dwClr1,DWORD dwClr2,DWORD dwClr3,DWORD dwClr4,C4ShaderCall * shader_call)659 void C4Draw::DrawQuadDw(C4Surface * sfcTarget, float *ipVtx, DWORD dwClr1, DWORD dwClr2, DWORD dwClr3, DWORD dwClr4, C4ShaderCall* shader_call)
660 {
661 C4BltVertex vertices[6];
662 vertices[0].ftx = ipVtx[0]; vertices[0].fty = ipVtx[1];
663 vertices[1].ftx = ipVtx[2]; vertices[1].fty = ipVtx[3];
664 vertices[2].ftx = ipVtx[4]; vertices[2].fty = ipVtx[5];
665 vertices[3].ftx = ipVtx[6]; vertices[3].fty = ipVtx[7];
666 DwTo4UB(dwClr1, vertices[0].color);
667 DwTo4UB(dwClr2, vertices[1].color);
668 DwTo4UB(dwClr3, vertices[2].color);
669 DwTo4UB(dwClr4, vertices[3].color);
670 vertices[4] = vertices[0];
671 vertices[5] = vertices[2];
672 PerformMultiTris(sfcTarget, vertices, 6, nullptr, nullptr, nullptr, nullptr, 0, shader_call);
673 }
674
DrawPatternedCircle(C4Surface * sfcDest,int x,int y,int r,BYTE col,C4Pattern & Pattern,CStdPalette & rPal)675 void C4Draw::DrawPatternedCircle(C4Surface * sfcDest, int x, int y, int r, BYTE col, C4Pattern & Pattern, CStdPalette &rPal)
676 {
677 bool fRenderTarget = sfcDest->IsRenderTarget();
678 if (!fRenderTarget) if (!sfcDest->Lock()) return;
679 for (int ycnt = -r; ycnt < r; ycnt++)
680 {
681 int lwdt = (int)sqrt(float(r * r - ycnt * ycnt));
682 // Set line
683 if (fRenderTarget)
684 {
685 for (int xcnt = x - lwdt; xcnt < x + lwdt; ++xcnt)
686 {
687 DrawPix(sfcDest, xcnt, y + ycnt, Pattern.PatternClr(xcnt, y + ycnt));
688 }
689 }
690 else
691 {
692 for (int xcnt = x - lwdt; xcnt < x + lwdt; ++xcnt)
693 {
694 sfcDest->SetPixDw(xcnt, y + ycnt, Pattern.PatternClr(xcnt, y + ycnt));
695 }
696 }
697 }
698 if (!fRenderTarget) sfcDest->Unlock();
699 }
700
Grayscale(C4Surface * sfcSfc,int32_t iOffset)701 void C4Draw::Grayscale(C4Surface * sfcSfc, int32_t iOffset)
702 {
703 // safety
704 if (!sfcSfc) return;
705 // change colors
706 int xcnt,ycnt,wdt=sfcSfc->Wdt,hgt=sfcSfc->Hgt;
707 // Lock surface
708 if (!sfcSfc->Lock()) return;
709 for (ycnt=0; ycnt<hgt; ycnt++)
710 {
711 for (xcnt=0; xcnt<wdt; xcnt++)
712 {
713 DWORD dwColor = sfcSfc->GetPixDw(xcnt,ycnt,false);
714 uint32_t r = GetRedValue(dwColor), g = GetGreenValue(dwColor), b = GetBlueValue(dwColor), a = dwColor >> 24;
715 int32_t gray = Clamp<int32_t>((r + g + b) / 3 + iOffset, 0, 255);
716 sfcSfc->SetPixDw(xcnt, ycnt, RGBA(gray, gray, gray, a));
717 }
718 }
719 sfcSfc->Unlock();
720 }
721
GetPrimaryClipper(int & rX1,int & rY1,int & rX2,int & rY2)722 bool C4Draw::GetPrimaryClipper(int &rX1, int &rY1, int &rX2, int &rY2)
723 {
724 // Store drawing clip values
725 rX1=fClipX1; rY1=fClipY1; rX2=fClipX2; rY2=fClipY2;
726 // Done
727 return true;
728 }
729
GetClipRect() const730 C4Rect C4Draw::GetClipRect() const
731 {
732 int iWdt=std::min(iClipX2, RenderTarget->Wdt-1)-iClipX1+1;
733 int iHgt=std::min(iClipY2, RenderTarget->Hgt-1)-iClipY1+1;
734 int iX=iClipX1; if (iX<0) { iWdt+=iX; iX=0; }
735 int iY=iClipY1; if (iY<0) { iHgt+=iY; iY=0; }
736 return C4Rect(iX, iY, iWdt, iHgt);
737 }
738
GetOutRect() const739 C4Rect C4Draw::GetOutRect() const
740 {
741 return C4Rect(0, 0, RenderTarget->Wdt, RenderTarget->Hgt);
742 }
743
SetGamma(float r,float g,float b,int32_t iRampIndex)744 void C4Draw::SetGamma(float r, float g, float b, int32_t iRampIndex)
745 {
746 // Set
747 gamma[iRampIndex][0] = r;
748 gamma[iRampIndex][1] = g;
749 gamma[iRampIndex][2] = b;
750 // Recalculate resulting gamma. Note that we flip gamma direction here,
751 // because higher gammaOut means darker.
752 gammaOut[0] = gammaOut[1] = gammaOut[2] = 1.0f;
753 for (auto & i : gamma) {
754 gammaOut[0] /= i[0];
755 gammaOut[1] /= i[1];
756 gammaOut[2] /= i[2];
757 }
758 }
759
ResetGamma()760 void C4Draw::ResetGamma()
761 {
762 for (auto & i : gamma) {
763 i[0] = 1.0f;
764 i[1] = 1.0f;
765 i[2] = 1.0f;
766 }
767 gammaOut[0] = 1.0f;
768 gammaOut[1] = 1.0f;
769 gammaOut[2] = 1.0f;
770 }
771
ApplyGammaTo(DWORD dwClr)772 DWORD C4Draw::ApplyGammaTo(DWORD dwClr)
773 {
774 return C4RGB(int(pow(float(GetRedValue(dwClr)) / 255.0f, gammaOut[0]) * 255.0),
775 int(pow(float(GetGreenValue(dwClr)) / 255.0f, gammaOut[1]) * 255.0),
776 int(pow(float(GetBlueValue(dwClr)) / 255.0f, gammaOut[2]) * 255.0));
777 }
778
SetZoom(float X,float Y,float Zoom)779 void C4Draw::SetZoom(float X, float Y, float Zoom)
780 {
781 this->ZoomX = X; this->ZoomY = Y; this->Zoom = Zoom;
782 }
783
ApplyZoom(float & X,float & Y)784 void C4Draw::ApplyZoom(float & X, float & Y)
785 {
786 X = (X - ZoomX) * Zoom + ZoomX;
787 Y = (Y - ZoomY) * Zoom + ZoomY;
788 }
789
RemoveZoom(float & X,float & Y)790 void C4Draw::RemoveZoom(float & X, float & Y)
791 {
792 X = (X - ZoomX) / Zoom + ZoomX;
793 Y = (Y - ZoomY) / Zoom + ZoomY;
794 }
795
DDrawInit(C4AbstractApp * pApp,unsigned int iXRes,unsigned int iYRes,unsigned int iMonitor)796 bool DDrawInit(C4AbstractApp * pApp, unsigned int iXRes, unsigned int iYRes, unsigned int iMonitor)
797 {
798 // create engine
799 #ifndef USE_CONSOLE
800 pDraw = new CStdGL();
801 #else
802 pDraw = new CStdNoGfx();
803 #endif
804 if (!pDraw) return false;
805 // init it
806 if (!pDraw->Init(pApp, iXRes, iYRes, iMonitor))
807 {
808 delete pDraw;
809 return false;
810 }
811 // done, success
812 return true;
813 }
814
Init(C4AbstractApp * pApp,unsigned int iXRes,unsigned int iYRes,unsigned int iMonitor)815 bool C4Draw::Init(C4AbstractApp * pApp, unsigned int iXRes, unsigned int iYRes, unsigned int iMonitor)
816 {
817 this->pApp = pApp;
818
819 pApp->pWindow->pSurface = new C4Surface(pApp, pApp->pWindow);
820
821 if (!RestoreDeviceObjects())
822 return false;
823
824 if (!CreatePrimaryClipper(iXRes, iYRes))
825 return Error(" Clipper failure.");
826
827 return true;
828 }
829
DrawBoxFade(C4Surface * sfcDest,float iX,float iY,float iWdt,float iHgt,DWORD dwClr1,DWORD dwClr2,DWORD dwClr3,DWORD dwClr4,C4ShaderCall * shader_call)830 void C4Draw::DrawBoxFade(C4Surface * sfcDest, float iX, float iY, float iWdt, float iHgt, DWORD dwClr1, DWORD dwClr2, DWORD dwClr3, DWORD dwClr4, C4ShaderCall* shader_call)
831 {
832 // set vertex buffer data
833 // vertex order:
834 // 0=upper left dwClr1
835 // 1=lower left dwClr3
836 // 2=lower right dwClr4
837 // 3=upper right dwClr2
838 float vtx[8];
839 vtx[0] = iX ; vtx[1] = iY;
840 vtx[2] = iX ; vtx[3] = iY+iHgt;
841 vtx[4] = iX+iWdt; vtx[5] = iY+iHgt;
842 vtx[6] = iX+iWdt; vtx[7] = iY;
843 DrawQuadDw(sfcDest, vtx, dwClr1, dwClr3, dwClr4, dwClr2, shader_call);
844 }
845
DrawBoxDw(C4Surface * sfcDest,int iX1,int iY1,int iX2,int iY2,DWORD dwClr)846 void C4Draw::DrawBoxDw(C4Surface * sfcDest, int iX1, int iY1, int iX2, int iY2, DWORD dwClr)
847 {
848 if (!sfcDest->IsRenderTarget())
849 {
850 // Box on non-render target: Emulate by setting pixels
851 if (!sfcDest->Lock()) return;
852 for (int y = iY1; y <= iY2; ++y)
853 for (int x = iX1; x <= iX2; ++x)
854 sfcDest->SetPixDw(x,y, dwClr);
855 sfcDest->Unlock();
856 }
857 else
858 {
859 DrawBoxFade(sfcDest, float(iX1), float(iY1), float(iX2 - iX1 + 1), float(iY2 - iY1 + 1), dwClr, dwClr, dwClr, dwClr, nullptr);
860 }
861 }
862