Make unlock of the backbuffer correctly restore the contents.
[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     if (This->myDesc.Format == D3DFMT_DXT1) /* DXT1 is half byte per pixel */
157         pLockedRect->Pitch = pLockedRect->Pitch/2;
158     
159     if (NULL == pRect) {
160       pLockedRect->pBits = This->allocatedMemory;
161       This->lockedRect.left   = 0;
162       This->lockedRect.top    = 0;
163       This->lockedRect.right  = This->myDesc.Width;
164       This->lockedRect.bottom = This->myDesc.Height;
165       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);
166     } else {
167       TRACE("Lock Rect (%p) = l %ld, t %ld, r %ld, b %ld\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);
168
169       if (This->myDesc.Format == D3DFMT_DXT1) { /* DXT1 is half byte per pixel */
170           pLockedRect->pBits = This->allocatedMemory + (pLockedRect->Pitch * pRect->top) + ((pRect->left * This->bytesPerPixel/2));
171       } else {
172           pLockedRect->pBits = This->allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
173       }
174       This->lockedRect.left   = pRect->left;
175       This->lockedRect.top    = pRect->top;
176       This->lockedRect.right  = pRect->right;
177       This->lockedRect.bottom = pRect->bottom;
178     }
179
180
181     if (0 == This->myDesc.Usage) { /* classic surface */
182
183       /* Nothing to do ;) */
184
185     } else if (D3DUSAGE_RENDERTARGET & This->myDesc.Usage) { /* render surfaces */
186       
187       if (This == This->Device->backBuffer || This == This->Device->renderTarget || This == This->Device->frontBuffer) {
188         GLint  prev_store;
189         GLenum prev_read;
190         
191         ENTER_GL();
192
193         /**
194          * for render->surface copy begin to begin of allocatedMemory
195          * unlock can be more easy
196          */
197         pLockedRect->pBits = This->allocatedMemory;
198         
199         glFlush();
200         vcheckGLcall("glFlush");
201         glGetIntegerv(GL_READ_BUFFER, &prev_read);
202         vcheckGLcall("glIntegerv");
203         glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
204         vcheckGLcall("glIntegerv");
205
206         if (This == This->Device->backBuffer) {
207           glReadBuffer(GL_BACK);
208         } else if (This == This->Device->frontBuffer || This == This->Device->renderTarget) {
209           glReadBuffer(GL_FRONT);
210         } else if (This == This->Device->depthStencilBuffer) {
211           ERR("Stencil Buffer lock unsupported for now\n");
212         }
213         vcheckGLcall("glReadBuffer");
214
215         {
216           long j;
217           GLenum format = D3DFmt2GLFmt(This->Device, This->myDesc.Format);
218           GLenum type   = D3DFmt2GLType(This->Device, This->myDesc.Format);
219           for (j = This->lockedRect.top; j < This->lockedRect.bottom - This->lockedRect.top; ++j) {
220             glReadPixels(This->lockedRect.left, 
221                          This->lockedRect.bottom - j - 1, 
222                          This->lockedRect.right - This->lockedRect.left, 
223                          1,
224                          format, 
225                          type, 
226                          (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
227             vcheckGLcall("glReadPixels");
228           }
229         }
230
231         glReadBuffer(prev_read);
232         vcheckGLcall("glReadBuffer");
233
234         LEAVE_GL();
235
236       } else {
237         FIXME("unsupported locking to Rendering surface surf@%p usage(%lu)\n", This, This->myDesc.Usage);
238       }
239
240     } else if (D3DUSAGE_DEPTHSTENCIL & This->myDesc.Usage) { /* stencil surfaces */
241
242       FIXME("TODO stencil depth surface locking surf@%p usage(%lu)\n", This, This->myDesc.Usage);
243
244     } else {
245       FIXME("unsupported locking to surface surf@%p usage(%lu)\n", This, This->myDesc.Usage);
246     }
247
248     if (Flags & (D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_READONLY)) {
249       /* Dont dirtify */
250     } else {
251       /**
252        * Dirtify on lock
253        * as seen in msdn docs
254        */
255       IDirect3DSurface8Impl_AddDirtyRect(iface, &This->lockedRect);
256
257       /** Dirtify Container if needed */
258       if (NULL != This->Container) {
259         IDirect3DBaseTexture8* cont = NULL;
260         hr = IUnknown_QueryInterface(This->Container, &IID_IDirect3DBaseTexture8, (void**) &cont);
261         
262         if (SUCCEEDED(hr) && NULL != cont) {
263           IDirect3DBaseTexture8Impl_SetDirty(cont, TRUE);
264           IDirect3DBaseTexture8_Release(cont);
265           cont = NULL;
266         }
267       }
268     }
269
270     TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch, This->Dirty);
271
272     This->locked = TRUE;
273     return D3D_OK;
274 }
275
276 HRESULT WINAPI IDirect3DSurface8Impl_UnlockRect(LPDIRECT3DSURFACE8 iface) {
277     ICOM_THIS(IDirect3DSurface8Impl,iface);
278     
279     if (FALSE == This->locked) {
280       ERR("trying to lock unlocked surf@%p\n", This);  
281       return D3DERR_INVALIDCALL;
282     }
283
284     if (This == This->Device->backBuffer || This == This->Device->frontBuffer || This->Device->depthStencilBuffer) {
285       if (This == This->Device->backBuffer) {
286         TRACE("(%p, backBuffer) : dirtyfied(%d)\n", This, This->Dirty);
287       } else if (This == This->Device->frontBuffer) {
288         TRACE("(%p, frontBuffer) : dirtyfied(%d)\n", This, This->Dirty);
289       } else if (This == This->Device->depthStencilBuffer) {
290         TRACE("(%p, stencilBuffer) : dirtyfied(%d)\n", This, This->Dirty);
291       }
292     } else {
293       TRACE("(%p) : dirtyfied(%d)\n", This, This->Dirty);
294     }
295     /*TRACE("(%p) see if behavior is correct\n", This);*/
296
297     if (FALSE == This->Dirty) {
298       TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
299       goto unlock_end;
300     }
301
302     if (0 == This->myDesc.Usage) { /* classic surface */
303       /**
304        * nothing to do
305        * waiting to reload the surface via IDirect3DDevice8::UpdateTexture
306        */
307     } else if (D3DUSAGE_RENDERTARGET & This->myDesc.Usage) { /* render surfaces */
308
309       if (This == This->Device->backBuffer || This == This->Device->frontBuffer) {
310         GLint  prev_store;
311         GLenum prev_draw;
312         GLint  prev_rasterpos[4];
313
314         ENTER_GL();
315         
316         glFlush();
317         vcheckGLcall("glFlush");
318         glGetIntegerv(GL_DRAW_BUFFER, &prev_draw);
319         vcheckGLcall("glIntegerv");
320         glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
321         vcheckGLcall("glIntegerv");
322         glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
323         vcheckGLcall("glIntegerv");
324         glPixelZoom(1.0, -1.0);
325         vcheckGLcall("glPixelZoom");
326
327         if (This == This->Device->backBuffer) {
328           glDrawBuffer(GL_BACK);
329         } else if (This == This->Device->frontBuffer) {
330           glDrawBuffer(GL_FRONT);
331         }
332         vcheckGLcall("glDrawBuffer");
333
334         glRasterPos2i(This->lockedRect.left, This->lockedRect.top);
335         vcheckGLcall("glRasterPos2f");
336         switch (This->myDesc.Format) {
337         case D3DFMT_R5G6B5:
338           {
339             glDrawPixels(This->lockedRect.right - This->lockedRect.left, This->lockedRect.bottom - This->lockedRect.top,
340                          GL_RGB, GL_UNSIGNED_SHORT_5_6_5, This->allocatedMemory);
341             vcheckGLcall("glDrawPixels");
342           }
343           break;
344         case D3DFMT_R8G8B8:
345           {
346             glDrawPixels(This->lockedRect.right - This->lockedRect.left, This->lockedRect.bottom - This->lockedRect.top,
347                          GL_RGB, GL_UNSIGNED_BYTE, This->allocatedMemory);
348             vcheckGLcall("glDrawPixels");
349           }
350           break;
351         case D3DFMT_A8R8G8B8:
352           {
353             glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
354             vcheckGLcall("glPixelStorei");
355             glDrawPixels(This->lockedRect.right - This->lockedRect.left, This->lockedRect.bottom - This->lockedRect.top,
356                          GL_BGRA, GL_UNSIGNED_BYTE, This->allocatedMemory);
357             vcheckGLcall("glDrawPixels");
358             glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
359             vcheckGLcall("glPixelStorei");
360           }
361           break;
362         default:
363           FIXME("Unsupported Format %u in locking func\n", This->myDesc.Format);
364         }
365
366         glPixelZoom(1.0,1.0);
367         vcheckGLcall("glPixelZoom");
368         glDrawBuffer(prev_draw);
369         vcheckGLcall("glDrawBuffer");
370         glRasterPos3iv(&prev_rasterpos[0]);
371         vcheckGLcall("glRasterPos3iv");
372
373         LEAVE_GL();
374
375         /** restore clean dirty state */
376         IDirect3DSurface8Impl_CleanDirtyRect(iface);
377
378       } else {
379         FIXME("unsupported unlocking to Rendering surface surf@%p usage(%lu)\n", This, This->myDesc.Usage);
380       }
381
382     } else if (D3DUSAGE_DEPTHSTENCIL & This->myDesc.Usage) { /* stencil surfaces */
383     
384       if (This == This->Device->depthStencilBuffer) {
385         FIXME("TODO stencil depth surface unlocking surf@%p usage(%lu)\n", This, This->myDesc.Usage);
386       } else {
387         FIXME("unsupported unlocking to StencilDepth surface surf@%p usage(%lu)\n", This, This->myDesc.Usage);
388       }
389
390     } else {
391       FIXME("unsupported unlocking to surface surf@%p usage(%lu)\n", This, This->myDesc.Usage);
392     }
393
394 unlock_end:
395     This->locked = FALSE;
396     memset(&This->lockedRect, 0, sizeof(RECT));
397     return D3D_OK;
398 }
399
400
401 ICOM_VTABLE(IDirect3DSurface8) Direct3DSurface8_Vtbl =
402 {
403     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
404     IDirect3DSurface8Impl_QueryInterface,
405     IDirect3DSurface8Impl_AddRef,
406     IDirect3DSurface8Impl_Release,
407     IDirect3DSurface8Impl_GetDevice,
408     IDirect3DSurface8Impl_SetPrivateData,
409     IDirect3DSurface8Impl_GetPrivateData,
410     IDirect3DSurface8Impl_FreePrivateData,
411     IDirect3DSurface8Impl_GetContainer,
412     IDirect3DSurface8Impl_GetDesc,
413     IDirect3DSurface8Impl_LockRect,
414     IDirect3DSurface8Impl_UnlockRect,
415 };
416
417
418 HRESULT WINAPI IDirect3DSurface8Impl_LoadTexture(LPDIRECT3DSURFACE8 iface, GLenum gl_target, GLenum gl_level) {
419   ICOM_THIS(IDirect3DSurface8Impl,iface);
420
421   if ((This->myDesc.Format == D3DFMT_P8 || This->myDesc.Format == D3DFMT_A8P8) 
422 #if defined(GL_EXT_paletted_texture)
423       && !GL_SUPPORT_DEV(EXT_PALETTED_TEXTURE, This->Device)
424 #endif
425       ) {
426     /**
427      * wanted a paletted texture and not really support it in HW 
428      * so software emulation code begin
429      */
430     UINT i;
431     PALETTEENTRY* pal = This->Device->palettes[This->Device->currentPalette];
432     VOID* surface = (VOID*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->myDesc.Width * This->myDesc.Height * sizeof(DWORD));
433     BYTE* dst = (BYTE*) surface;
434     BYTE* src = (BYTE*) This->allocatedMemory;
435           
436     for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
437       BYTE color = *src++;
438       *dst++ = pal[color].peRed;
439       *dst++ = pal[color].peGreen;
440       *dst++ = pal[color].peBlue;
441       if (This->myDesc.Format == D3DFMT_A8P8)
442         *dst++ = pal[color].peFlags; 
443       else
444         *dst++ = 0xFF; 
445     }
446
447     ENTER_GL();
448     
449     TRACE("Calling glTexImage2D %x i=%d, intfmt=%x, w=%d, h=%d,0=%d, glFmt=%x, glType=%x, Mem=%p\n",
450           gl_target,
451           gl_level, 
452           GL_RGBA,
453           This->myDesc.Width, 
454           This->myDesc.Height, 
455           0, 
456           GL_RGBA,
457           GL_UNSIGNED_BYTE,
458           surface);
459     glTexImage2D(gl_target,
460                  gl_level, 
461                  GL_RGBA,
462                  This->myDesc.Width,
463                  This->myDesc.Height,
464                  0,
465                  GL_RGBA,
466                  GL_UNSIGNED_BYTE,
467                  surface);
468     checkGLcall("glTexImage2D");
469     HeapFree(GetProcessHeap(), 0, surface);
470
471     LEAVE_GL();
472
473     return D3D_OK;    
474   }
475
476   if (This->myDesc.Format == D3DFMT_DXT1 || 
477       This->myDesc.Format == D3DFMT_DXT3 || 
478       This->myDesc.Format == D3DFMT_DXT5) {
479 #if defined(GL_EXT_texture_compression_s3tc)
480     if (GL_SUPPORT_DEV(EXT_TEXTURE_COMPRESSION_S3TC, This->Device)) {
481       TRACE("Calling glCompressedTexImage2D %x i=%d, intfmt=%x, w=%d, h=%d,0=%d, sz=%d, Mem=%p\n",
482             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       
491       ENTER_GL();
492
493       glCompressedTexImage2DARB(gl_target, 
494                                 gl_level, 
495                                 D3DFmt2GLIntFmt(This->Device, This->myDesc.Format),
496                                 This->myDesc.Width,
497                                 This->myDesc.Height,
498                                 0,
499                                 This->myDesc.Size,
500                                 This->allocatedMemory);
501       checkGLcall("glCommpressedTexTexImage2D");
502
503       LEAVE_GL();
504     }
505 #else
506     FIXME("Using DXT1/3/5 without advertized support\n");
507 #endif
508   } else {
509     TRACE("Calling glTexImage2D %x i=%d, intfmt=%x, w=%d, h=%d,0=%d, glFmt=%x, glType=%x, Mem=%p\n",
510           gl_target, 
511           gl_level, 
512           D3DFmt2GLIntFmt(This->Device, This->myDesc.Format),
513           This->myDesc.Width, 
514           This->myDesc.Height, 
515           0, 
516           D3DFmt2GLFmt(This->Device, This->myDesc.Format), 
517           D3DFmt2GLType(This->Device, This->myDesc.Format),
518           This->allocatedMemory);
519
520     ENTER_GL();
521
522     glTexImage2D(gl_target, 
523                  gl_level,
524                  D3DFmt2GLIntFmt(This->Device, This->myDesc.Format),
525                  This->myDesc.Width,
526                  This->myDesc.Height,
527                  0,
528                  D3DFmt2GLFmt(This->Device, This->myDesc.Format),
529                  D3DFmt2GLType(This->Device, This->myDesc.Format),
530                  This->allocatedMemory);
531     checkGLcall("glTexImage2D");
532
533     LEAVE_GL();
534
535 #if 0
536     {
537       static unsigned int gen = 0;
538       char buffer[4096];
539       ++gen;
540       if ((gen % 10) == 0) {
541         snprintf(buffer, sizeof(buffer), "/tmp/surface%u_level%u_%u.ppm", gl_target, gl_level, gen);
542         IDirect3DSurface8Impl_SaveSnapshot((LPDIRECT3DSURFACE8) This, buffer);
543       }
544     }
545 #endif
546   }
547
548   return D3D_OK;
549 }
550
551 #include <errno.h>
552 HRESULT WINAPI IDirect3DSurface8Impl_SaveSnapshot(LPDIRECT3DSURFACE8 iface, const char* filename) {
553   FILE* f = NULL;
554   ULONG i;
555   ICOM_THIS(IDirect3DSurface8Impl,iface);
556
557   f = fopen(filename, "w+");
558   if (NULL == f) {
559     ERR("opening of %s failed with: %s\n", filename, strerror(errno));
560     return D3DERR_INVALIDCALL;
561   }
562
563   TRACE("opened %s with format %s\n", filename, debug_d3dformat(This->myDesc.Format));
564
565   fprintf(f, "P6\n%u %u\n255\n", This->myDesc.Width, This->myDesc.Height);
566   switch (This->myDesc.Format) {
567   case D3DFMT_X8R8G8B8:
568   case D3DFMT_A8R8G8B8:
569     {
570       DWORD color;
571       for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
572         color = ((DWORD*) This->allocatedMemory)[i];
573         fputc((color >> 16) & 0xFF, f);
574         fputc((color >>  8) & 0xFF, f);
575         fputc((color >>  0) & 0xFF, f);
576       }
577     }
578     break;
579   case D3DFMT_R8G8B8:
580     {
581       BYTE* color;
582       for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
583         color = ((BYTE*) This->allocatedMemory) + (3 * i);
584         fputc((color[0]) & 0xFF, f);
585         fputc((color[1]) & 0xFF, f);
586         fputc((color[2]) & 0xFF, f);
587       }
588     }
589     break;
590   case D3DFMT_A1R5G5B5: 
591     {
592       WORD color;
593       for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
594         color = ((WORD*) This->allocatedMemory)[i];
595         fputc(((color >> 10) & 0x1F) * 255 / 31, f);
596         fputc(((color >>  5) & 0x1F) * 255 / 31, f);
597         fputc(((color >>  0) & 0x1F) * 255 / 31, f);
598       }
599     }
600     break;
601   case D3DFMT_A4R4G4B4:
602     {
603       WORD color;
604       for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
605         color = ((WORD*) This->allocatedMemory)[i];
606         fputc(((color >>  8) & 0x0F) * 255 / 15, f);
607         fputc(((color >>  4) & 0x0F) * 255 / 15, f);
608         fputc(((color >>  0) & 0x0F) * 255 / 15, f);
609       }
610     }
611     break;
612
613   case D3DFMT_R5G6B5: 
614     {
615       WORD color;
616       for (i = 0; i < This->myDesc.Width * This->myDesc.Height; i++) {
617         color = ((WORD*) This->allocatedMemory)[i];
618         fputc(((color >> 11) & 0x1F) * 255 / 31, f);
619         fputc(((color >>  5) & 0x3F) * 255 / 63, f);
620         fputc(((color >>  0) & 0x1F) * 255 / 31, f);
621       }
622     }
623     break;
624   default: 
625     FIXME("Unimplemented dump mode format(%u,%s)\n", This->myDesc.Format, debug_d3dformat(This->myDesc.Format));
626   }
627   fclose(f);
628   return D3D_OK;
629 }
630
631 HRESULT WINAPI IDirect3DSurface8Impl_CleanDirtyRect(LPDIRECT3DSURFACE8 iface) {
632   ICOM_THIS(IDirect3DSurface8Impl,iface);
633   This->Dirty = FALSE;
634   This->dirtyRect.left   = This->myDesc.Width;
635   This->dirtyRect.top    = This->myDesc.Height;
636   This->dirtyRect.right  = 0;
637   This->dirtyRect.bottom = 0;
638   return D3D_OK;
639 }
640
641 /**
642  * Raphael:
643  *   very stupid way to handle multiple dirty rects but it works :)
644  */
645 extern HRESULT WINAPI IDirect3DSurface8Impl_AddDirtyRect(LPDIRECT3DSURFACE8 iface, CONST RECT* pDirtyRect) {
646   ICOM_THIS(IDirect3DSurface8Impl,iface);
647   This->Dirty = TRUE;
648   if (NULL != pDirtyRect) {
649     This->dirtyRect.left   = min(This->dirtyRect.left,   pDirtyRect->left);
650     This->dirtyRect.top    = min(This->dirtyRect.top,    pDirtyRect->top);
651     This->dirtyRect.right  = max(This->dirtyRect.right,  pDirtyRect->right);
652     This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
653   } else {
654     This->dirtyRect.left   = 0;
655     This->dirtyRect.top    = 0;
656     This->dirtyRect.right  = This->myDesc.Width;
657     This->dirtyRect.bottom = This->myDesc.Height;
658   }
659   return D3D_OK;
660 }