Add CompressEx and ConvertEx methods that report progress (#375)

This commit is contained in:
Nicholas Hayes 2023-08-18 15:43:30 -06:00 committed by GitHub
parent 7e7b11db04
commit 1d1d4d0134
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 437 additions and 50 deletions

View File

@ -664,12 +664,26 @@ namespace DirectX
constexpr float TEX_THRESHOLD_DEFAULT = 0.5f; constexpr float TEX_THRESHOLD_DEFAULT = 0.5f;
// Default value for alpha threshold used when converting to 1-bit alpha // Default value for alpha threshold used when converting to 1-bit alpha
struct ConvertOptions
{
TEX_FILTER_FLAGS filter;
float threshold;
};
HRESULT __cdecl Convert( HRESULT __cdecl Convert(
_In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ TEX_FILTER_FLAGS filter, _In_ float threshold, _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ TEX_FILTER_FLAGS filter, _In_ float threshold,
_Out_ ScratchImage& image) noexcept; _Out_ ScratchImage& image) noexcept;
HRESULT __cdecl Convert( HRESULT __cdecl Convert(
_In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata,
_In_ DXGI_FORMAT format, _In_ TEX_FILTER_FLAGS filter, _In_ float threshold, _Out_ ScratchImage& result) noexcept; _In_ DXGI_FORMAT format, _In_ TEX_FILTER_FLAGS filter, _In_ float threshold, _Out_ ScratchImage& result) noexcept;
HRESULT __cdecl ConvertEx(
_In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ const ConvertOptions& options,
_Out_ ScratchImage& image, _In_opt_ std::function<bool __cdecl(size_t, size_t)> statusCallBack = nullptr);
HRESULT __cdecl ConvertEx(
_In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata,
_In_ DXGI_FORMAT format, _In_ const ConvertOptions& options, _Out_ ScratchImage& result,
_In_opt_ std::function<bool __cdecl(size_t, size_t)> statusCallBack = nullptr);
// Convert the image to a new format // Convert the image to a new format
HRESULT __cdecl ConvertToSinglePlane(_In_ const Image& srcImage, _Out_ ScratchImage& image) noexcept; HRESULT __cdecl ConvertToSinglePlane(_In_ const Image& srcImage, _Out_ ScratchImage& image) noexcept;
@ -756,6 +770,16 @@ namespace DirectX
// Compress is free to use multithreading to improve performance (by default it does not use multithreading) // Compress is free to use multithreading to improve performance (by default it does not use multithreading)
}; };
constexpr float TEX_ALPHA_WEIGHT_DEFAULT = 1.0f;
// Default value for alpha weight used for GPU BC7 compression
struct CompressOptions
{
TEX_COMPRESS_FLAGS flags;
float threshold;
float alphaWeight;
};
HRESULT __cdecl Compress( HRESULT __cdecl Compress(
_In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress, _In_ float threshold, _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress, _In_ float threshold,
_Out_ ScratchImage& cImage) noexcept; _Out_ ScratchImage& cImage) noexcept;
@ -764,6 +788,14 @@ namespace DirectX
_In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress, _In_ float threshold, _Out_ ScratchImage& cImages) noexcept; _In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress, _In_ float threshold, _Out_ ScratchImage& cImages) noexcept;
// Note that threshold is only used by BC1. TEX_THRESHOLD_DEFAULT is a typical value to use // Note that threshold is only used by BC1. TEX_THRESHOLD_DEFAULT is a typical value to use
HRESULT __cdecl CompressEx(
_In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ const CompressOptions& options,
_Out_ ScratchImage& cImage, _In_opt_ std::function<bool __cdecl(size_t, size_t)> statusCallBack = nullptr);
HRESULT __cdecl CompressEx(
_In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata,
_In_ DXGI_FORMAT format, _In_ const CompressOptions& options, _Out_ ScratchImage& cImages,
_In_opt_ std::function<bool __cdecl(size_t, size_t)> statusCallBack = nullptr);
#if defined(__d3d11_h__) || defined(__d3d11_x_h__) #if defined(__d3d11_h__) || defined(__d3d11_x_h__)
HRESULT __cdecl Compress( HRESULT __cdecl Compress(
_In_ ID3D11Device* pDevice, _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress, _In_ ID3D11Device* pDevice, _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress,
@ -772,6 +804,14 @@ namespace DirectX
_In_ ID3D11Device* pDevice, _In_ const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, _In_ ID3D11Device* pDevice, _In_ const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata,
_In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress, _In_ float alphaWeight, _Out_ ScratchImage& cImages) noexcept; _In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress, _In_ float alphaWeight, _Out_ ScratchImage& cImages) noexcept;
// DirectCompute-based compression (alphaWeight is only used by BC7. 1.0 is the typical value to use) // DirectCompute-based compression (alphaWeight is only used by BC7. 1.0 is the typical value to use)
HRESULT __cdecl CompressEx(
_In_ ID3D11Device* pDevice, _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ const CompressOptions& options,
_Out_ ScratchImage& image, _In_opt_ std::function<bool __cdecl(size_t, size_t)> statusCallBack = nullptr);
HRESULT __cdecl CompressEx(
_In_ ID3D11Device* pDevice, _In_ const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata,
_In_ DXGI_FORMAT format, _In_ const CompressOptions& options, _Out_ ScratchImage& cImages,
_In_opt_ std::function<bool __cdecl(size_t, size_t)> statusCallBack = nullptr);
#endif #endif
HRESULT __cdecl Decompress(_In_ const Image& cImage, _In_ DXGI_FORMAT format, _Out_ ScratchImage& image) noexcept; HRESULT __cdecl Decompress(_In_ const Image& cImage, _In_ DXGI_FORMAT format, _Out_ ScratchImage& image) noexcept;

