C++图片格式转换:BMP转JPEG

Wesley13
• 阅读 320

C++方式将BMP格式转换为JPEG格式,依赖了一个第三方库,工程下载链接为:点击打开链接

Bmp2Jpeg.h:

#pragma once

class CBmp2Jpeg
{
public:
    CBmp2Jpeg();
    ~CBmp2Jpeg();

public:
    int Bmp2Jpeg(const char *bmp, const char *jpeg);

private:
    int SaveJpeg(const char *filename, unsigned char *bits, int width, int height, int depth);
    int ReadBmp(const char *bmp, unsigned char **data,int &w, int &h, int &d);
    void Bgra2Rgb(const unsigned char *src, int w, int h, int d, unsigned char *dst);
    void InitFileHeader(void *pFile, void *fileHeader);
    void InitInfoHeader(void *pFile, void *infoHeader);
    void SaveBmp(void *fileHeader, void *infoHeader, int bitCount, unsigned char *data, const char *savename);
private:
    int    m_quality;    //它的大小决定jpg的质量好坏

    enum{
        JPEG_QUALITY = 100, 
    };
};

Bmp2Jpeg.cpp:

#include <iostream>
#include<Windows.h>
#include <fstream>
//#include<stdlib.h>
#include "Bmp2Jpeg.h"
#include <vector>

extern "C"
{
#include "jpeglib.h"
};

#pragma comment(lib,"libjpeg.lib")

using namespace std;

#pragma pack(2)  

struct bmp_fileheader   //文件头,长度为14Byte固定  
{  
    unsigned short bfType;  
    unsigned long bfSize;  
    unsigned short bfReserved1;  
    unsigned short bfReserved2;  
    unsigned long bfOffBits;  
};  

struct bmp_infoheader  //文件信息头,长度为40Byte固定  
{  
    unsigned long biSize;  
    unsigned long biWidth;  
    unsigned long biHeight;  
    unsigned short biPlanes;  
    unsigned short biBitCount;  
    unsigned long biCompression;  
    unsigned long biSizeImage;  
    unsigned long biXPelsPerMeter;  
    unsigned long biYPelsPerMeter;  
    unsigned long biClrUsed;  
    unsigned long biClrImportant;  
}; 

struct RGBPallete
{
    unsigned char b;
    unsigned char g;
    unsigned char r;
    unsigned char alpha;
};

CBmp2Jpeg::CBmp2Jpeg() :
    m_quality(JPEG_QUALITY)
{

}

