surface.c 61.9 KB
Newer Older
1
/*
2
 * Copyright (C) 2009-2010 Tony Wasserka
3
 * Copyright (C) 2012 Józef Kucia
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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"
22
#include "wine/unicode.h"
23 24
#include "d3dx9_36_private.h"

25
#include "initguid.h"
26
#include "ole2.h"
27 28
#include "wincodec.h"

29 30 31
WINE_DEFAULT_DEBUG_CHANNEL(d3dx);


32 33 34
/* Wine-specific WIC GUIDs */
DEFINE_GUID(GUID_WineContainerFormatTga, 0x0c44fda1,0xa5c5,0x4298,0x96,0x85,0x47,0x3f,0xc1,0x7c,0xd3,0x22);

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
static const struct
{
    const GUID *wic_guid;
    D3DFORMAT d3dformat;
} wic_pixel_formats[] = {
    { &GUID_WICPixelFormat8bppIndexed, D3DFMT_L8 },
    { &GUID_WICPixelFormat1bppIndexed, D3DFMT_L8 },
    { &GUID_WICPixelFormat4bppIndexed, D3DFMT_L8 },
    { &GUID_WICPixelFormat16bppBGR555, D3DFMT_X1R5G5B5 },
    { &GUID_WICPixelFormat16bppBGR565, D3DFMT_R5G6B5 },
    { &GUID_WICPixelFormat24bppBGR, D3DFMT_R8G8B8 },
    { &GUID_WICPixelFormat32bppBGR, D3DFMT_X8R8G8B8 },
    { &GUID_WICPixelFormat32bppBGRA, D3DFMT_A8R8G8B8 }
};

static D3DFORMAT wic_guid_to_d3dformat(const GUID *guid)
{
    int i;

    for (i = 0; i < sizeof(wic_pixel_formats) / sizeof(wic_pixel_formats[0]); i++)
    {
        if (IsEqualGUID(wic_pixel_formats[i].wic_guid, guid))
            return wic_pixel_formats[i].d3dformat;
    }

    return D3DFMT_UNKNOWN;
}

63 64 65 66 67 68 69 70 71 72 73 74 75
static const GUID *d3dformat_to_wic_guid(D3DFORMAT format)
{
    int i;

    for (i = 0; i < sizeof(wic_pixel_formats) / sizeof(wic_pixel_formats[0]); i++)
    {
        if (wic_pixel_formats[i].d3dformat == format)
            return wic_pixel_formats[i].wic_guid;
    }

    return NULL;
}

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
/* dds_header.flags */
#define DDS_CAPS 0x1
#define DDS_HEIGHT 0x2
#define DDS_WIDTH 0x2
#define DDS_PITCH 0x8
#define DDS_PIXELFORMAT 0x1000
#define DDS_MIPMAPCOUNT 0x20000
#define DDS_LINEARSIZE 0x80000
#define DDS_DEPTH 0x800000

/* dds_header.caps */
#define DDS_CAPS_COMPLEX 0x8
#define DDS_CAPS_TEXTURE 0x1000
#define DDS_CAPS_MIPMAP 0x400000

/* dds_header.caps2 */
#define DDS_CAPS2_CUBEMAP 0x200
#define DDS_CAPS2_CUBEMAP_POSITIVEX 0x400
#define DDS_CAPS2_CUBEMAP_NEGATIVEX 0x800
#define DDS_CAPS2_CUBEMAP_POSITIVEY 0x1000
#define DDS_CAPS2_CUBEMAP_NEGATIVEY 0x2000
#define DDS_CAPS2_CUBEMAP_POSITIVEZ 0x4000
#define DDS_CAPS2_CUBEMAP_NEGATIVEZ 0x8000
99 100 101
#define DDS_CAPS2_CUBEMAP_ALL_FACES ( DDS_CAPS2_CUBEMAP_POSITIVEX | DDS_CAPS2_CUBEMAP_NEGATIVEX \
                                    | DDS_CAPS2_CUBEMAP_POSITIVEY | DDS_CAPS2_CUBEMAP_NEGATIVEY \
                                    | DDS_CAPS2_CUBEMAP_POSITIVEZ | DDS_CAPS2_CUBEMAP_NEGATIVEZ )
102 103 104 105 106 107 108 109 110
#define DDS_CAPS2_VOLUME 0x200000

/* dds_pixel_format.flags */
#define DDS_PF_ALPHA 0x1
#define DDS_PF_ALPHA_ONLY 0x2
#define DDS_PF_FOURCC 0x4
#define DDS_PF_RGB 0x40
#define DDS_PF_YUV 0x200
#define DDS_PF_LUMINANCE 0x20000
111
#define DDS_PF_BUMPDUDV 0x80000
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143

struct dds_pixel_format
{
    DWORD size;
    DWORD flags;
    DWORD fourcc;
    DWORD bpp;
    DWORD rmask;
    DWORD gmask;
    DWORD bmask;
    DWORD amask;
};

struct dds_header
{
    DWORD signature;
    DWORD size;
    DWORD flags;
    DWORD height;
    DWORD width;
    DWORD pitch_or_linear_size;
    DWORD depth;
    DWORD miplevels;
    DWORD reserved[11];
    struct dds_pixel_format pixel_format;
    DWORD caps;
    DWORD caps2;
    DWORD caps3;
    DWORD caps4;
    DWORD reserved2;
};

144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
static D3DFORMAT dds_fourcc_to_d3dformat(DWORD fourcc)
{
    int i;
    static const DWORD known_fourcc[] = {
        MAKEFOURCC('U','Y','V','Y'),
        MAKEFOURCC('Y','U','Y','2'),
        MAKEFOURCC('R','G','B','G'),
        MAKEFOURCC('G','R','G','B'),
        MAKEFOURCC('D','X','T','1'),
        MAKEFOURCC('D','X','T','2'),
        MAKEFOURCC('D','X','T','3'),
        MAKEFOURCC('D','X','T','4'),
        MAKEFOURCC('D','X','T','5')
    };

    for (i = 0; i < sizeof(known_fourcc) / sizeof(known_fourcc[0]); i++)
    {
        if (known_fourcc[i] == fourcc)
            return fourcc;
    }

    WARN("Unknown FourCC %#x\n", fourcc);
    return D3DFMT_UNKNOWN;
}

static D3DFORMAT dds_rgb_to_d3dformat(const struct dds_pixel_format *pixel_format)
{
    int i;
    static const struct {
        DWORD bpp;
        DWORD rmask;
        DWORD gmask;
        DWORD bmask;
        DWORD amask;
        D3DFORMAT format;
    } rgb_pixel_formats[] = {
        { 8, 0xe0, 0x1c, 0x03, 0, D3DFMT_R3G3B2 },
        { 16, 0xf800, 0x07e0, 0x001f, 0x0000, D3DFMT_R5G6B5 },
        { 16, 0x7c00, 0x03e0, 0x001f, 0x8000, D3DFMT_A1R5G5B5 },
        { 16, 0x7c00, 0x03e0, 0x001f, 0x0000, D3DFMT_X1R5G5B5 },
        { 16, 0x0f00, 0x00f0, 0x000f, 0xf000, D3DFMT_A4R4G4B4 },
        { 16, 0x0f00, 0x00f0, 0x000f, 0x0000, D3DFMT_X4R4G4B4 },
        { 16, 0x00e0, 0x001c, 0x0003, 0xff00, D3DFMT_A8R3G3B2 },
        { 24, 0xff0000, 0x00ff00, 0x0000ff, 0x000000, D3DFMT_R8G8B8 },
        { 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000, D3DFMT_A8R8G8B8 },
        { 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000, D3DFMT_X8R8G8B8 },
        { 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000, D3DFMT_A2B10G10R10 },
        { 32, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000, D3DFMT_A2R10G10B10 },
        { 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000, D3DFMT_G16R16 },
193
        { 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000, D3DFMT_A8B8G8R8 },
194
        { 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000, D3DFMT_X8B8G8R8 },
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    };

    for (i = 0; i < sizeof(rgb_pixel_formats) / sizeof(rgb_pixel_formats[0]); i++)
    {
        if (rgb_pixel_formats[i].bpp == pixel_format->bpp
            && rgb_pixel_formats[i].rmask == pixel_format->rmask
            && rgb_pixel_formats[i].gmask == pixel_format->gmask
            && rgb_pixel_formats[i].bmask == pixel_format->bmask)
        {
            if ((pixel_format->flags & DDS_PF_ALPHA) && rgb_pixel_formats[i].amask == pixel_format->amask)
                return rgb_pixel_formats[i].format;
            if (rgb_pixel_formats[i].amask == 0)
                return rgb_pixel_formats[i].format;
        }
    }

    WARN("Unknown RGB pixel format (%#x, %#x, %#x, %#x)\n",
        pixel_format->rmask, pixel_format->gmask, pixel_format->bmask, pixel_format->amask);
    return D3DFMT_UNKNOWN;
}

static D3DFORMAT dds_luminance_to_d3dformat(const struct dds_pixel_format *pixel_format)
{
    if (pixel_format->bpp == 8)
    {
        if (pixel_format->rmask == 0xff)
            return D3DFMT_L8;
        if ((pixel_format->flags & DDS_PF_ALPHA) && pixel_format->rmask == 0x0f && pixel_format->amask == 0xf0)
            return D3DFMT_A4L4;
    }
    if (pixel_format->bpp == 16)
    {
227 228
        if (pixel_format->rmask == 0xffff)
            return D3DFMT_L16;
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
        if ((pixel_format->flags & DDS_PF_ALPHA) && pixel_format->rmask == 0x00ff && pixel_format->amask == 0xff00)
            return D3DFMT_A8L8;
    }

    WARN("Unknown luminance pixel format (bpp %#x, l %#x, a %#x)\n",
        pixel_format->bpp, pixel_format->rmask, pixel_format->amask);
    return D3DFMT_UNKNOWN;
}

