WPF头像裁剪

需求很常见,就是用户上传头像前进行固定大小的裁剪。
百度一番,找到几个差不多的,
其一 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、上传至服务器)。

猜你喜欢

转载自blog.csdn.net/leebin_20/article/details/73648956