[PATCH] Implement CustomLineCap and AdjustableArrowCap in libgdiplus
Dick Porter <[email protected]>
| Newsgroups | gmane.comp.gnome.winforms |
|---|---|
| Message-ID | <[email protected]> |
Hi all Attached is a patch that implements custom line caps, and the arrow line cap subclass. A simple test in C and C# is also attached. The C# test looks identical to me when run on windows. - Dick _______________________________________________ Mono-winforms-list maillist - [email protected] http://lists.ximian.com/mailman/listinfo/mono-winforms-list
arrow-diffs
(text/x-patch, 19.8 KB)
Index: src/customlinecap-private.h
===================================================================
--- src/customlinecap-private.h (revision 160043)
+++ src/customlinecap-private.h (working copy)
@@ -42,6 +42,7 @@
GpStatus (*setup) (GpGraphics *graphics, GpCustomLineCap *cap);
GpStatus (*clone_cap) (GpCustomLineCap *cap, GpCustomLineCap **clonedCap);
GpStatus (*destroy) (GpCustomLineCap *cap);
+ GpStatus (*draw) (GpGraphics *graphics, GpPen *pen, GpCustomLineCap *cap, float x, float y, float otherend_x, float otherend_y);
} CapClass;
typedef struct _CustomLineCap {
@@ -58,6 +59,8 @@
void gdip_custom_linecap_init (GpCustomLineCap *cap, CapClass *vt) GDIP_INTERNAL;
GpStatus gdip_linecap_setup (GpGraphics *graphics, GpCustomLineCap *customCap) GDIP_INTERNAL;
+GpStatus gdip_linecap_draw (GpGraphics *graphics, GpPen *pen, GpCustomLineCap *customCap, float x, float y, float otherend_x, float otherend_y) GDIP_INTERNAL;
+double gdip_custom_linecap_angle (float x, float y, float otherend_x, float otherend_y);
#include "customlinecap.h"
Index: src/pen-private.h
===================================================================
--- src/pen-private.h (revision 160043)
+++ src/pen-private.h (working copy)
@@ -59,9 +59,13 @@
GpUnit unit; /* Always set to UnitWorld. */
cairo_matrix_t matrix;
BOOL changed; /* flag to mark if pen is changed and needs setup */
+ GpCustomLineCap *custom_start_cap;
+ GpCustomLineCap *custom_end_cap;
};
GpStatus gdip_pen_setup (GpGraphics *graphics, GpPen *pen) GDIP_INTERNAL;
+GpStatus gdip_pen_draw_custom_start_cap (GpGraphics *graphics, GpPen *pen, float x1, float y1, float x2, float y2);
+GpStatus gdip_pen_draw_custom_end_cap (GpGraphics *graphics, GpPen *pen, float x1, float y1, float x2, float y2);
#include "pen.h"
Index: src/adjustablearrowcap.c
===================================================================
--- src/adjustablearrowcap.c (revision 160043)
+++ src/adjustablearrowcap.c (working copy)
@@ -23,10 +23,12 @@
*/
#include "adjustablearrowcap-private.h"
+#include "graphics-private.h"
static GpStatus gdip_adjust_arrowcap_setup (GpGraphics *graphics, GpCustomLineCap *cap);
static GpStatus gdip_adjust_arrowcap_clone_cap (GpCustomLineCap *cap, GpCustomLineCap **clonedCap);
static GpStatus gdip_adjust_arrowcap_destroy (GpCustomLineCap *cap);
+static GpStatus gdip_adjust_arrowcap_draw (GpGraphics *graphics, GpPen *pen, GpCustomLineCap *Cap, float x, float y, float otherend_x, float otherend_y);
/*
* we have a single copy of vtable for
@@ -36,7 +38,8 @@
static CapClass vtable = { CustomLineCapTypeAdjustableArrow,
gdip_adjust_arrowcap_setup,
gdip_adjust_arrowcap_clone_cap,
- gdip_adjust_arrowcap_destroy };
+ gdip_adjust_arrowcap_destroy,
+ gdip_adjust_arrowcap_draw };
static void
gdip_adjust_arrowcap_init (GpAdjustableArrowCap *arrow)
@@ -44,6 +47,8 @@
gdip_custom_linecap_init (&arrow->base, &vtable);
arrow->fill_state = TRUE;
arrow->middle_inset = 0.0;
+ arrow->width = 0.0;
+ arrow->height = 0.0;
}
static GpAdjustableArrowCap*
@@ -96,6 +101,59 @@
return NotImplemented;
}
+GpStatus
+gdip_adjust_arrowcap_draw (GpGraphics *graphics, GpPen *pen, GpCustomLineCap *customCap, float x, float y, float otherend_x, float otherend_y)
+{
+ double angle;
+ GpAdjustableArrowCap *arrowcap;
+ float w;
+ float h;
+ float penwidth;
+
+ if (!graphics || !customCap)
+ return InvalidParameter;
+
+ penwidth = pen->width;
+ if (penwidth < 2.0) {
+ /* Seems to be a minimum */
+ penwidth = 2.0;
+ }
+
+ arrowcap = (GpAdjustableArrowCap *)customCap;
+ w = arrowcap->width / 2;
+ h = arrowcap->height;
+
+ /* Vertical lines need some assistance to point the arrowhead correctly */
+ if ((x == otherend_x) &&
+ (y < otherend_y)) {
+ h = -h;
+ }
+
+ angle = gdip_custom_linecap_angle (x, y, otherend_x, otherend_y);
+
+ cairo_save (graphics->ct);
+
+ /* FIXME: handle base_inset (including set/get!) */
+ cairo_translate (graphics->ct, x, y);
+ cairo_rotate (graphics->ct, angle);
+
+ gdip_cairo_move_to (graphics, 0, 0, TRUE, TRUE);
+ gdip_cairo_line_to (graphics, -w * penwidth, -h * penwidth, TRUE, TRUE);
+ gdip_cairo_line_to (graphics, w * penwidth, -h * penwidth, TRUE, TRUE);
+ gdip_cairo_line_to (graphics, 0, 0, TRUE, TRUE);
+
+ if (arrowcap->fill_state) {
+ /* FIXME: handle middle_inset */
+ cairo_fill_preserve (graphics->ct);
+ }
+
+ cairo_stroke (graphics->ct);
+
+ cairo_restore (graphics->ct);
+
+ return Ok;
+}
+
/* AdjustableArrowCap functions */
// coverity[+alloc : arg-*3]
Index: src/customlinecap.c
===================================================================
--- src/customlinecap.c (revision 160043)
+++ src/customlinecap.c (working copy)
@@ -25,10 +25,14 @@
*/
#include "customlinecap-private.h"
+#include "graphics-path-private.h"
+#include "graphics-private.h"
+#include "graphics-cairo-private.h"
static GpStatus gdip_custom_linecap_setup (GpGraphics *graphics, GpCustomLineCap *cap);
static GpStatus gdip_custom_linecap_clone_cap (GpCustomLineCap *cap, GpCustomLineCap **clonedCap);
static GpStatus gdip_custom_linecap_destroy (GpCustomLineCap *cap);
+static GpStatus gdip_custom_linecap_draw (GpGraphics *graphics, GpPen *pen, GpCustomLineCap *cap, float x, float y, float otherend_x, float otherend_y);
/*
* we have a single copy of vtable for
@@ -38,7 +42,8 @@
static CapClass vtable = { CustomLineCapTypeDefault,
gdip_custom_linecap_setup,
gdip_custom_linecap_clone_cap,
- gdip_custom_linecap_destroy };
+ gdip_custom_linecap_destroy,
+ gdip_custom_linecap_draw };
void
gdip_custom_linecap_init (GpCustomLineCap *cap, CapClass *vt)
@@ -51,6 +56,8 @@
cap->base_inset = 0.0;
/* LAMESPEC: Default value is documented as 1.0, but actually it is 0.0 */
cap->width_scale = 0.0;
+ cap->fill_path = NULL;
+ cap->stroke_path = NULL;
}
static GpCustomLineCap*
@@ -68,6 +75,7 @@
gdip_custom_linecap_clone_cap (GpCustomLineCap *cap, GpCustomLineCap **clonedCap)
{
GpCustomLineCap *newcap;
+ GpPath *fillpath = NULL, *strokepath = NULL;
if (!cap || !clonedCap)
return InvalidParameter;
@@ -84,6 +92,27 @@
newcap->base_inset = cap->base_inset;
newcap->width_scale = cap->width_scale;
+ if (cap->fill_path) {
+ if (GdipClonePath (cap->fill_path, &fillpath) != Ok) {
+ if (fillpath != NULL)
+ GdipFree (fillpath);
+ GdipFree (newcap);
+ return OutOfMemory;
+ }
+ }
+ newcap->fill_path = fillpath;
+
+ if (cap->stroke_path) {
+ if (GdipClonePath (cap->stroke_path, &strokepath) != Ok) {
+ if (strokepath != NULL)
+ GdipFree (strokepath);
+ GdipFree (fillpath);
+ GdipFree (newcap);
+ return OutOfMemory;
+ }
+ }
+ newcap->stroke_path = strokepath;
+
*clonedCap = newcap;
return Ok;
@@ -95,6 +124,14 @@
if (!cap)
return InvalidParameter;
+ if (cap->fill_path) {
+ GdipDeletePath (cap->fill_path);
+ cap->fill_path = NULL;
+ }
+ if (cap->stroke_path) {
+ GdipDeletePath (cap->stroke_path);
+ cap->stroke_path = NULL;
+ }
GdipFree (cap);
return Ok;
@@ -110,6 +147,105 @@
return NotImplemented;
}
+double
+gdip_custom_linecap_angle (float x, float y, float otherend_x, float otherend_y)
+{
+ float slope;
+ double angle;
+
+ if (y < otherend_y) {
+ slope = (otherend_y - y) / (otherend_x - x);
+ if (x < otherend_x) {
+ angle = PI/2;
+ } else {
+ angle = PI/-2;
+ }
+ } else {
+ slope = (otherend_x - x) / (y - otherend_y);
+ angle = 0;
+ }
+ angle += atan (slope);
+
+ return angle;
+}
+
+GpStatus
+gdip_custom_linecap_draw (GpGraphics *graphics, GpPen *pen, GpCustomLineCap *customCap, float x, float y, float otherend_x, float otherend_y)
+{
+ double angle;
+ int points;
+ int i, idx = 0;
+ float penwidth;
+
+ if (!graphics || !pen || !customCap)
+ return InvalidParameter;
+
+ penwidth = pen->width;
+ angle = gdip_custom_linecap_angle (x, y, otherend_x, otherend_y);
+
+ cairo_save (graphics->ct);
+
+ /* FIXME: handle base_inset (including set/get!) */
+ cairo_translate (graphics->ct, x, y);
+ cairo_rotate (graphics->ct, angle);
+
+ if (customCap->stroke_path) {
+ GpPath *path = customCap->stroke_path;
+ points = path->count;
+
+ for (i = 0; i < points; i++) {
+ /* Adapted from gdip_plot_path() */
+ GpPointF point = g_array_index (path->points, GpPointF, i);
+ BYTE type = g_array_index (path->types, BYTE, i);
+ GpPointF pts [3];
+
+ /* mask the bits so that we get only the type value not the other flags */
+ switch (type & PathPointTypePathTypeMask) {
+ case PathPointTypeStart:
+ gdip_cairo_move_to (graphics, point.X * penwidth, point.Y * penwidth, TRUE, TRUE);
+ break;
+
+ case PathPointTypeLine:
+ gdip_cairo_line_to (graphics, point.X * penwidth, point.Y * penwidth, TRUE, TRUE);
+ break;
+
+ case PathPointTypeBezier:
+ /* make sure we only add at most 3 points to pts */
+ if (idx < 3) {
+ pts [idx] = point;
+ idx ++;
+ }
+
+ /* once we've added 3 pts, we can draw the curve */
+ if (idx == 3) {
+ gdip_cairo_curve_to (graphics, pts[0].X * penwidth, pts[0].Y * penwidth, pts[1].X * penwidth, pts[1].Y * penwidth, pts[2].X * penwidth, pts[2].Y * penwidth, TRUE, TRUE);
+ idx = 0;
+ }
+ break;
+
+ default:
+ g_warning ("Unknown PathPointType %d", type);
+ return NotImplemented;
+ }
+
+ /* close the subpath */
+ if (type & PathPointTypeCloseSubpath) {
+ cairo_close_path (graphics->ct);
+ }
+ }
+
+ gdip_pen_setup (graphics, pen);
+ cairo_stroke (graphics->ct);
+ cairo_set_matrix (graphics->ct, graphics->copy_of_ctm);
+ }
+
+ /* FIXME: handle fill_path */
+
+ cairo_restore (graphics->ct);
+
+ return gdip_get_status (cairo_status (graphics->ct));
+}
+
/* this setup function gets called from pen */
GpStatus
@@ -121,6 +257,17 @@
return customCap->vtable->setup (graphics, customCap);
}
+/* this draw function gets called from pen */
+
+GpStatus
+gdip_linecap_draw (GpGraphics *graphics, GpPen *pen, GpCustomLineCap *customCap, float x, float y, float otherend_x, float otherend_y)
+{
+ if (!graphics || !pen || !customCap)
+ return InvalidParameter;
+
+ return customCap->vtable->draw (graphics, pen, customCap, x, y, otherend_x, otherend_y);
+}
+
/* CustomLineCap functions */
// coverity[+alloc : arg-*4]
@@ -128,6 +275,7 @@
GdipCreateCustomLineCap (GpPath *fillPath, GpPath *strokePath, GpLineCap baseCap, float baseInset, GpCustomLineCap **customCap)
{
GpCustomLineCap *cap;
+ GpPath *fillpath_clone = NULL, *strokepath_clone = NULL;
if ((!fillPath && !strokePath) || !customCap)
return InvalidParameter;
@@ -136,8 +284,27 @@
if (!cap)
return OutOfMemory;
- cap->fill_path = fillPath;
- cap->stroke_path = strokePath;
+ if (fillPath) {
+ if (GdipClonePath (fillPath, &fillpath_clone) != Ok) {
+ if (fillpath_clone != NULL)
+ GdipFree (fillpath_clone);
+ GdipFree (cap);
+ return OutOfMemory;
+ }
+ }
+ cap->fill_path = fillpath_clone;
+
+ if (strokePath) {
+ if (GdipClonePath (strokePath, &strokepath_clone) != Ok) {
+ if (strokepath_clone != NULL)
+ GdipFree (strokepath_clone);
+ GdipFree (fillpath_clone);
+ GdipFree (cap);
+ return OutOfMemory;
+ }
+ }
+ cap->stroke_path = strokepath_clone;
+
cap->base_cap = baseCap;
cap->base_inset = baseInset;
Index: src/pen.c
===================================================================
--- src/pen.c (revision 160043)
+++ src/pen.c (working copy)
@@ -30,6 +30,7 @@
#include "solidbrush-private.h"
#include "matrix-private.h"
#include "graphics-private.h"
+#include "customlinecap-private.h"
static void
gdip_pen_init (GpPen *pen)
@@ -53,6 +54,8 @@
pen->compound_array = NULL;
pen->unit = UnitWorld;
pen->changed = TRUE;
+ pen->custom_start_cap = NULL;
+ pen->custom_end_cap = NULL;
cairo_matrix_init_identity (&pen->matrix);
}
@@ -204,6 +207,34 @@
return gdip_get_status (cairo_status (graphics->ct));
}
+GpStatus
+gdip_pen_draw_custom_start_cap (GpGraphics *graphics, GpPen *pen, float x1, float y1, float x2, float y2)
+{
+ if (!graphics || !pen)
+ return InvalidParameter;
+
+ if (pen->custom_start_cap) {
+ /* Draw the end cap */
+ gdip_linecap_draw (graphics, pen, pen->custom_start_cap, x1, y1, x2, y2);
+ }
+
+ return gdip_get_status (cairo_status (graphics->ct));
+}
+
+GpStatus
+gdip_pen_draw_custom_end_cap (GpGraphics *graphics, GpPen *pen, float x1, float y1, float x2, float y2)
+{
+ if (!graphics || !pen)
+ return InvalidParameter;
+
+ if (pen->custom_end_cap) {
+ /* Draw the end cap */
+ gdip_linecap_draw (graphics, pen, pen->custom_end_cap, x1, y1, x2, y2);
+ }
+
+ return gdip_get_status (cairo_status (graphics->ct));
+}
+
// coverity[+alloc : arg-*3]
GpStatus
GdipCreatePen1 (ARGB argb, float width, GpUnit unit, GpPen **pen)
@@ -319,6 +350,8 @@
GpPen *result;
float *dashes; /* copy off pen->dash_array */
float *compound_array = NULL; /* copy off pen->compound_array */
+ GpCustomLineCap *custom_start_cap = NULL;
+ GpCustomLineCap *custom_end_cap = NULL;
if (!pen || !clonepen)
return InvalidParameter;
@@ -345,6 +378,34 @@
clone_dash_array (compound_array, pen->compound_array, pen->compound_count);
}
+ if (pen->custom_start_cap != NULL) {
+ GpStatus status = GdipCloneCustomLineCap (pen->custom_start_cap, &custom_start_cap);
+ if (status != Ok) {
+ if (custom_start_cap)
+ GdipDeleteCustomLineCap (custom_start_cap);
+ if (compound_array != NULL)
+ GdipFree (compound_array);
+ if (pen->dash_count > 0)
+ GdipFree (dashes);
+ return OutOfMemory;
+ }
+ }
+
+ if (pen->custom_end_cap != NULL) {
+ GpStatus status = GdipCloneCustomLineCap (pen->custom_end_cap, &custom_end_cap);
+ if (status != Ok) {
+ if (custom_end_cap)
+ GdipDeleteCustomLineCap (custom_end_cap);
+ if (custom_start_cap)
+ GdipDeleteCustomLineCap (custom_start_cap);
+ if (compound_array != NULL)
+ GdipFree (compound_array);
+ if (pen->dash_count > 0)
+ GdipFree (dashes);
+ return OutOfMemory;
+ }
+ }
+
result = gdip_pen_new ();
if (result == NULL) {
if (pen->dash_count > 0)
@@ -382,6 +443,8 @@
result->unit = pen->unit;
gdip_cairo_matrix_copy (&result->matrix, &pen->matrix);
result->changed = pen->changed;
+ result->custom_start_cap = custom_start_cap;
+ result->custom_end_cap = custom_end_cap;
*clonepen = result;
@@ -411,6 +474,16 @@
pen->compound_count = 0;
}
+ if (pen->custom_start_cap != NULL) {
+ GdipDeleteCustomLineCap (pen->custom_start_cap);
+ pen->custom_start_cap = NULL;
+ }
+
+ if (pen->custom_end_cap != NULL) {
+ GdipDeleteCustomLineCap (pen->custom_end_cap);
+ pen->custom_end_cap = NULL;
+ }
+
GdipFree (pen);
return Ok;
}
@@ -1004,52 +1077,38 @@
return Ok;
}
-/* MonoTODO - not implemented */
GpStatus
GdipSetPenCustomStartCap (GpPen *pen, GpCustomLineCap *customCap)
{
- static BOOL called = FALSE;
- if (!called) {
- g_warning ("GdipSetPenCustomStartCap isn't implemented");
- called = TRUE;
- }
- return Ok;
+ if (!pen)
+ return InvalidParameter;
+
+ return GdipCloneCustomLineCap (customCap, &pen->custom_start_cap);
}
-/* MonoTODO - not implemented */
GpStatus
GdipGetPenCustomStartCap (GpPen *pen, GpCustomLineCap **customCap)
{
- static BOOL called = FALSE;
- if (!called) {
- g_warning ("GdipGetPenCustomStartCap isn't implemented");
- called = TRUE;
- }
- *customCap = NULL;
- return Ok;
+ if (!pen || !customCap)
+ return InvalidParameter;
+
+ return GdipCloneCustomLineCap (pen->custom_start_cap, customCap);
}
-/* MonoTODO - not implemented */
GpStatus
GdipSetPenCustomEndCap (GpPen *pen, GpCustomLineCap *customCap)
{
- static BOOL called = FALSE;
- if (!called) {
- g_warning ("GdipSetPenCustomEndCap isn't implemented");
- called = TRUE;
- }
- return Ok;
+ if (!pen)
+ return InvalidParameter;
+
+ return GdipCloneCustomLineCap (customCap, &pen->custom_end_cap);
}
-/* MonoTODO - not implemented */
GpStatus
GdipGetPenCustomEndCap (GpPen *pen, GpCustomLineCap **customCap)
{
- static BOOL called = FALSE;
- if (!called) {
- g_warning ("GdipGetPenCustomEndCap isn't implemented");
- called = TRUE;
- }
- *customCap = NULL;
- return Ok;
+ if (!pen || !customCap)
+ return InvalidParameter;
+
+ return GdipCloneCustomLineCap (pen->custom_end_cap, customCap);
}
Index: src/graphics-cairo.c
===================================================================
--- src/graphics-cairo.c (revision 160043)
+++ src/graphics-cairo.c (working copy)
@@ -485,41 +485,74 @@
GpStatus
cairo_DrawLine (GpGraphics *graphics, GpPen *pen, float x1, float y1, float x2, float y2)
{
+ GpStatus ret;
+
/* We use graphics->copy_of_ctm matrix for path creation. We should have it set already. */
gdip_cairo_move_to (graphics, x1, y1, TRUE, TRUE);
gdip_cairo_line_to (graphics, x2, y2, TRUE, TRUE);
- return stroke_graphics_with_pen (graphics, pen);
+ ret = stroke_graphics_with_pen (graphics, pen);
+
+ gdip_pen_draw_custom_start_cap (graphics, pen, x1, y1, x2, y2);
+ gdip_pen_draw_custom_end_cap (graphics, pen, x2, y2, x1, y1);
+
+ return ret;
}
GpStatus
cairo_DrawLines (GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF *points, int count)
{
int i;
+ float last_x, last_y, prev_x, prev_y;
+ GpStatus ret;
/* We use graphics->copy_of_ctm matrix for path creation. We should have it set already. */
gdip_cairo_move_to (graphics, points [0].X, points [0].Y, TRUE, TRUE);
for (i = 1; i < count; i++) {
gdip_cairo_line_to (graphics, points [i].X, points [i].Y, TRUE, TRUE);
+ prev_x = points [i - 1].X;
+ prev_y = points [i - 1].Y;
+ last_x = points [i].X;
+ last_y = points [i].Y;
}
- return stroke_graphics_with_pen (graphics, pen);
+ ret = stroke_graphics_with_pen (graphics, pen);
+
+ if (count > 1) {
+ gdip_pen_draw_custom_start_cap (graphics, pen, points [0].X, points [0].Y, points [1].X, points [1].Y);
+ gdip_pen_draw_custom_end_cap (graphics, pen, last_x, last_y, prev_x, prev_y);
+ }
+
+ return ret;
}
GpStatus
cairo_DrawLinesI (GpGraphics *graphics, GpPen *pen, GDIPCONST GpPoint *points, int count)
{
int i;
+ float last_x, last_y, prev_x, prev_y;
+ GpStatus ret;
/* We use graphics->copy_of_ctm matrix for path creation. We should have it set already. */
gdip_cairo_move_to (graphics, points [0].X, points [0].Y, TRUE, TRUE);
for (i = 1; i < count; i++) {
gdip_cairo_line_to (graphics, points [i].X, points [i].Y, TRUE, TRUE);
+ prev_x = points [i - 1].X;
+ prev_y = points [i - 1].Y;
+ last_x = points [i].X;
+ last_y = points [i].Y;
}
- return stroke_graphics_with_pen (graphics, pen);
+ ret = stroke_graphics_with_pen (graphics, pen);
+
+ if (count > 1) {
+ gdip_pen_draw_custom_start_cap (graphics, pen, points [0].X, points [0].Y, points [1].X, points [1].Y);
+ gdip_pen_draw_custom_end_cap (graphics, pen, last_x, last_y, prev_x, prev_y);
+ }
+
+ return ret;
}
GpStatus
@@ -574,12 +607,41 @@
GpStatus
cairo_DrawPath (GpGraphics *graphics, GpPen *pen, GpPath *path)
{
+ GpStatus ret;
+ int count;
+ GpPointF *points;
+
/* We use graphics->copy_of_ctm matrix for path creation. We should have it set already. */
GpStatus status = gdip_plot_path (graphics, path, TRUE);
if (status != Ok)
return status;
- return stroke_graphics_with_pen (graphics, pen);
+ ret = stroke_graphics_with_pen (graphics, pen);
+
+ /* Draw any custom pen end caps */
+
+ status = GdipGetPointCount (path, &count);
+
+ /* To know the angle of the end cap, we need the penultimate point.
+ Unfortunately there's no way of getting it without getting all
+ the points :-(
+ */
+ if (status == Ok && count > 1) {
+ points = gdip_calloc (count, sizeof(GpPointF));
+ if (points == NULL) {
+ return OutOfMemory;
+ }
+ status = GdipGetPathPoints (path, points, count);
+
+ if (status == Ok) {
+ gdip_pen_draw_custom_start_cap (graphics, pen, points [0].X, points [0].Y, points [1].X, points [1].Y);
+ gdip_pen_draw_custom_end_cap (graphics, pen, points [count - 1].X, points [count - 1].Y, points [count - 2].X, points [count - 2].Y);
+ }
+
+ GdipFree (points);
+ }
+
+ return ret;
}
/* FIXME - this doesn't match MS behaviour when we use really complex paths with internal intersections */
form.cs
(text/x-csharp, 2 KB)
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
public class arrow: Form
{
public arrow() {
InitializeComponent();
}
private void InitializeComponent() {
this.SuspendLayout();
this.ClientSize = new Size(400, 400);
this.Name = "Form";
this.Text = "Form";
this.Paint += new PaintEventHandler(this.arrow_Paint);
this.ResumeLayout(false);
}
private void arrow_Paint(object sender, PaintEventArgs e) {
AdjustableArrowCap arrowcap = new AdjustableArrowCap(6, 10);
Pen cappen = new Pen(Color.Black, 1.0F);
cappen.CustomStartCap = arrowcap;
cappen.CustomEndCap = arrowcap;
e.Graphics.DrawLine(cappen, 20, 20, 20, 50);
e.Graphics.DrawLine(cappen, 50, 20, 200, 20);
e.Graphics.DrawLine(cappen, 200, 50, 50, 50);
e.Graphics.DrawLine(cappen, 300, 20, 340, 50);
e.Graphics.DrawLine(cappen, 280, 20, 240, 50);
e.Graphics.DrawLine(cappen, 360, 50, 360, 20);
GraphicsPath path = new GraphicsPath();
path.AddLine(new Point(0, 0), new Point(0, 5));
path.AddLine(new Point(0, 5), new Point(5, 1));
path.AddLine(new Point(5, 1), new Point(3, 1));
CustomLineCap hookcap = new CustomLineCap(null, path);
hookcap.SetStrokeCaps(LineCap.Round, LineCap.Round);
Pen customcappen = new Pen(Color.Black, 5);
customcappen.CustomStartCap = hookcap;
customcappen.CustomEndCap = hookcap;
Pen cappen2 = new Pen(Color.Red, 10);
LineCap startcap;
LineCap endcap;
hookcap.GetStrokeCaps(out startcap, out endcap);
cappen2.StartCap = startcap;
cappen2.EndCap = endcap;
Point[] points = {
new Point(150, 150),
new Point(250, 100),
new Point(300, 350)
};
e.Graphics.DrawLines(cappen2, points);
e.Graphics.DrawLines(customcappen, points);
Point[] points2 = {
new Point(50, 150),
new Point(150, 350),
new Point(200, 100)
};
e.Graphics.DrawLines(cappen2, points2);
e.Graphics.DrawLines(customcappen, points2);
}
public static void Main() {
Application.Run(new arrow());
}
}
arrowtest.c
(text/x-csrc, 5 KB)
#include <stdio.h>
#include <stdlib.h>
#include "GdiPlusFlat.h"
#include "texturebrush.h"
#include <X11/Xlib.h>
void win_draw (Display *dpy, Window win)
{
GpStatus status;
GpGraphics *gp;
GpAdjustableArrowCap *arrowcap;
GpPen *pen;
ARGB solid_black = 0xFF000000;
ARGB solid_red = 0xFFFF0000;
GpPath *path;
GpCustomLineCap *hookcap;
GpPen *customcappen, *cappen2;
GpLineCap startcap, endcap;
GpPoint points[] = {
{150, 150},
{250, 100},
{300, 350}
};
GpPoint points2[] = {
{50, 150},
{150, 350},
{200, 100}
};
XClearWindow (dpy, win);
GdipCreateFromXDrawable_linux (win, dpy, &gp);
status = GdipCreateAdjustableArrowCap (10.0, 6.0, TRUE, &arrowcap);
if (status != Ok) {
fprintf (stderr, "Error creating arrowcap\n");
exit (-1);
}
status = GdipCreatePen1 (solid_black, 1.0, 0, &pen);
if (status != Ok) {
fprintf (stderr, "Error creating pen\n");
exit (-1);
}
status = GdipSetPenCustomStartCap (pen, arrowcap);
if (status != Ok) {
fprintf (stderr, "Error setting start cap\n");
exit (-1);
}
status = GdipSetPenCustomEndCap (pen, arrowcap);
if (status != Ok) {
fprintf (stderr, "Error setting end cap\n");
exit (-1);
}
status = GdipDrawLine (gp, pen, 20, 20, 20, 50);
status = GdipDrawLine (gp, pen, 50, 20, 200, 20);
status = GdipDrawLine (gp, pen, 200, 50, 50, 50);
status = GdipDrawLine (gp, pen, 300, 20, 340, 50);
status = GdipDrawLine (gp, pen, 280, 20, 240, 50);
status = GdipDrawLine (gp, pen, 360, 50, 360, 20);
status = GdipCreatePath (FillModeAlternate, &path);
if (status != Ok) {
fprintf (stderr, "Error creating path\n");
exit (-1);
}
status = GdipAddPathLine (path, 0, 0, 0, 5);
status = GdipAddPathLine (path, 0, 5, 5, 1);
status = GdipAddPathLine (path, 5, 1, 3, 1);
status = GdipCreateCustomLineCap (NULL, path, LineCapFlat, 0, &hookcap);
if (status != Ok) {
fprintf (stderr, "Error creating custom cap\n");
exit (-1);
}
status = GdipSetCustomLineCapStrokeCaps (hookcap, LineCapRound, LineCapRound);
if (status != Ok) {
fprintf (stderr, "Error setting stroke caps\n");
exit (-1);
}
status = GdipCreatePen1 (solid_black, 5.0, 0, &customcappen);
if (status != Ok) {
fprintf (stderr, "Error creating custom cap pen\n");
exit (-1);
}
status = GdipSetPenCustomStartCap (customcappen, hookcap);
if (status != Ok) {
fprintf (stderr, "Error setting custom pen start cap\n");
exit (-1);
}
status = GdipSetPenCustomEndCap (customcappen, hookcap);
if (status != Ok) {
fprintf (stderr, "Error setting custom pen end cap\n");
exit (-1);
}
status = GdipCreatePen1 (solid_red, 10.0, 0, &cappen2);
if (status != Ok) {
fprintf (stderr, "Error creating red pen\n");
exit (-1);
}
status = GdipGetCustomLineCapStrokeCaps (hookcap, &startcap, &endcap);
if (status != Ok) {
fprintf (stderr, "Error getting custom stroke caps\n");
exit (-1);
}
status = GdipSetPenStartCap (cappen2, startcap);
if (status != Ok) {
fprintf (stderr, "Error setting red pen start cap\n");
exit (-1);
}
status = GdipSetPenEndCap (cappen2, endcap);
if (status != Ok) {
fprintf (stderr, "Error setting red pen end cap\n");
exit (-1);
}
status = GdipDrawLinesI (gp, cappen2, points, 3);
if (status != Ok) {
fprintf (stderr, "Error drawing red lines\n");
exit (-1);
}
status = GdipDrawLinesI (gp, customcappen, points, 3);
if (status != Ok) {
fprintf (stderr, "Error drawing custom lines\n");
exit (-1);
}
status = GdipDrawLinesI (gp, cappen2, points2, 3);
if (status != Ok) {
fprintf (stderr, "Error drawing second red lines\n");
exit (-1);
}
status = GdipDrawLinesI (gp, customcappen, points2, 3);
if (status != Ok) {
fprintf (stderr, "Error drawing second custom lines\n");
exit (-1);
}
GdipDeletePath (path);
GdipDeleteCustomLineCap (hookcap);
GdipDeletePen (customcappen);
GdipDeletePen (cappen2);
}
void handle_events (Display *dpy, Window win)
{
XEvent xev;
KeyCode quit_code = XKeysymToKeycode (dpy, XStringToKeysym ("Q"));
while (1) {
XNextEvent (dpy, &xev);
switch (xev.type) {
case KeyPress: {
XKeyEvent *kev = &xev.xkey;
if (kev->keycode == quit_code) {
return;
}
break;
}
case ConfigureNotify: {
XConfigureEvent *cev = &xev.xconfigure;
break;
}
case Expose: {
XExposeEvent *eev = &xev.xexpose;
if (eev->count == 0) {
win_draw (dpy, win);
}
break;
}
}
}
}
int main (int argc, char **argv)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Display *dpy;
int scr;
Window win, root;
GC gc;
dpy = XOpenDisplay (0);
if (dpy == NULL) {
fprintf (stderr, "Failed to open display\n");
exit (-1);
}
XSynchronize (dpy, 1);
root = DefaultRootWindow (dpy);
scr = DefaultScreen (dpy);
win = XCreateSimpleWindow (dpy, root, 0, 0, 400, 400, 0,
WhitePixel (dpy, scr), WhitePixel (dpy, scr));
XSelectInput (dpy, win, KeyPressMask | StructureNotifyMask | ExposureMask);
XMapWindow (dpy, win);
GdiplusStartup (&gdiplusToken, &gdiplusStartupInput, NULL);
win_draw (dpy, win);
handle_events (dpy, win);
GdiplusShutdown (gdiplusToken);
XDestroyWindow (dpy, win);
XCloseDisplay (dpy);
exit (0);
}