using PaintDotNet.Measurement.HistoryMementos; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace PaintDotNet.Measurement.Tools { public class PencilTool : Tool { private bool mouseDown = false; private ColorBgra pencilColor; private MouseButtons mouseButton; private BitmapLayer bitmapLayer; private RenderArgs renderArgs; private List tracePoints; private List savedRects; private PdnRegion clipRegion; private Point lastPoint; private Point difference; private Cursor pencilToolCursor; private BinaryPixelOp blendOp = new UserBlendOps.NormalBlendOp(); private BinaryPixelOp copyOp = new BinaryPixelOps.AssignFromRhs(); protected override void OnActivate() { base.OnActivate(); this.pencilToolCursor = new Cursor(PdnResources.GetResourceStream("Cursors.PencilToolCursor.cur")); this.Cursor = this.pencilToolCursor; this.savedRects = new List(); if (ActiveLayer != null) { bitmapLayer = (BitmapLayer)ActiveLayer; renderArgs = new RenderArgs(bitmapLayer.Surface); tracePoints = new List(); } else { bitmapLayer = null; if (renderArgs != null) { renderArgs.Dispose(); renderArgs = null; } } } protected override void OnDeactivate() { base.OnDeactivate(); if (this.pencilToolCursor != null) { this.pencilToolCursor.Dispose(); this.pencilToolCursor = null; } if (mouseDown) { Point lastTracePoint = (Point)tracePoints[tracePoints.Count - 1]; OnMouseUp(new MouseEventArgs(mouseButton, 0, lastTracePoint.X, lastTracePoint.Y, 0)); } this.savedRects = null; this.tracePoints = null; this.bitmapLayer = null; if (this.renderArgs != null) { this.renderArgs.Dispose(); this.renderArgs = null; } this.mouseDown = false; if (clipRegion != null) { clipRegion.Dispose(); clipRegion = null; } } // Draws a point, but first intersects it with the selection private void DrawPoint(RenderArgs ra, Point p, ColorBgra color) { if (ra.Surface.Bounds.Contains(p)) { if (ra.Graphics.IsVisible(p)) { BinaryPixelOp op = AppEnvironment.AlphaBlending() ? blendOp : copyOp; ra.Surface[p.X, p.Y] = op.Apply(ra.Surface[p.X, p.Y], color); } } } private void DrawLines(RenderArgs ra, List points, int startIndex, int length, ColorBgra color) { // Draw a point in the line if (points.Count == 0) { return; } else if (points.Count == 1) { Point p = (Point)points[0]; if (ra.Surface.Bounds.Contains(p)) { DrawPoint(ra, p, color); } } else { for (int i = startIndex + 1; i < startIndex + length; ++i) { Point[] linePoints = Utility.GetLinePoints(points[i - 1], points[i]); int startPoint = 0; if (i != 1) { startPoint = 1; } for (int pi = startPoint; pi < linePoints.Length; ++pi) { Point p = linePoints[pi]; DrawPoint(ra, p, color); } } } } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (mouseDown) { return; } if (((e.Button & MouseButtons.Left) == MouseButtons.Left) || ((e.Button & MouseButtons.Right) == MouseButtons.Right)) { mouseDown = true; mouseButton = e.Button; tracePoints = new List(); bitmapLayer = (BitmapLayer)ActiveLayer; renderArgs = new RenderArgs(bitmapLayer.Surface); if (clipRegion != null) { clipRegion.Dispose(); clipRegion = null; } clipRegion = Selection.CreateRegion(); renderArgs.Graphics.SetClip(clipRegion.GetRegionReadOnly(), CombineMode.Replace); OnMouseMove(e); } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (mouseDown && ((e.Button & mouseButton) != MouseButtons.None)) { Point mouseXY = new Point(e.X, e.Y); if (lastPoint == Point.Empty) { lastPoint = mouseXY; } difference = new Point(mouseXY.X - lastPoint.X, mouseXY.Y - lastPoint.Y); if (tracePoints.Count > 0) { Point lastMouseXY = (Point)tracePoints[tracePoints.Count - 1]; if (lastMouseXY == mouseXY) { return; } } if ((mouseButton & MouseButtons.Left) == MouseButtons.Left) { this.pencilColor = AppEnvironment.PrimaryColor(); } else // if ((mouseButton & MouseButtons.Right) == MouseButtons.Right) { // right mouse button = swap primary/secondary this.pencilColor = AppEnvironment.SecondaryColor(); } if (!(tracePoints.Count > 0 && mouseXY == (Point)tracePoints[tracePoints.Count - 1])) { tracePoints.Add(mouseXY); } if (ActiveLayer is BitmapLayer) { Rectangle saveRect; if (tracePoints.Count == 1) { saveRect = Utility.PointsToRectangle(mouseXY, mouseXY); } else { // >1 points saveRect = Utility.PointsToRectangle((Point)tracePoints[tracePoints.Count - 1], (Point)tracePoints[tracePoints.Count - 2]); } saveRect.Inflate(2, 2); saveRect.Intersect(ActiveLayer.Bounds); // drawing outside of the canvas is a no-op, so don't do anything in that case! // also make sure it's within the clipping bounds if (saveRect.Width > 0 && saveRect.Height > 0 && renderArgs.Graphics.IsVisible(saveRect)) { SaveRegion(null, saveRect); this.savedRects.Add(saveRect); int startIndex; int length; if (tracePoints.Count == 1) { startIndex = 0; length = 1; } else { startIndex = tracePoints.Count - 2; length = 2; } DrawLines(this.renderArgs, tracePoints, startIndex, length, pencilColor); bitmapLayer.Invalidate(saveRect); Update(); } } else { // will have to do something here if we add other layer types besides BitmapLayer } lastPoint = mouseXY; } } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (mouseDown) { OnMouseMove(e); mouseDown = false; if (savedRects.Count > 0) { Rectangle[] savedScans = this.savedRects.ToArray(); PdnRegion saveMeRegion = Utility.RectanglesToRegion(savedScans); HistoryMemento ha = new BitmapHistoryMemento(Name, Image, DocumentWorkspace, ActiveLayerIndex, saveMeRegion, ScratchSurface); //HistoryStack.PushNewMemento(ha); saveMeRegion.Dispose(); this.savedRects.Clear(); ClearSavedMemory(); } tracePoints = null; } } public PencilTool(IDocumentWorkspace documentWorkspace) : base(documentWorkspace, PdnResources.GetImageResource("Icons.PencilToolIcon.png"), PdnResources.GetString("PencilTool.Name"), PdnResources.GetString("PencilTool.HelpText"), 'p', true, ToolBarConfigItems.AlphaBlending) { // initialize any state information you need mouseDown = false; } } }