SDL/test/testcolorspace.c

421 lines
11 KiB
C
Raw Normal View History

/*
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
*/
#include <SDL3/SDL.h>
#include <SDL3/SDL_test.h>
#include <SDL3/SDL_main.h>
#ifdef SDL_PLATFORM_EMSCRIPTEN
#include <emscripten/emscripten.h>
#endif
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
#define TEXT_START_X 6.0f
#define TEXT_START_Y 6.0f
#define TEXT_LINE_ADVANCE FONT_CHARACTER_SIZE * 2
static SDL_Window *window;
static SDL_Renderer *renderer;
static const char *renderer_name;
static int renderer_count = 0;
static int renderer_index = 0;
static int stage_count = 4;
static int stage_index = 0;
static int done;
static void FreeRenderer(void)
{
SDLTest_CleanupTextDrawing();
SDL_DestroyRenderer(renderer);
renderer = NULL;
}
static void CreateRenderer(void)
{
SDL_PropertiesID props;
SDL_RendererInfo info;
props = SDL_CreateProperties();
SDL_SetProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window);
SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, SDL_GetRenderDriver(renderer_index));
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_INPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB);
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB);
SDL_SetBooleanProperty(props, SDL_PROP_RENDERER_CREATE_COLORSPACE_CONVERSION_BOOLEAN, SDL_TRUE);
renderer = SDL_CreateRendererWithProperties(props);
SDL_DestroyProperties(props);
if (!renderer) {
SDL_Log("Couldn't create renderer: %s\n", SDL_GetError());
return;
}
SDL_GetRendererInfo(renderer, &info);
SDL_Log("Created renderer %s\n", info.name);
renderer_name = info.name;
}
static void NextRenderer( void )
{
if (renderer_count <= 0) {
return;
}
++renderer_index;
if (renderer_index == renderer_count) {
renderer_index = 0;
}
FreeRenderer();
CreateRenderer();
}
static void PrevRenderer(void)
{
if (renderer_count <= 0) {
return;
}
--renderer_index;
if (renderer_index == -1) {
renderer_index += renderer_count;
}
FreeRenderer();
CreateRenderer();
}
static void NextStage(void)
{
if (stage_count <= 0) {
return;
}
++stage_index;
if (stage_index == stage_count) {
stage_index = 0;
}
}
static void PrevStage(void)
{
--stage_index;
if (stage_index == -1) {
stage_index += stage_count;
}
}
static SDL_bool ReadPixel(int x, int y, SDL_Color *c)
{
SDL_Rect r;
r.x = x;
r.y = y;
r.w = 1;
r.h = 1;
if (SDL_RenderReadPixels(renderer, &r, SDL_PIXELFORMAT_RGBA32, c, sizeof(*c)) < 0) {
SDL_Log("Couldn't read back pixels: %s\n", SDL_GetError());
return SDL_FALSE;
}
return SDL_TRUE;
}
static void DrawText(float x, float y, const char *fmt, ...)
{
char *text;
va_list ap;
va_start(ap, fmt);
SDL_vasprintf(&text, fmt, ap);
va_end(ap);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDLTest_DrawString(renderer, x + 1.0f, y + 1.0f, text);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDLTest_DrawString(renderer, x, y, text);
SDL_free(text);
}
static void RenderClearBackground(void)
{
/* Draw a 50% gray background.
* This will be darker when using sRGB colors and lighter using linear colors
*/
SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
SDL_RenderClear(renderer);
/* Check the renderered pixels */
SDL_Color c;
if (!ReadPixel(0, 0, &c)) {
return;
}
float x = TEXT_START_X;
float y = TEXT_START_Y;
DrawText(x, y, renderer_name);
y += TEXT_LINE_ADVANCE;
DrawText(x, y, "Test: Clear 50%% Gray Background");
y += TEXT_LINE_ADVANCE;
DrawText(x, y, "Background color written: 0x808080, read: 0x%.2x%.2x%.2x", c.r, c.g, c.b);
y += TEXT_LINE_ADVANCE;
if (c.r != 128) {
DrawText(x, y, "Incorrect background color, unknown reason");
y += TEXT_LINE_ADVANCE;
}
}
static void RenderDrawBackground(void)
{
/* Draw a 50% gray background.
* This will be darker when using sRGB colors and lighter using linear colors
*/
SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
SDL_RenderFillRect(renderer, NULL);
/* Check the renderered pixels */
SDL_Color c;
if (!ReadPixel(0, 0, &c)) {
return;
}
float x = TEXT_START_X;
float y = TEXT_START_Y;
DrawText(x, y, renderer_name);
y += TEXT_LINE_ADVANCE;
DrawText(x, y, "Test: Draw 50%% Gray Background");
y += TEXT_LINE_ADVANCE;
DrawText(x, y, "Background color written: 0x808080, read: 0x%.2x%.2x%.2x", c.r, c.g, c.b);
y += TEXT_LINE_ADVANCE;
if (c.r != 128) {
DrawText(x, y, "Incorrect background color, unknown reason");
y += TEXT_LINE_ADVANCE;
}
}
static void RenderBlendDrawing(void)
{
SDL_Color a = { 238, 70, 166, 255 }; /* red square */
SDL_Color b = { 147, 255, 0, 255 }; /* green square */
SDL_FRect rect;
/* Draw a green square blended over a red square
* This will have different effects based on whether sRGB colorspaces and sRGB vs linear blending is used.
*/
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
rect.x = WINDOW_WIDTH / 3;
rect.y = 0;
rect.w = WINDOW_WIDTH / 3;
rect.h = WINDOW_HEIGHT;
SDL_SetRenderDrawColor(renderer, a.r, a.g, a.b, a.a);
SDL_RenderFillRect(renderer, &rect);
rect.x = 0;
rect.y = WINDOW_HEIGHT / 3;
rect.w = WINDOW_WIDTH;
rect.h = WINDOW_HEIGHT / 6;
SDL_SetRenderDrawColor(renderer, b.r, b.g, b.b, b.a);
SDL_RenderFillRect(renderer, &rect);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer, b.r, b.g, b.b, 128);
rect.y += WINDOW_HEIGHT / 6;
SDL_RenderFillRect(renderer, &rect);
SDL_Color ar, br, cr;
if (!ReadPixel(WINDOW_WIDTH / 2, 0, &ar) ||
!ReadPixel(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 3, &br) ||
!ReadPixel(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2, &cr)) {
return;
}
float x = TEXT_START_X;
float y = TEXT_START_Y;
DrawText(x, y, renderer_name);
y += TEXT_LINE_ADVANCE;
DrawText(x, y, "Test: Draw Linear Blending");
y += TEXT_LINE_ADVANCE;
if (cr.r == 199 && cr.g == 193 && cr.b == 121) {
DrawText(x, y, "Correct blend color in linear space");
} else if ((cr.r == 192 && cr.g == 163 && cr.b == 83) ||
(cr.r == 191 && cr.g == 162 && cr.b == 82)) {
DrawText(x, y, "Incorrect blend color, blending in sRGB space");
} else {
DrawText(x, y, "Incorrect blend color, unknown reason");
}
y += TEXT_LINE_ADVANCE;
}
static void RenderBlendTexture(void)
{
SDL_Color color_a = { 238, 70, 166, 255 }; /* red square */
SDL_Color color_b = { 147, 255, 0, 255 }; /* green square */
SDL_Texture *a;
SDL_Texture *b;
SDL_FRect rect;
/* Draw a green square blended over a red square
* This will have different effects based on whether sRGB colorspaces and sRGB vs linear blending is used.
*/
a = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, 1, 1);
SDL_UpdateTexture(a, NULL, &color_a, sizeof(color_a));
b = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, 1, 1);
SDL_UpdateTexture(b, NULL, &color_b, sizeof(color_b));
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
rect.x = WINDOW_WIDTH / 3;
rect.y = 0;
rect.w = WINDOW_WIDTH / 3;
rect.h = WINDOW_HEIGHT;
SDL_RenderTexture(renderer, a, NULL, &rect);
rect.x = 0;
rect.y = WINDOW_HEIGHT / 3;
rect.w = WINDOW_WIDTH;
rect.h = WINDOW_HEIGHT / 6;
SDL_RenderTexture(renderer, b, NULL, &rect);
rect.y += WINDOW_HEIGHT / 6;
SDL_SetTextureBlendMode(b, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaModFloat(b, 128 / 255.0f);
SDL_RenderTexture(renderer, b, NULL, &rect);
SDL_Color ar, br, cr;
if (!ReadPixel(WINDOW_WIDTH / 2, 0, &ar) ||
!ReadPixel(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 3, &br) ||
!ReadPixel(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2, &cr)) {
return;
}
float x = TEXT_START_X;
float y = TEXT_START_Y;
DrawText(x, y, renderer_name);
y += TEXT_LINE_ADVANCE;
DrawText(x, y, "Test: Texture Linear Blending");
y += TEXT_LINE_ADVANCE;
if (cr.r == 199 && cr.g == 193 && cr.b == 121) {
DrawText(x, y, "Correct blend color in linear space");
} else if ((cr.r == 192 && cr.g == 163 && cr.b == 83) ||
(cr.r == 191 && cr.g == 162 && cr.b == 82)) {
DrawText(x, y, "Incorrect blend color, blending in sRGB space");
} else {
DrawText(x, y, "Incorrect blend color, unknown reason");
}
y += TEXT_LINE_ADVANCE;
SDL_DestroyTexture(a);
SDL_DestroyTexture(b);
}
static void loop(void)
{
SDL_Event event;
/* Check for events */
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_KEY_DOWN) {
switch (event.key.keysym.sym) {
case SDLK_ESCAPE:
done = 1;
case SDLK_SPACE:
case SDLK_RIGHT:
NextStage();
break;
case SDLK_LEFT:
PrevStage();
break;
case SDLK_DOWN:
NextRenderer();
break;
case SDLK_UP:
PrevRenderer();
break;
default:
break;
}
} else if (event.type == SDL_EVENT_QUIT) {
done = 1;
}
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
switch (stage_index) {
case 0:
RenderClearBackground();
break;
case 1:
RenderDrawBackground();
break;
case 2:
RenderBlendDrawing();
break;
case 3:
RenderBlendTexture();
break;
}
SDL_RenderPresent(renderer);
SDL_Delay(100);
#ifdef SDL_PLATFORM_EMSCRIPTEN
if (done) {
emscripten_cancel_main_loop();
}
#endif
}
int main(int argc, char *argv[])
{
int return_code = -1;
int i;
if (argc > 2) {
SDL_Log("Usage: %s [renderer]\n", argv[0]);
return_code = 1;
goto quit;
}
window = SDL_CreateWindow("SDL colorspace test", WINDOW_WIDTH, WINDOW_HEIGHT, 0);
if (!window) {
SDL_Log("Couldn't create window: %s\n", SDL_GetError());
return_code = 2;
goto quit;
}
renderer_count = SDL_GetNumRenderDrivers();
SDL_Log("There are %d render drivers:\n", renderer_count);
for (i = 0; i < renderer_count; ++i) {
const char *name = SDL_GetRenderDriver(i);
if (argv[1] && SDL_strcasecmp(argv[1], name) == 0) {
renderer_index = i;
}
SDL_Log(" %s\n", name);
}
CreateRenderer();
/* Main render loop */
done = 0;
#ifdef SDL_PLATFORM_EMSCRIPTEN
emscripten_set_main_loop(loop, 0, 1);
#else
while (!done) {
loop();
}
#endif
return_code = 0;
quit:
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return return_code;
}