jpeg库的移植和jpeg图片的显示

一. jpeg介绍

jpeg (jpg)是一种国际图片压缩标准格式
        手机照片
        网页图片

用jpeg算法压缩的格式。  "jpeg算法"


jpeg的所有压缩和解压缩的代码都是一个开源的第三方库(libjpeg)提供的
只需要使用一下libjpeg中的函数解压缩jpg图片即可获取图片中的像素数据

libjpeg是什么呢?
        是一个开源的第三方库,与jpeg图片的压缩和解压缩有关系,压缩和解压缩都需要使用libjpeg库
    
libjpeg的开源的库:
        compress:压缩       RGB数据 -> jpeg图片
        decompress:解压缩    jpeg图片 -> RGB数据


目的:学会第三方库的使用和移植(不仅仅是jpeg)


一般在网络上面下载的仅仅是源代码,需要把源代码编译成动态库或者静态库的形式才可以使用

使用自己的程序去调用libjpeg库中的功能
        libjpeg库,放到x86上面使用:  gcc
        libjpeg库,放到开发板上面使用:   arm-linux-gcc
    
移植/适配:把库放到开发板或者其他的宿主机器中使用

二. libjpeg库移植及使用

移植步骤:

1. 下载并且解压源码(jpegsrc.v8a.tar.gz)
    (不要解压在共享目录里面,共享目录是windows下面的文件系统,它不支持链接文件)
        mkdir  ~/libjpeg
        cp /mnt/hgfs/CS2406F/2024-1-22文件IO/2024-1-24项目知识点/03jpeg库的移植和jpeg图片的显示/jpegsrc.v8a.tar.gz   /home/china/libjpeg/
        cd  ~/libjpeg

        tar -zxvf jpegsrc.v8a.tar.gz
    
2. 编译源代码生成库文件
        cd jpeg-8a
        // 创建一个文件夹,用来保存编译之后生成的库文件等信息
        mkdir  /home/china/libjpeg/arm_libs
    
        // 配置,根据指定信息配置编译选项
          (直接配置./configure,会检测系统信息,认为你需要安装到当前的系统)
    
        // 我们的库是需要放到开发板上面去运行的,所以在配置的时候需要指定主机架构和库文件的生成路径
        ./configure --host=arm-linux --target=arm-linux--prefix=/home/china/libjpeg

/arm_libs CC=arm-linux-gcc
                
        自动生成            
        => Makefile            

                
3. 
    根据Makefile 编译源代码
    make


4. 因为你在配置的时候,指定了安装路径 /home/china/libjpeg/arm_libs  
    // 安装
    make install                
         
5. 所有的库都安装在指定的目录中    
    /home/china/libjpeg/arm_libs
    因为是动态库,在编译和链接的时候都需要使用动态库    
    libjpeg.so
    libjpeg.so.8
    libjpeg.so.8.0.1
    把需要的库,下载到开发板的/lib目录中
            如果没有放到/lib中,在使用的时候需要指定库的加载路径
    
到此为止,jpeg库的编译移植就完成了,就可以在开发板上面使用jpeg库了

利用libjpeg库解压jpeg文件的步骤:

1. 分配并初始化一个jpeg解压对象

    struct jpeg_decompress_struct 这个结构体在libjpeg这个库中,
    是用来保存解压一个jpeg文件所需要的信息的

    struct jpeg_error_mgr 这个结构体在libjpeg这个库中,
    是用来保存解压或压缩过程的一些出错信息的

    典型代码:
    struct jpeg_decompress_struct dinfo; // 声明一个解压的对象
    struct jpeg_error_mgr jerr; // 声明一个出错信息的对象
    dinfo.err = jpeg_std_error(&jerr); // 初始化这个出错对象
    jpeg_create_decompress(&dinfo); // 初始化dinfo这个解压对象

2. 指定要解压缩的图像文件


    图像文件(jpeg文件)来源有两个:
        1) 一个在文件系统中(路径名)
        2) 在内存中(内存地址,长度)
             打开文件,把所有的内容读取(read)到指定的内存地址


    1) 用标准IO去打开这个文件
        FILE *infile;
        infile = fopen("xxx.jpg", "r");
        if (infile == NULL)
        {}
        
        jpeg_stdio_src(&dinfo, infile); // 指定要解压的图像文件

    2) 
        jpeg_mem_src(&dinfo, pbuf, len); 
        // 指定要解压的图像在pbuf指向的内存中,并且长度为len
          
