diff --git a/Auxiliary/DirectXTexJPEG.cpp b/Auxiliary/DirectXTexJPEG.cpp new file mode 100644 index 0000000..3828ac0 --- /dev/null +++ b/Auxiliary/DirectXTexJPEG.cpp @@ -0,0 +1,421 @@ +//-------------------------------------------------------------------------------------- +// File: DirectXTexJPEG.cpp +// +// DirectXTex Auxilary functions for using the JPEG(https://www.ijg.org) library +// +// For the Windows platform, the strong recommendation is to make use of the WIC +// functions rather than using the open source library. This module exists to support +// Windows Subsystem on Linux. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//-------------------------------------------------------------------------------------- + +#include "DirectXTexP.h" +#include "DirectXTexJPEG.h" + +#if __cplusplus < 201703L +#error Requires C++17 (and /Zc:__cplusplus with MSVC) +#endif + +#ifndef _BASETSD_H_ +#define _BASETSD_H_ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +using namespace DirectX; +using std::filesystem::path; +using ScopedFILE = std::unique_ptr; + +namespace +{ +#ifdef _WIN32 + ScopedFILE OpenFILE(const path& p) noexcept(false) + { + const std::wstring fpath = p.generic_wstring(); + FILE* fp = nullptr; + if (auto ec = _wfopen_s(&fp, fpath.c_str(), L"rb"); ec) + throw std::system_error{ static_cast(_doserrno), std::system_category(), "_wfopen_s" }; + return { fp, &fclose }; + } + ScopedFILE CreateFILE(const path& p) noexcept(false) + { + const std::wstring fpath = p.generic_wstring(); + FILE* fp = nullptr; + if (auto ec = _wfopen_s(&fp, fpath.c_str(), L"w+b"); ec) + throw std::system_error{ static_cast(_doserrno), std::system_category(), "_wfopen_s" }; + return { fp, &fclose }; + } +#else + ScopedFILE OpenFILE(const path& p) noexcept(false) + { + const std::string fpath = p.generic_string(); + FILE* fp = fopen(fpath.c_str(), "rb"); + if (!fp) + throw std::system_error{ errno, std::system_category(), "fopen" }; + return { fp, &fclose }; + } + ScopedFILE CreateFILE(const path& p) noexcept(false) + { + const std::string fpath = p.generic_string(); + FILE* fp = fopen(fpath.c_str(), "w+b"); + if (!fp) + throw std::system_error{ errno, std::system_category(), "fopen" }; + return { fp, &fclose }; + } +#endif + + [[noreturn]] void OnJPEGError(j_common_ptr ptr) + { + char msg[JMSG_LENGTH_MAX]{}; + // "0x89 0x50": PNG + // "0x52 0x49": WEBP + (*ptr->err->format_message)(ptr, msg); + jpeg_destroy(ptr); + throw std::runtime_error{ msg }; + } + + class JPEGDecompress final + { + jpeg_error_mgr err; + jpeg_decompress_struct dec; + + public: + JPEGDecompress() : err{}, dec{} + { + jpeg_std_error(&err); + err.error_exit = &OnJPEGError; + dec.err = &err; + jpeg_create_decompress(&dec); + } + ~JPEGDecompress() noexcept + { + jpeg_destroy_decompress(&dec); + } + + void UseInput(FILE* fin) noexcept + { + jpeg_stdio_src(&dec, fin); + } + + static 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 + case JCS_YCbCr: // 3 component, YCbCr + default: + return DXGI_FORMAT_UNKNOWN; + } + } + + void GetMetadata(TexMetadata& metadata) noexcept(false) + { + metadata.width = dec.image_width; + metadata.height = dec.image_height; + metadata.depth = 1; + metadata.arraySize = 1; + metadata.mipLevels = 1; + metadata.dimension = TEX_DIMENSION_TEXTURE2D; + metadata.miscFlags2 |= TEX_ALPHA_MODE_OPAQUE; + + metadata.format = TranslateColor(dec.out_color_space); + if (metadata.format == DXGI_FORMAT_UNKNOWN) + throw std::runtime_error{ "unexpected out_color_space in jpeg_decompress_struct" }; + } + + HRESULT GetHeader(TexMetadata& metadata) noexcept(false) + { + metadata = {}; + switch (jpeg_read_header(&dec, false)) + { + case JPEG_HEADER_TABLES_ONLY: + [[fallthrough]]; + case JPEG_HEADER_OK: + break; + case JPEG_SUSPENDED: + return E_FAIL; + } + GetMetadata(metadata); + return S_OK; + } + + #if !defined(LIBJPEG_TURBO_VERSION) + // shift pixels with padding in reverse order (to make it work in-memory) + void ShiftPixels(ScratchImage& 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) + { + 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; + } + } + #endif + + HRESULT GetImage(TexMetadata& metadata, ScratchImage& image) noexcept(false) + { + metadata = {}; + switch (jpeg_read_header(&dec, true)) + { + case JPEG_HEADER_TABLES_ONLY: + GetMetadata(metadata); + [[fallthrough]]; + case JPEG_SUSPENDED: + return E_FAIL; + case JPEG_HEADER_OK: + 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)) + 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; + #endif + if (jpeg_start_decompress(&dec) == false) + return E_FAIL; + + uint8_t* dest = image.GetPixels(); + const size_t stride = dec.output_width * static_cast(dec.output_components); + std::vector rows(dec.output_height); + for (size_t i = 0u; i < dec.output_height; ++i) + rows[i] = dest + (stride * i); + + JDIMENSION leftover = dec.output_height; + while (leftover > 0) + { + 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) + // if NOT TurboJPEG, we need to make 3 component images to 4 component image + if (dec.out_color_space != JCS_GRAYSCALE) + ShiftPixels(image); + #endif + return S_OK; + } + + HRESULT GetImage(ScratchImage& image) noexcept(false) + { + TexMetadata metadata{}; + return GetImage(metadata, image); + } + }; + + class JPEGCompress final + { + jpeg_error_mgr err{}; + jpeg_compress_struct enc{}; + + public: + JPEGCompress() : err{}, enc{} + { + jpeg_std_error(&err); + err.error_exit = &OnJPEGError; + enc.err = &err; + jpeg_create_compress(&enc); + } + ~JPEGCompress() noexcept + { + jpeg_destroy_compress(&enc); + } + + void UseOutput(FILE* fout) + { + jpeg_stdio_dest(&enc, fout); + } + + /// @todo More correct DXGI_FORMAT mapping + HRESULT WriteImage(const Image& image) noexcept(false) + { + switch (image.format) + { + case DXGI_FORMAT_R8_UNORM: + enc.input_components = 1; + enc.in_color_space = JCS_GRAYSCALE; + break; + #ifdef LIBJPEG_TURBO_VERSION + case DXGI_FORMAT_R8G8B8A8_UNORM: + enc.input_components = 4; + enc.in_color_space = JCS_EXT_RGBA; + break; + case DXGI_FORMAT_B8G8R8A8_UNORM: + enc.input_components = 4; + enc.in_color_space = JCS_EXT_BGRA; + break; + #else + case DXGI_FORMAT_R8G8B8A8_UNORM: + enc.input_components = 3; + enc.in_color_space = JCS_RGB; + #endif + + default: + return E_INVALIDARG; + } + enc.image_width = static_cast(image.width); + enc.image_height = static_cast(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(enc.input_components); + while (enc.next_scanline < enc.image_height) + { + JSAMPROW rows[1]{ image.pixels + stride * enc.next_scanline }; + jpeg_write_scanlines(&enc, rows, 1); + } + jpeg_finish_compress(&enc); + return S_OK; + } + }; +} + +_Use_decl_annotations_ +HRESULT DirectX::GetMetadataFromJPEGFile( + const wchar_t* file, + TexMetadata& metadata) +{ + if (!file) + return E_INVALIDARG; + + try + { + auto fin = OpenFILE(file); + JPEGDecompress decoder{}; + decoder.UseInput(fin.get()); + return decoder.GetHeader(metadata); + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + catch (const std::system_error& ec) + { +#ifdef _WIN32 + return HRESULT_FROM_WIN32(static_cast(ec.code().value())); +#else + return (ec.code().value() == ENOENT) ? HRESULT_ERROR_FILE_NOT_FOUND : E_FAIL; +#endif + } + catch (const std::exception&) + { + return E_FAIL; + } +} + +_Use_decl_annotations_ +HRESULT DirectX::LoadFromJPEGFile( + const wchar_t* file, + TexMetadata* metadata, + ScratchImage&image) +{ + if (!file) + return E_INVALIDARG; + + image.Release(); + + try + { + auto fin = OpenFILE(file); + JPEGDecompress decoder{}; + decoder.UseInput(fin.get()); + if (!metadata) + return decoder.GetImage(image); + return decoder.GetImage(*metadata, image); + } + catch (const std::bad_alloc&) + { + image.Release(); + return E_OUTOFMEMORY; + } + catch (const std::system_error& ec) + { + image.Release(); +#ifdef _WIN32 + return HRESULT_FROM_WIN32(static_cast(ec.code().value())); +#else + return (ec.code().value() == ENOENT) ? HRESULT_ERROR_FILE_NOT_FOUND : E_FAIL; +#endif + } + catch (const std::exception&) + { + image.Release(); + return E_FAIL; + } +} + +_Use_decl_annotations_ +HRESULT DirectX::SaveToJPEGFile( + const Image& image, + const wchar_t* file) +{ + if (!file) + return E_INVALIDARG; + + try + { + auto fout = CreateFILE(file); + JPEGCompress encoder{}; + encoder.UseOutput(fout.get()); + return encoder.WriteImage(image); + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + catch (const std::system_error& ec) + { +#ifdef _WIN32 + return HRESULT_FROM_WIN32(static_cast(ec.code().value())); +#else + return (ec.code().value() == ENOENT) ? HRESULT_ERROR_FILE_NOT_FOUND : E_FAIL; +#endif + } + catch (const std::exception&) + { + return E_FAIL; + } +} diff --git a/Auxiliary/DirectXTexJPEG.h b/Auxiliary/DirectXTexJPEG.h new file mode 100644 index 0000000..396a957 --- /dev/null +++ b/Auxiliary/DirectXTexJPEG.h @@ -0,0 +1,33 @@ +//-------------------------------------------------------------------------------------- +// File: DirectXTexJPEG.h +// +// DirectXTex Auxilary functions for using the JPEG(https://www.ijg.org) library +// +// For the Windows platform, the strong recommendation is to make use of the WIC +// functions rather than using the open source library. This module exists to support +// Windows Subsystem on Linux. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//-------------------------------------------------------------------------------------- + +#pragma once + +#include "DirectXTex.h" + + +namespace DirectX +{ + HRESULT __cdecl GetMetadataFromJPEGFile( + _In_z_ const wchar_t* szFile, + _Out_ TexMetadata& metadata); + + HRESULT __cdecl LoadFromJPEGFile( + _In_z_ const wchar_t* szFile, + _Out_opt_ TexMetadata* metadata, + _Out_ ScratchImage& image); + + HRESULT __cdecl SaveToJPEGFile( + _In_ const Image& image, + _In_z_ const wchar_t* szFile); +} diff --git a/Auxiliary/DirectXTexPNG.cpp b/Auxiliary/DirectXTexPNG.cpp new file mode 100644 index 0000000..06181b7 --- /dev/null +++ b/Auxiliary/DirectXTexPNG.cpp @@ -0,0 +1,414 @@ +//-------------------------------------------------------------------------------------- +// File: DirectXTexPNG.cpp +// +// DirectXTex Auxilary functions for using the PNG(http://www.libpng.org/pub/png/libpng.html) library +// +// For the Windows platform, the strong recommendation is to make use of the WIC +// functions rather than using the open source library. This module exists to support +// Windows Subsystem on Linux. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//-------------------------------------------------------------------------------------- + +#include "DirectXTexP.h" +#include "DirectXTexPNG.h" + +#if __cplusplus < 201703L +#error Requires C++17 (and /Zc:__cplusplus with MSVC) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +using namespace DirectX; +using std::filesystem::path; +using ScopedFILE = std::unique_ptr; + +namespace +{ +#ifdef _WIN32 + ScopedFILE OpenFILE(const path& p) noexcept(false) + { + const std::wstring fpath = p.generic_wstring(); + FILE* fp = nullptr; + if (auto ec = _wfopen_s(&fp, fpath.c_str(), L"rb"); ec) + throw std::system_error{ static_cast(_doserrno), std::system_category(), "_wfopen_s" }; + return { fp, &fclose }; + } + ScopedFILE CreateFILE(const path& p) noexcept(false) + { + const std::wstring fpath = p.generic_wstring(); + FILE* fp = nullptr; + if (auto ec = _wfopen_s(&fp, fpath.c_str(), L"w+b"); ec) + throw std::system_error{ static_cast(_doserrno), std::system_category(), "_wfopen_s" }; + return { fp, &fclose }; + } +#else + ScopedFILE OpenFILE(const path& p) noexcept(false) + { + const std::string fpath = p.generic_string(); + FILE* fp = fopen(fpath.c_str(), "rb"); + if (!fp) + throw std::system_error{ errno, std::system_category(), "fopen" }; + return { fp, &fclose }; + } + ScopedFILE CreateFILE(const path& p) noexcept(false) + { + const std::string fpath = p.generic_string(); + FILE* fp = fopen(fpath.c_str(), "w+b"); + if (!fp) + throw std::system_error{ errno, std::system_category(), "fopen" }; + return { fp, &fclose }; + } +#endif + + [[noreturn]] void OnPNGError(png_structp, png_const_charp msg) + { + throw std::runtime_error{ msg }; + } + + void OnPNGWarning(png_structp, png_const_charp) + { + // drain warning messages ... + } + + /// @note If the PNG contains some extra chunks like EXIF, this will be used + void OnPNGRead(png_structp st, png_bytep ptr, size_t len) + { + FILE* fin = reinterpret_cast(png_get_io_ptr(st)); + fread(ptr, len, 1, fin); + } + + + /// @see http://www.libpng.org/pub/png/libpng.html + /// @see http://www.libpng.org/pub/png/libpng-manual.txt + class PNGDecompress final + { + png_structp st; + png_infop info; + + public: + PNGDecompress() noexcept(false) : st{ nullptr }, info{ nullptr } + { + st = png_create_read_struct(PNG_LIBPNG_VER_STRING, this, &OnPNGError, &OnPNGWarning); + if (!st) + throw std::runtime_error{ "png_create_read_struct" }; + info = png_create_info_struct(st); + if (!info) + { + png_destroy_read_struct(&st, nullptr, nullptr); + throw std::runtime_error{ "png_create_info_struct" }; + } + } + + ~PNGDecompress() noexcept + { + png_destroy_read_struct(&st, &info, nullptr); + } + + void UseInput(FILE* fin) noexcept + { + // we assume the signature is unread. if so, we have to use `png_set_sig_bytes` + png_init_io(st, fin); + png_set_read_fn(st, fin, &OnPNGRead); + } + + void Update() noexcept(false) + { + png_read_info(st, info); + // color handling + png_byte color_type = png_get_color_type(st, info); + if (color_type == PNG_COLOR_TYPE_GRAY) + { + // bit_depth will be 8 or 16 + if (png_get_bit_depth(st, info) < 8) + png_set_expand_gray_1_2_4_to_8(st); + } + else if (color_type == PNG_COLOR_TYPE_PALETTE) + { + // request RGB color + png_set_palette_to_rgb(st); + if (png_get_valid(st, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(st); + } + // Here we don't know if the pixel data is in BGR/RGB order + // png_set_bgr(st); + png_set_alpha_mode(st, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + // make 4 component + // using `png_set_add_alpha` here may confuse `TEX_ALPHA_MODE_OPAQUE` estimation + if (png_get_channels(st, info) == 3) + png_set_filler(st, 0, PNG_FILLER_AFTER); + // prefer DXGI_FORMAT_R8G8B8A8_UNORM. strip in decode + //if (png_get_bit_depth(st, info) > 8) + // png_set_strip_16(st); + png_read_update_info(st, info); + } + + /// @note must call `Update` before this + /// @todo Proper detection of DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB + DXGI_FORMAT GuessFormat() const noexcept(false) + { + // 1 or 4. 1 is for gray + auto c = png_get_channels(st, info); + if (c == 1) + { + if (png_get_bit_depth(st, info) == 16) + return DXGI_FORMAT_R16_UNORM; + // with `png_set_expand_gray_1_2_4_to_8`, libpng will change to R8_UNORM + return DXGI_FORMAT_R8_UNORM; + } + + // 8 or 16. expanded if 1, 2, 4 + auto d = png_get_bit_depth(st, info); + if (d == 16) + return DXGI_FORMAT_R16G16B16A16_UNORM; + if (d != 8) + throw std::runtime_error{ "unexpected info from libpng" }; + + int intent = 0; + if (png_get_sRGB(st, info, &intent) == PNG_INFO_sRGB) + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + + return DXGI_FORMAT_R8G8B8A8_UNORM; + } + + /// @todo More correct DXGI_FORMAT mapping + void GetHeader(TexMetadata& metadata) noexcept(false) + { + metadata.width = png_get_image_width(st, info); + metadata.height = png_get_image_height(st, info); + metadata.arraySize = 1; + metadata.mipLevels = 1; + metadata.depth = 1; + metadata.dimension = TEX_DIMENSION_TEXTURE2D; + metadata.format = GuessFormat(); + png_byte color_type = png_get_color_type(st, info); + bool have_alpha = (color_type & PNG_COLOR_MASK_ALPHA); + if (have_alpha == false) + metadata.miscFlags2 |= TEX_ALPHA_MODE_OPAQUE; + } + + /// @todo More correct DXGI_FORMAT mapping + HRESULT GetImage(TexMetadata& metadata, ScratchImage& image) noexcept(false) + { + metadata = {}; + GetHeader(metadata); + + if (auto hr = image.Initialize2D(metadata.format, metadata.width, metadata.height, metadata.arraySize, metadata.mipLevels); FAILED(hr)) + return hr; + auto* dest = image.GetPixels(); + const auto stride = metadata.width * png_get_channels(st, info); + std::vector rows(metadata.height); + for (auto i = 0u; i < metadata.height; ++i) + rows[i] = dest + (stride * i); + + png_read_rows(st, rows.data(), nullptr, static_cast(rows.size())); + png_read_end(st, info); + return S_OK; + } + + HRESULT GetImage(ScratchImage& image) noexcept(false) + { + TexMetadata metadata{}; + return GetImage(metadata, image); + } + }; + + /// @see http://www.libpng.org/pub/png/libpng.html + /// @see http://www.libpng.org/pub/png/libpng-manual.txt + class PNGCompress final + { + png_structp st; + png_infop info; + + public: + PNGCompress() noexcept(false) : st{ nullptr }, info{ nullptr } + { + st = png_create_write_struct(PNG_LIBPNG_VER_STRING, this, &OnPNGError, &OnPNGWarning); + if (!st) + throw std::runtime_error{ "png_create_write_struct" }; + info = png_create_info_struct(st); + if (!info) + { + png_destroy_write_struct(&st, nullptr); + throw std::runtime_error{ "png_create_info_struct" }; + } + png_set_compression_level(st, 0); + } + + ~PNGCompress() noexcept + { + png_destroy_write_struct(&st, &info); + } + + void UseOutput(FILE* fout) noexcept + { + png_init_io(st, fout); + } + + HRESULT WriteImage(const Image& image) noexcept(false) + { + int color_type = PNG_COLOR_TYPE_RGB; + bool using_bgr = false; + int channel = 4; + int bit_depth = 8; + switch (image.format) + { + case DXGI_FORMAT_R8_UNORM: + color_type = PNG_COLOR_TYPE_GRAY; + break; + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + using_bgr = true; + [[fallthrough]]; + case DXGI_FORMAT_R8G8B8A8_UNORM: + color_type = PNG_COLOR_TYPE_RGBA; + break; + default: + return E_INVALIDARG; + } + + png_set_IHDR(st, info, + static_cast(image.width), + static_cast(image.height), + bit_depth, + color_type, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_write_info(st, info); + if (using_bgr) + png_set_bgr(st); + + const size_t stride = static_cast(channel) * image.width; + std::vector rows(image.height); + for (size_t i = 0u; i< image.height; ++i) + rows[i] = image.pixels + stride * i; + png_write_rows(st, rows.data(), static_cast(rows.size())); + + // actual write will be done here + png_write_end(st, info); + return S_OK; + } + }; +} + +_Use_decl_annotations_ +HRESULT DirectX::GetMetadataFromPNGFile( + const wchar_t* file, + TexMetadata& metadata) +{ + if (!file) + return E_INVALIDARG; + + try + { + auto fin = OpenFILE(file); + PNGDecompress decoder{}; + decoder.UseInput(fin.get()); + decoder.Update(); + decoder.GetHeader(metadata); + return S_OK; + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + catch (const std::system_error& ec) + { +#ifdef _WIN32 + return HRESULT_FROM_WIN32(static_cast(ec.code().value())); +#else + return (ec.code().value() == ENOENT) ? HRESULT_ERROR_FILE_NOT_FOUND : E_FAIL; +#endif + } + catch (const std::exception&) + { + return E_FAIL; + } +} + +_Use_decl_annotations_ +HRESULT DirectX::LoadFromPNGFile( + const wchar_t* file, + TexMetadata* metadata, + ScratchImage& image) +{ + if (!file) + return E_INVALIDARG; + + image.Release(); + + try + { + auto fin = OpenFILE(file); + PNGDecompress decoder{}; + decoder.UseInput(fin.get()); + decoder.Update(); + if (metadata == nullptr) + return decoder.GetImage(image); + return decoder.GetImage(*metadata, image); + } + catch (const std::bad_alloc&) + { + image.Release(); + return E_OUTOFMEMORY; + } + catch (const std::system_error& ec) + { + image.Release(); +#ifdef _WIN32 + return HRESULT_FROM_WIN32(static_cast(ec.code().value())); +#else + return (ec.code().value() == ENOENT) ? HRESULT_ERROR_FILE_NOT_FOUND : E_FAIL; +#endif + } + catch (const std::exception&) + { + image.Release(); + return E_FAIL; + } +} + +_Use_decl_annotations_ +HRESULT DirectX::SaveToPNGFile( + const Image& image, + const wchar_t* file) +{ + if (!file) + return E_INVALIDARG; + + try + { + auto fout = CreateFILE(file); + PNGCompress encoder{}; + encoder.UseOutput(fout.get()); + return encoder.WriteImage(image); + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + catch (const std::system_error& ec) + { +#ifdef _WIN32 + return HRESULT_FROM_WIN32(static_cast(ec.code().value())); +#else + return (ec.code().value() == ENOENT) ? HRESULT_ERROR_FILE_NOT_FOUND : E_FAIL; +#endif + } + catch (const std::exception&) + { + return E_FAIL; + } +} diff --git a/Auxiliary/DirectXTexPNG.h b/Auxiliary/DirectXTexPNG.h new file mode 100644 index 0000000..d6e2ee2 --- /dev/null +++ b/Auxiliary/DirectXTexPNG.h @@ -0,0 +1,33 @@ +//-------------------------------------------------------------------------------------- +// File: DirectXTexPNG.h +// +// DirectXTex Auxilary functions for using the PNG(http://www.libpng.org/pub/png/libpng.html) library +// +// For the Windows platform, the strong recommendation is to make use of the WIC +// functions rather than using the open source library. This module exists to support +// Windows Subsystem on Linux. +// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//-------------------------------------------------------------------------------------- + +#pragma once + +#include "DirectXTex.h" + + +namespace DirectX +{ + HRESULT __cdecl GetMetadataFromPNGFile( + _In_z_ const wchar_t* szFile, + _Out_ TexMetadata& metadata); + + HRESULT __cdecl LoadFromPNGFile( + _In_z_ const wchar_t* szFile, + _Out_opt_ TexMetadata* metadata, + _Out_ ScratchImage& image); + + HRESULT __cdecl SaveToPNGFile( + _In_ const Image& image, + _In_z_ const wchar_t* szFile); +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d3ab63..f9d6bdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,12 @@ option(BUILD_FUZZING "Build for fuzz testing" OFF) # Includes the functions for loading/saving OpenEXR files at runtime option(ENABLE_OPENEXR_SUPPORT "Build with OpenEXR support" OFF) +# See https://www.ijg.org/ +option(ENABLE_LIBJPEG_SUPPORT "Build with libjpeg support" OFF) + +# See http://www.libpng.org/pub/png/libpng.html +option(ENABLE_LIBPNG_SUPPORT "Build with libpng support" OFF) + set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -181,6 +187,22 @@ if(ENABLE_OPENEXR_SUPPORT) list(APPEND LIBRARY_SOURCES Auxiliary/DirectXTexEXR.cpp) endif() +if(ENABLE_LIBJPEG_SUPPORT) + if(WIN32) + message(STATUS "Use of the Windows Imaging Component (WIC) instead of libjpeg is recommended.") + endif() + list(APPEND LIBRARY_HEADERS Auxiliary/DirectXTexJPEG.h) + list(APPEND LIBRARY_SOURCES Auxiliary/DirectXTexJPEG.cpp) +endif() + +if(ENABLE_LIBPNG_SUPPORT) + if(WIN32) + message(STATUS "Use of the Windows Imaging Component (WIC) instead of libpng is recommended.") + endif() + list(APPEND LIBRARY_HEADERS Auxiliary/DirectXTexPNG.h) + list(APPEND LIBRARY_SOURCES Auxiliary/DirectXTexPNG.cpp) +endif() + if(BUILD_DX11 AND WIN32) if(NOT COMPILED_SHADERS) if(USE_PREBUILT_SHADERS) @@ -255,6 +277,16 @@ elseif(BUILD_XBOX_EXTS_XBOXONE AND WIN32) endif() endif() +if(ENABLE_LIBJPEG_SUPPORT) + find_package(JPEG REQUIRED) + target_link_libraries(${PROJECT_NAME} PUBLIC JPEG::JPEG) +endif() + +if(ENABLE_LIBPNG_SUPPORT) + find_package(PNG REQUIRED) + target_link_libraries(${PROJECT_NAME} PUBLIC PNG::PNG) +endif() + if(NOT MINGW) target_precompile_headers(${PROJECT_NAME} PRIVATE DirectXTex/DirectXTexP.h) endif() @@ -364,6 +396,13 @@ endif() if(directx-headers_FOUND) list(APPEND DIRECTXTEX_DEP_L "DirectX-Headers") endif() +if(ENABLE_LIBJPEG_SUPPORT AND JPEG_FOUND) + list(APPEND DIRECTXTEX_DEP_L "libjpeg") +endif() +if(ENABLE_LIBPNG_SUPPORT AND PNG_FOUND) + list(APPEND DIRECTXTEX_DEP_L "libpng") +endif() + list(LENGTH DIRECTXTEX_DEP_L DEP_L) if(DEP_L) STRING(REPLACE ";" ", " DIRECTXTEX_DEP " ${DIRECTXTEX_DEP_L}") @@ -425,6 +464,20 @@ if(BUILD_TOOLS AND WIN32) target_compile_definitions(${t} PRIVATE USE_OPENEXR) endforeach() endif() + if(ENABLE_LIBJPEG_SUPPORT) + foreach(t IN LISTS TOOL_EXES) + target_include_directories(${t} PRIVATE Auxiliary) + target_link_libraries(${t} PRIVATE JPEG::JPEG) + target_compile_definitions(${t} PRIVATE USE_LIBJPEG) + endforeach() + endif() + if(ENABLE_LIBPNG_SUPPORT) + foreach(t IN LISTS TOOL_EXES) + target_include_directories(${t} PRIVATE Auxiliary) + target_link_libraries(${t} PRIVATE PNG::PNG) + target_compile_definitions(${t} PRIVATE USE_LIBPNG) + endforeach() + endif() endif() #--- DDSView sample diff --git a/CMakePresets.json b/CMakePresets.json index b7d763f..2e7a0ee 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -172,6 +172,14 @@ }, "hidden": true }, + { + "name": "JPEG_PNG", + "cacheVariables": { + "ENABLE_LIBJPEG_SUPPORT": true, + "ENABLE_LIBPNG_SUPPORT": true + }, + "hidden": true + }, { "name": "MinGW32", "hidden": true, @@ -241,6 +249,9 @@ { "name": "x64-Debug-EXR" , "description": "MSVC for x64 (Debug) using VCPKG/OpenEXR", "inherits": [ "base", "x64", "Debug", "MSVC", "VCPKG", "EXR" ] }, { "name": "x64-Release-EXR" , "description": "MSVC for x64 (Release) using VCPKG/OpenEXR", "inherits": [ "base", "x64", "Release", "MSVC", "VCPKG", "EXR" ] }, + { "name": "x64-Debug-JPEG-PNG" , "description": "MSVC for x64 (Debug) using VCPKG/PNG/JPEG", "inherits": [ "base", "x64", "Debug", "MSVC", "VCPKG", "JPEG_PNG" ] }, + { "name": "x64-Release-JPEG-PNG", "description": "MSVC for x64 (Release) using VCPKG/PNG/JPEG", "inherits": [ "base", "x64", "Release", "MSVC", "VCPKG", "JPEG_PNG" ] }, + { "name": "x64-Debug-Clang" , "description": "Clang/LLVM for x64 (Debug) with DX12", "inherits": [ "base", "x64", "Debug", "Clang" ] }, { "name": "x64-Release-Clang" , "description": "Clang/LLVM for x64 (Release) with DX12", "inherits": [ "base", "x64", "Release", "Clang" ] }, { "name": "x86-Debug-Clang" , "description": "Clang/LLVM for x86 (Debug) with DX12", "inherits": [ "base", "x86", "Debug", "Clang" ], "environment": { "CXXFLAGS": "-m32" } }, @@ -260,6 +271,9 @@ { "name": "x86-Debug-Win7-Clang" , "description": "Clang/LLVM for x86 (Debug) for Windows 7", "inherits": [ "base", "x86", "Debug", "Clang", "Win7" ], "environment": { "CXXFLAGS": "-m32" } }, { "name": "x86-Release-Win7-Clang", "description": "Clang/LLVM for x86 (Release) for Windows 7", "inherits": [ "base", "x86", "Release", "Clang", "Win7" ], "environment": { "CXXFLAGS": "-m32" } }, + { "name": "x64-Debug-Clang-JPEG-PNG" , "description": "Clang/LLVM for x64 (Debug) with DX12", "inherits": [ "base", "x64", "Debug", "Clang", "VCPKG", "JPEG_PNG" ] }, + { "name": "x64-Release-Clang-JPEG-PNG" , "description": "Clang/LLVM for x64 (Release) with DX12", "inherits": [ "base", "x64", "Release", "Clang", "VCPKG", "JPEG_PNG" ] }, + { "name": "x64-Debug-GDKX" , "description": "MSVC for x64 (Debug) Xbox One Extensions", "inherits": [ "base", "x64", "Debug", "GDKX", "MSVC" ] }, { "name": "x64-Release-GDKX" , "description": "MSVC for x64 (Release) Xbox One Extensions", "inherits": [ "base", "x64", "Release", "GDKX", "MSVC" ] }, { "name": "x64-Debug-GDKX-S" , "description": "MSVC for x64 (Debug) Xbox Series X|S Extensions", "inherits": [ "base", "x64", "Debug", "GDKXS", "MSVC" ] }, @@ -277,10 +291,15 @@ { "name": "x86-Debug-MinGW" , "description": "MinG-W32 (Debug)", "inherits": [ "base", "x86", "Debug", "GNUC", "VCPKG", "MinGW32" ] }, { "name": "x86-Release-MinGW", "description": "MinG-W32 (Release)", "inherits": [ "base", "x86", "Release", "GNUC", "VCPKG", "MinGW32" ] }, - { "name": "x64-Debug-Linux", "description": "WSL Linux x64 (Debug)", "inherits": [ "base", "x64", "Debug", "VCPKG" ] }, - { "name": "x64-Release-Linux", "description": "WSL Linux x64 (Release)", "inherits": [ "base", "x64", "Release", "VCPKG" ] }, - { "name": "arm64-Debug-Linux", "description": "WSL Linux ARM64 (Debug)", "inherits": [ "base", "ARM64", "Debug", "VCPKG" ] }, - { "name": "arm64-Release-Linux", "description": "WSL Linux ARM64 (Release)", "inherits": [ "base", "ARM64", "Release", "VCPKG" ] }, + { "name": "x64-Debug-MinGW-JPEG-PNG" , "description": "MinG-W64 (Debug)", "inherits": [ "base", "x64", "Debug", "GNUC", "VCPKG", "MinGW64", "JPEG_PNG" ] }, + { "name": "x64-Release-MinGW-JPEG-PNG", "description": "MinG-W64 (Release)", "inherits": [ "base", "x64", "Release", "GNUC", "VCPKG", "MinGW64", "JPEG_PNG" ] }, + { "name": "x86-Debug-MinGW-JPEG-PNG" , "description": "MinG-W32 (Debug)", "inherits": [ "base", "x86", "Debug", "GNUC", "VCPKG", "MinGW32", "JPEG_PNG" ] }, + { "name": "x86-Release-MinGW-JPEG-PNG", "description": "MinG-W32 (Release)", "inherits": [ "base", "x86", "Release", "GNUC", "VCPKG", "MinGW32", "JPEG_PNG" ] }, + + { "name": "x64-Debug-Linux", "description": "WSL Linux x64 (Debug)", "inherits": [ "base", "x64", "Debug", "VCPKG", "JPEG_PNG" ] }, + { "name": "x64-Release-Linux", "description": "WSL Linux x64 (Release)", "inherits": [ "base", "x64", "Release", "VCPKG", "JPEG_PNG" ] }, + { "name": "arm64-Debug-Linux", "description": "WSL Linux ARM64 (Debug)", "inherits": [ "base", "ARM64", "Debug", "VCPKG", "JPEG_PNG" ] }, + { "name": "arm64-Release-Linux", "description": "WSL Linux ARM64 (Release)", "inherits": [ "base", "ARM64", "Release", "VCPKG", "JPEG_PNG" ] }, { "name": "x64-Fuzzing" , "description": "MSVC for x64 (Release) with ASan", "inherits": [ "base", "x64", "Release", "MSVC", "Fuzzing" ] } ], diff --git a/DirectXTex/DirectXTexP.h b/DirectXTex/DirectXTexP.h index 2c201bf..add3148 100644 --- a/DirectXTex/DirectXTexP.h +++ b/DirectXTex/DirectXTexP.h @@ -198,6 +198,9 @@ using WICPixelFormatGUID = GUID; #define E_BOUNDS static_cast(0x8000000BL) #endif +// HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) +#define HRESULT_ERROR_FILE_NOT_FOUND static_cast(0x80070002) + // HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW) #define HRESULT_E_ARITHMETIC_OVERFLOW static_cast(0x80070216L) diff --git a/README.md b/README.md index 1023365..b05d8f9 100644 --- a/README.md +++ b/README.md @@ -127,4 +127,6 @@ The DirectXTex library is the work of Chuck Walbourn, with contributions from Ma Thanks to Paul Penson for his help with the implementation of ``MemoryStreamOnBlob``. -Thanks to Andrew Farrier and Scott Matloff for their on-going help with code reviews. \ No newline at end of file +Thanks to Andrew Farrier and Scott Matloff for their on-going help with code reviews. + +Thanks to Park DongHa for their contribution of the JPEG/PNG auxiliary functions. diff --git a/Texassemble/texassemble.cpp b/Texassemble/texassemble.cpp index ab05083..a6228b5 100644 --- a/Texassemble/texassemble.cpp +++ b/Texassemble/texassemble.cpp @@ -62,14 +62,19 @@ #include "DirectXTex.h" -//Uncomment to add support for OpenEXR (.exr) -//#define USE_OPENEXR - #ifdef USE_OPENEXR // See for details #include "DirectXTexEXR.h" #endif +// See for details +#ifdef USE_LIBJPEG +#include "DirectXTexJPEG.h" +#endif +#ifdef USE_LIBPNG +#include "DirectXTexPNG.h" +#endif + using namespace DirectX; using Microsoft::WRL::ComPtr; @@ -318,26 +323,41 @@ namespace #ifdef USE_OPENEXR #define CODEC_EXR 0xFFFF0006 +#endif +#ifdef USE_LIBJPEG +#define CODEC_JPEG 0xFFFF0007 +#endif +#ifdef USE_LIBPNG +#define CODEC_PNG 0xFFFF0008 #endif const SValue g_pExtFileTypes[] = { - { L".BMP", WIC_CODEC_BMP }, + { L".BMP", WIC_CODEC_BMP }, + #ifdef USE_LIBJPEG + { L".JPG", CODEC_JPEG }, + { L".JPEG", CODEC_JPEG }, + #else { L".JPG", WIC_CODEC_JPEG }, { L".JPEG", WIC_CODEC_JPEG }, - { L".PNG", WIC_CODEC_PNG }, - { L".DDS", CODEC_DDS }, - { L".TGA", CODEC_TGA }, - { L".HDR", CODEC_HDR }, + #endif + #ifdef USE_LIBPNG + { L".PNG", CODEC_PNG }, + #else + { L".PNG", WIC_CODEC_PNG }, + #endif + { L".DDS", CODEC_DDS }, + { L".TGA", CODEC_TGA }, + { L".HDR", CODEC_HDR }, { L".TIF", WIC_CODEC_TIFF }, { L".TIFF", WIC_CODEC_TIFF }, - { L".WDP", WIC_CODEC_WMP }, - { L".HDP", WIC_CODEC_WMP }, - { L".JXR", WIC_CODEC_WMP }, - #ifdef USE_OPENEXR - { L"EXR", CODEC_EXR }, - #endif - { nullptr, CODEC_DDS } + { L".WDP", WIC_CODEC_WMP }, + { L".HDP", WIC_CODEC_WMP }, + { L".JXR", WIC_CODEC_WMP }, + #ifdef USE_OPENEXR + { L".EXR", CODEC_EXR }, + #endif + { nullptr, CODEC_DDS } }; const SValue g_pFeatureLevels[] = // valid feature levels for -fl for maximimum size @@ -833,10 +853,18 @@ namespace case CODEC_HDR: return SaveToHDRFile(img, szOutputFile); - #ifdef USE_OPENEXR + #ifdef USE_OPENEXR case CODEC_EXR: return SaveToEXRFile(img, szOutputFile); - #endif + #endif + #ifdef USE_LIBJPEG + case CODEC_JPEG: + return SaveToJPEGFile(img, szOutputFile); + #endif + #ifdef USE_LIBPNG + case CODEC_PNG: + return SaveToPNGFile(img, szOutputFile); + #endif default: { @@ -1590,6 +1618,29 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) } } #endif + #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); + if (FAILED(hr)) + { + wprintf(L" FAILED (%08X%ls)\n", static_cast(hr), GetErrorDesc(hr)); + return 1; + } + } + #endif + #ifdef USE_LIBPNG + else if (_wcsicmp(ext.c_str(), L".png") == 0) + { + hr = LoadFromPNGFile(curpath.c_str(), &info, *image); + if (FAILED(hr)) + { + wprintf(L" FAILED (%08X%ls)\n", static_cast(hr), GetErrorDesc(hr)); + return 1; + } + } + #endif + else { // WIC shares the same filter values for mode and dither diff --git a/Texconv/texconv.cpp b/Texconv/texconv.cpp index cbb5b8b..633d7ec 100644 --- a/Texconv/texconv.cpp +++ b/Texconv/texconv.cpp @@ -64,14 +64,19 @@ #include "DirectXPackedVector.h" -//Uncomment to add support for OpenEXR (.exr) -//#define USE_OPENEXR - #ifdef USE_OPENEXR // See for details #include "DirectXTexEXR.h" #endif +// See for details +#ifdef USE_LIBJPEG +#include "DirectXTexJPEG.h" +#endif +#ifdef USE_LIBPNG +#include "DirectXTexPNG.h" +#endif + using namespace DirectX; using namespace DirectX::PackedVector; using Microsoft::WRL::ComPtr; @@ -456,14 +461,29 @@ namespace #ifdef USE_OPENEXR #define CODEC_EXR 0xFFFF0008 +#endif +#ifdef USE_LIBJPEG +#define CODEC_JPEG 0xFFFF0009 +#endif +#ifdef USE_LIBPNG +#define CODEC_PNG 0xFFFF000A #endif const SValue g_pSaveFileTypes[] = // valid formats to write to { { L"bmp", WIC_CODEC_BMP }, +#ifdef USE_LIBJPEG + { L"jpg", CODEC_JPEG }, + { L"jpeg", CODEC_JPEG }, +#else { L"jpg", WIC_CODEC_JPEG }, { L"jpeg", WIC_CODEC_JPEG }, +#endif +#ifdef USE_LIBPNG + { L"png", CODEC_PNG }, +#else { L"png", WIC_CODEC_PNG }, +#endif { L"dds", CODEC_DDS }, { L"ddx", CODEC_DDS }, { L"tga", CODEC_TGA }, @@ -2212,6 +2232,30 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) continue; } } + #endif + #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); + if (FAILED(hr)) + { + wprintf(L" FAILED (%08X%ls)\n", static_cast(hr), GetErrorDesc(hr)); + retVal = 1; + continue; + } + } + #endif + #ifdef USE_LIBPNG + else if (_wcsicmp(ext.c_str(), L".png") == 0) + { + hr = LoadFromPNGFile(curpath.c_str(), &info, *image); + if (FAILED(hr)) + { + wprintf(L" FAILED (%08X%ls)\n", static_cast(hr), GetErrorDesc(hr)); + retVal = 1; + continue; + } + } #endif else { @@ -3803,6 +3847,16 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) hr = SaveToEXRFile(img[0], destName.c_str()); break; #endif + #ifdef USE_LIBJPEG + case CODEC_JPEG: + hr = SaveToJPEGFile(img[0], destName.c_str()); + break; + #endif + #ifdef USE_LIBPNG + case CODEC_PNG: + hr = SaveToPNGFile(img[0], destName.c_str()); + break; + #endif default: { diff --git a/Texdiag/texdiag.cpp b/Texdiag/texdiag.cpp index 00f62c6..cfdfe4c 100644 --- a/Texdiag/texdiag.cpp +++ b/Texdiag/texdiag.cpp @@ -58,14 +58,19 @@ #include -//Uncomment to add support for OpenEXR (.exr) -//#define USE_OPENEXR - #ifdef USE_OPENEXR // See for details #include "DirectXTexEXR.h" #endif +// See for details +#ifdef USE_LIBJPEG +#include "DirectXTexJPEG.h" +#endif +#ifdef USE_LIBPNG +#include "DirectXTexPNG.h" +#endif + using namespace DirectX; enum COMMANDS : uint32_t @@ -348,13 +353,28 @@ const SValue g_pFilters[] = #ifdef USE_OPENEXR #define CODEC_EXR 0xFFFF0006 #endif +#ifdef USE_LIBJPEG +#define CODEC_JPEG 0xFFFF0007 +#endif +#ifdef USE_LIBPNG +#define CODEC_PNG 0xFFFF0008 +#endif const SValue g_pDumpFileTypes[] = { { L"bmp", WIC_CODEC_BMP }, +#ifdef USE_LIBJPEG + { L"jpg", CODEC_JPEG }, + { L"jpeg", CODEC_JPEG }, +#else { L"jpg", WIC_CODEC_JPEG }, { L"jpeg", WIC_CODEC_JPEG }, +#endif +#ifdef USE_LIBPNG + { L"png", CODEC_PNG }, +#else { L"png", WIC_CODEC_PNG }, +#endif { L"tga", CODEC_TGA }, { L"hdr", CODEC_HDR }, { L"tif", WIC_CODEC_TIFF }, @@ -368,22 +388,31 @@ const SValue g_pDumpFileTypes[] = const SValue g_pExtFileTypes[] = { - { L".bmp", WIC_CODEC_BMP }, + { L".bmp", WIC_CODEC_BMP }, +#ifdef USE_LIBJPEG + { L".jpg", CODEC_JPEG }, + { L".jpeg", CODEC_JPEG }, +#else { L".jpg", WIC_CODEC_JPEG }, { L".jpeg", WIC_CODEC_JPEG }, - { L".png", WIC_CODEC_PNG }, - { L".dds", CODEC_DDS }, - { L".tga", CODEC_TGA }, - { L".hdr", CODEC_HDR }, +#endif +#ifdef USE_LIBPNG + { L".png", CODEC_PNG }, +#else + { L".png", WIC_CODEC_PNG }, +#endif + { L".dds", CODEC_DDS }, + { L".tga", CODEC_TGA }, + { L".hdr", CODEC_HDR }, { L".tif", WIC_CODEC_TIFF }, { L".tiff", WIC_CODEC_TIFF }, - { L".wdp", WIC_CODEC_WMP }, - { L".hdp", WIC_CODEC_WMP }, - { L".jxr", WIC_CODEC_WMP }, + { L".wdp", WIC_CODEC_WMP }, + { L".hdp", WIC_CODEC_WMP }, + { L".jxr", WIC_CODEC_WMP }, #ifdef USE_OPENEXR - { L"exr", CODEC_EXR }, + { L"exr", CODEC_EXR }, #endif - { nullptr, CODEC_DDS } + { nullptr, CODEC_DDS } }; ////////////////////////////////////////////////////////////////////////////// @@ -813,6 +842,18 @@ namespace { return LoadFromEXRFile(fileName, &info, *image); } + #endif + #ifdef USE_LIBJPEG + else if (_wcsicmp(ext.c_str(), L".jpg") == 0 || _wcsicmp(ext.c_str(), L".jpeg") == 0) + { + return LoadFromJPEGFile(fileName, &info, *image); + } + #endif + #ifdef USE_LIBPNG + else if (_wcsicmp(ext.c_str(), L".png") == 0) + { + return LoadFromPNGFile(fileName, &info, *image); + } #endif else { @@ -853,11 +894,18 @@ namespace case CODEC_HDR: return SaveToHDRFile(*image, fileName); - #ifdef USE_OPENEXR + #ifdef USE_OPENEXR case CODEC_EXR: return SaveToEXRFile(*image, fileName); - #endif - + #endif + #ifdef USE_LIBJPEG + case CODEC_JPEG: + return SaveToJPEGFile(*image, fileName); + #endif + #ifdef USE_LIBPNG + case CODEC_PNG: + return SaveToPNGFile(*image, fileName); + #endif default: return SaveToWICFile(*image, WIC_FLAGS_NONE, GetWICCodec(static_cast(codec)), fileName); } diff --git a/build/DirectXTex-config.cmake.in b/build/DirectXTex-config.cmake.in index 60426a5..155f3d9 100644 --- a/build/DirectXTex-config.cmake.in +++ b/build/DirectXTex-config.cmake.in @@ -13,6 +13,16 @@ if(ENABLE_OPENEXR_SUPPORT) find_dependency(OpenEXR) endif() +set(ENABLE_LIBJPEG_SUPPORT @ENABLE_LIBJPEG_SUPPORT@) +if(ENABLE_LIBJPEG_SUPPORT) + find_dependency(JPEG) +endif() + +set(ENABLE_LIBPNG_SUPPORT @ENABLE_LIBPNG_SUPPORT@) +if(ENABLE_LIBPNG_SUPPORT) + find_dependency(PNG) +endif() + if(MINGW OR (NOT WIN32)) find_dependency(directx-headers) find_dependency(directxmath)