注:分析Tiny Jpeg Decoder源代码的文章:

Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 1:解码文件头
Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 2:解码数据

===================

Tiny Jpeg Decoder是一个可以用于嵌入式系统的JPEG解码器。也可以在Windows上编译通过。在此分析一下它部分的源代码,辅助学习JPEG解码知识。

通过TinyJpeg可以将JPEG(*.jpg)文件解码为YUV(*.yuv)或者RGB(*.tga)文件。

真正的解码开始于convert_one_image()函数:

/*** Load one jpeg image, and decompress it, and save the result.*/
int convert_one_image(LPVOID lparam,const char *infilename, const char *outfilename, int output_format)
{FILE *fp;unsigned int length_of_file;unsigned int width, height;unsigned char *buf;struct jdec_private *jdec;unsigned char *components[3];/* Load the Jpeg into memory */fp = fopen(infilename, "rb");if (fp == NULL)exitmessage("Cannot open filename\n");length_of_file = filesize(fp);buf = (unsigned char *)malloc(length_of_file + 4);if (buf == NULL)exitmessage("Not enough memory for loading file\n");fread(buf, length_of_file, 1, fp);fclose(fp);/* Decompress it *///分配内存jdec = tinyjpeg_init();//传入句柄--------------jdec->dlg=(CSpecialVIJPGDlg *)lparam;if (jdec == NULL)exitmessage("Not enough memory to alloc the structure need for decompressing\n");//解头部if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)exitmessage(tinyjpeg_get_errorstring(jdec));/* Get the size of the image *///获得图像长宽tinyjpeg_get_size(jdec, &width, &height);snprintf(error_string, sizeof(error_string),"Decoding JPEG image...\n");//解码实际数据if (tinyjpeg_decode(jdec, output_format) < 0)exitmessage(tinyjpeg_get_errorstring(jdec));/* * Get address for each plane (not only max 3 planes is supported), and* depending of the output mode, only some components will be filled * RGB: 1 plane, YUV420P: 3 planes, GREY: 1 plane*/tinyjpeg_get_components(jdec, components);/* Save it */switch (output_format){case TINYJPEG_FMT_RGB24:case TINYJPEG_FMT_BGR24:write_tga(outfilename, output_format, width, height, components);break;case TINYJPEG_FMT_YUV420P://开始写入成YUV420P文件write_yuv(outfilename, width, height, components);break;case TINYJPEG_FMT_GREY://开始写入成灰度文件write_pgm(outfilename, width, height, components);break;}/* Only called this if the buffers were allocated by tinyjpeg_decode() */
//modify by lei!  tinyjpeg_free(jdec);/* else called just free(jdec); */free(buf);return 0;
}

tinyjpeg_init()用于初始化:

/*** Allocate a new tinyjpeg decoder object.** Before calling any other functions, an object need to be called.*/
struct jdec_private *tinyjpeg_init(void)
{struct jdec_private *priv;priv = (struct jdec_private *)calloc(1, sizeof(struct jdec_private));if (priv == NULL)return NULL;priv->DQT_table_num=0;return priv;
}

tinyjpeg_parse_header()用于解码JPEG文件头,可见函数前几句主要验证文件是否为JPEG文件:

/*** Initialize the tinyjpeg object and prepare the decoding of the stream.** Check if the jpeg can be decoded with this jpeg decoder.* Fill some table used for preprocessing.*/
int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size)
{int ret;/* Identify the file *///0x FF D8//是否是JPEG格式文件?if ((buf[0] != 0xFF) || (buf[1] != SOI))snprintf(error_string, sizeof(error_string),"Not a JPG file ?\n");//是char temp_str[MAX_URL_LENGTH];sprintf(temp_str,"0x %X %X",buf[0],buf[1]);//JPEG格式文件固定的文件头//begin指针前移2字节priv->stream_begin = buf+2;priv->stream_length = size-2;priv->stream_end = priv->stream_begin + priv->stream_length;//开始解析JFIFret = parse_JFIF(priv, priv->stream_begin);return ret;
}

parse_JFIF()用于解析各种标签(SOF,SOS,DHT...):

