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