c# WinForm制作图片编辑工具(图像拖动、缩放、旋转、抠图)

闲暇之余,开发一个图片编辑小程序。程序主要特点就是可方便的对多个图像编辑,实现了一些基本的操作。本文主要介绍一下程序的功能、设计思路。

执行程序 下载地址:

https://pan.baidu.com/s/1cszsgjKN9ecWZ9sm1hDAdQ

1 功能介绍

程序主界面

点击打开图片,可选择多个图片文件。图片缩略图左侧显示,双击左侧图片,添加到编辑区。

图片编辑区分为:纸张区域和打印区域。图片只能在打印区编辑。当选中这两个区,可调整各个区的大小。

主要功能点:

1 拖动:选中图片后,可以任意拖动图片。

2 缩放:可对图片左右上下实现缩放。可以锁定显示比例缩放。

3 旋转,可以选择旋转基点再旋转。如果不选择旋转基点,以对角为基点旋转。

4 抠图

5 其他一些操作

当有多个图片相互覆盖时,可以调整图层。

选中一个图片后,可以对图片的位置、大小、旋转角度调整。

选择保存,会将编辑的图片保存为文件。

2 处理思路

图片编辑信息 每个图像都有对应的变量记录该图像的详细,比如位置、尺寸、旋转角度、剪切区域。见下面代码:

public class ImageProperty
 {
 public string Name { get; set; }
 public Image EditImage { get; set; } //原始图片

 public int ActualWidth => EditImage.Width; //实际尺寸
 public int ActualHeight => EditImage.Height;

 public bool ShowImageTip { get; set; } = true;

 public bool LockSizeRate { get; set; } //比例是否锁定
 public Size DrawSize { get; set; } //显示尺寸
 public object Tag { get; set; }
 }

 public class ImageEditInfo
 {
 public ImageProperty ImageProperty { get; set; }

 public Point Location { get; set; } = new Point(0, 0); //相对于打印区的位置
 public Point LocationTopRight => new Point(Location.X + Width, Location.Y);
 public Point LocationBottomRight => new Point(Location.X + Width, Location.Y + Height);
 public Point LocationBottomLeft => new Point(Location.X, Location.Y + Height);

 public int RightX => Location.X + Width;
 public int ButtomY => Location.Y + Height;

 public Size DrawSize
 {
 get { return ImageProperty.DrawSize; }
 set { ImageProperty.DrawSize = value; }
 }

 public Image Image => ImageProperty.EditImage;

 public float RotateAngle { get; set; } = 0; //旋转角度

 public bool IsSelect { get; set; }

 public bool LockSizeRate //显示比例是否锁定
 {
 get
 {
 return ImageProperty.LockSizeRate;
 }
 set
 {
 ImageProperty.LockSizeRate = value;
 }
 }

 public int Width
 {
 get
 {
 return DrawSize.Width;
 }
 set
 {
 ImageProperty.DrawSize = new Size(value, DrawSize.Height);
 }
 }

 public int Height
 {
 get
 {
 return DrawSize.Height;
 }
 set
 {
 ImageProperty.DrawSize = new Size(DrawSize.Width, value);
 }
 }

 public bool ShowImageTip
 {
 get { return ImageProperty.ShowImageTip; }
 set { ImageProperty.ShowImageTip = value; }
 } 

 public Point? RotatioBasePoint { get; set; } //旋转基点

 public Point RotatioBasePointValue => RotatioBasePoint.Value;

 public bool HasRotatioBasePoint => (RotatioBasePoint != null && RotatioBasePoint.HasValue);
}

图片旋转

对正常的图片移动、缩放并不难。只要调整图像的长宽、位置就行,基本就是加法减法计算。如果图片有旋转,计算起来就麻烦。比如判断鼠标是否点击了图片、鼠标缩放等,实现这些操作都麻烦。

比如判断鼠标是否点击了图片,如果一个图片是斜的(旋转后的),如何处理?我的思路是旋转:将图片和鼠标所在的点都反向旋转;此后,判断逻辑就和常规方法一样了。旋转函数如下:

