前几日在公司做sl上传头像,因为代码不能拿出来,所以自己大体写了下。功能上算是实现了。效果图:
点击Browse按钮打开一张图片,点击Clip会出来一个红色的方框。可以用鼠标拖动红框,在右边角有一个三角,可以改变大小。还有四周灰色的蒙版,用了四个Rectangle,如下图
四个Rectangle的放置,当然不一定按照我的做法,只要能实现功能即可。
xaml代码:
<controls:ChildWindow x:Class="SlTest.ClipImg"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
Width="352" Height="437"
Title="ClipImg">
<Grid x:Name="LayoutRoot" Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--上方两个功能按钮-->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Button Content="Browse" Width="75" Click="OnSelectPicClick"/>
<Button Content="Clip" Width="75" Click="OnClipClick"/>
</StackPanel>
<Canvas x:Name="ImgCanvas" Grid.Row="1" Height="340" Width="310" Margin="0 5 0 0">
<Image x:Name="SourceImg" Margin="0" MaxHeight="340" MaxWidth="310" Stretch="Uniform" HorizontalAlignment="Center" VerticalAlignment="Center"
MouseLeftButtonDown="OnImgMouseLeftBtnDown" MouseMove="OnImgMouseMove" MouseLeftButtonUp="OnImgMosueLeftBtnUp"/>
<!--做蒙版的四个Rectangle-->
<Rectangle Name="topMask" Fill="#4333" Canvas.Top="0" Canvas.Left="0" />
<Rectangle Name="bottomMask" Fill="#4333" />
<Rectangle Name="leftMask" Fill="#4333" Canvas.Left="0"/>
<Rectangle Name="rightMask" Fill="#4333" Canvas.Top="0" />
<!--红色的框,还有右下角的红色三角(一个Path)-->
<Border x:Name="ImgCropBorder" Height="140" Width="140" BorderBrush="Red" BorderThickness="1" Visibility="Collapsed" Margin="0"
MouseLeftButtonDown="OnBorderMouseLeftBtnDown" MouseMove="OnBorderMouseMove" MouseLeftButtonUp="OnBorderMosueLeftBtnUp" >
<Path Data="M400,215 L370,240 L400,240 z" Fill="Red" HorizontalAlignment="Right" VerticalAlignment="Bottom" Cursor="SizeNWSE"
Stretch="Fill" Stroke="Red" Width="10" Height="10" UseLayoutRounding="False" />
</Border>
</Canvas>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Grid.Row="2">
<Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75" Height="23" Margin="5 0"/>
<Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_Click" Width="75" Height="23" Margin="5 0"/>
</StackPanel>
</Grid>
</controls:ChildWindow>
cs代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.Windows.Media.Imaging;
namespace SlTest
{
public partial class ClipImg : ChildWindow
{
public ClipImg()
{
InitializeComponent();
}
private void OKButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
}
#region move & resize the Red Boreder
private bool isBorder, isPath;
private Point mousePosition;
private void OnImgMouseLeftBtnDown(object sender, MouseButtonEventArgs e)
{
if (ImgCropBorder.Visibility == Visibility.Visible)
{
FrameworkElement element = sender as FrameworkElement;
Point p = e.GetPosition(ImgCropBorder);
if (p.X > 0 && p.X <= ImgCropBorder.Width
&& p.Y > 0 && p.Y <= ImgCropBorder.Height)
{
isBorder = true;
mousePosition = e.GetPosition(null);
if (element != null)
{
element.CaptureMouse();
element.Cursor = Cursors.Hand;
}
}
}
}
private void OnImgMouseMove(object sender, MouseEventArgs e)
{
if (isBorder)
{
double deltaV = e.GetPosition(null).Y - mousePosition.Y;
double deltaH = e.GetPosition(null).X - mousePosition.X;
double newTop = deltaV + (double)ImgCropBorder.GetValue(Canvas.TopProperty);
double newLeft = deltaH + (double)ImgCropBorder.GetValue(Canvas.LeftProperty);
if (newTop >= 0 && newTop <= SourceImg.Height - ImgCropBorder.Height)
ImgCropBorder.SetValue(Canvas.TopProperty, newTop);
if (newLeft >= 0 && newLeft <= SourceImg.Width - ImgCropBorder.Width)
ImgCropBorder.SetValue(Canvas.LeftProperty, newLeft);
mousePosition = e.GetPosition(null);
//CropImg();
SetMask();
}
}
private void OnImgMosueLeftBtnUp(object sender, MouseButtonEventArgs e)
{
isBorder = false;
SourceImg.ReleaseMouseCapture();
mousePosition.X = mousePosition.Y = 0;
SourceImg.Cursor = null;
}
private void OnBorderMouseLeftBtnDown(object sender, MouseButtonEventArgs e)
{
FrameworkElement element = sender as FrameworkElement;
mousePosition = e.GetPosition(null);
Point p = e.GetPosition(ImgCropBorder);
if (p.X > 0 && p.X <= ImgCropBorder.Width
&& p.Y > 0 && p.Y <= ImgCropBorder.Height)
{
mousePosition = e.GetPosition(null);
isPath = true;
if (element != null)
{
element.CaptureMouse();
element.Cursor = Cursors.SizeNWSE;
}
}
}
private void OnBorderMouseMove(object sender, MouseEventArgs e)
{
if (isPath)
{
double deltaV = e.GetPosition(null).Y - mousePosition.Y;
double deltaH = e.GetPosition(null).X - mousePosition.X;
double newHeight = deltaV + ImgCropBorder.Height;
double newWidth = deltaH + ImgCropBorder.Width;
if (newHeight + Convert.ToDouble(ImgCropBorder.GetValue(Canvas.TopProperty)) > SourceImg.Height)
newHeight = SourceImg.Height - Convert.ToDouble(ImgCropBorder.GetValue(Canvas.TopProperty));
if (newWidth + Convert.ToDouble(ImgCropBorder.GetValue(Canvas.LeftProperty)) > SourceImg.Width)
newWidth = SourceImg.Width - Convert.ToDouble(ImgCropBorder.GetValue(Canvas.LeftProperty));
if (newHeight > 0 && newWidth > 0)
{
ImgCropBorder.Height = newHeight;
ImgCropBorder.Width = newWidth;
}
mousePosition = e.GetPosition(null);
//CropImg();
SizeToMask();
}
}
private void OnBorderMosueLeftBtnUp(object sender, MouseButtonEventArgs e)
{
isPath = false;
FrameworkElement element = sender as FrameworkElement;
element.ReleaseMouseCapture();
mousePosition.X = mousePosition.Y = 0;
element.Cursor = null;
}
#endregion
private void OnSelectPicClick(object sender, RoutedEventArgs e)
{
try
{
OpenFileDialog ofd = new OpenFileDialog();
if (true == ofd.ShowDialog())
{
FileInfo fi = ofd.File;
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(fi.OpenRead());
if (bitmap.PixelHeight >= bitmap.PixelWidth)
{
if (bitmap.PixelHeight >= ImgCanvas.Height)
{
SourceImg.Width = ImgCanvas.Height / bitmap.PixelWidth * bitmap.PixelWidth;
SourceImg.Height = ImgCanvas.Height;
}
else
{
SourceImg.Width = bitmap.PixelWidth;
SourceImg.Height = bitmap.PixelHeight;
}
}
else
{
if (bitmap.PixelWidth > ImgCanvas.Width)
{
SourceImg.Height = bitmap.PixelHeight * ImgCanvas.Width / bitmap.PixelWidth;
SourceImg.Width = ImgCanvas.Width;
}
else
{
SourceImg.Height = bitmap.PixelHeight;
SourceImg.Width = bitmap.PixelWidth;
}
}
SourceImg.Source = bitmap;
ResetMask();
}
}
catch (Exception ex) { MessageBox.Show(ex.ToString()); }
}
private void OnClipClick(object sender, RoutedEventArgs e)
{
if (SourceImg.Source != null)
{
ImgCropBorder.Width = ImgCropBorder.Height = 140;
if (SourceImg.Height < 140)
ImgCropBorder.Height = SourceImg.Height / 2;
if (SourceImg.Width < 140)
ImgCropBorder.Width = SourceImg.Width / 2;
if (ImgCropBorder.Width > ImgCropBorder.Height)
ImgCropBorder.Width = ImgCropBorder.Height;
else
ImgCropBorder.Height = ImgCropBorder.Width;
Canvas.SetTop(ImgCropBorder, SourceImg.Height / 2 - ImgCropBorder.Width / 2);
Canvas.SetLeft(ImgCropBorder, SourceImg.Width / 2 - ImgCropBorder.Width / 2);
if (ImgCropBorder.Visibility == Visibility.Collapsed)
{
ImgCropBorder.Visibility = System.Windows.Visibility.Visible;
SetMask();
}
else
{
ImgCropBorder.Visibility = System.Windows.Visibility.Collapsed;
WriteableBitmap _WriteableBitmap = new WriteableBitmap((int)ImgCropBorder.Width, (int)ImgCropBorder.Width);
TranslateTransform t = new TranslateTransform();
t.Y = -1 * Convert.ToDouble(ImgCropBorder.GetValue(Canvas.TopProperty));
t.X = -1 * Convert.ToDouble(ImgCropBorder.GetValue(Canvas.LeftProperty));
_WriteableBitmap.Render(SourceImg, t);
_WriteableBitmap.Invalidate();
SourceImg.Source = _WriteableBitmap;
ResetMask();
}
}
}
private void SetMask()
{
double left = Canvas.GetLeft(ImgCropBorder);
double top = Canvas.GetTop(ImgCropBorder);
topMask.Height = top;
leftMask.Height = SourceImg.Height - top;
leftMask.Width = left;
Canvas.SetTop(leftMask, top);
SizeToMask();
}
private void SizeToMask()
{
double left = Canvas.GetLeft(ImgCropBorder);
double top = Canvas.GetTop(ImgCropBorder);
topMask.Width = left + ImgCropBorder.Width;
rightMask.Height = top + ImgCropBorder.Height;
rightMask.Width = SourceImg.Width - left - ImgCropBorder.Width;
Canvas.SetLeft(rightMask, left + ImgCropBorder.Width);
bottomMask.Width = SourceImg.Width - left;
bottomMask.Height = SourceImg.Height - top - ImgCropBorder.Height;
Canvas.SetLeft(bottomMask, left);
Canvas.SetTop(bottomMask, top + ImgCropBorder.Height);
}
private void ResetMask()
{
leftMask.Width = 0;
rightMask.Width = 0;
topMask.Width = 0;
bottomMask.Width = 0;
}
}
}
移动红色框的事件是写在Image上的,因为红色的框是透明的。改变大小的事件写在Border上不是Path上。代码比较简单基本上都能够看懂
我就不浪费口舌了。。。
图片的截取和压缩主要通过WriteableBitmap 这个类实现的,对它进行相应的变换就可以实现截取和压缩。
由WriteableBitmap转换成byte我调用了FluxJpeg这个三方的。博客园上有很多说明,在博客园上搜索“FluxJpeg”就可以找的到,
也有很详细说明。
还有一个我用到的类:
public class ImgByte
{
public static byte[] BitMapToByte(System.Windows.Media.Imaging.WriteableBitmap bitmap)
{
if (bitmap == null) return null;
int width = bitmap.PixelWidth;
int height = bitmap.PixelHeight;
int bands = 3;
byte[][,] raster = new byte[bands][,];
for (int i = 0; i < bands; i++)
{
raster[i] = new byte[width, height];
}
for (int row = 0; row < height; row++)
{
for (int column = 0; column < width; column++)
{
int pixel = bitmap.Pixels[width * row + column];
byte a = ((byte)(pixel >> 24));
byte r = (byte)(pixel >> 16);//4 R
byte g = (byte)(pixel >> 8);//2 G
byte b = (byte)pixel;//0 B
if (a < 2)
{
raster[0][column, row] = (byte)(255 - r);
raster[1][column, row] = (byte)(255 - g);
raster[2][column, row] = (byte)(255 - b);
}
else
{
raster[0][column, row] = (byte)(r * 255.0 / a);
raster[1][column, row] = (byte)(g * 255.0 / a);
raster[2][column, row] = (byte)(b * 255.0 / a);
}
}
}
FluxJpeg.Core.ColorModel model = new FluxJpeg.Core.ColorModel { colorspace = FluxJpeg.Core.ColorSpace.RGB };
FluxJpeg.Core.Image img = new FluxJpeg.Core.Image(model, raster);
//Encode the Image as a JPEG
System.IO.MemoryStream stream = new System.IO.MemoryStream();
FluxJpeg.Core.Encoder.JpegEncoder encoder = new FluxJpeg.Core.Encoder.JpegEncoder(img, 100, stream);
encoder.Encode();
//Back to the start
stream.Seek(0, System.IO.SeekOrigin.Begin);
//Get teh Bytes and write them to the stream
byte[] binaryData = new byte[stream.Length];
long bytesRead = stream.Read(binaryData, 0, (int)stream.Length);
return binaryData;
}
}
使用的这个类转化成Image的,对于透明的图片,我做了点修正,透明图片会用白色填充。