1 /*
2 * customlinecap.c
3 *
4 * Copyright (C) Novell, Inc. 2003-2004.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
7 * and associated documentation files (the "Software"), to deal in the Software without restriction,
8 * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all copies or substantial
13 * portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
16 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
19 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * Author:
22 * Ravindra (rkumar@novell.com)
23 *
24 * Copyright (C) 2004 Novell, Inc. http://www.novell.com
25 */
26
27 #include "customlinecap-private.h"
28 #include "general-private.h"
29 #include "graphics-path-private.h"
30 #include "graphics-private.h"
31 #include "graphics-cairo-private.h"
32
33 static GpStatus gdip_custom_linecap_setup (GpGraphics *graphics, GpCustomLineCap *cap);
34 static GpStatus gdip_custom_linecap_clone_cap (GpCustomLineCap *cap, GpCustomLineCap **clonedCap);
35 static GpStatus gdip_custom_linecap_destroy (GpCustomLineCap *cap);
36 static GpStatus gdip_custom_linecap_draw (GpGraphics *graphics, GpPen *pen, GpCustomLineCap *cap, float x, float y, float otherend_x, float otherend_y);
37
38 /*
39 * we have a single copy of vtable for
40 * all instances of custom linecap.
41 */
42
43 static CapClass vtable = { CustomLineCapTypeDefault,
44 gdip_custom_linecap_setup,
45 gdip_custom_linecap_clone_cap,
46 gdip_custom_linecap_destroy,
47 gdip_custom_linecap_draw };
48
49 void
gdip_custom_linecap_init(GpCustomLineCap * cap,CapClass * vt)50 gdip_custom_linecap_init (GpCustomLineCap *cap, CapClass *vt)
51 {
52 cap->vtable = vt;
53 cap->base_cap = LineCapTriangle;
54 cap->start_cap = LineCapFlat;
55 cap->end_cap = LineCapFlat;
56 cap->stroke_join = LineJoinMiter;
57 cap->base_inset = 0.0;
58 cap->width_scale = 1.0;
59 cap->fill_path = NULL;
60 cap->stroke_path = NULL;
61 }
62
63 static GpCustomLineCap*
gdip_custom_linecap_new(void)64 gdip_custom_linecap_new (void)
65 {
66 GpCustomLineCap *result = (GpCustomLineCap *) GdipAlloc (sizeof (GpCustomLineCap));
67
68 if (result)
69 gdip_custom_linecap_init (result, &vtable);
70
71 return result;
72 }
73
74 GpStatus
gdip_custom_linecap_clone_cap(GpCustomLineCap * cap,GpCustomLineCap ** clonedCap)75 gdip_custom_linecap_clone_cap (GpCustomLineCap *cap, GpCustomLineCap **clonedCap)
76 {
77 GpCustomLineCap *newcap;
78 GpPath *fillpath = NULL, *strokepath = NULL;
79
80 if (!cap || !clonedCap)
81 return InvalidParameter;
82
83 newcap = (GpCustomLineCap *) GdipAlloc (sizeof (GpCustomLineCap));
84 if (!newcap)
85 return OutOfMemory;
86
87 newcap->vtable = cap->vtable;
88 newcap->base_cap = cap->base_cap;
89 newcap->start_cap = cap->start_cap;
90 newcap->end_cap = cap->end_cap;
91 newcap->stroke_join = cap->stroke_join;
92 newcap->base_inset = cap->base_inset;
93 newcap->width_scale = cap->width_scale;
94
95 if (cap->fill_path) {
96 if (GdipClonePath (cap->fill_path, &fillpath) != Ok) {
97 if (fillpath != NULL)
98 GdipFree (fillpath);
99 GdipFree (newcap);
100 return OutOfMemory;
101 }
102 }
103 newcap->fill_path = fillpath;
104
105 if (cap->stroke_path) {
106 if (GdipClonePath (cap->stroke_path, &strokepath) != Ok) {
107 if (strokepath != NULL)
108 GdipFree (strokepath);
109 GdipFree (fillpath);
110 GdipFree (newcap);
111 return OutOfMemory;
112 }
113 }
114 newcap->stroke_path = strokepath;
115
116 *clonedCap = newcap;
117
118 return Ok;
119 }
120
121 GpStatus
gdip_custom_linecap_destroy(GpCustomLineCap * cap)122 gdip_custom_linecap_destroy (GpCustomLineCap *cap)
123 {
124 if (!cap)
125 return InvalidParameter;
126
127 if (cap->fill_path) {
128 GdipDeletePath (cap->fill_path);
129 cap->fill_path = NULL;
130 }
131 if (cap->stroke_path) {
132 GdipDeletePath (cap->stroke_path);
133 cap->stroke_path = NULL;
134 }
135 GdipFree (cap);
136
137 return Ok;
138 }
139
140 /* MonoTODO: Implement this function when cairo supports cairo_stroke_path API */
141 static GpStatus
gdip_custom_linecap_setup(GpGraphics * graphics,GpCustomLineCap * customCap)142 gdip_custom_linecap_setup (GpGraphics *graphics, GpCustomLineCap *customCap)
143 {
144 if (!graphics || !customCap)
145 return InvalidParameter;
146
147 return NotImplemented;
148 }
149
150 double
gdip_custom_linecap_angle(float x,float y,float otherend_x,float otherend_y)151 gdip_custom_linecap_angle (float x, float y, float otherend_x, float otherend_y)
152 {
153 float slope;
154 double angle;
155
156 if (x == otherend_x) {
157 slope = 0;
158 if (y < otherend_y) {
159 angle = PI;
160 } else {
161 angle = PI * 2;
162 }
163 } else if (y == otherend_y) {
164 slope = 0;
165 if (x < otherend_x) {
166 angle = PI/2;
167 } else {
168 angle = PI/-2;
169 }
170 } else {
171 if (y < otherend_y) {
172 slope = (otherend_y - y) / (otherend_x - x);
173 if (x < otherend_x) {
174 angle = PI/2;
175 } else {
176 angle = PI/-2;
177 }
178 } else {
179 slope = (otherend_x - x) / (y - otherend_y);
180 angle = 0;
181 }
182 }
183
184 angle += atan (slope);
185
186 return angle;
187 }
188
189 GpStatus
gdip_custom_linecap_draw(GpGraphics * graphics,GpPen * pen,GpCustomLineCap * customCap,float x,float y,float otherend_x,float otherend_y)190 gdip_custom_linecap_draw (GpGraphics *graphics, GpPen *pen, GpCustomLineCap *customCap, float x, float y, float otherend_x, float otherend_y)
191 {
192 double angle;
193 int points;
194 int i, idx = 0;
195 float penwidth;
196
197 if (!graphics || !pen || !customCap)
198 return InvalidParameter;
199
200 penwidth = pen->width;
201 angle = gdip_custom_linecap_angle (x, y, otherend_x, otherend_y);
202
203 cairo_save (graphics->ct);
204
205 /* FIXME: handle base_inset (including set/get!) */
206 cairo_translate (graphics->ct, x, y);
207 cairo_rotate (graphics->ct, angle);
208
209 if (customCap->stroke_path) {
210 GpPath *path = customCap->stroke_path;
211 points = path->count;
212
213 for (i = 0; i < points; i++) {
214 /* Adapted from gdip_plot_path() */
215 GpPointF point = path->points[i];
216 BYTE type = path->types[i];
217 GpPointF pts [3];
218
219 /* mask the bits so that we get only the type value not the other flags */
220 switch (type & PathPointTypePathTypeMask) {
221 case PathPointTypeStart:
222 gdip_cairo_move_to (graphics, (double)point.X * penwidth, (double)point.Y * penwidth, TRUE, TRUE);
223 break;
224
225 case PathPointTypeLine:
226 gdip_cairo_line_to (graphics, (double)point.X * penwidth, (double)point.Y * penwidth, TRUE, TRUE);
227 break;
228
229 case PathPointTypeBezier:
230 /* make sure we only add at most 3 points to pts */
231 if (idx < 3) {
232 pts [idx] = point;
233 idx ++;
234 }
235
236 /* once we've added 3 pts, we can draw the curve */
237 if (idx == 3) {
238 gdip_cairo_curve_to (graphics, (double)pts[0].X * penwidth, (double)pts[0].Y * penwidth, (double)pts[1].X * penwidth, (double)pts[1].Y * penwidth, (double)pts[2].X * penwidth, (double)pts[2].Y * penwidth, TRUE, TRUE);
239 idx = 0;
240 }
241 break;
242
243 default:
244 g_warning ("Unknown PathPointType %d", type);
245 return NotImplemented;
246 }
247
248 /* close the subpath */
249 if (type & PathPointTypeCloseSubpath) {
250 cairo_close_path (graphics->ct);
251 }
252 }
253
254 gdip_pen_setup (graphics, pen);
255 cairo_stroke (graphics->ct);
256 gdip_cairo_set_matrix (graphics, graphics->copy_of_ctm);
257 }
258
259 /* FIXME: handle fill_path */
260
261 cairo_restore (graphics->ct);
262
263 return gdip_get_status (cairo_status (graphics->ct));
264 }
265
266 /* this setup function gets called from pen */
267
268 GpStatus
gdip_linecap_setup(GpGraphics * graphics,GpCustomLineCap * customCap)269 gdip_linecap_setup (GpGraphics *graphics, GpCustomLineCap *customCap)
270 {
271 if (!graphics || !customCap)
272 return InvalidParameter;
273
274 return customCap->vtable->setup (graphics, customCap);
275 }
276
277 /* this draw function gets called from pen */
278
279 GpStatus
gdip_linecap_draw(GpGraphics * graphics,GpPen * pen,GpCustomLineCap * customCap,float x,float y,float otherend_x,float otherend_y)280 gdip_linecap_draw (GpGraphics *graphics, GpPen *pen, GpCustomLineCap *customCap, float x, float y, float otherend_x, float otherend_y)
281 {
282 if (!graphics || !pen || !customCap)
283 return InvalidParameter;
284
285 return customCap->vtable->draw (graphics, pen, customCap, x, y, otherend_x, otherend_y);
286 }
287
288 /* CustomLineCap functions */
289
290 // coverity[+alloc : arg-*4]
291 GpStatus WINGDIPAPI
GdipCreateCustomLineCap(GpPath * fillPath,GpPath * strokePath,GpLineCap baseCap,REAL baseInset,GpCustomLineCap ** customCap)292 GdipCreateCustomLineCap (GpPath *fillPath, GpPath *strokePath, GpLineCap baseCap, REAL baseInset, GpCustomLineCap **customCap)
293 {
294 GpStatus status;
295 GpCustomLineCap *result;
296
297 if (!gdiplusInitialized)
298 return GdiplusNotInitialized;
299
300 if ((!fillPath && !strokePath) || !customCap)
301 return InvalidParameter;
302
303 result = gdip_custom_linecap_new ();
304 if (!result)
305 return OutOfMemory;
306
307 if (fillPath) {
308 status = GdipClonePath (fillPath, &result->fill_path);
309 if (status != Ok) {
310 GdipDeleteCustomLineCap (result);
311 return status;
312 }
313 }
314
315 if (strokePath) {
316 status = GdipClonePath (strokePath, &result->stroke_path);
317 if (status != Ok) {
318 GdipDeleteCustomLineCap (result);
319 return status;
320 }
321 }
322
323 if (baseCap >= LineCapFlat && baseCap <= LineCapTriangle) {
324 result->base_cap = baseCap;
325 } else {
326 result->base_cap = LineCapFlat;
327 }
328
329 result->base_inset = baseInset;
330
331 *customCap = result;
332 return Ok;
333 }
334
335 GpStatus WINGDIPAPI
GdipDeleteCustomLineCap(GpCustomLineCap * customCap)336 GdipDeleteCustomLineCap (GpCustomLineCap *customCap)
337 {
338 if (!customCap)
339 return InvalidParameter;
340
341 return customCap->vtable->destroy (customCap);
342 }
343
344 GpStatus WINGDIPAPI
GdipCloneCustomLineCap(GpCustomLineCap * customCap,GpCustomLineCap ** clonedCap)345 GdipCloneCustomLineCap (GpCustomLineCap *customCap, GpCustomLineCap **clonedCap)
346 {
347 if (!customCap || !clonedCap)
348 return InvalidParameter;
349
350 return customCap->vtable->clone_cap (customCap, clonedCap);
351 }
352
353 GpStatus WINGDIPAPI
GdipGetCustomLineCapType(GpCustomLineCap * customCap,CustomLineCapType * capType)354 GdipGetCustomLineCapType (GpCustomLineCap *customCap, CustomLineCapType *capType)
355 {
356 if (!customCap || !capType)
357 return InvalidParameter;
358
359 *capType = customCap->vtable->type;
360 return Ok;
361 }
362
363 GpStatus WINGDIPAPI
GdipSetCustomLineCapStrokeCaps(GpCustomLineCap * customCap,GpLineCap startCap,GpLineCap endCap)364 GdipSetCustomLineCapStrokeCaps (GpCustomLineCap *customCap, GpLineCap startCap, GpLineCap endCap)
365 {
366 if (!customCap || startCap < LineCapFlat || startCap > LineCapTriangle || endCap < LineCapFlat || endCap > LineCapTriangle)
367 return InvalidParameter;
368
369 customCap->start_cap = startCap;
370 customCap->end_cap = endCap;
371
372 return Ok;
373 }
374
375 GpStatus WINGDIPAPI
GdipGetCustomLineCapStrokeCaps(GpCustomLineCap * customCap,GpLineCap * startCap,GpLineCap * endCap)376 GdipGetCustomLineCapStrokeCaps (GpCustomLineCap *customCap, GpLineCap *startCap, GpLineCap *endCap)
377 {
378 if (!customCap || !startCap || !endCap)
379 return InvalidParameter;
380
381 *startCap = customCap->start_cap;
382 *endCap = customCap->end_cap;
383
384 return Ok;
385 }
386
387 GpStatus WINGDIPAPI
GdipSetCustomLineCapStrokeJoin(GpCustomLineCap * customCap,GpLineJoin lineJoin)388 GdipSetCustomLineCapStrokeJoin (GpCustomLineCap *customCap, GpLineJoin lineJoin)
389 {
390 if (!customCap)
391 return InvalidParameter;
392
393 customCap->stroke_join = lineJoin;
394 return Ok;
395 }
396
397 GpStatus WINGDIPAPI
GdipGetCustomLineCapStrokeJoin(GpCustomLineCap * customCap,GpLineJoin * lineJoin)398 GdipGetCustomLineCapStrokeJoin (GpCustomLineCap *customCap, GpLineJoin *lineJoin)
399 {
400 if (!customCap || !lineJoin)
401 return InvalidParameter;
402
403 *lineJoin = customCap->stroke_join;
404 return Ok;
405 }
406
407 GpStatus WINGDIPAPI
GdipSetCustomLineCapBaseCap(GpCustomLineCap * customCap,GpLineCap baseCap)408 GdipSetCustomLineCapBaseCap (GpCustomLineCap *customCap, GpLineCap baseCap)
409 {
410 if (!customCap || baseCap > LineCapTriangle)
411 return InvalidParameter;
412
413 customCap->base_cap = baseCap;
414 return Ok;
415 }
416
417 GpStatus WINGDIPAPI
GdipGetCustomLineCapBaseCap(GpCustomLineCap * customCap,GpLineCap * baseCap)418 GdipGetCustomLineCapBaseCap (GpCustomLineCap *customCap, GpLineCap *baseCap)
419 {
420 if (!customCap || !baseCap)
421 return InvalidParameter;
422
423 *baseCap = customCap->base_cap;
424 return Ok;
425 }
426
427 GpStatus WINGDIPAPI
GdipSetCustomLineCapBaseInset(GpCustomLineCap * customCap,REAL inset)428 GdipSetCustomLineCapBaseInset (GpCustomLineCap *customCap, REAL inset)
429 {
430 if (!customCap)
431 return InvalidParameter;
432
433 customCap->base_inset = inset;
434 return Ok;
435 }
436
437 GpStatus WINGDIPAPI
GdipGetCustomLineCapBaseInset(GpCustomLineCap * customCap,REAL * inset)438 GdipGetCustomLineCapBaseInset (GpCustomLineCap *customCap, REAL *inset)
439 {
440 if (!customCap || !inset)
441 return InvalidParameter;
442
443 *inset = customCap->base_inset;
444 return Ok;
445 }
446
447 GpStatus WINGDIPAPI
GdipSetCustomLineCapWidthScale(GpCustomLineCap * customCap,REAL widthScale)448 GdipSetCustomLineCapWidthScale (GpCustomLineCap *customCap, REAL widthScale)
449 {
450 if (!customCap)
451 return InvalidParameter;
452
453 customCap->width_scale = widthScale;
454 return Ok;
455 }
456
457 GpStatus WINGDIPAPI
GdipGetCustomLineCapWidthScale(GpCustomLineCap * customCap,REAL * widthScale)458 GdipGetCustomLineCapWidthScale (GpCustomLineCap *customCap, REAL *widthScale)
459 {
460 if (!customCap || !widthScale)
461 return InvalidParameter;
462
463 *widthScale = customCap->width_scale;
464 return Ok;
465 }
466