ddrawex: Rename impl_from_dd4() to impl_from_IDirectDraw4().
[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 static HRESULT WINAPI IWineGDISurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
197     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
198     WINED3DLOCKED_RECT lock;
199     HRESULT hr;
200     RGBQUAD col[256];
201
202     TRACE("(%p)->(%p)\n",This,pHDC);
203
204     if (!(This->flags & SFLAG_DIBSECTION))
205     {
206         WARN("DC not supported on this surface\n");
207         return WINED3DERR_INVALIDCALL;
208     }
209
210     if (This->flags & SFLAG_USERPTR)
211     {
212         ERR("Not supported on surfaces with an application-provided surfaces\n");
213         return WINEDDERR_NODC;
214     }
215
216     /* Give more detailed info for ddraw */
217     if (This->flags & SFLAG_DCINUSE)
218         return WINEDDERR_DCALREADYCREATED;
219
220     /* Can't GetDC if the surface is locked */
221     if (This->flags & SFLAG_LOCKED)
222         return WINED3DERR_INVALIDCALL;
223
224     memset(&lock, 0, sizeof(lock)); /* To be sure */
225
226     /* Should have a DIB section already */
227
228     /* Map the surface. */
229     hr = IWineD3DSurface_Map(iface, &lock, NULL, 0);
230     if (FAILED(hr))
231     {
232         ERR("IWineD3DSurface_Map failed, hr %#x.\n", hr);
233         /* keep the dib section */
234         return hr;
235     }
236
237     if (This->resource.format->id == WINED3DFMT_P8_UINT
238             || This->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
239     {
240         unsigned int n;
241         const PALETTEENTRY *pal = NULL;
242
243         if(This->palette) {
244             pal = This->palette->palents;
245         } else {
246             IWineD3DSurfaceImpl *dds_primary;
247             IWineD3DSwapChainImpl *swapchain;
248             swapchain = This->resource.device->swapchains[0];
249             dds_primary = swapchain->front_buffer;
250             if (dds_primary && dds_primary->palette)
251                 pal = dds_primary->palette->palents;
252         }
253
254         if (pal) {
255             for (n=0; n<256; n++) {
256                 col[n].rgbRed   = pal[n].peRed;
257                 col[n].rgbGreen = pal[n].peGreen;
258                 col[n].rgbBlue  = pal[n].peBlue;
259                 col[n].rgbReserved = 0;
260             }
261             SetDIBColorTable(This->hDC, 0, 256, col);
262         }
263     }
264
265     *pHDC = This->hDC;
266     TRACE("returning %p\n",*pHDC);
267     This->flags |= SFLAG_DCINUSE;
268
269     return WINED3D_OK;
270 }
271
272 static HRESULT WINAPI IWineGDISurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
273     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
274
275     TRACE("(%p)->(%p)\n",This,hDC);
276
277     if (!(This->flags & SFLAG_DCINUSE))
278         return WINEDDERR_NODC;
279
280     if (This->hDC !=hDC) {
281         WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
282         return WINEDDERR_NODC;
283     }
284
285     /* we locked first, so unlock now */
286     IWineD3DSurface_Unmap(iface);
287
288     This->flags &= ~SFLAG_DCINUSE;
289
290     return WINED3D_OK;
291 }
292
293 static HRESULT WINAPI IWineGDISurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
294     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
295     RGBQUAD col[256];
296     IWineD3DPaletteImpl *pal = This->palette;
297     unsigned int n;
298     TRACE("(%p)\n", This);
299
300     if (!pal) return WINED3D_OK;
301
302     if (This->flags & SFLAG_DIBSECTION)
303     {
304         TRACE("(%p): Updating the hdc's palette\n", This);
305         for (n=0; n<256; n++) {
306             col[n].rgbRed   = pal->palents[n].peRed;
307             col[n].rgbGreen = pal->palents[n].peGreen;
308             col[n].rgbBlue  = pal->palents[n].peBlue;
309             col[n].rgbReserved = 0;
310         }
311         SetDIBColorTable(This->hDC, 0, 256, col);
312     }
313
314     /* Update the image because of the palette change. Some games like e.g Red Alert
315        call SetEntries a lot to implement fading. */
316     /* Tell the swapchain to update the screen */
317     if (This->container.type == WINED3D_CONTAINER_SWAPCHAIN)
318     {
319         IWineD3DSwapChainImpl *swapchain = This->container.u.swapchain;
320         if (This == swapchain->front_buffer)
321         {
322             x11_copy_to_screen(swapchain, NULL);
323         }
324     }
325
326     return WINED3D_OK;
327 }
328
329 /*****************************************************************************
330  * IWineD3DSurface::PrivateSetup, GDI version
331  *
332  * Initializes the GDI surface, aka creates the DIB section we render to
333  * The DIB section creation is done by calling GetDC, which will create the
334  * section and releasing the dc to allow the app to use it. The dib section
335  * will stay until the surface is released
336  *
337  * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
338  * are set to the real sizes to save memory. The NONPOW2 flag is unset to
339  * avoid confusion in the shared surface code.
340  *
341  * Returns:
342  *  WINED3D_OK on success
343  *  The return values of called methods on failure
344  *
345  *****************************************************************************/
346 static HRESULT WINAPI
347 IWineGDISurfaceImpl_PrivateSetup(IWineD3DSurface *iface)
348 {
349     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
350     HRESULT hr;
351
352     if(This->resource.usage & WINED3DUSAGE_OVERLAY)
353     {
354         ERR("(%p) Overlays not yet supported by GDI surfaces\n", This);
355         return WINED3DERR_INVALIDCALL;
356     }
357
358     /* Sysmem textures have memory already allocated -
359      * release it, this avoids an unnecessary memcpy
360      */
361     hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
362     if(SUCCEEDED(hr))
363     {
364         HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
365         This->resource.heapMemory = NULL;
366         This->resource.allocatedMemory = This->dib.bitmap_data;
367     }
368
369     /* We don't mind the nonpow2 stuff in GDI */
370     This->pow2Width = This->currentDesc.Width;
371     This->pow2Height = This->currentDesc.Height;
372
373     return WINED3D_OK;
374 }
375
376 static HRESULT WINAPI IWineGDISurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
377     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
378
379     /* Render targets depend on their hdc, and we can't create an hdc on a user pointer */
380     if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
381         ERR("Not supported on render targets\n");
382         return WINED3DERR_INVALIDCALL;
383     }
384
385     if (This->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
386     {
387         WARN("Surface is locked or the HDC is in use\n");
388         return WINED3DERR_INVALIDCALL;
389     }
390
391     if(Mem && Mem != This->resource.allocatedMemory) {
392         void *release = NULL;
393
394         /* Do I have to copy the old surface content? */
395         if (This->flags & SFLAG_DIBSECTION)
396         {
397                 /* Release the DC. No need to hold the critical section for the update
398             * Thread because this thread runs only on front buffers, but this method
399             * fails for render targets in the check above.
400                 */
401             SelectObject(This->hDC, This->dib.holdbitmap);
402             DeleteDC(This->hDC);
403             /* Release the DIB section */
404             DeleteObject(This->dib.DIBsection);
405             This->dib.bitmap_data = NULL;
406             This->resource.allocatedMemory = NULL;
407             This->hDC = NULL;
408             This->flags &= ~SFLAG_DIBSECTION;
409         }
410         else if(!(This->flags & SFLAG_USERPTR))
411         {
412             release = This->resource.allocatedMemory;
413         }
414         This->resource.allocatedMemory = Mem;
415         This->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
416
417         /* Now free the old memory if any */
418         HeapFree(GetProcessHeap(), 0, release);
419     }
420     else if (This->flags & SFLAG_USERPTR)
421     {
422         /* Map() and GetDC() will re-create the dib section and allocated memory. */
423         This->resource.allocatedMemory = NULL;
424         This->flags &= ~SFLAG_USERPTR;
425     }
426     return WINED3D_OK;
427 }
428
429 static WINED3DSURFTYPE WINAPI IWineGDISurfaceImpl_GetImplType(IWineD3DSurface *iface) {
430     return SURFACE_GDI;
431 }
432
433 static HRESULT WINAPI IWineGDISurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
434     FIXME("GDI surfaces can't draw overlays yet\n");
435     return E_FAIL;
436 }
437
438 /* FIXME: This vtable should not use any IWineD3DSurface* implementation functions,
439  * only IWineD3DBaseSurface and IWineGDISurface ones.
440  */
441 const IWineD3DSurfaceVtbl IWineGDISurface_Vtbl =
442 {
443     /* IUnknown */
444     IWineD3DBaseSurfaceImpl_QueryInterface,
445     IWineD3DBaseSurfaceImpl_AddRef,
446     IWineGDISurfaceImpl_Release,
447     /* IWineD3DResource */
448     IWineD3DBaseSurfaceImpl_GetParent,
449     IWineD3DBaseSurfaceImpl_SetPrivateData,
450     IWineD3DBaseSurfaceImpl_GetPrivateData,
451     IWineD3DBaseSurfaceImpl_FreePrivateData,
452     IWineD3DBaseSurfaceImpl_SetPriority,
453     IWineD3DBaseSurfaceImpl_GetPriority,
454     IWineGDISurfaceImpl_PreLoad,
455     IWineGDISurfaceImpl_UnLoad,
456     IWineD3DBaseSurfaceImpl_GetType,
457     /* IWineD3DSurface */
458     IWineD3DBaseSurfaceImpl_GetDesc,
459     IWineGDISurfaceImpl_Map,
460     IWineGDISurfaceImpl_Unmap,
461     IWineGDISurfaceImpl_GetDC,
462     IWineGDISurfaceImpl_ReleaseDC,
463     IWineGDISurfaceImpl_Flip,
464     IWineD3DBaseSurfaceImpl_Blt,
465     IWineD3DBaseSurfaceImpl_GetBltStatus,
466     IWineD3DBaseSurfaceImpl_GetFlipStatus,
467     IWineD3DBaseSurfaceImpl_IsLost,
468     IWineD3DBaseSurfaceImpl_Restore,
469     IWineD3DBaseSurfaceImpl_BltFast,
470     IWineD3DBaseSurfaceImpl_GetPalette,
471     IWineD3DBaseSurfaceImpl_SetPalette,
472     IWineGDISurfaceImpl_RealizePalette,
473     IWineD3DBaseSurfaceImpl_SetColorKey,
474     IWineD3DBaseSurfaceImpl_GetPitch,
475     IWineGDISurfaceImpl_SetMem,
476     IWineD3DBaseSurfaceImpl_SetOverlayPosition,
477     IWineD3DBaseSurfaceImpl_GetOverlayPosition,
478     IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
479     IWineD3DBaseSurfaceImpl_UpdateOverlay,
480     IWineD3DBaseSurfaceImpl_SetClipper,
481     IWineD3DBaseSurfaceImpl_GetClipper,
482     /* Internal use: */
483     IWineD3DBaseSurfaceImpl_SetFormat,
484     IWineGDISurfaceImpl_PrivateSetup,
485     IWineGDISurfaceImpl_GetImplType,
486     IWineGDISurfaceImpl_DrawOverlay
487 };