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