c# WinForm制作圖片編輯工具(圖像拖動、縮放、旋轉(zhuǎn)、摳圖)
閑暇之余,開發(fā)一個圖片編輯小程序。程序主要特點就是可方便的對多個圖像編輯,實現(xiàn)了一些基本的操作。本文主要介紹一下程序的功能、設(shè)計思路。
執(zhí)行程序 下載地址:
https://pan.baidu.com/s/1cszsgjKN9ecWZ9sm1hDAdQ
1 功能介紹
程序主界面

點擊打開圖片,可選擇多個圖片文件。圖片縮略圖左側(cè)顯示,雙擊左側(cè)圖片,添加到編輯區(qū)。
圖片編輯區(qū)分為:紙張區(qū)域和打印區(qū)域。圖片只能在打印區(qū)編輯。當(dāng)選中這兩個區(qū),可調(diào)整各個區(qū)的大小。
主要功能點:
1 拖動:選中圖片后,可以任意拖動圖片。

2 縮放:可對圖片左右上下實現(xiàn)縮放??梢枣i定顯示比例縮放。

3 旋轉(zhuǎn),可以選擇旋轉(zhuǎn)基點再旋轉(zhuǎn)。如果不選擇旋轉(zhuǎn)基點,以對角為基點旋轉(zhuǎn)。


4 摳圖

5 其他一些操作
當(dāng)有多個圖片相互覆蓋時,可以調(diào)整圖層。
選中一個圖片后,可以對圖片的位置、大小、旋轉(zhuǎn)角度調(diào)整。
選擇保存,會將編輯的圖片保存為文件。
2 處理思路
圖片編輯信息 每個圖像都有對應(yīng)的變量記錄該圖像的詳細,比如位置、尺寸、旋轉(zhuǎn)角度、剪切區(qū)域。見下面代碼:
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); //相對于打印區(qū)的位置
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; //旋轉(zhuǎn)角度
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; } //旋轉(zhuǎn)基點
public Point RotatioBasePointValue => RotatioBasePoint.Value;
public bool HasRotatioBasePoint => (RotatioBasePoint != null && RotatioBasePoint.HasValue);
}
圖片旋轉(zhuǎn)
對正常的圖片移動、縮放并不難。只要調(diào)整圖像的長寬、位置就行,基本就是加法減法計算。如果圖片有旋轉(zhuǎn),計算起來就麻煩。比如判斷鼠標(biāo)是否點擊了圖片、鼠標(biāo)縮放等,實現(xiàn)這些操作都麻煩。
比如判斷鼠標(biāo)是否點擊了圖片,如果一個圖片是斜的(旋轉(zhuǎn)后的),如何處理?我的思路是旋轉(zhuǎn):將圖片和鼠標(biāo)所在的點都反向旋轉(zhuǎn);此后,判斷邏輯就和常規(guī)方法一樣了。旋轉(zhuǎn)函數(shù)如下:
/// <summary>
/// pointMove相對于removeAt,以一定角度旋轉(zhuǎn)
/// </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)
{
//鼠標(biāo)位置 反向旋轉(zhuǎn),
pt = DrawHelper.RotationAt(pt, Location, RotateAngle, false);
//下面就是 和正常判斷邏輯一樣
EN_LinePart result = MouseMove_HitTest_Corner(pt);
if (result != EN_LinePart.無)
return result;
}
畫圖:
對圖片相關(guān)參數(shù)修改后,需要調(diào)用refresh,強制重畫。調(diào)用GDI+。根據(jù)圖片在列表的順序調(diào)用(也就是根據(jù)圖層)。調(diào)用時,根據(jù)設(shè)定顯示區(qū)域,旋轉(zhuǎn)角度等,做變換后再畫。
void DrawWithRotation(Graphics g, bool saveToFile)
{
//設(shè)置質(zhì)量
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);
//畫旋轉(zhuǎn)基點
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();
}
//畫選中狀態(tài)
if (!saveToFile && selectImage != null)
{
DrawSelectImageWithRotation(g, selectImage);
}
}
后記:
一般來講,圖像的處理屬于比較難的操作。需要有空間想象能力,相應(yīng)的幾何數(shù)學(xué)基礎(chǔ)。不過,如果掌握好了圖像操作,對了解控件原理很有幫助。當(dāng)遇到難以實現(xiàn)的界面,gdi+就是最后的手段;winform也是微軟過時的技術(shù)了,使用winform作圖效率很難提高;為了響應(yīng)的事件,不停重畫,效率很低。WPF對圖像的操作又進了一步,wpf屬于“保持模型”,就是你告訴操作系統(tǒng)你要畫什么就行了,只需要告訴一次。而對于winform,操作系統(tǒng)不停的告訴你,你需要重畫了。這就導(dǎo)致winform畫圖效率比較低,但是省了內(nèi)存。
以上就是c# WinForm制作圖片編輯工具(圖像拖動、縮放、旋轉(zhuǎn)、摳圖)的詳細內(nèi)容,更多關(guān)于c# WinForm圖片編輯的資料請關(guān)注腳本之家其它相關(guān)文章!

