1 /++
2  + Authors: Avaxar <avaxar@nekkl.org>
3  + Copyright: Copyright © 2023, Avaxar
4  + License: $(LINK2 https://mit-license.org, MIT License)
5  +/
6 
7 module dsdl2.renderer;
8 @safe:
9 
10 import bindbc.sdl;
11 import dsdl2.sdl;
12 import dsdl2.blend;
13 import dsdl2.rect;
14 import dsdl2.frect;
15 import dsdl2.pixels;
16 import dsdl2.surface;
17 import dsdl2.texture;
18 import dsdl2.window;
19 
20 import core.memory : GC;
21 import std.bitmanip : bitfields;
22 import std.conv : to;
23 import std.format : format;
24 import std.string : toStringz;
25 import std.typecons : Nullable, nullable;
26 
27 private uint toSDLRendererFlags(bool software, bool accelerated, bool presentVSync, bool targetTexture) {
28     uint flags = 0;
29 
30     flags |= software ? SDL_RENDERER_SOFTWARE : 0;
31     flags |= accelerated ? SDL_RENDERER_ACCELERATED : 0;
32     flags |= presentVSync ? SDL_RENDERER_PRESENTVSYNC : 0;
33     flags |= targetTexture ? SDL_RENDERER_TARGETTEXTURE : 0;
34 
35     return flags;
36 }
37 
38 /++
39  + D struct that wraps `SDL_RendererInfo` containing renderer information
40  +/
41 struct RendererInfo {
42     string name; /// Name of the renderer
43     PixelFormat[] textureFormats; /// Available texture pixel formats
44     uint[2] maxTextureSize; /// Maximum texture size
45     uint sdlFlags; /// Internal SDL bitmask of supported renderer flags
46 
47     this() @disable;
48 
49     /++
50      + Constructs a `dsdl2.RendererInfo` from a vanilla `SDL_RendererInfo` from bindbc-sdl
51      +
52      + Params:
53      +   sdlRendererInfo = the `SDL_RendererInfo` struct
54      +/
55     this(SDL_RendererInfo sdlRendererInfo) @trusted {
56         this.name = sdlRendererInfo.name.to!string;
57         this.sdlFlags = sdlRendererInfo.flags;
58         this.textureFormats.length = sdlRendererInfo.num_texture_formats;
59         foreach (i; 0 .. sdlRendererInfo.num_texture_formats) {
60             this.textureFormats[i] = new PixelFormat(sdlRendererInfo.texture_formats[i]);
61         }
62         this.maxTextureSize = [
63             sdlRendererInfo.max_texture_width.to!uint,
64             sdlRendererInfo.max_texture_height.to!uint
65         ];
66     }
67 
68     /++
69      + Constructs a `dsdl2.RendererInfo` by feeding it its attributes
70      +
71      + Params:
72      +   name = name of the renderer
73      +   textureFormats = available texture pixel format(s)
74      +   maxTextureSize = maximum size a texture can be
75      +   software = adds `SDL_RENDERER_SOFTWARE` flag
76      +   accelerated = adds `SDL_RENDERER_ACCELERATED` flag
77      +   presentVSync = adds `SDL_RENDERER_PRESENTVSYNC` flag
78      +   targetTexture = adds `SDL_RENDERER_TARGETTEXTURE` flag
79      +/
80     this(string name, PixelFormat[] textureFormats, uint[2] maxTextureSize, bool software = false,
81         bool accelerated = false, bool presentVSync = false, bool targetTexture = false) @trusted {
82         this.name = name;
83         this.textureFormats = textureFormats;
84         this.maxTextureSize = maxTextureSize;
85         this.sdlFlags = toSDLRendererFlags(software, accelerated, presentVSync, targetTexture);
86     }
87 
88     /++
89      + Formats the `dsdl2.RendererInfo` into its construction representation:
90      + `"dsdl2.RendererInfo(<name>, <textureFormats>, <maxTextureSize>, <flag> : <value> ...)"`
91      +
92      + Returns: the formatted `string`
93      +/
94     string toString() const {
95         return "dsdl2.RendererInfo(%s, %s, %s, software : %s, accelerated : %s, presentVSync : %s, targetTexture : %s)"
96             .format([this.name].to!string[1 .. $ - 1], this.textureFormats, this.maxTextureSize,
97                 this.software, this.accelerated, this.presentVSync, this.targetTexture);
98     }
99 
100     /++
101      + Gets the internal `SDL_RendererInfo` representation
102      +
103      + Returns: `SDL_RendererInfo` with all of the attributes
104      +/
105     inout(SDL_RendererInfo) sdlRendererInfo() inout @property {
106         uint[16] textureFormatEnums = void;
107         foreach (i, inout textureFormat; this.textureFormats) {
108             textureFormatEnums[i] = textureFormat.sdlPixelFormatEnum;
109         }
110 
111         return inout SDL_RendererInfo(this.name.toStringz(), this.sdlFlags, this.textureFormats.length.to!uint,
112             textureFormatEnums, this.maxTextureWidth, this.maxTextureHeight);
113     }
114 
115     /++
116      + Gets whether the `dsdl2.RendererInfo` has `SDL_RENDERER_SOFTWARE` flag
117      +
118      + Returns: `true` if it has `SDL_RENDERER_SOFTWARE` flag, otherwise `false`
119      +/
120     bool software() const @property {
121         return (this.sdlFlags & SDL_RENDERER_SOFTWARE) != 0;
122     }
123 
124     /++
125      + Sets whether the `dsdl2.RendererInfo` has `SDL_RENDERER_SOFTWARE` flag
126      +
127      + Params:
128      +   value = `true` to set `SDL_RENDERER_SOFTWARE` flag; `false` to unset it
129      +/
130     void software(bool value) @property {
131         this.sdlFlags |= value ? SDL_RENDERER_SOFTWARE : 0;
132     }
133 
134     /++
135      + Gets whether the `dsdl2.RendererInfo` has `SDL_RENDERER_ACCELERATED` flag
136      +
137      + Returns: `true` if it has `SDL_RENDERER_ACCELERATED` flag, otherwise `false`
138      +/
139     bool accelerated() const @property {
140         return (this.sdlFlags & SDL_RENDERER_ACCELERATED) != 0;
141     }
142 
143     /++
144      + Sets whether the `dsdl2.RendererInfo` has `SDL_RENDERER_ACCELERATED` flag
145      +
146      + Params:
147      +   value = `true` to set `SDL_RENDERER_ACCELERATED` flag; `false` to unset it
148      +/
149     void accelerated(bool value) @property {
150         this.sdlFlags |= value ? SDL_RENDERER_ACCELERATED : 0;
151     }
152 
153     /++
154      + Gets whether the `dsdl2.RendererInfo` has `SDL_RENDERER_PRESENTVSYNC` flag
155      +
156      + Returns: `true` if it has `SDL_RENDERER_PRESENTVSYNC` flag, otherwise `false`
157      +/
158     bool presentVSync() const @property {
159         return (this.sdlFlags & SDL_RENDERER_PRESENTVSYNC) != 0;
160     }
161 
162     /++
163      + Sets whether the `dsdl2.RendererInfo` has `SDL_RENDERER_PRESENTVSYNC` flag
164      +
165      + Params:
166      +   value = `true` to set `SDL_RENDERER_PRESENTVSYNC` flag; `false` to unset it
167      +/
168     void presentVSync(bool value) @property {
169         this.sdlFlags |= value ? SDL_RENDERER_PRESENTVSYNC : 0;
170     }
171 
172     /++
173      + Gets whether the `dsdl2.RendererInfo` has `SDL_RENDERER_TARGETTEXTURE` flag
174      +
175      + Returns: `true` if it has `SDL_RENDERER_TARGETTEXTURE` flag, otherwise `false`
176      +/
177     bool targetTexture() const @property {
178         return (this.sdlFlags & SDL_RENDERER_TARGETTEXTURE) != 0;
179     }
180 
181     /++
182      + Sets whether the `dsdl2.RendererInfo` has `SDL_RENDERER_TARGETTEXTURE` flag
183      +
184      + Params:
185      +   value = `true` to set `SDL_RENDERER_TARGETTEXTURE` flag; `false` to unset it
186      +/
187     void targetTexture(bool value) @property {
188         this.sdlFlags |= value ? SDL_RENDERER_TARGETTEXTURE : 0;
189     }
190 
191     /++
192      + Proxy to the maximum texture width of the `dsdl2.RendererInfo`
193      +
194      + Returns: maximum texture width of the `dsdl2.RendererInfo`
195      +/
196     ref inout(uint) maxTextureWidth() return inout @property {
197         return this.maxTextureSize[0];
198     }
199 
200     /++
201      + Proxy to the maximum texture height of the `dsdl2.RendererInfo`
202      +
203      + Returns: maximum texture height of the `dsdl2.RendererInfo`
204      +/
205     ref inout(uint) maxTextureHeight() return inout @property {
206         return this.maxTextureSize[1];
207     }
208 }
209 
210 /++
211  + D class that acts as a proxy for a render driver from a render driver index
212  +/
213 final class RenderDriver {
214     const uint sdlRenderDriverIndex; /// Render driver index from SDL
215     const RendererInfo info = void; /// `dsdl2.RendererInfo` instance fetched from the driver
216 
217     this() @disable;
218 
219     private this(uint sdlRenderDriverIndex) @trusted {
220         this.sdlRenderDriverIndex = sdlRenderDriverIndex;
221 
222         SDL_RendererInfo sdlRendererInfo;
223         if (SDL_GetRenderDriverInfo(sdlRenderDriverIndex.to!int, &sdlRendererInfo) != 0) {
224             throw new SDLException;
225         }
226 
227         this.info = RendererInfo(sdlRendererInfo);
228     }
229 
230     /++
231      + Formats the `dsdl2.RenderDriver` into its construction representation:
232      + `"dsdl2.RenderDriver(<sdlRenderDriverIndex>)"`
233      +
234      + Returns: the formatted `string`
235      +/
236     override string toString() const {
237         return "dsdl2.RenderDriver(%d)".format(this.sdlRenderDriverIndex);
238     }
239 }
240 
241 /++
242  + Gets `dsdl2.RenderDriver` proxy instances of the available render drivers in the system
243  +
244  + Returns: array of proxies to the available `dsdl2.RenderDriver`s
245  + Throws: `dsdl2.SDLException` if failed to get the available render drivers
246  +/
247 const(RenderDriver[]) getRenderDrivers() @trusted {
248     int numDrivers = SDL_GetNumRenderDrivers();
249     if (numDrivers < 0) {
250         throw new SDLException;
251     }
252 
253     static RenderDriver[] drivers;
254     if (drivers !is null) {
255         size_t originalLength = drivers.length;
256         drivers.length = numDrivers;
257 
258         if (numDrivers > originalLength) {
259             foreach (i; originalLength .. numDrivers) {
260                 drivers[i] = new RenderDriver(i.to!uint);
261             }
262         }
263     }
264     else {
265         drivers = new RenderDriver[](numDrivers);
266         foreach (i; 0 .. numDrivers) {
267             drivers[i] = new RenderDriver(i);
268         }
269     }
270 
271     return drivers;
272 }
273 
274 static if (sdlSupport >= SDLSupport.v2_0_18) {
275     /++
276      + D struct that wraps `SDL_Vertex` (from SDL 2.0.18) containing 2D vertex information
277      +
278      + `dsdl2.Vertex` stores the `position` of the vertex, `color` modulation (as well as alpha), and mapped texture
279      + `texCoord`inate.
280      +/
281     struct Vertex {
282         SDL_Vertex sdlVertex; /// Internal `SDL_Vertex` struct
283 
284         this() @disable;
285 
286         /++
287          + Constructs a `dsdl2.Vertex` from a vanilla `SDL_Vertex` from bindbc-sdl
288          +
289          + Params:
290          +   sdlVertex = the `dsdl2.Vertex` struct
291          +/
292         this(SDL_Vertex sdlVertex) {
293             this.sdlVertex = sdlVertex;
294         }
295 
296         /++
297          + Constructs a `dsdl2.Vertex` by feeding in the position, color, and texture coordinate
298          +
299          + Params:
300          +   position = vertex target position
301          +   color = color and alpha modulation of the vertex
302          +   texCoord = vertex texture coordinate
303          +/
304         this(FPoint position, Color color, FPoint texCoord) {
305             this.sdlVertex.position = position.sdlFPoint;
306             this.sdlVertex.color = color.sdlColor;
307             this.sdlVertex.tex_coord = texCoord.sdlFPoint;
308         }
309 
310         /++
311          + Formats the `dsdl2.Vertex` into its construction representation:
312          + `"dsdl2.Vertex(<position>, <color>, <texCoord>)"`
313          +
314          + Returns: the formatted `string`
315          +/
316         string toString() const {
317             return "dsdl2.Vertex(%f, %f)".format(this.position, this.color, this.texCoord);
318         }
319 
320         /++
321          + Proxy to the X position of the `dsdl2.Vertex`
322          +
323          + Returns: X position of the `dsdl2.Vertex`
324          +/
325         ref inout(float) x() return inout @property {
326             return this.sdlVertex.position.x;
327         }
328 
329         /++
330          + Proxy to the Y position of the `dsdl2.Vertex`
331          +
332          + Returns: Y position of the `dsdl2.Vertex`
333          +/
334         ref inout(float) y() return inout @property {
335             return this.sdlVertex.position.y;
336         }
337 
338         /++
339          + Proxy to the position of the `dsdl2.Vertex`
340          +
341          + Returns: position of the `dsdl2.Vertex`
342          +/
343         ref inout(FPoint) position() return inout @property {
344             return *cast(inout(FPoint*))&this.sdlVertex.position;
345         }
346 
347         /++
348          + Proxy to the color of the `dsdl2.Vertex`
349          +
350          + Returns: color of the `dsdl2.Vertex`
351          +/
352         ref inout(Color) color() return inout @property {
353             return *cast(inout(Color*))&this.sdlVertex.color;
354         }
355 
356         /++
357          + Proxy to the X texture coordinate of the `dsdl2.Vertex`
358          +
359          + Returns: X texture coordinate of the `dsdl2.Vertex`
360          +/
361         ref inout(float) texX() return inout @property {
362             return this.sdlVertex.tex_coord.x;
363         }
364 
365         /++
366          + Proxy to the Y texture coordinate of the `dsdl2.Vertex`
367          +
368          + Returns: Y texture coordinate of the `dsdl2.Vertex`
369          +/
370         ref inout(float) texY() return inout @property {
371             return this.sdlVertex.tex_coord.y;
372         }
373 
374         /++
375          + Proxy to the texture coordinate of the `dsdl2.Vertex`
376          +
377          + Returns: texture coordinate of the `dsdl2.Vertex`
378          +/
379         ref inout(FPoint) texCoord() return inout @property {
380             return *cast(inout(FPoint*))&this.sdlVertex.tex_coord;
381         }
382     }
383 }
384 
385 /++
386  + D class that wraps `SDL_Renderer` managing a backend rendering instance
387  +
388  + `dsdl2.Renderer` provides access to 2D draw commands, which accesses the internal backend renderer. The output/target
389  + of the renderer can be displayed to a `dsdl2.Window` if desired, or be done in software to the RAM as a
390  + `dsdl2.Surface`.
391  +
392  + Example:
393  + ---
394  + auto window = new dsdl2.Window("My Window", [dsdl2.WindowPos.centered, dsdl2.WindowPos.centered], [800, 600]);
395  + auto renderer = new dsdl2.Renderer(window, accelerated : true, acceleratedVSync : true);
396  + ---
397  +/
398 final class Renderer {
399     private Texture targetProxy = null;
400     private bool isOwner = true;
401     private void* userRef = null;
402 
403     @system SDL_Renderer* sdlRenderer = null; /// Internal `SDL_Renderer` pointer
404 
405     /++
406      + Constructs a `dsdl2.Renderer` from a vanilla `SDL_Renderer*` from bindbc-sdl
407      +
408      + Params:
409      +   sdlRenderer = the `SDL_Renderer` pointer to manage
410      +   isOwner = whether the instance owns the given `SDL_Renderer*` and should destroy it on its own
411      +   userRef = optional pointer to maintain reference link, avoiding GC cleanup
412      +/
413     this(SDL_Renderer* sdlRenderer, bool isOwner = true, void* userRef = null) @system
414     in {
415         assert(sdlRenderer !is null);
416     }
417     do {
418         this.sdlRenderer = sdlRenderer;
419         this.isOwner = isOwner;
420         this.userRef = userRef;
421     }
422 
423     /++
424      + Creates a hardware `dsdl2.Renderer` that renders to a `dsdl2.Window`, which wraps `SDL_CreateRenderer`
425      +
426      + Params:
427      +   window = target `dsdl2.Window` for the renderer to draw onto which must not have a surface associated
428      +   renderDriver = the `dsdl2.RenderDriver` to use; `null` to use the default
429      +   software = adds `SDL_RENDERER_SOFTWARE` flag
430      +   accelerated = adds `SDL_RENDERER_ACCELERATED` flag
431      +   presentVSync = adds `SDL_RENDERER_PRESENTVSYNC` flag
432      +   targetTexture = adds `SDL_RENDERER_TARGETTEXTURE` flag
433      + Throws: `dsdl2.SDLException` if creation failed
434      +/
435     this(Window window, const RenderDriver renderDriver = null, bool software = false,
436         bool accelerated = false, bool presentVSync = false, bool targetTexture = false) @trusted
437     in {
438         assert(window !is null);
439     }
440     do {
441         uint flags = toSDLRendererFlags(software, accelerated, presentVSync, targetTexture);
442         this.sdlRenderer = SDL_CreateRenderer(window.sdlWindow,
443             renderDriver is null ? -1 : renderDriver.sdlRenderDriverIndex.to!uint, flags);
444         if (this.sdlRenderer is null) {
445             throw new SDLException;
446         }
447     }
448 
449     /++
450      + Creates a software `dsdl2.Renderer` that renders to a target surface, which wraps `SDL_CreateSoftwareRenderer`
451      +
452      + Params:
453      +   surface = `dsdl2.Surface` to be the target of rendering
454      + Throws: `dsdl2.SDLException` if creation failed
455      +/
456     this(Surface surface) @trusted
457     in {
458         assert(surface !is null);
459     }
460     do {
461         this.sdlRenderer = SDL_CreateSoftwareRenderer(surface.sdlSurface);
462         if (this.sdlRenderer is null) {
463             throw new SDLException;
464         }
465 
466         this.userRef = cast(void*) surface;
467     }
468 
469     ~this() @trusted {
470         if (this.isOwner) {
471             SDL_DestroyRenderer(this.sdlRenderer);
472         }
473     }
474 
475     @trusted invariant { // @suppress(dscanner.trust_too_much)
476         // Instance might be in an invalid state due to holding a non-owned externally-freed object when
477         // destructed in an unpredictable order.
478         if (!this.isOwner && GC.inFinalizer) {
479             return;
480         }
481 
482         assert(this.sdlRenderer !is null);
483     }
484 
485     /++
486      + Equality operator overload
487      +/
488     bool opEquals(const Renderer rhs) const @trusted {
489         return this.sdlRenderer is rhs.sdlRenderer;
490     }
491 
492     /++
493      + Gets the hash of the `dsdl2.Renderer`
494      +
495      + Returns: unique hash for the instance being the pointer of the internal `SDL_Renderer` pointer
496      +/
497     override hash_t toHash() const @trusted {
498         return cast(hash_t) this.sdlRenderer;
499     }
500 
501     /++
502      + Formats the `dsdl2.Renderer` into its construction representation: `"dsdl2.Renderer(<sdlRenderer>)"`
503      +
504      + Returns: the formatted `string`
505      +/
506     override string toString() const @trusted {
507         return "dsdl2.Renderer(0x%x)".format(this.sdlRenderer);
508     }
509 
510     /++
511      + Wraps `SDL_GetRendererInfo` which gets the renderer information
512      +
513      + Returns: `dsdl2.RendererInfo` of the renderer
514      + Throws: `dsdl2.SDLException` if failed to get the renderer information
515      +/
516     RendererInfo info() const @property @trusted {
517         SDL_RendererInfo sdlRendererInfo = void;
518         if (SDL_GetRendererInfo(cast(SDL_Renderer*) this.sdlRenderer, &sdlRendererInfo) != 0) {
519             throw new SDLException;
520         }
521 
522         return RendererInfo(sdlRendererInfo);
523     }
524 
525     /++
526      + Wraps `SDL_GetRendererOutputSize` which gets the renderer output's width
527      +
528      + Returns: drawable width of the renderer's output/target
529      + Throws: `dsdl2.SDLException` if failed to get the renderer output width
530      +/
531     uint width() const @property @trusted {
532         uint w = void;
533         if (SDL_GetRendererOutputSize(cast(SDL_Renderer*) this.sdlRenderer, cast(int*) w, null) != 1) {
534             throw new SDLException;
535         }
536 
537         return w;
538     }
539 
540     /++
541      + Wraps `SDL_GetRendererOutputSize` which gets the renderer output's height
542      +
543      + Returns: drawable height of the renderer's output/target
544      + Throws: `dsdl2.SDLException` if failed to get the renderer output height
545      +/
546     uint height() const @property @trusted {
547         uint h = void;
548         if (SDL_GetRendererOutputSize(cast(SDL_Renderer*) this.sdlRenderer, null, cast(int*) h) != 1) {
549             throw new SDLException;
550         }
551 
552         return h;
553     }
554 
555     /++
556      + Wraps `SDL_GetRendererOutputSize` which gets the renderer output's size
557      +
558      + Returns: drawable size of the renderer's output/target
559      + Throws: `dsdl2.SDLException` if failed to get the renderer output size
560      +/
561     uint[2] size() const @property @trusted {
562         uint[2] xy = void;
563         if (SDL_GetRendererOutputSize(cast(SDL_Renderer*) this.sdlRenderer, cast(int*) xy[0],
564                 cast(int*) xy[1]) != 1) {
565             throw new SDLException;
566         }
567 
568         return xy;
569     }
570 
571     /++
572      + Wraps `SDL_RenderTargetSupported` which checks if the renderer supports texture targets
573      +
574      + Returns: `true` if the renderer supports, otherwise `false`
575      +/
576     bool supportsTarget() const @property @trusted {
577         return SDL_RenderTargetSupported(cast(SDL_Renderer*) this.sdlRenderer) == SDL_TRUE;
578     }
579 
580     /++
581      + Wraps `SDL_GetRenderTarget` which gets the renderer's target
582      +
583      + Returns: `null` if the renderer uses the default target (usually the window), otherwise a `dsdl2.Texture`
584      +          proxy to the the set texture target
585      +/
586     inout(Texture) target() inout @property @trusted {
587         SDL_Texture* targetPtr = SDL_GetRenderTarget(cast(SDL_Renderer*) this.sdlRenderer);
588         if (targetPtr is null) {
589             (cast(Renderer) this).targetProxy = null;
590         }
591         else {
592             // If the target texture pointer happens to change, rewire the proxy.
593             if (this.targetProxy is null || this.targetProxy.sdlTexture !is targetPtr) {
594                 (cast(Renderer) this).targetProxy = new Texture(targetPtr);
595             }
596         }
597 
598         return this.targetProxy;
599     }
600 
601     /++
602      + Wraps `SDL_SetRenderTarget` which sets the renderer's target
603      +
604      + Params:
605      +   newTarget = `null` to set the target to be the default target (usually the window), or a valid target
606      +               `dsdl2.Texture` as the texture target
607      + Throws: `dsdl2.SDLException` if failed to set the renderer's target
608      +/
609     void target(Texture newTarget) @property @trusted {
610         if (newTarget is null) {
611             if (SDL_SetRenderTarget(cast(SDL_Renderer*) this.sdlRenderer, null) != 0) {
612                 throw new SDLException;
613             }
614         }
615         else {
616             if (SDL_SetRenderTarget(cast(SDL_Renderer*) this.sdlRenderer, newTarget.sdlTexture) != 0) {
617                 throw new SDLException;
618             }
619         }
620     }
621 
622     /++
623      + Wraps `SDL_RenderGetClipRect` which gets the clipping `dsdl2.Rect` of the renderer
624      +
625      + Returns: clipping `dsdl2.Rect` of the renderer
626      +/
627     Rect clipRect() const @property @trusted {
628         Rect rect = void;
629         SDL_RenderGetClipRect(cast(SDL_Renderer*) this.sdlRenderer, &rect.sdlRect);
630         return rect;
631     }
632 
633     /++
634      + Wraps `SDL_RenderSetClipRect` which sets the clipping `dsdl2.Rect` of the renderer
635      +
636      + Params:
637      +   newRect = `dsdl2.Rect` to set as the clipping rectangle
638      +/
639     void clipRect(Rect newRect) @property @trusted {
640         SDL_RenderSetClipRect(this.sdlRenderer, &newRect.sdlRect);
641     }
642 
643     /++
644      + Acts as `SDL_RenderSetClipRect(renderer, NULL)` which removes the clipping `dsdl2.Rect` of the
645      + renderer
646      +/
647     void clipRect(typeof(null) _) @property @trusted {
648         SDL_RenderSetClipRect(this.sdlRenderer, null);
649     }
650 
651     /++
652      + Wraps `SDL_RenderSetClipRect` which sets or removes the clipping `dsdl2.Rect` of the renderer
653      +
654      + Params:
655      +   newRect = `dsdl2.Rect` to set as the clipping rectangle; `null` to remove the clipping rectangle
656      +/
657     void clipRect(Nullable!Rect newRect) @property @trusted {
658         if (newRect.isNull) {
659             this.clipRect = null;
660         }
661         else {
662             this.clipRect = newRect.get;
663         }
664     }
665 
666     /++
667      + Wraps `SDL_RenderGetLogicalSize` which gets the renderer output's logical width
668      +
669      + Returns: logical width of the renderer's output/target
670      +/
671     uint logicalWidth() const @property @trusted {
672         uint w = void;
673         SDL_RenderGetLogicalSize(cast(SDL_Renderer*) this.sdlRenderer, cast(int*) w, null);
674         return w;
675     }
676 
677     /++
678      + Wraps `SDL_RenderSetLogicalSize` which sets the renderer output's logical width
679      +
680      + Params:
681      +   newWidth = new logical width of the renderer's output
682      + Throws: `dsdl2.SDLException` if failed to set the renderer's logical width
683      +/
684     void logicalWidth(uint newWidth) @property @trusted {
685         if (SDL_RenderSetLogicalSize(this.sdlRenderer, newWidth, this.logicalHeight) != 0) {
686             throw new SDLException;
687         }
688     }
689 
690     /++
691      + Wraps `SDL_RenderGetLogicalSize` which gets the renderer output's logical height
692      +
693      + Returns: logical height of the renderer's output/target
694      +/
695     uint logicalHeight() const @property @trusted {
696         uint h = void;
697         SDL_RenderGetLogicalSize(cast(SDL_Renderer*) this.sdlRenderer, null, cast(int*) h);
698         return h;
699     }
700 
701     /++
702      + Wraps `SDL_RenderSetLogicalSize` which sets the renderer output's logical height
703      +
704      + Params:
705      +   newHeight = new logical height of the renderer's output
706      + Throws: `dsdl2.SDLException` if failed to set the renderer's logical height
707      +/
708     void logicalHeight(uint newHeight) @property @trusted {
709         if (SDL_RenderSetLogicalSize(this.sdlRenderer, this.logicalWidth, newHeight) != 0) {
710             throw new SDLException;
711         }
712     }
713 
714     /++
715      + Wraps `SDL_RenderGetLogicalSize` which gets the renderer logical size
716      +
717      + Returns: logical size of the renderer's output/target
718      +/
719     uint[2] logicalSize() const @property @trusted {
720         uint[2] wh = void;
721         SDL_RenderGetLogicalSize(cast(SDL_Renderer*) this.sdlRenderer, cast(int*) wh[0], cast(int*) wh[1]);
722         return wh;
723     }
724 
725     /++
726      + Wraps `SDL_RenderSetLogicalSize` which sets the renderer output's logical size
727      +
728      + Params:
729      +   newSize = new logical size (width and height) of the renderer's output
730      + Throws: `dsdl2.SDLException` if failed to set the renderer's logical size
731      +/
732     void logicalSize(uint[2] newSize) @property @trusted {
733         if (SDL_RenderSetLogicalSize(this.sdlRenderer, newSize[0].to!int, newSize[1].to!int) != 0) {
734             throw new SDLException;
735         }
736     }
737 
738     /++
739      + Wraps `SDL_RenderGetViewport` which gets the `dsdl2.Rect` viewport of the renderer
740      +
741      + Returns: viewport `dsdl2.Rect` of the renderer
742      +/
743     Rect viewport() const @property @trusted {
744         Rect rect = void;
745         SDL_RenderGetClipRect(cast(SDL_Renderer*) this.sdlRenderer, &rect.sdlRect);
746         return rect;
747     }
748 
749     /++
750      + Wraps `SDL_RenderSetViewport` which sets the `dsdl2.Rect` viewport of the `dsdl2.Renderer`
751      +
752      + Params:
753      +   newViewport = `dsdl2.Rect` to set as the rectangle viewport
754      + Throws: `dsdl2.SDLException` if failed to set the renderer's viewport
755      +/
756     void viewport(Rect newViewport) @property @trusted {
757         if (SDL_RenderSetViewport(this.sdlRenderer, &newViewport.sdlRect) != 0) {
758             throw new SDLException;
759         }
760     }
761 
762     /++
763      + Acts as `SDL_RenderSetViewport(renderer, NULL)` which removes the `dsdl2.Rect` viewport of the
764      + `dsdl2.Renderer`
765      + Throws: `dsdl2.SDLException` if failed to set the renderer's viewport
766      +/
767     void viewport(typeof(null) _) @property @trusted {
768         if (SDL_RenderSetViewport(this.sdlRenderer, null) != 0) {
769             throw new SDLException;
770         }
771     }
772 
773     /++
774      + Wraps `SDL_RenderSetViewport` which sets or removes the viewport `dsdl2.Rect` of the `dsdl2.Renderer`
775      +
776      + Params:
777      +   newViewport = `dsdl2.Rect` to set as the rectangle viewport; `null` to remove the rectangle viewport
778      + Throws: `dsdl2.SDLException` if failed to set the renderer's viewport
779      +/
780     void viewport(Nullable!Rect newViewport) @property @trusted {
781         if (newViewport.isNull) {
782             this.clipRect = null;
783         }
784         else {
785             this.clipRect = newViewport.get;
786         }
787     }
788 
789     /++
790      + Wraps `SDL_RenderGetScale` which gets the X drawing scale of the renderer target
791      +
792      + Returns: `float` scale in the X axis
793      +/
794     float scaleX() const @property @trusted {
795         float x = void;
796         SDL_RenderGetScale(cast(SDL_Renderer*) this.sdlRenderer, &x, null);
797         return x;
798     }
799 
800     /++
801      + Wraps `SDL_RenderSetScale` which sets the X drawing scale of the renderer target
802      +
803      + Params:
804      +   newX = new `float` scale of the X axis
805      + Throws: `dsdl2.SDLException` if failed to set the scale
806      +/
807     void scaleX(float newX) @property @trusted {
808         if (SDL_RenderSetScale(this.sdlRenderer, newX, this.scaleY) != 0) {
809             throw new SDLException;
810         }
811     }
812 
813     /++
814      + Wraps `SDL_RenderGetScale` which gets the Y drawing scale of the renderer target
815      +
816      + Returns: `float` scale in the Y axis
817      +/
818     float scaleY() const @property @trusted {
819         float y = void;
820         SDL_RenderGetScale(cast(SDL_Renderer*) this.sdlRenderer, null, &y);
821         return y;
822     }
823 
824     /++
825      + Wraps `SDL_RenderSetScale` which sets the Y drawing scale of the renderer target
826      +
827      + Params:
828      +   newY = new `float` scale of the Y axis
829      + Throws: `dsdl2.SDLException` if failed to set the scale
830      +/
831     void scaleY(float newY) @property @trusted {
832         if (SDL_RenderSetScale(this.sdlRenderer, this.scaleX, newY) != 0) {
833             throw new SDLException;
834         }
835     }
836 
837     /++
838      + Wraps `SDL_RenderGetScale` which gets the drawing scale of the renderer target
839      +
840      + Returns: array of 2 `float`s for the X and Y scales
841      +/
842     float[2] scale() const @property @trusted {
843         float[2] xy = void;
844         SDL_RenderGetScale(cast(SDL_Renderer*) this.sdlRenderer, &xy[0], &xy[1]);
845         return xy;
846     }
847 
848     /++
849      + Wraps `SDL_RenderSetScale` which sets the drawing scale of the renderer target
850      +
851      + Params:
852      +   newScale = array of 2 `float`s for the new X and Y scales
853      + Throws: `dsdl2.SDLException` if failed to set the scale
854      +/
855     void scale(float[2] newScale) @property @trusted {
856         if (SDL_RenderSetScale(this.sdlRenderer, newScale[0], newScale[1]) != 0) {
857             throw new SDLException;
858         }
859     }
860 
861     /++
862      + Wraps `SDL_GetRenderDrawColor` which gets the draw color for the following draw calls
863      +
864      + Returns: `dsdl2.Color` of the renderer's current draw color
865      +/
866     Color drawColor() const @property @trusted {
867         Color color = void;
868         if (SDL_GetRenderDrawColor(cast(SDL_Renderer*) this.sdlRenderer, &color.r(), &color.g(), &color.b(),
869                 &color.a()) != 0) {
870             throw new SDLException;
871         }
872 
873         return color;
874     }
875 
876     /++
877      + Wraps `SDL_SetRenderDrawColor` which sets the draw color for the following draw calls
878      +
879      + Params:
880      +   newColor = new `dsdl2.Color` as the renderer's current draw color
881      + Throws: `dsdl2.SDLException` if failed to set the draw color
882      +/
883     void drawColor(Color newColor) @property @trusted {
884         if (SDL_SetRenderDrawColor(this.sdlRenderer, newColor.r, newColor.g, newColor.b, newColor.a) != 0) {
885             throw new SDLException;
886         }
887     }
888 
889     /++
890      + Wraps `SDL_GetRenderDrawBlendMode` which gets the color blending mode of the renderer
891      +
892      + Returns: color `dsdl2.BlendMode` of the renderer
893      + Throws: `dsdl2.SDLException` if failed to get the color blending mode
894      +/
895     BlendMode blendMode() const @property @trusted {
896         BlendMode mode = void;
897         if (SDL_GetRenderDrawBlendMode(cast(SDL_Renderer*) this.sdlRenderer, &mode.sdlBlendMode) != 0) {
898             throw new SDLException;
899         }
900 
901         return mode;
902     }
903 
904     /++
905      + Wraps `SDL_SetRenderDrawBlendMode` which sets the color blending mode of the renderer
906      +
907      + Params:
908      +   newMode = new `dsdl2.BlendMode` as the renderer's current color blending mode
909      + Throws: `dsdl2.SDLException` if failed to set the color blending mode
910      +/
911     void blendMode(BlendMode newMode) @property @trusted {
912         if (SDL_SetRenderDrawBlendMode(this.sdlRenderer, newMode.sdlBlendMode) != 0) {
913             throw new SDLException;
914         }
915     }
916 
917     /++
918      + Wraps `SDL_RenderClear` which clears the target with the renderer's draw color
919      +
920      + Throws: `dsdl2.SDLException` if failed to clear
921      +/
922     void clear() @trusted {
923         if (SDL_RenderClear(this.sdlRenderer) != 0) {
924             throw new SDLException;
925         }
926     }
927 
928     /++
929      + Wraps `SDL_RenderDrawPoint` which draws a single point at a given position with the renderer's draw color
930      +
931      + Params:
932      +   point = `dsdl2.Point` position the point is drawn at
933      + Throws: `dsdl2.SDLException` if point failed to draw
934      +/
935     void drawPoint(Point point) @trusted {
936         if (SDL_RenderDrawPoint(this.sdlRenderer, point.x, point.y) != 0) {
937             throw new SDLException;
938         }
939     }
940 
941     /++
942      + Wraps `SDL_RenderDrawPoints` which draws multiple points at given positions with the renderer's draw color
943      +
944      + Params:
945      +   points = array of `dsdl2.Point` positions the points are drawn at
946      + Throws: `dsdl2.SDLException` if points failed to draw
947      +/
948     void drawPoints(const Point[] points) @trusted {
949         if (SDL_RenderDrawPoints(this.sdlRenderer, cast(SDL_Point*) points.ptr, points.length.to!int) != 0) {
950             throw new SDLException;
951         }
952     }
953 
954     /++
955      + Wraps `SDL_RenderDrawLine` which draws a line between two points with the renderer's draw color
956      +
957      + Params:
958      +   line = array of two `dsdl2.Point`s indicating the line's start and end
959      + Throws: `dsdl2.SDLException` if line failed to draw
960      +/
961     void drawLine(Point[2] line) @trusted {
962         if (SDL_RenderDrawLine(this.sdlRenderer, line[0].x, line[0].y, line[1].x, line[1].y) != 0) {
963             throw new SDLException;
964         }
965     }
966 
967     /++
968      + Wraps `SDL_RenderDrawLines` which draws multiple lines following given points with the renderer's draw color
969      +
970      + Params:
971      +   points = array of `dsdl2.Point` edges the lines are drawn from and to
972      + Throws: `dsdl2.SDLException` if lines failed to draw
973      +/
974     void drawLines(const Point[] points) @trusted {
975         if (SDL_RenderDrawLines(this.sdlRenderer, cast(SDL_Point*) points.ptr, points.length.to!int) != 0) {
976             throw new SDLException;
977         }
978     }
979 
980     /++
981      + Wraps `SDL_RenderDrawRect` which draws a rectangle's edges with the renderer's draw color
982      +
983      + Params:
984      +   rect = `dsdl2.Rect` of the rectangle
985      + Throws: `dsdl2.SDLException` if rectangle failed to draw
986      +/
987     void drawRect(Rect rect) @trusted {
988         if (SDL_RenderDrawRect(this.sdlRenderer, &rect.sdlRect) != 0) {
989             throw new SDLException;
990         }
991     }
992 
993     /++
994      + Wraps `SDL_RenderDrawRects` which draws multiple rectangles' edges with the renderer's draw color
995      +
996      + Params:
997      +   rects = array of `dsdl2.Rect` of the rectangles
998      + Throws: `dsdl2.SDLException` if rectangles failed to draw
999      +/
1000     void drawRects(const Rect[] rects) @trusted {
1001         if (SDL_RenderDrawRects(this.sdlRenderer, cast(SDL_Rect*) rects.ptr, rects.length.to!int) != 0) {
1002             throw new SDLException;
1003         }
1004     }
1005 
1006     /++
1007      + Wraps `SDL_RenderFillRect` which fills a rectangle with the renderer's draw color
1008      +
1009      + Params:
1010      +   rect = `dsdl2.Rect` of the rectangle
1011      + Throws: `dsdl2.SDLException` if rectangle failed to fill
1012      +/
1013     void fillRect(Rect rect) @trusted {
1014         if (SDL_RenderFillRect(this.sdlRenderer, &rect.sdlRect) != 0) {
1015             throw new SDLException;
1016         }
1017     }
1018 
1019     /++
1020      + Wraps `SDL_RenderFillRects` which fills multiple rectangles with the renderer's draw color
1021      +
1022      + Params:
1023      +   rects = array of `dsdl2.Rect` of the rectangles
1024      + Throws: `dsdl2.SDLException` if rectangles failed to fill
1025      +/
1026     void fillRects(const Rect[] rects) @trusted {
1027         if (SDL_RenderFillRects(this.sdlRenderer, cast(SDL_Rect*) rects.ptr, rects.length.to!int) != 0) {
1028             throw new SDLException;
1029         }
1030     }
1031 
1032     /++
1033      + Acts as `SDL_RenderCopy(renderer, texture, NULL, destRect)` which copies the entire texture to `destRect` at
1034      + the renderer's target
1035      +
1036      + Params:
1037      +   texture = `dsdl2.Texture` to be copied/drawn
1038      +   destRect = destination `dsdl2.Rect` in the target for the texture to be drawn to
1039      + Throws: `dsdl2.SDLException` if texture failed to draw
1040      +/
1041     void copy(const Texture texture, Rect destRect) @trusted
1042     in {
1043         assert(texture !is null);
1044     }
1045     do {
1046         if (SDL_RenderCopy(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, null, &destRect.sdlRect) != 0) {
1047             throw new SDLException;
1048         }
1049     }
1050 
1051     /++
1052      + Wraps `SDL_RenderCopy` which copies a part of the texture at `srcRect` to `destRect` at the renderer's target
1053      +
1054      + Params:
1055      +   texture = `dsdl2.Texture` to be copied/drawn
1056      +   destRect = destination `dsdl2.Rect` in the target for the texture to be drawn to
1057      +   srcRect = source `dsdl2.Rect` which clips the given texture
1058      + Throws: `dsdl2.SDLException` if texture failed to draw
1059      +/
1060     void copy(const Texture texture, Rect destRect, Rect srcRect) @trusted
1061     in {
1062         assert(texture !is null);
1063     }
1064     do {
1065         if (SDL_RenderCopy(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, &srcRect.sdlRect,
1066                 &destRect.sdlRect) != 0) {
1067             throw new SDLException;
1068         }
1069     }
1070 
1071     /++
1072      + Acts as `SDL_RenderCopyEx(renderer, texture, NULL, destRect, angle, NULL, flip)` which copies the
1073      + entire texture to `destRect` at the renderer's target with certain `angle` and flipping
1074      +
1075      + Params:
1076      +   texture = `dsdl2.Texture` to be copied/drawn
1077      +   destRect = destination `dsdl2.Rect` in the target for the texture to be drawn to
1078      +   angle = angle in degrees to rotate the texture counterclockwise
1079      +   flippedHorizontally = `true` to flip the texture horizontally, otherwise `false`
1080      +   flippedVertically = `true` to flip the texture vertically, otherwise `false`
1081      + Throws: `dsdl2.SDLException` if texture failed to draw
1082      +/
1083     void copyEx(const Texture texture, Rect destRect, double angle, bool flippedHorizontally = false,
1084         bool flippedVertically = false) @trusted
1085     in {
1086         assert(texture !is null);
1087     }
1088     do {
1089         if (SDL_RenderCopyEx(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, null, &destRect.sdlRect, angle,
1090                 null, (flippedHorizontally ? SDL_FLIP_HORIZONTAL : 0) |
1091                 (flippedVertically ? SDL_FLIP_VERTICAL : 0)) != 0) {
1092             throw new SDLException;
1093         }
1094     }
1095 
1096     /++
1097      + Acts as `SDL_RenderCopyEx(renderer, texture, srcRect, destRect, angle, NULL, flip)` which copies the
1098      + entire texture to `destRect` at the renderer's target with certain `angle` and flipping
1099      +
1100      + Params:
1101      +   texture = `dsdl2.Texture` to be copied/drawn
1102      +   destRect = destination `dsdl2.Rect` in the target for the texture to be drawn to
1103      +   angle = angle in degrees to rotate the texture counterclockwise
1104      +   srcRect = source `dsdl2.Rect` which clips the given texture
1105      +   flippedHorizontally = `true` to flip the texture horizontally, otherwise `false`
1106      +   flippedVertically = `true` to flip the texture vertically, otherwise `false`
1107      + Throws: `dsdl2.SDLException` if texture failed to draw
1108      +/
1109     void copyEx(const Texture texture, Rect destRect, double angle, Rect srcRect, bool flippedHorizontally = false,
1110         bool flippedVertically = false) @trusted
1111     in {
1112         assert(texture !is null);
1113     }
1114     do {
1115         if (SDL_RenderCopyEx(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, &srcRect.sdlRect,
1116                 &destRect.sdlRect, angle, null, (flippedHorizontally ? SDL_FLIP_HORIZONTAL : 0) |
1117                 (flippedVertically ? SDL_FLIP_VERTICAL : 0)) != 0) {
1118             throw new SDLException;
1119         }
1120     }
1121 
1122     /++
1123      + Acts as `SDL_RenderCopyEx(renderer, texture, NULL, destRect, angle, center, flip)` which copies the
1124      + entire texture to `destRect` at the renderer's target with certain `angle` and flipping
1125      +
1126      + Params:
1127      +   texture = `dsdl2.Texture` to be copied/drawn
1128      +   destRect = destination `dsdl2.Rect` in the target for the texture to be drawn to
1129      +   angle = angle in degrees to rotate the texture counterclockwise
1130      +   center = pivot `dsdl2.Point` of the texture for rotation
1131      +   flippedHorizontally = `true` to flip the texture horizontally, otherwise `false`
1132      +   flippedVertically = `true` to flip the texture vertically, otherwise `false`
1133      + Throws: `dsdl2.SDLException` if texture failed to draw
1134      +/
1135     void copyEx(const Texture texture, Rect destRect, double angle, Point center, bool flippedHorizontally = false,
1136         bool flippedVertically = false) @trusted
1137     in {
1138         assert(texture !is null);
1139     }
1140     do {
1141         if (SDL_RenderCopyEx(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, null, &destRect.sdlRect, angle,
1142                 &center.sdlPoint, (flippedHorizontally ? SDL_FLIP_HORIZONTAL : 0) |
1143                 (flippedVertically ? SDL_FLIP_VERTICAL : 0)) != 0) {
1144             throw new SDLException;
1145         }
1146     }
1147 
1148     /++
1149      + Wraps `SDL_RenderCopyEx` which copies the entire texture to `destRect` at the renderer's target with certain
1150      + `angle` and flipping
1151      +
1152      + Params:
1153      +   texture = `dsdl2.Texture` to be copied/drawn
1154      +   destRect = destination `dsdl2.Rect` in the target for the texture to be drawn to
1155      +   angle = angle in degrees to rotate the texture counterclockwise
1156      +   srcRect = source `dsdl2.Rect` which clips the given texture
1157      +   center = pivot `dsdl2.Point` of the texture for rotation
1158      +   flippedHorizontally = `true` to flip the texture horizontally, otherwise `false`
1159      +   flippedVertically = `true` to flip the texture vertically, otherwise `false`
1160      + Throws: `dsdl2.SDLException` if texture failed to draw
1161      +/
1162     void copyEx(const Texture texture, Rect destRect, double angle, Rect srcRect, Point center,
1163         bool flippedHorizontally = false, bool flippedVertically = false) @trusted
1164     in {
1165         assert(texture !is null);
1166     }
1167     do {
1168         if (SDL_RenderCopyEx(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, &srcRect.sdlRect,
1169                 &destRect.sdlRect, angle, &center.sdlPoint, (flippedHorizontally ? SDL_FLIP_HORIZONTAL
1170                 : 0) | (flippedVertically ? SDL_FLIP_VERTICAL : 0)) != 0) {
1171             throw new SDLException;
1172         }
1173     }
1174 
1175     /++
1176      + Wraps `SDL_RenderReadPixels` which makes a `dsdl2.Surface` from the renderer's entire target
1177      +
1178      + Params:
1179      +   format = requested `dsdl2.PixelFormat` of the returned `dsdl2.Surface`
1180      + Returns: `dsdl2.Surface` copy of the renderer's entire target
1181      + Throws: `dsdl2.SDLException` if pixels failed to be read
1182      +/
1183     Surface readPixels(const PixelFormat format = PixelFormat.rgba8888) const @trusted
1184     in {
1185         assert(!format.indexed);
1186     }
1187     do {
1188         Surface surface = new Surface(this.size, format);
1189         if (SDL_RenderReadPixels(cast(SDL_Renderer*) this.sdlRenderer, null, format.sdlPixelFormatEnum,
1190                 surface.buffer.ptr, surface.pitch.to!int) != 0) {
1191             throw new SDLException;
1192         }
1193 
1194         return surface;
1195     }
1196 
1197     /++
1198      + Wraps `SDL_RenderReadPixels` which makes a `dsdl2.Surface` from a specified `dsdl2.Rect` boundary at the
1199      + renderer's target
1200      +
1201      + Params:
1202      +   rect = `dsdl2.Rect` boundary to be read and copied
1203      +   format = requested `dsdl2.PixelFormat` of the returned `dsdl2.Surface`
1204      + Returns: `dsdl2.Surface` copy of the specified rectangle boundary in the renderer's target
1205      + Throws: `dsdl2.SDLException` if pixels failed to be read
1206      +/
1207     Surface readPixels(Rect rect, const PixelFormat format = PixelFormat.rgba8888) const @trusted
1208     in {
1209         assert(!format.indexed);
1210     }
1211     do {
1212         Surface surface = new Surface([rect.x, rect.x], format);
1213         if (SDL_RenderReadPixels(cast(SDL_Renderer*) this.sdlRenderer, &rect.sdlRect, format.sdlPixelFormatEnum,
1214                 surface.buffer.ptr, surface.pitch.to!int) != 0) {
1215             throw new SDLException;
1216         }
1217 
1218         return surface;
1219     }
1220 
1221     /++
1222      + Wraps `SDL_RenderPresent` which presents any appending changes to the renderer's target
1223      +/
1224     void present() @trusted {
1225         SDL_RenderPresent(this.sdlRenderer);
1226     }
1227 
1228     static if (sdlSupport >= SDLSupport.v2_0_4) {
1229         /++
1230          + Wraps `SDL_RenderIsClipEnabled` (from SDL 2.0.4) which checks whether a clipping rectangle is set in the
1231          + renderer
1232          +
1233          + Returns: `true` if a clipping rectangle is set, otherwise `false`
1234          +/
1235         bool hasClipRect() const @property @trusted
1236         in {
1237             assert(getVersion() >= Version(2, 0, 4));
1238         }
1239         do {
1240             return SDL_RenderIsClipEnabled(cast(SDL_Renderer*) this.sdlRenderer) == SDL_TRUE;
1241         }
1242     }
1243 
1244     static if (sdlSupport >= SDLSupport.v2_0_5) {
1245         /++
1246          + Wraps `SDL_RenderGetIntegerScale` (from SDL 2.0.5) which gets whether integer scales are forced
1247          +
1248          + Returns: `true` if integer scaling is enabled, otherwise `false`
1249          +/
1250         bool integerScaling() const @property @trusted
1251         in {
1252             assert(getVersion() >= Version(2, 0, 5));
1253         }
1254         do {
1255             return SDL_RenderGetIntegerScale(cast(SDL_Renderer*) this.sdlRenderer) == SDL_TRUE;
1256         }
1257 
1258         /++
1259          + Wraps `SDL_RenderSetIntegerScale` (from SDL 2.0.5) which sets whether integer scales should be forced
1260          +
1261          + Params:
1262          +   newScale = `true` to enable integer scaling, otherwise `false`
1263          + Throws: `dsdl2.SDLException` if failed to set integer scaling
1264          +/
1265         void integerScaling(bool newScale) @property @trusted
1266         in {
1267             assert(getVersion() >= Version(2, 0, 5));
1268         }
1269         do {
1270             if (SDL_RenderSetIntegerScale(this.sdlRenderer, newScale) != 0) {
1271                 throw new SDLException;
1272             }
1273         }
1274     }
1275 
1276     static if (sdlSupport >= SDLSupport.v2_0_8) {
1277         /++
1278          + Wraps `SDL_RenderGetMetalLayer` (from SDL 2.0.8) which gets the `CAMetalLayer` pointer associated with the
1279          + given Metal renderer
1280          +
1281          + Returns: pointer to the `CAMetalLayer`, otherwise `null` if not using a Metal renderer
1282          +/
1283         void* getMetalLayer() @system
1284         in {
1285             assert(getVersion() >= Version(2, 0, 8));
1286         }
1287         do {
1288             return SDL_RenderGetMetalLayer(this.sdlRenderer);
1289         }
1290 
1291         /++
1292          + Wraps `SDL_RenderGetMetalCommandEncoder` (from SDL 2.0.8) which gets the Metal command encoder for the
1293          + current frame
1294          +
1295          + Returns: ID of the `MTLRenderCommandEncoder`, otherwise `null` if not using a Metal renderer
1296          +/
1297         void* getMetalCommandEncoder() @system
1298         in {
1299             assert(getVersion() >= Version(2, 0, 8));
1300         }
1301         do {
1302             return SDL_RenderGetMetalCommandEncoder(this.sdlRenderer);
1303         }
1304     }
1305 
1306     static if (sdlSupport >= SDLSupport.v2_0_10) {
1307         /++
1308          + Wraps `SDL_RenderDrawPointF` (from SDL 2.0.10) which draws a single point at a given position with the
1309          + renderer's draw color
1310          +
1311          + Params:
1312          +   point = `dsdl2.FPoint` position the point is drawn at
1313          + Throws: `dsdl2.SDLException` if point failed to draw
1314          +/
1315         void drawPoint(FPoint point) @trusted
1316         in {
1317             assert(getVersion() >= Version(2, 0, 10));
1318         }
1319         do {
1320             if (SDL_RenderDrawPointF(this.sdlRenderer, point.x, point.y) != 0) {
1321                 throw new SDLException;
1322             }
1323         }
1324 
1325         /++
1326          + Wraps `SDL_RenderDrawPointsF` (from SDL 2.0.10) which draws multiple points at given positions with the
1327          + renderer's draw color
1328          +
1329          + Params:
1330          +   points = array of `dsdl2.FPoint` positions the points are drawn at
1331          + Throws: `dsdl2.SDLException` if points failed to draw
1332          +/
1333         void drawPoints(const FPoint[] points) @trusted
1334         in {
1335             assert(getVersion() >= Version(2, 0, 10));
1336         }
1337         do {
1338             if (SDL_RenderDrawPointsF(this.sdlRenderer, cast(SDL_FPoint*) points.ptr, points.length.to!int) != 0) {
1339                 throw new SDLException;
1340             }
1341         }
1342 
1343         /++
1344          + Wraps `SDL_RenderDrawLineF` (from SDL 2.0.10) which draws a line between two points with the renderer's draw
1345          + color
1346          +
1347          + Params:
1348          +   line = array of two `dsdl2.FPoint`s indicating the line's start and end
1349          + Throws: `dsdl2.SDLException` if line failed to draw
1350          +/
1351         void drawLine(FPoint[2] line) @trusted
1352         in {
1353             assert(getVersion() >= Version(2, 0, 10));
1354         }
1355         do {
1356             if (SDL_RenderDrawLineF(this.sdlRenderer, line[0].x, line[0].y, line[1].x, line[1].y) != 0) {
1357                 throw new SDLException;
1358             }
1359         }
1360 
1361         /++
1362          + Wraps `SDL_RenderDrawLinesF` (from SDL 2.0.10) which draws multiple lines following given points with the
1363          + renderer's draw color
1364          +
1365          + Params:
1366          +   points = array of `dsdl2.FPoint` edges the lines are drawn from and to
1367          + Throws: `dsdl2.SDLException` if lines failed to draw
1368          +/
1369         void drawLines(const FPoint[] points) @trusted
1370         in {
1371             assert(getVersion() >= Version(2, 0, 10));
1372         }
1373         do {
1374             if (SDL_RenderDrawLinesF(this.sdlRenderer, cast(SDL_FPoint*) points.ptr, points.length.to!int) != 0) {
1375                 throw new SDLException;
1376             }
1377         }
1378 
1379         /++
1380          + Wraps `SDL_RenderDrawRectF` (from SDL 2.0.10) which draws a rectangle's edges with the renderer's draw color
1381          +
1382          + Params:
1383          +   rect = `dsdl2.FRect` of the rectangle
1384          + Throws: `dsdl2.SDLException` if rectangle failed to draw
1385          +/
1386         void drawRect(FRect rect) @trusted
1387         in {
1388             assert(getVersion() >= Version(2, 0, 10));
1389         }
1390         do {
1391             if (SDL_RenderDrawRectF(this.sdlRenderer, &rect.sdlFRect) != 0) {
1392                 throw new SDLException;
1393             }
1394         }
1395 
1396         /++
1397          + Wraps `SDL_RenderDrawRectsF` (from SDL 2.0.10) which draws multiple rectangles' edges with the renderer's
1398          + draw color
1399          +
1400          + Params:
1401          +   rects = array of `dsdl2.FRect` of the rectangles
1402          + Throws: `dsdl2.SDLException` if rectangles failed to draw
1403          +/
1404         void drawRects(const FRect[] rects) @trusted
1405         in {
1406             assert(getVersion() >= Version(2, 0, 10));
1407         }
1408         do {
1409             if (SDL_RenderDrawRectsF(this.sdlRenderer, cast(SDL_FRect*) rects.ptr, rects.length.to!int) != 0) {
1410                 throw new SDLException;
1411             }
1412         }
1413 
1414         /++
1415          + Wraps `SDL_RenderFillRectF` (from SDL 2.0.10) which fills a rectangle with the renderer's draw color
1416          +
1417          + Params:
1418          +   rect = `dsdl2.FRect` of the rectangle
1419          + Throws: `dsdl2.SDLException` if rectangle failed to fill
1420          +/
1421         void fillRect(FRect rect) @trusted
1422         in {
1423             assert(getVersion() >= Version(2, 0, 10));
1424         }
1425         do {
1426             if (SDL_RenderFillRectF(this.sdlRenderer, &rect.sdlFRect) != 0) {
1427                 throw new SDLException;
1428             }
1429         }
1430 
1431         /++
1432          + Wraps `SDL_RenderFillRectsF` (from SDL 2.0.10) which fills multiple rectangles with the renderer's draw color
1433          +
1434          + Params:
1435          +   rects = array of `dsdl2.FRect` of the rectangles
1436          + Throws: `dsdl2.SDLException` if rectangles failed to fill
1437          +/
1438         void fillRects(const FRect[] rects) @trusted
1439         in {
1440             assert(getVersion() >= Version(2, 0, 10));
1441         }
1442         do {
1443             if (SDL_RenderFillRectsF(this.sdlRenderer, cast(SDL_FRect*) rects.ptr, rects.length.to!int) != 0) {
1444                 throw new SDLException;
1445             }
1446         }
1447 
1448         /++
1449          + Acts as `SDL_RenderCopyF(renderer, texture, NULL, destRect)` (from SDL 2.0.10) which copies the entire
1450          + texture to `destRect` at the renderer's target
1451          +
1452          + Params:
1453          +   texture = `dsdl2.Texture` to be copied/drawn
1454          +   destRect = destination `dsdl2.FRect` in the target for the texture to be drawn to
1455          + Throws: `dsdl2.SDLException` if texture failed to draw
1456          +/
1457         void copy(const Texture texture, FRect destRect) @trusted
1458         in {
1459             assert(getVersion() >= Version(2, 0, 10));
1460             assert(texture !is null);
1461         }
1462         do {
1463             if (SDL_RenderCopyF(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, null,
1464                     &destRect.sdlFRect) != 0) {
1465                 throw new SDLException;
1466             }
1467         }
1468 
1469         /++
1470          + Wraps `SDL_RenderCopyF` (from SDL 2.0.10) which copies a part of the texture at `srcRect` to `destRect` at
1471          + the renderer's target
1472          +
1473          + Params:
1474          +   texture = `dsdl2.Texture` to be copied/drawn
1475          +   destRect = destination `dsdl2.FRect` in the target for the texture to be drawn to
1476          +   srcRect = source `dsdl2.Rect` which clips the given texture
1477          + Throws: `dsdl2.SDLException` if texture failed to draw
1478          +/
1479         void copy(const Texture texture, FRect destRect, Rect srcRect) @trusted
1480         in {
1481             assert(getVersion() >= Version(2, 0, 10));
1482             assert(texture !is null);
1483         }
1484         do {
1485             if (SDL_RenderCopyF(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, &srcRect.sdlRect,
1486                     &destRect.sdlFRect) != 0) {
1487                 throw new SDLException;
1488             }
1489         }
1490 
1491         /++
1492          + Acts as `SDL_RenderCopyExF(renderer, texture, NULL, destRect, angle, NULL, flip)` (from SDL 2.0.10) which
1493          + copies the entire texture to `destRect` at the renderer's target with certain `angle` and flipping
1494          +
1495          + Params:
1496          +   texture = `dsdl2.Texture` to be copied/drawn
1497          +   destRect = destination `dsdl2.FRect` in the target for the texture to be drawn to
1498          +   angle = angle in degrees to rotate the texture counterclockwise
1499          +   flippedHorizontally = `true` to flip the texture horizontally, otherwise `false`
1500          +   flippedVertically = `true` to flip the texture vertically, otherwise `false`
1501          + Throws: `dsdl2.SDLException` if texture failed to draw
1502          +/
1503         void copyEx(const Texture texture, FRect destRect, double angle, bool flippedHorizontally = false,
1504             bool flippedVertically = false) @trusted
1505         in {
1506             assert(getVersion() >= Version(2, 0, 10));
1507             assert(texture !is null);
1508         }
1509         do {
1510             if (SDL_RenderCopyExF(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, null, &destRect.sdlFRect,
1511                     angle, null, (flippedHorizontally ? SDL_FLIP_HORIZONTAL : 0) |
1512                     (flippedVertically ? SDL_FLIP_VERTICAL : 0)) != 0) {
1513                 throw new SDLException;
1514             }
1515         }
1516 
1517         /++
1518          + Acts as `SDL_RenderCopyExF(renderer, texture, srcRect, destRect, angle, NULL, flip)` (from SDL 2.0.10) which
1519          + copies the entire texture to `destRect` at the renderer's target with certain `angle` and flipping
1520          +
1521          + Params:
1522          +   texture = `dsdl2.Texture` to be copied/drawn
1523          +   destRect = destination `dsdl2.FRect` in the target for the texture to be drawn to
1524          +   angle = angle in degrees to rotate the texture counterclockwise
1525          +   srcRect = source `dsdl2.Rect` which clips the given texture
1526          +   flippedHorizontally = `true` to flip the texture horizontally, otherwise `false`
1527          +   flippedVertically = `true` to flip the texture vertically, otherwise `false`
1528          + Throws: `dsdl2.SDLException` if texture failed to draw
1529          +/
1530         void copyEx(const Texture texture, FRect destRect, double angle, Rect srcRect, bool flippedHorizontally = false,
1531             bool flippedVertically = false) @trusted
1532         in {
1533             assert(getVersion() >= Version(2, 0, 10));
1534             assert(texture !is null);
1535         }
1536         do {
1537             if (SDL_RenderCopyExF(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, &srcRect.sdlRect,
1538                     &destRect.sdlFRect, angle, null, (flippedHorizontally ? SDL_FLIP_HORIZONTAL : 0) |
1539                     (flippedVertically ? SDL_FLIP_VERTICAL : 0)) != 0) {
1540                 throw new SDLException;
1541             }
1542         }
1543 
1544         /++
1545          + Acts as `SDL_RenderCopyExF(renderer, texture, NULL, destRect, angle, center, flip)` (from SDL 2.0.10) which
1546          + copies the entire texture to `destRect` at the renderer's target with certain `angle` and flipping
1547          +
1548          + Params:
1549          +   texture = `dsdl2.Texture` to be copied/drawn
1550          +   destRect = destination `dsdl2.FRect` in the target for the texture to be drawn to
1551          +   angle = angle in degrees to rotate the texture counterclockwise
1552          +   center = pivot `dsdl2.FPoint` of the texture for rotation
1553          +   flippedHorizontally = `true` to flip the texture horizontally, otherwise `false`
1554          +   flippedVertically = `true` to flip the texture vertically, otherwise `false`
1555          + Throws: `dsdl2.SDLException` if texture failed to draw
1556          +/
1557         void copyEx(const Texture texture, FRect destRect, double angle, FPoint center,
1558             bool flippedHorizontally = false, bool flippedVertically = false) @trusted
1559         in {
1560             assert(getVersion() >= Version(2, 0, 10));
1561             assert(texture !is null);
1562         }
1563         do {
1564             if (SDL_RenderCopyExF(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, null, &destRect.sdlFRect,
1565                     angle, &center.sdlFPoint, (flippedHorizontally ? SDL_FLIP_HORIZONTAL : 0) |
1566                     (flippedVertically ? SDL_FLIP_VERTICAL : 0)) != 0) {
1567                 throw new SDLException;
1568             }
1569         }
1570 
1571         /++
1572          + Wraps `SDL_RenderCopyExF` (from SDL 2.0.10) which copies the entire texture to `destRect` at the renderer's
1573          + target with certain `angle` and flipping
1574          +
1575          + Params:
1576          +   texture = `dsdl2.Texture` to be copied/drawn
1577          +   destRect = destination `dsdl2.FRect` in the target for the texture to be drawn to
1578          +   angle = angle in degrees to rotate the texture counterclockwise
1579          +   srcRect = source `dsdl2.Rect` which clips the given texture
1580          +   center = pivot `dsdl2.FPoint` of the texture for rotation
1581          +   flippedHorizontally = `true` to flip the texture horizontally, otherwise `false`
1582          +   flippedVertically = `true` to flip the texture vertically, otherwise `false`
1583          + Throws: `dsdl2.SDLException` if texture failed to draw
1584          +/
1585         void copyEx(const Texture texture, FRect destRect, double angle, Rect srcRect, FPoint center,
1586             bool flippedHorizontally = false, bool flippedVertically = false) @trusted
1587         in {
1588             assert(getVersion() >= Version(2, 0, 10));
1589             assert(texture !is null);
1590         }
1591         do {
1592             if (SDL_RenderCopyExF(this.sdlRenderer, cast(SDL_Texture*) texture.sdlTexture, &srcRect.sdlRect,
1593                     &destRect.sdlFRect, angle, &center.sdlFPoint, (flippedHorizontally ? SDL_FLIP_HORIZONTAL
1594                     : 0) | (flippedVertically ? SDL_FLIP_VERTICAL : 0)) != 0) {
1595                 throw new SDLException;
1596             }
1597         }
1598 
1599         /++
1600          + Wraps `SDL_RenderFlush` (from SDL 2.0.10) which executes and flushes all pending rendering operations
1601          +
1602          + Throws: `dsdl2.SDLException` if cannot flush
1603          +/
1604         void flush() @trusted
1605         in {
1606             assert(getVersion() >= Version(2, 0, 10));
1607         }
1608         do {
1609             if (SDL_RenderFlush(this.sdlRenderer) != 0) {
1610                 throw new SDLException;
1611             }
1612         }
1613     }
1614 
1615     static if (sdlSupport >= SDLSupport.v2_0_18) {
1616         /++
1617          + Wraps `SDL_RenderWindowToLogical` (from SDL 2.0.18) which maps window coordinates to logical coordinates
1618          +
1619          + Params:
1620          +   xy = `int[2]` window coordinate of X and Y
1621          + Returns: mapped `float[2]` logical coordinate of X and Y
1622          +/
1623         float[2] windowToLogical(int[2] xy) const @trusted
1624         in {
1625             assert(getVersion() >= Version(2, 0, 18));
1626         }
1627         do {
1628             float[2] fxy = void;
1629             SDL_RenderWindowToLogical(cast(SDL_Renderer*) this.sdlRenderer, xy[0], xy[1], &fxy[0], &fxy[1]);
1630             return fxy;
1631         }
1632 
1633         /++
1634          + Wraps `SDL_RenderLogicalToWindow` (from SDL 2.0.18) which maps logical coordinates to window coordinates
1635          +
1636          + Params:
1637          +   fxy = `float[2]` logical coordinate of X and Y
1638          + Returns: mapped `int[2]` window coordinate of X and Y
1639          +/
1640         int[2] logicalToWindow(float[2] fxy) const @trusted
1641         in {
1642             assert(getVersion() >= Version(2, 0, 18));
1643         }
1644         do {
1645             int[2] xy = void;
1646             SDL_RenderLogicalToWindow(cast(SDL_Renderer*) this.sdlRenderer, fxy[0], fxy[1], &xy[0], &xy[1]);
1647             return xy;
1648         }
1649 
1650         /++
1651          + Wraps `SDL_RenderGeometry` (from SDL 2.0.18) which renders triangles to the renderer's target
1652          +
1653          + Params:
1654          +   vertices = array of `dsdl2.Vertex`es of the triangles
1655          +   texture = `dsdl2.Texture` for the drawn triangles; `null` for none
1656          +   indices = array of `uint` indices for the vertices to be drawn (must be in multiples of three); `null`
1657          +             for order defined by `vertices` directly
1658          + Throws: `dsdl2.SDLException` if failed to render
1659          +/
1660         void renderGeometry(const Vertex[] vertices, Texture texture = null, const uint[] indices = null) @trusted {
1661             SDL_Texture* sdlTexture = texture is null ? null : texture.sdlTexture;
1662             if (SDL_RenderGeometry(this.sdlRenderer, sdlTexture, cast(SDL_Vertex*) vertices.ptr,
1663                     vertices.length.to!int, cast(int*) indices.ptr, indices.length.to!int) != 0) {
1664                 throw new SDLException;
1665             }
1666         }
1667 
1668         /++
1669          + Wraps `SDL_RenderSetVSync` which sets whether vertical synchronization should be enabled
1670          +
1671          + Params:
1672          +   vSync = `true` to enable v-sync, otherwise `false`
1673          + Throws: `dsdl2.SDLException` if failed to set v-sync
1674          +/
1675         void setVSync(bool vSync) @trusted {
1676             if (SDL_RenderSetVSync(this.sdlRenderer, vSync) != 0) {
1677                 throw new SDLException;
1678             }
1679         }
1680     }
1681 
1682     static if (sdlSupport >= SDLSupport.v2_0_22) {
1683         /++
1684          + Wraps `SDL_RenderGetWindow` (from SDL 2.0.22) which gets a `dsdl2.Window` proxy to the window associated
1685          + with the renderer
1686          +
1687          + Returns: `dsdl2.Window` proxy to the window
1688          + Throws: `dsdl2.SDLException` if failed to get window
1689          +/
1690         inout(Window) window() inout @property @trusted
1691         in {
1692             assert(getVersion() >= Version(2, 0, 22));
1693         }
1694         do {
1695             SDL_Window* sdlWindow = SDL_RenderGetWindow(cast(SDL_Renderer*) this.sdlRenderer);
1696             if (sdlWindow is null) {
1697                 throw new SDLException;
1698             }
1699 
1700             return cast(inout Window) new Window(sdlWindow, false, cast(void*) this);
1701         }
1702     }
1703 }