WPF头像裁剪

来源:互联网 发布:中国地名数据库下载 编辑:程序博客网 时间:2024/05/16 15:09

需求很常见,就是用户上传头像前进行固定大小的裁剪。
百度一番,找到几个差不多的,
其一 http://download.csdn.net/detail/tianhaosen/7159901,这个的实现方式是截图框大小固定不变,背景图可以通过鼠标拖动和鼠标滚轮缩放,经过测试,这个对图片的裁剪不是很准确,尤其是大图或者靠近图片边缘裁剪的时候会出现较大误差,然后我尝试调整了下截图位置的算法,但多少还是有偏差,无奈只好放弃,有兴趣的同学可以下下来研究研究。
其二 http://blog.csdn.net/jtl309/article/details/50651911,这个的实现方式是背景图片保持大小不变,裁剪框可以通过鼠标拖动进行缩放。于是尝试在这个的基础上进行调整:)
首先,裁剪框的拖动以及缩放作者已经做的很完善了,主要是利用了Thumb控件进行自定义。我的需求是裁剪框要是一个固定大小的正方形,所以我对作者的源码进行了修改。源码中作者是在DragHelperBase.cs中对矩形框的大小进行实时计算,其中有4个ResizeFrom**方法,我在这4个方法的入参中均加入了宽高(out double HeightNew,out double WidthNew),并且在方法的最后一行赋值高和宽相等,这样就保证了裁剪框始终是正方形,另外,作者的这个框框拖动的时候能把框拖到大边框以外,是个小bug,我这边在每次拖动之后都跟Parent.ActualWidth比较一下。ok,算法代码修改完毕,下面就是对样式代码进行了一些调整,这个各位同学根据自己需求改动即可。
主要修改了DragHelperBase.cs代码:

   #region ResizeElement        private Rect ResizeElement(CustomThumb HitedThumb, double HorizontalChange, double VerticalChange)        {            #region Get Old Value            if (HitedThumb == null) return Rect.Empty;            Rect TargetActualBound = GetTargetActualBound();            double TopOld    = CorrectDoubleValue(TargetActualBound.Y);            double LeftOld   = CorrectDoubleValue(TargetActualBound.X);            double WidthOld  = CorrectDoubleValue(TargetActualBound.Width);            double HeightOld = CorrectDoubleValue(TargetActualBound.Height);            double TopNew    = TopOld;            double LeftNew   = LeftOld;            double WidthNew  = WidthOld;            double HeightNew = HeightOld;            #endregion            if (HitedThumb.DragDirection == DragDirection.TopLeft                || HitedThumb.DragDirection == DragDirection.MiddleLeft                || HitedThumb.DragDirection == DragDirection.BottomLeft)            {                ResizeFromLeft(DragHelperParent, LeftOld, WidthOld, TopOld, HeightOld, HorizontalChange, out LeftNew, out WidthNew, out HeightNew);            }            if (HitedThumb.DragDirection == DragDirection.TopLeft                || HitedThumb.DragDirection == DragDirection.TopCenter                || HitedThumb.DragDirection == DragDirection.TopRight)            {                ResizeFromTop(DragHelperParent, LeftOld, WidthOld, TopOld, HeightOld, VerticalChange, out TopNew, out HeightNew, out WidthNew);            }            if (HitedThumb.DragDirection == DragDirection.TopRight                || HitedThumb.DragDirection == DragDirection.MiddleRight                || HitedThumb.DragDirection == DragDirection.BottomRight)            {                ResizeFromRight(DragHelperParent, LeftOld, WidthOld,TopOld,HeightOld, HorizontalChange, out WidthNew, out HeightNew);            }            if (HitedThumb.DragDirection == DragDirection.BottomLeft                || HitedThumb.DragDirection == DragDirection.BottomCenter                || HitedThumb.DragDirection == DragDirection.BottomRight)            {                ResizeFromBottom(DragHelperParent, LeftOld, WidthOld, TopOld, HeightOld, VerticalChange, out HeightNew, out WidthNew);            }            WidthNew = WidthNew < 0 ? 0 : WidthNew;            HeightNew = HeightNew < 0 ? 0 : HeightNew;            this.Width = WidthNew;            this.Height = HeightNew;            Canvas.SetTop(this, TopNew);            Canvas.SetLeft(this, LeftNew);            return new Rect            {                X = LeftNew,                Y = TopNew,                Width = WidthNew,                Height = HeightNew            };        }        #endregion        #region Resize Base Methods        private static void CalcSize(double h,double w)        {        }        #region ResizeFromTop        private static void ResizeFromTop(FrameworkElement Parent, double LeftOld, double WidthOld, double TopOld, double HeightOld, double VerticalChange, out double TopNew, out double HeightNew, out double WidthNew)        {            double MiniHeight = 10;            double top = TopOld + VerticalChange;            TopNew = ((top + MiniHeight) > (HeightOld + TopOld)) ? HeightOld + TopOld - MiniHeight : top;            TopNew = TopNew < 0 ? 0 : TopNew;            HeightNew = HeightOld + TopOld - TopNew;            HeightNew = CorrectNewHeight(Parent, TopNew, HeightNew);            double tmpWidth = WidthOld;            if (LeftOld+HeightNew>Parent.ActualWidth)            {                HeightNew = tmpWidth;                WidthNew = tmpWidth;            }            else            {                WidthNew = HeightNew;                tmpWidth = HeightNew;            }        }        #endregion        #region ResizeFromLeft        private static void ResizeFromLeft(FrameworkElement Parent, double LeftOld, double WidthOld, double TopOld, double HeightOld, double HorizontalChange, out double LeftNew, out double WidthNew, out double HeightNew)        {            double MiniWidth = 10;            double left = LeftOld + HorizontalChange;            LeftNew = ((left + MiniWidth) > (WidthOld + LeftOld)) ? WidthOld + LeftOld - MiniWidth : left;            LeftNew = LeftNew < 0 ? 0 : LeftNew;            WidthNew = WidthOld + LeftOld - LeftNew;            WidthNew = CorrectNewWidth(Parent, LeftNew, WidthNew);            double tmpHeight = HeightOld;            if (TopOld + WidthNew > Parent.ActualHeight)            {                WidthNew = tmpHeight;                HeightNew = tmpHeight;            }            else            {                HeightNew = WidthNew;                tmpHeight = WidthNew;            }        }        #endregion        #region ResizeFromRight        private static void ResizeFromRight(FrameworkElement Parent, double LeftOld, double WidthOld, double TopOld, double HeightOld, double HorizontalChange, out double WidthNew, out double HeightNew)        {            if (LeftOld + WidthOld + HorizontalChange < Parent.ActualWidth)            {                WidthNew = WidthOld + HorizontalChange;            }            else            {                WidthNew = Parent.ActualWidth - LeftOld;            }            if (TopOld + HeightOld + HorizontalChange < Parent.ActualHeight)            {                HeightNew = HeightOld + HorizontalChange;            }            else            {                HeightNew = Parent.ActualWidth - TopOld;            }            WidthNew = WidthNew < 0 ? 0 : WidthNew;            if (WidthNew<HeightNew)            {                HeightNew = WidthNew;            }            else            {                WidthNew = HeightNew;            }        }        #endregion        #region ResizeFromBottom        private static void ResizeFromBottom(FrameworkElement Parent, double LeftOld, double WidthOld, double TopOld, double HeightOld, double VerticalChange, out double HeightNew, out double WidthNew)        {            if (TopOld + HeightOld + VerticalChange < Parent.ActualWidth)            {                HeightNew = HeightOld + VerticalChange;            }            else            {                HeightNew = Parent.ActualWidth - TopOld;            }            if (LeftOld + WidthOld + VerticalChange < Parent.ActualWidth)            {                WidthNew = WidthOld + VerticalChange;            }            else            {                WidthNew = Parent.ActualWidth - LeftOld;            }            HeightNew = HeightNew < 0 ? 0 : HeightNew;            if (WidthNew < HeightNew)            {                HeightNew = WidthNew;            }            else            {                WidthNew = HeightNew;            }        }        #endregion

