截图并使用libjpeg库压缩BMP为JPG与将JPG转换为BMP

libjpeg库下载地址
使用VS2015编译好的libjpeg库(如果你不是使用的VS2015编译器,可能无法使用,建议自己重新编译)
关于自身如何编译libjpeg库

近来有朋友做远程一块,需要对屏幕截屏的图片进行压缩,用以保证传输的效率。于是自身也帮忙查找了些相关资料,实现了对截屏后的BMP图片进行压缩为JPG格式和将JPG转换为BMP格式的操作。避免大家也碰壁,拿出来和大家分享。
源码

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
extern "C" {
    #include "./jpeg_lib/jpeglib.h"
}
#pragma comment(lib,"./jpeg_lib/libjpeg.lib")

/*
** 压缩 BMP 图片为 JPG 图片
** 如果要进行对 JPG 图片的清晰度的调整,调宏 JPEG_QUALITY 的值即可,越大越清晰
*/
#define JPEG_QUALITY 80 // 根据这个值,来调整.jpg画质的清晰度
int CompressBMPtoJPG(char *filename, unsigned char *bits, int width, int height, int depth)
{
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;
    FILE * outfile;                 //target file   
    JSAMPROW row_pointer[1];        //pointer to JSAMPLE row[s]   
    int     row_stride;             //physical row width in image buffer   
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    if ((outfile = fopen(filename, "wb")) == NULL)
    {
        fprintf(stderr, "can't open %s/n", filename);
        return -1;
    }
    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, JPEG_QUALITY, TRUE);//limit to baseline-JPEG values   
    jpeg_start_compress(&cinfo, TRUE);

    row_stride = width * depth; // JSAMPLEs per row in image_buffer   
    while (cinfo.next_scanline < cinfo.image_height)
    {
        //这里我做过修改,由于jpg文件的图像是倒的,所以改了一下读的顺序  
        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 ScreenShot(BITMAP & bitmap, HDC & hdc, HBITMAP & hBitmap,int & nWidth, int & nHeight)
{
    HDC hMemdc;
    HBITMAP hOldBitmap;
    // 获取屏幕的宽和高
    nWidth = GetSystemMetrics(SM_CXSCREEN);
    nHeight = GetSystemMetrics(SM_CYSCREEN);

    hdc = CreateDCA("DISPLAY", 0, 0, 0);
    hMemdc = CreateCompatibleDC(hdc);
    hBitmap = CreateCompatibleBitmap(hdc, nWidth, nHeight);
    hOldBitmap = (HBITMAP)SelectObject(hMemdc, hBitmap);
    BitBlt(hMemdc, 0, 0, nWidth, nHeight, hdc, 0, 0, SRCCOPY);

    GetObject(hBitmap, sizeof(BITMAP), &bitmap);

    DeleteDC(hMemdc);
}

/*
** 将截图放入粘贴板中
*/
void CopyScreenshotToClipbord(HBITMAP hBitmap)
{
    OpenClipboard(NULL);//打开剪切板
    EmptyClipboard();
    SetClipboardData(CF_BITMAP, hBitmap);
    CloseClipboard();
}

/*
** 保存截图为 bmp 格式
*/
void SaveScreenshotBMP(char* filename, HDC hdc, BITMAP bitmap, HBITMAP hBitmap, char* & pBitmapData)
{
    BITMAPFILEHEADER bitmapFileHeader = { 0 };
    BITMAPINFOHEADER bitmapInfoHeader = { 0 };
    DWORD bitmapSize = bitmap.bmWidth * bitmap.bmHeight * 4;

    // 填充文件头
    bitmapFileHeader.bfType = 0x4D42;
    bitmapFileHeader.bfReserved1 = 0;
    bitmapFileHeader.bfReserved2 = 0;
    bitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    bitmapFileHeader.bfSize = bitmapSize + bitmapFileHeader.bfOffBits;
    // 填充信息头
    bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
    bitmapInfoHeader.biHeight = bitmap.bmHeight;
    bitmapInfoHeader.biWidth = bitmap.bmWidth;
    bitmapInfoHeader.biPlanes = 1;
    bitmapInfoHeader.biBitCount = bitmap.bmBitsPixel;// 32真彩高清
    bitmapInfoHeader.biCompression = BI_RGB;
    bitmapInfoHeader.biSizeImage = bitmapSize;
    bitmapInfoHeader.biXPelsPerMeter = 0;
    bitmapInfoHeader.biYPelsPerMeter = 0;
    bitmapInfoHeader.biClrUsed = 0;
    bitmapInfoHeader.biClrImportant = 0;
    GetDIBits(hdc, hBitmap, 0, bitmap.bmHeight, pBitmapData, (LPBITMAPINFO)&bitmapInfoHeader, DIB_RGB_COLORS);

    // 输出截图的文件到 “截图.bmp”
    DWORD dwSize = 0;
    HANDLE hFile = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    WriteFile(hFile, (void*)&bitmapFileHeader, sizeof(BITMAPFILEHEADER), &dwSize, 0);
    WriteFile(hFile, (void*)&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), &dwSize, 0);
    WriteFile(hFile, (void*)pBitmapData, bitmapSize, &dwSize, 0);
    CloseHandle(hFile);
}


