23. 图片显示

本章代码所在的位置:lubancat_rk_code_storage/base_linux/screen/image/

本章节将会介绍三种常用的图片文件的图片显示的方法

  1. bmp文件

  2. jpeg文件

  3. png文件

注意

本章节主要以引导读者对这三种文件的基础图像显示, 更多详细的操作请去相关网站去下载并阅读相关源码进行更高层次的操作

23.1. BMP

23.1.1. 获取BMP文件

我们可以先找到你想要生成的图片文件,以下面为例

图片 evlove.png

我们可以打开格式工厂,然后选择->bmp,将png文件转为bmp文件

未找到图片

23.1.2. 文件格式解析

解析文件需要一个能查看文件二进制格式的工具, 这里我们使用 UltraEdit 来讲解, 我们可以在网站下载学习版(绿色版)使用, 这里就不介绍下载和安装的过程了。

BMP是一种常见的图像格式,BMP文件可看成由4个部分组成:

  1. 位图文件头(bitmap-file header)

  2. 位图信息头(bitmap-information)

  3. 调色板(color palette)

  4. 位图信息

23.1.2.1. 位图文件头

共14个字节

位图文件头

名称

字节数

偏移量

作用

bfType

2字节

0

0x42,0x4D(‘B’,’M’),用于区分是否为BMP文件

bfSize

4字节

2

BMP文件的大小

bfReserved1

2字节

6

保留,必须设为0

bfReserved2

2字节

8

保留,必须设为0

bfOffBits

4字节

10

说明从文件头开始到实际的图象数据之间的字节的偏移量。

bfOffBits这个参数是非常有用的,因为位图信息头和调色板的长度会根据不同情况而变化,所以我们可以用这个偏移值迅速的从文件中读取到位图数据

bfOffBits = 位图信息头 + 调色板

未找到图片

上图为UltraEdit解析的二进制文件,下图为windows上查看的文件

未找到图片
  • 可以看到BMP文件的前两个字节分别是0x42,0x4D

  • 文件大小:0x467A0700,换成十进制为4,618,759,显然文件大小不对应。 是因为BMP文件采用的是小端模式,正确的文件大小为0x00077A46, 换成十进制为 490,054 与在windows上看到的一模一样

  • 有四位保留的字节

  • bfOffBits = 0x36,十进制为54,即从文件头到图片信息的偏移量为54

  1. 小端模式 :如果一个数据需要用几个字节来表示的话,那么,低位数据存在低位地址上,高位数据存在高位地址上。

  2. 大端模式 :如果一个数据需要用几个字节来表示的话,那么,低位数据存在高位地址上,高位数据存在低位地址上。

23.1.2.2. 位图信息头

位图信息头

名称

字节数

偏移量

作用

biSize

4字节

14

整个位图信息头结构体的大小

biWidth

4字节

18

图像宽度,单位为像素

biHeight

4字节

22

图像高度,单位为像素

biPlanes

2字节

26

颜色平面书,其值总为1

biBitCount

2字节

28

1个像素用多少位的数据来表示,其值可能为1,4,8,16,24,32。

biCompression

4字节

30

数据的压缩类型

biSizeImage

4字节

34

图像数据的大小,单位为字节

biXPelsPerMeter

4字节

38

水平分辨率,单位是像素/米

biYPelsPerMeter

4字节

42

垂直分辨率,单位是像素/米

biClrUsed

4字节

46

调色板中的颜色索引数

biClrImportant

4字节

50

说明有对图像有重要影响的颜色索引的数

未找到图片
  • biSize = 40(0x28),即信息头的大小为40个字节

  • biWidth = 350(0x015E),图像宽度为720个像素

  • biHeight = 350(0x015E),图像高度为720个像素,

注意

biHeight的正负可以判断图像是正向还是倒向的,若为正,则表示是正向;若为负,则表示反向。

  • biPlanes = 1 颜色平面书,其值总为1

  • biBitCount = 32(0x20) 一个像素占据32位

  • biCompression = 0 不压缩

  • biSizeImage = 0 ,有些bmp文件会没有这个参数,我们可以通过利用bfSize - bfOffBits可得

  • biXPelsPerMeter = 3780(0x0EC4) 水平分辨率3780像素/米

  • biYPelsPerMeter = 3780(0x0EC4) 垂直分辨率3780像素/米

  • biClrUsed = 0 颜色索引数为0(用于调色板)

  • biClrImportant = 0 重要的颜色索引数为0(用于调色板)

