Fixed definitions of TTTOOLINFOA/W_V1_SIZE and
[wine] / dlls / d3d8 / surface.c
1 /*
2  * IDirect3DSurface8 implementation
3  *
4  * Copyright 2002-2003 Jason Edmeades
5  *                     Raphael Junqueira
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23
24 #include <stdarg.h>
25 #include <stdio.h>
26
27 #define NONAMELESSUNION
28 #define NONAMELESSSTRUCT
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "wingdi.h"
33 #include "wine/debug.h"
34
35 #include "d3d8_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
38
39 /* IDirect3DVolume IUnknown parts follow: */
40 HRESULT WINAPI IDirect3DSurface8Impl_QueryInterface(LPDIRECT3DSURFACE8 iface,REFIID riid,LPVOID *ppobj)
41 {
42     ICOM_THIS(IDirect3DSurface8Impl,iface);
43
44     if (IsEqualGUID(riid, &IID_IUnknown)
45         || IsEqualGUID(riid, &IID_IDirect3DSurface8)) {
46         IDirect3DSurface8Impl_AddRef(iface);
47         *ppobj = This;
48         return D3D_OK;
49     }
50
51     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppobj);
52     return E_NOINTERFACE;
53 }
54
55 ULONG WINAPI IDirect3DSurface8Impl_AddRef(LPDIRECT3DSURFACE8 iface) {
56     ICOM_THIS(IDirect3DSurface8Impl,iface);
57     TRACE("(%p) : AddRef from %ld\n", This, This->ref);
58     return ++(This->ref);
59 }
60
61 ULONG WINAPI IDirect3DSurface8Impl_Release(LPDIRECT3DSURFACE8 iface) {
62     ICOM_THIS(IDirect3DSurface8Impl,iface);
63     ULONG ref = --This->ref;
64     TRACE("(%p) : ReleaseRef to %ld\n", This, This->ref);
65     if (ref == 0) {
66       HeapFree(GetProcessHeap(), 0, This->allocatedMemory);
67       HeapFree(GetProcessHeap(), 0, This);
68     }
69     return ref;
70 }
71
72 /* IDirect3DSurface8: */
73 HRESULT WINAPI IDirect3DSurface8Impl_GetDevice(LPDIRECT3DSURFACE8 iface, IDirect3DDevice8** ppDevice) {
74     ICOM_THIS(IDirect3DSurface8Impl,iface);
75     TRACE("(%p) : returning %p\n", This, This->Device);
76     *ppDevice = (LPDIRECT3DDEVICE8) This->Device;
77     /**
78      * Note  Calling this method will increase the internal reference count 
79      * on the IDirect3DDevice8 interface. 
80      */
81     IDirect3DDevice8Impl_AddRef(*ppDevice);
82     return D3D_OK;
83 }
84
85 HRESULT WINAPI IDirect3DSurface8Impl_SetPrivateData(LPDIRECT3DSURFACE8 iface, REFGUID refguid, CONST void* pData, DWORD SizeOfData, DWORD Flags) {
86     ICOM_THIS(IDirect3DSurface8Impl,iface);
87     FIXME("(%p) : stub\n", This);    
88     return D3D_OK;
89 }
90
91 HRESULT WINAPI IDirect3DSurface8Impl_GetPrivateData(LPDIRECT3DSURFACE8 iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
92     ICOM_THIS(IDirect3DSurface8Impl,iface);
93     FIXME("(%p) : stub\n", This);    
94     return D3D_OK;
95 }
96
97 HRESULT WINAPI IDirect3DSurface8Impl_FreePrivateData(LPDIRECT3DSURFACE8 iface, REFGUID refguid) {
98     ICOM_THIS(IDirect3DSurface8Impl,iface);
99     FIXME("(%p) : stub\n", This);    
100     return D3D_OK;
101 }
102
103 HRESULT WINAPI IDirect3DSurface8Impl_GetContainer(LPDIRECT3DSURFACE8 iface, REFIID riid, void** ppContainer) {
104     ICOM_THIS(IDirect3DSurface8Impl,iface);
105     HRESULT res;
106     res = IUnknown_QueryInterface(This->Container, riid, ppContainer);
107     if (E_NOINTERFACE == res) { 
108       /**
109        * If the surface is created using CreateImageSurface, CreateRenderTarget, 
110        * or CreateDepthStencilSurface, the surface is considered stand alone. In this case, 
111        * GetContainer will return the Direct3D device used to create the surface. 
112        */
113       res = IUnknown_QueryInterface(This->Container, &IID_IDirect3DDevice8, ppContainer);
114     }
115     TRACE("(%p) : returning %p\n", This, *ppContainer);
116     return res;
117 }
118
119 HRESULT WINAPI IDirect3DSurface8Impl_GetDesc(LPDIRECT3DSURFACE8 iface, D3DSURFACE_DESC *pDesc) {
120     ICOM_THIS(IDirect3DSurface8Impl,iface);
121
122     TRACE("(%p) : copying into %p\n", This, pDesc);
123     memcpy(pDesc, &This->myDesc, sizeof(D3DSURFACE_DESC));
124     return D3D_OK;
125 }
126
127 HRESULT WINAPI IDirect3DSurface8Impl_LockRect(LPDIRECT3DSURFACE8 iface, D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
128     HRESULT hr;
129     ICOM_THIS(IDirect3DSurface8Impl,iface);
130   
131     /* fixme: should we really lock as such? */
132
133     if (FALSE == This->lockable) {
134       /* Note: UpdateTextures calls CopyRects which calls this routine to populate the 
135             texture regions, and since the destination is an unlockable region we need
136             to tolerate this                                                           */
137       TRACE("Warning: trying to lock unlockable surf@%p\n", This);  
138       /*return D3DERR_INVALIDCALL; */
139     }
140
141     if (This == This->Device->backBuffer || This == This->Device->renderTarget || This == This->Device->frontBuffer || This->Device->depthStencilBuffer) {
142       if (This == This->Device->backBuffer) {
143         TRACE("(%p, backBuffer) : rect@%p flags(%08lx), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->allocatedMemory);
144       } else if (This == This->Device->frontBuffer) {
145         TRACE("(%p, frontBuffer) : rect@%p flags(%08lx), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->allocatedMemory);
146       } else if (This == This->Device->renderTarget) {
147         TRACE("(%p, renderTarget) : rect@%p flags(%08lx), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->allocatedMemory);
148       } else if (This == This->Device->depthStencilBuffer) {
149         TRACE("(%p, stencilBuffer) : rect@%p flags(%08lx), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->allocatedMemory);
150       }
151     } else {
152       TRACE("(%p) : rect@%p flags(%08lx), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->allocatedMemory);
153     }
154
155     pLockedRect->Pitch = This->bytesPerPixel * This->myDesc.Width;  /* Bytes / row */    
156     
157     if (NULL == pRect) {
158       pLockedRect->pBits = This->allocatedMemory;
159       This->lockedRect.left   = 0;
160       This->lockedRect.top    = 0;
161       This->lockedRect.right  = This->myDesc.Width;
162       This->lockedRect.bottom = This->myDesc.Height;
163       TRACE("Locked Rect (%p) = l %ld, t %ld, r %ld, b %ld\n", &This->lockedRect, This->lockedRect.left, This->lockedRect.top, This->lockedRect.right, This->lockedRect.bottom);
164     } else {
165       TRACE("Lock Rect (%p) = l %ld, t %ld, r %ld, b %ld\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);
166       pLockedRect->pBits = This->allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
167       This->lockedRect.left   = pRect->left;
168       This->lockedRect.top    = pRect->top;
169       This->lockedRect.right  = pRect->right;
170       This->lockedRect.bottom = pRect->bottom;
171     }
172
173
174     if (0 == This->myDesc.Usage) { /* classic surface */
175
176       /* Nothing to do ;) */
177
178     } else if (D3DUSAGE_RENDERTARGET & This->myDesc.Usage) { /* render surfaces */
179       
180       if (This == This->Device->backBuffer || This == This->Device->renderTarget || This == This->Device->frontBuffer) {
181         GLint  prev_store;
182         GLenum prev_read;
183         
184         ENTER_GL();
185
186         /**
187          * for render->surface copy begin to begin of allocatedMemory
188          * unlock can be more easy
189          */
190         pLockedRect->pBits = This->allocatedMemory;
191         
192         glFlush();
193         vcheckGLcall("glFlush");
194         glGetIntegerv(GL_READ_BUFFER, &prev_read);
195         vcheckGLcall("glIntegerv");
196         glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
197         vcheckGLcall("glIntegerv");
198
199         if (This == This->Device->backBuffer) {
200           glReadBuffer(GL_BACK);
201         } else if (This == This->Device->frontBuffer || This == This->Device->renderTarget) {
202           glReadBuffer(GL_FRONT);
203         } else if (This == This->Device->depthStencilBuffer) {
204           ERR("Stencil Buffer lock unsupported for now\n");
205         }
206         vcheckGLcall("glReadBuffer");
207
208         {
209           long j;
210           GLenum format = D3DFmt2GLFmt(This->Device, This->myDesc.Format);
211           GLenum type   = D3DFmt2GLType(This->Device, This->myDesc.Format);
212           for (j = This->lockedRect.top; j < This->lockedRect.bottom - This->lockedRect.top; ++j) {
213             glReadPixels(This->lockedRect.left, 
214                          This->lockedRect.bottom - j - 1, 
215                          This->lockedRect.right - This->lockedRect.left, 
216                          1,
217                          format, 
218                          type, 
219                          (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
220             vcheckGLcall("glReadPixels");
221           }
222         }
223
224         glReadBuffer(prev_read);
225         vcheckGLcall("glReadBuffer");
226
227         LEAVE_GL();
228
229       } else {
230         FIXME("unsupported locking to Rendering surface surf@%p usage(%lu)\n", This, This->myDesc.Usage);
231       }
232
233     } else if (D3DUSAGE_DEPTHSTENCIL & This->myDesc.Usage) { /* stencil surfaces */
234
235       FIXME("TODO stencil depth surface locking surf@%p usage(%lu)\n", This, This->myDesc.Usage);
236
237     } else {
238       FIXME("unsupported locking to surface surf@%p usage(%lu)\n", This, This->myDesc.Usage);
239     }
240
241     if (Flags & (D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_READONLY)) {
242       /* Dont dirtify */
243     } else {
244       /**
245        * Dirtify on lock
246        * as seen in msdn docs
247        */
248       IDirect3DSurface8Impl_AddDirtyRect(iface, &This->lockedRect);
249
250       /** Dirtify Container if needed */
251       if (NULL != This->Container) {
252         IDirect3DBaseTexture8* cont = NULL;
253         hr = IUnknown_QueryInterface(This->Container, &IID_IDirect3DBaseTexture8, (void**) &cont);
254         
255         if (SUCCEEDED(hr) && NULL != cont) {
256           IDirect3DBaseTexture8Impl_SetDirty(cont, TRUE);
257           IDirect3DBaseTexture8_Release(cont);
258           cont = NULL;
259         }
260       }
261     }
262
263     TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch, This->Dirty);
264
265     This->locked = TRUE;
266     return D3D_OK;
267 }
268
269 HRESULT WINAPI IDirect3DSurface8Impl_UnlockRect(LPDIRECT3DSURFACE8 iface) {
270     ICOM_THIS(IDirect3DSurface8Impl,iface);
271     
272     if (FALSE == This->locked) {
273       ERR("trying to lock unlocked surf@%p\n", This);  
274       return D3DERR_INVALIDCALL;
275     }
276
277     if (This == This->Device->backBuffer || This == This->Device->frontBuffer || This->Device->depthStencilBuffer) {
278       if (This == This->Device->backBuffer) {
279         TRACE("(%p, backBuffer) : dirtyfied(%d)\n", This, This->Dirty);
280       } else if (This == This->Device->frontBuffer) {
281         TRACE("(%p, frontBuffer) : dirtyfied(%d)\n", This, This->Dirty);
282       } else if (This == This->Device->depthStencilBuffer) {
283         TRACE("(%p, stencilBuffer) : dirtyfied(%d)\n", This, This->Dirty);
284       }
285     } else {
286       TRACE("(%p) : dirtyfied(%d)\n", This, This->Dirty);
287     }
288     /*TRACE("(%p) see if behavior is correct\n", This);*/
289
290     if (FALSE == This->Dirty) {
291       TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
292       goto unlock_end;
293     }
294
295     if (0 == This->myDesc.Usage) { /* classic surface */
296       /**
297        * nothing to do
298        * waiting to reload the surface via IDirect3DDevice8::UpdateTexture
299        */
300     } else if (D3DUSAGE_RENDERTARGET & This->myDesc.Usage) { /* render surfaces */
301
302       if (This == This->Device->backBuffer || This == This->Device->frontBuffer) {
303         GLint  prev_store;
304         GLenum prev_draw;
305         GLint  prev_rasterpos[4];
306
307         ENTER_GL();
308         
309         glFlush();
310         vcheckGLcall("glFlush");
311         glGetIntegerv(GL_DRAW_BUFFER, &prev_draw);
312         vcheckGLcall("glIntegerv");
313         glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
314         vcheckGLcall("glIntegerv");
315         glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
316         vcheckGLcall("glIntegerv");
317
318         if (This == This->Device->backBuffer) {
319           glDrawBuffer(GL_BACK);
320         } else if (This == This->Device->frontBuffer) {
321           glDrawBuffer(GL_FRONT);
322         }
323         vcheckGLcall("glDrawBuffer");
324
325         glRasterPos2i(This->lockedRect.left, This->lockedRect.top);
326         vcheckGLcall("glRasterPos2f");
327         switch (This->myDesc.Format) {
328         case D3DFMT_R5G6B5:
329           {
330             glDrawPixels(This->lockedRect.right - This->lockedRect.left, This->lockedRect.bottom - This->lockedRect.top,
331                          GL_RGB, GL_UNSIGNED_SHORT_5_6_5, This->allocatedMemory);
332             vcheckGLcall("glDrawPixels");
333           }
334           break;
335         case D3DFMT_R8G8B8:
336           {
337             glDrawPixels(This->lockedRect.right - This->lockedRect.left, This->lockedRect.bottom - This->lockedRect.top,
338                          GL_RGB, GL_UNSIGNED_BYTE, This->allocatedMemory);
339             vcheckGLcall("glDrawPixels");
340           }
341           break;
342         case D3DFMT_A8R8G8B8:
343           {
344             glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
345             vcheckGLcall("glPixelStorei");
346             glDrawPixels(This->lockedRect.right - This->lockedRect.left, This->lockedRect.bottom - This->lockedRect.top,
347                          GL_BGRA, GL_UNSIGNED_BYTE, This->allocatedMemory);
348             vcheckGLcall("glDrawPixels");
349             glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
350             vcheckGLcall("glPixelStorei");
351           }
352           break;
353         default:
354           FIXME("Unsupported Format %u in locking func\n", This->myDesc.Format);
355         }
356
357         glDrawBuffer(prev_draw);
358         vcheckGLcall("glDrawBuffer");
359         glRasterPos3iv(&prev_rasterpos[0]);
360         vcheckGLcall("glRasterPos3iv");
361
362         LEAVE_GL();
363
364         /** restore clean dirty state */
365         IDirect3DSurface8Impl_CleanDirtyRect(iface);
366
367       } else {
368         FIXME("unsupported unlocking to Rendering surface surf@%p usage(%lu)\n", This, This->myDesc.Usage);
369       }
370
371     } else if (D3DUSAGE_DEPTHSTENCIL & This->myDesc.Usage) { /* stencil surfaces */
372     
373       if (This == This->Device->depthStencilBuffer) {
374         FIXME("TODO stencil depth surface unlocking surf@%p usage(%lu)\n", This, This->myDesc.Usage);
375       } else {
376         FIXME("unsupported unlocking to StencilDepth surface surf@%p usage(%lu)\n", This, This->myDesc.Usage);
377       }
378
379     } else {
380       FIXME("unsupported unlocking to surface surf@%p usage(%lu)\n", This, This->myDesc.Usage);
381     }
382
383 unlock_end:
384     This->locked = FALSE;
385     memset(&This->lockedRect, 0, sizeof(RECT));
386     return D3D_OK;
387 }
388
389
390 ICOM_VTABLE(IDirect3DSurface8) Direct3DSurface8_Vtbl =
391 {
392     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
393     IDirect3DSurface8Impl_QueryInterface,
394     IDirect3DSurface8Impl_AddRef,
395     IDirect3DSurface8Impl_Release,
396     IDirect3DSurface8Impl_GetDevice,
397     IDirect3DSurface8Impl_SetPrivateData,
398     IDirect3DSurface8Impl_GetPrivateData,
399     IDirect3DSurface8Impl_FreePrivateData,
400     IDirect3DSurface8Impl_GetContainer,
401     IDirect3DSurface8Impl_GetDesc,
402     IDirect3DSurface8Impl_LockRect,
403     IDirect3DSurface8Impl_UnlockRect,
404 };
405
406
407 HRESULT WINAPI IDirect3DSurface8Impl_LoadTexture(LPDIRECT3DSURFACE8 iface, GLenum gl_target, GLenum gl_level) {
408   ICOM_THIS(IDirect3DSurface8Impl,iface);
409
410   if ((This->myDesc.Format == D3DFMT_P8 || This->myDesc.Format == D3DFMT_A8P8) 
411 #if defined(GL_EXT_paletted_texture)
412       && !GL_SUPPORT_DEV(EXT_PALETTED_TEXTURE, This->Device)
413 #endif
414       ) {
415     /**
416      * wanted a paletted texture and not really support it in HW 
417      * so software emulation code begin
418      */
419     UINT i;
420     PALETTEENTRY* pal = This->Device->palettes[This->Device->currentPalette];
421     VOID* surface = (VOID*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->myDesc.Width * This->myDesc.Height * sizeof(DWORD));
422     BYTE* dst = (BYTE*) surface;
423     BYTE* src = (BYTE*) This->allocatedMemory;
424           
425     for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
426       BYTE color = *src++;
427       *dst++ = pal[color].peRed;
428       *dst++ = pal[color].peGreen;
429       *dst++ = pal[color].peBlue;
430       if (This->myDesc.Format == D3DFMT_A8P8)
431         *dst++ = pal[color].peFlags; 
432       else
433         *dst++ = 0xFF; 
434     }
435
436     ENTER_GL();
437     
438     TRACE("Calling glTexImage2D %x i=%d, intfmt=%x, w=%d, h=%d,0=%d, glFmt=%x, glType=%x, Mem=%p\n",
439           gl_target,
440           gl_level, 
441           GL_RGBA,
442           This->myDesc.Width, 
443           This->myDesc.Height, 
444           0, 
445           GL_RGBA,
446           GL_UNSIGNED_BYTE,
447           surface);
448     glTexImage2D(gl_target,
449                  gl_level, 
450                  GL_RGBA,
451                  This->myDesc.Width,
452                  This->myDesc.Height,
453                  0,
454                  GL_RGBA,
455                  GL_UNSIGNED_BYTE,
456                  surface);
457     checkGLcall("glTexImage2D");
458     HeapFree(GetProcessHeap(), 0, surface);
459
460     LEAVE_GL();
461
462     return D3D_OK;    
463   }
464
465   if (This->myDesc.Format == D3DFMT_DXT1 || 
466       This->myDesc.Format == D3DFMT_DXT3 || 
467       This->myDesc.Format == D3DFMT_DXT5) {
468 #if defined(GL_EXT_texture_compression_s3tc)
469     if (GL_SUPPORT_DEV(EXT_TEXTURE_COMPRESSION_S3TC, This->Device)) {
470       TRACE("Calling glCompressedTexImage2D %x i=%d, intfmt=%x, w=%d, h=%d,0=%d, sz=%d, Mem=%p\n",
471             gl_target, 
472             gl_level, 
473             D3DFmt2GLIntFmt(This->Device, This->myDesc.Format), 
474             This->myDesc.Width, 
475             This->myDesc.Height, 
476             0, 
477             This->myDesc.Size,
478             This->allocatedMemory);
479       
480       ENTER_GL();
481
482       glCompressedTexImage2DARB(gl_target, 
483                                 gl_level, 
484                                 D3DFmt2GLIntFmt(This->Device, This->myDesc.Format),
485                                 This->myDesc.Width,
486                                 This->myDesc.Height,
487                                 0,
488                                 This->myDesc.Size,
489                                 This->allocatedMemory);
490       checkGLcall("glCommpressedTexTexImage2D");
491
492       LEAVE_GL();
493     }
494 #endif
495   } else {
496     TRACE("Calling glTexImage2D %x i=%d, intfmt=%x, w=%d, h=%d,0=%d, glFmt=%x, glType=%x, Mem=%p\n",
497           gl_target, 
498           gl_level, 
499           D3DFmt2GLIntFmt(This->Device, This->myDesc.Format),
500           This->myDesc.Width, 
501           This->myDesc.Height, 
502           0, 
503           D3DFmt2GLFmt(This->Device, This->myDesc.Format), 
504           D3DFmt2GLType(This->Device, This->myDesc.Format),
505           This->allocatedMemory);
506
507     ENTER_GL();
508
509     glTexImage2D(gl_target, 
510                  gl_level,
511                  D3DFmt2GLIntFmt(This->Device, This->myDesc.Format),
512                  This->myDesc.Width,
513                  This->myDesc.Height,
514                  0,
515                  D3DFmt2GLFmt(This->Device, This->myDesc.Format),
516                  D3DFmt2GLType(This->Device, This->myDesc.Format),
517                  This->allocatedMemory);
518     checkGLcall("glTexImage2D");
519
520     LEAVE_GL();
521
522 #if 0
523     {
524       static unsigned int gen = 0;
525       char buffer[4096];
526       ++gen;
527       if ((gen % 10) == 0) {
528         snprintf(buffer, sizeof(buffer), "/tmp/surface%u_level%u_%u.ppm", gl_target, gl_level, gen);
529         IDirect3DSurface8Impl_SaveSnapshot((LPDIRECT3DSURFACE8) This, buffer);
530       }
531     }
532 #endif
533   }
534
535   return D3D_OK;
536 }
537
538 #include <errno.h>
539 HRESULT WINAPI IDirect3DSurface8Impl_SaveSnapshot(LPDIRECT3DSURFACE8 iface, const char* filename) {
540   FILE* f = NULL;
541   ULONG i;
542   ICOM_THIS(IDirect3DSurface8Impl,iface);
543
544   f = fopen(filename, "w+");
545   if (NULL == f) {
546     ERR("opening of %s failed with: %s\n", filename, strerror(errno));
547     return D3DERR_INVALIDCALL;
548   }
549
550   TRACE("opened %s with format %s\n", filename, debug_d3dformat(This->myDesc.Format));
551
552   fprintf(f, "P6\n%u %u\n255\n", This->myDesc.Width, This->myDesc.Height);
553   switch (This->myDesc.Format) {
554   case D3DFMT_X8R8G8B8:
555   case D3DFMT_A8R8G8B8:
556     {
557       DWORD color;
558       for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
559         color = ((DWORD*) This->allocatedMemory)[i];
560         fputc((color >> 16) & 0xFF, f);
561         fputc((color >>  8) & 0xFF, f);
562         fputc((color >>  0) & 0xFF, f);
563       }
564     }
565     break;
566   case D3DFMT_R8G8B8:
567     {
568       BYTE* color;
569       for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
570         color = ((BYTE*) This->allocatedMemory) + (3 * i);
571         fputc((color[0]) & 0xFF, f);
572         fputc((color[1]) & 0xFF, f);
573         fputc((color[2]) & 0xFF, f);
574       }
575     }
576     break;
577   case D3DFMT_A1R5G5B5: 
578     {
579       WORD color;
580       for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
581         color = ((WORD*) This->allocatedMemory)[i];
582         fputc(((color >> 10) & 0x1F) * 255 / 31, f);
583         fputc(((color >>  5) & 0x1F) * 255 / 31, f);
584         fputc(((color >>  0) & 0x1F) * 255 / 31, f);
585       }
586     }
587     break;
588   case D3DFMT_A4R4G4B4:
589     {
590       WORD color;
591       for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
592         color = ((WORD*) This->allocatedMemory)[i];
593         fputc(((color >>  8) & 0x0F) * 255 / 15, f);
594         fputc(((color >>  4) & 0x0F) * 255 / 15, f);
595         fputc(((color >>  0) & 0x0F) * 255 / 15, f);
596       }
597     }
598     break;
599
600   case D3DFMT_R5G6B5: 
601     {
602       WORD color;
603       for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
604         color = ((WORD*) This->allocatedMemory)[i];
605         fputc(((color >> 11) & 0x1F) * 255 / 31, f);
606         fputc(((color >>  5) & 0x3F) * 255 / 63, f);
607         fputc(((color >>  0) & 0x1F) * 255 / 31, f);
608       }
609     }
610     break;
611   default: 
612     FIXME("Unimplemented dump mode format(%u,%s)\n", This->myDesc.Format, debug_d3dformat(This->myDesc.Format));
613   }
614   fclose(f);
615   return D3D_OK;
616 }
617
618 HRESULT WINAPI IDirect3DSurface8Impl_CleanDirtyRect(LPDIRECT3DSURFACE8 iface) {
619   ICOM_THIS(IDirect3DSurface8Impl,iface);
620   This->Dirty = FALSE;
621   This->dirtyRect.left   = This->myDesc.Width;
622   This->dirtyRect.top    = This->myDesc.Height;
623   This->dirtyRect.right  = 0;
624   This->dirtyRect.bottom = 0;
625   return D3D_OK;
626 }
627
628 /**
629  * Raphael:
630  *   very stupid way to handle multiple dirty rects but it works :)
631  */
632 extern HRESULT WINAPI IDirect3DSurface8Impl_AddDirtyRect(LPDIRECT3DSURFACE8 iface, CONST RECT* pDirtyRect) {
633   ICOM_THIS(IDirect3DSurface8Impl,iface);
634   This->Dirty = TRUE;
635   if (NULL != pDirtyRect) {
636     This->dirtyRect.left   = min(This->dirtyRect.left,   pDirtyRect->left);
637     This->dirtyRect.top    = min(This->dirtyRect.top,    pDirtyRect->top);
638     This->dirtyRect.right  = max(This->dirtyRect.right,  pDirtyRect->right);
639     This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
640   } else {
641     This->dirtyRect.left   = 0;
642     This->dirtyRect.top    = 0;
643     This->dirtyRect.right  = This->myDesc.Width;
644     This->dirtyRect.bottom = This->myDesc.Height;
645   }
646   return D3D_OK;
647 }