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
GdipCloneCustomLineCap(GpCustomLineCap * from,GpCustomLineCap ** to)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
init_custom_linecap(GpCustomLineCap * cap,GpPathData * pathdata,BOOL fill,GpLineCap basecap,REAL base_inset)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. */
GdipCreateCustomLineCap(GpPath * fillPath,GpPath * strokePath,GpLineCap baseCap,REAL baseInset,GpCustomLineCap ** customCap)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
GdipDeleteCustomLineCap(GpCustomLineCap * customCap)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
GdipGetCustomLineCapStrokeJoin(GpCustomLineCap * customCap,GpLineJoin * lineJoin)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
GdipGetCustomLineCapWidthScale(GpCustomLineCap * custom,REAL * widthScale)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
GdipSetCustomLineCapStrokeCaps(GpCustomLineCap * custom,GpLineCap start,GpLineCap end)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
GdipSetCustomLineCapBaseCap(GpCustomLineCap * custom,GpLineCap base)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
GdipGetCustomLineCapBaseInset(GpCustomLineCap * custom,REAL * inset)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
GdipSetCustomLineCapBaseInset(GpCustomLineCap * custom,REAL inset)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 */
GdipSetCustomLineCapStrokeJoin(GpCustomLineCap * custom,GpLineJoin join)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
GdipSetCustomLineCapWidthScale(GpCustomLineCap * custom,REAL width)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
GdipGetCustomLineCapBaseCap(GpCustomLineCap * customCap,GpLineCap * baseCap)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
GdipGetCustomLineCapType(GpCustomLineCap * customCap,CustomLineCapType * type)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
arrowcap_update_path(GpAdjustableArrowCap * cap)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
GdipCreateAdjustableArrowCap(REAL height,REAL width,BOOL fill,GpAdjustableArrowCap ** cap)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
GdipGetAdjustableArrowCapFillState(GpAdjustableArrowCap * cap,BOOL * fill)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
GdipGetAdjustableArrowCapHeight(GpAdjustableArrowCap * cap,REAL * height)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
GdipGetAdjustableArrowCapMiddleInset(GpAdjustableArrowCap * cap,REAL * middle)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
GdipGetAdjustableArrowCapWidth(GpAdjustableArrowCap * cap,REAL * width)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
GdipSetAdjustableArrowCapFillState(GpAdjustableArrowCap * cap,BOOL fill)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
GdipSetAdjustableArrowCapHeight(GpAdjustableArrowCap * cap,REAL height)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
GdipSetAdjustableArrowCapMiddleInset(GpAdjustableArrowCap * cap,REAL middle)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
GdipSetAdjustableArrowCapWidth(GpAdjustableArrowCap * cap,REAL width)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