C# - 强制矩形的大小/比例固定

时间:2023-02-10 00:22:10

Similar to Photoshop, where you can type in a ratio or in my case a certain dimention such as 800x600, I want to be able to force a Rectangle to be a fixed ratio/size when dragging the mouse..

类似于Photoshop,您可以输入一个比例,或者在我的情况下输入一定的尺寸,例如800x600,我希望能够在拖动鼠标时强制Rectangle为固定比率/大小。

At the moment I have this:

目前我有这个:

C# - 强制矩形的大小/比例固定

Which will crop an image using the Rectangle created from clicking and dragging on the PictureBox. The bounding box selects the area without any constraints. I want to be able to force the rectangle to be a certain ratio (prefereably from a set resolution) similar to the way Photoshop's cropping tool works.

这将使用通过在PictureBox上单击并拖动创建的矩形来裁剪图像。边界框选择没有任何约束的区域。我希望能够将矩形强制为一定的比例(优选来自设定的分辨率),类似于Photoshop的裁剪工具的工作方式。

My source if anyone needs more detail:

我的来源,如果有人需要更多细节:

Form1.cs

Form1.cs的

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CropResize
{
public partial class Form1 : Form
{
    private static string path;

    public Form1(string filePath)
    {
        InitializeComponent();
        path = filePath;
    }

    private Image _originalImage;
    private Image _newImage;

    private bool _selecting;
    private Rectangle _selection;


    private void Form1_Load(object sender, System.EventArgs e)
    {
        pictureBox1.Image = Image.FromFile(path);

        if (pictureBox1.Image.Height > Screen.PrimaryScreen.Bounds.Height - 50 || pictureBox1.Image.Width > Screen.PrimaryScreen.Bounds.Width - 50)
        {
            if (pictureBox1.Image.Height > Screen.PrimaryScreen.Bounds.Height - 50)
            {
                Height = Screen.PrimaryScreen.Bounds.Height - 50;
                panel1.Height = Size.Height - statusStrip1.Height - buttonSave.Height - 60;
            }
            if (pictureBox1.Image.Width > Screen.PrimaryScreen.Bounds.Width - 50)
            {
                Size = new Size(Screen.PrimaryScreen.Bounds.Width - 50, Screen.PrimaryScreen.Bounds.Height - 50);
                panel1.Width = Size.Width - statusStrip1.Height - buttonSave.Height - 60;
            }

            pictureBox1.Image = pictureBox1.Image.Fit2PictureBox(pictureBox1);

            panel1.Size = new Size(pictureBox1.Image.Width, pictureBox1.Image.Height);

        }

        Size = new Size(panel1.Size.Width + 50, panel1.Size.Height + buttonSave.Height + statusStrip1.Height + 80);

        // Create a copy of the original image for later use
        _originalImage = pictureBox1.Image.Clone() as Image;
        _newImage = pictureBox1.Image.Clone() as Image;
    }

    private void buttonOrig_Click(object sender, System.EventArgs e)
    {
        pictureBox1.Image = _originalImage.Clone() as Image;
    }

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
        // Starting point of the selection:
        if (e.Button == MouseButtons.Left)
        {
            pictureBox1.Image.Dispose();
            pictureBox1.Image = _originalImage.Clone() as Image;
            _selecting = true;
            _selection = new Rectangle(new Point(e.X, e.Y), new Size());
        }
    }

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        // Update the actual size of the selection:
        if (_selecting)
        {

            _selection.Width = (e.X - _selection.X);
            _selection.Height = (e.Y - _selection.Y);

            //int nGCD = GetGreatestCommonDivisor(1920, 1080);
            //_selection.Width = _selection.Width / nGCD;
            //_selection.Height = _selection.Height / nGCD;


            int widthRatio = 16;
            int heightRatio = 9;

            if (_selection.Height * widthRatio <= _selection.Width)
            {
                _selection.Width = _selection.Height * widthRatio;
            }
            else if (_selection.Width * heightRatio <= _selection.Width)
            {
                _selection.Height = _selection.Width * heightRatio;
            }

            // Redraw the picturebox:
            pictureBox1.Refresh();
        }
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        if (_selecting && _selection.Height != 0)
        {

            // Draw a rectangle displaying the current selection
            e.Graphics.DrawRectangle(Pens.WhiteSmoke, _selection);

            //e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(50, Color.Gray)), 0, pictureBox1.Height - pictureBox1.Image.Height, pictureBox1.Image.Width, pictureBox1.Image.Height);
            e.Graphics.SetClip(_selection, CombineMode.Exclude);
            e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(100, Color.Black)), 0, 0, pictureBox1.Width, pictureBox1.Height);

            int nGCD = GetGreatestCommonDivisor(_selection.Width, _selection.Height);
            string str = string.Format("{0}:{1}", _selection.Width / nGCD, _selection.Height / nGCD);

            toolStripStatusLabel1.Text = "Image Size: " + _selection.Width + "x" + _selection.Height + "px.    Aspect Ratio: " + str;
        }
    }

    public static int GetGreatestCommonDivisor(int height, int width)
    {
        return width == 0 ? height : GetGreatestCommonDivisor(width, height % width);
    }


    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left &&
            _selecting &&
            _selection.Size != new Size())
        {
            // Create cropped image:
            _newImage = pictureBox1.Image.Crop(_selection);

            _selecting = false;

            try
            {
                // Set new image to the picturebox:
                //pictureBox1.Image = _newImage.Fit2PictureBox(pictureBox1);

                pictureBox1.Image = _newImage;

                //toolStripStatusLabel1.Text = "Image Cropped.";
            }
            catch (Exception)
            { }
        }
        else
        {
            _selecting = false;
        }
    }

    private void buttonResize_Click(object sender, EventArgs e)
    {
        pictureBox1.Image = ResizeImage(pictureBox1.Image, new Size(800, 600));

        int nGCD = GetGreatestCommonDivisor(pictureBox1.Image.Width, pictureBox1.Image.Height);
        string str = string.Format("{0}:{1}", pictureBox1.Image.Width / nGCD, pictureBox1.Image.Height / nGCD);

        toolStripStatusLabel1.Text = "Image Resized to " + pictureBox1.Image.Width + "x" + pictureBox1.Image.Height + "px.    Aspect Ratio: " + str;
    }


    public static Image ResizeImage(Image image, Size size, bool preserveAspectRatio = true)
    {
        int newWidth;
        int newHeight;
        if (preserveAspectRatio)
        {
            int originalWidth = image.Width;
            int originalHeight = image.Height;
            float percentWidth = size.Width / originalWidth;
            float percentHeight = size.Height / originalHeight;
            float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
            newWidth = (int)(originalWidth * percent);
            newHeight = (int)(originalHeight * percent);
        }
        else
        {
            newWidth = size.Width;
            newHeight = size.Height;
        }
        Image newImage = new Bitmap(newWidth, newHeight);
        using (Graphics graphicsHandle = Graphics.FromImage(newImage))
        {
            graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight);
        }
        return newImage;
    }


    private void buttonSave_Click(object sender, EventArgs e)
    {
        string filename = path.Substring(path.LastIndexOf("\\") + 1);
        string newPath = path.Substring(0, path.LastIndexOf(".") - 1) + "NEW.png";

        toolStripStatusLabel1.Text = "Saving " + filename + " to " + newPath;

        pictureBox1.Image.Save(newPath, ImageFormat.Png);

        toolStripStatusLabel1.Text = filename + " saved to " + newPath;

    }
}
}

