工程中有个需求是要求在windows平台下截图某一个窗口,并实现保存成jpeg,实现功能大概是:
1.查找窗口
2.截图得到bitmap数据
3.把bitmap数据保存成jpeg文件
其中bitmap数据保存成jpeg文件这里使用libjpeg来实现,这里记录下编译和使用时遇到的一些坑,首先要下载源码进行编译
1、下载源代码下载地址:http://www.ijg.org/files/,
选择最新版本的windows版本压缩包,进行下载。
2、然后开始编译,这里环境是用vs2019,windows 10 64位编译:
- 2.1.修改文件名jconfig.vc为jconfig.h
- 2.2.在开始菜单中打到x64 Native Tools Command Prompt For VS 2019并打开,然后进入目录输入命令:
nmake -f makefile.vc libjpeg.lib
有可能编译会失败,提示找不到Win32.mak,这是编译环境里找不到这个文件,这个文件一般是在安装的vs的c++编译环境中自带的,如果没有可以下载一个,这里已经文件下载到本地了
3、使用
libjpeg.lib是用c语言开发的,
如果在C++程序里使用,需要用extern “C” { }包含一下。
相关头文件:
extern “C”
{
#include “jpeglib.h”
}
cpp文件中引用编译好的文件:
#pragma comment(lib, "libjpeg/jpeg.lib")
贴出实现bitmap像素保存jpeg函数源码:
//===================================================================================
//function: jpeg压缩
//input: 1:生成的文件名,2:bmp的指针,3:位图宽度,4:位图高度,5:颜色深度
//return: int
//description: bmp的像素格式为(RGB)
//===================================================================================
int savejpeg(const char* filename, unsigned char* bits, int width, int height, int depth)
{
//RGB顺序调整,这里很重要,因为传进来的像素数组是使用CreateDIBSettion分配的,它的顺序是BGR,不是RGB,而jpeg文件是RGB,所以需要进行顺序调整,否则会出现红色变成蓝色,这个网上很多文章都没说到,不了解的小白肯定会遇到这个问题!
/*顺便普及知识
2.DIB注意点:
<1>DIB位图每行数据必须是32bit(4个字节)的整数倍,如果图像数据为Byte型(0~255),即每个像素为一个字节(8bit),这样图 像每行的宽必须是4的整数倍,不足4的整数倍的部分,以0补充。
<2>DIB数据存储顺序是:自左到右,自下到上,逆序存储。即:图像的第一行数据,存在DIB数据部分最后一行,最后一行存在第一行 ,因为显示的时候,DIB位图是,自下而上显示的,即读取的第一行数据,会显示在界面的最后一行,然后,第二行显示在界面倒数第 二行。
<3>调色板顺序为BGR而不是RGB。
*/
unsigned char buf = 0;
unsigned char* tmp = bits;//image_buffer = tmp = (unsigned char*)map + m_buf.offsets[frame];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++)
{
buf = *tmp;
*tmp = *(tmp + 2);
*(tmp + 2) = buf;
tmp += 3;
}
}
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 (0 != fopen_s(&outfile, filename, "wb"))
{
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
指定亮度及色度质量
cinfo.q_scale_factor[0] = jpeg_quality_scaling(100);
cinfo.q_scale_factor[1] = jpeg_quality_scaling(100);
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.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;
}
顺便贴一下截图代码:
int captureRenderProgressWindow(HWND hwnd, LPCSTR filePath)
{
HDC hDC;
HDC MemDC;
BYTE* Data;
HBITMAP hBmp;
BITMAPINFO bi;
RECT rtClientWnd;
::GetClientRect(hwnd, &rtClientWnd);
int width = rtClientWnd.right - rtClientWnd.left;
int height = rtClientWnd.bottom - rtClientWnd.top;
memset(&bi, 0, sizeof(bi));
bi.bmiHeader.biSize = sizeof(BITMAPINFO);
bi.bmiHeader.biWidth = width;
bi.bmiHeader.biHeight = height;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 24;
hDC = GetWindowDC(hwnd);
MemDC = CreateCompatibleDC(hDC);
//创建位图
hBmp = CreateDIBSection(MemDC, &bi, DIB_RGB_COLORS, (void**)& Data, NULL, 0);
SelectObject(MemDC, hBmp);
BitBlt(MemDC, 0, 0, bi.bmiHeader.biWidth, bi.bmiHeader.biHeight, hDC, 0, 0, SRCCOPY);
//int result = SaveBitmapToFile(hBmp, filePath);
// 保存
int result = savejpeg(filePath, Data, width, height, 3);
//int result = colorBitmap(hBmp);
ReleaseDC(NULL, hDC);
DeleteDC(MemDC);
DeleteObject(hBmp);
return result;
}
libjpeg源码已经配好win32.mak编译有问题的可以看下一下这个库,libjpeg已经把win32.mak文件加上,且已经编译好一个libjpeg.lib的64位版本