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..0d9fa42c 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,101 @@ 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 +290,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 +307,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 +342,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 +363,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 +391,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 +408,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 +450,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 +487,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 +507,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)) {