#ifndef _WINSAIL_JPEG_20081015_H_
#define _WINSAIL_JPEG_20081015_H_

#ifdef _WINDOWS_
	#pragma pack(push, 1)
#endif

//http://en.wikipedia.org/wiki/JPEG

//MCU-----------最小编码单元---T[8*nMaxX, 8*nMaxY]
//DU------------数据单元-------T[8x8]
//BaseLine------基本序列模式

/*
    JPEG （Joint Photographic Experts GROUP,联合图像专家组的缩写）是由国际
标准组织（ISO:International Standardization Organization）和国际电话电报咨
询委员会（CCITT:Consultation Commitee of the International Telephone and
Telegraph）为静态图像所建立的第一个国际数字图像压缩标准，也是至今一直在使用
的、应用最广的图像压缩标准。JPEG由于可以提供有损压缩，因此压缩比可以达到其
他传统压缩算法无法比拟的程度。

    JPEG 是 Joint Photographic Experts Group 的缩写，即 ISO 和 IEC 联合图像
专家组，负责静态图像压缩标准的制定，这个专家组开发的算法就被称为 JPEG 算法，
并且已经成为了大家通用的标准，即 JPEG 标准。 JPEG 压缩是有损压缩，但这个损失
的部分是人的视觉不容易察觉到的部分，它充分利用了人眼对计算机色彩中的高频信息
部分不敏感的特点，来大大节省了需要处理的数据信息。

    人眼对构成图像的不同频率成分具有不同的敏感度，这个是由人眼的视觉生理特性
所决定的。如人的眼睛含有对亮度敏感的柱状细胞1.8亿个，含有对色彩敏感的椎状细
胞0.08亿个，由于柱状细胞的数量远大于椎状细胞，所以眼睛对亮度的敏感程度要大于
对色彩的敏感程度。


  JPEG标准制定了四种工作模式： 

（1）顺序的基于DCT（Sequential DCT-based ）模式
     由DCT（离散余弦变换）系数的形成、量化和熵编码三步组成。从左到右，从上到下扫
描信号，为每个图像编码。 

（2）累进的基于DCT（Progressive DCT-based）模式 
     生成DCT系数和量化中的关键步骤与基本顺序编码解码器相同。主要的区别在于每个图
像部件由多次扫描进行编码而不是仅一次扫描。每次继续的扫描都对图像作了改善，直到达
到由量化表建立的图像质量为止。 

（3）无损（Lossless）模式 
     独立于DCT处理，用来定义一种达到无损连续色调压缩的手段。预测器将采样区域组合起
来并基于采样区域预测出邻系统区域。预测出的区域对照着每一区域的完全无损采样进行预测，
同时通过Huffman 编码法或算术熵编码法对这一差别进行无损编码，对较好质量的复制通常可
达到2：1的压缩率。 

（4）分层（Hierarchical）模式 
     分层模式提供了一种可实现多种分辨率的手段。每个接续层次上的图像编码在水平或垂直
方向上的分辨率都被降低二倍。它所传送的数据包括所支持的最低分辨率图像，以及用于解码恢
复到原有的全分辨率图像所需的、分辨率以2的倍数递降的相邻图像的差分信息。

JPEG 2000有两种编码模式： 
  （1）基于DCT的编码模式：采用现在的基线JPEG； 
  （2）基于小波的编码模式：包括不能还原和能还原的变换。


JPEG压缩编码算法的主要计算步骤如下：
（1）正向离散余弦变换（FDCT）。 
（2）量化（Quantization）。 
（3）Z字形编码（Zigzag Scan）。 
（4）使用差分脉冲编码调制（Differential Pulse Code Modulation，DPCM)对直流系数(DC)进行编码。 
（5）使用行程长度编码（Run-Length Encoding，RLE）对交流系数（AC）进行编码。 
（6）熵编码（Entropy Eoding）。

    JPEG 的图片使用的是 YCrCb 颜色模型, 而不是计算机上最常用的 RGB. 关于色彩模型, 这
里不多阐述. 只是说明, YCrCb 模型更适合图形压缩. 因为人眼对图片上的亮度 Y 的变化远比色
度 C 的变化敏感. 我们完全可以每个点保存一个 8bit 的亮度值, 每 2x2 个点保存一个 Cr Cb 值,
而图象在肉眼中的感觉不会起太大的变化. 所以, 原来用 RGB 模型, 4 个点需要 4x3=12 字节. 而
现在仅需要 4+2=6 字节; 平均每个点占 12bit. 当然 JPEG 格式里允许每个点的 C 值都记录下来; 
不过 MPEG 里都是按 12bit 一个点来存放的, 我们简写为 YUV12.

    一般, C 值 (包括 Cb Cr) 应该是一个有符号的数字, 但这里被处理过了, 方法是加上了 128. 
JPEG 里的数据都是无符号 8bit 的.

    JPEG 里, 要对数据压缩, 先要做一次 DCT 变换. DCT 变换的原理, 涉及到数学知识, 这里
我们不必深究. 反正和傅立叶变换(学过高数的都知道) 是差不多了. 经过这个变换, 就把图片里
点和点间的规律呈现出来了, 更方便压缩.JPEG 里是对每 8x8 个点为一个单位处理的. 所以如果
原始图片的长宽不是 8 的倍数, 都需要先补成 8 的倍数, 好一块块的处理. 另外, 记得刚才我
说的 Cr Cb 都是 2x2 记录一次吗? 所以大多数情况, 是要补成 16x16 的整数块.按从左到右, 
从上到下的次序排列 (和我们写字的次序一样). JPEG 里是对 Y Cr Cb 分别做DCT 变换的. 这里
进行 DCT 变换的 Y, Cr, Cb 值的范围都是 -128~127. (Y 被减去 128)
    
    JPEG 编码时使用的是 Forward DCT (FDCT) 解码时使用的 Inverse DCT (IDCT).下面给出公式:

<1>编码使用的公式：FDCT:
							 7   7                 2*x+1                2*y+1
F(u,v) = alpha(u)*alpha(v)* sum sum f(x,y) * cos (------- *u*PI)* cos (------ *v*PI)
                            x=0 y=0                 16                   16

u,v = 0,1,...,7

		   { 1/sqrt(8)  (u==0)
alpha(u) = {
           { 1/2        (u!=0)

<2>解码使用的公式：IDCT:
          7   7                                 2*x+1                2*y+1
f(x,y) = sum sum alpha(u)*alpha(v)*F(u,v)*cos (------- *u*PI)* cos (------ *v*PI)
         u=0 v=0                                 16                   16

x,y=0,1...7


  计算有一定的复杂性。但是，实际上这个函数是用代码来实现的，即两个余弦项只在程序开始时进行1次
计算，将计算的结果储存起来，而后通过查表就可以了，其它各项都可以通过查表解决，其程序采用了双层
嵌套循环。。利用C语言程序对N×N个矩阵元素的代码采用双层嵌套循环计算如下：

pixel[x][y]表示式中的f(x,y),用DCT[u][v]表示式中的F(u,v)。
for (u = 0 , u < N, u ++)
{
	for (v = 0,v < N, v++)
	{
		double temp = 0,0；
		for (x = 0, x < N, x++)
		{
			for (y = 0,y < N, y++) 
			{
				temp + = Cosines[x][u]*Cosines [y] [v] * pixel [x] [y];
			}
		}
		temp* = sqrt(2 * N ) * Coefficients[u][v]；
		DCT[u] [v] = INT_ROUND(temp)：
	}
}

<3> 重排列 DCT 结果
	 DCT 将一个 8x8 的数组变换成另一个 8x8 的数组. 但是内存里所有数据都是线形存放的,
	 如果我们一行行的存放这 64 个数字, 每行的结尾的点和下行开始的点就没有什么关系, 
	 所以 JPEG 规定按如下次序整理 64 个数字.

                  0,  1,  5,  6, 14, 15, 27, 28,
                  2,  4,  7, 13, 16, 26, 29, 42,
                  3,  8, 12, 17, 25, 30, 41, 43,
                  9, 11, 18, 24, 31, 40, 44, 53,
                 10, 19, 23, 32, 39, 45, 52, 54,
                 20, 22, 33, 38, 46, 51, 55, 60,
                 21, 34, 37, 47, 50, 56, 59, 61,
                 35, 36, 48, 49, 57, 58, 62, 63

<4>量化
     对于前面得到的 64 个空间频率振幅值, 我们将对它们作幅度分层量化操作.方法就是分
	 别除以量化表里对应值并四舍五入.

	for (i = 0 ; i < 64; i++ )
	{
		vector[i] = (int) (vector[i] / quantization_table[i] + 0.5)
	}



JPEG的压缩步骤分为：

1.颜色转换
    有余JPEG只支持YUV颜色模式的数据结构，而不支持RGB图像数据结构，所以在将彩色图像进行压
	缩之前，必须先对颜色模式进行数据转换。各个值的转换可以通过下面的转换公式计算得出：
    Y=0.299R+0.587G+0.114B
    U=-0.169R-0.3313G+0.5B
    V=0.5R-0.4187G-0.0813B


    其中，Y表示亮度，U和V表示颜色。

　　YUV主要的采样格式有YCbCr 4:2:0、YCbCr 4:2:2、YCbCr 4:1:1和 YCbCr 4:4:4。其中YCbCr 4:1:1 比
较常用，其含义为：每个点保存一个 8bit 的亮度值，每 2x2 个点保存一个 Cr 和Cb 值, 图像在肉眼中的
感觉不会起太大的变化。所以， 原来用 RGB(R，G，B 都是 8bit unsigned) 模型， 1个点需要 8x3=24字节，
按4:1:1采样后，而现在平均仅需要 8+(8/4)+(8/4)=12字节（4个点，8*4（Y）+8(U)+8(V) = 48bit）, 平均
每个点占12bites。这样就把图像的数据压缩了一半。 

　　（1） YUV 4:4:4 
　　 YUV三个信道的抽样率相同，因此在生成的图像里，每个象素的三个分量信息完整（每个分量通常8比特），
经过8比特量化之后，未经压缩的每个像素占用3个字节。 
　
 　 下面的四个像素为: [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3] 
　　存放的码流为: Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3 

    （2） YUV 4:2:2 
　　 每个色差信道的抽样率是亮度信道的一半，所以水平方向的色度抽样率只是4:4:4的一半。对非压缩的8比
特量化的图像来说，每个由两个水平方向相邻的像素组成的宏像素需要占用4字节内存。 

  　下面的四个像素为：[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3] 
　　存放的码流为：Y0 U0 Y1 V1 Y2 U2 Y3 V3
　　映射出像素点为：[Y0 U0 V1] [Y1 U0 V1] [Y2 U2 V3] [Y3 U2 V3] 

   （3） YUV 4:1:1
　　4:1:1的色度抽样，是在水平方向上对色度进行4:1抽样。对于低端用户和消费类产品这仍然是可以接受的。
    对非压缩的8比特量化的视频来说，每个由4个水平方向相邻的像素组成的宏像素需要占用6字节内存。 
　　
    四个像素为:     [Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3] 
　　存放的码流为:    Y0 U0 Y1 Y2 V2 Y3 
　　映射出像素点为：[Y0 U0 V2] [Y1 U0 V2] [Y2 U0 V2] [Y3 U0 V2] 

   （4）YUV4:2:0 
　　4:2:0并不意味着只有Y，Cb而没有Cr分量。它指得是对每行扫描线来说，只有一种色度分量以2:1的抽样率存储。
    相邻的扫描行存储不同的色度分量，也就是说，如果一行是4:2:0的话，下一行就是4:0:2，再下一行是4:2:0...
	以此类推。对每个色度分量来说，水平方向和竖直方向的抽样率都是2:1，所以可以说色度的抽样率是4:1。对非压
	缩的8比特量化的视频来说，每个由2x2个2行2列相邻的像素组成的宏像素需要占用6字节内存。 
　　
    下面八个像素为：[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3] [Y5 U5 V5] [Y6 U6 V6] [Y7U7 V7] [Y8 U8 V8] 
　　存放的码流为：  Y0 U0 Y1 Y2 U2 Y3 Y5 V5 Y6 Y7 V7 Y8 
　　映射出的像素点为：[Y0 U0 V5] [Y1 U0 V5] [Y2 U2 V7] [Y3 U2 V7] [Y5 U0 V5] [Y6 U0 V5] [Y7U2 V7] [Y8 U2 V7]


2.DCT变换
    DCT（Discrete Consine Transform）是将图像信号在频率域上进行变换，分离出高频和低频信息的处理过程。然后再对图像的高频部分
（即图像细节）进行压缩，以达到压缩图像数据的目的。

    首先将图像划分为多个8*8的矩阵。然后对每一个矩阵作DCT变换（变换公式此略）。变换后得到一个频率系数矩阵，其中的频率系数都是
浮点数。

3.量化
    由于在后面编码过程中使用的码本都是整数，因此需要对变换后的频率系数进行量化，将之转换为整数。
    由于进行数据量化后，矩阵中的数据都是近似值，和原始图像数据之间有了差异，这一差异是造成图像压缩后失真的主要原因。
    在这一过程中，质量因子的选取至为重要。值选得过大，可以大幅度提高压缩比，但是图像质量就比较差；反之，质量因子越小
（最小为1），图像重建质量越好，但是压缩比越低。对此，ISO已经制定了一组供JPEG代码实现者使用的标准量化值。


4.编码
    从前面过程我们可以看到，颜色转换完成到编码之前，图像并没有得到进一步的压缩，DCT变换和量化可以说是为编码阶段做准备。
	编码采用两种机制：一是0值的行程长度编码；二是熵编码（Entropy Coding）。
    在JPEG中，采用曲徊序列，即以矩阵对角线的法线方向作“之”字排列矩阵中的元素。这样做的优点是使得靠近矩阵左上角、值比
较大的元素排列在行程的前面，而行程的后面所排列的矩阵元素基本上为0值。行程长度编码是非常简单和常用的编码方式，在此不再赘述。

    编码实际上是一种基于统计特性的编码方法。在JPEG中允许采用HUFFMAN编码或者算术编码

	在计算机信息处理中，”哈夫曼编码”是一种一致性编码法（又称”熵编码法”），用于数据的无损耗压缩。

    另外在 huffman 编码区域结束时, 碰到几个 bit 没有用的时候, 应该用 1 去填充.然后后面跟 FF.

*/


