File: render_test.py

package info (click to toggle)
pysdl2 0.9.17%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,328 kB
  • sloc: python: 24,685; makefile: 36; sh: 8
file content (894 lines) | stat: -rw-r--r-- 31,536 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
import sys
import copy
import pytest
import ctypes
from ctypes import byref, POINTER, c_int, c_float, sizeof
import sdl2
from sdl2 import SDL_Init, SDL_Quit, SDL_INIT_EVERYTHING, SDL_GetError
import itertools
from sdl2.stdinc import Uint8, Uint32, SDL_TRUE, SDL_FALSE
from sdl2.rect import SDL_FPoint
from sdl2.pixels import SDL_Color
from sdl2 import video, surface, pixels, blendmode, rect
from sdl2.ext.compat import byteify, stringify
from sdl2.ext.pixelaccess import PixelView
from .conftest import _check_error_msg

# TODO: Write tests for more functions

def _create_window(pos, size, flags=video.SDL_WINDOW_HIDDEN):
    # Convenience function to create renderer and window for tests
    sdl2.SDL_ClearError()
    window = video.SDL_CreateWindow(
        b"Test", pos[0], pos[1], size[0], size[1], video.SDL_WINDOW_HIDDEN
    )
    assert window, _check_error_msg()
    assert isinstance(window.contents, video.SDL_Window)
    sdl2.SDL_ClearError()
    return window

def _get_renderflags():
    flags = sdl2.SDL_RENDERER_ACCELERATED
    if video.SDL_GetCurrentVideoDriver() == b"dummy":
        flags = sdl2.SDL_RENDERER_SOFTWARE
    return flags

@pytest.fixture(scope="module")
def supported_renderers(with_sdl):
    supported = []
    flags = _get_renderflags()
    for i in range(sdl2.SDL_GetNumRenderDrivers()):
        window = _create_window((30, 30), (100, 100))
        try:
            renderer = sdl2.SDL_CreateRenderer(window, i, flags)
        except OSError:
            renderer = None
        if (renderer and renderer.contents):
            supported.append(i)
            sdl2.SDL_DestroyRenderer(renderer)
        video.SDL_DestroyWindow(window)
    if not len(supported):
        raise RuntimeError("Unable to create a renderer with any backend")
    sdl2.SDL_ClearError()
    yield supported

@pytest.fixture
def testsurf(with_sdl):
    # Create a solid black surface for tests
    sf = surface.SDL_CreateRGBSurface(
        0, 100, 100, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF
    )
    assert sf, _check_error_msg()
    pixfmt = sf.contents.format.contents
    fill = pixels.SDL_MapRGBA(pixfmt, 0, 0, 0, 255)
    ret = surface.SDL_FillRect(sf, None, fill)
    assert ret == 0, _check_error_msg()
    yield sf
    surface.SDL_FreeSurface(sf)

@pytest.fixture
def sw_renderer(testsurf):
    renderer = sdl2.SDL_CreateSoftwareRenderer(testsurf)
    assert renderer, _check_error_msg()
    assert isinstance(renderer.contents, sdl2.SDL_Renderer)
    yield (renderer, testsurf)
    sdl2.SDL_DestroyRenderer(renderer)

@pytest.fixture
def with_renderer(with_sdl):
    flags = _get_renderflags()
    sdl2.SDL_ClearError()
    window = video.SDL_CreateWindow(
        b"Test", 30, 30, 100, 100, video.SDL_WINDOW_HIDDEN
    )
    assert window, _check_error_msg()
    assert isinstance(window.contents, sdl2.SDL_Window)
    sdl2.SDL_ClearError()
    renderer = sdl2.SDL_CreateRenderer(window, -1, flags)
    assert renderer, _check_error_msg()
    yield (renderer, window)
    sdl2.SDL_DestroyRenderer(renderer)
    video.SDL_DestroyWindow(window)

@pytest.fixture
def texture(with_renderer):
    renderer, win = with_renderer
    fmt = pixels.SDL_PIXELFORMAT_ARGB8888
    access = sdl2.SDL_TEXTUREACCESS_STREAMING
    tx = sdl2.SDL_CreateTexture(renderer, fmt, access, 16, 16)
    assert tx, _check_error_msg()
    assert isinstance(tx.contents, sdl2.SDL_Texture)
    yield tx
    sdl2.SDL_DestroyTexture(tx)


# Test structs and classes

