xref: /reactos/dll/directx/wine/d3dx9_36/surface.c (revision 19f6fc25)
1 /*
2  * Copyright (C) 2009-2010 Tony Wasserka
3  * Copyright (C) 2012 Józef Kucia
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  *
19  */
20 
21 #include "config.h"
22 #include "wine/port.h"
23 
24 #include "d3dx9_private.h"
25 
26 #include "initguid.h"
27 #include "ole2.h"
28 #include "wincodec.h"
29 
30 #include "wine/wined3d.h"
31 
32 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
33 
34 
35 /* Wine-specific WIC GUIDs */
36 DEFINE_GUID(GUID_WineContainerFormatTga, 0x0c44fda1,0xa5c5,0x4298,0x96,0x85,0x47,0x3f,0xc1,0x7c,0xd3,0x22);
37 
38 static const struct
39 {
40     const GUID *wic_guid;
41     D3DFORMAT d3dformat;
42 } wic_pixel_formats[] = {
43     { &GUID_WICPixelFormat8bppIndexed, D3DFMT_P8 },
44     { &GUID_WICPixelFormat1bppIndexed, D3DFMT_P8 },
45     { &GUID_WICPixelFormat4bppIndexed, D3DFMT_P8 },
46     { &GUID_WICPixelFormat8bppGray, D3DFMT_L8 },
47     { &GUID_WICPixelFormat16bppBGR555, D3DFMT_X1R5G5B5 },
48     { &GUID_WICPixelFormat16bppBGR565, D3DFMT_R5G6B5 },
49     { &GUID_WICPixelFormat24bppBGR, D3DFMT_R8G8B8 },
50     { &GUID_WICPixelFormat32bppBGR, D3DFMT_X8R8G8B8 },
51     { &GUID_WICPixelFormat32bppBGRA, D3DFMT_A8R8G8B8 }
52 };
53 
54 static D3DFORMAT wic_guid_to_d3dformat(const GUID *guid)
55 {
56     unsigned int i;
57 
58     for (i = 0; i < ARRAY_SIZE(wic_pixel_formats); i++)
59     {
60         if (IsEqualGUID(wic_pixel_formats[i].wic_guid, guid))
61             return wic_pixel_formats[i].d3dformat;
62     }
63 
64     return D3DFMT_UNKNOWN;
65 }
66 
67 static const GUID *d3dformat_to_wic_guid(D3DFORMAT format)
68 {
69     unsigned int i;
70 
71     for (i = 0; i < ARRAY_SIZE(wic_pixel_formats); i++)
72     {
73         if (wic_pixel_formats[i].d3dformat == format)
74             return wic_pixel_formats[i].wic_guid;
75     }
76 
77     return NULL;
78 }
79 
80 /* dds_header.flags */
81 #define DDS_CAPS 0x1
82 #define DDS_HEIGHT 0x2
83 #define DDS_WIDTH 0x4
84 #define DDS_PITCH 0x8
85 #define DDS_PIXELFORMAT 0x1000
86 #define DDS_MIPMAPCOUNT 0x20000
87 #define DDS_LINEARSIZE 0x80000
88 #define DDS_DEPTH 0x800000
89 
90 /* dds_header.caps */
91 #define DDS_CAPS_COMPLEX 0x8
92 #define DDS_CAPS_TEXTURE 0x1000
93 #define DDS_CAPS_MIPMAP 0x400000
94 
95 /* dds_header.caps2 */
96 #define DDS_CAPS2_CUBEMAP 0x200
97 #define DDS_CAPS2_CUBEMAP_POSITIVEX 0x400
98 #define DDS_CAPS2_CUBEMAP_NEGATIVEX 0x800
99 #define DDS_CAPS2_CUBEMAP_POSITIVEY 0x1000
100 #define DDS_CAPS2_CUBEMAP_NEGATIVEY 0x2000
101 #define DDS_CAPS2_CUBEMAP_POSITIVEZ 0x4000
102 #define DDS_CAPS2_CUBEMAP_NEGATIVEZ 0x8000
103 #define DDS_CAPS2_CUBEMAP_ALL_FACES ( DDS_CAPS2_CUBEMAP_POSITIVEX | DDS_CAPS2_CUBEMAP_NEGATIVEX \
104                                     | DDS_CAPS2_CUBEMAP_POSITIVEY | DDS_CAPS2_CUBEMAP_NEGATIVEY \
105                                     | DDS_CAPS2_CUBEMAP_POSITIVEZ | DDS_CAPS2_CUBEMAP_NEGATIVEZ )
106 #define DDS_CAPS2_VOLUME 0x200000
107 
108 /* dds_pixel_format.flags */
109 #define DDS_PF_ALPHA 0x1
110 #define DDS_PF_ALPHA_ONLY 0x2
111 #define DDS_PF_FOURCC 0x4
112 #define DDS_PF_RGB 0x40
113 #define DDS_PF_YUV 0x200
114 #define DDS_PF_LUMINANCE 0x20000
115 #define DDS_PF_BUMPLUMINANCE 0x40000
116 #define DDS_PF_BUMPDUDV 0x80000
117 
118 struct dds_pixel_format
119 {
120     DWORD size;
121     DWORD flags;
122     DWORD fourcc;
123     DWORD bpp;
124     DWORD rmask;
125     DWORD gmask;
126     DWORD bmask;
127     DWORD amask;
128 };
129 
130 struct dds_header
131 {
132     DWORD signature;
133     DWORD size;
134     DWORD flags;
135     DWORD height;
136     DWORD width;
137     DWORD pitch_or_linear_size;
138     DWORD depth;
139     DWORD miplevels;
140     DWORD reserved[11];
141     struct dds_pixel_format pixel_format;
142     DWORD caps;
143     DWORD caps2;
144     DWORD caps3;
145     DWORD caps4;
146     DWORD reserved2;
147 };
148 
149 static D3DFORMAT dds_fourcc_to_d3dformat(DWORD fourcc)
150 {
151     unsigned int i;
152     static const DWORD known_fourcc[] = {
153         D3DFMT_UYVY,
154         D3DFMT_YUY2,
155         D3DFMT_R8G8_B8G8,
156         D3DFMT_G8R8_G8B8,
157         D3DFMT_DXT1,
158         D3DFMT_DXT2,
159         D3DFMT_DXT3,
160         D3DFMT_DXT4,
161         D3DFMT_DXT5,
162         D3DFMT_R16F,
163         D3DFMT_G16R16F,
164         D3DFMT_A16B16G16R16F,
165         D3DFMT_R32F,
166         D3DFMT_G32R32F,
167         D3DFMT_A32B32G32R32F,
168     };
169 
170     for (i = 0; i < ARRAY_SIZE(known_fourcc); i++)
171     {
172         if (known_fourcc[i] == fourcc)
173             return fourcc;
174     }
175 
176     WARN("Unknown FourCC %#x\n", fourcc);
177     return D3DFMT_UNKNOWN;
178 }
179 
180 static const struct {
181     DWORD bpp;
182     DWORD rmask;
183     DWORD gmask;
184     DWORD bmask;
185     DWORD amask;
186     D3DFORMAT format;
187 } rgb_pixel_formats[] = {
188     { 8, 0xe0, 0x1c, 0x03, 0, D3DFMT_R3G3B2 },
189     { 16, 0xf800, 0x07e0, 0x001f, 0x0000, D3DFMT_R5G6B5 },
190     { 16, 0x7c00, 0x03e0, 0x001f, 0x8000, D3DFMT_A1R5G5B5 },
191     { 16, 0x7c00, 0x03e0, 0x001f, 0x0000, D3DFMT_X1R5G5B5 },
192     { 16, 0x0f00, 0x00f0, 0x000f, 0xf000, D3DFMT_A4R4G4B4 },
193     { 16, 0x0f00, 0x00f0, 0x000f, 0x0000, D3DFMT_X4R4G4B4 },
194     { 16, 0x00e0, 0x001c, 0x0003, 0xff00, D3DFMT_A8R3G3B2 },
195     { 24, 0xff0000, 0x00ff00, 0x0000ff, 0x000000, D3DFMT_R8G8B8 },
196     { 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000, D3DFMT_A8R8G8B8 },
197     { 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000, D3DFMT_X8R8G8B8 },
198     { 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000, D3DFMT_A2B10G10R10 },
199     { 32, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000, D3DFMT_A2R10G10B10 },
200     { 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000, D3DFMT_G16R16 },
201     { 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000, D3DFMT_A8B8G8R8 },
202     { 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000, D3DFMT_X8B8G8R8 },
203 };
204 
205 static D3DFORMAT dds_rgb_to_d3dformat(const struct dds_pixel_format *pixel_format)
206 {
207     unsigned int i;
208 
209     for (i = 0; i < ARRAY_SIZE(rgb_pixel_formats); i++)
210     {
211         if (rgb_pixel_formats[i].bpp == pixel_format->bpp
212             && rgb_pixel_formats[i].rmask == pixel_format->rmask
213             && rgb_pixel_formats[i].gmask == pixel_format->gmask
214             && rgb_pixel_formats[i].bmask == pixel_format->bmask)
215         {
216             if ((pixel_format->flags & DDS_PF_ALPHA) && rgb_pixel_formats[i].amask == pixel_format->amask)
217                 return rgb_pixel_formats[i].format;
218             if (rgb_pixel_formats[i].amask == 0)
219                 return rgb_pixel_formats[i].format;
220         }
221     }
222 
223     WARN("Unknown RGB pixel format (%#x, %#x, %#x, %#x)\n",
224         pixel_format->rmask, pixel_format->gmask, pixel_format->bmask, pixel_format->amask);
225     return D3DFMT_UNKNOWN;
226 }
227 
228 static D3DFORMAT dds_luminance_to_d3dformat(const struct dds_pixel_format *pixel_format)
229 {
230     if (pixel_format->bpp == 8)
231     {
232         if (pixel_format->rmask == 0xff)
233             return D3DFMT_L8;
234         if ((pixel_format->flags & DDS_PF_ALPHA) && pixel_format->rmask == 0x0f && pixel_format->amask == 0xf0)
235             return D3DFMT_A4L4;
236     }
237     if (pixel_format->bpp == 16)
238     {
239         if (pixel_format->rmask == 0xffff)
240             return D3DFMT_L16;
241         if ((pixel_format->flags & DDS_PF_ALPHA) && pixel_format->rmask == 0x00ff && pixel_format->amask == 0xff00)
242             return D3DFMT_A8L8;
243     }
244 
245     WARN("Unknown luminance pixel format (bpp %u, l %#x, a %#x)\n",
246         pixel_format->bpp, pixel_format->rmask, pixel_format->amask);
247     return D3DFMT_UNKNOWN;
248 }
249 
250 static D3DFORMAT dds_alpha_to_d3dformat(const struct dds_pixel_format *pixel_format)
251 {
252     if (pixel_format->bpp == 8 && pixel_format->amask == 0xff)
253         return D3DFMT_A8;
254 
255     WARN("Unknown Alpha pixel format (%u, %#x)\n", pixel_format->bpp, pixel_format->rmask);
256     return D3DFMT_UNKNOWN;
257 }
258 
259 static D3DFORMAT dds_bump_to_d3dformat(const struct dds_pixel_format *pixel_format)
260 {
261     if (pixel_format->bpp == 16 && pixel_format->rmask == 0x00ff && pixel_format->gmask == 0xff00)
262         return D3DFMT_V8U8;
263     if (pixel_format->bpp == 32 && pixel_format->rmask == 0x0000ffff && pixel_format->gmask == 0xffff0000)
264         return D3DFMT_V16U16;
265 
266     WARN("Unknown bump pixel format (%u, %#x, %#x, %#x, %#x)\n", pixel_format->bpp,
267         pixel_format->rmask, pixel_format->gmask, pixel_format->bmask, pixel_format->amask);
268     return D3DFMT_UNKNOWN;
269 }
270 
271 static D3DFORMAT dds_bump_luminance_to_d3dformat(const struct dds_pixel_format *pixel_format)
272 {
273     if (pixel_format->bpp == 32 && pixel_format->rmask == 0x000000ff && pixel_format->gmask == 0x0000ff00
274             && pixel_format->bmask == 0x00ff0000)
275         return D3DFMT_X8L8V8U8;
276 
277     WARN("Unknown bump pixel format (%u, %#x, %#x, %#x, %#x)\n", pixel_format->bpp,
278         pixel_format->rmask, pixel_format->gmask, pixel_format->bmask, pixel_format->amask);
279     return D3DFMT_UNKNOWN;
280 }
281 
282 static D3DFORMAT dds_pixel_format_to_d3dformat(const struct dds_pixel_format *pixel_format)
283 {
284     TRACE("pixel_format: size %u, flags %#x, fourcc %#x, bpp %u.\n", pixel_format->size,
285             pixel_format->flags, pixel_format->fourcc, pixel_format->bpp);
286     TRACE("rmask %#x, gmask %#x, bmask %#x, amask %#x.\n", pixel_format->rmask, pixel_format->gmask,
287             pixel_format->bmask, pixel_format->amask);
288 
289     if (pixel_format->flags & DDS_PF_FOURCC)
290         return dds_fourcc_to_d3dformat(pixel_format->fourcc);
291     if (pixel_format->flags & DDS_PF_RGB)
292         return dds_rgb_to_d3dformat(pixel_format);
293     if (pixel_format->flags & DDS_PF_LUMINANCE)
294         return dds_luminance_to_d3dformat(pixel_format);
295     if (pixel_format->flags & DDS_PF_ALPHA_ONLY)
296         return dds_alpha_to_d3dformat(pixel_format);
297     if (pixel_format->flags & DDS_PF_BUMPDUDV)
298         return dds_bump_to_d3dformat(pixel_format);
299     if (pixel_format->flags & DDS_PF_BUMPLUMINANCE)
300         return dds_bump_luminance_to_d3dformat(pixel_format);
301 
302     WARN("Unknown pixel format (flags %#x, fourcc %#x, bpp %u, r %#x, g %#x, b %#x, a %#x)\n",
303         pixel_format->flags, pixel_format->fourcc, pixel_format->bpp,
304         pixel_format->rmask, pixel_format->gmask, pixel_format->bmask, pixel_format->amask);
305     return D3DFMT_UNKNOWN;
306 }
307 
308 static HRESULT d3dformat_to_dds_pixel_format(struct dds_pixel_format *pixel_format, D3DFORMAT d3dformat)
309 {
310     unsigned int i;
311 
312     memset(pixel_format, 0, sizeof(*pixel_format));
313 
314     pixel_format->size = sizeof(*pixel_format);
315 
316     for (i = 0; i < ARRAY_SIZE(rgb_pixel_formats); i++)
317     {
318         if (rgb_pixel_formats[i].format == d3dformat)
319         {
320             pixel_format->flags |= DDS_PF_RGB;
321             pixel_format->bpp = rgb_pixel_formats[i].bpp;
322             pixel_format->rmask = rgb_pixel_formats[i].rmask;
323             pixel_format->gmask = rgb_pixel_formats[i].gmask;
324             pixel_format->bmask = rgb_pixel_formats[i].bmask;
325             pixel_format->amask = rgb_pixel_formats[i].amask;
326             if (pixel_format->amask) pixel_format->flags |= DDS_PF_ALPHA;
327             return D3D_OK;
328         }
329     }
330 
331     /* Reuse dds_fourcc_to_d3dformat as D3DFORMAT and FOURCC are DWORD with same values */
332     if (dds_fourcc_to_d3dformat(d3dformat) != D3DFMT_UNKNOWN)
333     {
334         pixel_format->flags |= DDS_PF_FOURCC;
335         pixel_format->fourcc = d3dformat;
336         return D3D_OK;
337     }
338 
339     WARN("Unknown pixel format %#x\n", d3dformat);
340     return E_NOTIMPL;
341 }
342 
343 static HRESULT calculate_dds_surface_size(D3DFORMAT format, UINT width, UINT height,
344     UINT *pitch, UINT *size)
345 {
346     const struct pixel_format_desc *format_desc = get_format_info(format);
347     if (format_desc->type == FORMAT_UNKNOWN)
348         return E_NOTIMPL;
349 
350     if (format_desc->block_width != 1 || format_desc->block_height != 1)
351     {
352         *pitch = format_desc->block_byte_count
353             * max(1, (width + format_desc->block_width - 1) / format_desc->block_width);
354         *size = *pitch
355             * max(1, (height + format_desc->block_height - 1) / format_desc->block_height);
356     }
357     else
358     {
359         *pitch = width * format_desc->bytes_per_pixel;
360         *size = *pitch * height;
361     }
362 
363     return D3D_OK;
364 }
365 
366 static UINT calculate_dds_file_size(D3DFORMAT format, UINT width, UINT height, UINT depth,
367     UINT miplevels, UINT faces)
368 {
369     UINT i, file_size = 0;
370 
371     for (i = 0; i < miplevels; i++)
372     {
373         UINT pitch, size = 0;
374         calculate_dds_surface_size(format, width, height, &pitch, &size);
375         size *= depth;
376         file_size += size;
377         width = max(1, width / 2);
378         height = max(1, height / 2);
379         depth = max(1, depth / 2);
380     }
381 
382     file_size *= faces;
383     file_size += sizeof(struct dds_header);
384     return file_size;
385 }
386 
387 /************************************************************
388 * get_image_info_from_dds
389 *
390 * Fills a D3DXIMAGE_INFO structure with information
391 * about a DDS file stored in the memory.
392 *
393 * PARAMS
394 *   buffer  [I] pointer to DDS data
395 *   length  [I] size of DDS data
396 *   info    [O] pointer to D3DXIMAGE_INFO structure
397 *
398 * RETURNS
399 *   Success: D3D_OK
400 *   Failure: D3DXERR_INVALIDDATA
401 *
402 */
403 static HRESULT get_image_info_from_dds(const void *buffer, UINT length, D3DXIMAGE_INFO *info)
404 {
405     UINT faces = 1;
406     UINT expected_length;
407     const struct dds_header *header = buffer;
408 
409     if (length < sizeof(*header) || !info)
410         return D3DXERR_INVALIDDATA;
411 
412     if (header->pixel_format.size != sizeof(header->pixel_format))
413         return D3DXERR_INVALIDDATA;
414 
415     info->Width = header->width;
416     info->Height = header->height;
417     info->Depth = 1;
418     info->MipLevels = header->miplevels ? header->miplevels : 1;
419 
420     info->Format = dds_pixel_format_to_d3dformat(&header->pixel_format);
421     if (info->Format == D3DFMT_UNKNOWN)
422         return D3DXERR_INVALIDDATA;
423 
424     TRACE("Pixel format is %#x\n", info->Format);
425 
426     if (header->caps2 & DDS_CAPS2_VOLUME)
427     {
428         info->Depth = header->depth;
429         info->ResourceType = D3DRTYPE_VOLUMETEXTURE;
430     }
431     else if (header->caps2 & DDS_CAPS2_CUBEMAP)
432     {
433         DWORD face;
434         faces = 0;
435         for (face = DDS_CAPS2_CUBEMAP_POSITIVEX; face <= DDS_CAPS2_CUBEMAP_NEGATIVEZ; face <<= 1)
436         {
437             if (header->caps2 & face)
438                 faces++;
439         }
440         info->ResourceType = D3DRTYPE_CUBETEXTURE;
441     }
442     else
443     {
444         info->ResourceType = D3DRTYPE_TEXTURE;
445     }
446 
447     expected_length = calculate_dds_file_size(info->Format, info->Width, info->Height, info->Depth,
448         info->MipLevels, faces);
449     if (length < expected_length)
450     {
451         WARN("File is too short %u, expected at least %u bytes\n", length, expected_length);
452         return D3DXERR_INVALIDDATA;
453     }
454 
455     info->ImageFileFormat = D3DXIFF_DDS;
456     return D3D_OK;
457 }
458 
459 static HRESULT load_surface_from_dds(IDirect3DSurface9 *dst_surface, const PALETTEENTRY *dst_palette,
460     const RECT *dst_rect, const void *src_data, const RECT *src_rect, DWORD filter, D3DCOLOR color_key,
461     const D3DXIMAGE_INFO *src_info)
462 {
463     UINT size;
464     UINT src_pitch;
465     const struct dds_header *header = src_data;
466     const BYTE *pixels = (BYTE *)(header + 1);
467 
468     if (src_info->ResourceType != D3DRTYPE_TEXTURE)
469         return D3DXERR_INVALIDDATA;
470 
471     if (FAILED(calculate_dds_surface_size(src_info->Format, src_info->Width, src_info->Height, &src_pitch, &size)))
472         return E_NOTIMPL;
473 
474     return D3DXLoadSurfaceFromMemory(dst_surface, dst_palette, dst_rect, pixels, src_info->Format,
475         src_pitch, NULL, src_rect, filter, color_key);
476 }
477 
478 static HRESULT save_dds_surface_to_memory(ID3DXBuffer **dst_buffer, IDirect3DSurface9 *src_surface, const RECT *src_rect)
479 {
480     HRESULT hr;
481     UINT dst_pitch, surface_size, file_size;
482     D3DSURFACE_DESC src_desc;
483     D3DLOCKED_RECT locked_rect;
484     ID3DXBuffer *buffer;
485     struct dds_header *header;
486     BYTE *pixels;
487     struct volume volume;
488     const struct pixel_format_desc *pixel_format;
489 
490     if (src_rect)
491     {
492         FIXME("Saving a part of a surface to a DDS file is not implemented yet\n");
493         return E_NOTIMPL;
494     }
495 
496     hr = IDirect3DSurface9_GetDesc(src_surface, &src_desc);
497     if (FAILED(hr)) return hr;
498 
499     pixel_format = get_format_info(src_desc.Format);
500     if (pixel_format->type == FORMAT_UNKNOWN) return E_NOTIMPL;
501 
502     file_size = calculate_dds_file_size(src_desc.Format, src_desc.Width, src_desc.Height, 1, 1, 1);
503 
504     hr = calculate_dds_surface_size(src_desc.Format, src_desc.Width, src_desc.Height, &dst_pitch, &surface_size);
505     if (FAILED(hr)) return hr;
506 
507     hr = D3DXCreateBuffer(file_size, &buffer);
508     if (FAILED(hr)) return hr;
509 
510     header = ID3DXBuffer_GetBufferPointer(buffer);
511     pixels = (BYTE *)(header + 1);
512 
513     memset(header, 0, sizeof(*header));
514     header->signature = MAKEFOURCC('D','D','S',' ');
515     /* The signature is not really part of the DDS header */
516     header->size = sizeof(*header) - FIELD_OFFSET(struct dds_header, size);
517     header->flags = DDS_CAPS | DDS_HEIGHT | DDS_WIDTH | DDS_PIXELFORMAT;
518     header->height = src_desc.Height;
519     header->width = src_desc.Width;
520     header->caps = DDS_CAPS_TEXTURE;
521     hr = d3dformat_to_dds_pixel_format(&header->pixel_format, src_desc.Format);
522     if (FAILED(hr))
523     {
524         ID3DXBuffer_Release(buffer);
525         return hr;
526     }
527 
528     hr = IDirect3DSurface9_LockRect(src_surface, &locked_rect, NULL, D3DLOCK_READONLY);
529     if (FAILED(hr))
530     {
531         ID3DXBuffer_Release(buffer);
532         return hr;
533     }
534 
535     volume.width = src_desc.Width;
536     volume.height = src_desc.Height;
537     volume.depth = 1;
538     copy_pixels(locked_rect.pBits, locked_rect.Pitch, 0, pixels, dst_pitch, 0,
539         &volume, pixel_format);
540 
541     IDirect3DSurface9_UnlockRect(src_surface);
542 
543     *dst_buffer = buffer;
544     return D3D_OK;
545 }
546 
547 static HRESULT get_surface(D3DRESOURCETYPE type, struct IDirect3DBaseTexture9 *tex,
548         int face, UINT level, struct IDirect3DSurface9 **surf)
549 {
550     switch (type)
551     {
552         case D3DRTYPE_TEXTURE:
553             return IDirect3DTexture9_GetSurfaceLevel((IDirect3DTexture9*) tex, level, surf);
554         case D3DRTYPE_CUBETEXTURE:
555             return IDirect3DCubeTexture9_GetCubeMapSurface((IDirect3DCubeTexture9*) tex, face, level, surf);
556         default:
557             ERR("Unexpected texture type\n");
558             return E_NOTIMPL;
559     }
560 }
561 
562 HRESULT save_dds_texture_to_memory(ID3DXBuffer **dst_buffer, IDirect3DBaseTexture9 *src_texture, const PALETTEENTRY *src_palette)
563 {
564     HRESULT hr;
565     D3DRESOURCETYPE type;
566     UINT mip_levels;
567     IDirect3DSurface9 *surface;
568 
569     type = IDirect3DBaseTexture9_GetType(src_texture);
570 
571     if ((type !=  D3DRTYPE_TEXTURE) && (type != D3DRTYPE_CUBETEXTURE) && (type != D3DRTYPE_VOLUMETEXTURE))
572         return D3DERR_INVALIDCALL;
573 
574     if (type == D3DRTYPE_CUBETEXTURE)
575     {
576         FIXME("Cube texture not supported yet\n");
577         return E_NOTIMPL;
578     }
579     else if (type == D3DRTYPE_VOLUMETEXTURE)
580     {
581         FIXME("Volume texture not supported yet\n");
582         return E_NOTIMPL;
583     }
584 
585     mip_levels = IDirect3DTexture9_GetLevelCount(src_texture);
586 
587     if (mip_levels > 1)
588     {
589         FIXME("Mipmap not supported yet\n");
590         return E_NOTIMPL;
591     }
592 
593     if (src_palette)
594     {
595         FIXME("Saving surfaces with palettized pixel formats not implemented yet\n");
596         return E_NOTIMPL;
597     }
598 
599     hr = get_surface(type, src_texture, D3DCUBEMAP_FACE_POSITIVE_X, 0, &surface);
600 
601     if (SUCCEEDED(hr))
602     {
603         hr = save_dds_surface_to_memory(dst_buffer, surface, NULL);
604         IDirect3DSurface9_Release(surface);
605     }
606 
607     return hr;
608 }
609 HRESULT load_volume_from_dds(IDirect3DVolume9 *dst_volume, const PALETTEENTRY *dst_palette,
610     const D3DBOX *dst_box, const void *src_data, const D3DBOX *src_box, DWORD filter, D3DCOLOR color_key,
611     const D3DXIMAGE_INFO *src_info)
612 {
613     UINT row_pitch, slice_pitch;
614     const struct dds_header *header = src_data;
615     const BYTE *pixels = (BYTE *)(header + 1);
616 
617     if (src_info->ResourceType != D3DRTYPE_VOLUMETEXTURE)
618         return D3DXERR_INVALIDDATA;
619 
620     if (FAILED(calculate_dds_surface_size(src_info->Format, src_info->Width, src_info->Height, &row_pitch, &slice_pitch)))
621         return E_NOTIMPL;
622 
623     return D3DXLoadVolumeFromMemory(dst_volume, dst_palette, dst_box, pixels, src_info->Format,
624         row_pitch, slice_pitch, NULL, src_box, filter, color_key);
625 }
626 
627 HRESULT load_texture_from_dds(IDirect3DTexture9 *texture, const void *src_data, const PALETTEENTRY *palette,
628         DWORD filter, D3DCOLOR color_key, const D3DXIMAGE_INFO *src_info, unsigned int skip_levels,
629         unsigned int *loaded_miplevels)
630 {
631     HRESULT hr;
632     RECT src_rect;
633     UINT src_pitch;
634     UINT mip_level;
635     UINT mip_levels;
636     UINT mip_level_size;
637     UINT width, height;
638     IDirect3DSurface9 *surface;
639     const struct dds_header *header = src_data;
640     const BYTE *pixels = (BYTE *)(header + 1);
641 
642     /* Loading a cube texture as a simple texture is also supported
643      * (only first face texture is taken). Same with volume textures. */
644     if ((src_info->ResourceType != D3DRTYPE_TEXTURE)
645             && (src_info->ResourceType != D3DRTYPE_CUBETEXTURE)
646             && (src_info->ResourceType != D3DRTYPE_VOLUMETEXTURE))
647     {
648         WARN("Trying to load a %u resource as a 2D texture, returning failure.\n", src_info->ResourceType);
649         return D3DXERR_INVALIDDATA;
650     }
651 
652     width = src_info->Width;
653     height = src_info->Height;
654     mip_levels = min(src_info->MipLevels, IDirect3DTexture9_GetLevelCount(texture));
655     if (src_info->ResourceType == D3DRTYPE_VOLUMETEXTURE)
656         mip_levels = 1;
657     for (mip_level = 0; mip_level < mip_levels + skip_levels; ++mip_level)
658     {
659         hr = calculate_dds_surface_size(src_info->Format, width, height, &src_pitch, &mip_level_size);
660         if (FAILED(hr)) return hr;
661 
662         if (mip_level >= skip_levels)
663         {
664             SetRect(&src_rect, 0, 0, width, height);
665 
666             IDirect3DTexture9_GetSurfaceLevel(texture, mip_level - skip_levels, &surface);
667             hr = D3DXLoadSurfaceFromMemory(surface, palette, NULL, pixels, src_info->Format, src_pitch,
668                     NULL, &src_rect, filter, color_key);
669             IDirect3DSurface9_Release(surface);
670             if (FAILED(hr))
671                 return hr;
672         }
673 
674         pixels += mip_level_size;
675         width = max(1, width / 2);
676         height = max(1, height / 2);
677     }
678 
679     *loaded_miplevels = mip_levels - skip_levels;
680 
681     return D3D_OK;
682 }
683 
684 HRESULT load_cube_texture_from_dds(IDirect3DCubeTexture9 *cube_texture, const void *src_data,
685     const PALETTEENTRY *palette, DWORD filter, DWORD color_key, const D3DXIMAGE_INFO *src_info)
686 {
687     HRESULT hr;
688     int face;
689     UINT mip_level;
690     UINT size;
691     RECT src_rect;
692     UINT src_pitch;
693     UINT mip_levels;
694     UINT mip_level_size;
695     IDirect3DSurface9 *surface;
696     const struct dds_header *header = src_data;
697     const BYTE *pixels = (BYTE *)(header + 1);
698 
699     if (src_info->ResourceType != D3DRTYPE_CUBETEXTURE)
700         return D3DXERR_INVALIDDATA;
701 
702     if ((header->caps2 & DDS_CAPS2_CUBEMAP_ALL_FACES) != DDS_CAPS2_CUBEMAP_ALL_FACES)
703     {
704         WARN("Only full cubemaps are supported\n");
705         return D3DXERR_INVALIDDATA;
706     }
707 
708     mip_levels = min(src_info->MipLevels, IDirect3DCubeTexture9_GetLevelCount(cube_texture));
709     for (face = D3DCUBEMAP_FACE_POSITIVE_X; face <= D3DCUBEMAP_FACE_NEGATIVE_Z; face++)
710     {
711         size = src_info->Width;
712         for (mip_level = 0; mip_level < src_info->MipLevels; mip_level++)
713         {
714             hr = calculate_dds_surface_size(src_info->Format, size, size, &src_pitch, &mip_level_size);
715             if (FAILED(hr)) return hr;
716 
717             /* if texture has fewer mip levels than DDS file, skip excessive mip levels */
718             if (mip_level < mip_levels)
719             {
720                 SetRect(&src_rect, 0, 0, size, size);
721 
722                 IDirect3DCubeTexture9_GetCubeMapSurface(cube_texture, face, mip_level, &surface);
723                 hr = D3DXLoadSurfaceFromMemory(surface, palette, NULL, pixels, src_info->Format, src_pitch,
724                     NULL, &src_rect, filter, color_key);
725                 IDirect3DSurface9_Release(surface);
726                 if (FAILED(hr)) return hr;
727             }
728 
729             pixels += mip_level_size;
730             size = max(1, size / 2);
731         }
732     }
733 
734     return D3D_OK;
735 }
736 
737 HRESULT load_volume_texture_from_dds(IDirect3DVolumeTexture9 *volume_texture, const void *src_data,
738     const PALETTEENTRY *palette, DWORD filter, DWORD color_key, const D3DXIMAGE_INFO *src_info)
739 {
740     HRESULT hr;
741     UINT mip_level;
742     UINT mip_levels;
743     UINT src_slice_pitch;
744     UINT src_row_pitch;
745     D3DBOX src_box;
746     UINT width, height, depth;
747     IDirect3DVolume9 *volume;
748     const struct dds_header *header = src_data;
749     const BYTE *pixels = (BYTE *)(header + 1);
750 
751     if (src_info->ResourceType != D3DRTYPE_VOLUMETEXTURE)
752         return D3DXERR_INVALIDDATA;
753 
754     width = src_info->Width;
755     height = src_info->Height;
756     depth = src_info->Depth;
757     mip_levels = min(src_info->MipLevels, IDirect3DVolumeTexture9_GetLevelCount(volume_texture));
758 
759     for (mip_level = 0; mip_level < mip_levels; mip_level++)
760     {
761         hr = calculate_dds_surface_size(src_info->Format, width, height, &src_row_pitch, &src_slice_pitch);
762         if (FAILED(hr)) return hr;
763 
764         hr = IDirect3DVolumeTexture9_GetVolumeLevel(volume_texture, mip_level, &volume);
765         if (FAILED(hr)) return hr;
766 
767         src_box.Left = 0;
768         src_box.Top = 0;
769         src_box.Right = width;
770         src_box.Bottom = height;
771         src_box.Front = 0;
772         src_box.Back = depth;
773 
774         hr = D3DXLoadVolumeFromMemory(volume, palette, NULL, pixels, src_info->Format,
775             src_row_pitch, src_slice_pitch, NULL, &src_box, filter, color_key);
776 
777         IDirect3DVolume9_Release(volume);
778         if (FAILED(hr)) return hr;
779 
780         pixels += depth * src_slice_pitch;
781         width = max(1, width / 2);
782         height = max(1, height / 2);
783         depth = max(1, depth / 2);
784     }
785 
786     return D3D_OK;
787 }
788 
789 static BOOL convert_dib_to_bmp(void **data, UINT *size)
790 {
791     ULONG header_size;
792     ULONG count = 0;
793     ULONG offset;
794     BITMAPFILEHEADER *header;
795     BYTE *new_data;
796     UINT new_size;
797 
798     if ((*size < 4) || (*size < (header_size = *(ULONG*)*data)))
799         return FALSE;
800 
801     if ((header_size == sizeof(BITMAPINFOHEADER)) ||
802         (header_size == sizeof(BITMAPV4HEADER)) ||
803         (header_size == sizeof(BITMAPV5HEADER)) ||
804         (header_size == 64 /* sizeof(BITMAPCOREHEADER2) */))
805     {
806         /* All structures begin with the same memory layout as BITMAPINFOHEADER */
807         BITMAPINFOHEADER *info_header = (BITMAPINFOHEADER*)*data;
808         count = info_header->biClrUsed;
809 
810         if (!count && info_header->biBitCount <= 8)
811             count = 1 << info_header->biBitCount;
812 
813         offset = sizeof(BITMAPFILEHEADER) + header_size + sizeof(RGBQUAD) * count;
814 
815         /* For BITMAPINFOHEADER with BI_BITFIELDS compression, there are 3 additional color masks after header */
816         if ((info_header->biSize == sizeof(BITMAPINFOHEADER)) && (info_header->biCompression == BI_BITFIELDS))
817             offset += 3 * sizeof(DWORD);
818     }
819     else if (header_size == sizeof(BITMAPCOREHEADER))
820     {
821         BITMAPCOREHEADER *core_header = (BITMAPCOREHEADER*)*data;
822 
823         if (core_header->bcBitCount <= 8)
824             count = 1 << core_header->bcBitCount;
825 
826         offset = sizeof(BITMAPFILEHEADER) + header_size + sizeof(RGBTRIPLE) * count;
827     }
828     else
829     {
830         return FALSE;
831     }
832 
833     TRACE("Converting DIB file to BMP\n");
834 
835     new_size = *size + sizeof(BITMAPFILEHEADER);
836     new_data = HeapAlloc(GetProcessHeap(), 0, new_size);
837     CopyMemory(new_data + sizeof(BITMAPFILEHEADER), *data, *size);
838 
839     /* Add BMP header */
840     header = (BITMAPFILEHEADER*)new_data;
841     header->bfType = 0x4d42; /* BM */
842     header->bfSize = new_size;
843     header->bfReserved1 = 0;
844     header->bfReserved2 = 0;
845     header->bfOffBits = offset;
846 
847     /* Update input data */
848     *data = new_data;
849     *size = new_size;
850 
851     return TRUE;
852 }
853 
854 /************************************************************
855  * D3DXGetImageInfoFromFileInMemory
856  *
857  * Fills a D3DXIMAGE_INFO structure with info about an image
858  *
859  * PARAMS
860  *   data     [I] pointer to the image file data
861  *   datasize [I] size of the passed data
862  *   info     [O] pointer to the destination structure
863  *
864  * RETURNS
865  *   Success: D3D_OK, if info is not NULL and data and datasize make up a valid image file or
866  *                    if info is NULL and data and datasize are not NULL
867  *   Failure: D3DXERR_INVALIDDATA, if data is no valid image file and datasize and info are not NULL
868  *            D3DERR_INVALIDCALL, if data is NULL or
869  *                                if datasize is 0
870  *
871  * NOTES
872  *   datasize may be bigger than the actual file size
873  *
874  */
875 HRESULT WINAPI D3DXGetImageInfoFromFileInMemory(const void *data, UINT datasize, D3DXIMAGE_INFO *info)
876 {
877     IWICImagingFactory *factory;
878     IWICBitmapDecoder *decoder = NULL;
879     IWICStream *stream;
880     HRESULT hr;
881     HRESULT initresult;
882     BOOL dib;
883 
884     TRACE("(%p, %d, %p)\n", data, datasize, info);
885 
886     if (!data || !datasize)
887         return D3DERR_INVALIDCALL;
888 
889     if (!info)
890         return D3D_OK;
891 
892     if ((datasize >= 4) && !strncmp(data, "DDS ", 4)) {
893         TRACE("File type is DDS\n");
894         return get_image_info_from_dds(data, datasize, info);
895     }
896 
897     /* In case of DIB file, convert it to BMP */
898     dib = convert_dib_to_bmp((void**)&data, &datasize);
899 
900     initresult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
901 
902     hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, (void**)&factory);
903 
904     if (SUCCEEDED(hr)) {
905         IWICImagingFactory_CreateStream(factory, &stream);
906         IWICStream_InitializeFromMemory(stream, (BYTE*)data, datasize);
907         hr = IWICImagingFactory_CreateDecoderFromStream(factory, (IStream*)stream, NULL, 0, &decoder);
908         IWICStream_Release(stream);
909         IWICImagingFactory_Release(factory);
910     }
911 
912     if (FAILED(hr)) {
913         if ((datasize >= 2) && (!strncmp(data, "P3", 2) || !strncmp(data, "P6", 2)))
914             FIXME("File type PPM is not supported yet\n");
915         else if ((datasize >= 10) && !strncmp(data, "#?RADIANCE", 10))
916             FIXME("File type HDR is not supported yet\n");
917         else if ((datasize >= 2) && (!strncmp(data, "PF", 2) || !strncmp(data, "Pf", 2)))
918             FIXME("File type PFM is not supported yet\n");
919     }
920 
921     if (SUCCEEDED(hr)) {
922         GUID container_format;
923         UINT frame_count;
924 
925         hr = IWICBitmapDecoder_GetContainerFormat(decoder, &container_format);
926         if (SUCCEEDED(hr)) {
927             if (IsEqualGUID(&container_format, &GUID_ContainerFormatBmp)) {
928                 if (dib) {
929                     TRACE("File type is DIB\n");
930                     info->ImageFileFormat = D3DXIFF_DIB;
931                 } else {
932                     TRACE("File type is BMP\n");
933                     info->ImageFileFormat = D3DXIFF_BMP;
934                 }
935             } else if (IsEqualGUID(&container_format, &GUID_ContainerFormatPng)) {
936                 TRACE("File type is PNG\n");
937                 info->ImageFileFormat = D3DXIFF_PNG;
938             } else if(IsEqualGUID(&container_format, &GUID_ContainerFormatJpeg)) {
939                 TRACE("File type is JPG\n");
940                 info->ImageFileFormat = D3DXIFF_JPG;
941             } else if(IsEqualGUID(&container_format, &GUID_WineContainerFormatTga)) {
942                 TRACE("File type is TGA\n");
943                 info->ImageFileFormat = D3DXIFF_TGA;
944             } else {
945                 WARN("Unsupported image file format %s\n", debugstr_guid(&container_format));
946                 hr = D3DXERR_INVALIDDATA;
947             }
948         }
949 
950         if (SUCCEEDED(hr))
951             hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
952         if (SUCCEEDED(hr) && !frame_count)
953             hr = D3DXERR_INVALIDDATA;
954 
955         if (SUCCEEDED(hr)) {
956             IWICBitmapFrameDecode *frame = NULL;
957 
958             hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
959 
960             if (SUCCEEDED(hr))
961                 hr = IWICBitmapFrameDecode_GetSize(frame, &info->Width, &info->Height);
962 
963             if (SUCCEEDED(hr)) {
964                 WICPixelFormatGUID pixel_format;
965 
966                 hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &pixel_format);
967                 if (SUCCEEDED(hr)) {
968                     info->Format = wic_guid_to_d3dformat(&pixel_format);
969                     if (info->Format == D3DFMT_UNKNOWN) {
970                         WARN("Unsupported pixel format %s\n", debugstr_guid(&pixel_format));
971                         hr = D3DXERR_INVALIDDATA;
972                     }
973                 }
974             }
975 
976             /* For 32 bpp BMP, windowscodecs.dll never returns a format with alpha while
977              * d3dx9_xx.dll returns one if at least 1 pixel has a non zero alpha component */
978             if (SUCCEEDED(hr) && (info->Format == D3DFMT_X8R8G8B8) && (info->ImageFileFormat == D3DXIFF_BMP)) {
979                 DWORD size = sizeof(DWORD) * info->Width * info->Height;
980                 BYTE *buffer = HeapAlloc(GetProcessHeap(), 0, size);
981                 hr = IWICBitmapFrameDecode_CopyPixels(frame, NULL, sizeof(DWORD) * info->Width, size, buffer);
982                 if (SUCCEEDED(hr)) {
983                     DWORD i;
984                     for (i = 0; i < info->Width * info->Height; i++) {
985                         if (buffer[i*4+3]) {
986                             info->Format = D3DFMT_A8R8G8B8;
987                             break;
988                         }
989                     }
990                 }
991                 HeapFree(GetProcessHeap(), 0, buffer);
992             }
993 
994             if (frame)
995                  IWICBitmapFrameDecode_Release(frame);
996 
997             info->Depth = 1;
998             info->MipLevels = 1;
999             info->ResourceType = D3DRTYPE_TEXTURE;
1000         }
1001     }
1002 
1003     if (decoder)
1004         IWICBitmapDecoder_Release(decoder);
1005 
1006     if (SUCCEEDED(initresult))
1007         CoUninitialize();
1008 
1009     if (dib)
1010         HeapFree(GetProcessHeap(), 0, (void*)data);
1011 
1012     if (FAILED(hr)) {
1013         TRACE("Invalid or unsupported image file\n");
1014         return D3DXERR_INVALIDDATA;
1015     }
1016 
1017     return D3D_OK;
1018 }
1019 
1020 /************************************************************
1021  * D3DXGetImageInfoFromFile
1022  *
1023  * RETURNS
1024  *   Success: D3D_OK, if we successfully load a valid image file or
1025  *                    if we successfully load a file which is no valid image and info is NULL
1026  *   Failure: D3DXERR_INVALIDDATA, if we fail to load file or
1027  *                                 if file is not a valid image file and info is not NULL
1028  *            D3DERR_INVALIDCALL, if file is NULL
1029  *
1030  */
1031 HRESULT WINAPI D3DXGetImageInfoFromFileA(const char *file, D3DXIMAGE_INFO *info)
1032 {
1033     WCHAR *widename;
1034     HRESULT hr;
1035     int strlength;
1036 
1037     TRACE("file %s, info %p.\n", debugstr_a(file), info);
1038 
1039     if( !file ) return D3DERR_INVALIDCALL;
1040 
1041     strlength = MultiByteToWideChar(CP_ACP, 0, file, -1, NULL, 0);
1042     widename = HeapAlloc(GetProcessHeap(), 0, strlength * sizeof(*widename));
1043     MultiByteToWideChar(CP_ACP, 0, file, -1, widename, strlength);
1044 
1045     hr = D3DXGetImageInfoFromFileW(widename, info);
1046     HeapFree(GetProcessHeap(), 0, widename);
1047 
1048     return hr;
1049 }
1050 
1051 HRESULT WINAPI D3DXGetImageInfoFromFileW(const WCHAR *file, D3DXIMAGE_INFO *info)
1052 {
1053     void *buffer;
1054     HRESULT hr;
1055     DWORD size;
1056 
1057     TRACE("file %s, info %p.\n", debugstr_w(file), info);
1058 
1059     if (!file)
1060         return D3DERR_INVALIDCALL;
1061 
1062     if (FAILED(map_view_of_file(file, &buffer, &size)))
1063         return D3DXERR_INVALIDDATA;
1064 
1065     hr = D3DXGetImageInfoFromFileInMemory(buffer, size, info);
1066     UnmapViewOfFile(buffer);
1067 
1068     return hr;
1069 }
1070 
1071 /************************************************************
1072  * D3DXGetImageInfoFromResource
1073  *
1074  * RETURNS
1075  *   Success: D3D_OK, if resource is a valid image file
1076  *   Failure: D3DXERR_INVALIDDATA, if resource is no valid image file or NULL or
1077  *                                 if we fail to load resource
1078  *
1079  */
1080 HRESULT WINAPI D3DXGetImageInfoFromResourceA(HMODULE module, const char *resource, D3DXIMAGE_INFO *info)
1081 {
1082     HRSRC resinfo;
1083     void *buffer;
1084     DWORD size;
1085 
1086     TRACE("module %p, resource %s, info %p.\n", module, debugstr_a(resource), info);
1087 
1088     if (!(resinfo = FindResourceA(module, resource, (const char *)RT_RCDATA))
1089             /* Try loading the resource as bitmap data (which is in DIB format D3DXIFF_DIB) */
1090             && !(resinfo = FindResourceA(module, resource, (const char *)RT_BITMAP)))
1091         return D3DXERR_INVALIDDATA;
1092 
1093     if (FAILED(load_resource_into_memory(module, resinfo, &buffer, &size)))
1094         return D3DXERR_INVALIDDATA;
1095 
1096     return D3DXGetImageInfoFromFileInMemory(buffer, size, info);
1097 }
1098 
1099 HRESULT WINAPI D3DXGetImageInfoFromResourceW(HMODULE module, const WCHAR *resource, D3DXIMAGE_INFO *info)
1100 {
1101     HRSRC resinfo;
1102     void *buffer;
1103     DWORD size;
1104 
1105     TRACE("module %p, resource %s, info %p.\n", module, debugstr_w(resource), info);
1106 
1107     if (!(resinfo = FindResourceW(module, resource, (const WCHAR *)RT_RCDATA))
1108             /* Try loading the resource as bitmap data (which is in DIB format D3DXIFF_DIB) */
1109             && !(resinfo = FindResourceW(module, resource, (const WCHAR *)RT_BITMAP)))
1110         return D3DXERR_INVALIDDATA;
1111 
1112     if (FAILED(load_resource_into_memory(module, resinfo, &buffer, &size)))
1113         return D3DXERR_INVALIDDATA;
1114 
1115     return D3DXGetImageInfoFromFileInMemory(buffer, size, info);
1116 }
1117 
1118 /************************************************************
1119  * D3DXLoadSurfaceFromFileInMemory
1120  *
1121  * Loads data from a given buffer into a surface and fills a given
1122  * D3DXIMAGE_INFO structure with info about the source data.
1123  *
1124  * PARAMS
1125  *   pDestSurface [I] pointer to the surface
1126  *   pDestPalette [I] palette to use
1127  *   pDestRect    [I] to be filled area of the surface
1128  *   pSrcData     [I] pointer to the source data
1129  *   SrcDataSize  [I] size of the source data in bytes
1130  *   pSrcRect     [I] area of the source data to load
1131  *   dwFilter     [I] filter to apply on stretching
1132  *   Colorkey     [I] colorkey
1133  *   pSrcInfo     [O] pointer to a D3DXIMAGE_INFO structure
1134  *
1135  * RETURNS
1136  *   Success: D3D_OK
1137  *   Failure: D3DERR_INVALIDCALL, if pDestSurface, pSrcData or SrcDataSize is NULL
1138  *            D3DXERR_INVALIDDATA, if pSrcData is no valid image file
1139  *
1140  */
1141 HRESULT WINAPI D3DXLoadSurfaceFromFileInMemory(IDirect3DSurface9 *pDestSurface,
1142         const PALETTEENTRY *pDestPalette, const RECT *pDestRect, const void *pSrcData, UINT SrcDataSize,
1143         const RECT *pSrcRect, DWORD dwFilter, D3DCOLOR Colorkey, D3DXIMAGE_INFO *pSrcInfo)
1144 {
1145     D3DXIMAGE_INFO imginfo;
1146     HRESULT hr, com_init;
1147 
1148     IWICImagingFactory *factory = NULL;
1149     IWICBitmapDecoder *decoder;
1150     IWICBitmapFrameDecode *bitmapframe;
1151     IWICStream *stream;
1152 
1153     const struct pixel_format_desc *formatdesc;
1154     WICRect wicrect;
1155     RECT rect;
1156 
1157     TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_data %p, src_data_size %u, "
1158             "src_rect %s, filter %#x, color_key 0x%08x, src_info %p.\n",
1159             pDestSurface, pDestPalette, wine_dbgstr_rect(pDestRect), pSrcData, SrcDataSize,
1160             wine_dbgstr_rect(pSrcRect), dwFilter, Colorkey, pSrcInfo);
1161 
1162     if (!pDestSurface || !pSrcData || !SrcDataSize)
1163         return D3DERR_INVALIDCALL;
1164 
1165     hr = D3DXGetImageInfoFromFileInMemory(pSrcData, SrcDataSize, &imginfo);
1166 
1167     if (FAILED(hr))
1168         return hr;
1169 
1170     if (pSrcRect)
1171     {
1172         wicrect.X = pSrcRect->left;
1173         wicrect.Y = pSrcRect->top;
1174         wicrect.Width = pSrcRect->right - pSrcRect->left;
1175         wicrect.Height = pSrcRect->bottom - pSrcRect->top;
1176     }
1177     else
1178     {
1179         wicrect.X = 0;
1180         wicrect.Y = 0;
1181         wicrect.Width = imginfo.Width;
1182         wicrect.Height = imginfo.Height;
1183     }
1184 
1185     SetRect(&rect, 0, 0, wicrect.Width, wicrect.Height);
1186 
1187     if (imginfo.ImageFileFormat == D3DXIFF_DDS)
1188     {
1189         hr = load_surface_from_dds(pDestSurface, pDestPalette, pDestRect, pSrcData, &rect,
1190             dwFilter, Colorkey, &imginfo);
1191         if (SUCCEEDED(hr) && pSrcInfo)
1192             *pSrcInfo = imginfo;
1193         return hr;
1194     }
1195 
1196     if (imginfo.ImageFileFormat == D3DXIFF_DIB)
1197         convert_dib_to_bmp((void**)&pSrcData, &SrcDataSize);
1198 
1199     com_init = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
1200 
1201     if (FAILED(CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, (void**)&factory)))
1202         goto cleanup_err;
1203 
1204     if (FAILED(IWICImagingFactory_CreateStream(factory, &stream)))
1205     {
1206         IWICImagingFactory_Release(factory);
1207         factory = NULL;
1208         goto cleanup_err;
1209     }
1210 
1211     IWICStream_InitializeFromMemory(stream, (BYTE*)pSrcData, SrcDataSize);
1212 
1213     hr = IWICImagingFactory_CreateDecoderFromStream(factory, (IStream*)stream, NULL, 0, &decoder);
1214 
1215     IWICStream_Release(stream);
1216 
1217     if (FAILED(hr))
1218         goto cleanup_err;
1219 
1220     hr = IWICBitmapDecoder_GetFrame(decoder, 0, &bitmapframe);
1221 
1222     if (FAILED(hr))
1223         goto cleanup_bmp;
1224 
1225     formatdesc = get_format_info(imginfo.Format);
1226 
1227     if (formatdesc->type == FORMAT_UNKNOWN)
1228     {
1229         FIXME("Unsupported pixel format\n");
1230         hr = D3DXERR_INVALIDDATA;
1231     }
1232     else
1233     {
1234         BYTE *buffer;
1235         DWORD pitch;
1236         PALETTEENTRY *palette = NULL;
1237         WICColor *colors = NULL;
1238 
1239         pitch = formatdesc->bytes_per_pixel * wicrect.Width;
1240         buffer = HeapAlloc(GetProcessHeap(), 0, pitch * wicrect.Height);
1241 
1242         hr = IWICBitmapFrameDecode_CopyPixels(bitmapframe, &wicrect, pitch,
1243                                               pitch * wicrect.Height, buffer);
1244 
1245         if (SUCCEEDED(hr) && (formatdesc->type == FORMAT_INDEX))
1246         {
1247             IWICPalette *wic_palette = NULL;
1248             UINT nb_colors;
1249 
1250             hr = IWICImagingFactory_CreatePalette(factory, &wic_palette);
1251             if (SUCCEEDED(hr))
1252                 hr = IWICBitmapFrameDecode_CopyPalette(bitmapframe, wic_palette);
1253             if (SUCCEEDED(hr))
1254                 hr = IWICPalette_GetColorCount(wic_palette, &nb_colors);
1255             if (SUCCEEDED(hr))
1256             {
1257                 colors = HeapAlloc(GetProcessHeap(), 0, nb_colors * sizeof(colors[0]));
1258                 palette = HeapAlloc(GetProcessHeap(), 0, nb_colors * sizeof(palette[0]));
1259                 if (!colors || !palette)
1260                     hr = E_OUTOFMEMORY;
1261             }
1262             if (SUCCEEDED(hr))
1263                 hr = IWICPalette_GetColors(wic_palette, nb_colors, colors, &nb_colors);
1264             if (SUCCEEDED(hr))
1265             {
1266                 UINT i;
1267 
1268                 /* Convert colors from WICColor (ARGB) to PALETTEENTRY (ABGR) */
1269                 for (i = 0; i < nb_colors; i++)
1270                 {
1271                     palette[i].peRed   = (colors[i] >> 16) & 0xff;
1272                     palette[i].peGreen = (colors[i] >> 8) & 0xff;
1273                     palette[i].peBlue  = colors[i] & 0xff;
1274                     palette[i].peFlags = (colors[i] >> 24) & 0xff; /* peFlags is the alpha component in DX8 and higher */
1275                 }
1276             }
1277             if (wic_palette)
1278                 IWICPalette_Release(wic_palette);
1279         }
1280 
1281         if (SUCCEEDED(hr))
1282         {
1283             hr = D3DXLoadSurfaceFromMemory(pDestSurface, pDestPalette, pDestRect,
1284                                            buffer, imginfo.Format, pitch,
1285                                            palette, &rect, dwFilter, Colorkey);
1286         }
1287 
1288         HeapFree(GetProcessHeap(), 0, colors);
1289         HeapFree(GetProcessHeap(), 0, palette);
1290         HeapFree(GetProcessHeap(), 0, buffer);
1291     }
1292 
1293     IWICBitmapFrameDecode_Release(bitmapframe);
1294 
1295 cleanup_bmp:
1296     IWICBitmapDecoder_Release(decoder);
1297 
1298 cleanup_err:
1299     if (factory)
1300         IWICImagingFactory_Release(factory);
1301 
1302     if (SUCCEEDED(com_init))
1303         CoUninitialize();
1304 
1305     if (imginfo.ImageFileFormat == D3DXIFF_DIB)
1306         HeapFree(GetProcessHeap(), 0, (void*)pSrcData);
1307 
1308     if (FAILED(hr))
1309         return D3DXERR_INVALIDDATA;
1310 
1311     if (pSrcInfo)
1312         *pSrcInfo = imginfo;
1313 
1314     return D3D_OK;
1315 }
1316 
1317 HRESULT WINAPI D3DXLoadSurfaceFromFileA(IDirect3DSurface9 *dst_surface,
1318         const PALETTEENTRY *dst_palette, const RECT *dst_rect, const char *src_file,
1319         const RECT *src_rect, DWORD filter, D3DCOLOR color_key, D3DXIMAGE_INFO *src_info)
1320 {
1321     WCHAR *src_file_w;
1322     HRESULT hr;
1323     int strlength;
1324 
1325     TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_file %s, "
1326             "src_rect %s, filter %#x, color_key 0x%08x, src_info %p.\n",
1327             dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), debugstr_a(src_file),
1328             wine_dbgstr_rect(src_rect), filter, color_key, src_info);
1329 
1330     if (!src_file || !dst_surface)
1331         return D3DERR_INVALIDCALL;
1332 
1333     strlength = MultiByteToWideChar(CP_ACP, 0, src_file, -1, NULL, 0);
1334     src_file_w = HeapAlloc(GetProcessHeap(), 0, strlength * sizeof(*src_file_w));
1335     MultiByteToWideChar(CP_ACP, 0, src_file, -1, src_file_w, strlength);
1336 
1337     hr = D3DXLoadSurfaceFromFileW(dst_surface, dst_palette, dst_rect,
1338             src_file_w, src_rect, filter, color_key, src_info);
1339     HeapFree(GetProcessHeap(), 0, src_file_w);
1340 
1341     return hr;
1342 }
1343 
1344 HRESULT WINAPI D3DXLoadSurfaceFromFileW(IDirect3DSurface9 *dst_surface,
1345         const PALETTEENTRY *dst_palette, const RECT *dst_rect, const WCHAR *src_file,
1346         const RECT *src_rect, DWORD filter, D3DCOLOR color_key, D3DXIMAGE_INFO *src_info)
1347 {
1348     UINT data_size;
1349     void *data;
1350     HRESULT hr;
1351 
1352     TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_file %s, "
1353             "src_rect %s, filter %#x, color_key 0x%08x, src_info %p.\n",
1354             dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), debugstr_w(src_file),
1355             wine_dbgstr_rect(src_rect), filter, color_key, src_info);
1356 
1357     if (!src_file || !dst_surface)
1358         return D3DERR_INVALIDCALL;
1359 
1360     if (FAILED(map_view_of_file(src_file, &data, &data_size)))
1361         return D3DXERR_INVALIDDATA;
1362 
1363     hr = D3DXLoadSurfaceFromFileInMemory(dst_surface, dst_palette, dst_rect,
1364             data, data_size, src_rect, filter, color_key, src_info);
1365     UnmapViewOfFile(data);
1366 
1367     return hr;
1368 }
1369 
1370 HRESULT WINAPI D3DXLoadSurfaceFromResourceA(IDirect3DSurface9 *dst_surface,
1371         const PALETTEENTRY *dst_palette, const RECT *dst_rect, HMODULE src_module, const char *resource,
1372         const RECT *src_rect, DWORD filter, D3DCOLOR color_key, D3DXIMAGE_INFO *src_info)
1373 {
1374     UINT data_size;
1375     HRSRC resinfo;
1376     void *data;
1377 
1378     TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_module %p, resource %s, "
1379             "src_rect %s, filter %#x, color_key 0x%08x, src_info %p.\n",
1380             dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), src_module, debugstr_a(resource),
1381             wine_dbgstr_rect(src_rect), filter, color_key, src_info);
1382 
1383     if (!dst_surface)
1384         return D3DERR_INVALIDCALL;
1385 
1386     if (!(resinfo = FindResourceA(src_module, resource, (const char *)RT_RCDATA))
1387             /* Try loading the resource as bitmap data (which is in DIB format D3DXIFF_DIB) */
1388             && !(resinfo = FindResourceA(src_module, resource, (const char *)RT_BITMAP)))
1389         return D3DXERR_INVALIDDATA;
1390 
1391     if (FAILED(load_resource_into_memory(src_module, resinfo, &data, &data_size)))
1392         return D3DXERR_INVALIDDATA;
1393 
1394     return D3DXLoadSurfaceFromFileInMemory(dst_surface, dst_palette, dst_rect,
1395             data, data_size, src_rect, filter, color_key, src_info);
1396 }
1397 
1398 HRESULT WINAPI D3DXLoadSurfaceFromResourceW(IDirect3DSurface9 *dst_surface,
1399         const PALETTEENTRY *dst_palette, const RECT *dst_rect, HMODULE src_module, const WCHAR *resource,
1400         const RECT *src_rect, DWORD filter, D3DCOLOR color_key, D3DXIMAGE_INFO *src_info)
1401 {
1402     UINT data_size;
1403     HRSRC resinfo;
1404     void *data;
1405 
1406     TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_module %p, resource %s, "
1407             "src_rect %s, filter %#x, color_key 0x%08x, src_info %p.\n",
1408             dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), src_module, debugstr_w(resource),
1409             wine_dbgstr_rect(src_rect), filter, color_key, src_info);
1410 
1411     if (!dst_surface)
1412         return D3DERR_INVALIDCALL;
1413 
1414     if (!(resinfo = FindResourceW(src_module, resource, (const WCHAR *)RT_RCDATA))
1415             /* Try loading the resource as bitmap data (which is in DIB format D3DXIFF_DIB) */
1416             && !(resinfo = FindResourceW(src_module, resource, (const WCHAR *)RT_BITMAP)))
1417         return D3DXERR_INVALIDDATA;
1418 
1419     if (FAILED(load_resource_into_memory(src_module, resinfo, &data, &data_size)))
1420         return D3DXERR_INVALIDDATA;
1421 
1422     return D3DXLoadSurfaceFromFileInMemory(dst_surface, dst_palette, dst_rect,
1423             data, data_size, src_rect, filter, color_key, src_info);
1424 }
1425 
1426 
1427 /************************************************************
1428  * helper functions for D3DXLoadSurfaceFromMemory
1429  */
1430 struct argb_conversion_info
1431 {
1432     const struct pixel_format_desc *srcformat;
1433     const struct pixel_format_desc *destformat;
1434     DWORD srcshift[4], destshift[4];
1435     DWORD srcmask[4], destmask[4];
1436     BOOL process_channel[4];
1437     DWORD channelmask;
1438 };
1439 
1440 static void init_argb_conversion_info(const struct pixel_format_desc *srcformat, const struct pixel_format_desc *destformat, struct argb_conversion_info *info)
1441 {
1442     UINT i;
1443     ZeroMemory(info->process_channel, 4 * sizeof(BOOL));
1444     info->channelmask = 0;
1445 
1446     info->srcformat  =  srcformat;
1447     info->destformat = destformat;
1448 
1449     for(i = 0;i < 4;i++) {
1450         /* srcshift is used to extract the _relevant_ components */
1451         info->srcshift[i]  =  srcformat->shift[i] + max( srcformat->bits[i] - destformat->bits[i], 0);
1452 
1453         /* destshift is used to move the components to the correct position */
1454         info->destshift[i] = destformat->shift[i] + max(destformat->bits[i] -  srcformat->bits[i], 0);
1455 
1456         info->srcmask[i]  = ((1 <<  srcformat->bits[i]) - 1) <<  srcformat->shift[i];
1457         info->destmask[i] = ((1 << destformat->bits[i]) - 1) << destformat->shift[i];
1458 
1459         /* channelmask specifies bits which aren't used in the source format but in the destination one */
1460         if(destformat->bits[i]) {
1461             if(srcformat->bits[i]) info->process_channel[i] = TRUE;
1462             else info->channelmask |= info->destmask[i];
1463         }
1464     }
1465 }
1466 
1467 /************************************************************
1468  * get_relevant_argb_components
1469  *
1470  * Extracts the relevant components from the source color and
1471  * drops the less significant bits if they aren't used by the destination format.
1472  */
1473 static void get_relevant_argb_components(const struct argb_conversion_info *info, const BYTE *col, DWORD *out)
1474 {
1475     unsigned int i, j;
1476     unsigned int component, mask;
1477 
1478     for (i = 0; i < 4; ++i)
1479     {
1480         if (!info->process_channel[i])
1481             continue;
1482 
1483         component = 0;
1484         mask = info->srcmask[i];
1485         for (j = 0; j < 4 && mask; ++j)
1486         {
1487             if (info->srcshift[i] < j * 8)
1488                 component |= (col[j] & mask) << (j * 8 - info->srcshift[i]);
1489             else
1490                 component |= (col[j] & mask) >> (info->srcshift[i] - j * 8);
1491             mask >>= 8;
1492         }
1493         out[i] = component;
1494     }
1495 }
1496 
1497 /************************************************************
1498  * make_argb_color
1499  *
1500  * Recombines the output of get_relevant_argb_components and converts
1501  * it to the destination format.
1502  */
1503 static DWORD make_argb_color(const struct argb_conversion_info *info, const DWORD *in)
1504 {
1505     UINT i;
1506     DWORD val = 0;
1507 
1508     for(i = 0;i < 4;i++) {
1509         if(info->process_channel[i]) {
1510             /* necessary to make sure that e.g. an X4R4G4B4 white maps to an R8G8B8 white instead of 0xf0f0f0 */
1511             signed int shift;
1512             for(shift = info->destshift[i]; shift > info->destformat->shift[i]; shift -= info->srcformat->bits[i]) val |= in[i] << shift;
1513             val |= (in[i] >> (info->destformat->shift[i] - shift)) << info->destformat->shift[i];
1514         }
1515     }
1516     val |= info->channelmask;   /* new channels are set to their maximal value */
1517     return val;
1518 }
1519 
1520 /* It doesn't work for components bigger than 32 bits (or somewhat smaller but unaligned). */
1521 static void format_to_vec4(const struct pixel_format_desc *format, const BYTE *src, struct vec4 *dst)
1522 {
1523     DWORD mask, tmp;
1524     unsigned int c;
1525 
1526     for (c = 0; c < 4; ++c)
1527     {
1528         static const unsigned int component_offsets[4] = {3, 0, 1, 2};
1529         float *dst_component = (float *)dst + component_offsets[c];
1530 
1531         if (format->bits[c])
1532         {
1533             mask = ~0u >> (32 - format->bits[c]);
1534 
1535             memcpy(&tmp, src + format->shift[c] / 8,
1536                     min(sizeof(DWORD), (format->shift[c] % 8 + format->bits[c] + 7) / 8));
1537 
1538             if (format->type == FORMAT_ARGBF16)
1539                 *dst_component = float_16_to_32(tmp);
1540             else if (format->type == FORMAT_ARGBF)
1541                 *dst_component = *(float *)&tmp;
1542             else
1543                 *dst_component = (float)((tmp >> format->shift[c] % 8) & mask) / mask;
1544         }
1545         else
1546             *dst_component = 1.0f;
1547     }
1548 }
1549 
1550 /* It doesn't work for components bigger than 32 bits. */
1551 static void format_from_vec4(const struct pixel_format_desc *format, const struct vec4 *src, BYTE *dst)
1552 {
1553     DWORD v, mask32;
1554     unsigned int c, i;
1555 
1556     memset(dst, 0, format->bytes_per_pixel);
1557 
1558     for (c = 0; c < 4; ++c)
1559     {
1560         static const unsigned int component_offsets[4] = {3, 0, 1, 2};
1561         const float src_component = *((const float *)src + component_offsets[c]);
1562 
1563         if (!format->bits[c])
1564             continue;
1565 
1566         mask32 = ~0u >> (32 - format->bits[c]);
1567 
1568         if (format->type == FORMAT_ARGBF16)
1569             v = float_32_to_16(src_component);
1570         else if (format->type == FORMAT_ARGBF)
1571             v = *(DWORD *)&src_component;
1572         else
1573             v = (DWORD)(src_component * ((1 << format->bits[c]) - 1) + 0.5f);
1574 
1575         for (i = format->shift[c] / 8 * 8; i < format->shift[c] + format->bits[c]; i += 8)
1576         {
1577             BYTE mask, byte;
1578 
1579             if (format->shift[c] > i)
1580             {
1581                 mask = mask32 << (format->shift[c] - i);
1582                 byte = (v << (format->shift[c] - i)) & mask;
1583             }
1584             else
1585             {
1586                 mask = mask32 >> (i - format->shift[c]);
1587                 byte = (v >> (i - format->shift[c])) & mask;
1588             }
1589             dst[i / 8] |= byte;
1590         }
1591     }
1592 }
1593 
1594 /************************************************************
1595  * copy_pixels
1596  *
1597  * Copies the source buffer to the destination buffer.
1598  * Works for any pixel format.
1599  * The source and the destination must be block-aligned.
1600  */
1601 void copy_pixels(const BYTE *src, UINT src_row_pitch, UINT src_slice_pitch,
1602         BYTE *dst, UINT dst_row_pitch, UINT dst_slice_pitch, const struct volume *size,
1603         const struct pixel_format_desc *format)
1604 {
1605     UINT row, slice;
1606     BYTE *dst_addr;
1607     const BYTE *src_addr;
1608     UINT row_block_count = (size->width + format->block_width - 1) / format->block_width;
1609     UINT row_count = (size->height + format->block_height - 1) / format->block_height;
1610 
1611     for (slice = 0; slice < size->depth; slice++)
1612     {
1613         src_addr = src + slice * src_slice_pitch;
1614         dst_addr = dst + slice * dst_slice_pitch;
1615 
1616         for (row = 0; row < row_count; row++)
1617         {
1618             memcpy(dst_addr, src_addr, row_block_count * format->block_byte_count);
1619             src_addr += src_row_pitch;
1620             dst_addr += dst_row_pitch;
1621         }
1622     }
1623 }
1624 
1625 /************************************************************
1626  * convert_argb_pixels
1627  *
1628  * Copies the source buffer to the destination buffer, performing
1629  * any necessary format conversion and color keying.
1630  * Pixels outsize the source rect are blacked out.
1631  */
1632 void convert_argb_pixels(const BYTE *src, UINT src_row_pitch, UINT src_slice_pitch, const struct volume *src_size,
1633         const struct pixel_format_desc *src_format, BYTE *dst, UINT dst_row_pitch, UINT dst_slice_pitch,
1634         const struct volume *dst_size, const struct pixel_format_desc *dst_format, D3DCOLOR color_key,
1635         const PALETTEENTRY *palette)
1636 {
1637     struct argb_conversion_info conv_info, ck_conv_info;
1638     const struct pixel_format_desc *ck_format = NULL;
1639     DWORD channels[4];
1640     UINT min_width, min_height, min_depth;
1641     UINT x, y, z;
1642 
1643     ZeroMemory(channels, sizeof(channels));
1644     init_argb_conversion_info(src_format, dst_format, &conv_info);
1645 
1646     min_width = min(src_size->width, dst_size->width);
1647     min_height = min(src_size->height, dst_size->height);
1648     min_depth = min(src_size->depth, dst_size->depth);
1649 
1650     if (color_key)
1651     {
1652         /* Color keys are always represented in D3DFMT_A8R8G8B8 format. */
1653         ck_format = get_format_info(D3DFMT_A8R8G8B8);
1654         init_argb_conversion_info(src_format, ck_format, &ck_conv_info);
1655     }
1656 
1657     for (z = 0; z < min_depth; z++) {
1658         const BYTE *src_slice_ptr = src + z * src_slice_pitch;
1659         BYTE *dst_slice_ptr = dst + z * dst_slice_pitch;
1660 
1661         for (y = 0; y < min_height; y++) {
1662             const BYTE *src_ptr = src_slice_ptr + y * src_row_pitch;
1663             BYTE *dst_ptr = dst_slice_ptr + y * dst_row_pitch;
1664 
1665             for (x = 0; x < min_width; x++) {
1666                 if (!src_format->to_rgba && !dst_format->from_rgba
1667                         && src_format->type == dst_format->type
1668                         && src_format->bytes_per_pixel <= 4 && dst_format->bytes_per_pixel <= 4)
1669                 {
1670                     DWORD val;
1671 
1672                     get_relevant_argb_components(&conv_info, src_ptr, channels);
1673                     val = make_argb_color(&conv_info, channels);
1674 
1675                     if (color_key)
1676                     {
1677                         DWORD ck_pixel;
1678 
1679                         get_relevant_argb_components(&ck_conv_info, src_ptr, channels);
1680                         ck_pixel = make_argb_color(&ck_conv_info, channels);
1681                         if (ck_pixel == color_key)
1682                             val &= ~conv_info.destmask[0];
1683                     }
1684                     memcpy(dst_ptr, &val, dst_format->bytes_per_pixel);
1685                 }
1686                 else
1687                 {
1688                     struct vec4 color, tmp;
1689 
1690                     format_to_vec4(src_format, src_ptr, &color);
1691                     if (src_format->to_rgba)
1692                         src_format->to_rgba(&color, &tmp, palette);
1693                     else
1694                         tmp = color;
1695 
1696                     if (ck_format)
1697                     {
1698                         DWORD ck_pixel;
1699 
1700                         format_from_vec4(ck_format, &tmp, (BYTE *)&ck_pixel);
1701                         if (ck_pixel == color_key)
1702                             tmp.w = 0.0f;
1703                     }
1704 
1705                     if (dst_format->from_rgba)
1706                         dst_format->from_rgba(&tmp, &color);
1707                     else
1708                         color = tmp;
1709 
1710                     format_from_vec4(dst_format, &color, dst_ptr);
1711                 }
1712 
1713                 src_ptr += src_format->bytes_per_pixel;
1714                 dst_ptr += dst_format->bytes_per_pixel;
1715             }
1716 
1717             if (src_size->width < dst_size->width) /* black out remaining pixels */
1718                 memset(dst_ptr, 0, dst_format->bytes_per_pixel * (dst_size->width - src_size->width));
1719         }
1720 
1721         if (src_size->height < dst_size->height) /* black out remaining pixels */
1722             memset(dst + src_size->height * dst_row_pitch, 0, dst_row_pitch * (dst_size->height - src_size->height));
1723     }
1724     if (src_size->depth < dst_size->depth) /* black out remaining pixels */
1725         memset(dst + src_size->depth * dst_slice_pitch, 0, dst_slice_pitch * (dst_size->depth - src_size->depth));
1726 }
1727 
1728 /************************************************************
1729  * point_filter_argb_pixels
1730  *
1731  * Copies the source buffer to the destination buffer, performing
1732  * any necessary format conversion, color keying and stretching
1733  * using a point filter.
1734  */
1735 void point_filter_argb_pixels(const BYTE *src, UINT src_row_pitch, UINT src_slice_pitch, const struct volume *src_size,
1736         const struct pixel_format_desc *src_format, BYTE *dst, UINT dst_row_pitch, UINT dst_slice_pitch,
1737         const struct volume *dst_size, const struct pixel_format_desc *dst_format, D3DCOLOR color_key,
1738         const PALETTEENTRY *palette)
1739 {
1740     struct argb_conversion_info conv_info, ck_conv_info;
1741     const struct pixel_format_desc *ck_format = NULL;
1742     DWORD channels[4];
1743     UINT x, y, z;
1744 
1745     ZeroMemory(channels, sizeof(channels));
1746     init_argb_conversion_info(src_format, dst_format, &conv_info);
1747 
1748     if (color_key)
1749     {
1750         /* Color keys are always represented in D3DFMT_A8R8G8B8 format. */
1751         ck_format = get_format_info(D3DFMT_A8R8G8B8);
1752         init_argb_conversion_info(src_format, ck_format, &ck_conv_info);
1753     }
1754 
1755     for (z = 0; z < dst_size->depth; z++)
1756     {
1757         BYTE *dst_slice_ptr = dst + z * dst_slice_pitch;
1758         const BYTE *src_slice_ptr = src + src_slice_pitch * (z * src_size->depth / dst_size->depth);
1759 
1760         for (y = 0; y < dst_size->height; y++)
1761         {
1762             BYTE *dst_ptr = dst_slice_ptr + y * dst_row_pitch;
1763             const BYTE *src_row_ptr = src_slice_ptr + src_row_pitch * (y * src_size->height / dst_size->height);
1764 
1765             for (x = 0; x < dst_size->width; x++)
1766             {
1767                 const BYTE *src_ptr = src_row_ptr + (x * src_size->width / dst_size->width) * src_format->bytes_per_pixel;
1768 
1769                 if (!src_format->to_rgba && !dst_format->from_rgba
1770                         && src_format->type == dst_format->type
1771                         && src_format->bytes_per_pixel <= 4 && dst_format->bytes_per_pixel <= 4)
1772                 {
1773                     DWORD val;
1774 
1775                     get_relevant_argb_components(&conv_info, src_ptr, channels);
1776                     val = make_argb_color(&conv_info, channels);
1777 
1778                     if (color_key)
1779                     {
1780                         DWORD ck_pixel;
1781 
1782                         get_relevant_argb_components(&ck_conv_info, src_ptr, channels);
1783                         ck_pixel = make_argb_color(&ck_conv_info, channels);
1784                         if (ck_pixel == color_key)
1785                             val &= ~conv_info.destmask[0];
1786                     }
1787                     memcpy(dst_ptr, &val, dst_format->bytes_per_pixel);
1788                 }
1789                 else
1790                 {
1791                     struct vec4 color, tmp;
1792 
1793                     format_to_vec4(src_format, src_ptr, &color);
1794                     if (src_format->to_rgba)
1795                         src_format->to_rgba(&color, &tmp, palette);
1796                     else
1797                         tmp = color;
1798 
1799                     if (ck_format)
1800                     {
1801                         DWORD ck_pixel;
1802 
1803                         format_from_vec4(ck_format, &tmp, (BYTE *)&ck_pixel);
1804                         if (ck_pixel == color_key)
1805                             tmp.w = 0.0f;
1806                     }
1807 
1808                     if (dst_format->from_rgba)
1809                         dst_format->from_rgba(&tmp, &color);
1810                     else
1811                         color = tmp;
1812 
1813                     format_from_vec4(dst_format, &color, dst_ptr);
1814                 }
1815 
1816                 dst_ptr += dst_format->bytes_per_pixel;
1817             }
1818         }
1819     }
1820 }
1821 
1822 typedef BOOL (*dxtn_conversion_func)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
1823                                      enum wined3d_format_id format, unsigned int w, unsigned int h);
1824 
1825 static dxtn_conversion_func get_dxtn_conversion_func(D3DFORMAT format, BOOL encode)
1826 {
1827     switch (format)
1828     {
1829         case D3DFMT_DXT1:
1830             return encode ? wined3d_dxt1_encode : wined3d_dxt1_decode;
1831         case D3DFMT_DXT3:
1832             return encode ? wined3d_dxt3_encode : wined3d_dxt3_decode;
1833         case D3DFMT_DXT5:
1834             return encode ? wined3d_dxt5_encode : wined3d_dxt5_decode;
1835         default:
1836             return NULL;
1837     }
1838 }
1839 
1840 /************************************************************
1841  * D3DXLoadSurfaceFromMemory
1842  *
1843  * Loads data from a given memory chunk into a surface,
1844  * applying any of the specified filters.
1845  *
1846  * PARAMS
1847  *   pDestSurface [I] pointer to the surface
1848  *   pDestPalette [I] palette to use
1849  *   pDestRect    [I] to be filled area of the surface
1850  *   pSrcMemory   [I] pointer to the source data
1851  *   SrcFormat    [I] format of the source pixel data
1852  *   SrcPitch     [I] number of bytes in a row
1853  *   pSrcPalette  [I] palette used in the source image
1854  *   pSrcRect     [I] area of the source data to load
1855  *   dwFilter     [I] filter to apply on stretching
1856  *   Colorkey     [I] colorkey
1857  *
1858  * RETURNS
1859  *   Success: D3D_OK, if we successfully load the pixel data into our surface or
1860  *                    if pSrcMemory is NULL but the other parameters are valid
1861  *   Failure: D3DERR_INVALIDCALL, if pDestSurface, SrcPitch or pSrcRect is NULL or
1862  *                                if SrcFormat is an invalid format (other than D3DFMT_UNKNOWN) or
1863  *                                if DestRect is invalid
1864  *            D3DXERR_INVALIDDATA, if we fail to lock pDestSurface
1865  *            E_FAIL, if SrcFormat is D3DFMT_UNKNOWN or the dimensions of pSrcRect are invalid
1866  *
1867  * NOTES
1868  *   pSrcRect specifies the dimensions of the source data;
1869  *   negative values for pSrcRect are allowed as we're only looking at the width and height anyway.
1870  *
1871  */
1872 HRESULT WINAPI D3DXLoadSurfaceFromMemory(IDirect3DSurface9 *dst_surface,
1873         const PALETTEENTRY *dst_palette, const RECT *dst_rect, const void *src_memory,
1874         D3DFORMAT src_format, UINT src_pitch, const PALETTEENTRY *src_palette, const RECT *src_rect,
1875         DWORD filter, D3DCOLOR color_key)
1876 {
1877     const struct pixel_format_desc *srcformatdesc, *destformatdesc;
1878     void *tmp_src_memory = NULL, *tmp_dst_memory = NULL;
1879     dxtn_conversion_func pre_convert = NULL, post_convert = NULL;
1880     IDirect3DSurface9 *surface = dst_surface;
1881     IDirect3DDevice9 *device;
1882     D3DSURFACE_DESC surfdesc;
1883     D3DLOCKED_RECT lockrect;
1884     struct volume src_size, dst_size;
1885     HRESULT hr = D3D_OK;
1886 
1887     TRACE("(%p, %p, %s, %p, %#x, %u, %p, %s, %#x, 0x%08x)\n",
1888             dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), src_memory, src_format,
1889             src_pitch, src_palette, wine_dbgstr_rect(src_rect), filter, color_key);
1890 
1891     if (!dst_surface || !src_memory || !src_rect)
1892     {
1893         WARN("Invalid argument specified.\n");
1894         return D3DERR_INVALIDCALL;
1895     }
1896     if (src_format == D3DFMT_UNKNOWN
1897             || src_rect->left >= src_rect->right
1898             || src_rect->top >= src_rect->bottom)
1899     {
1900         WARN("Invalid src_format or src_rect.\n");
1901         return E_FAIL;
1902     }
1903 
1904     if (filter == D3DX_DEFAULT)
1905         filter = D3DX_FILTER_TRIANGLE | D3DX_FILTER_DITHER;
1906 
1907     IDirect3DSurface9_GetDesc(dst_surface, &surfdesc);
1908 
1909     src_size.width = src_rect->right - src_rect->left;
1910     src_size.height = src_rect->bottom - src_rect->top;
1911     src_size.depth = 1;
1912     if (!dst_rect)
1913     {
1914         dst_size.width = surfdesc.Width;
1915         dst_size.height = surfdesc.Height;
1916     }
1917     else
1918     {
1919         if (dst_rect->left > dst_rect->right || dst_rect->right > surfdesc.Width
1920                 || dst_rect->top > dst_rect->bottom || dst_rect->bottom > surfdesc.Height
1921                 || dst_rect->left < 0 || dst_rect->top < 0)
1922         {
1923             WARN("Invalid dst_rect specified.\n");
1924             return D3DERR_INVALIDCALL;
1925         }
1926         dst_size.width = dst_rect->right - dst_rect->left;
1927         dst_size.height = dst_rect->bottom - dst_rect->top;
1928         if (!dst_size.width || !dst_size.height)
1929             return D3D_OK;
1930     }
1931     dst_size.depth = 1;
1932 
1933     srcformatdesc = get_format_info(src_format);
1934     destformatdesc = get_format_info(surfdesc.Format);
1935     if (srcformatdesc->type == FORMAT_UNKNOWN || destformatdesc->type == FORMAT_UNKNOWN)
1936     {
1937         FIXME("Unsupported pixel format conversion %#x -> %#x\n", src_format, surfdesc.Format);
1938         return E_NOTIMPL;
1939     }
1940 
1941     if (surfdesc.Pool == D3DPOOL_DEFAULT && !(surfdesc.Usage & D3DUSAGE_DYNAMIC))
1942     {
1943         IDirect3DSurface9_GetDevice(dst_surface, &device);
1944         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, surfdesc.Width,
1945                 surfdesc.Height, surfdesc.Format, D3DPOOL_SYSTEMMEM, &surface, NULL);
1946         IDirect3DDevice9_Release(device);
1947         if (FAILED(hr))
1948         {
1949             WARN("Failed to create staging surface, hr %#x.\n", hr);
1950             return D3DERR_INVALIDCALL;
1951         }
1952     }
1953 
1954     if (FAILED(IDirect3DSurface9_LockRect(surface, &lockrect, dst_rect, 0)))
1955     {
1956         if (surface != dst_surface)
1957             IDirect3DSurface9_Release(surface);
1958         return D3DXERR_INVALIDDATA;
1959     }
1960 
1961     if (src_format == surfdesc.Format
1962             && dst_size.width == src_size.width
1963             && dst_size.height == src_size.height
1964             && color_key == 0) /* Simple copy. */
1965     {
1966         if (src_rect->left & (srcformatdesc->block_width - 1)
1967                 || src_rect->top & (srcformatdesc->block_height - 1)
1968                 || (src_rect->right & (srcformatdesc->block_width - 1)
1969                     && src_size.width != surfdesc.Width)
1970                 || (src_rect->bottom & (srcformatdesc->block_height - 1)
1971                     && src_size.height != surfdesc.Height))
1972         {
1973             WARN("Source rect %s is misaligned.\n", wine_dbgstr_rect(src_rect));
1974             hr = D3DXERR_INVALIDDATA;
1975             goto done;
1976         }
1977 
1978         copy_pixels(src_memory, src_pitch, 0, lockrect.pBits, lockrect.Pitch, 0,
1979                 &src_size, srcformatdesc);
1980     }
1981     else /* Stretching or format conversion. */
1982     {
1983         UINT tmp_src_pitch, tmp_dst_pitch;
1984 
1985         pre_convert  = get_dxtn_conversion_func(srcformatdesc->format, FALSE);
1986         post_convert = get_dxtn_conversion_func(destformatdesc->format, TRUE);
1987 
1988         if ((!pre_convert && !is_conversion_from_supported(srcformatdesc)) ||
1989                 (!post_convert && !is_conversion_to_supported(destformatdesc)))
1990         {
1991             FIXME("Unsupported format conversion %#x -> %#x.\n", src_format, surfdesc.Format);
1992             hr = E_NOTIMPL;
1993             goto done;
1994         }
1995 
1996         /* handle pre-conversion */
1997         if (pre_convert)
1998         {
1999             tmp_src_memory = HeapAlloc(GetProcessHeap(), 0, src_size.width * src_size.height * sizeof(DWORD));
2000             if (!tmp_src_memory)
2001             {
2002                 hr = E_OUTOFMEMORY;
2003                 goto done;
2004             }
2005             tmp_src_pitch = src_size.width * sizeof(DWORD);
2006             if (!pre_convert(src_memory, tmp_src_memory, src_pitch, tmp_src_pitch,
2007                     WINED3DFMT_B8G8R8A8_UNORM, src_size.width, src_size.height))
2008             {
2009                 hr = E_FAIL;
2010                 goto done;
2011             }
2012             srcformatdesc = get_format_info(D3DFMT_A8R8G8B8);
2013         }
2014         else
2015         {
2016             tmp_src_memory = (void *)src_memory;
2017             tmp_src_pitch  = src_pitch;
2018         }
2019 
2020         /* handle post-conversion */
2021         if (post_convert)
2022         {
2023             tmp_dst_memory = HeapAlloc(GetProcessHeap(), 0, dst_size.width * dst_size.height * sizeof(DWORD));
2024             if (!tmp_dst_memory)
2025             {
2026                 hr = E_OUTOFMEMORY;
2027                 goto done;
2028             }
2029             tmp_dst_pitch = dst_size.width * sizeof(DWORD);
2030             destformatdesc = get_format_info(D3DFMT_A8R8G8B8);
2031         }
2032         else
2033         {
2034             tmp_dst_memory = lockrect.pBits;
2035             tmp_dst_pitch  = lockrect.Pitch;
2036         }
2037 
2038         if ((filter & 0xf) == D3DX_FILTER_NONE)
2039         {
2040             convert_argb_pixels(tmp_src_memory, tmp_src_pitch, 0, &src_size, srcformatdesc,
2041                     tmp_dst_memory, tmp_dst_pitch, 0, &dst_size, destformatdesc, color_key, src_palette);
2042         }
2043         else /* if ((filter & 0xf) == D3DX_FILTER_POINT) */
2044         {
2045             if ((filter & 0xf) != D3DX_FILTER_POINT)
2046                 FIXME("Unhandled filter %#x.\n", filter);
2047 
2048             /* Always apply a point filter until D3DX_FILTER_LINEAR,
2049              * D3DX_FILTER_TRIANGLE and D3DX_FILTER_BOX are implemented. */
2050             point_filter_argb_pixels(tmp_src_memory, tmp_src_pitch, 0, &src_size, srcformatdesc,
2051                     tmp_dst_memory, tmp_dst_pitch, 0, &dst_size, destformatdesc, color_key, src_palette);
2052         }
2053 
2054         /* handle post-conversion */
2055         if (post_convert)
2056         {
2057             if (!post_convert(tmp_dst_memory, lockrect.pBits, tmp_dst_pitch, lockrect.Pitch,
2058                     WINED3DFMT_B8G8R8A8_UNORM, dst_size.width, dst_size.height))
2059             {
2060                 hr = E_FAIL;
2061                 goto done;
2062             }
2063         }
2064     }
2065 
2066 done:
2067     IDirect3DSurface9_UnlockRect(surface);
2068     if (surface != dst_surface)
2069     {
2070         if (SUCCEEDED(hr))
2071         {
2072             IDirect3DSurface9_GetDevice(dst_surface, &device);
2073             hr = IDirect3DDevice9_UpdateSurface(device, surface, NULL, dst_surface, NULL);
2074             IDirect3DDevice9_Release(device);
2075         }
2076         IDirect3DSurface9_Release(surface);
2077 
2078         if (pre_convert)
2079             HeapFree(GetProcessHeap(), 0, tmp_src_memory);
2080         if (post_convert)
2081             HeapFree(GetProcessHeap(), 0, tmp_dst_memory);
2082         IDirect3DSurface9_UnlockRect(dst_surface);
2083     }
2084 
2085     return hr;
2086 }
2087 
2088 /************************************************************
2089  * D3DXLoadSurfaceFromSurface
2090  *
2091  * Copies the contents from one surface to another, performing any required
2092  * format conversion, resizing or filtering.
2093  *
2094  * PARAMS
2095  *   pDestSurface [I] pointer to the destination surface
2096  *   pDestPalette [I] palette to use
2097  *   pDestRect    [I] to be filled area of the surface
2098  *   pSrcSurface  [I] pointer to the source surface
2099  *   pSrcPalette  [I] palette used for the source surface
2100  *   pSrcRect     [I] area of the source data to load
2101  *   dwFilter     [I] filter to apply on resizing
2102  *   Colorkey     [I] any ARGB value or 0 to disable color-keying
2103  *
2104  * RETURNS
2105  *   Success: D3D_OK
2106  *   Failure: D3DERR_INVALIDCALL, if pDestSurface or pSrcSurface is NULL
2107  *            D3DXERR_INVALIDDATA, if one of the surfaces is not lockable
2108  *
2109  */
2110 HRESULT WINAPI D3DXLoadSurfaceFromSurface(IDirect3DSurface9 *dst_surface,
2111         const PALETTEENTRY *dst_palette, const RECT *dst_rect, IDirect3DSurface9 *src_surface,
2112         const PALETTEENTRY *src_palette, const RECT *src_rect, DWORD filter, D3DCOLOR color_key)
2113 {
2114     IDirect3DSurface9 *surface = src_surface;
2115     D3DTEXTUREFILTERTYPE d3d_filter;
2116     IDirect3DDevice9 *device;
2117     D3DSURFACE_DESC src_desc;
2118     D3DLOCKED_RECT lock;
2119     HRESULT hr;
2120     RECT s;
2121 
2122     TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_surface %p, "
2123             "src_palette %p, src_rect %s, filter %#x, color_key 0x%08x.\n",
2124             dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), src_surface,
2125             src_palette, wine_dbgstr_rect(src_rect), filter, color_key);
2126 
2127     if (!dst_surface || !src_surface)
2128         return D3DERR_INVALIDCALL;
2129 
2130     if (!dst_palette && !src_palette && !color_key)
2131     {
2132         switch (filter)
2133         {
2134             case D3DX_FILTER_NONE:
2135                 d3d_filter = D3DTEXF_NONE;
2136                 break;
2137 
2138             case D3DX_FILTER_POINT:
2139                 d3d_filter = D3DTEXF_POINT;
2140                 break;
2141 
2142             case D3DX_FILTER_LINEAR:
2143                 d3d_filter = D3DTEXF_LINEAR;
2144                 break;
2145 
2146             default:
2147                 d3d_filter = ~0u;
2148                 break;
2149         }
2150 
2151         if (d3d_filter != ~0u)
2152         {
2153             IDirect3DSurface9_GetDevice(src_surface, &device);
2154             hr = IDirect3DDevice9_StretchRect(device, src_surface, src_rect, dst_surface, dst_rect, d3d_filter);
2155             IDirect3DDevice9_Release(device);
2156             if (SUCCEEDED(hr))
2157                 return D3D_OK;
2158         }
2159     }
2160 
2161     IDirect3DSurface9_GetDesc(src_surface, &src_desc);
2162 
2163     if (!src_rect)
2164     {
2165         SetRect(&s, 0, 0, src_desc.Width, src_desc.Height);
2166         src_rect = &s;
2167     }
2168 
2169     if (FAILED(IDirect3DSurface9_LockRect(surface, &lock, NULL, D3DLOCK_READONLY)))
2170     {
2171         IDirect3DSurface9_GetDevice(src_surface, &device);
2172         if (FAILED(IDirect3DDevice9_CreateRenderTarget(device, src_desc.Width, src_desc.Height,
2173                 src_desc.Format, D3DMULTISAMPLE_NONE, 0, TRUE, &surface, NULL)))
2174         {
2175             IDirect3DDevice9_Release(device);
2176             return D3DXERR_INVALIDDATA;
2177         }
2178 
2179         if (SUCCEEDED(hr = IDirect3DDevice9_StretchRect(device, src_surface, NULL, surface, NULL, D3DTEXF_NONE)))
2180             hr = IDirect3DSurface9_LockRect(surface, &lock, NULL, D3DLOCK_READONLY);
2181         IDirect3DDevice9_Release(device);
2182         if (FAILED(hr))
2183         {
2184             IDirect3DSurface9_Release(surface);
2185             return D3DXERR_INVALIDDATA;
2186         }
2187     }
2188 
2189     hr = D3DXLoadSurfaceFromMemory(dst_surface, dst_palette, dst_rect, lock.pBits,
2190             src_desc.Format, lock.Pitch, src_palette, src_rect, filter, color_key);
2191 
2192     IDirect3DSurface9_UnlockRect(surface);
2193     if (surface != src_surface)
2194         IDirect3DSurface9_Release(surface);
2195 
2196     return hr;
2197 }
2198 
2199 
2200 HRESULT WINAPI D3DXSaveSurfaceToFileA(const char *dst_filename, D3DXIMAGE_FILEFORMAT file_format,
2201         IDirect3DSurface9 *src_surface, const PALETTEENTRY *src_palette, const RECT *src_rect)
2202 {
2203     int len;
2204     WCHAR *filename;
2205     HRESULT hr;
2206     ID3DXBuffer *buffer;
2207 
2208     TRACE("(%s, %#x, %p, %p, %s): relay\n",
2209             wine_dbgstr_a(dst_filename), file_format, src_surface, src_palette, wine_dbgstr_rect(src_rect));
2210 
2211     if (!dst_filename) return D3DERR_INVALIDCALL;
2212 
2213     len = MultiByteToWideChar(CP_ACP, 0, dst_filename, -1, NULL, 0);
2214     filename = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2215     if (!filename) return E_OUTOFMEMORY;
2216     MultiByteToWideChar(CP_ACP, 0, dst_filename, -1, filename, len);
2217 
2218     hr = D3DXSaveSurfaceToFileInMemory(&buffer, file_format, src_surface, src_palette, src_rect);
2219     if (SUCCEEDED(hr))
2220     {
2221         hr = write_buffer_to_file(filename, buffer);
2222         ID3DXBuffer_Release(buffer);
2223     }
2224 
2225     HeapFree(GetProcessHeap(), 0, filename);
2226     return hr;
2227 }
2228 
2229 HRESULT WINAPI D3DXSaveSurfaceToFileW(const WCHAR *dst_filename, D3DXIMAGE_FILEFORMAT file_format,
2230         IDirect3DSurface9 *src_surface, const PALETTEENTRY *src_palette, const RECT *src_rect)
2231 {
2232     HRESULT hr;
2233     ID3DXBuffer *buffer;
2234 
2235     TRACE("(%s, %#x, %p, %p, %s): relay\n",
2236         wine_dbgstr_w(dst_filename), file_format, src_surface, src_palette, wine_dbgstr_rect(src_rect));
2237 
2238     if (!dst_filename) return D3DERR_INVALIDCALL;
2239 
2240     hr = D3DXSaveSurfaceToFileInMemory(&buffer, file_format, src_surface, src_palette, src_rect);
2241     if (SUCCEEDED(hr))
2242     {
2243         hr = write_buffer_to_file(dst_filename, buffer);
2244         ID3DXBuffer_Release(buffer);
2245     }
2246 
2247     return hr;
2248 }
2249 
2250 HRESULT WINAPI D3DXSaveSurfaceToFileInMemory(ID3DXBuffer **dst_buffer, D3DXIMAGE_FILEFORMAT file_format,
2251         IDirect3DSurface9 *src_surface, const PALETTEENTRY *src_palette, const RECT *src_rect)
2252 {
2253     IWICBitmapEncoder *encoder = NULL;
2254     IWICBitmapFrameEncode *frame = NULL;
2255     IPropertyBag2 *encoder_options = NULL;
2256     IStream *stream = NULL;
2257     HRESULT hr;
2258     HRESULT initresult;
2259     const CLSID *encoder_clsid;
2260     const GUID *pixel_format_guid;
2261     WICPixelFormatGUID wic_pixel_format;
2262     D3DFORMAT d3d_pixel_format;
2263     D3DSURFACE_DESC src_surface_desc;
2264     D3DLOCKED_RECT locked_rect;
2265     int width, height;
2266     STATSTG stream_stats;
2267     HGLOBAL stream_hglobal;
2268     ID3DXBuffer *buffer;
2269     DWORD size;
2270 
2271     TRACE("(%p, %#x, %p, %p, %s)\n",
2272         dst_buffer, file_format, src_surface, src_palette, wine_dbgstr_rect(src_rect));
2273 
2274     if (!dst_buffer || !src_surface) return D3DERR_INVALIDCALL;
2275 
2276     if (src_palette)
2277     {
2278         FIXME("Saving surfaces with palettized pixel formats is not implemented yet\n");
2279         return D3DERR_INVALIDCALL;
2280     }
2281 
2282     switch (file_format)
2283     {
2284         case D3DXIFF_BMP:
2285         case D3DXIFF_DIB:
2286             encoder_clsid = &CLSID_WICBmpEncoder;
2287             break;
2288         case D3DXIFF_PNG:
2289             encoder_clsid = &CLSID_WICPngEncoder;
2290             break;
2291         case D3DXIFF_JPG:
2292             encoder_clsid = &CLSID_WICJpegEncoder;
2293             break;
2294         case D3DXIFF_DDS:
2295             return save_dds_surface_to_memory(dst_buffer, src_surface, src_rect);
2296         case D3DXIFF_HDR:
2297         case D3DXIFF_PFM:
2298         case D3DXIFF_TGA:
2299         case D3DXIFF_PPM:
2300             FIXME("File format %#x is not supported yet\n", file_format);
2301             return E_NOTIMPL;
2302         default:
2303             return D3DERR_INVALIDCALL;
2304     }
2305 
2306     IDirect3DSurface9_GetDesc(src_surface, &src_surface_desc);
2307     if (src_rect)
2308     {
2309         if (src_rect->left == src_rect->right || src_rect->top == src_rect->bottom)
2310         {
2311             WARN("Invalid rectangle with 0 area\n");
2312             return D3DXCreateBuffer(64, dst_buffer);
2313         }
2314         if (src_rect->left < 0 || src_rect->top < 0)
2315             return D3DERR_INVALIDCALL;
2316         if (src_rect->left > src_rect->right || src_rect->top > src_rect->bottom)
2317             return D3DERR_INVALIDCALL;
2318         if (src_rect->right > src_surface_desc.Width || src_rect->bottom > src_surface_desc.Height)
2319             return D3DERR_INVALIDCALL;
2320 
2321         width = src_rect->right - src_rect->left;
2322         height = src_rect->bottom - src_rect->top;
2323     }
2324     else
2325     {
2326         width = src_surface_desc.Width;
2327         height = src_surface_desc.Height;
2328     }
2329 
2330     initresult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
2331 
2332     hr = CoCreateInstance(encoder_clsid, NULL, CLSCTX_INPROC_SERVER,
2333         &IID_IWICBitmapEncoder, (void **)&encoder);
2334     if (FAILED(hr)) goto cleanup_err;
2335 
2336     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
2337     if (FAILED(hr)) goto cleanup_err;
2338 
2339     hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache);
2340     if (FAILED(hr)) goto cleanup_err;
2341 
2342     hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frame, &encoder_options);
2343     if (FAILED(hr)) goto cleanup_err;
2344 
2345     hr = IWICBitmapFrameEncode_Initialize(frame, encoder_options);
2346     if (FAILED(hr)) goto cleanup_err;
2347 
2348     hr = IWICBitmapFrameEncode_SetSize(frame, width, height);
2349     if (FAILED(hr)) goto cleanup_err;
2350 
2351     pixel_format_guid = d3dformat_to_wic_guid(src_surface_desc.Format);
2352     if (!pixel_format_guid)
2353     {
2354         FIXME("Pixel format %#x is not supported yet\n", src_surface_desc.Format);
2355         hr = E_NOTIMPL;
2356         goto cleanup;
2357     }
2358 
2359     memcpy(&wic_pixel_format, pixel_format_guid, sizeof(GUID));
2360     hr = IWICBitmapFrameEncode_SetPixelFormat(frame, &wic_pixel_format);
2361     d3d_pixel_format = wic_guid_to_d3dformat(&wic_pixel_format);
2362     if (SUCCEEDED(hr) && d3d_pixel_format != D3DFMT_UNKNOWN)
2363     {
2364         TRACE("Using pixel format %s %#x\n", debugstr_guid(&wic_pixel_format), d3d_pixel_format);
2365 
2366         if (src_surface_desc.Format == d3d_pixel_format) /* Simple copy */
2367         {
2368             hr = IDirect3DSurface9_LockRect(src_surface, &locked_rect, src_rect, D3DLOCK_READONLY);
2369             if (SUCCEEDED(hr))
2370             {
2371                 IWICBitmapFrameEncode_WritePixels(frame, height,
2372                     locked_rect.Pitch, height * locked_rect.Pitch, locked_rect.pBits);
2373                 IDirect3DSurface9_UnlockRect(src_surface);
2374             }
2375         }
2376         else /* Pixel format conversion */
2377         {
2378             const struct pixel_format_desc *src_format_desc, *dst_format_desc;
2379             struct volume size;
2380             DWORD dst_pitch;
2381             void *dst_data;
2382 
2383             src_format_desc = get_format_info(src_surface_desc.Format);
2384             dst_format_desc = get_format_info(d3d_pixel_format);
2385             if (!is_conversion_from_supported(src_format_desc)
2386                     || !is_conversion_to_supported(dst_format_desc))
2387             {
2388                 FIXME("Unsupported format conversion %#x -> %#x.\n",
2389                     src_surface_desc.Format, d3d_pixel_format);
2390                 hr = E_NOTIMPL;
2391                 goto cleanup;
2392             }
2393 
2394             size.width = width;
2395             size.height = height;
2396             size.depth = 1;
2397             dst_pitch = width * dst_format_desc->bytes_per_pixel;
2398             dst_data = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height);
2399             if (!dst_data)
2400             {
2401                 hr = E_OUTOFMEMORY;
2402                 goto cleanup;
2403             }
2404 
2405             hr = IDirect3DSurface9_LockRect(src_surface, &locked_rect, src_rect, D3DLOCK_READONLY);
2406             if (SUCCEEDED(hr))
2407             {
2408                 convert_argb_pixels(locked_rect.pBits, locked_rect.Pitch, 0, &size, src_format_desc,
2409                     dst_data, dst_pitch, 0, &size, dst_format_desc, 0, NULL);
2410                 IDirect3DSurface9_UnlockRect(src_surface);
2411             }
2412 
2413             IWICBitmapFrameEncode_WritePixels(frame, height, dst_pitch, dst_pitch * height, dst_data);
2414             HeapFree(GetProcessHeap(), 0, dst_data);
2415         }
2416 
2417         hr = IWICBitmapFrameEncode_Commit(frame);
2418         if (SUCCEEDED(hr)) hr = IWICBitmapEncoder_Commit(encoder);
2419     }
2420     else WARN("Unsupported pixel format %#x\n", src_surface_desc.Format);
2421 
2422     /* copy data from stream to ID3DXBuffer */
2423     hr = IStream_Stat(stream, &stream_stats, STATFLAG_NONAME);
2424     if (FAILED(hr)) goto cleanup_err;
2425 
2426     if (stream_stats.cbSize.u.HighPart != 0)
2427     {
2428         hr = D3DXERR_INVALIDDATA;
2429         goto cleanup;
2430     }
2431     size = stream_stats.cbSize.u.LowPart;
2432 
2433     /* Remove BMP header for DIB */
2434     if (file_format == D3DXIFF_DIB)
2435         size -= sizeof(BITMAPFILEHEADER);
2436 
2437     hr = D3DXCreateBuffer(size, &buffer);
2438     if (FAILED(hr)) goto cleanup;
2439 
2440     hr = GetHGlobalFromStream(stream, &stream_hglobal);
2441     if (SUCCEEDED(hr))
2442     {
2443         void *buffer_pointer = ID3DXBuffer_GetBufferPointer(buffer);
2444         void *stream_data = GlobalLock(stream_hglobal);
2445         /* Remove BMP header for DIB */
2446         if (file_format == D3DXIFF_DIB)
2447             stream_data = (void*)((BYTE*)stream_data + sizeof(BITMAPFILEHEADER));
2448         memcpy(buffer_pointer, stream_data, size);
2449         GlobalUnlock(stream_hglobal);
2450         *dst_buffer = buffer;
2451     }
2452     else ID3DXBuffer_Release(buffer);
2453 
2454 cleanup_err:
2455     if (FAILED(hr) && hr != E_OUTOFMEMORY)
2456         hr = D3DERR_INVALIDCALL;
2457 
2458 cleanup:
2459     if (stream) IStream_Release(stream);
2460 
2461     if (frame) IWICBitmapFrameEncode_Release(frame);
2462     if (encoder_options) IPropertyBag2_Release(encoder_options);
2463 
2464     if (encoder) IWICBitmapEncoder_Release(encoder);
2465 
2466     if (SUCCEEDED(initresult)) CoUninitialize();
2467 
2468     return hr;
2469 }
2470