//标记类型
#define JPEG_MARKER_TEM      0xFF01 //For temporary use in arithmetic coding
;
//Start Of Frame markers, non-hierarchical Huffman coding
#define JPEG_MARKER_SOF0     0xFFC0 //Sequence DCT
#define JPEG_MARKER_SOF1     0xFFC1 //Extended sequential DCT
#define JPEG_MARKER_SOF2     0xFFC2 //Progressive DCT
#define JPEG_MARKER_SOF3     0xFFC3 //Spatial (sequential) lossless
;
//Start Of Frame markers, hierarchical Huffman coding
#define JPEG_MARKER_SOF5     0xFFC5 //Differential sequential DCT
#define JPEG_MARKER_SOF6     0xFFC6 //Differential progressive DCT
#define JPEG_MARKER_SOF7     0xFFC7 //Differential spatial lossless
;
//Start Of Frame markers, non-hierarchical arithmetic coding
#define JPEG_MARKER_SOF9     0xFFC9 //Extended sequential DCT
#define JPEG_MARKER_SOF10    0xFFCA //Progressive DCT
#define JPEG_MARKER_SOF11    0xFFCB //Spatial (sequential) Lossless
;
//Start Of Frame markers, hierarchical arithmetic coding
#define JPEG_MARKER_SOF13    0xFFCD //Differential sequential DCT
#define JPEG_MARKER_SOF14    0xFFCE //Differential progressive DCT
#define JPEG_MARKER_SOF15    0xFFCF //Differential spatial Lossless