static D3DFORMAT dds_alpha_to_d3dformat(const struct dds_pixel_format *pixel_format)
{
    if (pixel_format->bpp == 8 && pixel_format->amask == 0xff)
        return D3DFMT_A8;

    WARN("Unknown Alpha pixel format (%#x, %#x)\n", pixel_format->bpp, pixel_format->rmask);
    return D3DFMT_UNKNOWN;
}

247 248 249 250 251 252 253 254 255 256 257 258
static D3DFORMAT dds_bump_to_d3dformat(const struct dds_pixel_format *pixel_format)
{
    if (pixel_format->bpp == 16 && pixel_format->rmask == 0x00ff && pixel_format->gmask == 0xff00)
        return D3DFMT_V8U8;
    if (pixel_format->bpp == 32 && pixel_format->rmask == 0x0000ffff && pixel_format->gmask == 0xffff0000)
        return D3DFMT_V16U16;

    WARN("Unknown bump pixel format (%#x, %#x, %#x, %#x, %#x)\n", pixel_format->bpp,
        pixel_format->rmask, pixel_format->gmask, pixel_format->bmask, pixel_format->amask);
    return D3DFMT_UNKNOWN;
}

259 260
static D3DFORMAT dds_pixel_format_to_d3dformat(const struct dds_pixel_format *pixel_format)
{
261 262 263 264 265 266 267 268
    if (pixel_format->flags & DDS_PF_FOURCC)
        return dds_fourcc_to_d3dformat(pixel_format->fourcc);
    if (pixel_format->flags & DDS_PF_RGB)
        return dds_rgb_to_d3dformat(pixel_format);
    if (pixel_format->flags & DDS_PF_LUMINANCE)
        return dds_luminance_to_d3dformat(pixel_format);
    if (pixel_format->flags & DDS_PF_ALPHA_ONLY)
        return dds_alpha_to_d3dformat(pixel_format);
269 270
    if (pixel_format->flags & DDS_PF_BUMPDUDV)
        return dds_bump_to_d3dformat(pixel_format);
271

272 273 274
    WARN("Unknown pixel format (flags %#x, fourcc %#x, bpp %#x, r %#x, g %#x, b %#x, a %#x)\n",
        pixel_format->flags, pixel_format->fourcc, pixel_format->bpp,
        pixel_format->rmask, pixel_format->gmask, pixel_format->bmask, pixel_format->amask);
275 276 277
    return D3DFMT_UNKNOWN;
}

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
static HRESULT calculate_dds_surface_size(const D3DXIMAGE_INFO *img_info,
    UINT width, UINT height, UINT *pitch, UINT *size)
{
    const PixelFormatDesc *format_desc = get_format_info(img_info->Format);
    if (format_desc->format == D3DFMT_UNKNOWN)
        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;
}

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
/************************************************************
* get_image_info_from_dds
*
* Fills a D3DXIMAGE_INFO structure with information
* about a DDS file stored in the memory.
*
* PARAMS
*   buffer  [I] pointer to DDS data
*   length  [I] size of DDS data
*   info    [O] pointer to D3DXIMAGE_INFO structure
*
* RETURNS
*   Success: D3D_OK
*   Failure: D3DXERR_INVALIDDATA
*
*/
317
static HRESULT get_image_info_from_dds(const void *buffer, UINT length, D3DXIMAGE_INFO *info)
318
{
319 320 321
   UINT i;
   UINT faces = 0;
   UINT width, height;
322
   const struct dds_header *header = buffer;
323
   UINT expected_length = 0;
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339

   if (length < sizeof(*header) || !info)
       return D3DXERR_INVALIDDATA;

   if (header->pixel_format.size != sizeof(header->pixel_format))
       return D3DXERR_INVALIDDATA;

   info->Width = header->width;
   info->Height = header->height;
   info->Depth = 1;
   info->MipLevels = (header->flags & DDS_MIPMAPCOUNT) ?  header->miplevels : 1;

   info->Format = dds_pixel_format_to_d3dformat(&header->pixel_format);
   if (info->Format == D3DFMT_UNKNOWN)
       return D3DXERR_INVALIDDATA;

Józef Kucia's avatar
Józef Kucia committed
340 341
   TRACE("Pixel format is %#x\n", info->Format);

342 343 344 345 346 347 348
   if (header->caps2 & DDS_CAPS2_VOLUME)
   {
       info->Depth = header->depth;
       info->ResourceType = D3DRTYPE_VOLUMETEXTURE;
   }
   else if (header->caps2 & DDS_CAPS2_CUBEMAP)
   {
349 350 351 352 353 354
       DWORD face;
       for (face = DDS_CAPS2_CUBEMAP_POSITIVEX; face <= DDS_CAPS2_CUBEMAP_NEGATIVEZ; face <<= 1)
       {
           if (header->caps2 & face)
               faces++;
       }
355 356 357 358
       info->ResourceType = D3DRTYPE_CUBETEXTURE;
   }
   else
   {
359
       faces = 1;
360 361 362
       info->ResourceType = D3DRTYPE_TEXTURE;
   }

363 364 365 366 367 368 369 370 371
   /* calculate the expected length */
   width = info->Width;
   height = info->Height;
   for (i = 0; i < info->MipLevels; i++)
   {
       UINT pitch, size = 0;
       calculate_dds_surface_size(info, width, height, &pitch, &size);
       expected_length += size;
       width = max(1, width / 2);
372
       height = max(1, height / 2);
373 374 375 376 377
   }

   expected_length *= faces;
   expected_length += sizeof(*header);
   if (length < expected_length)
378 379
   {
       WARN("File is too short %u, expected at least %u bytes\n", length, expected_length);
380
       return D3DXERR_INVALIDDATA;
381
   }
382

383 384 385 386 387
   info->ImageFileFormat = D3DXIFF_DDS;

   return D3D_OK;
}

388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
static HRESULT load_surface_from_dds(IDirect3DSurface9 *dst_surface, const PALETTEENTRY *dst_palette,
    const RECT *dst_rect, const void *src_data, const RECT *src_rect, DWORD filter, D3DCOLOR color_key,
    const D3DXIMAGE_INFO *src_info)
{
    UINT size;
    UINT src_pitch;
    const struct dds_header *header = src_data;
    const BYTE *pixels = (BYTE *)(header + 1);

    if (src_info->ResourceType != D3DRTYPE_TEXTURE)
        return D3DXERR_INVALIDDATA;

    if (FAILED(calculate_dds_surface_size(src_info, src_info->Width, src_info->Height, &src_pitch, &size)))
        return E_NOTIMPL;

    return D3DXLoadSurfaceFromMemory(dst_surface, dst_palette, dst_rect, pixels, src_info->Format,
        src_pitch, NULL, src_rect, filter, color_key);
}

407 408
HRESULT load_texture_from_dds(IDirect3DTexture9 *texture, const void *src_data, const PALETTEENTRY *palette,
    DWORD filter, D3DCOLOR color_key, const D3DXIMAGE_INFO *src_info)
409

410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
{
    HRESULT hr;
    RECT src_rect;
    UINT src_pitch;
    UINT mip_level;
    UINT mip_levels;
    UINT mip_level_size;
    UINT width, height;
    IDirect3DSurface9 *surface;
    const struct dds_header *header = src_data;
    const BYTE *pixels = (BYTE *)(header + 1);

    if (src_info->ResourceType != D3DRTYPE_TEXTURE)
        return D3DXERR_INVALIDDATA;

    width = src_info->Width;
    height = src_info->Height;
    mip_levels = min(src_info->MipLevels, IDirect3DTexture9_GetLevelCount(texture));
    for (mip_level = 0; mip_level < mip_levels; mip_level++)
    {
        hr = calculate_dds_surface_size(src_info, width, height, &src_pitch, &mip_level_size);
        if (FAILED(hr)) return hr;

        SetRect(&src_rect, 0, 0, width, height);

        IDirect3DTexture9_GetSurfaceLevel(texture, mip_level, &surface);
        hr = D3DXLoadSurfaceFromMemory(surface, palette, NULL, pixels, src_info->Format, src_pitch,
            NULL, &src_rect, filter, color_key);
        IDirect3DSurface9_Release(surface);
        if (FAILED(hr)) return hr;

        pixels += mip_level_size;
        width = max(1, width / 2);
        height = max(1, height / 2);
    }

    return D3D_OK;
}