23.1.2.3. 调色板

由于我们使用的是24位真彩色,没有调色板, 可以看到上面的biClrUsed和biClrImportant为0, 这两个是描述调色板的重要参数。

下面的数据就是调色板了。 前面也已经提过,调色板其实是一张映射表,标识颜色索引号与其代表的颜色的对应关系。 它在文件中的布局就像一个二维数组palette[N][4], 其中N表示总的颜色索引数,每行的四个元素分别表示该索引对应的B、G、R和A1pha的值, 每个分量占一个字节。如不设透明通道时,Alpha为0。 索引号就是所在行的行号,对应的颜色就是所在行的四个元素。

  • 索引:(蓝,绿,红,A1pha)

  • 0号:(fe,fa,fd,00)

  • 1号:(fd,f3,fc,00)

23.1.2.4. 位图信息

如果biHeight为正即以下

未找到图片
  • 位图信息的存放的信息是从左下角到右上角的,因此在显示画面时,需要调整数据

  • 由于BMP文件使用的是小端模式,24位色和32位色的数据变为BGR和BGRA

  • 对于256色及以下会使用到调色板,根据调色板的索引号即可获取颜色数据

23.1.3. 代码位置

1
base_linux/screen/image/bmp

23.1.4. 代码结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#代码结构

bmp/
|-- Makefile            #makefile
|-- build
|   |-- drm-core.o
|   |-- main.o
|   `-- test            #执行文件
|-- file                #图片文件
|   |-- bmp
|   |   |-- MERCURY.bmp
|   |   |-- Night_Visions.bmp
|   |   |-- SILENCE.bmp
|   |   |-- SMOKE+MIRRORS.bmp
|   |   `-- evlove.bmp
|   |-- jpeg
|   |   |-- MERCURY.jpg
|   |   |-- Night_Visions.jpg
|   |   |-- SILENCE.jpg
|   |   |-- SMOKE+MIRRORS.jpg
|   |   `-- evlove.jpg
|   `-- png
|       |-- MERCURY.png
|       |-- Night_Visions.png
|       |-- SILENCE.png
|       |-- SMOKE+MIRRORS.png
|       `-- evlove.png
|-- includes
|   `-- drm-core.h
|-- sources
|   |-- drm-core.c
|   `-- main.c
`-- test -> build/test #可执行文件

23.1.5. 编译&运行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#编译
make
#运行
./test file/bmp/xxx.bmp

#运行效果
#终端上打印出BMP文件的信息
#屏幕上显示图片
#按键
#退出程序

23.1.6. 代码分析

main()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int main(int argc, char **argv)
{
	int i,j;
	int fd_bmp;
	int ret;
	struct bmpfile cbf;
	//获取BMP文件
	if(argc <2 ){
		printf("Wrong use !!!!\n");
		printf("Usage: %s xxx.bmp\n",argv[0]);
		goto fail1;
	}
	//初始化drm
	ret = drm_init();
	if(ret < 0){
		printf("drm init fail\n");
		return -1;
	}
	//判断是否为bmp文件
	ret = judge_bmp(argv[1],&cbf);
	if(ret < 0){
		printf("something wrong in bmp file %d\n",fd_bmp);
		goto fail1;
	}
	//获取BMP文件的各种信息
	ret = get_bmp_file(argv[1],&cbf);
	if(ret < 0){
		printf("something wrong in bmp file %d\n",fd_bmp);
		goto fail1;
	}
	//打印信息
	show_bmp_info(&cbf);
	//显示画面信息在正中央
	ret = show_bmp(&cbf , buf.width/2 - cbf.biWidth/2, buf.height/2 - cbf.biHeight/2);
	if(ret < 0){
		printf("show_bmp wrong!\n");
		goto fail2;
	}
	//获取按键,按键后退出程序
	getchar();
	//注销drm
	drm_exit();
	//释放内存空间
	free_bmp_file(&cbf);	
	return 0;

fail2: 
	drm_exit();
	free_bmp_file(&cbf);
fail1:	
	printf("Proglem run fail,please check !\n");
	return -1;
}
  • 第7-12行,获取要打开的bmp文件,如果没有bmp文件就会退出程序

  • 第13-18行,初始化DRM,用于显示作用,将drm_init替换成framebuffer初始化亦可

  • 第19-24行,判断是否为bmp文件,如果是,获取文件大小和偏移量

  • 第25-30行,获取bmp文件的位图信息头以及位图信息,并将BMP的位图格式转换为屏幕坐标系的RGB信息

  • 第31-32行,打印所有关于bmp文件的信息,如下图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