;
//Huffman table specification
#define JPEG_MARKER_DHT      0xFFC4 //Define Huffman Table
;
#define JPEG_MARKER_JPG      0xFFC8 //Reserved for JPEG extensions
;
//arithmetic coding conditioning specification
#define JPEG_MARKER_DAC      0xFFCC //define Define Arithmetic Table(版权保护)
;
//Restart interval termination
#define JPEG_MARKER_RST0     0xFFD0 //Restart with modulo 8 counter 0
#define JPEG_MARKER_RST1     0xFFD1 //Restart with modulo 8 counter 1
#define JPEG_MARKER_RST2     0xFFD2 //Restart with modulo 8 counter 2
#define JPEG_MARKER_RST3     0xFFD3 //Restart with modulo 8 counter 3
#define JPEG_MARKER_RST4     0xFFD4 //Restart with modulo 8 counter 4
#define JPEG_MARKER_RST5     0xFFD5 //Restart with modulo 8 counter 5
#define JPEG_MARKER_RST6     0xFFD6 //Restart with modulo 8 counter 6
#define JPEG_MARKER_RST7     0xFFD7 //Restart with modulo 8 counter 7
;
//Other marker
#define JPEG_MARKER_SOI      0xFFD8 //Start Of Image
#define JPEG_MARKER_EOI      0xFFD9 //End Of Image
#define JPEG_MARKER_SOS      0xFFDA //Start Of Scan
#define JPEG_MARKER_DQT      0xFFDB //Define Quantization Table(s)
#define JPEG_MARKER_DNL      0xFFDC //Define Number of Lines
#define JPEG_MARKER_DRI      0xFFDD //Define Restart Interval
#define JPEG_MARKER_DHP      0xFFDE //Define hierarchical progression
#define JPEG_MARKER_EXP      0xFFDF //Expand reference image(s)