CBmp2Jpeg::~CBmp2Jpeg()
{

}
/*===================================================================================
function:       jpeg压缩
input:          1:生成的文件名,2:bmp的指针,3:位图宽度,4:位图高度,5:颜色深度
return:         int
description:    bmp的像素格式为(RGB)
===================================================================================*/
int CBmp2Jpeg::SaveJpeg(const char *filename, unsigned char *bits, int width, int height, int depth)
{
    FILE * outfile;                 /* target file */
    fopen_s(&outfile, filename, "wb");
    if (outfile == NULL) {
        return -1;
    }

    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    jpeg_stdio_dest(&cinfo, outfile);

    cinfo.image_width = width;      /* image width and height, in pixels */
    cinfo.image_height = height;
    cinfo.input_components = 3;         /* # of color components per pixel */
    cinfo.in_color_space = JCS_RGB;         /* colorspace of input image */

    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, m_quality, TRUE /* limit to baseline-JPEG values */);

    jpeg_start_compress(&cinfo, TRUE);

    JSAMPROW row_pointer[1];        /* pointer to JSAMPLE row[s] */
    int     row_stride;             /* physical row width in image buffer */
    row_stride = width * depth; /* JSAMPLEs per row in image_buffer */

    while (cinfo.next_scanline < cinfo.image_height) {
        //这里我做过修改,由于jpg文件的图像是倒的,所以改了一下读的顺序
        //row_pointer[0] = & bits[cinfo.next_scanline * row_stride];
        row_pointer[0] = & bits[(cinfo.image_height - cinfo.next_scanline - 1) * row_stride];
        (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

    jpeg_finish_compress(&cinfo);
    fclose(outfile);

    jpeg_destroy_compress(&cinfo);
    return 0;
}

void CBmp2Jpeg::InitFileHeader(void *pFile, void *fileHeader)
{
    bmp_fileheader *pfileHeader = (bmp_fileheader *)fileHeader;
    fstream *filein = (fstream *)pFile;
    //初始化文件头
    pfileHeader->bfType = 0;
    pfileHeader->bfSize=0;
    pfileHeader->bfReserved1=0;
    pfileHeader->bfReserved2=0;
    pfileHeader->bfOffBits=0;
    //读位图文件头并输出相应信息
    filein->read((char*)fileHeader, sizeof(bmp_fileheader));
}

void CBmp2Jpeg::InitInfoHeader(void *pFile, void *infoHeader)
{
    bmp_infoheader *pinfoHeader = (bmp_infoheader *)infoHeader;
    fstream *filein = (fstream *)pFile;
    //初始化信息头
    pinfoHeader->biSize=0;
    pinfoHeader->biWidth=0;
    pinfoHeader->biHeight=0;
    pinfoHeader->biPlanes=0;
    pinfoHeader->biBitCount=0;
    pinfoHeader->biCompression=0;
    pinfoHeader->biSizeImage=0;
    pinfoHeader->biXPelsPerMeter=0;
    pinfoHeader->biYPelsPerMeter=0;
    pinfoHeader->biClrUsed=0;
    pinfoHeader->biClrImportant=0;

    //读位图信息头并输出相应信息
    filein->read((char*)infoHeader, sizeof(bmp_infoheader));
}

void CBmp2Jpeg::SaveBmp(void *fileHeader, void *infoHeader, int bitCount, unsigned char *data, const char *savename)
{
    bmp_fileheader *pfileHeader = (bmp_fileheader *)fileHeader;
    bmp_infoheader *pinfoHeader = (bmp_infoheader *)infoHeader;
    //写入文件
    std::string str(savename);
    str.append(".bmp");
    fstream fileout;
    fileout.open(str, std::ios::binary | std::ios::out);
    fileout.write((char*)fileHeader, sizeof(bmp_fileheader));
    fileout.write((char*)infoHeader, sizeof(bmp_infoheader));
    fileout.write((char*)data, sizeof(unsigned char) * pfileHeader->bfSize - pfileHeader->bfOffBits);
    
    fileout.close();
}

//读取并将图片另存为一个新文件, 转换成rgb格式
int CBmp2Jpeg::ReadBmp(const char *bmp, unsigned char **data, int &w, int &h, int &d)
{
    //打开位图文件
    fstream filein;
    filein.open(bmp, std::ios::binary | std::ios::in);
    if(!filein.is_open())
    {
        char clog[256] = {0};
        sprintf_s(clog, sizeof(clog), "bmp转jpeg,找不到 %s\n", bmp);
        OutputDebugStringA(clog);
        return -1;
    }

    //定义变量
    long width=0;
    long height=0;
    long bitCount=0;

    bmp_fileheader  fileHeader;
    bmp_infoheader  infoHeader;

    InitFileHeader(&filein, &fileHeader);

    if (fileHeader.bfType != 0x4d42)
    {
        filein.close();
        return -1;
    }

    InitInfoHeader(&filein, &infoHeader);

    width=infoHeader.biWidth;
    height=infoHeader.biHeight;
    bitCount=infoHeader.biBitCount;
    
    int bitPerLine= ((width * bitCount + 31)  >> 5) << 2;
    int imgSize = abs(height * bitPerLine);
    int imgReal = fileHeader.bfSize - fileHeader.bfOffBits;
    if(imgSize != imgReal)
    {
        char clog[256] = {0};
        sprintf_s(clog, sizeof(clog), "bmp转jpeg,图像尺寸不对\n");
        OutputDebugStringA(clog);
        filein.close();
        return -1;
    }

    if (bitCount == 8)
    {
        std::vector<RGBPallete> palletes;
        unsigned char buf[256 * sizeof(RGBPallete)];

        filein.read((char*)buf, 256 * sizeof(RGBPallete));

        for (int i = 0; i < 256; i++)
        {
            RGBPallete pallete;
            memcpy(&pallete, buf + i * sizeof(RGBPallete), sizeof(RGBPallete));

            palletes.push_back(pallete);
        }

        unsigned char* pTemp = new unsigned char[imgSize];
        filein.read((char*)pTemp, imgSize);

        *data = new unsigned char[width * abs(height) * 4];
        for (int i = 0; i < imgSize; i++)
        {
            RGBPallete& p = palletes[pTemp[i]];
            memcpy((*data) + i * sizeof(RGBPallete), &p, sizeof(RGBPallete));
        }

        bitCount = 32;
        delete pTemp;
    }
    else if (bitCount == 24 || bitCount == 32)
    {
        *data = new unsigned char[imgSize];
        filein.read((char*)(*data), imgSize);
        filein.close();
    }
    else
    {
        filein.close();
        return -1;
    }

    w = width;
    h = height;
    d = bitCount;
    
    return 0;
} 

void CBmp2Jpeg::Bgra2Rgb(const unsigned char *src, int w, int h, int d, unsigned char *dst)
{
    unsigned char* pTempDst = dst;
    for (int i = 0; i < abs(h); i++)
    {
        const unsigned char* pTempSrc = nullptr;
        if (h > 0)
        {
            pTempSrc = src + w * i * d;
        }
        else
        {
            pTempSrc = src + w * abs( i  + h + 1 ) * d;
        }

        for (int j = 0; j < w; j++)
        {
            *(pTempDst) = *(pTempSrc + 2);
            *(pTempDst + 1) = *(pTempSrc + 1);
            *(pTempDst + 2) = *(pTempSrc);
            pTempDst += 3;
            pTempSrc += d;
        }
    }
}


int CBmp2Jpeg::Bmp2Jpeg(const char *bmp, const char *jpeg)
{
    unsigned char *brga = nullptr; //指向位图buffer的全局指针,window下像素格式: BGRA(4个字节)
    int width = 0, height = 0, depth = 0;

    if(ReadBmp(bmp, &brga,width, height, depth) < 0)
    {
        return -1;
    }

    unsigned char *rgb = new unsigned char[width * abs(height) * depth/8];
    Bgra2Rgb(brga, width, height, depth/8, rgb);

    int ret = SaveJpeg(jpeg, rgb, width, abs(height), 3);

    delete [] brga;
    delete [] rgb;
    brga = nullptr;
    rgb = nullptr;
    return ret;
}

调用:

#include <iostream>
#include <stdio.h>
#include "Bmp2Jpeg.h"
using namespace std;
int main()
{
    CBmp2Jpeg bmp;
    bmp.Bmp2Jpeg("111_24.bmp", "lena.jpg");
    cout<<"good job."<<endl;
    cin.get();
    return 0;
}
点赞
收藏
评论区
推荐文章
秃头王路飞 秃头王路飞
5个月前
webpack5手撸vue2脚手架
webpack5手撸vue相信工作个12年的小伙伴们在面试的时候多多少少怕被问到关于webpack方面的知识,本菜鸟最近闲来无事,就尝试了手撸了下vue2的脚手架,第一次发帖实在是没有经验,望海涵。languageJavaScript"name":"vuecliversion2","version":"1.0.0","desc
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
光头强的博客 光头强的博客
5个月前
Java面向对象试题
1、请创建一个Animal动物类,要求有方法eat()方法,方法输出一条语句“吃东西”。创建一个接口A,接口里有一个抽象方法fly()。创建一个Bird类继承Animal类并实现接口A里的方法输出一条有语句“鸟儿飞翔”,重写eat()方法输出一条语句“鸟儿吃虫”。在Test类中向上转型创建b对象,调用eat方法。然后向下转型调用eat()方
刚刚好 刚刚好
5个月前
css问题
1、在IOS中图片不显示(给图片加了圆角或者img没有父级)<div<imgsrc""/</divdiv{width:20px;height:20px;borderradius:20px;overflow:h
小森森 小森森
5个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
晴空闲云 晴空闲云
5个月前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
艾木酱 艾木酱
5个月前
快速入门|使用MemFire Cloud构建React Native应用程序
MemFireCloud是一款提供云数据库,用户可以创建云数据库,并对数据库进行管理,还可以对数据库进行备份操作。它还提供后端即服务,用户可以在1分钟内新建一个应用,使用自动生成的API和SDK,访问云数据库、对象存储、用户认证与授权等功能,可专
Wesley13 Wesley13
1年前
Java处理.tif或.tiff图片
前言Java将图片读取到内存用的是ImageIO,默认可以处理的图片格式如下:ImageIO.getWriterFileSuffixes()//此方法返回可以处理的图片格式数组jpgbmpgifpngwbmpjpeg当要处理其他图片格式时,如tif或tiff则要加入第
Stella981 Stella981
1年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
helloworld_28799839 helloworld_28799839
5个月前
常用知识整理
Javascript判断对象是否为空jsObject.keys(myObject).length0经常使用的三元运算我们经常遇到处理表格列状态字段如status的时候可以用到vue