xref: /reactos/dll/directx/wine/d3dx9_36/surface.c (revision aad80191)
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             if (!wined3d_dxtn_supported()) return NULL;
1831             return encode ? wined3d_dxt1_encode : wined3d_dxt1_decode;
1832         case D3DFMT_DXT3:
1833             if (!wined3d_dxtn_supported()) return NULL;
1834             return encode ? wined3d_dxt3_encode : wined3d_dxt3_decode;
1835         case D3DFMT_DXT5:
1836             if (!wined3d_dxtn_supported()) return NULL;
1837             return encode ? wined3d_dxt5_encode : wined3d_dxt5_decode;
1838         default:
1839             return NULL;
1840     }
1841 }
1842 
1843 /************************************************************
1844  * D3DXLoadSurfaceFromMemory
1845  *
1846  * Loads data from a given memory chunk into a surface,
1847  * applying any of the specified filters.
1848  *
1849  * PARAMS
1850  *   pDestSurface [I] pointer to the surface
1851  *   pDestPalette [I] palette to use
1852  *   pDestRect    [I] to be filled area of the surface
1853  *   pSrcMemory   [I] pointer to the source data
1854  *   SrcFormat    [I] format of the source pixel data
1855  *   SrcPitch     [I] number of bytes in a row
1856  *   pSrcPalette  [I] palette used in the source image
1857  *   pSrcRect     [I] area of the source data to load
1858  *   dwFilter     [I] filter to apply on stretching
1859  *   Colorkey     [I] colorkey
1860  *
1861  * RETURNS
1862  *   Success: D3D_OK, if we successfully load the pixel data into our surface or
1863  *                    if pSrcMemory is NULL but the other parameters are valid
1864  *   Failure: D3DERR_INVALIDCALL, if pDestSurface, SrcPitch or pSrcRect is NULL or
1865  *                                if SrcFormat is an invalid format (other than D3DFMT_UNKNOWN) or
1866  *                                if DestRect is invalid
1867  *            D3DXERR_INVALIDDATA, if we fail to lock pDestSurface
1868  *            E_FAIL, if SrcFormat is D3DFMT_UNKNOWN or the dimensions of pSrcRect are invalid
1869  *
1870  * NOTES
1871  *   pSrcRect specifies the dimensions of the source data;
1872  *   negative values for pSrcRect are allowed as we're only looking at the width and height anyway.
1873  *
1874  */
1875 HRESULT WINAPI D3DXLoadSurfaceFromMemory(IDirect3DSurface9 *dst_surface,
1876         const PALETTEENTRY *dst_palette, const RECT *dst_rect, const void *src_memory,
1877         D3DFORMAT src_format, UINT src_pitch, const PALETTEENTRY *src_palette, const RECT *src_rect,
1878         DWORD filter, D3DCOLOR color_key)
1879 {
1880     const struct pixel_format_desc *srcformatdesc, *destformatdesc;
1881     D3DSURFACE_DESC surfdesc;
1882     D3DLOCKED_RECT lockrect;
1883     struct volume src_size, dst_size;
1884     HRESULT ret = D3D_OK;
1885 
1886     TRACE("(%p, %p, %s, %p, %#x, %u, %p, %s, %#x, 0x%08x)\n",
1887             dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), src_memory, src_format,
1888             src_pitch, src_palette, wine_dbgstr_rect(src_rect), filter, color_key);
1889 
1890     if (!dst_surface || !src_memory || !src_rect)
1891     {
1892         WARN("Invalid argument specified.\n");
1893         return D3DERR_INVALIDCALL;
1894     }
1895     if (src_format == D3DFMT_UNKNOWN
1896             || src_rect->left >= src_rect->right
1897             || src_rect->top >= src_rect->bottom)
1898     {
1899         WARN("Invalid src_format or src_rect.\n");
1900         return E_FAIL;
1901     }
1902 
1903     if (filter == D3DX_DEFAULT)
1904         filter = D3DX_FILTER_TRIANGLE | D3DX_FILTER_DITHER;
1905 
1906     IDirect3DSurface9_GetDesc(dst_surface, &surfdesc);
1907 
1908     src_size.width = src_rect->right - src_rect->left;
1909     src_size.height = src_rect->bottom - src_rect->top;
1910     src_size.depth = 1;
1911     if (!dst_rect)
1912     {
1913         dst_size.width = surfdesc.Width;
1914         dst_size.height = surfdesc.Height;
1915     }
1916     else
1917     {
1918         if (dst_rect->left > dst_rect->right || dst_rect->right > surfdesc.Width
1919                 || dst_rect->top > dst_rect->bottom || dst_rect->bottom > surfdesc.Height
1920                 || dst_rect->left < 0 || dst_rect->top < 0)
1921         {
1922             WARN("Invalid dst_rect specified.\n");
1923             return D3DERR_INVALIDCALL;
1924         }
1925         dst_size.width = dst_rect->right - dst_rect->left;
1926         dst_size.height = dst_rect->bottom - dst_rect->top;
1927         if (!dst_size.width || !dst_size.height)
1928             return D3D_OK;
1929     }
1930     dst_size.depth = 1;
1931 
1932     srcformatdesc = get_format_info(src_format);
1933     destformatdesc = get_format_info(surfdesc.Format);
1934     if (srcformatdesc->type == FORMAT_UNKNOWN || destformatdesc->type == FORMAT_UNKNOWN)
1935     {
1936         FIXME("Unsupported pixel format conversion %#x -> %#x\n", src_format, surfdesc.Format);
1937         return E_NOTIMPL;
1938     }
1939 
1940     if (src_format == surfdesc.Format
1941             && dst_size.width == src_size.width
1942             && dst_size.height == src_size.height
1943             && color_key == 0) /* Simple copy. */
1944     {
1945         if (src_rect->left & (srcformatdesc->block_width - 1)
1946                 || src_rect->top & (srcformatdesc->block_height - 1)
1947                 || (src_rect->right & (srcformatdesc->block_width - 1)
1948                     && src_size.width != surfdesc.Width)
1949                 || (src_rect->bottom & (srcformatdesc->block_height - 1)
1950                     && src_size.height != surfdesc.Height))
1951         {
1952             WARN("Source rect %s is misaligned.\n", wine_dbgstr_rect(src_rect));
1953             return D3DXERR_INVALIDDATA;
1954         }
1955 
1956         if (FAILED(IDirect3DSurface9_LockRect(dst_surface, &lockrect, dst_rect, 0)))
1957             return D3DXERR_INVALIDDATA;
1958 
1959         copy_pixels(src_memory, src_pitch, 0, lockrect.pBits, lockrect.Pitch, 0,
1960                 &src_size, srcformatdesc);
1961 
1962         IDirect3DSurface9_UnlockRect(dst_surface);
1963     }
1964     else /* Stretching or format conversion. */
1965     {
1966         dxtn_conversion_func pre_convert, post_convert;
1967         void *tmp_src_memory = NULL, *tmp_dst_memory = NULL;
1968         UINT tmp_src_pitch, tmp_dst_pitch;
1969 
1970         pre_convert  = get_dxtn_conversion_func(srcformatdesc->format, FALSE);
1971         post_convert = get_dxtn_conversion_func(destformatdesc->format, TRUE);
1972 
1973         if ((!pre_convert && !is_conversion_from_supported(srcformatdesc)) ||
1974                 (!post_convert && !is_conversion_to_supported(destformatdesc)))
1975         {
1976             FIXME("Unsupported format conversion %#x -> %#x.\n", src_format, surfdesc.Format);
1977             return E_NOTIMPL;
1978         }
1979 
1980         if (FAILED(IDirect3DSurface9_LockRect(dst_surface, &lockrect, dst_rect, 0)))
1981             return D3DXERR_INVALIDDATA;
1982 
1983         /* handle pre-conversion */
1984         if (pre_convert)
1985         {
1986             tmp_src_memory = HeapAlloc(GetProcessHeap(), 0, src_size.width * src_size.height * sizeof(DWORD));
1987             if (!tmp_src_memory)
1988             {
1989                 ret = E_OUTOFMEMORY;
1990                 goto error;
1991             }
1992             tmp_src_pitch = src_size.width * sizeof(DWORD);
1993             if (!pre_convert(src_memory, tmp_src_memory, src_pitch, tmp_src_pitch,
1994                     WINED3DFMT_B8G8R8A8_UNORM, src_size.width, src_size.height))
1995             {
1996                 ret = E_FAIL;
1997                 goto error;
1998             }
1999             srcformatdesc = get_format_info(D3DFMT_A8R8G8B8);
2000         }
2001         else
2002         {
2003             tmp_src_memory = (void *)src_memory;
2004             tmp_src_pitch  = src_pitch;
2005         }
2006 
2007         /* handle post-conversion */
2008         if (post_convert)
2009         {
2010             tmp_dst_memory = HeapAlloc(GetProcessHeap(), 0, dst_size.width * dst_size.height * sizeof(DWORD));
2011             if (!tmp_dst_memory)
2012             {
2013                 ret = E_OUTOFMEMORY;
2014                 goto error;
2015             }
2016             tmp_dst_pitch = dst_size.width * sizeof(DWORD);
2017             destformatdesc = get_format_info(D3DFMT_A8R8G8B8);
2018         }
2019         else
2020         {
2021             tmp_dst_memory = lockrect.pBits;
2022             tmp_dst_pitch  = lockrect.Pitch;
2023         }
2024 
2025         if ((filter & 0xf) == D3DX_FILTER_NONE)
2026         {
2027             convert_argb_pixels(tmp_src_memory, tmp_src_pitch, 0, &src_size, srcformatdesc,
2028                     tmp_dst_memory, tmp_dst_pitch, 0, &dst_size, destformatdesc, color_key, src_palette);
2029         }
2030         else /* if ((filter & 0xf) == D3DX_FILTER_POINT) */
2031         {
2032             if ((filter & 0xf) != D3DX_FILTER_POINT)
2033                 FIXME("Unhandled filter %#x.\n", filter);
2034 
2035             /* Always apply a point filter until D3DX_FILTER_LINEAR,
2036              * D3DX_FILTER_TRIANGLE and D3DX_FILTER_BOX are implemented. */
2037             point_filter_argb_pixels(tmp_src_memory, tmp_src_pitch, 0, &src_size, srcformatdesc,
2038                     tmp_dst_memory, tmp_dst_pitch, 0, &dst_size, destformatdesc, color_key, src_palette);
2039         }
2040 
2041         /* handle post-conversion */
2042         if (post_convert)
2043         {
2044             if (!post_convert(tmp_dst_memory, lockrect.pBits, tmp_dst_pitch, lockrect.Pitch,
2045                     WINED3DFMT_B8G8R8A8_UNORM, dst_size.width, dst_size.height))
2046             {
2047                 ret = E_FAIL;
2048                 goto error;
2049             }
2050         }
2051 
2052 error:
2053         if (pre_convert)
2054             HeapFree(GetProcessHeap(), 0, tmp_src_memory);
2055         if (post_convert)
2056             HeapFree(GetProcessHeap(), 0, tmp_dst_memory);
2057         IDirect3DSurface9_UnlockRect(dst_surface);
2058     }
2059 
2060     return ret;
2061 }
2062 
2063 /************************************************************
2064  * D3DXLoadSurfaceFromSurface
2065  *
2066  * Copies the contents from one surface to another, performing any required
2067  * format conversion, resizing or filtering.
2068  *
2069  * PARAMS
2070  *   pDestSurface [I] pointer to the destination surface
2071  *   pDestPalette [I] palette to use
2072  *   pDestRect    [I] to be filled area of the surface
2073  *   pSrcSurface  [I] pointer to the source surface
2074  *   pSrcPalette  [I] palette used for the source surface
2075  *   pSrcRect     [I] area of the source data to load
2076  *   dwFilter     [I] filter to apply on resizing
2077  *   Colorkey     [I] any ARGB value or 0 to disable color-keying
2078  *
2079  * RETURNS
2080  *   Success: D3D_OK
2081  *   Failure: D3DERR_INVALIDCALL, if pDestSurface or pSrcSurface is NULL
2082  *            D3DXERR_INVALIDDATA, if one of the surfaces is not lockable
2083  *
2084  */
2085 HRESULT WINAPI D3DXLoadSurfaceFromSurface(IDirect3DSurface9 *dst_surface,
2086         const PALETTEENTRY *dst_palette, const RECT *dst_rect, IDirect3DSurface9 *src_surface,
2087         const PALETTEENTRY *src_palette, const RECT *src_rect, DWORD filter, D3DCOLOR color_key)
2088 {
2089     RECT rect;
2090     D3DLOCKED_RECT lock;
2091     D3DSURFACE_DESC SrcDesc;
2092     HRESULT hr;
2093 
2094     TRACE("dst_surface %p, dst_palette %p, dst_rect %s, src_surface %p, "
2095             "src_palette %p, src_rect %s, filter %#x, color_key 0x%08x.\n",
2096             dst_surface, dst_palette, wine_dbgstr_rect(dst_rect), src_surface,
2097             src_palette, wine_dbgstr_rect(src_rect), filter, color_key);
2098 
2099     if (!dst_surface || !src_surface)
2100         return D3DERR_INVALIDCALL;
2101 
2102     IDirect3DSurface9_GetDesc(src_surface, &SrcDesc);
2103 
2104     if (!src_rect)
2105         SetRect(&rect, 0, 0, SrcDesc.Width, SrcDesc.Height);
2106     else
2107         rect = *src_rect;
2108 
2109     if (FAILED(IDirect3DSurface9_LockRect(src_surface, &lock, NULL, D3DLOCK_READONLY)))
2110         return D3DXERR_INVALIDDATA;
2111 
2112     hr = D3DXLoadSurfaceFromMemory(dst_surface, dst_palette, dst_rect,
2113             lock.pBits, SrcDesc.Format, lock.Pitch, src_palette, &rect, filter, color_key);
2114 
2115     IDirect3DSurface9_UnlockRect(src_surface);
2116 
2117     return hr;
2118 }
2119 
2120 
2121 HRESULT WINAPI D3DXSaveSurfaceToFileA(const char *dst_filename, D3DXIMAGE_FILEFORMAT file_format,
2122         IDirect3DSurface9 *src_surface, const PALETTEENTRY *src_palette, const RECT *src_rect)
2123 {
2124     int len;
2125     WCHAR *filename;
2126     HRESULT hr;
2127     ID3DXBuffer *buffer;
2128 
2129     TRACE("(%s, %#x, %p, %p, %s): relay\n",
2130             wine_dbgstr_a(dst_filename), file_format, src_surface, src_palette, wine_dbgstr_rect(src_rect));
2131 
2132     if (!dst_filename) return D3DERR_INVALIDCALL;
2133 
2134     len = MultiByteToWideChar(CP_ACP, 0, dst_filename, -1, NULL, 0);
2135     filename = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2136     if (!filename) return E_OUTOFMEMORY;
2137     MultiByteToWideChar(CP_ACP, 0, dst_filename, -1, filename, len);
2138 
2139     hr = D3DXSaveSurfaceToFileInMemory(&buffer, file_format, src_surface, src_palette, src_rect);
2140     if (SUCCEEDED(hr))
2141     {
2142         hr = write_buffer_to_file(filename, buffer);
2143         ID3DXBuffer_Release(buffer);
2144     }
2145 
2146     HeapFree(GetProcessHeap(), 0, filename);
2147     return hr;
2148 }
2149 
2150 HRESULT WINAPI D3DXSaveSurfaceToFileW(const WCHAR *dst_filename, D3DXIMAGE_FILEFORMAT file_format,
2151         IDirect3DSurface9 *src_surface, const PALETTEENTRY *src_palette, const RECT *src_rect)
2152 {
2153     HRESULT hr;
2154     ID3DXBuffer *buffer;
2155 
2156     TRACE("(%s, %#x, %p, %p, %s): relay\n",
2157         wine_dbgstr_w(dst_filename), file_format, src_surface, src_palette, wine_dbgstr_rect(src_rect));
2158 
2159     if (!dst_filename) return D3DERR_INVALIDCALL;
2160 
2161     hr = D3DXSaveSurfaceToFileInMemory(&buffer, file_format, src_surface, src_palette, src_rect);
2162     if (SUCCEEDED(hr))
2163     {
2164         hr = write_buffer_to_file(dst_filename, buffer);
2165         ID3DXBuffer_Release(buffer);
2166     }
2167 
2168     return hr;
2169 }
2170 
2171 HRESULT WINAPI D3DXSaveSurfaceToFileInMemory(ID3DXBuffer **dst_buffer, D3DXIMAGE_FILEFORMAT file_format,
2172         IDirect3DSurface9 *src_surface, const PALETTEENTRY *src_palette, const RECT *src_rect)
2173 {
2174     IWICBitmapEncoder *encoder = NULL;
2175     IWICBitmapFrameEncode *frame = NULL;
2176     IPropertyBag2 *encoder_options = NULL;
2177     IStream *stream = NULL;
2178     HRESULT hr;
2179     HRESULT initresult;
2180     const CLSID *encoder_clsid;
2181     const GUID *pixel_format_guid;
2182     WICPixelFormatGUID wic_pixel_format;
2183     D3DFORMAT d3d_pixel_format;
2184     D3DSURFACE_DESC src_surface_desc;
2185     D3DLOCKED_RECT locked_rect;
2186     int width, height;
2187     STATSTG stream_stats;
2188     HGLOBAL stream_hglobal;
2189     ID3DXBuffer *buffer;
2190     DWORD size;
2191 
2192     TRACE("(%p, %#x, %p, %p, %s)\n",
2193         dst_buffer, file_format, src_surface, src_palette, wine_dbgstr_rect(src_rect));
2194 
2195     if (!dst_buffer || !src_surface) return D3DERR_INVALIDCALL;
2196 
2197     if (src_palette)
2198     {
2199         FIXME("Saving surfaces with palettized pixel formats is not implemented yet\n");
2200         return D3DERR_INVALIDCALL;
2201     }
2202 
2203     switch (file_format)
2204     {
2205         case D3DXIFF_BMP:
2206         case D3DXIFF_DIB:
2207             encoder_clsid = &CLSID_WICBmpEncoder;
2208             break;
2209         case D3DXIFF_PNG:
2210             encoder_clsid = &CLSID_WICPngEncoder;
2211             break;
2212         case D3DXIFF_JPG:
2213             encoder_clsid = &CLSID_WICJpegEncoder;
2214             break;
2215         case D3DXIFF_DDS:
2216             return save_dds_surface_to_memory(dst_buffer, src_surface, src_rect);
2217         case D3DXIFF_HDR:
2218         case D3DXIFF_PFM:
2219         case D3DXIFF_TGA:
2220         case D3DXIFF_PPM:
2221             FIXME("File format %#x is not supported yet\n", file_format);
2222             return E_NOTIMPL;
2223         default:
2224             return D3DERR_INVALIDCALL;
2225     }
2226 
2227     IDirect3DSurface9_GetDesc(src_surface, &src_surface_desc);
2228     if (src_rect)
2229     {
2230         if (src_rect->left == src_rect->right || src_rect->top == src_rect->bottom)
2231         {
2232             WARN("Invalid rectangle with 0 area\n");
2233             return D3DXCreateBuffer(64, dst_buffer);
2234         }
2235         if (src_rect->left < 0 || src_rect->top < 0)
2236             return D3DERR_INVALIDCALL;
2237         if (src_rect->left > src_rect->right || src_rect->top > src_rect->bottom)
2238             return D3DERR_INVALIDCALL;
2239         if (src_rect->right > src_surface_desc.Width || src_rect->bottom > src_surface_desc.Height)
2240             return D3DERR_INVALIDCALL;
2241 
2242         width = src_rect->right - src_rect->left;
2243         height = src_rect->bottom - src_rect->top;
2244     }
2245     else
2246     {
2247         width = src_surface_desc.Width;
2248         height = src_surface_desc.Height;
2249     }
2250 
2251     initresult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
2252 
2253     hr = CoCreateInstance(encoder_clsid, NULL, CLSCTX_INPROC_SERVER,
2254         &IID_IWICBitmapEncoder, (void **)&encoder);
2255     if (FAILED(hr)) goto cleanup_err;
2256 
2257     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
2258     if (FAILED(hr)) goto cleanup_err;
2259 
2260     hr = IWICBitmapEncoder_Initialize(encoder, stream, WICBitmapEncoderNoCache);
2261     if (FAILED(hr)) goto cleanup_err;
2262 
2263     hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frame, &encoder_options);
2264     if (FAILED(hr)) goto cleanup_err;
2265 
2266     hr = IWICBitmapFrameEncode_Initialize(frame, encoder_options);
2267     if (FAILED(hr)) goto cleanup_err;
2268 
2269     hr = IWICBitmapFrameEncode_SetSize(frame, width, height);
2270     if (FAILED(hr)) goto cleanup_err;
2271 
2272     pixel_format_guid = d3dformat_to_wic_guid(src_surface_desc.Format);
2273     if (!pixel_format_guid)
2274     {
2275         FIXME("Pixel format %#x is not supported yet\n", src_surface_desc.Format);
2276         hr = E_NOTIMPL;
2277         goto cleanup;
2278     }
2279 
2280     memcpy(&wic_pixel_format, pixel_format_guid, sizeof(GUID));
2281     hr = IWICBitmapFrameEncode_SetPixelFormat(frame, &wic_pixel_format);
2282     d3d_pixel_format = wic_guid_to_d3dformat(&wic_pixel_format);
2283     if (SUCCEEDED(hr) && d3d_pixel_format != D3DFMT_UNKNOWN)
2284     {
2285         TRACE("Using pixel format %s %#x\n", debugstr_guid(&wic_pixel_format), d3d_pixel_format);
2286 
2287         if (src_surface_desc.Format == d3d_pixel_format) /* Simple copy */
2288         {
2289             hr = IDirect3DSurface9_LockRect(src_surface, &locked_rect, src_rect, D3DLOCK_READONLY);
2290             if (SUCCEEDED(hr))
2291             {
2292                 IWICBitmapFrameEncode_WritePixels(frame, height,
2293                     locked_rect.Pitch, height * locked_rect.Pitch, locked_rect.pBits);
2294                 IDirect3DSurface9_UnlockRect(src_surface);
2295             }
2296         }
2297         else /* Pixel format conversion */
2298         {
2299             const struct pixel_format_desc *src_format_desc, *dst_format_desc;
2300             struct volume size;
2301             DWORD dst_pitch;
2302             void *dst_data;
2303 
2304             src_format_desc = get_format_info(src_surface_desc.Format);
2305             dst_format_desc = get_format_info(d3d_pixel_format);
2306             if (!is_conversion_from_supported(src_format_desc)
2307                     || !is_conversion_to_supported(dst_format_desc))
2308             {
2309                 FIXME("Unsupported format conversion %#x -> %#x.\n",
2310                     src_surface_desc.Format, d3d_pixel_format);
2311                 hr = E_NOTIMPL;
2312                 goto cleanup;
2313             }
2314 
2315             size.width = width;
2316             size.height = height;
2317             size.depth = 1;
2318             dst_pitch = width * dst_format_desc->bytes_per_pixel;
2319             dst_data = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height);
2320             if (!dst_data)
2321             {
2322                 hr = E_OUTOFMEMORY;
2323                 goto cleanup;
2324             }
2325 
2326             hr = IDirect3DSurface9_LockRect(src_surface, &locked_rect, src_rect, D3DLOCK_READONLY);
2327             if (SUCCEEDED(hr))
2328             {
2329                 convert_argb_pixels(locked_rect.pBits, locked_rect.Pitch, 0, &size, src_format_desc,
2330                     dst_data, dst_pitch, 0, &size, dst_format_desc, 0, NULL);
2331                 IDirect3DSurface9_UnlockRect(src_surface);
2332             }
2333 
2334             IWICBitmapFrameEncode_WritePixels(frame, height, dst_pitch, dst_pitch * height, dst_data);
2335             HeapFree(GetProcessHeap(), 0, dst_data);
2336         }
2337 
2338         hr = IWICBitmapFrameEncode_Commit(frame);
2339         if (SUCCEEDED(hr)) hr = IWICBitmapEncoder_Commit(encoder);
2340     }
2341     else WARN("Unsupported pixel format %#x\n", src_surface_desc.Format);
2342 
2343     /* copy data from stream to ID3DXBuffer */
2344     hr = IStream_Stat(stream, &stream_stats, STATFLAG_NONAME);
2345     if (FAILED(hr)) goto cleanup_err;
2346 
2347     if (stream_stats.cbSize.u.HighPart != 0)
2348     {
2349         hr = D3DXERR_INVALIDDATA;
2350         goto cleanup;
2351     }
2352     size = stream_stats.cbSize.u.LowPart;
2353 
2354     /* Remove BMP header for DIB */
2355     if (file_format == D3DXIFF_DIB)
2356         size -= sizeof(BITMAPFILEHEADER);
2357 
2358     hr = D3DXCreateBuffer(size, &buffer);
2359     if (FAILED(hr)) goto cleanup;
2360 
2361     hr = GetHGlobalFromStream(stream, &stream_hglobal);
2362     if (SUCCEEDED(hr))
2363     {
2364         void *buffer_pointer = ID3DXBuffer_GetBufferPointer(buffer);
2365         void *stream_data = GlobalLock(stream_hglobal);
2366         /* Remove BMP header for DIB */
2367         if (file_format == D3DXIFF_DIB)
2368             stream_data = (void*)((BYTE*)stream_data + sizeof(BITMAPFILEHEADER));
2369         memcpy(buffer_pointer, stream_data, size);
2370         GlobalUnlock(stream_hglobal);
2371         *dst_buffer = buffer;
2372     }
2373     else ID3DXBuffer_Release(buffer);
2374 
2375 cleanup_err:
2376     if (FAILED(hr) && hr != E_OUTOFMEMORY)
2377         hr = D3DERR_INVALIDCALL;
2378 
2379 cleanup:
2380     if (stream) IStream_Release(stream);
2381 
2382     if (frame) IWICBitmapFrameEncode_Release(frame);
2383     if (encoder_options) IPropertyBag2_Release(encoder_options);
2384 
2385     if (encoder) IWICBitmapEncoder_Release(encoder);
2386 
2387     if (SUCCEEDED(initresult)) CoUninitialize();
2388 
2389     return hr;
2390 }
2391