;
//Reserved for application use
#define JPEG_MARKER_APP0     0xFFE0 //Reserved for application use
#define JPEG_MARKER_APP1     0xFFE1 //Reserved for application use
#define JPEG_MARKER_APP2     0xFFE2 //Reserved for application use
#define JPEG_MARKER_APP3     0xFFE3 //Reserved for application use
#define JPEG_MARKER_APP4     0xFFE4 //Reserved for application use
#define JPEG_MARKER_APP5     0xFFE5 //Reserved for application use
#define JPEG_MARKER_APP6     0xFFE6 //Reserved for application use
#define JPEG_MARKER_APP7     0xFFE7 //Reserved for application use
#define JPEG_MARKER_APP8     0xFFE8 //Reserved for application use
#define JPEG_MARKER_APP9     0xFFE9 //Reserved for application use
#define JPEG_MARKER_APP10    0xFFEA //Reserved for application use
#define JPEG_MARKER_APP11    0xFFEB //Reserved for application use
#define JPEG_MARKER_APP12    0xFFEC //Reserved for application use
#define JPEG_MARKER_APP13    0xFFED //Reserved for application use
#define JPEG_MARKER_APP14    0xFFEE //Reserved for application use
#define JPEG_MARKER_APP15    0xFFEF //Reserved for application use
;
#define JPEG_MARKER_COM      0xFFFE //Comment

#define JPEG_MARKER_JPG0     0xFFF0 //Reserved for JPEG extension
#define JPEG_MARKER_JPG1     0xFFF1 //Reserved for JPEG extension
#define JPEG_MARKER_JPG2     0xFFF2 //Reserved for JPEG extension
#define JPEG_MARKER_JPG3     0xFFF3 //Reserved for JPEG extension
#define JPEG_MARKER_JPG4     0xFFF4 //Reserved for JPEG extension
#define JPEG_MARKER_JPG5     0xFFF5 //Reserved for JPEG extension
#define JPEG_MARKER_JPG6     0xFFF6 //Reserved for JPEG extension
#define JPEG_MARKER_JPG7     0xFFF7 //Reserved for JPEG extension
#define JPEG_MARKER_JPG8     0xFFF8 //Reserved for JPEG extension
#define JPEG_MARKER_JPG9     0xFFF9 //Reserved for JPEG extension
#define JPEG_MARKER_JPG10    0xFFFA //Reserved for JPEG extension
#define JPEG_MARKER_JPG11    0xFFFB //Reserved for JPEG extension
#define JPEG_MARKER_JPG12    0xFFFC //Reserved for JPEG extension
#define JPEG_MARKER_JPEG13   0xFFFD //Reserved for JPEG extension


/*
	由于JPEG的无损压缩方式并不比其他的压缩方法更优秀，因此我们着重来看它的有损压缩。以一幅24位彩色图像
为例，JPEG的压缩步骤分为：1.颜色转换 2.DCT变换 3.量化 4.编码

1.颜色转换
	由于JPEG只支持YUV颜色模式的数据结构，而不支持RGB图像数据结构，所以在将彩色图像进行压缩之前，必须先对颜色模式
进行数据转换。各个值的转换可以通过下面的转换公式计算得出：

Y=0.2990R+0.5870G+0.1140B
U=-0.169R-0.3313G+0.5B
V=0.5R-0.4187G-0.0813B

Y=0.3R+0.59G+0.11B
U=B-Y
V=R-Y

R = Y+1.402V
G = Y-0.34414U-0.71414V
B = Y+1.772U


Y =0.2990 R + 0.5870G + 0.1140B + 0
Cr=0.5000 R - 0.4187G - 0.0813B + 128
Cb=-0.1687R - 0.3313G + 0.5B    + 128


R = Y + 1.402 (Cr-128)
G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
B = Y + 1.772 (Cb-128)


其中，Y表示亮度，U和V表示颜色。
    转换完成之后还需要进行数据采样。一般采用的采样比例是2：1：1或4：2：2。由于在执行了此项工作之后，每两行数据
只保留一行，因此，采样后图像数据量将压缩为原来的一半。

2.DCT变换
DCT（Discrete Consine Transform）是将图像信号在频率域上进行变换，分离出高频和低频信息的处理过程。然后再对图像
的高频部分（即图像细节）进行压缩，以达到压缩图像数据的目的。
    首先将图像划分为多个8*8的矩阵。然后对每一个矩阵作DCT变换（变换公式此略）。变换后得到一个频率系数矩阵，其中
的频率系数都是浮点数。

3.量化
    由于在后面编码过程中使用的码本都是整数，因此需要对变换后的频率系数进行量化，将之转换为整数。
	由于进行数据量化后，矩阵中的数据都是近似值，和原始图像数据之间有了差异，这一差异是造成图像压缩后失真的主要
原因。
	在这一过程中，质量因子的选取至为重要。值选得过大，可以大幅度提高压缩比，但是图像质量就比较差；反之，质量因
子越小（最小为1），图像重建质量越好，但是压缩比越低。对此，ISO已经制定了一组供JPEG代码实现者使用的标准量化值。

	量化表的计算方法如下：对于1<=Q<=99，
	用JPEG组织的公式[5]来计算一个标量量化因子S：
	S=5000/Q假如1<=Q<=50
	S=200-2*Q假如51<=Q<=99--------------http://www.xue5.com




4.编码
    从前面过程我们可以看到，颜色转换完成到编码之前，图像并没有得到进一步的压缩，DCT变换和量化可以说是为编码阶段
做准备。编码采用两种机制：一是0值的行程长度编码；二是熵编码（Entropy Coding）。

	RLE算法对AC压缩,假设输入数据1,0,0,0,3,0,5 ,输出为(0,1)(3,3)(1,5),左位表示右位数据前0的个数,左位用4bits表示,
0的个数超过表示范围则输出为(15,0),其余的0数据在下一个符号中表示

  在JPEG中，采用曲徊序列，即以矩阵对角线的法线方向作“之”字排列矩阵中的元素。这样做的优点是使得靠近矩阵左上
角、值比较大的元素排列在行程的前面，而行程的后面所排列的矩阵元素基本上为0值。行程长度编码是非常简单和常用的编码
方式，在此不再赘述。

    编码实际上是一种基于统计特性的编码方法。在JPEG中允许采用HUFFMAN编码或者算术编码。

5. 解码
    解码程序先从 JPG 文件中读出采样系数, 这样就知道了 MCU 的大小, 算出整个图象有几个 MCU. 解码程序再循环逐个对 
MCU 解码, 一直到检查到 EOI 标记. 对于每个MCU, 按正规的次序解出每个 DU, 然后组合, 转换成 (R,G,B) 就 OK 了

*/

