| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 | using PaintDotNet.SystemLayer;using System;using System.Drawing;using System.Drawing.Drawing2D;using System.Windows.Forms;namespace PaintDotNet.Measurement{    public class SelectionRenderer        : SurfaceBoxGraphicsRenderer    {        private const int dancingAntsInterval = 60;        private const double maxCpuTime = 0.2; // max 20% CPU time        private Color tintColor = Color.FromArgb(32, 32, 32, 255);        private static Pen outlinePen1 = null;        private static Pen outlinePen2 = null;        private UserControl2 ownerControl;        private System.Windows.Forms.Timer selectionTimer;        private bool enableOutlineAnimation = true;        private System.ComponentModel.IContainer components = null;        private bool invertedTinting = false;        private bool render = true; // when false, we do not Render()        private PdnGraphicsPath selectedPath;        private Selection selection;        private PdnGraphicsPath zoomInSelectedPath;        private int dancingAntsT = 0;        private int whiteOpacity = 255;        private int lastTickMod = 0;        private Rectangle[] simplifiedRegionForTimer = null;        private double coolOffTimeTickCount = 0.0;        /// <summary>        /// This variable is used to accumulate an invalidation region. It is initialized        /// upon responding to the SelectedPathChanging event that is raised by the        /// DocumentEnvironment. Then, when the SelectedPathChanged event is raised, the        /// full region that needs to be redrawn is accounted for.        /// </summary>        private PdnRegion selectionRedrawInterior = PdnRegion.CreateEmpty();        private PdnGraphicsPath selectionRedrawOutline = new PdnGraphicsPath();        private DateTime lastFullInvalidate = DateTime.Now;        protected override void OnVisibleChanged()        {            this.selectionTimer.Enabled = this.Visible;            if (this.selection != null)            {                Rectangle rect = this.selection.GetBounds();                Invalidate(rect);            }        }        public override void OnDestinationSizeChanged()        {            lock (SyncRoot)            {                this.simplifiedRegionForTimer = null;            }            base.OnDestinationSizeChanged();        }        public override void OnSourceSizeChanged()        {            lock (SyncRoot)            {                this.simplifiedRegionForTimer = null;            }            base.OnSourceSizeChanged();        }        public bool EnableOutlineAnimation        {            get            {                return this.enableOutlineAnimation;            }            set            {                if (this.enableOutlineAnimation != value)                {                    this.enableOutlineAnimation = value;                    Invalidate();                }            }        }        public bool InvertedTinting        {            get            {                return this.invertedTinting;            }            set            {                if (this.invertedTinting != value)                {                    this.invertedTinting = value;                    Invalidate();                }            }        }        public Color TintColor        {            get            {                return this.tintColor;            }            set            {                if (value != this.tintColor)                {                    this.tintColor = value;                    if (this.interiorBrush != null)                    {                        this.interiorBrush.Dispose();                        this.interiorBrush = null;                    }                    Invalidate();                }            }        }        private void OnSelectionChanging(object sender, EventArgs e)        {            this.render = false;            if (!this.selectionTimer.Enabled)            {                this.selectionTimer.Enabled = true;            }        }        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();            }        }        /// <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 PdnGraphicsPath GetAppropriateRenderPath()        {            if (OwnerList.ScaleFactor.Ratio >= 1.01)            {                return GetZoomInPath();            }            else            {                return this.selectedPath;            }        }        private Timing timer = new Timing();        private double renderTime = 0.0;        public override bool ShouldRender()        {            return (this.render && (this.EnableSelectionOutline || this.EnableSelectionTinting));        }        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);                    //System.Console.WriteLine(path.GetLastPoint().X);                    DrawSelection(g, path);                }                double end = timer.GetTickCountDouble();                this.renderTime += (end - start);            }        }        public SelectionRenderer(SurfaceBoxRendererList ownerList, Selection selection)            : this(ownerList, selection, null)        {        }        public SelectionRenderer(SurfaceBoxRendererList ownerList, Selection selection, UserControl2 ownerControl)            : base(ownerList)        {            this.ownerControl = ownerControl;            this.selection = selection;            this.selection.Changing += new EventHandler(OnSelectionChanging);            this.selection.Changed += new EventHandler(OnSelectionChanged);            this.components = new System.ComponentModel.Container();            this.selectionTimer = new System.Windows.Forms.Timer(this.components);            this.selectionTimer.Enabled = true;            this.selectionTimer.Interval = dancingAntsInterval / 2;            this.selectionTimer.Tick += new System.EventHandler(this.SelectionTimer_Tick);        }        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);        }        private Brush interiorBrush;        private Brush InteriorBrush        {            get            {                if (interiorBrush == null)                {                    interiorBrush = new SolidBrush(tintColor);                }                return interiorBrush;            }        }        private bool enableSelectionOutline = true;        public bool EnableSelectionOutline        {            get            {                return enableSelectionOutline;            }            set            {                if (this.enableSelectionOutline != value)                {                    enableSelectionOutline = value;                    Invalidate();                }            }        }        private bool enableSelectionTinting = true;        public bool EnableSelectionTinting        {            get            {                return enableSelectionTinting;            }            set            {                if (enableSelectionTinting != value)                {                    enableSelectionTinting = value;                    Invalidate();                }            }        }        /// <summary>        /// This is a silly function name.        /// </summary>        public void ResetOutlineWhiteOpacity()        {            if (this.whiteOpacity > 0)            {                Invalidate();            }            this.whiteOpacity = 0;        }        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;        }        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;        }        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);        }        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;        }    }}
 |