gdiplus: Implemented GdipAddPathClosedCurve2 with tests.
[wine] / dlls / gdiplus / region.c
1 /*
2  * Copyright (C) 2008 Google (Lei Zhang)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20
21 #include "windef.h"
22 #include "winbase.h"
23 #include "wingdi.h"
24
25 #include "objbase.h"
26
27 #include "gdiplus.h"
28 #include "gdiplus_private.h"
29 #include "wine/debug.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
32
33 /**********************************************************
34  *
35  * Data returned by GdipGetRegionData looks something like this:
36  *
37  * struct region_data_header
38  * {
39  *   DWORD size;     size in bytes of the data - 8.
40  *   DWORD magic1;   probably a checksum.
41  *   DWORD magic2;   always seems to be 0xdbc01001 - version?
42  *   DWORD num_ops;  number of combining ops * 2
43  * };
44  *
45  * Then follows a sequence of combining ops and region elements.
46  *
47  * A region element is either a RECTF or some path data.
48  *
49  * Combining ops are just stored as their CombineMode value.
50  *
51  * Each RECTF is preceded by the DWORD 0x10000000.  An empty rect is
52  * stored as 0x10000002 (with no following RECTF) and an infinite rect
53  * is stored as 0x10000003 (again with no following RECTF).
54  *
55  * Path data is preceded by the DWORD 0x10000001.  Then follows a
56  * DWORD size and then size bytes of data.
57  *
58  * The combining ops are stored in the reverse order to the region
59  * elements and in the reverse order to which the region was
60  * constructed.
61  *
62  * When two or more complex regions (ie those with more than one
63  * element) are combined, the combining op for the two regions comes
64  * first, then the combining ops for the region elements in region 1,
65  * followed by the region elements for region 1, then follows the
66  * combining ops for region 2 and finally region 2's region elements.
67  * Presumably you're supposed to use the 0x1000000x header to find the
68  * end of the op list (the count of the elements in each region is not
69  * stored).
70  *
71  * When a simple region (1 element) is combined, it's treated as if a
72  * single rect/path is being combined.
73  *
74  */
75
76 typedef enum RegionType
77 {
78     RegionDataRect          = 0x10000000,
79     RegionDataPath          = 0x10000001,
80     RegionDataEmptyRect     = 0x10000002,
81     RegionDataInfiniteRect  = 0x10000003,
82 } RegionType;
83
84 /* Header size as far as header->size is concerned. This doesn't include
85  * header->size or header->checksum
86  */
87 static const INT sizeheader_size = sizeof(DWORD) * 2;
88
89 static inline INT get_element_size(const region_element* element)
90 {
91     INT needed = sizeof(DWORD); /* DWORD for the type */
92     switch(element->type)
93     {
94         case RegionDataRect:
95             return needed + sizeof(GpRect);
96         case RegionDataPath:
97              needed += element->elementdata.pathdata.pathheader.size;
98              needed += sizeof(DWORD); /* Extra DWORD for pathheader.size */
99              return needed;
100         case RegionDataEmptyRect:
101         case RegionDataInfiniteRect:
102             return needed;
103         default:
104             needed += get_element_size(element->elementdata.combine.left);
105             needed += get_element_size(element->elementdata.combine.right);
106             return needed;
107     }
108
109     return 0;
110 }
111
112 /* Does not check parameters, caller must do that */
113 static inline GpStatus init_region(GpRegion* region, const RegionType type)
114 {
115     region->node.type       = type;
116     region->header.checksum = 0xdeadbeef;
117     region->header.magic    = VERSION_MAGIC;
118     region->header.num_children  = 0;
119     region->header.size     = sizeheader_size + get_element_size(&region->node);
120
121     return Ok;
122 }
123
124 static inline void delete_element(region_element* element)
125 {
126     switch(element->type)
127     {
128         case RegionDataRect:
129             break;
130         case RegionDataPath:
131             GdipDeletePath(element->elementdata.pathdata.path);
132             break;
133         case RegionDataEmptyRect:
134         case RegionDataInfiniteRect:
135             break;
136         default:
137             delete_element(element->elementdata.combine.left);
138             delete_element(element->elementdata.combine.right);
139             GdipFree(element->elementdata.combine.left);
140             GdipFree(element->elementdata.combine.right);
141             break;
142     }
143 }
144
145 /*****************************************************************************
146  * GdipCloneRegion [GDIPLUS.@]
147  */
148 GpStatus WINGDIPAPI GdipCloneRegion(GpRegion *region, GpRegion **clone)
149 {
150     FIXME("(%p %p): stub\n", region, clone);
151
152     *clone = NULL;
153     return NotImplemented;
154 }
155
156 GpStatus WINGDIPAPI GdipCombineRegionPath(GpRegion *region, GpPath *path, CombineMode mode)
157 {
158     FIXME("(%p %p %d): stub\n", region, path, mode);
159     return NotImplemented;
160 }
161
162 GpStatus WINGDIPAPI GdipCombineRegionRect(GpRegion *region, GDIPCONST GpRectF *rect,
163                                           CombineMode mode)
164 {
165     FIXME("(%p %p %d): stub\n", region, rect, mode);
166     return NotImplemented;
167 }
168
169 GpStatus WINGDIPAPI GdipCombineRegionRectI(GpRegion *region, GDIPCONST GpRect *rect,
170                                            CombineMode mode)
171 {
172     FIXME("(%p %p %d): stub\n", region, rect, mode);
173     return NotImplemented;
174 }
175
176 GpStatus WINGDIPAPI GdipCombineRegionRegion(GpRegion *region1, GpRegion *region2,
177                                             CombineMode mode)
178 {
179     FIXME("(%p %p %d): stub\n", region1, region2, mode);
180     return NotImplemented;
181 }
182
183 GpStatus WINGDIPAPI GdipCreateRegion(GpRegion **region)
184 {
185     if(!region)
186         return InvalidParameter;
187
188     TRACE("%p\n", region);
189
190     *region = GdipAlloc(sizeof(GpRegion));
191     if(!*region)
192         return OutOfMemory;
193
194     return init_region(*region, RegionDataInfiniteRect);
195 }
196
197 GpStatus WINGDIPAPI GdipCreateRegionPath(GpPath *path, GpRegion **region)
198 {
199     FIXME("(%p, %p): stub\n", path, region);
200
201     *region = NULL;
202     return NotImplemented;
203 }
204
205 GpStatus WINGDIPAPI GdipCreateRegionRect(GDIPCONST GpRectF *rect, GpRegion **region)
206 {
207     FIXME("(%p, %p): stub\n", rect, region);
208
209     *region = NULL;
210     return NotImplemented;
211 }
212
213 GpStatus WINGDIPAPI GdipCreateRegionRectI(GDIPCONST GpRect *rect, GpRegion **region)
214 {
215     FIXME("(%p, %p): stub\n", rect, region);
216
217     *region = NULL;
218     return NotImplemented;
219 }
220
221 GpStatus WINGDIPAPI GdipCreateRegionRgnData(GDIPCONST BYTE *data, INT size, GpRegion **region)
222 {
223     FIXME("(%p, %d, %p): stub\n", data, size, region);
224
225     *region = NULL;
226     return NotImplemented;
227 }
228
229 GpStatus WINGDIPAPI GdipCreateRegionHrgn(HRGN hrgn, GpRegion **region)
230 {
231     FIXME("(%p, %p): stub\n", hrgn, region);
232
233     *region = NULL;
234     return NotImplemented;
235 }
236
237 GpStatus WINGDIPAPI GdipDeleteRegion(GpRegion *region)
238 {
239     TRACE("%p\n", region);
240
241     if (!region)
242         return InvalidParameter;
243
244     delete_element(&region->node);
245     GdipFree(region);
246
247     return Ok;
248 }
249
250 GpStatus WINGDIPAPI GdipGetRegionBounds(GpRegion *region, GpGraphics *graphics, GpRectF *rect)
251 {
252     FIXME("(%p, %p, %p): stub\n", region, graphics, rect);
253
254     return NotImplemented;
255 }
256
257 GpStatus WINGDIPAPI GdipGetRegionBoundsI(GpRegion *region, GpGraphics *graphics, GpRect *rect)
258 {
259     FIXME("(%p, %p, %p): stub\n", region, graphics, rect);
260
261     return NotImplemented;
262 }
263
264 GpStatus WINGDIPAPI GdipGetRegionData(GpRegion *region, BYTE *buffer, UINT size, UINT *needed)
265 {
266     FIXME("(%p, %p, %d, %p): stub\n", region, buffer, size, needed);
267
268     return NotImplemented;
269 }
270
271 GpStatus WINGDIPAPI GdipGetRegionDataSize(GpRegion *region, UINT *needed)
272 {
273     if (!(region && needed))
274         return InvalidParameter;
275
276     TRACE("%p, %p\n", region, needed);
277
278     /* header.size doesn't count header.size and header.checksum */
279     *needed = region->header.size + sizeof(DWORD) * 2;
280
281     return Ok;
282 }
283
284 /*****************************************************************************
285  * GdipGetRegionHRgn [GDIPLUS.@]
286  */
287 GpStatus WINGDIPAPI GdipGetRegionHRgn(GpRegion *region, GpGraphics *graphics, HRGN *hrgn)
288 {
289     FIXME("(%p, %p, %p): stub\n", region, graphics, hrgn);
290
291     *hrgn = NULL;
292     return NotImplemented;
293 }
294
295 GpStatus WINGDIPAPI GdipIsEmptyRegion(GpRegion *region, GpGraphics *graphics, BOOL *res)
296 {
297     FIXME("(%p, %p, %p): stub\n", region, graphics, res);
298
299     return NotImplemented;
300 }
301
302 GpStatus WINGDIPAPI GdipIsEqualRegion(GpRegion *region, GpRegion *region2, GpGraphics *graphics,
303                                       BOOL *res)
304 {
305     FIXME("(%p, %p, %p, %p): stub\n", region, region2, graphics, res);
306
307     return NotImplemented;
308 }
309
310 GpStatus WINGDIPAPI GdipIsInfiniteRegion(GpRegion *region, GpGraphics *graphics, BOOL *res)
311 {
312     FIXME("(%p, %p, %p): stub\n", region, graphics, res);
313
314     return NotImplemented;
315 }
316
317 GpStatus WINGDIPAPI GdipSetEmpty(GpRegion *region)
318 {
319     GpStatus stat;
320
321     TRACE("%p\n", region);
322
323     if (!region)
324         return InvalidParameter;
325
326     delete_element(&region->node);
327     stat = init_region(region, RegionDataEmptyRect);
328
329     return stat;
330 }
331
332 GpStatus WINGDIPAPI GdipSetInfinite(GpRegion *region)
333 {
334     GpStatus stat;
335
336     if (!region)
337         return InvalidParameter;
338
339     TRACE("%p\n", region);
340
341     delete_element(&region->node);
342     stat = init_region(region, RegionDataInfiniteRect);
343
344     return stat;
345 }
346
347 GpStatus WINGDIPAPI GdipTransformRegion(GpRegion *region, GpMatrix *matrix)
348 {
349     FIXME("(%p, %p): stub\n", region, matrix);
350
351     return NotImplemented;
352 }
353
354 GpStatus WINGDIPAPI GdipTranslateRegion(GpRegion *region, REAL dx, REAL dy)
355 {
356     FIXME("(%p, %f, %f): stub\n", region, dx, dy);
357
358     return NotImplemented;
359 }
360
361 GpStatus WINGDIPAPI GdipTranslateRegionI(GpRegion *region, INT dx, INT dy)
362 {
363     FIXME("(%p, %d, %d): stub\n", region, dx, dy);
364
365     return NotImplemented;
366 }