/*
http://en.wikipedia.org/wiki/JPEG#JPEG_file_extensions
http://www.chinavib.com/space/37560/viewspace-16672.html
http://blog.21ic.com/user1/942/archives/2006/11364.html
http://jpkc.zust.edu.cn/2007/dmt/course/Mmt08_03_2.htm


(符號)       (標記代碼)         (說明)
Symbol    Code Assignment   Deforbiddenion

Start Of Frame markers, non-hierarchical Huffman coding
SOF0        0xFFC0        Baseline DCT
SOF1        0xFFC1        Extended sequential DCT
SOF2        0xFFC2        Progressive DCT
SOF3        0xFFC3        Spatial (sequential) lossless

Start Of Frame markers, hierarchical Huffman coding
SOF5        0xFFC5        Differential sequential DCT
SOF6        0xFFC6        Differential progressive DCT
SOF7        0xFFC7        Differential spatial lossless

Start Of Frame markers, non-hierarchical arithmetic coding
JPG        0xFFC8        Reserved for JPEG extensions
SOF9        0xFFC9        Extended sequential DCT
SOF10        0xFFCA        Progressive DCT
SOF11        0xFFCB        Spatial (sequential) Lossless

Start Of Frame markers, hierarchical arithmetic coding
SOF13        0xFFCD        Differential sequential DCT
SOF14        0xFFCE        Differential progressive DCT
SOF15        0xFFCF        Differential spatial Lossless

Huffman table specification
DHT        0xFFC4        Define Huffman table(s)

arithmetic coding conditioning specification
DAC        0xFFCC        Define arithmetic conditioning table

Restart interval termination
RSTm        0xFFD0～0xFFD7        Restart with modulo 8 counter m

Other marker
SOI        0xFFD8        Start of image
EOI        0xFFD9        End of image
SOS        0xFFDA        Start of scan
DQT        0xFFDB        Define quantization table(s)
DNL        0xFFDC        Define number of lines
DRI        0xFFDD        Define restart interval
DHP        0xFFDE        Define hierarchical progression
EXP        0xFFDF        Expand reference image(s) 
APPn        0xFFE0～0xFFEF        Reserved for application use
JPGn        0xFFF0～0xFFFD        Reserved for JPEG extension
COM        0xFFFE        Comment

Reserved markers
TEM        0xFF01        For temporary use in arithmetic coding
RES        0xFF02～0xFFBF        Reserved

*/

//JPEG文件的调色板
struct JPEG_RGBQUAD
{
	BYTE rgbRed;
	BYTE rgbGreen;
	BYTE rgbBlue;
};

//定义JPEG颜色空间
typedef enum 
{
	JPEG_COLORSPACE_UNKNOWN = 0,	//非法
	JPEG_COLORSPACE_GRAYSCALE,		//单色
	JPEG_COLORSPACE_RGB,		    //RGB
	JPEG_COLORSPACE_YCbCr,		    //YCbCr = YUV
	JPEG_COLORSPACE_CMYK,		    //CMYK
	JPEG_COLORSPACE_YCCK		    //YCbCrK
}JPEGCOLORSPACE;



typedef struct tagJPEG_PUBLIC
{
	BYTE byMarker1;
	BYTE byMarker2;
}JPEG_PUBLIC;

typedef struct tagJPEG_PUBLIC2
{
	BYTE byMarker1;
	BYTE byMarker2;
	WORD wSize;
}JPEG_PUBLIC2;

typedef struct tagJPEG_HEAD
{
	BYTE byMarker1;  //0xFFD8，网络顺序
	BYTE byMarker2;
}JPEG_HEAD;





typedef struct tagJPEG_APP0_BLOCK
{
	BYTE byMarker1;  //0xFFEn=0xFFE0，网络顺序
	BYTE byMarker2;

	WORD wSize;    //0x0010，网络顺序	
	BYTE bysFlags[5]; //TIXX=4A,46,49,46,0, TIFF

	WORD wVersion;

	BYTE byDensityUnit;//0：无单位;1：点数/英寸;2：点数/厘米
	WORD wDensityX;    //水平方向像素密度
	WORD wDensityY;    //Y方向像素密度
	BYTE byThumbnailX; //缩略图水平像素数目
	BYTE byThumbnailY; //缩略图垂直像素数目

	JPEG_RGBQUAD Quad[1];//个数受byThumbnailX和byThumbnailY控制
}JPEG_APP0_BLOCK;

typedef struct tagJPEG_APPLICATION_D
{
	BYTE byMarker1;  //0xFFEn=0xFFED，网络顺序
	BYTE byMarker2;

	WORD wSize;    //0x09AE，网络顺序	
	BYTE bysSign[13]; //"Photoshop 3.0 "
	
	BYTE bysReserve[10];
}JPEG_APPLICATION_D;