449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
HRESULT load_cube_texture_from_dds(IDirect3DCubeTexture9 *cube_texture, const void *src_data,
    const PALETTEENTRY *palette, DWORD filter, DWORD color_key, const D3DXIMAGE_INFO *src_info)
{
    HRESULT hr;
    int face;
    int mip_level;
    UINT size;
    RECT src_rect;
    UINT src_pitch;
    UINT mip_levels;
    UINT mip_level_size;
    IDirect3DSurface9 *surface;
    const struct dds_header *header = src_data;
    const BYTE *pixels = (BYTE *)(header + 1);

    if (src_info->ResourceType != D3DRTYPE_CUBETEXTURE)
        return D3DXERR_INVALIDDATA;

    if ((header->caps2 & DDS_CAPS2_CUBEMAP_ALL_FACES) != DDS_CAPS2_CUBEMAP_ALL_FACES)
    {
        WARN("Only full cubemaps are supported\n");
        return D3DXERR_INVALIDDATA;
    }

    mip_levels = min(src_info->MipLevels, IDirect3DCubeTexture9_GetLevelCount(cube_texture));
    for (face = D3DCUBEMAP_FACE_POSITIVE_X; face <= D3DCUBEMAP_FACE_NEGATIVE_Z; face++)
    {
        size = src_info->Width;
        for (mip_level = 0; mip_level < src_info->MipLevels; mip_level++)
        {
            hr = calculate_dds_surface_size(src_info, size, size, &src_pitch, &mip_level_size);
            if (FAILED(hr)) return hr;

            /* if texture has fewer mip levels than DDS file, skip excessive mip levels */
            if (mip_level < mip_levels)
            {
                SetRect(&src_rect, 0, 0, size, size);

                IDirect3DCubeTexture9_GetCubeMapSurface(cube_texture, face, mip_level, &surface);
                hr = D3DXLoadSurfaceFromMemory(surface, palette, NULL, pixels, src_info->Format, src_pitch,
                    NULL, &src_rect, filter, color_key);
                IDirect3DSurface9_Release(surface);
                if (FAILED(hr)) return hr;
            }

            pixels += mip_level_size;
            size = max(1, size / 2);
        }
    }

    return D3D_OK;
}

502 503 504 505 506 507 508 509 510 511 512
/************************************************************
 * D3DXGetImageInfoFromFileInMemory
 *
 * Fills a D3DXIMAGE_INFO structure with info about an image
 *
 * PARAMS
 *   data     [I] pointer to the image file data
 *   datasize [I] size of the passed data
 *   info     [O] pointer to the destination structure
 *
 * RETURNS
513 514 515 516 517 518 519 520
 *   Success: D3D_OK, if info is not NULL and data and datasize make up a valid image file or
 *                    if info is NULL and data and datasize are not NULL
 *   Failure: D3DXERR_INVALIDDATA, if data is no valid image file and datasize and info are not NULL
 *            D3DERR_INVALIDCALL, if data is NULL or
 *                                if datasize is 0
 *
 * NOTES
 *   datasize may be bigger than the actual file size
521 522 523 524
 *
 */
HRESULT WINAPI D3DXGetImageInfoFromFileInMemory(LPCVOID data, UINT datasize, D3DXIMAGE_INFO *info)
{
525 526 527 528 529 530
    IWICImagingFactory *factory;
    IWICBitmapDecoder *decoder = NULL;
    IWICStream *stream;
    HRESULT hr;
    HRESULT initresult;

531
    TRACE("(%p, %d, %p)\n", data, datasize, info);
532 533 534 535 536 537 538

    if (!data || !datasize)
        return D3DERR_INVALIDCALL;

    if (!info)
        return D3D_OK;

Józef Kucia's avatar
Józef Kucia committed
539 540
    if ((datasize >= 4) && !strncmp(data, "DDS ", 4)) {
        TRACE("File type is DDS\n");
541
        return get_image_info_from_dds(data, datasize, info);
Józef Kucia's avatar
Józef Kucia committed
542
    }
543

544 545 546 547 548 549 550 551 552 553 554 555
    initresult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

    hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, (void**)&factory);

    if (SUCCEEDED(hr)) {
        IWICImagingFactory_CreateStream(factory, &stream);
        IWICStream_InitializeFromMemory(stream, (BYTE*)data, datasize);
        hr = IWICImagingFactory_CreateDecoderFromStream(factory, (IStream*)stream, NULL, 0, &decoder);
        IStream_Release(stream);
        IWICImagingFactory_Release(factory);
    }

556
    if (FAILED(hr)) {
557
        if ((datasize >= 2) && (!strncmp(data, "P3", 2) || !strncmp(data, "P6", 2)))
558 559 560 561 562 563 564 565 566
            FIXME("File type PPM is not supported yet\n");
        else if ((datasize >= 2) && !strncmp(data, "BM", 2))
            FIXME("File type DIB is not supported yet\n");
        else if ((datasize >= 10) && !strncmp(data, "#?RADIANCE", 10))
            FIXME("File type HDR is not supported yet\n");
        else if ((datasize >= 2) && (!strncmp(data, "PF", 2) || !strncmp(data, "Pf", 2)))
            FIXME("File type PFM is not supported yet\n");
    }

567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
    if (SUCCEEDED(hr)) {
        GUID container_format;
        UINT frame_count;

        hr = IWICBitmapDecoder_GetContainerFormat(decoder, &container_format);
        if (SUCCEEDED(hr)) {
            if (IsEqualGUID(&container_format, &GUID_ContainerFormatBmp)) {
                TRACE("File type is BMP\n");
                info->ImageFileFormat = D3DXIFF_BMP;
            } else if (IsEqualGUID(&container_format, &GUID_ContainerFormatPng)) {
                TRACE("File type is PNG\n");
                info->ImageFileFormat = D3DXIFF_PNG;
            } else if(IsEqualGUID(&container_format, &GUID_ContainerFormatJpeg)) {
                TRACE("File type is JPG\n");
                info->ImageFileFormat = D3DXIFF_JPG;
582 583 584
            } else if(IsEqualGUID(&container_format, &GUID_WineContainerFormatTga)) {
                TRACE("File type is TGA\n");
                info->ImageFileFormat = D3DXIFF_TGA;
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
            } else {
                WARN("Unsupported image file format %s\n", debugstr_guid(&container_format));
                hr = D3DXERR_INVALIDDATA;
            }
        }

        if (SUCCEEDED(hr))
            hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
        if (SUCCEEDED(hr) && !frame_count)
            hr = D3DXERR_INVALIDDATA;

        if (SUCCEEDED(hr)) {
            IWICBitmapFrameDecode *frame = NULL;

            hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);

            if (SUCCEEDED(hr))
                hr = IWICBitmapFrameDecode_GetSize(frame, &info->Width, &info->Height);

            if (SUCCEEDED(hr)) {
                WICPixelFormatGUID pixel_format;

                hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &pixel_format);
                if (SUCCEEDED(hr)) {
609 610
                    info->Format = wic_guid_to_d3dformat(&pixel_format);
                    if (info->Format == D3DFMT_UNKNOWN) {
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
                        WARN("Unsupported pixel format %s\n", debugstr_guid(&pixel_format));
                        hr = D3DXERR_INVALIDDATA;
                    }
                }
            }

            if (frame)
                 IWICBitmapFrameDecode_Release(frame);

            info->Depth = 1;
            info->MipLevels = 1;
            info->ResourceType = D3DRTYPE_TEXTURE;
        }
    }

    if (decoder)
        IWICBitmapDecoder_Release(decoder);

    if (SUCCEEDED(initresult))
        CoUninitialize();
631

632
    if (FAILED(hr)) {
633
        TRACE("Invalid or unsupported image file\n");
634 635
        return D3DXERR_INVALIDDATA;
    }
636

637
    return D3D_OK;
638 639
}

640 641 642 643 644 645 646 647 648 649 650
/************************************************************
 * D3DXGetImageInfoFromFile
 *
 * RETURNS
 *   Success: D3D_OK, if we successfully load a valid image file or
 *                    if we successfully load a file which is no valid image and info is NULL
 *   Failure: D3DXERR_INVALIDDATA, if we fail to load file or
 *                                 if file is not a valid image file and info is not NULL
 *            D3DERR_INVALIDCALL, if file is NULL
 *
 */
651 652
HRESULT WINAPI D3DXGetImageInfoFromFileA(LPCSTR file, D3DXIMAGE_INFO *info)
{
653 654 655
    LPWSTR widename;
    HRESULT hr;
    int strlength;
656 657

    TRACE("(%s, %p): relay\n", debugstr_a(file), info);
658 659 660 661

    if( !file ) return D3DERR_INVALIDCALL;

    strlength = MultiByteToWideChar(CP_ACP, 0, file, -1, NULL, 0);
662
    widename = HeapAlloc(GetProcessHeap(), 0, strlength * sizeof(*widename));
663 664 665 666 667 668
    MultiByteToWideChar(CP_ACP, 0, file, -1, widename, strlength);

    hr = D3DXGetImageInfoFromFileW(widename, info);
    HeapFree(GetProcessHeap(), 0, widename);

    return hr;
669 670 671 672
}

HRESULT WINAPI D3DXGetImageInfoFromFileW(LPCWSTR file, D3DXIMAGE_INFO *info)
{
673 674 675
    HRESULT hr;
    DWORD size;
    LPVOID buffer;
676 677

    TRACE("(%s, %p): relay\n", debugstr_w(file), info);
678 679 680 681 682 683 684 685 686 687

    if( !file ) return D3DERR_INVALIDCALL;

    hr = map_view_of_file(file, &buffer, &size);
    if(FAILED(hr)) return D3DXERR_INVALIDDATA;

    hr = D3DXGetImageInfoFromFileInMemory(buffer, size, info);
    UnmapViewOfFile(buffer);

    return hr;
688 689
}

690 691 692 693 694 695 696 697 698
/************************************************************
 * D3DXGetImageInfoFromResource
 *
 * RETURNS
 *   Success: D3D_OK, if resource is a valid image file
 *   Failure: D3DXERR_INVALIDDATA, if resource is no valid image file or NULL or
 *                                 if we fail to load resource
 *
 */
