Exemplo n.º 1
0
        protected override void OnMouseDown(Gtk.DrawingArea canvas, Gtk.ButtonPressEventArgs args, PointD point)
        {
            Document doc = PintaCore.Workspace.ActiveDocument;

            Point pos = new Point((int)point.X, (int)point.Y);

            // Don't do anything if we're outside the canvas
            if (pos.X < 0 || pos.X >= doc.ImageSize.Width)
            {
                return;
            }
            if (pos.Y < 0 || pos.Y >= doc.ImageSize.Height)
            {
                return;
            }

            base.OnMouseDown(canvas, args, point);

            Gdk.Region currentRegion = Gdk.Region.Rectangle(doc.GetSelectedBounds(true));

            // See if the mouse click is valid
            if (!currentRegion.PointIn(pos.X, pos.Y) && limitToSelection)
            {
                currentRegion.Dispose();
                currentRegion = null;
                return;
            }

            ImageSurface surface         = doc.CurrentUserLayer.Surface;
            ImageSurface stencil_surface = new ImageSurface(Format.Argb32, (int)surface.Width, (int)surface.Height);

            IBitVector2D stencilBuffer = new BitVector2DSurfaceAdapter(stencil_surface);
            int          tol           = (int)(Tolerance * Tolerance * 256);
            Rectangle    boundingBox;

            if (IsContinguousMode)
            {
                FillStencilFromPoint(surface, stencilBuffer, pos, tol, out boundingBox, currentRegion, limitToSelection);
            }
            else
            {
                FillStencilByColor(surface, stencilBuffer, surface.GetColorBgra(pos.X, pos.Y), tol, out boundingBox, currentRegion, LimitToSelection);
            }

            stencil = stencilBuffer;
            OnFillRegionComputed(stencilBuffer);

            // If a derived tool is only going to use the stencil,
            // don't waste time building the polygon set
            if (CalculatePolygonSet)
            {
                Point[][] polygonSet = stencilBuffer.CreatePolygonSet(boundingBox, 0, 0);
                OnFillRegionComputed(polygonSet);
            }
        }
Exemplo n.º 2
0
        protected unsafe override void OnFillRegionComputed(IBitVector2D stencil)
        {
            Document     doc  = PintaCore.Workspace.ActiveDocument;
            ImageSurface surf = doc.ToolLayer.Surface;

            using (var g = new Context(surf)) {
                g.Operator = Operator.Source;
                g.SetSource(doc.CurrentUserLayer.Surface);
                g.Paint();
            }

            SimpleHistoryItem hist = new SimpleHistoryItem(Icon, Name);

            hist.TakeSnapshotOfLayer(doc.CurrentUserLayer);

            ColorBgra  color  = fill_color.ToColorBgra().ToPremultipliedAlpha();
            ColorBgra *dstPtr = (ColorBgra *)surf.DataPtr;
            int        width  = surf.Width;

            surf.Flush();

            // Color in any pixel that the stencil says we need to fill
            Parallel.For(0, stencil.Height, y =>
            {
                int stencil_width = stencil.Width;
                for (int x = 0; x < stencil_width; ++x)
                {
                    if (stencil.GetUnchecked(x, y))
                    {
                        surf.SetColorBgraUnchecked(dstPtr, width, color, x, y);
                    }
                }
            });

            surf.MarkDirty();

            // Transfer the temp layer to the real one,
            // respecting any selection area
            using (var g = doc.CreateClippedContext()) {
                g.Operator = Operator.Source;
                g.SetSource(surf);
                g.Paint();
            }

            doc.ToolLayer.Clear();

            doc.History.PushNewItem(hist);
            doc.Workspace.Invalidate();
        }