typedef struct tagJPEG_APPLICATION_C
{
	BYTE byMarker1;  //0xFFEn=0xFFEC，网络顺序
	BYTE byMarker2;

	WORD wSize;    //0x0011，网络顺序	
	BYTE bysSign[5]; //"Ducky"
	
	BYTE bysReserve[10];
}JPEG_APPLICATION_C;

typedef struct tagJPEG_APPLICATION_E
{
	BYTE byMarker1;  //0xFFEn=0xFFEE，网络顺序
	BYTE byMarker2;

	WORD wSize;    //0x000E，网络顺序
	BYTE bysSign[5]; //"Adobe"
	
	BYTE bysReserve[7];
}JPEG_APPLICATION_E;


//HUMAN 哈夫曼表-DHT
typedef struct tagJPEG_DHT_BLOCK
{
	BYTE byMarker1;  //0xFFC4,网络顺序
	BYTE byMarker2;

	WORD wSize;    //0x00F6,包的大小,不包括“标记”占用的2个字节空间

	//一个单独的 DHT 段可以包含多个 HT, 每个都有自己的信息字节
	struct
	{
		BYTE byID:4;
		BYTE byStyle:1;//0:DC,1:AC
		BYTE byReserve:3;

	}Attrib;
	BYTE bysBitTable[16];
	BYTE bysValueTable[1]; //长度 = Sum(bysBitTable[i])

}JPEG_DHT_BLOCK;


//Y分量代表了亮度信息，UV分量代表了色差信息。相比之下，人眼对于Y分量更为敏感
//量化表的作用就是对于一些不需要的量进行去除，这也是JPEG有损压缩损失数据的关键。
//一般有两个量化表，一个给Y分量，另一个给UV分量。其实，他们也可以共
//用一个量化表。

//量化表-DQT
typedef struct tagJPEG_DQT_BLOCK//QTQUANTIZATION_TABLE
{
	BYTE byMarker1;  //0xFFDB,网络顺序
	BYTE byMarker2;

	WORD wSize;    //0x0084，包的大小,不包括“标记”占用的2个字节空间

//一个单独的 DQT 段可以包含多个 QT, 每个都有自己的信息字节,当精度=1 (16 bit), 
//每个字都是高位在前低位在后

	struct
	{
		BYTE byID:4; //nQT=0~3
		BYTE byPrecision:4; //0->8Bit,other = 16Bit
	}Attrib;



	//BYTE bysData[8x8*wNumber] //n = 64*(精度+1)

}JPEG_DQT_BLOCK;//QUANTIZATION_TABLE;


struct JPEG_SOF_COMPONENT_ATTRIB
{
	BYTE byID_Component; //(1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q)
	struct
	{
		//JPEG标准规定了最小编码单元MCU，要求Hy×Vy+Hu×Vu+Hv×Vv≤10。		
		BYTE byFactorV: 4; //垂直采样系数
		BYTE byFactorH: 4; //水平采样系数
	}Factor;//采样系数
	BYTE byID_DQT; //quantization table 号
};

typedef struct tagJPEG_SOF_BLOCK//FRAME_DCT_BASELINE
{
	BYTE byMarker1;  //0xFFC0,网络顺序
	BYTE byMarker2;

	WORD wSize;    //网络顺序,0x11,包的大小,不包括“标记”占用的2个字节空间
	               //Size = 8+components*3
	               
	BYTE byPrecision; //精度，每个颜色分量像系的位数

	WORD wImageH; //网络顺序
	WORD wImageW;//网络顺序

	BYTE byComponentNum; //灰度图是 1, YCbCr/YIQ 彩色图是 3, CMYK 彩色图是 4


	JPEG_SOF_COMPONENT_ATTRIB Attrib[1]; //个数受ComponentNum控制

}JPEG_SOF_BLOCK;//FRAME_DCT_BASELINE;

struct JPEG_SOS_COMPONENT_ATTRIB
{
	BYTE byID_Component; //1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q
	struct //????????????????????????
	{
		BYTE byID_DHT_AC:4; //??AC,使用的 Huffman 表索引
		BYTE byID_DHT_DC:4; //??DC,使用的 Huffman 表索引
	}TableID;
};

//
typedef struct tagJPEG_SOS_BLOCK
{
	WORD wMarker;  //0xFFDA,网络顺序
	WORD wSize;    //0x000C，包的大小,不包括“标记”占用的2个字节空间
	               //必须是 6+2*(扫描行内组件的数量)

	BYTE byComponentNum; //扫描行内组件的数量,必须1~4，通常是 3

	JPEG_SOS_COMPONENT_ATTRIB Attrib[1]; 
	
	//BYTE Ss;     //谱选择开始，基本系统中为0
 	//BYTE Se;     //谱选择结束，基本系统中为63
 	//BYTE Bf;     //高位和低位的普选择，基本系统中为0


}JPEG_SOS_BLOCK;

//注释
typedef struct tagJPEG_COMMENT_BLOCK
{
	BYTE byMarker1;  //0xFFFE,网络顺序
	BYTE byMarker2;

	WORD wSize;    //0x0026，包的大小,不包括“标记”占用的2个字节空间


	//BYTE bysReserve[1]; //数量=wSize - 2;

}JPEG_COMMENT_BLOCK;

//Define Restart Interval
typedef struct tagJPEG_DRI_BLOCK
{
	BYTE byMarker1;  //0xFFFE,网络顺序
	BYTE byMarker2;

	WORD wSize;    //=0x4，包的大小,不包括“标记”占用的2个字节空间

	WORD wInterval; //MCU 块的单元中的重新开始间隔 (高字节, 低字节)
	                //意思是说, 每 n 个 MCU 块就有一个 RSTn 标记.
                    //第一个标记是 RST0, 然后是 RST1 等, RST7 后再从 RST0 重复

}JPEG_DRI_BLOCK;


//Huffman码结构
typedef struct tagJPEG_HUFF_NODE
{
	WORD wCode;    //huffman 码字
	BYTE byLength;  //编码长度
	WORD wValue;     //码字对应的值
}JPEG_HUFF_NODE, *LPJPEG_HUFF_NODE;