/*
** 写 bmp 格式的文件头
*/
void write_bmp_header(j_decompress_ptr cinfo, FILE* output_file)
{
    BITMAPFILEHEADER bfh;
    BITMAPINFOHEADER bih;

    unsigned long width;
    unsigned long height;
    unsigned short depth;
    unsigned long headersize;
    unsigned long filesize;

    width = cinfo->output_width;
    height = cinfo->output_height;
    depth = cinfo->output_components;

    if (depth == 1)
    {
        headersize = 14 + 40 + 256 * 4;
        filesize = headersize + width*height;
    }

    if (depth == 3)
    {
        headersize = 14 + 40;
        filesize = headersize + width*height*depth;
    }

    memset(&bfh, 0, sizeof(BITMAPFILEHEADER));
    memset(&bih, 0, sizeof(BITMAPINFOHEADER));

    //写入比较关键的几个bmp头参数
    bfh.bfType = 0x4D42;
    bfh.bfSize = filesize;
    bfh.bfOffBits = headersize;

    bih.biSize = 40;
    bih.biWidth = width;
    bih.biHeight = height;
    bih.biPlanes = 1;
    bih.biBitCount = (unsigned short)depth * 8;
    bih.biSizeImage = width*height*depth;

    fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, output_file);
    fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, output_file);

    if (depth == 1)        //灰度图像要添加调色板
    {
        unsigned char *platte;
        platte = new unsigned char[256 * 4];
        unsigned char j = 0;
        for (int i = 0; i < 1024; i += 4)
        {
            platte[i] = j;
            platte[i + 1] = j;
            platte[i + 2] = j;
            platte[i + 3] = 0;
            j++;
        }
        fwrite(platte, sizeof(unsigned char) * 1024, 1, output_file);
        delete[] platte;
    }
}

/*
** 写 bmp 格式的数据
*/
void write_bmp_data(j_decompress_ptr cinfo, unsigned char *src_buff, FILE * output_file)
{
    unsigned char *dst_width_buff;
    unsigned char *point;

    unsigned long width;
    unsigned long height;
    unsigned short depth;

    width = cinfo->output_width;
    height = cinfo->output_height;
    depth = cinfo->output_components;

    dst_width_buff = new unsigned char[width*depth];
    memset(dst_width_buff, 0, sizeof(unsigned char)*width*depth);

    point = src_buff + width*depth*(height - 1);    //倒着写数据,bmp格式是倒的,jpg是正的
    for (unsigned long i = 0; i < height; i++)
    {
        for (unsigned long j = 0; j < width*depth; j += depth)
        {
            if (depth == 1)        //处理灰度图
            {
                dst_width_buff[j] = point[j];
            }

            if (depth == 3)        //处理彩色图
            {
                dst_width_buff[j + 2] = point[j + 0];
                dst_width_buff[j + 1] = point[j + 1];
                dst_width_buff[j + 0] = point[j + 2];
            }
        }
        point -= width*depth;
        fwrite(dst_width_buff, sizeof(unsigned char)*width*depth, 1, output_file);    //一次写一行
    }
}

