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;
}
运行效果:
本文的完整源码(VS2015)
注:如果源码无法运行,可能是你的编译器不是VS2015,因为libjpeg是使用VS2015编译的。
注:libjpeg库编译出来的静态库为jpeg.lib,文中使用的libjpeg.lib只是我将jpeg.lib进行的另命名.
本文难免有所错误,如有问题欢迎留言