前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >minigui:增加__mg_save_jpg保存JPEG格式图像

minigui:增加__mg_save_jpg保存JPEG格式图像

作者头像
10km
发布2019-05-25 20:59:52
5030
发布2019-05-25 20:59:52
举报
文章被收录于专栏:10km的专栏10km的专栏

版权声明:本文为博主原创文章,转载请注明源地址。 https://cloud.tencent.com/developer/article/1433486

minigui 库中有SaveBitmapToFileSaveMyBitmapToFile可以将一个(BITMAP或MYBITMAP)图像对象存储为文件。然而图像格式只支持bmp(libminigui-3.2.0)。

但嵌入式平台上存储空间有限,我确实需要存储为JPEG啊。

怎么办?自己撸一个。

JPEG压缩的流程都是固定的,网上有很多代码,JPEG自己也有例子,这不难,复杂的地方是将一个MYBITMAP对象的图像数据转成JPEG压缩所需要的RGB color space(色彩空间)。minigui支持RGB,BRG,RGBA,RGB565,index 16 index 256,几种color space,RGB最简单,不需要转,其他几种color space都需要分别进行转换。

以下为JPEG压缩流程主函数代码:

代码语言:javascript
复制
int __fl_save_jpg (MG_RWops* fp, MYBITMAP* mybmp, RGB* pal)
{
    j_compress_ptr cinfo;
    struct my_error_mgr *jerr;
    JSAMPROW linebuffer = NULL;
    JSAMPROW row_pointer[1];
    MYBITMAP_get_pixel_row get_row;
    int retcode = ERR_BMP_CANT_SAVE;

    /* Step 1: 创建 JPEG 压缩对象 */
    cinfo = calloc (1, sizeof(struct jpeg_compress_struct));

    if(NULL == cinfo)
    {
        fprintf(stderr, "__fl_save_jpg allocation error!\n");
        return ERR_BMP_MEM;
    }
    jpeg_create_compress(cinfo);

    /* Step 2: 创建错误处理对象  */
    jerr = cinfo->mem->alloc_small((j_common_ptr) cinfo, JPOOL_IMAGE,sizeof(struct my_error_mgr));

    if(NULL == jerr)
    {
        retcode = ERR_BMP_MEM;
        goto do_finally;
    }
    memset(jerr,0,sizeof(struct my_error_mgr));

    /* We set up the normal JPEG error routines first. */
    cinfo->err = jpeg_std_error (&jerr->pub);
    jerr->pub.error_exit = my_error_exit;

    /* Establish the setjmp return context for my_error_exit to use. */
    if (setjmp (jerr->setjmp_buffer)) {
        /* 出错退出 */
        fprintf(stderr, "__fl_save_jpg error!\n");
        goto do_finally;
    }
    /* 只支持 RWAREA_TYPE_STDIO 类型的MG_RWops对象,否则报错,
     *  MEM 类型的对象不能动态管理内存,不安全,所以不支持 */
    if(RWAREA_TYPE_STDIO != fp->type )
    {
        fprintf(stderr, "unsupported type of MG_RWops,only support RWAREA_TYPE_STDIO so far\n");
        longjmp (jerr->setjmp_buffer, 1);
    }
    /* Step 3: 将MG_RWops对象的FILE设置为压缩数据输出对象 */
    jpeg_stdio_dest(cinfo, fp->hidden.stdio.fp);

    /* Step 4: 初始化JPEG 压缩对象 */
    /* for JPEG compression, supported color space : JCS_GRAYSCALE,JCS_RGB,JCS_YCbCr,JCS_CMYK,JCS_YCCK
     * in this case,MYBITMAP is base on RGB ,
     * we can select JCS_RGB only,so we must convert all color space (eg.RGBA,BGR,RGB565,...) to RGB
     * */
    cinfo->in_color_space = JCS_RGB;
    cinfo->image_width = mybmp->w;
    cinfo->image_height = mybmp->h;
    cinfo->input_components = 3;
    /* set jpeg compression parameters to default */
    jpeg_set_defaults(cinfo);

#if _MGIMAGE_JPG_SAVE_QUALITY > 0 && _MGIMAGE_JPG_SAVE_QUALITY <= 100
    /* 如果定义了_MGIMAGE_JPG_SAVE_QUALITY,就用它来指定压缩质量,否则使用默认值 75
     * see also: libjpeg/jcparam.c jpeg_set_defaults function.
     * */
    jpeg_set_quality(cinfo, _MGIMAGE_JPG_SAVE_QUALITY, TRUE);
#endif

    int mybmp_type = mybmp->flags & MYBMP_TYPE_MASK;
    /* 根据色彩空间类型确定 色彩空间转换函数(本文后面有提供) */
    switch(mybmp->depth)
    {
    case 4:
        get_row = MYBITMAP_get_pixel_row_pal16;
        break;
    case 8:
        get_row = MYBITMAP_get_pixel_row_pal256;
        break;
    case 16:
        get_row = MYBITMAP_get_pixel_row_RGB565;
        break;
    case 24:
        if(MYBMP_TYPE_RGB == mybmp_type)
            get_row = MYBITMAP_get_pixel_row_RGB;
        else
            get_row = MYBITMAP_get_pixel_row_BGR;
        break;
    case 32:
        get_row = MYBITMAP_get_pixel_row_RGBA;
        break;
    default:
        fprintf(stderr, "invalid MYBITMAP.depth = %d\n",mybmp->depth);
        longjmp (jerr->setjmp_buffer, 1);
        break;

    }
    if(24 == mybmp->depth && MYBMP_TYPE_RGB == mybmp_type)
    {
        /*
         * do nothing while RGB type,
         * the MYBITMAP_get_pixel_row function will return address in MYBITMAP.bits data directly,
         * without using line buffer
         * */
    }
    else
    {
        /* Allocate one-row buffer for color space conversion(4 byte alignment)  */
        linebuffer = (JSAMPROW)cinfo->mem->alloc_large
            ((j_common_ptr) cinfo, JPOOL_IMAGE,
            (((cinfo->image_width + 3) & ~3) * cinfo->input_components));
        if(NULL == linebuffer)
        {
            retcode = ERR_BMP_MEM;
            fprintf(stderr, "libjpeg allocation error!\n");
            longjmp (jerr->setjmp_buffer, 1);
        }
    }
    /* Step 5: 逐行执行压缩 */
    jpeg_start_compress(cinfo, TRUE);
    while (cinfo->next_scanline < cinfo->image_height) {
        /* 调用色彩空间转函数将一行数据转为RGB */
        row_pointer[0] = get_row(cinfo->next_scanline, mybmp, pal, linebuffer);
        jpeg_write_scanlines(cinfo, row_pointer, 1);
    }

    jpeg_finish_compress(cinfo);
    retcode = ERR_BMP_OK;
do_finally:
    /* clean up the JPEG object, free the objects and return. */
    jpeg_destroy_compress (cinfo);
    free (cinfo);
    return retcode;
}

