From a30c37220b1b67bf7e7f98bd6680dfde8d4701d0 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Wed, 14 Sep 2016 00:03:22 -0700 Subject: [PATCH] Support premultiply reverse and -alpha switch --- DirectXTex/DirectXTex.h | 12 ++- DirectXTex/DirectXTexConvert.cpp | 2 +- DirectXTex/DirectXTexMipmaps.cpp | 2 +- DirectXTex/DirectXTexPMAlpha.cpp | 121 ++++++++++++++++++++++++++++--- DirectXTex/DirectXTexResize.cpp | 2 +- Texconv/texconv.cpp | 93 ++++++++++++++++++++++-- 6 files changed, 205 insertions(+), 27 deletions(-) diff --git a/DirectXTex/DirectXTex.h b/DirectXTex/DirectXTex.h index 65aca97..8e59c9b 100644 --- a/DirectXTex/DirectXTex.h +++ b/DirectXTex/DirectXTex.h @@ -15,10 +15,6 @@ #pragma once -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (_WIN32_WINNT <= _WIN32_WINNT_WIN8) -#error WIC is not supported on Windows Phone 8.0 -#endif - #include #include @@ -137,6 +133,7 @@ namespace DirectX bool __cdecl IsPMAlpha() const { return ((miscFlags2 & TEX_MISC2_ALPHA_MODE_MASK) == TEX_ALPHA_MODE_PREMULTIPLIED) != 0; } void __cdecl SetAlphaMode( TEX_ALPHA_MODE mode ) { miscFlags2 = (miscFlags2 & ~TEX_MISC2_ALPHA_MODE_MASK) | static_cast(mode); } + TEX_ALPHA_MODE __cdecl GetAlphaMode() const { return static_cast(miscFlags2 & TEX_MISC2_ALPHA_MODE_MASK); } // Helpers for miscFlags2 bool __cdecl IsVolumemap() const { return (dimension == TEX_DIMENSION_TEXTURE3D); } @@ -493,9 +490,10 @@ namespace DirectX // if the output format type is IsSRGB(), then SRGB_OUT is on by default }; - HRESULT __cdecl PremultiplyAlpha( _In_ const Image& srcImage, _In_ DWORD flags, _Out_ ScratchImage& image ); - HRESULT __cdecl PremultiplyAlpha( _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, _In_ DWORD flags, _Out_ ScratchImage& result ); - // Converts to a premultiplied alpha version of the texture + HRESULT __cdecl PremultiplyAlpha( _In_ const Image& srcImage, _In_ DWORD flags, _Out_ ScratchImage& image, bool reverse = false ); + HRESULT __cdecl PremultiplyAlpha( _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DWORD flags, _Out_ ScratchImage& result, bool reverse = false); + // Converts to/from a premultiplied alpha version of the texture enum TEX_COMPRESS_FLAGS { diff --git a/DirectXTex/DirectXTexConvert.cpp b/DirectXTex/DirectXTexConvert.cpp index 284fab9..2e3e13e 100644 --- a/DirectXTex/DirectXTexConvert.cpp +++ b/DirectXTex/DirectXTexConvert.cpp @@ -4680,7 +4680,7 @@ HRESULT DirectX::Convert( } WICPixelFormatGUID pfGUID, targetGUID; - bool usewic = UseWICConversion(filter, metadata.format, format, pfGUID, targetGUID); + bool usewic = !metadata.IsPMAlpha() && UseWICConversion(filter, metadata.format, format, pfGUID, targetGUID); switch (metadata.dimension) { diff --git a/DirectXTex/DirectXTexMipmaps.cpp b/DirectXTex/DirectXTexMipmaps.cpp index ec11166..0577aff 100644 --- a/DirectXTex/DirectXTexMipmaps.cpp +++ b/DirectXTex/DirectXTexMipmaps.cpp @@ -2746,7 +2746,7 @@ HRESULT DirectX::GenerateMipMaps( static_assert(TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK"); - if (UseWICFiltering(metadata.format, filter)) + if (!metadata.IsPMAlpha() && UseWICFiltering(metadata.format, filter)) { //--- Use WIC filtering to generate mipmaps ----------------------------------- switch (filter & TEX_FILTER_MASK) diff --git a/DirectXTex/DirectXTexPMAlpha.cpp b/DirectXTex/DirectXTexPMAlpha.cpp index 633c154..ae6717c 100644 --- a/DirectXTex/DirectXTexPMAlpha.cpp +++ b/DirectXTex/DirectXTexPMAlpha.cpp @@ -19,7 +19,8 @@ using namespace DirectX; namespace { - + //--------------------------------------------------------------------------------- + // NonPremultiplied alpha -> Premultiplied alpha HRESULT PremultiplyAlpha_(const Image& srcImage, const Image& destImage) { assert(srcImage.width == destImage.width); @@ -100,6 +101,89 @@ namespace return S_OK; } + + //--------------------------------------------------------------------------------- + // Premultiplied alpha -> NonPremultiplied alpha (a.k.a. Straight alpha) + HRESULT DemultiplyAlpha(const Image& srcImage, const Image& destImage) + { + assert(srcImage.width == destImage.width); + assert(srcImage.height == destImage.height); + + ScopedAlignedArrayXMVECTOR scanline(reinterpret_cast(_aligned_malloc((sizeof(XMVECTOR)*srcImage.width), 16))); + if (!scanline) + return E_OUTOFMEMORY; + + const uint8_t *pSrc = srcImage.pixels; + uint8_t *pDest = destImage.pixels; + if (!pSrc || !pDest) + return E_POINTER; + + for (size_t h = 0; h < srcImage.height; ++h) + { + if (!_LoadScanline(scanline.get(), srcImage.width, pSrc, srcImage.rowPitch, srcImage.format)) + return E_FAIL; + + XMVECTOR* ptr = scanline.get(); + for (size_t w = 0; w < srcImage.width; ++w) + { + XMVECTOR v = *ptr; + XMVECTOR alpha = XMVectorSplatW(*ptr); + alpha = XMVectorDivide(v, alpha); + *(ptr++) = XMVectorSelect(v, alpha, g_XMSelect1110); + } + + if (!_StoreScanline(pDest, destImage.rowPitch, destImage.format, scanline.get(), srcImage.width)) + return E_FAIL; + + pSrc += srcImage.rowPitch; + pDest += destImage.rowPitch; + } + + return S_OK; + } + + HRESULT DemultiplyAlphaLinear(const Image& srcImage, DWORD flags, const Image& destImage) + { + assert(srcImage.width == destImage.width); + assert(srcImage.height == destImage.height); + + static_assert(TEX_PMALPHA_SRGB_IN == TEX_FILTER_SRGB_IN, "TEX_PMALHPA_SRGB* should match TEX_FILTER_SRGB*"); + static_assert(TEX_PMALPHA_SRGB_OUT == TEX_FILTER_SRGB_OUT, "TEX_PMALHPA_SRGB* should match TEX_FILTER_SRGB*"); + static_assert(TEX_PMALPHA_SRGB == TEX_FILTER_SRGB, "TEX_PMALHPA_SRGB* should match TEX_FILTER_SRGB*"); + flags &= TEX_PMALPHA_SRGB; + + ScopedAlignedArrayXMVECTOR scanline(reinterpret_cast(_aligned_malloc((sizeof(XMVECTOR)*srcImage.width), 16))); + if (!scanline) + return E_OUTOFMEMORY; + + const uint8_t *pSrc = srcImage.pixels; + uint8_t *pDest = destImage.pixels; + if (!pSrc || !pDest) + return E_POINTER; + + for (size_t h = 0; h < srcImage.height; ++h) + { + if (!_LoadScanlineLinear(scanline.get(), srcImage.width, pSrc, srcImage.rowPitch, srcImage.format, flags)) + return E_FAIL; + + XMVECTOR* ptr = scanline.get(); + for (size_t w = 0; w < srcImage.width; ++w) + { + XMVECTOR v = *ptr; + XMVECTOR alpha = XMVectorSplatW(*ptr); + alpha = XMVectorDivide(v, alpha); + *(ptr++) = XMVectorSelect(v, alpha, g_XMSelect1110); + } + + if (!_StoreScanlineLinear(pDest, destImage.rowPitch, destImage.format, scanline.get(), srcImage.width, flags)) + return E_FAIL; + + pSrc += srcImage.rowPitch; + pDest += destImage.rowPitch; + } + + return S_OK; + } } @@ -108,13 +192,14 @@ namespace //===================================================================================== //------------------------------------------------------------------------------------- -// Converts to a premultiplied alpha version of the texture +// Converts to/from a premultiplied alpha version of the texture //------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::PremultiplyAlpha( const Image& srcImage, DWORD flags, - ScratchImage& image) + ScratchImage& image, + bool reverse) { if (!srcImage.pixels) return E_POINTER; @@ -140,7 +225,14 @@ HRESULT DirectX::PremultiplyAlpha( return E_POINTER; } - hr = (flags & TEX_PMALPHA_IGNORE_SRGB) ? PremultiplyAlpha_(srcImage, *rimage) : PremultiplyAlphaLinear(srcImage, flags, *rimage); + if (reverse) + { + hr = (flags & TEX_PMALPHA_IGNORE_SRGB) ? DemultiplyAlpha(srcImage, *rimage) : DemultiplyAlphaLinear(srcImage, flags, *rimage); + } + else + { + hr = (flags & TEX_PMALPHA_IGNORE_SRGB) ? PremultiplyAlpha_(srcImage, *rimage) : PremultiplyAlphaLinear(srcImage, flags, *rimage); + } if (FAILED(hr)) { image.Release(); @@ -152,7 +244,7 @@ HRESULT DirectX::PremultiplyAlpha( //------------------------------------------------------------------------------------- -// Converts to a premultiplied alpha version of the texture (complex) +// Converts to/from a premultiplied alpha version of the texture (complex) //------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::PremultiplyAlpha( @@ -160,7 +252,8 @@ HRESULT DirectX::PremultiplyAlpha( size_t nimages, const TexMetadata& metadata, DWORD flags, - ScratchImage& result) + ScratchImage& result, + bool reverse) { if (!srcImages || !nimages) return E_INVALIDARG; @@ -175,14 +268,11 @@ HRESULT DirectX::PremultiplyAlpha( if ((metadata.width > UINT32_MAX) || (metadata.height > UINT32_MAX)) return E_INVALIDARG; - if (metadata.IsPMAlpha()) - { - // Already premultiplied + if (metadata.IsPMAlpha() != reverse) return E_FAIL; - } TexMetadata mdata2 = metadata; - mdata2.SetAlphaMode(TEX_ALPHA_MODE_PREMULTIPLIED); + mdata2.SetAlphaMode(reverse ? TEX_ALPHA_MODE_STRAIGHT : TEX_ALPHA_MODE_PREMULTIPLIED); HRESULT hr = result.Initialize(mdata2); if (FAILED(hr)) return hr; @@ -221,7 +311,14 @@ HRESULT DirectX::PremultiplyAlpha( return E_FAIL; } - hr = (flags & TEX_PMALPHA_IGNORE_SRGB) ? PremultiplyAlpha_(src, dst) : PremultiplyAlphaLinear(src, flags, dst); + if (reverse) + { + hr = (flags & TEX_PMALPHA_IGNORE_SRGB) ? DemultiplyAlpha(src, dst) : DemultiplyAlphaLinear(src, flags, dst); + } + else + { + hr = (flags & TEX_PMALPHA_IGNORE_SRGB) ? PremultiplyAlpha_(src, dst) : PremultiplyAlphaLinear(src, flags, dst); + } if (FAILED(hr)) { result.Release(); diff --git a/DirectXTex/DirectXTexResize.cpp b/DirectXTex/DirectXTexResize.cpp index b6e3ca9..fa79231 100644 --- a/DirectXTex/DirectXTexResize.cpp +++ b/DirectXTex/DirectXTexResize.cpp @@ -923,7 +923,7 @@ HRESULT DirectX::Resize( if (FAILED(hr)) return hr; - bool usewic = UseWICFiltering(metadata.format, filter); + bool usewic = !metadata.IsPMAlpha() && UseWICFiltering(metadata.format, filter); WICPixelFormatGUID pfGUID = { 0 }; bool wicpf = (usewic) ? _DXGIToWIC(metadata.format, pfGUID, true) : false; diff --git a/Texconv/texconv.cpp b/Texconv/texconv.cpp index e353b9b..334bfb6 100644 --- a/Texconv/texconv.cpp +++ b/Texconv/texconv.cpp @@ -67,6 +67,7 @@ enum OPTIONS OPT_TYPELESS_UNORM, OPT_TYPELESS_FLOAT, OPT_PREMUL_ALPHA, + OPT_DEMUL_ALPHA, OPT_EXPAND_LUMINANCE, OPT_TA_WRAP, OPT_TA_MIRROR, @@ -132,6 +133,7 @@ SValue g_pOptions[] = { L"tu", OPT_TYPELESS_UNORM }, { L"tf", OPT_TYPELESS_FLOAT }, { L"pmalpha", OPT_PREMUL_ALPHA }, + { L"alpha", OPT_DEMUL_ALPHA }, { L"xlum", OPT_EXPAND_LUMINANCE }, { L"wrap", OPT_TA_WRAP }, { L"mirror", OPT_TA_MIRROR }, @@ -531,6 +533,19 @@ namespace break; } + switch (info.GetAlphaMode()) + { + case TEX_ALPHA_MODE_OPAQUE: + wprintf(L" \x0e0:Opaque"); + break; + case TEX_ALPHA_MODE_PREMULTIPLIED: + wprintf(L" \x0e0:PM"); + break; + case TEX_ALPHA_MODE_STRAIGHT: + wprintf(L" \x0e0:NonPM"); + break; + } + wprintf(L")"); } @@ -617,6 +632,7 @@ namespace wprintf(L" from color channels\n"); wprintf(L" -wrap, -mirror texture addressing mode (wrap, mirror, or clamp)\n"); wprintf(L" -pmalpha convert final texture to use premultiplied alpha\n"); + wprintf(L" -alpha convert premultiplied alpha to straight alpha\n"); wprintf(L" -pow2 resize to fit a power-of-2, respecting aspect ratio\n"); wprintf( L" -nmap converts height-map to normal-map\n" @@ -1019,6 +1035,24 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) } break; + case OPT_PREMUL_ALPHA: + if (dwOptions & (DWORD64(1) << OPT_DEMUL_ALPHA)) + { + wprintf(L"Can't use -pmalpha and -alpha at same time\n\n"); + PrintUsage(); + return 1; + } + break; + + case OPT_DEMUL_ALPHA: + if (dwOptions & (DWORD64(1) << OPT_PREMUL_ALPHA)) + { + wprintf(L"Can't use -pmalpha and -alpha at same time\n\n"); + PrintUsage(); + return 1; + } + break; + case OPT_TA_WRAP: if (dwFilterOpts & TEX_FILTER_MIRROR) { @@ -1497,6 +1531,55 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) } } + // --- Undo Premultiplied Alpha (if requested) --------------------------------- + if ((dwOptions & (DWORD64(1) << OPT_DEMUL_ALPHA)) + && HasAlpha(info.format) + && info.format != DXGI_FORMAT_A8_UNORM) + { + if (info.GetAlphaMode() == TEX_ALPHA_MODE_STRAIGHT) + { + printf("\nWARNING: Image is already using straight alpha\n"); + } + else if (!info.IsPMAlpha()) + { + printf("\nWARNING: Image is not using premultipled alpha\n"); + } + else + { + auto img = image->GetImage(0, 0, 0); + assert(img); + size_t nimg = image->GetImageCount(); + + std::unique_ptr timage(new (std::nothrow) ScratchImage); + if (!timage) + { + wprintf(L"\nERROR: Memory allocation failed\n"); + return 1; + } + + hr = PremultiplyAlpha(img, nimg, info, dwSRGB, *timage, true); + if (FAILED(hr)) + { + wprintf(L" FAILED [demultiply alpha] (%x)\n", hr); + continue; + } + + auto& tinfo = timage->GetMetadata(); + info.miscFlags2 = tinfo.miscFlags2; + + assert(info.width == tinfo.width); + assert(info.height == tinfo.height); + assert(info.depth == tinfo.depth); + assert(info.arraySize == tinfo.arraySize); + assert(info.mipLevels == tinfo.mipLevels); + assert(info.miscFlags == tinfo.miscFlags); + assert(info.dimension == tinfo.dimension); + + image.swap(timage); + cimage.reset(); + } + } + // --- Flip/Rotate ------------------------------------------------------------- if (dwOptions & ((DWORD64(1) << OPT_HFLIP) | (DWORD64(1) << OPT_VFLIP))) { @@ -1858,7 +1941,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) { if (info.IsPMAlpha()) { - printf("WARNING: Image is already using premultiplied alpha\n"); + printf("\nWARNING: Image is already using premultiplied alpha\n"); } else { @@ -2029,14 +2112,14 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) { info.SetAlphaMode(TEX_ALPHA_MODE_CUSTOM); } - else + else if (info.GetAlphaMode() == TEX_ALPHA_MODE_UNKNOWN) { info.SetAlphaMode(TEX_ALPHA_MODE_STRAIGHT); } } else { - info.miscFlags2 &= ~TEX_MISC2_ALPHA_MODE_MASK; + info.SetAlphaMode(TEX_ALPHA_MODE_UNKNOWN); } // --- Save result ------------------------------------------------------------- @@ -2166,11 +2249,11 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) if (nonpow2warn && maxSize <= 4096) { // Only emit this warning if ran with -fl set to a 9.x feature level - wprintf(L"\n WARNING: Not all feature levels support non-power-of-2 textures with mipmaps\n"); + wprintf(L"\nWARNING: Not all feature levels support non-power-of-2 textures with mipmaps\n"); } if (non4bc) - wprintf(L"\n WARNING: Direct3D requires BC image to be multiple of 4 in width & height\n"); + wprintf(L"\nWARNING: Direct3D requires BC image to be multiple of 4 in width & height\n"); if (dwOptions & (DWORD64(1) << OPT_TIMING)) {