| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 | using PaintDotNet.Measurement.Enum;using System.Collections.Generic;using System.ComponentModel;using System.Drawing;using System.Windows.Forms;namespace PaintDotNet.Measurement.Tools{    public abstract class FloodToolBase : Tool    {        private bool contiguous;        private bool clipToSelection = true;        protected bool ClipToSelection        {            get            {                return clipToSelection;            }            set            {                clipToSelection = value;            }        }        public FloodToolBase(IDocumentWorkspace documentWorkspace, ImageResource toolBarImage, string name,            string helpText, char hotKey, bool skipIfActiveOnHotKey, ToolBarConfigItems toolBarConfigItems)            : base(documentWorkspace, toolBarImage, name, helpText, hotKey, skipIfActiveOnHotKey,              ToolBarConfigItems.FloodMode | ToolBarConfigItems.Tolerance | toolBarConfigItems)        {        }        private static bool CheckColor(ColorBgra a, ColorBgra b, int tolerance)        {            int sum = 0;            int diff;            diff = a.R - b.R;            sum += (1 + diff * diff) * a.A / 256;            diff = a.G - b.G;            sum += (1 + diff * diff) * a.A / 256;            diff = a.B - b.B;            sum += (1 + diff * diff) * a.A / 256;            diff = a.A - b.A;            sum += diff * diff;            return (sum <= tolerance * tolerance * 4);        }        public unsafe static void FillStencilByColor(Surface surface, IBitVector2D stencil, ColorBgra cmp, int tolerance,            out Rectangle boundingBox, PdnRegion limitRegion, bool limitToSelection)        {            int top = int.MaxValue;            int bottom = int.MinValue;            int left = int.MaxValue;            int right = int.MinValue;            Rectangle[] scans;            stencil.Clear(false);            if (limitToSelection)            {                using (PdnRegion excluded = new PdnRegion(new Rectangle(0, 0, stencil.Width, stencil.Height)))                {                    excluded.Xor(limitRegion);                    scans = excluded.GetRegionScansReadOnlyInt();                }            }            else            {                scans = new Rectangle[0];            }            foreach (Rectangle rect in scans)            {                stencil.Set(rect, true);            }            for (int y = 0; y < surface.Height; ++y)            {                bool foundPixelInRow = false;                ColorBgra* ptr = surface.GetRowAddressUnchecked(y);                for (int x = 0; x < surface.Width; ++x)                {                    if (CheckColor(cmp, *ptr, tolerance))                    {                        stencil.SetUnchecked(x, y, true);                        if (x < left)                        {                            left = x;                        }                        if (x > right)                        {                            right = x;                        }                        foundPixelInRow = true;                    }                    ++ptr;                }                if (foundPixelInRow)                {                    if (y < top)                    {                        top = y;                    }                    if (y >= bottom)                    {                        bottom = y;                    }                }            }            foreach (Rectangle rect in scans)            {                stencil.Set(rect, false);            }            boundingBox = Rectangle.FromLTRB(left, top, right + 1, bottom + 1);        }        public unsafe static void FillStencilFromPoint(Surface surface, IBitVector2D stencil, Point start,            int tolerance, out Rectangle boundingBox, PdnRegion limitRegion, bool limitToSelection)        {            ColorBgra cmp = surface[start];            int top = int.MaxValue;            int bottom = int.MinValue;            int left = int.MaxValue;            int right = int.MinValue;            Rectangle[] scans;            stencil.Clear(false);            if (limitToSelection)            {                using (PdnRegion excluded = new PdnRegion(new Rectangle(0, 0, stencil.Width, stencil.Height)))                {                    excluded.Xor(limitRegion);                    scans = excluded.GetRegionScansReadOnlyInt();                }            }            else            {                scans = new Rectangle[0];            }            foreach (Rectangle rect in scans)            {                stencil.Set(rect, true);            }            Queue<Point> queue = new Queue<Point>(16);            queue.Enqueue(start);            while (queue.Count > 0)            {                Point pt = queue.Dequeue();                ColorBgra* rowPtr = surface.GetRowAddressUnchecked(pt.Y);                int localLeft = pt.X - 1;                int localRight = pt.X;                while (localLeft >= 0 &&                       !stencil.GetUnchecked(localLeft, pt.Y) &&                       CheckColor(cmp, rowPtr[localLeft], tolerance))                {                    stencil.SetUnchecked(localLeft, pt.Y, true);                    --localLeft;                }                while (localRight < surface.Width &&                       !stencil.GetUnchecked(localRight, pt.Y) &&                       CheckColor(cmp, rowPtr[localRight], tolerance))                {                    stencil.SetUnchecked(localRight, pt.Y, true);                    ++localRight;                }                ++localLeft;                --localRight;                if (pt.Y > 0)                {                    int sleft = localLeft;                    int sright = localLeft;                    ColorBgra* rowPtrUp = surface.GetRowAddressUnchecked(pt.Y - 1);                    for (int sx = localLeft; sx <= localRight; ++sx)                    {                        if (!stencil.GetUnchecked(sx, pt.Y - 1) &&                            CheckColor(cmp, rowPtrUp[sx], tolerance))                        {                            ++sright;                        }                        else                        {                            if (sright - sleft > 0)                            {                                queue.Enqueue(new Point(sleft, pt.Y - 1));                            }                            ++sright;                            sleft = sright;                        }                    }                    if (sright - sleft > 0)                    {                        queue.Enqueue(new Point(sleft, pt.Y - 1));                    }                }                if (pt.Y < surface.Height - 1)                {                    int sleft = localLeft;                    int sright = localLeft;                    ColorBgra* rowPtrDown = surface.GetRowAddressUnchecked(pt.Y + 1);                    for (int sx = localLeft; sx <= localRight; ++sx)                    {                        if (!stencil.GetUnchecked(sx, pt.Y + 1) &&                            CheckColor(cmp, rowPtrDown[sx], tolerance))                        {                            ++sright;                        }                        else                        {                            if (sright - sleft > 0)                            {                                queue.Enqueue(new Point(sleft, pt.Y + 1));                            }                            ++sright;                            sleft = sright;                        }                    }                    if (sright - sleft > 0)                    {                        queue.Enqueue(new Point(sleft, pt.Y + 1));                    }                }                if (localLeft < left)                {                    left = localLeft;                }                if (localRight > right)                {                    right = localRight;                }                if (pt.Y < top)                {                    top = pt.Y;                }                if (pt.Y > bottom)                {                    bottom = pt.Y;                }            }            foreach (Rectangle rect in scans)            {                stencil.Set(rect, false);            }            boundingBox = Rectangle.FromLTRB(left, top, right + 1, bottom + 1);        }        protected abstract void OnFillRegionComputed(Point[][] polygonSet);        protected override void OnMouseDown(MouseEventArgs e)        {            Point pos = new Point(e.X, e.Y);            switch (AppEnvironment.FloodMode())            {                case FloodMode.Local:                    this.contiguous = true;                    break;                case FloodMode.Global:                    this.contiguous = false;                    break;                default:                    throw new InvalidEnumArgumentException();            }            if ((ModifierKeys & Keys.Shift) != 0)            {                this.contiguous = !this.contiguous;            }            if (Document.Bounds.Contains(pos))            {                base.OnMouseDown(e);                PdnRegion currentRegion = Selection.CreateRegion();                // See if the mouse click is valid                if (!currentRegion.IsVisible(pos) && clipToSelection)                {                    currentRegion.Dispose();                    currentRegion = null;                    return;                }                // Set the current surface, color picked and color to draw                Surface surface = ((BitmapLayer)ActiveLayer).Surface;                IBitVector2D stencilBuffer = new BitVector2DSurfaceAdapter(this.ScratchSurface);                Rectangle boundingBox;                int tolerance = (int)(AppEnvironment.Tolerance() * AppEnvironment.Tolerance() * 256);                if (contiguous)                {                    // FloodMode.Local                    FillStencilFromPoint(surface, stencilBuffer, pos, tolerance, out boundingBox, currentRegion, clipToSelection);                }                else                {                    // FloodMode.Global                    FillStencilByColor(surface, stencilBuffer, surface[pos], tolerance, out boundingBox, currentRegion, clipToSelection);                }                Point[][] polygonSet = PdnGraphicsPath.PolygonSetFromStencil(stencilBuffer, boundingBox, 0, 0);                OnFillRegionComputed(polygonSet);            }            base.OnMouseDown(e);        }    }}
 |