//解各种不同的标签
static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)
{int chuck_len;int marker;int sos_marker_found = 0;int dht_marker_found = 0;const unsigned char *next_chunck;/* Parse marker *///在Start of scan标签之前while (!sos_marker_found){if (*stream++ != 0xff)goto bogus_jpeg_format;/* Skip any padding ff byte (this is normal) *///跳过0xff字节while (*stream == 0xff)stream++;//marker是跳过0xff字节后1个字节的内容marker = *stream++;//chunk_len是marker后面2个字节的内容(大端模式需要转换)//包含自身,但不包含0xff+marker2字节chuck_len = be16_to_cpu(stream);//指向下一个chunk的指针next_chunck = stream + chuck_len;//各种不同的标签switch (marker){case SOF://开始解析SOFif (parse_SOF(priv, stream) < 0)return -1;break;//Define quantization tablecase DQT://开始解析DQTif (parse_DQT(priv, stream) < 0)return -1;break;case SOS://开始解析SOSif (parse_SOS(priv, stream) < 0)return -1;sos_marker_found = 1;break;//Define Huffman tablecase DHT://开始解析DHTif (parse_DHT(priv, stream) < 0)return -1;dht_marker_found = 1;break;case DRI://开始解析DRIif (parse_DRI(priv, stream) < 0)return -1;break;default:
#if TRACE_PARAMfprintf(param_trace,"> Unknown marker %2.2x\n", marker);fflush(param_trace);
#endifbreak;}//解下一个segmentstream = next_chunck;}if (!dht_marker_found) {
#if TRACE_PARAMfprintf(param_trace,"No Huffman table loaded, using the default one\n");fflush(param_trace);
#endifbuild_default_huffman_tables(priv);}#ifdef SANITY_CHECKif (   (priv->component_infos[cY].Hfactor < priv->component_infos[cCb].Hfactor)|| (priv->component_infos[cY].Hfactor < priv->component_infos[cCr].Hfactor))snprintf(error_string, sizeof(error_string),"Horizontal sampling factor for Y should be greater than horitontal sampling factor for Cb or Cr\n");if (   (priv->component_infos[cY].Vfactor < priv->component_infos[cCb].Vfactor)|| (priv->component_infos[cY].Vfactor < priv->component_infos[cCr].Vfactor))snprintf(error_string, sizeof(error_string),"Vertical sampling factor for Y should be greater than vertical sampling factor for Cb or Cr\n");if (   (priv->component_infos[cCb].Hfactor!=1) || (priv->component_infos[cCr].Hfactor!=1)|| (priv->component_infos[cCb].Vfactor!=1)|| (priv->component_infos[cCr].Vfactor!=1))snprintf(error_string, sizeof(error_string),"Sampling other than 1x1 for Cr and Cb is not supported");
#endifreturn 0;
bogus_jpeg_format:
#if TRACE_PARAMfprintf(param_trace,"Bogus jpeg format\n");fflush(param_trace);
#endifreturn -1;
}

parse_SOF()用于解析SOF标签:

注意:其中包含了部分自己写的代码,形如:

itoa(width,temp_str1,10);priv->dlg->AppendBInfo("SOF0","宽",temp_str1,"图像的宽度");

这些代码主要用于在解码过程中提取一些信息,比如图像宽,高,颜色分量数等等