def test_SDL_RendererInfo():
    # Tested extensively in SDL_GetRenderDriverInfo
    info = sdl2.SDL_RendererInfo()
    assert isinstance(info, sdl2.SDL_RendererInfo)

def test_SDL_Renderer():
    val = sdl2.SDL_Renderer()
    assert isinstance(val, sdl2.SDL_Renderer)

def test_SDL_Texture():
    val = sdl2.SDL_Texture()
    assert isinstance(val, sdl2.SDL_Texture)


class TestSDLVertex(object):
    __tags__ = ["sdl"]

    def test_init(self):
        # Test creating an SDL vertex without any args
        vtx = sdl2.SDL_Vertex()
        assert type(vtx.position) == rect.SDL_FPoint
        assert type(vtx.color) == pixels.SDL_Color
        assert type(vtx.tex_coord) == rect.SDL_FPoint

        # Test creating a vertex with a custom position and color
        pos = rect.SDL_FPoint(20, 30)
        col = pixels.SDL_Color(255, 0, 0, 255)
        vtx2 = sdl2.SDL_Vertex(pos, col)
        assert vtx2.position.x == 20 and vtx2.position.y == 30
        assert vtx2.color.r == 255 and vtx2.color.g == 0

        # Test creating an SDL vertex using Python types
        vtx3 = sdl2.SDL_Vertex([15, 25], [128, 127, 126], [5, 5])
        assert vtx3.position.x == 15 and vtx3.position.y == 25
        assert vtx3.color.r == 128 and vtx3.color.g == 127
        assert vtx3.color.a == 255
        assert vtx3.tex_coord.x == 5

        # Test exceptions on bad input
        with pytest.raises(ValueError):
            sdl2.SDL_Vertex(10)
        with pytest.raises(ValueError):
            sdl2.SDL_Vertex(color="red")

    def test_repr(self):
        vtx = sdl2.SDL_Vertex([1.5, 4], [0, 0, 0, 255])
        assert repr(vtx) == "SDL_Vertex(x=1.5, y=4.0, color=[0, 0, 0, 255])"

    def test_copy(self):
        vtx = sdl2.SDL_Vertex([15, 25], [128, 127, 126], [5, 5])
        vtx2 = copy.copy(vtx)
        assert vtx.position == vtx2.position
        assert vtx.color == vtx2.color
        assert vtx.tex_coord == vtx2.tex_coord
        # Make sure editing the new copy doesn't affect the original
        vtx2.position.x = 7
        vtx2.color.r = 200
        vtx2.tex_coord.y = 7
        assert vtx.position != vtx2.position
        assert vtx.color != vtx2.color
        assert vtx.tex_coord != vtx2.tex_coord


# Test SDL2 renderer bindings

def test_SDL_GetNumRenderDrivers(with_sdl):
    val = sdl2.SDL_GetNumRenderDrivers()
    assert val >= 1

def test_SDL_GetRenderDriverInfo(with_sdl):
    renderers = []
    errs = []
    pxformats = {}
    drivers = sdl2.SDL_GetNumRenderDrivers()
    for x in range(drivers):
        sdl2.SDL_ClearError()
        info = sdl2.SDL_RendererInfo()
        ret = sdl2.SDL_GetRenderDriverInfo(x, info)
        if ret != 0:
            err = stringify(sdl2.SDL_GetError())
            errs.append("Renderer {0} error: {1}".format(x, err))
            continue
        rname = stringify(info.name)
        renderers.append(rname)
        pxformats[rname] = []
        for i in range(info.num_texture_formats):
            fmt_name = pixels.SDL_GetPixelFormatName(info.texture_formats[i])
            pxformats[rname].append(stringify(fmt_name).split("_")[-1])
    assert len(renderers)
    assert "software" in renderers
    print("Render drivers supported by current SDL2 binary:")
    print(renderers)
    print("\nTexture formats supported by each renderer:")
    for rname in renderers:
        print(rname)
        print(" - " + " ".join(pxformats[rname]))

def test_SDL_CreateWindowAndRenderer(with_sdl):
    window = POINTER(video.SDL_Window)()
    renderer = POINTER(sdl2.SDL_Renderer)()
    ret = sdl2.SDL_CreateWindowAndRenderer(
        10, 10, video.SDL_WINDOW_HIDDEN, byref(window), byref(renderer)
    )
    sdl2.SDL_DestroyRenderer(renderer)
    video.SDL_DestroyWindow(window)
    assert ret == 0, _check_error_msg()

