[Image_Codec]常见图片格式的封装及编解码-Android平台(三)JPG

[TOC]

JPG图片格式

JPG全称Jpeg(Joint Photographic Experts Group),是一种常用的,有损压缩图片格式。压缩比例可以选择,这样可以在文件大小和图片质量间做一个平衡。通常采用10:1的压缩比例。而实际上,JPEG是JPEG/ExifJPEG/JFIF等统称,所以Jpeg一般采用.jpg and .jpeg, though .jpe, .jfif and .jif等文件后缀。JPEG压缩方案可以很好地压缩类似的色调,但是 JPEG 压缩方案不能很好地处理亮度的强烈差异或处理纯色区域。

压缩模式和步骤

JPEG的压缩模式有以下几种:

  • 顺序式编码(Sequential Encoding)
    一次将图像由左到右、由上到下顺序处理。
  • 递增式编码(Progressive Encoding)
    当图像传输的时间较长时,可将图像分数次处理,以从模糊到清晰的方式来传送图像(效果类似GIF在网络上的传输)。
  • 无失真编码(Lossless Encoding)
  • 阶梯式编码(Hierarchical Encoding)
    图像以数种分辨率来压缩,其目的是为了让具有高分辨率的图像也可以在较低分辨率的设备上显示。

压缩步骤:

  • 颜色转换
    由于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表示颜色。
    转换完成之后还需要进行数据采样。一般采用的采样比例是4:1:1或4:2:2。由于在执行了此项工作之后,每两行数据只保留一行,因此,采样后图像数据量将压缩为原来的一半。

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

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

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

JPG文件结构

我们将前面的Png图片,通过软件转换为Jpeg图片:

JPG样例图片

放大后的效果:

放大的JPG样例图片

看看转换后的效果图,面目全非,这就是有损压缩的结果,图片已经失真~,我们这里只是为了说明问题用。

用二级制工具打开jpg_4_2_32bit.jpg:

00000000: ffd8 ffe0 0010 4a46 4946 0001 0101 0060  ......JFIF.....`
00000010: 0060 0000 ffdb 0043 0002 0101 0201 0102  .`.....C........
00000020: 0202 0202 0202 0203 0503 0303 0303 0604  ................
00000030: 0403 0507 0607 0707 0607 0708 090b 0908  ................
00000040: 080a 0807 070a 0d0a 0a0b 0c0c 0c0c 0709  ................
00000050: 0e0f 0d0c 0e0b 0c0c 0cff db00 4301 0202  ............C...
00000060: 0203 0303 0603 0306 0c08 0708 0c0c 0c0c  ................
00000070: 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c  ................
00000080: 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c  ................
00000090: 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c ffc0  ................
000000a0: 0011 0800 0200 0403 0122 0002 1101 0311  ........."......
000000b0: 01ff c400 1f00 0001 0501 0101 0101 0100  ................
000000c0: 0000 0000 0000 0001 0203 0405 0607 0809  ................
000000d0: 0a0b ffc4 00b5 1000 0201 0303 0204 0305  ................
000000e0: 0504 0400 0001 7d01 0203 0004 1105 1221  ......}........!
000000f0: 3141 0613 5161 0722 7114 3281 91a1 0823  1A..Qa."q.2....#
00000100: 42b1 c115 52d1 f024 3362 7282 090a 1617  B...R..$3br.....
00000110: 1819 1a25 2627 2829 2a34 3536 3738 393a  ...%&'()*456789:
00000120: 4344 4546 4748 494a 5354 5556 5758 595a  CDEFGHIJSTUVWXYZ
00000130: 6364 6566 6768 696a 7374 7576 7778 797a  cdefghijstuvwxyz
00000140: 8384 8586 8788 898a 9293 9495 9697 9899  ................
00000150: 9aa2 a3a4 a5a6 a7a8 a9aa b2b3 b4b5 b6b7  ................
00000160: b8b9 bac2 c3c4 c5c6 c7c8 c9ca d2d3 d4d5  ................
00000170: d6d7 d8d9 dae1 e2e3 e4e5 e6e7 e8e9 eaf1  ................
00000180: f2f3 f4f5 f6f7 f8f9 faff c400 1f01 0003  ................
00000190: 0101 0101 0101 0101 0100 0000 0000 0001  ................
000001a0: 0203 0405 0607 0809 0a0b ffc4 00b5 1100  ................
000001b0: 0201 0204 0403 0407 0504 0400 0102 7700  ..............w.
000001c0: 0102 0311 0405 2131 0612 4151 0761 7113  ......!1..AQ.aq.
000001d0: 2232 8108 1442 91a1 b1c1 0923 3352 f015  "2...B.....#3R..
000001e0: 6272 d10a 1624 34e1 25f1 1718 191a 2627  br...$4.%.....&'
000001f0: 2829 2a35 3637 3839 3a43 4445 4647 4849  ()*56789:CDEFGHI
00000200: 4a53 5455 5657 5859 5a63 6465 6667 6869  JSTUVWXYZcdefghi
00000210: 6a73 7475 7677 7879 7a82 8384 8586 8788  jstuvwxyz.......
00000220: 898a 9293 9495 9697 9899 9aa2 a3a4 a5a6  ................
00000230: a7a8 a9aa b2b3 b4b5 b6b7 b8b9 bac2 c3c4  ................
00000240: c5c6 c7c8 c9ca d2d3 d4d5 d6d7 d8d9 dae2  ................
00000250: e3e4 e5e6 e7e8 e9ea f2f3 f4f5 f6f7 f8f9  ................
00000260: faff da00 0c03 0100 0211 0311 003f 00f9  .............?..
00000270: 1ffe 0afd e28d 4ff6 66ff 0082 887c 4af0  ......O.f....|J.
00000280: 9fc3 7d46 fbe1 f785 6c75 3716 fa37 86a7  ..}F....lu7..7..
00000290: 7d27 4fb7 da4c 6bb2 0b72 91ae 1234 4185  }'O..Lk..r...4A.
000002a0: e151 4740 0514 515f a064 1ff2 2ac2 7fd7  .QG@..Q_.d..*...
000002b0: aa5f fa6e 27f1 ff00 8a5f f254 e2fd 63ff  ._.n'...._.T..c.
000002c0: 00a4 44ff d9                             ..D..

