192 lines
5.5 KiB
C++
192 lines
5.5 KiB
C++
//--------------------------------------------------------------------------------------
|
|
// File: ExtendedBMP.cpp
|
|
//
|
|
// Utilities for reading BMP files including the DXTn unofficial "FS70"
|
|
// extension created for Microsoft flight simulators.
|
|
//
|
|
// http://www.mwgfx.co.uk/programs/dxtbmp.htm
|
|
//
|
|
// 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 NOMCX
|
|
#define NOSERVICE
|
|
#define NOHELP
|
|
#pragma warning(pop)
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <cstdint>
|
|
#include <memory>
|
|
|
|
#include "DirectXTex.h"
|
|
|
|
using namespace DirectX;
|
|
|
|
namespace
|
|
{
|
|
struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } };
|
|
|
|
using ScopedHandle = std::unique_ptr<void, handle_closer>;
|
|
|
|
inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
|
|
|
|
HRESULT ReadData(_In_z_ const wchar_t* szFile, std::unique_ptr<uint8_t[]>& blob, size_t& blobSize)
|
|
{
|
|
blob.reset();
|
|
|
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
|
ScopedHandle hFile(safe_handle(CreateFile2(szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr)));
|
|
#else
|
|
ScopedHandle hFile(safe_handle(CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN, nullptr)));
|
|
#endif
|
|
if (!hFile)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
// Get the file size
|
|
FILE_STANDARD_INFO fileInfo;
|
|
if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo)))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
// File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough)
|
|
if (fileInfo.EndOfFile.HighPart > 0)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE);
|
|
}
|
|
|
|
// Zero-sized files assumed to be invalid
|
|
if (fileInfo.EndOfFile.LowPart < 1)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Read file
|
|
blob.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]);
|
|
if (!blob)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
DWORD bytesRead = 0;
|
|
if (!ReadFile(hFile.get(), blob.get(), fileInfo.EndOfFile.LowPart, &bytesRead, nullptr))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
if (bytesRead != fileInfo.EndOfFile.LowPart)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
blobSize = fileInfo.EndOfFile.LowPart;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT LoadFromExtendedBMPMemory(
|
|
_In_reads_bytes_(size) const void* pSource,
|
|
_In_ size_t size,
|
|
_Out_opt_ TexMetadata* metadata,
|
|
_Out_ ScratchImage& image)
|
|
{
|
|
// This loads from non-standard BMP files that are not supported by WIC
|
|
image.Release();
|
|
|
|
if (size < (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)))
|
|
return E_FAIL;
|
|
|
|
// Valid BMP files always start with 'BM' at the top
|
|
auto filehdr = reinterpret_cast<const BITMAPFILEHEADER*>(pSource);
|
|
if (filehdr->bfType != 0x4D42)
|
|
return E_FAIL;
|
|
|
|
if (size < filehdr->bfOffBits)
|
|
return E_FAIL;
|
|
|
|
auto header = reinterpret_cast<const BITMAPINFOHEADER*>(reinterpret_cast<const uint8_t*>(pSource) + sizeof(BITMAPFILEHEADER));
|
|
if (header->biSize != sizeof(BITMAPINFOHEADER))
|
|
return E_FAIL;
|
|
|
|
if (header->biWidth < 1 || header->biHeight < 1 || header->biPlanes != 1 || header->biBitCount != 16)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
|
|
switch (header->biCompression)
|
|
{
|
|
case 0x31545844: // FourCC "DXT1"
|
|
format = DXGI_FORMAT_BC1_UNORM;
|
|
break;
|
|
case 0x33545844: // FourCC "DXT3"
|
|
format = DXGI_FORMAT_BC2_UNORM;
|
|
break;
|
|
case 0x35545844: // FourCC "DXT5"
|
|
format = DXGI_FORMAT_BC3_UNORM;
|
|
break;
|
|
|
|
default:
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
HRESULT hr = image.Initialize2D(format, size_t(header->biWidth), size_t(header->biHeight), 1, 1);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (header->biSizeImage != image.GetPixelsSize())
|
|
return E_UNEXPECTED;
|
|
|
|
size_t remaining = size - filehdr->bfOffBits;
|
|
if (!remaining)
|
|
return E_FAIL;
|
|
|
|
if (remaining < image.GetPixelsSize())
|
|
return E_UNEXPECTED;
|
|
|
|
auto pixels = reinterpret_cast<const uint8_t*>(pSource) + filehdr->bfOffBits;
|
|
|
|
memcpy(image.GetPixels(), pixels, image.GetPixelsSize());
|
|
|
|
if (metadata)
|
|
{
|
|
*metadata = image.GetMetadata();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
HRESULT __cdecl LoadFromBMPEx(
|
|
_In_z_ const wchar_t* szFile,
|
|
_In_ WIC_FLAGS flags,
|
|
_Out_opt_ TexMetadata* metadata,
|
|
_Out_ ScratchImage& image) noexcept
|
|
{
|
|
std::unique_ptr<uint8_t[]> bmpData;
|
|
size_t bmpSize;
|
|
HRESULT hr = ReadData(szFile, bmpData, bmpSize);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = LoadFromWICMemory(bmpData.get(), bmpSize, flags, metadata, image);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = LoadFromExtendedBMPMemory(bmpData.get(), bmpSize, metadata, image);
|
|
}
|
|
|
|
return hr;
|
|
}
|