wined3d: Rename wined3d_format_desc to wined3d_format.
[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((IWineD3DResource *)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 /*****************************************************************************
113  * IWineD3DSurface::LockRect, GDI version
114  *
115  * Locks the surface and returns a pointer to the surface memory
116  *
117  * Params:
118  *  pLockedRect: Address to return the locking info at
119  *  pRect: Rectangle to lock
120  *  Flags: Some flags
121  *
122  * Returns:
123  *  WINED3D_OK on success
124  *  WINED3DERR_INVALIDCALL on errors
125  *
126  *****************************************************************************/
127 static HRESULT WINAPI
128 IWineGDISurfaceImpl_LockRect(IWineD3DSurface *iface,
129                              WINED3DLOCKED_RECT* pLockedRect,
130                              CONST RECT* pRect,
131                              DWORD Flags)
132 {
133     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
134
135     /* Already locked? */
136     if(This->Flags & SFLAG_LOCKED)
137     {
138         WARN("(%p) Surface already locked\n", This);
139         /* What should I return here? */
140         return WINED3DERR_INVALIDCALL;
141     }
142     This->Flags |= SFLAG_LOCKED;
143
144     if(!This->resource.allocatedMemory) {
145         /* This happens on gdi surfaces if the application set a user pointer and resets it.
146          * Recreate the DIB section
147          */
148         IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
149         This->resource.allocatedMemory = This->dib.bitmap_data;
150     }
151
152     return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
153 }
154
155 /*****************************************************************************
156  * IWineD3DSurface::UnlockRect, GDI version
157  *
158  * Unlocks a surface. This implementation doesn't do much, except updating
159  * the window if the front buffer is unlocked
160  *
161  * Returns:
162  *  WINED3D_OK on success
163  *  WINED3DERR_INVALIDCALL on failure
164  *
165  *****************************************************************************/
166 static HRESULT WINAPI
167 IWineGDISurfaceImpl_UnlockRect(IWineD3DSurface *iface)
168 {
169     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
170     TRACE("(%p)\n", This);
171
172     if (!(This->Flags & SFLAG_LOCKED))
173     {
174         WARN("trying to Unlock an unlocked surf@%p\n", This);
175         return WINEDDERR_NOTLOCKED;
176     }
177
178     /* Tell the swapchain to update the screen */
179     if (This->container.type == WINED3D_CONTAINER_SWAPCHAIN)
180     {
181         IWineD3DSwapChainImpl *swapchain = This->container.u.swapchain;
182         if (This == swapchain->front_buffer)
183         {
184             x11_copy_to_screen(swapchain, &This->lockedRect);
185         }
186     }
187
188     This->Flags &= ~SFLAG_LOCKED;
189     memset(&This->lockedRect, 0, sizeof(RECT));
190     return WINED3D_OK;
191 }
192
193 /*****************************************************************************
194  * IWineD3DSurface::Flip, GDI version
195  *
196  * Flips 2 flipping enabled surfaces. Determining the 2 targets is done by
197  * the parent library. This implementation changes the data pointers of the
198  * surfaces and copies the new front buffer content to the screen
199  *
200  * Params:
201  *  override: Flipping target(e.g. back buffer)
202  *
203  * Returns:
204  *  WINED3D_OK on success
205  *
206  *****************************************************************************/
207 static HRESULT WINAPI
208 IWineGDISurfaceImpl_Flip(IWineD3DSurface *iface,
209                          IWineD3DSurface *override,
210                          DWORD Flags)
211 {
212     IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)iface;
213     IWineD3DSwapChainImpl *swapchain;
214     HRESULT hr;
215
216     if (surface->container.type != WINED3D_CONTAINER_SWAPCHAIN)
217     {
218         ERR("Flipped surface is not on a swapchain\n");
219         return WINEDDERR_NOTFLIPPABLE;
220     }
221
222     swapchain = surface->container.u.swapchain;
223     hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *)swapchain,
224             NULL, NULL, swapchain->win_handle, NULL, 0);
225
226     return hr;
227 }
228
229 /*****************************************************************************
230  * IWineD3DSurface::LoadTexture, GDI version
231  *
232  * This is mutually unsupported by GDI surfaces
233  *
234  * Returns:
235  *  D3DERR_INVALIDCALL
236  *
237  *****************************************************************************/
238 static HRESULT WINAPI
239 IWineGDISurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode)
240 {
241     ERR("Unsupported on X11 surfaces\n");
242     return WINED3DERR_INVALIDCALL;
243 }
244
245 static void WINAPI IWineGDISurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb)
246 {
247     ERR("Not supported.\n");
248 }
249
250 static HRESULT WINAPI IWineGDISurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
251     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
252     WINED3DLOCKED_RECT lock;
253     HRESULT hr;
254     RGBQUAD col[256];
255
256     TRACE("(%p)->(%p)\n",This,pHDC);
257
258     if(!(This->Flags & SFLAG_DIBSECTION))
259     {
260         WARN("DC not supported on this surface\n");
261         return WINED3DERR_INVALIDCALL;
262     }
263
264     if(This->Flags & SFLAG_USERPTR) {
265         ERR("Not supported on surfaces with an application-provided surfaces\n");
266         return WINEDDERR_NODC;
267     }
268
269     /* Give more detailed info for ddraw */
270     if (This->Flags & SFLAG_DCINUSE)
271         return WINEDDERR_DCALREADYCREATED;
272
273     /* Can't GetDC if the surface is locked */
274     if (This->Flags & SFLAG_LOCKED)
275         return WINED3DERR_INVALIDCALL;
276
277     memset(&lock, 0, sizeof(lock)); /* To be sure */
278
279     /* Should have a DIB section already */
280
281     /* Lock the surface */
282     hr = IWineD3DSurface_LockRect(iface,
283                                   &lock,
284                                   NULL,
285                                   0);
286     if(FAILED(hr)) {
287         ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
288         /* keep the dib section */
289         return hr;
290     }
291
292     if (This->resource.format->id == WINED3DFMT_P8_UINT
293             || This->resource.format->id == WINED3DFMT_P8_UINT_A8_UNORM)
294     {
295         unsigned int n;
296         const PALETTEENTRY *pal = NULL;
297
298         if(This->palette) {
299             pal = This->palette->palents;
300         } else {
301             IWineD3DSurfaceImpl *dds_primary;
302             IWineD3DSwapChainImpl *swapchain;
303             swapchain = (IWineD3DSwapChainImpl *)This->resource.device->swapchains[0];
304             dds_primary = swapchain->front_buffer;
305             if (dds_primary && dds_primary->palette)
306                 pal = dds_primary->palette->palents;
307         }
308
309         if (pal) {
310             for (n=0; n<256; n++) {
311                 col[n].rgbRed   = pal[n].peRed;
312                 col[n].rgbGreen = pal[n].peGreen;
313                 col[n].rgbBlue  = pal[n].peBlue;
314                 col[n].rgbReserved = 0;
315             }
316             SetDIBColorTable(This->hDC, 0, 256, col);
317         }
318     }
319
320     *pHDC = This->hDC;
321     TRACE("returning %p\n",*pHDC);
322     This->Flags |= SFLAG_DCINUSE;
323
324     return WINED3D_OK;
325 }
326
327 static HRESULT WINAPI IWineGDISurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
328     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
329
330     TRACE("(%p)->(%p)\n",This,hDC);
331
332     if (!(This->Flags & SFLAG_DCINUSE))
333         return WINEDDERR_NODC;
334
335     if (This->hDC !=hDC) {
336         WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
337         return WINEDDERR_NODC;
338     }
339
340     /* we locked first, so unlock now */
341     IWineD3DSurface_UnlockRect(iface);
342
343     This->Flags &= ~SFLAG_DCINUSE;
344
345     return WINED3D_OK;
346 }
347
348 static HRESULT WINAPI IWineGDISurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
349     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
350     RGBQUAD col[256];
351     IWineD3DPaletteImpl *pal = This->palette;
352     unsigned int n;
353     TRACE("(%p)\n", This);
354
355     if (!pal) return WINED3D_OK;
356
357     if(This->Flags & SFLAG_DIBSECTION) {
358         TRACE("(%p): Updating the hdc's palette\n", This);
359         for (n=0; n<256; n++) {
360             col[n].rgbRed   = pal->palents[n].peRed;
361             col[n].rgbGreen = pal->palents[n].peGreen;
362             col[n].rgbBlue  = pal->palents[n].peBlue;
363             col[n].rgbReserved = 0;
364         }
365         SetDIBColorTable(This->hDC, 0, 256, col);
366     }
367
368     /* Update the image because of the palette change. Some games like e.g Red Alert
369        call SetEntries a lot to implement fading. */
370     /* Tell the swapchain to update the screen */
371     if (This->container.type == WINED3D_CONTAINER_SWAPCHAIN)
372     {
373         IWineD3DSwapChainImpl *swapchain = This->container.u.swapchain;
374         if (This == swapchain->front_buffer)
375         {
376             x11_copy_to_screen(swapchain, NULL);
377         }
378     }
379
380     return WINED3D_OK;
381 }
382
383 /*****************************************************************************
384  * IWineD3DSurface::PrivateSetup, GDI version
385  *
386  * Initializes the GDI surface, aka creates the DIB section we render to
387  * The DIB section creation is done by calling GetDC, which will create the
388  * section and releasing the dc to allow the app to use it. The dib section
389  * will stay until the surface is released
390  *
391  * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
392  * are set to the real sizes to save memory. The NONPOW2 flag is unset to
393  * avoid confusion in the shared surface code.
394  *
395  * Returns:
396  *  WINED3D_OK on success
397  *  The return values of called methods on failure
398  *
399  *****************************************************************************/
400 static HRESULT WINAPI
401 IWineGDISurfaceImpl_PrivateSetup(IWineD3DSurface *iface)
402 {
403     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
404     HRESULT hr;
405
406     if(This->resource.usage & WINED3DUSAGE_OVERLAY)
407     {
408         ERR("(%p) Overlays not yet supported by GDI surfaces\n", This);
409         return WINED3DERR_INVALIDCALL;
410     }
411
412     /* Sysmem textures have memory already allocated -
413      * release it, this avoids an unnecessary memcpy
414      */
415     hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
416     if(SUCCEEDED(hr))
417     {
418         HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
419         This->resource.heapMemory = NULL;
420         This->resource.allocatedMemory = This->dib.bitmap_data;
421     }
422
423     /* We don't mind the nonpow2 stuff in GDI */
424     This->pow2Width = This->currentDesc.Width;
425     This->pow2Height = This->currentDesc.Height;
426
427     return WINED3D_OK;
428 }
429
430 static HRESULT WINAPI IWineGDISurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
431     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
432
433     /* Render targets depend on their hdc, and we can't create an hdc on a user pointer */
434     if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
435         ERR("Not supported on render targets\n");
436         return WINED3DERR_INVALIDCALL;
437     }
438
439     if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
440         WARN("Surface is locked or the HDC is in use\n");
441         return WINED3DERR_INVALIDCALL;
442     }
443
444     if(Mem && Mem != This->resource.allocatedMemory) {
445         void *release = NULL;
446
447         /* Do I have to copy the old surface content? */
448         if(This->Flags & SFLAG_DIBSECTION) {
449                 /* Release the DC. No need to hold the critical section for the update
450             * Thread because this thread runs only on front buffers, but this method
451             * fails for render targets in the check above.
452                 */
453             SelectObject(This->hDC, This->dib.holdbitmap);
454             DeleteDC(This->hDC);
455             /* Release the DIB section */
456             DeleteObject(This->dib.DIBsection);
457             This->dib.bitmap_data = NULL;
458             This->resource.allocatedMemory = NULL;
459             This->hDC = NULL;
460             This->Flags &= ~SFLAG_DIBSECTION;
461         } else if(!(This->Flags & SFLAG_USERPTR)) {
462             release = This->resource.allocatedMemory;
463         }
464         This->resource.allocatedMemory = Mem;
465         This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
466
467         /* Now free the old memory if any */
468         HeapFree(GetProcessHeap(), 0, release);
469     } else if(This->Flags & SFLAG_USERPTR) {
470         /* LockRect and GetDC will re-create the dib section and allocated memory */
471         This->resource.allocatedMemory = NULL;
472         This->Flags &= ~SFLAG_USERPTR;
473     }
474     return WINED3D_OK;
475 }
476
477 static WINED3DSURFTYPE WINAPI IWineGDISurfaceImpl_GetImplType(IWineD3DSurface *iface) {
478     return SURFACE_GDI;
479 }
480
481 static HRESULT WINAPI IWineGDISurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
482     FIXME("GDI surfaces can't draw overlays yet\n");
483     return E_FAIL;
484 }
485
486 /* FIXME: This vtable should not use any IWineD3DSurface* implementation functions,
487  * only IWineD3DBaseSurface and IWineGDISurface ones.
488  */
489 const IWineD3DSurfaceVtbl IWineGDISurface_Vtbl =
490 {
491     /* IUnknown */
492     IWineD3DBaseSurfaceImpl_QueryInterface,
493     IWineD3DBaseSurfaceImpl_AddRef,
494     IWineGDISurfaceImpl_Release,
495     /* IWineD3DResource */
496     IWineD3DBaseSurfaceImpl_GetParent,
497     IWineD3DBaseSurfaceImpl_SetPrivateData,
498     IWineD3DBaseSurfaceImpl_GetPrivateData,
499     IWineD3DBaseSurfaceImpl_FreePrivateData,
500     IWineD3DBaseSurfaceImpl_SetPriority,
501     IWineD3DBaseSurfaceImpl_GetPriority,
502     IWineGDISurfaceImpl_PreLoad,
503     IWineGDISurfaceImpl_UnLoad,
504     IWineD3DBaseSurfaceImpl_GetType,
505     /* IWineD3DSurface */
506     IWineD3DBaseSurfaceImpl_GetDesc,
507     IWineGDISurfaceImpl_LockRect,
508     IWineGDISurfaceImpl_UnlockRect,
509     IWineGDISurfaceImpl_GetDC,
510     IWineGDISurfaceImpl_ReleaseDC,
511     IWineGDISurfaceImpl_Flip,
512     IWineD3DBaseSurfaceImpl_Blt,
513     IWineD3DBaseSurfaceImpl_GetBltStatus,
514     IWineD3DBaseSurfaceImpl_GetFlipStatus,
515     IWineD3DBaseSurfaceImpl_IsLost,
516     IWineD3DBaseSurfaceImpl_Restore,
517     IWineD3DBaseSurfaceImpl_BltFast,
518     IWineD3DBaseSurfaceImpl_GetPalette,
519     IWineD3DBaseSurfaceImpl_SetPalette,
520     IWineGDISurfaceImpl_RealizePalette,
521     IWineD3DBaseSurfaceImpl_SetColorKey,
522     IWineD3DBaseSurfaceImpl_GetPitch,
523     IWineGDISurfaceImpl_SetMem,
524     IWineD3DBaseSurfaceImpl_SetOverlayPosition,
525     IWineD3DBaseSurfaceImpl_GetOverlayPosition,
526     IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
527     IWineD3DBaseSurfaceImpl_UpdateOverlay,
528     IWineD3DBaseSurfaceImpl_SetClipper,
529     IWineD3DBaseSurfaceImpl_GetClipper,
530     /* Internal use: */
531     IWineGDISurfaceImpl_LoadTexture,
532     IWineGDISurfaceImpl_BindTexture,
533     IWineD3DBaseSurfaceImpl_GetData,
534     IWineD3DBaseSurfaceImpl_SetFormat,
535     IWineGDISurfaceImpl_PrivateSetup,
536     IWineGDISurfaceImpl_GetImplType,
537     IWineGDISurfaceImpl_DrawOverlay
538 };