关于 QImage 加载本地大图片的崩溃问题
问题查找QImage reloadImg; reloadImg.load(fileName);代码运行在load时崩溃下面我们来看一下load函数做了什么事情为什么会崩溃。bool QImage::load(const QString fileName, const char* format) { QImage image QImageReader(fileName, format).read(); operator(image); return !isNull(); }QImageReader(fileName, format).read();调用了QImageReader的构造函数和read函数QImageReader::QImageReader(const QString fileName, const QByteArray format) : QImageReader(new QFile(fileName), format) { d-deleteDevice true; } QImageReader::QImageReader(QIODevice *device, const QByteArray format) : d(new QImageReaderPrivate(this)) { d-device device; d-format format; }QImageReader的构造函数使用了委托构造QImageReaderPrivate内部持有类的实际内容而QIODevice *device形参传入的new QFile(fileName)仅仅是使用多态作为IO接口去加载文件。这里把QImageReaderPrivate的定义展示出来笔者写了部分的注释。class QImageReaderPrivate { public: /// 构造函数绑定外部的 QImageReader 实例 QImageReaderPrivate(QImageReader *qq); /// 析构函数释放资源如 device、handler 等 ~QImageReaderPrivate(); // // 设备与格式相关 // /// 文件格式如 png、jpg、bmp 等 QByteArray format; /// 是否自动检测图像格式若为 true则根据文件头判断格式 bool autoDetectImageFormat; /// 是否忽略文件格式与扩展名例如强制使用 handler 解析 bool ignoresFormatAndExtension; /// 当前绑定的输入设备例如 QFile、QBuffer、QByteArray 等 QIODevice *device; /// 是否在析构时自动删除 device例如用户未显式管理时 bool deleteDevice; /// 当前用于实际解码图像的处理器对象QImageIOHandler 派生类 QImageIOHandler *handler; /// 初始化 handler根据 format / device 自动选择解码插件 bool initHandler(); // // 图像选项与读取参数 // /// 读取时的裁剪区域在图像坐标系中 QRect clipRect; /// 缩放后的目标尺寸若为空则不缩放 QSize scaledSize; /// 缩放后再裁剪的区域用于优化部分读取 QRect scaledClipRect; /// 图像质量参数通常用于写入时某些解码器可能会参考 int quality; /// 图像内嵌文本信息键值对形式如 Exif、注释、元数据 QMapQString, QString text; /// 从 handler 获取所有文本信息 void getText(); /// 自动应用图像变换的策略枚举 enum { UsePluginDefault, /// 使用插件默认行为由解码器决定是否旋转 ApplyTransform, /// 总是应用图像的 EXIF / 方向变换 DoNotApplyTransform /// 不应用任何图像方向变换 } autoTransform; // // 错误状态 // /// 最近一次错误的类型如 不支持的格式、读失败、内存不足 等 QImageReader::ImageReaderError imageReaderError; /// 最近一次错误的字符串描述 QString errorString; // // 关联的外部对象 // /// 指向外部的 QImageReader 公有接口类用于回调与状态同步 QImageReader *q; };这里的QIODevice *device是本地图片的IO设备指针QImageIOHandler *handler用来进行关键的加载图片内容的处理操作在下文中有提及。构造函数就这么多内容看不出所以然。下面去看看QImageReader::read()QImage QImageReader::read() { // Because failed image reading might have side effects, we explicitly // return a null image instead of the image weve just created. QImage image; return read(image) ? image : QImage(); } bool QImageReader::read(QImage *image) { ... if (!d-handler !d-initHandler()) return false; ... // read the image if (!d-handler-read(image)) { d-imageReaderError InvalidDataError; d-errorString QImageReader::tr(Unable to read image data); return false; } ... }bool QImageReader::read(QImage *image)内部做了很多关于自动识别图片类型的操作这里省略展示了这些代码仅展示关键代码。对于此文的BMP图片d-initHandler()函数的实现了定义d-handler变量为new QBmpHandler同时设置handler-setDevice(device),使得d-handler可以持有图片文件的IO设备指针。接下来是最关键的d-handler-read(image)加载图片数据。bool QBmpHandler::read(QImage *image) { if (state Error) return false; if (!image) { qWarning(QBmpHandler::read: cannot read into null pointer); return false; } if (state Ready !readHeader()) { state Error; return false; } QIODevice *d device(); QDataStream s(d); // Intel byte order s.setByteOrder(QDataStream::LittleEndian); // read image const bool readSuccess m_format BmpFormat ? read_dib_body(s, infoHeader, fileHeader.bfOffBits, startpos, *image) : read_dib_body(s, infoHeader, -1, startpos - BMP_FILEHDR_SIZE, *image); if (!readSuccess) return false; state Ready; return true; }QBmpHandler::read(QImage *image)函数内部的关键是QBmpHandler::readHeader()函数和read_dib_body()函数。bool QBmpHandler::readHeader() { state Error; QIODevice *d device(); QDataStream s(d); startpos d-pos(); // Intel byte order s.setByteOrder(QDataStream::LittleEndian); // read BMP file header if (m_format BmpFormat !read_dib_fileheader(s, fileHeader)) return false; // read BMP info header if (!read_dib_infoheader(s, infoHeader)) return false; state ReadHeader; return true; }先来看看QBmpHandler::readHeader()内部实现了从IO设备文件中加载图片的文件头和消息头可以根据下面BMP文件的存储格式查看。------------------------- | BMP_FILEHDR (14 bytes) | -- 文件头 ------------------------- | BMP_INFOHDR (40~124 B) | -- 信息头DIB Header ------------------------- | Color Table (可选) | -- 调色板灰度/索引图时有 ------------------------- | Pixel Array | -- 实际像素数据 -------------------------这是qt内部定义的文件头和消息头结构体加载存放到变量BMP_FILEHDR fileHeader和BMP_INFOHDR infoHeader中struct BMP_FILEHDR { char bfType[2]; // 文件类型必须是 B,M qint32 bfSize; // 文件总大小字节 qint16 bfReserved1; // 保留一般为 0 qint16 bfReserved2; // 保留一般为 0 qint32 bfOffBits; // 图像数据起始偏移从文件头开始的偏移 }; struct BMP_INFOHDR { qint32 biSize; // 结构体大小字节数 qint32 biWidth; // 图像宽度像素 qint32 biHeight; // 图像高度像素 qint16 biPlanes; // 平面数固定为 1 qint16 biBitCount; // 每像素位数 (1,4,8,16,24,32) qint32 biCompression; // 压缩方式0BI_RGB qint32 biSizeImage; // 图像数据大小字节 qint32 biXPelsPerMeter; // 水平分辨率像素/米 qint32 biYPelsPerMeter; // 垂直分辨率像素/米 qint32 biClrUsed; // 实际使用的调色板颜色数 qint32 biClrImportant; // 重要颜色数 // 以下是 Windows V4/V5 扩展部分Qt 兼容处理 quint32 biRedMask; // 红通道掩码 quint32 biGreenMask; // 绿通道掩码 quint32 biBlueMask; // 蓝通道掩码 quint32 biAlphaMask; // 透明度掩码 qint32 biCSType; // 色彩空间类型如 LCS_sRGB qint32 biEndpoints[9]; // 色彩空间端点 qint32 biGammaRed; qint32 biGammaGreen; qint32 biGammaBlue; qint32 biIntent; // 渲染意图V5 qint32 biProfileData; qint32 biProfileSize; qint32 biReserved; };这里提一嘴BMP_FILEHDR的qint32 bfSize最大值转为无符号类型的内存容量仅有4096MB的大小意味着图片大小超过4GB时实际上的bfSize会越界所以没有可信度但是Qt这里并没有使用这个变量应该也是考虑到了这个问题。这也解释了为什么有的看图软件无法打开比较大的图片。下面是关键的static bool read_dib_body(QDataStream s, const BMP_INFOHDR bi, qint64 offset, qint64 startpos, QImage image)函数因为确认格式是BmpFormat而不是DibFormat所以QBmpHandler::read(QImage *image)内部调用的逻辑简化后是:const bool readSuccess read_dib_body(s, infoHeader, fileHeader.bfOffBits, startpos, *image)这里的传参s为文件数据流infoHeader为消息头fileHeader.bfOffBits为图像数据起始偏移startpos基本为0代表文件头数据位置*image为存放图片的对象因为read_dib_body函数太长这里仅展示Format_RGB888、Format_Grayscale8、Format_RGB32的内容具体的源码可以看文章最后的源代码章节static bool read_dib_body(QDataStream s, const BMP_INFOHDR bi, qint64 offset, qint64 startpos, QImage image) { ... QImage::Format format; switch (nbits) { case 32: case 24: case 16: depth 32; format transp ? QImage::Format_ARGB32 : QImage::Format_RGB32; break; case 8: case 4: depth 8; format QImage::Format_Indexed8; break; default: depth 1; format QImage::Format_Mono; } ... if (image.size() ! QSize(w, h) || image.format() ! format) { image QImage(w, h, format); if (image.isNull()) // could not create image return false; if (ncols) image.setColorCount(ncols); // Ensure valid QImage } ... int bpl image.bytesPerLine(); uchar *data image.bits(); if (nbits 1) { // 1 bit BMP image ...} else if (nbits 4) { // 4 bit BMP image ...} else if (nbits 8) { // 8 bit BMP image if (comp BMP_RLE8) { // run length compression ... } else if (comp BMP_RGB) { // uncompressed while (--h 0) { if (d-read((char *)data h*bpl, bpl) ! bpl) break; } } } else if (nbits 16 || nbits 24 || nbits 32) { // 16,24,32 bit BMP image QRgb *p; QRgb *end; uchar *buf24 new uchar[bpl]; int bpl24 ((w*nbits31)/32)*4; uchar *b; int c; while (--h 0) { p (QRgb *)(data h*bpl); end p w; if (d-read((char *)buf24,bpl24) ! bpl24) break; b buf24; while (p end) { c *(uchar*)b | (*(uchar*)(b1)8); if (nbits 16) c | *(uchar*)(b2)16; if (nbits 24) c | *(uchar*)(b3)24; *p qRgba(((c red_mask) red_shift) * red_scale, ((c green_mask) green_shift) * green_scale, ((c blue_mask) blue_shift) * blue_scale, transp ? ((c alpha_mask) alpha_shift) * alpha_scale : 0xff); b nbits/8; } } delete[] buf24; } ... }

相关新闻

[SampleTexture2DArray节点]原理解析与实际应用

[SampleTexture2DArray节点]原理解析与实际应用

在 Shader Graph 中使用 Sample Texture 2D Array 节点时,您需要提供 UV 坐标来确定采样位置,同时可以通过采样器状态节点来定义纹理的过滤方式和环绕模式。节点的核心特性是索引输入端口,它决定了从纹理数组中选取哪个具体的纹理进行采样。 …

2026/7/1 2:11:58阅读更多 →
为什么我们需要关注线程?

为什么我们需要关注线程?

在多核处理器成为主流的今天,我们手中的手机、电脑甚至智能家居设备都拥有多个计算核心。这意味着,如果我们的程序只能在一个核心上运行,就相当于让其他核心"闲置",无法充分发挥硬件性能。想象一下,一个餐厅…

2026/7/1 2:11:58阅读更多 →
tpshop商城Web项目实战:从业务测试到缺陷管理全流程(功能测试)

tpshop商城Web项目实战:从业务测试到缺陷管理全流程(功能测试)

Web项目实战——tpshop商城 一、项目介绍 1.1项目是什么 Tpshop商城地址:https://hmshop-test.itheima.net/ Tpshop商城,类 似于淘宝、京东类的(B2C)电子商务平台,主要为线上用户提供优质便捷的购物服务。 前台地址…

2026/7/1 2:11:58阅读更多 →
(十一)「JVS-Rules规则引擎 V2.5」— 决策流的可视化组成

(十一)「JVS-Rules规则引擎 V2.5」— 决策流的可视化组成

规则引擎是由多个组件组成的,这些组件共同协作实现规则的管理、执行和决策流的构建。决策流决策流是由多个业务节点连接而成的流程,用于实现复杂的业务逻辑。决策流中的业务节点按照特定的顺序执行,每个节点根据输入数据和规则引擎的执行结果…

2026/7/1 3:07:06阅读更多 →
Agent Harness 有了“眼睛”和“手”:更细的 Trace 事件 + 自动错误恢复

Agent Harness 有了“眼睛”和“手”:更细的 Trace 事件 + 自动错误恢复

Agent Harness 有了“眼睛”和“手”:更细的 Trace 事件 自动错误恢复系列博客第三篇 2026-06-29 从“看得清”到“自己会修”一、前情回顾 前两天的成果: Day 1:跑通最小闭环,用户输入 → 模型 → 工具 → 输出,能跑…

2026/7/1 3:07:06阅读更多 →
NS-USBLoader:一站式Switch文件传输与系统管理解决方案

NS-USBLoader:一站式Switch文件传输与系统管理解决方案

NS-USBLoader:一站式Switch文件传输与系统管理解决方案 【免费下载链接】ns-usbloader Awoo Installer and GoldLeaf uploader of the NSPs (and other files), RCM payload injector, application for split/merge files. 项目地址: https://gitcode.com/gh_mirr…

2026/7/1 3:07:06阅读更多 →
宽带信号多相滤波及脉压处理(FPGA+ADC)

宽带信号多相滤波及脉压处理(FPGA+ADC)

关于雷达宽带回波采样DDC、并行滤波及脉压处理,分享相关内容,欢迎交流;留言/私信获取相关源码。应用场景:雷达宽带信号采样及脉压处理,例如回波信号为中频IF 750Mhz,带宽BW 200Mhz;接收ADC的采样…

2026/7/1 3:07:06阅读更多 →
Ubuntu 系统安装 Hermes Agent 使用教程

Ubuntu 系统安装 Hermes Agent 使用教程

今天给大家带来Ubuntu 系统完整部署 Hermes Agent 实操教程,全程无踩坑、零基础可直接复刻。本文适配 Ubuntu 20.04 / 22.04 / 24.04 主流版本,包含环境预检、三种安装方案、初始化配置、常用命令、卸载方式及常见报错排查,是目前全网最全面的…

2026/7/1 3:07:06阅读更多 →
成都靠谱的暖通公司找哪家

成都靠谱的暖通公司找哪家

痛点深度剖析我们团队在实践中发现,暖通行业存在诸多实际技术困境。从产品层面来看,市面上多数五恒系统除湿能力弱,在成都这种潮湿地区,夏季室内闷热、易结露滋生霉菌,还会引发空调病,且温湿度联动控制不合…

2026/7/1 3:02:06阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/6/30 4:03:30阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/6/30 4:36:27阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/1 0:01:44阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/1 0:01:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/1 0:01:44阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/1 0:01:44阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/1 0:01:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/1 0:01:44阅读更多 →