3. 调用jpeg_read_header()获取图像信息
        jpeg_read_header(&dinfo, TRUE);
 
4. 用于设置jpeg解压对象dinfo的一些参数。
        可采用默认参数

5. 调用jpeg_start_decompress()启动解压过程
        jpeg_start_decompress(&dinfo);
        
    调用jpeg_start_decompress函数之后,JPEG解压对象dinfo中
    下面这几个字段(成员变量)将会比较有用:
        dinfo.output_width:     图像输出宽度,一行占多少个像素点
        dinfo.output_height:    图像输出高度,占多少行
        dinfo.output_components:  每个像素点的分量数,每个像素点占多少个字节
                                3: R G B
                                4:A R G B
        width * height * components  图像大小

    在调用jpeg_start_decompress之后,往往需要为解压后的扫描线上的所有像素点分配存储空间:
        可以每一次读取一行解压后的像素点信息(RGB)
        开辟合适的空间,存储那一行的所有像素点的信息。
        存一行: output_width * output_components

6. 读取一行或者多行扫描线上数据并处理,通常的代码是这样子的:

    unsigned char *buffer = (unsigned char*)malloc(dinfo.output_width * dinfo.output_components);

        // dinfo.output_scanline  表示的意思是,已经扫描了多少行
        
        while (dinfo.output_scanline < dinfo.output_height) {
            jpeg_read_scanlines(&dinfo,  // 解压对象
                                &buffer, // 保存解压后数据的二级指针, 
                                1 // 读取多少行数据来解压
                                );    
                                // dinfo.output_scanline + 1
        }
    对扫描线的读取是按照从上到下的顺序进行的,也就是说图像最上方的扫描线最先被jpeg_read_scanlines()读入到存储空间中,紧接着是第二行扫描线,最后是图像底边的扫描线被读入到存储空间中去
    
    一行一行的把jpeg图像的信息(RGB)拿出来    

7. 调用jpeg_finish_decompress()完成解压过程

        jpeg_finish_decompress(&dinfo);


8. 调用jpeg_destroy_decompress释放jpeg解压对象dinfo

        jpeg_destroy_decompress(&dinfo);

        // 关闭前面打开的文件
        fclose(infile);

代码实现:

// 1.分配并初始化一个jpeg解压对象
struct jpeg_decompress_struct dinfo; // 声明一个解压的对象
struct jpeg_error_mgr jerr; // 声明一个出错信息的对象
dinfo.err = jpeg_std_error(&jerr); // 初始化这个出错对象
jpeg_create_decompress(&dinfo); // 初始化dinfo这个解压对象

// 2.指定要解压缩的图像文件
FILE *infile = fopen("1.jpg", "r"); // 用标准IO去打开这个文件
if (infile == NULL) {
    perror("in jpeg decompress fopen failed");
    return -1;
}
jpeg_stdio_src(&dinfo, infile); // 指定要解压的图像文件
                        
// 3.调用jpeg_read_header()获取图像信息
jpeg_read_header(&dinfo, TRUE);
            
// 4.用于设置jpeg解压对象dinfo的一些参数。可采用默认参数

// 5.调用jpeg_start_decompress()启动解压过程
jpeg_start_decompress(&dinfo);
            
// 调用jpeg_start_decompress函数之后,JPEG解压对象dinfo中
// 下面这几个字段(成员变量)将会比较有用:
// dinfo.output_width: 	图像输出宽度,一行占多少个像素点
// dinfo.output_height:	图像输出高度,占多少行
// dinfo.output_components:  每个像素点的分量数,每个像素点占多少个字节
// 在调用jpeg_start_decompress之后,往往需要为解压后的扫描线上的所有像素点分配存储空间:可以每一次读取一行解压后的像素点信息(RGB)
printf("dinfo.output_width = %d\n", dinfo.output_width);
printf("dinfo.output_height = %d\n", dinfo.output_height);
printf("dinfo.output_components = %d\n", dinfo.output_components);