Exemplo n.º 3
0
		protected unsafe override void OnFillRegionComputed (IBitVector2D stencil)
		{
			Document doc = PintaCore.Workspace.ActiveDocument;
			ImageSurface surf = doc.ToolLayer.Surface;

			using (var g = new Context (surf)) {
				g.Operator = Operator.Source;
				g.SetSource (doc.CurrentUserLayer.Surface);
				g.Paint ();
			}

			SimpleHistoryItem hist = new SimpleHistoryItem (Icon, Name);
			hist.TakeSnapshotOfLayer (doc.CurrentUserLayer);

			ColorBgra color = fill_color.ToColorBgra ().ToPremultipliedAlpha ();
			ColorBgra* dstPtr = (ColorBgra*)surf.DataPtr;
			int width = surf.Width;

			surf.Flush ();

			// Color in any pixel that the stencil says we need to fill
			Parallel.For (0, stencil.Height, y =>
			{
				int stencil_width = stencil.Width;
				for (int x = 0; x < stencil_width; ++x) {
					if (stencil.GetUnchecked (x, y)) {
						surf.SetColorBgraUnchecked (dstPtr, width, color, x, y);
					}
				}
			});

			surf.MarkDirty ();

			// Transfer the temp layer to the real one,
			// respecting any selection area
			using (var g = doc.CreateClippedContext ()) {
				g.Operator = Operator.Source;
				g.SetSource (surf);
				g.Paint ();
			}

			doc.ToolLayer.Clear ();

			doc.History.PushNewItem (hist); 
			doc.Workspace.Invalidate ();
		}
Exemplo n.º 4
0
        public static unsafe void FillStencilByColor(ImageSurface surface, IBitVector2D stencil, ColorBgra cmp, int tolerance, out Rectangle boundingBox)
        {
            int top = int.MaxValue;
            int bottom = int.MinValue;
            int left = int.MaxValue;
            int right = int.MinValue;

            stencil.Clear (false);

            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;
                    }
                }
            }

            boundingBox = new Rectangle (left, top, right + 1, bottom + 1);
        }
Exemplo n.º 5
0
        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);
        }
Exemplo n.º 6
0
        public static unsafe 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);
        }
Exemplo n.º 7
0
        /// <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);
        }
Exemplo n.º 8
0
 protected virtual void OnFillRegionComputed(IBitVector2D stencil)
 {
 }
Exemplo n.º 9
0
        /// <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;
        }
Exemplo n.º 10
0
        public unsafe static Point[][] PolygonSetFromStencil(IBitVector2D stencil, Rectangle bounds, int translateX, int translateY)
        {
            List<Point[]> polygons = new List<Point[]>();

            if (!stencil.IsEmpty)
            {
                Point start = bounds.Location;
                List<Point> pts = new List<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);
                    }

                    Utility.TranslatePointsInPlace(points, translateX, translateY);
                    polygons.Add(points);
                }
            }

            Point[][] returnVal = polygons.ToArray();
            return returnVal;
        }