Program.cs

Program.cs中

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CropResize
{
static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1(args[0]));
    }


    public static Image SetImageWithinResolution(this Image image, PictureBox pictureBox)
    {
        //Bitmap bitmap = null;
        if (image.Height > Screen.PrimaryScreen.Bounds.Height)
        {
            //ActiveForm.Size = new Size(100, 100);
            Form.ActiveForm.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
            image = image.Fit2PictureBox(pictureBox);
            //Bitmap bitmap = new Bitmap(image, );

        }
        if (image.Width > Screen.PrimaryScreen.Bounds.Width)
        {
            //ActiveForm.Size = new Size(100, 100);
            Form.ActiveForm.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
            image = image.Fit2PictureBox(pictureBox);
            //Bitmap bitmap = new Bitmap();

        }
        return image;
    }


    public static Image Crop(this Image image, Rectangle selection)
    {
        Bitmap bmp = image as Bitmap;

        try
        {
            // Check if it is a bitmap:
            if (bmp == null)
                throw new ArgumentException("No valid bitmap");

            // Crop the image:
            Bitmap cropBmp = bmp.Clone(selection, bmp.PixelFormat);

            // Release the resources:
            image.Dispose();

            return cropBmp;
        }
        catch (Exception)
        {
            return bmp;
        }
    }


    public static Image Fit2PictureBox(this Image image, PictureBox picBox)
    {
        Bitmap bmp = null;
        Graphics g;

        // Scale:
        double scaleY = (double)image.Width / picBox.Width;
        double scaleX = (double)image.Height / picBox.Height;
        double scale = scaleY < scaleX ? scaleX : scaleY;

        // Create new bitmap:
        bmp = new Bitmap(
            (int)((double)image.Width / scale),
            (int)((double)image.Height / scale));

        // Set resolution of the new image:
        bmp.SetResolution(
            image.HorizontalResolution,
            image.VerticalResolution);

        // Create graphics:
        g = Graphics.FromImage(bmp);

        // Set interpolation mode:
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;

        // Draw the new image:
        g.DrawImage(
            image,
            new Rectangle(            // Destination
                0, 0,
                bmp.Width, bmp.Height),
            new Rectangle(            // Source
                0, 0,
                image.Width, image.Height),
            GraphicsUnit.Pixel);

        // Release the resources of the graphics:
        g.Dispose();

        // Release the resources of the origin image:
        image.Dispose();

        return bmp;
    }
}