/// <summary>
 /// pointMove相对于removeAt,以一定角度旋转
 /// </summary>
 /// <param name="pointMove"></param>
 /// <param name="removeAt"></param>
 /// <param name="rotateAngle"></param>
 /// <param name="clockwise"></param>
 /// <returns></returns>
 public static Point RotationAt(Point pointMove, Point removeAt, double rotateAngle, bool clockwise)
 {
 if (rotateAngle == 0)
 return pointMove;

 lock (matrix)
 {
 matrix.Reset();
 matrix.Rotate((float)(clockwise ? rotateAngle : -rotateAngle));

 Point pt2 = new Point(pointMove.X - removeAt.X, pointMove.Y - removeAt.Y);
 Point[] pts = new Point[] { new Point(pt2.X, pt2.Y) };
 matrix.TransformPoints(pts);

 Point result = new Point(pts[0].X + removeAt.X, pts[0].Y + removeAt.Y);
 return result;
 }
 }

 internal EN_LinePart MouseMove_HitTest(Point pt)
 {
 //鼠标位置 反向旋转,
 pt = DrawHelper.RotationAt(pt, Location, RotateAngle, false);

 //下面就是 和正常判断逻辑一样
 EN_LinePart result = MouseMove_HitTest_Corner(pt);
 if (result != EN_LinePart.无)
 return result;
}

画图:

对图片相关参数修改后,需要调用refresh,强制重画。调用GDI+。根据图片在列表的顺序调用(也就是根据图层)。调用时,根据设定显示区域,旋转角度等,做变换后再画。

void DrawWithRotation(Graphics g, bool saveToFile)
 {
 //设置质量
 ImageHelper.SetHighQuality(g);

 //置背景色
 if (!saveToFile)
 g.Clear(BackgroundColor);

 ImageEditInfo selectImage = null;
 foreach (ImageEditInfo imageInfo in ImageGroup.ListImageToDraw)
 {
 //画图片
 if (imageInfo.IsSelect)
 {
  Debug.Assert(selectImage == null);
  selectImage = imageInfo;
 }

 g.TranslateTransform(imageInfo.Location.X, imageInfo.Location.Y);
 g.RotateTransform(imageInfo.RotateAngle);

 //是否需要画 抠图
 Image imageToDraw = imageInfo.Image;
 if (imageInfo.CutStat == ImageCutStat.have_cut
  && imageInfo.CutPoints.Count > 2)
 {
  Bitmap bitmap = imageToDraw as Bitmap;
  System.Windows.Point[] points = imageInfo.CutPoints.Select(o => new System.Windows.Point(o.X,o.Y)).ToArray();
  Bitmap cutBitmap = ImageCutout.GetImage(bitmap, points);
  imageToDraw = cutBitmap;
 }

 g.DrawImage(imageToDraw,
  new Rectangle(0, 0, imageInfo.DrawSize.Width, imageInfo.DrawSize.Height),
  new Rectangle(0, 0, imageInfo.Image.Width, imageInfo.Image.Height),
  GraphicsUnit.Pixel);

 //画旋转基点
 if (!saveToFile && imageInfo.HasRotatioBasePoint)
 {
  Point pt = imageInfo.RotatioBasePointValue;
  g.FillEllipse(RotatioBaseBrush, pt.X - RotatioBaseRadius, pt.Y - RotatioBaseRadius, RotatioBaseRadius * 2, RotatioBaseRadius * 2);
 }

 //显示信息
 if (!saveToFile && imageInfo.ShowImageTip)
 {
  ImageProperty ImageProperty = imageInfo.ImageProperty;
  string info = string.Format($"({imageInfo.Location.X},{imageInfo.Location.Y}) ({ImageProperty.ActualWidth}X{ImageProperty.ActualHeight}--{imageInfo.DrawSize.Width}X{imageInfo.DrawSize.Height}) (∠{imageInfo.RotateAngle.ToString("0.00")})");

  SizeF sizeF = g.MeasureString(info, _drawProperty.TxtFont);
  g.FillRectangle(_drawProperty.TxtBackgroundBrush,
  new RectangleF(new Point(), sizeF));

  g.DrawString(info, _drawProperty.TxtFont, _drawProperty.TxtBrush, new Point());
 }

 //画抠图线
 if(!saveToFile
  && imageInfo.CutStat == ImageCutStat.in_cuting
  && imageInfo.CutPoints.Count>1)
 {
  for(int i=1;i< imageInfo.CutPoints.Count;i++ )
  {
  g.DrawLine(SelectBorderPen, imageInfo.ToDestImage(imageInfo.CutPoints[i-1]),
  imageInfo.ToDestImage(imageInfo.CutPoints[i]));
  }

  if(imageInfo.CutPoints.Count > 2)
  {
  g.DrawLine(SelectBorderPen, imageInfo.ToDestImage(imageInfo.CutPoints.First()),
  imageInfo.ToDestImage(imageInfo.CutPoints.Last()));
  }
 }

 g.ResetTransform();
 }

 //画选中状态
 if (!saveToFile && selectImage != null)
 {
 DrawSelectImageWithRotation(g, selectImage);
 }
 }

