From 6f443e2acafd14d872c6f1c79e9c61dfe1156813 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 31 Jan 2024 17:25:25 -0800 Subject: [PATCH] Added support for the scRGB colorspace on D3D11 and D3D12 --- include/SDL3/SDL_surface.h | 163 +++++++++++++---------- src/dynapi/SDL_dynapi.sym | 4 + src/dynapi/SDL_dynapi_overrides.h | 4 + src/dynapi/SDL_dynapi_procs.h | 4 + src/render/SDL_render.c | 37 ++--- src/render/direct3d/SDL_render_d3d.c | 10 +- src/render/direct3d11/SDL_render_d3d11.c | 122 +++++++++++++---- src/render/direct3d12/SDL_render_d3d12.c | 69 ++++++++-- src/render/metal/SDL_render_metal.m | 9 ++ src/render/opengl/SDL_render_gl.c | 13 +- src/render/opengles2/SDL_render_gles2.c | 14 +- src/render/ps2/SDL_render_ps2.c | 9 ++ src/render/psp/SDL_render_psp.c | 9 ++ src/render/software/SDL_render_sw.c | 1 + src/render/vitagxm/SDL_render_vita_gxm.c | 9 ++ src/video/SDL_blit.c | 20 ++- src/video/SDL_blit.h | 34 +++-- src/video/SDL_blit_slow.c | 144 +++++++------------- src/video/SDL_pixels.c | 72 ++++++++++ src/video/SDL_pixels_c.h | 10 ++ src/video/SDL_surface.c | 128 +++++++++++++----- test/testcolorspace.c | 92 +++++++++---- 22 files changed, 654 insertions(+), 323 deletions(-) diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 084cf632c..4b8f1e426 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -216,8 +216,7 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface); * * The following properties are understood by SDL: * - * - `SDL_PROP_SURFACE_COLORSPACE_NUMBER`: an SDL_ColorSpace value describing - * the surface colorspace + * - `SDL_PROP_SURFACE_COLORSPACE_NUMBER`: an SDL_ColorSpace value describing the surface colorspace, defaults to SDL_COLORSPACE_SCRGB for floating point formats, SDL_COLORSPACE_HDR10 for 10-bit formats, SDL_COLORSPACE_SRGB for other RGB surfaces and SDL_COLORSPACE_BT709_FULL for YUV textures. * - `SDL_PROP_SURFACE_MAXCLL_NUMBER`: MaxCLL (Maximum Content Light Level) * indicates the maximum light level of any single pixel (in cd/m2 or nits) * of the entire playback sequence. MaxCLL is usually measured off the final @@ -245,6 +244,34 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetSurfaceProperties(SDL_Surface *s #define SDL_PROP_SURFACE_MAXCLL_NUMBER "SDL.surface.maxCLL" #define SDL_PROP_SURFACE_MAXFALL_NUMBER "SDL.surface.maxFALL" +/** + * Set the colorspace used by a surface. + * + * Setting the colorspace doesn't change the pixels, only how they are interpreted in color operations. + * + * \param surface the SDL_Surface structure to update + * \param colorspace an SDL_ColorSpace value describing the surface colorspace + * \returns 0 on success or a negative error code on failure; call + * SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC int SDLCALL SDL_SetSurfaceColorspace(SDL_Surface *surface, SDL_Colorspace colorspace); + +/** + * Get the colorspace used by a surface. + * + * The colorspace defaults to SDL_COLORSPACE_SCRGB for floating point formats, SDL_COLORSPACE_HDR10 for 10-bit formats, SDL_COLORSPACE_SRGB for other RGB surfaces and SDL_COLORSPACE_BT709_FULL for YUV textures. + * + * \param surface the SDL_Surface structure to query + * \param colorspace a pointer filled in with an SDL_ColorSpace value describing the surface colorspace + * \returns 0 on success or a negative error code on failure; call + * SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC int SDLCALL SDL_GetSurfaceColorspace(SDL_Surface *surface, SDL_Colorspace *colorspace); + /** * Set the palette used by a surface. * @@ -257,8 +284,7 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetSurfaceProperties(SDL_Surface *s * * \since This function is available since SDL 3.0.0. */ -extern DECLSPEC int SDLCALL SDL_SetSurfacePalette(SDL_Surface *surface, - SDL_Palette *palette); +extern DECLSPEC int SDLCALL SDL_SetSurfacePalette(SDL_Surface *surface, SDL_Palette *palette); /** * Set up a surface for directly accessing the pixels. @@ -393,8 +419,7 @@ extern DECLSPEC int SDLCALL SDL_SaveBMP(SDL_Surface *surface, const char *file); * \sa SDL_LockSurface * \sa SDL_UnlockSurface */ -extern DECLSPEC int SDLCALL SDL_SetSurfaceRLE(SDL_Surface *surface, - int flag); +extern DECLSPEC int SDLCALL SDL_SetSurfaceRLE(SDL_Surface *surface, int flag); /** * Returns whether the surface is RLE enabled @@ -434,8 +459,7 @@ extern DECLSPEC SDL_bool SDLCALL SDL_SurfaceHasRLE(SDL_Surface *surface); * \sa SDL_BlitSurface * \sa SDL_GetSurfaceColorKey */ -extern DECLSPEC int SDLCALL SDL_SetSurfaceColorKey(SDL_Surface *surface, - int flag, Uint32 key); +extern DECLSPEC int SDLCALL SDL_SetSurfaceColorKey(SDL_Surface *surface, int flag, Uint32 key); /** * Returns whether the surface has a color key @@ -470,8 +494,7 @@ extern DECLSPEC SDL_bool SDLCALL SDL_SurfaceHasColorKey(SDL_Surface *surface); * \sa SDL_BlitSurface * \sa SDL_SetSurfaceColorKey */ -extern DECLSPEC int SDLCALL SDL_GetSurfaceColorKey(SDL_Surface *surface, - Uint32 *key); +extern DECLSPEC int SDLCALL SDL_GetSurfaceColorKey(SDL_Surface *surface, Uint32 *key); /** * Set an additional color value multiplied into blit operations. @@ -494,8 +517,7 @@ extern DECLSPEC int SDLCALL SDL_GetSurfaceColorKey(SDL_Surface *surface, * \sa SDL_GetSurfaceColorMod * \sa SDL_SetSurfaceAlphaMod */ -extern DECLSPEC int SDLCALL SDL_SetSurfaceColorMod(SDL_Surface *surface, - Uint8 r, Uint8 g, Uint8 b); +extern DECLSPEC int SDLCALL SDL_SetSurfaceColorMod(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b); /** @@ -513,9 +535,7 @@ extern DECLSPEC int SDLCALL SDL_SetSurfaceColorMod(SDL_Surface *surface, * \sa SDL_GetSurfaceAlphaMod * \sa SDL_SetSurfaceColorMod */ -extern DECLSPEC int SDLCALL SDL_GetSurfaceColorMod(SDL_Surface *surface, - Uint8 *r, Uint8 *g, - Uint8 *b); +extern DECLSPEC int SDLCALL SDL_GetSurfaceColorMod(SDL_Surface *surface, Uint8 *r, Uint8 *g, Uint8 *b); /** * Set an additional alpha value used in blit operations. @@ -535,8 +555,7 @@ extern DECLSPEC int SDLCALL SDL_GetSurfaceColorMod(SDL_Surface *surface, * \sa SDL_GetSurfaceAlphaMod * \sa SDL_SetSurfaceColorMod */ -extern DECLSPEC int SDLCALL SDL_SetSurfaceAlphaMod(SDL_Surface *surface, - Uint8 alpha); +extern DECLSPEC int SDLCALL SDL_SetSurfaceAlphaMod(SDL_Surface *surface, Uint8 alpha); /** * Get the additional alpha value used in blit operations. @@ -551,8 +570,7 @@ extern DECLSPEC int SDLCALL SDL_SetSurfaceAlphaMod(SDL_Surface *surface, * \sa SDL_GetSurfaceColorMod * \sa SDL_SetSurfaceAlphaMod */ -extern DECLSPEC int SDLCALL SDL_GetSurfaceAlphaMod(SDL_Surface *surface, - Uint8 *alpha); +extern DECLSPEC int SDLCALL SDL_GetSurfaceAlphaMod(SDL_Surface *surface, Uint8 *alpha); /** * Set the blend mode used for blit operations. @@ -570,8 +588,7 @@ extern DECLSPEC int SDLCALL SDL_GetSurfaceAlphaMod(SDL_Surface *surface, * * \sa SDL_GetSurfaceBlendMode */ -extern DECLSPEC int SDLCALL SDL_SetSurfaceBlendMode(SDL_Surface *surface, - SDL_BlendMode blendMode); +extern DECLSPEC int SDLCALL SDL_SetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode blendMode); /** * Get the blend mode used for blit operations. @@ -585,8 +602,7 @@ extern DECLSPEC int SDLCALL SDL_SetSurfaceBlendMode(SDL_Surface *surface, * * \sa SDL_SetSurfaceBlendMode */ -extern DECLSPEC int SDLCALL SDL_GetSurfaceBlendMode(SDL_Surface *surface, - SDL_BlendMode *blendMode); +extern DECLSPEC int SDLCALL SDL_GetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode *blendMode); /** * Set the clipping rectangle for a surface. @@ -608,8 +624,7 @@ extern DECLSPEC int SDLCALL SDL_GetSurfaceBlendMode(SDL_Surface *surface, * \sa SDL_BlitSurface * \sa SDL_GetSurfaceClipRect */ -extern DECLSPEC SDL_bool SDLCALL SDL_SetSurfaceClipRect(SDL_Surface *surface, - const SDL_Rect *rect); +extern DECLSPEC SDL_bool SDLCALL SDL_SetSurfaceClipRect(SDL_Surface *surface, const SDL_Rect *rect); /** * Get the clipping rectangle for a surface. @@ -629,8 +644,7 @@ extern DECLSPEC SDL_bool SDLCALL SDL_SetSurfaceClipRect(SDL_Surface *surface, * \sa SDL_BlitSurface * \sa SDL_SetSurfaceClipRect */ -extern DECLSPEC int SDLCALL SDL_GetSurfaceClipRect(SDL_Surface *surface, - SDL_Rect *rect); +extern DECLSPEC int SDLCALL SDL_GetSurfaceClipRect(SDL_Surface *surface, SDL_Rect *rect); /* * Flip a surface vertically or horizontally. @@ -677,11 +691,10 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_DuplicateSurface(SDL_Surface *surface); * \sa SDL_ConvertSurfaceFormat * \sa SDL_CreateSurface */ -extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurface(SDL_Surface *surface, - const SDL_PixelFormat *format); +extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurface(SDL_Surface *surface, const SDL_PixelFormat *format); /** - * Copy an existing surface to a new surface of the specified format enum. + * Copy an existing surface to a new surface of the specified format. * * This function operates just like SDL_ConvertSurface(), but accepts an * SDL_PixelFormatEnum value instead of an SDL_PixelFormat structure. As such, @@ -689,8 +702,7 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurface(SDL_Surface *surface, * information for the destination surface, in case that would be important. * * \param surface the existing SDL_Surface structure to convert - * \param pixel_format the SDL_PixelFormatEnum that the new surface is - * optimized for + * \param pixel_format the new pixel format * \returns the new SDL_Surface structure that is created or NULL if it fails; * call SDL_GetError() for more information. * @@ -700,8 +712,26 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurface(SDL_Surface *surface, * \sa SDL_ConvertSurface * \sa SDL_CreateSurface */ -extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurfaceFormat(SDL_Surface *surface, - Uint32 pixel_format); +extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurfaceFormat(SDL_Surface *surface, Uint32 pixel_format); + +/** + * Copy an existing surface to a new surface of the specified format and colorspace. + * + * This function converts an existing surface to a new format and colorspace and returns the new surface. This will perform any pixel format and colorspace conversion needed. + * + * \param surface the existing SDL_Surface structure to convert + * \param pixel_format the new pixel format + * \param colorspace the new colorspace + * \returns the new SDL_Surface structure that is created or NULL if it fails; + * call SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_CreatePixelFormat + * \sa SDL_ConvertSurface + * \sa SDL_CreateSurface + */ +extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurfaceFormatAndColorspace(SDL_Surface *surface, Uint32 pixel_format, SDL_Colorspace colorspace); /** * Copy a block of pixels of one format to another format. @@ -719,11 +749,27 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurfaceFormat(SDL_Surface *surfa * * \since This function is available since SDL 3.0.0. */ -extern DECLSPEC int SDLCALL SDL_ConvertPixels(int width, int height, - Uint32 src_format, - const void *src, int src_pitch, - Uint32 dst_format, - void *dst, int dst_pitch); +extern DECLSPEC int SDLCALL SDL_ConvertPixels(int width, int height, Uint32 src_format, const void *src, int src_pitch, Uint32 dst_format, void *dst, int dst_pitch); + +/** + * Copy a block of pixels of one format and colorspace to another format and colorspace. + * + * \param width the width of the block to copy, in pixels + * \param height the height of the block to copy, in pixels + * \param src_format an SDL_PixelFormatEnum value of the `src` pixels format + * \param src_colorspace an SDL_ColorSpace value describing the colorspace of the `src` pixels + * \param src a pointer to the source pixels + * \param src_pitch the pitch of the source pixels, in bytes + * \param dst_format an SDL_PixelFormatEnum value of the `dst` pixels format + * \param dst_colorspace an SDL_ColorSpace value describing the colorspace of the `dst` pixels + * \param dst a pointer to be filled in with new pixel data + * \param dst_pitch the pitch of the destination pixels, in bytes + * \returns 0 on success or a negative error code on failure; call + * SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC int SDLCALL SDL_ConvertPixelsAndColorspace(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch); /** * Premultiply the alpha on a block of pixels. @@ -745,11 +791,7 @@ extern DECLSPEC int SDLCALL SDL_ConvertPixels(int width, int height, * * \since This function is available since SDL 3.0.0. */ -extern DECLSPEC int SDLCALL SDL_PremultiplyAlpha(int width, int height, - Uint32 src_format, - const void *src, int src_pitch, - Uint32 dst_format, - void *dst, int dst_pitch); +extern DECLSPEC int SDLCALL SDL_PremultiplyAlpha(int width, int height, Uint32 src_format, const void *src, int src_pitch, Uint32 dst_format, void *dst, int dst_pitch); /** * Perform a fast fill of a rectangle with a specific color. @@ -774,8 +816,7 @@ extern DECLSPEC int SDLCALL SDL_PremultiplyAlpha(int width, int height, * * \sa SDL_FillSurfaceRects */ -extern DECLSPEC int SDLCALL SDL_FillSurfaceRect - (SDL_Surface *dst, const SDL_Rect *rect, Uint32 color); +extern DECLSPEC int SDLCALL SDL_FillSurfaceRect(SDL_Surface *dst, const SDL_Rect *rect, Uint32 color); /** * Perform a fast fill of a set of rectangles with a specific color. @@ -800,8 +841,7 @@ extern DECLSPEC int SDLCALL SDL_FillSurfaceRect * * \sa SDL_FillSurfaceRect */ -extern DECLSPEC int SDLCALL SDL_FillSurfaceRects - (SDL_Surface *dst, const SDL_Rect *rects, int count, Uint32 color); +extern DECLSPEC int SDLCALL SDL_FillSurfaceRects(SDL_Surface *dst, const SDL_Rect *rects, int count, Uint32 color); /** * Performs a fast blit from the source surface to the destination surface. @@ -871,9 +911,7 @@ extern DECLSPEC int SDLCALL SDL_FillSurfaceRects * * \sa SDL_BlitSurfaceScaled */ -extern DECLSPEC int SDLCALL SDL_BlitSurface - (SDL_Surface *src, const SDL_Rect *srcrect, - SDL_Surface *dst, SDL_Rect *dstrect); +extern DECLSPEC int SDLCALL SDL_BlitSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect); /** * Perform low-level surface blitting only. @@ -894,9 +932,7 @@ extern DECLSPEC int SDLCALL SDL_BlitSurface * * \sa SDL_BlitSurface */ -extern DECLSPEC int SDLCALL SDL_BlitSurfaceUnchecked - (SDL_Surface *src, const SDL_Rect *srcrect, - SDL_Surface *dst, const SDL_Rect *dstrect); +extern DECLSPEC int SDLCALL SDL_BlitSurfaceUnchecked(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect); /** * Perform stretch blit between two surfaces of the same format. @@ -918,11 +954,7 @@ extern DECLSPEC int SDLCALL SDL_BlitSurfaceUnchecked * * \sa SDL_BlitSurfaceScaled */ -extern DECLSPEC int SDLCALL SDL_SoftStretch(SDL_Surface *src, - const SDL_Rect *srcrect, - SDL_Surface *dst, - const SDL_Rect *dstrect, - SDL_ScaleMode scaleMode); +extern DECLSPEC int SDLCALL SDL_SoftStretch(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode); /** * Perform a scaled surface copy to a destination surface. @@ -942,11 +974,7 @@ extern DECLSPEC int SDLCALL SDL_SoftStretch(SDL_Surface *src, * * \sa SDL_BlitSurface */ -extern DECLSPEC int SDLCALL SDL_BlitSurfaceScaled(SDL_Surface *src, - const SDL_Rect *srcrect, - SDL_Surface *dst, - SDL_Rect *dstrect, - SDL_ScaleMode scaleMode); +extern DECLSPEC int SDLCALL SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect, SDL_ScaleMode scaleMode); /** * Perform low-level surface scaled blitting only. @@ -968,11 +996,7 @@ extern DECLSPEC int SDLCALL SDL_BlitSurfaceScaled(SDL_Surface *src, * * \sa SDL_BlitSurfaceScaled */ -extern DECLSPEC int SDLCALL SDL_BlitSurfaceUncheckedScaled(SDL_Surface *src, - const SDL_Rect *srcrect, - SDL_Surface *dst, - const SDL_Rect *dstrect, - SDL_ScaleMode scaleMode); +extern DECLSPEC int SDLCALL SDL_BlitSurfaceUncheckedScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode); /** * Retrieves a single pixel from a surface. @@ -1001,7 +1025,6 @@ extern DECLSPEC int SDLCALL SDL_BlitSurfaceUncheckedScaled(SDL_Surface *src, */ extern DECLSPEC int SDLCALL SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a); - /** * Set the YUV conversion mode * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 6e7fa2ee3..1dc5a5405 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -965,6 +965,10 @@ SDL3_0.0.0 { SDL_GetTextureAlphaModFloat; SDL_SetRenderDrawColorFloat; SDL_GetRenderDrawColorFloat; + SDL_ConvertPixelsAndColorspace; + SDL_SetSurfaceColorspace; + SDL_GetSurfaceColorspace; + SDL_ConvertSurfaceFormatAndColorspace; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index cae15b798..42a2e4846 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -990,3 +990,7 @@ #define SDL_GetTextureAlphaModFloat SDL_GetTextureAlphaModFloat_REAL #define SDL_SetRenderDrawColorFloat SDL_SetRenderDrawColorFloat_REAL #define SDL_GetRenderDrawColorFloat SDL_GetRenderDrawColorFloat_REAL +#define SDL_ConvertPixelsAndColorspace SDL_ConvertPixelsAndColorspace_REAL +#define SDL_SetSurfaceColorspace SDL_SetSurfaceColorspace_REAL +#define SDL_GetSurfaceColorspace SDL_GetSurfaceColorspace_REAL +#define SDL_ConvertSurfaceFormatAndColorspace SDL_ConvertSurfaceFormatAndColorspace_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 05561268a..b6221bb51 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1015,3 +1015,7 @@ SDL_DYNAPI_PROC(int,SDL_SetTextureAlphaModFloat,(SDL_Texture *a, float b),(a,b), SDL_DYNAPI_PROC(int,SDL_GetTextureAlphaModFloat,(SDL_Texture *a, float *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_SetRenderDrawColorFloat,(SDL_Renderer *a, float b, float c, float d, float e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(int,SDL_GetRenderDrawColorFloat,(SDL_Renderer *a, float *b, float *c, float *d, float *e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(int,SDL_ConvertPixelsAndColorspace,(int a, int b, Uint32 c, SDL_Colorspace d, const void *e, int f, Uint32 g, SDL_Colorspace h, void *i, int j),(a,b,c,d,e,f,g,h,i,j),return) +SDL_DYNAPI_PROC(int,SDL_SetSurfaceColorspace,(SDL_Surface *a, SDL_Colorspace b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_GetSurfaceColorspace,(SDL_Surface *a, SDL_Colorspace *b),(a,b),return) +SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurfaceFormatAndColorspace,(SDL_Surface *a, Uint32 b, SDL_Colorspace c),(a,b,c),return) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 12e2d9428..a94bf9c5b 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -865,7 +865,7 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props) SDL_Renderer *renderer = NULL; const int n = SDL_GetNumRenderDrivers(); const char *hint; - int i; + int i, attempted = 0; if (!window && surface) { return SDL_CreateSoftwareRenderer(surface); @@ -904,6 +904,7 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props) const SDL_RenderDriver *driver = render_drivers[i]; if (SDL_strcasecmp(name, driver->info.name) == 0) { /* Create a new renderer instance */ + ++attempted; renderer = driver->CreateRenderer(window, props); break; } @@ -912,6 +913,7 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props) for (i = 0; i < n; i++) { const SDL_RenderDriver *driver = render_drivers[i]; /* Create a new renderer instance */ + ++attempted; renderer = driver->CreateRenderer(window, props); if (renderer) { /* Yay, we got one! */ @@ -921,7 +923,9 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props) } if (!renderer) { - SDL_SetError("Couldn't find matching render driver"); + if (!name || !attempted) { + SDL_SetError("Couldn't find matching render driver"); + } goto error; } @@ -1175,19 +1179,6 @@ static SDL_ScaleMode SDL_GetScaleMode(void) } } -static SDL_Colorspace SDL_GetDefaultTextureColorspace(Uint32 format) -{ - if (SDL_ISPIXELFORMAT_FOURCC(format)) { - return SDL_COLORSPACE_BT709_FULL; - } else if (SDL_ISPIXELFORMAT_FLOAT(format)) { - return SDL_COLORSPACE_SCRGB; - } else if (SDL_ISPIXELFORMAT_10BIT(format)) { - return SDL_COLORSPACE_HDR10; - } else { - return SDL_COLORSPACE_SRGB; - } -} - SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_PropertiesID props) { SDL_Texture *texture; @@ -1195,7 +1186,7 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert int access = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); int w = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, 0); int h = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, 0); - Uint32 default_colorspace; + SDL_Colorspace default_colorspace; SDL_bool texture_is_fourcc_and_target; CHECK_RENDERER_MAGIC(renderer, NULL); @@ -1223,14 +1214,14 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert return NULL; } - default_colorspace = SDL_GetDefaultTextureColorspace(format); + default_colorspace = SDL_GetDefaultColorspaceForFormat(format); texture = (SDL_Texture *)SDL_calloc(1, sizeof(*texture)); if (!texture) { return NULL; } texture->magic = &SDL_texture_magic; - texture->colorspace = (Uint32)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, default_colorspace); + texture->colorspace = (SDL_Colorspace)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, default_colorspace); texture->format = format; texture->access = access; texture->w = w; @@ -1334,7 +1325,7 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s Uint32 format = SDL_PIXELFORMAT_UNKNOWN; SDL_Texture *texture; SDL_PropertiesID props; - SDL_Colorspace default_colorspace, colorspace; + SDL_Colorspace colorspace = SDL_COLORSPACE_UNKNOWN; CHECK_RENDERER_MAGIC(renderer, NULL); @@ -1414,12 +1405,8 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s direct_update = SDL_FALSE; } - if (direct_update) { - default_colorspace = SDL_GetDefaultTextureColorspace(format); - colorspace = (SDL_Colorspace)SDL_GetNumberProperty(SDL_GetSurfaceProperties(surface), SDL_PROP_SURFACE_COLORSPACE_NUMBER, default_colorspace); - } else { - /* We're updating through an intermediate surface, so we lose colorspace information */ - colorspace = SDL_COLORSPACE_RGB_DEFAULT; + if (SDL_GetSurfaceColorspace(surface, &colorspace) < 0) { + return NULL; } props = SDL_CreateProperties(); diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index 08ca0e676..4ba113a93 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -1566,6 +1566,14 @@ SDL_Renderer *D3D_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_pro } renderer->magic = &SDL_renderer_magic; + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { + SDL_SetError("Unsupported output colorspace"); + SDL_free(renderer); + return NULL; + } + data = (D3D_RenderData *)SDL_calloc(1, sizeof(*data)); if (!data) { SDL_free(renderer); @@ -1573,9 +1581,9 @@ SDL_Renderer *D3D_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_pro } if (!D3D_LoadDLL(&data->d3dDLL, &data->d3d)) { + SDL_SetError("Unable to create Direct3D interface"); SDL_free(renderer); SDL_free(data); - SDL_SetError("Unable to create Direct3D interface"); return NULL; } diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 8966bda40..45e5fcb76 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -31,13 +31,14 @@ #include "../SDL_d3dmath.h" #include +#include #include "SDL_shaders_d3d11.h" #ifdef SDL_PLATFORM_WINRT #if NTDDI_VERSION > NTDDI_WIN8 -#include +#include #endif #include "SDL_render_winrt.h" @@ -189,6 +190,7 @@ static const GUID SDL_IID_ID3D11Texture2D = { 0x6f15aaf2, 0xd208, 0x4e89, { 0x9a static const GUID SDL_IID_ID3D11Device1 = { 0xa04bfb29, 0x08ef, 0x43d6, { 0xa4, 0x9c, 0xa9, 0xbd, 0xbd, 0xcb, 0xe6, 0x86 } }; static const GUID SDL_IID_ID3D11DeviceContext1 = { 0xbb2c6faa, 0xb5fb, 0x4082, { 0x8e, 0x6b, 0x38, 0x8b, 0x8c, 0xfa, 0x90, 0xe1 } }; /*static const GUID SDL_IID_ID3D11Debug = { 0x79cf2233, 0x7536, 0x4948, { 0x9d, 0x36, 0x1e, 0x46, 0x92, 0xdc, 0x57, 0x60 } };*/ +static const GUID SDL_IID_IDXGISwapChain2 = { 0x94d99bdb, 0xf1f8, 0x4ab0, { 0xb2, 0x36, 0x7d, 0xa0, 0x17, 0x0e, 0xda, 0xb1 } }; #ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA #pragma GCC diagnostic pop @@ -203,6 +205,10 @@ Uint32 D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat) case DXGI_FORMAT_B8G8R8X8_UNORM: case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: return SDL_PIXELFORMAT_XRGB8888; + case DXGI_FORMAT_R10G10B10A2_UNORM: + return SDL_PIXELFORMAT_XBGR2101010; + case DXGI_FORMAT_R16G16B16A16_FLOAT: + return SDL_PIXELFORMAT_RGBA64_FLOAT; default: return SDL_PIXELFORMAT_UNKNOWN; } @@ -271,21 +277,12 @@ static void D3D11_ReleaseAll(SDL_Renderer *renderer) if (data) { int i; - SAFE_RELEASE(data->dxgiFactory); - SAFE_RELEASE(data->dxgiAdapter); - SAFE_RELEASE(data->d3dDevice); - SAFE_RELEASE(data->d3dContext); - SAFE_RELEASE(data->swapChain); - SAFE_RELEASE(data->mainRenderTargetView); - SAFE_RELEASE(data->currentOffscreenRenderTargetView); - SAFE_RELEASE(data->inputLayout); - for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) { - SAFE_RELEASE(data->vertexBuffers[i]); - } - SAFE_RELEASE(data->vertexShader); - for (i = 0; i < SDL_arraysize(data->pixelShaders); ++i) { - SAFE_RELEASE(data->pixelShaders[i]); - } + SAFE_RELEASE(data->vertexShaderConstants); + SAFE_RELEASE(data->clippedRasterizer); + SAFE_RELEASE(data->mainRasterizer); + SAFE_RELEASE(data->linearSampler); + SAFE_RELEASE(data->nearestPixelSampler); + if (data->blendModesCount > 0) { for (i = 0; i < data->blendModesCount; ++i) { SAFE_RELEASE(data->blendModes[i].blendState); @@ -294,11 +291,26 @@ static void D3D11_ReleaseAll(SDL_Renderer *renderer) data->blendModesCount = 0; } - SAFE_RELEASE(data->nearestPixelSampler); - SAFE_RELEASE(data->linearSampler); - SAFE_RELEASE(data->mainRasterizer); - SAFE_RELEASE(data->clippedRasterizer); - SAFE_RELEASE(data->vertexShaderConstants); + for (i = 0; i < SDL_arraysize(data->pixelShaders); ++i) { + SAFE_RELEASE(data->pixelShaders[i]); + } + SAFE_RELEASE(data->vertexShader); + for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) { + SAFE_RELEASE(data->vertexBuffers[i]); + } + SAFE_RELEASE(data->inputLayout); + SAFE_RELEASE(data->currentOffscreenRenderTargetView); + SAFE_RELEASE(data->mainRenderTargetView); + SAFE_RELEASE(data->swapChain); + + /* Make sure the swap chain is fully released */ + ID3D11DeviceContext_ClearState(data->d3dContext); + ID3D11DeviceContext_Flush(data->d3dContext); + + SAFE_RELEASE(data->d3dContext); + SAFE_RELEASE(data->d3dDevice); + SAFE_RELEASE(data->dxgiAdapter); + SAFE_RELEASE(data->dxgiFactory); data->swapEffect = (DXGI_SWAP_EFFECT)0; data->rotation = DXGI_MODE_ROTATION_UNSPECIFIED; @@ -779,7 +791,17 @@ static HRESULT D3D11_CreateSwapChain(SDL_Renderer *renderer, int w, int h) SDL_zero(swapChainDesc); swapChainDesc.Width = w; swapChainDesc.Height = h; - swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; /* This is the most common swap chain format. */ + switch (renderer->output_colorspace) { + case SDL_COLORSPACE_SCRGB: + swapChainDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; + break; + case SDL_COLORSPACE_HDR10: + swapChainDesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; + break; + default: + swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; /* This is the most common swap chain format. */ + break; + } swapChainDesc.Stereo = FALSE; swapChainDesc.SampleDesc.Count = 1; /* Don't use multi-sampling. */ swapChainDesc.SampleDesc.Quality = 0; @@ -865,6 +887,28 @@ static HRESULT D3D11_CreateSwapChain(SDL_Renderer *renderer, int w, int h) } data->swapEffect = swapChainDesc.SwapEffect; + IDXGISwapChain3 *swapChain3 = NULL; + if (SUCCEEDED(IDXGISwapChain1_QueryInterface(data->swapChain, &SDL_IID_IDXGISwapChain2, (void **)&swapChain3))) { + DXGI_COLOR_SPACE_TYPE ColorSpace; + switch (renderer->output_colorspace) { + case SDL_COLORSPACE_SCRGB: + ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; + break; + case SDL_COLORSPACE_HDR10: + ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + break; + default: + /* sRGB */ + ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + break; + } + result = IDXGISwapChain3_SetColorSpace1(swapChain3, ColorSpace); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain3::SetColorSpace1"), result); + goto done; + } + } + done: SAFE_RELEASE(coreWindow); return result; @@ -1004,10 +1048,20 @@ static HRESULT D3D11_CreateWindowSizeDependentResources(SDL_Renderer *renderer) /* Create a render target view of the swap chain back buffer. */ D3D11_RENDER_TARGET_VIEW_DESC desc; SDL_zero(desc); - if (renderer->colorspace_conversion && renderer->output_colorspace == SDL_COLORSPACE_SRGB) { - desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; - } else { - desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + switch (renderer->output_colorspace) { + case SDL_COLORSPACE_SCRGB: + desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; + break; + case SDL_COLORSPACE_HDR10: + desc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; + break; + default: + if (renderer->colorspace_conversion && renderer->output_colorspace == SDL_COLORSPACE_SRGB) { + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + } else { + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + } + break; } desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; result = ID3D11Device_CreateRenderTargetView(data->d3dDevice, @@ -2354,12 +2408,14 @@ static int D3D11_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, /* Copy the data into the desired buffer, converting pixels to the * desired format at the same time: */ - status = SDL_ConvertPixels( + status = SDL_ConvertPixelsAndColorspace( rect->w, rect->h, D3D11_DXGIFormatToSDLPixelFormat(stagingTextureDesc.Format), + renderer->target ? renderer->target->colorspace : renderer->output_colorspace, textureMemory.pData, textureMemory.RowPitch, format, + renderer->input_colorspace, pixels, pitch); @@ -2457,14 +2513,22 @@ SDL_Renderer *D3D11_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_p } renderer->magic = &SDL_renderer_magic; + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_SRGB && + renderer->output_colorspace != SDL_COLORSPACE_SCRGB && + renderer->output_colorspace != SDL_COLORSPACE_HDR10) { + SDL_SetError("Unsupported output colorspace"); + SDL_free(renderer); + return NULL; + } + data = (D3D11_RenderData *)SDL_calloc(1, sizeof(*data)); if (!data) { SDL_free(renderer); return NULL; } - SDL_SetupRendererColorspace(renderer, create_props); - data->identity = MatrixIdentity(); renderer->WindowEvent = D3D11_WindowEvent; diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index 10fe44350..e2999b1ac 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -174,6 +174,7 @@ typedef struct ID3D12GraphicsCommandList2 *commandList; DXGI_SWAP_EFFECT swapEffect; UINT swapFlags; + DXGI_FORMAT renderTargetFormat; SDL_bool pixelSizeChanged; /* Descriptor heaps */ @@ -278,6 +279,10 @@ Uint32 D3D12_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat) case DXGI_FORMAT_B8G8R8X8_UNORM: case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: return SDL_PIXELFORMAT_XRGB8888; + case DXGI_FORMAT_R10G10B10A2_UNORM: + return SDL_PIXELFORMAT_XBGR2101010; + case DXGI_FORMAT_R16G16B16A16_FLOAT: + return SDL_PIXELFORMAT_RGBA64_FLOAT; default: return SDL_PIXELFORMAT_UNKNOWN; } @@ -751,6 +756,8 @@ static HRESULT D3D12_CreateDeviceResources(SDL_Renderer *renderer) SDL_BLENDMODE_MUL }; const DXGI_FORMAT defaultRTVFormats[] = { + DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8X8_UNORM, DXGI_FORMAT_R8_UNORM @@ -1178,7 +1185,24 @@ static HRESULT D3D12_CreateSwapChain(SDL_Renderer *renderer, int w, int h) SDL_zero(swapChainDesc); swapChainDesc.Width = w; swapChainDesc.Height = h; - swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; /* This is the most common swap chain format. */ + switch (renderer->output_colorspace) { + case SDL_COLORSPACE_SCRGB: + swapChainDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; + data->renderTargetFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; + break; + case SDL_COLORSPACE_HDR10: + swapChainDesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM; + data->renderTargetFormat = DXGI_FORMAT_R10G10B10A2_UNORM; + break; + default: + swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; /* This is the most common swap chain format. */ + if (renderer->colorspace_conversion && renderer->output_colorspace == SDL_COLORSPACE_SRGB) { + data->renderTargetFormat = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + } else { + data->renderTargetFormat = DXGI_FORMAT_B8G8R8A8_UNORM; + } + break; + } swapChainDesc.Stereo = FALSE; swapChainDesc.SampleDesc.Count = 1; /* Don't use multi-sampling. */ swapChainDesc.SampleDesc.Quality = 0; @@ -1227,6 +1251,25 @@ static HRESULT D3D12_CreateSwapChain(SDL_Renderer *renderer, int w, int h) data->swapEffect = swapChainDesc.SwapEffect; data->swapFlags = swapChainDesc.Flags; + DXGI_COLOR_SPACE_TYPE ColorSpace; + switch (renderer->output_colorspace) { + case SDL_COLORSPACE_SCRGB: + ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; + break; + case SDL_COLORSPACE_HDR10: + ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + break; + default: + /* sRGB */ + ColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + break; + } + result = D3D_CALL(data->swapChain, SetColorSpace1, ColorSpace); + if (FAILED(result)) { + WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("IDXGISwapChain3::SetColorSpace1"), result); + goto done; + } + done: SAFE_RELEASE(swapChain); return result; @@ -1354,11 +1397,7 @@ static HRESULT D3D12_CreateWindowSizeDependentResources(SDL_Renderer *renderer) #endif SDL_zero(rtvDesc); - if (renderer->colorspace_conversion && renderer->output_colorspace == SDL_COLORSPACE_SRGB) { - rtvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; - } else { - rtvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - } + rtvDesc.Format = data->renderTargetFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; SDL_zero(rtvDescriptor); @@ -2379,7 +2418,7 @@ static int D3D12_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *c SDL_bool updateSubresource = SDL_FALSE; int i; D3D12_CPU_DESCRIPTOR_HANDLE firstShaderResource; - DXGI_FORMAT rtvFormat = DXGI_FORMAT_B8G8R8A8_UNORM; + DXGI_FORMAT rtvFormat = rendererData->renderTargetFormat; if (rendererData->textureRenderTarget) { rtvFormat = rendererData->textureRenderTarget->mainTextureFormat; @@ -2877,12 +2916,14 @@ static int D3D12_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, /* Copy the data into the desired buffer, converting pixels to the * desired format at the same time: */ - status = SDL_ConvertPixels( + status = SDL_ConvertPixelsAndColorspace( rect->w, rect->h, D3D12_DXGIFormatToSDLPixelFormat(textureDesc.Format), + renderer->target ? renderer->target->colorspace : renderer->output_colorspace, textureMemory, pitchedDesc.RowPitch, format, + renderer->input_colorspace, pixels, pitch); @@ -2997,14 +3038,22 @@ SDL_Renderer *D3D12_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_p } renderer->magic = &SDL_renderer_magic; + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_SRGB && + renderer->output_colorspace != SDL_COLORSPACE_SCRGB && + renderer->output_colorspace != SDL_COLORSPACE_HDR10) { + SDL_SetError("Unsupported output colorspace"); + SDL_free(renderer); + return NULL; + } + data = (D3D12_RenderData *)SDL_calloc(1, sizeof(*data)); if (!data) { SDL_free(renderer); return NULL; } - SDL_SetupRendererColorspace(renderer, create_props); - data->identity = MatrixIdentity(); renderer->WindowEvent = D3D12_WindowEvent; diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index 6e48b48ed..09f39fbf2 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -1737,6 +1737,15 @@ static SDL_Renderer *METAL_CreateRenderer(SDL_Window *window, SDL_PropertiesID c if (!renderer) { return NULL; } + renderer->magic = &SDL_renderer_magic; + + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { + SDL_SetError("Unsupported output colorspace"); + SDL_free(renderer); + return NULL; + } #ifdef SDL_PLATFORM_MACOS if (SDL_GetHintBoolean(SDL_HINT_RENDER_METAL_PREFER_LOW_POWER_DEVICE, SDL_TRUE)) { diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index 17c438b49..22553053c 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -1710,14 +1710,20 @@ static SDL_Renderer *GL_CreateRenderer(SDL_Window *window, SDL_PropertiesID crea goto error; } + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { + SDL_SetError("Unsupported output colorspace"); + SDL_free(renderer); + goto error; + } + data = (GL_RenderData *)SDL_calloc(1, sizeof(*data)); if (!data) { SDL_free(renderer); goto error; } - SDL_SetupRendererColorspace(renderer, create_props); - renderer->WindowEvent = GL_WindowEvent; renderer->SupportsBlendMode = GL_SupportsBlendMode; renderer->CreateTexture = GL_CreateTexture; @@ -1939,10 +1945,13 @@ static SDL_Renderer *GL_CreateRenderer(SDL_Window *window, SDL_PropertiesID crea error: if (changed_window) { /* Uh oh, better try to put it back... */ + char *error = SDL_strdup(SDL_GetError()); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); SDL_RecreateWindow(window, window_flags); + SDL_SetError("%s", error); + SDL_free(error); } return NULL; } diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 6a443c369..a3ea1eee0 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -886,7 +886,7 @@ static int GLES2_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, S verts->position.y = xy_[1] * scale_y; if (colorswap) { - Uint8 r = col_.r; + float r = col_.r; col_.r = col_.b; col_.b = r; } @@ -2049,6 +2049,15 @@ static SDL_Renderer *GLES2_CreateRenderer(SDL_Window *window, SDL_PropertiesID c if (!renderer) { goto error; } + renderer->magic = &SDL_renderer_magic; + + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { + SDL_SetError("Unsupported output colorspace"); + SDL_free(renderer); + goto error; + } data = (GLES2_RenderData *)SDL_calloc(1, sizeof(GLES2_RenderData)); if (!data) { @@ -2207,10 +2216,13 @@ static SDL_Renderer *GLES2_CreateRenderer(SDL_Window *window, SDL_PropertiesID c error: if (changed_window) { /* Uh oh, better try to put it back... */ + char *error = SDL_strdup(SDL_GetError()); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); SDL_RecreateWindow(window, window_flags); + SDL_SetError("%s", error); + SDL_free(error); } return NULL; } diff --git a/src/render/ps2/SDL_render_ps2.c b/src/render/ps2/SDL_render_ps2.c index dc0cedc20..d4abc6940 100644 --- a/src/render/ps2/SDL_render_ps2.c +++ b/src/render/ps2/SDL_render_ps2.c @@ -630,6 +630,15 @@ static SDL_Renderer *PS2_CreateRenderer(SDL_Window *window, SDL_PropertiesID cre if (!renderer) { return NULL; } + renderer->magic = &SDL_renderer_magic; + + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { + SDL_SetError("Unsupported output colorspace"); + SDL_free(renderer); + return NULL; + } data = (PS2_RenderData *)SDL_calloc(1, sizeof(*data)); if (!data) { diff --git a/src/render/psp/SDL_render_psp.c b/src/render/psp/SDL_render_psp.c index 6cb53f70f..0fa89d46f 100644 --- a/src/render/psp/SDL_render_psp.c +++ b/src/render/psp/SDL_render_psp.c @@ -1312,6 +1312,15 @@ SDL_Renderer *PSP_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_pro if (!renderer) { return NULL; } + renderer->magic = &SDL_renderer_magic; + + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { + SDL_SetError("Unsupported output colorspace"); + SDL_free(renderer); + return NULL; + } data = (PSP_RenderData *)SDL_calloc(1, sizeof(*data)); if (!data) { diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c index 8763b1fa9..3de58e81b 100644 --- a/src/render/software/SDL_render_sw.c +++ b/src/render/software/SDL_render_sw.c @@ -1124,6 +1124,7 @@ SDL_Renderer *SW_CreateRendererForSurface(SDL_Surface *surface) if (!renderer) { return NULL; } + renderer->magic = &SDL_renderer_magic; data = (SW_RenderData *)SDL_calloc(1, sizeof(*data)); if (!data) { diff --git a/src/render/vitagxm/SDL_render_vita_gxm.c b/src/render/vitagxm/SDL_render_vita_gxm.c index 4282ee3a4..5bd09e4a1 100644 --- a/src/render/vitagxm/SDL_render_vita_gxm.c +++ b/src/render/vitagxm/SDL_render_vita_gxm.c @@ -220,6 +220,15 @@ SDL_Renderer *VITA_GXM_CreateRenderer(SDL_Window *window, SDL_PropertiesID creat if (!renderer) { return NULL; } + renderer->magic = &SDL_renderer_magic; + + SDL_SetupRendererColorspace(renderer, create_props); + + if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { + SDL_SetError("Unsupported output colorspace"); + SDL_free(renderer); + return NULL; + } data = (VITA_GXM_RenderData *)SDL_calloc(1, sizeof(VITA_GXM_RenderData)); if (!data) { diff --git a/src/video/SDL_blit.c b/src/video/SDL_blit.c index 07c858583..56ea4505f 100644 --- a/src/video/SDL_blit.c +++ b/src/video/SDL_blit.c @@ -183,23 +183,21 @@ static SDL_BlitFunc SDL_ChooseBlitFunc(Uint32 src_format, Uint32 dst_format, int } #endif /* SDL_HAVE_BLIT_AUTO */ -static SDL_Colorspace GetSurfaceColorspace(SDL_Surface *surface) -{ - if (surface->flags & SDL_SURFACE_USES_PROPERTIES) { - SDL_PropertiesID props = SDL_GetSurfaceProperties(surface); - return (SDL_Colorspace)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_COLORSPACE_NUMBER, SDL_COLORSPACE_RGB_DEFAULT); - } - return SDL_COLORSPACE_RGB_DEFAULT; -} - /* Figure out which of many blit routines to set up on a surface */ int SDL_CalculateBlit(SDL_Surface *surface) { SDL_BlitFunc blit = NULL; SDL_BlitMap *map = surface->map; SDL_Surface *dst = map->dst; - SDL_Colorspace src_colorspace = GetSurfaceColorspace(surface); - SDL_Colorspace dst_colorspace = GetSurfaceColorspace(dst); + SDL_Colorspace src_colorspace = SDL_COLORSPACE_UNKNOWN; + SDL_Colorspace dst_colorspace = SDL_COLORSPACE_UNKNOWN; + + if (SDL_GetSurfaceColorspace(surface, &src_colorspace) < 0) { + return -1; + } + if (SDL_GetSurfaceColorspace(dst, &dst_colorspace) < 0) { + return -1; + } /* We don't currently support blitting to < 8 bpp surfaces */ if (dst->format->BitsPerPixel < 8) { diff --git a/src/video/SDL_blit.h b/src/video/SDL_blit.h index 285a098b8..826de1a5f 100644 --- a/src/video/SDL_blit.h +++ b/src/video/SDL_blit.h @@ -262,13 +262,16 @@ extern SDL_BlitFunc SDL_CalculateBlitA(SDL_Surface *surface); a = (a * 3) / 255; \ Pixel = (a << 30) | (r << 20) | (g << 10) | b; \ } -#define ARGB2101010_FROM_RGBAFLOAT(Pixel, r, g, b, a) \ - { \ - r = SDL_clamp(r, 0.0f, 1.0f) * 1023.0f; \ - g = SDL_clamp(g, 0.0f, 1.0f) * 1023.0f; \ - b = SDL_clamp(b, 0.0f, 1.0f) * 1023.0f; \ - a = SDL_clamp(a, 0.0f, 1.0f) * 3.0f; \ - Pixel = (((Uint32)a) << 30) | (((Uint32)r) << 20) | (((Uint32)g) << 10) | (Uint32)b; \ +#define ARGB2101010_FROM_RGBAFLOAT(Pixel, r, g, b, a) \ + { \ + r = SDL_clamp(r, 0.0f, 1.0f) * 1023.0f; \ + g = SDL_clamp(g, 0.0f, 1.0f) * 1023.0f; \ + b = SDL_clamp(b, 0.0f, 1.0f) * 1023.0f; \ + a = SDL_clamp(a, 0.0f, 1.0f) * 3.0f; \ + Pixel = (((Uint32)SDL_roundf(a)) << 30) | \ + (((Uint32)SDL_roundf(r)) << 20) | \ + (((Uint32)SDL_roundf(g)) << 10) | \ + (Uint32)SDL_roundf(b); \ } #define ABGR2101010_FROM_RGBA(Pixel, r, g, b, a) \ { \ @@ -278,13 +281,16 @@ extern SDL_BlitFunc SDL_CalculateBlitA(SDL_Surface *surface); a = (a * 3) / 255; \ Pixel = (a << 30) | (b << 20) | (g << 10) | r; \ } -#define ABGR2101010_FROM_RGBAFLOAT(Pixel, r, g, b, a) \ - { \ - r = SDL_clamp(r, 0.0f, 1.0f) * 1023.0f; \ - g = SDL_clamp(g, 0.0f, 1.0f) * 1023.0f; \ - b = SDL_clamp(b, 0.0f, 1.0f) * 1023.0f; \ - a = SDL_clamp(a, 0.0f, 1.0f) * 3.0f; \ - Pixel = (((Uint32)a) << 30) | (((Uint32)b) << 20) | (((Uint32)g) << 10) | (Uint32)r; \ +#define ABGR2101010_FROM_RGBAFLOAT(Pixel, r, g, b, a) \ + { \ + r = SDL_clamp(r, 0.0f, 1.0f) * 1023.0f; \ + g = SDL_clamp(g, 0.0f, 1.0f) * 1023.0f; \ + b = SDL_clamp(b, 0.0f, 1.0f) * 1023.0f; \ + a = SDL_clamp(a, 0.0f, 1.0f) * 3.0f; \ + Pixel = (((Uint32)SDL_roundf(a)) << 30) | \ + (((Uint32)SDL_roundf(b)) << 20) | \ + (((Uint32)SDL_roundf(g)) << 10) | \ + (Uint32)SDL_roundf(r); \ } #define ASSEMBLE_RGB(buf, bpp, fmt, r, g, b) \ { \ diff --git a/src/video/SDL_blit_slow.c b/src/video/SDL_blit_slow.c index ad91273fd..da4f9b955 100644 --- a/src/video/SDL_blit_slow.c +++ b/src/video/SDL_blit_slow.c @@ -366,65 +366,6 @@ static Uint16 float_to_half(float a) return ir; } -static float scRGBtoNits(float v) -{ - return v * 80.0f; -} - -static float scRGBfromNits(float v) -{ - return v / 80.0f; -} - -static float sRGBtoNits(float v) -{ - if (v <= 0.04045f) { - v = (v / 12.92f); - } else { - v = SDL_powf((v + 0.055f) / 1.055f, 2.4f); - } - return scRGBtoNits(v); -} - -static float sRGBfromNits(float v) -{ - v = scRGBfromNits(v); - - if (v <= 0.0031308f) { - v = (v * 12.92f); - } else { - v = (SDL_powf(v, 1.0f / 2.4f) * 1.055f - 0.055f); - } - return v; -} - -static float PQtoNits(float v) -{ - const float c1 = 0.8359375f; - const float c2 = 18.8515625f; - const float c3 = 18.6875f; - const float oo_m1 = 1.0f / 0.1593017578125f; - const float oo_m2 = 1.0f / 78.84375f; - - float num = SDL_max(SDL_powf(v, oo_m2) - c1, 0.0f); - float den = c2 - c3 * SDL_powf(v, oo_m2); - return 10000.0f * SDL_powf(num / den, oo_m1); -} - -static float PQfromNits(float v) -{ - const float c1 = 0.8359375f; - const float c2 = 18.8515625f; - const float c3 = 18.6875f; - const float m1 = 0.1593017578125f; - const float m2 = 78.84375f; - - float y = SDL_clamp(v / 10000.0f, 0.0f, 1.0f); - float num = c1 + c2 * pow(y, m1); - float den = 1.0f + c3 * pow(y, m1); - return pow(num / den, m2); -} - static void ReadFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelFormat *fmt, SDL_Colorspace colorspace, float *outR, float *outG, float *outB, float *outA) { @@ -555,20 +496,20 @@ static void ReadFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelF /* Convert to nits so src and dst are guaranteed to be linear and in the same units */ switch (SDL_COLORSPACETRANSFER(colorspace)) { case SDL_TRANSFER_CHARACTERISTICS_SRGB: - fR = sRGBtoNits(fR); - fG = sRGBtoNits(fG); - fB = sRGBtoNits(fB); + fR = SDL_sRGBtoNits(fR); + fG = SDL_sRGBtoNits(fG); + fB = SDL_sRGBtoNits(fB); break; case SDL_TRANSFER_CHARACTERISTICS_PQ: - fR = PQtoNits(fR); - fG = PQtoNits(fG); - fB = PQtoNits(fB); + fR = SDL_PQtoNits(fR); + fG = SDL_PQtoNits(fG); + fB = SDL_PQtoNits(fB); break; case SDL_TRANSFER_CHARACTERISTICS_LINEAR: /* Assuming scRGB for now */ - fR = scRGBtoNits(fR); - fG = scRGBtoNits(fG); - fB = scRGBtoNits(fB); + fR = SDL_scRGBtoNits(fR); + fG = SDL_scRGBtoNits(fG); + fB = SDL_scRGBtoNits(fB); break; default: /* Unknown, leave it alone */ @@ -590,20 +531,20 @@ static void WriteFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_Pixel /* We converted to nits so src and dst are guaranteed to be linear and in the same units */ switch (SDL_COLORSPACETRANSFER(colorspace)) { case SDL_TRANSFER_CHARACTERISTICS_SRGB: - fR = sRGBfromNits(fR); - fG = sRGBfromNits(fG); - fB = sRGBfromNits(fB); + fR = SDL_sRGBfromNits(fR); + fG = SDL_sRGBfromNits(fG); + fB = SDL_sRGBfromNits(fB); break; case SDL_TRANSFER_CHARACTERISTICS_PQ: - fR = PQfromNits(fR); - fG = PQfromNits(fG); - fB = PQfromNits(fB); + fR = SDL_PQfromNits(fR); + fG = SDL_PQfromNits(fG); + fB = SDL_PQfromNits(fB); break; case SDL_TRANSFER_CHARACTERISTICS_LINEAR: /* Assuming scRGB for now */ - fR = scRGBfromNits(fR); - fG = scRGBfromNits(fG); - fB = scRGBfromNits(fB); + fR = SDL_scRGBfromNits(fR); + fG = SDL_scRGBfromNits(fG); + fB = SDL_scRGBfromNits(fB); break; default: /* Unknown, leave it alone */ @@ -612,16 +553,16 @@ static void WriteFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_Pixel switch (access) { case SlowBlitPixelAccess_RGB: - R = (Uint8)(SDL_clamp(fR, 0.0f, 1.0f) * 255.0f); - G = (Uint8)(SDL_clamp(fG, 0.0f, 1.0f) * 255.0f); - B = (Uint8)(SDL_clamp(fB, 0.0f, 1.0f) * 255.0f); + R = (Uint8)SDL_roundf(SDL_clamp(fR, 0.0f, 1.0f) * 255.0f); + G = (Uint8)SDL_roundf(SDL_clamp(fG, 0.0f, 1.0f) * 255.0f); + B = (Uint8)SDL_roundf(SDL_clamp(fB, 0.0f, 1.0f) * 255.0f); ASSEMBLE_RGB(pixels, fmt->BytesPerPixel, fmt, R, G, B); break; case SlowBlitPixelAccess_RGBA: - R = (Uint8)(SDL_clamp(fR, 0.0f, 1.0f) * 255.0f); - G = (Uint8)(SDL_clamp(fG, 0.0f, 1.0f) * 255.0f); - B = (Uint8)(SDL_clamp(fB, 0.0f, 1.0f) * 255.0f); - A = (Uint8)(SDL_clamp(fA, 0.0f, 1.0f) * 255.0f); + R = (Uint8)SDL_roundf(SDL_clamp(fR, 0.0f, 1.0f) * 255.0f); + G = (Uint8)SDL_roundf(SDL_clamp(fG, 0.0f, 1.0f) * 255.0f); + B = (Uint8)SDL_roundf(SDL_clamp(fB, 0.0f, 1.0f) * 255.0f); + A = (Uint8)SDL_roundf(SDL_clamp(fA, 0.0f, 1.0f) * 255.0f); ASSEMBLE_RGBA(pixels, fmt->BytesPerPixel, fmt, R, G, B, A); break; case SlowBlitPixelAccess_10Bit: @@ -692,11 +633,11 @@ static void WriteFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_Pixel } switch (SDL_PIXELTYPE(fmt->format)) { case SDL_PIXELTYPE_ARRAYU16: - ((Uint16 *)pixels)[0] = (Uint16)(SDL_clamp(v[0], 0.0f, 1.0f) * SDL_MAX_UINT16); - ((Uint16 *)pixels)[1] = (Uint16)(SDL_clamp(v[1], 0.0f, 1.0f) * SDL_MAX_UINT16); - ((Uint16 *)pixels)[2] = (Uint16)(SDL_clamp(v[2], 0.0f, 1.0f) * SDL_MAX_UINT16); + ((Uint16 *)pixels)[0] = (Uint16)SDL_roundf(SDL_clamp(v[0], 0.0f, 1.0f) * SDL_MAX_UINT16); + ((Uint16 *)pixels)[1] = (Uint16)SDL_roundf(SDL_clamp(v[1], 0.0f, 1.0f) * SDL_MAX_UINT16); + ((Uint16 *)pixels)[2] = (Uint16)SDL_roundf(SDL_clamp(v[2], 0.0f, 1.0f) * SDL_MAX_UINT16); if (fmt->BytesPerPixel == 8) { - ((Uint16 *)pixels)[3] = (Uint16)(SDL_clamp(v[3], 0.0f, 1.0f) * SDL_MAX_UINT16); + ((Uint16 *)pixels)[3] = (Uint16)SDL_roundf(SDL_clamp(v[3], 0.0f, 1.0f) * SDL_MAX_UINT16); } break; case SDL_PIXELTYPE_ARRAYF16: @@ -765,17 +706,26 @@ void SDL_Blit_Slow_Float(SDL_BlitInfo *info) int dstbpp = dst_fmt->BytesPerPixel; SlowBlitPixelAccess src_access; SlowBlitPixelAccess dst_access; - SDL_PropertiesID src_props = SDL_GetSurfaceProperties(info->src_surface); - SDL_Colorspace src_colorspace = (SDL_Colorspace)SDL_GetNumberProperty(src_props, SDL_PROP_SURFACE_COLORSPACE_NUMBER, SDL_COLORSPACE_RGB_DEFAULT); - SDL_ColorPrimaries src_primaries = SDL_COLORSPACEPRIMARIES(src_colorspace); - SDL_TransferCharacteristics src_transfer = SDL_COLORSPACETRANSFER(src_colorspace); - SDL_PropertiesID dst_props = SDL_GetSurfaceProperties(info->dst_surface); - SDL_Colorspace dst_colorspace = (SDL_Colorspace)SDL_GetNumberProperty(dst_props, SDL_PROP_SURFACE_COLORSPACE_NUMBER, SDL_COLORSPACE_RGB_DEFAULT); - SDL_ColorPrimaries dst_primaries = SDL_COLORSPACEPRIMARIES(dst_colorspace); - SDL_TransferCharacteristics dst_transfer = SDL_COLORSPACETRANSFER(dst_colorspace); - const float *color_primaries_matrix = SDL_GetColorPrimariesConversionMatrix(src_primaries, dst_primaries); + SDL_Colorspace src_colorspace; + SDL_ColorPrimaries src_primaries; + SDL_TransferCharacteristics src_transfer; + SDL_Colorspace dst_colorspace; + SDL_ColorPrimaries dst_primaries; + SDL_TransferCharacteristics dst_transfer; + const float *color_primaries_matrix; SDL_bool compress_PQ = SDL_FALSE; + if (SDL_GetSurfaceColorspace(info->src_surface, &src_colorspace) < 0 || + SDL_GetSurfaceColorspace(info->dst_surface, &dst_colorspace) < 0) { + return; + } + + src_primaries = SDL_COLORSPACEPRIMARIES(src_colorspace); + src_transfer = SDL_COLORSPACETRANSFER(src_colorspace); + dst_primaries = SDL_COLORSPACEPRIMARIES(dst_colorspace); + dst_transfer = SDL_COLORSPACETRANSFER(dst_colorspace); + color_primaries_matrix = SDL_GetColorPrimariesConversionMatrix(src_primaries, dst_primaries); + if (src_transfer == SDL_TRANSFER_CHARACTERISTICS_PQ && dst_transfer != SDL_TRANSFER_CHARACTERISTICS_PQ && dst_colorspace != SDL_COLORSPACE_SCRGB) { diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index ef38104f6..d9082d3dd 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -692,6 +692,78 @@ void SDL_DestroyPixelFormat(SDL_PixelFormat *format) return; } +SDL_Colorspace SDL_GetDefaultColorspaceForFormat(Uint32 format) +{ + if (SDL_ISPIXELFORMAT_FOURCC(format)) { + return SDL_COLORSPACE_BT709_FULL; + } else if (SDL_ISPIXELFORMAT_FLOAT(format)) { + return SDL_COLORSPACE_SCRGB; + } else if (SDL_ISPIXELFORMAT_10BIT(format)) { + return SDL_COLORSPACE_HDR10; + } else { + return SDL_COLORSPACE_RGB_DEFAULT; + } +} + +float SDL_scRGBtoNits(float v) +{ + return v * 80.0f; +} + +float SDL_scRGBfromNits(float v) +{ + return v / 80.0f; +} + +float SDL_sRGBtoNits(float v) +{ + if (v <= 0.04045f) { + v = (v / 12.92f); + } else { + v = SDL_powf((v + 0.055f) / 1.055f, 2.4f); + } + return SDL_scRGBtoNits(v); +} + +float SDL_sRGBfromNits(float v) +{ + v = SDL_scRGBfromNits(v); + + if (v <= 0.0031308f) { + v = (v * 12.92f); + } else { + v = (SDL_powf(v, 1.0f / 2.4f) * 1.055f - 0.055f); + } + return v; +} + +float SDL_PQtoNits(float v) +{ + const float c1 = 0.8359375f; + const float c2 = 18.8515625f; + const float c3 = 18.6875f; + const float oo_m1 = 1.0f / 0.1593017578125f; + const float oo_m2 = 1.0f / 78.84375f; + + float num = SDL_max(SDL_powf(v, oo_m2) - c1, 0.0f); + float den = c2 - c3 * SDL_powf(v, oo_m2); + return 10000.0f * SDL_powf(num / den, oo_m1); +} + +float SDL_PQfromNits(float v) +{ + const float c1 = 0.8359375f; + const float c2 = 18.8515625f; + const float c3 = 18.6875f; + const float m1 = 0.1593017578125f; + const float m2 = 78.84375f; + + float y = SDL_clamp(v / 10000.0f, 0.0f, 1.0f); + float num = c1 + c2 * pow(y, m1); + float den = 1.0f + c3 * pow(y, m1); + return pow(num / den, m2); +} + SDL_Palette *SDL_CreatePalette(int ncolors) { SDL_Palette *palette; diff --git a/src/video/SDL_pixels_c.h b/src/video/SDL_pixels_c.h index 37a735696..e7c66ff71 100644 --- a/src/video/SDL_pixels_c.h +++ b/src/video/SDL_pixels_c.h @@ -31,6 +31,16 @@ /* Pixel format functions */ extern int SDL_InitFormat(SDL_PixelFormat *format, Uint32 pixel_format); extern int SDL_CalculateSize(Uint32 format, int width, int height, size_t *size, size_t *pitch, SDL_bool minimalPitch); +extern SDL_Colorspace SDL_GetDefaultColorspaceForFormat(Uint32 pixel_format); + +/* Colorspace conversion functions */ +extern float SDL_scRGBtoNits(float v); +extern float SDL_scRGBfromNits(float v); +extern float SDL_sRGBtoNits(float v); +extern float SDL_sRGBfromNits(float v); +extern float SDL_PQtoNits(float v); +extern float SDL_PQfromNits(float v); + /* Blit mapping functions */ extern SDL_BlitMap *SDL_AllocBlitMap(void); diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index 9a52b13fb..aa51e4e53 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -269,6 +269,36 @@ SDL_PropertiesID SDL_GetSurfaceProperties(SDL_Surface *surface) return props; } +int SDL_SetSurfaceColorspace(SDL_Surface *surface, SDL_Colorspace colorspace) +{ + if (!surface) { + return SDL_InvalidParamError("surface"); + } + + return SDL_SetNumberProperty(SDL_GetSurfaceProperties(surface), SDL_PROP_SURFACE_COLORSPACE_NUMBER, colorspace); +} + +int SDL_GetSurfaceColorspace(SDL_Surface *surface, SDL_Colorspace *colorspace) +{ + SDL_Colorspace surface_colorspace = SDL_COLORSPACE_UNKNOWN; + + if (!surface) { + return SDL_InvalidParamError("surface"); + } + + if (surface->flags & SDL_SURFACE_USES_PROPERTIES) { + surface_colorspace = (SDL_Colorspace)SDL_GetNumberProperty(SDL_GetSurfaceProperties(surface), SDL_PROP_SURFACE_COLORSPACE_NUMBER, SDL_COLORSPACE_UNKNOWN); + } + if (surface_colorspace == SDL_COLORSPACE_UNKNOWN) { + surface_colorspace = SDL_GetDefaultColorspaceForFormat(surface->format->format); + } + + if (colorspace) { + *colorspace = surface_colorspace; + } + return 0; +} + int SDL_SetSurfacePalette(SDL_Surface *surface, SDL_Palette *palette) { if (!surface) { @@ -1159,25 +1189,10 @@ int SDL_FlipSurface(SDL_Surface *surface, SDL_FlipMode flip) } } -/* - * Creates a new surface identical to the existing surface - */ -SDL_Surface *SDL_DuplicateSurface(SDL_Surface *surface) -{ - if (!surface) { - SDL_InvalidParamError("surface"); - return NULL; - } - - return SDL_ConvertSurface(surface, surface->format); -} - -/* - * Convert a surface into the specified pixel format. - */ -SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, const SDL_PixelFormat *format) +static SDL_Surface *SDL_ConvertSurfaceWithPixelFormatAndColorspace(SDL_Surface *surface, const SDL_PixelFormat *format, Uint32 colorspace) { SDL_Surface *convert; + SDL_Colorspace src_colorspace; Uint32 copy_flags; SDL_Color copy_color; SDL_Rect bounds; @@ -1211,19 +1226,23 @@ SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, const SDL_PixelFormat *for } } + if (SDL_GetSurfaceColorspace(surface, &src_colorspace) < 0) { + return NULL; + } + /* Create a new surface with the desired format */ convert = SDL_CreateSurface(surface->w, surface->h, format->format); if (!convert) { return NULL; } + if (colorspace == SDL_COLORSPACE_UNKNOWN) { + colorspace = src_colorspace; + } + SDL_SetSurfaceColorspace(convert, colorspace); + if (SDL_ISPIXELFORMAT_FOURCC(format->format) || SDL_ISPIXELFORMAT_FOURCC(surface->format->format)) { - - ret = SDL_ConvertPixels(surface->w, surface->h, - surface->format->format, surface->pixels, surface->pitch, - convert->format->format, convert->pixels, convert->pitch); - - if (ret < 0) { + if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, surface->format->format, src_colorspace, surface->pixels, surface->pitch, convert->format->format, colorspace, convert->pixels, convert->pitch) < 0) { SDL_DestroySurface(convert); return NULL; } @@ -1430,15 +1449,35 @@ end: return convert; } +SDL_Surface *SDL_DuplicateSurface(SDL_Surface *surface) +{ + if (!surface) { + SDL_InvalidParamError("surface"); + return NULL; + } + + return SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, surface->format, SDL_COLORSPACE_UNKNOWN); +} + +SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, const SDL_PixelFormat *format) +{ + return SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, format, SDL_COLORSPACE_UNKNOWN); +} + SDL_Surface *SDL_ConvertSurfaceFormat(SDL_Surface *surface, Uint32 pixel_format) { - SDL_PixelFormat *fmt; + return SDL_ConvertSurfaceFormatAndColorspace(surface, pixel_format, SDL_COLORSPACE_UNKNOWN); +} + +SDL_Surface *SDL_ConvertSurfaceFormatAndColorspace(SDL_Surface *surface, Uint32 pixel_format, SDL_Colorspace colorspace) +{ + SDL_PixelFormat *format; SDL_Surface *convert = NULL; - fmt = SDL_CreatePixelFormat(pixel_format); - if (fmt) { - convert = SDL_ConvertSurface(surface, fmt); - SDL_DestroyPixelFormat(fmt); + format = SDL_CreatePixelFormat(pixel_format); + if (format) { + convert = SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, format, colorspace); + SDL_DestroyPixelFormat(format); } return convert; } @@ -1481,12 +1520,9 @@ static SDL_INLINE SDL_bool SDL_CreateSurfaceOnStack(int width, int height, Uint3 return SDL_TRUE; } -/* - * Copy a block of pixels of one format to another format - */ -int SDL_ConvertPixels(int width, int height, - Uint32 src_format, const void *src, int src_pitch, - Uint32 dst_format, void *dst, int dst_pitch) +int SDL_ConvertPixelsAndColorspace(int width, int height, + Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, + Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch) { SDL_Surface src_surface, dst_surface; SDL_PixelFormat src_fmt, dst_fmt; @@ -1523,7 +1559,7 @@ int SDL_ConvertPixels(int width, int height, #endif /* Fast path for same format copy */ - if (src_format == dst_format) { + if (src_format == dst_format && src_colorspace == dst_colorspace) { int i; const int bpp = SDL_BYTESPERPIXEL(src_format); width *= bpp; @@ -1540,10 +1576,17 @@ int SDL_ConvertPixels(int width, int height, &src_surface, &src_fmt, &src_blitmap)) { return -1; } + if (src_colorspace != SDL_COLORSPACE_UNKNOWN) { + SDL_SetNumberProperty(SDL_GetSurfaceProperties(&src_surface), SDL_PROP_SURFACE_COLORSPACE_NUMBER, src_colorspace); + } + if (!SDL_CreateSurfaceOnStack(width, height, dst_format, dst, dst_pitch, &dst_surface, &dst_fmt, &dst_blitmap)) { return -1; } + if (dst_colorspace != SDL_COLORSPACE_UNKNOWN) { + SDL_SetNumberProperty(SDL_GetSurfaceProperties(&dst_surface), SDL_PROP_SURFACE_COLORSPACE_NUMBER, dst_colorspace); + } /* Set up the rect and go! */ rect.x = 0; @@ -1555,9 +1598,24 @@ int SDL_ConvertPixels(int width, int height, /* Free blitmap reference, after blitting between stack'ed surfaces */ SDL_InvalidateMap(src_surface.map); + if (src_colorspace != SDL_COLORSPACE_UNKNOWN) { + SDL_DestroyProperties(SDL_GetSurfaceProperties(&src_surface)); + } + if (dst_colorspace != SDL_COLORSPACE_UNKNOWN) { + SDL_DestroyProperties(SDL_GetSurfaceProperties(&dst_surface)); + } return ret; } +int SDL_ConvertPixels(int width, int height, + Uint32 src_format, const void *src, int src_pitch, + Uint32 dst_format, void *dst, int dst_pitch) +{ + return SDL_ConvertPixelsAndColorspace(width, height, + src_format, SDL_COLORSPACE_UNKNOWN, src, src_pitch, + dst_format, SDL_COLORSPACE_UNKNOWN, dst, dst_pitch); +} + /* * Premultiply the alpha on a block of pixels * diff --git a/test/testcolorspace.c b/test/testcolorspace.c index ad427c672..baf726377 100644 --- a/test/testcolorspace.c +++ b/test/testcolorspace.c @@ -28,6 +28,8 @@ static SDL_Window *window; static SDL_Renderer *renderer; static const char *renderer_name; +static SDL_Colorspace colorspace = SDL_COLORSPACE_SRGB; +static const char *colorspace_name = "sRGB"; static int renderer_count = 0; static int renderer_index = 0; static int stage_count = 4; @@ -50,7 +52,7 @@ static void CreateRenderer(void) 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_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, colorspace); SDL_SetBooleanProperty(props, SDL_PROP_RENDERER_CREATE_COLORSPACE_CONVERSION_BOOLEAN, SDL_TRUE); renderer = SDL_CreateRendererWithProperties(props); SDL_DestroyProperties(props); @@ -158,7 +160,7 @@ static void RenderClearBackground(void) float x = TEXT_START_X; float y = TEXT_START_Y; - DrawText(x, y, renderer_name); + DrawText(x, y, "%s %s", renderer_name, colorspace_name); y += TEXT_LINE_ADVANCE; DrawText(x, y, "Test: Clear 50%% Gray Background"); y += TEXT_LINE_ADVANCE; @@ -186,7 +188,7 @@ static void RenderDrawBackground(void) float x = TEXT_START_X; float y = TEXT_START_Y; - DrawText(x, y, renderer_name); + DrawText(x, y, "%s %s", renderer_name, colorspace_name); y += TEXT_LINE_ADVANCE; DrawText(x, y, "Test: Draw 50%% Gray Background"); y += TEXT_LINE_ADVANCE; @@ -236,7 +238,7 @@ static void RenderBlendDrawing(void) float x = TEXT_START_X; float y = TEXT_START_Y; - DrawText(x, y, renderer_name); + DrawText(x, y, "%s %s", renderer_name, colorspace_name); y += TEXT_LINE_ADVANCE; DrawText(x, y, "Test: Draw Linear Blending"); y += TEXT_LINE_ADVANCE; @@ -294,7 +296,7 @@ static void RenderBlendTexture(void) float x = TEXT_START_X; float y = TEXT_START_Y; - DrawText(x, y, renderer_name); + DrawText(x, y, "%s %s", renderer_name, colorspace_name); y += TEXT_LINE_ADVANCE; DrawText(x, y, "Test: Texture Linear Blending"); y += TEXT_LINE_ADVANCE; @@ -343,25 +345,27 @@ static void loop(void) } } - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - SDL_RenderClear(renderer); + if (renderer) { + 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; + switch (stage_index) { + case 0: + RenderClearBackground(); + break; + case 1: + RenderDrawBackground(); + break; + case 2: + RenderBlendDrawing(); + break; + case 3: + RenderBlendTexture(); + break; + } + + SDL_RenderPresent(renderer); } - - SDL_RenderPresent(renderer); SDL_Delay(100); #ifdef SDL_PLATFORM_EMSCRIPTEN @@ -371,15 +375,47 @@ static void loop(void) #endif } +static void LogUsage(const char *argv0) +{ + SDL_Log("Usage: %s [--renderer renderer] [--colorspace colorspace]\n", argv0); +} + int main(int argc, char *argv[]) { - int return_code = -1; + int return_code = 1; int i; - if (argc > 2) { - SDL_Log("Usage: %s [renderer]\n", argv[0]); - return_code = 1; - goto quit; + for (i = 1; i < argc; ++i) { + if (SDL_strcmp(argv[i], "--renderer") == 0) { + if (argv[i + 1]) { + renderer_name = argv[i + 1]; + ++i; + } else { + LogUsage(argv[0]); + goto quit; + } + } else if (SDL_strcmp(argv[i], "--colorspace") == 0) { + if (argv[i + 1]) { + colorspace_name = argv[i + 1]; + if (SDL_strcasecmp(colorspace_name, "srgb") == 0) { + colorspace = SDL_COLORSPACE_SRGB; + } else if (SDL_strcasecmp(colorspace_name, "scrgb") == 0) { + colorspace = SDL_COLORSPACE_SCRGB; + } else if (SDL_strcasecmp(colorspace_name, "hdr10") == 0) { + colorspace = SDL_COLORSPACE_HDR10; + } else { + SDL_Log("Unknown colorspace %s\n", argv[i + 1]); + goto quit; + } + ++i; + } else { + LogUsage(argv[0]); + goto quit; + } + } else { + LogUsage(argv[0]); + goto quit; + } } window = SDL_CreateWindow("SDL colorspace test", WINDOW_WIDTH, WINDOW_HEIGHT, 0); @@ -394,7 +430,7 @@ int main(int argc, char *argv[]) for (i = 0; i < renderer_count; ++i) { const char *name = SDL_GetRenderDriver(i); - if (argv[1] && SDL_strcasecmp(argv[1], name) == 0) { + if (renderer_name && SDL_strcasecmp(renderer_name, name) == 0) { renderer_index = i; } SDL_Log(" %s\n", name);