View File

@ -74,7 +74,8 @@ namespace
const Image& result, const Image& result,
uint32_t bcflags, uint32_t bcflags,
TEX_FILTER_FLAGS srgb, TEX_FILTER_FLAGS srgb,
float threshold) noexcept float threshold,
const std::function<bool __cdecl(size_t, size_t)>& statusCallback) noexcept
{ {
if (!image.pixels || !result.pixels) if (!image.pixels || !result.pixels)
return E_POINTER; return E_POINTER;
@ -111,6 +112,14 @@ namespace
const size_t rowPitch = image.rowPitch; const size_t rowPitch = image.rowPitch;
for (size_t h = 0; h < image.height; h += 4) for (size_t h = 0; h < image.height; h += 4)
{ {
if (statusCallback)
{
if (!statusCallback(h, image.height))
{
return E_ABORT;
}
}
const uint8_t *sptr = pSrc; const uint8_t *sptr = pSrc;
uint8_t* dptr = pDest; uint8_t* dptr = pDest;
const size_t ph = std::min<size_t>(4, image.height - h); const size_t ph = std::min<size_t>(4, image.height - h);
@ -203,7 +212,8 @@ namespace
const Image& result, const Image& result,
uint32_t bcflags, uint32_t bcflags,
TEX_FILTER_FLAGS srgb, TEX_FILTER_FLAGS srgb,
float threshold) noexcept float threshold,
const std::function<bool __cdecl(size_t, size_t)>& statusCallback) noexcept
{ {
if (!image.pixels || !result.pixels) if (!image.pixels || !result.pixels)
return E_POINTER; return E_POINTER;
@ -239,9 +249,22 @@ namespace
bool fail = false; bool fail = false;
#pragma omp parallel for size_t progress = 0;
bool abort = false;
const size_t progressTotal = std::max<size_t>(1, (image.height + 3) / 4);
#pragma omp parallel for shared(progress)
for (int nb = 0; nb < static_cast<int>(nBlocks); ++nb) for (int nb = 0; nb < static_cast<int>(nBlocks); ++nb)
{ {
#pragma omp flush (abort)
if (abort)
{
// Short circuit the loop body if an abort is requested.
// OpenMP 2.0 does not support cancellation of a 'parallel for' loop.
continue;
}
const int nbWidth = std::max<int>(1, int((image.width + 3) / 4)); const int nbWidth = std::max<int>(1, int((image.width + 3) / 4));
int y = nb / nbWidth; int y = nb / nbWidth;
@ -323,9 +346,29 @@ namespace
pfEncode(pDest, temp, bcflags); pfEncode(pDest, temp, bcflags);
else else
D3DXEncodeBC1(pDest, temp, threshold, bcflags); D3DXEncodeBC1(pDest, temp, threshold, bcflags);
// Report progress when a new row is reached.
if (x == 0 && statusCallback)
{
#pragma omp atomic
progress += 4;
if (!statusCallback(progress, progressTotal))
{
abort = true;
#pragma omp flush (abort)
}
}
} }
return (fail) ? E_FAIL : S_OK; if (abort)
{
return E_ABORT;
}
else
{
return (fail) ? E_FAIL : S_OK;
}
} }
#endif // _OPENMP #endif // _OPENMP
@ -592,6 +635,38 @@ HRESULT DirectX::Compress(
TEX_COMPRESS_FLAGS compress, TEX_COMPRESS_FLAGS compress,
float threshold, float threshold,
ScratchImage& image) noexcept ScratchImage& image) noexcept
{
CompressOptions options = {};
options.flags = compress;
options.threshold = threshold;
return CompressEx(srcImage, format, options, image, nullptr);
}
_Use_decl_annotations_
HRESULT DirectX::Compress(
const Image* srcImages,
size_t nimages,
const TexMetadata& metadata,
DXGI_FORMAT format,
TEX_COMPRESS_FLAGS compress,
float threshold,
ScratchImage& cImages) noexcept
{
CompressOptions options = {};
options.flags = compress;
options.threshold = threshold;
return CompressEx(srcImages, nimages, metadata, format, options, cImages, nullptr);
}
_Use_decl_annotations_
HRESULT DirectX::CompressEx(
const Image& srcImage,
DXGI_FORMAT format,
const CompressOptions& options,
ScratchImage& image,
std::function<bool __cdecl(size_t, size_t)> statusCallback)
{ {
if (IsCompressed(srcImage.format) || !IsCompressed(format)) if (IsCompressed(srcImage.format) || !IsCompressed(format))
return E_INVALIDARG; return E_INVALIDARG;
@ -612,35 +687,56 @@ HRESULT DirectX::Compress(
return E_POINTER; return E_POINTER;
} }
if (statusCallback)
{
if (!statusCallback(0, img->height))
{
image.Release();
return E_ABORT;
}
}
// Compress single image // Compress single image
if (compress & TEX_COMPRESS_PARALLEL) if (options.flags & TEX_COMPRESS_PARALLEL)
{ {
#ifndef _OPENMP #ifndef _OPENMP
return E_NOTIMPL; hr = E_NOTIMPL;
#else #else
hr = CompressBC_Parallel(srcImage, *img, GetBCFlags(compress), GetSRGBFlags(compress), threshold); hr = CompressBC_Parallel(srcImage, *img, GetBCFlags(options.flags), GetSRGBFlags(options.flags), options.threshold, statusCallback);
#endif // _OPENMP #endif // _OPENMP
} }
else else
{ {
hr = CompressBC(srcImage, *img, GetBCFlags(compress), GetSRGBFlags(compress), threshold); hr = CompressBC(srcImage, *img, GetBCFlags(options.flags), GetSRGBFlags(options.flags), options.threshold, statusCallback);
} }
if (FAILED(hr)) if (FAILED(hr))
{
image.Release(); image.Release();
return hr;
}
return hr; if (statusCallback)
{
if (!statusCallback(img->height, img->height))
{
image.Release();
return E_ABORT;
}
}
return S_OK;
} }
_Use_decl_annotations_ _Use_decl_annotations_
HRESULT DirectX::Compress( HRESULT DirectX::CompressEx(
const Image* srcImages, const Image* srcImages,
size_t nimages, size_t nimages,
const TexMetadata& metadata, const TexMetadata& metadata,
DXGI_FORMAT format, DXGI_FORMAT format,
TEX_COMPRESS_FLAGS compress, const CompressOptions& options,
float threshold, ScratchImage& cImages,
ScratchImage& cImages) noexcept std::function<bool __cdecl(size_t, size_t)> statusCallback)
{ {
if (!srcImages || !nimages) if (!srcImages || !nimages)
return E_INVALIDARG; return E_INVALIDARG;
@ -654,6 +750,19 @@ HRESULT DirectX::Compress(
cImages.Release(); cImages.Release();
if (statusCallback
&& nimages == 1
&& !metadata.IsVolumemap()
&& metadata.mipLevels == 1
&& metadata.arraySize == 1)
{
// If progress reporting is requested when compressing a single 1D or 2D image, call
// the CompressEx overload that takes a single image.
// This provides a better user experience as progress will be reported as the image
// is being processed, instead of after processing has been completed.
return CompressEx(srcImages[0], format, options, cImages, statusCallback);
}
TexMetadata mdata2 = metadata; TexMetadata mdata2 = metadata;
mdata2.format = format; mdata2.format = format;
HRESULT hr = cImages.Initialize(mdata2); HRESULT hr = cImages.Initialize(mdata2);
@ -673,6 +782,15 @@ HRESULT DirectX::Compress(
return E_POINTER; return E_POINTER;
} }
if (statusCallback)
{
if (!statusCallback(0, nimages))
{
cImages.Release();
return E_ABORT;
}
}
for (size_t index = 0; index < nimages; ++index) for (size_t index = 0; index < nimages; ++index)
{ {
assert(dest[index].format == format); assert(dest[index].format == format);
@ -685,33 +803,44 @@ HRESULT DirectX::Compress(
return E_FAIL; return E_FAIL;
} }
if ((compress & TEX_COMPRESS_PARALLEL)) if (options.flags & TEX_COMPRESS_PARALLEL)
{ {
#ifndef _OPENMP #ifndef _OPENMP
return E_NOTIMPL; hr = E_NOTIMPL;
#else #else
if (compress & TEX_COMPRESS_PARALLEL) hr = CompressBC_Parallel(src, dest[index], GetBCFlags(options.flags), GetSRGBFlags(options.flags), options.threshold, nullptr);
{
hr = CompressBC_Parallel(src, dest[index], GetBCFlags(compress), GetSRGBFlags(compress), threshold);
if (FAILED(hr))
{
cImages.Release();
return hr;
}
}
#endif // _OPENMP #endif // _OPENMP
} }
else else
{ {
hr = CompressBC(src, dest[index], GetBCFlags(compress), GetSRGBFlags(compress), threshold); hr = CompressBC(src, dest[index], GetBCFlags(options.flags), GetSRGBFlags(options.flags), options.threshold, nullptr);
if (FAILED(hr)) }
if (FAILED(hr))
{
cImages.Release();
return hr;
}
if (statusCallback)
{
if (!statusCallback(index, nimages))
{ {
cImages.Release(); cImages.Release();
return hr; return E_ABORT;
} }
} }
} }
if (statusCallback)
{
if (!statusCallback(nimages, nimages))
{
cImages.Release();
return E_ABORT;
}
}
return S_OK; return S_OK;
} }