static int parse_SOF(struct jdec_private *priv, const unsigned char *stream)
{int i, width, height, nr_components, cid, sampling_factor;int Q_table;struct component *c;
#if TRACE_PARAMfprintf(param_trace,"> SOF marker\n");fflush(param_trace);
#endifprint_SOF(stream);height = be16_to_cpu(stream+3);width  = be16_to_cpu(stream+5);nr_components = stream[7];
#if SANITY_CHECKif (stream[2] != 8)snprintf(error_string, sizeof(error_string),"Precision other than 8 is not supported\n");if (width>JPEG_MAX_WIDTH || height>JPEG_MAX_HEIGHT)snprintf(error_string, sizeof(error_string),"Width and Height (%dx%d) seems suspicious\n", width, height);if (nr_components != 3)snprintf(error_string, sizeof(error_string),"We only support YUV images\n");if (height%16)snprintf(error_string, sizeof(error_string),"Height need to be a multiple of 16 (current height is %d)\n", height);if (width%16)snprintf(error_string, sizeof(error_string),"Width need to be a multiple of 16 (current Width is %d)\n", width);
#endifchar temp_str1[MAX_URL_LENGTH]={0};itoa(width,temp_str1,10);priv->dlg->AppendBInfo("SOF0","宽",temp_str1,"图像的宽度");itoa(height,temp_str1,10);priv->dlg->AppendBInfo("SOF0","高",temp_str1,"图像的高度");itoa(nr_components,temp_str1,10);priv->dlg->AppendBInfo("SOF0","颜色分量数",temp_str1,"图像的颜色分量数。一个字节,例如03,代表有三个分量,YCrCb");itoa(stream[2],temp_str1,10);priv->dlg->AppendBInfo("SOF0","精度",temp_str1,"图像的精度。一个字节,例如08,即精度为一个字节。");stream += 8;for (i=0; i<nr_components; i++) {cid = *stream++;sampling_factor = *stream++;Q_table = *stream++;c = &priv->component_infos[i];
#if SANITY_CHECKc->cid = cid;if (Q_table >= COMPONENTS)snprintf(error_string, sizeof(error_string),"Bad Quantization table index (got %d, max allowed %d)\n", Q_table, COMPONENTS-1);
#endifc->Vfactor = sampling_factor&0xf;c->Hfactor = sampling_factor>>4;c->Q_table = priv->Q_tables[Q_table];//------------char temp_str2[MAX_URL_LENGTH]={0};sprintf(temp_str2,"垂直采样因子【%d】",i);itoa(c->Hfactor,temp_str1,10);priv->dlg->AppendBInfo("SOF0",temp_str2,temp_str1,"颜色分量信息:每个分量有三个字节,第一个为分量的ID,01:Y 02:U 03:V;第二个字节高位为水平采样因子,低位为垂直采样因子。");sprintf(temp_str2,"水平采样因子【%d】",i);itoa(c->Hfactor,temp_str1,10);priv->dlg->AppendBInfo("SOF0",temp_str2,temp_str1,"颜色分量信息:每个分量有三个字节,第一个为分量的ID,01:Y 02:U 03:V;第二个字节高位为水平采样因子,低位为垂直采样因子。");sprintf(temp_str2,"对应量化表ID【%d】",i);itoa((int)Q_table,temp_str1,10);priv->dlg->AppendBInfo("SOF0",temp_str2,temp_str1,"颜色分量信息:第三个字节代表这个分量对应的量化表ID,例如,Y对应的量化表ID索引值为00,而UV对应的量化表ID都为01,即它们共用一张量化表。");//-------------
#if TRACE_PARAMfprintf(param_trace,"Component:%d  factor:%dx%d  Quantization table:%d\n",cid, c->Hfactor, c->Hfactor, Q_table );fflush(param_trace);
#endif}priv->width = width;priv->height = height;
#if TRACE_PARAMfprintf(param_trace,"< SOF marker\n");fflush(param_trace);
#endifreturn 0;
}

parse_DHT()用于解析DHT标签:

//解析DHT表
static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)
{unsigned int count, i,j;unsigned char huff_bits[17];int length, index;//------------------------------------------char *temp;FILE *fp;//------------------------------------------length = be16_to_cpu(stream) - 2;//跳过length字段stream += 2; /* Skip length */
#if TRACE_PARAMfprintf(param_trace,"> DHT marker (length=%d)\n", length);fflush(param_trace);
#endifwhile (length>0) {//跳过第1字节://Huffman 表ID号和类型,高 4 位为表的类型,0:DC 直流;1:AC交流//低四位为 Huffman 表 ID。 index = *stream++;/* We need to calculate the number of bytes 'vals' will takes */huff_bits[0] = 0;count = 0;//不同长度 Huffman 的码字数量:固定为 16 个字节,每个字节代表从长度为 1到长度为 16 的码字的个数for (i=1; i<17; i++) {huff_bits[i] = *stream++;//count记录码字的个数count += huff_bits[i];}
#if SANITY_CHECKif (count >= HUFFMAN_BITS_SIZE)snprintf(error_string, sizeof(error_string),"No more than %d bytes is allowed to describe a huffman table", HUFFMAN_BITS_SIZE);if ( (index &0xf) >= HUFFMAN_TABLES)snprintf(error_string, sizeof(error_string),"No more than %d Huffman tables is supported (got %d)\n", HUFFMAN_TABLES, index&0xf);
#if TRACE_PARAMfprintf(param_trace,"Huffman table %s[%d] length=%d\n", (index&0xf0)?"AC":"DC", index&0xf, count);fflush(param_trace);
#endif
#endifif (index & 0xf0 ){//---------------------char temp_str1[MAX_URL_LENGTH]={0};char temp_str2[MAX_URL_LENGTH]={0};temp=(char *)stream;//fp = fopen("DHT.txt", "a+");//fwrite(temp, 16, 1, fp);for(j=0;j<16;j++){//fprintf(fp,"%d ",temp[j]);sprintf(temp_str2,"%d ",temp[j]);strcat(temp_str1,temp_str2);}//fprintf(fp,"\n-----------------------\n");//fclose(fp);//-----------------------------------------------------priv->dlg->AppendBInfo("DHT","定义霍夫曼表【交流系数表】",temp_str1,"Huffman表ID号和类型:1字节,高4位为表的类型,0:DC直流;1:AC交流 可以看出这里是直流表;低四位为Huffman表ID");//-----------------------------------------------------//交流霍夫曼表build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);}else{//---------------------char temp_str1[MAX_URL_LENGTH]={0};char temp_str2[MAX_URL_LENGTH]={0};temp=(char *)stream;//fp = fopen("DHT.txt", "a+");//fwrite(temp, 16, 1, fp);for(j=0;j<16;j++){//fprintf(fp,"%d ",temp[j]);sprintf(temp_str2,"%d ",temp[j]);strcat(temp_str1,temp_str2);}//fprintf(fp,"\n-----------------------\n");//fclose(fp);//-----------------------------------------------------priv->dlg->AppendBInfo("DHT","定义霍夫曼表【直流系数表】",temp_str1,"Huffman表ID号和类型:1字节,高4位为表的类型,0:DC直流;1:AC交流 可以看出这里是直流表;低四位为Huffman表ID");//-----------------------------------------------------//直流霍夫曼表build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);}length -= 1;length -= 16;length -= count;stream += count;}
#if TRACE_PARAMfprintf(param_trace,"< DHT marker\n");fflush(param_trace);
#endifreturn 0;
}