// 6.读取一行或者多行扫描线上数据并处理,通常的代码是这样子的:
unsigned char *buffer = (unsigned char*)malloc(dinfo.output_width * 
dinfo.output_components);
        
// dinfo.output_scanline 表示的意思是,已经扫描了多少行
int i, j = 0;
unsigned char a = 0, r, g, b;
// 一行一行的把jpg图像信息(RGB)拿出来,并且显示        
while (dinfo.output_scanline < dinfo.output_height) {
    jpeg_read_scanlines(&dinfo, // 解压对象
                        &buffer, // 保存解压后数据的二级指针, 
                        1 // 读取多少行数据来解压
                        ); // dinfo.output_scanline + 1
    // 解析当前行的像素信息(保存到了buffer中)
    char *p = buffer;
    for (i = 0; i < dinfo.output_width; i++) { // 一行有dinfo.output_width个点
        if (dinfo.output_components == 4) {
            a = *p++;
        }
        r = *p++;
        g = *p++;
        b = *p++;
        int color = a << 24 | r << 16 | g << 8 | b;
        *(plcd + j * 800 + i) = color;
    }
    j++;
}
 
free(buffer);

// 7.调用jpeg_finish_decompress()完成解压过程
jpeg_finish_decompress(&dinfo);

// 8.调用jpeg_destroy_decompress释放jpeg解压对象dinfo
jpeg_destroy_decompress(&dinfo);
   
// 关闭前面打开的文件
fclose(infile);

pic代码实现

pic.h

#ifndef __PIC_H__
#define __PIC_H__

#include "lcd.h"
// #include "jpeglib.h"

/*
    lcd_draw_bmp:把bmpname表示的图片显示到lp表示的屏幕的(x0,y0)位置
*/
void lcd_draw_bmp(lcd *lp, char *bmpname, int x0, int y0);

/*
    lcd_draw_jpg:把指定的jpg图片显示到开发板的指定的位置
    lp:你要操作屏幕结构体的地址
    jpgname:图片的路径名
    x0,y0:图片的显示位置坐标(显示到开发板的哪一个位置)
    返回值:无
*/
// void lcd_draw_jpg(lcd *lp, char *jpgname, int x0, int y0);	

#endif

pic.c

#include "pic.h"

/*
    lcd_draw_bmp:把bmpname表示的图片显示到lp表示的屏幕的(x0,y0)位置
*/
void lcd_draw_bmp(lcd *lp, char *bmpname, int x0, int y0) 
{
    // 打开BMP图片
    int fd = open(bmpname, O_RDWR);
    if (fd == -1) 
    {
        perror("open bmp failed");
        return ;
    }

    // 读取BMP图片的像素信息到内存
    char buf[lp->xres * lp->yres * lp->bits_per_pixel + 54];
    memset(buf, 0, sizeof(buf));
    ssize_t r_bmp = read(fd, buf, lp->xres * lp->yres * lp->bits_per_pixel + 54);
    if (-1 == r_bmp) 
    {
        perror("read bmp failed");
        close(fd);
        return ;
    }
    // 关闭BMP图片
    close(fd);

    int size = 0, w = 0, h = 0;
    size = buf[5] << 24 | buf[4] << 16 | buf[3] << 8 | buf[2];
    w = buf[21] << 24 | buf[20] << 16 | buf[19] << 8 | buf[18];
    h = buf[25] << 24 | buf[24] << 16 | buf[23] << 8 | buf[22];
    int colour_dep = buf[29] << 8 | buf[28];
    
    // 判断是否超出屏幕范围
    if (x0 + w > lp->xres || y0 + h > lp->yres)
    {
        printf("brother,Out of range\n");
        return ;
    }
    
    // 把读取到的像素点信息解析出来,显示到屏幕对应的位置
    int i, j, k = 54;
    unsigned char a, r, g, b;
    int colour;
    for (j = h - 1; j >= 0; j--) // 图片有h行
    { 
        // 显示当前行的每一个点
        for (i = 0; i < w; i++) // 每一行w个点
        {
            // 解析每一个像素点
            b = buf[k++];
            g = buf[k++];
            r = buf[k++];
            a = (colour_dep == 32) ? buf[k++] : 0;
            colour = (a << 24) | (r << 16) | (g << 8) | b;
            lcd_draw_point(lp, i + x0, j + y0, colour);
        }
        if ((w * (colour_dep / 8)) % 4 != 0) {
            k = k + 4 - (w * (colour_dep / 8)) % 4;
        }
    }
}

