WPF 图片裁剪大部分是在前台界面写死的,而我的是利用装饰器Adorner做的,大小、位置可以修改。
前台是这样的:
<StackPanel Background="#d9d9d9" Grid.Row="1" Orientation="Horizontal">
<Image Source="Image/cut.png" Margin="10,0" Width="84" Height="82"/>
<Grid Margin="150,0,15,0" Width="150">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25*"/>
<ColumnDefinition Width="40*"/>
<ColumnDefinition Width="25*"/>
</Grid.ColumnDefinitions>
<Label Content="宽度" Grid.Column="0" Foreground="#666668" Height="28" FontSize="13" Name="label1" />
<TextBox Grid.Column="1" Height="28" Name="Cutwidth" TextChanged="Cutwidth_TextChanged" KeyDown="Cutwidth_KeyDown" Margin="0,36,0,0" VerticalAlignment="Top" />
<Label Content="像素" Grid.Column="2" Foreground="#666668" Height="28" Name="label2" />
</Grid>
<Grid Width="150">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25*"/>
<ColumnDefinition Width="40*"/>
<ColumnDefinition Width="25*"/>
</Grid.ColumnDefinitions>
<Label Content="高度" Grid.Column="0" Foreground="#666668" Height="28" FontSize="13" Name="label3" />
<TextBox Grid.Column="1" Height="28" Name="Cutheight" TextChanged="Cutheight_TextChanged" KeyDown="Cutheight_KeyDown" Margin="0,36,0,0" VerticalAlignment="Top" />
<Label Content="像素" Foreground="#666668" Grid.Column="2" Height="28" Name="label4" />
</Grid>
</StackPanel>
<Border Grid.Row="2" Name="border">
<Canvas Name="canvas" HorizontalAlignment="Center" VerticalAlignment="Center">
<Image Name="imageCut" Stretch="UniformToFill" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Canvas>
</Border>
<StackPanel Grid.Row="3" Orientation="Horizontal">
<Button x:Name="button1"
Width="85"
Height="26"
Margin="10,0"
BorderThickness="0"
Cursor="Hand"
FocusVisualStyle="{x:Null}"
Padding="0"
Style="{DynamicResource XmBtnOk}" Click="btnLook_Click" />
<Button x:Name="button2"
Width="85"
Height="26"
Margin="10,0"
BorderThickness="0"
Cursor="Hand"
FocusVisualStyle="{x:Null}"
Padding="0"
Style="{DynamicResource XmBtnCancel}" Click="btnCancel_Click" />
<Button x:Name="button3"
Width="85"
Height="26"
Margin="10,0"
BorderThickness="0"
Cursor="Hand"
FocusVisualStyle="{x:Null}"
Padding="0"
Style="{DynamicResource XmBtnSave}" Click="btnSave_Click" />
</StackPanel>
</Grid>
文本框用来显示裁剪框大小,相关的Adorner装饰器用一个.cs类:
public class MyCanvasAdorner : Adorner
{
const double THUMB_SIZE = 12;
const double MINIMAL_SIZE = 0;
const double MOVE_OFFSET = 20;
// ModelTools model = new ModelTools();
Thumb tl, tr, bl, br, rc, lc, tc, bc;
Thumb mov;
VisualCollection visCollec;
public System.Windows.UIElement saveuie;
bool flag;
public MyCanvasAdorner(UIElement adorned, bool _flag)
: base(adorned)
{
saveuie = adorned;
flag = _flag;
visCollec = new VisualCollection(this);
if (!flag)
{
visCollec.Add(mov = GetMoveThumb());
}
else
{
visCollec.Add(tl = GetResizeThumb(Cursors.SizeNWSE, HorizontalAlignment.Left, VerticalAlignment.Top));
visCollec.Add(tr = GetResizeThumb(Cursors.SizeNESW, HorizontalAlignment.Right, VerticalAlignment.Top));
visCollec.Add(bl = GetResizeThumb(Cursors.SizeNESW, HorizontalAlignment.Left, VerticalAlignment.Bottom));
visCollec.Add(br = GetResizeThumb(Cursors.SizeNWSE, HorizontalAlignment.Right, VerticalAlignment.Bottom));
visCollec.Add(rc = GetResizeThumb(Cursors.SizeWE, HorizontalAlignment.Right, VerticalAlignment.Center));
visCollec.Add(lc = GetResizeThumb(Cursors.SizeWE, HorizontalAlignment.Left, VerticalAlignment.Center));
visCollec.Add(tc = GetResizeThumb(Cursors.SizeNS, HorizontalAlignment.Center, VerticalAlignment.Top));
visCollec.Add(bc = GetResizeThumb(Cursors.SizeNS, HorizontalAlignment.Center, VerticalAlignment.Bottom));
// visCollec.Add(mov = GetMoveThumb());
}
}
protected override Size ArrangeOverride(Size finalSize)
{
double offset = THUMB_SIZE / 2;
Size sz = new Size(THUMB_SIZE, THUMB_SIZE);
Size newsz = new Size(800, 2);
if (!flag)
{
mov.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width / 2 - THUMB_SIZE / 2, -(MOVE_OFFSET * 2)), sz));
mov.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(tl_PreviewMouseLeftButtonUp);
}
else
{
tl.Arrange(new Rect(new Point(-offset * 2, -offset * 2), sz));
tl.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(tl_PreviewMouseLeftButtonUp);
tr.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width, -offset * 2), sz));
tr.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(tl_PreviewMouseLeftButtonUp);
bl.Arrange(new Rect(new Point(-offset * 2, AdornedElement.RenderSize.Height), sz));
bl.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(tl_PreviewMouseLeftButtonUp);
br.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width, AdornedElement.RenderSize.Height), sz));
br.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(tl_PreviewMouseLeftButtonUp);
rc.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width, (AdornedElement.RenderSize.Height - offset) / 2), sz));
rc.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(tl_PreviewMouseLeftButtonUp);
lc.Arrange(new Rect(new Point(-offset * 2, (AdornedElement.RenderSize.Height - offset) / 2), sz));
lc.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(tl_PreviewMouseLeftButtonUp);
tc.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width / 2 - THUMB_SIZE / 2, -offset * 2), sz));
tc.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(tl_PreviewMouseLeftButtonUp);
bc.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width / 2 - THUMB_SIZE / 2, AdornedElement.RenderSize.Height), sz));
bc.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(tl_PreviewMouseLeftButtonUp);
// mov.Arrange(new Rect(new Point(AdornedElement.RenderSize.Width / 2 - THUMB_SIZE / 2, -(MOVE_OFFSET * 2)), sz));
// mov.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(tl_PreviewMouseLeftButtonUp);
}
return finalSize;
}
void tl_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
}
void Resize(FrameworkElement ff)
{
if (Double.IsNaN(ff.Width))
ff.Width = ff.RenderSize.Width;
if (Double.IsNaN(ff.Height))
ff.Height = ff.RenderSize.Height;
}
Thumb GetMoveThumb()
{
var thumb = new Thumb()
{
Width = THUMB_SIZE,
Height = THUMB_SIZE,
Cursor = Cursors.SizeAll,
Template = new ControlTemplate(typeof(Thumb))
{
VisualTree = GetFactory(new SolidColorBrush(Colors.Red))
}
};
thumb.DragDelta += (s, e) =>
{
var element = AdornedElement as FrameworkElement;
if (element == null)
return;
Canvas.SetLeft(element, double.Parse((Canvas.GetLeft(element) + e.HorizontalChange).ToString("F0")));
Canvas.SetTop(element, double.Parse((Canvas.GetTop(element) + e.VerticalChange).ToString("F0")));
};
return thumb;
}
Thumb GetResizeThumb(Cursor cur, HorizontalAlignment hor, VerticalAlignment ver)
{
var thumb = new Thumb()
{
Background = Brushes.Red,
Width = THUMB_SIZE,
Height = THUMB_SIZE,
HorizontalAlignment = hor,
VerticalAlignment = ver,
Cursor = cur,
Template = new ControlTemplate(typeof(Thumb))
{
VisualTree = GetFactory(new SolidColorBrush(Colors.DarkTurquoise))
}
};
if (flag)
{
thumb.DragDelta += (s, e) =>
{
var element = AdornedElement as FrameworkElement;
if (element == null)
return;
Resize(element);
switch (thumb.VerticalAlignment)
{
case VerticalAlignment.Bottom:
if (element.Height + e.VerticalChange > MINIMAL_SIZE)
{
element.Height += double.Parse(e.VerticalChange.ToString("F0"));
}
break;
case VerticalAlignment.Top:
if ((double)element.GetValue(Canvas.TopProperty) + e.VerticalChange < 0)
return;
if (element.Height - e.VerticalChange > MINIMAL_SIZE)
{
element.Height -= double.Parse(e.VerticalChange.ToString("F0"));
Canvas.SetTop(element, double.Parse((Canvas.GetTop(element) + e.VerticalChange).ToString("F0")));
}
break;
}
switch (thumb.HorizontalAlignment)
{
case HorizontalAlignment.Left:
if ((double)element.GetValue(Canvas.LeftProperty) + e.HorizontalChange < 0)
return;
if (element.Width - e.HorizontalChange > MINIMAL_SIZE)
{
element.Width -= double.Parse(e.HorizontalChange.ToString("F0"));
Canvas.SetLeft(element, double.Parse((Canvas.GetLeft(element) + e.HorizontalChange).ToString("F0")));
}
break;
case HorizontalAlignment.Right:
if (element.Width + e.HorizontalChange > MINIMAL_SIZE)
{
element.Width += double.Parse(e.HorizontalChange.ToString("F0"));
}
break;
}
e.Handled = true;
};
}
return thumb;
}
/// <summary>
/// 将Color转换成字符串
/// </summary>
/// <param name="_color"></param>
/// <returns></returns>
public static string ColorToHex(System.Drawing.Color _color)
{
return "0x" + String.Format("{0:X}", System.Drawing.Color.FromArgb(_color.R, _color.G, _color.B).ToArgb()).Substring(2);
}
Brush GetMoveEllipseBack()
{
string lan = "M 0,5 h 10 M 5,0 v 10";
var converter = TypeDescriptor.GetConverter(typeof(Geometry));
var geometry = (Geometry)converter.ConvertFrom(lan);
TileBrush bsh = new DrawingBrush(new GeometryDrawing(Brushes.Transparent, new Pen(Brushes.Black, 2), geometry));
bsh.Stretch = Stretch.Fill;
return bsh;
}
FrameworkElementFactory GetFactory(Brush back)
{
back.Opacity = 1.0;
var fef = new FrameworkElementFactory(typeof(Rectangle));
fef.SetValue(Rectangle.FillProperty, back);
fef.SetValue(Rectangle.StrokeProperty, Brushes.Gray);
fef.SetValue(Rectangle.StrokeThicknessProperty, (double)1);
return fef;
}
FrameworkElementFactory GetlineFactory(Brush back)
{
back.Opacity = 1.0;
var fef = new FrameworkElementFactory(typeof(Line));
fef.SetValue(Line.FillProperty, back);
fef.SetValue(Line.StrokeProperty, Brushes.Blue);
fef.SetValue(Line.StrokeThicknessProperty, (double)1);
return fef;
}
FrameworkElementFactory GetLRTBFactory(Brush back)
{
back.Opacity = 1.0;
var fef = new FrameworkElementFactory(typeof(Ellipse));
fef.SetValue(Ellipse.FillProperty, back);
fef.SetValue(Ellipse.StrokeProperty, Brushes.DarkTurquoise);
fef.SetValue(Ellipse.StrokeThicknessProperty, (double)1);
return fef;
}
protected override Visual GetVisualChild(int index)
{
return visCollec[index];
}
protected override int VisualChildrenCount
{
get
{
return visCollec.Count;
}
}
}
后台声明的一些变量:
//裁剪用值
MyCanvasAdorner myAdorner = null;
AdornerLayer layer;
public double m = (Math.PI / 2);
Rectangle rect = null;
RectangleGeometry rectgeo;
public static double _newx, _newy, _oldx, _oldy;
private Image img;
private Canvas can;
private double rectleft,recttop,rectWidth,rectHeight; //未缩放的
private double sfrectleft,sfrecttop,sfrectWidth,sfrectHeight; //缩放了的
BitmapImage newPathImg;
private string uriImg;
bool IsMouseDown = false;
Point mousePoint;
object mouseCtrl = null;
public static Canvas workcanvas = null;
private Stream stream2;
后台加载事件加载装饰,是这样利用上面的类:
rect = new Rectangle();
rect.Opacity = 0.5;
rect.Fill = new SolidColorBrush(Colors.Black);
rect.SetValue(Canvas.LeftProperty, 0.0);
rect.SetValue(Canvas.TopProperty, 0.0);
canvas.Children.Add(rect);
layer = AdornerLayer.GetAdornerLayer(canvas);
myAdorner = new MyCanvasAdorner(rect, true);
layer.Add(myAdorner);
rect.Width = Math.Round(canvas.Width * 0.8);
rect.Height = Math.Round(canvas.Height * 0.8);
Cutwidth.Text = rect.Width + "";
//Cutwidth.Text = (double.Parse(rect.GetValue(Canvas.WidthProperty).ToString())/canvas.Width*newPathImg.PixelWidth).ToString();
Cutheight.Text = rect.Height + ""; ;//(double.Parse(rect.GetValue(Canvas.HeightProperty).ToString())/canvas.Height*newPathImg.PixelHeight).ToString();
rect.SizeChanged += new SizeChangedEventHandler(rect_SizeChanged); //为了文本框数值随着裁剪框大小改变
rect.PreviewMouseDown += new MouseButtonEventHandler(rect_PreviewMouseDown);
文本框数据随着装饰器的大小变化:
private void rect_SizeChanged(object sender, SizeChangedEventArgs e)
{
//文本框绑定矩形宽
Cutwidth.Text = Math.Round(double.Parse(rect.GetValue(Canvas.WidthProperty).ToString()) / canvas.Width * newPathImg.PixelWidth).ToString();
//文本框绑定矩形高
Cutheight.Text = Math.Round(double.Parse(rect.GetValue(Canvas.HeightProperty).ToString()) / canvas.Height * newPathImg.PixelHeight).ToString();
计算裁剪框的移动,不可移到图片外
//矩形左上角坐标
double leftTopl = double.Parse(rect.GetValue(Canvas.LeftProperty).ToString());
double leftTopt = double.Parse(rect.GetValue(Canvas.TopProperty).ToString());
//矩形右下角坐标
double rightBottom1 = leftTopl + rect.Width;// double.Parse(Cutwidth.Text);
double rightBottom2 = leftTopt + rect.Height;// double.Parse(Cutheight.Text);
if (rightBottom1 >= canvas.Width)
{
rect.Width = canvas.Width - leftTopl;
}
if (rightBottom2 >= canvas.Height)
{
rect.Height = canvas.Height - leftTopt;
}
}
void rect_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Point point = e.MouseDevice.GetPosition(canvas);
Point pointimg = rect.TranslatePoint(new Point(0, 0), canvas);
_newx = point.X;
_newy = point.Y;
_oldx = pointimg.X;
_oldy = pointimg.Y;
canvas.MouseMove += new MouseEventHandler(canvas_MouseMove);
rect.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(rect_PreviewMouseLeftButtonUp);
}
void rect_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
canvas.MouseMove -= new MouseEventHandler(canvas_MouseMove);
rect.PreviewMouseLeftButtonUp -= new MouseButtonEventHandler(rect_PreviewMouseLeftButtonUp);
}
void canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point mp = e.MouseDevice.GetPosition(canvas);
if (_oldx + rect.Width > canvas.Width)
{
rect.SetValue(Canvas.LeftProperty, canvas.ActualWidth - rect.Width);
_oldx = canvas.ActualWidth - rect.Width;
}
else if (_oldx <= -0.001)
{
rect.SetValue(Canvas.LeftProperty, 0.0);
_oldx = 0.0;
}
else
{
double nx = mp.X - _newx;
_newx = mp.X;
rect.SetValue(Canvas.LeftProperty, double.Parse((_oldx + nx).ToString("F0")));
_oldx += nx;
}
if (_oldy + rect.Height >= canvas.Height)
{
rect.SetValue(Canvas.TopProperty, canvas.Height - rect.Height);
_oldy--;
}
else if (_oldy <= -0.001)
{
rect.SetValue(Canvas.TopProperty, 0.0);
_oldy = 0.0;
}
else
{
double ny = mp.Y - _newy;
_newy = mp.Y;
rect.SetValue(Canvas.TopProperty, double.Parse((_oldy + ny).ToString("F0")));
_oldy += ny;
}
e.Handled = true;
}
}
还有用来屏蔽非法按键的一段编码,这样输入的值就必须是数字了,如果想要小数点那就改下,已注释掉了:
//屏蔽非法按键
if ((e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9) || e.Key == Key.Decimal)
{
if (Cutwidth.Text.Contains(".") && e.Key == Key.Decimal)
{
e.Handled = false;
} e.Handled = false; } else if (((e.Key >= Key.D0 && e.Key <= Key.D9) || e.Key == Key.OemPeriod) && e.KeyboardDevice.Modifiers != ModifierKeys.Shift) { if (Cutwidth.Text.Contains(".") && e.Key == Key.OemPeriod) { e.Handled = false;// e.Handled = true; return;
} e.Handled = false; } else { e.Handled = true; }// e.Handled = true; return;
//屏蔽中文输入和非法字符粘贴输入 TextChange[] change = new TextChange[e.Changes.Count]; e.Changes.CopyTo(change, 0); int offset = change[0].Offset; if (change[0].AddedLength > 0) { double num = 0; if (!Double.TryParse(Cutwidth.Text, out num)) { Cutwidth.Text = Cutwidth.Text.Remove(offset, change[0].AddedLength); Cutwidth.Select(offset, 0); MessageBox.Show("请不要中文输入和非法字符粘贴输入"); } } //限制只能输入整数 Regex regex = new Regex("^([1-9][0-9]*|0)$"); if (regex.IsMatch(Cutwidth.Text)) { rect.Width = double.Parse(Cutwidth.Text)/newPathImg.PixelWidth*canvas.Width; //判断输入值,当大于显示范围则为原始的宽或高 if (double.Parse(Cutwidth.Text) > newPathImg.PixelWidth) { rect.Width = Math.Round(canvas.Width); rect.SetValue(Canvas.LeftProperty, 0.0); Cutwidth.Text = newPathImg.PixelWidth.ToString(); } } else {
裁剪图片,确定裁剪某部分时,可以预览裁剪的部分:
CutImage(imageCut); //这句只是调用下面方法,即预览图片按钮这句话就行了
private void CutImage(Image img) { rectgeo = new RectangleGeometry(new Rect(double.Parse(rect.GetValue(Canvas.LeftProperty).ToString()), double.Parse(rect.GetValue(Canvas.TopProperty).ToString()), rect.Width, rect.Height), 0, 0); img.Clip = rectgeo; rect.Visibility = Visibility.Collapsed; layer.Remove(myAdorner); } Cutwidth.Text = "0"; rect.SetValue(Canvas.WidthProperty, 0.0); }
当然看到对裁剪并满意,还可以取消预览,即重新选择裁剪:那么,当我们确定裁剪时,为了避免出现占用资源,所以我在传图片过来时已将图片转化为字节数组作为备份,我是将将图片BitmapImage传值过来的,利用显示图片的裁剪情况来计算原始裁剪情况,那么要将裁剪过后的原始大小传回主窗口,我采用将生成的BitmapSource转为Bitmap方法,在主窗口接收Bitmap类型的值:imageCut.Clip = null; rect.Visibility = Visibility.Visible; layer.Add(myAdorner); //文本框绑定矩形宽 // Cutwidth.Text =(canvas.Width*0.8).ToString(); Cutwidth.Text = Math.Round(double.Parse(rect.GetValue(Canvas.WidthProperty).ToString()) / canvas.Width * newPathImg.PixelWidth).ToString(); //文本框绑定矩形高 // Cutheight.Text = (canvas.Height*0.8).ToString(); Cutheight.Text = Math.Round(double.Parse(rect.GetValue(Canvas.HeightProperty).ToString()) / canvas.Height * newPathImg.PixelHeight).ToString();
这样就可以了,裁剪图片也在后台控制。//缩放的图片截图 sfrectWidth = rect.Width; sfrectHeight = rect.Height; sfrectleft = double.Parse(rect.GetValue(Canvas.LeftProperty).ToString()); sfrecttop = double.Parse(rect.GetValue(Canvas.TopProperty).ToString()); //未缩放的图片截图 rectWidth = sfrectWidth / canvas.Width * newPathImg.PixelWidth; rectHeight = sfrectHeight / canvas.Height * newPathImg.PixelHeight; rectleft = sfrectleft / canvas.Width * newPathImg.PixelWidth; recttop = sfrecttop / canvas.Height * newPathImg.PixelHeight; img = new Image(); MemoryStream mstream = new MemoryStream(MainImg.newbyte); BitmapImage bmp = new BitmapImage(); bmp.CacheOption = BitmapCacheOption.OnLoad; bmp.BeginInit(); bmp.StreamSource = mstream; bmp.EndInit(); BitmapImage bb = bmp.Clone(); img.Source = bb; var cut = new Int32Rect(int.Parse(rectleft.ToString("F0")), int.Parse(recttop.ToString("F0")), int.Parse(rectWidth.ToString("F0")), int.Parse(rectHeight.ToString("F0"))); var stride = bmp.Format.BitsPerPixel * cut.Width / 8; byte[] data = new byte[cut.Height * stride]; bmp.CopyPixels(cut, data, stride, 0); //转换为Bitmap BitmapSource s = BitmapSource.Create(int.Parse(rectWidth.ToString("F0")), int.Parse(rectHeight.ToString("F0")), 0, 0, PixelFormats.Bgr32, null, data, stride); System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(s.PixelWidth, s.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); System.Drawing.Imaging.BitmapData bitmapdata = bitmap.LockBits(new System.Drawing.Rectangle(System.Drawing.Point.Empty, bitmap.Size), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); s.CopyPixels(Int32Rect.Empty, bitmapdata.Scan0, bitmapdata.Height * bitmapdata.Stride, bitmapdata.Stride); bitmap.UnlockBits(bitmapdata); img.Source = BitmapSource.Create(int.Parse(rectWidth.ToString("F0")), int.Parse(rectHeight.ToString("F0")), 0, 0, PixelFormats.Bgr32, null, data, stride); MainImg.act2(bitmap);