Silverlight客户端实现图片的截取和压缩----之上传头像
前几日在公司做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的,对于透明的图片,我做了点修正,透明图片会用白色填充。