parse_DQT()用于解析DQT标签:

//解析Define quantization table
static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{int qi;float *table;const unsigned char *dqt_block_end;//------------------------------------------------int j,k;char *temp;FILE *fp;
//------------------------------------------------
#if TRACE_PARAMfprintf(param_trace,"> DQT marker\n");fflush(param_trace);
#endif//该Segment末尾dqt_block_end = stream + be16_to_cpu(stream);//跳过标签length(2字节)stream += 2;  /* Skip length *///没到末尾while (stream < dqt_block_end){//跳过该Segment的第1个字节,QT信息//precision: 00  (Higher 4 bit)//index: 00  (Lower 4 bit) //qi取1,第1张量化表(例如,亮度表),取2,第2张量化表(例如,色度表)qi = *stream++;
#if SANITY_CHECKif (qi>>4)snprintf(error_string, sizeof(error_string),"16 bits quantization table is not supported\n");if (qi>4)snprintf(error_string, sizeof(error_string),"No more 4 quantization table is supported (got %d)\n", qi);
#endif//table指向jdec_private的Q_tables数组,为了在其中写入数据table = priv->Q_tables[qi];//注意:一次搞定整张!写入//需要对数值进行变换!cos(k*PI/16) * sqrt(2)//这样才能得到离散余弦变换的系数build_quantization_table(table, stream);
//----------------------------------------------------------temp=(char *)stream;//fp = fopen("DQT.txt", "a+");//fwrite(temp, 64, 1, fp);char temp_str1[MAX_URL_LENGTH]={0};char temp_str2[MAX_URL_LENGTH]={0};for(j=0;j<64;j++){sprintf(temp_str2,"%d ",temp[j]);strcat(temp_str1,temp_str2);//fprintf(fp,"%d ",temp[j]);}//计数char temp_str3[MAX_URL_LENGTH]={0};sprintf(temp_str3,"量化表【%d】",priv->DQT_table_num);priv->dlg->AppendBInfo("DQT",temp_str3,temp_str1,"JPEG格式文件的量化表,一般来说第一张是亮度的,后面是色度的");priv->DQT_table_num++;//fprintf(fp,"\n-----------------------\n");//fclose(fp);
#if TRACE_PARAMfor(j=0;j<8;j++){for(k=0;k<8;k++){fprintf(param_trace,"%d ",temp[j*8+k]);}fprintf(param_trace,"\n");}fprintf(fp,"\n-----------------------\n");fflush(param_trace);
#endif
//----------------------------------------------------------//完事了!stream += 64;}
#if TRACE_PARAMfprintf(param_trace,"< DQT marker\n");fflush(param_trace);
#endifreturn 0;
}

其他标签的解析不一一列举。

待续未完。。。