699 700
HRESULT WINAPI D3DXGetImageInfoFromResourceA(HMODULE module, LPCSTR resource, D3DXIMAGE_INFO *info)
{
701
    HRSRC resinfo;
702 703

    TRACE("(%p, %s, %p)\n", module, debugstr_a(resource), info);
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721

    resinfo = FindResourceA(module, resource, (LPCSTR)RT_RCDATA);
    if(resinfo) {
        LPVOID buffer;
        HRESULT hr;
        DWORD size;

        hr = load_resource_into_memory(module, resinfo, &buffer, &size);
        if(FAILED(hr)) return D3DXERR_INVALIDDATA;
        return D3DXGetImageInfoFromFileInMemory(buffer, size, info);
    }

    resinfo = FindResourceA(module, resource, (LPCSTR)RT_BITMAP);
    if(resinfo) {
        FIXME("Implement loading bitmaps from resource type RT_BITMAP\n");
        return E_NOTIMPL;
    }
    return D3DXERR_INVALIDDATA;
722 723 724 725
}

HRESULT WINAPI D3DXGetImageInfoFromResourceW(HMODULE module, LPCWSTR resource, D3DXIMAGE_INFO *info)
{
726
    HRSRC resinfo;
727 728

    TRACE("(%p, %s, %p)\n", module, debugstr_w(resource), info);
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746

    resinfo = FindResourceW(module, resource, (LPCWSTR)RT_RCDATA);
    if(resinfo) {
        LPVOID buffer;
        HRESULT hr;
        DWORD size;

        hr = load_resource_into_memory(module, resinfo, &buffer, &size);
        if(FAILED(hr)) return D3DXERR_INVALIDDATA;
        return D3DXGetImageInfoFromFileInMemory(buffer, size, info);
    }

    resinfo = FindResourceW(module, resource, (LPCWSTR)RT_BITMAP);
    if(resinfo) {
        FIXME("Implement loading bitmaps from resource type RT_BITMAP\n");
        return E_NOTIMPL;
    }
    return D3DXERR_INVALIDDATA;
747
}
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771

/************************************************************
 * D3DXLoadSurfaceFromFileInMemory
 *
 * Loads data from a given buffer into a surface and fills a given
 * D3DXIMAGE_INFO structure with info about the source data.
 *
 * PARAMS
 *   pDestSurface [I] pointer to the surface
 *   pDestPalette [I] palette to use
 *   pDestRect    [I] to be filled area of the surface
 *   pSrcData     [I] pointer to the source data
 *   SrcDataSize  [I] size of the source data in bytes
 *   pSrcRect     [I] area of the source data to load
 *   dwFilter     [I] filter to apply on stretching
 *   Colorkey     [I] colorkey
 *   pSrcInfo     [O] pointer to a D3DXIMAGE_INFO structure
 *
 * RETURNS
 *   Success: D3D_OK
 *   Failure: D3DERR_INVALIDCALL, if pDestSurface or pSrcData or SrcDataSize are NULL
 *            D3DXERR_INVALIDDATA, if pSrcData is no valid image file
 *
 */
772 773 774
HRESULT WINAPI D3DXLoadSurfaceFromFileInMemory(IDirect3DSurface9 *pDestSurface,
        const PALETTEENTRY *pDestPalette, const RECT *pDestRect, const void *pSrcData, UINT SrcDataSize,
        const RECT *pSrcRect, DWORD dwFilter, D3DCOLOR Colorkey, D3DXIMAGE_INFO *pSrcInfo)
775
{
776 777 778
    D3DXIMAGE_INFO imginfo;
    HRESULT hr;

779 780 781 782 783 784 785 786 787
    IWICImagingFactory *factory;
    IWICBitmapDecoder *decoder;
    IWICBitmapFrameDecode *bitmapframe;
    IWICStream *stream;

    const PixelFormatDesc *formatdesc;
    WICRect wicrect;
    RECT rect;

788 789 790 791
    TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_data %p, src_data_size %u, "
            "src_rect %s, filter %#x, color_key 0x%08x, src_info %p.\n",
            pDestSurface, pDestPalette, wine_dbgstr_rect(pDestRect), pSrcData, SrcDataSize,
            wine_dbgstr_rect(pSrcRect), dwFilter, Colorkey, pSrcInfo);
792

793
    if (!pDestSurface || !pSrcData || !SrcDataSize)
794 795 796 797 798 799 800
        return D3DERR_INVALIDCALL;

    hr = D3DXGetImageInfoFromFileInMemory(pSrcData, SrcDataSize, &imginfo);

    if (FAILED(hr))
        return hr;

801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
    if (pSrcRect)
    {
        wicrect.X = pSrcRect->left;
        wicrect.Y = pSrcRect->top;
        wicrect.Width = pSrcRect->right - pSrcRect->left;
        wicrect.Height = pSrcRect->bottom - pSrcRect->top;
    }
    else
    {
        wicrect.X = 0;
        wicrect.Y = 0;
        wicrect.Width = imginfo.Width;
        wicrect.Height = imginfo.Height;
    }

    SetRect(&rect, 0, 0, wicrect.Width, wicrect.Height);

    if (imginfo.ImageFileFormat == D3DXIFF_DDS)
    {
        hr = load_surface_from_dds(pDestSurface, pDestPalette, pDestRect, pSrcData, &rect,
            dwFilter, Colorkey, &imginfo);
        if (SUCCEEDED(hr) && pSrcInfo)
            *pSrcInfo = imginfo;
        return hr;
    }

827
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
828

829 830
    if (FAILED(CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, (void**)&factory)))
        goto cleanup_err;
831

832 833 834 835 836
    if (FAILED(IWICImagingFactory_CreateStream(factory, &stream)))
    {
        IWICImagingFactory_Release(factory);
        goto cleanup_err;
    }
837

838
    IWICStream_InitializeFromMemory(stream, (BYTE*)pSrcData, SrcDataSize);
839

840
    hr = IWICImagingFactory_CreateDecoderFromStream(factory, (IStream*)stream, NULL, 0, &decoder);
841

842 843
    IStream_Release(stream);
    IWICImagingFactory_Release(factory);
844

845 846
    if (FAILED(hr))
        goto cleanup_err;
847

848
    hr = IWICBitmapDecoder_GetFrame(decoder, 0, &bitmapframe);
849

850 851
    if (FAILED(hr))
        goto cleanup_bmp;
852

853
    formatdesc = get_format_info(imginfo.Format);
854

855 856 857 858 859 860 861 862 863
    if (formatdesc->format == D3DFMT_UNKNOWN)
    {
        FIXME("Unsupported pixel format\n");
        hr = D3DXERR_INVALIDDATA;
    }
    else
    {
        BYTE *buffer;
        DWORD pitch;
864

865 866
        pitch = formatdesc->bytes_per_pixel * wicrect.Width;
        buffer = HeapAlloc(GetProcessHeap(), 0, pitch * wicrect.Height);
867

868 869
        hr = IWICBitmapFrameDecode_CopyPixels(bitmapframe, &wicrect, pitch,
                                              pitch * wicrect.Height, buffer);
870

871 872 873 874 875 876 877 878 879
        if (SUCCEEDED(hr))
        {
            hr = D3DXLoadSurfaceFromMemory(pDestSurface, pDestPalette, pDestRect,
                                           buffer, imginfo.Format, pitch,
                                           NULL, &rect, dwFilter, Colorkey);
        }

        HeapFree(GetProcessHeap(), 0, buffer);
    }
880

881
    IWICBitmapFrameDecode_Release(bitmapframe);
882 883

cleanup_bmp:
884
    IWICBitmapDecoder_Release(decoder);
885 886

cleanup_err:
887
    CoUninitialize();
888

889 890
    if (FAILED(hr))
        return D3DXERR_INVALIDDATA;
891 892 893 894 895

    if (pSrcInfo)
        *pSrcInfo = imginfo;

    return D3D_OK;
896
}
897

898 899 900
HRESULT WINAPI D3DXLoadSurfaceFromFileA(IDirect3DSurface9 *dst_surface,
        const PALETTEENTRY *dst_palette, const RECT *dst_rect, const char *src_file,
        const RECT *src_rect, DWORD filter, D3DCOLOR color_key, D3DXIMAGE_INFO *src_info)
901 902 903 904
{
    LPWSTR pWidename;
    HRESULT hr;
    int strlength;
905

906 907 908 909
    TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_file %s, "
            "src_rect %s, filter %#x, color_key 0x%08x, src_info %p.\n",
            dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), debugstr_a(src_file),
            wine_dbgstr_rect(src_rect), filter, color_key, src_info);
910

911 912
    if (!src_file || !dst_surface)
        return D3DERR_INVALIDCALL;
913

914
    strlength = MultiByteToWideChar(CP_ACP, 0, src_file, -1, NULL, 0);
915
    pWidename = HeapAlloc(GetProcessHeap(), 0, strlength * sizeof(*pWidename));
916
    MultiByteToWideChar(CP_ACP, 0, src_file, -1, pWidename, strlength);
917

918 919
    hr = D3DXLoadSurfaceFromFileW(dst_surface, dst_palette, dst_rect,
            pWidename, src_rect, filter, color_key, src_info);
920 921 922 923 924
    HeapFree(GetProcessHeap(), 0, pWidename);

    return hr;
}

925 926 927
HRESULT WINAPI D3DXLoadSurfaceFromFileW(IDirect3DSurface9 *dst_surface,
        const PALETTEENTRY *dst_palette, const RECT *dst_rect, const WCHAR *src_file,
        const RECT *src_rect, DWORD filter, D3DCOLOR color_key, D3DXIMAGE_INFO *src_info)
928
{
929 930
    UINT data_size;
    void *data;
931
    HRESULT hr;
932

933 934 935 936
    TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_file %s, "
            "src_rect %s, filter %#x, color_key 0x%08x, src_info %p.\n",
            dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), debugstr_w(src_file),
            wine_dbgstr_rect(src_rect), filter, color_key, src_info);
937

938 939
    if (!src_file || !dst_surface)
        return D3DERR_INVALIDCALL;
