Example #1
0
        /// <summary>
        /// Loads and processes an image from the filesystem.
        /// </summary>
        /// <param name="ImagePath">The path on the filesystem of the image to load</param>
        private void LoadImage(string ImagePath)
        {
            // Clear the previous image if there was one
            Image preview = this.FindControl <Image>("Preview");

            preview.Source = null;
            preview.InvalidateVisual();

            // Process and draw the new image
            ImageToEtch = new EtchableImage(ImagePath);
            Slider luminanceSlider = this.FindControl <Slider>("LuminanceSlider");

            ImageToEtch.ProcessImage(1.0 - luminanceSlider.Value);
            preview.Source = ImageToEtch.Bitmap;
            preview.InvalidateVisual();

            // Set the image dimension labels
            TextBlock imageWidthLabel  = this.FindControl <TextBlock>("ImageWidthLabel");
            TextBlock imageHeightLabel = this.FindControl <TextBlock>("ImageHeightLabel");

            imageWidthLabel.Text  = $"{ImageToEtch.Width} px";
            imageHeightLabel.Text = $"{ImageToEtch.Height} px";

            // Route it
            RecalculateRoute();
        }
Example #2
0
        /// <summary>
        /// Loads a picture from a file when it's dropped onto the preview image control.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">The drop event arguments containing the contents that were dropped</param>
        private async void Drop(object sender, DragEventArgs e)
        {
            IEnumerable <string> files = e.Data.GetFileNames();

            if (files.Count() > 0)
            {
                string imagePath = files.ElementAt(0);
                try
                {
                    Button recalculateButton = this.FindControl <Button>("RecalculateButton");
                    Button exportButton      = this.FindControl <Button>("ExportButton");
                    recalculateButton.IsEnabled = false;
                    exportButton.IsEnabled      = false;
                    ImageToEtch = null;
                    Route       = null;
                    Title       = $"OpenEtch v{VersionInfo.Version}";

                    LoadImage(imagePath);
                    recalculateButton.IsEnabled = true;
                    exportButton.IsEnabled      = true;
                    OriginalFilename            = System.IO.Path.GetFileName(imagePath);
                    Title = $"OpenEtch v{VersionInfo.Version} - {OriginalFilename}";
                }
                catch (Exception ex)
                {
                    ImageToEtch = null;
                    Route       = null;
                    await MessageBox.Show(this, $"Error loading image: {ex.Message}", "Error loading image");

                    return;
                }
            }
        }
Example #3
0
        /// <summary>
        /// Loads an image from the filesystem.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">Not used</param>
        public async void LoadImageButton_Click(object sender, RoutedEventArgs e)
        {
            Button recalculateButton = this.FindControl <Button>("RecalculateButton");
            Button exportButton      = this.FindControl <Button>("ExportButton");

            recalculateButton.IsEnabled = false;
            exportButton.IsEnabled      = false;
            ImageToEtch = null;
            Route       = null;
            Title       = $"OpenEtch v{VersionInfo.Version}";

            // Give the user a new dialog for choosing the image file
            OpenFileDialog dialog = new OpenFileDialog()
            {
                AllowMultiple = false,
                Title         = "Select Image",
                Filters       =
                {
                    new FileDialogFilter
                    {
                        Name       = "Images",
                        Extensions ={ "bmp",                  "png", "jpg", "jpeg" }
                    },
                    new FileDialogFilter
                    {
                        Name       = "All Files",
                        Extensions ={ "*" }
                    }
                }
            };

            // Get the selection, if they selected something
            string[] selection = await dialog.ShowAsync(this);

            if (selection.Length != 1)
            {
                return;
            }

            // Try to load and process the image
            string imagePath = selection[0];

            try
            {
                LoadImage(imagePath);
                RecalculateRoute();
                recalculateButton.IsEnabled = true;
                exportButton.IsEnabled      = true;
                OriginalFilename            = System.IO.Path.GetFileName(imagePath);
                Title = $"OpenEtch v{VersionInfo.Version} - {OriginalFilename}";
            }
            catch (Exception ex)
            {
                ImageToEtch = null;
                Route       = null;
                await MessageBox.Show(this, $"Error loading image: {ex.Message}", "Error loading image");

                return;
            }
        }
Example #4
0
        /// <summary>
        /// Creates an etching route to etch the outlines of all of the bodies in the image,
        /// thus providing a stencil.
        /// </summary>
        /// <param name="Image">The image to etch</param>
        /// <returns>A route for etching a stencil of the provided image</returns>
        public Route Route(EtchableImage Image)
        {
            Point origin = new Point(0, 0);

            // Handle the pre-etch trace first
            Point topRight     = new Point(Image.Width - 1, 0);
            Point bottomRight  = new Point(Image.Width - 1, Image.Height - 1);
            Point bottomLeft   = new Point(0, Image.Height - 1);
            Path  preEtchTrace = new Path(
                new List <Point>()
            {
                origin,
                topRight,
                bottomRight,
                bottomLeft,
                origin
            });

            // Handle the outline etching
            List <Body> bodies   = FindBodies(Image);
            List <Path> outlines = new List <Path>();

            foreach (Body body in bodies)
            {
                outlines.Add(body.Outline);
            }

            // Done!
            Route route = new Route(Config, preEtchTrace, outlines);

            return(route);
        }
