使用libjpeg库在LCD上显示图片

背景:
网上已经有很多关于利用libjpeg显示图片的文章了,因此本文的技术含量不算高。本文是使用libjpeg的v8版本,在开发板的LCD上显示jpg格式图片,关于libjpeg,可到其官方网站下载源码,源码附有许多文档,包括详细的例子(example.c)。

关于如何使用libjpeg,本文不再说明,因为网上文章已经很多了。本文中的代码有几处是自已经修改过的。
1、将所有操作framebuffer的函数放到单独的文件,名称为fb_utils.c,代码主要是来自一个叫ripple的项目。这个文件也用到其它很多地方。
2、libjpeg指定的图片来源有两种,一是文件,一是内存。后者是新版本支持的,旧版本,例如6b,就没有这个函数。

完整的显示jpg图片函数如下:

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/** 
 * draw_jpeg - Display a jpeg picture from (0, 0) on framebuffer
 * @name: picture name(foo.jpg)
 */
int draw_jpeg(char *name)
{
        struct jpeg_decompress_struct cinfo;
        struct jpeg_error_mgr jerr;
        FILE *infile;
        unsigned char  *buffer;
        int x, y;

        unsigned char *tmp_buf;
        unsigned long size;
        int ret;

        if ((infile = fopen(name, "rb")) == NULL) {
                fprintf(stderr, "open %s failed\n", name);
                return -1;
        }
        cinfo.err = jpeg_std_error(&jerr);
        jpeg_create_decompress(&cinfo);

        // 获取图片文件大小
        fseek(infile, 0L, SEEK_END);
        size = ftell(infile);
        rewind(infile);
        
        tmp_buf = (unsigned char *)malloc(sizeof(char) * size);
        if (tmp_buf == NULL)
        {
                printf("malloc failed.\n");
                return -1;
        }
        ret = fread(tmp_buf, 1, size, infile);
        if (ret != size)
        {
                printf("read jpeg file error.\n");
                return -1;
        }
        // memory
        jpeg_mem_src(&cinfo, tmp_buf, size);        // 指定图片在内存的地址及大小

        
        //jpeg_stdio_src(&cinfo, infile); // 指定图片文件
        
        jpeg_read_header(&cinfo, TRUE);
        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);
        //printf("%d %d\n", cinfo.output_width, cinfo.output_components); /*eg, 240 3(rgb888)*/
        x = 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++) {
                                // LCD为rgb565格式
                                color = make16color(buffer[x*3], buffer[x*3+1], buffer[x*3+2]);
                                fb_pixel(x, y, color);
                        }
                } else if (fb_depth == 24) {
                        // not test
                        memcpy((unsigned char *) fb_mem + y * fb_width * 3,
                                   buffer, cinfo.output_width * cinfo.output_components);
                }
                y++;        // next scanline
        }

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

        fclose(infile);

        free(tmp_buf);

        free(buffer);
        return 0;
}

几点说明:
1、使用图片文件测试,第一种是用jpeg_stdio_src指定图片来源为文件,第二种是将图片读入内存,再使用jpeg_mem_src来指定图片在内存的位置及大小。
2、本文测试所用的LCD格式为rgb565,即16色,而原来图片为24色,所以需要使用一个函数来转换。当得到适合在LCD显示的颜色值后,就可以调用像素函数在LCD上画点了。
就这么简单。
下面是在LCD上显示的图片,使用fb2png截图。
rgb565格式的图片rgb888格式的图片

左图是rgb565格式,因为LCD就是这种格式,显示正常,而右图是rgb888格式,不能正常显示出来。
关于fb2png这个工具,我找了好久,现在已经不记得原代码出处了。可以到 http://download.csdn.net/detail/subfate/3345143下载,如果没有CSDN的积分,请到 http://www.latelee.org/yetanothertest/tools/下载。
完整的工程及测试用的图片压缩包下载: http://www.latelee.org/yetanothertest/program/src/jpeg-test-latelee.org.tar.bz2
注:压缩包中的fb_utils.c文件在不断修改,当修改后,恕不更新此处的工程。