summaryrefslogtreecommitdiff
path: root/src/contrib/SDL-3.2.20/test/testsprite.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/testsprite.c')
-rw-r--r--src/contrib/SDL-3.2.20/test/testsprite.c597
1 files changed, 597 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/test/testsprite.c b/src/contrib/SDL-3.2.20/test/testsprite.c
new file mode 100644
index 0000000..2f202d6
--- /dev/null
+++ b/src/contrib/SDL-3.2.20/test/testsprite.c
@@ -0,0 +1,597 @@
1/*
2 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely.
11*/
12/* Simple program: Move N sprites around on the screen as fast as possible */
13
14#define SDL_MAIN_USE_CALLBACKS 1
15#include <SDL3/SDL_test.h>
16#include <SDL3/SDL_test_common.h>
17#include <SDL3/SDL_main.h>
18#include "testutils.h"
19
20#define NUM_SPRITES 100
21#define MAX_SPEED 1
22
23static SDLTest_CommonState *state;
24static const char *icon = "icon.bmp";
25static int num_sprites;
26static SDL_Texture **sprites;
27static bool cycle_color;
28static bool cycle_alpha;
29static int cycle_direction = 1;
30static int current_alpha = 0;
31static int current_color = 0;
32static SDL_FRect *positions;
33static SDL_FRect *velocities;
34static float sprite_w, sprite_h;
35static SDL_BlendMode blendMode = SDL_BLENDMODE_BLEND;
36static Uint64 next_fps_check;
37static Uint32 frames;
38static const int fps_check_delay = 5000;
39static int use_rendergeometry = 0;
40static bool suspend_when_occluded;
41
42/* Number of iterations to move sprites - used for visual tests. */
43/* -1: infinite random moves (default); >=0: enables N deterministic moves */
44static int iterations = -1;
45
46void SDL_AppQuit(void *appstate, SDL_AppResult result)
47{
48 SDL_free(sprites);
49 SDL_free(positions);
50 SDL_free(velocities);
51 SDLTest_CommonQuit(state);
52}
53
54static int LoadSprite(const char *file)
55{
56 int i, w, h;
57
58 for (i = 0; i < state->num_windows; ++i) {
59 /* This does the SDL_LoadBMP step repeatedly, but that's OK for test code. */
60 if (sprites[i]) {
61 SDL_DestroyTexture(sprites[i]);
62 }
63 sprites[i] = LoadTexture(state->renderers[i], file, true, &w, &h);
64 sprite_w = (float)w;
65 sprite_h = (float)h;
66 if (!sprites[i]) {
67 return -1;
68 }
69 if (!SDL_SetTextureBlendMode(sprites[i], blendMode)) {
70 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set blend mode: %s", SDL_GetError());
71 SDL_DestroyTexture(sprites[i]);
72 return -1;
73 }
74 }
75
76 /* We're ready to roll. :) */
77 return 0;
78}
79
80static void MoveSprites(SDL_Renderer *renderer, SDL_Texture *sprite)
81{
82 int i;
83 SDL_Rect viewport;
84 SDL_FRect temp;
85 SDL_FRect *position, *velocity;
86
87 /* Query the sizes */
88 SDL_SetRenderViewport(renderer, NULL);
89 SDL_GetRenderSafeArea(renderer, &viewport);
90 SDL_SetRenderViewport(renderer, &viewport);
91
92 /* Cycle the color and alpha, if desired */
93 if (cycle_color) {
94 current_color += cycle_direction;
95 if (current_color < 0) {
96 current_color = 0;
97 cycle_direction = -cycle_direction;
98 }
99 if (current_color > 255) {
100 current_color = 255;
101 cycle_direction = -cycle_direction;
102 }
103 SDL_SetTextureColorMod(sprite, 255, (Uint8)current_color,
104 (Uint8)current_color);
105 }
106 if (cycle_alpha) {
107 current_alpha += cycle_direction;
108 if (current_alpha < 0) {
109 current_alpha = 0;
110 cycle_direction = -cycle_direction;
111 }
112 if (current_alpha > 255) {
113 current_alpha = 255;
114 cycle_direction = -cycle_direction;
115 }
116 SDL_SetTextureAlphaMod(sprite, (Uint8)current_alpha);
117 }
118
119 /* Draw a gray background */
120 SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0x00 /* used with --transparent */);
121 SDL_RenderClear(renderer);
122
123 /* Test points */
124 SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
125 SDL_RenderPoint(renderer, 0.0f, 0.0f);
126 SDL_RenderPoint(renderer, (float)(viewport.w - 1), 0.0f);
127 SDL_RenderPoint(renderer, 0.0f, (float)(viewport.h - 1));
128 SDL_RenderPoint(renderer, (float)(viewport.w - 1), (float)(viewport.h - 1));
129
130 /* Test horizontal and vertical lines */
131 SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
132 SDL_RenderLine(renderer, 1.0f, 0.0f, (float)(viewport.w - 2), 0.0f);
133 SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
134 SDL_RenderLine(renderer, 1.0f, (float)(viewport.h - 1), (float)(viewport.w - 2), (float)(viewport.h - 1));
135 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0xFF, 0xFF);
136 SDL_RenderLine(renderer, 0.0f, 1.0f, 0.0f, (float)(viewport.h - 2));
137 SDL_RenderLine(renderer, (float)(viewport.w - 1), 1.0f, (float)(viewport.w - 1), (float)(viewport.h - 2));
138
139 /* Test fill and copy */
140 SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
141 temp.x = 1.0f;
142 temp.y = 1.0f;
143 temp.w = sprite_w;
144 temp.h = sprite_h;
145 if (use_rendergeometry == 0) {
146 SDL_RenderFillRect(renderer, &temp);
147 } else {
148 /* Draw two triangles, filled, uniform */
149 SDL_FColor color;
150 SDL_Vertex verts[3];
151 SDL_zeroa(verts);
152 color.r = 1.0f;
153 color.g = 1.0f;
154 color.b = 1.0f;
155 color.a = 1.0f;
156
157 verts[0].position.x = temp.x;
158 verts[0].position.y = temp.y;
159 verts[0].color = color;
160
161 verts[1].position.x = temp.x + temp.w;
162 verts[1].position.y = temp.y;
163 verts[1].color = color;
164
165 verts[2].position.x = temp.x + temp.w;
166 verts[2].position.y = temp.y + temp.h;
167 verts[2].color = color;
168
169 SDL_RenderGeometry(renderer, NULL, verts, 3, NULL, 0);
170
171 verts[1].position.x = temp.x;
172 verts[1].position.y = temp.y + temp.h;
173 verts[1].color = color;
174
175 SDL_RenderGeometry(renderer, NULL, verts, 3, NULL, 0);
176 }
177 SDL_RenderTexture(renderer, sprite, NULL, &temp);
178 temp.x = viewport.w - sprite_w - 1;
179 temp.y = 1.0f;
180 temp.w = sprite_w;
181 temp.h = sprite_h;
182 SDL_RenderFillRect(renderer, &temp);
183 SDL_RenderTexture(renderer, sprite, NULL, &temp);
184 temp.x = 1.0f;
185 temp.y = viewport.h - sprite_h - 1;
186 temp.w = sprite_w;
187 temp.h = sprite_h;
188 SDL_RenderFillRect(renderer, &temp);
189 SDL_RenderTexture(renderer, sprite, NULL, &temp);
190 temp.x = viewport.w - sprite_w - 1;
191 temp.y = viewport.h - sprite_h - 1;
192 temp.w = sprite_w;
193 temp.h = sprite_h;
194 SDL_RenderFillRect(renderer, &temp);
195 SDL_RenderTexture(renderer, sprite, NULL, &temp);
196
197 /* Test diagonal lines */
198 SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
199 SDL_RenderLine(renderer, sprite_w, sprite_h,
200 viewport.w - sprite_w - 2, viewport.h - sprite_h - 2);
201 SDL_RenderLine(renderer, viewport.w - sprite_w - 2, sprite_h,
202 sprite_w, viewport.h - sprite_h - 2);
203
204 /* Conditionally move the sprites, bounce at the wall */
205 if (iterations == -1 || iterations > 0) {
206 for (i = 0; i < num_sprites; ++i) {
207 position = &positions[i];
208 velocity = &velocities[i];
209 position->x += velocity->x;
210 if ((position->x < 0) || (position->x >= (viewport.w - sprite_w))) {
211 velocity->x = -velocity->x;
212 position->x += velocity->x;
213 }
214 position->y += velocity->y;
215 if ((position->y < 0) || (position->y >= (viewport.h - sprite_h))) {
216 velocity->y = -velocity->y;
217 position->y += velocity->y;
218 }
219 }
220
221 /* Countdown sprite-move iterations and disable color changes at iteration end - used for visual tests. */
222 if (iterations > 0) {
223 iterations--;
224 if (iterations == 0) {
225 cycle_alpha = false;
226 cycle_color = false;
227 }
228 }
229 }
230
231 /* Draw sprites */
232 if (use_rendergeometry == 0) {
233 for (i = 0; i < num_sprites; ++i) {
234 position = &positions[i];
235
236 /* Blit the sprite onto the screen */
237 SDL_RenderTexture(renderer, sprite, NULL, position);
238 }
239 } else if (use_rendergeometry == 1) {
240 /*
241 * 0--1
242 * | /|
243 * |/ |
244 * 3--2
245 *
246 * Draw sprite2 as triangles that can be recombined as rect by software renderer
247 */
248 SDL_Vertex *verts = (SDL_Vertex *)SDL_malloc(num_sprites * sizeof(SDL_Vertex) * 6);
249 SDL_Vertex *verts2 = verts;
250 if (verts) {
251 SDL_FColor color;
252 SDL_GetTextureColorModFloat(sprite, &color.r, &color.g, &color.b);
253 SDL_GetTextureAlphaModFloat(sprite, &color.a);
254 for (i = 0; i < num_sprites; ++i) {
255 position = &positions[i];
256 /* 0 */
257 verts->position.x = position->x;
258 verts->position.y = position->y;
259 verts->color = color;
260 verts->tex_coord.x = 0.0f;
261 verts->tex_coord.y = 0.0f;
262 verts++;
263 /* 1 */
264 verts->position.x = position->x + position->w;
265 verts->position.y = position->y;
266 verts->color = color;
267 verts->tex_coord.x = 1.0f;
268 verts->tex_coord.y = 0.0f;
269 verts++;
270 /* 2 */
271 verts->position.x = position->x + position->w;
272 verts->position.y = position->y + position->h;
273 verts->color = color;
274 verts->tex_coord.x = 1.0f;
275 verts->tex_coord.y = 1.0f;
276 verts++;
277 /* 0 */
278 verts->position.x = position->x;
279 verts->position.y = position->y;
280 verts->color = color;
281 verts->tex_coord.x = 0.0f;
282 verts->tex_coord.y = 0.0f;
283 verts++;
284 /* 2 */
285 verts->position.x = position->x + position->w;
286 verts->position.y = position->y + position->h;
287 verts->color = color;
288 verts->tex_coord.x = 1.0f;
289 verts->tex_coord.y = 1.0f;
290 verts++;
291 /* 3 */
292 verts->position.x = position->x;
293 verts->position.y = position->y + position->h;
294 verts->color = color;
295 verts->tex_coord.x = 0.0f;
296 verts->tex_coord.y = 1.0f;
297 verts++;
298 }
299
300 /* Blit sprites as triangles onto the screen */
301 SDL_RenderGeometry(renderer, sprite, verts2, num_sprites * 6, NULL, 0);
302 SDL_free(verts2);
303 }
304 } else if (use_rendergeometry == 2) {
305 /* 0-----1
306 * |\ A /|
307 * | \ / |
308 * |D 2 B|
309 * | / \ |
310 * |/ C \|
311 * 3-----4
312 *
313 * Draw sprite2 as triangles that can *not* be recombined as rect by software renderer
314 * Use an 'indices' array
315 */
316 SDL_Vertex *verts = (SDL_Vertex *)SDL_malloc(num_sprites * sizeof(SDL_Vertex) * 5);
317 SDL_Vertex *verts2 = verts;
318 int *indices = (int *)SDL_malloc(num_sprites * sizeof(int) * 4 * 3);
319 int *indices2 = indices;
320 if (verts && indices) {
321 int pos = 0;
322 SDL_FColor color;
323 SDL_GetTextureColorModFloat(sprite, &color.r, &color.g, &color.b);
324 SDL_GetTextureAlphaModFloat(sprite, &color.a);
325 for (i = 0; i < num_sprites; ++i) {
326 position = &positions[i];
327 /* 0 */
328 verts->position.x = position->x;
329 verts->position.y = position->y;
330 verts->color = color;
331 verts->tex_coord.x = 0.0f;
332 verts->tex_coord.y = 0.0f;
333 verts++;
334 /* 1 */
335 verts->position.x = position->x + position->w;
336 verts->position.y = position->y;
337 verts->color = color;
338 verts->tex_coord.x = 1.0f;
339 verts->tex_coord.y = 0.0f;
340 verts++;
341 /* 2 */
342 verts->position.x = position->x + position->w / 2.0f;
343 verts->position.y = position->y + position->h / 2.0f;
344 verts->color = color;
345 verts->tex_coord.x = 0.5f;
346 verts->tex_coord.y = 0.5f;
347 verts++;
348 /* 3 */
349 verts->position.x = position->x;
350 verts->position.y = position->y + position->h;
351 verts->color = color;
352 verts->tex_coord.x = 0.0f;
353 verts->tex_coord.y = 1.0f;
354 verts++;
355 /* 4 */
356 verts->position.x = position->x + position->w;
357 verts->position.y = position->y + position->h;
358 verts->color = color;
359 verts->tex_coord.x = 1.0f;
360 verts->tex_coord.y = 1.0f;
361 verts++;
362 /* A */
363 *indices++ = pos + 0;
364 *indices++ = pos + 1;
365 *indices++ = pos + 2;
366 /* B */
367 *indices++ = pos + 1;
368 *indices++ = pos + 2;
369 *indices++ = pos + 4;
370 /* C */
371 *indices++ = pos + 3;
372 *indices++ = pos + 2;
373 *indices++ = pos + 4;
374 /* D */
375 *indices++ = pos + 3;
376 *indices++ = pos + 2;
377 *indices++ = pos + 0;
378 pos += 5;
379 }
380 }
381
382 /* Blit sprites as triangles onto the screen */
383 SDL_RenderGeometry(renderer, sprite, verts2, num_sprites * 5, indices2, num_sprites * 4 * 3);
384 SDL_free(verts2);
385 SDL_free(indices2);
386 }
387
388 /* Update the screen! */
389 SDL_RenderPresent(renderer);
390}
391
392SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
393{
394 SDL_Rect safe_area;
395 int i;
396 Uint64 seed;
397
398 /* Initialize parameters */
399 num_sprites = NUM_SPRITES;
400
401 /* Initialize test framework */
402 state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
403 if (!state) {
404 return SDL_APP_FAILURE;
405 }
406
407 for (i = 1; i < argc;) {
408 int consumed;
409
410 consumed = SDLTest_CommonArg(state, i);
411 if (consumed == 0) {
412 consumed = -1;
413 if (SDL_strcasecmp(argv[i], "--blend") == 0) {
414 if (argv[i + 1]) {
415 if (SDL_strcasecmp(argv[i + 1], "none") == 0) {
416 blendMode = SDL_BLENDMODE_NONE;
417 consumed = 2;
418 } else if (SDL_strcasecmp(argv[i + 1], "blend") == 0) {
419 blendMode = SDL_BLENDMODE_BLEND;
420 consumed = 2;
421 } else if (SDL_strcasecmp(argv[i + 1], "blend_premultiplied") == 0) {
422 blendMode = SDL_BLENDMODE_BLEND_PREMULTIPLIED;
423 consumed = 2;
424 } else if (SDL_strcasecmp(argv[i + 1], "add") == 0) {
425 blendMode = SDL_BLENDMODE_ADD;
426 consumed = 2;
427 } else if (SDL_strcasecmp(argv[i + 1], "add_premultiplied") == 0) {
428 blendMode = SDL_BLENDMODE_ADD_PREMULTIPLIED;
429 consumed = 2;
430 } else if (SDL_strcasecmp(argv[i + 1], "mod") == 0) {
431 blendMode = SDL_BLENDMODE_MOD;
432 consumed = 2;
433 } else if (SDL_strcasecmp(argv[i + 1], "mul") == 0) {
434 blendMode = SDL_BLENDMODE_MUL;
435 consumed = 2;
436 } else if (SDL_strcasecmp(argv[i + 1], "sub") == 0) {
437 blendMode = SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_SUBTRACT, SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_SUBTRACT);
438 consumed = 2;
439 }
440 }
441 } else if (SDL_strcasecmp(argv[i], "--iterations") == 0) {
442 if (argv[i + 1]) {
443 iterations = SDL_atoi(argv[i + 1]);
444 if (iterations < -1) {
445 iterations = -1;
446 }
447 consumed = 2;
448 }
449 } else if (SDL_strcasecmp(argv[i], "--cyclecolor") == 0) {
450 cycle_color = true;
451 consumed = 1;
452 } else if (SDL_strcasecmp(argv[i], "--cyclealpha") == 0) {
453 cycle_alpha = true;
454 consumed = 1;
455 } else if (SDL_strcasecmp(argv[i], "--suspend-when-occluded") == 0) {
456 suspend_when_occluded = true;
457 consumed = 1;
458 } else if (SDL_strcasecmp(argv[i], "--use-rendergeometry") == 0) {
459 if (argv[i + 1]) {
460 if (SDL_strcasecmp(argv[i + 1], "mode1") == 0) {
461 /* Draw sprite2 as triangles that can be recombined as rect by software renderer */
462 use_rendergeometry = 1;
463 } else if (SDL_strcasecmp(argv[i + 1], "mode2") == 0) {
464 /* Draw sprite2 as triangles that can *not* be recombined as rect by software renderer
465 * Use an 'indices' array */
466 use_rendergeometry = 2;
467 } else {
468 return SDL_APP_FAILURE;
469 }
470 }
471 consumed = 2;
472 } else if (SDL_isdigit(*argv[i])) {
473 num_sprites = SDL_atoi(argv[i]);
474 consumed = 1;
475 } else if (argv[i][0] != '-') {
476 icon = argv[i];
477 consumed = 1;
478 }
479 }
480 if (consumed < 0) {
481 static const char *options[] = {
482 "[--blend none|blend|blend_premultiplied|add|add_premultiplied|mod|mul|sub]",
483 "[--cyclecolor]",
484 "[--cyclealpha]",
485 "[--suspend-when-occluded]",
486 "[--iterations N]",
487 "[--use-rendergeometry mode1|mode2]",
488 "[num_sprites]",
489 "[icon.bmp]",
490 NULL
491 };
492 SDLTest_CommonLogUsage(state, argv[0], options);
493 return SDL_APP_FAILURE;
494 }
495 i += consumed;
496 }
497 if (!SDLTest_CommonInit(state)) {
498 return SDL_APP_FAILURE;
499 }
500
501 /* Create the windows, initialize the renderers, and load the textures */
502 sprites =
503 (SDL_Texture **)SDL_malloc(state->num_windows * sizeof(*sprites));
504 if (!sprites) {
505 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
506 return SDL_APP_FAILURE;
507 }
508 for (i = 0; i < state->num_windows; ++i) {
509 SDL_Renderer *renderer = state->renderers[i];
510 SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
511 SDL_RenderClear(renderer);
512 }
513 if (LoadSprite(icon) < 0) {
514 return SDL_APP_FAILURE;
515 }
516
517 /* Allocate memory for the sprite info */
518 positions = (SDL_FRect *)SDL_malloc(num_sprites * sizeof(*positions));
519 velocities = (SDL_FRect *)SDL_malloc(num_sprites * sizeof(*velocities));
520 if (!positions || !velocities) {
521 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory!");
522 return SDL_APP_FAILURE;
523 }
524
525 /* Position sprites and set their velocities using the fuzzer */
526 /* Really we should be using per-window safe area, but this is fine for a simple test */
527 SDL_GetRenderSafeArea(state->renderers[0], &safe_area);
528 if (iterations >= 0) {
529 /* Deterministic seed - used for visual tests */
530 seed = (Uint64)iterations;
531 } else {
532 /* Pseudo-random seed generated from the time */
533 seed = SDL_GetPerformanceCounter();
534 }
535 SDLTest_FuzzerInit(seed);
536 for (i = 0; i < num_sprites; ++i) {
537 positions[i].x = (float)SDLTest_RandomIntegerInRange(0, (int)(safe_area.w - sprite_w));
538 positions[i].y = (float)SDLTest_RandomIntegerInRange(0, (int)(safe_area.h - sprite_h));
539 positions[i].w = sprite_w;
540 positions[i].h = sprite_h;
541 velocities[i].x = 0;
542 velocities[i].y = 0;
543 while (velocities[i].x == 0.f && velocities[i].y == 0.f) {
544 velocities[i].x = (float)SDLTest_RandomIntegerInRange(-MAX_SPEED, MAX_SPEED);
545 velocities[i].y = (float)SDLTest_RandomIntegerInRange(-MAX_SPEED, MAX_SPEED);
546 }
547 }
548
549 /* Main render loop in SDL_AppIterate will begin when this function returns. */
550 frames = 0;
551 next_fps_check = SDL_GetTicks() + fps_check_delay;
552
553 return SDL_APP_CONTINUE;
554}
555
556
557SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
558{
559 if (event->type == SDL_EVENT_RENDER_DEVICE_RESET) {
560 LoadSprite(icon);
561 }
562 return SDLTest_CommonEventMainCallbacks(state, event);
563}
564
565SDL_AppResult SDL_AppIterate(void *appstate)
566{
567 Uint64 now;
568 int i;
569 int active_windows = 0;
570
571 for (i = 0; i < state->num_windows; ++i) {
572 if (state->windows[i] == NULL ||
573 (suspend_when_occluded && (SDL_GetWindowFlags(state->windows[i]) & SDL_WINDOW_OCCLUDED))) {
574 continue;
575 }
576 ++active_windows;
577 MoveSprites(state->renderers[i], sprites[i]);
578 }
579
580 /* If all windows are occluded, throttle the event polling to 15hz. */
581 if (!active_windows) {
582 SDL_DelayNS(SDL_NS_PER_SECOND / 15);
583 }
584
585 frames++;
586 now = SDL_GetTicks();
587 if (now >= next_fps_check) {
588 /* Print out some timing information */
589 const Uint64 then = next_fps_check - fps_check_delay;
590 const double fps = ((double)frames * 1000) / (now - then);
591 SDL_Log("%2.2f frames per second", fps);
592 next_fps_check = now + fps_check_delay;
593 frames = 0;
594 }
595
596 return SDL_APP_CONTINUE;
597}