wine/dlls/d3dx9_36/d3dx_helpers.c
Connor McAdams e2ffae327a d3dx9: Don't include d3dx9 header in d3dx_helpers.
Signed-off-by: Connor McAdams <cmcadams@codeweavers.com>
2025-08-01 20:59:53 +09:00

2931 lines
110 KiB
C

/*
* Copyright 2025 Connor McAdams for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
#include "wine/debug.h"
#include "d3dx_helpers.h"
#define COBJMACROS
#include "ole2.h"
#include "wincodec.h"
#define BCDEC_IMPLEMENTATION
#define BCDEC_STATIC
#include "bcdec.h"
#define STB_DXT_IMPLEMENTATION
#define STB_DXT_STATIC
#include "stb_dxt.h"
#include <assert.h>
WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
#define D3DERR_INVALIDCALL 0x8876086c
#define D3DXERR_INVALIDDATA 0x88760b59
#define D3D_OK S_OK
#define D3DX_FILTER_NONE 0x00000001
#define D3DX_FILTER_POINT 0x00000002
#define D3DX_FILTER_LINEAR 0x00000003
#define D3DX_FILTER_TRIANGLE 0x00000004
#define D3DX_FILTER_BOX 0x00000005
#define D3DX_FILTER_MIRROR_U 0x00010000
#define D3DX_FILTER_MIRROR_V 0x00020000
#define D3DX_FILTER_MIRROR_W 0x00040000
#define D3DX_FILTER_MIRROR 0x00070000
#define D3DX_FILTER_DITHER 0x00080000
#define D3DX_FILTER_DITHER_DIFFUSION 0x00100000
#define D3DX_FILTER_SRGB_IN 0x00200000
#define D3DX_FILTER_SRGB_OUT 0x00400000
#define D3DX_FILTER_SRGB 0x00600000
HRESULT WINAPI WICCreateImagingFactory_Proxy(UINT, IWICImagingFactory**);
/************************************************************
* pixel format table providing info about number of bytes per pixel,
* number of bits per channel and format type.
*
* Call get_format_info to request information about a specific format.
*/
static const struct pixel_format_desc formats[] =
{
/* format bpc shifts bpp blocks alpha type rgb type flags */
{D3DX_PIXEL_FORMAT_B8G8R8_UNORM, { 0, 8, 8, 8}, { 0, 16, 8, 0}, 3, 1, 1, 3, CTYPE_EMPTY, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM, { 8, 8, 8, 8}, {24, 16, 8, 0}, 4, 1, 1, 4, CTYPE_UNORM, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_B8G8R8X8_UNORM, { 0, 8, 8, 8}, { 0, 16, 8, 0}, 4, 1, 1, 4, CTYPE_EMPTY, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_R8G8B8A8_UNORM, { 8, 8, 8, 8}, {24, 0, 8, 16}, 4, 1, 1, 4, CTYPE_UNORM, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_R8G8B8X8_UNORM, { 0, 8, 8, 8}, { 0, 0, 8, 16}, 4, 1, 1, 4, CTYPE_EMPTY, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_B5G6R5_UNORM, { 0, 5, 6, 5}, { 0, 11, 5, 0}, 2, 1, 1, 2, CTYPE_EMPTY, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_B5G5R5X1_UNORM, { 0, 5, 5, 5}, { 0, 10, 5, 0}, 2, 1, 1, 2, CTYPE_EMPTY, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_B5G5R5A1_UNORM, { 1, 5, 5, 5}, {15, 10, 5, 0}, 2, 1, 1, 2, CTYPE_UNORM, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_B2G3R3_UNORM, { 0, 3, 3, 2}, { 0, 5, 2, 0}, 1, 1, 1, 1, CTYPE_EMPTY, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_B2G3R3A8_UNORM, { 8, 3, 3, 2}, { 8, 5, 2, 0}, 2, 1, 1, 2, CTYPE_UNORM, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_B4G4R4A4_UNORM, { 4, 4, 4, 4}, {12, 8, 4, 0}, 2, 1, 1, 2, CTYPE_UNORM, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_B4G4R4X4_UNORM, { 0, 4, 4, 4}, { 0, 8, 4, 0}, 2, 1, 1, 2, CTYPE_EMPTY, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_B10G10R10A2_UNORM, { 2, 10, 10, 10}, {30, 20, 10, 0}, 4, 1, 1, 4, CTYPE_UNORM, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_R10G10B10A2_UNORM, { 2, 10, 10, 10}, {30, 0, 10, 20}, 4, 1, 1, 4, CTYPE_UNORM, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_R16G16B16_UNORM, { 0, 16, 16, 16}, { 0, 0, 16, 32}, 6, 1, 1, 6, CTYPE_EMPTY, CTYPE_UNORM, FMT_FLAG_INTERNAL},
{D3DX_PIXEL_FORMAT_R16G16B16A16_UNORM, {16, 16, 16, 16}, {48, 0, 16, 32}, 8, 1, 1, 8, CTYPE_UNORM, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_R16G16_UNORM, { 0, 16, 16, 0}, { 0, 0, 16, 0}, 4, 1, 1, 4, CTYPE_EMPTY, CTYPE_UNORM, 0 },
{D3DX_PIXEL_FORMAT_A8_UNORM, { 8, 0, 0, 0}, { 0, 0, 0, 0}, 1, 1, 1, 1, CTYPE_UNORM, CTYPE_EMPTY, 0 },
{D3DX_PIXEL_FORMAT_L8A8_UNORM, { 8, 8, 0, 0}, { 8, 0, 0, 0}, 2, 1, 1, 2, CTYPE_UNORM, CTYPE_LUMA, 0 },
{D3DX_PIXEL_FORMAT_L4A4_UNORM, { 4, 4, 0, 0}, { 4, 0, 0, 0}, 1, 1, 1, 1, CTYPE_UNORM, CTYPE_LUMA, 0 },
{D3DX_PIXEL_FORMAT_L8_UNORM, { 0, 8, 0, 0}, { 0, 0, 0, 0}, 1, 1, 1, 1, CTYPE_EMPTY, CTYPE_LUMA, 0 },
{D3DX_PIXEL_FORMAT_L16_UNORM, { 0, 16, 0, 0}, { 0, 0, 0, 0}, 2, 1, 1, 2, CTYPE_EMPTY, CTYPE_LUMA, 0 },
{D3DX_PIXEL_FORMAT_DXT1_UNORM, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 1, 4, 4, 8, CTYPE_UNORM, CTYPE_UNORM, FMT_FLAG_DXT},
{D3DX_PIXEL_FORMAT_DXT2_UNORM, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 1, 4, 4, 16, CTYPE_UNORM, CTYPE_UNORM, FMT_FLAG_DXT},
{D3DX_PIXEL_FORMAT_DXT3_UNORM, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 1, 4, 4, 16, CTYPE_UNORM, CTYPE_UNORM, FMT_FLAG_DXT},
{D3DX_PIXEL_FORMAT_DXT4_UNORM, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 1, 4, 4, 16, CTYPE_UNORM, CTYPE_UNORM, FMT_FLAG_DXT},
{D3DX_PIXEL_FORMAT_DXT5_UNORM, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 1, 4, 4, 16, CTYPE_UNORM, CTYPE_UNORM, FMT_FLAG_DXT},
{D3DX_PIXEL_FORMAT_R16_FLOAT, { 0, 16, 0, 0}, { 0, 0, 0, 0}, 2, 1, 1, 2, CTYPE_EMPTY, CTYPE_FLOAT, 0 },
{D3DX_PIXEL_FORMAT_R16G16_FLOAT, { 0, 16, 16, 0}, { 0, 0, 16, 0}, 4, 1, 1, 4, CTYPE_EMPTY, CTYPE_FLOAT, 0 },
{D3DX_PIXEL_FORMAT_R16G16B16A16_FLOAT, {16, 16, 16, 16}, {48, 0, 16, 32}, 8, 1, 1, 8, CTYPE_FLOAT, CTYPE_FLOAT, 0 },
{D3DX_PIXEL_FORMAT_R32_FLOAT, { 0, 32, 0, 0}, { 0, 0, 0, 0}, 4, 1, 1, 4, CTYPE_EMPTY, CTYPE_FLOAT, 0 },
{D3DX_PIXEL_FORMAT_R32G32_FLOAT, { 0, 32, 32, 0}, { 0, 0, 32, 0}, 8, 1, 1, 8, CTYPE_EMPTY, CTYPE_FLOAT, 0 },
{D3DX_PIXEL_FORMAT_R32G32B32A32_FLOAT, {32, 32, 32, 32}, {96, 0, 32, 64}, 16, 1, 1, 16, CTYPE_FLOAT, CTYPE_FLOAT, 0 },
{D3DX_PIXEL_FORMAT_P1_UINT, { 8, 8, 8, 8}, { 0, 0, 0, 0}, 1, 8, 1, 1, CTYPE_INDEX, CTYPE_INDEX, FMT_FLAG_INTERNAL},
{D3DX_PIXEL_FORMAT_P2_UINT, { 8, 8, 8, 8}, { 0, 0, 0, 0}, 1, 4, 1, 1, CTYPE_INDEX, CTYPE_INDEX, FMT_FLAG_INTERNAL},
{D3DX_PIXEL_FORMAT_P4_UINT, { 8, 8, 8, 8}, { 0, 0, 0, 0}, 1, 2, 1, 1, CTYPE_INDEX, CTYPE_INDEX, FMT_FLAG_INTERNAL},
{D3DX_PIXEL_FORMAT_P8_UINT, { 8, 8, 8, 8}, { 0, 0, 0, 0}, 1, 1, 1, 1, CTYPE_INDEX, CTYPE_INDEX, 0 },
{D3DX_PIXEL_FORMAT_P8_UINT_A8_UNORM, { 8, 8, 8, 8}, { 8, 0, 0, 0}, 2, 1, 1, 2, CTYPE_UNORM, CTYPE_INDEX, 0 },
{D3DX_PIXEL_FORMAT_U8V8W8Q8_SNORM, { 8, 8, 8, 8}, {24, 0, 8, 16}, 4, 1, 1, 4, CTYPE_SNORM, CTYPE_SNORM, 0 },
{D3DX_PIXEL_FORMAT_U16V16W16Q16_SNORM, {16, 16, 16, 16}, {48, 0, 16, 32}, 8, 1, 1, 8, CTYPE_SNORM, CTYPE_SNORM, 0 },
{D3DX_PIXEL_FORMAT_U8V8_SNORM, { 0, 8, 8, 0}, { 0, 0, 8, 0}, 2, 1, 1, 2, CTYPE_EMPTY, CTYPE_SNORM, 0 },
{D3DX_PIXEL_FORMAT_U16V16_SNORM, { 0, 16, 16, 0}, { 0, 0, 16, 0}, 4, 1, 1, 4, CTYPE_EMPTY, CTYPE_SNORM, 0 },
{D3DX_PIXEL_FORMAT_U8V8_SNORM_L8X8_UNORM, { 8, 8, 8, 0}, {16, 0, 8, 0}, 4, 1, 1, 4, CTYPE_UNORM, CTYPE_SNORM, 0 },
{D3DX_PIXEL_FORMAT_U10V10W10_SNORM_A2_UNORM, { 2, 10, 10, 10}, {30, 0, 10, 20}, 4, 1, 1, 4, CTYPE_UNORM, CTYPE_SNORM, 0 },
{D3DX_PIXEL_FORMAT_R8G8_B8G8_UNORM, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 1, 2, 1, 4, CTYPE_EMPTY, CTYPE_UNORM, FMT_FLAG_PACKED},
{D3DX_PIXEL_FORMAT_G8R8_G8B8_UNORM, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 1, 2, 1, 4, CTYPE_EMPTY, CTYPE_UNORM, FMT_FLAG_PACKED},
{D3DX_PIXEL_FORMAT_UYVY, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 1, 2, 1, 4, CTYPE_EMPTY, CTYPE_UNORM, FMT_FLAG_PACKED},
{D3DX_PIXEL_FORMAT_YUY2, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 1, 2, 1, 4, CTYPE_EMPTY, CTYPE_UNORM, FMT_FLAG_PACKED},
/* marks last element */
{D3DX_PIXEL_FORMAT_COUNT, { 0, 0, 0, 0}, { 0, 0, 0, 0}, 0, 1, 1, 0, CTYPE_EMPTY, CTYPE_EMPTY, 0 },
};
const struct pixel_format_desc *get_d3dx_pixel_format_info(enum d3dx_pixel_format_id format)
{
return &formats[min(format, D3DX_PIXEL_FORMAT_COUNT)];
}
static const struct
{
const GUID *wic_guid;
enum d3dx_pixel_format_id d3dx_pixel_format;
} wic_pixel_formats[] =
{
{ &GUID_WICPixelFormat8bppIndexed, D3DX_PIXEL_FORMAT_P8_UINT },
{ &GUID_WICPixelFormat1bppIndexed, D3DX_PIXEL_FORMAT_P1_UINT },
{ &GUID_WICPixelFormat2bppIndexed, D3DX_PIXEL_FORMAT_P2_UINT },
{ &GUID_WICPixelFormat4bppIndexed, D3DX_PIXEL_FORMAT_P4_UINT },
{ &GUID_WICPixelFormat8bppGray, D3DX_PIXEL_FORMAT_L8_UNORM },
{ &GUID_WICPixelFormat16bppBGR555, D3DX_PIXEL_FORMAT_B5G5R5X1_UNORM },
{ &GUID_WICPixelFormat16bppBGR565, D3DX_PIXEL_FORMAT_B5G6R5_UNORM },
{ &GUID_WICPixelFormat24bppBGR, D3DX_PIXEL_FORMAT_B8G8R8_UNORM },
{ &GUID_WICPixelFormat32bppBGR, D3DX_PIXEL_FORMAT_B8G8R8X8_UNORM },
{ &GUID_WICPixelFormat32bppBGRA, D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM },
{ &GUID_WICPixelFormat48bppRGB, D3DX_PIXEL_FORMAT_R16G16B16_UNORM },
{ &GUID_WICPixelFormat64bppRGBA, D3DX_PIXEL_FORMAT_R16G16B16A16_UNORM },
};
static enum d3dx_pixel_format_id d3dx_pixel_format_id_from_wic_pixel_format(const GUID *guid)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(wic_pixel_formats); i++)
{
if (IsEqualGUID(wic_pixel_formats[i].wic_guid, guid))
return wic_pixel_formats[i].d3dx_pixel_format;
}
return D3DX_PIXEL_FORMAT_COUNT;
}
static const GUID *wic_guid_from_d3dx_pixel_format_id(enum d3dx_pixel_format_id d3dx_pixel_format)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(wic_pixel_formats); i++)
{
if (wic_pixel_formats[i].d3dx_pixel_format == d3dx_pixel_format)
return wic_pixel_formats[i].wic_guid;
}
return NULL;
}
#define TGA_IMAGETYPE_COLORMAPPED 1
#define TGA_IMAGETYPE_TRUECOLOR 2
#define TGA_IMAGETYPE_GRAYSCALE 3
#define TGA_IMAGETYPE_MASK 0x07
#define TGA_IMAGETYPE_RLE 8
#define TGA_IMAGE_RIGHTTOLEFT 0x10
#define TGA_IMAGE_TOPTOBOTTOM 0x20
#pragma pack(push,1)
struct tga_header
{
uint8_t id_length;
uint8_t color_map_type;
uint8_t image_type;
uint16_t color_map_firstentry;
uint16_t color_map_length;
uint8_t color_map_entrysize;
uint16_t xorigin;
uint16_t yorigin;
uint16_t width;
uint16_t height;
uint8_t depth;
uint8_t image_descriptor;
};
#pragma pack(pop)
#ifndef MAKEFOURCC
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 ))
#endif
static const struct
{
struct dds_pixel_format dds_pixel_format;
enum d3dx_pixel_format_id d3dx_pixel_format;
} dds_pixel_formats[] =
{
/* DDS_PF_FOURCC. */
{ { 32, DDS_PF_FOURCC, MAKEFOURCC('U','Y','V','Y') }, D3DX_PIXEL_FORMAT_UYVY },
{ { 32, DDS_PF_FOURCC, MAKEFOURCC('Y','U','Y','2') }, D3DX_PIXEL_FORMAT_YUY2 },
{ { 32, DDS_PF_FOURCC, MAKEFOURCC('R','G','B','G') }, D3DX_PIXEL_FORMAT_R8G8_B8G8_UNORM },
{ { 32, DDS_PF_FOURCC, MAKEFOURCC('G','R','G','B') }, D3DX_PIXEL_FORMAT_G8R8_G8B8_UNORM },
{ { 32, DDS_PF_FOURCC, MAKEFOURCC('D','X','T','1') }, D3DX_PIXEL_FORMAT_DXT1_UNORM },
{ { 32, DDS_PF_FOURCC, MAKEFOURCC('D','X','T','2') }, D3DX_PIXEL_FORMAT_DXT2_UNORM },
{ { 32, DDS_PF_FOURCC, MAKEFOURCC('D','X','T','3') }, D3DX_PIXEL_FORMAT_DXT3_UNORM },
{ { 32, DDS_PF_FOURCC, MAKEFOURCC('D','X','T','4') }, D3DX_PIXEL_FORMAT_DXT4_UNORM },
{ { 32, DDS_PF_FOURCC, MAKEFOURCC('D','X','T','5') }, D3DX_PIXEL_FORMAT_DXT5_UNORM },
/* These aren't actually fourcc values, they're just D3DFMT values. */
{ { 32, DDS_PF_FOURCC, 0x24 }, D3DX_PIXEL_FORMAT_R16G16B16A16_UNORM },
{ { 32, DDS_PF_FOURCC, 0x6e }, D3DX_PIXEL_FORMAT_U16V16W16Q16_SNORM },
{ { 32, DDS_PF_FOURCC, 0x6f }, D3DX_PIXEL_FORMAT_R16_FLOAT },
{ { 32, DDS_PF_FOURCC, 0x70 }, D3DX_PIXEL_FORMAT_R16G16_FLOAT },
{ { 32, DDS_PF_FOURCC, 0x71 }, D3DX_PIXEL_FORMAT_R16G16B16A16_FLOAT },
{ { 32, DDS_PF_FOURCC, 0x72 }, D3DX_PIXEL_FORMAT_R32_FLOAT },
{ { 32, DDS_PF_FOURCC, 0x73 }, D3DX_PIXEL_FORMAT_R32G32_FLOAT },
{ { 32, DDS_PF_FOURCC, 0x74 }, D3DX_PIXEL_FORMAT_R32G32B32A32_FLOAT },
/* DDS_PF_RGB. */
{ { 32, DDS_PF_RGB, 0, 8, 0xe0, 0x1c, 0x03, 0x00 }, D3DX_PIXEL_FORMAT_B2G3R3_UNORM },
{ { 32, DDS_PF_RGB, 0, 16, 0xf800, 0x07e0, 0x001f, 0x0000 }, D3DX_PIXEL_FORMAT_B5G6R5_UNORM },
{ { 32, DDS_PF_RGB, 0, 16, 0x7c00, 0x03e0, 0x001f, 0x0000 }, D3DX_PIXEL_FORMAT_B5G5R5X1_UNORM },
{ { 32, DDS_PF_RGB, 0, 16, 0x0f00, 0x00f0, 0x000f, 0x0000 }, D3DX_PIXEL_FORMAT_B4G4R4X4_UNORM },
{ { 32, DDS_PF_RGB, 0, 24, 0xff0000, 0x00ff00, 0x0000ff, 0x000000 }, D3DX_PIXEL_FORMAT_B8G8R8_UNORM },
{ { 32, DDS_PF_RGB, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 }, D3DX_PIXEL_FORMAT_B8G8R8X8_UNORM },
{ { 32, DDS_PF_RGB, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }, D3DX_PIXEL_FORMAT_R16G16_UNORM },
{ { 32, DDS_PF_RGB, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000 }, D3DX_PIXEL_FORMAT_R8G8B8X8_UNORM },
{ { 32, DDS_PF_RGB | DDS_PF_ALPHA, 0, 16, 0x00e0, 0x001c, 0x0003, 0xff00 }, D3DX_PIXEL_FORMAT_B2G3R3A8_UNORM },
{ { 32, DDS_PF_RGB | DDS_PF_ALPHA, 0, 16, 0x7c00, 0x03e0, 0x001f, 0x8000 }, D3DX_PIXEL_FORMAT_B5G5R5A1_UNORM },
{ { 32, DDS_PF_RGB | DDS_PF_ALPHA, 0, 16, 0x0f00, 0x00f0, 0x000f, 0xf000 }, D3DX_PIXEL_FORMAT_B4G4R4A4_UNORM },
{ { 32, DDS_PF_RGB | DDS_PF_ALPHA, 0, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }, D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM },
{ { 32, DDS_PF_RGB | DDS_PF_ALPHA, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }, D3DX_PIXEL_FORMAT_R8G8B8A8_UNORM },
{ { 32, DDS_PF_RGB | DDS_PF_ALPHA, 0, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 }, D3DX_PIXEL_FORMAT_R10G10B10A2_UNORM },
{ { 32, DDS_PF_RGB | DDS_PF_ALPHA, 0, 32, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000 }, D3DX_PIXEL_FORMAT_B10G10R10A2_UNORM },
/* DDS_PF_INDEXED. */
{ { 32, DDS_PF_INDEXED, 0, 8 }, D3DX_PIXEL_FORMAT_P8_UINT },
{ { 32, DDS_PF_INDEXED | DDS_PF_ALPHA, 0, 16, 0, 0, 0, 0xff00, }, D3DX_PIXEL_FORMAT_P8_UINT_A8_UNORM },
/* DDS_PF_LUMINANCE. */
{ { 32, DDS_PF_LUMINANCE, 0, 8, 0x00ff }, D3DX_PIXEL_FORMAT_L8_UNORM },
{ { 32, DDS_PF_LUMINANCE, 0, 16, 0xffff }, D3DX_PIXEL_FORMAT_L16_UNORM },
{ { 32, DDS_PF_LUMINANCE | DDS_PF_ALPHA, 0, 8, 0x000f, 0, 0, 0x00f0 }, D3DX_PIXEL_FORMAT_L4A4_UNORM },
{ { 32, DDS_PF_LUMINANCE | DDS_PF_ALPHA, 0, 16, 0x00ff, 0, 0, 0xff00 }, D3DX_PIXEL_FORMAT_L8A8_UNORM },
/* Exceptional case, A8L8 can also have 8bpp. */
{ { 32, DDS_PF_LUMINANCE | DDS_PF_ALPHA, 0, 8, 0x00ff, 0, 0, 0xff00 }, D3DX_PIXEL_FORMAT_L8A8_UNORM },
/* DDS_PF_ALPHA_ONLY. */
{ { 32, DDS_PF_ALPHA_ONLY, 0, 8, 0, 0, 0, 0xff }, D3DX_PIXEL_FORMAT_A8_UNORM },
/* DDS_PF_BUMPDUDV. */
{ { 32, DDS_PF_BUMPDUDV, 0, 16, 0x000000ff, 0x0000ff00, 0x00000000, 0x00000000 }, D3DX_PIXEL_FORMAT_U8V8_SNORM },
{ { 32, DDS_PF_BUMPDUDV, 0, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }, D3DX_PIXEL_FORMAT_U16V16_SNORM },
{ { 32, DDS_PF_BUMPDUDV, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }, D3DX_PIXEL_FORMAT_U8V8W8Q8_SNORM },
{ { 32, DDS_PF_BUMPDUDV | DDS_PF_ALPHA, 0, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 }, D3DX_PIXEL_FORMAT_U10V10W10_SNORM_A2_UNORM },
/* DDS_PF_BUMPLUMINANCE. */
{ { 32, DDS_PF_BUMPLUMINANCE, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000 }, D3DX_PIXEL_FORMAT_U8V8_SNORM_L8X8_UNORM },
};
static BOOL dds_pixel_format_compare(const struct dds_pixel_format *pf_a, const struct dds_pixel_format *pf_b,
BOOL check_rmask, BOOL check_gmask, BOOL check_bmask, BOOL check_amask)
{
return pf_a->bpp == pf_b->bpp && !((check_rmask && pf_a->rmask != pf_b->rmask)
|| (check_gmask && pf_a->gmask != pf_b->gmask) || (check_bmask && pf_a->bmask != pf_b->bmask)
|| (check_amask && pf_a->amask != pf_b->amask));
}
static enum d3dx_pixel_format_id d3dx_pixel_format_id_from_dds_pixel_format(const struct dds_pixel_format *pixel_format)
{
uint32_t i;
TRACE("pixel_format: size %lu, flags %#lx, fourcc %#lx, bpp %lu.\n", pixel_format->size,
pixel_format->flags, pixel_format->fourcc, pixel_format->bpp);
TRACE("rmask %#lx, gmask %#lx, bmask %#lx, amask %#lx.\n", pixel_format->rmask, pixel_format->gmask,
pixel_format->bmask, pixel_format->amask);
for (i = 0; i < ARRAY_SIZE(dds_pixel_formats); ++i)
{
const struct dds_pixel_format *dds_pf = &dds_pixel_formats[i].dds_pixel_format;
if (pixel_format->flags != dds_pf->flags)
continue;
switch (pixel_format->flags & ~DDS_PF_ALPHA)
{
case DDS_PF_ALPHA_ONLY:
if (dds_pixel_format_compare(pixel_format, dds_pf, FALSE, FALSE, FALSE, TRUE))
return dds_pixel_formats[i].d3dx_pixel_format;
break;
case DDS_PF_FOURCC:
if (pixel_format->fourcc == dds_pf->fourcc)
return dds_pixel_formats[i].d3dx_pixel_format;
break;
case DDS_PF_INDEXED:
if (dds_pixel_format_compare(pixel_format, dds_pf, FALSE, FALSE, FALSE, pixel_format->flags & DDS_PF_ALPHA))
return dds_pixel_formats[i].d3dx_pixel_format;
break;
case DDS_PF_RGB:
if (dds_pixel_format_compare(pixel_format, dds_pf, TRUE, TRUE, TRUE, pixel_format->flags & DDS_PF_ALPHA))
return dds_pixel_formats[i].d3dx_pixel_format;
break;
case DDS_PF_LUMINANCE:
if (dds_pixel_format_compare(pixel_format, dds_pf, TRUE, FALSE, FALSE, pixel_format->flags & DDS_PF_ALPHA))
return dds_pixel_formats[i].d3dx_pixel_format;
break;
case DDS_PF_BUMPLUMINANCE:
if (dds_pixel_format_compare(pixel_format, dds_pf, TRUE, TRUE, TRUE, FALSE))
return dds_pixel_formats[i].d3dx_pixel_format;
break;
case DDS_PF_BUMPDUDV:
if (dds_pixel_format_compare(pixel_format, dds_pf, TRUE, TRUE, TRUE, TRUE))
return dds_pixel_formats[i].d3dx_pixel_format;
break;
default:
assert(0); /* Should not happen. */
break;
}
}
WARN("Unknown pixel format (flags %#lx, fourcc %#lx, bpp %lu, r %#lx, g %#lx, b %#lx, a %#lx).\n",
pixel_format->flags, pixel_format->fourcc, pixel_format->bpp,
pixel_format->rmask, pixel_format->gmask, pixel_format->bmask, pixel_format->amask);
return D3DX_PIXEL_FORMAT_COUNT;
}
static HRESULT dds_pixel_format_from_d3dx_pixel_format_id(struct dds_pixel_format *pixel_format,
enum d3dx_pixel_format_id d3dx_pixel_format)
{
const struct dds_pixel_format *pf = NULL;
uint32_t i;
for (i = 0; i < ARRAY_SIZE(dds_pixel_formats); ++i)
{
if (dds_pixel_formats[i].d3dx_pixel_format == d3dx_pixel_format)
{
pf = &dds_pixel_formats[i].dds_pixel_format;
break;
}
}
if (!pf)
{
WARN("Unhandled format %#x.\n", d3dx_pixel_format);
return E_NOTIMPL;
}
if (pixel_format)
*pixel_format = *pf;
return D3D_OK;
}
static void d3dx_get_next_mip_level_size(struct volume *size)
{
size->width = max(size->width / 2, 1);
size->height = max(size->height / 2, 1);
size->depth = max(size->depth / 2, 1);
}
static const char *debug_volume(const struct volume *volume)
{
if (!volume)
return "(null)";
return wine_dbg_sprintf("(%ux%ux%u)", volume->width, volume->height, volume->depth);
}
static HRESULT d3dx_calculate_pixels_size(enum d3dx_pixel_format_id format, uint32_t width, uint32_t height,
uint32_t *pitch, uint32_t *size)
{
const struct pixel_format_desc *format_desc = get_d3dx_pixel_format_info(format);
if (is_unknown_format(format_desc))
return E_NOTIMPL;
if (format_desc->block_width != 1 || format_desc->block_height != 1)
{
*pitch = format_desc->block_byte_count
* max(1, (width + format_desc->block_width - 1) / format_desc->block_width);
*size = *pitch
* max(1, (height + format_desc->block_height - 1) / format_desc->block_height);
}
else
{
*pitch = width * format_desc->bytes_per_pixel;
*size = *pitch * height;
}
return D3D_OK;
}
uint32_t d3dx_calculate_layer_pixels_size(enum d3dx_pixel_format_id format, uint32_t width, uint32_t height, uint32_t depth,
uint32_t mip_levels)
{
uint32_t layer_size, row_pitch, slice_pitch, i;
struct volume dims = { width, height, depth };
layer_size = 0;
for (i = 0; i < mip_levels; ++i)
{
if (FAILED(d3dx_calculate_pixels_size(format, dims.width, dims.height, &row_pitch, &slice_pitch)))
return 0;
layer_size += slice_pitch * dims.depth;
d3dx_get_next_mip_level_size(&dims);
}
return layer_size;
}
HRESULT d3dx_init_dds_header(struct dds_header *header, enum d3dx_resource_type resource_type,
enum d3dx_pixel_format_id format, const struct volume *size, uint32_t mip_levels)
{
HRESULT hr;
memset(header, 0, sizeof(*header));
header->signature = MAKEFOURCC('D','D','S',' ');
/* The signature is not really part of the DDS header. */
header->size = sizeof(*header) - FIELD_OFFSET(struct dds_header, size);
hr = dds_pixel_format_from_d3dx_pixel_format_id(&header->pixel_format, format);
if (FAILED(hr))
return hr;
header->flags = DDS_CAPS | DDS_HEIGHT | DDS_WIDTH | DDS_PIXELFORMAT;
header->height = size->height;
header->width = size->width;
header->caps = DDS_CAPS_TEXTURE;
if (size->depth > 1)
{
header->flags |= DDS_DEPTH;
header->depth = size->depth;
header->caps2 |= DDS_CAPS2_VOLUME;
}
if (mip_levels > 1)
{
header->flags |= DDS_MIPMAPCOUNT;
header->caps |= (DDS_CAPS_MIPMAP | DDS_CAPS_COMPLEX);
header->miplevels = mip_levels;
}
if (resource_type == D3DX_RESOURCE_TYPE_CUBE_TEXTURE)
{
header->caps |= DDS_CAPS_COMPLEX;
header->caps2 |= (DDS_CAPS2_CUBEMAP | DDS_CAPS2_CUBEMAP_ALL_FACES);
}
if (header->pixel_format.flags & DDS_PF_ALPHA || header->pixel_format.flags & DDS_PF_ALPHA_ONLY)
header->caps |= DDSCAPS_ALPHA;
if (header->pixel_format.flags & DDS_PF_INDEXED)
header->caps |= DDSCAPS_PALETTE;
return D3D_OK;
}
static const GUID *wic_container_guid_from_d3dx_file_format(enum d3dx_image_file_format iff)
{
switch (iff)
{
case D3DX_IMAGE_FILE_FORMAT_DIB:
case D3DX_IMAGE_FILE_FORMAT_BMP: return &GUID_ContainerFormatBmp;
case D3DX_IMAGE_FILE_FORMAT_JPG: return &GUID_ContainerFormatJpeg;
case D3DX_IMAGE_FILE_FORMAT_PNG: return &GUID_ContainerFormatPng;
default:
assert(0 && "Unexpected file format.");
return NULL;
}
}
static HRESULT d3dx_pixels_save_wic(struct d3dx_pixels *pixels, const struct pixel_format_desc *fmt_desc,
enum d3dx_image_file_format image_file_format, IStream **wic_file, uint32_t *wic_file_size)
{
const GUID *container_format = wic_container_guid_from_d3dx_file_format(image_file_format);
const GUID *pixel_format_guid = wic_guid_from_d3dx_pixel_format_id(fmt_desc->format);
IWICBitmapFrameEncode *wic_frame = NULL;
IPropertyBag2 *encoder_options = NULL;
IWICBitmapEncoder *wic_encoder = NULL;
WICPixelFormatGUID wic_pixel_format;
const LARGE_INTEGER seek = { 0 };
IWICImagingFactory *wic_factory;
IWICPalette *wic_palette = NULL;
IStream *stream = NULL;
STATSTG stream_stats;
HRESULT hr;
assert(container_format && pixel_format_guid);
hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &wic_factory);
if (FAILED(hr))
return D3DERR_INVALIDCALL;
hr = IWICImagingFactory_CreateEncoder(wic_factory, container_format, NULL, &wic_encoder);
if (FAILED(hr))
{
hr = D3DERR_INVALIDCALL;
goto exit;
}
hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
if (FAILED(hr))
goto exit;
hr = IWICBitmapEncoder_Initialize(wic_encoder, stream, WICBitmapEncoderNoCache);
if (FAILED(hr))
goto exit;
hr = IWICBitmapEncoder_CreateNewFrame(wic_encoder, &wic_frame, &encoder_options);
if (FAILED(hr))
goto exit;
hr = IWICBitmapFrameEncode_Initialize(wic_frame, encoder_options);
if (FAILED(hr))
goto exit;
if (image_file_format == D3DX_IMAGE_FILE_FORMAT_JPG)
FIXME("JPEG saving quality adjustment currently unimplemented, expect lower quality JPEG.\n");
hr = IWICBitmapFrameEncode_SetSize(wic_frame, pixels->size.width, pixels->size.height);
if (FAILED(hr))
goto exit;
if (pixels->palette)
{
WICColor tmp_palette[256];
unsigned int i;
hr = IWICImagingFactory_CreatePalette(wic_factory, &wic_palette);
if (FAILED(hr))
goto exit;
for (i = 0; i < ARRAY_SIZE(tmp_palette); ++i)
{
const PALETTEENTRY *pe = &pixels->palette[i];
tmp_palette[i] = (pe->peFlags << 24) | (pe->peRed << 16) | (pe->peGreen << 8) | (pe->peBlue);
}
hr = IWICPalette_InitializeCustom(wic_palette, tmp_palette, ARRAY_SIZE(tmp_palette));
if (FAILED(hr))
goto exit;
hr = IWICBitmapFrameEncode_SetPalette(wic_frame, wic_palette);
if (FAILED(hr))
goto exit;
}
/*
* Encode 32bpp BGRA format surfaces as 32bpp BGRX for BMP.
* This matches the behavior of native.
*/
if (IsEqualGUID(&GUID_ContainerFormatBmp, container_format) && (fmt_desc->format == D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM))
pixel_format_guid = wic_guid_from_d3dx_pixel_format_id(D3DX_PIXEL_FORMAT_B8G8R8X8_UNORM);
wic_pixel_format = *pixel_format_guid;
hr = IWICBitmapFrameEncode_SetPixelFormat(wic_frame, &wic_pixel_format);
if (FAILED(hr))
goto exit;
if (!IsEqualGUID(pixel_format_guid, &wic_pixel_format))
{
ERR("SetPixelFormat returned a different pixel format.\n");
hr = E_FAIL;
goto exit;
}
hr = IWICBitmapFrameEncode_WritePixels(wic_frame, pixels->size.height, pixels->row_pitch, pixels->slice_pitch,
(BYTE *)pixels->data);
if (FAILED(hr))
goto exit;
hr = IWICBitmapFrameEncode_Commit(wic_frame);
if (FAILED(hr))
goto exit;
hr = IWICBitmapEncoder_Commit(wic_encoder);
if (FAILED(hr))
goto exit;
hr = IStream_Seek(stream, seek, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
goto exit;
hr = IStream_Stat(stream, &stream_stats, STATFLAG_NONAME);
if (FAILED(hr))
goto exit;
if (!stream_stats.cbSize.u.HighPart && !!stream_stats.cbSize.u.LowPart)
{
*wic_file = stream;
*wic_file_size = stream_stats.cbSize.u.LowPart;
}
else
{
hr = D3DXERR_INVALIDDATA;
}
exit:
if (wic_factory)
IWICImagingFactory_Release(wic_factory);
if (stream && (*wic_file != stream))
IStream_Release(stream);
if (wic_frame)
IWICBitmapFrameEncode_Release(wic_frame);
if (wic_palette)
IWICPalette_Release(wic_palette);
if (encoder_options)
IPropertyBag2_Release(encoder_options);
if (wic_encoder)
IWICBitmapEncoder_Release(wic_encoder);
return hr;
}
static const enum d3dx_pixel_format_id tga_save_pixel_formats[] =
{
D3DX_PIXEL_FORMAT_B8G8R8_UNORM,
D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM
};
static const enum d3dx_pixel_format_id png_save_pixel_formats[] =
{
D3DX_PIXEL_FORMAT_B8G8R8_UNORM,
D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM,
D3DX_PIXEL_FORMAT_R16G16B16A16_UNORM
};
static const enum d3dx_pixel_format_id jpg_save_pixel_formats[] =
{
D3DX_PIXEL_FORMAT_B8G8R8_UNORM,
};
static const enum d3dx_pixel_format_id bmp_save_pixel_formats[] =
{
D3DX_PIXEL_FORMAT_B5G5R5X1_UNORM,
D3DX_PIXEL_FORMAT_B5G6R5_UNORM,
D3DX_PIXEL_FORMAT_B8G8R8_UNORM,
D3DX_PIXEL_FORMAT_B8G8R8X8_UNORM,
D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM,
D3DX_PIXEL_FORMAT_P8_UINT,
};
static const enum d3dx_pixel_format_id unimplemented_bmp_save_pixel_formats[] =
{
D3DX_PIXEL_FORMAT_A8_UNORM,
D3DX_PIXEL_FORMAT_P8_UINT_A8_UNORM,
D3DX_PIXEL_FORMAT_L8A8_UNORM,
D3DX_PIXEL_FORMAT_L16_UNORM,
D3DX_PIXEL_FORMAT_B2G3R3_UNORM,
D3DX_PIXEL_FORMAT_R16_FLOAT,
D3DX_PIXEL_FORMAT_R16G16_FLOAT,
D3DX_PIXEL_FORMAT_R16G16_UNORM,
D3DX_PIXEL_FORMAT_R32_FLOAT,
D3DX_PIXEL_FORMAT_R32G32_FLOAT,
D3DX_PIXEL_FORMAT_B4G4R4X4_UNORM,
D3DX_PIXEL_FORMAT_B4G4R4A4_UNORM,
D3DX_PIXEL_FORMAT_B2G3R3A8_UNORM,
D3DX_PIXEL_FORMAT_B5G5R5A1_UNORM,
D3DX_PIXEL_FORMAT_R8G8B8X8_UNORM,
D3DX_PIXEL_FORMAT_R8G8B8A8_UNORM,
D3DX_PIXEL_FORMAT_B10G10R10A2_UNORM,
D3DX_PIXEL_FORMAT_R10G10B10A2_UNORM,
};
static enum d3dx_pixel_format_id d3dx_get_closest_d3dx_pixel_format_id(const enum d3dx_pixel_format_id *format_ids,
uint32_t format_ids_size, enum d3dx_pixel_format_id format_id)
{
const struct pixel_format_desc *fmt, *curfmt, *bestfmt = NULL;
int bestscore = INT_MIN, rgb_channels, a_channel, i, j;
BOOL alpha_only, rgb_only;
for (i = 0; i < format_ids_size; ++i)
{
if (format_ids[i] == format_id)
return format_id;
}
TRACE("Requested format is not directly supported, looking for the best alternative.\n");
switch (format_id)
{
case D3DX_PIXEL_FORMAT_P8_UINT:
case D3DX_PIXEL_FORMAT_P8_UINT_A8_UNORM:
case D3DX_PIXEL_FORMAT_DXT1_UNORM:
case D3DX_PIXEL_FORMAT_DXT2_UNORM:
case D3DX_PIXEL_FORMAT_DXT3_UNORM:
case D3DX_PIXEL_FORMAT_DXT4_UNORM:
case D3DX_PIXEL_FORMAT_DXT5_UNORM:
fmt = get_d3dx_pixel_format_info(D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM);
break;
default:
fmt = get_d3dx_pixel_format_info(format_id);
break;
}
alpha_only = rgb_only = FALSE;
if (fmt->a_type != CTYPE_EMPTY && fmt->rgb_type == CTYPE_EMPTY)
alpha_only = TRUE;
else if (fmt->a_type == CTYPE_EMPTY && fmt->rgb_type != CTYPE_EMPTY)
rgb_only = TRUE;
if (fmt->rgb_type == CTYPE_LUMA)
rgb_channels = 3;
else
rgb_channels = !!fmt->bits[1] + !!fmt->bits[2] + !!fmt->bits[3];
a_channel = !!fmt->bits[0];
for (i = 0; i < format_ids_size; ++i)
{
int cur_rgb_channels, cur_a_channel, score;
curfmt = get_d3dx_pixel_format_info(format_ids[i]);
if (!is_conversion_to_supported(curfmt))
continue;
if (alpha_only && curfmt->a_type == CTYPE_EMPTY)
continue;
if (rgb_only && curfmt->rgb_type == CTYPE_EMPTY)
continue;
if (fmt->rgb_type == CTYPE_SNORM && curfmt->rgb_type != CTYPE_SNORM)
continue;
cur_rgb_channels = !!curfmt->bits[1] + !!curfmt->bits[2] + !!curfmt->bits[3];
cur_a_channel = !!curfmt->bits[0];
/* Calculate a score for this format. */
score = 512 * (format_types_match(curfmt, fmt));
score -= 32 * abs(cur_a_channel - a_channel);
score -= 32 * abs(cur_rgb_channels - rgb_channels);
for (j = 0; j < 4; ++j)
{
int diff = curfmt->bits[j] - fmt->bits[j];
score -= (diff < 0 ? -diff * 8 : diff) * (j == 0 ? 1 : 2);
}
if (score > bestscore)
{
bestscore = score;
bestfmt = curfmt;
}
}
return (bestfmt) ? bestfmt->format : D3DX_PIXEL_FORMAT_COUNT;
}
HRESULT d3dx_save_pixels_to_memory(struct d3dx_pixels *src_pixels, const struct pixel_format_desc *src_fmt_desc,
enum d3dx_image_file_format file_format, const struct d3dx_buffer_wrapper *wrapper,
struct d3dx_buffer *dst_buffer)
{
enum d3dx_pixel_format_id dst_format = src_fmt_desc->format;
const struct pixel_format_desc *dst_fmt_desc;
uint32_t dst_row_pitch, dst_slice_pitch;
struct d3dx_pixels dst_pixels;
uint8_t *pixels, *tmp_buf;
HRESULT hr;
memset(dst_buffer, 0, sizeof(*dst_buffer));
pixels = tmp_buf = NULL;
switch (file_format)
{
case D3DX_IMAGE_FILE_FORMAT_DDS:
hr = dds_pixel_format_from_d3dx_pixel_format_id(NULL, dst_format);
if (FAILED(hr))
return hr;
break;
case D3DX_IMAGE_FILE_FORMAT_TGA:
dst_format = d3dx_get_closest_d3dx_pixel_format_id(tga_save_pixel_formats, ARRAY_SIZE(tga_save_pixel_formats),
dst_format);
break;
case D3DX_IMAGE_FILE_FORMAT_PNG:
dst_format = d3dx_get_closest_d3dx_pixel_format_id(png_save_pixel_formats, ARRAY_SIZE(png_save_pixel_formats),
dst_format);
break;
case D3DX_IMAGE_FILE_FORMAT_JPG:
dst_format = d3dx_get_closest_d3dx_pixel_format_id(jpg_save_pixel_formats, ARRAY_SIZE(jpg_save_pixel_formats),
dst_format);
break;
case D3DX_IMAGE_FILE_FORMAT_BMP:
case D3DX_IMAGE_FILE_FORMAT_DIB:
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(unimplemented_bmp_save_pixel_formats); ++i)
{
if (unimplemented_bmp_save_pixel_formats[i] == dst_format)
{
FIXME("Saving pixel format %d to BMP files is currently unsupported.\n", dst_format);
return E_NOTIMPL;
}
}
dst_format = d3dx_get_closest_d3dx_pixel_format_id(bmp_save_pixel_formats, ARRAY_SIZE(bmp_save_pixel_formats),
dst_format);
break;
}
default:
assert(0 && "Unexpected file format.");
return E_FAIL;
}
if (dst_format == D3DX_PIXEL_FORMAT_COUNT)
{
WARN("Failed to find adequate replacement format for saving.\n");
return D3DERR_INVALIDCALL;
}
if (dst_format != src_fmt_desc->format && !is_conversion_from_supported(src_fmt_desc))
{
FIXME("Cannot convert d3dx pixel format %d, can't save.\n", src_fmt_desc->format);
return E_NOTIMPL;
}
dst_fmt_desc = get_d3dx_pixel_format_info(dst_format);
src_pixels->size.depth = (file_format == D3DX_IMAGE_FILE_FORMAT_DDS) ? src_pixels->size.depth : 1;
hr = d3dx_calculate_pixels_size(dst_format, src_pixels->size.width, src_pixels->size.height, &dst_row_pitch,
&dst_slice_pitch);
if (FAILED(hr))
return hr;
switch (file_format)
{
case D3DX_IMAGE_FILE_FORMAT_DDS:
{
struct dds_header *header;
uint32_t header_size;
header_size = is_index_format(dst_fmt_desc) ? sizeof(*header) + DDS_PALETTE_SIZE : sizeof(*header);
hr = wrapper->d3dx_buffer_create((dst_slice_pitch * src_pixels->size.depth) + header_size, dst_buffer);
if (FAILED(hr))
return hr;
header = dst_buffer->buffer_data;
pixels = (uint8_t *)dst_buffer->buffer_data + header_size;
hr = d3dx_init_dds_header(header, D3DX_RESOURCE_TYPE_TEXTURE_2D, dst_format, &src_pixels->size, 1);
if (FAILED(hr))
goto exit;
if (is_index_format(dst_fmt_desc))
memcpy((uint8_t *)dst_buffer->buffer_data + sizeof(*header), src_pixels->palette, DDS_PALETTE_SIZE);
break;
}
case D3DX_IMAGE_FILE_FORMAT_TGA:
{
struct tga_header *header;
hr = wrapper->d3dx_buffer_create(dst_slice_pitch + sizeof(*header), dst_buffer);
if (FAILED(hr))
return hr;
header = dst_buffer->buffer_data;
pixels = (uint8_t *)dst_buffer->buffer_data + sizeof(*header);
memset(header, 0, sizeof(*header));
header->image_type = TGA_IMAGETYPE_TRUECOLOR;
header->width = src_pixels->size.width;
header->height = src_pixels->size.height;
header->image_descriptor = TGA_IMAGE_TOPTOBOTTOM;
header->depth = dst_fmt_desc->bytes_per_pixel * 8;
if (dst_fmt_desc->format == D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM)
header->image_descriptor |= 0x08;
break;
}
case D3DX_IMAGE_FILE_FORMAT_PNG:
case D3DX_IMAGE_FILE_FORMAT_JPG:
case D3DX_IMAGE_FILE_FORMAT_BMP:
case D3DX_IMAGE_FILE_FORMAT_DIB:
if (src_fmt_desc == dst_fmt_desc)
dst_pixels = *src_pixels;
else
pixels = tmp_buf = malloc(dst_slice_pitch);
break;
default:
break;
}
if (src_pixels->size.width != 0 && src_pixels->size.height != 0)
{
if (pixels)
{
const PALETTEENTRY *dst_palette = is_index_format(dst_fmt_desc) ? src_pixels->palette : NULL;
const RECT dst_rect = { 0, 0, src_pixels->size.width, src_pixels->size.height };
set_d3dx_pixels(&dst_pixels, pixels, dst_row_pitch, dst_slice_pitch, dst_palette,
src_pixels->size.width, src_pixels->size.height, src_pixels->size.depth, &dst_rect);
hr = d3dx_load_pixels_from_pixels(&dst_pixels, dst_fmt_desc, src_pixels, src_fmt_desc, D3DX_FILTER_NONE, 0);
if (FAILED(hr))
goto exit;
}
/* WIC path, encode the image. */
if (!dst_buffer->buffer_data)
{
IStream *wic_file = NULL;
uint32_t buf_size = 0;
hr = d3dx_pixels_save_wic(&dst_pixels, dst_fmt_desc, file_format, &wic_file, &buf_size);
if (FAILED(hr))
goto exit;
hr = wrapper->d3dx_buffer_create(buf_size, dst_buffer);
if (FAILED(hr))
{
IStream_Release(wic_file);
goto exit;
}
hr = IStream_Read(wic_file, dst_buffer->buffer_data, buf_size, NULL);
IStream_Release(wic_file);
if (FAILED(hr))
goto exit;
}
}
/* Return an empty buffer for size 0 images via WIC. */
else if (!dst_buffer->buffer_data)
{
FIXME("Returning empty buffer for size 0 image.\n");
hr = wrapper->d3dx_buffer_create(64, dst_buffer);
if (FAILED(hr))
goto exit;
}
exit:
free(tmp_buf);
if (FAILED(hr))
wrapper->d3dx_buffer_destroy(dst_buffer);
return hr;
}
static const uint8_t bmp_file_signature[] = { 'B', 'M' };
static const uint8_t jpg_file_signature[] = { 0xff, 0xd8 };
static const uint8_t png_file_signature[] = { 0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a };
static const uint8_t dds_file_signature[] = { 'D', 'D', 'S', ' ' };
static const uint8_t ppm_plain_file_signature[] = { 'P', '3' };
static const uint8_t ppm_raw_file_signature[] = { 'P', '6' };
static const uint8_t hdr_file_signature[] = { '#', '?', 'R', 'A', 'D', 'I', 'A', 'N', 'C', 'E', '\n' };
static const uint8_t pfm_color_file_signature[] = { 'P', 'F' };
static const uint8_t pfm_gray_file_signature[] = { 'P', 'f' };
/*
* If none of these match, the file is either DIB, TGA, or something we don't
* support.
*/
struct d3dx_file_format_signature
{
const uint8_t *file_signature;
uint32_t file_signature_len;
enum d3dx_image_file_format image_file_format;
};
static const struct d3dx_file_format_signature file_format_signatures[] =
{
{ bmp_file_signature, sizeof(bmp_file_signature), D3DX_IMAGE_FILE_FORMAT_BMP },
{ jpg_file_signature, sizeof(jpg_file_signature), D3DX_IMAGE_FILE_FORMAT_JPG },
{ png_file_signature, sizeof(png_file_signature), D3DX_IMAGE_FILE_FORMAT_PNG },
{ dds_file_signature, sizeof(dds_file_signature), D3DX_IMAGE_FILE_FORMAT_DDS },
{ ppm_plain_file_signature, sizeof(ppm_plain_file_signature), D3DX_IMAGE_FILE_FORMAT_PPM },
{ ppm_raw_file_signature, sizeof(ppm_raw_file_signature), D3DX_IMAGE_FILE_FORMAT_PPM },
{ hdr_file_signature, sizeof(hdr_file_signature), D3DX_IMAGE_FILE_FORMAT_HDR },
{ pfm_color_file_signature, sizeof(pfm_color_file_signature), D3DX_IMAGE_FILE_FORMAT_PFM },
{ pfm_gray_file_signature, sizeof(pfm_gray_file_signature), D3DX_IMAGE_FILE_FORMAT_PFM },
};
static BOOL d3dx_get_image_file_format_from_file_signature(const void *src_data, uint32_t src_data_size,
enum d3dx_image_file_format *out_iff)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(file_format_signatures); ++i)
{
const struct d3dx_file_format_signature *signature = &file_format_signatures[i];
if ((src_data_size >= signature->file_signature_len)
&& !memcmp(src_data, signature->file_signature, signature->file_signature_len))
{
*out_iff = signature->image_file_format;
return TRUE;
}
}
return FALSE;
}
static HRESULT d3dx_initialize_image_from_dds(const void *src_data, uint32_t src_data_size,
struct d3dx_image *image, uint32_t starting_mip_level)
{
uint32_t expected_src_data_size, header_size;
const struct dds_header *header = src_data;
BOOL is_indexed_fmt;
HRESULT hr;
if (src_data_size < sizeof(*header) || header->pixel_format.size != sizeof(header->pixel_format))
return D3DXERR_INVALIDDATA;
TRACE("File type is DDS.\n");
is_indexed_fmt = !!(header->pixel_format.flags & DDS_PF_INDEXED);
header_size = is_indexed_fmt ? sizeof(*header) + DDS_PALETTE_SIZE : sizeof(*header);
set_volume_struct(&image->size, header->width, header->height, 1);
image->mip_levels = header->miplevels ? header->miplevels : 1;
image->format = d3dx_pixel_format_id_from_dds_pixel_format(&header->pixel_format);
image->layer_count = 1;
if (image->format == D3DX_PIXEL_FORMAT_COUNT)
return D3DXERR_INVALIDDATA;
TRACE("Pixel format is %#x.\n", image->format);
if (header->flags & DDS_DEPTH)
{
image->size.depth = max(header->depth, 1);
image->resource_type = D3DX_RESOURCE_TYPE_TEXTURE_3D;
}
else if (header->caps2 & DDS_CAPS2_CUBEMAP)
{
if ((header->caps2 & DDS_CAPS2_CUBEMAP_ALL_FACES) != DDS_CAPS2_CUBEMAP_ALL_FACES)
{
WARN("Tried to load a partial cubemap DDS file.\n");
return D3DXERR_INVALIDDATA;
}
image->layer_count = 6;
image->resource_type = D3DX_RESOURCE_TYPE_CUBE_TEXTURE;
}
else
{
image->resource_type = D3DX_RESOURCE_TYPE_TEXTURE_2D;
}
image->layer_pitch = d3dx_calculate_layer_pixels_size(image->format, image->size.width, image->size.height,
image->size.depth, image->mip_levels);
if (!image->layer_pitch)
return D3DXERR_INVALIDDATA;
expected_src_data_size = (image->layer_pitch * image->layer_count) + header_size;
if (src_data_size < expected_src_data_size)
{
WARN("File is too short %u, expected at least %u bytes.\n", src_data_size, expected_src_data_size);
return D3DXERR_INVALIDDATA;
}
image->palette = (is_indexed_fmt) ? (PALETTEENTRY *)(((uint8_t *)src_data) + sizeof(*header)) : NULL;
image->pixels = ((BYTE *)src_data) + header_size;
image->image_file_format = D3DX_IMAGE_FILE_FORMAT_DDS;
if (starting_mip_level && (image->mip_levels > 1))
{
uint32_t i, row_pitch, slice_pitch, initial_mip_levels;
const struct volume initial_size = image->size;
initial_mip_levels = image->mip_levels;
for (i = 0; i < starting_mip_level; i++)
{
hr = d3dx_calculate_pixels_size(image->format, image->size.width, image->size.height, &row_pitch, &slice_pitch);
if (FAILED(hr))
return hr;
image->pixels += slice_pitch * image->size.depth;
d3dx_get_next_mip_level_size(&image->size);
if (--image->mip_levels == 1)
break;
}
TRACE("Requested starting mip level %u, actual starting mip level is %u (of %u total in image).\n",
starting_mip_level, (initial_mip_levels - image->mip_levels), initial_mip_levels);
TRACE("Original dimensions %s, new dimensions %s.\n", debug_volume(&initial_size), debug_volume(&image->size));
}
return D3D_OK;
}
static BOOL convert_dib_to_bmp(const void **data, unsigned int *size)
{
ULONG header_size;
ULONG count = 0;
ULONG offset;
BITMAPFILEHEADER *header;
BYTE *new_data;
UINT new_size;
if ((*size < 4) || (*size < (header_size = *(ULONG*)*data)))
return FALSE;
if ((header_size == sizeof(BITMAPINFOHEADER)) ||
(header_size == sizeof(BITMAPV4HEADER)) ||
(header_size == sizeof(BITMAPV5HEADER)) ||
(header_size == 64 /* sizeof(BITMAPCOREHEADER2) */))
{
/* All structures begin with the same memory layout as BITMAPINFOHEADER */
BITMAPINFOHEADER *info_header = (BITMAPINFOHEADER*)*data;
count = info_header->biClrUsed;
if (!count && info_header->biBitCount <= 8)
count = 1 << info_header->biBitCount;
offset = sizeof(BITMAPFILEHEADER) + header_size + sizeof(RGBQUAD) * count;
/* For BITMAPINFOHEADER with BI_BITFIELDS compression, there are 3 additional color masks after header */
if ((info_header->biSize == sizeof(BITMAPINFOHEADER)) && (info_header->biCompression == BI_BITFIELDS))
offset += 3 * sizeof(DWORD);
}
else if (header_size == sizeof(BITMAPCOREHEADER))
{
BITMAPCOREHEADER *core_header = (BITMAPCOREHEADER*)*data;
if (core_header->bcBitCount <= 8)
count = 1 << core_header->bcBitCount;
offset = sizeof(BITMAPFILEHEADER) + header_size + sizeof(RGBTRIPLE) * count;
}
else
{
return FALSE;
}
TRACE("Converting DIB file to BMP\n");
new_size = *size + sizeof(BITMAPFILEHEADER);
new_data = malloc(new_size);
CopyMemory(new_data + sizeof(BITMAPFILEHEADER), *data, *size);
/* Add BMP header */
header = (BITMAPFILEHEADER*)new_data;
header->bfType = 0x4d42; /* BM */
header->bfSize = new_size;
header->bfReserved1 = 0;
header->bfReserved2 = 0;
header->bfOffBits = offset;
/* Update input data */
*data = new_data;
*size = new_size;
return TRUE;
}
/* windowscodecs always returns xRGB, but we should return ARGB if and only if
* at least one pixel has a non-zero alpha component. */
static BOOL image_is_argb(IWICBitmapFrameDecode *frame, struct d3dx_image *image)
{
unsigned int size, i;
BYTE *buffer;
HRESULT hr;
if (image->format != D3DX_PIXEL_FORMAT_B8G8R8X8_UNORM || image->image_file_format != D3DX_IMAGE_FILE_FORMAT_BMP)
return FALSE;
size = image->size.width * image->size.height * 4;
if (!(buffer = malloc(size)))
return FALSE;
if (FAILED(hr = IWICBitmapFrameDecode_CopyPixels(frame, NULL, image->size.width * 4, size, buffer)))
{
ERR("Failed to copy pixels, hr %#lx.\n", hr);
free(buffer);
return FALSE;
}
for (i = 0; i < image->size.width * image->size.height; ++i)
{
if (buffer[i * 4 + 3])
{
free(buffer);
return TRUE;
}
}
free(buffer);
return FALSE;
}
const char *debug_d3dx_image_file_format(enum d3dx_image_file_format format)
{
switch (format)
{
#define FMT_TO_STR(format) case format: return #format
FMT_TO_STR(D3DX_IMAGE_FILE_FORMAT_BMP);
FMT_TO_STR(D3DX_IMAGE_FILE_FORMAT_JPG);
FMT_TO_STR(D3DX_IMAGE_FILE_FORMAT_TGA);
FMT_TO_STR(D3DX_IMAGE_FILE_FORMAT_PNG);
FMT_TO_STR(D3DX_IMAGE_FILE_FORMAT_DDS);
FMT_TO_STR(D3DX_IMAGE_FILE_FORMAT_PPM);
FMT_TO_STR(D3DX_IMAGE_FILE_FORMAT_DIB);
FMT_TO_STR(D3DX_IMAGE_FILE_FORMAT_HDR);
FMT_TO_STR(D3DX_IMAGE_FILE_FORMAT_PFM);
#undef FMT_TO_STR
default:
return "unrecognized";
}
}
static HRESULT d3dx_image_wic_frame_decode(struct d3dx_image *image,
IWICImagingFactory *wic_factory, IWICBitmapFrameDecode *bitmap_frame)
{
const struct pixel_format_desc *fmt_desc;
uint32_t row_pitch, slice_pitch;
IWICPalette *wic_palette = NULL;
PALETTEENTRY *palette = NULL;
WICColor *colors = NULL;
BYTE *buffer = NULL;
HRESULT hr;
fmt_desc = get_d3dx_pixel_format_info(image->format);
hr = d3dx_calculate_pixels_size(image->format, image->size.width, image->size.height, &row_pitch, &slice_pitch);
if (FAILED(hr))
return hr;
/* Allocate a buffer for our image. */
if (!(buffer = malloc(slice_pitch)))
return E_OUTOFMEMORY;
hr = IWICBitmapFrameDecode_CopyPixels(bitmap_frame, NULL, row_pitch, slice_pitch, buffer);
if (FAILED(hr))
{
free(buffer);
return hr;
}
if (is_index_format(fmt_desc))
{
uint32_t nb_colors, i;
hr = IWICImagingFactory_CreatePalette(wic_factory, &wic_palette);
if (FAILED(hr))
goto exit;
hr = IWICBitmapFrameDecode_CopyPalette(bitmap_frame, wic_palette);
if (FAILED(hr))
goto exit;
hr = IWICPalette_GetColorCount(wic_palette, &nb_colors);
if (FAILED(hr))
goto exit;
colors = malloc(nb_colors * sizeof(colors[0]));
palette = malloc(256 * sizeof(palette[0]));
if (!colors || !palette)
{
hr = E_OUTOFMEMORY;
goto exit;
}
hr = IWICPalette_GetColors(wic_palette, nb_colors, colors, &nb_colors);
if (FAILED(hr))
goto exit;
/* Convert colors from WICColor (ARGB) to PALETTEENTRY (ABGR) */
for (i = 0; i < nb_colors; i++)
{
palette[i].peRed = (colors[i] >> 16) & 0xff;
palette[i].peGreen = (colors[i] >> 8) & 0xff;
palette[i].peBlue = colors[i] & 0xff;
palette[i].peFlags = (colors[i] >> 24) & 0xff; /* peFlags is the alpha component in DX8 and higher */
}
if (nb_colors < 256)
memset(&palette[nb_colors], 0xff, sizeof(*palette) * (256 - nb_colors));
}
image->image_buf = image->pixels = buffer;
image->image_palette = image->palette = palette;
exit:
free(colors);
if (image->image_buf != buffer)
free(buffer);
if (image->image_palette != palette)
free(palette);
if (wic_palette)
IWICPalette_Release(wic_palette);
return hr;
}
static HRESULT d3dx_initialize_image_from_wic(const void *src_data, uint32_t src_data_size,
struct d3dx_image *image, enum d3dx_image_file_format d3dx_file_format, uint32_t flags)
{
const GUID *container_format_guid = wic_container_guid_from_d3dx_file_format(d3dx_file_format);
IWICBitmapFrameDecode *bitmap_frame = NULL;
IWICBitmapDecoder *bitmap_decoder = NULL;
IWICImagingFactory *wic_factory;
WICPixelFormatGUID pixel_format;
IWICStream *wic_stream = NULL;
uint32_t frame_count = 0;
HRESULT hr;
hr = WICCreateImagingFactory_Proxy(WINCODEC_SDK_VERSION, &wic_factory);
if (FAILED(hr))
return hr;
hr = IWICImagingFactory_CreateDecoder(wic_factory, container_format_guid, NULL, &bitmap_decoder);
if (FAILED(hr))
goto exit;
hr = IWICImagingFactory_CreateStream(wic_factory, &wic_stream);
if (FAILED(hr))
goto exit;
hr = IWICStream_InitializeFromMemory(wic_stream, (BYTE *)src_data, src_data_size);
if (FAILED(hr))
goto exit;
hr = IWICBitmapDecoder_Initialize(bitmap_decoder, (IStream *)wic_stream, 0);
if (FAILED(hr))
goto exit;
hr = IWICBitmapDecoder_GetFrameCount(bitmap_decoder, &frame_count);
if (FAILED(hr) || (SUCCEEDED(hr) && !frame_count))
{
hr = D3DXERR_INVALIDDATA;
goto exit;
}
image->image_file_format = d3dx_file_format;
hr = IWICBitmapDecoder_GetFrame(bitmap_decoder, 0, &bitmap_frame);
if (FAILED(hr))
goto exit;
hr = IWICBitmapFrameDecode_GetSize(bitmap_frame, &image->size.width, &image->size.height);
if (FAILED(hr))
goto exit;
hr = IWICBitmapFrameDecode_GetPixelFormat(bitmap_frame, &pixel_format);
if (FAILED(hr))
goto exit;
image->format = d3dx_pixel_format_id_from_wic_pixel_format(&pixel_format);
switch (image->format)
{
case D3DX_PIXEL_FORMAT_P2_UINT:
if (image->image_file_format != D3DX_IMAGE_FILE_FORMAT_BMP)
break;
/* Fall through. */
case D3DX_PIXEL_FORMAT_COUNT:
WARN("Unsupported pixel format %s.\n", debugstr_guid(&pixel_format));
hr = D3DXERR_INVALIDDATA;
goto exit;
default:
break;
}
if (image_is_argb(bitmap_frame, image))
image->format = D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM;
if (!(flags & D3DX_IMAGE_INFO_ONLY))
{
hr = d3dx_image_wic_frame_decode(image, wic_factory, bitmap_frame);
if (FAILED(hr))
goto exit;
}
image->size.depth = 1;
image->mip_levels = 1;
image->layer_count = 1;
image->resource_type = D3DX_RESOURCE_TYPE_TEXTURE_2D;
exit:
if (bitmap_frame)
IWICBitmapFrameDecode_Release(bitmap_frame);
if (bitmap_decoder)
IWICBitmapDecoder_Release(bitmap_decoder);
if (wic_stream)
IWICStream_Release(wic_stream);
IWICImagingFactory_Release(wic_factory);
return hr;
}
static enum d3dx_pixel_format_id d3dx_get_tga_format_for_bpp(uint8_t bpp)
{
switch (bpp)
{
case 15: return D3DX_PIXEL_FORMAT_B5G5R5X1_UNORM;
case 16: return D3DX_PIXEL_FORMAT_B5G5R5A1_UNORM;
case 24: return D3DX_PIXEL_FORMAT_B8G8R8_UNORM;
case 32: return D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM;
default:
WARN("Unhandled bpp %u for targa.\n", bpp);
return D3DX_PIXEL_FORMAT_COUNT;
}
}
static HRESULT d3dx_image_tga_rle_decode_row(const uint8_t **src, uint32_t src_bytes_left, uint32_t row_width,
uint32_t bytes_per_pixel, uint8_t *dst_row)
{
const uint8_t *src_ptr = *src;
uint32_t pixel_count = 0;
while (pixel_count != row_width)
{
uint32_t rle_count = (src_ptr[0] & 0x7f) + 1;
uint32_t rle_packet_size = 1;
rle_packet_size += (src_ptr[0] & 0x80) ? bytes_per_pixel : (bytes_per_pixel * rle_count);
if ((rle_packet_size > src_bytes_left) || (pixel_count + rle_count) > row_width)
return D3DXERR_INVALIDDATA;
if (src_ptr[0] & 0x80)
{
uint32_t i;
for (i = 0; i < rle_count; ++i)
memcpy(&dst_row[(pixel_count + i) * bytes_per_pixel], src_ptr + 1, bytes_per_pixel);
}
else
{
memcpy(&dst_row[pixel_count * bytes_per_pixel], src_ptr + 1, rle_packet_size - 1);
}
src_ptr += rle_packet_size;
src_bytes_left -= rle_packet_size;
pixel_count += rle_count;
if (!src_bytes_left && pixel_count != row_width)
return D3DXERR_INVALIDDATA;
}
*src = src_ptr;
return D3D_OK;
}
struct d3dx_color_key;
static void convert_argb_pixels(const BYTE *src, UINT src_row_pitch, UINT src_slice_pitch, const struct volume *src_size,
const struct pixel_format_desc *src_format, BYTE *dst, UINT dst_row_pitch, UINT dst_slice_pitch,
const struct volume *dst_size, const struct pixel_format_desc *dst_format, const struct d3dx_color_key *color_key,
const PALETTEENTRY *palette);
static HRESULT d3dx_image_tga_decode(const void *src_data, uint32_t src_data_size, uint32_t src_header_size,
struct d3dx_image *image)
{
const struct pixel_format_desc *fmt_desc = get_d3dx_pixel_format_info(image->format);
const struct tga_header *header = (const struct tga_header *)src_data;
const BOOL right_to_left = !!(header->image_descriptor & TGA_IMAGE_RIGHTTOLEFT);
const BOOL bottom_to_top = !(header->image_descriptor & TGA_IMAGE_TOPTOBOTTOM);
const BOOL is_rle = !!(header->image_type & TGA_IMAGETYPE_RLE);
uint8_t *img_buf = NULL, *src_row = NULL;
uint32_t row_pitch, slice_pitch, i;
PALETTEENTRY *palette = NULL;
const uint8_t *src_pos;
HRESULT hr;
hr = d3dx_calculate_pixels_size(image->format, image->size.width, image->size.height, &row_pitch, &slice_pitch);
if (FAILED(hr))
return hr;
/* File is too small. */
if (!is_rle && (src_header_size + slice_pitch) > src_data_size)
return D3DXERR_INVALIDDATA;
if (image->format == D3DX_PIXEL_FORMAT_P8_UINT)
{
const uint8_t *src_palette = ((const uint8_t *)src_data) + sizeof(*header) + header->id_length;
const struct volume image_map_size = { header->color_map_length, 1, 1 };
uint32_t src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
const struct pixel_format_desc *src_desc, *dst_desc;
if (!(palette = malloc(sizeof(*palette) * 256)))
return E_OUTOFMEMORY;
/*
* Convert from a TGA colormap to PALETTEENTRY. TGA is BGRA,
* PALETTEENTRY is RGBA.
*/
src_desc = get_d3dx_pixel_format_info(d3dx_get_tga_format_for_bpp(header->color_map_entrysize));
hr = d3dx_calculate_pixels_size(src_desc->format, header->color_map_length, 1, &src_row_pitch, &src_slice_pitch);
if (FAILED(hr))
goto exit;
dst_desc = get_d3dx_pixel_format_info(D3DX_PIXEL_FORMAT_R8G8B8A8_UNORM);
d3dx_calculate_pixels_size(dst_desc->format, 256, 1, &dst_row_pitch, &dst_slice_pitch);
convert_argb_pixels(src_palette, src_row_pitch, src_slice_pitch, &image_map_size, src_desc, (BYTE *)palette,
dst_row_pitch, dst_slice_pitch, &image_map_size, dst_desc, NULL, NULL);
/* Initialize unused palette entries to 0xff. */
if (header->color_map_length < 256)
memset(&palette[header->color_map_length], 0xff, sizeof(*palette) * (256 - header->color_map_length));
}
if (!is_rle && !bottom_to_top && !right_to_left)
{
image->pixels = (uint8_t *)src_data + src_header_size;
image->image_palette = image->palette = palette;
return D3D_OK;
}
if (!(img_buf = malloc(slice_pitch)))
{
hr = E_OUTOFMEMORY;
goto exit;
}
/* Allocate an extra row to use as a temporary buffer. */
if (is_rle)
{
if (!(src_row = malloc(row_pitch)))
{
hr = E_OUTOFMEMORY;
goto exit;
}
}
src_pos = (const uint8_t *)src_data + src_header_size;
for (i = 0; i < image->size.height; ++i)
{
const uint32_t dst_row_idx = bottom_to_top ? (image->size.height - i - 1) : i;
uint8_t *dst_row = img_buf + (dst_row_idx * row_pitch);
if (is_rle)
{
hr = d3dx_image_tga_rle_decode_row(&src_pos, src_data_size - (src_pos - (const uint8_t *)src_data),
image->size.width, fmt_desc->bytes_per_pixel, src_row);
if (FAILED(hr))
goto exit;
}
else
{
src_row = (uint8_t *)src_pos;
src_pos += row_pitch;
}
if (right_to_left)
{
const uint8_t *src_pixel = &src_row[((image->size.width - 1)) * fmt_desc->bytes_per_pixel];
uint8_t *dst_pixel = dst_row;
uint32_t j;
for (j = 0; j < image->size.width; ++j)
{
memcpy(dst_pixel, src_pixel, fmt_desc->bytes_per_pixel);
src_pixel -= fmt_desc->bytes_per_pixel;
dst_pixel += fmt_desc->bytes_per_pixel;
}
}
else
{
memcpy(dst_row, src_row, row_pitch);
}
}
image->image_buf = image->pixels = img_buf;
image->image_palette = image->palette = palette;
exit:
if (is_rle)
free(src_row);
if (img_buf && (image->image_buf != img_buf))
free(img_buf);
if (palette && (image->image_palette != palette))
free(palette);
return hr;
}
static HRESULT d3dx_initialize_image_from_tga(const void *src_data, uint32_t src_data_size, struct d3dx_image *image,
uint32_t flags)
{
const struct tga_header *header = (const struct tga_header *)src_data;
uint32_t expected_header_size = sizeof(*header);
if (src_data_size < sizeof(*header))
return D3DXERR_INVALIDDATA;
expected_header_size += header->id_length;
expected_header_size += header->color_map_length * ((header->color_map_entrysize + 7) / CHAR_BIT);
if (src_data_size < expected_header_size)
return D3DXERR_INVALIDDATA;
if (header->color_map_type && ((header->color_map_type > 1) || (!header->color_map_length)
|| (d3dx_get_tga_format_for_bpp(header->color_map_entrysize) == D3DX_PIXEL_FORMAT_COUNT)))
return D3DXERR_INVALIDDATA;
switch (header->image_type & TGA_IMAGETYPE_MASK)
{
case TGA_IMAGETYPE_COLORMAPPED:
if (header->depth != 8 || !header->color_map_type)
return D3DXERR_INVALIDDATA;
image->format = D3DX_PIXEL_FORMAT_P8_UINT;
break;
case TGA_IMAGETYPE_TRUECOLOR:
if ((image->format = d3dx_get_tga_format_for_bpp(header->depth)) == D3DX_PIXEL_FORMAT_COUNT)
return D3DXERR_INVALIDDATA;
break;
case TGA_IMAGETYPE_GRAYSCALE:
if (header->depth != 8)
return D3DXERR_INVALIDDATA;
image->format = D3DX_PIXEL_FORMAT_L8_UNORM;
break;
default:
return D3DXERR_INVALIDDATA;
}
set_volume_struct(&image->size, header->width, header->height, 1);
image->mip_levels = 1;
image->layer_count = 1;
image->resource_type = D3DX_RESOURCE_TYPE_TEXTURE_2D;
image->image_file_format = D3DX_IMAGE_FILE_FORMAT_TGA;
if (!(flags & D3DX_IMAGE_INFO_ONLY))
return d3dx_image_tga_decode(src_data, src_data_size, expected_header_size, image);
return D3D_OK;
}
HRESULT d3dx_image_init(const void *src_data, uint32_t src_data_size, struct d3dx_image *image,
uint32_t starting_mip_level, uint32_t flags)
{
enum d3dx_image_file_format iff = D3DX_IMAGE_FILE_FORMAT_FORCE_DWORD;
HRESULT hr;
if (!src_data || !src_data_size || !image)
return D3DERR_INVALIDCALL;
memset(image, 0, sizeof(*image));
if (!d3dx_get_image_file_format_from_file_signature(src_data, src_data_size, &iff))
{
uint32_t src_image_size = src_data_size;
const void *src_image = src_data;
if (convert_dib_to_bmp(&src_image, &src_image_size))
{
hr = d3dx_image_init(src_image, src_image_size, image, starting_mip_level, flags);
free((void *)src_image);
if (SUCCEEDED(hr))
image->image_file_format = D3DX_IMAGE_FILE_FORMAT_DIB;
return hr;
}
/* Last resort, try TGA. */
return d3dx_initialize_image_from_tga(src_data, src_data_size, image, flags);
}
switch (iff)
{
case D3DX_IMAGE_FILE_FORMAT_BMP:
case D3DX_IMAGE_FILE_FORMAT_JPG:
case D3DX_IMAGE_FILE_FORMAT_PNG:
hr = d3dx_initialize_image_from_wic(src_data, src_data_size, image, iff, flags);
break;
case D3DX_IMAGE_FILE_FORMAT_DDS:
hr = d3dx_initialize_image_from_dds(src_data, src_data_size, image, starting_mip_level);
break;
case D3DX_IMAGE_FILE_FORMAT_PPM:
case D3DX_IMAGE_FILE_FORMAT_HDR:
case D3DX_IMAGE_FILE_FORMAT_PFM:
WARN("Unsupported file format %s.\n", debug_d3dx_image_file_format(iff));
hr = E_NOTIMPL;
break;
case D3DX_IMAGE_FILE_FORMAT_FORCE_DWORD:
ERR("Unrecognized file format.\n");
hr = D3DXERR_INVALIDDATA;
break;
default:
assert(0);
return E_FAIL;
}
return hr;
}
void d3dx_image_cleanup(struct d3dx_image *image)
{
free(image->image_buf);
free(image->image_palette);
}
HRESULT d3dx_image_get_pixels(struct d3dx_image *image, uint32_t layer, uint32_t mip_level,
struct d3dx_pixels *pixels)
{
struct volume mip_level_size = image->size;
const BYTE *pixels_ptr = image->pixels;
uint32_t row_pitch, slice_pitch, i;
RECT unaligned_rect;
HRESULT hr = S_OK;
if (mip_level >= image->mip_levels)
{
ERR("Tried to retrieve mip level %u, but image only has %u mip levels.\n", mip_level, image->mip_levels);
return E_FAIL;
}
if (layer >= image->layer_count)
{
ERR("Tried to retrieve layer %u, but image only has %u layers.\n", layer, image->layer_count);
return E_FAIL;
}
slice_pitch = row_pitch = 0;
for (i = 0; i < image->mip_levels; i++)
{
hr = d3dx_calculate_pixels_size(image->format, mip_level_size.width, mip_level_size.height, &row_pitch, &slice_pitch);
if (FAILED(hr))
return hr;
if (i == mip_level)
break;
pixels_ptr += slice_pitch * mip_level_size.depth;
d3dx_get_next_mip_level_size(&mip_level_size);
}
pixels_ptr += (layer * image->layer_pitch);
SetRect(&unaligned_rect, 0, 0, mip_level_size.width, mip_level_size.height);
set_d3dx_pixels(pixels, pixels_ptr, row_pitch, slice_pitch, image->palette, mip_level_size.width,
mip_level_size.height, mip_level_size.depth, &unaligned_rect);
return D3D_OK;
}
unsigned short float_32_to_16(const float in)
{
int exp = 0, origexp;
float tmp = fabsf(in);
int sign = (copysignf(1, in) < 0);
unsigned int mantissa;
unsigned short ret;
/* Deal with special numbers */
if (isinf(in)) return (sign ? 0xffff : 0x7fff);
if (isnan(in)) return (sign ? 0xffff : 0x7fff);
if (in == 0.0f) return (sign ? 0x8000 : 0x0000);
if (tmp < (float)(1u << 10))
{
do
{
tmp *= 2.0f;
exp--;
} while (tmp < (float)(1u << 10));
}
else if (tmp >= (float)(1u << 11))
{
do
{
tmp /= 2.0f;
exp++;
} while (tmp >= (float)(1u << 11));
}
exp += 10; /* Normalize the mantissa */
exp += 15; /* Exponent is encoded with excess 15 */
origexp = exp;
mantissa = (unsigned int) tmp;
if ((tmp - mantissa == 0.5f && mantissa % 2 == 1) || /* round half to even */
(tmp - mantissa > 0.5f))
{
mantissa++; /* round to nearest, away from zero */
}
if (mantissa == 2048)
{
mantissa = 1024;
exp++;
}
if (exp > 31)
{
/* too big */
ret = 0x7fff; /* INF */
}
else if (exp <= 0)
{
unsigned int rounding = 0;
/* Denormalized half float */
/* return 0x0000 (=0.0) for numbers too small to represent in half floats */
if (exp < -11)
return (sign ? 0x8000 : 0x0000);
exp = origexp;
/* the 13 extra bits from single precision are used for rounding */
mantissa = (unsigned int)(tmp * (1u << 13));
mantissa >>= 1 - exp; /* denormalize */
mantissa -= ~(mantissa >> 13) & 1; /* round half to even */
/* remove 13 least significant bits to get half float precision */
mantissa >>= 12;
rounding = mantissa & 1;
mantissa >>= 1;
ret = mantissa + rounding;
}
else
{
ret = (exp << 10) | (mantissa & 0x3ff);
}
ret |= ((sign ? 1 : 0) << 15); /* Add the sign */
return ret;
}
/* Native d3dx9's D3DXFloat16to32Array lacks support for NaN and Inf. Specifically, e = 16 is treated as a
* regular number - e.g., 0x7fff is converted to 131008.0 and 0xffff to -131008.0. */
float float_16_to_32(const unsigned short in)
{
const unsigned short s = (in & 0x8000);
const unsigned short e = (in & 0x7C00) >> 10;
const unsigned short m = in & 0x3FF;
const float sgn = (s ? -1.0f : 1.0f);
if (e == 0)
{
if (m == 0) return sgn * 0.0f; /* +0.0 or -0.0 */
else return sgn * powf(2, -14.0f) * (m / 1024.0f);
}
else
{
return sgn * powf(2, e - 15.0f) * (1.0f + (m / 1024.0f));
}
}
struct argb_conversion_info
{
const struct pixel_format_desc *srcformat;
const struct pixel_format_desc *destformat;
DWORD srcshift[4], destshift[4];
DWORD srcmask[4], destmask[4];
BOOL process_channel[4];
DWORD channelmask;
};
static void init_argb_conversion_info(const struct pixel_format_desc *srcformat, const struct pixel_format_desc *destformat, struct argb_conversion_info *info)
{
UINT i;
ZeroMemory(info->process_channel, 4 * sizeof(BOOL));
info->channelmask = 0;
info->srcformat = srcformat;
info->destformat = destformat;
for(i = 0;i < 4;i++) {
/* srcshift is used to extract the _relevant_ components */
info->srcshift[i] = srcformat->shift[i] + max( srcformat->bits[i] - destformat->bits[i], 0);
/* destshift is used to move the components to the correct position */
info->destshift[i] = destformat->shift[i] + max(destformat->bits[i] - srcformat->bits[i], 0);
info->srcmask[i] = ((1 << srcformat->bits[i]) - 1) << srcformat->shift[i];
info->destmask[i] = ((1 << destformat->bits[i]) - 1) << destformat->shift[i];
/* channelmask specifies bits which aren't used in the source format but in the destination one */
if(destformat->bits[i]) {
if(srcformat->bits[i]) info->process_channel[i] = TRUE;
else info->channelmask |= info->destmask[i];
}
}
}
/************************************************************
* get_relevant_argb_components
*
* Extracts the relevant components from the source color and
* drops the less significant bits if they aren't used by the destination format.
*/
static void get_relevant_argb_components(const struct argb_conversion_info *info, const BYTE *col, DWORD *out)
{
unsigned int i, j;
unsigned int component, mask;
for (i = 0; i < 4; ++i)
{
if (!info->process_channel[i])
continue;
component = 0;
mask = info->srcmask[i];
for (j = 0; j < 4 && mask; ++j)
{
if (info->srcshift[i] < j * 8)
component |= (col[j] & mask) << (j * 8 - info->srcshift[i]);
else
component |= (col[j] & mask) >> (info->srcshift[i] - j * 8);
mask >>= 8;
}
out[i] = component;
}
}
static float d3dx_clamp(float value, float min_value, float max_value)
{
if (isnan(value))
return max_value;
return value < min_value ? min_value : value > max_value ? max_value : value;
}
/************************************************************
* make_argb_color
*
* Recombines the output of get_relevant_argb_components and converts
* it to the destination format.
*/
static DWORD make_argb_color(const struct argb_conversion_info *info, const DWORD *in)
{
UINT i;
DWORD val = 0;
for(i = 0;i < 4;i++) {
if(info->process_channel[i]) {
/* necessary to make sure that e.g. an X4R4G4B4 white maps to an R8G8B8 white instead of 0xf0f0f0 */
signed int shift;
for(shift = info->destshift[i]; shift > info->destformat->shift[i]; shift -= info->srcformat->bits[i]) val |= in[i] << shift;
val |= (in[i] >> (info->destformat->shift[i] - shift)) << info->destformat->shift[i];
}
}
val |= info->channelmask; /* new channels are set to their maximal value */
return val;
}
static enum range get_range_for_component_type(enum component_type type)
{
switch (type)
{
case CTYPE_SNORM:
return RANGE_SNORM;
case CTYPE_LUMA:
case CTYPE_INDEX:
case CTYPE_UNORM:
return RANGE_UNORM;
case CTYPE_EMPTY:
case CTYPE_FLOAT:
return RANGE_FULL;
default:
assert(0);
return RANGE_FULL;
}
}
/* It doesn't work for components bigger than 32 bits (or somewhat smaller but unaligned). */
void format_to_d3dx_color(const struct pixel_format_desc *format, const BYTE *src, const PALETTEENTRY *palette,
struct d3dx_color *dst)
{
DWORD mask, tmp;
unsigned int c;
dst->rgb_range = get_range_for_component_type(format->rgb_type);
dst->a_range = get_range_for_component_type(format->a_type);
for (c = 0; c < 4; ++c)
{
const enum component_type dst_ctype = !c ? format->a_type : format->rgb_type;
static const unsigned int component_offsets[4] = {3, 0, 1, 2};
float *dst_component = &dst->value.x + component_offsets[c];
if (format->bits[c])
{
mask = ~0u >> (32 - format->bits[c]);
memcpy(&tmp, src + format->shift[c] / 8,
min(sizeof(DWORD), (format->shift[c] % 8 + format->bits[c] + 7) / 8));
tmp = (tmp >> (format->shift[c] % 8)) & mask;
switch (dst_ctype)
{
case CTYPE_FLOAT:
if (format->bits[c] == 16)
*dst_component = float_16_to_32(tmp);
else
*dst_component = *(float *)&tmp;
break;
case CTYPE_INDEX:
*dst_component = (&palette[tmp].peRed)[component_offsets[c]] / 255.0f;
break;
case CTYPE_LUMA:
case CTYPE_UNORM:
*dst_component = (float)tmp / mask;
break;
case CTYPE_SNORM:
{
const uint32_t sign_bit = (1u << (format->bits[c] - 1));
uint32_t tmp_extended = (tmp & sign_bit) ? (tmp | ~(sign_bit - 1)) : tmp;
/*
* In order to clamp to an even range, we need to ignore
* the maximum negative value.
*/
if (tmp == sign_bit)
tmp_extended |= 1;
*dst_component = (float)(((int32_t)tmp_extended)) / (sign_bit - 1);
break;
}
default:
break;
}
}
else if (dst_ctype == CTYPE_LUMA)
{
assert(format->bits[1]);
*dst_component = dst->value.x;
}
else
{
*dst_component = 1.0f;
}
}
}
/* It doesn't work for components bigger than 32 bits. */
void format_from_d3dx_color(const struct pixel_format_desc *format, const struct d3dx_color *src, BYTE *dst)
{
DWORD v, mask32;
unsigned int c, i;
memset(dst, 0, format->bytes_per_pixel);
for (c = 0; c < 4; ++c)
{
const enum component_type dst_ctype = !c ? format->a_type : format->rgb_type;
static const unsigned int component_offsets[4] = {3, 0, 1, 2};
const float src_component = *(&src->value.x + component_offsets[c]);
const enum range src_range = !c ? src->a_range : src->rgb_range;
if (!format->bits[c])
continue;
mask32 = ~0u >> (32 - format->bits[c]);
switch (dst_ctype)
{
case CTYPE_FLOAT:
if (format->bits[c] == 16)
v = float_32_to_16(src_component);
else
v = *(DWORD *)&src_component;
break;
case CTYPE_LUMA:
{
float val = src->value.x * 0.2125f + src->value.y * 0.7154f + src->value.z * 0.0721f;
if (src_range == RANGE_SNORM)
val = (val + 1.0f) / 2.0f;
v = d3dx_clamp(val, 0.0f, 1.0f) * ((1u << format->bits[c]) - 1) + 0.5f;
break;
}
case CTYPE_UNORM:
{
float val = src_component;
if (src_range == RANGE_SNORM)
val = (val + 1.0f) / 2.0f;
v = d3dx_clamp(val, 0.0f, 1.0f) * ((1u << format->bits[c]) - 1) + 0.5f;
break;
}
case CTYPE_SNORM:
{
const uint32_t max_value = (1u << (format->bits[c] - 1)) - 1;
float val = src_component;
if (src_range == RANGE_UNORM)
val = (val * 2.0f) - 1.0f;
v = d3dx_clamp(val, -1.0f, 1.0f) * max_value + 0.5f;
break;
}
/* We shouldn't be trying to output to CTYPE_INDEX. */
case CTYPE_INDEX:
assert(0);
break;
default:
v = 0;
break;
}
for (i = format->shift[c] / 8 * 8; i < format->shift[c] + format->bits[c]; i += 8)
{
BYTE mask, byte;
if (format->shift[c] > i)
{
mask = mask32 << (format->shift[c] - i);
byte = (v << (format->shift[c] - i)) & mask;
}
else
{
mask = mask32 >> (i - format->shift[c]);
byte = (v >> (i - format->shift[c])) & mask;
}
dst[i / 8] |= byte;
}
}
}
struct d3dx_color_key
{
uint8_t color_key_min[4];
uint8_t color_key_max[4];
};
static const char *debug_d3dx_color_key(const struct d3dx_color_key *color_key)
{
if (!color_key)
return "(null)";
return wine_dbg_sprintf("(0x%02x->0x%02x)-(0x%02x->0x%02x)-(0x%02x->0x%02x)-(0x%02x->0x%02x)",
color_key->color_key_min[0], color_key->color_key_max[0],
color_key->color_key_min[1], color_key->color_key_max[1],
color_key->color_key_min[2], color_key->color_key_max[2],
color_key->color_key_min[3], color_key->color_key_max[3]);
}
static void d3dx_init_color_key(const struct pixel_format_desc *src_fmt, uint32_t color_key,
struct d3dx_color_key *color_key_out)
{
unsigned int i;
for (i = 0; i < 4; ++i)
{
const enum component_type src_ctype = !i ? src_fmt->a_type : src_fmt->rgb_type;
const uint8_t channel_bits = (src_ctype == CTYPE_LUMA) ? src_fmt->bits[1] : src_fmt->bits[i];
const uint8_t ck_channel = (color_key >> (24 - (i * 8))) & 0xff;
float slop, channel_conv, unique_values;
if (!channel_bits)
{
color_key_out->color_key_min[i] = 0x00;
color_key_out->color_key_max[i] = 0xff;
continue;
}
/*
* If the source format channel can represent all unique channel
* values in the color key, no extra processing is necessary.
*/
if (src_ctype == CTYPE_FLOAT || (src_ctype == CTYPE_SNORM && channel_bits > 8)
|| (src_ctype != CTYPE_SNORM && channel_bits >= 8))
{
color_key_out->color_key_min[i] = color_key_out->color_key_max[i] = ck_channel;
continue;
}
channel_conv = ck_channel / 255.0f;
if (src_ctype == CTYPE_SNORM)
{
const uint32_t max_value = (1u << (channel_bits - 1)) - 1;
unique_values = (1u << channel_bits) - 2;
channel_conv = (channel_conv * 2.0f) - 1.0f;
channel_conv = rintf(channel_conv * max_value) / max_value;
channel_conv = (channel_conv + 1.0f) / 2.0f;
}
else
{
unique_values = (1u << channel_bits) - 1;
channel_conv = rintf(channel_conv * unique_values) / unique_values;
}
channel_conv = channel_conv * 255.0f;
slop = (255.0f - unique_values) / unique_values / 2.0f;
color_key_out->color_key_min[i] = rintf(d3dx_clamp(channel_conv - slop, 0.0f, 255.0f));
color_key_out->color_key_max[i] = rintf(d3dx_clamp(channel_conv + slop, 0.0f, 255.0f));
}
}
/************************************************************
* copy_pixels
*
* Copies the source buffer to the destination buffer.
* Works for any pixel format.
* The source and the destination must be block-aligned.
*/
static void copy_pixels(const BYTE *src, UINT src_row_pitch, UINT src_slice_pitch,
BYTE *dst, UINT dst_row_pitch, UINT dst_slice_pitch, const struct volume *size,
const struct pixel_format_desc *format)
{
UINT row, slice;
BYTE *dst_addr;
const BYTE *src_addr;
UINT row_block_count = (size->width + format->block_width - 1) / format->block_width;
UINT row_count = (size->height + format->block_height - 1) / format->block_height;
for (slice = 0; slice < size->depth; slice++)
{
src_addr = src + slice * src_slice_pitch;
dst_addr = dst + slice * dst_slice_pitch;
for (row = 0; row < row_count; row++)
{
memcpy(dst_addr, src_addr, row_block_count * format->block_byte_count);
src_addr += src_row_pitch;
dst_addr += dst_row_pitch;
}
}
}
static void convert_argb_pixel(const uint8_t *src_ptr, const struct pixel_format_desc *src_fmt,
uint8_t *dst_ptr, const struct pixel_format_desc *dst_fmt, const PALETTEENTRY *palette,
struct argb_conversion_info *conv_info, const struct d3dx_color_key *color_key,
const struct pixel_format_desc *ck_format, struct argb_conversion_info *ck_conv_info)
{
unsigned int i;
if (format_types_match(src_fmt, dst_fmt) && src_fmt->bytes_per_pixel <= 4 && dst_fmt->bytes_per_pixel <= 4)
{
DWORD channels[4];
DWORD val;
get_relevant_argb_components(conv_info, src_ptr, channels);
val = make_argb_color(conv_info, channels);
if (color_key)
{
DWORD ck_pixel;
get_relevant_argb_components(ck_conv_info, src_ptr, channels);
ck_pixel = make_argb_color(ck_conv_info, channels);
for (i = 0; i < 4; ++i)
{
const uint8_t ck_channel = (ck_pixel >> (24 - (i * 8))) & 0xff;
if ((ck_channel < color_key->color_key_min[i]) || (ck_channel > color_key->color_key_max[i]))
break;
}
if (i == 4)
val = 0;
}
memcpy(dst_ptr, &val, dst_fmt->bytes_per_pixel);
}
else
{
struct d3dx_color color, tmp;
format_to_d3dx_color(src_fmt, src_ptr, palette, &color);
tmp = color;
if (color_key)
{
DWORD ck_pixel = 0;
format_from_d3dx_color(ck_format, &tmp, (BYTE *)&ck_pixel);
for (i = 0; i < 4; ++i)
{
const uint8_t ck_channel = (ck_pixel >> (24 - (i * 8))) & 0xff;
if ((ck_channel < color_key->color_key_min[i]) || (ck_channel > color_key->color_key_max[i]))
break;
}
if (i == 4)
tmp.value.x = tmp.value.y = tmp.value.z = tmp.value.w = 0.0f;
}
color = tmp;
format_from_d3dx_color(dst_fmt, &color, dst_ptr);
}
}
/************************************************************
* convert_argb_pixels
*
* Copies the source buffer to the destination buffer, performing
* any necessary format conversion and color keying.
* Pixels outsize the source rect are blacked out.
*/
static void convert_argb_pixels(const BYTE *src, UINT src_row_pitch, UINT src_slice_pitch, const struct volume *src_size,
const struct pixel_format_desc *src_format, BYTE *dst, UINT dst_row_pitch, UINT dst_slice_pitch,
const struct volume *dst_size, const struct pixel_format_desc *dst_format, const struct d3dx_color_key *color_key,
const PALETTEENTRY *palette)
{
/* Color keys are always represented in D3DFMT_A8R8G8B8 format. */
const struct pixel_format_desc *ck_format = color_key ? get_d3dx_pixel_format_info(D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM) : NULL;
struct argb_conversion_info conv_info, ck_conv_info;
UINT min_width, min_height, min_depth;
UINT x, y, z;
TRACE("src %p, src_row_pitch %u, src_slice_pitch %u, src_size %p, src_format %p, dst %p, "
"dst_row_pitch %u, dst_slice_pitch %u, dst_size %p, dst_format %p, color_key %s, palette %p.\n",
src, src_row_pitch, src_slice_pitch, src_size, src_format, dst, dst_row_pitch, dst_slice_pitch, dst_size,
dst_format, debug_d3dx_color_key(color_key), palette);
init_argb_conversion_info(src_format, dst_format, &conv_info);
min_width = min(src_size->width, dst_size->width);
min_height = min(src_size->height, dst_size->height);
min_depth = min(src_size->depth, dst_size->depth);
if (color_key)
init_argb_conversion_info(src_format, ck_format, &ck_conv_info);
for (z = 0; z < min_depth; z++) {
const BYTE *src_slice_ptr = src + z * src_slice_pitch;
BYTE *dst_slice_ptr = dst + z * dst_slice_pitch;
for (y = 0; y < min_height; y++) {
const BYTE *src_ptr = src_slice_ptr + y * src_row_pitch;
BYTE *dst_ptr = dst_slice_ptr + y * dst_row_pitch;
for (x = 0; x < min_width; x++) {
convert_argb_pixel(src_ptr, src_format, dst_ptr, dst_format, palette,
&conv_info, color_key, ck_format, &ck_conv_info);
src_ptr += src_format->bytes_per_pixel;
dst_ptr += dst_format->bytes_per_pixel;
}
if (src_size->width < dst_size->width) /* black out remaining pixels */
memset(dst_ptr, 0, dst_format->bytes_per_pixel * (dst_size->width - src_size->width));
}
if (src_size->height < dst_size->height) /* black out remaining pixels */
memset(dst + src_size->height * dst_row_pitch, 0, dst_row_pitch * (dst_size->height - src_size->height));
}
if (src_size->depth < dst_size->depth) /* black out remaining pixels */
memset(dst + src_size->depth * dst_slice_pitch, 0, dst_slice_pitch * (dst_size->depth - src_size->depth));
}
/************************************************************
* point_filter_argb_pixels
*
* Copies the source buffer to the destination buffer, performing
* any necessary format conversion, color keying and stretching
* using a point filter.
*/
static void point_filter_argb_pixels(const BYTE *src, UINT src_row_pitch, UINT src_slice_pitch,
const struct volume *src_size, const struct pixel_format_desc *src_format, BYTE *dst, UINT dst_row_pitch,
UINT dst_slice_pitch, const struct volume *dst_size, const struct pixel_format_desc *dst_format,
const struct d3dx_color_key *color_key, const PALETTEENTRY *palette)
{
/* Color keys are always represented in D3DFMT_A8R8G8B8 format. */
const struct pixel_format_desc *ck_format = color_key ? get_d3dx_pixel_format_info(D3DX_PIXEL_FORMAT_B8G8R8A8_UNORM) : NULL;
struct argb_conversion_info conv_info, ck_conv_info;
UINT x, y, z;
TRACE("src %p, src_row_pitch %u, src_slice_pitch %u, src_size %p, src_format %p, dst %p, "
"dst_row_pitch %u, dst_slice_pitch %u, dst_size %p, dst_format %p, color_key %s, palette %p.\n",
src, src_row_pitch, src_slice_pitch, src_size, src_format, dst, dst_row_pitch, dst_slice_pitch, dst_size,
dst_format, debug_d3dx_color_key(color_key), palette);
init_argb_conversion_info(src_format, dst_format, &conv_info);
if (color_key)
init_argb_conversion_info(src_format, ck_format, &ck_conv_info);
for (z = 0; z < dst_size->depth; z++)
{
BYTE *dst_slice_ptr = dst + z * dst_slice_pitch;
const BYTE *src_slice_ptr = src + src_slice_pitch * (z * src_size->depth / dst_size->depth);
for (y = 0; y < dst_size->height; y++)
{
BYTE *dst_ptr = dst_slice_ptr + y * dst_row_pitch;
const BYTE *src_row_ptr = src_slice_ptr + src_row_pitch * (y * src_size->height / dst_size->height);
for (x = 0; x < dst_size->width; x++)
{
const BYTE *src_ptr = src_row_ptr + (x * src_size->width / dst_size->width) * src_format->bytes_per_pixel;
convert_argb_pixel(src_ptr, src_format, dst_ptr, dst_format, palette,
&conv_info, color_key, ck_format, &ck_conv_info);
dst_ptr += dst_format->bytes_per_pixel;
}
}
}
}
static HRESULT d3dx_pixels_decompress(struct d3dx_pixels *pixels, const struct pixel_format_desc *desc,
BOOL is_dst, void **out_memory, uint32_t *out_row_pitch, uint32_t *out_slice_pitch,
const struct pixel_format_desc **out_desc)
{
uint32_t uncompressed_slice_pitch, uncompressed_row_pitch, block_buf_row_pitch, block_width_mask, block_height_mask;
void (*decompress_bcn_block)(const void *src, void *dst, int dst_row_pitch);
const struct pixel_format_desc *uncompressed_desc = NULL;
const struct volume *size = &pixels->size;
BYTE *uncompressed_mem;
uint8_t block_buf[64];
unsigned int x, y, z;
RECT aligned_rect;
switch (desc->format)
{
case D3DX_PIXEL_FORMAT_DXT1_UNORM:
uncompressed_desc = get_d3dx_pixel_format_info(D3DX_PIXEL_FORMAT_R8G8B8A8_UNORM);
decompress_bcn_block = bcdec_bc1;
break;
case D3DX_PIXEL_FORMAT_DXT2_UNORM:
case D3DX_PIXEL_FORMAT_DXT3_UNORM:
uncompressed_desc = get_d3dx_pixel_format_info(D3DX_PIXEL_FORMAT_R8G8B8A8_UNORM);
decompress_bcn_block = bcdec_bc2;
break;
case D3DX_PIXEL_FORMAT_DXT4_UNORM:
case D3DX_PIXEL_FORMAT_DXT5_UNORM:
uncompressed_desc = get_d3dx_pixel_format_info(D3DX_PIXEL_FORMAT_R8G8B8A8_UNORM);
decompress_bcn_block = bcdec_bc3;
break;
default:
FIXME("Unexpected compressed texture format %u.\n", desc->format);
return E_NOTIMPL;
}
block_width_mask = desc->block_width - 1;
block_height_mask = desc->block_height - 1;
block_buf_row_pitch = desc->block_width * uncompressed_desc->bytes_per_pixel;
uncompressed_row_pitch = size->width * uncompressed_desc->bytes_per_pixel;
uncompressed_slice_pitch = uncompressed_row_pitch * size->height;
if (!(uncompressed_mem = malloc(size->depth * uncompressed_slice_pitch)))
return E_OUTOFMEMORY;
/*
* For compressed destination pixels, width/height will represent
* the entire set of compressed blocks our destination rectangle touches.
* If we're only updating a sub-area of any blocks, we need to decompress
* the pixels outside of the sub-area.
*/
if (is_dst)
{
SetRect(&aligned_rect, 0, 0, size->width, size->height);
/*
* If our destination covers the entire set of blocks, no
* decompression needs to be done, just return the allocated memory.
*/
if (EqualRect(&aligned_rect, &pixels->unaligned_rect))
goto exit;
}
/*
* For compressed source pixels, width/height will represent the size of
* the unaligned rectangle. I.e, if we have an 8x8 source with an
* unaligned rect of (2,2)-(6,6) our width/height will be 4.
*/
else
{
SetRect(&aligned_rect, 0, 0, (pixels->unaligned_rect.right + block_width_mask) & ~block_width_mask,
(pixels->unaligned_rect.bottom + block_height_mask) & ~block_height_mask);
}
TRACE("Decompressing pixels.\n");
for (z = 0; z < size->depth; ++z)
{
const uint8_t *src_slice = &((const uint8_t *)pixels->data)[z * pixels->slice_pitch];
uint8_t *dst_slice = &uncompressed_mem[z * uncompressed_slice_pitch];
for (y = 0; y < aligned_rect.bottom; y += desc->block_height)
{
const uint8_t *src_ptr = &src_slice[(y / desc->block_height) * pixels->row_pitch];
for (x = 0; x < aligned_rect.right; x += desc->block_width)
{
struct volume dst_block_size;
RECT src_rect, dst_rect;
uint8_t *dst_ptr;
SetRect(&src_rect, x, y, x + desc->block_width, y + desc->block_height);
IntersectRect(&src_rect, &src_rect, &pixels->unaligned_rect);
dst_rect = src_rect;
OffsetRect(&dst_rect, -pixels->unaligned_rect.left, -pixels->unaligned_rect.top);
set_volume_struct(&dst_block_size, dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top, 1);
dst_ptr = &dst_slice[(dst_rect.top * uncompressed_row_pitch)];
dst_ptr += dst_rect.left * uncompressed_desc->bytes_per_pixel;
if (dst_block_size.width != desc->block_width || dst_block_size.height != desc->block_height)
{
if (!is_dst)
{
unsigned int block_buf_offset;
decompress_bcn_block(src_ptr, block_buf, block_buf_row_pitch);
block_buf_offset = (src_rect.top - y) * block_buf_row_pitch;
block_buf_offset += uncompressed_desc->bytes_per_pixel * (src_rect.left - x);
copy_pixels(&block_buf[block_buf_offset], block_buf_row_pitch, 0, dst_ptr,
uncompressed_row_pitch, 0, &dst_block_size, uncompressed_desc);
}
/*
* If this is the destination, we can just copy the whole
* block. It will be partially overwritten later.
*/
else
{
dst_ptr = &dst_slice[y * uncompressed_row_pitch + x * uncompressed_desc->bytes_per_pixel];
decompress_bcn_block(src_ptr, dst_ptr, uncompressed_row_pitch);
}
}
/* Full block copy. */
else if (!is_dst)
{
decompress_bcn_block(src_ptr, dst_ptr, uncompressed_row_pitch);
}
src_ptr += desc->block_byte_count;
}
}
}
exit:
*out_memory = uncompressed_mem;
*out_row_pitch = uncompressed_row_pitch;
*out_slice_pitch = uncompressed_slice_pitch;
*out_desc = uncompressed_desc;
return S_OK;
}
static HRESULT d3dx_pixels_unpack_index(struct d3dx_pixels *pixels, const struct pixel_format_desc *desc,
void **out_memory, uint32_t *out_row_pitch, uint32_t *out_slice_pitch, const struct pixel_format_desc **out_desc)
{
uint32_t x, y, z, unpacked_slice_pitch, unpacked_row_pitch;
const struct pixel_format_desc *unpacked_desc = NULL;
const struct volume *size = &pixels->size;
uint8_t *unpacked_mem;
uint8_t mask, shift;
switch (desc->format)
{
case D3DX_PIXEL_FORMAT_P1_UINT:
case D3DX_PIXEL_FORMAT_P2_UINT:
case D3DX_PIXEL_FORMAT_P4_UINT:
unpacked_desc = get_d3dx_pixel_format_info(D3DX_PIXEL_FORMAT_P8_UINT);
break;
default:
FIXME("Unexpected format %u.\n", desc->format);
return E_NOTIMPL;
}
unpacked_row_pitch = size->width * unpacked_desc->bytes_per_pixel;
unpacked_slice_pitch = unpacked_row_pitch * size->height;
if (!(unpacked_mem = malloc(size->depth * unpacked_slice_pitch)))
return E_OUTOFMEMORY;
shift = 8 / desc->block_width;
mask = (1u << shift) - 1;
TRACE("Unpacking pixels.\n");
for (z = 0; z < size->depth; ++z)
{
const uint8_t *slice_data = (const uint8_t *)pixels->data + (pixels->slice_pitch * z);
for (y = 0; y < size->height; ++y)
{
uint8_t *ptr = &unpacked_mem[(z * unpacked_slice_pitch) + (y * unpacked_row_pitch)];
const uint8_t *row_data = slice_data + (pixels->row_pitch * y);
for (x = 0; x < size->width; x += desc->block_width)
{
const uint8_t packed_data = *row_data;
unsigned int i;
for (i = 0; i < desc->block_width; ++i)
{
const uint8_t cur_shift = ((desc->block_width - 1) - i) * shift;
if (x + i >= size->width)
break;
ptr[i] = (packed_data >> cur_shift) & mask;
}
ptr += unpacked_desc->bytes_per_pixel * desc->block_width;
row_data++;
}
}
}
*out_memory = unpacked_mem;
*out_row_pitch = unpacked_row_pitch;
*out_slice_pitch = unpacked_slice_pitch;
*out_desc = unpacked_desc;
return S_OK;
}
static void d3dx_compress_block(enum d3dx_pixel_format_id fmt, uint8_t *block_buf, void *dst_buf)
{
switch (fmt)
{
case D3DX_PIXEL_FORMAT_DXT1_UNORM:
stb_compress_dxt_block(dst_buf, block_buf, FALSE, 0);
break;
case D3DX_PIXEL_FORMAT_DXT2_UNORM:
case D3DX_PIXEL_FORMAT_DXT3_UNORM:
{
uint8_t *dst_data_offset = dst_buf;
unsigned int y;
/* STB doesn't do DXT2/DXT3, we'll do the alpha part ourselves. */
for (y = 0; y < 4; ++y)
{
uint8_t *tmp_row = &block_buf[y * 4 * 4];
dst_data_offset[0] = (tmp_row[7] & 0xf0);
dst_data_offset[0] |= (tmp_row[3] >> 4);
dst_data_offset[1] = (tmp_row[15] & 0xf0);
dst_data_offset[1] |= (tmp_row[11] >> 4);
/*
* Set all alpha values to 0xff so they aren't considered during
* compression. This modifies the source data being passed in.
*/
tmp_row[3] = tmp_row[7] = tmp_row[11] = tmp_row[15] = 0xff;
dst_data_offset += 2;
}
stb_compress_dxt_block(dst_data_offset, block_buf, FALSE, 0);
break;
}
case D3DX_PIXEL_FORMAT_DXT4_UNORM:
case D3DX_PIXEL_FORMAT_DXT5_UNORM:
stb_compress_dxt_block(dst_buf, block_buf, TRUE, 0);
break;
default:
assert(0);
break;
}
}
/*
* Source data passed into this function is potentially modified (currently
* only in the case of DXT2/DXT3). As of now we only pass temporary buffers
* into this function, but this should be taken to account if used elsewhere
* outside of d3dx_load_pixels_from_pixels() in the future.
*/
static HRESULT d3dx_pixels_compress(struct d3dx_pixels *src_pixels,
const struct pixel_format_desc *src_desc, struct d3dx_pixels *dst_pixels,
const struct pixel_format_desc *dst_desc)
{
unsigned int x, y, z, block_buf_row_pitch;
uint8_t block_buf[64];
switch (dst_desc->format)
{
case D3DX_PIXEL_FORMAT_DXT1_UNORM:
case D3DX_PIXEL_FORMAT_DXT2_UNORM:
case D3DX_PIXEL_FORMAT_DXT3_UNORM:
case D3DX_PIXEL_FORMAT_DXT4_UNORM:
case D3DX_PIXEL_FORMAT_DXT5_UNORM:
assert(src_desc->format == D3DX_PIXEL_FORMAT_R8G8B8A8_UNORM);
break;
default:
FIXME("Unexpected compressed texture format %u.\n", dst_desc->format);
return E_NOTIMPL;
}
TRACE("Compressing pixels.\n");
block_buf_row_pitch = src_desc->bytes_per_pixel * dst_desc->block_width;
for (z = 0; z < src_pixels->size.depth; ++z)
{
const uint8_t *src_slice = &((const uint8_t *)src_pixels->data)[z * src_pixels->slice_pitch];
uint8_t *dst_slice = &((uint8_t *)dst_pixels->data)[z * dst_pixels->slice_pitch];
for (y = 0; y < src_pixels->size.height; y += dst_desc->block_height)
{
const unsigned int tmp_src_height = min(dst_desc->block_height, src_pixels->size.height - y);
uint8_t *dst_ptr = &dst_slice[(y / dst_desc->block_height) * dst_pixels->row_pitch];
const uint8_t *src_ptr = &src_slice[y * src_pixels->row_pitch];
for (x = 0; x < src_pixels->size.width; x += dst_desc->block_width)
{
const unsigned int tmp_src_width = min(dst_desc->block_width, src_pixels->size.width - x);
struct volume block_buf_size = { tmp_src_width, tmp_src_height, 1 };
if (tmp_src_width != dst_desc->block_width || tmp_src_height != dst_desc->block_height)
memset(block_buf, 0, sizeof(block_buf));
copy_pixels(src_ptr, src_pixels->row_pitch, src_pixels->slice_pitch, block_buf, block_buf_row_pitch, 0,
&block_buf_size, src_desc);
d3dx_compress_block(dst_desc->format, block_buf, dst_ptr);
src_ptr += (src_desc->bytes_per_pixel * dst_desc->block_width);
dst_ptr += dst_desc->block_byte_count;
}
}
}
return S_OK;
}
HRESULT d3dx_pixels_init(const void *data, uint32_t row_pitch, uint32_t slice_pitch,
const PALETTEENTRY *palette, enum d3dx_pixel_format_id format, uint32_t left, uint32_t top, uint32_t right,
uint32_t bottom, uint32_t front, uint32_t back, struct d3dx_pixels *pixels)
{
const struct pixel_format_desc *fmt_desc = get_d3dx_pixel_format_info(format);
const BYTE *ptr = data;
RECT unaligned_rect;
memset(pixels, 0, sizeof(*pixels));
if (is_unknown_format(fmt_desc))
{
FIXME("Unsupported format %#x.\n", format);
return E_NOTIMPL;
}
ptr += front * slice_pitch;
ptr += (top / fmt_desc->block_height) * row_pitch;
ptr += (left / fmt_desc->block_width) * fmt_desc->block_byte_count;
if (is_compressed_format(fmt_desc))
{
uint32_t left_aligned, top_aligned;
top_aligned = top & ~(fmt_desc->block_height - 1);
left_aligned = left & ~(fmt_desc->block_width - 1);
SetRect(&unaligned_rect, left, top, right, bottom);
OffsetRect(&unaligned_rect, -left_aligned, -top_aligned);
}
else
{
SetRect(&unaligned_rect, 0, 0, (right - left), (bottom - top));
}
if (!slice_pitch)
slice_pitch = row_pitch * (bottom - top);
set_d3dx_pixels(pixels, ptr, row_pitch, slice_pitch, palette, (right - left), (bottom - top), (back - front),
&unaligned_rect);
return S_OK;
}
static const char *debug_d3dx_pixels(struct d3dx_pixels *pixels)
{
if (!pixels)
return "(null)";
return wine_dbg_sprintf("(data %p, row_pitch %d, slice_pitch %d, palette %p, width %d, height %d, depth %d, "
"unaligned_rect %s)", pixels->data, pixels->row_pitch, pixels->slice_pitch, pixels->palette,
pixels->size.width, pixels->size.height, pixels->size.depth, wine_dbgstr_rect(&pixels->unaligned_rect));
}
HRESULT d3dx_load_pixels_from_pixels(struct d3dx_pixels *dst_pixels,
const struct pixel_format_desc *dst_desc, struct d3dx_pixels *src_pixels,
const struct pixel_format_desc *src_desc, uint32_t filter_flags, uint32_t color_key)
{
struct volume src_size, dst_size, dst_size_aligned;
const struct d3dx_color_key *d3dx_ck = NULL;
struct d3dx_color_key d3dx_color_key;
HRESULT hr = S_OK;
TRACE("dst_pixels %s, dst_desc %p, src_pixels %s, src_desc %p, filter_flags %#x, color_key %#x.\n",
debug_d3dx_pixels(dst_pixels), dst_desc, debug_d3dx_pixels(src_pixels), src_desc,
filter_flags, color_key);
if (is_compressed_format(src_desc))
set_volume_struct(&src_size, (src_pixels->unaligned_rect.right - src_pixels->unaligned_rect.left),
(src_pixels->unaligned_rect.bottom - src_pixels->unaligned_rect.top), src_pixels->size.depth);
else
src_size = src_pixels->size;
dst_size_aligned = dst_pixels->size;
if (is_compressed_format(dst_desc))
set_volume_struct(&dst_size, (dst_pixels->unaligned_rect.right - dst_pixels->unaligned_rect.left),
(dst_pixels->unaligned_rect.bottom - dst_pixels->unaligned_rect.top), dst_pixels->size.depth);
else
dst_size = dst_size_aligned;
/* Everything matches, simply copy the pixels. */
if (src_desc->format == dst_desc->format
&& (dst_size.width == src_size.width && !(dst_size.width % dst_desc->block_width))
&& (dst_size.height == src_size.height && !(dst_size.height % dst_desc->block_height))
&& (dst_size.depth == src_size.depth)
&& color_key == 0
&& !(src_pixels->unaligned_rect.left & (src_desc->block_width - 1))
&& !(src_pixels->unaligned_rect.top & (src_desc->block_height - 1))
&& !(dst_pixels->unaligned_rect.left & (dst_desc->block_width - 1))
&& !(dst_pixels->unaligned_rect.top & (dst_desc->block_height - 1)))
{
TRACE("Simple copy.\n");
copy_pixels(src_pixels->data, src_pixels->row_pitch, src_pixels->slice_pitch, (void *)dst_pixels->data,
dst_pixels->row_pitch, dst_pixels->slice_pitch, &src_size, src_desc);
return S_OK;
}
/* Stretching or format conversion. */
if (!is_conversion_from_supported(src_desc)
|| !is_conversion_to_supported(dst_desc))
{
FIXME("Unsupported format conversion %#x -> %#x.\n", src_desc->format, dst_desc->format);
return E_NOTIMPL;
}
if (is_index_format(src_desc) && (src_desc->block_width > 1))
{
uint32_t unpacked_row_pitch, unpacked_slice_pitch;
const struct pixel_format_desc *unpacked_desc;
void *unpacked_mem = NULL;
hr = d3dx_pixels_unpack_index(src_pixels, src_desc, &unpacked_mem, &unpacked_row_pitch,
&unpacked_slice_pitch, &unpacked_desc);
if (SUCCEEDED(hr))
{
struct d3dx_pixels unpacked_pixels;
d3dx_pixels_init(unpacked_mem, unpacked_row_pitch, unpacked_slice_pitch, src_pixels->palette,
unpacked_desc->format, 0, 0, src_pixels->size.width, src_pixels->size.height,
0, src_pixels->size.depth, &unpacked_pixels);
hr = d3dx_load_pixels_from_pixels(dst_pixels, dst_desc, &unpacked_pixels, unpacked_desc,
filter_flags, color_key);
}
free(unpacked_mem);
goto exit;
}
/*
* If the source is a compressed image, we need to decompress it first
* before doing any modifications.
*/
if (is_compressed_format(src_desc))
{
uint32_t uncompressed_row_pitch, uncompressed_slice_pitch;
const struct pixel_format_desc *uncompressed_desc;
void *uncompressed_mem = NULL;
hr = d3dx_pixels_decompress(src_pixels, src_desc, FALSE, &uncompressed_mem, &uncompressed_row_pitch,
&uncompressed_slice_pitch, &uncompressed_desc);
if (SUCCEEDED(hr))
{
struct d3dx_pixels uncompressed_pixels;
d3dx_pixels_init(uncompressed_mem, uncompressed_row_pitch, uncompressed_slice_pitch, NULL,
uncompressed_desc->format, 0, 0, src_pixels->size.width, src_pixels->size.height,
0, src_pixels->size.depth, &uncompressed_pixels);
if (sizeof(void *) == 4 && color_key)
{
TRACE("Clearing color key value on compressed source pixels.\n");
color_key = 0;
}
hr = d3dx_load_pixels_from_pixels(dst_pixels, dst_desc, &uncompressed_pixels, uncompressed_desc,
filter_flags, color_key);
}
free(uncompressed_mem);
goto exit;
}
/* Same as the above, need to decompress the destination prior to modifying. */
if (is_compressed_format(dst_desc))
{
uint32_t uncompressed_row_pitch, uncompressed_slice_pitch;
const struct pixel_format_desc *uncompressed_desc;
struct d3dx_pixels uncompressed_pixels;
void *uncompressed_mem = NULL;
hr = d3dx_pixels_decompress(dst_pixels, dst_desc, TRUE, &uncompressed_mem, &uncompressed_row_pitch,
&uncompressed_slice_pitch, &uncompressed_desc);
if (FAILED(hr))
goto exit;
d3dx_pixels_init(uncompressed_mem, uncompressed_row_pitch, uncompressed_slice_pitch, NULL,
uncompressed_desc->format, dst_pixels->unaligned_rect.left, dst_pixels->unaligned_rect.top,
dst_pixels->unaligned_rect.right, dst_pixels->unaligned_rect.bottom, 0, dst_pixels->size.depth,
&uncompressed_pixels);
hr = d3dx_load_pixels_from_pixels(&uncompressed_pixels, uncompressed_desc, src_pixels, src_desc, filter_flags,
color_key);
if (SUCCEEDED(hr))
{
d3dx_pixels_init(uncompressed_mem, uncompressed_row_pitch, uncompressed_slice_pitch, NULL,
uncompressed_desc->format, 0, 0, dst_size_aligned.width, dst_size_aligned.height, 0,
dst_pixels->size.depth, &uncompressed_pixels);
hr = d3dx_pixels_compress(&uncompressed_pixels, uncompressed_desc, dst_pixels, dst_desc);
if (FAILED(hr))
WARN("Failed to compress pixels, hr %#lx.\n", hr);
}
free(uncompressed_mem);
goto exit;
}
if (color_key)
{
d3dx_init_color_key(src_desc, color_key, &d3dx_color_key);
d3dx_ck = &d3dx_color_key;
}
if ((filter_flags & 0xf) == D3DX_FILTER_NONE)
{
convert_argb_pixels(src_pixels->data, src_pixels->row_pitch, src_pixels->slice_pitch, &src_size, src_desc,
(BYTE *)dst_pixels->data, dst_pixels->row_pitch, dst_pixels->slice_pitch, &dst_size, dst_desc,
d3dx_ck, src_pixels->palette);
}
else /* if ((filter & 0xf) == D3DX_FILTER_POINT) */
{
if ((filter_flags & 0xf) != D3DX_FILTER_POINT)
FIXME("Unhandled filter %#x.\n", filter_flags);
/* Always apply a point filter until D3DX_FILTER_LINEAR,
* D3DX_FILTER_TRIANGLE and D3DX_FILTER_BOX are implemented. */
point_filter_argb_pixels(src_pixels->data, src_pixels->row_pitch, src_pixels->slice_pitch, &src_size,
src_desc, (BYTE *)dst_pixels->data, dst_pixels->row_pitch, dst_pixels->slice_pitch, &dst_size,
dst_desc, d3dx_ck, src_pixels->palette);
}
exit:
if (FAILED(hr))
WARN("Failed to load pixels, hr %#lx.\n", hr);
return hr;
}
void get_aligned_rect(uint32_t left, uint32_t top, uint32_t right, uint32_t bottom, uint32_t width, uint32_t height,
const struct pixel_format_desc *fmt_desc, RECT *aligned_rect)
{
SetRect(aligned_rect, left, top, right, bottom);
if (aligned_rect->left & (fmt_desc->block_width - 1))
aligned_rect->left = aligned_rect->left & ~(fmt_desc->block_width - 1);
if (aligned_rect->top & (fmt_desc->block_height - 1))
aligned_rect->top = aligned_rect->top & ~(fmt_desc->block_height - 1);
if (aligned_rect->right & (fmt_desc->block_width - 1) && aligned_rect->right != width)
aligned_rect->right = min((aligned_rect->right + fmt_desc->block_width - 1)
& ~(fmt_desc->block_width - 1), width);
if (aligned_rect->bottom & (fmt_desc->block_height - 1) && aligned_rect->bottom != height)
aligned_rect->bottom = min((aligned_rect->bottom + fmt_desc->block_height - 1)
& ~(fmt_desc->block_height - 1), height);
}