/*
** 转(解压缩) jpg 为 bmp 格式
*/
void DeCompressJPGtoBMP(FILE* input_file,FILE* output_file)
{
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    JSAMPARRAY buffer;
    unsigned char *src_buff;
    unsigned char *point;

    cinfo.err = jpeg_std_error(&jerr);    //一下为libjpeg函数,具体参看相关文档
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo, input_file);
    jpeg_read_header(&cinfo, TRUE);
    jpeg_start_decompress(&cinfo);

    unsigned long width = cinfo.output_width;
    unsigned long height = cinfo.output_height;
    unsigned short depth = cinfo.output_components;

    src_buff = new unsigned char[width*height*depth];
    memset(src_buff, 0, sizeof(unsigned char)*width*height*depth);

    buffer = (*cinfo.mem->alloc_sarray)
        ((j_common_ptr)&cinfo, JPOOL_IMAGE, width*depth, 1);

    point = src_buff;
    while (cinfo.output_scanline < height)
    {
        jpeg_read_scanlines(&cinfo, buffer, 1);    //读取一行jpg图像数据到buffer
        memcpy(point, *buffer, width*depth);    //将buffer中的数据逐行给src_buff
        point += width*depth;            //一次改变一行
    }

    write_bmp_header(&cinfo,output_file);            //写bmp文件头
    write_bmp_data(&cinfo, src_buff, output_file);    //写bmp像素数据

    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    delete[] src_buff;
}


int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
    /************************************************************************/
    /* 测试截图并将bmp压缩为jpg                                               */
    /************************************************************************/
    HDC hdc = 0;
    HBITMAP hBitmap = 0;
    BITMAP bitmap = { 0 };
    int nWidth = 0;
    int nHeight = 0;
    // 1、截屏
    ScreenShot(bitmap, hdc, hBitmap, nWidth, nHeight);

    // 2、保存截屏为bmp
    DWORD bitmapSize = bitmap.bmWidth * bitmap.bmHeight * 4;
    char* pBitmapData = new char[bitmapSize];
    ZeroMemory(pBitmapData, bitmapSize);
    SaveScreenshotBMP("截图.bmp", hdc, bitmap, hBitmap, pBitmapData);

    // 3、将文件写入剪切板中(可以通过粘贴到文档中实现)
    CopyScreenshotToClipbord(hBitmap);

    // 4、将BMP图压缩为jpg格式
    // 由于window的位图的像素格式是:BGRA,4个字节,jpeglib库使用的是RGB,3个字节的格式,所以需要将源像素去掉其透明字节,同时改变RGB的顺序。
    for (int i = 0, j = 0; j < nWidth*nHeight * 4; i += 3, j += 4)
    {
        *(pBitmapData + i) = *(pBitmapData + j + 2);
        *(pBitmapData + i + 1) = *(pBitmapData + j + 1);
        *(pBitmapData + i + 2) = *(pBitmapData + j);
    }
    CompressBMPtoJPG("截图.jpg", (unsigned char*)pBitmapData, nWidth, nHeight, 3);

    // 5、释放DC
    DeleteDC(hdc);

    /************************************************************************/
    /* 测试将 jpg 格式解压缩为 bmp 格式                                       */
    /************************************************************************/
    FILE* inputFile;
    FILE* outputFile;
    inputFile = fopen("截图.jpg", "rb");
    outputFile = fopen("解压缩.bmp", "wb");
    DeCompressJPGtoBMP(inputFile, outputFile);
    fclose(inputFile);
    fclose(outputFile);

    return 0;
}

运行效果:
截图并使用libjpeg库压缩BMP为JPG与将JPG转换为BMP_第1张图片
本文的完整源码(VS2015)
注:如果源码无法运行,可能是你的编译器不是VS2015,因为libjpeg是使用VS2015编译的。
注:libjpeg库编译出来的静态库为jpeg.lib,文中使用的libjpeg.lib只是我将jpeg.lib进行的另命名.

本文难免有所错误,如有问题欢迎留言

你可能感兴趣的:(音视频)