def test_SDL_CreateDestroyRenderer(supported_renderers):
    flags = _get_renderflags()
    errs = {}
    rcount = sdl2.SDL_GetNumRenderDrivers()
    for i in range(rcount):
        window = _create_window((30, 30), (100, 100))
        try:
            renderer = sdl2.SDL_CreateRenderer(window, i, flags)
        except OSError:
            renderer = None
        if (renderer and renderer.contents):
            assert isinstance(renderer.contents, sdl2.SDL_Renderer)
            sdl2.SDL_DestroyRenderer(renderer)
        else:
            name = "Renderer {0}".format(i)
            errs[name] = stringify(sdl2.SDL_GetError())
        video.SDL_DestroyWindow(window)
    assert rcount > len(errs.keys()) # Make sure at least one working renderer

def test_SDL_CreateSoftwareRenderer(with_sdl):
    sf = surface.SDL_CreateRGBSurface(
        0, 100, 100, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF
    )
    renderer = sdl2.SDL_CreateSoftwareRenderer(sf)
    assert renderer, _check_error_msg()
    assert isinstance(renderer.contents, sdl2.SDL_Renderer)
    sdl2.SDL_DestroyRenderer(renderer)
    surface.SDL_FreeSurface(sf)

def test_SDL_GetRenderer(supported_renderers):
    flags = _get_renderflags()
    usable = 0
    for i in supported_renderers:
        window = _create_window((30, 30), (100, 100))
        renderer = sdl2.SDL_CreateRenderer(window, i, flags)
        if (renderer and renderer.contents):
            usable += 1
            sdl2.SDL_ClearError()
            ren = sdl2.SDL_GetRenderer(window)
            assert ren, _check_error_msg()
            assert isinstance(ren.contents, sdl2.SDL_Renderer)
            sdl2.SDL_DestroyRenderer(renderer)
            assert not sdl2.SDL_GetRenderer(window)
        video.SDL_DestroyWindow(window)
    assert usable > 0

@pytest.mark.skipif(sdl2.dll.version < 2022, reason="not available")
def test_SDL_RenderGetWindow(supported_renderers):
    flags = _get_renderflags()
    for i in supported_renderers:
        window = _create_window((30, 30), (100, 100))
        renderer = sdl2.SDL_CreateRenderer(window, i, flags)
        if (renderer and renderer.contents):
            sdl2.SDL_ClearError()
            win = sdl2.SDL_RenderGetWindow(renderer)
            assert win, _check_error_msg()
            assert isinstance(win.contents, sdl2.SDL_Window)
            assert sdl2.SDL_GetWindowID(window) == sdl2.SDL_GetWindowID(win)
            sdl2.SDL_DestroyRenderer(renderer)
            assert not sdl2.SDL_GetRenderer(win)
        video.SDL_DestroyWindow(window)


def test_SDL_GetRendererInfo(with_sdl):
    renderers = []
    max_sizes = {}
    errs = []
    flags = _get_renderflags()
    rcount = sdl2.SDL_GetNumRenderDrivers()
    for i in range(rcount):
        sdl2.SDL_ClearError()
        window = _create_window((30, 30), (100, 100))
        try:
            renderer = sdl2.SDL_CreateRenderer(window, i, flags)
        except OSError:
            renderer = None
        if not (renderer and renderer.contents):
            err = stringify(sdl2.SDL_GetError())
            errs.append("Unable to create renderer {0}: {1}".format(i, err))
            video.SDL_DestroyWindow(window)
            continue
        assert isinstance(renderer.contents, sdl2.SDL_Renderer)
        info = sdl2.SDL_RendererInfo()
        ret = sdl2.SDL_GetRendererInfo(renderer, byref(info))
        if ret == 0:
            rname = stringify(info.name)
            max_size = (info.max_texture_width, info.max_texture_height)
            renderers.append(rname)
            max_sizes[rname] = max_size
        else:
            err = stringify(sdl2.SDL_GetError())
            errs.append("Renderer {0} error: {1}".format(i, err))
        sdl2.SDL_DestroyRenderer(renderer)
        video.SDL_DestroyWindow(window)
    assert len(renderers)
    assert "software" in renderers
    print("Render drivers loadable on the current system:")
    for rname in renderers:
        w, h = max_sizes[rname]
        print(" - " + rname + " (max texture size: {0}x{1})".format(w, h))

@pytest.mark.skip("not implemented")
def test_SDL_GetRendererOutputSize(self):
    pass

