275 строки
6.8 KiB
C#
275 строки
6.8 KiB
C#
//
|
|
// DrawingContext.cs
|
|
//
|
|
// Author:
|
|
// Eric Maupin <ermau@xamarin.com>
|
|
// Lytico (http://limada.sourceforge.net)
|
|
//
|
|
// Copyright (c) 2012 Xamarin, Inc.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using System.Windows.Media;
|
|
using SWM = System.Windows.Media;
|
|
using SW = System.Windows;
|
|
|
|
namespace Xwt.WPFBackend
|
|
{
|
|
internal class DrawingContext:IDisposable
|
|
{
|
|
Stack<ContextData> pushes = new Stack<ContextData> ();
|
|
TransformGroup transforms = new TransformGroup ();
|
|
int pushCount;
|
|
bool disposed;
|
|
bool positionSet;
|
|
|
|
PathGeometry geometry;
|
|
SWM.Brush patternBrush;
|
|
SWM.SolidColorBrush colorBrush;
|
|
|
|
public double ScaleFactor { get; set; }
|
|
|
|
public SW.Point LastFigureStart { get; set; }
|
|
public SW.Point EndPoint { get; set; }
|
|
|
|
public PathFigure Path { get; private set; }
|
|
public SWM.Pen Pen { get; private set; }
|
|
|
|
public SWM.Brush Brush {
|
|
get {
|
|
return patternBrush ?? colorBrush;
|
|
}
|
|
}
|
|
|
|
public SWM.Matrix CurrentTransform {
|
|
get {
|
|
TransformCollection children = transforms.Children;
|
|
Matrix ctm = Matrix.Identity;
|
|
foreach (Transform t in children) {
|
|
ctm.Prepend (t.Value);
|
|
};
|
|
return ctm;
|
|
}
|
|
}
|
|
|
|
class ContextData
|
|
{
|
|
public int PushCount;
|
|
public Color CurrentColor;
|
|
public double Thickness;
|
|
public DashStyle DashStyle;
|
|
public Brush Pattern;
|
|
public int TransformCount;
|
|
}
|
|
|
|
public System.Windows.Media.DrawingContext Context { get; private set; }
|
|
|
|
internal DrawingContext (System.Windows.Media.DrawingContext dc, double scaleFactor)
|
|
{
|
|
Context = dc;
|
|
ScaleFactor = scaleFactor;
|
|
AllocatePen (Color.FromRgb (0, 0, 0), 1, DashStyles.Solid);
|
|
ResetPath ();
|
|
}
|
|
|
|
internal DrawingContext (DrawingContext context)
|
|
{
|
|
Context = context.Context;
|
|
|
|
if (context.Pen != null)
|
|
AllocatePen (context.colorBrush.Color, context.Pen.Thickness, context.Pen.DashStyle);
|
|
|
|
patternBrush = context.patternBrush;
|
|
|
|
geometry = (PathGeometry) context.Geometry.Clone ();
|
|
Path = geometry.Figures[geometry.Figures.Count - 1];
|
|
positionSet = context.positionSet;
|
|
}
|
|
|
|
internal DrawingContext()
|
|
{
|
|
ResetPath ();
|
|
}
|
|
|
|
public void AppendPath (DrawingContext context)
|
|
{
|
|
foreach (var f in context.Geometry.Figures)
|
|
geometry.Figures.Add (f.Clone ());
|
|
Path = context.geometry.Figures[context.geometry.Figures.Count - 1];
|
|
}
|
|
|
|
public void Save ()
|
|
{
|
|
var cd = new ContextData () {
|
|
Thickness = Pen.Thickness,
|
|
CurrentColor = colorBrush.Color,
|
|
PushCount = pushCount,
|
|
Pattern = patternBrush,
|
|
DashStyle = Pen.DashStyle,
|
|
TransformCount = transforms.Children.Count,
|
|
};
|
|
pushes.Push (cd);
|
|
pushCount = 0;
|
|
}
|
|
|
|
public void Restore ()
|
|
{
|
|
if (pushes.Count == 0)
|
|
return;
|
|
|
|
for (int n = 0; n < pushCount; n++)
|
|
Context.Pop ();
|
|
|
|
var cd = pushes.Pop ();
|
|
pushCount = cd.PushCount;
|
|
|
|
while (transforms.Children.Count > cd.TransformCount)
|
|
transforms.Children.RemoveAt (transforms.Children.Count - 1);
|
|
|
|
AllocatePen (cd.CurrentColor, cd.Thickness, cd.DashStyle);
|
|
patternBrush = cd.Pattern;
|
|
}
|
|
|
|
public void NotifyPush ()
|
|
{
|
|
pushCount++;
|
|
}
|
|
|
|
public void PushTransform (Transform t)
|
|
{
|
|
Context.PushTransform (t);
|
|
transforms.Children.Add (t);
|
|
pushCount++;
|
|
}
|
|
|
|
public void SetColor (Color color)
|
|
{
|
|
patternBrush = null;
|
|
AllocatePen (color, Pen != null ? Pen.Thickness : 1, Pen != null ? Pen.DashStyle : DashStyles.Solid);
|
|
}
|
|
|
|
public void SetThickness (double t)
|
|
{
|
|
var ds = Pen.DashStyle;
|
|
if (ds != DashStyles.Solid) {
|
|
var dashes = Pen.DashStyle.Dashes.Clone ();
|
|
for (int n = 0; n < dashes.Count; n++)
|
|
dashes[n] = (dashes[n] * Pen.Thickness) / t;
|
|
var offset = (Pen.DashStyle.Offset * Pen.Thickness) / t;
|
|
ds = new DashStyle (dashes, offset);
|
|
}
|
|
AllocatePen (colorBrush.Color, t, ds);
|
|
}
|
|
|
|
internal void SetDash (double offset, double[] pattern)
|
|
{
|
|
DashStyle ds = new DashStyle (pattern.Select (d => d / Pen.Thickness), offset);
|
|
AllocatePen (colorBrush.Color, Pen.Thickness, ds);
|
|
}
|
|
|
|
void AllocatePen (Color color, double thickness, DashStyle dashStyle)
|
|
{
|
|
if (colorBrush != null && color == colorBrush.Color) {
|
|
if (Pen.Thickness != thickness || !dashStyle.Equals (Pen.DashStyle))
|
|
Pen = new SWM.Pen (colorBrush, thickness) {
|
|
DashStyle = dashStyle
|
|
};
|
|
}
|
|
else
|
|
{
|
|
colorBrush = new SolidColorBrush (color);
|
|
Pen = new SWM.Pen (colorBrush, thickness) {
|
|
DashStyle = dashStyle
|
|
};
|
|
}
|
|
Pen.DashCap = PenLineCap.Flat;
|
|
}
|
|
|
|
public void SetPattern (Brush brush)
|
|
{
|
|
patternBrush = brush;
|
|
}
|
|
|
|
public void NewFigure (SW.Point p)
|
|
{
|
|
if (Path.Segments.Count > 0) {
|
|
Path = new PathFigure ();
|
|
geometry.Figures.Add (Path);
|
|
}
|
|
LastFigureStart = p;
|
|
EndPoint = p;
|
|
Path.StartPoint = p;
|
|
positionSet = true;
|
|
}
|
|
|
|
public void ConnectToLastFigure (SW.Point p, bool stroke)
|
|
{
|
|
if (EndPoint != p) {
|
|
var pathIsOpen = Path.Segments.Count != 0 || geometry.Figures.Count > 1 || positionSet;
|
|
if (pathIsOpen) {
|
|
// LastFigureStart = p;
|
|
Path.Segments.Add (new LineSegment (p, stroke));
|
|
}
|
|
else
|
|
NewFigure (p);
|
|
}
|
|
if (!stroke)
|
|
LastFigureStart = p;
|
|
}
|
|
|
|
public void ResetPath ()
|
|
{
|
|
Path = new PathFigure ();
|
|
geometry = new PathGeometry ();
|
|
geometry.Figures.Add (Path);
|
|
Path.StartPoint = EndPoint = new SW.Point (0, 0);
|
|
positionSet = false;
|
|
}
|
|
|
|
public PathGeometry Geometry {
|
|
get { return geometry; }
|
|
}
|
|
|
|
public SW.Point GetStartPoint ()
|
|
{
|
|
if (Path.Segments.Count == 0)
|
|
return Path.StartPoint;
|
|
var last = Path.Segments[0];
|
|
if (last is LineSegment)
|
|
return ((LineSegment)last).Point;
|
|
if (last is PolyLineSegment) {
|
|
var p = ((PolyLineSegment)last).Points;
|
|
return p[0];
|
|
}
|
|
return Path.StartPoint;
|
|
}
|
|
|
|
public void Dispose ()
|
|
{
|
|
if (!disposed) {
|
|
Context.Close ();
|
|
disposed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|