1 个解决方案

#1


1  

Maybe this example will help; is shows how to restrict a drawn rectangle to a given ratio:

也许这个例子会有所帮助;显示如何将绘制的矩形限制为给定的比率:

float ratio = 0.33f;

Rectangle setRect()
{
    int x = Math.Min(mDown.X, currPt.X);
    int y = Math.Min(mDown.Y, currPt.Y);

    int w = Math.Max(mDown.X, currPt.X) - x;
    int h = Math.Max(mDown.Y, currPt.Y) - y;

    if (ratio > 1)  h = (int)(1f * w / ratio);
    else            w = (int)(1f * h * ratio);

    return new Rectangle(x, y, w, h);
}

It uses two Points, one set in the MouseDown and one updated in the MouseMove.

它使用两个Point,一个在MouseDown中设置,另一个在MouseMove中更新。

It is up to you to integrate it with you programm; instead of painting all those pixels during MouseMove I would simply draw a rubberband rectangle on the surface of the control using the Paint event..

您可以将它与您的程序集成;而不是在MouseMove期间绘制所有这些像素我只是使用Paint事件在控件的表面上绘制一个橡皮带矩形。

If you are scaling things you may want to switch to using all floats.

如果要缩放内容,可能需要切换到使用所有浮动内容。

#1


1  

Maybe this example will help; is shows how to restrict a drawn rectangle to a given ratio:

也许这个例子会有所帮助;显示如何将绘制的矩形限制为给定的比率:

float ratio = 0.33f;

Rectangle setRect()
{
    int x = Math.Min(mDown.X, currPt.X);
    int y = Math.Min(mDown.Y, currPt.Y);

    int w = Math.Max(mDown.X, currPt.X) - x;
    int h = Math.Max(mDown.Y, currPt.Y) - y;

    if (ratio > 1)  h = (int)(1f * w / ratio);
    else            w = (int)(1f * h * ratio);

    return new Rectangle(x, y, w, h);
}

It uses two Points, one set in the MouseDown and one updated in the MouseMove.

它使用两个Point,一个在MouseDown中设置,另一个在MouseMove中更新。

It is up to you to integrate it with you programm; instead of painting all those pixels during MouseMove I would simply draw a rubberband rectangle on the surface of the control using the Paint event..

您可以将它与您的程序集成;而不是在MouseMove期间绘制所有这些像素我只是使用Paint事件在控件的表面上绘制一个橡皮带矩形。

If you are scaling things you may want to switch to using all floats.

如果要缩放内容,可能需要切换到使用所有浮动内容。