| 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 |
| 37 | CStdDDraw *lpDDraw = nullptr; |
| 38 | CStdPalette *lpDDrawPal = nullptr; |
| 39 | int iGfxEngine = -1; |
| 40 | |
| 41 | void 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 | |
| 62 | bool 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 | |
| 81 | void 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 | |
| 91 | CPattern &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 | |
| 120 | bool 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 | |
| 144 | bool 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 | |
| 162 | CPattern::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 | |
| 173 | void 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 | |
| 187 | bool 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 | |
| 232 | CGammaControl::~CGammaControl() |
| 233 | { |
| 234 | delete[] red; |
| 235 | } |
| 236 | |
| 237 | void 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 | |
| 273 | void 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 | |
| 289 | int CGammaControl::GetSize() const |
| 290 | { |
| 291 | return size; |
| 292 | } |
| 293 | |
| 294 | uint32_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 | |
| 300 | void 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 | |
| 336 | void 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 | |
| 362 | void 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 | |
| 388 | uint32_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 | |
| 401 | CColorFadeMatrix::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 | |
| 418 | uint32_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 | |
| 431 | void CStdShader::SetMacro(const std::string &key, const std::string &value) |
| 432 | { |
| 433 | macros[key] = value; |
| 434 | } |
| 435 | |
| 436 | void CStdShader::UnsetMacro(const std::string &key) |
| 437 | { |
| 438 | macros.erase(x: key); |
| 439 | } |
| 440 | |
| 441 | void CStdShader::SetSource(const std::string &source) |
| 442 | { |
| 443 | this->source = source; |
| 444 | } |
| 445 | |
| 446 | void CStdShader::SetType(Type type) |
| 447 | { |
| 448 | this->type = type; |
| 449 | } |
| 450 | |
| 451 | void CStdShader::Clear() |
| 452 | { |
| 453 | source.clear(); |
| 454 | macros.clear(); |
| 455 | } |
| 456 | |
| 457 | CStdShaderProgram *CStdShaderProgram::currentShaderProgram = nullptr; |
| 458 | |
| 459 | bool 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 | |
| 476 | void CStdShaderProgram::Clear() |
| 477 | { |
| 478 | shaders.clear(); |
| 479 | } |
| 480 | |
| 481 | void CStdShaderProgram::Select() |
| 482 | { |
| 483 | if (currentShaderProgram != this) |
| 484 | { |
| 485 | OnSelect(); |
| 486 | currentShaderProgram = this; |
| 487 | } |
| 488 | } |
| 489 | |
| 490 | void CStdShaderProgram::Deselect() |
| 491 | { |
| 492 | if (currentShaderProgram) |
| 493 | { |
| 494 | currentShaderProgram->OnDeselect(); |
| 495 | currentShaderProgram = nullptr; |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | CStdShaderProgram *CStdShaderProgram::GetCurrentShaderProgram() |
| 500 | { |
| 501 | return currentShaderProgram; |
| 502 | } |
| 503 | |
| 504 | void 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 | |
| 517 | void CStdDDraw::Clear() |
| 518 | { |
| 519 | DisableGamma(); |
| 520 | Active = BlitModulated = fUseClrModMap = false; |
| 521 | dwBlitMode = 0; |
| 522 | } |
| 523 | |
| 524 | bool CStdDDraw::WipeSurface(C4Surface *sfcSurface) |
| 525 | { |
| 526 | if (!sfcSurface) return false; |
| 527 | return sfcSurface->Wipe(); |
| 528 | } |
| 529 | |
| 530 | bool CStdDDraw::GetSurfaceSize(C4Surface *sfcSurface, int &iWdt, int &iHgt) |
| 531 | { |
| 532 | return sfcSurface->GetSurfaceSize(irX&: iWdt, irY&: iHgt); |
| 533 | } |
| 534 | |
| 535 | bool 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 | |
| 545 | bool 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 | |
| 552 | bool CStdDDraw::StorePrimaryClipper() |
| 553 | { |
| 554 | // Store current primary clipper |
| 555 | StClipX1 = ClipX1; StClipY1 = ClipY1; StClipX2 = ClipX2; StClipY2 = ClipY2; |
| 556 | return true; |
| 557 | } |
| 558 | |
| 559 | bool CStdDDraw::RestorePrimaryClipper() |
| 560 | { |
| 561 | // Restore primary clipper |
| 562 | SetPrimaryClipper(iX1: StClipX1, iY1: StClipY1, iX2: StClipX2, iY2: StClipY2); |
| 563 | return true; |
| 564 | } |
| 565 | |
| 566 | bool 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 | |
| 575 | bool CStdDDraw::NoPrimaryClipper() |
| 576 | { |
| 577 | // apply maximum clipper |
| 578 | SetPrimaryClipper(iX1: 0, iY1: 0, iX2: 439832, iY2: 439832); |
| 579 | // Done |
| 580 | return true; |
| 581 | } |
| 582 | |
| 583 | bool 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 | |
| 602 | void 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 | |
| 608 | void 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 | |
| 630 | bool 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 | |
| 637 | bool 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 | |
| 789 | bool 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 | |
| 836 | bool 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 | |
| 931 | bool 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 | |
| 939 | bool CStdDDraw::AttachPrimaryPalette(C4Surface *sfcSurface) |
| 940 | { |
| 941 | return true; |
| 942 | } |
| 943 | |
| 944 | bool 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 | |
| 962 | bool 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 | |
| 993 | bool 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 | |
| 1035 | bool 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 | |
| 1044 | bool 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 | |
| 1052 | bool 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 | |
| 1072 | void 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 | |
| 1083 | void 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 | |
| 1113 | void 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 | |
| 1143 | void 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 | |
| 1173 | void 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 | |
| 1181 | void 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 | |
| 1191 | void 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 | |
| 1210 | void 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 | |
| 1241 | void 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 | |
| 1262 | bool 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 | |
| 1270 | void 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 | |
| 1278 | void CStdDDraw::DisableGamma() |
| 1279 | { |
| 1280 | // set it |
| 1281 | ApplyGammaRamp(ramp&: DefRamp, fForce: true); |
| 1282 | } |
| 1283 | |
| 1284 | void CStdDDraw::EnableGamma() |
| 1285 | { |
| 1286 | // set it |
| 1287 | ApplyGammaRamp(ramp&: Gamma, fForce: false); |
| 1288 | } |
| 1289 | |
| 1290 | uint32_t CStdDDraw::ApplyGammaTo(uint32_t dwClr) |
| 1291 | { |
| 1292 | return Gamma.ApplyTo(dwClr); |
| 1293 | } |
| 1294 | |
| 1295 | void CStdDDraw::SetBlitMode(uint32_t dwBlitMode) |
| 1296 | { |
| 1297 | this->dwBlitMode = dwBlitMode & Config.Graphics.AllowedBlitModes; |
| 1298 | } |
| 1299 | |
| 1300 | CStdDDraw *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 | |
| 1322 | bool 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 | |
| 1358 | void 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 | |
| 1401 | void 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 | |