mirror of
https://github.com/microsoft/DirectXTex.git
synced 2025-12-06 07:11:44 +01:00
Rework libjpeg Auxiliary supports to address some problems (#638)
This commit is contained in:
parent
f3b184489b
commit
f04d5cb47f
@ -91,9 +91,10 @@ namespace
|
||||
{
|
||||
jpeg_error_mgr err;
|
||||
jpeg_decompress_struct dec;
|
||||
JPEG_FLAGS userFlags;
|
||||
|
||||
public:
|
||||
JPEGDecompress() : err{}, dec{}
|
||||
JPEGDecompress(JPEG_FLAGS flags) : err{}, dec{}, userFlags(flags)
|
||||
{
|
||||
jpeg_std_error(&err);
|
||||
err.error_exit = &OnJPEGError;
|
||||
@ -110,23 +111,14 @@ namespace
|
||||
jpeg_stdio_src(&dec, fin);
|
||||
}
|
||||
|
||||
static DXGI_FORMAT TranslateColor(J_COLOR_SPACE colorspace) noexcept
|
||||
DXGI_FORMAT TranslateColor(J_COLOR_SPACE colorspace) noexcept
|
||||
{
|
||||
switch (colorspace)
|
||||
{
|
||||
case JCS_GRAYSCALE: // 1 component
|
||||
return DXGI_FORMAT_R8_UNORM;
|
||||
case JCS_RGB: // 3 component, Standard RGB
|
||||
return DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
case JCS_CMYK: // 4 component. WIC retuns this for CMYK
|
||||
return DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
#ifdef LIBJPEG_TURBO_VERSION
|
||||
case JCS_EXT_RGBX: // 4 component
|
||||
case JCS_EXT_RGBA:
|
||||
return DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
case JCS_RGB565:
|
||||
return DXGI_FORMAT_B5G6R5_UNORM;
|
||||
#endif
|
||||
return (userFlags & JPEG_FLAGS_DEFAULT_LINEAR) ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
||||
case JCS_YCbCr: // 3 component, YCbCr
|
||||
default:
|
||||
return DXGI_FORMAT_UNKNOWN;
|
||||
@ -146,7 +138,8 @@ namespace
|
||||
{
|
||||
throw std::runtime_error{ "unexpected out_color_space in jpeg_decompress_struct" };
|
||||
}
|
||||
if (metadata.format == DXGI_FORMAT_R8G8B8A8_UNORM)
|
||||
if (metadata.format == DXGI_FORMAT_R8G8B8A8_UNORM
|
||||
|| metadata.format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
|
||||
{
|
||||
metadata.miscFlags2 |= TEX_ALPHA_MODE_OPAQUE;
|
||||
}
|
||||
@ -168,19 +161,22 @@ namespace
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#if !defined(LIBJPEG_TURBO_VERSION)
|
||||
#ifndef LIBJPEG_TURBO_VERSION
|
||||
// shift pixels with padding in reverse order (to make it work in-memory)
|
||||
void ShiftPixels(ScratchImage& image) noexcept
|
||||
void ShiftPixels(const Image& image) noexcept
|
||||
{
|
||||
size_t num_pixels = dec.output_width * dec.output_height;
|
||||
uint8_t* dst = image.GetPixels();
|
||||
const uint8_t* src = dst;
|
||||
for (size_t i = num_pixels - 1; i > 0; i -= 1)
|
||||
uint8_t* scanline = image.pixels;
|
||||
for (size_t y = 0; y < image.height; ++y)
|
||||
{
|
||||
dst[4*i + 0] = src[3*i + 0];
|
||||
dst[4*i + 1] = src[3*i + 1];
|
||||
dst[4*i + 2] = src[3*i + 2];
|
||||
dst[4*i + 3] = 0;
|
||||
for (size_t i = (image.width - 1); i > 0; i -= 1)
|
||||
{
|
||||
scanline[4*i + 0] = scanline[3*i + 0];
|
||||
scanline[4*i + 1] = scanline[3*i + 1];
|
||||
scanline[4*i + 2] = scanline[3*i + 2];
|
||||
scanline[4*i + 3] = 0xff;
|
||||
}
|
||||
|
||||
scanline += image.rowPitch;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -199,31 +195,26 @@ namespace
|
||||
break;
|
||||
}
|
||||
GetMetadata(metadata);
|
||||
// if the JPEG library doesn't have color space conversion, it should be ERROR_NOT_SUPPORTED
|
||||
if (dec.jpeg_color_space == JCS_YCCK)
|
||||
{
|
||||
// CMYK is the known case
|
||||
if (dec.out_color_space == JCS_CMYK)
|
||||
return HRESULT_E_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (auto hr = image.Initialize2D(metadata.format, metadata.width, metadata.height, metadata.arraySize, metadata.mipLevels); FAILED(hr))
|
||||
if (auto hr = image.Initialize2D(metadata.format, metadata.width, metadata.height, 1u, 1u); FAILED(hr))
|
||||
return hr;
|
||||
|
||||
#ifdef LIBJPEG_TURBO_VERSION
|
||||
// grayscale is the only color space which uses 1 component
|
||||
if (dec.out_color_space != JCS_GRAYSCALE)
|
||||
// if there is no proper conversion to 4 component, E_FAIL...
|
||||
dec.out_color_space = JCS_EXT_RGBX;
|
||||
{
|
||||
dec.out_color_space = JCS_EXT_RGBA;
|
||||
}
|
||||
#endif
|
||||
if (jpeg_start_decompress(&dec) == false)
|
||||
return E_FAIL;
|
||||
|
||||
uint8_t* dest = image.GetPixels();
|
||||
const size_t stride = dec.output_width * static_cast<size_t>(dec.output_components);
|
||||
const auto img = *image.GetImage(0, 0, 0);
|
||||
|
||||
std::vector<JSAMPROW> rows(dec.output_height);
|
||||
for (size_t i = 0u; i < dec.output_height; ++i)
|
||||
rows[i] = dest + (stride * i);
|
||||
{
|
||||
rows[i] = img.pixels + (i * img.rowPitch);
|
||||
}
|
||||
|
||||
JDIMENSION leftover = dec.output_height;
|
||||
while (leftover > 0)
|
||||
@ -231,14 +222,18 @@ namespace
|
||||
JDIMENSION consumed = jpeg_read_scanlines(&dec, &rows[dec.output_scanline], leftover);
|
||||
leftover -= consumed;
|
||||
}
|
||||
|
||||
if (jpeg_finish_decompress(&dec) == false)
|
||||
return E_FAIL;
|
||||
|
||||
#if !defined(LIBJPEG_TURBO_VERSION)
|
||||
#ifndef LIBJPEG_TURBO_VERSION
|
||||
// if NOT TurboJPEG, we need to make 3 component images to 4 component image
|
||||
if (dec.out_color_space != JCS_GRAYSCALE)
|
||||
ShiftPixels(image);
|
||||
{
|
||||
ShiftPixels(img);
|
||||
}
|
||||
#endif
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@ -251,11 +246,12 @@ namespace
|
||||
|
||||
class JPEGCompress final
|
||||
{
|
||||
jpeg_error_mgr err{};
|
||||
jpeg_compress_struct enc{};
|
||||
jpeg_error_mgr err;
|
||||
jpeg_compress_struct enc;
|
||||
JPEG_FLAGS userFlags;
|
||||
|
||||
public:
|
||||
JPEGCompress() : err{}, enc{}
|
||||
JPEGCompress(JPEG_FLAGS flags) : err{}, enc{}, userFlags(flags)
|
||||
{
|
||||
jpeg_std_error(&err);
|
||||
err.error_exit = &OnJPEGError;
|
||||
@ -272,7 +268,6 @@ namespace
|
||||
jpeg_stdio_dest(&enc, fout);
|
||||
}
|
||||
|
||||
/// @todo More correct DXGI_FORMAT mapping
|
||||
HRESULT WriteImage(const Image& image) noexcept(false)
|
||||
{
|
||||
switch (image.format)
|
||||
@ -283,34 +278,74 @@ namespace
|
||||
break;
|
||||
#ifdef LIBJPEG_TURBO_VERSION
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
|
||||
enc.input_components = 4;
|
||||
enc.in_color_space = JCS_EXT_RGBA;
|
||||
break;
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM:
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
|
||||
enc.input_components = 4;
|
||||
enc.in_color_space = JCS_EXT_BGRA;
|
||||
break;
|
||||
case DXGI_FORMAT_B8G8R8X8_UNORM:
|
||||
case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
|
||||
enc.input_components = 4;
|
||||
enc.in_color_space = JCS_EXT_BGRX;
|
||||
break;
|
||||
#else
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
|
||||
enc.input_components = 3;
|
||||
enc.in_color_space = JCS_RGB;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return HRESULT_E_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// TODO: Add sRGB intent?
|
||||
|
||||
enc.image_width = static_cast<JDIMENSION>(image.width);
|
||||
enc.image_height = static_cast<JDIMENSION>(image.height);
|
||||
jpeg_set_defaults(&enc);
|
||||
jpeg_set_quality(&enc, 100, true);
|
||||
|
||||
// we will write a row each time ...
|
||||
jpeg_start_compress(&enc, true);
|
||||
const size_t stride = enc.image_width * static_cast<size_t>(enc.input_components);
|
||||
while (enc.next_scanline < enc.image_height)
|
||||
|
||||
#ifndef LIBJPEG_TURBO_VERSION
|
||||
if (enc.input_components == 3)
|
||||
{
|
||||
JSAMPROW rows[1]{ image.pixels + stride * enc.next_scanline };
|
||||
jpeg_write_scanlines(&enc, rows, 1);
|
||||
const size_t stride = enc.image_width * static_cast<size_t>(enc.input_components);
|
||||
auto scanline = std::make_unique<uint8_t[]>(stride);
|
||||
JSAMPROW rows[1]{ scanline.get() };
|
||||
|
||||
while (enc.next_scanline < enc.image_height)
|
||||
{
|
||||
// Copy 4 to 3 components
|
||||
const uint8_t* src = image.pixels + enc.next_scanline * image.rowPitch;
|
||||
uint8_t* dst = scanline.get();
|
||||
for(size_t i=0; i < image.width; ++i)
|
||||
{
|
||||
dst[3*i + 0] = src[4*i + 0];
|
||||
dst[3*i + 1] = src[4*i + 1];
|
||||
dst[3*i + 2] = src[4*i + 2];
|
||||
}
|
||||
|
||||
jpeg_write_scanlines(&enc, rows, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
while (enc.next_scanline < enc.image_height)
|
||||
{
|
||||
JSAMPROW rows[1]{ image.pixels + image.rowPitch * enc.next_scanline };
|
||||
jpeg_write_scanlines(&enc, rows, 1);
|
||||
}
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&enc);
|
||||
return S_OK;
|
||||
}
|
||||
@ -320,6 +355,7 @@ namespace
|
||||
_Use_decl_annotations_
|
||||
HRESULT DirectX::GetMetadataFromJPEGFile(
|
||||
const wchar_t* file,
|
||||
JPEG_FLAGS flags,
|
||||
TexMetadata& metadata)
|
||||
{
|
||||
if (!file)
|
||||
@ -328,7 +364,7 @@ HRESULT DirectX::GetMetadataFromJPEGFile(
|
||||
try
|
||||
{
|
||||
auto fin = OpenFILE(file);
|
||||
JPEGDecompress decoder{};
|
||||
JPEGDecompress decoder(flags);
|
||||
decoder.UseInput(fin.get());
|
||||
return decoder.GetHeader(metadata);
|
||||
}
|
||||
@ -353,6 +389,7 @@ HRESULT DirectX::GetMetadataFromJPEGFile(
|
||||
_Use_decl_annotations_
|
||||
HRESULT DirectX::LoadFromJPEGFile(
|
||||
const wchar_t* file,
|
||||
JPEG_FLAGS flags,
|
||||
TexMetadata* metadata,
|
||||
ScratchImage&image)
|
||||
{
|
||||
@ -364,7 +401,7 @@ HRESULT DirectX::LoadFromJPEGFile(
|
||||
try
|
||||
{
|
||||
auto fin = OpenFILE(file);
|
||||
JPEGDecompress decoder{};
|
||||
JPEGDecompress decoder(flags);
|
||||
decoder.UseInput(fin.get());
|
||||
if (!metadata)
|
||||
return decoder.GetImage(image);
|
||||
@ -394,6 +431,7 @@ HRESULT DirectX::LoadFromJPEGFile(
|
||||
_Use_decl_annotations_
|
||||
HRESULT DirectX::SaveToJPEGFile(
|
||||
const Image& image,
|
||||
JPEG_FLAGS flags,
|
||||
const wchar_t* file)
|
||||
{
|
||||
if (!file)
|
||||
@ -402,7 +440,7 @@ HRESULT DirectX::SaveToJPEGFile(
|
||||
try
|
||||
{
|
||||
auto fout = CreateFILE(file);
|
||||
JPEGCompress encoder{};
|
||||
JPEGCompress encoder(flags);
|
||||
encoder.UseOutput(fout.get());
|
||||
return encoder.WriteImage(image);
|
||||
}
|
||||
|
||||
@ -18,16 +18,39 @@
|
||||
|
||||
namespace DirectX
|
||||
{
|
||||
enum JPEG_FLAGS : uint32_t
|
||||
{
|
||||
JPEG_FLAGS_NONE = 0x0,
|
||||
|
||||
JPEG_FLAGS_DEFAULT_LINEAR = 0x1,
|
||||
// Return non-SRGB formats intead of sRGB
|
||||
};
|
||||
|
||||
DIRECTX_TEX_API HRESULT __cdecl GetMetadataFromJPEGFile(
|
||||
_In_z_ const wchar_t* szFile,
|
||||
JPEG_FLAGS flags,
|
||||
_Out_ TexMetadata& metadata);
|
||||
|
||||
DIRECTX_TEX_API HRESULT __cdecl LoadFromJPEGFile(
|
||||
_In_z_ const wchar_t* szFile,
|
||||
JPEG_FLAGS flags,
|
||||
_Out_opt_ TexMetadata* metadata,
|
||||
_Out_ ScratchImage& image);
|
||||
|
||||
DIRECTX_TEX_API HRESULT __cdecl SaveToJPEGFile(
|
||||
_In_ const Image& image,
|
||||
JPEG_FLAGS flags,
|
||||
_In_z_ const wchar_t* szFile);
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec"
|
||||
#pragma clang diagnostic ignored "-Wextra-semi-stmt"
|
||||
#endif
|
||||
|
||||
DEFINE_ENUM_FLAG_OPERATORS(JPEG_FLAGS);
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -85,6 +85,8 @@ For a full change history, see [CHANGELOG.md](https://github.com/microsoft/Direc
|
||||
|
||||
* The CMake projects require 3.21 or later. VS 2019 users will need to install a standalone version of CMake 3.21 or later and add it to their PATH.
|
||||
|
||||
* Starting with the October 2025 release, the _Auxiliary_ functions for loading and saving JPEG and PNG files now take a flag parameter after the filename.
|
||||
|
||||
* Starting with the March 2025 release, Windows 7 and Windows 8.0 support has been retired.
|
||||
|
||||
* Starting with the July 2022 release, the ``bool forceSRGB`` parameter for the **CreateTextureEx** and **CreateShaderResourceViewEx** functions is now a ``CREATETEX_FLAGS`` typed enum bitmask flag parameter. This may have a _breaking change_ impact to client code. Replace ``true`` with ``CREATETEX_FORCE_SRGB`` and ``false`` with ``CREATETEX_DEFAULT``.
|
||||
|
||||
@ -618,7 +618,7 @@ namespace
|
||||
#endif
|
||||
#ifdef USE_LIBJPEG
|
||||
case CODEC_JPEG:
|
||||
return SaveToJPEGFile(img, szOutputFile);
|
||||
return SaveToJPEGFile(img, JPEG_FLAGS_NONE, szOutputFile);
|
||||
#endif
|
||||
#ifdef USE_LIBPNG
|
||||
case CODEC_PNG:
|
||||
@ -1423,7 +1423,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
|
||||
#ifdef USE_LIBJPEG
|
||||
else if (_wcsicmp(ext.c_str(), L".jpg") == 0 || _wcsicmp(ext.c_str(), L".jpeg") == 0)
|
||||
{
|
||||
hr = LoadFromJPEGFile(curpath.c_str(), &info, *image);
|
||||
hr = LoadFromJPEGFile(curpath.c_str(), JPEG_FLAGS_NONE, &info, *image);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
|
||||
|
||||
@ -2157,7 +2157,13 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
|
||||
#ifdef USE_LIBJPEG
|
||||
else if (_wcsicmp(ext.c_str(), L".jpg") == 0 || _wcsicmp(ext.c_str(), L".jpeg") == 0)
|
||||
{
|
||||
hr = LoadFromJPEGFile(curpath.c_str(), &info, *image);
|
||||
JPEG_FLAGS jpegFlags = JPEG_FLAGS_NONE;
|
||||
if (dwOptions & (UINT64_C(1) << OPT_IGNORE_SRGB_METADATA))
|
||||
{
|
||||
jpegFlags |= JPEG_FLAGS_DEFAULT_LINEAR;
|
||||
}
|
||||
|
||||
hr = LoadFromJPEGFile(curpath.c_str(), jpegFlags, &info, *image);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
wprintf(L" FAILED (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
|
||||
@ -3800,7 +3806,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
|
||||
#endif
|
||||
#ifdef USE_LIBJPEG
|
||||
case CODEC_JPEG:
|
||||
hr = SaveToJPEGFile(img[0], destName.c_str());
|
||||
hr = SaveToJPEGFile(img[0], JPEG_FLAGS_NONE, destName.c_str());
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_LIBPNG
|
||||
|
||||
@ -585,7 +585,7 @@ namespace
|
||||
#ifdef USE_LIBJPEG
|
||||
else if (_wcsicmp(ext.c_str(), L".jpg") == 0 || _wcsicmp(ext.c_str(), L".jpeg") == 0)
|
||||
{
|
||||
return LoadFromJPEGFile(fileName, &info, *image);
|
||||
return LoadFromJPEGFile(fileName, JPEG_FLAGS_NONE, &info, *image);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_LIBPNG
|
||||
@ -639,7 +639,7 @@ namespace
|
||||
#endif
|
||||
#ifdef USE_LIBJPEG
|
||||
case CODEC_JPEG:
|
||||
return SaveToJPEGFile(*image, fileName);
|
||||
return SaveToJPEGFile(*image, JPEG_FLAGS_NONE, fileName);
|
||||
#endif
|
||||
#ifdef USE_LIBPNG
|
||||
case CODEC_PNG:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user