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多万色。

格式最高支持色彩通道索引色编辑支持透明支持
PNG8256索引色支持 支持设定特定索引色为透明色(布尔透明)
支持为索引色附加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编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。

十进制数13780787113102610
十六进制数89504e470d0a1a0a

当中第一个字节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字节图像宽度,以像素为单位
Height4字节图像高度,以像素为单位
Bit depth1字节图像深度:
索引彩色图像:1,2。4或8
灰度图像:1,2,4,8或16
真彩色图像:8或16
ColorType1字节颜色类型:
0:灰度图像, 1,2。4。8或16
2:真彩色图像,8或16
3:索引彩色图像,1,2,4或8
4:带α通道数据的灰度图像,8或16
6:带α通道数据的真彩色图像,8或16
Compression method1字节压缩方法(LZ77派生算法)
Filter method1字节滤波器方法
Interlace method1字节隔行扫描方法:
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; }

tp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值