wined3d: Pass an IWineD3DResourceImpl pointer to resource_cleanup().
[wine] / dlls / wined3d / surface_gdi.c
1 /*
2  * 2D Surface implementation without OpenGL
3  *
4  * Copyright 1997-2000 Marcus Meissner
5  * Copyright 1998-2000 Lionel Ulmer
6  * Copyright 2000-2001 TransGaming Technologies Inc.
7  * Copyright 2002-2005 Jason Edmeades
8  * Copyright 2002-2003 Raphael Junqueira
9  * Copyright 2004 Christian Costa
10  * Copyright 2005 Oliver Stieber
11  * Copyright 2006-2008 Stefan Dösinger
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public
15  * License as published by the Free Software Foundation; either
16  * version 2.1 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public
24  * License along with this library; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26  */
27
28 #include "config.h"
29 #include "wine/port.h"
30 #include "wined3d_private.h"
31
32 #include <stdio.h>
33
34 /* Use the d3d_surface debug channel to have one channel for all surfaces */
35 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
36
37 void surface_gdi_cleanup(IWineD3DSurfaceImpl *This)
38 {
39     TRACE("(%p) : Cleaning up.\n", This);
40
41     if (This->flags & SFLAG_DIBSECTION)
42     {
43         /* Release the DC. */
44         SelectObject(This->hDC, This->dib.holdbitmap);
45         DeleteDC(This->hDC);
46         /* Release the DIB section. */
47         DeleteObject(This->dib.DIBsection);
48         This->dib.bitmap_data = NULL;
49         This->resource.allocatedMemory = NULL;
50     }
51
52     if (This->flags & SFLAG_USERPTR) IWineD3DSurface_SetMem((IWineD3DSurface *)This, NULL);
53     if (This->overlay_dest) list_remove(&This->overlay_entry);
54
55     HeapFree(GetProcessHeap(), 0, This->palette9);
56
57     resource_cleanup((IWineD3DResourceImpl *)This);
58 }
59
60 /*****************************************************************************
61  * IWineD3DSurface::Release, GDI version
62  *
63  * In general a normal COM Release method, but the GDI version doesn't have
64  * to destroy all the GL things.
65  *
66  *****************************************************************************/
67 static ULONG WINAPI IWineGDISurfaceImpl_Release(IWineD3DSurface *iface) {
68     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
69     ULONG ref = InterlockedDecrement(&This->resource.ref);
70     TRACE("(%p) : Releasing from %d\n", This, ref + 1);
71
72     if (!ref)
73     {
74         surface_gdi_cleanup(This);
75
76         TRACE("(%p) Released.\n", This);
77         HeapFree(GetProcessHeap(), 0, This);
78     }
79
80     return ref;
81 }
82
83 /*****************************************************************************
84  * IWineD3DSurface::PreLoad, GDI version
85  *
86  * This call is unsupported on GDI surfaces, if it's called something went
87  * wrong in the parent library. Write an informative warning
88  *
89  *****************************************************************************/
90 static void WINAPI
91 IWineGDISurfaceImpl_PreLoad(IWineD3DSurface *iface)
92 {
93     ERR("(%p): PreLoad is not supported on X11 surfaces!\n", iface);
94     ERR("(%p): Most likely the parent library did something wrong.\n", iface);
95     ERR("(%p): Please report to wine-devel\n", iface);
96 }
97
98 /*****************************************************************************
99  * IWineD3DSurface::UnLoad, GDI version
100  *
101  * This call is unsupported on GDI surfaces, if it's called something went
102  * wrong in the parent library. Write an informative warning.
103  *
104  *****************************************************************************/
105 static void WINAPI IWineGDISurfaceImpl_UnLoad(IWineD3DSurface *iface)
106 {
107     ERR("(%p): UnLoad is not supported on X11 surfaces!\n", iface);
108     ERR("(%p): Most likely the parent library did something wrong.\n", iface);
109     ERR("(%p): Please report to wine-devel\n", iface);
110 }
111
112 static HRESULT WINAPI IWineGDISurfaceImpl_Map(IWineD3DSurface *iface,
113         WINED3DLOCKED_RECT *pLockedRect, const RECT *pRect, DWORD flags)
114 {
115     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
116
117     /* Already locked? */
118     if (This->flags & SFLAG_LOCKED)
119     {
120         WARN("(%p) Surface already locked\n", This);
121         /* What should I return here? */
122         return WINED3DERR_INVALIDCALL;
123     }
124     This->flags |= SFLAG_LOCKED;
125
126     if(!This->resource.allocatedMemory) {
127         /* This happens on gdi surfaces if the application set a user pointer and resets it.
128          * Recreate the DIB section
129          */
130         IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
131         This->resource.allocatedMemory = This->dib.bitmap_data;
132     }
133
134     return IWineD3DBaseSurfaceImpl_Map(iface, pLockedRect, pRect, flags);
135 }
136
137 static HRESULT WINAPI IWineGDISurfaceImpl_Unmap(IWineD3DSurface *iface)
138 {
139     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
140     TRACE("(%p)\n", This);
141
142     if (!(This->flags & SFLAG_LOCKED))
143     {
144         WARN("Trying to unmap unmapped surfaces %p.\n", iface);
145         return WINEDDERR_NOTLOCKED;
146     }
147
148     /* Tell the swapchain to update the screen */
149     if (This->container.type == WINED3D_CONTAINER_SWAPCHAIN)
150     {
151         IWineD3DSwapChainImpl *swapchain = This->container.u.swapchain;
152         if (This == swapchain->front_buffer)
153         {
154             x11_copy_to_screen(swapchain, &This->lockedRect);
155         }
156     }
157
158     This->flags &= ~SFLAG_LOCKED;
159     memset(&This->lockedRect, 0, sizeof(RECT));
160     return WINED3D_OK;
161 }
162
163 /*****************************************************************************
164  * IWineD3DSurface::Flip, GDI version
165  *
166  * Flips 2 flipping enabled surfaces. Determining the 2 targets is done by
167  * the parent library. This implementation changes the data pointers of the
168  * surfaces and copies the new front buffer content to the screen
169  *
170  * Params:
171  *  override: Flipping target(e.g. back buffer)
172  *
173  * Returns:
174  *  WINED3D_OK on success
175  *
176  *****************************************************************************/
177 static HRESULT WINAPI IWineGDISurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD flags)
178 {
179     IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
180     IWineD3DSwapChainImpl *swapchain;
181     HRESULT hr;
182
183     if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
184     {
185         ERR("Flipped surface is not on a swapchain\n");
186         return WINEDDERR_NOTFLIPPABLE;
187     }
188
189     swapchain = surface->container.u.swapchain;
190     hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *)swapchain,
191             NULL, NULL, swapchain->win_handle, NULL, 0);
192
193     return hr;
194 }
195
196 /*****************************************************************************
197  * IWineD3DSurface::LoadTexture, GDI version
198  *
199  * This is mutually unsupported by GDI surfaces
200  *
201  * Returns:
202  *  D3DERR_INVALIDCALL
203  *
204  *****************************************************************************/
205 static HRESULT WINAPI
206 IWineGDISurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode)
207 {
208     ERR("Unsupported on X11 surfaces\n");
209     return WINED3DERR_INVALIDCALL;
210 }
211
212 static void WINAPI IWineGDISurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb)
213 {
214     ERR("Not supported.\n");
215 }
216
217 static HRESULT WINAPI IWineGDISurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
218     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
219     WINED3DLOCKED_RECT lock;
220     HRESULT hr;
221     RGBQUAD col[256];
222
223     TRACE("(%p)->(%p)\n",This,pHDC);
224
225     if (!(This->flags & SFLAG_DIBSECTION))
226     {
227         WARN("DC not supported on this surface\n");
228         return WINED3DERR_INVALIDCALL;
229     }
230
231     if (This->flags & SFLAG_USERPTR)
232     {
233         ERR("Not supported on surfaces with an application-provided surfaces\n");
234         return WINEDDERR_NODC;
235     }
236
237     /* Give more detailed info for ddraw */
238     if (This->flags & SFLAG_DCINUSE)
239         return WINEDDERR_DCALREADYCREATED;
240
241     /* Can't GetDC if the surface is locked */
242     if (This->flags & SFLAG_LOCKED)
243         return WINED3DERR_INVALIDCALL;
244
245     memset(&lock, 0, sizeof(lock)); /* To be sure */
246
247     /* Should have a DIB section already */
248
249     /* Map the surface. */
250     hr = IWineD3DSurface_Map(iface, &lock, NULL, 0);
251     if (FAILED(hr))
252     {
253         ERR("IWineD3DSurface_Map failed, hr %#x.\n", hr);
254         /* keep the dib section */
255         return hr;
256     }
257
258     if (This->resource.format->id == WINED3DFMT_P8_UINT
259             || This->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
260     {
261         unsigned int n;
262         const PALETTEENTRY *pal = NULL;
263
264         if(This->palette) {
265             pal = This->palette->palents;
266         } else {
267             IWineD3DSurfaceImpl *dds_primary;
268             IWineD3DSwapChainImpl *swapchain;
269             swapchain = (IWineD3DSwapChainImpl *)This->resource.device->swapchains[0];
270             dds_primary = swapchain->front_buffer;
271             if (dds_primary && dds_primary->palette)
272                 pal = dds_primary->palette->palents;
273         }
274
275         if (pal) {
276             for (n=0; n<256; n++) {
277                 col[n].rgbRed   = pal[n].peRed;
278                 col[n].rgbGreen = pal[n].peGreen;
279                 col[n].rgbBlue  = pal[n].peBlue;
280                 col[n].rgbReserved = 0;
281             }
282             SetDIBColorTable(This->hDC, 0, 256, col);
283         }
284     }
285
286     *pHDC = This->hDC;
287     TRACE("returning %p\n",*pHDC);
288     This->flags |= SFLAG_DCINUSE;
289
290     return WINED3D_OK;
291 }
292
293 static HRESULT WINAPI IWineGDISurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
294     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
295
296     TRACE("(%p)->(%p)\n",This,hDC);
297
298     if (!(This->flags & SFLAG_DCINUSE))
299         return WINEDDERR_NODC;
300
301     if (This->hDC !=hDC) {
302         WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
303         return WINEDDERR_NODC;
304     }
305
306     /* we locked first, so unlock now */
307     IWineD3DSurface_Unmap(iface);
308
309     This->flags &= ~SFLAG_DCINUSE;
310
311     return WINED3D_OK;
312 }
313
314 static HRESULT WINAPI IWineGDISurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
315     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
316     RGBQUAD col[256];
317     IWineD3DPaletteImpl *pal = This->palette;
318     unsigned int n;
319     TRACE("(%p)\n", This);
320
321     if (!pal) return WINED3D_OK;
322
323     if (This->flags & SFLAG_DIBSECTION)
324     {
325         TRACE("(%p): Updating the hdc's palette\n", This);
326         for (n=0; n<256; n++) {
327             col[n].rgbRed   = pal->palents[n].peRed;
328             col[n].rgbGreen = pal->palents[n].peGreen;
329             col[n].rgbBlue  = pal->palents[n].peBlue;
330             col[n].rgbReserved = 0;
331         }
332         SetDIBColorTable(This->hDC, 0, 256, col);
333     }
334
335     /* Update the image because of the palette change. Some games like e.g Red Alert
336        call SetEntries a lot to implement fading. */
337     /* Tell the swapchain to update the screen */
338     if (This->container.type == WINED3D_CONTAINER_SWAPCHAIN)
339     {
340         IWineD3DSwapChainImpl *swapchain = This->container.u.swapchain;
341         if (This == swapchain->front_buffer)
342         {
343             x11_copy_to_screen(swapchain, NULL);
344         }
345     }
346
347     return WINED3D_OK;
348 }
349
350 /*****************************************************************************
351  * IWineD3DSurface::PrivateSetup, GDI version
352  *
353  * Initializes the GDI surface, aka creates the DIB section we render to
354  * The DIB section creation is done by calling GetDC, which will create the
355  * section and releasing the dc to allow the app to use it. The dib section
356  * will stay until the surface is released
357  *
358  * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
359  * are set to the real sizes to save memory. The NONPOW2 flag is unset to
360  * avoid confusion in the shared surface code.
361  *
362  * Returns:
363  *  WINED3D_OK on success
364  *  The return values of called methods on failure
365  *
366  *****************************************************************************/
367 static HRESULT WINAPI
368 IWineGDISurfaceImpl_PrivateSetup(IWineD3DSurface *iface)
369 {
370     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
371     HRESULT hr;
372
373     if(This->resource.usage & WINED3DUSAGE_OVERLAY)
374     {
375         ERR("(%p) Overlays not yet supported by GDI surfaces\n", This);
376         return WINED3DERR_INVALIDCALL;
377     }
378
379     /* Sysmem textures have memory already allocated -
380      * release it, this avoids an unnecessary memcpy
381      */
382     hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
383     if(SUCCEEDED(hr))
384     {
385         HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
386         This->resource.heapMemory = NULL;
387         This->resource.allocatedMemory = This->dib.bitmap_data;
388     }
389
390     /* We don't mind the nonpow2 stuff in GDI */
391     This->pow2Width = This->currentDesc.Width;
392     This->pow2Height = This->currentDesc.Height;
393
394     return WINED3D_OK;
395 }
396
397 static HRESULT WINAPI IWineGDISurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
398     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
399
400     /* Render targets depend on their hdc, and we can't create an hdc on a user pointer */
401     if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
402         ERR("Not supported on render targets\n");
403         return WINED3DERR_INVALIDCALL;
404     }
405
406     if (This->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
407     {
408         WARN("Surface is locked or the HDC is in use\n");
409         return WINED3DERR_INVALIDCALL;
410     }
411
412     if(Mem && Mem != This->resource.allocatedMemory) {
413         void *release = NULL;
414
415         /* Do I have to copy the old surface content? */
416         if (This->flags & SFLAG_DIBSECTION)
417         {
418                 /* Release the DC. No need to hold the critical section for the update
419             * Thread because this thread runs only on front buffers, but this method
420             * fails for render targets in the check above.
421                 */
422             SelectObject(This->hDC, This->dib.holdbitmap);
423             DeleteDC(This->hDC);
424             /* Release the DIB section */
425             DeleteObject(This->dib.DIBsection);
426             This->dib.bitmap_data = NULL;
427             This->resource.allocatedMemory = NULL;
428             This->hDC = NULL;
429             This->flags &= ~SFLAG_DIBSECTION;
430         }
431         else if(!(This->flags & SFLAG_USERPTR))
432         {
433             release = This->resource.allocatedMemory;
434         }
435         This->resource.allocatedMemory = Mem;
436         This->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
437
438         /* Now free the old memory if any */
439         HeapFree(GetProcessHeap(), 0, release);
440     }
441     else if (This->flags & SFLAG_USERPTR)
442     {
443         /* Map() and GetDC() will re-create the dib section and allocated memory. */
444         This->resource.allocatedMemory = NULL;
445         This->flags &= ~SFLAG_USERPTR;
446     }
447     return WINED3D_OK;
448 }
449
450 static WINED3DSURFTYPE WINAPI IWineGDISurfaceImpl_GetImplType(IWineD3DSurface *iface) {
451     return SURFACE_GDI;
452 }
453
454 static HRESULT WINAPI IWineGDISurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
455     FIXME("GDI surfaces can't draw overlays yet\n");
456     return E_FAIL;
457 }
458
459 /* FIXME: This vtable should not use any IWineD3DSurface* implementation functions,
460  * only IWineD3DBaseSurface and IWineGDISurface ones.
461  */
462 const IWineD3DSurfaceVtbl IWineGDISurface_Vtbl =
463 {
464     /* IUnknown */
465     IWineD3DBaseSurfaceImpl_QueryInterface,
466     IWineD3DBaseSurfaceImpl_AddRef,
467     IWineGDISurfaceImpl_Release,
468     /* IWineD3DResource */
469     IWineD3DBaseSurfaceImpl_GetParent,
470     IWineD3DBaseSurfaceImpl_SetPrivateData,
471     IWineD3DBaseSurfaceImpl_GetPrivateData,
472     IWineD3DBaseSurfaceImpl_FreePrivateData,
473     IWineD3DBaseSurfaceImpl_SetPriority,
474     IWineD3DBaseSurfaceImpl_GetPriority,
475     IWineGDISurfaceImpl_PreLoad,
476     IWineGDISurfaceImpl_UnLoad,
477     IWineD3DBaseSurfaceImpl_GetType,
478     /* IWineD3DSurface */
479     IWineD3DBaseSurfaceImpl_GetDesc,
480     IWineGDISurfaceImpl_Map,
481     IWineGDISurfaceImpl_Unmap,
482     IWineGDISurfaceImpl_GetDC,
483     IWineGDISurfaceImpl_ReleaseDC,
484     IWineGDISurfaceImpl_Flip,
485     IWineD3DBaseSurfaceImpl_Blt,
486     IWineD3DBaseSurfaceImpl_GetBltStatus,
487     IWineD3DBaseSurfaceImpl_GetFlipStatus,
488     IWineD3DBaseSurfaceImpl_IsLost,
489     IWineD3DBaseSurfaceImpl_Restore,
490     IWineD3DBaseSurfaceImpl_BltFast,
491     IWineD3DBaseSurfaceImpl_GetPalette,
492     IWineD3DBaseSurfaceImpl_SetPalette,
493     IWineGDISurfaceImpl_RealizePalette,
494     IWineD3DBaseSurfaceImpl_SetColorKey,
495     IWineD3DBaseSurfaceImpl_GetPitch,
496     IWineGDISurfaceImpl_SetMem,
497     IWineD3DBaseSurfaceImpl_SetOverlayPosition,
498     IWineD3DBaseSurfaceImpl_GetOverlayPosition,
499     IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
500     IWineD3DBaseSurfaceImpl_UpdateOverlay,
501     IWineD3DBaseSurfaceImpl_SetClipper,
502     IWineD3DBaseSurfaceImpl_GetClipper,
503     /* Internal use: */
504     IWineGDISurfaceImpl_LoadTexture,
505     IWineGDISurfaceImpl_BindTexture,
506     IWineD3DBaseSurfaceImpl_GetData,
507     IWineD3DBaseSurfaceImpl_SetFormat,
508     IWineGDISurfaceImpl_PrivateSetup,
509     IWineGDISurfaceImpl_GetImplType,
510     IWineGDISurfaceImpl_DrawOverlay
511 };