From 145d2469ae99a588c7853686520a1d59cbebbfa9 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 | 30 +- 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, 8368 insertions(+), 4310 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 636b7ab19..d4c66b856 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 d593d68d2..e70ededaf 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 1be707e6b..2329d391b 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 5564b185c..841a0b966 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 f23cd1890..5a2068996 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 183e427f4..2091df090 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 304c0472d..9aafdce6c 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 c291871b2..f7b887cc6 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 badba57cc..4bfbe8302 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 f29cd42ac..2e1fc8ba6 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 5c601be17..3e2f4c68e 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 ec044385b..d3f34d7a2 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 f6b398a97..b499c70a8 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 5feee2409..b30d4eb54 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 24fd72238..a4fb4478d 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 fb00991bf..dcfd74a70 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 000000000..c5738c67d --- /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 72cd2adf4..4557c94f0 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 99afeb1ee..89e381e0a 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 c0f1179d3..cd3c096da 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 2e9cbb3a3..519d1507b 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 4208d9af9..aada0f3e6 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 b233aca8e..743cd0552 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 f8b556eb6..e55d02e3e 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 510690c9c..ab9ba1197 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 5f2c0a428..cf61acf9b 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 01b4da198..de2d48807 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 90da9f9b9..000000000 --- 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 49fce6db2..0ca9c969e 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; - } - - 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; + if (!swdata->pixels) { + 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); - } - - /* 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 248dd45e2..5cd878666 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 c40f699bb..04982481e 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 000000000..37a0dd7d0 --- /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 d175ebf28..bf94a50b2 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 73cf68013..3f51e5bfd --- 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); + if (D3D11_CreateVertexShader(data->d3dDevice, &data->vertexShader, &data->inputLayout) < 0) { 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); - 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 * 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 + 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); + + 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 000000000..97fd038b4 --- /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 000000000..5624be42c --- /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 3bd00ac23..000000000 --- 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 a950d8b81..edbc8641d 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 6d0bf8437..c94fd925e 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 9489b6116..6e22f74bb 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 bd343cf4f..4b09e3a04 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 77d40b39c..8b3306b66 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_TextureYUVSrc + &GLES2_FragmentSrc_TextureYUVJPEGSrc } }; -static GLES2_Shader GLES2_FragmentShader_TextureNV12Src = { +static GLES2_Shader GLES2_FragmentShader_TextureYUVBT601Src = { 1, { - &GLES2_FragmentSrc_TextureNV12Src + &GLES2_FragmentSrc_TextureYUVBT601Src } }; -static GLES2_Shader GLES2_FragmentShader_TextureNV21Src = { +static GLES2_Shader GLES2_FragmentShader_TextureYUVBT709Src = { 1, { - &GLES2_FragmentSrc_TextureNV21Src + &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_TextureNV21JPEGSrc + } +}; + +static GLES2_Shader GLES2_FragmentShader_TextureNV21BT601Src = { + 1, + { + &GLES2_FragmentSrc_TextureNV21BT601Src + } +}; + +static GLES2_Shader GLES2_FragmentShader_TextureNV21BT709Src = { + 1, + { + &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 1f2667eb3..6be9c2fd1 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 649303c58..2244ec592 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 e79e83102..89c07c760 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 38addea00..2e114284a 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 000000000..966de7f56 --- /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 000000000..c7b342d1e --- /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 000000000..a76efd7be --- /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 000000000..5a3ef9046 --- /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 000000000..072ef5c11 --- /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 000000000..0fe28a8ac --- /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 000000000..bf4f48eab --- /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 0ac70e923..c625622aa 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) \ - testwm2$(EXE) \ - torturethread$(EXE) \ - testrendercopyex$(EXE) \ - testmessage$(EXE) \ - testdisplayinfo$(EXE) \ - testqsort$(EXE) \ - controllermap$(EXE) \ testvulkan$(EXE) \ + testwm2$(EXE) \ + testyuv$(EXE) \ + torturethread$(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 2145c4d36..7d01c8564 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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

w$ro 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�knhFlZIN5)Y& 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

#Oicj?Qg%im$Co()q~jQst4ZLXXNQ~5M# zizcI?cnXXaQ836eBB3pbKx6qd=u0Ef;5z3$EKN~3>WU{pT`&Rays)m)`dn zCZH~FJk;5tP-TapCMyIr*+I}`2cae>5Y^cM@>-o02z6EfYIBtLYIB0+UOuZfI~3~7 zU{q)F^P#B83`2FsIC)-mChrSHU3OR(yO2l7ASKCu!VlzznyhftWKTe4YOruiRZ0M= zQvIPyQ;y|HV^Ml%lrRigrt}sWWh_c=`=K;BP@Ykd9E@seEh!<2ZgSfn#VNt!cPU8@ zLD9`1l->!IW65phyzEYxcyBAxCX1(*`v5poM4jVL|NGyWO}X1}Z(Fp4}DJc=j35OvowRHOL~j(CgB>Z(rGING2KO zGa4R41_`(H?$<{$UHW?r5N|E#kjOqgdUh99>FY5-yej0PXI^?9&%OGRunBpDy`H{< z2MW7%@7G&;L+lUn8rg*Y6`vq~c>9i#`04|zp0T&B?DGEFoTvJ2THnDap4A)?c`dr9P(ne({&d8+)jFI+KKGVWJzTrByq zoZVTxdJa~sn~ODx3$bbYGAvjYBVJYM-z=Vm4O>@<&a!Cb9NGJ`c2hjI>{=$fbI{txGqx9;04 zjKVyXH$FU$dCTH3erlNf+xYLN7nyw^@(x*rp4z#KqJ_Q4T5$^}W7(=G%wH6aY0>_e zwm7N`^EPLuWRY-3FL&g1v=yV19MWElc4rapI*ZWR=6c;; zf_v>Ha?Tw#UHfb9731FhB3Jfmj_k%`o+{^bI0r0w?RRgHUryc+hc6 z=4_ZJ!aa5!ZC8{zUH3do+Xcz{c+h_S4|P%W3wLjaY{I(l;*oote{s*}{=t*-Z?6dV z_kOSJw>`3@cJ?7b)+5PDtd1tFX8=-#>e$5~zWRGiHUhqz~9gj49}aGvbi zZRehIA&*d-5YMS=myA0bMEs-lbJ&&M2|0m`A^j2Zfvb3axAns=kH*8c&v38(1n#we ziO#k!#4qCZv{LhM`#{a$_6di+FhSIO$ee`xv z&yY-b^C7fZ4#PXY!i(Na-)7*R;(-?P8znIz6U)gg4yR z`5tu-RtJx+>{g!BvnqOrg}#tP(KjUT%(T^&akfKxE>81SVF|JdwF@_cIM_F7+<;bN zf?OxRw39c??AdIV^VUZ8an{12U#Hw}SPw@dbr7ZO`ZZF#?rdZqM=9yo=-0`-OtXP} zvP$WpFt=45henmc8)OyX4fQ0d{LD)7FB7!s7xJ;%2Xey(tV*y_fxL%nCdV5P)$7~EdbSN zK?-Z62ZHaHr;e3-s?(`o1VWXe=qLO>@{uMd44Ui-sL2=)RYovMlYE6!$RkpzV^Ms| z8+liUBIoiD*f zCdULy-^YEV@3!z(2#S-!WG;yFLx2A3UqwHmrb1R>?}tTJA)mOtEA*@U>$&ITNS%c} zA@+6H4Q4d!JsS(@?)rbetZ-J^UW_pV<2$i-b>$kQyH`2_yY{gk7ke_=W@un*@Y$mN*O{ zk`Xr^!4pHpYwPx}(DzC`g5P5|@7WRTB zMNN_!W%_H$FhfQS#$;ZP4#$LP<3(Sg_A+r=nCJ`S7W!AhB7-q0Dn$10jF0rkf|apY zx;74r*2EzuK3eu2&R;$U8+NS1!qsyTziJK=wy(gl^>eZPwKcNCaQ&uuEL;|awHxEG ze$yQ6*}oEpk0)Z|)weLAH|XPj$u{eS}fnVO79*x^CqIgHX z6~>SqH1;oLM;&`4?%j8s%awE2&KmJ~xOhbREzAb7pOF5E?3Zc%Lh*mJeu2AA*F22l zQ{fc$Pw4C5TnlqSm^mi5^8)$cs8{U$8 zsF}#juARP8_G{jgXVCA#XYu*$qeu^?nV#0SaHJ{iEVZty?3!!QP3x z$ROg~&@PwWhJiCFobA=FRD7u95bY|M>K4LMy9ie8Qt6@aoVl!=wQbd{Kx^YFG;5c` zp<9Vo!)nn^_}Z>pE*@8N{UXKZTDwSQUZ`aVA86*oQNKVm2b*S&lwGuwIF}wm9V8ak zKOAkESkX@!E2j$=SX48F8EiGPU{+1XADvsx)r z%}f}p$sIFWN3>gLR&mZITlP5?x@e7fcl~daG&!z zb%oV^Dl^6pC()MA@y;a&N))VaU7ciNb3`rHXEjkbY449baJ;RcatAlKfD7W3=!~ z!L@~?WK;Ho!@u8%^&^)bRUc~?fG_@(Tz5J3eNcTRyg;mHU^r`&s-~T3j@~;pr0@dS%rJ`qGF>FK5er2RRYEbO8@_buw>mLtVmcQeq7ExCv00Rf6KZ}^RQvl zJSx8B)>Cc9d))tv2hvj3O+fF+Z@P1%3b<}5~A zYf)F8Yinzc?D%C?T6=q@a0P(Uh`SzA1VUxQpNlLZeL8miGGSqlKOY%)xZ=lVU1@x1$mARKwGPfexr_S%>HIL#;Z9gl=dz`;*Q`p0uk=p6n zd*`0Vz2DCHny;lg+rAQiYA1cHF0SA;VGid>Ir9A9`DI^;hS9-!6z6BkY)boQVE=~N zh?J|R!afbP683N0`DM&TrPd++6lRb)Kft|~cg1_*&Ku*AUJR+GHzlK8_UxMW$?h1t z`Jj?VCfTTODEns|@8X{0J;l@Ecw0V89i-Foj&Ke=Ae}ATu}3CSvRB!oA%n;sJl%F! z^;>1GmU$qOQRd3}aOZ)zv&)#J%6&7uZg1QsuepQ9J&)UL-0bpfDE*RlQzF_qgG*m1 zXHlqUSeW(B8QcW%W-!}>y_OF4S}g3{Y?OQMH}Aq->vk!APSHD7qt&=pO7VQs4@xa# z4V;GcO0UM809Lz&&)FcaTN>9$ktwWoi==GK_RuYdL%%|*rGY-wcsO*6VQ*N3ruuj( z_Hd|&P;(%6STqW|h@Vs)Cp{GRy-m7>Xwfg}>g}-C)1qC37Hzz62lWR_brfvsnP{$! zkt1I>X=b5W6AN2yjP!IY>X|TAM+<9E&mfynKOwKM*!z+G4?oA})Xb9WJn}i#>Y2ha z4drBmaA5*+g1$0Bv=B0knfIvJ=b;}pLbMO+BBI&2`ZCl`*stOD^KWmgQ20Ywqi{Ub zxq+z8^hHgUpD;vSK`0su#z9vYhWflv)aHjM`UkHUO@OXs0(8X_45$*tkSHRKsyE4}HBqHDvY$SB1(hsm>eFIk7a6{(wJx?a0E$E~{* zTvs?H_u^m_Ugh603MDs2O5cdS-NKurg z^rC1gWD)V>zWk!h67kr7u!m?YJ^Rsz+a29{_YzMo=ZDxsda>K9a!w{;?}y%5_I0SK zh!17VNO?^!Ept{_f#ZWQX*%=OrXXTwBmyS{VN}3q3>!0C=8wrI^q0^ZOO1td#?(p3 zE6k;J>nYS&{DXZZN0;w&{zrbdBgcuqmh(UK*3xI|6EX(=)L1x6G$llS*Qd^o5YI04 z750mmH&3pSzDVq3$v~O2cox&o9 zueOsvZpjW_W}hkjn;VjE#=Nt3C-?4h7jK%9_u8JSFiUGP?zJ+PK0}Im^~_{zb0*9C z%yFZ4g>yYE%@@&Tzl@fq^9qx+UXff^XY)1Kn=VVg$kBWScB``Eh8hcX6VAqPw=Q|X z`G?&w?43wY#r~Q2OF4_;-V4KwF?sEpTT$j{J{Qlb&GarzhGQ@_z7Ct^4K!KbL^C~{ zcJ8`+8+OZ^XfnS8yXigIk!RJv1xw@GFzb#Tm=p0?W7WPy)H>=yA zaE5M!!Xos3m^Vp2N2_tYumq3oM)qjdqr=2L4fo@@7^HCxoW|8~=$9+L4*ve@mPv`< zL%&>(%oL;U(P`U&cJo@%J$T+>UXT0D8*$gV9&M&I=rFAo79o>38dP9m5hhJbOP!N$3a^ZCc1>BYAP(1Q7~0ag-sQWCd~|C7|~BEr^8(JZ%!erxS52! zVX2wb#XRH}euhUrlYBzm!&Es1W@;c65mLs=NEpheNKx-#FUeG{=p}qj?Sr}qHIc@$ z$-)=rSg6x|<#Tm80dlNK^O4eI_)77-DplbY-d~k82G!|4!Yb5X_;3r}X9ptn z`vLMkbrb3&WDj~%c>dzcFUygS9Lal^M!gF#9 z&nHX{LwJNT!%R-0pM^zzg^a=;36JCyzc63%rE=HL+@-S-ziPf{DZH1pVEH`pvXE8S z4JY*K0vj@c2Tla3ltoUg%SG;`vd~DvOu*$Nvu~@k-7DwOSf{(t~ zk7I8oieF{_k&W28dleG4E)&1V^tq9e;m>Sb*8F92<;Z={D>p62a_&Xmyh?T{?|<_U z-um=ieE8KT*m-yl<}R6o=(y>aIe&%}nI&QSM(lfIKR*2GBP?1Ik9FHtV9()1ymn|K z_8!_Gy3VZFafq1ehmg<_2nrsK`1nXPG|&s8$HNDWxPQ-phYyT+@X&-uk1cro$b`oa z^?3Y9C;R;#-mj5*@IWK=^UpdwdQ^vpk2HAvW1aY5*(YLVJvA3+OP2U-o2)lbuRaB% z?mSHT3$R*mz~M*|?S=b;dEV?uk$hR^-jjRSU%Kne!Tt6Ew6)|Y`S(uO^^P3bC;Z@k z0iOI^jwe6y-;jYPKjy(^Jdf642ZPV5q$Dm7LQ{PJ z4fThW&#Cr6U9}yxsx7cI?t@0P84Yz?p;jfJLA@1a1uIdSzYY~e2`J84i-OEmD9Txl zyv$`N&RLGi;#H_AUjvCKjO zBU()x(4<=}eHHio2{n!m>n8Di&~r)-Vc(>wp59V&#=jYa93ok!+9l!v6)%T6PVtG> z#7bX+{fU-_1yT;(JlGrJgdylTB`c5}sD-dN6C*l^W}f&+>CGTJ(2qecYD>dHI2sn9 zseX?9=95YIYo)be}S(}@wD z5M4w#Bm(BjiLk0B!B{>Xrpn1MS4|O?FqSJviz*V<>Zxd|nGUOJ8k%cnb`?3sS~Xp` z##lzC2p8Vqvw5$%DndTbXPC;vrOXwRVBz;vOoF+5vQn;VWEhsQ!galTG7QBNU@Q%n z;_JqeiO?5^p|NyA*E0>|t-{bQPH8L-g}F2grqVFs7P5;rF97wq{xB4UKwl69U7m7H zn-?VfP@f%u269bKpl}FTgAAgSzg#EdG~@+9o9i#fx}347&Gvyhb2REQ$Dk%-l)P5X zeUxWp`k^w3eHbrPr;bs$Bz-j0Y3$>8q3ot7%5D#n*X7h_Zh1+SULT6mTSHNChx&(? z9LX|#7W+YD6tW4qMnk?z8H17=!-P@tuM9--H4nL$pXa@0w??4onkR~`3`W5v50u~V z5?(2|G6Z?lXs&vq`1&xET=zoh^^qvPIzn_7@=3)VpROZWCGXNu(N}UV4Mq0(!BUxL z`y=giA7q^yfb{SCA^W@svMvrn+L^w{`eBf;1^p)UsE}1SV@yq>d(WQI17a_Tyuu^( z6&AG?@(O!E?!FLT(?3g|U|)#O7~(Y?9>a!WOn^W9LzwM84x@a>V%SJ;3>d=M=YGO5 z1BML7pkYI$sGsl|?m1_24|#}3@x=7(CjQ!2di8{-_Xyz@>MNcjskIChJ%w3t!|9o2 zZzj|io<75cPpGHRr$Qesdpz7b$lXG`MvaC02BX$c>S$J zY)D*y&D)n?&%p%ozl@6vL3s28(Nt#5S9(6P7sp5shn%u{^D3;`v=Yk_mSg4SHCVl6 z9rhkSAUeyD_gj2c@mtU)a-!=IC*Bbozs0_dUR)b%D zsR5t`(1726uf?yws`1OuRru}KV*K_?K7M+{Ueq}>+fL%ghs=gO39aTR%!YSi(!YZy z)5owGKZ2@!pHx-peyB~`UvXOhtN=a2wL?%sLD2>AZsxS zvlgN-djSe_<{>vN2Dxdokd+*boV2+p&RLA|!WGC#n}_1uMW`rR22I6EsLPi_SF;KR z%^H~M*Ms}x=G4(&xe62m2{or0D0MuY>co zyvBJL&aKcJ%J~-NhH+-4xnY^)o;pqIq;vZwHx#+3C%-3#Dh`&@U2BAY+JMqB=%;66{M*FJMWp zLi!!-fv^{$^iZs>b4v}qq*Gz7j+Ed0#;VE6@2@HnHqCUHYNCW6=+mJ7z?m6p6zr4O z$rJ3kXlBW?t?H<*bLtz~5^5jpN%%us7}Qm|;t=_5))j=v?=XL#S;mraXeU16X+lSiJ{=lRKJ$PM+mzR(o}z)%zfQ&BMVh5S5qj4{Fw+B`qG#$!Vc zwGkiG=lCg=M7QJppln)K07rF)?|V}vjW&lScP33b{?)Mku^I(-Dxsl%a4 z@g1uQN%n#&*$dSvBb3jk@^hoWBfq0QL*bjcbYAn8d#jR$qx8mL zDRr{9oL41z$!B;Z(~xaSZhE5p)=*U5;dhUes=Pe{WjDwggHd$V1I3pWULmsx58d=c z!R7wQIoA_;KlDNFg}%uCp*M1V=!cvO1B74lFAkJr&bj_b`KG%t4E2}%OG8j_*%Nt} z*nb)#=W^}J(5`D4XZuQJeb46(M8=r`$UHYdVVBeWg;mG~^s$mVIKM+CAbYUK!`=_s zgnEkPu>8k=hz3Lbh4;{JOaCp|f}f*)LT!aUShC8H5nk{K41iC7KYW7%;Wc`cyyxx# zvB%@qVyK;PzKDG!_Kw^~vI@Pq^uWB><5ijQ={v{+UOuB_{)aiX%(JDY;_m;j*TXE8 zA!CNgYkF$C_wONk3K@hR6rUg;B^Q63zw7}b!;n9C&e!hiWD~xo*Ou%;&4v9O_JYVL z+#w|1m55OB*3w@~PN7~CI57ahlY%g1=49y$@fvj*dRN$!AwRI@Et@TzLT!b5}L9o~y?FPxFr?|%LvPM-S)?|$|Uj=gsTTXwENY+N|TPxQv=??1pV zzdP{TuO0a9mrgLV0{EM{Z#%UOT)M?|*h2so7Vd(H7wSPYz(+hBzF3Yde0p{5cY~ zEX1^#K{7+l|1O_>{RZCr;5F>nvl^x4$@ukG8-9Lb0)DsQ*I&%?`p2Id@#tX#+S{t} z=wStZeq4#4AC=*kpGrk5;lAP@ALQWIp9=Bg<2k?to zZicabqi_kcHSDHD@qso}EfZGYxut=#uG`>brn-5T;^{E%?CPb6XVkbw`Xmms;va1? zP>a}t7V|baa&E<;OMta*t>_ojD41c+ITX&8FuRL67i0kDb;;fq&0@*e;*MG7iA%mG z_q!ENmkbOOcdyhg6mNz_yBJ1SzBxZMU**` zNYM+(3;bQ@Z#;{-1IxnSb=U8LOcg~M1j5$3Xqu#`^_Rv=f%bBjVy$KUY$ zvBCrH?=Y8#!BR0^;hiA)m%q26z*nv}Fq&a0A#LY?7-+AMFmzcy_oDwBtx zD#cTIKWVtaF)1U2MT)QXL+OqFD7)4VC0F{O=u$7_U+RhC%e_%?V*sjd4w7TVtwF*u zsyoA!?@IPU)twJSuP9)zMx9>O$PKlDf5`GLqd-AA;P+zW%`8rdf4 z>sOHcbvGn`+Y?!5J%m}vEji~s#J}p+HRxr1v3obsLOeLM!D-{3*$*}orxCyYn<)JTMePlUJ680qVf zPsk-?5ON89FZ8(l`KiB4<}KeN|F}8k#cr>NkA)evoI&P%GGBWPAB=&+JtUiU0Cx-e zdP~m9Sb9vt0>oQOCUNr#br7-z_bPjh_C(+~Uj&c$m&~*={@(EP9tfX+k?wGL)9)(TY7UP{yj$rq}L~Pu(9?VRkFJfo1!Whh2Gz;@q%*WCV%dlkqQmos)9vgRW5?$rMTL)z(i2a}+Zd^i2ZWccO?rU7U zbsaa;Q}Dr;pWu^kKgatg-oxwf9>l_BF^Gr?KvZ-fGO{kh*;WsyQ-{_TrJ8KDqOr6& z4S4jZN%HhtTC33BUI}MQA&mN4(AAwqP1Q-LRNvzI^}{m9&20Y9zWx9ooOl->eEA;I z@>5Z)Dnwdd3NG9{k8ghX8cA8V@ZKkHO6K0;RdXfRZqAY!Sh8+DzWL!x9DVN)&R#u* z6Q@7I7pFczcK&r}wVW*~LapWsbh-Aq|?^G}P9mL#NAy)pQ9BnlI60{1PqJ z6RHXNPAEVj)f%sTC z>(a_h)8=GF)qK623Py?~ zf2m}Y>JpT^uLfn`9D69tEtQOPqmujKv}_T7hofONoQ8F1)~}G?dggq}p10bCXsnql z{tEUq%+<4Hc7u7GHF*|vRkK`kD$3jl`ws42N3%K_j=C9Wu8val5tm*-je`G=&wc`R3;w>d z`1{SGX3$s`h=w9x=u7-zEDeOYG6Xi&c-X4LU{{Buxq1@F2=?ko*w_cDoT%s(l@YMl zC|UT?x$u871 zv<1G<7YCrB&<}NapmLfycWh)#)PN62kqx{?WUD>0R_cKR;RiEx9$Jz`pXwrv6l{{FgCe;(Q8N*%I zhCr1(1l1|LMh(PM?x{)|4At#Hs7e|n*QlA)Wq6B@!v8kzXV#?;LrwBvl;7xw{PV9O z^UMnmyZuy+6vY4M5q=0dgeEuz0=bVs8{*>W7N!gHU>fjL<`h z_wydUpMSnPiZAsMCUPJ1&UHu5_pc)3yBCpl`eo#v?S{Ow-H?6yW#pXhCdJq3-#m}o zC!RsdH!ljKkYlpH?9Do#}zJZ@VGsei!&=qBV7_K2vjFelE@i?S9|VMq zhHs#^unM!|xr1k1c#v?(z#;t=b_w*xh%rMk+HZu^#E5ai8qBRFt3=0675-okh zd=@O3i@EVJ7#|rbS#b2!Qg@-JmaM{@xHX$s$u8rV1v7+G)@@lW-dg%o5_YVWp3k=Z ziCCVn6bakcDSM0JrefED&Dgnb1KxOl4_2&=!M0tC@xiBiaP+NB*#6owymmN2Smo`H zj$r4ZZPrU%5bDFWwp|<0KHcBdP!D7yYw(hL#7c@3rl=-3u4@>atFB$-P0F8L^ z;|-`QjzFt=4Gq;hpsCy_UJ$E(ADS8uz-HJFlWs3e4F_PTQ;wY3Tro|ls%Y_1aDIdR6!CdzH zgf{(Bv>7Dr9PFL6Xy>4*j;s;|vnCqN z^|R4jH%I1e={u$0gR{4sMG@bIdZzd_=p(t2-EbOPS z_?z5R8IESvWZ0{x2zyv7>7fV#^$Y$cQ%BGh`odfu3{yq0unT{$sduobH<(I8l`0#D z7EJ`2YLwmzwHmTalPX+zL!QfLQ|~Aa7H!1MB)ms-lG0E#Qfny>>pJqat!g5gE5l$e z2@=i_rl<&!zlBHn`^u=f1PC_>I~4iBQ0ObXKz1+}`^jta1Ftvajgit9j1~5%&lx4> z|j0Jx397Dbj8mP5oc|(^q6532J zXi_~>k>`9T;YZ7`~n2MRxs8LDm%M8(bi zP$zq!HhqX3)v1HzxvD!J@;$X_Ls647M6U6B$sfGN;ys#_p{Pk6DqN#Z8`kyO{dr9a zpF3DS$L}k>+FKZ<@@8LD-t6DSCDdR_ul9xNmWQxR<;{VpxIPf2R|W{HlwGCH(HDgm z6@7+U4PO^t=z)^Uy-|9FS_v8DWn`Ux5qalcMg9-nq>3-~LdBK7Ql*#sqVR{_$UE~2 za?f-}{@I==y4V-_=lde}TyJEa?v9L8-H`V6OGy3dMcn-4AGrP5Gr05lGf4UJIb?kE z5>ih-kK3R9Q?BuP@)ytI&WUGn`?Ke8+=_Iee- z{k!gpwsP_n-1_1r-2U=qVHU;js`P8vk73`1{6Holn{WpZ`$K=DZ{@Fl6=sn=h64wP z?}R-b>L=utr=NQkfBWY@@GSi*eR^ZGzn|zSG4tYtS$HqC8*&c6m;EC4eaI-}6KW{b zY3O(14D{gP!{j^q4e}7*EOTh1=fsGYmRWDy4MZN95jPWYi{lV8KSn$%%w(f~mK;Mb z3K@mvG2BD?Hq1g9IJ6H2d-cai-(gZCd_3VbW(Y#V12A&zaP;cm4I_O<2%}JM8R9ii z_6(C_$SUNW;XXr!J?OEWJ~v8oRXAtEz0RDo;k=D+=oo}f4ifJz^&EO|S?n3{KK6m= zapjzH)SL*>V0b4kYf`jP3ik340{$+AP}2=GPLj>&R|w-MV?xsk6dm&YJt7H}6~{S-aGqHtkxEorkvL#Hr8m!O0J$ z*F|s6n;##;>dng$GC2Uz^QPg*y9crV_#Ww{ee(4O$SS;!jGV8a*PVmi`W>3gC(zRT zo#erCUvZP=41Rx-3;bRr`*V%j{m`isVW>%fp;jrgb}Kj=Yi>wHW8FqHHExH+unpG6 z-LM#T!K~j2lWvC`d5yEQ)H|5h!FgF*!WIOD=uSNbnj zyLO~36}~X3bzZguFq|h*L+6WhQKO^qR(q-;^A~S=-izx$+*pp}dFZZTflAe`%?o343j{ ztG_cvjy!K~n1$B5XxM9~!>*nNN6l1ND<+A)(V~tNRw1LbYNiUa*eVsiAU{w$A%F1b z&`c6t#Z=DTjiN#DNKJx8c2P8oP~i(Q23f>jGg+SDsEw58kZ-7akdIqU!)N`t+`LIW%@Cv_+Wvv`1*T^a4DzXQefxKZZ3J{i{o}tel32nBw zlp$YXj)wfvaz8Z=@<&}I!qL-sJ}a=iY(GUSeeA&1;S z-^XyM(}oIum`QfO~913mLFpxvYG2|Itwl^BGMkt?29Slth`C}kz zk_RZqB;_^FS*lxoP<6X6s&A7Ylv3U9kE&Y(gfpt|cnEt`-Wnjh!E@eMe!aIGD{l2g z$<^M%J!BL&r;ux^slnVFDD0tVDE)**$Sr&x`Gdv&5H*sL%YB7Q*au>gq>n=mA*&Q!>?vH5cjjfOq93{=_w-B1`u2G_r?x|Oace6@=lck=WS@E! z*{8apkgW23&#t1L;y&kVIX~M2*=M>T^VBQIJoOT?&%7e%)L7ELdlea{dLa8;U*vq> z7g?u!BjdZC$oQr^vZD z)ygc8YiAIzlQGCEBYehSY)~Kudkq)AD>@FhgZ#+-!t|kH^qaBV^~#v#&fao|NfxqA?|U3Zi185IT9B z6um0+sL)@+{ttDPA>KoT6UY{BPN9y%OxK|Cz6hHfgfRgl;4!qH)c8r`5H%|TQ=-_r z8YcVyxNA6UQn2L5d3X(wJ`lNseiV9B-2B1a%JY`akv-0Xy$1@XkX4u$$0PI9$T7^{ zXVI_1{to*=oG<1c=%9)I!ZJLk4#ON=?hKp~Jx=m#nOVDLLkzay##lbgrV$YEsqN%)gbTs|$X31z^FA>+iuopc?{%dt+M6MEH*zDY?CS4{ecu z*9Rw#Ef4ug(8op)hvJT86^lkP1vYTtlPa}13Q@55$10bAof*bIAQ51iz88Mi1s z67HLoowJ)|&ef_{>>+zS1q+@HeT_Vjl;467ygi#`wTl4UL!-{Z`RrEVn*s>LvCmdj39 zQ}trW1*5NnyHc3HA^YpeC92ubSIv?v^4g+^E{J{V?&Z5wV z!BW^DMwwlyoef9b9N`vnMu%|;I?cnT3d@^D4FP|^DuA@{MzW2+() zaK>e-_(Z8~kU#i5_G9S%6dtLXr1&*TL&YN^9uRs(s=|aF$OrD82)Tkx!1Lzn@n}+o z!B!ChYk9C}3npq0WkI5ESa>WAgtaV0K10UfXZc*R1^YMb#RyXr`od5&Rx}H03cO}4 z_7{%_-;?L^*{*VbhTp;OcNgDx>mJlAgdcK8!CLGGbKzLw09_6_!4o?2M)ol1bG=~5 z8v$d%DCl!Xbn!<+_Hfi?c#3YpqZD5rN_eNcI$2WnFKp*~{>^x0l8Uuv^TLJaj2KD$5ZtGsCvujIMOUzJ4LBfD#a{WW@d)6 zU3Qh3nHgkeM%hAJWQ&=ZSr*G;$(ChiDCnxDnYlA}efPsV)4jj7Pbk$rKi(CYkr9!p zsvqy;efHU>Xds>xxTB|tbrpAc-&rsKJ%xN8y(EMBS%bXtynKYzaahYClPJ2%aHU36 zs?>^D3nHuVdh!W%6*5R$j;pXqSMC7e6tW6eYlai*6RlC7Vk_6QX1Ss{-4#t~j(D8r zBCOJxJ48Gzc5hja*Ql3M^dA4L`j4-O2e&Co1o;lkq6kr!*@iQ`$2GVOalR9f*DwYB8LvJaXc2O#N<>3) zeJ%Q0kzO}pedECKz0&)$Y4lDsh*nvye z_QChw8H9vUKRJkf$2K7}J`mB#5eQEXM{sPg;pP^XZ+{i z{|kTn`+wo@_}hP5{|SXnfd3MH(O<(O7kTA>|L6aMzy1CH0`Pz0Z-4(6czCSAyzfV2 z?)*`h`@>``TRmUq40!roL2PmeLZkh#_wXjk_&;&!uy|6g+&zoq7Y}0R;Y~Ps`7k1q zLJ*%3jpUqoBxJ_O`~62YAu#L;Y8xZ)?Q0^w(ck*nOKLg){zuM(dDx%-&ir=Hvtu93 zUw&3*(=xZ?fBZ?wMyJ>G-~Z4z$A#Hpf4m`cUD%Jy9$NO?()S@5>V5NPseO<|r0&8j z^nZHKURo}F&nz_%=8m!Fg#9UGlYf)`lv9$a!CVdIbTDt@uiuXTBd_DzA<5=o4-A=v zIbO`}AY1(FwTB&?zQwQsSeBDx0iHs0@$ zoDDJxpTW!yvWn!AzEfC(Jud%^3qEWH*Pp3F^u3lo**-4eksi^%yj`#C`LEaFkFVB< zPN8^3SBgiKngy9c^pF>;C0mqC!5Ylhr_ASCAiW>#|Bzl5?)T4^ijF}}(D#^7&)|9L z5M0z0=%HZ!MOljflHN_~7d-dAWs1}*$O_#5sW<%oVu>u)b=c!dCXxP`rwfHOzOla2 z=V2vt(0}rWmp@1ih3vsP3itb;pU%clPiD#b);$xy^~}Scd2KI!rgP+)U%Kbu_h<9* z$KLPpr{@dg8h*z6zO+xpx6bMK<;g7k-aA*;AA07$AB#7A*S@YISGeHSqe0558Z1olymE*z$Fov5VG44@i?RWDTIhu5CGL1xJ*1y? z$Qn_S#4874KF?gB+Cp=LrB z`RAjaPulZbrOwlsW-mO_k?kr+>L_jLj{UEjlkJt2#&gbioa%s8lto&g zF3}l{DQ>8VcSL2hEox#N@hI9JRZ;e+i+4d|(t!Tg^+_(mDkWi-qFqqGU|ol~Dr64n z6SlLH8W5R*T*6#9)`j#8p`Y_ZErxy0tlQ9QYwzkJ`pS0;7f9V_@yZpVsaQGK3&ZeP z%wwUKmiJL-;WN4PnJnZNK9`;q_WdyHjTtI)e_S9LaIF7KoH1GYe(0rj9_%9h$L!M~ ztI)r~EH~x-LgAH()5Z#`P($I|J#q^3Tvu;m4(&Y2f)@svHX75vn+2q`GM5o4!--MhJ z8W)Xwp&Uwf@|KlB)>MRBoDb|#YoG`MOa)c0>VP!8xn~74+BwAQ-c5Z z|N9RB|LdRD|Iw$D@W{{f>wos0r-s7M^w;tIzl*NIp5?9E=41YXF_<-HBxcMVi&g7? zkp3rXDZ$}h;w$zzxf6aNw`86YXA@lVJPohlYY2?;f`8a;B;`b-s4@#hkJ54N&IzpE zFcVj9Y(rB^1U`RBf@H@3IZ*m>IggIs59WLDNbd*NfBfcyfBXGo@px2(a?`@wVU$7O$vWOnprhKVQS>q&6w zGe?Cvz8(}`=pWwg>i3BL%eO;P2cak9Pak=Ht5WxPzpKxiv0XSr{1$Jw_2+Dej=?_H z4_hTqL&@t>GD9V&tFIrH8C}#PxR^7_><#wdl3V`m+kRQ(8$O3DLQkn)tDqjiJ`pbZ zK)I+laIqfp2llD-ERomrivG*Hby5!@Q;;Kgt_P0&N}YK0tG>!9`^%a! zGhb>MA6q9X`+ED-KHtMM@m#Rygx=2Idw&pr2R$Bq{nyt^rIw;-BlMWe#n&fu@VRT2 zFbKc%W7`ybYMX?w?Gt5vY#u9%`@ihjJho24&z;loOV@P#(m4%3wNDXF_{{o9>v*~T zbIUkc zBk-nf7+%*7#k+PQ}DW1l>_j)${jCCT=2Zu8NEeL=qYdz z4tbJik0-gd=*+T0Pk|$z<~!hNzCF5gZPA`-DX+;ZeBYI0E6hUX>BzE1OPUVt88))o z(yh^+WrvPTTRcv+LSuqCYGc)COw^(!)e@aqcIe7-z|&l3bZ0xtYyFY;JS%V!e&M;+ z3|m|iq@hj#SYc6Iy{OoM_ITUHF4wuOVlM; z$>$Z38u?80lQ=81q&VPlsy)ajkCW`slxUAedQ74%Q4?n=?|E%)ycOyb?9h`CT4!g0((nKcP>fyuu89dQS8iu?v>X z!_4oeiPx5SY^?8a9w7UbsjZMzF5Nnhlb4Q4?=l&NnQf;oosilOx#;}$v*N#C-u_LW z3-AxVE;D%8`*Y#iK6xDwej9<2J_w5TM{If&60#GKlAnsugfQ`>+zSsxaC9i5QsR(N zm?tZ%xByAn8A!{|5r(0T!WEa1gv9Jr6xWnX4^UHQ2WncHgj;+b1|T9a9KA2iOQ{-4kB_rJfyfBol+zU!Dp!u6A6k}zZK|NdXik`?Q5 z=sT=hGZ_nhn1JbX$7B7r{totdkQJoYqkFFO#j+2T=k$IE-pl?MvIZA5 z4048eEy);9<|w*G_ZVFV7b!`bEz?(J-iG@LJX^^mPj!S?^%&gTG6^{_u3J=pEEW_&Zo9 z;qT(}f9jecTE<8ECt4=qC;q06Y528kI)3e#EII@k;cLrS(IvjMvev=c#%O$Q9Ep$h z!<9X`ei%MC48iB7VfffE1fLp*D#!Z4cwgfV9zWC$#D{8kys2`*i!w*_6xqt^EwRVT zawoif86PIy=4hBuW1@cz+2e6AgaZw({yxo$Y#RSgndaEp(Ry|whRkprR#)Mo) zXic#~dzuZJ6Rl7eZHDRyH5%h|Xi2t2bCLy`5_M>ZGsEL#9oo}v(UfS3N0Dk&JTydE zumOq!R455DKxKp}D#J`r9j%qu#leOs4^<1l$Y=aKT!XTQCUUHbFhgyO1s+A}P!*x@ z4!Nf&&=>{x4N(|iB&#UM2-V>_)FoJ>D%u>SA*Of~Y9Z&U9_mmTYJrj<4T?idQU1`Z ze-#Fq3ZoPSnV}%S6a_(Al!sfRHr5`M5mqP&RHGz%uju`>5GGxh0HVV?ey)yrfK;n1|jDm9_OqXuKZ zU{_3^JyqrdQxn;^V>Pz!S&vm47Gm|rAEdr8d){QsUZC^>k!PITZ83PHo4g)2Y5=Cs zo`4^h&c(P%!!csqV9{SVYnY2YLYzI!I?>kM8^kBUyjl8HSQFxr-dNUi$S8bI{|Vny zhat~Uf1&n59p%*JW1_2^xNuZhg|nUM#bJ;0xoc;z|HK~AR&MxS6kX-q)q~izf2GV= z_VhoC2Qfal=6wN2FC4^8ziaRg@sXSq&ibKGmKknjmGpvK6jYWWr@RDtm1V*!WR`-e z5@Z(VqP*b|s$1$&+t!Fjj~meb;u*T%yh7`}x9iKjx;lrDBe0-CP z&u>%k?R_S`eayh;cd7XJCLSMN$Kk`fSbYANh%cX#@cDfTzJ1QY=l5Co_N@qCKIh=W zyC{5o8-WjRVsYu*LM&N68uJ#6!R!T-arDeSY~QyL52FJR6z+pN{#WJi42kxJPrwz) z=w)BCZ{T$tIlB{=J&(ih!6l^TJw#@nH%iL=(A4COk8gwU?cGiM`r$l&d2-lE0P8F^@ze0pO4@#pFHrV&wCZW#@k)e!}{yHy^^)@$M*+h-U>N{xg4BJ|J$40 z(gVZ3Tz<}sbm_}|w?lj(-(GH2vPR$RP-ed}1O4qz>AThU!RqIi^yi1Y;=I?@eREef zNNt4~8uWzdvoz?bU=}#}L$71}@@%=RKQYIHJ+r-v?}Aw#tc@^Ni@m1$epC7;sBMr# zSc}m!3iG|lF|1`UGwe&xkNC{Y&!*kV_)tF*AL~Ylwm{v2Jo3-`HuvbBLVMu=vd60md%US~#EWt}ysC7- z>k22lDtAOrp|z|hxjOU~TH{%v1v;}$(UxH>t0~n0PYQH$E$`8D%iFpkcvCwV&nw;V zqDo;H@&kVZf6IqDa>Zcb8uAA(G^BC96GKi>^FNJT11vlR|sZOsJi3@mXE@cETxq|3Y*X2XtmzqBUJ7+6h^NT=F={ zOt^wN3m3VBpLJzhgFM6ct!d_HN;X4dk_HX&YBVKk(2;2=_tr!kqK;Zhf*G2qr=(cm zaT3|U0FNRK(Ga6XV~ke#q&CtFRiP$minByxycTsarl^TBL4B+S&B+SCG$oj$Cejp- z<8-KxG(&Zy2DMSWIhmp2X_p8eXSxiG^@UNr;K8$}n@` zmg*=AVU-*o74q*H2zOM4YVjz{9Hn7u6bBn4*H484KSLDw8X(_Cg&ZFPWO?zZLcYI| za1L1~$KMcn_l^6xBir9Zc!e5^o?{9FH7E%&N3Nd<(mYkj^)nSl;knXaEviDTQ52{{ zzP}dPz8Yltm?HCzTC@xUwMqYcAND4bSM;?S@vc}~iPoXihx+uBVFTedWB}|O?cw0; zDEbOD7p;|r=p?qzPQoY?r%w}2g}u=9-Lk$zp9||nd>(m(N1i7au{Vf)&H5Z$&i$dL zLRR7H@zW~4<<6Nh8;m_e94eGJA=9wEBQ(9!NvPnz@n z4AE@pV_C6w2|P|5kX~al2m63F?b;wdUixWS!(si0S@q1erLMB?=x%J>y&l^SD%tnU zS~+p)nD}8=Z&`&sM|NZF)^#!`;NXcvxa@fer>~w=e54-RWzO-jbNjIC&_?V(u>;q= zFN=@%z={3Xb!a#ILi~kQE_z;)Oj`CIpSp4m_rih@m7IXsv}A-Q#UnT-5($}UQU{7n zPZVt>y)aw!l#0e`lvbCc^+_jsUOq?rlP)y0H6XV<4T%|^sH?t+U*F%r?{5y{FYk8C ztku7K*eksmf8iefc9+bD|MLe$H(+*_z8{0x7W8Vej=@|l&Xaxr>IS^-{D{TNM_|F? zN!WgH3ohM0hls?7xODRje1mTyDk&J@alW{I_aycnUWF?+_aY?h4ARoCASLw}5~4RC zH+3~$b{xmI7asWS?NR*xX0POsv(J@_9*zI;P4QZ=@0Guay(Y{Km;7+yf(`vTgZL(2 zuN7TEe3)d2-j(9BWS=N|J-DQI^z{nySaP4&FMXCLGqmW@q@R*KAM6F;YkC>tlAcNSi$41ve|@tAe|q^NetY$!_#^1q z{Oh~rQkVGi%f+G<@ZbK_DWor>eTMw|pPmOU>JZ|A=<_S^9JL7UyWGn~6KI>NFaq^~ zcIpg@Z<78=dLQX^q>qy8rw;Dn6Y#ZpoYWRrzu;caeg9|nkv2~duLA22tVytbAs&XN z(Mrvuc>=yQvlcQLzjjYmyb|@>+Q?XZX&xi`#K*>w zqE-CbISD_v^Y@I9^=tch{M0fApB|5tMg4$U0{s?T-`d9FL*sCKZXP8X#OKBl`1*L1 z+(TXBW77!b-iG1*`#SXje#ZU&OT%zkpBt!oxZ_QY8{R&0#k*=ZysdJ<>oNztDR)F~ zk)^CxrS|B_w?t>UDc+RW<5TS*e5oITceMlYw#HrRE7TTVRXE{!slqMKOKs%dm!&p% zR%9d0z&Z>yiH|jk&O$xqb)^g53U3U+n<`g1N6vX(Vu#LrD}{ylzQ_hov(1&2XMtz= z*67KzLPxr}?Ay<|{}(yQUQYI4jYXKF+*RR#e0w~}wvzRx*a@$T9OeGr0vmMYSm0^C zHJmNWZYIG?WPG*ZW$xZQ;i%iEy{xIk$=w$ z#X)w+^422%o+S$J>yUrX426DLR6Vd0t)%RMrPOlDA6lU@+!__3)~E`%MRA}dvVF8D zxNnVoe`^#6+RHJ=#{wC5HL`Mib;$FxLegzD;;tDZ_No!$uc?uIOPPhtS`2-(^rVng z*z-gGD|yAtQYXB^S`c$isDW@svdbV>S4H~RM_W3R$*T< zbrtsfEL*!wxP@L7&io;Nu>Y8~9qJ!HE}xIdv&LfP{K>MYr?B^j*>KEUnfJqNc|Bt6 z5aAVSDO{`tjhVpyrh$?nH(}}+@xYLU*f+#JXZA9aKTcmhA$}Cj{@H$Dn|NuNQ|od1 zkj(w#3_xbFc$_{cvw}`tIU!m5e9t+8FAlj0GP7>AVn z97LxjOAUw_D$JzKF3m?mR*Lj0lUZ`h3sK+t7|-9l!n4=CXz8d$d38LJ)6Sxy`WQa+ zc;IW#R{Y$%0lz%woYVFA*1HiuJ>Mk#CG0Pi^?Dn=JX?qNUCTv3kUXuInTeJAq;f+KvzyE1d`NNn0ZAAW%+;U9Pu@kv+ks5%Ij zFE7BY8{Z*5Y9*?QcHmX#VSMd9f?r?l#~)tp#xF0GEOI@INLI$%Ey|3P_uKkC8)OjH zKJ>K^$sA>m$D1|6B0Q2i*!!VmcG5FJf8{bck~{Q$qxvH~7W`a}FPHvPRt0%wv1l92 zogj-y<`uIwm}$lAiYJN|K|Mlh9(_HY{I@*+?M}wvIsW^duX+5~JVlmV+cBeGx8VKy z>-&2^>AhfIY3pRs4#bB^{h_ZuK@EVcA-x=ZUP`&QWg_0!jl%1i5yBaK&wfv`g)l_x zBsoX_1bq-cw@noO5Wi*PXyFjD1^MPL9fWzUSUe4(QG_6E5h;)QBeXy3`S0Yuxd%(iQJ3Srbv}Hmvh}YZ{5) zJ0?giNc5312Yjw|$ER9X;U2Qa$69y1V}0k*Ks+yY6#amD#jA2xkSCPojJJILqXGD- zX}CQ5O@#~IlsVyZ%|N^@bwqET6`ti<|H-RQnkVkP4Q~f#TtvwK+WQH`2f*P z8WK#<7;lV*SYtHA83|+b(%+F|jrKGxIx}?gTc~GrWm?E;PuHL&#YFimLnqe>nFDBCx9cdP5OSV9lcth-@9>eE6PSgpL zJPI{JZG;*PtiObspdv(t$`C_gkIG;ZJPI{KZ5Xu$Q(=+v5JOZxG?rt<17j53SE2a6 z0ZIdngf$+8X@x=b*ONyoADE&n*hH3m9%&(31@(zSUn3OzsZr!_g5vuoC=E14(LG}n z`WqqN#}K)`hA0X!RoLT!xm;7|XDqA8UyVXP6Xe`6M7Ea!^8HLu9AJjhKnvvgYec6g z^fyDkuc>fE!F>&i?rTu~K!?HrGvSDAUqd;cA7GAj)>`hUk>_iUTyJw^+%ZGkB|}7? zH$coq1H@l8LEL2{q}~InDvbMom~p;v{zT1#td=~cFJU_XvE99^6+a?B_U88sB+ri{n* zSu-$k$|Q`RIswDSj=;d7?ie#+GzN_r3}<)F^l=xhg&8R9<)LPx=qL7~ySNV?D0&Qi zE@U5a4v)N#^9zTL9)>A1r^{>~dQ_&ν-##)@Z_zFGDmGc$#K$jr;SA-izS z4`0*2LMC7@(4^_(Fnr8l@wl@7GHbzf@su#Djk*Uj+Vp)xtV3Te{VuM9 z9OXUvgxM?XX&yd)sN~DhH_JNBrac?5@Aw|++1apt1GXL9j^#YRYcmcUKPXwT%-rX1 zBD*l(e%IlhxO(R@4xc(GEW_)m6^$G}3L__s#=5N=iN4h|BqA{@U2@&XA@{?B#bX;16Nb>( zFg%EUDE?YrPY(<0Ma-$~dj14Yo_C^=rEgp*PdskuH^ik5Ye86L) z^k@6tJBswo8%Rn#fsE86NJ-d zn!j4E%vyW7Qn-Uh&N7jCCVg4w>`$eClR2M%ez!^(gEbRob~2m%Pj8lr2EtwtW^~b4 zp=S$vC&&jc>&M|;^JGP{V4p|VoW4xWxuQ{!5xD1ncsyD7f_)isuFnIZ@6}-Mh2CGu zItQ~R$PMh1;66_#c+b7Qj=i0ugbm0GAL>VgT7=@094+VRk$l%MQgj093tZ#~_Jy*i zlZ$;IeVg zKyL%@<2hM+F9vmqhT*~o)GWBZHI9<+;qg6tN6Xx#PVl~(euqKw?|9`nKv;l0K!*6z z^pD?1F9*-l-@zlj9ApW3&ALXlqAjrQz}gDE7W9R_s&WOr7q7U=T}7MV^?DBB^SGx| zztAt%JzkVJ;6;fOUKKmxO{uHAr&d4>f$Z_J*d8CN2I6DYAiOIdfH$Qscv)bN=Q-AR z#rj0HIXcqRXiw3|>dCQ?J-#d59Brv)c#>y>S7k1EQ{mRn8hkF<;#H9yo@8m!nXW;1 zwoVx6J->&Z)I2+>U(nx5pX=)~xBl;cQRpDNz~B75z+S$mE!kAo(=1Co$*@FMnhs5| z#%PQ&LPxTNtj-imbfl7F?8Vzbf5(#?JMn9fQ|h9OQ4^umJ33O#(VA!~?9!ZMD)klm zI2z(LXiPBe=O5}PT^UxQiBK2m&U6w_M`xN1o@P0qE!h$s>9(SGR6Z~e#;A!fLVdIv zUFo*yOtVI7l1^58ik0XYtglcjA)8c(8KWjljfx;c(J4yqn+P`)`WYhM+W>i9#>l&4 zgxtGEDD+mNBtR|vP#j<+I!8&Mv2qMh_wxlAqa?^oxT8GCTvqWt>K!>X3ZXRIV)yutP4{f%-4CyYXk zWbcuEIC1`z)Pr^&+9A352Tw8Ef3tXTW!BKz6@D92!b)Tf1H1W{-KDdvE(P79g&OuH|E+P^l5fb%4GWD6O&px2arW%w~7oo8^ z8&5kP;C1&Ue0{!0@+JPuaUnhB3teddk^sn#N%i?Ud zZ_k$ETkm4Y%4F{NhmI9!f3zMuH)*k8p))*AY{rsR-{I z5zn$&KhQ|cg8BgU1Zo0pNg8yrrV($3#%L4S)5!oWu_owDHbYOgmDEa}=h~xt_s}Z%H&qeS|S;!wgXss*?SloKPR7L0gh_KR?t)t5L%h zX(~047WTTvSxOCr*EYnMitmDc(&i)^)I@4g9btxsSo%qA(41f`*HW91T1li96(L&T zgOdA3C=F0JLAW8%R6L&adC~(~@qqOfT|ZA$J~Weah4R8fU2~MO9^q?=oVylCyRJcorw%!HERl6bgY3IzDDbyHo}ZccJ#&0ak?W(Cx=8A6 z6C~d>Lb|6$R{RweVlSzXc-0v3mkmXqphgjU$q?~ZjgWBFL|#XpRUz$$g=iL$XAKZ? zTqWz_Nfn|mnjrdu8lk6Dh&XE`uOrVH%L+eZh%oN^XH5}tPJ@SM)Uv|QsqyfXF#U{)txHb-ftqy%fl2m_Ns7crwTXr z8{+n1V_e;(!j+vW+&N^7oBLF_wp)ekdsMi*LnWWx+HVNYgGRWy&!GR>y9bQne^d*< zW2W#wu0g;lbID|5O@`VE=l;-NOKru<)*3o13k(?Kj&T#mW9p1)n73d)rq7-x`?RC0 z1I(?ou&~i#=*S@$K6;qE)>)g&X9I@~`EObYJ-A$M0|&s`&ITHDGg#VK$-P5H42I(X zC#flseaJhmgWWJ}%y4|Sa4zQkI3J@Yju8!qx(RDL)Kv7ZSp%X+h3ulAnau1rG7A}o znhWz+_)PBoi&rfb4+u4i4LdeUpEG9%O`khcxaf!Fi$u#IBav5DtY0C|88mW`JeRz- zc;ymdHhvEmd5Inw=H>D^Q|C;V9&54|XFyM$#hJ|uv1sKIEM2o46Q)lRk1*!~P%k07 z@JQC#eRvO6Y*;DRFkg-}Abw7LW#z_I*l}Q|@Xv~k%dyL2hxlXIXT-jxxj)R4HFN%K zY}>m-GT%aCL;Gtvi8+~wNly_?h1v>rmFm_yl-87?uC*SOjTLC@s7LG5W|YInG0KR>H1#WzOxIt>8Ft$zY+BX z8}XuP3qEvg6z$|g`x1O;TZo^!7t5S>K3CYHFULyyQTuvQ>CO1{**wXbklu{GUJ^3M zCwep4tMPQE{GK-rWBW5J-al6QM(M|79tHV>eWqlQPksHNK3nRhvCEHG15CqPX(`~wjuqY`d-kwKIUj(ZYFCGtWgXR9fG<9wFA9=K#v7Aj-S{s z(#X6`#Un{hAg{1KLZ+c^L4O6Afc1ix<&H|Ph}1Zg=j;1Sn0>)}f1qoE&Lw(FX@}; zrDl<5C;p1gOl$E!HYaQ4bJilrMbrhTKhO(G{epZ##vl(+3*f%ZXK;`2&a{>z>k^L> zOy$^~suO*H@5v(E-|Hid(4A@{8pM+{TUl-KmS~C9N&SKTNpb}J6U_WKDqd(=mpi*Mp_JoO3( z)JADg7ip?^G{afrp!T5?uAt9?=eT%ZO|(XQoa71mHu=o@XdN1&tWg_ofx0M5S@qFY zsExD~zb17C)-hP8VEv*d+zRzkwzA3}DE`S@ANHsk_wW7WfULWUhmuF?2-FmkZx|!- znxSY7dhH?kngLR8829r5uZh2EAS@7d(Ey=mRLJzQP&5TE3&dYHLF_dH#9T2%!gW)r zKjip0A?AV^!p@o?;D`!=M^p$srN)EPCJ3M=aLfSy9x4PLRmtZeCk^oMv=M?&sPN#F zO7?sG{_cB7+2?t^uZIeLM^p$nZU{dQ6}{sF5VMDo|x&arpg}~!# z_#7~R_kI<84jIAwpdq{t7~&51^&Nd{y9(ELsBm+afgH&R`gL)O!VA}Ssc?yW!Rz)K z;rea^;Ry1?jh!mo+HEMG-P&i28+#0e8OROfgxd#<=G}C~i?9<@p0aIMq zs*>}3=C$2MxUts+H}{+1%1(p+&v?y^y{5QzKr5VceUCA2A2h}F{VLo#XpEZ&jKr%# z9||=U=FxJISI8;k3DG`AkHq*%6EJhmEG$~~113$I04Fy`*g4q>m)JYoVes%lu(r2? znS}-#ovGqk89Y$Dw(Rxcvb3|o;9*0AgG@APnCY}qI~q4>tkjz5)ul#5Rv9>akmxh4 zgZ!}UM>&#R$RlJGate8c`ilNYUZJ;^d@_F8c+ouQVWB3&MXiR6Kz>-cVKp{v--PY^ zb_xTKFJ>?JPB>-$;suy8dm6@1n=JJ%)}P2Hb7zo*nFSf_B1 zJ)h5JZRJG?`#710q2yfgwURHvj0p$y7AX1#_jJ}G=v`pHXKT7v_J8(+ve$z;2>T}V z{sGCV$a4@afPJ9SOOa)xFhRDGWx+nq?p)R=>}7T5I7mNdTdFm>G91y8WR31D2k{;- z%c3XW883?mO8&&tJmq&j%Xh|;9OZZNx$GU~KF@kVXNH~l6zXHNsEgI2IoTR*X?C)Y zvnRA6o|=Oi^>OC1T2ic}=ahY?tWVU(nM!X4drBWCSfVY(Ry2U-1RMGHKQ(~%6bC%b zaz%TJz3lss<809!V=G?Cst0DM2sA-`xCMIB-O(6rgX&Oov?V&BCwC~ivj)j(Pjf?O zhC7;*oY0o)ik1Xt>Cu?wQLqxxO0Y`fHJL%Lqv~43Kog5J}gKkbYZ>OfNI>Ve&`~BH@}L zl5g^8jFg*Zh`DTx=u1Y3IA@5k(~7=8Zg_A)g_!duh&W>+S_Ab3(G*S_Ao{!pQRhtY z@Pq+^j;j!Q##q>ax`EUU4k~&AIfI&k59`ZTzj6{$PN`Q?J$(rE;N$tlcIA*;~) zVqtA5M_xaQddrXjQfrz#lReIZF?zx{xtIFP{2v!$?!xb-SDL)BdgB@_UA;{71L_;R zXTg$%Sg~#;maJMTucv=EL(b2bH%l`4nRm;q7WPw5o;4lI)~*u2F7x`y5Q9ez7dDwP zdxp$MWCk5)PSa~c-mrIb7Vi!3=X*zYS2_N(*T%-tUcQ6(Fjtql%(4xuuyOZRY~HgI zOV+LuAIXMYTd-#H2F#rQo#fonUqb$%e&TWZh}3$HoH;DB4reW#iFr%rO09|+z023H z5MH5v!+8Sqw(LK?PjYaFj2sQ1VcWr-;-_V13OVILTr`5C!r}KI z2)QNsD0x(d%)%_;m6GaG6jl}?F*6B?8OiW{7$P%(^2$n4*V>Aj=EtaNs76b7Evjpy z&{TgFFWV2}W5;TIZCA1+*dNLsP4}OWqW-9GG3j9I9ve17uI<-rkZz&E8b{E)~C`jc@~>!M(jL!vU?S zwx~~_24RE7IOay^(2?qb)+9&SPkF8-*;aZg+cTWd5O0mTIHix0uNxC=#Z1idFbnCgWY1-7jFsd{FlVA7%vACtm>EHjB6};-JypoO zt3rmCO8O(27r~y*`dC}(mEd*U)0s)dYzSsSR7G0jQKVMs@eEe_Dfk(4A(;QbS^$sq zAQT5#$l`1EU{V{Pzku3+^i*6kLiTM9ay-qDa@|n00rozm-7-Vm6+=W{R3Y{a^LS;yf5jh3hB3vk$2Br7=gY>GD4=82AN*w;!$J`f%|&oSv6uVD|Ls| zTUx~*c}tDN>qeqML|;%L@`8bILevFgsWrr0G!8KRxU-*Hh9T}9VSU3`n8N?CF@jHM5q#Vfen*TDaKaRRM-Aa~ z*g*I}m}IX3d=IH{cfT69_ZW#@!6VO8&mhCxCd2Ge36J<5Hi7>UHSQnN!1t(Hc;xn8 z1BEYksBnFkA?_YD!_~gmclMDzjQY9b$~FUB*`mVbK0So`1#2bLG{{8c6+Vx(6lxyS zHZE^d;p%onxkv8XqQb?^D)2}h;^saLypLFk#=$+G`~Bq|hH{>~aczgO@CuoN8p-9Y z2DrY{SQd22)fcTc^UA4Ju)bOI!K3sB!nO1^kX!!*idRJcsx5 zyLn!;75@EuSnE;a#x4_K88Qs-zr4*@xQ3rm_hC)Q?}Qn=kE-Rr18YXqP_FMY5FO?2 zVKqEGjODs(dkmp6R6%86Ad6Z0T*js*Fx6^=Q>^SPg;}Vxu-@a~Vh{HrZgSpOV+574 zN|vG8K>RD@lwl)>W8CD4{k&rD;sjHjR*v+k=<7;DM+}pmXTG02W19F?MvNPYVWUS% zKN0tSdRWF!nS`mcW+-)}aU(_JpofOO-uZoAmf7FW!KewNMf0E*!_TPEFpr-znaLh{ z<`^|`ob)M?H^?CToH;Ih&Fie4>}6H}^Xs_uS-hNc#F>mE#*G!m;q`W|PQq16_R9+F zJ8}qn4j%x0Bda&9#hPs!uzcfcY}~aSa~J=Bt^0Rk?RI9StQD<=_kH)nJkfeM*NAK1 zvAyD@VMg4NwTrQM%_7lHeptCsv=#OPv0s~d4EvwSGCTI~fXiSPj2JUac*SX;6I_S5 zV%Wry*nVi2^eM-rB_Js;13}Rd2#R_r^OF>i@Jq))wm5kb$jrlaU*)`mZ7(5 z8s64V!kfC$c=2es%o*dXu~#)CWhNMBsVcKhnd!}J?ol#xgReg}j+gbRNtt`f3@`TG za&{T#rhaG`hxfIkWu^ybsXnjZ?2*Cvz-O1Zqcg`rSfHoS6&-nw!XL^^5jV-{rUoIo z+vEcNJ3o)v)Hbr;w`bX-Gsh8)$rf_%Md={P;^w)oTqpUQ*YNs|YzMUGIQAcTt|5{B zLmeI`SqU>VQRh#wk=*Uh95>0xZb%|GIH4)o8VyOh{_o`dZCOre)-!}KhZSmL%+L_W zyazkU&t`^pO{}%#Pu9g+qwJxQ?^O0cgW70o6bG5eV&>DMaHS@|>`dlLN&dnEHA)_6 zB!lJ+A@`mc^6%@A=Wl^*A9IBzZfTI? zYbkp_IfIMLLH1zZ1ig(>7nOdB*z?ASJgq|Xc_W0LR3ZGdN_+~`2|`aABIKA#I3eMh zR=9z^5eZl5K~Tx!^;|I*3=wsXo(MzHCa6zHP2!{qA$`1XXRk^YJq!2fUto=c+;Eh7 zg9;DNDCd0+sKlSZ+J$gKU(JH&sZDUv*T|X(k74Ic5pmHBq36`XAwfqCggvM=&?7;w z1X+aZo`->aKRJYY1kbS+LhjHnzrzN&cf?58gM1Ns#!~7b{N3abpZ(NH$P|6Gl*7jG z-e&+W{(bxYp@*<$LS7-4Q0MSIXoP!*)$luL3@<+CkP+@3HATR2GvN)szIRju?*qod zE@TbXNN(;{$-Vqu)?oZR)S{D!cGAZ$p7e7ZG{CLBDqQD1y9{LYdpkDK>tTo+I}LDa zhkp{GSH6`*9dFcE` z6)tX2Scz=J#cSlgT?V+uzh#$UzYavLi2wcQGoi-hxtIU#HB#px&-DL>oyva`{;lFg zIbnkPr%d5@%mm&Z#<+XP5MGCkX zwxBMZMq)Vv%@Pm}5uZE%Q<6MIoDP*}EBQx2@@C7J6)0H)5TNo|YwR7GcAV zby&M)Eo@zE<^Khlg>xNU2D`~Df-ASLA*ZZBytT}EqrMWA8Y{g%^xV>WOO9bqEoUlc zl;j~SF%DkAf#PjRFDgJ$%_HPhmZQ158%>>^Xy|A~`?FT`zG=qu7e#p16^gdnlV~hi zj;_k@@U(Kg)Mwt*jY3b^K$#WxynK)_0B44A9tQVi&ggm}^VZ216rL21L3_aj zbQVs-vx@0>TQ>{66=U(FWU%lD=YDZ^DQAQ|uNW@#OgT@zr(`HPa^2CMGXPJEhRVzg z&bD|~G*sSq<_$z^jw{;p2B1E}0WEoM@|y2uMpwF%T*Lc0hm`Y2nL*9PEbGQ(TRBR0 zb%GT-a$L}wZZFz^ULRn_BWG(=M(QN{k?+gGO;H)8l_RsQB_}e(1Xavn3Ra6=!2P}= z!W@MU)S?}5hC2Cy|F&0Z{AQ9BSr)2A=|eN*2b&=0zA-ZWl$>g2T9-V~isn!gVP>TPm_R*u;|7D)Cq6&?sXtL)e8 zmrS{BB6}6AsA%c&o z5X`;)gaIC$R3Yq)O0*1mC-okP=t~;u%M3ebi14!pa*Z6%s-#CW?h5}qYUH`Bn}nZJ zA^MUb;;xz?{Gtj`myMC)X^qGWYSA_L+y|!&5qp(AqU| zPShnGqAyz@^sFg@Pa4SzJ7(Bmf3 zj}myySb9(B_n@yN?7S91Cl&t(xrJOo*6?9XhI)*LAp(w>;Qmn~gq+ey?+N=)gp-aK z_3I@;C(Wh5CG4C&ZprA$||F88Xk^eTMzZYrm0jj{hMO*?ak$ z$Vt>>$VoiXE9!GZ`JDSRYeB3D(Kn)J5VFM8UB*%i^4w#JJ9{;_O&`Y|BZW8mYD}y% zkrTLhj#?4xPIvbjZJn@m{G{9Wa#7cx0`N{J?v-{EwN* zk^f!!{5yM$IkFl*s_ELgq-i&n1?E+LQ1`Eh~Ndp7Of zfxSl$!{f|JT=Vk8!IQ_Z>+k{Wq4sk0fM^%wlqqv(2z!u2$R=C&ZNaL|D}`0aE7Vq~ zoh)9xL~2EwcWo7~&8m%Sq+dw-i4N?h(us{4B1|TpxRQ$K81-XbyO@<$51SP~Gqqq>I^|it& zl?@G23#x5>jP~c(_tPT0f0K>ZJ$`7e+>DCUk!ULzhL&sxG-cSzzRrL5*Ha%z zw?}KHqwv7v3`f)?TFGoJ&ey8u?1^N%{`s6$an`6#u*Rc!3sl6Kp)uVK)rl5p&T>RU zsvVlr9b`5apHUvAMbSf3*~jaX9AutWRivfNt|*HzS5}l36)`%LMw-d7Hpv#{(dNhr zGDJy)GP8o$Rz#Y~{I2R)3(*cZ2cs;^47vAJC%+330G7`xS%Z5 z9(nh5qEn=J8YAJR5t42iiB>>Ph`X#3Z==%hU?_S-@-3C*C&XS+A?>ajdG{@aHOLIC zFOW}?uA55FL*xaO)FD`l;Qq?R*YrD*JDB;zmF{hhq+1%qT;;y2XeR8pOt@)`OkWEW z1o3y;3kS$^&KgKQL+TynJ|0=0;NJf5v`S%_D<+7#szQpVR=g4XP2uOcFDv^$J(!8t z)N-CWNW@v?J^MG4Z)oIPnwJ^kZx{;WP&XmVkcG%Re9w9a{~mfSSWn?={d<0Y%q8}e zS|j1Ax#%T4CmeIdL^Kxi46k90CC$rPax3^bb0;!9t>y2{yu*xf8|Clyv=A;z{1FfhRS>A3R6B zL%cNyjSzTThoBP{at}F-jB@|DS{C2)+5DWEkual&2L2uz-09;u*3s^t)WGk!F?{*o z^q2_(PwRvm@3O9U*hF}b9H;LEqA!H?8hS}|Hdf*ZVgE3*^vN0Yi?XMfz7a0^Na-hK zPY=DW>=|aC%(|`XBoCh6R@P%2T%CnAIOAvhl*t%AX0$A3%IPz4sjo0Im$|s)7G~1% zsGo<(EL&tj1Vg0s^ zQfr~Fm3=szhqz<^9&F#Y8ymK7!NFrkW!B=trAs7hm$Qtf%$g}P7#FQjuBX4`z%dVa zoH~vzdv{4qhn%u>-D=^G)mt_Sn~-0qtDL@aQJ92Wvi;z0S)2E67d_?hnPWI~`l$3V zGq;U17rB@NM_yqcGmlHxEs=BV{h_CY87=gz?AX5xdk*gt9frNotO3#U%i0+Kzt982 zzu7m?5BX(<$gL96Pb&WSof|+esWdafy463&w@s5g>|?Pyqx&LKKOP3IZgWkm$Xd4if6U z_udP|C7C3XnQwpV+4r8S7vf~iAA6mC?tNv}x{rT*pM4&;?#0@1zZ_iCO^$~3()+Wx zLkn$!`?gMO>@P4LP>zK4mji2h>)lzG<9;M(x85hGHr}Q8gBpf8 z64p=aN5d`NK?mV3jMKbN)Bx2z;0dWnbkA*n;(N&raB8aSz!Z&`sCFJNRB=gRB?`bQOFo#7GhcLEcJC zV=ET*mK}k;BxZg$*)qSI`aw3&y+dOv&_x)>&{T-Eh#~gEhclRWNTiol@_k2}z&C@A z0`K6fVUC%{nk@@^sj)xLGyJ5C;L}~>FJK0Z)m+$9-+?+~#9=T7Gi_Ob9@0nql%I_D zVxJx59O)LgX+9cKZ;A2iA)AO7@$aMgt;lB;x456ijF3+=HlUBP8)r|joq>Jy_tMYr zLi<4nBHsmuZiAl)-3Ip4JXyj0HQto?Q{qg~X<)paf&Fx^T|tBN%)}I9`a@R_m$Xy0m@mF=t$^77;8BfgBC=|SbWf(YOxCk z%8msCWM{x2E%7_yyVCq$FqBVEeGc=;1x60AP2RnPv90s^%g%tiWV`=BUC(>*P8;DM zk6scr+hQcfnZL=~Vmjh!;2~mTqW${IwgrQfpU|e@DdKElsn~`1*9KVYef#NLn2Ffh z2#?+t%gpI7QQiY&+k$%~X5QTr?gbddU(vW^{Mk z*V0F|3lqbu@iWAY*oHrbG4;(5SBxKsxE#1)avxiN>MbEt`f1EBItZ-5Z$w`~?{R!P zT9MCv^pB0~?de6i@vSe}}37IiamQ3v{i`{x_xy*fl zi40cc0i=Vrhp>7$qNk{;|fZ5VJz=6Icbm&uj0!E-$?PqQ;?+7jBl{ zOf9K%E(m`R97EoG&IfU(d92%bHR=|tjM7+Q&L;8Sgmc$RRxXocXHLlJi)ZA>*%O-Y z?cnL7>I<3^I9I#^=E}m*V9gJ=c4MTt&zY?nP{8sfdZw9kLhGV8YHr(=8zN*yWVoEU znji^T8FC>dMUr!K z?|w+q_Hi%zPc+s7zJN1e63WC~KN9>fl=xv1;~$nQaX-{HJ{|5IXZ+HRA4)>pL%MIu z-bcIU#Xl^WhaQ*IeUC}<9{PW*b=urlemV9>aw+ylawYCzeXny{?zj4OJgE1ZpWbx0 zoaBzt=)uY|_-J_Mg)R4~HpBg*SGGSS7q|RS4zA&T(|hFbx&g`?#Ce{Jx>sW|89xzz zr|KrBBkol@v*CU@xA6yZdFw-RcH{kWI&!d_invFPtsA6ys7;$Yz38Jv7dgJ}9<@{9 z_o=}v#BLHhabU$DIl6wZ91p)w?IM&g5sCmm$v zmkPN{$6y!w_}1f>!T6RR4CR|U*vh31-vsv2oR;L-=G#u*D!$*_137y#Nc}_PtYW`i z!2?y-KyQIVU;@Stt-4qDpv`w5PJus+Ux#n=o+bCFw!%B8-)BDg!|~_916IB)n1(&9 zefsKqpsA2^lU$zg7w@V5t-yZzJ75*Vak_VKfBk!qb0c~#@iaY@$>1r@!r=4b?+DYu zEN~E#dieLVZ^_%-v-a_5{<@V$HS2G1HOP+FxZX~jzH6bM~J2aUO|X4OV|yxt@QsI4 zh|9%@dx1HWUF_Hx{6LGw_R@Zh8zHuZ^U3ykW4`mms}MIsObKoC?I*58_qF?sKgoJ# zclAHP6xiZ%y)@^E*`HV!%*44+E}Ga=8DqX3`?7FCZ*6N_3$Zj~x@*jiS;IKzd**W9 ziI|^p-L%bFW@1^04d!gKo|ztYhqdp7-a3!7#KerCp}-MnC*;wGCD1*HCHcj#p3oRq z^q0qe`M7cc+=4yx+^;qN$xE-kD9`=jd0o#wu*%){-7i0U_{Z|lBafkGaGJYfOJ%m-zRw#)X!T-a3&XmNj5VHcCaE6CI^bhV2dVk1A zdM7jAZO#qBTu(puYt63$d*J_h`RzB9EuMe*CH4InEd}4tvoHKX^%XQ0j5aa8?2Vu2 zt&cuXHi22#2aV<3Pd=8H-+I&GmEXVqlDzQdALWa2Ba~G>897wX<&2*(N%O>wbsr~_ zXS>O-o_kVbYT=X7ZlhFF88-e)`QWqn^_@|M_ zjNgWTDrV<)IsDa0%{hDU)Nu)m-Y6$7oR!^24oc*j76UKt_*q07a8*%H~feVfMCUP()J>~d;~)>E=GBt1VxGP4fKvAvTee#5Ks z)wT!aRP?>d8^nLWC(0qaeq=Gp-bW;PpT!&S$EBSQ%9$MWYl2!iZdmVgH(Sx9M)fsMGjI8hC9sJlNb-;J-EyKXc#`BK{|&uht>^Hf6dW# z1C(8ij>8;y27a&%F6e9B;>e{PiYC%iV@!7~>!EFYJj9wZALITn&Wmz( z6vi-|L7WJCY0QcJoFnT7$zkp*UDZ$T2s^wMp0U`3_)xSLp2@z4)(lj}G0!o58mAiC zSI@K@Tr*Jiuj+4cO$ffNfwF5U=XC72)aCuv2ZS$Z=i)x9yYPM96MCoYS-Ts;FV!h1F&KvLI64HT+R6MLdPhi%zjc;{HuH#k;Th~>@rIqF zGhl$m$KYEc&j!9A?iks<>>l+i;ddgIg;*N=Oth(EaMI4;{t~+smb*)0g9a)C@ts#6 z7WqLJq6zd-=7F);AD>m+l7YGhJ}#JRM^HcA8(oFxP{JQD6uJ+_JMLO~x7J}TjnDP# zt}#CBLvC{RC$Bj%Dm*`WL0{Q8ua_D=A=Ny5`>6j0UlE#1ly5iv%{+s3TghqePYelu z7|ZazpmA*S>8o)w#ImTt2XM!%ZW^~j?26_bBNk>x4~=0V?}*y;?%Id129_Y!g>%Km zx3hWf0DT6}!so+2>u2@SdDJoOpVL=kcv;V0oFBr7Zy|T8#;Ty3%;=+j9P*tKtFmfV zZwZ~=OIFULzn|rcnA%5{PU|5nX7tv6#+Vc0-cy!O>!JP}_360vQZ9iV@Hr5pfzJcq z1HKB*{}A5^yATstr<<5HM|2BF{jaqvTT zSwJl9SS!!TqVavy7Lto(Oi%r0!vmbL;k#~q+pTyO)5o_Sn>U&>$K7=uc}(<7&e&es zZ>?pV9Bo*4J-;(=L=WqX&=}%ad+OO9*07#7^9)bWHhp@ocuY_68*PpIPw3^ahtA`? z_V_;f4z$S;=R2yKn%|gi)_m5Cheuf3SI-VHm*>qN-&4;k2Tkp#TtXg{$De#c4KCsQ z4Y92-#Z$liwfcKtm1{s1Ro|@a}t*Q-1Q; z&*TRWJ*@X9;|n5A1?|MdvcNLkd-u`j(GRD!zkKFtjjw&-)mM~9U=nl` z^b=xF&{^;cQHN8EU+9hZ-%(cK+%oaLe;V?!a?2a62B_bqWM^pjKnli?q$ z{(`oG4~uMCJteiytvHGpERO9}m6faGLiFnKjo-{Fg4$Sljh6n<{~~9NYABoqHter*d?|qjGq|qdNa!L+q2;vw0+=7+Lp?SpbK>|xor_Cbja zz1Ol8_sGtbgJnm^U~TUV87$lRzJ=Z`JC@%m@hb;w-~RPK)OCATKPa)w2Fvy(cWb$G z`Mt7h#RIZ`?L%6|hTdn{vca-r>0sFsbf;_!zFW2h-zD481Qy>dJBV2bzFW2i-6Ju9 zgLEF_JC@#~d$Kkz%)e8@eEUnd-vG79 zd4nX}cc85G?k{WR^po}8{WW(tb0X#rlnwLl(!Iib21w-GJ7ukBKUqI#fX-jxUd7C(pIAXzuhdRKjC-+|72>wS2J^=#gS_gdvKKvvHlC@W|6*LZ*xp4NLa zpSi2N2Fi-r1N3|aXDmWz4U`oggJhNGT~@rnjQ)Z#rsqldW>4>@eayp_x%Zb~;w`83 zv*r=^K-}fDemX`BCg1wd8U1A`XO7+bO7OIvdN!3d`!Sb2mcRq?uB@ zdW!GpUNUEN&n_F)OE8aN-DDPd)<*OY@6o-*Yh+I~+SGkU^%Czf_S#Xs#B+F09n*4n zPnkWehxm-{E8gT%8QE7nhxZn*5xsN{^Jfq5uIqiq^wDRtm%i7SUiR;?`gs=5WR1_* z-r_sXo_b?JM5n`Z&*+JHEfwH`-b=Z(Kj| z8{b#_Cl8c>se@Fn;2iG#5B*5byubzAHON_AzSYE-Qer>(`OkD-f8tAi^q`*I{oMwp`$x0y3R#IN9^!C%7}V)PeukC)$gUFX9U_;t+LVEAIlu+O!P4~ICF@lz-38RIFl zrYqNw^JLiAQR)|Z;x|ug{0sZ?H(+1(Gcmo-zxuL_cAF@}#*LLX|MY?Sl-~L11AWHm zNn>T4`y_e)vyYTX+&rhLu0r3ed7jD^pNt$LLq>fn?|=S*vdZV$LWt(wcO!v{2ucEgUXns+}mV!h^!i#-@G+xBgj&GFH)c5|5e zjEKuUlW<9npC`}$X}OS^ESJ*KR8xUjuI6N^KPV$VOR@{oB){;Ylop+l(ww!De#u`l zF3*(Rb-ENKyG!xaX_B8f#j2-Fm%IcwZ5Ldfs#bV)x)dhP(6T7WDoavkN!BGd z$xC$CzJjC~juoZMlHybk$xogs#VNC;Ji}Wm(!8ZGb+#0zds*#FUnxzSBjp)BQkFhP zijqB~G~Ls(G%qPknk9wFGj*?$WDhA$vi4+O>Sd{(Qj|Pf3a-ww#+aWpOL8txll+8f zQjqAbzmMNo%zGryl+v`B*1Iy6FirQ>eXh>b-&a7HG)qd8J*6;VrW9P6A!R9EQj#=V zDpI_3Zt+zQHTo;leWWbKTgsAVOL?-V_OrG;&7PYwTS`(rr6PTf_Ek_%@zS|fY2H$u z>8E>Cr~7D|{phRC@|BvbxoY(3+;ktQ%(DJw{M{-t=Bkya`$<*CT&Ya=lj_WQTGEG= z@NOyIQkv=`6{%Lc@T#Yjr~0U|rYe1&T5+P6lqCB|X^OATrN1(5uI|mgJfkGdPv=zF zW95uz`m0rD%$JHRf2qk^B$ZhKYIV7b)M~O9N_}3Smh{!<1W0wpd}+vCB(*sK&b+Fu z`MMYH&NFD&<}P%sIyXS7vli$a_M~o=0aBA~JxiaHw@7O91NB`R@`JSGIn_CVQkNer z^#x0`pSg`#!D6lRZnZg9yQwf(8ViG@E|0yfXI1Ael!k&psn1^|P5D929{P;zg}RTf zbM_08MtxSW)aNah{Hvb&o6O&(DrceorkdO!smfj?mDvmIu^_3-4U&q?h1$>W=5Je; z<*y}m{>H4)by)#YmhP|X*~9v~ERxEM1#0DK^Yy<~Wwte^oPQ_T3#62PCz%YaqBo8S`^-|;CHUBt+8H-B z+G$@+k#x#alO@GA=BAvPBB`g{wD0Pf$&z?-q9mP|D9I-$$<dyK4?v??ry<_ZdSTl`)@*yU%nrcm*z*J^0 zS{{GqSE>b}o6HMdC=t;c)yNIU`DA=RD>sD8lC{fa+4@k~aUfRW4#!K_mPiR)8LSM$ z-9e`>Uy$v4cFT?f`!v25eFeLeo+`=NS#mxpLCz;!kV}bQNqXjGX>B|wolU3YFJGUK z|NiEX{P)*;<$u53EC2g-y!^j!cFF&IyG#D@O}v)W|M$Dy@;~3~QTxZuJ@SvQyW0Qn zyM6NC-|UgU-Q1=9jQ!WwyW}r7r9({Fn#vTJzD2^|9xY(8vFd?#y%bYpBua7?>G0zU$5_yzu$;g<9+^m zBVPXNdYt_C&0X^Mn>%l{8}ahD>v3w-b?&uzYwv4&dL z_IUYQ=WeyXcI?uze=z^r9&IxZ`%7n>{JCSN?m_!+9XsXkopJK_j$LZ(&3=Dlzjmwc zJi~qt&-!bdwTAt^Ymb#bcf`wg?bPGs+cxWc{?Znwb1CU#&YxR%s(sf&Y4x*)IgFW- zvA?v%%hzo?b?rATJMA^rdggxD5-Z=d#ml#?_H&ze$TzLA@^#A&olpPGwm7-b5+^rX z;^ap2PVHxonRl~khkV-{E8jG2m+!EaSYaRCt7*I3Y_^QLY3u$?+pRe*JMBGoICHG^ z?72g3G;NU^P21#p^LDx3w8PntKK8!e6syL2+-Te`UpH=(n+;pFq<+1@GV0oHj1kMW z%C&|qTEEs1?W|#~F|&rYzI($~>s=a|vrTT)Z?*S|k?VCaa-)8W?$uepSvu=u)Y@w| zN=Mx$9lur^-PPZ)S=#D1sdY5czgg#X)J02styOQUiIR@`jcV8GHc4mgMjg}fnkZ?n z-XLwY8>GE1$|+lGBDLO8yFu+*%?9bHijcONFlnt`FD+H;v}~_huhv|>-m#{tb<$k9 zMw%*COLOH~X)Ir@^`?q7(p12V3p@B|2W2yI898m#9_c1*_4n%wHlEwDT8BWnPd}=LJh;PLNdO2C9|k zEYgxOtRg$m9uKg_vKL8t)*>m(Htj&CU7EE}$}$7AU7i&nr5W6XvQVuwGeE5*bAcLd zGv-M7~b0t5;Px6u(^V4=t zvajSO`AANpkK|qTm7F9$wX8&|FE`Ogaxc%3oCI&jvMzf`<|R+HtSfUQgZgDJ8T`W^ zXx=lv|7a0LqrmsV_a2=EEG9y&&aP|cwSkB*c$v(yeFJO9=PzBO&U)dv?^42 zC3^P`S-&MpHtvj;sMyVF^{4yd&^>{B!CwmRlpYjm%={erdE?U9c9z0zL0Ppz$fuVbxsd!(gyw;KJ_ zn``5xwSKpbF>cm%*6vfIPuJD&m1gS9buqIaWm9#$?#G@jHF39CbImRtZ>-)aO*L^^ zG7qC|O4c=3?{uuGD%P%3-fB%%JEWz0n_6Sl)?2N5i(m~^G5=)smC@2rzFDola+7V* zQdhBA>dT{~u55$Umqn^Ilt!r4mPYDWZCQlWl}Bnxy|ye|>dLLMrZh~dOV+EIdS%gC zsVZJ4)g^1Bs(7{5wXbNcR2Ht0%A!?LQMgi#dU;`}l;$s&@`4p=Wd$MHN4qRPRLUsr zG1islhe}Cah>kIbI(?!&RnML!mK4~j2C7ulcJ1ZDNJANSYAe;v4B>Bk!x;`f@K-;;=0a9T1XJ1{QmX+u)IY|p8CuxynUk#9~#D!{^R~AZE z0&RcW7D{Hq0!dF;pk>Atf62P+Z}nZCFBpBsQZM?drCst>Z~?i`OdKn` zLLYhx`UWLz!?zn&dHYWv$Wy=ljXeL-i|X@%Q}FM=EN})ag1@NuzyZoH=q&hwjNa0t zZyz<{X<38c2>k`7p~TQiU?x}uUl48H0c{6e<;B-tRW?C);eFu~-Vc_6v+(_pZ-rd7 z#Ic~EaHbjdpdbE#OVC^B=S&fr3+wO$!9!?D{q7#5aXQRppPxVVg!)!~^WyI{#`f7i z{$8GcjhNmyWYTmuahowkKK}d@y|V{i89L%~ndm-Q&_yPBOi@h*UiosuFs*y}duk52 zNwX)3r@x0x^>P#M1)ef%?hJAFo+`6^r;F!24?QD9PJLo@$w$jMAo9ntCprtcT8Q(7 zP0&=x@j^})&LrU{S{=1snPWxxItdM1E6c;y%GSNRWK;Y$2`An*VwL8GTNfQJk=r=~ zv{U<5ZdfC$H?GsQoMqfrNd?v z!zqSKTp7jT7+A(-zQY=@io+sZtYVnOtTUzA&y@ev@4_t9T{%UcZ$Arm=xjNm@6pk8 zLRp1A!z%EKtKGsWjTWac_jYEnxaM}wv6#lN3cLcVs5Kl?UNOu9tH3UB2?ndQ*B$O^ z+gxK^4ouX=EM^|O0jn5mso#GKs~ARcN}F+L8y?cN^?Q|B%sw!V z;T6Lwl=LZw)bEzI28&0u-)0`eKFTz3P5oYJs_kML*EO!ZV)%oy)rCdi7z|c1<~rBZ zm0MiqG*rc^!6mQ?Wn<-bX{ff^%{ANAnyR-s2Di{wet}uw7#D77tcZ5ZVwGs+7sD!s zSL#Y5lvgMXtJGI)P*#CgZe^ATsVNP2SOsQ*QH)i?E5+-SQw*!XEAR`9VR%K^#BM8} z6m)Tj)$U>y*S2yDjDqE@loA(Kfme!iR@i2-4NOxErx>$Y1#W>=;1adlSOo@gWfeFD zHc?9rl$_K>$|i)PHpxo~QchtFMjM97O%7B}u^0tT`Da#9PWd;i z0=Kxb3XEcyg|W0t^KA2z^viRVS4^H(zQ+Rx-=jJLeDcswx%>Db)m-2MSOym0Y|e8p zzMyd;@BpzR_*&3P;0-hqn8V~yp-mk#=YKe-+@n`7>D8x?`g&N$n3;zzgWkeEFbU6w zTR8g+Gxf30LZiRHFK`n)!+Y{hyf=CXb72&d>lXGf=bMQ;As-9l&piLE!zpMm&%OMD z@(Od&ZP1l^4eY16a*5$ZpJ5KX!+pxc?&34zOf=_@$tMd#kO$?pKfNh$e*BJ}lO~7T zN5hB6XJfyV;S+{w9PN9bzb7N7jFc&Jrby80AdOS;Su{uHESxQ~=g*XBbKGRugfC>o zgfCTRA^!@#Ao8wYUyT1!CeE599&=}jhu>_Cw>~n1*5Yl8`wGte1gF=rhcvEymU67(-P+CY`f6KJMbZdm2^ z(%}hv9pkXX?eo5`%^K$&U7pMOj>eOgQOBB(TCCDSy-QO;Yx!P7Los6w?1$FUrMJK# z$|4O%t$w4s*zMa(GrzUrh;3cWqWl8OG#qmD6o+YShB0lpN3H3QsM+f*mf3GJO@E-PT^rI(Wm>jULimw@;dCE#81R3}3X> z?pC9%ECVCe!%UWL!``NkJ-Td4a zFUDM11zusizIvx)hHp&Cm~u?@4&|2ms_l-^XWDg@TcoaXn>18zQERH);#hNqt-Dli zk@|`lHRYAEO;T67QS}ux7Gp3>b!nv3lx@(GI#yK@AvGltQe7M_HN|0S)kW*AvLsBZ ziq~tKKI*sHIw>z$tr|;3!D@$DV3+dzRmvcxc_GRrw9Dzo3Sb=OuaPpH$J|v?oWD|8 z1zsu5qmOxOq$n4Du^0x9Dai?y((Di^%vvr**&%AhcH3f<nWym)m9vh z1unTwS3!5lzOqoujD!F!=}Wu3K#ds3)C=<@_2OK|U=nl{^cC{T^NoiOs53@CtUwGa z8UsEW4DH4EN6Up_Ba>%Mk`Znrjzz?tinu+ldwOV78Mq}w{wRna0wRTIRfjyX`tYT{| zl&}gMV%Xzm$3=%#RNt}Z{$E&ycVmA`XE~=nE2FWf|Ek5(U|d;-`uCYDGyJ67hjqM{ceXgY$YiT&9X0?x~e~5YQO-HOg<43ZYM91heOya^gs(BdSQqv*9 z;1qbnl!iTA`z+?zr)*&O12$2sKd4+}+B{Er#f%$FSJflXRt;4_@>JKVh)5R)=Uu^#n zZDkkr|5%)2cm+l=^^$^BQj)(~83j(kXcy-B?oq~`9)a;UP)doS;;|K8eV}_ z{vEFvZpm=*5y2+uR~9O(WG61tcIqX6hgH%p&hOG!F3wjSg0nWTNk8)1557lzEQ9VH ztcJe@mO-!Jn+*rRFYt`&7Ek?3^%htIzJOCSx7)q<>V3@v1`Sds;Vx&i6d2=q>u=%r z(2uVRzY@#^i@_jp4R-^P!v!wkULo?hz$xf27}^Ojw@>}<8MP;W`;_LF`}m6?niCf- zg*+(uWj-A-R6ZE`C%qq;JD2eT(I)pScm9w+WwPgF_4~|QI!}Cpd}Ol6L>V&jW0~wR zLHvTeWl`t?dH=I_Wb~Ag;x>Dd=3V)8Q=mrnFF^di@+(wxqMGs^%xTaW5>T3-Nj)InllVH%wkFy!_+li#$~SZhs_?!GVqV# z8^c13HyqHpW?cJhmNDb5ta1y#*gA>jBeG&)U=G6{a7r`%E_~uzcX-8ci(NL??w6(- zqurq27(;J?SI}X0t8S!a%`R!J*`<1piDfZ5jKei98ckzWoMkq%z%{0>rOUYS8`W3t zP)!9^fm1NUER;29aCYzkTy7Fk{n1=FbsVdzhHKmpg z1FzsK!r+kG&2WoriN;b{Wch}SP6E4B5XYjiENiTOn^g>xSaa8^W`b^Fth8W_n(8U} z*0?fD-Wn;*T`eU!t5k16bHO)+l_;}VKBA)RP{#_hLR2qNy#-duST6Y)%e0>{%Pdx* zA1lbD-DUY{OQj%vnU>UD4Q7E|R9``BajB!N&_|hdHBfSCUk%iHCft#@NIAsRDbY`0 z65}6&Qw*!5UkOl4yNtGCS?Z+)$}TAv=c~aUeBbc}@l8jI!1%VCHXH++n7CFLh3`Gg z0t>(foI!r_*Uu=Yz!rB89xV6Xf4>a6>n^qa0|!d?o;{T{;EkTWdn>EJ9L$Gd;1t6i zunFv9*vGW*y!&n$c;}t^9QcTQEyTdUQ{qRoEe&5_MMO4 zl~><=O`iD8uXGK0QRwrW=Ov!=XDeGw^q44ts{&>2lDYE5gfC>2+X#(MS+XWbLc*3x z(8>TA_93=}Q+?J4wmO*0>c@!Z0bBS(2WUD>(&)l3!A+R#Z_e zWmWl7R*@==4HmClBZdXGu=NeY0IsZJ*yG=o4y)Ll;*>^XG5w}ZiH>7@LazOG-IY}g zvs|}Xg*k>z91gKr<@UC&wc}q5s~Dfm|D`l6Lg~sj-?z3C9Y*ad<&uBJDu!KLS;zF5 zeR-Z)4=3EtCEwRpAJ}b7qH9eYFD&IUZdk~*&y`h_S6t>(H=Lr(V*8k26%+ewIE4~c zF|oXkpUCzXDc2b0aA6eF55u&gm$=Mvo&UXcd_*qubq_eB?yzM>kFnW9xrM$u;#@4> z6P)2NM;)wk$cmLQd;E)%-^E_6H)a^+U$cwFF!-F>YV7#lnqA5%l!j|y8&@;&E*kR! zm%uDlaTb?U?v#d#c+03)#p*bv@dd#q|7=~pBEvFO?4j(^DRt1a6s^;l_)RFy_c zZE1}5;Wt8O!AEopv*06A4aL$}99BVNDG9S#h1iz$Qc<|pVim(EHjh-~ud&Q-!ylD} z>!qqFOv{RbbyATZCZ+jn)!-EP#2AcXOqs>_gtFn4RjRiT({ekj7)F6plsQ~kB{S60 zT(UwORx!+?+>#L@`RS~4wPki2epzPoNf)yiZZZ0b`h${!ZebPJ<#t9fZCC|^TMVaA z&q$zekz`y6QiEMAX0dn$y@hWzz8zvz&>V<^y`y(8jQ77PBLJC+;z`AGWfpxq<7!Ga!2>>a!0prYTbJD&=Q>m-2@I{4JE9CKZz0!A!MRITNsFF%wSelumvwDFq1|HChb=-oxrygX#yk|4d;$7|ty zarg1k_HF6Zb_k3HP%R3O`+7*=SW*~x@s$~@wUnj_;L(exRi!RzV5i})S0V#ipxCexA%SD z+M8`xoPIOza0>4LkF=d};S?)&#bJ*Y>&%kj9o8t{xW2!OPGjo&3=@+>{BFxx$Ixdm z*FNH43`e-KiqqH386zu(rpw=SE3=q6t|fJJ7S>qLu%2V>byoY7k6g4H*Saf1xz=@# zi$24c(QqhT=ic7$u#t(QaVd43y|3XH!zGRn$o36&)*n-&Z8R6dGM4Vbxg;yL#;obu z!#cC1^KOriF>x_2yu*6aR!+HX&MjwX_*qb1^JKyS{vz)RkGK z&b66EHJH+ks=eSl!Z)O)9n<3Yfr=s>)_^(){&mMqhzXu%awtS5~>i-rlOOpt-2-k`*d&4fB;zGDD>xeTAi=WQ1rR zCB_`J%vIuQ#aXMZ^%*ObQ}R-lOD>#}46`heoaA6N?Nff~>N8_mSJ7FzEc0rRWr=7m zK|=X`mXm0W!7J(56~iwuixtDd_Z)u+tb$&G9|&II8_###eB+4=L4V=nYZ45Pse%Z9{u|2Z-7VO7UEd=J^W6zAbtyVbQOF+__yE_;#1%f?r-LN5b-F) zt#Edkv(4mHnLJ~P=7{BtaZu=D@me@XV@BW>`ml-9Cuxi={+>U+^{Q$rCKiS}1?d|) z@-unyjhFPy@`ocok?}Jo$c(u&WMSw+@eB4-Rta3`FCOz|$ml5}^z8E3X`?ky9C_iU z%y!dw6*rH`nwOt*M6e6*!@4_haTR#^FLXZz2Uh;1?IH(I?RT+adF1Ayo zXDX{0X1JXC>)I2w z$ICF!%r|w~W?W-!946w-lVO#wq_gSP=en{6b+aGkzcS|C&M9Ue({IHlTUw6kGpxeC zW)$|{CMx=QODwfdawIA2HCX+B|n7^X4bf5QNVW4hX~LDyMniw%t5 z$zq(YHCBHY1G${lcBz|jWfME5*!4YJ>*m~*>2u|j);jBql=jsg5)A%P26EAA3>P_L zHI^3BN{Roe#;}UTHSh`RDd8H!E3gf7o2&Oa2GiWaFg1G}jmBs(a0~TjyEGi58jZ~| z=rMKWI|NQK#(C*lc*O35O<)(pC~!(c#de*?+?o56kG8u_=w;WjcXyc zrPzvbq3?EsLB7|D!tDM?sV<0=io!^#Du|NG!i`c9XWo$-)J75%L4(b^zEpDOASgGt{IEJ>)$zCm3N%ops z*QKuz3|`4eTI!TA37nFbx>SvNb`mix7RMM)F@0H#*<3@P8Na>EPOumTR!Ot{LGTKC z46K5#!Wm<@;1|E*zU61MP8+=ic7adu0l_SM=ZQ~I{|aZ3@!b$RGH9SOf$^!ByMPJl#F3Ei#l+4sj(>-DBGwlFkYNrq72e;x zH@^Xn;W=n6=rj1KU>ANDzlDBs#lR{UJ|yl(CPzMJlQ|Rg(i`Mtq5ZPPqs$BR7Y|=g z)ld9`7bvHY&+XIEp9?u?_^@o)WlXp~kL^o;*T69r3Yp3At@QA3apQAeZfkk2L=Z z`U^pf}j+{HA_ZPw?@Cb1#t}!YpIXRM;k)b{yv=&$e zUr=^ofu!4Y)m;kmm0!?S@``da))rPF&h~o8C5IVYzxTJ-4ST=ab~IU5l2 z+t<6c%~>nXTVXugRd+%AFbrYhUv62i%!5XA#(J;jGh(buH-SCAl6KpucQ#t{=(G6f zjMm#4PdjFw%b6qf6`5QvcI*pGVlm3C=a%3bs<62 zquUs5Mq_=ecMH0U(OyiwxpJ4ZRPA=oDb>L$w!tm+6|rhH9O3L#$Slm*8uV}sULlN4F3$qkttxzk3 zPjc2coKcXqN(wS9?x5d{85S|@lAjr>X4oY^!&;|nv)1T7%q>b^C534#B`-BZa#deh zF8OJp>NCP`WcuKc?Brm{PFkv#b#;kkCI)xuDOWi&6s!if7)F6xjJfJ8#uwzouUrX` zR6BkJ-NG1oP58dUA$;TU_n24~zU|aimw5CMT?2o>Fz75WfcfUbENCTg3cexgtU(hY zR~zgBC%_)WjTr62xa*$3X@H{!>lk3C?O7vFqMhEEu)c}d8(&pkUMCybVHQzpnvZx3bie7!D*j97YFzD_jVqN|X8O3x(2<|0pNE*-p(BUL*eRpM zH_%&iv7o8=1^O!c%<%Kjvp{2~O;lE)L{ouRM!HRuA)|)NkWnM#{m+NWG;c3)_nD(5 zcM~p;2$L02k+OL0YVi+QCUchtt6yl%Ce8)zk-aC5$)4j!W!KTea^S>q&0$L}IC88| zS{loF)me_6J)`B(GhfO1ge#I@`-$?)%hVr~nw=#X`8kr2lcucF+Cr|lB;^N3=di!+ zl*$aYKgIOxcb+*mrx;ebg&7!cz1+nrZ5F3|-I=7EqRi5sC||=a?TNzp&9(%&-g4P_ zrePA-GtjED*sNmo5}t+e{)UZ|Q93Oyf~y!ezr(!0=9Y2EHSLuMfIgVS%vC8^oYno{6l0xD7vx&=MYS8v*12)# zdA6RS{v+eZf^BZ|VHxgmSjDhO;~8ZW*Y-6R)}oG?b#R!r4P(G6Hm?|sg|ZVLQ{zeJ zJFhIzKz^Do4T`qehcz&P?!h{8$(TOc4Y1Ew($RQYt+nBlv@{&Io^9^~1F1%Zc4Wua zz)Pxc7&bAyW6n3Zu!_E;D=)d!88h#}^LTc1y~QlBQEMY+{T}l^TJpDHy#A=PxUdOr zWiPm<)*546O<4tv<*>yaHTb0PBhfx2O}ClpYpyvWEj351{pt@pJ}mrK#@7U|z$xY| zwdyU^d(_uNy|sFe#>)^NQ(qOQW>|%`VI0m|WAwo-%+dK3u~JvI)8Urt(k)V1Oss9R z!z=0&!Y@?1MXHOVrKTiC=P|dcBu3lBzI-36z%qtg&_)cmRF!Oy%3`antYULYRna!7 zF5V%vr8}jzG*;VH#akSMWndPgvy|kmlj59pQk=WiGW;_zgv}xJm*$13mE^A1xg|N_ zQj#Aor3De%SCqY0`(Y63XeWkU*snNijgFad<}eQT6lJcK;*8anA1Qr>a*CGe%Ox*u znKBF244+K1E;_@_;rY*g=@HP5N;vf1>cgglKT{vy4Lh&=yxd@#=-SN?17y(_~ejuSVJ88T+dB)#v4^F73zjGHn^-uvW3 z`P0ykES==dmo;wo{m(yCK7l#7kB~Nb^{JEpo^#5?)V}$_+v@*8SK&MmXPL>9KWySi zJ!j0FhTIkC=H;e7prDnDWJN@X1g{B@DV~!wx5}tV!)4^e;WB*umzrCJn3Y-cJjBg& zikfebpR?CYKTpl4Pp7Q} z>hdLxt0g}iXM)gIh-XosP(i-7lQPmJJv&Y6>kFm5C12WGQsqY5Rr#g^cIeU}%=g`V zTg^BApP0n<3mF~7u!CtEF43~>s$6ToDmUAc)U39}D{#x#ZCB;nYYEB*<~f{kHfHz) z25`n*ScUg@p4X8eUthcGu!@<_9;|WNCeFrYE6VFF7Hg@_1JATx7PtnUG4H}VzzE7A zXeurmhQ6O2`-?85^WYNBc*7z#fAAdoT>Ci>uGVr<{651g4o|@#wiyjZH6b({^Nucgbd%ow%vw1;E&phShea3;%(!Ub=3c4#{#oQT_xPITl*PnsNxxMs;{82)RbWDo3|rvGpiZj`dZXsIaPCY8lIq^dYhs!QUfvS_DN7H*aDg3W4W1shdkfmO(vRTWb+;Az(36tV%+hCuZbrzGrFInrQ zAbY*efpcinuUZSSF1EoW)KyDK4;5F-OJ6Ak8Ed6DJ6sC0BBUTIO!vvlSRq*{OO#b$ z6vHa$D*ujEjGkgjhgB{IDrfM0$DadNps^6I3P<4kL8pLMU~`0-8-X&i>qUw`w2gDCjNdF8F=$1!4GTjE~504XgsY zz%9HNbDnG25X{0|LFCV0y(!#@TXFOBkQu(dGTVQi zcrRKg>!LTQpJ&g>FXf<~h{4nC*bd_cO!)@76#Tr>>BC&~iwfXyrBx4|mdEql1IieU%(l!rR3 zGfo=6!aVy~@I*)Bc{P0oe_xxEc$V>B@f*I@zel=!-H|Hav?nRg=^S{X^_=n%CC}FP zvN?u5bq=}w42!w+xiX96J95eIXXfgj=xXfKd`>#*`6gSw8;djW6}kAMY#oGeyr~;T zF+L@pXZXmJU3yLhF4r!#W0&Go%g6erL2NRgTWCl=Sls}PUt$|>k3 zuu4s3ta3?hdAyb|iZS@a_@Wvr_i2Ak`A+4K`tmp@#urY4ORCGZtIok*wPm}du413m zSMHbk%H2{^7N@LY`Ee{x!Kc(%i9Qme?Be)-3a!`_G!3k*Fv{}r6mOI2(w$OMx=ZTH z_eyQq9%YuQqU}6i0+SeH4hCx|t7NWIjYR!D_GcW~ls(i;`48Hep0_=dl z1h$|JBftU1SHv1~4u~_!_dW1{Jo?yU4igYhg7$)$y!kK&v8@=|264Rjkk}s`h1_dA z6D}g2h0>foqGZgxm&peMOX1%^KY^FfP|Q8f_@Ce~-h~pq2ZK>y9rPQX`OwcElb=2L zguM91>+;F4FEwU`yszY^g;U56NBr%7KJ$e9?1}%7*WZ6b#!eZhtnuDwf6_P=bP`wv zb|Jo%`<3y>ps_HAm=p}35AiMJ$RF-DM%?{8oVEVT{UszKL{>(I%Ic_rdwN!xSQ+j>95rRM=3L1 zs2n_fTKUB2DrhTl2lh)$+)mlFW4lCc+bX*b9n}2$iQF6US`lM5Z+UCe?22 zSQ*ywe$M+gT~JnOZ#bvy(u`(7%rX1Gk^H-DJn!&{iIJsLc5(3m@eOx)#{TxxcI6eA z0^gGH^}zvZ{M}%aW-C_8{5zuG_5BUEp#P`_Wb=n%F?>Y$ftt`h>W(U_7{)PcT}#H= z>Q6d61KX$$Qw!HvEYntJ={WSGyEN7uP))^^Rfs)t*aW@A&dZ`_e(>W|S%$6xSHLTl z#$xFoMsI;TU=kRkv1-5aO1*N&ZtbhD*lU^HM%$@_M_5<7OKQvaN<-ygX{zRT9FxYX z1FC_bs~A>6S3ys~h(W>7T#PY}&H<;C~I0IdT@!X6x>dP^lf}Y~Ut+=p?*3(x?emWXWn8Q7+&rDuw#it}K zzQqq@;#Ev(^cEAdqTF%?J_(ZaD~ly1VX35D!InAf0i&4s5_~A=4)|%%EMN)r3v>~_ z{V)pi@$oR1HhE~tpTb#X&i}9%9AoqqI0%-2M@-BtyaEToK$P@rj@T!DsagvhgD(oE zKvO|q;l1D$_=0yr`@uJaeu9q)PJvr6G$LYdiCclccvm<@eMCQfRNsZ?@!Y#%ut$C( zPd)cLdF#VJX^ib}U;2Zd{o%es^1+cWj(jS_%c83c8E!EMdGlcsd^q@W@cj^93pb#h zyglSSWfS_~6Lb{D;FV#MN6Q3{DH`L#INHd%81CI!D;r}s$i|)F;u|4@vSZZu;~O|%JpFxS_Cg<7v}%cL+aIsCW$#YC`)AFj z4YEFFvn*S`RzkwpE32%C442?_Ycxj1H#kW36f_mIl@%K{$l^6?WLa34gl*j_u?G&w z-s8tLK4s_reaa+gD&&SkH!;RpAd_bWzfVC$xs=w{NLgL26jfD9Np*!(Hq=T@W4$!B z)~L49*_I{OUR~vdm zR^fYmqcd6c7RF&4)l%$dE3epjZjGj5xW%vqbB(S7R~S9TDQ%yQ>Jcq=oNVg_{nnW8 z411VwwU#DE#AX%U_cmXOVG`cQ@DzQHFNnGeTe*Il_4^FBIND8ua^KC?MC;zBHvT4- zspi#Y@tgi;bfNaMPVN@^sGeXh+SrcEDp(>CMu^R5nG z**>5yRs@1o?Y${o`&Gxl-^5%yUWfkHU z`K&XCJ@A7V-f-eJY*ulY#AXJ=FP4AB#ZP1FDuyYPof^-`wT27QS%2QqS9nMM`|+Oo zzGy!-%XM*u<-;w*%HyK>R&L9=Vsgb%)@r`EuDDt&X2sS~D)1q3Prz==-%|l^>{HHYsy--9 zRfnXJ(l+90;23zNu4b#{I_haKdtYKuLf4ZFM~e^7U3+BxSys&OtRJ_YtbJE<*+SF0)AtyWnSXYE@^%*uAP%0erag?JX21YUtf&_~c( zh{>%f*`Zcm6eC5sQ4V{kE|Oz$0*nHapl=uk$oDGl=b7rG~EvRzX`~K9-%fO45@;Bt3D7 z6=$2gR9Po0WtkIyn|+lr>s&Lel1@&QgvFAXKm@raO!Cqq zvU!)~%cL z>eWlnCF3iC5y+_zyQu%@;fIue{`lq_$_OU@1ib~l#N?hOCYC%el#Cgjg?L!>7<3UB zgMRAfE=AY_j-hS*L#&5yXu~z!{S4#qY~F|YXfw=#lki7zxAR*cyf5z#`A9w<_PM!l>=8Vm9TA55`S!;96EPgb{yIzrxPz|-Z#!6 zN5yW}c-hklm*x1y^K$U)R}!&fo9Zg@$Bsz+@uRA*96o|Vme6;5h z6XnFE3vxCoQLbdB%cT@6$3AD0$*Dq#arTF^%9J^UMN&{&Cb`8$&b-3X0;y{%la|&( zz2ERA{ux`xaQqndc^{)yz!>`f`<-d>?X`6I?pmh&`9`K{Bj_U6Y~Ks4;AkA}oC!)$ z7WvDKRQc|Dnra2#-pG`%uchmru!`yi=Df0DCEEw&Fp4<`C9FwBum( z{fI4YPtm{Y&5m^Ww$sv5U^#O25xc^E?5%zb+fSmU9ZPEEbR%!A^?hWXx$jYZEii%o zH#024@6vu4&gL^^3fl*!{V;{iulilK?^oh|oITN;^j^#>8f(oRQ6}c5qZvL-aK<$b zr^SlN(URYWUyS&i=8Mwaa!%TtFQ~D$v-ygyzpfmYDA(Ap`J%&lx~B1r6GzhAfKFjq zGjXlxC55|`UL(J#>}|I7+roA;|D@7>1ZU*`I6DIP8BhJ?5h zlh4J9S+U|~$;WRr6f_k$Mb9LotyIQKJw78BgD>D8J%?1q86+!ChTqo+qf{MIR%)y^ z>~vUtJMazq46(Dsw&J5fS7E*W4Pnp5GpYksmc(l8hw3W!xgYiU6v8g}grapHjm0h9 zCS7L5(AJgi(!JTgsw7q_ingm!*Z7r^SgjMo!kil7S&Fx-KM3Z4K@5A8;v*{9tc+2h zW5lUMOKCxj8g0+YZe#I$g32u>lER((M!A(EB6+={Ec8X_6Vp^}}vLb6g< zN>0j3$x2xzIch5*Y@7>4KYSZ?ze_`{zAPQf>< z8Hk_%oF2<_;HMw{{AXHTyP=2q4|mV?*D4 zQ@ublmdq8az%OHGPO_OxQ*GX=C7Ss&erAeINu6V}moByqyLYO;2Ufv9yYuJ~JCu=T zyH6am^?P>Nj`t3WM^0p&6Q{r?r*g71PdQRvZh7&jWfvD&PSj<=!7F%CvI_Dow=itk zp#m-XmRaX=^Q^eE%p#>FVig{jUlg{I%3^D6D|6aPTXw=bdIhTl+`;#IuAkH-U=(pm zccESA2|10VkDW&0dX+Er=7~}IyYl5-k&i{%LVDu*?ysxCCGa7eLqROlDzNh zG92T*d-l*U69+S&&AjJ0_}*cV-%-hcpmW2@w|ej!_OvWgyY)M#39VYgeyD?c5ocO zwCY*Tp9F94^N3H{nHA-p?~mztTbjEaa0)&3XiLlqm(Lg;q88T-ueiFLXBBZklj9RU z2fRXfV_}43ox<-^WAXRHFS<=_Cg2sX#h|UgDR{s6jL-tO&bpe9t4&cWf@j_5@9IAI zZ>1kle+oE-_b5K`ec(ynT%9k_o)X*O{QS)PjyNukL9_8*+uD7uR|wCoXB9Z52EGXL z{DW+kL?2Jm97M0H;0u9Mq?^=k(;Oph#XMx!?Unw~SjAlIqt?ae(DYuwO!$Yb*}fO%=P_Lme3!x-fGLpggI!KQ47Bd@gWGeJNUs;}yKR74bDz5nG#J7I>n(c%4`U9}AE5tU^5pFACa<{3nrR zrWIXcar`KeWjbGZY^_z`eTl8r`O0GO$7$Sh$SU0~iL4Qql*ZO;mB-dwd31wT zMRb!@#_;@Zv5NQxF$}s)tYE%H3g%c*pr61cs`=#4R^1031x^X)xE^Numf;oHgxVA} zCUhUR(E|7bPs{>67wrTMCI8%1X)y3jC=>tgT#M!{6Q`6FZMIlsvqi!itT=z8^c3`u zFMjDun%_fyz4y*i+xeHzf5E=+WydYlS*YVs2l~f<{)}7y^iTH9Yp<1em8>@w^&m3e zl&SymkEs@<=LGefU*7Uto#&^&{Drsxo`6ZH_u#js<^(gq8}JJYc7Z+EM;~F!^Zt0p z?Ya*=1YfT2b>=>JhU4fiEN;U_^bqlSFcG6t;wRm)& z9r~h;nmobQ@7!$jS1z^*=qyvm+puw?)w`TBZ;mZowOrcD=xLKR18CmLWy;!Lw{x3( zCwNQ_oH`*b1+NL2D(ElxYQ67-tXk?kM^2uw6B+69qHrudE7Q*9`A<@)51W=bz{rO&4r>&*#gtasrQ*A>U3;tPjmb$~bmUBJlJ&8FyvjkcS z$620L_}pQ%zV;k_uI%G8=J7BWe1N{9{^r^}iEKEpsi?mgE~(pS^|c!{|4=Lv^zYz} zMf;F%Ry7`{oxnWQcN(gh8BGq|9x)tQFRJ0xF-QBLF?XBCc+G_#YxkJV0aL&hu9ky8 z7Ox3930wJ4Dpp$+IV|)sm#tQv2VV+xAoLaA+l(dySHK`*jIym(Q@YJ6OSf8O$#$zL z-C@=79adGc*{Vyo$WL2=MST24jDN-K*kmrg=mhdPh%TgIoN1&>5sHcNSIbz-51 zz&Mq$jS0?xJIc^Uq8qFv*v8RHiq~s#e?@HL6?{@&ywOUF)>~O*!xiU=hgVxHw9?`Q zE2ObR3KlybOJ1sUmE_t_ICrKMWIO#NoITefxeG0pzg)UVEVSy1Rh+-ViVId+VeT?3 z%vowhxyvk)$Nm!4dGa#nT5jfCE6848g*nTuFlVI|WUaLP%oP^SULh@o9+||PozHwm z{#Y~+*yLZo@J0LLSFVx|#AleuxAM7vwSWHXzbHrlYgb<_Ee1xRjs)+(EYyhL9qK^% zRq&6%46wn^e)UUvO5g!_!h289JkUGf3U~u%;qfeV6>3882G4_K=y665;XWRZhC&ZB zy*)gZIuDO!OTCEfIXt=Eugh!kp5Po_3ttSZ!t1FNgSm|^3F+vsUy5`D$Y8@%Vyzu5gx z{#B2?^Pzj>sU=s1S`eJ^tTLLn#$N|lWqCN)yl@-dT_UG+Plj(@7wLxo}I?perUh#KY7fKXP&Xc>8EtxhMn7N z(W;fUY~5O0vwdq~mNI@6>O3d2GsP{ScTiA)m35`Sf;GH(#oq#t)-<*-W7Uu=+&Y3T*~l}JGz6~IfyWgt z@vM=|DC~PS@{jRb|9G~ZTN2L=*u>8|^tainmcy2;wbm0sP0D$cVFxip%L(~R^u1f0 zaV4wx+7ELDlUGOUA@NFbzYEtCUu)2727NVrR~M(CRq*?S6M|Zf(^%3HjMCnOR-fkP zUCNh~W9UGt8@6Xj_dxsLZ-yGyU{9e9_C>6(J8Zojx#F94xFpb3yoMqjCEyj_gZiED zF>!8qrz>RrJ%!qGjQ7a3S~U@L3wn8zYbuQ}>sjYV?#k9@$>X@LC%6RdEMOD%oo;f> zd2`Uqnvc5PoW_HyC&5H;8b2QzPe;qyD^@2ze`kU9bcC!c=yQX+_?+PsF;5-7SbCtS zBRS8_6|6$<4mF^l?}>e$(^OC0h*};0_RfjxIFHwc&{v#Sg}k^ui7Z>`DC$$*Qmu!&4Lv(`mD{A5Xj`$_SolzC%C}f` z*=DOM-K0g$McHxaAf;=>8)T|@k1Sr4N@_i&E2O7TC&G(@zJniyC67uOb)(=MI_Fkt zJB>B_t)a@Ty7K+jSborID|bq>!LOoz<k=1l}l%Y_gK#4OSL&TlW2JG!vHBPuNdp6__O+UaLA#yl{A zYsgLU{l9PrYy;O&=Yda{{o^zBsRwZ`mhTzzzE_w8&k5dFWy^l~OY+QeZso=O=!aqu zSOx6^{RABby#qF3;aA~2@CkYdYyzkJ{J(x}zxv%RcI#bt+JFA$zjQww!fS9Yo&$@( zLoApFzb^MtAL4y+8*Ta7pe(8_S`edSc5U>!+CMoOY)Au z5R>OlQ7wr2&$F*Tr~9Z2;V+?A=hpjg6|=xHzx>m$q_Hp)2!?@CsP9ngVJ;B;%Wx39 zf)9oq6?7Zegx+N8NiV+fk`_J~j*Uqft6C5}%X615vK1TGNk7@Le@|kq+PYaj+7+8O zs_t_nBh8MV$q;8`hVw0}ut2LIT4Z_AA}cJ3YClpIw}N=F|sR zW`rhcFekO7FEpQY{L_{p9zyf!Y<1^=7r5rb8t5rv6|cbsOyX)jaD=M?aU6c(IG?Hf zCk=_66@TY!#b$R-sQ=y~5O1%D3t9-p9ggs5coe zE*cK~MWriUzFVowqSbA3RZ3PXAD12_9tWp1(u+jRrxbqKBu;6rJ!DO_N1cXMcgkAp z(yXIC-Ri3kS#9|q@d{quiV|uzs~oEY87Zzmd9hVRS4hhd2T^~*dt0(v+KSIqq5cE6 z$V*eQ*-E1utt<+I?6Ru(9;+zXZIz{atSq)Ou_|KQttz(N%8NHA7F@$^7{#rP@`RKW z;tg@Dlx<{jfiCjd&wpO*K~0Bx5WUIxVc`;Z z18*%0rch7tHP?tu;1udTJfD5v3q=1iEJL3#^9ErPyt@9pFc6DAA-uPo8@&d9Ep;8( z0bdH*2K)hsa15^t$MDdir=ZDD(_xOQaxbCpL-9HGp&Kizw)-T&16np?=Rx5mAtxrML2`D zYa`#V5fet*n3VB0Z1iw@YZNnyUJ;|rUOLAn&z+`u$}2amx3$|gOJAYBv*WPKoW&Qr za?2*o|G`@evrz9@vSziOv-`+FJDzdcPMk^8I(06?&gJKbRm3L+xt0?NYac#gjxv47 z^!bqUrrftsfn}k&kONncpI{a5x5Zz}W6)h<70zSJF;-DLYK4(}tEh-sS7)L1bw~97 zF#k8R(DnKLtU6GVS2fuO;#nneKR%V@?EXX#k;|7IAb%y`3eOh_b|J6D`$&@X5%nnr z{X+B}z2BE_mwF29LU)eqkA{~z@kuzJrhE-;&J&VgEUwR7OX&@!pPKwQYDNKLB-kUV zpXGnkRlJ^)@DcDjLG6U+@|?@Oa#>4Bp24iwfay9ioR0?YDDOkPxJMHF0h@Rh@p=mU z;dupS;eIVxg_;Oi^=S0L<6sq*uLJSE`7FBHVaqHn>I~8@I&$@UztCH3=X)Y<+Z$Yi zyO?*Gr`inl8R{}xZQ0h_k}2&3UO<0uk?WXvwH0L5y3AS4EF@2#{I?*>7OlhgDWkJQ>Is0T&E^d--A*>1{V zS)-Z{-qXsmUDi-V@6vHC_UkK;SY5?otE)O_)g^nZGPYHGQd+c8j6!{?revp%b6-__ zTd)t$Y?Mv|!}uB!{#IT~btOEwp*7-+c+qMtj+I5$Nh?7=(Q^yeS#fBYn5HOiu@&Vk zly*{>yGXnOvxrYJ7g;!GscJ*vycObx;yzxoyX)Mq|zU-;4& z?W(VT)js=$&)L6z{`)%zz$xwNcdC&zl!vcAS;gNp6_& zHe8c4ohKv7D=UWZGYEYRD5=j^nhm{8>buTyKG-f-523$V8WOqw@QOcgVjf{ITRHHJ zz;JwyaFe_k!S{V}htHa48+d5p56>%!ZIX|c=SeFFX5NTZS{$=D&&v^)U51`Sy$N1{ zLp$NokZLyl-9@?{U+62hOXq9t{lO}`G*}`1pr?yEM}9(6P>lw?p*_!fTXU_wElGoE zIU9Ic(&h7LgIC%z^qTxm;1;fFXbaS+^uDMzadI*t#7*G0RMhSK<+bvGT==c|5a&Enau8W0=> z{|xjL>)*yeZ-t%juhN!(D}6mB`n>wP`L~lNrbh>XHMq9JAkwkaLzgXI5DXyQA<#7N zc)&;PO>Qm`x(4^@Sdb0lyuPlUB{tEuKj;Ih-|skvSwca!igXryw?W>D`gh3bui2~m z2{n|)T6oO$KDX7mJYLn;>W(HpH*qcfI{s%0dXuRKQ6KVkBi?Ic?U5^VLVoA14JU$g z(pSAp8FBKblrFQXl7(7ui)I3GEFR?MM;BW1(yYPQO6fYz=X1(h8!{ZLG@Z3Jbd{O| zR#U#q%Hx~lZ7m6UcYJ0od2nS#E3Beug;f?WS1t?nAo`3suI$?gy~x4rpQ8CL8%{k% zo2;UAr`1*+w#NE2Yplz#`r1?0SbNf%YED=~)iJ9n+im5stqDI1wIaAgT2J{NE01o| z{T0zI0n4m+zL&^4)q)~LYb;W@)`|-^ShQ%9l|;8#ylA6E!|Sa$yvAbGcmj5TU#RP# zui%}Dg;&dmQjk5*3NmL~ICHk^2g*scP-d#-XQf(R<~-GWV3bG!ud`7a3R()9N;JIH zqJ>*5651qx?7w~GONRgCi(mdX@yVC2`m*DbuYXkx!+q!|a0)!(>pd_EnhLcZbd@h( zb(M1M(O1w^SoATo_4j)XhTE{pCqMmZu?y!Q6CVwRdJ{Dr&ml00_tc`NcwY+a!a`T! zIGPH&3%ZG~AECv-EieeI0*_D!;<)$N!YlLxQ7fYUgWr~8oa;|_-r;IY|M7jh`4>M^ z?*3g5-fy?x{}=n~6OSqrg`OU2LDXr`PT&-LwCF2v%D9>1#3s!8d1uPo;uU5oKk@Pt zS}@AA1=DQqin-zvIA!s=Wj1#D1l5$r&6ueEX!?Anq^77h*>B+&be6X#zN_`lq)|Es zv&>$aYRfjRlxJnlj!n{4@T;ubx!rag*sttZ^6;tu;F~>`kuDZFly=gNoy)Kz8K><~ z`f+)TVWS^ z%j|rA-1!c=TwWaCg&nWBc|KweIKr>)4ChlxVijp5L7$0ZlptF+=pp8u?8^_+;q-gg zKa!>EnmC0TLs!0P3)=F2+Ouq+Gf%z=@k{49>*~g@o2i-=egziCVGDhy5BhH49IhQ) zJN11(sfHs~L05rSd`%~*zZVwa{i$c08d+b&F7y=!eNA{if>}sO`LbdY)fe8=KJ_2& zhfiP?mdg-vS+L2h0(Zzm8}uhjH)%R}MgOwz`SI6Y-`A`q>`?aZ~l zF5Z7soYL1teWz5Ma_M4&T^y{jO9Pd<9;?RDT^R68L|a%veA87ZR^WVSD#@#-J;xpI z%$2`GdJc6PJf_O1ce9%vx1dYq*!jMw4GxqUn#EvGT<@#5i`RK02^E*96+!Ey9vM;@ao}31Hv`=WE$BG{{IRf#yecJ& zt-5rPW0kU{#)4mJV3^o^EoLcK#phXh(L80pRhO+7+rcDgXVe8->yBA{#X+km*&_c) zMakxbe+J(QxhoCju1|>D)O_%#kRhjB6=oTdxlaae(LBvmW=3*ZVXEdcTBM!raH$cb=B|bXRa;VZxvC;Ncd{$4Wefm9R$q-?{8&%hm{6(A!=LJ-sf6;@h)b@${yOO^=qhl@=f3!P z=_=f2AMFLr1P1Ya&B{{w_{a5ln8x=Kd6q$E!PA0f0;{m^^%S<~ENCm-PrZn9qrp&P zB14Xv53GWB7XQibZ~L>3QQtvZflaud-eTAVW`R+B1}@qPS_z&M>N_xsuLW_O8W7Av zod=x-wt-dt?$e)2a0{<}`(N&nuJYX<->iP12cCS){(SG<_NRO9w7)#^p!$sIyMa}x z0pUYITNyijoQ<6Pu6lP=$C*A#j52%qEK6B5O`a5H|FH0>(C@Qg%>wyN7OY-qGZ)WN z?-1IFdXe9GT^^Ki( zo4#nS`g^t>*lRnF929%N9q@;k;pky`ZC7vGY>PLpwT0_fTI%YhHgnk`^$m@kJ~c5j zh`GyXFl6Y%9Xk&06MO7Cc0`L~EY67*gNJwHuI;vd$JWH!wttW9Jha~qoj7X8(ob1> z_BqQf3Q2cCi=jsd?+PB<{{DKq&|hmG4A$BCfeIV!j~c8%52`edWUq?PQ}N!~fKhxc zMm@*WbNuz&b4pj1^>t^vMLuh1o(*(3FXcdYSWLqAWBGi=DEMZI?P6clF7_9zPull- z`1+LI1NuaFwq5KCX)Us9?@vGOxfH-UI8Iih2#+ zQq{bB^L;TJ#BYHY7A>tiCO)}%zRCvs%bjk} zQ>^+RHA0r>1NP7NmD>4Uo*&%T8Fu{B9T8`6pFADC{4K#cdE!g?h&{u#I3Foo0sHW` zcYd(et-fmO?JBjtZul9Ur_L_)*N7e91xCeU)~x-+d%tR$cA;ub)qBJr&d=+zT&c-;jRUs8qk$IUJuL8t ze8o+!mgGIWyjFW7`G&4mMm@~eVj8NcZ5atIz8!Ma@XL^aiiwVJB-TYWjK zw8LBv5xhcg&q~c$RDV)@iB&}Ai&Ns^85YY=v3O{v6&K7>Urs!-LJY)bh<*ZhC?}Lwe~X3}%O{IxBpzC0vCz6i50H9NnG@n*Lo>%=Fp37QExZuIovnWgSin7u&9xvwB=zIuU* z^Ve8W-dZaPt(SIERIt(2e)2b2QSJr{<*e5#$XRP&z4~kR^>19QMJ&o$rrhJEAO zZ;E5YAz%5DG!I_`LKA^gcYOFADV@ zj=?uPp6BAx<$lfuub|=JSK&P5?Beac{@=f+J|Hqy?)dAyVwGS0@pty8zuc~DTD-L6 ztUU7Eqw>r$M{v@d$?~1dUp-%aJyRDj$9S5Jn>AK`S$dF1P8n&F=1#JiOJ+(}L6=#y zcCnu8dWuHLi-K2n%(MyGGLH~WAv5K^ryjD$Uw+1>%%5RX7tFNO6$_-LuwArvsZE_X z&E~IKWXrd#vE9cGN;BDa;%K6lgVu54T!xrp!OCT}V)F)@G;g*|TQt|^uU&2vQ)k)Z zFFt3FJU2wMgP0>Ub>3XfPS$+FjML&C&VjB%O^8K}2z|xtF=#Vz(c#l4w9sZwXP;Be zCo7a^p;$zI7-~b*kHYjamz7#$OQ{VGHru81&Gx~CM!PUrscRD7hvA0Kj&u5d9{qzw z4FQHp)LOh>B=DKY>mmKXQJQ_Y<#xt2}EI~br@0y96v$h}`KG3JcDb2hN zH6rI#Nv`)uXMtamvfo|3=<+-saZBJ0R*fgnf}|OJBtK2B2d~k$(iO77-lz?9$E1re zbAdTgunBA-Cg}>R?n0df4&nOjw}ZXKc445zF7}rtc;x&*Ovk7leQ>_o{`+FReR!eH zE)J9jzvGZ!3s1f1K+-q$OyS3nx)`p!4R>6bH=Pe&n zdyaG+SV#QTTdcZSS4WO~G}JlNr%bjRI#KiSgl3fNmC^4-eOPYZq3T}Bb73yC(;v`7 zcn)mS6J+7R8!!bwn_egI45LH&ULzQ$ss4DNALSZ47v34(J6yu^$ZBKDZT!+OO?x9; z&A%c3hSS6=o>e>}!71eDdJhhokn0J;d*WvMkXMUNhNeS5PECd5l&Z3=R!!DQ*;Xz3 zh1|@ZwLxDHx%Nx6D$5pGWqg5E#}>)Qg1&;Tf~Tdfbg9+S|6JxC&rBfA5)ATYnXQcX zR@zM|{@&eITei>Y$`4pg*+HwTJY;nhd#$Q$n|!zED%6Hl_lYjhtmV?8SyocW%$;d& zg;J!i;9VgTj%+uwQs}3lXNL@v;=ELgREpP&%WA98T2@(p)*Q<}H%mN%=QdjC zd^J2ynhRds{3QuqiWj2iY*XC_|15bb@Da72vd9i;EYyS0TVM=oJJetB*uoL;#J6v_ zL0So%^0jNeo?sPtgU9>Y4|)i!!Sh%!3AfQwl(qkfPiTwY0?Tke+6>uq-}&x$6aE%B zi1$DS3;A(y1B}27o*AcgSx+ zV}Vt84HkXQXfK=>PVuvy;U6@dKi_?qW*Of3zOd(ArrYbI-cp7NtTJru zJMz?0_hH7*+mlDz&@pe?o3mnpO`bR1 zULE0u2YbwPobuDol7=%&k?NWb@zQg+Otz6Hj0qDDTS4g@z*E-lh*W)1n4Nbu>X)5&l zB(VyQhgE#95nOVyCt~mS#_ZyNt2v>$>)gRi9QjhZ$?V8+oYe06p}ns~9ufR6fu6zE zALI3XJx2OS`;|FzXd4&1@#*3v#uFN3#Rhq0?Jy1*ck~r^=OyY;XexLo;8Jp7T+m< z57c1)re7@S40wbOEl#WbpGHpKC9_Q#?)Y zpO4rF7mD@w$KL~=DSX0=;nv#iiJ8Of`>a=2e>)a%rt4L1X`qMav}zoQx(T%za^1-N z5B%J!aRnK?;*QoddCKvA&@;_-oh4oiW;0SR;r+A35lz&xUURonqL+CO ztn`}Zv-%z3t+~)wDqcYoqL-_+{-pbC8jlBz;=Cl(dhn@sC?f_B$O%^yA`b=+OYI@m zfV{4PrjpDmb>fw+(oS@|a+ft!?nz|5q0KbZ9@XNtT)hd6W{+kYQV*hUnLHIZMY>9C z(PdtVFBYHRuWhJU<7O_S$3z!ed32sz#W2lstEyPzdVi<^mF*7vuE(sQ=7crY9XIM; z_-|j=ghRy@LZj@qIijXF+84Ze`XPpwO^dSz@qu{+#JHtT;<|34+yrPMn+y9S_=$A z-WwTitWc)Qr)35ZIkov`Q_zc49(=lR4txTuz(D9I)PL}_pxNN(nDs+H^4J+pS9xLB3-Yau znLb8*Lam7Xg=-h8p9jvNzen?z#|(G8GVX2pXx|+7w!JX?Rk6tQC9`bSvU&F0TQ7@e z<}97B{I{8l<|PL7|#Rl%$H_#EX3tbWQ!cqf*Q@nOVo(FuxcS|u5 zEYxw@<-+=YqoAkA^99!;&q>-3GGx8BLXC&xEO|!Svuz;IM(8o&8m{l->Me2_lGuq_ z71v_wBqGMiww_%azqmdtn1s)a`WW6G^%T zZoOO7RH&m+zewhk*1D})ZS~u@ne%(X8$kwKa(|KQk2#R=nZRG_WkSpG zc___6c3aai%`IYf5$EH!ugB@%Mr)emH~O%dC4lcnj1sZ_uDJS+(3|*MLgRyX%U2eXvXT=2G#X{$WB(q+Y=v|jYFGTc z^a?fevo@YmeG6TMx{ExuWYreU7ps)OE8(dY3r(@OSY?Xa7q2*dg~!rE#0=%qaH>UN zl>Dg{&7G`OoHs?c;Tzq@ET4ionybw7nB~lzWaZK3&ai0SOe@Y~R^e2wd~#@WW+eE8 zS%jf1X8BC8aOOnIKQ}%xLz$UAEN1yoXR0O#w{)L0k^JnXmVIW9bQgJ5&dj!O_Tofu zbF^Ts73HpQ{XjX(IcfbyC2Rz}~1oq%D{v*f728*s^u-~XO86>{u+_8dLYEOZq-Dj)mgKd3f@9>Z~7D!am307WUCu zV3xZdxySB!=nnNOlliuI{bFs&Q<=4Nmb<-du9%0K5IHXRck#{A8-&*uhGC8~=U=>T zi7i~S#C~(z?=@q2;p(L}W6^xgQ+_vPqP;eHgpHpy)m|JvRP%yTS1pq77C+09^~-I; zt}W_q-hN=Oa^Ucl>^giv%)*SIE&KN>w|4J)N3`94?5K1V_yuq7p_3=HWnLj!EA%Zh zLzyjo%q%pPNO_qS>=LW0)IM3Y^evMUM7VS)ICK4!Avr~!3%*V_4kI{V;Kv(}{x zjrRV97H!$bD{!&DLjM=)`Wxid^8cszuXs&^SU+hMKl zw!_Lw>2AT7c_`tn^}M3%CqCV_qw)avwIA2w=j6Hvt4P~XE*or=FLv?Y^W`T-KWfR; z@9;b|8Lq8u8LoELo1@<++KarYWY#vhJ{slJaxLZg%`RKt`5o{);1x;NYQxV;T`b@o zzPsx>&$Sp`q4}t^2;Y-SJ%j8N&LQolGb~M~vn5-Zu*zD&2dryxRP{jixemfeXh?jX z{9fTV{Ibk0gg3m0m96@E>n`gk@CmHaQoF@k>$fDhMfytJ&VU`=3`Tx#my73T71GN~ zE-ikO-Qq9w4$q0OAwR2pCUx}rxLki~OXy|PjCwmy*g)q=*DKw4z}nGq+Ro^2u)C{R zKArw<*L#Fk1;gM6p(mM{fXoJ@meoy9Q;TCw{?6DJ6VlUDe_Wq`duyh3&`;HpZS>B- zCTJb}&gcb~E*119`x(jfKjZo3HPB7)g2;>Gdzb%1fJGaZr*ZirFxWMkdmTQD=kWKoKkd|Szr}b zv>?Ueg|k&FQlC)442u>_v*P?o7R{Y#v78AO%bR2+1yjTnJgwL$eyHqe;ZB-XHT?H=6LZ6w+k{SXrEchunJlW^(Z_$CDGkh z9NMCOWX&PWnyF0v?6k>Na1Kp|47tVXX+~?o+Y%|DMzv0!UA(|}a5K}Uhzrm<&_&=2 zv=Dd&W`Hp~lfW_PAaD%#!x#8%InMGuJ)T+o%pg1{9D_f29eBkr7>C#Moa9-B*S_I9 z-?7{8ywmQu?;h2Fe*DXy+s}XVE7fFR3FN37qW7d zoBR3CUE!^TRlF91#|8ZaUoJHu&JX`k1NznPe{0XY^t^h0$ggE?;p5K_QHDL)`1C82 zbB}g{Uj-&XS0Pg$Z!L@4)8?nh=lal)hm{+LzC!;GepGS8>$cf|Z)e&#mqt8EUe|hW? zd+^C8Z0Oq~Y{K*uTd`ric!in{y+bUrYgy#L`7B%NJQ=y%&KH;9LE$md<;X{)kczRL^r2=#z&KA zJz_mU{}AWm+9=O(E1byuCfAz{r%)SdYi1o2&wO}3Vjm6`>vut(D)k7MlJ~}A^xePd zq`Qy#J3cE;oD<9?BsZ>|8PR85e~&y(C#ATzrnqy`-MTYvHASf3QIJ>vL3|{sH+-sO7*V=p;@nVV0lMPP~TV zSw-3ktP=1F>_WyX{Xl%K^8B=)(R<`GM%VD3AvlFxdwOip$>>3Uzdz3|_GOA+8f*86 zH|fdZy3g!mmtW6&gK^v}pN869;uk(o=_kE$^+Dk$#_K~4AKq7T?wNt?XAss^ZW6nY zX-nTc+L>}jDqZG@dV=T~Z+HFa_^i5HGQ}@V^>|>I9dOPX>8GhVpuS-GZJ517_AFU) zyk}`BRW5VivkE#3KHB;!eug8if2bkNnj15$r72TdSVwEVwKwNlTT`|)9&%=x2}BO9 za`Tz%leExUSXCu%J`nXGcm-ccMRBTC7SFZvqS;~cXeA-$9^>@tN;qh*TvnT!S%9?C`NpoR_a^?)> z+>$3#LGMt>PI+qRRc8KjZu$hv%a~;O8B;CqOp1D$$>%5gmf6a_M~GPhICL%2dw`Uh_dxl}l zd0->FwRle83}z4A{PUmKkACqpyXod1NaMh3f+rSDgc=PspYQ+hW-$sL6&AV*&t=Q~ z%ESHP&8jO=<3T@xL-4tJKm6Wq`q6)AHqdWx|AVv(nBak@ z$k=~cS#R_y!zANpP7t@Ct-vpMXVFUV&cY{nR3^-xV3SfO+ME@$ZPv1x^4u<1JKyLP z;uv0A>Ok-dGYiRU;e7B9btJsE<5MPBO6qj^X3Jljh8{H%5=N z$6kJ3>&cg2uos5CX3r0M#aok9j-SiB~dhK zZ=mgPpVM@91bU8}f80<_-u>ppmVMZWY&B{+=x!JLi^RIr(B8jLE`5*R1=@z2F}U6u zs@GU!&005quxg|Fa^N2NbLy+MT3yvY%kk14)?y!KVUgR0cU7zUfcm)UV5C7~dqX3;oK>REAgb*``mi?6fJCn=wrq zOWv7jmXkikGEa}!%1WDHS?S{~>-1d`VGIM|5WtUYr6~z!G?6VH@f_Wa85A%z|||?z3^Z4Y%OA^|^5PSkO~o7xWaoENC=v3z`cH zedTwz-)6u3{qNNG1DjCydGe(p_S-vtZ+Aa(uQU&GR3@iRR;}laF>i=lI0jqbO<{2y zt;F{P(F-(V$#ktL^C#Q9)v4l_>5Efr+2*CTcIR5vg7C1wH}DFx5z%e%-AHe@Js42Rp3uf1fCzVw{^^{K~g*!XvC=;&eUAzHU{ zqa98^YVVynE|1In6-&hqr>Mge6-ghV#>4y{W-Fte;7{=y3fc*tlhe7`mQj#r$IhM6 z>}77BD-5Y;nYluEak+2DZ7k$C@7gMIK}i+%V(vwe7pevA@#ZPGQ~%`Wy#k<2RMlR#5RW);s7 zdXBH}pry#867&?|cj-*iW2D6dtdd}zpnnEE$1gmxem}7#BTjXHH_IjAy9hk6ye9u| z;~AxIqPvUhuB!o|qmYqCUkutg{Nd{l>YJg5mrQm1HC^ybfpvGe>$BGqU=-?B_-K3| zbh1B1S^Cs*0*^{hJM#ryPOXl4K7mbqT?n30ZOHXCi&c{50(pN+BKr-`XOJt$Y(p|c zxKH{+Fsl#G2!4^SW|uXmSMm_+vasx-iu5hGMs|nL|#(O>S4a%UTGF=sYRffu+UJ% zApxUwG;FnY&nd}up5SqwQT&$2wu@D^h%sO{{=Q%p<)dh(u*(MSY&u{a4b05h>gED6 z@2|?`#=4n2!K~vtScW-&o2;p3qczoTu*SNL)=;~_nrq!LSb%Fb{ty@&&4_*-v=4ID z&@`AaSYN%tJ-=pcz$@#mL35HfT3ux@ho@q_)mE-^`b#zX(0*mbP`C57EOeBnhII98 z(VPC^g%b7o!y#nP*Ojl(!YkrDuxq5@z!$tv-cMuQA$gFfC&DIOoe}Hqj9OP`Nn&xk zyCbZBOY*QN3y*&1P1abs+09z4+A3ygqn1{?L&ud>TfWNbD%V(j#Y*)8(HB%5o2NzY zitBM^J|Z&^spTE8R{3vEiHpc-YpgzM%9*V_X^l0fv=7HLR34JIh90Ksl13!&;rYtWjTJ7icxZm&c1hTAj%OTZEi*S+nQv$3SXTN}%gUHyx#!#*=As-w zt9h+DR2)n4LJtCpxMyJ{JTHjYIi;GS9|8=m&7V}J#??K-yRzBsI(OLf!-c; zk`a@Ji$~rV`?`92&_l?38$ETTJ^#iK8=Er97OYCO84IV`)YM6qGJlGtE_b>Leax^5 zJ-m1Ko@Bi#(`|>xh zwjccTC-&^Cuh_8Bqx8I|U!|w%4O_B)t)-;Swb_dnDpLi$W&8epHf!Mmd0Cic2(N73 zx5xIKIA)=;63t?UQ5LUTBfVwjf(5o_%N9G9ks)8p;q=q$7xMi-)P9&ZL>~}yh|pN5 z3zb)eG;_J51Aj%aGz0okRp+2zQ#qpKnP=*|pJ$VgZaups7{lkV1k3@u_@%toAfqku zxFBj>NPC$kgEKroNkspW*YA(43oqmVCKM_%i0YoX`3VW+h<2HHvzv%n!R2;Ab? z;ju` zTg+0qPFuJ|s{(z+`AqP-$Zx`2f##FqL3A6kbpEHm&;~osSTpmN%hy4qXL?tFPFm8VH^kYDqQ0LdU_kOC}tP`cqAO zfmKB2TB&-6rX@HfmOs%-@+WEwtKe6mwu6TjPKkvUsZK*LH~rMqsa#&|3iSqwS&9}K z3pR-qq^gfO8d{)jB!8Yo^HZh6xIXDQ7Rs4nXfNn61!ty7E6L56YB?E`T>U3&hK1>= z&YN%1y!p~{%EAjQo;TYH&++*2mXk5YvQLlFLQ}~+HA;*A8}C11OI-(FEBi1E{uG!4 zFA0nM6*A)B6f_aIg#0-6VH>h9pnpN{jzkGYj3>XzJ1e;@}+#88qhUY%ZuV?6~Q0)Qg}X$;+cnYdaZ_D zXy0c<=03F`GWXF{;1u#$*upOOZP8gCe)=i9^RM^YJ&)Y4x)1XM(M<5P;zfCT@(8VA z6W+3sQ{S-(bH>~F+2h12@XFAUuh{q*V=QICB%8Brx{VnBmRM!fly}4~i#IG%{RdVV zK5>||7g%M<#>LWDV3(&}eM?NmTMhdi>|1Xl6v zA^pT35BS92hFv(8m|Ym$7NelSG#{}p`B@IRnh<(Q(*f&h*)LZ4=$8Amnh(i?G1!xB zmjRlXR8-t~C2#0PQV9*{$dl)M3y`@N#f&F-qM=W3|?@HYK>l zvkQD7RtX;0T)!^hmCM?SXBN1Hb4zQ1anNIe$2Hb(zU;xN-IAy!NvjDo78r)w67Nw9 zFK*R#=Yg%Dr9C#uFg{H>4@m}3fnOMkp5phDDXh?k|!lHU%6~}QDBqH+c{PinPuh0 zGpsB!-AY+SGp#H#+sf$$imq3`bbZZHX*m49MHYTd`BA5*)VWN4`j;Cj4?6Ek$!2Aq z6c;cDh%C4Tnr~QMG+#Q4`kC`*sb`1Yj|2!ej(VSEV@d) z#5I+Bt)_IZl@x7OPQGRk7BOFNk@zH>JKI7zWTwmzlhE4?r%>lXKcVJRm_6Hya^_fG z&UEz-!77DWDbh+JIe2I%TPS;)<>$<@aPB-S%$qNdinN!rDVBSBg5{kaXW40EEceV< z%Re*D@-oIsV}V!DTHulI{on^$-~Img?WXU2&#wLUx9#eyueR&2zg~H5XeMOFkrhY& zKG|{q_|KnFFEhu;Um-K@hMR6w2CmPs6$gF&t9JD_uhHY^eMY}QU%~qV55Wf1a;W3L z5zH}!Gq{bm!h&1qVP^R{4;~bby7j($?7k-+RmT0p&pxKP$7G_AgTf4C^b}_Nz!YT9PMkZz#?BmL z?@k*jhM7EPg3Vqs!|}@eDfae+VK#Eo2zgjmY+0fEW-Xqs9-yaRds==K_+`$jxi)p@?^KLV>f^~y{3%xkaMb8if_ zSKk?7Z;Ye2daNzouvWRT@0~tjX<28*CnwIHQN0K5IF*wn-k|4nY9Dm$PGQK2su-GW(p^b!QKZ4>nZ`U-j?uIA9=bRfSalU+;wI%WFqPv)p{J9^8s z&uwzWSnMa?_Q%=cYa}C;{%!KfSmgR@4O9pBm$?l3!0#!K2RSZ*rqbDd%&k_}yOUs+ zp!Sp4`kD`(l)tI@I1L4s@%MdHSMj&u7_ER+{B0QJiq-7&m7bRU)-Andzq06f{~uh6 z+lTMh*?)i7V*m4DgK|sp++5sW*uH5te$dTh1EUZ_Q0dt(hG9 z2Ic~imA_R?0-N~d*`&Q5e%Y+s979imGhh#x0o_B4)4a!e+7DS<6Fo&+#4xalXB+L; zZB8(YW0j58!a1nd1R99W=^2KtXO{n;Ezi-skq0T)F7&sg9Fv@A`aTlZxm}Ezc+wMboXkFvZHF)2*yHMce|rcxGXyGJM$Gwpa6q z=!s@tGspOQXsB2#-G=P9(!v?)|0xYkwPOhtT~@=l`pi>*|%h!#mR!PA0gwlHg&Me~BZ zT=H)V=8IFvSSiSyChjQCTcmy-dU?psFU(tN@z64h=P$OxbXrV=O=csQ8 z7RgR~*K*RGmXen-&I+@qSU3|sW{mg+A1*Z+v=8(Y*a9v=`#|&f>}Nl#exINI{O9(A zoBv%bLY^(U3Vg!)%)k7Tj=>}7E8NHO=Yvn+73Lo@4-sDs`V9VDwy*^mZp;9}qk=vH zXZRWqJi>jxw}-wSo`bFeqp**Emg6j*kGA5U@4Yi@`MKaAUWfC3{O=_*KYwdu#lg_WGEi(pKOY zxMa%QiT38`p*C*%Xqz;9yvrsx>xcw2hxJ)*gM~QJXq{s->=)E1hNT>QsAk z!do^vWwd4qF{_zAX#Bf)R_Ko=EAG+fpRmVXcrw8%Prv$-y*p*1y*Bb~yZ_0@?cT>9 zwTFg0ZI8c59qC2sE=bx{+6;OMbsan?=qtP4J1j2?Gn3(&1ILfq zx@}u+*}Aow)4XQ;R`vF*+O$!}(O>9m-n3)8W)E>**oM9zc!ge|(^*;at&mv@tH39D z%t4GrT;@v9%N(zYTX{{~np(@Ov#U&Vmt9^6bBQl=3jFcEv5IFD&nMjW`<_?W!YcCL z2CTyORra|Kp^@;t+}B&sH_$!c4$Zg?^btHGXeHv2peLul&(*6ulW<(${R0+JKSh6) zw(NTjfjKVrIqpatAES`mQEd)S zE*=!>arl@6Kaj7*ag0n@_d6x?tx$Xlr>buyU=?ad_(T2jecAM+p}CRs!{?4qM;c4O zDzJ#J{di{atm1FO81Ra867-P2h z-6ZY9pTnP1kHNn}J*d{{Dc%D^4-uLY*|%DO-w0;G;{vO|HS`%%^V8oNT4mr1!`H2x z+gjHjg&u`3nAyh-WgEmP_2o;fu7bW_)^e*YUuN}XZq=17w%U>^qPoXYrYZL3P=C@-ZlHp|s`0!D#V zDkDxyVGFBhMN+O{6*LySuyBGrsDaM|t)w*MG!yof;f7b1eA>_yu?c(<3s1Iq!6b2t z`gsZ_ic#Pct^A2s@=D$WE6#EJ!nQbfycOk65UbFegwKb0i}+n@nI%+p*lH@69mGuI zrK-#LtQPbT^(3F0D0aXz!+aw09&P;K-$*Fh6Dp{vTYN4mVNbnK96|@$7xOhdV$*>ip1id`mM*l#IfGc1a z*a8lLOW==hUiU5OEzA@8$X>Q?0)*3ue<(wX*bv3 zc%$Yjf9D6^7c*RY;|<~xG!wifFbjP>aLV^?{txy0prgPeXeaXCUiB3*3;cmT0OQ*ZA2le`PoSC01bztDML>YbUeMDHorNx6FcEJDPUV<}6ugZ;c))jb?2y=ZD-|X8i0va!`Jj z4Li2m%57V0#paFbD}r;-Sjeko;m?InsQr{wS11FH95`~}Sk!__s>>{1Sz@7B$XYsT zth2k+<*GBkC*Y3$9#`*yIXs(qedTYqL7j;IE0gboQ+%C=E$33-PFJ4lEzG48U%&z6 z(4DV$xh?~>jvwgZ?5(s*0Z+gcu!M5i2WxGhKj02#$_!L!)*K9iUs&fL50zY0a@+^p z{-wcsH+yEV*8KhKvv>~oUkox-$(bSdoyU1?h4FavL_{Z-}f&EHKOGEyq2Q-0{sL&k$wV`G|?Lp_*a5$S1Vo? zd@Xnx4y%`#ektbn(eK>SbRyvu#_P%)U9oImiE{7%-w&$f&m?=UOPMUkT;>Tr9%eFh zWNX2rUW1b6;&bM@3#Hk>JkrRzBjOduD!IlKnempBr zlm~+kr*V(9*Y7m(RQYXwxqp{?Vvq;W3i{y?;Y>V>XmC068G{Z59Pt-PP3xSiB@oKoECX1zIPd&1CY>L28_r7i@A{Kt=O)`DSJ z*WK`KX)(9_;dkN~)q<|NO1W}qIMkZ1|L#rp??3*L{pyzAibLQNua8jwLHnTJ7@k4@ zfKSj=U=$t)pHRm^V}W11=E5;*Je-d{CG-@0E56R;YfUf_&;9l9Zn2;Ir^~=){xbd0 zWUSnA|DE>g@Yn3=m!Gn`AG*t)dwqzFp8Bpm`O1@;kxbp@-Kp=|D@`vrS2zs;vE)OJ~S$ zOMejB4Ub_SB7M?Z0lj)o#7-FA2Zw6ED4>`Vd+RwIceC$$LAJktRN&rb7*gEwh%>gZaty8l!<^ zkZGHJPV;?sA3ZFdA&)jMS|qBMZPU(ecI4D?+jVe{oj7|;KHIKNyaYM&(t16`Z~t#B0+;B!96S^B z3jJ@aqCVrmcT1lp8VWfQVuSO|cHu&^UA)+0moBy12Nzp)oBaO|&)3`ga7#b+7*?&c zj*AzYwCK5pL12^fgEa>8sCIL{PPz!%2ZFp2hqULf{a@}(p_ z)*nYd;d^#MKWW;VU=_cwZ8KRSd$ix)v_CP^nc2VM7v>OiyxH}@kipX5ljCM1kTnzJ z&f(AOCesYhOwj*~j}x{bcSZf;kbk)o)1Dd=iXOze9Y1 zj-kF~&PlH{{may1YIZ40o;>-MAP2UYjQW5(@aSrh3yU_w%$<7J zx-ruSoq}yswc`Qy8>;qOLk)et8!EBt=Rc&{qmN@j^q z;1sb+Y^K%3XK8Vt*aTJyI3<}?s*0z&6-lwmqG?u9G}*4;mEx&Z4!;BpQ&u?D%EHsM zsPSlpr&xJ-isO^;L@NzV_#0L!Etq7b`IE&eunUjztOB2iRr1GKao$*Q3fm~W5->|) zj$@6Yp#KLhi4`tW&5hZH=sn?_6mbZ7w2^b;t?1ks3ulhD&{?^X1M%>T2(N5^)fj`hmU=-F(|NaB% zEbs~%i?0R28{fL&dTBH4`{SR#>Pz<3Z(OZxmaD#bwdzgGFoav+l}~*7lVT#~Hj}*% zqo@Y-xqq=QUHw(N^}gHfzVhtr_--ewj|b1|*>JwH^6|amQ-L#>^^+OSciAX;*L?zY=DC(KzfXG|!fqDWMNB*{4^$x*V12!ata zs0d0ZGF8ONIp>^!q5@#g{$2Na-sf;&?D_q%uQ!}fRfwFs)?Rz}AD&)(;9uc+RBdgl)mA6#Jwm$(YM3v=bK`mwnprMp@2<-{ zk%xsGcX%Z{Uxm5kF#n3zOv3Ml`&0bm;u!B!!B5M)k8)Ff4CyFdQwgz2z$j&f-<`b| z#xv+H=RC&q3x3eP9Qq1PC)k61MA;`Uf7Y=P@8OvTZsBJ~@6gX)@RQ4k$8%A*O`Zxe z_~{L)N5rgEnVS=$4}{NF*>HvA;YRB3BzdbPg~2}MB9}j`eU)Xq_4~eOmGiX_&Fp%2a_@@RS#jBi z=ts=Z!~UFWhMwLQtQC0J`MH^gKilI>u)3;ve&V9REE0C{#wmtWbF# z;g}vfO{@a1z%FU=(=9bNsF<13O+1^V$IKL?q{U1#80EJLCV^A%$9i6YS-3_=@r)9F zOguD6m2@Cr70)XNCp%_|nrg98Gt^^|=XL)y@kw;#6pN0UY%%*MS?vA^7Poi2#q1wz z(R;^P%s!Z5oa*SFv8tH86D%eYt!Iwrpvm3G6N`3oXxDhvk=FByGGcd zoulmF&fz*9-aS(12X+h#-9ulI&az{)(^+>rA7BOfIs#qA=ck~tFb{;^ z1crf6DBmN3ZQvCAxv&T9LXYR!=UZCKR?peZx7{LL1vc^e4SWRuz$UyenhTG^K=28L z?&0Tm;0vzNOW>3{*jsq}ZFcWN52{Dx`$c5e^4P6+-D%g|dXr|JZ*6|3-FDAi+SSZ1 zAzsJ(|M_o!wd-!aQTNHQrT0UY%dO3Cw})ChVlT9RNxc{L^e97R_5}50=>7B?)lZrU zeI9y0_-V;afmN6V;y&|1%mKag-aMO6PVMT&VwK6WC)vYKJ)%9$&$el$-O1=B(-+PN z?J=D5?i}@pnDZGseS-4!$IY0ibMjQkuJwLf{44X8&)5D!_AQgA|9YREp?(jZ6=s(^ z^ysd0Smniz?X7i(b}AklGJdShT)e>EUAx*o{q`#{$2S{)l!tcf-d)-iguli6T(pDm zP?T5%K4FJ4yu$tUKmVkD(B_@n#WM7M{O-bo@iF2Y^cUuo>HB0A=1E`Sde6Z~@d>YG zKM@&qy#Ka6+bkhD(#p!>^zW!LywE->_sbW+ERbgnd;+?LR|1BCJ$$*$ap#-Wd7!oM z8aG$uW_xNY6XbDisw=Wn^%ZuysoG98QdQR2P-b;?#ZEIp|ENhday{vdqpc|azoE>U zn#!!Pu~g4f&qlo=#}e=htitYI<=xenIL0}VtM_1zMY|edpqfmptw|5@NJxh{USRbn z3SHIZ>-FL!_7A{96nhEKeuCY?%n{MM;Ad4Eqwz6(93f*zmxYsq2-_@(C0w=V1^ksq1^TNR#Up!_2P;)idV=}@jeq+ zMZ6N|DgGMffm5_gDDOx4Vrr`*?L-aSK_7(tTIQ#JRK6{JF1U+5knHDaIFWMpc!IyT zZs*bQpg-w#Jo=3d{5`A77q_D!*43nIe!QfREZp7l7vndVpQ}1f8dPl+JClFb{7-Ji zYRk=BB`(1eTaeED@%y1351L6nY?8K2=Ul@o`5DU%R>?^Y*Hac*Uiy-ASOsp$N?si5 z1@UK6QT&}e8tI8kodh<4S;Q#*T&&`> z73nJhtH3AHRNxczmzbH3Rbr-zRl<2ix{B9V{4wYWp})W>o>kzIa8BWzd&vhUoy#f* zCtB=*3F-^sVTp@+TYeVUB>KQ4i;0{VDmW#2-x!PEKhEOzja9)cNB54_eU34EMvGyN z>=|!|caL-0i|3i$W9-PDQR0xJyGE*x>>Qz^SOs1QE6*wL%fTHZRp>1+OVrNcs>p~D zCLaf0j_@L5<-CGld`}3DfiXD3FC5Wk{5g6F#r0*EU1op#+uy`AXf5<&lzZ~;|1J)K zQ)hjYd#TgQn!#cA2j2+3TKYdd2K5xb(DNBKd8jxAE}5}ps?A$5TmBU^7C#4sma=Ts zV(BU$u3K$A`uDVJZo1y?dGsNBpv7a_*F0*|#3+Nu z4U7>*Qs4U=;+Ej2G&y&$|RunMz4Fc9qHd1%jpDBH1br<-9eOVq!K-V5Nf zOkfT^d%54QWfR?oeuLr>fh}k^Hk3#hb<_dY0Bk4P@O{>wEYaiWHSAE*XU$1B?~7-Y z&n9u6SnQ@i5o?A#Z z*+#1<-6k)xbO!QO$mu9MtpBz;mN74n2ZC%Hr}4qMXcpR2gC`{)?+reVAFL#IgYNUY zzBHYHi`c&dABizae%4HRb@|rdbzcR2pmQ0eJns`LgI5B-t@35{nY=~4J^p)=ZA|7r zJ*=Yq4aVLdbPsxf@*3A>sHaKa5`PsrG6Lkxytf0SIUFJY>)Jlv_Ln3Nu;5WNwQRI z0-wMw+z;>laGsmCSmkwNL!&Nbe^t)PlfV)f|u6$X=etyisO_}FDPz?bQQ5m^ejCWCX%;Bz82TdNjds< zP*Xzs3e1vrWQwI9oht6(Jz*EIi1(=Y{tp^UptE>gg&t7C!3h?BV7$)b4@}Tw@llgh zu~C!6DbWWeSWMJ-JBo%9InH8(f>mPnk5R?$9rHgbdiN;lE3gS#3w+`k1-%4LiQY9v z<(b7Zins(e2`iZ8{EFN$%=SkNwaD#5#29EP{|A3HYZ{JWhFi1(w0TizWl7z#C{KoO47cx#^Z$#5Hh} z?_Ejr`NMyTY2Xwz7oLw723BE@P>biDu~x6Vq&xG8bQU zxjoe4F+Jy+o36EHw_I!Oy0jOk;1^+Tm`pbOv~To%L;3migYdsncuU}xnMp)v!w;{U@zuoNo zv_A#C9@qx1p?~CiLfqqZFc0hjx4{)= zn&lLvSY=h3^bS8~%D>OrHSFg&0u*;HRDErnjtnWhTuB9f;ym^Z=0z}M8*s!7oAsOTeP>o%Uqv}09?nkzv|r}!O~ znVp9EJh2DA%fcw0E6_{e8Qy~))bZ*}tEx<~O6C~?eTC;V)D??UPB+yAx=)qkmd0xL zSyRUzf?~aINMC`MG^<1|$r1Ot9F{##-mSQ;VyDXw!RtW<8T|6MpttyWA@9lcvq8o9 zV9(C~z$hU;31)qyYXp5Ae+)52!FOU0ScP6s_2z&7YDl8{BSZk*mYOJZg+L{__^cc^9Y4|%^fzDZ(sGWwjRjJCGRll)1$!aUf zWhD=Gwe%Bwvha!bpg2|uw3O7PAw7kDj|y%%&!ghJDGNiK0-t23E>OWNTr*>woxIpF zie`|{>M0>kNt!QCfmP5`R6#!|oL&6A5U0Q=o=xIrOD9PQJSpn^#LW;_h%I8LTM9f9 zJwx6UX)1viS6WG+n!cph@ot_dKIo6{0jXsA{&{U+W>^{pTo>PwO8EJ=iI}JsA z67+t;StXoLI6t3HxQ>b#s)AMgIs22*Cg|bdOMw;W>!6#!B(O=ihT`w}Y&Wjqlz;wz z_;3G3>~Rj`c-{%uZG!UKW_V zzqh^6wzWO}?Bmw*rIyyY_v^~1B{!Dy_C4BL$6g(6;Mjqh+nKs}n!U4XfihKK6}W{Q zT6#fvWbxE8|HB;6^aWFG#mCES`urKvZOG%j>Za@M#=CB}=U#0s&k2mezCtqRn3E>c z1wRVK><>L4%_t9eQ+aS7ezD5l|75w%SU63-7POc5K3Q#ZmoJiM#p@}|0nzWlqvA8- z;FXrIwh^nqDP+|4A2Z6vPM>PyW=yjgixx;1LF1s%M_?B?1jgVVT!Pkup2BWr9z!!h zTj4RhE@bMXukbt`YwuNN~o%|lv7eW9JIFR-TiJUi3K>*8H@KJzx(A9O&otng9&@g%E1=H_PQ2Pog? zd;;YM^*b=ydo5q@IG-cM^24Bu@cS%1BlH!#v?pq^tfo3$Tq8d%KHIuNeQroop)b`~ zZuFh#Psv+@mJ;YRXLX%SmpcNVv5O_pk~Di;7435An7yuM_T_+f0BWysafI7_Q%r^& zd-8q><;Qt1u4fiF1Q*X?_NS1i7G7b8i)R!x5U+p08sZV@B#u-3y|ZlMG!%cI z=juGrO~QF3bYA$CQDp^R>d5_4xTSD|G!J|&WSEl84ofy13w8f*_p`r#o`-AZT9gl`tT@+S(i~DS8>IVaF8SZ9M}&8U-VWLcd{UhA(b;?y z&fyiZ*O)QR{z8BD?4_0mypGwjR55d2E zs=z9~JfFZMoQqA4P7=q!GD(LgiDx*+PwVD?c-+ka!6&{46n}W4%4;lmZR!0m$DDX* zh9#1-e{iPendt+eu~77aY^c}^YOJwn&8iSPUPbFR@< z@Xz{Q561%$BSPib<>2)5Tcb?Dm3y1mui-ssiEUhnyaz1jCo<=S`Z)!CkD^Q^tt@g;Ez+6q~4cx>tO zyt8t?jh{VEd@^ghqP#zg_tLJJ!Bi zJL@^LuPt8vp3Q%6nZ5B=FZFxi6Z8}G6$&olp7tmA=&me%a^lEe88m*Z&3<>O@@I!l z94{>d{_r~onM)>*1s#PW8Vj0<%S_oXhM~8ErUJ{rDde%hHh6R46N>kQeZJlJy>yk> zqy#%08*N*5McDdHKibBvKih`Q8|8U{kC+dNNs6_c!VHT`I;eeyd=BI9VAnF`uibo3 zLC6CVn%ikCw=<`!?DVNhJ9Vnsemz}lr%%_o{z+4Xv=Zif_%~R3M_rEV)tm@?tu^km z3a;VbWpYyCjHZ+He-7$13x5nVHu5^iySm5SFWaZjCp}iSOGoVq*4*p??eO7u-qTGb z_UoxiJ9DZ={L$1QWzy_=oP6-UHaFc>*4`upZ ze0~2l&{FVEovb}5PYI02-emZMok8T%(l7CPif0q}B*ZPjxt^1=PJh0z3c2(R_3849 z6c$8SVg44!DdOhCno+{xcxmTD=Z)1EIploQ#_yGIZ*|y z;_rJlas8fvW5W7A=d((PQ359M4C2`{*hjq*I=pXap`&nMy6{yfAfVXOk5cve9} zfltDV$3nUa+DcIPPBi=T8>|w}EMk@Ocm*DTJzy2M#c_l$_r|cee4yV8^unKi#*Kn~(=;%3R zCq5SZEtBlXo{1`axCeKRu|vDY%5RIV;#uXu_Q4@mVeS_{2<82(eBSdp&tv4Uk(1^* z-~w{qF1tk6e%^-H@*1w0xw-oKYn6%5@!~7Vd%N5&yXJDej?5Ku;b0iD_?df#W0(=* zocjI$b^W1`XC|Co{Q2*GC-(7kNqAm3_ud)t5IY$E^^8Z@k>OjW`94Xxpu=b?(#Iy7%uQ z4nZ$rzYm%D?EWDe1zuqeh`tbhTXq!syg0JtI`rshPrmq!J^I`e_UtRqOEYl9i^r89Nb6qK!e5(wlPyi+q`9SZTZ?|%4@+7Ghk$Y z>(al6b`;K7y3jhm)jQ-(flFYN{-Z{y7lgKgKZQKo;gcq4SMtcWC)uPqvuyO#x2^YZ zJUBDtwZ+%^`TB3{!_PjpRiAxq8-CejpMUqQvftiWu{^Y&khy0+!;BV!AD6xl{UR6# zzb*GEG!y22eD)hh@>IyKWe&(+lM%-~=fBOd?4oSTEy}bLb>;f~uCcK))bH?~SLb`h z`&wm9O|JAjdL*Z6?R3*IJ9YZFojG;fPMtnxXQ)$L*BZ}}rqST$dz_ar&|GTL)rW67 z$?Q$M{_Q7g?RX7YVEf#>GkccFlPlY;BFlt(Dt0cH?{vGDIV$^pk7hE^TbR!wKmNZ@ zm)eLc1CAdd@-6O?92IETH; z_Gk{6eRRqwE!ieL26lm2@M)-u@V&a3L0%@qfE`^@yXT|7=Z5tHP7WWI`X*IH4|8XY<3 zakLe@F?BWMh~R4_TX&CUiK~mZx!L|;4;7v*yuajXpdrCnew!>Hio0A34=n zYo{C0Rcgg6@JoGNr8osII6EOTQh&6(EcROBh1ui2hsxdBAISVM+4sssfl+*gaYugY z;t;FAA?PHbWAZ}FOIdUdlXx9P&r4Y#KJkpgQ9YeNJIP6zrwYHvIgH}@#q$cs5RU}& zJ(-EKrJ496OoPY7XQ_DoB)sRtIUZS9CHxrfpY^8Y*R`#7wcI=(lyQY}%uf zEGgP)DvnEnEEU+~P@uhp=_>Slytcx5;(;j^7v=UVCmto|WwG-5Gn5DCb{vt}pKxfF zo+mF09^9z$fvz$}J)k&zD|<(&&e2l#jxb+G_YAkAyM|e`XBBv5_b{=`p6-FbNED@fDZp{F)oCvm0)`*>1i2HoNJLo9(vdx7)3E-J#=+x8J0?>iVm-fAG?) zuCObwy~b|7<2Er3dkNX~L#EtS*EQ2lA@Wx!z6Y=Ka75eTKJ0|1L+*;MFS;n?lM&bg z7w*`zlk!bCGXK+|X9pWNx?d>YZPw!H+D%B7KKlpJROko6EX)V7Cx|_SS_Ld+a9AIcfVIA!&^l{RzX z4C^;?ptbMuhO+i&En8w;`u7#1c>M%cK}YE`Y>4_hunGQ^!Q*((5n>k}Q$By6UN&jo z9PK+?zGjtXhIj1WBP|7=3Ay_le%@?fZ`x>|ZTL>}&g8VL{Nhvla^v^5?uYf#MBo!< zm(gInAD3PbECQ44Jrt!L5PcxHg!?hc385qBJddonjQkvVR`Sd8t+Kk*8k=fW{JR=n z+}H1d{5}}`TZ@(rpPW8@@|-&JYn>iD(^RK_n|U94^!)Bvk3R+Nh8da@+3Jb#nO#>C ztIulfgbC)NeLlbE5%CJlQnABTkcaE9d7l5=k`|*KgUoImcC$x8&x+hR#}96fhrSp3 z2*1yk72^dyDlLSpdHPX!WB47pwj$Y%l_xu|MR}az2W6iUSu3Fo5b{5QjCpqd!96e! z{uVx`<(FZe32mQ#NNtMS7Z~)%)F%qtY23?|W%Pbv4n>&p{4w zW3hAwe5ytHEF?XvedvJl4D~rKg~Cgt#J)y~7{LAIsy?fr>WT@rv!@ zHS`o13D!_;VgG%m${L%>)KkR2BHlk$ZjJTX zV$!_yZ9MD_8g<7eB|_$^cC{O=BF)Jd9N(IQjoSp1)F$nMGTU@NFLd6R`Grm z@13Qu6Vgut9mO+Ch(nU*h*dm~gzo0 zm}UuwXKFt)`xi5lS6OD#YDUN9HQ$O|wDxZUa`K|AV&{zY44Zqr~hR>Da{g zetwfxhC4kaV3otWhU%LBPt>k4;u7{1rp7K;vBQ~+T4tTeg~Oi$pTH`<4s07FHt~$Y zJn(=1`H!08ML+q&1%J}{UoK|94w$F-lrKH=5Sz}`yPGB zo_g+Cd-=7t*5cV`?9r#5w0j==Y+=u6RaqThtq1-~vj z3t4geeDD^!%0*XSVK>})o89%${dV8ukJuB>KPzrw9_RVCFNXI1w0!kBaSDD~pQqoY zUl)7%^_MjV#16t)i>AptJ8{-H^?1-&$k=Bd$oGJ_=KS4{-jzR>xgs=_A>)VZ$lNhG zDa=NrwZJfN5`4ma7^Z8#H|4Emrg_|q@!DYsU-8*jTicNFW5gNV z2KJYK7T+v8lhIHn&z-G)4_=g3?b^z(!Yt4my?R>jK?6hH+nz%Q+q6XsZ0b96l~GI2 z2M=uI(F5WKb_>$?*>(7!a^IrjW0h6Qo@Mg+*Zj7Z@2h`d%Gg{>l%$ErzqD7O7g9|qQt6e%dM`i+8P^= zi%&S7I#s6tHZ zq+Q5v*WowHE@eOD>G~wAKar?E3q0n`%)`KB3E_b{eTw{_WO;$}(?2uz9T#S-k{^Y0 zcJ)v`10PQDnu^Z7Hznxj{MHd>5uf0Z4Q6-n%!U_i@eesA$V2f*p6k#5(GeD*Lb^)Q zEK5&teiZJVqp1X}A{`~@0eMC_|LB>8qvw{;y|A88^3my`Qq~)JZ)EESdw-OtALP{X zxH4=HO|yg}(^YYYU9LVy`aPi?L1fv&D~D!SQuJKyWloD**`&Uj_L_?8|vS+yX1V%Zhb`FzQBy%9h^D(ETn zfS4KP*s{%w*5ZX{)Ps4oOIyv^bnV|&*|cy9^FPchlL5zmpV^D2OG}wDZ<39hKH3J1 z>T5lR_RtJ7d@^Cqc;(je{3-8DvDO`46o;UHkS_<9(DT8U;^&az5_&!OYB@(wA)gkH zEzd{i!Q;#KGj@jSIn7@&U+?$q%dM?N>*wv2H#*59+h@cu%`20wk8U#coq6IFv=#OR z(F5|DOSiZBSch(1<+o*~dCu}>>H$q(INuh1u)@}S@tJ%iU;pr}yd%4#B9)f{myl7r z^Uwi%|I?3c&ax%8;QjZsTbbQL>{#YG=rF(Rh>!<`{CxKP#HA#QTQUoB#UQW>g=gib zZNJ!$TQ-YR*saLdi`ctI&(~h&`049m_77;V)9>}n$H>1@u->W* zHmE$SoTHiiFU%5pUZAVc<58dIAL=S!1YEOTv%>gJnEO$0r}C)F3L!J4@JD66lGQ=4 zr8wt%?cnqNS2(3O|Kku_l;pV{O^8?M;hg7>gcKk~19(6=ri>V+Gl3mB)rQqFsd98!Rt#gLcJa zrG8^s=^HiwjDP&srd&I5EJj)N>_yH?`%qrl!p!%qAU!DZQqn&NYL%P)L0<{{D&8}T zmU2F)g!gzntFVvfw|FI-RXG3Oc*OU2q?^#k34FAkTet^{oWm-KE<*(t@pC)A|08`R z@X-34H?IA?-{O^UUy8UTW~R%Ei=7q9UBS!ZnI-ke3>CY9JddD_BpjJ#2}fpYw{r5) zVAoLeT;0=t!XvXS?vV4%;!V+O4$ss*cI=RiuN{O3=V{*$_oAbwIlsz*Y0hUGIa%Hn zvTMmqAq$Q7jL$6$-vK2Y2Ok6d*5rEWL!B^QNuDdR7p=-W`2UVWMU_0kLNvSydr zwYOYrx8HlaIOKsR?z8(IyT_h*?ooT_$p^$KZ}fb_+P~4kZf|~<-ErSt%3&kNq}h$v z+toK-XU%TD!J6H4qg{8)&341>w*=qhBsz1sCvW!1tT_-S3f+gy23nEB~Fq=)qy{HDD%vX@PqJI?ik7EMzo z3wsIa>!6)5mkf8{pQXnHr!eD#KZbc^?!zYBLs#k2x2xuY;1|3t ztQVx-P?MX_MCT?GdNg_6|jo2s4LH!XeLLS&-DeArWorm;d@Q=`kI$m)|J{NQtvOds8 z@T%ZD;CEnrH{{S!r}5qfbEfbMY$3il>GlS|C(2@}uW+vm@>!hMvLbY>uP;}=MuTRQ zO5E%!*}Y`qogizbNZ#HM+XOkiFcRJmyuHk$s4rAX2Kq0`U&DI}tMEA+Ud$AG4#A`9 z&%?QebMEV2*sPD|l+ye_Pbp+q;JQ#hi2E=IUf{Z_BX+WuJ%vYIhFb0qCT$}33+W#m zOX>CGeW`QlBxIlDe{5w1ABEV$vxd`1=-sS2dk#~mXXCuHPH$oN@h6U5!uA}Caip`H ztE2G#(qY1TM&92-4+=depFKm)Bclwyf*-6j_iMLfIeV=*r8s+)3TB~Tn8K_z%8*5K zDaiiV$bZdA|6HuY-}RHVd&O&6scWU7kdac5>1KNhGv2qt^bf2!YmI&$ekS*Gt+Ara z)mE6f(s@_BhxQ+^N;aAbmAu&PHcnZrd^hHfJ#|vi>|oX&{2GUha=a`Zn@fSX?~O4 z`^dfaP>YAG)vGP7YoFJx?dxsqnHQh5wy(cpU3+&8^?S%!gHQ0GT+!@myZqX#HIwtF zzg?)jd**iG6=^3o-6(Fk+l%+dzb}c7Z5%c zJU8^S$c|&4iG4z_&@IjH7S~*HZ8Lozd>#5g=qist`;t{8RRN?{3z)XD1sp zdZ6;&$ky*Zu)B?%GEzPi_6wq=utOO?3Nt`M#}Bd*lZM)`$%Ad=)M4_lz$*A#+PwaX zd?*h;^SJsva0|K#eIB?3PJvs0Ip{FAL(_nEEv<}2I0cej20%VzBTytsVUq7RT?QdN;4KZpFI<$I-vz$so+31<~?O8HiC3cSK&FpTG#W7T`y z?8mWa%}Ovo#OF3W2mG&iY8#G|(Us=7h%IR|7s}m&@DKP^G*UygT8gc~-m& z@XJXzAI*D^4|ehd*}P~lCE`8&$*>A@*34M*{qb|qOC#&m?H?plB*?H0V-@eOg;hMS zoX;m*dw%hc!6(1PE8>)b&qAC+56F31zmey+@mQ=i)g@WO@x#j5FU|cX@Ws0Qf^IjD znhXN$@^FIq&VwC$0}JLSV2&*L{ZiUR+tl1_9~;) zE6Vstx(hv@TxI014EFRa*PIS>$Y>`enQP=Lh6nkxWu6KCq0n1g{@dA^9{E$kW_xr_ z7A^OJ`Q_|%mtm`XIC9}KmO7tH$^tQp=M{7nSOYx-&hU(JzTYIxduPKK1ZL4|gV*z# zkXJWxp8Fb!?--?weKOnv9R*GytM+_83H5o-%cWKSh}Q?@8O84gLQhFMGTTz4=U95o zd`pX4XqgGi~)qo%*RNKmBw4x5`OcYZ*x&h-c{e zq{Y2wsqri1Q%Q?mVd-)2TSn}A=M?=RcKoErzH8|*%Pci|p`{$2t2t+~S&r@O> z1T#Or@8ek|q^0Z_B3|JdMuAu06tRkqa7$Q^C+PLSDwOAw@Z(k2UTIgLpIm#TU4Hdt zI$nLa?>5R+buy)hv!^%{dKyJABEz5xCiI(T=x8M&e!?tWfzNwZoK1WWv-B~ zLT~7WS6{SF-Cws>ZJ*bk!p^-q+OP>jY|!|D+Vw*=EnZpXlgUsSHEpE4B7-LkveDB= zsV4-xyfv)1_8XF=PflC=H#>+io_^^$`C^ziy6=gHq?ypup~pi%2;U0*AoLUTlpX{7 z*sE`}wF63V!i=SM7=AhXDL>3G4&G>rZ6naGHF!X>DQjJQL^fQj}oQnN!Mh0I?8Ci5k)xW_Hd$NXH6SCdhZbG~vWN)ECC|e&*BFL*{ zFCouU&jr88Ntcfg+n`I3)utI@=AFs5WB<@eevWE89r%Ma(?n+7DQ2ef#6&O>x$3;n zDdwKZi9v%2WquuZSsM6~>7_NEEuXQ`c(PW^0=IZ}Iayz+^7oj0%$R#1)lo?-U zRhSbhE8c1)0jH=3RP?>MjD44x?^(s`DE=5?63qd-tQ4=Ggma4T1^LIEKV_X&=C8A= z0{SG*Z;Q5q@3pQv!j4sJRlYy3DJ}TS%JM(eXRkjp-$QOJ#kn*L?@I}r*E#1rc%1hT zACaq%mn46avRq0_c55a}e_u-XTUFT>&2MYwIp?F0*Y>x##oco~ozmP7tt4l)7=&X< z_8Qke3UY7tURi6zBQS~ARf=;yu%hgh|FDX)R;r3J-&Yl8tZ+r&hisH|H;0@{c1qU! z`r2iAYs5L^=i^aPe<)*lkW1@&KOs&@ce6j}EacO|J%w5ChrW*PaXdXdjXWuV&sLeW%HdyN zc_~XmML&p~{IhHVr<^U{6AEV(-}9mG!@lM7cP}R&oh$7mHDu8 zVpd9n(axXU6O>iUULT)B%S^JXp^i~@4zly>(2jw2Fk(QcbiHGs9f%m9I(t4S)b|Nz zm5VO>oBidY3+!)~UTBwId5Ku1*$vm&)z@8V*WJ`i$E&Wr!fw6uX1nKsyY0?P z<>%jT*WY%d-E`+I`d;sO_yK#O<&*Yuhc?!Ga4)e&yKZf*&(L1V%AdAqs`@V42um^o&~=;5bZ$Pe?#vri~Dh5IlH zdJ209xrSdnr*!P`hP~XWtvs_``}VN@BL{1a=v$VtIB3wunQJySXR+(T2ryF%tZo)kQ_>|jP;AzuZSVRi^!nJ{OTYUa|#(r4Ih z2=mbYVdfY{p|`W*laK7(wI6B@$nR$MkHIS6{IXf>!tTNkKKa;o9f(rxIeJ*EvSr^M zI}jTq|7}!Ew0tcee*T#)dVjesUG=`L|K&$zvyiv)%g*h#Y5NwPr)Fh{OY#cyRk=ll zmRDS41*OGSd*YZip``@6%CBb{9k-lml(zEs)Ajc2nd9n<;0-00_E>eixI+8de9mo6 ztkoZj)vn&-)rYlzi2W|dDt3i56^*Oq{FB%DRM4BUJrYP`u z;0^KFFPg6iGVtX!Igw|lgPn$&OKHfrraE>bx?KE<5|<}O#yhzzFoE*|SG$bSKo8-4 z$!viYm<7`8PuUTxDUVZTsdD<2(NET`n^T2Pyg!-wsPNKbqevk{^Ri{jfaRupW@-l#m8NZi?3i_h8jxpJ@z zk87t-;FS&M6wd!QR^fGGm6*Af8aq!j#7THgjxJE%Nk+m7%Sl;fIjL(kUzL~srR8LP zZv{D=l@uPZ z;({nE%8%5M>yn}IjD4IQo`zCAu zkN2_2%d&fv%T@XRVHNn~!1n&)m2g&x+TLHh;*P(~Dsal5{`yDz<6r*EF8JG@b$!`Y zm)a#)Tx2)ga-DeP`kSw{=J(%aPql1e_dR^KU3J}M_V7~=*qi;j+L)=M?D6Lwv%4N> zuFrfv_u&+LuXjIizw{9J0#<=V;03R#z!hA>GO!8zjXl5o`=9e=1~j~$?#6j){C)DbpdY=3bHo)z-o$lb@Q0<-iT-dnoLq}k)OpJ({Qq55-S2ATOD zG?F%*+KW59cZI$W-W4+QC(eFbeI7IvScTn$_dfQpW|!aS)7|DRvXbCkHshSZ+|DcrZSrzO2tK{FGNS6lDcr4lKYuPP(#N~z2 z2MRp1oJ(t|*ymWL%H{df=fDdKBgm^kRtby}%v0beVJ4dX2|XHld7JQm6qnXLs+%I`>)$N8j)HM?4XJzc7c)m9LbekB6Gf+lG_u;YpTfmA^xpW6u6u ze8X#*33BB;weDWHzt%nO8HBzK&-c2CyeL_#gX0GwKB50Z{Ub&}zYwQntuR&kdq#PU zgI)~mp$cOQ&m;cmJtkzO+ zW|kQq!7tOHdl!49a|dhPzKwVVT?GxMRl65#@VFtia@`u6v1G2jHG*upZuU~A_F|Q% zUw%>j9dc=XCN28O>pgqO&jPn_AHNE7KyVAX3YrVr$-r@AH0z8vcj%-EHk!QsSu|jjT7=gres|ZtdUClq*jSmf-W7 z-OIJ*QFfy8uso~uD)5cq1#Jql&`wq-SY2hb=6Bfrf^XJ)XYt!I+r#ceaz&h1Hp%U8 z3+8dOOQ^mg@b2PyJ)2WUp9W4LllB-p5Nnb&bBqqt*udW8Ok*|$j|Z9r{#-nuctOeU z$Lj*qkjLwH0+Ag@p4{oiOl7bz+YB32mFy9h@iQuq_e8eU*3t_ix7V}E3CAN1$CaC> z?3pI_=cydMpvUC-grj(cy#U%hUFc?xj^$`>nT$EI!^m6FXLR*p%>nV}j!wm&V|l?( z@@iOh-g&gcdi^~&lGV;j#Z-X8+1R0Lj;a8}`Qu05Zi zm4u&rRte`6^pbEEkyj)vtL^-A&noO5U~Ze);D$Qpwqo`7ioHPEBb51(^blSTgTNgy zhkrkg{v0;(T;iYWuQ}pJ;W?Df1HD9?lIeUW{$4@miqLs@uZMn5QO0r=y&Tv32{rSA@qtG&liiGu?s*+0@YK@#@tl&CFf(8j zI0ZhLc@CrCh4sv$^I-PJGm4+_Vb71eD9j}Xv&ryBX7o&zXA!TbI6j%GYtH?=56|OV zS#N$GS^b~+mX@&8a#B`XZt5qNm-dc2DT4iOXm6xQ2s;oH0%8FC1qR3TAQG(^=9;I(h&^eq!Pbn)c!g5l+wdClv7JG29MMuuF!~190;k{EdcTE25 z!Cj-JtGLYl5sp&=KA{KX8O5^+N3W}J4x>bF>lZq5?yd)hvQ?C^zkR5<ag>Vd=&Tr zUVsT;4BwMM1A#ZtPU!V;%{i8kmIFa-Z_r3HNv$27*_(&rw>*EjNo% z$YOcmu}AIxM;}qaE9@>rOM#*II`~uA1@y#oE$pcmp0>wZK4H(i^t8S3S}S|z<)^Ig zsJArdgLX1$?j-3a%mPiEGf`|Z1P{x&K{|(3m{ERfWFH$cZlGp=$kgxHgZZ9zVv;s* zw6_;Jykae1WnXho>pGyPbsN~rUg^@odJgGlZw~HbtvkJ91I7-u*ZX$YZe(5qf3)rT zy5@VxP4RhfW2TMO4(6pFE*IN$8^GLgCwsYb2YaT?OR5&FTgktIhXw6~zR+uL;D7FH zFSLKnW!CoUX`SB0zsqYoTDvZ9h-Y{{nYQ$Z=o^igGTDYq953w#4QJ5!;WlpO+uFsv zaODckLF2o{qw>q%-O^CdMYx7v&`sc%%{wC02lCnb%T|40bC)bujz3+Os6U)ks1e`)J}_{!FA-e8eOBP}i^-ZpL9Y@e+A*uMPs3p*5ZRE2*f zyCBbwovg8Xvfu)3fh;RD3sqyWK0EPiG#*b9r-(1g_K8)rce(nAvRzLc!>gKVjdiK| z{Z~Dm@`K7%!CTAh$oZ_oXD@qDnB^f`1Wf@iN@ykrFG-M%i{?Sems-zGAND9GTLrl) zRWb6{;-fv)#4hGUW!tN7P`+3BpeJe%$s5D>%q$TzK=8`nPgg0&3$29=6+A8Q4YSs8 z3cGH|ZGnH#YwFpH#N5*<_q~zf>v=>yp&;j%Oy5whFKiOM3gx(4FVHwR1=l`hw5&38Q zxhOle#O)|%?!~{hBurO1tEG5NWu1Jinw!c0RBQsX$m0=sQo{Wy=Xg|@C&oJ)%p;#a zTa15%@=U?e%@=>69?;3^NUN`fYtd4Ec5}r!>*PbFcjF6<1jfJ*tDa84DgJTKC(PQE zWPNC5_*TOB#WRZc(4wPI_)ySIJg4Z~dr>$BeiV6GGnZLm*1N{h>nWiux4;9d3|e#% z^pVtsR+zfj3Q}CI8>|9{v*^7NW{Y3YU_7hvxGLB$7-E*#S<+N8qNl06U&YV( zcsB7YT7AwfxYej{Jth6NFD$0|sygc11%Gqa{t*R`~vGVdvD=T52WVV%;W?5Njx{ejx zE6KFd(p1aMJ8Eee`z<45uVrQKv0OBi%w5j6k{)4s89U^I$x7L3Y4M+1+@Vz#bMRe@ zj#_9j2NqfUp@qsui90aUWw)?LXwPUPKgDy3juC@@lT|#gz${ec*1i_G?JbMi-q)hG z_0v6c74(&TTL-$#{T(Bvsr=#3|7rj7@BexZui&q}_Qqy*(PbA}vm36qyY9Qqo_*m- zYj(?3cK1VfTE}i3?co-WXooSgzHkE!0yn@IFa&)c^b-w6#)Zs;Y>ctlJTGv;Vi-L!C@c6;%@sKsk-X9o+xn=kSb|Eum z%=9tRRM1+;gIoFOd)Bc>J8SvMGwSOw)AL5(9^w-GBrkSsXB~TWRStfKo?Yyv&aa78 z`ivN0o%(dOw%t03JzBPXQT)+s$Ux~Scxstt9ykX7$s~Jc#RBOK%nz|k`L%9uNLN84 zA@>c>Y^!$cQEo2|(e9s49mOkf3Dv%97xjhwK0~rqC|(c0z$@r2fcIqCA)9*vKc)>3t>*na)BMxIf2_0`sEWmhB1pI{V#c ze^rz+iIB(th|&m#{n)Ij@KR%JK!y0w;_HMu}Y(xvpLn z(9=O*A%CAEo}1DFvhKI(c{~>`J5d`ak1e?|_+a3bljN^7FuN1zJ$O#)D??eO;*ntP z=fsH;)$!xZ`;@uYG*oJr(urd^+SOZAm1M^%6Rf86sQMcG+3@wrbcavy`;c8*!H(gg zP4d>d-NI*g8L}tH=_c!~B;XTiCp^D+tJn=~3BLz?iARW^kL;Vu(qEK^3)htT9NREH zk$w_rD{zWu75pjOXZF{#3OkMA74>_<{j`3bnAw}`vw3YWgt#S)Rm3UG1~U(%O#E-G zrtBB1DcxelIp4V)S^7iS!922OjV$Mzg;zYAcpWEn9rj#U1x^WP70)Of^D~_{1wQd? zf|epbYtZxYyyCsEo>js*1@8!~0-tcM{!a2ju?U<}km}Bh$jA@pl#~TlkoJ!HHgHH@ zn6HFu%5%wC7NN&8M~sq_Fw=5_%87T(0;7a839OQp=orP{=jh6Di1d{}SHYK(5qEB- z#mu(!*m>vG9CtrvrrYBaGgJN(`a0a>8h)YYqsM|-}^(*x6jH-qs1y^Whqus zkr85)%F0|TFVD8p(o8EU$*_vD9IGsM_c(Gbeko74l9D9L%a69a+{2ci7iEQc`{hr` z$&9evjO~__zRhwowpvEwMoW(Q$P%MJwA8q@mX@%_(&AQGO7wF1fR*n;9$n;kmrJ{I zh#lF*%#ZWW!YW=*@mzBL5jKHUIQrr_o>lg4A7uL@hAMmiPk;Hd+b?**pLEXg4}Wsr zl`ETFq5cnjpnD#?(>lD-R-D2PV)EVYedq!8eb6r82zbT!W?&KgC;##1KWPRCAIf7- zKc(Ch7y}N0E6_U7O6d7`j|uNXZwKuJ7U4Ot2IsH}Yyyjr!NT$37Eh?S&pu_J(LyHf zBTqe{92Mr0(0;hbM8*Tsg8A1pTMJF>6llt;WhLVJ(N#b*}~ z8T(|#a(~>+aXP{@%rsA5G~Id+>usHS;{$oqW-OX5&j}jIYu!7GC!T(}wa&@G?>?}P z80MK*Ua)?nhKNz%mrlLA+0$)W>A8JJFe}|huR&KKw|2q%3vJN{i*3axAGn;jfqks! z-~raPUmy8Y@TkBacx&Mf&S4KYq~*&mS?3;a+KcU8v!`3XU~M{f5U;>3Xe=DjQ{WW% z2H(reojU2f|ES?Mc>G9dDih~S)BMrc=~HadoSDjtTlv|?(nFY$W=GJ+Uwx^$AH1_a zMr^Y!yLagtU4GNOnI3cgbZmTX~KhN%Ysbz>=Tk^Vl?R(^#zD z4El$9FZ6nBhc@toYZkCY`FuBn7rCCHogOo9F(jYZjQE(?zQ5|?G- zGEW@SxPFb-OE`v~pJkP`RuZs{_oMi)1>^Xhk=IrHQFF=6CkF+qP8TlCd~0yVtE;h#VN3gDs7SY1P(c;&{%?d=qXfQ3Oq7LoC2#zCqXOuUrLM;_Bhv}N}OTY z3DYe*eunP3-VgmASE1*{2fh{ZSID(Zm~WYh3oSEgk!2(FveSOltPl5LmXz4FmJ;`or6;VjoYZf`DfnvhbGBGsPK4#> z?YE-B81V+&fsR6WH zO8wcg(|)n+^q(y!?I+7jT5oCbA6shNN0yoNndPQ^VVQ|*EhXk%OFBH?Vk4)h|AU^Q zxn$Uc{?D#~AznGSqrVFN@Z1uvt9Vv%y`KRgugZROm54#Md)r{!vu(IV?V4c!`XB#h z|M6e{DL#Q)=mTAF;a@_tLCh=TvAyNa8>FkWdiA+bX8yxZJSH}v$AXRmQ@|7SabOiR z5xgiAS_)hNr=WXqMECI82s#Sv;%A4L{efp-6aT)v2h752e6NRlJePZ7o4alolaMzD zpFI0wD`nA-4 zxMbdY^OPG)?g}%+@Cv!KeTKbdW2TL_VG~Aae{!q#ZS3(Eo{`4!T-!GGYS)fp5Bfi7 zEG=GaX}yOHaQ&fveWa~C(fV0=W#1gw&xTGMEp}nH8CHQ)&{d|sGeiEB9)tT@pJ9W= zHDu^_>Dxzo2$~4K6tosH-r$gq-MfiT=m&W=@#j3|brl!~?S?h{4oE!s!S@e)xvbF71B_c=PAsMu%d$9R#FhDDlLlCzUz{_?N*e##q#mdW^NLr6lQJKH9Vur*klEH zdoQ7^6(x!CyAD=BNtT zWC!y-uCH^xroy%B{|vDGTR9)7o{!IkBO`9lwo$e}V!R#LHN*bzzy3e%HilQ&cMPZe z^`Z;y;>$0Rm*qeH_-}UERhNiW9)0pbd*Zpr?1`37*rQK9X1Cpar+lhpz0v;>Q`~W< zHM{X9yZoAFcFUc2*^PJJp+Yx7d-41MgTNWS&*Qzd+=Fpo7xWd_2M*!2un0`VeY6<3 zhUbypg0J`1=65Pv?&7O1w@a_NQavAZ75YBtDHJ)nx7~AxHGlXXduvEv8~yeu8#-=? z^7GlDJaEh)n>_bzX(ODEpE*gHC*;~sm^D#Zv6JRZwpmMN+t}&jq_vQV(yn`Z?H;5r zGkEN9=_Bpm>?(ca{wE)|hn{&-{6Qb+jkkJXk z+H`3rKkdl3N7?LUvu*CX^K9l~^4lg@*Z#fqKJ=L26y}tB3>qL#@%~d zwedUKeR#h`rz9#9gntknuq9@v zic4UagK>xKhb=$ao&$TOp~NJ`DhDp7*zrnwUZ!Q_XINormgN=1YM-z^lj%A5SsnHV z!Xv>RLGO$8M`n7Cl}Ff#3bOAbLVE?}ODW$cPC0ojQGQfrknxAWLll|p%;Hddj?zOa zFWIF!QG3uDj>jp#y}XdU%5LvZWyvn>mO*oE=+xh(bUa&|dz zWWX^;L?1}iP$T~enJrXk_NTGjaSfTZ#|uonquK8mJ6=hjEkb*C*e^^kCd4I0>jN(d z_kRd{DnDC!;TFd>MG-opF;U_#yf*A1pubj=;Cf888S)~-Da=^8-2q9`sQ6ixU&8z6 z1-&HtNXp4)7tfa}=7YldM4S@jk@zFotLh2me&)C(Y&IEgIGnMa+@A zR@rTyH<*8hh2RQ*&vS_Xy7IW3$020O(lhc$?Pu0bpwHA}$|r+OGe@85y+bq0es-B- z_J?8@^@XId1eqp*w&GdEdsW0K7CbVm@BzJ@MO_vmG zwbJ|ut0-dU`9AfCIr9H3e=kdNH;P^I(mvJe$z;n){!rdnSf()J6U$0kX(_Qw+#chE z_m#<3l(XLQvVXCHyd73r%SGbtfDMVy`bWvSaFNquOiK=E3&M%GEbaRT~T0F)!9~F zo~q}Oxl&qq*eXg6SwZe@%guE8DX9tTEGh0I={gw+pIJuYrg?$J-9-w@=yQ#dmZuA(*J>5@Y<5MLhj06F8Z@FR+#^} zwAsaCmHQsK-)_0{cICdo30@#`4!gbiJ$A=E_u9>O-(`0P8TW7qJtEH=UJJov z>$BqM6)_8h76Y50nZP5wj^}&5g?kiSqkA{sAWq>3J5l&($a{J0nJ4YJms{CO?b_ID zueY~$Z*;K!!w1-yx5vt_@^Z(QtV55EHfqXf@drJhHwX4m|7XPH(KdSOIO{#MkFsKU z4&1`NAo69I_>jR*PGH&UTEJ|y&rb;Jot1AyZPR`<*{}B8u~3=rH8;Q z=oaus=RV!-%|UOeCp2m9bbEWjH0wKhfW9Yo5#je;_`yP(wPdah7&XKOjva18Cydp5 zGS@>72!AZQ3dw|H?uQv5Smnu9&#NAJ`YC(F>^@YdqJ#d8bKz$#?+!z%1F zM7tR=d4d=Vy=U~ai8gKF9R2LfB~6|?Redk!b~f$YA`W36b427`?LA~(nT)q@H~lC- z%8sag%1znx{}Fc9VRn_*+x|(tlmcz3(H1RG+=C?q4Nig+_Yik?7c_!}1PckC5QzIE zGZ}Yxcb|y|)_2{{yWf*Z+VA`RSl8ZXpEGi10{fn4J?mL9;kIr6E_;9Nhqh?htM=`V zO|~a$zpj6_{tMf<{X1Lm`a)a3X@kqv&&`mw0;@2uoKx)dm7=mNtEx`7s`7)E;ew*^_V&IjV3M~Cnw?`%!D`YMOmV?(xmL%K8)a!y zyrbv?2*>Utsd6`PbPQd_aX>MAydikuOOYjBKM1x~36 zleR#gheBt7SKu(b4`?!V<(sUbVykjG#4Z&(r6+O!rZ-ey8>b$Pb{ID1IPdSFYBxW0 z#=h#dc`GN5G@qJVKE}T4dd)K7#nCKwGy4rQwVRN+C}q2sa4-1slvg(JqtFB5dsQ#1 zf_W7eoGZIn~_#t8fN7syLA7l zEZ$-@FpxNCi`a=Qmx@xiCr7?r@>Iy$S1$dUfL~ykPlK%5PfoFl&#-U9E1p&S(d#E( zQ&GvjrKfpSU=?%~ z7)9C&NA-6WSy9F!D^6dia%{5D3ew4dov*%+ve^PwDdJiiR`I-&pE64Y%ZO8WO`HA? z=Z;4fTXym*R*;5YCh*QM3(ZbLdOX2Qa#jjiw)lOSdHOim<+Rbt3${qhz|+OOn_c7$ z)hR(GSzT3}Rh30)KQ*r{%Kltl*PQeZRAj`K=B~5ST(?)6d1DHXY;oQf+I3yVF7Dz8 zt0<2ai`3O-SY2I?_@TBo->R!~HMhg;PEA#Y^p5h9c=1G8@qT5g6l8yGnW<|mGx;M+ zPyX04)4#T&!tLs9qshP=9Py{I)3mNK#hPmKtg*Jp8mfz|zAi<1DVeF?N~0+)++z8e zUt3DT2bM;^DQUH3B(GFve|*FeOO1Wga+BY()TkvEyLYCt-q1P7pLLnEPDep2iQYBP zB13hGS3

9Ye(`@QU}M=(vsSw^KU_*#Welto>bM?BL!B7QSnO9o#*|qW8|Uh<&eE zY{UvnNmy$?{rS(e>+iq+_kXpg_^h+f))7`gW4Z8>i|h}7I$!=<^py**_;an*(oLaM6Xz!9VYh=i8tDc7dHwo-KQl|Nb}aCiGeP z|ikpdjtpKf$iH>`U%G| z)5p76pBJCDyC3Rkci!Jo87k}$eC*jS*8afwk<_F)( zu<>JTz=)wXe#TVgp$r;1!bVPL$k9ph%Fri=7JmvF z3!Jij|6ciQ=^Jg@yUSLt{Y1VO_BtEZIPE|jsygFZhTU3?ucM4i zYj~(4$YW{HoHDcCKC{;6)}B1pYA25!vLB8gwqr+I-CoFJE)UMlIF;IwmO{-}!4}Rh z`?d3m6o2V_vL(#sd@ELy-V^MW)*MqU+{G@QWbqfgQeC#&=}-8enFFs+H1-HMk58I1 zTkuZP$6^-#Zb~=;g7q4s_W~Ccy@gFf0wL^U-v={Ui?)~h{Vcv)% z48sl}7={ctm$A<=&{u*y+K>(scx+w&W>x4q{J~B^J|EdCzVtN;T2j6;o~x>4u?sp!Rq=K!%=$`xShN&a zggIHjT_!pe)tH`$`O<&5MH#1mPmtMvK#J)#@tWqxUS8EY*k{S!-!dRsF) z<;8gK`R^#(8fvoS!Apw$z@iSkDjs3RC^me)MeUz$iP4Lk7L@Rs_8y|0MDL%hJK&b`Mg)E2q`Ymn~+O(^}+9q5spS2c(>Rb`$PqFY|bp zf4g_QG!(vm?15Ppxo@^b?4DzB;jddl?5CEV^0nn;?XdIz^e6l6x#!t0fBkE5%2{Xs z)_(SjUxYli?Ed@Pg@3o-oloY<@9obQ{@E_N;v(zVsiXES(`#X`4!%{qB4okcdiNdj z(cbgm1L76#gY>Y$z;2rMEd@Omi?K-yC-saoy?Vuc$hn{*| zYyzL$eAk_J;~lr#)i>W{w|BfpS_V6f*+X<|hr8_QZZBH5{(Y=V*B5jo_pM8}=k#^> zS|g{9ly8O%7xHr7SoON*lF)nL68bT43Ny=?iDufZ@aJTVPH6%KZHA?evg7{;HdA5U;>k@D<+& zy3(ufyk>8{zf_)@od@?=LVA*R`H+c^XBLg+vv0q#9S8R59CpFCvToygo3?PC_2lyn zAEL~;w?0}e&n+4IVNnMxCOJ-(kd|mknMsz0k0mctIV*XkMbcRcN;2g)bN>%=IzwaC zR;!b4a*9J5s<${!fmPs?psMM^w0X=qT;q2l%U4?;t3D5XpAaw9#jEcCH=wD|N2)8| zB0YqjPD91FD$gW!6cblwqE70dw!`Y-ROEj0jt1J(s%I9R7Z(XG@HciOEsA( z%mHmua=gSf!)&iB%CxM$HxRqDfWXjQgq>g-qGvujQA8qwt>a{i37L(_yws^VZti z2!ABI+~7*Q!C_Wiy2mQZ(SqWQ9gbo|zqh$5Px}gZjQ0jV5Id-SwKk+{$D?*VGC!_8 zODX!VCI>A(SPC<->dP=0>g0b`$l3QdSE5| zZZ^6q{?7Hsg5DQ>2FiJ)x4UD|3*#uyHNIl>r|SJ~9@m!KFW61!ytnEJ1^I9PiB(_} zSjBTnF}=)^$oQd)=-lvPVuvXFfaNnGlNyd zn=ChNjin{Nt35jCDdqW}i(|={O^;uqIb?Pw6=biotn|;Vq;Q+&^6DxRG+SIhqhp1o^m?UP;>E$`aKkpxh7-i>x&^ek4dxGM_ z#%i}va^y@)Jm@lT$?cC1pRJvW$+2&1{s?a|dH;pv(-pN+Szr}( z74Kj9^96rVZ-{;n`+@#`@dbA6EzIZKBu3Hv&sA4QSNYcs*NPW9JaE6}dc0Sb{5P`T ze)Ze4?I*wdh5gUZf2Ly3Ao>Z~3g<8k+RI-rWdF0v+UH~G3-NV)mK$G(c^;RSa*y5f z(Ea+lcinf-Y5tV!+ufkTpVH~6hvhGM;>9P$C+y#OzRycKM_(a777q$LjM)u1VAMbx zJ#CEoK6gIYURftI7S9%^ps|pLf8$-Z+NEeYS6^w@-g=AO)9E4UC%3l0+wQoxqaNSZ zp}qDd@_d(JSHvlOhYYqyo_bRJ z2=h@e3VkW25OX+Q@r-in_`TIveQ$M@n}TaRb{wM~4Lu>URH~Ul+3RMOnOm+opubtT z#@C`;&u43hH5w{5T0`Y}tFQRxw1QVuW$W8;%(o%kMVu1oE9fe)ih4aYk?Q?$lvkx< zr@Ob7e4}jo>auUFrhL6TBGrLcRrklzEz&z$>Jr5&$65+Qdw|G}BLfa5@qBUwPC3@1 zVjnfxzi29C|Dvam#lpNZ{7_dDt9w5_9dg|?S6qY!5ags3ZFIij@*SEFhp*^G!6S85 zv4Q_5RXP=UFnDR0zlJ$$shR}!tK_9^%9o!O&eDG9regVexvu9Mb6oyjeTEo?BP`R} zkZnAt9$r(5=7``vd_TJX^Z$0{!6;Z0UMTtADht20^5QS0wMb(@XDM`h`M6J_vEV0h z_0cJh2y@BoWk#PV+8`}Sej+%neD|q2G;;YW;3dk<(Zg_|Y+-gvRYkbo9W(P)(N_Aa!O)TggkECSE~b$xWJNImxr#Zo$mgArS(P{oTnT!?Yjoy$hl$Xg& zIaz}E(GLdR?=-PVbA7Hg)@ECCZB{V57G>yD^hk>HHb|F3D9lBfZ;v}L*<$xk(lH@?q9uflw}k!UlzET0Bt7mmF->&X ze2ZZ}^1cb$IT#l<+M>fosABeyw73IfEh=nuz$YW5pTzDPW-)sQTkIaEk9hX*y`HwL z61#VhMeiPTix$g8Y=7=rY4BV&@2HDslVATlkJK7QSPgMeUts zG2w4mYWydbletknu>8CzD=Lh&;^M>*o6rmT$L`k>oprSX`=NUI6$1j%s4$Cm`@tlU;h%y4)(qvG#C1HXkqZ?lg~b54?OyaG%fU% zzg~Kg*zA#~pOUUZ9}HjZ;uTA5)+@8^gD*a|#GDjM%1p7Zzh7_PY}#laefFts*z&!Y zg&Af1wd_{LUrX(ciIl$uZwr|$>z8TmPunv*F`NzKX- zw~%97P?B#&W!Y9-nrLOEkycf4K>rVRo{&wN_eEoYNj#VMa~)v!p6 z9;NVrvtq<44RAQ}Xt2D3_Knw7@a>vL-07xqFn zH)QMc!718DU7sdyVV5;K6Iz?e`i~YX(O)9ZhIw!AtCSW|5vHFP_qTHTEbcInmO}Rg)c|`=c+`VYX+P- za58J^Qi8w#ogRQLfZk8h6Z7S~Jb60m=R$^XW4?R7AzSxO^eD6}_Cdgo=vWnn&P!TW z@Qw3bIPJ9&m>C(SaL1TRW%+ACrwmybUZ;?So?C9@mXFpTzaLf|Ev%ekKK0vd{2euDY z?b|};+Zc;GFwf=6WPE9bx!bHLe~;zm#aKZ>f)y4fi%;;Xz$$1f;*?+iTG?pZu?m@CnSqbH6_4x9Sto|G6TVN9ON-JTUOepD+BI_~h@GU2L9P;1$?~=in2x zm-Y|bYxh6eNx8Il-G8@wKri-w$$AXzZUaX2x4uLA*$chc^Vd_}6gcIv=bzM^&zR|B zZPe7!;u?BDT!&@QEXZU-i-A|zfqXsLFL-k3m)v!yUHq>rL%ed+op-1Y1f$&j!2R;H z!Z3JNUmiU|J{5W?-3Rnl-{>@p-TrLSg?V3s={Zg0=`e@UFe4A6U@uC%4AmWi9t zVd(pyr!ZSYP77KJob~p{@7RpRGv!s`>$SiCKJkb5EA#sxr;j7QE3_7VPp~CCgC54m zJlgpw*9W@fQvDA2IisP`!@`$?zO-=J5?i!N2Vs?nRXLbjn&rk>BV{A8m-R)a!<;Ne` zYb)NcW$&%96)WGeT?ckqR$h`7my+w4V3p;O@}_tWQBNo6^C*ie&{{NO6U+g7U#+|? z_*Da41xBH`rc##IW~;^jO1}ngY$eAHAq@p4sVV!yYRkVoRb^l4dWcs7ZACuX(C7Is z?V_f{^^|JLzYcii8}UnZ$+tl{T?1C3ufpy^vS<7p zjqBr7i$P8vZw~1w;**ny?KpcMPaG1fxEUb!5oYWD&i8=spnZRZ>#T|@{#?8SBl0tn zCZriGGT_*`6scYX_Y3uY>bPHKh5RS2jc&)|p{4>m+El1L)XJ!7EtfauaBG$7(4neR zGu7-?YAMov*l7&(>l5Ww@x7APreyVhxIffXA5dQe)>3~2|3cXT={V%VP~5xOO8{fg zJApYWiZ{vgP*LdkMGOO@1w7-uCCq=IC(#R(N0|NFfj<}~ktV`CS#3&4|A5haxn4xj zn}8=8GF;VXSwnr6X0Bk7riMIIFN}TNK}mDce(qw;XK6oXbBVPyms)F6iK?Zs$hgMO zjJ;FJ*=fksecSmnqx9Y@OZPedOVMU|BnorZXvViN<88M?Fl~vO@d^4pZT+%^DRZqT z^>kKozLeS0NJ1=Uhw(=01#x@9I#Gj{R7CCn%5JYkw; zB}_3L_Bua$*#;}jd|$K1IZjNH6fshj z7(PPfIAw$-92g#48)XRx9GAc*j!%YL;{IWl5H{2j_7Ac6u)!8DUKu701$Kc^&{5j< zc;fc8;TF#?!O=4coZ?yK%yZ8u;XC?<%3p(9!ngMmv%oETw~$*q(vl+QDvKsBeU0U3 zePsnX8!ab0Tsr{@3zEbt=qdm6lb^WW&pGGFw?gj9Pk#PWd1~?2QatyIvwp5U%$=Tk z(E1PUFE7Xw&pu_Ho_tK@do;esz&sDq`j0;Sn34U~uH$Xi zwcktDy+3&(&uiX?OcdDT@fV&Fn=seXZD4o#YT0S*Sp{8%>*z7~aoN{HU+4OE%yK^{ z{p7+cF1JgszDm6x^41>i{FFWL_+w%em<10DnQZt|VHEZmvMUIGE&z6}SW}Y8??rG^NOIMLmzuI1Z?+v{VW_eT|ja2m>cdT*q&6*`*o;i4~sdkSwRqt@#T<*mco2;SwJ248JLiv7A zUB&5~!ecQ@8&(mol&zPp;`J2RMBEe11@ZqraYuPDH^Y$`X7rM>4UUIOzdTJ>;dAlv ze1DCV+cb|&KCgN&H7>)yt}e~p=UQDqg?m49KgW(YIW{?YSi2v^D#s5yj|y|lM=RBD zC@c70+CgQ>TDOmwJv#-TYX1)JNwe5xo8_007o%*q=0M5j<$f)n3Ox+GCiG~U@|BT7 zUJ5>%W36R&yp`RK>`AH!JiIlUsb;R4TwU!TVz1JXDr;#ek*<#y!R=*?RBi>C6{Y!T z8)5o;wybowRg~_sn#z5yk5(I@OdS3Wc6*$dNl(%_kCm%a7p=d^{mhmBcft%j{K#YN zUsUGZCO7Lv<_%fH>hm1Xyfu90_ijt~fe#up_5SHqp?}cJg4bXWw2S)qqHXWVz#Y-@qGTUe~$eQjs@{X7dXwWG0XMKl&u$MP5S$P zxBlI+mx)pLmXdQ{408Ej?zOkgIbxzEADnPnC4_X8;g%FW%#sd{u*8Vr zmK;99k`4^FcsS(1@Y4$Y1TIm94HcUtg$+@8R)JUI_YTuzxI{f4w3Iyq)!Wgvz3#DN z6nZ`V|D&$*pLpdzGRn?=q3Z{>_fhTN-q-eT9bjQwowjmd$5e~hz0ksTF0ttFcPu@1 zrxoN!TVbK=0l_M5wUuA|>Q~ZN=m(*80PKe$RDR+vV3^qij0-y=Xh+s-UT8=g(sgSmzhJ*kjK= zs{O}ZUV74c4CrcI`?J@$r#;*AX?x_EPWJMcfi`*WM0`yTFO zU0!%zb2iNU^cd8~p6~UXb$;n7?IW5rf1=w*v}lU<5Wy*PU!P|me)*AnD0o#UcxT#z zS=uMaTob*jcRqPnyZ+|Cxybqs8>l%Ryfe&>!${06!!Gwf_NeBT=~1Eo@O|+!=6k>R zsw>1J%zWd;q0a>uqFep_@=NU>S6pUyJ#epjR?H~#HR+v^sf(`j&DM>!{lIQzt1t%? zo04de@o^Rr8)M;daVnVPU_!jK6SDPp9@uYjX{oCC^fXJ&&a$+eY)i||vZVAhi%Uti zn50BY$<9zOY3|v96md@Ymi<9eF*L)PIGr&MLZS!t=UqWq#{d}nOZ;R!%Q)=K4>>Egkywn#3prR zpNFcU>~pIv{Y*Mbb@`W8UGhapTY+ypv!IXE;%^ChNc4BmPT&tQ3-`aWUHW?+%^n88 zi-&KJJP33G=LLyV&VAed0QbU{hCKBG*jL!pR22GqoWIMD9j>q+k2lzlCtB?I53P3e zV4Liex~AmYL{TC+uy^_ z4jo>eWIPx3$;zDN-plT5WzV%?5x2Lw%37N%be}zP$ny$E^69FyCxCs$Xc+Xx_#NPB zcKZ+4Y6gd0LVOR*Pw+F7Pq{WxJ(ZBBpe|cKA2b~_3#t)MLw&C6nUI}_&LIsX!a|IWDcPal2b@$@5v6})+WF9ccgFo58XNOfcyHLJg#OXeRBne_ zD)pYZb||>U&!AOIQm!mMpVi0lP-}H)FM>F(wbqUvs1`JZ)&UC8)T zMsczJeKa=ZsnBzn6-Sq=U=}y$Q!C0KM})ayx9b>REllEjK;jenJV8k_(OeI`AE%{w z{UkqSniZr@x4h)3Djw%1P5HO-&-){<@qChnQYj23XWySMW*f-hnzs?do@0@!Y9v zt``)!v#%Z8(MJ`ry|*3Q(N{g9Q`dREubvCv*30&9>+Nc5AEU%D+xkjtVU{^7`4i^_ zFW)aFX{)QiDV|mE*m6!!i2e}0pz|;Iqg{6GrMkcO8QM#%!VW-m73Of@3g(H?Q(zBz z4E){BV}InFzu}oVq5r`7uYQY<tOB&tplIy^o3KEA$Q^B7u#Pi{)fI#JgEJK4VF&A z{hYoKtb$JjKZy5)_#-UvY|j_v>m4$7u(*l6&FmK%H+!u15|M|CPlY)f`ah$lj@4df zyfXBWrp%jR%U0pHeO(-bXM}T@VZg}2_V}~UC`TV{hfI|xo_kiADxF_^L78w5J^6$f z22Tpb3=>8F4F2rU=>dE6na+BQPK5@9rb7RTd>3{ozWUA^%Ep6_-dMTJUVnF)GTzWk z&{WV?q7xG=Dk0tuCL~y7VxqK?n3QBo%F5IcPQjm&nww)OIoXzym1*g@IhLB0X$h$* z79JCATXyZVZ?|r;12K^yPT}(>Wu}Q$&{e`?A~esOo}FrWMY-C0QB#wm*&XJ1Gy}pM z2syL$Nru-F9|Jk^r!Hdz#|m*Chzx9{{%lnbD^C)QmOri(( zKPUXNE@O=y$e)Hd#qamyNVcoz7alXOE1xMImXhz~wc+RIy`pFhXbj}J!79qgJ&aaT zr{b~h#Vz!CT%i%P)d%qTG8Oy|4*$FKWzSI${{Jv|5_eWv<`Cmnct?r zBL7CfDTkX2O}?0xQuRq-IDR%9n;RHt02;Rp1=|^*J^-m55X5ABm;lm4+Pkrg(3fOE37^iZb7ow&FAMVHM9Q z96hTPq|FNP3c3os;=C$>c7lG=hEX_&LHyC5``7T;U-Rc2J*&Vdo>O2KaZBQ4qp}kx zx{90dZ=3>`q{oi6^tdr%k?i>K;uTnh=fpDb4CevMv}F$%Bsp@Vr9_Ull&Ddb8a2k! zV#Zi%^k_?q8l~qs{-5rQ++Qv*9X?=(FvK#KaR_Acb+}CC+$7JD(19+9A1@gcA z`NF@7&EP3!wFi$HZqIgmQOv~s{;|$aY5v-?mpnKB_`7rpnCiBUciN-RJt2Qe?_s@d z(71u(74|2RrNY-@{}8$Q=oe(J(1UvX`OcadqW|*t$M0$PGM-?3Blt(yM>Jy680Dgn z_XeYo^Tv*!$DV)IdJP(A14j&39|~VDS#ad=GegZx66O0;JqPtyuL>_PxpkT?o;%CN zO`l@(-&m}k4_}YjBfgh6SFW&?pMGFrF_G%ypr0Iwjj?@Ekvh`Tp|}pCaNHGsz{2BV zL$61NIS?HcI_^6dVM!V3ViRVF@U4W$MoF{57lY?EyC`3MA+q|j3v(@`FZpzkP9A$&Bzb`0y_$@6t*4&g4nv;cNv_q$f`No7`HiMk9-PTaK z!yPYtiS2i^C|t@YAV)RUHLkzEnBOqDPL#sN^Qj#)==@4)l~*wmQpmBFI4o3 zU>WaqfmiU&s_#>|(+)Mo+u`OkF%>&;@Q<`KWvP$AeftPZa;(aZA0vy_?Q>>lV%u_= zwA|OK<*}u|z&#re>ajz)cJg?!9Y0cR$6FJm*^tqJUld=8W<`rWvg$(i`nlqqYdmH) zgCo~q7q|u=3)+zD$GJROd>n8V%y6`ouUF-EA^OZ*^_*&zIY*y|`)DgV1D@KTxX-gA z686AXL4FDIIaF1F%fVoFL*9~-&FcS1OKYG9)~3G#i!{6b(~;J4I|?^QzbKWq;q?u! zaelP9EV%BN2F|00!ftFl!J0W?r*&<-)iaY#CQlnq;d2~r_Pkgn?%@5(6QoQ&#~YsA zJiqzeU|uWDJD0i(3d^E=$6%p89_dZ;nFRpkeu+Em~V+W1-d8=bv_7 zU&pKr9-3rnQe+#JWUaFNRI>Kn9zs9!TmM53@e<(aKlrw3XZ>r>UT&wB?bu ztm2vE->xN3HYz`Pa=<7~SCPIF=q%Dz&{oh?f)cBEtp)8QcB~3cajXKb_;Szr*R(yJ z$to}kl@>KJ;1kCt@CS_IH4~1GNjRdV3|F;1PmUNCD$gkYS7xCXBtJ@^tLz_W)G6J> z>nEol(NJI&jub248o=g1EKhF_1@#Qs@@SS~B{xKZm^prjpv8}I#Z|kjsO~SUG z(pNmQgl+3%`?mJB-J6EkuJ4A~j*TP4Cg>-=&`;~m%A3%#DTC!TxMZojvk-S_DI*6GPd?DA``RK7hK_izUK3AzeL`aJLlJi<9W zAfBVoQ7w5n-yvAqh!zq8g@bA)Z&i~tA^tGgG1iORa1z5$;0x{PN z6I^`drFPL3m)NzpT_bH}{LC>ndF}+8|Hd4fzHo{?*XwDq%bZtd+O!3em1VN%t%det z{}(kgJY~TYx36%&1T)Gr>!EjE9UKVK4RWq5jiDPdsXO-qXP@ zz48kAQh6W#{h$rqe$SnD!(BIP=0|;{S>tTzq#-uzwVAf`gEv*|9qKi-m-Z{;U1iRP zTo_p8i5I%4?*p$ehqGYmLS?e?8uk&9v%kZ|(3>rOD`;(^5pJ@|k zPqmM}SSJq({T)0gun4>onV29g1uqJo6nZ|1nHl01m2X3@sx%aI z8t-3mo|P2!hL~%{bNlhKuMikBB4u5Og zx~zO=ewaHx(h%@UjaUVr1pThE;+^VW9BPQx9If)n$de$;Rec_GlaH*j;C-tqT%}`W z;VP>xTCK-b9Q`@Z7p@Yw)Re$$czBrMb-RC*t6RBG8rRX5e0?9%6JV%7TVXz0yQw{^ zw3h03#QT+2bf{R_E{7ZA4EzA{nxdRvtJMs_cL9qmbAhMCzRJh{z<;uP^i zpf?>wyP*#guo3+iu|-RzuA}X6E+2^Zg)pbY=OXi^CRt4DdOqya#24gzC2#@n^Dupz z!`1G+vdiE|v-=%shP+X^H{zEz{K0dSet(DACtfF3K`VhpxUT09H@VMuq{WUMZWgcb z8nhIBT)--5GkBHg`!s1MWuf|m@QU=6#$r8gsLOHvM0BXSc=?74Ggm3cM6A+gKKV>m zfmcFo63q9U*#m+>JgfL4*XZwfPT~0fW0m}5`aX_JU={eJZC}W<3VZ^ugtU`0SS2H7 zv{;3sJ0BbB_3&EX`|#d`wB?VsOw#swf8A** zj#JUuK3nuC7qV2r(;c{E?;ai&L!3#ou$?!>|e6hW< zVu{#=e7620UUqqHLk3&-{{6%ueFnd5FOMCqJwH7L_Or3mCWmrw{VdU#>ExwMvq57< zX%-qCrE|9zwabV-(cK0Qut8%++N8O&Y|d*-Y~JgOY}S%_^39B$KGB9v94XIl=dRD% zt8Xo{_t&klPrmumzS+D<-W2qc{V~zDH}ar;z3F?~vS*Kdw_}I)8p0^_h2R$Wgo01- zs-V5_Je(4rmMmt0Q+9;yvClVrZQJ+k(##R_(TkS8Zg0M`Of%D^WjVqB?`w50;_qjC z2o20oG#9&!cVSGoQ) zuL_*vS*5P@Q>#N~397zqt<{%(s;Vnp6RO&hPeKK|-~p*%Uofmvz0YOWHfD;m_`bY$ zg7+0Rfk((|Bi9YDEa$q%k@rT9TX4;BOtl?9Tq2#Osd~TWHqlSm_eU05HT#FrM$k#n zO^Q}qbus&eS6Wr!`&L~9r@$lb+#mfl9@i9qU^T@bTW#@Lx68O_tyn^NB}Lz8fAW#0 zRG0e_^nrL!Fp7U~y53smdNNIJKQ#Ie`VzA~n$s!#My%pK?-!bfsw>0$deCLqHYQt3 zbD(c90~2tM=aII(CEssi$F$ow?YM-Ujq1y^nL~m%Q~~R%Z`6?O^sbg7^?9TNh4w-^ zJ~@ra4ma6}qo-!W$+FR$_;I(}z~|kBX2*{>{vc<#wY6R>;;(rf`W}!p75ubhr35`7 z&HDu270)hsV%;2&(^Nv9m6Rz)c`r(S(iAHQdP8ECpr@0ACk0IfrlH@HJkhd~9IJRO z#q$bB*yPNk@8>uMag1_GQ*qjgXA^()ypjo*M2-lR*aSX_8fIycLoFq0h^0ghwX}o7 z^%}GiG?dhcVXEYVLsThn%7G!`4|!K2$69LCIOCXn(9I^FIR}KN)_ZGV7pJQX*3ol` zcx7Mz5U(V_F`>TCK#SSi&!YGAwb-B{clEZY-HttAmxH_esTbsV#WTv8dP-Ye#rafD zc~(yGisu%mslY2CzsecsXf3KOy=~uSdP04ayQX}TbI()hYkmDgXT-pBJjU$FZgdB8e6c%O6(cJ44I{7lyubPat2-the$?$Lhk_^k8JRiQDU zvv3b5I~Co5`#k*;v=;8^d<-mr_mh1<^nm_()s@mxU=;kG%mJaD;Qxe4?&x@z-F07k zX&Z19y9!;8>PEZs{yXF|88K<7_+!T6$u@1_L>oGOfK8Y+O03drNLL#%b-1|Uw)<|g zN%JPzYws+P{(_GMugbNzUT1%~=mP0G^ql-I)S4>|hm`hiu@bC#`o(^jopDJ_Nm&+-pe*gK!B6bry11BbtC{f4;R z%giLrTr|Tb&7P=pSY`Oc(Kc}8F!@T*OW5&4hAq38;T80fiL<6_7vazeW0il4UNUUr zI31tu)x#cn_G#^g?(oP%D)Q#~jUH|zrcF}5?2N?=Y|g6-Z1M8fY{IN5(o*PA!Ba!W zkF?2iW{P{d4eY0$)08>0?cI+)l*R&k;HmxcyYK9)@4vT=+qX$m;X1ktn#xxjzqJoO zU1K}L_Q}gazbG-omDg6tX+cNXu<1KH7#pp}Wccri*k}8r!yT&}YY1{dYIR@I9B1&i zEpsH?6TL@(dr@->{rUp^z1@1a#Eu-T48897kxKbK`2RNjq{ce-4>9lYt;9wBrI^b>tu^>mzmf<_{p zr09L4U=Uwzxx}-If6hH#E!~Bl5d9%l;VSJa#?L~g>!JFCcC01cP8=<<6UQp-jKUSt54|*0oJNj~XpYf`a)5`9j!rbqytms?ES!C1~u2&|j=C^8M-TZQWn#+Z2 zDR8qhXY`Un8cAEdq?P%iG8M0fMUET^D)0ewua~dY^UC3Jc|4dYrmrSOL7#zj4p&<% z6|h1`JJMcAen&oY@38%N!e#HlEXv>u@@{wySvVBG=fg+pRo;){^%gxo)F|DBqt5ZE z1bshgDJ|%T?t9~VgiG}O(&q|(4)C0xZ=CX6s-m<)uCCbwG^UB_*(w0>` zpR_%~Dxvvg`Bt2#7Tu(6c`XH2p}fZ8StZm1LUT!)BzA#S@TGV?MGO;g3asL_l+zi- zc~oE&uci2-yLP&V*6S#3Sp^=cpf?P==Bt?x8)MfeW9&T2ex&$16zCAfvvq%Atv!`a>jAr=3W-Isi%c) z?jcqo--MZA=84I`CzFl(<=MZnbIF1G!|&`Tzx=uC<~wiItndAgK4`bxb(`iz&FaW+2zayEi!@Ml_ zd2(yn0d(G<&sQd#*EX)b=?1&*mg~hV^raqpvXfnX^ELM1GW*qOs*zKM*xc7<*{mfq?4Q?Ps(GhZ-(F%<=1sCmb0+BH>3dyu z<2Ca7^1ZTS+A|B^3!H`*7$ZILQf0s3Qz0t_?<%?b%=Zi#JIdy~vPgVD#yx$Zm&XoOHXHeDcva}hFuT-$ z_z?AWMopezBPWfw;SzCwlz zx(k}hq`9-jE6gGBwcsP>kVZ@%DgQ5zC(W5=GZ)Xb*WX!TpML$7efZfrTmJq^+p=f3 zn1uch^FVtJMyOAO)`I4u9#Al=%sGsLR|Sp57n)0QW`>w$+umKaD{QaDCMBpBq@1~! zNO@WklB4X<;X21EN9&y*;%IG1e;~J2v$*UTKIA-x`dhNOSbq<5?>Tm~I`nt`3H%?2 zE99@>|JzM<2b|}XS>)2soPRdx=P)PZS%vf3qJT|GKeGC=kBwt3Oj7)T)s~#9n&Q=Z z%+ayRI?WlivbVV4~p;Q6s?qof{vmpe9wIGoStuUEp+Z*U+})`6Pq)fcp}$M9?P~Lj%L~6)?{mGh;=i=)Vm1VoFx_pEGZN3Fd8de-3$D(Me#7O1+jIk^C%p zVp|L3XQF?^eO&kS(|D!a%?+_vk$zC%MS)Ka;VlX3@R2$(#*w29PXA)h5*%{;u$??{ zMAi1(a}2tQ=M?%w>fy*6>t=yCmtMkSd@OKLD_^(H9eExd1x<;WqLwoCxBSlmerawl zcQZ%K9r4~_F6}5tmM5AWIpnz(q`zfktjJp%>H*DB-kUU)U`E;dX*C0cM+L1VeMTtf zjd^7JwfI&%n{Y1AZJ@8ZPEnGN<>S6QG(eXKjRb3 z{4n>^)~Dhbg^-&dXe&W^ z4TWRe-hMj9?CxXHyLziQm$t&=J$*y`a&Tv_f2)WcJynrAda8IFzP(3?QT~lpw)V9B zTYA|3Egao>TXs1>4`_3*HqSdw*|({?n1uW_b_h~vENwIH|9An{{;ImE-L2YH{N(db zskefL!F(`Y4WD(={(*b!$>%yN^B$%^JAqS}*F`hozV7uF7=`D!r+fb_NACG(DBRM=|1(l_?l$B^4>U7@E7kFK0z1a{i3m;8n>TbNV7;|r;paIXR=bJEu1bUA=957MEFbC2gE!OoHBmaBr3?By}T z{Qup1 zHk2)YtU-*T8I)H1lBK6S3}je%R-spNw9ZZ*t8v;2IUp_UJKOJaPD(#_GrT3IG!%3c zr>U&5`jStrzI2T>lzgOuQG7XOp-p@FCFFRp+b3Rj~~vmqpi#}?bcke=68x#OE(E=C$LK4d;fPP zDSXc=3f|Rg{Lz1m=aq{5_fA#8yLw&dSoofm7rbZeB4!URS+6yvo2;VnJ6A;;q?fpy z^bPV!ka?q>2i3c^yOCW<+AmanP`h!M?`myKwPUS0^5MV@zURmNS^9w(#mxnCPxg8O z*EI`GFA7#^EtKb2`Txoxbr~s|U1oNug-o?d=TXKRe5gb-Gt4>5OC%kmPK@F-5XUH? z9^J7Pm%B&i9(ln5tF%4mIk)5BaEMhrr@$6XO=Y2iGsGG#RZc@W;`}IL7xjDGzwg$T ze0i~85&q5N8Rloe&xZG;YAn+GBtw^d#1%z5t*mIPvCjyPby4O!%7%qiJg>0x2TqZ` z!hU4Dv|<+a5OO~2bUh`=&{u8>tfHBq>B>xDCs13B#d}ql_o2{Me2)mm;T%@U_N>C| z0#?Dp;#tM(D)ezYqj+BNy&u@5EvvLW=Q%hfBWk2&L=O+K3cNzUhrW*2Qqm6&v5ZK^ zBwT}EV3YKSVPX|Aif5IG!IpS{=SNy<)C9|jnIXW^aq-DS^o zd)9gm>}3x=@sL=hL#Os)m43r|S??iTZS>S3HgoYL=_zllT%tMUB`X%%yw~Phj{!aH zqN^`fmh6O?&3p^H3!YTo%{Nf`BSBxF!!_gt;NcxW5+X^33>{7xg8(7-!8b~V!N?@JM9o6M{dCA zp*C#N7=12y0c~aElrhpX=)<6^ki{}!#8Axwku}RMAF@`+v!&0|rF&P+FF)6VeU9in zedTHCGi->?nHzexS9keppYG9B*>m)Y$e?4_BJ3t6^YhhJ35N#Ye|ewbNi zM1kau<6 z^G@`AS`2(D zTy%aW%o1q^nHjH^EH`^jwp_E*K$tmZz9rQ6@mdNq(7|kxf6jZC^zVW`NK>Y=>zLDZ z`K=q}uVLm|eI@3Hf_@^ri$}SvI8L4@{#}K7F-IGm_J!sZ>{t>@vcn+IQ{WSSP6i+4 zH5S+V2{e@>&Ek-j=1QxtCkF*>1V0U#!WGhb_;(H`(bH_gZ$b}Cv+Mct7FU$A?;u_? zSVx+3^lx2RbLc@$ZdZYJSXV??dEqWA&)a0Bc^j-G_iL*tUN0>yKYf|@1+q)9AcGw| zWZutLJ{%cw`Kb%UE_o^QLgn-pvh>~TGCGUfO~^jwnUnR!_-_W46c7`MAE1@BeUwO?-K7@sGtVa7tTN@qM1) z=$XW8E1pyQW4NSkB^?-~f>q=_31{YclK3Pue!f@*O(i+jReIbU=`G~PC4`N!#ITVe zRv{;j%>AVOBSUk^Z5c(n3b`p{yY221D$gn$J)=lt3G@`tDV|aM@jtPO@A*T47lV@e#8#Bcz_*%zJA1hDFW!GO}&-8rW`VH-Gy$AQQp<{>H;88=xDtte%%5}Hh zYL7qN#UAU@S*$`oh{v6ue@+?o^nB1)D42|17P<;O6Z{?QN+i>tJ`b#d&z5^XIty7T z^WIn}R$=a^{Ui6=-4EZV%v|)55mUz5;PE5n7a2EWl2`?=$}>G)P@f6ig?tq9bJ?rN z-ob~vJgKae_76T_4?okz9)ICEaSE(L{|824#}NG>^p%IYJSA@}9^0q7z944lH)5#v zCbDxHK0+UYyGBhLWn*WK6|an+JzlyHdjrr{$b8}beE7v0=_||x`9fo%&{(+U`$1?e zUT5LCkH7fbR;+qouVF@roH=HfVHMbfT|;E!ez$X*_Brp3+G7bBanius_Vk^{qt<0; zkdao;UX@g9s86;=_PsIF+L-D7Ca=%dy+;`&_#Md9Xw0!AE$McwIa*pkZP7Zb#Xnm5 zk$fhci(NRDd?HPS%m+LS^Yc2M|Pr0`D`aoI&J0T z4=rXt9I_uzw%QLr9#Z}ILyL0Ue)yrzemGHOKc2*QkfJ#v?lmO^YpkO96R`lh-@GOQ zQ`8i#wCck5#42zKOj2F)o~klnln|#Bziq03XU;srHe3_C1jmY^x2&>Yg;f>0V`ctZ z;+Cp{cZ1{Gx>lM0j+Ga#IE`mu82+Xw$3~epHDRW#8GN%XIYthz`aI1gnk&})7dsBg zm=AhBVwAw2A&n)-n@2}eW^Rx{bA)XA`Z(z}I6H;Q4=3cUyG5ZN=Wg?5Cjog;agwa?4iU`07-R4z-HodS*ZiShwycO==r)v?<9 zOQv0E-X<&0UvFjkUt4LxS5}(;x#efB((d29w567p`i8RX^U_~+`+3q{RqhI2mb|ou z+SS88!Gg4fA^jzfd1kz~Y3v%rgF9cG1MB3Z1eLr{dzEvO=bctLNpmePX^!P2&a|9_ zS(Z!Ae&S5aCBHUc7@a3hcm95|qwMJ$Vv`v1Qi8q@N6#&uRnSl16wfL=_I$z-M)8HNLixvS z8O5J_RtekOU3}trqo`=X*Y5&-LtL3zyEeNpnW42Q+E! zSnirl{LV18mXKg<_WZZ!QqO;9tQHGW(VJ z>fo$Rq^o>rC$ zxwWuLhetZu{ZBrw+?5yl_Le6F4{ew3?4Ry#-3Rqo|L6HW^n`k=|3eRm?;r0jK3a4f zG?gLahbn)DEIYD)7rZ%7{L*`9A92{MSLWKxC3BQt2TQ;eTlep?En$0Y+rE9aFUs|W zJgaa#5Ff9CU3k1FI#PNI9v3}67@_k`yLO6o;FPrd9KCM+_O15S_g~xo=>6Km+!PzrPLSG_i??R>XOwer<<&{x}e|{uKCB+#qYbho5D59%>VIdnw>nFtLzhU z+T=k!QENZ`(BSIFX8YlX20MAOT3NF{9L=y}Es1ux;h^rf%4O3`ubbWRo|BrQcU9Ge z?}Vzh;61A@di%5ru}QJJURk_C>;ZqYEzcpk7HBQ#D4t7Tm9|xpzg&;~k>@J%R#-*B z($MS5bC+9L-rH85zf%5R^4{>}G8@&@kSJE+-mJWNGEmS(&`r)@6zM8>X4T^$%Qx4} zb&&(ulCSJLa%Rv?>Z-Qs{#=~eI{U1Hosn<}jE(2yaEr_A)w~zIOm<`+ zZw&dR@l5}~+z@kMf&U6VIeaL~emqG&P^{%R5IfV>~f~^GL~B|ADg~HJ9yZ`L*=EusnU7MtJ>9* zl{nwB6X%Fo@OOw`;1+f=C(d;9LHJtY+vL*1ED0{3)@v*3>%@++?6^^u6+2Qr9{N0Z zWhpe2(;f%CpY-UVmKHT6RH>1J|DTl{IauYHr7fp$?sXM*{xAoW9zEM~6Bk-W+)PW3 zoNS2)CuoNuzFPJYrbLdHw}p8jW$uSL)=4=q+|nb)Xs$?k`2nLu@9uRPpXf2yd=GRrAX9Q^>8q zz2hCyE%2?73(I5tApM5*Ro~_L9xqB)p+^L(&!gpTJCbYcIX}GHd@pd+YGf z-S*%U_u13kp0vSZ``Mypb8Pl2Q^YFcW{$K;bH>|*S>xnMA)CMN;QsQll7WwQF!%KZ znhz!ypKKKL9#{oU=$;22w42)99%2=IDSWSZY?QNm^;LSWH{Nl(bQtp2@VSzK!p_1$ zBVV@JOJ-?)8UG6Y6ms?Pn=oU8&jkN#?_mSQB+T+mm_1Ye5`jDA*t7UZm(JP|1jn$OsNH=X?bePR?9u0*k-qZqGo9^;7oU@U(sS@Y>oaVyj`(Wn z`M@dtM-B^pKYfM{kmiG~GI-n&^?*iB8(|Y?kGE+Hrf82M?}b?=d@TG7nR9;oqm}l} zmhWxd#tpXa+iz{nS6|uk_useAzujOv_J@V^mCbwi+Safz`)bQ3du#P7dw0#pw(UTe z^cFm~XfAkM=p&)Kz$%;e?zXSCZLzf*H`=PTpJ*N^D=*Wks!HUomG|UGrS3`0)HKz` zD3i6mdY|^BD#N5c#*Q7%aC0a}OID#=6n4) ztOAEgtk>+SgW5@OjWfVdnV#5Rbqp@JVgqJ62QhwipA>s409)*Q$z^>v47Q z3ROkXaw{)fc8VVgm+3Jd1K0THc}&47Xd^JnX$6bqFL!Lh=L-rp@qEJbe0?5QQU3M$ zu5VPDzsid8KDOG*E!NZ!t6V&?@Z_BhW?AsQ($|q+qm@ip=buJPVec^cW@rgj72%q1 zA-krf$z}bqM~HoW_-OFcz({5J?^;RTdsdkJfo8ked4Qj{yl{u*XKl5r@@W0L(!VkE zji6U27CF(P{RQkW(7eu(y3jlhGeWS9^Ax#WC7u)cN19xgY;!}AwKS5^TddrDn9KL9 z&{)Lae2$~l(s{^wsjk>-xtZ&gH(Z>%&hpaVw&I+(tT1bt6=l6;rTL#&W7QVz<*X=m z?}hhMQL^2N^EYX}3V#Y!UHsjtJ&gq$#5sOhqT6ePl(stE?#JJ?#%H z&E@O9VL54wPpiVrrB;^vzFyDw!(Qj&ytUGA&=X5@*IHTLx=@wmeqzPhA1F5$T_-2` zHOo$1tb2N1@i5VeQ@pH+ZUA(6jR%y#CDF;kJK9dufX@B4UOkY@8Q8Ug;(PES3o{t)~@p9qe?Yl2S}t%b)h z2z?^*+;|PUe(=TMJNd`Ukt}7UqGjQ||PczQ-PxH$2to)HvMp=)+{j@(B%>;idoWN}HfKemV ztLZ*yfV{C!b$!w9ef&{rC~yclwe)nz(Dyr@Z@9hPsoqa#r@P$q=)=-g@Xu1{DKHP5 zLhc*$Kk}sv=&Q$EM{9wb&{mkAhUM_A3>rgEYJ?4+JX~43Wc=b?!GFW!$tsI?Sy7&wb;j3= z)ACFmf&$T@*Q>g#i@t~S%?GiqmYu%oc9KHKUWa)NC89KEK#Izv@gm2Qo-*~(}0 z`-(W?slrRe-1o_&>;xb`_iM{dUupSiE36=Wsd%9@Z@CrczNwx~Vde^#c~ktQxTC6c zi`A5ESC!;#wEVm;t+a51SVf*^d`_j?(rxUV&NuL%}qjRXC?$6JP#0j<5lXD_ z@PTm7?!qUYf82Tu>~6y+3{ys2_W|8Be?ztj87MFa+5(L7XqP872ZM(NUBdTz=>2dF zF9)ypxwC(|_yW87*6Z!;Kb)gH6L!?x_sB!`;G>V&g_m8b{1>=|`D(K3;H)dJMXR~U zdiL+7yt)n#-J`wGXetjp(b2jOc+uv*Hr?jEKEsAj9B9KP47AbHhFQPiy)~bF@xLz7 zywjM;6YRx)-BirhOqe}cGsf55f#>%&eV_PG(1Xx==<)b$7;8sH9S_{6-VXg8`aLiTN9iZ`bg(<_?HK9@;ay>eAv4Kt&Zn<>K=gUwlNb8-5|_Y7 zd|j?nunO16l7m;6H-c5zQw`I}o5K9k)RFS7%wh(6^-{f;rK^@{*Wp_qzGq9{f6G>{ z{nXa1|JuIYvBNg)-e((k?6$8qZ??4?HrRK&{tsnu9i_*er0MS2ft}fz*_oa0neLv( zX)uFq$sid-mSkDX%*@Qp%(i98mYF13%(9rNKvFR?mn2hn@4ffjxaWB@<5Q|E_ul;@ z&M)(qnO}WX?sh(YpcKqT++j`)Dt$+7jJ9PTAf}GLuOBbZ0&{qyV!FP+ULQd%5 z>65nh?M*gi!2)~li=DP@|9;zf@Sq(&aomba^R28b#mbAXSw-Ma{;DxTwUAIcAL3j1Of z<#^t)D#d22DB5I|#hb0FAPDz~aGx|7R%(UuztZQVV2jq${CCA9C3*N#1MNmNI-%c0 z{t|gh-ca7g`NH)QR@gwS60!$;5wHrpvcW3y*6SYU5?`Zi;jH3Z;(4K(tim(mmE5&f znzt^-K*cajcuhE?l*u+`X2Y@sacs{`;M|eEhe3dERGMU9?@n4C&JB53B@U$=i_<8x$4XCwvulm$T^~gp!IMLUsR*(RNl1Pw~{nRvAXJpqN?(`R&*ac zuFO|dR3+K%ieSzrYjt_xGhuc$v!cJc9cSNubyfUbn6*n>kdw033NzMPQN}7ONMB?5 zX+bWA+)HNC3gv4GGTs&UGP8-iPj%@rE6F=x*_qod7mgzjRCGvtu-#Xc@%D9fl007c zyuQAD)2b`3YEC1yLWMbd)Z?ZoZ>w~Y+>9VcRFL_O@K$*+fg8gfT# zH4gv}4W8fJ8wo%!zBPQcp}pAnMQhNkf&3oiMjAG6q&yJ$6=nl>>fO#oo*pH5T~sU7@MZO7;Tb$DuCcIB-LlU`q(+&#Ap9(7 z7EeF-tUdkQGxpCv{#W~lfBk1Yi4nmG-S^DTd7S?2Ye;B;Od~X(Hm0HEPq$x)16lZ3W*4dJ3}& znX!YHwNCw)l%r9-&VYWZDMpjQ|Ju4+7x`ZswtUSRw{B^58#YkB$LA+M@$55J=fxLv z4!fYUpsBR&+C_OH^cX&u`eZ&EO$8Pq)WdDW(3qYA!{Dh!XX!a)kbW-ngYd^tYXr~r z9@a;>AhZ>DW%}ajnpM5!^LI6?hvW%?DmX~|hit>-@-^eLFj*jldb>*Te%HyoEB3>)Gsfu#^-s!21qOqL%ng`9)NpleWSN(v~aoQNDa+I!1(v~M68!zX5SNq{ z98w;rxZt1_g&O*iCgLVPW0ZtRhgjw1}J6@b578kj~pG|7;;Z} zY40d^gXTxRh(5#gEiNh8tDhy0Ownm8FTJGSU9*QmR)g2{_o@u|th)4y=0gVkiudTV z&==|ZA}>wm$Kp@cd>AIhVqX`%-|?t2 zV+TEg97^L?n_Ba>&E-dJ+N!D5ZBW-5z1F~5bZBl(S~Zns(!P5K>(r~Wwd~Zwy7lj> z{%Gux_n{7l91gi1Jh8m1@!;ZBeXU(f^_0a^g3kp%F8LjF6l!_#tHLc0KJln{!~4E* zjhbcpsr>!F{F7>-VFhZ4sF!Zpu8lMza!Kel=s7$~?GN7ChRt7=r*`7((KdDdc-1Kn z9^1zTjOt}$rVXr>FYr z!y$Nm2z)oNihRPgpO#L9X9b^agQiWaO_wg#cj!>-J#2^#89&Be>(a)COd4*}7tIo@ zbnf3-epFZm-zq+m=NdIoeuw&E`pMG&WYE~r(pF#fr=>$rr5eI>+I7#pQ(ooc{Ess+0gHN{E>7OI0RlfaPp*marm%(vS*KN z*}C06*|Sfa^3Lbm#4#W3*dZV8v5V*BwI%mM{}pP5$N{l_dEu<>IDF8yY~5-rHg2-b zpMGk`E?m%T=TEnNVqfguVmEH=x1xe0R#CV|oI*`8d~#PCdEagoz9+v(MOfEUSrqhY zgEi1hs*1zDvPJLdn)3*pqI(u} zigSa!kMo7Ea7UC;_^$ly=w9?*?!RBaBIqa5P$H}Xub{86SDdp(yh2W+F!L=d%KE@6 z3J=;hw-WR&{qAdK^aOeang-|csoYAEN2M@-j}_*AuGxyrj$}q8fzB3aYx}IahDfVhsn>{Wd&K=tuXrw z&Dk!_+o6Di)SoBk3-L=nK4ALSC9Ss1xOtXwZMtS4-@Gzbz7)^-WRUl9U8RPH)_p4X z(@WqGXBPMbCaD>|5359(MXVCe8`RuEpNo8Ygz9+IGoQX~r}|p_nLsOnSK?3gwS?3C zV!Q&oz$%wc^i^Cr(ObOYjNo1lA;$Kf;K1@_QJ=(~a*LLSHG z3-TVvw?bVHoK(M21ADDi3%%p{K71w|Lym~gq^HW?{Nvx--~PjYmzNg(h2Up6-|#bd zmS=wSAOEJD&TAc-Ti@ZmtyAB&>QTFBT`&h|&dQ*#JwDK})5gdn%yZ1@<6VzOmHEQV zHtsdFw`z&eTBtE<-nLcH3-75~vAF@vWJFKFr$Rk8o)vOI%z%bh_)NHgc{xMI3{}n1 z>Ni(t9wPnVIt}P*V`ombS<4nk&tT3V3_-mPOo9f2M}@o%+`@U2wymY9pskPxq6QiE zAn1*PcEWsR?xpt|&$3dV%xAGO|A#$lfLvE04@5r|azOZ4Sm~>R*1|P(mhJ<4t4@j< zC2~OnMh}#41>et@X`=#7W$PB(^w~Q$YWjH9F0X!Ty{+4_(H5;+VJkPjZtLFJY#;6X zLLOWEu-Eg=$(0*7>{=2Y+v^J0MIPIP zc-1s}Ju_Uxb8mj|o^ASQi|yRM)e^3su*~dJmY;jf@^kj;-*5O=(M$r3CD2R)AIke; z7qk*Fi1%%CHYwU{L{-V#G0uQBZWU}+cuq(66mGIxMH{WU@J*{OcGEv8wzv-@9I( zw^l(uCk zf0f$*^S_Gh&)=rnoyv1o1v?dQQ+<3v)&|Q-U21u$ODsQSq2;B_*Zdvk8t13Xk3~UJ zc%Hhz^3xYvLHaT)&0Z`2a&hJx^4sDcF3#O1zX==x^Wm|rDm|r~OBH%YIWr5cEB`|d zi20r0-nwkJt4_*iTu`vzO7XlD9gxO_u7X}elu{?3|Ec~zRw7+xkNORl7Y4P{e7*xC0jq?Ly>y2{*71z%WxF^IRr~8Rd;!gLwC*T$j zm?iFHAG>_2k6k+1JH{yJD)5TO<>S2+myY#RTshV=7MH*5p}2IkyInroE2f{grV?G@ zlxUni)a||@Z)~Wcz$bp+nS)(*%u1ZzUn355G2-O@u8QM(y2@9^hcl2{FJ#VkJ zZf@QBb+Kk`nut@HwrL_=g>zU1KT5x0{nfJsP9f(5d%y?eJXH7c_#?{E&~t@;`}AAk zny>H*{6a7<`F9UJU_bilzuWH~_`Nl2`I`Eta1BnOpB&l@EJC22{M|qOqy6>Y|DF1) z@Uyti_s8$b?*hBPEaaD{Uw)-kW9v5p&uDk`s9nEhm95;k#HKEqA`j^Bi9@9i{pt_D z(Raq<(WF&NYuTy2`lt06+*7_$avJnEqrY5-?wzbt&u;S7GS>*t3ObM1HIeh-oNIVl zsAWdi=rwqN`kr*@-(7w5-}!X2JgCcFUvBODbg=5O!UCZ}^ zRS0^)Q5OZP;8&p*2(JzG%B$a6DNThL0SneGwsEs3+r+ukZQ7zaw*K9>Z1v{XZPi@ay|1~_@&FZmW@UxJ%tGp~%jFUNMDMO(9`Z-puPFFTtVj>Nvh4R_x=NH) zVr&Ala#bm^qC+ zhdx8C4E=OV3O+M(La+++g{T$7>seiSNj2_)M>G(>&emu0A z;jNj)DKX!P7^Ny+ddi)u#30Y|RhCgpjR#mg*$VbrQSPUzt)YHK^}d=LxbS6z>Zg`^^Fzzd*rB;qW%+^c)qDBzcP+|%Talmfw(6+J*)YcmA1yN`t4o=o za8=x<{7-eVIHj`ux|Ns3TUqgCD=8$`bxd{C_=?hRuC?U&Me6689KTHU*z%bxFSIYn z3uSy@*-5X{t4xPy7ud$dLQb6sa@t?ayvY)oJ;LmVh4BuPN8QT zep!0Bd9M+CCImSh<`YxH%se7yCjQNT|9kmQ$d_;pZ3Yg4J>U?2#{c-Yf3^SeH-9Z( zEx9IsCLAOU=cO0LUT84fPaQQg0p$N3+S3M)>!+S?3s%zuZkkP;Jx&_HfDr@L4~Bdc z--{dyZ17UEhT;lpS{ANbU^AD^u=%U!+r(KDtnZNi*0xJWeFx@2qfeow5cqA|bn2)c zz<7Yk2hrb-dsEHuM(uVHH;Rg<}FHL05rQ_#F6#d=EYoe7zi#OXA!)1!jRm;1qNeytLH+z$)%t zL1&@23pGG}hYgmlf=1MT)F9PN^&QdAMo+^xJW;a&$Ysr6wZImvU21bzE;c+m6X(pd zHE+KmR$01!rM>;}2g>d2KY3ix&gd<8Yw_FCbLCRPHEAm7D)h5uMi7h=mvmkHl9+PcuHA^UOL6DyYQkB&c=fQI zy|CL7ZyvFXtdo|NecW=hPgp_TAuGt)srM*ainyd;v(}(i=k1tQ;=H0Y^v3$0Gf0e4 zV39Cj4nMxT27A&*e&Bj=?e)7nf~FGDO8(NZ>nP4FuCr9;uCwa=4YB>o{8i!>zpo;9 zm6!ldAs@sV1Uk!xyFK0V=q1Bk!fjSvw#TYV_bP6c?+^OUmK~BmxvKPN;9D*`Ye;@CtRwumqlR@Hz0@vCzp=@Z64qPVjg6L__JK4F zJ_G(NOxr9^FJ4~!tmLKW`Id2giF6Rvf#+{iEp%n!VXG+oQn?_ss><>!Vm3U+l@*E7 zQ>ZIv{u6a>d1+fLJ#nR_#4{gfrX^jSVQC4AEI0LSD=XL`Rw~Terk*cZN$V`*=5qaP zYJZY0jS-u`B+2L{*MhoZye7WKzOKP7A)iEb6EqcACH2Zs9VcBJq!kw7*!2@%;gaa_ zJ)9D93(OLWvjbwR;<}2nN|aY%6F)~o;hfd=74#G5lj!RE@QLdwzB;S?P~Hbtfm0NR zx)|Ym=MHpLz$&hp_zHWR+TYnu?(3wLV}g5KSAkOq7{x=ZvagFc0X+b{0=~dQg5Tt| zcCU$7U>5qP)ou8k{HfIY@b1U2iVp;~3Hrd*j_Dd`9OQ1$HQ*GyCU6T30He6ylzrac z28n?Tp02TR@hFUzmO zY{Q42dPIKTuKhc!xBj>pqixob={9omFdI64unieE$U64wVEu*;(C>jp1Q#`H-$s7Z zCT*Hq`(B+?&w>U6uPl3Gne`t&K>lF98=OKejriw(`?0ht_=_5#-#++&JzDEYd1_j; zYiq;CjkRIp$5@+g9aQJLdh=@ODzlf(k(YxWw7kz@334}RA?}-H^%`Rs1ZF{VflD|? zbAeaL<@h<;3%o-P2t5Tp!AA?fcn^I%wOk8gi1ZVf1VbFIn96^LFO^K|67Jj~zI)*=}Cn8RP;A-jm-1 z{)pvr$mfKb3j6n*<1@iif?p%-PvSY7A6|nTjpuZHCBNdw(G?yc$HR)o;C$lx#}BQp zlSEhE^&WTM{VT*LFbaF+IV*$63+i{!G6H>t@AtZVzx0I(W=MY~-^Z=;z3MZ2tMq_s zr(hDzK(0Eg8t|`fT~e=nJfieh<2oJ?EK;tC)*O-gQ4!&h2Kc2ztCQTZlfmU**b!@l{o-dd+=JpX4g?N7qzCR9${S zec5hTT~NJlRrwkFy84uOuP|e?<)*DsA2o4C@;oa{nQKMKv&ADY35)`d#8@S1rqv7> zCWtxebx-eDcm*AdzdL?nYWI2PGn11(!G)Q4i32||H9RF*?^{vkJE}9H4q3Hl(#p20 zM<4SN@@}rtJRo}FXR81G%%Jx@$4QISYlXTh{d2&#jDNXkpFAvhp5aRLEBXX{U3JmE zsw5Y+TizG?sW5Md{`#rcrdrCisp_YKHbfq`B<~Z;O?}5Ql9pLo;sVQ{H{A6g_amQd z$Reysmxjh7y3aAWpZl@OJ(|kJLE;l?_^%Un4XO#Npc0`5Z>`O!~*B3*^N4)YVqpO81<8hQ!A z^9`Cbwig<_Y(M_@f4BegU;nH4hMHy9Y54j0NcefMQKQ!ytH*xF-tBF`sD3tM>2#aC zfI8uU^3hVa!o0uEy}HWpOKyw4cCU46ulIGm#tp1fpKj_8f}X;hn{ETUtDcG93+;w_ zWOS5AYdt0I;vA1HGm2h$t-0na(=U#GyFA;j=fr!o!5gp(`U#9e?g!n3eOQBY7zF(UodhOVF4=A~Dg#`>smf4d$%tncvQ zs(a>{KvNkk?<|}`tq*z%{#xaOMh>+ugQ!6oCRQOgL`@Jqeev?p3yxWZufMlZS_^Xt z=wUZ;-ZYyqXPQl2JlCc#U1+mcEVDT)m)qcpV{PG@m133UZ?3fucWkrww|{P*?A~EJ z4j-`hwtcSHesI6|1zlzTv16JWMC}iGAT$(?T~qN{h|CR2Oij`ZLuLhe+(=Kc8|g{G zc}lV+hI5cPP)F@X5jKc4Y zRztsgdLZ8pYvaGYbJD)Pb=<1U_K6pYGT*Yi6+~j$dn>1H^0*e$T%~BMk z%vP|MpFGR*l4fWPjstGVy*VSsFU~BQF_^qUeR!A`UI};Pe5@Wecv11|(x)Un>}j8! zxX=nxR|UO@na99f?z~`T6Z}_{w$?J^76vnq6Q)>pd>G-pW9IcR+eh`oDJ#Q%{A=Vr z@tPs>fz)8#sW@QOB|B7`o_=GcCCAUP^!S;U6+c6B2&wUfSLnr0e?V&W3Nzlc^n_)W ze055Y^P%rs9CMRLg*l$#mV9NXrCc3mNtcIc<=olD`6S9D97oU5R=&?E_)z-CG!$3` zCV^9&O?-`U$=QCEaHg-$SrzzY!yv!oYy!i0PRRFyTA#p|;w-}Id=g##y0eONim$K= z$DaFvOPo_;S_+y zJZLJs&*2Eq5&3m^1y(_qfJf*#`#*m4ul84e_uuTFfBYlmOd7q~EXFwGg1EOs*Usu& zM_%QDM;{T}{FlG}EBlw9{8*pab3k9~*0+~DyRc33cCT5te%+M=!ml!a%{=ufqiYwsLllsboG<>a@=J3$#jJli#EgGvA92~?vpvRtmO7TSP zI`&Mx7u2g3UkYsUYU@@uY|Lo!3*K6CTGZcAvpjy*belA9hIQ!OSv^~ZO&TLkfg7A1 zU=J9=^EiIaO6~?efkn_rxCb_&pDi^)WbOI)$&x-x}3LzIW_-9QUr) z$muw5{BPI%z<&xyz$C05&MD3+ceRvIGl5g`!sFZ(fsT^9!pd@%-y7L0tR!c7z%Y41 zZb&{``tnn+oxerzKIRRTF>@&ET`SA^P_@7GwBgOYTPg&FPBVo6{}l#xw=&63j53BmYZr<_0TBTWnuNIv9t?xk(}!@Ec^PjAQGpmKM7u$g0vNum%2*zMAQ@&XRK9Ca8^9C1ScANl9e#Q zaunk&J8`0A#ZMAvz$~u0z#r-Hvn@4lj{3D~j&Q;XOHEj&`F%I9&bFj0lP#H^ZJO&d zK{dwI8q?E_oERDp|GeNT&HlSOCa60O8O1%aFpAg&KDjzXk#uFSxCNa>ToSJLcxa=3 z+VD8aE7#BU6RWs}5?!59qAS;HG7GE{f4WbMRbUq95^+j|U0hRf53RF^ub%7ib6=fR zoK3_huBjZo%P05KQlzUK>SE^(c8)QMvkEJ`5@i%Wc0O@E#n;n&JBd}`6~Z~?#GX!e zEQ~LAcZ}sL$N^E0gZBgv3c5+%M$c=W4)cc5Qpowx1MZcU_)zMrU)qaJ8|d9lFV+{( zQ(mlV4?q2+>UgQ;<-JY+5%N3)S_7-{J9TR7Jx{N+pZwzI_7DH^&+6%hPQpFR{(*U5 zA9}992R-`tk=K@73!g=}W+Vpto$*2JE#Ur*l^ZH3xudjC&bG|l>qV73kO zZn|2}A-zhHv)im!7fuuQsuPBS%=%wyy;~*nT~%RhM>}{YNd2=dRFx_zuil zq)wTijjn{YGh_Z7>(IT6Sd8D1yjJ~Y&FnuPen_5@X$$A6AFbG6Kp$&^2GXIO4H`c- z=xG~zNnj9iLiBz^N1Qj)T)xm2uUoB} zpf^5v+qQiEiG93dn|;1#r+sl?uW~+kYIh$#q)Cm!Oe?n@CJh$;F$zi0}7yI|yTOWR?TIb}HYxd_q=LLCqYD4H-f=)sm zFy7X%X4q?NTqlX;c+f~f{&0=N&*2Sc5%+^c*Wh?{O#5&R1kbAJ-h8`lk9y(1WmWXW098gB4}0x5D&wfwv=hh2^I%x5AXbW5JBS zKu4Kvxzb8z2Hq3=C`mJ=qZB63w1T7=3V5X8=1j}KF(Wt*_Y0DzS$@(~%ey&6!LgqQ z+%jFvl6_;gBA0o_H(`^hmX$cga<5Odoa>Xs8aatm!u_dYm(0ZJv4CU5G-xT;C+Pl+ z__3CmFiw$`I9}`mv%oT$36sN^VrVcKaY3YCof>E&*CuO4Kk@nwqqP*~2az?f$)hSIF(0s}W}pb-5?t71-qb{w{W5f43O3uyZt2|w=w%+^ngF}O0HesEYpSP!9d{&I|4{}Y?a2|PB ztU|3X`4Z;(F*gxy<(K469)3vsPd)dnbQQE2-v8~ocCyaBy6fFfj)l)-{vrMpdfB48 zz*~4_+H`KGcP9Q{IP3X_&uLZ>?^R|Pckb84+T&9hNq_jEHg@Vbd+^DJY~0L=_HgaT z#a`6x)NAsR7=`{J{BHDng*Uv1J`4i4;6)+d<8|AkCylik^JhsrLPPQzp@*M(%0^C{ zDE@#CsHJJqxkI3B^yy{odiNBI(C6)$1}}?I$l1UQ?7|J#~aFUNhfz9^9_p#r$uGS;?NvtSoP_mF6z8^4ulCbukTG6R=Hb*7Cb`#M!H? zBnRy!2=oiQCYq0!|AF5Bx62MnTTo7@>`VD=<*zM2CV#>=w@xeYw|rG~(!RNK&c3^I z*}lCKXW!hpZr|QXQlHvCQiF3RGl*NMVV|;8`{quPeMOy4^);~#evIO*O;(t`!V1#o zTVcimE6kX01?lq?`Dt^lAR~yvjJcMdu|j-$tL!t?Kv$RS*NkQSBzJBl*f(FN1Tz!A z&9m>mE7UBaKmDm#JyDo*`KNEwR4Yxtmp^?KZ{Jp*mj;A(LeG10Ak-QcP;Z9RRSJyeZ{%OS;cvU6;>f?vPzU! zqQ}lCzQQUsdFA|(Zg%lVw|iIxMsaS5@=BCd&hHC-wIQdVvA{5d?>US33X>3L_jI@+6sMV;g#nazMy#Q znWwE*-Djv-+u8kz3=hn zHf{BqSOBKtJ{X~XlUKwm-shfc@Cw>cla|d?2Sk55Jhse+W`^>(>0?#T)PH1OTee}b zdaF#BIo^K#;BTZm{EFJ5I<-}|{6f~bfN9@7Rgq852^d30CTDEUz z&oy|-9)IpBn>1&dd>aj#H?@HyhT4Dor+-xN{Lryu#47LvIULx68G>jR#LLZ_N$Y6a zt+Tu)^j3i%;1u|V`eQsP=q;?U3LaYgEBJ5GL0}E$C%@9BrM=prt+bG-^JmKYO7Ga= zlSb(~PM*)qqfvSWPcENB{gZog(Qx=q)csJuGIc1W# z1$Kd9xVG`VckRP%TkY`a6LultnzR&V9ny;*w)u4D4*O{PHv3}#KJgC>g#YDC+*S3O zW9H#|pMPTcdFRw`i=L+H6U+RZ!Z*b$uu6oSW7Hs$y;fa9_TA*C}G6T$>qQyH6ohNsmNchaP1_}PZnEg&JrtwPr^V? zfm57S%5oMxHN0A@+Lf|$l`kNIi=b3^H?T-F4~JUmx3);Rs5;E2=a86?zYNO z<|gmgd?wiBtE%(%?d?nU&8>^-syMaidhXL+uVhFBAf1q5pmI<*~tBWO}1q86M*l7{ys7 zsNESP{z$zvNa31^bBFk(hKAyM!98B<6KE^0u{eKxe+_h%yZIjcDfdReDt%&}6=xJN zOV~dJEd@rQmkQxt70&@hxumA1;^)pOtoPGYU=_mIB)U4MoH^K80fWFK7eX#^4aL`( zZW3xJ_vU>%#aP9e#aHe-zNbShU=`-qP_IHiSbEw&R{IJ2{i6?PHZk*l&?LyA&^w>p z5ASEFt_Zs#&U2TXoIw3O|EqsK?=6eh<8|-TL;BU&ZGrG8-C?O7{WXG}Wfgnq<9IUZQ$nU`PC9A*9%=qB`a{fGbgFXF=b&6=ujEDS)OeK-ZS=rN$b^&J}U z2mV=RC8JF|`fMGm^+H|o3hY7sGPOYP3q0ZZ9~g$`sB4CM&_{w?(Kyu|GY@&hq%qP} zCeNE@Bd3g!rx<-?@c5Cck%m>!V(2HwXTU~iI^>fEjSjS!fKf(Cj~YICxQ&@M(mvS! zu5H`*sV!T-)VdAE+dJ4M%$_0k7(Hv6Em*%=8VZ_G^UfV@z}V3?e$FguD+9)jlD5Ju z0iMCbgSPVaC-2$L!+Y)M=@Xje^ZD*w(pBgi_s0A0+LHBaHD`L#+}ZlhGnXt9!!VDT z+|UPGKUW+&b=*#0xhPKy`U(0A+6tNrx(ZqhyaEf6TRNW@Z%5CcwNG|#x3!zzw1n&X zEGuij>IyVxke;#898j1GqUWpfLE-EiR@Lr=jN)wKtP)%4Us8ir+&>bm0e{5U!*f3Q ztK=!E$Xy{`VGZ&)fo|ej$`3IL+(7Ol#uXu7z$tzltO1X}DKJS%&O${gxggGotc5xj zuY~)$mWAhIVZb=B3_l}e7PJ;>o;5EhZ?l!#(kuylC)5y!JOXc$a(Kf^r5B@H*iPb4$%Ere3*T!x!y|XKE=2gtkKcF*P_(JXc3KAoLXY#90Imac+TQ@SbuHjKMkI zgCJLe|AS*a|KRVX_b}UsK61=Hq?U-8MbreL3y}{(iy;@(s$)Cd+og98=`hSsY}K)i zb??_xI?LnFJfV323s%juRU22>vey^d!qxL^(CC5U8fuhlzx13vS+}+|YVoRiz|j{T zPc0hBd1{-sX{o*? z=nTq(OrNT}P5it$mx@9=;ZOrX^ogXQ9qwvaSK;3wKv{>%Rb$`%?_SC zCVgc8@uQjr1doVacI~j;Ummf~_V2YfKX}*Pcz=t%zhk@nxUdI#puNYxRPPoT1>MAJ znvY*NYbP(Amq!<$%iAA)sAo=GzNnvp_QLZAzx>p$T-#~+IotIv4Qd{Oe(`~a^o>AY z32TjmKJ{TOPo##2{0<>D36GWYDO_vS1#9nN5%EawD(NWBBvtwNQda7mz3Kw=mX(1f z;@S%5HKHb~c#g+46mmIYjF45x{gBT=6H(+Ww6feq3XV&%7bw`{JR0TM3#}q&k%D8w zuj~GhYn)-=oT99yL5?VUl@(^KvVzQ&>X*+9Wjwj1xgUuO=p)C>Uiy5M6(1I}z$>?I zCEBgq$?C(VUTnS*%Q=2=n3e6fi$32QNtK0{HI zIz!A*kUAq^m(-xXCh)W_kO!4sao^n9Ww*+hd--utQ=Iy``lI59!FQ6EyhwrP1aBR%`+H@;Qo~kHFnX3E^j3Oopy(ZCBOp-L!3PVkWd=J_Q;j99q z5O7Fd(nQO@IUyE#H^%FjYdJT^De`WPvjRBfCc4TvgIjXZSZ<6}po=K3kFjXLBt&*% zz#}k;2ii$S!YDBd*9q}U;%M!s$B&FfM%)O?xHi(#6Gp`L(&B<+R$UKQ&+#~$_!`wx z(yo&K!LJf*EMw3C6#50U%fT=|~x*m>pV#eR1Ee4iMjxK;wQa6fC1 z<8fUjU=;S8Qv#1l?@(6>R%aEiabMi2Ku<|H*;^rAIT7@pg+*W&!kLA<5a*YU_Xr~N zr}#?V=P&6hkh*1>G$$7?@f57&Cso_wyBJznQ=d+Pb87 z*n>|!sy_EW{q-;G-+%oJ`_D%nl-I;_K5z-F;w*xW64g@BTDS(wP(ux?@GQCo8VTN7 zyefEW$pcY8gntDe2>C0Rfxt6@rxm85FD^`jPDH+{Mf+Cr15;;>E`+A?`n&6F>H0-B zY0h}D7(G_n_w1}XmIklAB2UMtX`__uU|u0!R6HvWJn^`GC)fy&3cd5uR^XP}FVwRq zpL^DxZ_vP+wP|B`Q1FmUo;SzFO`9q&EjgD-^XJ;ErHl2BCYRHuOJ~hQrdJ%i1E(~4 zEznQUQJ4wDY(nxq)E`sR45Pp%?7S$g5(hebo7hs%(OBW>i= z@iuPO6tMyNPm{K-Vtr}(&iGW&ZRiQtclbaXG-jCo7ArTdvbFE5wUuwJwB@fawJCEa zTi>Dm)N6&>X82>+hh@qOY{;)5Cg=7$QvvKhCg;RY(>UW$~?&ffUT48cP_rxTiR4 zz7=QBwUV59T6LVgKz>$yBY0c+zUWXv4fk6{&9d?@*>71%{>N5Xvd3;!9JSlk=j;|W zId|giyKj<%KKi#V2fbU$_GvZ{b8qO;Do+UgaTeho_PCGZD0}#EPW&j#N*EX5%PskEwXDvIPg|2#jn%)lWk+g%yQp6+HA3Wty7cL)+9325JR*2Y zn1xL3Gkiji6m%0j9WV&9a>&yVKl$Y^lzSr2;Xv@1U<}HgUb@R9n-rM^~{5 z{Z7cS@Gi&u0Xv}Opy|-x1#b=YKlE>bRlL^=-!JeFKdzh*dccM)Ypvgi-qxa1bLG6+ zc4==DW=&DukIyUYH@dHCho~(kFY{uPM*7+O{8!ty(aL3-wK?9 zCxzpkM-EAUQIENk$Hgvu209JgB46(56L#!;(EFBL(b@Q`cKX_7J9p*0oxX6~jvwD{ ziSfHFFME#_WPcvaImvxj+(K?h{bs}bkJlS}T~5#wHkhvyXd;1UR*b?v8VQV2u;QKw z^pfRPsc>(~@)(y?i{%~q6V&j})HZon;B^Q|arevo4lJ1tfXaY5=L zd10A-SY7(D-LBYSx5{^0Rq0;4U3t{LxpUV3^zB9a(^seDC83vlPU>ng1WX|<#QTn< z-p%ibMN$GT;r$MmFG!iJmE%~XOph^(*AK%b-qVEKPJWGCj-CnG#6QQm z1n%%-R-GqJw4$VmR+v1=IEFpo4bRC$brEsM4d<1~x<7V3Ehk~5^t|RV40Gjpk9)#dCCVrq zvk$AdC&lwUH95u4Ri{kur>3rge`SA1JH4-i!WpIJdOA`EZg=U=XCFEp-eFEx3=UVMdmUzntU^&HsUI`{6V{wmbfbm-Ah zUfY%(Tgl^5>-neD^OZS-)Z+Z?H@{Tw2dxC|AlE~lhS@kU%fJ5gC$V0y_&D6FLOutM zfnT^E?SlG<9h-f~j6>%A zG;jZ^4IVqdR=u^NbAKI`!{p^r@v^95vbWTY*i` zJ~S_=V+U*9xs&#oNeG|78{~27HEO7w4eWq+Lj4Z!fBMwJ5qM9y-#>%TBL_sFsqh(f z>c1qvEQ|u55U>xv66%!U7jixD3gK*6D=qhL<>=U2w+aqQj$=fYEbGsGfe5M{%)V2hDTf_dcx$CSdFPIOAU)J>$ z^pYTgc{|Q2_*-EWUt`BHR&i#bpUKh~pTH^3D$XC@V-@(sYjtX{3Yy0~yb|Vi{J#6K z3cOL8IX{R9t2oQ(TF5Pa-B&mTMk&jgtJS$h43jy>3NvS0QPvy<$MU(R&#|KP1?p8- zR#w5-r&KJ^9u8-3>oDz%hbE16W%o1f1uKQ>CzH`aF>!^TJ zBCG`;Y%1ejyjd zGw=yHAaoUSDzFN<9`Zlb0>LOS0NxSe>6hxMUb<29S2Q00mZGK_ZH0K^*(as@P}@ZP z5`L9>jq6&Qt}SfAs#!K{{2=w_eX-F?`gu)Sw~$Bpk=nJS;S8HJOg+w8c4;F{!CwnQ zpsBRz&|aL;saH?&1&mVb`5;FG%ivML7mL4ycQZXv#?7224{5JK1C{f^i^Au@IrNix zt$jP?fjal@C7tD|7wgHV!h0UAgc=}tMH)odTZMN%=hW+vyMc>1r$z`qA~!^zd}?#Z z^{~>H7iJ>RP2d+ivG5Atk)HakyLHz6-f5Zq}hkcl~bg7a9#((D=F2t!3BtHh1|# zo4<0gO+=zU8r2z`aT4t`n~ z1wJ9*6KbDf8+!P25A%k|37x!r*3MozZ&zXpRVUJi?MZOfz@wk@ad7lW2__4H+2#dJ4MD@a<&%~L+8AJIN z>WL!}y9Z9G=`%q$!FS?1O2mVrTu;tCUGEw6=W>7+_ZU?tDMeK%S#DDeaTW6NaMiU z;hG8g9BCP7C}A##J@EpX1(7@<7KJGjgFw?ro}lCCb$^EZN>1`b%T1XSctDaT-PJ+D z&(&vz`>fnU@T`ZQ`!!boc`^MX)I;);gFH@*H6q+0mTcR_!uPWiM=4+t7$hqU*GaMyh6Ul;3FjQg4Oe8v54Fs=A>WIPt3wr04$(3G zS0dC%qHB~x((%eh`lUp#!7rZYVHM9@85q-0#3CV|IEN%%=xeE=wi0C(zwUloc~zo* zm2bos&{XKTU+0B7^0JTv>e{!P z>Vcnm=~?+#9(epAd+>>erK?c?4119Kp+_2i6mmgu3!W61g!I&0yh87iW^G!>^c3Vv3bTCjStJh$uKe#16w z*=T!@A638^^1~iJC`S2c=NICYPj>HA{)gUi&MMpoqmcW7S?DuIPu#70_9(B!bv}_XfnJ9G7t^q5T_zGv$SW1FzI%70=H&hty;cI3&g*A$#~eettg|VNYC=6|Avye6;sr z71uzbEaI#Z(@DZS5BG{cLN;+0@$)FFz$adZbC*@7T4BaiE6SX%+zdKPe%fryPoH7= z=`)R-4*4C=@yHjFG(+Bx;uP{W=p%tfB0eDx6Xs&jKb%W=e{&pV1V0W|uHWSr&&}kf zz$z2P5O75F8UH-3A-g!AxIW_NHCO)}XOV(r@-$=RHId&W)H%4$z4=M#9%BP9Ecu#{ zRaoH^_nYt>x{0$2975cWRg}94J+Qtyn@B4OIVGCY$xax0FRvsF6Q95;Xe%{2CF|M{ z1*`&ZIAb`IWLz7pXZ##(g=1f1_oJmahlIKc*PKmQ(MDqD5iP~}!IwN=;sYRj!@tem6qDN|a50=-Bf=_hS`j6K5Cr#P@u4T}9f; z{~@cK-rqi!1EOa>EaFTOT{(_&iRXKK-*pw&R9GK)l8d+4bL?bUY8tmnX9(l79;P!mL-eDXi^)yK0!{)gNS zxg6&c*aUrro+$7MJzrrISmxh<@$;bn>$A1RC-ipXvzP;f7lrwVut%q!-Q>09KKAjX zkb6SsY0;sjHEdDeUTxphI`{5iy$AQOw%uD>t1c~c-fM6-8#QH!EnPp)rZ1deQ|C?8 z>;Tn3J^7gYHF#`ZYTU@W_35o>*s{5_8*(*p1OYF>5pW5-@bFW$v_D|jQ0v;Sw=@-g z?>{_(Px)DUsMeF37tp?I7x`6rccUXTYz{}&Q{508$Bbz5OK2a&8E#b*ln9Opx4PAShrKREZVpr?@g!P6o? z?%@OS@{%i}?wML7Ry3AN30G|8TkCD#@k4eg@v>b{zHFK4r!71Epygz2v;3@2f*G>e zTdXQ~gCfWS(FZN4)p2i1>^#&}D)N^F;d)BmV)K9@oJV5yHaVe}ByU0BE6JT_6?u2P zC7#2nxq7}P$|AAP!k6M+*)UgAo;6qDoKluGTj5ON=fznw6s4In6y=#StSn=?m4ty# zLX>38RKOsd!z|?)v#d0Inw6(dv(mH~L2XRhR3l0=rdmn*6e~`fY{jWlvC18~zayvxoq!^#T5XC7I6h%oBf=C{3_l=s@{rm^k!kgpo8?XhuKwd|= z9oI|3S{!E+f8H6S;0AR$Yy+NYCr7?Yq)o1h`1*G@=)z@?QRUPv?~K*_xir~Q9&zVk2NLABq5umT!K;h z#;%D=@TP=)RATv_i+$|+h2D0P9MHMmvABM=*L@@5bPr2B(^EPLS_#Jl`!yr}RCkLz z(L)iB#=`OOo(gh7SB?kQuO06m=7xej_KC~Ky4lq*u6)^5ap}u$3g;16MZIYw^~(3? zDu=oU{q-ZcAaXsY4|Gy^zUS2bjtW2b6-M!ZKb%vdd(I=#)qAS27v+=_yE@oIk3DFQ z)q2Dpd-`$v)$f08Kl{zk>?gnai9J;70lmlRP2Z|hYpeBKEvsFxwmtpAQ}$Z>7V5V` zu7$bGQr9@JO$$n;oYz7YLv;S}Z((YLPuh0P z&<2j`YvX5*u(g|)*`|*+DF5@D2Y+J^Kk=A&%6Z|1h7A;GLXSRM+n%iVoU{)7B=7|u zSooo1&tRSozLS@lykcV}O|(8k28%!b+dux3J^0w;(oVRqL$|Khd+;Fjy(d=#tDw!m z9L^o|l!JxjUkU4udk!3+pUbmd`}Pr+(5sE_f-b{*AI*i_3f$!Hcyd87OS2BG6fL@S zu&x7pi%pm}$jkux*fL*`-uv{O8!~p3jhr~ae)RMIuwOm!2YH0)2SY9kpH8RVJ#56} z(YA8q8k@IrzVxQfeR_(c@a*vY;3K{}zcYUe@=$yyc#2#YjKVQ>P1Iwfq44*hMvA`) z*U^$-FY39ep=Mqpz4&G>o~zoR*~{kGlJ!fau`F7<%GPY&s9X=e6j+5?A-uM(scin_ zBU``aZ991SqyknU4@9nqd=VZN^c8dzxJI#mk9@rNZwXk1_3+7~cKYghJACGtdet2| zal|esoU`QQvzDB6*iut=TUzo~%T52>3bH;^?QL1kTbh%jo-52NrWZ=sf6eQ4?qQX@ zrB<20EXetUS_x~ESzwi@-!;Y@IdkO&@th5MM>IT76TR;Gi1UlD@|}eKS7#OO#S0iIZdnrYba6zW^q>W zbHDE163?qdS;g;hR)I}m7I6pqNzyp&v1%WFNrEkcYr6jbgHhlKo{2qs^RBOzW3GEn z#u>%+5@!*(LTqtkjCjFU*dcn%`8|<1M!E>KHfSQ$-uDR2tmobp4A!hV!X&`V$yc*Gfn<7mX4>?RIzUV%vnc*S{y zYw(D(34G$$bR05@uQ68nGSE??tm3@F8uQpPpD>(J=zbJ(J=6igD>@E21zvGRVIO}j zY!daTM0v%JeRVEjbyk5>+*2E@?d{YIjk z!t6iv3UVrN2{|8n*mmmK$r?0mXiwLzBh3R=fhowhJ$f zx(fV4t_Xbv{(yC05cs2M%NEvzy4|pzhrTHs(<_%cWPTp3@@gCIc}1+!b5IYPwqS}4 zn=nLtGH`T%>oc^67-i_Vfi`q}f9WZ6mrt|R8<$(FPAydX^i-s#nj940^LGzDY>zxy zOAJDO2Nn?rO1ewc^r&|{ccLrtw& z`!>>1G-If5FKgWPRjc3f6??T)EA?sX)W5s+9Wg+f$ke&hZ1SwBHfZD!Yukk!S{v0~ z!I73nK+7wy=EQ})@O?RM(o8B0uxw}hLQ?0V8UyK?oAUA?x~l5QTd!n{u{Kl6R{ z;-HR&T3qFRBCO)|K2_8myPlG}#46POfyI71;k-NaqP@-%i#t2yjy(6ZH z(OJ^Nn#9gW2EKZ9A&J2oPT|!f_tQc)X+$3 zp6j#1=L_JV#8JVs*GB|Q60!*l;>W%^m$;4+aEoguA;ZLUl^@nooLOKMXB1zZGdK_Q zk-Hq@_r!Q5qNg}}po@Gz`b#H4NAdf7jWP;)3K6i%-DA8fgli}@S9m4LDmBl4NI!|P zO2WyW3TGDQ6z3MP%84E^ZgJfNodq@_-$U-l_nlFEy?nHb;^NURcJXM}yX+G2tH3E} zD_ReAiTP`tRal)>Twn3k^_7$RIw;_fsD{F^?>VFR3YVPP*+FrymU5R@*#GITekNA= z-NV0EEzk>%Ua%JJTZm6ucW$HJZ*9A_Q{NTxFnCp9mzQ6uZ;fGu`t|IQryf(CGJQ|* zl>Gd+zg7+eZ!Da`d(Zp3QJ+k$4|ySG`B9h5%FJRo2Y(8mqrFBA#VBn$cZg{=%q9x@ z&^@hw`po^I-^-(Q9#KEqDf7q`47E{HMyNM!zu~=X$k+ikeBwYGJAJrKoj=Z&teaVQj^Pa}ft8Cghm#^-$+co{JpR@bS!V9wg0${Lk!V zi>+m+_WC*0PSX4K{n(5}UtjxvhI=lja_BymI3k@~fb=;9bFU3#YvK z!F%d^>%Nv)4(Q0C7}LNpa0-4Fdd3|&b5ixp^mo~F^pI`ax7$u!I%DVJFWJS!I6Hgo zvYowr$>Ni5SVD55UAunCt|gwg3s+89PR?OVOW9$W=^t5s))w^&Q9s)Jpf`$`CG3sj ztm2$f5qe>x+KTHbQBKjZ*AwT=6??cI5@nMptH2@BM?A-qeIHhFX1UJ_r$kw$X09j7 zAi;6iV=Zl}&RsY0nj68RAudxs>D07-x(^rHLR|6$(o znQP7UuDnFgIroP-o@YI4t+$0S)`#!>&iOF6Fx{lS5gz$HR)J06kt@d&HFS>7V!59a z@pX8`G!{m<#Nm{>tYUn^XpF*m@en@Q7>`$A7T6@XZn=(M#obTYMwld+PmEI-ja7_O z{G*>$Ebqe_bM`J|KOkH8GmCj^|NQ)4lvB`E=mpoaUr&imiBa)%mO>G zhcR`+@CLdH^*=nX$(Qh~=2~(fXb0pvU=8l$I`S*{Sg6&-3yVHO-h@6Qum}1J%o5|i zFZu*IA3g_r2*N8o|IuAwCq5I5fX@VkfZSs za1Fl|yv1+8cZNS;4}2*E^-ypMVf#Vbo&k7NSf_VBdk~QSp=KJ-3k=5I&Us@;=w64^ zT9X4pcbT*Fb6N29GWqh`)!G+s&E}2jSAk8wShGqNt@zs8OK8=`@4fsGjKaP`3zmJM zz2%HyHvagdY~Jys@(HX0v!Ja|dxXwH-~Qtl&T8-54coWK*Q$p6AuPufZeNJ8J=L#IL73sh%j0@tEAE ze~H5{z@ywO<2^_##SaL3~c`j0poiJwXQbLEq&v2tJUp-y+C$0}Y9 z$MvVc8*qWK1e%CH$jRtEZjQ*#J-RVMqdwkvSmB(rYaU)7uDS@ZKIdWp*?v^__9FI+|4%NBkc(~5bC3niG;|b(>s;}m0*fjx0aeOJP zYwpK9vabImz%CB6n1*8GZ!t<`ai->h?EEu)Qk*VTm(!fMlu?IAU=3aK_{6V~I9^#} z6-MP3YLU;TJ3&3qnIyS(Hd$_*OVNN+IQDBLepX>_jAA-Tz2nJb(FpRVc&rkX2ZB{h zOR;>9aY}IRC%`N5QhY2{E**)MVsb!-V?ADhQ;b#M6|c6()l&TVAKUYX(HOK!!yY<4 z`t?+AExHQ&N$-K5Dy!fR={vZe@(R6c3352F1&l$@HR_S^qR>-?-m>^s@SD;LA3i`| z;h5*+YYpDgP(7t*H`Qj)XXuB*8vXU*n1&xT)_Q1|g>}b&+e!PlC1k|O)VY&n*w|d@ z+P9O|`b4Kj%BU%YGHKR$-M1!XNP>)=Hd6cAv6ld!nOqaRgujN`B=T0)lMYSjohI+A zHUJ;M1!x=W+e5vr`B?F*7_0D%hE>Rizz+B~xDNi{enxuOaUDE@rUJ824}1 zVw02feev~j9~uN+Tf8gQli&0nMtm>qAw<3ep1_MTYU)^tN{y8P` zk93zvT>t2ceGyaI>dvxQg4_b_MwLp&_&w{O*8FGTz-Ge2LbpKZ?4 z#j@zDFJ-}!rJDCKe=WHm80F-p^K$umnQATckE2(A$@Mb1T6tZ{s;;TN!tsSG#d5y% zvYfndQr2%=F5j-7C8Zaa%ahvG@(cSRlLx|^@_@aJ9ae!?EZ`PAvG`JgNAtnjsGQ}4#ypp zQ*Jr_Sj+L4c4GR7Y9!S9x;lxSH&!v$AfMycN#GOb9%sB?fg|w8^4y0*$f;PLTH_bi&{kjp_I7U23g~#VvoMP zG_S+!yxa7B)jQxDv=4L{dbbP+579of@C*4M_9P6C50Qzp#!6yNjAW0X&LvjsmqOx0 zW$5TU88$9o_xwrE%TP^$JQbf$d$@G(rc8ym*7gJF_DOH)-n)-JgLz)_%=P@VdAVBS zg2w`m;2DjEK<|BOf{ZO#BVUs_Y^Vm;TaE-?;XZT+7z2)hL3kgY@h}PJ@OrQZ5&XkB zUX#};7&}JyYo^C5e1i9tJV|U?vie7ovNI%q>?p1EVUHtfi|HN1Zw&*&ipG(!B(>vz zdhYM?hrj(*8InC2sfU7L;5l9!A1(FFpMAMh3dW7r{=skx`6|3J=qkJ}xg)+0zt@1s zFo{fxS51iT!)s70HEi@K?HxxC-w{(MD!Y(Vqkk^_Uf>aeUUbw_<69Xub&~d}v)U#& z2JS(J88vx=?lri4!y1`1Z-(n*`BL*jYc~HND>rSBFTYzWi`T4_C2PNtl^eg8^*?Tx z^*?QsZ#I7~-)`S38-D&#)@<7(U#(lKj6>gBytmfxmV6O?`C%3EK4(iWd+btt?W$b7 zTA~aC$CTf$()|*PuV2$T=S$_6W%;UkQg(Hl+`ILi?oC7A{9oxkd(Y{6VzoW~k^Iho zx;q z(MU!+c^6;Jj^<%}zF5oaz$U71R2J&mqe|!4Si(OVZ#ZkigGT2*UXSZw3geFY#z)+b zUg8GVIGRa-N1WFfD)+DF$vuDgm?L=ZFo??^{_$F_5O>Ols5XK=;y-W4j2c&Sg|IrE zz;U+J5?6*uZK->nxf}kxj&X>NCD{@jwXBt9YTPc#(708Sp}cXcIK58Xyqu=Tu!sfM zux?RxDOIY_PEv#E0ZjH8*bj(*~C37X2e zL?_NBh{ic`IEhm3hF$}^_}0o!CCJs2@lKpbkUGKoiMrldSj0HRSmnxb@<2|$$Cw3% zDLEdiBMd^oAj%=8pSWydJ{4HTpZBpjJ{3n#aq>VH55{UxlN=oMQF$iZUbQd^y2+`c zSPk>k+6bpGhfhxb-abFi)cH95-i%MIuRi0Eo#7hP0jn<3`D5uHIzSl(PN63)9$IRC z*aPU(L4746E>iUs@-Xyxg;m%;h(7pm2c8r(67n^01%XeKXW{d&ysXc||M=@)vZk)iE1Bc0pyu@CZe;v`u1zupDr^$RsAR< zr{v3|IpZ}4G$69S_HfG@F+}=B4%8kj(Mhpdr^K_FfPp&q>>)3`j=$LPlj14u)V+tk zC%y|b5VQsK0s6k78^9IhWq3}j*LLtAT z3F`zL!@bShw$WZK?3YOGbBmALDXXBd;AdgKfkq!T)!udZN$`%qBpJhURbL5DjFwNs z2IxN6EjzT+ybiu+zB_*xYO(M?Q$tSPhrb~;U%psIPoJngc8SAfK;ZApr48wkir4={i(v53!=1Pe&&DE+BIeXzJxlz7J`@lb~U8;3J=qGq(3H8ph@1I+X zY#aiwD4*P!p7R(V`K-eVTkN!(n{uQijr zx?%IbGE!gs>#YE%z$eBm&YU{qvGUXnSVZ}yYK*QqYyzK*(d!tkkBPAgx(RD&B-X>k z?{%%iEaY8WE@6%?5nvDa!y>>e$~7*7*m;L(?ApM1zcQeO7^Bq3EygGhD~36miL0MH z%PVm!_2z*i>tH2{yPQ>Y0 zb`sx-!ySxv&iKPdKdZ22_xLrI0IN8hf?k4F0;@3B;~-X14~y%gWi-!<o5zf za_LZTc*?4jUrRhhlRhNND8Z~dG_w(N9HF#X$l?ESBqt!>> z2QETq;kC(4!9(a6Xcy=z_*Tf-Bxa{7S8xu!1pSNeIyf>yYpu|hEEmLi>aPYxge#-K zD)?s5k|xfaq5Bx&i^VSkk67IgzXy5`+7Had^{l~DmPdlq_^f+ga~p$1iH)V%a?QyXKzo$Qztb)bhh}YTq^!a?o@A; zhczqZ>D|w@PaB-_f8Bsfm^1oy6Pv>&o{mC2558E9smdboh^L*v7_N4r>+W&zXqt+# zi5*ib><{A*TjSW#PaNF@E(vHUFbwNxD~^WZp9gaa_u87nE7a@YPq991PQHh8kE=#1 zgP4|LIUUtKsvKT1{&4gXC-(v~7?1pq#>#xrsPN@+^tFa5k9fRNF--2?7%C4eD)RKa z9diyQ@z}-X6L`f5Cx2s{0&Do^!R%qIa`*ZWxpOV24y$k<*Bhre^BlPg*Py9fL4$#7 z$oph_BfR3*QP5KCdec?VT*&(vyTCN)C?2mA=ZJ<+SMf88pHG-u7{mCFnWLW=p9IfM zTd@&#L03_Radj2)KIClR9M)hN;~BVwxyL6kipALkDP!)_QT)1!X(~3tEEaG|>G4?S z*v4qOr(u!#Csd^O5`R`KSC;*?Fu`}o6rE3gWT z;*CX7Lcl2+)GNEbT1Qt2^jSIU+XsmG8FUw|`*CW2j8PbkQ~Ybc#VUSng*n;?URZ*@ zDCipl!Ut*Ihk9jLg+3}VNwLZ*Xeh7^z2>P6q0X3G%pd;x7y0);{ZUy2Rw2+s@Ts7I z;IoBWtoJ^1nBdPZ{9XR>+AEq=7QKjQ!XT9*T5%RBEi($_{0;5p7` zrwXwXQ|3-}G_p9?4OEQTZSu8<3|eGEAVOYl0JR~=^H0A&@p zgzNF%;`!h`xQ`qWS_}8`Y=?o+b-0)3JPgEnj>)}nAGs%<;R%^(lA4<>d80;X@06_J zxl%ZBoc7_xmt#I+xC9=9TVO$QK6Z|L7=I(q!BxE1`03LmEGbEbjUB7L5;P|K!!R0b z2G8*wO^3qcL*9qa%lD&x8|?+Xg?l-NR|h`9t3y52d#zfihbL!5zUH#<8&f+3x6sR< zS}}aw{N}v|_LD&|Vah4-S=3^uOLk$7?gc%0-W2)l%Z0kP0Q(0lUiFnMS^c%lUba{! zFPJOymwzRTSAQd4ZTMCh1w92``F_`r^2NH<>S@`qV~58oXe`qg&X=uwcgd=a8?-(N zpY4`iKgo|pdxYL`aEsM6E5DpPp}t%=g}S2iS4-qv>3J!=eo{`K{Z4LNTPyc!mdm4> z1MIGloR5(s0D`dCE! zwmp;IaWoWT5=P^a-x^^RbQHqzri}8m5MvY@VHGqH)k@S40$T+5!nBb8$q0wQ7#eO4 z$8-~qK`gi9vWVt&uI0-88*W_B^Kw1#g~uPJsW7_Qh@UNtO^i7>S0_wQDbE%SxWo0F z)Va>P&t((K>sZd`YPQ2DHU{NUu7@$F8Pg{9&VU$?uKE zC;rixgt0zN#nw0<&`;dD9elFtpLKIQL8CEB8CuHeIMq~4SAjiDTY*h1Ogn*5U=s^t z79E4~J}?UTo#JCL8UZdL=TkTDV|-#-ihs1+PkpR%;b64qRk?5=Mzs{!#B>w5#Lp@o zm+X&{^ZTPcR?+KyIUs-CGW-IcSPqE(`y9h0^sQwK4&xMKl=?u5=uM-;0el`81<;PC%dUOx!e(c#!{Rq9@@Y8B8=$*G!Td}+fc?a|rScQ7n zMjteFyezN0sQWYHJAqZ=(;e?8uZ<1^Gw>StJ$bfrFWLvsEBwBFLVC-{$s=UWl4&w$ z@eJMTr)R%UR3k#~fvI?|!4^D6VHLQE+)uB*pUQJDy(k}k)LM^uRugE&~0F{1E5(d% zWsCG2d7!VqUnd)O?oeOuyk$$(lS@xIdalp|cT3T3<(F@_Zqjk(_v^K<%ch@qD7U~a z^pVrPai@;Uk#i^Hz$vHqE%_pP^7 z&nPxG4dqvK6t8Z_(Nf?H*u$Ue31${$k(vooZ@4)h^Tj^HCAY`O)9SJEs~f-E9^=K6 zo1=98^wwy3OrVq89HsMzx5mnYn`89&Vbw_0MbJi!Rg6VAr{^n2Ywr{0@Ci91YJdE! zqPZSl9glir-5d~})+aX{Z%MtZQZY>G4fDQwJ`;y^JQi`8#I8dZ@i@li6lX10?p+_^ zg))nIQt+d=_j$UAo6FJsP5BT{XE7EDu!h4bcNy`V7^@hg*vL8eTFd)5qw7a;nWW}w zwp62|7^C<`XYTY#F@Ctl#IgtB-PmD=|S;c?OYJBWkV-)zq z=5<)b{3ySX3o=%*(OAWpr2K50Tss$EM^jNoITb6!m6I_JxA;9N=1n;nUq?^j*f_=X z6w^)2!xCT;M=S9k2hW}SPPD@^M`NY7!so)nyQ~6*2=ilWYZjK4v23CQG;2AUqv;cIA(70&TXFBxk zrhU;`c5EX#g+pcZ)bW~&;Q0zaz!5MBykXqJGn(f!IsyStz$|IGL-d)>GaIJiISr$5 zY*Kjao8VdBxMg#B>8;o0k1sghS@;jG#5=?s zeG5;md25Yh;1qa`zZdU`&v(My*^)DQr1o?(R)I^-=mGiZ z;4b<3;2!PAkLGgp%yBt!;iMe9cwE+R{aOwkT`u=(e~?EtOXX?JJne0-eNf!}i{TT~ zQK%DUZhWF+?PNz&aq~Q;kvy%P=!8#K@${0K3F=G1o8k|+gk$uRN4FhMEX<+W$?Y-X z4?m;8Cc(jda7vv(Q-NK4tYTc^F^9`4cKo2SP{TW>KMMU&Tqa>eQ*n3%Uy19by)ojy z#VQ_WROE?<=5FeGVvSE2VHE!{%u!!>dWz{P?r1uSe>C1;G<^h(#nV}Wc*M@3#ppf8 zCuKRx9`J{*xh%t|YsM$8C&fPopCezxc{m0>(QALBFFt-*Wt7r%xmA+pL{MJGcm&=E z<`82LM(c}WYkn@NZ*Klr%lVk6)<2qe)<%z0)Q=LUyy9Q0k5&A9;-4GCn1=FOtm5~h z_;nKIFp8}`J36`xoZ@E{>XMaN+}uwcPI2p#jaATBlv54_e6+?Zu!?zAj7#7ZJ8$el z4oG{_7Daot$?9EkH5K^8AI2j#8k;z|p}MSMeN~KA{G(rAfj4;e6V&kXoQGGa>tT=M zjy*bSzJzT}SZ zwBY~cJHcZx6`Vu9ikfOT2?nBvE?!@B1o9qm3V9OlbG*YJDmR&rCnYak|1Y1OpQU|U zVHGqC@<;T3`*h$S=`&!U))4jXKR}~@NT`HG$H?G_NEs9!AxY^O`dqfUW_Y82Xo&ho z@U_BA_(k}<=sBL?tCjuK@$JmYZHGc_T;4&{3nbu8Qx(cVQnP*pNDD{6H`YdJhcsYJ<0= zRj0PvKX>TZVKQ~WRGIPl3|Y8*kp_MhazX3`FnR8Dt^FA}eXIv;Ie{D)5+)qq!yT|6nL zE+3Q9@_kZUyHjebR>gcV@k`A()k#z@arF>uVQ-CcIfN12WVD8{3Cx0?V#m()qvXlW zk-p%#Zal6k^q9rGuxKGR4~zk?ENjLne(x)s66`&p4u`s5^bzJ>U5@K7VI+U^u#((P zAXj5Ko!=Y1e2zcI<9m(Yn)`JYo8P^bQ#V{jv30y8wpLq~rE#w;+lxEp*>%FMVN@TA zJ8~WKnyZ;oTbiM9=Srs3l%z}bl?+|Ck!x%Yf2il=O28v)9uz;1Ji{u+EG21jvpCg@ zD(3E5)#ViB7G)IIkK$()V-wR%IA(4m_i^m!5}TVJ<@)(&b3JG&*Uva{?R2cp88uGD z2~mDBwoaJF;@4ZyTuM(wYcSVqe14VSIUG}REXIpsG?Syzb=EH*g+Uw+@pFq`OJQ#8 zVtR__OL2R?DWCXrK=4XXoIl`rQqC1QIUiW&{C@UQ4%7j`BIZ?LgikE2?^}JNF$!z~ zr<`)b){R}@66V4Bil0$z4x`{x!LLGIg*jdkbQ7M}oTpAFG9gxdt!OHER_OOi?u6%` z)sis6C}=0}i}_-C&TtI3z#H}+ydPWwtKcn3%1lvB1%BfB$!j-m)?7J)nxIC_n&{p` zuoC+wcj(z(dJXC!X+u-Ak4oFFZM628YpvG{--S9|p3ATZ`q2yjd|5whuYP^?Gs0Hp z&7eM-&(H4xtAxcy=x3uYDj_po3MLGfxpde?zJ8ZE2zU=#m7Z|ODT5S5p%vwBKW-pyBL&pu3nDkgFoJ_x#G-=cAV`0(*7Zmn_&Y_fXGCaqtd^x0hHn3bD;kTu)3D92FO zOl{DdWlQC&@7BrW`SWDl?3prZ`c!3{E&KM!*8RI=D;s~_s;n~gvzfB^>!q^K zWfb-dVjrRX#}2C2a_Hm{?azPe(h0dxd{D~DcgxKiKggY%E9KGc#qtDRp$^EWp#*zn zf4MU$pqCvPEvP{X5z3%FspD~fKg0a3FefM?)?s{ zJgpj~;jl`8P2iN@W|g|U;&99e&+8h@DSGVFMr^JKIA>VT@z7DIvDCMVOG>lWqPQ*OJD#j_sEygO0-u(ZARg_mA>tPl61ZJ@jPO&gfF;-Dc#nn@6^y@0dD6oso8ISLY z@R$TPA*cz0SAL6CPV9ysPk^bzW-$w|=zW^l|P-SdaN2~%>DBsnKZ-g&>FdUcz(ZY9lH zwNwTnhrws$Gw`fNJ3+7D*@?!&`;o6gPl3bue(+h}kbzqBJTN9iIc3(OnUa#1D%pj( z$|$)bhpSgbYj|3=QVobbfd2N67v=SL8hGr&-a@U~eJm~8w$nX^K4{TW*}(Ef=ni~O z-rsu3kf(yB@V3Ao^6o175%ff=>tTA?Jmb z#2hB#?+Takcj51+@6Eml?LXGvgZdxz4f><-8DI$ZNTgpInhN_44vve_eF)(d_BP_X z@R{xN;W6SpVG!z>UVHl;dF$Oq`kv@La15Uh_CgzivGPV1%Jlhj)vrQdJAA^!$BdRx z+k+baa8`=eq966YtDx{Ao4$W zRv2Lt^p=s+rpU7O-^#Z;wrQQvm+QaPToJkp=ecgls+HQmpJOzajeCBS&2FHl>^pu? zb{*a)KWyJ5Kkofmb{{E{orm_yoyENj&85b%bx?#v$s)fjV5JtO8jaeXF_ zsMiV7PT&%cEsRAf@{}oT^fQIdjYZtixZ~c9T&=l*S2SEtN`OVoqv9}$oo6&IVZ2}N z1ao5z#^7WBIX!UDatsmwh}y6UqNSav=-x!`bJMv z(LIfw+z;0U>nNtFprHgudJ%T7jnd;+TwS58Gc;qr)oG)=`n!X5rRkbiEPis>o!jm9d$Y{D`8VmzXA-?7Ih z^w5V<4o1r5gOPQ#6xC7oN9xF2kAqmnazFS}%%kGhRg6{8Ti_Bn<#>^!wHU7$tH39! znfO@6^b{Lm5u5vSKju;4yv<>b7HwK9JMfGqcLJx7pFsnG7YOu~p8fi$t^qf|IIY~h zeyH<7vw&03PZ-fD;0??Da1HY}-hD^@`Ocfl2yhFmLO(hfgxrq#W_XTrJ+H&PXguVm zoSalINy|%>VPl3$LROrl4@;HgA&Kgzr2jtr@j;6Z<^AT(yk{}bWL^_yp+=WyFLlAZ zCSGFJl#AN7R!xRA_VT0WEBT%&^Cs!Oh`k1XBE3WVO1thI)eA#T2Cs=}DU7ea`Ih|i zt+zEAzTZUG(1&0Zj+wIu4vfPyozFtAyr1jS<#qV(G06#9D-4rOn>SZ=5B39q zQ|LR(m|Zwr`2|j4e*nH0`~fpj4+H~|e?voI4Q6ZoaeL)OzAxX2&xyYl4F=9xx^jj3 zRnqcuv_`pUn^yX_gE6^==rN$T42%wyo`ZVJCn29oNL+;U4IijA>8(0`tUdebQ#*Fn z49Olfl1I+W-MN$z*G~Z`tzmHszOXyLQOtpSH@j z-P>j7fn9Rw=b0(o+KrsGAS7mD?2bG;@oi*X90 zpHIm1*sP0(e!YcH*5MOBqZoe#MmL{BuN0fRBaC5u5*Xo>{~oK*zk~?Z zN*?&UC=RzcwLR1ko5#dq7l%PC$7A{l%wc+nuKDy7|J-8^mqmwJFhNI z)3w{BX?ne%Pb{BfYaFY07Djld%sd>q&d$1ogJ!!d*XCw^8 z=U@&4;JHN~qCY?07g&Wgp4I9V>D^uXyr8Mz(U~}Ff=rq{Q6|orqPI2_k^d0=RMD6*aJ4P97#x2q;d!S-}p?_NaHnW@Ie!;xq(gMQ&S{{oD7-;J^CXf zRGXj%8I6Kt^8&NqB03CxS<;3&bu~F7hselj<0OB=DAhS&vgYmEsXv7tD`+Xb1`pI4 zW0(paf!zpxBQzG)$yG%sCU~D2os4~`$%WyQjYy2sTopMcyudT&f9B+^+O^Zy#7pz~ zyYI?djT`HJ*8G+Yn|`359q+C7%yIK%XjQOV)`)!FD~Q@;yeN28U=s2;j~|fThaAt!?t^<}=lY(BR6ktk(-sP z<(HZ{^2_a++LulHvAMd6c~G8Oj;Cgv*5*8|8KXg+&cE=YxW|^)c~U)EBUn2@Q-M{U zRF9O$xADIgI{BU3h4SRqhybVD8lmT&R23?J1b77hNufNh9O0aE=iV_s+e-Hu_gd3U z7(F&|`FF();_FsGd?e)YD4p*G$OE45@QYS;b`;m;+v6&Ge}LLQ@BNK{V>Yyz_w zt1x=J!u)ii23{4|Lp2oR6ZDkR(NcCQ%F|b1m8-|2)v2qc8mz zZ>ygLZZdA8$4pMaP{|#UukSN)<|LW6V5a`xo#!yTKo5R;>3-0AO38D8OatnLL?Veg;F_&Ci`k&A&f(528`@S^a%hC_HJ zlfR;l2|q49?(v^;9h!#uaQWWoG9P#DB>kv+rcYd0sD#BtOF~+z=69HrSAv)Dy-<^k zmchPM>BRneuq9bKsOqA{^$li^xl;w$F6Z)>; zUtvy;hddM76?rcHjdbYRO+P=s!LTtSwa*;-5j8)2k5}J$TYW9`z=OdWHE*u00FQar znqt#U9#@am9FNry8=Jr>tOeM_sV(Lh7O9)h8705C-dWh>F?^!l*ip(Uk8e6!32V%i zU0{`Zm;*Mkz|ZQE0|^>1_L&h^yc7^`@7JeIR@Im54& z7;`vdjCfz8bSSMjajU| z=lbb*&F_?*iPi9P3akRBXfP*SR$&gWTsan{>;kJ0<}V2zi;qV*EOIQ;(^b$>&`cOF zAB}J#SX=S2io+wuA;vFtS>@89C=FNyUNKfNHZdLL{J{t(Ea&6uDs@IT4+NvIezqt= zP8UTge;AL{HyX1rn$AN0M{_`XT+ghJSBz88PFOb$#XrI*{_wL38UxR7{HJ(Q(LZ1i z_yGL_9pvRVUsZNs4?)|*kX#8m2|R)B!pMDgzrzGwrDM-d(x!7;>HJ9-Y1Rh+W;5jz z?&UtbwVw`VFXe&CD&%j_`uh$VAniJJ&~wxY8{T_7Cb8)?bE=&es}e7>a&$ufF|;G8EUc|1dhz zgxQm2*w_)83*mW;&ViQFt8YKm1wQ5Jw{%Fry`mxsP>?1#*LjIfICeH?P`?B3;5VV4Ea&jXplOlYg1^vr`0Q{N zyk&i7&_W0p4(=j1g!W`PA2b?Ri2M?HA~YDzv1a$-ry(~6^Pv0SQGqGYK+s5xS;+gq zBCL`3VT4!U6z+jb&|XGNnWSs@X~_v$EfDNN4UyFm;cKCfKl;jdJGROXKktxDJGaT^ zUE3XAIlND6fAG}8Gt@W#@Z&aFzjc$c3b~_=Fw4H3^6loe^2O?fvg^S2a`w_*sjm7? z?o@pv_ioOW$G4}+liNc)daSi^J? zV-;Ih&Ep2lfiEP>JGM1PW5M?ddpI0Y-~2u~oom_3Bz4AX8FIHgT^Nb`*E0jhrTmt710Zu_bflC}tfmMhb7ZO}$1v?v++)xNf5&7eHh^re;9`_UPD_s5f#u;Tt>0+%JE1IdYAl7zG|N{lsGv-`b@k;Q@AmU)(@rvH5>@R8Bb@rHlfXpsR3fjB?>Xgq+_WE*J3B zQupKfRn8p<_jDH6gfLEF#8-Q=C{j)_y0t&_fID6kslghIVs*-}iROOxhC3tvTGwN1 zyyDkSj8#lqfln;wX4wqqL^JL7lQR*kj8IdhXS;?x)@ENSG z1`UXyA20c%h{SN+D=2s55M?Pe8v6+#4p#jHPC-XO zL;1LK7Y&$&bw<{>7T$nW$g>QNj@Fu8Gyrls=m-SAMgHhQ{oLqN&04iqJ*HWkHu7$> zW~w8gF;Jt!a~$0Sodm|=yV2W@S|WZsz61Qi2vd?zBNxN7onE`-P3WZtkHHfh!zuJ! z>(HxpNkqK7>v({zJuQet%-AJC*-O4{b5B$_y)bk`r>j7yD*|b zq3KXpP2L73q3#C_1Xh7l;1aYGn1sO70*{dUfl;`Yk-QLG0;g~ec0s41-WhH&c7avc z=b8PTzgY8)dRM;LxK6%YyFym|@U5)eutvEBW`R|9A3h)})~}Tf+qcM`BL`Ji*|~qO z{9Lq4HJIfj{aT96VGjaMfmdJ?e{e3CS4xjXc&uW~LcWOk z?=gyTjPi@CwWvqM)m)5KZ0)}@n&;Md#dH;zMcD<DaTq zwC~nlx_{bTJ`L@odmU4w%IRzvQfR^_=p| zCvSxRl)MG=sQ3i+qIBumOBn=T3QU4mv`vSO$}8w8_*l?Z=(R$QsDF64gvG@vpEPgV zRyy_SB|ZA}Q-22e7ix36_v@q2cDx$&mZi^0+b&(CU6<~vg`g?GHFzro_{2uTMEN1kJyLjw!hH;0D#w0cxhxm{0T+7h7dp*;cyV{FsEH?W2!{&^4%hQ4)-O*Lt zoDjUh+_=L=;}7E(JHB@{L&J_4xyBpI(mXaXMzOIzJ|TAlR~VQ4SL3bXL?k<**ROY5nY8jRZHyVf7$T_qU3)4{Q9J{&-Yp3>wE05rtH8z1$lvP}2 z@#a2l#p4rKW1+W7aPK$E_ZYhb*u-I!<9pC+qMupkI`{%U!8?Ms0&8$gZ4O>mv7gFic3s+Qo&_xh4F+Ee+ycL_&vL^L8cEaEO{GWQ9ugiOBAt4*mwsWNN?a;E zIMQ{WA-IIv9)dhYM0}L83*J`xu92ttuw4t~lEkb8)l&*5j*zT^OzGaQn{@2eLHdRF zRX<5mcA|9slzj$!YyC5f1A|fX0|zB$BADBFm~$RRr-fHdJ46^fx9;XO@hgHA7it>b%)nTa2z@{@&;R`Kso5$n{XO47ZTi zqK^yx{i$WgzXEH~d;j%!8*1JMUkf=P*o52;x(eT$&w;iAj}W|9@6f?oe?$)%bQJUy zt|QQ91}DTRQ>7N86qtg151I*l!U&IW4&B83w|H1MkFJ6r7p5VX1e;h7Iqqeh zk=Olv<#MfOUc6?d_QhSleTy;+H9}}C%hs-Pj>!{k+pNs8>(GAHRQ4V@tlYBa(0*NG zeS6U!*?sho>^`qSw&iFswo=eW9w@ra*Qyc|yutC*JrRxv(-GvE+ChE?Dbx7Ql1VLW1^t@)4Z zV-#C6Mu9Ql5MvGh+|C7ait!2S#vQiCJuph0aPvR`kE`oNarG0ll3-Rbjl&sX7bhnK zmjttkF^KVp9ou;KO8W1zig5`eY+~%f_*<-^c^j8OU=^D)`q{<5uIJo*4}4O6Iazt7 zK1K=V65|u&7mo3#7>87{S8`BPUCOAlR>2;FE~|K+6<0&SA6tGl)*YQ5`RFDv3U$c8 z$0{(1YAWVWVRW?=;}+u)^pmT{BI>XTYkpnDbQEJ1(@u<2?7WS}E5<4?iRmY3Ep>S0 zAbFZFy~e-J{J&xq&GDF5?fw%^*%CUVB9;>Lf;@hjx>Xqx& zRa|yCy*EZ9m{m+mVN`AjViS&kE6`KWRct>PJgKa)FCcy2&^f5p;o0wKF7GH)@NC4R z0-tC!ZX|CvYAA0sd{g7?#&1ifo}DBrIZVRh221LYWJ%6SmL8wtuXK9P@tSyAd9Je0 za_3%MwHGaW5Je`2%i!pNl9(N@b;Cmg4#H2+^{`WH z4t-Mp{^Co@Dvg?aAniMMQT+s8sQQLGc2Exnbvu9j>)+&+*WXYr21cTG8D`Pvy*_ip zwdWb05%M_H@A6C~c>Yqei-w?n(eB;Us~LQ}E&N{hEICk>;-Gse&h% zJP@80YRBOgyhN}HwL>rtOoq0?@5B6)e*LB2;1JcF)L#=9tGOlKkKYS!+e>pw*|dr`MNcl7h(^gpIuI& zK8V=3XNzpxw_Wxe`B{E0+AKQ{Zj@aI*2;-9-$_~7_j2>bDygYhDt9X8%Dt+ox{r^a zS1h;l3q9KK!{V8BeJ4+D71W8xH}f4o3Z4|#hw|uVzC5fNrss@9>WtRU#559eGwvM5 zFdlhyBTwT&1vNIgI(N>|GuF{Z9{O~Whu3rK1bG*Y8(E_9JIC+@*Z4;-7eijg4L@`E z=bSfP#557kKPbnO0*9pQ{C*i43OYxM&N+rxU=@F8-o~HD@z?NNNfjs3l~aEE*tyQn zBRaRb9yjk}IUL5iECQFfnu%#AK7P4blB7{pk|0&ZiE{IDg2ypj2dfaqB{sq%>Q_N) zQ6>raRM1gaS0C-gbg8(MDV3LUr0R067dMOZytq}8r%_d$Emd55F(cryy^yFH%8hez za_vlvl%I)oMweME*Mp7{9G35aNmMIgJQ-Ox++5I=6A`*zb|ON<^cBuCH&!t=@kWorgPQ1bkmn-j$g`lUpsA>jwMk=nuW1u$*o0p0 zZ^%3EzoV>@nwuyYLsKO_JywsKfApa=ZPiQ}g?b-yG(1xYdc3ix^MJ_y$~Lj7(GnUz zSbM$UE$JJIKeU(jkYx|(!O_&ihRKNWh3cst5HV0cGkn!&P=EDgXy3Tl2pJePK=VIw z8PPIy%n+G2e~R=C`&4xpyr^&hb;H!+;8|7PpeIbT=IWVk)wZ3q>)1&>we-tJFW^~= z_X3UMFV8(Muf6rQuG1R@odwSd{!pH;_+t|?GW7rdLysE%UT7=mEofe7V(4H|DRCN+$+0qI z;vs~D%iEXFFPiNGSP`8h>>ATF;ci@3dA86REG z(J)pq-Y_P4cr9Clbz8I1&mr~AO$Xte&5c)#IpC1{<@j1*ku;qL*d$HvU3J3IRvcbY zMsdBcs)zV;IWPvt#wK@4)9SEFZAq%G-6>6}6EF!J;<1Ta(_`!CB%C+Br22BA)D+{b zg;NrgLHvwjb3dz8UUsh|59I1Fw_y^OLrgDWG-e5$b3G_-&)J*oUs#+gw@M47rhKf_ zT%Rbl*C$JD*(9mCI#F(4ohUWeCOJ_yUT$6~l&a!k$}$xfb5x7DPA&4ec&*=oQ?8wj z(tuaW&ctZIBZP5E@Z7W$;}vC+Q{g%?H`XXU9xhjog~`?9;Tk1J!#!T9k5yn0<&|R* zdM#|iG3)S0FsrDqmK@NbFu8OvR9OXHv14NpIECQYv=uw&AN_p7-1HR9`}lap&nocA zGqEpR^%Yn}eYL(``e-Uvlgv54t^%uQUI_1s8*mA%a%NwgoZJ_q$Bw>2y|cq5M|Vbg zeB$R7KdT(u6XP8p-xaHDLJiFGufC*tAnQ%S^POJy=pOhVc=o|2jOZ!kdFT<#8uz^Q z-aG1zZP28F#=FfLNRNKqWb~8~l9rn+oj+k;o)6`tPHnWV7@nX$1z!a^K+CotDaWuE zVSHw+#HL0`NX$S<$c&L8BeFE#gC{pUK0;zs`M-6T`dFx2riPima4{*d`nmDDP;(p+ zALHr^oz&kFkrbwNL&GQJYh4lja``SiKhtuuwI2z6K+smm^Wd398^L>mFNItWJy^7! z#oa4d^Dy{b=_}Iv6ZOSX!-F=$GZw7`HX%oW=M;?v{@~e84v4y5&e6|a_uuK*!K-ya zlj$|EpXO}v$C4`xjf#-4=t$K{21kV}qon4fs~?Bohagu6PolY?KhfhC4TZgoCe58H z3zsiep9%fb;xkfp?_%;!)I+0@kXIQqWwL5NUHbOXnjhN-k)HkJY^Vup*{Orxhi*d8 z6a2%`$;pyAY?yKuyal869yCaQJ922`*6;);q^3zsVv_zw{7!HVzmxqQum?H`EP`eN zgRqwn@9*p>kgVtVJ^3vMgoa6ET)f03r|5gaSn2sgwGSM%#{^y!V-@&>e9-v0Gc|@z z7^6&se>QjQXw_t36Z+}XOD=ER808br!zXAhDZ}z4V?=>hQ-lTs^Ps)JJE{3YW%SIc z+JgnZ%c{*AwQugsrHgc&_W67r$q(Vn#h<%+)Azc6GxbCps1@3=Nj)#r6zwS5se2*r zIaVYGPw$ugC-%s`W837&sjYJS)CM_odWBS8TPb%c7RlqPY4W6!{Ray)p98BXuiPq- zr?(wF#o?8@+6w1AZh>9URVs&iobu>5xW!|Xs$tID&HX&Aq&HiDRUR>S10F$3A#4s` zJgmreM*q2MnWAw$Lo6O$cQqE%T6|gx*P??wKm)PhxI9D8J-U&p>+nZ#*fshZ?!EqN zShHjQ+}0S)$KtR{pmqm#@#k@15qQOz#6O2IYD<%(ro`cm+7hR3$Kw%czFp)(NqOo}zInNp4+oYJOlAjsra6XeYtUg7<{}ZScv>OF44;a-Q5O z9V7Q{Op%8-=j;9vk88e^hqso=!%8RU(R{aJvHHequg{h0vT1Vb$^^MtGDdD*9;vTI zpA{$X<7g_XtDJUvqSzQ*=i_G<V(5B#xnl=5FA5|^5tXU zQgSRzj~$&QT3O|atF4%(V*H{S%8@X+bSOj_1x~RrM!9$ZmI>F9d3^!Hz$0gTOk#Xu z40Corx{0fu?02s(3YC-lLgdu`5IIvsgsMm7bP>5C-~K=QVm-~od@9B;r}w)YQWUHF zVSECkD4!I?YG0NU@XFp;C-%fEe=wfd9j9SD;vaRsD^`x}j+5iuzdrzs1pB-q)ID{J5;aD(^OHByYXeNYf82Y!MF~B+GKf3kpt@$kV_H^qaU3>M=+8;EQ z=%iTvTk(6)Cl9R&{i#R4o_Y+|4H=OuGZxL3nV-**MJtxbq&d?heOR`7VtEhhi}2Cl z{UxU|a^eKt51GCuja#>rR-HS_kir6)G<$}gCuc)Xm3Lb-S584Ufl0#S<2C=noc#%5 zFY;LQlR;x@^g&bQK$wnw9YZ3cR0H96fXiSOG!util9WXpUWwK^@i!VZQVoXv7}-aN z_i!|*eyU^PZ4Qr#m5A6l^|??plvy}j3MNhPYLC%6$n(GxXe(o9PnU6XW~lyx4;L)%Ga1XW5a1cC`Hhie$j4AZi@cGVO zwpasJfm`UKi(WPNi^a+<)7#|KsWo!#%1Wudu~6<@o1t2Y?ZM;dDzHgCdWy>>HX4)oS;h2};1MqI_9?cx zSC8Z5c$7QH>$v>kA5~9r>udbnVRJMNxB)gXPO*`5^+mm`V$9+j{d`epj*jALDXiZ^ zE4iAYBXh#m>pN%q3i%-?o~_w|IgC*lVG#>khgCG(bFhe?QA|&0Y6SNex6Xg)? z(dO(OXnj$1A3@*#g8tt5H!o#)HO24>;pY>Zqpdi7QqtsRNsiQ(4VSxRqvhfCNpin( zo;3Rfkn9zoR;famnFO(QuE^SX5`Jhf7pn*&iyw@q4Ud%;KlboY+E>1#RWeFZ@;i z?~W(ad=TVE&>qP3z$&l_H9w5_Y2g<9F7#BRcS`fNP38TTjilFr?vh=QAyG-;S_d>R zvY#YpCrET^q}B|#@7_Uk7UnNOqxra7d(}VaK|5@8u8f>GTzRK|SbvF6Pmt{VA?mwD zGcd33*r}tXZ)k5RoHSgy1E zRN1?SXC6Th6*Lrlp!A@nju>44?}(Rsh>BEA1x~?-!Rz5Ag-PHPuH!k34+Ad;x(R*o z>E{NIpe2waV&5K~+17uR_ff5;Z*T2QOHVv>6-EMX!IJ{3pn>5l?%ubD)!eWom&r(A@dGw(u%7ne)b zwdHcZV!S-87%Pt}*}sRqkB7_CDn~CdE-@_yJ~0k?T$!g~$97J=D8B2=|N5I_rQ?V7 z*rXy`brZ|!z%Q_hbBs4r7lxyi%Sf z53Z-mz4GKbJOZ;=`0r;9qXhWGIK}CEVm#p=jX@aEQ3&!j9-|nmxFgI#e>ND!(PzjB z1@b<;wsMK9SHIC6u89CEygM5i$XLwK1bA}sn{B9a(Z8g27IEr zN>P}c-51CKai07SdWzGhHb$A_?14nNa41dA9~mMSj^@hw!?|+qaE_clkS?bVq{`_- zsdD;2qMSMqFDKDhd|ab^vp-qR9ZHw8hjZlIp&@eiV2;L_gIRL=K&G55N|h5u$#QI; z!z(Zltm5YrG!IbxYlIRC1(D|7?bOh5z>-{~+{QqyIfY&smPi^AJ2w33|yG@k5^wScK<0 zngm)5-WF2bz=)E@Kf$8!NMz%&G`z;(PIb<(QY z^zNxX68tv=jAHpX@^Ae@2dHL?+_#7XHC+5i0Mg7 zy)wEG?{6GsHNy0}rZm?gR++9R|EYh&SKfG2xrluv8+^dO$sXzrf>&S( zG!J8m!fBIa?pI5-|125@9K$u}BMC#Y)x!ecz#ZHJcc7P`zp&pleP{5ppuaG}EZj>D zh}t1uk9pqs(W=`_{A{*rIWP-)3Vm-0<};QolqGAwQC7j{!rq8$w``IX8`f(NzSkQ! zl6Jj%sJ=6C&P;Q#w|*yUH?NSN_IxQvk1dy~@_ABSK3neGm?HOX zjFCqbqqLW-)$ACrFq)PEpFFA>`rEAHdS@ApQA|g%(bi#=d*oRr!Nad8M8lP@T1FDGsZYCp)@|JKilz zk~>!%hVk=C{hZ=3i^C`mSHKs_DK`2v6>>hTYwo8s-WkaQX+FofA5L-dNdZ>zGZ2hI zfBfnzS*lIkEh~@*H^#`_it%!%VzT~wxLYw*s>>&8;32tleX88Ank^4+&6Nk0bELL> zy81^dF6GHB^}A*`wLGpT<>uuy)nS~yfwJXR$xx{-86mah6XfoV8FKf=EV+e;>gICg z6!_$q2fO885BJHx9`2X_e0Wg)_4uGXeNZG%?(LKZwVUO^?RE0t_DXqh^9!jio2Lv^ zcG~IVhMoea5HN~z%IOH@7FflLGjU!ITGde8`eS1i&EvQpT4NRSr=X`?Js#@mDyo+p z4_8)!J75y!7T;)`!aA%{AFmjn1hb0PExUddV--K6*xXpfMq?J^4$JQtr!c}NzsD-Z zDd;KIPaj4(b09^|9?F*D69sbR++-=gFkQ;dPm|I!lco677`b$OxLiDuD`yX9IJ~0z zN|L}QPB`~pI671=ofsu0r^m|0W25ELkx_E-$Ot)qIA6{j%$3szv*dVDiX7P;FGuz` z`5yR$Ky!J9RT{jdwLfSl_WyQjeDIRsk!alP18MSMGtJSw^xCWP{L3%O8x8Q{zAW!G zYblw{i-e7<$*Tk0JUCbxSap z?!TA9J~xrNo?Dowednlkf-Bg==go%i%0J%a6QLJOAyEf08#FHjuwO|D62s zZ-15l{fGaM|MegLNB-llf6=-rdbXjtywR|M{OP&B%imu7hy3yR=j6FpUy)bde#@&p zW?vgL6Eqe)DvsX*k4n79EN}qzxo8&f8CyJ*ms~{Vu4JWH%aDvF-K;6K0_jsVl?R0HaI3y_e|z<@LuGmU?%3O0mVe>?>u|q zTp2lGwBsjg|FPEWke{Od8m)to{0khBHGH@vWM%1V;v1o-3fdAqdts4&u3sf%=up*K z;56!w$qNzi4NTMX)4rN-L(js?0;k|b9yMi(^pA{`0g*A%wrdYbA6g*Y`VLTbL9>A` zVG}szAFsTsK~2`b|M5@qhrj$){`B|fwC?=HH{Q^m!QEj1FtQ*3H}x4^k9Kkn8Q1a=q%_c_;6trG!|F~o*~~f?em4QXw^#9X{a^g z{U^+xsqby~+56J7j{b6JI^*WdlEP_IWzN#Y^2HjbCOT*22xXPFJ-ciE2`1uR>&**m zq0KB=^OY=HvqZk%zDkM?ERd4RUrS~A=Td!bmfXEQNuOB{D@M4y;`B^0R(ViGjj>Z_ z>@bNPJH8XE<53Ta&wJwOCc!x#*L!0592<>IG_PY`6h?UChNEZrxuV`Vxg6L7p1GEy za~nMt@o6ajxt&+d1l{B}0w%F@%9RFbOR-CCDX9#l?sdI(l8l3(t9 zCI5P`R(`p^R-W8lt@#J8LJpq-FcfK#Z?q1Fc< zNuv+m*BYO<-ff`qF235v4dlJ%?@6B0c(O0_k?x-HwypcJ| zDrtF15|%Jn^FZ{nC1*qqh`w*=A!q|=0DT63s(Bh}lIh8klu7Rl_QdP1UX0GYI;-9i z5;I8sFhdG6C8sb`^FW!yvQ($RbHm<)#wq9z>=l4N=g-eSr_VQ@b8j|$Pu^|PO#i1V@^Ylxj|H$87_=i0A;!6(y{QYmrM%4V0Yl2nKH~t@I@BLn7nZErx z&ZuLXv3CIrf&xlWiYQV7AtAkoBoGLpgdWv$k~v0g81 zc~NtppQoqCz=h$)B`^tk3S7ebFyalt*I~U&lH!$`F-famT5G=_|NeM+s5fTqK(q|< zF>ngGA96g2Q&{6*n%|p)&z71pIFL1^lcr8J-G%GXVXUW2qL&{+CxJIun*a^#(SDCx z4vu^qoCC8=o;6F)g@kBiNSI<%Co3vBRpX=MEH^|h2%Uz1H|x#EnvuO9)opj(ZFO1H zX8rZn+pO;V_WSR*d+1>YFOa{1B^In*t)(x$==aXT8u(6N4}x5g%kRJ+@CuBY`tH_igj-!dUNp`MI`z`jOQeZTn=q-ud)nZU1t! zzS;S@4jtK|^3u&}t=p`Qx)*i4{&}5gTCB4zi*>HWt8LLqHb0}VA$I?`po2iQ2*{aB$qZ&A+@@2Kx zzpeJBPj#a83w1Pou8zh})mr<$S}I>tUGYjUw{vKUpK(f#&Qw{>e3j=dRaNmiHI%=i z#_DZqsM@N!ve#5wx6=;TNxq(&=cTB7-Im#$l6YXuDpdPXD0kG8osIw{_A z8LV%^_rsIJynExVx7b_?t1zFkF7lwE!_{x#Q}%Z~o?2>-@zs)x8BYKD(W5kM#IwdG zFp7Cn$nE%QuJMPK*ml8H5Pye921~pFjvz%cG}$8=3%8ym^xm#;MTkE z($&{rr>m~JR@eRYFBWY7`NkV`{Y`(>ZFk?L`yS|_{!cxrA%Ttl9SoDe#R<({UdoBxE| z5bOmD{^h2db<-`k>aP3l*DZJ4soU?qM~^-Jgy|>bfBFs@tijKYR9HfS)l%b;>CwBl zaUot^dhoGsGyj%oQfMvYhF~VVEBI%qC(4{Z-~221P@HFld=453EJDC2FbR1bxP@(U zKU{}b7q);i&`)C1CmEl>DAX=Hj|%5`4$MLwGn|4Z!?UPC!rzNtLkfqhXCsH?Gh{@($2a(6Q&^E)109}Q;;$P_} zu4V@g@o`C@rgEXh^P*Tjr#ZuNIk3qOzF^zWD&%~8xtgx5;y;d;#HXvA@v(~I563KS z-|+|gf!mj#_gKW8hfB^iqPMu?T{)$bQH*D5Q;azruLO>PjM8=6&nnf)J{}3sQ)-fQ zvMRv>jpbC;M4j-(@ycW`suI;vk>uZZ=h0f|<8r2Pfvq8RspCccc=9z}I{ktEb>SQR z&rgT+?;nrq-)BCu?S_y>gnG%QDqcu|OvqR_SE>d%AG?GyQZH_W52vo&VbWwHHr+tc$1q zt{=~Qp&w3pd7rZ#TXnYe6}zv!dY+n#r>WWUKJlihz$$1bE?^b!n0LHBFUmNjHb2TA z95XLUeynLJm$3`A%H(%omB0~iimzWv;AouUtM`FVDp~u-w+3N3{b#8~J{oD)IYx0K ztirbA6UQcQA3kB*af)M>3XUHP_cIEt(y6V4sboLr!d1L4%)-wmK1M0nJK^_PU-6!rs<*49_G5caL)E*gEq`5A z#T$)V%CetV*|EhcJ~ChV2WP3^zzpRc$n+TI;B?FLP{+euj>m((9ai(x=TY6${eH{) zz$^Fm=;mjY2YNlIK7$_B)1!x)j)D(`T+dU_4$`3E0~8U@-}x~XXesc-l-X%EPMbH? zdYwEyW|*zTL*F*o1dj;;x6qpnPDz_K*{&fsM1BX)EgDTmR=QHABq%aIR3Wirv~cBI z&00L&dffIOK1ltae$x7-(T8@<;(69%AFmCy#V`eaVro*TOU8#{zLWt^>d9eVuQ&2Q z@CyD<_dW&wl{We{3)t_|5pZ@5LS6-<jWp&do4i%BTrrHFVSn(@$U(c!;_r_+&h5{`uAs9q-A}Fvzbth>ta&(e z%qUHoKHchd{(Adu`qR}{>yKAmtv~(oD*f@AKU@Chn(J@S^?$v|ayw{U5A;TZ8>wh| zzQxmnCC)rBw(bD`M!xk8=-tQX;D@1SAN&#-7jJCC`wV*aS$k*LgSsC0!R2l6#=-`-&$YYh5An_;B4w`uYE zwdPlPW!nb5_wfdO{K+bP^UsAUFI=aF>NWN^@cE|Yy41Yb*6Oj|Dn4E19621=!`H)& zu`8>fpWwyybQNljy}Dzksd#xG`n`JI6L`gO$;HMgx)e}{Z2aMJI|1I3K>lDGR^i^W zO_MKUl}^_9?|Gfyx~8j6(z);Slg{yU-R1g8AgiF2bPf3AM0JuKXFKq`m)r4iitz|c z;%O<4Q%+PQ=wxN0PE;frm%t_+l?fKGN_$1T+RGCy?07|z)g9YfJ~dgEn>k*))M{&f zIKADpl>hv6Ouzh)XL*=^|8UIwBmX+T&vXv7j(?G>!Ao-SuzoyuP(S~0Q2+De0sYUV zf9R*v@9TW~7OOLBsamY&vMjZfXR4)aikeF%skLmHX)pL;PIr8$3#UHUrBh$)(uuEh zs_k9XmA|CA@^xyg+@RwPuj_ORKHAM{tHBqsNVP>X)KoN84F##byic5(sQc-{Dju`I zDY3>R>|4*2F05k5eXQbgKd?z4tGI2n7x;t`f6ISkm5Q8bW0ku6WYrf;w6%OpS2-3X zBCy8CX)1xN;yKGTi$wtc;)s?JMO~DG4WiPT^68WT(!*k3#i+>g$Dt;Asf&QoDd+0-p zp2A2yF}a^d2lO)SBq+>V>nAoP%5)U!fBFx>vociiX|cvAY18RB8>fu9X_}IiV%kc0 zA~i9ijZw&7z(rpF+WyuTgum^nQHhra=6PP9 z(eN>2j76v^MsGnAKw}{vL-6-Kb0(kLEiA)`_QG*KXYraf+Whw0#v<@b zTGlM{_Hqy2TdqZ0S@-%Dt$byZX*zt5^sJd$wdp0T+O)xZGHbTHtj+Ik)#i6MYs2d= zXycn}_0ESIweR4&DlL6q4YixqQny;C8<*%@<9zeV;+J*oVs$;{LBY?OVJt&`H>ayO zK5?U~>v6R`fqh@CkBQGW3$MT)XdQuEg0|s>J5H`=k}foO;aJ7_VI7B@Zb-9mx=3Iy zhv%JcO!b}j)%f^y5*Wo}k%_i_sv*U~u?zbr>nG}TeX?EWUGI5S9EZ5kF$w#pYLk7@ z$tvbasZP{s3!g`&GEo*)2_oQ?6P5A)=%|eMg~uxFx0T1Lqb$zictxCr@k>R#S}Wt! z%2=MHw(=>u(7ICp`_g;*zkmK#|JTp^^}l{Tr2qNxp#IlS2lc;yI;j8o*M9xyr+xa* z5Bn|9UVgcBRR8%QSO4qBZ2jxf_qufEBb`3}s@m!ns;N9(jU`EHDxp7HtQRFo*8lC7 zQ)~3&*=_py+&2Ak_6?nHU2A=5n@T3xx;}VwPgc*;>Dnx-k7kV@@-__xQ5H?Uu|7Z2 zg5&;epT2@_;D7_St@s3ssE6v!%$QyiZ-UBxj9YY^7w#Hqoz9--^)W;H!N&k9^p zlbdLq(p)@QHF@z~Pq(9yepaD>iVMdirH3OdU=erB@rlz|*yp^{Phgg!y&=Xbh5JKQ zurI{+Iaainb6&tJPETQkRa}@irArUE!hKP`{`raKJHby|a&)#T@|UTlYKxAO18V%t z^pv)yFN`~y>fnwKEtgYrY?X?RE>}hV3#u)7O)XX1)!y)>PBrf^Jq9jms(nvYrEjXb zY^&<4-dAJIr)sJHr`j67Q%l{qYOMZRwH2SMw(Jwtlzgb#viDV6_O7anwy7*{vx;&z zswDqaRTaH%bv>{GxePqDXav;u+;#uGy5ruvjZf%9%UXob20d$>GIZo%>(iDnDc1b6 zcxqu3@?PAQX~sm2kK%9rA?8zIO=bKLunM&;Xesnfp=K6N zp*K7pnx{t(HwK|E%EDFi&8ItZy!Re>WT-2m4w^cju()syczTGf??<1sq{*q)hn}1k zc@p}dFfURIOl=6c1?rXg``i5;4!hu2aqIoKo~q9T1z9cx9|{4Zz$$~E8E%ZiGvO81 zv4L-x=lOe|Iv%_s^hueuaDl&{$wPe~HCCY}hc$-LYVf?cc^i!dUyQ9${76sr9njwb zej(6S@bgkr%=@6-P*cS7;?gGS+41A72MWhsZ&-XXTm!ps4_XTR;%b1{=lrsDtF6xo zHA~FrjVpx5q

nk^D)(izlM=N9QTzfCxZHlix8qEbg1y3&drp(0)O;h21p1=0h zO;#_o<`u?G#w+tzF4s$MY|xt@yskIi-=ZyVt=GFBzNkI>-c)}6X4Tics*d^>tS1-R z{+Y)4I@>r$XPfZX(o4nHtIc^+Tu$gWe6+6a*s+S+HxF%qz7ptBxm-_yQ5>hZZI4sn z8UikXQP}=(tkN}~1H-@>T;Itl-Z952jK(E(9%}?1H&&_d`tF%|d8J{>7VZXI3M(w4s7O+Z3d8}H?V%1h2r{?k)+iodK z@U2liUB_#d>wL$X`sKo>`sLzR`tj^%mWQFP_?Ppa>!)*{TYl!>7rxXlmv$Soz%&28 zw9m@}oqS6tTh^+na;6%JC;IxxQkN5BVf8x2X*vai;K{9j-g?5dm1mhJm-=HoDAen; z6sM`VDAntOf*01uBaQiyzh;%Z2-W39_<6-~i32 z8-pAQ^K%M3;v$e++%Z_i`BR*(0;>@0cV!ir#j%NF6?7FlwlDm0uZpk#8$D^MJw{il z$jMS^_B@s6E>nHk2G2)Z{efvIXd-ANL|enBYN>k1d?$6KFRQWgO*L1)r?$FJb)xAT zooxQ6X(n|Q@2jrj4V4&gysYZ7zp1YBV~H8vSDVWjnXLuVlm1V>N{G;vCt64IhHd+~J5T9j#CmVv_tT3vL{Cmz+~tN}b? zxb-<>JwEanUjO(I;}+{{J7$EQd}g5OCkvjRYcV!*j7Ek$r{QCVSsz+FwP-7`sWIkp z=|60+zYZ6@fZp=-X{RQL{1$!KsMCcj&{>=|0>_Z+ack}2M}Z&c0m9$+tWyZjkOLyO z!@cBy*ymZywRm1(6rMjWGE70?p;qIBz5|P8Et+qBAbRhyh99{oxB(_$eIV*$=)c0; zO}{nXm0m1pID970zX4A9%Z=u_rIv|%(JAQH50~KG<(cpafsR29(tQs^WB zn4U5*(SDx?`}8(n@U%HuTDxhZ<%N3m?q&Vg$o;VHA|92vNt298$O+*W!Ly1^LGKgz z5A9{(GtU?Y!ZT=7cxCT-@B!U^|9zH2Lf66P%RAGr*WQObRkGK+pZ7xVf%^!y`EGb! zqEjc@I*NE+@W`aj$W+?Q>6U+DjGLTh41`Y^&+~PE`K#{j-otw2Qj0{O#S9+xoca!V z%6jjHCnYFl`ZSG;jQToc^cJ%RV!?b$QNJRY<%%t=y=_5+pT(_ z?_<`Jm-Pc+Bc92#&|=^v=VL)rVO{Kz6GH5}(LWDO&DEjJSTtX&Us^e6Zlhh^I{XFALU6MbX|F!Grd?*}0xbMs){#0 zvE!BT{%Zp@6}Fvj;>LD3B_QCGz(fI{&Vq5{c`>b{p;+8h$|GT3z%GX;sQlmrRU8VlvGm}C z-;Clz6I682V-m+Ju#009H#$wFa1ZR#$tv&(ypq3rf(5K%yuy0QhvR*HYGYM&C|PC4 zGE`GAUrl8ztUkH6^aa%vqm`^zWyx!*t9;8e5#yEmuS`2>srkDat3FV3&2}|beW;e& z&(zlN51nYHc63b$jqW4Sn`W zJw0NO64PRgOJEW-6u5*wD)?&21%=0l8nd9GOv+4B+O$M}{W4x$w3H`?K4p9Yr{Je} zb{y>Ct+hwL6!WYEk5qUZJ?IlPD)d=}#spj5htC=~e1Ngag!mBCQ(8)2%lPxi02@e5|bbgTI8jV`_Wg25OJV#SqjY!w6_D1Ud_O z3Gx%v<8aRWD$fiz&VgCT0l_-th{*AT#m87(&DWZ)u^!>0#w(md zUx7>TyN;SLK?8;lx8vv_^h=?i3K|pNi+6>gcrWU-$t6*@JRv&Dv?@Lmtp$w-FEDc} zBVjdEK0aY2rq0bWuT18GdBzm<{)aK>#Ye91p0!N_7zz?&Q zFV!61IrI}W8W;mcp+0-T%9WPi8u;vRV<=vFf`iak z@Y{@!kG1a-J1Nze3$BGNso%c6M>l&m{uz8U^vd1x-ZrZtd+Cjpdik~a`uOjwm3#Ch zl@x4JW7S5r)~qpRL1#H#KTGEueCrFMtN1h)^P;%^vT#ew6hEH?a!Vkauzj}K3wIn= zAv)Q_)$w>aAmbTyk;eZLUA(a88a!4xTc4;ijY+o8F~=)@%>=C^fK}W!jN;;SZKB1g zngrtz$0DYYIK9N@JMr>6UY^HtI@R%Z-^t22;}bu#RL0pc$1aX*y0Qu^;==JtTWPcf zOybV5?T$0Lb1+MDX}slIShL60+9^&mU7)FGk{V6pNVWNzy5!dC=dC7(`s3qutJGS# z(E6w}mrl0a4|$)a;wa-3H#%;y98O_`>I=gC8i>V4>=pPi`Myi_mpCmQphn;4HA3Y81?tB!^n zueft%2SP1im68LYDmfV91=|NgjZ}CRh-7+bQL&-AP0nwlD9j=Mz&!Us|TVd z+`g#Jo^ShEYdOhkl}nFKHCAb^T%*?7SJYUw$!d(LGp;V(Vt$f_%J==O(pLYCV3xMJ zFV)fbm6~h6P;>p)me*--{!R@wAFI6J6%`#@r_$V2D$iS|@}fH0@Da({AIKmWJ;w8tZo&&vZ2Jv)UhY6+EK!r6u|e>~FcBUVR?X z!;kjRQ%?_2Xw+DxPfyXfh%uJ)85R7D^}eTGIAdAqQ?gfw0fLw-T(G>8*!dNg0 zT*N#~&Iqq7c^p^(HgnHJOMze9bIAF?3FtP|A;AI!x(c<*tgEwVqJ?}>Io(1(TIZ`6R( zyN2E}XkBP1oacMN9k3_wiw_5WfE$>zVGi^Zwln6U+epfu!PmLU5zX3hzSciJ?+Etbt znzwqHR=m7g%U@h-{Q#D%$k3bxv3mKHbR9YLqFQQS(ee7#I#IhwXY0M((7DDe^Q?G1 zWW9cDFbW=&OHFuSJNvKUGx7E>*HQdT(&W|Obmnth9gg$Lx_vKS18*2}z#V}R;Dt4& z31E%DZ9k*SJJ=_rjQtaqHG=2F%Wj8$_<|)l?X+#)2^aXxnf_LD;Xt@d;yKz$^Z9 z`4eQ}W0m@xP-7K${4!kyjm4*})G+#3#WWT?D^|Chtj3~rHJ8se-)~#hA~l!KS6%5G z)tAjxQ{@7+R4-CvmB&l^&tr{X~@S&}c=vy6c`p!oDCol-d8Y;J|G;foNvtO|1R29CY^1_!@ zUAj$;wSPCBfmL9W)~5YxZ9Jg1rh{s4=@QL{)Y^Pd9W6(6qBX~Q)H288tEKJ(zbpBl zhac;yhp6}I-%CLuBdz8pCMn$fDbJ35TCvFyicgKyq?t*|m^Vqu8L>*39AkAk;Rzw; zRY3h}BY)?-ifv-iYv z3r`T#Im0R(L+fB(#kUhb3IA%GX&mT4dv64KLcogwl+ zeAl=B{*gZWeupuTdp=wR!%(A)=Y@CS`Q)C^OVDoMBzn5gr)A^ouj!>No6RG`+R#hZ zueBck_;2aq%lbt#moK)p2Y3#7BG%7Jo*bsnzSyWkhhA1e&U)39zNnV!^=hkKtB(2= zI$pm>$Lr_lME!J~Y0R`fDyFM6QnTavP00JOAIK^fn&}_w{Ih9ttm5T$y!=n+Hr|rU z>v5c30%QE2vI_hGqwq}pB*rT~UB&biUp~lf2QrG&M;xo5pP0wQF^SI?%jj6e_`{d` zajepH|73NnJ)3JCqqq_N2;6qe;+Vu~DP2dWt8l!zG*ZoF;c6?3Qfq0Xor7guv=m2P z7R@D5o?cQIEduSt*rYhhu4^a^H$G{CQ{a-q32G<^Rb&1HFA68vG5=T)X1w9JB#>3m zPYA~&U0DTQsn7EQKB>){U}4+2;UcPi0k4o>s--VWQL@!h!zS&uD|Nhny^hzvWPNdu zH*Qp0-Fh`vt+xM`n`_pnrRD{1ZKBq9b-eL4wbyMlW~s=TrpnxO)fY@tL&*%|p5s;X z)Kof6HF?RN55LxDvu_sIvWirL$$e?7Ik^_y`Si zZ>YI?o0=-OskY*8YOedpJhpAk-&n5ac+(D@Xx?c-UI$I3z42=`R(+(p@;9xYN`1v< zt6{Dz+N`RQt){t9S7fZxxX%~`PBB(#KCI)dM|8aPs6RSdvUR*AS0~#F%}49}t$0=7 ziU)f?r28JcUp*gvSR+Oc(b$j?8Z~~n!sCK9A$pu5;zE?19&6!he5TGxwVI#kiQ!iN zGiBCfTW6WIjCn4-PGFTGBcC-+K}Vqv96e~+rWagr)Hp>CJ*JMBWB3Yvja4>l2bXW3sVI>Wnn==(1+fnDOH@ zW_++ljT@_nA9+N#+{s!rx9HZ}Z`b|ZyW6pDJ$k5Hw{E)czWa2`ZMW;@TW_=Rx*KlP zUH9ChyYKaQgr2tCPtMHgE98~P4@JaAYst!`#wqxf$t%&HpAp_b+rXDXkh5VvM*E<5 z9P>Rn5B6agv=E-lTuzROZL|mcHJpP#U;yTI?t?+lNt_meJ^`~(V}&mTPY?W(GHr?y zCVScne{ZwKGMWl}M9mG%kveUfpF`+rN3Rs#kJ_NjxpVCtHC1?gZ@&93(d+)M`@5O` z@^Ih2>hsi7=7Eh#OHsm<$@ZCefAOoJ-SmBOkm+E2SKc2@1P);vzJWF17Dm%bCa2l6 zxDKrY#=vU=KRAtrZE`qhBJc*+IQDQ%law*Vv=KB5-i_aF#(Xc2#P#H-_}p1b7TG;B z7tA+Zh<$VxK9kSKzv4VB=rU+5unGDH`ik@DvbG^SLtPQ;Gs7qB<2ivRU=H2`y`=Y3 zgDhu54hjz~&meyUf4u+I7v@PJw*)6eq$XMae)_|q*PyL%4y}ZB8PRgkR;UA_4;>@5 zNaUW(H#|4XJTWeZ1oOZ<=s!zdctL4dGqi5YM!oXZ7QOTLZTj%D*R=i9jr!)hE!weT zlXmafsO;=bswmr}dNkykWjaweU#IG3>TKO~e@>^K29`YB>ttEqIh8jGii9FX-#F`r6^ zDi4OLd_Nk>I19%vj#Wzb2OFz6CUF`{S3RZceju|H?iue7r>!_|t;_wC9*9#-c8Y3p z(o~Z@*|Ze&l=35KD&3!~f_<^d*%PhY{R!49g&z71qiaZ`NytJ}8Yf ztmBT~M`~&K z#?LC~CT-1!y=XeB4&#&@9rr~?Tb@p|<{7Kdcf_spM~Gu`CR*IlQpuDM26{^?J;;*VGA zia-28S6p$0{=i6Fd8Mwt`f6Q&?X|l4&)4eO>u<1LW!K;M7u|m6onG8?x8;baCngsJ zuW;<{?%m8&GGOqNicLx|4-H&F-Ue?jS_r-tcm?f)ybrk+Gz>haXc**A$YY_m5S)Wc z$p648Y%|i61;)Xb!aPo{$gv8X!annTM%HZ0hvA=rO{UDAp{ykfE&oFf4!;n&DCTGC zbI=-Kri`pC^OK;P#HFVCgIo`50Fm!ueQ4g9Tn%-|@FLzGddd<{J@=f3j2fi@&pfSR zL8Gk)9%~NqUa$dkJ~{_^2|TX&L7CSbuaLJ%pE<+4p^Wf>%i+Kx=p#;B;T#MCvp6>4 zeB{I=zqf_s%+GKWJ`Qp^cxhoLen*b+JD@G`j_mV(upAy{$3ZX?Yfba)tYu5=onRI? z1uX@Q17<+~aQ+oH!Wd{Se9!QSNyZm&4nA6Vg!}ki_ze;>Gc;|{0`sBJQ;z<$AAkFe z-H+}uf6WRDm{Hxv!5MIV;}z<-ct;cHv$54E(tpwreDY*Y0tiO%FkV^hRPS! zR{gw=S1r-$#;K;UP^;|xDwpXft_GR0A=MwAmeR>6j$2?4*dmZm0&_Z-ZIjpW=YBd_ z!Faf<0FXet&lI$qwH>w!yP4yUcyHv8m#$n}&(`B?>Cu^bTR130CnB%+f|3d3}H zz%3ERB^-xUES!!4lN5~i^9rou=_($pIA(F9pF{GzoDMn($KV&!O!7mFb)0?zr?Pb+UPzPSm}ujv9LEt~6e$ zE1IpEyveG{N%Zwc!KX4_6$e6$RZ8}c^+mAp35;T#asWMLoC^1iGu@=LM} zIF<=y6}Rm%i|1K!p4x&vp0AetPuanERUJ!F*`Y*v>nA6vWPhTH_s1!3Zy%_12xtCL(Pr5d>)kpot)B=Z8}P)mXfdb z)?6KLE7Zx>BI5!qOh-x0NKk4FPxF5VdWs)Yf!+sh9jZx^akA|{%^$IOr zyUM&g(8PF~!|{cnd%y+o2iyS9z$!2Z z0k6O(@CRB8^D)oiI_7$|xjv9zcn%td;|-Vtwu4t-6!?hWVfvgbFCXVyhXB71&*gK; z3Gv?JW25b}ImWyXr=Y38C2UhOL|=clUH=x-U6w5|EoCV=8+zuhTctTGm)ZJ-tYf(O z{kOH~1uwtEy)K`$`Mr0nJ{g`^?Ca@b+6+3#x;4g9JQJ@lpMj3VHucKv^LwJ##7&uO z??Ju_Uk$zH5~fa7%8VIG%*e3()Ygx;+q2Pu&|A=Sw*TV`z5U5Y+WOJEdUN|b+PeKM zZT;{KZQcH|-v8`n{r$_0+Ocz^@{Vj!W7+GL7dlx#Tc;bmyb$`zxkj(%*m|V+v=lhS zv=E>Fr0aOTG38gs+%=c+NdS{{J$?>ft#L{xtGNBswP+`u`U%I`KUG7m4jz)YF5&rA z9H%&)1txLf7^LgI(@@-bHyWQ*M%&n48Eb)V(lwlRV!ToA@rY?7pwr0>E|Dc^yB$sx^!m0E}q)0_J+@O;p9&JeC~*TIhU;qXLhN*>F?HWzO8n% z<%8O*)~Tg@rP?c(tFbIgHTjdQZ!H=K`bovXVB?gseZeZ*KlZY4?BeMy<5jdj*rFJA zAp(3Wuu8#xFWm8xedAT)&joeX{gC%@nhM_9JUq60LY2Flek|cCW!;~Hu`1dZV}W<2 zaBsAQ@yhNfTOYaX=rpTcwqE(U3vJE8`~z9aIXG9<#V@M9@=fbE+g$UJ`B$hXrhiIV z;j5}D*=!n0ZRuv~xkCRHYJe&VHma(4i)u=L%_?|R@T0&dC)#pNM?p*JXf4q3)*|DS z6K%y71BMRL!+jnxFYTT8-(|TU#(TTpqizr0ukJnX(Xi*9G)76EL2XaG64D}+kP>G4 z3jSH@f9R_}YWye#O&Di>3~He9mr^H8-xhkD;Dw=g3;kSd4WQuB=25|?;#h^}WzL^$ zzCP-qAA0N|>(`$?GtK(IO`efzeO=&|@c0SlU1g0QatZYPqLyXytPIV4et{CEBx~C2 zOg;LqTpoyA5Ix`UwNSgvwXR1Uy+81?GB3gt4?gy&zPOKalkETF}()%H?|-*6C4v|ep9#v4uLt~1h@lcA-D#H;5@nqJcFLX{pN@D<&B&^ z!?So--W|UX9F@!(*fXYE4L5b%{5E_ibP0Sed?wGqvkE7%?P`G4)4Qvh12*g;62`l+!eYFYfa1_YzAGR)$_3;0AyS?X%jqCKr2XAWYM_cvQ2b=WCXD?~j&dsVWU7`Al6>6?t zq>kD-I$4)#>(tP@tt+dTu0mZ;fNtV=gwY*G<8a5_^&Ioqq*FifxW&u$z$yP{{##=pleq1`(PNS*!7Gkc;1|a#jyoKyFuEL%JLg8nBaTzt zzMoA>A}p6fKF6Xk%y`7KlY$8WOyaT1uaEtARw3`>&;10jiaYL& zO27P6p&u_5>C*XZT|9GGCtG&v(&>Y`bn37!ojhu5K%Z>+LQU1LtD)j0wNk*yEG~3q6i@e?8#wK}tCMbVzn7>Y$ZR&tZ4n!HRP!Hq+W`SSw_eG1^ zpo0BL!n;y*c$%$YSb8i=#fN9B_~=~gtKU$uNez`-)KvYJ<$2IgV3f+jO)4)~uZqH# zj8jT;R;nU*rOFFmv>y7b1B73ty8L~s8KPzxzY5-3JhbMYZNrz6Z+rr$oMNB9Py7%m1S_+y9ec$k^+|%uD-Phw@_3rzCf~ZZ-h*QSQWF@CZD<(PAbe5?z zsnuZ3+aczS9UB&6^)9T>jCKO2(9?~P{!J33{3BBE@HKuk5wqgC|KFsy_YC}V84o7=n z&SaivZpX8Ncb8{yKkP$J$@=8S#afOC-wX9Juo`(Dw57>2GfhK*SEzYK|3E8&6WHdt z^u6u&NKeb*peevSe5R{`<~`UaC*dJj>;Y zoTlRTolh1PK@;LPC-`2xAI!t&kQ>9F%Xi@Wz%vBjll&Y09oL5ruMgjw^UecA|JYee zyqah}6NW+afO8lp&6%mpB@2|bVySTpOk(Rc`*K70aA6(T0Zu_DL9;{@-s;~TkA#O{<%7OpP-o}=uCaQ7jEA-cIqci zNAbo^2Ep6pA6+Hd&nJOg;~l|)*& z1#*ga-eVQVE9890?Z79sxxxNGPpQogws6`C`!&8mUvbP*lM|-u+z8{8*2+1yu0Tum zYPD8x(23R$Y+a)tFCNmrF6QXpKNOgz^3xAR`r%@ME}qZRg|kO>?(`vDJbgsxPae{_ z6NmNVnf*F{;#(bW_?z_}X((Hvj@q?4QM=B1>eUovsN_(zX(DJSaEh@?7d^%8J6*-) zf}F0xh^`XIDddG*ZpeS!r>U@JVcFq$TO+yfAbgRig1zyU&&k;xF8Zhx?2lH#{uoa` zp-1iBNPj=Ld>^l{eqqUh7!@CgQQ?7D(R07x;6xQ3PFGp>bd~31sVsYr%5&zbDsQ2R zv*)WQXOYSaR(mx*#T!jWu^#zlTU1lN#dxJ8dxfzIS`7W-ytNCrsjmD3^Q%ys?A0sp zv6|$zhW*AX_*71{!zsl+MuAgGbgHAwxCAyK?*pfFd$_x&uk^UrYL@S~8!BKQp)uef`8U)TdKfy?;!f$R8=T+ey~ z2`Q;o3pHryFw@ZZw~CvXY&Bf4BfP?QLHmFm;0xG-n7M3`rY~8bX^ZA*>Vi4OBxojR zFVyhBDsV%}%;}b2;(j;<&VW7W+fVKXej%t+hG+N;G!;B5{8o=WHOO*6N#uJnGwnBp z+wdLH&yRl#bQg3S!u8Y5SUA^wF?eCnWAO6gtwCGCwzl+7=f2U!Gv64qTspl= zKV3MepDrCSt>x1BW4d_msKpQGj_T5xqq=bFuzotX-#oTw+upX?p~mth)+>)Spj*o3 zslI56N)AREdjztIW0pW}Avfe$1+NOc638m%UE!EdLvgGU$SclI%X-Sx_E3Y(I*|<} zb5)x^LlxQSes;l|0>?1op|zZkuYMU;$=?&9f;|!b{wr_`yi#~DLB)7)k4{l#UY5#p zW~t=pbQK=TQ2xQG%0HZG%u;e}vA0%XvDf3arTTTX)KNS1swxZCsQBm-mFBEaWx-lo zSD8Ni)IPV>f9BN@HSV-tElt&5skZzJwbboUNAm%l>c}yDh1wraUx8anb@Ls!sYlO; z^kA=^USIwD=%;#@?tkE3jR_v5F=5XsIAWCLfZ!B(Au1`{`tA3m|4JX$TpnmOr_8nZ zW_$bkwTuZ3HlGUlpHbt2G;$ns_6Q9aI>75o8$|8PXpNZ=r14SXtf$7;&XdC_L^VaV%^?JOId`X&~fno_=nmt)b_< zD&)sfGtzAyV-CkZOHB_RRO_kA+BmE^L_d{LBkdXR37QCe!r$%8ujGH=1$ct_mE-s= z=r4npHa0EM*7t#5=x5{Vj(9Gg3kMP8cz6e%PrqHx@hlj|yuHEn!R52VO=E#ms8MFF zb~Q+Fh+`M0v%oLB7tbYJ-iCRfd&ynEA#jG%RA30lD=xPKd%!NR2-nhAmR>6`2>0W` zK%aqSo*p~K*oB_6a2PDX_lD8rMmPqq@O!{{ z&M)n9d)*)IY5#59bo(9tI`=EDzQ%me{F}RfFZccVey|1}6Ka6gyt2`9Kjea_y_vpn zp7oZ+v$E;!t;THVMSOSIhTnzniS7fp@C>*HKEeNj_Cn1vzFOFV>p70rfl%kiK@7^}c5E}X8?UKU}&HLa!LzKHbeCr(FUgjZb7$JO)r zS*0jczls1pae9i=QT%?A{9qB}Y&?I7r>ppLHn}0H%^{a_c{JvPSh(YLIl*4|bd{Q% zajMQ9Ya`pW*5uQ#_jsC0`9V)#aaszaV-!YmKLp!F2RZ{*A<$RwsgVDHUF`VY zU={2cXWUY-n`2=rJs7XLqD(cHFR*;nsk+r_DPN@8!r9h~w)9ZCjnz5RRdF=KtNl4N zQLL>Dmyk2c**RXh--lQ)7Rw#&iBR6&NagR15gH3UZ3*&1xqB!2@zE_JjV*2(s4zi)+n(AC#pYilV#{Ae#@7Wf07?c)RcDlRET z<03~}eGfH1^v#c%7-?%MKhXODJ=FUVTTh6;`T74bb2N26_)_rFvSuK)KjKkvl$<{5p|KSJB7XznEnm$=`mS$n0;-@S)!yX$U?JMXt?QMzS?Fa0|DcuNRbl>yS$GFq+vm9vHW$Mx z%-vq?a)ezE%dmDM{omLoCqxewIK=T1?}XOCybQMxY;&9uje@zBxe<+n@5nI%?tnR9 z2Ui2bbsS@!MI(VT&_&pWH@JuV5dBBcRp1ql!7;YIL5Z)6vrlA$Nx80@i-*L&oG_5;`vh?+jM0Wk5fEWaoULSicc?T zD-Hh@tF)AaUB)PWEyb7b@z?UW`eVl5Vik^2`xD@wML$6&fhh>%l)Q14vti${M98?eByY8W3@R!b{;&wZE^k?H}t@>&H5G@++P0_*~~YzR3#h zou`VN`6~6Tgn#YCXd534I;K&?R=C3rZr7U-)D)LvWEN6|1j;!$fw>fK+ zcX)*gvtLwk&K6Y`f1sL*f2gT`kJSO4XfLun5Z(-GZCO{jcfUUBH;DCqA6CC7`zbm( zQW1%v-g?Oy3D%Qs_?V%(|Ka=9t!EGG&rb~wo)k0`G>uW?#~Q2Hy3He=(cov`7}g;k zqmjWQO)sJLc-W{Rdg_^{6p;|An2FvRh}02HoQf`xtl?vZnI{GRY)VG5(q~RmQhI_W z%}CR%MKcv1A7TC%K8Mc?3LR@&4}GgRKX=(2P0gC3zJvSQ+C%6kFcs{C&O=Uy+F$B% z#!d*e-lp_hi%(6qbr0!HM*il(UcL3#TW_=Y^9?uZia%Xtxg^$^xv$3q#wXYR^(H&_ zr$1k-KU{USuK2^1);|t!3-?@m<6r#i4c>V3Ep{JuL3}RrBl9FW1b?%`53mIDV@PC# z)d}Hy;a$+J_`4szKnvj>c!gukt?&-dLI&dOiX1Uab6R) znR^Me2A30oDcI*8g8K<{0p?^p864+2{)XpTbO)Zp+)a;qaz;F#IT@cTjDYq4&kP+i z%6hl5mI3=Pp5r#^#pxGAeH2<3zY)Jbc|E?P{hLKc*?&X)Zv3~zx>2kdH9B~_f>E6!SLuacywU|-i7bMJCkoAPm{H5u@)JNlsC5J>lhrA6r8;;Sp1$~L%5B{W2 z4%_ex*Ag(&7T=mi_-F}q7V4YHN8xEfgTbE*$Kd;gkH{a9Cqfe(j8&*Hrl-m8vB+iW~hJN)^1}v5PzAxP|?$ItdKoaz^k8HO$t-txJ86V;Ohc=_*b~2^=qDmH<7) zV-z^WV-=^N{MP8_6<@zur=bLnm_4VeKbfl z#v@~mPXf2$8KJ;W9bsLR{U$Y4tyNRSYPD9aP;2!PmFJ|W@W2GWuf%G8e04vLQ_x9D z_m5H8zA@f-Ajn3Jbu!90%l{PZMQiEI2|3-RVDDJv?+LPzZO1Bkd&env&sgQ}8h2Sx z^Mfu@uqVpXOAf@FccttoYX@Ygsd%m$i{==oR2`dY*HHIkKHTp^j8k%Vj`N4Hi*d^Y zt7$Gel&R8VGfYo8`hC1|b|skqw)jwr3J#>I_;9)k4yG%2Uz)P_q$q!1nhN$$QpwS2 z=FdI0FI{;Dr>mxDnJV)aEB8RAj_yj+k?&J=bk}6#n*4*alzSjcg~yhu;MfZ)D_~9L zFRjOZds~i99xv4Kj$-qA;NL)3>HhG8=2yYD!n(t9&5%ipKN=P)G#0Xm}nepMi59+CB z2W$8kdbRhrwVS8SnW~^Ldb+ZP%y7#AWi6hisk71J6E$sahV_4gZ&=?DMq+Ki2{GhU zQZ;SfRLxpEL+P_7>)wa&(H-6Ivo(#$=TL70TlMJmu(1LR!2FN)0XGoz#pjvKFVyYO z+l;v7jysJ{=wnOVaMR5e^xj8vL02Jezxy6L{@0stu|BtO3i%?uFVr-{JshL98D3$I zWd1`RVctheVKk13id1lTnCU6c;&~Z8%Fc0~ybydqu7dl?OE_r@1gkyZ50^h`bb^%jfgiPSmwQ)kZ5toidacj*%A$-iLPQq5Vk z(6(8dV9vrtib+f|eS$hJa(?86SPL5u6C7k(RZ@a66eI6M@ZQtr!!Xnf(OV8Lix;z( zE;3&E+s7a3t-o*AiVZIs*T5vy^pMvfm&HB|=YA`+CAbYnK^I{SX>v0#1pWP8-&}q_ z?xFsST6z8*c&6*GORrn>A3U_M3ppNgK4>R!51fLA<9LP7fLHJUqqR_DO}#VkfT!m5 z58l%2@4lu_|M`x-{AQ!}?OU#@!bLh+JHzro^iOfD;^lrW=N88qzc)G$ipM3t&nj?= zV-=V8v78V2AYUFRP**X{1&;CMf1KXJ{(oZ?$189MJyQZ%9y5V-$|VC~n{J z3VEMEMrkgdV8Qu7MsXU7JJyv|0=1Lh+jgu1V+1ma+xD}EFXz*c6Rbubx42`0?7}hj zVHK`*xgL(WZI4f2l1@Fv&n7-z2^^~qk5$FdaVkF?Y|K)5#9K?y^}c6B3n5r@2k%Ge zp*ZV1U!6Zwb;S!*l|S3&TYMX~7BTAx9wa|A%5pp&TX?KNT@Nf`I*N}~O7@MuEPjhs z@T_zm4^q+IF~4FJc!hvd0$Bx(C4cu=i@fj0dd%_t1ZD3E)8TJJb@clPr^$ z+;rz{=3(j6|8eWp|7ib5jXPL}nf@$Ovl5lHaFX@8?b+`^^&8w*{f7=PCW%dnR^sG% zjSd}Y8q4@7yr!d#Rq(82WKGeG`I(ALh*V@kwB=Cn>Bgtx-9V2?Guos~fmdJ=n1@~}1iAs&GtY9)d4KsFw&4@5VJ=1!;u`c8d@Iz| zpmET@gunZVQ4wJZj*GFp1B`<1!Mu%M78YS2mWi4)QPGoAj7{JdYM3KZl8twqM#FLD zd02yIpvf@zv(FsN{**MpXG)YQOCa1eDv+|PF!GJ2$*eC}D>h98`l8172SNY~`q^uZ;!HOcxI zX3Wdd%*6|gNfxbHX*vrzq8DF#)#`((YsSBtm^sDXg_>e`h2NLo4^Crz;_0ErFR#7# zj`fWrcZ2o;Z{T5JT)b|bqEk{dCOq7D<-vY^?6>4Re6t4M%hoq6ze8VKID|SN^c9yE zLT4d(9z22$1Eat)E9uF*X@eH6U2FYYU>dlG{w$w=_nEeTxm6#0xJcC{%XF%4mQK~D z`WXct>8hbPMnMlTO{7bH=R~bnYwYhO8*mh9G5V5Wf#XP)FpRi6Swb1 z$1G?mUS7wm>+!a|x*wPY_HZNoVLfL9`p()h*XJ#ucU$1L$19zz;yA?{eYMB|qsswt z3_Znq(iVAr@m&x6|Hdi5rK>n@aiilEMvir56PP2AQ{1*=lFRwTtNn53oVMad*re)6 z&}C71c#J9!k5<)@F{(Z`T2)6!sq!$_jZ@X3U@wjYsp8-mRU8UZrLoKyRdAm@YrN`n z;&ie+L#<`$s>w~X+G4n*?7(OXyeP&a`$vf=IXGIy`#l{6R&iQNS5_(9Gui?kaod5M z!Zzpf_l~wePce;U?`S{2I5u%(-mW0!em};dU{{a@$8vU#wy<-%$BBSva(4!Kx{2v1 zLALK$CHuP|f7r2|)JR8KeyQ@)IP&RN4}e&Lpwrsct@D>cE_q{f0FyVrj{u~ z6*)^)TeQ)7xEyc#QuN)Yp1HmGnAN`UH~+17-C?>)&%QnN*udTjjvAw=#PN!s94YcY z1BN|ey-vv6JU4cve@$fSf7}|)-FiM~nnmv?`WmO;*?pwnBc_jp#EjM4C7GHwH(B$R zXDBo-$g~vJ_o25w-r9(SFvX|w_c*$N*Z*zg_z~t=A^$URYLeCe;HiyH@^lsY^e0Xx z*FbL%`jS0o{+IEQVY>0Qn~g#66yPnOjt1Wceibwgj@|M9kaphhT~%4%ex^O+G#zZ% z0O`^@r1#zvAS59q^r|355DTEFprbNkcWe|92%S&@goIubNkJy}DR! z!5eSLv#-3O-%H&X@|)lJbh#}5f=l_SSVhm#p63QivyUb$q;1APIj7_X9xDeuSQ8^(=OXpL29VH6m|SOs2T z4$K0psKF-6CyaTl0r>VCR#1N~ z->cidIahYBO_Lq#)4Vm-Wl7cgY}vIgT^Xi{RSMLvw10Dv?AwHHGfAo|XG-m^1yXnXlbsr?c7${ckQ6( z_YjvtEfxGFR^yHOZunR50g+3C|EF8;o|?-Ol@zWuZpTh7mWeY)%Cu+3OL)Q{J>v(z z5SBSKO?^Pb?h+Fdn;Na`!dZr#`-5c;%h3Gg#H<9#FZRxFrnU>Qw45iz*+MW3J|W_H zscV8xg0F{t9ag~?#CVUs{iIEYj#`_AegFQ4ACZUKwvz`ReM}yGoVej`s;LkQ3)4_T zUwvgybklefd_oUB)<)WP=%_|qZRc*?rCX1l@NzEV@GMR>z(&A{;8>YZZol_=J$a^&_fvGIdm2LKWHrY zcbJC}Yl<1O5KD`}BIL)Q&Z-Z%A2ex7!F)hT7 zunu#HktJRQKEeNk#)D3T?t#Ar?S{VxHTTKepkwbKi!}$C^LVI}a^uV}^$Xz}3S|%1 z-}7^Qd^fIs@^gLDUP9-=E_bpfHnBP>US5yOC)S5Mv|hgDANw^^G#5FjpF&)1j>gau zM|<%|Rx`u@(_BySd_b0yY%wQyibHW3B(!h*K^xpV;KkDhtWuk*HTKZ-{T%d}i6ZK3>^n&$>9- zw>Ckl*LmEsXI+BqT9+uh)+S5UI@l#m`NcSfR%2T>W;D}P;FbNGa-@1=w#MjIZOBkQ z*|#A}`*2Ft#$4I6F<^87}yO7*59*}JJ+_HUme)s;`nvHc6=?8z_W{HayaP`6eY zh4X*WL^}5*R$-vTq(n%MmTUmv(gm@N4!3RXkdB=kz z!zC=5b8aH^`4QzgGG=O_OnqjIjGQn`bsJ)7ld}^8PRT9ejNvrtJ-CO)^=1^LN!IX8 zDHxeALrV)Jw0$WbPL8AHoJKY>r!_c2%@EILMGCDAL;T-tZ(tmhQM7icTAW^fDfE9fm9I(3$= z-MUNbhab_H6>8-E>7IKuhlm<2_uhZMJl3|Ya!tQMgEiKLoDqxPg_VfW955tI`V1Ht z@Cvao?&Sg86($kHG`w(T*Lo zCZ5IMaOQMEW}5z8_-11wc!Y0&AJ8cnhd1C0Sc5S*!WalG2VTK9gHMLJyw}(U?T0bA zgO)Wg*5$Vt^S~;+!x)9wV4h`7i={<7fm84Wp?$y({9SmCKK>lyM~F9pSI|IU2Q&DF znzqzrfmg^AqW0U!DIWj8Dm)91%zfo$%~ytTSQmz&MjiaaIu>8M_?>0)>bvh~Yz(Zz zcfc?U-&mr#PiQU+UVlyg^1++3cGLTEu;xuUbMytR;dTA=82P5Y#LH7YS9lw%{2Y_G z_wo5~O?xL+X^A^bPf<<9v=zT)?8@<6xq2c``?P@tJ|BEMmVcZZ@QG@4Q@t_E(@QpH2dqLa5!az@ z)rLIf6k>JNHW5!#Ec-T%lzm&v<>2mVa-w#w96hj9j@JG~P9OO|&mN}6J~c(qL&D=D zBr2Kn1ml%eh*t@V8>De8U3+$y)@>h^N7}ay@{y^pqCTHq-L>CyKwo7UVrn_N5MK`Z zOxc7&nKYwJrp_5Bsrm7$p-?-8TxIID5NpdBKh&h9Weh>BCs0&Aa z53#euMh%seoK*DzrRJn-ofqO_a|?$mkKoHePoYI0QAY7|Owmoc^ysN|*odK}kIurr zf8Rq7Yg{C}0^8t+AzzB~fT&SR9e%h0KDn>;L&_PP!EAMHiBF-{?SK99S3!MS_4oYt zw?S=xo+Xdy!ABm|wcs3h1rD-YA!@cz!-YBU5WXg^VH}>ti0wsp!Pf-O;M;=D@XL`i zPK_1zZ0fVI2cw_B7na+@9*uSav*5>pN8lE;5aw~8mV0mtMve~#qrfc~b9jz;75b*N z7}LND#I|sa!7#7@`!d=FF&!8@Vl0Et$hd{vm-wu7&yVESk8>FYhG3tMNl%fC;RQj? zw!I(SN3|Y5h8ISFd-_BSk%v3A*Iaw@?K#VlGpJb`UV;nk8Q#J7pmV?wTw}&8^kE9F zVHTdXxEaQ{$Fq#X5%36OFbYh=_dhY9pT@SpH2i+%z*V&9Hq?3J9yNHVtwIhHYjFM$ zJb~Yam>O6Hu6g@!A1fhf0ZvWruD#|8)F7p2ms|1<}JYp6WsS%g&MS4GsUluVr+7@I!Vs#OH^IO)mm<2lf4P5yBMd?!X)sCYsM`t z$FR6M3Nb7R0iPJBD3ACtC`Y%)1*~!my#>3Iu0ov@d_UU~)Iu4>uHhNeRA@tW6?nzC z1Wvi#5~OBRywq-plLH&$wa>NcCO)S$(^Xo~Q(%=)gHx(1;#E^|nFUS7tZHq%RDGQw zd)6k(?zM@^E5<6Csdm?s|+j4 zk+l3I%|j+98cxCR{S=lB)p~H`EmM~^EjLYLSDsfGGEA%14=SUy@6uIu4Pt4j zmw}&%SX^q}>Uqq+{H5{=^=jb?V-<3i$xo&ZKlaOC|HkK(CRU*yzgOq~zr8aQfAil# z%nLpsc!TvczU6nn*SKBcU0h?_@{AZKwpVkc{rAC5@EC^Pm5`nyqb81%K4F6-IVV&1 zVR*t~OH4N*CKf*s*Dws6!nK*jslXN(dI~y)aS9y3IP+iX92YdK%f1%T`D9qC!rPfk^yfFGDk3oGyp#x>kQYq7H2=bG<;Ti_L9ZDA3Z z1*Tve?tpcSU$|$wis>xIGR$E z0-m8oV}Tp!!x3mQycb?V`@yG#P6EfEsW1;c1|N{d#eViTWgu$S!XCuJFwZy$mVs5! zZ{Qi;$Fpz?*RaauIdha%_&u{1%#}}9zbk7h-jvE6i>2=PVre`zQ!btye_L!V@hNbK zv5Inv&nm_zZfkmq>(6mH#TdhFVGz?ztZj))T6zX9X~~RHXf5`{SOs2jSw*##quI@T zJ}#rcDypgY{+@+})(^F_ewG+FBO;k&9>gES562dAjx9A*)RkUWl z!z!k&7^@HidnYF0o|>bh+=*2@c1cwJFx`aKd_CS+b5>C;#n)3{6yp_ZkM4-`%#T-T zYMbI||5H|hS1_~{_5b*BE3SreJEIu4B*^}aaSp4*2CM?Fn10d{qlEf^j92zn#L4dU zaRIB~18TA+R#9eIn;5VPyb>_W##E`=kgl;R_ zOX-9nDVtm*1tW8$&k*811_ys^{D^qj(x`#;M4_dTFB_<#Gm z->I&``9iEg%nErzFb<4@jsl~oKJte@>KR1nG|E1A{Z8|Yxc>9K_vu_X>Fz(bQvHTG zrs41`HT&^f5l@siVwmpPL*k+(F)LkJ1q}rzfk$8#*a58tGfjnkopBh3JZ1X!4BP>a z@GNGUifJzJiE)VegU~bV9B3GZwa``Qv$wNgFpLpjR8>he?+IMu7f5$7Z3-<#?@mZxV#0O+7(h{qfh7!stp{(NF zx7b?ZW}9@A6T9Ml^L#$WFz|`SsK6<{9j%O2UNL4tYdNw#PR$sl>H0s5S2?sjK@R$i zVyvR=)&x1YIZkRe#kR;N+{4TtWWFEOR(wB@=_>y(tm3i^&!MT9u2Qu&_BK}8w>DAD zSjAfWL3`GFv9-k85|cvxxPVo*7HO_Dwc{{ym@)d~GFNXYk=n|!a%k5SIkNjnIlOC{ z)>_%OmD(#s$}!{yRc#tCd$vrL>K(I`JyZu7JV1K%@1?P-N!f{7|E+xTNYzEi0Sb!= zmyBF$>&I)&Rh}W9wnMirs%KDB1?KT=kVM2ss4hYd5eBQ|7iDTKxa8~vWfkhTP&c32 zEwBp48HE$3kC73hhbqSqmqNWb;%e~)p|PN;a5mw@>65h09+s;*54qFSc!f*w`H+uH zO!6juKAb&7Eq~U4d5C#o zePU{f&DDDkKjQIGo3=WiXQ<@@A7Pwn1gr747+NwyipQ2~OfBamajqbm2E4+aZCVI^ z9+(4u!B2x92X3Kn`#aBZA5MW+xHi6FEx3hsxR3EH^UVjtzKMoj2pm(4X z(83YsM}k4%0Am@h(G19`fnlshEk2P!aWPuI7Cy0e@Lu~KzLD>NVPGUU15IT}LafB4 zr>L)tmY5auMe&WWis>c1A3lIV-~l)buHpOWvj?M1(8r&`JGjqtwm-uea1Y$VTr>@M z##n`QjZ@Nx4wbCJLSHlT^coZd{`ScVr||Fx1gcGLChgv zdiiI6Qzjx;oinsmgF)YU?pc{Jf4=HCoFnwchs$Ku=2cQr`LS%M5Z)$*vw^oB?YXm(ao;v_EGqv*>dta^%WMPg{XaE*$q`W2uF&nu~X~ z4|zPUuJUuN;>Oh)yO^$Gv9+9O9Lg##mzZ8+ZRkB?6>GiPvrV;Vja6I?#rmd|+}4KV z@8Iuo4MuTwl~68meLu!7=qE6U>jMgnmo+8{WfkKSTILw1(7LPwuVAo=aZ2;nW0g=J zkIN(6Ggd)M;U4}UjaflcQEdc$uose@Kgdf^Pv=qD~#p(HKtNF@?&Fo2h3r=gSK@1co{Wef{vr{z$47#H^E$d z6Tg$Wj5CKd(h3UnOya`Pqa}O92+8HQl$J{I81Jm*F;k|<$Z_Lz{pS}iR%U^1%s&K& zz&ZR57z5sbgG@7lU0@h^1~YAjxo{cGgLYFkae|)v%zD(RMYABb)?!J>C#HTsu`6gI z&Iznw82rfr4 zo~@lxsJX~~eK+gVa+a~zdi7G!P^?ah>L~u%fW|50-)L^J6Bi3>1lK2tG06$+^6~6u z#y!T2agYlbaaD_XP#I%KB8z0U}<-a)0psP=o(;?Q}U= zJyVYDnXVi{ecC-+#>&2J6XZzMOgX;)Sv^OH`uisiJTIpYz96Tn=Sc0gDYAFlWI0qd zQ;zOms@X$+)LRsYXIw{pjQfjnm~ELeBQ#J~jN&S*Wc-oi}uqF;hqBa}#EiOYztu z{T{x7y81BGxTnUbA86DBYPRNSJZ)HPl-9-P%wn{Zn8YNBOHNUC;W{!QR=*LA1a|K##S5Tx*>C{+_#)5B_-f-D+?O+(OJPtbj2F z!@wc%3bC`MpP;4S&*9u=^cnm+#Jk|bp$-diD8%V<4fmL_K5JsUoA+qU?_I>m{91nT zt6v4YLOd{k3-(-cgDid)|BLA#un7!cU;$W$bzlUTBRZLy zEGfz*QAu!6R&ZVr_c`Z~^Ml|P?xp19=osfV!w9B*5L*Hl@DA4DtVEaxPBC6#pJu;C zs~~O!p5QxR3S%((!{G{F2jZG}unflU8ai^MvI=`X9D>1L#%I)8;am7Nv>5)5%*iP# zQk`RXdAX$K=SzHMrZNlb@q0M0xNPD?d2$Z*ZD%WQz#%Y+X)dq}F(F)=&j?wp3;GZFK=1+n9BQq>0B{0vCe(hxF9TN)XJWZJjKM7ECFn8e z9Iy&Z1h1fR5eH{2n+3CL#tGGu`FYcbCb#D?XD?*L|&e&eUh; z$O~`0rr!bIz&X@w;d|ft;%~BI^E!F`{l&6$*<{(UWue@vA0;>IN>yKR83itB$>0-X z6yjhlP9><(=KE`0RDiRJegyEv?(Y~sh&!Yaxu z4yV8)T!(UsJ4f&DNl;E?tTi3Qv=Y1aS~q^SiAju4j9*+vp>IrLeY6!D zqlHzDRK^BOVr+uOVtrbVQ$n;8KR=lkR#9H@b(NoEm6{FF$}6<{E23oo#u(2wMT?q$ zZCp}Q5v%61%f5;j<&-@YF#)5XzwBKX(`4(TwQrn49k^Pp*`KB71;Hw4D0}hwIGmz6 zK^4i$DYaX2wI2V0t@%>3nfh)8a&SkvvI^%KDvQ*L1#!02E{RHvQ$JGg!F?oaScdv}h*hEf zEcrduSs^Ex7+P3`yr0C(IQ28-73Ijp>El&r88xw3rad!3S%ugXVtuKnLflHx*g_dr zR-n1e#bZjeUM>EPl#e~)&t8qMh<%#5d^5a7 ztr)(E?}IUDsgr^pf}X^E&TfRc;1&1-CgJ@s2E4(%-h&5eojk6Idtr{%kr`H2riPCM zO$By|P3OEM{9pLkJPnDu`{Slf)op)sIkCu@+qgGfpu^v78?C z5BhdYR8u*;En80QE|eoX3Z!;Rw(Lh+(OhMiCDrr&tWOe*x+}XY z()IjAYSj`?OMdd6iX4qKIlgy-9Nsrqj@P~@jrH%!`MM9(8tOih3-zB#!>Lc@^pW@E z^pSVv_`$d2z`j?dX4gw{aNiO+b>uy{c;-vF&`>E?E>+3ZOM5lfHzGbtEg?Ng^J=JJ zf{qc95Gh@IchmeHbQJtO54C$tS%ouo$W=zqKu>{FV3&?PI!RPgw4U9=xr1mac7`&3 z9?MH6_XnR)Tw1i&SfMU0MyxG$U8X-fS*FezueDwhGUJs?$WtzxTq>m#OSI++tTJxe zcuCF4)LJia2zB$}5UqgH?{06n?@re*$i;jZk0E-Zh0-NwG{vwzQpAq-*196Tr zwNwpiG6@Kvx&G!jf_<xA>D=FD|y0%8M zP_fT5FbuiTUM?}Lg64ps%MiEA?|`Myd-y(LWnd9l2^~>+)TuAeEH4N@ zkTC`PfgcFHgt%H_Rxtc7@CsZ3v%naPn=S*>m_7rmFwT7VB_cUd<3V5x41EO-fK80; z@WTvFjF$m%F6~uMz2AGfl*i&hT)t6zMULuz7Jm%-;NFghcJ(K z!!$$DjGmsZzN9xl`9QT4)}H_BtNI?a8fv6#B@{l>Rs&%Why?bK-b z=5)E|2ckxt*_oo-bQR?lM@tFi6_-<5)KYv_F&0rjkFki~>X_pX3RuQx6}vW8K_}6E z2&))>7?)UU<5$pJ99A(-xs5}7RbBt|HOB#wh zW_)5Xw4v9krBuZQEv%xxp*_URCa4|T5vz;>vovuF_nLSG<}fy~7B-Yn5VCJwcg+pmq&u@&7Q_$JOiH?tI!&+m>H_&|;_&3$rlJS%hdIa0-4PI0Y?*vy8hlLfu+&l!>+FKDx`Kr^ji$3U%r5NpZgNq^Bp!#HYtgL1~`G|8fQp@wFNGIr@I` zi#aO}|BjuD%ejQqgX4_C#Pl@n!zRihJ$oqEptry?=qlt7Q6KJ4_uZ@YZ28Ub3}+_Y z+xkHrYyH@xnj6IV)WoZxr@$|;3=CuOEX0wJS45rdzXhg5L+9&#!-Xm->NN|1Q7wOW+^6=WhL0YQ<1P#?DMO9ff_ET5s&> zXb_CEr_;hL`R?9pC4is$cP9rkuu#$r{_USJCJ63kcy9RyZk484VM_HuLxR9&84Ks%L^JdRKF431QsEdh4=AIun6n&J1o8zCSeW6 z8H3gMJ#YxTW4id&I;n zS-wn`fBuoI+W495uKrSX?tV|MoEqcRhQkk3SJK2P^~I{OT(2uqvupDU-8fyS=l9%+ zSKt=7!t@p86o*w{l2Ct-=`EqG5NC;d_CsN zxf8S4xXp807{$zdJQ^o!@hEr5KZZkG?ZnkoxaY>mTHn=D+`h4i+j8I7#nn_!?2c`^ z_H&it71L5eTVok-ym`Klo3E@~;@5aH9fj6d#vOxIup`@JJVQV6c}2Apr(e4@+8eur zt`f>Drn6XUtfJiF#H+w52mII-^Z7Kj&0|$eOMz94Q{WKQO?*GkKI+mEo3buOs@Hnv za*N*6+HlmWjraUK)ZVX1kwcaFYSmkEw5}U%Nk+Css)P$rXB*@|n|Xlu52$-YM6w?2#)M zcgfX@RdVgpUb%j0zg)h2RIXn=F4wM|P`?iGCgdP<4j{gkgp5RuJ9(l{chywLOD4~k zeVe?W{^5g_Q)poo3~i-dm-c!d59j-E=3rtb_1@D}PocgWb!w>%M?G3>Q1k#95<5uG zNS^TI1g#AR*Py*T`OIXExuq^`UU9C*(>^u#Nhuj$Br$1G5|%)XxlGMnCbpJu(K;#Z z+G@NBjDp`MiTstUOg$42ez9|NsIg*Z^=zqM3-9n-;S-n#jYgRUj_J@|<8kp15xauH zFvPOL6P(|}S<1>Ztsj(!9(hD_jL46++-7o)U=?zG^epDPey1E`Y(hQ|MjQ*VF08|v zyu)~jYx1Zu-fMZ(a1HiIySA#S^y)uAwG{U5F4XJo_Jnc@Tmq9YZoVGah5egr{3K{B zunaBp8OL`5>%a-@^V}m=1?He77KJ??W?+xUhr%`f5zb0vPGn-D&W8hd&UlFRUo??b-88v+dZJuAT`o83M`=AF(<|$s->Wf__b2t5gnu6tYgL_v=@$e=CTHT zV+~^xyKbp<=X2lq#PpKTHk3h3D`}}UCb?7lb9@4;pqH2#qoAp{7K}SJ-Xe*|p5T}B{DwwOKg!+D96Vp{pV}Vnu)<&wCcCwc|A3s)Q-`CMuajT9eUzNqKSPf1 zpD71-KP^Y9=gEn~Z^^k+pU8!?>*T_j4RYbkdbv>lwVbb8qnuKAYOOS!-6~fu?vd-4 zYUSFM19Ih}XWv{tBv-DSkgHcu%C&2!q*J#iv>q*W+sHG{EXb50F+)^Sp|%@(3R()j zA2b$f#t~23b3kw9nLdO2Y0L{TEv+89Px^%o(6f2q6nKSv z%fQHi+EVjv!c!A9A33KeOMO67W=)XcrQ~6x>iNr4W=@pxPma;p7u98kr%UlTVl#`i zzCNr%tO;?k_;2v}#8UUn*HZBJkoSXj0>j`7;y#)S>_NUVS_J1RldDV}IO@SY(W|%i zIhP0CpdJf(&TTt((z^Dr2YJp`^R`3RCv**-d!lDAoktEd&)GYeOD-~XXInk^u-tP$ zXGcD)_pMew-+(S+IZAxDYC*j`e&lx&V`KMeiN7H>7+r`s9JmZ7!O%t6gW(JOLByS4 zXes6cqJ>3>{eUaT?}1@p2=;aM|E_&{D+{m&W9(1Fr_gc^BK(0r$aIv!F)^x3urAEN zT4)!13-KcuyaA)Y5a=5)6YHa+z!;v+Q=;p$Hxg?L|DfIA8^Y(pHGIK6FU}=P*CNI? z+ph%&W5FLV6bwS&ScY%nU9bxp4{M&*M$EOL?f z=hA!dVCgYnfb<+VP#$jIUh5~4_y5r2k1Mw=e(NoH=`VlLxEOp#yn_}#qV^oQQ~V9l zlQ`1|Zs8p1cRu@2w(Z*@U#|U7RbtPIWj@tU_|2Z*KTaQzQ%2hR=S^O4WQU36mB+yVCUy!oM(M;7q z?0Xd>Km>Z9mMz!uSromw?DaA-{MVzc3+C9`SoSbRud~ro)2-eUhFJfV)yC8A?|%wL$SW) zD4Wm6_{8EAw&Mp+jTunI9MunGE!#jDtudkwSf!q!Af)w&pMcdUuk+~fn>hsu$i z#d2szsT|%pN@}-{P)%jumJxDr*Er4PX*~J1oIClBoH_Zf)SvoXE}mZ}7aKOp#j{w2 zTspTw&-XcddYv?$+a{M9cgxi)HOeWMF4f583$=2ou|}?5JS0~y9hd7@PRg~br!^L% zL)XsIcW^(+$j?x2=smEHvI@Q*i=!o$mYihvO2(Y&d{?V{htsh6eGOU6xiQMO@ zvnHu;NNfL>4VCgKr7~g0IDIZ9J56(Z&_Li6YOKH_7FTO|KKOo!MS)e|6!vnf^V+jt ze?4DkKv=leU7;2n^;cjQ%xc=VdeGyF2dJ&`czb0LxP|#}3iahM&gx-JI|C60G0rgt z^5*rIUi}9JV|K<5tfKGt&s>B(@KfPuBc2(?;`$+d&K5&k6LcTsi2|2 zDX<6oGwgtFf?k3#j=>-3D5?=;dh=ih^agl>xi$y=0v3T0-~|kBA=U$S;J>xG|3I5I z${z3s??yvG%YX^^4tND_Vh?nEG%yP9h9S^u-~l)YhA=k5-vl4^4GWWQ{rYL2Z|7MI zPAM5(u6<%=(RWyfoE`FvVH18E3}xR1x4>%b#rS`CC$YEWB9%^@sI}614H+T>qN6o_ z1+9hrXZ%p?y)cI{1iXQ+Vwws3z`oBIaW`;{F$>S~9CI-IL(IXS#C-IOM>==ZKKJkw zS=zO+!ei@)I$efvoF{{5vx_wH)+;cpneUwF7`T+^O;MqXUv=_dG-;FRIS2KzoF zn2*09{xTSjZyo#8R9U+GP5EN&=dynL8d9L`TNDG+G_v8G{L@YoGgEDoFuo-k5R3KnzpbCz97w8=G;M;<-{<#c6_*(w`}># z#|v6y75JkiR?%@kb|sWmTt1;6T;G9JOiyuJ_ytyJIGnDWa{i#lC-92LC#iDjV47UK z1FN`!AtMvnP$D=o1KT8iPH|a<_!HAr;1XjJ`WCCAJ|B%$A)dwa1F6RH zwHKS>a!H`8tc_Miu~u0Hon=j=>|7Ngm22RobUC`aNKWi2Q-9B)%29G~N3nOV&($VR| zmmb}eWzbXb6%nI?k>5;wOHvlPew6AdLt?_EUqoNcb*8QxHE20csCX>#3;eh85J^Z+ z(0aD;5C-qSI@D3g9+4@qO2+VH88xXya*ELIb7kz*@}TBQ{>WU7S0QG_i;Kz8a};3^ z{{NES6Hjfk+#K}-QE$ceb#w=`3D^a*++}<~)MJ4ysL4VLhZv{88?XlY2s{Fp7)vk? zQyAOOHx|JM1AEY-(_r?FJ_85oIBZ0p_wo(&d7kw#*o$~ycmxI`{ueHRwP?{KFc`)B zUHpD@CHRJ%V>rNA1=c_}K|?V3qZZxBA{rbv1 zty@d$cJ0)E#d^fi!XG?qb#wU*_$%NjzSE1R9ihL2{TJUCe*+AS2v%V~CXed=#~xEH zfgI(cv19$3w|UAM@B(~5i@_JBs~9(!-U5pl$G{gBduz`Uj{=jBe?%S;qp7elf`mP{XrwN zwf^-}g~~5hdqvMT4)S;$KTk;C)lJ-cjB~BJi64s+=qYyXXer7iN7Ds^OSo1)(BaHL zM?p`4QOsbJhT4=SJCLkn@XCc6?xm_`VtyWrH!(ku=_#~k%@`%nQM{TdcJ2ChtZ!P0 zX)1PYEqr47iR<%0Gl5wG?Zk3^h*JqMi%o$+tZ%LHiD@XtEXpL6G0H1&hWU7mMXWUq z#oEBvV&1zkQTA>~)^mVrHs{Ep zox_AW`unz($f2rfQg`e%xpeMRx!SNst~G9yYZpF|3-#}NR`;QtJ-JK{R4ta1N0!Uw z^Iyw_hAo=wbKzX2XAN8B!ujoTsd0x~yR=8XxpY`=UOuKVEBF>TkMQ^R{7HEwuP|S_ z_Ig4&rA?=H(xWfu`gK#?f?BosTFCdI-bzwtistABv zw16S8vC1>(HfSm6D*Ptq@(yB9&|on52qTUb{{?$Cd#ml=Xcvs3lMqKji@yZcU>>>% z`!sxlUxsVu!y0HW%;z5Kz$W}%(4JV!v>Dz7lkkkI-SC_}PfNXA-c23fUV{ed-=%Y( zJ`$FYAnAp}BsMEU^$mPNPBh23-K((;}XyE4g7LyxEj2IuHv$a zGKuf&u{K~6(@HEB*6|6!8pbGYT#EKB9_3J)$27+>lvR{d;1$mJIg}Bw35??Mim}T1 z8hkj(0;?Fi7^~ptfmJLH#dw7=msLU;#l1Ej`FE``39YdReY6o93)M|rKA}$>t&Z)E z4YU=PSKyMCI3<)P? z)wX{XsaClz;azU?S#PW}`Ho*(42T*XtPmp^N9X$d$$#xqj)O`h$o; z;mp53-QP-MXrmHi>?DL0XFiO@&-%^cAiDHl(lg9MDtivJgi? zjTFu-OaDVMBEFxLGTPY%y3FxQNGsKk55b1+#6y~?aV@}ov-o0)P3vJ+0#JCL)Kj6p2`ET z0J;jC0(-y*#vH~UwD3k`T)grH906z0V(<*lX)NpDAu=c;Qh9{=?6Lg+IeY?_5CeiA zg6Clp{+slPzI~Nlc#eIPJsIXuO(-r__itkrxC4!XahL-=g?sGr+=DmJ8sHSoeRA@P z(K48W#sWjIj_vF4ig5*u!Zr8d6y~sp^E~@GoPn^KE_%HA?!Di$x6TbqV5rbag%B4(C69_r+WrzEL= z32lUbr-`2;CXD#4yYIVSe&?V4O`Q4zZ64FuZeoq?Td6%q>@T_w@8!3^WrbtPyB+ z-@PyYbL#{7*Ufk2hwDq_*2M+#-Gy0l>->{)^W0Rqd48f?J5w%KP8S8*$~UJ9MPqPJ zdHx`auf1|KOD-SrVoTr%W0Bihi$iIl4g5atJzrOWN7Q^h#aP9z&Bt@@K#Cgm*616T zSbOeZ)40W~SWF77 zL{B-1|7TZBz$wNeCy858jm2`1J>QOLD$Fxo#cg2}&%9U`(_K8j4t!#U@2H7aU=yy* z4sDB8v-n!Z4{nK+1DiZEeFZ*oxx{$I9Wy?0TZ^d;+Ko|qpKI!@cs`v-sa_xHUwd)2 z#woB1jDoI0yZh^i!0g)YtM=m8S%Fc|U0h>qCq5wTt0>i0wtW>Y+g3*?uWVfzDce@X z$>E)aa(G9XoT{EG)!W9(k-gL9{EI&ewmX=H&=|D#s5jmtzM$&^mAz&h3yZjeF%vW3~Eo+IH@sF|;ws37X48 zK2N(Y9W|zw*c58h6C;7HVzpRcmcB#!Ywi$!oG!h(=(#~?9)+WaN!IW*tyhb-LT*oF zVz@*lM#+H4ej0O|o|i6t2KSM$xWStLlbW9_;fX^eE+a;soI6#S&-EP_7; zJq5miLC{?^c4g3DeGT7*z;-G%kc}rD(r3WfUyO? zNwt>rH02ETXL!K$1?I8;8>?`Q?g1MxhqbuRHSb3QVGY)UKa4%_0T81CH+VVAp6&wM zV6Xw}*)`wJx56KM4|)b{KnokNPqRL}1GDhFF%9o$9e9SnBk$%LnUD5CE)cwd{|6?5 zJJ|bqKP?8sc)z7UV^;VE*2lQ7T1L?bU4v^_fjyM(V-Dx@u=m3%@B=mXsRf4)LmVy~ z0T1B&L4UztLQIC`FLNJ53!#NQhmV z7C(`lHQ@1IzW%%1xtI69HT%j*T)w~hru_5T%kt0bi{!^EFUa2;XUg@n<5gopUkP%Bd`{7vpp%@_ zljY?B9rgS`p?uPeEBt&OctqLcaE_?CeB$?2i}7P`T~-PB#E(l+&Bc$QMOQIip;ax# z_W>!t_^e`lf)B|0v;m`-)^R>YOMIz#Gy3j5pN#6(xj_+u`6(jr-`7a;M?)ogqW5l^BCpO)~IG? zoI?9^tWvuvQq35{d^@3x!Zr7pM+|MiE7W@PxkPz|x^RANIG0uStq<307zI9oJB(Lc z7HO%^SYS>J3!H+EV!XmV^c7Q&ed;LpU~xo${;6~T5iP85{p7?3iVSs=Z6>-7zK`rh>!7N zQo;r(r%-eMiN4*WC;pu9K9Z7?Bt@h7Z}%{*laf0kOY7BA53XyU&eEfQ4~b5SmeO(5 zOy|6q7_F^eHj&(o6g?*hwwXG6qWXTQ#ZS#RYRFMnzi15gN>e3$Xtu_@T#YE4jYp*mT05bziSUd=^Auxoo3oX}}X(z@i=pN>yflXi) z)`V|ZpE0|p<#{v*&E<@WQf}cIRzZ(|QE1T!;1h4pk5o-zP)wvmBzk8E!Yi-@dpxYb zK1R!)#vVYd1^3u5mON}FR=Hs77W3BTC@xJglAzH*5i5Ji@^aH zV{ijw@B;b;eb#3k#<-@>7~jVn`aH+AwY-~HTUdoLbQOLdjKDnJXW9$vau44Re>b>= zzb}7Vv<{dF&VVg=AHM@byC4RHF@8HOYh(Oop5c9r!z53*W~jROJz^hhjQ%jJ96^yVmWhUo?Nb*FW1k#C|By9ms^c5 z%MVxHkiTDgUA}2RXL(=#_3h{K(|2FUkGDQkjpfI0-jW}#ESB#s%vX)&`k8TF{`2V( zLA!Vp@r{cHh{9aTtZ`CO$D%A&-Z?r<0(S z7_%6w*tNAbuDZ(R2){)KiIkd+5rJRG;}@?ctucwQ3mifWZJ?uAO?~R>J7(kPDcHVs z5ndajtH3M)hd4~aIJV2lW8V99WDsWymtf5}#o?CSUq#6--wy<{VEBc$ua1`Ot72r^ z%2=sfl^|PJ#>&aTd#hhb9Jj#bI^Q|0ev+WnS5jz1>X>T zG!^Q^C8Vb)uaKKe4iGuZ)QY1{EoT*`<|Zq5#HUjiHA3=>bCgwxjYU)GJ*c;I?A}o# z6C$N#4CmJ6N_={(=J<@AI$HC4U=uWy@=2u{-$INFoHBZHnVzkzwO_JQBsnKj>%UPG zrcDR$j2`N{KHi~&bm`thx{+txtGD#?eKc?cjAHSyum_BS-v{1+G2jj6a1CQ{jV^*7 zLLZ~gJ(vakgg#7Sejxg+WyU!9DKH58sp${4x7rvvC-4j!4B7^JU$4Ofq(6F=uP@NT z5A0*Mm$@v#{?8uH9*4mg@QUd&+=m-5mtoisxyPEUXKX>=*vK6>27w!36=NWtWB)hZ zg7p}KJM10wna}$e<38WaHQ#J(!|&uC90jA8e+HJ}d02w?@EiHt5idjB4BST_jfLMx zOfBczz%_g*_Y1gIpXK0A7G$U>5u${6@alz7u9c7lL{C_k`c4Y?PIuJ{WWk{5`ZV z0c?Q2GG+E`dH&T`HAk45`RE?huNygGf@&+U3UMJY4&S2uG3#mlCccZc&^_qmr!m%m zUEmwe9K=7wv&68#Cd9D3`q#JRqg5+ZcdP z;s?SH_4I=0Wc;k@GJXDB88>~JOq^CKbLNhfS6&?_D^|>u{d*Tk{n5qJShrLz)Gd+g z=iiiDjc>~Jb4%pv*`@OBwU6Xq-+m?k=f?{9-|yGSPv3p2ddt7Aza{^;xKO@r#3wXa zZk?eX?ua0sR<#eGM>Hnoc&1=3t0+*>4hjB+}%Y9=Jv=h5_Thml%U0w-gmEhXZT8vGs_40o#wzjD?ErqrvR>7|W zhZv_Aix`{OJ?`5a8-rD_CSAqr6USn#Vj79@is>q_ipwR&Bi3@ySOrEg$H`M#}b8ak6twid1hLCN-N&rDkinRIV+Q&8zcd>sLc%)2gAeWlgahtA0lR z_f5|8`O_cD*}5;~T-~Q~`qkx7z_VL1|?kt*54@->%-SQP5VKK$sT(y2>Vt!dJ^TX$`H z`P_hy1~bis7Jm?VJ@5+t9qz*;^tq409`K3Akq~PV8>f1U#kHWl;Jb-WNmc)iF%N5* z1{0Z(EMc+uQsVT#kMUdJFCZ?Iy%mjxS`}QQvk+H`ZjM$$d`A0jPiUXKW8zToi@-DN z#qa>^fEgFCzq1FkkK5jFoC9C5C!0>gGcW?rvmT5!VpWH*`nANEV65k9FcFfFnI<_SJiiYt!ng5F#xuN&F*w5W{}f3=c82LrlUdKtl+uZXl07U|%EqP0U*DT4UwySu zcI|vaP8@w#8tUJdE9c*pZ?FDMe*AW={O|XhuDq| zpO}_nnuyCA)`wNB@3M*Y-PYw2w|{&`l-kjqk!mo?(H&8NwZtlnnRa44Vtiub#wdrj zMh1Ovt{0QyatS^oV-aH$;}l~P(^G65J;iht>$|L?HB&4G#n)6^T?H;N7BTI_IK}i7 z9bXqMm@$gmnx1keUg7?pH4$nqmyq*gY{IzqeMX^0TR}@9C&-w^SOrGeu_97-tPGcJ zE5c;!m*G0L^~)i$<%=-2tt;>u#mn}U$x^v0MK*t#C>uVHQKP?kMVjp1SS%-NXG_D$ zC33lbxm-H?jvTFiPPVTr)tD4m1s@MFDR78ak2XpF*pUIMjY|9sX6I-&M{{d zcIn;8_vsM3n5udR=M};n_RC&63o-Y(1xt*i&+p$?NGsd@HrvdiGLX1ztg8K`Vhdj6>iF#%R%2 zj7zw0eji%P)j>aDE@SWtE!P;jN>qG;#-1<_O@?*QQ%svdzlT%M@X=V<=U^1Hed878 z;0MC50B67`7&$O)I(L%Jy?O>UQY`KirU}hMh7-(;C%7iA<*`m3br0kkwtyGl0Or9Q ztVN54f>vM*!(8JB-fjHDebW?R9GC>XgFTMi?#-7jjG7sj1_4xOPGZKxPELO$Xz_`p@9*{i;luSH|oiB?bAo=qVdm?`^|re5mtK@fvu4P+rzXkp z(S=$IVfOrqGUwTHdFhq$^8WiX)7^;s^Rz zbA-OXyhy%nm@VHnJgKa5<@iuv?`YCbE+5U1i-*&s@rY*)hg0SJp%lGVp7C`RYgIEr zH#wXmmyhQAb><4>>dB$HzMTt%kIAjUuRNprN>l9=jYskGlbe_%#gCb79#`x6eLP(S ze^E=UVtR^kir0qLbhG+xo~~kCQoomaC|*sJrZLa&W4z+(C&n)BnAVHJ-}%=cq$Q~azOPiws5G7DT{tP(ojoKHMo(EbhK z$|!J(a*59>?lnAOW*Up>DmHGcLc4!`jGC*fSRY1F9`R#XtZ(eXwX3U`-v?H)ekiNJ zDOGEtZ(|kWY+)DWlogRuxgsp!mF+9Tl~p`m8LYgr>GN>e@@2Ga{vtv)eio)iU+wc~ z+3;DsZ1^Hwsy3EO_2x+$le^)|9I5y$NtuIsZdpTfGzW;h9`uX8g9b>aZqyN>##v|S z*{_epCM8IpL4BoLpYEE=lQ)91Rg&b1zFoAgN_s(>qz_HiTpi9rrZztLJ;^yqnjch9 zGE_#6E7h}`hm}$%o%qfu_3extU!u7^BSsf${xW$!oK2K7JWp%AC1xZCtinE+S2$er zjmbGcAL-hor?QFFV}n6p5)2(g{V{_ED^H+%z#QhkfkEIFV+>mK6z28rKR^aYMCurt z3pxrMf-%Qh`mD!&3=KxvCOk~{V)k|O@6fV8vp2I}+n&uH&z{X(3|@JNa}Bz83-)fF zg)3N>m_=H~FnbREV9$d+U=6r{8v75oe_UHM2F8s?c$P8dGsd^Em-BayN=}ft^c3|+ z!A`IYjKep>0x%f+GsfP{fBT~|zz^t0^kE0)nqLW?fou3K8#6}XyBOo|z%%>}_zrXx z;!V&(@R!i?9k2{6!ruX=W1mNp;#;Zf0`G92cQF_Lj`@Xn9+p8%VLgo4TD9_W&F|qm zxi)_c@v|@{{Dkp5IvD3z^KO_3)-o*wzYP2UoAS5d8GZ-n0Zf}cTkDU^od2v$nlVF- z8Zy*=z3WeRYkW9N+Lp63AGlw0*7@7<-T0~|PJc>k(2*YmC%`OlkoszTzYsAc=q_%& zt<|zM%?Bny^Fd282BJ?+6M5G3S)1pGzeNKf76u-IU*Ht{M(8h@MTM%l;M0LwU=nKf z<150bt&het>xCC&?xIC9Yrz6}e#sJ9x_r4T`}kvd?VV-v@>_q=_?P)Fd1n$4*TV0Z z_wquSJZHL0oi{^fy*OK*SvXr}JUdP1yf96cyggegcYP=~Z|;&GzT2ky$`3bI%0F+s zCqLeJNAsA!zxVNnPLmhy+o?jie9X&*R;KaufExTfAoLV3582a3@aqu!3ac28(7zC{N~%}mE%5hv zIm@oj;`KfMkLf5Dn_@aj6N?y=cr98A_dHDnT_x0-VoX}%6IjIf<3HsR=DU1inhLF} zrC47z7RR>}+7D$BS3@yfh1SNswux2n{kZx{D32JYD6?#dlv?~he(Px}UhWRLKkDDX z$K#m!f^5w66&-itRg6_&7h-N*R;j6o4OoSk6>7wJHEO9->(#1-Nz4K^v3M0}?hUlVr;m3EuT*QJTX;4Y%a1RIQl;t3)To$`iea zKkX{*yL6Jy-KiV@goH&$YV8$xB|I)%S%q5qeTMXsu=pX8GlJa6B&~sBoD!EFtLOG) z3{BUwk%>9s4Cl_P%94IhumcLVfHTSk&@T@ zhd;N{8nW#=c9J%}Psg}~93JY!!7Gfx8gPsGci1AlOj zIWP&=7}sbmjG@W!EPf#NO7?a3f71(K3dUd+H~~iBKKnJlk9!#VFR?4`8a%@u&Rq6j z=CdAiS%bcfGly$51NJxG&wA|t?1AtL`z&+ZHDMvv;2HdFXc6#AY?`;{v+u)IypI?h zxP+GY4SYrH$!JN8vquw$16T15Scva}Mc@*CJLCKvU=)52Yulb~?8bfG$2iP^20=^* z?7^DGH~hQ7HNP2+#55h4f@_|GRTyV&n@?-Jrd%>^oQ#?@N&g0D8vG9G*1{6_WB5ka zgJt-B{-*F9>w7sxUX2;{akK~iF7OX)MMxv9Tx#*I}%eL%)4-kHkAEKM=Cf;?WqFEm7bL->aR^EpNN#o?B% zpGSFS9P?S2atS%d#LU7h^r@4Nj-rgxql5^A?nwGJ?LZH zH;#cr+;Lb3KM=+^&#?}BvCAN^23)}y#vV?~ICJq+zz*;NE%(@aF&M#g2m0)Ba^5{<7qqW&*UGI8^hkrZ^YQ^;RJpQ+`?Ma zo8|Auny>;o2=_cKWVEsad}8-m8{V;X_%352{(0~o{5R+o#L2=b{7(1+R)LT5OFUyO zeiMIVz6rep{(w7R8_v8Tww1pXe-Hi^#JEsjo?K_vB(8-R8`e(B%uyRwTq@%xPgB-G z_aHWwTCezO;0H7-{7}P!gtdt^ zfms+slVJ>IK@TF|8b20(AUKG%VI2HE=q=P-;ap|(6>@=C8!kd0;a%_q^;*dHfmz_5 zJ`rIOm6D>h>8Q_=IefUDTg`96KZ>suuH)Y$VuLx;dG?DhYTPg!#&@Gv<&~D|+Uiq! z5$1VDUU+q;e7#JO^pDdS7mdWLl#d5W-SgxG%IfXiI8bj-Gipwg- zEBJtnQRu49m!KI<=V;Na^*yx#?kVe`FAY0$Kqsf*F-#B z1x7KJFvD<3)Y^T!M+>*$8wzC=E^cf2 z$LJ~asUypIgag6`E2~h$woA{h(xYEbiAjo8ZXx$LIV)M&gxHkS+$3cb;!f!26lQ6B z%AhFD&Kn>Z1)0h#)Tf0%h{x^TzlYYmMT;pOTO={5(UM=1r}g;@M&_%J2n|N-@0X0w zy^Q^g{hR-8r4B9UB9qgDR)W#Tj8R|^7y?5}K|g^vFlyryR|})S9p=x0MbJg?;qW{R zWHBwqHH@CoX%RO55W1eSC_I3JvE9?TZzyxR!jPnfZuots8qbIPBprf$w z!X?Ho^#3nq_xWZ=b?y6}BoH}@Bnjo5rH%@%oTXM4LVy4PHYN!$7y||eFc=#fhwW`| z=Y(+}N1+H3IcJA`?!M2t|H!=0XVe<2OO0fo^I|@;YF4hby2p3@MwoboeP^HRdV=#f zpYv+&(fKgf{1r>ftVNYBWF1?ipTqUsXFk7o=KbV5@ofEv4UOKuj`d8=ty$J^O!|QM zeXx*m-zVEFyGQRvJ`dYT;|R`JusC?deFrafFORq>?}g9Fd&feq@4oqT%y+03n>mTq zc0I9QznB}z+yitcxkG$t$$75V-Zi((UbM7KpFJ;X)YD|d%<@TXzT%4T#fVw)j;pwt z)n(hQUkV$}j%(OLk2%E3Vg~G>rV5tHS}$Ty_-*)$@CVJq7+!I_ZFQ!ht%!BO8<+(% zq&|aX@QZcl(Yv;|7XF}%uf8hu82b$xJGu-WJ2u{nchk~Uv2VsCd-qsH43Kw(6H|vt zKUeprO))Ri<=eKE%dWdFa)|KCz2Cp5-1oq?^2B4embYK~M)}9vmy}Om-&8(+Wlj0y zwbkWsZ?29Qk^^IDYg$Ro|8oGN;FJcdgf{Z>;wpDJSf#uC)ery(wj%HM}0rbbv{WpNmfaIdF7cIRcqF6>#37s438v> zWS@+Zz44bG?=1VCm|olez?v%jJ1;)g`I*?+I;&um&{H1kjD24WU8PYErB`3qPI8;E zgS9%VB(o%|)Z=a~Czl+oPaeT5y?sEF>$$-n@cO>J6)e&*#@|<>}wF2d7|_ zic5NYLG_rGjJ2)zbdzYmm|pOT<;Q-}9)0r4n91YH@bM#}UW&T;a*^dE(@${9sEMP> z=!s)u?jA9&XAL|%*hMY373<}Bb~kc%XeLWb_qt_e;j+1rqdcX3YUnonIAUTt=5$8P zO6#mjyD=`mXVv+uqZTc{5nTnpbZ=Z$X)tS6mF26uqs|L0MJ?7f=WmQUnQG=^l+;oz zoAa1vl4Hpt;$Bl<$v9a1nuE}Bc!L&_v9jv4;SI+*UosIU!9K}F_Mx9RcWOKKag24o z0e%)7kh%pMi(S~a)K2gVTWlY;SnoG}eILiMrH)CPn(UGskWAuS7>8|TPi<#oox`=P z>m82`XdKu9OSqAmJ1ZWAZQ<*&u8DPJb6iK*kG0V6_?|9en?9OMb-i*7Z-h0x!zU$FMFw4rd z>l!|oP2tnRWf%iLdLB&YUFJPIMobOg3|;3k8i=`s8_b67!~tVuHPY~|?-~tm_JW1+ z{%q%O(&v1bSkF1waqoauL$k?svD$39Tx@=m&);}s|$Mvt)~83 z>?emOYTB-B@XyLv&%EPc8u2LNXZJPQ{w2XI$t`N+fB0fGk6>M2`FQWbXc(pP{bX!y zPp!9Pmdr)2^GPoEW|Tv%_huFQ;gmO?!xS@{tb#|9Npd;Y55^x`1isi)wSXuoYM zS{0kpR-X7xYkBgwZK16gd+gU8<woNV*rcEp} z=gkUM>6|ko{7Z6&<}6Wzy}cRF%h$t4B>vX?g_nLoJ@FM$mv-suyr^<~b&YR+Ku z-mrI#0=vG{D_ZSBk$R=b7ZYK=MY=VzoW-- zY_4a};}hB)#`QhH#om?o#`k1d{Ezu{&KWW!*l*tAC86Qr7XG5hhgR=@?b?`Q(K`zJ ze!~snGr<@161qtGc5nwD4b6kLLL<>I2L{0#7zK;q6udKg*^=lTllx*0no9bE9FJw3 zLvLSvL|>Ax9J+qGe|=T?#~a;I=S{xP z-(Fc-K7D0TBkyNXlT&a=vP!*AodvJhCwxJ2kLyjXlvkF8p2F9IO+I{SNwA7O{XjI8 zx__sxr_fkF+Owd^DygYdedPnfDOpqBvSX4@>RL%}HmUn|>U@&R_=L}=zD`$3eWlJP z_2oK$B(u~xqrSbqocpAHlH8K(_1?Z%CA5`>o|68b(pJbKXH~aMc z9I8*Pg@#gR6}di{?^9=xTuv6z=UCj&eHh)-!SO!|P5O|sAN zx^99?>YS3ko*ZlPNkdDCzTx|+vx@O#6`T^h@|*VZ_^(=nRq)E=zp~yQoKk0%I;U8! zH?{H2BRFtG|1y01(3qoV__*Q0Dzuf+lSf1?x9Rgb%92$}%7CE*%NghPD+7lQjC%RB z6pb&)KI+o8OmB^xBK29+yj7b;<72`q)91~Ink?q?*|24O#L}+VusrJ0UU1pw(zR%I zu(>h{1%QI zHf~(x;tn1?x*T`fX<^6xwzi*`4*HAZv$+?1Bfm$S4GysltKbsNGENZRf@9pbykq$; z{1$8?+bd>N9ESO6#&semeio>|@g5U-Fn(G<6*x$Pm^CEWH7~RG- zLVH=fq%2&f4#388<#pGEuA`0|exdi=bo*_gy?Fk}!@2X0cvftIQSb&v5Q}R&&BeO+ z;l1GxdI^r9W#A3kNxoa-d{xQ6d~8>U8@}w4O8fYNeB&)qx1WDWyey4}?m}mozHokg zF5;Olz542yBXW&*w|P^1E{>)DxUS|Mc@~YX!?$GY!p~n;?!5Q5^3z}5Ri1wOyXB*o z&o3YCTUS1LS-le+As2a3Fv=${FD#$DvY`B}(SN*uLHT%J#V+!eHChW^32j7NY%gDs zoa9emUsL|}#@gsVdZjyZeB}9Pd_kJ!_g`FE@ybh8?hn00`*eR*rxuHRuy=lWf6u&N z6}*yhD0)1jHoyGnkN2%8AHKM(e6V+E)f#y}@9drzEg9wQU2_j;@9dmiwOW2p#-m`8 zJm(*DWi@fqwXLL|Dai&)S26EU+*O{7tiwjM{@n@y5Q>f?JK?P#v=dvtoV;FU+G zmpy;&D0>^clH5}7d$UL~OTD*@Q`FDbf@S_Py=c2=E9$`gsk7{SxTEa;)6BB#PqlfJ zn>jz7<%MWf4iF}J{*OJm$o2RX43ez!>_gL|C6}b0lIz=lU&YPhl>=DickKr-Oft*A z^8qER^zQkEj6L?twgcLuzi5qGvUHR2En~{Ww(*hEGic7;>GRqn z4u#e-X!y`Fc+}wF9GZ&Sw9D5mjaXVSD)f}ui{?k&TXk8oUW@v-;%f1VUJf%>;q%#e z$%e>V<`0^?WPY&9>hsqJr_fW_c}y^^qdiz<$jA|8*l2l5Bg&YG6M{!FcBHPW@ab(O1|KdIBE6E7*n2WREp-?wI@7{W%Z*&|<#C5yOK^ z<}Y0mdeGVD4UBp)=6W{ABBt@%nlHe0UC;k|*D?3=j7Ds&G5o=ogiSPKxWKaglP&bA z-Png6q}6x^oWtfP!#Ezp*e92Bn`5+!Q>wX?lO-_6_?DJn6!ZAf3)o$@6fdyhm_hUV z*ce++W6;|#H3r;+TlhDOv&rl`O@&sGEP{71m1n?+a)dKR#l72)?}r+dpMuWT+KzO=S{v~Oiq6Xn&V<-`38%BQa`EFZr-zx?fG ztdebE@JrNJX)sIrh_aqa_=)(68g<}4ePdO`>i+lJ=a+xJwK13_;$pi~TPgCu)oUuO`@_3>np^bhr3H=gW*Q}9Z1O7FhTD0K}bmy<(!vr3L9 z%jhvm_Q@)B9{Imvl{%vwY&lsaVpK57Ay{SiUps~ zD}S6`v>i45q~ev%vi;$X9^EA8E^O4IJy;wqE{QpQ8gnXJ$0Rrzg? z%2~!Fnz$4(D_F(4ZSs@Z_jQ{tD2v2P%$*aug}Gd@O2@33<)l+iDW~`ASIw0=apfPc*Sr{_D*+`{&y#^M;`c*8b&ihb=vvvE%tD33Faz35o>^Q^}353PgU^n0F-WS?m(#%U$A zdG-^Z;Dk+=Z7ECFR#ry8WF)tuy+JxCFd{RoE!@Cz&U8jASYI zbneh0<htxL_ zqhi^+$5HOfcY{u*9-(KT7tuj_xJ@mTHC24Y#TQrc7>ydf-ic>QCc{VUb;i430JdFx zF5d|55zpcM(@roS9c$6b)unCLoOplyZ{DZoKD=Mgg*)UnUw6wb!5kQcjmHX@!FIME zkJ#UR;3Qm7@7<4Mja&8^S=9JFCjZOV!HVRDS%uC1wBi&y{!gt_>#0 zm=i1#Jc3*HEh>N8zqou#=h(lX(pwsFD}Q@+apfx#-x6^y<_T&v@hPvaDt~`tLsd`Z zt&7Uv-`re2eQk63=NlK5f4qKS71#Uf#`4K4>%$jC-}&&R6;+KCu_ydO*dKKAEIdmN)=!z#%usilZt2~KG;Och6)dP(*)6D(3^nVo;`i0yi9 z$Di6GHbs0&Hn~8SQ%`9!Nkd1WrG%bRV->uTItm`?y>1Mj)LXJjy|3#kPyME~T5tG* zGR8Lh9!{xh*CvZ(3`*)Jhtg7-KA>N=MP842dd5v2Rpu@3Ds8jc%1QlBin!Wooh_lM zU=?vG=0ra8+%wAP@gpN%W#LNoM(37gYnOyJBbHW7%YvngV;&*(`uTv&D}-6*ES4`+ zi{;Kal!x^p(gLTD2_ZQN$|U>(-Yg-7CW%LW5xYXUq}DIw9sMJp9Nb%TdSn zEhn9RTEr`gTcJ(x-?01mLyu47`G^sivv5(c2pt5gOm1t9^Ya_oC-Z@@it~v#@jISP zXTRAuwO#Ou+=qQK_SSxEsO|1ES;fA&kL~WAJvWY3(#Pa{c!lr7d*^?$zcD@+%XAbP z2;IYPd$v%lqgV{T;YuD?F~OL)b(G@pZO(t6A2WqJ4jT=V(Ql z_ng{{9(TAGxzABs<%Sy~K3LrBRX5!f+pv(hV9i{K_=Nuno5*d#FP=*c7rf;&+_?3k za^aPilzZ=4Q1(85b$Msc`IX@o^77KCH7n)?qhvi= zJ|A(r|9tEG@=rO@Z(mgYpLZ`S|MQOdhAxRb=}%ujKRD$dZ)^&#k<%<+D112LPa5~~ z!Ar|R|M~l?>!JqkC;PjDP2S(LBzPs*=EY@k+yTteV3m({tN%8?;+5TVgIAJO_=J*8 zl2>}KC$HF^%gHCHvmA_5=qiU|mE@H4`y7l{ax6KezK&6FN#)P!$@#I4MUqPn7Jp(a zIVJg|&L+tr+4trVKA-ga*k(EVWE1`FzqB{k@kut^(&U&1tI$nSJBj6nZc-mlM#;X; zDi|d>MW3vaeX>dK_2iQD=_Hrv>#fcy_2oLNSWYb^V`=FrcqMfeJznY2S1MLXA5XGM zlU*8qpX`%i9{qV+#VfyT3xAIsM+_>fZ;FWpH#6HZAyyT0oRAa(# z&}9)%t8VU!b*sa+(=CDnXbiK|ESXj4C`TM~bUFI?)U(;N7QQGPV~mZ) z1&(9?*wEw>jN<)i_D|oCbJ*Xpc@Fl|c^vCH^c~#fooYCOt;AfOiCx4jbRo>eUeiz5 zP?`e%sh($kXeMklp1~>jhtI?^UlLyNOy(8nH*i4c8ftNfEpeZkXTf6b*L`^=^=eN$ z_ngpJT+21tMGXV+>xf0s*i5!p-Y@&g=K77E_BL&>n3CX~^-1~K1J#OJs!gc9m@_uPgzP}iW)Jh%7e zeQ3tKhg`=U_Q`nOWEJOh9-5ZVA>wGaUK+<+*61oU9^4`a`BF9VuDv$usa&~jTj(p> zZo4h!WV9bXag1j$myo#x=rfo_4P4`D@^8KF>T<_jE6U@KURz$@wWYkjcXfGhZ+ChB z#Z}RNu(!K>^y13$@!sXZ168e(h2@i%76!9?v~Pa2&{%TLpVwALezF{8En-z(Ul+CD z{_)zzLR+a=WlQaLY$~mp1)B=JHWTjvpxF zRrJXwb^ge)IQ$QL;C-=aKg4n@rN+6LBfHBYi#x zTc)Ax`b&GXoquj?YAlu3(ukRDu!BIfMrr6O$tc;s@K9&8x_*LBdUTcc z^86p$o9n5kB!B#$u*$*sH`XfGth`48Kpy^v))? z@EjDl5Wt9!C=LD;J0Ybx&IFA&2r zY)loqO9M()Ve9D)Y@){3ld&e)K{Jjs*dje2ka>RUwdIX@SM0gp?|RI^UL|+rIgDcy zHdqT?W%;t;BYnmmxHh)(jt7ny5q#pAaTnI$|M70Uf9qQ63fKW}vEO1k+>^$>(pi#M z=rfj6zjBOw6B|3Bqb<(EMtgR4H(AVnIMQdMw;f}M!_dS?*KEEZo>%+{7V&)fUGRBf6z@9YZ*hpD^M-q>|%d3k%)Uf*$9 zd2`oA<;@)%%9}g8%X_<*RQWdh<~L()|NhGIn9JuMudS(S>BpRh>!Q}%Ki}REaVej^ zysG^D6*X2WeMSHAzBT28eJjg{`&X5Z_OC7f^X|pv|NCHT`QPtf7COv_`@5q)f0akn z9sZ%ezq+CPuQxVV`Ae^DE`NKainU2rd3Vp^XzugfU5&c@yX64UQz}luD&kylNS#%Z zPf|CjbIHM$lRJ`2vQKR#*V7l2>j(CSj#Wt=hQUL|#vW}Z)tRgzDdT=J*( z;FJUUUV4h!Zg}MYKTrC5qVEV+NtQ|NBv~c<)LU%FCdn|#DfT-+NBMn6#VZY;Pj4+H zdF5cNA`hrHrzD4D-(-_tv{#x+EpA1;iWn3Ao{p~0h^ZYuc0`%CXik|lZ9-YHYF=5h zX?f{hzqGW@m>m9~Mcw{4E{OWH@^{qApW4|H^B#(+)zri10~$JJWUvZ95Y0tCve??x zRW7>n!m@HBeP7N=SJYf_u7xX>hCTFuo$aNA_`k2GgYfTQiPTWe8#yv;1T94#u~-n> z?2k$GnXh9xwGchdps6??EyeNu2M-B$!7sELwiwUgkcfE|@7mB)oKK^dxE9TYEw?Y9 zir5zVkahFXv8yy5ejj-->V%0|krTw9!uDVobvN({ZeT0%QpQ5Co9sI7z#w!6J`nTh z4Ie)~>^>H79J^>=`V6jN)7cU&83jLBb}c=d$dokBK9Y8JCsvZn`%WuIx@&#$`iM_=udYl#SVD9YL&;I-- za(m>{IIn$i3;lvV;W*F3c00FYykF<>o;))>#kJU9?2_>-GzrJLhB(;PIbEUos6Q)z zmM+8xW3J^h1`G(+_4&%rp|ALCu^b-_?Zx}@-a>m@)g5uS@?O*pF;@;Y(_3O(ux7<>0xc&C<6X7V|5BI@`bl11PUGBX9{#f>0 zH{Etc`Nn-$l|MavbJ_9y?d7S*Zz}iSv%GxgzK!KSe{e<*|n@D|?^W zQeNLlcUf5JDf{L{EbISzYi;>|-`f;*X=y0YUR_ho^Yi+eravcRNdErn>S*FhXd&`{ zK6r6?aE|)>8hxhHR93|Cd_v-XKi*f>>&GaTKYDpp@D5gSp7-`t?E^6_dzO}WcP}XK zG&m)6mE8+s%<}s?dg|#XpS-zacCe>nhG4C;dPx1&(Nme;<`9OUn zwUpFW>iS7zEVxErwHk*s{5#1gw3S@W9*bmrO7?s| z$tac&*6@sdQa4Ff!6$VV!6^r0m7Nc_H(E#J|HuK-b~JRAI-exBH2I_vvl4x;d}QnV zIQn`^hRK@w=J{#P_4E7bQBPk@{cOo98J}_}-;esVbq$3ND72Ga`98T`_x)siEk6(a zM1Bu$IcLzh!7W%tT*|7A;xm?%xr^xqt!4J&nNb6do}!Na{N)RREzHF%CT05EuFzA& zsSFuCq6{29q>LCpCgyU+FN;?#sp4lBso&8Rxj*7moQLm6P5qhk=L8S<-R}Q5+l~SH z4;dV~1f2zou+y|S_j?r0g2Bx5~;1b&_&7$(%u(j+yyM;mcV=^}d*RT<3i=7KE=)G4hnMI%b zrQg9A`>fFf+^=`xy=0!!+(nDyKG|aSmpX|XSNo@qk{S%V&3?L;b6^-Yn;xPLneEO+Kf`mj zyAIzGUBPd6_uzaO#xf0ojdZ-4t5}7$gwOcIJh#R#DgRyb&dgnP>N)2G|8|-)YRc63 zPGacLoK~$0Uco7Hcdx$r=J21;fxLs%E<7i;%FoCB`iw$P67#)cdHBX?PC0i_vW#a} z2L_Y*F0{{^6Wnpvz2A&R*GWcX|FH>nQGeHb0miHwr@>$jejxnfoq5-^q74^rj-2Hy zuDd#T#b;j48$B()XS5XB4S$VmYP1^9Emmdo<)167HeDEck~p8G>(&LYc-I=9`^vrd z7P;5%1NYEY%ta)2MNYK21#Z9R%jGNge6ig7z;)$EKfba2{-Ljzi!YvC7B8JvwqCKO zT)cHA8_FxYww5<{ua4MOz92cm|9oS0`JcBp#@xwOot8Dh8gv$O zC)-C3j%MCL`NiU3zl5;8dW)<2>-Oux{Sf$P^$tc+;n+40BrXa{4JWRm0->*nu?IeZ$tf;oHuGsMu2 z7&kV2C|LtVY$|^ZEhPCwya}HVj=>PPf_CCq+J}Z;H2McWQ~F|3Upb}UnZXhGg|3qN zievDPx-ROwXy?kG9XzO8GHCd)a`w6B#+dk;3FZjwnicuh?ec5Xmg|~b zCQWN8r=59L89aPM%oB|T_*-z+wDyj$W9}DQ<-Lm0wTy$X3Y&vtEGH+>GBASp5x?ba zXXn{C_L<#g*SudGB)$aOuqo_VY7y+7^(8C2gQ4`^w>&F8A@@IP?z~_Yyuv3Trp0~W zmedrcAAZ4kM-`!Gq^9m@7X=}iVd}I{PsV8W1I)) z(ir5|c;>vmdoeC{nBL?aU_QEpdvZKxV|z8n8}GhgV_9?21!1%44s;jSbFbLLsz1mELpoY{6AuC=`8BTxzG8&r%RVMV{hHB_fM<2`73vXKkLS?+*Ypp@-1cS zHCL6@o6Whfs$6m1rDf|EHkYfmU0SZ$c6k{;Wpw->Q-4zJ?Mt?&x~h zE-G*BTwOlgvm|QNs#BZ!Lu$FnSN{8J-Q{m$SoSY%a*5oVY-*%@xMyDUStEsC#(2cN z?(Qn@?3xw(+a7+K2Gf|EP~DVI<@>z6s$!Ryy35~QSzrF~`UT~`U%w!DB{UX!LVFfg zT1ul&c6oParK|iq-wz&nyU{FXtNVabGwETGDqaP<1iv)&75%GEch*{^k<@u4b(0*k zj#09`{A5QoSW0mBQI-k^+lT%`>p{M9GHYGF^ zeDeG0&H0s6M@e4Ep1&ts#;imwxP#~@*yWL*w>0&W)KPL@+aCE@YvdlE(f^EK6}+N# z%` z3oPCPJMaT#-VZ*(E9@wHPnVFJ>^w984eyB4<-74));dn1uh0kBVa!6S=MQ3wvsV|L zUx+@VHp(ge&M3#7d{Q~~q!S}<#&X{iPbhtkJ2u*JC!A1@J^uJ|%n8R=F~BEO%O{+A zN<7EW#~ojeIsSxl;;E;Vz9*bmjy>_DXeXb3Mj1A0bg;{Kb1hEqj2IYkxvq=1Qoq43 z>vwqCd-iYD`VEo8vuxGs zXx@iLhdOWQu=p%+p7_|x=Oc!sGwiSR*~?P zd|uueE)btVbKoCwEj?bLU8oC-S=^WVOI~p=IKh33Q^u+04!{rQhonb&KKbEvFPe@# zA>SR$fgf;~_la43PV&j!7oHLuw!$Ek3uML;t zP1|vaxeU#Hq}_hcJuz?LO}F13x!WtxKR+}c^8uL)TimX?G4ipu-Fj=}2wi{c&E@kq zTvsmGN&{LObA@*;UJ%djx0L#ie#4lr04Hs{Y)iz)tv!E9x$N@U<-WV;m6xC2P~P3O zw7mb)yz>6uIpu?w=0qRMje2eG@0t_sgWYqZC4aoPYgYMS&+KTq|3{A5-BsS-JuBL~ zJ7>hYW3Wpwj2M$fEjKzz)J$n;Egy;j-bcr|AYxf)D{`B$3bqNpd9mV_SZ~Cz^p01l z`+jmcS>>%pU6eYjq@I%fp;#sPBw6L*_Pjv);B$Mdz2R)_EE6FO!DEj0UY*E)z zl38*r*(0@-`Tt z^oM~%hQ{ww>%&KnDPtx~Ea!+d88$q|=@x$P(=@b1?M;{&i(S7@t zBaS+%xo+G(#tuL7sB+j5M@HVY-0S+f<4!s4^m5iY=azv(hlFNBmr?u9dr+s>y=z#8 zUy6>zN2BF);y2Cnn>)$AG#ZTNf3+U7(TePY@x%vXATbtM3&s0V>t@B8wUMi2o8SMg zNz3w%*kqhYgTi~see@JI6gRN@>=#?g-qCU_VikHN&Vh7v!m)L*$1J=fdxR;+#-ja9kGh;L9I%@ohE2hDSUy9|iYDP~E zUZXj92hK}Bz$thxu7CN~U@iCVd0daaBTmJ3&+DCfK6(ZXDeCAA864d2+I%xu1mEI5 z_M3(ymc)6S7o+m=@k@FR@7`yT?18Jqm0(`a?t1Rgd$EkCsv0xrH`uuPK8XDhuZ&;t z)EB<^#faUJ<1MZSm+|MQ4|DZRHqsnMeL3G zcbhN0B;H5XjmCt0bF`!@uD!P0{FSei`+o3)$SV@dzhdp;vT{vF`SLCE%I>E&mbZ7R zgEF^#u&1khuymG_s$8|qjbgox&6ak)jC#5 ztp&5>+=E%ajYc9rS-i{pd(_=sQ$BrdUBt4yyL(A_XJ@6U$P22s@JGoDYUBYWm*|s4 z>OBp`dTJ@@(@93bF|-%!si9bY<=N>~3ogMJ9laN1%y870T`J-SMz!6cs~w|r*37C(Ei<>Zp& zm7RZTt(fJn4R&edCfB*da&SqnSe4XL4(R{b9=b|zZAI=+ok@BxC%ZIRrREcg+@R!{ zTHMM3x=O=Wl)1@rfW+5k46X4ef8Fw#_QWq+qCFOTf>BzUyn{la%*`KW;8Giv+J=mZOepZaeD8KBdo56}$93?znQ?vB$@{eVwb~n4`*JM^!asPCV_@ za_X7=${FXJT}F%@Q$~y#Th0{^F?v+oZ~k7-ns+ge^r+Es|HqwtO6hyjiBT{2+`)r_ zTl)1sH|pp*Ufnu#4A4kUIPKJO*1!SruJ9Xvp#gX&xWv1QTDuDt1hcSV^e=3}jU&2(oEQ@)F;?Cc|cWL9X5x(;bXux>>E4FM$#zQ>C8D{_cV5rKI5DkCeqS( zl)f-Fc=&krn5$eO=dbMMMR6X?!Tz(!ww*nAQ0Nf+Y@uJt^;)^=L;$AqK!P^`N* z@g1}o=cTLA5IhS_%e}f!@5p<@J@`kARO(t7F8p+Sg6-{b&sfEM51%w4>bc^f(3%?a z4&^&aUl4w?&3(8p>{8cVyf2yu-AH{lxk>mnVpe8%1sCB@nv{Ac-n-l&akw}lYR@fM z8u}XEPww-6D($MX!J1-|m&LW+gLhZO3RUw0RG)n{pCqo5<0UWMyZ4^>eB}RN9^WxO zK)Mni5O(ow7>K^p_vF)JeumS}J}2UV_-obV^Ih~E^nDb&eE!83mDOUVmn;s=QLdJA z;~VwnuK&_4k@K~3{mQcW(pBZb2R~n4+Ctk%VinBNRo>k>GxQXi%6l)&DDUi;QSr;3*}*BvD)NriMM=LxlKp{ppqeQ)iO8zEWqH9u}#X zCfBh=F6(#wrM1#m4iP(>+>+Z>HZdf()aM%)UGMJ^Anh~>t5=qb5RulbW>oxca0H0C2z_s#r1SjD!`RDRPM>EV)!O)6fQdH}mT(pbkPfBo6CXvr+>pE!_#LkCB#TKUKAUF|V< z@`4p}BR`p@Vtmnx#iealN5rGFbheinbE-I6^=bX@RZGMiKw?w;FO0cwTBkN*Sf+($ z!UtrY<;ANPMQs%|YsIke0nMD>74tr~&a7fvR&88Y<}F?n_89{lcGzK|KOA-RG3A(J zk1NNYcv3m>uUK+}jB!pWeJj@1WmVXNPa<@T2<# zQ>jU#cHWVFkBMhIs_(Jkhw!c}V=!ED>`5ob*tr7+mQ&9>E8;7TJf?5(n)Bi~{x=MW z|He+76q=OSm8s^2o;5RgYS@U%XEnLCCG3P6dhCXlHQ3p3{w1Hwz@a089mN@mU9q3M zVfn*s6dQ|2Fai6=zWLszspaPrFWEVVAADCfiRB zQEx8eK4>$p!Tw?bz7@U~*Tw;^?VPxPw&5I>ozHLl3opAg#$6j9xKI0dCill@RON1| zOO(|8J(f8VL}=V9Z16^eU3XmzMJ~KC!SP}IpM_8=lJ8wNoSlM z-0k;>oWYER=fA~gSe!3aeF5(tt779B^JbO#OS{S)cU)X{KC`X7`0OQBya^xAp04sy zLtDWk?_!n)rv$6)o?hPH)mik>8tjsqi~hYG)5|-(8l5FLrV;BBF)dx!D?X6lTsc9>FFDu**R- z6=SKZ)O-B$IKNP@W*=jZ{%lHl?B`Qs`LUn1lt=%oVwGeTtRgOyzA}2kxH5Fi(1^E{ z=QC%Cxqw@PQ|2#SP&#K%FXN|8j(G?>yJnV-Su=u5D3f6}*E}=(1u|`A$NwIO2$4g5(3bMxSH) z2CJ~OG#7pv8VUBm9P|QqP`+)p6HYxXY`Xo$4A4rjOELmVjJxq3`<;7ETqCq0ymn;8TKGuJj@meUStG}d4X&Ym@y87sI&b#fFo6Buq zy{mljj@u)?qT;El_Kj<(hr;i{Uh|dE5ZG_mpb7EQV3mwd!YKF(2ieE@=n|g8GKRuZ z_P4KR!cFc^FE(iFRiCfsX1@NqU=qG2oI+Q@Dr%w7SLA17DLGE+4r5EP-CfjhizGyYS26AMv<+f&5YK%X{_CF{YATj;>kCYBAG=9Rm@ z+Fc%d_^R^et`+62oy#Il6;p(s(%_WPT9Qo~tkUF_2CLNjI;&t5`bualyJkk5ZB-9N z{H=Pr-Q|=0tD{x1Dyw1{r&QzSFI4-MjG;@%XS5ix<`*M;` z>OCFhwWm4`$trbDv78K&x=Qvrp8H#`wp9$0+<{AySCUn1d+D*(;1qrGNcP5hx8#yT zGf9tcDEF!Q1M`29S(0IrMY8`_tb$E?w`7!Ll4O+ZbG*(exg6Y5W0hRa*xKZks8##$ z%wP+8NwP`3Z*oY3S1?Mp+?MOXFZDT<)q!j1D9I?al+;slIo2DxN^;1-_ym_Eo7kS~ z(brfdbrrcQdwS+ACg7!{nt zpWwGk_~FIBs!!iCCE{+ytH?=~`!i#{yyumntEdB~-pYiQNoBFewn3lqCTuo)!=}mx{Wwcbxg2>ZL}u7COKI6 zL`*I{h`qLqKX8&b7j_lbxZcW*>&n9J<+0s8*yeoh-F9%!6uq~9JNWrtxoBhS;n?b3!d;^=_ulrXehXZPgT59&J{tkm7kA3ja_{64>1zj{~HOS$@%n<57b`)$Olo6e78X)Kt< z-!oq)K8aZNV~;II9d~ThwKjevP4U>iv3$CDqz0WAb4l@I;XRt!k~Q7slFwaGZvOIx z<%d7mR-S$A{IYNR=JNUrE6RIfLw3z+@=C=g9p!`Foxv!y}fg0#VWfNRkihBHuqsQkB~ZY+9&%PHCIAYSss2LT;mw^Yt`ES zNPO+C`As&#DEQ>{?X#-7DBG)L{vK@7TUSYjNmkL*SF%rCCE3F|{zzU)M#+BP;~1m0 z$t?D@-dhu~UT2itjz#u9GVK6XF-B+6Y}46Pcx^z}U4b^xQ`6xz!6huRL>a0qP# zn_!mSjFQ}uY|@)Yp89P|bD!Kd`i6!QexF~rG#Mq=lTV)f&9w5wuc!8KN<$;jFCt_J7R(LUD`P{2s$1 zv;?fehk{Emi)DI&W!rFxeQ5^u{n>T8iXN-v{opG1=RU>TX8cL=2D|-lx#b|da_}+n zF%?G~e%KM^h{LNG-FP3iMfi*~#ZQZ3?Vr~Mp;AkK84s3!e{08v%B^E3+keRcB zT_R3<>eOH@dJ%t+F~1?O3O@0>0gqr5aXO9_`@}aSC(7R}-!0y}{|DbMcYp6Y<)%At zFWYXvwOoGvHSry|@|vr|hh$%W^L~ez!(8)y>37yy<+xK$E=QkmeCRE>#he@`_dBCG z*SYu|wV&~dcPt%X>R|bd=OjGBzc9tdiRf z#VU9u*#xJgzM@Yisq;y4OKK(+i}2TAlAgZKFUcaQksPYWC42rlwaF*RDf(RQ%_@6) zamvBCr1zM0%z{y}HDgz5IY7xKdNtwdz8-win^lro>MRmkN{v0*m-4g%fzy5)uOU=<>E5Et0U(4Q9pm!*b$L`JZ$W! z%CFPe5r5}=JmaQL3PusPnK`}kZRO^uF{g>mRm+7>$$ZW7qUHaLnmDG+Tr{T)88@Pg zYn@c4&gdxfS5&`U{ZE#QaoSmD2D9)T_#bro*=Geq@PF`6h^4?3epiXT;MW*6ZhZI_ z#D-!N+(I*UD*4u`F)ORY9C>z+3MhnL$gNO{C~wf?EaBQICqUVYAoWN@#|o`^zm4acgdgD zU@-5=v*|VO2D2s0$=S}lZO_ZsBQ7S+CHDBJK5_ntKmM2e@-Ln@e1AuW|49Cd?*yNN z_+7uRHQI#ui!=DJPC7a4p?oAU58@;=b786@EEWc*u;XmHhJn=S6GuZkVgKd)u;=Mp zseDgW{Kd>gi^|+a?5utGHn5QRi-jv!26Nye`U3tCJB&w?UoZuB$ha1KZvh6Fkm#;qX?eewnekW@Fi+81~__c~vcP8H~Ya#lK=|4aF-EYfxfBNHc&w~$?+wQwN zc!jSDJIYz|KEyJ~&mKNuY}9m;+h$I7`R(T67E6BWS!b3L`<)TZx>y!ERcK}1DdEx1s%D(5el(%14R^Hn=J2>UzJ&o9uhW`hv zyt|`4dKydbW_xc|d3#5D)n4d~*jqV4Q8#zLxs+FzPhP#C{Ns%+d6nHofh+Zl5=v?JTc7-CkaQrlVT6 z-e|8p-CkaPs;z3h;!x_W5{&Y6M|tVVDy|lzWUKQ@j`i*%}Y9ANjAz(Q+HkFkjC(=ZQBPS;kEoQ&y~5 zQWh;+P+B|HX`K>z&F7668g*#tB>X!0|6$ZbF)kzHcM3hl{~~cJI7QsSpb>+k9&Y!B z?ugl)yR@sU-qc;O%9<5r^7NL{I=izB8Z)fPD(Z6k|Ka~KUy>#cLX8!iVp$IHfWbq8 zALt9z;8$82*LnoofgbqRrIr7Nx|A@owdu-LP zkVd1>XgS(3NB1p#k2yBp4Tf^gekJ8oAz{7h8GWWU=0T__3Vll#_#-d_J^0 zF_Qd`w13Zvlhlay+4EDdk$BbbdG{;k+wW+0N~{Vlq2XbjnBRN-y3i7)FPtAd!>*?v zg-(D!<}6zl^;7YMM!a{F6mO^?4vEF%wwwnRJP2U3Skoe^w?{pBpn%eGE`+460~!I*2&#OSXgt<1>}pxtK_zdLweZd zrN{Vg_;gyD4ZEbyl6-<$a=EUb1fMkgJ(lrFomX<1FG%anDR`yw^HiKeOWFD7X~8R1 zZ@q_AFiL1Cf1MGG^3tQTqG6P5z4@fhD7h{BWE4!a{f|?EQJ(+9lxUbGxCF1%Smn7t zObgCPR>2&&Bw2;F63ckSwnj@Znt6-LAf7=>-^3<=Ueuh`-n|tL&b=) zS=R9cokR2c-x$8IJ!+%ylb<&z)-i`>%sI5=4>s1Z>@zM>_bu1iXZwi*?RWNoI7glz z3!QuhA5;}@k-QRP4Ik1er=J!1I_@u9+I82}a6{@oS#M7Mk5~@%@SIoU8*^`BdMs;s z4(D`!*RAuFcjTGfb267_vyXR>j3+)hYuC`I{GO)$>%}AZEzS1(KB!Tr`JD8Avx1mo8;4ieF*O4DB<11LUFZN9 z0^`VS!5(xBItu>W$MX5`nbBG>jJE#b%~Ai}T*Ne$ zMQgfC%j}tD^Oal6`b#b<7hm)F@~!{;Vfo_M?<}`|^XpOLM6Qr$mh+26&g_3)y#Fd@ zeNZ{$oB3)8-1Fd9%g=s)f4TqLo66p2HkG$tSQI%n zAMBjfj9K|`XJ`3fN5v@^rLL*aT5yRtTfU!nX()|GXTd4;_SW{!^5zTE%UdtZig^nE z{@U7V4xv}pMvdBtIeEULy!KpsdHvb8^2T%R(cXN%Bbwvid45KDJNf0=&Ypc5W7#`C z&hcz#unJzGvuLkA)mC15sx{idm?e1xySyr9rID{(u}6>ZCv}wMlf92jZ8l>S&)C0e z$tcz{E+xl;Pa1j(F8Oy2mt?)S9!}|rXX#;;*5C*JpWdvp@9~*s-(y{6|KqdE{wHPy zyF?61qlQYIL+B)0j@5h1xjpq1obuf7C)Zd7uT;#Eno8;_7$kY+h0s>onlUQR$mfaN zA2~i1uV9peHH^}1ziFwolwY-!XbpWO`^SGZB^u7Cvq~-}oAmCJOZ0VCv23i)GQlec z(N%KnuRom>tTJTuu+T+1)M%_3uFe}zCD;q9eTPAn-z0^`xpT9b4!0`i3Y;TGB-|by9 zB34)ZCbmJop?0FY>S05JhulNv`>3Nr!=a&MjTFD***&(kv^ntmCWzBVL%lDS-M>nB~so`R~ zIxS)-#TKwXdOE*&1D~~qA$>>GmkVAw_nf$&jNQ)jIJadraJ*0Vnq%(G`yM)ccsv)D z$mb7+JF#Ll?3cA>@_sOs-{EYY+*M3MZ=dTm8nXMH|N>{O6tSsM**c2S(cYVgS zq`pJ%V3%==Mn~Z@vY#6G@`G@Ux7k%2i*yp=`MH;+R9=(rd0Nm*03@x#RwC1{>g&yTA89x$T~B#Qlk1_O3^b zpAFc=tNSab zNnEprTa4+&6pMAf@wP9=T!?r@9h=G@v?#c3+nqO+AN>5=F`v-=_pL53KC`jBy<=g- zhG371NyMofoK;d=$=H=a8TmbbUhEU!P`6|pJiDSYdN`JttRkEX#V zbymSFOy7c(o@uRkC4E2*MtSAQ)@b{lu6TtnD0rp8Bfa&Km&MOE z^po6{Y*O!W3sy;OB{`-yrzD%y^_5_khVLi+J*lfCvslmCZ-=T+Tk%MxrBpF0m5!2e zDp)0X1(Rgl?5;mm@wCYwG!omJTFJvL!64b!xunh}>GMfFrOqnP(O4R+LQiS7h7Tus z1*_nej8}R35AD%#N}W~G2UPEyoWd7WXOmx5+(J*umaKv?l1J*x^|54<q2KRpr{7)drr-OMTUw*O2X0||@d;a;43Rb)gIH$ElU3}885~!) z+w3FzSs!;kExCftXL~gqgBNfGTg}eO2gel{0t@K#Soh#uxy^RljJe0)r-1|F-Fkkk zh7G-E&9e8M`Ixq+#vyi*?veTj-NBf3c^EW%%Nlzhzk!Diiynhu5qdvPQBMU67|WPP z`#ToD`aJmleK*7jSZ4Qq{uyVTdQ8M!Hn>7BpWHcpmU`cv(G$kUd51T?Yk0?d_Br62 z{5;&3dlm1gwu?G1c^>^&VR<^^rk*)_*GZG~@0 ztdQC(d_+E5T*EJAUdGF>yRKY&>n*`)d|7h1yccza{SB+#;ydR1spm5i|6?D%8#Rf) z{nP&__x<<>W$U)<%H=m)UvBxv*UGnl^5b&a&(QL^osjFDG{q5)5o9&(FI}0CA#GpJk zGnVNoZ#-9NDQ`X7-fXF@=#yV?OX?+ct%P3k>eDR;G~4k??nfgDog{rgPqvnQPpENH z#mgp-^sq*cpC`A~8Ku6QoKhc4R!KHVJtfC6O1<^2QCnx11MBS{yq3Ngl>=K(Ol_T6 z=rY)(cS}C0bISpY@=$Au);k`h5u0+T@!qVG+6pdFS6`oTwH2%2l^$lPmMd-L=|-$d zolTNia*Qu1c_sZo$tn7Jyh@!*EC-wXYDzPnaOwlbllLlUxo?X~eeVa`wS7 zjhGdzf>YGNA3S1M88CEUIdABIidE)JFT+L+DT7813tpj_up@Mn^aHU`Y!OyD?d-Fn zXIt$@PiJe)-Hcbn)-GM`|GT;h?U4s?N1ikJ535+lC75K;h~dF0mVE}sjbW7- zQ+s#J*sz}%fL#gO62F0mHti}~Zj4=FAFONE!}g9FU&c+E99)sx*x=+1tb$v8F8mw5 z6S&srFTV`Cu=i|{&ry#roy$3rE&NV*ecvl-J) z)8jkgykfiYtM7(uc@EzJeo67!J{P{GroPh1sTLP3M%ZUKv#Tq#6fqVwmSC`H(?Y-C z`{CCRFH2Lw&e%}yE^S5J1zmux#w%i0^q7GTf-~?6CgBgsJR)p@HSmi4?2lD&jv8uW zaj}WsIK2de&|K7EHRf2_isSJPwsBruV_nS``Ofs2D{r{2eEq@i#GE)6eg2AAr(azC z<(td(U-@$R@_l!g+rM>Bx%T!iMa=DwfAg!*LueiHk<9}zXw<0EZ(x;+j8nuYW&Eue z9?xx#pG{l0hVCIx8b8r}@D<;fcpq%yw@J>QPMZ>wP1lFE^7)%@igx`S zx0T!P`)2v>Pye&r`-2C|-QWLStl#vtuav94bW^$b>Z_Vs$_;ni7Cs}KqrR~D5Y)b- zzx>B9eqO%(wXcSzqMi-E6b`*$>xMFZ>N#cG^)t#7f8JbP-@dXj2awutJxtQ$|7kDp z?5g5xlT-MB>YS3xxFmIygR)9!ES;54sL3d8<+W#8${WwL1gE_DOj~*TIs8&FN>e9E z-w^*#BYrkD6a6bsP5Vp>hIy(jw(oy@T9Z|RQJ!ciFFii(Gi;K)k^F&E>a1eC-t3?I z=CM_OV67CalH5XLN#9TSf5fZQ<|oV;mAa&QI9q+P zN%mMHm+OpD*GMAfq!u@uI!cZ?N4DHXQ^6}@Ru15lKTa>Z{xmZb{;sEI;+@$yKXqDVCr7<&WoV3I~YP@PHYx=Vfh(O*oeS}n%* zum3eMI0TE-TYVqP*v9tQ|Gy@O)`C^WPn{6D$MCTu%82nJ%BV>rO6yE>{7x;yMzLul z%8-%l9-bKWf?P8^A)j|>>GRSerPeN2N`EK zjpLrQ|EW_r#=R#WxE=<;0`v>xc))s&v7?T0PRF}eas+OpZATk)-T}w5YpHcu#tVA; z<~Z%cxMrMxL&G8Ly8ZElWj(&oY?0h7oMXCi$D09Wu_I8)K8~ljn18$L9OQr_S#)4~~iak}Ag)A7IsF zm1ga*nX}^D@?bH;s`clWm1{Rd9Wwj4p7-h5@DDD;qhg=sa7SEBBZpdSv$!FQ+s1~r zw8pz|AKr)O@h;^a;V9ejF;425H$RT)?84if!7XZ`v44C*{707AYV|}mY`(CZe^E8> z4qe7O@qGL`?559vR)c9UhdQpBT4@@s1p8!cE?*ID!9tio%@+AKa)HFKh+m<-Typj2 zgH0~I{_1F$MC>Y!MUL=wcibAjATg~s+;w~T!LNT_?)=uh5zi{FWA?HoWm?zFGQPdF zjGjCxzPr`;cW`i_n!7{BjE?)mUFIk-2O~~UKfh)6tmx%j(UoXT?#th^`Fq6TWWEz# z*?PmZq4Usu+!G$*qmr-mgJ1okeCMbC8Os<3r)<6U+H&dDS4S+1c$S;LdS}GZ=A4P% zlbDsS-~X*(umvlZmGP}p<2`x*^OjC8-K(dTpZsWT+5Pl}^4fMe%QKsCDiyC(aVm6{ z_jclx&$3GJ$_s7fz3pw~-RE1&d)qsry}iA)7;kDS+gq!#?H$1{b)Ds{=h~vZ`D|;n z1J<8u3vPMi>DH>R`GYKDn7WqI!zxqD%TFGnJ<$?=AU$@`SzuT#U9lYXGyJwH#zrC=0o$DgJ) zmy=tnzG4=<5{!aVdbJlGo*K=#{j5L#(B$aF%i@*f5$mz-kK$wH-b|{vE`TcdU}k zB9``7lbWp3dmWq5OtSs;7n8~()_djpq&AX0-%fHzF5{2-m~mtE_LrYbZ0<+*;G>z) zGNFv0HolCVIy&mqPHvf0hK?8(wfD~(Hl)(yM~!U8ugG<-=1!g$vAJq1vDfUCW%inF z=RZ<|f98T&QTr|D0c!8+jJRI0i*pt)2v*@kU%Yx{bxQ+Rb2$O%l|RD3x>&B z;eNlfX@1wUcbFmch2g{EUXoqdRW11hgV=^iGj|NMD1JO$2R-L@BJ~O!=`%P z)HdvEpEzeD7s+)TV_d@q$sz0}cEA;yZOI|IUyeB+TYy>YlYHV_sfE~=4RtQ(PTPt> zl0A&u$2QB!D7Ggn<#Dzrzv$U}^)#@C&&g+^_ZfKiG!eEno8xI8uHiT~Ue6Zb2kep@ zV{S#CVQ_B47lc3AZr5{+KJB^jJU(oDW3EcqTeN&d>6*W&w9lN~tpDMHU7lL9Np)bs;bc8z@tJv75d(AB;gQFMZLklW4daX) zNB5Z)@71+w473oNSxJ3@k%C+kvMwhzrRu&Wd@0=p5ow z=q;H~WM7(%aef%n8H~=YPFx0y^gWrqu%%po{j&1R z<5!fIp6f1eyfCMl@26Q;rP5YMD)6mO7{8a>Xk+Wy(Q$#eVoCSp~1uSp}aYn|!9P<@wZg6s)r6 zv8sMsvI*X>94yj^M`^A%YQELiE!SDa@pV2)HmUcipVYa;a(%tpr;4rpzh#xk>1l8Y zHo+yKu~>enhh0*4sk2JNzsmJ_s3n*MTV&jcdMIkq?tWxpdGYZjWyhaq9>6NSV`p(o zatwb@vP$Ot^k$acjMAG;f?FEw^29GHzfPT3{uQgFc2f7{WIPH+Nmj9(%z{m_)tSZi zh*uduDtt10GwQ!h?VK7q%b?+69;!Bc4Er}CG?c-khDW|m)~(H&`fRX#=Kezmm4U;D zlye3TEN2fI5INE2L|n9TQOvJAd(qs`TEy(C$*=aU{04PZ+UL$F<65RPc_ej{8FOZb zPlY}7dy7rf*tFC+a06b#1;(%eZlGzTE|R_l``O<;;|%-i?VrclW`E=O%eZ5bg>3iR z->hqdhF}YAH_mQp_`^CLah!b|YdJZ`x`tou zXCLR(yRKeyp41rF?mVA;wB#Ds4b5Z1#OAdyf#&nT7I=l0mKuwFT{9U-?^=4tVF?W{ zu;F?Bo+>3^hjJq!Fgzm$? zB(8Yy&|wib#MehF;d2s8AtpoouEysfhBtDQ4nHDt=fysyug|%}4%1tVxxeHNa{;RP zB8P|$B!`IK!!kVuqo}c*pP|nTw~6zm1;{yObLG7718uwYOA(WSW$=l7U-?J;Lz>uL zej8)rR%k2BH=0-Z%HR;X37-$00;dcaJ1Xk0eEos@qqYg112-Q3w_VrP3W2_D&W<>kQ;YR;*VD}F`n2;RXi=6S{{TIee? zXGH9X7!n$cd?hh8U%KaOkq@-(&RgStQ@@R#f>)AJ^vNUt zst>OS2lUUhl{cPlZ}Lj&DCrZ@Ydw0(wBVEAk%q3a|A~rOqHi!t z_WR}Q;E#rmQtwk!$^L+C4IdD8IdHkEI~y8FLqka}(bw6eJ|0}ss8gH0nk#iK$>rpe z?2}FObd_xNag0*u6q-q0S4p2uU0bn?Q&L~C{M;WV1*7O=xsjLLWECv)yDEQJ9&z%8 zcoTeL-s9~L%`Dp=?kam8T~yw9ero~ho(new`7#aLH^yeYW;U@6`#mOuEnZE zOp3UagESmc=ao9EV3YI(>8+=p!pCFzV7{JUlZKzC>EEfi7Rhyt^5_4YZ~&{M zu3}%YDzue3i`AgwTW<}nIJw^`p{Zb$%n_om;1B*Exy3W)%?>}1oM$Y+52RL{?dq}M zm9x(4A2Ba@XiEFknD>X@2cxL%qUP;_`ZbnKGlbjQalxyYFv1ifi5b$HzW4 zIhY&-2#}EW-pYs!B1aQV29a~l!Q>1kgAgEc7D0%d3>bs4jqknV{gdh)pSk*3yZ1); z{7|E+yQ{mayVvUZ?Pt!anmen;Lcl4$vuP+;rQ!^>no_JFK8BA42ZRyuM0`8AKzk(o zA>o8DMVLh%w!sj%hVRFI>a)S>I8SsF%h)5VVp(17kQ_(eUl%4C?%Od*U;AK_!X|Qc zFo!zq6x*w8+>q!h;Q{sRBPpvJF2N1qjM!@SURhEfr?BD57{>X+7RtUc@q~KLX@AGW zClY4CDOtu2>S~92Y_!~d%Fb(BY&T|**l|g$lJPK%Uu=vv>+poO*jJpy*p536$2`R= zn8We35C4QgO3&zgL$>1)*P`s0&Z`ba(EmhTb#~cf&zi4D9MmFXgY17m={fF^d!-%D zgj}OqkI9+1H z*>iCh`m^x!h!4de;%@nQ=otFo&^j=RG4JU)SVcc1`|N$#!4;=MGr=KO+;&s>e2iyr z9RDqM*uL$w&tC1prB7DO?YUQ9UVaz)#NCfR+{CidFZfn&fAE3o+kC@aceI=Cy}Mm^ z>n+72*x=gRZ>?Anz8SiQc@ni7rBzux=-ew#5(*``d!nZ?tbXOicisR+I`TUT3ioqF@DWq z$9Cfd9=3Zs_uPHjC-0wGy2`)5G9RF^Q+6K2(RQ&Ym48W7nW&@CP5SHN>-kTdf;SxD!>E~gTfLEkMK%31*`PeS)XLiLNP1*AGUwn=iq&6jJ7#D&1su% zwOMfr-Vk4lJ#Y$5g-?f1*Em#|MV&3D8dG6P*>qY1n-wj?{xlREgH7zie-z%??@;r~ z?onex%-LmOo4e~yWvj%i@b|D=`gtnryNo5)+jyhm3QU0^umhgJFt{Y#p?_m-_21+b1@iO~nM3?U!TWi13Pi9N&KICl+#k@h&;9ZH~phV-B{y zw2)5QqHobbQb!&xQ6IbD0nCE~FanmaEa3yJU`!IcV7xGlddQJS)Ht(ZJ+!&}K?}!Y zPV5&JavyTtj%gouKj(CQ$vK=mT%@e6+L*EKjVI5~6rBSLX&3Fob*Fx=Q{1jTT>3}z z35k6X10z0Lf3Mtk`?`*_Q`^PWoObp(?Zm~W6}z}5JYz1a#CKyKebD_j{f>;&s(-S+ z%heC~lvCOz*IZL`5z-OZUj1h=Nw=hd=tW6!+w{C4Ig7q)Y+x}sfm+b!*iTW%`;z%2YiSKoeXCF7r9h6}H| zW~ifBR!?jRT?MPqRt`Aoh|*K&E|`SALI)9p%ReN}S5^@`ommx%Ur;~jUj3$D1Lcn_=a|L}<^i$6a3 z%+uO2Cmr8TIQ8&$-=8mPUw(W=`|D@NwV%G&uf}tut0XR^&muo(7XF}de(AGH7-T~J z`Ht=1pDh^j3cbYk;*`%8w4XkmU)ym?pI83<(TSyJ3}SP`E2W|I z`K4nPOoBneD#Q9cUg_&6UoW3uDV#FSFB5f?37X0zKAF6XQ^F_l0evC%YjqZmhk(Y<64u+?-;SXeyS&C)pMaW#zKD z?fqxx40RN7u}SLSkG`KL%Q(dPu!?*#tKb*OShdDLF+bt1d+%Dw*ln{H%&UYkj0J}` z_;zR};%TSP8LPMw@wFI4+$~;VhcN(K&aQi}(h}$>#^X2FPxP2w_ujMSXf}QYR-wCM zppgZ0i%n?Az9F%Sanbc1#S-RG#U#p!f5j0}_$Z7JU4_P>EpqL`D(WbwosOrzq^!Pe z(L$`F+ep!Ha2b1zm0Y)F^_3kzEQ4R1)3LRY?ZOCb77oA!_&`#YFot^g0h>tbrw-d} z->^uy#lF77jn`{`cGb4nOUIDH9Beh4tB!aW=Wz~sv=PV1`Pu%Vt?v3NM_X}j$77Sj zC-_ENox^(DF#{bT#|fh(Ph9n(M;%#Xp6F9!AO0Zm*7kGl7(#Lm>*N`)7N^)x+=}FU z?g19SA+BG$v~#HObiO4%7%9FZ*Q{(moE6rxuXFMb=`T-9(J#K{FX((i`o`-UBk9Yl zkBoR+ZIL`*93*yDa%}fe-Hd6M{_|m7nu-1@;yL(w#OoTrgsyVYRaaKLu6{~16`Z19 zFdfSO%=jw&D>MtX-}rC%fNmkimVf2yTW_kd?9adY(su5Z7q>efxxYQ|)T8aZt1fA$ zU35-6{+u(~ac7;;&b;iBVwL-rJW+jx_0JL08b;ysIrhxcOEal>k=t&mzB)9SCti52 zVn^sHI7OVU7+aiTOg4;i{hha0EbpK0yrbQC$L-bDLytRVz)^o1#QoA$^aXP6a0=ew z2jkb_lcJx{P4EbR*}0cqT4Vo)WpJB)#5!X=zM#`DIJaGP-No(cWw*CCUcI1w{{9K= zUtb*5{@+)-cfKDCGSE``J|OzaFz)0_v9n$LY%hj3d=jms&m+YoU0QYS*3VpQvaWyFOU0x z!YSbk`D9i}o%nrHM_g^Cj#a`V_Lssg^7wq@Smm3}7nFSm+Xu0|munKl54TX;K!tA#4`B|01D$!KJC6?(brKxl*lRi6PmFOzrly{z)HCWfh zr{IwANH`_iEN5MKMIKh!cE)yXyP4Y;pX{~&qPEArdsbg!aVN&X5<{ym4;#$xCXUj# zZTV*Q*}Lo%yNm;3uhmudz0E%$HkSR__mBfC&L!MJFY!H&ZNz6v?2))x^?iejHS~SW zALO`e{eJz5T@(W#mQnv=j&t zQub4qrh`v#2R?F*vDvO8_bRM{U9{UVw27`SXQQ-}&BX?|And`0;R)*`-`TcVj~Uo} zwx6wKXVp=*o=tb$>|>jK!bbglq6@IM&S#sH^V&{-z!z-0^>X#CPaLdz>~?%LsbfFS zFd7D)2%oUY)<^fSj0Yt5)P1wBV}&Q&_ry@U&z7|z$Ko5>Va}Z4*zSBF`or>lh%I$) z$E26YU9+}__p}Gg*yf!2+=?5~_n0o>y4_#f(_VUzHd~Li#Hi3%j6H{0Xf0w|>en5Y^s$F>9W$l7%FKvH*`q6gPZPypSoOt#b z6_;!5HB4cA6Jy@<^@yLr8sbH8hgcM4xpCNT`}6%Z)~qqy@R3+s`7ZnIQ<{fk{OO*% zYV0}3Df7@s|;CX*{oJ6`bw{V4>pOGl5&>AGH*XMr@j01y!Ou0xx=)@HpcDP zqV$zr7wuNe!Y3q_WcI@O#Vl3caY5M#{et;om&rIJg7n`1UncEdG)7Wvww$x;UKTnNE|jj^5%LwI@vG+Ff(jhf(a8*jh{?;S|h* z3&JXF4x7nlvCqovxH@bq`@}D!KHD_pu}+`KvDh#B(Hq!KHq`$8{p_3bu*s4(X`ACY zh7_ButPWeM%;r~l-rRbwmctY|r}NV`atyYeJ(XgY9n*SsByE-aUfe(D2+y(Mo?$dB z&sIMz&)qirV-xy;c>`$*HD^vYMu#y`Fokn#BYTxTH@P0iuua>=j_{)x|D$|D`|ex! z$8&T4?d$hxj>P2RPb|;E99wN&dI&#`u?gka1gBt+@P~1h^)2#j-9x`!F|}fN{myX@ zW|448VtVN_?v3YVAJ11@3N|ypv@u!I4|wSF>H0Do!-bAOL%=M`bQb;?8i#RLuDtoi zcJ=KywCnETySc92bpP$`?#CY}orT^ariD#6z8e1#7NLXi_0UjGyWoQI>F~RV5ux2+ z8)L5-1J|;C%lOE4yrMsJ;&vTJeZC@o8+GxFm=p6uiD?YjABRKQ)nq&IhD=ug^-gZg5=f1`5qj%43 zKYn&(`@dfG2ml&nu4Jta&tT<%yUToP94FI$IEqOF8eunIO=6{}#63GpeN-{*@r=T;hDHU`Qd zVU(~);!(mWd_h&_|50y(u7XF#xnsa6m}Jf{ee~k&O8s>c%B$d$un4Ur+>(0M*`D>$ zOp=FB@JfGO_ywD+id9xTGy4}=1;6xJCE7{yY_mKp_ZS7UNLxihAAG{!gDd2?gyz9cw@=ruGK=M5t`t}<3AKB0Sb^ZQ^A>$QdbvMiMy+h*%Jwr!Zhd_ulY%SSS8`&!04 zj%SRpn$LOawsmaBa6IuV%JCOzi-d#1XxJxZ*DJZEx;LHv5e-C6>xh1WC447`QPgKs z*+g}ul-VS<6<1jASZsRiy8XP1Eqgz+&z2qAaruYXMLc6)>o7xD#xb=mJmXwp5pBq_ z^C&xx^CWj2j)ytqNzShw*^m9e1eT?=*ZzKMen09vZuqCx(NT8ZsrVJsSf?GaNA@>< z7`DjRZERBMD+eA_T%oS^;SuY^pg;|%V2KDwQIgb&&K`7D*6){6HuHA#<7f(Jf~=L z?yY<8nw?MX8r)Zjh9af~v+#2mpG7|*aT(%o>6K!bF-!H?J?xMgb4@>>;+0#kYd79| zYrFpLn~Pg+d+6?V>!0s#SKo4dyX^XFicv6z`-P1zz5e=k*$p?8k7e;W=eNU8KC$h6 z_+ix#D1IjWt+5I2n7`-lrIFwr{9-&eF)VZ$z9D=wtqxD|4S_FL1ARu4j*<@vAwrGY5jWTJusLDUR1Ta+hD! zF1zmBwq)s5?cOCA@)E%J0MX^W}J-pJ*z@8GXNw zGUmW6olYYDq*BKxwuMu&PL4~G7pHuF4+)yfX0dV37f*eA8(wU%feZm{!Fi!x)sV|MBEJtkTz2hD;JRnVff=k{q*? zPSSBo80CM>yKzy%CzJDRPaalDJviK3 zOD5|n!);H`YwtZXzZfMsR@q=vaCb=ip<1#7d#4E*GoxcYc zur=%(n-p$f`>==gY@mb})J+P%zn5Qtl@ew3$~)ciB-aR zj<5X~!Wgnx)^-{emc`7@jX%`0zgQEjfmh6x!zPv9aL~cS7#7Fl6Oh6z;zdd?>HJ2< zP-BB>HR3*8Qy5d*-RImddYpB1m7%`U@u27Fy6Ja*vosP6LTkY<`aVnMhZd)jaa#Na zF^Xl{jAM9qSj9c`48u(Bljr4rx)08cRrr65wL)vaBXWIg^vM;&OvmIip{ua}bP#bY zScUKBvYW0hR=MGxTiP{u+}Lh=@Sb+lJ-4+J&pd6&JjQQv&uA3JwmW|D8SS`JPjCAi z@rN2WMc-w7flnmoz#zBA1GA_}M)HNmx-;Q+DDJPZgBktDz2OWKM`@`|a z7uQ^U>&=bJE$1+YPr}(U!e*TU)u}%J#QUk83}DzE92LgHysM(MzJI^m(P~7^8O5_$bj) z{`JYcN~>g*j#2POw3Yt4KA-&K!+Dj|{l^D`Wb72YGROyge)_x;HiHB+zKAvUceSOISFkei~8Pkogul;xn_k>6A2|H1Jhr2%0YzI3M z+aZr#Vbj%*ZD;G)l<s!EO$M&<@)SvIh-*q_vTvCj}>#cMsX-&Nt}~TLIbf5pRk?AYGt$7A+e_J znS0^9;!OT<^f5IaK0Xo8VA+@_;T*^2L*fU*KXS~HzEbAGaSz-t_jlv1wi@&k?&5dF zv0??=T(95b+P~kR#)+%B3%YT69m8|Lwf_ zrTJ~*I64$w5|=_(p_^b6_m4j){jA*Qxw{PJxG)amF^f-bha7u!^<_Tdq~qJ^mz>ki zzvhy5#VyyiYw!G1yZOG`+Eq7S*Zy?(t?jh)G!eabzX{I>iCjn%v3u6wj?Mn>Ay z(a|<#haK9y-FGh?1+$nNsOB}kRg_ate98yU&1);4ol_}#O7bCd^z;<#!Xt^7O}*rA zKRq_6)AIr06luz~Z0;5nw_^MhW7zJp*RC}`kU4_PD>!@M++r26E#8}S_vjSS6>x#% zU5W*;M`AZ<3+z0*PD8;d7zL})YxG^F>(gj_CyP_TERL09iXRD|DD%-+#u(8+yxYaa z`euy=;+(}hVq3P_x_mkOIrbF`kU5KSO2w*7pH}k?Vj0I_Th?5Ey`lc%x?M9{O((Ej z@*ZSsX#H%GWI0Tb?Q%AmZHi5g?!f+NCygcjMA&!jwjWLjKVTHLNSs9U6S;b9cKYgC zrXM-3bK1{)+BqFt!XvS_%JzxvWP{ap9chDdj)fPT7ps^jm%Vat95)Q2Zpw0Y!#!~9 zw9B$~i3{PcVN=;xW1h1~>>)n!-OH9>i^GmOy0m^=;a-@7$G-eO;R<|WT-b^?J?5B- zr*wZjANL#I(N)A*VwYo2I=P*4hA}seFXrLfVDH4fh|P_@l54;!TWmMzr{%s(nKo@W z7axtqygXRJJ#u~6#DB=Vi~0@u4#4JoTDJRdDI1rLUkSHh_8O;7EYB`G*E05T@7)Xb z)iqh?JHk2K&$_1z<`3r=5~IQwRB@+^_9`E%@ox1AGOvaCxQw;-hZBx%dmem1+i}qz z?Wo1awUf>}tu4OjtV-A2abvsdVO()TyYbH3s-Ko~x}Scx@pY}U!NzU9jW(^hg2V`K zwDs0)(IJOa+^ZOu%Wu4~{6*&G;cGF+@#6E(FW=9VH~*>nG}BNp2>*~67UQLeU9nC- zWU;fjhrXhZkMZ%YxbeF7$kR*O4R_pHa~}CU_)U2>+9zqNd(B6->wf#SJr6pl^dNQJ zi}IHpd3ePb-*n#{?Uws)YWF;HO?%?WGuzi6pVWT(eE;_EFL!JI_lp6O6kl|_5?v(> z6K0XCmps}?pGj=DJjn6MoMM!$$1LJe{?%iWVi4NN2XlwIN-eLPSFG~=2eaDWKbkY# zmUxpN#i&e*j}3N^FiD?9`s>0feWn@cCbW`)&nJu`|Kj!8l|Fl2T*>TWl`mc!*ID{p(l3AV z>a5|i^ym%);>mOnK%&L~~KnG%q(Aye4m34sBvmBim~-^3ZIa7s_`Pk?DGAX zix7w44=jRDEYrhrhTJk{!4!0maEtoZ^AmYTv70mw>_ETpU5iB$PwQA>2IR)>!Y$6B zY>rasss_Rgl0ge|mzJrVODc~`RA*89fCBi^Io z0nC6+*m*W5cHMhcnN2fJTFvjM?`r3J3gh@@^lk0>wGR_Wj_-KJe)sLmRjShlXI;aU^x*w#&mOeO}2i({E?WjOjIoI@>GpM`WxEeB!$o zlMFcK;PUD4H((Kd9vneGNgaK6a0F&iM^Yc-Xs?uKfcIR7cC-H{pMFNgTH+fT4O=JS z6tTO$(~j{f6Uf$=o&i+obOeZNu+dUC%8VqW_WS#s|aiiCOqOCmtWYy7uRX&T1dMb3*(6;{)5jKHa_i zK4B4TGJ#XNF=?~CpZ8fLY+_wMMN9eVQ~sg3m9Wb{a7V`;eNMqDNnw}2ubflKa-|RE zx4*wXw-^Pd{2*?2#k?wuMXA*F{Ry8W52p;5J6&aRj!`hls#3PqKG9Y1$Xj!UY!W>s z>!Yh+lyFM|(_fNcvyyFw=`mCaiLEc?DRyj=tt4Lv!$*dBcB)%Z55^f2X$T7*}<#0+^ zCA!KM#tfOVW!q%S%`0xjoIpG6X`YwarLm069j!jh;%;Zm8m(_OoDv@p7Kx_fdlA3j z66{sO)vXzNM=u^9hNy^{%gPz^u|6?J*g+XT$ngu#i1tyw8~&X2 ze_wUdhX+rvwQN2cj~{S~xZ3`{%K8A=U&21lfps?FFWPjI(qD`pW6tE&*IB#jh;hLe z-huW}KU{z{=o#2Zvh01%e~`EbY`|vHI-;#;D;p}u3=;dv?&n)xo-y)#A1kwQ?38Ve z?VI9ge}~XMSP67Fq|b z!tvC{SoXJ!cjy@y#MmIl*6=OM2FLCgA44BfeOKrl`jqP5W4tzYh1P*3Fbd7&*i%m} z9#Mx6h~A;kF?)n7_=mL1{d5iCN6(UBj>|9+KPwFd8{ui zn@Oj@446v1s&9OprTuc;Wehg#LLD-tmx&c1HDO1F%^V^QitU0+b%8@6ZP`Pp9jyU<) zw)n!c+r`&k*)F-^s&>|87ZkT#e&f}}D*P@Z_^bNePj8z~nc6noWXrbxhMN|z`2E&z zaN)x0KWr{R^Yiepm^WFx$-aO1Lp%J06WR%9o>_h&Jag*<_tl)v=77HW&-b<#|EJ><_khNNRrWvf4>i8-lg}+{_dfD)-7kJP%<4By3-x~ukK+`q>o-&L zN$$UITe$DuHI^Lx=i=+GZkJtmdAt0EbJ`tu9^XD#er)^xll_ZTD*mMN0VNNwR4hu@ zucyx-15V-Bp_$B?psVyX7Uco2s1sJ1Tb%OahqH%V(&v*fO4fx{et3U&`+mi&_QQ%f zT^cY-IHmNJE_QY@lVFp-zB^|^vMxGG^pk%6?b~zOH_K;Nt}ON0rRsJuvhn>yM@f!f zq_9f)eU{H@U%fTEQlCvy?lVZZWOBZ$Iyfcu!Yp#>)7NGduN0ezJ(;AV^jQV3;F0)$ ziak1335Ur0EYe>;)Kp#=8E7c|e3G8>fjE>^unJxYkNl@R`-e$zN?0Z9lG9Z%2_BIT z{XZS6gj15MgH_7^)5X_LW|gpvyw55rVuqGFu`4j5`W_{g$xSJ=sZUmLL!VHNCRY+1|x2V#TM@87fY|G}Dck#LM< zbubE6!7Te6bl`BzGGn6PxZEGHv$#o~`{ul~i_CK<*SE%)uKE}Ar8tH}Lt*C+I_AjY z6l2+*aMtPVsMAjF#!T9jn z)?8;W{+-{y|3Pu>uKFbHU(efp^?bw>?RWU$?dVfZsn`|c&x%c}w<>w^ze+^lh2 z^j#LGi@oSeu9prc$Azw!4~Zs%ckX=Xq2Y6LK5XLmgS-8H6LVc{IBXCfgIC1vise1; z@=MzJSDe?*yY$3%>#aw$FIJw=e*AR*_S0v34B~0KzR7fx@QU1WpHadd$#DpMB>G9# z%Tvy_YC)fm!eDn6KDu4UVICu1! ztK-ZN%C+>d_To9U7w(A!zxwoVpI}WYkB(UNE4$mb-I|o8Ew&l=J?sU?X-t6FLtVY zmp#P>@>z@x->ozhJW{b6-JE^ObZ0h`?abJ0bQj->=3K)n`Y9W;mcF8ok+~1UDZU52 zzi|jw!64xk+KCtwToE3ThgIyOK0QTwcBfbH!{8M;e(-ITdRPUo;SjMd7$@9;eSWvr zn&tb+d9lh`>ksCuvyb)aizO&#;X|6bZT02wj#l;^hhON=8%-Jbd9;D8^#1PaC~P?U zDUZgYj(015u#bG+P7B)}#=1J<@Va+=0C>-Sm?%ua?&^~pCc+rD;U27#IE~_lZY&$y zoYS|bvW*7ycO>NK@fkvP^@)9BdJu(mr{vkv63L zY^`?YUbCr=!+zl@`iwTvR;0wRX#YUB8N{aehOaoAKO9!y{^1wbqA#sJ!uTU|MtEla z8-DY2F&u;G{U4MMJNl^No5Y**0l7Dr4yU+358d%_uu+c}qB)Xu!<{2D*@l=IFWjH7bi zxpfS`I~wBe)>vnl%u78SA7J}wb+0^c%+S{~7A{&;ejwwr;2&)>CY*6@jj@7JB>k4f zsJOq;u#92n9vBw|e;MmVjLYNCFKbI)e6iwqPdf9=;v#H>FLAEl3SXXe{C)a48(U`3 z59-t!$K{Wwp485|==65Mr6;t9A33#s`N8q+$Itd{KYc#%0cG4bY=SYuCzDwvdP+DX z+<`-~U!O^`U0ym0E#<@6<@bqhBF81k@klsDPD{ZlDPxsBpC~7Nw_^4nbvjD&p{CN~ zjIcWqYSLs+qSsJoQ=l>ZlcdR1Lws@t-B>$-#e(AGHm_&|6R>dmOQ^G3!*xJOa zU=;m-wwXS){66~~YD|-ziXpr&jkhv?=Y=)KiWoXvLc7K)7(|Xw;tR4IpSixn68*(D zqj$7;75$+ltRgpl|6K8dyYE)tkoxe@OZ3ykA#@HoE>Xr16&Ks<`yv5Wck zXCpC#_KCaIUU{y;*r8%pY79|K)17m*wbyEEth-LN#TZ^VMcj(9M}PG{zpb${u#@xJ zkA8!P#ElrUi>a2K{oUmvW3jl}atmocY|c1D*;{0erF*ii{9@Tp-xI+W+(S@E&4 zxsv=9W28p#>YTM316_=vD4uS_8U_;AKt(#?7X`47tA6>yHSVF$1$-0jZpSqbp|ZNJ(={r)9{b=y`WN0YJ`0V;x43U`ItyJuj;+{RJfw_=l<7Zw zNOTuGfmg(}(gOG@u$XJ|d=jUH?XkZ93fA{OvyOj>Zs2)i7tfR4;`&^Rd*;6LG0}2p z9L{MxT5}1r`LqTx9uiFj3*aX9T|GJ8IQP;^ie180ju*cihLo(sL;Nmu8+>rV6_?k1 z#b;e`e*JchVduYVOc;Hz{pN8y_NEp3jf7Wd9hlM_0PfpWf4Z^SpzW7jb9Hft_P8dx ziP&1#gkOwXi%V+!+Uu@sXI*qrZKuhYJ2B(kKDz9g8jBW}=?^X8k=+j1ukC)|{-rU+ zC*@qug9XL8?05J@XCNM~NB#aV`WHO%&d8E@8uBg)w? z%h@isF1ku`I?6yx8E7T(@!*hf3O!}WB%RM^$Q&<^mM$VsIXcNuQ^6qkG8S)Ggbbg>%~he>k9GRE*hHn)FWI z=VQewzWuDDS$nUt_p}x7)$K=S4t+nCy>G>;;12Ix?^o|xx=O{;&Yv^E2Q)TsZdtSSi|^?b}h zxb7w!5Bsh8J;sOXSyg}HZoUc6Ix)D~y!)cP>;8KN^c=B1&VySp3l701bQF37--g6S zC&nYXk9{x;TP@Kn=vj^_7Dv4t7qifgGOnC+8Iy!QbJFRjRo`#--v1%2?zz-&bC;b< zJH;q84f+*sp>@zT&bsKrVT_OV(}!@1b7Kf&?HkXQKPYo4^Y@sy@#3qmt}@>d{|}C# zDH*T-iDl0g2l0L39r_Xt2D9*sSuZ)K>*dqaSAOSxb}v@B?&h=GtFK+zzWU(A_V-Wr zYybRY)vOX`NgihDbIUlF4EiEZTpq9rHd$5bvr74OqNQ|f(r1;FG0WfHomIS2e4_tP zG?lk!|BO|>duMjBi#!|>PQe|0M#=iG-zCYT6|4~!=`%>mtKySv!zRjMl;nL@8E7MzV`QM2yfUt@gjI$yv@Z>OKSMpG$1JO2 z73?wWcRZAC_L2PZuD&ZCJ zwzaKe6@8BJ%3DhZDeTeLO@`V@xJ19^ZXA_pFn!-oG#34o%?UJhhixlHW#2>gEng6= zgH7|zj!DGJrXMh0I{Sf1=oAvWA9nGMWlO^WzU9^DcNe2VOA)7{ACR%&_<=^|%x-h$ zVW(}14`>`%0FTJoakd>3@CD%(yn-LX3HYS?9Z#D+6!YxuUr9gc;0wB0$B?W4YJ+la~ObRc!;F>Ej0 zB?&W#3C_2v?|(Xq=a5(i_ZuI2UYJF~FP1$=Ho`t(GYlbCEgYnU=rdWJ|>)N$9xq7&UZo}5{6S48Ml*|igyb}HsT7u(9jys4wU07u{TAaJSL;4EQ z7M$0z`ZONL$0s-jv$!ri#uwvS7oUArJO9#4+PN2BRN5c?C_0h<`c8Z9R?OwUnEpt( zh-NeJ_Z?Mb*N_-mZ5Ka+PmJS#!(De&P9w3Nj^)|t*GxyDsnA#GFZ@cz?#D3p=O3cQ z(3kXg#&Q+AbK0pj2Hk?a_9%|RQ->aVM7!Xs^V$QC-PD%9eM|fL!_(W}K0L7f^OM~x zwic7b-xD6O?jN7dt<+}~Ok!CcA5S>Na@Ivt!74wj9P83(En}4-pLG3vYPr)=`ni6~ zmT4;Ay)#z4lDbNAH||?lrN6FU&e*h= zCEOBSC2_X;C7WwmA7%3v?y=tyTTAC-`S1 zcAxH&x|Zn|a$LZl<6E2W2dB_j*lXXXbOvo-dxPJ%)z(?N>N=nOFauU`j%mKljk((O zi^foR#WL1#?wnUW=V2Fd3O~K=j_G)E*MU`>!?D)iV8gbh{>t5)FWQPp@Cx054QI>Q zW;R}#oey(~ZQvVle?6DP3gHv>0h@R(%ASMm*0Jqa!SQ1Au!LjF*+ADIsiS^$5N)!r zq-+}wkcS^I3;U0M!bErjH@FszV&Bq$7VKEx-MG>*u>)-*?Z7I0P4-tm`|yi7A5OwY z>e&aUi0gHJF*A-SKE`>q(e-GD>#>iv+6QYnwsm-g#)3D*r}X3bxxW%`#$v=#lk z_%ir~(qD+Zr9ZI4<###t2<4i}l4C*aVO0PhEYgcU(~HgrBs_@vPr&bfnJfJlam< zkT8n#U*W5$;pt%Su~pY!u|(S#%# zj%C;2TCfnV;LE}yG!?93jz#R}S;PmY&izk5UgNRQTj(&?+;&Sl_lnDg zuD!Z+C!AvZIrqmLLp$#~7$1&KcHR{iwQFvt>7+RcyM}CP_!Y1LB$&8X^u_+UEm9b&}pYr*LmF=ftAD@9g2%}hsQzW{IgjMiK za-3572@T~POw%QOcP48gJyr>iWZf_6Dp&-Ygh?jn;gf#euM;i_lY~3^d7nZ0&Jgjd2Z;gnV715IU|Kf);Tr0~eQ&&(WXFCEuRW|gX!c-oFtvOJkphWbgz zD8(sHjSh8`XeH5Ca7f}(Y^#3Allmu5;FoUfH=Kf7`nn2c!7I^JrjPAVy2|`r=eKn> zT(_;e@dl-nh>vB@<@5(W9p8TvzYiV}lY&Vk+vQjV>nMBYM!)9&*=pLhrLF9D=s~5c zh_&5eY_$3Z|K_*9ZNFQ6_0k<^7wkKRVDrNT?7Xt|VHcdBOmD#vwqYFK+U$Q4Tdm#f zI@_Rq`YvG!^&K~w2d1FW;1zv}95*p7;S<~RuNG^9lcGb&@rGCw%p+b!9s5bfEHxi5 zo-#j-TpRNJZ(nW309b+z#SzNvv2SmT;y&XQ_c(qZ&jw3k6WR)!fLCy)ZR$Bz_`@;T zJ9gi(b3VE~Zqf#Jk*(K0JeC+5`=}oui|yKKz2nz!2dh_sqG}E5AlJ z28v_h9X=Xbh=fCI$1mC(t;aUZ!G7TfjAOaaRIbJK+K(?sOp0r8Oy_VualGPEunPZ# z=h)s!hfm`Sfd@a!nXb#4?=<+-@W%3&4#pfL!R;VV*K8Jo~jY%{JLorU&- zclgKXE74WlA8bdDx#OV++6{Nz)E;{JzP9}RN83+7KGMGV=&bh5`}?#XK3dTJ)$5lW zT_xIyd{t>OuOx1^pZ7T>+DfexoAUnb_P6)OR>>+DrN*o6m}ST+?~Qfk@p#(s3O*5= zGUOCKAY-H~pFKe*iG~tB!7F_orC*MAf;;;9NjM~IVmW1bwkPj1i82=H=P8SW?Q@Da z6Z%NlB>j5mCtn%wt?TF0&#_HdB>D+nnanD+-0Qy+T?MaX|8PojoFb++`-M;NigL!z zw~R+tJU7zbe}260Phw)ls7UWEn>nbH_>``{j^(h*P&^32#! zSD~r&b(OG6{65iGYI)Kawc(ULvrMSd#j8x#Rr<`5xZ0U>Myh}E%(*k$W?OIGHr;y5 zp^wD4t@IK;9lYWjkR9~xPEQe+!rt?#u>E{N-ob37cbs=Gdx}?Rz*xn6h+m=zw_dcVi4ooi+wdFEuT`aPc>dq$2N96c32tjU>qzJ z=D{yxbLZ7ucedlI=rrs#Tg_%;1dLW<`mWP<=kcs^Z2Q{>L*Nt3Y_)yUR_x(8_=6^cDw@*YbTAZ zA6B8OOx=FlHf_fAw#{}^+uQ~7+P2fDw&^3ri=R^Cie$bVjKX)o?%|KpZ;gpLYpkuY z-n!*SVGq62*niv9o3j1X>Ypqw#aOjlZnst2V%sTg>WuB$yj>Oyw~13RmI+S5EBe@o zMftDa{7=QK;0OL5eis~pU2uwoGc4l_cAxz>7O#1J#3!)#{0nkzq66V8nvnLoE_RY# z#x!C>#GF)Itnuyjv+ep$;~`ukr;j+7gkxwhkeR!|k6RhHXxd+;Azvw4yXt;p=XHUWaIL100gjJm1 zGJETM{g`C;0}?O>~Ii~mJk`U>{M z4C-MJJcB#%$Mkt~iXF7y`t*6mr}{yA=B~?e@D9GAao~o;sNxFerCZ<yq95*vHQMHjWJZ@Q`CmGOvY8%}heaVk!( zcu$bI)#9-+pV2J$LC% z*SG8Mxuacu$1Uxq`)_aeJ@x1I@UsuMC!fE+t^DMv_V0f^(Z2cU`1bdY7PTKg+NJst z|NG;4?Wd0heA3ra!X$DmVl0$@np?0N7oSElA?_qGwY^xBpRq~LrxV@e=lQsP5*bRZPI>FeS#6EA*KF%=`1|(zjW%dgw$ZnCO0kPE<&D>>e=fa)_Q0lN z6z?cDk0#6x@-N_&#ICT5-eJDs&3D4bgH^=TZnD*8<+I*;`qZ}L?mLyP!Y?$%{A^op z(SG&oUzZ<$H>$E)VEz(8`*nhz97jm9-}8@Jc??s z{zRK?UaVkyt`|EfW0kG-$C|QL&4(8s0-q53B}Rz-5~t(2NPKhd2MvQQX7eT2AHNT+ zMV&Afw$cZjzpHpl->dFExK8&C|6mWv@!0U_80I+A8-I3zy)}KK9X@kFa!S!77?dHS5cQABrJkk9D`3s zTneot+(koE#;E*8#<%s{jlF|Y_V0 zF`s#6JL1F>+Z8w8T&!aJmV3pwJn~SDJNLlTPqv#LxTjrr_wDV5d+%)5-F;)bf9Zqm z$(NpN4=lO6J^RX^+h4wZto`uyrR}>94r@PrxO@BQOy>ZXjd)%^(xqqf@U+;PL ze)xU9-L}Osa_ias`rev7TI11*p~Wh*7tXKc(fPC5%(m9afdmXTMZP(|B4~X6U zpEXvmen5ObiB(BlDkfm(*+%}E=o4%w{lYd{3QnQZU?2Tz*i^Px>ietsg0xw}1+k%Q zwEZxHb2=AZa9(k>VWKb!UBR~0#ZLS@+Jm$3mTPbv>>;=8ICukh*vJ09>tpxXZfz-h zO&4+<-8e27$-Z*y!anXj9$~x0+u{`C>#-%)*@idSc6OGHjm?ct!1lWz#U8zT>wa5y zz9h?@lk1neZ#afFM6&9SV{CKG zXd-pq1q(_~F*ZMK#WJ5k;*+p7_NGm+>tb-}C2}@TVsmj1?r<-*<5@m;#I74h$eRmA@siAUFUo@DC}gOY=zm)Wb6Ljp}pUjg_z6r45O_?fR(G zGd=UfS<_8$37?aATI?duS8`t3iQ~$nqi83Nq2=%=IiI%a^MhT~7Z2qf;P>V~<#&aT zv6_44w^ZdF7Zk(LUN9IRkvW9UzU0Do>wR~(3$MDo80Nqu&0jExd%;v!U4KJ8TVp30 z-v%G*o8|tyzmDzMyZ?T_xUg7tuf2*(`Q!NK#1sATlvAodv~}hxJo2QIYOchKuf4Xk zlsg}NxH#pe`|oY{Jo#w5`-w-|Ef3z)9(elkcI$(8wL2cUw>|ap()Pg8``csB-{0PR z?}7H?_xH3fR-DkjTe)}p@xz5R7h!29^pj8YJ038~xQh-=`8#^%0oX- z7bBZ|B9jc_W@{a;=`VM&v(Zq(ELr|``BSBD;Okt%KyeH<;xk4i9bG03f`cl^m)a)#G-^(;)?8<; z;uU4Avfc*km%Yaiv=#FO;S^)MMpt3yy{p)H-&Xal-f1nqTaBAPGH-VE@!|XN-DKU^ zg4u2QtQl=&{%G6p&;v?S*=Wnn>e$$bjw5I1qbVq}gP4GRAhGMVv(-3--L{NXw0C@- zqrrSVY-jmarcWy!DE<)3^}g-Kn8gR|pl>xckWF@eImV#(&~}EldDG3RE$W0t)YT^K z#fo$7m()rTdQ; z*%j+uBL1_`^^FPu}%w!QxK(`&u9t8ct98kp_F=eM)|yZXW$R^Qm{l<#eJ z9PhBTj^o+W1>Fz6rlhn_o3z3Gu+Hy7(&p6B2KNtNX#-}#IhFR=yKH|L1c!(V!2s!F zj0@-{a{7gRunJaCKfJ;(l=0;;NuN(FyC(O?|IhCli-`eoT&yAS?O+tKCCb)056y&* zB4GyGCH#Uf#Mp!-uuS@$VHEEI@oe4&?uqB(8G8PhiN=aA#Z!roagEr7Pw1|P9%$!Z zacMDBG^cYexwxHm{slE3gL{j8u*s3f9#<^sS@{0Iv7Qa)_B?Pe9`&rn6V05vpkk*p z&!m1@JL-3S)KTr2Q%@bT$~AY~R@%yq_uk#^eC(li|I#PgZ4ckqZhGLZcH2XDxBHen z)Es}kFzC786Rp=<`&%@7yNx~_~`+QQ2 z(v9^NT_x+1#|MN{!YW}8d3Yu3hPq0pv1Gm6y7cG4EXv^(ED~)coYBw2D#>k&->1JW z>q}D+pVBeO&lu$wSjGCVO0mmRqs22grJwN0TTc!2mVUXc6WTr|<<~7e1L;^XHY_r%{xqGIK_~-@OO%iT6A`1*hbFWt;aH-w!PX zqbM7v-<(2XRA$T>X}j#bYdi4pgWEPcY+uLPa5H^;*DK$QKE(Bn-f0@_u*8-u6VCziuIHg+kRzVGM-JMGq{&zw~{$@G!Y zVh45+n^gO{xuvzGw2tZ1ii7AR+U&mQ8-+F4cJFlBxowyS3(~G^6Ca$uJKBp=Fi*II zJ`+vHI_%&+g&FXIab5oVcdOMoJtNPDy%68PUa-sBka!&05w>7!*;(&@_sPA8KEhsT z8)k9eqj~X3Ij1&Bmfag{#cs17?3Me;emReO<{6s*z;m;WEn@%aA+baBDDl;fr7ajo zyAtD#J?xw|YIAr5bJ*s0&`<1U zd`;ScZ`fvS3|B=%!Wnpj2I8FYwRj%ouj%^F&@5;j(KoOSmZ8Cv#?p=X$4^9?!6d#L zBuwJjdd`-y!JHlS?Ha_qI2H!MEY2x89!&-xNbU745Fa9%+BN@1AzwQ;)Y>9=xxeb;Tv^#{2GS_dN0E_TV#*l^^J#WskHcUw)#! zuzX2d@zK-m#~+?-U#vX6eZAtq_QT3uD-PxF;!t{HyoFWdlUZdlw}emR85gCmtKgAw z1{v2+tV`a)t%Z;t$oRffKu&Iim$V;;gaNEy*@T9 zhdpewZ@f25+piZE z!71v5LF9c_>941;l)p!vu77gYS?=>n;%dVi_@Wp^E_Hp1#n4vj;#G=6x)|C1x*?-< zF|dl~Wg?%9Gs{~~icuNF zsl4&{XnXy!k+#vM=I>aod_Ws)ykT2s{dL^-~e z9mO7cKlz?#hrNd|im`9y#;7$;|Bk!vT&#ju4mkY4w%a~?w2h|>VqC@WimhFJ-F4cp ze)rpoP4P}*+hVV=0Q(jWU^i{AemLff-A3PJv9w|-wy0(HmfiF|V?VUnyIs2_?{)Sq z-_CS}*e&nWu{jIc{Dr%gZDn8CUfZ3M%?+#Z`Dmy2HHNbdTZL7$6Ib9c*Qks=ezW>& zHKq*qsMPhLb{_9_JdzlZXc{<4S?)aUiF<*4a7y{Ex^G>%HnMHnB}V$!tFKnytlsl$ zth-*>6t;y;agUV4FPNrma5v7LJlBFxa7&+AjAx^tIL4q=u=V-Yc0JmyovzifoYQ^A z2iPF@o=)aDYmas84<7KWJU?}PPh&gUk^AKs7=Vt1PbA;hVpZ5!+5y`y#|<==#5ssR z!72HMcRXeBD`GFisboxE>u4tZ*p{#h-eDK3t=cpbM(;ta>Dm|0`-V*;AV z!3PcFQLqXgQOEg@=Qmn>YQ?r_6PA$TLlW!bnrK9lxEkD_cLPSrn27i}x|iqezvy@A zS<-#ngT($~H9jlX>wmK8=9{<8w%DS6H-0;G&CG4#x99gpSKD}tt=d-GO|ReT7Tavs zrffT{O&=NfhU$0MjVW#X8#;^MhWOi4FFe1UdD$i9|0!S4(kI%IS6?h%x%kFw+wBkE zQ+}X(p1i-^`Pe<}>DQOGWp6yyUU_Rt`}W&s+P9xy*}htFSo?m(uI;A}7qlPfCLhk5 zz$oFBikt1l%=XzNW!tNqKF8_z(~ajACXt6b`uX_w*+UH_T8jMJ8q32+OP>O z37;hIvkG2`o)RVrZ%k&D=p$hhoFb0Jdi#V)QZKBM*pzI2)H=Zr}Av(ucmaF2!U;3E&N z`G+>0vPJ0%?4IwW4ULP^jko4qvESCy8fX_~|GP2y*>*OV zet=EbP;A1cmgdsw{On`&1-6I1Wh>S3F3z_*yTDFZ$7bT5aFB0KW&M%dL;4Ah;^XqI zh^5wCZ{3Q|$@q7+MX#aLxF#HcMU?T7_3o2P zag6Dr9)+igoHnCIgt@~HJ zNE_=oC4L*cB1VMG=bLiRv4`~%Ho+3^H-88JmT!9+9zMy~Y5KyHW}@HkVTTU&05*_z zalqk+*H|3B#3;#U~Q~$l0GwS~$PM1zZ`w{<(m9#~S)#~f4UH;&T@$bg4j^4J~n(MZ8*59bD z`}>WX`JCk&Y`l5faFZ=6ZMpS!ZM*3sZQ6{nw$;>Wb?*G9{r-%F%7D=%+X z+;UU9*}Tk;J=7k1aanuh*`@7?7nZgAmpsz$eB}Oi=fiik2bVt7UV7){_S~D#x98t_ zp}qU*>+Q9-|J+`B`Lg!G^0V6KZy(jZd3W#j{rkJLe|)fG`)4XA=G> z?&#`$^Tw=7S^o0PS(UzcL#*pa_07>g$9nnXa$-=z9O03EOiDN;b^5Fl9VPi>HVK1R zAAQBTBn*N(vYatel4FzMSSa1NDfYn~>H9O}m(H(a+xyS&PpO=LcUd)xcF>h3VTI0TdY>bL(hj9LEmYQHO1F+U=1 z!4TL4XJCcWVb)r^+D$)+)*Zhm-{gd1q z{NY#_#qr#?FpAvqBi)*}VLiLb&S4d{dNQlf7&2!MK4GgeR*JImQ)*0>Zd}@NO~!bt z2N&ODEI2U{{7m|zsE22i#eiyS+KpFS?@({)`t#F1qOGvYiB;h*Vz>EOTm%0M7N9ez zhdF9}7c(OEhMp2u6MGU?p_54NgX7~I8U}3%@6d9@{?bKgZ}@>W;c0Cn(XY>OJRAB?`G&eNj{HvikK6~msXd8dq1%{0 z5&!T(@j2ranycTX-z;54!Y6C5w_#gj?e&Lc>#~ev*57c`aenDyzMWee=qsL^@m9{d z{L*&b)mK(J|Jp0d4`d9u2bMhE9(#Uid*a1s+Jj3UDOSNL(wiT?)!zR2wf6K2cem$W zx~~23?LFj_v!EgYjdBW1sZ;_Y|+J9Gfr~GER}gBz>Pw zv=!^H3Qh^LBp>INaaQU3dlEw%#z-u!Wm?KGy*@UKDTy{x{L<+vt74S_ljuX-^&?In zovvSTn5CAxaZ|z}zr-q8-{+JtOme&upG}`xQcldukV`t<1goS@xFj5sJUsF^q-<^^Tz)EkcS0 z<9PZ0_s+s8c*MI)PG`yYe*fFwI_#ppcX|?5!6&#y@~-!8j&CeB(>pmD41d1(6@9C` zgY|ooez(?|#Vp>(7-Y@0*J-P-xmM{UmUBO_0uG^{{MUc~y8YL$e^cDzp4qpqaifik zS4tyad#&<2Ilg%q-D|mH;GG;pY)GZwtyX>{9D{kH)BJihZ5(h@I0jFJN9a@TsgxKF z>=%6jr(hCV34KIj#}gAGm&A{-^K$jo!!E|Wul_kS12Gr-A5i^4#8gcinOXV@`-)fC zboQP-mSYqe1Dj1hp{;~X*k1igXd{_38MD}qOVp*M=>Lpwtg|iklhb4DZyCp=ea520 zFF4En@H_B3p*yhG+KgM+Y@CvK6w7!;!XE6oa(p#@TlM=oVE_7k(U-7~x&tN~#Gml} z&`$79v>V!sdt=-+@g~O4_xts`#^N|5+|7@xz5Wk;ZsAGiaZfX@-EoT-SN~wTi1ky2`3cFZ-YGzyhgfS%r>bUHBw&d1)!TEi(4`u5Dz_ ztcs)EVfM^6b;h(VO|5bK=_;FV;~Q(>YodXKMcCN*9@t&>w_+Q_s&-70`u53w^p^Nd z=rGo+mwhe!?#Crkv=h3tcNx2Dec}PJ3Edd0*j60V&Ee>sPUDW<@t$XY#OsPVv5j5z z%}Hx#L)j$SJ-fiZiU*dIeM8c+q61-)O*h}7uGc!(#ur3iaZT2#lkZKOfi2uG+v69~ zE^YMu=sUB`jbKc_E@l^R;JVx^z8ad7d#(;GM_jG%V)qCS^11OxNn$c^2w^-@RfSn-#zCB{^<55JID5e&lyvdQ?u_dWjvO@Ym(v$)6nDr_Jv zL1LqEz`*t!+iag=iW*PMm>f80zx|3autxko;hC_DI<`stJ>oPa`$%*ZbsS$ji|fNE z+OO=Iu}S)l`>kMG&uC-g0BpZq`9pAoao6PHYqcF;i0yDKd^j~W%z+2gZ;Y>o&Xo8Q z*Bn1mVodxFt+PKykmESVrNy{+cn72Sjf*Go8yEXdD-7FXQqP9pM*AGg`HW*?o@BnO z<4;@Mjx`po*c|+|(09vD_3Y)C4Ij~TFcj?vQ%T1zKDk|R9rr;@hvZr(S-h zJ^uWXw)CZ^+A}Xd)!zB&x%T}(UTB|ubY1)Oy;Iw#ZynLTU9n61>x!M*-`<-)82`lh z_?_Peqx4gsRr*}gFJqB$?PL%qn`L?UWiqQ|IcyR=rIsgaDxJPk>w9c6xg1u(Bl_cf zl0G@B$fK#)R{YY9lM?pmvq-kddmOOZs{E zM4t8dWOAC!FWFxF(rGDj{-5wl#>x+~$gPV`65S;1(&rOp+p{kF6rXf)vpA-<_k2R( z5;3#3Wx4W>Rl+DWrplA!@hFpHQ=*^rStaZGdP>S+k$&E1l`u;3KC5g#g@1RWido_R zF@EiK)3-0}Roth(In_T$-<7S+|I2?Oj%^TgOJ5nAN8{Gdb^A6pe^y&`z~03y*6X7@ zGEW~y^W$vMrj1Q&JMFns+yBr5+NN7=*0$JYa7?;^_mSiCPg}+-n8SAzyNN^8NjaK{ zbzvIzlb!WGbByRPY%F_BAD{>05jh*|UB*5uTZc*NJFM&PoR|uBm|Y2rC}R>yA85%s z+PU{WRxuuz{tj#?eMM59k6Y%=ExUzZe0$;78_ev+Jt%Qy7>gz z5@Xq72yMYkmL+zPE#Jl5Fx`2yZ^15Na0WWP?`X$2hOTqtmBh&45B3QsVjyL8d}BvT z$-AEh65kEm%tnh@wN8B;;=Z^}J_U9wc2-^2?EcXkXk6|EyB3?z{^Jdr0|t=9aNrjj z1KZ7p;uJQJ&x0Lhla&W{nx-(A^I5yF2DZ>18c6&(w2<;g8Ixn5fnUnC&`wG}F{bU@ zxo!Gv^AGP@?1IB^OngDr-fnz3$5-|&*lxdp^quyb@EgDf#wf%un89;0jy*u+E z2lp>NCLDtoavpjIhEP{~_=_+Kor3MhF7ykrwe%<3=^E}Mw%~KZE_LpanZ>zS7pvfS z&nQfZQO!ZEFR**y+MVD2?z{14Pd)3*I+uGvTQUEVIg|7qqUrEMiPQ01J-368KBAp< z@dfR&>#nW+#rbOYqW$)-Zy0`4nja0(f8INwew$lu+cw#JFo#yfjIX_Z`=8a;s{gz1 z6XUdOvh~(&(`~kCTko(#wMCoEkz_ug8q?)ZH`d&Uv=!s!nvdE1hd11Hd%O9*yDM%* z|K+8xJ=fm;?EUuQyRWr}mp##*c;Tt`?CZE<>)Cm zq@VCf*hBust0Svqlc7%1)tSsC<^k-dPhOcZm>aOuT*4~-xRgGpghj$DeMU*S&nbze zjejSs($`h8t=7FfYsenhBDzVRL;B2tGr}kR^%ZNf%v{GKL;p^e|5F|&!4<_HxMUS< zGMPWZAz_c=5plB}gY?Hw344TBCTl8_*Qb8*2~B0mjM7z#QJxqtM{AKMP3Dpb8cG+( zl64dEE*7PqE5Gva4wdLA;T2^Gt4KIyvn@9-O=a6@+m&AetMC`|A4r%boRatze1cOJ z?q;kG{c2{k-S^p}E!btJ(p7Lujj1wf%!jEJt1>cgr0ufTuH`eL-H6k`Bi=pYOK}n1 zz;a?#@CVkg9R9E!%g_eAui_^OmsqAd&|>h7oFBux43|j8)5j$=6ug2<*k?J0v5oD{ z`c$Cm0*Bt-g^Z+i2hHfNx!Ph)oKQuu+o!q*Ck@8-{IS>+9Qf{=h$AUmQ{M z9nPLryn>hs)MoGK}R$-IHozOt+udT+n6=$i9S;it^ z71|0`DNSeJ{fbH0Wp%Y@=Dc}juiYn!-NrDE?|K|pJ^GL`{emv!x;ZQx~_Rk2}82m^C)=nXb83{r9~Cybm_p za+@}F`$4~C|5cg`ACUih`F*EMDPGamZAV7xIo5oKM<3btJ7O@-%;nc#S8QTVApRh8 zHk*scTte61d0V^b-n-gu58l%rdiL@5@`^XwbIV_D_dfYxdt}*?_Q-QjwxzE;+wOSy zj<$T|vi9Xy&;0+5-TA*AMVZHO681$vM4TB^)+F~Pkc9wA3CIuq9zhfP_u< z0YQ-o5(8Yq8Ukb^Aq%L3BjC7<^P1QEUp24ybNYMI_jofiKh&$btE;=KtNZlxIp620 z>h{V0=i3**xv_n||A!UB@*f{BZvXYk`R%`dJJ47XKU*;=AJ1uj+dupN#VWLwFpB)I zd&Y-6()R%w-_vK6Nj{(+tCWt?=_h?&>6c*_X~-JA^^`?l3BO>L{&E;bJ*?upgT9{} z{e(W!=aA9mKC7gzxoHP!pr_!G@QOVC9`nZ*v-G|>*w;(KD$zow%1a;Vd_13Q9{6>_ zC4F}3`+HKJyzk>Fzs|d3P3p7Als=u#&l6S|_1%O&YJ740Ihk{IiWu6&*{04=M;T$2 zFo_(uB&8gGU=sPRO=Fc(Hq=sjaV%LL)l#s?&R55VDSXn;#itne!v|!n@;A>ot>)Ut zBp5<$14f}!NXl_a#sZx^dq$goAsb}Qhtt}OIWy~f&2!K9t)Q{`9-?h?7M#--UA(xR zd;YleusP8{*dp&}|9_Q9-rIC<@rHB^ zy2=IJ_g>fnwn~mA*fVy8En~m11$#zQ!5*{;>xo~?{_0BcEnq0N-~Kqr@$BO`mYqv| zxnmABoB0c>kFW+m4Ev;?*h)6acJ>M@SZ5p9VfD&h&RmGfiVpr%Z%U9f98j!xJZ@>Hg`d3e>v1j^3fAH7FXX_bqFYt-9 zVCm9|EyAB-kbGa+Hyvqp^d{^QMp?D)zVZwC=HcVNc&=E*n4pdCY-*c#f6(6Fx1&At ztLG~Y_vOv6w-+Whv?pJ9wymFdp?&&?U2Wg~x7+W2^V9a}$M?2B?7P1GaqngAulpCZ z|NPjPpasMD+4yj9$5a|h_+t{I>>F>DhT6(Gf5#>LvB>fH9E?$LNuN_J_sg)!7dsBH z$KNr?t~nL2qC8AetTHnHiS=kE{cj}4mlFmV?$?bamK%4>w-cRYQl2?yN5=YKljtIS zHVLyR>(@!$KC9pp>*17I?)h{Eykbtb!Mtwam*jjt*rrnVePo>}Y4I!gJKx_B4bg#YUG?wU2w zwb>UoSUGLQ`(2rLvgQ1L#Wlq?GtMp@g3Vw%*b+KG7z0CK4Yq*&!3J!N|CwSL*#exv z|Fhu2!S`{kvsr8ujRU7RhPrHvZFnQObFm+ejYrgHXXMUVY&3sf*(dFE4DG>Cu}AEs zS87BqrbAh?QDFXRkS%A=DbPT!Yr0EatgjqqWI82C=`o?6;&3Bz8M2=RDyf_b@4ZD%S@|_(5_%-51O5q5E!M_nj@~ zucSY**BKv!Q`l^5?RmyLIK?xDM?7EQ7RT~DBsqsv&u2Gpjq^J%7IVJvfcvh0@SOXT zdqg8~Pxyi`qBvU&AdUs6C@UY-kFKkGLKx=m zhaMOv<@Z1NSbO4^KWpnZZESDt+)~=gvm0J0|Ioioyxi`%|Gswhtv9us@4llw`SYjR z<`3R%J3o4%?f&TL_VM0z?bE%>+wVWTwEb!CqV}Kr&nutJ=y;z#tMnNqoI*pXw0CS0 zs|@B&8O)h7rO&7H|0HfUd9;({@$F!fKD$Im>2rwX_;&iNGKp7q%$cN{ghj$Da%Cxx zmJ(Jm=ZTcq*{R}C@JZ&I4XZ>?!5(3haEKhAL`&(HXSvTReNG9lB<5tGp_tcg@SUCL zCzJRjoFeb}e98yZ`{wZ|ql8c77$te1Rj|oaDZD~InQHs6>`mV(?7sEW^X-I7`gs^- z_iHnUDXh}hRPYE6Ny;|s{bii8*aeU9&xmCkj7>eM zbQZjjam>aDi9_N48Jjy^d@}C>;~d3nEUa|lMT?46F1~EpP;a5J_>Qyv&HX`3q2FYj z3LYvR`TD7~j8iPDE8!PhQY<6p)7<9W*ktS21+VZcN!cz&l>J~wz0>^nWM|lw@Jr%u zOJDEiSC{9%8ZE{DTL0@L<1z679R&}t$KIjrNo+v06kIW672{XBZ#=S9>``nA8y*{> z+;-d8E=gI+**9g5$M&%EGz-Z#S$}dF$dO^rN;o9$Oeq0vl9&_-mG! zJMF@RjeStVTJIP9}y zsFU`|^@H|%#@KY%AX&x=`cyxA7Cb}vK>xT8;l^xtf84*^8~0fMIERF@^uOFSIH&tW zUy$58e66n?Lylh@$N8McGvfTt`b9@2&RO&wtkLfAaBSoQtmbZoBx(@3rf0UEUsh z_UZP*D?e*nc5ZC@e)H?LZ_iKL@Auu@KKtmh_Lsej>l-`1Z~V7U24j?QO4ub@OL!%| zp1KM|Gs;?=2pQd{nXb^`kX>Tk-{j+M_C1z9L)C`ynJ=Z5JFTS8D!oRrvPZ`*bd--?pE=Z5lt+W<=Y2f|m*A19_@vJ%S(bmeX=dpvVUXx1(M7OG zm?gQomZPbt6E^AReNM4#?zfCtKJCoY+w61a6t9%F@wLJC_1Jp+K>xrhG#7E1nG24O zXThTLN;jdWV3lQ;UsArI@p-;!wy0QzuCnBkZ?%8?=72dI3$M^`l7_lU&-XKoxAmQ% zuAFYdCnbIbONm#(F40%C*}8gR6+R&Eb?;oc|DbGY-uLts>)yd^akS<5f7oT5LXW4X zu(RI#Y!Cm1_rG_mWPWh(dhdTsqMqD7IEKB+|5zG`da);t6Fyd_?IVbLW=r5*s1j0<+Lg@}C~N z&{_Pi$13ci>vBB4B>bU`xXXF^%z;O&<1BgXo&WF{#&*{#cdZf@(Z+C&HrtLJY*VNB zrHgIqvuyv~K}w#Ln^qg+pi~^eznP-gxe?X~qBh z4*0sC+}M6_-L>u3yY6Z?tz0?WPM4{%(cL!!_>d&L;`#ENVo3~T44F8U8cTk~6*a%U z*co%%VHC&0Q~XWF0X_8eQ|14;=H{E)^($^GO=ZP>YuY`JJk%ck*%R&Q7k^Rn?!Wud z*7pA1o$bmSt|>;j^s4W-@89s_+GpkcYuj!2-rH9HbX|M$mru3z6VJ5|c0AW!T7PBx zc*oW4^N+sW{=8>#`_tZq?XUY6w7=E2j|X$WRs2d9Tia(9e1cVc3puQEARn*_zfV7z z`wgd5>c%2h9Ig4p z^p+m06ti@3DbY{lSViiWjp{04mcH*N8cOnT$yE6$tKbpr5gmoD@;l%1DUHQGurl7&TiXZo>{5SDx=Hhti>wQjIo);Dm8}rA7BqG<2}z7OZh&F@xse4`%b;9 z?Q0+P*@);LY?Nc$&o;J%-3Zgfez86FVfXZk}UwkU+^u^KYaICxpT0EY;!ol{^@`J{k31eRsVJ4g^f2BL&;t_mjBq|EX2x6_#?h5 zZ3v6V9V31y>|~q}ZoxM3ZOPTOZ=R7{Z;pi>^g}UGr(=WUQ&;D@$V$ds}{?Yp;=G)r(l)uO|Xt(Q8&+#4esvFFmb8W5v_?BBL z-MMycyKCLL_Rr5h-_|_%K#fOo4)MOe4{Z*Z^%EP~d%L$)e65(3pRByCt+?-=cJtkL zw!0pFu&r3Tw%xq?p0?_Nb#2`fkG2P%dbmxz^NaS*yX)Gn?JL`Fc3j*3ZRdB}A9gQm zf84XU{rjE;rLh>xGx#=g=Fm4U{(<>aqN~tM`bnLNPuV@*q;N@JLmB0hFi4+MKHokz zOxZ5~f-eV8bnz&3ks8i`B_7u6{5r>U~vr2ziee0vF5*8WN zM8Yc3R&Yr8L_WIS-Q!x*m7?$t?R-k`KG!Jnq=7W>?f95Zm-{xL0&{D+G zo<8gJDqHxih3$;7GfHm}Q|o&`c*T4TCw}eZ(i~_M-mk`K;t|KkBREMCLmOVfBHAw}RmuM}$^TDw#=F~p zRPS%&Hexr}OyB$w%c2fj%qCzJ@hSAE`Q3kJ?`pij_IP(Y2Ah)q+~b`MaX$7aIu3io zmdM#0xqVWPO>-P`&MB9itN%ZmO>|CkuK4bgc4?z{U*BIj=D6e9H%~u(7)y=g^qs!K ze>5ra3vzY}t6(~P75l|jTV~hTvFIyx&9j|v5Qn2JxQ30_XOjQKxB_FyX&e{@KP09T z&y>Agyr`}@*RQWqUyOn=#2CZ<(n==kqST{EvzYFHo7F@yS;`-cM42em^_liwF{*+VN zaVMW#F(aN0&$RupirFt5#Dx};tFy~}ER zGwo%?n$`8aLop}zqph7L27YX;uFJLA#@B;;=sT9N%6G0BjBgibYknC1q1#ulZY%G( zr&=kC&AZoluoVwJleex%*9 zW=+kDOOtu_S5LQXyI*X3_C3=!zi~_3vH6C!XZv;Sce}1^f7rF8G?u^Z6LUMSH~x4q z|7`S=gRx4uC9Fa-`TuarfJ4TMPcREs`E1*);d(gc^X&s(p`T!pA!GE|q%@Ikd!JRp zDfa2JO0h^{Rl2#{!YA^?)mk31O~*Llj^vmHTl5*E&m38nXP>Z$vTPr+$(C6|pHJ26 z=0yps^mDwDb$k*H#IkaXGt^7EaX$y+lYTiy!6d~j@+|k`YrDAG=p@lil806LY!c20 zpM+PEr*7))nwT+(Q~K+CK$Uv&DHX5M=_*5JNepe5YsF&`XO5g%)=6gmoCnKgG-8=E&)oU&-yq8jsK{4%}89Ja>I z_TQ%&f9V}sxz$`%gB0AZB0K_2~t?MxgkZV_A4ua`E{0*nEY^Akx~<{RpB zi*n;~9hW^e?;4vFUETj;?>#o(yWYDT7x-qQdhD@xwmIv?_pwvn(QMEhHl~~R4HxjE zN!pFcug%t9x;gDzI6ECoBxS@(vLV3b4obR zyl}J{9|f;Z%RM(&3`V9R5(os0f& zU*}dI_qa#&ALqwceKye!?ZYK}uU*AW(_lZZ^GcP{ZUV8ocwte?RTfg!C z_KO#8Ya3o((Y9}0(SG~k)$Q}$%i5p!UQoO;m@DPLJo(mXCt_rc=?Rt^}$9kV8BCDBd7D9H!f%IxA3jFLoG!5jQKecw*FB-|lSS@C#9*>w~i0Y+qS>H*)ur?C4AD)!z$5Cl7~maC;i;EK9BVE zl+p5?ugxe{!60}gy2>zK<&{}OUg`UE`b?r6i~L{gBA(V*WxOJ;mcC-{T1>!>vH$pi zjbrP~GfP_$8|%A;d_6eD-1+&|&-qKwZ%Zy+Qe%F^*kYQ&ocn`s+2{S~eM(cIQO90K z7r{!->D=tLxLY<{Ui{L{*=l@}@j%%h@7UKq>d{B=N5$0|qhVfZX*j1fIW8^T)ZXs>6(Gm(A5B+jjE&aX_euVdoY z@}+cqp{<@N+(9$J9^nt=m__}tn0xDa^4pOtV;B5^M6R&}42U0iV| zVs5nut2iGY4Q_Hi@kr`PbQWw8t%LtYy88Mbw`*^_q1|%Vs+vpgfhQgw7gG zXv13*?WNaWZf|e>pyF2MEm_hozvh~@eATMbRaUKC+itygO?&jYXWFapZfb9CeZ9T6 zdvkm4#mC$FS08KZH>_$~-ug-V?T6R4&vq?sf7!FJ;%@UDWQ-!=l8omGqxAVCnu@Za zzo)0Ke71dN@rhWJ=q1VVNmzxh^4YfWVTz`LRWL`fMlUut>;GFGe&H*sdYwMPuM^)+ z%H+dX+7V49b>jmHlZ-Nm^^~bQTCdM0soT%-Nn&ZEjr4s$2XvGH$9((_Kh1zE`nt-% z4|L#r$K}uIc%}4`ZjSr#%Fw4X^zn3hN{>~FKlpcgJ|1Q9`^dAb9J_=~qLqX{vfkg` z=b7^JOw4E>zIt|XisgR7DwQT?4&qdzuS|>=kM#MZzdXt+*260wY&dJUee18!u4G%r zDC3mV&pKl`hB=&oSE6?)i;cuAl5hJ+SOuTRmwsny`F)J{`TCjuZ;#j9aI}HGKFyZW zR(vCe?PNoJ|4?#X%)!<=zisBF!z9Z2bK=**Em%OZ4HMW8Td}Qp#k{OFCwwQ zCIzF=2rR1?c2UMAdFQgB{2TrgvMcNayQA%{!#g`Rj9x%Hkl2Qb9XRRB#aiaw#2zP{ za6&uw*kg-HXc$8_2qX0JX|_+BRxE)#jyUS5%8xt#_&S#3@!8-ExxSP16$Yu}Pn$M4 z&h+WS^*YD2Y3;Bhjwns#)KgDwXU&+=PWgv_Xh$7&R6F$0Lx*iU?1&@VF~@zW`V+%t zu5+y7eU44|&)6#Oc6M2xDUXhVPb7BDb{r6&3VWx%W9dKkUVKRGymee4xhLvi4tAIB zz@F2&@Jfv*y6p109~rOXIG6=LIF|D{2Hwy<$5rOo@@ORCr8-xqkJxVAK5}&z@&$D< zy2_oyz8KB5I+q-ygh9d|;T3j0z8=pFd#*0K$p<5Mt;*F6d*GFDis#b4+U2@13ym%F zv#5t_@P^n_T)=MQ61KlGkbG6)l*PX?==`VdoBhq%ywv^{m9sg7K$8KJAdwctXEj9n! z8y~#Wezj?1duQ9$I&WgXOB1}}2gNMcEMH#nF3)XvvFiG!(61)`rM>j}FWawQd$Mhu zSkrcGTh%_@b!GeG?hEU?f%qi65;hSlE8&t+PWjWWv0*t@`C{kTAb$44!I+-#$Rt+T zIac*1bIO2KFbZZ#tjZ{>L_3Mb5?1N!E-8zyGGv{ON5U#Y2I(}Lu*y{RYTVD(Ig>OK zY!N*qdP&y9De~}0>crP`u)N}7hhvTh{+t8rVqRx;$^0eaLuOQtQLGOc-nN@z&u}7Dd!v7;)CG)K$)-}w40sM#0cML05 z<${G3o5J@)TM?sj_MEdT7i;U=gyyPMm#!lI!n>2MBC+9gVDTRG5{xn6l&=iWi#aez zY;Rb_e|!J)%}ZPV@w-@He4>tZ{DWioE#wkDiI##zu#C8rXf0TU{-O>o!MiHo5b|G~ z1|(+DI3JuMuespHXV?4Ld(Qt#^IjMylk3#};XJ@cAKi{S?zndJkw*?#V*2zp9Ygf^ znWp`9C7$SBbh#Zz$phiJ`Otk@OI*fC$^JLKDp8L&%553op1;H z$6jC#d2voR&NlzwGpC$=XfqO>#=2wU4BARKLQeBgN75E`qJwCk^D47H9(8`}mThyr z=I&Wy4u0{z=PfKoi4KE*ic|QNy79{N5^c90-cp8Dl!ZycE^H(gljAY_U>tR^34W%b z=m*DfE`4PGZ1bFAnesPvakT7wjkj4o&>Pr!nu=Vx#QrNYX6D*kme+WltFFJkU3v92 zX&T|&dZx>v&r0jfHh0Vt&I0J*wZL|kJ(Ne_tD#J7M zABncY_jBIT#cl4Q^NVS4j{WSDdhu~pJj!bEk^c{hw6JnzL987T=PEr z&3azt?yKY-;92&(Vn%cR-hTHT?WKvA+lDt^Zf|XSw{70}e%ti!=4!_`X3VJHf@foV z{=#yWHma-e_e{kq#W8rL8>2kXR3@{^sIC(3z$)?SgfXy5e>tp@ zvSHroDakQOX(-+PpT0jdiFZVe{J^RV=iF*HQ zN^ERzT+e~Bfu<5YMVqZez^*H!x4`*nupQ?N=g$fg;^D}8@Ye_5T~n`TrR x)IXrDgj3|vP*OI^F3RyqQZ$wD3r>ly(jW7KNBX=n6`vf8RcI-g#r7Vn{12h=lB)m! literal 0 HcmV?d00001 diff --git a/test/testyuv.c b/test/testyuv.c new file mode 100644 index 000000000..a4071d4c7 --- /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 000000000..977f034f2 --- /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 000000000..173a4e2f8 --- /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);