940

941 942
    if (FAILED(map_view_of_file(src_file, &data, &data_size)))
        return D3DXERR_INVALIDDATA;
943

944 945 946
    hr = D3DXLoadSurfaceFromFileInMemory(dst_surface, dst_palette, dst_rect,
            data, data_size, src_rect, filter, color_key, src_info);
    UnmapViewOfFile(data);
947 948 949

    return hr;
}
950

951 952 953
HRESULT WINAPI D3DXLoadSurfaceFromResourceA(IDirect3DSurface9 *dst_surface,
        const PALETTEENTRY *dst_palette, const RECT *dst_rect, HMODULE src_module, const char *resource,
        const RECT *src_rect, DWORD filter, D3DCOLOR color_key, D3DXIMAGE_INFO *src_info)
954 955
{
    HRSRC hResInfo;
956

957 958 959 960
    TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_module %p, resource %s, "
            "src_rect %s, filter %#x, color_key 0x%08x, src_info %p.\n",
            dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), src_module, debugstr_a(resource),
            wine_dbgstr_rect(src_rect), filter, color_key, src_info);
961

962 963
    if (!dst_surface)
        return D3DERR_INVALIDCALL;
964

965 966 967 968
    if ((hResInfo = FindResourceA(src_module, resource, (const char *)RT_RCDATA)))
    {
        UINT data_size;
        void *data;
969

970 971 972 973 974
        if (FAILED(load_resource_into_memory(src_module, hResInfo, &data, &data_size)))
            return D3DXERR_INVALIDDATA;

        return D3DXLoadSurfaceFromFileInMemory(dst_surface, dst_palette, dst_rect,
                data, data_size, src_rect, filter, color_key, src_info);
975 976
    }

977 978 979
    if ((hResInfo = FindResourceA(src_module, resource, (const char *)RT_BITMAP)))
    {
        FIXME("Implement loading bitmaps from resource type RT_BITMAP.\n");
980 981
        return E_NOTIMPL;
    }
982

983 984 985
    return D3DXERR_INVALIDDATA;
}

986 987 988
HRESULT WINAPI D3DXLoadSurfaceFromResourceW(IDirect3DSurface9 *dst_surface,
        const PALETTEENTRY *dst_palette, const RECT *dst_rect, HMODULE src_module, const WCHAR *resource,
        const RECT *src_rect, DWORD filter, D3DCOLOR color_key, D3DXIMAGE_INFO *src_info)
989 990
{
    HRSRC hResInfo;
991

992 993 994 995
    TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_module %p, resource %s, "
            "src_rect %s, filter %#x, color_key 0x%08x, src_info %p.\n",
            dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), src_module, debugstr_w(resource),
            wine_dbgstr_rect(src_rect), filter, color_key, src_info);
996

997 998
    if (!dst_surface)
        return D3DERR_INVALIDCALL;
999

1000 1001 1002 1003
    if ((hResInfo = FindResourceW(src_module, resource, (const WCHAR *)RT_RCDATA)))
    {
        UINT data_size;
        void *data;
1004

1005 1006 1007 1008 1009
        if (FAILED(load_resource_into_memory(src_module, hResInfo, &data, &data_size)))
            return D3DXERR_INVALIDDATA;

        return D3DXLoadSurfaceFromFileInMemory(dst_surface, dst_palette, dst_rect,
                data, data_size, src_rect, filter, color_key, src_info);
1010 1011
    }

1012 1013 1014
    if ((hResInfo = FindResourceW(src_module, resource, (const WCHAR *)RT_BITMAP)))
    {
        FIXME("Implement loading bitmaps from resource type RT_BITMAP.\n");
1015 1016
        return E_NOTIMPL;
    }
1017

1018 1019
    return D3DXERR_INVALIDDATA;
}
1020

1021 1022

/************************************************************
1023
 * helper functions for D3DXLoadSurfaceFromMemory
1024
 */
1025
struct argb_conversion_info
1026
{
1027 1028
    CONST PixelFormatDesc *srcformat;
    CONST PixelFormatDesc *destformat;
1029 1030 1031
    DWORD srcshift[4], destshift[4];
    DWORD srcmask[4], destmask[4];
    BOOL process_channel[4];
1032 1033
    DWORD channelmask;
};
1034

1035 1036 1037 1038 1039
static void init_argb_conversion_info(CONST PixelFormatDesc *srcformat, CONST PixelFormatDesc *destformat, struct argb_conversion_info *info)
{
    UINT i;
    ZeroMemory(info->process_channel, 4 * sizeof(BOOL));
    info->channelmask = 0;
1040

1041 1042
    info->srcformat  =  srcformat;
    info->destformat = destformat;
1043 1044 1045

    for(i = 0;i < 4;i++) {
        /* srcshift is used to extract the _relevant_ components */
1046
        info->srcshift[i]  =  srcformat->shift[i] + max( srcformat->bits[i] - destformat->bits[i], 0);
1047 1048

        /* destshift is used to move the components to the correct position */
1049
        info->destshift[i] = destformat->shift[i] + max(destformat->bits[i] -  srcformat->bits[i], 0);
1050

1051 1052
        info->srcmask[i]  = ((1 <<  srcformat->bits[i]) - 1) <<  srcformat->shift[i];
        info->destmask[i] = ((1 << destformat->bits[i]) - 1) << destformat->shift[i];
1053 1054 1055

        /* channelmask specifies bits which aren't used in the source format but in the destination one */
        if(destformat->bits[i]) {
1056 1057
            if(srcformat->bits[i]) info->process_channel[i] = TRUE;
            else info->channelmask |= info->destmask[i];
1058 1059
        }
    }
1060 1061
}

1062 1063 1064 1065 1066 1067
static DWORD dword_from_bytes(CONST BYTE *src, UINT bytes_per_pixel)
{
    DWORD ret = 0;
    static BOOL fixme_once;

    if(bytes_per_pixel > sizeof(DWORD)) {
1068
        if(!fixme_once++) FIXME("Unsupported image: %u bytes per pixel\n", bytes_per_pixel);
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
        bytes_per_pixel = sizeof(DWORD);
    }

    memcpy(&ret, src, bytes_per_pixel);
    return ret;
}

static void dword_to_bytes(BYTE *dst, DWORD dword, UINT bytes_per_pixel)
{
    static BOOL fixme_once;

    if(bytes_per_pixel > sizeof(DWORD)) {
1081
        if(!fixme_once++) FIXME("Unsupported image: %u bytes per pixel\n", bytes_per_pixel);
1082 1083 1084 1085 1086 1087 1088
        ZeroMemory(dst, bytes_per_pixel);
        bytes_per_pixel = sizeof(DWORD);
    }

    memcpy(dst, &dword, bytes_per_pixel);
}

1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
/************************************************************
 * 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 DWORD col, DWORD *out)
{
    UINT i = 0;
    for(;i < 4;i++)
        if(info->process_channel[i])
            out[i] = (col & info->srcmask[i]) >> info->srcshift[i];
}

/************************************************************
 * make_argb_color
 *
 * Recombines the output of get_relevant_argb_components and converts
 * it to the destination format.
 */
1109
static DWORD make_argb_color(CONST struct argb_conversion_info *info, CONST DWORD *in)
1110 1111
{
    UINT i;
1112
    DWORD val = 0;
1113 1114 1115 1116 1117

    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;
1118 1119
            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];
1120 1121
        }
    }
1122 1123
    val |= info->channelmask;   /* new channels are set to their maximal value */
    return val;
1124 1125
}

1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
static void format_to_vec4(const PixelFormatDesc *format, const DWORD *src, struct vec4 *dst)
{
    DWORD mask;

    if (format->bits[1])
    {
        mask = (1 << format->bits[1]) - 1;
        dst->x = (float)((*src >> format->shift[1]) & mask) / mask;
    }
    else
        dst->x = 1.0f;

    if (format->bits[2])
    {
        mask = (1 << format->bits[2]) - 1;
        dst->y = (float)((*src >> format->shift[2]) & mask) / mask;
    }
    else
        dst->y = 1.0f;

    if (format->bits[3])
    {
        mask = (1 << format->bits[3]) - 1;
        dst->z = (float)((*src >> format->shift[3]) & mask) / mask;
    }
    else
        dst->z = 1.0f;

    if (format->bits[0])
    {
        mask = (1 << format->bits[0]) - 1;
        dst->w = (float)((*src >> format->shift[0]) & mask) / mask;
    }
    else
        dst->w = 1.0f;
}

static void format_from_vec4(const PixelFormatDesc *format, const struct vec4 *src, DWORD *dst)
{
    *dst = 0;

    if (format->bits[1])
        *dst |= (DWORD)(src->x * ((1 << format->bits[1]) - 1) + 0.5f) << format->shift[1];
    if (format->bits[2])
        *dst |= (DWORD)(src->y * ((1 << format->bits[2]) - 1) + 0.5f) << format->shift[2];
    if (format->bits[3])
        *dst |= (DWORD)(src->z * ((1 << format->bits[3]) - 1) + 0.5f) << format->shift[3];
    if (format->bits[0])
        *dst |= (DWORD)(src->w * ((1 << format->bits[0]) - 1) + 0.5f) << format->shift[0];
}

1177 1178 1179 1180 1181
/************************************************************
 * copy_simple_data
 *
 * Copies the source buffer to the destination buffer, performing
 * any necessary format conversion and color keying.
1182
 * Pixels outsize the source rect are blacked out.
1183 1184
 * Works only for ARGB formats with 1 - 4 bytes per pixel.
 */
