From 1208f967331ab68d1a150ef4e669551c6b71bf22 Mon Sep 17 00:00:00 2001 From: Chuck Walbourn Date: Wed, 1 Jul 2020 01:37:13 -0700 Subject: [PATCH] Reorganized code in texassmble --- CMakeLists.txt | 4 +- Texassemble/AnimatedGif.cpp | 407 +++++++++ Texassemble/Texassemble_Desktop_2017.vcxproj | 1 + .../Texassemble_Desktop_2017.vcxproj.filters | 1 + Texassemble/Texassemble_Desktop_2019.vcxproj | 1 + .../Texassemble_Desktop_2019.vcxproj.filters | 1 + Texassemble/texassemble.cpp | 803 +++++------------- 7 files changed, 636 insertions(+), 582 deletions(-) create mode 100644 Texassemble/AnimatedGif.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bfa9e6..97fafdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,7 +99,9 @@ if(MSVC) string(REPLACE "/GR " "/GR- " CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) endif() -add_executable(texassemble Texassemble/texassemble.cpp) +add_executable(texassemble + Texassemble/texassemble.cpp + Texassemble/AnimatedGif.cpp) target_link_libraries(texassemble ${PROJECT_NAME}) source_group(texassemble REGULAR_EXPRESSION Texassemble/*.*) diff --git a/Texassemble/AnimatedGif.cpp b/Texassemble/AnimatedGif.cpp new file mode 100644 index 0000000..8df877f --- /dev/null +++ b/Texassemble/AnimatedGif.cpp @@ -0,0 +1,407 @@ +//-------------------------------------------------------------------------------------- +// File: AnimatedGif.cpp +// +// Code for converting an animated GIF to a series of texture frames. +// +// References: +// https://code.msdn.microsoft.com/windowsapps/Windows-Imaging-Component-65abbc6a/ +// http://www.imagemagick.org/Usage/anim_basics/#dispose +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +//-------------------------------------------------------------------------------------- + +#pragma warning(push) +#pragma warning(disable : 4005) +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#define NODRAWTEXT +#define NOGDI +#define NOMCX +#define NOSERVICE +#define NOHELP +#pragma warning(pop) + +#include +#include + +#include + +#include + +#pragma warning(disable : 4619 4616 26812) + +#include "DirectXTex.h" + +using namespace DirectX; +using Microsoft::WRL::ComPtr; + +namespace +{ + enum + { + DM_UNDEFINED = 0, + DM_NONE = 1, + DM_BACKGROUND = 2, + DM_PREVIOUS = 3 + }; + + + void FillRectangle(const Image& img, const RECT& destRect, uint32_t color) + { + RECT clipped = + { + (destRect.left < 0) ? 0 : destRect.left, + (destRect.top < 0) ? 0 : destRect.top, + (destRect.right > static_cast(img.width)) ? static_cast(img.width) : destRect.right, + (destRect.bottom > static_cast(img.height)) ? static_cast(img.height) : destRect.bottom + }; + + auto ptr = reinterpret_cast(img.pixels + size_t(clipped.top) * img.rowPitch + size_t(clipped.left) * sizeof(uint32_t)); + + for (long y = clipped.top; y < clipped.bottom; ++y) + { + auto pixelPtr = reinterpret_cast(ptr); + for (long x = clipped.left; x < clipped.right; ++x) + { + *pixelPtr++ = color; + } + + ptr += img.rowPitch; + } + } + + + void BlendRectangle(const Image& composed, const Image& raw, const RECT& destRect, uint32_t transparent) + { + RECT clipped = + { + (destRect.left < 0) ? 0 : destRect.left, + (destRect.top < 0) ? 0 : destRect.top, + (destRect.right > static_cast(composed.width)) ? static_cast(composed.width) : destRect.right, + (destRect.bottom > static_cast(composed.height)) ? static_cast(composed.height) : destRect.bottom + }; + + auto rawPtr = reinterpret_cast(raw.pixels); + auto composedPtr = reinterpret_cast( + composed.pixels + + size_t(clipped.top) * composed.rowPitch + + size_t(clipped.left) * sizeof(uint32_t)); + + for (long y = clipped.top; y < clipped.bottom; ++y) + { + auto srcPtr = reinterpret_cast(rawPtr); + auto destPtr = reinterpret_cast(composedPtr); + for (long x = clipped.left; x < clipped.right; ++x, ++srcPtr, ++destPtr) + { + if (transparent == *srcPtr) + continue; + + *destPtr = *srcPtr; + } + + rawPtr += raw.rowPitch; + composedPtr += composed.rowPitch; + } + } +} + +HRESULT LoadAnimatedGif(const wchar_t* szFile, std::vector>& loadedImages, bool usebgcolor) +{ + bool iswic2; + auto pWIC = GetWICFactory(iswic2); + if (!pWIC) + return E_NOINTERFACE; + + ComPtr decoder; + HRESULT hr = pWIC->CreateDecoderFromFilename(szFile, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, decoder.GetAddressOf()); + if (FAILED(hr)) + return hr; + + { + GUID containerFormat; + hr = decoder->GetContainerFormat(&containerFormat); + if (FAILED(hr)) + return hr; + + if (memcmp(&containerFormat, &GUID_ContainerFormatGif, sizeof(GUID)) != 0) + { + // This function only works for GIF + return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + } + } + + ComPtr metareader; + hr = decoder->GetMetadataQueryReader(metareader.GetAddressOf()); + if (FAILED(hr)) + return hr; + + PROPVARIANT propValue; + PropVariantInit(&propValue); + + // Get palette + WICColor rgbColors[256] = {}; + UINT actualColors = 0; + { + ComPtr palette; + hr = pWIC->CreatePalette(palette.GetAddressOf()); + if (FAILED(hr)) + return hr; + + hr = decoder->CopyPalette(palette.Get()); + if (FAILED(hr)) + return hr; + + hr = palette->GetColors(_countof(rgbColors), rgbColors, &actualColors); + if (FAILED(hr)) + return hr; + } + + // Get background color + UINT bgColor = 0; + if (usebgcolor) + { + // Most browsers just ignore the background color metadata and always use transparency + hr = metareader->GetMetadataByName(L"/logscrdesc/GlobalColorTableFlag", &propValue); + if (SUCCEEDED(hr)) + { + bool hasTable = (propValue.vt == VT_BOOL && propValue.boolVal); + PropVariantClear(&propValue); + + if (hasTable) + { + hr = metareader->GetMetadataByName(L"/logscrdesc/BackgroundColorIndex", &propValue); + if (SUCCEEDED(hr)) + { + if (propValue.vt == VT_UI1) + { + uint8_t index = propValue.bVal; + + if (index < actualColors) + { + bgColor = rgbColors[index]; + } + } + PropVariantClear(&propValue); + } + } + } + } + + // Get global frame size + UINT width = 0; + UINT height = 0; + + hr = metareader->GetMetadataByName(L"/logscrdesc/Width", &propValue); + if (FAILED(hr)) + return hr; + + if (propValue.vt != VT_UI2) + return E_FAIL; + + width = propValue.uiVal; + PropVariantClear(&propValue); + + hr = metareader->GetMetadataByName(L"/logscrdesc/Height", &propValue); + if (FAILED(hr)) + return hr; + + if (propValue.vt != VT_UI2) + return E_FAIL; + + height = propValue.uiVal; + PropVariantClear(&propValue); + + UINT fcount; + hr = decoder->GetFrameCount(&fcount); + if (FAILED(hr)) + return hr; + + UINT disposal = DM_UNDEFINED; + RECT rct = {}; + + UINT previousFrame = 0; + for (UINT iframe = 0; iframe < fcount; ++iframe) + { + int transparentIndex = -1; + + std::unique_ptr frameImage(new (std::nothrow) ScratchImage); + if (!frameImage) + return E_OUTOFMEMORY; + + if (disposal == DM_PREVIOUS) + { + hr = frameImage->InitializeFromImage(*loadedImages[previousFrame]->GetImage(0, 0, 0)); + } + else if (iframe > 0) + { + hr = frameImage->InitializeFromImage(*loadedImages[iframe - 1]->GetImage(0, 0, 0)); + } + else + { + hr = frameImage->Initialize2D(DXGI_FORMAT_B8G8R8A8_UNORM, width, height, 1, 1); + } + if (FAILED(hr)) + return hr; + + auto composedImage = frameImage->GetImage(0, 0, 0); + + if (!iframe) + { + RECT fullRct = { 0, 0, static_cast(width), static_cast(height) }; + FillRectangle(*composedImage, fullRct, bgColor); + } + else if (disposal == DM_BACKGROUND) + { + FillRectangle(*composedImage, rct, bgColor); + } + + ComPtr frame; + hr = decoder->GetFrame(iframe, frame.GetAddressOf()); + if (FAILED(hr)) + return hr; + + WICPixelFormatGUID pixelFormat; + hr = frame->GetPixelFormat(&pixelFormat); + if (FAILED(hr)) + return hr; + + if (memcmp(&pixelFormat, &GUID_WICPixelFormat8bppIndexed, sizeof(GUID)) != 0) + { + // GIF is always loaded as this format + return E_UNEXPECTED; + } + + ComPtr frameMeta; + hr = frame->GetMetadataQueryReader(frameMeta.GetAddressOf()); + if (SUCCEEDED(hr)) + { + hr = frameMeta->GetMetadataByName(L"/imgdesc/Left", &propValue); + if (SUCCEEDED(hr)) + { + hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + rct.left = static_cast(propValue.uiVal); + } + PropVariantClear(&propValue); + } + + hr = frameMeta->GetMetadataByName(L"/imgdesc/Top", &propValue); + if (SUCCEEDED(hr)) + { + hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + rct.top = static_cast(propValue.uiVal); + } + PropVariantClear(&propValue); + } + + hr = frameMeta->GetMetadataByName(L"/imgdesc/Width", &propValue); + if (SUCCEEDED(hr)) + { + hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + rct.right = static_cast(propValue.uiVal) + rct.left; + } + PropVariantClear(&propValue); + } + + hr = frameMeta->GetMetadataByName(L"/imgdesc/Height", &propValue); + if (SUCCEEDED(hr)) + { + hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + rct.bottom = static_cast(propValue.uiVal) + rct.top; + } + PropVariantClear(&propValue); + } + + disposal = DM_UNDEFINED; + hr = frameMeta->GetMetadataByName(L"/grctlext/Disposal", &propValue); + if (SUCCEEDED(hr)) + { + hr = (propValue.vt == VT_UI1 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + disposal = propValue.bVal; + } + PropVariantClear(&propValue); + } + + hr = frameMeta->GetMetadataByName(L"/grctlext/TransparencyFlag", &propValue); + if (SUCCEEDED(hr)) + { + hr = (propValue.vt == VT_BOOL ? S_OK : E_FAIL); + if (SUCCEEDED(hr) && propValue.boolVal) + { + PropVariantClear(&propValue); + hr = frameMeta->GetMetadataByName(L"/grctlext/TransparentColorIndex", &propValue); + if (SUCCEEDED(hr)) + { + hr = (propValue.vt == VT_UI1 ? S_OK : E_FAIL); + if (SUCCEEDED(hr) && propValue.uiVal < actualColors) + { + transparentIndex = static_cast(propValue.uiVal); + } + } + } + PropVariantClear(&propValue); + } + + } + + UINT w, h; + hr = frame->GetSize(&w, &h); + if (FAILED(hr)) + return hr; + + ScratchImage rawFrame; + hr = rawFrame.Initialize2D(DXGI_FORMAT_B8G8R8A8_UNORM, w, h, 1, 1); + if (FAILED(hr)) + return hr; + + ComPtr FC; + hr = pWIC->CreateFormatConverter(FC.GetAddressOf()); + if (FAILED(hr)) + return hr; + + hr = FC->Initialize(frame.Get(), GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, nullptr, 0, WICBitmapPaletteTypeMedianCut); + if (FAILED(hr)) + return hr; + + auto img = rawFrame.GetImage(0, 0, 0); + + hr = FC->CopyPixels(nullptr, static_cast(img->rowPitch), static_cast(img->slicePitch), img->pixels); + if (FAILED(hr)) + return hr; + + if (!iframe || transparentIndex == -1) + { + Rect fullRect(0, 0, img->width, img->height); + hr = CopyRectangle(*img, fullRect, *composedImage, TEX_FILTER_DEFAULT, size_t(rct.left), size_t(rct.top)); + if (FAILED(hr)) + return hr; + } + else + { + BlendRectangle(*composedImage, *img, rct, rgbColors[transparentIndex]); + } + + if (disposal == DM_UNDEFINED || disposal == DM_NONE) + { + previousFrame = iframe; + } + + loadedImages.emplace_back(std::move(frameImage)); + } + + PropVariantClear(&propValue); + + return S_OK; +} diff --git a/Texassemble/Texassemble_Desktop_2017.vcxproj b/Texassemble/Texassemble_Desktop_2017.vcxproj index ba5c32e..c34ca34 100644 --- a/Texassemble/Texassemble_Desktop_2017.vcxproj +++ b/Texassemble/Texassemble_Desktop_2017.vcxproj @@ -316,6 +316,7 @@ + diff --git a/Texassemble/Texassemble_Desktop_2017.vcxproj.filters b/Texassemble/Texassemble_Desktop_2017.vcxproj.filters index 781df78..e1687da 100644 --- a/Texassemble/Texassemble_Desktop_2017.vcxproj.filters +++ b/Texassemble/Texassemble_Desktop_2017.vcxproj.filters @@ -8,6 +8,7 @@ + diff --git a/Texassemble/Texassemble_Desktop_2019.vcxproj b/Texassemble/Texassemble_Desktop_2019.vcxproj index dd77c25..08e59b7 100644 --- a/Texassemble/Texassemble_Desktop_2019.vcxproj +++ b/Texassemble/Texassemble_Desktop_2019.vcxproj @@ -322,6 +322,7 @@ + diff --git a/Texassemble/Texassemble_Desktop_2019.vcxproj.filters b/Texassemble/Texassemble_Desktop_2019.vcxproj.filters index 781df78..e1687da 100644 --- a/Texassemble/Texassemble_Desktop_2019.vcxproj.filters +++ b/Texassemble/Texassemble_Desktop_2019.vcxproj.filters @@ -8,6 +8,7 @@ + diff --git a/Texassemble/texassemble.cpp b/Texassemble/texassemble.cpp index e5ad450..1f338f9 100644 --- a/Texassemble/texassemble.cpp +++ b/Texassemble/texassemble.cpp @@ -51,216 +51,218 @@ using namespace DirectX; using Microsoft::WRL::ComPtr; -enum COMMANDS +namespace { - CMD_CUBE = 1, - CMD_VOLUME, - CMD_ARRAY, - CMD_CUBEARRAY, - CMD_H_CROSS, - CMD_V_CROSS, - CMD_H_STRIP, - CMD_V_STRIP, - CMD_MERGE, - CMD_GIF, - CMD_ARRAY_STRIP, - CMD_MAX -}; + enum COMMANDS + { + CMD_CUBE = 1, + CMD_VOLUME, + CMD_ARRAY, + CMD_CUBEARRAY, + CMD_H_CROSS, + CMD_V_CROSS, + CMD_H_STRIP, + CMD_V_STRIP, + CMD_MERGE, + CMD_GIF, + CMD_ARRAY_STRIP, + CMD_MAX + }; -enum OPTIONS -{ - OPT_RECURSIVE = 1, - OPT_WIDTH, - OPT_HEIGHT, - OPT_FORMAT, - OPT_FILTER, - OPT_SRGBI, - OPT_SRGBO, - OPT_SRGB, - OPT_OUTPUTFILE, - OPT_TOLOWER, - OPT_OVERWRITE, - OPT_USE_DX10, - OPT_NOLOGO, - OPT_SEPALPHA, - OPT_NO_WIC, - OPT_DEMUL_ALPHA, - OPT_TA_WRAP, - OPT_TA_MIRROR, - OPT_TONEMAP, - OPT_FILELIST, - OPT_GIF_BGCOLOR, - OPT_MAX -}; + enum OPTIONS + { + OPT_RECURSIVE = 1, + OPT_WIDTH, + OPT_HEIGHT, + OPT_FORMAT, + OPT_FILTER, + OPT_SRGBI, + OPT_SRGBO, + OPT_SRGB, + OPT_OUTPUTFILE, + OPT_TOLOWER, + OPT_OVERWRITE, + OPT_USE_DX10, + OPT_NOLOGO, + OPT_SEPALPHA, + OPT_NO_WIC, + OPT_DEMUL_ALPHA, + OPT_TA_WRAP, + OPT_TA_MIRROR, + OPT_TONEMAP, + OPT_FILELIST, + OPT_GIF_BGCOLOR, + OPT_MAX + }; -static_assert(OPT_MAX <= 32, "dwOptions is a DWORD bitfield"); + static_assert(OPT_MAX <= 32, "dwOptions is a DWORD bitfield"); -struct SConversion -{ - wchar_t szSrc[MAX_PATH]; -}; + struct SConversion + { + wchar_t szSrc[MAX_PATH]; + }; -struct SValue -{ - LPCWSTR pName; - DWORD dwValue; -}; + struct SValue + { + LPCWSTR pName; + DWORD dwValue; + }; -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// -const SValue g_pCommands[] = -{ - { L"cube", CMD_CUBE }, - { L"volume", CMD_VOLUME }, - { L"array", CMD_ARRAY }, - { L"cubearray", CMD_CUBEARRAY }, - { L"h-cross", CMD_H_CROSS }, - { L"v-cross", CMD_V_CROSS }, - { L"h-strip", CMD_H_STRIP }, - { L"v-strip", CMD_V_STRIP }, - { L"merge", CMD_MERGE }, - { L"gif", CMD_GIF }, - { L"array-strip", CMD_ARRAY_STRIP }, - { nullptr, 0 } -}; + const SValue g_pCommands[] = + { + { L"cube", CMD_CUBE }, + { L"volume", CMD_VOLUME }, + { L"array", CMD_ARRAY }, + { L"cubearray", CMD_CUBEARRAY }, + { L"h-cross", CMD_H_CROSS }, + { L"v-cross", CMD_V_CROSS }, + { L"h-strip", CMD_H_STRIP }, + { L"v-strip", CMD_V_STRIP }, + { L"merge", CMD_MERGE }, + { L"gif", CMD_GIF }, + { L"array-strip", CMD_ARRAY_STRIP }, + { nullptr, 0 } + }; -const SValue g_pOptions[] = -{ - { L"r", OPT_RECURSIVE }, - { L"w", OPT_WIDTH }, - { L"h", OPT_HEIGHT }, - { L"f", OPT_FORMAT }, - { L"if", OPT_FILTER }, - { L"srgbi", OPT_SRGBI }, - { L"srgbo", OPT_SRGBO }, - { L"srgb", OPT_SRGB }, - { L"o", OPT_OUTPUTFILE }, - { L"l", OPT_TOLOWER }, - { L"y", OPT_OVERWRITE }, - { L"dx10", OPT_USE_DX10 }, - { L"nologo", OPT_NOLOGO }, - { L"sepalpha", OPT_SEPALPHA }, - { L"nowic", OPT_NO_WIC }, - { L"alpha", OPT_DEMUL_ALPHA }, - { L"wrap", OPT_TA_WRAP }, - { L"mirror", OPT_TA_MIRROR }, - { L"tonemap", OPT_TONEMAP }, - { L"flist", OPT_FILELIST }, - { L"bgcolor", OPT_GIF_BGCOLOR }, - { nullptr, 0 } -}; + const SValue g_pOptions[] = + { + { L"r", OPT_RECURSIVE }, + { L"w", OPT_WIDTH }, + { L"h", OPT_HEIGHT }, + { L"f", OPT_FORMAT }, + { L"if", OPT_FILTER }, + { L"srgbi", OPT_SRGBI }, + { L"srgbo", OPT_SRGBO }, + { L"srgb", OPT_SRGB }, + { L"o", OPT_OUTPUTFILE }, + { L"l", OPT_TOLOWER }, + { L"y", OPT_OVERWRITE }, + { L"dx10", OPT_USE_DX10 }, + { L"nologo", OPT_NOLOGO }, + { L"sepalpha", OPT_SEPALPHA }, + { L"nowic", OPT_NO_WIC }, + { L"alpha", OPT_DEMUL_ALPHA }, + { L"wrap", OPT_TA_WRAP }, + { L"mirror", OPT_TA_MIRROR }, + { L"tonemap", OPT_TONEMAP }, + { L"flist", OPT_FILELIST }, + { L"bgcolor", OPT_GIF_BGCOLOR }, + { nullptr, 0 } + }; #define DEFFMT(fmt) { L## #fmt, DXGI_FORMAT_ ## fmt } -const SValue g_pFormats[] = -{ - // List does not include _TYPELESS or depth/stencil formats - DEFFMT(R32G32B32A32_FLOAT), - DEFFMT(R32G32B32A32_UINT), - DEFFMT(R32G32B32A32_SINT), - DEFFMT(R32G32B32_FLOAT), - DEFFMT(R32G32B32_UINT), - DEFFMT(R32G32B32_SINT), - DEFFMT(R16G16B16A16_FLOAT), - DEFFMT(R16G16B16A16_UNORM), - DEFFMT(R16G16B16A16_UINT), - DEFFMT(R16G16B16A16_SNORM), - DEFFMT(R16G16B16A16_SINT), - DEFFMT(R32G32_FLOAT), - DEFFMT(R32G32_UINT), - DEFFMT(R32G32_SINT), - DEFFMT(R10G10B10A2_UNORM), - DEFFMT(R10G10B10A2_UINT), - DEFFMT(R11G11B10_FLOAT), - DEFFMT(R8G8B8A8_UNORM), - DEFFMT(R8G8B8A8_UNORM_SRGB), - DEFFMT(R8G8B8A8_UINT), - DEFFMT(R8G8B8A8_SNORM), - DEFFMT(R8G8B8A8_SINT), - DEFFMT(R16G16_FLOAT), - DEFFMT(R16G16_UNORM), - DEFFMT(R16G16_UINT), - DEFFMT(R16G16_SNORM), - DEFFMT(R16G16_SINT), - DEFFMT(R32_FLOAT), - DEFFMT(R32_UINT), - DEFFMT(R32_SINT), - DEFFMT(R8G8_UNORM), - DEFFMT(R8G8_UINT), - DEFFMT(R8G8_SNORM), - DEFFMT(R8G8_SINT), - DEFFMT(R16_FLOAT), - DEFFMT(R16_UNORM), - DEFFMT(R16_UINT), - DEFFMT(R16_SNORM), - DEFFMT(R16_SINT), - DEFFMT(R8_UNORM), - DEFFMT(R8_UINT), - DEFFMT(R8_SNORM), - DEFFMT(R8_SINT), - DEFFMT(A8_UNORM), - //DEFFMT(R1_UNORM) - DEFFMT(R9G9B9E5_SHAREDEXP), - DEFFMT(R8G8_B8G8_UNORM), - DEFFMT(G8R8_G8B8_UNORM), - DEFFMT(B5G6R5_UNORM), - DEFFMT(B5G5R5A1_UNORM), + const SValue g_pFormats[] = + { + // List does not include _TYPELESS or depth/stencil formats + DEFFMT(R32G32B32A32_FLOAT), + DEFFMT(R32G32B32A32_UINT), + DEFFMT(R32G32B32A32_SINT), + DEFFMT(R32G32B32_FLOAT), + DEFFMT(R32G32B32_UINT), + DEFFMT(R32G32B32_SINT), + DEFFMT(R16G16B16A16_FLOAT), + DEFFMT(R16G16B16A16_UNORM), + DEFFMT(R16G16B16A16_UINT), + DEFFMT(R16G16B16A16_SNORM), + DEFFMT(R16G16B16A16_SINT), + DEFFMT(R32G32_FLOAT), + DEFFMT(R32G32_UINT), + DEFFMT(R32G32_SINT), + DEFFMT(R10G10B10A2_UNORM), + DEFFMT(R10G10B10A2_UINT), + DEFFMT(R11G11B10_FLOAT), + DEFFMT(R8G8B8A8_UNORM), + DEFFMT(R8G8B8A8_UNORM_SRGB), + DEFFMT(R8G8B8A8_UINT), + DEFFMT(R8G8B8A8_SNORM), + DEFFMT(R8G8B8A8_SINT), + DEFFMT(R16G16_FLOAT), + DEFFMT(R16G16_UNORM), + DEFFMT(R16G16_UINT), + DEFFMT(R16G16_SNORM), + DEFFMT(R16G16_SINT), + DEFFMT(R32_FLOAT), + DEFFMT(R32_UINT), + DEFFMT(R32_SINT), + DEFFMT(R8G8_UNORM), + DEFFMT(R8G8_UINT), + DEFFMT(R8G8_SNORM), + DEFFMT(R8G8_SINT), + DEFFMT(R16_FLOAT), + DEFFMT(R16_UNORM), + DEFFMT(R16_UINT), + DEFFMT(R16_SNORM), + DEFFMT(R16_SINT), + DEFFMT(R8_UNORM), + DEFFMT(R8_UINT), + DEFFMT(R8_SNORM), + DEFFMT(R8_SINT), + DEFFMT(A8_UNORM), + //DEFFMT(R1_UNORM) + DEFFMT(R9G9B9E5_SHAREDEXP), + DEFFMT(R8G8_B8G8_UNORM), + DEFFMT(G8R8_G8B8_UNORM), + DEFFMT(B5G6R5_UNORM), + DEFFMT(B5G5R5A1_UNORM), - // DXGI 1.1 formats - DEFFMT(B8G8R8A8_UNORM), - DEFFMT(B8G8R8X8_UNORM), - DEFFMT(R10G10B10_XR_BIAS_A2_UNORM), - DEFFMT(B8G8R8A8_UNORM_SRGB), - DEFFMT(B8G8R8X8_UNORM_SRGB), + // DXGI 1.1 formats + DEFFMT(B8G8R8A8_UNORM), + DEFFMT(B8G8R8X8_UNORM), + DEFFMT(R10G10B10_XR_BIAS_A2_UNORM), + DEFFMT(B8G8R8A8_UNORM_SRGB), + DEFFMT(B8G8R8X8_UNORM_SRGB), - // DXGI 1.2 formats - DEFFMT(AYUV), - DEFFMT(Y410), - DEFFMT(Y416), - DEFFMT(YUY2), - DEFFMT(Y210), - DEFFMT(Y216), - // No support for legacy paletted video formats (AI44, IA44, P8, A8P8) - DEFFMT(B4G4R4A4_UNORM), + // DXGI 1.2 formats + DEFFMT(AYUV), + DEFFMT(Y410), + DEFFMT(Y416), + DEFFMT(YUY2), + DEFFMT(Y210), + DEFFMT(Y216), + // No support for legacy paletted video formats (AI44, IA44, P8, A8P8) + DEFFMT(B4G4R4A4_UNORM), - { nullptr, DXGI_FORMAT_UNKNOWN } -}; + { nullptr, DXGI_FORMAT_UNKNOWN } + }; -const SValue g_pFormatAliases[] = -{ - { L"RGBA", DXGI_FORMAT_R8G8B8A8_UNORM }, - { L"BGRA", DXGI_FORMAT_B8G8R8A8_UNORM }, + const SValue g_pFormatAliases[] = + { + { L"RGBA", DXGI_FORMAT_R8G8B8A8_UNORM }, + { L"BGRA", DXGI_FORMAT_B8G8R8A8_UNORM }, - { L"FP16", DXGI_FORMAT_R16G16B16A16_FLOAT }, - { L"FP32", DXGI_FORMAT_R32G32B32A32_FLOAT }, + { L"FP16", DXGI_FORMAT_R16G16B16A16_FLOAT }, + { L"FP32", DXGI_FORMAT_R32G32B32A32_FLOAT }, - { nullptr, DXGI_FORMAT_UNKNOWN } -}; + { nullptr, DXGI_FORMAT_UNKNOWN } + }; -const SValue g_pFilters[] = -{ - { L"POINT", TEX_FILTER_POINT }, - { L"LINEAR", TEX_FILTER_LINEAR }, - { L"CUBIC", TEX_FILTER_CUBIC }, - { L"FANT", TEX_FILTER_FANT }, - { L"BOX", TEX_FILTER_BOX }, - { L"TRIANGLE", TEX_FILTER_TRIANGLE }, - { L"POINT_DITHER", TEX_FILTER_POINT | TEX_FILTER_DITHER }, - { L"LINEAR_DITHER", TEX_FILTER_LINEAR | TEX_FILTER_DITHER }, - { L"CUBIC_DITHER", TEX_FILTER_CUBIC | TEX_FILTER_DITHER }, - { L"FANT_DITHER", TEX_FILTER_FANT | TEX_FILTER_DITHER }, - { L"BOX_DITHER", TEX_FILTER_BOX | TEX_FILTER_DITHER }, - { L"TRIANGLE_DITHER", TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER }, - { L"POINT_DITHER_DIFFUSION", TEX_FILTER_POINT | TEX_FILTER_DITHER_DIFFUSION }, - { L"LINEAR_DITHER_DIFFUSION", TEX_FILTER_LINEAR | TEX_FILTER_DITHER_DIFFUSION }, - { L"CUBIC_DITHER_DIFFUSION", TEX_FILTER_CUBIC | TEX_FILTER_DITHER_DIFFUSION }, - { L"FANT_DITHER_DIFFUSION", TEX_FILTER_FANT | TEX_FILTER_DITHER_DIFFUSION }, - { L"BOX_DITHER_DIFFUSION", TEX_FILTER_BOX | TEX_FILTER_DITHER_DIFFUSION }, - { L"TRIANGLE_DITHER_DIFFUSION", TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER_DIFFUSION }, - { nullptr, TEX_FILTER_DEFAULT } -}; + const SValue g_pFilters[] = + { + { L"POINT", TEX_FILTER_POINT }, + { L"LINEAR", TEX_FILTER_LINEAR }, + { L"CUBIC", TEX_FILTER_CUBIC }, + { L"FANT", TEX_FILTER_FANT }, + { L"BOX", TEX_FILTER_BOX }, + { L"TRIANGLE", TEX_FILTER_TRIANGLE }, + { L"POINT_DITHER", TEX_FILTER_POINT | TEX_FILTER_DITHER }, + { L"LINEAR_DITHER", TEX_FILTER_LINEAR | TEX_FILTER_DITHER }, + { L"CUBIC_DITHER", TEX_FILTER_CUBIC | TEX_FILTER_DITHER }, + { L"FANT_DITHER", TEX_FILTER_FANT | TEX_FILTER_DITHER }, + { L"BOX_DITHER", TEX_FILTER_BOX | TEX_FILTER_DITHER }, + { L"TRIANGLE_DITHER", TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER }, + { L"POINT_DITHER_DIFFUSION", TEX_FILTER_POINT | TEX_FILTER_DITHER_DIFFUSION }, + { L"LINEAR_DITHER_DIFFUSION", TEX_FILTER_LINEAR | TEX_FILTER_DITHER_DIFFUSION }, + { L"CUBIC_DITHER_DIFFUSION", TEX_FILTER_CUBIC | TEX_FILTER_DITHER_DIFFUSION }, + { L"FANT_DITHER_DIFFUSION", TEX_FILTER_FANT | TEX_FILTER_DITHER_DIFFUSION }, + { L"BOX_DITHER_DIFFUSION", TEX_FILTER_BOX | TEX_FILTER_DITHER_DIFFUSION }, + { L"TRIANGLE_DITHER_DIFFUSION", TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER_DIFFUSION }, + { nullptr, TEX_FILTER_DEFAULT } + }; #define CODEC_DDS 0xFFFF0001 #define CODEC_TGA 0xFFFF0002 @@ -270,25 +272,34 @@ const SValue g_pFilters[] = #define CODEC_EXR 0xFFFF0006 #endif -const SValue g_pExtFileTypes[] = -{ - { L".BMP", WIC_CODEC_BMP }, - { 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 }, - { 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 } -}; + const SValue g_pExtFileTypes[] = + { + { L".BMP", WIC_CODEC_BMP }, + { 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 }, + { 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 } + }; +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +HRESULT LoadAnimatedGif(const wchar_t* szFile, + std::vector>& loadedImages, + bool usebgcolor); ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -566,378 +577,8 @@ namespace return SaveToWICFile(img, WIC_FLAGS_NONE, GetWICCodec(static_cast(fileType)), szOutputFile); } } - - - enum - { - DM_UNDEFINED = 0, - DM_NONE = 1, - DM_BACKGROUND = 2, - DM_PREVIOUS = 3 - }; - - - void FillRectangle(const Image& img, const RECT& destRect, uint32_t color) - { - RECT clipped = - { - (destRect.left < 0) ? 0 : destRect.left, - (destRect.top < 0) ? 0 : destRect.top, - (destRect.right > static_cast(img.width)) ? static_cast(img.width) : destRect.right, - (destRect.bottom > static_cast(img.height)) ? static_cast(img.height) : destRect.bottom - }; - - auto ptr = reinterpret_cast(img.pixels + size_t(clipped.top) * img.rowPitch + size_t(clipped.left) * sizeof(uint32_t)); - - for (long y = clipped.top; y < clipped.bottom; ++y) - { - auto pixelPtr = reinterpret_cast(ptr); - for (long x = clipped.left; x < clipped.right; ++x) - { - *pixelPtr++ = color; - } - - ptr += img.rowPitch; - } - } - - - void BlendRectangle(const Image& composed, const Image& raw, const RECT& destRect, uint32_t transparent) - { - using namespace DirectX::PackedVector; - - RECT clipped = - { - (destRect.left < 0) ? 0 : destRect.left, - (destRect.top < 0) ? 0 : destRect.top, - (destRect.right > static_cast(composed.width)) ? static_cast(composed.width) : destRect.right, - (destRect.bottom > static_cast(composed.height)) ? static_cast(composed.height) : destRect.bottom - }; - - auto rawPtr = reinterpret_cast(raw.pixels); - auto composedPtr = reinterpret_cast(composed.pixels + size_t(clipped.top) * composed.rowPitch + size_t(clipped.left) * sizeof(uint32_t)); - - for (long y = clipped.top; y < clipped.bottom; ++y) - { - auto srcPtr = reinterpret_cast(rawPtr); - auto destPtr = reinterpret_cast(composedPtr); - for (long x = clipped.left; x < clipped.right; ++x, ++srcPtr, ++destPtr) - { - if (transparent == *srcPtr) - continue; - - *destPtr = *srcPtr; - } - - rawPtr += raw.rowPitch; - composedPtr += composed.rowPitch; - } - } - - - HRESULT LoadAnimatedGif(const wchar_t* szFile, std::vector>& loadedImages, bool usebgcolor) - { - // https://code.msdn.microsoft.com/windowsapps/Windows-Imaging-Component-65abbc6a/ - // http://www.imagemagick.org/Usage/anim_basics/#dispose - - bool iswic2; - auto pWIC = GetWICFactory(iswic2); - if (!pWIC) - return E_NOINTERFACE; - - ComPtr decoder; - HRESULT hr = pWIC->CreateDecoderFromFilename(szFile, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, decoder.GetAddressOf()); - if (FAILED(hr)) - return hr; - - { - GUID containerFormat; - hr = decoder->GetContainerFormat(&containerFormat); - if (FAILED(hr)) - return hr; - - if (memcmp(&containerFormat, &GUID_ContainerFormatGif, sizeof(GUID)) != 0) - { - // This function only works for GIF - return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); - } - } - - ComPtr metareader; - hr = decoder->GetMetadataQueryReader(metareader.GetAddressOf()); - if (FAILED(hr)) - return hr; - - PROPVARIANT propValue; - PropVariantInit(&propValue); - - // Get palette - WICColor rgbColors[256] = {}; - UINT actualColors = 0; - { - ComPtr palette; - hr = pWIC->CreatePalette(palette.GetAddressOf()); - if (FAILED(hr)) - return hr; - - hr = decoder->CopyPalette(palette.Get()); - if (FAILED(hr)) - return hr; - - hr = palette->GetColors(_countof(rgbColors), rgbColors, &actualColors); - if (FAILED(hr)) - return hr; - } - - // Get background color - UINT bgColor = 0; - if (usebgcolor) - { - // Most browsers just ignore the background color metadata and always use transparency - hr = metareader->GetMetadataByName(L"/logscrdesc/GlobalColorTableFlag", &propValue); - if (SUCCEEDED(hr)) - { - bool hasTable = (propValue.vt == VT_BOOL && propValue.boolVal); - PropVariantClear(&propValue); - - if (hasTable) - { - hr = metareader->GetMetadataByName(L"/logscrdesc/BackgroundColorIndex", &propValue); - if (SUCCEEDED(hr)) - { - if (propValue.vt == VT_UI1) - { - uint8_t index = propValue.bVal; - - if (index < actualColors) - { - bgColor = rgbColors[index]; - } - } - PropVariantClear(&propValue); - } - } - } - } - - // Get global frame size - UINT width = 0; - UINT height = 0; - - hr = metareader->GetMetadataByName(L"/logscrdesc/Width", &propValue); - if (FAILED(hr)) - return hr; - - if (propValue.vt != VT_UI2) - return E_FAIL; - - width = propValue.uiVal; - PropVariantClear(&propValue); - - hr = metareader->GetMetadataByName(L"/logscrdesc/Height", &propValue); - if (FAILED(hr)) - return hr; - - if (propValue.vt != VT_UI2) - return E_FAIL; - - height = propValue.uiVal; - PropVariantClear(&propValue); - - UINT fcount; - hr = decoder->GetFrameCount(&fcount); - if (FAILED(hr)) - return hr; - - UINT disposal = DM_UNDEFINED; - RECT rct = {}; - - UINT previousFrame = 0; - for (UINT iframe = 0; iframe < fcount; ++iframe) - { - int transparentIndex = -1; - - std::unique_ptr frameImage(new (std::nothrow) ScratchImage); - if (!frameImage) - return E_OUTOFMEMORY; - - if (disposal == DM_PREVIOUS) - { - hr = frameImage->InitializeFromImage(*loadedImages[previousFrame]->GetImage(0, 0, 0)); - } - else if (iframe > 0) - { - hr = frameImage->InitializeFromImage(*loadedImages[iframe - 1]->GetImage(0, 0, 0)); - } - else - { - hr = frameImage->Initialize2D(DXGI_FORMAT_B8G8R8A8_UNORM, width, height, 1, 1); - } - if (FAILED(hr)) - return hr; - - auto composedImage = frameImage->GetImage(0, 0, 0); - - if (!iframe) - { - RECT fullRct = { 0, 0, static_cast(width), static_cast(height) }; - FillRectangle(*composedImage, fullRct, bgColor); - } - else if (disposal == DM_BACKGROUND) - { - FillRectangle(*composedImage, rct, bgColor); - } - - ComPtr frame; - hr = decoder->GetFrame(iframe, frame.GetAddressOf()); - if (FAILED(hr)) - return hr; - - WICPixelFormatGUID pixelFormat; - hr = frame->GetPixelFormat(&pixelFormat); - if (FAILED(hr)) - return hr; - - if (memcmp(&pixelFormat, &GUID_WICPixelFormat8bppIndexed, sizeof(GUID)) != 0) - { - // GIF is always loaded as this format - return E_UNEXPECTED; - } - - ComPtr frameMeta; - hr = frame->GetMetadataQueryReader(frameMeta.GetAddressOf()); - if (SUCCEEDED(hr)) - { - hr = frameMeta->GetMetadataByName(L"/imgdesc/Left", &propValue); - if (SUCCEEDED(hr)) - { - hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - rct.left = static_cast(propValue.uiVal); - } - PropVariantClear(&propValue); - } - - hr = frameMeta->GetMetadataByName(L"/imgdesc/Top", &propValue); - if (SUCCEEDED(hr)) - { - hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - rct.top = static_cast(propValue.uiVal); - } - PropVariantClear(&propValue); - } - - hr = frameMeta->GetMetadataByName(L"/imgdesc/Width", &propValue); - if (SUCCEEDED(hr)) - { - hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - rct.right = static_cast(propValue.uiVal) + rct.left; - } - PropVariantClear(&propValue); - } - - hr = frameMeta->GetMetadataByName(L"/imgdesc/Height", &propValue); - if (SUCCEEDED(hr)) - { - hr = (propValue.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - rct.bottom = static_cast(propValue.uiVal) + rct.top; - } - PropVariantClear(&propValue); - } - - disposal = DM_UNDEFINED; - hr = frameMeta->GetMetadataByName(L"/grctlext/Disposal", &propValue); - if (SUCCEEDED(hr)) - { - hr = (propValue.vt == VT_UI1 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - disposal = propValue.bVal; - } - PropVariantClear(&propValue); - } - - hr = frameMeta->GetMetadataByName(L"/grctlext/TransparencyFlag", &propValue); - if (SUCCEEDED(hr)) - { - hr = (propValue.vt == VT_BOOL ? S_OK : E_FAIL); - if (SUCCEEDED(hr) && propValue.boolVal) - { - PropVariantClear(&propValue); - hr = frameMeta->GetMetadataByName(L"/grctlext/TransparentColorIndex", &propValue); - if (SUCCEEDED(hr)) - { - hr = (propValue.vt == VT_UI1 ? S_OK : E_FAIL); - if (SUCCEEDED(hr) && propValue.uiVal < actualColors) - { - transparentIndex = static_cast(propValue.uiVal); - } - } - } - PropVariantClear(&propValue); - } - - } - - UINT w, h; - hr = frame->GetSize(&w, &h); - if (FAILED(hr)) - return hr; - - ScratchImage rawFrame; - hr = rawFrame.Initialize2D(DXGI_FORMAT_B8G8R8A8_UNORM, w, h, 1, 1); - if (FAILED(hr)) - return hr; - - ComPtr FC; - hr = pWIC->CreateFormatConverter(FC.GetAddressOf()); - if (FAILED(hr)) - return hr; - - hr = FC->Initialize(frame.Get(), GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, nullptr, 0, WICBitmapPaletteTypeMedianCut); - if (FAILED(hr)) - return hr; - - auto img = rawFrame.GetImage(0, 0, 0); - - hr = FC->CopyPixels(nullptr, static_cast(img->rowPitch), static_cast(img->slicePitch), img->pixels); - if (FAILED(hr)) - return hr; - - if (!iframe || transparentIndex == -1) - { - Rect fullRect(0, 0, img->width, img->height); - hr = CopyRectangle(*img, fullRect, *composedImage, TEX_FILTER_DEFAULT, size_t(rct.left), size_t(rct.top)); - if (FAILED(hr)) - return hr; - } - else - { - BlendRectangle(*composedImage, *img, rct, rgbColors[transparentIndex]); - } - - if (disposal == DM_UNDEFINED || disposal == DM_NONE) - { - previousFrame = iframe; - } - - loadedImages.emplace_back(std::move(frameImage)); - } - - PropVariantClear(&propValue); - - return S_OK; - } } - //-------------------------------------------------------------------------------------- // Entry-point //--------------------------------------------------------------------------------------