主页:http://www.saillard.org/programs_and_patches/tinyjpegdecoder/

源代码下载:http://download.csdn.net/detail/leixiaohua1020/6383115

Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 1:解码文件头相关推荐

  1. ART世界探险(12) - OAT文件分析(2) - ELF文件头分析(中)

    ART世界探险(12) - OAT文件分析(2) - ELF文件头分析(中) 段(section)的概念 一块内存分配给应用程序之后,从代码的组织上,我们就有将它们分段的需求. 比如,可以分为代码段, ...

  2. PE文件格式分析-WinHex工具-文件头-32位PE-部分64位PE

    文章目录 1.名称来源 2.PE文件基本结构 3.DOS头 4.DOS存根: 5.NT头 5.1.文件头 5.2.可选头 6.节区头表 7.数据目录详解 7.1.导入函数表 7.2.重定位表 8.作者 ...

  3. Android应用Activity、Dialog、PopWindow、Toast窗体加入机制及源代码分析

    [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处.尊重劳动成果] 1 背景 之所以写这一篇博客的原因是由于之前有写过一篇<Android应用setCont ...

  4. UPack的PE文件头分析与OEP查找

    UPack(Ultimate PE压缩器),是一种PE文件的运行时压缩器,特点是使用独特的方法对PE头进行变形.UPack会使许多的PE分析程序无法正常运行,因此很多恶意代码都是通过UPack进行压缩 ...

  5. 程序的本质之二ELF文件的文件头、section header和program header

    操作系统:CentOS Linux release 7.7.1908 内核版本:3.10.0-1062.1.1.el7.x86_64 运行平台:x86_64 参考文献:http://refspecs. ...

  6. Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 2:解码数据

    注:分析Tiny Jpeg Decoder源代码的文章: Tiny Jpeg Decoder (JPEG解码程序) 源代码分析 1:解码文件头 Tiny Jpeg Decoder (JPEG解码程序) ...

  7. MediaInfo源代码分析 5:JPEG解析代码分析

    ===================================================== MediaInfo源代码分析系列文章列表: MediaInfo源代码分析 1:整体结构 Me ...

  8. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  9. Android应用程序进程启动过程的源代码分析(1)

    Android应用程序框架层创建的应用程序进程具有两个特点,一是进程的入口函数是ActivityThread.main,二是进程天然支持Binder进程间通信机制:这两个特点都是在进程的初始化过程中实 ...

最新文章

  1. 开源炫酷css轮播图 可直接引入html文件使用 含注释 jQuery插件
  2. 20155337祁家伟做中学
  3. Activity动画效果笔记
  4. 【Python】调用百度云API人脸检测 Face Detect
  5. 复习(三)—— 进程管理详解
  6. SpringBoot+Vue使用Get请求时提示:Error parsing HTTP request header
  7. C#中简单的正则表达式(也经常会用到的)
  8. 无招胜有招之Java进阶JVM(八)类加载机制
  9. python字符串转日期_Python:将字符串时间字典转换为日期时间
  10. 我使用 html 反向输出自己打自己(7)
  11. 图的表示方法和C++实现
  12. 如何在MaxCompute中利用bitmap进行数据处理?
  13. css揭秘实战技巧 - 形状 [二]
  14. 两位动态数码管电子秒表c语言,清翔电子51单片机6课动态显示数码管作业秒表...
  15. MaterialPropertyBlock的使用
  16. jQuery 的第一个例子
  17. centos中,tomcat项目创建文件的权限研究
  18. 博图os更新_博途V14的新功能(通过U盘给第二代的精智及精简屏传输组态)
  19. java考勤表导出_考勤打卡机导出的excel考勤时间表如何生成实用的考勤表
  20. 腾讯云服务器被黑客攻击的解决办法

热门文章

  1. Bailian4147 汉诺塔问题(Hanoi)
  2. HDU2048 神、上帝以及老天爷【递推】
  3. Vijos P1127 级数求和【数列】
  4. Need ffmpeg exe. You can download it by calling: imageio.plugins.ffmpeg.download()
  5. Python 库的使用 —— dis
  6. Python 标准库 —— os 路径(os.path)
  7. python数据归一化代码_Python 数据归一化/标准化
  8. ios旧版本app网站_这两款app已解锁永久订阅版!
  9. php screw.so扩展下载,CentOS下安装php加解密工具php
  10. delphi 获取数组长度_Java中的数组(基础篇六)