View File

@ -219,6 +219,40 @@ HRESULT DirectX::Compress(
TEX_COMPRESS_FLAGS compress, TEX_COMPRESS_FLAGS compress,
float alphaWeight, float alphaWeight,
ScratchImage& image) noexcept ScratchImage& image) noexcept
{
CompressOptions options = {};
options.flags = compress;
options.alphaWeight = alphaWeight;
return CompressEx(pDevice, srcImage, format, options, image, nullptr);
}
_Use_decl_annotations_
HRESULT DirectX::Compress(
ID3D11Device* pDevice,
const Image* srcImages,
size_t nimages,
const TexMetadata& metadata,
DXGI_FORMAT format,
TEX_COMPRESS_FLAGS compress,
float alphaWeight,
ScratchImage& cImages) noexcept
{
CompressOptions options = {};
options.flags = compress;
options.alphaWeight = alphaWeight;
return CompressEx(pDevice, srcImages, nimages, metadata, format, options, cImages);
}
_Use_decl_annotations_
HRESULT DirectX::CompressEx(
ID3D11Device* pDevice,
const Image& srcImage,
DXGI_FORMAT format,
const CompressOptions& options,
ScratchImage& image,
std::function<bool __cdecl(size_t, size_t)> statusCallback)
{ {
if (!pDevice || IsCompressed(srcImage.format) || !IsCompressed(format)) if (!pDevice || IsCompressed(srcImage.format) || !IsCompressed(format))
return E_INVALIDARG; return E_INVALIDARG;
@ -236,7 +270,7 @@ HRESULT DirectX::Compress(
if (FAILED(hr)) if (FAILED(hr))
return hr; return hr;
hr = gpubc->Prepare(srcImage.width, srcImage.height, compress, format, alphaWeight); hr = gpubc->Prepare(srcImage.width, srcImage.height, options.flags, format, options.alphaWeight);
if (FAILED(hr)) if (FAILED(hr))
return hr; return hr;
@ -252,23 +286,45 @@ HRESULT DirectX::Compress(
return E_POINTER; return E_POINTER;
} }
hr = GPUCompress(gpubc.get(), srcImage, *img, compress); if (statusCallback)
if (FAILED(hr)) {
image.Release(); if (!statusCallback(0, 100))
{
image.Release();
return E_ABORT;
}
}
return hr; hr = GPUCompress(gpubc.get(), srcImage, *img, options.flags);
if (FAILED(hr))
{
image.Release();
return hr;
}
if (statusCallback)
{
if (!statusCallback(100, 100))
{
image.Release();
return E_ABORT;
}
}
return S_OK;
} }
_Use_decl_annotations_ _Use_decl_annotations_
HRESULT DirectX::Compress( HRESULT DirectX::CompressEx(
ID3D11Device* pDevice, ID3D11Device* pDevice,
const Image* srcImages, const Image* srcImages,
size_t nimages, size_t nimages,
const TexMetadata& metadata, const TexMetadata& metadata,
DXGI_FORMAT format, DXGI_FORMAT format,
TEX_COMPRESS_FLAGS compress, const CompressOptions& options,
float alphaWeight, ScratchImage& cImages,
ScratchImage& cImages) noexcept std::function<bool __cdecl(size_t, size_t)> statusCallback)
{ {
if (!pDevice || !srcImages || !nimages) if (!pDevice || !srcImages || !nimages)
return E_INVALIDARG; return E_INVALIDARG;
@ -311,6 +367,15 @@ HRESULT DirectX::Compress(
return E_POINTER; return E_POINTER;
} }
if (statusCallback)
{
if (!statusCallback(0, nimages))
{
cImages.Release();
return E_ABORT;
}
}
// Process images (ordered by size) // Process images (ordered by size)
switch (metadata.dimension) switch (metadata.dimension)
{ {
@ -319,10 +384,11 @@ HRESULT DirectX::Compress(
{ {
size_t w = metadata.width; size_t w = metadata.width;
size_t h = metadata.height; size_t h = metadata.height;
size_t progress = 0;
for (size_t level = 0; level < metadata.mipLevels; ++level) for (size_t level = 0; level < metadata.mipLevels; ++level)
{ {
hr = gpubc->Prepare(w, h, compress, format, alphaWeight); hr = gpubc->Prepare(w, h, options.flags, format, options.alphaWeight);
if (FAILED(hr)) if (FAILED(hr))
{ {
cImages.Release(); cImages.Release();
@ -348,12 +414,21 @@ HRESULT DirectX::Compress(
return E_FAIL; return E_FAIL;
} }
hr = GPUCompress(gpubc.get(), src, dest[index], compress); hr = GPUCompress(gpubc.get(), src, dest[index], options.flags);
if (FAILED(hr)) if (FAILED(hr))
{ {
cImages.Release(); cImages.Release();
return hr; return hr;
} }
if (statusCallback)
{
if (!statusCallback(progress++, nimages))
{
cImages.Release();
return E_ABORT;
}
}
} }
if (h > 1) if (h > 1)
@ -370,10 +445,11 @@ HRESULT DirectX::Compress(
size_t w = metadata.width; size_t w = metadata.width;
size_t h = metadata.height; size_t h = metadata.height;
size_t d = metadata.depth; size_t d = metadata.depth;
size_t progress = 0;
for (size_t level = 0; level < metadata.mipLevels; ++level) for (size_t level = 0; level < metadata.mipLevels; ++level)
{ {
hr = gpubc->Prepare(w, h, compress, format, alphaWeight); hr = gpubc->Prepare(w, h, options.flags, format, options.alphaWeight);
if (FAILED(hr)) if (FAILED(hr))
{ {
cImages.Release(); cImages.Release();
@ -399,12 +475,21 @@ HRESULT DirectX::Compress(
return E_FAIL; return E_FAIL;
} }
hr = GPUCompress(gpubc.get(), src, dest[index], compress); hr = GPUCompress(gpubc.get(), src, dest[index], options.flags);
if (FAILED(hr)) if (FAILED(hr))
{ {
cImages.Release(); cImages.Release();
return hr; return hr;
} }
if (statusCallback)
{
if (!statusCallback(progress++, nimages))
{
cImages.Release();
return E_ABORT;
}
}
} }
if (h > 1) if (h > 1)
@ -423,5 +508,14 @@ HRESULT DirectX::Compress(
return HRESULT_E_NOT_SUPPORTED; return HRESULT_E_NOT_SUPPORTED;
} }
if (statusCallback)
{
if (!statusCallback(nimages, nimages))
{
cImages.Release();
return E_ABORT;
}
}
return S_OK; return S_OK;
} }

View File

@ -4798,7 +4798,8 @@ namespace
_In_ TEX_FILTER_FLAGS filter, _In_ TEX_FILTER_FLAGS filter,
_In_ const Image& destImage, _In_ const Image& destImage,
_In_ float threshold, _In_ float threshold,
size_t z) noexcept size_t z,
const std::function<bool __cdecl(size_t, size_t)>& statusCallback) noexcept
{ {
assert(srcImage.width == destImage.width); assert(srcImage.width == destImage.width);
assert(srcImage.height == destImage.height); assert(srcImage.height == destImage.height);
@ -4822,6 +4823,14 @@ namespace
for (size_t h = 0; h < srcImage.height; ++h) for (size_t h = 0; h < srcImage.height; ++h)
{ {
if (statusCallback)
{
if (!statusCallback(h, srcImage.height))
{
return E_ABORT;
}
}
if (!LoadScanline(scanline.get(), width, pSrc, srcImage.rowPitch, srcImage.format)) if (!LoadScanline(scanline.get(), width, pSrc, srcImage.rowPitch, srcImage.format))
return E_FAIL; return E_FAIL;
@ -4845,6 +4854,14 @@ namespace
// Ordered dithering // Ordered dithering
for (size_t h = 0; h < srcImage.height; ++h) for (size_t h = 0; h < srcImage.height; ++h)
{ {
if (statusCallback)
{
if (!statusCallback(h, srcImage.height))
{
return E_ABORT;
}
}
if (!LoadScanline(scanline.get(), width, pSrc, srcImage.rowPitch, srcImage.format)) if (!LoadScanline(scanline.get(), width, pSrc, srcImage.rowPitch, srcImage.format))
return E_FAIL; return E_FAIL;
@ -4862,6 +4879,14 @@ namespace
// No dithering // No dithering
for (size_t h = 0; h < srcImage.height; ++h) for (size_t h = 0; h < srcImage.height; ++h)
{ {
if (statusCallback)
{
if (!statusCallback(h, srcImage.height))
{
return E_ABORT;
}
}
if (!LoadScanline(scanline.get(), width, pSrc, srcImage.rowPitch, srcImage.format)) if (!LoadScanline(scanline.get(), width, pSrc, srcImage.rowPitch, srcImage.format))
return E_FAIL; return E_FAIL;
@ -5061,6 +5086,21 @@ HRESULT DirectX::Convert(
TEX_FILTER_FLAGS filter, TEX_FILTER_FLAGS filter,
float threshold, float threshold,
ScratchImage& image) noexcept ScratchImage& image) noexcept
{
ConvertOptions options = {};
options.filter = filter;
options.threshold = threshold;
return ConvertEx(srcImage, format, options, image, nullptr);
}
_Use_decl_annotations_
HRESULT DirectX::ConvertEx(
const Image& srcImage,
DXGI_FORMAT format,
const ConvertOptions& options,
ScratchImage& image,
std::function<bool __cdecl(size_t, size_t)> statusCallback)
{ {
if ((srcImage.format == format) || !IsValid(format)) if ((srcImage.format == format) || !IsValid(format))
return E_INVALIDARG; return E_INVALIDARG;
@ -5088,14 +5128,23 @@ HRESULT DirectX::Convert(
return E_POINTER; return E_POINTER;
} }
WICPixelFormatGUID pfGUID, targetGUID; if (statusCallback)
if (UseWICConversion(filter, srcImage.format, format, pfGUID, targetGUID))
{ {
hr = ConvertUsingWIC(srcImage, pfGUID, targetGUID, filter, threshold, *rimage); if (!statusCallback(0, rimage->height))
{
image.Release();
return E_ABORT;
}
}
WICPixelFormatGUID pfGUID, targetGUID;
if (UseWICConversion(options.filter, srcImage.format, format, pfGUID, targetGUID))
{
hr = ConvertUsingWIC(srcImage, pfGUID, targetGUID, options.filter, options.threshold, *rimage);
} }
else else
{ {
hr = ConvertCustom(srcImage, filter, *rimage, threshold, 0); hr = ConvertCustom(srcImage, options.filter, *rimage, options.threshold, 0, statusCallback);
} }
if (FAILED(hr)) if (FAILED(hr))
@ -5104,6 +5153,15 @@ HRESULT DirectX::Convert(
return hr; return hr;
} }
if (statusCallback)
{
if (!statusCallback(rimage->height, rimage->height))
{
image.Release();
return E_ABORT;
}
}
return S_OK; return S_OK;
} }
@ -5120,6 +5178,23 @@ HRESULT DirectX::Convert(
TEX_FILTER_FLAGS filter, TEX_FILTER_FLAGS filter,
float threshold, float threshold,
ScratchImage& result) noexcept ScratchImage& result) noexcept
{
ConvertOptions options = {};
options.filter = filter;
options.threshold = threshold;
return ConvertEx(srcImages, nimages, metadata, format, options, result, nullptr);
}
_Use_decl_annotations_
HRESULT DirectX::ConvertEx(
const Image* srcImages,
size_t nimages,
const TexMetadata& metadata,
DXGI_FORMAT format,
const ConvertOptions& options,
ScratchImage& result,
std::function<bool __cdecl(size_t, size_t)> statusCallback)
{ {
if (!srcImages || !nimages || (metadata.format == format) || !IsValid(format)) if (!srcImages || !nimages || (metadata.format == format) || !IsValid(format))
return E_INVALIDARG; return E_INVALIDARG;
@ -5133,6 +5208,19 @@ HRESULT DirectX::Convert(
if ((metadata.width > UINT32_MAX) || (metadata.height > UINT32_MAX)) if ((metadata.width > UINT32_MAX) || (metadata.height > UINT32_MAX))
return E_INVALIDARG; return E_INVALIDARG;
if (statusCallback
&& nimages == 1
&& !metadata.IsVolumemap()
&& metadata.mipLevels == 1
&& metadata.arraySize == 1)
{
// If progress reporting is requested when converting a single 1D or 2D image, call
// the ConvertEx overload that takes a single image.
// This provides a better user experience as progress will be reported as the image
// is being processed, instead of after processing has been completed.
return ConvertEx(srcImages[0], format, options, result, statusCallback);
}
TexMetadata mdata2 = metadata; TexMetadata mdata2 = metadata;
mdata2.format = format; mdata2.format = format;
HRESULT hr = result.Initialize(mdata2); HRESULT hr = result.Initialize(mdata2);
@ -5152,8 +5240,17 @@ HRESULT DirectX::Convert(
return E_POINTER; return E_POINTER;
} }
if (statusCallback)
{
if (!statusCallback(0, nimages))
{
result.Release();
return E_ABORT;
}
}
WICPixelFormatGUID pfGUID, targetGUID; WICPixelFormatGUID pfGUID, targetGUID;
const bool usewic = !metadata.IsPMAlpha() && UseWICConversion(filter, metadata.format, format, pfGUID, targetGUID); const bool usewic = !metadata.IsPMAlpha() && UseWICConversion(options.filter, metadata.format, format, pfGUID, targetGUID);
switch (metadata.dimension) switch (metadata.dimension)
{ {
@ -5185,11 +5282,11 @@ HRESULT DirectX::Convert(
if (usewic) if (usewic)
{ {
hr = ConvertUsingWIC(src, pfGUID, targetGUID, filter, threshold, dst); hr = ConvertUsingWIC(src, pfGUID, targetGUID, options.filter, options.threshold, dst);
} }
else else
{ {
hr = ConvertCustom(src, filter, dst, threshold, 0); hr = ConvertCustom(src, options.filter, dst, options.threshold, 0, nullptr);
} }
if (FAILED(hr)) if (FAILED(hr))
@ -5197,6 +5294,15 @@ HRESULT DirectX::Convert(
result.Release(); result.Release();
return hr; return hr;
} }
if (statusCallback)
{
if (!statusCallback(index, nimages))
{
result.Release();
return E_ABORT;
}
}
} }
break; break;
@ -5238,11 +5344,11 @@ HRESULT DirectX::Convert(
if (usewic) if (usewic)
{ {
hr = ConvertUsingWIC(src, pfGUID, targetGUID, filter, threshold, dst); hr = ConvertUsingWIC(src, pfGUID, targetGUID, options.filter, options.threshold, dst);
} }
else else
{ {
hr = ConvertCustom(src, filter, dst, threshold, slice); hr = ConvertCustom(src, options.filter, dst, options.threshold, slice, nullptr);
} }
if (FAILED(hr)) if (FAILED(hr))
@ -5250,6 +5356,15 @@ HRESULT DirectX::Convert(
result.Release(); result.Release();
return hr; return hr;
} }
if (statusCallback)
{
if (!statusCallback(index, nimages))
{
result.Release();
return E_ABORT;
}
}
} }
if (d > 1) if (d > 1)
@ -5263,6 +5378,15 @@ HRESULT DirectX::Convert(
return E_FAIL; return E_FAIL;
} }
if (statusCallback)
{
if (!statusCallback(nimages, nimages))
{
result.Release();
return E_ABORT;
}
}
return S_OK; return S_OK;
} }