From a6a4e27ae81783cede008460c9ef957e050860f4 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 12 Nov 2017 22:51:12 -0800 Subject: [PATCH] Updated SDL's YUV support, many thanks to Adrien Descamps New functions get and set the YUV colorspace conversion mode: SDL_SetYUVConversionMode() SDL_GetYUVConversionMode() SDL_GetYUVConversionModeForResolution() SDL_ConvertPixels() converts between all supported RGB and YUV formats, with SSE acceleration for converting from planar YUV formats (YV12, NV12, etc) to common RGB/RGBA formats. Added a new test program, testyuv, to verify correctness and speed of YUV conversion functionality. --- .hgignore | 12 +- Android.mk | 1 + CMakeLists.txt | 1 + VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj | 8 +- .../UWP_VS2015/SDL-UWP.vcxproj.filters | 24 +- .../WinPhone80_VS2012/SDL-WinPhone80.vcxproj | 8 +- .../SDL-WinPhone80.vcxproj.filters | 24 +- .../WinPhone81_VS2013/SDL-WinPhone81.vcxproj | 8 +- .../SDL-WinPhone81.vcxproj.filters | 20 +- .../WinRT80_VS2012/SDL-WinRT80.vcxproj | 8 +- .../SDL-WinRT80.vcxproj.filters | 24 +- .../WinRT81_VS2013/SDL-WinRT81.vcxproj | 8 +- .../SDL-WinRT81.vcxproj.filters | 48 +- VisualC/SDL.sln | 11 + VisualC/SDL/SDL.vcxproj | 11 +- VisualC/SDL/SDL.vcxproj.filters | 10 +- VisualC/tests/testyuv/testyuv.vcxproj | 229 ++ Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj | 72 +- .../SDL2test.xcodeproj/project.pbxproj | 2 + Xcode-iOS/Test/Info.plist | 2 - .../TestiPhoneOS.xcodeproj/project.pbxproj | 117 +- Xcode/SDL/SDL.xcodeproj/project.pbxproj | 51 +- configure | 1 + configure.in | 1 + include/SDL_surface.h | 25 + src/dynapi/SDL_dynapi_overrides.h | 3 + src/dynapi/SDL_dynapi_procs.h | 3 + src/render/SDL_yuv_mmx.c | 409 ---- src/render/SDL_yuv_sw.c | 1153 +--------- src/render/SDL_yuv_sw_c.h | 10 - src/render/direct3d/SDL_render_d3d.c | 414 +--- src/render/direct3d/SDL_shaders_d3d.c | 274 +++ .../SDL_shaders_d3d.h} | 16 +- src/render/direct3d11/SDL_render_d3d11.c | 851 ++----- src/render/direct3d11/SDL_shaders_d3d11.c | 1957 +++++++++++++++++ src/render/direct3d11/SDL_shaders_d3d11.h | 43 + src/render/mmx.h | 642 ------ src/render/opengl/SDL_render_gl.c | 38 +- src/render/opengl/SDL_shaders_gl.c | 371 ++-- src/render/opengl/SDL_shaders_gl.h | 12 +- src/render/opengles2/SDL_render_gles2.c | 53 +- src/render/opengles2/SDL_shaders_gles2.c | 343 ++- src/render/opengles2/SDL_shaders_gles2.h | 20 +- src/video/SDL_pixels.c | 24 - src/video/SDL_pixels_c.h | 1 - src/video/SDL_surface.c | 660 +----- src/video/SDL_yuv.c | 1834 +++++++++++++++ src/video/SDL_yuv_c.h | 30 + src/video/yuv2rgb/LICENSE | 27 + src/video/yuv2rgb/README.md | 63 + src/video/yuv2rgb/yuv_rgb.c | 687 ++++++ src/video/yuv2rgb/yuv_rgb.h | 380 ++++ src/video/yuv2rgb/yuv_rgb_sse_func.h | 498 +++++ src/video/yuv2rgb/yuv_rgb_std_func.h | 220 ++ test/Makefile.in | 28 +- test/testoverlay2.c | 115 +- test/testyuv.bmp | Bin 0 -> 739398 bytes test/testyuv.c | 455 ++++ test/testyuv_cvt.c | 300 +++ test/testyuv_cvt.h | 16 + 60 files changed, 8367 insertions(+), 4309 deletions(-) create mode 100644 VisualC/tests/testyuv/testyuv.vcxproj delete mode 100644 src/render/SDL_yuv_mmx.c create mode 100644 src/render/direct3d/SDL_shaders_d3d.c rename src/render/{SDL_yuv_mmx_c.h => direct3d/SDL_shaders_d3d.h} (76%) mode change 100644 => 100755 src/render/direct3d11/SDL_render_d3d11.c create mode 100755 src/render/direct3d11/SDL_shaders_d3d11.c create mode 100755 src/render/direct3d11/SDL_shaders_d3d11.h delete mode 100644 src/render/mmx.h create mode 100644 src/video/SDL_yuv.c create mode 100644 src/video/SDL_yuv_c.h create mode 100644 src/video/yuv2rgb/LICENSE create mode 100644 src/video/yuv2rgb/README.md create mode 100644 src/video/yuv2rgb/yuv_rgb.c create mode 100644 src/video/yuv2rgb/yuv_rgb.h create mode 100644 src/video/yuv2rgb/yuv_rgb_sse_func.h create mode 100644 src/video/yuv2rgb/yuv_rgb_std_func.h create mode 100644 test/testyuv.bmp create mode 100644 test/testyuv.c create mode 100644 test/testyuv_cvt.c create mode 100644 test/testyuv_cvt.h diff --git a/.hgignore b/.hgignore index 636b7ab198ece..d4c66b8565c45 100644 --- a/.hgignore +++ b/.hgignore @@ -70,14 +70,19 @@ test/controllermap test/loopwave test/loopwavequeue test/testatomic +test/testaudiocapture test/testaudiohotplug test/testaudioinfo test/testautomation +test/testbounds +test/testcustomcursor +test/testdisplayinfo test/testdraw2 test/testdrawchessboard test/testdropfile test/testerror test/testfile +test/testfilesystem test/testgamecontroller test/testgesture test/testgl2 @@ -99,7 +104,7 @@ test/testnative test/testoverlay2 test/testplatform test/testpower -test/testfilesystem +test/testqsort test/testrelative test/testrendercopyex test/testrendertarget @@ -117,11 +122,8 @@ test/testtimer test/testver test/testviewport test/testwm2 -test/testbounds +test/testyuv test/torturethread -test/testdisplayinfo -test/testqsort -test/testaudiocapture test/*.exe test/*.dSYM buildbot diff --git a/Android.mk b/Android.mk index d593d68d288a7..e70ededaf6e02 100755 --- a/Android.mk +++ b/Android.mk @@ -45,6 +45,7 @@ LOCAL_SRC_FILES := \ $(wildcard $(LOCAL_PATH)/src/timer/unix/*.c) \ $(wildcard $(LOCAL_PATH)/src/video/*.c) \ $(wildcard $(LOCAL_PATH)/src/video/android/*.c) \ + $(wildcard $(LOCAL_PATH)/src/video/yuv2rgb/*.c) \ $(wildcard $(LOCAL_PATH)/src/test/*.c)) LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES diff --git a/CMakeLists.txt b/CMakeLists.txt index 1be707e6b5c82..2329d391bfb99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -357,6 +357,7 @@ file(GLOB SOURCE_FILES ${SDL2_SOURCE_DIR}/src/thread/*.c ${SDL2_SOURCE_DIR}/src/timer/*.c ${SDL2_SOURCE_DIR}/src/video/*.c) + ${SDL2_SOURCE_DIR}/src/video/yuv2rgb/*.c) if(ASSERTIONS STREQUAL "auto") diff --git a/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj b/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj index 5564b185ca041..841a0b966be8e 100644 --- a/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj +++ b/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj @@ -117,7 +117,7 @@ - + @@ -155,12 +155,14 @@ + + @@ -251,11 +253,11 @@ true true + - @@ -305,6 +307,7 @@ + true true @@ -369,6 +372,7 @@ true true + {89e9b32e-a86a-47c3-a948-d2b1622925ce} diff --git a/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj.filters b/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj.filters index f23cd1890a8b5..5a206899651e0 100644 --- a/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj.filters +++ b/VisualC-WinRT/UWP_VS2015/SDL-UWP.vcxproj.filters @@ -267,9 +267,6 @@ Source Files - - Source Files - Source Files @@ -411,6 +408,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -551,9 +557,6 @@ Source Files - - Source Files - Source Files @@ -737,5 +740,14 @@ Source Files + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/VisualC-WinRT/WinPhone80_VS2012/SDL-WinPhone80.vcxproj b/VisualC-WinRT/WinPhone80_VS2012/SDL-WinPhone80.vcxproj index 183e427f46cab..2091df090b842 100644 --- a/VisualC-WinRT/WinPhone80_VS2012/SDL-WinPhone80.vcxproj +++ b/VisualC-WinRT/WinPhone80_VS2012/SDL-WinPhone80.vcxproj @@ -235,7 +235,7 @@ - + @@ -272,12 +272,14 @@ + + @@ -350,11 +352,11 @@ true true + - @@ -404,6 +406,7 @@ + true true @@ -452,6 +455,7 @@ true true + diff --git a/VisualC-WinRT/WinPhone80_VS2012/SDL-WinPhone80.vcxproj.filters b/VisualC-WinRT/WinPhone80_VS2012/SDL-WinPhone80.vcxproj.filters index 304c0472d05a0..9aafdce6cb061 100644 --- a/VisualC-WinRT/WinPhone80_VS2012/SDL-WinPhone80.vcxproj.filters +++ b/VisualC-WinRT/WinPhone80_VS2012/SDL-WinPhone80.vcxproj.filters @@ -216,9 +216,6 @@ Source Files - - Source Files - Source Files @@ -375,6 +372,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -467,9 +473,6 @@ Source Files - - Source Files - Source Files @@ -683,5 +686,14 @@ Source Files + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj b/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj index c291871b2bbeb..f7b887cc631cc 100644 --- a/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj +++ b/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj @@ -101,7 +101,7 @@ - + @@ -138,12 +138,14 @@ + + @@ -216,11 +218,11 @@ true true + - @@ -271,6 +273,7 @@ + true true @@ -319,6 +322,7 @@ true true + {48fadc0e-964d-4dab-bced-372e0ad19577} diff --git a/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters b/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters index badba57cc614c..4bfbe83021395 100644 --- a/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters +++ b/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters @@ -252,9 +252,6 @@ Source Files - - Source Files - Source Files @@ -384,6 +381,13 @@ Source Files + + Source Files + + + Source Files + + @@ -515,9 +519,6 @@ Source Files - - Source Files - Source Files @@ -695,5 +696,12 @@ Source Files + + Source Files + + + Source Files + + \ No newline at end of file diff --git a/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj b/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj index f29cd42acd2a5..2e1fc8ba6c338 100644 --- a/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj +++ b/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj @@ -114,11 +114,11 @@ true true + - @@ -168,6 +168,7 @@ + true true @@ -232,6 +233,7 @@ true true + @@ -323,7 +325,7 @@ - + @@ -361,12 +363,14 @@ + + {aeaea3a2-d4e6-45b1-8ec6-53d84287fc14} diff --git a/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj.filters b/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj.filters index 5c601be172bbe..3e2f4c68eca60 100644 --- a/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj.filters +++ b/VisualC-WinRT/WinRT80_VS2012/SDL-WinRT80.vcxproj.filters @@ -220,9 +220,6 @@ Source Files - - Source Files - Source Files @@ -322,6 +319,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -444,9 +450,6 @@ Source Files - - Source Files - Header Files @@ -723,6 +726,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj index ec044385b64ca..d3f34d7a20a17 100644 --- a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj +++ b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj @@ -116,7 +116,7 @@ - + @@ -152,12 +152,14 @@ + + @@ -248,11 +250,11 @@ true true + - @@ -303,6 +305,7 @@ + true true @@ -367,6 +370,7 @@ true true + {c8df6173-06a1-4f56-a9bc-2002596b30e9} diff --git a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters index f6b398a97d4a8..b499c70a8e0bc 100644 --- a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters +++ b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters @@ -252,9 +252,6 @@ Source Files - - Source Files - Source Files @@ -384,9 +381,6 @@ Source Files - - - Source Files @@ -396,6 +390,24 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -527,9 +539,6 @@ Source Files - - Source Files - Source Files @@ -695,9 +704,6 @@ Source Files - - - Source Files @@ -719,5 +725,23 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/VisualC/SDL.sln b/VisualC/SDL.sln index 5feee24096552..b30d4eb5438de 100644 --- a/VisualC/SDL.sln +++ b/VisualC/SDL.sln @@ -50,6 +50,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "controllermap", "tests\cont EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testvulkan", "tests\testvulkan\testvulkan.vcxproj", "{0D604DFD-AAB6-442C-9368-F91A344146AB}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testyuv", "tests\testyuv\testyuv.vcxproj", "{40FB7794-D3C3-4CFE-BCF4-A80C97635682}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -250,6 +252,14 @@ Global {0D604DFD-AAB6-442C-9368-F91A344146AB}.Release|Win32.Build.0 = Release|Win32 {0D604DFD-AAB6-442C-9368-F91A344146AB}.Release|x64.ActiveCfg = Release|x64 {0D604DFD-AAB6-442C-9368-F91A344146AB}.Release|x64.Build.0 = Release|x64 + {40FB7794-D3C3-4CFE-BCF4-A80C97635682}.Debug|Win32.ActiveCfg = Debug|Win32 + {40FB7794-D3C3-4CFE-BCF4-A80C97635682}.Debug|Win32.Build.0 = Debug|Win32 + {40FB7794-D3C3-4CFE-BCF4-A80C97635682}.Debug|x64.ActiveCfg = Debug|x64 + {40FB7794-D3C3-4CFE-BCF4-A80C97635682}.Debug|x64.Build.0 = Debug|x64 + {40FB7794-D3C3-4CFE-BCF4-A80C97635682}.Release|Win32.ActiveCfg = Release|Win32 + {40FB7794-D3C3-4CFE-BCF4-A80C97635682}.Release|Win32.Build.0 = Release|Win32 + {40FB7794-D3C3-4CFE-BCF4-A80C97635682}.Release|x64.ActiveCfg = Release|x64 + {40FB7794-D3C3-4CFE-BCF4-A80C97635682}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -276,5 +286,6 @@ Global {E9558DFE-1961-4DD4-B09B-DD0EEFD5C315} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} {55812185-D13C-4022-9C81-32E0F4A08306} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} {0D604DFD-AAB6-442C-9368-F91A344146AB} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} + {40FB7794-D3C3-4CFE-BCF4-A80C97635682} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} EndGlobalSection EndGlobal diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index 24fd722384c1c..a4fb4478dd85b 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -78,6 +78,7 @@ AllRules.ruleset + C:\Program Files %28x86%29\Microsoft DirectX SDK %28June 2010%29\Lib\x86;$(LibraryPath) @@ -324,7 +325,8 @@ - + + @@ -358,6 +360,7 @@ + @@ -372,6 +375,7 @@ + @@ -434,15 +438,16 @@ + + - @@ -494,6 +499,7 @@ + @@ -507,6 +513,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index fb00991bfc0fb..dcfd74a707c36 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -262,7 +262,6 @@ - @@ -310,6 +309,10 @@ + + + + @@ -439,12 +442,15 @@ - + + + + diff --git a/VisualC/tests/testyuv/testyuv.vcxproj b/VisualC/tests/testyuv/testyuv.vcxproj new file mode 100644 index 0000000000000..c5738c67dc50a --- /dev/null +++ b/VisualC/tests/testyuv/testyuv.vcxproj @@ -0,0 +1,229 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {40FB7794-D3C3-4CFE-BCF4-A80C97635682} + testyuv + + + + Application + + + Application + + + Application + + + Application + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/testyuv.tlb + + + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + Windows + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + .\Release/testyuv.tlb + + + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + Windows + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/testyuv.tlb + + + Disabled + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + OldStyle + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + Windows + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + X64 + .\Debug/testyuv.tlb + + + Disabled + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + OldStyle + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + Windows + + + + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} + false + false + true + + + {da956fd3-e142-46f2-9dd5-c78bebb56b7a} + false + false + true + + + {da956fd3-e143-46f2-9fe5-c77bebc56b1a} + false + false + true + + + + + Copying %(Filename)%(Extension) + copy %(FullPath) $(ProjectDir)\ + + $(ProjectDir)\%(Filename)%(Extension);%(Outputs) + Copying %(Filename)%(Extension) + copy %(FullPath) $(ProjectDir)\ + + $(ProjectDir)\%(Filename)%(Extension);%(Outputs) + Copying %(Filename)%(Extension) + copy %(FullPath) $(ProjectDir)\ + + $(ProjectDir)\%(Filename)%(Extension);%(Outputs) + Copying %(Filename)%(Extension) + copy %(FullPath) $(ProjectDir)\ + + $(ProjectDir)\%(Filename)%(Extension);%(Outputs) + + + + + + + + + + + + + diff --git a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj index 72cd2adf4871b..4557c94f0c12d 100755 --- a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj @@ -52,8 +52,6 @@ 041B2CF212FA0F680087D585 /* SDL_sysrender.h in Headers */ = {isa = PBXBuildFile; fileRef = 041B2CEB12FA0F680087D585 /* SDL_sysrender.h */; }; 0420497011E6F03D007E7EC9 /* SDL_clipboardevents_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 0420496E11E6F03D007E7EC9 /* SDL_clipboardevents_c.h */; }; 0420497111E6F03D007E7EC9 /* SDL_clipboardevents.c in Sources */ = {isa = PBXBuildFile; fileRef = 0420496F11E6F03D007E7EC9 /* SDL_clipboardevents.c */; }; - 04409BA612FA989600FB9AA8 /* mmx.h in Headers */ = {isa = PBXBuildFile; fileRef = 04409BA212FA989600FB9AA8 /* mmx.h */; }; - 04409BA712FA989600FB9AA8 /* SDL_yuv_mmx.c in Sources */ = {isa = PBXBuildFile; fileRef = 04409BA312FA989600FB9AA8 /* SDL_yuv_mmx.c */; }; 04409BA812FA989600FB9AA8 /* SDL_yuv_sw_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 04409BA412FA989600FB9AA8 /* SDL_yuv_sw_c.h */; }; 04409BA912FA989600FB9AA8 /* SDL_yuv_sw.c in Sources */ = {isa = PBXBuildFile; fileRef = 04409BA512FA989600FB9AA8 /* SDL_yuv_sw.c */; }; 0442EC5012FE1C1E004C9285 /* SDL_render_sw_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 0442EC4E12FE1C1E004C9285 /* SDL_render_sw_c.h */; }; @@ -119,6 +117,17 @@ AA0F8495178D5F1A00823F9D /* SDL_systls.c in Sources */ = {isa = PBXBuildFile; fileRef = AA0F8494178D5F1A00823F9D /* SDL_systls.c */; }; AA126AD41617C5E7005ABC8F /* SDL_uikitmodes.h in Headers */ = {isa = PBXBuildFile; fileRef = AA126AD21617C5E6005ABC8F /* SDL_uikitmodes.h */; }; AA126AD51617C5E7005ABC8F /* SDL_uikitmodes.m in Sources */ = {isa = PBXBuildFile; fileRef = AA126AD31617C5E6005ABC8F /* SDL_uikitmodes.m */; }; + AA13B3491FB8B27800D9FEE6 /* SDL_egl_c.h in Headers */ = {isa = PBXBuildFile; fileRef = AA13B3431FB8B27700D9FEE6 /* SDL_egl_c.h */; }; + AA13B34A1FB8B27800D9FEE6 /* SDL_shape.c in Sources */ = {isa = PBXBuildFile; fileRef = AA13B3441FB8B27800D9FEE6 /* SDL_shape.c */; }; + AA13B34B1FB8B27800D9FEE6 /* SDL_shape_internals.h in Headers */ = {isa = PBXBuildFile; fileRef = AA13B3451FB8B27800D9FEE6 /* SDL_shape_internals.h */; }; + AA13B34C1FB8B27800D9FEE6 /* SDL_rect_c.h in Headers */ = {isa = PBXBuildFile; fileRef = AA13B3461FB8B27800D9FEE6 /* SDL_rect_c.h */; }; + AA13B34D1FB8B27800D9FEE6 /* SDL_egl.c in Sources */ = {isa = PBXBuildFile; fileRef = AA13B3471FB8B27800D9FEE6 /* SDL_egl.c */; }; + AA13B34E1FB8B27800D9FEE6 /* SDL_yuv_c.h in Headers */ = {isa = PBXBuildFile; fileRef = AA13B3481FB8B27800D9FEE6 /* SDL_yuv_c.h */; }; + AA13B3501FB8B3CC00D9FEE6 /* SDL_yuv.c in Sources */ = {isa = PBXBuildFile; fileRef = AA13B34F1FB8B3CC00D9FEE6 /* SDL_yuv.c */; }; + AA13B3571FB8B46400D9FEE6 /* yuv_rgb_std_func.h in Headers */ = {isa = PBXBuildFile; fileRef = AA13B3531FB8B46300D9FEE6 /* yuv_rgb_std_func.h */; }; + AA13B3581FB8B46400D9FEE6 /* yuv_rgb_sse_func.h in Headers */ = {isa = PBXBuildFile; fileRef = AA13B3541FB8B46300D9FEE6 /* yuv_rgb_sse_func.h */; }; + AA13B3591FB8B46400D9FEE6 /* yuv_rgb.h in Headers */ = {isa = PBXBuildFile; fileRef = AA13B3551FB8B46300D9FEE6 /* yuv_rgb.h */; }; + AA13B35A1FB8B46400D9FEE6 /* yuv_rgb.c in Sources */ = {isa = PBXBuildFile; fileRef = AA13B3561FB8B46300D9FEE6 /* yuv_rgb.c */; }; AA628ADB159369E3005138DD /* SDL_rotate.c in Sources */ = {isa = PBXBuildFile; fileRef = AA628AD9159369E3005138DD /* SDL_rotate.c */; }; AA628ADC159369E3005138DD /* SDL_rotate.h in Headers */ = {isa = PBXBuildFile; fileRef = AA628ADA159369E3005138DD /* SDL_rotate.h */; }; AA704DD6162AA90A0076D1C1 /* SDL_dropevents_c.h in Headers */ = {isa = PBXBuildFile; fileRef = AA704DD4162AA90A0076D1C1 /* SDL_dropevents_c.h */; }; @@ -222,7 +231,6 @@ FAB598681BB5C31600BE72C5 /* SDL_render_sw.c in Sources */ = {isa = PBXBuildFile; fileRef = 0442EC4F12FE1C1E004C9285 /* SDL_render_sw.c */; }; FAB5986A1BB5C31600BE72C5 /* SDL_rotate.c in Sources */ = {isa = PBXBuildFile; fileRef = AA628AD9159369E3005138DD /* SDL_rotate.c */; }; FAB5986D1BB5C31600BE72C5 /* SDL_render.c in Sources */ = {isa = PBXBuildFile; fileRef = 041B2CEA12FA0F680087D585 /* SDL_render.c */; }; - FAB5986F1BB5C31600BE72C5 /* SDL_yuv_mmx.c in Sources */ = {isa = PBXBuildFile; fileRef = 04409BA312FA989600FB9AA8 /* SDL_yuv_mmx.c */; }; FAB598711BB5C31600BE72C5 /* SDL_yuv_sw.c in Sources */ = {isa = PBXBuildFile; fileRef = 04409BA512FA989600FB9AA8 /* SDL_yuv_sw.c */; }; FAB598721BB5C31600BE72C5 /* SDL_getenv.c in Sources */ = {isa = PBXBuildFile; fileRef = FD3F4A700DEA620800C5B771 /* SDL_getenv.c */; }; FAB598731BB5C31600BE72C5 /* SDL_iconv.c in Sources */ = {isa = PBXBuildFile; fileRef = FD3F4A710DEA620800C5B771 /* SDL_iconv.c */; }; @@ -358,8 +366,6 @@ 041B2CEB12FA0F680087D585 /* SDL_sysrender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sysrender.h; sourceTree = ""; }; 0420496E11E6F03D007E7EC9 /* SDL_clipboardevents_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_clipboardevents_c.h; sourceTree = ""; }; 0420496F11E6F03D007E7EC9 /* SDL_clipboardevents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_clipboardevents.c; sourceTree = ""; }; - 04409BA212FA989600FB9AA8 /* mmx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mmx.h; sourceTree = ""; }; - 04409BA312FA989600FB9AA8 /* SDL_yuv_mmx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_yuv_mmx.c; sourceTree = ""; }; 04409BA412FA989600FB9AA8 /* SDL_yuv_sw_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_yuv_sw_c.h; sourceTree = ""; }; 04409BA512FA989600FB9AA8 /* SDL_yuv_sw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_yuv_sw.c; sourceTree = ""; }; 0442EC4E12FE1C1E004C9285 /* SDL_render_sw_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_render_sw_c.h; sourceTree = ""; }; @@ -424,6 +430,17 @@ AA0F8494178D5F1A00823F9D /* SDL_systls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_systls.c; sourceTree = ""; }; AA126AD21617C5E6005ABC8F /* SDL_uikitmodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_uikitmodes.h; sourceTree = ""; }; AA126AD31617C5E6005ABC8F /* SDL_uikitmodes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_uikitmodes.m; sourceTree = ""; }; + AA13B3431FB8B27700D9FEE6 /* SDL_egl_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_egl_c.h; sourceTree = ""; }; + AA13B3441FB8B27800D9FEE6 /* SDL_shape.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_shape.c; sourceTree = ""; }; + AA13B3451FB8B27800D9FEE6 /* SDL_shape_internals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_shape_internals.h; sourceTree = ""; }; + AA13B3461FB8B27800D9FEE6 /* SDL_rect_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_rect_c.h; sourceTree = ""; }; + AA13B3471FB8B27800D9FEE6 /* SDL_egl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_egl.c; sourceTree = ""; }; + AA13B3481FB8B27800D9FEE6 /* SDL_yuv_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_yuv_c.h; sourceTree = ""; }; + AA13B34F1FB8B3CC00D9FEE6 /* SDL_yuv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_yuv.c; sourceTree = ""; }; + AA13B3531FB8B46300D9FEE6 /* yuv_rgb_std_func.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yuv_rgb_std_func.h; sourceTree = ""; }; + AA13B3541FB8B46300D9FEE6 /* yuv_rgb_sse_func.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yuv_rgb_sse_func.h; sourceTree = ""; }; + AA13B3551FB8B46300D9FEE6 /* yuv_rgb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yuv_rgb.h; sourceTree = ""; }; + AA13B3561FB8B46300D9FEE6 /* yuv_rgb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yuv_rgb.c; sourceTree = ""; }; AA628AD9159369E3005138DD /* SDL_rotate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_rotate.c; sourceTree = ""; }; AA628ADA159369E3005138DD /* SDL_rotate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_rotate.h; sourceTree = ""; }; AA704DD4162AA90A0076D1C1 /* SDL_dropevents_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_dropevents_c.h; sourceTree = ""; }; @@ -608,10 +625,8 @@ 041B2CE812FA0F680087D585 /* opengles */, 0402A85412FE70C600CECEE3 /* opengles2 */, 041B2CEC12FA0F680087D585 /* software */, - 04409BA212FA989600FB9AA8 /* mmx.h */, 041B2CEA12FA0F680087D585 /* SDL_render.c */, 041B2CEB12FA0F680087D585 /* SDL_sysrender.h */, - 04409BA312FA989600FB9AA8 /* SDL_yuv_mmx.c */, 04409BA412FA989600FB9AA8 /* SDL_yuv_sw_c.h */, 04409BA512FA989600FB9AA8 /* SDL_yuv_sw.c */, ); @@ -760,6 +775,17 @@ path = steam; sourceTree = ""; }; + AA13B3521FB8B41700D9FEE6 /* yuv2rgb */ = { + isa = PBXGroup; + children = ( + AA13B3541FB8B46300D9FEE6 /* yuv_rgb_sse_func.h */, + AA13B3531FB8B46300D9FEE6 /* yuv_rgb_std_func.h */, + AA13B3561FB8B46300D9FEE6 /* yuv_rgb.c */, + AA13B3551FB8B46300D9FEE6 /* yuv_rgb.h */, + ); + path = yuv2rgb; + sourceTree = ""; + }; FD3F4A6F0DEA620800C5B771 /* stdlib */ = { isa = PBXGroup; children = ( @@ -1070,10 +1096,9 @@ FDA682420DF2374D00F98A1A /* video */ = { isa = PBXGroup; children = ( - FD689F090E26E5D900F90B21 /* uikit */, FDA685F40DF244C800F98A1A /* dummy */, - FDA683000DF2374E00F98A1A /* SDL_blit.c */, - FDA683010DF2374E00F98A1A /* SDL_blit.h */, + FD689F090E26E5D900F90B21 /* uikit */, + AA13B3521FB8B41700D9FEE6 /* yuv2rgb */, FDA683020DF2374E00F98A1A /* SDL_blit_0.c */, FDA683030DF2374E00F98A1A /* SDL_blit_1.c */, FDA683040DF2374E00F98A1A /* SDL_blit_A.c */, @@ -1084,20 +1109,29 @@ FDA683090DF2374E00F98A1A /* SDL_blit_N.c */, FDA6830A0DF2374E00F98A1A /* SDL_blit_slow.c */, 0463873A0F0B5B7D0041FD65 /* SDL_blit_slow.h */, + FDA683000DF2374E00F98A1A /* SDL_blit.c */, + FDA683010DF2374E00F98A1A /* SDL_blit.h */, FDA6830B0DF2374E00F98A1A /* SDL_bmp.c */, 044E5FB711E606EB0076F181 /* SDL_clipboard.c */, + AA13B3431FB8B27700D9FEE6 /* SDL_egl_c.h */, + AA13B3471FB8B27800D9FEE6 /* SDL_egl.c */, 0463873E0F0B5B7D0041FD65 /* SDL_fillrect.c */, - FDA6830F0DF2374E00F98A1A /* SDL_pixels.c */, FDA683100DF2374E00F98A1A /* SDL_pixels_c.h */, + FDA6830F0DF2374E00F98A1A /* SDL_pixels.c */, + AA13B3461FB8B27800D9FEE6 /* SDL_rect_c.h */, FDA683110DF2374E00F98A1A /* SDL_rect.c */, - FDA683150DF2374E00F98A1A /* SDL_RLEaccel.c */, FDA683160DF2374E00F98A1A /* SDL_RLEaccel_c.h */, + FDA683150DF2374E00F98A1A /* SDL_RLEaccel.c */, + AA13B3451FB8B27800D9FEE6 /* SDL_shape_internals.h */, + AA13B3441FB8B27800D9FEE6 /* SDL_shape.c */, FDA683170DF2374E00F98A1A /* SDL_stretch.c */, FDA683190DF2374E00F98A1A /* SDL_surface.c */, FDA6831A0DF2374E00F98A1A /* SDL_sysvideo.h */, FDA6831B0DF2374E00F98A1A /* SDL_video.c */, 4D75171D1EE1D98200820EEA /* SDL_vulkan_internal.h */, 4D75171E1EE1D98200820EEA /* SDL_vulkan_utils.c */, + AA13B34F1FB8B3CC00D9FEE6 /* SDL_yuv.c */, + AA13B3481FB8B27800D9FEE6 /* SDL_yuv_c.h */, ); name = video; path = ../../src/video; @@ -1144,20 +1178,24 @@ 56A6703818565E760007D20F /* SDL_dynapi.h in Headers */, FD689FCF0E26E9D400F90B21 /* SDL_uikitappdelegate.h in Headers */, 56A6703518565E760007D20F /* SDL_dynapi_overrides.h in Headers */, + AA13B3571FB8B46400D9FEE6 /* yuv_rgb_std_func.h in Headers */, 047677BD0EA76A31008ABAF1 /* SDL_syshaptic.h in Headers */, 046387420F0B5B7D0041FD65 /* SDL_blit_slow.h in Headers */, 006E9888119552DD001DE610 /* SDL_rwopsbundlesupport.h in Headers */, 0420497011E6F03D007E7EC9 /* SDL_clipboardevents_c.h in Headers */, + AA13B34C1FB8B27800D9FEE6 /* SDL_rect_c.h in Headers */, + AA13B3581FB8B46400D9FEE6 /* yuv_rgb_sse_func.h in Headers */, 04BA9D6311EF474A00B60E01 /* SDL_gesture_c.h in Headers */, 04BA9D6511EF474A00B60E01 /* SDL_touch_c.h in Headers */, 041B2CF212FA0F680087D585 /* SDL_sysrender.h in Headers */, - 04409BA612FA989600FB9AA8 /* mmx.h in Headers */, 04409BA812FA989600FB9AA8 /* SDL_yuv_sw_c.h in Headers */, + AA13B3591FB8B46400D9FEE6 /* yuv_rgb.h in Headers */, 04F7807712FB751400FC43C0 /* SDL_blendfillrect.h in Headers */, 04F7807912FB751400FC43C0 /* SDL_blendline.h in Headers */, 04F7807B12FB751400FC43C0 /* SDL_blendpoint.h in Headers */, 04F7807C12FB751400FC43C0 /* SDL_draw.h in Headers */, 04F7807E12FB751400FC43C0 /* SDL_drawline.h in Headers */, + AA13B34E1FB8B27800D9FEE6 /* SDL_yuv_c.h in Headers */, 04F7808012FB751400FC43C0 /* SDL_drawpoint.h in Headers */, 04F7808412FB753F00FC43C0 /* SDL_nullframebuffer_c.h in Headers */, A7A9EEAA1F702631002A5589 /* SDL_steamcontroller.h in Headers */, @@ -1178,6 +1216,7 @@ AA75589F1595D55500BBD41B /* SDL_config_iphoneos.h in Headers */, AA7558A01595D55500BBD41B /* SDL_config.h in Headers */, AA7558A11595D55500BBD41B /* SDL_copying.h in Headers */, + AA13B3491FB8B27800D9FEE6 /* SDL_egl_c.h in Headers */, AA7558A21595D55500BBD41B /* SDL_cpuinfo.h in Headers */, AA7558A31595D55500BBD41B /* SDL_endian.h in Headers */, AA7558A41595D55500BBD41B /* SDL_error.h in Headers */, @@ -1188,6 +1227,7 @@ AA7558A81595D55500BBD41B /* SDL_hints.h in Headers */, 566726461DF72CF5001DD3DB /* SDL_dataqueue.h in Headers */, AA7558AA1595D55500BBD41B /* SDL_joystick.h in Headers */, + AA13B34B1FB8B27800D9FEE6 /* SDL_shape_internals.h in Headers */, AA7558AB1595D55500BBD41B /* SDL_keyboard.h in Headers */, AA7558AC1595D55500BBD41B /* SDL_keycode.h in Headers */, AA7558AD1595D55500BBD41B /* SDL_loadso.h in Headers */, @@ -1407,7 +1447,6 @@ FAB598681BB5C31600BE72C5 /* SDL_render_sw.c in Sources */, FAB5986A1BB5C31600BE72C5 /* SDL_rotate.c in Sources */, FAB5986D1BB5C31600BE72C5 /* SDL_render.c in Sources */, - FAB5986F1BB5C31600BE72C5 /* SDL_yuv_mmx.c in Sources */, FAB598711BB5C31600BE72C5 /* SDL_yuv_sw.c in Sources */, FAB598721BB5C31600BE72C5 /* SDL_getenv.c in Sources */, FAB598731BB5C31600BE72C5 /* SDL_iconv.c in Sources */, @@ -1501,6 +1540,7 @@ FD3F4A7A0DEA620800C5B771 /* SDL_stdlib.c in Sources */, FDA6844D0DF2374E00F98A1A /* SDL_blit.c in Sources */, FDA6844F0DF2374E00F98A1A /* SDL_blit_0.c in Sources */, + AA13B3501FB8B3CC00D9FEE6 /* SDL_yuv.c in Sources */, FDA684500DF2374E00F98A1A /* SDL_blit_1.c in Sources */, 566726451DF72CF5001DD3DB /* SDL_dataqueue.c in Sources */, FDA684510DF2374E00F98A1A /* SDL_blit_A.c in Sources */, @@ -1513,6 +1553,7 @@ FDA6845E0DF2374E00F98A1A /* SDL_rect.c in Sources */, FDA684620DF2374E00F98A1A /* SDL_RLEaccel.c in Sources */, FDA684640DF2374E00F98A1A /* SDL_stretch.c in Sources */, + AA13B34D1FB8B27800D9FEE6 /* SDL_egl.c in Sources */, FDA684660DF2374E00F98A1A /* SDL_surface.c in Sources */, FDA684680DF2374E00F98A1A /* SDL_video.c in Sources */, FDA685FB0DF244C800F98A1A /* SDL_nullevents.c in Sources */, @@ -1520,6 +1561,7 @@ FD5F9D2F0E0E08B3008E885B /* SDL_joystick.c in Sources */, FD689F030E26E5B600F90B21 /* SDL_sysjoystick.m in Sources */, FD689F1D0E26E5D900F90B21 /* SDL_uikitevents.m in Sources */, + AA13B35A1FB8B46400D9FEE6 /* yuv_rgb.c in Sources */, FD689F1F0E26E5D900F90B21 /* SDL_uikitopengles.m in Sources */, FD689F210E26E5D900F90B21 /* SDL_uikitvideo.m in Sources */, FD689F230E26E5D900F90B21 /* SDL_uikitview.m in Sources */, @@ -1542,7 +1584,6 @@ 04FFAB8B12E23B8D00BA343D /* SDL_atomic.c in Sources */, 04FFAB8C12E23B8D00BA343D /* SDL_spinlock.c in Sources */, 041B2CF112FA0F680087D585 /* SDL_render.c in Sources */, - 04409BA712FA989600FB9AA8 /* SDL_yuv_mmx.c in Sources */, 04409BA912FA989600FB9AA8 /* SDL_yuv_sw.c in Sources */, 04F7807612FB751400FC43C0 /* SDL_blendfillrect.c in Sources */, 04F7807812FB751400FC43C0 /* SDL_blendline.c in Sources */, @@ -1553,6 +1594,7 @@ 0442EC5112FE1C1E004C9285 /* SDL_render_sw.c in Sources */, 0442EC5312FE1C28004C9285 /* SDL_render_gles.c in Sources */, 0442EC5512FE1C3F004C9285 /* SDL_hints.c in Sources */, + AA13B34A1FB8B27800D9FEE6 /* SDL_shape.c in Sources */, 0402A85812FE70C600CECEE3 /* SDL_render_gles2.c in Sources */, 0402A85912FE70C600CECEE3 /* SDL_shaders_gles2.c in Sources */, 04BAC09D1300C1290055DE28 /* SDL_log.c in Sources */, diff --git a/Xcode-iOS/SDLtest/SDL2test.xcodeproj/project.pbxproj b/Xcode-iOS/SDLtest/SDL2test.xcodeproj/project.pbxproj index 99afeb1ee0e1a..89e381e0ae5c5 100644 --- a/Xcode-iOS/SDLtest/SDL2test.xcodeproj/project.pbxproj +++ b/Xcode-iOS/SDLtest/SDL2test.xcodeproj/project.pbxproj @@ -318,6 +318,7 @@ isa = XCBuildConfiguration; buildSettings = { EXECUTABLE_PREFIX = lib; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -326,6 +327,7 @@ isa = XCBuildConfiguration; buildSettings = { EXECUTABLE_PREFIX = lib; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; diff --git a/Xcode-iOS/Test/Info.plist b/Xcode-iOS/Test/Info.plist index c0f1179d35bb6..cd3c096dab348 100644 --- a/Xcode-iOS/Test/Info.plist +++ b/Xcode-iOS/Test/Info.plist @@ -22,7 +22,5 @@ ???? CFBundleVersion 1.0 - NSMainNibFile - diff --git a/Xcode-iOS/Test/TestiPhoneOS.xcodeproj/project.pbxproj b/Xcode-iOS/Test/TestiPhoneOS.xcodeproj/project.pbxproj index 2e9cbb3a31aa0..519d1507b4010 100755 --- a/Xcode-iOS/Test/TestiPhoneOS.xcodeproj/project.pbxproj +++ b/Xcode-iOS/Test/TestiPhoneOS.xcodeproj/project.pbxproj @@ -36,6 +36,22 @@ 56ED0508118A8FE400A56AA6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA8A89D0E2D111A00EA573E /* Foundation.framework */; }; 56ED0509118A8FE400A56AA6 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA8A89E0E2D111A00EA573E /* CoreAudio.framework */; }; 56ED0511118A904200A56AA6 /* testpower.c in Sources */ = {isa = PBXBuildFile; fileRef = 56ED0510118A904200A56AA6 /* testpower.c */; }; + AA13B3171FB8AEBC00D9FEE6 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FABA34761D8B4EAD00915323 /* AVFoundation.framework */; }; + AA13B3181FB8AEBC00D9FEE6 /* libSDL2test.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AA1EE452176059230029C7A5 /* libSDL2test.a */; }; + AA13B3191FB8AEBC00D9FEE6 /* libSDL2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD1B48B80E3131CA007AB34E /* libSDL2.a */; }; + AA13B31A1FB8AEBC00D9FEE6 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA684F7A1BAF1A4400DCFD1A /* GameController.framework */; }; + AA13B31B1FB8AEBC00D9FEE6 /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA8B4BAC1967076F00F8EB7C /* CoreMotion.framework */; }; + AA13B31C1FB8AEBC00D9FEE6 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA8A8980E2D111A00EA573E /* AudioToolbox.framework */; }; + AA13B31D1FB8AEBC00D9FEE6 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA8A8990E2D111A00EA573E /* QuartzCore.framework */; }; + AA13B31E1FB8AEBC00D9FEE6 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA8A89A0E2D111A00EA573E /* OpenGLES.framework */; }; + AA13B31F1FB8AEBC00D9FEE6 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA8A89B0E2D111A00EA573E /* CoreGraphics.framework */; }; + AA13B3201FB8AEBC00D9FEE6 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA8A89C0E2D111A00EA573E /* UIKit.framework */; }; + AA13B3211FB8AEBC00D9FEE6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA8A89D0E2D111A00EA573E /* Foundation.framework */; }; + AA13B3221FB8AEBC00D9FEE6 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA8A89E0E2D111A00EA573E /* CoreAudio.framework */; }; + AA13B32F1FB8AF0C00D9FEE6 /* testyuv.bmp in Resources */ = {isa = PBXBuildFile; fileRef = AA13B32E1FB8AF0C00D9FEE6 /* testyuv.bmp */; }; + AA13B3301FB8AF2300D9FEE6 /* testyuv.bmp in Resources */ = {isa = PBXBuildFile; fileRef = AA13B32E1FB8AF0C00D9FEE6 /* testyuv.bmp */; }; + AA13B35D1FB8B4E200D9FEE6 /* testyuv.c in Sources */ = {isa = PBXBuildFile; fileRef = AA13B35B1FB8B4D600D9FEE6 /* testyuv.c */; }; + AA13B3611FB8B52500D9FEE6 /* testyuv_cvt.c in Sources */ = {isa = PBXBuildFile; fileRef = AA13B35E1FB8B50D00D9FEE6 /* testyuv_cvt.c */; }; AA1EE470176059D00029C7A5 /* libSDL2test.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AA1EE452176059230029C7A5 /* libSDL2test.a */; }; AA1EE47117605A7F0029C7A5 /* libSDL2test.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AA1EE452176059230029C7A5 /* libSDL2test.a */; }; AA1EE47417605B5C0029C7A5 /* libSDL2test.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AA1EE452176059230029C7A5 /* libSDL2test.a */; }; @@ -407,6 +423,10 @@ 1D6058910D05DD3D006BFB54 /* testwm2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testwm2.app; sourceTree = BUILT_PRODUCTS_DIR; }; 56ED050D118A8FE400A56AA6 /* testpower.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testpower.app; sourceTree = BUILT_PRODUCTS_DIR; }; 56ED0510118A904200A56AA6 /* testpower.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testpower.c; path = ../../test/testpower.c; sourceTree = SOURCE_ROOT; }; + AA13B3261FB8AEBC00D9FEE6 /* testyuv.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testyuv.app; sourceTree = BUILT_PRODUCTS_DIR; }; + AA13B32E1FB8AF0C00D9FEE6 /* testyuv.bmp */ = {isa = PBXFileReference; lastKnownFileType = image.bmp; name = testyuv.bmp; path = ../../test/testyuv.bmp; sourceTree = ""; }; + AA13B35B1FB8B4D600D9FEE6 /* testyuv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testyuv.c; path = ../../test/testyuv.c; sourceTree = ""; }; + AA13B35E1FB8B50D00D9FEE6 /* testyuv_cvt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testyuv_cvt.c; path = ../../test/testyuv_cvt.c; sourceTree = ""; }; AA1EE44D176059220029C7A5 /* SDL2test.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SDL2test.xcodeproj; path = ../SDLtest/SDL2test.xcodeproj; sourceTree = ""; }; AAE7DEEC14CBB1E100DF1A0E /* testscale.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testscale.app; sourceTree = BUILT_PRODUCTS_DIR; }; AAE7DF4514CBB43900DF1A0E /* testscale.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testscale.c; path = ../../test/testscale.c; sourceTree = ""; }; @@ -552,6 +572,25 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + AA13B3161FB8AEBC00D9FEE6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AA13B3171FB8AEBC00D9FEE6 /* AVFoundation.framework in Frameworks */, + AA13B3181FB8AEBC00D9FEE6 /* libSDL2test.a in Frameworks */, + AA13B3191FB8AEBC00D9FEE6 /* libSDL2.a in Frameworks */, + AA13B31A1FB8AEBC00D9FEE6 /* GameController.framework in Frameworks */, + AA13B31B1FB8AEBC00D9FEE6 /* CoreMotion.framework in Frameworks */, + AA13B31C1FB8AEBC00D9FEE6 /* AudioToolbox.framework in Frameworks */, + AA13B31D1FB8AEBC00D9FEE6 /* QuartzCore.framework in Frameworks */, + AA13B31E1FB8AEBC00D9FEE6 /* OpenGLES.framework in Frameworks */, + AA13B31F1FB8AEBC00D9FEE6 /* CoreGraphics.framework in Frameworks */, + AA13B3201FB8AEBC00D9FEE6 /* UIKit.framework in Frameworks */, + AA13B3211FB8AEBC00D9FEE6 /* Foundation.framework in Frameworks */, + AA13B3221FB8AEBC00D9FEE6 /* CoreAudio.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; AAE7DEE014CBB1E100DF1A0E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1003,6 +1042,7 @@ FA3D99341BC4E644002C96C8 /* testgamecontroller-TV.app */, FABA34911D8B575200915323 /* testaudiocapture.app */, FABA34AA1D8B582100915323 /* loopwav-TV.app */, + AA13B3261FB8AEBC00D9FEE6 /* testyuv.app */, ); name = Products; sourceTree = ""; @@ -1051,9 +1091,9 @@ FDA8A73B0E2D0F0400EA573E /* src */ = { isa = PBXGroup; children = ( - FABA34931D8B578200915323 /* testaudiocapture.c */, 047A63F013285CD100CD7973 /* checkkeys.c */, FDA8A78B0E2D0F3D00EA573E /* loopwave.c */, + FABA34931D8B578200915323 /* testaudiocapture.c */, FDA8A7410E2D0F1600EA573E /* testaudioinfo.c */, FDC430090F0D86BF009C87E1 /* testdraw2.c */, FDA8A7470E2D0F1600EA573E /* testerror.c */, @@ -1076,6 +1116,8 @@ FDA8A75A0E2D0F1600EA573E /* testtimer.c */, FDA8A75B0E2D0F1600EA573E /* testver.c */, FDA8A75F0E2D0F1600EA573E /* testwm2.c */, + AA13B35E1FB8B50D00D9FEE6 /* testyuv_cvt.c */, + AA13B35B1FB8B4D600D9FEE6 /* testyuv.c */, FDA8A7610E2D0F1600EA573E /* torturethread.c */, ); name = src; @@ -1103,12 +1145,13 @@ FA0EF2281BAF4487000E07A6 /* axis.bmp */, FA0EF2291BAF4487000E07A6 /* button.bmp */, FA0EF22A1BAF4487000E07A6 /* controllermap.bmp */, - FDD2C18A0E2E52FE00B7A85F /* utf8.txt */, FDA8AAD90E2D33B000EA573E /* icon.bmp */, FDA8AADA0E2D33BA00EA573E /* moose.dat */, FDA8AADB0E2D33BA00EA573E /* picture.xbm */, FDA8AADE0E2D33C100EA573E /* sample.bmp */, FDA8AAE20E2D33C600EA573E /* sample.wav */, + AA13B32E1FB8AF0C00D9FEE6 /* testyuv.bmp */, + FDD2C18A0E2E52FE00B7A85F /* utf8.txt */, ); name = Resources; sourceTree = ""; @@ -1184,6 +1227,23 @@ productReference = 56ED050D118A8FE400A56AA6 /* testpower.app */; productType = "com.apple.product-type.application"; }; + AA13B3111FB8AEBC00D9FEE6 /* testyuv */ = { + isa = PBXNativeTarget; + buildConfigurationList = AA13B3231FB8AEBC00D9FEE6 /* Build configuration list for PBXNativeTarget "testyuv" */; + buildPhases = ( + AA13B3121FB8AEBC00D9FEE6 /* Resources */, + AA13B3141FB8AEBC00D9FEE6 /* Sources */, + AA13B3161FB8AEBC00D9FEE6 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = testyuv; + productName = Test; + productReference = AA13B3261FB8AEBC00D9FEE6 /* testyuv.app */; + productType = "com.apple.product-type.application"; + }; AAE7DEDA14CBB1E100DF1A0E /* testscale */ = { isa = PBXNativeTarget; buildConfigurationList = AAE7DEE914CBB1E100DF1A0E /* Build configuration list for PBXNativeTarget "testscale" */; @@ -1584,6 +1644,9 @@ attributes = { LastUpgradeCheck = 0630; TargetAttributes = { + AA13B3111FB8AEBC00D9FEE6 = { + DevelopmentTeam = EH385AYQ6F; + }; FA3D99331BC4E644002C96C8 = { CreatedOnToolsVersion = 7.1; }; @@ -1643,6 +1706,7 @@ FDD2C5740E2E8C7400B7A85F /* testtimer */, FDD2C5B30E2E8CFC00B7A85F /* testver */, 1D6058900D05DD3D006BFB54 /* testwm2 */, + AA13B3111FB8AEBC00D9FEE6 /* testyuv */, FDD2C6E20E2E959E00B7A85F /* torturethread */, ); }; @@ -1692,6 +1756,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + AA13B32F1FB8AF0C00D9FEE6 /* testyuv.bmp in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1710,6 +1775,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + AA13B3121FB8AEBC00D9FEE6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AA13B3301FB8AF2300D9FEE6 /* testyuv.bmp in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; AAE7DEDB14CBB1E100DF1A0E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1926,6 +1999,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + AA13B3141FB8AEBC00D9FEE6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AA13B3611FB8B52500D9FEE6 /* testyuv_cvt.c in Sources */, + AA13B35D1FB8B4E200D9FEE6 /* testyuv.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; AAE7DEDD14CBB1E100DF1A0E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2185,6 +2267,24 @@ }; name = Release; }; + AA13B3241FB8AEBC00D9FEE6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEVELOPMENT_TEAM = EH385AYQ6F; + INFOPLIST_FILE = Info.plist; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + AA13B3251FB8AEBC00D9FEE6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEVELOPMENT_TEAM = EH385AYQ6F; + INFOPLIST_FILE = Info.plist; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; AAE7DEEA14CBB1E100DF1A0E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2221,8 +2321,10 @@ isa = XCBuildConfiguration; buildSettings = { "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + ENABLE_BITCODE = NO; GCC_OPTIMIZATION_LEVEL = 0; HEADER_SEARCH_PATHS = ../../include; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-ObjC"; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; @@ -2235,7 +2337,9 @@ isa = XCBuildConfiguration; buildSettings = { "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + ENABLE_BITCODE = NO; HEADER_SEARCH_PATHS = ../../include; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; OTHER_LDFLAGS = "-ObjC"; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; SDKROOT = iphoneos; @@ -2696,6 +2800,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + AA13B3231FB8AEBC00D9FEE6 /* Build configuration list for PBXNativeTarget "testyuv" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AA13B3241FB8AEBC00D9FEE6 /* Debug */, + AA13B3251FB8AEBC00D9FEE6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; AAE7DEE914CBB1E100DF1A0E /* Build configuration list for PBXNativeTarget "testscale" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index 4208d9af94783..aada0f3e69564 100755 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -25,12 +25,8 @@ 0435673F1303160F00BA5428 /* SDL_shaders_gl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0435673D1303160F00BA5428 /* SDL_shaders_gl.h */; }; 043567401303160F00BA5428 /* SDL_shaders_gl.c in Sources */ = {isa = PBXBuildFile; fileRef = 0435673C1303160F00BA5428 /* SDL_shaders_gl.c */; }; 043567411303160F00BA5428 /* SDL_shaders_gl.h in Headers */ = {isa = PBXBuildFile; fileRef = 0435673D1303160F00BA5428 /* SDL_shaders_gl.h */; }; - 04409B9112FA97ED00FB9AA8 /* mmx.h in Headers */ = {isa = PBXBuildFile; fileRef = 04409B8D12FA97ED00FB9AA8 /* mmx.h */; }; - 04409B9212FA97ED00FB9AA8 /* SDL_yuv_mmx.c in Sources */ = {isa = PBXBuildFile; fileRef = 04409B8E12FA97ED00FB9AA8 /* SDL_yuv_mmx.c */; }; 04409B9312FA97ED00FB9AA8 /* SDL_yuv_sw_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 04409B8F12FA97ED00FB9AA8 /* SDL_yuv_sw_c.h */; }; 04409B9412FA97ED00FB9AA8 /* SDL_yuv_sw.c in Sources */ = {isa = PBXBuildFile; fileRef = 04409B9012FA97ED00FB9AA8 /* SDL_yuv_sw.c */; }; - 04409B9512FA97ED00FB9AA8 /* mmx.h in Headers */ = {isa = PBXBuildFile; fileRef = 04409B8D12FA97ED00FB9AA8 /* mmx.h */; }; - 04409B9612FA97ED00FB9AA8 /* SDL_yuv_mmx.c in Sources */ = {isa = PBXBuildFile; fileRef = 04409B8E12FA97ED00FB9AA8 /* SDL_yuv_mmx.c */; }; 04409B9712FA97ED00FB9AA8 /* SDL_yuv_sw_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 04409B8F12FA97ED00FB9AA8 /* SDL_yuv_sw_c.h */; }; 04409B9812FA97ED00FB9AA8 /* SDL_yuv_sw.c in Sources */ = {isa = PBXBuildFile; fileRef = 04409B9012FA97ED00FB9AA8 /* SDL_yuv_sw.c */; }; 0442EC1812FE1BBA004C9285 /* SDL_render_gl.c in Sources */ = {isa = PBXBuildFile; fileRef = 0442EC1712FE1BBA004C9285 /* SDL_render_gl.c */; }; @@ -540,6 +536,10 @@ AA75585E1595D4D800BBD41B /* SDL.h in Headers */ = {isa = PBXBuildFile; fileRef = AA7557F91595D4D800BBD41B /* SDL.h */; settings = {ATTRIBUTES = (Public, ); }; }; AA75585F1595D4D800BBD41B /* SDL.h in Headers */ = {isa = PBXBuildFile; fileRef = AA7557F91595D4D800BBD41B /* SDL.h */; settings = {ATTRIBUTES = (Public, ); }; }; AA8167541F5E727800518735 /* SDL_vulkan.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D4820431F0F10B400EDC31C /* SDL_vulkan.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AA9A7F111FB0206400FED37F /* yuv_rgb.h in Headers */ = {isa = PBXBuildFile; fileRef = AA9A7F0F1FB0206300FED37F /* yuv_rgb.h */; }; + AA9A7F121FB0206400FED37F /* yuv_rgb.c in Sources */ = {isa = PBXBuildFile; fileRef = AA9A7F101FB0206300FED37F /* yuv_rgb.c */; }; + AA9A7F151FB0209D00FED37F /* SDL_yuv.c in Sources */ = {isa = PBXBuildFile; fileRef = AA9A7F131FB0209C00FED37F /* SDL_yuv.c */; }; + AA9A7F161FB0209D00FED37F /* SDL_yuv_c.h in Headers */ = {isa = PBXBuildFile; fileRef = AA9A7F141FB0209C00FED37F /* SDL_yuv_c.h */; }; AA9E4093163BE51E007A2AD0 /* SDL_x11messagebox.c in Sources */ = {isa = PBXBuildFile; fileRef = AA9E4092163BE51E007A2AD0 /* SDL_x11messagebox.c */; }; AA9E4094163BE51E007A2AD0 /* SDL_x11messagebox.c in Sources */ = {isa = PBXBuildFile; fileRef = AA9E4092163BE51E007A2AD0 /* SDL_x11messagebox.c */; }; AA9FF95A1637CBF9000DF050 /* SDL_messagebox.h in Headers */ = {isa = PBXBuildFile; fileRef = AA9FF9591637CBF9000DF050 /* SDL_messagebox.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -644,7 +644,6 @@ DB313FB617554B71006C0E22 /* SDL_x11video.h in Headers */ = {isa = PBXBuildFile; fileRef = 04BDFFD412E6671800899322 /* SDL_x11video.h */; }; DB313FB717554B71006C0E22 /* SDL_x11window.h in Headers */ = {isa = PBXBuildFile; fileRef = 04BDFFD612E6671800899322 /* SDL_x11window.h */; }; DB313FB817554B71006C0E22 /* SDL_sysrender.h in Headers */ = {isa = PBXBuildFile; fileRef = 041B2C9F12FA0D680087D585 /* SDL_sysrender.h */; }; - DB313FB917554B71006C0E22 /* mmx.h in Headers */ = {isa = PBXBuildFile; fileRef = 04409B8D12FA97ED00FB9AA8 /* mmx.h */; }; DB313FBA17554B71006C0E22 /* SDL_yuv_sw_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 04409B8F12FA97ED00FB9AA8 /* SDL_yuv_sw_c.h */; }; DB313FBB17554B71006C0E22 /* SDL_nullframebuffer_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 04F7803712FB748500FC43C0 /* SDL_nullframebuffer_c.h */; }; DB313FBC17554B71006C0E22 /* SDL_blendfillrect.h in Headers */ = {isa = PBXBuildFile; fileRef = 04F7803E12FB74A200FC43C0 /* SDL_blendfillrect.h */; }; @@ -801,7 +800,6 @@ DB31405617554B71006C0E22 /* SDL_x11video.c in Sources */ = {isa = PBXBuildFile; fileRef = 04BDFFD312E6671800899322 /* SDL_x11video.c */; }; DB31405717554B71006C0E22 /* SDL_x11window.c in Sources */ = {isa = PBXBuildFile; fileRef = 04BDFFD512E6671800899322 /* SDL_x11window.c */; }; DB31405817554B71006C0E22 /* SDL_render.c in Sources */ = {isa = PBXBuildFile; fileRef = 041B2C9E12FA0D680087D585 /* SDL_render.c */; }; - DB31405917554B71006C0E22 /* SDL_yuv_mmx.c in Sources */ = {isa = PBXBuildFile; fileRef = 04409B8E12FA97ED00FB9AA8 /* SDL_yuv_mmx.c */; }; DB31405A17554B71006C0E22 /* SDL_yuv_sw.c in Sources */ = {isa = PBXBuildFile; fileRef = 04409B9012FA97ED00FB9AA8 /* SDL_yuv_sw.c */; }; DB31405B17554B71006C0E22 /* SDL_nullframebuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 04F7803812FB748500FC43C0 /* SDL_nullframebuffer.c */; }; DB31405C17554B71006C0E22 /* SDL_blendfillrect.c in Sources */ = {isa = PBXBuildFile; fileRef = 04F7803D12FB74A200FC43C0 /* SDL_blendfillrect.c */; }; @@ -852,8 +850,6 @@ 041B2C9F12FA0D680087D585 /* SDL_sysrender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sysrender.h; sourceTree = ""; }; 0435673C1303160F00BA5428 /* SDL_shaders_gl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_shaders_gl.c; sourceTree = ""; }; 0435673D1303160F00BA5428 /* SDL_shaders_gl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_shaders_gl.h; sourceTree = ""; }; - 04409B8D12FA97ED00FB9AA8 /* mmx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mmx.h; sourceTree = ""; }; - 04409B8E12FA97ED00FB9AA8 /* SDL_yuv_mmx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_yuv_mmx.c; sourceTree = ""; }; 04409B8F12FA97ED00FB9AA8 /* SDL_yuv_sw_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_yuv_sw_c.h; sourceTree = ""; }; 04409B9012FA97ED00FB9AA8 /* SDL_yuv_sw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_yuv_sw.c; sourceTree = ""; }; 0442EC1712FE1BBA004C9285 /* SDL_render_gl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_render_gl.c; sourceTree = ""; }; @@ -1104,6 +1100,10 @@ AA7557F71595D4D800BBD41B /* SDL_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_version.h; sourceTree = ""; }; AA7557F81595D4D800BBD41B /* SDL_video.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_video.h; sourceTree = ""; }; AA7557F91595D4D800BBD41B /* SDL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL.h; sourceTree = ""; }; + AA9A7F0F1FB0206300FED37F /* yuv_rgb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = yuv_rgb.h; sourceTree = ""; }; + AA9A7F101FB0206300FED37F /* yuv_rgb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = yuv_rgb.c; sourceTree = ""; }; + AA9A7F131FB0209C00FED37F /* SDL_yuv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_yuv.c; sourceTree = ""; }; + AA9A7F141FB0209C00FED37F /* SDL_yuv_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_yuv_c.h; sourceTree = ""; }; AA9E4092163BE51E007A2AD0 /* SDL_x11messagebox.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_x11messagebox.c; sourceTree = ""; }; AA9FF9591637CBF9000DF050 /* SDL_messagebox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_messagebox.h; sourceTree = ""; }; AABCC38B164063D200AB8930 /* SDL_cocoamessagebox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_cocoamessagebox.h; sourceTree = ""; }; @@ -1264,10 +1264,8 @@ children = ( 041B2C9A12FA0D680087D585 /* opengl */, 041B2CA012FA0D680087D585 /* software */, - 04409B8D12FA97ED00FB9AA8 /* mmx.h */, 041B2C9E12FA0D680087D585 /* SDL_render.c */, 041B2C9F12FA0D680087D585 /* SDL_sysrender.h */, - 04409B8E12FA97ED00FB9AA8 /* SDL_yuv_mmx.c */, 04409B8F12FA97ED00FB9AA8 /* SDL_yuv_sw_c.h */, 04409B9012FA97ED00FB9AA8 /* SDL_yuv_sw.c */, ); @@ -1568,8 +1566,7 @@ 04BDFEC112E6671800899322 /* cocoa */, 04BDFEE712E6671800899322 /* dummy */, 04BDFFB712E6671800899322 /* x11 */, - 04BDFF4E12E6671800899322 /* SDL_blit.c */, - 04BDFF4F12E6671800899322 /* SDL_blit.h */, + AA9A7F0E1FB0200B00FED37F /* yuv2rgb */, 04BDFF5012E6671800899322 /* SDL_blit_0.c */, 04BDFF5112E6671800899322 /* SDL_blit_1.c */, 04BDFF5212E6671800899322 /* SDL_blit_A.c */, @@ -1580,22 +1577,26 @@ 04BDFF5712E6671800899322 /* SDL_blit_N.c */, 04BDFF5812E6671800899322 /* SDL_blit_slow.c */, 04BDFF5912E6671800899322 /* SDL_blit_slow.h */, + 04BDFF4E12E6671800899322 /* SDL_blit.c */, + 04BDFF4F12E6671800899322 /* SDL_blit.h */, 04BDFF5A12E6671800899322 /* SDL_bmp.c */, 04BDFF5B12E6671800899322 /* SDL_clipboard.c */, 04BDFF6012E6671800899322 /* SDL_fillrect.c */, - 04BDFF6512E6671800899322 /* SDL_pixels.c */, 04BDFF6612E6671800899322 /* SDL_pixels_c.h */, + 04BDFF6512E6671800899322 /* SDL_pixels.c */, 04BDFF6712E6671800899322 /* SDL_rect.c */, - 04BDFF6F12E6671800899322 /* SDL_RLEaccel.c */, 04BDFF7012E6671800899322 /* SDL_RLEaccel_c.h */, - 04BDFF7112E6671800899322 /* SDL_shape.c */, + 04BDFF6F12E6671800899322 /* SDL_RLEaccel.c */, 04BDFF7212E6671800899322 /* SDL_shape_internals.h */, + 04BDFF7112E6671800899322 /* SDL_shape.c */, 04BDFF7312E6671800899322 /* SDL_stretch.c */, 04BDFF7412E6671800899322 /* SDL_surface.c */, 04BDFF7512E6671800899322 /* SDL_sysvideo.h */, 04BDFF7612E6671800899322 /* SDL_video.c */, 4D16644C1EDD6023003DE88E /* SDL_vulkan_internal.h */, 4D16644D1EDD6023003DE88E /* SDL_vulkan_utils.c */, + AA9A7F141FB0209C00FED37F /* SDL_yuv_c.h */, + AA9A7F131FB0209C00FED37F /* SDL_yuv.c */, ); name = video; path = ../../src/video; @@ -1763,6 +1764,16 @@ name = dynapi; sourceTree = ""; }; + AA9A7F0E1FB0200B00FED37F /* yuv2rgb */ = { + isa = PBXGroup; + children = ( + AA9A7F101FB0206300FED37F /* yuv_rgb.c */, + AA9A7F0F1FB0206300FED37F /* yuv_rgb.h */, + ); + name = yuv2rgb; + path = ../../src/video/yuv2rgb; + sourceTree = SOURCE_ROOT; + }; BEC562FE0761C0E800A33029 /* Linked Frameworks */ = { isa = PBXGroup; children = ( @@ -1882,6 +1893,7 @@ 04BD004B12E6671800899322 /* SDL_events_c.h in Headers */, 04BD004D12E6671800899322 /* SDL_gesture_c.h in Headers */, 04BD004F12E6671800899322 /* SDL_keyboard_c.h in Headers */, + AA9A7F111FB0206400FED37F /* yuv_rgb.h in Headers */, 04BD005112E6671800899322 /* SDL_mouse_c.h in Headers */, 04BD005312E6671800899322 /* SDL_sysevents.h in Headers */, 04BD005512E6671800899322 /* SDL_touch_c.h in Headers */, @@ -1937,7 +1949,7 @@ 04BD01F712E6671800899322 /* SDL_x11video.h in Headers */, 04BD01F912E6671800899322 /* SDL_x11window.h in Headers */, 041B2CA612FA0D680087D585 /* SDL_sysrender.h in Headers */, - 04409B9112FA97ED00FB9AA8 /* mmx.h in Headers */, + AA9A7F161FB0209D00FED37F /* SDL_yuv_c.h in Headers */, 04409B9312FA97ED00FB9AA8 /* SDL_yuv_sw_c.h in Headers */, 04F7803912FB748500FC43C0 /* SDL_nullframebuffer_c.h in Headers */, 04F7804A12FB74A200FC43C0 /* SDL_blendfillrect.h in Headers */, @@ -2094,7 +2106,6 @@ AAC07100195606770073DCDF /* SDL_opengles2_gl2ext.h in Headers */, 04BD041112E6671800899322 /* SDL_x11window.h in Headers */, 041B2CAC12FA0D680087D585 /* SDL_sysrender.h in Headers */, - 04409B9512FA97ED00FB9AA8 /* mmx.h in Headers */, 04409B9712FA97ED00FB9AA8 /* SDL_yuv_sw_c.h in Headers */, 04F7803B12FB748500FC43C0 /* SDL_nullframebuffer_c.h in Headers */, 04F7805612FB74A200FC43C0 /* SDL_blendfillrect.h in Headers */, @@ -2249,7 +2260,6 @@ AAC07101195606770073DCDF /* SDL_opengles2_gl2ext.h in Headers */, DB313FB717554B71006C0E22 /* SDL_x11window.h in Headers */, DB313FB817554B71006C0E22 /* SDL_sysrender.h in Headers */, - DB313FB917554B71006C0E22 /* mmx.h in Headers */, DB313FBA17554B71006C0E22 /* SDL_yuv_sw_c.h in Headers */, DB313FBB17554B71006C0E22 /* SDL_nullframebuffer_c.h in Headers */, DB313FBC17554B71006C0E22 /* SDL_blendfillrect.h in Headers */, @@ -2475,6 +2485,7 @@ 04BD00F412E6671800899322 /* SDL_cocoaclipboard.m in Sources */, 04BD00F612E6671800899322 /* SDL_cocoaevents.m in Sources */, 04BD00F812E6671800899322 /* SDL_cocoakeyboard.m in Sources */, + AA9A7F151FB0209D00FED37F /* SDL_yuv.c in Sources */, 04BD00FA12E6671800899322 /* SDL_cocoamodes.m in Sources */, 4D16644F1EDD6023003DE88E /* SDL_vulkan_utils.c in Sources */, 04BD00FC12E6671800899322 /* SDL_cocoamouse.m in Sources */, @@ -2487,6 +2498,7 @@ 04BD017512E6671800899322 /* SDL_blit.c in Sources */, 04BD017712E6671800899322 /* SDL_blit_0.c in Sources */, 04BD017812E6671800899322 /* SDL_blit_1.c in Sources */, + AA9A7F121FB0206400FED37F /* yuv_rgb.c in Sources */, 04BD017912E6671800899322 /* SDL_blit_A.c in Sources */, 04BD017A12E6671800899322 /* SDL_blit_auto.c in Sources */, 04BD017C12E6671800899322 /* SDL_blit_copy.c in Sources */, @@ -2516,7 +2528,6 @@ 04BD01F612E6671800899322 /* SDL_x11video.c in Sources */, 04BD01F812E6671800899322 /* SDL_x11window.c in Sources */, 041B2CA512FA0D680087D585 /* SDL_render.c in Sources */, - 04409B9212FA97ED00FB9AA8 /* SDL_yuv_mmx.c in Sources */, 04409B9412FA97ED00FB9AA8 /* SDL_yuv_sw.c in Sources */, 04F7803A12FB748500FC43C0 /* SDL_nullframebuffer.c in Sources */, 04F7804912FB74A200FC43C0 /* SDL_blendfillrect.c in Sources */, @@ -2641,7 +2652,6 @@ 04BD040E12E6671800899322 /* SDL_x11video.c in Sources */, 04BD041012E6671800899322 /* SDL_x11window.c in Sources */, 041B2CAB12FA0D680087D585 /* SDL_render.c in Sources */, - 04409B9612FA97ED00FB9AA8 /* SDL_yuv_mmx.c in Sources */, 04409B9812FA97ED00FB9AA8 /* SDL_yuv_sw.c in Sources */, 04F7803C12FB748500FC43C0 /* SDL_nullframebuffer.c in Sources */, 04F7805512FB74A200FC43C0 /* SDL_blendfillrect.c in Sources */, @@ -2764,7 +2774,6 @@ DB31405617554B71006C0E22 /* SDL_x11video.c in Sources */, DB31405717554B71006C0E22 /* SDL_x11window.c in Sources */, DB31405817554B71006C0E22 /* SDL_render.c in Sources */, - DB31405917554B71006C0E22 /* SDL_yuv_mmx.c in Sources */, DB31405A17554B71006C0E22 /* SDL_yuv_sw.c in Sources */, DB31405B17554B71006C0E22 /* SDL_nullframebuffer.c in Sources */, DB31405C17554B71006C0E22 /* SDL_blendfillrect.c in Sources */, diff --git a/configure b/configure index b233aca8e1ca6..743cd05522b83 100755 --- a/configure +++ b/configure @@ -16874,6 +16874,7 @@ SOURCES="$SOURCES $srcdir/src/stdlib/*.c" SOURCES="$SOURCES $srcdir/src/thread/*.c" SOURCES="$SOURCES $srcdir/src/timer/*.c" SOURCES="$SOURCES $srcdir/src/video/*.c" +SOURCES="$SOURCES $srcdir/src/video/yuv2rgb/*.c" # Check whether --enable-atomic was given. diff --git a/configure.in b/configure.in index f8b556eb6737c..e55d02e3ebe1a 100644 --- a/configure.in +++ b/configure.in @@ -343,6 +343,7 @@ SOURCES="$SOURCES $srcdir/src/stdlib/*.c" SOURCES="$SOURCES $srcdir/src/thread/*.c" SOURCES="$SOURCES $srcdir/src/timer/*.c" SOURCES="$SOURCES $srcdir/src/video/*.c" +SOURCES="$SOURCES $srcdir/src/video/yuv2rgb/*.c" dnl Enable/disable various subsystems of the SDL library diff --git a/include/SDL_surface.h b/include/SDL_surface.h index 510690c9cffe3..ab9ba11976aab 100644 --- a/include/SDL_surface.h +++ b/include/SDL_surface.h @@ -97,6 +97,17 @@ typedef struct SDL_Surface typedef int (SDLCALL *SDL_blit) (struct SDL_Surface * src, SDL_Rect * srcrect, struct SDL_Surface * dst, SDL_Rect * dstrect); +/** + * \brief The formula used for converting between YUV and RGB + */ +typedef enum +{ + SDL_YUV_CONVERSION_JPEG, /**< Full range JPEG */ + SDL_YUV_CONVERSION_BT601, /**< BT.601 (the default) */ + SDL_YUV_CONVERSION_BT709, /**< BT.709 */ + SDL_YUV_CONVERSION_AUTOMATIC /**< BT.601 for SD content, BT.709 for HD content */ +} SDL_YUV_CONVERSION_MODE; + /** * Allocate and free an RGB surface. * @@ -509,6 +520,20 @@ extern DECLSPEC int SDLCALL SDL_LowerBlitScaled (SDL_Surface * src, SDL_Rect * srcrect, SDL_Surface * dst, SDL_Rect * dstrect); +/** + * \brief Set the YUV conversion mode + */ +extern DECLSPEC void SDLCALL SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_MODE mode); + +/** + * \brief Get the YUV conversion mode + */ +extern DECLSPEC SDL_YUV_CONVERSION_MODE SDLCALL SDL_GetYUVConversionMode(void); + +/** + * \brief Get the YUV conversion mode, returning the correct mode for the resolution when the current conversion mode is SDL_YUV_CONVERSION_AUTOMATIC + */ +extern DECLSPEC SDL_YUV_CONVERSION_MODE SDLCALL SDL_GetYUVConversionModeForResolution(int width, int height); /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 5f2c0a42829d2..cf61acf9b46de 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -660,3 +660,6 @@ #define SDL_scalbnf SDL_scalbnf_REAL #define SDL_fmod SDL_fmod_REAL #define SDL_fmodf SDL_fmodf_REAL +#define SDL_SetYUVConversionMode SDL_SetYUVConversionMode_REAL +#define SDL_GetYUVConversionMode SDL_GetYUVConversionMode_REAL +#define SDL_GetYUVConversionModeForResolution SDL_GetYUVConversionModeForResolution_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 01b4da19836b3..de2d488070bb2 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -694,3 +694,6 @@ SDL_DYNAPI_PROC(float,SDL_powf,(float a, float b),(a,b),return) SDL_DYNAPI_PROC(float,SDL_scalbnf,(float a, int b),(a,b),return) SDL_DYNAPI_PROC(double,SDL_fmod,(double a, double b),(a,b),return) SDL_DYNAPI_PROC(float,SDL_fmodf,(float a, float b),(a,b),return) +SDL_DYNAPI_PROC(void,SDL_SetYUVConversionMode,(SDL_YUV_CONVERSION_MODE a),(a),) +SDL_DYNAPI_PROC(SDL_YUV_CONVERSION_MODE,SDL_GetYUVConversionMode,(void),(),return) +SDL_DYNAPI_PROC(SDL_YUV_CONVERSION_MODE,SDL_GetYUVConversionModeForResolution,(int a, int b),(a,b),return) diff --git a/src/render/SDL_yuv_mmx.c b/src/render/SDL_yuv_mmx.c deleted file mode 100644 index 90da9f9b9ffc7..0000000000000 --- a/src/render/SDL_yuv_mmx.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2017 Sam Lantinga - - 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, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "../SDL_internal.h" - -#include "SDL_yuv_mmx_c.h" - -#ifdef USE_MMX_ASSEMBLY - -#include "SDL_stdinc.h" - -#include "mmx.h" - -/* *INDENT-OFF* */ - -static mmx_t MMX_0080w = { .ud = {0x00800080, 0x00800080} }; -static mmx_t MMX_00FFw = { .ud = {0x00ff00ff, 0x00ff00ff} }; -static mmx_t MMX_FF00w = { .ud = {0xff00ff00, 0xff00ff00} }; - -static mmx_t MMX_Ycoeff = { .uw = {0x004a, 0x004a, 0x004a, 0x004a} }; - -static mmx_t MMX_UbluRGB = { .uw = {0x0072, 0x0072, 0x0072, 0x0072} }; -static mmx_t MMX_VredRGB = { .uw = {0x0059, 0x0059, 0x0059, 0x0059} }; -static mmx_t MMX_UgrnRGB = { .uw = {0xffea, 0xffea, 0xffea, 0xffea} }; -static mmx_t MMX_VgrnRGB = { .uw = {0xffd2, 0xffd2, 0xffd2, 0xffd2} }; - -static mmx_t MMX_Ublu5x5 = { .uw = {0x0081, 0x0081, 0x0081, 0x0081} }; -static mmx_t MMX_Vred5x5 = { .uw = {0x0066, 0x0066, 0x0066, 0x0066} }; -static mmx_t MMX_Ugrn565 = { .uw = {0xffe8, 0xffe8, 0xffe8, 0xffe8} }; -static mmx_t MMX_Vgrn565 = { .uw = {0xffcd, 0xffcd, 0xffcd, 0xffcd} }; - -static mmx_t MMX_red565 = { .uw = {0xf800, 0xf800, 0xf800, 0xf800} }; -static mmx_t MMX_grn565 = { .uw = {0x07e0, 0x07e0, 0x07e0, 0x07e0} }; - -/** - This MMX assembler is my first assembler/MMX program ever. - Thus it maybe buggy. - Send patches to: - mvogt@rhrk.uni-kl.de - - After it worked fine I have "obfuscated" the code a bit to have - more parallism in the MMX units. This means I moved - initilisation around and delayed other instruction. - Performance measurement did not show that this brought any advantage - but in theory it _should_ be faster this way. - - The overall performanve gain to the C based dither was 30%-40%. - The MMX routine calculates 256bit=8RGB values in each cycle - (4 for row1 & 4 for row2) - - The red/green/blue.. coefficents are taken from the mpeg_play - player. They look nice, but I dont know if you can have - better values, to avoid integer rounding errors. - - - IMPORTANT: - ========== - - It is a requirement that the cr/cb/lum are 8 byte aligned and - the out are 16byte aligned or you will/may get segfaults - -*/ - -void ColorRGBDitherYV12MMX1X( int *colortab, Uint32 *rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod ) -{ - Uint32 *row1; - Uint32 *row2; - - unsigned char* y = lum +cols*rows; /* Pointer to the end */ - int x = 0; - row1 = (Uint32 *)out; /* 32 bit target */ - row2 = (Uint32 *)out+cols+mod; /* start of second row */ - mod = (mod+cols+mod)*4; /* increment for row1 in byte */ - - __asm__ __volatile__ ( - ".align 8\n" - "1:\n" - - /* create Cr (result in mm1) */ - "movd (%0),%%mm1\n" /* 0 0 0 0 v3 v2 v1 v0 */ - "pxor %%mm7,%%mm7\n" /* 00 00 00 00 00 00 00 00 */ - "movd (%2), %%mm2\n" /* 0 0 0 0 l3 l2 l1 l0 */ - "punpcklbw %%mm7,%%mm1\n" /* 0 v3 0 v2 00 v1 00 v0 */ - "punpckldq %%mm1,%%mm1\n" /* 00 v1 00 v0 00 v1 00 v0 */ - "psubw %9,%%mm1\n" /* mm1-128:r1 r1 r0 r0 r1 r1 r0 r0 */ - - /* create Cr_g (result in mm0) */ - "movq %%mm1,%%mm0\n" /* r1 r1 r0 r0 r1 r1 r0 r0 */ - "pmullw %10,%%mm0\n" /* red*-46dec=0.7136*64 */ - "pmullw %11,%%mm1\n" /* red*89dec=1.4013*64 */ - "psraw $6, %%mm0\n" /* red=red/64 */ - "psraw $6, %%mm1\n" /* red=red/64 */ - - /* create L1 L2 (result in mm2,mm4) */ - /* L2=lum+cols */ - "movq (%2,%4),%%mm3\n" /* 0 0 0 0 L3 L2 L1 L0 */ - "punpckldq %%mm3,%%mm2\n" /* L3 L2 L1 L0 l3 l2 l1 l0 */ - "movq %%mm2,%%mm4\n" /* L3 L2 L1 L0 l3 l2 l1 l0 */ - "pand %12,%%mm2\n" /* L3 0 L1 0 l3 0 l1 0 */ - "pand %13,%%mm4\n" /* 0 L2 0 L0 0 l2 0 l0 */ - "psrlw $8,%%mm2\n" /* 0 L3 0 L1 0 l3 0 l1 */ - - /* create R (result in mm6) */ - "movq %%mm2,%%mm5\n" /* 0 L3 0 L1 0 l3 0 l1 */ - "movq %%mm4,%%mm6\n" /* 0 L2 0 L0 0 l2 0 l0 */ - "paddsw %%mm1, %%mm5\n" /* lum1+red:x R3 x R1 x r3 x r1 */ - "paddsw %%mm1, %%mm6\n" /* lum1+red:x R2 x R0 x r2 x r0 */ - "packuswb %%mm5,%%mm5\n" /* R3 R1 r3 r1 R3 R1 r3 r1 */ - "packuswb %%mm6,%%mm6\n" /* R2 R0 r2 r0 R2 R0 r2 r0 */ - "pxor %%mm7,%%mm7\n" /* 00 00 00 00 00 00 00 00 */ - "punpcklbw %%mm5,%%mm6\n" /* R3 R2 R1 R0 r3 r2 r1 r0 */ - - /* create Cb (result in mm1) */ - "movd (%1), %%mm1\n" /* 0 0 0 0 u3 u2 u1 u0 */ - "punpcklbw %%mm7,%%mm1\n" /* 0 u3 0 u2 00 u1 00 u0 */ - "punpckldq %%mm1,%%mm1\n" /* 00 u1 00 u0 00 u1 00 u0 */ - "psubw %9,%%mm1\n" /* mm1-128:u1 u1 u0 u0 u1 u1 u0 u0 */ - - /* create Cb_g (result in mm5) */ - "movq %%mm1,%%mm5\n" /* u1 u1 u0 u0 u1 u1 u0 u0 */ - "pmullw %14,%%mm5\n" /* blue*-109dec=1.7129*64 */ - "pmullw %15,%%mm1\n" /* blue*114dec=1.78125*64 */ - "psraw $6, %%mm5\n" /* blue=red/64 */ - "psraw $6, %%mm1\n" /* blue=blue/64 */ - - /* create G (result in mm7) */ - "movq %%mm2,%%mm3\n" /* 0 L3 0 L1 0 l3 0 l1 */ - "movq %%mm4,%%mm7\n" /* 0 L2 0 L0 0 l2 0 l1 */ - "paddsw %%mm5, %%mm3\n" /* lum1+Cb_g:x G3t x G1t x g3t x g1t */ - "paddsw %%mm5, %%mm7\n" /* lum1+Cb_g:x G2t x G0t x g2t x g0t */ - "paddsw %%mm0, %%mm3\n" /* lum1+Cr_g:x G3 x G1 x g3 x g1 */ - "paddsw %%mm0, %%mm7\n" /* lum1+blue:x G2 x G0 x g2 x g0 */ - "packuswb %%mm3,%%mm3\n" /* G3 G1 g3 g1 G3 G1 g3 g1 */ - "packuswb %%mm7,%%mm7\n" /* G2 G0 g2 g0 G2 G0 g2 g0 */ - "punpcklbw %%mm3,%%mm7\n" /* G3 G2 G1 G0 g3 g2 g1 g0 */ - - /* create B (result in mm5) */ - "movq %%mm2,%%mm3\n" /* 0 L3 0 L1 0 l3 0 l1 */ - "movq %%mm4,%%mm5\n" /* 0 L2 0 L0 0 l2 0 l1 */ - "paddsw %%mm1, %%mm3\n" /* lum1+blue:x B3 x B1 x b3 x b1 */ - "paddsw %%mm1, %%mm5\n" /* lum1+blue:x B2 x B0 x b2 x b0 */ - "packuswb %%mm3,%%mm3\n" /* B3 B1 b3 b1 B3 B1 b3 b1 */ - "packuswb %%mm5,%%mm5\n" /* B2 B0 b2 b0 B2 B0 b2 b0 */ - "punpcklbw %%mm3,%%mm5\n" /* B3 B2 B1 B0 b3 b2 b1 b0 */ - - /* fill destination row1 (needed are mm6=Rr,mm7=Gg,mm5=Bb) */ - - "pxor %%mm2,%%mm2\n" /* 0 0 0 0 0 0 0 0 */ - "pxor %%mm4,%%mm4\n" /* 0 0 0 0 0 0 0 0 */ - "movq %%mm6,%%mm1\n" /* R3 R2 R1 R0 r3 r2 r1 r0 */ - "movq %%mm5,%%mm3\n" /* B3 B2 B1 B0 b3 b2 b1 b0 */ - - /* process lower lum */ - "punpcklbw %%mm4,%%mm1\n" /* 0 r3 0 r2 0 r1 0 r0 */ - "punpcklbw %%mm4,%%mm3\n" /* 0 b3 0 b2 0 b1 0 b0 */ - "movq %%mm1,%%mm2\n" /* 0 r3 0 r2 0 r1 0 r0 */ - "movq %%mm3,%%mm0\n" /* 0 b3 0 b2 0 b1 0 b0 */ - "punpcklwd %%mm1,%%mm3\n" /* 0 r1 0 b1 0 r0 0 b0 */ - "punpckhwd %%mm2,%%mm0\n" /* 0 r3 0 b3 0 r2 0 b2 */ - - "pxor %%mm2,%%mm2\n" /* 0 0 0 0 0 0 0 0 */ - "movq %%mm7,%%mm1\n" /* G3 G2 G1 G0 g3 g2 g1 g0 */ - "punpcklbw %%mm1,%%mm2\n" /* g3 0 g2 0 g1 0 g0 0 */ - "punpcklwd %%mm4,%%mm2\n" /* 0 0 g1 0 0 0 g0 0 */ - "por %%mm3, %%mm2\n" /* 0 r1 g1 b1 0 r0 g0 b0 */ - "movq %%mm2,(%3)\n" /* wrote out ! row1 */ - - "pxor %%mm2,%%mm2\n" /* 0 0 0 0 0 0 0 0 */ - "punpcklbw %%mm1,%%mm4\n" /* g3 0 g2 0 g1 0 g0 0 */ - "punpckhwd %%mm2,%%mm4\n" /* 0 0 g3 0 0 0 g2 0 */ - "por %%mm0, %%mm4\n" /* 0 r3 g3 b3 0 r2 g2 b2 */ - "movq %%mm4,8(%3)\n" /* wrote out ! row1 */ - - /* fill destination row2 (needed are mm6=Rr,mm7=Gg,mm5=Bb) */ - /* this can be done "destructive" */ - "pxor %%mm2,%%mm2\n" /* 0 0 0 0 0 0 0 0 */ - "punpckhbw %%mm2,%%mm6\n" /* 0 R3 0 R2 0 R1 0 R0 */ - "punpckhbw %%mm1,%%mm5\n" /* G3 B3 G2 B2 G1 B1 G0 B0 */ - "movq %%mm5,%%mm1\n" /* G3 B3 G2 B2 G1 B1 G0 B0 */ - "punpcklwd %%mm6,%%mm1\n" /* 0 R1 G1 B1 0 R0 G0 B0 */ - "movq %%mm1,(%5)\n" /* wrote out ! row2 */ - "punpckhwd %%mm6,%%mm5\n" /* 0 R3 G3 B3 0 R2 G2 B2 */ - "movq %%mm5,8(%5)\n" /* wrote out ! row2 */ - - "addl $4,%2\n" /* lum+4 */ - "leal 16(%3),%3\n" /* row1+16 */ - "leal 16(%5),%5\n" /* row2+16 */ - "addl $2,%0\n" /* cr+2 */ - "addl $2,%1\n" /* cb+2 */ - - "addl $4,%6\n" /* x+4 */ - "cmpl %4,%6\n" - - "jl 1b\n" - "addl %4,%2\n" /* lum += cols */ - "addl %8,%3\n" /* row1+= mod */ - "addl %8,%5\n" /* row2+= mod */ - "movl $0,%6\n" /* x=0 */ - "cmpl %7,%2\n" - "jl 1b\n" - - "emms\n" /* reset MMX registers. */ - : - : "r" (cr), "r"(cb),"r"(lum), - "r"(row1),"r"(cols),"r"(row2),"m"(x),"m"(y),"m"(mod), - "m"(MMX_0080w),"m"(MMX_VgrnRGB),"m"(MMX_VredRGB), - "m"(MMX_FF00w),"m"(MMX_00FFw),"m"(MMX_UgrnRGB), - "m"(MMX_UbluRGB) - ); -} - -void Color565DitherYV12MMX1X( int *colortab, Uint32 *rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod ) -{ - Uint16 *row1; - Uint16 *row2; - - unsigned char* y = lum +cols*rows; /* Pointer to the end */ - int x = 0; - row1 = (Uint16 *)out; /* 16 bit target */ - row2 = (Uint16 *)out+cols+mod; /* start of second row */ - mod = (mod+cols+mod)*2; /* increment for row1 in byte */ - - __asm__ __volatile__( - ".align 8\n" - "1:\n" - - "movd (%1), %%mm0\n" /* 4 Cb 0 0 0 0 u3 u2 u1 u0 */ - "pxor %%mm7, %%mm7\n" - "movd (%0), %%mm1\n" /* 4 Cr 0 0 0 0 v3 v2 v1 v0 */ - - "punpcklbw %%mm7, %%mm0\n" /* 4 W cb 0 u3 0 u2 0 u1 0 u0 */ - "punpcklbw %%mm7, %%mm1\n" /* 4 W cr 0 v3 0 v2 0 v1 0 v0 */ - "psubw %9, %%mm0\n" - "psubw %9, %%mm1\n" - "movq %%mm0, %%mm2\n" /* Cb 0 u3 0 u2 0 u1 0 u0 */ - "movq %%mm1, %%mm3\n" /* Cr */ - "pmullw %10, %%mm2\n" /* Cb2green 0 R3 0 R2 0 R1 0 R0 */ - "movq (%2), %%mm6\n" /* L1 l7 L6 L5 L4 L3 L2 L1 L0 */ - "pmullw %11, %%mm0\n" /* Cb2blue */ - "pand %12, %%mm6\n" /* L1 00 L6 00 L4 00 L2 00 L0 */ - "pmullw %13, %%mm3\n" /* Cr2green */ - "movq (%2), %%mm7\n" /* L2 */ - "pmullw %14, %%mm1\n" /* Cr2red */ - "psrlw $8, %%mm7\n" /* L2 00 L7 00 L5 00 L3 00 L1 */ - "pmullw %15, %%mm6\n" /* lum1 */ - "paddw %%mm3, %%mm2\n" /* Cb2green + Cr2green == green */ - "pmullw %15, %%mm7\n" /* lum2 */ - - "movq %%mm6, %%mm4\n" /* lum1 */ - "paddw %%mm0, %%mm6\n" /* lum1 +blue 00 B6 00 B4 00 B2 00 B0 */ - "movq %%mm4, %%mm5\n" /* lum1 */ - "paddw %%mm1, %%mm4\n" /* lum1 +red 00 R6 00 R4 00 R2 00 R0 */ - "paddw %%mm2, %%mm5\n" /* lum1 +green 00 G6 00 G4 00 G2 00 G0 */ - "psraw $6, %%mm4\n" /* R1 0 .. 64 */ - "movq %%mm7, %%mm3\n" /* lum2 00 L7 00 L5 00 L3 00 L1 */ - "psraw $6, %%mm5\n" /* G1 - .. + */ - "paddw %%mm0, %%mm7\n" /* Lum2 +blue 00 B7 00 B5 00 B3 00 B1 */ - "psraw $6, %%mm6\n" /* B1 0 .. 64 */ - "packuswb %%mm4, %%mm4\n" /* R1 R1 */ - "packuswb %%mm5, %%mm5\n" /* G1 G1 */ - "packuswb %%mm6, %%mm6\n" /* B1 B1 */ - "punpcklbw %%mm4, %%mm4\n" - "punpcklbw %%mm5, %%mm5\n" - - "pand %16, %%mm4\n" - "psllw $3, %%mm5\n" /* GREEN 1 */ - "punpcklbw %%mm6, %%mm6\n" - "pand %17, %%mm5\n" - "pand %16, %%mm6\n" - "por %%mm5, %%mm4\n" /* */ - "psrlw $11, %%mm6\n" /* BLUE 1 */ - "movq %%mm3, %%mm5\n" /* lum2 */ - "paddw %%mm1, %%mm3\n" /* lum2 +red 00 R7 00 R5 00 R3 00 R1 */ - "paddw %%mm2, %%mm5\n" /* lum2 +green 00 G7 00 G5 00 G3 00 G1 */ - "psraw $6, %%mm3\n" /* R2 */ - "por %%mm6, %%mm4\n" /* MM4 */ - "psraw $6, %%mm5\n" /* G2 */ - "movq (%2, %4), %%mm6\n" /* L3 load lum2 */ - "psraw $6, %%mm7\n" - "packuswb %%mm3, %%mm3\n" - "packuswb %%mm5, %%mm5\n" - "packuswb %%mm7, %%mm7\n" - "pand %12, %%mm6\n" /* L3 */ - "punpcklbw %%mm3, %%mm3\n" - "punpcklbw %%mm5, %%mm5\n" - "pmullw %15, %%mm6\n" /* lum3 */ - "punpcklbw %%mm7, %%mm7\n" - "psllw $3, %%mm5\n" /* GREEN 2 */ - "pand %16, %%mm7\n" - "pand %16, %%mm3\n" - "psrlw $11, %%mm7\n" /* BLUE 2 */ - "pand %17, %%mm5\n" - "por %%mm7, %%mm3\n" - "movq (%2,%4), %%mm7\n" /* L4 load lum2 */ - "por %%mm5, %%mm3\n" - "psrlw $8, %%mm7\n" /* L4 */ - "movq %%mm4, %%mm5\n" - "punpcklwd %%mm3, %%mm4\n" - "pmullw %15, %%mm7\n" /* lum4 */ - "punpckhwd %%mm3, %%mm5\n" - - "movq %%mm4, (%3)\n" /* write row1 */ - "movq %%mm5, 8(%3)\n" /* write row1 */ - - "movq %%mm6, %%mm4\n" /* Lum3 */ - "paddw %%mm0, %%mm6\n" /* Lum3 +blue */ - - "movq %%mm4, %%mm5\n" /* Lum3 */ - "paddw %%mm1, %%mm4\n" /* Lum3 +red */ - "paddw %%mm2, %%mm5\n" /* Lum3 +green */ - "psraw $6, %%mm4\n" - "movq %%mm7, %%mm3\n" /* Lum4 */ - "psraw $6, %%mm5\n" - "paddw %%mm0, %%mm7\n" /* Lum4 +blue */ - "psraw $6, %%mm6\n" /* Lum3 +blue */ - "movq %%mm3, %%mm0\n" /* Lum4 */ - "packuswb %%mm4, %%mm4\n" - "paddw %%mm1, %%mm3\n" /* Lum4 +red */ - "packuswb %%mm5, %%mm5\n" - "paddw %%mm2, %%mm0\n" /* Lum4 +green */ - "packuswb %%mm6, %%mm6\n" - "punpcklbw %%mm4, %%mm4\n" - "punpcklbw %%mm5, %%mm5\n" - "punpcklbw %%mm6, %%mm6\n" - "psllw $3, %%mm5\n" /* GREEN 3 */ - "pand %16, %%mm4\n" - "psraw $6, %%mm3\n" /* psr 6 */ - "psraw $6, %%mm0\n" - "pand %16, %%mm6\n" /* BLUE */ - "pand %17, %%mm5\n" - "psrlw $11, %%mm6\n" /* BLUE 3 */ - "por %%mm5, %%mm4\n" - "psraw $6, %%mm7\n" - "por %%mm6, %%mm4\n" - "packuswb %%mm3, %%mm3\n" - "packuswb %%mm0, %%mm0\n" - "packuswb %%mm7, %%mm7\n" - "punpcklbw %%mm3, %%mm3\n" - "punpcklbw %%mm0, %%mm0\n" - "punpcklbw %%mm7, %%mm7\n" - "pand %16, %%mm3\n" - "pand %16, %%mm7\n" /* BLUE */ - "psllw $3, %%mm0\n" /* GREEN 4 */ - "psrlw $11, %%mm7\n" - "pand %17, %%mm0\n" - "por %%mm7, %%mm3\n" - "por %%mm0, %%mm3\n" - - "movq %%mm4, %%mm5\n" - - "punpcklwd %%mm3, %%mm4\n" - "punpckhwd %%mm3, %%mm5\n" - - "movq %%mm4, (%5)\n" - "movq %%mm5, 8(%5)\n" - - "addl $8, %6\n" - "addl $8, %2\n" - "addl $4, %0\n" - "addl $4, %1\n" - "cmpl %4, %6\n" - "leal 16(%3), %3\n" - "leal 16(%5),%5\n" /* row2+16 */ - - "jl 1b\n" - "addl %4, %2\n" /* lum += cols */ - "addl %8, %3\n" /* row1+= mod */ - "addl %8, %5\n" /* row2+= mod */ - "movl $0, %6\n" /* x=0 */ - "cmpl %7, %2\n" - "jl 1b\n" - "emms\n" - : - : "r" (cr), "r"(cb),"r"(lum), - "r"(row1),"r"(cols),"r"(row2),"m"(x),"m"(y),"m"(mod), - "m"(MMX_0080w),"m"(MMX_Ugrn565),"m"(MMX_Ublu5x5), - "m"(MMX_00FFw),"m"(MMX_Vgrn565),"m"(MMX_Vred5x5), - "m"(MMX_Ycoeff),"m"(MMX_red565),"m"(MMX_grn565) - ); -} - -/* *INDENT-ON* */ - -#endif /* USE_MMX_ASSEMBLY */ - -/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/SDL_yuv_sw.c b/src/render/SDL_yuv_sw.c index 49fce6db2c6d3..0ca9c969e3443 100644 --- a/src/render/SDL_yuv_sw.c +++ b/src/render/SDL_yuv_sw.c @@ -22,1058 +22,15 @@ /* This is the software implementation of the YUV texture support */ -/* This code was derived from code carrying the following copyright notices: - - * Copyright (c) 1995 The Regents of the University of California. - * All rights reserved. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose, without fee, and without written agreement is - * hereby granted, provided that the above copyright notice and the following - * two paragraphs appear in all copies of this software. - * - * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT - * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF - * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - - * Copyright (c) 1995 Erik Corry - * All rights reserved. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose, without fee, and without written agreement is - * hereby granted, provided that the above copyright notice and the following - * two paragraphs appear in all copies of this software. - * - * IN NO EVENT SHALL ERIK CORRY BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, - * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF - * THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF ERIK CORRY HAS BEEN ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * ERIK CORRY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" - * BASIS, AND ERIK CORRY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, - * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - - * Portions of this software Copyright (c) 1995 Brown University. - * All rights reserved. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose, without fee, and without written agreement - * is hereby granted, provided that the above copyright notice and the - * following two paragraphs appear in all copies of this software. - * - * IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT - * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN - * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" - * BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, - * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - */ - #include "SDL_assert.h" -#include "SDL_video.h" -#include "SDL_cpuinfo.h" -#include "SDL_yuv_sw_c.h" -#include "SDL_yuv_mmx_c.h" - - -/* The colorspace conversion functions */ - -#ifdef USE_MMX_ASSEMBLY -extern void Color565DitherYV12MMX1X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod); -extern void ColorRGBDitherYV12MMX1X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod); -#endif - -static void -Color16DitherYV12Mod1X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned short *row1; - unsigned short *row2; - unsigned char *lum2; - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = cols / 2; - - row1 = (unsigned short *) out; - row2 = row1 + cols + mod; - lum2 = lum + cols; - - mod += cols + mod; - - y = rows / 2; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - ++cr; - ++cb; - - L = *lum++; - *row1++ = (unsigned short) (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - - L = *lum++; - *row1++ = (unsigned short) (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - - - /* Now, do second row. */ - - L = *lum2++; - *row2++ = (unsigned short) (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - - L = *lum2++; - *row2++ = (unsigned short) (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - } - - /* - * These values are at the start of the next line, (due - * to the ++'s above),but they need to be at the start - * of the line after that. - */ - lum += cols; - lum2 += cols; - row1 += mod; - row2 += mod; - } -} - -static void -Color24DitherYV12Mod1X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned int value; - unsigned char *row1; - unsigned char *row2; - unsigned char *lum2; - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = cols / 2; - - row1 = out; - row2 = row1 + cols * 3 + mod * 3; - lum2 = lum + cols; - - mod += cols + mod; - mod *= 3; - - y = rows / 2; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - ++cr; - ++cb; - - L = *lum++; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - *row1++ = (value) & 0xFF; - *row1++ = (value >> 8) & 0xFF; - *row1++ = (value >> 16) & 0xFF; - - L = *lum++; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - *row1++ = (value) & 0xFF; - *row1++ = (value >> 8) & 0xFF; - *row1++ = (value >> 16) & 0xFF; - - - /* Now, do second row. */ - - L = *lum2++; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - *row2++ = (value) & 0xFF; - *row2++ = (value >> 8) & 0xFF; - *row2++ = (value >> 16) & 0xFF; - - L = *lum2++; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - *row2++ = (value) & 0xFF; - *row2++ = (value >> 8) & 0xFF; - *row2++ = (value >> 16) & 0xFF; - } - - /* - * These values are at the start of the next line, (due - * to the ++'s above),but they need to be at the start - * of the line after that. - */ - lum += cols; - lum2 += cols; - row1 += mod; - row2 += mod; - } -} - -static void -Color32DitherYV12Mod1X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned int *row1; - unsigned int *row2; - unsigned char *lum2; - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = (cols + 1) / 2; - /* not even dimensions */ - int skip_last_col = 0; - int skip_last_row = 0; - - if ( (cols & 0x1) ) { - skip_last_col = 1; - } - - if ( (rows & 0x1) ) { - skip_last_row = 1; - } - - row1 = (unsigned int *) out; - row2 = row1 + cols + mod; - lum2 = lum + cols; - - mod += cols + mod; - - y = (rows + 1) / 2; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - ++cr; - ++cb; - - L = *lum++; - *row1++ = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - - if (!(x == 0 && skip_last_col)) { - L = *lum++; - *row1++ = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - } /* skip col */ - - - if (!(y == 0 && skip_last_row)) { - - /* Now, do second row. */ - - L = *lum2++; - *row2++ = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - - if (!(x == 1 && skip_last_col)) { - L = *lum2++; - *row2++ = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - } /* skip col */ - } /* skip row */ - } - - /* - * These values are at the start of the next line, (due - * to the ++'s above),but they need to be at the start - * of the line after that. - */ - lum += cols; - lum2 += cols; - row1 += mod; - row2 += mod; - } -} - -/* - * In this function I make use of a nasty trick. The tables have the lower - * 16 bits replicated in the upper 16. This means I can write ints and get - * the horisontal doubling for free (almost). - */ -static void -Color16DitherYV12Mod2X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned int *row1 = (unsigned int *) out; - const int next_row = cols + (mod / 2); - unsigned int *row2 = row1 + 2 * next_row; - unsigned char *lum2; - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = cols / 2; - - lum2 = lum + cols; - mod = (next_row * 3) + (mod / 2); - - y = rows / 2; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - ++cr; - ++cb; - - L = *lum++; - row1[0] = row1[next_row] = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - row1++; - - L = *lum++; - row1[0] = row1[next_row] = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - row1++; - - - /* Now, do second row. */ - - L = *lum2++; - row2[0] = row2[next_row] = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - row2++; - - L = *lum2++; - row2[0] = row2[next_row] = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - row2++; - } - - /* - * These values are at the start of the next line, (due - * to the ++'s above),but they need to be at the start - * of the line after that. - */ - lum += cols; - lum2 += cols; - row1 += mod; - row2 += mod; - } -} - -static void -Color24DitherYV12Mod2X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned int value; - unsigned char *row1 = out; - const int next_row = (cols * 2 + mod) * 3; - unsigned char *row2 = row1 + 2 * next_row; - unsigned char *lum2; - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = cols / 2; - - lum2 = lum + cols; - - mod = next_row * 3 + mod * 3; - - y = rows / 2; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - ++cr; - ++cb; - - L = *lum++; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row1[0 + 0] = row1[3 + 0] = row1[next_row + 0] = - row1[next_row + 3 + 0] = (value) & 0xFF; - row1[0 + 1] = row1[3 + 1] = row1[next_row + 1] = - row1[next_row + 3 + 1] = (value >> 8) & 0xFF; - row1[0 + 2] = row1[3 + 2] = row1[next_row + 2] = - row1[next_row + 3 + 2] = (value >> 16) & 0xFF; - row1 += 2 * 3; - - L = *lum++; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row1[0 + 0] = row1[3 + 0] = row1[next_row + 0] = - row1[next_row + 3 + 0] = (value) & 0xFF; - row1[0 + 1] = row1[3 + 1] = row1[next_row + 1] = - row1[next_row + 3 + 1] = (value >> 8) & 0xFF; - row1[0 + 2] = row1[3 + 2] = row1[next_row + 2] = - row1[next_row + 3 + 2] = (value >> 16) & 0xFF; - row1 += 2 * 3; - - - /* Now, do second row. */ - - L = *lum2++; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row2[0 + 0] = row2[3 + 0] = row2[next_row + 0] = - row2[next_row + 3 + 0] = (value) & 0xFF; - row2[0 + 1] = row2[3 + 1] = row2[next_row + 1] = - row2[next_row + 3 + 1] = (value >> 8) & 0xFF; - row2[0 + 2] = row2[3 + 2] = row2[next_row + 2] = - row2[next_row + 3 + 2] = (value >> 16) & 0xFF; - row2 += 2 * 3; - - L = *lum2++; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row2[0 + 0] = row2[3 + 0] = row2[next_row + 0] = - row2[next_row + 3 + 0] = (value) & 0xFF; - row2[0 + 1] = row2[3 + 1] = row2[next_row + 1] = - row2[next_row + 3 + 1] = (value >> 8) & 0xFF; - row2[0 + 2] = row2[3 + 2] = row2[next_row + 2] = - row2[next_row + 3 + 2] = (value >> 16) & 0xFF; - row2 += 2 * 3; - } - - /* - * These values are at the start of the next line, (due - * to the ++'s above),but they need to be at the start - * of the line after that. - */ - lum += cols; - lum2 += cols; - row1 += mod; - row2 += mod; - } -} - -static void -Color32DitherYV12Mod2X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned int *row1 = (unsigned int *) out; - const int next_row = cols * 2 + mod; - unsigned int *row2 = row1 + 2 * next_row; - unsigned char *lum2; - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = cols / 2; - - lum2 = lum + cols; - - mod = (next_row * 3) + mod; - - y = rows / 2; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - ++cr; - ++cb; - - L = *lum++; - row1[0] = row1[1] = row1[next_row] = row1[next_row + 1] = - (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row1 += 2; - - L = *lum++; - row1[0] = row1[1] = row1[next_row] = row1[next_row + 1] = - (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row1 += 2; - - - /* Now, do second row. */ - - L = *lum2++; - row2[0] = row2[1] = row2[next_row] = row2[next_row + 1] = - (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row2 += 2; - - L = *lum2++; - row2[0] = row2[1] = row2[next_row] = row2[next_row + 1] = - (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row2 += 2; - } - - /* - * These values are at the start of the next line, (due - * to the ++'s above),but they need to be at the start - * of the line after that. - */ - lum += cols; - lum2 += cols; - row1 += mod; - row2 += mod; - } -} - -static void -Color16DitherYUY2Mod1X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned short *row; - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = cols / 2; - - row = (unsigned short *) out; - - y = rows; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - cr += 4; - cb += 4; - - L = *lum; - lum += 2; - *row++ = (unsigned short) (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - - L = *lum; - lum += 2; - *row++ = (unsigned short) (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - - } - - row += mod; - } -} - -static void -Color24DitherYUY2Mod1X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned int value; - unsigned char *row; - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = cols / 2; - - row = (unsigned char *) out; - mod *= 3; - y = rows; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - cr += 4; - cb += 4; - - L = *lum; - lum += 2; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - *row++ = (value) & 0xFF; - *row++ = (value >> 8) & 0xFF; - *row++ = (value >> 16) & 0xFF; - - L = *lum; - lum += 2; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - *row++ = (value) & 0xFF; - *row++ = (value >> 8) & 0xFF; - *row++ = (value >> 16) & 0xFF; - - } - row += mod; - } -} - -static void -Color32DitherYUY2Mod1X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned int *row; - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = (cols + 1) / 2; - /* not even dimensions */ - int skip_last_col = 0; - if ( (cols & 0x1) ) { - skip_last_col = 1; - } - - row = (unsigned int *) out; - y = rows; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - cr += 4; - cb += 4; - - L = *lum; - lum += 2; - *row++ = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - - L = *lum; - lum += 2; - - if (!(x == 0 && skip_last_col)) { - *row++ = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - } /* skip col */ - - } - row += mod; - } -} - -/* - * In this function I make use of a nasty trick. The tables have the lower - * 16 bits replicated in the upper 16. This means I can write ints and get - * the horisontal doubling for free (almost). - */ -static void -Color16DitherYUY2Mod2X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned int *row = (unsigned int *) out; - const int next_row = cols + (mod / 2); - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = cols / 2; - - y = rows; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - cr += 4; - cb += 4; - - L = *lum; - lum += 2; - row[0] = row[next_row] = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - row++; - - L = *lum; - lum += 2; - row[0] = row[next_row] = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | - rgb_2_pix[L + cb_b]); - row++; - - } - row += next_row; - } -} - -static void -Color24DitherYUY2Mod2X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned int value; - unsigned char *row = out; - const int next_row = (cols * 2 + mod) * 3; - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = cols / 2; - y = rows; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - cr += 4; - cb += 4; - - L = *lum; - lum += 2; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row[0 + 0] = row[3 + 0] = row[next_row + 0] = - row[next_row + 3 + 0] = (value) & 0xFF; - row[0 + 1] = row[3 + 1] = row[next_row + 1] = - row[next_row + 3 + 1] = (value >> 8) & 0xFF; - row[0 + 2] = row[3 + 2] = row[next_row + 2] = - row[next_row + 3 + 2] = (value >> 16) & 0xFF; - row += 2 * 3; - - L = *lum; - lum += 2; - value = (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row[0 + 0] = row[3 + 0] = row[next_row + 0] = - row[next_row + 3 + 0] = (value) & 0xFF; - row[0 + 1] = row[3 + 1] = row[next_row + 1] = - row[next_row + 3 + 1] = (value >> 8) & 0xFF; - row[0 + 2] = row[3 + 2] = row[next_row + 2] = - row[next_row + 3 + 2] = (value >> 16) & 0xFF; - row += 2 * 3; - - } - row += next_row; - } -} - -static void -Color32DitherYUY2Mod2X(int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod) -{ - unsigned int *row = (unsigned int *) out; - const int next_row = cols * 2 + mod; - int x, y; - int cr_r; - int crb_g; - int cb_b; - int cols_2 = cols / 2; - mod += mod; - y = rows; - while (y--) { - x = cols_2; - while (x--) { - register int L; - - cr_r = 0 * 768 + 256 + colortab[*cr + 0 * 256]; - crb_g = 1 * 768 + 256 + colortab[*cr + 1 * 256] - + colortab[*cb + 2 * 256]; - cb_b = 2 * 768 + 256 + colortab[*cb + 3 * 256]; - cr += 4; - cb += 4; - - L = *lum; - lum += 2; - row[0] = row[1] = row[next_row] = row[next_row + 1] = - (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row += 2; - - L = *lum; - lum += 2; - row[0] = row[1] = row[next_row] = row[next_row + 1] = - (rgb_2_pix[L + cr_r] | - rgb_2_pix[L + crb_g] | rgb_2_pix[L + cb_b]); - row += 2; - - - } - - row += next_row; - } -} - -/* - * How many 1 bits are there in the Uint32. - * Low performance, do not call often. - */ -static int -number_of_bits_set(Uint32 a) -{ - if (!a) - return 0; - if (a & 1) - return 1 + number_of_bits_set(a >> 1); - return (number_of_bits_set(a >> 1)); -} - -/* - * How many 0 bits are there at least significant end of Uint32. - * Low performance, do not call often. - */ -static int -free_bits_at_bottom_nonzero(Uint32 a) -{ - SDL_assert(a != 0); - return (((Sint32) a) & 1l) ? 0 : 1 + free_bits_at_bottom_nonzero(a >> 1); -} - -static SDL_INLINE int -free_bits_at_bottom(Uint32 a) -{ - return a ? free_bits_at_bottom_nonzero(a) : 32; -} - -static int -SDL_SW_SetupYUVDisplay(SDL_SW_YUVTexture * swdata, Uint32 target_format) -{ - Uint32 *r_2_pix_alloc; - Uint32 *g_2_pix_alloc; - Uint32 *b_2_pix_alloc; - int i; - int bpp; - Uint32 Rmask, Gmask, Bmask, Amask; - int freebits; - - if (!SDL_PixelFormatEnumToMasks - (target_format, &bpp, &Rmask, &Gmask, &Bmask, &Amask) || bpp < 15) { - return SDL_SetError("Unsupported YUV destination format"); - } - - swdata->target_format = target_format; - r_2_pix_alloc = &swdata->rgb_2_pix[0 * 768]; - g_2_pix_alloc = &swdata->rgb_2_pix[1 * 768]; - b_2_pix_alloc = &swdata->rgb_2_pix[2 * 768]; - - /* - * Set up entries 0-255 in rgb-to-pixel value tables. - */ - for (i = 0; i < 256; ++i) { - r_2_pix_alloc[i + 256] = i >> (8 - number_of_bits_set(Rmask)); - freebits = free_bits_at_bottom(Rmask); - if (freebits < 32) { - r_2_pix_alloc[i + 256] <<= freebits; - } - r_2_pix_alloc[i + 256] |= Amask; - - g_2_pix_alloc[i + 256] = i >> (8 - number_of_bits_set(Gmask)); - freebits = free_bits_at_bottom(Gmask); - if (freebits < 32) { - g_2_pix_alloc[i + 256] <<= freebits; - } - g_2_pix_alloc[i + 256] |= Amask; - - b_2_pix_alloc[i + 256] = i >> (8 - number_of_bits_set(Bmask)); - freebits = free_bits_at_bottom(Bmask); - if (freebits < 32) { - b_2_pix_alloc[i + 256] <<= freebits; - } - b_2_pix_alloc[i + 256] |= Amask; - } - - /* - * If we have 16-bit output depth, then we double the value - * in the top word. This means that we can write out both - * pixels in the pixel doubling mode with one op. It is - * harmless in the normal case as storing a 32-bit value - * through a short pointer will lose the top bits anyway. - */ - if (SDL_BYTESPERPIXEL(target_format) == 2) { - for (i = 0; i < 256; ++i) { - r_2_pix_alloc[i + 256] |= (r_2_pix_alloc[i + 256]) << 16; - g_2_pix_alloc[i + 256] |= (g_2_pix_alloc[i + 256]) << 16; - b_2_pix_alloc[i + 256] |= (b_2_pix_alloc[i + 256]) << 16; - } - } - - /* - * Spread out the values we have to the rest of the array so that - * we do not need to check for overflow. - */ - for (i = 0; i < 256; ++i) { - r_2_pix_alloc[i] = r_2_pix_alloc[256]; - r_2_pix_alloc[i + 512] = r_2_pix_alloc[511]; - g_2_pix_alloc[i] = g_2_pix_alloc[256]; - g_2_pix_alloc[i + 512] = g_2_pix_alloc[511]; - b_2_pix_alloc[i] = b_2_pix_alloc[256]; - b_2_pix_alloc[i + 512] = b_2_pix_alloc[511]; - } - - /* You have chosen wisely... */ - switch (swdata->format) { - case SDL_PIXELFORMAT_YV12: - case SDL_PIXELFORMAT_IYUV: - if (SDL_BYTESPERPIXEL(target_format) == 2) { -#ifdef USE_MMX_ASSEMBLY - /* inline assembly functions */ - if (SDL_HasMMX() && (Rmask == 0xF800) && - (Gmask == 0x07E0) && (Bmask == 0x001F) - && (swdata->w & 15) == 0) { -/* printf("Using MMX 16-bit 565 dither\n"); */ - swdata->Display1X = Color565DitherYV12MMX1X; - } else { -/* printf("Using C 16-bit dither\n"); */ - swdata->Display1X = Color16DitherYV12Mod1X; - } -#else - swdata->Display1X = Color16DitherYV12Mod1X; -#endif - swdata->Display2X = Color16DitherYV12Mod2X; - } - if (SDL_BYTESPERPIXEL(target_format) == 3) { - swdata->Display1X = Color24DitherYV12Mod1X; - swdata->Display2X = Color24DitherYV12Mod2X; - } - if (SDL_BYTESPERPIXEL(target_format) == 4) { -#ifdef USE_MMX_ASSEMBLY - /* inline assembly functions */ - if (SDL_HasMMX() && (Rmask == 0x00FF0000) && - (Gmask == 0x0000FF00) && - (Bmask == 0x000000FF) && (swdata->w & 15) == 0) { -/* printf("Using MMX 32-bit dither\n"); */ - swdata->Display1X = ColorRGBDitherYV12MMX1X; - } else { -/* printf("Using C 32-bit dither\n"); */ - swdata->Display1X = Color32DitherYV12Mod1X; - } -#else - swdata->Display1X = Color32DitherYV12Mod1X; -#endif - swdata->Display2X = Color32DitherYV12Mod2X; - } - break; - case SDL_PIXELFORMAT_YUY2: - case SDL_PIXELFORMAT_UYVY: - case SDL_PIXELFORMAT_YVYU: - if (SDL_BYTESPERPIXEL(target_format) == 2) { - swdata->Display1X = Color16DitherYUY2Mod1X; - swdata->Display2X = Color16DitherYUY2Mod2X; - } - if (SDL_BYTESPERPIXEL(target_format) == 3) { - swdata->Display1X = Color24DitherYUY2Mod1X; - swdata->Display2X = Color24DitherYUY2Mod2X; - } - if (SDL_BYTESPERPIXEL(target_format) == 4) { - swdata->Display1X = Color32DitherYUY2Mod1X; - swdata->Display2X = Color32DitherYUY2Mod2X; - } - break; - case SDL_PIXELFORMAT_NV21: - case SDL_PIXELFORMAT_NV12: - /* no Display{1,2}X function */ - swdata->Display1X = NULL; - swdata->Display2X = NULL; - break; - - default: - /* We should never get here (caught above) */ - break; - } +#include "SDL_yuv_sw_c.h" - SDL_FreeSurface(swdata->display); - swdata->display = NULL; - return 0; -} SDL_SW_YUVTexture * SDL_SW_CreateYUVTexture(Uint32 format, int w, int h) { SDL_SW_YUVTexture *swdata; - int *Cr_r_tab; - int *Cr_g_tab; - int *Cb_g_tab; - int *Cb_b_tab; - int i; - int CR, CB; switch (format) { case SDL_PIXELFORMAT_YV12: @@ -1127,32 +84,14 @@ SDL_SW_CreateYUVTexture(Uint32 format, int w, int h) break; } swdata->pixels = (Uint8 *) SDL_malloc(dst_size); - } - swdata->colortab = (int *) SDL_malloc(4 * 256 * sizeof(int)); - swdata->rgb_2_pix = (Uint32 *) SDL_malloc(3 * 768 * sizeof(Uint32)); - if (!swdata->pixels || !swdata->colortab || !swdata->rgb_2_pix) { - SDL_SW_DestroyYUVTexture(swdata); - SDL_OutOfMemory(); - return NULL; - } - - /* Generate the tables for the display surface */ - Cr_r_tab = &swdata->colortab[0 * 256]; - Cr_g_tab = &swdata->colortab[1 * 256]; - Cb_g_tab = &swdata->colortab[2 * 256]; - Cb_b_tab = &swdata->colortab[3 * 256]; - for (i = 0; i < 256; i++) { - /* Gamma correction (luminescence table) and chroma correction - would be done here. See the Berkeley mpeg_play sources. - */ - CB = CR = (i - 128); - Cr_r_tab[i] = (int) ((0.419 / 0.299) * CR); - Cr_g_tab[i] = (int) (-(0.299 / 0.419) * CR); - Cb_g_tab[i] = (int) (-(0.114 / 0.331) * CB); - Cb_b_tab[i] = (int) ((0.587 / 0.331) * CB); + if (!swdata->pixels) { + SDL_SW_DestroyYUVTexture(swdata); + SDL_OutOfMemory(); + return NULL; + } } - /* Find the pitch and offset values for the overlay */ + /* Find the pitch and offset values for the texture */ switch (format) { case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: @@ -1300,8 +239,6 @@ SDL_SW_UpdateYUVTexture(SDL_SW_YUVTexture * swdata, const SDL_Rect * rect, } } } - break; - } return 0; } @@ -1398,27 +335,16 @@ SDL_SW_CopyYUVToRGB(SDL_SW_YUVTexture * swdata, const SDL_Rect * srcrect, Uint32 target_format, int w, int h, void *pixels, int pitch) { - const int targetbpp = SDL_BYTESPERPIXEL(target_format); int stretch; - int scale_2x; - Uint8 *lum, *Cr, *Cb; - int mod; - - if (targetbpp == 0) { - return SDL_SetError("Invalid target pixel format"); - } /* Make sure we're set up to display in the desired format */ - if (target_format != swdata->target_format) { - if (SDL_SW_SetupYUVDisplay(swdata, target_format) < 0) { - return -1; - } + if (target_format != swdata->target_format && swdata->display) { + SDL_FreeSurface(swdata->display); + swdata->display = NULL; } stretch = 0; - scale_2x = 0; - if (srcrect->x || srcrect->y || srcrect->w < swdata->w - || srcrect->h < swdata->h) { + if (srcrect->x || srcrect->y || srcrect->w < swdata->w || srcrect->h < swdata->h) { /* The source rectangle has been clipped. Using a scratch surface is easier than adding clipped source support to all the blitters, plus that would @@ -1426,11 +352,7 @@ SDL_SW_CopyYUVToRGB(SDL_SW_YUVTexture * swdata, const SDL_Rect * srcrect, */ stretch = 1; } else if ((srcrect->w != w) || (srcrect->h != h)) { - if ((w == 2 * srcrect->w) && (h == 2 * srcrect->h)) { - scale_2x = 1; - } else { - stretch = 1; - } + stretch = 1; } if (stretch) { int bpp; @@ -1466,51 +388,10 @@ SDL_SW_CopyYUVToRGB(SDL_SW_YUVTexture * swdata, const SDL_Rect * srcrect, pixels = swdata->stretch->pixels; pitch = swdata->stretch->pitch; } - switch (swdata->format) { - case SDL_PIXELFORMAT_YV12: - lum = swdata->planes[0]; - Cr = swdata->planes[1]; - Cb = swdata->planes[2]; - break; - case SDL_PIXELFORMAT_IYUV: - lum = swdata->planes[0]; - Cr = swdata->planes[2]; - Cb = swdata->planes[1]; - break; - case SDL_PIXELFORMAT_YUY2: - lum = swdata->planes[0]; - Cr = lum + 3; - Cb = lum + 1; - break; - case SDL_PIXELFORMAT_UYVY: - lum = swdata->planes[0] + 1; - Cr = lum + 1; - Cb = lum - 1; - break; - case SDL_PIXELFORMAT_YVYU: - lum = swdata->planes[0]; - Cr = lum + 1; - Cb = lum + 3; - break; - case SDL_PIXELFORMAT_NV12: - case SDL_PIXELFORMAT_NV21: - return SDL_ConvertPixels(swdata->w, swdata->h, - swdata->format, swdata->planes[0], swdata->pitches[0], - target_format, pixels, pitch); - break; - default: - return SDL_SetError("Unsupported YUV format in copy"); - } - mod = (pitch / targetbpp); - - if (scale_2x) { - mod -= (swdata->w * 2); - swdata->Display2X(swdata->colortab, swdata->rgb_2_pix, - lum, Cr, Cb, pixels, swdata->h, swdata->w, mod); - } else { - mod -= swdata->w; - swdata->Display1X(swdata->colortab, swdata->rgb_2_pix, - lum, Cr, Cb, pixels, swdata->h, swdata->w, mod); + if (SDL_ConvertPixels(swdata->w, swdata->h, swdata->format, + swdata->planes[0], swdata->pitches[0], + target_format, pixels, pitch) < 0) { + return -1; } if (stretch) { SDL_Rect rect = *srcrect; @@ -1524,8 +405,6 @@ SDL_SW_DestroyYUVTexture(SDL_SW_YUVTexture * swdata) { if (swdata) { SDL_free(swdata->pixels); - SDL_free(swdata->colortab); - SDL_free(swdata->rgb_2_pix); SDL_FreeSurface(swdata->stretch); SDL_FreeSurface(swdata->display); SDL_free(swdata); diff --git a/src/render/SDL_yuv_sw_c.h b/src/render/SDL_yuv_sw_c.h index 248dd45e20c26..5cd8786661e36 100644 --- a/src/render/SDL_yuv_sw_c.h +++ b/src/render/SDL_yuv_sw_c.h @@ -30,16 +30,6 @@ struct SDL_SW_YUVTexture Uint32 target_format; int w, h; Uint8 *pixels; - int *colortab; - Uint32 *rgb_2_pix; - void (*Display1X) (int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod); - void (*Display2X) (int *colortab, Uint32 * rgb_2_pix, - unsigned char *lum, unsigned char *cr, - unsigned char *cb, unsigned char *out, - int rows, int cols, int mod); /* These are just so we don't have to allocate them separately */ Uint16 pitches[3]; diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index c40f699bb8eb0..04982481eed23 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -39,85 +39,7 @@ #include #endif - -#ifdef ASSEMBLE_SHADER -#pragma comment(lib, "d3dx9.lib") - -/************************************************************************** - * ID3DXBuffer: - * ------------ - * The buffer object is used by D3DX to return arbitrary size data. - * - * GetBufferPointer - - * Returns a pointer to the beginning of the buffer. - * - * GetBufferSize - - * Returns the size of the buffer, in bytes. - **************************************************************************/ - -typedef interface ID3DXBuffer ID3DXBuffer; -typedef interface ID3DXBuffer *LPD3DXBUFFER; - -/* {8BA5FB08-5195-40e2-AC58-0D989C3A0102} */ -DEFINE_GUID(IID_ID3DXBuffer, -0x8ba5fb08, 0x5195, 0x40e2, 0xac, 0x58, 0xd, 0x98, 0x9c, 0x3a, 0x1, 0x2); - -#undef INTERFACE -#define INTERFACE ID3DXBuffer - -typedef interface ID3DXBuffer { - const struct ID3DXBufferVtbl FAR* lpVtbl; -} ID3DXBuffer; -typedef const struct ID3DXBufferVtbl ID3DXBufferVtbl; -const struct ID3DXBufferVtbl -{ - /* IUnknown */ - STDMETHOD(QueryInterface)(THIS_ REFIID iid, LPVOID *ppv) PURE; - STDMETHOD_(ULONG, AddRef)(THIS) PURE; - STDMETHOD_(ULONG, Release)(THIS) PURE; - - /* ID3DXBuffer */ - STDMETHOD_(LPVOID, GetBufferPointer)(THIS) PURE; - STDMETHOD_(DWORD, GetBufferSize)(THIS) PURE; -}; - -HRESULT WINAPI - D3DXAssembleShader( - LPCSTR pSrcData, - UINT SrcDataLen, - CONST LPVOID* pDefines, - LPVOID pInclude, - DWORD Flags, - LPD3DXBUFFER* ppShader, - LPD3DXBUFFER* ppErrorMsgs); - -static void PrintShaderData(LPDWORD shader_data, DWORD shader_size) -{ - OutputDebugStringA("const DWORD shader_data[] = {\n\t"); - { - SDL_bool newline = SDL_FALSE; - unsigned i; - for (i = 0; i < shader_size / sizeof(DWORD); ++i) { - char dword[11]; - if (i > 0) { - if ((i%6) == 0) { - newline = SDL_TRUE; - } - if (newline) { - OutputDebugStringA(",\n "); - newline = SDL_FALSE; - } else { - OutputDebugStringA(", "); - } - } - SDL_snprintf(dword, sizeof(dword), "0x%8.8x", shader_data[i]); - OutputDebugStringA(dword); - } - OutputDebugStringA("\n};\n"); - } -} - -#endif /* ASSEMBLE_SHADER */ +#include "SDL_shaders_d3d.h" /* Direct3D renderer implementation */ @@ -188,7 +110,7 @@ typedef struct IDirect3DSurface9 *defaultRenderTarget; IDirect3DSurface9 *currentRenderTarget; void* d3dxDLL; - LPDIRECT3DPIXELSHADER9 ps_yuv; + LPDIRECT3DPIXELSHADER9 shaders[NUM_SHADERS]; } D3D_RenderData; typedef struct @@ -197,6 +119,7 @@ typedef struct int w, h; DWORD usage; Uint32 format; + D3DFORMAT d3dfmt; IDirect3DTexture9 *texture; IDirect3DTexture9 *staging; } D3D_TextureRep; @@ -313,6 +236,8 @@ PixelFormatToD3DFMT(Uint32 format) return D3DFMT_A8R8G8B8; case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_NV12: + case SDL_PIXELFORMAT_NV21: return D3DFMT_L8; default: return D3DFMT_UNKNOWN; @@ -661,137 +586,19 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags) /* Set up parameters for rendering */ D3D_InitRenderState(data); - if (caps.MaxSimultaneousTextures >= 3) - { -#ifdef ASSEMBLE_SHADER - /* This shader was created by running the following HLSL through the fxc compiler - and then tuning the generated assembly. - - fxc /T fx_4_0 /O3 /Gfa /Fc yuv.fxc yuv.fx - - --- yuv.fx --- - Texture2D g_txY; - Texture2D g_txU; - Texture2D g_txV; - - SamplerState samLinear - { - Filter = ANISOTROPIC; - AddressU = Clamp; - AddressV = Clamp; - MaxAnisotropy = 1; - }; - - struct VS_OUTPUT - { - float2 TextureUV : TEXCOORD0; - }; - - struct PS_OUTPUT - { - float4 RGBAColor : SV_Target; - }; - - PS_OUTPUT YUV420( VS_OUTPUT In ) - { - const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; - const float3 Rcoeff = {1.164, 0.000, 1.596}; - const float3 Gcoeff = {1.164, -0.391, -0.813}; - const float3 Bcoeff = {1.164, 2.018, 0.000}; - - PS_OUTPUT Output; - float2 TextureUV = In.TextureUV; - - float3 yuv; - yuv.x = g_txY.Sample( samLinear, TextureUV ).r; - yuv.y = g_txU.Sample( samLinear, TextureUV ).r; - yuv.z = g_txV.Sample( samLinear, TextureUV ).r; - - yuv += offset; - Output.RGBAColor.r = dot(yuv, Rcoeff); - Output.RGBAColor.g = dot(yuv, Gcoeff); - Output.RGBAColor.b = dot(yuv, Bcoeff); - Output.RGBAColor.a = 1.0f; - - return Output; - } - - technique10 RenderYUV420 - { - pass P0 - { - SetPixelShader( CompileShader( ps_4_0_level_9_0, YUV420() ) ); - } - } - */ - const char *shader_text = - "ps_2_0\n" - "def c0, -0.0627451017, -0.501960814, -0.501960814, 1\n" - "def c1, 1.16400003, 0, 1.59599996, 0\n" - "def c2, 1.16400003, -0.391000003, -0.813000023, 0\n" - "def c3, 1.16400003, 2.01799989, 0, 0\n" - "dcl t0.xy\n" - "dcl v0.xyzw\n" - "dcl_2d s0\n" - "dcl_2d s1\n" - "dcl_2d s2\n" - "texld r0, t0, s0\n" - "texld r1, t0, s1\n" - "texld r2, t0, s2\n" - "mov r0.y, r1.x\n" - "mov r0.z, r2.x\n" - "add r0.xyz, r0, c0\n" - "dp3 r1.x, r0, c1\n" - "dp3 r1.y, r0, c2\n" - "dp2add r1.z, r0, c3, c3.z\n" /* Logically this is "dp3 r1.z, r0, c3" but the optimizer did its magic */ - "mov r1.w, c0.w\n" - "mul r0, r1, v0\n" /* Not in the HLSL, multiply by vertex color */ - "mov oC0, r0\n" - ; - LPD3DXBUFFER pCode; - LPD3DXBUFFER pErrorMsgs; - LPDWORD shader_data = NULL; - DWORD shader_size = 0; - result = D3DXAssembleShader(shader_text, SDL_strlen(shader_text), NULL, NULL, 0, &pCode, &pErrorMsgs); - if (!FAILED(result)) { - shader_data = (DWORD*)pCode->lpVtbl->GetBufferPointer(pCode); - shader_size = pCode->lpVtbl->GetBufferSize(pCode); - PrintShaderData(shader_data, shader_size); - } else { - const char *error = (const char *)pErrorMsgs->lpVtbl->GetBufferPointer(pErrorMsgs); - SDL_SetError("Couldn't assemble shader: %s", error); - } - if (shader_data != NULL) -#else - const DWORD shader_data[] = { - 0xffff0200, 0x05000051, 0xa00f0000, 0xbd808081, 0xbf008081, 0xbf008081, - 0x3f800000, 0x05000051, 0xa00f0001, 0x3f94fdf4, 0x00000000, 0x3fcc49ba, - 0x00000000, 0x05000051, 0xa00f0002, 0x3f94fdf4, 0xbec83127, 0xbf5020c5, - 0x00000000, 0x05000051, 0xa00f0003, 0x3f94fdf4, 0x400126e9, 0x00000000, - 0x00000000, 0x0200001f, 0x80000000, 0xb0030000, 0x0200001f, 0x80000000, - 0x900f0000, 0x0200001f, 0x90000000, 0xa00f0800, 0x0200001f, 0x90000000, - 0xa00f0801, 0x0200001f, 0x90000000, 0xa00f0802, 0x03000042, 0x800f0000, - 0xb0e40000, 0xa0e40800, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40801, - 0x03000042, 0x800f0002, 0xb0e40000, 0xa0e40802, 0x02000001, 0x80020000, - 0x80000001, 0x02000001, 0x80040000, 0x80000002, 0x03000002, 0x80070000, - 0x80e40000, 0xa0e40000, 0x03000008, 0x80010001, 0x80e40000, 0xa0e40001, - 0x03000008, 0x80020001, 0x80e40000, 0xa0e40002, 0x0400005a, 0x80040001, - 0x80e40000, 0xa0e40003, 0xa0aa0003, 0x02000001, 0x80080001, 0xa0ff0000, - 0x03000005, 0x800f0000, 0x80e40001, 0x90e40000, 0x02000001, 0x800f0800, - 0x80e40000, 0x0000ffff - }; -#endif - { - result = IDirect3DDevice9_CreatePixelShader(data->device, shader_data, &data->ps_yuv); - if (!FAILED(result)) { - renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12; - renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV; - } else { + if (caps.MaxSimultaneousTextures >= 3) { + int i; + for (i = 0; i < SDL_arraysize(data->shaders); ++i) { + result = D3D9_CreatePixelShader(data->device, (D3D9_Shader)i, &data->shaders[i]); + if (FAILED(result)) { D3D_SetError("CreatePixelShader()", result); } } + if (data->shaders[SHADER_YUV_JPEG] && data->shaders[SHADER_YUV_BT601] && data->shaders[SHADER_YUV_BT709]) { + renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12; + renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV; + } } - return renderer; } @@ -870,7 +677,7 @@ GetScaleQuality(void) } static int -D3D_CreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, DWORD usage, Uint32 format, int w, int h) +D3D_CreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, DWORD usage, Uint32 format, D3DFORMAT d3dfmt, int w, int h) { HRESULT result; @@ -879,6 +686,7 @@ D3D_CreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, DWORD us texture->h = h; texture->usage = usage; texture->format = format; + texture->d3dfmt = d3dfmt; result = IDirect3DDevice9_CreateTexture(device, w, h, 1, usage, PixelFormatToD3DFMT(format), @@ -897,8 +705,7 @@ D3D_CreateStagingTexture(IDirect3DDevice9 *device, D3D_TextureRep *texture) if (texture->staging == NULL) { result = IDirect3DDevice9_CreateTexture(device, texture->w, texture->h, 1, 0, - PixelFormatToD3DFMT(texture->format), - D3DPOOL_SYSTEMMEM, &texture->staging, NULL); + texture->d3dfmt, D3DPOOL_SYSTEMMEM, &texture->staging, NULL); if (FAILED(result)) { return D3D_SetError("CreateTexture(D3DPOOL_SYSTEMMEM)", result); } @@ -934,7 +741,7 @@ D3D_BindTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, DWORD samp } static int -D3D_RecreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, Uint32 format, int w, int h) +D3D_RecreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture) { if (texture->texture) { IDirect3DTexture9_Release(texture->texture); @@ -948,7 +755,7 @@ D3D_RecreateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, Uint32 } static int -D3D_UpdateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, Uint32 format, int x, int y, int w, int h, const void *pixels, int pitch) +D3D_UpdateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, int x, int y, int w, int h, const void *pixels, int pitch) { RECT d3drect; D3DLOCKED_RECT locked; @@ -972,8 +779,8 @@ D3D_UpdateTextureRep(IDirect3DDevice9 *device, D3D_TextureRep *texture, Uint32 f } src = (const Uint8 *)pixels; - dst = locked.pBits; - length = w * SDL_BYTESPERPIXEL(format); + dst = (Uint8 *)locked.pBits; + length = w * SDL_BYTESPERPIXEL(texture->format); if (length == pitch && length == locked.Pitch) { SDL_memcpy(dst, src, length*h); } else { @@ -1032,7 +839,7 @@ D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) usage = 0; } - if (D3D_CreateTextureRep(data->device, &texturedata->texture, usage, texture->format, texture->w, texture->h) < 0) { + if (D3D_CreateTextureRep(data->device, &texturedata->texture, usage, texture->format, PixelFormatToD3DFMT(texture->format), texture->w, texture->h) < 0) { return -1; } @@ -1040,11 +847,11 @@ D3D_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) texture->format == SDL_PIXELFORMAT_IYUV) { texturedata->yuv = SDL_TRUE; - if (D3D_CreateTextureRep(data->device, &texturedata->utexture, usage, texture->format, texture->w / 2, texture->h / 2) < 0) { + if (D3D_CreateTextureRep(data->device, &texturedata->utexture, usage, texture->format, PixelFormatToD3DFMT(texture->format), (texture->w + 1) / 2, (texture->h + 1) / 2) < 0) { return -1; } - if (D3D_CreateTextureRep(data->device, &texturedata->vtexture, usage, texture->format, texture->w / 2, texture->h / 2) < 0) { + if (D3D_CreateTextureRep(data->device, &texturedata->vtexture, usage, texture->format, PixelFormatToD3DFMT(texture->format), (texture->w + 1) / 2, (texture->h + 1) / 2) < 0) { return -1; } } @@ -1061,16 +868,16 @@ D3D_RecreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) return 0; } - if (D3D_RecreateTextureRep(data->device, &texturedata->texture, texture->format, texture->w, texture->h) < 0) { + if (D3D_RecreateTextureRep(data->device, &texturedata->texture) < 0) { return -1; } if (texturedata->yuv) { - if (D3D_RecreateTextureRep(data->device, &texturedata->utexture, texture->format, texture->w / 2, texture->h / 2) < 0) { + if (D3D_RecreateTextureRep(data->device, &texturedata->utexture) < 0) { return -1; } - if (D3D_RecreateTextureRep(data->device, &texturedata->vtexture, texture->format, texture->w / 2, texture->h / 2) < 0) { + if (D3D_RecreateTextureRep(data->device, &texturedata->vtexture) < 0) { return -1; } } @@ -1089,7 +896,7 @@ D3D_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, return -1; } - if (D3D_UpdateTextureRep(data->device, &texturedata->texture, texture->format, rect->x, rect->y, rect->w, rect->h, pixels, pitch) < 0) { + if (D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, pixels, pitch) < 0) { return -1; } @@ -1097,13 +904,13 @@ D3D_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, /* Skip to the correct offset into the next texture */ pixels = (const void*)((const Uint8*)pixels + rect->h * pitch); - if (D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, texture->format, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, pixels, pitch / 2) < 0) { + if (D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2) < 0) { return -1; } /* Skip to the correct offset into the next texture */ - pixels = (const void*)((const Uint8*)pixels + (rect->h * pitch)/4); - if (D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, texture->format, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, pixels, pitch / 2) < 0) { + pixels = (const void*)((const Uint8*)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2)); + if (D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, rect->x / 2, (rect->y + 1) / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2) < 0) { return -1; } } @@ -1125,13 +932,13 @@ D3D_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, return -1; } - if (D3D_UpdateTextureRep(data->device, &texturedata->texture, texture->format, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch) < 0) { + if (D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch) < 0) { return -1; } - if (D3D_UpdateTextureRep(data->device, &texturedata->utexture, texture->format, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Uplane, Upitch) < 0) { + if (D3D_UpdateTextureRep(data->device, &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Uplane, Upitch) < 0) { return -1; } - if (D3D_UpdateTextureRep(data->device, &texturedata->vtexture, texture->format, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Vplane, Vpitch) < 0) { + if (D3D_UpdateTextureRep(data->device, &texturedata->vtexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, Vplane, Vpitch) < 0) { return -1; } return 0; @@ -1609,13 +1416,60 @@ D3D_UpdateTextureScaleMode(D3D_RenderData *data, D3D_TextureData *texturedata, u } } +static int +D3D_RenderSetupTextureState(SDL_Renderer * renderer, SDL_Texture * texture, LPDIRECT3DPIXELSHADER9 *shader) +{ + D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; + D3D_TextureData *texturedata; + + *shader = NULL; + + texturedata = (D3D_TextureData *)texture->driverdata; + if (!texturedata) { + SDL_SetError("Texture is not currently available"); + return -1; + } + + D3D_UpdateTextureScaleMode(data, texturedata, 0); + + if (D3D_BindTextureRep(data->device, &texturedata->texture, 0) < 0) { + return -1; + } + + if (texturedata->yuv) { + switch (SDL_GetYUVConversionModeForResolution(texture->w, texture->h)) { + case SDL_YUV_CONVERSION_JPEG: + *shader = data->shaders[SHADER_YUV_JPEG]; + break; + case SDL_YUV_CONVERSION_BT601: + *shader = data->shaders[SHADER_YUV_BT601]; + break; + case SDL_YUV_CONVERSION_BT709: + *shader = data->shaders[SHADER_YUV_BT709]; + break; + default: + return SDL_SetError("Unsupported YUV conversion mode"); + } + + D3D_UpdateTextureScaleMode(data, texturedata, 1); + D3D_UpdateTextureScaleMode(data, texturedata, 2); + + if (D3D_BindTextureRep(data->device, &texturedata->utexture, 1) < 0) { + return -1; + } + if (D3D_BindTextureRep(data->device, &texturedata->vtexture, 2) < 0) { + return -1; + } + } + return 0; +} + static int D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_FRect * dstrect) { D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - D3D_TextureData *texturedata; - LPDIRECT3DPIXELSHADER9 shader = NULL; + LPDIRECT3DPIXELSHADER9 shader; float minx, miny, maxx, maxy; float minu, maxu, minv, maxv; DWORD color; @@ -1626,12 +1480,6 @@ D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, return -1; } - texturedata = (D3D_TextureData *)texture->driverdata; - if (!texturedata) { - SDL_SetError("Texture is not currently available"); - return -1; - } - minx = dstrect->x - 0.5f; miny = dstrect->y - 0.5f; maxx = dstrect->x + dstrect->w - 0.5f; @@ -1674,45 +1522,25 @@ D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, D3D_SetBlendMode(data, texture->blendMode); - D3D_UpdateTextureScaleMode(data, texturedata, 0); - - if (D3D_BindTextureRep(data->device, &texturedata->texture, 0) < 0) { + if (D3D_RenderSetupTextureState(renderer, texture, &shader) < 0) { return -1; } - - if (texturedata->yuv) { - shader = data->ps_yuv; - - D3D_UpdateTextureScaleMode(data, texturedata, 1); - D3D_UpdateTextureScaleMode(data, texturedata, 2); - - if (D3D_BindTextureRep(data->device, &texturedata->utexture, 1) < 0) { - return -1; - } - if (D3D_BindTextureRep(data->device, &texturedata->vtexture, 2) < 0) { - return -1; - } - } - + if (shader) { result = IDirect3DDevice9_SetPixelShader(data->device, shader); if (FAILED(result)) { return D3D_SetError("SetShader()", result); } } - result = - IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, - vertices, sizeof(*vertices)); + result = IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, + vertices, sizeof(*vertices)); if (FAILED(result)) { - return D3D_SetError("DrawPrimitiveUP()", result); + D3D_SetError("DrawPrimitiveUP()", result); } if (shader) { - result = IDirect3DDevice9_SetPixelShader(data->device, NULL); - if (FAILED(result)) { - return D3D_SetError("SetShader()", result); - } + IDirect3DDevice9_SetPixelShader(data->device, NULL); } - return 0; + return FAILED(result) ? -1 : 0; } @@ -1722,7 +1550,6 @@ D3D_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip) { D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; - D3D_TextureData *texturedata; LPDIRECT3DPIXELSHADER9 shader = NULL; float minx, miny, maxx, maxy; float minu, maxu, minv, maxv; @@ -1736,12 +1563,6 @@ D3D_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, return -1; } - texturedata = (D3D_TextureData *)texture->driverdata; - if (!texturedata) { - SDL_SetError("Texture is not currently available"); - return -1; - } - centerx = center->x; centery = center->y; @@ -1798,54 +1619,37 @@ D3D_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, D3D_SetBlendMode(data, texture->blendMode); + if (D3D_RenderSetupTextureState(renderer, texture, &shader) < 0) { + return -1; + } + /* Rotate and translate */ modelMatrix = MatrixMultiply( MatrixRotationZ((float)(M_PI * (float) angle / 180.0f)), MatrixTranslation(dstrect->x + center->x - 0.5f, dstrect->y + center->y - 0.5f, 0)); IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*)&modelMatrix); - - D3D_UpdateTextureScaleMode(data, texturedata, 0); - - if (D3D_BindTextureRep(data->device, &texturedata->texture, 0) < 0) { - return -1; - } - - if (texturedata->yuv) { - shader = data->ps_yuv; - - D3D_UpdateTextureScaleMode(data, texturedata, 1); - D3D_UpdateTextureScaleMode(data, texturedata, 2); - - if (D3D_BindTextureRep(data->device, &texturedata->utexture, 1) < 0) { - return -1; - } - if (D3D_BindTextureRep(data->device, &texturedata->vtexture, 2) < 0) { - return -1; - } - } - + if (shader) { result = IDirect3DDevice9_SetPixelShader(data->device, shader); if (FAILED(result)) { - return D3D_SetError("SetShader()", result); + D3D_SetError("SetShader()", result); + goto done; } } - result = - IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, - vertices, sizeof(*vertices)); + result = IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2, + vertices, sizeof(*vertices)); if (FAILED(result)) { - return D3D_SetError("DrawPrimitiveUP()", result); + D3D_SetError("DrawPrimitiveUP()", result); } +done: if (shader) { - result = IDirect3DDevice9_SetPixelShader(data->device, NULL); - if (FAILED(result)) { - return D3D_SetError("SetShader()", result); - } + IDirect3DDevice9_SetPixelShader(data->device, NULL); } modelMatrix = MatrixIdentity(); IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*)&modelMatrix); - return 0; + + return FAILED(result) ? -1 : 0; } static int @@ -1955,6 +1759,8 @@ D3D_DestroyRenderer(SDL_Renderer * renderer) D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; if (data) { + int i; + /* Release the render target */ if (data->defaultRenderTarget) { IDirect3DSurface9_Release(data->defaultRenderTarget); @@ -1964,11 +1770,15 @@ D3D_DestroyRenderer(SDL_Renderer * renderer) IDirect3DSurface9_Release(data->currentRenderTarget); data->currentRenderTarget = NULL; } - if (data->ps_yuv) { - IDirect3DPixelShader9_Release(data->ps_yuv); + for (i = 0; i < SDL_arraysize(data->shaders); ++i) { + if (data->shaders[i]) { + IDirect3DPixelShader9_Release(data->shaders[i]); + data->shaders[i] = NULL; + } } if (data->device) { IDirect3DDevice9_Release(data->device); + data->device = NULL; } if (data->d3d) { IDirect3D9_Release(data->d3d); diff --git a/src/render/direct3d/SDL_shaders_d3d.c b/src/render/direct3d/SDL_shaders_d3d.c new file mode 100644 index 0000000000000..37a0dd7d0e062 --- /dev/null +++ b/src/render/direct3d/SDL_shaders_d3d.c @@ -0,0 +1,274 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2017 Sam Lantinga + + 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, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "SDL_render.h" +#include "SDL_system.h" + +#if SDL_VIDEO_RENDER_D3D && !SDL_RENDER_DISABLED + +#include "../../core/windows/SDL_windows.h" + +#include + +#include "SDL_shaders_d3d.h" + +/* The shaders here were compiled with: + + fxc /T ps_2_0 /Fo"" "" + + Shader object code was converted to a list of DWORDs via the following + *nix style command (available separately from Windows + MSVC): + + hexdump -v -e '6/4 "0x%08.8x, " "\n"' +*/ + +/* --- D3D9_PixelShader_YUV_JPEG.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureU : register(t1); + Texture2D theTextureV : register(t2); + SamplerState theSampler = sampler_state + { + addressU = Clamp; + addressV = Clamp; + mipfilter = NONE; + minfilter = LINEAR; + magfilter = LINEAR; + }; + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {0.0, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.0000, 0.0000, 1.4020}; + const float3 Gcoeff = {1.0000, -0.3441, -0.7141}; + const float3 Bcoeff = {1.0000, 1.7720, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.y = theTextureU.Sample(theSampler, input.tex).r; + yuv.z = theTextureV.Sample(theSampler, input.tex).r; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } +*/ +static const DWORD D3D9_PixelShader_YUV_JPEG[] = { + 0xffff0200, 0x0044fffe, 0x42415443, 0x0000001c, 0x000000d7, 0xffff0200, + 0x00000003, 0x0000001c, 0x00000100, 0x000000d0, 0x00000058, 0x00010003, + 0x00000001, 0x00000070, 0x00000000, 0x00000080, 0x00020003, 0x00000001, + 0x00000098, 0x00000000, 0x000000a8, 0x00000003, 0x00000001, 0x000000c0, + 0x00000000, 0x53656874, 0x6c706d61, 0x742b7265, 0x65546568, 0x72757478, + 0xab005565, 0x00070004, 0x00040001, 0x00000001, 0x00000000, 0x53656874, + 0x6c706d61, 0x742b7265, 0x65546568, 0x72757478, 0xab005665, 0x00070004, + 0x00040001, 0x00000001, 0x00000000, 0x53656874, 0x6c706d61, 0x742b7265, + 0x65546568, 0x72757478, 0xab005965, 0x00070004, 0x00040001, 0x00000001, + 0x00000000, 0x325f7370, 0x4d00305f, 0x6f726369, 0x74666f73, 0x29522820, + 0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320, 0x656c6970, 0x2e362072, + 0x36392e33, 0x312e3030, 0x34383336, 0xababab00, 0x05000051, 0xa00f0000, + 0x00000000, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001, + 0x3f800000, 0x00000000, 0x3fb374bc, 0x00000000, 0x05000051, 0xa00f0002, + 0x3f800000, 0xbeb02de0, 0xbf36cf42, 0x00000000, 0x05000051, 0xa00f0003, + 0x3f800000, 0x3fe2d0e5, 0x00000000, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0x900f0000, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, 0x90000000, + 0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, 0x03000042, + 0x800f0001, 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0002, 0xb0e40000, + 0xa0e40802, 0x02000001, 0x80020000, 0x80000001, 0x02000001, 0x80040000, + 0x80000002, 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, 0x03000008, + 0x80010001, 0x80e40000, 0xa0e40001, 0x03000008, 0x80020001, 0x80e40000, + 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003, + 0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001, + 0x90e40000, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff +}; + +/* --- D3D9_PixelShader_YUV_BT601.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureU : register(t1); + Texture2D theTextureV : register(t2); + SamplerState theSampler = sampler_state + { + addressU = Clamp; + addressV = Clamp; + mipfilter = NONE; + minfilter = LINEAR; + magfilter = LINEAR; + }; + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.1644, 0.0000, 1.5960}; + const float3 Gcoeff = {1.1644, -0.3918, -0.8130}; + const float3 Bcoeff = {1.1644, 2.0172, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.y = theTextureU.Sample(theSampler, input.tex).r; + yuv.z = theTextureV.Sample(theSampler, input.tex).r; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } +*/ +static const DWORD D3D9_PixelShader_YUV_BT601[] = { + 0xffff0200, 0x0044fffe, 0x42415443, 0x0000001c, 0x000000d7, 0xffff0200, + 0x00000003, 0x0000001c, 0x00000100, 0x000000d0, 0x00000058, 0x00010003, + 0x00000001, 0x00000070, 0x00000000, 0x00000080, 0x00020003, 0x00000001, + 0x00000098, 0x00000000, 0x000000a8, 0x00000003, 0x00000001, 0x000000c0, + 0x00000000, 0x53656874, 0x6c706d61, 0x742b7265, 0x65546568, 0x72757478, + 0xab005565, 0x00070004, 0x00040001, 0x00000001, 0x00000000, 0x53656874, + 0x6c706d61, 0x742b7265, 0x65546568, 0x72757478, 0xab005665, 0x00070004, + 0x00040001, 0x00000001, 0x00000000, 0x53656874, 0x6c706d61, 0x742b7265, + 0x65546568, 0x72757478, 0xab005965, 0x00070004, 0x00040001, 0x00000001, + 0x00000000, 0x325f7370, 0x4d00305f, 0x6f726369, 0x74666f73, 0x29522820, + 0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320, 0x656c6970, 0x2e362072, + 0x36392e33, 0x312e3030, 0x34383336, 0xababab00, 0x05000051, 0xa00f0000, + 0xbd808081, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001, + 0x3f950b0f, 0x00000000, 0x3fcc49ba, 0x00000000, 0x05000051, 0xa00f0002, + 0x3f950b0f, 0xbec89a02, 0xbf5020c5, 0x00000000, 0x05000051, 0xa00f0003, + 0x3f950b0f, 0x400119ce, 0x00000000, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0x900f0000, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, 0x90000000, + 0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, 0x03000042, + 0x800f0001, 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0002, 0xb0e40000, + 0xa0e40802, 0x02000001, 0x80020000, 0x80000001, 0x02000001, 0x80040000, + 0x80000002, 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, 0x03000008, + 0x80010001, 0x80e40000, 0xa0e40001, 0x03000008, 0x80020001, 0x80e40000, + 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003, + 0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001, + 0x90e40000, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff +}; + +/* --- D3D9_PixelShader_YUV_BT709.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureU : register(t1); + Texture2D theTextureV : register(t2); + SamplerState theSampler = sampler_state + { + addressU = Clamp; + addressV = Clamp; + mipfilter = NONE; + minfilter = LINEAR; + magfilter = LINEAR; + }; + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.1644, 0.0000, 1.7927}; + const float3 Gcoeff = {1.1644, -0.2132, -0.5329}; + const float3 Bcoeff = {1.1644, 2.1124, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.y = theTextureU.Sample(theSampler, input.tex).r; + yuv.z = theTextureV.Sample(theSampler, input.tex).r; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } +*/ +static const DWORD D3D9_PixelShader_YUV_BT709[] = { + 0xffff0200, 0x0044fffe, 0x42415443, 0x0000001c, 0x000000d7, 0xffff0200, + 0x00000003, 0x0000001c, 0x00000100, 0x000000d0, 0x00000058, 0x00010003, + 0x00000001, 0x00000070, 0x00000000, 0x00000080, 0x00020003, 0x00000001, + 0x00000098, 0x00000000, 0x000000a8, 0x00000003, 0x00000001, 0x000000c0, + 0x00000000, 0x53656874, 0x6c706d61, 0x742b7265, 0x65546568, 0x72757478, + 0xab005565, 0x00070004, 0x00040001, 0x00000001, 0x00000000, 0x53656874, + 0x6c706d61, 0x742b7265, 0x65546568, 0x72757478, 0xab005665, 0x00070004, + 0x00040001, 0x00000001, 0x00000000, 0x53656874, 0x6c706d61, 0x742b7265, + 0x65546568, 0x72757478, 0xab005965, 0x00070004, 0x00040001, 0x00000001, + 0x00000000, 0x325f7370, 0x4d00305f, 0x6f726369, 0x74666f73, 0x29522820, + 0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320, 0x656c6970, 0x2e362072, + 0x36392e33, 0x312e3030, 0x34383336, 0xababab00, 0x05000051, 0xa00f0000, + 0xbd808081, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001, + 0x3f950b0f, 0x00000000, 0x3fe57732, 0x00000000, 0x05000051, 0xa00f0002, + 0x3f950b0f, 0xbe5a511a, 0xbf086c22, 0x00000000, 0x05000051, 0xa00f0003, + 0x3f950b0f, 0x40073190, 0x00000000, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0x900f0000, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, 0x90000000, + 0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, 0x03000042, + 0x800f0001, 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0002, 0xb0e40000, + 0xa0e40802, 0x02000001, 0x80020000, 0x80000001, 0x02000001, 0x80040000, + 0x80000002, 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, 0x03000008, + 0x80010001, 0x80e40000, 0xa0e40001, 0x03000008, 0x80020001, 0x80e40000, + 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003, + 0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001, + 0x90e40000, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff +}; + + +static const DWORD *D3D9_shaders[] = { + D3D9_PixelShader_YUV_JPEG, + D3D9_PixelShader_YUV_BT601, + D3D9_PixelShader_YUV_BT709, +}; + +HRESULT D3D9_CreatePixelShader(IDirect3DDevice9 *d3dDevice, D3D9_Shader shader, IDirect3DPixelShader9 **pixelShader) +{ + return IDirect3DDevice9_CreatePixelShader(d3dDevice, D3D9_shaders[shader], pixelShader); +} + +#endif /* SDL_VIDEO_RENDER_D3D && !SDL_RENDER_DISABLED */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/SDL_yuv_mmx_c.h b/src/render/direct3d/SDL_shaders_d3d.h similarity index 76% rename from src/render/SDL_yuv_mmx_c.h rename to src/render/direct3d/SDL_shaders_d3d.h index d175ebf28ff6d..bf94a50b24ee5 100644 --- a/src/render/SDL_yuv_mmx_c.h +++ b/src/render/direct3d/SDL_shaders_d3d.h @@ -18,11 +18,17 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ -#include "../SDL_internal.h" +#include "../../SDL_internal.h" -/* FIXME: This breaks on various versions of GCC and should be rewritten using intrinsics */ -#if 0 /* (__GNUC__ > 2) && defined(__i386__) && __OPTIMIZE__ && SDL_ASSEMBLY_ROUTINES && !defined(__clang__) */ -#define USE_MMX_ASSEMBLY 1 -#endif +/* D3D9 shader implementation */ + +typedef enum { + SHADER_YUV_JPEG, + SHADER_YUV_BT601, + SHADER_YUV_BT709, + NUM_SHADERS +} D3D9_Shader; + +extern HRESULT D3D9_CreatePixelShader(IDirect3DDevice9 *d3dDevice, D3D9_Shader shader, IDirect3DPixelShader9 **pixelShader); /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c old mode 100644 new mode 100755 index 73cf68013ec71..3f51e5bfd83a2 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -29,10 +29,10 @@ #include "SDL_syswm.h" #include "../SDL_sysrender.h" #include "../SDL_d3dmath.h" -/* #include "SDL_log.h" */ #include +#include "SDL_shaders_d3d11.h" #ifdef __WINRT__ @@ -88,6 +88,12 @@ typedef struct ID3D11ShaderResourceView *mainTextureResourceViewU; ID3D11Texture2D *mainTextureV; ID3D11ShaderResourceView *mainTextureResourceViewV; + + /* NV12 texture support */ + SDL_bool nv12; + ID3D11Texture2D *mainTextureNV; + ID3D11ShaderResourceView *mainTextureResourceViewNV; + Uint8 *pixels; int pitch; SDL_Rect locked_rect; @@ -116,9 +122,7 @@ typedef struct ID3D11InputLayout *inputLayout; ID3D11Buffer *vertexBuffer; ID3D11VertexShader *vertexShader; - ID3D11PixelShader *colorPixelShader; - ID3D11PixelShader *texturePixelShader; - ID3D11PixelShader *yuvPixelShader; + ID3D11PixelShader *pixelShaders[NUM_SHADERS]; int blendModesCount; D3D11_BlendMode *blendModes; ID3D11SamplerState *nearestPixelSampler; @@ -170,553 +174,6 @@ static const GUID SDL_IID_ID3D11Debug = { 0x79cf2233, 0x7536, 0x4948, { 0x9d, 0x #pragma GCC diagnostic pop #endif -/* Direct3D 11.x shaders - - SDL's shaders are compiled into SDL itself, to simplify distribution. - - All Direct3D 11.x shaders were compiled with the following: - - fxc /E"main" /T "" /Fo"" "" - - Variables: - - : the type of shader. A table of utilized shader types is - listed below. - - : where to store compiled output - - : where to read shader source code from - - Shader types: - - ps_4_0_level_9_1: Pixel shader for Windows 8+, including Windows RT - - vs_4_0_level_9_1: Vertex shader for Windows 8+, including Windows RT - - ps_4_0_level_9_3: Pixel shader for Windows Phone 8 - - vs_4_0_level_9_3: Vertex shader for Windows Phone 8 - - - Shader object code was converted to a list of DWORDs via the following - *nix style command (available separately from Windows + MSVC): - - hexdump -v -e '6/4 "0x%08.8x, " "\n"' - */ -#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP -#define D3D11_USE_SHADER_MODEL_4_0_level_9_3 -#else -#define D3D11_USE_SHADER_MODEL_4_0_level_9_1 -#endif - -/* The color-only-rendering pixel shader: - - --- D3D11_PixelShader_Colors.hlsl --- - struct PixelShaderInput - { - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; - }; - - float4 main(PixelShaderInput input) : SV_TARGET - { - return input.color; - } -*/ -#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) -static const DWORD D3D11_PixelShader_Colors[] = { - 0x43425844, 0xd74c28fe, 0xa1eb8804, 0x269d512a, 0x7699723d, 0x00000001, - 0x00000240, 0x00000006, 0x00000038, 0x00000084, 0x000000c4, 0x00000140, - 0x00000198, 0x0000020c, 0x396e6f41, 0x00000044, 0x00000044, 0xffff0200, - 0x00000020, 0x00000024, 0x00240000, 0x00240000, 0x00240000, 0x00240000, - 0x00240000, 0xffff0200, 0x0200001f, 0x80000000, 0xb00f0001, 0x02000001, - 0x800f0800, 0xb0e40001, 0x0000ffff, 0x52444853, 0x00000038, 0x00000040, - 0x0000000e, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, 0x001020f2, - 0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000002, - 0x0100003e, 0x54415453, 0x00000074, 0x00000002, 0x00000000, 0x00000000, - 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x46454452, 0x00000050, 0x00000000, 0x00000000, - 0x00000000, 0x0000001c, 0xffff0400, 0x00000100, 0x0000001c, 0x7263694d, - 0x666f736f, 0x52282074, 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, - 0x6c69706d, 0x39207265, 0x2e30332e, 0x30303239, 0x3336312e, 0xab003438, - 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, - 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, - 0x00000000, 0x00000003, 0x00000001, 0x00000003, 0x00000065, 0x00000000, - 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, - 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, - 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, - 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 -}; -#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) -static const DWORD D3D11_PixelShader_Colors[] = { - 0x43425844, 0x93f6ccfc, 0x5f919270, 0x7a11aa4f, 0x9148e931, 0x00000001, - 0x00000240, 0x00000006, 0x00000038, 0x00000084, 0x000000c4, 0x00000140, - 0x00000198, 0x0000020c, 0x396e6f41, 0x00000044, 0x00000044, 0xffff0200, - 0x00000020, 0x00000024, 0x00240000, 0x00240000, 0x00240000, 0x00240000, - 0x00240000, 0xffff0201, 0x0200001f, 0x80000000, 0xb00f0001, 0x02000001, - 0x800f0800, 0xb0e40001, 0x0000ffff, 0x52444853, 0x00000038, 0x00000040, - 0x0000000e, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, 0x001020f2, - 0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000002, - 0x0100003e, 0x54415453, 0x00000074, 0x00000002, 0x00000000, 0x00000000, - 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x46454452, 0x00000050, 0x00000000, 0x00000000, - 0x00000000, 0x0000001c, 0xffff0400, 0x00000100, 0x0000001c, 0x7263694d, - 0x666f736f, 0x52282074, 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, - 0x6c69706d, 0x39207265, 0x2e30332e, 0x30303239, 0x3336312e, 0xab003438, - 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, - 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, - 0x00000000, 0x00000003, 0x00000001, 0x00000003, 0x00000065, 0x00000000, - 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, - 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, - 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, - 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 -}; -#else -#error "An appropriate 'colors' pixel shader is not defined." -#endif - -/* The texture-rendering pixel shader: - - --- D3D11_PixelShader_Textures.hlsl --- - Texture2D theTexture : register(t0); - SamplerState theSampler : register(s0); - - struct PixelShaderInput - { - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; - }; - - float4 main(PixelShaderInput input) : SV_TARGET - { - return theTexture.Sample(theSampler, input.tex) * input.color; - } -*/ -#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) -static const DWORD D3D11_PixelShader_Textures[] = { - 0x43425844, 0x6299b59f, 0x155258f2, 0x873ab86a, 0xfcbb6dcd, 0x00000001, - 0x00000330, 0x00000006, 0x00000038, 0x000000c0, 0x0000015c, 0x000001d8, - 0x00000288, 0x000002fc, 0x396e6f41, 0x00000080, 0x00000080, 0xffff0200, - 0x00000058, 0x00000028, 0x00280000, 0x00280000, 0x00280000, 0x00240001, - 0x00280000, 0x00000000, 0xffff0200, 0x0200001f, 0x80000000, 0xb0030000, - 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, 0xa00f0800, - 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, 0x03000005, 0x800f0000, - 0x80e40000, 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff, - 0x52444853, 0x00000094, 0x00000040, 0x00000025, 0x0300005a, 0x00106000, - 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x03001062, - 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, - 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x09000045, 0x001000f2, - 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, 0x00106000, - 0x00000000, 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, - 0x00101e46, 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x00000003, - 0x00000001, 0x00000000, 0x00000003, 0x00000001, 0x00000000, 0x00000000, - 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000a8, - 0x00000000, 0x00000000, 0x00000002, 0x0000001c, 0xffff0400, 0x00000100, - 0x00000072, 0x0000005c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000001, 0x00000001, 0x00000067, 0x00000002, 0x00000005, - 0x00000004, 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x53656874, - 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x694d0065, 0x736f7263, - 0x2074666f, 0x20295228, 0x4c534c48, 0x61685320, 0x20726564, 0x706d6f43, - 0x72656c69, 0x332e3920, 0x32392e30, 0x312e3030, 0x34383336, 0xababab00, - 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, - 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, - 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000065, 0x00000000, - 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, - 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, - 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, - 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 -}; -#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) -static const DWORD D3D11_PixelShader_Textures[] = { - 0x43425844, 0x5876569a, 0x01b6c87e, 0x8447454f, 0xc7f3ef10, 0x00000001, - 0x00000330, 0x00000006, 0x00000038, 0x000000c0, 0x0000015c, 0x000001d8, - 0x00000288, 0x000002fc, 0x396e6f41, 0x00000080, 0x00000080, 0xffff0200, - 0x00000058, 0x00000028, 0x00280000, 0x00280000, 0x00280000, 0x00240001, - 0x00280000, 0x00000000, 0xffff0201, 0x0200001f, 0x80000000, 0xb0030000, - 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, 0xa00f0800, - 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, 0x03000005, 0x800f0000, - 0x80e40000, 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff, - 0x52444853, 0x00000094, 0x00000040, 0x00000025, 0x0300005a, 0x00106000, - 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x03001062, - 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, - 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x09000045, 0x001000f2, - 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, 0x00106000, - 0x00000000, 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, - 0x00101e46, 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x00000003, - 0x00000001, 0x00000000, 0x00000003, 0x00000001, 0x00000000, 0x00000000, - 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000a8, - 0x00000000, 0x00000000, 0x00000002, 0x0000001c, 0xffff0400, 0x00000100, - 0x00000072, 0x0000005c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000001, 0x00000001, 0x00000067, 0x00000002, 0x00000005, - 0x00000004, 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x53656874, - 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x694d0065, 0x736f7263, - 0x2074666f, 0x20295228, 0x4c534c48, 0x61685320, 0x20726564, 0x706d6f43, - 0x72656c69, 0x332e3920, 0x32392e30, 0x312e3030, 0x34383336, 0xababab00, - 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, - 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, - 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000065, 0x00000000, - 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, - 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, - 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, - 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 -}; -#else -#error "An appropriate 'textures' pixel shader is not defined" -#endif - -/* The yuv-rendering pixel shader: - - --- D3D11_PixelShader_YUV.hlsl --- - Texture2D theTextureY : register(t0); - Texture2D theTextureU : register(t1); - Texture2D theTextureV : register(t2); - SamplerState theSampler : register(s0); - - struct PixelShaderInput - { - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; - }; - - float4 main(PixelShaderInput input) : SV_TARGET - { - const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; - const float3 Rcoeff = {1.164, 0.000, 1.596}; - const float3 Gcoeff = {1.164, -0.391, -0.813}; - const float3 Bcoeff = {1.164, 2.018, 0.000}; - - float4 Output; - - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.y = theTextureU.Sample(theSampler, input.tex).r; - yuv.z = theTextureV.Sample(theSampler, input.tex).r; - - yuv += offset; - Output.r = dot(yuv, Rcoeff); - Output.g = dot(yuv, Gcoeff); - Output.b = dot(yuv, Bcoeff); - Output.a = 1.0f; - - return Output * input.color; - } - -*/ -#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) -static const DWORD D3D11_PixelShader_YUV[] = { - 0x43425844, 0x2321c6c6, 0xf14df2d1, 0xc79d068d, 0x8e672abf, 0x00000001, - 0x000005e8, 0x00000006, 0x00000038, 0x000001dc, 0x000003bc, 0x00000438, - 0x00000540, 0x000005b4, 0x396e6f41, 0x0000019c, 0x0000019c, 0xffff0200, - 0x0000016c, 0x00000030, 0x00300000, 0x00300000, 0x00300000, 0x00240003, - 0x00300000, 0x00000000, 0x00010001, 0x00020002, 0xffff0200, 0x05000051, - 0xa00f0000, 0xbd808081, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, - 0xa00f0001, 0x3f94fdf4, 0x3fcc49ba, 0x00000000, 0x00000000, 0x05000051, - 0xa00f0002, 0x3f94fdf4, 0xbec83127, 0xbf5020c5, 0x00000000, 0x05000051, - 0xa00f0003, 0x3f94fdf4, 0x400126e9, 0x00000000, 0x00000000, 0x0200001f, - 0x80000000, 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, - 0x90000000, 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, - 0x90000000, 0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, - 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0002, - 0xb0e40000, 0xa0e40802, 0x02000001, 0x80020000, 0x80000001, 0x02000001, - 0x80040000, 0x80000002, 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, - 0x03000005, 0x80080000, 0x80000000, 0xa0000001, 0x04000004, 0x80010001, - 0x80aa0000, 0xa0550001, 0x80ff0000, 0x03000008, 0x80020001, 0x80e40000, - 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003, - 0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001, - 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff, 0x52444853, - 0x000001d8, 0x00000040, 0x00000076, 0x0300005a, 0x00106000, 0x00000000, - 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04001858, 0x00107000, - 0x00000001, 0x00005555, 0x04001858, 0x00107000, 0x00000002, 0x00005555, - 0x03001062, 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, - 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, - 0x001000f2, 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, - 0x00106000, 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, - 0x00000001, 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, - 0x00100022, 0x00000000, 0x0010000a, 0x00000001, 0x09000045, 0x001000f2, - 0x00000001, 0x00101046, 0x00000001, 0x00107e46, 0x00000002, 0x00106000, - 0x00000000, 0x05000036, 0x00100042, 0x00000000, 0x0010000a, 0x00000001, - 0x0a000000, 0x00100072, 0x00000000, 0x00100246, 0x00000000, 0x00004002, - 0xbd808081, 0xbf008081, 0xbf008081, 0x00000000, 0x0a00000f, 0x00100012, - 0x00000001, 0x00100086, 0x00000000, 0x00004002, 0x3f94fdf4, 0x3fcc49ba, - 0x00000000, 0x00000000, 0x0a000010, 0x00100022, 0x00000001, 0x00100246, - 0x00000000, 0x00004002, 0x3f94fdf4, 0xbec83127, 0xbf5020c5, 0x00000000, - 0x0a00000f, 0x00100042, 0x00000001, 0x00100046, 0x00000000, 0x00004002, - 0x3f94fdf4, 0x400126e9, 0x00000000, 0x00000000, 0x05000036, 0x00100082, - 0x00000001, 0x00004001, 0x3f800000, 0x07000038, 0x001020f2, 0x00000000, - 0x00100e46, 0x00000001, 0x00101e46, 0x00000002, 0x0100003e, 0x54415453, - 0x00000074, 0x0000000c, 0x00000002, 0x00000000, 0x00000003, 0x00000005, - 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x46454452, 0x00000100, 0x00000000, 0x00000000, 0x00000004, 0x0000001c, - 0xffff0400, 0x00000100, 0x000000cb, 0x0000009c, 0x00000003, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x000000a7, - 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000000, 0x00000001, - 0x0000000d, 0x000000b3, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, - 0x00000001, 0x00000001, 0x0000000d, 0x000000bf, 0x00000002, 0x00000005, - 0x00000004, 0xffffffff, 0x00000002, 0x00000001, 0x0000000d, 0x53656874, - 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x74005965, 0x65546568, - 0x72757478, 0x74005565, 0x65546568, 0x72757478, 0x4d005665, 0x6f726369, - 0x74666f73, 0x29522820, 0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320, - 0x656c6970, 0x2e362072, 0x36392e33, 0x312e3030, 0x34383336, 0xababab00, - 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, - 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, - 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000065, 0x00000000, - 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, - 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, - 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, - 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 -}; -#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) -static const DWORD D3D11_PixelShader_YUV[] = { - 0x43425844, 0x6ede7360, 0x45ff5f8a, 0x34ac92ba, 0xb865f5e0, 0x00000001, - 0x000005c0, 0x00000006, 0x00000038, 0x000001b4, 0x00000394, 0x00000410, - 0x00000518, 0x0000058c, 0x396e6f41, 0x00000174, 0x00000174, 0xffff0200, - 0x00000144, 0x00000030, 0x00300000, 0x00300000, 0x00300000, 0x00240003, - 0x00300000, 0x00000000, 0x00010001, 0x00020002, 0xffff0201, 0x05000051, - 0xa00f0000, 0xbd808081, 0xbf008081, 0x3f800000, 0x00000000, 0x05000051, - 0xa00f0001, 0x3f94fdf4, 0x3fcc49ba, 0x00000000, 0x400126e9, 0x05000051, - 0xa00f0002, 0x3f94fdf4, 0xbec83127, 0xbf5020c5, 0x00000000, 0x0200001f, - 0x80000000, 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, - 0x90000000, 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, - 0x90000000, 0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40801, - 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40800, 0x02000001, 0x80020001, - 0x80000000, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40802, 0x02000001, - 0x80040001, 0x80000000, 0x03000002, 0x80070000, 0x80e40001, 0xa0d40000, - 0x0400005a, 0x80010001, 0x80e80000, 0xa0e40001, 0xa0aa0001, 0x03000008, - 0x80020001, 0x80e40000, 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, - 0xa0ec0001, 0xa0aa0001, 0x02000001, 0x80080001, 0xa0aa0000, 0x03000005, - 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, - 0x0000ffff, 0x52444853, 0x000001d8, 0x00000040, 0x00000076, 0x0300005a, - 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, - 0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x04001858, 0x00107000, - 0x00000002, 0x00005555, 0x03001062, 0x00101032, 0x00000001, 0x03001062, - 0x001010f2, 0x00000002, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, - 0x00000002, 0x09000045, 0x001000f2, 0x00000000, 0x00101046, 0x00000001, - 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x09000045, 0x001000f2, - 0x00000001, 0x00101046, 0x00000001, 0x00107e46, 0x00000001, 0x00106000, - 0x00000000, 0x05000036, 0x00100022, 0x00000000, 0x0010000a, 0x00000001, - 0x09000045, 0x001000f2, 0x00000001, 0x00101046, 0x00000001, 0x00107e46, - 0x00000002, 0x00106000, 0x00000000, 0x05000036, 0x00100042, 0x00000000, - 0x0010000a, 0x00000001, 0x0a000000, 0x00100072, 0x00000000, 0x00100246, - 0x00000000, 0x00004002, 0xbd808081, 0xbf008081, 0xbf008081, 0x00000000, - 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, 0x00000000, 0x00004002, - 0x3f94fdf4, 0x3fcc49ba, 0x00000000, 0x00000000, 0x0a000010, 0x00100022, - 0x00000001, 0x00100246, 0x00000000, 0x00004002, 0x3f94fdf4, 0xbec83127, - 0xbf5020c5, 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, 0x00100046, - 0x00000000, 0x00004002, 0x3f94fdf4, 0x400126e9, 0x00000000, 0x00000000, - 0x05000036, 0x00100082, 0x00000001, 0x00004001, 0x3f800000, 0x07000038, - 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, 0x00000002, - 0x0100003e, 0x54415453, 0x00000074, 0x0000000c, 0x00000002, 0x00000000, - 0x00000003, 0x00000005, 0x00000000, 0x00000000, 0x00000001, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x46454452, 0x00000100, 0x00000000, 0x00000000, - 0x00000004, 0x0000001c, 0xffff0400, 0x00000100, 0x000000cb, 0x0000009c, - 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, - 0x00000001, 0x000000a7, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, - 0x00000000, 0x00000001, 0x0000000d, 0x000000b3, 0x00000002, 0x00000005, - 0x00000004, 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, 0x000000bf, - 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000002, 0x00000001, - 0x0000000d, 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, - 0x74005965, 0x65546568, 0x72757478, 0x74005565, 0x65546568, 0x72757478, - 0x4d005665, 0x6f726369, 0x74666f73, 0x29522820, 0x534c4820, 0x6853204c, - 0x72656461, 0x6d6f4320, 0x656c6970, 0x2e362072, 0x36392e33, 0x312e3030, - 0x34383336, 0xababab00, 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, - 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, - 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000303, - 0x00000065, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, - 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, - 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, - 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, - 0x45475241, 0xabab0054 -}; -#else -#error "An appropriate 'yuv' pixel shader is not defined." -#endif - -/* The sole vertex shader: - - --- D3D11_VertexShader.hlsl --- - #pragma pack_matrix( row_major ) - - cbuffer VertexShaderConstants : register(b0) - { - matrix model; - matrix projectionAndView; - }; - - struct VertexShaderInput - { - float3 pos : POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; - }; - - struct VertexShaderOutput - { - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; - }; - - VertexShaderOutput main(VertexShaderInput input) - { - VertexShaderOutput output; - float4 pos = float4(input.pos, 1.0f); - - // Transform the vertex position into projected space. - pos = mul(pos, model); - pos = mul(pos, projectionAndView); - output.pos = pos; - - // Pass through texture coordinates and color values without transformation - output.tex = input.tex; - output.color = input.color; - - return output; - } -*/ -#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) -static const DWORD D3D11_VertexShader[] = { - 0x43425844, 0x62dfae5f, 0x3e8bd8df, 0x9ec97127, 0x5044eefb, 0x00000001, - 0x00000598, 0x00000006, 0x00000038, 0x0000016c, 0x00000334, 0x000003b0, - 0x000004b4, 0x00000524, 0x396e6f41, 0x0000012c, 0x0000012c, 0xfffe0200, - 0x000000f8, 0x00000034, 0x00240001, 0x00300000, 0x00300000, 0x00240000, - 0x00300001, 0x00000000, 0x00010008, 0x00000000, 0x00000000, 0xfffe0200, - 0x0200001f, 0x80000005, 0x900f0000, 0x0200001f, 0x80010005, 0x900f0001, - 0x0200001f, 0x80020005, 0x900f0002, 0x03000005, 0x800f0000, 0x90550000, - 0xa0e40002, 0x04000004, 0x800f0000, 0x90000000, 0xa0e40001, 0x80e40000, - 0x04000004, 0x800f0000, 0x90aa0000, 0xa0e40003, 0x80e40000, 0x03000002, - 0x800f0000, 0x80e40000, 0xa0e40004, 0x03000005, 0x800f0001, 0x80550000, - 0xa0e40006, 0x04000004, 0x800f0001, 0x80000000, 0xa0e40005, 0x80e40001, - 0x04000004, 0x800f0001, 0x80aa0000, 0xa0e40007, 0x80e40001, 0x04000004, - 0x800f0000, 0x80ff0000, 0xa0e40008, 0x80e40001, 0x04000004, 0xc0030000, - 0x80ff0000, 0xa0e40000, 0x80e40000, 0x02000001, 0xc00c0000, 0x80e40000, - 0x02000001, 0xe0030000, 0x90e40001, 0x02000001, 0xe00f0001, 0x90e40002, - 0x0000ffff, 0x52444853, 0x000001c0, 0x00010040, 0x00000070, 0x04000059, - 0x00208e46, 0x00000000, 0x00000008, 0x0300005f, 0x00101072, 0x00000000, - 0x0300005f, 0x00101032, 0x00000001, 0x0300005f, 0x001010f2, 0x00000002, - 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x00102032, - 0x00000001, 0x03000065, 0x001020f2, 0x00000002, 0x02000068, 0x00000002, - 0x08000038, 0x001000f2, 0x00000000, 0x00101556, 0x00000000, 0x00208e46, - 0x00000000, 0x00000001, 0x0a000032, 0x001000f2, 0x00000000, 0x00101006, - 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00100e46, 0x00000000, - 0x0a000032, 0x001000f2, 0x00000000, 0x00101aa6, 0x00000000, 0x00208e46, - 0x00000000, 0x00000002, 0x00100e46, 0x00000000, 0x08000000, 0x001000f2, - 0x00000000, 0x00100e46, 0x00000000, 0x00208e46, 0x00000000, 0x00000003, - 0x08000038, 0x001000f2, 0x00000001, 0x00100556, 0x00000000, 0x00208e46, - 0x00000000, 0x00000005, 0x0a000032, 0x001000f2, 0x00000001, 0x00100006, - 0x00000000, 0x00208e46, 0x00000000, 0x00000004, 0x00100e46, 0x00000001, - 0x0a000032, 0x001000f2, 0x00000001, 0x00100aa6, 0x00000000, 0x00208e46, - 0x00000000, 0x00000006, 0x00100e46, 0x00000001, 0x0a000032, 0x001020f2, - 0x00000000, 0x00100ff6, 0x00000000, 0x00208e46, 0x00000000, 0x00000007, - 0x00100e46, 0x00000001, 0x05000036, 0x00102032, 0x00000001, 0x00101046, - 0x00000001, 0x05000036, 0x001020f2, 0x00000002, 0x00101e46, 0x00000002, - 0x0100003e, 0x54415453, 0x00000074, 0x0000000b, 0x00000002, 0x00000000, - 0x00000006, 0x00000003, 0x00000000, 0x00000000, 0x00000001, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x46454452, 0x000000fc, 0x00000001, 0x00000054, - 0x00000001, 0x0000001c, 0xfffe0400, 0x00000100, 0x000000c6, 0x0000003c, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, - 0x00000001, 0x74726556, 0x68537865, 0x72656461, 0x736e6f43, 0x746e6174, - 0xabab0073, 0x0000003c, 0x00000002, 0x0000006c, 0x00000080, 0x00000000, - 0x00000000, 0x0000009c, 0x00000000, 0x00000040, 0x00000002, 0x000000a4, - 0x00000000, 0x000000b4, 0x00000040, 0x00000040, 0x00000002, 0x000000a4, - 0x00000000, 0x65646f6d, 0xabab006c, 0x00030002, 0x00040004, 0x00000000, - 0x00000000, 0x6a6f7270, 0x69746365, 0x6e416e6f, 0x65695664, 0x694d0077, - 0x736f7263, 0x2074666f, 0x20295228, 0x4c534c48, 0x61685320, 0x20726564, - 0x706d6f43, 0x72656c69, 0x332e3920, 0x32392e30, 0x312e3030, 0x34383336, - 0xababab00, 0x4e475349, 0x00000068, 0x00000003, 0x00000008, 0x00000050, - 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000707, 0x00000059, - 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000062, - 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x49534f50, - 0x4e4f4954, 0x58455400, 0x524f4f43, 0x4f430044, 0x00524f4c, 0x4e47534f, - 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, - 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, - 0x00000003, 0x00000001, 0x00000c03, 0x00000065, 0x00000000, 0x00000000, - 0x00000003, 0x00000002, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, - 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f -}; -#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) -static const DWORD D3D11_VertexShader[] = { - 0x43425844, 0x01a24e41, 0x696af551, 0x4b2a87d1, 0x82ea03f6, 0x00000001, - 0x00000598, 0x00000006, 0x00000038, 0x0000016c, 0x00000334, 0x000003b0, - 0x000004b4, 0x00000524, 0x396e6f41, 0x0000012c, 0x0000012c, 0xfffe0200, - 0x000000f8, 0x00000034, 0x00240001, 0x00300000, 0x00300000, 0x00240000, - 0x00300001, 0x00000000, 0x00010008, 0x00000000, 0x00000000, 0xfffe0201, - 0x0200001f, 0x80000005, 0x900f0000, 0x0200001f, 0x80010005, 0x900f0001, - 0x0200001f, 0x80020005, 0x900f0002, 0x03000005, 0x800f0000, 0x90550000, - 0xa0e40002, 0x04000004, 0x800f0000, 0x90000000, 0xa0e40001, 0x80e40000, - 0x04000004, 0x800f0000, 0x90aa0000, 0xa0e40003, 0x80e40000, 0x03000002, - 0x800f0000, 0x80e40000, 0xa0e40004, 0x03000005, 0x800f0001, 0x80550000, - 0xa0e40006, 0x04000004, 0x800f0001, 0x80000000, 0xa0e40005, 0x80e40001, - 0x04000004, 0x800f0001, 0x80aa0000, 0xa0e40007, 0x80e40001, 0x04000004, - 0x800f0000, 0x80ff0000, 0xa0e40008, 0x80e40001, 0x04000004, 0xc0030000, - 0x80ff0000, 0xa0e40000, 0x80e40000, 0x02000001, 0xc00c0000, 0x80e40000, - 0x02000001, 0xe0030000, 0x90e40001, 0x02000001, 0xe00f0001, 0x90e40002, - 0x0000ffff, 0x52444853, 0x000001c0, 0x00010040, 0x00000070, 0x04000059, - 0x00208e46, 0x00000000, 0x00000008, 0x0300005f, 0x00101072, 0x00000000, - 0x0300005f, 0x00101032, 0x00000001, 0x0300005f, 0x001010f2, 0x00000002, - 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x00102032, - 0x00000001, 0x03000065, 0x001020f2, 0x00000002, 0x02000068, 0x00000002, - 0x08000038, 0x001000f2, 0x00000000, 0x00101556, 0x00000000, 0x00208e46, - 0x00000000, 0x00000001, 0x0a000032, 0x001000f2, 0x00000000, 0x00101006, - 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00100e46, 0x00000000, - 0x0a000032, 0x001000f2, 0x00000000, 0x00101aa6, 0x00000000, 0x00208e46, - 0x00000000, 0x00000002, 0x00100e46, 0x00000000, 0x08000000, 0x001000f2, - 0x00000000, 0x00100e46, 0x00000000, 0x00208e46, 0x00000000, 0x00000003, - 0x08000038, 0x001000f2, 0x00000001, 0x00100556, 0x00000000, 0x00208e46, - 0x00000000, 0x00000005, 0x0a000032, 0x001000f2, 0x00000001, 0x00100006, - 0x00000000, 0x00208e46, 0x00000000, 0x00000004, 0x00100e46, 0x00000001, - 0x0a000032, 0x001000f2, 0x00000001, 0x00100aa6, 0x00000000, 0x00208e46, - 0x00000000, 0x00000006, 0x00100e46, 0x00000001, 0x0a000032, 0x001020f2, - 0x00000000, 0x00100ff6, 0x00000000, 0x00208e46, 0x00000000, 0x00000007, - 0x00100e46, 0x00000001, 0x05000036, 0x00102032, 0x00000001, 0x00101046, - 0x00000001, 0x05000036, 0x001020f2, 0x00000002, 0x00101e46, 0x00000002, - 0x0100003e, 0x54415453, 0x00000074, 0x0000000b, 0x00000002, 0x00000000, - 0x00000006, 0x00000003, 0x00000000, 0x00000000, 0x00000001, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x46454452, 0x000000fc, 0x00000001, 0x00000054, - 0x00000001, 0x0000001c, 0xfffe0400, 0x00000100, 0x000000c6, 0x0000003c, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, - 0x00000001, 0x74726556, 0x68537865, 0x72656461, 0x736e6f43, 0x746e6174, - 0xabab0073, 0x0000003c, 0x00000002, 0x0000006c, 0x00000080, 0x00000000, - 0x00000000, 0x0000009c, 0x00000000, 0x00000040, 0x00000002, 0x000000a4, - 0x00000000, 0x000000b4, 0x00000040, 0x00000040, 0x00000002, 0x000000a4, - 0x00000000, 0x65646f6d, 0xabab006c, 0x00030002, 0x00040004, 0x00000000, - 0x00000000, 0x6a6f7270, 0x69746365, 0x6e416e6f, 0x65695664, 0x694d0077, - 0x736f7263, 0x2074666f, 0x20295228, 0x4c534c48, 0x61685320, 0x20726564, - 0x706d6f43, 0x72656c69, 0x332e3920, 0x32392e30, 0x312e3030, 0x34383336, - 0xababab00, 0x4e475349, 0x00000068, 0x00000003, 0x00000008, 0x00000050, - 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000707, 0x00000059, - 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000062, - 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x49534f50, - 0x4e4f4954, 0x58455400, 0x524f4f43, 0x4f430044, 0x00524f4c, 0x4e47534f, - 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, - 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, - 0x00000003, 0x00000001, 0x00000c03, 0x00000065, 0x00000000, 0x00000000, - 0x00000003, 0x00000002, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, - 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f -}; -#else -#error "An appropriate vertex shader is not defined." -#endif - /* Direct3D 11.1 renderer implementation */ static SDL_Renderer *D3D11_CreateRenderer(SDL_Window * window, Uint32 flags); @@ -773,12 +230,14 @@ SDL_RenderDriver D3D11_RenderDriver = { SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE ), /* flags. see SDL_RendererFlags */ - 4, /* num_texture_formats */ + 6, /* num_texture_formats */ { /* texture_formats */ SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_RGB888, SDL_PIXELFORMAT_YV12, - SDL_PIXELFORMAT_IYUV + SDL_PIXELFORMAT_IYUV, + SDL_PIXELFORMAT_NV12, + SDL_PIXELFORMAT_NV21 }, 0, /* max_texture_width: will be filled in later */ 0 /* max_texture_height: will be filled in later */ @@ -787,7 +246,8 @@ SDL_RenderDriver D3D11_RenderDriver = { Uint32 -D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat) { +D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat) +{ switch (dxgiFormat) { case DXGI_FORMAT_B8G8R8A8_UNORM: return SDL_PIXELFORMAT_ARGB8888; @@ -808,6 +268,8 @@ SDLPixelFormatToDXGIFormat(Uint32 sdlFormat) return DXGI_FORMAT_B8G8R8X8_UNORM; case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_NV12: /* For the Y texture */ + case SDL_PIXELFORMAT_NV21: /* For the Y texture */ return DXGI_FORMAT_R8_UNORM; default: return DXGI_FORMAT_UNKNOWN; @@ -918,9 +380,9 @@ D3D11_ReleaseAll(SDL_Renderer * renderer) SAFE_RELEASE(data->inputLayout); SAFE_RELEASE(data->vertexBuffer); SAFE_RELEASE(data->vertexShader); - SAFE_RELEASE(data->colorPixelShader); - SAFE_RELEASE(data->texturePixelShader); - SAFE_RELEASE(data->yuvPixelShader); + for (i = 0; i < SDL_arraysize(data->pixelShaders); ++i) { + SAFE_RELEASE(data->pixelShaders[i]); + } if (data->blendModesCount > 0) { for (i = 0; i < data->blendModesCount; ++i) { SAFE_RELEASE(data->blendModes[i].blendState); @@ -1074,6 +536,7 @@ D3D11_CreateDeviceResources(SDL_Renderer * renderer) IDXGIDevice1 *dxgiDevice = NULL; HRESULT result = S_OK; UINT creationFlags; + int i; /* This array defines the set of DirectX hardware feature levels this app will support. * Note the ordering should be preserved. @@ -1091,14 +554,6 @@ D3D11_CreateDeviceResources(SDL_Renderer * renderer) D3D_FEATURE_LEVEL_9_1 }; - /* Declare how the input layout for SDL's vertex shader will be setup: */ - const D3D11_INPUT_ELEMENT_DESC vertexDesc[] = - { - { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 }, - }; - D3D11_BUFFER_DESC constantBufferDesc; D3D11_SAMPLER_DESC samplerDesc; D3D11_RASTERIZER_DESC rasterDesc; @@ -1230,63 +685,14 @@ D3D11_CreateDeviceResources(SDL_Renderer * renderer) goto done; } - /* Load in SDL's one and only vertex shader: */ - result = ID3D11Device_CreateVertexShader(data->d3dDevice, - D3D11_VertexShader, - sizeof(D3D11_VertexShader), - NULL, - &data->vertexShader - ); - if (FAILED(result)) { - WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateVertexShader"), result); - goto done; - } - - /* Create an input layout for SDL's vertex shader: */ - result = ID3D11Device_CreateInputLayout(data->d3dDevice, - vertexDesc, - ARRAYSIZE(vertexDesc), - D3D11_VertexShader, - sizeof(D3D11_VertexShader), - &data->inputLayout - ); - if (FAILED(result)) { - WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateInputLayout"), result); - goto done; - } - - /* Load in SDL's pixel shaders */ - result = ID3D11Device_CreatePixelShader(data->d3dDevice, - D3D11_PixelShader_Colors, - sizeof(D3D11_PixelShader_Colors), - NULL, - &data->colorPixelShader - ); - if (FAILED(result)) { - WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreatePixelShader ['color' shader]"), result); - goto done; - } - - result = ID3D11Device_CreatePixelShader(data->d3dDevice, - D3D11_PixelShader_Textures, - sizeof(D3D11_PixelShader_Textures), - NULL, - &data->texturePixelShader - ); - if (FAILED(result)) { - WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreatePixelShader ['textures' shader]"), result); + if (D3D11_CreateVertexShader(data->d3dDevice, &data->vertexShader, &data->inputLayout) < 0) { goto done; } - result = ID3D11Device_CreatePixelShader(data->d3dDevice, - D3D11_PixelShader_YUV, - sizeof(D3D11_PixelShader_YUV), - NULL, - &data->yuvPixelShader - ); - if (FAILED(result)) { - WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreatePixelShader ['yuv' shader]"), result); - goto done; + for (i = 0; i < SDL_arraysize(data->pixelShaders); ++i) { + if (D3D11_CreatePixelShader(data->d3dDevice, (D3D11_Shader)i, &data->pixelShaders[i]) < 0) { + goto done; + } } /* Setup space to hold vertex shader constants: */ @@ -1829,8 +1235,8 @@ D3D11_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) texture->format == SDL_PIXELFORMAT_IYUV) { textureData->yuv = SDL_TRUE; - textureDesc.Width /= 2; - textureDesc.Height /= 2; + textureDesc.Width = (textureDesc.Width + 1) / 2; + textureDesc.Height = (textureDesc.Height + 1) / 2; result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice, &textureDesc, @@ -1855,6 +1261,28 @@ D3D11_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) } } + if (texture->format == SDL_PIXELFORMAT_NV12 || + texture->format == SDL_PIXELFORMAT_NV21) { + D3D11_TEXTURE2D_DESC nvTextureDesc = textureDesc; + + textureData->nv12 = SDL_TRUE; + + nvTextureDesc.Format = DXGI_FORMAT_R8G8_UNORM; + nvTextureDesc.Width = (textureDesc.Width + 1) / 2; + nvTextureDesc.Height = (textureDesc.Height + 1) / 2; + + result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice, + &nvTextureDesc, + NULL, + &textureData->mainTextureNV + ); + if (FAILED(result)) { + D3D11_DestroyTexture(renderer, texture); + WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result); + return -1; + } + } + resourceViewDesc.Format = textureDesc.Format; resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; resourceViewDesc.Texture2D.MostDetailedMip = 0; @@ -1893,6 +1321,23 @@ D3D11_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) } } + if (textureData->nv12) { + D3D11_SHADER_RESOURCE_VIEW_DESC nvResourceViewDesc = resourceViewDesc; + + nvResourceViewDesc.Format = DXGI_FORMAT_R8G8_UNORM; + + result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice, + (ID3D11Resource *)textureData->mainTextureNV, + &nvResourceViewDesc, + &textureData->mainTextureResourceViewNV + ); + if (FAILED(result)) { + D3D11_DestroyTexture(renderer, texture); + WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result); + return -1; + } + } + if (texture->access & SDL_TEXTUREACCESS_TARGET) { D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc; renderTargetViewDesc.Format = textureDesc.Format; @@ -1937,7 +1382,7 @@ D3D11_DestroyTexture(SDL_Renderer * renderer, } static int -D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Texture2D *texture, Uint32 format, int x, int y, int w, int h, const void *pixels, int pitch) +D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Texture2D *texture, int bpp, int x, int y, int w, int h, const void *pixels, int pitch) { ID3D11Texture2D *stagingTexture; const Uint8 *src; @@ -1981,7 +1426,7 @@ D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Texture2D *tex src = (const Uint8 *)pixels; dst = textureMemory.pData; - length = w * SDL_BYTESPERPIXEL(format); + length = w * bpp; if (length == pitch && length == textureMemory.RowPitch) { SDL_memcpy(dst, src, length*h); } else { @@ -2032,7 +1477,7 @@ D3D11_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, return -1; } - if (D3D11_UpdateTextureInternal(rendererData, textureData->mainTexture, texture->format, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch) < 0) { + if (D3D11_UpdateTextureInternal(rendererData, textureData->mainTexture, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch) < 0) { return -1; } @@ -2040,13 +1485,22 @@ D3D11_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, /* Skip to the correct offset into the next texture */ srcPixels = (const void*)((const Uint8*)srcPixels + rect->h * srcPitch); - if (D3D11_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureV : textureData->mainTextureU, texture->format, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, srcPixels, srcPitch / 2) < 0) { + if (D3D11_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureV : textureData->mainTextureU, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2) < 0) { + return -1; + } + + /* Skip to the correct offset into the next texture */ + srcPixels = (const void*)((const Uint8*)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1) / 2)); + if (D3D11_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureU : textureData->mainTextureV, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2) < 0) { return -1; } + } + if (textureData->nv12) { /* Skip to the correct offset into the next texture */ - srcPixels = (const void*)((const Uint8*)srcPixels + (rect->h * srcPitch) / 4); - if (D3D11_UpdateTextureInternal(rendererData, texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainTextureU : textureData->mainTextureV, texture->format, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, srcPixels, srcPitch / 2) < 0) { + srcPixels = (const void*)((const Uint8*)srcPixels + rect->h * srcPitch); + + if (D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureNV, 2, rect->x / 2, rect->y / 2, ((rect->w + 1) / 2), (rect->h + 1) / 2, srcPixels, 2*((srcPitch + 1) / 2)) < 0) { return -1; } } @@ -2068,13 +1522,13 @@ D3D11_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, return -1; } - if (D3D11_UpdateTextureInternal(rendererData, textureData->mainTexture, texture->format, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch) < 0) { + if (D3D11_UpdateTextureInternal(rendererData, textureData->mainTexture, SDL_BYTESPERPIXEL(texture->format), rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch) < 0) { return -1; } - if (D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureU, texture->format, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Uplane, Upitch) < 0) { + if (D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureU, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Uplane, Upitch) < 0) { return -1; } - if (D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureV, texture->format, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Vplane, Vpitch) < 0) { + if (D3D11_UpdateTextureInternal(rendererData, textureData->mainTextureV, SDL_BYTESPERPIXEL(texture->format), rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Vplane, Vpitch) < 0) { return -1; } return 0; @@ -2095,7 +1549,7 @@ D3D11_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, return -1; } - if (textureData->yuv) { + if (textureData->yuv || textureData->nv12) { /* It's more efficient to upload directly... */ if (!textureData->pixels) { textureData->pitch = texture->w; @@ -2178,7 +1632,7 @@ D3D11_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture) return; } - if (textureData->yuv) { + if (textureData->yuv || textureData->nv12) { const SDL_Rect *rect = &textureData->locked_rect; void *pixels = (void *) ((Uint8 *) textureData->pixels + rect->y * textureData->pitch + @@ -2594,7 +2048,7 @@ D3D11_RenderDrawPoints(SDL_Renderer * renderer, D3D11_SetPixelShader( renderer, - rendererData->colorPixelShader, + rendererData->pixelShaders[SHADER_SOLID], 0, NULL, NULL); @@ -2633,7 +2087,7 @@ D3D11_RenderDrawLines(SDL_Renderer * renderer, D3D11_SetPixelShader( renderer, - rendererData->colorPixelShader, + rendererData->pixelShaders[SHADER_SOLID], 0, NULL, NULL); @@ -2678,7 +2132,7 @@ D3D11_RenderFillRects(SDL_Renderer * renderer, D3D11_SetPixelShader( renderer, - rendererData->colorPixelShader, + rendererData->pixelShaders[SHADER_SOLID], 0, NULL, NULL); @@ -2689,20 +2143,91 @@ D3D11_RenderFillRects(SDL_Renderer * renderer, return 0; } -static ID3D11SamplerState * -D3D11_RenderGetSampler(SDL_Renderer * renderer, SDL_Texture * texture) +static int +D3D11_RenderSetupSampler(SDL_Renderer * renderer, SDL_Texture * texture) { D3D11_RenderData *rendererData = (D3D11_RenderData *) renderer->driverdata; D3D11_TextureData *textureData = (D3D11_TextureData *) texture->driverdata; + ID3D11SamplerState *textureSampler; switch (textureData->scaleMode) { case D3D11_FILTER_MIN_MAG_MIP_POINT: - return rendererData->nearestPixelSampler; + textureSampler = rendererData->nearestPixelSampler; + break; case D3D11_FILTER_MIN_MAG_MIP_LINEAR: - return rendererData->linearSampler; + textureSampler = rendererData->linearSampler; + break; default: - return NULL; + return SDL_SetError("Unknown scale mode: %d\n", textureData->scaleMode); + } + + if (textureData->yuv) { + ID3D11ShaderResourceView *shaderResources[] = { + textureData->mainTextureResourceView, + textureData->mainTextureResourceViewU, + textureData->mainTextureResourceViewV + }; + D3D11_Shader shader; + + switch (SDL_GetYUVConversionModeForResolution(texture->w, texture->h)) { + case SDL_YUV_CONVERSION_JPEG: + shader = SHADER_YUV_JPEG; + break; + case SDL_YUV_CONVERSION_BT601: + shader = SHADER_YUV_BT601; + break; + case SDL_YUV_CONVERSION_BT709: + shader = SHADER_YUV_BT709; + break; + default: + return SDL_SetError("Unsupported YUV conversion mode"); + } + + D3D11_SetPixelShader( + renderer, + rendererData->pixelShaders[shader], + SDL_arraysize(shaderResources), + shaderResources, + textureSampler); + + } else if (textureData->nv12) { + ID3D11ShaderResourceView *shaderResources[] = { + textureData->mainTextureResourceView, + textureData->mainTextureResourceViewNV, + }; + D3D11_Shader shader; + + switch (SDL_GetYUVConversionModeForResolution(texture->w, texture->h)) { + case SDL_YUV_CONVERSION_JPEG: + shader = texture->format == SDL_PIXELFORMAT_NV12 ? SHADER_NV12_JPEG : SHADER_NV21_JPEG; + break; + case SDL_YUV_CONVERSION_BT601: + shader = texture->format == SDL_PIXELFORMAT_NV12 ? SHADER_NV12_BT601 : SHADER_NV21_BT601; + break; + case SDL_YUV_CONVERSION_BT709: + shader = texture->format == SDL_PIXELFORMAT_NV12 ? SHADER_NV12_BT709 : SHADER_NV21_BT709; + break; + default: + return SDL_SetError("Unsupported YUV conversion mode"); + } + + D3D11_SetPixelShader( + renderer, + rendererData->pixelShaders[shader], + SDL_arraysize(shaderResources), + shaderResources, + textureSampler); + + } else { + D3D11_SetPixelShader( + renderer, + rendererData->pixelShaders[SHADER_RGB], + 1, + &textureData->mainTextureResourceView, + textureSampler); } + + return 0; } static int @@ -2714,7 +2239,6 @@ D3D11_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, float minu, maxu, minv, maxv; Float4 color; VertexPositionColor vertices[4]; - ID3D11SamplerState *textureSampler; D3D11_RenderStartDrawOp(renderer); D3D11_RenderSetBlendMode(renderer, texture->blendMode); @@ -2769,26 +2293,8 @@ D3D11_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, return -1; } - textureSampler = D3D11_RenderGetSampler(renderer, texture); - if (textureData->yuv) { - ID3D11ShaderResourceView *shaderResources[] = { - textureData->mainTextureResourceView, - textureData->mainTextureResourceViewU, - textureData->mainTextureResourceViewV - }; - D3D11_SetPixelShader( - renderer, - rendererData->yuvPixelShader, - SDL_arraysize(shaderResources), - shaderResources, - textureSampler); - } else { - D3D11_SetPixelShader( - renderer, - rendererData->texturePixelShader, - 1, - &textureData->mainTextureResourceView, - textureSampler); + if (D3D11_RenderSetupSampler(renderer, texture) < 0) { + return -1; } D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, sizeof(vertices) / sizeof(VertexPositionColor)); @@ -2808,7 +2314,6 @@ D3D11_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, Float4X4 modelMatrix; float minx, maxx, miny, maxy; VertexPositionColor vertices[4]; - ID3D11SamplerState *textureSampler; D3D11_RenderStartDrawOp(renderer); D3D11_RenderSetBlendMode(renderer, texture->blendMode); @@ -2885,26 +2390,8 @@ D3D11_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture, return -1; } - textureSampler = D3D11_RenderGetSampler(renderer, texture); - if (textureData->yuv) { - ID3D11ShaderResourceView *shaderResources[] = { - textureData->mainTextureResourceView, - textureData->mainTextureResourceViewU, - textureData->mainTextureResourceViewV - }; - D3D11_SetPixelShader( - renderer, - rendererData->yuvPixelShader, - SDL_arraysize(shaderResources), - shaderResources, - textureSampler); - } else { - D3D11_SetPixelShader( - renderer, - rendererData->texturePixelShader, - 1, - &textureData->mainTextureResourceView, - textureSampler); + if (D3D11_RenderSetupSampler(renderer, texture) < 0) { + return -1; } D3D11_RenderFinishDrawOp(renderer, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, sizeof(vertices) / sizeof(VertexPositionColor)); diff --git a/src/render/direct3d11/SDL_shaders_d3d11.c b/src/render/direct3d11/SDL_shaders_d3d11.c new file mode 100755 index 0000000000000..97fd038b46c47 --- /dev/null +++ b/src/render/direct3d11/SDL_shaders_d3d11.c @@ -0,0 +1,1957 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2017 Sam Lantinga + + 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, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED + +#include "SDL_stdinc.h" + +#define COBJMACROS +#include "../../core/windows/SDL_windows.h" +#include + +#include "SDL_shaders_d3d11.h" + +#define SDL_COMPOSE_ERROR(str) SDL_STRINGIFY_ARG(__FUNCTION__) ", " str + + +/* Direct3D 11.x shaders + + SDL's shaders are compiled into SDL itself, to simplify distribution. + + All Direct3D 11.x shaders were compiled with the following: + + fxc /E"main" /T "" /Fo"" "" + + Variables: + - : the type of shader. A table of utilized shader types is + listed below. + - : where to store compiled output + - : where to read shader source code from + + Shader types: + - ps_4_0_level_9_1: Pixel shader for Windows 8+, including Windows RT + - vs_4_0_level_9_1: Vertex shader for Windows 8+, including Windows RT + - ps_4_0_level_9_3: Pixel shader for Windows Phone 8 + - vs_4_0_level_9_3: Vertex shader for Windows Phone 8 + + + Shader object code was converted to a list of DWORDs via the following + *nix style command (available separately from Windows + MSVC): + + hexdump -v -e '6/4 "0x%08.8x, " "\n"' + */ +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP +#define D3D11_USE_SHADER_MODEL_4_0_level_9_3 +#else +#define D3D11_USE_SHADER_MODEL_4_0_level_9_1 +#endif + +/* The color-only-rendering pixel shader: + + --- D3D11_PixelShader_Colors.hlsl --- + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + return input.color; + } +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_PixelShader_Colors[] = { + 0x43425844, 0xd74c28fe, 0xa1eb8804, 0x269d512a, 0x7699723d, 0x00000001, + 0x00000240, 0x00000006, 0x00000038, 0x00000084, 0x000000c4, 0x00000140, + 0x00000198, 0x0000020c, 0x396e6f41, 0x00000044, 0x00000044, 0xffff0200, + 0x00000020, 0x00000024, 0x00240000, 0x00240000, 0x00240000, 0x00240000, + 0x00240000, 0xffff0200, 0x0200001f, 0x80000000, 0xb00f0001, 0x02000001, + 0x800f0800, 0xb0e40001, 0x0000ffff, 0x52444853, 0x00000038, 0x00000040, + 0x0000000e, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, 0x001020f2, + 0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000002, + 0x0100003e, 0x54415453, 0x00000074, 0x00000002, 0x00000000, 0x00000000, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x46454452, 0x00000050, 0x00000000, 0x00000000, + 0x00000000, 0x0000001c, 0xffff0400, 0x00000100, 0x0000001c, 0x7263694d, + 0x666f736f, 0x52282074, 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, + 0x6c69706d, 0x39207265, 0x2e30332e, 0x30303239, 0x3336312e, 0xab003438, + 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, + 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, + 0x00000000, 0x00000003, 0x00000001, 0x00000003, 0x00000065, 0x00000000, + 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, + 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, + 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, + 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_PixelShader_Colors[] = { + 0x43425844, 0x93f6ccfc, 0x5f919270, 0x7a11aa4f, 0x9148e931, 0x00000001, + 0x00000240, 0x00000006, 0x00000038, 0x00000084, 0x000000c4, 0x00000140, + 0x00000198, 0x0000020c, 0x396e6f41, 0x00000044, 0x00000044, 0xffff0200, + 0x00000020, 0x00000024, 0x00240000, 0x00240000, 0x00240000, 0x00240000, + 0x00240000, 0xffff0201, 0x0200001f, 0x80000000, 0xb00f0001, 0x02000001, + 0x800f0800, 0xb0e40001, 0x0000ffff, 0x52444853, 0x00000038, 0x00000040, + 0x0000000e, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, 0x001020f2, + 0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000002, + 0x0100003e, 0x54415453, 0x00000074, 0x00000002, 0x00000000, 0x00000000, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x46454452, 0x00000050, 0x00000000, 0x00000000, + 0x00000000, 0x0000001c, 0xffff0400, 0x00000100, 0x0000001c, 0x7263694d, + 0x666f736f, 0x52282074, 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, + 0x6c69706d, 0x39207265, 0x2e30332e, 0x30303239, 0x3336312e, 0xab003438, + 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, + 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, + 0x00000000, 0x00000003, 0x00000001, 0x00000003, 0x00000065, 0x00000000, + 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, + 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, + 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, + 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#else +#error "An appropriate 'colors' pixel shader is not defined." +#endif + +/* The texture-rendering pixel shader: + + --- D3D11_PixelShader_Textures.hlsl --- + Texture2D theTexture : register(t0); + SamplerState theSampler : register(s0); + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + return theTexture.Sample(theSampler, input.tex) * input.color; + } +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_PixelShader_Textures[] = { + 0x43425844, 0x6299b59f, 0x155258f2, 0x873ab86a, 0xfcbb6dcd, 0x00000001, + 0x00000330, 0x00000006, 0x00000038, 0x000000c0, 0x0000015c, 0x000001d8, + 0x00000288, 0x000002fc, 0x396e6f41, 0x00000080, 0x00000080, 0xffff0200, + 0x00000058, 0x00000028, 0x00280000, 0x00280000, 0x00280000, 0x00240001, + 0x00280000, 0x00000000, 0xffff0200, 0x0200001f, 0x80000000, 0xb0030000, + 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, 0xa00f0800, + 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, 0x03000005, 0x800f0000, + 0x80e40000, 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff, + 0x52444853, 0x00000094, 0x00000040, 0x00000025, 0x0300005a, 0x00106000, + 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x03001062, + 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, + 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x09000045, 0x001000f2, + 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, 0x00106000, + 0x00000000, 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, + 0x00101e46, 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x00000003, + 0x00000001, 0x00000000, 0x00000003, 0x00000001, 0x00000000, 0x00000000, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000a8, + 0x00000000, 0x00000000, 0x00000002, 0x0000001c, 0xffff0400, 0x00000100, + 0x00000072, 0x0000005c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000001, 0x00000001, 0x00000067, 0x00000002, 0x00000005, + 0x00000004, 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x53656874, + 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x694d0065, 0x736f7263, + 0x2074666f, 0x20295228, 0x4c534c48, 0x61685320, 0x20726564, 0x706d6f43, + 0x72656c69, 0x332e3920, 0x32392e30, 0x312e3030, 0x34383336, 0xababab00, + 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, + 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, + 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000065, 0x00000000, + 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, + 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, + 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, + 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_PixelShader_Textures[] = { + 0x43425844, 0x5876569a, 0x01b6c87e, 0x8447454f, 0xc7f3ef10, 0x00000001, + 0x00000330, 0x00000006, 0x00000038, 0x000000c0, 0x0000015c, 0x000001d8, + 0x00000288, 0x000002fc, 0x396e6f41, 0x00000080, 0x00000080, 0xffff0200, + 0x00000058, 0x00000028, 0x00280000, 0x00280000, 0x00280000, 0x00240001, + 0x00280000, 0x00000000, 0xffff0201, 0x0200001f, 0x80000000, 0xb0030000, + 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, 0xa00f0800, + 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, 0x03000005, 0x800f0000, + 0x80e40000, 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff, + 0x52444853, 0x00000094, 0x00000040, 0x00000025, 0x0300005a, 0x00106000, + 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x03001062, + 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, + 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x09000045, 0x001000f2, + 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, 0x00106000, + 0x00000000, 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, + 0x00101e46, 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x00000003, + 0x00000001, 0x00000000, 0x00000003, 0x00000001, 0x00000000, 0x00000000, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000a8, + 0x00000000, 0x00000000, 0x00000002, 0x0000001c, 0xffff0400, 0x00000100, + 0x00000072, 0x0000005c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000001, 0x00000001, 0x00000067, 0x00000002, 0x00000005, + 0x00000004, 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x53656874, + 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x694d0065, 0x736f7263, + 0x2074666f, 0x20295228, 0x4c534c48, 0x61685320, 0x20726564, 0x706d6f43, + 0x72656c69, 0x332e3920, 0x32392e30, 0x312e3030, 0x34383336, 0xababab00, + 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, + 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, + 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000065, 0x00000000, + 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, + 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, + 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, + 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#else +#error "An appropriate 'textures' pixel shader is not defined" +#endif + +/* The yuv-rendering pixel shader: + + --- D3D11_PixelShader_YUV_JPEG.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureU : register(t1); + Texture2D theTextureV : register(t2); + SamplerState theSampler : register(s0); + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {0.0, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.0000, 0.0000, 1.4020}; + const float3 Gcoeff = {1.0000, -0.3441, -0.7141}; + const float3 Bcoeff = {1.0000, 1.7720, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.y = theTextureU.Sample(theSampler, input.tex).r; + yuv.z = theTextureV.Sample(theSampler, input.tex).r; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } + +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_PixelShader_YUV_JPEG[] = { + 0x43425844, 0x10359e9c, 0x92c3d2c4, 0x00bf0cd5, 0x5ce8c499, 0x00000001, + 0x000005e8, 0x00000006, 0x00000038, 0x000001dc, 0x000003bc, 0x00000438, + 0x00000540, 0x000005b4, 0x396e6f41, 0x0000019c, 0x0000019c, 0xffff0200, + 0x0000016c, 0x00000030, 0x00300000, 0x00300000, 0x00300000, 0x00240003, + 0x00300000, 0x00000000, 0x00010001, 0x00020002, 0xffff0200, 0x05000051, + 0xa00f0000, 0x00000000, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, + 0xa00f0001, 0x3f800000, 0x3fb374bc, 0x00000000, 0x00000000, 0x05000051, + 0xa00f0002, 0x3f800000, 0xbeb02de0, 0xbf36cf42, 0x00000000, 0x05000051, + 0xa00f0003, 0x3f800000, 0x3fe2d0e5, 0x00000000, 0x00000000, 0x0200001f, + 0x80000000, 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, + 0x90000000, 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, + 0x90000000, 0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, + 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0002, + 0xb0e40000, 0xa0e40802, 0x02000001, 0x80020000, 0x80000001, 0x02000001, + 0x80040000, 0x80000002, 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, + 0x03000005, 0x80080000, 0x80000000, 0xa0000001, 0x04000004, 0x80010001, + 0x80aa0000, 0xa0550001, 0x80ff0000, 0x03000008, 0x80020001, 0x80e40000, + 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003, + 0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001, + 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff, 0x52444853, + 0x000001d8, 0x00000040, 0x00000076, 0x0300005a, 0x00106000, 0x00000000, + 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04001858, 0x00107000, + 0x00000001, 0x00005555, 0x04001858, 0x00107000, 0x00000002, 0x00005555, + 0x03001062, 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, + 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, + 0x001000f2, 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, + 0x00106000, 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, + 0x00000001, 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, + 0x00100022, 0x00000000, 0x0010000a, 0x00000001, 0x09000045, 0x001000f2, + 0x00000001, 0x00101046, 0x00000001, 0x00107e46, 0x00000002, 0x00106000, + 0x00000000, 0x05000036, 0x00100042, 0x00000000, 0x0010000a, 0x00000001, + 0x0a000000, 0x00100072, 0x00000000, 0x00100246, 0x00000000, 0x00004002, + 0x00000000, 0xbf008081, 0xbf008081, 0x00000000, 0x0a00000f, 0x00100012, + 0x00000001, 0x00100086, 0x00000000, 0x00004002, 0x3f800000, 0x3fb374bc, + 0x00000000, 0x00000000, 0x0a000010, 0x00100022, 0x00000001, 0x00100246, + 0x00000000, 0x00004002, 0x3f800000, 0xbeb02de0, 0xbf36cf42, 0x00000000, + 0x0a00000f, 0x00100042, 0x00000001, 0x00100046, 0x00000000, 0x00004002, + 0x3f800000, 0x3fe2d0e5, 0x00000000, 0x00000000, 0x05000036, 0x00100082, + 0x00000001, 0x00004001, 0x3f800000, 0x07000038, 0x001020f2, 0x00000000, + 0x00100e46, 0x00000001, 0x00101e46, 0x00000002, 0x0100003e, 0x54415453, + 0x00000074, 0x0000000c, 0x00000002, 0x00000000, 0x00000003, 0x00000005, + 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x46454452, 0x00000100, 0x00000000, 0x00000000, 0x00000004, 0x0000001c, + 0xffff0400, 0x00000100, 0x000000cb, 0x0000009c, 0x00000003, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x000000a7, + 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000000, 0x00000001, + 0x0000000d, 0x000000b3, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, + 0x00000001, 0x00000001, 0x0000000d, 0x000000bf, 0x00000002, 0x00000005, + 0x00000004, 0xffffffff, 0x00000002, 0x00000001, 0x0000000d, 0x53656874, + 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x74005965, 0x65546568, + 0x72757478, 0x74005565, 0x65546568, 0x72757478, 0x4d005665, 0x6f726369, + 0x74666f73, 0x29522820, 0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320, + 0x656c6970, 0x2e362072, 0x36392e33, 0x312e3030, 0x34383336, 0xababab00, + 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, + 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, + 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000065, 0x00000000, + 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, + 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, + 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, + 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_PixelShader_YUV_JPEG[] = { + 0x43425844, 0x616d6673, 0x83174178, 0x15aac25d, 0x2a340487, 0x00000001, + 0x000005c0, 0x00000006, 0x00000038, 0x000001b4, 0x00000394, 0x00000410, + 0x00000518, 0x0000058c, 0x396e6f41, 0x00000174, 0x00000174, 0xffff0200, + 0x00000144, 0x00000030, 0x00300000, 0x00300000, 0x00300000, 0x00240003, + 0x00300000, 0x00000000, 0x00010001, 0x00020002, 0xffff0201, 0x05000051, + 0xa00f0000, 0x00000000, 0xbf008081, 0x3f800000, 0x3fb374bc, 0x05000051, + 0xa00f0001, 0x3f800000, 0xbeb02de0, 0xbf36cf42, 0x00000000, 0x05000051, + 0xa00f0002, 0x3f800000, 0x3fe2d0e5, 0x00000000, 0x00000000, 0x0200001f, + 0x80000000, 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, + 0x90000000, 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, + 0x90000000, 0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40801, + 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40800, 0x02000001, 0x80020001, + 0x80000000, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40802, 0x02000001, + 0x80040001, 0x80000000, 0x03000002, 0x80070000, 0x80e40001, 0xa0d40000, + 0x0400005a, 0x80010001, 0x80e80000, 0xa0ee0000, 0xa0000000, 0x03000008, + 0x80020001, 0x80e40000, 0xa0e40001, 0x0400005a, 0x80040001, 0x80e40000, + 0xa0e40002, 0xa0aa0002, 0x02000001, 0x80080001, 0xa0aa0000, 0x03000005, + 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, + 0x0000ffff, 0x52444853, 0x000001d8, 0x00000040, 0x00000076, 0x0300005a, + 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, + 0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x04001858, 0x00107000, + 0x00000002, 0x00005555, 0x03001062, 0x00101032, 0x00000001, 0x03001062, + 0x001010f2, 0x00000002, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, + 0x00000002, 0x09000045, 0x001000f2, 0x00000000, 0x00101046, 0x00000001, + 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x09000045, 0x001000f2, + 0x00000001, 0x00101046, 0x00000001, 0x00107e46, 0x00000001, 0x00106000, + 0x00000000, 0x05000036, 0x00100022, 0x00000000, 0x0010000a, 0x00000001, + 0x09000045, 0x001000f2, 0x00000001, 0x00101046, 0x00000001, 0x00107e46, + 0x00000002, 0x00106000, 0x00000000, 0x05000036, 0x00100042, 0x00000000, + 0x0010000a, 0x00000001, 0x0a000000, 0x00100072, 0x00000000, 0x00100246, + 0x00000000, 0x00004002, 0x00000000, 0xbf008081, 0xbf008081, 0x00000000, + 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, 0x00000000, 0x00004002, + 0x3f800000, 0x3fb374bc, 0x00000000, 0x00000000, 0x0a000010, 0x00100022, + 0x00000001, 0x00100246, 0x00000000, 0x00004002, 0x3f800000, 0xbeb02de0, + 0xbf36cf42, 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, 0x00100046, + 0x00000000, 0x00004002, 0x3f800000, 0x3fe2d0e5, 0x00000000, 0x00000000, + 0x05000036, 0x00100082, 0x00000001, 0x00004001, 0x3f800000, 0x07000038, + 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, 0x00000002, + 0x0100003e, 0x54415453, 0x00000074, 0x0000000c, 0x00000002, 0x00000000, + 0x00000003, 0x00000005, 0x00000000, 0x00000000, 0x00000001, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x46454452, 0x00000100, 0x00000000, 0x00000000, + 0x00000004, 0x0000001c, 0xffff0400, 0x00000100, 0x000000cb, 0x0000009c, + 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, + 0x00000001, 0x000000a7, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, + 0x00000000, 0x00000001, 0x0000000d, 0x000000b3, 0x00000002, 0x00000005, + 0x00000004, 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, 0x000000bf, + 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000002, 0x00000001, + 0x0000000d, 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, + 0x74005965, 0x65546568, 0x72757478, 0x74005565, 0x65546568, 0x72757478, + 0x4d005665, 0x6f726369, 0x74666f73, 0x29522820, 0x534c4820, 0x6853204c, + 0x72656461, 0x6d6f4320, 0x656c6970, 0x2e362072, 0x36392e33, 0x312e3030, + 0x34383336, 0xababab00, 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, + 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, + 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000303, + 0x00000065, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, + 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, + 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, + 0x45475241, 0xabab0054 +}; +#else +#error "An appropriate 'yuv' pixel shader is not defined." +#endif + +/* The yuv-rendering pixel shader: + + --- D3D11_PixelShader_YUV_BT601.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureU : register(t1); + Texture2D theTextureV : register(t2); + SamplerState theSampler : register(s0); + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.1644, 0.0000, 1.5960}; + const float3 Gcoeff = {1.1644, -0.3918, -0.8130}; + const float3 Bcoeff = {1.1644, 2.0172, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.y = theTextureU.Sample(theSampler, input.tex).r; + yuv.z = theTextureV.Sample(theSampler, input.tex).r; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } + +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_PixelShader_YUV_BT601[] = { + 0x43425844, 0x628ec838, 0xbe9cec6a, 0xc9ee10bb, 0x63283218, 0x00000001, + 0x000005e8, 0x00000006, 0x00000038, 0x000001dc, 0x000003bc, 0x00000438, + 0x00000540, 0x000005b4, 0x396e6f41, 0x0000019c, 0x0000019c, 0xffff0200, + 0x0000016c, 0x00000030, 0x00300000, 0x00300000, 0x00300000, 0x00240003, + 0x00300000, 0x00000000, 0x00010001, 0x00020002, 0xffff0200, 0x05000051, + 0xa00f0000, 0xbd808081, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, + 0xa00f0001, 0x3f950b0f, 0x3fcc49ba, 0x00000000, 0x00000000, 0x05000051, + 0xa00f0002, 0x3f950b0f, 0xbec89a02, 0xbf5020c5, 0x00000000, 0x05000051, + 0xa00f0003, 0x3f950b0f, 0x400119ce, 0x00000000, 0x00000000, 0x0200001f, + 0x80000000, 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, + 0x90000000, 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, + 0x90000000, 0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, + 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0002, + 0xb0e40000, 0xa0e40802, 0x02000001, 0x80020000, 0x80000001, 0x02000001, + 0x80040000, 0x80000002, 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, + 0x03000005, 0x80080000, 0x80000000, 0xa0000001, 0x04000004, 0x80010001, + 0x80aa0000, 0xa0550001, 0x80ff0000, 0x03000008, 0x80020001, 0x80e40000, + 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003, + 0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001, + 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff, 0x52444853, + 0x000001d8, 0x00000040, 0x00000076, 0x0300005a, 0x00106000, 0x00000000, + 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04001858, 0x00107000, + 0x00000001, 0x00005555, 0x04001858, 0x00107000, 0x00000002, 0x00005555, + 0x03001062, 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, + 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, + 0x001000f2, 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, + 0x00106000, 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, + 0x00000001, 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, + 0x00100022, 0x00000000, 0x0010000a, 0x00000001, 0x09000045, 0x001000f2, + 0x00000001, 0x00101046, 0x00000001, 0x00107e46, 0x00000002, 0x00106000, + 0x00000000, 0x05000036, 0x00100042, 0x00000000, 0x0010000a, 0x00000001, + 0x0a000000, 0x00100072, 0x00000000, 0x00100246, 0x00000000, 0x00004002, + 0xbd808081, 0xbf008081, 0xbf008081, 0x00000000, 0x0a00000f, 0x00100012, + 0x00000001, 0x00100086, 0x00000000, 0x00004002, 0x3f950b0f, 0x3fcc49ba, + 0x00000000, 0x00000000, 0x0a000010, 0x00100022, 0x00000001, 0x00100246, + 0x00000000, 0x00004002, 0x3f950b0f, 0xbec89a02, 0xbf5020c5, 0x00000000, + 0x0a00000f, 0x00100042, 0x00000001, 0x00100046, 0x00000000, 0x00004002, + 0x3f950b0f, 0x400119ce, 0x00000000, 0x00000000, 0x05000036, 0x00100082, + 0x00000001, 0x00004001, 0x3f800000, 0x07000038, 0x001020f2, 0x00000000, + 0x00100e46, 0x00000001, 0x00101e46, 0x00000002, 0x0100003e, 0x54415453, + 0x00000074, 0x0000000c, 0x00000002, 0x00000000, 0x00000003, 0x00000005, + 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x46454452, 0x00000100, 0x00000000, 0x00000000, 0x00000004, 0x0000001c, + 0xffff0400, 0x00000100, 0x000000cb, 0x0000009c, 0x00000003, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x000000a7, + 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000000, 0x00000001, + 0x0000000d, 0x000000b3, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, + 0x00000001, 0x00000001, 0x0000000d, 0x000000bf, 0x00000002, 0x00000005, + 0x00000004, 0xffffffff, 0x00000002, 0x00000001, 0x0000000d, 0x53656874, + 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x74005965, 0x65546568, + 0x72757478, 0x74005565, 0x65546568, 0x72757478, 0x4d005665, 0x6f726369, + 0x74666f73, 0x29522820, 0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320, + 0x656c6970, 0x2e362072, 0x36392e33, 0x312e3030, 0x34383336, 0xababab00, + 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, + 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, + 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000065, 0x00000000, + 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, + 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, + 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, + 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_PixelShader_YUV_BT601[] = { + 0x43425844, 0x692b159b, 0xf58723cc, 0xf4ceac9e, 0x35eec738, 0x00000001, + 0x000005c0, 0x00000006, 0x00000038, 0x000001b4, 0x00000394, 0x00000410, + 0x00000518, 0x0000058c, 0x396e6f41, 0x00000174, 0x00000174, 0xffff0200, + 0x00000144, 0x00000030, 0x00300000, 0x00300000, 0x00300000, 0x00240003, + 0x00300000, 0x00000000, 0x00010001, 0x00020002, 0xffff0201, 0x05000051, + 0xa00f0000, 0xbd808081, 0xbf008081, 0x3f800000, 0x00000000, 0x05000051, + 0xa00f0001, 0x3f950b0f, 0x3fcc49ba, 0x00000000, 0x400119ce, 0x05000051, + 0xa00f0002, 0x3f950b0f, 0xbec89a02, 0xbf5020c5, 0x00000000, 0x0200001f, + 0x80000000, 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, + 0x90000000, 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, + 0x90000000, 0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40801, + 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40800, 0x02000001, 0x80020001, + 0x80000000, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40802, 0x02000001, + 0x80040001, 0x80000000, 0x03000002, 0x80070000, 0x80e40001, 0xa0d40000, + 0x0400005a, 0x80010001, 0x80e80000, 0xa0e40001, 0xa0aa0001, 0x03000008, + 0x80020001, 0x80e40000, 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, + 0xa0ec0001, 0xa0aa0001, 0x02000001, 0x80080001, 0xa0aa0000, 0x03000005, + 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, + 0x0000ffff, 0x52444853, 0x000001d8, 0x00000040, 0x00000076, 0x0300005a, + 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, + 0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x04001858, 0x00107000, + 0x00000002, 0x00005555, 0x03001062, 0x00101032, 0x00000001, 0x03001062, + 0x001010f2, 0x00000002, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, + 0x00000002, 0x09000045, 0x001000f2, 0x00000000, 0x00101046, 0x00000001, + 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x09000045, 0x001000f2, + 0x00000001, 0x00101046, 0x00000001, 0x00107e46, 0x00000001, 0x00106000, + 0x00000000, 0x05000036, 0x00100022, 0x00000000, 0x0010000a, 0x00000001, + 0x09000045, 0x001000f2, 0x00000001, 0x00101046, 0x00000001, 0x00107e46, + 0x00000002, 0x00106000, 0x00000000, 0x05000036, 0x00100042, 0x00000000, + 0x0010000a, 0x00000001, 0x0a000000, 0x00100072, 0x00000000, 0x00100246, + 0x00000000, 0x00004002, 0xbd808081, 0xbf008081, 0xbf008081, 0x00000000, + 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, 0x00000000, 0x00004002, + 0x3f950b0f, 0x3fcc49ba, 0x00000000, 0x00000000, 0x0a000010, 0x00100022, + 0x00000001, 0x00100246, 0x00000000, 0x00004002, 0x3f950b0f, 0xbec89a02, + 0xbf5020c5, 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, 0x00100046, + 0x00000000, 0x00004002, 0x3f950b0f, 0x400119ce, 0x00000000, 0x00000000, + 0x05000036, 0x00100082, 0x00000001, 0x00004001, 0x3f800000, 0x07000038, + 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, 0x00000002, + 0x0100003e, 0x54415453, 0x00000074, 0x0000000c, 0x00000002, 0x00000000, + 0x00000003, 0x00000005, 0x00000000, 0x00000000, 0x00000001, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x46454452, 0x00000100, 0x00000000, 0x00000000, + 0x00000004, 0x0000001c, 0xffff0400, 0x00000100, 0x000000cb, 0x0000009c, + 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, + 0x00000001, 0x000000a7, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, + 0x00000000, 0x00000001, 0x0000000d, 0x000000b3, 0x00000002, 0x00000005, + 0x00000004, 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, 0x000000bf, + 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000002, 0x00000001, + 0x0000000d, 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, + 0x74005965, 0x65546568, 0x72757478, 0x74005565, 0x65546568, 0x72757478, + 0x4d005665, 0x6f726369, 0x74666f73, 0x29522820, 0x534c4820, 0x6853204c, + 0x72656461, 0x6d6f4320, 0x656c6970, 0x2e362072, 0x36392e33, 0x312e3030, + 0x34383336, 0xababab00, 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, + 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, + 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000303, + 0x00000065, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, + 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, + 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, + 0x45475241, 0xabab0054 +}; +#else +#error "An appropriate 'yuv' pixel shader is not defined." +#endif + +/* The yuv-rendering pixel shader: + + --- D3D11_PixelShader_YUV_BT709.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureU : register(t1); + Texture2D theTextureV : register(t2); + SamplerState theSampler : register(s0); + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.1644, 0.0000, 1.7927}; + const float3 Gcoeff = {1.1644, -0.2132, -0.5329}; + const float3 Bcoeff = {1.1644, 2.1124, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.y = theTextureU.Sample(theSampler, input.tex).r; + yuv.z = theTextureV.Sample(theSampler, input.tex).r; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } + +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_PixelShader_YUV_BT709[] = { + 0x43425844, 0x5045fa84, 0xc2908cce, 0x278dacc3, 0xd4276f8f, 0x00000001, + 0x000005e8, 0x00000006, 0x00000038, 0x000001dc, 0x000003bc, 0x00000438, + 0x00000540, 0x000005b4, 0x396e6f41, 0x0000019c, 0x0000019c, 0xffff0200, + 0x0000016c, 0x00000030, 0x00300000, 0x00300000, 0x00300000, 0x00240003, + 0x00300000, 0x00000000, 0x00010001, 0x00020002, 0xffff0200, 0x05000051, + 0xa00f0000, 0xbd808081, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, + 0xa00f0001, 0x3f950b0f, 0x3fe57732, 0x00000000, 0x00000000, 0x05000051, + 0xa00f0002, 0x3f950b0f, 0xbe5a511a, 0xbf086c22, 0x00000000, 0x05000051, + 0xa00f0003, 0x3f950b0f, 0x40073190, 0x00000000, 0x00000000, 0x0200001f, + 0x80000000, 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, + 0x90000000, 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, + 0x90000000, 0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40800, + 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0002, + 0xb0e40000, 0xa0e40802, 0x02000001, 0x80020000, 0x80000001, 0x02000001, + 0x80040000, 0x80000002, 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, + 0x03000005, 0x80080000, 0x80000000, 0xa0000001, 0x04000004, 0x80010001, + 0x80aa0000, 0xa0550001, 0x80ff0000, 0x03000008, 0x80020001, 0x80e40000, + 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003, + 0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001, + 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff, 0x52444853, + 0x000001d8, 0x00000040, 0x00000076, 0x0300005a, 0x00106000, 0x00000000, + 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04001858, 0x00107000, + 0x00000001, 0x00005555, 0x04001858, 0x00107000, 0x00000002, 0x00005555, + 0x03001062, 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, + 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, + 0x001000f2, 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, + 0x00106000, 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, + 0x00000001, 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, + 0x00100022, 0x00000000, 0x0010000a, 0x00000001, 0x09000045, 0x001000f2, + 0x00000001, 0x00101046, 0x00000001, 0x00107e46, 0x00000002, 0x00106000, + 0x00000000, 0x05000036, 0x00100042, 0x00000000, 0x0010000a, 0x00000001, + 0x0a000000, 0x00100072, 0x00000000, 0x00100246, 0x00000000, 0x00004002, + 0xbd808081, 0xbf008081, 0xbf008081, 0x00000000, 0x0a00000f, 0x00100012, + 0x00000001, 0x00100086, 0x00000000, 0x00004002, 0x3f950b0f, 0x3fe57732, + 0x00000000, 0x00000000, 0x0a000010, 0x00100022, 0x00000001, 0x00100246, + 0x00000000, 0x00004002, 0x3f950b0f, 0xbe5a511a, 0xbf086c22, 0x00000000, + 0x0a00000f, 0x00100042, 0x00000001, 0x00100046, 0x00000000, 0x00004002, + 0x3f950b0f, 0x40073190, 0x00000000, 0x00000000, 0x05000036, 0x00100082, + 0x00000001, 0x00004001, 0x3f800000, 0x07000038, 0x001020f2, 0x00000000, + 0x00100e46, 0x00000001, 0x00101e46, 0x00000002, 0x0100003e, 0x54415453, + 0x00000074, 0x0000000c, 0x00000002, 0x00000000, 0x00000003, 0x00000005, + 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x46454452, 0x00000100, 0x00000000, 0x00000000, 0x00000004, 0x0000001c, + 0xffff0400, 0x00000100, 0x000000cb, 0x0000009c, 0x00000003, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x000000a7, + 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000000, 0x00000001, + 0x0000000d, 0x000000b3, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, + 0x00000001, 0x00000001, 0x0000000d, 0x000000bf, 0x00000002, 0x00000005, + 0x00000004, 0xffffffff, 0x00000002, 0x00000001, 0x0000000d, 0x53656874, + 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x74005965, 0x65546568, + 0x72757478, 0x74005565, 0x65546568, 0x72757478, 0x4d005665, 0x6f726369, + 0x74666f73, 0x29522820, 0x534c4820, 0x6853204c, 0x72656461, 0x6d6f4320, + 0x656c6970, 0x2e362072, 0x36392e33, 0x312e3030, 0x34383336, 0xababab00, + 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, + 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, + 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000065, 0x00000000, + 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, + 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, + 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, + 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_PixelShader_YUV_BT709[] = { + 0x43425844, 0x72d13260, 0xf6c36f65, 0x8b9b28f5, 0x5010733c, 0x00000001, + 0x000005c0, 0x00000006, 0x00000038, 0x000001b4, 0x00000394, 0x00000410, + 0x00000518, 0x0000058c, 0x396e6f41, 0x00000174, 0x00000174, 0xffff0200, + 0x00000144, 0x00000030, 0x00300000, 0x00300000, 0x00300000, 0x00240003, + 0x00300000, 0x00000000, 0x00010001, 0x00020002, 0xffff0201, 0x05000051, + 0xa00f0000, 0xbd808081, 0xbf008081, 0x3f800000, 0x00000000, 0x05000051, + 0xa00f0001, 0x3f950b0f, 0x3fe57732, 0x00000000, 0x40073190, 0x05000051, + 0xa00f0002, 0x3f950b0f, 0xbe5a511a, 0xbf086c22, 0x00000000, 0x0200001f, + 0x80000000, 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, + 0x90000000, 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x0200001f, + 0x90000000, 0xa00f0802, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40801, + 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40800, 0x02000001, 0x80020001, + 0x80000000, 0x03000042, 0x800f0000, 0xb0e40000, 0xa0e40802, 0x02000001, + 0x80040001, 0x80000000, 0x03000002, 0x80070000, 0x80e40001, 0xa0d40000, + 0x0400005a, 0x80010001, 0x80e80000, 0xa0e40001, 0xa0aa0001, 0x03000008, + 0x80020001, 0x80e40000, 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, + 0xa0ec0001, 0xa0aa0001, 0x02000001, 0x80080001, 0xa0aa0000, 0x03000005, + 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, + 0x0000ffff, 0x52444853, 0x000001d8, 0x00000040, 0x00000076, 0x0300005a, + 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, + 0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x04001858, 0x00107000, + 0x00000002, 0x00005555, 0x03001062, 0x00101032, 0x00000001, 0x03001062, + 0x001010f2, 0x00000002, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, + 0x00000002, 0x09000045, 0x001000f2, 0x00000000, 0x00101046, 0x00000001, + 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x09000045, 0x001000f2, + 0x00000001, 0x00101046, 0x00000001, 0x00107e46, 0x00000001, 0x00106000, + 0x00000000, 0x05000036, 0x00100022, 0x00000000, 0x0010000a, 0x00000001, + 0x09000045, 0x001000f2, 0x00000001, 0x00101046, 0x00000001, 0x00107e46, + 0x00000002, 0x00106000, 0x00000000, 0x05000036, 0x00100042, 0x00000000, + 0x0010000a, 0x00000001, 0x0a000000, 0x00100072, 0x00000000, 0x00100246, + 0x00000000, 0x00004002, 0xbd808081, 0xbf008081, 0xbf008081, 0x00000000, + 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, 0x00000000, 0x00004002, + 0x3f950b0f, 0x3fe57732, 0x00000000, 0x00000000, 0x0a000010, 0x00100022, + 0x00000001, 0x00100246, 0x00000000, 0x00004002, 0x3f950b0f, 0xbe5a511a, + 0xbf086c22, 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, 0x00100046, + 0x00000000, 0x00004002, 0x3f950b0f, 0x40073190, 0x00000000, 0x00000000, + 0x05000036, 0x00100082, 0x00000001, 0x00004001, 0x3f800000, 0x07000038, + 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, 0x00000002, + 0x0100003e, 0x54415453, 0x00000074, 0x0000000c, 0x00000002, 0x00000000, + 0x00000003, 0x00000005, 0x00000000, 0x00000000, 0x00000001, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x46454452, 0x00000100, 0x00000000, 0x00000000, + 0x00000004, 0x0000001c, 0xffff0400, 0x00000100, 0x000000cb, 0x0000009c, + 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, + 0x00000001, 0x000000a7, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, + 0x00000000, 0x00000001, 0x0000000d, 0x000000b3, 0x00000002, 0x00000005, + 0x00000004, 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, 0x000000bf, + 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000002, 0x00000001, + 0x0000000d, 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, + 0x74005965, 0x65546568, 0x72757478, 0x74005565, 0x65546568, 0x72757478, + 0x4d005665, 0x6f726369, 0x74666f73, 0x29522820, 0x534c4820, 0x6853204c, + 0x72656461, 0x6d6f4320, 0x656c6970, 0x2e362072, 0x36392e33, 0x312e3030, + 0x34383336, 0xababab00, 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, + 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, + 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000303, + 0x00000065, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, + 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, + 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, + 0x45475241, 0xabab0054 +}; +#else +#error "An appropriate 'yuv' pixel shader is not defined." +#endif + +/* The yuv-rendering pixel shader: + + --- D3D11_PixelShader_NV12_JPEG.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureUV : register(t1); + SamplerState theSampler : register(s0); + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {0.0, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.0000, 0.0000, 1.4020}; + const float3 Gcoeff = {1.0000, -0.3441, -0.7141}; + const float3 Bcoeff = {1.0000, 1.7720, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.yz = theTextureUV.Sample(theSampler, input.tex).rg; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } + +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_PixelShader_NV12_JPEG[] = { + 0x43425844, 0x8fb9c77a, 0xe9e39686, 0x62b0e0e9, 0xd2bf8183, 0x00000001, + 0x00000548, 0x00000006, 0x00000038, 0x000001b0, 0x00000348, 0x000003c4, + 0x000004a0, 0x00000514, 0x396e6f41, 0x00000170, 0x00000170, 0xffff0200, + 0x00000144, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0200, 0x05000051, 0xa00f0000, + 0x00000000, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001, + 0x3f800000, 0x3fb374bc, 0x00000000, 0x00000000, 0x05000051, 0xa00f0002, + 0x3f800000, 0xbeb02de0, 0xbf36cf42, 0x00000000, 0x05000051, 0xa00f0003, + 0x3f800000, 0x3fe2d0e5, 0x00000000, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40800, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40801, + 0x02000001, 0x80060000, 0x80d20001, 0x03000002, 0x80070000, 0x80e40000, + 0xa0e40000, 0x03000005, 0x80080000, 0x80000000, 0xa0000001, 0x04000004, + 0x80010001, 0x80aa0000, 0xa0550001, 0x80ff0000, 0x03000008, 0x80020001, + 0x80e40000, 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, + 0xa0aa0003, 0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, + 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff, + 0x52444853, 0x00000190, 0x00000040, 0x00000064, 0x0300005a, 0x00106000, + 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04001858, + 0x00107000, 0x00000001, 0x00005555, 0x03001062, 0x00101032, 0x00000001, + 0x03001062, 0x001010f2, 0x00000002, 0x03000065, 0x001020f2, 0x00000000, + 0x02000068, 0x00000002, 0x09000045, 0x001000f2, 0x00000000, 0x00101046, + 0x00000001, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x09000045, + 0x001000f2, 0x00000001, 0x00101046, 0x00000001, 0x00107e46, 0x00000001, + 0x00106000, 0x00000000, 0x05000036, 0x00100062, 0x00000000, 0x00100106, + 0x00000001, 0x0a000000, 0x00100072, 0x00000000, 0x00100246, 0x00000000, + 0x00004002, 0x00000000, 0xbf008081, 0xbf008081, 0x00000000, 0x0a00000f, + 0x00100012, 0x00000001, 0x00100086, 0x00000000, 0x00004002, 0x3f800000, + 0x3fb374bc, 0x00000000, 0x00000000, 0x0a000010, 0x00100022, 0x00000001, + 0x00100246, 0x00000000, 0x00004002, 0x3f800000, 0xbeb02de0, 0xbf36cf42, + 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, 0x00100046, 0x00000000, + 0x00004002, 0x3f800000, 0x3fe2d0e5, 0x00000000, 0x00000000, 0x05000036, + 0x00100082, 0x00000001, 0x00004001, 0x3f800000, 0x07000038, 0x001020f2, + 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, 0x00000002, 0x0100003e, + 0x54415453, 0x00000074, 0x0000000a, 0x00000002, 0x00000000, 0x00000003, + 0x00000005, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x46454452, 0x000000d4, 0x00000000, 0x00000000, 0x00000003, + 0x0000001c, 0xffff0400, 0x00000100, 0x000000a0, 0x0000007c, 0x00000003, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, + 0x00000087, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000000, + 0x00000001, 0x0000000d, 0x00000093, 0x00000002, 0x00000005, 0x00000004, + 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, 0x53656874, 0x6c706d61, + 0x74007265, 0x65546568, 0x72757478, 0x74005965, 0x65546568, 0x72757478, + 0x00565565, 0x7263694d, 0x666f736f, 0x52282074, 0x4c482029, 0x53204c53, + 0x65646168, 0x6f432072, 0x6c69706d, 0x36207265, 0x392e332e, 0x2e303036, + 0x38333631, 0xabab0034, 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, + 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, + 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000303, + 0x00000065, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, + 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, + 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, + 0x45475241, 0xabab0054 +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_PixelShader_NV12_JPEG[] = { + 0x43425844, 0xe33e5d8b, 0x1b5f6461, 0x1afee99f, 0xcc345c04, 0x00000001, + 0x00000520, 0x00000006, 0x00000038, 0x00000188, 0x00000320, 0x0000039c, + 0x00000478, 0x000004ec, 0x396e6f41, 0x00000148, 0x00000148, 0xffff0200, + 0x0000011c, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0201, 0x05000051, 0xa00f0000, + 0x00000000, 0xbf008081, 0x3f800000, 0x3fb374bc, 0x05000051, 0xa00f0001, + 0x3f800000, 0xbeb02de0, 0xbf36cf42, 0x00000000, 0x05000051, 0xa00f0002, + 0x3f800000, 0x3fe2d0e5, 0x00000000, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40800, + 0x02000001, 0x80060001, 0x80d00000, 0x03000002, 0x80070000, 0x80e40001, + 0xa0d40000, 0x0400005a, 0x80010001, 0x80e80000, 0xa0ee0000, 0xa0000000, + 0x03000008, 0x80020001, 0x80e40000, 0xa0e40001, 0x0400005a, 0x80040001, + 0x80e40000, 0xa0e40002, 0xa0aa0002, 0x02000001, 0x80080001, 0xa0aa0000, + 0x03000005, 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, + 0x80e40000, 0x0000ffff, 0x52444853, 0x00000190, 0x00000040, 0x00000064, + 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, + 0x00005555, 0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x03001062, + 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, + 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, 0x001000f2, + 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, 0x00106000, + 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, 0x00000001, + 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, 0x00100062, + 0x00000000, 0x00100106, 0x00000001, 0x0a000000, 0x00100072, 0x00000000, + 0x00100246, 0x00000000, 0x00004002, 0x00000000, 0xbf008081, 0xbf008081, + 0x00000000, 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, 0x00000000, + 0x00004002, 0x3f800000, 0x3fb374bc, 0x00000000, 0x00000000, 0x0a000010, + 0x00100022, 0x00000001, 0x00100246, 0x00000000, 0x00004002, 0x3f800000, + 0xbeb02de0, 0xbf36cf42, 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, + 0x00100046, 0x00000000, 0x00004002, 0x3f800000, 0x3fe2d0e5, 0x00000000, + 0x00000000, 0x05000036, 0x00100082, 0x00000001, 0x00004001, 0x3f800000, + 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, + 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x0000000a, 0x00000002, + 0x00000000, 0x00000003, 0x00000005, 0x00000000, 0x00000000, 0x00000001, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000d4, 0x00000000, + 0x00000000, 0x00000003, 0x0000001c, 0xffff0400, 0x00000100, 0x000000a0, + 0x0000007c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000001, 0x00000001, 0x00000087, 0x00000002, 0x00000005, 0x00000004, + 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x00000093, 0x00000002, + 0x00000005, 0x00000004, 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, + 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x74005965, + 0x65546568, 0x72757478, 0x00565565, 0x7263694d, 0x666f736f, 0x52282074, + 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, 0x6c69706d, 0x36207265, + 0x392e332e, 0x2e303036, 0x38333631, 0xabab0034, 0x4e475349, 0x0000006c, + 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, + 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, + 0x00000001, 0x00000303, 0x00000065, 0x00000000, 0x00000000, 0x00000003, + 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, + 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, + 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, + 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#else +#error "An appropriate 'yuv' pixel shader is not defined." +#endif + +/* The yuv-rendering pixel shader: + + --- D3D11_PixelShader_NV12_BT601.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureUV : register(t1); + SamplerState theSampler : register(s0); + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.1644, 0.0000, 1.5960}; + const float3 Gcoeff = {1.1644, -0.3918, -0.8130}; + const float3 Bcoeff = {1.1644, 2.0172, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.yz = theTextureUV.Sample(theSampler, input.tex).rg; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } + +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_PixelShader_NV12_BT601[] = { + 0x43425844, 0xd1d24a0c, 0x337c447a, 0x22b55cff, 0xb5c9c74b, 0x00000001, + 0x00000548, 0x00000006, 0x00000038, 0x000001b0, 0x00000348, 0x000003c4, + 0x000004a0, 0x00000514, 0x396e6f41, 0x00000170, 0x00000170, 0xffff0200, + 0x00000144, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0200, 0x05000051, 0xa00f0000, + 0xbd808081, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001, + 0x3f950b0f, 0x3fcc49ba, 0x00000000, 0x00000000, 0x05000051, 0xa00f0002, + 0x3f950b0f, 0xbec89a02, 0xbf5020c5, 0x00000000, 0x05000051, 0xa00f0003, + 0x3f950b0f, 0x400119ce, 0x00000000, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40800, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40801, + 0x02000001, 0x80060000, 0x80d20001, 0x03000002, 0x80070000, 0x80e40000, + 0xa0e40000, 0x03000005, 0x80080000, 0x80000000, 0xa0000001, 0x04000004, + 0x80010001, 0x80aa0000, 0xa0550001, 0x80ff0000, 0x03000008, 0x80020001, + 0x80e40000, 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, + 0xa0aa0003, 0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, + 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff, + 0x52444853, 0x00000190, 0x00000040, 0x00000064, 0x0300005a, 0x00106000, + 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04001858, + 0x00107000, 0x00000001, 0x00005555, 0x03001062, 0x00101032, 0x00000001, + 0x03001062, 0x001010f2, 0x00000002, 0x03000065, 0x001020f2, 0x00000000, + 0x02000068, 0x00000002, 0x09000045, 0x001000f2, 0x00000000, 0x00101046, + 0x00000001, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x09000045, + 0x001000f2, 0x00000001, 0x00101046, 0x00000001, 0x00107e46, 0x00000001, + 0x00106000, 0x00000000, 0x05000036, 0x00100062, 0x00000000, 0x00100106, + 0x00000001, 0x0a000000, 0x00100072, 0x00000000, 0x00100246, 0x00000000, + 0x00004002, 0xbd808081, 0xbf008081, 0xbf008081, 0x00000000, 0x0a00000f, + 0x00100012, 0x00000001, 0x00100086, 0x00000000, 0x00004002, 0x3f950b0f, + 0x3fcc49ba, 0x00000000, 0x00000000, 0x0a000010, 0x00100022, 0x00000001, + 0x00100246, 0x00000000, 0x00004002, 0x3f950b0f, 0xbec89a02, 0xbf5020c5, + 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, 0x00100046, 0x00000000, + 0x00004002, 0x3f950b0f, 0x400119ce, 0x00000000, 0x00000000, 0x05000036, + 0x00100082, 0x00000001, 0x00004001, 0x3f800000, 0x07000038, 0x001020f2, + 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, 0x00000002, 0x0100003e, + 0x54415453, 0x00000074, 0x0000000a, 0x00000002, 0x00000000, 0x00000003, + 0x00000005, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x46454452, 0x000000d4, 0x00000000, 0x00000000, 0x00000003, + 0x0000001c, 0xffff0400, 0x00000100, 0x000000a0, 0x0000007c, 0x00000003, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, + 0x00000087, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000000, + 0x00000001, 0x0000000d, 0x00000093, 0x00000002, 0x00000005, 0x00000004, + 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, 0x53656874, 0x6c706d61, + 0x74007265, 0x65546568, 0x72757478, 0x74005965, 0x65546568, 0x72757478, + 0x00565565, 0x7263694d, 0x666f736f, 0x52282074, 0x4c482029, 0x53204c53, + 0x65646168, 0x6f432072, 0x6c69706d, 0x36207265, 0x392e332e, 0x2e303036, + 0x38333631, 0xabab0034, 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, + 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, + 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000303, + 0x00000065, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, + 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, + 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, + 0x45475241, 0xabab0054 +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_PixelShader_NV12_BT601[] = { + 0x43425844, 0x84b8b692, 0x589b9edd, 0x51ef2f0b, 0xf7247962, 0x00000001, + 0x00000520, 0x00000006, 0x00000038, 0x00000188, 0x00000320, 0x0000039c, + 0x00000478, 0x000004ec, 0x396e6f41, 0x00000148, 0x00000148, 0xffff0200, + 0x0000011c, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0201, 0x05000051, 0xa00f0000, + 0xbd808081, 0xbf008081, 0x3f800000, 0x00000000, 0x05000051, 0xa00f0001, + 0x3f950b0f, 0x3fcc49ba, 0x00000000, 0x400119ce, 0x05000051, 0xa00f0002, + 0x3f950b0f, 0xbec89a02, 0xbf5020c5, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40800, + 0x02000001, 0x80060001, 0x80d00000, 0x03000002, 0x80070000, 0x80e40001, + 0xa0d40000, 0x0400005a, 0x80010001, 0x80e80000, 0xa0e40001, 0xa0aa0001, + 0x03000008, 0x80020001, 0x80e40000, 0xa0e40002, 0x0400005a, 0x80040001, + 0x80e40000, 0xa0ec0001, 0xa0aa0001, 0x02000001, 0x80080001, 0xa0aa0000, + 0x03000005, 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, + 0x80e40000, 0x0000ffff, 0x52444853, 0x00000190, 0x00000040, 0x00000064, + 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, + 0x00005555, 0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x03001062, + 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, + 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, 0x001000f2, + 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, 0x00106000, + 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, 0x00000001, + 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, 0x00100062, + 0x00000000, 0x00100106, 0x00000001, 0x0a000000, 0x00100072, 0x00000000, + 0x00100246, 0x00000000, 0x00004002, 0xbd808081, 0xbf008081, 0xbf008081, + 0x00000000, 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, 0x00000000, + 0x00004002, 0x3f950b0f, 0x3fcc49ba, 0x00000000, 0x00000000, 0x0a000010, + 0x00100022, 0x00000001, 0x00100246, 0x00000000, 0x00004002, 0x3f950b0f, + 0xbec89a02, 0xbf5020c5, 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, + 0x00100046, 0x00000000, 0x00004002, 0x3f950b0f, 0x400119ce, 0x00000000, + 0x00000000, 0x05000036, 0x00100082, 0x00000001, 0x00004001, 0x3f800000, + 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, + 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x0000000a, 0x00000002, + 0x00000000, 0x00000003, 0x00000005, 0x00000000, 0x00000000, 0x00000001, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000d4, 0x00000000, + 0x00000000, 0x00000003, 0x0000001c, 0xffff0400, 0x00000100, 0x000000a0, + 0x0000007c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000001, 0x00000001, 0x00000087, 0x00000002, 0x00000005, 0x00000004, + 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x00000093, 0x00000002, + 0x00000005, 0x00000004, 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, + 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x74005965, + 0x65546568, 0x72757478, 0x00565565, 0x7263694d, 0x666f736f, 0x52282074, + 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, 0x6c69706d, 0x36207265, + 0x392e332e, 0x2e303036, 0x38333631, 0xabab0034, 0x4e475349, 0x0000006c, + 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, + 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, + 0x00000001, 0x00000303, 0x00000065, 0x00000000, 0x00000000, 0x00000003, + 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, + 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, + 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, + 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#else +#error "An appropriate 'yuv' pixel shader is not defined." +#endif + +/* The yuv-rendering pixel shader: + + --- D3D11_PixelShader_NV12_BT709.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureUV : register(t1); + SamplerState theSampler : register(s0); + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.1644, 0.0000, 1.7927}; + const float3 Gcoeff = {1.1644, -0.2132, -0.5329}; + const float3 Bcoeff = {1.1644, 2.1124, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.yz = theTextureUV.Sample(theSampler, input.tex).rg; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } + +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_PixelShader_NV12_BT709[] = { + 0x43425844, 0x40d1b8d5, 0xaf4b78b5, 0x907fd0b5, 0xa2d23686, 0x00000001, + 0x00000548, 0x00000006, 0x00000038, 0x000001b0, 0x00000348, 0x000003c4, + 0x000004a0, 0x00000514, 0x396e6f41, 0x00000170, 0x00000170, 0xffff0200, + 0x00000144, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0200, 0x05000051, 0xa00f0000, + 0xbd808081, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001, + 0x3f950b0f, 0x3fe57732, 0x00000000, 0x00000000, 0x05000051, 0xa00f0002, + 0x3f950b0f, 0xbe5a511a, 0xbf086c22, 0x00000000, 0x05000051, 0xa00f0003, + 0x3f950b0f, 0x40073190, 0x00000000, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40800, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40801, + 0x02000001, 0x80060000, 0x80d20001, 0x03000002, 0x80070000, 0x80e40000, + 0xa0e40000, 0x03000005, 0x80080000, 0x80000000, 0xa0000001, 0x04000004, + 0x80010001, 0x80aa0000, 0xa0550001, 0x80ff0000, 0x03000008, 0x80020001, + 0x80e40000, 0xa0e40002, 0x0400005a, 0x80040001, 0x80e40000, 0xa0e40003, + 0xa0aa0003, 0x02000001, 0x80080001, 0xa0ff0000, 0x03000005, 0x800f0000, + 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, 0x80e40000, 0x0000ffff, + 0x52444853, 0x00000190, 0x00000040, 0x00000064, 0x0300005a, 0x00106000, + 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04001858, + 0x00107000, 0x00000001, 0x00005555, 0x03001062, 0x00101032, 0x00000001, + 0x03001062, 0x001010f2, 0x00000002, 0x03000065, 0x001020f2, 0x00000000, + 0x02000068, 0x00000002, 0x09000045, 0x001000f2, 0x00000000, 0x00101046, + 0x00000001, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x09000045, + 0x001000f2, 0x00000001, 0x00101046, 0x00000001, 0x00107e46, 0x00000001, + 0x00106000, 0x00000000, 0x05000036, 0x00100062, 0x00000000, 0x00100106, + 0x00000001, 0x0a000000, 0x00100072, 0x00000000, 0x00100246, 0x00000000, + 0x00004002, 0xbd808081, 0xbf008081, 0xbf008081, 0x00000000, 0x0a00000f, + 0x00100012, 0x00000001, 0x00100086, 0x00000000, 0x00004002, 0x3f950b0f, + 0x3fe57732, 0x00000000, 0x00000000, 0x0a000010, 0x00100022, 0x00000001, + 0x00100246, 0x00000000, 0x00004002, 0x3f950b0f, 0xbe5a511a, 0xbf086c22, + 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, 0x00100046, 0x00000000, + 0x00004002, 0x3f950b0f, 0x40073190, 0x00000000, 0x00000000, 0x05000036, + 0x00100082, 0x00000001, 0x00004001, 0x3f800000, 0x07000038, 0x001020f2, + 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, 0x00000002, 0x0100003e, + 0x54415453, 0x00000074, 0x0000000a, 0x00000002, 0x00000000, 0x00000003, + 0x00000005, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x46454452, 0x000000d4, 0x00000000, 0x00000000, 0x00000003, + 0x0000001c, 0xffff0400, 0x00000100, 0x000000a0, 0x0000007c, 0x00000003, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000001, + 0x00000087, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000000, + 0x00000001, 0x0000000d, 0x00000093, 0x00000002, 0x00000005, 0x00000004, + 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, 0x53656874, 0x6c706d61, + 0x74007265, 0x65546568, 0x72757478, 0x74005965, 0x65546568, 0x72757478, + 0x00565565, 0x7263694d, 0x666f736f, 0x52282074, 0x4c482029, 0x53204c53, + 0x65646168, 0x6f432072, 0x6c69706d, 0x36207265, 0x392e332e, 0x2e303036, + 0x38333631, 0xabab0034, 0x4e475349, 0x0000006c, 0x00000003, 0x00000008, + 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, + 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000303, + 0x00000065, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, + 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, 0x44524f4f, 0x4c4f4300, + 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, + 0x45475241, 0xabab0054 +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_PixelShader_NV12_BT709[] = { + 0x43425844, 0xa3bba187, 0x71b6afa9, 0x15998682, 0x2d545cae, 0x00000001, + 0x00000520, 0x00000006, 0x00000038, 0x00000188, 0x00000320, 0x0000039c, + 0x00000478, 0x000004ec, 0x396e6f41, 0x00000148, 0x00000148, 0xffff0200, + 0x0000011c, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0201, 0x05000051, 0xa00f0000, + 0xbd808081, 0xbf008081, 0x3f800000, 0x00000000, 0x05000051, 0xa00f0001, + 0x3f950b0f, 0x3fe57732, 0x00000000, 0x40073190, 0x05000051, 0xa00f0002, + 0x3f950b0f, 0xbe5a511a, 0xbf086c22, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40800, + 0x02000001, 0x80060001, 0x80d00000, 0x03000002, 0x80070000, 0x80e40001, + 0xa0d40000, 0x0400005a, 0x80010001, 0x80e80000, 0xa0e40001, 0xa0aa0001, + 0x03000008, 0x80020001, 0x80e40000, 0xa0e40002, 0x0400005a, 0x80040001, + 0x80e40000, 0xa0ec0001, 0xa0aa0001, 0x02000001, 0x80080001, 0xa0aa0000, + 0x03000005, 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, + 0x80e40000, 0x0000ffff, 0x52444853, 0x00000190, 0x00000040, 0x00000064, + 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, + 0x00005555, 0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x03001062, + 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, + 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, 0x001000f2, + 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, 0x00106000, + 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, 0x00000001, + 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, 0x00100062, + 0x00000000, 0x00100106, 0x00000001, 0x0a000000, 0x00100072, 0x00000000, + 0x00100246, 0x00000000, 0x00004002, 0xbd808081, 0xbf008081, 0xbf008081, + 0x00000000, 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, 0x00000000, + 0x00004002, 0x3f950b0f, 0x3fe57732, 0x00000000, 0x00000000, 0x0a000010, + 0x00100022, 0x00000001, 0x00100246, 0x00000000, 0x00004002, 0x3f950b0f, + 0xbe5a511a, 0xbf086c22, 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, + 0x00100046, 0x00000000, 0x00004002, 0x3f950b0f, 0x40073190, 0x00000000, + 0x00000000, 0x05000036, 0x00100082, 0x00000001, 0x00004001, 0x3f800000, + 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, + 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x0000000a, 0x00000002, + 0x00000000, 0x00000003, 0x00000005, 0x00000000, 0x00000000, 0x00000001, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000d4, 0x00000000, + 0x00000000, 0x00000003, 0x0000001c, 0xffff0400, 0x00000100, 0x000000a0, + 0x0000007c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000001, 0x00000001, 0x00000087, 0x00000002, 0x00000005, 0x00000004, + 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x00000093, 0x00000002, + 0x00000005, 0x00000004, 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, + 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x74005965, + 0x65546568, 0x72757478, 0x00565565, 0x7263694d, 0x666f736f, 0x52282074, + 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, 0x6c69706d, 0x36207265, + 0x392e332e, 0x2e303036, 0x38333631, 0xabab0034, 0x4e475349, 0x0000006c, + 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, + 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, + 0x00000001, 0x00000303, 0x00000065, 0x00000000, 0x00000000, 0x00000003, + 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, + 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, + 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, + 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#else +#error "An appropriate 'yuv' pixel shader is not defined." +#endif + +/* The yuv-rendering pixel shader: + + --- D3D11_PixelShader_NV21_JPEG.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureUV : register(t1); + SamplerState theSampler : register(s0); + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {0.0, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.0000, 0.0000, 1.4020}; + const float3 Gcoeff = {1.0000, -0.3441, -0.7141}; + const float3 Bcoeff = {1.0000, 1.7720, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.yz = theTextureUV.Sample(theSampler, input.tex).gr; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } + +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_PixelShader_NV21_JPEG[] = { + 0x43425844, 0x9c41f579, 0xfd1019d8, 0x7c27e3ae, 0x52e3a5ff, 0x00000001, + 0x00000554, 0x00000006, 0x00000038, 0x000001bc, 0x00000354, 0x000003d0, + 0x000004ac, 0x00000520, 0x396e6f41, 0x0000017c, 0x0000017c, 0xffff0200, + 0x00000150, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0200, 0x05000051, 0xa00f0000, + 0x00000000, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001, + 0x3f800000, 0x3fb374bc, 0x00000000, 0x00000000, 0x05000051, 0xa00f0002, + 0x3f800000, 0xbeb02de0, 0xbf36cf42, 0x00000000, 0x05000051, 0xa00f0003, + 0x3f800000, 0x3fe2d0e5, 0x00000000, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40800, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40801, + 0x02000001, 0x80020000, 0x80550001, 0x02000001, 0x80040000, 0x80000001, + 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, 0x03000005, 0x80080000, + 0x80000000, 0xa0000001, 0x04000004, 0x80010001, 0x80aa0000, 0xa0550001, + 0x80ff0000, 0x03000008, 0x80020001, 0x80e40000, 0xa0e40002, 0x0400005a, + 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003, 0x02000001, 0x80080001, + 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, + 0x800f0800, 0x80e40000, 0x0000ffff, 0x52444853, 0x00000190, 0x00000040, + 0x00000064, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, + 0x00000000, 0x00005555, 0x04001858, 0x00107000, 0x00000001, 0x00005555, + 0x03001062, 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, + 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, + 0x001000f2, 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, + 0x00106000, 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, + 0x00000001, 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, + 0x00100062, 0x00000000, 0x00100456, 0x00000001, 0x0a000000, 0x00100072, + 0x00000000, 0x00100246, 0x00000000, 0x00004002, 0x00000000, 0xbf008081, + 0xbf008081, 0x00000000, 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, + 0x00000000, 0x00004002, 0x3f800000, 0x3fb374bc, 0x00000000, 0x00000000, + 0x0a000010, 0x00100022, 0x00000001, 0x00100246, 0x00000000, 0x00004002, + 0x3f800000, 0xbeb02de0, 0xbf36cf42, 0x00000000, 0x0a00000f, 0x00100042, + 0x00000001, 0x00100046, 0x00000000, 0x00004002, 0x3f800000, 0x3fe2d0e5, + 0x00000000, 0x00000000, 0x05000036, 0x00100082, 0x00000001, 0x00004001, + 0x3f800000, 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, + 0x00101e46, 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x0000000a, + 0x00000002, 0x00000000, 0x00000003, 0x00000005, 0x00000000, 0x00000000, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000d4, + 0x00000000, 0x00000000, 0x00000003, 0x0000001c, 0xffff0400, 0x00000100, + 0x000000a0, 0x0000007c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000001, 0x00000001, 0x00000087, 0x00000002, 0x00000005, + 0x00000004, 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x00000093, + 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000001, 0x00000001, + 0x0000000d, 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, + 0x74005965, 0x65546568, 0x72757478, 0x00565565, 0x7263694d, 0x666f736f, + 0x52282074, 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, 0x6c69706d, + 0x36207265, 0x392e332e, 0x2e303036, 0x38333631, 0xabab0034, 0x4e475349, + 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, + 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, + 0x00000003, 0x00000001, 0x00000303, 0x00000065, 0x00000000, 0x00000000, + 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, + 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, 0x0000002c, + 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, + 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_PixelShader_NV21_JPEG[] = { + 0x43425844, 0x5705ccc9, 0xeb57571d, 0x8ce556e0, 0x2adef743, 0x00000001, + 0x00000520, 0x00000006, 0x00000038, 0x00000188, 0x00000320, 0x0000039c, + 0x00000478, 0x000004ec, 0x396e6f41, 0x00000148, 0x00000148, 0xffff0200, + 0x0000011c, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0201, 0x05000051, 0xa00f0000, + 0x00000000, 0xbf008081, 0x3f800000, 0x3fb374bc, 0x05000051, 0xa00f0001, + 0x3f800000, 0xbeb02de0, 0xbf36cf42, 0x00000000, 0x05000051, 0xa00f0002, + 0x3f800000, 0x3fe2d0e5, 0x00000000, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40800, + 0x02000001, 0x80060001, 0x80c40000, 0x03000002, 0x80070000, 0x80e40001, + 0xa0d40000, 0x0400005a, 0x80010001, 0x80e80000, 0xa0ee0000, 0xa0000000, + 0x03000008, 0x80020001, 0x80e40000, 0xa0e40001, 0x0400005a, 0x80040001, + 0x80e40000, 0xa0e40002, 0xa0aa0002, 0x02000001, 0x80080001, 0xa0aa0000, + 0x03000005, 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, + 0x80e40000, 0x0000ffff, 0x52444853, 0x00000190, 0x00000040, 0x00000064, + 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, + 0x00005555, 0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x03001062, + 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, + 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, 0x001000f2, + 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, 0x00106000, + 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, 0x00000001, + 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, 0x00100062, + 0x00000000, 0x00100456, 0x00000001, 0x0a000000, 0x00100072, 0x00000000, + 0x00100246, 0x00000000, 0x00004002, 0x00000000, 0xbf008081, 0xbf008081, + 0x00000000, 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, 0x00000000, + 0x00004002, 0x3f800000, 0x3fb374bc, 0x00000000, 0x00000000, 0x0a000010, + 0x00100022, 0x00000001, 0x00100246, 0x00000000, 0x00004002, 0x3f800000, + 0xbeb02de0, 0xbf36cf42, 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, + 0x00100046, 0x00000000, 0x00004002, 0x3f800000, 0x3fe2d0e5, 0x00000000, + 0x00000000, 0x05000036, 0x00100082, 0x00000001, 0x00004001, 0x3f800000, + 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, + 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x0000000a, 0x00000002, + 0x00000000, 0x00000003, 0x00000005, 0x00000000, 0x00000000, 0x00000001, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000d4, 0x00000000, + 0x00000000, 0x00000003, 0x0000001c, 0xffff0400, 0x00000100, 0x000000a0, + 0x0000007c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000001, 0x00000001, 0x00000087, 0x00000002, 0x00000005, 0x00000004, + 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x00000093, 0x00000002, + 0x00000005, 0x00000004, 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, + 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x74005965, + 0x65546568, 0x72757478, 0x00565565, 0x7263694d, 0x666f736f, 0x52282074, + 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, 0x6c69706d, 0x36207265, + 0x392e332e, 0x2e303036, 0x38333631, 0xabab0034, 0x4e475349, 0x0000006c, + 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, + 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, + 0x00000001, 0x00000303, 0x00000065, 0x00000000, 0x00000000, 0x00000003, + 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, + 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, + 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, + 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#else +#error "An appropriate 'yuv' pixel shader is not defined." +#endif + +/* The yuv-rendering pixel shader: + + --- D3D11_PixelShader_NV21_BT601.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureUV : register(t1); + SamplerState theSampler : register(s0); + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.1644, 0.0000, 1.5960}; + const float3 Gcoeff = {1.1644, -0.3918, -0.8130}; + const float3 Bcoeff = {1.1644, 2.0172, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.yz = theTextureUV.Sample(theSampler, input.tex).gr; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } + +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_PixelShader_NV21_BT601[] = { + 0x43425844, 0x7fc6cfdc, 0xba87a4ff, 0xa72685a6, 0xa051b38c, 0x00000001, + 0x00000554, 0x00000006, 0x00000038, 0x000001bc, 0x00000354, 0x000003d0, + 0x000004ac, 0x00000520, 0x396e6f41, 0x0000017c, 0x0000017c, 0xffff0200, + 0x00000150, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0200, 0x05000051, 0xa00f0000, + 0xbd808081, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001, + 0x3f950b0f, 0x3fcc49ba, 0x00000000, 0x00000000, 0x05000051, 0xa00f0002, + 0x3f950b0f, 0xbec89a02, 0xbf5020c5, 0x00000000, 0x05000051, 0xa00f0003, + 0x3f950b0f, 0x400119ce, 0x00000000, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40800, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40801, + 0x02000001, 0x80020000, 0x80550001, 0x02000001, 0x80040000, 0x80000001, + 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, 0x03000005, 0x80080000, + 0x80000000, 0xa0000001, 0x04000004, 0x80010001, 0x80aa0000, 0xa0550001, + 0x80ff0000, 0x03000008, 0x80020001, 0x80e40000, 0xa0e40002, 0x0400005a, + 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003, 0x02000001, 0x80080001, + 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, + 0x800f0800, 0x80e40000, 0x0000ffff, 0x52444853, 0x00000190, 0x00000040, + 0x00000064, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, + 0x00000000, 0x00005555, 0x04001858, 0x00107000, 0x00000001, 0x00005555, + 0x03001062, 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, + 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, + 0x001000f2, 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, + 0x00106000, 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, + 0x00000001, 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, + 0x00100062, 0x00000000, 0x00100456, 0x00000001, 0x0a000000, 0x00100072, + 0x00000000, 0x00100246, 0x00000000, 0x00004002, 0xbd808081, 0xbf008081, + 0xbf008081, 0x00000000, 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, + 0x00000000, 0x00004002, 0x3f950b0f, 0x3fcc49ba, 0x00000000, 0x00000000, + 0x0a000010, 0x00100022, 0x00000001, 0x00100246, 0x00000000, 0x00004002, + 0x3f950b0f, 0xbec89a02, 0xbf5020c5, 0x00000000, 0x0a00000f, 0x00100042, + 0x00000001, 0x00100046, 0x00000000, 0x00004002, 0x3f950b0f, 0x400119ce, + 0x00000000, 0x00000000, 0x05000036, 0x00100082, 0x00000001, 0x00004001, + 0x3f800000, 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, + 0x00101e46, 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x0000000a, + 0x00000002, 0x00000000, 0x00000003, 0x00000005, 0x00000000, 0x00000000, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000d4, + 0x00000000, 0x00000000, 0x00000003, 0x0000001c, 0xffff0400, 0x00000100, + 0x000000a0, 0x0000007c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000001, 0x00000001, 0x00000087, 0x00000002, 0x00000005, + 0x00000004, 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x00000093, + 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000001, 0x00000001, + 0x0000000d, 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, + 0x74005965, 0x65546568, 0x72757478, 0x00565565, 0x7263694d, 0x666f736f, + 0x52282074, 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, 0x6c69706d, + 0x36207265, 0x392e332e, 0x2e303036, 0x38333631, 0xabab0034, 0x4e475349, + 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, + 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, + 0x00000003, 0x00000001, 0x00000303, 0x00000065, 0x00000000, 0x00000000, + 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, + 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, 0x0000002c, + 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, + 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_PixelShader_NV21_BT601[] = { + 0x43425844, 0x1e92bca4, 0xfeb04e20, 0x3f4226b1, 0xc89c58ad, 0x00000001, + 0x00000520, 0x00000006, 0x00000038, 0x00000188, 0x00000320, 0x0000039c, + 0x00000478, 0x000004ec, 0x396e6f41, 0x00000148, 0x00000148, 0xffff0200, + 0x0000011c, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0201, 0x05000051, 0xa00f0000, + 0xbd808081, 0xbf008081, 0x3f800000, 0x00000000, 0x05000051, 0xa00f0001, + 0x3f950b0f, 0x3fcc49ba, 0x00000000, 0x400119ce, 0x05000051, 0xa00f0002, + 0x3f950b0f, 0xbec89a02, 0xbf5020c5, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40800, + 0x02000001, 0x80060001, 0x80c40000, 0x03000002, 0x80070000, 0x80e40001, + 0xa0d40000, 0x0400005a, 0x80010001, 0x80e80000, 0xa0e40001, 0xa0aa0001, + 0x03000008, 0x80020001, 0x80e40000, 0xa0e40002, 0x0400005a, 0x80040001, + 0x80e40000, 0xa0ec0001, 0xa0aa0001, 0x02000001, 0x80080001, 0xa0aa0000, + 0x03000005, 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, + 0x80e40000, 0x0000ffff, 0x52444853, 0x00000190, 0x00000040, 0x00000064, + 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, + 0x00005555, 0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x03001062, + 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, + 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, 0x001000f2, + 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, 0x00106000, + 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, 0x00000001, + 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, 0x00100062, + 0x00000000, 0x00100456, 0x00000001, 0x0a000000, 0x00100072, 0x00000000, + 0x00100246, 0x00000000, 0x00004002, 0xbd808081, 0xbf008081, 0xbf008081, + 0x00000000, 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, 0x00000000, + 0x00004002, 0x3f950b0f, 0x3fcc49ba, 0x00000000, 0x00000000, 0x0a000010, + 0x00100022, 0x00000001, 0x00100246, 0x00000000, 0x00004002, 0x3f950b0f, + 0xbec89a02, 0xbf5020c5, 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, + 0x00100046, 0x00000000, 0x00004002, 0x3f950b0f, 0x400119ce, 0x00000000, + 0x00000000, 0x05000036, 0x00100082, 0x00000001, 0x00004001, 0x3f800000, + 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, + 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x0000000a, 0x00000002, + 0x00000000, 0x00000003, 0x00000005, 0x00000000, 0x00000000, 0x00000001, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000d4, 0x00000000, + 0x00000000, 0x00000003, 0x0000001c, 0xffff0400, 0x00000100, 0x000000a0, + 0x0000007c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000001, 0x00000001, 0x00000087, 0x00000002, 0x00000005, 0x00000004, + 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x00000093, 0x00000002, + 0x00000005, 0x00000004, 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, + 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x74005965, + 0x65546568, 0x72757478, 0x00565565, 0x7263694d, 0x666f736f, 0x52282074, + 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, 0x6c69706d, 0x36207265, + 0x392e332e, 0x2e303036, 0x38333631, 0xabab0034, 0x4e475349, 0x0000006c, + 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, + 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, + 0x00000001, 0x00000303, 0x00000065, 0x00000000, 0x00000000, 0x00000003, + 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, + 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, + 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, + 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#else +#error "An appropriate 'yuv' pixel shader is not defined." +#endif + +/* The yuv-rendering pixel shader: + + --- D3D11_PixelShader_NV21_BT709.hlsl --- + Texture2D theTextureY : register(t0); + Texture2D theTextureUV : register(t1); + SamplerState theSampler : register(s0); + + struct PixelShaderInput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + float4 main(PixelShaderInput input) : SV_TARGET + { + const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; + const float3 Rcoeff = {1.1644, 0.0000, 1.7927}; + const float3 Gcoeff = {1.1644, -0.2132, -0.5329}; + const float3 Bcoeff = {1.1644, 2.1124, 0.0000}; + + float4 Output; + + float3 yuv; + yuv.x = theTextureY.Sample(theSampler, input.tex).r; + yuv.yz = theTextureUV.Sample(theSampler, input.tex).gr; + + yuv += offset; + Output.r = dot(yuv, Rcoeff); + Output.g = dot(yuv, Gcoeff); + Output.b = dot(yuv, Bcoeff); + Output.a = 1.0f; + + return Output * input.color; + } + +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_PixelShader_NV21_BT709[] = { + 0x43425844, 0x754ba6c4, 0xe321a747, 0x23680787, 0x6bb1bdcc, 0x00000001, + 0x00000554, 0x00000006, 0x00000038, 0x000001bc, 0x00000354, 0x000003d0, + 0x000004ac, 0x00000520, 0x396e6f41, 0x0000017c, 0x0000017c, 0xffff0200, + 0x00000150, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0200, 0x05000051, 0xa00f0000, + 0xbd808081, 0xbf008081, 0xbf008081, 0x3f800000, 0x05000051, 0xa00f0001, + 0x3f950b0f, 0x3fe57732, 0x00000000, 0x00000000, 0x05000051, 0xa00f0002, + 0x3f950b0f, 0xbe5a511a, 0xbf086c22, 0x00000000, 0x05000051, 0xa00f0003, + 0x3f950b0f, 0x40073190, 0x00000000, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40800, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40801, + 0x02000001, 0x80020000, 0x80550001, 0x02000001, 0x80040000, 0x80000001, + 0x03000002, 0x80070000, 0x80e40000, 0xa0e40000, 0x03000005, 0x80080000, + 0x80000000, 0xa0000001, 0x04000004, 0x80010001, 0x80aa0000, 0xa0550001, + 0x80ff0000, 0x03000008, 0x80020001, 0x80e40000, 0xa0e40002, 0x0400005a, + 0x80040001, 0x80e40000, 0xa0e40003, 0xa0aa0003, 0x02000001, 0x80080001, + 0xa0ff0000, 0x03000005, 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, + 0x800f0800, 0x80e40000, 0x0000ffff, 0x52444853, 0x00000190, 0x00000040, + 0x00000064, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, + 0x00000000, 0x00005555, 0x04001858, 0x00107000, 0x00000001, 0x00005555, + 0x03001062, 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, + 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, + 0x001000f2, 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, + 0x00106000, 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, + 0x00000001, 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, + 0x00100062, 0x00000000, 0x00100456, 0x00000001, 0x0a000000, 0x00100072, + 0x00000000, 0x00100246, 0x00000000, 0x00004002, 0xbd808081, 0xbf008081, + 0xbf008081, 0x00000000, 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, + 0x00000000, 0x00004002, 0x3f950b0f, 0x3fe57732, 0x00000000, 0x00000000, + 0x0a000010, 0x00100022, 0x00000001, 0x00100246, 0x00000000, 0x00004002, + 0x3f950b0f, 0xbe5a511a, 0xbf086c22, 0x00000000, 0x0a00000f, 0x00100042, + 0x00000001, 0x00100046, 0x00000000, 0x00004002, 0x3f950b0f, 0x40073190, + 0x00000000, 0x00000000, 0x05000036, 0x00100082, 0x00000001, 0x00004001, + 0x3f800000, 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, + 0x00101e46, 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x0000000a, + 0x00000002, 0x00000000, 0x00000003, 0x00000005, 0x00000000, 0x00000000, + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000d4, + 0x00000000, 0x00000000, 0x00000003, 0x0000001c, 0xffff0400, 0x00000100, + 0x000000a0, 0x0000007c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000001, 0x00000001, 0x00000087, 0x00000002, 0x00000005, + 0x00000004, 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x00000093, + 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000001, 0x00000001, + 0x0000000d, 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, + 0x74005965, 0x65546568, 0x72757478, 0x00565565, 0x7263694d, 0x666f736f, + 0x52282074, 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, 0x6c69706d, + 0x36207265, 0x392e332e, 0x2e303036, 0x38333631, 0xabab0034, 0x4e475349, + 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, + 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, + 0x00000003, 0x00000001, 0x00000303, 0x00000065, 0x00000000, 0x00000000, + 0x00000003, 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, + 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, 0x0000002c, + 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, + 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_PixelShader_NV21_BT709[] = { + 0x43425844, 0xb6219b20, 0xb71bbcf7, 0xf361cc45, 0xc4d5f5be, 0x00000001, + 0x00000520, 0x00000006, 0x00000038, 0x00000188, 0x00000320, 0x0000039c, + 0x00000478, 0x000004ec, 0x396e6f41, 0x00000148, 0x00000148, 0xffff0200, + 0x0000011c, 0x0000002c, 0x002c0000, 0x002c0000, 0x002c0000, 0x00240002, + 0x002c0000, 0x00000000, 0x00010001, 0xffff0201, 0x05000051, 0xa00f0000, + 0xbd808081, 0xbf008081, 0x3f800000, 0x00000000, 0x05000051, 0xa00f0001, + 0x3f950b0f, 0x3fe57732, 0x00000000, 0x40073190, 0x05000051, 0xa00f0002, + 0x3f950b0f, 0xbe5a511a, 0xbf086c22, 0x00000000, 0x0200001f, 0x80000000, + 0xb0030000, 0x0200001f, 0x80000000, 0xb00f0001, 0x0200001f, 0x90000000, + 0xa00f0800, 0x0200001f, 0x90000000, 0xa00f0801, 0x03000042, 0x800f0000, + 0xb0e40000, 0xa0e40801, 0x03000042, 0x800f0001, 0xb0e40000, 0xa0e40800, + 0x02000001, 0x80060001, 0x80c40000, 0x03000002, 0x80070000, 0x80e40001, + 0xa0d40000, 0x0400005a, 0x80010001, 0x80e80000, 0xa0e40001, 0xa0aa0001, + 0x03000008, 0x80020001, 0x80e40000, 0xa0e40002, 0x0400005a, 0x80040001, + 0x80e40000, 0xa0ec0001, 0xa0aa0001, 0x02000001, 0x80080001, 0xa0aa0000, + 0x03000005, 0x800f0000, 0x80e40001, 0xb0e40001, 0x02000001, 0x800f0800, + 0x80e40000, 0x0000ffff, 0x52444853, 0x00000190, 0x00000040, 0x00000064, + 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, + 0x00005555, 0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x03001062, + 0x00101032, 0x00000001, 0x03001062, 0x001010f2, 0x00000002, 0x03000065, + 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x09000045, 0x001000f2, + 0x00000000, 0x00101046, 0x00000001, 0x00107e46, 0x00000000, 0x00106000, + 0x00000000, 0x09000045, 0x001000f2, 0x00000001, 0x00101046, 0x00000001, + 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x05000036, 0x00100062, + 0x00000000, 0x00100456, 0x00000001, 0x0a000000, 0x00100072, 0x00000000, + 0x00100246, 0x00000000, 0x00004002, 0xbd808081, 0xbf008081, 0xbf008081, + 0x00000000, 0x0a00000f, 0x00100012, 0x00000001, 0x00100086, 0x00000000, + 0x00004002, 0x3f950b0f, 0x3fe57732, 0x00000000, 0x00000000, 0x0a000010, + 0x00100022, 0x00000001, 0x00100246, 0x00000000, 0x00004002, 0x3f950b0f, + 0xbe5a511a, 0xbf086c22, 0x00000000, 0x0a00000f, 0x00100042, 0x00000001, + 0x00100046, 0x00000000, 0x00004002, 0x3f950b0f, 0x40073190, 0x00000000, + 0x00000000, 0x05000036, 0x00100082, 0x00000001, 0x00004001, 0x3f800000, + 0x07000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x00101e46, + 0x00000002, 0x0100003e, 0x54415453, 0x00000074, 0x0000000a, 0x00000002, + 0x00000000, 0x00000003, 0x00000005, 0x00000000, 0x00000000, 0x00000001, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x46454452, 0x000000d4, 0x00000000, + 0x00000000, 0x00000003, 0x0000001c, 0xffff0400, 0x00000100, 0x000000a0, + 0x0000007c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000001, 0x00000001, 0x00000087, 0x00000002, 0x00000005, 0x00000004, + 0xffffffff, 0x00000000, 0x00000001, 0x0000000d, 0x00000093, 0x00000002, + 0x00000005, 0x00000004, 0xffffffff, 0x00000001, 0x00000001, 0x0000000d, + 0x53656874, 0x6c706d61, 0x74007265, 0x65546568, 0x72757478, 0x74005965, + 0x65546568, 0x72757478, 0x00565565, 0x7263694d, 0x666f736f, 0x52282074, + 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, 0x6c69706d, 0x36207265, + 0x392e332e, 0x2e303036, 0x38333631, 0xabab0034, 0x4e475349, 0x0000006c, + 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, + 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, + 0x00000001, 0x00000303, 0x00000065, 0x00000000, 0x00000000, 0x00000003, + 0x00000002, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x43584554, + 0x44524f4f, 0x4c4f4300, 0xab00524f, 0x4e47534f, 0x0000002c, 0x00000001, + 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, + 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054 +}; +#else +#error "An appropriate 'yuv' pixel shader is not defined." +#endif + +/* The sole vertex shader: + + --- D3D11_VertexShader.hlsl --- + #pragma pack_matrix( row_major ) + + cbuffer VertexShaderConstants : register(b0) + { + matrix model; + matrix projectionAndView; + }; + + struct VertexShaderInput + { + float3 pos : POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + struct VertexShaderOutput + { + float4 pos : SV_POSITION; + float2 tex : TEXCOORD0; + float4 color : COLOR0; + }; + + VertexShaderOutput main(VertexShaderInput input) + { + VertexShaderOutput output; + float4 pos = float4(input.pos, 1.0f); + + // Transform the vertex position into projected space. + pos = mul(pos, model); + pos = mul(pos, projectionAndView); + output.pos = pos; + + // Pass through texture coordinates and color values without transformation + output.tex = input.tex; + output.color = input.color; + + return output; + } +*/ +#if defined(D3D11_USE_SHADER_MODEL_4_0_level_9_1) +static const DWORD D3D11_VertexShader[] = { + 0x43425844, 0x62dfae5f, 0x3e8bd8df, 0x9ec97127, 0x5044eefb, 0x00000001, + 0x00000598, 0x00000006, 0x00000038, 0x0000016c, 0x00000334, 0x000003b0, + 0x000004b4, 0x00000524, 0x396e6f41, 0x0000012c, 0x0000012c, 0xfffe0200, + 0x000000f8, 0x00000034, 0x00240001, 0x00300000, 0x00300000, 0x00240000, + 0x00300001, 0x00000000, 0x00010008, 0x00000000, 0x00000000, 0xfffe0200, + 0x0200001f, 0x80000005, 0x900f0000, 0x0200001f, 0x80010005, 0x900f0001, + 0x0200001f, 0x80020005, 0x900f0002, 0x03000005, 0x800f0000, 0x90550000, + 0xa0e40002, 0x04000004, 0x800f0000, 0x90000000, 0xa0e40001, 0x80e40000, + 0x04000004, 0x800f0000, 0x90aa0000, 0xa0e40003, 0x80e40000, 0x03000002, + 0x800f0000, 0x80e40000, 0xa0e40004, 0x03000005, 0x800f0001, 0x80550000, + 0xa0e40006, 0x04000004, 0x800f0001, 0x80000000, 0xa0e40005, 0x80e40001, + 0x04000004, 0x800f0001, 0x80aa0000, 0xa0e40007, 0x80e40001, 0x04000004, + 0x800f0000, 0x80ff0000, 0xa0e40008, 0x80e40001, 0x04000004, 0xc0030000, + 0x80ff0000, 0xa0e40000, 0x80e40000, 0x02000001, 0xc00c0000, 0x80e40000, + 0x02000001, 0xe0030000, 0x90e40001, 0x02000001, 0xe00f0001, 0x90e40002, + 0x0000ffff, 0x52444853, 0x000001c0, 0x00010040, 0x00000070, 0x04000059, + 0x00208e46, 0x00000000, 0x00000008, 0x0300005f, 0x00101072, 0x00000000, + 0x0300005f, 0x00101032, 0x00000001, 0x0300005f, 0x001010f2, 0x00000002, + 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x00102032, + 0x00000001, 0x03000065, 0x001020f2, 0x00000002, 0x02000068, 0x00000002, + 0x08000038, 0x001000f2, 0x00000000, 0x00101556, 0x00000000, 0x00208e46, + 0x00000000, 0x00000001, 0x0a000032, 0x001000f2, 0x00000000, 0x00101006, + 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00100e46, 0x00000000, + 0x0a000032, 0x001000f2, 0x00000000, 0x00101aa6, 0x00000000, 0x00208e46, + 0x00000000, 0x00000002, 0x00100e46, 0x00000000, 0x08000000, 0x001000f2, + 0x00000000, 0x00100e46, 0x00000000, 0x00208e46, 0x00000000, 0x00000003, + 0x08000038, 0x001000f2, 0x00000001, 0x00100556, 0x00000000, 0x00208e46, + 0x00000000, 0x00000005, 0x0a000032, 0x001000f2, 0x00000001, 0x00100006, + 0x00000000, 0x00208e46, 0x00000000, 0x00000004, 0x00100e46, 0x00000001, + 0x0a000032, 0x001000f2, 0x00000001, 0x00100aa6, 0x00000000, 0x00208e46, + 0x00000000, 0x00000006, 0x00100e46, 0x00000001, 0x0a000032, 0x001020f2, + 0x00000000, 0x00100ff6, 0x00000000, 0x00208e46, 0x00000000, 0x00000007, + 0x00100e46, 0x00000001, 0x05000036, 0x00102032, 0x00000001, 0x00101046, + 0x00000001, 0x05000036, 0x001020f2, 0x00000002, 0x00101e46, 0x00000002, + 0x0100003e, 0x54415453, 0x00000074, 0x0000000b, 0x00000002, 0x00000000, + 0x00000006, 0x00000003, 0x00000000, 0x00000000, 0x00000001, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x46454452, 0x000000fc, 0x00000001, 0x00000054, + 0x00000001, 0x0000001c, 0xfffe0400, 0x00000100, 0x000000c6, 0x0000003c, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, + 0x00000001, 0x74726556, 0x68537865, 0x72656461, 0x736e6f43, 0x746e6174, + 0xabab0073, 0x0000003c, 0x00000002, 0x0000006c, 0x00000080, 0x00000000, + 0x00000000, 0x0000009c, 0x00000000, 0x00000040, 0x00000002, 0x000000a4, + 0x00000000, 0x000000b4, 0x00000040, 0x00000040, 0x00000002, 0x000000a4, + 0x00000000, 0x65646f6d, 0xabab006c, 0x00030002, 0x00040004, 0x00000000, + 0x00000000, 0x6a6f7270, 0x69746365, 0x6e416e6f, 0x65695664, 0x694d0077, + 0x736f7263, 0x2074666f, 0x20295228, 0x4c534c48, 0x61685320, 0x20726564, + 0x706d6f43, 0x72656c69, 0x332e3920, 0x32392e30, 0x312e3030, 0x34383336, + 0xababab00, 0x4e475349, 0x00000068, 0x00000003, 0x00000008, 0x00000050, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000707, 0x00000059, + 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000062, + 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x49534f50, + 0x4e4f4954, 0x58455400, 0x524f4f43, 0x4f430044, 0x00524f4c, 0x4e47534f, + 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, + 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, + 0x00000003, 0x00000001, 0x00000c03, 0x00000065, 0x00000000, 0x00000000, + 0x00000003, 0x00000002, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, + 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f +}; +#elif defined(D3D11_USE_SHADER_MODEL_4_0_level_9_3) +static const DWORD D3D11_VertexShader[] = { + 0x43425844, 0x01a24e41, 0x696af551, 0x4b2a87d1, 0x82ea03f6, 0x00000001, + 0x00000598, 0x00000006, 0x00000038, 0x0000016c, 0x00000334, 0x000003b0, + 0x000004b4, 0x00000524, 0x396e6f41, 0x0000012c, 0x0000012c, 0xfffe0200, + 0x000000f8, 0x00000034, 0x00240001, 0x00300000, 0x00300000, 0x00240000, + 0x00300001, 0x00000000, 0x00010008, 0x00000000, 0x00000000, 0xfffe0201, + 0x0200001f, 0x80000005, 0x900f0000, 0x0200001f, 0x80010005, 0x900f0001, + 0x0200001f, 0x80020005, 0x900f0002, 0x03000005, 0x800f0000, 0x90550000, + 0xa0e40002, 0x04000004, 0x800f0000, 0x90000000, 0xa0e40001, 0x80e40000, + 0x04000004, 0x800f0000, 0x90aa0000, 0xa0e40003, 0x80e40000, 0x03000002, + 0x800f0000, 0x80e40000, 0xa0e40004, 0x03000005, 0x800f0001, 0x80550000, + 0xa0e40006, 0x04000004, 0x800f0001, 0x80000000, 0xa0e40005, 0x80e40001, + 0x04000004, 0x800f0001, 0x80aa0000, 0xa0e40007, 0x80e40001, 0x04000004, + 0x800f0000, 0x80ff0000, 0xa0e40008, 0x80e40001, 0x04000004, 0xc0030000, + 0x80ff0000, 0xa0e40000, 0x80e40000, 0x02000001, 0xc00c0000, 0x80e40000, + 0x02000001, 0xe0030000, 0x90e40001, 0x02000001, 0xe00f0001, 0x90e40002, + 0x0000ffff, 0x52444853, 0x000001c0, 0x00010040, 0x00000070, 0x04000059, + 0x00208e46, 0x00000000, 0x00000008, 0x0300005f, 0x00101072, 0x00000000, + 0x0300005f, 0x00101032, 0x00000001, 0x0300005f, 0x001010f2, 0x00000002, + 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x00102032, + 0x00000001, 0x03000065, 0x001020f2, 0x00000002, 0x02000068, 0x00000002, + 0x08000038, 0x001000f2, 0x00000000, 0x00101556, 0x00000000, 0x00208e46, + 0x00000000, 0x00000001, 0x0a000032, 0x001000f2, 0x00000000, 0x00101006, + 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00100e46, 0x00000000, + 0x0a000032, 0x001000f2, 0x00000000, 0x00101aa6, 0x00000000, 0x00208e46, + 0x00000000, 0x00000002, 0x00100e46, 0x00000000, 0x08000000, 0x001000f2, + 0x00000000, 0x00100e46, 0x00000000, 0x00208e46, 0x00000000, 0x00000003, + 0x08000038, 0x001000f2, 0x00000001, 0x00100556, 0x00000000, 0x00208e46, + 0x00000000, 0x00000005, 0x0a000032, 0x001000f2, 0x00000001, 0x00100006, + 0x00000000, 0x00208e46, 0x00000000, 0x00000004, 0x00100e46, 0x00000001, + 0x0a000032, 0x001000f2, 0x00000001, 0x00100aa6, 0x00000000, 0x00208e46, + 0x00000000, 0x00000006, 0x00100e46, 0x00000001, 0x0a000032, 0x001020f2, + 0x00000000, 0x00100ff6, 0x00000000, 0x00208e46, 0x00000000, 0x00000007, + 0x00100e46, 0x00000001, 0x05000036, 0x00102032, 0x00000001, 0x00101046, + 0x00000001, 0x05000036, 0x001020f2, 0x00000002, 0x00101e46, 0x00000002, + 0x0100003e, 0x54415453, 0x00000074, 0x0000000b, 0x00000002, 0x00000000, + 0x00000006, 0x00000003, 0x00000000, 0x00000000, 0x00000001, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x46454452, 0x000000fc, 0x00000001, 0x00000054, + 0x00000001, 0x0000001c, 0xfffe0400, 0x00000100, 0x000000c6, 0x0000003c, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, + 0x00000001, 0x74726556, 0x68537865, 0x72656461, 0x736e6f43, 0x746e6174, + 0xabab0073, 0x0000003c, 0x00000002, 0x0000006c, 0x00000080, 0x00000000, + 0x00000000, 0x0000009c, 0x00000000, 0x00000040, 0x00000002, 0x000000a4, + 0x00000000, 0x000000b4, 0x00000040, 0x00000040, 0x00000002, 0x000000a4, + 0x00000000, 0x65646f6d, 0xabab006c, 0x00030002, 0x00040004, 0x00000000, + 0x00000000, 0x6a6f7270, 0x69746365, 0x6e416e6f, 0x65695664, 0x694d0077, + 0x736f7263, 0x2074666f, 0x20295228, 0x4c534c48, 0x61685320, 0x20726564, + 0x706d6f43, 0x72656c69, 0x332e3920, 0x32392e30, 0x312e3030, 0x34383336, + 0xababab00, 0x4e475349, 0x00000068, 0x00000003, 0x00000008, 0x00000050, + 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000707, 0x00000059, + 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000303, 0x00000062, + 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000f0f, 0x49534f50, + 0x4e4f4954, 0x58455400, 0x524f4f43, 0x4f430044, 0x00524f4c, 0x4e47534f, + 0x0000006c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, + 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, + 0x00000003, 0x00000001, 0x00000c03, 0x00000065, 0x00000000, 0x00000000, + 0x00000003, 0x00000002, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, + 0x43584554, 0x44524f4f, 0x4c4f4300, 0xab00524f +}; +#else +#error "An appropriate vertex shader is not defined." +#endif + +static struct +{ + const void *shader_data; + SIZE_T shader_size; +} D3D11_shaders[] = { + { D3D11_PixelShader_Colors, sizeof(D3D11_PixelShader_Colors) }, + { D3D11_PixelShader_Textures, sizeof(D3D11_PixelShader_Textures) }, + { D3D11_PixelShader_YUV_JPEG, sizeof(D3D11_PixelShader_YUV_JPEG) }, + { D3D11_PixelShader_YUV_BT601, sizeof(D3D11_PixelShader_YUV_BT601) }, + { D3D11_PixelShader_YUV_BT709, sizeof(D3D11_PixelShader_YUV_BT709) }, + { D3D11_PixelShader_NV12_JPEG, sizeof(D3D11_PixelShader_NV12_JPEG) }, + { D3D11_PixelShader_NV12_BT601, sizeof(D3D11_PixelShader_NV12_BT601) }, + { D3D11_PixelShader_NV12_BT709, sizeof(D3D11_PixelShader_NV12_BT709) }, + { D3D11_PixelShader_NV21_JPEG, sizeof(D3D11_PixelShader_NV21_JPEG) }, + { D3D11_PixelShader_NV21_BT601, sizeof(D3D11_PixelShader_NV21_BT601) }, + { D3D11_PixelShader_NV21_BT709, sizeof(D3D11_PixelShader_NV21_BT709) }, +}; + +int D3D11_CreateVertexShader(ID3D11Device1 *d3dDevice, ID3D11VertexShader **vertexShader, ID3D11InputLayout **inputLayout) +{ + /* Declare how the input layout for SDL's vertex shader will be setup: */ + const D3D11_INPUT_ELEMENT_DESC vertexDesc[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + HRESULT result; + + /* Load in SDL's one and only vertex shader: */ + result = ID3D11Device_CreateVertexShader(d3dDevice, + D3D11_VertexShader, + sizeof(D3D11_VertexShader), + NULL, + vertexShader + ); + if (FAILED(result)) { + return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateVertexShader"), result); + } + + /* Create an input layout for SDL's vertex shader: */ + result = ID3D11Device_CreateInputLayout(d3dDevice, + vertexDesc, + ARRAYSIZE(vertexDesc), + D3D11_VertexShader, + sizeof(D3D11_VertexShader), + inputLayout + ); + if (FAILED(result)) { + return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateInputLayout"), result); + } + return 0; +} + +int D3D11_CreatePixelShader(ID3D11Device1 *d3dDevice, D3D11_Shader shader, ID3D11PixelShader **pixelShader) +{ + HRESULT result; + + result = ID3D11Device_CreatePixelShader(d3dDevice, + D3D11_shaders[shader].shader_data, + D3D11_shaders[shader].shader_size, + NULL, + pixelShader + ); + if (FAILED(result)) { + return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreatePixelShader"), result); + } + return 0; +} + +#endif /* SDL_VIDEO_RENDER_D3D11 && !SDL_RENDER_DISABLED */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/direct3d11/SDL_shaders_d3d11.h b/src/render/direct3d11/SDL_shaders_d3d11.h new file mode 100755 index 0000000000000..5624be42c14aa --- /dev/null +++ b/src/render/direct3d11/SDL_shaders_d3d11.h @@ -0,0 +1,43 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2017 Sam Lantinga + + 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, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +/* D3D11 shader implementation */ + +typedef enum { + SHADER_SOLID, + SHADER_RGB, + SHADER_YUV_JPEG, + SHADER_YUV_BT601, + SHADER_YUV_BT709, + SHADER_NV12_JPEG, + SHADER_NV12_BT601, + SHADER_NV12_BT709, + SHADER_NV21_JPEG, + SHADER_NV21_BT601, + SHADER_NV21_BT709, + NUM_SHADERS +} D3D11_Shader; + +extern int D3D11_CreateVertexShader(ID3D11Device1 *d3dDevice, ID3D11VertexShader **vertexShader, ID3D11InputLayout **inputLayout); +extern int D3D11_CreatePixelShader(ID3D11Device1 *d3dDevice, D3D11_Shader shader, ID3D11PixelShader **pixelShader); + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/mmx.h b/src/render/mmx.h deleted file mode 100644 index 3bd00ac2398a3..0000000000000 --- a/src/render/mmx.h +++ /dev/null @@ -1,642 +0,0 @@ -/* mmx.h - - MultiMedia eXtensions GCC interface library for IA32. - - To use this library, simply include this header file - and compile with GCC. You MUST have inlining enabled - in order for mmx_ok() to work; this can be done by - simply using -O on the GCC command line. - - Compiling with -DMMX_TRACE will cause detailed trace - output to be sent to stderr for each mmx operation. - This adds lots of code, and obviously slows execution to - a crawl, but can be very useful for debugging. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT - LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS FOR ANY PARTICULAR PURPOSE. - - 1997-99 by H. Dietz and R. Fisher - - Notes: - It appears that the latest gas has the pand problem fixed, therefore - I'll undefine BROKEN_PAND by default. -*/ - -#ifndef _MMX_H -#define _MMX_H - - -/* Warning: at this writing, the version of GAS packaged - with most Linux distributions does not handle the - parallel AND operation mnemonic correctly. If the - symbol BROKEN_PAND is defined, a slower alternative - coding will be used. If execution of mmxtest results - in an illegal instruction fault, define this symbol. -*/ -#undef BROKEN_PAND - - -/* The type of an value that fits in an MMX register - (note that long long constant values MUST be suffixed - by LL and unsigned long long values by ULL, lest - they be truncated by the compiler) -*/ -typedef union -{ - long long q; /* Quadword (64-bit) value */ - unsigned long long uq; /* Unsigned Quadword */ - int d[2]; /* 2 Doubleword (32-bit) values */ - unsigned int ud[2]; /* 2 Unsigned Doubleword */ - short w[4]; /* 4 Word (16-bit) values */ - unsigned short uw[4]; /* 4 Unsigned Word */ - char b[8]; /* 8 Byte (8-bit) values */ - unsigned char ub[8]; /* 8 Unsigned Byte */ - float s[2]; /* Single-precision (32-bit) value */ -} __attribute__ ((aligned(8))) mmx_t; /* On an 8-byte (64-bit) boundary */ - - -#if 0 -/* Function to test if multimedia instructions are supported... -*/ -inline extern int -mm_support(void) -{ - /* Returns 1 if MMX instructions are supported, - 3 if Cyrix MMX and Extended MMX instructions are supported - 5 if AMD MMX and 3DNow! instructions are supported - 0 if hardware does not support any of these - */ - register int rval = 0; - - __asm__ __volatile__( - /* See if CPUID instruction is supported ... */ - /* ... Get copies of EFLAGS into eax and ecx */ - "pushf\n\t" - "popl %%eax\n\t" "movl %%eax, %%ecx\n\t" - /* ... Toggle the ID bit in one copy and store */ - /* to the EFLAGS reg */ - "xorl $0x200000, %%eax\n\t" - "push %%eax\n\t" "popf\n\t" - /* ... Get the (hopefully modified) EFLAGS */ - "pushf\n\t" "popl %%eax\n\t" - /* ... Compare and test result */ - "xorl %%eax, %%ecx\n\t" "testl $0x200000, %%ecx\n\t" "jz NotSupported1\n\t" /* CPUID not supported */ - /* Get standard CPUID information, and - go to a specific vendor section */ - "movl $0, %%eax\n\t" "cpuid\n\t" - /* Check for Intel */ - "cmpl $0x756e6547, %%ebx\n\t" - "jne TryAMD\n\t" - "cmpl $0x49656e69, %%edx\n\t" - "jne TryAMD\n\t" - "cmpl $0x6c65746e, %%ecx\n" - "jne TryAMD\n\t" "jmp Intel\n\t" - /* Check for AMD */ - "\nTryAMD:\n\t" - "cmpl $0x68747541, %%ebx\n\t" - "jne TryCyrix\n\t" - "cmpl $0x69746e65, %%edx\n\t" - "jne TryCyrix\n\t" - "cmpl $0x444d4163, %%ecx\n" - "jne TryCyrix\n\t" "jmp AMD\n\t" - /* Check for Cyrix */ - "\nTryCyrix:\n\t" - "cmpl $0x69727943, %%ebx\n\t" - "jne NotSupported2\n\t" - "cmpl $0x736e4978, %%edx\n\t" - "jne NotSupported3\n\t" - "cmpl $0x64616574, %%ecx\n\t" - "jne NotSupported4\n\t" - /* Drop through to Cyrix... */ - /* Cyrix Section */ - /* See if extended CPUID level 80000001 is supported */ - /* The value of CPUID/80000001 for the 6x86MX is undefined - according to the Cyrix CPU Detection Guide (Preliminary - Rev. 1.01 table 1), so we'll check the value of eax for - CPUID/0 to see if standard CPUID level 2 is supported. - According to the table, the only CPU which supports level - 2 is also the only one which supports extended CPUID levels. - */ - "cmpl $0x2, %%eax\n\t" "jne MMXtest\n\t" /* Use standard CPUID instead */ - /* Extended CPUID supported (in theory), so get extended - features */ - "movl $0x80000001, %%eax\n\t" "cpuid\n\t" "testl $0x00800000, %%eax\n\t" /* Test for MMX */ - "jz NotSupported5\n\t" /* MMX not supported */ - "testl $0x01000000, %%eax\n\t" /* Test for Ext'd MMX */ - "jnz EMMXSupported\n\t" "movl $1, %0:\n\n\t" /* MMX Supported */ - "jmp Return\n\n" "EMMXSupported:\n\t" "movl $3, %0:\n\n\t" /* EMMX and MMX Supported */ - "jmp Return\n\t" - /* AMD Section */ - "AMD:\n\t" - /* See if extended CPUID is supported */ - "movl $0x80000000, %%eax\n\t" "cpuid\n\t" "cmpl $0x80000000, %%eax\n\t" "jl MMXtest\n\t" /* Use standard CPUID instead */ - /* Extended CPUID supported, so get extended features */ - "movl $0x80000001, %%eax\n\t" "cpuid\n\t" "testl $0x00800000, %%edx\n\t" /* Test for MMX */ - "jz NotSupported6\n\t" /* MMX not supported */ - "testl $0x80000000, %%edx\n\t" /* Test for 3DNow! */ - "jnz ThreeDNowSupported\n\t" "movl $1, %0:\n\n\t" /* MMX Supported */ - "jmp Return\n\n" "ThreeDNowSupported:\n\t" "movl $5, %0:\n\n\t" /* 3DNow! and MMX Supported */ - "jmp Return\n\t" - /* Intel Section */ - "Intel:\n\t" - /* Check for MMX */ - "MMXtest:\n\t" "movl $1, %%eax\n\t" "cpuid\n\t" "testl $0x00800000, %%edx\n\t" /* Test for MMX */ - "jz NotSupported7\n\t" /* MMX Not supported */ - "movl $1, %0:\n\n\t" /* MMX Supported */ - "jmp Return\n\t" - /* Nothing supported */ - "\nNotSupported1:\n\t" "#movl $101, %0:\n\n\t" "\nNotSupported2:\n\t" "#movl $102, %0:\n\n\t" "\nNotSupported3:\n\t" "#movl $103, %0:\n\n\t" "\nNotSupported4:\n\t" "#movl $104, %0:\n\n\t" "\nNotSupported5:\n\t" "#movl $105, %0:\n\n\t" "\nNotSupported6:\n\t" "#movl $106, %0:\n\n\t" "\nNotSupported7:\n\t" "#movl $107, %0:\n\n\t" "movl $0, %0:\n\n\t" "Return:\n\t":"=a"(rval): /* no input */ - :"eax", "ebx", "ecx", "edx"); - - /* Return */ - return (rval); -} - -/* Function to test if mmx instructions are supported... -*/ -inline extern int -mmx_ok(void) -{ - /* Returns 1 if MMX instructions are supported, 0 otherwise */ - return (mm_support() & 0x1); -} -#endif - -/* Helper functions for the instruction macros that follow... - (note that memory-to-register, m2r, instructions are nearly - as efficient as register-to-register, r2r, instructions; - however, memory-to-memory instructions are really simulated - as a convenience, and are only 1/3 as efficient) -*/ -#ifdef MMX_TRACE - -/* Include the stuff for printing a trace to stderr... -*/ - -#define mmx_i2r(op, imm, reg) \ - { \ - mmx_t mmx_trace; \ - mmx_trace.uq = (imm); \ - printf(#op "_i2r(" #imm "=0x%08x%08x, ", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - printf(#reg "=0x%08x%08x) => ", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "X" (imm)); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - printf(#reg "=0x%08x%08x\n", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - } - -#define mmx_m2r(op, mem, reg) \ - { \ - mmx_t mmx_trace; \ - mmx_trace = (mem); \ - printf(#op "_m2r(" #mem "=0x%08x%08x, ", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - printf(#reg "=0x%08x%08x) => ", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "X" (mem)); \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - printf(#reg "=0x%08x%08x\n", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - } - -#define mmx_r2m(op, reg, mem) \ - { \ - mmx_t mmx_trace; \ - __asm__ __volatile__ ("movq %%" #reg ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - printf(#op "_r2m(" #reg "=0x%08x%08x, ", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - mmx_trace = (mem); \ - printf(#mem "=0x%08x%08x) => ", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - __asm__ __volatile__ (#op " %%" #reg ", %0" \ - : "=X" (mem) \ - : /* nothing */ ); \ - mmx_trace = (mem); \ - printf(#mem "=0x%08x%08x\n", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - } - -#define mmx_r2r(op, regs, regd) \ - { \ - mmx_t mmx_trace; \ - __asm__ __volatile__ ("movq %%" #regs ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - printf(#op "_r2r(" #regs "=0x%08x%08x, ", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - __asm__ __volatile__ ("movq %%" #regd ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - printf(#regd "=0x%08x%08x) => ", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - __asm__ __volatile__ (#op " %" #regs ", %" #regd); \ - __asm__ __volatile__ ("movq %%" #regd ", %0" \ - : "=X" (mmx_trace) \ - : /* nothing */ ); \ - printf(#regd "=0x%08x%08x\n", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - } - -#define mmx_m2m(op, mems, memd) \ - { \ - mmx_t mmx_trace; \ - mmx_trace = (mems); \ - printf(#op "_m2m(" #mems "=0x%08x%08x, ", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - mmx_trace = (memd); \ - printf(#memd "=0x%08x%08x) => ", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - __asm__ __volatile__ ("movq %0, %%mm0\n\t" \ - #op " %1, %%mm0\n\t" \ - "movq %%mm0, %0" \ - : "=X" (memd) \ - : "X" (mems)); \ - mmx_trace = (memd); \ - printf(#memd "=0x%08x%08x\n", \ - mmx_trace.d[1], mmx_trace.d[0]); \ - } - -#else - -/* These macros are a lot simpler without the tracing... -*/ - -#define mmx_i2r(op, imm, reg) \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "X" (imm) ) - -#define mmx_m2r(op, mem, reg) \ - __asm__ __volatile__ (#op " %0, %%" #reg \ - : /* nothing */ \ - : "m" (mem)) - -#define mmx_r2m(op, reg, mem) \ - __asm__ __volatile__ (#op " %%" #reg ", %0" \ - : "=m" (mem) \ - : /* nothing */ ) - -#define mmx_r2r(op, regs, regd) \ - __asm__ __volatile__ (#op " %" #regs ", %" #regd) - -#define mmx_m2m(op, mems, memd) \ - __asm__ __volatile__ ("movq %0, %%mm0\n\t" \ - #op " %1, %%mm0\n\t" \ - "movq %%mm0, %0" \ - : "=X" (memd) \ - : "X" (mems)) - -#endif - - -/* 1x64 MOVe Quadword - (this is both a load and a store... - in fact, it is the only way to store) -*/ -#define movq_m2r(var, reg) mmx_m2r(movq, var, reg) -#define movq_r2m(reg, var) mmx_r2m(movq, reg, var) -#define movq_r2r(regs, regd) mmx_r2r(movq, regs, regd) -#define movq(vars, vard) \ - __asm__ __volatile__ ("movq %1, %%mm0\n\t" \ - "movq %%mm0, %0" \ - : "=X" (vard) \ - : "X" (vars)) - - -/* 1x32 MOVe Doubleword - (like movq, this is both load and store... - but is most useful for moving things between - mmx registers and ordinary registers) -*/ -#define movd_m2r(var, reg) mmx_m2r(movd, var, reg) -#define movd_r2m(reg, var) mmx_r2m(movd, reg, var) -#define movd_r2r(regs, regd) mmx_r2r(movd, regs, regd) -#define movd(vars, vard) \ - __asm__ __volatile__ ("movd %1, %%mm0\n\t" \ - "movd %%mm0, %0" \ - : "=X" (vard) \ - : "X" (vars)) - - -/* 2x32, 4x16, and 8x8 Parallel ADDs -*/ -#define paddd_m2r(var, reg) mmx_m2r(paddd, var, reg) -#define paddd_r2r(regs, regd) mmx_r2r(paddd, regs, regd) -#define paddd(vars, vard) mmx_m2m(paddd, vars, vard) - -#define paddw_m2r(var, reg) mmx_m2r(paddw, var, reg) -#define paddw_r2r(regs, regd) mmx_r2r(paddw, regs, regd) -#define paddw(vars, vard) mmx_m2m(paddw, vars, vard) - -#define paddb_m2r(var, reg) mmx_m2r(paddb, var, reg) -#define paddb_r2r(regs, regd) mmx_r2r(paddb, regs, regd) -#define paddb(vars, vard) mmx_m2m(paddb, vars, vard) - - -/* 4x16 and 8x8 Parallel ADDs using Saturation arithmetic -*/ -#define paddsw_m2r(var, reg) mmx_m2r(paddsw, var, reg) -#define paddsw_r2r(regs, regd) mmx_r2r(paddsw, regs, regd) -#define paddsw(vars, vard) mmx_m2m(paddsw, vars, vard) - -#define paddsb_m2r(var, reg) mmx_m2r(paddsb, var, reg) -#define paddsb_r2r(regs, regd) mmx_r2r(paddsb, regs, regd) -#define paddsb(vars, vard) mmx_m2m(paddsb, vars, vard) - - -/* 4x16 and 8x8 Parallel ADDs using Unsigned Saturation arithmetic -*/ -#define paddusw_m2r(var, reg) mmx_m2r(paddusw, var, reg) -#define paddusw_r2r(regs, regd) mmx_r2r(paddusw, regs, regd) -#define paddusw(vars, vard) mmx_m2m(paddusw, vars, vard) - -#define paddusb_m2r(var, reg) mmx_m2r(paddusb, var, reg) -#define paddusb_r2r(regs, regd) mmx_r2r(paddusb, regs, regd) -#define paddusb(vars, vard) mmx_m2m(paddusb, vars, vard) - - -/* 2x32, 4x16, and 8x8 Parallel SUBs -*/ -#define psubd_m2r(var, reg) mmx_m2r(psubd, var, reg) -#define psubd_r2r(regs, regd) mmx_r2r(psubd, regs, regd) -#define psubd(vars, vard) mmx_m2m(psubd, vars, vard) - -#define psubw_m2r(var, reg) mmx_m2r(psubw, var, reg) -#define psubw_r2r(regs, regd) mmx_r2r(psubw, regs, regd) -#define psubw(vars, vard) mmx_m2m(psubw, vars, vard) - -#define psubb_m2r(var, reg) mmx_m2r(psubb, var, reg) -#define psubb_r2r(regs, regd) mmx_r2r(psubb, regs, regd) -#define psubb(vars, vard) mmx_m2m(psubb, vars, vard) - - -/* 4x16 and 8x8 Parallel SUBs using Saturation arithmetic -*/ -#define psubsw_m2r(var, reg) mmx_m2r(psubsw, var, reg) -#define psubsw_r2r(regs, regd) mmx_r2r(psubsw, regs, regd) -#define psubsw(vars, vard) mmx_m2m(psubsw, vars, vard) - -#define psubsb_m2r(var, reg) mmx_m2r(psubsb, var, reg) -#define psubsb_r2r(regs, regd) mmx_r2r(psubsb, regs, regd) -#define psubsb(vars, vard) mmx_m2m(psubsb, vars, vard) - - -/* 4x16 and 8x8 Parallel SUBs using Unsigned Saturation arithmetic -*/ -#define psubusw_m2r(var, reg) mmx_m2r(psubusw, var, reg) -#define psubusw_r2r(regs, regd) mmx_r2r(psubusw, regs, regd) -#define psubusw(vars, vard) mmx_m2m(psubusw, vars, vard) - -#define psubusb_m2r(var, reg) mmx_m2r(psubusb, var, reg) -#define psubusb_r2r(regs, regd) mmx_r2r(psubusb, regs, regd) -#define psubusb(vars, vard) mmx_m2m(psubusb, vars, vard) - - -/* 4x16 Parallel MULs giving Low 4x16 portions of results -*/ -#define pmullw_m2r(var, reg) mmx_m2r(pmullw, var, reg) -#define pmullw_r2r(regs, regd) mmx_r2r(pmullw, regs, regd) -#define pmullw(vars, vard) mmx_m2m(pmullw, vars, vard) - - -/* 4x16 Parallel MULs giving High 4x16 portions of results -*/ -#define pmulhw_m2r(var, reg) mmx_m2r(pmulhw, var, reg) -#define pmulhw_r2r(regs, regd) mmx_r2r(pmulhw, regs, regd) -#define pmulhw(vars, vard) mmx_m2m(pmulhw, vars, vard) - - -/* 4x16->2x32 Parallel Mul-ADD - (muls like pmullw, then adds adjacent 16-bit fields - in the multiply result to make the final 2x32 result) -*/ -#define pmaddwd_m2r(var, reg) mmx_m2r(pmaddwd, var, reg) -#define pmaddwd_r2r(regs, regd) mmx_r2r(pmaddwd, regs, regd) -#define pmaddwd(vars, vard) mmx_m2m(pmaddwd, vars, vard) - - -/* 1x64 bitwise AND -*/ -#ifdef BROKEN_PAND -#define pand_m2r(var, reg) \ - { \ - mmx_m2r(pandn, (mmx_t) -1LL, reg); \ - mmx_m2r(pandn, var, reg); \ - } -#define pand_r2r(regs, regd) \ - { \ - mmx_m2r(pandn, (mmx_t) -1LL, regd); \ - mmx_r2r(pandn, regs, regd) \ - } -#define pand(vars, vard) \ - { \ - movq_m2r(vard, mm0); \ - mmx_m2r(pandn, (mmx_t) -1LL, mm0); \ - mmx_m2r(pandn, vars, mm0); \ - movq_r2m(mm0, vard); \ - } -#else -#define pand_m2r(var, reg) mmx_m2r(pand, var, reg) -#define pand_r2r(regs, regd) mmx_r2r(pand, regs, regd) -#define pand(vars, vard) mmx_m2m(pand, vars, vard) -#endif - - -/* 1x64 bitwise AND with Not the destination -*/ -#define pandn_m2r(var, reg) mmx_m2r(pandn, var, reg) -#define pandn_r2r(regs, regd) mmx_r2r(pandn, regs, regd) -#define pandn(vars, vard) mmx_m2m(pandn, vars, vard) - - -/* 1x64 bitwise OR -*/ -#define por_m2r(var, reg) mmx_m2r(por, var, reg) -#define por_r2r(regs, regd) mmx_r2r(por, regs, regd) -#define por(vars, vard) mmx_m2m(por, vars, vard) - - -/* 1x64 bitwise eXclusive OR -*/ -#define pxor_m2r(var, reg) mmx_m2r(pxor, var, reg) -#define pxor_r2r(regs, regd) mmx_r2r(pxor, regs, regd) -#define pxor(vars, vard) mmx_m2m(pxor, vars, vard) - - -/* 2x32, 4x16, and 8x8 Parallel CoMPare for EQuality - (resulting fields are either 0 or -1) -*/ -#define pcmpeqd_m2r(var, reg) mmx_m2r(pcmpeqd, var, reg) -#define pcmpeqd_r2r(regs, regd) mmx_r2r(pcmpeqd, regs, regd) -#define pcmpeqd(vars, vard) mmx_m2m(pcmpeqd, vars, vard) - -#define pcmpeqw_m2r(var, reg) mmx_m2r(pcmpeqw, var, reg) -#define pcmpeqw_r2r(regs, regd) mmx_r2r(pcmpeqw, regs, regd) -#define pcmpeqw(vars, vard) mmx_m2m(pcmpeqw, vars, vard) - -#define pcmpeqb_m2r(var, reg) mmx_m2r(pcmpeqb, var, reg) -#define pcmpeqb_r2r(regs, regd) mmx_r2r(pcmpeqb, regs, regd) -#define pcmpeqb(vars, vard) mmx_m2m(pcmpeqb, vars, vard) - - -/* 2x32, 4x16, and 8x8 Parallel CoMPare for Greater Than - (resulting fields are either 0 or -1) -*/ -#define pcmpgtd_m2r(var, reg) mmx_m2r(pcmpgtd, var, reg) -#define pcmpgtd_r2r(regs, regd) mmx_r2r(pcmpgtd, regs, regd) -#define pcmpgtd(vars, vard) mmx_m2m(pcmpgtd, vars, vard) - -#define pcmpgtw_m2r(var, reg) mmx_m2r(pcmpgtw, var, reg) -#define pcmpgtw_r2r(regs, regd) mmx_r2r(pcmpgtw, regs, regd) -#define pcmpgtw(vars, vard) mmx_m2m(pcmpgtw, vars, vard) - -#define pcmpgtb_m2r(var, reg) mmx_m2r(pcmpgtb, var, reg) -#define pcmpgtb_r2r(regs, regd) mmx_r2r(pcmpgtb, regs, regd) -#define pcmpgtb(vars, vard) mmx_m2m(pcmpgtb, vars, vard) - - -/* 1x64, 2x32, and 4x16 Parallel Shift Left Logical -*/ -#define psllq_i2r(imm, reg) mmx_i2r(psllq, imm, reg) -#define psllq_m2r(var, reg) mmx_m2r(psllq, var, reg) -#define psllq_r2r(regs, regd) mmx_r2r(psllq, regs, regd) -#define psllq(vars, vard) mmx_m2m(psllq, vars, vard) - -#define pslld_i2r(imm, reg) mmx_i2r(pslld, imm, reg) -#define pslld_m2r(var, reg) mmx_m2r(pslld, var, reg) -#define pslld_r2r(regs, regd) mmx_r2r(pslld, regs, regd) -#define pslld(vars, vard) mmx_m2m(pslld, vars, vard) - -#define psllw_i2r(imm, reg) mmx_i2r(psllw, imm, reg) -#define psllw_m2r(var, reg) mmx_m2r(psllw, var, reg) -#define psllw_r2r(regs, regd) mmx_r2r(psllw, regs, regd) -#define psllw(vars, vard) mmx_m2m(psllw, vars, vard) - - -/* 1x64, 2x32, and 4x16 Parallel Shift Right Logical -*/ -#define psrlq_i2r(imm, reg) mmx_i2r(psrlq, imm, reg) -#define psrlq_m2r(var, reg) mmx_m2r(psrlq, var, reg) -#define psrlq_r2r(regs, regd) mmx_r2r(psrlq, regs, regd) -#define psrlq(vars, vard) mmx_m2m(psrlq, vars, vard) - -#define psrld_i2r(imm, reg) mmx_i2r(psrld, imm, reg) -#define psrld_m2r(var, reg) mmx_m2r(psrld, var, reg) -#define psrld_r2r(regs, regd) mmx_r2r(psrld, regs, regd) -#define psrld(vars, vard) mmx_m2m(psrld, vars, vard) - -#define psrlw_i2r(imm, reg) mmx_i2r(psrlw, imm, reg) -#define psrlw_m2r(var, reg) mmx_m2r(psrlw, var, reg) -#define psrlw_r2r(regs, regd) mmx_r2r(psrlw, regs, regd) -#define psrlw(vars, vard) mmx_m2m(psrlw, vars, vard) - - -/* 2x32 and 4x16 Parallel Shift Right Arithmetic -*/ -#define psrad_i2r(imm, reg) mmx_i2r(psrad, imm, reg) -#define psrad_m2r(var, reg) mmx_m2r(psrad, var, reg) -#define psrad_r2r(regs, regd) mmx_r2r(psrad, regs, regd) -#define psrad(vars, vard) mmx_m2m(psrad, vars, vard) - -#define psraw_i2r(imm, reg) mmx_i2r(psraw, imm, reg) -#define psraw_m2r(var, reg) mmx_m2r(psraw, var, reg) -#define psraw_r2r(regs, regd) mmx_r2r(psraw, regs, regd) -#define psraw(vars, vard) mmx_m2m(psraw, vars, vard) - - -/* 2x32->4x16 and 4x16->8x8 PACK and Signed Saturate - (packs source and dest fields into dest in that order) -*/ -#define packssdw_m2r(var, reg) mmx_m2r(packssdw, var, reg) -#define packssdw_r2r(regs, regd) mmx_r2r(packssdw, regs, regd) -#define packssdw(vars, vard) mmx_m2m(packssdw, vars, vard) - -#define packsswb_m2r(var, reg) mmx_m2r(packsswb, var, reg) -#define packsswb_r2r(regs, regd) mmx_r2r(packsswb, regs, regd) -#define packsswb(vars, vard) mmx_m2m(packsswb, vars, vard) - - -/* 4x16->8x8 PACK and Unsigned Saturate - (packs source and dest fields into dest in that order) -*/ -#define packuswb_m2r(var, reg) mmx_m2r(packuswb, var, reg) -#define packuswb_r2r(regs, regd) mmx_r2r(packuswb, regs, regd) -#define packuswb(vars, vard) mmx_m2m(packuswb, vars, vard) - - -/* 2x32->1x64, 4x16->2x32, and 8x8->4x16 UNPaCK Low - (interleaves low half of dest with low half of source - as padding in each result field) -*/ -#define punpckldq_m2r(var, reg) mmx_m2r(punpckldq, var, reg) -#define punpckldq_r2r(regs, regd) mmx_r2r(punpckldq, regs, regd) -#define punpckldq(vars, vard) mmx_m2m(punpckldq, vars, vard) - -#define punpcklwd_m2r(var, reg) mmx_m2r(punpcklwd, var, reg) -#define punpcklwd_r2r(regs, regd) mmx_r2r(punpcklwd, regs, regd) -#define punpcklwd(vars, vard) mmx_m2m(punpcklwd, vars, vard) - -#define punpcklbw_m2r(var, reg) mmx_m2r(punpcklbw, var, reg) -#define punpcklbw_r2r(regs, regd) mmx_r2r(punpcklbw, regs, regd) -#define punpcklbw(vars, vard) mmx_m2m(punpcklbw, vars, vard) - - -/* 2x32->1x64, 4x16->2x32, and 8x8->4x16 UNPaCK High - (interleaves high half of dest with high half of source - as padding in each result field) -*/ -#define punpckhdq_m2r(var, reg) mmx_m2r(punpckhdq, var, reg) -#define punpckhdq_r2r(regs, regd) mmx_r2r(punpckhdq, regs, regd) -#define punpckhdq(vars, vard) mmx_m2m(punpckhdq, vars, vard) - -#define punpckhwd_m2r(var, reg) mmx_m2r(punpckhwd, var, reg) -#define punpckhwd_r2r(regs, regd) mmx_r2r(punpckhwd, regs, regd) -#define punpckhwd(vars, vard) mmx_m2m(punpckhwd, vars, vard) - -#define punpckhbw_m2r(var, reg) mmx_m2r(punpckhbw, var, reg) -#define punpckhbw_r2r(regs, regd) mmx_r2r(punpckhbw, regs, regd) -#define punpckhbw(vars, vard) mmx_m2m(punpckhbw, vars, vard) - - -/* Empty MMx State - (used to clean-up when going from mmx to float use - of the registers that are shared by both; note that - there is no float-to-mmx operation needed, because - only the float tag word info is corruptible) -*/ -#ifdef MMX_TRACE - -#define emms() \ - { \ - printf("emms()\n"); \ - __asm__ __volatile__ ("emms"); \ - } - -#else - -#define emms() __asm__ __volatile__ ("emms") - -#endif - -#endif -/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index a950d8b815c9e..edbc8641d2000 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -1349,13 +1349,37 @@ GL_SetupCopy(SDL_Renderer * renderer, SDL_Texture * texture) GL_SetBlendMode(data, texture->blendMode); - if (texturedata->yuv) { - GL_SetShader(data, SHADER_YUV); - } else if (texturedata->nv12) { - if (texture->format == SDL_PIXELFORMAT_NV12) { - GL_SetShader(data, SHADER_NV12); - } else { - GL_SetShader(data, SHADER_NV21); + if (texturedata->yuv || texturedata->nv12) { + switch (SDL_GetYUVConversionModeForResolution(texture->w, texture->h)) { + case SDL_YUV_CONVERSION_JPEG: + if (texturedata->yuv) { + GL_SetShader(data, SHADER_YUV_JPEG); + } else if (texture->format == SDL_PIXELFORMAT_NV12) { + GL_SetShader(data, SHADER_NV12_JPEG); + } else { + GL_SetShader(data, SHADER_NV21_JPEG); + } + break; + case SDL_YUV_CONVERSION_BT601: + if (texturedata->yuv) { + GL_SetShader(data, SHADER_YUV_BT601); + } else if (texture->format == SDL_PIXELFORMAT_NV12) { + GL_SetShader(data, SHADER_NV12_BT601); + } else { + GL_SetShader(data, SHADER_NV21_BT601); + } + break; + case SDL_YUV_CONVERSION_BT709: + if (texturedata->yuv) { + GL_SetShader(data, SHADER_YUV_BT709); + } else if (texture->format == SDL_PIXELFORMAT_NV12) { + GL_SetShader(data, SHADER_NV12_BT709); + } else { + GL_SetShader(data, SHADER_NV21_BT709); + } + break; + default: + return SDL_SetError("Unsupported YUV conversion mode"); } } else { GL_SetShader(data, SHADER_RGB); diff --git a/src/render/opengl/SDL_shaders_gl.c b/src/render/opengl/SDL_shaders_gl.c index 6d0bf8437f7e9..c94fd925e9266 100644 --- a/src/render/opengl/SDL_shaders_gl.c +++ b/src/render/opengl/SDL_shaders_gl.c @@ -62,6 +62,151 @@ struct GL_ShaderContext GL_ShaderData shaders[NUM_SHADERS]; }; +#define COLOR_VERTEX_SHADER \ +"varying vec4 v_color;\n" \ +"\n" \ +"void main()\n" \ +"{\n" \ +" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" \ +" v_color = gl_Color;\n" \ +"}" \ + +#define TEXTURE_VERTEX_SHADER \ +"varying vec4 v_color;\n" \ +"varying vec2 v_texCoord;\n" \ +"\n" \ +"void main()\n" \ +"{\n" \ +" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" \ +" v_color = gl_Color;\n" \ +" v_texCoord = vec2(gl_MultiTexCoord0);\n" \ +"}" \ + +#define JPEG_SHADER_CONSTANTS \ +"// YUV offset \n" \ +"const vec3 offset = vec3(0, -0.501960814, -0.501960814);\n" \ +"\n" \ +"// RGB coefficients \n" \ +"const vec3 Rcoeff = vec3(1, 0.000, 1.402);\n" \ +"const vec3 Gcoeff = vec3(1, -0.3441, -0.7141);\n" \ +"const vec3 Bcoeff = vec3(1, 1.772, 0.000);\n" \ + +#define BT601_SHADER_CONSTANTS \ +"// YUV offset \n" \ +"const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" \ +"\n" \ +"// RGB coefficients \n" \ +"const vec3 Rcoeff = vec3(1.1644, 0.000, 1.596);\n" \ +"const vec3 Gcoeff = vec3(1.1644, -0.3918, -0.813);\n" \ +"const vec3 Bcoeff = vec3(1.1644, 2.0172, 0.000);\n" \ + +#define BT709_SHADER_CONSTANTS \ +"// YUV offset \n" \ +"const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" \ +"\n" \ +"// RGB coefficients \n" \ +"const vec3 Rcoeff = vec3(1.1644, 0.000, 1.7927);\n" \ +"const vec3 Gcoeff = vec3(1.1644, -0.2132, -0.5329);\n" \ +"const vec3 Bcoeff = vec3(1.1644, 2.1124, 0.000);\n" \ + +#define YUV_SHADER_PROLOGUE \ +"varying vec4 v_color;\n" \ +"varying vec2 v_texCoord;\n" \ +"uniform sampler2D tex0; // Y \n" \ +"uniform sampler2D tex1; // U \n" \ +"uniform sampler2D tex2; // V \n" \ +"\n" \ + +#define YUV_SHADER_BODY \ +"\n" \ +"void main()\n" \ +"{\n" \ +" vec2 tcoord;\n" \ +" vec3 yuv, rgb;\n" \ +"\n" \ +" // Get the Y value \n" \ +" tcoord = v_texCoord;\n" \ +" yuv.x = texture2D(tex0, tcoord).r;\n" \ +"\n" \ +" // Get the U and V values \n" \ +" tcoord *= UVCoordScale;\n" \ +" yuv.y = texture2D(tex1, tcoord).r;\n" \ +" yuv.z = texture2D(tex2, tcoord).r;\n" \ +"\n" \ +" // Do the color transform \n" \ +" yuv += offset;\n" \ +" rgb.r = dot(yuv, Rcoeff);\n" \ +" rgb.g = dot(yuv, Gcoeff);\n" \ +" rgb.b = dot(yuv, Bcoeff);\n" \ +"\n" \ +" // That was easy. :) \n" \ +" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" \ +"}" \ + +#define NV12_SHADER_PROLOGUE \ +"varying vec4 v_color;\n" \ +"varying vec2 v_texCoord;\n" \ +"uniform sampler2D tex0; // Y \n" \ +"uniform sampler2D tex1; // U/V \n" \ +"\n" \ + +#define NV12_SHADER_BODY \ +"\n" \ +"void main()\n" \ +"{\n" \ +" vec2 tcoord;\n" \ +" vec3 yuv, rgb;\n" \ +"\n" \ +" // Get the Y value \n" \ +" tcoord = v_texCoord;\n" \ +" yuv.x = texture2D(tex0, tcoord).r;\n" \ +"\n" \ +" // Get the U and V values \n" \ +" tcoord *= UVCoordScale;\n" \ +" yuv.yz = texture2D(tex1, tcoord).ra;\n" \ +"\n" \ +" // Do the color transform \n" \ +" yuv += offset;\n" \ +" rgb.r = dot(yuv, Rcoeff);\n" \ +" rgb.g = dot(yuv, Gcoeff);\n" \ +" rgb.b = dot(yuv, Bcoeff);\n" \ +"\n" \ +" // That was easy. :) \n" \ +" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" \ +"}" \ + +#define NV21_SHADER_PROLOGUE \ +"varying vec4 v_color;\n" \ +"varying vec2 v_texCoord;\n" \ +"uniform sampler2D tex0; // Y \n" \ +"uniform sampler2D tex1; // U/V \n" \ +"\n" \ + +#define NV21_SHADER_BODY \ +"\n" \ +"void main()\n" \ +"{\n" \ +" vec2 tcoord;\n" \ +" vec3 yuv, rgb;\n" \ +"\n" \ +" // Get the Y value \n" \ +" tcoord = v_texCoord;\n" \ +" yuv.x = texture2D(tex0, tcoord).r;\n" \ +"\n" \ +" // Get the U and V values \n" \ +" tcoord *= UVCoordScale;\n" \ +" yuv.yz = texture2D(tex1, tcoord).ar;\n" \ +"\n" \ +" // Do the color transform \n" \ +" yuv += offset;\n" \ +" rgb.r = dot(yuv, Rcoeff);\n" \ +" rgb.g = dot(yuv, Gcoeff);\n" \ +" rgb.b = dot(yuv, Bcoeff);\n" \ +"\n" \ +" // That was easy. :) \n" \ +" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" \ +"}" \ + /* * NOTE: Always use sampler2D, etc here. We'll #define them to the * texture_rectangle versions if we choose to use that extension. @@ -74,13 +219,7 @@ static const char *shader_source[NUM_SHADERS][2] = /* SHADER_SOLID */ { /* vertex shader */ -"varying vec4 v_color;\n" -"\n" -"void main()\n" -"{\n" -" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" -" v_color = gl_Color;\n" -"}", + COLOR_VERTEX_SHADER, /* fragment shader */ "varying vec4 v_color;\n" "\n" @@ -93,15 +232,7 @@ static const char *shader_source[NUM_SHADERS][2] = /* SHADER_RGB */ { /* vertex shader */ -"varying vec4 v_color;\n" -"varying vec2 v_texCoord;\n" -"\n" -"void main()\n" -"{\n" -" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" -" v_color = gl_Color;\n" -" v_texCoord = vec2(gl_MultiTexCoord0);\n" -"}", + TEXTURE_VERTEX_SHADER, /* fragment shader */ "varying vec4 v_color;\n" "varying vec2 v_texCoord;\n" @@ -113,156 +244,86 @@ static const char *shader_source[NUM_SHADERS][2] = "}" }, - /* SHADER_YUV */ + /* SHADER_YUV_JPEG */ { /* vertex shader */ -"varying vec4 v_color;\n" -"varying vec2 v_texCoord;\n" -"\n" -"void main()\n" -"{\n" -" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" -" v_color = gl_Color;\n" -" v_texCoord = vec2(gl_MultiTexCoord0);\n" -"}", + TEXTURE_VERTEX_SHADER, /* fragment shader */ -"varying vec4 v_color;\n" -"varying vec2 v_texCoord;\n" -"uniform sampler2D tex0; // Y \n" -"uniform sampler2D tex1; // U \n" -"uniform sampler2D tex2; // V \n" -"\n" -"// YUV offset \n" -"const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" -"\n" -"// RGB coefficients \n" -"const vec3 Rcoeff = vec3(1.164, 0.000, 1.596);\n" -"const vec3 Gcoeff = vec3(1.164, -0.391, -0.813);\n" -"const vec3 Bcoeff = vec3(1.164, 2.018, 0.000);\n" -"\n" -"void main()\n" -"{\n" -" vec2 tcoord;\n" -" vec3 yuv, rgb;\n" -"\n" -" // Get the Y value \n" -" tcoord = v_texCoord;\n" -" yuv.x = texture2D(tex0, tcoord).r;\n" -"\n" -" // Get the U and V values \n" -" tcoord *= UVCoordScale;\n" -" yuv.y = texture2D(tex1, tcoord).r;\n" -" yuv.z = texture2D(tex2, tcoord).r;\n" -"\n" -" // Do the color transform \n" -" yuv += offset;\n" -" rgb.r = dot(yuv, Rcoeff);\n" -" rgb.g = dot(yuv, Gcoeff);\n" -" rgb.b = dot(yuv, Bcoeff);\n" -"\n" -" // That was easy. :) \n" -" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" -"}" + YUV_SHADER_PROLOGUE + JPEG_SHADER_CONSTANTS + YUV_SHADER_BODY }, - - /* SHADER_NV12 */ + /* SHADER_YUV_BT601 */ { /* vertex shader */ -"varying vec4 v_color;\n" -"varying vec2 v_texCoord;\n" -"\n" -"void main()\n" -"{\n" -" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" -" v_color = gl_Color;\n" -" v_texCoord = vec2(gl_MultiTexCoord0);\n" -"}", + TEXTURE_VERTEX_SHADER, /* fragment shader */ -"varying vec4 v_color;\n" -"varying vec2 v_texCoord;\n" -"uniform sampler2D tex0; // Y \n" -"uniform sampler2D tex1; // U/V \n" -"\n" -"// YUV offset \n" -"const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" -"\n" -"// RGB coefficients \n" -"const vec3 Rcoeff = vec3(1.164, 0.000, 1.596);\n" -"const vec3 Gcoeff = vec3(1.164, -0.391, -0.813);\n" -"const vec3 Bcoeff = vec3(1.164, 2.018, 0.000);\n" -"\n" -"void main()\n" -"{\n" -" vec2 tcoord;\n" -" vec3 yuv, rgb;\n" -"\n" -" // Get the Y value \n" -" tcoord = v_texCoord;\n" -" yuv.x = texture2D(tex0, tcoord).r;\n" -"\n" -" // Get the U and V values \n" -" tcoord *= UVCoordScale;\n" -" yuv.yz = texture2D(tex1, tcoord).ra;\n" -"\n" -" // Do the color transform \n" -" yuv += offset;\n" -" rgb.r = dot(yuv, Rcoeff);\n" -" rgb.g = dot(yuv, Gcoeff);\n" -" rgb.b = dot(yuv, Bcoeff);\n" -"\n" -" // That was easy. :) \n" -" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" -"}" + YUV_SHADER_PROLOGUE + BT601_SHADER_CONSTANTS + YUV_SHADER_BODY }, - - /* SHADER_NV21 */ + /* SHADER_YUV_BT709 */ { /* vertex shader */ -"varying vec4 v_color;\n" -"varying vec2 v_texCoord;\n" -"\n" -"void main()\n" -"{\n" -" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" -" v_color = gl_Color;\n" -" v_texCoord = vec2(gl_MultiTexCoord0);\n" -"}", + TEXTURE_VERTEX_SHADER, /* fragment shader */ -"varying vec4 v_color;\n" -"varying vec2 v_texCoord;\n" -"uniform sampler2D tex0; // Y \n" -"uniform sampler2D tex1; // U/V \n" -"\n" -"// YUV offset \n" -"const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" -"\n" -"// RGB coefficients \n" -"const vec3 Rcoeff = vec3(1.164, 0.000, 1.596);\n" -"const vec3 Gcoeff = vec3(1.164, -0.391, -0.813);\n" -"const vec3 Bcoeff = vec3(1.164, 2.018, 0.000);\n" -"\n" -"void main()\n" -"{\n" -" vec2 tcoord;\n" -" vec3 yuv, rgb;\n" -"\n" -" // Get the Y value \n" -" tcoord = v_texCoord;\n" -" yuv.x = texture2D(tex0, tcoord).r;\n" -"\n" -" // Get the U and V values \n" -" tcoord *= UVCoordScale;\n" -" yuv.yz = texture2D(tex1, tcoord).ar;\n" -"\n" -" // Do the color transform \n" -" yuv += offset;\n" -" rgb.r = dot(yuv, Rcoeff);\n" -" rgb.g = dot(yuv, Gcoeff);\n" -" rgb.b = dot(yuv, Bcoeff);\n" -"\n" -" // That was easy. :) \n" -" gl_FragColor = vec4(rgb, 1.0) * v_color;\n" -"}" + YUV_SHADER_PROLOGUE + BT709_SHADER_CONSTANTS + YUV_SHADER_BODY + }, + /* SHADER_NV12_JPEG */ + { + /* vertex shader */ + TEXTURE_VERTEX_SHADER, + /* fragment shader */ + NV12_SHADER_PROLOGUE + JPEG_SHADER_CONSTANTS + NV12_SHADER_BODY + }, + /* SHADER_NV12_BT601 */ + { + /* vertex shader */ + TEXTURE_VERTEX_SHADER, + /* fragment shader */ + NV12_SHADER_PROLOGUE + BT601_SHADER_CONSTANTS + NV12_SHADER_BODY + }, + /* SHADER_NV12_BT709 */ + { + /* vertex shader */ + TEXTURE_VERTEX_SHADER, + /* fragment shader */ + NV12_SHADER_PROLOGUE + BT709_SHADER_CONSTANTS + NV12_SHADER_BODY + }, + /* SHADER_NV21_JPEG */ + { + /* vertex shader */ + TEXTURE_VERTEX_SHADER, + /* fragment shader */ + NV21_SHADER_PROLOGUE + JPEG_SHADER_CONSTANTS + NV21_SHADER_BODY + }, + /* SHADER_NV21_BT601 */ + { + /* vertex shader */ + TEXTURE_VERTEX_SHADER, + /* fragment shader */ + NV21_SHADER_PROLOGUE + BT601_SHADER_CONSTANTS + NV21_SHADER_BODY + }, + /* SHADER_NV21_BT709 */ + { + /* vertex shader */ + TEXTURE_VERTEX_SHADER, + /* fragment shader */ + NV21_SHADER_PROLOGUE + BT709_SHADER_CONSTANTS + NV21_SHADER_BODY }, }; diff --git a/src/render/opengl/SDL_shaders_gl.h b/src/render/opengl/SDL_shaders_gl.h index 9489b6116a816..6e22f74bbade4 100644 --- a/src/render/opengl/SDL_shaders_gl.h +++ b/src/render/opengl/SDL_shaders_gl.h @@ -26,9 +26,15 @@ typedef enum { SHADER_NONE, SHADER_SOLID, SHADER_RGB, - SHADER_YUV, - SHADER_NV12, - SHADER_NV21, + SHADER_YUV_JPEG, + SHADER_YUV_BT601, + SHADER_YUV_BT709, + SHADER_NV12_JPEG, + SHADER_NV12_BT601, + SHADER_NV12_BT709, + SHADER_NV21_JPEG, + SHADER_NV21_BT601, + SHADER_NV21_BT709, NUM_SHADERS } GL_Shader; diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index bd343cf4fed47..4b09e3a043092 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -950,7 +950,7 @@ static void GLES2_EvictShader(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *en static GLES2_ProgramCacheEntry *GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex, GLES2_ShaderCacheEntry *fragment); -static int GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source); +static int GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, int w, int h); static GLES2_ProgramCacheEntry * GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex, @@ -1189,7 +1189,7 @@ GLES2_EvictShader(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *entry) } static int -GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source) +GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, int w, int h) { GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata; GLES2_ShaderCacheEntry *vertex = NULL; @@ -1216,13 +1216,52 @@ GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source) ftype = GLES2_SHADER_FRAGMENT_TEXTURE_BGR_SRC; break; case GLES2_IMAGESOURCE_TEXTURE_YUV: - ftype = GLES2_SHADER_FRAGMENT_TEXTURE_YUV_SRC; + switch (SDL_GetYUVConversionModeForResolution(w, h)) { + case SDL_YUV_CONVERSION_JPEG: + ftype = GLES2_SHADER_FRAGMENT_TEXTURE_YUV_JPEG_SRC; + break; + case SDL_YUV_CONVERSION_BT601: + ftype = GLES2_SHADER_FRAGMENT_TEXTURE_YUV_BT601_SRC; + break; + case SDL_YUV_CONVERSION_BT709: + ftype = GLES2_SHADER_FRAGMENT_TEXTURE_YUV_BT709_SRC; + break; + default: + SDL_SetError("Unsupported YUV conversion mode: %d\n", SDL_GetYUVConversionModeForResolution(w, h)); + goto fault; + } break; case GLES2_IMAGESOURCE_TEXTURE_NV12: - ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV12_SRC; + switch (SDL_GetYUVConversionModeForResolution(w, h)) { + case SDL_YUV_CONVERSION_JPEG: + ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV12_JPEG_SRC; + break; + case SDL_YUV_CONVERSION_BT601: + ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV12_BT601_SRC; + break; + case SDL_YUV_CONVERSION_BT709: + ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV12_BT709_SRC; + break; + default: + SDL_SetError("Unsupported YUV conversion mode: %d\n", SDL_GetYUVConversionModeForResolution(w, h)); + goto fault; + } break; case GLES2_IMAGESOURCE_TEXTURE_NV21: - ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV21_SRC; + switch (SDL_GetYUVConversionModeForResolution(w, h)) { + case SDL_YUV_CONVERSION_JPEG: + ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV21_JPEG_SRC; + break; + case SDL_YUV_CONVERSION_BT601: + ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV21_BT601_SRC; + break; + case SDL_YUV_CONVERSION_BT709: + ftype = GLES2_SHADER_FRAGMENT_TEXTURE_NV21_BT709_SRC; + break; + default: + SDL_SetError("Unsupported YUV conversion mode: %d\n", SDL_GetYUVConversionModeForResolution(w, h)); + goto fault; + } break; default: goto fault; @@ -1445,7 +1484,7 @@ GLES2_SetDrawingState(SDL_Renderer * renderer) GLES2_SetTexCoords(data, SDL_FALSE); /* Activate an appropriate shader and set the projection matrix */ - if (GLES2_SelectProgram(renderer, GLES2_IMAGESOURCE_SOLID) < 0) { + if (GLES2_SelectProgram(renderer, GLES2_IMAGESOURCE_SOLID, 0, 0) < 0) { return -1; } @@ -1707,7 +1746,7 @@ GLES2_SetupCopy(SDL_Renderer *renderer, SDL_Texture *texture) } } - if (GLES2_SelectProgram(renderer, sourceType) < 0) { + if (GLES2_SelectProgram(renderer, sourceType, texture->w, texture->h) < 0) { return -1; } diff --git a/src/render/opengles2/SDL_shaders_gles2.c b/src/render/opengles2/SDL_shaders_gles2.c index 77d40b39cb289..8b3306b66cd1e 100644 --- a/src/render/opengles2/SDL_shaders_gles2.c +++ b/src/render/opengles2/SDL_shaders_gles2.c @@ -126,73 +126,154 @@ static const Uint8 GLES2_FragmentSrc_TextureBGRSrc_[] = " \ } \ "; +#define JPEG_SHADER_CONSTANTS \ +"// YUV offset \n" \ +"const vec3 offset = vec3(0, -0.501960814, -0.501960814);\n" \ +"\n" \ +"// RGB coefficients \n" \ +"const mat3 matrix = mat3( 1, 1, 1,\n" \ +" 0, -0.3441, 1.772,\n" \ +" 1.402, -0.7141, 0);\n" \ + +#define BT601_SHADER_CONSTANTS \ +"// YUV offset \n" \ +"const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" \ +"\n" \ +"// RGB coefficients \n" \ +"const mat3 matrix = mat3( 1.1644, 1.1644, 1.1644,\n" \ +" 0, -0.3918, 2.0172,\n" \ +" 1.596, -0.813, 0);\n" \ + +#define BT709_SHADER_CONSTANTS \ +"// YUV offset \n" \ +"const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" \ +"\n" \ +"// RGB coefficients \n" \ +"const mat3 matrix = mat3( 1.1644, 1.1644, 1.1644,\n" \ +" 0, -0.2132, 2.1124,\n" \ +" 1.7927, -0.5329, 0);\n" \ + + +#define YUV_SHADER_PROLOGUE \ +"precision mediump float;\n" \ +"uniform sampler2D u_texture;\n" \ +"uniform sampler2D u_texture_u;\n" \ +"uniform sampler2D u_texture_v;\n" \ +"uniform vec4 u_modulation;\n" \ +"varying vec2 v_texCoord;\n" \ +"\n" \ + +#define YUV_SHADER_BODY \ +"\n" \ +"void main()\n" \ +"{\n" \ +" mediump vec3 yuv;\n" \ +" lowp vec3 rgb;\n" \ +"\n" \ +" // Get the YUV values \n" \ +" yuv.x = texture2D(u_texture, v_texCoord).r;\n" \ +" yuv.y = texture2D(u_texture_u, v_texCoord).r;\n" \ +" yuv.z = texture2D(u_texture_v, v_texCoord).r;\n" \ +"\n" \ +" // Do the color transform \n" \ +" yuv += offset;\n" \ +" rgb = matrix * yuv;\n" \ +"\n" \ +" // That was easy. :) \n" \ +" gl_FragColor = vec4(rgb, 1);\n" \ +" gl_FragColor *= u_modulation;\n" \ +"}" \ + +#define NV12_SHADER_BODY \ +"\n" \ +"void main()\n" \ +"{\n" \ +" mediump vec3 yuv;\n" \ +" lowp vec3 rgb;\n" \ +"\n" \ +" // Get the YUV values \n" \ +" yuv.x = texture2D(u_texture, v_texCoord).r;\n" \ +" yuv.yz = texture2D(u_texture_u, v_texCoord).ra;\n" \ +"\n" \ +" // Do the color transform \n" \ +" yuv += offset;\n" \ +" rgb = matrix * yuv;\n" \ +"\n" \ +" // That was easy. :) \n" \ +" gl_FragColor = vec4(rgb, 1);\n" \ +" gl_FragColor *= u_modulation;\n" \ +"}" \ + +#define NV21_SHADER_BODY \ +"\n" \ +"void main()\n" \ +"{\n" \ +" mediump vec3 yuv;\n" \ +" lowp vec3 rgb;\n" \ +"\n" \ +" // Get the YUV values \n" \ +" yuv.x = texture2D(u_texture, v_texCoord).r;\n" \ +" yuv.yz = texture2D(u_texture_u, v_texCoord).ar;\n" \ +"\n" \ +" // Do the color transform \n" \ +" yuv += offset;\n" \ +" rgb = matrix * yuv;\n" \ +"\n" \ +" // That was easy. :) \n" \ +" gl_FragColor = vec4(rgb, 1);\n" \ +" gl_FragColor *= u_modulation;\n" \ +"}" \ + /* YUV to ABGR conversion */ -static const Uint8 GLES2_FragmentSrc_TextureYUVSrc_[] = " \ - precision mediump float; \ - uniform sampler2D u_texture; \ - uniform sampler2D u_texture_u; \ - uniform sampler2D u_texture_v; \ - uniform vec4 u_modulation; \ - varying vec2 v_texCoord; \ - \ - void main() \ - { \ - mediump vec3 yuv; \ - lowp vec3 rgb; \ - yuv.x = texture2D(u_texture, v_texCoord).r; \ - yuv.y = texture2D(u_texture_u, v_texCoord).r - 0.5; \ - yuv.z = texture2D(u_texture_v, v_texCoord).r - 0.5; \ - rgb = mat3( 1, 1, 1, \ - 0, -0.39465, 2.03211, \ - 1.13983, -0.58060, 0) * yuv; \ - gl_FragColor = vec4(rgb, 1); \ - gl_FragColor *= u_modulation; \ - } \ -"; +static const Uint8 GLES2_FragmentSrc_TextureYUVJPEGSrc_[] = \ + YUV_SHADER_PROLOGUE \ + JPEG_SHADER_CONSTANTS \ + YUV_SHADER_BODY \ +; +static const Uint8 GLES2_FragmentSrc_TextureYUVBT601Src_[] = \ + YUV_SHADER_PROLOGUE \ + BT601_SHADER_CONSTANTS \ + YUV_SHADER_BODY \ +; +static const Uint8 GLES2_FragmentSrc_TextureYUVBT709Src_[] = \ + YUV_SHADER_PROLOGUE \ + BT709_SHADER_CONSTANTS \ + YUV_SHADER_BODY \ +; /* NV12 to ABGR conversion */ -static const Uint8 GLES2_FragmentSrc_TextureNV12Src_[] = " \ - precision mediump float; \ - uniform sampler2D u_texture; \ - uniform sampler2D u_texture_u; \ - uniform vec4 u_modulation; \ - varying vec2 v_texCoord; \ - \ - void main() \ - { \ - mediump vec3 yuv; \ - lowp vec3 rgb; \ - yuv.x = texture2D(u_texture, v_texCoord).r; \ - yuv.yz = texture2D(u_texture_u, v_texCoord).ra - 0.5; \ - rgb = mat3( 1, 1, 1, \ - 0, -0.39465, 2.03211, \ - 1.13983, -0.58060, 0) * yuv; \ - gl_FragColor = vec4(rgb, 1); \ - gl_FragColor *= u_modulation; \ - } \ -"; +static const Uint8 GLES2_FragmentSrc_TextureNV12JPEGSrc_[] = \ + YUV_SHADER_PROLOGUE \ + JPEG_SHADER_CONSTANTS \ + NV12_SHADER_BODY \ +; +static const Uint8 GLES2_FragmentSrc_TextureNV12BT601Src_[] = \ + YUV_SHADER_PROLOGUE \ + BT601_SHADER_CONSTANTS \ + NV12_SHADER_BODY \ +; +static const Uint8 GLES2_FragmentSrc_TextureNV12BT709Src_[] = \ + YUV_SHADER_PROLOGUE \ + BT709_SHADER_CONSTANTS \ + NV12_SHADER_BODY \ +; /* NV21 to ABGR conversion */ -static const Uint8 GLES2_FragmentSrc_TextureNV21Src_[] = " \ - precision mediump float; \ - uniform sampler2D u_texture; \ - uniform sampler2D u_texture_u; \ - uniform vec4 u_modulation; \ - varying vec2 v_texCoord; \ - \ - void main() \ - { \ - mediump vec3 yuv; \ - lowp vec3 rgb; \ - yuv.x = texture2D(u_texture, v_texCoord).r; \ - yuv.yz = texture2D(u_texture_u, v_texCoord).ar - 0.5; \ - rgb = mat3( 1, 1, 1, \ - 0, -0.39465, 2.03211, \ - 1.13983, -0.58060, 0) * yuv; \ - gl_FragColor = vec4(rgb, 1); \ - gl_FragColor *= u_modulation; \ - } \ -"; +static const Uint8 GLES2_FragmentSrc_TextureNV21JPEGSrc_[] = \ + YUV_SHADER_PROLOGUE \ + JPEG_SHADER_CONSTANTS \ + NV21_SHADER_BODY \ +; +static const Uint8 GLES2_FragmentSrc_TextureNV21BT601Src_[] = \ + YUV_SHADER_PROLOGUE \ + BT601_SHADER_CONSTANTS \ + NV21_SHADER_BODY \ +; +static const Uint8 GLES2_FragmentSrc_TextureNV21BT709Src_[] = \ + YUV_SHADER_PROLOGUE \ + BT709_SHADER_CONSTANTS \ + NV21_SHADER_BODY \ +; static const GLES2_ShaderInstance GLES2_VertexSrc_Default = { GL_VERTEX_SHADER, @@ -236,25 +317,67 @@ static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureBGRSrc = { GLES2_FragmentSrc_TextureBGRSrc_ }; -static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureYUVSrc = { +static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureYUVJPEGSrc = { GL_FRAGMENT_SHADER, GLES2_SOURCE_SHADER, - sizeof(GLES2_FragmentSrc_TextureYUVSrc_), - GLES2_FragmentSrc_TextureYUVSrc_ + sizeof(GLES2_FragmentSrc_TextureYUVJPEGSrc_), + GLES2_FragmentSrc_TextureYUVJPEGSrc_ }; -static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureNV12Src = { +static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureYUVBT601Src = { GL_FRAGMENT_SHADER, GLES2_SOURCE_SHADER, - sizeof(GLES2_FragmentSrc_TextureNV12Src_), - GLES2_FragmentSrc_TextureNV12Src_ + sizeof(GLES2_FragmentSrc_TextureYUVBT601Src_), + GLES2_FragmentSrc_TextureYUVBT601Src_ }; -static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureNV21Src = { +static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureYUVBT709Src = { GL_FRAGMENT_SHADER, GLES2_SOURCE_SHADER, - sizeof(GLES2_FragmentSrc_TextureNV21Src_), - GLES2_FragmentSrc_TextureNV21Src_ + sizeof(GLES2_FragmentSrc_TextureYUVBT709Src_), + GLES2_FragmentSrc_TextureYUVBT709Src_ +}; + +static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureNV12JPEGSrc = { + GL_FRAGMENT_SHADER, + GLES2_SOURCE_SHADER, + sizeof(GLES2_FragmentSrc_TextureNV12JPEGSrc_), + GLES2_FragmentSrc_TextureNV12JPEGSrc_ +}; + +static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureNV12BT601Src = { + GL_FRAGMENT_SHADER, + GLES2_SOURCE_SHADER, + sizeof(GLES2_FragmentSrc_TextureNV12BT601Src_), + GLES2_FragmentSrc_TextureNV12BT601Src_ +}; + +static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureNV21BT709Src = { + GL_FRAGMENT_SHADER, + GLES2_SOURCE_SHADER, + sizeof(GLES2_FragmentSrc_TextureNV21BT709Src_), + GLES2_FragmentSrc_TextureNV21BT709Src_ +}; + +static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureNV21JPEGSrc = { + GL_FRAGMENT_SHADER, + GLES2_SOURCE_SHADER, + sizeof(GLES2_FragmentSrc_TextureNV21JPEGSrc_), + GLES2_FragmentSrc_TextureNV21JPEGSrc_ +}; + +static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureNV21BT601Src = { + GL_FRAGMENT_SHADER, + GLES2_SOURCE_SHADER, + sizeof(GLES2_FragmentSrc_TextureNV21BT601Src_), + GLES2_FragmentSrc_TextureNV21BT601Src_ +}; + +static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureNV12BT709Src = { + GL_FRAGMENT_SHADER, + GLES2_SOURCE_SHADER, + sizeof(GLES2_FragmentSrc_TextureNV12BT709Src_), + GLES2_FragmentSrc_TextureNV12BT709Src_ }; @@ -304,24 +427,66 @@ static GLES2_Shader GLES2_FragmentShader_TextureBGRSrc = { } }; -static GLES2_Shader GLES2_FragmentShader_TextureYUVSrc = { +static GLES2_Shader GLES2_FragmentShader_TextureYUVJPEGSrc = { + 1, + { + &GLES2_FragmentSrc_TextureYUVJPEGSrc + } +}; + +static GLES2_Shader GLES2_FragmentShader_TextureYUVBT601Src = { + 1, + { + &GLES2_FragmentSrc_TextureYUVBT601Src + } +}; + +static GLES2_Shader GLES2_FragmentShader_TextureYUVBT709Src = { + 1, + { + &GLES2_FragmentSrc_TextureYUVBT709Src + } +}; + +static GLES2_Shader GLES2_FragmentShader_TextureNV12JPEGSrc = { + 1, + { + &GLES2_FragmentSrc_TextureNV12JPEGSrc + } +}; + +static GLES2_Shader GLES2_FragmentShader_TextureNV12BT601Src = { + 1, + { + &GLES2_FragmentSrc_TextureNV12BT601Src + } +}; + +static GLES2_Shader GLES2_FragmentShader_TextureNV12BT709Src = { + 1, + { + &GLES2_FragmentSrc_TextureNV12BT709Src + } +}; + +static GLES2_Shader GLES2_FragmentShader_TextureNV21JPEGSrc = { 1, { - &GLES2_FragmentSrc_TextureYUVSrc + &GLES2_FragmentSrc_TextureNV21JPEGSrc } }; -static GLES2_Shader GLES2_FragmentShader_TextureNV12Src = { +static GLES2_Shader GLES2_FragmentShader_TextureNV21BT601Src = { 1, { - &GLES2_FragmentSrc_TextureNV12Src + &GLES2_FragmentSrc_TextureNV21BT601Src } }; -static GLES2_Shader GLES2_FragmentShader_TextureNV21Src = { +static GLES2_Shader GLES2_FragmentShader_TextureNV21BT709Src = { 1, { - &GLES2_FragmentSrc_TextureNV21Src + &GLES2_FragmentSrc_TextureNV21BT709Src } }; @@ -345,12 +510,24 @@ const GLES2_Shader *GLES2_GetShader(GLES2_ShaderType type) return &GLES2_FragmentShader_TextureRGBSrc; case GLES2_SHADER_FRAGMENT_TEXTURE_BGR_SRC: return &GLES2_FragmentShader_TextureBGRSrc; - case GLES2_SHADER_FRAGMENT_TEXTURE_YUV_SRC: - return &GLES2_FragmentShader_TextureYUVSrc; - case GLES2_SHADER_FRAGMENT_TEXTURE_NV12_SRC: - return &GLES2_FragmentShader_TextureNV12Src; - case GLES2_SHADER_FRAGMENT_TEXTURE_NV21_SRC: - return &GLES2_FragmentShader_TextureNV21Src; + case GLES2_SHADER_FRAGMENT_TEXTURE_YUV_JPEG_SRC: + return &GLES2_FragmentShader_TextureYUVJPEGSrc; + case GLES2_SHADER_FRAGMENT_TEXTURE_YUV_BT601_SRC: + return &GLES2_FragmentShader_TextureYUVBT601Src; + case GLES2_SHADER_FRAGMENT_TEXTURE_YUV_BT709_SRC: + return &GLES2_FragmentShader_TextureYUVBT709Src; + case GLES2_SHADER_FRAGMENT_TEXTURE_NV12_JPEG_SRC: + return &GLES2_FragmentShader_TextureNV12JPEGSrc; + case GLES2_SHADER_FRAGMENT_TEXTURE_NV12_BT601_SRC: + return &GLES2_FragmentShader_TextureNV12BT601Src; + case GLES2_SHADER_FRAGMENT_TEXTURE_NV12_BT709_SRC: + return &GLES2_FragmentShader_TextureNV12BT709Src; + case GLES2_SHADER_FRAGMENT_TEXTURE_NV21_JPEG_SRC: + return &GLES2_FragmentShader_TextureNV21JPEGSrc; + case GLES2_SHADER_FRAGMENT_TEXTURE_NV21_BT601_SRC: + return &GLES2_FragmentShader_TextureNV21BT601Src; + case GLES2_SHADER_FRAGMENT_TEXTURE_NV21_BT709_SRC: + return &GLES2_FragmentShader_TextureNV21BT709Src; default: return NULL; } diff --git a/src/render/opengles2/SDL_shaders_gles2.h b/src/render/opengles2/SDL_shaders_gles2.h index 1f2667eb3594f..6be9c2fd198a2 100644 --- a/src/render/opengles2/SDL_shaders_gles2.h +++ b/src/render/opengles2/SDL_shaders_gles2.h @@ -20,11 +20,11 @@ */ #include "../../SDL_internal.h" -#if SDL_VIDEO_RENDER_OGL_ES2 - #ifndef SDL_shaders_gles2_h_ #define SDL_shaders_gles2_h_ +#if SDL_VIDEO_RENDER_OGL_ES2 + typedef struct GLES2_ShaderInstance { GLenum type; @@ -47,17 +47,23 @@ typedef enum GLES2_SHADER_FRAGMENT_TEXTURE_ARGB_SRC, GLES2_SHADER_FRAGMENT_TEXTURE_BGR_SRC, GLES2_SHADER_FRAGMENT_TEXTURE_RGB_SRC, - GLES2_SHADER_FRAGMENT_TEXTURE_YUV_SRC, - GLES2_SHADER_FRAGMENT_TEXTURE_NV12_SRC, - GLES2_SHADER_FRAGMENT_TEXTURE_NV21_SRC + GLES2_SHADER_FRAGMENT_TEXTURE_YUV_JPEG_SRC, + GLES2_SHADER_FRAGMENT_TEXTURE_YUV_BT601_SRC, + GLES2_SHADER_FRAGMENT_TEXTURE_YUV_BT709_SRC, + GLES2_SHADER_FRAGMENT_TEXTURE_NV12_JPEG_SRC, + GLES2_SHADER_FRAGMENT_TEXTURE_NV12_BT601_SRC, + GLES2_SHADER_FRAGMENT_TEXTURE_NV12_BT709_SRC, + GLES2_SHADER_FRAGMENT_TEXTURE_NV21_JPEG_SRC, + GLES2_SHADER_FRAGMENT_TEXTURE_NV21_BT601_SRC, + GLES2_SHADER_FRAGMENT_TEXTURE_NV21_BT709_SRC, } GLES2_ShaderType; #define GLES2_SOURCE_SHADER (GLenum)-1 const GLES2_Shader *GLES2_GetShader(GLES2_ShaderType type); -#endif /* SDL_shaders_gles2_h_ */ - #endif /* SDL_VIDEO_RENDER_OGL_ES2 */ +#endif /* SDL_shaders_gles2_h_ */ + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index 649303c583eec..2244ec5925649 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -748,30 +748,6 @@ SDL_DitherColors(SDL_Color * colors, int bpp) } } -/* - * Calculate the pad-aligned scanline width of a surface - */ -int -SDL_CalculatePitch(SDL_Surface * surface) -{ - int pitch; - - /* Surface should be 4-byte aligned for speed */ - pitch = surface->w * surface->format->BytesPerPixel; - switch (surface->format->BitsPerPixel) { - case 1: - pitch = (pitch + 7) / 8; - break; - case 4: - pitch = (pitch + 1) / 2; - break; - default: - break; - } - pitch = (pitch + 3) & ~3; /* 4-byte aligning */ - return (pitch); -} - /* * Match an RGB value to a particular palette index */ diff --git a/src/video/SDL_pixels_c.h b/src/video/SDL_pixels_c.h index e79e8310246cd..89c07c76040cf 100644 --- a/src/video/SDL_pixels_c.h +++ b/src/video/SDL_pixels_c.h @@ -34,7 +34,6 @@ extern int SDL_MapSurface(SDL_Surface * src, SDL_Surface * dst); extern void SDL_FreeBlitMap(SDL_BlitMap * map); /* Miscellaneous functions */ -extern int SDL_CalculatePitch(SDL_Surface * surface); extern void SDL_DitherColors(SDL_Color * colors, int bpp); extern Uint8 SDL_FindColor(SDL_Palette * pal, Uint8 r, Uint8 g, Uint8 b, Uint8 a); diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index 38addea004206..2e114284ae8e7 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -25,17 +25,8 @@ #include "SDL_blit.h" #include "SDL_RLEaccel_c.h" #include "SDL_pixels_c.h" +#include "SDL_yuv_c.h" -/* Private routines */ -static int -SDL_ConvertPixels_YUV_to_ARGB8888(int width, int height, - Uint32 src_format, const void *src, - void *dst, int dst_pitch); - -static int -SDL_ConvertPixels_ARGB8888_to_YUV(int width, int height, - const void *src, int src_pitch, - Uint32 dst_format, void *dst); /* Check to make sure we can safely check multiplication of surface w and pitch and it won't overflow size_t */ SDL_COMPILE_TIME_ASSERT(surface_size_assumptions, @@ -43,6 +34,30 @@ SDL_COMPILE_TIME_ASSERT(surface_size_assumptions, /* Public routines */ +/* + * Calculate the pad-aligned scanline width of a surface + */ +int +SDL_CalculatePitch(Uint32 format, int width) +{ + int pitch; + + /* Surface should be 4-byte aligned for speed */ + pitch = width * SDL_BYTESPERPIXEL(format); + switch (SDL_BITSPERPIXEL(format)) { + case 1: + pitch = (pitch + 7) / 8; + break; + case 4: + pitch = (pitch + 1) / 2; + break; + default: + break; + } + pitch = (pitch + 3) & ~3; /* 4-byte aligning */ + return pitch; +} + /* * Create an empty RGB surface of the appropriate depth using the given * enum SDL_PIXELFORMAT_* format @@ -70,7 +85,7 @@ SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, } surface->w = width; surface->h = height; - surface->pitch = SDL_CalculatePitch(surface); + surface->pitch = SDL_CalculatePitch(format, width); SDL_SetClipRect(surface, NULL); if (SDL_ISPIXELFORMAT_INDEXED(surface->format->format)) { @@ -1138,135 +1153,27 @@ int SDL_ConvertPixels(int width, int height, return SDL_InvalidParamError("dst_pitch"); } + if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) { + return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch); + } else if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { + return SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch); + } else if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) { + return SDL_ConvertPixels_RGB_to_YUV(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch); + } + /* Fast path for same format copy */ if (src_format == dst_format) { int i; - - if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { - switch (src_format) { - case SDL_PIXELFORMAT_YUY2: - case SDL_PIXELFORMAT_UYVY: - case SDL_PIXELFORMAT_YVYU: - /* Packed planes */ - width = 4 * ((width + 1) / 2); - for (i = height; i--;) { - SDL_memcpy(dst, src, width); - src = (const Uint8*)src + src_pitch; - dst = (Uint8*)dst + dst_pitch; - } - break; - case SDL_PIXELFORMAT_YV12: - case SDL_PIXELFORMAT_IYUV: - case SDL_PIXELFORMAT_NV12: - case SDL_PIXELFORMAT_NV21: - { - /* Y plane */ - for (i = height; i--;) { - SDL_memcpy(dst, src, width); - src = (const Uint8*)src + src_pitch; - dst = (Uint8*)dst + dst_pitch; - } - - /* not sure the pitch is relevant here. - this also works to add the size of two chroma planes */ -#if 0 - SDL_memcpy(dst, src, 2 * ((width + 1)/2) * ((height+1)/2)); -#else - - if (src_format == SDL_PIXELFORMAT_YV12 || src_format == SDL_PIXELFORMAT_IYUV) { - /* U and V planes are a quarter the size of the Y plane */ - width = (width + 1) / 2; - height = (height + 1) / 2; - src_pitch = (src_pitch + 1) / 2; - dst_pitch = (dst_pitch + 1) / 2; - for (i = height * 2; i--;) { - SDL_memcpy(dst, src, width); - src = (const Uint8*)src + src_pitch; - dst = (Uint8*)dst + dst_pitch; - } - } else if (src_format == SDL_PIXELFORMAT_NV12 || src_format == SDL_PIXELFORMAT_NV21) { - /* U/V plane is half the height of the Y plane */ - height = (height + 1) / 2; - width = (width + 1) / 2; - src_pitch = (src_pitch + 1) / 2; - dst_pitch = (dst_pitch + 1) / 2; - for (i = height; i--;) { - SDL_memcpy(dst, src, 2 * width); - src = (const Uint8*)src + 2 * src_pitch; - dst = (Uint8*)dst + 2 * dst_pitch; - } - } -#endif - } - break; - default: - return SDL_SetError("Unknown FOURCC pixel format"); - } - } else { - const int bpp = SDL_BYTESPERPIXEL(src_format); - width *= bpp; - for (i = height; i--;) { - SDL_memcpy(dst, src, width); - src = (const Uint8*)src + src_pitch; - dst = (Uint8*)dst + dst_pitch; - } + const int bpp = SDL_BYTESPERPIXEL(src_format); + width *= bpp; + for (i = height; i--;) { + SDL_memcpy(dst, src, width); + src = (const Uint8*)src + src_pitch; + dst = (Uint8*)dst + dst_pitch; } return 0; } - /* FOURCC to Any */ - if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { - /* FOURCC to ARGB8888 */ - if (dst_format == SDL_PIXELFORMAT_ARGB8888) { - SDL_ConvertPixels_YUV_to_ARGB8888(width, height, src_format, src, dst, dst_pitch); - return 0; - } - else /* FOURCC to not(ARGB8888) : need an intermediate conversion */ - { - int ret; - void *tmp = SDL_malloc(width * height * 4); - if (tmp == NULL) { - return -1; - } - - /* convert src/FOURCC to tmp/ARGB8888 */ - SDL_ConvertPixels_YUV_to_ARGB8888(width, height, src_format, src, tmp, width * 4); - - /* convert tmp/ARGB8888 to dst/dst_format */ - ret = SDL_ConvertPixels(width, height, SDL_PIXELFORMAT_ARGB8888, tmp, width * 4, dst_format, dst, dst_pitch); - SDL_free(tmp); - return ret; - } - } - - /* Any to FOURCC */ - if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) { - /* ARGB8888 to FOURCC */ - if (src_format == SDL_PIXELFORMAT_ARGB8888) { - SDL_ConvertPixels_ARGB8888_to_YUV(width, height, src, src_pitch, dst_format, dst); - return 0; - } - else /* not(ARGB8888) to FOURCC : need an intermediate conversion */ - { - int ret; - void *tmp = SDL_malloc(width * height * 4); - if (tmp == NULL) { - return -1; - } - /* convert src/src_format to tmp/ARGB8888 */ - ret = SDL_ConvertPixels(width, height, src_format, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, tmp, width * 4); - if (ret == -1) { - SDL_free(tmp); - return ret; - } - /* convert tmp/ARGB8888 to dst/FOURCC */ - SDL_ConvertPixels_ARGB8888_to_YUV(width, height, tmp, width * 4, dst_format, dst); - - SDL_free(tmp); - return 0; - } - } - if (!SDL_CreateSurfaceOnStack(width, height, src_format, nonconst_src, src_pitch, &src_surface, &src_fmt, &src_blitmap)) { @@ -1322,491 +1229,4 @@ SDL_FreeSurface(SDL_Surface * surface) SDL_free(surface); } - -/* YUV-RGB conversion */ -#define CLAMP(val) ((val) > 0 ? ((val) < 255 ? (val) : 255) : 0) - -#if 1 - -/* Coefficients from CCIR 601 */ -#define MAKE_Y(r, g, b) (int)( 0.29900f * (r) + 0.58700f * (g) + 0.11400f * (b)) -#define MAKE_U(r, g, b) (int)(-0.16874f * (r) - 0.33126f * (g) + 0.50000f * (b) + 128) -#define MAKE_V(r, g, b) (int)( 0.50000f * (r) - 0.41869f * (g) - 0.08131f * (b) + 128) - -#define MAKE_R(y, u, v) CLAMP((int)((y) + 1.40200f * ((v) - 128))) -#define MAKE_G(y, u, v) CLAMP((int)((y) - 0.34414f * ((u) - 128) - 0.71414f * ((v) - 128))) -#define MAKE_B(y, u, v) CLAMP((int)((y) + 1.77200f * ((u) - 128) )) - -#else - -/* Coefficients from Video Demystified */ -#define MAKE_Y(r, g, b) ((( 66 * (r) + 129 * (g) + 25 * (b) + 128) >> 8) + 16) -#define MAKE_U(r, g, b) ((( -38 * (r) - 74 * (g) + 112 * (b) + 128) >> 8) + 128) -#define MAKE_V(r, g, b) ((( 112 * (r) - 94 * (g) - 18 * (b) + 128) >> 8) + 128) - -#define MAKE_R(y, u, v) CLAMP(( 298 * ((y) - 16) + 409 * ((v) - 128) + 128) >> 8) -#define MAKE_G(y, u, v) CLAMP(( 298 * ((y) - 16) - 100 * ((u) - 128) - 208 * ((v) - 128) + 128) >> 8) -#define MAKE_B(y, u, v) CLAMP(( 298 * ((y) - 16) + 516 * ((u) - 128) + 128) >> 8) - -#endif - - -static int -SDL_ConvertPixels_YUV_to_ARGB8888(int width, int height, - Uint32 src_format, const void *src, - void *dst, int dst_pitch) -{ - const int sz_plane = width * height; - const int sz_plane_chroma = ((width + 1) / 2) * ((height + 1) / 2); - const int width_remainder = (width & 0x1); - const int width_half = width / 2; - const int curr_row_padding = dst_pitch - 4 * width; - int i, j; - Uint8 *curr_row = (Uint8*)dst; - - // SDL_Log("SDL_ConvertPixels_YUV_to_ARGB8888 (from %s)", SDL_GetPixelFormatName(src_format)); - -#define WRITE_RGB_PIXEL(y, u, v) \ - *((Uint32*)curr_row) = \ - (MAKE_B((y), (u), (v)) \ - | (MAKE_G((y), (u), (v)) << 8) \ - | (MAKE_R((y), (u), (v)) << 16) \ - | 0xff000000); \ - curr_row += 4; \ - - switch (src_format) - { - case SDL_PIXELFORMAT_YV12: - case SDL_PIXELFORMAT_IYUV: - case SDL_PIXELFORMAT_NV12: - case SDL_PIXELFORMAT_NV21: - { - const Uint8 *plane_y = (const Uint8*)src; - - if (src_format == SDL_PIXELFORMAT_YV12 || src_format == SDL_PIXELFORMAT_IYUV) - { - const Uint8 *plane_u = (src_format == SDL_PIXELFORMAT_YV12 ? plane_y + sz_plane + sz_plane_chroma : plane_y + sz_plane); - const Uint8 *plane_v = (src_format == SDL_PIXELFORMAT_YV12 ? plane_y + sz_plane : plane_y + sz_plane + sz_plane_chroma); - - for (j = 0; j < height; j++) { - for (i = 0; i < width_half; i++) { - const Uint8 u = *plane_u++; - const Uint8 v = *plane_v++; - const Uint8 y = *plane_y++; - const Uint8 y1 = *plane_y++; - WRITE_RGB_PIXEL(y, u, v); - WRITE_RGB_PIXEL(y1, u, v); - } - if (width_remainder) { - const Uint8 u = *plane_u++; - const Uint8 v = *plane_v++; - const Uint8 y = *plane_y++; - WRITE_RGB_PIXEL(y, u, v); - } - /* Re-use the same line of chroma planes */ - if ((j & 0x1) == 0x0) { - plane_u -= width_half + width_remainder; - plane_v -= width_half + width_remainder; - } - curr_row += curr_row_padding; - } - } - else if (src_format == SDL_PIXELFORMAT_NV12) - { - const Uint8 *plane_interleaved_uv = plane_y + sz_plane; - for (j = 0; j < height; j++) { - for (i = 0; i < width_half; i++) { - const Uint8 y = *plane_y++; - const Uint8 y1 = *plane_y++; - const Uint8 u = *plane_interleaved_uv++; - const Uint8 v = *plane_interleaved_uv++; - WRITE_RGB_PIXEL(y, u, v); - WRITE_RGB_PIXEL(y1, u, v); - } - if (width_remainder) { - const Uint8 y = *plane_y++; - const Uint8 u = *plane_interleaved_uv++; - const Uint8 v = *plane_interleaved_uv++; - WRITE_RGB_PIXEL(y, u, v); - } - /* Re-use the same line of chroma planes */ - if ((j & 0x1) == 0x0) { - plane_interleaved_uv -= 2 * (width_half + width_remainder); - } - curr_row += curr_row_padding; - } - } - else /* src_format == SDL_PIXELFORMAT_NV21 */ - { - const Uint8 *plane_interleaved_uv = plane_y + sz_plane; - for (j = 0; j < height; j++) { - for (i = 0; i < width_half; i++) { - const Uint8 y = *plane_y++; - const Uint8 y1 = *plane_y++; - const Uint8 v = *plane_interleaved_uv++; - const Uint8 u = *plane_interleaved_uv++; - WRITE_RGB_PIXEL(y, u, v); - WRITE_RGB_PIXEL(y1, u, v); - } - if (width_remainder) { - const Uint8 y = *plane_y++; - const Uint8 v = *plane_interleaved_uv++; - const Uint8 u = *plane_interleaved_uv++; - WRITE_RGB_PIXEL(y, u, v); - } - /* Re-use the same line of chroma planes */ - if ((j & 0x1) == 0x0) { - plane_interleaved_uv -= 2 * (width_half + width_remainder); - } - curr_row += curr_row_padding; - } - } - } - break; - - case SDL_PIXELFORMAT_YUY2: - case SDL_PIXELFORMAT_UYVY: - case SDL_PIXELFORMAT_YVYU: - { - const Uint8 *plane = (const Uint8 *)src; - -#define READ_PACKED_YUV(var1, var2, var3, var4) \ - const Uint8 var1 = plane[0]; \ - const Uint8 var2 = plane[1]; \ - const Uint8 var3 = plane[2]; \ - const Uint8 var4 = plane[3]; \ - plane += 4; \ - - if (src_format == SDL_PIXELFORMAT_YUY2) /* Y U Y1 V */ - { - for (j = 0; j < height; j++) { - for (i = 0; i < width_half; i++) { - READ_PACKED_YUV(y, u, y1, v); - WRITE_RGB_PIXEL(y, u, v); - WRITE_RGB_PIXEL(y1, u, v); - } - if (width_remainder) { - READ_PACKED_YUV(y, u, y1, v); - (void)y1; /* y1 unused */ - WRITE_RGB_PIXEL(y, u, v); - } - curr_row += curr_row_padding; - } - } - else if (src_format == SDL_PIXELFORMAT_UYVY) /* U Y V Y1 */ - { - for (j = 0; j < height; j++) { - for (i = 0; i < width_half; i++) { - READ_PACKED_YUV(u, y, v, y1); - WRITE_RGB_PIXEL(y, u, v); - WRITE_RGB_PIXEL(y1, u, v); - } - if (width_remainder) { - READ_PACKED_YUV(u, y, v, y1); - (void) y1; /* y1 unused */ - WRITE_RGB_PIXEL(y, u, v); - } - curr_row += curr_row_padding; - } - } - else if (src_format == SDL_PIXELFORMAT_YVYU) /* Y V Y1 U */ - { - for (j = 0; j < height; j++) { - for (i = 0; i < width_half; i++) { - READ_PACKED_YUV(y, v, y1, u); - WRITE_RGB_PIXEL(y, u, v); - WRITE_RGB_PIXEL(y1, u, v); - } - if (width_remainder) { - READ_PACKED_YUV(y, v, y1, u); - (void) y1; /* y1 unused */ - WRITE_RGB_PIXEL(y, u, v); - } - curr_row += curr_row_padding; - } - } -#undef READ_PACKED_YUV - } - break; - } -#undef WRITE_RGB_PIXEL - return 0; -} - -static int -SDL_ConvertPixels_ARGB8888_to_YUV(int width, int height, const void *src, int src_pitch, Uint32 dst_format, void *dst) -{ - const int src_pitch_x_2 = src_pitch * 2; - const int sz_plane = width * height; - const int sz_plane_chroma = ((width + 1) / 2) * ((height + 1) / 2); - const int height_half = height / 2; - const int height_remainder = (height & 0x1); - const int width_half = width / 2; - const int width_remainder = (width & 0x1); - int i, j; - - // SDL_Log("SDL_ConvertPixels_ARGB8888_to_YUV (to %s)", SDL_GetPixelFormatName(dst_format)); - - switch (dst_format) - { - case SDL_PIXELFORMAT_YV12: - case SDL_PIXELFORMAT_IYUV: - case SDL_PIXELFORMAT_NV12: - case SDL_PIXELFORMAT_NV21: - { - const Uint8 *curr_row, *next_row; - - Uint8 *plane_y = (Uint8*) dst; - Uint8 *plane_u = (dst_format == SDL_PIXELFORMAT_YV12 ? plane_y + sz_plane + sz_plane_chroma : plane_y + sz_plane); - Uint8 *plane_v = (dst_format == SDL_PIXELFORMAT_YV12 ? plane_y + sz_plane : plane_y + sz_plane + sz_plane_chroma); - Uint8 *plane_interleaved_uv = plane_y + sz_plane; - - curr_row = (const Uint8*)src; - - /* Write Y plane */ - for (j = 0; j < height; j++) { - for (i = 0; i < width; i++) { - const Uint8 b = curr_row[4 * i + 0]; - const Uint8 g = curr_row[4 * i + 1]; - const Uint8 r = curr_row[4 * i + 2]; - *plane_y++ = MAKE_Y(r, g, b); - } - curr_row += src_pitch; - } - - curr_row = (const Uint8*)src; - next_row = (const Uint8*)src; - next_row += src_pitch; - -#if 1 -/* slightly faster */ -#define READ_2x2_PIXELS \ - const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \ - const Uint32 p2 = ((const Uint32 *)curr_row)[2 * i + 1]; \ - const Uint32 p3 = ((const Uint32 *)next_row)[2 * i]; \ - const Uint32 p4 = ((const Uint32 *)next_row)[2 * i + 1]; \ - const Uint32 b = ((p1 & 0x000000ff) + (p2 & 0x000000ff) + (p3 & 0x000000ff) + (p4 & 0x000000ff)) >> 2; \ - const Uint32 g = ((p1 & 0x0000ff00) + (p2 & 0x0000ff00) + (p3 & 0x0000ff00) + (p4 & 0x0000ff00)) >> 10; \ - const Uint32 r = ((p1 & 0x00ff0000) + (p2 & 0x00ff0000) + (p3 & 0x00ff0000) + (p4 & 0x00ff0000)) >> 18; \ - -#else - -#define READ_2x2_PIXELS \ - const Uint8 b = (curr_row[8 * i + 0] + curr_row[8 * i + 4] \ - + next_row[8 * i + 0] + next_row[8 * i + 4] ) >> 2; \ - const Uint8 g = (curr_row[8 * i + 1] + curr_row[8 * i + 5] \ - + next_row[8 * i + 1] + next_row[8 * i + 5] ) >> 2; \ - const Uint8 r = (curr_row[8 * i + 2] + curr_row[8 * i + 6] \ - + next_row[8 * i + 2] + next_row[8 * i + 6] ) >> 2; \ - -#endif - -#define READ_2x1_PIXELS \ - const Uint8 b = (curr_row[8 * i + 0] + next_row[8 * i + 0]) >> 1; \ - const Uint8 g = (curr_row[8 * i + 1] + next_row[8 * i + 1]) >> 1; \ - const Uint8 r = (curr_row[8 * i + 2] + next_row[8 * i + 2]) >> 1; \ - -#define READ_1x2_PIXELS \ - const Uint8 b = (curr_row[8 * i + 0] + curr_row[8 * i + 4]) >> 1; \ - const Uint8 g = (curr_row[8 * i + 1] + curr_row[8 * i + 5]) >> 1; \ - const Uint8 r = (curr_row[8 * i + 2] + curr_row[8 * i + 6]) >> 1; \ - -#define READ_1x1_PIXEL \ - const Uint8 b = curr_row[8 * i + 0]; \ - const Uint8 g = curr_row[8 * i + 1]; \ - const Uint8 r = curr_row[8 * i + 2]; \ - - if (dst_format == SDL_PIXELFORMAT_YV12 || dst_format == SDL_PIXELFORMAT_IYUV) - { - /* Write UV planes, not interleaved */ - for (j = 0; j < height_half; j++) { - for (i = 0; i < width_half; i++) { - READ_2x2_PIXELS; - *plane_u++ = MAKE_U(r, g, b); - *plane_v++ = MAKE_V(r, g, b); - } - if (width_remainder) { - READ_2x1_PIXELS; - *plane_u++ = MAKE_U(r, g, b); - *plane_v++ = MAKE_V(r, g, b); - } - curr_row += src_pitch_x_2; - next_row += src_pitch_x_2; - } - if (height_remainder) { - for (i = 0; i < width_half; i++) { - READ_1x2_PIXELS; - *plane_u++ = MAKE_U(r, g, b); - *plane_v++ = MAKE_V(r, g, b); - } - if (width_remainder) { - READ_1x1_PIXEL; - *plane_u++ = MAKE_U(r, g, b); - *plane_v++ = MAKE_V(r, g, b); - } - } - } - else if (dst_format == SDL_PIXELFORMAT_NV12) - { - for (j = 0; j < height_half; j++) { - for (i = 0; i < width_half; i++) { - READ_2x2_PIXELS; - *plane_interleaved_uv++ = MAKE_U(r, g, b); - *plane_interleaved_uv++ = MAKE_V(r, g, b); - } - if (width_remainder) { - READ_2x1_PIXELS; - *plane_interleaved_uv++ = MAKE_U(r, g, b); - *plane_interleaved_uv++ = MAKE_V(r, g, b); - } - curr_row += src_pitch_x_2; - next_row += src_pitch_x_2; - } - if (height_remainder) { - for (i = 0; i < width_half; i++) { - READ_1x2_PIXELS; - *plane_interleaved_uv++ = MAKE_U(r, g, b); - *plane_interleaved_uv++ = MAKE_V(r, g, b); - } - if (width_remainder) { - READ_1x1_PIXEL; - *plane_interleaved_uv++ = MAKE_U(r, g, b); - *plane_interleaved_uv++ = MAKE_V(r, g, b); - } - } - } - else /* dst_format == SDL_PIXELFORMAT_NV21 */ - { - for (j = 0; j < height_half; j++) { - for (i = 0; i < width_half; i++) { - READ_2x2_PIXELS; - *plane_interleaved_uv++ = MAKE_V(r, g, b); - *plane_interleaved_uv++ = MAKE_U(r, g, b); - } - if (width_remainder) { - READ_2x1_PIXELS; - *plane_interleaved_uv++ = MAKE_V(r, g, b); - *plane_interleaved_uv++ = MAKE_U(r, g, b); - } - curr_row += src_pitch_x_2; - next_row += src_pitch_x_2; - } - if (height_remainder) { - for (i = 0; i < width_half; i++) { - READ_1x2_PIXELS; - *plane_interleaved_uv++ = MAKE_V(r, g, b); - *plane_interleaved_uv++ = MAKE_U(r, g, b); - } - if (width_remainder) { - READ_1x1_PIXEL; - *plane_interleaved_uv++ = MAKE_V(r, g, b); - *plane_interleaved_uv++ = MAKE_U(r, g, b); - } - } - } -#undef READ_2x2_PIXELS -#undef READ_2x1_PIXELS -#undef READ_1x2_PIXELS -#undef READ_1x1_PIXEL - } - break; - - case SDL_PIXELFORMAT_YUY2: - case SDL_PIXELFORMAT_UYVY: - case SDL_PIXELFORMAT_YVYU: - { - const Uint8 *curr_row = (const Uint8*) src; - Uint8 *plane = (Uint8*) dst; - -#define READ_TWO_RGB_PIXELS \ - const Uint8 b = curr_row[8 * i + 0]; \ - const Uint8 g = curr_row[8 * i + 1]; \ - const Uint8 r = curr_row[8 * i + 2]; \ - const Uint8 b1 = curr_row[8 * i + 4]; \ - const Uint8 g1 = curr_row[8 * i + 5]; \ - const Uint8 r1 = curr_row[8 * i + 6]; \ - const Uint8 B = (b + b1) >> 1; \ - const Uint8 G = (g + g1) >> 1; \ - const Uint8 R = (r + r1) >> 1; \ - -#define READ_ONE_RGB_PIXEL \ - const Uint8 b = curr_row[8 * i + 0]; \ - const Uint8 g = curr_row[8 * i + 1]; \ - const Uint8 r = curr_row[8 * i + 2]; \ - - /* Write YUV plane, packed */ - if (dst_format == SDL_PIXELFORMAT_YUY2) - { - for (j = 0; j < height; j++) { - for (i = 0; i < width_half; i++) { - READ_TWO_RGB_PIXELS; - /* Y U Y1 V */ - *plane++ = MAKE_Y(r, g, b); - *plane++ = MAKE_U(R, G, B); - *plane++ = MAKE_Y(r1, g1, b1); - *plane++ = MAKE_V(R, G, B); - } - if (width_remainder) { - READ_ONE_RGB_PIXEL; - /* Y U Y V */ - *plane++ = MAKE_Y(r, g, b); - *plane++ = MAKE_U(r, g, b); - *plane++ = MAKE_Y(r, g, b); - *plane++ = MAKE_V(r, g, b); - } - curr_row += src_pitch; - } - } - else if (dst_format == SDL_PIXELFORMAT_UYVY) - { - for (j = 0; j < height; j++) { - for (i = 0; i < width_half; i++) { - READ_TWO_RGB_PIXELS; - /* U Y V Y1 */ - *plane++ = MAKE_U(R, G, B); - *plane++ = MAKE_Y(r, g, b); - *plane++ = MAKE_V(R, G, B); - *plane++ = MAKE_Y(r1, g1, b1); - } - if (width_remainder) { - READ_ONE_RGB_PIXEL; - /* U Y V Y */ - *plane++ = MAKE_U(r, g, b); - *plane++ = MAKE_Y(r, g, b); - *plane++ = MAKE_V(r, g, b); - *plane++ = MAKE_Y(r, g, b); - } - curr_row += src_pitch; - } - } - else if (dst_format == SDL_PIXELFORMAT_YVYU) - { - for (j = 0; j < height; j++) { - for (i = 0; i < width_half; i++) { - READ_TWO_RGB_PIXELS; - /* Y V Y1 U */ - *plane++ = MAKE_Y(r, g, b); - *plane++ = MAKE_V(R, G, B); - *plane++ = MAKE_Y(r1, g1, b1); - *plane++ = MAKE_U(R, G, B); - } - if (width_remainder) { - READ_ONE_RGB_PIXEL; - /* Y V Y U */ - *plane++ = MAKE_Y(r, g, b); - *plane++ = MAKE_V(r, g, b); - *plane++ = MAKE_Y(r, g, b); - *plane++ = MAKE_U(r, g, b); - } - curr_row += src_pitch; - } - } -#undef READ_TWO_RGB_PIXELS -#undef READ_ONE_RGB_PIXEL - } - break; - } - return 0; -} - /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/SDL_yuv.c b/src/video/SDL_yuv.c new file mode 100644 index 0000000000000..966de7f566a1d --- /dev/null +++ b/src/video/SDL_yuv.c @@ -0,0 +1,1834 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2017 Sam Lantinga + + 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, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../SDL_internal.h" + +#include "SDL_endian.h" +#include "SDL_video.h" +#include "SDL_pixels_c.h" + +#include "yuv2rgb/yuv_rgb.h" + +#define SDL_YUV_SD_THRESHOLD 576 + + +static SDL_YUV_CONVERSION_MODE SDL_YUV_ConversionMode = SDL_YUV_CONVERSION_BT601; + + +void SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_MODE mode) +{ + SDL_YUV_ConversionMode = mode; +} + +SDL_YUV_CONVERSION_MODE SDL_GetYUVConversionMode() +{ + return SDL_YUV_ConversionMode; +} + +SDL_YUV_CONVERSION_MODE SDL_GetYUVConversionModeForResolution(int width, int height) +{ + SDL_YUV_CONVERSION_MODE mode = SDL_GetYUVConversionMode(); + if (mode == SDL_YUV_CONVERSION_AUTOMATIC) { + if (height <= SDL_YUV_SD_THRESHOLD) { + mode = SDL_YUV_CONVERSION_BT601; + } else { + mode = SDL_YUV_CONVERSION_BT709; + } + } + return mode; +} + +static int GetYUVConversionType(int width, int height, YCbCrType *yuv_type) +{ + switch (SDL_GetYUVConversionModeForResolution(width, height)) { + case SDL_YUV_CONVERSION_JPEG: + *yuv_type = YCBCR_JPEG; + break; + case SDL_YUV_CONVERSION_BT601: + *yuv_type = YCBCR_601; + break; + case SDL_YUV_CONVERSION_BT709: + *yuv_type = YCBCR_709; + break; + default: + return SDL_SetError("Unexpected YUV conversion mode"); + } + return 0; +} + +static SDL_bool IsPlanar2x2Format(Uint32 format) +{ + return (format == SDL_PIXELFORMAT_YV12 || + format == SDL_PIXELFORMAT_IYUV || + format == SDL_PIXELFORMAT_NV12 || + format == SDL_PIXELFORMAT_NV21); +} + +static SDL_bool IsPacked4Format(Uint32 format) +{ + return (format == SDL_PIXELFORMAT_YUY2 || + format == SDL_PIXELFORMAT_UYVY || + format == SDL_PIXELFORMAT_YVYU); +} + +static int GetYUVPlanes(int width, int height, Uint32 format, const void *yuv, int yuv_pitch, + const Uint8 **y, const Uint8 **u, const Uint8 **v, Uint32 *y_stride, Uint32 *uv_stride) +{ + const Uint8 *planes[3]; + int pitches[3]; + + switch (format) { + case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_IYUV: + pitches[0] = yuv_pitch; + pitches[1] = (pitches[0] + 1) / 2; + pitches[2] = (pitches[0] + 1) / 2; + planes[0] = (const Uint8 *)yuv; + planes[1] = planes[0] + pitches[0] * height; + planes[2] = planes[1] + pitches[1] * ((height + 1) / 2); + break; + case SDL_PIXELFORMAT_YUY2: + case SDL_PIXELFORMAT_UYVY: + case SDL_PIXELFORMAT_YVYU: + pitches[0] = yuv_pitch; + planes[0] = (const Uint8 *)yuv; + break; + case SDL_PIXELFORMAT_NV12: + case SDL_PIXELFORMAT_NV21: + pitches[0] = yuv_pitch; + pitches[1] = 2 * ((pitches[0] + 1) / 2); + planes[0] = (const Uint8 *)yuv; + planes[1] = planes[0] + pitches[0] * height; + break; + default: + return SDL_SetError("GetYUVPlanes(): Unsupported YUV format: %s", SDL_GetPixelFormatName(format)); + } + + switch (format) { + case SDL_PIXELFORMAT_YV12: + *y = planes[0]; + *y_stride = pitches[0]; + *v = planes[1]; + *u = planes[2]; + *uv_stride = pitches[1]; + break; + case SDL_PIXELFORMAT_IYUV: + *y = planes[0]; + *y_stride = pitches[0]; + *v = planes[2]; + *u = planes[1]; + *uv_stride = pitches[1]; + break; + case SDL_PIXELFORMAT_YUY2: + *y = planes[0]; + *y_stride = pitches[0]; + *v = *y + 3; + *u = *y + 1; + *uv_stride = pitches[0]; + break; + case SDL_PIXELFORMAT_UYVY: + *y = planes[0] + 1; + *y_stride = pitches[0]; + *v = *y + 1; + *u = *y - 1; + *uv_stride = pitches[0]; + break; + case SDL_PIXELFORMAT_YVYU: + *y = planes[0]; + *y_stride = pitches[0]; + *v = *y + 1; + *u = *y + 3; + *uv_stride = pitches[0]; + break; + case SDL_PIXELFORMAT_NV12: + *y = planes[0]; + *y_stride = pitches[0]; + *u = planes[1]; + *v = *u + 1; + *uv_stride = pitches[1]; + break; + case SDL_PIXELFORMAT_NV21: + *y = planes[0]; + *y_stride = pitches[0]; + *v = planes[1]; + *u = *v + 1; + *uv_stride = pitches[1]; + break; + default: + /* Should have caught this above */ + return SDL_SetError("GetYUVPlanes[2]: Unsupported YUV format: %s", SDL_GetPixelFormatName(format)); + } + return 0; +} + +static SDL_bool yuv_rgb_sse( + Uint32 src_format, Uint32 dst_format, + Uint32 width, Uint32 height, + const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride, + Uint8 *rgb, Uint32 rgb_stride, + YCbCrType yuv_type) +{ +#ifdef __SSE2__ + if (!SDL_HasSSE2()) { + return SDL_FALSE; + } + + if (src_format == SDL_PIXELFORMAT_YV12 || + src_format == SDL_PIXELFORMAT_IYUV) { + + switch (dst_format) { + case SDL_PIXELFORMAT_RGB565: + yuv420_rgb565_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB24: + yuv420_rgb24_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGBX8888: + case SDL_PIXELFORMAT_RGBA8888: + yuv420_rgba_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGRX8888: + case SDL_PIXELFORMAT_BGRA8888: + yuv420_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB888: + case SDL_PIXELFORMAT_ARGB8888: + yuv420_argb_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGR888: + case SDL_PIXELFORMAT_ABGR8888: + yuv420_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + default: + break; + } + } + + if (src_format == SDL_PIXELFORMAT_YUY2 || + src_format == SDL_PIXELFORMAT_UYVY || + src_format == SDL_PIXELFORMAT_YVYU) { + + switch (dst_format) { + case SDL_PIXELFORMAT_RGB565: + yuv422_rgb565_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB24: + yuv422_rgb24_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGBX8888: + case SDL_PIXELFORMAT_RGBA8888: + yuv422_rgba_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGRX8888: + case SDL_PIXELFORMAT_BGRA8888: + yuv422_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB888: + case SDL_PIXELFORMAT_ARGB8888: + yuv422_argb_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGR888: + case SDL_PIXELFORMAT_ABGR8888: + yuv422_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + default: + break; + } + } + + if (src_format == SDL_PIXELFORMAT_NV12 || + src_format == SDL_PIXELFORMAT_NV21) { + + switch (dst_format) { + case SDL_PIXELFORMAT_RGB565: + yuvnv12_rgb565_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB24: + yuvnv12_rgb24_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGBX8888: + case SDL_PIXELFORMAT_RGBA8888: + yuvnv12_rgba_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGRX8888: + case SDL_PIXELFORMAT_BGRA8888: + yuvnv12_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB888: + case SDL_PIXELFORMAT_ARGB8888: + yuvnv12_argb_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGR888: + case SDL_PIXELFORMAT_ABGR8888: + yuvnv12_bgra_sseu(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + default: + break; + } + } +#endif + return SDL_FALSE; +} + +static SDL_bool yuv_rgb_std( + Uint32 src_format, Uint32 dst_format, + Uint32 width, Uint32 height, + const Uint8 *y, const Uint8 *u, const Uint8 *v, Uint32 y_stride, Uint32 uv_stride, + Uint8 *rgb, Uint32 rgb_stride, + YCbCrType yuv_type) +{ + if (src_format == SDL_PIXELFORMAT_YV12 || + src_format == SDL_PIXELFORMAT_IYUV) { + + switch (dst_format) { + case SDL_PIXELFORMAT_RGB565: + yuv420_rgb565_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB24: + yuv420_rgb24_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGBX8888: + case SDL_PIXELFORMAT_RGBA8888: + yuv420_rgba_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGRX8888: + case SDL_PIXELFORMAT_BGRA8888: + yuv420_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB888: + case SDL_PIXELFORMAT_ARGB8888: + yuv420_argb_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGR888: + case SDL_PIXELFORMAT_ABGR8888: + yuv420_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + default: + break; + } + } + + if (src_format == SDL_PIXELFORMAT_YUY2 || + src_format == SDL_PIXELFORMAT_UYVY || + src_format == SDL_PIXELFORMAT_YVYU) { + + switch (dst_format) { + case SDL_PIXELFORMAT_RGB565: + yuv422_rgb565_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB24: + yuv422_rgb24_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGBX8888: + case SDL_PIXELFORMAT_RGBA8888: + yuv422_rgba_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGRX8888: + case SDL_PIXELFORMAT_BGRA8888: + yuv422_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB888: + case SDL_PIXELFORMAT_ARGB8888: + yuv422_argb_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGR888: + case SDL_PIXELFORMAT_ABGR8888: + yuv422_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + default: + break; + } + } + + if (src_format == SDL_PIXELFORMAT_NV12 || + src_format == SDL_PIXELFORMAT_NV21) { + + switch (dst_format) { + case SDL_PIXELFORMAT_RGB565: + yuvnv12_rgb565_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB24: + yuvnv12_rgb24_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGBX8888: + case SDL_PIXELFORMAT_RGBA8888: + yuvnv12_rgba_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGRX8888: + case SDL_PIXELFORMAT_BGRA8888: + yuvnv12_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_RGB888: + case SDL_PIXELFORMAT_ARGB8888: + yuvnv12_argb_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + case SDL_PIXELFORMAT_BGR888: + case SDL_PIXELFORMAT_ABGR8888: + yuvnv12_bgra_std(width, height, y, u, v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + default: + break; + } + } + return SDL_FALSE; +} + +int +SDL_ConvertPixels_YUV_to_RGB(int width, int height, + Uint32 src_format, const void *src, int src_pitch, + Uint32 dst_format, void *dst, int dst_pitch) +{ + const Uint8 *y; + const Uint8 *u; + const Uint8 *v; + Uint32 y_stride; + Uint32 uv_stride; + YCbCrType yuv_type; + + if (GetYUVPlanes(width, height, src_format, src, src_pitch, &y, &u, &v, &y_stride, &uv_stride) < 0) { + return -1; + } + + if (GetYUVConversionType(width, height, &yuv_type) < 0) { + return -1; + } + + if (yuv_rgb_sse(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8*)dst, dst_pitch, yuv_type)) { + return 0; + } + + if (yuv_rgb_std(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8*)dst, dst_pitch, yuv_type)) { + return 0; + } + + /* No fast path for the RGB format, instead convert using an intermediate buffer */ + if (dst_format != SDL_PIXELFORMAT_ARGB8888) { + int ret; + void *tmp; + int tmp_pitch = (width * sizeof(Uint32)); + + tmp = SDL_malloc(tmp_pitch * height); + if (tmp == NULL) { + return SDL_OutOfMemory(); + } + + /* convert src/src_format to tmp/ARGB8888 */ + ret = SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, tmp, tmp_pitch); + if (ret < 0) { + SDL_free(tmp); + return ret; + } + + /* convert tmp/ARGB8888 to dst/RGB */ + ret = SDL_ConvertPixels(width, height, SDL_PIXELFORMAT_ARGB8888, tmp, tmp_pitch, dst_format, dst, dst_pitch); + SDL_free(tmp); + return ret; + } + + return SDL_SetError("Unsupported YUV conversion"); +} + +struct RGB2YUVFactors +{ + int y_offset; + float y[3]; /* Rfactor, Gfactor, Bfactor */ + float u[3]; /* Rfactor, Gfactor, Bfactor */ + float v[3]; /* Rfactor, Gfactor, Bfactor */ +}; + +static int +SDL_ConvertPixels_ARGB8888_to_YUV(int width, int height, const void *src, int src_pitch, Uint32 dst_format, void *dst, int dst_pitch) +{ + const int src_pitch_x_2 = src_pitch * 2; + const int height_half = height / 2; + const int height_remainder = (height & 0x1); + const int width_half = width / 2; + const int width_remainder = (width & 0x1); + int i, j; + + static struct RGB2YUVFactors RGB2YUVFactorTables[SDL_YUV_CONVERSION_BT709 + 1] = + { + /* ITU-T T.871 (JPEG) */ + { + 0, + { 0.2990f, 0.5870f, 0.1140f }, + { -0.1687f, -0.3313f, 0.5000f }, + { 0.5000f, -0.4187f, -0.0813f }, + }, + /* ITU-R BT.601-7 */ + { + 16, + { 0.2568f, 0.5041f, 0.0979f }, + { -0.1482f, -0.2910f, 0.4392f }, + { 0.4392f, -0.3678f, -0.0714f }, + }, + /* ITU-R BT.709-6 */ + { + 16, + { 0.1826f, 0.6142f, 0.0620f }, + {-0.1006f, -0.3386f, 0.4392f }, + { 0.4392f, -0.3989f, -0.0403f }, + }, + }; + const struct RGB2YUVFactors *cvt = &RGB2YUVFactorTables[SDL_GetYUVConversionModeForResolution(width, height)]; + +#define MAKE_Y(r, g, b) (Uint8)((int)(cvt->y[0] * (r) + cvt->y[1] * (g) + cvt->y[2] * (b) + 0.5f) + cvt->y_offset) +#define MAKE_U(r, g, b) (Uint8)((int)(cvt->u[0] * (r) + cvt->u[1] * (g) + cvt->u[2] * (b) + 0.5f) + 128) +#define MAKE_V(r, g, b) (Uint8)((int)(cvt->v[0] * (r) + cvt->v[1] * (g) + cvt->v[2] * (b) + 0.5f) + 128) + +#define READ_2x2_PIXELS \ + const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \ + const Uint32 p2 = ((const Uint32 *)curr_row)[2 * i + 1]; \ + const Uint32 p3 = ((const Uint32 *)next_row)[2 * i]; \ + const Uint32 p4 = ((const Uint32 *)next_row)[2 * i + 1]; \ + const Uint32 r = ((p1 & 0x00ff0000) + (p2 & 0x00ff0000) + (p3 & 0x00ff0000) + (p4 & 0x00ff0000)) >> 18; \ + const Uint32 g = ((p1 & 0x0000ff00) + (p2 & 0x0000ff00) + (p3 & 0x0000ff00) + (p4 & 0x0000ff00)) >> 10; \ + const Uint32 b = ((p1 & 0x000000ff) + (p2 & 0x000000ff) + (p3 & 0x000000ff) + (p4 & 0x000000ff)) >> 2; \ + +#define READ_2x1_PIXELS \ + const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \ + const Uint32 p2 = ((const Uint32 *)next_row)[2 * i]; \ + const Uint32 r = ((p1 & 0x00ff0000) + (p2 & 0x00ff0000)) >> 17; \ + const Uint32 g = ((p1 & 0x0000ff00) + (p2 & 0x0000ff00)) >> 9; \ + const Uint32 b = ((p1 & 0x000000ff) + (p2 & 0x000000ff)) >> 1; \ + +#define READ_1x2_PIXELS \ + const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \ + const Uint32 p2 = ((const Uint32 *)curr_row)[2 * i + 1]; \ + const Uint32 r = ((p1 & 0x00ff0000) + (p2 & 0x00ff0000)) >> 17; \ + const Uint32 g = ((p1 & 0x0000ff00) + (p2 & 0x0000ff00)) >> 9; \ + const Uint32 b = ((p1 & 0x000000ff) + (p2 & 0x000000ff)) >> 1; \ + +#define READ_1x1_PIXEL \ + const Uint32 p = ((const Uint32 *)curr_row)[2 * i]; \ + const Uint32 r = (p & 0x00ff0000) >> 16; \ + const Uint32 g = (p & 0x0000ff00) >> 8; \ + const Uint32 b = (p & 0x000000ff); \ + +#define READ_TWO_RGB_PIXELS \ + const Uint32 p = ((const Uint32 *)curr_row)[2 * i]; \ + const Uint32 r = (p & 0x00ff0000) >> 16; \ + const Uint32 g = (p & 0x0000ff00) >> 8; \ + const Uint32 b = (p & 0x000000ff); \ + const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i + 1]; \ + const Uint32 r1 = (p1 & 0x00ff0000) >> 16; \ + const Uint32 g1 = (p1 & 0x0000ff00) >> 8; \ + const Uint32 b1 = (p1 & 0x000000ff); \ + const Uint32 R = (r + r1)/2; \ + const Uint32 G = (g + g1)/2; \ + const Uint32 B = (b + b1)/2; \ + +#define READ_ONE_RGB_PIXEL READ_1x1_PIXEL + + switch (dst_format) + { + case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_NV12: + case SDL_PIXELFORMAT_NV21: + { + const Uint8 *curr_row, *next_row; + + Uint8 *plane_y; + Uint8 *plane_u; + Uint8 *plane_v; + Uint8 *plane_interleaved_uv; + Uint32 y_stride, uv_stride, y_skip, uv_skip; + + GetYUVPlanes(width, height, dst_format, dst, dst_pitch, + (const Uint8 **)&plane_y, (const Uint8 **)&plane_u, (const Uint8 **)&plane_v, + &y_stride, &uv_stride); + plane_interleaved_uv = (plane_y + height * y_stride); + y_skip = (y_stride - width); + + curr_row = (const Uint8*)src; + + /* Write Y plane */ + for (j = 0; j < height; j++) { + for (i = 0; i < width; i++) { + const Uint32 p1 = ((const Uint32 *)curr_row)[i]; + const Uint32 r = (p1 & 0x00ff0000) >> 16; + const Uint32 g = (p1 & 0x0000ff00) >> 8; + const Uint32 b = (p1 & 0x000000ff); + *plane_y++ = MAKE_Y(r, g, b); + } + plane_y += y_skip; + curr_row += src_pitch; + } + + curr_row = (const Uint8*)src; + next_row = (const Uint8*)src; + next_row += src_pitch; + + if (dst_format == SDL_PIXELFORMAT_YV12 || dst_format == SDL_PIXELFORMAT_IYUV) + { + /* Write UV planes, not interleaved */ + uv_skip = (uv_stride - (width + 1)/2); + for (j = 0; j < height_half; j++) { + for (i = 0; i < width_half; i++) { + READ_2x2_PIXELS; + *plane_u++ = MAKE_U(r, g, b); + *plane_v++ = MAKE_V(r, g, b); + } + if (width_remainder) { + READ_2x1_PIXELS; + *plane_u++ = MAKE_U(r, g, b); + *plane_v++ = MAKE_V(r, g, b); + } + plane_u += uv_skip; + plane_v += uv_skip; + curr_row += src_pitch_x_2; + next_row += src_pitch_x_2; + } + if (height_remainder) { + for (i = 0; i < width_half; i++) { + READ_1x2_PIXELS; + *plane_u++ = MAKE_U(r, g, b); + *plane_v++ = MAKE_V(r, g, b); + } + if (width_remainder) { + READ_1x1_PIXEL; + *plane_u++ = MAKE_U(r, g, b); + *plane_v++ = MAKE_V(r, g, b); + } + plane_u += uv_skip; + plane_v += uv_skip; + } + } + else if (dst_format == SDL_PIXELFORMAT_NV12) + { + uv_skip = (uv_stride - ((width + 1)/2)*2); + for (j = 0; j < height_half; j++) { + for (i = 0; i < width_half; i++) { + READ_2x2_PIXELS; + *plane_interleaved_uv++ = MAKE_U(r, g, b); + *plane_interleaved_uv++ = MAKE_V(r, g, b); + } + if (width_remainder) { + READ_2x1_PIXELS; + *plane_interleaved_uv++ = MAKE_U(r, g, b); + *plane_interleaved_uv++ = MAKE_V(r, g, b); + } + plane_interleaved_uv += uv_skip; + curr_row += src_pitch_x_2; + next_row += src_pitch_x_2; + } + if (height_remainder) { + for (i = 0; i < width_half; i++) { + READ_1x2_PIXELS; + *plane_interleaved_uv++ = MAKE_U(r, g, b); + *plane_interleaved_uv++ = MAKE_V(r, g, b); + } + if (width_remainder) { + READ_1x1_PIXEL; + *plane_interleaved_uv++ = MAKE_U(r, g, b); + *plane_interleaved_uv++ = MAKE_V(r, g, b); + } + } + } + else /* dst_format == SDL_PIXELFORMAT_NV21 */ + { + uv_skip = (uv_stride - ((width + 1)/2)*2); + for (j = 0; j < height_half; j++) { + for (i = 0; i < width_half; i++) { + READ_2x2_PIXELS; + *plane_interleaved_uv++ = MAKE_V(r, g, b); + *plane_interleaved_uv++ = MAKE_U(r, g, b); + } + if (width_remainder) { + READ_2x1_PIXELS; + *plane_interleaved_uv++ = MAKE_V(r, g, b); + *plane_interleaved_uv++ = MAKE_U(r, g, b); + } + plane_interleaved_uv += uv_skip; + curr_row += src_pitch_x_2; + next_row += src_pitch_x_2; + } + if (height_remainder) { + for (i = 0; i < width_half; i++) { + READ_1x2_PIXELS; + *plane_interleaved_uv++ = MAKE_V(r, g, b); + *plane_interleaved_uv++ = MAKE_U(r, g, b); + } + if (width_remainder) { + READ_1x1_PIXEL; + *plane_interleaved_uv++ = MAKE_V(r, g, b); + *plane_interleaved_uv++ = MAKE_U(r, g, b); + } + } + } + } + break; + + case SDL_PIXELFORMAT_YUY2: + case SDL_PIXELFORMAT_UYVY: + case SDL_PIXELFORMAT_YVYU: + { + const Uint8 *curr_row = (const Uint8*) src; + Uint8 *plane = (Uint8*) dst; + const int row_size = (4 * ((width + 1) / 2)); + int plane_skip; + + if (dst_pitch < row_size) { + return SDL_SetError("Destination pitch is too small, expected at least %d\n", row_size); + } + plane_skip = (dst_pitch - row_size); + + /* Write YUV plane, packed */ + if (dst_format == SDL_PIXELFORMAT_YUY2) + { + for (j = 0; j < height; j++) { + for (i = 0; i < width_half; i++) { + READ_TWO_RGB_PIXELS; + /* Y U Y1 V */ + *plane++ = MAKE_Y(r, g, b); + *plane++ = MAKE_U(R, G, B); + *plane++ = MAKE_Y(r1, g1, b1); + *plane++ = MAKE_V(R, G, B); + } + if (width_remainder) { + READ_ONE_RGB_PIXEL; + /* Y U Y V */ + *plane++ = MAKE_Y(r, g, b); + *plane++ = MAKE_U(r, g, b); + *plane++ = MAKE_Y(r, g, b); + *plane++ = MAKE_V(r, g, b); + } + plane += plane_skip; + curr_row += src_pitch; + } + } + else if (dst_format == SDL_PIXELFORMAT_UYVY) + { + for (j = 0; j < height; j++) { + for (i = 0; i < width_half; i++) { + READ_TWO_RGB_PIXELS; + /* U Y V Y1 */ + *plane++ = MAKE_U(R, G, B); + *plane++ = MAKE_Y(r, g, b); + *plane++ = MAKE_V(R, G, B); + *plane++ = MAKE_Y(r1, g1, b1); + } + if (width_remainder) { + READ_ONE_RGB_PIXEL; + /* U Y V Y */ + *plane++ = MAKE_U(r, g, b); + *plane++ = MAKE_Y(r, g, b); + *plane++ = MAKE_V(r, g, b); + *plane++ = MAKE_Y(r, g, b); + } + plane += plane_skip; + curr_row += src_pitch; + } + } + else if (dst_format == SDL_PIXELFORMAT_YVYU) + { + for (j = 0; j < height; j++) { + for (i = 0; i < width_half; i++) { + READ_TWO_RGB_PIXELS; + /* Y V Y1 U */ + *plane++ = MAKE_Y(r, g, b); + *plane++ = MAKE_V(R, G, B); + *plane++ = MAKE_Y(r1, g1, b1); + *plane++ = MAKE_U(R, G, B); + } + if (width_remainder) { + READ_ONE_RGB_PIXEL; + /* Y V Y U */ + *plane++ = MAKE_Y(r, g, b); + *plane++ = MAKE_V(r, g, b); + *plane++ = MAKE_Y(r, g, b); + *plane++ = MAKE_U(r, g, b); + } + plane += plane_skip; + curr_row += src_pitch; + } + } + } + break; + + default: + return SDL_SetError("Unsupported YUV destination format: %s", SDL_GetPixelFormatName(dst_format)); + } +#undef MAKE_Y +#undef MAKE_U +#undef MAKE_V +#undef READ_2x2_PIXELS +#undef READ_2x1_PIXELS +#undef READ_1x2_PIXELS +#undef READ_1x1_PIXEL +#undef READ_TWO_RGB_PIXELS +#undef READ_ONE_RGB_PIXEL + return 0; +} + +int +SDL_ConvertPixels_RGB_to_YUV(int width, int height, + Uint32 src_format, const void *src, int src_pitch, + Uint32 dst_format, void *dst, int dst_pitch) +{ +#if 0 /* Doesn't handle odd widths */ + /* RGB24 to FOURCC */ + if (src_format == SDL_PIXELFORMAT_RGB24) { + Uint8 *y; + Uint8 *u; + Uint8 *v; + Uint32 y_stride; + Uint32 uv_stride; + YCbCrType yuv_type; + + if (GetYUVPlanes(width, height, dst_format, dst, dst_pitch, (const Uint8 **)&y, (const Uint8 **)&u, (const Uint8 **)&v, &y_stride, &uv_stride) < 0) { + return -1; + } + + if (GetYUVConversionType(width, height, &yuv_type) < 0) { + return -1; + } + + rgb24_yuv420_std(width, height, src, src_pitch, y, u, v, y_stride, uv_stride, yuv_type); + return 0; + } +#endif + + /* ARGB8888 to FOURCC */ + if (src_format == SDL_PIXELFORMAT_ARGB8888) { + return SDL_ConvertPixels_ARGB8888_to_YUV(width, height, src, src_pitch, dst_format, dst, dst_pitch); + } + + /* not ARGB8888 to FOURCC : need an intermediate conversion */ + { + int ret; + void *tmp; + int tmp_pitch = (width * sizeof(Uint32)); + + tmp = SDL_malloc(tmp_pitch * height); + if (tmp == NULL) { + return SDL_OutOfMemory(); + } + + /* convert src/src_format to tmp/ARGB8888 */ + ret = SDL_ConvertPixels(width, height, src_format, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, tmp, tmp_pitch); + if (ret == -1) { + SDL_free(tmp); + return ret; + } + + /* convert tmp/ARGB8888 to dst/FOURCC */ + ret = SDL_ConvertPixels_ARGB8888_to_YUV(width, height, tmp, tmp_pitch, dst_format, dst, dst_pitch); + SDL_free(tmp); + return ret; + } +} + +static int +SDL_ConvertPixels_YUV_to_YUV_Copy(int width, int height, Uint32 format, + const void *src, int src_pitch, void *dst, int dst_pitch) +{ + int i; + + if (IsPlanar2x2Format(format)) { + /* Y plane */ + for (i = height; i--;) { + SDL_memcpy(dst, src, width); + src = (const Uint8*)src + src_pitch; + dst = (Uint8*)dst + dst_pitch; + } + + if (format == SDL_PIXELFORMAT_YV12 || format == SDL_PIXELFORMAT_IYUV) { + /* U and V planes are a quarter the size of the Y plane, rounded up */ + width = (width + 1) / 2; + height = (height + 1) / 2; + src_pitch = (src_pitch + 1) / 2; + dst_pitch = (dst_pitch + 1) / 2; + for (i = height * 2; i--;) { + SDL_memcpy(dst, src, width); + src = (const Uint8*)src + src_pitch; + dst = (Uint8*)dst + dst_pitch; + } + } else if (format == SDL_PIXELFORMAT_NV12 || format == SDL_PIXELFORMAT_NV21) { + /* U/V plane is half the height of the Y plane, rounded up */ + height = (height + 1) / 2; + width = ((width + 1) / 2)*2; + src_pitch = ((src_pitch + 1) / 2)*2; + dst_pitch = ((dst_pitch + 1) / 2)*2; + for (i = height; i--;) { + SDL_memcpy(dst, src, width); + src = (const Uint8*)src + src_pitch; + dst = (Uint8*)dst + dst_pitch; + } + } + return 0; + } + + if (IsPacked4Format(format)) { + /* Packed planes */ + width = 4 * ((width + 1) / 2); + for (i = height; i--;) { + SDL_memcpy(dst, src, width); + src = (const Uint8*)src + src_pitch; + dst = (Uint8*)dst + dst_pitch; + } + return 0; + } + + return SDL_SetError("SDL_ConvertPixels_YUV_to_YUV_Copy: Unsupported YUV format: %s", SDL_GetPixelFormatName(format)); +} + +static int +SDL_ConvertPixels_SwapUVPlanes(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) +{ + int y; + const int UVwidth = (width + 1)/2; + const int UVheight = (height + 1)/2; + + /* Skip the Y plane */ + src = (const Uint8 *)src + height * src_pitch; + dst = (Uint8 *)dst + height * dst_pitch; + + if (src == dst) { + int UVpitch = (dst_pitch + 1)/2; + Uint8 *tmp; + Uint8 *row1 = dst; + Uint8 *row2 = (Uint8 *)dst + UVheight * UVpitch; + + /* Allocate a temporary row for the swap */ + tmp = (Uint8 *)SDL_malloc(UVwidth); + if (!tmp) { + return SDL_OutOfMemory(); + } + for (y = 0; y < UVheight; ++y) { + SDL_memcpy(tmp, row1, UVwidth); + SDL_memcpy(row1, row2, UVwidth); + SDL_memcpy(row2, tmp, UVwidth); + row1 += UVpitch; + row2 += UVpitch; + } + SDL_free(tmp); + } else { + const Uint8 *srcUV; + Uint8 *dstUV; + int srcUVPitch = ((src_pitch + 1)/2); + int dstUVPitch = ((dst_pitch + 1)/2); + + /* Copy the first plane */ + srcUV = (const Uint8 *)src; + dstUV = (Uint8 *)dst + UVheight * dstUVPitch; + for (y = 0; y < UVheight; ++y) { + SDL_memcpy(dstUV, srcUV, UVwidth); + srcUV += srcUVPitch; + dstUV += dstUVPitch; + } + + /* Copy the second plane */ + dstUV = (Uint8 *)dst; + for (y = 0; y < UVheight; ++y) { + SDL_memcpy(dstUV, srcUV, UVwidth); + srcUV += srcUVPitch; + dstUV += dstUVPitch; + } + } + return 0; +} + +static int +SDL_ConvertPixels_PackUVPlanes_to_NV(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch, SDL_bool reverseUV) +{ + int x, y; + const int UVwidth = (width + 1)/2; + const int UVheight = (height + 1)/2; + const int srcUVPitch = ((src_pitch + 1)/2); + const int srcUVPitchLeft = srcUVPitch - UVwidth; + const int dstUVPitch = ((dst_pitch + 1)/2)*2; + const int dstUVPitchLeft = dstUVPitch - UVwidth*2; + const Uint8 *src1, *src2; + Uint8 *dstUV; + Uint8 *tmp = NULL; +#ifdef __SSE2__ + const SDL_bool use_SSE2 = SDL_HasSSE2(); +#endif + + /* Skip the Y plane */ + src = (const Uint8 *)src + height * src_pitch; + dst = (Uint8 *)dst + height * dst_pitch; + + if (src == dst) { + /* Need to make a copy of the buffer so we don't clobber it while converting */ + tmp = (Uint8 *)SDL_malloc(2*UVheight*srcUVPitch); + if (!tmp) { + return SDL_OutOfMemory(); + } + SDL_memcpy(tmp, src, 2*UVheight*srcUVPitch); + src = tmp; + } + + if (reverseUV) { + src2 = (const Uint8 *)src; + src1 = src2 + UVheight * srcUVPitch; + } else { + src1 = (const Uint8 *)src; + src2 = src1 + UVheight * srcUVPitch; + } + dstUV = (Uint8 *)dst; + + y = UVheight; + while (y--) { + x = UVwidth; +#ifdef __SSE2__ + if (use_SSE2) { + while (x >= 16) { + __m128i u = _mm_loadu_si128((__m128i *)src1); + __m128i v = _mm_loadu_si128((__m128i *)src2); + __m128i uv1 = _mm_unpacklo_epi8(u, v); + __m128i uv2 = _mm_unpackhi_epi8(u, v); + _mm_storeu_si128((__m128i*)dstUV, uv1); + _mm_storeu_si128((__m128i*)(dstUV + 16), uv2); + src1 += 16; + src2 += 16; + dstUV += 32; + x -= 16; + } + } +#endif + while (x--) { + *dstUV++ = *src1++; + *dstUV++ = *src2++; + } + src1 += srcUVPitchLeft; + src2 += srcUVPitchLeft; + dstUV += dstUVPitchLeft; + } + + if (tmp) { + SDL_free(tmp); + } + return 0; +} + +static int +SDL_ConvertPixels_SplitNV_to_UVPlanes(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch, SDL_bool reverseUV) +{ + int x, y; + const int UVwidth = (width + 1)/2; + const int UVheight = (height + 1)/2; + const int srcUVPitch = ((src_pitch + 1)/2)*2; + const int srcUVPitchLeft = srcUVPitch - UVwidth*2; + const int dstUVPitch = ((dst_pitch + 1)/2); + const int dstUVPitchLeft = dstUVPitch - UVwidth; + const Uint8 *srcUV; + Uint8 *dst1, *dst2; + Uint8 *tmp = NULL; +#ifdef __SSE2__ + const SDL_bool use_SSE2 = SDL_HasSSE2(); +#endif + + /* Skip the Y plane */ + src = (const Uint8 *)src + height * src_pitch; + dst = (Uint8 *)dst + height * dst_pitch; + + if (src == dst) { + /* Need to make a copy of the buffer so we don't clobber it while converting */ + tmp = (Uint8 *)SDL_malloc(UVheight*srcUVPitch); + if (!tmp) { + return SDL_OutOfMemory(); + } + SDL_memcpy(tmp, src, UVheight*srcUVPitch); + src = tmp; + } + + if (reverseUV) { + dst2 = (Uint8 *)dst; + dst1 = dst2 + UVheight * dstUVPitch; + } else { + dst1 = (Uint8 *)dst; + dst2 = dst1 + UVheight * dstUVPitch; + } + srcUV = (const Uint8 *)src; + + y = UVheight; + while (y--) { + x = UVwidth; +#ifdef __SSE2__ + if (use_SSE2) { + __m128i mask = _mm_set1_epi16(0x00FF); + while (x >= 16) { + __m128i uv1 = _mm_loadu_si128((__m128i*)srcUV); + __m128i uv2 = _mm_loadu_si128((__m128i*)(srcUV+16)); + __m128i u1 = _mm_and_si128(uv1, mask); + __m128i u2 = _mm_and_si128(uv2, mask); + __m128i u = _mm_packus_epi16(u1, u2); + __m128i v1 = _mm_srli_epi16(uv1, 8); + __m128i v2 = _mm_srli_epi16(uv2, 8); + __m128i v = _mm_packus_epi16(v1, v2); + _mm_storeu_si128((__m128i*)dst1, u); + _mm_storeu_si128((__m128i*)dst2, v); + srcUV += 32; + dst1 += 16; + dst2 += 16; + x -= 16; + } + } +#endif + while (x--) { + *dst1++ = *srcUV++; + *dst2++ = *srcUV++; + } + srcUV += srcUVPitchLeft; + dst1 += dstUVPitchLeft; + dst2 += dstUVPitchLeft; + } + + if (tmp) { + SDL_free(tmp); + } + return 0; +} + +static int +SDL_ConvertPixels_SwapNV(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) +{ + int x, y; + const int UVwidth = (width + 1)/2; + const int UVheight = (height + 1)/2; + const int srcUVPitch = ((src_pitch + 1)/2)*2; + const int srcUVPitchLeft = (srcUVPitch - UVwidth*2)/sizeof(Uint16); + const int dstUVPitch = ((dst_pitch + 1)/2)*2; + const int dstUVPitchLeft = (dstUVPitch - UVwidth*2)/sizeof(Uint16); + const Uint16 *srcUV; + Uint16 *dstUV; +#ifdef __SSE2__ + const SDL_bool use_SSE2 = SDL_HasSSE2(); +#endif + + /* Skip the Y plane */ + src = (const Uint8 *)src + height * src_pitch; + dst = (Uint8 *)dst + height * dst_pitch; + + srcUV = (const Uint16 *)src; + dstUV = (Uint16 *)dst; + y = UVheight; + while (y--) { + x = UVwidth; +#ifdef __SSE2__ + if (use_SSE2) { + while (x >= 8) { + __m128i uv = _mm_loadu_si128((__m128i*)srcUV); + __m128i v = _mm_slli_epi16(uv, 8); + __m128i u = _mm_srli_epi16(uv, 8); + __m128i vu = _mm_or_si128(v, u); + _mm_storeu_si128((__m128i*)dstUV, vu); + srcUV += 8; + dstUV += 8; + x -= 8; + } + } +#endif + while (x--) { + *dstUV++ = SDL_Swap16(*srcUV++); + } + srcUV += srcUVPitchLeft; + dstUV += dstUVPitchLeft; + } + return 0; +} + +static int +SDL_ConvertPixels_Planar2x2_to_Planar2x2(int width, int height, + Uint32 src_format, const void *src, int src_pitch, + Uint32 dst_format, void *dst, int dst_pitch) +{ + if (src != dst) { + /* Copy Y plane */ + int i; + const Uint8 *srcY = (const Uint8 *)src; + Uint8 *dstY = (Uint8 *)dst; + for (i = height; i--; ) { + SDL_memcpy(dstY, srcY, width); + srcY += src_pitch; + dstY += dst_pitch; + } + } + + switch (src_format) { + case SDL_PIXELFORMAT_YV12: + switch (dst_format) { + case SDL_PIXELFORMAT_IYUV: + return SDL_ConvertPixels_SwapUVPlanes(width, height, src, src_pitch, dst, dst_pitch); + case SDL_PIXELFORMAT_NV12: + return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, SDL_TRUE); + case SDL_PIXELFORMAT_NV21: + return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, SDL_FALSE); + default: + break; + } + break; + case SDL_PIXELFORMAT_IYUV: + switch (dst_format) { + case SDL_PIXELFORMAT_YV12: + return SDL_ConvertPixels_SwapUVPlanes(width, height, src, src_pitch, dst, dst_pitch); + case SDL_PIXELFORMAT_NV12: + return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, SDL_FALSE); + case SDL_PIXELFORMAT_NV21: + return SDL_ConvertPixels_PackUVPlanes_to_NV(width, height, src, src_pitch, dst, dst_pitch, SDL_TRUE); + default: + break; + } + break; + case SDL_PIXELFORMAT_NV12: + switch (dst_format) { + case SDL_PIXELFORMAT_YV12: + return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, SDL_TRUE); + case SDL_PIXELFORMAT_IYUV: + return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, SDL_FALSE); + case SDL_PIXELFORMAT_NV21: + return SDL_ConvertPixels_SwapNV(width, height, src, src_pitch, dst, dst_pitch); + default: + break; + } + break; + case SDL_PIXELFORMAT_NV21: + switch (dst_format) { + case SDL_PIXELFORMAT_YV12: + return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, SDL_FALSE); + case SDL_PIXELFORMAT_IYUV: + return SDL_ConvertPixels_SplitNV_to_UVPlanes(width, height, src, src_pitch, dst, dst_pitch, SDL_TRUE); + case SDL_PIXELFORMAT_NV12: + return SDL_ConvertPixels_SwapNV(width, height, src, src_pitch, dst, dst_pitch); + default: + break; + } + break; + default: + break; + } + return SDL_SetError("SDL_ConvertPixels_Planar2x2_to_Planar2x2: Unsupported YUV conversion: %s -> %s", SDL_GetPixelFormatName(src_format), SDL_GetPixelFormatName(dst_format)); +} + +#define PACKED4_TO_PACKED4_ROW_SSE2(shuffle) \ + while (x >= 4) { \ + __m128i yuv = _mm_loadu_si128((__m128i*)srcYUV); \ + __m128i lo = _mm_unpacklo_epi8(yuv, _mm_setzero_si128()); \ + __m128i hi = _mm_unpackhi_epi8(yuv, _mm_setzero_si128()); \ + lo = _mm_shufflelo_epi16(lo, shuffle); \ + lo = _mm_shufflehi_epi16(lo, shuffle); \ + hi = _mm_shufflelo_epi16(hi, shuffle); \ + hi = _mm_shufflehi_epi16(hi, shuffle); \ + yuv = _mm_packus_epi16(lo, hi); \ + _mm_storeu_si128((__m128i*)dstYUV, yuv); \ + srcYUV += 16; \ + dstYUV += 16; \ + x -= 4; \ + } \ + +static int +SDL_ConvertPixels_YUY2_to_UYVY(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) +{ + int x, y; + const int YUVwidth = (width + 1)/2; + const int srcYUVPitchLeft = (src_pitch - YUVwidth*4); + const int dstYUVPitchLeft = (dst_pitch - YUVwidth*4); + const Uint8 *srcYUV = (const Uint8 *)src; + Uint8 *dstYUV = (Uint8 *)dst; +#ifdef __SSE2__ + const SDL_bool use_SSE2 = SDL_HasSSE2(); +#endif + + y = height; + while (y--) { + x = YUVwidth; +#ifdef __SSE2__ + if (use_SSE2) { + PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(2, 3, 0, 1)); + } +#endif + while (x--) { + Uint8 Y1, U, Y2, V; + + Y1 = srcYUV[0]; + U = srcYUV[1]; + Y2 = srcYUV[2]; + V = srcYUV[3]; + srcYUV += 4; + + dstYUV[0] = U; + dstYUV[1] = Y1; + dstYUV[2] = V; + dstYUV[3] = Y2; + dstYUV += 4; + } + srcYUV += srcYUVPitchLeft; + dstYUV += dstYUVPitchLeft; + } + return 0; +} + +static int +SDL_ConvertPixels_YUY2_to_YVYU(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) +{ + int x, y; + const int YUVwidth = (width + 1)/2; + const int srcYUVPitchLeft = (src_pitch - YUVwidth*4); + const int dstYUVPitchLeft = (dst_pitch - YUVwidth*4); + const Uint8 *srcYUV = (const Uint8 *)src; + Uint8 *dstYUV = (Uint8 *)dst; +#ifdef __SSE2__ + const SDL_bool use_SSE2 = SDL_HasSSE2(); +#endif + + y = height; + while (y--) { + x = YUVwidth; +#ifdef __SSE2__ + if (use_SSE2) { + PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(1, 2, 3, 0)); + } +#endif + while (x--) { + Uint8 Y1, U, Y2, V; + + Y1 = srcYUV[0]; + U = srcYUV[1]; + Y2 = srcYUV[2]; + V = srcYUV[3]; + srcYUV += 4; + + dstYUV[0] = Y1; + dstYUV[1] = V; + dstYUV[2] = Y2; + dstYUV[3] = U; + dstYUV += 4; + } + srcYUV += srcYUVPitchLeft; + dstYUV += dstYUVPitchLeft; + } + return 0; +} + +static int +SDL_ConvertPixels_UYVY_to_YUY2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) +{ + int x, y; + const int YUVwidth = (width + 1)/2; + const int srcYUVPitchLeft = (src_pitch - YUVwidth*4); + const int dstYUVPitchLeft = (dst_pitch - YUVwidth*4); + const Uint8 *srcYUV = (const Uint8 *)src; + Uint8 *dstYUV = (Uint8 *)dst; +#ifdef __SSE2__ + const SDL_bool use_SSE2 = SDL_HasSSE2(); +#endif + + y = height; + while (y--) { + x = YUVwidth; +#ifdef __SSE2__ + if (use_SSE2) { + PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(2, 3, 0, 1)); + } +#endif + while (x--) { + Uint8 Y1, U, Y2, V; + + U = srcYUV[0]; + Y1 = srcYUV[1]; + V = srcYUV[2]; + Y2 = srcYUV[3]; + srcYUV += 4; + + dstYUV[0] = Y1; + dstYUV[1] = U; + dstYUV[2] = Y2; + dstYUV[3] = V; + dstYUV += 4; + } + srcYUV += srcYUVPitchLeft; + dstYUV += dstYUVPitchLeft; + } + return 0; +} + +static int +SDL_ConvertPixels_UYVY_to_YVYU(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) +{ + int x, y; + const int YUVwidth = (width + 1)/2; + const int srcYUVPitchLeft = (src_pitch - YUVwidth*4); + const int dstYUVPitchLeft = (dst_pitch - YUVwidth*4); + const Uint8 *srcYUV = (const Uint8 *)src; + Uint8 *dstYUV = (Uint8 *)dst; +#ifdef __SSE2__ + const SDL_bool use_SSE2 = SDL_HasSSE2(); +#endif + + y = height; + while (y--) { + x = YUVwidth; +#ifdef __SSE2__ + if (use_SSE2) { + PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(0, 3, 2, 1)); + } +#endif + while (x--) { + Uint8 Y1, U, Y2, V; + + U = srcYUV[0]; + Y1 = srcYUV[1]; + V = srcYUV[2]; + Y2 = srcYUV[3]; + srcYUV += 4; + + dstYUV[0] = Y1; + dstYUV[1] = V; + dstYUV[2] = Y2; + dstYUV[3] = U; + dstYUV += 4; + } + srcYUV += srcYUVPitchLeft; + dstYUV += dstYUVPitchLeft; + } + return 0; +} + +static int +SDL_ConvertPixels_YVYU_to_YUY2(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) +{ + int x, y; + const int YUVwidth = (width + 1)/2; + const int srcYUVPitchLeft = (src_pitch - YUVwidth*4); + const int dstYUVPitchLeft = (dst_pitch - YUVwidth*4); + const Uint8 *srcYUV = (const Uint8 *)src; + Uint8 *dstYUV = (Uint8 *)dst; +#ifdef __SSE2__ + const SDL_bool use_SSE2 = SDL_HasSSE2(); +#endif + + y = height; + while (y--) { + x = YUVwidth; +#ifdef __SSE2__ + if (use_SSE2) { + PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(1, 2, 3, 0)); + } +#endif + while (x--) { + Uint8 Y1, U, Y2, V; + + Y1 = srcYUV[0]; + V = srcYUV[1]; + Y2 = srcYUV[2]; + U = srcYUV[3]; + srcYUV += 4; + + dstYUV[0] = Y1; + dstYUV[1] = U; + dstYUV[2] = Y2; + dstYUV[3] = V; + dstYUV += 4; + } + srcYUV += srcYUVPitchLeft; + dstYUV += dstYUVPitchLeft; + } + return 0; +} + +static int +SDL_ConvertPixels_YVYU_to_UYVY(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch) +{ + int x, y; + const int YUVwidth = (width + 1)/2; + const int srcYUVPitchLeft = (src_pitch - YUVwidth*4); + const int dstYUVPitchLeft = (dst_pitch - YUVwidth*4); + const Uint8 *srcYUV = (const Uint8 *)src; + Uint8 *dstYUV = (Uint8 *)dst; +#ifdef __SSE2__ + const SDL_bool use_SSE2 = SDL_HasSSE2(); +#endif + + y = height; + while (y--) { + x = YUVwidth; +#ifdef __SSE2__ + if (use_SSE2) { + PACKED4_TO_PACKED4_ROW_SSE2(_MM_SHUFFLE(2, 1, 0, 3)); + } +#endif + while (x--) { + Uint8 Y1, U, Y2, V; + + Y1 = srcYUV[0]; + V = srcYUV[1]; + Y2 = srcYUV[2]; + U = srcYUV[3]; + srcYUV += 4; + + dstYUV[0] = U; + dstYUV[1] = Y1; + dstYUV[2] = V; + dstYUV[3] = Y2; + dstYUV += 4; + } + srcYUV += srcYUVPitchLeft; + dstYUV += dstYUVPitchLeft; + } + return 0; +} + +static int +SDL_ConvertPixels_Packed4_to_Packed4(int width, int height, + Uint32 src_format, const void *src, int src_pitch, + Uint32 dst_format, void *dst, int dst_pitch) +{ + switch (src_format) { + case SDL_PIXELFORMAT_YUY2: + switch (dst_format) { + case SDL_PIXELFORMAT_UYVY: + return SDL_ConvertPixels_YUY2_to_UYVY(width, height, src, src_pitch, dst, dst_pitch); + case SDL_PIXELFORMAT_YVYU: + return SDL_ConvertPixels_YUY2_to_YVYU(width, height, src, src_pitch, dst, dst_pitch); + default: + break; + } + break; + case SDL_PIXELFORMAT_UYVY: + switch (dst_format) { + case SDL_PIXELFORMAT_YUY2: + return SDL_ConvertPixels_UYVY_to_YUY2(width, height, src, src_pitch, dst, dst_pitch); + case SDL_PIXELFORMAT_YVYU: + return SDL_ConvertPixels_UYVY_to_YVYU(width, height, src, src_pitch, dst, dst_pitch); + default: + break; + } + break; + case SDL_PIXELFORMAT_YVYU: + switch (dst_format) { + case SDL_PIXELFORMAT_YUY2: + return SDL_ConvertPixels_YVYU_to_YUY2(width, height, src, src_pitch, dst, dst_pitch); + case SDL_PIXELFORMAT_UYVY: + return SDL_ConvertPixels_YVYU_to_UYVY(width, height, src, src_pitch, dst, dst_pitch); + default: + break; + } + break; + default: + break; + } + return SDL_SetError("SDL_ConvertPixels_Packed4_to_Packed4: Unsupported YUV conversion: %s -> %s", SDL_GetPixelFormatName(src_format), SDL_GetPixelFormatName(dst_format)); +} + +static int +SDL_ConvertPixels_Planar2x2_to_Packed4(int width, int height, + Uint32 src_format, const void *src, int src_pitch, + Uint32 dst_format, void *dst, int dst_pitch) +{ + int x, y; + const Uint8 *srcY1, *srcY2, *srcU, *srcV; + Uint32 srcY_pitch, srcUV_pitch; + Uint32 srcY_pitch_left, srcUV_pitch_left, srcUV_pixel_stride; + Uint8 *dstY1, *dstY2, *dstU1, *dstU2, *dstV1, *dstV2; + Uint32 dstY_pitch, dstUV_pitch; + Uint32 dst_pitch_left; + + if (src == dst) { + return SDL_SetError("Can't change YUV plane types in-place"); + } + + if (GetYUVPlanes(width, height, src_format, src, src_pitch, + &srcY1, &srcU, &srcV, &srcY_pitch, &srcUV_pitch) < 0) { + return -1; + } + srcY2 = srcY1 + srcY_pitch; + srcY_pitch_left = (srcY_pitch - width); + + if (src_format == SDL_PIXELFORMAT_NV12 || src_format == SDL_PIXELFORMAT_NV21) { + srcUV_pixel_stride = 2; + srcUV_pitch_left = (srcUV_pitch - 2*((width + 1)/2)); + } else { + srcUV_pixel_stride = 1; + srcUV_pitch_left = (srcUV_pitch - ((width + 1)/2)); + } + + if (GetYUVPlanes(width, height, dst_format, dst, dst_pitch, + (const Uint8 **)&dstY1, (const Uint8 **)&dstU1, (const Uint8 **)&dstV1, + &dstY_pitch, &dstUV_pitch) < 0) { + return -1; + } + dstY2 = dstY1 + dstY_pitch; + dstU2 = dstU1 + dstUV_pitch; + dstV2 = dstV1 + dstUV_pitch; + dst_pitch_left = (dstY_pitch - 4*((width + 1)/2)); + + /* Copy 2x2 blocks of pixels at a time */ + for (y = 0; y < (height - 1); y += 2) { + for (x = 0; x < (width - 1); x += 2) { + /* Row 1 */ + *dstY1 = *srcY1++; + dstY1 += 2; + *dstY1 = *srcY1++; + dstY1 += 2; + *dstU1 = *srcU; + *dstV1 = *srcV; + + /* Row 2 */ + *dstY2 = *srcY2++; + dstY2 += 2; + *dstY2 = *srcY2++; + dstY2 += 2; + *dstU2 = *srcU; + *dstV2 = *srcV; + + srcU += srcUV_pixel_stride; + srcV += srcUV_pixel_stride; + dstU1 += 4; + dstU2 += 4; + dstV1 += 4; + dstV2 += 4; + } + + /* Last column */ + if (x == (width - 1)) { + /* Row 1 */ + *dstY1 = *srcY1; + dstY1 += 2; + *dstY1 = *srcY1++; + dstY1 += 2; + *dstU1 = *srcU; + *dstV1 = *srcV; + + /* Row 2 */ + *dstY2 = *srcY2; + dstY2 += 2; + *dstY2 = *srcY2++; + dstY2 += 2; + *dstU2 = *srcU; + *dstV2 = *srcV; + + srcU += srcUV_pixel_stride; + srcV += srcUV_pixel_stride; + dstU1 += 4; + dstU2 += 4; + dstV1 += 4; + dstV2 += 4; + } + + srcY1 += srcY_pitch_left + srcY_pitch; + srcY2 += srcY_pitch_left + srcY_pitch; + srcU += srcUV_pitch_left; + srcV += srcUV_pitch_left; + dstY1 += dst_pitch_left + dstY_pitch; + dstY2 += dst_pitch_left + dstY_pitch; + dstU1 += dst_pitch_left + dstUV_pitch; + dstU2 += dst_pitch_left + dstUV_pitch; + dstV1 += dst_pitch_left + dstUV_pitch; + dstV2 += dst_pitch_left + dstUV_pitch; + } + + /* Last row */ + if (y == (height - 1)) { + for (x = 0; x < (width - 1); x += 2) { + /* Row 1 */ + *dstY1 = *srcY1++; + dstY1 += 2; + *dstY1 = *srcY1++; + dstY1 += 2; + *dstU1 = *srcU; + *dstV1 = *srcV; + + srcU += srcUV_pixel_stride; + srcV += srcUV_pixel_stride; + dstU1 += 4; + dstV1 += 4; + } + + /* Last column */ + if (x == (width - 1)) { + /* Row 1 */ + *dstY1 = *srcY1; + dstY1 += 2; + *dstY1 = *srcY1++; + dstY1 += 2; + *dstU1 = *srcU; + *dstV1 = *srcV; + + srcU += srcUV_pixel_stride; + srcV += srcUV_pixel_stride; + dstU1 += 4; + dstV1 += 4; + } + } + return 0; +} + +static int +SDL_ConvertPixels_Packed4_to_Planar2x2(int width, int height, + Uint32 src_format, const void *src, int src_pitch, + Uint32 dst_format, void *dst, int dst_pitch) +{ + int x, y; + const Uint8 *srcY1, *srcY2, *srcU1, *srcU2, *srcV1, *srcV2; + Uint32 srcY_pitch, srcUV_pitch; + Uint32 src_pitch_left; + Uint8 *dstY1, *dstY2, *dstU, *dstV; + Uint32 dstY_pitch, dstUV_pitch; + Uint32 dstY_pitch_left, dstUV_pitch_left, dstUV_pixel_stride; + + if (src == dst) { + return SDL_SetError("Can't change YUV plane types in-place"); + } + + if (GetYUVPlanes(width, height, src_format, src, src_pitch, + &srcY1, &srcU1, &srcV1, &srcY_pitch, &srcUV_pitch) < 0) { + return -1; + } + srcY2 = srcY1 + srcY_pitch; + srcU2 = srcU1 + srcUV_pitch; + srcV2 = srcV1 + srcUV_pitch; + src_pitch_left = (srcY_pitch - 4*((width + 1)/2)); + + if (GetYUVPlanes(width, height, dst_format, dst, dst_pitch, + (const Uint8 **)&dstY1, (const Uint8 **)&dstU, (const Uint8 **)&dstV, + &dstY_pitch, &dstUV_pitch) < 0) { + return -1; + } + dstY2 = dstY1 + dstY_pitch; + dstY_pitch_left = (dstY_pitch - width); + + if (dst_format == SDL_PIXELFORMAT_NV12 || dst_format == SDL_PIXELFORMAT_NV21) { + dstUV_pixel_stride = 2; + dstUV_pitch_left = (dstUV_pitch - 2*((width + 1)/2)); + } else { + dstUV_pixel_stride = 1; + dstUV_pitch_left = (dstUV_pitch - ((width + 1)/2)); + } + + /* Copy 2x2 blocks of pixels at a time */ + for (y = 0; y < (height - 1); y += 2) { + for (x = 0; x < (width - 1); x += 2) { + /* Row 1 */ + *dstY1++ = *srcY1; + srcY1 += 2; + *dstY1++ = *srcY1; + srcY1 += 2; + + /* Row 2 */ + *dstY2++ = *srcY2; + srcY2 += 2; + *dstY2++ = *srcY2; + srcY2 += 2; + + *dstU = (Uint8)(((Uint32)*srcU1 + *srcU2)/2); + *dstV = (Uint8)(((Uint32)*srcV1 + *srcV2)/2); + + srcU1 += 4; + srcU2 += 4; + srcV1 += 4; + srcV2 += 4; + dstU += dstUV_pixel_stride; + dstV += dstUV_pixel_stride; + } + + /* Last column */ + if (x == (width - 1)) { + /* Row 1 */ + *dstY1 = *srcY1; + srcY1 += 2; + *dstY1++ = *srcY1; + srcY1 += 2; + + /* Row 2 */ + *dstY2 = *srcY2; + srcY2 += 2; + *dstY2++ = *srcY2; + srcY2 += 2; + + *dstU = (Uint8)(((Uint32)*srcU1 + *srcU2)/2); + *dstV = (Uint8)(((Uint32)*srcV1 + *srcV2)/2); + + srcU1 += 4; + srcU2 += 4; + srcV1 += 4; + srcV2 += 4; + dstU += dstUV_pixel_stride; + dstV += dstUV_pixel_stride; + } + + srcY1 += src_pitch_left + srcY_pitch; + srcY2 += src_pitch_left + srcY_pitch; + srcU1 += src_pitch_left + srcUV_pitch; + srcU2 += src_pitch_left + srcUV_pitch; + srcV1 += src_pitch_left + srcUV_pitch; + srcV2 += src_pitch_left + srcUV_pitch; + dstY1 += dstY_pitch_left + dstY_pitch; + dstY2 += dstY_pitch_left + dstY_pitch; + dstU += dstUV_pitch_left; + dstV += dstUV_pitch_left; + } + + /* Last row */ + if (y == (height - 1)) { + for (x = 0; x < (width - 1); x += 2) { + *dstY1++ = *srcY1; + srcY1 += 2; + *dstY1++ = *srcY1; + srcY1 += 2; + + *dstU = *srcU1; + *dstV = *srcV1; + + srcU1 += 4; + srcV1 += 4; + dstU += dstUV_pixel_stride; + dstV += dstUV_pixel_stride; + } + + /* Last column */ + if (x == (width - 1)) { + *dstY1 = *srcY1; + *dstU = *srcU1; + *dstV = *srcV1; + } + } + return 0; +} + +int +SDL_ConvertPixels_YUV_to_YUV(int width, int height, + Uint32 src_format, const void *src, int src_pitch, + Uint32 dst_format, void *dst, int dst_pitch) +{ + if (src_format == dst_format) { + if (src == dst) { + /* Nothing to do */ + return 0; + } + return SDL_ConvertPixels_YUV_to_YUV_Copy(width, height, src_format, src, src_pitch, dst, dst_pitch); + } + + if (IsPlanar2x2Format(src_format) && IsPlanar2x2Format(dst_format)) { + return SDL_ConvertPixels_Planar2x2_to_Planar2x2(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch); + } else if (IsPacked4Format(src_format) && IsPacked4Format(dst_format)) { + return SDL_ConvertPixels_Packed4_to_Packed4(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch); + } else if (IsPlanar2x2Format(src_format) && IsPacked4Format(dst_format)) { + return SDL_ConvertPixels_Planar2x2_to_Packed4(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch); + } else if (IsPacked4Format(src_format) && IsPlanar2x2Format(dst_format)) { + return SDL_ConvertPixels_Packed4_to_Planar2x2(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch); + } else { + return SDL_SetError("SDL_ConvertPixels_YUV_to_YUV: Unsupported YUV conversion: %s -> %s", SDL_GetPixelFormatName(src_format), SDL_GetPixelFormatName(dst_format)); + } +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/SDL_yuv_c.h b/src/video/SDL_yuv_c.h new file mode 100644 index 0000000000000..c7b342d1e073d --- /dev/null +++ b/src/video/SDL_yuv_c.h @@ -0,0 +1,30 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2017 Sam Lantinga + + 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, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../SDL_internal.h" + + +/* YUV conversion functions */ + +extern int SDL_ConvertPixels_YUV_to_RGB(int width, int height, Uint32 src_format, const void *src, int src_pitch, Uint32 dst_format, void *dst, int dst_pitch); +extern int SDL_ConvertPixels_RGB_to_YUV(int width, int height, Uint32 src_format, const void *src, int src_pitch, Uint32 dst_format, void *dst, int dst_pitch); +extern int SDL_ConvertPixels_YUV_to_YUV(int width, int height, Uint32 src_format, const void *src, int src_pitch, Uint32 dst_format, void *dst, int dst_pitch); + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/yuv2rgb/LICENSE b/src/video/yuv2rgb/LICENSE new file mode 100644 index 0000000000000..a76efd7be2a36 --- /dev/null +++ b/src/video/yuv2rgb/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2016, Adrien Descamps +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of yuv2rgb nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/video/yuv2rgb/README.md b/src/video/yuv2rgb/README.md new file mode 100644 index 0000000000000..5a3ef9046efdd --- /dev/null +++ b/src/video/yuv2rgb/README.md @@ -0,0 +1,63 @@ +From: https://github.com/descampsa/yuv2rgb +# yuv2rgb +C library for fast image conversion between yuv420p and rgb24. + +This is a simple library for optimized image conversion between YUV420p and rgb24. +It was done mainly as an exercise to learn to use sse instrinsics, so there may still be room for optimization. + +For each conversion, a standard c optimized function and two sse function (with aligned and unaligned memory) are implemented. +The sse version requires only SSE2, which is available on any reasonnably recent CPU. +The library also supports the three different YUV (YCrCb to be correct) color spaces that exist (see comments in code), and others can be added simply. + +There is a simple test program, that convert a raw YUV file to rgb ppm format, and measure computation time. +Optionnaly, it also compares the result and computation time with the ffmpeg implementation (that uses MMX), and with the IPP functions. + +To compile, simply do : + + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Release .. + make + +The test program only support raw YUV files for the YUV420 format, and ppm for the RGB24 format. +To generate a raw yuv file, you can use avconv: + + avconv -i example.jpg -c:v rawvideo -pix_fmt yuv420p example.yuv + +To generate the rgb file, you can use the ImageMagick convert program: + + convert example.jpg example.ppm + +Then, for YUV420 to RGB24 conversion, use the test program like that: + + ./test_yuv_rgb yuv2rgb image.yuv 4096 2160 image + +The second and third parameters are image width and height (that are needed because not available in the raw YUV file), and fourth parameter is the output filename template (several output files will be generated, named for example output_sse.ppm, output_av.ppm, etc.) + +Similarly, for RGB24 to YUV420 conversion: + + ./test_yuv_rgb yuv2rgb image.ppm image + +On my computer, the test program on a 4K image give the following for yuv2rgb: + + Time will be measured in each configuration for 100 iterations... + Processing time (std) : 2.630193 sec + Processing time (sse2_unaligned) : 0.704394 sec + Processing time (ffmpeg_unaligned) : 1.221432 sec + Processing time (ipp_unaligned) : 0.636274 sec + Processing time (sse2_aligned) : 0.606648 sec + Processing time (ffmpeg_aligned) : 1.227100 sec + Processing time (ipp_aligned) : 0.636951 sec + +And for rgb2yuv: + + Time will be measured in each configuration for 100 iterations... + Processing time (std) : 2.588675 sec + Processing time (sse2_unaligned) : 0.676625 sec + Processing time (ffmpeg_unaligned) : 3.385816 sec + Processing time (ipp_unaligned) : 0.593890 sec + Processing time (sse2_aligned) : 0.640630 sec + Processing time (ffmpeg_aligned) : 3.397952 sec + Processing time (ipp_aligned) : 0.579043 sec + +configuration : gcc 4.9.2, swscale 3.0.0, IPP 9.0.1, intel i7-5500U diff --git a/src/video/yuv2rgb/yuv_rgb.c b/src/video/yuv2rgb/yuv_rgb.c new file mode 100644 index 0000000000000..072ef5c11fe65 --- /dev/null +++ b/src/video/yuv2rgb/yuv_rgb.c @@ -0,0 +1,687 @@ +// Copyright 2016 Adrien Descamps +// Distributed under BSD 3-Clause License +#include "../../SDL_internal.h" + +#include "yuv_rgb.h" + +#include "SDL_cpuinfo.h" +/*#include */ + +#define PRECISION 6 +#define PRECISION_FACTOR (1<[0-255]) +// for ITU-R BT.709-6 values are derived from equations in sections 3.2-3.4, assuming RGB is encoded using full range ([0-1]<->[0-255]) +// all values are rounded to the fourth decimal + +static const YUV2RGBParam YUV2RGB[3] = { + // ITU-T T.871 (JPEG) + {/*.y_shift=*/ 0, /*.y_factor=*/ V(1.0), /*.v_r_factor=*/ V(1.402), /*.u_g_factor=*/ -V(0.3441), /*.v_g_factor=*/ -V(0.7141), /*.u_b_factor=*/ V(1.772)}, + // ITU-R BT.601-7 + {/*.y_shift=*/ 16, /*.y_factor=*/ V(1.1644), /*.v_r_factor=*/ V(1.596), /*.u_g_factor=*/ -V(0.3918), /*.v_g_factor=*/ -V(0.813), /*.u_b_factor=*/ V(2.0172)}, + // ITU-R BT.709-6 + {/*.y_shift=*/ 16, /*.y_factor=*/ V(1.1644), /*.v_r_factor=*/ V(1.7927), /*.u_g_factor=*/ -V(0.2132), /*.v_g_factor=*/ -V(0.5329), /*.u_b_factor=*/ V(2.1124)} +}; + +static const RGB2YUVParam RGB2YUV[3] = { + // ITU-T T.871 (JPEG) + {/*.y_shift=*/ 0, /*.matrix=*/ {{V(0.299), V(0.587), V(0.114)}, {-V(0.1687), -V(0.3313), V(0.5)}, {V(0.5), -V(0.4187), -V(0.0813)}}}, + // ITU-R BT.601-7 + {/*.y_shift=*/ 16, /*.matrix=*/ {{V(0.2568), V(0.5041), V(0.0979)}, {-V(0.1482), -V(0.291), V(0.4392)}, {V(0.4392), -V(0.3678), -V(0.0714)}}}, + // ITU-R BT.709-6 + {/*.y_shift=*/ 16, /*.matrix=*/ {{V(0.1826), V(0.6142), V(0.062)}, {-V(0.1006), -V(0.3386), V(0.4392)}, {V(0.4392), -V(0.3989), -V(0.0403)}}} +}; + +/* The various layouts of YUV data we support */ +#define YUV_FORMAT_420 1 +#define YUV_FORMAT_422 2 +#define YUV_FORMAT_NV12 3 + +/* The various formats of RGB pixel that we support */ +#define RGB_FORMAT_RGB565 1 +#define RGB_FORMAT_RGB24 2 +#define RGB_FORMAT_RGBA 3 +#define RGB_FORMAT_BGRA 4 +#define RGB_FORMAT_ARGB 5 +#define RGB_FORMAT_ABGR 6 + +// divide by PRECISION_FACTOR and clamp to [0:255] interval +// input must be in the [-128*PRECISION_FACTOR:384*PRECISION_FACTOR] range +static uint8_t clampU8(int32_t v) +{ + static const uint8_t lut[512] = + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46, + 47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90, + 91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, + 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158, + 159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224, + 225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 + }; + return lut[(v+128*PRECISION_FACTOR)>>PRECISION]; +} + + +#define STD_FUNCTION_NAME yuv420_rgb565_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_RGB565 +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuv420_rgb24_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_RGB24 +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuv420_rgba_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_RGBA +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuv420_bgra_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_BGRA +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuv420_argb_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_ARGB +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuv420_abgr_std +#define YUV_FORMAT YUV_FORMAT_420 +#define RGB_FORMAT RGB_FORMAT_ABGR +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuv422_rgb565_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_RGB565 +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuv422_rgb24_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_RGB24 +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuv422_rgba_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_RGBA +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuv422_bgra_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_BGRA +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuv422_argb_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_ARGB +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuv422_abgr_std +#define YUV_FORMAT YUV_FORMAT_422 +#define RGB_FORMAT RGB_FORMAT_ABGR +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuvnv12_rgb565_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_RGB565 +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuvnv12_rgb24_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_RGB24 +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuvnv12_rgba_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_RGBA +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuvnv12_bgra_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_BGRA +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuvnv12_argb_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_ARGB +#include "yuv_rgb_std_func.h" + +#define STD_FUNCTION_NAME yuvnv12_abgr_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_ABGR +#include "yuv_rgb_std_func.h" + +void rgb24_yuv420_std( + uint32_t width, uint32_t height, + const uint8_t *RGB, uint32_t RGB_stride, + uint8_t *Y, uint8_t *U, uint8_t *V, uint32_t Y_stride, uint32_t UV_stride, + YCbCrType yuv_type) +{ + const RGB2YUVParam *const param = &(RGB2YUV[yuv_type]); + + uint32_t x, y; + for(y=0; y<(height-1); y+=2) + { + const uint8_t *rgb_ptr1=RGB+y*RGB_stride, + *rgb_ptr2=RGB+(y+1)*RGB_stride; + + uint8_t *y_ptr1=Y+y*Y_stride, + *y_ptr2=Y+(y+1)*Y_stride, + *u_ptr=U+(y/2)*UV_stride, + *v_ptr=V+(y/2)*UV_stride; + + for(x=0; x<(width-1); x+=2) + { + // compute yuv for the four pixels, u and v values are summed + int32_t y_tmp, u_tmp, v_tmp; + + y_tmp = param->matrix[0][0]*rgb_ptr1[0] + param->matrix[0][1]*rgb_ptr1[1] + param->matrix[0][2]*rgb_ptr1[2]; + u_tmp = param->matrix[1][0]*rgb_ptr1[0] + param->matrix[1][1]*rgb_ptr1[1] + param->matrix[1][2]*rgb_ptr1[2]; + v_tmp = param->matrix[2][0]*rgb_ptr1[0] + param->matrix[2][1]*rgb_ptr1[1] + param->matrix[2][2]*rgb_ptr1[2]; + y_ptr1[0]=clampU8(y_tmp+((param->y_shift)<matrix[0][0]*rgb_ptr1[3] + param->matrix[0][1]*rgb_ptr1[4] + param->matrix[0][2]*rgb_ptr1[5]; + u_tmp += param->matrix[1][0]*rgb_ptr1[3] + param->matrix[1][1]*rgb_ptr1[4] + param->matrix[1][2]*rgb_ptr1[5]; + v_tmp += param->matrix[2][0]*rgb_ptr1[3] + param->matrix[2][1]*rgb_ptr1[4] + param->matrix[2][2]*rgb_ptr1[5]; + y_ptr1[1]=clampU8(y_tmp+((param->y_shift)<matrix[0][0]*rgb_ptr2[0] + param->matrix[0][1]*rgb_ptr2[1] + param->matrix[0][2]*rgb_ptr2[2]; + u_tmp += param->matrix[1][0]*rgb_ptr2[0] + param->matrix[1][1]*rgb_ptr2[1] + param->matrix[1][2]*rgb_ptr2[2]; + v_tmp += param->matrix[2][0]*rgb_ptr2[0] + param->matrix[2][1]*rgb_ptr2[1] + param->matrix[2][2]*rgb_ptr2[2]; + y_ptr2[0]=clampU8(y_tmp+((param->y_shift)<matrix[0][0]*rgb_ptr2[3] + param->matrix[0][1]*rgb_ptr2[4] + param->matrix[0][2]*rgb_ptr2[5]; + u_tmp += param->matrix[1][0]*rgb_ptr2[3] + param->matrix[1][1]*rgb_ptr2[4] + param->matrix[1][2]*rgb_ptr2[5]; + v_tmp += param->matrix[2][0]*rgb_ptr2[3] + param->matrix[2][1]*rgb_ptr2[4] + param->matrix[2][2]*rgb_ptr2[5]; + y_ptr2[1]=clampU8(y_tmp+((param->y_shift)<matrix[0][0])), \ + _mm_mullo_epi16(G, _mm_set1_epi16(param->matrix[0][1]))); \ +Y = _mm_add_epi16(Y, _mm_mullo_epi16(B, _mm_set1_epi16(param->matrix[0][2]))); \ +Y = _mm_add_epi16(Y, _mm_set1_epi16((param->y_shift)<matrix[1][0])), \ + _mm_mullo_epi16(G, _mm_set1_epi16(param->matrix[1][1]))); \ +U = _mm_add_epi16(U, _mm_mullo_epi16(B, _mm_set1_epi16(param->matrix[1][2]))); \ +U = _mm_add_epi16(U, _mm_set1_epi16(128<matrix[2][0])), \ + _mm_mullo_epi16(G, _mm_set1_epi16(param->matrix[2][1]))); \ +V = _mm_add_epi16(V, _mm_mullo_epi16(B, _mm_set1_epi16(param->matrix[2][2]))); \ +V = _mm_add_epi16(V, _mm_set1_epi16(128< + +typedef enum +{ + YCBCR_JPEG, + YCBCR_601, + YCBCR_709 +} YCbCrType; + +// yuv to rgb, standard c implementation +void yuv420_rgb565_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_rgb24_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_rgba_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_bgra_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_argb_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_abgr_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_rgb565_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_rgb24_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_rgba_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_bgra_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_argb_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_abgr_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_rgb565_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_rgb24_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_rgba_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_bgra_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_argb_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_abgr_std( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +// yuv to rgb, sse implementation +// pointers must be 16 byte aligned, and strides must be divisable by 16 +void yuv420_rgb565_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_rgb24_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_rgba_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_bgra_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_argb_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_abgr_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_rgb565_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_rgb24_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_rgba_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_bgra_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_argb_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_abgr_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_rgb565_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_rgb24_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_rgba_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_bgra_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_argb_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_abgr_sse( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +// yuv to rgb, sse implementation +// pointers do not need to be 16 byte aligned +void yuv420_rgb565_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_rgb24_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_rgba_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_bgra_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_argb_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv420_abgr_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_rgb565_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_rgb24_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_rgba_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_bgra_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_argb_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuv422_abgr_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_rgb565_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_rgb24_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_rgba_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_bgra_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_argb_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + +void yuvnv12_abgr_sseu( + uint32_t width, uint32_t height, + const uint8_t *y, const uint8_t *u, const uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + + +// rgb to yuv, standard c implementation +void rgb24_yuv420_std( + uint32_t width, uint32_t height, + const uint8_t *rgb, uint32_t rgb_stride, + uint8_t *y, uint8_t *u, uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + YCbCrType yuv_type); + +// rgb to yuv, sse implementation +// pointers must be 16 byte aligned, and strides must be divisible by 16 +void rgb24_yuv420_sse( + uint32_t width, uint32_t height, + const uint8_t *rgb, uint32_t rgb_stride, + uint8_t *y, uint8_t *u, uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + YCbCrType yuv_type); + +// rgb to yuv, sse implementation +// pointers do not need to be 16 byte aligned +void rgb24_yuv420_sseu( + uint32_t width, uint32_t height, + const uint8_t *rgb, uint32_t rgb_stride, + uint8_t *y, uint8_t *u, uint8_t *v, uint32_t y_stride, uint32_t uv_stride, + YCbCrType yuv_type); + diff --git a/src/video/yuv2rgb/yuv_rgb_sse_func.h b/src/video/yuv2rgb/yuv_rgb_sse_func.h new file mode 100644 index 0000000000000..0fe28a8ac53e3 --- /dev/null +++ b/src/video/yuv2rgb/yuv_rgb_sse_func.h @@ -0,0 +1,498 @@ +// Copyright 2016 Adrien Descamps +// Distributed under BSD 3-Clause License + +/* You need to define the following macros before including this file: + SSE_FUNCTION_NAME + STD_FUNCTION_NAME + YUV_FORMAT + RGB_FORMAT +*/ +/* You may define the following macro, which affects generated code: + SSE_ALIGNED +*/ + +#ifdef SSE_ALIGNED +/* Unaligned instructions seem faster, even on aligned data? */ +/* +#define LOAD_SI128 _mm_load_si128 +#define SAVE_SI128 _mm_stream_si128 +*/ +#define LOAD_SI128 _mm_loadu_si128 +#define SAVE_SI128 _mm_storeu_si128 +#else +#define LOAD_SI128 _mm_loadu_si128 +#define SAVE_SI128 _mm_storeu_si128 +#endif + +#define UV2RGB_16(U,V,R1,G1,B1,R2,G2,B2) \ + r_tmp = _mm_mullo_epi16(V, _mm_set1_epi16(param->v_r_factor)); \ + g_tmp = _mm_add_epi16( \ + _mm_mullo_epi16(U, _mm_set1_epi16(param->u_g_factor)), \ + _mm_mullo_epi16(V, _mm_set1_epi16(param->v_g_factor))); \ + b_tmp = _mm_mullo_epi16(U, _mm_set1_epi16(param->u_b_factor)); \ + R1 = _mm_unpacklo_epi16(r_tmp, r_tmp); \ + G1 = _mm_unpacklo_epi16(g_tmp, g_tmp); \ + B1 = _mm_unpacklo_epi16(b_tmp, b_tmp); \ + R2 = _mm_unpackhi_epi16(r_tmp, r_tmp); \ + G2 = _mm_unpackhi_epi16(g_tmp, g_tmp); \ + B2 = _mm_unpackhi_epi16(b_tmp, b_tmp); \ + +#define ADD_Y2RGB_16(Y1,Y2,R1,G1,B1,R2,G2,B2) \ + Y1 = _mm_mullo_epi16(_mm_sub_epi16(Y1, _mm_set1_epi16(param->y_shift)), _mm_set1_epi16(param->y_factor)); \ + Y2 = _mm_mullo_epi16(_mm_sub_epi16(Y2, _mm_set1_epi16(param->y_shift)), _mm_set1_epi16(param->y_factor)); \ + \ + R1 = _mm_srai_epi16(_mm_add_epi16(R1, Y1), PRECISION); \ + G1 = _mm_srai_epi16(_mm_add_epi16(G1, Y1), PRECISION); \ + B1 = _mm_srai_epi16(_mm_add_epi16(B1, Y1), PRECISION); \ + R2 = _mm_srai_epi16(_mm_add_epi16(R2, Y2), PRECISION); \ + G2 = _mm_srai_epi16(_mm_add_epi16(G2, Y2), PRECISION); \ + B2 = _mm_srai_epi16(_mm_add_epi16(B2, Y2), PRECISION); \ + +#define PACK_RGB565_32(R1, R2, G1, G2, B1, B2, RGB1, RGB2, RGB3, RGB4) \ +{ \ + __m128i red_mask, tmp1, tmp2, tmp3, tmp4; \ +\ + red_mask = _mm_set1_epi16(0xF800); \ + RGB1 = _mm_and_si128(_mm_unpacklo_epi8(_mm_setzero_si128(), R1), red_mask); \ + RGB2 = _mm_and_si128(_mm_unpackhi_epi8(_mm_setzero_si128(), R1), red_mask); \ + RGB3 = _mm_and_si128(_mm_unpacklo_epi8(_mm_setzero_si128(), R2), red_mask); \ + RGB4 = _mm_and_si128(_mm_unpackhi_epi8(_mm_setzero_si128(), R2), red_mask); \ + tmp1 = _mm_slli_epi16(_mm_srli_epi16(_mm_unpacklo_epi8(G1, _mm_setzero_si128()), 2), 5); \ + tmp2 = _mm_slli_epi16(_mm_srli_epi16(_mm_unpackhi_epi8(G1, _mm_setzero_si128()), 2), 5); \ + tmp3 = _mm_slli_epi16(_mm_srli_epi16(_mm_unpacklo_epi8(G2, _mm_setzero_si128()), 2), 5); \ + tmp4 = _mm_slli_epi16(_mm_srli_epi16(_mm_unpackhi_epi8(G2, _mm_setzero_si128()), 2), 5); \ + RGB1 = _mm_or_si128(RGB1, tmp1); \ + RGB2 = _mm_or_si128(RGB2, tmp2); \ + RGB3 = _mm_or_si128(RGB3, tmp3); \ + RGB4 = _mm_or_si128(RGB4, tmp4); \ + tmp1 = _mm_srli_epi16(_mm_unpacklo_epi8(B1, _mm_setzero_si128()), 3); \ + tmp2 = _mm_srli_epi16(_mm_unpackhi_epi8(B1, _mm_setzero_si128()), 3); \ + tmp3 = _mm_srli_epi16(_mm_unpacklo_epi8(B2, _mm_setzero_si128()), 3); \ + tmp4 = _mm_srli_epi16(_mm_unpackhi_epi8(B2, _mm_setzero_si128()), 3); \ + RGB1 = _mm_or_si128(RGB1, tmp1); \ + RGB2 = _mm_or_si128(RGB2, tmp2); \ + RGB3 = _mm_or_si128(RGB3, tmp3); \ + RGB4 = _mm_or_si128(RGB4, tmp4); \ +} + +#define PACK_RGB24_32_STEP1(R1, R2, G1, G2, B1, B2, RGB1, RGB2, RGB3, RGB4, RGB5, RGB6) \ +RGB1 = _mm_packus_epi16(_mm_and_si128(R1,_mm_set1_epi16(0xFF)), _mm_and_si128(R2,_mm_set1_epi16(0xFF))); \ +RGB2 = _mm_packus_epi16(_mm_and_si128(G1,_mm_set1_epi16(0xFF)), _mm_and_si128(G2,_mm_set1_epi16(0xFF))); \ +RGB3 = _mm_packus_epi16(_mm_and_si128(B1,_mm_set1_epi16(0xFF)), _mm_and_si128(B2,_mm_set1_epi16(0xFF))); \ +RGB4 = _mm_packus_epi16(_mm_srli_epi16(R1,8), _mm_srli_epi16(R2,8)); \ +RGB5 = _mm_packus_epi16(_mm_srli_epi16(G1,8), _mm_srli_epi16(G2,8)); \ +RGB6 = _mm_packus_epi16(_mm_srli_epi16(B1,8), _mm_srli_epi16(B2,8)); \ + +#define PACK_RGB24_32_STEP2(R1, R2, G1, G2, B1, B2, RGB1, RGB2, RGB3, RGB4, RGB5, RGB6) \ +R1 = _mm_packus_epi16(_mm_and_si128(RGB1,_mm_set1_epi16(0xFF)), _mm_and_si128(RGB2,_mm_set1_epi16(0xFF))); \ +R2 = _mm_packus_epi16(_mm_and_si128(RGB3,_mm_set1_epi16(0xFF)), _mm_and_si128(RGB4,_mm_set1_epi16(0xFF))); \ +G1 = _mm_packus_epi16(_mm_and_si128(RGB5,_mm_set1_epi16(0xFF)), _mm_and_si128(RGB6,_mm_set1_epi16(0xFF))); \ +G2 = _mm_packus_epi16(_mm_srli_epi16(RGB1,8), _mm_srli_epi16(RGB2,8)); \ +B1 = _mm_packus_epi16(_mm_srli_epi16(RGB3,8), _mm_srli_epi16(RGB4,8)); \ +B2 = _mm_packus_epi16(_mm_srli_epi16(RGB5,8), _mm_srli_epi16(RGB6,8)); \ + +#define PACK_RGB24_32(R1, R2, G1, G2, B1, B2, RGB1, RGB2, RGB3, RGB4, RGB5, RGB6) \ +PACK_RGB24_32_STEP1(R1, R2, G1, G2, B1, B2, RGB1, RGB2, RGB3, RGB4, RGB5, RGB6) \ +PACK_RGB24_32_STEP2(R1, R2, G1, G2, B1, B2, RGB1, RGB2, RGB3, RGB4, RGB5, RGB6) \ +PACK_RGB24_32_STEP1(R1, R2, G1, G2, B1, B2, RGB1, RGB2, RGB3, RGB4, RGB5, RGB6) \ +PACK_RGB24_32_STEP2(R1, R2, G1, G2, B1, B2, RGB1, RGB2, RGB3, RGB4, RGB5, RGB6) \ +PACK_RGB24_32_STEP1(R1, R2, G1, G2, B1, B2, RGB1, RGB2, RGB3, RGB4, RGB5, RGB6) \ + +#define PACK_RGBA_32(R1, R2, G1, G2, B1, B2, A1, A2, RGB1, RGB2, RGB3, RGB4, RGB5, RGB6, RGB7, RGB8) \ +{ \ + __m128i lo_ab, hi_ab, lo_gr, hi_gr; \ +\ + lo_ab = _mm_unpacklo_epi8( A1, B1 ); \ + hi_ab = _mm_unpackhi_epi8( A1, B1 ); \ + lo_gr = _mm_unpacklo_epi8( G1, R1 ); \ + hi_gr = _mm_unpackhi_epi8( G1, R1 ); \ + RGB1 = _mm_unpacklo_epi16( lo_ab, lo_gr ); \ + RGB2 = _mm_unpackhi_epi16( lo_ab, lo_gr ); \ + RGB3 = _mm_unpacklo_epi16( hi_ab, hi_gr ); \ + RGB4 = _mm_unpackhi_epi16( hi_ab, hi_gr ); \ +\ + lo_ab = _mm_unpacklo_epi8( A2, B2 ); \ + hi_ab = _mm_unpackhi_epi8( A2, B2 ); \ + lo_gr = _mm_unpacklo_epi8( G2, R2 ); \ + hi_gr = _mm_unpackhi_epi8( G2, R2 ); \ + RGB5 = _mm_unpacklo_epi16( lo_ab, lo_gr ); \ + RGB6 = _mm_unpackhi_epi16( lo_ab, lo_gr ); \ + RGB7 = _mm_unpacklo_epi16( hi_ab, hi_gr ); \ + RGB8 = _mm_unpackhi_epi16( hi_ab, hi_gr ); \ +} + +#if RGB_FORMAT == RGB_FORMAT_RGB565 + +#define PACK_PIXEL \ + __m128i rgb_1, rgb_2, rgb_3, rgb_4, rgb_5, rgb_6, rgb_7, rgb_8; \ + \ + PACK_RGB565_32(r_8_11, r_8_12, g_8_11, g_8_12, b_8_11, b_8_12, rgb_1, rgb_2, rgb_3, rgb_4) \ + \ + PACK_RGB565_32(r_8_21, r_8_22, g_8_21, g_8_22, b_8_21, b_8_22, rgb_5, rgb_6, rgb_7, rgb_8) \ + +#elif RGB_FORMAT == RGB_FORMAT_RGB24 + +#define PACK_PIXEL \ + __m128i rgb_1, rgb_2, rgb_3, rgb_4, rgb_5, rgb_6; \ + __m128i rgb_7, rgb_8, rgb_9, rgb_10, rgb_11, rgb_12; \ + \ + PACK_RGB24_32(r_8_11, r_8_12, g_8_11, g_8_12, b_8_11, b_8_12, rgb_1, rgb_2, rgb_3, rgb_4, rgb_5, rgb_6) \ + \ + PACK_RGB24_32(r_8_21, r_8_22, g_8_21, g_8_22, b_8_21, b_8_22, rgb_7, rgb_8, rgb_9, rgb_10, rgb_11, rgb_12) \ + +#elif RGB_FORMAT == RGB_FORMAT_RGBA + +#define PACK_PIXEL \ + __m128i rgb_1, rgb_2, rgb_3, rgb_4, rgb_5, rgb_6, rgb_7, rgb_8; \ + __m128i rgb_9, rgb_10, rgb_11, rgb_12, rgb_13, rgb_14, rgb_15, rgb_16; \ + __m128i a = _mm_set1_epi8( 0xFF ); \ + \ + PACK_RGBA_32(r_8_11, r_8_12, g_8_11, g_8_12, b_8_11, b_8_12, a, a, rgb_1, rgb_2, rgb_3, rgb_4, rgb_5, rgb_6, rgb_7, rgb_8) \ + \ + PACK_RGBA_32(r_8_21, r_8_22, g_8_21, g_8_22, b_8_21, b_8_22, a, a, rgb_9, rgb_10, rgb_11, rgb_12, rgb_13, rgb_14, rgb_15, rgb_16) \ + +#elif RGB_FORMAT == RGB_FORMAT_BGRA + +#define PACK_PIXEL \ + __m128i rgb_1, rgb_2, rgb_3, rgb_4, rgb_5, rgb_6, rgb_7, rgb_8; \ + __m128i rgb_9, rgb_10, rgb_11, rgb_12, rgb_13, rgb_14, rgb_15, rgb_16; \ + __m128i a = _mm_set1_epi8( 0xFF ); \ + \ + PACK_RGBA_32(b_8_11, b_8_12, g_8_11, g_8_12, r_8_11, r_8_12, a, a, rgb_1, rgb_2, rgb_3, rgb_4, rgb_5, rgb_6, rgb_7, rgb_8) \ + \ + PACK_RGBA_32(b_8_21, b_8_22, g_8_21, g_8_22, r_8_21, r_8_22, a, a, rgb_9, rgb_10, rgb_11, rgb_12, rgb_13, rgb_14, rgb_15, rgb_16) \ + +#elif RGB_FORMAT == RGB_FORMAT_ARGB + +#define PACK_PIXEL \ + __m128i rgb_1, rgb_2, rgb_3, rgb_4, rgb_5, rgb_6, rgb_7, rgb_8; \ + __m128i rgb_9, rgb_10, rgb_11, rgb_12, rgb_13, rgb_14, rgb_15, rgb_16; \ + __m128i a = _mm_set1_epi8( 0xFF ); \ + \ + PACK_RGBA_32(a, a, r_8_11, r_8_12, g_8_11, g_8_12, b_8_11, b_8_12, rgb_1, rgb_2, rgb_3, rgb_4, rgb_5, rgb_6, rgb_7, rgb_8) \ + \ + PACK_RGBA_32(a, a, r_8_21, r_8_22, g_8_21, g_8_22, b_8_21, b_8_22, rgb_9, rgb_10, rgb_11, rgb_12, rgb_13, rgb_14, rgb_15, rgb_16) \ + +#elif RGB_FORMAT == RGB_FORMAT_ABGR + +#define PACK_PIXEL \ + __m128i rgb_1, rgb_2, rgb_3, rgb_4, rgb_5, rgb_6, rgb_7, rgb_8; \ + __m128i rgb_9, rgb_10, rgb_11, rgb_12, rgb_13, rgb_14, rgb_15, rgb_16; \ + __m128i a = _mm_set1_epi8( 0xFF ); \ + \ + PACK_RGBA_32(a, a, b_8_11, b_8_12, g_8_11, g_8_12, r_8_11, r_8_12, rgb_1, rgb_2, rgb_3, rgb_4, rgb_5, rgb_6, rgb_7, rgb_8) \ + \ + PACK_RGBA_32(a, a, b_8_21, b_8_22, g_8_21, g_8_22, r_8_21, r_8_22, rgb_9, rgb_10, rgb_11, rgb_12, rgb_13, rgb_14, rgb_15, rgb_16) \ + +#else +#error PACK_PIXEL unimplemented +#endif + +#if RGB_FORMAT == RGB_FORMAT_RGB565 + +#define SAVE_LINE1 \ + SAVE_SI128((__m128i*)(rgb_ptr1), rgb_1); \ + SAVE_SI128((__m128i*)(rgb_ptr1+16), rgb_2); \ + SAVE_SI128((__m128i*)(rgb_ptr1+32), rgb_3); \ + SAVE_SI128((__m128i*)(rgb_ptr1+48), rgb_4); \ + +#define SAVE_LINE2 \ + SAVE_SI128((__m128i*)(rgb_ptr2), rgb_5); \ + SAVE_SI128((__m128i*)(rgb_ptr2+16), rgb_6); \ + SAVE_SI128((__m128i*)(rgb_ptr2+32), rgb_7); \ + SAVE_SI128((__m128i*)(rgb_ptr2+48), rgb_8); \ + +#elif RGB_FORMAT == RGB_FORMAT_RGB24 + +#define SAVE_LINE1 \ + SAVE_SI128((__m128i*)(rgb_ptr1), rgb_1); \ + SAVE_SI128((__m128i*)(rgb_ptr1+16), rgb_2); \ + SAVE_SI128((__m128i*)(rgb_ptr1+32), rgb_3); \ + SAVE_SI128((__m128i*)(rgb_ptr1+48), rgb_4); \ + SAVE_SI128((__m128i*)(rgb_ptr1+64), rgb_5); \ + SAVE_SI128((__m128i*)(rgb_ptr1+80), rgb_6); \ + +#define SAVE_LINE2 \ + SAVE_SI128((__m128i*)(rgb_ptr2), rgb_7); \ + SAVE_SI128((__m128i*)(rgb_ptr2+16), rgb_8); \ + SAVE_SI128((__m128i*)(rgb_ptr2+32), rgb_9); \ + SAVE_SI128((__m128i*)(rgb_ptr2+48), rgb_10); \ + SAVE_SI128((__m128i*)(rgb_ptr2+64), rgb_11); \ + SAVE_SI128((__m128i*)(rgb_ptr2+80), rgb_12); \ + +#elif RGB_FORMAT == RGB_FORMAT_RGBA || RGB_FORMAT == RGB_FORMAT_BGRA || \ + RGB_FORMAT == RGB_FORMAT_ARGB || RGB_FORMAT == RGB_FORMAT_ABGR + +#define SAVE_LINE1 \ + SAVE_SI128((__m128i*)(rgb_ptr1), rgb_1); \ + SAVE_SI128((__m128i*)(rgb_ptr1+16), rgb_2); \ + SAVE_SI128((__m128i*)(rgb_ptr1+32), rgb_3); \ + SAVE_SI128((__m128i*)(rgb_ptr1+48), rgb_4); \ + SAVE_SI128((__m128i*)(rgb_ptr1+64), rgb_5); \ + SAVE_SI128((__m128i*)(rgb_ptr1+80), rgb_6); \ + SAVE_SI128((__m128i*)(rgb_ptr1+96), rgb_7); \ + SAVE_SI128((__m128i*)(rgb_ptr1+112), rgb_8); \ + +#define SAVE_LINE2 \ + SAVE_SI128((__m128i*)(rgb_ptr2), rgb_9); \ + SAVE_SI128((__m128i*)(rgb_ptr2+16), rgb_10); \ + SAVE_SI128((__m128i*)(rgb_ptr2+32), rgb_11); \ + SAVE_SI128((__m128i*)(rgb_ptr2+48), rgb_12); \ + SAVE_SI128((__m128i*)(rgb_ptr2+64), rgb_13); \ + SAVE_SI128((__m128i*)(rgb_ptr2+80), rgb_14); \ + SAVE_SI128((__m128i*)(rgb_ptr2+96), rgb_15); \ + SAVE_SI128((__m128i*)(rgb_ptr2+112), rgb_16); \ + +#else +#error SAVE_LINE unimplemented +#endif + +#if YUV_FORMAT == YUV_FORMAT_420 + +#define READ_Y(y_ptr) \ + y = LOAD_SI128((const __m128i*)(y_ptr)); \ + +#define READ_UV \ + u = LOAD_SI128((const __m128i*)(u_ptr)); \ + v = LOAD_SI128((const __m128i*)(v_ptr)); \ + +#elif YUV_FORMAT == YUV_FORMAT_422 + +#define READ_Y(y_ptr) \ +{ \ + __m128i y1, y2; \ + y1 = _mm_srli_epi16(_mm_slli_epi16(LOAD_SI128((const __m128i*)(y_ptr)), 8), 8); \ + y2 = _mm_srli_epi16(_mm_slli_epi16(LOAD_SI128((const __m128i*)(y_ptr+16)), 8), 8); \ + y = _mm_packus_epi16(y1, y2); \ +} + +#define READ_UV \ +{ \ + __m128i u1, u2, u3, u4, v1, v2, v3, v4; \ + u1 = _mm_srli_epi32(_mm_slli_epi32(LOAD_SI128((const __m128i*)(u_ptr)), 24), 24); \ + u2 = _mm_srli_epi32(_mm_slli_epi32(LOAD_SI128((const __m128i*)(u_ptr+16)), 24), 24); \ + u3 = _mm_srli_epi32(_mm_slli_epi32(LOAD_SI128((const __m128i*)(u_ptr+32)), 24), 24); \ + u4 = _mm_srli_epi32(_mm_slli_epi32(LOAD_SI128((const __m128i*)(u_ptr+48)), 24), 24); \ + u = _mm_packus_epi16(_mm_packs_epi32(u1, u2), _mm_packs_epi32(u3, u4)); \ + v1 = _mm_srli_epi32(_mm_slli_epi32(LOAD_SI128((const __m128i*)(v_ptr)), 24), 24); \ + v2 = _mm_srli_epi32(_mm_slli_epi32(LOAD_SI128((const __m128i*)(v_ptr+16)), 24), 24); \ + v3 = _mm_srli_epi32(_mm_slli_epi32(LOAD_SI128((const __m128i*)(v_ptr+32)), 24), 24); \ + v4 = _mm_srli_epi32(_mm_slli_epi32(LOAD_SI128((const __m128i*)(v_ptr+48)), 24), 24); \ + v = _mm_packus_epi16(_mm_packs_epi32(v1, v2), _mm_packs_epi32(v3, v4)); \ +} + +#elif YUV_FORMAT == YUV_FORMAT_NV12 + +#define READ_Y(y_ptr) \ + y = LOAD_SI128((const __m128i*)(y_ptr)); \ + +#define READ_UV \ +{ \ + __m128i u1, u2, v1, v2; \ + u1 = _mm_srli_epi16(_mm_slli_epi16(LOAD_SI128((const __m128i*)(u_ptr)), 8), 8); \ + u2 = _mm_srli_epi16(_mm_slli_epi16(LOAD_SI128((const __m128i*)(u_ptr+16)), 8), 8); \ + u = _mm_packus_epi16(u1, u2); \ + v1 = _mm_srli_epi16(_mm_slli_epi16(LOAD_SI128((const __m128i*)(v_ptr)), 8), 8); \ + v2 = _mm_srli_epi16(_mm_slli_epi16(LOAD_SI128((const __m128i*)(v_ptr+16)), 8), 8); \ + v = _mm_packus_epi16(v1, v2); \ +} + +#else +#error READ_UV unimplemented +#endif + +#define YUV2RGB_32 \ + __m128i r_tmp, g_tmp, b_tmp; \ + __m128i r_16_1, g_16_1, b_16_1, r_16_2, g_16_2, b_16_2; \ + __m128i r_uv_16_1, g_uv_16_1, b_uv_16_1, r_uv_16_2, g_uv_16_2, b_uv_16_2; \ + __m128i y_16_1, y_16_2; \ + __m128i y, u, v, u_16, v_16; \ + __m128i r_8_11, g_8_11, b_8_11, r_8_21, g_8_21, b_8_21; \ + __m128i r_8_12, g_8_12, b_8_12, r_8_22, g_8_22, b_8_22; \ + \ + READ_UV \ + \ + /* process first 16 pixels of first line */\ + u_16 = _mm_unpacklo_epi8(u, _mm_setzero_si128()); \ + v_16 = _mm_unpacklo_epi8(v, _mm_setzero_si128()); \ + u_16 = _mm_add_epi16(u_16, _mm_set1_epi16(-128)); \ + v_16 = _mm_add_epi16(v_16, _mm_set1_epi16(-128)); \ + \ + UV2RGB_16(u_16, v_16, r_16_1, g_16_1, b_16_1, r_16_2, g_16_2, b_16_2) \ + r_uv_16_1=r_16_1; g_uv_16_1=g_16_1; b_uv_16_1=b_16_1; \ + r_uv_16_2=r_16_2; g_uv_16_2=g_16_2; b_uv_16_2=b_16_2; \ + \ + READ_Y(y_ptr1) \ + y_16_1 = _mm_unpacklo_epi8(y, _mm_setzero_si128()); \ + y_16_2 = _mm_unpackhi_epi8(y, _mm_setzero_si128()); \ + \ + ADD_Y2RGB_16(y_16_1, y_16_2, r_16_1, g_16_1, b_16_1, r_16_2, g_16_2, b_16_2) \ + \ + r_8_11 = _mm_packus_epi16(r_16_1, r_16_2); \ + g_8_11 = _mm_packus_epi16(g_16_1, g_16_2); \ + b_8_11 = _mm_packus_epi16(b_16_1, b_16_2); \ + \ + /* process first 16 pixels of second line */\ + r_16_1=r_uv_16_1; g_16_1=g_uv_16_1; b_16_1=b_uv_16_1; \ + r_16_2=r_uv_16_2; g_16_2=g_uv_16_2; b_16_2=b_uv_16_2; \ + \ + READ_Y(y_ptr2) \ + y_16_1 = _mm_unpacklo_epi8(y, _mm_setzero_si128()); \ + y_16_2 = _mm_unpackhi_epi8(y, _mm_setzero_si128()); \ + \ + ADD_Y2RGB_16(y_16_1, y_16_2, r_16_1, g_16_1, b_16_1, r_16_2, g_16_2, b_16_2) \ + \ + r_8_21 = _mm_packus_epi16(r_16_1, r_16_2); \ + g_8_21 = _mm_packus_epi16(g_16_1, g_16_2); \ + b_8_21 = _mm_packus_epi16(b_16_1, b_16_2); \ + \ + /* process last 16 pixels of first line */\ + u_16 = _mm_unpackhi_epi8(u, _mm_setzero_si128()); \ + v_16 = _mm_unpackhi_epi8(v, _mm_setzero_si128()); \ + u_16 = _mm_add_epi16(u_16, _mm_set1_epi16(-128)); \ + v_16 = _mm_add_epi16(v_16, _mm_set1_epi16(-128)); \ + \ + UV2RGB_16(u_16, v_16, r_16_1, g_16_1, b_16_1, r_16_2, g_16_2, b_16_2) \ + r_uv_16_1=r_16_1; g_uv_16_1=g_16_1; b_uv_16_1=b_16_1; \ + r_uv_16_2=r_16_2; g_uv_16_2=g_16_2; b_uv_16_2=b_16_2; \ + \ + READ_Y(y_ptr1+16*y_pixel_stride) \ + y_16_1 = _mm_unpacklo_epi8(y, _mm_setzero_si128()); \ + y_16_2 = _mm_unpackhi_epi8(y, _mm_setzero_si128()); \ + \ + ADD_Y2RGB_16(y_16_1, y_16_2, r_16_1, g_16_1, b_16_1, r_16_2, g_16_2, b_16_2) \ + \ + r_8_12 = _mm_packus_epi16(r_16_1, r_16_2); \ + g_8_12 = _mm_packus_epi16(g_16_1, g_16_2); \ + b_8_12 = _mm_packus_epi16(b_16_1, b_16_2); \ + \ + /* process last 16 pixels of second line */\ + r_16_1=r_uv_16_1; g_16_1=g_uv_16_1; b_16_1=b_uv_16_1; \ + r_16_2=r_uv_16_2; g_16_2=g_uv_16_2; b_16_2=b_uv_16_2; \ + \ + READ_Y(y_ptr2+16*y_pixel_stride) \ + y_16_1 = _mm_unpacklo_epi8(y, _mm_setzero_si128()); \ + y_16_2 = _mm_unpackhi_epi8(y, _mm_setzero_si128()); \ + \ + ADD_Y2RGB_16(y_16_1, y_16_2, r_16_1, g_16_1, b_16_1, r_16_2, g_16_2, b_16_2) \ + \ + r_8_22 = _mm_packus_epi16(r_16_1, r_16_2); \ + g_8_22 = _mm_packus_epi16(g_16_1, g_16_2); \ + b_8_22 = _mm_packus_epi16(b_16_1, b_16_2); \ + \ + + +void SSE_FUNCTION_NAME(uint32_t width, uint32_t height, + const uint8_t *Y, const uint8_t *U, const uint8_t *V, uint32_t Y_stride, uint32_t UV_stride, + uint8_t *RGB, uint32_t RGB_stride, + YCbCrType yuv_type) +{ + const YUV2RGBParam *const param = &(YUV2RGB[yuv_type]); +#if YUV_FORMAT == YUV_FORMAT_420 + const int y_pixel_stride = 1; + const int uv_pixel_stride = 1; + const int uv_x_sample_interval = 2; + const int uv_y_sample_interval = 2; +#elif YUV_FORMAT == YUV_FORMAT_422 + const int y_pixel_stride = 2; + const int uv_pixel_stride = 4; + const int uv_x_sample_interval = 2; + const int uv_y_sample_interval = 1; +#elif YUV_FORMAT == YUV_FORMAT_NV12 + const int y_pixel_stride = 1; + const int uv_pixel_stride = 2; + const int uv_x_sample_interval = 2; + const int uv_y_sample_interval = 2; +#endif +#if RGB_FORMAT == RGB_FORMAT_RGB565 + const int rgb_pixel_stride = 2; +#elif RGB_FORMAT == RGB_FORMAT_RGB24 + const int rgb_pixel_stride = 3; +#elif RGB_FORMAT == RGB_FORMAT_RGBA || RGB_FORMAT == RGB_FORMAT_BGRA || \ + RGB_FORMAT == RGB_FORMAT_ARGB || RGB_FORMAT == RGB_FORMAT_ABGR + const int rgb_pixel_stride = 4; +#else +#error Unknown RGB pixel size +#endif + + if (width >= 32) { + uint32_t x, y; + for(y=0; y<(height-(uv_y_sample_interval-1)); y+=uv_y_sample_interval) + { + const uint8_t *y_ptr1=Y+y*Y_stride, + *y_ptr2=Y+(y+1)*Y_stride, + *u_ptr=U+(y/uv_y_sample_interval)*UV_stride, + *v_ptr=V+(y/uv_y_sample_interval)*UV_stride; + + uint8_t *rgb_ptr1=RGB+y*RGB_stride, + *rgb_ptr2=RGB+(y+1)*RGB_stride; + + for(x=0; x<(width-31); x+=32) + { + YUV2RGB_32 + { + PACK_PIXEL + SAVE_LINE1 + if (uv_y_sample_interval > 1) + { + SAVE_LINE2 + } + } + + y_ptr1+=32*y_pixel_stride; + y_ptr2+=32*y_pixel_stride; + u_ptr+=32*uv_pixel_stride/uv_x_sample_interval; + v_ptr+=32*uv_pixel_stride/uv_x_sample_interval; + rgb_ptr1+=32*rgb_pixel_stride; + rgb_ptr2+=32*rgb_pixel_stride; + } + } + + /* Catch the last line, if needed */ + if (uv_y_sample_interval == 2 && y == (height-1)) + { + const uint8_t *y_ptr=Y+y*Y_stride, + *u_ptr=U+(y/uv_y_sample_interval)*UV_stride, + *v_ptr=V+(y/uv_y_sample_interval)*UV_stride; + + uint8_t *rgb_ptr=RGB+y*RGB_stride; + + STD_FUNCTION_NAME(width, 1, y_ptr, u_ptr, v_ptr, Y_stride, UV_stride, rgb_ptr, RGB_stride, yuv_type); + } + } + + /* Catch the right column, if needed */ + { + int converted = (width & ~31); + if (converted != width) + { + const uint8_t *y_ptr=Y+converted*y_pixel_stride, + *u_ptr=U+converted*uv_pixel_stride/uv_x_sample_interval, + *v_ptr=V+converted*uv_pixel_stride/uv_x_sample_interval; + + uint8_t *rgb_ptr=RGB+converted*rgb_pixel_stride; + + STD_FUNCTION_NAME(width-converted, height, y_ptr, u_ptr, v_ptr, Y_stride, UV_stride, rgb_ptr, RGB_stride, yuv_type); + } + } +} + +#undef SSE_FUNCTION_NAME +#undef STD_FUNCTION_NAME +#undef YUV_FORMAT +#undef RGB_FORMAT +#undef SSE_ALIGNED +#undef LOAD_SI128 +#undef SAVE_SI128 +#undef UV2RGB_16 +#undef ADD_Y2RGB_16 +#undef PACK_RGB24_32_STEP1 +#undef PACK_RGB24_32_STEP2 +#undef PACK_RGB24_32 +#undef PACK_RGBA_32 +#undef PACK_PIXEL +#undef SAVE_LINE1 +#undef SAVE_LINE2 +#undef READ_Y +#undef READ_UV +#undef YUV2RGB_32 diff --git a/src/video/yuv2rgb/yuv_rgb_std_func.h b/src/video/yuv2rgb/yuv_rgb_std_func.h new file mode 100644 index 0000000000000..bf4f48eaba093 --- /dev/null +++ b/src/video/yuv2rgb/yuv_rgb_std_func.h @@ -0,0 +1,220 @@ +// Copyright 2016 Adrien Descamps +// Distributed under BSD 3-Clause License + +/* You need to define the following macros before including this file: + STD_FUNCTION_NAME + YUV_FORMAT + RGB_FORMAT +*/ + +#if RGB_FORMAT == RGB_FORMAT_RGB565 + +#define PACK_PIXEL(rgb_ptr) \ + *(Uint16 *)rgb_ptr = \ + ((((Uint16)clampU8(y_tmp+r_tmp)) << 8 ) & 0xF800) | \ + ((((Uint16)clampU8(y_tmp+g_tmp)) << 3) & 0x07E0) | \ + (((Uint16)clampU8(y_tmp+b_tmp)) >> 3); \ + rgb_ptr += 2; \ + +#elif RGB_FORMAT == RGB_FORMAT_RGB24 + +#define PACK_PIXEL(rgb_ptr) \ + rgb_ptr[0] = clampU8(y_tmp+r_tmp); \ + rgb_ptr[1] = clampU8(y_tmp+g_tmp); \ + rgb_ptr[2] = clampU8(y_tmp+b_tmp); \ + rgb_ptr += 3; \ + +#elif RGB_FORMAT == RGB_FORMAT_RGBA + +#define PACK_PIXEL(rgb_ptr) \ + *(Uint32 *)rgb_ptr = \ + (((Uint32)clampU8(y_tmp+r_tmp)) << 24) | \ + (((Uint32)clampU8(y_tmp+g_tmp)) << 16) | \ + (((Uint32)clampU8(y_tmp+b_tmp)) << 8) | \ + 0x000000FF; \ + rgb_ptr += 4; \ + +#elif RGB_FORMAT == RGB_FORMAT_BGRA + +#define PACK_PIXEL(rgb_ptr) \ + *(Uint32 *)rgb_ptr = \ + (((Uint32)clampU8(y_tmp+b_tmp)) << 24) | \ + (((Uint32)clampU8(y_tmp+g_tmp)) << 16) | \ + (((Uint32)clampU8(y_tmp+r_tmp)) << 8) | \ + 0x000000FF; \ + rgb_ptr += 4; \ + +#elif RGB_FORMAT == RGB_FORMAT_ARGB + +#define PACK_PIXEL(rgb_ptr) \ + *(Uint32 *)rgb_ptr = \ + 0xFF000000 | \ + (((Uint32)clampU8(y_tmp+r_tmp)) << 16) | \ + (((Uint32)clampU8(y_tmp+g_tmp)) << 8) | \ + (((Uint32)clampU8(y_tmp+b_tmp)) << 0); \ + rgb_ptr += 4; \ + +#elif RGB_FORMAT == RGB_FORMAT_ABGR + +#define PACK_PIXEL(rgb_ptr) \ + *(Uint32 *)rgb_ptr = \ + 0xFF000000 | \ + (((Uint32)clampU8(y_tmp+b_tmp)) << 16) | \ + (((Uint32)clampU8(y_tmp+g_tmp)) << 8) | \ + (((Uint32)clampU8(y_tmp+r_tmp)) << 0); \ + rgb_ptr += 4; \ + +#else +#error PACK_PIXEL unimplemented +#endif + + +void STD_FUNCTION_NAME( + uint32_t width, uint32_t height, + const uint8_t *Y, const uint8_t *U, const uint8_t *V, uint32_t Y_stride, uint32_t UV_stride, + uint8_t *RGB, uint32_t RGB_stride, + YCbCrType yuv_type) +{ + const YUV2RGBParam *const param = &(YUV2RGB[yuv_type]); +#if YUV_FORMAT == YUV_FORMAT_420 + const int y_pixel_stride = 1; + const int uv_pixel_stride = 1; + const int uv_x_sample_interval = 2; + const int uv_y_sample_interval = 2; +#elif YUV_FORMAT == YUV_FORMAT_422 + const int y_pixel_stride = 2; + const int uv_pixel_stride = 4; + const int uv_x_sample_interval = 2; + const int uv_y_sample_interval = 1; +#elif YUV_FORMAT == YUV_FORMAT_NV12 + const int y_pixel_stride = 1; + const int uv_pixel_stride = 2; + const int uv_x_sample_interval = 2; + const int uv_y_sample_interval = 2; +#endif + + uint32_t x, y; + for(y=0; y<(height-(uv_y_sample_interval-1)); y+=uv_y_sample_interval) + { + const uint8_t *y_ptr1=Y+y*Y_stride, + *y_ptr2=Y+(y+1)*Y_stride, + *u_ptr=U+(y/uv_y_sample_interval)*UV_stride, + *v_ptr=V+(y/uv_y_sample_interval)*UV_stride; + + uint8_t *rgb_ptr1=RGB+y*RGB_stride, + *rgb_ptr2=RGB+(y+1)*RGB_stride; + + for(x=0; x<(width-(uv_x_sample_interval-1)); x+=uv_x_sample_interval) + { + // Compute U and V contributions, common to the four pixels + + int32_t u_tmp = ((*u_ptr)-128); + int32_t v_tmp = ((*v_ptr)-128); + + int32_t r_tmp = (v_tmp*param->v_r_factor); + int32_t g_tmp = (u_tmp*param->u_g_factor + v_tmp*param->v_g_factor); + int32_t b_tmp = (u_tmp*param->u_b_factor); + + // Compute the Y contribution for each pixel + + int32_t y_tmp = ((y_ptr1[0]-param->y_shift)*param->y_factor); + PACK_PIXEL(rgb_ptr1); + + y_tmp = ((y_ptr1[y_pixel_stride]-param->y_shift)*param->y_factor); + PACK_PIXEL(rgb_ptr1); + + if (uv_y_sample_interval > 1) { + y_tmp = ((y_ptr2[0]-param->y_shift)*param->y_factor); + PACK_PIXEL(rgb_ptr2); + + y_tmp = ((y_ptr2[y_pixel_stride]-param->y_shift)*param->y_factor); + PACK_PIXEL(rgb_ptr2); + } + + y_ptr1+=2*y_pixel_stride; + y_ptr2+=2*y_pixel_stride; + u_ptr+=2*uv_pixel_stride/uv_x_sample_interval; + v_ptr+=2*uv_pixel_stride/uv_x_sample_interval; + } + + /* Catch the last pixel, if needed */ + if (uv_x_sample_interval == 2 && x == (width-1)) + { + // Compute U and V contributions, common to the four pixels + + int32_t u_tmp = ((*u_ptr)-128); + int32_t v_tmp = ((*v_ptr)-128); + + int32_t r_tmp = (v_tmp*param->v_r_factor); + int32_t g_tmp = (u_tmp*param->u_g_factor + v_tmp*param->v_g_factor); + int32_t b_tmp = (u_tmp*param->u_b_factor); + + // Compute the Y contribution for each pixel + + int32_t y_tmp = ((y_ptr1[0]-param->y_shift)*param->y_factor); + PACK_PIXEL(rgb_ptr1); + + if (uv_y_sample_interval > 1) { + y_tmp = ((y_ptr2[0]-param->y_shift)*param->y_factor); + PACK_PIXEL(rgb_ptr2); + } + } + } + + /* Catch the last line, if needed */ + if (uv_y_sample_interval == 2 && y == (height-1)) + { + const uint8_t *y_ptr1=Y+y*Y_stride, + *u_ptr=U+(y/uv_y_sample_interval)*UV_stride, + *v_ptr=V+(y/uv_y_sample_interval)*UV_stride; + + uint8_t *rgb_ptr1=RGB+y*RGB_stride; + + for(x=0; x<(width-(uv_x_sample_interval-1)); x+=uv_x_sample_interval) + { + // Compute U and V contributions, common to the four pixels + + int32_t u_tmp = ((*u_ptr)-128); + int32_t v_tmp = ((*v_ptr)-128); + + int32_t r_tmp = (v_tmp*param->v_r_factor); + int32_t g_tmp = (u_tmp*param->u_g_factor + v_tmp*param->v_g_factor); + int32_t b_tmp = (u_tmp*param->u_b_factor); + + // Compute the Y contribution for each pixel + + int32_t y_tmp = ((y_ptr1[0]-param->y_shift)*param->y_factor); + PACK_PIXEL(rgb_ptr1); + + y_tmp = ((y_ptr1[y_pixel_stride]-param->y_shift)*param->y_factor); + PACK_PIXEL(rgb_ptr1); + + y_ptr1+=2*y_pixel_stride; + u_ptr+=2*uv_pixel_stride/uv_x_sample_interval; + v_ptr+=2*uv_pixel_stride/uv_x_sample_interval; + } + + /* Catch the last pixel, if needed */ + if (uv_x_sample_interval == 2 && x == (width-1)) + { + // Compute U and V contributions, common to the four pixels + + int32_t u_tmp = ((*u_ptr)-128); + int32_t v_tmp = ((*v_ptr)-128); + + int32_t r_tmp = (v_tmp*param->v_r_factor); + int32_t g_tmp = (u_tmp*param->u_g_factor + v_tmp*param->v_g_factor); + int32_t b_tmp = (u_tmp*param->u_b_factor); + + // Compute the Y contribution for each pixel + + int32_t y_tmp = ((y_ptr1[0]-param->y_shift)*param->y_factor); + PACK_PIXEL(rgb_ptr1); + } + } +} + +#undef STD_FUNCTION_NAME +#undef YUV_FORMAT +#undef RGB_FORMAT +#undef PACK_PIXEL diff --git a/test/Makefile.in b/test/Makefile.in index 0ac70e923be68..c625622aa2b8a 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -9,19 +9,23 @@ LIBS = @LIBS@ TARGETS = \ checkkeys$(EXE) \ + controllermap$(EXE) \ loopwave$(EXE) \ loopwavequeue$(EXE) \ testatomic$(EXE) \ - testaudioinfo$(EXE) \ testaudiocapture$(EXE) \ + testaudiohotplug$(EXE) \ + testaudioinfo$(EXE) \ testautomation$(EXE) \ testbounds$(EXE) \ testcustomcursor$(EXE) \ + testdisplayinfo$(EXE) \ testdraw2$(EXE) \ testdrawchessboard$(EXE) \ testdropfile$(EXE) \ testerror$(EXE) \ testfile$(EXE) \ + testfilesystem$(EXE) \ testgamecontroller$(EXE) \ testgesture$(EXE) \ testgl2$(EXE) \ @@ -29,26 +33,26 @@ TARGETS = \ testgles2$(EXE) \ testhaptic$(EXE) \ testhittesting$(EXE) \ - testrumble$(EXE) \ testhotplug$(EXE) \ - testthread$(EXE) \ testiconv$(EXE) \ testime$(EXE) \ testintersections$(EXE) \ - testrelative$(EXE) \ testjoystick$(EXE) \ testkeys$(EXE) \ testloadso$(EXE) \ testlock$(EXE) \ + testmessage$(EXE) \ testmultiaudio$(EXE) \ - testaudiohotplug$(EXE) \ testnative$(EXE) \ testoverlay2$(EXE) \ testplatform$(EXE) \ testpower$(EXE) \ - testfilesystem$(EXE) \ + testqsort$(EXE) \ + testrelative$(EXE) \ + testrendercopyex$(EXE) \ testrendertarget$(EXE) \ testresample$(EXE) \ + testrumble$(EXE) \ testscale$(EXE) \ testsem$(EXE) \ testshader$(EXE) \ @@ -56,17 +60,14 @@ TARGETS = \ testsprite2$(EXE) \ testspriteminimal$(EXE) \ teststreaming$(EXE) \ + testthread$(EXE) \ testtimer$(EXE) \ testver$(EXE) \ testviewport$(EXE) \ + testvulkan$(EXE) \ testwm2$(EXE) \ + testyuv$(EXE) \ torturethread$(EXE) \ - testrendercopyex$(EXE) \ - testmessage$(EXE) \ - testdisplayinfo$(EXE) \ - testqsort$(EXE) \ - controllermap$(EXE) \ - testvulkan$(EXE) \ all: Makefile $(TARGETS) copydatafiles @@ -266,6 +267,9 @@ testviewport$(EXE): $(srcdir)/testviewport.c testwm2$(EXE): $(srcdir)/testwm2.c $(CC) -o $@ $^ $(CFLAGS) $(LIBS) +testyuv$(EXE): $(srcdir)/testyuv.c + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + torturethread$(EXE): $(srcdir)/torturethread.c $(CC) -o $@ $^ $(CFLAGS) $(LIBS) diff --git a/test/testoverlay2.c b/test/testoverlay2.c index 2145c4d36b045..7d01c85647651 100644 --- a/test/testoverlay2.c +++ b/test/testoverlay2.c @@ -16,16 +16,14 @@ * * ********************************************************************************/ -#include -#include -#include - #ifdef __EMSCRIPTEN__ #include #endif #include "SDL.h" +#include "testyuv_cvt.h" + #define MOOSEPIC_W 64 #define MOOSEPIC_H 88 @@ -149,7 +147,6 @@ SDL_Renderer *renderer; int paused = 0; int i; SDL_bool done = SDL_FALSE; -Uint32 pixel_format = SDL_PIXELFORMAT_YV12; static int fpsdelay; /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ @@ -160,91 +157,6 @@ quit(int rc) exit(rc); } -/* All RGB2YUV conversion code and some other parts of code has been taken from testoverlay.c */ - -/* NOTE: These RGB conversion functions are not intended for speed, - only as examples. -*/ - -void -RGBtoYUV(Uint8 * rgb, int *yuv, int monochrome, int luminance) -{ - if (monochrome) { -#if 1 /* these are the two formulas that I found on the FourCC site... */ - yuv[0] = (int)(0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]); - yuv[1] = 128; - yuv[2] = 128; -#else - yuv[0] = (int)(0.257 * rgb[0]) + (0.504 * rgb[1]) + (0.098 * rgb[2]) + 16; - yuv[1] = 128; - yuv[2] = 128; -#endif - } else { -#if 1 /* these are the two formulas that I found on the FourCC site... */ - yuv[0] = (int)(0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]); - yuv[1] = (int)((rgb[2] - yuv[0]) * 0.565 + 128); - yuv[2] = (int)((rgb[0] - yuv[0]) * 0.713 + 128); -#else - yuv[0] = (0.257 * rgb[0]) + (0.504 * rgb[1]) + (0.098 * rgb[2]) + 16; - yuv[1] = 128 - (0.148 * rgb[0]) - (0.291 * rgb[1]) + (0.439 * rgb[2]); - yuv[2] = 128 + (0.439 * rgb[0]) - (0.368 * rgb[1]) - (0.071 * rgb[2]); -#endif - } - - if (luminance != 100) { - yuv[0] = yuv[0] * luminance / 100; - if (yuv[0] > 255) - yuv[0] = 255; - } -} - -void -ConvertRGBtoYV12(Uint8 *rgb, Uint8 *out, int w, int h, - int monochrome, int luminance) -{ - int x, y; - int yuv[3]; - Uint8 *op[3]; - - op[0] = out; - op[1] = op[0] + w*h; - op[2] = op[1] + w*h/4; - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - RGBtoYUV(rgb, yuv, monochrome, luminance); - *(op[0]++) = yuv[0]; - if (x % 2 == 0 && y % 2 == 0) { - *(op[1]++) = yuv[2]; - *(op[2]++) = yuv[1]; - } - rgb += 3; - } - } -} - -void -ConvertRGBtoNV12(Uint8 *rgb, Uint8 *out, int w, int h, - int monochrome, int luminance) -{ - int x, y; - int yuv[3]; - Uint8 *op[2]; - - op[0] = out; - op[1] = op[0] + w*h; - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - RGBtoYUV(rgb, yuv, monochrome, luminance); - *(op[0]++) = yuv[0]; - if (x % 2 == 0 && y % 2 == 0) { - *(op[1]++) = yuv[1]; - *(op[1]++) = yuv[2]; - } - rgb += 3; - } - } -} - static void PrintUsage(char *argv0) { @@ -307,7 +219,7 @@ loop() if (!paused) { i = (i + 1) % MOOSEFRAMES_COUNT; - SDL_UpdateTexture(MooseTexture, NULL, MooseFrame[i], MOOSEPIC_W*SDL_BYTESPERPIXEL(pixel_format)); + SDL_UpdateTexture(MooseTexture, NULL, MooseFrame[i], MOOSEPIC_W); } SDL_RenderClear(renderer); SDL_RenderCopy(renderer, MooseTexture, NULL, &displayrect); @@ -329,11 +241,6 @@ main(int argc, char **argv) int j; int fps = 12; int nodelay = 0; -#ifdef TEST_NV12 - Uint32 pixel_format = SDL_PIXELFORMAT_NV12; -#else - Uint32 pixel_format = SDL_PIXELFORMAT_YV12; -#endif int scale = 5; /* Enable standard application logging */ @@ -439,7 +346,7 @@ main(int argc, char **argv) quit(4); } - MooseTexture = SDL_CreateTexture(renderer, pixel_format, SDL_TEXTUREACCESS_STREAMING, MOOSEPIC_W, MOOSEPIC_H); + MooseTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, MOOSEPIC_W, MOOSEPIC_H); if (!MooseTexture) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create texture: %s\n", SDL_GetError()); free(RawMooseData); @@ -461,17 +368,9 @@ main(int argc, char **argv) rgb[2] = MooseColors[frame[j]].b; rgb += 3; } - switch (pixel_format) { - case SDL_PIXELFORMAT_YV12: - ConvertRGBtoYV12(MooseFrameRGB, MooseFrame[i], MOOSEPIC_W, MOOSEPIC_H, 0, 100); - break; - case SDL_PIXELFORMAT_NV12: - ConvertRGBtoNV12(MooseFrameRGB, MooseFrame[i], MOOSEPIC_W, MOOSEPIC_H, 0, 100); - break; - default: - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unsupported pixel format\n"); - break; - } + ConvertRGBtoYUV(SDL_PIXELFORMAT_YV12, MooseFrameRGB, MOOSEPIC_W*3, MooseFrame[i], MOOSEPIC_W, MOOSEPIC_H, + SDL_GetYUVConversionModeForResolution(MOOSEPIC_W, MOOSEPIC_H), + 0, 100); } free(RawMooseData); diff --git a/test/testyuv.bmp b/test/testyuv.bmp new file mode 100644 index 0000000000000000000000000000000000000000..af32034b6b5b858be470f9ef3bb023bc199bf96e GIT binary patch literal 739398 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ŽAD149Wj?1|X01)I4BQdAPsJ(8D&;wq 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

{o>^o9lF%RQ; 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 z�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 literal 0 HcmV?d00001 diff --git a/test/testyuv.c b/test/testyuv.c new file mode 100644 index 0000000000000..a4071d4c79538 --- /dev/null +++ b/test/testyuv.c @@ -0,0 +1,455 @@ +/* + Copyright (C) 1997-2017 Sam Lantinga + + 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 +#include +#include + +#include "SDL.h" +#include "SDL_test_font.h" +#include "testyuv_cvt.h" + + +/* 422 (YUY2, etc) formats are the largest */ +#define MAX_YUV_SURFACE_SIZE(W, H, P) (H*4*(W+P+1)/2) + + +/* Return true if the YUV format is packed pixels */ +static SDL_bool is_packed_yuv_format(Uint32 format) +{ + return (format == SDL_PIXELFORMAT_YUY2 || + format == SDL_PIXELFORMAT_UYVY || + format == SDL_PIXELFORMAT_YVYU); +} + +/* Create a surface with a good pattern for verifying YUV conversion */ +static SDL_Surface *generate_test_pattern(int pattern_size) +{ + SDL_Surface *pattern = SDL_CreateRGBSurfaceWithFormat(0, pattern_size, pattern_size, 0, SDL_PIXELFORMAT_RGB24); + + if (pattern) { + int i, x, y; + Uint8 *p, c; + const int thickness = 2; /* Important so 2x2 blocks of color are the same, to avoid Cr/Cb interpolation over pixels */ + + /* R, G, B in alternating horizontal bands */ + for (y = 0; y < pattern->h; y += thickness) { + for (i = 0; i < thickness; ++i) { + p = (Uint8 *)pattern->pixels + (y + i) * pattern->pitch + ((y/thickness) % 3); + for (x = 0; x < pattern->w; ++x) { + *p = 0xFF; + p += 3; + } + } + } + + /* Black and white in alternating vertical bands */ + c = 0xFF; + for (x = 1*thickness; x < pattern->w; x += 2*thickness) { + for (i = 0; i < thickness; ++i) { + p = (Uint8 *)pattern->pixels + (x + i)*3; + for (y = 0; y < pattern->h; ++y) { + SDL_memset(p, c, 3); + p += pattern->pitch; + } + } + if (c) { + c = 0x00; + } else { + c = 0xFF; + } + } + } + return pattern; +} + +static SDL_bool verify_yuv_data(Uint32 format, const Uint8 *yuv, int yuv_pitch, SDL_Surface *surface) +{ + const int tolerance = 20; + const int size = (surface->h * surface->pitch); + Uint8 *rgb; + SDL_bool result = SDL_FALSE; + + rgb = (Uint8 *)SDL_malloc(size); + if (!rgb) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory"); + return SDL_FALSE; + } + + if (SDL_ConvertPixels(surface->w, surface->h, format, yuv, yuv_pitch, surface->format->format, rgb, surface->pitch) == 0) { + int x, y; + result = SDL_TRUE; + for (y = 0; y < surface->h; ++y) { + const Uint8 *actual = rgb + y * surface->pitch; + const Uint8 *expected = (const Uint8 *)surface->pixels + y * surface->pitch; + for (x = 0; x < surface->w; ++x) { + int deltaR = (int)actual[0] - expected[0]; + int deltaG = (int)actual[1] - expected[1]; + int deltaB = (int)actual[2] - expected[2]; + int distance = (deltaR * deltaR + deltaG * deltaG + deltaB * deltaB); + if (distance > tolerance) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Pixel at %d,%d was 0x%.2x,0x%.2x,0x%.2x, expected 0x%.2x,0x%.2x,0x%.2x, distance = %d\n", x, y, actual[0], actual[1], actual[2], expected[0], expected[1], expected[2], distance); + result = SDL_FALSE; + } + actual += 3; + expected += 3; + } + } + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(format), SDL_GetPixelFormatName(surface->format->format), SDL_GetError()); + } + SDL_free(rgb); + + return result; +} + +static int run_automated_tests(int pattern_size, int extra_pitch) +{ + const Uint32 formats[] = { + SDL_PIXELFORMAT_YV12, + SDL_PIXELFORMAT_IYUV, + SDL_PIXELFORMAT_NV12, + SDL_PIXELFORMAT_NV21, + SDL_PIXELFORMAT_YUY2, + SDL_PIXELFORMAT_UYVY, + SDL_PIXELFORMAT_YVYU + }; + int i, j; + SDL_Surface *pattern = generate_test_pattern(pattern_size); + const int yuv_len = MAX_YUV_SURFACE_SIZE(pattern->w, pattern->h, extra_pitch); + Uint8 *yuv1 = (Uint8 *)SDL_malloc(yuv_len); + Uint8 *yuv2 = (Uint8 *)SDL_malloc(yuv_len); + int yuv1_pitch, yuv2_pitch; + int result = -1; + + if (!pattern || !yuv1 || !yuv2) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't allocate test surfaces"); + goto done; + } + + /* Verify conversion from YUV formats */ + for (i = 0; i < SDL_arraysize(formats); ++i) { + if (!ConvertRGBtoYUV(formats[i], pattern->pixels, pattern->pitch, yuv1, pattern->w, pattern->h, SDL_GetYUVConversionModeForResolution(pattern->w, pattern->h), 0, 100)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ConvertRGBtoYUV() doesn't support converting to %s\n", SDL_GetPixelFormatName(formats[i])); + goto done; + } + yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w); + if (!verify_yuv_data(formats[i], yuv1, yuv1_pitch, pattern)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to RGB\n", SDL_GetPixelFormatName(formats[i])); + goto done; + } + } + + /* Verify conversion to YUV formats */ + for (i = 0; i < SDL_arraysize(formats); ++i) { + yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch; + if (SDL_ConvertPixels(pattern->w, pattern->h, pattern->format->format, pattern->pixels, pattern->pitch, formats[i], yuv1, yuv1_pitch) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError()); + goto done; + } + if (!verify_yuv_data(formats[i], yuv1, yuv1_pitch, pattern)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from RGB to %s\n", SDL_GetPixelFormatName(formats[i])); + goto done; + } + } + + /* Verify conversion between YUV formats */ + for (i = 0; i < SDL_arraysize(formats); ++i) { + for (j = 0; j < SDL_arraysize(formats); ++j) { + yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch; + yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch; + if (SDL_ConvertPixels(pattern->w, pattern->h, pattern->format->format, pattern->pixels, pattern->pitch, formats[i], yuv1, yuv1_pitch) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError()); + goto done; + } + if (SDL_ConvertPixels(pattern->w, pattern->h, formats[i], yuv1, yuv1_pitch, formats[j], yuv2, yuv2_pitch) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError()); + goto done; + } + if (!verify_yuv_data(formats[j], yuv2, yuv2_pitch, pattern)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j])); + goto done; + } + } + } + + /* Verify conversion between YUV formats in-place */ + for (i = 0; i < SDL_arraysize(formats); ++i) { + for (j = 0; j < SDL_arraysize(formats); ++j) { + if (is_packed_yuv_format(formats[i]) != is_packed_yuv_format(formats[j])) { + /* Can't change plane vs packed pixel layout in-place */ + continue; + } + + yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch; + yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch; + if (SDL_ConvertPixels(pattern->w, pattern->h, pattern->format->format, pattern->pixels, pattern->pitch, formats[i], yuv1, yuv1_pitch) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError()); + goto done; + } + if (SDL_ConvertPixels(pattern->w, pattern->h, formats[i], yuv1, yuv1_pitch, formats[j], yuv1, yuv2_pitch) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError()); + goto done; + } + if (!verify_yuv_data(formats[j], yuv1, yuv2_pitch, pattern)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j])); + goto done; + } + } + } + + + result = 0; + +done: + SDL_free(yuv1); + SDL_free(yuv2); + SDL_FreeSurface(pattern); + return result; +} + +int +main(int argc, char **argv) +{ + struct { + SDL_bool enable_intrinsics; + int pattern_size; + int extra_pitch; + } automated_test_params[] = { + /* Test: even width and height */ + { SDL_FALSE, 2, 0 }, + { SDL_FALSE, 4, 0 }, + /* Test: odd width and height */ + { SDL_FALSE, 1, 0 }, + { SDL_FALSE, 3, 0 }, + /* Test: even width and height, extra pitch */ + { SDL_FALSE, 2, 3 }, + { SDL_FALSE, 4, 3 }, + /* Test: odd width and height, extra pitch */ + { SDL_FALSE, 1, 3 }, + { SDL_FALSE, 3, 3 }, + /* Test: even width and height with intrinsics */ + { SDL_TRUE, 32, 0 }, + /* Test: odd width and height with intrinsics */ + { SDL_TRUE, 33, 0 }, + { SDL_TRUE, 37, 0 }, + /* Test: even width and height with intrinsics, extra pitch */ + { SDL_TRUE, 32, 3 }, + /* Test: odd width and height with intrinsics, extra pitch */ + { SDL_TRUE, 33, 3 }, + { SDL_TRUE, 37, 3 }, + }; + int arg = 1; + const char *filename; + SDL_Surface *original; + SDL_Surface *converted; + SDL_Window *window; + SDL_Renderer *renderer; + SDL_Texture *output[3]; + const char *titles[3] = { "ORIGINAL", "SOFTWARE", "HARDWARE" }; + char title[128]; + const char *yuv_name; + const char *yuv_mode; + Uint32 rgb_format = SDL_PIXELFORMAT_RGBX8888; + Uint32 yuv_format = SDL_PIXELFORMAT_YV12; + int current = 0; + int pitch; + Uint8 *raw_yuv; + Uint32 then, now, i, iterations = 100; + SDL_bool should_run_automated_tests = SDL_FALSE; + + while (argv[arg] && *argv[arg] == '-') { + if (SDL_strcmp(argv[arg], "--jpeg") == 0) { + SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_JPEG); + } else if (SDL_strcmp(argv[arg], "--bt601") == 0) { + SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_BT601); + } else if (SDL_strcmp(argv[arg], "--bt709") == 0) { + SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_BT709); + } else if (SDL_strcmp(argv[arg], "--auto") == 0) { + SDL_SetYUVConversionMode(SDL_YUV_CONVERSION_AUTOMATIC); + } else if (SDL_strcmp(argv[arg], "--yv12") == 0) { + yuv_format = SDL_PIXELFORMAT_YV12; + } else if (SDL_strcmp(argv[arg], "--iyuv") == 0) { + yuv_format = SDL_PIXELFORMAT_IYUV; + } else if (SDL_strcmp(argv[arg], "--yuy2") == 0) { + yuv_format = SDL_PIXELFORMAT_YUY2; + } else if (SDL_strcmp(argv[arg], "--uyvy") == 0) { + yuv_format = SDL_PIXELFORMAT_UYVY; + } else if (SDL_strcmp(argv[arg], "--yvyu") == 0) { + yuv_format = SDL_PIXELFORMAT_YVYU; + } else if (SDL_strcmp(argv[arg], "--nv12") == 0) { + yuv_format = SDL_PIXELFORMAT_NV12; + } else if (SDL_strcmp(argv[arg], "--nv21") == 0) { + yuv_format = SDL_PIXELFORMAT_NV21; + } else if (SDL_strcmp(argv[arg], "--rgb555") == 0) { + rgb_format = SDL_PIXELFORMAT_RGB555; + } else if (SDL_strcmp(argv[arg], "--rgb565") == 0) { + rgb_format = SDL_PIXELFORMAT_RGB565; + } else if (SDL_strcmp(argv[arg], "--rgb24") == 0) { + rgb_format = SDL_PIXELFORMAT_RGB24; + } else if (SDL_strcmp(argv[arg], "--argb") == 0) { + rgb_format = SDL_PIXELFORMAT_ARGB8888; + } else if (SDL_strcmp(argv[arg], "--abgr") == 0) { + rgb_format = SDL_PIXELFORMAT_ABGR8888; + } else if (SDL_strcmp(argv[arg], "--rgba") == 0) { + rgb_format = SDL_PIXELFORMAT_RGBA8888; + } else if (SDL_strcmp(argv[arg], "--bgra") == 0) { + rgb_format = SDL_PIXELFORMAT_BGRA8888; + } else if (SDL_strcmp(argv[arg], "--automated") == 0) { + should_run_automated_tests = SDL_TRUE; + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Usage: %s [--jpeg|--bt601|-bt709|--auto] [--yv12|--iyuv|--yuy2|--uyvy|--yvyu|--nv12|--nv21] [--rgb555|--rgb565|--rgb24|--argb|--abgr|--rgba|--bgra] [image_filename]\n", argv[0]); + return 1; + } + ++arg; + } + + /* Run automated tests */ + if (should_run_automated_tests) { + for (i = 0; i < SDL_arraysize(automated_test_params); ++i) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Running automated test, pattern size %d, extra pitch %d, intrinsics %s\n", + automated_test_params[i].pattern_size, + automated_test_params[i].extra_pitch, + automated_test_params[i].enable_intrinsics ? "enabled" : "disabled"); + if (run_automated_tests(automated_test_params[i].pattern_size, automated_test_params[i].extra_pitch) < 0) { + return 2; + } + } + return 0; + } + + if (argv[arg]) { + filename = argv[arg]; + } else { + filename = "testyuv.bmp"; + } + original = SDL_ConvertSurfaceFormat(SDL_LoadBMP(filename), SDL_PIXELFORMAT_RGB24, 0); + if (!original) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", filename, SDL_GetError()); + return 3; + } + + raw_yuv = SDL_calloc(1, MAX_YUV_SURFACE_SIZE(original->w, original->h, 0)); + ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, + SDL_GetYUVConversionModeForResolution(original->w, original->h), + 0, 100); + pitch = CalculateYUVPitch(yuv_format, original->w); + + converted = SDL_CreateRGBSurfaceWithFormat(0, original->w, original->h, 0, rgb_format); + if (!converted) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create converted surface: %s\n", SDL_GetError()); + return 3; + } + + then = SDL_GetTicks(); + for ( i = 0; i < iterations; ++i ) { + SDL_ConvertPixels(original->w, original->h, yuv_format, raw_yuv, pitch, rgb_format, converted->pixels, converted->pitch); + } + now = SDL_GetTicks(); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%d iterations in %d ms, %.2fms each\n", iterations, (now - then), (float)(now - then)/iterations); + + window = SDL_CreateWindow("YUV test", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + original->w, original->h, + 0); + if (!window) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); + return 4; + } + + renderer = SDL_CreateRenderer(window, -1, 0); + if (!renderer) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError()); + return 4; + } + + output[0] = SDL_CreateTextureFromSurface(renderer, original); + output[1] = SDL_CreateTextureFromSurface(renderer, converted); + output[2] = SDL_CreateTexture(renderer, yuv_format, SDL_TEXTUREACCESS_STREAMING, original->w, original->h); + if (!output[0] || !output[1] || !output[2]) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create texture: %s\n", SDL_GetError()); + return 5; + } + SDL_UpdateTexture(output[2], NULL, raw_yuv, pitch); + + yuv_name = SDL_GetPixelFormatName(yuv_format); + if (SDL_strncmp(yuv_name, "SDL_PIXELFORMAT_", 16) == 0) { + yuv_name += 16; + } + + switch (SDL_GetYUVConversionModeForResolution(original->w, original->h)) { + case SDL_YUV_CONVERSION_JPEG: + yuv_mode = "JPEG"; + break; + case SDL_YUV_CONVERSION_BT601: + yuv_mode = "BT.601"; + break; + case SDL_YUV_CONVERSION_BT709: + yuv_mode = "BT.709"; + break; + default: + yuv_mode = "UNKNOWN"; + break; + } + + { int done = 0; + while ( !done ) + { + SDL_Event event; + while (SDL_PollEvent(&event) > 0) { + if (event.type == SDL_QUIT) { + done = 1; + } + if (event.type == SDL_KEYDOWN) { + if (event.key.keysym.sym == SDLK_ESCAPE) { + done = 1; + } else if (event.key.keysym.sym == SDLK_LEFT) { + --current; + } else if (event.key.keysym.sym == SDLK_RIGHT) { + ++current; + } + } + if (event.type == SDL_MOUSEBUTTONDOWN) { + if (event.button.x < (original->w/2)) { + --current; + } else { + ++current; + } + } + } + + /* Handle wrapping */ + if (current < 0) { + current += SDL_arraysize(output); + } + if (current >= SDL_arraysize(output)) { + current -= SDL_arraysize(output); + } + + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, output[current], NULL, NULL); + SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); + if (current == 0) { + SDLTest_DrawString(renderer, 4, 4, titles[current]); + } else { + SDL_snprintf(title, sizeof(title), "%s %s %s", titles[current], yuv_name, yuv_mode); + SDLTest_DrawString(renderer, 4, 4, title); + } + SDL_RenderPresent(renderer); + SDL_Delay(10); + } + } + SDL_Quit(); + return 0; +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/test/testyuv_cvt.c b/test/testyuv_cvt.c new file mode 100644 index 0000000000000..977f034f26ae6 --- /dev/null +++ b/test/testyuv_cvt.c @@ -0,0 +1,300 @@ +/* + Copyright (C) 1997-2017 Sam Lantinga + + 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 "SDL.h" + +#include "testyuv_cvt.h" + + +static float clip3(float x, float y, float z) +{ + return ((z < x) ? x : ((z > y) ? y : z)); +} + +static void RGBtoYUV(Uint8 * rgb, int *yuv, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance) +{ + if (mode == SDL_YUV_CONVERSION_JPEG) { + /* Full range YUV */ + yuv[0] = (int)(0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]); + yuv[1] = (int)((rgb[2] - yuv[0]) * 0.565 + 128); + yuv[2] = (int)((rgb[0] - yuv[0]) * 0.713 + 128); + } else { + // This formula is from Microsoft's documentation: + // https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx + // L = Kr * R + Kb * B + (1 - Kr - Kb) * G + // Y = floor(2^(M-8) * (219*(L-Z)/S + 16) + 0.5); + // U = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(B-L) / ((1-Kb)*S) + 128) + 0.5)); + // V = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(R-L) / ((1-Kr)*S) + 128) + 0.5)); + float S, Z, R, G, B, L, Kr, Kb, Y, U, V; + + if (mode == SDL_YUV_CONVERSION_BT709) { + /* BT.709 */ + Kr = 0.2126f; + Kb = 0.0722f; + } else { + /* BT.601 */ + Kr = 0.299f; + Kb = 0.114f; + } + + S = 255.0f; + Z = 0.0f; + R = rgb[0]; + G = rgb[1]; + B = rgb[2]; + L = Kr * R + Kb * B + (1 - Kr - Kb) * G; + Y = (Uint8)SDL_floorf((219*(L-Z)/S + 16) + 0.5f); + U = (Uint8)clip3(0, 255, SDL_floorf((112.0f*(B-L) / ((1.0f-Kb)*S) + 128) + 0.5f)); + V = (Uint8)clip3(0, 255, SDL_floorf((112.0f*(R-L) / ((1.0f-Kr)*S) + 128) + 0.5f)); + + yuv[0] = (Uint8)Y; + yuv[1] = (Uint8)U; + yuv[2] = (Uint8)V; + } + + if (monochrome) { + yuv[1] = 128; + yuv[2] = 128; + } + + if (luminance != 100) { + yuv[0] = yuv[0] * luminance / 100; + if (yuv[0] > 255) + yuv[0] = 255; + } +} + +static void ConvertRGBtoPlanar2x2(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance) +{ + int x, y; + int yuv[4][3]; + Uint8 *Y1, *Y2, *U, *V; + Uint8 *rgb1, *rgb2; + int rgb_row_advance = (pitch - w*3) + pitch; + int UV_advance; + + rgb1 = src; + rgb2 = src + pitch; + + Y1 = out; + Y2 = Y1 + w; + switch (format) { + case SDL_PIXELFORMAT_YV12: + V = (Y1 + h * w); + U = V + ((h + 1)/2)*((w + 1)/2); + UV_advance = 1; + break; + case SDL_PIXELFORMAT_IYUV: + U = (Y1 + h * w); + V = U + ((h + 1)/2)*((w + 1)/2); + UV_advance = 1; + break; + case SDL_PIXELFORMAT_NV12: + U = (Y1 + h * w); + V = U + 1; + UV_advance = 2; + break; + case SDL_PIXELFORMAT_NV21: + V = (Y1 + h * w); + U = V + 1; + UV_advance = 2; + break; + default: + SDL_assert(!"Unsupported planar YUV format"); + return; + } + + for (y = 0; y < (h - 1); y += 2) { + for (x = 0; x < (w - 1); x += 2) { + RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); + rgb1 += 3; + *Y1++ = (Uint8)yuv[0][0]; + + RGBtoYUV(rgb1, yuv[1], mode, monochrome, luminance); + rgb1 += 3; + *Y1++ = (Uint8)yuv[1][0]; + + RGBtoYUV(rgb2, yuv[2], mode, monochrome, luminance); + rgb2 += 3; + *Y2++ = (Uint8)yuv[2][0]; + + RGBtoYUV(rgb2, yuv[3], mode, monochrome, luminance); + rgb2 += 3; + *Y2++ = (Uint8)yuv[3][0]; + + *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1] + yuv[2][1] + yuv[3][1])/4.0f + 0.5f); + U += UV_advance; + + *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2] + yuv[2][2] + yuv[3][2])/4.0f + 0.5f); + V += UV_advance; + } + /* Last column */ + if (x == (w - 1)) { + RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); + rgb1 += 3; + *Y1++ = (Uint8)yuv[0][0]; + + RGBtoYUV(rgb2, yuv[2], mode, monochrome, luminance); + rgb2 += 3; + *Y2++ = (Uint8)yuv[2][0]; + + *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[2][1])/2.0f + 0.5f); + U += UV_advance; + + *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[2][2])/2.0f + 0.5f); + V += UV_advance; + } + Y1 += w; + Y2 += w; + rgb1 += rgb_row_advance; + rgb2 += rgb_row_advance; + } + /* Last row */ + if (y == (h - 1)) { + for (x = 0; x < (w - 1); x += 2) { + RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); + rgb1 += 3; + *Y1++ = (Uint8)yuv[0][0]; + + RGBtoYUV(rgb1, yuv[1], mode, monochrome, luminance); + rgb1 += 3; + *Y1++ = (Uint8)yuv[1][0]; + + *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1])/2.0f + 0.5f); + U += UV_advance; + + *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2])/2.0f + 0.5f); + V += UV_advance; + } + /* Last column */ + if (x == (w - 1)) { + RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); + *Y1++ = (Uint8)yuv[0][0]; + + *U = (Uint8)yuv[0][1]; + U += UV_advance; + + *V = (Uint8)yuv[0][2]; + V += UV_advance; + } + } +} + +static void ConvertRGBtoPacked4(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance) +{ + int x, y; + int yuv[2][3]; + Uint8 *Y1, *Y2, *U, *V; + Uint8 *rgb; + int rgb_row_advance = (pitch - w*3); + + rgb = src; + + switch (format) { + case SDL_PIXELFORMAT_YUY2: + Y1 = out; + U = out+1; + Y2 = out+2; + V = out+3; + break; + case SDL_PIXELFORMAT_UYVY: + U = out; + Y1 = out+1; + V = out+2; + Y2 = out+3; + break; + case SDL_PIXELFORMAT_YVYU: + Y1 = out; + V = out+1; + Y2 = out+2; + U = out+3; + break; + default: + SDL_assert(!"Unsupported packed YUV format"); + return; + } + + for (y = 0; y < h; ++y) { + for (x = 0; x < (w - 1); x += 2) { + RGBtoYUV(rgb, yuv[0], mode, monochrome, luminance); + rgb += 3; + *Y1 = (Uint8)yuv[0][0]; + Y1 += 4; + + RGBtoYUV(rgb, yuv[1], mode, monochrome, luminance); + rgb += 3; + *Y2 = (Uint8)yuv[1][0]; + Y2 += 4; + + *U = (Uint8)SDL_floorf((yuv[0][1] + yuv[1][1])/2.0f + 0.5f); + U += 4; + + *V = (Uint8)SDL_floorf((yuv[0][2] + yuv[1][2])/2.0f + 0.5f); + V += 4; + } + /* Last column */ + if (x == (w - 1)) { + RGBtoYUV(rgb, yuv[0], mode, monochrome, luminance); + rgb += 3; + *Y2 = *Y1 = (Uint8)yuv[0][0]; + Y1 += 4; + Y2 += 4; + + *U = (Uint8)yuv[0][1]; + U += 4; + + *V = (Uint8)yuv[0][2]; + V += 4; + } + rgb += rgb_row_advance; + } +} + +SDL_bool ConvertRGBtoYUV(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance) +{ + switch (format) + { + case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_NV12: + case SDL_PIXELFORMAT_NV21: + ConvertRGBtoPlanar2x2(format, src, pitch, out, w, h, mode, monochrome, luminance); + return SDL_TRUE; + case SDL_PIXELFORMAT_YUY2: + case SDL_PIXELFORMAT_UYVY: + case SDL_PIXELFORMAT_YVYU: + ConvertRGBtoPacked4(format, src, pitch, out, w, h, mode, monochrome, luminance); + return SDL_TRUE; + default: + return SDL_FALSE; + } +} + +int CalculateYUVPitch(Uint32 format, int width) +{ + switch (format) + { + case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_NV12: + case SDL_PIXELFORMAT_NV21: + return width; + case SDL_PIXELFORMAT_YUY2: + case SDL_PIXELFORMAT_UYVY: + case SDL_PIXELFORMAT_YVYU: + return 4*((width + 1)/2); + default: + return 0; + } +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/test/testyuv_cvt.h b/test/testyuv_cvt.h new file mode 100644 index 0000000000000..173a4e2f8e2fd --- /dev/null +++ b/test/testyuv_cvt.h @@ -0,0 +1,16 @@ +/* + Copyright (C) 1997-2017 Sam Lantinga + + 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. +*/ + +/* These functions are designed for testing correctness, not for speed */ + +extern SDL_bool ConvertRGBtoYUV(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, SDL_YUV_CONVERSION_MODE mode, int monochrome, int luminance); +extern int CalculateYUVPitch(Uint32 format, int width);