//数据单元DU的宽度
#define JPEG_DU_X  8u
//数据单元DU的高度
#define JPEG_DU_Y  8u
//数据单元DU的大小
#define JPEG_DU_T  (8u*8u)

//解压前的图象数据缓冲区的CACHE长度
#define JPEG_DECODE_CACHE_LENGTH (4 * 1024)


//---------------------------------------------------------------------------
//编码采用机制：
#define JPEG_CODING_RLE_RLE_0	        0 //0值的行程长度编码
#define JPEG_CODING_ENTROPY_HUFFMAN		1 //熵编码->哈夫曼编码
#define JPEG_CODING_ENTROPY_ARITHMETIC	2 //熵编码->自适应二进制算术编码

//JPEG压缩模式定义
#define JPEG_MODE_SEQUENTIAL    0 //普通JPEG模式，顺序的基于DCT（Sequential DCT-based ）模式 
#define JPEG_MODE_PROGRESSIVE   1 //普通JPEG模式，累进的基于DCT（Progressive DCT-based）模式 
#define JPEG_MODE_LOSSLESS      2 //普通JPEG模式，无损（Lossless）模式 
#define JPEG_MODE_HIERARCHICAL  3 //普通JPEG模式，分层（Hierarchical）模式 
#define JPEG2000_MODE_DCT       4 //JPEG2000模式，基于DCT的编码模式
#define JPEG2000_MODE_WAVELET   5 //JPEG2000模式，基于小波的编码模式

//SOS的ID值
#define JPEG_SOS_ID_Y   1
#define JPEG_SOS_ID_Cb  2
#define JPEG_SOS_ID_Cr  3
#define JPEG_SOS_ID_I   4
#define JPEG_SOS_ID_Q   5

//HUFFNODE表缓冲区的长度
#define MAX_HUFFNODE_DC_LENGTH 16  //直流系数的HUFFMAN表长度
#define MAX_HUFFNODE_AC_LENGTH 256 //交流系数的HUFFMAN表开度


class CJpeg
{
private:
	//
	int m_nMode;
	int m_nScan;

	//一边解码一边画图标志
	BOOL m_bDirect;

	//CACHE初始化标志
	//BOOL m_bCacheInit;

	#ifdef _WINDOWS_
		//解压前的图象数据缓冲区
		HGLOBAL m_hXmsMemory; 
		BYTE* m_pXmsMemory;
		//解压后的图象数据缓冲区
		HGLOBAL m_hXmsMemory_Bitmap;
		BYTE* m_pXmsMemory_Bitmap;
		//累进模式的暂存数据
		HGLOBAL m_hXmsMemory_Progressive;
		BYTE* m_pXmsMemory_Progressive;
		//解压前的图象数据缓冲区的当前位置
		long  m_gXmsPoint;


		//Win32的DC名柄
		HDC   m_hDC;
	#else
		//解压前的图象数据缓冲区
		CXmsMemory* m_pXmsMemory;
		//解压后的图象数据缓冲区
		CXmsMemory* m_pXmsMemory_Bitmap;
		//累进模式的暂存数据
		CXmsMemory* m_pXmsMemory_Progressive;
		//解压前的图象数据缓冲区的当前位置
		long  m_gXmsPoint;

	#endif
	
	

	//解压前的图象数据缓冲区的CACHE
	BYTE m_bysCache[JPEG_DECODE_CACHE_LENGTH];
	//CACHE的位置索引
	int   m_nCacheIndex;
	//CACHE的位置数据指针
	BYTE* m_pNew;

	//从HUFFMAN树中查询到的结果（临时存贮）
	JPEG_HUFF_NODE m_HuffNode;
	//HUFFMAN指针（临时存贮）
	JPEG_HUFF_NODE* m_pHuffTree;
private:
	//显示的左上角X坐标
	int  m_nLeft;
	//显示的左上角Y坐标
	int  m_nTop;
	//图象宽度
	int  m_nImageWidth;
	//图像高度
	int  m_nImageHeight;

	//图象对齐后宽度
	int  m_nAlignWidth;
	//图像对齐后的高度
	int  m_nAlignHeight;

	//MCU的宽度，以象素为单位
	int  m_nMcuWidth;
	//MCU的高度，以象素为单位
	int  m_nMcuHeight;

	//图象的缩入比例
	int  m_nScale;

	//Y通道的采样因子
	int  m_nScaleY;
	//Cb通道的采样因子
	int  m_nScaleCb;
	//Cr通道的采样因子
	int  m_nScaleCr;
private:
	//DC通道的哈夫曼表的原始数值
	BYTE m_DHTCount_DC[4][16];
	BYTE m_DHTTable_DC[4][256];

	//AC通道的哈夫曼表的原始数值
	BYTE m_DHTCount_AC[4][16];
	BYTE m_DHTTable_AC[4][256];

	//哈夫曼表的建立数据
	JPEG_HUFF_NODE m_HuffNode_DC[4][MAX_HUFFNODE_DC_LENGTH];
	JPEG_HUFF_NODE m_HuffNode_AC[4][MAX_HUFFNODE_AC_LENGTH];


	//两个质量表
	BYTE m_DQTTable[6][2 * 64];



	//分量个数
	int m_nComponentCount;
	//SOS属性
	JPEG_SOS_COMPONENT_ATTRIB  m_SOSAttrib[5];
	//SOF属性
	JPEG_SOF_COMPONENT_ATTRIB m_SOFAttrib[5 + 1];

private:
	BOOL m_bSOS;
	BYTE m_bySs; //谱选择开始
	BYTE m_bySe; //谱选择结束
	union 
	{
		BYTE m_byBf; 
		struct
		{
			BYTE AL:4;
			BYTE AH:4;
		}byBf;
	}Bf;//高低位谱选择