def test_SDL_CreateDestroyTexture(with_renderer):
    renderer, win = with_renderer
    formats = (
        pixels.SDL_PIXELFORMAT_ARGB8888,
        pixels.SDL_PIXELFORMAT_RGB555,
        pixels.SDL_PIXELFORMAT_RGBA4444,
        pixels.SDL_PIXELFORMAT_RGBA8888,
        pixels.SDL_PIXELFORMAT_ARGB2101010,
        pixels.SDL_PIXELFORMAT_YUY2,
    )
    access = (
        sdl2.SDL_TEXTUREACCESS_STATIC,
        sdl2.SDL_TEXTUREACCESS_STREAMING,
        sdl2.SDL_TEXTUREACCESS_TARGET,
    )
    sizes = [(4, 4), (7, 7), (64, 32), (256, 256), (512, 512)]
    for fmt in formats:
        for acc in access:
            for w, h in sizes:
                sdl2.SDL_ClearError()
                tx = sdl2.SDL_CreateTexture(renderer, fmt, acc, w, h)
                assert tx, _check_error_msg()
                assert isinstance(tx.contents, sdl2.SDL_Texture)
                sdl2.SDL_DestroyTexture(tx)
    # Test SDL error on bad input
    sdl2.SDL_CreateTexture(
        renderer, pixels.SDL_PIXELFORMAT_RGB555, 1, -8, 8
    )
    assert len(SDL_GetError()) > 0

def test_SDL_CreateTextureFromSurface(with_renderer, testsurf):
    renderer, win = with_renderer
    tx = sdl2.SDL_CreateTextureFromSurface(renderer, testsurf)
    assert tx, _check_error_msg()
    assert isinstance(tx.contents, sdl2.SDL_Texture)
    sdl2.SDL_DestroyTexture(tx)

def test_SDL_QueryTexture(with_renderer):
    renderer, win = with_renderer
    formats = (
        pixels.SDL_PIXELFORMAT_ARGB8888,
        pixels.SDL_PIXELFORMAT_RGB555,
        pixels.SDL_PIXELFORMAT_RGBA4444,
        pixels.SDL_PIXELFORMAT_RGBA8888,
        pixels.SDL_PIXELFORMAT_ARGB2101010,
        pixels.SDL_PIXELFORMAT_YUY2,
    )
    access = (
        sdl2.SDL_TEXTUREACCESS_STATIC,
        sdl2.SDL_TEXTUREACCESS_STREAMING,
        sdl2.SDL_TEXTUREACCESS_TARGET,
    )
    sizes = [(4, 4), (7, 7), (64, 32), (256, 256), (512, 512)]
    for fmt in formats:
        for acc in access:
            for w, h in sizes:
                tx = sdl2.SDL_CreateTexture(renderer, fmt, acc, w, h)
                assert isinstance(tx.contents, sdl2.SDL_Texture)
                txf, txa, txw, txh = Uint32(0), c_int(0), c_int(0), c_int(0)
                ret = sdl2.SDL_QueryTexture(
                    tx, byref(txf), byref(txa), byref(txw), byref(txh)
                )
                assert ret == 0, _check_error_msg()
                assert txf.value == fmt
                assert txa.value == acc
                assert txw.value == w
                assert txh.value == h
                sdl2.SDL_DestroyTexture(tx)

def test_SDL_GetSetTextureColorMod(texture):
    colors = [
        (16, 22, 185),
        (32, 64, 128),
        (64, 32, 128),
        (64, 32, 255),
        (255, 32, 128),
        (255, 255, 255),
        (128, 128, 128),
        (0, 0, 0),
    ]
    for r, g, b in colors:
        ret = sdl2.SDL_SetTextureColorMod(texture, r, g, b)
        assert ret == 0, _check_error_msg()
        tr, tg, tb = Uint8(0), Uint8(0), Uint8(0)
        ret = sdl2.SDL_GetTextureColorMod(
            texture, byref(tr), byref(tg), byref(tb)
        )
        assert ret == 0, _check_error_msg()
        assert (tr.value, tg.value, tb.value) == (r, g, b)

def test_SDL_GetSetTextureAlphaMod(texture):
    for alpha in range(0, 255, 7):
        ret = sdl2.SDL_SetTextureAlphaMod(texture, alpha)
        assert ret == 0, _check_error_msg()
        talpha = Uint8(0)
        ret = sdl2.SDL_GetTextureAlphaMod(texture, byref(talpha))
        assert ret == 0, _check_error_msg()
        assert talpha.value == alpha