Exemplo n.º 11
0
		public unsafe static void FillStencilByColor (ImageSurface surface, IBitVector2D stencil, ColorBgra cmp, int tolerance, 
		                                              out Rectangle boundingBox, Gdk.Region limitRegion, bool limitToSelection)
		{
			int top = int.MaxValue;
			int bottom = int.MinValue;
			int left = int.MaxValue;
			int right = int.MinValue;
			Gdk.Rectangle[] scans;

			stencil.Clear (false);

			if (limitToSelection) {
				using (Gdk.Region excluded = Gdk.Region.Rectangle (new Gdk.Rectangle (0, 0, stencil.Width, stencil.Height))) {
					excluded.Xor (limitRegion);
					scans = excluded.GetRectangles ();
				}
			} else {
				scans = new Gdk.Rectangle[0];
			}

			foreach (Gdk.Rectangle rect in scans)
				stencil.Set (rect, true);

            Parallel.For(0, surface.Height, y =>
            {
                bool foundPixelInRow = false;
                ColorBgra* ptr = surface.GetRowAddressUnchecked(y);

                int surfaceWidth = surface.Width;
                for (int x = 0; x < surfaceWidth; ++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 (Gdk.Rectangle rect in scans)
				stencil.Set (rect, false);

			boundingBox = new Rectangle (left, top, right - left + 1, bottom - top + 1);
		}
Exemplo n.º 12
0
		public unsafe static void FillStencilFromPoint (ImageSurface surface, IBitVector2D stencil, Point start, int tolerance, 
		                                                out Rectangle boundingBox, Gdk.Region limitRegion, bool limitToSelection)
		{
			ColorBgra cmp = surface.GetColorBgraUnchecked (start.X, start.Y);
			int top = int.MaxValue;
			int bottom = int.MinValue;
			int left = int.MaxValue;
			int right = int.MinValue;
			Gdk.Rectangle[] scans;

			stencil.Clear (false);

			if (limitToSelection) {
				using (Gdk.Region excluded = Gdk.Region.Rectangle (new Gdk.Rectangle (0, 0, stencil.Width, stencil.Height))) {
					excluded.Xor (limitRegion);
					scans = excluded.GetRectangles ();
				}
			} else {
				scans = new Gdk.Rectangle[0];
			}

			foreach (Gdk.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;
				}

                int surfaceWidth = surface.Width;
				while (localRight < surfaceWidth &&
				       !stencil.GetUnchecked (localRight, pt.Y) &&
				       CheckColor (cmp, rowPtr[localRight], tolerance)) {
					stencil.SetUnchecked (localRight, pt.Y, true);
					++localRight;
				}

				++localLeft;
				--localRight;

                Action<int> checkRow = (row) =>
                {
					int sleft = localLeft;
					int sright = localLeft;
					ColorBgra* otherRowPtr = surface.GetRowAddressUnchecked (row);

					for (int sx = localLeft; sx <= localRight; ++sx) {
						if (!stencil.GetUnchecked (sx, row) &&
						    CheckColor (cmp, otherRowPtr[sx], tolerance)) {
							++sright;
						} else {
							if (sright - sleft > 0) {
								queue.Enqueue (new Point (sleft, row));
							}

							++sright;
							sleft = sright;
						}
					}

					if (sright - sleft > 0) {
						queue.Enqueue (new Point (sleft, row));
					}
                };

				if (pt.Y > 0) {
                    checkRow (pt.Y - 1);
				}

				if (pt.Y < surface.Height - 1) {
                    checkRow (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 (Gdk.Rectangle rect in scans)
				stencil.Set (rect, false);
			
			boundingBox = new Rectangle (left, top, right - left + 1, bottom - top + 1);
		}
Exemplo n.º 13
0
        protected override void OnMouseDown(Gtk.DrawingArea canvas, Gtk.ButtonPressEventArgs args, PointD point)
        {
            Document doc = PintaCore.Workspace.ActiveDocument;

            Point pos = new Point ((int)point.X, (int)point.Y);

            // Don't do anything if we're outside the canvas
            if (pos.X < 0 || pos.X >= doc.ImageSize.Width)
                return;
            if (pos.Y < 0 || pos.Y >= doc.ImageSize.Height)
                return;

            base.OnMouseDown (canvas, args, point);

            Gdk.Region currentRegion = Gdk.Region.Rectangle (doc.GetSelectedBounds (true));

            // See if the mouse click is valid
            if (!currentRegion.PointIn (pos.X, pos.Y) && limitToSelection) {
                currentRegion.Dispose ();
                currentRegion = null;
                return;
            }

            ImageSurface surface = doc.CurrentUserLayer.Surface;
            ImageSurface stencil_surface = new ImageSurface (Format.Argb32, (int)surface.Width, (int)surface.Height);

            IBitVector2D stencilBuffer = new BitVector2DSurfaceAdapter (stencil_surface);
            int tol = (int)(Tolerance * Tolerance * 256);
            Rectangle boundingBox;

            if (IsContinguousMode)
                FillStencilFromPoint (surface, stencilBuffer, pos, tol, out boundingBox, currentRegion, limitToSelection);
            else
                FillStencilByColor (surface, stencilBuffer, surface.GetColorBgra (pos.X, pos.Y), tol, out boundingBox, currentRegion, LimitToSelection);

            stencil = stencilBuffer;
            OnFillRegionComputed (stencilBuffer);

            // If a derived tool is only going to use the stencil,
            // don't waste time building the polygon set
            if (CalculatePolygonSet) {
                Point[][] polygonSet = stencilBuffer.CreatePolygonSet (boundingBox, 0, 0);
                OnFillRegionComputed (polygonSet);
            }
        }
Exemplo n.º 14
0
        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);
        }
Exemplo n.º 15
0
        public unsafe static void FillStencilFromPoint(ImageSurface surface, IBitVector2D stencil, Point start, int tolerance,
                                                       out Rectangle boundingBox, Gdk.Region limitRegion, bool limitToSelection)
        {
            ColorBgra cmp    = surface.GetColorBgra(start.X, start.Y);
            int       top    = int.MaxValue;
            int       bottom = int.MinValue;
            int       left   = int.MaxValue;
            int       right  = int.MinValue;

            Gdk.Rectangle[] scans;

            stencil.Clear(false);

            if (limitToSelection)
            {
                using (Gdk.Region excluded = Gdk.Region.Rectangle(new Gdk.Rectangle(0, 0, stencil.Width, stencil.Height))) {
                    excluded.Xor(limitRegion);
                    scans = excluded.GetRectangles();
                }
            }
            else
            {
                scans = new Gdk.Rectangle[0];
            }

            foreach (Gdk.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;
                }

                int surfaceWidth = surface.Width;
                while (localRight < surfaceWidth &&
                       !stencil.GetUnchecked(localRight, pt.Y) &&
                       CheckColor(cmp, rowPtr[localRight], tolerance))
                {
                    stencil.SetUnchecked(localRight, pt.Y, true);
                    ++localRight;
                }

                ++localLeft;
                --localRight;

                Action <int> checkRow = (row) =>
                {
                    int        sleft       = localLeft;
                    int        sright      = localLeft;
                    ColorBgra *otherRowPtr = surface.GetRowAddressUnchecked(row);

                    for (int sx = localLeft; sx <= localRight; ++sx)
                    {
                        if (!stencil.GetUnchecked(sx, row) &&
                            CheckColor(cmp, otherRowPtr[sx], tolerance))
                        {
                            ++sright;
                        }
                        else
                        {
                            if (sright - sleft > 0)
                            {
                                queue.Enqueue(new Point(sleft, row));
                            }

                            ++sright;
                            sleft = sright;
                        }
                    }

                    if (sright - sleft > 0)
                    {
                        queue.Enqueue(new Point(sleft, row));
                    }
                };

                if (pt.Y > 0)
                {
                    checkRow(pt.Y - 1);
                }

                if (pt.Y < surface.Height - 1)
                {
                    checkRow(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 (Gdk.Rectangle rect in scans)
            {
                stencil.Set(rect, false);
            }

            boundingBox = new Rectangle(left, top, right - left + 1, bottom - top + 1);
        }
Exemplo n.º 16
0
        public unsafe static void FillStencilByColor(ImageSurface surface, IBitVector2D stencil, ColorBgra cmp, int tolerance,
                                                     out Rectangle boundingBox, Gdk.Region limitRegion, bool limitToSelection)
        {
            int top    = int.MaxValue;
            int bottom = int.MinValue;
            int left   = int.MaxValue;
            int right  = int.MinValue;

            Gdk.Rectangle[] scans;

            stencil.Clear(false);

            if (limitToSelection)
            {
                using (Gdk.Region excluded = Gdk.Region.Rectangle(new Gdk.Rectangle(0, 0, stencil.Width, stencil.Height))) {
                    excluded.Xor(limitRegion);
                    scans = excluded.GetRectangles();
                }
            }
            else
            {
                scans = new Gdk.Rectangle[0];
            }

            foreach (Gdk.Rectangle rect in scans)
            {
                stencil.Set(rect, true);
            }

            Parallel.For(0, surface.Height, y =>
            {
                bool foundPixelInRow = false;
                ColorBgra *ptr       = surface.GetRowAddressUnchecked(y);

                int surfaceWidth = surface.Width;
                for (int x = 0; x < surfaceWidth; ++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 (Gdk.Rectangle rect in scans)
            {
                stencil.Set(rect, false);
            }

            boundingBox = new Rectangle(left, top, right - left + 1, bottom - top + 1);
        }
Exemplo n.º 17
0
 protected virtual void OnFillRegionComputed(IBitVector2D stencil)
 {
 }
Exemplo n.º 18
0
        public unsafe static Point[][] PolygonSetFromStencil(IBitVector2D stencil, Rectangle bounds, int translateX, int translateY)
        {
            List <Point[]> polygons = new List <Point[]>();

            if (!stencil.IsEmpty)
            {
                Point        start = bounds.Location;
                List <Point> pts   = new List <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);
                    }

                    Utility.TranslatePointsInPlace(points, translateX, translateY);
                    polygons.Add(points);
                }
            }

            Point[][] returnVal = polygons.ToArray();
            return(returnVal);
        }
Exemplo n.º 19
0
        public static unsafe void FillStencilFromPoint(ImageSurface surface, IBitVector2D stencil, Point start, int tolerance, out Rectangle boundingBox)
        {
            ColorBgra cmp = surface.GetColorBgra (start.X, start.Y);
            int top = int.MaxValue;
            int bottom = int.MinValue;
            int left = int.MaxValue;
            int right = int.MinValue;

            stencil.Clear (false);

            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;
                }
            }

            boundingBox = new Rectangle (left, top, right + 1, bottom + 1);
        }
Exemplo n.º 20
0
        protected override void OnMouseDown(Gtk.DrawingArea canvas, Gtk.ButtonPressEventArgs args, PointD point)
        {
            Point pos = new Point ((int)point.X, (int)point.Y);

            // Don't do anything if we're outside the canvas
            if (pos.X < 0 || pos.X >= PintaCore.Workspace.ImageSize.Width)
                return;
            if (pos.Y < 0 || pos.Y >= PintaCore.Workspace.ImageSize.Height)
                return;

            base.OnMouseDown (canvas, args, point);

            Gdk.Region currentRegion = Gdk.Region.Rectangle(PintaCore.Layers.SelectionPath.GetBounds());

            // See if the mouse click is valid
            if (!currentRegion.PointIn (pos.X, pos.Y) && limitToSelection) {
                currentRegion.Dispose ();
                currentRegion = null;
                return;
            }

            ImageSurface surface = PintaCore.Layers.CurrentLayer.Surface;
            ImageSurface stencil_surface = new ImageSurface (Format.Argb32, (int)surface.Width, (int)surface.Height);

            IBitVector2D stencilBuffer = new BitVector2DSurfaceAdapter (stencil_surface);
            int tol = (int)(Tolerance * Tolerance * 256);
            Rectangle boundingBox;

            surface.Flush ();

            if (IsContinguousMode)
                FillStencilFromPoint (surface, stencilBuffer, pos, tol, out boundingBox, currentRegion, limitToSelection);
            else
                FillStencilByColor (surface, stencilBuffer, surface.GetColorBgra (pos.X, pos.Y), tol, out boundingBox, currentRegion, LimitToSelection);

            surface.MarkDirty ();
            stencil = stencilBuffer;

            Point[][] polygonSet = stencilBuffer.CreatePolygonSet (boundingBox, 0, 0);
            OnFillRegionComputed (polygonSet);
        }
Exemplo n.º 21
0
        protected override void OnMouseDown(Gtk.DrawingArea canvas, Gtk.ButtonPressEventArgs args, PointD point)
        {
            Point pos = new Point ((int)point.X, (int)point.Y);

            // Don't do anything if we're outside the canvas
            if (pos.X < 0 || pos.X >= PintaCore.Workspace.ImageSize.X)
                return;
            if (pos.Y < 0 || pos.Y >= PintaCore.Workspace.ImageSize.Y)
                return;

            base.OnMouseDown (canvas, args, point);

            ImageSurface surface = PintaCore.Layers.CurrentLayer.Surface;
            ImageSurface stencil_surface = new ImageSurface (Format.Argb32, (int)surface.Width, (int)surface.Height);

            IBitVector2D stencilBuffer = new BitVector2DSurfaceAdapter (stencil_surface);
            int tol = (int)(Tolerance * Tolerance * 256);
            Rectangle boundingBox;

            if (IsContinguousMode)
                FillStencilFromPoint (surface, stencilBuffer, pos, tol, out boundingBox);
            else
                FillStencilByColor (surface, stencilBuffer, surface.GetColorBgra (pos.X, pos.Y), tol, out boundingBox);

            stencil = stencilBuffer;
            OnFillRegionComputed (null);
        }
Exemplo n.º 22
0
        public static unsafe 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);
        }
Exemplo n.º 23
0
        /// <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)
        {
            throw new StillNotPortedException();
            //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 = PixelUtils.GetScans(points);

            //    foreach (Scanline scan in scans)
            //    {
            //        stencil.Invert(scan);
            //    }

            //    ret.AddLines(points);
            //    ret.CloseFigure();
            //}

            //return ret;
        }