1185 1186
static void copy_simple_data(const BYTE *src, UINT srcpitch, SIZE src_size, const PixelFormatDesc *srcformat,
        BYTE *dest, UINT destpitch, SIZE dst_size, const PixelFormatDesc *destformat, D3DCOLOR colorkey)
1187
{
1188
    struct argb_conversion_info conv_info, ck_conv_info;
1189
    const PixelFormatDesc *ck_format = NULL;
1190
    DWORD channels[4], pixel;
1191 1192 1193 1194 1195
    UINT minwidth, minheight;
    UINT x, y;

    ZeroMemory(channels, sizeof(channels));
    init_argb_conversion_info(srcformat, destformat, &conv_info);
1196

1197 1198
    minwidth = (src_size.cx < dst_size.cx) ? src_size.cx : dst_size.cx;
    minheight = (src_size.cy < dst_size.cy) ? src_size.cy : dst_size.cy;
1199

1200 1201 1202 1203 1204
    if (colorkey)
    {
        /* Color keys are always represented in D3DFMT_A8R8G8B8 format. */
        ck_format = get_format_info(D3DFMT_A8R8G8B8);
        init_argb_conversion_info(srcformat, ck_format, &ck_conv_info);
1205 1206
    }

1207
    for(y = 0;y < minheight;y++) {
1208
        const BYTE *srcptr = src + y * srcpitch;
1209
        BYTE *destptr = dest + y * destpitch;
1210
        DWORD val;
1211

1212 1213
        for(x = 0;x < minwidth;x++) {
            /* extract source color components */
1214
            pixel = dword_from_bytes(srcptr, srcformat->bytes_per_pixel);
1215

1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
            if (!srcformat->to_rgba && !destformat->from_rgba)
            {
                get_relevant_argb_components(&conv_info, pixel, channels);
                val = make_argb_color(&conv_info, channels);

                if (colorkey)
                {
                    get_relevant_argb_components(&ck_conv_info, pixel, channels);
                    pixel = make_argb_color(&ck_conv_info, channels);
                    if (pixel == colorkey)
                        val &= ~conv_info.destmask[0];
                }
            }
            else
            {
                struct vec4 color, tmp;

                format_to_vec4(srcformat, &pixel, &color);
                if (srcformat->to_rgba)
                    srcformat->to_rgba(&color, &tmp);
                else
                    tmp = color;

                if (ck_format)
                {
                    format_from_vec4(ck_format, &tmp, &pixel);
                    if (pixel == colorkey)
                        tmp.w = 0.0f;
                }

                if (destformat->from_rgba)
                    destformat->from_rgba(&tmp, &color);
                else
                    color = tmp;
1250

1251
                format_from_vec4(destformat, &color, &val);
1252 1253
            }

1254
            dword_to_bytes(destptr, val, destformat->bytes_per_pixel);
1255 1256 1257
            srcptr  +=  srcformat->bytes_per_pixel;
            destptr += destformat->bytes_per_pixel;
        }
1258

1259 1260
        if (src_size.cx < dst_size.cx) /* black out remaining pixels */
            memset(destptr, 0, destformat->bytes_per_pixel * (dst_size.cx - src_size.cx));
1261
    }
1262 1263
    if (src_size.cy < dst_size.cy) /* black out remaining pixels */
        memset(dest + src_size.cy * destpitch, 0, destpitch * (dst_size.cy - src_size.cy));
1264 1265
}

1266 1267 1268 1269 1270 1271 1272 1273
/************************************************************
 * point_filter_simple_data
 *
 * Copies the source buffer to the destination buffer, performing
 * any necessary format conversion, color keying and stretching
 * using a point filter.
 * Works only for ARGB formats with 1 - 4 bytes per pixel.
 */
1274 1275
static void point_filter_simple_data(const BYTE *src, UINT srcpitch, SIZE src_size, const PixelFormatDesc *srcformat,
        BYTE *dest, UINT destpitch, SIZE dst_size, const PixelFormatDesc *destformat, D3DCOLOR colorkey)
1276
{
1277
    struct argb_conversion_info conv_info, ck_conv_info;
1278
    const PixelFormatDesc *ck_format = NULL;
1279
    DWORD channels[4], pixel;
1280 1281 1282 1283 1284 1285

    UINT x, y;

    ZeroMemory(channels, sizeof(channels));
    init_argb_conversion_info(srcformat, destformat, &conv_info);

1286 1287 1288 1289 1290
    if (colorkey)
    {
        /* Color keys are always represented in D3DFMT_A8R8G8B8 format. */
        ck_format = get_format_info(D3DFMT_A8R8G8B8);
        init_argb_conversion_info(srcformat, ck_format, &ck_conv_info);
1291 1292
    }

1293 1294
    for (y = 0; y < dst_size.cy; ++y)
    {
1295
        BYTE *destptr = dest + y * destpitch;
1296
        const BYTE *bufptr = src + srcpitch * (y * src_size.cy / dst_size.cy);
1297

1298 1299 1300
        for (x = 0; x < dst_size.cx; ++x)
        {
            const BYTE *srcptr = bufptr + (x * src_size.cx / dst_size.cx) * srcformat->bytes_per_pixel;
1301
            DWORD val;
1302

1303
            /* extract source color components */
1304
            pixel = dword_from_bytes(srcptr, srcformat->bytes_per_pixel);
1305

1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339
            if (!srcformat->to_rgba && !destformat->from_rgba)
            {
                get_relevant_argb_components(&conv_info, pixel, channels);
                val = make_argb_color(&conv_info, channels);

                if (colorkey)
                {
                    get_relevant_argb_components(&ck_conv_info, pixel, channels);
                    pixel = make_argb_color(&ck_conv_info, channels);
                    if (pixel == colorkey)
                        val &= ~conv_info.destmask[0];
                }
            }
            else
            {
                struct vec4 color, tmp;

                format_to_vec4(srcformat, &pixel, &color);
                if (srcformat->to_rgba)
                    srcformat->to_rgba(&color, &tmp);
                else
                    tmp = color;

                if (ck_format)
                {
                    format_from_vec4(ck_format, &tmp, &pixel);
                    if (pixel == colorkey)
                        tmp.w = 0.0f;
                }

                if (destformat->from_rgba)
                    destformat->from_rgba(&tmp, &color);
                else
                    color = tmp;
1340

1341
                format_from_vec4(destformat, &color, &val);
1342 1343
            }

1344
            dword_to_bytes(destptr, val, destformat->bytes_per_pixel);
1345 1346 1347 1348 1349
            destptr += destformat->bytes_per_pixel;
        }
    }
}

1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371
/************************************************************
 * D3DXLoadSurfaceFromMemory
 *
 * Loads data from a given memory chunk into a surface,
 * applying any of the specified filters.
 *
 * PARAMS
 *   pDestSurface [I] pointer to the surface
 *   pDestPalette [I] palette to use
 *   pDestRect    [I] to be filled area of the surface
 *   pSrcMemory   [I] pointer to the source data
 *   SrcFormat    [I] format of the source pixel data
 *   SrcPitch     [I] number of bytes in a row
 *   pSrcPalette  [I] palette used in the source image
 *   pSrcRect     [I] area of the source data to load
 *   dwFilter     [I] filter to apply on stretching
 *   Colorkey     [I] colorkey
 *
 * RETURNS
 *   Success: D3D_OK, if we successfully load the pixel data into our surface or
 *                    if pSrcMemory is NULL but the other parameters are valid
 *   Failure: D3DERR_INVALIDCALL, if pDestSurface, SrcPitch or pSrcRect are NULL or
1372 1373
 *                                if SrcFormat is an invalid format (other than D3DFMT_UNKNOWN) or
 *                                if DestRect is invalid
1374 1375 1376 1377
 *            D3DXERR_INVALIDDATA, if we fail to lock pDestSurface
 *            E_FAIL, if SrcFormat is D3DFMT_UNKNOWN or the dimensions of pSrcRect are invalid
 *
 * NOTES
1378 1379
 *   pSrcRect specifies the dimensions of the source data;
 *   negative values for pSrcRect are allowed as we're only looking at the width and height anyway.
1380 1381
 *
 */
1382 1383 1384 1385
HRESULT WINAPI D3DXLoadSurfaceFromMemory(IDirect3DSurface9 *dst_surface,
        const PALETTEENTRY *dst_palette, const RECT *dst_rect, const void *src_memory,
        D3DFORMAT src_format, UINT src_pitch, const PALETTEENTRY *src_palette, const RECT *src_rect,
        DWORD filter, D3DCOLOR color_key)
1386
{
1387 1388 1389
    CONST PixelFormatDesc *srcformatdesc, *destformatdesc;
    D3DSURFACE_DESC surfdesc;
    D3DLOCKED_RECT lockrect;
1390
    SIZE src_size, dst_size;
1391
    HRESULT hr;
1392

1393 1394 1395
    TRACE("(%p, %p, %s, %p, %#x, %u, %p, %s %#x, 0x%08x)\n",
            dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), src_memory, src_format,
            src_pitch, src_palette, wine_dbgstr_rect(src_rect), filter, color_key);
1396

1397 1398 1399 1400 1401 1402
    if (!dst_surface || !src_memory || !src_rect)
        return D3DERR_INVALIDCALL;
    if (src_format == D3DFMT_UNKNOWN
            || src_rect->left >= src_rect->right
            || src_rect->top >= src_rect->bottom)
        return E_FAIL;
1403

1404 1405
    if (filter == D3DX_DEFAULT)
        filter = D3DX_FILTER_TRIANGLE | D3DX_FILTER_DITHER;
1406

1407
    IDirect3DSurface9_GetDesc(dst_surface, &surfdesc);
1408