/*
    lcd_draw_jpg:把指定的jpg图片显示到开发板的指定的位置
    lp:你要操作屏幕结构体的地址
    jpgname:图片的路径名
    x0,y0:图片的显示位置坐标(显示到开发板的哪一个位置)
    返回值:无
*/
// void lcd_draw_jpg(lcd *lp, char *jpgname, int x0, int y0) 
// {
//     // 1.分配并初始化一个jpeg解压对象
//     struct jpeg_decompress_struct dinfo; // 声明一个解压的对象
//     struct jpeg_error_mgr jerr; // 声明一个出错信息的对象
//     dinfo.err = jpeg_std_error(&jerr); // 初始化这个出错对象
//     jpeg_create_decompress(&dinfo); // 初始化dinfo这个解压对象

//     // 2.指定要解压缩的图像文件
//     FILE *infile = fopen(jpgname, "r"); // 用标准IO去打开这个文件
//     if (infile == NULL) 
//     {
//         perror("in jpeg decompress fopen failed");
//         return ;
//     }
//     jpeg_stdio_src(&dinfo, infile); // 指定要解压的图像文件
                            
//     // 3.调用jpeg_read_header()获取图像信息
//     jpeg_read_header(&dinfo, TRUE);
                
//     // 4.用于设置jpeg解压对象dinfo的一些参数。可采用默认参数

//     // 5.调用jpeg_start_decompress()启动解压过程
//     jpeg_start_decompress(&dinfo);
                
//     // 调用jpeg_start_decompress函数之后,JPEG解压对象dinfo中
//     // 下面这几个字段(成员变量)将会比较有用:
//     // dinfo.output_width: 	图像输出宽度,一行占多少个像素点
//     // dinfo.output_height:	图像输出高度,占多少行
//     // dinfo.output_components:  每个像素点的分量数,每个像素点占多少个字节
//     // 在调用jpeg_start_decompress之后,往往需要为解压后的扫描线上的所有像素点分配存储空间:可以每一次读取一行解压后的像素点信息(RGB)
//     // printf("dinfo.output_width = %d\n", dinfo.output_width);
//     // printf("dinfo.output_height = %d\n", dinfo.output_height);
//     // printf("dinfo.output_components = %d\n", dinfo.output_components);

//     // 6.读取一行或者多行扫描线上数据并处理,通常的代码是这样子的:
//     unsigned char *buffer = (unsigned char*)malloc(dinfo.output_width * 
//     dinfo.output_components);
//     // dinfo.output_scanline 表示的意思是,已经扫描了多少行
//     int i, j = 0;
//     unsigned char a = 0, r, g, b;
//     // 一行一行的把jpg图像信息(RGB)拿出来,并且显示        
//     while (dinfo.output_scanline < dinfo.output_height) 
//     {
//         jpeg_read_scanlines(&dinfo, // 解压对象
//                             &buffer, // 保存解压后数据的二级指针, 
//                             1 // 读取多少行数据来解压
//                             ); // dinfo.output_scanline + 1
//         // 解析当前行的像素信息(保存到了buffer中)
//         char *p = buffer;
//         for (i = 0; i < dinfo.output_width; i++) // 一行有dinfo.output_width个点
//         {
//             if (dinfo.output_components == 4) 
//             {
//                 a = *p++;
//             }
//             r = *p++;
//             g = *p++;
//             b = *p++;
//             int color = a << 24 | r << 16 | g << 8 | b;
//             lcd_draw_point(lp, i + x0, j + y0, color);
//         }
//         j++;
//     }

//     free(buffer);

//     // 7.调用jpeg_finish_decompress()完成解压过程
//     jpeg_finish_decompress(&dinfo);

//     // 8.调用jpeg_destroy_decompress释放jpeg解压对象dinfo
//     jpeg_destroy_decompress(&dinfo);
    
//     // 关闭前面打开的文件
//     fclose(infile);
// }

练习

完成一个简单的程序,实现轮播指定目录下面所有的bmp图片文件和jpg图片文件

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值