色彩空间转代码:

代码语言:javascript
复制
//  索引色(16色) 转换为RGB
static BYTE* MYBITMAP_get_pixel_row_pal16(unsigned int next_scanline,
                       MYBITMAP* mybmp,RGB* pal,BYTE* linebuffer)
{

    BYTE* bits = mybmp->bits + mybmp->pitch * next_scanline;
    RGB rgb0,rgb1;
    for (int i = 0, j = 0, end_i = (mybmp->w + 1) >> 1; i < end_i; ++i) {

        rgb0 = pal[ (bits[ i ] & 0XF0) >> 4 ];
        rgb1 = pal[  bits[ i ] & 0X0F ];

        linebuffer[ j ++ ] = rgb0.r;
        linebuffer[ j ++ ] = rgb0.g;
        linebuffer[ j ++ ] = rgb0.b;
        linebuffer[ j ++ ] = rgb1.r;
        linebuffer[ j ++ ] = rgb1.g;
        linebuffer[ j ++ ] = rgb1.b;
    }
    return linebuffer;

}
// 索引色(256色) 转换为RGB
static BYTE* MYBITMAP_get_pixel_row_pal256(unsigned int next_scanline,
                       MYBITMAP* mybmp,RGB* pal,BYTE* linebuffer)
{

    BYTE* bits = mybmp->bits + mybmp->pitch * next_scanline;
    RGB rgb;
    for (int i = 0, j = 0; i < mybmp->w; i++) {

        rgb = pal [bits[i] ];

        linebuffer[ j++ ] = rgb.r;
        linebuffer[ j++ ] = rgb.g;
        linebuffer[ j++ ] = rgb.b;

    }
    return linebuffer;

}