struct bmpfile {
	FILE* fp;
	int fd;
	int file_size;				//文件中图像的大小-4Bytes
	int bmp_offset;				//图像偏移量-4Bytes
	int biSize;					//位图信息头大小-4Bytes
	int biWidth;				//图像宽度-4Bytes
	int biHeight;				//图像高度-4Bytes
	short biPlanes;				//颜色平面书-2Bytes
	short biBitCount;			//每像素占用位数-2Bytes
	int biCompression;			//数据压缩类型-4Bytes
	int biSizeImage;			//图像数据大小-4Bytes
	int biXPelsPerMeter;		//像素/米-4Bytes
	int biYPelsPerMeter;		//像素/米-4Bytes
	int biClrUsed;				//调色板索引数-4Bytes
	int biClrImportant;			//索引数目-4Bytes
	unsigned char *mem_buf;		//内存映射,整个文件
	unsigned char *bmp_buf;		//将bmp文件的格式改成图像rgb格式
	unsigned char bpp;			//每个像素占用的字节数
};
  • 第33-38行,将画面信息显示在屏幕的正中央的位置

  • 第39-40行,获取按键信息,没获取前程序进入阻塞状态

  • 第41-42行,注销drm

  • 第43-44行,释放内存空间

  • 第46-53行,错误信息的执行方向流控

judge_bmp()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
int judge_bmp(char *filename,struct bmpfile *pfd)
{
	int offset = 0;
	int count;
	char file_head[14];
	//打开文件
	pfd->fp = fopen(filename,"rb");
	if(pfd->fp == NULL){
		printf("open file fail\n");
		return -1;
	}
	//获取前14个字节的内容
	count = fread(file_head,1,14,pfd->fp);
	if(count != 14){
		printf("read file fail\n");
		return -1;
	}
	//判断是否为BMP文件
	if(file_head[0] == 0x42 &&  file_head[1] == 0x4d){
		//解析参数
		offset = 2;
		memcpy(&pfd->file_size,file_head+offset,4);
		offset += 8;		//中间有四个字节的空白区域
		memcpy(&pfd->bmp_offset,file_head+offset,4);
		fclose(pfd->fp);
		return 1;
	}
	else{
		fclose(pfd->fp);
		return -1;
	}
		
}
  • 第6-11行,根据提供的文件打开

  • 第12-17行,读取文件头14个字节的内容

  • 第19行,判断是否为bmp文件

  • 第21-24行,获取文件的大小以及获取从文件头开始到实际的图象数据之间的字节的偏移量

  • 第28-31行,若不为bmp文件就退出

