zoukankan      html  css  js  c++  java
  • WPF实现放大镜

    这是一个之前遗留的问题。wpf里面有很多很多的东西,我以前用的真的只是其中很小的一个角落都不到。

    需求背景:图片来源于相机拍摄,由于对像素要求,拍出来的图像素比较高,原图尺寸为3072*2048,以目前的电脑屏幕,很多都是显示不了这么大的,比如1440 * 900啊,1280 * 1024啊这种的,
    在全屏情况下图像的显示都是很小的,图片细节看不清。
    为了满足看清图片细节的需求,我们想要做一个放大镜。windows系统下有自带的放大镜功能,在一定程度上可以满足需求,但是实际情况是,将宽度为三千多的图放到一千多的屏幕上,图像被压缩了,
    损失了很多点,这种情况下,即使使用了放大镜,也只能实现将1个像素放大到2个像素那么大,而那些损失的点找不回来了。
    当时做的时候,受放大镜这个名词的束缚,在网络上搜索wpf放大镜的实现,这种情况下,找到了一个类似于实现windows放大镜功能的方法,勉(ying)为(fu)其(jiao)难(chai),就拿来用了。
    有一定的效果,但是我心里知道,这个是不行的。
    以后说不定还会有用,所以代码还是贴在这里,代码来源已经不记得了(以后找到补上地址),反正是别人那边搬运来的,我记得当时是直接下载了一份代码,那份代码里面太多修饰性的东西了,我不需要就全都去掉了。
    主要是使用VisualBrush来实现的。
    这是我调整过之后的源码,这份代码勉为其难也算是我部分原创吧。
    页面部分:

            <Canvas Grid.Row="1" Grid.Column="0" VerticalAlignment="Top" HorizontalAlignment="Left">
                <Canvas Name="magnifierCanvas" IsHitTestVisible="False" Visibility="{Binding ElementName=checkEnableMagnifier,Path=IsChecked,Converter={StaticResource BoolToVis}}">
                    <Rectangle Width="149" Height="149" Name="magnifierEllipse" StrokeThickness="1">
                        <Rectangle.Fill>
                            <VisualBrush ViewboxUnits="Absolute" Viewbox="0,0,149,149" ViewportUnits="RelativeToBoundingBox" Viewport="0,0,1,1"/>
                        </Rectangle.Fill>
                        <Rectangle.Stroke>
                            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                <GradientStop Offset="0" Color="#AAA" />
                                <GradientStop Offset="1" Color="#111" />
                            </LinearGradientBrush>
                        </Rectangle.Stroke>
                    </Rectangle>
                    <Line X1="73" X2="77" Y1="75" Y2="75" StrokeThickness="1" Stroke="Cyan" Visibility="Visible"/>
                    <Line X1="75" X2="75" Y1="73" Y2="77" StrokeThickness="1" Stroke="Cyan" Visibility="Visible"/>
                </Canvas>
            </Canvas>
    

    后台代码:

    private double _delta = 0;
    
            private void Image_MouseWheel(object sender, MouseWheelEventArgs e)
            {
                if (_delta <= 0 && e.Delta < 0) return;
                if (_delta >= 80 && e.Delta > 0) return;
                _delta += (double)e.Delta / 10;
                if (_delta > 130) _delta = 130;
    
                Point pos = e.MouseDevice.GetPosition(image);
    
                if (magnifierEllipse != null)
                {
                    Rect viewBox = vb.Viewbox;
                    double val = (double)magnifierEllipse.Width - _delta;
                    viewBox.Width = val;
                    viewBox.Height = val;
    
                    double xoffset = viewBox.Width / 2.0;
                    double yoffset = viewBox.Height / 2.0;
                    viewBox.X = pos.X - xoffset;
                    viewBox.Y = pos.Y - yoffset;
                    vb.Viewbox = viewBox;
    
                    Canvas.SetLeft(magnifierCanvas, pos.X - magnifierEllipse.Width / 2);
                    Canvas.SetTop(magnifierCanvas, pos.Y - magnifierEllipse.Height / 2);
                }
            }
    

    当时遇到了一个小小的坑,Image控件必须放在一个和这个Canvas一样大的容器里面,不然显示的时候总是会错位。
    完整代码就不贴了,这是一个大程序的一小部分。

    昨天在看一份图像处理的资料的时候,一开始就提到了压缩图片尺寸导致细节丢失的问题,我突然意识到我之前的那种做法特别的坑,今天重新想了一下,我为什么要抓住放大镜这一点不放呢。
    也不是,其实我之前的问题在于我要怎么在Image控件上显示某张图片上的指定偏移量指定宽高的图片区域,一直没有找到这个方法。
    今天突然醒悟了,我傻了吧唧的,其实可以使用图片裁切的方法,把原图在指定位置指定大小的内容裁切下来显示不就好了么。。。。

    代码大部分也是来源网络,这个方法感觉还可以,我之前用了另一种方法,内存没管理好,竟然爆表了。。。。。
    源代码来源:https://blog.csdn.net/qq_18995513/article/details/67637521
    下面贴的代码是我根据实际情况微调之后的,更符合我现阶段的情况。
    找找和源代码哪里不一样?

    // 图像工具类
            public static class SystemUtils
            {
                /// <summary>
                /// 切图
                /// </summary>
                /// <param name="bitmapSource">图源</param>
                /// <param name="cut">切割区域</param>
                /// <returns></returns>
                public static BitmapSource CutImage(BitmapSource bitmapSource, Int32Rect cut)
                {
                    //计算Stride
                    int max = cut.Width > cut.Height ? cut.Width : cut.Height;
                    //var stride = bitmapSource.Format.BitsPerPixel * cut.Width / 8;
                    var stride = bitmapSource.Format.BitsPerPixel * max / 8;
                    //声明字节数组
                    byte[] data = new byte[stride * max];
                    //调用CopyPixels
                    bitmapSource.CopyPixels(cut, data, stride, 0);
    
                    return BitmapSource.Create(cut.Width, cut.Height, 0, 0, PixelFormats.Bgr32, null, data, stride);
                }
    
                // ImageSource --> Bitmap
                public static System.Drawing.Bitmap ImageSourceToBitmap(ImageSource imageSource)
                {
                    BitmapSource m = (BitmapSource)imageSource;
    
                    System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(m.PixelWidth, m.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
    
                    System.Drawing.Imaging.BitmapData data = bmp.LockBits(
                    new System.Drawing.Rectangle(System.Drawing.Point.Empty, bmp.Size), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
    
                    m.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride); bmp.UnlockBits(data);
    
                    return bmp;
                }
    
                // Bitmap --> BitmapImage
                public static BitmapImage BitmapToBitmapImage(Bitmap bitmap)
                {
                    using (MemoryStream stream = new MemoryStream())
                    {
                        bitmap.Save(stream, ImageFormat.Bmp);
    
                        stream.Position = 0;
                        BitmapImage result = new BitmapImage();
                        result.BeginInit();
                        // According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed."
                        // Force the bitmap to load right now so we can dispose the stream.
                        result.CacheOption = BitmapCacheOption.OnLoad;
                        result.StreamSource = stream;
                        result.EndInit();
                        result.Freeze();
    
                        return result;
                    }
                }
        }
    

    不一样的地方就是我遇到的坑,真的好坑啊,没有人和我遇到一样的问题吗?我就看到我有这个问题。。。。。
    在切割宽度小于高度的时候,就是会有一个异常,改掉之后就好啦。
    其他关于放大缩小后坐标的计算的代码就不贴了。其实也算了好久,终于算对了。为了省事,我把鼠标所在的位置作为放大后的局部图片的左上角坐标了,比中心坐标稍微少算点东西。


    本来呢,事情到这边就应该告一段落了,放大功能已经实现了,但是问题是,其实我的需求不仅仅是把图片放大,图片上可能会有一些新画上去的东西,这个东西有可能会需要擦掉,
    上面这种操作只将图片显示在了放大框内,而我后期添加的那些线条啊文字啊,全都没有显示,怎么办呢?第一种方法的好处就是它直接把这个控件里面的所有东西都放大了,而第二种放大仅仅把控件里的图片放大了。唉,还是没有解决问题呀。

  • 相关阅读:
    新手讲排序:希尔排序
    安装部署VMware vSphere 5.5文档 (6-6) 集群和vMotion
    安装部署VMware vSphere 5.5文档 (6-5) 安装配置vCenter
    同步VDP时间
    vdp配置
    python定义常量
    OpenStack Keystone V3 简介
    nginx + uswgi +django
    安装系统
    Standard NSD file
  • 原文地址:https://www.cnblogs.com/yutou2016/p/11732559.html
Copyright © 2011-2022 走看看