protected override void Dispose(bool disposing) { if (disposing) { if (this.components != null) { this.components.Dispose(); this.components = null; } if (this.selectionTimer != null) { this.selectionTimer.Dispose(); this.selectionTimer = null; } if (this.zoomInSelectedPath != null) { this.zoomInSelectedPath.Dispose(); this.zoomInSelectedPath = null; } } base.Dispose(disposing); }
public MaskedSurface(Surface source, PdnGraphicsPath path) { RectangleF boundsF = path.GetBounds(); Rectangle bounds = Utility.RoundRectangle(boundsF); Rectangle boundsClipped = Rectangle.Intersect(bounds, source.Bounds); Rectangle boundsRead; if (bounds != boundsClipped) { PdnRegion region = new PdnRegion(path); region.Intersect(source.Bounds); SetPathField(PdnGraphicsPath.FromRegion(region)); this.region = region; boundsRead = region.GetBoundsInt(); } else { SetPathField(path.Clone()); this.region = new PdnRegion(this.path); boundsRead = boundsClipped; } if (boundsRead.Width > 0 && boundsRead.Height > 0) { this.surface = new Surface(boundsRead.Size); this.surface.CopySurface(source, boundsRead); } else { this.surface = null; } }
public PdnGraphicsPath Clone() { PdnGraphicsPath path = new PdnGraphicsPath((GraphicsPath)gdiPath.Clone()); path.tooComplex = this.tooComplex; return(path); }
// only works if base is empty public void SetContinuation(PdnGraphicsPath path, CombineMode combineMode, bool takeOwnership) { lock (this.syncRoot) { if (!this.data.basePath.IsEmpty) { throw new InvalidOperationException("base path must be empty to use this overload of SetContinuation"); } OnChanging(); CommitInterimTransform(); ResetCumulativeTransform(); this.data.continuationCombineMode = combineMode; if (takeOwnership) { this.data.continuation.Dispose(); this.data.continuation = path; } else { this.data.continuation.Reset(); this.data.continuation.AddPath(path, false); } OnChanged(); } }
public static PdnGraphicsPath Combine(PdnGraphicsPath subjectPath, CombineMode combineMode, PdnGraphicsPath clipPath) { switch (combineMode) { case CombineMode.Complement: return(Combine(clipPath, CombineMode.Exclude, subjectPath)); case CombineMode.Replace: return(clipPath.Clone()); case CombineMode.Xor: case CombineMode.Intersect: case CombineMode.Union: case CombineMode.Exclude: if (subjectPath.IsEmpty && clipPath.IsEmpty) { return(new PdnGraphicsPath()); // empty path } else if (subjectPath.IsEmpty) { switch (combineMode) { case CombineMode.Xor: case CombineMode.Union: return(clipPath.Clone()); case CombineMode.Intersect: case CombineMode.Exclude: return(new PdnGraphicsPath()); default: throw new InvalidEnumArgumentException(); } } else if (clipPath.IsEmpty) { switch (combineMode) { case CombineMode.Exclude: case CombineMode.Xor: case CombineMode.Union: return(subjectPath.Clone()); case CombineMode.Intersect: return(new PdnGraphicsPath()); default: throw new InvalidEnumArgumentException(); } } else { GraphicsPath resultPath = PdnGraphics.ClipPath(subjectPath, combineMode, clipPath); return(new PdnGraphicsPath(resultPath)); } default: throw new InvalidEnumArgumentException(); } }
public void Dispose(bool disposing) { if (disposing) { if (this.basePath != null) { this.basePath.Dispose(); this.basePath = null; } if (this.continuation != null) { this.continuation.Dispose(); this.continuation = null; } if (this.cumulativeTransform != null) { this.cumulativeTransform.Dispose(); this.cumulativeTransform = null; } if (this.interimTransform != null) { this.interimTransform.Dispose(); this.interimTransform = null; } } }
private void Dispose(bool disposing) { if (disposing) { if (this.surface != null) { this.surface.Dispose(); this.surface = null; } if (this.region != null) { this.region.Dispose(); this.region = null; } if (this.path != null) { this.path.Dispose(); this.path = null; } if (this.shadowPath != null) { this.shadowPath.Dispose(); this.shadowPath = null; } } this.disposed = true; }
public PdnRegion CreateRegionRaw() { using (PdnGraphicsPath path = CreatePath()) //PdnGraphicsPath path = GetPathReadOnly(); { return(new PdnRegion(path)); } }
public bool IsVisible(Point pt) { using (PdnGraphicsPath path = CreatePath()) //PdnGraphicsPath path = GetPathReadOnly(); { return(path.IsVisible(pt)); } }
public void OnDeserialization(object sender) { threadPool = new PixelDotNet.Threading.ThreadPool(); if (this.path != null) { this.shadowPath = this.path.Clone(); } }
public RectangleF GetBoundsF(bool applyInterimTransformation) { using (PdnGraphicsPath path = this.CreatePath(applyInterimTransformation)) //PdnGraphicsPath path = GetPathReadOnly(applyInterimTransformation); { RectangleF bounds2 = path.GetBounds2(); return(bounds2); } }
public Data() { this.basePath = new PdnGraphicsPath(); this.continuation = new PdnGraphicsPath(); this.continuationCombineMode = CombineMode.Xor; this.cumulativeTransform = new Matrix(); this.cumulativeTransform.Reset(); this.interimTransform = new Matrix(); this.interimTransform.Reset(); }
private PdnGraphicsPath GetPath() { if (this.path == null) { // TODO: FromRegion() is a VERY expensive call! PdnGraphicsPath newPath = PdnGraphicsPath.FromRegion(this.region); SetPathField(newPath); } return(this.shadowPath); }
public PdnGraphicsPath CreatePixelatedPath() { using (PdnGraphicsPath path = CreatePath()) //PdnGraphicsPath path = GetPathReadOnly(); { using (PdnRegion region = new PdnRegion(path)) { PdnGraphicsPath pixellatedPath = PdnGraphicsPath.FromRegion(region); return(pixellatedPath); } } }
private void DrawSelectionOutline(Graphics g, PdnGraphicsPath outline) { if (outline == null) { return; } if (outlinePen1 == null) { outlinePen1 = new Pen(Color.FromArgb(160, Color.Black), 1.0f); outlinePen1.Alignment = PenAlignment.Outset; outlinePen1.LineJoin = LineJoin.Bevel; outlinePen1.Width = -1; } if (outlinePen2 == null) { outlinePen2 = new Pen(Color.White, 1.0f); outlinePen2.Alignment = PenAlignment.Outset; outlinePen2.LineJoin = LineJoin.Bevel; outlinePen2.MiterLimit = 2; outlinePen2.Width = -1; outlinePen2.DashStyle = DashStyle.Dash; outlinePen2.DashPattern = new float[] { 4, 4 }; outlinePen2.Color = Color.White; outlinePen2.DashOffset = 4.0f; } PixelOffsetMode oldPOM = g.PixelOffsetMode; g.PixelOffsetMode = PixelOffsetMode.None; SmoothingMode oldSM = g.SmoothingMode; g.SmoothingMode = SmoothingMode.AntiAlias; outline.Draw(g, outlinePen1); float offset = (float)((double)dancingAntsT / OwnerList.ScaleFactor.Ratio); outlinePen2.DashOffset += offset; if (whiteOpacity != 0) { outlinePen2.Color = Color.FromArgb(whiteOpacity, Color.White); outline.Draw(g, outlinePen2); } outlinePen2.DashOffset -= offset; g.SmoothingMode = oldSM; g.PixelOffsetMode = oldPOM; }
public void CommitContinuation() { lock (this.syncRoot) { OnChanging(); this.data.continuation.CloseAllFigures(); PdnGraphicsPath newBasePath = CreatePath(); this.data.basePath.Dispose(); this.data.basePath = newBasePath; this.data.continuation.Reset(); this.data.continuationCombineMode = CombineMode.Xor; OnChanged(); } }
public PdnGraphicsPath CreatePath(bool applyInterimTransform) { lock (this.syncRoot) { PdnGraphicsPath returnPath = PdnGraphicsPath.Combine(this.data.basePath, this.data.continuationCombineMode, this.data.continuation); if (applyInterimTransform) { returnPath.Transform(this.data.interimTransform); } return(returnPath); } }
private void DrawSelectionTinting(Graphics g, PdnGraphicsPath outline) { if (outline == null) { return; } CompositingMode oldCM = g.CompositingMode; g.CompositingMode = CompositingMode.SourceOver; SmoothingMode oldSM = g.SmoothingMode; g.SmoothingMode = SmoothingMode.AntiAlias; PixelOffsetMode oldPOM = g.PixelOffsetMode; g.PixelOffsetMode = PixelOffsetMode.None; Region oldClipRegion = null; RectangleF outlineBounds = outline.GetBounds(); if (outlineBounds.Left < 0 || outlineBounds.Top < 0 || outlineBounds.Right >= this.SourceSize.Width || outlineBounds.Bottom >= this.SourceSize.Height) { oldClipRegion = g.Clip; Region newClipRegion = oldClipRegion.Clone(); newClipRegion.Intersect(new Rectangle(0, 0, this.SourceSize.Width, this.SourceSize.Height)); g.Clip = newClipRegion; newClipRegion.Dispose(); } g.FillPath(InteriorBrush, outline); if (oldClipRegion != null) { g.Clip = oldClipRegion; oldClipRegion.Dispose(); } g.PixelOffsetMode = oldPOM; g.SmoothingMode = oldSM; g.CompositingMode = oldCM; }
/// <summary> /// When we zoom in, we want to "stair-step" the selected path. /// </summary> /// <returns></returns> private PdnGraphicsPath GetZoomInPath() { lock (this.SyncRoot) { if (this.zoomInSelectedPath == null) { if (this.selectedPath == null) { this.zoomInSelectedPath = new PdnGraphicsPath(); } else { this.zoomInSelectedPath = this.selection.CreatePixelatedPath(); } } return(this.zoomInSelectedPath); } }
private void DrawSelection(Graphics gdiG, PdnGraphicsPath outline) { if (outline == null) { return; } float ratio = (float)OwnerList.ScaleFactor.Ratio; gdiG.ScaleTransform(ratio, ratio); if (EnableSelectionTinting) { PdnGraphicsPath outline2; if (invertedTinting) { outline2 = (PdnGraphicsPath)outline.Clone(); outline2.AddRectangle(new Rectangle(-1, -1, this.SourceSize.Width + 1, this.SourceSize.Height + 1)); outline2.CloseAllFigures(); } else { outline2 = outline; } DrawSelectionTinting(gdiG, outline2); if (invertedTinting) { outline2.Dispose(); } } if (EnableSelectionOutline) { DrawSelectionOutline(gdiG, outline); } gdiG.ScaleTransform(1 / ratio, 1 / ratio); }
public override void RenderToGraphics(Graphics g, Point offset) { double start = timer.GetTickCountDouble(); lock (SyncRoot) { PdnGraphicsPath path = GetAppropriateRenderPath(); if (path == null || path.IsEmpty) { this.render = false; // will be reset next time selection changes } else { g.TranslateTransform(-offset.X, -offset.Y); DrawSelection(g, path); } double end = timer.GetTickCountDouble(); this.renderTime += (end - start); } }
/// <summary> /// Constructs a MaskSurface by copying the given region-of-interest from an Image. /// </summary> /// <param name="source">The Surface to copy pixels from.</param> /// <param name="roi">Defines the Region from which to copy pixels from the Image.</param> public MaskedSurface(Surface source, PdnRegion roi) { PdnRegion roiClipped = (PdnRegion)roi.Clone(); roiClipped.Intersect(source.Bounds); Rectangle boundsClipped = roiClipped.GetBoundsInt(); this.surface = new Surface(boundsClipped.Size); this.surface.Clear(ColorBgra.FromUInt32(0x00ffffff)); Rectangle rect = boundsClipped; Point dstOffset = new Point(rect.X - boundsClipped.X, rect.Y - boundsClipped.Y); this.surface.CopySurface(source, dstOffset, rect); this.region = roiClipped; // TODO: FromRegion() is a VERY expensive call for what we are doing! PdnGraphicsPath newPath = PdnGraphicsPath.FromRegion(this.region); SetPathField(newPath); }
public static PdnGraphicsPath FromRegion(PdnRegion region) { Rectangle[] scans = region.GetRegionScansReadOnlyInt(); if (scans.Length == 1) { PdnGraphicsPath path = new PdnGraphicsPath(); path.AddRectangle(scans[0]); path.CloseFigure(); return(path); } else { Rectangle bounds = region.GetBoundsInt(); BitVector2D stencil = new BitVector2D(bounds.Width, bounds.Height); for (int i = 0; i < scans.Length; ++i) { Rectangle rect = scans[i]; rect.X -= bounds.X; rect.Y -= bounds.Y; stencil.SetUnchecked(rect, true); } PdnGraphicsPath path = PathFromStencil(stencil, new Rectangle(0, 0, stencil.Width, stencil.Height)); using (Matrix matrix = new Matrix()) { matrix.Reset(); matrix.Translate(bounds.X, bounds.Y); path.Transform(matrix); } return(path); } }
/// <summary> /// Creates a graphics path from the given stencil buffer. It should be filled with 'true' values /// to indicate the areas that should be outlined. /// </summary> /// <param name="stencil">The stencil buffer to read from. NOTE: The contents of this will be destroyed when this method returns.</param> /// <param name="bounds">The bounding box within the stencil buffer to limit discovery to.</param> /// <returns>A PdnGraphicsPath with traces that outline the various areas from the given stencil buffer.</returns> public unsafe static PdnGraphicsPath PathFromStencil(IBitVector2D stencil, Rectangle bounds) { if (stencil.IsEmpty) { return(new PdnGraphicsPath()); } PdnGraphicsPath ret = new PdnGraphicsPath(); Point start = bounds.Location; Vector <Point> pts = new Vector <Point>(); int count = 0; // find all islands while (true) { bool startFound = false; while (true) { if (stencil[start]) { startFound = true; break; } ++start.X; if (start.X >= bounds.Right) { ++start.Y; start.X = bounds.Left; if (start.Y >= bounds.Bottom) { break; } } } if (!startFound) { break; } pts.Clear(); Point last = new Point(start.X, start.Y + 1); Point curr = new Point(start.X, start.Y); Point next = curr; Point left = Point.Empty; Point right = Point.Empty; // trace island outline while (true) { left.X = ((curr.X - last.X) + (curr.Y - last.Y) + 2) / 2 + curr.X - 1; left.Y = ((curr.Y - last.Y) - (curr.X - last.X) + 2) / 2 + curr.Y - 1; right.X = ((curr.X - last.X) - (curr.Y - last.Y) + 2) / 2 + curr.X - 1; right.Y = ((curr.Y - last.Y) + (curr.X - last.X) + 2) / 2 + curr.Y - 1; if (bounds.Contains(left) && stencil[left]) { // go left next.X += curr.Y - last.Y; next.Y -= curr.X - last.X; } else if (bounds.Contains(right) && stencil[right]) { // go straight next.X += curr.X - last.X; next.Y += curr.Y - last.Y; } else { // turn right next.X -= curr.Y - last.Y; next.Y += curr.X - last.X; } if (Math.Sign(next.X - curr.X) != Math.Sign(curr.X - last.X) || Math.Sign(next.Y - curr.Y) != Math.Sign(curr.Y - last.Y)) { pts.Add(curr); ++count; } last = curr; curr = next; if (next.X == start.X && next.Y == start.Y) { break; } } Point[] points = pts.ToArray(); Scanline[] scans = Utility.GetScans(points); foreach (Scanline scan in scans) { stencil.Invert(scan); } ret.AddLines(points); ret.CloseFigure(); } return(ret); }
private void SelectionTimer_Tick(object sender, System.EventArgs e) { if (this.IsDisposed || this.ownerControl.IsDisposed) { return; } if (this.selectedPath == null || this.selectedPath.IsEmpty) { this.selectionTimer.Enabled = false; return; } if (!this.enableOutlineAnimation) { return; } if (this.timer.GetTickCountDouble() < this.coolOffTimeTickCount) { return; } if (this.ownerControl != null && this.ownerControl.IsMouseCaptured()) { return; } Form form = this.ownerControl.FindForm(); if (form != null && form.WindowState == FormWindowState.Minimized) { return; } int presentTickMod = (int)((Utility.GetTimeMs() / dancingAntsInterval) % 2); if (presentTickMod != lastTickMod) { lastTickMod = presentTickMod; dancingAntsT = unchecked (dancingAntsT + 1); if (this.simplifiedRegionForTimer == null) { using (PdnGraphicsPath invalidPath = (PdnGraphicsPath)selectedPath.Clone()) { invalidPath.CloseAllFigures(); float ratio = 1.0f / (float)OwnerList.ScaleFactor.Ratio; int inflateAmount = (int)Math.Ceiling(ratio); this.simplifiedRegionForTimer = Utility.SimplifyTrace(invalidPath, 50); Utility.InflateRectanglesInPlace(this.simplifiedRegionForTimer, inflateAmount); } } try { foreach (Rectangle rect in this.simplifiedRegionForTimer) { Invalidate(rect); } } catch (ObjectDisposedException) { try { this.selectionTimer.Enabled = false; } catch (Exception) { // Ignore error } } if (this.ownerControl == null || (this.ownerControl != null && !this.ownerControl.IsMouseCaptured())) { whiteOpacity = Math.Min(whiteOpacity + 16, 255); } } // If it takes "too long" to render the dancing ants, then we institute // a cooling-off period during which we will not render the ants. // This will curb the CPU usage by a few percent, which will avoid us // monopolizing the CPU. double maxRenderTime = (double)dancingAntsInterval * maxCpuTime; if (renderTime > maxRenderTime) { double coolOffTime = renderTime / maxRenderTime; this.coolOffTimeTickCount = timer.GetTickCountDouble() + coolOffTime; } this.renderTime = 0.0; }
public unsafe void Draw(Surface dst, Matrix transform, ResamplingAlgorithm sampling) { if (this.disposed) { throw new ObjectDisposedException("MaskedSurface"); } if (this.surface == null || !transform.IsInvertible) { return; } PdnRegion theRegion; Rectangle regionBounds; if (this.path == null) { theRegion = this.region.Clone(); regionBounds = this.region.GetBoundsInt(); theRegion.Transform(transform); } else { using (PdnGraphicsPath mPath = this.shadowPath.Clone()) { regionBounds = Rectangle.Truncate(mPath.GetBounds()); mPath.Transform(transform); theRegion = new PdnRegion(mPath); } } DrawContext dc = new DrawContext(); dc.boundsX = regionBounds.X; dc.boundsY = regionBounds.Y; Matrix inverse = transform.Clone(); inverse.Invert(); dc.inverses = new Matrix[Processor.LogicalCpuCount]; for (int i = 0; i < dc.inverses.Length; ++i) { dc.inverses[i] = inverse.Clone(); } // change in source-[X|Y] w.r.t. destination-[X|Y] PointF[] pts = new PointF[] { new PointF(1, 0), new PointF(0, 1) }; inverse.TransformVectors(pts); inverse.Dispose(); inverse = null; dc.dsxddx = pts[0].X; if (Math.Abs(dc.dsxddx) > fp_MaxValue) { dc.dsxddx = 0.0f; } dc.dsyddx = pts[0].Y; if (Math.Abs(dc.dsyddx) > fp_MaxValue) { dc.dsyddx = 0.0f; } dc.dsxddy = pts[1].X; if (Math.Abs(dc.dsxddy) > fp_MaxValue) { dc.dsxddy = 0.0f; } dc.dsyddy = pts[1].Y; if (Math.Abs(dc.dsyddy) > fp_MaxValue) { dc.dsyddy = 0.0f; } dc.fp_dsxddx = (int)(dc.dsxddx * fp_MultFactor); dc.fp_dsyddx = (int)(dc.dsyddx * fp_MultFactor); dc.fp_dsxddy = (int)(dc.dsxddy * fp_MultFactor); dc.fp_dsyddy = (int)(dc.dsyddy * fp_MultFactor); dc.dst = dst; dc.src = this.surface; Rectangle[] scans = theRegion.GetRegionScansReadOnlyInt(); if (scans.Length == 1) { dc.dstScans = new Rectangle[Processor.LogicalCpuCount]; Utility.SplitRectangle(scans[0], dc.dstScans); } else { dc.dstScans = scans; } WaitCallback wc; switch (sampling) { case ResamplingAlgorithm.NearestNeighbor: wc = new WaitCallback(dc.DrawScansNearestNeighbor); break; case ResamplingAlgorithm.Bilinear: wc = new WaitCallback(dc.DrawScansBilinear); break; default: throw new System.ComponentModel.InvalidEnumArgumentException(); } for (int i = 0; i < Processor.LogicalCpuCount; ++i) { if (i == Processor.LogicalCpuCount - 1) { // Don't queue the last work item into a separate thread wc(BoxedConstants.GetInt32(i)); } else { threadPool.QueueUserWorkItem(wc, BoxedConstants.GetInt32(i)); } } threadPool.Drain(); for (int i = 0; i < Processor.LogicalCpuCount; ++i) { dc.inverses[i].Dispose(); dc.inverses[i] = null; } dc.src = null; theRegion.Dispose(); theRegion = null; }
private void SetPathField(PdnGraphicsPath newPath) { this.path = newPath; this.shadowPath = newPath.Clone(); }
public static PdnGraphicsPath FromRegions(PdnRegion lhs, CombineMode combineMode, PdnRegion rhs) { Rectangle lhsBounds = lhs.GetBoundsInt(); Rectangle rhsBounds = rhs.GetBoundsInt(); int left = Math.Min(lhsBounds.Left, rhsBounds.Left); int top = Math.Min(lhsBounds.Top, rhsBounds.Top); int right = Math.Max(lhsBounds.Right, rhsBounds.Right); int bottom = Math.Max(lhsBounds.Bottom, rhsBounds.Bottom); Rectangle bounds = Rectangle.FromLTRB(left, top, right, bottom); BitVector2D stencil = new BitVector2D(bounds.Width, bounds.Height); Rectangle[] lhsScans = lhs.GetRegionScansReadOnlyInt(); Rectangle[] rhsScans = rhs.GetRegionScansReadOnlyInt(); switch (combineMode) { case CombineMode.Complement: case CombineMode.Intersect: case CombineMode.Replace: throw new ArgumentException("combineMode can't be Complement, Intersect, or Replace"); default: break; } for (int i = 0; i < lhsScans.Length; ++i) { Rectangle rect = lhsScans[i]; rect.X -= bounds.X; rect.Y -= bounds.Y; stencil.SetUnchecked(rect, true); } for (int i = 0; i < rhsScans.Length; ++i) { Rectangle rect = rhsScans[i]; rect.X -= bounds.X; rect.Y -= bounds.Y; switch (combineMode) { case CombineMode.Xor: stencil.InvertUnchecked(rect); break; case CombineMode.Union: stencil.SetUnchecked(rect, true); break; case CombineMode.Exclude: stencil.SetUnchecked(rect, false); break; } } PdnGraphicsPath path = PathFromStencil(stencil, new Rectangle(0, 0, stencil.Width, stencil.Height)); using (Matrix matrix = new Matrix()) { matrix.Reset(); matrix.Translate(bounds.X, bounds.Y); path.Transform(matrix); } return(path); }
public PdnRegion(PdnGraphicsPath pdnPath) : this(pdnPath.GetRegionCache()) { }
private void OnSelectionChanged(object sender, EventArgs e) { this.render = true; PdnGraphicsPath path = this.selection.CreatePath(); //this.selection.GetPathReadOnly(); if (this.selectedPath == null) { Invalidate(); } else { this.selectedPath.Dispose(); // this.selectedPath = null; } bool fullInvalidate = false; this.selectedPath = path; // HACK: Sometimes the selection leaves behind artifacts. So do a full invalidate // every 1 second. if (this.selectedPath.PointCount > 10 && (DateTime.Now - lastFullInvalidate > new TimeSpan(0, 0, 0, 1, 0))) { fullInvalidate = true; } // if we're moving to a simpler selection region ... if (this.selectedPath == null)// || this.selectedPath.PointCount == 0) { // then invalidate everything fullInvalidate = true; } else { // otherwise, be intelligent about it and only redraw the 'new' area PdnRegion xorMe = new PdnRegion(this.selectedPath); selectionRedrawInterior.Xor(xorMe); xorMe.Dispose(); } float ratio = 1.0f / (float)OwnerList.ScaleFactor.Ratio; int ratioInt = (int)Math.Ceiling(ratio); if (this.Visible && (this.EnableSelectionOutline || this.EnableSelectionTinting)) { using (PdnRegion simplified = Utility.SimplifyAndInflateRegion(selectionRedrawInterior, Utility.DefaultSimplificationFactor, 2 * ratioInt)) { Invalidate(simplified); } } if (fullInvalidate) { Rectangle rect = Rectangle.Inflate(Rectangle.Truncate(selectionRedrawOutline.GetBounds2()), 1, 1); Invalidate(rect); lastFullInvalidate = DateTime.Now; } this.selectionRedrawInterior.Dispose(); this.selectionRedrawInterior = null; if (this.zoomInSelectedPath != null) { this.zoomInSelectedPath.Dispose(); this.zoomInSelectedPath = null; } this.simplifiedRegionForTimer = null; // prepare for next call if (this.selectedPath != null && !this.selectedPath.IsEmpty) { this.selectionRedrawOutline = (PdnGraphicsPath)this.selectedPath.Clone(); this.selectionRedrawInterior = new PdnRegion(this.selectedPath); } else { if (invertedTinting) { this.selectionRedrawInterior = new PdnRegion(new Rectangle(0, 0, this.SourceSize.Width, this.SourceSize.Height)); } else { this.selectionRedrawInterior = new PdnRegion(); this.selectionRedrawInterior.MakeEmpty(); } Invalidate(); this.selectionRedrawOutline = new PdnGraphicsPath(); } }