diff --git a/DirectXTex/DirectXTex.h b/DirectXTex/DirectXTex.h index 01bbee7..65aca97 100644 --- a/DirectXTex/DirectXTex.h +++ b/DirectXTex/DirectXTex.h @@ -615,6 +615,15 @@ namespace DirectX HRESULT __cdecl Evaluate( _In_ const Image& image, _In_ std::function pixelFunc ); + HRESULT __cdecl Transform( _In_ const Image& image, + _In_ std::function pixelFunc, + ScratchImage& result ); + HRESULT __cdecl Transform(_In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ std::function pixelFunc, + ScratchImage& result ); + //--------------------------------------------------------------------------------- // WIC utility code diff --git a/DirectXTex/DirectXTexConvert.cpp b/DirectXTex/DirectXTexConvert.cpp index 7af37f4..284fab9 100644 --- a/DirectXTex/DirectXTexConvert.cpp +++ b/DirectXTex/DirectXTexConvert.cpp @@ -4696,7 +4696,10 @@ HRESULT DirectX::Convert( } if ((src.width > UINT32_MAX) || (src.height > UINT32_MAX)) + { + result.Release(); return E_FAIL; + } const Image& dst = dest[index]; assert(dst.format == format); @@ -4733,7 +4736,10 @@ HRESULT DirectX::Convert( for (size_t slice = 0; slice < d; ++slice, ++index) { if (index >= nimages) + { + result.Release(); return E_FAIL; + } const Image& src = srcImages[index]; if (src.format != metadata.format) @@ -4743,7 +4749,10 @@ HRESULT DirectX::Convert( } if ((src.width > UINT32_MAX) || (src.height > UINT32_MAX)) + { + result.Release(); return E_FAIL; + } const Image& dst = dest[index]; assert(dst.format == format); @@ -4777,6 +4786,7 @@ HRESULT DirectX::Convert( break; default: + result.Release(); return E_FAIL; } diff --git a/DirectXTex/DirectXTexMisc.cpp b/DirectXTex/DirectXTexMisc.cpp index df821b4..84c3457 100644 --- a/DirectXTex/DirectXTexMisc.cpp +++ b/DirectXTex/DirectXTexMisc.cpp @@ -201,6 +201,58 @@ namespace return S_OK; } + + + //------------------------------------------------------------------------------------- + HRESULT Transform_( + const Image& srcImage, + std::function pixelFunc, + const Image& destImage) + { + if (!pixelFunc) + return E_INVALIDARG; + + if (!srcImage.pixels || !destImage.pixels) + return E_POINTER; + + if (srcImage.width != destImage.width || srcImage.height != destImage.height || srcImage.format != destImage.format) + return E_FAIL; + + const size_t width = srcImage.width; + + ScopedAlignedArrayXMVECTOR scanlines(reinterpret_cast(_aligned_malloc((sizeof(XMVECTOR)*width*2), 16))); + if (!scanlines) + return E_OUTOFMEMORY; + + XMVECTOR* sScanline = scanlines.get(); + XMVECTOR* dScanline = scanlines.get() + width; + + const uint8_t *pSrc = srcImage.pixels; + const size_t spitch = srcImage.rowPitch; + + uint8_t *pDest = destImage.pixels; + const size_t dpitch = destImage.rowPitch; + + for (size_t h = 0; h < srcImage.height; ++h) + { + if (!_LoadScanline(sScanline, width, pSrc, spitch, srcImage.format)) + return E_FAIL; + +#ifdef _DEBUG + memset(dScanline, 0xCD, sizeof(XMVECTOR)*width); +#endif + + pixelFunc(dScanline, sScanline, width, h); + + if (!_StoreScanline(pDest, destImage.rowPitch, destImage.format, dScanline, width)) + return E_FAIL; + + pSrc += spitch; + pDest += dpitch; + } + + return S_OK; + } }; @@ -338,8 +390,12 @@ HRESULT DirectX::ComputeMSE( if (image1.width != image2.width || image1.height != image2.height) return E_INVALIDARG; + if (!IsValid(image1.format) || !IsValid(image2.format)) + return E_INVALIDARG; + if (IsPlanar(image1.format) || IsPlanar(image2.format) - || IsPalettized(image1.format) || IsPalettized(image2.format)) + || IsPalettized(image1.format) || IsPalettized(image2.format) + || IsTypeless(image1.format) || IsTypeless(image2.format)) return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); if (IsCompressed(image1.format)) @@ -416,7 +472,10 @@ HRESULT DirectX::Evaluate( || image.height > UINT32_MAX) return E_INVALIDARG; - if (IsPlanar(image.format) || IsPalettized(image.format)) + if (!IsValid(image.format)) + return E_INVALIDARG; + + if (IsPlanar(image.format) || IsPalettized(image.format) || IsTypeless(image.format)) return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); if (IsCompressed(image.format)) @@ -436,6 +495,172 @@ HRESULT DirectX::Evaluate( { return Evaluate_(image, pixelFunc); } +} - return E_NOTIMPL; -} \ No newline at end of file + +//------------------------------------------------------------------------------------- +// Use a user-supplied function to compute a new image from an input image +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT DirectX::Transform( + const Image& image, + std::function pixelFunc, + ScratchImage& result) +{ + if (image.width > UINT32_MAX + || image.height > UINT32_MAX) + return E_INVALIDARG; + + if (IsPlanar(image.format) || IsPalettized(image.format) || IsCompressed(image.format) || IsTypeless(image.format)) + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + HRESULT hr = result.Initialize2D(image.format, image.width, image.height, 1, 1); + if (FAILED(hr)) + return hr; + + const Image* dimg = result.GetImage(0, 0, 0); + if (!dimg) + { + result.Release(); + return E_POINTER; + } + + hr = Transform_(image, pixelFunc, *dimg); + if (FAILED(hr)) + { + result.Release(); + return hr; + } + + return S_OK; +} + +_Use_decl_annotations_ +HRESULT DirectX::Transform( + const Image* srcImages, + size_t nimages, const TexMetadata& metadata, + std::function pixelFunc, + ScratchImage& result) +{ + if (!srcImages || !nimages) + return E_INVALIDARG; + + if (IsPlanar(metadata.format) || IsPalettized(metadata.format) || IsCompressed(metadata.format) || IsTypeless(metadata.format)) + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + if (metadata.width > UINT32_MAX + || metadata.height > UINT32_MAX) + return E_INVALIDARG; + + if (metadata.IsVolumemap() && metadata.depth > UINT32_MAX) + return E_INVALIDARG; + + HRESULT hr = result.Initialize(metadata); + if (FAILED(hr)) + return hr; + + if (nimages != result.GetImageCount()) + { + result.Release(); + return E_FAIL; + } + + const Image* dest = result.GetImages(); + if (!dest) + { + result.Release(); + return E_POINTER; + } + + switch (metadata.dimension) + { + case TEX_DIMENSION_TEXTURE1D: + case TEX_DIMENSION_TEXTURE2D: + for (size_t index = 0; index < nimages; ++index) + { + const Image& src = srcImages[index]; + if (src.format != metadata.format) + { + result.Release(); + return E_FAIL; + } + + if ((src.width > UINT32_MAX) || (src.height > UINT32_MAX)) + { + result.Release(); + return E_FAIL; + } + + const Image& dst = dest[index]; + + if (src.width != dst.width || src.height != dst.height) + { + result.Release(); + return E_FAIL; + } + + hr = Transform_(src, pixelFunc, dst); + if (FAILED(hr)) + { + result.Release(); + return hr; + } + } + break; + + case TEX_DIMENSION_TEXTURE3D: + { + size_t index = 0; + size_t d = metadata.depth; + for (size_t level = 0; level < metadata.mipLevels; ++level) + { + for (size_t slice = 0; slice < d; ++slice, ++index) + { + if (index >= nimages) + { + result.Release(); + return E_FAIL; + } + + const Image& src = srcImages[index]; + if (src.format != metadata.format) + { + result.Release(); + return E_FAIL; + } + + if ((src.width > UINT32_MAX) || (src.height > UINT32_MAX)) + { + result.Release(); + return E_FAIL; + } + + const Image& dst = dest[index]; + + if (src.width != dst.width || src.height != dst.height) + { + result.Release(); + return E_FAIL; + } + + hr = Transform_(src, pixelFunc, dst); + if (FAILED(hr)) + { + result.Release(); + return hr; + } + } + + if (d > 1) + d >>= 1; + } + } + break; + + default: + result.Release(); + return E_FAIL; + } + + return S_OK; +} diff --git a/Texconv/texconv.cpp b/Texconv/texconv.cpp index b8151f7..e353b9b 100644 --- a/Texconv/texconv.cpp +++ b/Texconv/texconv.cpp @@ -35,7 +35,10 @@ #include "directxtex.h" +#include "DirectXPackedVector.h" + using namespace DirectX; +using namespace DirectX::PackedVector; using Microsoft::WRL::ComPtr; enum OPTIONS @@ -80,6 +83,7 @@ enum OPTIONS OPT_COMPRESS_DITHER, OPT_WIC_QUALITY, OPT_WIC_LOSSLESS, + OPT_COLORKEY, OPT_MAX }; @@ -144,6 +148,7 @@ SValue g_pOptions[] = { L"bcdither", OPT_COMPRESS_DITHER }, { L"wicq", OPT_WIC_QUALITY }, { L"wiclossless", OPT_WIC_LOSSLESS }, + { L"c", OPT_COLORKEY }, { nullptr, 0 } }; @@ -641,6 +646,7 @@ namespace wprintf( L" -aw BC7 GPU compressor weighting for alpha error metric\n" L" (defaults to 1.0)\n"); + wprintf(L" -c colorkey (a.k.a. chromakey) transparency\n"); wprintf(L"\n"); wprintf(L" : "); @@ -840,6 +846,8 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) float nmapAmplitude = 1.f; float wicQuality = -1.f; bool wicLossless = false; + bool useColorKey = false; + DWORD colorKey = 0; wchar_t szPrefix[MAX_PATH]; wchar_t szSuffix[MAX_PATH]; @@ -903,6 +911,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) case OPT_NORMAL_MAP: case OPT_NORMAL_MAP_AMPLITUDE: case OPT_WIC_QUALITY: + case OPT_COLORKEY: if (!*pValue) { if ((iArg + 1 >= argc)) @@ -1179,6 +1188,18 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) case OPT_WIC_LOSSLESS: wicLossless = true; break; + + case OPT_COLORKEY: + if (swscanf_s(pValue, L"%x", &colorKey) != 1) + { + printf("Invalid value specified with -c (%ls)\n", pValue); + printf("\n"); + PrintUsage(); + return 1; + } + colorKey &= 0xFFFFFF; + useColorKey = true; + break; } } else if (wcspbrk(pArg, L"?*") != nullptr) @@ -1409,13 +1430,14 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) hr = ConvertToSinglePlane(img, nimg, info, *timage); if (FAILED(hr)) { - wprintf(L" FAILED [converttosingeplane] (%x)\n", hr); + wprintf(L" FAILED [converttosingleplane] (%x)\n", hr); continue; } auto& tinfo = timage->GetMetadata(); info.format = tinfo.format; + assert(info.width == tinfo.width); assert(info.height == tinfo.height); assert(info.depth == tinfo.depth); @@ -1626,6 +1648,61 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) cimage.reset(); } + // --- ColorKey/ChromaKey ------------------------------------------------------ + if (useColorKey && HasAlpha(info.format)) + { + std::unique_ptr timage(new (std::nothrow) ScratchImage); + if (!timage) + { + wprintf(L"\nERROR: Memory allocation failed\n"); + return 1; + } + + XMVECTOR colorKeyValue = XMLoadColor(reinterpret_cast(&colorKey)); + + hr = Transform(image->GetImages(), image->GetImageCount(), image->GetMetadata(), + [&](XMVECTOR* outPixels, const XMVECTOR* inPixels, size_t width, size_t y) + { + static const XMVECTORF32 s_tolerance = { 0.2f, 0.2f, 0.2f, 0.f }; + + UNREFERENCED_PARAMETER(y); + + for (size_t j = 0; j < width; ++j) + { + XMVECTOR value = inPixels[j]; + + if (XMVector3NearEqual(value, colorKeyValue, s_tolerance)) + { + value = g_XMZero; + } + else + { + value = XMVectorSelect(g_XMOne, value, g_XMSelect1110); + } + + outPixels[j] = value; + } + }, *timage); + if (FAILED(hr)) + { + wprintf(L" FAILED [colorkey] (%x)\n", hr); + return 1; + } + + auto& tinfo = timage->GetMetadata(); + + 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(); + } + // --- Generate mips ----------------------------------------------------------- if (!ispow2(info.width) || !ispow2(info.height) || !ispow2(info.depth)) { @@ -1767,7 +1844,6 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) 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);