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 | |
25 | static SDLTest_CommonState *state; |
26 | |
27 | typedef 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 | |
36 | DrawState *drawstates; |
37 | int done; |
38 | SDL_bool test_composite = SDL_FALSE; |
39 | |
40 | /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ |
41 | static void |
42 | quit(int rc) |
43 | { |
44 | SDLTest_CommonQuit(state); |
45 | exit(rc); |
46 | } |
47 | |
48 | SDL_Texture * |
49 | LoadTexture(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 | |
98 | SDL_bool |
99 | DrawComposite(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 | |
181 | SDL_bool |
182 | Draw(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 | |
225 | void |
226 | loop() |
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 | |
251 | int |
252 | main(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 | |