public void PatternsAndImages (Context ctx, double x, double y) { ctx.Save (); ctx.Translate (x, y); ctx.SetColor (Colors.Black); // Dashed lines ctx.SetLineWidth (2); ctx.SetLineDash (15, 10, 10, 5, 5); ctx.Rectangle (10, 10, 100, 100); ctx.Stroke (); ctx.SetLineDash (0); // Image var arcColor = new Color (1, 0, 1); ImageBuilder ib = new ImageBuilder (30, 30); ib.Context.Arc (15, 15, 15, 0, 360); ib.Context.SetColor (arcColor); ib.Context.Fill (); ib.Context.SetColor (Colors.DarkKhaki); ib.Context.Rectangle (0, 0, 5, 5); ib.Context.Fill (); var img = ib.ToVectorImage (); ctx.DrawImage (img, 0, 0); ctx.DrawImage (img, 0, 50, 50, 10); ctx.Arc (100, 100, 15, 0, 360); arcColor.Alpha = 0.4; ctx.SetColor (arcColor); ctx.Fill (); // ImagePattern ctx.Save (); ctx.Translate (x + 130, y); ctx.Pattern = new ImagePattern (img); ctx.Rectangle (0, 0, 100, 100); ctx.Fill (); ctx.Restore (); ctx.Restore (); // Setting pixels ctx.SetLineWidth (1); for (int i=0; i<50;i++) { for (var j=0; j<50;j++) { Color c = Color.FromHsl (0.5, (double)i / 50d, (double)j / 50d); ctx.Rectangle (i, j, 1, 1); ctx.SetColor (c); ctx.Fill (); } } }
public virtual void Rotate(Xwt.Drawing.Context ctx, double x, double y) { ctx.Save(); ctx.Translate(x + 30, y + 30); ctx.SetLineWidth(3); // Rotation double end = 270; for (double n = 0; n <= end; n += 5) { ctx.Save(); ctx.Rotate(n); ctx.MoveTo(0, 0); ctx.RelLineTo(30, 0); double c = n / end; ctx.SetColor(new Color(c, c, c)); ctx.Stroke(); ctx.Restore(); } //ctx.ResetTransform(); ctx.Restore(); }
public virtual void Curves2(Context ctx, double sx, double sy) { ctx.Save (); ctx.Translate (sx, sy); ctx.SetColor (Colors.Black); double x = 0, y = 40; double x1 = y - x, y1 = x1 + y, x2 = x + y, y2 = x, x3 = y1, y3 = y; ctx.MoveTo (x, y); ctx.CurveTo (x1, y1, x2, y2, x3, y3); ctx.SetLineWidth (2.0); ctx.Stroke (); ctx.SetColor (new Color (1, 0.2, 0.2, 0.6)); ctx.SetLineWidth (1.0); ctx.MoveTo (x, y); ctx.LineTo (x1, y1); ctx.MoveTo (x2, y2); ctx.LineTo (x3, y3); ctx.Stroke (); ctx.Restore (); }
public virtual void SpeedTest(Xwt.Drawing.Context ctx, double sx, double sy) { ctx.Save(); ctx.Translate(sx, sy); var n = 1000; var ll = 80; var p = new Point(0, 0); for (double i = 1; i < n; i++) { ctx.MoveTo(p.X, p.Y); ctx.SetColor(new Color(i / n, i / n, i / n)); ctx.LineTo(p.X + ll, p.Y + ll); ctx.Stroke(); if (p.Y + ll > this.Bounds.Bottom) { p.Y = 0; p.X += ll + 5; } else { p.Y++; } } ctx.Restore(); }
public virtual void Rotate(Xwt.Drawing.Context ctx, double x, double y) { // draws a line along the x-axis from (0,0) to (r,0) with a constant translation and an increasing // rotational component. This composite transform is then applied to a vertical line, with inverse // color, and an additional x-offset, to form a mirror image figure for easy visual comparison. // These transformed points must be drawn with the identity CTM, hence the Restore() each time. ctx.Save(); // save caller's context (assumed to be the Identity CTM) ctx.SetLineWidth(3); // should align exactly if drawn with half-pixel coordinates // Vector length (pixels) and rotation limit (degrees) double r = 30; double end = 270; for (double n = 0; n <= end; n += 5) { ctx.Save(); // save context and identity CTM for each line // Set up translation to centre point of first figure, ensuring pixel alignment ctx.Translate(x + 30.5, y + 30.5); ctx.Rotate(n); ctx.MoveTo(0, 0); ctx.RelLineTo(r, 0); double c = n / end; ctx.SetColor(new Color(c, c, c)); ctx.Stroke(); // stroke first figure with composite Translation and Rotation CTM // Generate mirror image figure as a visual test of TransformPoints Point p0 = new Point(0, 0); Point p1 = new Point(0, -r); Point[] p = new Point[] { p0, p1 }; ctx.TransformPoints(p); // using composite transformation ctx.Restore(); // restore identity CTM ctx.Save(); // save again (to restore after additional Translation) ctx.Translate(2 * r + 1, 0); // extra x-offset to clear first figure ctx.MoveTo(p[0]); ctx.LineTo(p[1]); c = 1 - c; ctx.SetColor(new Color(c, c, c)); ctx.Stroke(); // stroke transformed points with offset in CTM ctx.Restore(); // restore identity CTM for next line } ctx.Restore(); // restore caller's context }
protected override void OnDraw (Context ctx, Rectangle bounds) { ctx.Save (); ctx.Translate (bounds.Location); ctx.Scale (bounds.Width / Size.Width, bounds.Height / Size.Height); ToolkitEngine.VectorImageRecorderContextHandler.Draw (ctx.Handler, Toolkit.GetBackend (ctx), data); ctx.Restore (); }
protected override void OnDraw(Context ctx, Rectangle bounds) { ctx.Save(); ctx.Translate(bounds.Location); ctx.Scale(bounds.Width / Size.Width, bounds.Height / Size.Height); Paint(ctx); ctx.Restore(); }
protected override void OnDraw(Context ctx, Rectangle bounds) { ctx.Save(); ctx.Translate(bounds.Location); ctx.Scale(bounds.Width / Size.Width, bounds.Height / Size.Height); ToolkitEngine.VectorImageRecorderContextHandler.Draw(ctx.Handler, Toolkit.GetBackend(ctx), data); ctx.Restore(); }
protected void TestDrawing(Xwt.Drawing.Context ctx, double x, double y) { ctx.Save(); ctx.Translate(x, y); var tl = new TextLayout(ctx); tl.Text = "\nX\n"; tl.Width = 200; new ReferencePainter().DrawText(ctx, tl, ref y); ctx.Restore(); }
public virtual void Rotate(Xwt.Drawing.Context ctx, double x, double y) { ctx.Save(); ctx.Translate(x + 30, y + 30); ctx.SetLineWidth(3); // Rotation double end = 270; double r = 30; for (double n = 0; n <= end; n += 5) { ctx.Save(); ctx.Rotate(n); ctx.MoveTo(0, 0); ctx.RelLineTo(r, 0); double c = n / end; ctx.SetColor(new Color(c, c, c)); ctx.Stroke(); // Visual test for TransformPoints Point p0 = new Point(0, 0); Point p1 = new Point(0, -r); Point[] p = new Point[] { p0, p1 }; ctx.TransformPoints(p); ctx.ResetTransform(); ctx.Translate(2 * r + 1, 0); ctx.MoveTo(p[0]); ctx.LineTo(p[1]); c = 1 - c; ctx.SetColor(new Color(c, c, c)); ctx.Stroke(); ctx.Restore(); } ctx.Restore(); }
public virtual void Curves1(Context ctx, double x, double y) { ctx.Save (); ctx.Translate (x, y); ctx.SetLineWidth (1); Action curve1 = () => { ctx.MoveTo (0, 30); ctx.CurveTo (20, 0, 50, 0, 60, 25); }; // curve2 with lineTo; curve1 is closed Action curve2 = () => { ctx.LineTo (0, 0); ctx.CurveTo (20, 30, 50, 30, 60, 5); }; Action paint = () => { curve1 (); curve2 (); ctx.ClosePath (); ctx.SetColor (new Color (0, 0, 0, .5)); ctx.StrokePreserve (); ctx.SetColor (new Color (1, 0, 1, .5)); ctx.Fill (); }; paint (); ctx.Translate (0, 40); // curve2 with moveTo; curve1 is open curve2 = () => { ctx.MoveTo (0, 0); ctx.CurveTo (20, 30, 50, 30, 60, 5); }; paint (); ctx.Restore (); //Todo: same stuff with arc }
protected void TestDrawing2(Xwt.Drawing.Context ctx, double x, double y) { ctx.Save(); ctx.Translate(x, y); var arcColor = new Color(1, 0, 1); ctx.Arc(100, 100, 15, 0, 360); ctx.ClosePath(); arcColor.Alpha = 0.4; ctx.SetColor(arcColor); ctx.StrokePreserve(); ctx.Fill(); ctx.Restore(); }
protected override void OnDraw(Xwt.Drawing.Context ctx) { base.OnDraw(ctx); ctx.Translate(30, 30); double end = 270; for (double n = 0; n <= end; n += 5) { ctx.Save(); ctx.Rotate(n); ctx.MoveTo(0, 0); ctx.RelLineTo(30, 0); double c = n / end; ctx.SetColor(new Color(c, c, c)); ctx.Stroke(); ctx.Restore(); } }
protected void TestDrawing1(Xwt.Drawing.Context ctx, double x, double y) { ctx.Save(); ctx.Translate(x, y); var r = 5; var l = 10; var t = 10; var w = 50; var h = 30; // top left //ctx.Arc(l + r, t + r, r, 180, 270); ctx.Rectangle(l, t, w, h); ctx.SetColor(Colors.Black); ctx.Stroke(); ctx.Restore(); }
internal protected override void Draw(Xwt.Drawing.Context cr, Rectangle area, DocumentLine lineSegment, int line, double x, double y, double lineHeight) { var gutterMarker = lineSegment != null ? (MarginMarker)lineSegment.Markers.FirstOrDefault(marker => marker is MarginMarker && ((MarginMarker)marker).CanDraw(this)) : null; if (gutterMarker != null && gutterMarker.CanDrawBackground(this)) { bool hasDrawn = gutterMarker.DrawBackground(editor, cr, new MarginDrawMetrics(this, area, lineSegment, line, x, y, lineHeight)); if (!hasDrawn) { DrawGutterBackground(cr, line, x, y, lineHeight); } } else { DrawGutterBackground(cr, line, x, y, lineHeight); } if (gutterMarker != null && gutterMarker.CanDrawForeground(this)) { gutterMarker.DrawForeground(editor, cr, new MarginDrawMetrics(this, area, lineSegment, line, x, y, lineHeight)); return; } if (line <= editor.Document.LineCount) { // Due to a mac? gtk bug I need to re-create the layout here // otherwise I get pango exceptions. using (var layout = editor.LayoutCache.RequestLayout()) { layout.Font = gutterFont; layout.Width = (int)Width; layout.Alignment = Alignment.End; layout.SetText(line.ToString()); cr.Save(); cr.Translate(x + (int)Width + (editor.Options.ShowFoldMargin ? 0 : -2), y); cr.SetSourceColor(lineNumberGC); cr.ShowLayout(layout); cr.Restore(); } } }
protected override void OnDraw(Context ctx, Rectangle bounds) { var size = !Size.IsZero ? Size : images [0].Size; ctx.Save(); ctx.Translate(bounds.Location); if (!size.IsZero) { ctx.Scale(bounds.Width / size.Width, bounds.Height / size.Height); } for (int n = 0; n < images.Length; n++) { var image = images [n]; if (image.Size != size) { image = image.WithSize(size); } ctx.DrawImage(image, 0, 0); } ctx.Restore(); }
public override void DrawForeground(TextEditor editor, Xwt.Drawing.Context cr, MarginDrawMetrics metrics) { var width = metrics.Width; var lineNumberBgGC = editor.ColorStyle.LineNumbers.Background; if (metrics.LineNumber <= editor.Document.LineCount) { // Due to a mac? gtk bug I need to re-create the layout here // otherwise I get pango exceptions. using (var layout = PangoUtil.CreateLayout(editor)) { layout.Font = editor.Options.Font; layout.Width = (int)width; layout.TextAlignment = Alignment.End; layout.Text = metrics.LineNumber.ToString(); cr.Save(); cr.Translate(metrics.X + (int)width + (editor.Options.ShowFoldMargin ? 0 : -2), metrics.Y); cr.SetSourceColor(lineNumberBgGC); cr.ShowLayout(layout); cr.Restore(); } } }
public void Path (Context ctx, double px, double py) { ctx.Save (); ctx.Translate (px, py); var path = new DrawingPath (); path.MoveTo (0.44, 18); path.LineTo (-1, 18); path.LineTo (-1, 26); path.LineTo (0.44, 26); path.LineTo (0, 42); path.LineTo (29, 21.98); path.LineTo (29, 21.98); path.LineTo (0, 2); path.LineTo (0.44, 18); ctx.AppendPath (path); ctx.SetColor (Colors.Black); ctx.SetLineWidth (2); ctx.Stroke (); var path2 = path.CopyPath (); path2.LineTo (15, 8); path2.ClosePath (); ctx.Rotate (180); ctx.AppendPath (path2); ctx.SetColor (Colors.Red); ctx.SetLineDash (0, 5); ctx.Stroke (); ctx.Restore (); }
protected override sealed void OnDraw(Context ctx, Rectangle bounds) { var frame = GetFrame (ctx.ScaleFactor); var fixedWidth = frame.Bitmap.Width - 2 - frame.StretchableWidth; var fixedHeight = frame.Bitmap.Height - 2 - frame.StretchableHeight; double totalVariableWidth = bounds.Width - fixedWidth / frame.ScaleFactor; double totalVariableHeight = bounds.Height - fixedHeight / frame.ScaleFactor; double remainingVariableHeight = totalVariableHeight; double y = bounds.Y, yb = 1; int tileIndex = 0; ctx.Save (); if (totalVariableWidth < 0) { if (fixedWidth > 0) ctx.Scale (bounds.Width / fixedWidth, 1); totalVariableWidth = 0; } if (totalVariableHeight < 0) { if (fixedHeight > 0) ctx.Scale (1, bounds.Height / fixedHeight); totalVariableHeight = 0; } foreach (var vs in frame.VerticalSections) { double sh = CalcSectionSize (frame, vs, totalVariableHeight, frame.StretchableHeight, ref remainingVariableHeight); double x = bounds.X, xb = 1; double remainingVariableWidth = totalVariableWidth; foreach (var hs in frame.HorizontalSections) { var sourceRegion = new Rectangle (xb, yb, hs.Size, vs.Size); double sw = CalcSectionSize (frame, hs, totalVariableWidth, frame.StretchableWidth, ref remainingVariableWidth); var targetRegion = new Rectangle (x, y, sw, sh); if (vs.Mode != RenderMode.Tile && hs.Mode != RenderMode.Tile) { var t = GetTile (frame, tileIndex, sourceRegion); ctx.DrawImage (t, targetRegion); } else { double pw = hs.Size / frame.ScaleFactor; double ph = vs.Size / frame.ScaleFactor; if (hs.Mode == RenderMode.Stretch) { pw = targetRegion.Width; } if (vs.Mode == RenderMode.Stretch) { ph = targetRegion.Height; } ctx.Save (); ctx.Translate (targetRegion.Location); targetRegion.Location = Point.Zero; ctx.Pattern = new ImagePattern (GetTile (frame, tileIndex, sourceRegion).WithSize (pw, ph)); ctx.NewPath (); ctx.Rectangle (targetRegion); ctx.Fill (); ctx.Restore (); } x += sw; xb += hs.Size; tileIndex++; } yb += vs.Size; y += sh; } ctx.Restore (); }
void DrawFocus(Context ctx, Point p) { // Draw a 'zoom'-style Focus at specified point. // Following values draw at (0,0) with Size (64,64) double r1 = 12, r2 = 22, w = 31; Point o = Point.Zero; // Drawing origin // Align single-thickness lines on 0.5 pixel coords o.X += 0.5; o.Y += 0.5; ctx.Save (); ctx.SetColor (Colors.Black); ctx.SetLineWidth (1); ctx.Translate (p); // Final translation to point p // draw as 4 quadrants, each rotated by +90 degrees for (double theta = 0; theta < 300; theta += 90) { ctx.Rotate (theta); // Hairline in X-direction, ending at x=r1 ctx.MoveTo (o.X + w, o.Y); ctx.LineTo (o.X + r1, o.Y); // Inner single-thickness arc ctx.Arc (o.X, o.Y, r1, 0, 90); ctx.Stroke (); // Double thickness outer arc, 10 - 80 degrees. Draw at (0,0) and rotate ctx.Save (); ctx.Rotate (10); ctx.MoveTo (r2, 0); ctx.Arc (0, 0, r2, 0, 70); ctx.SetLineWidth (2); ctx.Stroke (); ctx.Restore (); } ctx.Restore (); }
protected override void OnDraw(Context ctx, Rectangle dirtyRect) { base.OnDraw (ctx, dirtyRect); if (!pset) { ParentWindow.BoundsChanged += delegate { QueueDraw (); }; pset = true; } ctx.Rectangle (Bounds); ctx.SetColor (Colors.LightGray); ctx.Fill (); var size = Size; size.Width--; size.Height--; var fx = size.Width / Desktop.Bounds.Width; if (Desktop.Bounds.Height * fx > size.Height) fx = size.Height / Desktop.Bounds.Height; if (Desktop.Bounds.X < 0) ctx.Translate (-Desktop.Bounds.X * fx, 0); if (Desktop.Bounds.Y < 0) ctx.Translate (0, -Desktop.Bounds.Y * fx); ctx.SetLineWidth (1); foreach (var s in Desktop.Screens) { if (s.Bounds != s.VisibleBounds) { var vr = new Rectangle ((int)(s.Bounds.X * fx), (int)(s.Bounds.Y * fx), (int)(s.Bounds.Width * fx), (int)(s.Bounds.Height * fx)); vr = vr.Offset (0.5, 0.5); ctx.Rectangle (vr); ctx.SetColor (Colors.White); ctx.FillPreserve (); ctx.SetColor (Colors.Black); ctx.Stroke (); } var r = new Rectangle ((int)(s.VisibleBounds.X * fx), (int)(s.VisibleBounds.Y * fx), (int)(s.VisibleBounds.Width * fx), (int)(s.VisibleBounds.Height * fx)); r = r.Offset (0.5, 0.5); ctx.Rectangle (r); ctx.SetColor (new Color (0.4, 0.62, 0.83)); ctx.FillPreserve (); ctx.SetColor (Colors.Black); ctx.Stroke (); TextLayout tl = new TextLayout (ctx); tl.Text = s.DeviceName; tl.Font = Font; ctx.DrawTextLayout (tl, r.Center.X - tl.Width / 2, r.Center.Y - tl.Height / 2); } var wr = ParentWindow.ScreenBounds; wr = new Rectangle ((int)(wr.X * fx), (int)(wr.Y * fx), (int)(wr.Width * fx), (int)(wr.Height * fx)); ctx.Rectangle (wr); ctx.SetColor (Colors.Azure.WithAlpha (0.5)); ctx.FillPreserve (); ctx.SetColor (Colors.Azure); ctx.Stroke (); }
void DrawFocus(Context ctx, Point p) { // Draw a 'zoom'-style Focus at specified point double focusRadius = 32; double r = 12, w = focusRadius - 1; Point o = Point.Zero; // Drawing origin // Align single-thickness lines on 0.5 pixel coords o.X += 0.5; o.Y += 0.5; ctx.Save (); ctx.Translate (p); // Final translation // Hairlines in X-direction ctx.MoveTo (o.X + r, o.Y); ctx.LineTo (o.X + w, o.Y); ctx.MoveTo (o.X - r, o.Y); ctx.LineTo (o.X - w, o.Y); // Hairlines in Y-direction ctx.MoveTo (o.X, o.Y + r); ctx.LineTo (o.X, o.Y + w); ctx.MoveTo (o.X, o.Y - r); ctx.LineTo (o.X, o.Y - w); // Inner single-thickness circle ctx.MoveTo (o.X + r, o.Y); ctx.Arc (o.X, o.Y, r, 0, 360); ctx.SetColor (Colors.Black); ctx.SetLineWidth (1); ctx.Stroke (); // Double thickness outer arcs. Draw at (0,0) and rotate o = Point.Zero; r = 22; ctx.Rotate (5); ctx.MoveTo (r, 0); ctx.Arc (o.X, o.Y, r, 0, 80); ctx.MoveTo (o.X, r); ctx.Arc (o.X, o.Y, r, 90, 170); ctx.MoveTo (-r, o.Y); ctx.Arc (o.X, o.Y, r, 180, 260); ctx.MoveTo (o.X, -r); ctx.Arc (o.X, o.Y, r, 270, 350); ctx.SetLineWidth (2); ctx.Stroke (); ctx.Restore (); }
public virtual void Scale(Context ctx, double ax, double ay) { ctx.Save (); ctx.Translate (ax, ay); ctx.SetColor (Colors.Black); ctx.SetLineWidth (1); var x = 0d; var y = 0d; var w = 10d; var inc = .1d; for (var i = inc; i < 3.5d; i +=inc) { ctx.Save (); ctx.Scale (i, i); ctx.Rectangle (x, y, w, w); ctx.SetColor (Colors.Yellow.WithAlpha (1 / i)); ctx.FillPreserve (); ctx.SetColor (Colors.Red.WithAlpha (1 / i)); ctx.Stroke (); ctx.MoveTo (x += w * inc, y += w * inc / 3); ctx.Restore (); } ctx.Restore (); }
protected override void OnDraw(Xwt.Drawing.Context ctx) { base.OnDraw(ctx); // Simple rectangles ctx.SetLineWidth(1); ctx.Rectangle(100, 5, 10, 10); ctx.SetColor(Color.Black); ctx.Fill(); ctx.Rectangle(115, 5, 10, 10); ctx.SetColor(Color.Black); ctx.Stroke(); // ctx.SetLineWidth(3); ctx.Rectangle(100, 20, 10, 10); ctx.SetColor(Color.Black); ctx.Fill(); ctx.Rectangle(115, 20, 10, 10); ctx.SetColor(Color.Black); ctx.Stroke(); // Rectangle with hole ctx.Rectangle(10, 100, 40, 40); ctx.MoveTo(45, 135); ctx.RelLineTo(0, -20); ctx.RelLineTo(-20, 0); ctx.RelLineTo(0, 20); ctx.ClosePath(); ctx.SetColor(Color.Black); ctx.Fill(); // Dashed lines ctx.SetLineDash(15, 10, 10, 5, 5); ctx.Rectangle(100, 100, 100, 100); ctx.Stroke(); ctx.SetLineDash(0); ImageBuilder ib = new ImageBuilder(30, 30, ImageFormat.ARGB32); ib.Context.Arc(15, 15, 15, 0, 360); ib.Context.SetColor(new Color(1, 0, 1)); ib.Context.Rectangle(0, 0, 5, 5); ib.Context.Fill(); var img = ib.ToImage(); ctx.DrawImage(img, 90, 90); ctx.DrawImage(img, 90, 140, 50, 10); ctx.Arc(190, 190, 15, 0, 360); ctx.SetColor(new Color(1, 0, 1, 0.4)); ctx.Fill(); ctx.Save(); ctx.Translate(90, 220); ctx.Pattern = new ImagePattern(img); ctx.Rectangle(0, 0, 100, 70); ctx.Fill(); ctx.Restore(); ctx.Translate(30, 30); double end = 270; for (double n = 0; n <= end; n += 5) { ctx.Save(); ctx.Rotate(n); ctx.MoveTo(0, 0); ctx.RelLineTo(30, 0); double c = n / end; ctx.SetColor(new Color(c, c, c)); ctx.Stroke(); ctx.Restore(); } ctx.ResetTransform(); }
protected override void OnDraw(Context ctx) { ctx.Translate (-hscroll.Value, -vscroll.Value); ctx.Rectangle (new Rectangle (0, 0, imageSize, imageSize)); ctx.SetColor (Color.White); ctx.Fill (); ctx.Arc (imageSize / 2, imageSize / 2, imageSize / 2 - 20, 0, 360); ctx.SetColor (new Color (0,0,1)); ctx.Fill (); ctx.ResetTransform (); ctx.Rectangle (0, 0, Bounds.Width, 30); ctx.SetColor (new Color (1, 0, 0, 0.5)); ctx.Fill (); }
/// <summary> /// Draw the Axis Label /// </summary> /// <param name="ctx>The Drawing Context with which to draw.</param> /// <param name="offset">offset from axis. Should be calculated so as to make sure axis label misses tick labels.</param> /// <param name="axisPhysicalMin">The physical position corresponding to the world minimum of the axis.</param> /// <param name="axisPhysicalMax">The physical position corresponding to the world maximum of the axis.</param> /// <returns>boxed Rectangle indicating bounding box of label. null if no label printed.</returns> public object DrawLabel(Context ctx, Point offset, Point axisPhysicalMin, Point axisPhysicalMax) { if (Label != "") { // first calculate any extra offset for axis label spacing. double extraOffsetAmount = LabelOffset; extraOffsetAmount += 2; // empirically determed - text was too close to axis before this. if (AutoScaleText && LabelOffsetScaled) { extraOffsetAmount *= FontScale; } // now extend offset. double offsetLength = Math.Sqrt (offset.X*offset.X + offset.Y*offset.Y); if (offsetLength > 0.01) { double x_component = offset.X / offsetLength; double y_component = offset.Y / offsetLength; x_component *= extraOffsetAmount; y_component *= extraOffsetAmount; if (LabelOffsetAbsolute) { offset.X = x_component; offset.Y = y_component; } else { offset.X += x_component; offset.Y += y_component; } } // determine angle of axis in degrees double theta = Math.Atan2 ( axisPhysicalMax.Y - axisPhysicalMin.Y, axisPhysicalMax.X - axisPhysicalMin.X); theta = theta * 180.0 / Math.PI; Point average = new Point ( (axisPhysicalMax.X + axisPhysicalMin.X)/2, (axisPhysicalMax.Y + axisPhysicalMin.Y)/2); ctx.Save (); ctx.Translate (average.X + offset.X , average.Y + offset.Y); // this is done last. ctx.Rotate (theta); // this is done first. TextLayout layout = new TextLayout (); layout.Font = labelFontScaled; layout.Text = Label; Size labelSize = layout.GetSize (); //Draw label centered around zero. ctx.DrawTextLayout (layout, -labelSize.Width/2, -labelSize.Height/2); // now work out physical bounds of Rotated and Translated label. Point [] recPoints = new Point [2]; recPoints[0] = new Point (-labelSize.Width/2, -labelSize.Height/2); recPoints[1] = new Point ( labelSize.Width/2, labelSize.Height/2); ctx.TransformPoints (recPoints); double x1 = Math.Min (recPoints[0].X, recPoints[1].X); double x2 = Math.Max (recPoints[0].X, recPoints[1].X); double y1 = Math.Min (recPoints[0].Y, recPoints[1].Y); double y2 = Math.Max (recPoints[0].Y, recPoints[1].Y); ctx.Restore (); // and return label bounding box. return new Rectangle (x1, y1, (x2-x1), (y2-y1)); } return null; }
public virtual void Rectangles (Context ctx, double x, double y) { ctx.Save (); ctx.Translate (x, y); // Simple rectangles ctx.SetLineWidth (1); ctx.Rectangle (0, 0, 10, 10); ctx.SetColor (Colors.Black); ctx.Fill (); ctx.Rectangle (15, 0, 10, 10); ctx.SetColor (Colors.Black); ctx.Stroke (); ctx.SetLineWidth (3); ctx.Rectangle (0, 15, 10, 10); ctx.SetColor (Colors.Black); ctx.Fill (); ctx.Rectangle (15, 15, 10, 10); ctx.SetColor (Colors.Black); ctx.Stroke (); ctx.Restore (); // Rectangle with hole ctx.Save (); ctx.Translate (x + 50, y); ctx.Rectangle (0, 0, 40, 40); ctx.MoveTo (35, 35); ctx.RelLineTo (0, -20); ctx.RelLineTo (-20, 0); ctx.RelLineTo (0, 20); ctx.ClosePath (); ctx.SetColor (Colors.Black); ctx.Fill (); ctx.Restore (); // Rounded Rectangle with Arcs ctx.Save (); ctx.Translate (x + 120, y); var r = 5; var l = 0; var t = 0; var w = 50; var h = 30; ctx.SetColor (Colors.Black); // top left ctx.Arc (l + r, t + r, r, 180, 270); // top right ctx.Arc (l + w - r, t + r, r, 270, 0); // bottom right ctx.Arc (l + w - r, t + h - r, r, 0, 90); // bottom left ctx.Arc (l + r, t + h - r, r, 90, 180); ctx.ClosePath (); ctx.StrokePreserve (); ctx.SetColor (Colors.AntiqueWhite); ctx.Fill (); ctx.Restore (); }
/// <summary> /// Draw text. /// </summary> protected override void OnDraw(Context ctx, Rectangle dirtyRect) { base.OnDraw(ctx, dirtyRect); // If no template dont draw! if (Template == null) return; // Stop drawing (count size for request) CanDraw = false; // Clear ClearValues(); // Set Context CTX = ctx; // Data set DrawText("{"); NextLine(); Right(); DrawJson(Template); Left(); DrawText("}"); // Set image size - scrollbars! if (hscroll != null && vscroll != null && (hscroll.UpperValue != maxX || vscroll.UpperValue != Y+20)) { hscroll.UpperValue = maxX; vscroll.UpperValue = Y + 20; } // Start drawing CanDraw = true; // Clear and draw again! - translate context! if (hscroll != null && vscroll != null) ctx.Translate(-hscroll.Value, -vscroll.Value); // Clear values! ClearValues(); // Data set DrawText("{"); NextLine(); Right(); DrawJson(Template); Left(); DrawText("}"); }
protected override void OnDraw(Context ctx, Rectangle dirtyRect) { ctx.Save (); ctx.Translate (-hscroll.Value, -vscroll.Value); ctx.Rectangle (new Rectangle (0, 0, imageSize, imageSize)); ctx.SetColor (Xwt.Drawing.Colors.White); ctx.Fill (); ctx.Arc (imageSize / 2, imageSize / 2, imageSize / 2 - 20, 0, 360); ctx.SetColor (new Color (0,0,1)); ctx.Fill (); ctx.Restore (); ctx.Rectangle (0, 0, Bounds.Width, 30); ctx.SetColor (new Color (1, 0, 0, 0.5)); ctx.Fill (); }
/// <summary> /// Called when the widget needs to be redrawn /// </summary> /// <param name='ctx'> /// Drawing context /// </param> /// <param name="dirtyRect"></param> protected override void OnDraw(Context ctx, Rectangle dirtyRect) { if (Bounds.IsEmpty) return; base.OnDraw(ctx, dirtyRect); // apply scale factor dirtyRect.X /= scaleFactor; dirtyRect.Y /= scaleFactor; dirtyRect.Width /= scaleFactor; dirtyRect.Height /= scaleFactor; // actual drawing bool redraw = Draw(ctx, dirtyRect, scaleFactor); // draw minimap if (ShowMiniMap && MinWidth > 0 && MinHeight > 0) { Point size = new Point(180.0, 180.0 * (MinHeight / MinWidth)); double minimapScale = Math.Min(size.X / MinWidth, size.Y / MinHeight); Point minimapPosition = new Point( scrollview.HorizontalScrollControl.Value + scrollview.HorizontalScrollControl.PageSize - size.X - 16, scrollview.VerticalScrollControl.Value + 16); ctx.RoundRectangle(minimapPosition, size.X, size.Y, 6); ctx.SetColor(Colors.LightGray.WithAlpha(0.4)); ctx.Fill(); ctx.Save(); ctx.Translate(minimapPosition); Draw(ctx, new Rectangle(0, 0, MinWidth, MinHeight), minimapScale); ctx.Restore(); } // set canvas min size foreach (PipelineNode node in nodes) { Rectangle boundwe = node.BoundWithExtras; if (boundwe.Right * scaleFactor > MinWidth) { MinWidth = boundwe.Right * scaleFactor + PipelineNode.NodeMargin.Right; } if (boundwe.Bottom * scaleFactor > MinHeight) { MinHeight = boundwe.Bottom * scaleFactor + PipelineNode.NodeMargin.Bottom; } Point offset = new Point(Math.Max(0, -boundwe.Left), Math.Max(0, -boundwe.Top)); if (offset != Point.Zero) { TranslateAllNodesBy(offset); redraw = true; QueueDraw(); } } // update things if (mouseAction.HasFlag(MouseAction.MoveNode)) { // move scrollbar Rectangle boundwe = lastSelectedNode.BoundWithExtras; boundwe.X *= scaleFactor; boundwe.Y *= scaleFactor; boundwe.Height *= scaleFactor; boundwe.Width *= scaleFactor; double viewportRight = scrollview.HorizontalScrollControl.Value + scrollview.Size.Width; double offsetH = (nodeToMoveOffset.X + boundwe.Width) * 0.5 / scaleFactor; if (boundwe.Right - offsetH > viewportRight) { scrollview.HorizontalScrollControl.Value += boundwe.Right - offsetH - viewportRight; } else if (boundwe.Left + offsetH < scrollview.HorizontalScrollControl.Value) { scrollview.HorizontalScrollControl.Value -= scrollview.HorizontalScrollControl.Value - offsetH - boundwe.Left; } double viewportBottom = scrollview.VerticalScrollControl.Value + scrollview.Size.Height; double offsetV = (nodeToMoveOffset.Y + boundwe.Height) * 0.5; if (boundwe.Bottom - offsetV > viewportBottom) { scrollview.VerticalScrollControl.Value += boundwe.Bottom - offsetV - viewportBottom; } else if (boundwe.Top + offsetV < scrollview.VerticalScrollControl.Value) { scrollview.VerticalScrollControl.Value -= scrollview.VerticalScrollControl.Value - offsetV - boundwe.Top; } } if ((mouseAction.HasFlag(MouseAction.AddEdge) || mouseAction.HasFlag(MouseAction.MoveEdge)) && !mouseAction.HasFlag(MouseAction.AddEdgeNew)) { ctx.MoveTo( connectNodesStartMarker.IsInput ? connectNodesStartMarker.Bounds.Left * scaleFactor : connectNodesStartMarker.Bounds.Right * scaleFactor, connectNodesStartMarker.Bounds.Center.Y * scaleFactor ); ctx.LineTo(connectNodesEnd.X * scaleFactor, connectNodesEnd.Y * scaleFactor); ctx.Stroke(); } // set redraw finish? redrawQueued = redraw; // initial scroll position if (!initialScrollPosition.IsEmpty && !redraw && (scrollview.HorizontalScrollControl.Value < 1.0 || scrollview.VerticalScrollControl.Value < 1.0)) { scrollview.HorizontalScrollControl.Value = Math.Min( initialScrollPosition.X, scrollview.HorizontalScrollControl.UpperValue - scrollview.HorizontalScrollControl.PageSize ); scrollview.VerticalScrollControl.Value = Math.Min( initialScrollPosition.Y, scrollview.VerticalScrollControl.UpperValue - scrollview.VerticalScrollControl.PageSize ); } }
/// <summary> /// Draw a tick on the axis. /// </summary> /// <param name="ctx">The Drawing Context with on which to draw.</param> /// <param name="w">The tick position in world coordinates.</param> /// <param name="size">The size of the tick (in pixels)</param> /// <param name="text">The text associated with the tick</param> /// <param name="textOffset">The Offset to draw from the auto calculated position</param> /// <param name="axisPhysMin">The minimum physical extent of the axis</param> /// <param name="axisPhysMax">The maximum physical extent of the axis</param> /// <param name="boundingBox">out: The bounding rectangle for the tick and tickLabel drawn</param> /// <param name="labelOffset">out: offset from the axies required for axis label</param> public virtual void DrawTick( Context ctx, double w, double size, string text, Point textOffset, Point axisPhysMin, Point axisPhysMax, out Point labelOffset, out Rectangle boundingBox ) { // determine physical location where tick touches axis. Point tickStart = WorldToPhysical (w, axisPhysMin, axisPhysMax, true); // determine offset from start point. Point axisDir = Utils.UnitVector (axisPhysMin, axisPhysMax); // rotate axisDir anti-clockwise by TicksAngle radians to get tick direction. Note that because // the physical (pixel) origin is at the top left, a RotationTransform by a positive angle will // be clockwise. Consequently, for anti-clockwise rotations, use cos(A-B), sin(A-B) formulae double x1 = Math.Cos (TicksAngle) * axisDir.X + Math.Sin (TicksAngle) * axisDir.Y; double y1 = Math.Cos (TicksAngle) * axisDir.Y - Math.Sin (TicksAngle) * axisDir.X; // now get the scaled tick vector. Point tickVector = new Point (TickScale * size * x1, TickScale * size * y1); if (TicksCrossAxis) { tickStart.X -= tickVector.X / 2; tickStart.Y -= tickVector.Y / 2; } // and the end point [point off axis] of tick mark. Point tickEnd = new Point (tickStart.X + tickVector.X, tickStart.Y + tickVector.Y); // and draw it ctx.SetLineWidth (1); ctx.SetColor (LineColor); ctx.MoveTo (tickStart.X+0.5, tickStart.Y+0.5); ctx.LineTo (tickEnd.X+0.5, tickEnd.Y+0.5); ctx.Stroke (); // calculate bounds of tick. double minX = Math.Min (tickStart.X, tickEnd.X); double minY = Math.Min (tickStart.Y, tickEnd.Y); double maxX = Math.Max (tickStart.X, tickEnd.X); double maxY = Math.Max (tickStart.Y, tickEnd.Y); boundingBox = new Rectangle (minX, minY, maxX-minX, maxY-minY); // by default, label offset from axis is 0. TODO: revise this. labelOffset = new Point (-tickVector.X, -tickVector.Y); // ------------------------ // now draw associated text. // **** TODO **** // The following code needs revising. A few things are hard coded when // they should not be. Also, angled tick text currently just works for // the bottom x-axis. Also, it's a bit hacky. if (text != "" && !HideTickText) { TextLayout layout = new TextLayout (); layout.Font = tickTextFontScaled; layout.Text = text; Size textSize = layout.GetSize (); // determine the center point of the tick text. double textCenterX; double textCenterY; // if text is at pointy end of tick. if (!TickTextNextToAxis) { // offset due to tick. textCenterX = tickStart.X + tickVector.X*1.2; textCenterY = tickStart.Y + tickVector.Y*1.2; // offset due to text box size. textCenterX += 0.5 * x1 * textSize.Width; textCenterY += 0.5 * y1 * textSize.Height; } // else it's next to the axis. else { // start location. textCenterX = tickStart.X; textCenterY = tickStart.Y; // offset due to text box size. textCenterX -= 0.5 * x1 * textSize.Width; textCenterY -= 0.5 * y1 * textSize.Height; // bring text away from the axis a little bit. textCenterX -= x1*(2.0+FontScale); textCenterY -= y1*(2.0+FontScale); } // If tick text is angled.. if (TickTextAngle != 0) { // determine the point we want to rotate text about. Point textScaledTickVector = new Point ( TickScale * x1 * (textSize.Height/2), TickScale * y1 * (textSize.Height/2) ); Point rotatePoint; if (TickTextNextToAxis) { rotatePoint = new Point ( tickStart.X - textScaledTickVector.X, tickStart.Y - textScaledTickVector.Y); } else { rotatePoint = new Point ( tickEnd.X + textScaledTickVector.X, tickEnd.Y + textScaledTickVector.Y); } double actualAngle; if (FlipTickText) { double radAngle = TickTextAngle * Math.PI / 180; rotatePoint.X += textSize.Width * Math.Cos (radAngle); rotatePoint.Y += textSize.Width * Math.Sin (radAngle); actualAngle = TickTextAngle + 180; } else { actualAngle = TickTextAngle; } ctx.Save (); ctx.Translate (rotatePoint.X, rotatePoint.Y); ctx.Rotate (actualAngle); Point [] recPoints = new Point [2]; recPoints[0] = new Point (0.0, -textSize.Height/2); recPoints[1] = new Point (textSize.Width, textSize.Height); ctx.TransformPoints (recPoints); double t_x1 = Math.Min (recPoints[0].X, recPoints[1].X); double t_x2 = Math.Max (recPoints[0].X, recPoints[1].X); double t_y1 = Math.Min (recPoints[0].Y, recPoints[1].Y); double t_y2 = Math.Max (recPoints[0].Y, recPoints[1].Y); boundingBox = Rectangle.Union (boundingBox, new Rectangle (t_x1, t_y1, (t_x2-t_x1), (t_y2-t_y1))); ctx.DrawTextLayout (layout, 0, -textSize.Height/2); t_x2 -= tickStart.X; t_y2 -= tickStart.Y; t_x2 *= 1.25; t_y2 *= 1.25; labelOffset = new Point (t_x2, t_y2); ctx.Restore (); //ctx.Rectangle (boundingBox.X, boundingBox.Y, boundingBox.Width, boundingBox.Height); //ctx.Stroke (); } else { double bx1 = (textCenterX - textSize.Width/2); double by1 = (textCenterY - textSize.Height/2); double bx2 = textSize.Width; double by2 = textSize.Height; Rectangle drawRect = new Rectangle (bx1, by1, bx2, by2); // ctx.Rectangle (drawRect); boundingBox = Rectangle.Union (boundingBox, drawRect); // ctx.Rectangle (boundingBox); ctx.DrawTextLayout (layout, bx1, by1); textCenterX -= tickStart.X; textCenterY -= tickStart.Y; textCenterX *= 2.3; textCenterY *= 2.3; labelOffset = new Point (textCenterX, textCenterY); } } }
protected sealed override void OnDraw(Context ctx, Rectangle bounds) { if (bounds.Width <= 0 || bounds.Height <= 0) { return; } var frame = GetFrame(ctx.ScaleFactor); var fixedWidth = frame.Bitmap.Width - 2 - frame.StretchableWidth; var fixedHeight = frame.Bitmap.Height - 2 - frame.StretchableHeight; double totalVariableWidth = bounds.Width - fixedWidth / frame.ScaleFactor; double totalVariableHeight = bounds.Height - fixedHeight / frame.ScaleFactor; double remainingVariableHeight = totalVariableHeight; double y = bounds.Y, yb = 1; int tileIndex = 0; ctx.Save(); if (totalVariableWidth < 0) { if (fixedWidth > 0) { ctx.Scale(bounds.Width / fixedWidth, 1); } totalVariableWidth = 0; } if (totalVariableHeight < 0) { if (fixedHeight > 0) { ctx.Scale(1, bounds.Height / fixedHeight); } totalVariableHeight = 0; } foreach (var vs in frame.VerticalSections) { double sh = CalcSectionSize(frame, vs, totalVariableHeight, frame.StretchableHeight, ref remainingVariableHeight); double x = bounds.X, xb = 1; double remainingVariableWidth = totalVariableWidth; foreach (var hs in frame.HorizontalSections) { var sourceRegion = new Rectangle(xb, yb, hs.Size, vs.Size); double sw = CalcSectionSize(frame, hs, totalVariableWidth, frame.StretchableWidth, ref remainingVariableWidth); var targetRegion = new Rectangle(x, y, sw, sh); if (vs.Mode != RenderMode.Tile && hs.Mode != RenderMode.Tile) { var t = GetTile(frame, tileIndex, sourceRegion); ctx.DrawImage(t, targetRegion); } else { double pw = hs.Size / frame.ScaleFactor; double ph = vs.Size / frame.ScaleFactor; if (hs.Mode == RenderMode.Stretch) { pw = targetRegion.Width; } if (vs.Mode == RenderMode.Stretch) { ph = targetRegion.Height; } if (pw <= 0 || ph <= 0) { continue; } ctx.Save(); ctx.Translate(targetRegion.Location); targetRegion.Location = Point.Zero; ctx.Pattern = new ImagePattern(GetTile(frame, tileIndex, sourceRegion).WithSize(pw, ph)); ctx.NewPath(); ctx.Rectangle(targetRegion); ctx.Fill(); ctx.Restore(); } x += sw; xb += hs.Size; tileIndex++; } yb += vs.Size; y += sh; } ctx.Restore(); }
public virtual void Texts(Xwt.Drawing.Context ctx, double x, double y) { ctx.Save(); ctx.Translate(x, y); ctx.SetColor(Colors.Black); var col1 = new Rectangle(); var col2 = new Rectangle(); var text = new TextLayout(); text.Font = this.Font.WithSize(24); Console.WriteLine(text.Font.Size); // first text text.Text = "Lorem ipsum dolor sit amet,"; var size1 = text.GetSize(); col1.Width = size1.Width; col1.Height += size1.Height + 10; ctx.DrawTextLayout(text, 0, 0); // proofing width; test should align with text above ctx.SetColor(Colors.DarkMagenta); text.Text = "consetetur sadipscing elitr, sed diam nonumy"; text.Width = col1.Width; var size2 = text.GetSize(); ctx.DrawTextLayout(text, 0, col1.Bottom); col1.Height += size2.Height + 10; ctx.SetColor(Colors.Black); // proofing scale, on second col ctx.Save(); ctx.SetColor(Colors.Red); col2.Left = col1.Right + 10; text.Text = "eirmod tempor invidunt ut."; var scale = 1.2; text.Width = text.Width / scale; var size3 = text.GetSize(); col2.Height = size3.Height * scale; col2.Width = size3.Width * scale + 5; ctx.Scale(scale, scale); ctx.DrawTextLayout(text, col2.Left / scale, col2.Top / scale); ctx.Restore(); // proofing heigth, on second col ctx.Save(); ctx.SetColor(Colors.DarkCyan); text.Text = "Praesent ac lacus nec dolor pulvinar feugiat a id elit."; var size4 = text.GetSize(); text.Height = size4.Height / 2; text.Trimming = TextTrimming.WordElipsis; ctx.DrawTextLayout(text, col2.Left, col2.Bottom + 5); ctx.SetLineWidth(1); ctx.SetColor(Colors.Blue); ctx.Rectangle(new Rectangle(col2.Left, col2.Bottom + 5, text.Width, text.Height)); ctx.Stroke(); ctx.Restore(); // drawing col line ctx.SetLineWidth(1); ctx.SetColor(Colors.Black.WithAlpha(.5)); ctx.MoveTo(col1.Right + 5, col1.Top); ctx.LineTo(col1.Right + 5, col1.Bottom); ctx.Stroke(); ctx.MoveTo(col2.Right + 5, col2.Top); ctx.LineTo(col2.Right + 5, col2.Bottom); ctx.Stroke(); ctx.SetColor(Colors.Black); // proofing rotate, and printing size to see the values ctx.Save(); text.Font = this.Font.WithSize(10); text.Text = string.Format("Size 1 {0}\r\nSize 2 {1}\r\nSize 3 {2} Scale {3}", size1, size2, size3, scale); text.Width = -1; // this clears textsize text.Height = -1; ctx.Rotate(5); // maybe someone knows a formula with angle and textsize to calculyte ty var ty = 30; ctx.DrawTextLayout(text, ty, col1.Bottom + 10); ctx.Restore(); // scale example here: ctx.Restore(); TextLayout tl0 = new TextLayout(this); tl0.Font = this.Font.WithSize(10); tl0.Text = "This text contains attributes."; tl0.SetUnderline(0, "This".Length); tl0.SetForeground(new Color(0, 1.0, 1.0), "This ".Length, "text".Length); tl0.SetBackground(new Color(0, 0, 0), "This ".Length, "text".Length); tl0.SetFontWeight(FontWeight.Bold, "This text ".Length, "contains".Length); tl0.SetFontStyle(FontStyle.Italic, "This text ".Length, "contains".Length); tl0.SetStrikethrough("This text contains ".Length, "attributes".Length); ctx.DrawTextLayout(tl0, col2.Left, col2.Bottom + 100); // Text boces y = 180; // Without wrapping TextLayout tl = new TextLayout(this); tl.Text = "Stright text"; DrawText(ctx, tl, ref y); // With wrapping tl = new TextLayout(this); tl.Text = "The quick brown fox jumps over the lazy dog"; tl.Width = 100; DrawText(ctx, tl, ref y); // With blank lines tl = new TextLayout(this); tl.Text = "\nEmpty line above\nLine break above\n\nEmpty line above\n\n\nTwo empty lines above\nEmpty line below\n"; tl.Width = 200; DrawText(ctx, tl, ref y); }
/// <summary> /// Illustrates the use of matrix transforms to skew and reflect text /// </summary> public void Reflect (Context ctx, double x, double y) { ctx.Save (); ctx.SetLineWidth (1); TextLayout layout = new TextLayout (); layout.Text = "Reflected and Skewed Text"; layout.Font = Font.WithSize (16); Size size = layout.GetSize (); Rectangle r = new Rectangle (Point.Zero, size); // Draw text with no transformations at (x+0.5, y+0.5) ctx.Translate (x+0.5, y+0.5); // final move to specified location ctx.SetColor (Colors.Blue); ctx.DrawTextLayout (layout, 0, 0); // Use Matrix transforms to reflect Y-values and skew X-values by -0.5*Y // Note that transforms are prepended, so are actioned in reverse order // This is the same order that Backend Context transforms are applied Matrix m = new Matrix (); // Identity matrix Matrix s = new Matrix (1.0, 0.0, // new skew matrix -0.5, 1.0, 0.0, 0.0); m.Translate (0, size.Height); // Shift text back to place m.Prepend (s); // Skew X-values m.Scale (1, -1); // Reflect text Y-values m.Translate (0, -size.Height); // Shift text base to (0,0) ctx.ModifyCTM (m); // NB ctx.Translate (x+0.5, y+0.5) still active ctx.SetColor (Colors.DarkGray); ctx.Rectangle (r); ctx.Fill (); ctx.SetColor (Colors.LightBlue); ctx.DrawTextLayout (layout, 0, 0); ctx.Restore (); }