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 应用的样子嘛,在保证自定义的同时也能与其他窗口样式保持一致当 ...
随机推荐
- IPMI在linux下常用命令
ipmitool lan print 1 ipmitool lan set 1 ipaddr 192.168.0.12 ipmitool lan set 1 netmask 255.255.255.0 ...
- Vue基础项目配置
一,使用Vuejs搭建项目需要一些基础配置,这样能使的编程过程事半功倍 1.首先下载nodejs,然后使用nodejs使用NPM命令下载VueCli3.0以上的Vue脚手架.通过脚手架可以使用Vue ...
- Eclipse的egit插件冲突合并方法
Eclipse有一个git的插件叫EGit,用于实现本地代码和远程代码对比.合并以及提交.但是在本地代码和远程代码有冲突的时候,EGit的处理方案还是有点复杂.今天就彻底把这些步骤给理清楚,并公开让一 ...
- selenium自动化测试-浏览器基本操作
webdriver 通过协议和接口发现DOM中的元素,并实现控制浏览器的行为,例如打开浏览器.控制浏览器大小. 浏览器刷新及浏览器前进.后退等,接下来介绍浏览器的一些基本操作. 1.启动浏览器 dri ...
- Spring Boot 简介与入门(2.1.6版)
Spring Boot 2.1.6 版 与时俱进是每一个程序员都应该有的意识,当一个Java程序员在当代遍布的时候,你就该意识到能多学点什么.可观的是后端的框架是稳定的,它们能够维持更久的 ...
- android实现emoji输入
学android也有一段时间, 一直都是自己摸索, 各种上网查资料, 也明白了不能一味去索取有时间也要分享一些自己的心得 . 最近几天都在写关于android emoji输入的小例子,网上有不少源码还 ...
- 正睿OI DAY3 杂题选讲
正睿OI DAY3 杂题选讲 CodeChef MSTONES n个点,可以构造7条直线使得每个点都在直线上,找到一条直线使得上面的点最多 随机化算法,check到答案的概率为\(1/49\) \(n ...
- php基础——语法、变量
一.php语法: 1.php语言需要写在<?php ?>标签里面 2.php语言每行结束需要使用:作为结束符 3.php是一门弱语言,不要求先声明变量 4.可嵌套在HTML和js语言中 ...
- python 写入txt的新方法
最新发现有新方法可以对txt等进行操作,比较有意思,之前没见过,故记录下 传统方法 with open(ur'D:\Desktop\a123.txt', 'a') as f: #以写的方式打开 f.w ...
- 让视频丝滑流畅——N/A通用补帧傻瓜解决方案
补帧就是字面意思,把24帧的视频通过算法即时补偿到更高的帧数,获得更优秀的观感体验 索尼大法brivia电视的中高端产品线中的motionflow技术,都可以实现硬件补帧,只需要把动态打开,相应的画面 ...