From 75116f9099bb13220d4996c4ee907dbacf063962 Mon Sep 17 00:00:00 2001 From: Joel Kiptoo Date: Tue, 29 Sep 2026 19:31:40 +0300 Subject: [PATCH 1/2] Implement FlipRotate without WIC for non-Windows platforms FlipRotate was the only image operation that always needed WIC, so it was not available when building for Linux / WSL. On non-Windows platforms it now moves whole pixels directly, for any format with a whole number of bytes per pixel that is not compressed, packed, planar or palettized (others return HRESULT_E_NOT_SUPPORTED). Flips are applied to the source before the rotation, which is how WIC combines WICBitmapTransformOptions. The Windows code paths are unchanged. Fixes #350 --- CMakeLists.txt | 2 +- DirectXTex/DirectXTex.h | 2 - DirectXTex/DirectXTexFlipRotate.cpp | 130 ++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d478c7f..998b0fb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,7 @@ set(LIBRARY_SOURCES DirectXTex/DirectXTexCompress.cpp DirectXTex/DirectXTexConvert.cpp DirectXTex/DirectXTexDDS.cpp + DirectXTex/DirectXTexFlipRotate.cpp DirectXTex/DirectXTexHDR.cpp DirectXTex/DirectXTexImage.cpp DirectXTex/DirectXTexMipmaps.cpp @@ -123,7 +124,6 @@ set(LIBRARY_SOURCES if(WIN32) list(APPEND LIBRARY_SOURCES - DirectXTex/DirectXTexFlipRotate.cpp DirectXTex/DirectXTexWIC.cpp) endif() diff --git a/DirectXTex/DirectXTex.h b/DirectXTex/DirectXTex.h index 1d90cae4..b5c089aa 100644 --- a/DirectXTex/DirectXTex.h +++ b/DirectXTex/DirectXTex.h @@ -783,7 +783,6 @@ namespace DirectX TEX_FR_FLIP_VERTICAL = 0x10, }; -#ifdef _WIN32 DIRECTX_TEX_API HRESULT __cdecl FlipRotate(_In_ const Image& srcImage, _In_ TEX_FR_FLAGS flags, _Out_ ScratchImage& image) noexcept; DIRECTX_TEX_API HRESULT __cdecl FlipRotate(_In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, @@ -791,7 +790,6 @@ namespace DirectX _In_ TEX_FR_FLAGS flags, _Out_ ScratchImage& result) noexcept; // Flip and/or rotate image -#endif enum TEX_FILTER_FLAGS : uint32_t { diff --git a/DirectXTex/DirectXTexFlipRotate.cpp b/DirectXTex/DirectXTexFlipRotate.cpp index 07e0757c..53aef6ee 100644 --- a/DirectXTex/DirectXTexFlipRotate.cpp +++ b/DirectXTex/DirectXTexFlipRotate.cpp @@ -13,10 +13,13 @@ using namespace DirectX; using namespace DirectX::Internal; +#ifdef _WIN32 using Microsoft::WRL::ComPtr; +#endif namespace { +#ifdef _WIN32 //------------------------------------------------------------------------------------- // Do flip/rotate operation using WIC //------------------------------------------------------------------------------------- @@ -166,6 +169,109 @@ namespace return S_OK; } +#else // !WIN32 + //------------------------------------------------------------------------------------- + // Flip/rotate by moving whole pixels, which needs a fixed number of bytes per pixel + //------------------------------------------------------------------------------------- + bool IsSupportedForFlipRotate(DXGI_FORMAT format) noexcept + { + if (IsCompressed(format) || IsPacked(format) || IsPlanar(format) || IsPalettized(format)) + return false; + + const size_t bpp = BitsPerPixel(format); + return (bpp >= 8) && ((bpp % 8) == 0); + } + + HRESULT PerformFlipRotate(const Image& srcImage, TEX_FR_FLAGS flags, const Image& destImage) noexcept + { + if (!srcImage.pixels || !destImage.pixels) + return E_POINTER; + + assert(srcImage.format == destImage.format); + + const size_t bytesPerPixel = BitsPerPixel(srcImage.format) / 8; + if (!bytesPerPixel) + return HRESULT_E_NOT_SUPPORTED; + + // Each destination pixel (x, y) is read from the source pixel (sx, sy), or (sy, sx) when the rotation + // swaps the axes. The flip flags are applied to the source before the rotation, as WIC does. + bool swapXY = false; + bool flipX = false; + bool flipY = false; + + switch (static_cast(flags & (TEX_FR_ROTATE0 | TEX_FR_ROTATE90 | TEX_FR_ROTATE180 | TEX_FR_ROTATE270))) + { + case TEX_FR_ROTATE90: + swapXY = flipX = true; + break; + + case TEX_FR_ROTATE180: + flipX = flipY = true; + break; + + case TEX_FR_ROTATE270: + swapXY = flipY = true; + break; + + default: + break; + } + + if (flags & TEX_FR_FLIP_HORIZONTAL) + { + if (swapXY) + flipY = !flipY; + else + flipX = !flipX; + } + + if (flags & TEX_FR_FLIP_VERTICAL) + { + if (swapXY) + flipX = !flipX; + else + flipY = !flipY; + } + + const size_t width = destImage.width; + const size_t height = destImage.height; + + if (swapXY) + { + if (srcImage.width != height || srcImage.height != width) + return E_FAIL; + } + else if (srcImage.width != width || srcImage.height != height) + { + return E_FAIL; + } + + const size_t rowBytes = width * bytesPerPixel; + + for (size_t y = 0; y < height; ++y) + { + const size_t sy = flipY ? (height - 1 - y) : y; + uint8_t* pDest = destImage.pixels + y * destImage.rowPitch; + + if (!swapXY && !flipX) + { + memcpy(pDest, srcImage.pixels + sy * srcImage.rowPitch, rowBytes); + continue; + } + + for (size_t x = 0; x < width; ++x) + { + const size_t sx = flipX ? (width - 1 - x) : x; + const uint8_t* pSrc = swapXY + ? srcImage.pixels + sx * srcImage.rowPitch + sy * bytesPerPixel + : srcImage.pixels + sy * srcImage.rowPitch + sx * bytesPerPixel; + memcpy(pDest + x * bytesPerPixel, pSrc, bytesPerPixel); + } + } + + return S_OK; + } +#endif // WIN32 } // namespace //===================================================================================== @@ -192,6 +298,12 @@ _Use_decl_annotations_ HRESULT DirectX::FlipRotate(const Image& srcImage, TEX_FR return HRESULT_E_NOT_SUPPORTED; } +#ifndef _WIN32 + if (!IsSupportedForFlipRotate(srcImage.format)) + return HRESULT_E_NOT_SUPPORTED; +#endif + +#ifdef _WIN32 static_assert(static_cast(TEX_FR_ROTATE0) == static_cast(WICBitmapTransformRotate0), "TEX_FR_ROTATE0 no longer matches WIC"); static_assert(static_cast(TEX_FR_ROTATE90) == static_cast(WICBitmapTransformRotate90), "TEX_FR_ROTATE90 no longer matches WIC"); @@ -203,6 +315,7 @@ _Use_decl_annotations_ HRESULT DirectX::FlipRotate(const Image& srcImage, TEX_FR "TEX_FR_FLIP_HORIZONTAL no longer matches WIC"); static_assert(static_cast(TEX_FR_FLIP_VERTICAL) == static_cast(WICBitmapTransformFlipVertical), "TEX_FR_FLIP_VERTICAL no longer matches WIC"); +#endif // Only supports 90, 180, 270, or no rotation flags... not a combination of rotation flags const int rotateMode = static_cast(flags & (TEX_FR_ROTATE0 | TEX_FR_ROTATE90 | TEX_FR_ROTATE180 | TEX_FR_ROTATE270)); @@ -237,6 +350,7 @@ _Use_decl_annotations_ HRESULT DirectX::FlipRotate(const Image& srcImage, TEX_FR return E_POINTER; } +#ifdef _WIN32 WICPixelFormatGUID pfGUID; if (DXGIToWIC(srcImage.format, pfGUID)) { @@ -257,6 +371,9 @@ _Use_decl_annotations_ HRESULT DirectX::FlipRotate(const Image& srcImage, TEX_FR hr = PerformFlipRotateViaF32(srcImage, flags, *rimage); } } +#else + hr = PerformFlipRotate(srcImage, flags, *rimage); +#endif if (FAILED(hr)) { @@ -282,6 +399,12 @@ DirectX::FlipRotate(const Image* srcImages, size_t nimages, const TexMetadata& m return HRESULT_E_NOT_SUPPORTED; } +#ifndef _WIN32 + if (!IsSupportedForFlipRotate(metadata.format)) + return HRESULT_E_NOT_SUPPORTED; +#endif + +#ifdef _WIN32 static_assert(static_cast(TEX_FR_ROTATE0) == static_cast(WICBitmapTransformRotate0), "TEX_FR_ROTATE0 no longer matches WIC"); static_assert(static_cast(TEX_FR_ROTATE90) == static_cast(WICBitmapTransformRotate90), "TEX_FR_ROTATE90 no longer matches WIC"); @@ -293,6 +416,7 @@ DirectX::FlipRotate(const Image* srcImages, size_t nimages, const TexMetadata& m "TEX_FR_FLIP_HORIZONTAL no longer matches WIC"); static_assert(static_cast(TEX_FR_FLIP_VERTICAL) == static_cast(WICBitmapTransformFlipVertical), "TEX_FR_FLIP_VERTICAL no longer matches WIC"); +#endif // Only supports 90, 180, 270, or no rotation flags... not a combination of rotation flags const int rotateMode = static_cast(flags & (TEX_FR_ROTATE0 | TEX_FR_ROTATE90 | TEX_FR_ROTATE180 | TEX_FR_ROTATE270)); @@ -334,8 +458,10 @@ DirectX::FlipRotate(const Image* srcImages, size_t nimages, const TexMetadata& m return E_POINTER; } +#ifdef _WIN32 WICPixelFormatGUID pfGUID; const bool wicpf = DXGIToWIC(metadata.format, pfGUID); +#endif for (size_t index = 0; index < nimages; ++index) { @@ -369,6 +495,7 @@ DirectX::FlipRotate(const Image* srcImages, size_t nimages, const TexMetadata& m } } +#ifdef _WIN32 if (wicpf) { // Case 1: Source format is supported by Windows Imaging Component @@ -388,6 +515,9 @@ DirectX::FlipRotate(const Image* srcImages, size_t nimages, const TexMetadata& m hr = PerformFlipRotateViaF32(src, flags, dst); } } +#else + hr = PerformFlipRotate(src, flags, dst); +#endif if (FAILED(hr)) { From 5961dc3a7aa02eb60fabd09fed5dc2c879e09d82 Mon Sep 17 00:00:00 2001 From: Joel Kiptoo Date: Thu, 1 Oct 2026 09:54:51 +0300 Subject: [PATCH 2/2] Apply clang-format to the non-WIC FlipRotate code --- DirectXTex/DirectXTexFlipRotate.cpp | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/DirectXTex/DirectXTexFlipRotate.cpp b/DirectXTex/DirectXTexFlipRotate.cpp index 53aef6ee..0d9fa42c 100644 --- a/DirectXTex/DirectXTexFlipRotate.cpp +++ b/DirectXTex/DirectXTexFlipRotate.cpp @@ -169,7 +169,7 @@ namespace return S_OK; } -#else // !WIN32 +#else // !WIN32 //------------------------------------------------------------------------------------- // Flip/rotate by moving whole pixels, which needs a fixed number of bytes per pixel //------------------------------------------------------------------------------------- @@ -201,20 +201,13 @@ namespace switch (static_cast(flags & (TEX_FR_ROTATE0 | TEX_FR_ROTATE90 | TEX_FR_ROTATE180 | TEX_FR_ROTATE270))) { - case TEX_FR_ROTATE90: - swapXY = flipX = true; - break; + case TEX_FR_ROTATE90: swapXY = flipX = true; break; - case TEX_FR_ROTATE180: - flipX = flipY = true; - break; + case TEX_FR_ROTATE180: flipX = flipY = true; break; - case TEX_FR_ROTATE270: - swapXY = flipY = true; - break; + case TEX_FR_ROTATE270: swapXY = flipY = true; break; - default: - break; + default: break; } if (flags & TEX_FR_FLIP_HORIZONTAL) @@ -250,7 +243,7 @@ namespace for (size_t y = 0; y < height; ++y) { - const size_t sy = flipY ? (height - 1 - y) : y; + const size_t sy = flipY ? (height - 1 - y) : y; uint8_t* pDest = destImage.pixels + y * destImage.rowPitch; if (!swapXY && !flipX) @@ -261,10 +254,9 @@ namespace for (size_t x = 0; x < width; ++x) { - const size_t sx = flipX ? (width - 1 - x) : x; - const uint8_t* pSrc = swapXY - ? srcImage.pixels + sx * srcImage.rowPitch + sy * bytesPerPixel - : srcImage.pixels + sy * srcImage.rowPitch + sx * bytesPerPixel; + const size_t sx = flipX ? (width - 1 - x) : x; + const uint8_t* pSrc = swapXY ? srcImage.pixels + sx * srcImage.rowPitch + sy * bytesPerPixel : + srcImage.pixels + sy * srcImage.rowPitch + sx * bytesPerPixel; memcpy(pDest + x * bytesPerPixel, pSrc, bytesPerPixel); } }