def test_SDL_GetSetTextureBlendMode(texture):
    modes = (
        blendmode.SDL_BLENDMODE_NONE,
        blendmode.SDL_BLENDMODE_ADD,
        blendmode.SDL_BLENDMODE_BLEND,
        blendmode.SDL_BLENDMODE_MOD,
    )
    for mode in modes:
        ret = sdl2.SDL_SetTextureBlendMode(texture, mode)
        assert ret == 0, _check_error_msg()
        tmode = blendmode.SDL_BlendMode()
        ret = sdl2.SDL_GetTextureBlendMode(texture, byref(tmode))
        assert ret == 0, _check_error_msg()
        assert tmode.value == mode

@pytest.mark.skipif(sdl2.dll.version < 2012, reason="not available")
def test_SDL_GetSetTextureScaleMode(texture):
    modes = (
        sdl2.SDL_ScaleModeNearest,
        sdl2.SDL_ScaleModeLinear,
        sdl2.SDL_ScaleModeBest,
    )
    for mode in modes:
        ret = sdl2.SDL_SetTextureScaleMode(texture, mode)
        assert ret == 0, _check_error_msg()
        tmode = sdl2.SDL_ScaleMode()
        ret = sdl2.SDL_GetTextureScaleMode(texture, byref(tmode))
        assert ret == 0, _check_error_msg()
        assert tmode.value == mode

@pytest.mark.skipif(sdl2.dll.version < 2018, reason="not available")
def test_SDL_GetSetTextureUserData(texture):
    # Create some user data and add it to the texture
    dat_raw = ctypes.c_char_p(b"hello!")
    dat = ctypes.cast(dat_raw, ctypes.c_void_p)
    ret = sdl2.SDL_SetTextureUserData(texture, dat)
    assert ret == 0, _check_error_msg()
    # Try retrieving the user data
    dat_ptr = sdl2.SDL_GetTextureUserData(texture)
    assert dat_ptr, _check_error_msg()
    dat_out = ctypes.cast(dat_ptr, ctypes.c_char_p)
    assert dat_raw.value == dat_out.value

@pytest.mark.skip("not implemented")
def test_SDL_UpdateTexture(texture):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_UpdateYUVTexture(with_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2016, reason="not available")
def test_SDL_UpdateNVTexture(with_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_LockUnlockTexture(texture):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2012, reason="not available")
def test_SDL_LockTextureToSurface(texture):
    pass

def test_SDL_RenderTargetSupported(supported_renderers):
    flags = _get_renderflags()
    usable = 0
    for i in supported_renderers:
        window = _create_window((30, 30), (100, 100))
        renderer = sdl2.SDL_CreateRenderer(window, i, flags)
        if (renderer and renderer.contents):
            usable += 1
            assert isinstance(renderer.contents, sdl2.SDL_Renderer)
            val = sdl2.SDL_RenderTargetSupported(renderer)
            assert val in (SDL_TRUE, SDL_FALSE)
            sdl2.SDL_DestroyRenderer(renderer)
        video.SDL_DestroyWindow(window)
    assert usable > 0

def test_SDL_GetSetRenderTarget(supported_renderers):
    # First, determine which renderers support render targets
    flags = _get_renderflags()
    usable = 0
    supports_targets = []
    for i in supported_renderers:
        window = _create_window((30, 30), (100, 100))
        renderer = sdl2.SDL_CreateRenderer(window, i, flags)
        if (renderer and renderer.contents):
            usable += 1
            assert isinstance(renderer.contents, sdl2.SDL_Renderer)
            if sdl2.SDL_RenderTargetSupported(renderer) == SDL_TRUE:
                supports_targets.append(i)
            sdl2.SDL_DestroyRenderer(renderer)
        video.SDL_DestroyWindow(window)
    assert usable > 0
    if len(supports_targets) == 0:
        pytest.skip("No available renderer supports targets")

    # For renderers that support render targets, try setting/getting one
    pixfmt = pixels.SDL_PIXELFORMAT_ARGB8888
    for i in supports_targets:
        window = _create_window((30, 30), (100, 100))
        sdl2.SDL_ClearError()
        renderer = sdl2.SDL_CreateRenderer(window, i, flags)
        # Try setting a texture as the render target
        tex = sdl2.SDL_CreateTexture(
            renderer, pixfmt, sdl2.SDL_TEXTUREACCESS_TARGET, 10, 10
        )
        ret = sdl2.SDL_SetRenderTarget(renderer, tex)
        assert ret == 0, _check_error_msg()
        sdl2.SDL_ClearError()
        tgt = sdl2.SDL_GetRenderTarget(renderer)
        assert tgt, _check_error_msg()
        assert isinstance(tgt.contents, sdl2.SDL_Texture)
        # Try setting NULL as the render target (resets target to window)
        ret = sdl2.SDL_SetRenderTarget(renderer, None)
        assert ret == 0
        assert not sdl2.SDL_GetRenderTarget(renderer)
        # Clean up before the next renderer
        sdl2.SDL_DestroyTexture(tex)
        sdl2.SDL_DestroyRenderer(renderer)
        video.SDL_DestroyWindow(window)