get_bmp_file()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
int get_bmp_file(char *filename,struct bmpfile *bf)
{
	
	int i;
	int word = 0;
	char bmp_head[14];
	int size;
	int offset = 14;

	bf->fd = open(filename,O_RDWR);
	if(bf->fd < 0){
		printf("can not openfile\n");
		return -1;
	}

	bf->mem_buf = (unsigned char *)mmap(NULL , bf->file_size, 
						PROT_READ | PROT_WRITE, MAP_SHARED, 
						bf->fd, 0);
	if(bf->mem_buf == NULL){
		printf("mmap wrong !\n");
		close(bf->fd);
		return -1;
	}

	memcpy(&bf->biSize,bf->mem_buf+offset,4);
	offset += 4;
	memcpy(&bf->biWidth,bf->mem_buf+offset,4);
	offset += 4;
	memcpy(&bf->biHeight,bf->mem_buf+offset,4);
	offset += 4;
	memcpy(&bf->biPlanes,bf->mem_buf+offset,2);
	offset += 2;
	memcpy(&bf->biBitCount,bf->mem_buf+offset,2);
	offset += 2;
	memcpy(&bf->biCompression,bf->mem_buf+offset,4);
	offset += 4;
	memcpy(&bf->biSizeImage,bf->mem_buf+offset,4);
	offset += 4;
	memcpy(&bf->biXPelsPerMeter,bf->mem_buf+offset,4);
	offset += 4;
	memcpy(&bf->biYPelsPerMeter,bf->mem_buf+offset,4);
	offset += 4;
	memcpy(&bf->biClrUsed,bf->mem_buf+offset,4);
	offset += 4;
	memcpy(&bf->biClrImportant,bf->mem_buf+offset,4);

	if(bf->biSizeImage == 0)
		bf->biSizeImage = bf->file_size - bf->bmp_offset;
	
	if(bf->biBitCount == 24 || bf->biBitCount == 32 )
		bf->bpp = bf->biBitCount/8;

	//分配空间,操作指针需要分配空间
	bf->bmp_buf = malloc(bf->biSizeImage);

	//将bmp文件的格式改为RGB适用的格式
	for(i = 0;i<bf->biHeight;i++)
		memcpy( bf->bmp_buf + i*bf->biWidth*bf->bpp, 
				bf->mem_buf +(bf->biHeight-1-i)*bf->biWidth*bf->bpp+ bf->bmp_offset,
				bf->biWidth * bf->bpp);

	close(bf->fd);
	return 0;
}
  • 第10-14行,以open的形式打开文件,方便mmap

  • 第16-23行,将整个bmp文件mmap到内存中

  • 第25-45行,获取并解析位图信息头

  • 第47-48行,如果碰到部分bmp文件不具有biSizeImage的信息,通过整个文件大小-位图信息偏移量

  • 第50-51行,获取每个像素占据的字节数,24位-3字节,32位-4字节

  • 第53-54行,分配内存空间给屏幕坐标系的buf中使用,大小为显示图像的大小

  • 第56-60行,将BMP文件的位图数据转化为屏幕坐标系的显示数据,效果:将bmp第一行数据变为最后一行,第二行变为倒数第二行

  • 第62行,关闭图像文件

show_bmp()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int show_bmp(struct bmpfile *bf,int x ,int y)
{
	int i,j;
	uint32_t word;
	if(x<0 || y<0){
		printf("wrong set !\n");
		return -1;
	}
		
	if(bf->biBitCount == 24 || bf->biBitCount == 32){
		for(j=0; j<bf->biHeight; j++){
			for(i = 0 ; i<bf->biWidth;i++){
				if((j+y) < buf.height && (i+x)<buf.width){
					word = 0;
					word = ((word | bf->bmp_buf[(j*bf->biWidth+i)*bf->bpp+2])<<16) | 
						   ((word | bf->bmp_buf[(j*bf->biWidth+i)*bf->bpp+1])<<8) | 
						   ((word | bf->bmp_buf[(j*bf->biWidth+i)*bf->bpp]));
					buf.vaddr[(j+y)*buf.width+(x+i)] = word;
				}
				else
					continue;
			}
		}
	}
	else
		printf("unsupport bpps!Please use 24bpps or 32bpps bmp file\n");
	return 0;	
}
  • 该函数具有自动分辨24位和32位的bmp图像,并显示出来

  • 第13行,如果图像没超出屏幕的右边界以及下边界就打印

  • 第14行-18行,将BGRA或BGR的位图信息转换位图像所能够识别的ARGB和RGB

  • 第20-21行,超出屏幕的右边界以及下边界就不打印

show_bmp_info()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void show_bmp_info(struct bmpfile *bf)
{
	//打印文件的内容
	printf("file_size = %d\n",bf->file_size);
	printf("bmp_offset = %d\n",bf->bmp_offset);
	printf("biSize = %d\n",bf->biSize);
	printf("biWidth = %d\n",bf->biWidth);
	printf("biHeight = %d\n",bf->biHeight);
	printf("biPlanes = %d\n",bf->biPlanes);
	printf("biBitCount = %d\n",bf->biBitCount);
	printf("biCompression = %d\n",bf->biCompression);
	printf("biSizeImage = %d\n",bf->biSizeImage);
	printf("biXPelsPerMeter = %d\n",bf->biXPelsPerMeter);
	printf("biYPelsPerMeter = %d\n",bf->biYPelsPerMeter);
	printf("biClrUsed = %d\n",bf->biClrUsed);
	printf("biClrImportant = %d\n",bf->biClrImportant);
}
  • 打印位图信息头的详细信息

