winex11.drv: Quiet a noisy FIXME.
[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 static void gdi_surface_realize_palette(IWineD3DSurfaceImpl *surface)
61 {
62     struct wined3d_palette *palette = surface->palette;
63
64     TRACE("surface %p.\n", surface);
65
66     if (!palette) return;
67
68     if (surface->flags & SFLAG_DIBSECTION)
69     {
70         RGBQUAD col[256];
71         unsigned int i;
72
73         TRACE("Updating the DC's palette.\n");
74
75         for (i = 0; i < 256; ++i)
76         {
77             col[i].rgbRed   = palette->palents[i].peRed;
78             col[i].rgbGreen = palette->palents[i].peGreen;
79             col[i].rgbBlue  = palette->palents[i].peBlue;
80             col[i].rgbReserved = 0;
81         }
82         SetDIBColorTable(surface->hDC, 0, 256, col);
83     }
84
85     /* Update the image because of the palette change. Some games like e.g.
86      * Red Alert call SetEntries a lot to implement fading. */
87     /* Tell the swapchain to update the screen. */
88     if (surface->container.type == WINED3D_CONTAINER_SWAPCHAIN)
89     {
90         IWineD3DSwapChainImpl *swapchain = surface->container.u.swapchain;
91         if (surface == swapchain->front_buffer)
92         {
93             x11_copy_to_screen(swapchain, NULL);
94         }
95     }
96 }
97
98 static HRESULT gdi_surface_draw_overlay(IWineD3DSurfaceImpl *surface)
99 {
100     FIXME("GDI surfaces can't draw overlays yet.\n");
101     return E_FAIL;
102 }
103
104 static const struct wined3d_surface_ops gdi_surface_ops =
105 {
106     gdi_surface_realize_palette,
107     gdi_surface_draw_overlay,
108 };
109
110 /*****************************************************************************
111  * IWineD3DSurface::Release, GDI version
112  *
113  * In general a normal COM Release method, but the GDI version doesn't have
114  * to destroy all the GL things.
115  *
116  *****************************************************************************/
117 static ULONG WINAPI IWineGDISurfaceImpl_Release(IWineD3DSurface *iface) {
118     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
119     ULONG ref = InterlockedDecrement(&This->resource.ref);
120     TRACE("(%p) : Releasing from %d\n", This, ref + 1);
121
122     if (!ref)
123     {
124         surface_gdi_cleanup(This);
125
126         TRACE("(%p) Released.\n", This);
127         HeapFree(GetProcessHeap(), 0, This);
128     }
129
130     return ref;
131 }
132
133 /*****************************************************************************
134  * IWineD3DSurface::PreLoad, GDI version
135  *
136  * This call is unsupported on GDI surfaces, if it's called something went
137  * wrong in the parent library. Write an informative warning
138  *
139  *****************************************************************************/
140 static void WINAPI
141 IWineGDISurfaceImpl_PreLoad(IWineD3DSurface *iface)
142 {
143     ERR("(%p): PreLoad is not supported on X11 surfaces!\n", iface);
144     ERR("(%p): Most likely the parent library did something wrong.\n", iface);
145     ERR("(%p): Please report to wine-devel\n", iface);
146 }
147
148 static HRESULT WINAPI IWineGDISurfaceImpl_Map(IWineD3DSurface *iface,
149         WINED3DLOCKED_RECT *pLockedRect, const RECT *pRect, DWORD flags)
150 {
151     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
152
153     /* Already locked? */
154     if (This->flags & SFLAG_LOCKED)
155     {
156         WARN("(%p) Surface already locked\n", This);
157         /* What should I return here? */
158         return WINED3DERR_INVALIDCALL;
159     }
160     This->flags |= SFLAG_LOCKED;
161
162     if(!This->resource.allocatedMemory) {
163         /* This happens on gdi surfaces if the application set a user pointer and resets it.
164          * Recreate the DIB section
165          */
166         IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
167         This->resource.allocatedMemory = This->dib.bitmap_data;
168     }
169
170     return IWineD3DBaseSurfaceImpl_Map(iface, pLockedRect, pRect, flags);
171 }
172
173 static HRESULT WINAPI IWineGDISurfaceImpl_Unmap(IWineD3DSurface *iface)
174 {
175     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
176     TRACE("(%p)\n", This);
177
178     if (!(This->flags & SFLAG_LOCKED))
179     {
180         WARN("Trying to unmap unmapped surfaces %p.\n", iface);
181         return WINEDDERR_NOTLOCKED;
182     }
183
184     /* Tell the swapchain to update the screen */
185     if (This->container.type == WINED3D_CONTAINER_SWAPCHAIN)
186     {
187         IWineD3DSwapChainImpl *swapchain = This->container.u.swapchain;
188         if (This == swapchain->front_buffer)
189         {
190             x11_copy_to_screen(swapchain, &This->lockedRect);
191         }
192     }
193
194     This->flags &= ~SFLAG_LOCKED;
195     memset(&This->lockedRect, 0, sizeof(RECT));
196     return WINED3D_OK;
197 }
198
199 /*****************************************************************************
200  * IWineD3DSurface::Flip, GDI version
201  *
202  * Flips 2 flipping enabled surfaces. Determining the 2 targets is done by
203  * the parent library. This implementation changes the data pointers of the
204  * surfaces and copies the new front buffer content to the screen
205  *
206  * Params:
207  *  override: Flipping target(e.g. back buffer)
208  *
209  * Returns:
210  *  WINED3D_OK on success
211  *
212  *****************************************************************************/
213 static HRESULT WINAPI IWineGDISurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD flags)
214 {
215     IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
216     IWineD3DSwapChainImpl *swapchain;
217     HRESULT hr;
218
219     if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
220     {
221         ERR("Flipped surface is not on a swapchain\n");
222         return WINEDDERR_NOTFLIPPABLE;
223     }
224
225     swapchain = surface->container.u.swapchain;
226     hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *)swapchain,
227             NULL, NULL, swapchain->win_handle, NULL, 0);
228
229     return hr;
230 }
231
232 static HRESULT WINAPI IWineGDISurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
233     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
234     WINED3DLOCKED_RECT lock;
235     HRESULT hr;
236     RGBQUAD col[256];
237
238     TRACE("(%p)->(%p)\n",This,pHDC);
239
240     if (!(This->flags & SFLAG_DIBSECTION))
241     {
242         WARN("DC not supported on this surface\n");
243         return WINED3DERR_INVALIDCALL;
244     }
245
246     if (This->flags & SFLAG_USERPTR)
247     {
248         ERR("Not supported on surfaces with an application-provided surfaces\n");
249         return WINEDDERR_NODC;
250     }
251
252     /* Give more detailed info for ddraw */
253     if (This->flags & SFLAG_DCINUSE)
254         return WINEDDERR_DCALREADYCREATED;
255
256     /* Can't GetDC if the surface is locked */
257     if (This->flags & SFLAG_LOCKED)
258         return WINED3DERR_INVALIDCALL;
259
260     memset(&lock, 0, sizeof(lock)); /* To be sure */
261
262     /* Should have a DIB section already */
263
264     /* Map the surface. */
265     hr = IWineD3DSurface_Map(iface, &lock, NULL, 0);
266     if (FAILED(hr))
267     {
268         ERR("IWineD3DSurface_Map failed, hr %#x.\n", hr);
269         /* keep the dib section */
270         return hr;
271     }
272
273     if (This->resource.format->id == WINED3DFMT_P8_UINT
274             || This->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
275     {
276         unsigned int n;
277         const PALETTEENTRY *pal = NULL;
278
279         if(This->palette) {
280             pal = This->palette->palents;
281         } else {
282             IWineD3DSurfaceImpl *dds_primary;
283             IWineD3DSwapChainImpl *swapchain;
284             swapchain = This->resource.device->swapchains[0];
285             dds_primary = swapchain->front_buffer;
286             if (dds_primary && dds_primary->palette)
287                 pal = dds_primary->palette->palents;
288         }
289
290         if (pal) {
291             for (n=0; n<256; n++) {
292                 col[n].rgbRed   = pal[n].peRed;
293                 col[n].rgbGreen = pal[n].peGreen;
294                 col[n].rgbBlue  = pal[n].peBlue;
295                 col[n].rgbReserved = 0;
296             }
297             SetDIBColorTable(This->hDC, 0, 256, col);
298         }
299     }
300
301     *pHDC = This->hDC;
302     TRACE("returning %p\n",*pHDC);
303     This->flags |= SFLAG_DCINUSE;
304
305     return WINED3D_OK;
306 }
307
308 static HRESULT WINAPI IWineGDISurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
309     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
310
311     TRACE("(%p)->(%p)\n",This,hDC);
312
313     if (!(This->flags & SFLAG_DCINUSE))
314         return WINEDDERR_NODC;
315
316     if (This->hDC !=hDC) {
317         WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
318         return WINEDDERR_NODC;
319     }
320
321     /* we locked first, so unlock now */
322     IWineD3DSurface_Unmap(iface);
323
324     This->flags &= ~SFLAG_DCINUSE;
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     This->surface_ops = &gdi_surface_ops;
353
354     if(This->resource.usage & WINED3DUSAGE_OVERLAY)
355     {
356         ERR("(%p) Overlays not yet supported by GDI surfaces\n", This);
357         return WINED3DERR_INVALIDCALL;
358     }
359
360     /* Sysmem textures have memory already allocated -
361      * release it, this avoids an unnecessary memcpy
362      */
363     hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
364     if(SUCCEEDED(hr))
365     {
366         HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
367         This->resource.heapMemory = NULL;
368         This->resource.allocatedMemory = This->dib.bitmap_data;
369     }
370
371     /* We don't mind the nonpow2 stuff in GDI */
372     This->pow2Width = This->currentDesc.Width;
373     This->pow2Height = This->currentDesc.Height;
374
375     return WINED3D_OK;
376 }
377
378 static HRESULT WINAPI IWineGDISurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
379     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
380
381     /* Render targets depend on their hdc, and we can't create an hdc on a user pointer */
382     if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
383         ERR("Not supported on render targets\n");
384         return WINED3DERR_INVALIDCALL;
385     }
386
387     if (This->flags & (SFLAG_LOCKED | SFLAG_DCINUSE))
388     {
389         WARN("Surface is locked or the HDC is in use\n");
390         return WINED3DERR_INVALIDCALL;
391     }
392
393     if(Mem && Mem != This->resource.allocatedMemory) {
394         void *release = NULL;
395
396         /* Do I have to copy the old surface content? */
397         if (This->flags & SFLAG_DIBSECTION)
398         {
399                 /* Release the DC. No need to hold the critical section for the update
400             * Thread because this thread runs only on front buffers, but this method
401             * fails for render targets in the check above.
402                 */
403             SelectObject(This->hDC, This->dib.holdbitmap);
404             DeleteDC(This->hDC);
405             /* Release the DIB section */
406             DeleteObject(This->dib.DIBsection);
407             This->dib.bitmap_data = NULL;
408             This->resource.allocatedMemory = NULL;
409             This->hDC = NULL;
410             This->flags &= ~SFLAG_DIBSECTION;
411         }
412         else if(!(This->flags & SFLAG_USERPTR))
413         {
414             release = This->resource.allocatedMemory;
415         }
416         This->resource.allocatedMemory = Mem;
417         This->flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
418
419         /* Now free the old memory if any */
420         HeapFree(GetProcessHeap(), 0, release);
421     }
422     else if (This->flags & SFLAG_USERPTR)
423     {
424         /* Map() and GetDC() will re-create the dib section and allocated memory. */
425         This->resource.allocatedMemory = NULL;
426         This->flags &= ~SFLAG_USERPTR;
427     }
428     return WINED3D_OK;
429 }
430
431 static WINED3DSURFTYPE WINAPI IWineGDISurfaceImpl_GetImplType(IWineD3DSurface *iface) {
432     return SURFACE_GDI;
433 }
434
435 /* FIXME: This vtable should not use any IWineD3DSurface* implementation functions,
436  * only IWineD3DBaseSurface and IWineGDISurface ones.
437  */
438 const IWineD3DSurfaceVtbl IWineGDISurface_Vtbl =
439 {
440     /* IUnknown */
441     IWineD3DBaseSurfaceImpl_QueryInterface,
442     IWineD3DBaseSurfaceImpl_AddRef,
443     IWineGDISurfaceImpl_Release,
444     /* IWineD3DResource */
445     IWineD3DBaseSurfaceImpl_GetParent,
446     IWineD3DBaseSurfaceImpl_SetPrivateData,
447     IWineD3DBaseSurfaceImpl_GetPrivateData,
448     IWineD3DBaseSurfaceImpl_FreePrivateData,
449     IWineD3DBaseSurfaceImpl_SetPriority,
450     IWineD3DBaseSurfaceImpl_GetPriority,
451     IWineGDISurfaceImpl_PreLoad,
452     IWineD3DBaseSurfaceImpl_GetType,
453     /* IWineD3DSurface */
454     IWineD3DBaseSurfaceImpl_GetDesc,
455     IWineGDISurfaceImpl_Map,
456     IWineGDISurfaceImpl_Unmap,
457     IWineGDISurfaceImpl_GetDC,
458     IWineGDISurfaceImpl_ReleaseDC,
459     IWineGDISurfaceImpl_Flip,
460     IWineD3DBaseSurfaceImpl_Blt,
461     IWineD3DBaseSurfaceImpl_GetBltStatus,
462     IWineD3DBaseSurfaceImpl_GetFlipStatus,
463     IWineD3DBaseSurfaceImpl_IsLost,
464     IWineD3DBaseSurfaceImpl_Restore,
465     IWineD3DBaseSurfaceImpl_BltFast,
466     IWineD3DBaseSurfaceImpl_GetPalette,
467     IWineD3DBaseSurfaceImpl_SetPalette,
468     IWineD3DBaseSurfaceImpl_SetColorKey,
469     IWineD3DBaseSurfaceImpl_GetPitch,
470     IWineGDISurfaceImpl_SetMem,
471     IWineD3DBaseSurfaceImpl_SetOverlayPosition,
472     IWineD3DBaseSurfaceImpl_GetOverlayPosition,
473     IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
474     IWineD3DBaseSurfaceImpl_UpdateOverlay,
475     IWineD3DBaseSurfaceImpl_SetClipper,
476     IWineD3DBaseSurfaceImpl_GetClipper,
477     /* Internal use: */
478     IWineD3DBaseSurfaceImpl_SetFormat,
479     IWineGDISurfaceImpl_PrivateSetup,
480     IWineGDISurfaceImpl_GetImplType,
481 };