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;
}
}
}
不一样的地方就是我遇到的坑,真的好坑啊,没有人和我遇到一样的问题吗?我就看到我有这个问题。。。。。
在切割宽度小于高度的时候,就是会有一个异常,改掉之后就好啦。
其他关于放大缩小后坐标的计算的代码就不贴了。其实也算了好久,终于算对了。为了省事,我把鼠标所在的位置作为放大后的局部图片的左上角坐标了,比中心坐标稍微少算点东西。
本来呢,事情到这边就应该告一段落了,放大功能已经实现了,但是问题是,其实我的需求不仅仅是把图片放大,图片上可能会有一些新画上去的东西,这个东西有可能会需要擦掉,
上面这种操作只将图片显示在了放大框内,而我后期添加的那些线条啊文字啊,全都没有显示,怎么办呢?第一种方法的好处就是它直接把这个控件里面的所有东西都放大了,而第二种放大仅仅把控件里的图片放大了。唉,还是没有解决问题呀。
WPF实现放大镜的更多相关文章
- WPF设置VistualBrush的Visual属性制作图片放大镜效果
原文:WPF设置VistualBrush的Visual属性制作图片放大镜效果 效果图片:原理:设置VistualBrush的Visual属性,利用它的Viewbox属性进行缩放. XAML代码:// ...
- WPF放大镜效果
在做WPF项目中,不止两个项目需要有放大镜功能. 第一个项目是一个手术室的远程示教系统,主要是为了方便专家演示病症时,可以放大图片上的某些部位. 第二个项目是一个工厂的MES项目,其中有个功能是质量预 ...
- WPF中利用RadialGradient模拟放大镜效果
原文:WPF中利用RadialGradient模拟放大镜效果 --------------------------------------------------------------------- ...
- 《深入浅出WPF》笔记——绘画与动画
<深入浅出WPF>笔记——绘画与动画 本篇将记录一下如何在WPF中绘画和设计动画,这方面一直都不是VS的强项,然而它有一套利器Blend:这方面也不是我的优势,幸好我有博客园,能记录一 ...
- WPF 学习笔记 路由事件
1. 可传递的消息: WPF的UI是由布局组建和控件构成的树形结构,当这棵树上的某个节点激发出某个事件时,程序员可以选择以传统的直接事件模式让响应者来响应之,也可以让这个事件在UI组件树沿着一定的方向 ...
- WPF 如何画出1像素的线
如何有人告诉你,请你画出1像素的线,是不是觉得很简单,实际上在 WPF 上还是比较难的. 本文告诉大家,如何让画出的线不模糊 画出线的第一个方法,创建一个 Canvas ,添加一个线 界面代码 < ...
- WPF自学入门(三)WPF路由事件之内置路由事件
有没有想过在.NET中已经有了事件机制,为什么在WPF中不直接使用.NET事件要加入路由事件来取代事件呢?最直观的原因就是典型的WPF应用程序使用很多元素关联和组合起来,是否还记得在WPF自学入门(一 ...
- Silverlight/WPF 系列汇总
Silverlight 解谜游戏系列 -- Silverlight 3 · Silverlight 解谜游戏 之一 新建项目 · Silverlight 解谜游戏 之二 创建题板 · Silverli ...
- WPF 使用 WindowChrome,在自定义窗口标题栏的同时最大程度保留原生窗口样式(类似 UWP/Chrome)
WPF 自定义窗口样式有多种方式,不过基本核心实现都是在修改 Win32 窗口样式.然而,Windows 上的应用就应该有 Windows 应用的样子嘛,在保证自定义的同时也能与其他窗口样式保持一致当 ...
随机推荐
- Linux 中 Xampp 的 https 安全证书配置
博客地址:http://www.moonxy.com 一.前言 HTTP 协议是不加密传输数据的,也就是用户跟你的网站之间传递数据有可能在途中被截获,破解传递的真实内容,所以使用不加密的 HTTP 的 ...
- Disruptor原理探讨
之前谈到了在我的项目里用到了Disruptor,因为对它了解不足的原因,才会引发之前的问题,因此,今天特意来探讨其原理. 为什么采用Disruptor 先介绍一下我的这个服务.这个服务主要是作为游戏服 ...
- (5)Makefile详解
Makefile是一个自动化的编译工具,关系到整个工程的编译规则,极大的提高了软件开发的效率. (1)Makefile的编译规则 //Makefile 也可以写作 makefile1 ...
- SpringBootSecurity学习(04)网页版登录其它授权和登录处理
其它授权配置 security的配置类中,对所有路径进行了统一授权配置.但是有的内容我们也需要让未登录游客有权限访问,比如js,css等静态文件,还有一些宣传页面等等.这些路径可以单独配置: 我们来试 ...
- SpringBootSecurity学习(05)网页版登录内存中配置默认用户
默认用户 前面的例子中我们使用的都是配置文件中配置好的默认用户: 除了可以配置账号密码,还可以在配置文件中配置角色: 这个角色是后面实现权限过滤的重要内容,后面会重点讨论. 在内存中配置默认用户 这样 ...
- 基于WeChat的消息存储备份、远程控制、小功能项目开源分享计划
WeChat+ 关于该项目 起源 该项目的起因是一个比较程(老)序(油)猿(条)的理由,有一天我发现我下班时间比较早,有点尴尬,但是又不想没事干还坐在公司,那么如何解决我的问题呢,初步想法是远程控制电 ...
- 一个低级错误引发Netty编码解码中文异常
前言 最近在调研Netty的使用,在编写编码解码模块的时候遇到了一个中文字符串编码和解码异常的情况,后来发现是笔者犯了个低级错误.这里做一个小小的回顾. 错误重现 在设计Netty的自定义协议的时候, ...
- 一文彻底理解Redis序列化协议,你也可以编写Redis客户端
前提 最近学习Netty的时候想做一个基于Redis服务协议的编码解码模块,过程中顺便阅读了Redis服务序列化协议RESP,结合自己的理解对文档进行了翻译并且简单实现了RESP基于Java语言的解析 ...
- Django学习之文件上传
就这么六步! 一.settings配置文件中配置 MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'medias').replace ...
- Spring Boot 2.X(七):Spring Cache 使用
Spring Cache 简介 在 Spring 3.1 中引入了多 Cache 的支持,在 spring-context 包中定义了org.springframework.cache.Cache 和 ...