1/*
2 Copyright (C) 1997-2021 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#include <stdlib.h>
15#include <stdio.h>
16#include <time.h>
17
18#ifdef __EMSCRIPTEN__
19#include <emscripten/emscripten.h>
20#endif
21
22#include "SDL_test_common.h"
23
24
25static SDLTest_CommonState *state;
26
27typedef struct {
28 SDL_Window *window;
29 SDL_Renderer *renderer;
30 SDL_Texture *background;
31 SDL_Texture *sprite;
32 SDL_Rect sprite_rect;
33 int scale_direction;
34} DrawState;
35
36DrawState *drawstates;
37int done;
38SDL_bool test_composite = SDL_FALSE;
39
40/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
41static void
42quit(int rc)
43{
44 SDLTest_CommonQuit(state);
45 exit(rc);
46}
47
48SDL_Texture *
49LoadTexture(SDL_Renderer *renderer, char *file, SDL_bool transparent)
50{
51 SDL_Surface *temp;
52 SDL_Texture *texture;
53
54 /* Load the sprite image */
55 temp = SDL_LoadBMP(file);
56 if (temp == NULL) {
57 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError());
58 return NULL;
59 }
60
61 /* Set transparent pixel as the pixel at (0,0) */
62 if (transparent) {
63 if (temp->format->palette) {
64 SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels);
65 } else {
66 switch (temp->format->BitsPerPixel) {
67 case 15:
68 SDL_SetColorKey(temp, SDL_TRUE,
69 (*(Uint16 *) temp->pixels) & 0x00007FFF);
70 break;
71 case 16:
72 SDL_SetColorKey(temp, SDL_TRUE, *(Uint16 *) temp->pixels);
73 break;
74 case 24:
75 SDL_SetColorKey(temp, SDL_TRUE,
76 (*(Uint32 *) temp->pixels) & 0x00FFFFFF);
77 break;
78 case 32:
79 SDL_SetColorKey(temp, SDL_TRUE, *(Uint32 *) temp->pixels);
80 break;
81 }
82 }
83 }
84
85 /* Create textures from the image */
86 texture = SDL_CreateTextureFromSurface(renderer, temp);
87 if (!texture) {
88 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError());
89 SDL_FreeSurface(temp);
90 return NULL;
91 }
92 SDL_FreeSurface(temp);
93
94 /* We're ready to roll. :) */
95 return texture;
96}
97
98SDL_bool
99DrawComposite(DrawState *s)
100{
101 SDL_Rect viewport, R;
102 SDL_Texture *target;
103
104 static SDL_bool blend_tested = SDL_FALSE;
105 if (!blend_tested) {
106 SDL_Texture *A, *B;
107 Uint32 P;
108
109 A = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1, 1);
110 SDL_SetTextureBlendMode(A, SDL_BLENDMODE_BLEND);
111
112 B = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1, 1);
113 SDL_SetTextureBlendMode(B, SDL_BLENDMODE_BLEND);
114
115 SDL_SetRenderTarget(s->renderer, A);
116 SDL_SetRenderDrawColor(s->renderer, 0x00, 0x00, 0x00, 0x80);
117 SDL_RenderFillRect(s->renderer, NULL);
118
119 SDL_SetRenderTarget(s->renderer, B);
120 SDL_SetRenderDrawColor(s->renderer, 0x00, 0x00, 0x00, 0x00);
121 SDL_RenderFillRect(s->renderer, NULL);
122 SDL_RenderCopy(s->renderer, A, NULL, NULL);
123 SDL_RenderReadPixels(s->renderer, NULL, SDL_PIXELFORMAT_ARGB8888, &P, sizeof(P));
124
125 SDL_Log("Blended pixel: 0x%8.8X\n", P);
126
127 SDL_DestroyTexture(A);
128 SDL_DestroyTexture(B);
129 blend_tested = SDL_TRUE;
130 }
131
132 SDL_RenderGetViewport(s->renderer, &viewport);
133
134 target = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, viewport.w, viewport.h);
135 SDL_SetTextureBlendMode(target, SDL_BLENDMODE_BLEND);
136 SDL_SetRenderTarget(s->renderer, target);
137
138 /* Draw the background.
139 This is solid black so when the sprite is copied to it, any per-pixel alpha will be blended through.
140 */
141 SDL_SetRenderDrawColor(s->renderer, 0x00, 0x00, 0x00, 0x00);
142 SDL_RenderFillRect(s->renderer, NULL);
143
144 /* Scale and draw the sprite */
145 s->sprite_rect.w += s->scale_direction;
146 s->sprite_rect.h += s->scale_direction;
147 if (s->scale_direction > 0) {
148 if (s->sprite_rect.w >= viewport.w || s->sprite_rect.h >= viewport.h) {
149 s->scale_direction = -1;
150 }
151 } else {
152 if (s->sprite_rect.w <= 1 || s->sprite_rect.h <= 1) {
153 s->scale_direction = 1;
154 }
155 }
156 s->sprite_rect.x = (viewport.w - s->sprite_rect.w) / 2;
157 s->sprite_rect.y = (viewport.h - s->sprite_rect.h) / 2;
158
159 SDL_RenderCopy(s->renderer, s->sprite, NULL, &s->sprite_rect);
160
161 SDL_SetRenderTarget(s->renderer, NULL);
162 SDL_RenderCopy(s->renderer, s->background, NULL, NULL);
163
164 SDL_SetRenderDrawBlendMode(s->renderer, SDL_BLENDMODE_BLEND);
165 SDL_SetRenderDrawColor(s->renderer, 0xff, 0x00, 0x00, 0x80);
166 R.x = 0;
167 R.y = 0;
168 R.w = 100;
169 R.h = 100;
170 SDL_RenderFillRect(s->renderer, &R);
171 SDL_SetRenderDrawBlendMode(s->renderer, SDL_BLENDMODE_NONE);
172
173 SDL_RenderCopy(s->renderer, target, NULL, NULL);
174 SDL_DestroyTexture(target);
175
176 /* Update the screen! */
177 SDL_RenderPresent(s->renderer);
178 return SDL_TRUE;
179}
180
181SDL_bool
182Draw(DrawState *s)
183{
184 SDL_Rect viewport;
185 SDL_Texture *target;
186
187 SDL_RenderGetViewport(s->renderer, &viewport);
188
189 target = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, viewport.w, viewport.h);
190 if (!target) {
191 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create render target texture: %s\n", SDL_GetError());
192 return SDL_FALSE;
193 }
194 SDL_SetRenderTarget(s->renderer, target);
195
196 /* Draw the background */
197 SDL_RenderCopy(s->renderer, s->background, NULL, NULL);
198
199 /* Scale and draw the sprite */
200 s->sprite_rect.w += s->scale_direction;
201 s->sprite_rect.h += s->scale_direction;
202 if (s->scale_direction > 0) {
203 if (s->sprite_rect.w >= viewport.w || s->sprite_rect.h >= viewport.h) {
204 s->scale_direction = -1;
205 }
206 } else {
207 if (s->sprite_rect.w <= 1 || s->sprite_rect.h <= 1) {
208 s->scale_direction = 1;
209 }
210 }
211 s->sprite_rect.x = (viewport.w - s->sprite_rect.w) / 2;
212 s->sprite_rect.y = (viewport.h - s->sprite_rect.h) / 2;
213
214 SDL_RenderCopy(s->renderer, s->sprite, NULL, &s->sprite_rect);
215
216 SDL_SetRenderTarget(s->renderer, NULL);
217 SDL_RenderCopy(s->renderer, target, NULL, NULL);
218 SDL_DestroyTexture(target);
219
220 /* Update the screen! */
221 SDL_RenderPresent(s->renderer);
222 return SDL_TRUE;
223}
224
225void
226loop()
227{
228 int i;
229 SDL_Event event;
230
231 /* Check for events */
232 while (SDL_PollEvent(&event)) {
233 SDLTest_CommonEvent(state, &event, &done);
234 }
235 for (i = 0; i < state->num_windows; ++i) {
236 if (state->windows[i] == NULL)
237 continue;
238 if (test_composite) {
239 if (!DrawComposite(&drawstates[i])) done = 1;
240 } else {
241 if (!Draw(&drawstates[i])) done = 1;
242 }
243 }
244#ifdef __EMSCRIPTEN__
245 if (done) {
246 emscripten_cancel_main_loop();
247 }
248#endif
249}
250
251int
252main(int argc, char *argv[])
253{
254 int i;
255 int frames;
256 Uint32 then, now;
257
258 /* Enable standard application logging */
259 SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
260
261 /* Initialize test framework */
262 state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
263 if (!state) {
264 return 1;
265 }
266 for (i = 1; i < argc;) {
267 int consumed;
268
269 consumed = SDLTest_CommonArg(state, i);
270 if (consumed == 0) {
271 consumed = -1;
272 if (SDL_strcasecmp(argv[i], "--composite") == 0) {
273 test_composite = SDL_TRUE;
274 consumed = 1;
275 }
276 }
277 if (consumed < 0) {
278 static const char *options[] = { "[--composite]", NULL };
279 SDLTest_CommonLogUsage(state, argv[0], options);
280 quit(1);
281 }
282 i += consumed;
283 }
284 if (!SDLTest_CommonInit(state)) {
285 quit(2);
286 }
287
288 drawstates = SDL_stack_alloc(DrawState, state->num_windows);
289 for (i = 0; i < state->num_windows; ++i) {
290 DrawState *drawstate = &drawstates[i];
291
292 drawstate->window = state->windows[i];
293 drawstate->renderer = state->renderers[i];
294 if (test_composite) {
295 drawstate->sprite = LoadTexture(drawstate->renderer, "icon-alpha.bmp", SDL_TRUE);
296 } else {
297 drawstate->sprite = LoadTexture(drawstate->renderer, "icon.bmp", SDL_TRUE);
298 }
299 drawstate->background = LoadTexture(drawstate->renderer, "sample.bmp", SDL_FALSE);
300 if (!drawstate->sprite || !drawstate->background) {
301 quit(2);
302 }
303 SDL_QueryTexture(drawstate->sprite, NULL, NULL,
304 &drawstate->sprite_rect.w, &drawstate->sprite_rect.h);
305 drawstate->scale_direction = 1;
306 }
307
308 /* Main render loop */
309 frames = 0;
310 then = SDL_GetTicks();
311 done = 0;
312
313#ifdef __EMSCRIPTEN__
314 emscripten_set_main_loop(loop, 0, 1);
315#else
316 while (!done) {
317 ++frames;
318 loop();
319 }
320#endif
321
322 /* Print out some timing information */
323 now = SDL_GetTicks();
324 if (now > then) {
325 double fps = ((double) frames * 1000) / (now - then);
326 SDL_Log("%2.2f frames per second\n", fps);
327 }
328
329 SDL_stack_free(drawstates);
330
331 quit(0);
332 return 0;
333}
334
335/* vi: set ts=4 sw=4 expandtab: */
336