android pdf框架-11,查看图片

前10篇文章,9章关于pdf的,pdf解析后,里面也是有各种图片,于是利用pdf的view来展示图片,似乎也是个不错的想法.

android手机中的图片查看功能,有的可以展示,有的不能.比如华为,荣耀对大体积的png是可以显示的,小米是不显示,只有缩略图.

一张png50m大,比如清明上河图,原图是tiff,2g左右,这是adobe收购公司发明的,也是ps常用的.

对于tiff,华为,小米都显示不了,一方面可能是太大了,我有两张,一张300多m,一张2g,还有一张600多清明上河图原图扫描版的缩略图.

查阅了不少关于tiff的解析,目前没有提供好的android可以直接用的so库或lib库,TiffBitmapFactory有一个解析版,比较老了,它解析每次都是从头解析,虽然有area,但还是不行,对于300m的图片,解析一次要7s多,不管这块是多大,就算只有100*100像素也是如此.

tif库4.1.0才支持按需加载,我还没有实现这个功能,它是链表存储的,解析每一块,要从头开始查询fd的位置.

mupdf里面有一个load-tiff.c的解析代码,每一次加载也是7s左右,后面的就不用了,于是就想到,我把它直接展示不就行了.

原来的apk已经具备了查看图片的功能,但对于pdf的view来显示图片,有几个不好的方面:

  • 手势,pdfview目前不支持双击放大这些操作,是针对pdf的手势.
  • 缩放比例,当前没有设置很大的比例.
  • 加载速度,不支持多线程,mupdf的问题,暂时没有处理.

加载大图用的是subsampling-scale-image-view,这个在github上比较有名,它具备tile的按块加载,只需解码的时候 实现regiondecoder就可以了.

而且写的很好,文档注释很全.

代码:

要实现两个类,一个是pooled的解码类,一个是普通的解码.它首先会先用pooled类,去检测高与宽.

public class MupdfPooledImageRegionDecoder implements ImageRegionDecoder

public class MupdfImageDecoder implements ImageDecoder

官方的解码skia有三个类,这里不需要,两个就够了.

首先,对于体积超过10m的图片,获取exif可能会有内存不足的问题,所以subsampling-scale-image-view的源码,把相关获取exif的去了,去了以后,有些图片的方向不对,手机转一下就可以了.目前没有好的解决方案,这只是对于三星手机拍的照片有这个问题.

图片展示要分两类,一类是android支持的各种图片,一类是tiff.因为tiff通常是比较大的.比如地图,星空,航天这些领域.

具体实现:

if (path!!.endsWith("tif") || path.endsWith("tiff")) {binding.imageView.setBitmapDecoderFactory(CompatDecoderFactory(MupdfImageDecoder::class.java,Bitmap.Config.ARGB_8888))binding.imageView.setRegionDecoderFactory(CompatDecoderFactory(MupdfPooledImageRegionDecoder::class.java,Bitmap.Config.ARGB_8888))} else {binding.imageView.setBitmapDecoderFactory(CompatDecoderFactory(SkiaImageDecoder::class.java,Bitmap.Config.ARGB_8888))binding.imageView.setRegionDecoderFactory(CompatDecoderFactory(SkiaPooledImageRegionDecoder::class.java,Bitmap.Config.ARGB_8888))}

tiff的设置解码器与其它不同,针对小内存的手机,可以尝试rgb565,由于mupdf现在没有支持,所以都用argb_8888.

先看MupdfImageDecoder,这是解析整张图片的.当它判断高宽在一定范围内,不需要分块加载,就会调用这个解码

public Bitmap decode(Context context, @NonNull Uri uri) throws Exception {Bitmap bitmap = null;String uriString = uri.toString();if (uriString.startsWith(FILE_PREFIX)) {String path = uriString.substring(FILE_PREFIX.length());Log.e(TAG, "mupdf trying to open " + path);try {document = Document.openDocument(path);} catch (Exception e) {Log.e(TAG, e.getMessage());return null;}bitmap = renderBitmap();} else {}if (bitmap == null) {throw new RuntimeException("Mupdf image region decoder returned null bitmap - image format may not be supported");}return bitmap;}

