diff options
Diffstat (limited to 'contrib/SDL-3.2.8/test/testcolorspace.c')
| -rw-r--r-- | contrib/SDL-3.2.8/test/testcolorspace.c | 743 |
1 files changed, 743 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/test/testcolorspace.c b/contrib/SDL-3.2.8/test/testcolorspace.c new file mode 100644 index 0000000..c3bcd3b --- /dev/null +++ b/contrib/SDL-3.2.8/test/testcolorspace.c | |||
| @@ -0,0 +1,743 @@ | |||
| 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 | |||
| 13 | #include <SDL3/SDL.h> | ||
| 14 | #include <SDL3/SDL_test.h> | ||
| 15 | #include <SDL3/SDL_main.h> | ||
| 16 | |||
| 17 | #ifdef SDL_PLATFORM_EMSCRIPTEN | ||
| 18 | #include <emscripten/emscripten.h> | ||
| 19 | #endif | ||
| 20 | |||
| 21 | #define WINDOW_WIDTH 640 | ||
| 22 | #define WINDOW_HEIGHT 480 | ||
| 23 | |||
| 24 | #define TEXT_START_X 6.0f | ||
| 25 | #define TEXT_START_Y 6.0f | ||
| 26 | #define TEXT_LINE_ADVANCE FONT_CHARACTER_SIZE * 2 | ||
| 27 | |||
| 28 | static SDL_Window *window; | ||
| 29 | static SDL_Renderer *renderer; | ||
| 30 | static const char *renderer_name; | ||
| 31 | static SDL_Colorspace colorspace = SDL_COLORSPACE_SRGB; | ||
| 32 | static const char *colorspace_name = "sRGB"; | ||
| 33 | static int renderer_count = 0; | ||
| 34 | static int renderer_index = 0; | ||
| 35 | static int stage_index = 0; | ||
| 36 | static int done; | ||
| 37 | static float HDR_headroom = 1.0f; | ||
| 38 | |||
| 39 | enum | ||
| 40 | { | ||
| 41 | StageClearBackground, | ||
| 42 | StageDrawBackground, | ||
| 43 | StageTextureBackground, | ||
| 44 | StageTargetBackground, | ||
| 45 | StageBlendDrawing, | ||
| 46 | StageBlendTexture, | ||
| 47 | StageGradientDrawing, | ||
| 48 | StageGradientTexture, | ||
| 49 | StageCount | ||
| 50 | }; | ||
| 51 | |||
| 52 | static void FreeRenderer(void) | ||
| 53 | { | ||
| 54 | SDLTest_CleanupTextDrawing(); | ||
| 55 | SDL_DestroyRenderer(renderer); | ||
| 56 | renderer = NULL; | ||
| 57 | } | ||
| 58 | |||
| 59 | static void UpdateHDRState(void) | ||
| 60 | { | ||
| 61 | SDL_PropertiesID props; | ||
| 62 | bool HDR_enabled; | ||
| 63 | |||
| 64 | props = SDL_GetWindowProperties(window); | ||
| 65 | HDR_enabled = SDL_GetBooleanProperty(props, SDL_PROP_WINDOW_HDR_ENABLED_BOOLEAN, false); | ||
| 66 | |||
| 67 | SDL_Log("HDR %s", HDR_enabled ? "enabled" : "disabled"); | ||
| 68 | |||
| 69 | if (HDR_enabled) { | ||
| 70 | props = SDL_GetRendererProperties(renderer); | ||
| 71 | if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SRGB_LINEAR) { | ||
| 72 | SDL_Log("Run with --colorspace linear to display HDR colors"); | ||
| 73 | } | ||
| 74 | HDR_headroom = SDL_GetFloatProperty(props, SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT, 1.0f); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | static void CreateRenderer(void) | ||
| 79 | { | ||
| 80 | SDL_PropertiesID props; | ||
| 81 | |||
| 82 | props = SDL_CreateProperties(); | ||
| 83 | SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window); | ||
| 84 | SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, SDL_GetRenderDriver(renderer_index)); | ||
| 85 | SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, colorspace); | ||
| 86 | renderer = SDL_CreateRendererWithProperties(props); | ||
| 87 | SDL_DestroyProperties(props); | ||
| 88 | if (!renderer) { | ||
| 89 | SDL_Log("Couldn't create renderer: %s", SDL_GetError()); | ||
| 90 | return; | ||
| 91 | } | ||
| 92 | |||
| 93 | renderer_name = SDL_GetRendererName(renderer); | ||
| 94 | SDL_Log("Created renderer %s", renderer_name); | ||
| 95 | |||
| 96 | UpdateHDRState(); | ||
| 97 | } | ||
| 98 | |||
| 99 | static void NextRenderer( void ) | ||
| 100 | { | ||
| 101 | if (renderer_count <= 0) { | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | |||
| 105 | ++renderer_index; | ||
| 106 | if (renderer_index == renderer_count) { | ||
| 107 | renderer_index = 0; | ||
| 108 | } | ||
| 109 | FreeRenderer(); | ||
| 110 | CreateRenderer(); | ||
| 111 | } | ||
| 112 | |||
| 113 | static void PrevRenderer(void) | ||
| 114 | { | ||
| 115 | if (renderer_count <= 0) { | ||
| 116 | return; | ||
| 117 | } | ||
| 118 | |||
| 119 | --renderer_index; | ||
| 120 | if (renderer_index == -1) { | ||
| 121 | renderer_index += renderer_count; | ||
| 122 | } | ||
| 123 | FreeRenderer(); | ||
| 124 | CreateRenderer(); | ||
| 125 | } | ||
| 126 | |||
| 127 | static void NextStage(void) | ||
| 128 | { | ||
| 129 | ++stage_index; | ||
| 130 | if (stage_index == StageCount) { | ||
| 131 | stage_index = 0; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | static void PrevStage(void) | ||
| 136 | { | ||
| 137 | --stage_index; | ||
| 138 | if (stage_index == -1) { | ||
| 139 | stage_index += StageCount; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | static bool ReadPixel(int x, int y, SDL_Color *c) | ||
| 144 | { | ||
| 145 | SDL_Surface *surface; | ||
| 146 | SDL_Rect r; | ||
| 147 | bool result = false; | ||
| 148 | |||
| 149 | r.x = x; | ||
| 150 | r.y = y; | ||
| 151 | r.w = 1; | ||
| 152 | r.h = 1; | ||
| 153 | |||
| 154 | surface = SDL_RenderReadPixels(renderer, &r); | ||
| 155 | if (surface) { | ||
| 156 | /* Don't tonemap back to SDR, our source content was SDR */ | ||
| 157 | SDL_SetStringProperty(SDL_GetSurfaceProperties(surface), SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING, "*=1"); | ||
| 158 | |||
| 159 | if (SDL_ReadSurfacePixel(surface, 0, 0, &c->r, &c->g, &c->b, &c->a)) { | ||
| 160 | result = true; | ||
| 161 | } else { | ||
| 162 | SDL_Log("Couldn't read pixel: %s", SDL_GetError()); | ||
| 163 | } | ||
| 164 | SDL_DestroySurface(surface); | ||
| 165 | } else { | ||
| 166 | SDL_Log("Couldn't read back pixels: %s", SDL_GetError()); | ||
| 167 | } | ||
| 168 | return result; | ||
| 169 | } | ||
| 170 | |||
| 171 | static void DrawText(float x, float y, const char *fmt, ...) | ||
| 172 | { | ||
| 173 | char *text; | ||
| 174 | |||
| 175 | va_list ap; | ||
| 176 | va_start(ap, fmt); | ||
| 177 | SDL_vasprintf(&text, fmt, ap); | ||
| 178 | va_end(ap); | ||
| 179 | |||
| 180 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); | ||
| 181 | SDLTest_DrawString(renderer, x + 1.0f, y + 1.0f, text); | ||
| 182 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); | ||
| 183 | SDLTest_DrawString(renderer, x, y, text); | ||
| 184 | SDL_free(text); | ||
| 185 | } | ||
| 186 | |||
| 187 | static void RenderClearBackground(void) | ||
| 188 | { | ||
| 189 | /* Draw a 50% gray background. | ||
| 190 | * This will be darker when using sRGB colors and lighter using linear colors | ||
| 191 | */ | ||
| 192 | SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255); | ||
| 193 | SDL_RenderClear(renderer); | ||
| 194 | |||
| 195 | /* Check the renderered pixels */ | ||
| 196 | SDL_Color c; | ||
| 197 | if (!ReadPixel(0, 0, &c)) { | ||
| 198 | return; | ||
| 199 | } | ||
| 200 | |||
| 201 | float x = TEXT_START_X; | ||
| 202 | float y = TEXT_START_Y; | ||
| 203 | DrawText(x, y, "%s %s", renderer_name, colorspace_name); | ||
| 204 | y += TEXT_LINE_ADVANCE; | ||
| 205 | DrawText(x, y, "Test: Clear 50%% Gray Background"); | ||
| 206 | y += TEXT_LINE_ADVANCE; | ||
| 207 | DrawText(x, y, "Background color written: 0x808080, read: 0x%.2x%.2x%.2x", c.r, c.g, c.b); | ||
| 208 | y += TEXT_LINE_ADVANCE; | ||
| 209 | if (c.r != 128) { | ||
| 210 | DrawText(x, y, "Incorrect background color, unknown reason"); | ||
| 211 | y += TEXT_LINE_ADVANCE; | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | static void RenderDrawBackground(void) | ||
| 216 | { | ||
| 217 | /* Draw a 50% gray background. | ||
| 218 | * This will be darker when using sRGB colors and lighter using linear colors | ||
| 219 | */ | ||
| 220 | SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255); | ||
| 221 | SDL_RenderFillRect(renderer, NULL); | ||
| 222 | |||
| 223 | /* Check the renderered pixels */ | ||
| 224 | SDL_Color c; | ||
| 225 | if (!ReadPixel(0, 0, &c)) { | ||
| 226 | return; | ||
| 227 | } | ||
| 228 | |||
| 229 | float x = TEXT_START_X; | ||
| 230 | float y = TEXT_START_Y; | ||
| 231 | DrawText(x, y, "%s %s", renderer_name, colorspace_name); | ||
| 232 | y += TEXT_LINE_ADVANCE; | ||
| 233 | DrawText(x, y, "Test: Draw 50%% Gray Background"); | ||
| 234 | y += TEXT_LINE_ADVANCE; | ||
| 235 | DrawText(x, y, "Background color written: 0x808080, read: 0x%.2x%.2x%.2x", c.r, c.g, c.b); | ||
| 236 | y += TEXT_LINE_ADVANCE; | ||
| 237 | if (c.r != 128) { | ||
| 238 | DrawText(x, y, "Incorrect background color, unknown reason"); | ||
| 239 | y += TEXT_LINE_ADVANCE; | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | static SDL_Texture *CreateGrayTexture(void) | ||
| 244 | { | ||
| 245 | SDL_Texture *texture; | ||
| 246 | Uint8 pixels[4]; | ||
| 247 | |||
| 248 | /* Floating point textures are in the linear colorspace by default */ | ||
| 249 | texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, 1, 1); | ||
| 250 | if (!texture) { | ||
| 251 | return NULL; | ||
| 252 | } | ||
| 253 | |||
| 254 | pixels[0] = 128; | ||
| 255 | pixels[1] = 128; | ||
| 256 | pixels[2] = 128; | ||
| 257 | pixels[3] = 255; | ||
| 258 | SDL_UpdateTexture(texture, NULL, pixels, sizeof(pixels)); | ||
| 259 | |||
| 260 | return texture; | ||
| 261 | } | ||
| 262 | |||
| 263 | static void RenderTextureBackground(void) | ||
| 264 | { | ||
| 265 | /* Fill the background with a 50% gray texture. | ||
| 266 | * This will be darker when using sRGB colors and lighter using linear colors | ||
| 267 | */ | ||
| 268 | SDL_Texture *texture = CreateGrayTexture(); | ||
| 269 | SDL_RenderTexture(renderer, texture, NULL, NULL); | ||
| 270 | SDL_DestroyTexture(texture); | ||
| 271 | |||
| 272 | /* Check the renderered pixels */ | ||
| 273 | SDL_Color c; | ||
| 274 | if (!ReadPixel(0, 0, &c)) { | ||
| 275 | return; | ||
| 276 | } | ||
| 277 | |||
| 278 | float x = TEXT_START_X; | ||
| 279 | float y = TEXT_START_Y; | ||
| 280 | DrawText(x, y, "%s %s", renderer_name, colorspace_name); | ||
| 281 | y += TEXT_LINE_ADVANCE; | ||
| 282 | DrawText(x, y, "Test: Fill 50%% Gray Texture"); | ||
| 283 | y += TEXT_LINE_ADVANCE; | ||
| 284 | DrawText(x, y, "Background color written: 0x808080, read: 0x%.2x%.2x%.2x", c.r, c.g, c.b); | ||
| 285 | y += TEXT_LINE_ADVANCE; | ||
| 286 | if (c.r != 128) { | ||
| 287 | DrawText(x, y, "Incorrect background color, unknown reason"); | ||
| 288 | y += TEXT_LINE_ADVANCE; | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | static void RenderTargetBackground(void) | ||
| 293 | { | ||
| 294 | /* Fill the background with a 50% gray texture. | ||
| 295 | * This will be darker when using sRGB colors and lighter using linear colors | ||
| 296 | */ | ||
| 297 | SDL_Texture *target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, 1, 1); | ||
| 298 | SDL_Texture *texture = CreateGrayTexture(); | ||
| 299 | |||
| 300 | /* Fill the render target with the gray texture */ | ||
| 301 | SDL_SetRenderTarget(renderer, target); | ||
| 302 | SDL_RenderTexture(renderer, texture, NULL, NULL); | ||
| 303 | SDL_DestroyTexture(texture); | ||
| 304 | |||
| 305 | /* Fill the output with the render target */ | ||
| 306 | SDL_SetRenderTarget(renderer, NULL); | ||
| 307 | SDL_RenderTexture(renderer, target, NULL, NULL); | ||
| 308 | SDL_DestroyTexture(target); | ||
| 309 | |||
| 310 | /* Check the renderered pixels */ | ||
| 311 | SDL_Color c; | ||
| 312 | if (!ReadPixel(0, 0, &c)) { | ||
| 313 | return; | ||
| 314 | } | ||
| 315 | |||
| 316 | float x = TEXT_START_X; | ||
| 317 | float y = TEXT_START_Y; | ||
| 318 | DrawText(x, y, "%s %s", renderer_name, colorspace_name); | ||
| 319 | y += TEXT_LINE_ADVANCE; | ||
| 320 | DrawText(x, y, "Test: Fill 50%% Gray Render Target"); | ||
| 321 | y += TEXT_LINE_ADVANCE; | ||
| 322 | DrawText(x, y, "Background color written: 0x808080, read: 0x%.2x%.2x%.2x", c.r, c.g, c.b); | ||
| 323 | y += TEXT_LINE_ADVANCE; | ||
| 324 | if (c.r != 128) { | ||
| 325 | DrawText(x, y, "Incorrect background color, unknown reason"); | ||
| 326 | y += TEXT_LINE_ADVANCE; | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | static void RenderBlendDrawing(void) | ||
| 331 | { | ||
| 332 | SDL_Color a = { 238, 70, 166, 255 }; /* red square */ | ||
| 333 | SDL_Color b = { 147, 255, 0, 255 }; /* green square */ | ||
| 334 | SDL_FRect rect; | ||
| 335 | |||
| 336 | /* Draw a green square blended over a red square | ||
| 337 | * This will have different effects based on whether sRGB colorspaces and sRGB vs linear blending is used. | ||
| 338 | */ | ||
| 339 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); | ||
| 340 | SDL_RenderClear(renderer); | ||
| 341 | rect.x = WINDOW_WIDTH / 3; | ||
| 342 | rect.y = 0; | ||
| 343 | rect.w = WINDOW_WIDTH / 3; | ||
| 344 | rect.h = WINDOW_HEIGHT; | ||
| 345 | SDL_SetRenderDrawColor(renderer, a.r, a.g, a.b, a.a); | ||
| 346 | SDL_RenderFillRect(renderer, &rect); | ||
| 347 | |||
| 348 | rect.x = 0; | ||
| 349 | rect.y = WINDOW_HEIGHT / 3; | ||
| 350 | rect.w = WINDOW_WIDTH; | ||
| 351 | rect.h = WINDOW_HEIGHT / 6; | ||
| 352 | SDL_SetRenderDrawColor(renderer, b.r, b.g, b.b, b.a); | ||
| 353 | SDL_RenderFillRect(renderer, &rect); | ||
| 354 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); | ||
| 355 | SDL_SetRenderDrawColor(renderer, b.r, b.g, b.b, 128); | ||
| 356 | rect.y += WINDOW_HEIGHT / 6; | ||
| 357 | SDL_RenderFillRect(renderer, &rect); | ||
| 358 | |||
| 359 | SDL_Color ar, br, cr; | ||
| 360 | if (!ReadPixel(WINDOW_WIDTH / 2, 0, &ar) || | ||
| 361 | !ReadPixel(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 3, &br) || | ||
| 362 | !ReadPixel(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2, &cr)) { | ||
| 363 | return; | ||
| 364 | } | ||
| 365 | |||
| 366 | float x = TEXT_START_X; | ||
| 367 | float y = TEXT_START_Y; | ||
| 368 | DrawText(x, y, "%s %s", renderer_name, colorspace_name); | ||
| 369 | y += TEXT_LINE_ADVANCE; | ||
| 370 | DrawText(x, y, "Test: Draw Blending"); | ||
| 371 | y += TEXT_LINE_ADVANCE; | ||
| 372 | if (cr.r == 199 && cr.g == 193 && cr.b == 121) { | ||
| 373 | DrawText(x, y, "Correct blend color, blending in linear space"); | ||
| 374 | } else if ((cr.r == 192 && cr.g == 163 && cr.b == 83) || | ||
| 375 | (cr.r == 191 && cr.g == 162 && cr.b == 82)) { | ||
| 376 | DrawText(x, y, "Correct blend color, blending in sRGB space"); | ||
| 377 | } else if (cr.r == 214 && cr.g == 156 && cr.b == 113) { | ||
| 378 | DrawText(x, y, "Incorrect blend color, blending in PQ space"); | ||
| 379 | } else { | ||
| 380 | DrawText(x, y, "Incorrect blend color, unknown reason"); | ||
| 381 | } | ||
| 382 | y += TEXT_LINE_ADVANCE; | ||
| 383 | } | ||
| 384 | |||
| 385 | static void RenderBlendTexture(void) | ||
| 386 | { | ||
| 387 | SDL_Color color_a = { 238, 70, 166, 255 }; /* red square */ | ||
| 388 | SDL_Color color_b = { 147, 255, 0, 255 }; /* green square */ | ||
| 389 | SDL_Texture *a; | ||
| 390 | SDL_Texture *b; | ||
| 391 | SDL_FRect rect; | ||
| 392 | |||
| 393 | /* Draw a green square blended over a red square | ||
| 394 | * This will have different effects based on whether sRGB colorspaces and sRGB vs linear blending is used. | ||
| 395 | */ | ||
| 396 | a = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, 1, 1); | ||
| 397 | SDL_UpdateTexture(a, NULL, &color_a, sizeof(color_a)); | ||
| 398 | b = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, 1, 1); | ||
| 399 | SDL_UpdateTexture(b, NULL, &color_b, sizeof(color_b)); | ||
| 400 | |||
| 401 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); | ||
| 402 | SDL_RenderClear(renderer); | ||
| 403 | rect.x = WINDOW_WIDTH / 3; | ||
| 404 | rect.y = 0; | ||
| 405 | rect.w = WINDOW_WIDTH / 3; | ||
| 406 | rect.h = WINDOW_HEIGHT; | ||
| 407 | SDL_RenderTexture(renderer, a, NULL, &rect); | ||
| 408 | |||
| 409 | rect.x = 0; | ||
| 410 | rect.y = WINDOW_HEIGHT / 3; | ||
| 411 | rect.w = WINDOW_WIDTH; | ||
| 412 | rect.h = WINDOW_HEIGHT / 6; | ||
| 413 | SDL_RenderTexture(renderer, b, NULL, &rect); | ||
| 414 | rect.y += WINDOW_HEIGHT / 6; | ||
| 415 | SDL_SetTextureBlendMode(b, SDL_BLENDMODE_BLEND); | ||
| 416 | SDL_SetTextureAlphaModFloat(b, 128 / 255.0f); | ||
| 417 | SDL_RenderTexture(renderer, b, NULL, &rect); | ||
| 418 | |||
| 419 | SDL_Color ar, br, cr; | ||
| 420 | if (!ReadPixel(WINDOW_WIDTH / 2, 0, &ar) || | ||
| 421 | !ReadPixel(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 3, &br) || | ||
| 422 | !ReadPixel(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2, &cr)) { | ||
| 423 | return; | ||
| 424 | } | ||
| 425 | |||
| 426 | float x = TEXT_START_X; | ||
| 427 | float y = TEXT_START_Y; | ||
| 428 | DrawText(x, y, "%s %s", renderer_name, colorspace_name); | ||
| 429 | y += TEXT_LINE_ADVANCE; | ||
| 430 | DrawText(x, y, "Test: Texture Blending"); | ||
| 431 | y += TEXT_LINE_ADVANCE; | ||
| 432 | if (cr.r == 199 && cr.g == 193 && cr.b == 121) { | ||
| 433 | DrawText(x, y, "Correct blend color, blending in linear space"); | ||
| 434 | } else if ((cr.r == 192 && cr.g == 163 && cr.b == 83) || | ||
| 435 | (cr.r == 191 && cr.g == 162 && cr.b == 82)) { | ||
| 436 | DrawText(x, y, "Correct blend color, blending in sRGB space"); | ||
| 437 | } else { | ||
| 438 | DrawText(x, y, "Incorrect blend color, unknown reason"); | ||
| 439 | } | ||
| 440 | y += TEXT_LINE_ADVANCE; | ||
| 441 | |||
| 442 | SDL_DestroyTexture(a); | ||
| 443 | SDL_DestroyTexture(b); | ||
| 444 | } | ||
| 445 | |||
| 446 | static void DrawGradient(float x, float y, float width, float height, float start, float end) | ||
| 447 | { | ||
| 448 | float xy[8]; | ||
| 449 | const int xy_stride = 2 * sizeof(float); | ||
| 450 | SDL_FColor color[4]; | ||
| 451 | const int color_stride = sizeof(SDL_FColor); | ||
| 452 | const int num_vertices = 4; | ||
| 453 | const int indices[6] = { 0, 1, 2, 0, 2, 3 }; | ||
| 454 | const int num_indices = 6; | ||
| 455 | const int size_indices = 4; | ||
| 456 | float minx, miny, maxx, maxy; | ||
| 457 | SDL_FColor min_color = { start, start, start, 1.0f }; | ||
| 458 | SDL_FColor max_color = { end, end, end, 1.0f }; | ||
| 459 | |||
| 460 | minx = x; | ||
| 461 | miny = y; | ||
| 462 | maxx = minx + width; | ||
| 463 | maxy = miny + height; | ||
| 464 | |||
| 465 | xy[0] = minx; | ||
| 466 | xy[1] = miny; | ||
| 467 | xy[2] = maxx; | ||
| 468 | xy[3] = miny; | ||
| 469 | xy[4] = maxx; | ||
| 470 | xy[5] = maxy; | ||
| 471 | xy[6] = minx; | ||
| 472 | xy[7] = maxy; | ||
| 473 | |||
| 474 | color[0] = min_color; | ||
| 475 | color[1] = max_color; | ||
| 476 | color[2] = max_color; | ||
| 477 | color[3] = min_color; | ||
| 478 | |||
| 479 | SDL_RenderGeometryRaw(renderer, NULL, xy, xy_stride, color, color_stride, NULL, 0, num_vertices, indices, num_indices, size_indices); | ||
| 480 | } | ||
| 481 | |||
| 482 | static void RenderGradientDrawing(void) | ||
| 483 | { | ||
| 484 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); | ||
| 485 | SDL_RenderClear(renderer); | ||
| 486 | |||
| 487 | float x = TEXT_START_X; | ||
| 488 | float y = TEXT_START_Y; | ||
| 489 | DrawText(x, y, "%s %s", renderer_name, colorspace_name); | ||
| 490 | y += TEXT_LINE_ADVANCE; | ||
| 491 | DrawText(x, y, "Test: Draw SDR and HDR gradients"); | ||
| 492 | y += TEXT_LINE_ADVANCE; | ||
| 493 | |||
| 494 | y += TEXT_LINE_ADVANCE; | ||
| 495 | |||
| 496 | DrawText(x, y, "SDR gradient"); | ||
| 497 | y += TEXT_LINE_ADVANCE; | ||
| 498 | DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 1.0f); | ||
| 499 | y += 64.0f; | ||
| 500 | |||
| 501 | y += TEXT_LINE_ADVANCE; | ||
| 502 | y += TEXT_LINE_ADVANCE; | ||
| 503 | |||
| 504 | if (HDR_headroom > 1.0f) { | ||
| 505 | DrawText(x, y, "HDR gradient"); | ||
| 506 | } else { | ||
| 507 | DrawText(x, y, "No HDR headroom, HDR and SDR gradient are the same"); | ||
| 508 | } | ||
| 509 | y += TEXT_LINE_ADVANCE; | ||
| 510 | /* Drawing is in the sRGB colorspace, so we need to use the color scale, which is applied in linear space, to get into high dynamic range */ | ||
| 511 | SDL_SetRenderColorScale(renderer, HDR_headroom); | ||
| 512 | DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 1.0f); | ||
| 513 | SDL_SetRenderColorScale(renderer, 1.0f); | ||
| 514 | y += 64.0f; | ||
| 515 | } | ||
| 516 | |||
| 517 | static SDL_Texture *CreateGradientTexture(int width, float start, float end) | ||
| 518 | { | ||
| 519 | SDL_Texture *texture; | ||
| 520 | float *pixels; | ||
| 521 | |||
| 522 | /* Floating point textures are in the linear colorspace by default */ | ||
| 523 | texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA128_FLOAT, SDL_TEXTUREACCESS_STATIC, width, 1); | ||
| 524 | if (!texture) { | ||
| 525 | return NULL; | ||
| 526 | } | ||
| 527 | |||
| 528 | pixels = (float *)SDL_malloc(width * sizeof(float) * 4); | ||
| 529 | if (pixels) { | ||
| 530 | int i; | ||
| 531 | float length = (end - start); | ||
| 532 | |||
| 533 | for (i = 0; i < width; ++i) { | ||
| 534 | float v = (start + (length * i) / width); | ||
| 535 | pixels[i * 4 + 0] = v; | ||
| 536 | pixels[i * 4 + 1] = v; | ||
| 537 | pixels[i * 4 + 2] = v; | ||
| 538 | pixels[i * 4 + 3] = 1.0f; | ||
| 539 | } | ||
| 540 | SDL_UpdateTexture(texture, NULL, pixels, width * sizeof(float) * 4); | ||
| 541 | SDL_free(pixels); | ||
| 542 | } | ||
| 543 | return texture; | ||
| 544 | } | ||
| 545 | |||
| 546 | static void DrawGradientTexture(float x, float y, float width, float height, float start, float end) | ||
| 547 | { | ||
| 548 | SDL_FRect rect = { x, y, width, height }; | ||
| 549 | SDL_Texture *texture = CreateGradientTexture((int)width, start, end); | ||
| 550 | SDL_RenderTexture(renderer, texture, NULL, &rect); | ||
| 551 | SDL_DestroyTexture(texture); | ||
| 552 | } | ||
| 553 | |||
| 554 | static void RenderGradientTexture(void) | ||
| 555 | { | ||
| 556 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); | ||
| 557 | SDL_RenderClear(renderer); | ||
| 558 | |||
| 559 | float x = TEXT_START_X; | ||
| 560 | float y = TEXT_START_Y; | ||
| 561 | DrawText(x, y, "%s %s", renderer_name, colorspace_name); | ||
| 562 | y += TEXT_LINE_ADVANCE; | ||
| 563 | DrawText(x, y, "Test: Texture SDR and HDR gradients"); | ||
| 564 | y += TEXT_LINE_ADVANCE; | ||
| 565 | |||
| 566 | y += TEXT_LINE_ADVANCE; | ||
| 567 | |||
| 568 | DrawText(x, y, "SDR gradient"); | ||
| 569 | y += TEXT_LINE_ADVANCE; | ||
| 570 | DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 1.0f); | ||
| 571 | y += 64.0f; | ||
| 572 | |||
| 573 | y += TEXT_LINE_ADVANCE; | ||
| 574 | y += TEXT_LINE_ADVANCE; | ||
| 575 | |||
| 576 | if (HDR_headroom > 1.0f) { | ||
| 577 | DrawText(x, y, "HDR gradient"); | ||
| 578 | } else { | ||
| 579 | DrawText(x, y, "No HDR headroom, HDR and SDR gradient are the same"); | ||
| 580 | } | ||
| 581 | y += TEXT_LINE_ADVANCE; | ||
| 582 | /* The gradient texture is in the linear colorspace, so we can use the HDR_headroom value directly */ | ||
| 583 | DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, HDR_headroom); | ||
| 584 | y += 64.0f; | ||
| 585 | } | ||
| 586 | |||
| 587 | static void loop(void) | ||
| 588 | { | ||
| 589 | SDL_Event event; | ||
| 590 | |||
| 591 | /* Check for events */ | ||
| 592 | while (SDL_PollEvent(&event)) { | ||
| 593 | if (event.type == SDL_EVENT_KEY_DOWN) { | ||
| 594 | switch (event.key.key) { | ||
| 595 | case SDLK_ESCAPE: | ||
| 596 | done = 1; | ||
| 597 | break; | ||
| 598 | case SDLK_SPACE: | ||
| 599 | case SDLK_RIGHT: | ||
| 600 | NextStage(); | ||
| 601 | break; | ||
| 602 | case SDLK_LEFT: | ||
| 603 | PrevStage(); | ||
| 604 | break; | ||
| 605 | case SDLK_DOWN: | ||
| 606 | NextRenderer(); | ||
| 607 | break; | ||
| 608 | case SDLK_UP: | ||
| 609 | PrevRenderer(); | ||
| 610 | break; | ||
| 611 | default: | ||
| 612 | break; | ||
| 613 | } | ||
| 614 | } else if (event.type == SDL_EVENT_WINDOW_HDR_STATE_CHANGED) { | ||
| 615 | UpdateHDRState(); | ||
| 616 | } else if (event.type == SDL_EVENT_QUIT) { | ||
| 617 | done = 1; | ||
| 618 | } | ||
| 619 | } | ||
| 620 | |||
| 621 | if (renderer) { | ||
| 622 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); | ||
| 623 | SDL_RenderClear(renderer); | ||
| 624 | |||
| 625 | switch (stage_index) { | ||
| 626 | case StageClearBackground: | ||
| 627 | RenderClearBackground(); | ||
| 628 | break; | ||
| 629 | case StageDrawBackground: | ||
| 630 | RenderDrawBackground(); | ||
| 631 | break; | ||
| 632 | case StageTextureBackground: | ||
| 633 | RenderTextureBackground(); | ||
| 634 | break; | ||
| 635 | case StageTargetBackground: | ||
| 636 | RenderTargetBackground(); | ||
| 637 | break; | ||
| 638 | case StageBlendDrawing: | ||
| 639 | RenderBlendDrawing(); | ||
| 640 | break; | ||
| 641 | case StageBlendTexture: | ||
| 642 | RenderBlendTexture(); | ||
| 643 | break; | ||
| 644 | case StageGradientDrawing: | ||
| 645 | RenderGradientDrawing(); | ||
| 646 | break; | ||
| 647 | case StageGradientTexture: | ||
| 648 | RenderGradientTexture(); | ||
| 649 | break; | ||
| 650 | } | ||
| 651 | |||
| 652 | SDL_RenderPresent(renderer); | ||
| 653 | } | ||
| 654 | SDL_Delay(100); | ||
| 655 | |||
| 656 | #ifdef SDL_PLATFORM_EMSCRIPTEN | ||
| 657 | if (done) { | ||
| 658 | emscripten_cancel_main_loop(); | ||
| 659 | } | ||
| 660 | #endif | ||
| 661 | } | ||
| 662 | |||
| 663 | static void LogUsage(const char *argv0) | ||
| 664 | { | ||
| 665 | SDL_Log("Usage: %s [--renderer renderer] [--colorspace colorspace]", argv0); | ||
| 666 | } | ||
| 667 | |||
| 668 | int main(int argc, char *argv[]) | ||
| 669 | { | ||
| 670 | int return_code = 1; | ||
| 671 | int i; | ||
| 672 | |||
| 673 | for (i = 1; i < argc; ++i) { | ||
| 674 | if (SDL_strcmp(argv[i], "--renderer") == 0) { | ||
| 675 | if (argv[i + 1]) { | ||
| 676 | renderer_name = argv[i + 1]; | ||
| 677 | ++i; | ||
| 678 | } else { | ||
| 679 | LogUsage(argv[0]); | ||
| 680 | goto quit; | ||
| 681 | } | ||
| 682 | } else if (SDL_strcmp(argv[i], "--colorspace") == 0) { | ||
| 683 | if (argv[i + 1]) { | ||
| 684 | colorspace_name = argv[i + 1]; | ||
| 685 | if (SDL_strcasecmp(colorspace_name, "sRGB") == 0) { | ||
| 686 | colorspace = SDL_COLORSPACE_SRGB; | ||
| 687 | } else if (SDL_strcasecmp(colorspace_name, "linear") == 0) { | ||
| 688 | colorspace = SDL_COLORSPACE_SRGB_LINEAR; | ||
| 689 | /* Not currently supported | ||
| 690 | } else if (SDL_strcasecmp(colorspace_name, "HDR10") == 0) { | ||
| 691 | colorspace = SDL_COLORSPACE_HDR10; | ||
| 692 | */ | ||
| 693 | } else { | ||
| 694 | SDL_Log("Unknown colorspace %s", argv[i + 1]); | ||
| 695 | goto quit; | ||
| 696 | } | ||
| 697 | ++i; | ||
| 698 | } else { | ||
| 699 | LogUsage(argv[0]); | ||
| 700 | goto quit; | ||
| 701 | } | ||
| 702 | } else { | ||
| 703 | LogUsage(argv[0]); | ||
| 704 | goto quit; | ||
| 705 | } | ||
| 706 | } | ||
| 707 | |||
| 708 | window = SDL_CreateWindow("SDL colorspace test", WINDOW_WIDTH, WINDOW_HEIGHT, 0); | ||
| 709 | if (!window) { | ||
| 710 | SDL_Log("Couldn't create window: %s", SDL_GetError()); | ||
| 711 | return_code = 2; | ||
| 712 | goto quit; | ||
| 713 | } | ||
| 714 | |||
| 715 | renderer_count = SDL_GetNumRenderDrivers(); | ||
| 716 | SDL_Log("There are %d render drivers:", renderer_count); | ||
| 717 | for (i = 0; i < renderer_count; ++i) { | ||
| 718 | const char *name = SDL_GetRenderDriver(i); | ||
| 719 | |||
| 720 | if (renderer_name && SDL_strcasecmp(renderer_name, name) == 0) { | ||
| 721 | renderer_index = i; | ||
| 722 | } | ||
| 723 | SDL_Log(" %s", name); | ||
| 724 | } | ||
| 725 | CreateRenderer(); | ||
| 726 | |||
| 727 | /* Main render loop */ | ||
| 728 | done = 0; | ||
| 729 | |||
| 730 | #ifdef SDL_PLATFORM_EMSCRIPTEN | ||
| 731 | emscripten_set_main_loop(loop, 0, 1); | ||
| 732 | #else | ||
| 733 | while (!done) { | ||
| 734 | loop(); | ||
| 735 | } | ||
| 736 | #endif | ||
| 737 | return_code = 0; | ||
| 738 | quit: | ||
| 739 | SDL_DestroyRenderer(renderer); | ||
| 740 | SDL_DestroyWindow(window); | ||
| 741 | SDL_Quit(); | ||
| 742 | return return_code; | ||
| 743 | } | ||