free_bmp_file()
1
2
3
4
5
int free_bmp_file(struct bmpfile *bf)
{
	munmap(bf->mem_buf, bf->file_size);
	free(bf->bmp_buf);
}
  • 释放内存空间

23.2. JPEG

23.2.1. 前言

JPEG是当前我们生活中最常用的图像格式, 这是一种使用有损压缩方法保存的图像格式。作为压缩的结果,输出图像无法兼顾质量和大小。 用户可以调整压缩级别以达到所需的质量级别,同时减小存储大小。 如果对图像应用 10:1 压缩,则对图像质量的影响可以忽略不计。压缩值越高,图像质量的劣化程度越高。

视频讲解JPEG压缩图片的同时图像质量还十分不错的原因 https://www.bilibili.com/video/BV1iv4y1N7sq/?spm_id_from=333.337.search-card.all.click&vd_source=4bad1f761b8ca5604d76a5b072cf66ff

23.2.2. 代码位置

1
 base_linux/screen/image/jpeg

23.2.3. 代码结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 #代码结构
 .
 |-- Makefile
 |-- build
 |   |-- drm-core.o
 |   |-- main.o
 |   `-- test
 |-- file
 |   |-- bmp
 |   |   |-- MERCURY.bmp
 |   |   |-- Night_Visions.bmp
 |   |   |-- SILENCE.bmp
 |   |   |-- SMOKE+MIRRORS.bmp
 |   |   `-- evlove.bmp
 |   |-- jpeg
 |   |   |-- MERCURY.jpg
 |   |   |-- Night_Visions.jpg
 |   |   |-- SILENCE.jpg
 |   |   |-- SMOKE+MIRRORS.jpg
 |   |   `-- evlove.jpg
 |   `-- png
 |       |-- MERCURY.png
 |       |-- Night_Visions.png
 |       |-- SILENCE.png
 |       |-- SMOKE+MIRRORS.png
 |       `-- evlove.png
 |-- includes
 |   `-- drm-core.h
 |-- sources
 |   |-- drm-core.c
 |   `-- main.c
 `-- test -> build/test

23.2.4. 编译&运行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#安装jpeg解码库
sudo apt install libjpeg-dev

#编译
make

#运行
./test xxx.jpeg

#eg:
./test file/jpeg/evlove.jpg

#运行结果
屏幕中央显示图片

23.2.5. 代码分析

main()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
int main(int argc, char **argv)
{
	int i,j;
	int x =0, y=0 ;
	uint32_t word;
	int ret;
	struct jpeg_file jf;

	if(argc <2 ){
		printf("Wrong use !!!!\n");
		printf("Usage: %s [xxx.jpg / xxx.jpeg]\n",argv[0]);
		goto fail1;
	}

	ret = drm_init();	
	if(ret < 0 ){
		printf("drm_init fail\n");
		return -1;
	}

	jf.fp= fopen(argv[1], "rb");
	if (jf.fp== NULL) {
		printf("can not open file");
		return -1;
	}

	memcpy(jf.filename,argv[1],sizeof(argv[1]));

	ret =judge_jpeg(argv[1],&jf);
	if(ret < 0 ){
		goto fail2;
	}

	ret = decode_jpeg(argv[1],&jf);
	if(ret < 0 ){
		printf("decode_jpeg wrong\n");
		goto fail2;
	}

	show_jpeg(&jf , buf.width/2 - jf.width/2, buf.height/2 - jf.height/2);

	getchar();
	
    fclose(jf.fp);
	free_jpeg(&jf);
	drm_exit();	
	return 0;

fail2:
	fclose(jf.fp);
	free_jpeg(&jf);
	drm_exit();
fail1:	
	printf("Proglem run fail,please check !\n");
	return -1;
}
  • 第9-13行,从第二个参数获取jpeg文件,如果没有第二个参数则退出程序

  • 第15-19行,初始化显示系统,用来显示

  • 第21-27行,打开文件

  • 第29-32行,判断是否为JPEG文件

  • 第34-38行,解码jpeg文件

  • 第40行,显示图片

