转自http://www.linuxsense.org/archives/281.html
apt-get install libjpeg62
编译的时候应该加上 -ljpeg
#include
<
stdio.h
>
#include
<
stdlib.h
>
#include
<
fcntl.h
>
#include
<
string
.h
>
#include
<
linux
/
fb.h
>
#include
<
sys
/
types.h
>
#include
<
sys
/
stat.h
>
#include
<
sys
/
mman.h
>
#include
<
jpeglib.h
>
#include
<
jerror.h
>
#define
FB_DEV "/dev/fb0"
/*
**************** function declaration *****************
*/
void
usage(
char
*
msg);
unsigned
short
RGB888toRGB565(unsigned
char
red,
unsigned
char
green, unsigned
char
blue);
int
fb_open(
char
*
fb_device);
int
fb_close(
int
fd);
int
fb_stat(
int
fd,
int
*
width,
int
*
height,
int
*
depth);
void
*
fb_mmap(
int
fd, unsigned
int
screensize);
int
fb_munmap(
void
*
start, size_t length);
int
fb_pixel(
void
*
fbmem,
int
width,
int
height,
int
x,
int
y, unsigned
short
color);
/*
*********** function implementation *******************
*/
int
main(
int
argc,
char
*
argv[])
{
/*
* declaration for jpeg decompression
*/
struct
jpeg_decompress_struct cinfo;
struct
jpeg_error_mgr jerr;
FILE
*
infile;
unsigned
char
*
buffer;
/*
* declaration for framebuffer device
*/
int
fbdev;
char
*
fb_device;
unsigned
char
*
fbmem;
unsigned
int
screensize;
unsigned
int
fb_width;
unsigned
int
fb_height;
unsigned
int
fb_depth;
unsigned
int
x;
unsigned
int
y;
/*
* check auguments
*/
if
(argc
!=
2
) {
usage(
"
insuffient auguments
"
);
exit(
-
1
);
}
/*
* open framebuffer device
*/
if
((fb_device
=
getenv(
"
FRAMEBUFFER
"
))
==
NULL)
fb_device
=
FB_DEV;
fbdev
=
fb_open(fb_device);
/*
* get status of framebuffer device
*/
fb_stat(fbdev,
&
fb_width,
&
fb_height,
&
fb_depth);
/*
* map framebuffer device to shared memory
*/
screensize
=
fb_width
*
fb_height
*
fb_depth
/
8
;
fbmem
=
fb_mmap(fbdev, screensize);
/*
* open input jpeg file
*/
if
((infile
=
fopen(argv[
1
],
"
rb
"
))
==
NULL) {
fprintf(stderr,
"
open %s failed\n
"
, argv[
1
]);
exit(
-
1
);
}
/*
* init jpeg decompress object error handler
*/
cinfo.err
=
jpeg_std_error(
&
jerr);
jpeg_create_decompress(
&
cinfo);
/*
* bind jpeg decompress object to infile
*/
jpeg_stdio_src(
&
cinfo, infile);
/*
* read jpeg header
*/
jpeg_read_header(
&
cinfo, TRUE);
/*
* decompress process.
* note: after jpeg_start_decompress() is called
* the dimension infomation will be known,
* so allocate memory buffer for scanline immediately
*/
jpeg_start_decompress(
&
cinfo);
if
((cinfo.output_width
>
fb_width)
||
(cinfo.output_height
>
fb_height)) {
printf(
"
too large JPEG file,cannot display\n
"
);
return
(
-
1
);
}
buffer
=
(unsigned
char
*
) malloc(cinfo.output_width
*
cinfo.output_components);
y
=
0
;
while
(cinfo.output_scanline
<
cinfo.output_height) {
jpeg_read_scanlines(
&
cinfo,
&
buffer,
1
);
if
(fb_depth
==
16
) {
unsigned
short
color;
for
(x
=
0
; x
<
cinfo.output_width; x
++
) {
color
=
RGB888toRGB565(buffer[x
*
3
],
buffer[x
*
3
+
1
], buffer[x
*
3
+
2
]);
fb_pixel(fbmem, fb_width, fb_height, x, y, color);
}
}
else
if
(fb_depth
==
24
) {
memcpy((unsigned
char
*
) fbmem
+
y
*
fb_width
*
3
,
buffer, cinfo.output_width
*
cinfo.output_components);
}
y
++
;
//
next scanline
}
/*
* finish decompress, destroy decompress object
*/
jpeg_finish_decompress(
&
cinfo);
jpeg_destroy_decompress(
&
cinfo);
/*
* release memory buffer
*/
free(buffer);
/*
* close jpeg inputing file
*/
fclose(infile);
/*
* unmap framebuffer's shared memory
*/
fb_munmap(fbmem, screensize);
/*
* close framebuffer device
*/
fb_close(fbdev);
return
(
0
);
}
void
usage(
char
*
msg)
{
fprintf(stderr,
"
%s\n
"
, msg);
printf(
"
Usage: fv some-jpeg-file.jpg\n
"
);
}
/*
* convert 24bit RGB888 to 16bit RGB565 color format
*/
unsigned
short
RGB888toRGB565(unsigned
char
red, unsigned
char
green, unsigned
char
blue)
{
unsigned
short
B
=
(blue
>>
3
)
&
0x001F
;
unsigned
short
G
=
((green
>>
2
)
<<
5
)
&
0x07E0
;
unsigned
short
R
=
((red
>>
3
)
<<
11
)
&
0xF800
;
return
(unsigned
short
) (R
|
G
|
B);
}
/*
* open framebuffer device.
* return positive file descriptor if success,
* else return -1.
*/
int
fb_open(
char
*
fb_device)
{
int
fd;
if
((fd
=
open(fb_device, O_RDWR))
<
0
) {
perror(__func__);
return
(
-
1
);
}
return
(fd);
}
/*
* get framebuffer's width,height,and depth.
* return 0 if success, else return -1.
*/
int
fb_stat(
int
fd,
int
*
width,
int
*
height,
int
*
depth)
{
struct
fb_fix_screeninfo fb_finfo;
struct
fb_var_screeninfo fb_vinfo;
if
(ioctl(fd, FBIOGET_FSCREENINFO,
&
fb_finfo)) {
perror(__func__);
return
(
-
1
);
}
if
(ioctl(fd, FBIOGET_VSCREENINFO,
&
fb_vinfo)) {
perror(__func__);
return
(
-
1
);
}
*
width
=
fb_vinfo.xres;
*
height
=
fb_vinfo.yres;
*
depth
=
fb_vinfo.bits_per_pixel;
return
(
0
);
}
/*
* map shared memory to framebuffer device.
* return maped memory if success,
* else return -1, as mmap dose.
*/
void
*
fb_mmap(
int
fd, unsigned
int
screensize)
{
caddr_t fbmem;
if
((fbmem
=
mmap(
0
, screensize, PROT_READ
|
PROT_WRITE,
MAP_SHARED, fd,
0
))
==
MAP_FAILED) {
perror(__func__);
return
(
void
*
) (
-
1
);
}
return
(fbmem);
}
/*
* unmap map memory for framebuffer device.
*/
int
fb_munmap(
void
*
start, size_t length)
{
return
(munmap(start, length));
}
/*
* close framebuffer device
*/
int
fb_close(
int
fd)
{
return
(close(fd));
}
/*
* display a pixel on the framebuffer device.
* fbmem is the starting memory of framebuffer,
* width and height are dimension of framebuffer,
* x and y are the coordinates to display,
* color is the pixel's color value.
* return 0 if success, otherwise return -1.
*/
int
fb_pixel(
void
*
fbmem,
int
width,
int
height,
int
x,
int
y, unsigned
short
color)
{
if
((x
>
width)
||
(y
>
height))
return
(
-
1
);
unsigned
short
*
dst
=
((unsigned
short
*
) fbmem
+
y
*
width
+
x);
*
dst
=
color;
return
0
;
}
libjpeg介绍:
libjpeg是一个被广泛使用的JPEG压缩/解压缩函数库,它能够读写JFIF格式的JPEG图像文件,通常这类文件是以.jpg或者.jpeg为后缀名的。通过libjpeg库,应用程序可以每次从JPEG压缩图像中读取一个或多个扫描线(scanline,所谓扫描线,是指由一行像素点构成的一条图像线条),而诸如颜色空间转换、降采样/增采样、颜色量化之类的工作则都由libjpeg去完成了。
对于彩色图像,每个像素通常用三个分量表示,即R(Red)、G(Green)、B(Blue)三个分量,每个分量用一个字节表示,因此每个分量的取值范围从0到255;对于灰度图像,每个像素通常用一个分量表示,一个分量同样由一个字节表示,取值范围从0到255。
在libjpeg中,图像数据是以扫描线的形式存放的。每一条扫描线由一行像素点构成,像素点沿着扫描线从左到右依次排列。对于彩色图像,每个分量由三个字节组成,因此这三个字节以R、G、B的顺序构成扫描线上的一个像素点。一个典型的扫描线形式如下:
R,G,B,R,G,B,R,G,B,…
通过libjpeg解压出来的图像数据也是以扫描线的形式存放的。
在本文中,只涉及到JPEG的解压缩,因此只对libjpeg的解压过程进行说明,有关libjpeg的压缩过程和其它高级用法,请参考[3]。一般地,libjpeg的解压过程如下:
1、分配并初始化一个JPEG解压对象(本文中将JPEG解压对象命名为cinfo):
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
...
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
2、指定要解压缩的图像文件:
FILE * infile;
...
if ((infile = fopen(filename, "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
exit(1);
}
jpeg_stdio_src(&cinfo, infile);
3、调用jpeg_read_header()获取图像信息:
jpeg_read_header(&cinfo, TRUE);
4、这是一个可选步骤,用于设置JPEG解压缩对象cinfo的一些参数,本文可忽略;
5、调用jpeg_start_decompress()开始解压过程:
jpeg_start_decompress(&cinfo);
调用jpeg_start_decompress()函数之后,JPEG解压缩对象cinfo中的下面这几个字段将会比较有用:
l output_width 这是图像输出的宽度
l output_height 这是图像输出的高度
l output_components 每个像素的分量数,也即字节数
这是因为在调用jpeg_start_decompress()之后往往需要为解压后的扫描线上的所有像素点分配存储空间,这个空间的大小可以通过output_width * output_componets确定,而要读取的扫描线的总数为output_height行。
6、读取一行或者多行扫描线数据并处理,通常的代码是这样的:
while (cinfo.output_scanline < cinfo.ouput_height) {
jpeg_read_scanlines();
/* deal with scanlines */
}
对扫描线的读取是按照从上到下的顺序进行的,也就是说图像最上方的扫描线最先被jpeg_read_scanlines()读入存储空间中,紧接着是第二个扫描线,最后是图像底边的扫描线被读入存储空间中。
7、调用jpeg_finish_decompress()完成解压过程:
jpeg_finish_decompress(&cinfo);
8、调用jpeg_destroy_decompress()释放JPEG解压对象cinfo:
jpeg_destroy_decompress(&cinfo);
以上就是通过libjpeg函数解压JPEG压缩图像的基本过程,由于本文不涉及libjpeg的高级特性和用法,因此,上面的介绍对于说明本文中要用到的libjpeg的功能已经足够了。
另外一个需要说明地方是:由于作者所用的Framebuffer设备的颜色深度为16位,颜色格式为5-6-5格式——即R(红色)在16bit中占据高5位,G(绿色)在16bit中占据中间6位,B(蓝色)在16bit中占据低5位;而libjpeg解压出来的图像数据为24位RGB格式,因此必须进行转换。对于24位的RGB,每个字节表示一个颜色分量,因此转换的方式为:对于R字节,右移3位,对于G字节,右移2位,对于B字节,右移3位,然后将右移得到的值拼接起来,就得到了16位的颜色值。在后面的程序中,将把24位的颜色称为RGB888,而把16位颜色值称为RGB565,这种命名方式可能不太规范,不过无论如何,在本文中就这样称呼了。另外,读者可能会想到,上面这种直接将颜色分量的低位丢弃的方式不是会导致图像细节的丢失吗?比如,对于24位颜色的R字节,假如原来低3位的值在0~7之间均匀分布,转换之后,所有这低3位的值全部都变成了0,这就是颜色细节的丢失。为了处理这个问题,可以采用误差扩散算法或者抖动算法来完成颜色转换。上面介绍的是最简单的转换方式——而且事实表明,这样做得到的结果并不差。
for
(i
=
0
;i
<
16
;i
+=
2
)
{
bits
=
ch[i
/
2
];
for
(j
=
0
;j
<
16
;j
+=
2
,bits
<<=
1
)
if
(bits
&
0x80
)
{
location1
=
(x
+
j)
*
(vinfo.bits_per_pixel
/
8
)
+
(y
+
i)
*
finfo.line_length;
location2
=
(x
+
j
+
1
)
*
(vinfo.bits_per_pixel
/
8
)
+
(y
+
i)
*
finfo.line_length;
location3
=
(x
+
j)
*
(vinfo.bits_per_pixel
/
8
)
+
(y
+
i
+
1
)
*
finfo.line_length;
location4
=
(x
+
j
+
1
)
*
(vinfo.bits_per_pixel
/
8
)
+
(y
+
i
+
1
)
*
finfo.line_length;
*
(unsigned
short
*
)(fbp
+
location1)
=
0x00ff
;
/*
蓝色的色深
*/
/*
直接赋值来改变屏幕上某点的颜色
*/
*
(unsigned
short
*
)(fbp
+
location2)
=
0x00ff
;
/*
蓝色的色深
*/
/*
直接赋值来改变屏幕上某点的颜色
*/
*
(unsigned
short
*
)(fbp
+
location3)
=
0x00ff
;
/*
蓝色的色深
*/
/*
直接赋值来改变屏幕上某点的颜色
*/
*
(unsigned
short
*
)(fbp
+
location4)
=
0x00ff
;
/*
蓝色的色深
*/
/*
直接赋值来改变屏幕上某点的颜色
*/
}
}