def test_SDL_RenderGetSetLogicalSize(sw_renderer):
    renderer, sf = sw_renderer
    BLACK_RGBA = 0x000000FF
    GREEN_RGBA = 0x00FF00FF

    # Try setting the logical size to 1/10 of normal
    ret = sdl2.SDL_RenderSetLogicalSize(renderer, 10, 10)
    assert ret == 0, _check_error_msg()
    lw, lh = c_int(0), c_int(0)
    sdl2.SDL_RenderGetLogicalSize(renderer, byref(lw), byref(lh))
    assert [lw.value, lh.value] == [10, 10]

    # Try drawing a single pixel to the renderer and see if fills 10x10
    ret = sdl2.SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255)
    assert ret == 0
    sdl2.SDL_RenderDrawPoint(renderer, 1, 1)
    view = PixelView(sf)
    assert view[1][1] == BLACK_RGBA
    assert view[10][10] == GREEN_RGBA
    assert view[19][19] == GREEN_RGBA
    assert view[9][9] == BLACK_RGBA
    assert view[20][20] == BLACK_RGBA

    # Reset the logical size back to its original values and try drawing again
    ret = sdl2.SDL_RenderSetLogicalSize(renderer, 0, 0)
    assert ret == 0
    sdl2.SDL_RenderDrawPoint(renderer, 1, 1)
    assert view[1][1] == GREEN_RGBA
    assert view[0][0] == BLACK_RGBA
    assert view[2][2] == BLACK_RGBA
    del view

def test_SDL_RenderGetSetViewport(sw_renderer):
    renderer, sf = sw_renderer
    rects = (
        rect.SDL_Rect(1, 1, 1, 1),
        rect.SDL_Rect(0, 0, 10, 10),
        rect.SDL_Rect(3, 3, 5, 5),
        rect.SDL_Rect(-5, -5, 10, 10),
        rect.SDL_Rect(10, 10, 20, 20),
        rect.SDL_Rect(50, 50, 20, 30),
        rect.SDL_Rect(0, -10, 10, 10),
    )
    # First, try setting viewport to whole window
    ret = sdl2.SDL_RenderSetViewport(renderer, None)
    assert ret == 0, _check_error_msg()
    vport = rect.SDL_Rect()
    sdl2.SDL_RenderGetViewport(renderer, byref(vport))
    assert vport == rect.SDL_Rect(0, 0, 100, 100)
    # Then, try setting it to different sizes
    for r in rects:
        ret = sdl2.SDL_RenderSetViewport(renderer, r)
        assert ret == 0, _check_error_msg()
        sdl2.SDL_RenderGetViewport(renderer, byref(vport))
        assert vport == r