1409 1410 1411
    src_size.cx = src_rect->right - src_rect->left;
    src_size.cy = src_rect->bottom - src_rect->top;
    if (!dst_rect)
1412 1413 1414 1415 1416 1417
    {
        dst_size.cx = surfdesc.Width;
        dst_size.cy = surfdesc.Height;
    }
    else
    {
1418 1419 1420 1421 1422 1423 1424 1425
        if (dst_rect->left > dst_rect->right || dst_rect->right > surfdesc.Width)
            return D3DERR_INVALIDCALL;
        if (dst_rect->top > dst_rect->bottom || dst_rect->bottom > surfdesc.Height)
            return D3DERR_INVALIDCALL;
        if (dst_rect->left < 0 || dst_rect->top < 0)
            return D3DERR_INVALIDCALL;
        dst_size.cx = dst_rect->right - dst_rect->left;
        dst_size.cy = dst_rect->bottom - dst_rect->top;
1426 1427
        if (!dst_size.cx || !dst_size.cy)
            return D3D_OK;
1428 1429
    }

1430
    srcformatdesc = get_format_info(src_format);
1431 1432 1433 1434 1435 1436 1437
    if (srcformatdesc->type == FORMAT_UNKNOWN)
        return E_NOTIMPL;

    destformatdesc = get_format_info(surfdesc.Format);
    if (destformatdesc->type == FORMAT_UNKNOWN)
        return E_NOTIMPL;

1438
    if (src_format == surfdesc.Format
1439 1440
            && dst_size.cx == src_size.cx
            && dst_size.cy == src_size.cy) /* Simple copy. */
1441
    {
1442 1443
        UINT row_block_count = ((src_size.cx + srcformatdesc->block_width - 1) / srcformatdesc->block_width);
        UINT row_count = (src_size.cy + srcformatdesc->block_height - 1) / srcformatdesc->block_height;
1444 1445
        const BYTE *src_addr;
        BYTE *dst_addr;
1446 1447
        UINT row;

1448 1449 1450
        if (src_rect->left & (srcformatdesc->block_width - 1)
                || src_rect->top & (srcformatdesc->block_height - 1)
                || (src_rect->right & (srcformatdesc->block_width - 1)
1451
                    && src_size.cx != surfdesc.Width)
1452
                || (src_rect->bottom & (srcformatdesc->block_height - 1)
1453
                    && src_size.cy != surfdesc.Height))
1454
        {
1455
            WARN("Source rect %s is misaligned.\n", wine_dbgstr_rect(src_rect));
1456 1457
            return D3DXERR_INVALIDDATA;
        }
1458

1459
        if (FAILED(hr = IDirect3DSurface9_LockRect(dst_surface, &lockrect, dst_rect, 0)))
1460 1461
            return D3DXERR_INVALIDDATA;

1462 1463 1464
        src_addr = src_memory;
        src_addr += (src_rect->top / srcformatdesc->block_height) * src_pitch;
        src_addr += (src_rect->left / srcformatdesc->block_width) * srcformatdesc->block_byte_count;
1465 1466
        dst_addr = lockrect.pBits;

1467
        for (row = 0; row < row_count; ++row)
1468
        {
1469
            memcpy(dst_addr, src_addr, row_block_count * srcformatdesc->block_byte_count);
1470
            src_addr += src_pitch;
1471 1472 1473
            dst_addr += lockrect.Pitch;
        }

1474
        IDirect3DSurface9_UnlockRect(dst_surface);
1475 1476 1477 1478 1479 1480 1481
    }
    else /* Stretching or format conversion. */
    {
        if (srcformatdesc->bytes_per_pixel > 4)
            return E_NOTIMPL;
        if (destformatdesc->bytes_per_pixel > 4)
            return E_NOTIMPL;
1482 1483 1484 1485
        if (srcformatdesc->block_height != 1 || srcformatdesc->block_width != 1)
            return E_NOTIMPL;
        if (destformatdesc->block_height != 1 || destformatdesc->block_width != 1)
            return E_NOTIMPL;
1486

1487
        if (FAILED(hr = IDirect3DSurface9_LockRect(dst_surface, &lockrect, dst_rect, 0)))
1488 1489
            return D3DXERR_INVALIDDATA;

1490
        if ((filter & 0xf) == D3DX_FILTER_NONE)
1491
        {
1492 1493
            copy_simple_data(src_memory, src_pitch, src_size, srcformatdesc,
                    lockrect.pBits, lockrect.Pitch, dst_size, destformatdesc, color_key);
1494
        }
1495
        else /* if ((filter & 0xf) == D3DX_FILTER_POINT) */
1496
        {
1497 1498
            if ((filter & 0xf) != D3DX_FILTER_POINT)
                FIXME("Unhandled filter %#x.\n", filter);
1499

1500 1501
            /* Always apply a point filter until D3DX_FILTER_LINEAR,
             * D3DX_FILTER_TRIANGLE and D3DX_FILTER_BOX are implemented. */
1502 1503
            point_filter_simple_data(src_memory, src_pitch, src_size, srcformatdesc,
                    lockrect.pBits, lockrect.Pitch, dst_size, destformatdesc, color_key);
1504
        }
1505

1506
        IDirect3DSurface9_UnlockRect(dst_surface);
1507
    }
1508 1509

    return D3D_OK;
1510
}
1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533

/************************************************************
 * D3DXLoadSurfaceFromSurface
 *
 * Copies the contents from one surface to another, performing any required
 * format conversion, resizing or filtering.
 *
 * PARAMS
 *   pDestSurface [I] pointer to the destination surface
 *   pDestPalette [I] palette to use
 *   pDestRect    [I] to be filled area of the surface
 *   pSrcSurface  [I] pointer to the source surface
 *   pSrcPalette  [I] palette used for the source surface
 *   pSrcRect     [I] area of the source data to load
 *   dwFilter     [I] filter to apply on resizing
 *   Colorkey     [I] any ARGB value or 0 to disable color-keying
 *
 * RETURNS
 *   Success: D3D_OK
 *   Failure: D3DERR_INVALIDCALL, if pDestSurface or pSrcSurface are NULL
 *            D3DXERR_INVALIDDATA, if one of the surfaces is not lockable
 *
 */
1534 1535 1536
HRESULT WINAPI D3DXLoadSurfaceFromSurface(IDirect3DSurface9 *dst_surface,
        const PALETTEENTRY *dst_palette, const RECT *dst_rect, IDirect3DSurface9 *src_surface,
        const PALETTEENTRY *src_palette, const RECT *src_rect, DWORD filter, D3DCOLOR color_key)
1537 1538 1539 1540 1541
{
    RECT rect;
    D3DLOCKED_RECT lock;
    D3DSURFACE_DESC SrcDesc;
    HRESULT hr;
1542

1543 1544 1545 1546
    TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_surface %p, "
            "src_palette %p, src_rect %s, filter %#x, color_key 0x%08x.\n",
            dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), src_surface,
            src_palette, wine_dbgstr_rect(src_rect), filter, color_key);
1547

1548 1549
    if (!dst_surface || !src_surface)
        return D3DERR_INVALIDCALL;
1550

1551
    IDirect3DSurface9_GetDesc(src_surface, &SrcDesc);
1552

1553 1554 1555 1556
    if (!src_rect)
        SetRect(&rect, 0, 0, SrcDesc.Width, SrcDesc.Height);
    else
        rect = *src_rect;
1557

1558 1559 1560 1561 1562
    if (FAILED(IDirect3DSurface9_LockRect(src_surface, &lock, NULL, D3DLOCK_READONLY)))
        return D3DXERR_INVALIDDATA;

    hr = D3DXLoadSurfaceFromMemory(dst_surface, dst_palette, dst_rect,
            lock.pBits, SrcDesc.Format, lock.Pitch, src_palette, &rect, filter, color_key);
1563

1564
    IDirect3DSurface9_UnlockRect(src_surface);
1565 1566 1567

    return hr;
}
1568 1569


1570 1571
HRESULT WINAPI D3DXSaveSurfaceToFileA(const char *dst_filename, D3DXIMAGE_FILEFORMAT file_format,
        IDirect3DSurface9 *src_surface, const PALETTEENTRY *src_palette, const RECT *src_rect)
1572
{
1573 1574 1575
    int len;
    WCHAR *filename;
    HRESULT hr;
1576
    ID3DXBuffer *buffer;
1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587

    TRACE("(%s, %#x, %p, %p, %s): relay\n",
            wine_dbgstr_a(dst_filename), file_format, src_surface, src_palette, wine_dbgstr_rect(src_rect));

    if (!dst_filename) return D3DERR_INVALIDCALL;

    len = MultiByteToWideChar(CP_ACP, 0, dst_filename, -1, NULL, 0);
    filename = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
    if (!filename) return E_OUTOFMEMORY;
    MultiByteToWideChar(CP_ACP, 0, dst_filename, -1, filename, len);

1588 1589 1590 1591 1592 1593
    hr = D3DXSaveSurfaceToFileInMemory(&buffer, file_format, src_surface, src_palette, src_rect);
    if (SUCCEEDED(hr))
    {
        hr = write_buffer_to_file(filename, buffer);
        ID3DXBuffer_Release(buffer);
    }
1594 1595 1596

    HeapFree(GetProcessHeap(), 0, filename);
    return hr;
1597 1598
}

1599 1600
HRESULT WINAPI D3DXSaveSurfaceToFileW(const WCHAR *dst_filename, D3DXIMAGE_FILEFORMAT file_format,
        IDirect3DSurface9 *src_surface, const PALETTEENTRY *src_palette, const RECT *src_rect)
