1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2021, The LegacyClonk 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
20#include "C4Config.h"
21
22#include <Standard.h>
23#include "StdApp.h"
24#include <StdDDraw2.h>
25#include <StdGL.h>
26#include <StdNoGfx.h>
27#include <StdMarkup.h>
28#include <StdFont.h>
29#include <StdWindow.h>
30
31#include <stdio.h>
32#include <limits.h>
33#include <cmath>
34#include <numbers>
35
36// Global access pointer
37CStdDDraw *lpDDraw = nullptr;
38CStdPalette *lpDDrawPal = nullptr;
39int iGfxEngine = -1;
40
41void CBltTransform::SetRotate(int iAngle, float fOffX, float fOffY) // set by angle and rotation offset
42{
43 // iAngle is in 1/100-degrees (cycling from 0 to 36000)
44 // determine sine and cos of reversed angle in radians
45 // fAngle = -iAngle/100 * pi/180 = iAngle * -pi/18000
46 float fAngle = static_cast<float>(iAngle) * (-1.7453292519943295769236907684886e-4f);
47 float fsin = sinf(x: fAngle); float fcos = cosf(x: fAngle);
48 // set matrix values
49 mat[0] = +fcos; mat[1] = +fsin; mat[2] = (1 - fcos) * fOffX - fsin * fOffY;
50 mat[3] = -fsin; mat[4] = +fcos; mat[5] = (1 - fcos) * fOffY + fsin * fOffX;
51 mat[6] = 0; mat[7] = 0; mat[8] = 1;
52 /* calculation of rotation matrix:
53 x2 = fcos*(x1-fOffX) + fsin*(y1-fOffY) + fOffX
54 = fcos*x1 - fcos*fOffX + fsin*y1 - fsin*fOffY + fOffX
55 = x1*fcos + y1*fsin + (1-fcos)*fOffX - fsin*fOffY
56
57 y2 = -fsin*(x1-fOffX) + fcos*(y1-fOffY) + fOffY
58 = x1*-fsin + fsin*fOffX + y1*fcos - fcos*fOffY + fOffY
59 = x1*-fsin + y1*fcos + fsin*fOffX + (1-fcos)*fOffY */
60}
61
62bool CBltTransform::SetAsInv(CBltTransform &r)
63{
64 // calc inverse of matrix
65 float det = r.mat[0] * r.mat[4] * r.mat[8] + r.mat[1] * r.mat[5] * r.mat[6]
66 + r.mat[2] * r.mat[3] * r.mat[7] - r.mat[2] * r.mat[4] * r.mat[6]
67 - r.mat[0] * r.mat[5] * r.mat[7] - r.mat[1] * r.mat[3] * r.mat[8];
68 if (!det) { Set(fA: 1, fB: 0, fC: 0, fD: 0, fE: 1, fF: 0, fG: 0, fH: 0, fI: 1); return false; }
69 mat[0] = (r.mat[4] * r.mat[8] - r.mat[5] * r.mat[7]) / det;
70 mat[1] = (r.mat[2] * r.mat[7] - r.mat[1] * r.mat[8]) / det;
71 mat[2] = (r.mat[1] * r.mat[5] - r.mat[2] * r.mat[4]) / det;
72 mat[3] = (r.mat[5] * r.mat[6] - r.mat[3] * r.mat[8]) / det;
73 mat[4] = (r.mat[0] * r.mat[8] - r.mat[2] * r.mat[6]) / det;
74 mat[5] = (r.mat[2] * r.mat[3] - r.mat[0] * r.mat[5]) / det;
75 mat[6] = (r.mat[3] * r.mat[7] - r.mat[4] * r.mat[6]) / det;
76 mat[7] = (r.mat[1] * r.mat[6] - r.mat[0] * r.mat[7]) / det;
77 mat[8] = (r.mat[0] * r.mat[4] - r.mat[1] * r.mat[3]) / det;
78 return true;
79}
80
81void CBltTransform::TransformPoint(float &rX, float &rY)
82{
83 // apply matrix
84 float fW = mat[6] * rX + mat[7] * rY + mat[8];
85 // store in temp, so original rX is used for calculation of rY
86 float fX = (mat[0] * rX + mat[1] * rY + mat[2]) / fW;
87 rY = (mat[3] * rX + mat[4] * rY + mat[5]) / fW;
88 rX = fX; // apply temp
89}
90
91CPattern &CPattern::operator=(const CPattern &nPattern)
92{
93 pClrs = nPattern.pClrs;
94 pAlpha = nPattern.pAlpha;
95 sfcPattern8 = nPattern.sfcPattern8;
96 sfcPattern32 = nPattern.sfcPattern32;
97 if (sfcPattern32) sfcPattern32->Lock();
98 delete[] CachedPattern;
99 if (nPattern.CachedPattern)
100 {
101 if (!sfcPattern32)
102 {
103 throw std::runtime_error{"Cached pattern without surface to back it"};
104 }
105
106 CachedPattern = new uint32_t[sfcPattern32->Wdt * sfcPattern32->Hgt];
107 memcpy(dest: CachedPattern, src: nPattern.CachedPattern, n: sfcPattern32->Wdt * sfcPattern32->Hgt * 4);
108 }
109 else
110 {
111 CachedPattern = nullptr;
112 }
113 Wdt = nPattern.Wdt;
114 Hgt = nPattern.Hgt;
115 Zoom = nPattern.Zoom;
116 Monochrome = nPattern.Monochrome;
117 return *this;
118}
119
120bool CPattern::Set(C4Surface *sfcSource, int iZoom, bool fMonochrome)
121{
122 // Safety
123 if (!sfcSource) return false;
124 // Clear existing pattern
125 Clear();
126 // new style: simply store pattern for modulation or shifting, which will be decided upon use
127 sfcPattern32 = sfcSource;
128 sfcPattern32->Lock();
129 Wdt = sfcPattern32->Wdt;
130 Hgt = sfcPattern32->Hgt;
131 // set zoom
132 Zoom = iZoom;
133 // set flags
134 Monochrome = fMonochrome;
135 CachedPattern = new uint32_t[Wdt * Hgt];
136 for (int y = 0; y < Hgt; ++y)
137 for (int x = 0; x < Wdt; ++x)
138 {
139 CachedPattern[y * Wdt + x] = sfcPattern32->GetPixDw(iX: x, iY: y, fApplyModulation: false);
140 }
141 return true;
142}
143
144bool CPattern::Set(CSurface8 *sfcSource, int iZoom, bool fMonochrome)
145{
146 // Safety
147 if (!sfcSource) return false;
148 // Clear existing pattern
149 Clear();
150 // new style: simply store pattern for modulation or shifting, which will be decided upon use
151 sfcPattern8 = sfcSource;
152 Wdt = sfcPattern8->Wdt;
153 Hgt = sfcPattern8->Hgt;
154 // set zoom
155 Zoom = iZoom;
156 // set flags
157 Monochrome = fMonochrome;
158 CachedPattern = nullptr;
159 return true;
160}
161
162CPattern::CPattern()
163{
164 // disable
165 sfcPattern32 = nullptr;
166 sfcPattern8 = nullptr;
167 CachedPattern = nullptr;
168 Zoom = 0;
169 Monochrome = false;
170 pClrs = nullptr; pAlpha = nullptr;
171}
172
173void CPattern::Clear()
174{
175 // pattern assigned
176 if (sfcPattern32)
177 {
178 // unlock it
179 sfcPattern32->Unlock();
180 // clear field
181 sfcPattern32 = nullptr;
182 }
183 sfcPattern8 = nullptr;
184 delete[] CachedPattern; CachedPattern = nullptr;
185}
186
187bool CPattern::PatternClr(int iX, int iY, uint8_t &byClr, uint32_t &dwClr, CStdPalette &rPal) const
188{
189 // pattern assigned?
190 if (!sfcPattern32 && !sfcPattern8) return false;
191 // position zoomed?
192 if (Zoom) { iX /= Zoom; iY /= Zoom; }
193 // modulate position
194 reinterpret_cast<unsigned int &>(iX) %= Wdt; reinterpret_cast<unsigned int &>(iY) %= Hgt;
195 // new style: modulate clr
196 if (CachedPattern)
197 {
198 uint32_t dwPix = CachedPattern[iY * Wdt + iX];
199 if (byClr)
200 {
201 if (Monochrome)
202 ModulateClrMonoA(dst&: dwClr, byMod: static_cast<uint8_t>(dwPix), byA: static_cast<uint8_t>(dwPix >> 24));
203 else
204 ModulateClrA(dst&: dwClr, src: dwPix);
205 LightenClr(dst&: dwClr);
206 }
207 else dwClr = dwPix;
208 }
209 // old style?
210 else if (sfcPattern8)
211 {
212 // if color triplet is given, use it
213 uint8_t byShift = sfcPattern8->GetPix(iX, iY);
214 if (pClrs)
215 {
216 // IFT (alpha only)
217 int iAShift = 0; if (byClr & 0xf0) iAShift = 3;
218 // compose color
219 dwClr = RGB(r: pClrs[byShift * 3 + 2], g: pClrs[byShift * 3 + 1], b: pClrs[byShift * 3]) + (pAlpha[byShift + iAShift] << 24);
220 }
221 else
222 {
223 // shift color index and return indexed color
224 byClr += byShift;
225 dwClr = rPal.GetClr(byCol: byClr);
226 }
227 }
228 // success
229 return true;
230}
231
232CGammaControl::~CGammaControl()
233{
234 delete[] red;
235}
236
237void CGammaControl::SetClrChannel(uint16_t *pBuf, uint8_t c1, uint8_t c2, int c3, uint16_t *ref)
238{
239 // Using this minimum value, gamma ramp errors on some cards can be avoided
240 int MinGamma = 0x100;
241 // adjust clr3-value
242 ++c3;
243 // calc ramp
244 uint16_t *pBuf1 = pBuf;
245 uint16_t *pBuf2 = pBuf + size / 2;
246 for (int i = 0; i < size / 2; ++i)
247 {
248 int i2 = size / 2 - i;
249 int size1 = size - 1;
250 // interpolate ramps between the three points
251 *pBuf1++ = BoundBy(bval: ((c1 * i2 + c2 * i) * size1 + (2 * c2 - c1 - c3) * 2 * i * i2) / (size1 * size1 / 512), lbound: 0, rbound: 0xffff);
252 *pBuf2++ = BoundBy(bval: ((c2 * i2 + c3 * i) * size1 + (2 * c2 - c1 - c3) * 2 * i * i2) / (size1 * size1 / 512), lbound: 0, rbound: 0xffff);
253 }
254 if (ref)
255 {
256 for (int i = 0; i < size; ++i)
257 {
258 int c = 0x10000 * i / (size - 1);
259 *pBuf = BoundBy(bval: *pBuf + ref[i] - c, lbound: MinGamma, rbound: 0xffff);
260 ++pBuf;
261 }
262 }
263 else
264 {
265 for (int i = 0; i < size; ++i)
266 {
267 *pBuf = BoundBy<int>(bval: *pBuf, lbound: MinGamma, rbound: 0xffff);
268 ++pBuf;
269 }
270 }
271}
272
273void CGammaControl::Set(uint32_t dwClr1, uint32_t dwClr2, uint32_t dwClr3, int nsize, CGammaControl *ref)
274{
275 if (nsize != size)
276 {
277 delete[] red;
278 red = new uint16_t[nsize * 3];
279 green = &red[nsize];
280 blue = &red[nsize * 2];
281 size = nsize;
282 }
283 // set red, green and blue channel
284 SetClrChannel(pBuf: red, GetBValue(dwClr1), GetBValue(dwClr2), GetBValue(dwClr3), ref: ref ? ref->red : nullptr);
285 SetClrChannel(pBuf: green, GetGValue(dwClr1), GetGValue(dwClr2), GetGValue(dwClr3), ref: ref ? ref->green : nullptr);
286 SetClrChannel(pBuf: blue, GetRValue(dwClr1), GetRValue(dwClr2), GetRValue(dwClr3), ref: ref ? ref->blue : nullptr);
287}
288
289int CGammaControl::GetSize() const
290{
291 return size;
292}
293
294uint32_t CGammaControl::ApplyTo(uint32_t dwClr)
295{
296 // apply to reg, green and blue color component
297 return RGBA(r: red[GetBValue(dwClr) * 256 / size] >> 8, g: green[GetGValue(dwClr) * 256 / size] >> 8, b: blue[GetRValue(dwClr) * 256 / size] >> 8, a: dwClr >> 24);
298}
299
300void CClrModAddMap::Reset(int iResX, int iResY, int iWdtPx, int iHgtPx, int iOffX, int iOffY, uint32_t dwModClr, uint32_t dwAddClr, int x0, int y0, uint32_t dwBackClr, class C4Surface *backsfc)
301{
302 // set values
303 iResolutionX = iResX; iResolutionY = iResY;
304 this->iOffX = -((iOffX) % iResolutionX);
305 this->iOffY = -((iOffY) % iResolutionY);
306 // calc w/h required for map
307 iWdt = (iWdtPx - this->iOffX + iResolutionX - 1) / iResolutionX + 1;
308 iHgt = (iHgtPx - this->iOffY + iResolutionY - 1) / iResolutionY + 1;
309 this->iOffX += x0;
310 this->iOffY += y0;
311 size_t iNewMapSize = iWdt * iHgt;
312 if (iNewMapSize > iMapSize || iNewMapSize < iMapSize / 2)
313 {
314 delete[] pMap;
315 pMap = new CClrModAdd[iMapSize = iNewMapSize];
316 }
317 // is a background color desired?
318 if (dwBackClr && backsfc)
319 {
320 // then draw a background now and fade against transparent later
321 lpDDraw->DrawBoxDw(sfcDest: backsfc, iX1: x0, iY1: y0, iX2: x0 + iWdtPx - 1, iY2: y0 + iHgtPx - 1, dwClr: dwBackClr);
322 dwModClr = 0xffffffff;
323 fFadeTransparent = true;
324 }
325 else
326 fFadeTransparent = false;
327 // reset all of map to given values
328 int i = iMapSize;
329 for (CClrModAdd *pCurr = pMap; i--; ++pCurr)
330 {
331 pCurr->dwModClr = dwModClr;
332 pCurr->dwAddClr = dwAddClr;
333 }
334}
335
336void CClrModAddMap::ReduceModulation(int cx, int cy, int iRadius1, int iRadius2)
337{
338 // reveal all within iRadius1; fade off squared until iRadius2
339 int i = iMapSize, x = iOffX, y = iOffY, xe = iWdt * iResolutionX + iOffX;
340 int iRadius1Sq = iRadius1 * iRadius1, iRadius2Sq = iRadius2 * iRadius2;
341 for (CClrModAdd *pCurr = pMap; i--; ++pCurr)
342 {
343 int d = (x - cx) * (x - cx) + (y - cy) * (y - cy);
344 if (d < iRadius2Sq)
345 {
346 if (d < iRadius1Sq)
347 pCurr->dwModClr = 0xffffff; // full visibility
348 else
349 {
350 // partly visible
351 int iVis = (iRadius2Sq - d) * 255 / (iRadius2Sq - iRadius1Sq);
352 pCurr->dwModClr = fFadeTransparent ? (0xffffff + (std::min<uint32_t>(a: pCurr->dwModClr >> 24, b: 255 - iVis) << 24))
353 : std::max<uint32_t>(a: pCurr->dwModClr, b: RGB(r: iVis, g: iVis, b: iVis));
354 }
355 }
356 // next pos
357 x += iResolutionX;
358 if (x >= xe) { x = iOffX; y += iResolutionY; }
359 }
360}
361
362void CClrModAddMap::AddModulation(int cx, int cy, int iRadius1, int iRadius2, uint8_t byTransparency)
363{
364 // hide all within iRadius1; fade off squared until iRadius2
365 int i = iMapSize, x = iOffX, y = iOffY, xe = iWdt * iResolutionX + iOffX;
366 int iRadius1Sq = iRadius1 * iRadius1, iRadius2Sq = iRadius2 * iRadius2;
367 for (CClrModAdd *pCurr = pMap; i--; ++pCurr)
368 {
369 int d = (x - cx) * (x - cx) + (y - cy) * (y - cy);
370 if (d < iRadius2Sq)
371 {
372 if (d < iRadius1Sq && !byTransparency)
373 pCurr->dwModClr = 0x000000; // full invisibility
374 else
375 {
376 // partly visible
377 int iVis = std::min<int>(a: 255 - std::min<int>(a: (iRadius2Sq - d) * 255 / (iRadius2Sq - iRadius1Sq), b: 255) + byTransparency, b: 255);
378 pCurr->dwModClr = fFadeTransparent ? (0xffffff + (std::max<uint32_t>(a: pCurr->dwModClr >> 24, b: 255 - iVis) << 24))
379 : std::min<uint32_t>(a: pCurr->dwModClr, b: RGB(r: iVis, g: iVis, b: iVis));
380 }
381 }
382 // next pos
383 x += iResolutionX;
384 if (x >= xe) { x = iOffX; y += iResolutionY; }
385 }
386}
387
388uint32_t CClrModAddMap::GetModAt(int x, int y) const
389{
390 // slower, more accurate method: Interpolate between 4 neighboured modulations
391 x -= iOffX;
392 y -= iOffY;
393 int tx = BoundBy(bval: x / iResolutionX, lbound: 0, rbound: iWdt - 1);
394 int ty = BoundBy(bval: y / iResolutionY, lbound: 0, rbound: iHgt - 1);
395 int tx2 = (std::min)(a: tx + 1, b: iWdt - 1);
396 int ty2 = (std::min)(a: ty + 1, b: iHgt - 1);
397 CColorFadeMatrix clrs(tx * iResolutionX, ty * iResolutionY, iResolutionX, iResolutionY, pMap[ty * iWdt + tx].dwModClr, pMap[ty * iWdt + tx2].dwModClr, pMap[ty2 * iWdt + tx].dwModClr, pMap[ty2 * iWdt + tx2].dwModClr);
398 return clrs.GetColorAt(iX: x, iY: y);
399}
400
401CColorFadeMatrix::CColorFadeMatrix(int iX, int iY, int iWdt, int iHgt, uint32_t dwClr1, uint32_t dwClr2, uint32_t dwClr3, uint32_t dwClr4)
402 : ox(iX), oy(iY), w(iWdt), h(iHgt)
403{
404 uint32_t dwMask = 0xff;
405 for (int iChan = 0; iChan < 4; (++iChan), (dwMask <<= 8))
406 {
407 int c0 = (dwClr1 & dwMask) >> (iChan * 8);
408 int cx = (dwClr2 & dwMask) >> (iChan * 8);
409 int cy = (dwClr3 & dwMask) >> (iChan * 8);
410 int ce = (dwClr4 & dwMask) >> (iChan * 8);
411 clrs[iChan].c0 = c0;
412 clrs[iChan].cx = cx - c0;
413 clrs[iChan].cy = cy - c0;
414 clrs[iChan].ce = ce;
415 }
416}
417
418uint32_t CColorFadeMatrix::GetColorAt(int iX, int iY)
419{
420 iX -= ox; iY -= oy;
421 uint32_t dwResult = 0x00;
422 for (int iChan = 0; iChan < 4; ++iChan)
423 {
424 int clr = clrs[iChan].c0 + clrs[iChan].cx * iX / w + clrs[iChan].cy * iY / h;
425 clr += iX * iY * (clrs[iChan].ce - clr) / (w * h);
426 dwResult |= (BoundBy(bval: clr, lbound: 0, rbound: 255) << (iChan * 8));
427 }
428 return dwResult;
429}
430
431void CStdShader::SetMacro(const std::string &key, const std::string &value)
432{
433 macros[key] = value;
434}
435
436void CStdShader::UnsetMacro(const std::string &key)
437{
438 macros.erase(x: key);
439}
440
441void CStdShader::SetSource(const std::string &source)
442{
443 this->source = source;
444}
445
446void CStdShader::SetType(Type type)
447{
448 this->type = type;
449}
450
451void CStdShader::Clear()
452{
453 source.clear();
454 macros.clear();
455}
456
457CStdShaderProgram *CStdShaderProgram::currentShaderProgram = nullptr;
458
459bool CStdShaderProgram::AddShader(CStdShader *shader)
460{
461 EnsureProgram();
462 if (std::find(first: shaders.cbegin(), last: shaders.cend(), val: shader) != shaders.cend())
463 {
464 return true;
465 }
466
467 if (AddShaderInt(shader))
468 {
469 shaders.push_back(x: shader);
470 return true;
471 }
472
473 return false;
474}
475
476void CStdShaderProgram::Clear()
477{
478 shaders.clear();
479}
480
481void CStdShaderProgram::Select()
482{
483 if (currentShaderProgram != this)
484 {
485 OnSelect();
486 currentShaderProgram = this;
487 }
488}
489
490void CStdShaderProgram::Deselect()
491{
492 if (currentShaderProgram)
493 {
494 currentShaderProgram->OnDeselect();
495 currentShaderProgram = nullptr;
496 }
497}
498
499CStdShaderProgram *CStdShaderProgram::GetCurrentShaderProgram()
500{
501 return currentShaderProgram;
502}
503
504void CStdDDraw::Default()
505{
506 RenderTarget = nullptr;
507 ClipAll = false;
508 Active = false;
509 BlitModulated = false;
510 dwBlitMode = 0;
511 Gamma.Default();
512 DefRamp.Default();
513 lpPrimary = lpBack = nullptr;
514 fUseClrModMap = false;
515}
516
517void CStdDDraw::Clear()
518{
519 DisableGamma();
520 Active = BlitModulated = fUseClrModMap = false;
521 dwBlitMode = 0;
522}
523
524bool CStdDDraw::WipeSurface(C4Surface *sfcSurface)
525{
526 if (!sfcSurface) return false;
527 return sfcSurface->Wipe();
528}
529
530bool CStdDDraw::GetSurfaceSize(C4Surface *sfcSurface, int &iWdt, int &iHgt)
531{
532 return sfcSurface->GetSurfaceSize(irX&: iWdt, irY&: iHgt);
533}
534
535bool CStdDDraw::SetPrimaryPalette(uint8_t *pBuf, uint8_t *pAlphaBuf)
536{
537 // store into loaded pal
538 memcpy(dest: &Pal.Colors, src: pBuf, n: 768);
539 // store alpha pal
540 if (pAlphaBuf) memcpy(dest: Pal.Alpha, src: pAlphaBuf, n: 256);
541 // success
542 return true;
543}
544
545bool CStdDDraw::SubPrimaryClipper(int iX1, int iY1, int iX2, int iY2)
546{
547 // Set sub primary clipper
548 SetPrimaryClipper(iX1: (std::max)(a: iX1, b: ClipX1), iY1: (std::max)(a: iY1, b: ClipY1), iX2: (std::min)(a: iX2, b: ClipX2), iY2: (std::min)(a: iY2, b: ClipY2));
549 return true;
550}
551
552bool CStdDDraw::StorePrimaryClipper()
553{
554 // Store current primary clipper
555 StClipX1 = ClipX1; StClipY1 = ClipY1; StClipX2 = ClipX2; StClipY2 = ClipY2;
556 return true;
557}
558
559bool CStdDDraw::RestorePrimaryClipper()
560{
561 // Restore primary clipper
562 SetPrimaryClipper(iX1: StClipX1, iY1: StClipY1, iX2: StClipX2, iY2: StClipY2);
563 return true;
564}
565
566bool CStdDDraw::SetPrimaryClipper(int iX1, int iY1, int iX2, int iY2)
567{
568 // set clipper
569 ClipX1 = iX1; ClipY1 = iY1; ClipX2 = iX2; ClipY2 = iY2;
570 UpdateClipper();
571 // Done
572 return true;
573}
574
575bool CStdDDraw::NoPrimaryClipper()
576{
577 // apply maximum clipper
578 SetPrimaryClipper(iX1: 0, iY1: 0, iX2: 439832, iY2: 439832);
579 // Done
580 return true;
581}
582
583bool CStdDDraw::CalculateClipper(int *const iX, int *const iY, int *const iWdt, int *const iHgt)
584{
585 // no render target? do nothing
586 if (!RenderTarget || !Active) return false;
587 // negative/zero?
588 *iWdt = (std::min)(a: ClipX2, b: RenderTarget->Wdt - 1) - ClipX1 + 1;
589 *iHgt = (std::min)(a: ClipY2, b: RenderTarget->Hgt - 1) - ClipY1 + 1;
590 *iX = ClipX1; if (*iX < 0) { *iWdt += *iX; *iX = 0; }
591 *iY = ClipY1; if (*iY < 0) { *iHgt += *iY; *iY = 0; }
592 if (*iWdt <= 0 || *iHgt <= 0)
593 {
594 ClipAll = true;
595 return false;
596 }
597 ClipAll = false;
598
599 return true;
600}
601
602void CStdDDraw::BlitLandscape(C4Surface *sfcSource, C4Surface *sfcSource2, C4Surface *sfcSource3, int fx, int fy,
603 C4Surface *sfcTarget, int tx, int ty, int wdt, int hgt)
604{
605 Blit(sfcSource, fx: float(fx), fy: float(fy), fwdt: float(wdt), fhgt: float(hgt), sfcTarget, tx: static_cast<float>(tx), ty: static_cast<float>(ty), twdt: static_cast<float>(wdt), thgt: static_cast<float>(hgt), fSrcColKey: false);
606}
607
608void CStdDDraw::Blit8Fast(CSurface8 *sfcSource, int fx, int fy,
609 C4Surface *sfcTarget, int tx, int ty, int wdt, int hgt)
610{
611 // blit 8bit-sfc
612 // lock surfaces
613 assert(sfcTarget->fPrimary);
614 bool fRender = sfcTarget->IsRenderTarget();
615 if (!fRender) if (!sfcTarget->Lock())
616 {
617 return;
618 }
619 // blit 8 bit pix
620 for (int ycnt = 0; ycnt < hgt; ++ycnt)
621 for (int xcnt = 0; xcnt < wdt; ++xcnt)
622 {
623 uint8_t byPix = sfcSource->GetPix(iX: fx + xcnt, iY: fy + ycnt);
624 if (byPix) DrawPixInt(sfcDest: sfcTarget, tx: static_cast<float>(tx + xcnt), ty: static_cast<float>(ty + ycnt), dwCol: sfcSource->pPal->GetClr(byCol: byPix));
625 }
626 // unlock
627 if (!fRender) sfcTarget->Unlock();
628}
629
630bool CStdDDraw::Blit(C4Surface *sfcSource, float fx, float fy, float fwdt, float fhgt,
631 C4Surface *sfcTarget, int tx, int ty, int twdt, int thgt,
632 bool fSrcColKey, CBltTransform *pTransform, bool noScalingCorrection)
633{
634 return Blit(sfcSource, fx, fy, fwdt, fhgt, sfcTarget, tx: static_cast<float>(tx), ty: static_cast<float>(ty), twdt: static_cast<float>(twdt), thgt: static_cast<float>(thgt), fSrcColKey, pTransform, noScalingCorrection);
635}
636
637bool CStdDDraw::Blit(C4Surface *sfcSource, float fx, float fy, float fwdt, float fhgt,
638 C4Surface *sfcTarget, float tx, float ty, float twdt, float thgt,
639 bool fSrcColKey, CBltTransform *pTransform, bool noScalingCorrection)
640{
641 // safety
642 if (!sfcSource || !sfcTarget || !twdt || !thgt || !fwdt || !fhgt) return false;
643 // emulated blit?
644 if (!sfcTarget->IsRenderTarget())
645 return Blit8(sfcSource, fx: static_cast<int>(fx), fy: static_cast<int>(fy), fwdt: static_cast<int>(fwdt), fhgt: static_cast<int>(fhgt), sfcTarget, tx: static_cast<int>(tx), ty: static_cast<int>(ty), twdt: static_cast<int>(twdt), thgt: static_cast<int>(thgt), fSrcColKey, pTransform);
646 // calc stretch
647
648 const auto scalingCorrection = (pApp->GetScale() != 1.f && !noScalingCorrection ? 0.5f : 0.f);
649 if (scalingCorrection != 0)
650 {
651 if (fwdt > 1)
652 {
653 fx += scalingCorrection;
654 fwdt -= 2 * scalingCorrection;
655 }
656 if (fhgt > 1)
657 {
658 fy += scalingCorrection;
659 fhgt -= 2 * scalingCorrection;
660 }
661 }
662
663 float scaleX = twdt / fwdt;
664 float scaleY = thgt / fhgt;
665 // bound
666 if (ClipAll) return true;
667 // check exact
668 bool fExact = !pTransform && fwdt == twdt && fhgt == thgt;
669 // inside screen?
670 if (twdt <= 0 || thgt <= 0) return false;
671 // prepare rendering to surface
672 if (!PrepareRendering(sfcToSurface: sfcTarget)) return false;
673 // texture present?
674 if (!sfcSource->ppTex)
675 {
676 // primary surface?
677 if (sfcSource->fPrimary)
678 {
679 // blit emulated
680 return Blit8(sfcSource, fx: static_cast<int>(fx), fy: int(fy), fwdt: int(fwdt), fhgt: int(fhgt), sfcTarget, tx: static_cast<int>(tx), ty: static_cast<int>(ty), twdt: static_cast<int>(twdt), thgt: static_cast<int>(thgt), fSrcColKey);
681 }
682 return false;
683 }
684 // create blitting struct
685 CBltData BltData;
686 // pass down pTransform
687 BltData.pTransform = pTransform;
688 // blit with basesfc?
689 bool fBaseSfc = false;
690 if (sfcSource->pMainSfc) if (sfcSource->pMainSfc->ppTex) fBaseSfc = true;
691 // set blitting state - done by PerformBlt
692 // get involved texture offsets
693 int iTexSize = sfcSource->iTexSize;
694 int iTexX = (std::max)(a: static_cast<int>(fx / iTexSize), b: 0);
695 int iTexY = (std::max)(a: static_cast<int>(fy / iTexSize), b: 0);
696 int iTexX2 = (std::min)(a: static_cast<int>(fx + fwdt - 1) / iTexSize + 1, b: sfcSource->iTexX);
697 int iTexY2 = (std::min)(a: static_cast<int>(fy + fhgt - 1) / iTexSize + 1, b: sfcSource->iTexY);
698 // calc stretch regarding texture size and indent
699 float scaleX2 = scaleX * (iTexSize + texIndent * 2);
700 float scaleY2 = scaleY * (iTexSize + texIndent * 2);
701 // blit from all these textures
702 SetTexture();
703
704 int chunkSize = iTexSize;
705 if (fUseClrModMap)
706 {
707 chunkSize = std::min(a: iTexSize, b: 64);
708 }
709
710 for (int iY = iTexY; iY < iTexY2; ++iY)
711 {
712 for (int iX = iTexX; iX < iTexX2; ++iX)
713 {
714 // get current blitting offset in texture (beforing any last-tex-size-changes)
715 int iBlitX = iTexSize * iX;
716 int iBlitY = iTexSize * iY;
717 C4TexRef *pTex = *(sfcSource->ppTex + iY * sfcSource->iTexX + iX);
718 // size changed? recalc dependent, relevant (!) values
719 if (iTexSize != pTex->iSize)
720 {
721 iTexSize = pTex->iSize;
722 scaleX2 = scaleX * (iTexSize + texIndent * 2);
723 scaleY2 = scaleY * (iTexSize + texIndent * 2);
724 }
725 int maxXChunk = std::min<int>(a: static_cast<int>((fx + fwdt - iBlitX - 1) / chunkSize + 1), b: iTexSize / chunkSize);
726 int maxYChunk = std::min<int>(a: static_cast<int>((fy + fhgt - iBlitY - 1) / chunkSize + 1), b: iTexSize / chunkSize);
727 for (int yChunk = std::max<int>(a: static_cast<int>((fy - iBlitY) / chunkSize), b: 0); yChunk < maxYChunk; ++yChunk)
728 {
729 for (int xChunk = std::max<int>(a: static_cast<int>((fx - iBlitX) / chunkSize), b: 0); xChunk < maxXChunk; ++xChunk)
730 {
731 int xOffset = xChunk * chunkSize;
732 int yOffset = yChunk * chunkSize;
733 // get new texture source bounds
734 const auto fTexBltLeft = std::max<float>(a: static_cast<float>(xOffset), b: fx - iBlitX);
735 const auto fTexBltTop = std::max<float>(a: static_cast<float>(yOffset), b: fy - iBlitY);
736 const auto fTexBltRight = std::min<float>(a: static_cast<float>(xOffset + chunkSize), b: fx + fwdt - iBlitX);
737 const auto fTexBltBottom = std::min<float>(a: static_cast<float>(yOffset + chunkSize), b: fy + fhgt - iBlitY);
738 // get new dest bounds
739 const float tTexBltLeft {(fTexBltLeft - fx + iBlitX) * scaleX + tx};
740 const float tTexBltTop {(fTexBltTop - fy + iBlitY) * scaleY + ty};
741 const float tTexBltRight {(fTexBltRight - fx + iBlitX) * scaleX + tx};
742 const float tTexBltBottom{(fTexBltBottom - fy + iBlitY) * scaleY + ty};
743 // prepare blit data texture matrix
744 // - translate back to texture 0/0 regarding indent and blit offset
745 // - apply back scaling and texture-indent - simply scale matrix down
746 // - finally, move in texture - this must be done last, so no stupid zoom is applied...
747 // Set resulting matrix directly
748 BltData.TexPos.SetMoveScale(
749 dx: (fTexBltLeft + texIndent) / iTexSize - (tTexBltLeft + blitOffset) / scaleX2,
750 dy: (fTexBltTop + texIndent) / iTexSize - (tTexBltTop + blitOffset) / scaleY2,
751 sx: 1 / scaleX2,
752 sy: 1 / scaleY2);
753 // set up blit data as rect
754 BltData.vtVtx[0].ftx = tTexBltLeft + blitOffset; BltData.vtVtx[0].fty = tTexBltTop + blitOffset;
755 BltData.vtVtx[1].ftx = tTexBltRight + blitOffset; BltData.vtVtx[1].fty = tTexBltTop + blitOffset;
756 BltData.vtVtx[2].ftx = tTexBltLeft + blitOffset; BltData.vtVtx[2].fty = tTexBltBottom + blitOffset;
757 BltData.vtVtx[3].ftx = tTexBltRight + blitOffset; BltData.vtVtx[3].fty = tTexBltBottom + blitOffset;
758
759 C4TexRef *pBaseTex = pTex;
760 // is there a base-surface to be blitted first?
761 if (fBaseSfc)
762 {
763 // then get this surface as same offset as from other surface
764 // assuming this is only valid as long as there's no texture management,
765 // organizing partially used textures together!
766 pBaseTex = *(sfcSource->pMainSfc->ppTex + iY * sfcSource->iTexX + iX);
767 }
768 // base blit
769 PerformBlt(rBltData&: BltData, pTex: pBaseTex, dwModClr: BlitModulated ? BlitModulateClr : 0xffffff, fMod2: !!(dwBlitMode & C4GFXBLIT_MOD2), fExact);
770 // overlay
771 if (fBaseSfc)
772 {
773 uint32_t dwModClr = sfcSource->ClrByOwnerClr;
774 // apply global modulation to overlay surfaces only if desired
775 if (BlitModulated && !(dwBlitMode & C4GFXBLIT_CLRSFC_OWNCLR))
776 ModulateClr(dst&: dwModClr, src: BlitModulateClr);
777 PerformBlt(rBltData&: BltData, pTex, dwModClr, fMod2: !!(dwBlitMode & C4GFXBLIT_CLRSFC_MOD2), fExact);
778 }
779 }
780 }
781 }
782 }
783 // reset texture
784 ResetTexture();
785 // success
786 return true;
787}
788
789bool CStdDDraw::Blit8(C4Surface *sfcSource, int fx, int fy, int fwdt, int fhgt,
790 C4Surface *sfcTarget, int tx, int ty, int twdt, int thgt,
791 bool fSrcColKey, CBltTransform *pTransform)
792{
793 if (!pTransform) return BlitRotate(sfcSource, fx, fy, fwdt, fhgt, sfcTarget, tx, ty, twdt, thgt, iAngle: 0, fTransparency: fSrcColKey != false);
794 // safety
795 if (!fwdt || !fhgt) return true;
796 // Lock the surfaces
797 if (!sfcSource->Lock())
798 return false;
799 if (!sfcTarget->Lock())
800 {
801 sfcSource->Unlock(); return false;
802 }
803 // transformed, emulated blit
804 // Calculate transform target rect
805 CBltTransform Transform;
806 Transform.SetMoveScale(dx: tx - static_cast<float>(fx) * twdt / fwdt, dy: ty - static_cast<float>(fy) * thgt / fhgt, sx: static_cast<float>(twdt) / fwdt, sy: static_cast<float>(thgt) / fhgt);
807 Transform *= *pTransform;
808 CBltTransform TransformBack;
809 TransformBack.SetAsInv(Transform);
810 float ttx0 = static_cast<float>(tx), tty0 = static_cast<float>(ty), ttx1 = static_cast<float>(tx + twdt), tty1 = static_cast<float>(ty + thgt);
811 float ttx2 = ttx0, tty2 = tty1, ttx3 = ttx1, tty3 = tty0;
812 pTransform->TransformPoint(rX&: ttx0, rY&: tty0);
813 pTransform->TransformPoint(rX&: ttx1, rY&: tty1);
814 pTransform->TransformPoint(rX&: ttx2, rY&: tty2);
815 pTransform->TransformPoint(rX&: ttx3, rY&: tty3);
816 int ttxMin = std::max<int>(a: static_cast<int>(floor(x: (std::min)(a: (std::min)(a: ttx0, b: ttx1), b: (std::min)(a: ttx2, b: ttx3)))), b: 0);
817 int ttxMax = std::min<int>(a: static_cast<int>(ceil(x: (std::max)(a: (std::max)(a: ttx0, b: ttx1), b: (std::max)(a: ttx2, b: ttx3)))), b: sfcTarget->Wdt);
818 int ttyMin = std::max<int>(a: static_cast<int>(floor(x: (std::min)(a: (std::min)(a: tty0, b: tty1), b: (std::min)(a: tty2, b: tty3)))), b: 0);
819 int ttyMax = std::min<int>(a: static_cast<int>(ceil(x: (std::max)(a: (std::max)(a: tty0, b: tty1), b: (std::max)(a: tty2, b: tty3)))), b: sfcTarget->Hgt);
820 // blit within target rect
821 for (int y = ttyMin; y < ttyMax; ++y)
822 for (int x = ttxMin; x < ttxMax; ++x)
823 {
824 float ffx = static_cast<float>(x), ffy = static_cast<float>(y);
825 TransformBack.TransformPoint(rX&: ffx, rY&: ffy);
826 int ifx = static_cast<int>(ffx), ify = static_cast<int>(ffy);
827 if (ifx < fx || ify < fy || ifx >= fx + fwdt || ify >= fy + fhgt) continue;
828 sfcTarget->BltPix(iX: x, iY: y, sfcSource, iSrcX: ifx, iSrcY: ify, fTransparency: !!fSrcColKey);
829 }
830 // Unlock the surfaces
831 sfcSource->Unlock();
832 sfcTarget->Unlock();
833 return true;
834}
835
836bool CStdDDraw::BlitRotate(C4Surface *sfcSource, int fx, int fy, int fwdt, int fhgt,
837 C4Surface *sfcTarget, int tx, int ty, int twdt, int thgt,
838 int iAngle, bool fTransparency)
839{
840 // rendertarget?
841 if (sfcTarget->IsRenderTarget())
842 {
843 CBltTransform rot;
844 rot.SetRotate(iAngle, fOffX: static_cast<float>(tx + tx + twdt) / 2, fOffY: static_cast<float>(ty + ty + thgt) / 2);
845 return Blit(sfcSource, fx: static_cast<float>(fx), fy: static_cast<float>(fy), fwdt: static_cast<float>(fwdt), fhgt: static_cast<float>(fhgt), sfcTarget, tx, ty, twdt, thgt, fSrcColKey: true, pTransform: &rot);
846 }
847 // Object is first stretched to dest rect, then rotated at place.
848 int xcnt, ycnt, fcx, fcy, tcx, tcy, cpcx, cpcy;
849 int npcx, npcy;
850 double mtx[4], dang;
851 if (!fwdt || !fhgt || !twdt || !thgt) return false;
852 // Lock the surfaces
853 if (!sfcSource->Lock())
854 return false;
855 if (!sfcTarget->Lock())
856 {
857 sfcSource->Unlock(); return false;
858 }
859 // Rectangle centers
860 fcx = fwdt / 2; fcy = fhgt / 2;
861 tcx = twdt / 2; tcy = thgt / 2;
862 // Adjust angle range
863 while (iAngle < 0) iAngle += 36000; while (iAngle > 35999) iAngle -= 36000;
864 // Exact/free rotation
865 switch (iAngle)
866 {
867 case 0:
868 for (ycnt = 0; ycnt < thgt; ycnt++)
869 if (Inside(ival: cpcy = ty + tcy - thgt / 2 + ycnt, lbound: 0, rbound: sfcTarget->Hgt - 1))
870 for (xcnt = 0; xcnt < twdt; xcnt++)
871 if (Inside(ival: cpcx = tx + tcx - twdt / 2 + xcnt, lbound: 0, rbound: sfcTarget->Wdt - 1))
872 sfcTarget->BltPix(iX: cpcx, iY: cpcy, sfcSource, iSrcX: xcnt * fwdt / twdt + fx, iSrcY: ycnt * fhgt / thgt + fy, fTransparency);
873 break;
874
875 case 9000:
876 for (ycnt = 0; ycnt < thgt; ycnt++)
877 if (Inside(ival: cpcx = ty + tcy + thgt / 2 - ycnt, lbound: 0, rbound: sfcTarget->Wdt - 1))
878 for (xcnt = 0; xcnt < twdt; xcnt++)
879 if (Inside(ival: cpcy = tx + tcx - twdt / 2 + xcnt, lbound: 0, rbound: sfcTarget->Hgt - 1))
880 sfcTarget->BltPix(iX: cpcx, iY: cpcy, sfcSource, iSrcX: xcnt * fwdt / twdt + fx, iSrcY: ycnt * fhgt / thgt + fy, fTransparency);
881 break;
882
883 case 18000:
884 for (ycnt = 0; ycnt < thgt; ycnt++)
885 if (Inside(ival: cpcy = ty + tcy + thgt / 2 - ycnt, lbound: 0, rbound: sfcTarget->Hgt - 1))
886 for (xcnt = 0; xcnt < twdt; xcnt++)
887 if (Inside(ival: cpcx = tx + tcx + twdt / 2 - xcnt, lbound: 0, rbound: sfcTarget->Wdt - 1))
888 sfcTarget->BltPix(iX: cpcx, iY: cpcy, sfcSource, iSrcX: xcnt * fwdt / twdt + fx, iSrcY: ycnt * fhgt / thgt + fy, fTransparency);
889 break;
890
891 case 27000:
892 for (ycnt = 0; ycnt < thgt; ycnt++)
893 if (Inside(ival: cpcx = ty + tcy - thgt / 2 + ycnt, lbound: 0, rbound: sfcTarget->Wdt - 1))
894 for (xcnt = 0; xcnt < twdt; xcnt++)
895 if (Inside(ival: cpcy = tx + tcx + twdt / 2 - xcnt, lbound: 0, rbound: sfcTarget->Hgt - 1))
896 sfcTarget->BltPix(iX: cpcx, iY: cpcy, sfcSource, iSrcX: xcnt * fwdt / twdt + fx, iSrcY: ycnt * fhgt / thgt + fy, fTransparency);
897 break;
898
899 default:
900 // Calculate rotation matrix
901 dang = std::numbers::pi * iAngle / 18000.0;
902 mtx[0] = cos(x: dang); mtx[1] = -sin(x: dang);
903 mtx[2] = sin(x: dang); mtx[3] = cos(x: dang);
904 // Blit source rect
905 for (ycnt = 0; ycnt < fhgt; ycnt++)
906 {
907 // Source line start
908 for (xcnt = 0; xcnt < fwdt; xcnt++)
909 {
910 // Current pixel coordinate as from source
911 cpcx = xcnt - fcx; cpcy = ycnt - fcy;
912 // Convert to coordinate as in dest
913 cpcx = cpcx * twdt / fwdt; cpcy = cpcy * thgt / fhgt;
914 // Rotate current pixel coordinate
915 npcx = static_cast<int>(mtx[0] * cpcx + mtx[1] * cpcy);
916 npcy = static_cast<int>(mtx[2] * cpcx + mtx[3] * cpcy);
917 // Place in dest
918 sfcTarget->BltPix(iX: tx + tcx + npcx, iY: ty + tcy + npcy, sfcSource, iSrcX: xcnt + fx, iSrcY: ycnt + fy, fTransparency);
919 sfcTarget->BltPix(iX: tx + tcx + npcx + 1, iY: ty + tcy + npcy, sfcSource, iSrcX: xcnt + fx, iSrcY: ycnt + fy, fTransparency);
920 }
921 }
922 break;
923 }
924
925 // Unlock the surfaces
926 sfcSource->Unlock();
927 sfcTarget->Unlock();
928 return true;
929}
930
931bool CStdDDraw::CreatePrimaryClipper()
932{
933 // simply setup primary viewport
934 SetPrimaryClipper(iX1: 0, iY1: 0, iX2: pApp->ScreenWidth() - 1, iY2: pApp->ScreenHeight() - 1);
935 StClipX1 = ClipX1; StClipY1 = ClipY1; StClipX2 = ClipX2; StClipY2 = ClipY2;
936 return true;
937}
938
939bool CStdDDraw::AttachPrimaryPalette(C4Surface *sfcSurface)
940{
941 return true;
942}
943
944bool CStdDDraw::BlitSurface(C4Surface *sfcSurface, C4Surface *sfcTarget, int tx, int ty, bool fBlitBase)
945{
946 if (fBlitBase)
947 {
948 Blit(sfcSource: sfcSurface, fx: 0.0f, fy: 0.0f, fwdt: static_cast<float>(sfcSurface->Wdt), fhgt: static_cast<float>(sfcSurface->Hgt), sfcTarget, tx, ty, twdt: sfcSurface->Wdt, thgt: sfcSurface->Hgt, fSrcColKey: false);
949 return true;
950 }
951 else
952 {
953 if (!sfcSurface) return false;
954 C4Surface *pSfcBase = sfcSurface->pMainSfc;
955 sfcSurface->pMainSfc = nullptr;
956 Blit(sfcSource: sfcSurface, fx: 0.0f, fy: 0.0f, fwdt: static_cast<float>(sfcSurface->Wdt), fhgt: static_cast<float>(sfcSurface->Hgt), sfcTarget, tx, ty, twdt: sfcSurface->Wdt, thgt: sfcSurface->Hgt, fSrcColKey: false);
957 sfcSurface->pMainSfc = pSfcBase;
958 return true;
959 }
960}
961
962bool CStdDDraw::BlitSurfaceTile(C4Surface *sfcSurface, C4Surface *sfcTarget, int iToX, int iToY, int iToWdt, int iToHgt, int iOffsetX, int iOffsetY, bool fSrcColKey, float scale)
963{
964 iToHgt = static_cast<decltype(iToHgt)>(iToHgt / scale);
965 iToWdt = static_cast<decltype(iToWdt)>(iToWdt / scale);
966
967 int iSourceWdt, iSourceHgt, iX, iY, iBlitX, iBlitY, iBlitWdt, iBlitHgt;
968 // Get source surface size
969 if (!GetSurfaceSize(sfcSurface, iWdt&: iSourceWdt, iHgt&: iSourceHgt)) return false;
970 // reduce offset to needed size
971 iOffsetX %= iSourceWdt;
972 iOffsetY %= iSourceHgt;
973
974 CBltTransform transform;
975 transform.SetMoveScale(dx: 0.f, dy: 0.f, sx: scale, sy: scale);
976 // Vertical blits
977 for (iY = iToY + iOffsetY; iY < iToY + iToHgt; iY += iSourceHgt)
978 {
979 // Vertical blit size
980 iBlitY = (std::max)(a: static_cast<int>(std::floor(x: (iToY - iY) / scale)), b: 0); iBlitHgt = (std::min)(a: iSourceHgt, b: iToY + iToHgt - iY) - iBlitY;
981 // Horizontal blits
982 for (iX = iToX + iOffsetX; iX < iToX + iToWdt; iX += iSourceWdt)
983 {
984 // Horizontal blit size
985 iBlitX = (std::max)(a: static_cast<int>(std::floor(x: (iToX - iX) / scale)), b: 0); iBlitWdt = (std::min)(a: iSourceWdt, b: iToX + iToWdt - iX) - iBlitX;
986 // Blit
987 if (!Blit(sfcSource: sfcSurface, fx: static_cast<float>(iBlitX), fy: static_cast<float>(iBlitY), fwdt: static_cast<float>(iBlitWdt), fhgt: static_cast<float>(iBlitHgt), sfcTarget, tx: iX + iBlitX, ty: iY + iBlitY, twdt: iBlitWdt, thgt: iBlitHgt, fSrcColKey, pTransform: &transform)) return false;
988 }
989 }
990 return true;
991}
992
993bool CStdDDraw::BlitSurfaceTile2(C4Surface *sfcSurface, C4Surface *sfcTarget, int iToX, int iToY, int iToWdt, int iToHgt, int iOffsetX, int iOffsetY, bool fSrcColKey)
994{
995 int tx, ty, iBlitX, iBlitY, iBlitWdt, iBlitHgt;
996 // get tile size
997 int iTileWdt = sfcSurface->Wdt;
998 int iTileHgt = sfcSurface->Hgt;
999 // adjust size of offsets
1000 iOffsetX %= iTileWdt;
1001 iOffsetY %= iTileHgt;
1002 if (iOffsetX < 0) iOffsetX += iTileWdt;
1003 if (iOffsetY < 0) iOffsetY += iTileHgt;
1004 // get start pos for blitting
1005 int iStartX = iToX - iOffsetX;
1006 int iStartY = iToY - iOffsetY;
1007 ty = 0;
1008 // blit vertical
1009 for (int iY = iStartY; ty < iToHgt; iY += iTileHgt)
1010 {
1011 // get vertical blit bounds
1012 iBlitY = 0; iBlitHgt = iTileHgt;
1013 if (iY < iToY) { iBlitY = iToY - iY; iBlitHgt += iY - iToY; }
1014 int iOver = ty + iBlitHgt - iToHgt; if (iOver > 0) iBlitHgt -= iOver;
1015 // blit horizontal
1016 tx = 0;
1017 for (int iX = iStartX; tx < iToWdt; iX += iTileWdt)
1018 {
1019 // get horizontal blit bounds
1020 iBlitX = 0; iBlitWdt = iTileWdt;
1021 if (iX < iToX) { iBlitX = iToX - iX; iBlitWdt += iX - iToX; }
1022 iOver = tx + iBlitWdt - iToWdt; if (iOver > 0) iBlitWdt -= iOver;
1023 // blit
1024 if (!Blit(sfcSource: sfcSurface, fx: static_cast<float>(iBlitX), fy: static_cast<float>(iBlitY), fwdt: static_cast<float>(iBlitWdt), fhgt: static_cast<float>(iBlitHgt), sfcTarget, tx: tx + iToX, ty: ty + iToY, twdt: iBlitWdt, thgt: iBlitHgt, fSrcColKey)) return false;
1025 // next col
1026 tx += iBlitWdt;
1027 }
1028 // next line
1029 ty += iBlitHgt;
1030 }
1031 // success
1032 return true;
1033}
1034
1035bool CStdDDraw::TextOut(const char *szText, CStdFont &rFont, float fZoom, C4Surface *sfcDest, int iTx, int iTy, uint32_t dwFCol, uint8_t byForm, bool fDoMarkup)
1036{
1037 CMarkup Markup(true);
1038 static char szLinebuf[2500 + 1];
1039 for (int cnt = 0; SCopySegmentEx(fstr: szText, segn: cnt, tstr: szLinebuf, sepa1: fDoMarkup ? '|' : '\n', sepa2: '\n', iMaxL: 2500); cnt++, iTy += int(fZoom * rFont.GetLineHeight()))
1040 if (!StringOut(szText: szLinebuf, sfcDest, iTx, iTy, dwFCol, byForm, fDoMarkup, Markup, pFont: &rFont, fZoom)) return false;
1041 return true;
1042}
1043
1044bool CStdDDraw::StringOut(const char *szText, CStdFont &rFont, float fZoom, C4Surface *sfcDest, int iTx, int iTy, uint32_t dwFCol, uint8_t byForm, bool fDoMarkup)
1045{
1046 // init markup
1047 CMarkup Markup(true);
1048 // output string
1049 return StringOut(szText, sfcDest, iTx, iTy, dwFCol, byForm, fDoMarkup, Markup, pFont: &rFont, fZoom);
1050}
1051
1052bool CStdDDraw::StringOut(const char *szText, C4Surface *sfcDest, int iTx, int iTy, uint32_t dwFCol, uint8_t byForm, bool fDoMarkup, CMarkup &Markup, CStdFont *pFont, float fZoom)
1053{
1054 // clip
1055 if (ClipAll) return true;
1056 // safety
1057 if (!PrepareRendering(sfcToSurface: sfcDest)) return false;
1058 // convert align
1059 int iFlags = 0;
1060 switch (byForm)
1061 {
1062 case ACenter: iFlags |= STDFONT_CENTERED; break;
1063 case ARight: iFlags |= STDFONT_RIGHTALGN; break;
1064 }
1065 if (!fDoMarkup) iFlags |= STDFONT_NOMARKUP;
1066 // draw text
1067 pFont->DrawText(sfcDest, iX: iTx, iY: iTy, dwColor: dwFCol, szText, dwFlags: iFlags, Markup, fZoom);
1068 // done, success
1069 return true;
1070}
1071
1072void CStdDDraw::DrawPix(C4Surface *sfcDest, float tx, float ty, uint32_t dwClr)
1073{
1074 // apply global modulation
1075 ClrByCurrentBlitMod(rdwClr&: dwClr);
1076 // apply modulation map
1077 if (fUseClrModMap)
1078 ModulateClr(dst&: dwClr, src: pClrModMap->GetModAt(x: static_cast<int>(tx), y: static_cast<int>(ty)));
1079 // Draw
1080 DrawPixInt(sfcDest, tx, ty, dwCol: dwClr);
1081}
1082
1083void CStdDDraw::DrawBox(C4Surface *sfcDest, int iX1, int iY1, int iX2, int iY2, uint8_t byCol)
1084{
1085 // get color
1086 uint32_t dwClr = Pal.GetClr(byCol);
1087 // offscreen emulation?
1088 if (!sfcDest->IsRenderTarget())
1089 {
1090 int iSfcWdt = sfcDest->Wdt, iSfcHgt = sfcDest->Hgt, xcnt, ycnt;
1091 // Lock surface
1092 if (!sfcDest->Lock()) return;
1093 // Outside of surface/clip
1094 if ((iX2 < (std::max)(a: 0, b: ClipX1)) || (iX1 > (std::min)(a: iSfcWdt - 1, b: ClipX2))
1095 || (iY2 < (std::max)(a: 0, b: ClipY1)) || (iY1 > (std::min)(a: iSfcHgt - 1, b: ClipY2)))
1096 {
1097 sfcDest->Unlock(); return;
1098 }
1099 // Clip to surface/clip
1100 if (iX1 < (std::max)(a: 0, b: ClipX1)) iX1 = (std::max)(a: 0, b: ClipX1); if (iX2 > (std::min)(a: iSfcWdt - 1, b: ClipX2)) iX2 = (std::min)(a: iSfcWdt - 1, b: ClipX2);
1101 if (iY1 < (std::max)(a: 0, b: ClipY1)) iY1 = (std::max)(a: 0, b: ClipY1); if (iY2 > (std::min)(a: iSfcHgt - 1, b: ClipY2)) iY2 = (std::min)(a: iSfcHgt - 1, b: ClipY2);
1102 // Set lines
1103 for (ycnt = iY2 - iY1; ycnt >= 0; ycnt--)
1104 for (xcnt = iX2 - iX1; xcnt >= 0; xcnt--)
1105 sfcDest->SetPixDw(iX: iX1 + xcnt, iY: iY1 + ycnt, dwCol: dwClr);
1106 // Unlock surface
1107 sfcDest->Unlock();
1108 }
1109 // draw as primitives
1110 DrawBoxDw(sfcDest, iX1, iY1, iX2, iY2, dwClr);
1111}
1112
1113void CStdDDraw::DrawHorizontalLine(C4Surface *sfcDest, int x1, int x2, int y, uint8_t col)
1114{
1115 // if this is a render target: draw it as a box
1116 if (sfcDest->IsRenderTarget())
1117 {
1118 DrawLine(sfcTarget: sfcDest, x1, y1: y, x2, y2: y, byCol: col);
1119 return;
1120 }
1121 int lWdt = sfcDest->Wdt, lHgt = sfcDest->Hgt, xcnt;
1122 // Lock surface
1123 if (!sfcDest->Lock()) return;
1124 // Fix coordinates
1125 if (x1 > x2) std::swap(a&: x1, b&: x2);
1126 // Determine clip
1127 int clpx1, clpx2, clpy1, clpy2;
1128 clpx1 = (std::max)(a: 0, b: ClipX1); clpy1 = (std::max)(a: 0, b: ClipY1);
1129 clpx2 = (std::min)(a: lWdt - 1, b: ClipX2); clpy2 = (std::min)(a: lHgt - 1, b: ClipY2);
1130 // Outside clip check
1131 if ((x2 < clpx1) || (x1 > clpx2) || (y < clpy1) || (y > clpy2))
1132 {
1133 sfcDest->Unlock(); return;
1134 }
1135 // Clip to clip
1136 if (x1 < clpx1) x1 = clpx1; if (x2 > clpx2) x2 = clpx2;
1137 // Set line
1138 for (xcnt = x2 - x1; xcnt >= 0; xcnt--) sfcDest->SetPix(iX: x1 + xcnt, iY: y, byCol: col);
1139 // Unlock surface
1140 sfcDest->Unlock();
1141}
1142
1143void CStdDDraw::DrawVerticalLine(C4Surface *sfcDest, int x, int y1, int y2, uint8_t col)
1144{
1145 // if this is a render target: draw it as a box
1146 if (sfcDest->IsRenderTarget())
1147 {
1148 DrawLine(sfcTarget: sfcDest, x1: x, y1, x2: x, y2, byCol: col);
1149 return;
1150 }
1151 int lWdt = sfcDest->Wdt, lHgt = sfcDest->Hgt, ycnt;
1152 // Lock surface
1153 if (!sfcDest->Lock()) return;
1154 // Fix coordinates
1155 if (y1 > y2) std::swap(a&: y1, b&: y2);
1156 // Determine clip
1157 int clpx1, clpx2, clpy1, clpy2;
1158 clpx1 = (std::max)(a: 0, b: ClipX1); clpy1 = (std::max)(a: 0, b: ClipY1);
1159 clpx2 = (std::min)(a: lWdt - 1, b: ClipX2); clpy2 = (std::min)(a: lHgt - 1, b: ClipY2);
1160 // Outside clip check
1161 if ((x < clpx1) || (x > clpx2) || (y2 < clpy1) || (y1 > clpy2))
1162 {
1163 sfcDest->Unlock(); return;
1164 }
1165 // Clip to clip
1166 if (y1 < clpy1) y1 = clpy1; if (y2 > clpy2) y2 = clpy2;
1167 // Set line
1168 for (ycnt = y1; ycnt <= y2; ycnt++) sfcDest->SetPix(iX: x, iY: ycnt, byCol: col);
1169 // Unlock surface
1170 sfcDest->Unlock();
1171}
1172
1173void CStdDDraw::DrawFrame(C4Surface *sfcDest, int x1, int y1, int x2, int y2, uint8_t col)
1174{
1175 DrawHorizontalLine(sfcDest, x1, x2, y: y1, col);
1176 DrawHorizontalLine(sfcDest, x1, x2, y: y2, col);
1177 DrawVerticalLine(sfcDest, x: x1, y1, y2, col);
1178 DrawVerticalLine(sfcDest, x: x2, y1, y2, col);
1179}
1180
1181void CStdDDraw::DrawFrameDw(C4Surface *sfcDest, int x1, int y1, int x2, int y2, uint32_t dwClr) // make these parameters float...?
1182{
1183 DrawLineDw(sfcTarget: sfcDest, x1: static_cast<float>(x1), y1: static_cast<float>(y1), x2: static_cast<float>(x2), y2: static_cast<float>(y1), dwClr);
1184 DrawLineDw(sfcTarget: sfcDest, x1: static_cast<float>(x2), y1: static_cast<float>(y1), x2: static_cast<float>(x2), y2: static_cast<float>(y2), dwClr);
1185 DrawLineDw(sfcTarget: sfcDest, x1: static_cast<float>(x2), y1: static_cast<float>(y2), x2: static_cast<float>(x1), y2: static_cast<float>(y2), dwClr);
1186 DrawLineDw(sfcTarget: sfcDest, x1: static_cast<float>(x1), y1: static_cast<float>(y2), x2: static_cast<float>(x1), y2: static_cast<float>(y1), dwClr);
1187}
1188
1189// Globally locked surface variables - for DrawLine callback crap
1190
1191void CStdDDraw::DrawPatternedCircle(C4Surface *sfcDest, int x, int y, int r, uint8_t col, CPattern &Pattern1, CPattern &Pattern2, CStdPalette &rPal)
1192{
1193 if (!sfcDest->Lock()) return;
1194 for (int ycnt = -r; ycnt < r; ycnt++)
1195 {
1196 int lwdt = static_cast<int>(sqrt(x: static_cast<float>(r * r - ycnt * ycnt)));
1197 // Set line
1198 for (int xcnt = x - lwdt; xcnt < x + lwdt; ++xcnt)
1199 {
1200 // apply both patterns
1201 uint32_t dwClr = rPal.GetClr(byCol: col);
1202 Pattern1.PatternClr(iX: xcnt, iY: y + ycnt, byClr&: col, dwClr, rPal);
1203 Pattern2.PatternClr(iX: xcnt, iY: y + ycnt, byClr&: col, dwClr, rPal);
1204 sfcDest->SetPixDw(iX: xcnt, iY: y + ycnt, dwCol: dwClr);
1205 }
1206 }
1207 sfcDest->Unlock();
1208}
1209
1210void CStdDDraw::SurfaceAllowColor(C4Surface *sfcSfc, uint32_t *pdwColors, int iNumColors, bool fAllowZero)
1211{
1212 // safety
1213 if (!sfcSfc) return;
1214 if (!pdwColors || !iNumColors) return;
1215 // change colors
1216 int xcnt, ycnt, wdt = sfcSfc->Wdt, hgt = sfcSfc->Hgt;
1217 // Lock surface
1218 if (!sfcSfc->Lock()) return;
1219 for (ycnt = 0; ycnt < hgt; ycnt++)
1220 {
1221 for (xcnt = 0; xcnt < wdt; xcnt++)
1222 {
1223 uint32_t dwColor = sfcSfc->GetPixDw(iX: xcnt, iY: ycnt, fApplyModulation: false);
1224 uint8_t px = 0;
1225 for (int i = 0; i < 256; ++i)
1226 if (lpDDrawPal->GetClr(byCol: i) == dwColor)
1227 {
1228 px = i; break;
1229 }
1230 if (fAllowZero)
1231 {
1232 if (!px) continue;
1233 --px;
1234 }
1235 sfcSfc->SetPixDw(iX: xcnt, iY: ycnt, dwCol: pdwColors[px % iNumColors]);
1236 }
1237 }
1238 sfcSfc->Unlock();
1239}
1240
1241void CStdDDraw::Grayscale(C4Surface *sfcSfc, int32_t iOffset)
1242{
1243 // safety
1244 if (!sfcSfc) return;
1245 // change colors
1246 int xcnt, ycnt, wdt = sfcSfc->Wdt, hgt = sfcSfc->Hgt;
1247 // Lock surface
1248 if (!sfcSfc->Lock()) return;
1249 for (ycnt = 0; ycnt < hgt; ycnt++)
1250 {
1251 for (xcnt = 0; xcnt < wdt; xcnt++)
1252 {
1253 uint32_t dwColor = sfcSfc->GetPixDw(iX: xcnt, iY: ycnt, fApplyModulation: false);
1254 uint32_t r = GetRValue(dwColor), g = GetGValue(dwColor), b = GetBValue(dwColor), a = dwColor >> 24;
1255 int32_t gray = BoundBy<int32_t>(bval: (r + g + b) / 3 + iOffset, lbound: 0, rbound: 255);
1256 sfcSfc->SetPixDw(iX: xcnt, iY: ycnt, dwCol: RGBA(r: gray, g: gray, b: gray, a));
1257 }
1258 }
1259 sfcSfc->Unlock();
1260}
1261
1262bool CStdDDraw::GetPrimaryClipper(int &rX1, int &rY1, int &rX2, int &rY2)
1263{
1264 // Store drawing clip values
1265 rX1 = ClipX1; rY1 = ClipY1; rX2 = ClipX2; rY2 = ClipY2;
1266 // Done
1267 return true;
1268}
1269
1270void CStdDDraw::SetGamma(uint32_t dwClr1, uint32_t dwClr2, uint32_t dwClr3)
1271{
1272 // calc ramp
1273 Gamma.Set(dwClr1, dwClr2, dwClr3, nsize: Gamma.size, ref: &DefRamp);
1274 // set it
1275 ApplyGammaRamp(ramp&: Gamma, fForce: false);
1276}
1277
1278void CStdDDraw::DisableGamma()
1279{
1280 // set it
1281 ApplyGammaRamp(ramp&: DefRamp, fForce: true);
1282}
1283
1284void CStdDDraw::EnableGamma()
1285{
1286 // set it
1287 ApplyGammaRamp(ramp&: Gamma, fForce: false);
1288}
1289
1290uint32_t CStdDDraw::ApplyGammaTo(uint32_t dwClr)
1291{
1292 return Gamma.ApplyTo(dwClr);
1293}
1294
1295void CStdDDraw::SetBlitMode(uint32_t dwBlitMode)
1296{
1297 this->dwBlitMode = dwBlitMode & Config.Graphics.AllowedBlitModes;
1298}
1299
1300CStdDDraw *DDrawInit(CStdApp *pApp, C4LogSystem &logSystem, int Engine)
1301{
1302 // create engine
1303 switch (iGfxEngine = Engine)
1304 {
1305 default: // Use the first engine possible if none selected
1306#ifndef USE_CONSOLE
1307 case GFXENGN_OPENGL: lpDDraw = new CStdGL(); break;
1308#endif
1309 case GFXENGN_NOGFX: lpDDraw = new CStdNoGfx(); break;
1310 }
1311 if (!lpDDraw) return nullptr;
1312 // init it
1313 if (!lpDDraw->Init(pApp, logSystem))
1314 {
1315 delete lpDDraw;
1316 return nullptr;
1317 }
1318 // done, success
1319 return lpDDraw;
1320}
1321
1322bool CStdDDraw::Init(CStdApp *pApp, C4LogSystem &logSystem)
1323{
1324 this->pApp = pApp;
1325 logger = logSystem.CreateLoggerWithDifferentName(config&: Config.Logging.DDraw, name: std::string{GetEngineName()});
1326
1327 logger->debug(msg: "Init DDraw");
1328 logger->debug(msg: "Create DirectDraw...");
1329
1330 if (!CreateDirectDraw())
1331 {
1332 logger->error(msg: "CreateDirectDraw failure.");
1333 return false;
1334 }
1335
1336 logger->debug(msg: "Create Device...");
1337
1338 if (!CreatePrimarySurfaces())
1339 {
1340 logger->error(msg: "CreateDevice failure.");
1341 return false;
1342 }
1343
1344 logger->debug(msg: "Create Clipper");
1345
1346 if (!CreatePrimaryClipper())
1347 {
1348 logger->error(msg: "Clipper failure");
1349 return false;
1350 }
1351
1352 // store default gamma
1353 SaveDefaultGammaRamp(pWindow: pApp->pWindow);
1354
1355 return true;
1356}
1357
1358void CStdDDraw::DrawBoxFade(C4Surface *sfcDest, int iX, int iY, int iWdt, int iHgt, uint32_t dwClr1, uint32_t dwClr2, uint32_t dwClr3, uint32_t dwClr4, int iBoxOffX, int iBoxOffY)
1359{
1360 // clipping not performed - this fn should be called for clipped rects only
1361 // apply modulation map: Must sectionize blit
1362 if (fUseClrModMap)
1363 {
1364 int iModResX = pClrModMap ? pClrModMap->GetResolutionX() : CClrModAddMap::iDefResolutionX;
1365 int iModResY = pClrModMap ? pClrModMap->GetResolutionY() : CClrModAddMap::iDefResolutionY;
1366 iBoxOffX %= iModResX;
1367 iBoxOffY %= iModResY;
1368 if (iWdt + iBoxOffX > iModResX || iHgt + iBoxOffY > iModResY)
1369 {
1370 if (iWdt <= 0 || iHgt <= 0) return;
1371 CColorFadeMatrix clrs(iX, iY, iWdt, iHgt, dwClr1, dwClr2, dwClr3, dwClr4);
1372 int iMaxH = iModResY - iBoxOffY;
1373 int w, h;
1374 for (int y = iY, H = iHgt; H > 0; (y += h), (H -= h), (iMaxH = iModResY))
1375 {
1376 h = std::min<int>(a: H, b: iMaxH);
1377 int iMaxW = iModResX - iBoxOffX;
1378 for (int x = iX, W = iWdt; W > 0; (x += w), (W -= w), (iMaxW = iModResX))
1379 {
1380 w = std::min<int>(a: W, b: iMaxW);
1381 DrawBoxFade(sfcDest, iX: x, iY: y, iWdt: w, iHgt: h, dwClr1: clrs.GetColorAt(iX: x, iY: y), dwClr2: clrs.GetColorAt(iX: x + w, iY: y), dwClr3: clrs.GetColorAt(iX: x, iY: y + h), dwClr4: clrs.GetColorAt(iX: x + w, iY: y + h), iBoxOffX: 0, iBoxOffY: 0);
1382 }
1383 }
1384 return;
1385 }
1386 }
1387 // set vertex buffer data
1388 // vertex order:
1389 // 0=upper left dwClr1
1390 // 1=lower left dwClr3
1391 // 2=lower right dwClr4
1392 // 3=upper right dwClr2
1393 int vtx[8];
1394 vtx[0] = iX; vtx[1] = iY;
1395 vtx[2] = iX; vtx[3] = iY + iHgt;
1396 vtx[4] = iX + iWdt; vtx[5] = iY + iHgt;
1397 vtx[6] = iX + iWdt; vtx[7] = iY;
1398 DrawQuadDw(sfcTarget: sfcDest, ipVtx: vtx, dwClr1, dwClr2: dwClr3, dwClr3: dwClr4, dwClr4: dwClr2);
1399}
1400
1401void CStdDDraw::DrawBoxDw(C4Surface *sfcDest, int iX1, int iY1, int iX2, int iY2, uint32_t dwClr)
1402{
1403 DrawBoxFade(sfcDest, iX: iX1, iY: iY1, iWdt: iX2 - iX1 + 1, iHgt: iY2 - iY1 + 1, dwClr1: dwClr, dwClr2: dwClr, dwClr3: dwClr, dwClr4: dwClr, iBoxOffX: 0, iBoxOffY: 0);
1404}
1405