| 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 | /* A wrapper class to DirectDraw */ |
| 19 | |
| 20 | #pragma once |
| 21 | |
| 22 | #include "C4Log.h" |
| 23 | #include <C4Surface.h> |
| 24 | #include <StdSurface8.h> |
| 25 | #include <StdBuf.h> |
| 26 | |
| 27 | #include <array> |
| 28 | #include <unordered_map> |
| 29 | #include <vector> |
| 30 | |
| 31 | // texref-predef |
| 32 | class CStdDDraw; |
| 33 | class C4TexRef; |
| 34 | class C4Surface; |
| 35 | struct CStdPalette; |
| 36 | class CStdGLCtx; |
| 37 | class CStdApp; |
| 38 | class CStdWindow; |
| 39 | class CStdFont; |
| 40 | class CMarkup; |
| 41 | |
| 42 | // engines |
| 43 | #define GFXENGN_OPENGL 0 |
| 44 | #define GFXENGN_NOGFX 3 |
| 45 | |
| 46 | // Global DDraw access pointer |
| 47 | extern CStdDDraw *lpDDraw; |
| 48 | extern CStdPalette *lpDDrawPal; |
| 49 | |
| 50 | extern int iGfxEngine; |
| 51 | |
| 52 | // rotation info class |
| 53 | class CBltTransform |
| 54 | { |
| 55 | public: |
| 56 | float mat[9]; // transformation matrix |
| 57 | |
| 58 | public: |
| 59 | CBltTransform() {} // default: don't init fields |
| 60 | |
| 61 | void Set(float fA, float fB, float fC, float fD, float fE, float fF, float fG, float fH, float fI) |
| 62 | { |
| 63 | mat[0] = fA; mat[1] = fB; mat[2] = fC; mat[3] = fD; mat[4] = fE; mat[5] = fF; mat[6] = fG; mat[7] = fH; mat[8] = fI; |
| 64 | } |
| 65 | |
| 66 | void SetRotate(int iAngle, float fOffX, float fOffY); // set by angle and rotation offset |
| 67 | bool SetAsInv(CBltTransform &rOfTransform); |
| 68 | |
| 69 | void Rotate(int iAngle, float fOffX, float fOffY) // rotate by angle around rotation offset |
| 70 | { |
| 71 | // multiply matrix as seen in SetRotate by own matrix |
| 72 | CBltTransform rot; rot.SetRotate(iAngle, fOffX, fOffY); |
| 73 | (*this) *= rot; |
| 74 | } |
| 75 | |
| 76 | void SetMoveScale(float dx, float dy, float sx, float sy) |
| 77 | { |
| 78 | mat[0] = sx; mat[1] = 0; mat[2] = dx; |
| 79 | mat[3] = 0; mat[4] = sy; mat[5] = dy; |
| 80 | mat[6] = 0; mat[7] = 0; mat[8] = 1; |
| 81 | } |
| 82 | |
| 83 | void MoveScale(float dx, float dy, float sx, float sy) |
| 84 | { |
| 85 | // multiply matrix by movescale matrix |
| 86 | CBltTransform move; move.SetMoveScale(dx, dy, sx, sy); |
| 87 | (*this) *= move; |
| 88 | } |
| 89 | |
| 90 | void ScaleAt(float sx, float sy, float tx, float ty) |
| 91 | { |
| 92 | // scale and move back so tx and ty remain fixpoints |
| 93 | MoveScale(dx: -tx * (sx - 1), dy: -ty * (sy - 1), sx, sy); |
| 94 | } |
| 95 | |
| 96 | CBltTransform &operator*=(CBltTransform &r) |
| 97 | { |
| 98 | // transform transformation |
| 99 | Set( |
| 100 | fA: mat[0] * r.mat[0] + mat[3] * r.mat[1] + mat[6] * r.mat[2], |
| 101 | fB: mat[1] * r.mat[0] + mat[4] * r.mat[1] + mat[7] * r.mat[2], |
| 102 | fC: mat[2] * r.mat[0] + mat[5] * r.mat[1] + mat[8] * r.mat[2], |
| 103 | fD: mat[0] * r.mat[3] + mat[3] * r.mat[4] + mat[6] * r.mat[5], |
| 104 | fE: mat[1] * r.mat[3] + mat[4] * r.mat[4] + mat[7] * r.mat[5], |
| 105 | fF: mat[2] * r.mat[3] + mat[5] * r.mat[4] + mat[8] * r.mat[5], |
| 106 | fG: mat[0] * r.mat[6] + mat[3] * r.mat[7] + mat[6] * r.mat[8], |
| 107 | fH: mat[1] * r.mat[6] + mat[4] * r.mat[7] + mat[7] * r.mat[8], |
| 108 | fI: mat[2] * r.mat[6] + mat[5] * r.mat[7] + mat[8] * r.mat[8]); |
| 109 | return *this; |
| 110 | } |
| 111 | |
| 112 | void TransformPoint(float &rX, float &rY); // rotate point by angle |
| 113 | }; |
| 114 | |
| 115 | // pattern |
| 116 | class CPattern |
| 117 | { |
| 118 | private: |
| 119 | // pattern surface for new-style patterns |
| 120 | class C4Surface *sfcPattern32; |
| 121 | class CSurface8 *sfcPattern8; |
| 122 | // Faster access |
| 123 | uint32_t *CachedPattern; int Wdt; int Hgt; |
| 124 | // pattern zoom factor; 0 means no zoom |
| 125 | int Zoom; |
| 126 | // pattern is to be applied monochromatic |
| 127 | bool Monochrome; |
| 128 | // color array for old-style patterns |
| 129 | uint32_t *pClrs; |
| 130 | // alpha array for old-style patterns |
| 131 | uint32_t *pAlpha; |
| 132 | |
| 133 | public: |
| 134 | CPattern &operator=(const CPattern &); |
| 135 | bool PatternClr(int iX, int iY, uint8_t &byClr, uint32_t &dwClr, CStdPalette &rPal) const; // apply pattern to color |
| 136 | bool Set(class C4Surface *sfcSource, int iZoom = 0, bool fMonochrome = false); // set and enable pattern |
| 137 | bool Set(class CSurface8 *sfcSource, int iZoom = 0, bool fMonochrome = false); // set and enable pattern |
| 138 | void SetColors(uint32_t *pClrs, uint32_t *pAlpha) { this->pClrs = pClrs; this->pAlpha = pAlpha; } // set color triplet for old-style textures |
| 139 | void SetZoom(int iZoom) { Zoom = iZoom; } |
| 140 | void Clear(); // clear pattern |
| 141 | CPattern(); |
| 142 | ~CPattern() { Clear(); } |
| 143 | }; |
| 144 | |
| 145 | // blit position on screen |
| 146 | struct CBltVertex |
| 147 | { |
| 148 | float ftx, fty; // blit positions |
| 149 | uint32_t dwModClr; // color modulation |
| 150 | }; |
| 151 | |
| 152 | // blit data |
| 153 | struct CBltData |
| 154 | { |
| 155 | std::array<CBltVertex, 4> vtVtx; // vertices for polygon |
| 156 | CBltTransform TexPos; // texture mapping matrix |
| 157 | CBltTransform *pTransform; // Vertex transformation |
| 158 | }; |
| 159 | |
| 160 | // gamma ramp control |
| 161 | class CGammaControl |
| 162 | { |
| 163 | private: |
| 164 | void SetClrChannel(uint16_t *pBuf, uint8_t c1, uint8_t c2, int c3, uint16_t *ref); // set color channel ramp |
| 165 | |
| 166 | protected: |
| 167 | uint16_t *red, *green, *blue; int size; |
| 168 | |
| 169 | public: |
| 170 | CGammaControl() : red(nullptr), green(nullptr), blue(nullptr), size(0) { Default(); } |
| 171 | ~CGammaControl(); |
| 172 | void Default() { Set(dwClr1: 0x000000, dwClr2: 0x808080, dwClr3: 0xffffff, size: 256, ref: nullptr); } // set default ramp |
| 173 | |
| 174 | void Set(uint32_t dwClr1, uint32_t dwClr2, uint32_t dwClr3, int size, CGammaControl *ref); // set color ramp |
| 175 | int GetSize() const; |
| 176 | |
| 177 | uint32_t ApplyTo(uint32_t dwClr); // apply gamma to color value |
| 178 | |
| 179 | friend class CStdDDraw; |
| 180 | friend class CStdGL; |
| 181 | }; |
| 182 | |
| 183 | class CStdRenderException : public std::runtime_error |
| 184 | { |
| 185 | public: |
| 186 | using std::runtime_error::runtime_error; |
| 187 | }; |
| 188 | |
| 189 | class CStdShader |
| 190 | { |
| 191 | public: |
| 192 | enum class Type : uint8_t |
| 193 | { |
| 194 | Vertex, |
| 195 | TesselationControl, |
| 196 | TesselationEvaluation, |
| 197 | Geometry, |
| 198 | Fragment |
| 199 | }; |
| 200 | |
| 201 | class Exception : public CStdRenderException |
| 202 | { |
| 203 | public: |
| 204 | using CStdRenderException::CStdRenderException; |
| 205 | }; |
| 206 | |
| 207 | public: |
| 208 | CStdShader() = default; |
| 209 | explicit CStdShader(Type type, const std::string &source) : type{type}, source{source} {} |
| 210 | CStdShader(const CStdShader &) = delete; |
| 211 | |
| 212 | virtual ~CStdShader() { Clear(); } |
| 213 | |
| 214 | void SetMacro(const std::string &key, const std::string &value); |
| 215 | void UnsetMacro(const std::string &key); |
| 216 | void SetSource(const std::string &source); |
| 217 | void SetType(Type type); |
| 218 | |
| 219 | virtual void Compile() = 0; |
| 220 | virtual void Clear(); |
| 221 | |
| 222 | std::string GetSource() const { return source; } |
| 223 | virtual int64_t GetHandle() const = 0; |
| 224 | std::unordered_map<std::string, std::string> GetMacros() const { return macros; } |
| 225 | virtual Type GetType() const { return type; } |
| 226 | |
| 227 | protected: |
| 228 | Type type; |
| 229 | std::string source; |
| 230 | std::unordered_map<std::string, std::string> macros; |
| 231 | }; |
| 232 | |
| 233 | class CStdShaderProgram |
| 234 | { |
| 235 | public: |
| 236 | class Exception : public CStdRenderException |
| 237 | { |
| 238 | public: |
| 239 | using CStdRenderException::CStdRenderException; |
| 240 | }; |
| 241 | |
| 242 | public: |
| 243 | CStdShaderProgram() = default; |
| 244 | CStdShaderProgram(const CStdShaderProgram &) = delete; |
| 245 | virtual ~CStdShaderProgram() { Clear(); } |
| 246 | |
| 247 | virtual explicit operator bool() const = 0; |
| 248 | |
| 249 | bool AddShader(CStdShader *shader); |
| 250 | |
| 251 | virtual void Link() = 0; |
| 252 | virtual void Validate() = 0; |
| 253 | void Select(); |
| 254 | static void Deselect(); |
| 255 | virtual void Clear(); |
| 256 | |
| 257 | virtual void EnsureProgram() = 0; |
| 258 | virtual int64_t GetProgram() const = 0; |
| 259 | |
| 260 | std::vector<CStdShader *> GetPendingShaders() const { return shaders; } |
| 261 | static CStdShaderProgram *GetCurrentShaderProgram(); |
| 262 | |
| 263 | protected: |
| 264 | virtual bool AddShaderInt(CStdShader *shader) = 0; |
| 265 | virtual void OnSelect() = 0; |
| 266 | virtual void OnDeselect() = 0; |
| 267 | |
| 268 | protected: |
| 269 | std::vector<CStdShader *> shaders; |
| 270 | static CStdShaderProgram *currentShaderProgram; |
| 271 | }; |
| 272 | |
| 273 | // direct draw encapsulation |
| 274 | class CStdDDraw |
| 275 | { |
| 276 | public: |
| 277 | CStdDDraw() { lpDDrawPal = &Pal; } |
| 278 | virtual ~CStdDDraw() { lpDDraw = nullptr; } |
| 279 | |
| 280 | public: |
| 281 | CStdApp *pApp; // the application |
| 282 | C4Surface *lpPrimary; // primary and back surface (emulation...) |
| 283 | C4Surface *lpBack; |
| 284 | CStdPalette Pal; // 8bit-pal |
| 285 | bool Active; // set if device is ready to render, etc. |
| 286 | CGammaControl Gamma; // gamma |
| 287 | CGammaControl DefRamp; // default gamma ramp |
| 288 | |
| 289 | protected: |
| 290 | std::shared_ptr<spdlog::logger> logger; |
| 291 | int ClipX1, ClipY1, ClipX2, ClipY2; |
| 292 | int StClipX1, StClipY1, StClipX2, StClipY2; |
| 293 | bool ClipAll; // set if clipper clips everything away |
| 294 | C4Surface *RenderTarget; // current render target |
| 295 | bool BlitModulated; // set if blits should be modulated with BlitModulateClr |
| 296 | uint32_t BlitModulateClr; // modulation color for blitting |
| 297 | uint32_t dwBlitMode; // extra flags for blit |
| 298 | CClrModAddMap *pClrModMap; // map to be used for global color modulation (invalid if !fUseClrModMap) |
| 299 | bool fUseClrModMap; // if set, pClrModMap will be checked for color modulations |
| 300 | float texIndent; |
| 301 | float blitOffset; |
| 302 | |
| 303 | public: |
| 304 | // General |
| 305 | bool Init(CStdApp *pApp, C4LogSystem &logSystem); |
| 306 | virtual void Clear(); |
| 307 | virtual void Default(); |
| 308 | virtual CStdGLCtx *CreateContext(CStdWindow *, CStdApp *) { return nullptr; } |
| 309 | #ifdef _WIN32 |
| 310 | virtual CStdGLCtx *CreateContext(HWND, CStdApp *) { return nullptr; } |
| 311 | #endif |
| 312 | virtual void PageFlip() = 0; |
| 313 | virtual int GetEngine() = 0; // get indexed engine |
| 314 | virtual std::string_view GetEngineName() const = 0; |
| 315 | virtual bool OnResolutionChanged() = 0; // reinit window for new resolution |
| 316 | |
| 317 | // Palette |
| 318 | bool SetPrimaryPalette(uint8_t *pBuf, uint8_t *pAlphaBuf = nullptr); |
| 319 | bool AttachPrimaryPalette(C4Surface *sfcSurface); |
| 320 | |
| 321 | // Clipper |
| 322 | bool GetPrimaryClipper(int &rX1, int &rY1, int &rX2, int &rY2); |
| 323 | bool SetPrimaryClipper(int iX1, int iY1, int iX2, int iY2); |
| 324 | bool SubPrimaryClipper(int iX1, int iY1, int iX2, int iY2); |
| 325 | bool StorePrimaryClipper(); |
| 326 | bool RestorePrimaryClipper(); |
| 327 | bool NoPrimaryClipper(); |
| 328 | virtual bool UpdateClipper() = 0; // set current clipper to render target |
| 329 | |
| 330 | // Surface |
| 331 | bool GetSurfaceSize(C4Surface *sfcSurface, int &iWdt, int &iHgt); |
| 332 | bool WipeSurface(C4Surface *sfcSurface); |
| 333 | void SurfaceAllowColor(C4Surface *sfcSfc, uint32_t *pdwColors, int iNumColors, bool fAllowZero = false); |
| 334 | void Grayscale(C4Surface *sfcSfc, int32_t iOffset = 0); |
| 335 | virtual bool PrepareRendering(C4Surface *sfcToSurface) = 0; // check if/make rendering possible to given surface |
| 336 | |
| 337 | // Blit |
| 338 | virtual void BlitLandscape(C4Surface *sfcSource, C4Surface *sfcSource2, C4Surface *sfcLiquidAnimation, int fx, int fy, |
| 339 | C4Surface *sfcTarget, int tx, int ty, int wdt, int hgt); |
| 340 | void Blit8Fast(CSurface8 *sfcSource, int fx, int fy, |
| 341 | C4Surface *sfcTarget, int tx, int ty, int wdt, int hgt); |
| 342 | bool Blit(C4Surface *sfcSource, float fx, float fy, float fwdt, float fhgt, |
| 343 | C4Surface *sfcTarget, int tx, int ty, int twdt, int thgt, |
| 344 | bool fSrcColKey = false, CBltTransform *pTransform = nullptr, bool noScalingCorrection = false); |
| 345 | bool Blit(C4Surface *sfcSource, float fx, float fy, float fwdt, float fhgt, |
| 346 | C4Surface *sfcTarget, float tx, float ty, float twdt, float thgt, |
| 347 | bool fSrcColKey = false, CBltTransform *pTransform = nullptr, bool noScalingCorrection = false); |
| 348 | virtual void PerformBlt(CBltData &rBltData, C4TexRef *pTex, uint32_t dwModClr, bool fMod2, bool fExact) = 0; |
| 349 | bool Blit8(C4Surface *sfcSource, int fx, int fy, int fwdt, int fhgt, // force 8bit-blit (inline) |
| 350 | C4Surface *sfcTarget, int tx, int ty, int twdt, int thgt, |
| 351 | bool fSrcColKey = false, CBltTransform *pTransform = nullptr); |
| 352 | bool BlitRotate(C4Surface *sfcSource, int fx, int fy, int fwdt, int fhgt, |
| 353 | C4Surface *sfcTarget, int tx, int ty, int twdt, int thgt, |
| 354 | int iAngle, bool fTransparency = true); |
| 355 | bool BlitSurface(C4Surface *sfcSurface, C4Surface *sfcTarget, int tx, int ty, bool fBlitBase); |
| 356 | bool BlitSurfaceTile(C4Surface *sfcSurface, C4Surface *sfcTarget, int iToX, int iToY, int iToWdt, int iToHgt, int iOffsetX = 0, int iOffsetY = 0, bool fSrcColKey = false, float scale = 1.f); |
| 357 | bool BlitSurfaceTile2(C4Surface *sfcSurface, C4Surface *sfcTarget, int iToX, int iToY, int iToWdt, int iToHgt, int iOffsetX = 0, int iOffsetY = 0, bool fSrcColKey = false); |
| 358 | virtual void FillBG(uint32_t dwClr = 0) = 0; |
| 359 | |
| 360 | // Text |
| 361 | enum { DEFAULT_MESSAGE_COLOR = 0xffffffff }; |
| 362 | bool TextOut(const char *szText, CStdFont &rFont, float fZoom, C4Surface *sfcDest, int iTx, int iTy, uint32_t dwFCol = 0xffffffff, uint8_t byForm = ALeft, bool fDoMarkup = true); |
| 363 | bool StringOut(const char *szText, CStdFont &rFont, float fZoom, C4Surface *sfcDest, int iTx, int iTy, uint32_t dwFCol = 0xffffffff, uint8_t byForm = ALeft, bool fDoMarkup = true); |
| 364 | |
| 365 | // Drawing |
| 366 | virtual void DrawPix(C4Surface *sfcDest, float tx, float ty, uint32_t dwCol); |
| 367 | void DrawBox(C4Surface *sfcDest, int iX1, int iY1, int iX2, int iY2, uint8_t byCol); // calls DrawBoxDw |
| 368 | void DrawBoxDw(C4Surface *sfcDest, int iX1, int iY1, int iX2, int iY2, uint32_t dwClr); // calls DrawBoxFade |
| 369 | void 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); // calls DrawQuadDw |
| 370 | void DrawPatternedCircle(C4Surface *sfcDest, int x, int y, int r, uint8_t col, CPattern &Pattern1, CPattern &Pattern2, CStdPalette &rPal); |
| 371 | void DrawHorizontalLine(C4Surface *sfcDest, int x1, int x2, int y, uint8_t col); |
| 372 | void DrawVerticalLine(C4Surface *sfcDest, int x, int y1, int y2, uint8_t col); |
| 373 | void DrawFrame(C4Surface *sfcDest, int x1, int y1, int x2, int y2, uint8_t col); |
| 374 | void DrawFrameDw(C4Surface *sfcDest, int x1, int y1, int x2, int y2, uint32_t dwClr); |
| 375 | |
| 376 | virtual void DrawLine(C4Surface *sfcTarget, int x1, int y1, int x2, int y2, uint8_t byCol) |
| 377 | { |
| 378 | DrawLineDw(sfcTarget, x1: static_cast<float>(x1), y1: static_cast<float>(y1), x2: static_cast<float>(x2), y2: static_cast<float>(y2), dwClr: Pal.GetClr(byCol)); |
| 379 | } |
| 380 | |
| 381 | virtual void DrawLineDw(C4Surface *sfcTarget, float x1, float y1, float x2, float y2, uint32_t dwClr) = 0; |
| 382 | virtual void DrawQuadDw(C4Surface *sfcTarget, int *ipVtx, uint32_t dwClr1, uint32_t dwClr2, uint32_t dwClr3, uint32_t dwClr4) = 0; |
| 383 | |
| 384 | // gamma |
| 385 | void SetGamma(uint32_t dwClr1, uint32_t dwClr2, uint32_t dwClr3); // set gamma ramp |
| 386 | virtual void DisableGamma(); // reset gamma ramp to default |
| 387 | virtual void EnableGamma(); // set current gamma ramp |
| 388 | uint32_t ApplyGammaTo(uint32_t dwClr); // apply gamma to given color |
| 389 | virtual bool ApplyGammaRamp(CGammaControl &ramp, bool fForce) = 0; // really apply gamma ramp |
| 390 | virtual bool SaveDefaultGammaRamp(CStdWindow *pWindow) = 0; |
| 391 | |
| 392 | // blit states |
| 393 | void ActivateBlitModulation(uint32_t dwWithClr) { BlitModulated = true; BlitModulateClr = dwWithClr; } // modulate following blits with a given color |
| 394 | void DeactivateBlitModulation() { BlitModulated = false; } // stop color modulation of blits |
| 395 | bool GetBlitModulation(uint32_t &rdwColor) { rdwColor = BlitModulateClr; return BlitModulated; } |
| 396 | void SetBlitMode(uint32_t dwBlitMode); // set blit mode extra flags (additive blits, mod2-modulation, etc.) |
| 397 | void ResetBlitMode() { dwBlitMode = 0; } |
| 398 | |
| 399 | void ClrByCurrentBlitMod(uint32_t &rdwClr) |
| 400 | { |
| 401 | // apply modulation if activated |
| 402 | if (BlitModulated) ModulateClr(dst&: rdwClr, src: BlitModulateClr); |
| 403 | } |
| 404 | |
| 405 | void SetClrModMap(CClrModAddMap *pClrModMap) { this->pClrModMap = pClrModMap; } |
| 406 | void SetClrModMapEnabled(bool fToVal) { fUseClrModMap = fToVal; } |
| 407 | bool GetClrModMapEnabled() const { return fUseClrModMap; } |
| 408 | virtual void SetTexture() = 0; |
| 409 | virtual void ResetTexture() = 0; |
| 410 | |
| 411 | // device objects |
| 412 | virtual bool RestoreDeviceObjects() = 0; // init/restore device dependent objects |
| 413 | virtual bool InvalidateDeviceObjects() = 0; // free device dependent objects |
| 414 | virtual bool DeviceReady() = 0; // return whether device exists |
| 415 | |
| 416 | protected: |
| 417 | bool StringOut(const char *szText, C4Surface *sfcDest, int iTx, int iTy, uint32_t dwFCol, uint8_t byForm, bool fDoMarkup, CMarkup &Markup, CStdFont *pFont, float fZoom); |
| 418 | virtual void DrawPixInt(C4Surface *sfcDest, float tx, float ty, uint32_t dwCol) = 0; // without ClrModMap |
| 419 | bool CreatePrimaryClipper(); |
| 420 | virtual bool CreatePrimarySurfaces() = 0; |
| 421 | virtual bool CreateDirectDraw() = 0; |
| 422 | bool CalculateClipper(int *iX, int *iY, int *iWdt, int *iHgt); |
| 423 | |
| 424 | friend class C4Surface; |
| 425 | friend class C4TexRef; |
| 426 | friend class CPattern; |
| 427 | }; |
| 428 | |
| 429 | C4LOGGERCONFIG_NAME_TYPE(CStdDDraw); |
| 430 | |
| 431 | CStdDDraw *DDrawInit(CStdApp *pApp, C4LogSystem &logSystem, int Engine); |
| 432 | |