You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
8.0 KiB
C
216 lines
8.0 KiB
C
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
#pragma once
|
|
|
|
#if defined(WIN32)
|
|
#include "CryWindows.h"
|
|
#include "IRenderer.h"
|
|
#include "IImage.h"
|
|
#include "smartptr.h"
|
|
#include "ImageExtensionHelper.h"
|
|
|
|
// Types supported by CreateResourceFromTexture
|
|
enum EResourceType
|
|
{
|
|
eResourceType_IconBig,
|
|
eResourceType_IconSmall,
|
|
eResourceType_Cursor,
|
|
};
|
|
|
|
// Loads a (DDS) texture resource from the renderer into a Win32 GDI resource.
|
|
// Returns a HICON that must be closed with DestroyIcon(), or NULL on failure.
|
|
static HICON CreateResourceFromTexture(IRenderer* pRenderer, const char* path, EResourceType type)
|
|
{
|
|
if (!pRenderer || !path || !*path)
|
|
{
|
|
// Invalid parameter passed
|
|
return NULL;
|
|
}
|
|
|
|
// Find the target dimensions of the GDI resource
|
|
const int nRequestedWidth = GetSystemMetrics(type == eResourceType_IconBig ? SM_CXICON : type == eResourceType_IconSmall ? SM_CXSMICON : SM_CXCURSOR);
|
|
const int nRequestedHeight = GetSystemMetrics(type == eResourceType_IconBig ? SM_CYICON : type == eResourceType_IconSmall ? SM_CYSMICON : SM_CYCURSOR);
|
|
if (nRequestedWidth != nRequestedHeight || nRequestedHeight <= 0 || nRequestedWidth <= 0)
|
|
{
|
|
// Don't support non-squares icons or cursors
|
|
return NULL;
|
|
}
|
|
|
|
// Load texture.
|
|
// in this case, we want to fall back on a null, not substiture the missing texture.
|
|
_smart_ptr<IImageFile> pImage = pRenderer->EF_LoadImage(path, FIM_NOFALLBACKS);
|
|
if (!pImage || pImage->mfGet_depth() != 1 || pImage->mfGet_NumSides() != 1 || pImage->mfGetTileMode() != eTM_None)
|
|
{
|
|
// Can't load texture, or texture is a cube-map or volume texture, or uses tiling of some kind
|
|
return NULL;
|
|
}
|
|
byte* pImgRaw = pImage->mfGet_image(0);
|
|
const ETEX_Format texFormat = pImage->mfGetFormat();
|
|
|
|
// Pick first mip level smaller than the GDI resource
|
|
int nMip = 0;
|
|
size_t offset = 0;
|
|
int nMipWidth = pImage->mfGet_width();
|
|
int nMipHeight = pImage->mfGet_height();
|
|
while (nMipWidth > nRequestedWidth)
|
|
{
|
|
++nMip;
|
|
offset += pRenderer->GetTextureFormatDataSize(nMipWidth, nMipHeight, 1, 1, texFormat);
|
|
nMipWidth = max(nMipWidth / 2, 1);
|
|
nMipHeight = max(nMipHeight / 2, 1);
|
|
}
|
|
pImgRaw += offset;
|
|
if (nMip >= pImage->mfGet_numMips())
|
|
{
|
|
// No appropriate mip in the texture
|
|
// Note: Consider creating a full mip-chain on the texture so this can't happen
|
|
return NULL;
|
|
}
|
|
const size_t nRawSize = pRenderer->GetTextureFormatDataSize(nMipWidth, nMipHeight, 1, 1, texFormat);
|
|
|
|
#if 0
|
|
// Check that DDS indexing is correct here
|
|
offset += nRawSize;
|
|
for (int nIdx = nMip + 1; nIdx < pImage->mfGet_numMips(); ++nIdx)
|
|
{
|
|
const int w = max(nMipWidth >> (nIdx - nMip), 1);
|
|
const int h = max(nMipHeight >> (nIdx - nMip), 1);
|
|
offset += pRenderer->GetTextureFormatDataSize(w, h, 1, 1, texFormat);
|
|
}
|
|
assert(offset == pImage->mfGet_ImageSize());
|
|
#endif
|
|
|
|
// Type of bitmap to create
|
|
BITMAPV5HEADER bi = { 0 };
|
|
bi.bV5Size = sizeof(BITMAPV5HEADER);
|
|
bi.bV5Width = nRequestedWidth;
|
|
bi.bV5Height = -nRequestedHeight;
|
|
bi.bV5Planes = 1;
|
|
bi.bV5BitCount = 32;
|
|
bi.bV5Compression = BI_BITFIELDS;
|
|
// The following mask specification specifies a supported 32 BPP alpha format for Windows XP+
|
|
bi.bV5AlphaMask = 0xFF000000;
|
|
bi.bV5RedMask = 0x00FF0000;
|
|
bi.bV5GreenMask = 0x0000FF00;
|
|
bi.bV5BlueMask = 0x000000FF;
|
|
|
|
// Create the DIB section with an alpha channel
|
|
const HDC hdc = GetDC(NULL);
|
|
void* lpBits;
|
|
const HBITMAP hBitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, &lpBits, NULL, 0);
|
|
ReleaseDC(NULL, hdc);
|
|
if (!hBitmap || !lpBits)
|
|
{
|
|
// Can't allocate OS bitmap
|
|
return NULL;
|
|
}
|
|
GdiFlush();
|
|
|
|
// Decompress texture
|
|
const bool bTryDecompress = texFormat != eTF_R8G8B8A8 && texFormat != eTF_B8G8R8A8 && texFormat != eTF_B8G8R8X8;
|
|
byte* const pImgDecomp = bTryDecompress ? new byte[nMipWidth * nMipHeight * sizeof(int32)] : pImgRaw;
|
|
const bool bImgDecompressed = bTryDecompress && pRenderer->DXTDecompress(pImgRaw, nRawSize, pImgDecomp, nMipWidth, nMipHeight, 1, texFormat, false, sizeof(int32));
|
|
|
|
// Check which conversions need to be performed
|
|
const bool bSRGB = (pImage->mfGet_Flags() & CImageExtensionHelper::EIF_SRGBRead) != 0;
|
|
const bool bSwap = (texFormat == eTF_R8G8B8A8) || bTryDecompress;
|
|
|
|
HICON result = NULL;
|
|
if (!bTryDecompress || bImgDecompressed)
|
|
{
|
|
// Assign texture data with mismatching sizes
|
|
// Note: Any pixels not in the selected mip will become 100% transparent black, no resizing is performed
|
|
const size_t sourceRowStride = nMipWidth;
|
|
const size_t targetRowStride = nRequestedWidth;
|
|
const uint32* pSourceRow = (uint32*)pImgDecomp;
|
|
uint32* pTargetRow = (uint32*)lpBits;
|
|
|
|
assert(sourceRowStride <= targetRowStride);
|
|
assert(nMipHeight <= nRequestedHeight);
|
|
assert(pSourceRow != pTargetRow);
|
|
assert(pSourceRow && pTargetRow);
|
|
|
|
uint8 gammaTable[256];
|
|
if (!bSRGB)
|
|
{
|
|
// Linear to sRGB table
|
|
const float gamma = 1.0f / 2.2f;
|
|
const float gammaMultiplier = 255.0f / powf(255.0f, gamma);
|
|
for (int i = 0; i < 256; ++i)
|
|
{
|
|
gammaTable[i] = uint8(powf(float(i), gamma) * gammaMultiplier);
|
|
}
|
|
}
|
|
else if (bSwap)
|
|
{
|
|
// sRGB to sRGB table (no change)
|
|
for (int i = 0; i < 256; ++i)
|
|
{
|
|
gammaTable[i] = uint8(i);
|
|
}
|
|
}
|
|
|
|
for (int y = 0; y < nMipHeight; ++y, pSourceRow += sourceRowStride, pTargetRow += targetRowStride)
|
|
{
|
|
if (!bSwap && bSRGB)
|
|
{
|
|
memcpy(pTargetRow, pSourceRow, sourceRowStride * sizeof(uint32));
|
|
}
|
|
else
|
|
{
|
|
const uint32* pSourceTexel = pSourceRow;
|
|
uint32* pTargetTexel = pTargetRow;
|
|
for (int x = 0; x < nMipWidth; ++x, ++pSourceTexel, ++pTargetTexel)
|
|
{
|
|
const uint8 red = gammaTable[uint8(*pSourceTexel >> 0)];
|
|
const uint8 green = gammaTable[uint8(*pSourceTexel >> 8)];
|
|
const uint8 blue = gammaTable[uint8(*pSourceTexel >> 16)];
|
|
const uint32 alpha = *pSourceTexel & 0xFF000000;
|
|
*pTargetTexel = (red << (bSwap ? 16 : 0)) | (green << 8) | (blue << (bSwap ? 0 : 16)) | alpha;
|
|
}
|
|
}
|
|
|
|
// Fill remaining columns with zeros
|
|
memset(pTargetRow + sourceRowStride, 0, (targetRowStride - sourceRowStride) * sizeof(uint32));
|
|
}
|
|
|
|
// Fill remaining rows with zeros
|
|
for (int y = nMipHeight; y < nRequestedHeight; ++y)
|
|
{
|
|
memset(pTargetRow, 0, targetRowStride * sizeof(uint32));
|
|
pTargetRow += targetRowStride;
|
|
}
|
|
|
|
// Convert to GDI icon
|
|
ICONINFO iconinfo = {0};
|
|
iconinfo.fIcon = type == eResourceType_Cursor ? FALSE : TRUE;
|
|
iconinfo.hbmMask = ::CreateBitmap(nRequestedWidth, nRequestedHeight, 1, 1, NULL);
|
|
iconinfo.hbmColor = hBitmap;
|
|
result = ::CreateIconIndirect(&iconinfo);
|
|
DeleteObject(iconinfo.hbmMask);
|
|
}
|
|
|
|
// Clean up
|
|
DeleteObject(hBitmap);
|
|
if (bTryDecompress)
|
|
{
|
|
delete[] pImgDecomp;
|
|
}
|
|
pImage.reset();
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif
|