• png图片解码

    2015-07-18 02:43:26
    PNG,可移植网络图形格式(Portable Network Graphic Format,PNG)名称来源于非官方的“PNG’s Not GIF”,是一种位图文件(bitmap file)存储格式。PNG用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时...

    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;
    }
    

    tp

    展开全文
  • 改资源包含JPEG,PNG编解码库,以及本人编写的列子,保证大家快速上手使用JPEG,PNG,BMP格式图片的编解码。和我的博客JPEG图片格式叠加PNG图片格式的水印相对应。
  • ffmpeg 解码 png apng 图片 1,搭建环境 参考网上搭建 ubuntu 下 ffmpeg 环境 2. 定义解码结构体 typedef struct Decode_PNG_Key{ int video_stream_index; AVFormatContext *pFormatCtx; ...

    ffmpeg 解码 png apng 图片

    1,搭建环境
    参考网上搭建 ubuntu 下 ffmpeg 环境

    2. 定义解码结构体
    typedef struct Decode_PNG_Key{
    	 int video_stream_index;
    	 AVFormatContext *pFormatCtx;
    	 AVCodecContext *pCodecCtx;
    	AVCodec *dec;
    	int init_flag;
    	int decode_flag;
    	AVFrame *filt_frame;
    } Decode_Png_key;

    3, 初始化编码器
    int decode_png_init(Decode_Png_key *args, char *filename)
    {
    	int ret = 0;
    	if (args == NULL || filename == NULL )
    		return -1;
    	if ((ret = avformat_open_input(&args->pFormatCtx, filename, NULL, NULL)) < 0)
    	{
    		dm_printf("Cannot open input file");
    		goto end;
    	}
    	if ((ret = avformat_find_stream_info(args->pFormatCtx, NULL)) < 0)
    	{
    		dm_printf("Cannot find stream information\n");
    		goto end;
    	}
    
    	for(int i = 0; i < args->pFormatCtx->nb_streams; i++)
    	{
    		if(args->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
    		{
    			args->video_stream_index = i;
    			break;
    		}
    	}
    	args->pCodecCtx = args->pFormatCtx->streams[args->video_stream_index]->codec;
    	args->dec = avcodec_find_decoder(args->pCodecCtx->codec_id);
    	if(args->dec == NULL)
    	{
    		fprintf(stderr, "can not find decoder!\n");
    		goto end;
    	}
    
    	av_opt_set_int(args->pCodecCtx, "refcounted_frames", 1, 0);
    	/* init the video decoder */
    	if ((ret = avcodec_open2(args->pCodecCtx, args->dec, NULL)) < 0)
    	{
    		dm_printf("Cannot open video decoder\n");
    		goto end;
    	}
    	args->filt_frame = av_frame_alloc();
    	if (args->filt_frame == NULL)
    		ret = -1;
    end:
    	if (ret < 0)
    	{
    		if (args->pCodecCtx)
    		{
    			avcodec_close(args->pCodecCtx);
    			args->pCodecCtx = NULL;
    		}
    		if (args->pFormatCtx)
    		{
    			avformat_close_input(&args->pFormatCtx);
    			args->pFormatCtx = NULL;
    		}
    	}
    	else
    		args->init_flag = 1;
    	return ret;
    }
    



    4. 编码一张图片
    这个切记一定编码一次不一定成功,所以需要多加几次循环才能最终编码成功
    int decode_png_next(Decode_Png_key *args, AVFrame *filt_frame)
    {
    	int ret = -1;
    	int got_frame = 0;
    	int decLen = 0;
    	int error_count = 0;
    	AVPacket  packet;
    	memset(&packet, 0, sizeof(packet));
    	if (args == NULL || filt_frame == NULL || args->pCodecCtx == NULL)
    	{
    		dm_printf("args == NULL || filt_frame== NULL || args->pCodecCtx == NULL");
    		return -1;
    	}
    	av_init_packet(&packet);
    	packet.data = NULL;
    	while(1)
    	{
    		if ((ret = av_read_frame(args->pFormatCtx, &packet)) < 0)
    		{
    			dm_printf("png_read_over");
    			av_free_packet(&packet);
    			break;
    		}
    		if (packet.stream_index == args->video_stream_index)
    		{
    			got_frame = 0;
    			while (packet.size > 0)
    			{
    				decLen = avcodec_decode_video2(args->pCodecCtx, filt_frame, &got_frame, &packet);
    				error_count++;
    				if (error_count > 50)
    				{
    					av_free_packet(&packet);
    					ret = -1;
    					break;
    				}
    				if (decLen < 0)
    				{
    					decLen = packet.size;
    					packet.data += decLen;
    					packet.size -= decLen;
    				}
    				if (got_frame)
    				{
    
    					dm_printf("[video_decode_example]picture->linesize[0]=%d, c->width=%d,c->height=%d,c->format=  %d\n",
    					          filt_frame->linesize[0], filt_frame->width, filt_frame->height, filt_frame->format);
    					packet.data += decLen;
    					packet.size -= decLen;
    					ret = 0;
    
    				}
    			}
    			av_free_packet(&packet);
    			if (got_frame)
    				break;
    		}
    		else
    		{
    			/* discard non-wanted packets */
    			av_free_packet(&packet);
    		}
    	}
    	if (ret == 0)
    		args->decode_flag = 1;
    	else
    		args->decode_flag = 0;
    	return ret;
    }
    

    5. 释放资源
     
    int decode_png_release(Decode_Png_key *args)
    {
    	if (args == NULL)
    		return -1;
    	if (args->pCodecCtx)
    	{
    		avcodec_close(args->pCodecCtx);
    		args->pCodecCtx = NULL;
    	}
    	if (args->pFormatCtx)
    	{
    		avformat_close_input(&args->pFormatCtx);
    		args->pFormatCtx = NULL;
    	}
    	if (args->filt_frame != NULL)
    		av_frame_free(&args->filt_frame );
    	memset(args, 0, sizeof(Decode_Png_key));
    	return 0;
    }
    


    展开全文
  • PNG图片编解码 libpng PNG图片格式 PNG(Portable Network Graphics) 是一种光栅化的,无损压缩的图片文件格式。其设计的目的是替换GIF,是目前网络中用得最广的无损压缩图片格式。我们可以用工具将前面的...

    PNG图片格式

    PNG(Portable Network Graphics) 是一种光栅化的,无损压缩的图片文件格式。其设计的目的是替换GIF,是目前网络中用得最广的无损压缩图片格式。我们可以用工具将前面的Bitmap转换为PNG。

    下面是从上一章所说的 BMP转换过来的png_4x2_32bit.png的PNG图片,图片比较小,看仔细了:

    ![PNG图片样例](https://img-blog.csdn.net/20180403180016486?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoYW9qaWFuZ2x1bw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
    ## PNG特点 请参考[百度百科PNG图片特性](https://baike.baidu.com/item/png/174154?fr=aladdin#2)
    • 体积小
      网络通讯中因受带宽制约,在保证图片清晰、逼真的前提下,网页中不可能大范围的使用文件较大的bmp格式文件。
    • 无损压缩 PNG文件采用LZ77算法的派生算法进行压缩,其结果是获得高的压缩比,不损失数据。它利用特殊的编码方法标记重复出现的数据,因而对图像的颜色没有影响,也不可能产生颜色的损失,这样就可以重复保存而不降低图像质量。
    • 索引彩色模式
      PNG-8格式与GIF图像类似,同样采用8位调色板将RGB彩色图像转换为索引彩色图像。图像中保存的不再是各个像素的彩色信息,而是从图像中挑选出来的具有代表性的颜色编号,每一编号对应一种颜色,图像的数据量也因此减少,这对彩色图像的传播非常有利。
    • 更优化的网络传输显示
      PNG图像在浏览器上采用流式浏览,即使经过交错处理的图像会在完全下载之前提供浏览者一个基本的图像内容,然后再逐渐清晰起来。它允许连续读出和写入图像数据,这个特性很适合于在通信过程中显示和生成图像。
    • 支持透明效果
      PNG可以为原图像定义256个透明层次,使得彩色图像的边缘能与任何背景平滑地融合,从而彻底地消除锯齿边缘。这种功能是GIF和JPEG没有的。
    • PNG同时还支持真彩和灰度级图像的Alpha通道透明度。
    • 最高支持24位真彩色图像以及8位灰度图像。
    • 支持Alpha通道的透明/半透明特性。
    • 支持图像亮度的Gamma校准信息。
    • 支持存储附加文本信息,以保留图像名称、作者、版权、创作时间、注释等信息。
    • 渐近显示和流式读写,适合在网络传输中快速显示预览效果后再展示全貌。
    • 使用CRC防止文件出错。
    • 最新的PNG标准允许在一个文件内存储多幅图像。

    PNG文件结构

    PNG图像格式文件(或者称为数据流)由一个8字节的PNG文件署名(PNG file signature)域和按照特定结构组织的3个以上的数据块(chunk)组成。
    我们以二进制的形式将PNG图png_4x2_32bit.png打开:

    00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
    00000010: 0000 0004 0000 0002 0806 0000 007f a87d  ...............}
    00000020: 6300 0000 0173 5247 4200 aece 1ce9 0000  c....sRGB.......
    00000030: 0004 6741 4d41 0000 b18f 0bfc 6105 0000  ..gAMA......a...
    00000040: 0009 7048 5973 0000 0ec3 0000 0ec3 01c7  ..pHYs..........
    00000050: 6fa8 6400 0000 1849 4441 5418 5763 0082  o.d....IDAT.Wc..
    00000060: ffff 8118 4c81 2110 8029 b0e0 ffff 0005  ....L.!..)......
    00000070: 2b10 f0fa b027 6800 0000 0049 454e 44ae  +....'h....IEND.
    00000080: 4260 82                                  B`.
    
    • 文件头
      PNG文件包含8字节的签名:
    8950 4e47 0d0a 1a0a
    
    值Value 作用Purpose
    89 超出了ASCII字符的范围,避免某些软件将图片当着文本来处理
    50 4E 47 ‘PNG’的ASCII值,PNG文件的标识
    0D 0A Dos风格的的回车,检测DOS-Unix行结束数据的转换
    1A Dos风格的换行符号
    0A Unix风格的回车
    • 数据块
      PNG文件中包含3个以上的数据块,数据块间以特定的顺序组成。而数据块又分为关键数据块和辅助数据块。基本每个数据块都用下面的结构描述:
    名称 字节数 说明
    Length (长度) 4字节 指定数据块中数据域的长度,其长度不超过(231-1)字节
    Chunk Type Code (数据块类型码) 4字节 数据块类型码由ASCII字母(A-Z和a-z)组成
    Chunk Data (数据块数据) 可变长度 存储按照Chunk Type Code指定的数据
    CRC (循环冗余检测) 4字节 存储用来检测是否有错误的循环冗余码
    • 关键数据块包括
    数据块符号 数据块名称 多数据块 可选否 位置限制
    IHDR 文件头数据块 第一块
    IDAT 图像数据块 与其他IDAT连续
    IEND 图像结束数据 最后一个数据块

    和Bitmap相比,关键数据块多了一个IEND块。

    • IHDR数据块
      IHDR也称为文件头数据块,它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流(文件)中只能有一个文件头数据块。

    IHDR文件头数据块由13字节组成

    00000000: ---- ---- ---- ---- 0000 000d 4948 4452  .PNG........IHDR
    00000010: 0000 0004 0000 0002 0806 0000 007f a87d   ...............}
    00000020: 63-- ---- ---- ---- ---- ---- ---- ----  c....sRGB.......
    

    它的格式如下表所示:

    offset 值 Value 域的名称 字节数 说明
    0000 000d 13 size 4 数据块大小
    4948 4452 IHDR type IHDR的ASCII 数据块的类型
    0000 0004 4 Width 4 bytes 图像宽度,以像素为单位
    0000 0002 2 Height 4 bytes 图像高度,以像素为单位
    08 8 Bit depth 1 byte 图像深度:
    索引彩色图像:1,2,4或8
    灰度图像:1,2,4,8或16
    真彩色图像:8或16
    06 6 ColorType 1 byte 颜色类型:
    0:灰度图像, 1,2,4,8或16
    2:真彩色图像,8或16
    3:索引彩色图像,1,2,4或8
    4:带α通道数据的灰度图像,8或16
    6:带α通道数据的真彩色图像,8或16
    00 0 Compression method 1 byte 压缩方法(LZ77派生算法)
    00 0 Filter method 1 byte 滤波器方法
    00 0 Interlace method 1 byte 隔行扫描方法:
    0:非隔行扫描
    1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法)
    7fa8 7d63 CRC 4 CRC校验码

    按照我们的文件来~

    • sRGB数据块
    00000020: --00 0000 0173 5247 4200 aece 1ce9 ----  c....sRGB.......
    
    offset 值 Value 域的名称 字节数 说明
    00 0000 01 1 size 4 数据块大小
    73 5247 42 sRGB type sRGB的ASCII 数据块的类型
    00 0 sRGB模式 1 bytes 0 1 2 3四种模式可选,可以参考W3对sRGB的介绍
    aece 1ce9 CRC 4 CRC校验码

    不是所有的解码器都支持sRGB,所以有sRGB数据块,就必须有gAMA 数据块,也可以有cHRM 数据块,以兼容不支持sRGB的解码器。

    • gAMA 数据块
    00000020: ---- ---- ---- ---- ---- ---- ---- 0000  c....sRGB.......
    00000030: 0004 6741 4d41 0000 b18f 0bfc 6105 0000  ..gAMA......a...
    
    offset 值 Value 域的名称 字节数 说明
    0000 0004 4 size 4 数据块大小
    6741 4d41 gAMA type gAMA 的ASCII 数据块的类型
    0000 b18f 0 gama校验 1 bytes gama校验
    0bfc 6105 CRC 4 CRC校验码

    如果有sRGB数据块或者iCCP数据块,gAMA数据块就不用了,被override。

    • pHYs 数据块
      期望的物理像素的尺寸,或比例当显示图片时。
    00000030: ---- ---- ---- ---- ---- ---- ---- 0000  ..gAMA......a...
    00000040: 0009 7048 5973 0000 0ec3 0000 0ec3 01c7  ..pHYs..........
    00000050: 6fa8 64-- ---- ---- ---- ---- ---- ---- o.d....IDAT.Wc..
    
    offset 值 Value 域的名称 字节数 说明
    0000 0009 9 size 4 数据块大小
    7048 5973 pHYs type pHYs 的ASCII 数据块的类型
    0000 0ec3 3779 x axis 4 bytes gama校验
    0000 0ec3 3779 y axis 4 bytes gama校验
    01 1 Unit specifier 1 单位说明
    c76f a8 64 CRC 4 CRC校验码

    Unit specifier,有下列定义的值:
    0:unit is unknown;只是定义了缩放比例
    1:unit is the metre

    接下来终于到我们的数据块了

    • IDAT 数据块

    关键数据块,这里就数据就是png图片的数据

    00000050: ---- --00 0000 1849 4441 5418 5763 0082  o.d....IDAT.Wc..
    00000060: ffff 8118 4c81 2110 8029 b0e0 ffff 0005  ....L.!..)......
    00000070: 2b10 f0fa b027 6800 0000 0049 454e 44ae  +....'h....IEND.
    00000080: 4260 82                                  B`.
    
    offset 值 Value 域的名称 字节数 说明
    00 0000 18 24 size 4 数据块大小
    49 4441 54 IDAT type IDAT 的ASCII 数据块的类型
    1857 6300 82ff ff81
    184c 8121 1080 29b0
    e0ff ff00 052b 10f0
    png编码数据 24 png编码数据
    fab0 2768 CRC 4 CRC校验码
    • IEND 数据块
      关键数据块
    00000070: ---- ---- ---- --00 0000 0049 454e 44ae  +....'h....IEND.
    00000080: 4260 82                                  B`.
    
    offset 值 Value 域的名称 字节数 说明
    00 0000 00 4 size 4 数据块大小
    49 454e 44 IEND type IEND 的ASCII 数据块的类型
    ae 4260 82 CRC 4 CRC校验码

    以上就是PNG文件结构的描述~~~

    PNG压缩原理

    这部分请参考原博客: PNG格式图片原理

    PNG的压缩过程是完全无损的,压缩过的文件可以准确的还原出原图,这要分两个阶段完成:推断(又称过滤[filtering])和压缩。

    过滤

    差分编码(Delta encoding)是最强大的数字压缩法之一。原理是根据前一个数据的值将后面的值替换成其他值,例如:

    [2,3,4,5,6,7,8]可以变成[2,1,1,1,1,1,1],算法是
    [2, 3-2=1, 4-3=1, 5-4=1, 6-5=1, 7-6=1, 8-7=1]

    这样看就很明显了,如果你的数据是线性相关的(线性相关的意思是,一组数据里的前后值都差别不大,或者具有相关性),就可以把你的数据集转换成一组重复的、低的值,这样的值更容易被压缩。

    PNG格式使用了差分编码(Delta encoding)里的过滤。原理是,对于每一行的像素,一个当前的像素都跟它的左边像素、上边的像素和左上角的像素有关系。

    ![](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYwNTIyMjAzNjU0MzE2)

    举个例子,如果我们要编码一个给定的像素通过它与A和B平均值的差异(X-(A+B)/2),那么我们将得到:

    ![](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYwNTIyMjAzNjI3MDM0)

    我们使用了ABC去推断出X的值,然后我们将X替换成更小的值。

    需要注意的是,每一行的像素都有可能不同,PNG允许5种不同的推断算法,它们是:

    • 不过滤
    • X-A
    • X-B
    • X-(A+B)/2(又称平均值)
      Paeth推断(A,B,C的线性方法,这种比较复杂可看W3C的规定)

    这里说明一下,每一行像素应该选择最适合的过滤算法,这样才能得到最少数量的特殊值。下面是我们关于不同模式的例子:

    ![](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYwNTIyMjAzNzEyMjg0)

    需要注意的是这些过滤器都是对每一行像素起作用而不是单个像素。也就是说过滤器会对每一行的红色像素起作用,再分别对蓝色的像素起作用。(尽管同一行的像素会用同样的过滤器)

    现在PNG格式在选择过滤器上有一些不错的方法,开发人员根据对不同类型图片的使用经验摸索出一些不错的规律。例如对于调色板的图像(palette images)和8位的灰色图就不要过滤。对于其他图片,就选择那种能最大限度地减少绝对差异总和的值的过滤器:将所有值的绝对值相加,然后对比不同过滤器得到的值,选择那个相加起来得到最小值的过滤器。

    压缩
    在一行像素被过滤后,就会执行DEFLATE压缩,这是LZ77延伸出来的一种算法。该算法结合了LZ77编码和哈夫曼编码,它跟PKWARE、PKZIP、GZIP等差不多相同。这种实现方式虽然是现成的,但用在压缩图片数据上,还是有一些需要注意的点:

    • Deflate算法只能匹配3到258个之间符号,所以最大的压缩比只能到1035:1;
    • 如果匹配到的符号小于3,那么你会产生一些额外的开销来表示这些符号;

    上面的这两点意味着你的图片大小会受到每一行像素的匹配程度影响。

    你可以看一下面这两张图片,左边那张270x90的图只有20k,而右边那张270x92的图是左边那张的2倍大。

    ![](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYwNTIyMjAzNzI3NjI4)

    这似乎不符合逻辑,一张图片多540像素在压缩率上就少了一半。不过我们看仔细点,就能知道原因了,下面这张图表示压缩器怎么去压缩一个给定的像素的。深蓝色代表压缩率很高的区域,黄色/红色代表没怎么被压缩的区域。

    这是怎么出现的呢,原因是小图的每一行像素的匹配度更高,那么它的压缩率就更高。你要是调整了大小,匹配度一变化,这种情况就有可能出现。一些潜在的匹配对象不在压缩区域里,它们没有匹配到,这就又可能导致出现一张大图。

    ![](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYwNTIyMjAzNzM3Nzg4)

    如果你想知道你的PNG图片的压缩率如何,可以下个PNGThermal看一下。

    PNG图片编解码 libpng

    PNG图片的编解码,已经有很多开源的项目。libpng都比较成熟的官方的png编解码器,以动态库的形式提供,使用时load libpng的库就可以使用。LodePNG是一个集成度比较高的编解码器,没有依赖,不用加载zlib或libpng等,直接可以使用, https://github.com/lvandeve/lodepng。 LodePNG建议大家去了解一下,通过LodePNG能够很好理解PNG的编解码实现。我们这里侧重用libpng使用。

    Android平台上,libpng的源码在external/libpng目录下;当前的版本应该是16.34。

    https://sourceforge.net/projects/libpng/files/libpng16/1.6.34/libpng-1.6.34.tar.gz
    

    png采用的压缩算法是分开的:

    git clone https://github.com/madler/zlib.git
    

    我们用Android Studio,创建一个纯Native的应用,直接在 native中去编译,使用libpng和zlib。

    样例代码请参考github Codec-PngCodec , 这是我们的PngCodec的文件结构:

    ├── build.gradle
    ├── CMakeLists.txt
    ├── libs
    ├── png
    │   └── libpng-1.6.34
    ├── src
    │   ├── AndroidManifest.xml
    │   ├── cpp
    │   │   └── PngCodecNativeActivity.cpp
    │   ├── java
    │   └── res
    └── zlib
        └── zlib-1.2.11
    

    将libpng和zlib,放到工程目录下。将工程连到CMakeLists.txt。

    zlib的CMake

    add_library( zlib
                 STATIC
                 zlib/zlib-1.2.11/adler32.c
                 zlib/zlib-1.2.11/compress.c
                 zlib/zlib-1.2.11/crc32.c
                 zlib/zlib-1.2.11/deflate.c
                 zlib/zlib-1.2.11/gzclose.c
                 zlib/zlib-1.2.11/gzlib.c
                 zlib/zlib-1.2.11/gzread.c
                 zlib/zlib-1.2.11/gzwrite.c
                 zlib/zlib-1.2.11/infback.c
                 zlib/zlib-1.2.11/inflate.c
                 zlib/zlib-1.2.11/inftrees.c
                 zlib/zlib-1.2.11/inffast.c
                 zlib/zlib-1.2.11/trees.c
                 zlib/zlib-1.2.11/uncompr.c
                 zlib/zlib-1.2.11/zutil.c )
    
    target_include_directories(zlib PRIVATE
                zlib/zlib-1.2.11 )
    

    libpng的CMake

    add_library( png
                 STATIC
                 png/libpng-1.6.34/png.c
                 png/libpng-1.6.34/pngerror.c
                 png/libpng-1.6.34/pngget.c
                 png/libpng-1.6.34/pngmem.c
                 png/libpng-1.6.34/pngpread.c
                 png/libpng-1.6.34/pngread.c
                 png/libpng-1.6.34/pngrio.c
                 png/libpng-1.6.34/pngrtran.c
                 png/libpng-1.6.34/pngrutil.c
                 png/libpng-1.6.34/pngset.c
                 png/libpng-1.6.34/pngtrans.c
                 png/libpng-1.6.34/pngwio.c
                 png/libpng-1.6.34/pngwrite.c
                 png/libpng-1.6.34/pngwtran.c
                 png/libpng-1.6.34/pngwutil.c )
    
    target_include_directories(png PRIVATE
                png/libpng-1.6.34
                zlib/zlib-1.2.11 )
    

    native应用的CMake

    add_library( png_codec
                 SHARED
                 src/main/cpp/PngCodecNativeActivity.cpp )
    
    target_include_directories(png_codec PRIVATE
                 ${ANDROID_NDK}/sources/android/native_app_glue
                 png/libpng-1.6.34
                 zlib/zlib-1.2.11 )
    
    target_link_libraries( png_codec
                           native_activity_glue
                           android
                           zlib
                           png
                           log )
    

    Native应用我们编译为libpng_codec.so,在AndroidManifest中使用:

        <application
            ...
            android:hasCode="false">
            <activity android:name="android.app.NativeActivity">
                <meta-data
                    android:name="android.app.lib_name"
                    android:value="png_codec" />
    

    编译时,pnglibconf.h需要放在png/libpng-1.6.34目录下,从scripts/pnglibconf.h.prebuilt拷贝。我们这边注释掉NEON的支持。

    下面是一个简易的读取png图片的实现:

    void readPngFile(char *name) {
        ALOGE("readPngFile %s\n",  name);
        // 前边几句是扯淡,初始化各种结构
        FILE *file = fopen(name, "rb");
    
        png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
    
        png_infop info_ptr = png_create_info_struct(png_ptr);
    
        setjmp(png_jmpbuf(png_ptr));
    
        // 这句很重要
        png_init_io(png_ptr, file);
    
        // 读文件了
        png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
    
        // 得到文件的宽高色深
        int m_width = png_get_image_width(png_ptr, info_ptr);
        int m_height = png_get_image_height(png_ptr, info_ptr);
    
        int color_type = png_get_color_type(png_ptr, info_ptr);
    
        // 申请个内存玩玩,这里用的是c++语法,甭想再c上编过
        int size = m_height * m_width * 4;
    
        char* head = static_cast<char*>(malloc(size));
    
        int pos = 0;
    
        // row_pointers里边就是传说中的rgba数据了
        png_bytep *row_pointers = png_get_rows(png_ptr, info_ptr);
    
        // 拷贝!!注意,如果你读取的png没有A通道,就要3位3位的读。还有就是注意字节对其的问题,最简单的就是别用不能被4整除的宽度就行了。读过你实在想用,就要在这里加上相关的对齐处理。
        ALOGE("png file %s size(%dx%d) pixles:", name, m_width, m_height);
        for (int i = 0; i < m_height; i++) {
            for (int j = 0; j < (4 * m_width); j += 4) {
                head[pos] = row_pointers[i][j + 2]; // blue
                pos++;
                head[pos] = row_pointers[i][j + 1]; // green
                pos++;
                head[pos] = row_pointers[i][j];   // red
                pos++;
                head[pos] = row_pointers[i][j + 3]; // alpha
                pos++;
                ALOGE("%02x %02x %02x %02x", head[pos-4], head[pos-3], head[pos-2], head[pos-1]);
            }
        }
    
        free(head);
    
        // 好了,你可以用这个数据作任何的事情了。。。把它显示出来或者打印出来都行。
        png_destroy_read_struct(&png_ptr, &info_ptr, 0);
    
        fclose(file);
    
        return;
    }
    

    我们将我们的png_4x2_32bit.png,读出来,pixels为:

    0000 00 ff 0000 ffff ff00 00ff 00ff 00ff
    ffff ffff 00ff 00ff 0000 ffff ff00 00ff
    

    还记得bmp的时候,你要转为png之前,pixels是什么样的吗?

    ffff ff7f 00ff 007f 0000 ff7f ff00 007f 0000 0000 0000 ff00 ff00 0000 00ff 0000
    

    对比一下:

    • 转换的时候,Alpha被去掉了,都设置为不透明的
    • png的扫描方式和bmp吧一样,png是从左上开始扫,bmp却是从左下。

    再回过头看看测试代码:

    png信息用结构体png_inforp描述,本体是png_info_def,定义在pnginfo.h中。

    struct png_info_def
    {
       /* The following are necessary for every PNG file */
       png_uint_32 width;  /* width of image in pixels (from IHDR) */
       png_uint_32 height; /* height of image in pixels (from IHDR) */
       png_uint_32 valid;  /* valid chunk data (see PNG_INFO_ below) */
       png_size_t rowbytes; /* bytes needed to hold an untransformed row */
       png_colorp palette;      /* array of color values (valid & PNG_INFO_PLTE) */
       png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
       png_uint_16 num_trans;   /* number of transparent palette color (tRNS) */
       png_byte bit_depth;      /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
       png_byte color_type;     /* see PNG_COLOR_TYPE_ below (from IHDR) */
       /* The following three should have been named *_method not *_type */
       png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
       png_byte filter_type;    /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
       png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
    
       ... ...
    };
    

    png文件用结构体png_structp描述,本体是png_struct_def,定义在pngstruct.h中。

    struct png_struct_def
    {
    #ifdef PNG_SETJMP_SUPPORTED
       jmp_buf jmp_buf_local;     /* New name in 1.6.0 for jmp_buf in png_struct */
       png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */
       jmp_buf *jmp_buf_ptr;      /* passed to longjmp_fn */
       size_t jmp_buf_size;       /* size of the above, if allocated */
    #endif
       png_error_ptr error_fn;    /* function for printing errors and aborting */
    #ifdef PNG_WARNINGS_SUPPORTED
       png_error_ptr warning_fn;  /* function for printing warnings */
    #endif
       png_voidp error_ptr;       /* user supplied struct for error functions */
       png_rw_ptr write_data_fn;  /* function for writing output data */
       png_rw_ptr read_data_fn;   /* function for reading input data */
       png_voidp io_ptr;          /* ptr to application struct for I/O functions */
       
       ... ...
    

    libpng都是用c写的,用的结构体,这样实现,C/C++中都可以用。

    png信息的读取,用的png_read_png接口,定义在pngread.c中。在png_read_info函数中,采用循环的模式,将PNG的所有数据块数据都读出来。比如IHDR数据块,采用png_handle_IHDR进行处理。

    /* Read and check the IDHR chunk */
    
    void /* PRIVATE */
    png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
    {
       png_byte buf[13];
       png_uint_32 width, height;
       int bit_depth, color_type, compression_type, filter_type;
       int interlace_type;
    
       png_debug(1, "in png_handle_IHDR");
    
       if ((png_ptr->mode & PNG_HAVE_IHDR) != 0)
          png_chunk_error(png_ptr, "out of place");
    
       /* Check the length */
       if (length != 13)
          png_chunk_error(png_ptr, "invalid");
    
       png_ptr->mode |= PNG_HAVE_IHDR;
    
       png_crc_read(png_ptr, buf, 13);
       png_crc_finish(png_ptr, 0);
    
       width = png_get_uint_31(png_ptr, buf);
       height = png_get_uint_31(png_ptr, buf + 4);
       bit_depth = buf[8];
       color_type = buf[9];
       compression_type = buf[10];
       filter_type = buf[11];
       interlace_type = buf[12];
    
       /* Set internal variables */
       png_ptr->width = width;
       png_ptr->height = height;
       png_ptr->bit_depth = (png_byte)bit_depth;
       png_ptr->interlaced = (png_byte)interlace_type;
       png_ptr->color_type = (png_byte)color_type;
    #ifdef PNG_MNG_FEATURES_SUPPORTED
       png_ptr->filter_type = (png_byte)filter_type;
    #endif
       png_ptr->compression_type = (png_byte)compression_type;
    
       /* Find number of channels */
       switch (png_ptr->color_type)
       {
          default: /* invalid, png_set_IHDR calls png_error */
          case PNG_COLOR_TYPE_GRAY:
          case PNG_COLOR_TYPE_PALETTE:
             png_ptr->channels = 1;
             break;
    
          case PNG_COLOR_TYPE_RGB:
             png_ptr->channels = 3;
             break;
    
          case PNG_COLOR_TYPE_GRAY_ALPHA:
             png_ptr->channels = 2;
             break;
    
          case PNG_COLOR_TYPE_RGB_ALPHA:
             png_ptr->channels = 4;
             break;
       }
    
       /* Set up other useful info */
       png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * png_ptr->channels);
       png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width);
       png_debug1(3, "bit_depth = %d", png_ptr->bit_depth);
       png_debug1(3, "channels = %d", png_ptr->channels);
       png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes);
       png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
           color_type, interlace_type, compression_type, filter_type);
    }
    

    IHDR的大小为13,所以,直接申请13byte的buf。信息保存到info_ptr中。

    图片数据,通过png_read_image进行获取。

    void PNGAPI
    png_read_image(png_structrp png_ptr, png_bytepp image)
    {
       png_uint_32 i, image_height;
       int pass, j;
       png_bytepp rp;
    
       png_debug(1, "in png_read_image");
    
       if (png_ptr == NULL)
          return;
    
    #ifdef PNG_READ_INTERLACING_SUPPORTED
       if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0)
       {
          pass = png_set_interlace_handling(png_ptr);
          /* And make sure transforms are initialized. */
          png_start_read_image(png_ptr);
       }
       else
       { 
          if (png_ptr->interlaced != 0 &&
              (png_ptr->transformations & PNG_INTERLACE) == 0)
          {
             /* Caller called png_start_read_image or png_read_update_info without
              * first turning on the PNG_INTERLACE transform.  We can fix this here,
              * but the caller should do it!
              */
             png_warning(png_ptr, "Interlace handling should be turned on when "
                 "using png_read_image");
             /* Make sure this is set correctly */
             png_ptr->num_rows = png_ptr->height;
          }
    
          /* Obtain the pass number, which also turns on the PNG_INTERLACE flag in
           * the above error case.
           */
          pass = png_set_interlace_handling(png_ptr);
       }
    #else
       if (png_ptr->interlaced)
          png_error(png_ptr,
              "Cannot read interlaced image -- interlace handler disabled");
    
       pass = 1;
    #endif
    
       image_height=png_ptr->height;
    
       for (j = 0; j < pass; j++)
       {
          rp = image;
          for (i = 0; i < image_height; i++)
          {
             png_read_row(png_ptr, *rp, NULL);
             rp++;
          }
       }
    }
    

    在png_read_row中,通过png_read_IDAT_data去获取pixels数据。这里的数据是经过压缩的,需要用到zlib进行解压缩。解压用的zlib的inflateInit() + inflate() + inflateEnd()等函数。

    解压用到z_stream_s结构,用以读写zlib输入输出的数据。

    typedef struct z_stream_s {
        z_const Bytef *next_in;     /* next input byte */
        uInt     avail_in;  /* number of bytes available at next_in */
        uLong    total_in;  /* total number of input bytes read so far */
    
        Bytef    *next_out; /* next output byte will go here */
        uInt     avail_out; /* remaining free space at next_out */
        uLong    total_out; /* total number of bytes output so far */
    
        z_const char *msg;  /* last error message, NULL if no error */
        struct internal_state FAR *state; /* not visible by applications */
    
        alloc_func zalloc;  /* used to allocate the internal state */
        free_func  zfree;   /* used to free the internal state */
        voidpf     opaque;  /* private data object passed to zalloc and zfree */
    
        int     data_type;  /* best guess about the data type: binary or text
                               for deflate, or the decoding state for inflate */
        uLong   adler;      /* Adler-32 or CRC-32 value of the uncompressed data */
        uLong   reserved;   /* reserved for future use */
    } z_stream;
    
    

    我们可以自己加log,将解压的PNG图片的像素数据打印出来

    PngCodec: png_show_byte bnext_in 1857630082FFFF81184C8121108029B0E0FFFF00052B10F0
    

    这和我们用二进制编辑器编辑png图片时的文件是不是一样的?

    • libpng编码
      编码和解码类似,编码时提供png_write_infopng_write_image,一个写Png文件信息,一个写像素点。我们来看实例。前面我们用png转码工具将bmp转换为png时,丢了Alpha信息,我们现在将Alpha信息加上。
    void writePngFile(char *fileName, png_byte* src , int width, int height)
    {
        png_structp png_ptr;
        png_infop info_ptr;
        png_colorp palette;
    
        FILE *fp = fopen(fileName, "wb");
        if (fp == NULL)
            return ;
    
        png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
        if (png_ptr == NULL)
        {
            fclose(fp);
            return ;
        }
    
        /* Allocate/initialize the image information data.  REQUIRED */
        info_ptr = png_create_info_struct(png_ptr);
        if (info_ptr == NULL)
        {
            fclose(fp);
            png_destroy_write_struct(&png_ptr,  NULL);
            return ;
        }
        if (setjmp(png_jmpbuf(png_ptr)))
        {
            /* If we get here, we had a problem writing the file */
            fclose(fp);
            png_destroy_write_struct(&png_ptr, &info_ptr);
            return ;
        }
    
        /* 接下来告诉 libpng 用 fwrite 来写入 PNG 文件,并传给它已按二进制方式打开的 FILE* fp */
        png_init_io(png_ptr, fp);
    
        /* 设置png文件的属性 */
        png_set_IHDR(png_ptr, info_ptr, width, height, 8,
                     PNG_COLOR_TYPE_RGB,
                     PNG_INTERLACE_NONE,
                     PNG_COMPRESSION_TYPE_BASE,
                     PNG_FILTER_TYPE_BASE);
    
        /* 分配调色板空间。常数 PNG_MAX_PALETTE_LENGTH 的值是256 */
        palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
    
        png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
    
        /* Write the file header information.  REQUIRED */
        png_write_info(png_ptr, info_ptr);
    
        /* The easiest way to write the image (you may have a different memory
        * layout, however, so choose what fits your needs best).  You need to
        * use the first method if you aren't handling interlacing yourself.
        */
        png_uint_32 k;
        png_byte *image;
        png_bytep row_pointers[height];
    
        image = src;
    
        if (height > PNG_UINT_32_MAX/sizeof(png_bytep))
            png_error (png_ptr, "Image is too tall to process in memory");
    
        for (k = 0; k < height; k++)
            row_pointers[k] = image + k*width*3;
    
        /* One of the following output methods is REQUIRED */
        png_write_image(png_ptr, row_pointers);
    
        //end,进行必要的扫尾工作:
        png_write_end(png_ptr, info_ptr);
        png_free(png_ptr, palette);
        png_destroy_write_struct(&png_ptr, &info_ptr);
    
        fclose(fp);
        ALOGE("success write png file %s\n", fileName);
        return ;
    }
    

    记住,PNG图片是从左上开始扫描的,切格式为RGBA,所以,pixel是数据要对应的调整一下。

    void buildPngFile(char *fileName) {
        int width = 4;
        int height = 2;
    
        png_byte src[] = {
                0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff,
                0xff, 0xff, 0xff, 0x7f, 0x00, 0xff, 0x00, 0x7f, 0xff, 0x00, 0x00, 0x7f, 0x00, 0x00, 0xff, 0x7f
        };
    
        writePngFile(fileName,src,width, height, PNG_COLOR_TYPE_RGBA);
    
        readPngFile(fileName);
    }
    

    log打出来的数据:

    ~ E/PngCodec: 00 00 00 ff
    ~ E/PngCodec: 00 00 ff ff
    ~ E/PngCodec: ff 00 00 ff
    ~ E/PngCodec: 00 ff 00 ff
    ~ E/PngCodec: ff ff ff 7f
    ~ E/PngCodec: 00 ff 00 7f
    ~ E/PngCodec: 00 00 ff 7f
    ~ E/PngCodec: ff 00 00 7f
    

    保留Alpha后的PNG图片如下:

    ![保留Alpha后的PNG图片](https://img-blog.csdn.net/2018040318184773?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoYW9qaWFuZ2x1bw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    放大后的效果:

    ![放大后的效果](https://img-blog.csdn.net/20180403181934551?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoYW9qaWFuZ2x1bw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    上面一行不透明,下面一行半透明。

    Android平台上,一般不直接去调用libpng的库,Android对编解码提供了统一的管理,这个角色为skia。skia中除了png还可以用其他的,比如jpg,gif等。

    Png就介绍到这里,简要的介绍了PNG文件格式,以及用libpng对Png图片进行编解码。再深的,大家继续去探索。

    实例代码可以高github里面去下载哦~
    Codec-PngCodec

    展开全文
  • 0 引 言   PNG(Portable Network Graphic Format)是流式网络图形格式的简称,是一种位图...PNG文件采用压缩率高的LZ77和Huffman两种无损压缩算法,支持网络彩色图像传输,支持Alpha通道、定义透明区域和多重透明

    http://www.21ic.com/app/ce/200905/42770_3.htm

    0 引 言 

        PNG(Portable Network Graphic Format)是流式网络图形格式的简称,是一种位图文件(Bitmap File)存储格式。PNG文件采用压缩率高的LZ77和Huffman两种无损压缩算法,支持网络彩色图像传输,支持Alpha通道、定义透明区域和多重透明,逐步细化地显示图片。
        PNG压缩的核心算法是采用Zip压缩算法,该算法的特点就是先利用LZ77算法进行短语式重复的压缩得到未匹配的字节和匹配长度、距离的组合值,然后再根据Huffman算法进行单字节重复的压缩最终得到压缩码流。PNG解码的原理也就是压缩的反过程,那么解码时可根据码表信息和压缩码流还原出原始图像数据。
        PNG文件的解码通常由软件完成,软件解码实现方式灵活,但相对硬件解码而言,软件解码速度慢,能量消耗大,不利于移动设备的低功耗设计优化。为此,这里讨论了PNG图像的硬件解码实现方法,其应用对象是手机专用芯片,对低功耗和解码速度都有较高的要求,并解决了PNG解码的快速查表、软硬件协调和硬件加速等实现方法,而硬件加速解码功能的主要作用是减少CPU的负担,极大加快PNG图片显示速度,并在一定程度上减少了功耗,延长了手机的待机时间,具有很大研究与开发的实际价值。

    1 PNG图像解码原理的介绍
    1.1 LZ77算法介绍

        LZ77算法可以称为“滑动窗口压缩”,该算法将一个虚拟的,可以跟随压缩进程滑动的窗口作为术语字典;要压缩的字符串若在窗口中出现,则输出匹配长度和距离的组合信息,来替换前面出现的相同字符串,且要求最小匹配的字符串为3个字节,这样可以保证压缩后的数据量小于原始数据。
        例如窗口的大小为15个字符,冈0刚编码过的15个字符为:byhelloeveryone,即将编码的字符为:helloto—e、,eryonehi。可以发现有些字符串前面已经出现过,则用()起来的字符串表示滑动窗口中已出现过的匹配串:(hello)to(everyone)hi。
        以上这些原始信息,可利用LZ77算法用匹配长度和距离的组合信息来替换有匹配的字符串,若碰到未匹配的字节则直接输出,压缩后的内容为:(5,13)to(8,15)hi。在LZ77解压缩时,只要维护好滑动窗口,随着压缩信息的不断输入,可根据匹配的组合信息从窗口中找到相应的匹配字符串作为输出,即可还原出原始数据。
    1.2 Huffman算法介绍
       
    Huffman算法属于编码式压缩,利用各个单字节所使用频率不一样的特点,使定长编码转变为变长的编码,给频率高的字节更短的编码,使用频率低的字节更长的编码,起到无损压缩的效果。这样,经过LZ77压缩后的未匹配的字节和匹配的组合信息可以进一步地进行Huffman压缩,从而得到很高的压缩效率。
        例如,对于一组元素的字符值为s={a,b,c,d,e,f},其对应的出现频率为P={10,2,2,2,2,9}。图1是根据以上信息建立的Huffman树。各元素出现频率和元素值如图1所示,编码后的各个元素长度分别为L一{1,3,3,3,3,2},可见编码后储存这些字符值所需的空间极大地减少了。

        这棵Huffman树是根据PNG规范的Dellate原则建立的,具有以下特点:
        (1)左边的叶子编码为0,右边的为1;
        (2)编码必须满足“前缀编码”的要求,即较短的编码不能是较长编码的前缀,这保证了码的惟一性;
        (3)每一层叶子的节点频率按从小到大排列,而同样频率的节点按字符值从小到大排列,这点也是PNG采用的zip算法对Huffman算法的一种改进。因此,解码时首先要提取出压缩流中的码表信息建立出Huffman树,其中每个叶结点应包含有码长和字符值信息,并把最终生成的码表保存在RAM中供给Huff_man解码模块查表还原出图像原始数据。

    2 PNG解码的软硬件协调机制
        整个PNG硬件解码过程都是由软件来调度的,在硬件解码中若校验到图片数据出错或解码完成时,则PNG硬件模块通过配置专门的寄存器给软件检查做中断处理;当软件检测到这个寄存器信号使能时就产生中断,就能即使关闭PNG硬件解码模块,在数据有误的情况下节省了硬件解码的功耗。
        解码前后数据的搬运机制是通过公用的AVI模块(相当于FIF0实现输入输出数据的缓存)实现了PNG数据的搬运:解码前,软件通过调配AVI模块从内存中搬取压缩数据给PNG硬件模块做解码;解码后的数据经过Resize模块缩放后又可以利用AVI搬运给VGA做显示,这种较优的软件调配机制,解决了该设计的软硬件协调问题,可以在节省功耗的前提下实现高效率的解码,具体的软件硬件协调原理如图2所示。

    3 PNG解码的总体硬件结构
        PNG硬件解码加速的整体结构主要由Bytesshift字符容器、PNG头信息处理模块、Inflate table建Huffman表模块、Inflate fast快速解码模块、Lz77寻找匹配串模块、Filter反滤波反交织模块和Resize放大缩小模块7大模块组成。具体PNG解码的硬件流程图如图3所示。

        如图3可见,PNG解码的基本流程为:通过AVI模块从总线上搬取压缩数据到Bytesshift字符容器进行缓存,并转换为压缩比特流;通过PNG头信息处理模块保留下文件的头信息,通过控制Inflate table模块读取码长信息来建立Huffman表,并对压缩数据进行解码;解码后的数据经过Filter模块进行反滤波和反交织等处理,然后发给Resize模块做放大缩小处理后,并通过AVI模块将最终解码后的数据传输出去。其中,解码核心模块和Filter模块通过采用了数据的流水线处理方式,也极大地提高了.PNG的解码效率。

    4 PNG核心解码模块的硬件结构
        由于编码长度可变和编码长度不统一,解码时若按位比较来查找Huffman表会消耗很多时间,且PNG数据流中Huffman编码的最长码长为9。因此,为了实现快速查表解码,本算法中将码长小于9的Huffman树的叶结点作为父结点来扩展到9层,即扩展出来的叶结点信息都同父结点一样,每次用固定的9比特压缩数据作为地址去查表。这样可以保证在每一个时钟内都可以查找到相应的字符值,就可以极大地提高硬件解码的效率。以前面的Huffman树为例子(如图4所示),简单地将第4层以内的叶结点补充到第4层,即把整个Huffman二叉树补满,则在第4层的子叶结点的长度和字符信息都同父结点一样。

        这种扩展Huffman树的方法,可以实现迅速查找Huffman表,得到相应的字符值和匹配的组合信息值,对解出匹配的组合信息值,则根据LZ77原则还原出解码数据作为输出。
        该设计中的硬件解码核心模块可参考图5。这种硬件结构的优点是利用扩展码表的方法实现快速解码。核心解码的基本流程为:每次用固定的9 b压缩数据作为地址去查表,查出包含有码长和字符信息的叶结点,并根据码长信息从字符容器模块移出使用过的压缩数据,并等待新进的压缩数据与字符容器剩余的压缩数据组成新的9 b数据作为查表地址。在下一个时钟重复查表的过程,以此方式反复查表直至Huffman解码结束。

    5 仿真和综合结果
       
    经Modelsim 6.3仿真提取出解码后数据,在Matlab工具中进行对原图显示与该设计解码后提取出的图像数据进行对比,比较结果完全一致,并且在验证平台上比较其对应的原始图像数据也完全吻合,因此,该硬件设计能够完全不失真的恢复PNG图像数据。
        在设计中,使用台积电90 nlTl的工艺库,在100 MHz的频率下对PNG解码核心模块用DC进行综合,结果如表1所示。(其中面积大小和功耗不包括RAM的面积和读写RAM的功耗)

    6 结 语
        这里讨论了PNG解码加速的硬件实现方法。其中分析了LZ77和Huffman两种算法的硬件解码原理,以及采用补满Huffman树的机制实现快速查表解码,并运用较优的软硬件协调机制,在节省功耗前的前提下实现PNG硬件解码加速。


    展开全文
  • 一般我们都是用ffmpeg来解码音视频,如果是JPG和PNG图片呢,其实跟解码视频是一样的,因为视频也是一幅一幅的图片进行解码的,只不过视频的帧是会前后参考的,而JPG等图片来讲,就是独立的一帧而已。 那么,我们...

        一般我们都是用ffmpeg来解码音视频,如果是JPG和PNG等图片呢,其实跟解码视频是一样的,因为视频也是一幅一幅的图片进行解码的,只不过视频的帧是会前后参考的,而JPG等图片来讲,就是独立的一帧而已。
    那么,我们参考之前的一篇文章《一段ffmpeg视频解码为YUV420P的示例代码》,稍作修改即可来演示。
        同时为了能够保存解码后的图片,我们还需要了解一些YUV或者RGB等各种格式的数据的内存存储方式,这些知识可以参照我之前的另一篇文章《YUV420格式解析》,这里详细描述了各种格式的空间存储机制。
        一般解码视频时,我们在调用ffmpeg进行解码时,生成的格式一般都是YUV420P的,但解码图皮时可能会有各种形式,如YUVJ422P、YUVJ444P、RGB24等等,本文没有采用ffmpeg的sws_scale函数做统一的转
    换,为的是记录如何来存储这些解码后的图片。

         先给出如何从ffmpeg的Frame结构体中保存上述的四种解码后的数据:

    /**
     * save yuv420p frame [YUV]
     */
    void yuv420p_save(AVFrame *pFrame, AVCodecContext *pCodecCtx)
    {
    	int i = 0;
    
    	int width = pCodecCtx->width, height = pCodecCtx->height;
    	int height_half = height / 2, width_half = width / 2;
    	int y_wrap = pFrame->linesize[0];
    	int u_wrap = pFrame->linesize[1];
    	int v_wrap = pFrame->linesize[2];
    
    	unsigned char *y_buf = pFrame->data[0];
    	unsigned char *u_buf = pFrame->data[1];
    	unsigned char *v_buf = pFrame->data[2];
    
    	//save y
    	for (i = 0; i < height; i++)
    		fwrite(y_buf + i * y_wrap, 1, width, pfout);
    	fprintf(stderr, "===>save Y success\n");
    	//save u
    	for (i = 0; i < height_half; i++)
    		fwrite(u_buf + i * u_wrap, 1, width_half, pfout);
    	fprintf(stderr, "===>save U success\n");
    	//save v
    	for (i = 0; i < height_half; i++)
    		fwrite(v_buf + i * v_wrap, 1, width_half, pfout);
    	fprintf(stderr, "===>save V success\n");
    
    	fflush(pfout);
    }
    
    /**
     * save yuv422p frame [YUV]
     */
    void yuv422p_save(AVFrame *pFrame, AVCodecContext *pCodecCtx)
    {
    	int i = 0;
    
    	int width = pCodecCtx->width, height = pCodecCtx->height;
    	int height_half = height / 2, width_half = width / 2;
    	int y_wrap = pFrame->linesize[0];
    	int u_wrap = pFrame->linesize[1];
    	int v_wrap = pFrame->linesize[2];
    
    	unsigned char *y_buf = pFrame->data[0];
    	unsigned char *u_buf = pFrame->data[1];
    	unsigned char *v_buf = pFrame->data[2];
    
    	//save y
    	for (i = 0; i < height; i++)
    		fwrite(y_buf + i * y_wrap, 1, width, pfout);
    	fprintf(stderr, "===>save Y success\n");
    	//save u
    	for (i = 0; i < height; i++)
    		fwrite(u_buf + i * u_wrap, 1, width_half, pfout);
    	fprintf(stderr, "===>save U success\n");
    	//save v
    	for (i = 0; i < height; i++)
    		fwrite(v_buf + i * v_wrap, 1, width_half, pfout);
    	fprintf(stderr, "===>save V success\n");
    
    	fflush(pfout);
    }
    
    /**
     * save rgb24 frame [PPM]
     */
    void rgb24_save(AVFrame *pFrame, AVCodecContext *pCodecCtx)
    {
    	int i = 0;
    	int width = pCodecCtx->width, height = pCodecCtx->height;
    	
    	/* write PPM header */
    	fprintf(pfout, "P6\n%d %d\n255\n", width, height);
      
    	/* write pixel data */
    	for(i =0; i < height; i++)
    		fwrite(pFrame->data[0] + i * pFrame->linesize[0], 1, width * 3, pfout);
        
    	fflush(pfout);
    }
    
    /**
     * save yuv444p frame [YUV]
     */
    void yuv444p_save(AVFrame *pFrame, AVCodecContext *pCodecCtx)
    {
    	int i = 0;
    
    	int width = pCodecCtx->width, height = pCodecCtx->height;
    	int y_wrap = pFrame->linesize[0];
    	int u_wrap = pFrame->linesize[1];
    	int v_wrap = pFrame->linesize[2];
    
    	unsigned char *y_buf = pFrame->data[0];
    	unsigned char *u_buf = pFrame->data[1];
    	unsigned char *v_buf = pFrame->data[2];
    
    	//save y
    	for (i = 0; i < height; i++)
    		fwrite(y_buf + i * y_wrap, 1, width, pfout);
    	fprintf(stderr, "===>save Y success\n");
    	//save u
    	for (i = 0; i < height; i++)
    		fwrite(u_buf + i * u_wrap, 1, width, pfout);
    	fprintf(stderr, "===>save U success\n");
    	//save v
    	for (i = 0; i < height; i++)
    		fwrite(v_buf + i * v_wrap, 1, width, pfout);
    	fprintf(stderr, "===>save V success\n");
    
    	fflush(pfout);
    }

         可以对照各种格式看一下代码,应该是很好理解的,下面是其余的main代码,可以编译运行/**
     
    /**
     * decode picture by ffmpeg-1.0 for jpg and png ... 
     *
     * 2013-05-14
     *    juguofeng<jgfntu@gmail.com>
     */
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    
    
    FILE *pfout = NULL;
    char ffrvout[128] = { 0 };
    
    /* how many yuv pic you want to save */
    #define FRAME_NUM 1
    /* enable video demux data save to file */
    //#define ENABLE_DEMUX_SAVE
    /* enable yuv pic save to file */
    #define ENABLE_YUV_SAVE
    /* enable print each video bytes */
    #define ENABLE_PRINT_FRAME_BYTES
    /* how many bytes you want to print */
    #define PRINT_BYTES 30
    
    /* ------------------------------------------------------------------------- */
    
    /**
     * main thread
     */
    int main(int argc, char *argv[])
    {
    	int i;
    	char szFileName[128] = {0};
    	int decLen = 0;
    	int frame = 0;
    
    	AVCodecContext *pCodecCtx = NULL;
    	AVFrame *pFrame = NULL;
    	AVCodec *pCodec = NULL;
    	AVFormatContext *pFormatCtx = NULL;
    
    	if(argc != 3)
    	{
    		fprintf(stderr, "ERROR:need 3 argument!\n");
    		exit(-1);
    	}
    	
    	sprintf(szFileName, "%s", argv[1]);
    
    #ifdef ENABLE_DEMUX_SAVE
    	FILE* frvdemux = fopen("rvdemuxout.rm","wb+");
    	if (NULL == frvdemux)
    	{
    		fprintf(stderr, "create rvdemuxout file failed\n");
    		exit(1);
    	}    
    #endif
    
    	/* output yuv file name */
    	sprintf(ffrvout, "%s", argv[2]);
    
    	pfout = fopen(ffrvout, "wb+");
    	if (NULL == pfout)
    	{
    		printf("create output file failed\n");
    		exit(1);
    	}
    	printf("==========> Begin test ffmpeg call ffmpeg rv decoder\n");
    	av_register_all();
    
    	/* Open input video file */
    	//printf("before avformat_open_input [%s]\n", szFileName);
    	if(avformat_open_input(&pFormatCtx, szFileName, NULL, NULL)!= 0)
    	{
    		fprintf(stderr, "Couldn't open input file\n");
    		return -1;
    	}
    	//printf("after avformat_open_input\n");
    
    	/* Retrieve stream information */
    	if(av_find_stream_info(pFormatCtx) < 0)
    	{
    		printf("av_find_stream_info ERROR\n");
    		return -1;
    	}
    	//printf("after av_find_stream_info, \n");
    
    
    	/* Find the first video stream */
    	int videoStream = -1;
    	printf("==========> pFormatCtx->nb_streams = %d\n", pFormatCtx->nb_streams);
    
    	for(i = 0; i < pFormatCtx->nb_streams; i++) {
    		if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
    			videoStream = i;
    			printf("the first video stream index: videoStream = %d\n",videoStream);
    			break;
    		}
    	}
    
    	if(videoStream == -1)
    		return -1;		// Didn't find a video stream
    
    	/* Get a pointer to the codec context for the video stream */
    	pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    	printf("pCodecCtx->codec_id = %d\n", pCodecCtx->codec_id);
    
    	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    	if(pCodec == NULL) {
    		fprintf(stderr, "can not find decoder!\n");
    		return -1;
    	}
    
    	/* Open codec */
    	if(avcodec_open(pCodecCtx, pCodec)<0)
    	{
    		printf("cannot open software codec\n");
    		return -1; // Could not open codec
    	}
    	printf("==========> Open software codec success\n");
    
    	pFrame = avcodec_alloc_frame();
    	if(pFrame == NULL)
    	{
    		fprintf(stderr, "avcodec_alloc_frame() ERROR\n");
    		return -1;
    	}
    	
    	/* flag whether we get a decoded yuv frame */
    	int frameFinished;
    	int packetno = 0;
    
    	AVPacket packet;
    	av_init_packet(&packet);
    
    	while(av_read_frame(pFormatCtx, &packet) >= 0) {
    		//printf("[main]avpkt->slice_count=%d\n", packet.sliceNum);
    
    		/* Is this a packet from the video stream? */
    		if(packet.stream_index == videoStream) {
    			packetno++;
    #ifdef ENABLE_PRINT_FRAME_BYTES
    		if ( 1 ) {
    			int i;
    			int size = packet.size < PRINT_BYTES ? packet.size : PRINT_BYTES;
    			unsigned char *data = packet.data;
    			printf("===>[%5d] [", packet.size);
    			for (i = 0; i < size; i++)
    				printf("%02x ", data[i]);
    			printf("]\n");
    		}
    #endif
    #ifdef ENABLE_DEMUX_SAVE
    			fwrite(packet.data, 1, packet.size, frvdemux);
    #endif
    			//printf("[the %d packet]packet.size = %d\n", packetno++, packet.size);
    
    			while (packet.size > 0) {
    				decLen = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
    				//printf("[video_decode_example]after avcodec_decode_video2,decoded=%d\n",decLen);
    
    				if (decLen < 0)	{
    					fprintf(stderr, "[video_decode_example]Error while decoding frame %d\n", frame);
    					//exit(1);
    					/* FIXME if decode one frame err, ignore this frame */
    					decLen = packet.size;
    				}
    
    				if (frameFinished) {
    					printf("got a yuv frame\n");
    					//printf(stderr, "[video_decode_example]saving frame %3d\n", frame);
    
    					/* the picture is allocated by the decoder. no need to free it */
    					if (frame == 0) {
    						printf("[video_decode_example]picture->linesize[0]=%d, c->width=%d,c->height=%d\n", 
    								pFrame->linesize[0], pCodecCtx->width, pCodecCtx->height);
    						printf("===>YUV format = %d\n", pFrame->format);
    					}
    #ifdef ENABLE_YUV_SAVE
    					/* save yuv pic */
    					if (frame < FRAME_NUM) {
    						switch (pFrame->format) {
    							case 0 :	/* YUV420P */
    								yuv420p_save(pFrame, pCodecCtx);
    								break;
    							case 2 :	/* RGB24 */
    								rgb24_save(pFrame, pCodecCtx);
    								break;
    							case 13 :	/* YUVJ422P */
    								yuv422p_save(pFrame, pCodecCtx);
    								break;
    							case 14 :	/* YUVJ444P */
    								yuv444p_save(pFrame, pCodecCtx);
    								break;
    							default :
    								fprintf(stderr, "unsupport YUV format for saving\n");
    								break;
    						}
    						fprintf(stderr, "===>save pic success\n");
    					}
    #endif
    					/* frame index grow */
    					frame++;
    				}
    				//printf("===========> %d\n", decLen);
    				/* left data in pkt , go on decoding */
    				packet.data += decLen;
    				packet.size -= decLen;
    			}
    			if (frame == FRAME_NUM) {
    				printf("==========> decoded [%d pkt frames] ---> save [%d YUV frames], enough to stop!\n", packetno, FRAME_NUM);
    				break;
    			}
    		}
    
    		/* FIXME no need free in this file */
    		//printf("free packet that was allocated by av_read_frame\n");
    		// Free the packet that was allocated by av_read_frame
    		//av_free_packet(&packet);
    	}
    
    	printf("decoding job down! begin to free\n");
    	/* Free the YUV frame */
    	av_free(pFrame);
    
    	/* Close the codec */
    	avcodec_close(pCodecCtx);
    
    	/* Close the video file */
    	av_close_input_file(pFormatCtx);
    	fclose(pfout);
    
    	printf("==========> END-OK\n");
    
    	return 0;
    }
    

         最后是Makefile文件

    # use pkg-config for getting CFLAGS abd LDFLAGS
    FFMPEG_LIBS=libavdevice libavformat libavfilter libavcodec libswscale libavutil
    CFLAGS+=$(shell pkg-config  --cflags $(FFMPEG_LIBS))
    LDFLAGS+=$(shell pkg-config --libs $(FFMPEG_LIBS))
    
    EXAMPLES=pic_dec
    
    OBJS=$(addsuffix .o,$(EXAMPLES))
    
    %: %.o
    	$(CC) $< $(LDFLAGS) -o $@
    
    %.o: %.c
    	$(CC) $< $(CFLAGS) -c -o $@
    
    .phony: all clean
    
    all: $(OBJS) $(EXAMPLES)
    
    clean:
    	rm -rf $(EXAMPLES) $(OBJS)

         注意如果是自己编译的ffmpeg-1.0等版本,安装到例如/usr/local/目录的话,需要在环境变量中设置
         PKG_CONFIG_PATH和LD_LIBRARY_PATH,指定到/usr/local/lib/pkgconfig和/usr/local/lib/目录(如果以后要利用你的PC来交叉编译如VLC等开源代码,最好将这两个变量注释掉,因为交叉编译时的configure脚本会根据
    这个配置错误的检查到PC也就是X86结构的lib,这个显然是不对的,会让VLC模块错误的认为你的机子上有了一些第三方的库,但VLC并不知道这是X86结构的)

    展开全文
  • http://p.blog.csdn.net/images/p_blog_csdn_net/dj0379/EntryImages/20090706/libpng.png下载后请把后缀从jpg改成rar再解压。
  • Android图片编解码实现方案(Skia)
  • JPEG图片格式叠加PNG图片格式水印 本文的主要目的是将一张JPEG的图片叠加一张PNG格式的水印,很多基础理论知识本文并没有涉及,博主也不是很懂。但是JPEG的编解码,PNG的解码大家还是可以参照完成的。 一、JEPG图片...
  • 在实现GIF图片缩放的文章中,我们使用了GIF解码器来实现GIF图片解码,然后修改了SSIV的代码来实现动图的缩放。而APNG也是一种动态图片,是在APNG格式上进行扩展而来的,关于apng的文档参考这个链接,关于PNG的详细...
  • 通过 windows平台下 mingw编译的libjpeg,libpng, zlib 第三方库,然后实现两种图片的编码与解码。 (一)写在前面 前一段时间本人一直在进行Opencv有关的学习,可是在学完了一堆又一堆的函数之后,发现自己对于图像...
  • 用java写的一个实现PNG编解码的小程序,内附详细说明文档,同时代码也有较详细的注解,缺陷是实现的算法不强大且有部分BUG,虽然分有点高,但应该物有所值,如果不给力的话,欢迎大家在评论处提意见……
  • JPEG解码最全面的讲解:从格式理论讨论到实际解码各模块的实例分析和注意事项都包括在这个文档内
  • 编解码学习笔记(一):基本概念 媒体业务是网络的主要业务之间。尤其移动互联网业务的兴起,在运营商和应用开发商中,媒体业务份量极重,其中媒体的编解码服务涉及需求分析、应用开发、释放license收费等等。最近...
  • 谈到视频的编解码,我们会自然地想到H.264、HEVC/H.265这些权威的视频编解码标准;谈到标准,有人觉得这个是有专门机构去研究的,我们关心应用就好;即使有兴趣读了标准和相关技术,面对更多的是各种数学公式和术语...
  • (3)通过libjpeg来编解码jpg图片,那么同样有一个libpng用来编解码png图片。 (4)工作思路和顺序:找到并移植并部署libpng,然后查readme和其他文档示例代码等来使用libpng提供的API来对png图片进行解码,并将解码出来...
  • 编解码学习笔记二codec类型编解码学习笔记三Mpeg系列Mpeg 1和Mpeg 2编解码学习笔记四Mpeg系列Mpeg 4编解码学习笔记五Mpeg系列AAC音频编解码学习笔记六H26x系列编解码学习笔记七微软Windows Media系列编解码学习笔记...
  • python3中的图片编解码

    2019-11-08 10:37:22
    编码(图片转字符串) with open('111.png', mode='rb') as file: img = file.read() image_str = base64.encodebytes(img)....解码(字符串转图片) image_data = base64.b64decode(image_str) file = open('...
1 2 3 4 5 ... 20
收藏数 6,076
精华内容 2,430
热门标签