#define RGB_FROM_RGB565(pixel, r, g, b)                         \
{                                                               \
    r = (((pixel&0xF800)>>11)<<3);                              \
    g = (((pixel&0x07E0)>>5)<<2);                               \
    b = ((pixel&0x001F)<<3);                                    \
}

// RGB565 转换为 RGB
static BYTE* MYBITMAP_get_pixel_row_RGB565(unsigned int next_scanline,
                       MYBITMAP* mybmp,RGB* pal,BYTE* linebuffer)
{

    Uint16* bits = (Uint16*)(mybmp->bits + mybmp->pitch * next_scanline);

    for (int i = 0, j = 0; i < mybmp->w; i++) {

        RGB_FROM_RGB565(bits[i], linebuffer[ j ++ ], linebuffer[ j ++ ], linebuffer[ j ++ ])

    }
    return linebuffer;

}
//  RGB to RGB,直接返回MYBITMAP的图像数据地址
static BYTE* MYBITMAP_get_pixel_row_RGB(unsigned int next_scanline,
        MYBITMAP* mybmp,RGB* pal,BYTE* linebuffer)
{
    return (JSAMPROW)(mybmp->bits + mybmp->pitch * next_scanline);
}

// BGR to RGB
static BYTE* MYBITMAP_get_pixel_row_BGR(unsigned int next_scanline,
        MYBITMAP* mybmp,RGB* pal,BYTE* linebuffer)
{
    BYTE* bits = mybmp->bits + mybmp->pitch * next_scanline;

    for (int i = 0, end_i = mybmp->w * 3; i < end_i; i += 3) {
        linebuffer[ i     ] = bits[ i + 2 ];
        linebuffer[ i + 1 ] = bits[ i + 1 ];
        linebuffer[ i + 2 ] = bits[ i     ];
    }
    return linebuffer;
}

#ifndef _MGIMAGE_JPG_RGBA_BGCOLOR
/* background color for RGBA color space conversion,for example: 0xFF0000 is red */
#define _MGIMAGE_JPG_RGBA_BGCOLOR 0xFFFFFF
#endif

// RGBA to RGB,转时会将alpha通道值与R,G,B合并计算出新的RGB值
static BYTE* MYBITMAP_get_pixel_row_RGBA(unsigned int next_scanline,
        MYBITMAP* mybmp,RGB* pal,BYTE* linebuffer)
{
    RGB* bits = (RGB*)(mybmp->bits + mybmp->pitch * next_scanline);
    RGB pixel;
    RGB bgcolor = {
            (_MGIMAGE_JPG_RGBA_BGCOLOR >> 16) & 0xFF,/* red */
            (_MGIMAGE_JPG_RGBA_BGCOLOR >>  8) & 0xFF,/* green */
             _MGIMAGE_JPG_RGBA_BGCOLOR        & 0xFF,/* blue */
            0x00                                     /* alpha,no used */
    };
    for (int i = 0,j = 0; i < mybmp->w; i++, j += 3) {
        pixel = bits[i];
        /* alpha composite,
         * C = Cx * ALPHAx + (1 - ALPHAx) * Cbg
         * see also : https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator */
        linebuffer[ j     ]  = (JSAMPLE)((Uint32)pixel.r * pixel.a >> 8); /* red */
        linebuffer[ j + 1 ]  = (JSAMPLE)((Uint32)pixel.g * pixel.a >> 8); /* green */
        linebuffer[ j + 2 ]  = (JSAMPLE)((Uint32)pixel.b * pixel.a >> 8); /* blue */

        linebuffer[ j     ] += (JSAMPLE)((Uint32)bgcolor.r * (255 - pixel.a) >> 8); /* red   + background color*/
        linebuffer[ j + 1 ] += (JSAMPLE)((Uint32)bgcolor.g * (255 - pixel.a) >> 8); /* green + background color */
        linebuffer[ j + 2 ] += (JSAMPLE)((Uint32)bgcolor.b * (255 - pixel.a) >> 8); /* blue  + background color */
    }
    return linebuffer;
}

好了那个童鞋,不要抄笔记了,完整的代码我已经pull request到MiniGUI 官方github仓库(https://github.com/VincentWei/minigui),MiniGUI作者已经将pull request合并到主版本。你可以直接下载三个更新文件:

src/mybmp/jpeg.c

src/include/readbmp.h,

src/mybmp/mybmp.c

覆盖自己的MiniGUI源码重新编译就可以了。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年09月04日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档