`

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

 
阅读更多

注:分析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;
  //开始解析JFIF
  ret = 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:
	 //开始解析SOF
	 if (parse_SOF(priv, stream) < 0)
	   return -1;
	 break;
	 //Define quantization table
       case DQT:
	 //开始解析DQT
	 if (parse_DQT(priv, stream) < 0)
	   return -1;
	 break;
       case SOS:
	 //开始解析SOS
	 if (parse_SOS(priv, stream) < 0)
	   return -1;
	 sos_marker_found = 1;
	 break;
	 //Define Huffman table
       case DHT:
	 //开始解析DHT
	 if (parse_DHT(priv, stream) < 0)
	   return -1;
	 dht_marker_found = 1;
	 break;
       case DRI:
	 //开始解析DRI
	 if (parse_DRI(priv, stream) < 0)
	   return -1;
	 break;
       default:
#if TRACE_PARAM
	fprintf(param_trace,"> Unknown marker %2.2x\n", marker);
	fflush(param_trace);
#endif
	 break;
      }
	 //解下一个segment
     stream = next_chunck;
   }

  if (!dht_marker_found) {
#if TRACE_PARAM
	  fprintf(param_trace,"No Huffman table loaded, using the default one\n");
	  fflush(param_trace);
#endif
    build_default_huffman_tables(priv);
  }

#ifdef SANITY_CHECK
  if (   (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");
#endif

  return 0;
bogus_jpeg_format:
#if TRACE_PARAM
  fprintf(param_trace,"Bogus jpeg format\n");
  fflush(param_trace);
#endif
  return -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_PARAM
  fprintf(param_trace,"> SOF marker\n");
  fflush(param_trace);
#endif
  print_SOF(stream);

  height = be16_to_cpu(stream+3);
  width  = be16_to_cpu(stream+5);
  nr_components = stream[7];
#if SANITY_CHECK
  if (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);
#endif
  char 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_CHECK
     c->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);
#endif
     c->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_PARAM
     fprintf(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_PARAM
  fprintf(param_trace,"< SOF marker\n");
  fflush(param_trace);
#endif

  return 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_PARAM
  fprintf(param_trace,"> DHT marker (length=%d)\n", length);
  fflush(param_trace);
#endif

  while (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_CHECK
     if (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_PARAM
     fprintf(param_trace,"Huffman table %s[%d] length=%d\n", (index&0xf0)?"AC":"DC", index&0xf, count);
	 fflush(param_trace);
#endif
#endif

     if (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_PARAM
  fprintf(param_trace,"< DHT marker\n");
  fflush(param_trace);
#endif
  return 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_PARAM
  fprintf(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_CHECK
     if (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_PARAM
		for(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_PARAM
  fprintf(param_trace,"< DQT marker\n");
  fflush(param_trace);
#endif
  return 0;
}


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

 

待续未完。。。

 

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

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

分享到:
评论

相关推荐

    tiny jpeg decoder (JPEG解码器)

    tiny jpeg decoder 是可以用于嵌入式系统的jpeg解码器,也可以在windows下编译通过。

    小型JPEG图片解码器Tiny JPEG Decoder

    压缩包中的"tinyjpegdecoder-20070609.tar.bz2"文件是Tiny JPEG Decoder的源代码包,日期为2007年6月9日,可能包含了编译说明、示例代码和其他相关资源。"welcome.txt"可能是对项目的一个简短介绍或使用指南。 学习...

    tinyjpeg jpge解码程序

    作为从jpge小组文档中移植的代码,tinyjpeg可能是开源的,允许开发者查看源代码、学习解码技术,并对其进行改进。这有助于整个图像处理社区的技术交流和发展。 总的来说,tinyjpeg是一个实用的JPEG解码工具,它的...

    TJpg_Decoder:基于Tiny JPEG Decompressor的Jpeg解码器库

    Arduino TJpg_Decoder库该Arduino库支持将存储在SD卡上以及程序存储器(FLASH)中的阵列中的Jpeg文件呈现到TFT显示器上。 此外,存储在SPIFFS Flash归档系统或“ PROGMEM”阵列中的图像可以与ESP8266和ESP32处理器...

    STM32中成功移值JPEG解码程序,有SD卡+TTF液晶

    1. **程序结构**:将JPEG解码、SD卡读取和TTF显示功能模块化,便于代码管理和维护。 2. **调试**:使用如STM32CubeIDE、Keil uVision等开发工具进行调试,确保每个模块的功能正常,优化性能。 3. **测试**:在实际...

    JPEG解码,可以移植到STM32中

    1. `tjpgd.c`:这是主要的JPEG解码器实现文件,包含了解码算法的核心逻辑。它将JPEG编码的二进制数据转换为RGB或灰度图像的原始像素数据,以便在STM32的显示屏上显示或者进行其他处理。 2. `jpegdecode.c`:可能包含...

    Tiny_Jpeg:微型 Jpeg 解码器

    Tiny_Jpeg 微型 Jpeg 解码器 Luc Saillard GNU 公共许可证的原始代码 2007 年 6 月 9 日 tinyjpegdecoder-20070609.... 2个流行的公共Jpeg解码器小代码之一 Nano Jpeg Martin Fiedler东德 Tiny Jpeg Luc Saillard 法国

    tiny+ 编译器源代码

    **源代码分析** 源代码是程序员用高级语言书写的程序文本,它是编译器工作的输入。在这个压缩包中,你将找到`Tiny+`编译器的源代码,这将使你有机会深入到编译器的内部,观察其如何处理源代码的每一个细节。 **...

    Tiny+编译器源代码

    此外,`请阅读此文件.txt` 可能包含了项目介绍、使用说明或者注意事项,而 `a.txt` 和 `b.txt` 可能是测试用例的 Tiny+ 源代码文件,用于验证编译器的功能是否正确。 学习 Tiny+ 编译器源代码,不仅可以加深对...

    Tiny JPEG Decompressor

    Tiny JPEG Decompressor的源代码简洁明了,易于理解和移植。开发者可以轻松地根据自己的需求进行定制和优化。源码的组织结构清晰,主要分为解码核心部分和平台相关的I/O函数。解码核心部分负责处理JPEG的编码格式,...

    TINY 编译器源代码

    《TINY编译器源代码》是针对C语言实现的一款小型编译器,它能够将输入的源代码文件转换为对应的机器码,从而在计算机上执行。这一项目通常与《编译原理与实践》这本教材相结合,为学习编译器设计与实现的初学者提供...

    TINYC tiny c最快的c编译器源代码.rar

    【TINYC tiny c最快的c编译器源代码.rar】是一个包含TINYC C编译器源代码的压缩包。TINYC是一个小型且快速的C语言编译器,它被设计为轻量级,适合在资源有限的环境中使用,如嵌入式系统或老旧的计算机上。该压缩包中...

    tinyxml源代码,示例,教程

    2. **源代码分析** 在`tinyxml_2_5_3.rar`中,包含了TinyXML的源代码,通过阅读源代码,你可以了解到XML解析器的工作原理。例如,`TiXmlElement`类如何存储元素名和属性,`TiXmlNode`如何构建节点树,以及`...

    Tiny编译器的源代码

    1. **词法分析**(Lexical Analysis):这是编译器的第一个阶段,负责将源代码转换成一个个称为“标记”(Token)的最小单元。这些标记通常包括关键字、标识符、常量、运算符等。在这个阶段,源代码的文本被分割成有...

    6410-linux驱动程序的位置

    - **备注**:在Linux-2.6.36/38内核版本中,多媒体驱动程序通常以目标文件形式存在,而非源代码形式。 ##### 25. NAND Flash 驱动 - **驱动程序源代码位置**:`Linux-2.6.38/drivers/mtd/nand` - **设备名**:无...

    语法分析器(C语言源代码)(附实验报告)

    语法分析器是编译器设计的关键组成部分,它负责将源代码转换为抽象语法树(AST),这是理解程序结构的关键步骤。本项目以C语言为工具,实现了一个语法分析器,采用了两种不同的方法:递归下降分析法和LL(1)语法分析...

    设计并实现TINYC语言的扫描程序TINYC.zip

    •完成并提交实验报告,扫描程序的源程序,编译后的可执行程序,例子和运行结果. 实验报告至少要包含如下内容: 1 实验目的; 2 TINYC语言的词法说明,扫描器的输入和输出; 3 实验原理(所采用的过程); 3.1 记号种类及各...

    基于Tiny6410的H264硬编码、软解码的源代码【有注释】

    这个是友善之臂专题8的源代码,不过没有注释,现在加上了注释,提供下载链接,也可以不下载,对照着这里看。因为这个过程包括采集、硬编码、软解码等过程。 调用的函数是规范的,所以不是用tiny6410的也可以看看,...

Global site tag (gtag.js) - Google Analytics