转换后的图片的GFIF的存储格式。JPEG文件大体可以分为两个部分:

  • 标记码
    由两个字节构成,其中,前一个字节是固定值0XFF代表了一个标记码的开始,后一个字节不同的值代表着不同的含义。需要提醒的是,连续的多个0XFF可以理解为一个0XFF,并表示一个标记码的开始。另外,标记码在文件中一般是以标记代码的形式出现的。例如,SOI的标记代码是0XFFD8,即,如果JPEG文件中出现了0XFFD8,则代表此处是一个SOI标记。

  • 压缩数据
    一个完整的两字节标记码的后面,就是该标记码对应的压缩数据了,它记录了关于文件的若干信息。

一些典型的标记码,及其所代表的含义如下所示:

  • SOI
00000000: ffd8 ---- ---- ---- ---- ---- ---- ----  ......JFIF.....
标记码 字节 Payload 标记码名 描述
SOI 0xFF 0xD8 none Start Of Image 图像开始,标记代码为固定值0XFFD8,用2字节表示
  • APP0
00000000: ---- ffe0 0010 4a46 4946 0001 0101 0060  ......JFIF.....`
00000010: 0060 0000 ---- ---- ---- ---- ---- ----  .`.....C........
标记码 字节 Payload 标记码名 描述
APP0 0XFFE0 Application 0 应用程序保留标记0,标记代码为固定值0XFFE0,用2字节表示;该标记码之后包含了9个具体的字段

APP0包括8个字段:

Byte 描述
ffe0 APP0 标记
0010 数据长度:2个字节,用来表示(1)--(9)的9个字段的总长度,即不包含标记代码但包含本字段
4a46 4946 00 标示符:5个字节,固定值0X4A6494600,表示了字符串“JFIF0”
0101 版本号:2个字节,一般为0X0102,表示JFIF的版本号为1.2;但也可能为其它数值,从而代表了其它版本号
01 X,Y方向的密度单位:1个字节,只有三个值可选,0:无单位;1:点数每英寸;2:点数每厘米
0060 X方向像素密度:2个字节,取值范围未知
0060 Y方向像素密度:2个字节,取值范围未知
00 缩略图垂直像素数目:1个字节,取值范围未知
00 缩略图垂直像素数目:1个字节,取值范围未知

缩略图RGB位图:长度可能是3的倍数,保存了一个24位的RGB位图;如果没有缩略位图(这种情况更常见),则字段(7)(8)的取值均为0

  • APPn

