oleaut32: Add a test for loading/saving an empty picture.
[wine] / dlls / ddraw / clipper.c
1 /* DirectDrawClipper implementation
2  *
3  * Copyright 2000 (c) Marcus Meissner
4  * Copyright 2000 (c) TransGaming Technologies Inc.
5  * Copyright 2006 (c) Stefan Dösinger
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include "ddraw_private.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(ddraw);
28
29 static inline struct ddraw_clipper *impl_from_IDirectDrawClipper(IDirectDrawClipper *iface)
30 {
31     return CONTAINING_RECORD(iface, struct ddraw_clipper, IDirectDrawClipper_iface);
32 }
33
34 static HRESULT WINAPI ddraw_clipper_QueryInterface(IDirectDrawClipper *iface, REFIID iid, void **object)
35 {
36     struct ddraw_clipper *clipper = impl_from_IDirectDrawClipper(iface);
37
38     TRACE("iface %p, iid %s, object %p.\n", iface, debugstr_guid(iid), object);
39
40     if (IsEqualGUID(&IID_IDirectDrawClipper, iid)
41             || IsEqualGUID(&IID_IUnknown, iid))
42     {
43         IDirectDrawClipper_AddRef(&clipper->IDirectDrawClipper_iface);
44         *object = &clipper->IDirectDrawClipper_iface;
45         return S_OK;
46     }
47
48     WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
49     *object = NULL;
50
51     return E_NOINTERFACE;
52 }
53
54 static ULONG WINAPI ddraw_clipper_AddRef(IDirectDrawClipper *iface)
55 {
56     struct ddraw_clipper *clipper = impl_from_IDirectDrawClipper(iface);
57     ULONG refcount = InterlockedIncrement(&clipper->ref);
58
59     TRACE("%p increasing refcount to %u.\n", clipper, refcount);
60
61     return refcount;
62 }
63
64 static ULONG WINAPI ddraw_clipper_Release(IDirectDrawClipper *iface)
65 {
66     struct ddraw_clipper *clipper = impl_from_IDirectDrawClipper(iface);
67     ULONG refcount = InterlockedDecrement(&clipper->ref);
68
69     TRACE("%p decreasing refcount to %u.\n", clipper, refcount);
70
71     if (!refcount)
72     {
73         if (clipper->region)
74             DeleteObject(clipper->region);
75         HeapFree(GetProcessHeap(), 0, clipper);
76     }
77
78     return refcount;
79 }
80
81 static HRESULT WINAPI ddraw_clipper_SetHWnd(IDirectDrawClipper *iface, DWORD flags, HWND window)
82 {
83     struct ddraw_clipper *clipper = impl_from_IDirectDrawClipper(iface);
84
85     TRACE("iface %p, flags %#x, window %p.\n", iface, flags, window);
86
87     if (flags)
88     {
89         FIXME("flags %#x, not supported.\n", flags);
90         return DDERR_INVALIDPARAMS;
91     }
92
93     wined3d_mutex_lock();
94     clipper->window = window;
95     wined3d_mutex_unlock();
96
97     return DD_OK;
98 }
99
100 static HRGN get_window_region(HWND window)
101 {
102     POINT origin;
103     HRGN rgn;
104     HDC dc;
105
106     if (!(dc = GetDC(window)))
107     {
108         WARN("Failed to get dc.\n");
109         return NULL;
110     }
111
112     if (!(rgn = CreateRectRgn(0, 0, 0, 0)))
113     {
114         ERR("Failed to create region.\n");
115         ReleaseDC(window, dc);
116         return NULL;
117     }
118
119     if (GetRandomRgn(dc, rgn, SYSRGN) != 1)
120     {
121         ERR("Failed to get window region.\n");
122         DeleteObject(rgn);
123         ReleaseDC(window, dc);
124         return NULL;
125     }
126
127     if (GetVersion() & 0x80000000)
128     {
129         GetDCOrgEx(dc, &origin);
130         OffsetRgn(rgn, origin.x, origin.y);
131     }
132
133     ReleaseDC(window, dc);
134     return rgn;
135 }
136
137 /*****************************************************************************
138  * IDirectDrawClipper::GetClipList
139  *
140  * Retrieve a copy of the clip list
141  *
142  * Arguments:
143  *  rect: Rectangle to be used to clip the clip list or NULL for the
144  *      entire clip list.
145  *  clip_list: structure for the resulting copy of the clip list.
146  *      If NULL, fills Size up to the number of bytes necessary to hold
147  *      the entire clip.
148  *  clip_list_size: Size of resulting clip list; size of the buffer at clip_list
149  *      or, if clip_list is NULL, receives the required size of the buffer
150  *      in bytes.
151  *
152  * RETURNS
153  *  Either DD_OK or DDERR_*
154  ************************************************************************/
155 static HRESULT WINAPI ddraw_clipper_GetClipList(IDirectDrawClipper *iface, RECT *rect,
156         RGNDATA *clip_list, DWORD *clip_list_size)
157 {
158     struct ddraw_clipper *clipper = impl_from_IDirectDrawClipper(iface);
159     HRGN region;
160
161     TRACE("iface %p, rect %s, clip_list %p, clip_list_size %p.\n",
162             iface, wine_dbgstr_rect(rect), clip_list, clip_list_size);
163
164     wined3d_mutex_lock();
165
166     if (clipper->window)
167     {
168         if (!(region = get_window_region(clipper->window)))
169         {
170             wined3d_mutex_unlock();
171             WARN("Failed to get window region.\n");
172             return E_FAIL;
173         }
174     }
175     else
176     {
177         if (!(region = clipper->region))
178         {
179             wined3d_mutex_unlock();
180             WARN("No clip list set.\n");
181             return DDERR_NOCLIPLIST;
182         }
183     }
184
185     if (rect)
186     {
187         HRGN clip_region;
188
189         if (!(clip_region = CreateRectRgnIndirect(rect)))
190         {
191             wined3d_mutex_unlock();
192             ERR("Failed to create region.\n");
193             if (clipper->window)
194                 DeleteObject(region);
195             return E_FAIL;
196         }
197
198         if (CombineRgn(clip_region, region, clip_region, RGN_AND) == ERROR)
199         {
200             wined3d_mutex_unlock();
201             ERR("Failed to combine regions.\n");
202             DeleteObject(clip_region);
203             if (clipper->window)
204                 DeleteObject(region);
205             return E_FAIL;
206         }
207
208         if (clipper->window)
209             DeleteObject(region);
210         region = clip_region;
211     }
212
213     *clip_list_size = GetRegionData(region, *clip_list_size, clip_list);
214     if (rect || clipper->window)
215         DeleteObject(region);
216
217     wined3d_mutex_unlock();
218     return DD_OK;
219 }
220
221 /*****************************************************************************
222  * IDirectDrawClipper::SetClipList
223  *
224  * Sets or deletes (if region is NULL) the clip list
225  *
226  * This implementation is a stub and returns DD_OK always to make the app
227  * happy.
228  *
229  * PARAMS
230  *  region  Pointer to a LRGNDATA structure or NULL
231  *  flags   not used, must be 0
232  * RETURNS
233  *  Either DD_OK or DDERR_*
234  *****************************************************************************/
235 static HRESULT WINAPI ddraw_clipper_SetClipList(IDirectDrawClipper *iface, RGNDATA *region, DWORD flags)
236 {
237     struct ddraw_clipper *clipper = impl_from_IDirectDrawClipper(iface);
238
239     TRACE("iface %p, region %p, flags %#x.\n", iface, region, flags);
240
241     wined3d_mutex_lock();
242
243     if (clipper->window)
244     {
245         wined3d_mutex_unlock();
246         return DDERR_CLIPPERISUSINGHWND;
247     }
248
249     if (clipper->region)
250         DeleteObject(clipper->region);
251     if (!region)
252         clipper->region = NULL;
253     else if (!(clipper->region = ExtCreateRegion(NULL, 0, region)))
254     {
255         wined3d_mutex_unlock();
256         ERR("Failed to create region.\n");
257         return E_FAIL;
258     }
259
260     wined3d_mutex_unlock();
261
262     return DD_OK;
263 }
264
265 static HRESULT WINAPI ddraw_clipper_GetHWnd(IDirectDrawClipper *iface, HWND *window)
266 {
267     struct ddraw_clipper *clipper = impl_from_IDirectDrawClipper(iface);
268
269     TRACE("iface %p, window %p.\n", iface, window);
270
271     wined3d_mutex_lock();
272     *window = clipper->window;
273     wined3d_mutex_unlock();
274
275     return DD_OK;
276 }
277
278 static HRESULT WINAPI ddraw_clipper_Initialize(IDirectDrawClipper *iface,
279         IDirectDraw *ddraw, DWORD flags)
280 {
281     struct ddraw_clipper *clipper = impl_from_IDirectDrawClipper(iface);
282
283     TRACE("iface %p, ddraw %p, flags %#x.\n", iface, ddraw, flags);
284
285     wined3d_mutex_lock();
286     if (clipper->initialized)
287     {
288         wined3d_mutex_unlock();
289         return DDERR_ALREADYINITIALIZED;
290     }
291
292     clipper->initialized = TRUE;
293     wined3d_mutex_unlock();
294
295     return DD_OK;
296 }
297
298 static HRESULT WINAPI ddraw_clipper_IsClipListChanged(IDirectDrawClipper *iface, BOOL *changed)
299 {
300     FIXME("iface %p, changed %p stub!\n", iface, changed);
301
302     /* XXX What is safest? */
303     *changed = FALSE;
304
305     return DD_OK;
306 }
307
308 static const struct IDirectDrawClipperVtbl ddraw_clipper_vtbl =
309 {
310     ddraw_clipper_QueryInterface,
311     ddraw_clipper_AddRef,
312     ddraw_clipper_Release,
313     ddraw_clipper_GetClipList,
314     ddraw_clipper_GetHWnd,
315     ddraw_clipper_Initialize,
316     ddraw_clipper_IsClipListChanged,
317     ddraw_clipper_SetClipList,
318     ddraw_clipper_SetHWnd,
319 };
320
321 HRESULT ddraw_clipper_init(struct ddraw_clipper *clipper)
322 {
323     clipper->IDirectDrawClipper_iface.lpVtbl = &ddraw_clipper_vtbl;
324     clipper->ref = 1;
325
326     return DD_OK;
327 }
328
329 struct ddraw_clipper *unsafe_impl_from_IDirectDrawClipper(IDirectDrawClipper *iface)
330 {
331     if (!iface)
332         return NULL;
333     assert(iface->lpVtbl == &ddraw_clipper_vtbl);
334
335     return impl_from_IDirectDrawClipper(iface);
336 }