Example #5
0
        /// <summary>
        /// Finds all of the bodies in the provided image (areas of connected black pixels).
        /// </summary>
        /// <param name="Image">The image to find the bodies in</param>
        /// <returns>A collection of bodies in the image</returns>
        public List <Body> FindBodies(EtchableImage Image)
        {
            List <Body> bodies = new List <Body>();

            bool[,] visitedPixels = new bool[Image.Width, Image.Height];

            using (ILockedFramebuffer buffer = Image.Bitmap.Lock())
            {
                IntPtr address = buffer.Address;

                for (int y = 0; y < Image.Height; y++)
                {
                    IntPtr rowOffsetAddress = address + (y * Image.Width * 4);

                    for (int x = 0; x < Image.Width; x++)
                    {
                        // Ignore pixels we've already looked at
                        if (visitedPixels[x, y])
                        {
                            continue;
                        }

                        // Set the flag for this pixel because now we've looked at it
                        visitedPixels[x, y] = true;

                        // Ignore white pixels
                        IntPtr pixelAddress = rowOffsetAddress + (x * 4);

                        // Get the blue channel since that isn't used for path drawing,
                        // so it will always have the correct pixel value of 0 or 255
                        byte pixelValue = Marshal.ReadByte(pixelAddress);
                        if (pixelValue == 0xFF)
                        {
                            continue;
                        }

                        // If we get here, we have a new body!
                        Body body = ProcessBody(buffer, x, y, visitedPixels);
                        bodies.Add(body);
                    }
                }
            }

            return(bodies);
        }
Example #6
0
        /// <summary>
        /// Creates a route for raster-etching the provided image.
        /// </summary>
        /// <param name="Image">The image to etch</param>
        /// <returns>A route for etching the provided image</returns>
        public Route Route(EtchableImage Image)
        {
            Point origin = new Point(0, 0);

            // Handle the pre-etch trace first
            Point topRight     = new Point(Image.Width - 1, 0);
            Point bottomRight  = new Point(Image.Width - 1, Image.Height - 1);
            Point bottomLeft   = new Point(0, Image.Height - 1);
            Path  preEtchTrace = new Path(
                new List <Point>()
            {
                origin,
                topRight,
                bottomRight,
                bottomLeft,
                origin
            });

            // Handle the image etching
            List <Path> etchMoves = new List <Path>();
            Point       lastPoint = origin;
            List <Line> etchLines = GetRasterEtchLines(Image);

            foreach (Line line in etchLines)
            {
                // Check if the start of the line or the end is closest to the last point
                Point  lineStart     = new Point(line.Start, line.YIndex);
                Point  lineEnd       = new Point(line.End, line.YIndex);
                double startDistance = lastPoint.GetDistance(lineStart);
                double endDistance   = lastPoint.GetDistance(lineEnd);

                // Go left-to-right
                if (startDistance <= endDistance)
                {
                    for (int i = 0; i < line.Segments.Count; i++)
                    {
                        EtchSegment segment   = line.Segments[i];
                        Point       etchStart = new Point(segment.Start, line.YIndex);
                        Point       etchEnd   = new Point(segment.End, line.YIndex);

                        // Run the etch
                        Path etch = new Path(new List <Point> {
                            etchStart, etchEnd
                        });
                        etchMoves.Add(etch);

                        // Set the last known point to the end of this etch
                        lastPoint = etchEnd;
                    }
                }

                // Go right-to-left
                else
                {
                    for (int i = line.Segments.Count - 1; i >= 0; i--)
                    {
                        EtchSegment segment   = line.Segments[i];
                        Point       etchStart = new Point(segment.End, line.YIndex);
                        Point       etchEnd   = new Point(segment.Start, line.YIndex);

                        // Run the etch
                        Path etch = new Path(new List <Point> {
                            etchStart, etchEnd
                        });
                        etchMoves.Add(etch);

                        // Set the last known point to the end of this etch
                        lastPoint = etchEnd;
                    }
                }
            }

            // Done!
            Route route = new Route(Config, preEtchTrace, etchMoves);

            return(route);
        }
Example #7
0
        /// <summary>
        /// Breaks the provided image into horizontal raster etch segments.
        /// </summary>
        /// <param name="Image">The image to get the raster etch lines for</param>
        /// <returns>A collection of horizontal lines (each of which can have multiple segments)
        /// necessary to etch the image in raster mode.</returns>
        private List <Line> GetRasterEtchLines(EtchableImage Image)
        {
            List <Line> lines = new List <Line>();

            using (ILockedFramebuffer bitmapBuffer = Image.Bitmap.Lock())
            {
                IntPtr bitmapAddress = bitmapBuffer.Address;

                for (int y = 0; y < Image.Height; y++)
                {
                    Line line            = new Line(y);
                    bool isInEtchSegment = false;
                    int  etchStart       = 0;

                    for (int x = 0; x < Image.Width; x++)
                    {
                        // Get the blue channel - since this is a black and white image, the channel doesn't really matter
                        byte pixelValue = Marshal.ReadByte(bitmapAddress);

                        // This is a white pixel
                        if (pixelValue > 127)
                        {
                            if (isInEtchSegment)
                            {
                                // Create a new etch segment from the recorded start to the previous pixel
                                EtchSegment segment = new EtchSegment(etchStart, x - 1);
                                line.Segments.Add(segment);
                                isInEtchSegment = false;
                            }
                        }

                        // This is a black pixel
                        else
                        {
                            if (!isInEtchSegment)
                            {
                                // Start a new etch segment
                                isInEtchSegment = true;
                                etchStart       = x;
                            }

                            // Check if we're at the last pixel in the row but still in an etch segment
                            else if (x == Image.Width - 1)
                            {
                                EtchSegment segment = new EtchSegment(etchStart, x);
                                line.Segments.Add(segment);
                                isInEtchSegment = false;
                            }
                        }

                        // Move to the next pixel in the bitmap buffer
                        bitmapAddress += 4;
                    }

                    // Ignore lines with no etch segments
                    if (line.Segments.Count > 0)
                    {
                        lines.Add(line);
                    }
                }
            }

            return(lines);
        }