这里先只支持path这种源.

剩下的解码,mupdf的解码就比较简单了

private Bitmap renderBitmap() {Page page = document.loadPage(0);Rect b = page.getBounds();float width = (b.x1 - b.x0);float height = (b.y1 - b.y0);Bitmap bitmap = BitmapPool.getInstance().acquire((int) width, (int) height);float zoom = 2f;Matrix ctm = new Matrix(zoom, zoom);RectI bbox = new RectI(page.getBounds().transform(ctm));float xscale = width / (bbox.x1 - bbox.x0);float yscale = height / (bbox.y1 - bbox.y0);ctm.scale(xscale, yscale);AndroidDrawDevice dev = new AndroidDrawDevice(bitmap, 0, 0, 0, 0,bitmap.getWidth(), bitmap.getHeight());page.run(dev, ctm, null);page.destroy();dev.close();dev.destroy();return bitmap;}

图片只有一张,所以直接加载第0页,然后计算高宽,解码.

MupdfPooledImageRegionDecoder,也只支持path.

public Point init(final Context context, @NonNull final Uri uri) throws Exception {this.context = context;this.uri = uri;initialiseDecoder();return this.imageDimensions;}

这里初始化解码器,然后返回图片的高宽维度.

private void initialiseDecoder() {String uriString = uri.toString();long fileLength = Long.MAX_VALUE;if (uriString.startsWith(FILE_PREFIX)) {String path = uriString.substring(FILE_PREFIX.length());File file = new File(path);if (file.exists()) {fileLength = file.length();}debug("mupdf trying to open " + path);try {decoder = Document.openDocument(path);} catch (Exception e) {debug(e.getMessage());return;}} else {}if (fileLength > SHOW_LOADING_SIZE) { //对于图片过大,需要一个等待状态,因为tiff很大,加载时间会比较长,可能要十几秒.showLoading();}this.fileLength = fileLength;page = decoder.loadPage(0); //这个速度很快,可以很快得到图片的高宽Rect b = page.getBounds();int width = (int) (b.x1 - b.x0);int height = (int) (b.y1 - b.y0);this.imageDimensions.set(width, height);hideLoading();}

上面初始化后,如果判断图片的高宽超过指定范围,它会启动分块加载,后续的分块加载全在这个pooled类里面实现.

public Bitmap decodeRegion(@NonNull android.graphics.Rect sRect, int sampleSize) {debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {if (null == decoder) {try {initialiseDecoder();} catch (Exception e) {}}}try {if (decoder != null) {Bitmap bitmap = renderBitmap(sRect, sampleSize);if (bitmap == null) {throw new RuntimeException("Mupdf image decoder returned null bitmap - image format may not be supported");}return bitmap;}} catch (Exception e) {debug(e.getMessage());}return null;}
public Bitmap renderBitmap(android.graphics.Rect cropBound, int sampleSize) {float scale = 1f / sampleSize; 缩放这里面要倒过来,其它没什么好说的了int pageW;int pageH;int patchX;int patchY;//如果页面的缩放为1,那么这时的pageW就是view的宽.pageW = (int) (cropBound.width() * scale);pageH = (int) (cropBound.height() * scale);patchX = (int) (cropBound.left * scale);patchY = (int) (cropBound.top * scale);Bitmap bitmap = BitmapPool.getInstance().acquire(pageW, pageH);if (null == page) {page = decoder.loadPage(0);}com.artifex.mupdf.fitz.Matrix ctm = new com.artifex.mupdf.fitz.Matrix(scale);AndroidDrawDevice dev = new AndroidDrawDevice(bitmap, patchX, patchY, 0, 0, pageW, pageH);try {page.run(dev, ctm, null);} catch (Exception e) {debug(e.getMessage());}dev.close();dev.destroy();return bitmap;}

剩下的实现接口的方法就不多说了.这样一个图片查看器就实现了.

具备了一般图片app的不具备,或者不太支持的功能.

  • 支持大的png,jpg,20mb,50mb,甚至更大的都可以快速打开.
  • 支持600mb(实测)的tiff图片的解析,初始化慢一些,后来就比较顺滑了.

到这里,打开300m的tiff是可以的,但是打开600m的会失败,打开2g的也是失败.因为mupdf的load-tiff.c解码前会经过它stream-read.c,这个里面判断了,如果超过一定值,会抛出异常,认为这是一个压缩炸弹,忽略.我把这个限制去了.600mb的图片没有问题.但2gb的依然不行.应该采用按需加载的形式.

虽然大图片能打开,但还是存在一些问题,tiff的解码不能按需加载,运行这个app后,会消耗大量的内存,导致其它app被回收了.但总算是能打开了.

关于查看tiff的apk,multi tiff view.apk这个虽然可以解析,但耗时也不短,另外移动缩放这些惨不忍睹, 它是目前我用的,唯一手机上能打开2gb的tiff的图片.

fast image,这个打开是各种慢,300mb就不行了.

bigtiff,这个开源的解析库是因为tiff的4g限制,以前tiff不能存储大于4gb的图片,后来改了,而bigtiff可以存储40,400gb的图片,官网上有介绍的,只是没有看到解析应用.如果能包装成jni可调用的,应该会是个不错的.

另一个服务器解码的应用非常不错.具体的名字下周去公司电脑上把相关链接都放上.这个可以解析大图,按需加载,由于对c++好久没弄,生疏了,想要弄到android上也不太容易.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/42310.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】string的底层原理及实现

文章目录 string类的存储结构默认成员函数构造函数析构函数拷贝构造函数赋值重载 容量操作size()capacity()reserve()resize()clear() 遍历与访问operator[ ]迭代器范围与for 增删查改push_back()pop_back()append()operatorinsert()erase()c_str()find()substr() 非成员函数op…

c#的List<T>的SelectMany 和Select

在C#中&#xff0c;List<T>&#xff08;以及任何实现了IEnumerable<T>的集合&#xff09;的Select和SelectMany扩展方法都是LINQ&#xff08;Language Integrated Query&#xff09;的一部分&#xff0c;用于对集合中的元素进行查询和转换。 尽管它们的作用有些相…

Virtualbox和ubuntu之间的关系

1、什么是ubuntu Ubuntu 是一个类似于 Windows 的操作系统&#xff0c;但它是基于 Linux 内核开发的开源操作系统 2、什么是Virtualbox VirtualBox 是一款虚拟机软件&#xff0c;使我们可以物理机上创建和运行虚拟机 也就是说,VirtualBox 提供了一个可以安装和运行其他操作系…

力扣考研经典题 反转链表

核心思想 头插法&#xff1a; 不断的将cur指针所指向的节点放到头节点之前&#xff0c;然后头节点指向cur节点&#xff0c;因为最后返回的是head.next 。 解题思路 1.如果头节点是空的&#xff0c;或者是只有一个节点&#xff0c;只需要返回head节点即可。 if (head null …

算法训练营day70

题目1&#xff1a;108. 冗余连接 (kamacoder.com) #include<iostream> #include<vector>using namespace std;int n; vector<int> father(10001, 0);void init() {for(int i 1;i < n;i) father[i] i; }int find(int u) {return u father[u] ? u : fa…

Echarts中的热力图和漏斗图(在Vue中使用热力图和漏斗图)

热力图 (Heatmap) Echarts的热力图用于展示两个维度数据矩阵中的值分布情况。它通过在平面上划分成多个矩形区域&#xff0c;并用不同的颜色填充这些区域来表示数据的大小或强度。颜色渐变从浅到深通常映射着数值从小到大&#xff0c;从而直观展示数据的集中程度和分布模式。热…

Mojolicious测试驱动开发:单元与集成测试的艺术

标题&#xff1a;Mojolicious测试驱动开发&#xff1a;单元与集成测试的艺术 Mojolicious是一个现代化的Perl Web开发框架&#xff0c;它不仅提供了强大的Web应用开发能力&#xff0c;还内置了丰富的测试工具来支持单元测试和集成测试。本文将深入探讨如何在Mojolicious中进行…

人工智能期末复习简答题

简答: 子句集的化简(1).消去连接词”—>”和”<—>” (2)减少否定符号的辖域 (3)对变元标准化 (4)化为前束范式 (5)消去存在量词 (6)化为Skolem标准形 (7)消去全称量词 (8)消去合取词 (9)更换变元名称 2.在状态空间搜索中,Open表与Close…

半同步主从复制

半同步主从复制的概念 半同步主从复制&#xff08;Semisynchronous Replication, SBR&#xff09;是MySQL数据库中的一种数据复制方式&#xff0c;它在异步复制的基础上增加了一定程度的同步性&#xff0c;旨在提高数据安全性&#xff0c;减少数据丢失的风险。 半同步主从复制…

LeetCode 3101.交替子数组计数:等差数列求和(较详题解)

【LetMeFly】3101.交替子数组计数&#xff1a;等差数列求和&#xff08;较详题解&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/count-alternating-subarrays/ 给你一个二进制数组 nums 。 如果一个子数组中 不存在 两个 相邻 元素的值 相同 的情况&a…

阶段三:项目开发---大数据开发运行环境搭建:任务8:安装配置Redis

任务描述 知识点&#xff1a;安装配置Redis 重 点&#xff1a; 安装配置Redis 难 点&#xff1a;无 内 容&#xff1a; Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可…

【C++:运算符重载】

运算符重载 特点&#xff1a; 函数名由operator运算符组成 注&#xff1a; 不能通过其他符号创建新的操作符&#xff0c;只能使用C/C语法存在的操作符重载操作符必须有一个类类型参数&#xff0c;原因&#xff1a;不能重载操作符改变内置类型的行为当类成员操作符重载时&#…

web自动化(五)上传文件

我们需要准备一个上传文件的html&#xff0c;创建a.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>文件上传示例</title> </head> <body><form action"/upload"…

电路基础知识汇总

1.0 串连&#xff0c;并联&#xff0c;混连 串联的定义 电路串联是一种电路元件的连接方式&#xff0c;其中各个元件沿着单一路径互相连接&#xff0c;形成一个连续的链。在串联电路中&#xff0c;每个节点最多只连接两个元件&#xff0c;这意味着电流只有一条路径可以通过整个…

Apache Seata Mac下的Seata Demo环境搭建

本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Mac下的Seata Demo环境搭建&#xff08;AT模式&#xff09; 前言 最近因为工作需要&#xf…

Git教程

文章目录 Git分布式版本控制工具版本控制器的方式常用命令远程仓库Tip Git分布式版本控制工具 ​ Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。 ​ Git是分布式的&#xff0c;Git不需要有中心服务器&#xff0c;我们每…

【感谢告知】本账号内容调整,聚焦于Google账号和产品的使用经验和问题案例分析

亲爱的各位朋友&#xff1a; 感谢您对本账号的关注和支持&#xff01; 基于对朋友们需求的分析和个人兴趣的转变&#xff0c;该账号从今天将对内容做一些调整&#xff0c;有原来的内容改为Google&#xff08;谷歌&#xff09;账号和产品的使用经验&#xff0c;以及相关问题的…

24西安电子科技大学经济与管理学院—考研录取情况

24西安电子科技大学—经理与管理学院—考研录取统计 01、经理与管理学院各个方向 02、24经济与管理近三年复试分数线对比 1、经管院24年院线相对于23年院线普遍下降2-15分&#xff0c;个别专业上涨4-10分。 2、经管院应用经济学2024年院线350分&#xff1b;管理科学与工程院线…

java join与yield方法

join() join() 方法的主要作用是使当前线程&#xff08;调用 join() 方法的线程&#xff09;等待目标线程完成执行。当目标线程执行完毕后&#xff0c;当前线程才会继续执行。 代码示例&#xff1a; public class JoinExample {public static void main(String[] args) {Thr…

保研复习 | 数据结构

目录 CH1 绪论☆ 数据项、数据元素、数据结构☆ 逻辑结构和存储结构的区别☆ 顺序存储结构和链式存储结构的比较☆ 算法的重要特性☆ 算法的复杂度 CH2 线性表☆ 单链表 CH3 栈、队列和数组☆ 栈和堆是什么&#xff1f;☆ 栈在括号匹配中的应用☆ 栈在表达式求值中的应用☆ …