xref: /reactos/dll/win32/gdiplus/customlinecap.c (revision 1734f297)
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
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 #include <assert.h>
21 
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 
26 #include "objbase.h"
27 
28 #include "gdiplus.h"
29 #include "gdiplus_private.h"
30 #include "wine/debug.h"
31 
32 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
33 
34 GpStatus WINGDIPAPI GdipCloneCustomLineCap(GpCustomLineCap* from,
35     GpCustomLineCap** to)
36 {
37     TRACE("(%p, %p)\n", from, to);
38 
39     if(!from || !to)
40         return InvalidParameter;
41 
42     if (from->type == CustomLineCapTypeDefault)
43         *to = heap_alloc_zero(sizeof(GpCustomLineCap));
44     else
45         *to = heap_alloc_zero(sizeof(GpAdjustableArrowCap));
46 
47     if (!*to)
48         return OutOfMemory;
49 
50     if (from->type == CustomLineCapTypeDefault)
51         **to = *from;
52     else
53         *(GpAdjustableArrowCap *)*to = *(GpAdjustableArrowCap *)from;
54 
55     /* Duplicate path data */
56     (*to)->pathdata.Points = heap_alloc_zero(from->pathdata.Count * sizeof(PointF));
57     (*to)->pathdata.Types = heap_alloc_zero(from->pathdata.Count);
58 
59     if((!(*to)->pathdata.Types  || !(*to)->pathdata.Points) && (*to)->pathdata.Count){
60         heap_free((*to)->pathdata.Points);
61         heap_free((*to)->pathdata.Types);
62         heap_free(*to);
63         return OutOfMemory;
64     }
65 
66     memcpy((*to)->pathdata.Points, from->pathdata.Points, from->pathdata.Count
67            * sizeof(PointF));
68     memcpy((*to)->pathdata.Types, from->pathdata.Types, from->pathdata.Count);
69 
70     TRACE("<-- %p\n", *to);
71 
72     return Ok;
73 }
74 
75 static GpStatus init_custom_linecap(GpCustomLineCap *cap, GpPathData *pathdata, BOOL fill, GpLineCap basecap,
76     REAL base_inset)
77 {
78     cap->fill = fill;
79 
80     cap->pathdata.Points = heap_alloc_zero(pathdata->Count * sizeof(PointF));
81     cap->pathdata.Types = heap_alloc_zero(pathdata->Count);
82 
83     if ((!cap->pathdata.Types || !cap->pathdata.Points) && pathdata->Count)
84     {
85         heap_free(cap->pathdata.Points);
86         heap_free(cap->pathdata.Types);
87         cap->pathdata.Points = NULL;
88         cap->pathdata.Types = NULL;
89         return OutOfMemory;
90     }
91 
92     if (pathdata->Points)
93         memcpy(cap->pathdata.Points, pathdata->Points, pathdata->Count * sizeof(PointF));
94     if (pathdata->Types)
95         memcpy(cap->pathdata.Types, pathdata->Types, pathdata->Count);
96     cap->pathdata.Count = pathdata->Count;
97 
98     cap->inset = base_inset;
99     cap->cap = basecap;
100     cap->join = LineJoinMiter;
101     cap->scale = 1.0;
102 
103     return Ok;
104 }
105 
106 /* FIXME: Sometimes when fillPath is non-null and stroke path is null, the native
107  * version of this function returns NotImplemented. I cannot figure out why. */
108 GpStatus WINGDIPAPI GdipCreateCustomLineCap(GpPath* fillPath, GpPath* strokePath,
109     GpLineCap baseCap, REAL baseInset, GpCustomLineCap **customCap)
110 {
111     GpPathData *pathdata;
112     GpStatus stat;
113 
114     TRACE("%p %p %d %f %p\n", fillPath, strokePath, baseCap, baseInset, customCap);
115 
116     if(!customCap || !(fillPath || strokePath))
117         return InvalidParameter;
118 
119     *customCap = heap_alloc_zero(sizeof(GpCustomLineCap));
120     if(!*customCap)    return OutOfMemory;
121 
122     if (strokePath)
123         pathdata = &strokePath->pathdata;
124     else
125         pathdata = &fillPath->pathdata;
126 
127     stat = init_custom_linecap(*customCap, pathdata, fillPath != NULL, baseCap, baseInset);
128     if (stat != Ok)
129     {
130         heap_free(*customCap);
131         return stat;
132     }
133 
134     TRACE("<-- %p\n", *customCap);
135 
136     return Ok;
137 }
138 
139 GpStatus WINGDIPAPI GdipDeleteCustomLineCap(GpCustomLineCap *customCap)
140 {
141     TRACE("(%p)\n", customCap);
142 
143     if(!customCap)
144         return InvalidParameter;
145 
146     heap_free(customCap->pathdata.Points);
147     heap_free(customCap->pathdata.Types);
148     heap_free(customCap);
149 
150     return Ok;
151 }
152 
153 GpStatus WINGDIPAPI GdipGetCustomLineCapStrokeJoin(GpCustomLineCap* customCap,
154     GpLineJoin* lineJoin)
155 {
156     TRACE("(%p, %p)\n", customCap, lineJoin);
157 
158     if(!customCap || !lineJoin)
159         return InvalidParameter;
160 
161     *lineJoin = customCap->join;
162 
163     return Ok;
164 }
165 
166 GpStatus WINGDIPAPI GdipGetCustomLineCapWidthScale(GpCustomLineCap* custom,
167     REAL* widthScale)
168 {
169     TRACE("(%p, %p)\n", custom, widthScale);
170 
171     if(!custom || !widthScale)
172         return InvalidParameter;
173 
174     *widthScale = custom->scale;
175 
176     return Ok;
177 }
178 
179 GpStatus WINGDIPAPI GdipSetCustomLineCapStrokeCaps(GpCustomLineCap* custom,
180     GpLineCap start, GpLineCap end)
181 {
182     static int calls;
183 
184     TRACE("(%p,%u,%u)\n", custom, start, end);
185 
186     if(!custom)
187         return InvalidParameter;
188 
189     if(!(calls++))
190         FIXME("not implemented\n");
191 
192     return NotImplemented;
193 }
194 
195 GpStatus WINGDIPAPI GdipSetCustomLineCapBaseCap(GpCustomLineCap* custom,
196     GpLineCap base)
197 {
198     static int calls;
199 
200     TRACE("(%p,%u)\n", custom, base);
201 
202     if(!(calls++))
203         FIXME("not implemented\n");
204 
205     return NotImplemented;
206 }
207 
208 GpStatus WINGDIPAPI GdipGetCustomLineCapBaseInset(GpCustomLineCap* custom,
209     REAL* inset)
210 {
211     TRACE("(%p, %p)\n", custom, inset);
212 
213     if(!custom || !inset)
214         return InvalidParameter;
215 
216     *inset = custom->inset;
217 
218     return Ok;
219 }
220 
221 GpStatus WINGDIPAPI GdipSetCustomLineCapBaseInset(GpCustomLineCap* custom,
222     REAL inset)
223 {
224     static int calls;
225 
226     TRACE("(%p,%0.2f)\n", custom, inset);
227 
228     if(!(calls++))
229         FIXME("not implemented\n");
230 
231     return NotImplemented;
232 }
233 
234 /*FIXME: LineJoin completely ignored now */
235 GpStatus WINGDIPAPI GdipSetCustomLineCapStrokeJoin(GpCustomLineCap* custom,
236     GpLineJoin join)
237 {
238     TRACE("(%p, %d)\n", custom, join);
239 
240     if(!custom)
241         return InvalidParameter;
242 
243     custom->join = join;
244 
245     return Ok;
246 }
247 
248 GpStatus WINGDIPAPI GdipSetCustomLineCapWidthScale(GpCustomLineCap* custom, REAL width)
249 {
250     TRACE("(%p,%0.2f)\n", custom, width);
251 
252     if(!custom)
253         return InvalidParameter;
254 
255     custom->scale = width;
256 
257     return Ok;
258 }
259 
260 GpStatus WINGDIPAPI GdipGetCustomLineCapBaseCap(GpCustomLineCap *customCap, GpLineCap *baseCap)
261 {
262     TRACE("(%p, %p)\n", customCap, baseCap);
263 
264     if(!customCap || !baseCap)
265         return InvalidParameter;
266 
267     *baseCap = customCap->cap;
268 
269     return Ok;
270 }
271 
272 GpStatus WINGDIPAPI GdipGetCustomLineCapType(GpCustomLineCap *customCap, CustomLineCapType *type)
273 {
274     TRACE("(%p, %p)\n", customCap, type);
275 
276     if(!customCap || !type)
277         return InvalidParameter;
278 
279     *type = customCap->type;
280     return Ok;
281 }
282 
283 static void arrowcap_update_path(GpAdjustableArrowCap *cap)
284 {
285     static const BYTE types_filled[] =
286     {
287         PathPointTypeStart, PathPointTypeLine, PathPointTypeLine, PathPointTypeLine | PathPointTypeCloseSubpath
288     };
289     static const BYTE types_unfilled[] =
290     {
291         PathPointTypeStart, PathPointTypeLine, PathPointTypeLine
292     };
293     GpPointF *points;
294 
295     assert(cap->cap.pathdata.Count == 3 || cap->cap.pathdata.Count == 4);
296 
297     points = cap->cap.pathdata.Points;
298     if (cap->cap.fill)
299     {
300         memcpy(cap->cap.pathdata.Types, types_filled, sizeof(types_filled));
301         cap->cap.pathdata.Count = 4;
302         points[0].X = -cap->width / 2.0;
303         points[0].Y = -cap->height;
304         points[1].X = 0.0;
305         points[1].Y = 0.0;
306         points[2].X = cap->width / 2.0;
307         points[2].Y = -cap->height;
308         points[3].X = 0.0;
309         points[3].Y = -cap->height - cap->middle_inset;
310     }
311     else
312     {
313         memcpy(cap->cap.pathdata.Types, types_unfilled, sizeof(types_unfilled));
314         cap->cap.pathdata.Count = 3;
315         points[0].X = -cap->width / 4.0;
316         points[0].Y = -cap->height / 2.0;
317         points[1].X = 0.0;
318         points[1].Y = 0.0;
319         points[2].X = cap->width / 4.0;
320         points[2].Y = -cap->height / 2.0;
321     }
322 
323     if (cap->width == 0.0)
324         cap->cap.inset = 0.0;
325     else
326         cap->cap.inset = cap->height / cap->width;
327 }
328 
329 GpStatus WINGDIPAPI GdipCreateAdjustableArrowCap(REAL height, REAL width, BOOL fill,
330     GpAdjustableArrowCap **cap)
331 {
332     GpPathData pathdata;
333     GpStatus stat;
334 
335     TRACE("(%0.2f,%0.2f,%i,%p)\n", height, width, fill, cap);
336 
337     if (!cap)
338         return InvalidParameter;
339 
340     *cap = heap_alloc_zero(sizeof(**cap));
341     if (!*cap)
342         return OutOfMemory;
343 
344     /* We'll need 4 points at most. */
345     pathdata.Count = 4;
346     pathdata.Points = NULL;
347     pathdata.Types = NULL;
348     stat = init_custom_linecap(&(*cap)->cap, &pathdata, fill, LineCapTriangle, width != 0.0 ? height / width : 0.0);
349     if (stat != Ok)
350     {
351         heap_free(*cap);
352         return stat;
353     }
354 
355     (*cap)->cap.type = CustomLineCapTypeAdjustableArrow;
356     (*cap)->height = height;
357     (*cap)->width = width;
358     (*cap)->middle_inset = 0.0;
359     arrowcap_update_path(*cap);
360 
361     return Ok;
362 }
363 
364 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapFillState(GpAdjustableArrowCap* cap, BOOL* fill)
365 {
366     TRACE("(%p,%p)\n", cap, fill);
367 
368     if (!cap || !fill)
369         return InvalidParameter;
370 
371     *fill = cap->cap.fill;
372     return Ok;
373 }
374 
375 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapHeight(GpAdjustableArrowCap* cap, REAL* height)
376 {
377     TRACE("(%p,%p)\n", cap, height);
378 
379     if (!cap || !height)
380         return InvalidParameter;
381 
382     *height = cap->height;
383     return Ok;
384 }
385 
386 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapMiddleInset(GpAdjustableArrowCap* cap, REAL* middle)
387 {
388     TRACE("(%p,%p)\n", cap, middle);
389 
390     if (!cap || !middle)
391         return InvalidParameter;
392 
393     *middle = cap->middle_inset;
394     return Ok;
395 }
396 
397 GpStatus WINGDIPAPI GdipGetAdjustableArrowCapWidth(GpAdjustableArrowCap* cap, REAL* width)
398 {
399     TRACE("(%p,%p)\n", cap, width);
400 
401     if (!cap || !width)
402         return InvalidParameter;
403 
404     *width = cap->width;
405     return Ok;
406 }
407 
408 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapFillState(GpAdjustableArrowCap* cap, BOOL fill)
409 {
410     TRACE("(%p,%i)\n", cap, fill);
411 
412     if (!cap)
413         return InvalidParameter;
414 
415     cap->cap.fill = fill;
416     arrowcap_update_path(cap);
417     return Ok;
418 }
419 
420 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapHeight(GpAdjustableArrowCap* cap, REAL height)
421 {
422     TRACE("(%p,%0.2f)\n", cap, height);
423 
424     if (!cap)
425         return InvalidParameter;
426 
427     cap->height = height;
428     arrowcap_update_path(cap);
429     return Ok;
430 }
431 
432 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapMiddleInset(GpAdjustableArrowCap* cap, REAL middle)
433 {
434     TRACE("(%p,%0.2f)\n", cap, middle);
435 
436     if (!cap)
437         return InvalidParameter;
438 
439     cap->middle_inset = middle;
440     arrowcap_update_path(cap);
441     return Ok;
442 }
443 
444 GpStatus WINGDIPAPI GdipSetAdjustableArrowCapWidth(GpAdjustableArrowCap* cap, REAL width)
445 {
446     TRACE("(%p,%0.2f)\n", cap, width);
447 
448     if (!cap)
449         return InvalidParameter;
450 
451     cap->width = width;
452     arrowcap_update_path(cap);
453     return Ok;
454 }
455