diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2015-10-21 04:05:29 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2015-10-21 04:05:29 +0000 |
| commit | f2e065dee10fb110e9d51175ee6381333319ef56 (patch) | |
| tree | da85ca78fe050280d7592bf6bf1071410846031f /TextureLoader/src | |
| download | DiligentTools-f2e065dee10fb110e9d51175ee6381333319ef56.tar.gz DiligentTools-f2e065dee10fb110e9d51175ee6381333319ef56.zip | |
Release v1.0.0
Diffstat (limited to 'TextureLoader/src')
| -rw-r--r-- | TextureLoader/src/DDSLoader.cpp | 1489 | ||||
| -rw-r--r-- | TextureLoader/src/Image.cpp | 409 | ||||
| -rw-r--r-- | TextureLoader/src/TextureLoader.cpp | 248 | ||||
| -rw-r--r-- | TextureLoader/src/TextureUtilities.cpp | 95 | ||||
| -rw-r--r-- | TextureLoader/src/pch.cpp | 25 |
5 files changed, 2266 insertions, 0 deletions
diff --git a/TextureLoader/src/DDSLoader.cpp b/TextureLoader/src/DDSLoader.cpp new file mode 100644 index 0000000..2c701f3 --- /dev/null +++ b/TextureLoader/src/DDSLoader.cpp @@ -0,0 +1,1489 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +//-------------------------------------------------------------------------------------- +// File: DDSLoader.cpp +// +// Functions for loading a DDS texture and creating a Direct3D 11 runtime resource for it +// +// Note these functions are useful as a light-weight runtime loader for DDS files. For +// a full-featured DDS file reader, writer, and texture processing pipeline see +// the 'Texconv' sample and the 'DirectXTex' library. +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248926 +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#include "pch.h" +#include "dxgiformat.h" +#include <memory> +#include <algorithm> +#include "DDSLoader.h" + +#ifndef _In_ +# define _In_ +#endif + +#ifndef _Out_ +# define _Out_ +#endif + +#ifndef _Out_opt_ +# define _Out_opt_ +#endif + +#ifndef _Outptr_opt_ +# define _Outptr_opt_ +#endif + +using namespace Diligent; +using namespace Diligent; + +// D3D11 definitions + +enum D3D11_RESOURCE_DIMENSION +{ + D3D11_RESOURCE_DIMENSION_UNKNOWN = 0, + D3D11_RESOURCE_DIMENSION_BUFFER = 1, + D3D11_RESOURCE_DIMENSION_TEXTURE1D = 2, + D3D11_RESOURCE_DIMENSION_TEXTURE2D = 3, + D3D11_RESOURCE_DIMENSION_TEXTURE3D = 4 +}; + +enum D3D11_RESOURCE_MISC_FLAG +{ + D3D11_RESOURCE_MISC_TEXTURECUBE = 0x4L, +}; + +#ifndef D3D11_REQ_MIP_LEVELS +# define D3D11_REQ_MIP_LEVELS ( 15 ) +#endif + +#ifndef D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION +# define D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION ( 2048 ) +#endif + +#ifndef D3D11_REQ_TEXTURE1D_U_DIMENSION +# define D3D11_REQ_TEXTURE1D_U_DIMENSION ( 16384 ) +#endif + +#ifndef D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION +# define D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION ( 2048 ) +#endif + +#ifndef D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION +# define D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION ( 16384 ) +#endif + +#ifndef D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION +# define D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION ( 2048 ) +#endif + +#ifndef D3D11_REQ_TEXTURECUBE_DIMENSION +# define D3D11_REQ_TEXTURECUBE_DIMENSION ( 16384 ) +#endif + + +//-------------------------------------------------------------------------------------- +// Macros +//-------------------------------------------------------------------------------------- +#ifndef MAKEFOURCC + #define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((Uint32)(Uint8)(ch0) | ((Uint32)(Uint8)(ch1) << 8) | \ + ((Uint32)(Uint8)(ch2) << 16) | ((Uint32)(Uint8)(ch3) << 24)) +#endif /* defined(MAKEFOURCC) */ + +//-------------------------------------------------------------------------------------- +// DDS file structure definitions +// +// See DDS.h in the 'Texconv' sample and the 'DirectXTex' library +//-------------------------------------------------------------------------------------- +#pragma pack(push, 1) + +#define DDS_MAGIC 0x20534444 // "DDS " + +struct DDS_PIXELFORMAT +{ + Uint32 size; + Uint32 flags; + Uint32 fourCC; + Uint32 RGBBitCount; + Uint32 RBitMask; + Uint32 GBitMask; + Uint32 BBitMask; + Uint32 ABitMask; +}; + +#define DDS_FOURCC 0x00000004 // DDPF_FOURCC +#define DDS_RGB 0x00000040 // DDPF_RGB +#define DDS_RGBA 0x00000041 // DDPF_RGB | DDPF_ALPHAPIXELS +#define DDS_LUMINANCE 0x00020000 // DDPF_LUMINANCE +#define DDS_LUMINANCEA 0x00020001 // DDPF_LUMINANCE | DDPF_ALPHAPIXELS +#define DDS_ALPHA 0x00000002 // DDPF_ALPHA +#define DDS_PAL8 0x00000020 // DDPF_PALETTEINDEXED8 + +#define DDS_HEADER_FLAGS_TEXTURE 0x00001007 // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT +#define DDS_HEADER_FLAGS_MIPMAP 0x00020000 // DDSD_MIPMAPCOUNT +#define DDS_HEADER_FLAGS_VOLUME 0x00800000 // DDSD_DEPTH +#define DDS_HEADER_FLAGS_PITCH 0x00000008 // DDSD_PITCH +#define DDS_HEADER_FLAGS_LINEARSIZE 0x00080000 // DDSD_LINEARSIZE + +#define DDS_HEIGHT 0x00000002 // DDSD_HEIGHT +#define DDS_WIDTH 0x00000004 // DDSD_WIDTH + +#define DDS_SURFACE_FLAGS_TEXTURE 0x00001000 // DDSCAPS_TEXTURE +#define DDS_SURFACE_FLAGS_MIPMAP 0x00400008 // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP +#define DDS_SURFACE_FLAGS_CUBEMAP 0x00000008 // DDSCAPS_COMPLEX + +#define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX +#define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX +#define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY +#define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY +#define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ +#define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + +#define DDS_CUBEMAP_ALLFACES (DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |\ + DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |\ + DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ) + +#define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP + +#define DDS_FLAGS_VOLUME 0x00200000 // DDSCAPS2_VOLUME + +enum DDS_MISC_FLAGS2 +{ + DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7L, +}; + +enum DDS_ALPHA_MODE +{ + DDS_ALPHA_MODE_UNKNOWN = 0, + DDS_ALPHA_MODE_STRAIGHT = 1, + DDS_ALPHA_MODE_PREMULTIPLIED = 2, + DDS_ALPHA_MODE_OPAQUE = 3, + DDS_ALPHA_MODE_CUSTOM = 4, +}; + +typedef struct +{ + Uint32 size; + Uint32 flags; + Uint32 height; + Uint32 width; + Uint32 pitchOrLinearSize; + Uint32 depth; // only if DDS_HEADER_FLAGS_VOLUME is set in flags + Uint32 mipMapCount; + Uint32 reserved1[11]; + DDS_PIXELFORMAT ddspf; + Uint32 caps; + Uint32 caps2; + Uint32 caps3; + Uint32 caps4; + Uint32 reserved2; +} DDS_HEADER; + + +typedef struct +{ + DXGI_FORMAT dxgiFormat; + Uint32 resourceDimension; + Uint32 miscFlag; // see D3D11_RESOURCE_MISC_FLAG + Uint32 arraySize; + Uint32 miscFlags2; +} DDS_HEADER_DXT10; + +#pragma pack(pop) + + + +//-------------------------------------------------------------------------------------- +// Return the BPP for a particular format +//-------------------------------------------------------------------------------------- +static size_t BitsPerPixel(_In_ DXGI_FORMAT fmt) +{ + switch (fmt) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + return 128; + + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + return 96; + + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + return 64; + + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + return 32; + + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + case DXGI_FORMAT_B4G4R4A4_UNORM: + return 16; + + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + case DXGI_FORMAT_A8_UNORM: + return 8; + + case DXGI_FORMAT_R1_UNORM: + return 1; + + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + return 4; + + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + return 8; + + default: + return 0; + } +} + + +static TEXTURE_FORMAT DXGIFormatToTexFormat( DXGI_FORMAT TexFormat ) +{ + switch(TexFormat) + { + case DXGI_FORMAT_UNKNOWN: return TEX_FORMAT_UNKNOWN; + + case DXGI_FORMAT_R32G32B32A32_TYPELESS: return TEX_FORMAT_RGBA32_TYPELESS; + case DXGI_FORMAT_R32G32B32A32_FLOAT: return TEX_FORMAT_RGBA32_FLOAT; + case DXGI_FORMAT_R32G32B32A32_UINT: return TEX_FORMAT_RGBA32_UINT; + case DXGI_FORMAT_R32G32B32A32_SINT: return TEX_FORMAT_RGBA32_SINT; + + case DXGI_FORMAT_R32G32B32_TYPELESS: return TEX_FORMAT_RGB32_TYPELESS; + case DXGI_FORMAT_R32G32B32_FLOAT: return TEX_FORMAT_RGB32_FLOAT; + case DXGI_FORMAT_R32G32B32_UINT: return TEX_FORMAT_RGB32_UINT; + case DXGI_FORMAT_R32G32B32_SINT: return TEX_FORMAT_RGB32_SINT; + + case DXGI_FORMAT_R16G16B16A16_TYPELESS: return TEX_FORMAT_RGBA16_TYPELESS; + case DXGI_FORMAT_R16G16B16A16_FLOAT: return TEX_FORMAT_RGBA16_FLOAT; + case DXGI_FORMAT_R16G16B16A16_UNORM: return TEX_FORMAT_RGBA16_UNORM; + case DXGI_FORMAT_R16G16B16A16_UINT: return TEX_FORMAT_RGBA16_UINT; + case DXGI_FORMAT_R16G16B16A16_SNORM: return TEX_FORMAT_RGBA16_SNORM; + case DXGI_FORMAT_R16G16B16A16_SINT: return TEX_FORMAT_RGBA16_SINT; + + case DXGI_FORMAT_R32G32_TYPELESS: return TEX_FORMAT_RG32_TYPELESS; + case DXGI_FORMAT_R32G32_FLOAT: return TEX_FORMAT_RG32_FLOAT; + case DXGI_FORMAT_R32G32_UINT: return TEX_FORMAT_RG32_UINT; + case DXGI_FORMAT_R32G32_SINT: return TEX_FORMAT_RG32_SINT; + + case DXGI_FORMAT_R32G8X24_TYPELESS: return TEX_FORMAT_R32G8X24_TYPELESS; + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: return TEX_FORMAT_D32_FLOAT_S8X24_UINT; + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: return TEX_FORMAT_R32_FLOAT_X8X24_TYPELESS; + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: return TEX_FORMAT_X32_TYPELESS_G8X24_UINT; + + case DXGI_FORMAT_R10G10B10A2_TYPELESS: return TEX_FORMAT_RGB10A2_TYPELESS; + case DXGI_FORMAT_R10G10B10A2_UNORM: return TEX_FORMAT_RGB10A2_UNORM; + case DXGI_FORMAT_R10G10B10A2_UINT: return TEX_FORMAT_RGB10A2_UINT; + + case DXGI_FORMAT_R11G11B10_FLOAT: return TEX_FORMAT_R11G11B10_FLOAT; + + case DXGI_FORMAT_R8G8B8A8_TYPELESS: return TEX_FORMAT_RGBA8_TYPELESS; + case DXGI_FORMAT_R8G8B8A8_UNORM: return TEX_FORMAT_RGBA8_UNORM; + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: return TEX_FORMAT_RGBA8_UNORM_SRGB; + case DXGI_FORMAT_R8G8B8A8_UINT: return TEX_FORMAT_RGBA8_UINT; + case DXGI_FORMAT_R8G8B8A8_SNORM: return TEX_FORMAT_RGBA8_SNORM; + case DXGI_FORMAT_R8G8B8A8_SINT: return TEX_FORMAT_RGBA8_SINT; + + case DXGI_FORMAT_R16G16_TYPELESS: return TEX_FORMAT_RG16_TYPELESS; + case DXGI_FORMAT_R16G16_FLOAT: return TEX_FORMAT_RG16_FLOAT; + case DXGI_FORMAT_R16G16_UNORM: return TEX_FORMAT_RG16_UNORM; + case DXGI_FORMAT_R16G16_UINT: return TEX_FORMAT_RG16_UINT; + case DXGI_FORMAT_R16G16_SNORM: return TEX_FORMAT_RG16_SNORM; + case DXGI_FORMAT_R16G16_SINT: return TEX_FORMAT_RG16_SINT; + + case DXGI_FORMAT_R32_TYPELESS: return TEX_FORMAT_R32_TYPELESS; + case DXGI_FORMAT_D32_FLOAT: return TEX_FORMAT_D32_FLOAT; + case DXGI_FORMAT_R32_FLOAT: return TEX_FORMAT_R32_FLOAT; + case DXGI_FORMAT_R32_UINT: return TEX_FORMAT_R32_UINT; + case DXGI_FORMAT_R32_SINT: return TEX_FORMAT_R32_SINT; + + case DXGI_FORMAT_R24G8_TYPELESS: return TEX_FORMAT_R24G8_TYPELESS; + case DXGI_FORMAT_D24_UNORM_S8_UINT: return TEX_FORMAT_D24_UNORM_S8_UINT; + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: return TEX_FORMAT_R24_UNORM_X8_TYPELESS; + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: return TEX_FORMAT_X24_TYPELESS_G8_UINT; + + case DXGI_FORMAT_R8G8_TYPELESS: return TEX_FORMAT_RG8_TYPELESS; + case DXGI_FORMAT_R8G8_UNORM: return TEX_FORMAT_RG8_UNORM; + case DXGI_FORMAT_R8G8_UINT: return TEX_FORMAT_RG8_UINT; + case DXGI_FORMAT_R8G8_SNORM: return TEX_FORMAT_RG8_SNORM; + case DXGI_FORMAT_R8G8_SINT: return TEX_FORMAT_RG8_SINT; + + case DXGI_FORMAT_R16_TYPELESS: return TEX_FORMAT_R16_TYPELESS; + case DXGI_FORMAT_R16_FLOAT: return TEX_FORMAT_R16_FLOAT; + case DXGI_FORMAT_D16_UNORM: return TEX_FORMAT_D16_UNORM; + case DXGI_FORMAT_R16_UNORM: return TEX_FORMAT_R16_UNORM; + case DXGI_FORMAT_R16_UINT: return TEX_FORMAT_R16_UINT; + case DXGI_FORMAT_R16_SNORM: return TEX_FORMAT_R16_SNORM; + case DXGI_FORMAT_R16_SINT: return TEX_FORMAT_R16_SINT; + + case DXGI_FORMAT_R8_TYPELESS: return TEX_FORMAT_R8_TYPELESS; + case DXGI_FORMAT_R8_UNORM: return TEX_FORMAT_R8_UNORM; + case DXGI_FORMAT_R8_UINT: return TEX_FORMAT_R8_UINT; + case DXGI_FORMAT_R8_SNORM: return TEX_FORMAT_R8_SNORM; + case DXGI_FORMAT_R8_SINT: return TEX_FORMAT_R8_SINT; + case DXGI_FORMAT_A8_UNORM: return TEX_FORMAT_A8_UNORM; + + case DXGI_FORMAT_R1_UNORM : return TEX_FORMAT_R1_UNORM; + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: return TEX_FORMAT_RGB9E5_SHAREDEXP; + case DXGI_FORMAT_R8G8_B8G8_UNORM: return TEX_FORMAT_RG8_B8G8_UNORM; + case DXGI_FORMAT_G8R8_G8B8_UNORM: return TEX_FORMAT_G8R8_G8B8_UNORM; + + case DXGI_FORMAT_BC1_TYPELESS: return TEX_FORMAT_BC1_TYPELESS; + case DXGI_FORMAT_BC1_UNORM: return TEX_FORMAT_BC1_UNORM; + case DXGI_FORMAT_BC1_UNORM_SRGB: return TEX_FORMAT_BC1_UNORM_SRGB; + case DXGI_FORMAT_BC2_TYPELESS: return TEX_FORMAT_BC2_TYPELESS; + case DXGI_FORMAT_BC2_UNORM: return TEX_FORMAT_BC2_UNORM; + case DXGI_FORMAT_BC2_UNORM_SRGB: return TEX_FORMAT_BC2_UNORM_SRGB; + case DXGI_FORMAT_BC3_TYPELESS: return TEX_FORMAT_BC3_TYPELESS; + case DXGI_FORMAT_BC3_UNORM: return TEX_FORMAT_BC3_UNORM; + case DXGI_FORMAT_BC3_UNORM_SRGB: return TEX_FORMAT_BC3_UNORM_SRGB; + case DXGI_FORMAT_BC4_TYPELESS: return TEX_FORMAT_BC4_TYPELESS; + case DXGI_FORMAT_BC4_UNORM: return TEX_FORMAT_BC4_UNORM; + case DXGI_FORMAT_BC4_SNORM: return TEX_FORMAT_BC4_SNORM; + case DXGI_FORMAT_BC5_TYPELESS: return TEX_FORMAT_BC5_TYPELESS; + case DXGI_FORMAT_BC5_UNORM: return TEX_FORMAT_BC5_UNORM; + case DXGI_FORMAT_BC5_SNORM: return TEX_FORMAT_BC5_SNORM; + + case DXGI_FORMAT_B5G6R5_UNORM: return TEX_FORMAT_B5G6R5_UNORM; + case DXGI_FORMAT_B5G5R5A1_UNORM: return TEX_FORMAT_B5G5R5A1_UNORM; + case DXGI_FORMAT_B8G8R8A8_UNORM: return TEX_FORMAT_BGRA8_UNORM; + case DXGI_FORMAT_B8G8R8X8_UNORM: return TEX_FORMAT_BGRX8_UNORM; + + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: return TEX_FORMAT_R10G10B10_XR_BIAS_A2_UNORM; + + case DXGI_FORMAT_B8G8R8A8_TYPELESS: return TEX_FORMAT_BGRA8_TYPELESS; + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: return TEX_FORMAT_BGRA8_UNORM_SRGB; + case DXGI_FORMAT_B8G8R8X8_TYPELESS: return TEX_FORMAT_BGRX8_TYPELESS; + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: return TEX_FORMAT_BGRX8_UNORM_SRGB; + + case DXGI_FORMAT_BC6H_TYPELESS: return TEX_FORMAT_BC6H_TYPELESS; + case DXGI_FORMAT_BC6H_UF16: return TEX_FORMAT_BC6H_UF16; + case DXGI_FORMAT_BC6H_SF16: return TEX_FORMAT_BC6H_SF16; + case DXGI_FORMAT_BC7_TYPELESS : return TEX_FORMAT_BC7_TYPELESS; + case DXGI_FORMAT_BC7_UNORM: return TEX_FORMAT_BC7_UNORM; + case DXGI_FORMAT_BC7_UNORM_SRGB: return TEX_FORMAT_BC7_UNORM_SRGB; + + default: UNEXPECTED( "Unsupported DXGI formate" ); return TEX_FORMAT_UNKNOWN; + } +} + +//-------------------------------------------------------------------------------------- +// Get surface information for a particular format +//-------------------------------------------------------------------------------------- +static void GetSurfaceInfo( + _In_ size_t width, + _In_ size_t height, + _In_ DXGI_FORMAT fmt, + _Out_opt_ size_t* outNumBytes, + _Out_opt_ size_t* outRowBytes, + _Out_opt_ size_t* outNumRows + ) +{ + size_t numBytes = 0; + size_t rowBytes = 0; + size_t numRows = 0; + + bool bc = false; + bool packed = false; + size_t bcnumBytesPerBlock = 0; + switch (fmt) + { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + bc = true; + bcnumBytesPerBlock = 8; + break; + + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + bc = true; + bcnumBytesPerBlock = 16; + break; + + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + packed = true; + break; + } + + if (bc) + { + size_t numBlocksWide = 0; + if (width > 0) + { + numBlocksWide = std::max<size_t>(1, (width + 3) / 4); + } + size_t numBlocksHigh = 0; + if (height > 0) + { + numBlocksHigh = std::max<size_t>(1, (height + 3) / 4); + } + rowBytes = numBlocksWide * bcnumBytesPerBlock; + numRows = numBlocksHigh; + } + else if (packed) + { + rowBytes = ((width + 1) >> 1) * 4; + numRows = height; + } + else + { + size_t bpp = BitsPerPixel(fmt); + rowBytes = (width * bpp + 7) / 8; // round up to nearest byte + numRows = height; + } + + numBytes = rowBytes * numRows; + if (outNumBytes) + { + *outNumBytes = numBytes; + } + if (outRowBytes) + { + *outRowBytes = rowBytes; + } + if (outNumRows) + { + *outNumRows = numRows; + } +} + + +//-------------------------------------------------------------------------------------- +#define ISBITMASK(r, g, b, a) (ddpf.RBitMask == r && ddpf.GBitMask == g && ddpf.BBitMask == b && ddpf.ABitMask == a) + +static DXGI_FORMAT GetDXGIFormat(const DDS_PIXELFORMAT& ddpf) +{ + if (ddpf.flags & DDS_RGB) + { + // Note that sRGB formats are written using the "DX10" extended header + + switch (ddpf.RGBBitCount) + { + case 32: + if (ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) + { + return DXGI_FORMAT_R8G8B8A8_UNORM; + } + + if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000)) + { + return DXGI_FORMAT_B8G8R8A8_UNORM; + } + + if (ISBITMASK(0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000)) + { + return DXGI_FORMAT_B8G8R8X8_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000) aka D3DFMT_X8B8G8R8 + + // Note that many common DDS reader/writers (including D3DX) swap the + // the RED/BLUE masks for 10:10:10:2 formats. We assumme + // below that the 'backwards' header mask is being used since it is most + // likely written by D3DX. The more robust solution is to use the 'DX10' + // header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly + + // For 'correct' writers, this should be 0x000003ff, 0x000ffc00, 0x3ff00000 for RGB data + if (ISBITMASK(0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000)) + { + return DXGI_FORMAT_R10G10B10A2_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000) aka D3DFMT_A2R10G10B10 + + if (ISBITMASK(0x0000ffff, 0xffff0000, 0x00000000, 0x00000000)) + { + return DXGI_FORMAT_R16G16_UNORM; + } + + if (ISBITMASK(0xffffffff, 0x00000000, 0x00000000, 0x00000000)) + { + // Only 32-bit color channel format in D3D9 was R32F + return DXGI_FORMAT_R32_FLOAT; // D3DX writes this out as a FourCC of 114 + } + break; + + case 24: + // No 24bpp DXGI formats aka D3DFMT_R8G8B8 + break; + + case 16: + if (ISBITMASK(0x7c00, 0x03e0, 0x001f, 0x8000)) + { + return DXGI_FORMAT_B5G5R5A1_UNORM; + } + if (ISBITMASK(0xf800, 0x07e0, 0x001f, 0x0000)) + { + return DXGI_FORMAT_B5G6R5_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x7c00, 0x03e0, 0x001f, 0x0000) aka D3DFMT_X1R5G5B5 + if (ISBITMASK(0x0f00, 0x00f0, 0x000f, 0xf000)) + { + return DXGI_FORMAT_B4G4R4A4_UNORM; + } + + // No DXGI format maps to ISBITMASK(0x0f00, 0x00f0, 0x000f, 0x0000) aka D3DFMT_X4R4G4B4 + + // No 3:3:2, 3:3:2:8, or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_R3G3B2, D3DFMT_P8, D3DFMT_A8P8, etc. + break; + } + } + else if (ddpf.flags & DDS_LUMINANCE) + { + if (8 == ddpf.RGBBitCount) + { + if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x00000000)) + { + return DXGI_FORMAT_R8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + + // No DXGI format maps to ISBITMASK(0x0f, 0x00, 0x00, 0xf0) aka D3DFMT_A4L4 + } + + if (16 == ddpf.RGBBitCount) + { + if (ISBITMASK(0x0000ffff, 0x00000000, 0x00000000, 0x00000000)) + { + return DXGI_FORMAT_R16_UNORM; // D3DX10/11 writes this out as DX10 extension + } + if (ISBITMASK(0x000000ff, 0x00000000, 0x00000000, 0x0000ff00)) + { + return DXGI_FORMAT_R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension + } + } + } + else if (ddpf.flags & DDS_ALPHA) + { + if (8 == ddpf.RGBBitCount) + { + return DXGI_FORMAT_A8_UNORM; + } + } + else if (ddpf.flags & DDS_FOURCC) + { + if (MAKEFOURCC('D', 'X', 'T', '1') == ddpf.fourCC) + { + return DXGI_FORMAT_BC1_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '3') == ddpf.fourCC) + { + return DXGI_FORMAT_BC2_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '5') == ddpf.fourCC) + { + return DXGI_FORMAT_BC3_UNORM; + } + + // While pre-mulitplied alpha isn't directly supported by the DXGI formats, + // they are basically the same as these BC formats so they can be mapped + if (MAKEFOURCC('D', 'X', 'T', '2') == ddpf.fourCC) + { + return DXGI_FORMAT_BC2_UNORM; + } + if (MAKEFOURCC('D', 'X', 'T', '4') == ddpf.fourCC) + { + return DXGI_FORMAT_BC3_UNORM; + } + + if (MAKEFOURCC('A', 'T', 'I', '1') == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_UNORM; + } + if (MAKEFOURCC('B', 'C', '4', 'U') == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_UNORM; + } + if (MAKEFOURCC('B', 'C', '4', 'S') == ddpf.fourCC) + { + return DXGI_FORMAT_BC4_SNORM; + } + + if (MAKEFOURCC('A', 'T', 'I', '2') == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_UNORM; + } + if (MAKEFOURCC('B', 'C', '5', 'U') == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_UNORM; + } + if (MAKEFOURCC('B', 'C', '5', 'S') == ddpf.fourCC) + { + return DXGI_FORMAT_BC5_SNORM; + } + + // BC6H and BC7 are written using the "DX10" extended header + + if (MAKEFOURCC('R', 'G', 'B', 'G') == ddpf.fourCC) + { + return DXGI_FORMAT_R8G8_B8G8_UNORM; + } + if (MAKEFOURCC('G', 'R', 'G', 'B') == ddpf.fourCC) + { + return DXGI_FORMAT_G8R8_G8B8_UNORM; + } + + // Check for D3DFORMAT enums being set here + switch (ddpf.fourCC) + { + case 36: // D3DFMT_A16B16G16R16 + return DXGI_FORMAT_R16G16B16A16_UNORM; + + case 110: // D3DFMT_Q16W16V16U16 + return DXGI_FORMAT_R16G16B16A16_SNORM; + + case 111: // D3DFMT_R16F + return DXGI_FORMAT_R16_FLOAT; + + case 112: // D3DFMT_G16R16F + return DXGI_FORMAT_R16G16_FLOAT; + + case 113: // D3DFMT_A16B16G16R16F + return DXGI_FORMAT_R16G16B16A16_FLOAT; + + case 114: // D3DFMT_R32F + return DXGI_FORMAT_R32_FLOAT; + + case 115: // D3DFMT_G32R32F + return DXGI_FORMAT_R32G32_FLOAT; + + case 116: // D3DFMT_A32B32G32R32F + return DXGI_FORMAT_R32G32B32A32_FLOAT; + } + } + + return DXGI_FORMAT_UNKNOWN; +} + + +//-------------------------------------------------------------------------------------- +static DXGI_FORMAT MakeSRGB(_In_ DXGI_FORMAT format) +{ + switch (format) + { + case DXGI_FORMAT_R8G8B8A8_UNORM: + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + + case DXGI_FORMAT_BC1_UNORM: + return DXGI_FORMAT_BC1_UNORM_SRGB; + + case DXGI_FORMAT_BC2_UNORM: + return DXGI_FORMAT_BC2_UNORM_SRGB; + + case DXGI_FORMAT_BC3_UNORM: + return DXGI_FORMAT_BC3_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8A8_UNORM: + return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + + case DXGI_FORMAT_B8G8R8X8_UNORM: + return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; + + case DXGI_FORMAT_BC7_UNORM: + return DXGI_FORMAT_BC7_UNORM_SRGB; + + default: + return format; + } +} + + +//-------------------------------------------------------------------------------------- +static void FillInitData( + _In_ size_t width, + _In_ size_t height, + _In_ size_t depth, + _In_ size_t mipCount, + _In_ size_t arraySize, + _In_ DXGI_FORMAT format, + _In_ size_t maxsize, + _In_ size_t bitSize, + _In_ const Uint8* bitData, + _Out_ size_t& twidth, + _Out_ size_t& theight, + _Out_ size_t& tdepth, + _Out_ size_t& skipMip, + _Out_ TextureSubResData *initData + ) +{ + if (!bitData || !initData) + { + LOG_ERROR_AND_THROW( "Invalid arguments" ); + } + + skipMip = 0; + twidth = 0; + theight = 0; + tdepth = 0; + + size_t NumBytes = 0; + size_t RowBytes = 0; + size_t NumRows = 0; + const Uint8* pSrcBits = bitData; + const Uint8* pEndBits = bitData + bitSize; + + size_t index = 0; + for (size_t j = 0; j < arraySize; j++) + { + size_t w = width; + size_t h = height; + size_t d = depth; + for (size_t i = 0; i < mipCount; i++) + { + GetSurfaceInfo(w, h, format, &NumBytes, &RowBytes, &NumRows); + + if ((mipCount <= 1) || !maxsize || (w <= maxsize && h <= maxsize && d <= maxsize)) + { + if (!twidth) + { + twidth = w; + theight = h; + tdepth = d; + } + + VERIFY_EXPR(index < mipCount * arraySize); + initData[index].pData = (const void*)pSrcBits; + initData[index].Stride = static_cast<Uint32>(RowBytes); + initData[index].DepthStride = static_cast<Uint32>(NumBytes); + ++index; + } + else if (!j) + { + // Count number of skipped mipmaps (first item only) + ++skipMip; + } + + if (pSrcBits + (NumBytes*d) > pEndBits) + { + LOG_ERROR_AND_THROW( "Out of bounds" ); + } + + pSrcBits += NumBytes * d; + + w = w >> 1; + h = h >> 1; + d = d >> 1; + if (w == 0) + { + w = 1; + } + if (h == 0) + { + h = 1; + } + if (d == 0) + { + d = 1; + } + } + } + + if (!index) + { + LOG_ERROR_AND_THROW( "Unknown error" ); + } +} + + +//-------------------------------------------------------------------------------------- +static void CreateTexture( + _In_ IRenderDevice* pDevice, + _In_ Uint32 resDim, + _In_ size_t width, + _In_ size_t height, + _In_ size_t depth, + _In_ size_t mipCount, + _In_ size_t arraySize, + _In_ DXGI_FORMAT format, + _In_ USAGE usage, + _In_ unsigned int bindFlags, + _In_ unsigned int cpuAccessFlags, + _In_ unsigned int miscFlags, + _In_ bool forceSRGB, + _In_ bool isCubeMap, + _In_ TextureSubResData* initData, + _Outptr_opt_ ITexture** texture + ) +{ + if (!pDevice || !initData) + { + LOG_ERROR_AND_THROW("Invalid arguments") + } + + + if (forceSRGB) + { + format = MakeSRGB(format); + } + + TextureDesc desc; + desc.Width = static_cast<Uint32>(width); + desc.MipLevels = static_cast<Uint32>(mipCount); + desc.ArraySize = static_cast<Uint32>(arraySize); + desc.Format = DXGIFormatToTexFormat(format); + desc.Usage = usage; + desc.BindFlags = bindFlags; + desc.CPUAccessFlags = cpuAccessFlags; + //desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE; + + TextureData InitData; + InitData.pSubResources = initData; + InitData.NumSubresources = static_cast<Uint32>(mipCount * arraySize); + + switch (resDim) + { + case D3D11_RESOURCE_DIMENSION_TEXTURE1D: + { + desc.Type = arraySize > 1 ? TEXTURE_TYPE_1D_ARRAY : TEXTURE_TYPE_1D; + pDevice->CreateTexture( desc, InitData, texture ); + + // ID3D11Texture1D* tex = nullptr; + // hr = d3dDevice->CreateTexture1D(&desc, initData, &tex); + + // if (SUCCEEDED(hr) && tex != 0) + // { + // if (textureView != 0) + // { + // D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; + // memset(&SRVDesc, 0, sizeof(SRVDesc)); + // SRVDesc.Format = format; + + // if (arraySize > 1) + // { + // SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE1DARRAY; + // SRVDesc.Texture1DArray.MipLevels = desc.MipLevels; + // SRVDesc.Texture1DArray.ArraySize = static_cast<UINT>(arraySize); + // } + // else + // { + // SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE1D; + // SRVDesc.Texture1D.MipLevels = desc.MipLevels; + // } + + // hr = d3dDevice->CreateShaderResourceView(tex, &SRVDesc, textureView); + + // if (FAILED(hr)) + // { + // tex->Release(); + // return hr; + // } + // } + + // if (texture != 0) + // { + // *texture = tex; + // } + // else + // { + // tex->Release(); + // } + // } + } + break; + + case TEXTURE_TYPE_2D: + case TEXTURE_TYPE_2D_ARRAY: + case TEXTURE_TYPE_CUBE: + case TEXTURE_TYPE_CUBE_ARRAY: + { + desc.Type = isCubeMap ? + (arraySize > 1 ? TEXTURE_TYPE_CUBE_ARRAY : TEXTURE_TYPE_CUBE) : + (arraySize > 1 ? TEXTURE_TYPE_2D_ARRAY : TEXTURE_TYPE_2D); + desc.Height = static_cast<Uint32>(height); + + //if (isCubeMap) + //{ + // desc.MiscFlags = miscFlags | D3D11_RESOURCE_MISC_TEXTURECUBE; + //} + //else + //{ + // desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE; + //} + + pDevice->CreateTexture( desc, InitData, texture ); + + //ID3D11Texture2D* tex = nullptr; + //hr = d3dDevice->CreateTexture2D(&desc, initData, &tex); + + //if (SUCCEEDED(hr) && tex != 0) + //{ + // if (textureView != 0) + // { + // D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; + // memset(&SRVDesc, 0, sizeof(SRVDesc)); + // SRVDesc.Format = format; + + // if (isCubeMap) + // { + // if (arraySize > 6) + // { + // SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURECUBEARRAY; + // SRVDesc.TextureCubeArray.MipLevels = desc.MipLevels; + + // // Earlier we set arraySize to (NumCubes * 6) + // SRVDesc.TextureCubeArray.NumCubes = static_cast<UINT>(arraySize / 6); + // } + // else + // { + // SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURECUBE; + // SRVDesc.TextureCube.MipLevels = desc.MipLevels; + // } + // } + // else if (arraySize > 1) + // { + // SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2DARRAY; + // SRVDesc.Texture2DArray.MipLevels = desc.MipLevels; + // SRVDesc.Texture2DArray.ArraySize = static_cast<UINT>(arraySize); + // } + // else + // { + // SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2D; + // SRVDesc.Texture2D.MipLevels = desc.MipLevels; + // } + + // hr = d3dDevice->CreateShaderResourceView(tex, &SRVDesc, textureView); + + // if (FAILED(hr)) + // { + // tex->Release(); + // return hr; + // } + // } + + // if (texture != 0) + // { + // *texture = tex; + // } + // else + // { + // tex->Release(); + // } + //} + } + break; + + case TEXTURE_TYPE_3D: + { + desc.Type = TEXTURE_TYPE_3D; + desc.Height = static_cast<Uint32>(height); + desc.Depth = static_cast<Uint32>(depth); + + //desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE; + + pDevice->CreateTexture( desc, InitData, texture ); + + //ID3D11Texture3D* tex = nullptr; + //hr = d3dDevice->CreateTexture3D(&desc, initData, &tex); + + //if (SUCCEEDED(hr) && tex != 0) + //{ + // if (textureView != 0) + // { + // D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc; + // memset(&SRVDesc, 0, sizeof(SRVDesc)); + // SRVDesc.Format = format; + // SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE3D; + // SRVDesc.Texture3D.MipLevels = desc.MipLevels; + + // hr = d3dDevice->CreateShaderResourceView(tex, &SRVDesc, textureView); + + // if (FAILED(hr)) + // { + // tex->Release(); + // return hr; + // } + // } + + // if (texture != 0) + // { + // *texture = tex; + // } + // else + // { + // tex->Release(); + // } + //} + } + break; + } +} + +//-------------------------------------------------------------------------------------- +static void CreateTextureFromDDS( + _In_ IRenderDevice* pDevice, + _In_ const DDS_HEADER* header, + _In_ const Uint8* bitData, + _In_ size_t bitSize, + _In_ size_t maxsize, + _In_ USAGE usage, + _In_ unsigned int bindFlags, + _In_ unsigned int cpuAccessFlags, + _In_ unsigned int miscFlags, + _In_ bool forceSRGB, + _Outptr_opt_ ITexture** texture + ) +{ + size_t width = header->width; + size_t height = header->height; + size_t depth = header->depth; + + Uint32 resDim = D3D11_RESOURCE_DIMENSION_UNKNOWN; + size_t arraySize = 1; + DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; + bool isCubeMap = false; + + size_t mipCount = header->mipMapCount; + if (0 == mipCount) + { + mipCount = 1; + } + + + if ((header->ddspf.flags & DDS_FOURCC) && + (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC)) + { + auto d3d10ext = reinterpret_cast<const DDS_HEADER_DXT10*>((const char*)header + sizeof(DDS_HEADER)); + + arraySize = d3d10ext->arraySize; + if (arraySize == 0) + { + LOG_ERROR_AND_THROW("Array size is zero"); + } + + if (BitsPerPixel(d3d10ext->dxgiFormat) == 0) + { + LOG_ERROR_AND_THROW("Undefined DXGI format"); + } + + format = d3d10ext->dxgiFormat; + + switch (d3d10ext->resourceDimension) + { + case D3D11_RESOURCE_DIMENSION_TEXTURE1D: + // D3DX writes 1D textures with a fixed Height of 1 + if ((header->flags & DDS_HEIGHT) && height != 1) + { + LOG_ERROR_AND_THROW("Unexpected height (", height, ") for texture 1D"); + } + height = depth = 1; + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE2D: + if (d3d10ext->miscFlag & D3D11_RESOURCE_MISC_TEXTURECUBE) + { + arraySize *= 6; + isCubeMap = true; + } + depth = 1; + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE3D: + if (!(header->flags & DDS_HEADER_FLAGS_VOLUME)) + { + LOG_ERROR_AND_THROW("DDS_HEADER_FLAGS_VOLUME flag is not set"); + } + + if (arraySize > 1) + { + LOG_ERROR_AND_THROW("Texture3D arrays are not allowed"); + } + break; + + default: + LOG_ERROR_AND_THROW("Unknown resource dimension"); + } + + resDim = d3d10ext->resourceDimension; + } + else + { + format = GetDXGIFormat(header->ddspf); + + if (format == DXGI_FORMAT_UNKNOWN) + { + LOG_ERROR_AND_THROW("Unknown DXGIF format"); + } + + if (header->flags & DDS_HEADER_FLAGS_VOLUME) + { + resDim = D3D11_RESOURCE_DIMENSION_TEXTURE3D; + } + else + { + if (header->caps2 & DDS_CUBEMAP) + { + // We require all six faces to be defined + if ((header->caps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES) + { + LOG_ERROR_AND_THROW("All six faces of a cubemap must be defined"); + } + + arraySize = 6; + isCubeMap = true; + } + + depth = 1; + resDim = D3D11_RESOURCE_DIMENSION_TEXTURE2D; + + // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture + } + + VERIFY_EXPR(BitsPerPixel(format) != 0); + } + + // Bound sizes (for security purposes we don't trust DDS file metadata larger than the D3D 11.x hardware requirements) + if (mipCount > D3D11_REQ_MIP_LEVELS) + { + LOG_ERROR_AND_THROW("Too many mip levels specified"); + } + + switch (resDim) + { + case D3D11_RESOURCE_DIMENSION_TEXTURE1D: + if ((arraySize > D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || + (width > D3D11_REQ_TEXTURE1D_U_DIMENSION)) + { + LOG_ERROR_AND_THROW("Texture1D dimensions are out of bounds"); + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE2D: + if (isCubeMap) + { + // This is the right bound because we set arraySize to (NumCubes*6) above + if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || + (width > D3D11_REQ_TEXTURECUBE_DIMENSION) || + (height > D3D11_REQ_TEXTURECUBE_DIMENSION)) + { + LOG_ERROR_AND_THROW("TextureCube dimensions are out of bounds"); + } + } + else if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || + (width > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION) || + (height > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION)) + { + LOG_ERROR_AND_THROW("Texture2D dimensions are out of bounds"); + } + break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE3D: + if ((arraySize > 1) || + (width > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || + (height > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || + (depth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION)) + { + LOG_ERROR_AND_THROW("Texture3D dimensions are out of bounds"); + } + break; + } + // Create the texture + std::unique_ptr<TextureSubResData[]> initData(new TextureSubResData[mipCount * arraySize]); + + size_t skipMip = 0; + size_t twidth = 0; + size_t theight = 0; + size_t tdepth = 0; + FillInitData(width, height, depth, mipCount, arraySize, format, maxsize, bitSize, bitData, twidth, theight, tdepth, skipMip, initData.get()); + + CreateTexture(pDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, format, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB, isCubeMap, initData.get(), texture/*, textureView*/); + +#if 0 + if (FAILED(hr) && !maxsize && (mipCount > 1)) + { + // Retry with a maxsize determined by feature level + switch (d3dDevice->GetFeatureLevel()) + { + case D3D_FEATURE_LEVEL_9_1: + case D3D_FEATURE_LEVEL_9_2: + if (isCubeMap) + { + maxsize = D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION; + } + else + { + maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) + ? D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION + : D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION; + } + break; + + case D3D_FEATURE_LEVEL_9_3: + maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) + ? D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION + : D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION; + break; + + default: // D3D_FEATURE_LEVEL_10_0 & D3D_FEATURE_LEVEL_10_1 + maxsize = (resDim == D3D11_RESOURCE_DIMENSION_TEXTURE3D) + ? D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION + : D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION; + break; + } + + FillInitData(width, height, depth, mipCount, arraySize, format, maxsize, bitSize, bitData, twidth, theight, tdepth, skipMip, initData.get()); + + hr = CreateTexture(d3dDevice, resDim, twidth, theight, tdepth, mipCount - skipMip, arraySize, format, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB, isCubeMap, initData.get(), texture, textureView); + } +#endif +} + +#if 0 +//-------------------------------------------------------------------------------------- +static D2D1_ALPHA_MODE GetAlphaMode(_In_ const DDS_HEADER* header) +{ + if (header->ddspf.flags & DDS_FOURCC) + { + if (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC) + { + auto d3d10ext = reinterpret_cast<const DDS_HEADER_DXT10*>((const char*)header + sizeof(DDS_HEADER)); + switch (d3d10ext->miscFlags2 & DDS_MISC_FLAGS2_ALPHA_MODE_MASK) + { + case DDS_ALPHA_MODE_STRAIGHT: + return D2D1_ALPHA_MODE_STRAIGHT; + + case DDS_ALPHA_MODE_PREMULTIPLIED: + return D2D1_ALPHA_MODE_PREMULTIPLIED; + + case DDS_ALPHA_MODE_OPAQUE: + case DDS_ALPHA_MODE_CUSTOM: + // No D2D1_ALPHA_MODE equivalent, so return "Ignore" for now + return D2D1_ALPHA_MODE_IGNORE; + } + } + else if ((MAKEFOURCC('D', 'X', 'T', '2') == header->ddspf.fourCC) + || (MAKEFOURCC('D', 'X', 'T', '4') == header->ddspf.fourCC)) + { + return D2D1_ALPHA_MODE_PREMULTIPLIED; + } + // DXT1, DXT3, and DXT5 legacy files could be straight alpha or something else, so return "Unknown" to leave it up to the app + } + + return D2D1_ALPHA_MODE_UNKNOWN; +} + +#endif + +//-------------------------------------------------------------------------------------- +void CreateDDSTextureFromMemory( + IRenderDevice* pDevice, + const Uint8* ddsData, + size_t ddsDataSize, + ITexture** texture, + size_t maxsize/*, + D2D1_ALPHA_MODE* alphaMode*/ + ) +{ + return CreateDDSTextureFromMemoryEx(pDevice, ddsData, ddsDataSize, maxsize, USAGE_DEFAULT, BIND_SHADER_RESOURCE, 0, 0, false, texture/*, alphaMode*/); +} + + +//-------------------------------------------------------------------------------------- +void CreateDDSTextureFromMemoryEx( + IRenderDevice* pDevice, + const Uint8* ddsData, + size_t ddsDataSize, + size_t maxsize, + USAGE usage, + unsigned int bindFlags, + unsigned int cpuAccessFlags, + unsigned int miscFlags, + bool forceSRGB, + ITexture** texture/*, + D2D1_ALPHA_MODE* alphaMode*/ + ) +{ + if (texture) + { + VERIFY( *texture == nullptr, "Overwriting reference to an existing object is unsafe") + *texture = nullptr; + } + //if (textureView) + //{ + // *textureView = nullptr; + //} + //if (alphaMode) + //{ + // *alphaMode = D2D1_ALPHA_MODE_UNKNOWN; + //} + + if (!pDevice || !ddsData || (!texture /*&& !textureView*/)) + { + LOG_ERROR_AND_THROW("Invalid arguments"); + } + + // Validate DDS file in memory + if (ddsDataSize < (sizeof(Uint32) + sizeof(DDS_HEADER))) + { + LOG_ERROR_AND_THROW("Invalid dds file"); + } + + Uint32 dwMagicNumber = *(const Uint32*)(ddsData); + if (dwMagicNumber != DDS_MAGIC) + { + LOG_ERROR_AND_THROW("Invalid dds file"); + } + + auto header = reinterpret_cast<const DDS_HEADER*>(ddsData + sizeof(Uint32)); + + // Verify header to validate DDS file + if (header->size != sizeof(DDS_HEADER) || + header->ddspf.size != sizeof(DDS_PIXELFORMAT)) + { + LOG_ERROR_AND_THROW("Invalid dds file header"); + } + + // Check for DX10 extension + bool bDXT10Header = false; + if ((header->ddspf.flags & DDS_FOURCC) && + (MAKEFOURCC('D', 'X', '1', '0') == header->ddspf.fourCC)) + { + // Must be long enough for both headers and magic value + if (ddsDataSize < (sizeof(DDS_HEADER) + sizeof(Uint32) + sizeof(DDS_HEADER_DXT10))) + { + LOG_ERROR_AND_THROW("Invalid DX10 extension"); + } + + bDXT10Header = true; + } + + ptrdiff_t offset = sizeof(Uint32) + sizeof(DDS_HEADER) + (bDXT10Header ? sizeof(DDS_HEADER_DXT10) : 0); + + CreateTextureFromDDS(pDevice, header, ddsData + offset, ddsDataSize - offset, maxsize, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB, texture/*, textureView*/); + + //if (alphaMode) + // *alphaMode = GetAlphaMode(header); +} diff --git a/TextureLoader/src/Image.cpp b/TextureLoader/src/Image.cpp new file mode 100644 index 0000000..d78f2da --- /dev/null +++ b/TextureLoader/src/Image.cpp @@ -0,0 +1,409 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include "Image.h" +#include "Errors.h" + +#include "tiffio.h" +#include "png.h" +#include "jpeglib.h" + +#include "DataBlobImpl.h" +#include "DebugUtilities.h" +#include "RefCntAutoPtr.h" + +using namespace Diligent; +using namespace Diligent; + +namespace Diligent +{ + class TIFFClientOpenWrapper + { + public: + TIFFClientOpenWrapper( IDataBlob *pData ) : + m_pData( pData ), + m_Offset( 0 ), + m_Size( pData->GetSize() ) + { + } + + static tmsize_t TIFFReadProc( thandle_t pClientData, void* pBuffer, tmsize_t Size ) + { + auto *pThis = reinterpret_cast<TIFFClientOpenWrapper*>(pClientData); + auto *pSrcPtr = reinterpret_cast<Uint8*>(pThis->m_pData->GetDataPtr()) + pThis->m_Offset; + memcpy( pBuffer, pSrcPtr, Size ); + pThis->m_Offset += Size; + return Size; + } + + static tmsize_t TIFFWriteProc( thandle_t pClientData, void* pBuffer, tmsize_t Size ) + { + auto *pThis = reinterpret_cast<TIFFClientOpenWrapper*>(pClientData); + if( pThis->m_Offset + Size > pThis->m_Size ) + { + pThis->m_Size = pThis->m_Offset + Size; + pThis->m_pData->Resize( pThis->m_Size ); + } + auto *pDstPtr = reinterpret_cast<Uint8*>(pThis->m_pData->GetDataPtr()) + pThis->m_Offset; + memcpy( pDstPtr, pBuffer, Size ); + pThis->m_Offset += Size; + return Size; + } + + static toff_t TIFFSeekProc( thandle_t pClientData, toff_t Offset, int Whence ) + { + auto *pThis = reinterpret_cast<TIFFClientOpenWrapper*>(pClientData); + switch( Whence ) + { + case SEEK_SET: pThis->m_Offset = static_cast<size_t>(Offset); break; + case SEEK_CUR: pThis->m_Offset += static_cast<size_t>(Offset); break; + case SEEK_END: pThis->m_Offset = pThis->m_Size + static_cast<size_t>(Offset); break; + default: UNEXPECTED( "Unexpected whence" ); + } + + return pThis->m_Offset; + } + + static int TIFFCloseProc( thandle_t pClientData ) + { + auto *pThis = reinterpret_cast<TIFFClientOpenWrapper*>(pClientData); + pThis->m_pData.Release(); + pThis->m_Size = 0; + pThis->m_Offset = 0; + return 0; + } + + static toff_t TIFFSizeProc( thandle_t pClientData ) + { + auto *pThis = reinterpret_cast<TIFFClientOpenWrapper*>(pClientData); + return pThis->m_Size; + } + + static int TIFFMapFileProc( thandle_t pClientData, void** base, toff_t* size ) + { + UNEXPECTED( "Client file mapping is not implemented. Use \'m\' when opening TIFF file to disable file mapping." ) + return 0; + } + + static void TIFFUnmapFileProc( thandle_t pClientData, void* base, toff_t size ) + { + UNEXPECTED( "Client file mapping is not implemented. Use \'m\' when opening TIFF file to disable file mapping." ) + } + + private: + size_t m_Offset; + size_t m_Size; + RefCntAutoPtr<IDataBlob> m_pData; + }; + + void Image::LoadTiffFile( IDataBlob *pFileData, const ImageLoadInfo& LoadInfo ) + { + TIFFClientOpenWrapper TiffClientOpenWrpr(pFileData); + + auto TiffFile = TIFFClientOpen("", "rm", &TiffClientOpenWrpr, + TIFFClientOpenWrapper::TIFFReadProc, + TIFFClientOpenWrapper::TIFFWriteProc, + TIFFClientOpenWrapper::TIFFSeekProc, + TIFFClientOpenWrapper::TIFFCloseProc, + TIFFClientOpenWrapper::TIFFSizeProc, + TIFFClientOpenWrapper::TIFFMapFileProc, + TIFFClientOpenWrapper::TIFFUnmapFileProc); + + TIFFGetField(TiffFile, TIFFTAG_IMAGEWIDTH, &m_Desc.Width); + TIFFGetField(TiffFile, TIFFTAG_IMAGELENGTH, &m_Desc.Height); + + Uint16 SamplesPerPixel = 0; + // SamplesPerPixel is usually 1 for bilevel, grayscale, and palette-color images. + // SamplesPerPixel is usually 3 for RGB images. If this value is higher, ExtraSamples + // should give an indication of the meaning of the additional channels. + TIFFGetField(TiffFile, TIFFTAG_SAMPLESPERPIXEL, &SamplesPerPixel); + m_Desc.NumComponents = SamplesPerPixel; + + Uint16 BitsPerSample = 0; + TIFFGetField(TiffFile, TIFFTAG_BITSPERSAMPLE, &BitsPerSample); + m_Desc.BitsPerPixel = m_Desc.NumComponents * BitsPerSample; + + auto ScanlineSize = TIFFScanlineSize(TiffFile); + m_Desc.RowStride = static_cast<Uint32>( ScanlineSize ); + m_pData->Resize(m_Desc.Height * m_Desc.RowStride ); + auto *pDataPtr = reinterpret_cast<Uint8*>( m_pData->GetDataPtr() ); + for (Uint32 row = 0; row < m_Desc.Height; row++, pDataPtr += ScanlineSize) + { + TIFFReadScanline(TiffFile, pDataPtr, row); + } + TIFFClose(TiffFile); + } + + + class PNGReadFnHelper + { + public: + PNGReadFnHelper( IDataBlob *pData ) : + m_pData( pData ), + m_Offset( 0 ) + { + } + + static void ReadData( png_structp pngPtr, png_bytep data, png_size_t length ) + { + auto pThis = reinterpret_cast<PNGReadFnHelper*>( png_get_io_ptr(pngPtr) ); + memcpy( data, reinterpret_cast<Uint8*>(pThis->m_pData->GetDataPtr()) + pThis->m_Offset, length ); + pThis->m_Offset += length; + } + + private: + RefCntAutoPtr<IDataBlob> m_pData; + size_t m_Offset; + }; + + void Image::LoadPngFile( IDataBlob *pFileData, const ImageLoadInfo& LoadInfo ) + { + // http://www.piko3d.net/tutorials/libpng-tutorial-loading-png-files-from-streams/ + // http://www.libpng.org/pub/png/book/chapter13.html#png.ch13.div.10 + // https://gist.github.com/niw/5963798\ + + PNGReadFnHelper ReadFnHelper(pFileData); + + const size_t PngSigSize = 8; + png_const_bytep pngsig = reinterpret_cast<png_const_bytep>(pFileData->GetDataPtr()); + //Let LibPNG check the signature. If this function returns 0, everything is OK. + if( png_sig_cmp( pngsig, 0, PngSigSize ) != 0 ) + { + LOG_ERROR_AND_THROW( "Invalid png signature" ); + } + + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + VERIFY(png, "png_create_read_struct() failed") + + png_infop info = png_create_info_struct(png); + VERIFY(info, "png_create_info_struct() failed") + + if( setjmp( png_jmpbuf( png ) ) ) + { + // When an error occurs during parsing, libPNG will jump to here + png_destroy_read_struct(&png, &info, (png_infopp)0); + LOG_ERROR_AND_THROW( "Failed to read png file" ); + } + + png_set_read_fn(png, (png_voidp)&ReadFnHelper, PNGReadFnHelper::ReadData); + + png_read_info(png, info); + + m_Desc.Width = png_get_image_width(png, info); + m_Desc.Height = png_get_image_height(png, info); + m_Desc.NumComponents = png_get_channels(png, info); + auto bit_depth = png_get_bit_depth(png, info); + m_Desc.BitsPerPixel = bit_depth * m_Desc.NumComponents; + auto color_type = png_get_color_type(png, info); + + // PNG files store 16-bit pixels in network byte order (big-endian, ie + // most significant bytes first). png_set_swap() shall switch the byte-order + // to little-endian (ie, least significant bits first). + if( bit_depth == 16 ) + png_set_swap(png); + +#if 0 + // Read any color_type into 8bit depth, RGBA format. + // See http://www.libpng.org/pub/png/libpng-manual.txt + + if( bit_depth == 16 ) + png_set_strip_16( png ); + + if( color_type == PNG_COLOR_TYPE_PALETTE ) + png_set_palette_to_rgb( png ); + + // PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. + if( color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8 ) + png_set_expand_gray_1_2_4_to_8( png ); + + if( png_get_valid( png, info, PNG_INFO_tRNS ) ) + png_set_tRNS_to_alpha( png ); + + // These color_type don't have an alpha channel then fill it with 0xff. + if( color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_PALETTE ) + png_set_filler( png, 0xFF, PNG_FILLER_AFTER ); + + if( color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA ) + png_set_gray_to_rgb( png ); + + png_read_update_info( png, info ); +#endif + + //Array of row pointers. One for every row. + std::vector<png_bytep> rowPtrs(m_Desc.Height); + + //Alocate a buffer with enough space. + m_Desc.RowStride = m_Desc.Width * bit_depth * m_Desc.NumComponents / 8; + m_pData->Resize( m_Desc.Height * m_Desc.RowStride ); + for( size_t i = 0; i < m_Desc.Height; i++ ) + rowPtrs[i] = reinterpret_cast<png_bytep>(m_pData->GetDataPtr()) + i * m_Desc.RowStride; + + //Read the imagedata and write it to the adresses pointed to + //by rowptrs (in other words: our image databuffer) + png_read_image( png, rowPtrs.data() ); + + png_destroy_read_struct(&png, &info, (png_infopp)0); + } + + + + struct my_jpeg_error_mgr { + jpeg_error_mgr pub; + jmp_buf setjmp_buffer;// for return to caller + }; + + // Here's the routine that will replace the standard error_exit method: + METHODDEF(void) + my_error_exit (j_common_ptr cinfo) + { + // cinfo->err really points to a my_jpeg_error_mgr struct, so coerce pointer + my_jpeg_error_mgr* myerr = (my_jpeg_error_mgr*) cinfo->err; + + /* Always display the message. */ + /* We could postpone this until after returning, if we chose. */ + (*cinfo->err->output_message) (cinfo); + + // Return control to the setjmp point + longjmp(myerr->setjmp_buffer, 1); + } + + void Image::LoadJpegFile( IDataBlob *pFileData, const ImageLoadInfo& LoadInfo ) + { + // https://github.com/LuaDist/libjpeg/blob/master/example.c + + // This struct contains the JPEG decompression parameters and pointers to + // working space (which is allocated as needed by the JPEG library). + jpeg_decompress_struct cinfo; + + // We use our private extension JPEG error handler. + // Note that this struct must live as long as the main JPEG parameter + // struct, to avoid dangling-pointer problems. + my_jpeg_error_mgr jerr; + + // Step 1: allocate and initialize JPEG decompression object + + // We set up the normal JPEG error routines, then override error_exit. + cinfo.err = jpeg_std_error( &jerr.pub ); + jerr.pub.error_exit = my_error_exit; + // Establish the setjmp return context for my_error_exit to use. + if( setjmp( jerr.setjmp_buffer ) ) + { + // If we get here, the JPEG code has signaled an error. + // We need to clean up the JPEG object, close the input file, and return. + jpeg_destroy_decompress( &cinfo ); + LOG_ERROR_AND_THROW( "Failed to decompress JPEG image" ) + } + // Now we can initialize the JPEG decompression object. + jpeg_create_decompress( &cinfo ); + + // Step 2: specify data source + jpeg_mem_src( &cinfo, reinterpret_cast<unsigned char*>(pFileData->GetDataPtr()), static_cast<unsigned long>(pFileData->GetSize()) ); + + // Step 3: read file parameters with jpeg_read_header() + jpeg_read_header( &cinfo, TRUE ); + // We can ignore the return value from jpeg_read_header since + // (a) suspension is not possible with the stdio data source, and + // (b) we passed TRUE to reject a tables-only JPEG file as an error. + // See libjpeg.txt for more info. + + + // Step 4: set parameters for decompression + + // In this example, we don't need to change any of the defaults set by + // jpeg_read_header(), so we do nothing here. + + + // Step 5: Start decompressor + + jpeg_start_decompress( &cinfo ); + // We can ignore the return value since suspension is not possible + // with the stdio data source. + + // We may need to do some setup of our own at this point before reading + // the data. After jpeg_start_decompress() we have the correct scaled + // output image dimensions available, as well as the output colormap + // if we asked for color quantization. + + m_Desc.Width = cinfo.output_width; + m_Desc.Height = cinfo.output_height; + m_Desc.NumComponents = cinfo.output_components; + m_Desc.RowStride = m_Desc.Width * m_Desc.NumComponents; + m_Desc.BitsPerPixel = 8 * m_Desc.NumComponents; + + m_pData->Resize(m_Desc.RowStride * m_Desc.Height); + // Step 6: while (scan lines remain to be read) + // jpeg_read_scanlines(...); + + // Here we use the library's state variable cinfo.output_scanline as the + // loop counter, so that we don't have to keep track ourselves. + while( cinfo.output_scanline < cinfo.output_height ) { + // jpeg_read_scanlines expects an array of pointers to scanlines. + // Here the array is only one element long, but you could ask for + // more than one scanline at a time if that's more convenient. + + + auto *pDstScanline = reinterpret_cast<Uint8*>( m_pData->GetDataPtr() ) + cinfo.output_scanline * m_Desc.RowStride; + JSAMPROW RowPtrs[] = { reinterpret_cast<JSAMPROW>(pDstScanline) }; + jpeg_read_scanlines( &cinfo, RowPtrs, 1 ); + } + + // Step 7: Finish decompression + + jpeg_finish_decompress( &cinfo ); + // We can ignore the return value since suspension is not possible + // with the stdio data source. + + // Step 8: Release JPEG decompression object + + // This is an important step since it will release a good deal of memory. + jpeg_destroy_decompress( &cinfo ); + + // At this point you may want to check to see whether any corrupt-data + // warnings occurred (test whether jerr.pub.num_warnings is nonzero). + } + + Image::Image( Diligent::IFileStream *pSrcFile, + const ImageLoadInfo& LoadInfo ) : + m_pData( new Diligent::DataBlobImpl ) + { + RefCntAutoPtr<IDataBlob> pFileData( new Diligent::DataBlobImpl ); + pSrcFile->Read(pFileData); + + if( LoadInfo.Format == EImageFileFormat::tiff ) + { + LoadTiffFile(pFileData, LoadInfo ); + } + else if( LoadInfo.Format == EImageFileFormat::png ) + { + LoadPngFile(pFileData, LoadInfo ); + } + else if( LoadInfo.Format == EImageFileFormat::jpeg ) + { + LoadJpegFile(pFileData, LoadInfo ); + } + } +} diff --git a/TextureLoader/src/TextureLoader.cpp b/TextureLoader/src/TextureLoader.cpp new file mode 100644 index 0000000..63134a4 --- /dev/null +++ b/TextureLoader/src/TextureLoader.cpp @@ -0,0 +1,248 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include "TextureLoader.h" +#include "GraphicsUtilities.h" +#include <algorithm> +#include <limits> +#include "DDSLoader.h" + +using namespace Diligent; + +namespace Diligent +{ + static const float a = 0.055f; + + // https://en.wikipedia.org/wiki/SRGB + float SRGBToLinear(float SRGB) + { + if( SRGB < 0.04045f ) + return SRGB / 12.92f; + else + return powf( (SRGB + a) / (1 + a), 2.4f ); + } + + float LinearToSRGB( float c ) + { + if( c < 0.0031308f ) + return 12.92f * c; + else + return (1+a) * pow(c, 1.f/2.4f) - a; + } + + template<typename ChannelType> + ChannelType SRGBAverage(ChannelType c0, ChannelType c1, ChannelType c2, ChannelType c3) + { + const float NormVal = static_cast<float>(std::numeric_limits<ChannelType>::max()); + float fc0 = static_cast<float>(c0) / NormVal; + float fc1 = static_cast<float>(c1) / NormVal; + float fc2 = static_cast<float>(c2) / NormVal; + float fc3 = static_cast<float>(c3) / NormVal; + + float fLinearAverage = (SRGBToLinear( fc0 ) + SRGBToLinear( fc1 ) + SRGBToLinear( fc2 ) + SRGBToLinear( fc3 )) / 4.f; + float fSRGBAverage = LinearToSRGB(fLinearAverage); + Int32 SRGBAverage = static_cast<Int32>(fSRGBAverage * NormVal); + SRGBAverage = std::min(SRGBAverage, static_cast<Int32>( std::numeric_limits<ChannelType>::max()) ); + SRGBAverage = std::max(SRGBAverage, static_cast<Int32>( std::numeric_limits<ChannelType>::min()) ); + return static_cast<ChannelType>(SRGBAverage); + } + + template < typename ChannelType > + void ComputeCoarseMip(Uint32 NumChannels, bool IsSRGB, + const void *pFineMip, Uint32 FineMipStride, + void *pCoarseMip, Uint32 CoarseMipStride, + Uint32 CoarseMipWidth, Uint32 CoarseMipHeight) + { + for( Uint32 row = 0; row < CoarseMipHeight; ++row ) + for( Uint32 col = 0; col < CoarseMipWidth; ++col ) + { + auto FineRow0 = reinterpret_cast<const ChannelType*>( reinterpret_cast<const Uint8*>(pFineMip) + row * 2 * FineMipStride ); + auto FineRow1 = reinterpret_cast<const ChannelType*>( reinterpret_cast<const Uint8*>(pFineMip) + (row * 2 + 1 ) * FineMipStride ); + + for( Uint32 c = 0; c < NumChannels; ++c ) + { + auto Col00 = FineRow0[ col*2 * NumChannels + c ]; + auto Col01 = FineRow0[ (col*2+1) * NumChannels + c ]; + auto Col10 = FineRow1[ col*2 * NumChannels + c ]; + auto Col11 = FineRow1[ (col*2+1) * NumChannels + c ]; + auto &DstCol = reinterpret_cast<ChannelType*>(reinterpret_cast<Uint8*>(pCoarseMip) + row * CoarseMipStride)[col * NumChannels + c]; + if( IsSRGB ) + DstCol = SRGBAverage( Col00, Col01, Col10, Col11 ); + else + DstCol = (Col00 + Col01 + Col10 + Col11)/4; + } + } + } + + template < typename ChannelType > + void RGBToRGBA(const void *pRGBData, Uint32 RGBStride, + void *pRGBAData, Uint32 RGBAStride, + Uint32 Width, Uint32 Height) + { + for( Uint32 row = 0; row < Height; ++row ) + for( Uint32 col = 0; col < Width; ++col ) + { + for( int c = 0; c < 3; ++c ) + { + reinterpret_cast<ChannelType*>( (reinterpret_cast<Uint8*>(pRGBAData) + RGBAStride * row) ) [col *4 + c] = + reinterpret_cast<const ChannelType*>( (reinterpret_cast<const Uint8*>(pRGBData) + RGBStride * row))[col*3 + c]; + } + reinterpret_cast<ChannelType*>( (reinterpret_cast<Uint8*>(pRGBAData) + RGBAStride * row) ) [col *4 + 3] = std::numeric_limits<ChannelType>::max(); + } + } + + void CreateTextureFromImage( Image *pSrcImage, + const TextureLoadInfo& TexLoadInfo, + Diligent::IRenderDevice *pDevice, + Diligent::ITexture **ppTexture ) + { + const auto& ImgDesc = pSrcImage->GetDesc(); + TextureDesc TexDesc; + TexDesc.Type = TEXTURE_TYPE_2D; + TexDesc.Width = ImgDesc.Width; + TexDesc.Height = ImgDesc.Height; + TexDesc.MipLevels = ComputeMipLevelsCount( TexDesc.Width, TexDesc.Height ); + if( TexLoadInfo.MipLevels > 0 ) + TexDesc.MipLevels = std::min(TexDesc.MipLevels, TexLoadInfo.MipLevels); + TexDesc.Usage = TexLoadInfo.Usage; + TexDesc.BindFlags = TexLoadInfo.BindFlags; + TexDesc.Format = TexLoadInfo.Format; + TexDesc.CPUAccessFlags = TexLoadInfo.CPUAccessFlags; + auto ChannelDepth = ImgDesc.BitsPerPixel / ImgDesc.NumComponents; + + Uint32 NumComponents = ImgDesc.NumComponents == 3 ? 4 : ImgDesc.NumComponents; + bool IsSRGB = (ImgDesc.NumComponents >= 3 && ChannelDepth == 8) ? TexLoadInfo.IsSRGB : false; + if( TexDesc.Format == TEX_FORMAT_UNKNOWN ) + { + if( ChannelDepth == 8 ) + { + switch( NumComponents ) + { + case 1: TexDesc.Format = TEX_FORMAT_R8_UNORM; break; + case 2: TexDesc.Format = TEX_FORMAT_RG8_UNORM; break; + case 4: TexDesc.Format = IsSRGB ? TEX_FORMAT_RGBA8_UNORM_SRGB : TEX_FORMAT_RGBA8_UNORM; break; + default: LOG_ERROR_AND_THROW( "Unexpected number of color channels (", ImgDesc.NumComponents, ")" ); + } + } + else if( ChannelDepth == 16 ) + { + switch( NumComponents ) + { + case 1: TexDesc.Format = TEX_FORMAT_R16_UNORM; break; + case 2: TexDesc.Format = TEX_FORMAT_RG16_UNORM; break; + case 4: TexDesc.Format = TEX_FORMAT_RGBA16_UNORM; break; + default: LOG_ERROR_AND_THROW( "Unexpected number of color channels (", ImgDesc.NumComponents, ")" ); + } + } + else + LOG_ERROR_AND_THROW( "Unsupported color channel depth (", ChannelDepth, ")" ); + } + else + { + const auto& TexFmtDesc = GetTextureFormatAttribs( TexDesc.Format ); + if( TexFmtDesc.NumComponents != NumComponents ) + LOG_ERROR_AND_THROW( "Incorrect number of components ", ImgDesc.NumComponents, ") for texture format ", TexFmtDesc.Name ); + if( TexFmtDesc.ComponentSize != ChannelDepth / 8 ) + LOG_ERROR_AND_THROW( "Incorrect channel size ", ChannelDepth, ") for texture format ", TexFmtDesc.Name ); + } + + + std::vector<TextureSubResData> pSubResources(TexDesc.MipLevels); + std::vector< std::vector<Uint8> > Mips(TexDesc.MipLevels); + + if( ImgDesc.NumComponents == 3 ) + { + VERIFY_EXPR( NumComponents == 4 ); + auto RGBAStride = ImgDesc.Width * NumComponents * ChannelDepth / 8; + RGBAStride = (RGBAStride + 3) & (-4); + Mips[0].resize(RGBAStride * ImgDesc.Height); + pSubResources[0].pData = Mips[0].data(); + pSubResources[0].Stride = RGBAStride; + if( ChannelDepth == 8 ) + RGBToRGBA<Uint8>( pSrcImage->GetData()->GetDataPtr(), ImgDesc.RowStride, + Mips[0].data(), RGBAStride, + ImgDesc.Width, ImgDesc.Height); + else if( ChannelDepth == 16 ) + RGBToRGBA<Uint16>( pSrcImage->GetData()->GetDataPtr(), ImgDesc.RowStride, + Mips[0].data(), RGBAStride, + ImgDesc.Width, ImgDesc.Height); + } + else + { + pSubResources[0].pData = pSrcImage->GetData()->GetDataPtr(); + pSubResources[0].Stride = ImgDesc.RowStride; + } + + auto MipWidth = TexDesc.Width; + auto MipHeight = TexDesc.Height; + for( Uint32 m = 1; m < TexDesc.MipLevels; ++m ) + { + auto CoarseMipWidth = std::max(MipWidth/2u, 1u); + auto CoarseMipHeight = std::max(MipHeight/2u, 1u); + auto CoarseMipStride = CoarseMipWidth * NumComponents * ChannelDepth / 8; + CoarseMipStride = (CoarseMipStride + 3) & (-4); + Mips[m].resize(CoarseMipStride * CoarseMipHeight); + + if( ChannelDepth == 8 ) + ComputeCoarseMip<Uint8>( NumComponents, IsSRGB, + pSubResources[m-1].pData, pSubResources[m-1].Stride, + Mips[m].data(), CoarseMipStride, + CoarseMipWidth, CoarseMipHeight); + else if( ChannelDepth == 16 ) + ComputeCoarseMip<Uint16>( NumComponents, IsSRGB, + pSubResources[m-1].pData, pSubResources[m-1].Stride, + Mips[m].data(), CoarseMipStride, + CoarseMipWidth, CoarseMipHeight); + + pSubResources[m].pData = Mips[m].data(); + pSubResources[m].Stride = CoarseMipStride; + + MipWidth = CoarseMipWidth; + MipHeight = CoarseMipHeight; + } + + TextureData TexData; + TexData.pSubResources = pSubResources.data(); + TexData.NumSubresources = TexDesc.MipLevels; + + pDevice->CreateTexture( TexDesc, TexData, ppTexture ); + } + + void CreateTextureFromDDS( IDataBlob *pDDSData, + const TextureLoadInfo& TexLoadInfo, + Diligent::IRenderDevice *pDevice, + Diligent::ITexture **ppTexture ) + { + CreateDDSTextureFromMemoryEx(pDevice, + reinterpret_cast<const Uint8*>(pDDSData->GetDataPtr()), + static_cast<size_t>(pDDSData->GetSize()), + 0, // maxSize + TexLoadInfo.Usage, + TexLoadInfo.BindFlags, + TexLoadInfo.CPUAccessFlags, + 0, // miscFlags + false, // forceSRGB + ppTexture ); + } +} diff --git a/TextureLoader/src/TextureUtilities.cpp b/TextureLoader/src/TextureUtilities.cpp new file mode 100644 index 0000000..2ee94ba --- /dev/null +++ b/TextureLoader/src/TextureUtilities.cpp @@ -0,0 +1,95 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include "TextureUtilities.h" +#include "Errors.h" +#include "TextureLoader.h" +#include "Image.h" +#include "BasicFileStream.h" +#include "RefCntAutoPtr.h" +#include "DataBlobImpl.h" +#include <algorithm> + +using namespace Diligent; + +namespace Diligent +{ + +void CreateImageFromFile( const Diligent::Char *FilePath, + Image **ppImage, + IDataBlob **ppDDSData) +{ + auto *pDotPos = strrchr( FilePath, '.' ); + if( pDotPos == nullptr ) + LOG_ERROR_AND_THROW( "File path ", FilePath, " does not contain extension" ); + + auto *pExtension = pDotPos + 1; + if( *pExtension == 0 ) + LOG_ERROR_AND_THROW( "File path ", FilePath, " contains empty extension" ); + + String Extension(pExtension); + std::transform( Extension.begin(), Extension.end(), Extension.begin(), ::tolower ); + + Diligent::RefCntAutoPtr<BasicFileStream> pFileStream( new BasicFileStream( FilePath, EFileAccessMode::Read ) ); + + if( Extension == "dds" ) + { + VERIFY_EXPR(ppDDSData != nullptr); + *ppDDSData = new DataBlobImpl; + pFileStream->Read(*ppDDSData); + (*ppDDSData)->AddRef(); + } + else + { + ImageLoadInfo ImgLoadInfo; + if( Extension == "png" ) + ImgLoadInfo.Format = EImageFileFormat::png; + else if( Extension == "jpeg" || Extension == "jpg" ) + ImgLoadInfo.Format = EImageFileFormat::jpeg; + else if( Extension == "tiff" || Extension == "tif" ) + ImgLoadInfo.Format = EImageFileFormat::tiff; + else + LOG_ERROR_AND_THROW( "Unsupported file format ", Extension ); + + *ppImage = new Image(pFileStream, ImgLoadInfo); + (*ppImage)->AddRef(); + } +} + +void CreateTextureFromFile( const Diligent::Char *FilePath, + const TextureLoadInfo& TexLoadInfo, + IRenderDevice *pDevice, + ITexture **ppTexture ) +{ + RefCntAutoPtr<Image> pImage; + RefCntAutoPtr<IDataBlob> pDDSData; + CreateImageFromFile( FilePath, &pImage, &pDDSData ); + + if( pImage ) + CreateTextureFromImage( pImage, TexLoadInfo, pDevice, ppTexture ); + else if(pDDSData) + CreateTextureFromDDS( pDDSData, TexLoadInfo, pDevice, ppTexture ); +} + +} diff --git a/TextureLoader/src/pch.cpp b/TextureLoader/src/pch.cpp new file mode 100644 index 0000000..4a893f2 --- /dev/null +++ b/TextureLoader/src/pch.cpp @@ -0,0 +1,25 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + |
