gdiplus: Implement GdipGetRegionScans.
[wine] / dlls / d3dx9_36 / surface.c
1 /*
2  * Copyright (C) 2009-2010 Tony Wasserka
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  */
19
20 #include "wine/debug.h"
21 #include "wine/unicode.h"
22 #include "d3dx9_36_private.h"
23
24 #include "initguid.h"
25 #include "wincodec.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
28
29
30 /* Wine-specific WIC GUIDs */
31
32 DEFINE_GUID(GUID_WineContainerFormatTga, 0x0c44fda1,0xa5c5,0x4298,0x96,0x85,0x47,0x3f,0xc1,0x7c,0xd3,0x22);
33
34 /************************************************************
35  * D3DXGetImageInfoFromFileInMemory
36  *
37  * Fills a D3DXIMAGE_INFO structure with info about an image
38  *
39  * PARAMS
40  *   data     [I] pointer to the image file data
41  *   datasize [I] size of the passed data
42  *   info     [O] pointer to the destination structure
43  *
44  * RETURNS
45  *   Success: D3D_OK, if info is not NULL and data and datasize make up a valid image file or
46  *                    if info is NULL and data and datasize are not NULL
47  *   Failure: D3DXERR_INVALIDDATA, if data is no valid image file and datasize and info are not NULL
48  *            D3DERR_INVALIDCALL, if data is NULL or
49  *                                if datasize is 0
50  *
51  * NOTES
52  *   datasize may be bigger than the actual file size
53  *
54  */
55 HRESULT WINAPI D3DXGetImageInfoFromFileInMemory(LPCVOID data, UINT datasize, D3DXIMAGE_INFO *info)
56 {
57     IWICImagingFactory *factory;
58     IWICBitmapDecoder *decoder = NULL;
59     IWICStream *stream;
60     HRESULT hr;
61     HRESULT initresult;
62
63     FIXME("(%p, %d, %p): partially implemented\n", data, datasize, info);
64
65     /* TODO: Add support for (or at least detect) TGA, DDS, PPM and DIB */
66
67     if (!data || !datasize)
68         return D3DERR_INVALIDCALL;
69
70     if (!info)
71         return D3D_OK;
72
73     initresult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
74
75     hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, (void**)&factory);
76
77     if (SUCCEEDED(hr)) {
78         IWICImagingFactory_CreateStream(factory, &stream);
79         IWICStream_InitializeFromMemory(stream, (BYTE*)data, datasize);
80         hr = IWICImagingFactory_CreateDecoderFromStream(factory, (IStream*)stream, NULL, 0, &decoder);
81         IStream_Release(stream);
82         IWICImagingFactory_Release(factory);
83     }
84
85     if (SUCCEEDED(hr)) {
86         GUID container_format;
87         UINT frame_count;
88
89         hr = IWICBitmapDecoder_GetContainerFormat(decoder, &container_format);
90         if (SUCCEEDED(hr)) {
91             if (IsEqualGUID(&container_format, &GUID_ContainerFormatBmp)) {
92                 TRACE("File type is BMP\n");
93                 info->ImageFileFormat = D3DXIFF_BMP;
94             } else if (IsEqualGUID(&container_format, &GUID_ContainerFormatPng)) {
95                 TRACE("File type is PNG\n");
96                 info->ImageFileFormat = D3DXIFF_PNG;
97             } else if(IsEqualGUID(&container_format, &GUID_ContainerFormatJpeg)) {
98                 TRACE("File type is JPG\n");
99                 info->ImageFileFormat = D3DXIFF_JPG;
100             } else if(IsEqualGUID(&container_format, &GUID_WineContainerFormatTga)) {
101                 TRACE("File type is TGA\n");
102                 info->ImageFileFormat = D3DXIFF_TGA;
103             } else {
104                 WARN("Unsupported image file format %s\n", debugstr_guid(&container_format));
105                 hr = D3DXERR_INVALIDDATA;
106             }
107         }
108
109         if (SUCCEEDED(hr))
110             hr = IWICBitmapDecoder_GetFrameCount(decoder, &frame_count);
111         if (SUCCEEDED(hr) && !frame_count)
112             hr = D3DXERR_INVALIDDATA;
113
114         if (SUCCEEDED(hr)) {
115             IWICBitmapFrameDecode *frame = NULL;
116
117             hr = IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
118
119             if (SUCCEEDED(hr))
120                 hr = IWICBitmapFrameDecode_GetSize(frame, &info->Width, &info->Height);
121
122             if (SUCCEEDED(hr)) {
123                 WICPixelFormatGUID pixel_format;
124
125                 hr = IWICBitmapFrameDecode_GetPixelFormat(frame, &pixel_format);
126                 if (SUCCEEDED(hr)) {
127                     if (IsEqualGUID(&pixel_format, &GUID_WICPixelFormat1bppIndexed))
128                         info->Format = D3DFMT_L8;
129                     else if (IsEqualGUID(&pixel_format, &GUID_WICPixelFormat4bppIndexed))
130                         info->Format = D3DFMT_L8;
131                     else if (IsEqualGUID(&pixel_format, &GUID_WICPixelFormat8bppIndexed))
132                         info->Format = D3DFMT_L8;
133                     else if (IsEqualGUID(&pixel_format, &GUID_WICPixelFormat16bppBGR555))
134                         info->Format = D3DFMT_X1R5G5B5;
135                     else if (IsEqualGUID(&pixel_format, &GUID_WICPixelFormat24bppBGR))
136                         info->Format = D3DFMT_R8G8B8;
137                     else if (IsEqualGUID(&pixel_format, &GUID_WICPixelFormat32bppBGR))
138                         info->Format = D3DFMT_X8R8G8B8;
139                     else if (IsEqualGUID(&pixel_format, &GUID_WICPixelFormat32bppBGRA))
140                         info->Format = D3DFMT_A8R8G8B8;
141                     else {
142                         WARN("Unsupported pixel format %s\n", debugstr_guid(&pixel_format));
143                         hr = D3DXERR_INVALIDDATA;
144                     }
145                 }
146             }
147
148             if (frame)
149                  IWICBitmapFrameDecode_Release(frame);
150
151             info->Depth = 1;
152             info->MipLevels = 1;
153             info->ResourceType = D3DRTYPE_TEXTURE;
154         }
155     }
156
157     if (decoder)
158         IWICBitmapDecoder_Release(decoder);
159
160     if (SUCCEEDED(initresult))
161         CoUninitialize();
162
163     if (FAILED(hr)) {
164         /* Missing formats are not detected yet and will fail silently without the FIXME */
165         FIXME("Invalid or unsupported image file\n");
166         return D3DXERR_INVALIDDATA;
167     }
168
169     return D3D_OK;
170 }
171
172 /************************************************************
173  * D3DXGetImageInfoFromFile
174  *
175  * RETURNS
176  *   Success: D3D_OK, if we successfully load a valid image file or
177  *                    if we successfully load a file which is no valid image and info is NULL
178  *   Failure: D3DXERR_INVALIDDATA, if we fail to load file or
179  *                                 if file is not a valid image file and info is not NULL
180  *            D3DERR_INVALIDCALL, if file is NULL
181  *
182  */
183 HRESULT WINAPI D3DXGetImageInfoFromFileA(LPCSTR file, D3DXIMAGE_INFO *info)
184 {
185     LPWSTR widename;
186     HRESULT hr;
187     int strlength;
188
189     TRACE("(%s, %p): relay\n", debugstr_a(file), info);
190
191     if( !file ) return D3DERR_INVALIDCALL;
192
193     strlength = MultiByteToWideChar(CP_ACP, 0, file, -1, NULL, 0);
194     widename = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlength * sizeof(WCHAR));
195     MultiByteToWideChar(CP_ACP, 0, file, -1, widename, strlength);
196
197     hr = D3DXGetImageInfoFromFileW(widename, info);
198     HeapFree(GetProcessHeap(), 0, widename);
199
200     return hr;
201 }
202
203 HRESULT WINAPI D3DXGetImageInfoFromFileW(LPCWSTR file, D3DXIMAGE_INFO *info)
204 {
205     HRESULT hr;
206     DWORD size;
207     LPVOID buffer;
208
209     TRACE("(%s, %p): relay\n", debugstr_w(file), info);
210
211     if( !file ) return D3DERR_INVALIDCALL;
212
213     hr = map_view_of_file(file, &buffer, &size);
214     if(FAILED(hr)) return D3DXERR_INVALIDDATA;
215
216     hr = D3DXGetImageInfoFromFileInMemory(buffer, size, info);
217     UnmapViewOfFile(buffer);
218
219     return hr;
220 }
221
222 /************************************************************
223  * D3DXGetImageInfoFromResource
224  *
225  * RETURNS
226  *   Success: D3D_OK, if resource is a valid image file
227  *   Failure: D3DXERR_INVALIDDATA, if resource is no valid image file or NULL or
228  *                                 if we fail to load resource
229  *
230  */
231 HRESULT WINAPI D3DXGetImageInfoFromResourceA(HMODULE module, LPCSTR resource, D3DXIMAGE_INFO *info)
232 {
233     HRSRC resinfo;
234
235     TRACE("(%p, %s, %p)\n", module, debugstr_a(resource), info);
236
237     resinfo = FindResourceA(module, resource, (LPCSTR)RT_RCDATA);
238     if(resinfo) {
239         LPVOID buffer;
240         HRESULT hr;
241         DWORD size;
242
243         hr = load_resource_into_memory(module, resinfo, &buffer, &size);
244         if(FAILED(hr)) return D3DXERR_INVALIDDATA;
245         return D3DXGetImageInfoFromFileInMemory(buffer, size, info);
246     }
247
248     resinfo = FindResourceA(module, resource, (LPCSTR)RT_BITMAP);
249     if(resinfo) {
250         FIXME("Implement loading bitmaps from resource type RT_BITMAP\n");
251         return E_NOTIMPL;
252     }
253     return D3DXERR_INVALIDDATA;
254 }
255
256 HRESULT WINAPI D3DXGetImageInfoFromResourceW(HMODULE module, LPCWSTR resource, D3DXIMAGE_INFO *info)
257 {
258     HRSRC resinfo;
259
260     TRACE("(%p, %s, %p)\n", module, debugstr_w(resource), info);
261
262     resinfo = FindResourceW(module, resource, (LPCWSTR)RT_RCDATA);
263     if(resinfo) {
264         LPVOID buffer;
265         HRESULT hr;
266         DWORD size;
267
268         hr = load_resource_into_memory(module, resinfo, &buffer, &size);
269         if(FAILED(hr)) return D3DXERR_INVALIDDATA;
270         return D3DXGetImageInfoFromFileInMemory(buffer, size, info);
271     }
272
273     resinfo = FindResourceW(module, resource, (LPCWSTR)RT_BITMAP);
274     if(resinfo) {
275         FIXME("Implement loading bitmaps from resource type RT_BITMAP\n");
276         return E_NOTIMPL;
277     }
278     return D3DXERR_INVALIDDATA;
279 }
280
281 /************************************************************
282  * D3DXLoadSurfaceFromFileInMemory
283  *
284  * Loads data from a given buffer into a surface and fills a given
285  * D3DXIMAGE_INFO structure with info about the source data.
286  *
287  * PARAMS
288  *   pDestSurface [I] pointer to the surface
289  *   pDestPalette [I] palette to use
290  *   pDestRect    [I] to be filled area of the surface
291  *   pSrcData     [I] pointer to the source data
292  *   SrcDataSize  [I] size of the source data in bytes
293  *   pSrcRect     [I] area of the source data to load
294  *   dwFilter     [I] filter to apply on stretching
295  *   Colorkey     [I] colorkey
296  *   pSrcInfo     [O] pointer to a D3DXIMAGE_INFO structure
297  *
298  * RETURNS
299  *   Success: D3D_OK
300  *   Failure: D3DERR_INVALIDCALL, if pDestSurface or pSrcData or SrcDataSize are NULL
301  *            D3DXERR_INVALIDDATA, if pSrcData is no valid image file
302  *
303  */
304 HRESULT WINAPI D3DXLoadSurfaceFromFileInMemory(LPDIRECT3DSURFACE9 pDestSurface,
305                                                CONST PALETTEENTRY *pDestPalette,
306                                                CONST RECT *pDestRect,
307                                                LPCVOID pSrcData,
308                                                UINT SrcDataSize,
309                                                CONST RECT *pSrcRect,
310                                                DWORD dwFilter,
311                                                D3DCOLOR Colorkey,
312                                                D3DXIMAGE_INFO *pSrcInfo)
313 {
314     D3DXIMAGE_INFO imginfo;
315     HRESULT hr;
316
317     TRACE("(%p, %p, %p, %p, %d, %p, %d, %x, %p)\n", pDestSurface, pDestPalette, pDestRect, pSrcData,
318         SrcDataSize, pSrcRect, dwFilter, Colorkey, pSrcInfo);
319
320     if (!pDestSurface || !pSrcData | !SrcDataSize)
321         return D3DERR_INVALIDCALL;
322
323     hr = D3DXGetImageInfoFromFileInMemory(pSrcData, SrcDataSize, &imginfo);
324
325     if (FAILED(hr))
326         return hr;
327
328     switch (imginfo.ImageFileFormat)
329     {
330         case D3DXIFF_BMP:
331         case D3DXIFF_PNG:
332         case D3DXIFF_JPG:
333         {
334             IWICImagingFactory *factory;
335             IWICBitmapDecoder *decoder;
336             IWICBitmapFrameDecode *bitmapframe;
337             IWICStream *stream;
338
339             const PixelFormatDesc *formatdesc;
340             WICRect wicrect;
341             RECT rect;
342
343             CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
344
345             if (FAILED(CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, (void**)&factory)))
346                 goto cleanup_err;
347
348             if (FAILED(IWICImagingFactory_CreateStream(factory, &stream)))
349             {
350                 IWICImagingFactory_Release(factory);
351                 goto cleanup_err;
352             }
353
354             IWICStream_InitializeFromMemory(stream, (BYTE*)pSrcData, SrcDataSize);
355
356             hr = IWICImagingFactory_CreateDecoderFromStream(factory, (IStream*)stream, NULL, 0, &decoder);
357
358             IStream_Release(stream);
359             IWICImagingFactory_Release(factory);
360
361             if (FAILED(hr))
362                 goto cleanup_err;
363
364             hr = IWICBitmapDecoder_GetFrame(decoder, 0, &bitmapframe);
365
366             if (FAILED(hr))
367                 goto cleanup_bmp;
368
369             if (pSrcRect)
370             {
371                 wicrect.X = pSrcRect->left;
372                 wicrect.Y = pSrcRect->top;
373                 wicrect.Width = pSrcRect->right - pSrcRect->left;
374                 wicrect.Height = pSrcRect->bottom - pSrcRect->top;
375             }
376             else
377             {
378                 wicrect.X = 0;
379                 wicrect.Y = 0;
380                 wicrect.Width = imginfo.Width;
381                 wicrect.Height = imginfo.Height;
382             }
383
384             SetRect(&rect, 0, 0, wicrect.Width, wicrect.Height);
385
386             formatdesc = get_format_info(imginfo.Format);
387
388             if (formatdesc->format == D3DFMT_UNKNOWN)
389             {
390                 FIXME("Unsupported pixel format\n");
391                 hr = D3DXERR_INVALIDDATA;
392             }
393             else
394             {
395                 BYTE *buffer;
396                 DWORD pitch;
397
398                 pitch = formatdesc->bytes_per_pixel * wicrect.Width;
399                 buffer = HeapAlloc(GetProcessHeap(), 0, pitch * wicrect.Height);
400
401                 hr = IWICBitmapFrameDecode_CopyPixels(bitmapframe, &wicrect, pitch,
402                                                       pitch * wicrect.Height, buffer);
403
404                 if (SUCCEEDED(hr))
405                 {
406                     hr = D3DXLoadSurfaceFromMemory(pDestSurface, pDestPalette, pDestRect,
407                                                    buffer, imginfo.Format, pitch,
408                                                    NULL, &rect, dwFilter, Colorkey);
409                 }
410
411                 HeapFree(GetProcessHeap(), 0, buffer);
412             }
413
414 cleanup_bmp:
415             IWICBitmapFrameDecode_Release(bitmapframe);
416             IWICBitmapDecoder_Release(decoder);
417
418 cleanup_err:
419             CoUninitialize();
420
421             if (FAILED(hr))
422                 return D3DXERR_INVALIDDATA;
423
424             break;
425         }
426
427         default:
428             FIXME("Unsupported image file format\n");
429             return E_NOTIMPL;
430     }
431
432     if (pSrcInfo)
433         *pSrcInfo = imginfo;
434
435     return D3D_OK;
436 }
437
438 /************************************************************
439  * D3DXLoadSurfaceFromFile
440  */
441 HRESULT WINAPI D3DXLoadSurfaceFromFileA(LPDIRECT3DSURFACE9 pDestSurface,
442                                         CONST PALETTEENTRY *pDestPalette,
443                                         CONST RECT *pDestRect,
444                                         LPCSTR pSrcFile,
445                                         CONST RECT *pSrcRect,
446                                         DWORD dwFilter,
447                                         D3DCOLOR Colorkey,
448                                         D3DXIMAGE_INFO *pSrcInfo)
449 {
450     LPWSTR pWidename;
451     HRESULT hr;
452     int strlength;
453
454     TRACE("(%p, %p, %p, %s, %p, %u, %#x, %p): relay\n", pDestSurface, pDestPalette, pDestRect, debugstr_a(pSrcFile),
455         pSrcRect, dwFilter, Colorkey, pSrcInfo);
456
457     if( !pSrcFile || !pDestSurface ) return D3DERR_INVALIDCALL;
458
459     strlength = MultiByteToWideChar(CP_ACP, 0, pSrcFile, -1, NULL, 0);
460     pWidename = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlength * sizeof(WCHAR));
461     MultiByteToWideChar(CP_ACP, 0, pSrcFile, -1, pWidename, strlength);
462
463     hr = D3DXLoadSurfaceFromFileW(pDestSurface, pDestPalette, pDestRect, pWidename, pSrcRect, dwFilter, Colorkey, pSrcInfo);
464     HeapFree(GetProcessHeap(), 0, pWidename);
465
466     return hr;
467 }
468
469 HRESULT WINAPI D3DXLoadSurfaceFromFileW(LPDIRECT3DSURFACE9 pDestSurface,
470                                         CONST PALETTEENTRY *pDestPalette,
471                                         CONST RECT *pDestRect,
472                                         LPCWSTR pSrcFile,
473                                         CONST RECT *pSrcRect,
474                                         DWORD Filter,
475                                         D3DCOLOR Colorkey,
476                                         D3DXIMAGE_INFO *pSrcInfo)
477 {
478     HRESULT hr;
479     DWORD dwSize;
480     LPVOID pBuffer;
481
482     TRACE("(%p, %p, %p, %s, %p, %u, %#x, %p): relay\n", pDestSurface, pDestPalette, pDestRect, debugstr_w(pSrcFile),
483         pSrcRect, Filter, Colorkey, pSrcInfo);
484
485     if( !pSrcFile || !pDestSurface ) return D3DERR_INVALIDCALL;
486
487     hr = map_view_of_file(pSrcFile, &pBuffer, &dwSize);
488     if(FAILED(hr)) return D3DXERR_INVALIDDATA;
489
490     hr = D3DXLoadSurfaceFromFileInMemory(pDestSurface, pDestPalette, pDestRect, pBuffer, dwSize, pSrcRect, Filter, Colorkey, pSrcInfo);
491     UnmapViewOfFile(pBuffer);
492
493     return hr;
494 }
495
496 /************************************************************
497  * D3DXLoadSurfaceFromResource
498  */
499 HRESULT WINAPI D3DXLoadSurfaceFromResourceA(LPDIRECT3DSURFACE9 pDestSurface,
500                                             CONST PALETTEENTRY *pDestPalette,
501                                             CONST RECT *pDestRect,
502                                             HMODULE hSrcModule,
503                                             LPCSTR pResource,
504                                             CONST RECT *pSrcRect,
505                                             DWORD dwFilter,
506                                             D3DCOLOR Colorkey,
507                                             D3DXIMAGE_INFO *pSrcInfo)
508 {
509     HRSRC hResInfo;
510
511     TRACE("(%p, %p, %p, %p, %s, %p, %u, %#x, %p): relay\n", pDestSurface, pDestPalette, pDestRect, hSrcModule,
512         debugstr_a(pResource), pSrcRect, dwFilter, Colorkey, pSrcInfo);
513
514     if( !pDestSurface ) return D3DERR_INVALIDCALL;
515
516     hResInfo = FindResourceA(hSrcModule, pResource, (LPCSTR)RT_RCDATA);
517     if(hResInfo) {
518         LPVOID pBuffer;
519         HRESULT hr;
520         DWORD dwSize;
521
522         hr = load_resource_into_memory(hSrcModule, hResInfo, &pBuffer, &dwSize);
523         if(FAILED(hr)) return D3DXERR_INVALIDDATA;
524         return D3DXLoadSurfaceFromFileInMemory(pDestSurface, pDestPalette, pDestRect, pBuffer, dwSize, pSrcRect, dwFilter, Colorkey, pSrcInfo);
525     }
526
527     hResInfo = FindResourceA(hSrcModule, pResource, (LPCSTR)RT_BITMAP);
528     if(hResInfo) {
529         FIXME("Implement loading bitmaps from resource type RT_BITMAP\n");
530         return E_NOTIMPL;
531     }
532     return D3DXERR_INVALIDDATA;
533 }
534
535 HRESULT WINAPI D3DXLoadSurfaceFromResourceW(LPDIRECT3DSURFACE9 pDestSurface,
536                                             CONST PALETTEENTRY *pDestPalette,
537                                             CONST RECT *pDestRect,
538                                             HMODULE hSrcModule,
539                                             LPCWSTR pResource,
540                                             CONST RECT *pSrcRect,
541                                             DWORD dwFilter,
542                                             D3DCOLOR Colorkey,
543                                             D3DXIMAGE_INFO *pSrcInfo)
544 {
545     HRSRC hResInfo;
546
547     TRACE("(%p, %p, %p, %p, %s, %p, %u, %#x, %p): relay\n", pDestSurface, pDestPalette, pDestRect, hSrcModule,
548         debugstr_w(pResource), pSrcRect, dwFilter, Colorkey, pSrcInfo);
549
550     if( !pDestSurface ) return D3DERR_INVALIDCALL;
551
552     hResInfo = FindResourceW(hSrcModule, pResource, (LPCWSTR)RT_RCDATA);
553     if(hResInfo) {
554         LPVOID pBuffer;
555         HRESULT hr;
556         DWORD dwSize;
557
558         hr = load_resource_into_memory(hSrcModule, hResInfo, &pBuffer, &dwSize);
559         if(FAILED(hr)) return D3DXERR_INVALIDDATA;
560         return D3DXLoadSurfaceFromFileInMemory(pDestSurface, pDestPalette, pDestRect, pBuffer, dwSize, pSrcRect, dwFilter, Colorkey, pSrcInfo);
561     }
562
563     hResInfo = FindResourceW(hSrcModule, pResource, (LPCWSTR)RT_BITMAP);
564     if(hResInfo) {
565         FIXME("Implement loading bitmaps from resource type RT_BITMAP\n");
566         return E_NOTIMPL;
567     }
568     return D3DXERR_INVALIDDATA;
569 }
570
571
572 /************************************************************
573  * helper functions for D3DXLoadSurfaceFromMemory
574  */
575 struct argb_conversion_info
576 {
577     CONST PixelFormatDesc *srcformat;
578     CONST PixelFormatDesc *destformat;
579     DWORD srcshift[4], destshift[4];
580     DWORD srcmask[4], destmask[4];
581     BOOL process_channel[4];
582     DWORD channelmask;
583 };
584
585 static void init_argb_conversion_info(CONST PixelFormatDesc *srcformat, CONST PixelFormatDesc *destformat, struct argb_conversion_info *info)
586 {
587     UINT i;
588     ZeroMemory(info->process_channel, 4 * sizeof(BOOL));
589     info->channelmask = 0;
590
591     info->srcformat  =  srcformat;
592     info->destformat = destformat;
593
594     for(i = 0;i < 4;i++) {
595         /* srcshift is used to extract the _relevant_ components */
596         info->srcshift[i]  =  srcformat->shift[i] + max( srcformat->bits[i] - destformat->bits[i], 0);
597
598         /* destshift is used to move the components to the correct position */
599         info->destshift[i] = destformat->shift[i] + max(destformat->bits[i] -  srcformat->bits[i], 0);
600
601         info->srcmask[i]  = ((1 <<  srcformat->bits[i]) - 1) <<  srcformat->shift[i];
602         info->destmask[i] = ((1 << destformat->bits[i]) - 1) << destformat->shift[i];
603
604         /* channelmask specifies bits which aren't used in the source format but in the destination one */
605         if(destformat->bits[i]) {
606             if(srcformat->bits[i]) info->process_channel[i] = TRUE;
607             else info->channelmask |= info->destmask[i];
608         }
609     }
610 }
611
612 /************************************************************
613  * get_relevant_argb_components
614  *
615  * Extracts the relevant components from the source color and
616  * drops the less significant bits if they aren't used by the destination format.
617  */
618 static void get_relevant_argb_components(CONST struct argb_conversion_info *info, CONST DWORD col, DWORD *out)
619 {
620     UINT i = 0;
621     for(;i < 4;i++)
622         if(info->process_channel[i])
623             out[i] = (col & info->srcmask[i]) >> info->srcshift[i];
624 }
625
626 /************************************************************
627  * make_argb_color
628  *
629  * Recombines the output of get_relevant_argb_components and converts
630  * it to the destination format.
631  */
632 static void make_argb_color(CONST struct argb_conversion_info *info, CONST DWORD *in, DWORD *out)
633 {
634     UINT i;
635     *out = 0;
636
637     for(i = 0;i < 4;i++) {
638         if(info->process_channel[i]) {
639             /* necessary to make sure that e.g. an X4R4G4B4 white maps to an R8G8B8 white instead of 0xf0f0f0 */
640             signed int shift;
641             for(shift = info->destshift[i]; shift > info->destformat->shift[i]; shift -= info->srcformat->bits[i]) *out |= in[i] << shift;
642             *out |= (in[i] >> (info->destformat->shift[i] - shift)) << info->destformat->shift[i];
643         }
644     }
645     *out |= info->channelmask;   /* new channels are set to their maximal value */
646 }
647
648 /************************************************************
649  * copy_simple_data
650  *
651  * Copies the source buffer to the destination buffer, performing
652  * any necessary format conversion and color keying.
653  * Pixels outsize the source rect are blacked out.
654  * Works only for ARGB formats with 1 - 4 bytes per pixel.
655  */
656 static void copy_simple_data(CONST BYTE *src,  UINT  srcpitch, POINT  srcsize, CONST PixelFormatDesc  *srcformat,
657                              BYTE *dest, UINT destpitch, POINT destsize, CONST PixelFormatDesc *destformat)
658 {
659     struct argb_conversion_info conv_info;
660     DWORD channels[4];
661     UINT minwidth, minheight;
662     UINT x, y;
663
664     ZeroMemory(channels, sizeof(channels));
665     init_argb_conversion_info(srcformat, destformat, &conv_info);
666
667     minwidth  = (srcsize.x < destsize.x) ? srcsize.x : destsize.x;
668     minheight = (srcsize.y < destsize.y) ? srcsize.y : destsize.y;
669
670     for(y = 0;y < minheight;y++) {
671         const BYTE *srcptr = src + y *  srcpitch;
672         BYTE *destptr = dest + y * destpitch;
673         for(x = 0;x < minwidth;x++) {
674             /* extract source color components */
675             if(srcformat->type == FORMAT_ARGB) get_relevant_argb_components(&conv_info, *(const DWORD*)srcptr, channels);
676
677             /* recombine the components */
678             if(destformat->type == FORMAT_ARGB) make_argb_color(&conv_info, channels, (DWORD*)destptr);
679
680             srcptr  +=  srcformat->bytes_per_pixel;
681             destptr += destformat->bytes_per_pixel;
682         }
683
684         if(srcsize.x < destsize.x) /* black out remaining pixels */
685             ZeroMemory(destptr, destformat->bytes_per_pixel * (destsize.x - srcsize.x));
686     }
687     if(srcsize.y < destsize.y) /* black out remaining pixels */
688         ZeroMemory(dest + srcsize.y * destpitch, destpitch * (destsize.y - srcsize.y));
689 }
690
691 /************************************************************
692  * point_filter_simple_data
693  *
694  * Copies the source buffer to the destination buffer, performing
695  * any necessary format conversion, color keying and stretching
696  * using a point filter.
697  * Works only for ARGB formats with 1 - 4 bytes per pixel.
698  */
699 static void point_filter_simple_data(CONST BYTE *src,  UINT  srcpitch, POINT  srcsize, CONST PixelFormatDesc  *srcformat,
700                                      BYTE *dest, UINT destpitch, POINT destsize, CONST PixelFormatDesc *destformat)
701 {
702     struct argb_conversion_info conv_info;
703     DWORD channels[4];
704
705     UINT x, y;
706
707     ZeroMemory(channels, sizeof(channels));
708     init_argb_conversion_info(srcformat, destformat, &conv_info);
709
710     for(y = 0;y < destsize.y;y++) {
711         BYTE *destptr = dest + y * destpitch;
712         const BYTE *bufptr = src + srcpitch * (y * srcsize.y / destsize.y);
713
714         for(x = 0;x < destsize.x;x++) {
715             const BYTE *srcptr = bufptr + (x * srcsize.x / destsize.x) * srcformat->bytes_per_pixel;
716
717             /* extract source color components */
718             if(srcformat->type == FORMAT_ARGB) get_relevant_argb_components(&conv_info, *(const DWORD*)srcptr, channels);
719
720             /* recombine the components */
721             if(destformat->type == FORMAT_ARGB) make_argb_color(&conv_info, channels, (DWORD*)destptr);
722
723             destptr += destformat->bytes_per_pixel;
724         }
725     }
726 }
727
728 /************************************************************
729  * D3DXLoadSurfaceFromMemory
730  *
731  * Loads data from a given memory chunk into a surface,
732  * applying any of the specified filters.
733  *
734  * PARAMS
735  *   pDestSurface [I] pointer to the surface
736  *   pDestPalette [I] palette to use
737  *   pDestRect    [I] to be filled area of the surface
738  *   pSrcMemory   [I] pointer to the source data
739  *   SrcFormat    [I] format of the source pixel data
740  *   SrcPitch     [I] number of bytes in a row
741  *   pSrcPalette  [I] palette used in the source image
742  *   pSrcRect     [I] area of the source data to load
743  *   dwFilter     [I] filter to apply on stretching
744  *   Colorkey     [I] colorkey
745  *
746  * RETURNS
747  *   Success: D3D_OK, if we successfully load the pixel data into our surface or
748  *                    if pSrcMemory is NULL but the other parameters are valid
749  *   Failure: D3DERR_INVALIDCALL, if pDestSurface, SrcPitch or pSrcRect are NULL or
750  *                                if SrcFormat is an invalid format (other than D3DFMT_UNKNOWN) or
751  *                                if DestRect is invalid
752  *            D3DXERR_INVALIDDATA, if we fail to lock pDestSurface
753  *            E_FAIL, if SrcFormat is D3DFMT_UNKNOWN or the dimensions of pSrcRect are invalid
754  *
755  * NOTES
756  *   pSrcRect specifies the dimensions of the source data;
757  *   negative values for pSrcRect are allowed as we're only looking at the width and height anyway.
758  *
759  */
760 HRESULT WINAPI D3DXLoadSurfaceFromMemory(LPDIRECT3DSURFACE9 pDestSurface,
761                                          CONST PALETTEENTRY *pDestPalette,
762                                          CONST RECT *pDestRect,
763                                          LPCVOID pSrcMemory,
764                                          D3DFORMAT SrcFormat,
765                                          UINT SrcPitch,
766                                          CONST PALETTEENTRY *pSrcPalette,
767                                          CONST RECT *pSrcRect,
768                                          DWORD dwFilter,
769                                          D3DCOLOR Colorkey)
770 {
771     CONST PixelFormatDesc *srcformatdesc, *destformatdesc;
772     D3DSURFACE_DESC surfdesc;
773     D3DLOCKED_RECT lockrect;
774     POINT srcsize, destsize;
775     HRESULT hr;
776
777     TRACE("(%p, %p, %p, %p, %x, %u, %p, %p %u, %#x)\n", pDestSurface, pDestPalette, pDestRect, pSrcMemory,
778         SrcFormat, SrcPitch, pSrcPalette, pSrcRect, dwFilter, Colorkey);
779
780     if( !pDestSurface || !pSrcMemory || !pSrcRect ) return D3DERR_INVALIDCALL;
781     if(SrcFormat == D3DFMT_UNKNOWN || pSrcRect->left >= pSrcRect->right || pSrcRect->top >= pSrcRect->bottom) return E_FAIL;
782
783     if(dwFilter == D3DX_DEFAULT) dwFilter = D3DX_FILTER_TRIANGLE | D3DX_FILTER_DITHER;
784
785     IDirect3DSurface9_GetDesc(pDestSurface, &surfdesc);
786
787     srcformatdesc = get_format_info(SrcFormat);
788     destformatdesc = get_format_info(surfdesc.Format);
789     if( srcformatdesc->type == FORMAT_UNKNOWN ||  srcformatdesc->bytes_per_pixel > 4) return E_NOTIMPL;
790     if(destformatdesc->type == FORMAT_UNKNOWN || destformatdesc->bytes_per_pixel > 4) return E_NOTIMPL;
791
792     srcsize.x = pSrcRect->right - pSrcRect->left;
793     srcsize.y = pSrcRect->bottom - pSrcRect->top;
794     if( !pDestRect ) {
795         destsize.x = surfdesc.Width;
796         destsize.y = surfdesc.Height;
797     } else {
798         if(pDestRect->left > pDestRect->right || pDestRect->right > surfdesc.Width) return D3DERR_INVALIDCALL;
799         if(pDestRect->top > pDestRect->bottom || pDestRect->bottom > surfdesc.Height) return D3DERR_INVALIDCALL;
800         if(pDestRect->left < 0 || pDestRect->top < 0) return D3DERR_INVALIDCALL;
801         destsize.x = pDestRect->right - pDestRect->left;
802         destsize.y = pDestRect->bottom - pDestRect->top;
803         if(destsize.x == 0 || destsize.y == 0) return D3D_OK;
804     }
805
806     hr = IDirect3DSurface9_LockRect(pDestSurface, &lockrect, pDestRect, 0);
807     if(FAILED(hr)) return D3DXERR_INVALIDDATA;
808
809     if((dwFilter & 0xF) == D3DX_FILTER_NONE) {
810         copy_simple_data(pSrcMemory, SrcPitch, srcsize, srcformatdesc,
811                          lockrect.pBits, lockrect.Pitch, destsize, destformatdesc);
812     } else /*if((dwFilter & 0xF) == D3DX_FILTER_POINT) */ {
813         /* always apply a point filter until D3DX_FILTER_LINEAR, D3DX_FILTER_TRIANGLE and D3DX_FILTER_BOX are implemented */
814         point_filter_simple_data(pSrcMemory, SrcPitch, srcsize, srcformatdesc,
815                                  lockrect.pBits, lockrect.Pitch, destsize, destformatdesc);
816     }
817
818     IDirect3DSurface9_UnlockRect(pDestSurface);
819     return D3D_OK;
820 }
821
822 /************************************************************
823  * D3DXLoadSurfaceFromSurface
824  *
825  * Copies the contents from one surface to another, performing any required
826  * format conversion, resizing or filtering.
827  *
828  * PARAMS
829  *   pDestSurface [I] pointer to the destination surface
830  *   pDestPalette [I] palette to use
831  *   pDestRect    [I] to be filled area of the surface
832  *   pSrcSurface  [I] pointer to the source surface
833  *   pSrcPalette  [I] palette used for the source surface
834  *   pSrcRect     [I] area of the source data to load
835  *   dwFilter     [I] filter to apply on resizing
836  *   Colorkey     [I] any ARGB value or 0 to disable color-keying
837  *
838  * RETURNS
839  *   Success: D3D_OK
840  *   Failure: D3DERR_INVALIDCALL, if pDestSurface or pSrcSurface are NULL
841  *            D3DXERR_INVALIDDATA, if one of the surfaces is not lockable
842  *
843  */
844 HRESULT WINAPI D3DXLoadSurfaceFromSurface(LPDIRECT3DSURFACE9 pDestSurface,
845                                           CONST PALETTEENTRY *pDestPalette,
846                                           CONST RECT *pDestRect,
847                                           LPDIRECT3DSURFACE9 pSrcSurface,
848                                           CONST PALETTEENTRY *pSrcPalette,
849                                           CONST RECT *pSrcRect,
850                                           DWORD dwFilter,
851                                           D3DCOLOR Colorkey)
852 {
853     RECT rect;
854     D3DLOCKED_RECT lock;
855     D3DSURFACE_DESC SrcDesc;
856     HRESULT hr;
857
858     TRACE("(%p, %p, %p, %p, %p, %p, %u, %#x): relay\n", pDestSurface, pDestPalette, pDestRect,
859         pSrcSurface, pSrcPalette, pSrcRect, dwFilter, Colorkey);
860
861     if( !pDestSurface || !pSrcSurface ) return D3DERR_INVALIDCALL;
862
863     IDirect3DSurface9_GetDesc(pSrcSurface, &SrcDesc);
864
865     if( !pSrcRect ) SetRect(&rect, 0, 0, SrcDesc.Width, SrcDesc.Height);
866     else rect = *pSrcRect;
867
868     hr = IDirect3DSurface9_LockRect(pSrcSurface, &lock, NULL, D3DLOCK_READONLY);
869     if(FAILED(hr)) return D3DXERR_INVALIDDATA;
870
871     hr = D3DXLoadSurfaceFromMemory(pDestSurface, pDestPalette, pDestRect,
872                                    lock.pBits, SrcDesc.Format, lock.Pitch,
873                                    pSrcPalette, &rect, dwFilter, Colorkey);
874
875     IDirect3DSurface9_UnlockRect(pSrcSurface);
876     return hr;
877 }