judge_jpeg()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
//判断文件是否是jpeg文件
int judge_jpeg(char *filename,struct jpeg_file *pfd)
{
	int ret;
	uint32_t word;

    pfd->cinfo.err = jpeg_std_error(&pfd->jerr);
	//创建一个jpeg_compress_struct结构体
    jpeg_create_decompress(&pfd->cinfo);
	
	//指定jpeg解压的源文件
    jpeg_stdio_src(&pfd->cinfo, pfd->fp);
	
	//解析jpeg文件,解析完成后可获得图像的格式
    ret = jpeg_read_header(&pfd->cinfo, TRUE);
	if(ret < 0){
		printf("file is not jpg ...\n");
        jpeg_destroy_decompress(&pfd->cinfo);
		return -1;
  • 第7-9行,分配和初始化一个jpeg_compress_struct结构体

  • 第11-12行,指定jpeg解压的源文件

  • 第14-19行,解析jpeg的头文件,判断是否为JPEG文件

decode_jpeg()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
int decode_jpeg(char *filename,struct jpeg_file *jf)
{
	int ret;
	uint32_t word;
	unsigned char *buf_cpy;
	//临时变量行buffer
	uint8_t *pucbuffer;
	//放大倍率
	jf->cinfo.scale_num = 2;
	//缩小倍率
	jf->cinfo.scale_denom = 1;

	//对cinfo所指定的源文件进行解压,并将解压后的数据存到cinfo结构体的成员变量中。
    jpeg_start_decompress(&jf->cinfo);
	//获取图片基本信息
    jf->row_size = jf->cinfo.output_width * jf->cinfo.output_components;
    jf->width = jf->cinfo.output_width;
    jf->height = jf->cinfo.output_height;
	jf->Bpp = jf->cinfo.output_components;
    jf->size = jf->row_size * jf->cinfo.output_height;
	//分配内存空间 
	pucbuffer = malloc(jf->row_size);
    jf->buffer = malloc(jf->size);

    printf("size: %d w: %d h: %d row_size: %d ,Bpp: %d\n",
			jf->size,jf->width,jf->height,jf->row_size,jf->Bpp);
	//缓冲指针指向buffer		
	buf_cpy = jf->buffer;

    while (jf->cinfo.output_scanline < jf->cinfo.output_height){
        //可以读取RGB数据到buffer中,参数3能指定读取多少行
		jpeg_read_scanlines(&jf->cinfo, &pucbuffer, 1);
        //复制到内存
		memcpy(buf_cpy , pucbuffer, jf->row_size);
		buf_cpy = buf_cpy + jf->row_size;
    }

	// 完成解码
	jpeg_finish_decompress(&jf->cinfo);
	free(pucbuffer);
	//释放结构体
    jpeg_destroy_decompress(&jf->cinfo);

	return 0;
}
  • 第8-11行,设置放大或者缩小的倍率

  • 第13-14行,开始解压并把解压的内容放在cinfo结构体内

  • 第15-20行,获取图片的基本信息,每行字节数,宽,高,Bpp,size等

  • 第21-23行,分配内存空间,用于获取图片信息

  • 第25-26行,打印相关信息

  • 第30-36行,读取数据 jpeg_read_scanlines() 读取数据到内存中

  • 第39-42行,完成解码,释放内存,摧毁结构体

show_jpeg()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
int show_jpeg(struct jpeg_file *jf,int x ,int y)
{

	int i,j;
	uint32_t word;

	for(j=0; j<jf->height; j++){
		for(i = 0 ; i<jf->width;i++){
			if((j+y) < buf.height && (i+x)<buf.width){
				word = 0;
				word = (word | jf->buffer[(j*jf->width+i)*jf->Bpp+2]) | 
					   (word | jf->buffer[(j*jf->width+i)*jf->Bpp+1])<<8 | 
					   (word | jf->buffer[(j*jf->width+i)*jf->Bpp])<<16;
				buf.vaddr[(j+y)*buf.width+(x+i)] = word;
			}
			else
				continue;
		}
	}
}

  • 24位图,jpeg解码的RGB格式为RGB888

  • 32位图,jpeg解码的RGB格式为ARGB8888

23.2.6. 总结

解码的步骤:

  1. jpeg_std_error() , jpeg_create_decompress() 创建一个jpeg_compress_struct结构体

  2. jpeg_stdio_src() 指定jpeg解压的源文件

  3. jpeg_read_header() 解析jpeg文件

  4. jpeg_start_decompress() 对cinfo所指定的源文件进行解压

  5. jpeg_read_scanlines() 读取图像数据

  6. jpeg_finish_decompress() 完成解码

  7. jpeg_destroy_decompress() 释放结构体

23.3. PNG

23.3.1. 前言

PNG(Portable Network Graphics),便携式网络图形, 是一种采用无损压缩算法的位图格式, 支持索引、灰度、RGB三种颜色方案以及Alpha通道等特性。 其设计目的是试图替代GIF和TIFF文件格式, 同时增加一些GIF文件格式所不具备的特性。 PNG使用从LZ77派生的无损数据压缩算法, 一般应用于JAVA程序、网页或S60程序中, 原因是它压缩比高,生成文件体积小。 PNG文件的扩展名为.png

23.3.2. 代码位置

1
 base_linux/screen/image/png

23.3.3. 代码结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 #代码结构
 .
 |-- Makefile
 |-- build
 |   |-- drm-core.o
 |   |-- main.o
 |   `-- test
 |-- file
 |   |-- bmp
 |   |   |-- MERCURY.bmp
 |   |   |-- Night_Visions.bmp
 |   |   |-- SILENCE.bmp
 |   |   |-- SMOKE+MIRRORS.bmp
 |   |   `-- evlove.bmp
 |   |-- jpeg
 |   |   |-- MERCURY.jpg
 |   |   |-- Night_Visions.jpg
 |   |   |-- SILENCE.jpg
 |   |   |-- SMOKE+MIRRORS.jpg
 |   |   `-- evlove.jpg
 |   `-- png
 |       |-- MERCURY.png
 |       |-- Night_Visions.png
 |       |-- SILENCE.png
 |       |-- SMOKE+MIRRORS.png
 |       `-- evlove.png
 |-- includes
 |   `-- drm-core.h
 |-- sources
 |   |-- drm-core.c
 |   `-- main.c
 `-- test -> build/test

23.3.4. 编译&运行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#编译
make

#运行
./test xxx.png

#eg:
./test file/png/evlove.png

#运行结果
屏幕中央显示图片

23.3.5. 代码分析

main()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
int main(int argc, char **argv)
{
	int i,j;
	int ret;
	uint32_t word;
	struct png_file pfd;

	if(argc <2 ){
		printf("Wrong use !!!!\n");
		printf("Usage: %s [xxx.png]\n",argv[0]);
		goto fail1;
	}

	ret = drm_init();	
	if(ret < 0 ){
		printf("drm_init fail\n");
		goto fail1;
	}

		//打开文件
	pfd.fp= fopen(argv[1], "rb");
	if (pfd.fp== NULL) {
		printf("can not open file");
		return -1;
	}

	memcpy(pfd.filename,argv[1],sizeof(argv[1]));

	ret = judge_png(&pfd);
	if(ret<0)
		goto fail1;

	ret = decode_png(&pfd);
	if(ret<0){
		printf("%d",ret);
		goto fail2;
	}

	//在屏幕中央显示图像
	show_png(&pfd , buf.width/2 - pfd.width/2, buf.height/2 - pfd.height/2);
		
	getchar();
	free_png(&pfd);
	drm_exit();	
	return 0;

fail2:
	drm_exit();
fail1:	
	printf("Proglem run fail,please check !\n");
	return -1;
}
  • 第8-12行,从第二个参数获取png文件,如果没有第二个参数则退出程序

  • 第14-18行,初始化显示系统,用来显示

  • 第20-25行,打开文件

  • 第29-31行,判断是否为PNG文件

  • 第33-37行,解码PNG文件

  • 第40行,显示图片

judge_png()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
int judge_png(struct png_file *pfd)
{
	int ret;
	char file_head[8]; 
	//读取文件的八个字节
	if (fread(file_head, 1, 8, pfd->fp) != 8) 
		return -1;
	//根据8个字节判断是否为png文件
	ret = png_sig_cmp(file_head, 0, 8); 
	if(ret < 0){
		printf("not a png file\n");
		return ret;
	}

	return PNG_FILE;
}
  • 第5-7行,读取文件的前八个字节的内容

  • 第8-13行,根据8个字节来判断是否为png文件

decode_png()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int decode_png(struct png_file *pfd)
{
	int ret;
	int i, j,k;
	uint32_t word;
	unsigned char **pucPngData; 
	unsigned char *buf_cpy;

	//分配和初始化两个libpng相关的结构体
	pfd->png_ptr  = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 
	pfd->info_ptr= png_create_info_struct(pfd->png_ptr);
	
	//设置错误返回点
	setjmp(png_jmpbuf(pfd->png_ptr));
	//fseek(fp, 0, SEEK_SET);
	rewind(pfd->fp); 
	//指定文件
	png_init_io(pfd->png_ptr, pfd->fp);

	//获取PNG图像的信息
	png_read_png(pfd->png_ptr, pfd->info_ptr, PNG_TRANSFORM_EXPAND, 0);

	//channels 4-32bits/3-24bits/...
	pfd->Bpp = png_get_channels(pfd->png_ptr, pfd->info_ptr); 
	pfd->width 	 = png_get_image_width(pfd->png_ptr, pfd->info_ptr);
	pfd->height  = png_get_image_height(pfd->png_ptr, pfd->info_ptr);
	pfd->bpp  = png_get_bit_depth(pfd->png_ptr, pfd->info_ptr) * pfd->channels;
	pfd->rowsize = png_get_rowbytes(pfd->png_ptr, pfd->info_ptr);
	pfd->size= pfd->width * pfd->height*pfd->Bpp; 

	pfd->buffer = (unsigned char*)malloc(pfd->size);

	//按行一次性获得图像
	pucPngData = png_get_rows(pfd->png_ptr, pfd->info_ptr); 
	buf_cpy = pfd->buffer;
	//存放buffer区
	for (j = 0; j < pfd->height; j ++) {
		memcpy(buf_cpy , pucPngData[j] , pfd->rowsize);
		buf_cpy +=pfd->rowsize ;
	}

	png_destroy_read_struct(&pfd->png_ptr, &pfd->info_ptr, 0);
	fclose(pfd->fp);
}
  • 第9-11行,创建两个与png文件相关的结构体,后面的png操作需要用到

  • 第13-14行,设置错误返回点

  • 第15-16行,将文件指针指在头文件上

  • 第17-18行,指定要解码的文件

  • 第20-21行,获取PNG的图像信息

  • 第23-29行,将PNG的图像信息保存到png_file结构体中

  • 第33-34行,将图像信息保存到一个二维数组上

  • 第35-40行,将二维数组的图像数据转换为buffer

  • 第42行,摧毁结构体

show_png()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int show_png(struct png_file *pfd,int x ,int y)
{
	int i,j;
	uint32_t word;

	for(j=0; j<pfd->height; j++){
		for(i = 0 ; i<pfd->width;i++){
			if((j+y) < buf.height && (i+x)<buf.width){
				word = 0;
				word = (word | pfd->buffer[(j*pfd->width+i)*pfd->Bpp+2]) | 
					   (word | pfd->buffer[(j*pfd->width+i)*pfd->Bpp+1])<<8 | 
					   (word | pfd->buffer[(j*pfd->width+i)*pfd->Bpp])<<16;
				buf.vaddr[(j+y)*buf.width+(x+i)] = word;
			}
			else
				continue;
		}
	}
}
  • 24位图,jpeg解码的RGB格式为RGB888

  • 32位图,jpeg解码的RGB格式为ARGB8888

23.3.6. 总结

解码的步骤:

  1. jpeg_std_error() , jpeg_create_decompress() 创建一个jpeg_compress_struct结构体

  2. jpeg_stdio_src() 指定jpeg解压的源文件

  3. jpeg_read_header() 解析jpeg文件

  4. jpeg_start_decompress() 对cinfo所指定的源文件进行解压

  5. jpeg_read_scanlines() 读取图像数据

  6. jpeg_finish_decompress() 完成解码

  7. jpeg_destroy_decompress() 释放结构体