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个字符看起来总应该是这样的:

00 00 00 00 49 45 4E 44 AE 42 60 82

不难明确。因为数据块结构的定义。IEND数据块的长度总是0(00 00 00 00,除非人为加入信息),数据标识总是IEND(49 45 4E 44)。因此,CRC码也总是AE 42 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 8
bool ret = false;
png_byte header[PNGSIGSIZE] = {0};
png_structp png_ptr = 0;
png_infop info_ptr = 0; do
{
// png header len is 8 bytes
CC_BREAK_IF(dataLen < PNGSIGSIZE); //文件头校验
// check the data is png or not
memcpy(header, data, PNGSIGSIZE);
CC_BREAK_IF(png_sig_cmp(header, 0, PNGSIGSIZE)); //初始化png_structp类型结构体,libpng内部使用
// init png_struct
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
CC_BREAK_IF(! png_ptr); //创建图像信息
// init png_info
info_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 function
tImageSource 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 info
png_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 channel
if (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 bits
if (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 channel
if (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 bits
if (bit_depth == 16)
{
png_set_strip_16(png_ptr);
} // Expanded earlier for grayscale, now take care of palette and rgb
if (bit_depth < 8)
{
png_set_packing(png_ptr);
}
//更新png数据的具体信息
// update info
png_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 data
png_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_cast<unsigned char*>(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 RGBA8888
if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
{
//预乘Alpha。使用渐变Alpha
premultipliedAlpha();
}
else
{
_hasPremultipliedAlpha = false;
} if (row_pointers != nullptr)
{
//释放图片数据的内存
free(row_pointers);
} ret = true;
} while (0); if (png_ptr)
{
//释放png_ptr
png_destroy_read_struct(&png_ptr, (info_ptr) ? &info_ptr : 0, 0);
}
return ret;
}

png图片解码的更多相关文章

  1. Android实现GIF图片解码与播放

    Android实现GIF图片解码与播放 如何在Android中播放GIF图片呢?如果直接按以前的方法,分解图片,可能相对比较麻烦. 今天给大伙介绍一种新的方式,构造自己的Android图片解码帮助类, ...

  2. STM32单片机图片解码

    图片解码首先是最简单的bmp图片解码,关于bmp的结构可自行查阅,代码如下 #ifndef __BMPDECODE_H_ #define __BMPDECODE_H_ #include "f ...

  3. 编译skia静态库时,图片解码库无法注册的问题

    转载:http://www.cnblogs.com/imlucky/archive/2012/08/01/2617851.html 今天编译skia库,增加图片解码库时总是无效.按照此博客的方法修改后 ...

  4. 【STM32H7教程】第58章 STM32H7的硬件JPEG应用之图片解码显示

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第58章       STM32H7的硬件JPEG应用之图片解 ...

  5. OpenCV-Python cv2.imdecode()和cv2.imencode() 图片解码和编码

    cv2.imdecode()函数从指定的内存缓存中读取数据,并把数据转换(解码)成图像格式;主要用于从网络传输数据中恢复出图像. cv2.imencode()函数是将图片格式转换(编码)成流数据,赋值 ...

  6. 如何让Android 支持HEIF 图片解码和加载(免费的方法)

    字节跳动火山引擎ImageX提供了一种能力,可以支持客户端android 直接解码HEIF 和HEIC图片,经过测试发现,可以免费使用: 一.阅前准备 HEIF图片格式是什么? 高效率图像格式(Hig ...

  7. android 图片解码显示流程

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/jingxia2008/article/details/32327699 问题来源 android 能 ...

  8. Instruments Time profiler 调优APP 之图片解码

    以前闲时用instruments的Time profiler调试过APP,发现用tableView: cellForRowAtIndexPath: 中cell的图片设置耗时较多,之前改了一下,如下 d ...

  9. Android 获得图片并解码成缩略图以减少内存消耗

    本文内容 环境 演示 下载 Demo 环境 Windows 2008 R2 64 位 Eclipse ADT V22.6.2,Android 4.4.3 SAMSUNG GT-I9008L,Andro ...

随机推荐

  1. 梦想CAD控件,用于浏览和编辑DWG文件,在脱离AUTOCAD的情况下独立运行,相当于简易CAD

    (百度百科连接) 梦想绘图控件5.2  是国内最强,最专业的CAD开发组件(控件),不需要AutoCAD就能独立运行.控件使用VC 2010开发,最早从2007年第一个版本完成,经过多年的累积已经非常 ...

  2. Django中使用多线程发送邮件

    1.settings.py 增加Email设置   #mail EMAIL_HOST = ‘smtp.gmail.com’                   #邮件smtp服务器 EMAIL_POR ...

  3. C#线程锁使用全功略

    C#线程锁使用全功略 前两篇简单介绍了线程同步lock,Monitor,同步事件EventWaitHandler,互斥体Mutex的基本用法,在此基础上,我们对 它们用法进行比较,并给出什么时候需要锁 ...

  4. Project Euler

    Euler 34 答案:40730 我用程序算了无数次都是145,蛋疼,最后拿别人的程序仔细对比…… 原来 !=…… 真蛋疼,我竟然连基础数学都忘了 Euler-44 根据公式容易得出:Pmin + ...

  5. 树莓派 - 通过sysfs操控GPIO

    点亮或熄灭LED 硬件上,一个LED灯接在pi的Pin-25.  该引脚为BCM的GPIO26 $ gpio readall +-----+-----+---------+------+---+--- ...

  6. 设计方案--移动端延迟300ms的原因以及解决方案

    一.前言 移动端浏览器提供一个特殊的功能:双击(double tap)缩放.   二.移动端延迟300ms的原因 为什么要用触摸事件?触摸事件是移动端浏览器特有的html5事件. 因为移动端的clic ...

  7. url方法使用与单例模式

    一.url方法使用 from django.contrib import admin from django.urls import path, include from django.conf.ur ...

  8. dsu+树链剖分+树分治

    dsu,对于无修改子树信息查询,并且操作支持undo的问题 暴力dfs,对于每个节点,对所有轻儿子dfs下去,然后再消除轻儿子的影响 dfs重儿子,然后dfs暴力恢复轻儿子们的影响,再把当前节点影响算 ...

  9. Python xml文件处理

    什么是XML文件? xml即可扩展标记语言,它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言. 从结构上,很像HTML超文本标记语言.但他们被设计的目的是不同的,具体如 ...

  10. [luoguP2401] 不等数列

    传送门 f[i][j]表示前i个数有j个<的方案数 #include <cstdio> #define N 1001 #define p 2015 int n, k; int f[N ...