深入解析png图片解码技术
PNG,可移植网络图形格式(Portable Network Graphic Format,PNG)名称来源于非官方的“PNG's Not GIF”,是一种位图文件(bitmap file)存储格式。PNG用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时,彩色图像的深度可多到48位,并且还可存储多到16位的α通道数据。
PNG格式有8位、24位、32位三种形式,其中8位PNG支持两种不同的透明形式(索引透明和alpha透明),24位PNG不支持透明,32位PNG在24位基础上增加了8位透明通道,因此可展现256级透明程度。
PNG8和PNG24后面的数字则是代表这种PNG格式最多可以索引和存储的颜色值。”8″代表2的8次方也就是256色,而24则代表2的24次方大概有1600多万色。
格式 |
最高支持色彩通道 |
索引色编辑支持 |
透明支持 |
PNG8 |
256索引色 |
支持 |
支持设定特定索引色为透明色(布尔透明) 支持为索引色附加8位透明度(256阶alpha透明) |
PNG24 |
约1600万色 |
不支持 |
不支持 |
PNG32 |
约1600万色 |
不支持 |
支持8位透明度(256阶alpha透明) |
PNG文件格式保留GIF文件格式的下列特性:
* 使用彩色查找表或者叫做调色板可支持256种颜色的彩色图像;
* 流式读/写性能(streamability):图像文件格式允许连续读出和写入图像数据,这个特性很适合于在通信过程中生成和显示图像;
* 逐次逼近显示(progressive display):这种特性可使在通信链路上传输图像文件的同时就在终端上显示图像,把整个轮廓显示出来之后逐步显示图像的细节,也就是先用低分辨率显示图像,然后逐步提高它的分辨率;
* 透明性(transparency):这个性能可使图像中某些部分不显示出来,用来创建一些有特色的图像。
* 辅助信息(ancillary information):这个特性可用来在图像文件中存储一些文本注释信息;
* 独立于计算机软硬件环境;
* 使用无损压缩。
PNG文件格式中要增加下列GIF文件格式所没有的特性:
* 每个像素为48位的真彩色图像;
* 每个像素为16位的灰度图像;
* 可为灰度图和真彩色图添加α通道;
* 添加图像的γ信息;
* 使用循环冗余码(cyclic redundancy code,CRC)检测损害的文件;
* 加快图像显示的逐次逼近显示方式;
* 标准的读/写工具包;
* 可在一个文件中存储多幅图像。
文件结构
PNG图像格式文件(或者称为数据流)由一个8字节的PNG文件署名(PNG file signature)域和按照特定结构组织的3个以上的数据块(chunk)组成。
PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。关键数据块定义了4个标准数据块,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。虽然PNG文件规范没有要求PNG编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。
十进制数 |
137 |
80 |
78 |
71 |
13 |
10 |
26 |
10 |
十六进制数 |
89 |
50 |
4e |
47 |
0d |
0a |
1a |
0a |
其中第一个字节0x89超出了ASCII字符的范围,这是为了避免某些软件将PNG文件当做文本文件来处理。文件中剩余的部分由3个以上的PNG的数据块(Chunk)按照特定的顺序组成,因此,一个标准的PNG文件结构应该如下:
PNG文件标志 |
PNG数据块 |
... |
PNG数据块 |
所以我们可以看到-x里面png格式的判断函数:
bool Image::isPng(const unsigned char * data, ssize_t dataLen)
{if (dataLen <= 8){return false;}static const unsigned char PNG_SIGNATURE[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};return memcmp(PNG_SIGNATURE, data, sizeof(PNG_SIGNATURE)) == 0;
}
PNG文件格式中的数据块 |
||||
数据块符号 |
数据块名称 |
多数据块 |
可选否 |
位置限制 |
IHDR |
文件头数据块 |
否 |
否 |
第一块 |
cHRM |
基色和白色点数据块 |
否 |
是 |
在PLTE和IDAT之前 |
gAMA |
图像γ数据块 |
否 |
是 |
在PLTE和IDAT之前 |
sBIT |
样本有效位数据块 |
否 |
是 |
在PLTE和IDAT之前 |
PLTE |
调色板数据块 |
否 |
是 |
在IDAT之前 |
bKGD |
背景颜色数据块 |
否 |
是 |
在PLTE之后IDAT之前 |
hIST |
图像直方图数据块 |
否 |
是 |
在PLTE之后IDAT之前 |
tRNS |
图像透明数据块 |
否 |
是 |
在PLTE之后IDAT之前 |
oFFs |
(专用公共数据块) |
否 |
是 |
在IDAT之前 |
pHYs |
物理像素尺寸数据块 |
否 |
是 |
在IDAT之前 |
sCAL |
(专用公共数据块) |
否 |
是 |
在IDAT之前 |
IDAT |
图像数据块 |
是 |
否 |
与其他IDAT连续 |
tIME |
像最后修改时间数据块 |
否 |
是 |
无限制 |
tEXt |
文本信息数据块 |
是 |
是 |
无限制 |
zTXt |
压缩文本数据块 |
是 |
是 |
无限制 |
fRAc |
(专用公共数据块) |
是 |
是 |
无限制 |
gIFg |
(专用公共数据块) |
是 |
是 |
无限制 |
gIFt |
(专用公共数据块) |
是 |
是 |
无限制 |
gIFx |
(专用公共数据块) |
是 |
是 |
无限制 |
IEND |
图像结束数据 |
否 |
否 |
最后一个数据块 |
数据块结构
名称 |
字节数 |
说明 |
Length (长度) |
4字节 |
指定数据块中数据域的长度,其长度不超过(231-1)字节 |
Chunk Type Code (数据块类型码) |
4字节 |
数据块类型码由ASCII字母(A-Z和a-z)组成 |
Chunk Data (数据块数据) |
可变长度 |
存储按照Chunk Type Code指定的数据 |
CRC (循环冗余检测) |
4字节 |
存储用来检测是否有错误的循环冗余码 |
IHDR
文件头数据块IHDR(header chunk):它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。文件头数据块由13字节组成,它的格式如下表所示。
域的名称 |
字节数 |
说明 |
Length (长度) |
4字节 |
图像宽度,以像素为单位 |
Height |
4字节 |
图像高度,以像素为单位 |
Bit depth |
1字节 |
图像深度: 索引彩色图像:1,2,4或8 灰度图像:1,2,4,8或16 真彩色图像:8或16 |
ColorType |
1字节 |
颜色类型: 0:灰度图像, 1,2,4,8或16 2:真彩色图像,8或16 3:索引彩色图像,1,2,4或8 4:带α通道数据的灰度图像,8或16 6:带α通道数据的真彩色图像,8或16 |
Compression method |
1字节 |
压缩方法(LZ77派生算法) |
Filter method |
1字节 |
滤波器方法 |
Interlace method |
1字节 |
隔行扫描方法: 0:非隔行扫描 1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法) |
PLTE
调色板数据块PLTE(palette chunk)包含有与索引彩色图像(indexed-color image)相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块(image data chunk)之前。
PLTE数据块是定义图像的调色板信息,PLTE可以包含1~256个调色板信息,每一个调色板信息由3个字节RGB组成,因此,调色板的长度应该是3的倍数,否则,这将是一个非法的调色板。同理调色板数据块所包含的最大字节数为768。
对于索引图像,调色板信息是必须的,调色板的颜色索引从0开始编号,然后是1、2……,调色板的颜色数不能超过色深中规定的颜色数(如图像色深为4的时候,调色板中的颜色数不可以超过2^4=16),否则,这将导致PNG图像不合法。
真彩色图像和带α通道数据的真彩色图像也可以有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像。
IDAT
图像数据块IDAT(image data chunk):它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。
IDAT存放着图像真正的数据信息,因此,如果能够了解IDAT的结构,我们就可以很方便的生成PNG图像。
IEND
图像结束数据IEND(image trailer chunk):它用来标记PNG文件或者数据流已经结束,并且必须要放在文件的尾部。如果我们仔细观察PNG文件,我们会发现,文件的结尾12个字符看起来总应该是这样的:
0000 00 00 49 45 4E 44 AE 42 60 82
不难明白,由于数据块结构的定义,IEND数据块的长度总是0(00 00 00 00,除非人为加入信息),数据标识总是IEND(4945 4E 44),因此,CRC码也总是AE42 60 82。
cocos2dx libpng的解码
bool Image::initWithPngData(const unsigned char * data, ssize_t dataLen)
{// length of bytes to check if it is a valid png file
#define PNGSIGSIZE 8bool ret = false;png_byte header[PNGSIGSIZE] = {0};png_structp png_ptr = 0;png_infop info_ptr = 0;do{// png header len is 8 bytesCC_BREAK_IF(dataLen < PNGSIGSIZE);//文件头校验// check the data is png or notmemcpy(header, data, PNGSIGSIZE);CC_BREAK_IF(png_sig_cmp(header, 0, PNGSIGSIZE));//初始化png_structp类型结构体,libpng内部使用// init png_structpng_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);CC_BREAK_IF(! png_ptr);//创建图像信息// init png_infoinfo_ptr = png_create_info_struct(png_ptr);CC_BREAK_IF(!info_ptr);#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA && CC_TARGET_PLATFORM != CC_PLATFORM_NACL)//设置异常处理CC_BREAK_IF(setjmp(png_jmpbuf(png_ptr)));
#endif//使用自定义的回调函数来设置libpng的数据源// set the read call back functiontImageSource imageSource;imageSource.data = (unsigned char*)data;imageSource.size = dataLen;imageSource.offset = 0;png_set_read_fn(png_ptr, &imageSource, pngReadCallback);// read png header info//使用底层处理来读取png数据// read png file infopng_read_info(png_ptr, info_ptr);//查询图像信息_width = png_get_image_width(png_ptr, info_ptr);_height = png_get_image_height(png_ptr, info_ptr);png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr);png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);//CCLOG("color type %u", color_type);//调色板格式的png图片,转化为RGB888的像素格式// force palette images to be expanded to 24-bit RGB// it may include alpha channelif (color_type == PNG_COLOR_TYPE_PALETTE){png_set_palette_to_rgb(png_ptr);}//像素格式少于1字节长度的灰度图,将其转为每像素占1字节的像素格式// low-bit-depth grayscale images are to be expanded to 8 bitsif (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8){bit_depth = 8;png_set_expand_gray_1_2_4_to_8(png_ptr);}//将tRNS块数据信息扩展为完整的ALPHA通道信息// expand any tRNS chunk data into a full alpha channelif (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)){png_set_tRNS_to_alpha(png_ptr);} //将16位输入降为8位// reduce images with 16-bit samples to 8 bitsif (bit_depth == 16){png_set_strip_16(png_ptr); }// Expanded earlier for grayscale, now take care of palette and rgbif (bit_depth < 8){png_set_packing(png_ptr);}//更新png数据的详细信息// update infopng_read_update_info(png_ptr, info_ptr);bit_depth = png_get_bit_depth(png_ptr, info_ptr);color_type = png_get_color_type(png_ptr, info_ptr);switch (color_type){case PNG_COLOR_TYPE_GRAY:_renderFormat = Texture2D::PixelFormat::I8;break;case PNG_COLOR_TYPE_GRAY_ALPHA:_renderFormat = Texture2D::PixelFormat::AI88;break;case PNG_COLOR_TYPE_RGB:_renderFormat = Texture2D::PixelFormat::RGB888;break;case PNG_COLOR_TYPE_RGB_ALPHA:_renderFormat = Texture2D::PixelFormat::RGBA8888;break;default:break;}//按行读取png信息,// read png datapng_size_t rowbytes;png_bytep* row_pointers = (png_bytep*)malloc( sizeof(png_bytep) * _height );//获取每一行像素的字节数量rowbytes = png_get_rowbytes(png_ptr, info_ptr);//申请内存地址_dataLen = rowbytes * _height;_data = static_castchar*="">(malloc(_dataLen * sizeof(unsigned char)));if (!_data){if (row_pointers != nullptr){free(row_pointers);}break;}for (unsigned short i = 0; i < _height; ++i){row_pointers[i] = _data + i*rowbytes;}//读取png数据png_read_image(png_ptr, row_pointers);//结束读取数据png_read_end(png_ptr, nullptr);// premultiplied alpha for RGBA8888if (color_type == PNG_COLOR_TYPE_RGB_ALPHA){//预乘Alpha,使用渐变AlphapremultipliedAlpha();}else{_hasPremultipliedAlpha = false;}if (row_pointers != nullptr){//释放图片数据的内存free(row_pointers);}ret = true;} while (0);if (png_ptr){//释放png_ptrpng_destroy_read_struct(&png_ptr, (info_ptr) ? &info_ptr : 0, 0);}return ret;
}
深入解析png图片解码技术相关推荐
- 新一代图片编解码技术在淘宝的应用及落地
本文回顾淘宝图片发展的历史,阐述了新一代图像编解码格式AVIF在淘宝业务场景中的应用及落地方案,节省流量,为用户提供更好的看图体验. 背景 淘宝图片空间下行链路承载着集团图片的访问请求,包括手淘.飞猪 ...
- 【H.264/AVC视频编解码技术具体解释】十三、熵编码算法(4):H.264使用CAVLC解析宏块的残差数据...
<H.264/AVC视频编解码技术具体解释>视频教程已经在"CSDN学院"上线,视频中详述了H.264的背景.标准协议和实现,并通过一个实战project的形式对H.2 ...
- 1.基于S5PV210的图片解码播放器(详解)
有道云笔记详细地址: 文档:图片解码播放器小项目(详解).note 链接:http://note.youdao.com/noteshare?id=9f9a43ac5ec6828cf467940dfa1 ...
- 视频监控与视频编解码技术
视频监控与视频编解码技术 参考文献链接 https://mp.weixin.qq.com/s/UW4Z0vu_Wypc0ddymrOkpg https://mp.weixin.qq.com/s/Rfs ...
- 【从零开始】理解视频编解码技术
[从零开始]理解视频编解码技术 auxten CovenantSQL 联合创始人 关注他 1,263 人赞同了该文章 转载自: https://github.com/leandromoreira ...
- 【H.264/AVC视频编解码技术详解】十九:熵编码算法(5)——H.264的CABAC(上):语法元素的二值化方法...
<H.264/AVC视频编解码技术详解>视频教程已经在"CSDN学院"上线,视频中详述了H.264的背景.标准协议和实现,并通过一个实战工程的形式对H.264的标准进行 ...
- Netty详解(六):Netty 编解码技术
1. 概述 基于Java提供的对象输入/输出流ObjectInputStream和ObjectOutputStream,可以直接把Java对象作为可村粗的字节数组写入文件,也可以传输到网络上去.Jav ...
- Netflix选择AVIF作为下一代图片压缩技术
Photo by John-Mark Smith from Pexels Netflix在情人节当天公开AVIF作为下一代图片压缩技术,同其宣布在Android端采用AV1视频编解码格式仅相隔一周时间 ...
- 【H.264/AVC视频编解码技术详解】七、 熵编码算法(1):基础知识
<H.264/AVC视频编解码技术详解>视频教程已经在"CSDN学院"上线,视频中详述了H.264的背景.标准协议和实现,并通过一个实战工程的形式对H.264的标准进行 ...
最新文章
- Android 图片缓存之内存缓存技术LruCache,软引用
- python真的超过java了吗-Python为什么突然就火了呢?竟然还超过了java
- 构建物联网网络的4个关键步骤简介
- js面向对象与PHP面向对象总结
- 单片机涡轮流量传感器_关于涡轮流量计传感器的维护保养
- Last_Error: Slave SQL thread retried transaction 10 time(s) in vain, giving up导致主从不同步的解决方法
- pytorch 构造读取数据的工具类 Dataset 与 DataLoader (pytorch Data学习一)
- 【Avro】两种根据avsc文件生成avro序列化的实体类-maven和avro-tool
- BSD:Berkeley Software Distribution,伯克利软件套件
- Android AlertDialog 实现对话框 警告对话框、列表对话框、自定义对话框
- 5G - MEC(移动边缘计算)
- Kite Compositor制作下雨打雷特效详细教学
- 高数 不定积分 欧拉代换
- 仿热血江湖游戏NpcClass_自动攻击事件 刀反伤
- python中使用“[函数名].[变量名]”声明变量
- 嵌入式 linux yum命令详解
- 部署-GPS授时系统:GPS授时系统
- 程序员宅男干货福利!手把手教你获取上千张cosplay小姐姐的美照
- 读书笔记:Mysql实战45讲 (1-10讲)
- 转载~时间复杂度分析(个人强推)