	//MCU计数
	DWORD m_dwMCU_Counter;
	//MCU的总个数
	DWORD m_dwMCU_Number;
	//通道的数据单元交织计数
	int m_nPoint_DU;

	//数据单元DU内部指针
	int   m_nDecodePoint;
	//每一个MCU内部的Y，Cb，Cr指示指针
	int   m_nSOS_ID;

	//三个直流分量
	SHORT  m_shDPCM[3];
	
	//解码后的Y,Cr,Cb数据缓冲区
	BYTE  m_bysY[4][64];
	BYTE  m_bysCr[4][64];
	BYTE  m_bysCb[4][64];

	//Y，Cr,Cb位置
	BYTE  m_bysLocY[256];
	BYTE  m_bysLocC[256];


	//解码流的有效位数
	int  m_nTotalBit;
	//解码流
	DWORD m_dwHuffman;
	//从图象数据中读出的一个字节（临时）
	BYTE  m_byStream;

	//结束标志
	BOOL m_bEnd;
	//JIIF文件标志
	BOOL m_bSOI;
	BOOL m_bSOF;
private:
	//装入图象数据
	BOOL LoadImage(FILE* pFile, long gLoc);

	//图像宽度和高度对齐
	int ModifyScreen(int nImage, int nFactor);
	//计算MCU的宽度和高度，以8象素为单位
	void CalcFactorWH(int* pFactor_MaxX, int* pFactor_MaxY);
	//跳过文件中的指定字节
	void Discard(FILE* pFile, WORD wLength);
	//计算最大值
	//BYTE GetMax(BYTE* pValueTable, int nCount);
	//计算哈夫曼表的尺寸
	int  CalcHuffmanSum(BYTE* pBitTable, int nCount);
	//建立哈夫曼表
	void BuildHuffmanTable(BYTE* pBitTable,
			BYTE* pValueTable, JPEG_HUFF_NODE* pHuffCode, int nMaxLength);
	//将主机字节转成网络字节
	WORD htons(WORD wData)
	{
		return ((wData >> 8) | (wData << 8));
	}
	//将网络字节转成主机字节
	WORD ntohs(WORD wData)
	{
		return ((wData >> 8) | (wData << 8));
	}
	
	//读入HUFFMAN表
	BOOL ReadDHT();

	//将原始图象数据装入到CACHE中
	void LoadCache(long gOff = 0L);

	//从图象数据中读入一个字节
	inline void ReadByte();//BOOL* pnRst, BOOL* pbImageEnd);

	//哈夫曼表查询（注：为了减少CPU时间开销，设计成无输入参数形式）
	BOOL Search_Huffman();

	//反ZIGZAG运算
	void IZigzag(SHORT* pshCode);
	//反质量运算
	void IDQT(SHORT* pshCode, const BYTE* pDQT_Table);
	//反I128运算
	void I128(BYTE* pbyCode, const SHORT* pshIDCT);
	//反RGB运算
	void IRGB();//BYTE* pBufferY, BYTE* pBufferCr, BYTE* pBufferCb);
	//反RGB运算--Y:Cb:Cr = 1:1:1
	void IRGB_111();
	//反RGB运算--Y:Cb:Cr = 4:1:1
	//反RGB运算--Y:Cb:Cr = 2:1:1
public:
	//是否边解压边显示标志
	void SetDirect(BOOL bDirect){m_bDirect = bDirect;}

	#ifdef _WINDOWS_
		//WIN32版本的设置属性
		void SetAttrib(int nLeft, int nTop, int nScale, HDC hDC)
		{
			m_nLeft = nLeft;
			m_nTop  = nTop;
			m_nScale = nScale;
			m_hDC = hDC;
		}
	#else
		//DOS-WINSAIL版本的设置属性
		void SetAttrib(int nLeft, int nTop, int nScale)
		{		
			m_nLeft = nLeft;
			m_nTop  = nTop;
			m_nScale = nScale;
		}
	#endif

	//装入文件
	BOOL LoadJpegFile(const char* strFile);
	//装入文件
	//BOOL LoadJpegFileEx(const char* strFile);
	//解码
	BOOL Decode();//int nLeft, int nTop, int nScale);
	//显示
	void Display();

	void CreateLocation();

private:
	void InitJpeg();

	//第一篇扫描直流
	BOOL ScanDC_Before();
	//后续扫描直流
	BOOL ScanDC_After();
	//第一篇扫描交流
	BOOL ScanAC_Before();
	//后续扫描交流
	BOOL ScanAC_After();
	
public:
	BOOL ReadDHP();
	void FreeJPEGMemory();
	long CalcImagePointer();
	void DOS_YUV_To_Color(void* pMCU);
	void DCT_ProgressiveToBitmap();
	void Decode_DU(SHORT* pshSrc, int nSOS_ID,  int nPoint_DU);

	int  m_nErrorCode;
	BOOL ReadDAC();
	void IDCT(SHORT* pshCode);
	void SeekXms(long gXms);
	BOOL FindSOS(BOOL bSeek = FALSE);
	BOOL Scan();
	BOOL ReadApp0();
	BOOL ReadSOF(BYTE byMarker2);
	BOOL ReadRST(BYTE byMarker2);
	BOOL ReadAppN(BYTE byMarker2);
	BOOL ReadDQT();
	BOOL ReadDiscard(BYTE byMarker2, char* strMarker);
	BOOL ReadDRI();
	BOOL ReadDNL();
	BOOL ReadEOI();
	BOOL ReadComment();
	BOOL ReadTEM();
	BOOL ReadSOI();
	BOOL ReadSOS();

	
	

	CJpeg();
	~CJpeg();
};



#ifdef _WINDOWS_
	BOOL DisplayJpgFile(int nLeft, int nTop, const char* strFile, int nScale, HDC hDC);
#else
	BOOL DisplayJpgFile(int nLeft, int nTop, const char* strFile, int nScale);
#endif


#ifdef _WINDOWS_
	#pragma pack(pop)
#endif

#endif