@pytest.mark.skip("not implemented")
def test_SDL_RenderGetSetClipRect(with_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_RenderIsClipEnabled(with_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_RenderGetSetScale(with_renderer):
    pass

@pytest.mark.skipif(sdl2.dll.version < 2018, reason="not available")
def test_SDL_RenderWindowToLogical(with_renderer):
    renderer, win = with_renderer
    wx, wy = (c_int(0), c_int(0))  # window coords
    lx, ly = (c_float(0), c_float(0))  # renderer coords
    # Test without resizing
    sdl2.SDL_RenderWindowToLogical(renderer, 50, 50, byref(lx), byref(ly))
    assert lx.value == 50
    assert ly.value == 50
    sdl2.SDL_RenderLogicalToWindow(renderer, 50, 50, byref(wx), byref(wy))
    assert wx.value == 50
    assert wy.value == 50
    # Set custom scaling on the renderer
    ret = sdl2.SDL_RenderSetScale(renderer, 2.0, 0.5)
    assert ret == 0, _check_error_msg()
    # Test again after resizing
    sdl2.SDL_RenderWindowToLogical(renderer, 50, 50, byref(lx), byref(ly))
    assert lx.value == 25
    assert ly.value == 100
    sdl2.SDL_RenderLogicalToWindow(renderer, 50, 50, byref(wx), byref(wy))
    assert wx.value == 100
    assert wy.value == 25

def test_SDL_RenderGetSetIntegerScale(with_renderer):
    renderer, win = with_renderer
    assert sdl2.SDL_RenderGetIntegerScale(renderer) == SDL_FALSE
    assert sdl2.SDL_RenderSetIntegerScale(renderer, SDL_FALSE) == 0
    assert sdl2.SDL_RenderGetIntegerScale(renderer) == SDL_FALSE
    assert sdl2.SDL_RenderSetIntegerScale(renderer, SDL_TRUE) == 0
    assert sdl2.SDL_RenderGetIntegerScale(renderer) == SDL_TRUE
    assert sdl2.SDL_RenderSetIntegerScale(renderer, SDL_FALSE) == 0
    assert sdl2.SDL_RenderGetIntegerScale(renderer) == SDL_FALSE

def test_SDL_GetSetRenderDrawColor(with_renderer):
    renderer, win = with_renderer
    colors = (
        (16, 22, 185, 217),
        (32, 64, 128, 255),
        (64, 32, 128, 255),
        (255, 32, 64, 128),
        (255, 255, 255, 255),
        (128, 128, 128, 255),
        (0, 0, 0, 0),
    )
    for r, g, b, a in colors:
        ret = sdl2.SDL_SetRenderDrawColor(renderer, r, g, b, a)
        assert ret == 0, _check_error_msg()
        rr, rg, rb, ra = Uint8(0), Uint8(0), Uint8(0), Uint8(0)
        ret = sdl2.SDL_GetRenderDrawColor(
            renderer, byref(rr), byref(rg), byref(rb), byref(ra)
        )
        assert ret == 0
        assert [v.value for v in (rr, rg, rb, ra)] == [r, g, b, a]

def test_SDL_GetSetRenderDrawBlendMode(with_renderer):
    renderer, win = with_renderer
    modes = [
        blendmode.SDL_BLENDMODE_NONE,
        blendmode.SDL_BLENDMODE_ADD,
        blendmode.SDL_BLENDMODE_BLEND,
        blendmode.SDL_BLENDMODE_MOD,
    ]
    for mode in modes:
        ret = sdl2.SDL_SetRenderDrawBlendMode(renderer, mode)
        assert ret == 0, _check_error_msg()
        bmode = blendmode.SDL_BlendMode()
        ret = sdl2.SDL_GetRenderDrawBlendMode(renderer, byref(bmode))
        assert ret == 0
        assert bmode.value == mode

def test_SDL_RenderClear(sw_renderer):
    renderer, sf = sw_renderer
    # Create a pixel view for the target surface and verify it's black
    view = PixelView(sf)
    assert view[0][0] == 0x000000FF
    # Set renderer draw color to white and then clear the renderer
    ret = sdl2.SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255)
    assert ret == 0
    ret = sdl2.SDL_RenderClear(renderer)
    assert ret == 0
    # Check whether the target surface is now white
    assert view[0][0] == 0xFFFFFFFF
    del view

def test_SDL_RenderDrawPoint(sw_renderer):
    renderer, sf = sw_renderer
    points = (
        (1, 1), (4, 6), (10, 10), (96, 92),
        (4, 22), (57, 88), (45, 15)
    )
    r, g, b, a = 0xAA, 0xBB, 0xCC, 0xDD
    color = pixels.SDL_MapRGBA(sf.contents.format, r, g, b, a)
    ret = sdl2.SDL_SetRenderDrawColor(renderer, r, g, b, a)
    assert ret == 0
    for x, y in points:
        ret = sdl2.SDL_RenderDrawPoint(renderer, x, y)
        assert ret == 0
    sdl2.SDL_RenderPresent(renderer)
    # Check whether the points were actually drawn
    view = PixelView(sf)
    for x, y in points:
        # NOTE: Assumes no points are adjacent or on surface edge 
        assert hex(view[y][x]) == hex(color)
        assert hex(view[y+1][x+1]) != hex(color)
        assert hex(view[y-1][x-1]) != hex(color)
    del view

@pytest.mark.skip("not implemented")
def test_SDL_RenderDrawPoints(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_RenderDrawLine(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_RenderDrawLines(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_RenderDrawRect(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_RenderDrawRects(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_RenderFillRect(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_RenderFillRects(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_RenderCopy(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_RenderCopyEx(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2010, reason="not available")
def test_SDL_RenderDrawPointF(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2010, reason="not available")
def test_SDL_RenderDrawPointsF(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2010, reason="not available")
def test_SDL_RenderDrawLineF(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2010, reason="not available")
def test_SDL_RenderDrawLinesF(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2010, reason="not available")
def test_SDL_RenderDrawRectF(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2010, reason="not available")
def test_SDL_RenderDrawRectsF(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2010, reason="not available")
def test_SDL_RenderFillRectF(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2010, reason="not available")
def test_SDL_RenderFillRectsF(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2010, reason="not available")
def test_SDL_RenderCopyF(sw_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2010, reason="not available")
def test_SDL_RenderCopyExF(sw_renderer):
    pass

@pytest.mark.skipif(sdl2.dll.version < 2018, reason="not available")
def test_SDL_RenderGeometry(sw_renderer):
    renderer, target = sw_renderer
    # Create vertices for rendering
    RED = (255, 0, 0, 255)
    vertices = [
        sdl2.SDL_Vertex((0, 0), RED),
        sdl2.SDL_Vertex((0, 50), RED),
        sdl2.SDL_Vertex((50, 50), RED)
    ]
    # Try rendering the vertices
    vtx = (sdl2.SDL_Vertex * len(vertices))(*vertices)
    ret = sdl2.SDL_RenderGeometry(
        renderer, None, vtx, len(vertices), None, 0
    )
    assert ret == 0, _check_error_msg()
    # TODO: Actually check the surface for the rendered triangle

@pytest.mark.skipif(sdl2.dll.version < 2018, reason="not available")
def test_SDL_RenderGeometryRaw(sw_renderer):
    renderer, target = sw_renderer
    # Create vertices for rendering
    RED = SDL_Color(255, 0, 0, 255)
    points = [0, 0, 0, 50, 50, 50]
    colors = [RED, RED, RED]
    tex_coords = [0, 0, 0, 0, 0, 0]
    # Convert vertices to ctypes-friendly format
    xy_size = sizeof(c_float) * 2
    col_size = sizeof(SDL_Color)
    xy = (c_float * len(points))(*points)
    col = (SDL_Color * len(colors))(*colors)
    uv = (c_float * len(tex_coords))(*tex_coords)
    # Try rendering the vertices
    ret = sdl2.SDL_RenderGeometryRaw(
        renderer, None,
        xy, xy_size,
        col, col_size,
        uv, xy_size,
        3, None, 0, 1
    )
    assert ret == 0, _check_error_msg()
    # Check the surface for the rendered triangle
    RED_RGBA = 0xFF0000FF
    BLACK_RGBA = 0x000000FF
    view = PixelView(target)
    assert view[25][5] == RED_RGBA
    assert view[99][99] == BLACK_RGBA
    del view

@pytest.mark.skip("not implemented")
def test_SDL_RenderReadPixels(with_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_RenderPresent(with_renderer):
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2010, reason="not available")
def test_SDL_RenderFlush(with_renderer):
    pass

@pytest.mark.skip("not implemented")
def test_SDL_GL_BindTexture(with_sdl):
    # NOTE: Needs OpenGL renderer
    pass

@pytest.mark.skip("not implemented")
def test_SDL_GL_UnbindTexture(with_sdl):
    # NOTE: Needs OpenGL renderer
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2008, reason="not available")
def test_SDL_RendererGetMetalLayer(with_renderer):
    # NOTE: Needs metal renderer
    pass

@pytest.mark.skip("not implemented")
@pytest.mark.skipif(sdl2.dll.version < 2008, reason="not available")
def test_SDL_RendererGetMetalCommandEncoder(with_renderer):
    # NOTE: Needs metal renderer
    pass

@pytest.mark.skipif(sdl2.dll.version < 2018, reason="not available")
def test_SDL_RenderSetVSync(with_renderer):
    renderer, win = with_renderer
    # Not super thorough, but hard to test more extensively
    ret = sdl2.SDL_RenderSetVSync(renderer, 1)
    assert ret <= 0
    ret = sdl2.SDL_RenderSetVSync(renderer, 0)
    assert ret <= 0