App0也算是AppN中特殊的一个

标记码 字节 Payload 标记码名 描述
APPn 0xFF, 0xEn variable size Application n 应用程序保留标记n(n=1---15),标记代码为2个字节,取值为0XFFE1--0XFFFF;包含了两个字段:
(1)数据长度,2个字节,表示(1)(2)两个字段的总长度;即,不包含标记代码,但包含本字段;
(2)详细信息:数据长度-2个字节,内容不定;
  • DQT
00000010: ---- ---- ffdb 0043 0002 0101 0201 0102  .`.....C........
00000020: 0202 0202 0202 0203 0503 0303 0303 0604  ................
00000030: 0403 0507 0607 0707 0607 0708 090b 0908  ................
00000040: 080a 0807 070a 0d0a 0a0b 0c0c 0c0c 0709  ................
00000050: 0e0f 0d0c 0e0b 0c0c 0cff db00 4301 0202  ............C...
00000060: 0203 0303 0603 0306 0c08 0708 0c0c 0c0c  ................
00000070: 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c  ................
00000080: 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c  ................
00000090: 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c 0c0c ----  ................
标记码 字节 Payload 标记码名 描述
DQT Define Quantization Table 定义量化表;标记代码为固定值0XFFDB;包含9个具体字段

数据长度:2个字节,表示数据长度和多个量化表字段的总长度;即,不包含标记代码,但包含本字段;量化表:数据长度-2个字节,其中包括以下内容: 精度及量化表ID,1个字节,高4位表示精度,只有两个可选值,0:8位;1:16位;低4位表示量化表ID,取值范围表项,64(精度取值+1)个字节,例如,8位精度的量化表,其表项长度为64(0+1)=64字节;

本标记段中,量化表可以重复出现,表示多个量化表,但最多只能出现4次; 本段中,包含两个ffdb。

  • SOFO
00000090: ---- ---- ---- ---- ---- ---- ---- ffc0  ................
000000a0: 0011 0800 0200 0403 0122 0002 1101 0311  ........."......
000000b0: 01ff c400 1f00 0001 0501 0101 0101 0100  ................
标记码 字节 Payload 标记码名 描述
SOFO ffc0 variable size Start Of Frame 帧图像开始,标记代码为固定值0XFFC0;包含9个具体字段:
Byte 描述
0011 数据长度:2个字节,字段的总长度;即,不包含标记代码,但包含本字段
08 精度:1个字节,代表每个数据样本的位数;通常是8位
00 02 图像高度:2个字节,表示以像素为单位的图像高度,如果不支持DNL就必须大于0
00 04 图像宽度:2个字节,表示以像素为单位的图像宽度,如果不支持DNL就必须大于0
03 颜色分量个数:1个字节,由于JPEG采用YCrCb颜色空间,这里恒定为3
0122 0002 1101 0311 01 颜色分量信息:颜色分量个数*3个字节,这里通常为9个字节;并依此表示如下一些信息:
(a)颜色分量ID: 1个字节;
(b)水平/垂直采样因子:1个字节,高4位代表水平采样因子,低4位代表垂直采样因子;
(c)量化表:1个字节,当前分量使用的量化表ID

本标记段中,颜色分量信息应该重复出现3次,因为这里有3个颜色分量;

  • DHT
000000d0: ---- ffc4 00b5 1000 0201 0303 0204 0305  ................
000000e0: 0504 0400 0001 7d01 0203 0004 1105 1221  ......}........!
000000f0: 3141 0613 5161 0722 7114 3281 91a1 0823  1A..Qa."q.2....#
00000100: 42b1 c115 52d1 f024 3362 7282 090a 1617  B...R..$3br.....
00000110: 1819 1a25 2627 2829 2a34 3536 3738 393a  ...%&'()*456789:
00000120: 4344 4546 4748 494a 5354 5556 5758 595a  CDEFGHIJSTUVWXYZ
00000130: 6364 6566 6768 696a 7374 7576 7778 797a  cdefghijstuvwxyz
00000140: 8384 8586 8788 898a 9293 9495 9697 9899  ................
00000150: 9aa2 a3a4 a5a6 a7a8 a9aa b2b3 b4b5 b6b7  ................
00000160: b8b9 bac2 c3c4 c5c6 c7c8 c9ca d2d3 d4d5  ................
00000170: d6d7 d8d9 dae1 e2e3 e4e5 e6e7 e8e9 eaf1  ................
00000180: f2f3 f4f5 f6f7 f8f9 faff c400 1f01 0003  ................
00000190: 0101 0101 0101 0101 0100 0000 0000 0001  ................
000001a0: 0203 0405 0607 0809 0a0b ffc4 00b5 1100  ................
000001b0: 0201 0204 0403 0407 0504 0400 0102 7700  ..............w.
000001c0: 0102 0311 0405 2131 0612 4151 0761 7113  ......!1..AQ.aq.
000001d0: 2232 8108 1442 91a1 b1c1 0923 3352 f015  "2...B.....#3R..
000001e0: 6272 d10a 1624 34e1 25f1 1718 191a 2627  br...$4.%.....&'
000001f0: 2829 2a35 3637 3839 3a43 4445 4647 4849  ()*56789:CDEFGHI
00000200: 4a53 5455 5657 5859 5a63 6465 6667 6869  JSTUVWXYZcdefghi
00000210: 6a73 7475 7677 7879 7a82 8384 8586 8788  jstuvwxyz.......
00000220: 898a 9293 9495 9697 9899 9aa2 a3a4 a5a6  ................
00000230: a7a8 a9aa b2b3 b4b5 b6b7 b8b9 bac2 c3c4  ................
00000240: c5c6 c7c8 c9ca d2d3 d4d5 d6d7 d8d9 dae2  ................
00000250: e3e4 e5e6 e7e8 e9ea f2f3 f4f5 f6f7 f8f9  ................
00000260: fa-- ---- ---- ---- ---- ---- ---- ----  .............?..
标记码 字节 Payload 标记码名 描述
DHT ffc4 variable size Define Huffman Table 定义Huffman表,标记码为0XFFC4;包含2个字段:
(1)数据长度,2个字节,不包含标记代码,但包含本字段;
(2)Huffman表,数据长度-2个字节

Huffman表包括:
包括表ID和表类型,1个字节,高4位表示表的类型,取值只有两个:0,DC直流;1,AC交流;低4位,Huffman表ID;需要提醒的是,DC表和AC表分开进行编码。 包括不同位数的码字数量,16个字节。包括编码内容,16个不同位数的码字数量之和(字节)

Huffman表可以重复出现,一般需要重复4次。

  • DRI
标记码 字节 Payload 标记码名 描述
DRI 0xFF, 0xDD 4 bytes Define Restart Interval 定义差分编码累计复位的间隔,标记码为固定值0XFFDD

包含2个具体字段:
数据长度:2个字节,取值为固定值0X0004,字段的总长度;即,不包含标记代码,但包含本字段;

MCU块的单元中重新开始间隔:2个字节,如果取值为n,就代表每n个MCU块就有一个RSTn标记;第一个标记是RST0,第二个是RST1,RST7之后再从RST0开始重复;如果没有本标记段,或者间隔值为0,就表示不存在重开始间隔和标记RST;

  • SOS
00000260: --ff da00 0c03 0100 0211 0311 003f 00f9  .............?..
00000270: 1ffe 0afd e28d 4ff6 66ff 0082 887c 4af0  ......O.f....|J.
00000280: 9fc3 7d46 fbe1 f785 6c75 3716 fa37 86a7  ..}F....lu7..7..
00000290: 7d27 4fb7 da4c 6bb2 0b72 91ae 1234 4185  }'O..Lk..r...4A.
000002a0: e151 4740 0514 515f a064 1ff2 2ac2 7fd7  .QG@..Q_.d..*...
000002b0: aa5f fa6e 27f1 ff00 8a5f f254 e2fd 63ff  ._.n'...._.T..c.
000002c0: 00a4 44-- --                             ..D..
标记码 字节 Payload 标记码名 描述
SOS FFDA variable Start Of Scan 扫描开始;标记码为0XFFDA,包含2个具体字段:
Byte 描述
00 0c 数据长度, 2个字节,表示该字段的总长度
03 颜色分量数目:1个字节,只有3个可选值,1:灰度图;3:YCrCb或YIQ;4:CMYK
0100 0211 0311 颜色分量信息:包括以下字段,颜色分量ID:1个字节; 直流/交流系数表ID,1个字节,高4位表示直流分量的Huffman表的ID;低4位表示交流分量的Huffman表的ID
003f 00 压缩图像数据:谱选择开始:1个字节,固定值0X00; 谱选择结束:1个字节,固定值0X3F;谱选择:1个字节,固定值0X00
  • 数据块
00000260: ---- ---- ---- ---- ---- ---- ---- --f9  .............?..
00000270: 1ffe 0afd e28d 4ff6 66ff 0082 887c 4af0  ......O.f....|J.
00000280: 9fc3 7d46 fbe1 f785 6c75 3716 fa37 86a7  ..}F....lu7..7..
00000290: 7d27 4fb7 da4c 6bb2 0b72 91ae 1234 4185  }'O..Lk..r...4A.
000002a0: e151 4740 0514 515f a064 1ff2 2ac2 7fd7  .QG@..Q_.d..*...
000002b0: aa5f fa6e 27f1 ff00 8a5f f254 e2fd 63ff  ._.n'...._.T..c.
000002c0: 00a4 44-- --                             ..D..
  • EOI
000002c0: ---- --ff d9                             ..D..
标记码 字节 Payload 标记码名 描述
EOI ffd9 none End Of Image 、 图像结束;标记代码为0XFFD9

libjpeg编解码Jpeg图片

Jpeg的编解码,可采用libjpeg-turbo,它是采用SIMD进行加速,能够提升编解码的性能。Android采用的是1.5.1的版本。在github上可以下到最新版本的代码:

https://github.com/libjpeg-turbo/libjpeg-turbo.git

我们的样例代码也可以在github上进行下载 :Codec-JPGCodec

在 编译 libjpeg时,需要根据平台进行SIMD的实现编译:

if(${ANDROID_ABI} STREQUAL "arm")
    MESSAGE(STATUS "JPEG: arm")
    set(JPEG_SOURCES
            jpeg/libjpeg-turbo/simd/jsimd_arm_neon.S
            jpeg/libjpeg-turbo/simd/jsimd_arm.c )
elseif(${ANDROID_ABI} STREQUAL "arm64")
    MESSAGE(STATUS "JPEG: arm64")
    set(JPEG_SOURCES
            jpeg/libjpeg-turbo/simd/jsimd_arm64_neon.S
            jpeg/libjpeg-turbo/simd/jsimd_arm64.c )
... ...
else()
    MESSAGE(STATUS "JPEG: else")
    set(JPEG_SOURCES
            jpeg/libjpeg-turbo/jsimd_none.c )
endif()

add_library( jpeg
             STATIC
             jpeg/libjpeg-turbo/jcapimin.c
             jpeg/libjpeg-turbo/jcapistd.c
             jpeg/libjpeg-turbo/jaricom.c
             jpeg/libjpeg-turbo/jcarith.c
             jpeg/libjpeg-turbo/jccoefct.c
             jpeg/libjpeg-turbo/jccolor.c
             jpeg/libjpeg-turbo/jcdctmgr.c
             jpeg/libjpeg-turbo/jchuff.c
             jpeg/libjpeg-turbo/jcinit.c
             jpeg/libjpeg-turbo/jcmainct.c
             jpeg/libjpeg-turbo/jcmarker.c
             jpeg/libjpeg-turbo/jcmaster.c
             jpeg/libjpeg-turbo/jcomapi.c
             jpeg/libjpeg-turbo/jcparam.c
             jpeg/libjpeg-turbo/jcphuff.c
             jpeg/libjpeg-turbo/jcprepct.c
             jpeg/libjpeg-turbo/jcsample.c
             jpeg/libjpeg-turbo/jctrans.c
             jpeg/libjpeg-turbo/jdapimin.c
             jpeg/libjpeg-turbo/jdapistd.c
             jpeg/libjpeg-turbo/jdarith.c
             jpeg/libjpeg-turbo/jdatadst.c
             jpeg/libjpeg-turbo/jdatasrc.c
             jpeg/libjpeg-turbo/jdcoefct.c
             jpeg/libjpeg-turbo/jdcolor.c
             jpeg/libjpeg-turbo/jddctmgr.c
             jpeg/libjpeg-turbo/jdhuff.c
             jpeg/libjpeg-turbo/jdinput.c
             jpeg/libjpeg-turbo/jdmainct.c
             jpeg/libjpeg-turbo/jdmarker.c
             jpeg/libjpeg-turbo/jdmaster.c
             jpeg/libjpeg-turbo/jdmerge.c
             jpeg/libjpeg-turbo/jdphuff.c
             jpeg/libjpeg-turbo/jdpostct.c
             jpeg/libjpeg-turbo/jdsample.c
             jpeg/libjpeg-turbo/jdtrans.c
             jpeg/libjpeg-turbo/jerror.c
             jpeg/libjpeg-turbo/jfdctflt.c
             jpeg/libjpeg-turbo/jfdctfst.c
             jpeg/libjpeg-turbo/jfdctint.c
             jpeg/libjpeg-turbo/jidctflt.c
             jpeg/libjpeg-turbo/jidctfst.c
             jpeg/libjpeg-turbo/jidctint.c
             jpeg/libjpeg-turbo/jidctred.c
             jpeg/libjpeg-turbo/jmemmgr.c
             jpeg/libjpeg-turbo/jmemnobs.c
             jpeg/libjpeg-turbo/jquant1.c
             jpeg/libjpeg-turbo/jquant2.c
             jpeg/libjpeg-turbo/jutils.c
             ${JPEG_SOURCES} )

target_include_directories(jpeg PRIVATE
            jpeg/libjpeg-turbo
            jpeg/libjpeg-turbo/simd )

我们这里也是采用静态库的形式。

  • 解码
#include <jpeglib.h>

#include "JpegTest.h"

void jpgfile_to_jpgmem(char *jpg_file,byte **jpg,int *size)
{
    FILE *fp = fopen(jpg_file,"rb");
    if(fp == NULL) return;

    fseek(fp,0,SEEK_END);
    int length = ftell(fp);
    fseek(fp,0,SEEK_SET);

    *jpg = new byte[length];
    fread(*jpg,length,1,fp);
    *size = length;

    fclose(fp);
}

void jpgmem_to_bgr(byte *jpg,int size,byte **bgr,int *b_size,int *w,int *h)
{
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    cinfo.err = jpeg_std_error(&jerr);

    jpeg_create_decompress(&cinfo);
    jpeg_mem_src(&cinfo,jpg,size);

    jpeg_read_header(&cinfo,TRUE);
    jpeg_start_decompress(&cinfo);

    unsigned long width = cinfo.output_width;
    unsigned long height = cinfo.output_height;
    unsigned short depth = cinfo.output_components;

    *w = width;
    *h = height;
    *b_size = width*height*depth;
    *bgr = (byte*)malloc(width*height*depth);
    memset(*bgr,0,width*height*depth);

    JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, width*depth,1);

    byte *point = (*bgr)+(height-cinfo.output_scanline-1)*(width*depth);
    while(cinfo.output_scanline<height)
    {
        jpeg_read_scanlines(&cinfo, buffer, 1);
        memcpy(point, *buffer, width*depth);
        point -= width*depth;
    }

    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
}

void readJpegFile(char* fileName) {
    byte* jpg = static_cast<byte*> (malloc(1024));
    int size = 0;

    jpgfile_to_jpgmem(fileName, &jpg, &size);

    my_show_byte("Jpeg raw data", jpg, size);

    byte* rgb = static_cast<byte*> (malloc(1024));
    int rgb_size = 0;
    int w = 0;
    int h = 0;
    jpgmem_to_bgr(jpg, size, &rgb, &rgb_size, &w, &h);

    ALOGE("jpgmem_to_bgr rgb_size %d w %d h %d", rgb_size, w, h);

    my_show_byte("Jpeg rgb data", rgb, rgb_size);

    free(rgb);
    free(jpg);
}

通过jpgfile_to_jpgmem函数,将jpg文件,转换为对应的字节码。

~ E/PngCodec: png_show_byte Jpeg raw data 1418 bytes 
FFD8FFE000104A464946000101010060
00600000FFDB00430002010102010102
02020202020202030503030303030604
040305070607070706070708090B0908
080A0807070A0D0A0A0B0C0C0C0C0709
... ...

logcat只能打1024个字节~

然后再通过jpgmem_to_bgr函数,将Jpeg的原始数据,解码为RGR的数据:

~E/PngCodec: png_show_byte Jpeg rgb data 48 bytes
 FFFFC69F9E5E4549891216560807005352121317578C90D0

我们可以将这个BGR的数据,写到Bitmap文件中,再图片浏览器打开看看,和前面的 Jpeg的图片是一样的。

实例中的代码可以到github上下载!

Codec-JPGCodec

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容