现在回到我们自己的项目,把上面修改过的工程生成的dll引用进来,

<Canvas x:Name="canvas" Grid.Row="1" Width="500" Height="500">            <Rectangle Stroke="{StaticResource ButtonBackgroundBrush}"                        Fill="Transparent" StrokeThickness="2"                        Width="300" Height="300" Canvas.Left="100" Canvas.Top="100"                       UICommon:DragControlHelper.IsEditable="True"                        UICommon:DragControlHelper.IsSelectable="True"/>            <UICommon:DragControlHelper CornerWidth="6" Background="{StaticResource CancleButtonBackgroudBrush}" BorderBrush="{StaticResource ButtonBackgroundBrush}" DragChanging="DragControlHelper_DragChanging" DragCompleted="DragControlHelper_DragCompleted"/> </Canvas><StackPanel Grid.Row="2" HorizontalAlignment="Center" Orientation="Horizontal">            <Button Content="取消" Name="btnCancel" Width="62" Height="32"  Foreground="#FFFFFF" Style="{StaticResource CustomButtonStyle}"                        Margin="10" Click="btnCancel_Click" Background="{StaticResource CancleButtonBackgroudBrush}"></Button>            <Button Content="确定" Name="btnConfirm" Width="62" Height="32"  Foreground="#FFFFFF" Style="{StaticResource CustomButtonStyle}"                        Margin="10" Click="btnConfirm_Click" Background="{StaticResource ButtonBackgroundBrush}"></Button></StackPanel>
private void DragControlHelper_DragCompleted(object Sender, UICommon.Controls.DragChangedEventArgs e){            newBound = e.NewBound;//拖动完成后的新位置}private void btnConfirm_Click(object sender, RoutedEventArgs e){            Bitmap map = new Bitmap(choosedImagePath);            int mapHeight = map.Height;            int mapWidth = map.Width;//图片宽高            int actualHeight = 0;            int actualWidth = 0;//Canvas宽高            int offset = 1;            int left = 0;            int top = 0;            int x = (int)newBound.X;            int y = (int)newBound.Y;            if (mapHeight>mapWidth)//此时高度撑满500            {                actualHeight = 500;                actualWidth = 500 * mapWidth / mapHeight;                left = (500 - actualWidth) / 2;                x -=  left;                offset = mapHeight / 500d;//需要小数            }            else//此时宽度撑满500            {                actualWidth = 500;                actualHeight = 500 * mapHeight / mapWidth;                top = (500 - actualHeight) / 2;                y -= top;                offset = mapWidth / 500d;            }            //计算截图位置和大小            int w= (int)Math.Round(newBound.Width * offset, MidpointRounding.AwayFromZero);            int h = (int)Math.Round(newBound.Height * offset, MidpointRounding.AwayFromZero);            int startX=(int)Math.Round( x * offset,MidpointRounding.AwayFromZero);            int startY=(int)Math.Round( y * offset,MidpointRounding.AwayFromZero);            var resultMap = KiCut(map,startX,startY, w, h);            if (resultMap==null)            {                Alert alert = new Alert("图片裁剪失败,请重新选择图片。");                alert.Owner = this;                alert.ShowDialog();                return;            }            string savePath = NIM.ToolsAPI.GetLocalAppDataDir() + ConfigHelper.GetSettingStr("appDataDir") + "\\CutImageTemp\\";            if (!Directory.Exists(savePath))            {                Directory.CreateDirectory(savePath);            }            string name = "UserHead_" + DateTimeHelper.DatetimeConvertUnix() + ".jpg";            string path=savePath + name;            if (resultMap.Width!=300)//用户调整了裁剪框大小            {                Bitmap newImage = new Bitmap(300, 300, System.Drawing.Imaging.PixelFormat.Format24bppRgb);                Graphics g = Graphics.FromImage(newImage);                g.DrawImage(resultMap, 0, 0, 300, 300);                g.Dispose();                newImage.Save(path, ImageFormat.Jpeg);            }            else//直接保存            {                resultMap.Save(path, ImageFormat.Jpeg);            }}private static Bitmap KiCut(Bitmap b, int StartX, int StartY, int iWidth, int iHeight){            if (b == null)            {                return null;            }            try            {                Bitmap bmpOut = new Bitmap(iWidth, iHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);                Graphics g = Graphics.FromImage(bmpOut);                g.DrawImage(b, new System.Drawing.Rectangle(0, 0, iWidth, iHeight), new System.Drawing.Rectangle(StartX, StartY, iWidth, iHeight), GraphicsUnit.Pixel);                g.Dispose();                return bmpOut;            }            catch(Exception ex)            {                return null;            }}private void Window_Loaded(object sender, RoutedEventArgs e){            BinaryReader binReader = new BinaryReader(File.Open(choosedImagePath, FileMode.Open));            FileInfo fileInfo = new FileInfo(choosedImagePath);            byte[] bytes = binReader.ReadBytes((int)fileInfo.Length);            binReader.Close();            bitmap = new BitmapImage();            bitmap.BeginInit();            bitmap.StreamSource = new MemoryStream(bytes);            bitmap.EndInit();            ImageBrush brush = new ImageBrush(bitmap);            brush.Stretch = Stretch.Uniform;            this.canvas.Background = brush;}

稍微解释下代码,以上代码只是核心部分,另外newBound在构造函数中需要初始化new Rect(100, 100, 300, 300),因为我这边需要裁剪成300X300的图片,并且我在窗口的Load事件里给Canvas设置了背景图片,也就是用户选择的图片(路径存放在choosedImagePath变量中),并且设置其Stretch属性为Uniform,这样能保证图片在框中自适应正常显示。
虽然我这边默认框大小是300X300,但是一旦用户缩放了裁剪框,我们就需要对裁剪下来的图片进行放大或压缩,然后再做进一步操作(如转码base64、上传至服务器)。

原创粉丝点击