2311skia,08解码区域

解码区域

1,抽象

1,图片很大时,解码速度缓慢,占用内存很高,并且,图片超过一定尺寸时,无法上传和显示纹理(这跟GPU能力有关,一般的GPU8192*8192).这时只好下采样,但会牺牲图片显示质量.

2,-对图库等,需要清晰浏览图片的应用,不可能设置下采样率来解决这一问题,因此,Google加入了解码区域功能,这样可从原始图片文件中,解码出一部分完整区域图片内容.

3,解码区域难点主要在,定位像素区域所对应的文件位置,需要图像编码有一定的连续性,所幸,主流图像格式都是如此.

4,目前解码区域主要实现了png,jpeg,webp类型图片的支持.本篇,介绍最常用的jpeg格式的解码区域实现.

2,解码区域总流程

框架侧创建BitmapRegionDecoder时,创建对应类型的SkImageDecoder来扫描全文件,调用其onBuildTileIndex方法构建tileIndex,嵌入在其关联的SkImageDecoder之中,在后续调用decodeRegion时,使用SkImageDecoderonDecodeSubset方法来解码区域.

3,Jpeg的解码区域

#ifdef SK_BUILD_FOR_ANDROID
bool SkJPEGImageDecoder::onBuildTileIndex(SkStreamRewindable* stream, int *width, int *height) {SkAutoTDelete<SkJPEGImageIndex> imageIndex(SkNEW_ARGS(SkJPEGImageIndex, (stream, this)));jpeg_decompress_struct* cinfo = imageIndex->cinfo();skjpeg_error_mgr sk_err;set_error_mgr(cinfo, &sk_err);//在调用此`setjmp`前,实例化所有对象,以便在错误时正确清理它们.if (setjmp(sk_err.fJmpBuf)) {return false;}//创建,来创建/构建`HuffmanIndex`的`cinfo`if (!imageIndex->initializeInfoAndReadHeader()) {return false;}if (!imageIndex->buildHuffmanIndex()) {return false;}//析构,来创建/构建霍夫曼索引的`cinfo`imageIndex->destroyInfo();//初化`解码器`到`图像解码`模式if (!imageIndex->initializeInfoAndReadHeader()) {return false;}//`FIXME`:这设置了`cinfo->out_color_space`,稍后可能会根据`onDecodeSubset`中的配置更改它.这应该没问题,因为在(它调用`jinit_color_deconverter`时)更改它后,`jpeg_init_read_tile_scanline`调用再次检查`out_color_space`.(void) this->getBitmapColorType(cinfo);turn_off_visual_optimizations(cinfo);//不是`jpeg_start_decompress()`,而开始平铺解压缩if (!imageIndex->startTileDecompress()) {return false;}SkASSERT(1 == cinfo->scale_num);fImageWidth = cinfo->output_width;fImageHeight = cinfo->output_height;if (width) {*width = fImageWidth;}if (height) {*height = fImageHeight;}SkDELETE(fImageIndex);fImageIndex = imageIndex.detach();return true;
}
bool SkJPEGImageDecoder::onDecodeSubset(SkBitmap* bm, const SkIRect& region) {if (NULL == fImageIndex) {return false;}jpeg_decompress_struct* cinfo = fImageIndex->cinfo();SkIRect rect = SkIRect::MakeWH(fImageWidth, fImageHeight);if (!rect.intersect(region)) {//如果请求区域,完全在`图像`外,则返回`false`return false;}skjpeg_error_mgr errorManager;set_error_mgr(cinfo, &errorManager);if (setjmp(errorManager.fJmpBuf)) {return false;}int requestedSampleSize = this->getSampleSize();cinfo->scale_denom = requestedSampleSize;set_dct_method(*this, cinfo);const SkColorType colorType = this->getBitmapColorType(cinfo);adjust_out_color_space_and_dither(cinfo, colorType, *this);int startX = rect.fLeft;int startY = rect.fTop;int width = rect.width();int height = rect.height();jpeg_init_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &startX, &startY, &width, &height);int skiaSampleSize = recompute_sampleSize(requestedSampleSize, *cinfo);int actualSampleSize = skiaSampleSize * (DCTSIZE / cinfo->min_DCT_scaled_size);SkScaledBitmapSampler sampler(width, height, skiaSampleSize);SkBitmap bitmap;//假设`A8`位图不透明,以避免检查每个单独的像素.它不太可能是不透明的,因为不透明的`A8`位图不会很有趣.否则,`jpeg`图像是不透明的.bitmap.setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), colorType, kAlpha_8_SkColorType == colorType ?  kPremul_SkAlphaType : kOpaque_SkAlphaType));//提前检查是否可交换`(dest,src)`.如果是,则坚持使用`AllocPixelRef`,因为交换时它更便宜.如果否,则使用`alloc`来分配像素以避免垃集.  int w = rect.width() / actualSampleSize;int h = rect.height() / actualSampleSize;bool swapOnly = (rect == region) && bm->isNull() &&(w == bitmap.width()) && (h == bitmap.height()) &&((startX - rect.x()) / actualSampleSize == 0) &&((startY - rect.y()) / actualSampleSize == 0);if (swapOnly) {if (!this->allocPixelRef(&bitmap, NULL)) {return return_false(*cinfo, bitmap, "allocPixelRef");}} else {if (!bitmap.allocPixels()) {return return_false(*cinfo, bitmap, "allocPixels");}}SkAutoLockPixels alp(bitmap);
#ifdef ANDROID_RGB/*如果可能,请短路`SkScaledBitmapSampler`,因为这会显著提高性能.*/if (skiaSampleSize == 1 &&((kN32_SkColorType == colorType && cinfo->out_color_space == JCS_RGBA_8888) ||(kRGB_565_SkColorType == colorType && cinfo->out_color_space == JCS_RGB_565))){JSAMPLE* rowptr = (JSAMPLE*)bitmap.getPixels();INT32 const bpr = bitmap.rowBytes();int rowTotalCount = 0;while (rowTotalCount < height) {int rowCount = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);//如果`rowCount==0`,则没有得到扫描行,所以中止.`onDecodeSubset()`依赖`onBuildTileIndex()`,它需要完整的映射才能成功.  if (0 == rowCount) {return return_false(*cinfo, bitmap, "read_scanlines");}if (this->shouldCancelDecode()) {return return_false(*cinfo, bitmap, "shouldCancelDecode");}rowTotalCount += rowCount;rowptr += bpr;}if (swapOnly) {bm->swap(bitmap);} else {cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(), region.width(), region.height(), startX, startY);}return true;}
#endif//检查支持的格式SkScaledBitmapSampler::SrcConfig sc;int srcBytesPerPixel;if (!get_src_config(*cinfo, &sc, &srcBytesPerPixel)) {return return_false(*cinfo, *bm, "jpeg colorspace");}if (!sampler.begin(&bitmap, sc, *this)) {return return_false(*cinfo, bitmap, "sampler.begin");}SkAutoMalloc  srcStorage(width * srcBytesPerPixel);uint8_t* srcRow = (uint8_t*)srcStorage.get();//可能跳过初始行`[sampler.srcY0]`if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcY0())) {return return_false(*cinfo, bitmap, "skip rows");}//现在遍历扫描行,直到`y==bitmap->height()-1`for (int y = 0;; y++) {JSAMPLE* rowptr = (JSAMPLE*)srcRow;int row_count = jpeg_read_tile_scanline(cinfo, fImageIndex->huffmanIndex(), &rowptr);//如果`row_count==0`,则没有得到扫描行,所以中止.`onDecodeSubset()`依赖`onBuildTileIndex()`,它需要完整的映射才能成功.  if (0 == row_count) {return return_false(*cinfo, bitmap, "read_scanlines");}if (this->shouldCancelDecode()) {return return_false(*cinfo, bitmap, "shouldCancelDecode");}if (JCS_CMYK == cinfo->out_color_space) {convert_CMYK_to_RGB(srcRow, width);}sampler.next(srcRow);if (bitmap.height() - 1 == y) {//大功告成break;}if (!skip_src_rows_tile(cinfo, fImageIndex->huffmanIndex(), srcRow, sampler.srcDY() - 1)) {return return_false(*cinfo, bitmap, "skip rows");}}if (swapOnly) {bm->swap(bitmap);} else {cropBitmap(bm, &bitmap, actualSampleSize, region.x(), region.y(),region.width(), region.height(), startX, startY);}return true;
}
#endif

onBuildTileIndex时,创建了huffman_index,其中的内容主要是一系列,记录每个对应偏移huffman_offset.
解码时,先移到对应位置,然后解出像素.

GLOBAL(JDIMENSION)
jpeg_read_tile_scanline (j_decompress_ptr cinfo, huffman_index *index, JSAMPARRAY scanlines)
{//计算`iMCU`的边界int lines_per_iMCU_row = cinfo->max_v_samp_factor * DCTSIZE;int lines_per_iMCU_col = cinfo->max_h_samp_factor * DCTSIZE;int sample_size = DCTSIZE / cinfo->min_DCT_scaled_size;JDIMENSION row_ctr = 0;if (cinfo->progressive_mode) {(*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, 1);} else {if (cinfo->output_scanline % (lines_per_iMCU_row / sample_size) == 0) {//读头设置到下一个`iMCU`行int iMCU_row_offset = cinfo->output_scanline / (lines_per_iMCU_row / sample_size);int offset_data_col_position = cinfo->coef->MCU_column_left_boundary /index->MCU_sample_size;huffman_offset_data offset_data =index->scan[0].offset[iMCU_row_offset][offset_data_col_position];(*cinfo->entropy->configure_huffman_decoder) (cinfo, offset_data);}(*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, 1);}cinfo->output_scanline += row_ctr;return row_ctr;
}

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

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

相关文章

The Matrix format of Least Square Method (LMS)

I. Cost function For the cost function J ∑ i 1 n ∣ ∣ y i − x i T θ ∣ ∣ 2 (1) J \sum_{i1}^n || y_i - x_i^T \theta ||^2 \tag{1} Ji1∑n​∣∣yi​−xiT​θ∣∣2(1) where θ ( m 1 ) \theta (m\times 1) θ(m1) is the unknow parameters, x i T ( 1 m…

基于官方YOLOv4-u5【yolov5风格实现】开发构建目标检测模型超详细实战教程【以自建缺陷检测数据集为例】

本文是关于基于YOLOv4开发构建目标检测模型的超详细实战教程,超详细实战教程相关的博文在前文有相应的系列,感兴趣的话可以自行移步阅读即可: 《基于yolov7开发实践实例分割模型超详细教程》《YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程》《DETR (DE…

内衣洗衣机怎么选?内衣洗衣机便宜好用的牌子推荐

相信不少用户并不太在意衣服和内衣裤裤能不能同时洗&#xff0c;每次清洗都是把内衣裤与其他衣服一起放入洗衣机清洗&#xff0c;其实内衣裤不能直接跟大件的衣物一起放入洗衣机洗的&#xff0c;很容易会造成我们皮肤的瘙痒&#xff0c;我们大部分时间都在户外&#xff0c;暴露…

python 基于opencv和face_recognition的人脸识别

python 基于opencv和face_recognition的人脸识别 代码如下&#xff1a; 使用一个photos存放你需要识别的照片&#xff0c;注意一个人一张就行 然后通过下面代码注册用户&#xff0c;之后启动程序&#xff0c;就会调用摄像头进行识别了。 AddPhoto(“发哥”, “./photos/fag…

Java实现集合和Excel文件相互转换

目录 一、集合转化为Excel文件二、Excel文件转化为集合 一、集合转化为Excel文件 效果如下&#xff0c;是将集合转化为Excel文件&#xff0c;Excel包含合并单元格。 实体类&#xff1a; Data public class ClassGrade {/** 年级 */private String grade;/** 班主任 */privat…

纯新手发布鸿蒙的第一个java应用

第一个java开发鸿蒙应用 1.下载和安装华为自己的app开发软件DevEco Studio HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 2.打开IDE新建工程&#xff08;当前用的IDEA 3.1.1 Release&#xff09; 选择第一个&#xff0c;其他的默认只能用(API9)版本&#xff0c;…

ConcurrentHashMap的数据结构+以及各个版本之间的区别

ConcurrentHashMap 1.7与1.8的区别 1、锁结构不同 2、put的流程不同 3、size的计算方式不同(1.8使用的使用basecell[]计算&#xff0c;有点类似于LongAdder&#xff0c;1.7使用三级通缉判断是否一样&#xff0c;不一样通过分段式加锁再求和) 4、数据结构不同&#xff0c;1.6 Re…

tauri中使用rust调用动态链接库例子(使用libloading库和libc库)

前言 当前采用桌面端框架位tauri&#xff0c;现在需要调用读卡器等硬件设备&#xff0c;硬件厂商提供了32位的动态链接库&#xff0c;现在记录例子&#xff0c;需要注意的点是使用libloading库和libc库&#xff0c; [package] name "yyt-device-rust" version &q…

企业数据建设再思考

近些年企业数据建设进入深水区&#xff0c;近六成受访企业高管表示在未来一到两年会增加数据建设的投入。 数据建设得分最明显的三项指标分别为&#xff1a;开拓数据变现模式、实现数据流与业务流程在各部门间的无缝衔接、搭建基于数据分析的决策体系与管控体系。 因此&#…

文件太大彻底删除怎么找回?速来get这3个有效方法!

“我的电脑里保存了一些很大的文件&#xff0c;由于太占内存了&#xff0c;我就将它们删除掉了&#xff0c;但是我发现有些比较重要的文件也一起被删除了。这可怎么办呢&#xff1f;有什么方法可以找回这些删除文件吗&#xff1f;” 在日常使用电脑的过程中&#xff0c;可能很多…

角色管理--专利软著产品负责人岗

研发组织管理--角色管理--专利软著产品负责人岗 定位 知识产权的站岗人&#xff0c;牵头推动软著落地及专利沉淀 所需资质 熟悉专利和软著的价值&#xff0c;撰写方法和申请流程有较强的文档撰写整合及驱动大家共同完成文档的能力对整体产品框架内的价值点&#xff0c;具有良…

自动驾驶学习笔记(十二)——定位技术

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo Beta宣讲和线下沙龙》免费报名—>传送门 文章目录 前言 卫星定位 RTK定位 IMU定位 GNSS定…

力扣日记11.27-【二叉树篇】二叉树的最大深度

力扣日记&#xff1a;【二叉树篇】二叉树的最大深度 日期&#xff1a;2023.11.27 参考&#xff1a;代码随想录、力扣 104. 二叉树的最大深度 题目描述 难度&#xff1a; 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最…

分布式篇---第六篇

系列文章目录 文章目录 系列文章目录前言一、说说什么是漏桶算法二、说说什么是令牌桶算法三、数据库如何处理海量数据?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码…

第二证券:数据要素概念活跃,零点有数“20cm”涨停,皖通科技3连板

数据要素概念27日盘中强势拉升&#xff0c;到发稿&#xff0c;零点稀有“20cm”涨停&#xff0c;易华录涨超10%&#xff0c;兴正确通、德生科技、皖通科技、中远海科等亦涨停&#xff0c;三维六合涨近10%。值得注意的是&#xff0c;皖通科技已接连3个生意日涨停。 消息面上&am…

STM32Cube高效开发教程<基础篇>(九)----实时时钟

声明:本人水平有限,博客可能存在部分错误的地方,请广大读者谅解并向本人反馈错误。    本专栏博客参考《STM32Cube高效开发教程(基础篇)》,有意向的读者可以购买正版书籍辅助学习,本书籍由王维波老师、鄢志丹老师、王钊老师倾力打造,书籍内容干货满满。 一、 功能概述…

GPTs 初体验 - 1 分钟就能创建一个自己的 ChatGPT? | 京东云技术团队

就在 11.10 号早上&#xff0c;ChatGPT 已经偷摸的把GPTs功能&#xff0c;开放给所有尊贵的 Plus 用户了。 随着这波的功能开放&#xff0c;界面也是改了不少。点击左侧的 Explore 或者左下角的用户处&#xff0c;就可以直接进入新的 GPTs 功能&#xff1a; 这里可以看到我们…

[网络] 5. TCP 链接的建立与释放~汇总

大部分内容源于网络加之个人理解&#xff5e;巨人的肩膀有多大决定你可以看得多远&#xff5e; 文章目录 1. 三次握手说一下三次握手的过程为什么是三次握手 2. 四次挥手说一下四次挥手的过程为什么需要四次挥手有可能出现三次挥手吗&#xff0c;什么时候会出现呢&#xff1f;为…

在 Banana Pi BPI-R2 PRO RK3568开源路由器上安装 OpenWrt 23 快照固件

这是在 BPI-R2 Pro&#xff08;到内部 eMMC&#xff09;上安装 OpenWrt 23 快照固件的快速指南。该固件已预装 LuCI 和一些软件包。这是 2023 年 9 月 2 日的屏幕截图。 LuCI 主页概述。Linux内核是6.1.50 网络接口概述。PPPoE 连接已启动并正在运行 速度测试和 CPU 使用情况…

springboot(ssm网络相册 在线相册管理系统Java(codeLW)

springboot(ssm网络相册 在线相册管理系统Java(code&LW) 开发语言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&#xff08;或8.0&#xff09…