1601
{
1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
    HRESULT hr;
    ID3DXBuffer *buffer;

    TRACE("(%s, %#x, %p, %p, %s): relay\n",
        wine_dbgstr_w(dst_filename), file_format, src_surface, src_palette, wine_dbgstr_rect(src_rect));

    if (!dst_filename) return D3DERR_INVALIDCALL;

    hr = D3DXSaveSurfaceToFileInMemory(&buffer, file_format, src_surface, src_palette, src_rect);
    if (SUCCEEDED(hr))
    {
        hr = write_buffer_to_file(dst_filename, buffer);
        ID3DXBuffer_Release(buffer);
    }

    return hr;
}

HRESULT WINAPI D3DXSaveSurfaceToFileInMemory(ID3DXBuffer **dst_buffer, D3DXIMAGE_FILEFORMAT file_format,
        IDirect3DSurface9 *src_surface, const PALETTEENTRY *src_palette, const RECT *src_rect)
{
1623 1624 1625
    IWICBitmapEncoder *encoder = NULL;
    IWICBitmapFrameEncode *frame = NULL;
    IPropertyBag2 *encoder_options = NULL;
1626
    IStream *stream = NULL;
1627 1628 1629 1630 1631 1632 1633 1634 1635
    HRESULT hr;
    HRESULT initresult;
    const CLSID *encoder_clsid;
    const GUID *pixel_format_guid;
    WICPixelFormatGUID wic_pixel_format;
    D3DFORMAT d3d_pixel_format;
    D3DSURFACE_DESC src_surface_desc;
    D3DLOCKED_RECT locked_rect;
    int width, height;
1636 1637 1638 1639
    STATSTG stream_stats;
    HGLOBAL stream_hglobal;
    ID3DXBuffer *buffer;
    DWORD size;
1640

1641 1642
    TRACE("(%p, %#x, %p, %p, %s)\n",
        dst_buffer, file_format, src_surface, src_palette, wine_dbgstr_rect(src_rect));
1643

1644
    if (!dst_buffer || !src_surface) return D3DERR_INVALIDCALL;
1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678

    if (src_palette)
    {
        FIXME("Saving surfaces with palettized pixel formats not implemented yet\n");
        return D3DERR_INVALIDCALL;
    }

    switch (file_format)
    {
        case D3DXIFF_BMP:
            encoder_clsid = &CLSID_WICBmpEncoder;
            break;
        case D3DXIFF_PNG:
            encoder_clsid = &CLSID_WICPngEncoder;
            break;
        case D3DXIFF_JPG:
            encoder_clsid = &CLSID_WICJpegEncoder;
            break;
        case D3DXIFF_DDS:
        case D3DXIFF_DIB:
        case D3DXIFF_HDR:
        case D3DXIFF_PFM:
        case D3DXIFF_TGA:
        case D3DXIFF_PPM:
            FIXME("File format %#x is not supported yet\n", file_format);
            return E_NOTIMPL;
        default:
            return D3DERR_INVALIDCALL;
    }

    IDirect3DSurface9_GetDesc(src_surface, &src_surface_desc);
    if (src_rect)
    {
        if (src_rect->left == src_rect->right || src_rect->top == src_rect->bottom)
1679 1680 1681 1682
        {
            WARN("Invalid rectangle with 0 area\n");
            return D3DXCreateBuffer(64, dst_buffer);
        }
1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704
        if (src_rect->left < 0 || src_rect->top < 0)
            return D3DERR_INVALIDCALL;
        if (src_rect->left > src_rect->right || src_rect->top > src_rect->bottom)
            return D3DERR_INVALIDCALL;
        if (src_rect->right > src_surface_desc.Width || src_rect->bottom > src_surface_desc.Height)
            return D3DERR_INVALIDCALL;

        width = src_rect->right - src_rect->left;
        height = src_rect->bottom - src_rect->top;
    }
    else
    {
        width = src_surface_desc.Width;
        height = src_surface_desc.Height;
    }

    initresult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

    hr = CoCreateInstance(encoder_clsid, NULL, CLSCTX_INPROC_SERVER,
        &IID_IWICBitmapEncoder, (void **)&encoder);
    if (FAILED(hr)) goto cleanup_err;

1705 1706 1707 1708
    hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
    if (FAILED(hr)) goto cleanup_err;

    hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache);
1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744
    if (FAILED(hr)) goto cleanup_err;

    hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frame, &encoder_options);
    if (FAILED(hr)) goto cleanup_err;

    hr = IWICBitmapFrameEncode_Initialize(frame, encoder_options);
    if (FAILED(hr)) goto cleanup_err;

    hr = IWICBitmapFrameEncode_SetSize(frame, width, height);
    if (FAILED(hr)) goto cleanup_err;

    pixel_format_guid = d3dformat_to_wic_guid(src_surface_desc.Format);
    if (!pixel_format_guid)
    {
        FIXME("Pixel format %#x is not supported yet\n", src_surface_desc.Format);
        hr = E_NOTIMPL;
        goto cleanup;
    }

    memcpy(&wic_pixel_format, pixel_format_guid, sizeof(GUID));
    hr = IWICBitmapFrameEncode_SetPixelFormat(frame, &wic_pixel_format);
    d3d_pixel_format = wic_guid_to_d3dformat(&wic_pixel_format);
    if (SUCCEEDED(hr) && d3d_pixel_format != D3DFMT_UNKNOWN)
    {
        TRACE("Using pixel format %s %#x\n", debugstr_guid(&wic_pixel_format), d3d_pixel_format);

        if (src_surface_desc.Format == d3d_pixel_format) /* Simple copy */
        {
            hr = IDirect3DSurface9_LockRect(src_surface, &locked_rect, src_rect, D3DLOCK_READONLY);
            if (SUCCEEDED(hr))
            {
                IWICBitmapFrameEncode_WritePixels(frame, height,
                    locked_rect.Pitch, height * locked_rect.Pitch, locked_rect.pBits);
                IDirect3DSurface9_UnlockRect(src_surface);
            }
        }
1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756
        else /* Pixel format conversion */
        {
            const PixelFormatDesc *src_format_desc, *dst_format_desc;
            SIZE size;
            DWORD dst_pitch;
            void *dst_data;

            src_format_desc = get_format_info(src_surface_desc.Format);
            dst_format_desc = get_format_info(d3d_pixel_format);
            if (src_format_desc->format == D3DFMT_UNKNOWN || dst_format_desc->format == D3DFMT_UNKNOWN)
            {
                FIXME("Unsupported pixel format conversion %#x -> %#x\n",
1757
                    src_surface_desc.Format, d3d_pixel_format);
1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794
                hr = E_NOTIMPL;
                goto cleanup;
            }

            if (src_format_desc->bytes_per_pixel > 4
                || dst_format_desc->bytes_per_pixel > 4
                || src_format_desc->block_height != 1 || src_format_desc->block_width != 1
                || dst_format_desc->block_height != 1 || dst_format_desc->block_width != 1)
            {
                hr = E_NOTIMPL;
                goto cleanup;
            }

            size.cx = width;
            size.cy = height;
            dst_pitch = width * dst_format_desc->bytes_per_pixel;
            dst_data = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height);
            if (!dst_data)
            {
                hr = E_OUTOFMEMORY;
                goto cleanup;
            }

            hr = IDirect3DSurface9_LockRect(src_surface, &locked_rect, src_rect, D3DLOCK_READONLY);
            if (SUCCEEDED(hr))
            {
                copy_simple_data(locked_rect.pBits, locked_rect.Pitch, size, src_format_desc,
                    dst_data, dst_pitch, size, dst_format_desc, 0);
                IDirect3DSurface9_UnlockRect(src_surface);
            }

            IWICBitmapFrameEncode_WritePixels(frame, height, dst_pitch, dst_pitch * height, dst_data);
            HeapFree(GetProcessHeap(), 0, dst_data);
        }

        hr = IWICBitmapFrameEncode_Commit(frame);
        if (SUCCEEDED(hr)) hr = IWICBitmapEncoder_Commit(encoder);
1795 1796 1797
    }
    else WARN("Unsupported pixel format %#x\n", src_surface_desc.Format);

1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822
    /* copy data from stream to ID3DXBuffer */
    hr = IStream_Stat(stream, &stream_stats, STATFLAG_NONAME);
    if (FAILED(hr)) goto cleanup_err;

    if (stream_stats.cbSize.u.HighPart != 0)
    {
        hr = D3DXERR_INVALIDDATA;
        goto cleanup;
    }
    size = stream_stats.cbSize.u.LowPart;

    hr = D3DXCreateBuffer(size, &buffer);
    if (FAILED(hr)) goto cleanup;

    hr = GetHGlobalFromStream(stream, &stream_hglobal);
    if (SUCCEEDED(hr))
    {
        void *buffer_pointer = ID3DXBuffer_GetBufferPointer(buffer);
        void *stream_data = GlobalLock(stream_hglobal);
        memcpy(buffer_pointer, stream_data, size);
        GlobalUnlock(stream_hglobal);
        *dst_buffer = buffer;
    }
    else ID3DXBuffer_Release(buffer);

1823
cleanup_err:
1824 1825
    if (FAILED(hr) && hr != E_OUTOFMEMORY)
        hr = D3DERR_INVALIDCALL;
1826 1827

cleanup:
1828
    if (stream) IStream_Release(stream);
1829 1830 1831 1832 1833 1834 1835 1836 1837

    if (frame) IWICBitmapFrameEncode_Release(frame);
    if (encoder_options) IPropertyBag2_Release(encoder_options);

    if (encoder) IWICBitmapEncoder_Release(encoder);

    if (SUCCEEDED(initresult)) CoUninitialize();

    return hr;
1838
}