后记:

一般来讲,图像的处理属于比较难的操作。需要有空间想象能力,相应的几何数学基础。不过,如果掌握好了图像操作,对了解控件原理很有帮助。当遇到难以实现的界面,gdi+就是最后的手段;winform也是微软过时的技术了,使用winform作图效率很难提高;为了响应的事件,不停重画,效率很低。WPF对图像的操作又进了一步,wpf属于“保持模型”,就是你告诉操作系统你要画什么就行了,只需要告诉一次。而对于winform,操作系统不停的告诉你,你需要重画了。这就导致winform画图效率比较低,但是省了内存。

以上就是c# WinForm制作图片编辑工具(图像拖动、缩放、旋转、抠图)的详细内容,更多关于c# WinForm图片编辑的资料请关注我们其它相关文章!

(0)

相关推荐

  • C# Winform按钮中图片实现左图右字的效果实例

    设置流程 百度查阅了几个资料感觉没啥用,经过自己一番试验,总结了简单的方法,具体做法是 1.从工具栏拖出一个按钮 2.在vs外部 使用编辑进入到画图中使用 修改其像素值 3.设置按钮的image属性,插入图片 4.设置图片的属性 5.关于按钮Text属性的设置 6.看一下效果 需要注意的点 最好事先知道你需要的按钮的大小,比如我需要把button放入到size高度为40px的panel中,则你的图片最好像素值35以下,不然会出现图片超过边框的效果:如果要将设计好的按钮放置到容器内,需考虑到容器本

  • 基于C# winform实现图片上传功能的方法

    本文所述实例实现将一张图片上传到指定的文件夹,然后在窗体上的PictrueBox控件中显示出来. 具体功能代码如下: private void btnUpload_Click(object sender, EventArgs e) { //创建一个对话框对象 OpenFileDialog ofd = new OpenFileDialog(); //为对话框设置标题 ofd.Title = "请选择上传的图片"; //设置筛选的图片格式 ofd.Filter = "图片格式|*

  • C#实现winform中RichTextBox在指定光标位置插入图片的方法

    本文实例讲述了C#实现winform中RichTextBox在指定光标位置插入图片的方法.分享给大家供大家参考,具体如下: //获取RichTextBox控件中鼠标焦点的索引位置 int startPosition = this.richTextBox1.SelectionStart; //从鼠标焦点处开始选中几个字符 this.richTextBox1.SelectionLength = 2; //清空剪切板,防止里面之前有内容 Clipboard.Clear(); //给剪切板设置图片对象

  • C# WinForm 登录界面的图片验证码(区分大小写+不区分大小写)

    一.功能界面 图1 验证码(区分大小写) 图2 验证码(不区分大小写) 二.创建一个产生验证码的类Class1 (1)生成随机验证码字符串,用的是Random随机函数 (2)创建验证码图片,将该字符串画在PictureBox控件中 Class1.cs: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using S

  • C# WinForm控件对透明图片重叠时出现图片不透明的简单解决方法

    本文实例讲述了C# WinForm控件对透明图片重叠时出现图片不透明的简单解决方法.分享给大家供大家参考,具体如下: 在Winform中如果将一个透明图片放在窗体上能正常显示透明,但是如果将该图片放在另一个控件上会导致不能显示透明效果. 解决这种情况,可以采取在控件上使用GDI+绘画出透明图片. 这里我们就以一个pictureBox2控件上面重叠一张png透明图片为例: 我们只需要给pictureBox2控件添加Paint事件,然后对png透明图片进行绘制即可,代码如下: private voi

  • C# WinForm实现图片浏览器

    C#WinForm程序设计之图片浏览器,这次我们一起做一个图片查看器,这个图片查看器的原始图如下: 我们首先来介绍一下这个原始图的构成: 左边上面是一个 TextBox 和 一个 Button,分别用来显示当前路径以及返回上一个路径.左边下面是一个浏览文件的文件路径树状图(TreeView),用来显示当前路径下的文件和文件夹.右边是一个pictureBox,用来展示选中的图片. 接下来我们一步一步实现这个图片查看器! 首先大家应该看到了左边的TreeView上面已经有显示当前PC的所有路径信息,

  • c# winform 解决PictureBox 无法打印全部图片的问题

    作者:沐汐 Vicky 出处:http://www.cnblogs.com/EasyInvoice 一.   问题描述 在页面使用PictureBox 加载资料图片后,点击"打印",只能打印图片首页,较大图片则无法全部打印. 二.   原因分析 PictureBox中打印图片时没有设置继续打印相关属性,因此每次只能打印第1页. 三.解决方法 PictureBox控件增加打印全部页面属性,如果为True,表示打印全部页面:如果为False,保留原有逻辑不变. 在打印全部页面时,将控件的图

  • c# WinForm制作图片编辑工具(图像拖动、缩放、旋转、抠图)

    闲暇之余,开发一个图片编辑小程序.程序主要特点就是可方便的对多个图像编辑,实现了一些基本的操作.本文主要介绍一下程序的功能.设计思路. 执行程序 下载地址: https://pan.baidu.com/s/1cszsgjKN9ecWZ9sm1hDAdQ 1 功能介绍 程序主界面 点击打开图片,可选择多个图片文件.图片缩略图左侧显示,双击左侧图片,添加到编辑区. 图片编辑区分为:纸张区域和打印区域.图片只能在打印区编辑.当选中这两个区,可调整各个区的大小. 主要功能点: 1 拖动:选中图片后,可以

  • iOS手势识别的详细使用方法(拖动,缩放,旋转,点击,手势依赖,自定义手势)

    手势识别在iOS上非常重要,手势操作移动设备的重要特征,极大的增加了移动设备使用便捷性. 1.UIGestureRecognizer介绍 手势识别在iOS上非常重要,手势操作移动设备的重要特征,极大的增加了移动设备使用便捷性. iOS系统在3.2以后,为方便开发这使用一些常用的手势,提供了UIGestureRecognizer类.手势识别UIGestureRecognizer类是个抽象类,下面的子类是具体的手势,开发这可以直接使用这些手势识别. UITapGestureRecognizer UI

  • c#基于winform制作音乐播放器

    前言:项目是c#的winform 写的,使用的播放器是基于AxWindowsMediaPlayer. AxWindowsMediaPlayer的方法 1 首先新建一个页面 如图所示: 图片左侧是列表 使用listview 右侧是背景图片.图片框框的地方是后面可以实现的,+和-按钮分别代表添加文件和删除文件 还有就是控制播放的顺序.下面的分别是修改歌词的字体 和展示/隐藏 2 新建一个透明的歌词页面[窗体] 3 新建一个半透明的页面[窗体] 4 业务代码 using System; using S

  • C# WinForm制作异形窗体与控件的方法

    本文实例讲述了C# WinForm制作异形窗体与控件的方法.分享给大家供大家参考,具体如下: 制作异形窗体或控件的思路一般都是想办法生成一个region,然后设置给指定的窗口或控件.生成region的方法有很多,最常用的就是从一幅图片生成,把该图片中的透明色部分"抠"掉,剩下的部分作为一个region.设置窗口或控件的region可以用SetWindowRgn API,不过.NET framework封装了这个操作,在C#中只要对窗口或控件的Region属性赋值就可以了.下面我就把我在

  • PHP实现对png图像进行缩放的方法(支持透明背景)

    本文实例讲述了PHP实现对png图像进行缩放的方法.分享给大家供大家参考.具体实现方法如下: function smart_resize_image( $file, $width = 0, $height = 0, $proportional = false, $output = 'file', $delete_original = true, $use_linux_commands = false ) { if ( $height <= 0 && $width <= 0 )

  • Python图像处理之图像的缩放、旋转与翻转实现方法示例

    本文实例讲述了Python图像处理之图像的缩放.旋转与翻转实现方法.分享给大家供大家参考,具体如下: 图像的几何变换,如缩放.旋转和翻转等,在图像处理中扮演着重要的角色,python中的Image类分别提供了这些操作的接口函数,下面进行逐一介绍. 1.图像的缩放 图像的缩放使用resize()成员函数,直接在入参中指定缩放后的尺寸即可,示例如下: #-*- coding: UTF-8 -*- from PIL import Image #读取图像 im = Image.open("lenna.j

  • vue-drag-chart 拖动/缩放的图表组件的实例代码

    vue-drag-chart 一个可以拖动 / 缩放的图表组件 使用 npm i vue-drag-chart --save import VueDragChart from "../src/vue-drag-chart/index.vue"; components: { //注册插件 VueDragChart }, <VueDragChart :list="item" v-for="(item,index) in chartlist" :

  • Python使用OpenCV对图像进行缩放功能

    OpenCV:图片缩放和图像金字塔 对图像进行缩放的最简单方法当然是调用resize函数啦! resize函数可以将源图像精确地转化为指定尺寸的目标图像. 要缩小图像,一般推荐使用CV_INETR_AREA来插值:若要放大图像,推荐使用CV_INTER_LINEAR. 现在说说调用方式 第一种,规定好你要图片的尺寸,就是你填入你要的图片的长和高. #include<opencv2\opencv.hpp> #include<opencv2\highgui\highgui.hpp> u

  • Python使用PyAudio制作录音工具的实现代码

    目录 应用平台 音频录制部分 音频播放部分 GUI窗口所需属性值代码部分 pynput监听键盘 总结 最近有在使用屏幕录制软件录制桌面,在用的过程中突发奇想,使用python能不能做屏幕录制工具,也锻炼下自己的动手能力.接下准备写使用python如何做屏幕录制工具的系列文章: 录制屏幕制作视频 录制音频 合成视频,音频 基于pyqt5制作可视化窗口 大概上述四个部分,希望自己能够尽快完善,上一篇文章利用opencv制作了屏幕录制部分,接下继续更新系列,使用python录制音频. 应用平台 win

  • C# WinForm制作登录界面的实现步骤

    在[解决方案资源管理器]中找到Form1.cs,单击,快捷键F2重命名为“Login.cs”(命名很重要,不然之后项目多了根据不知道哪个项目的内容是什么) 对窗体[Text]属性.[size]属性和[FormBoardStyle]属性进行修改 添加一个新的窗体 Ctrl+Shift+A,在弹出框中选择[Windows窗体],命名为main.cs 取消登录界面最大化最小化关闭按钮在父窗体菜单栏上显示最大化:MaximizeBox,最小化:MinimizeBox如果设置一个为False 的时候会显示

随机推荐