C#实现同屏的时候,频繁截屏内存并不能很好的释放,所以就打算用C++实现这部分的功能。
这里图片的压缩用到了JpegLib, JpegLib是一个用C编写的jpeg图像压缩免费库,许多应用程序对jepg的支持都依赖于该库。
下载地址:https://www.ijg.org/
官方下载地址:jpegsr9d.zip
百度云链接:https://pan.baidu.com/s/13xhEjxWR9b6Bx7OL6tLTIw 提取码:6owy
使用Vistual studio的命令行工具进入源码文件夹
如图
注意
不要用系统的CMD或者Powershell,无法编译。
源码中复制一份jconfig.vc
,改变后缀变为jconfig.h
执行如下命令
nmake -f makefile.vc
提示找不到win32.mak文件,在C盘搜索这个文件名,没有发现该文件,可以直接下一个
链接:https://pan.baidu.com/s/1y-uAsb5e6KdWFRgY3MxaKg
提取码:kn30
把·win32.mak
放在源码目录下即可(Win10环境下需要下载)
编译成功后再项目下创建libjpeg
文件夹,里面再创建include
和lib
把编辑后的libjpeg.lib
放在lib
目录下,所有的.h
的头文件放在include
下
处理好的文件如下
链接:https://pan.baidu.com/s/1vEjL6CHgFDuqQioLq3dhJw 提取码:qeb3
项目下创建modules
文件夹,把之前的libjpeg
文件夹放进来
项目右键属性
在C/C++
下常规
中的附加包含目录
中添加modules\libjpeg\include
链接器
=>常规
=>附加库文件
中添加modules\libjpeg\lib
链接器
=>输入
=>附加依赖项
中添加libjpeg.lib
这样项目中就可以调用了
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;
}
截屏
ScreenShot.h
#pragma once
bool ScreenShot(const char* szSavePath);
ScreenShot.cpp
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <iostream>
using namespace std;
#pragma warning(disable:4996)
#define TAG_DEV_PLAS 1
#define BITS_PER_PIX 32
#define NO_COLOR_TAB 0
#define UNCMP_RGB 0
#define H_RESOL_0 0
#define V_RESOL_0 0
#define ALL_COLOR 0
#define MUST_ZERO 0
#define TYPE_BMP 0x4D42
#define FILE_HEAD sizeof(BITMAPFILEHEADER)
#define INFO_HEAD sizeof(BITMAPINFOHEADER)
#define HEAD_SIZE sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER)
bool ScreenShot(const char* szSavePath)
{
//显示器屏幕
HDC hCurrScreen = GetDC(NULL);
//创建一个兼容的DC,在内存中表示当前位图的上下文
HDC hCmpDC = CreateCompatibleDC(hCurrScreen);
//宽高
int iScreenWidth = GetDeviceCaps(hCurrScreen, HORZRES);
int iScreenHeight = GetDeviceCaps(hCurrScreen, VERTRES);
//当前屏幕位图
HBITMAP hBmp = CreateCompatibleBitmap(hCurrScreen, iScreenWidth, iScreenHeight);
//用当前位图句柄表示内存中屏幕位图上下文
SelectObject(hCmpDC, hBmp);
//将当前屏幕图像复制到内存中
BOOL ret = BitBlt(hCmpDC, 0, 0, iScreenWidth, iScreenHeight, hCurrScreen, 0, 0, SRCCOPY);
//BMP图像信息头
BITMAPINFOHEADER hBmpInfo;
hBmpInfo.biSize = INFO_HEAD;
hBmpInfo.biWidth = iScreenWidth;
hBmpInfo.biHeight = iScreenHeight;
hBmpInfo.biPlanes = TAG_DEV_PLAS;
hBmpInfo.biClrUsed = NO_COLOR_TAB;
hBmpInfo.biBitCount = BITS_PER_PIX;
hBmpInfo.biSizeImage = UNCMP_RGB;
hBmpInfo.biCompression = BI_RGB;
hBmpInfo.biClrImportant = ALL_COLOR;
hBmpInfo.biXPelsPerMeter = H_RESOL_0;
hBmpInfo.biYPelsPerMeter = V_RESOL_0;
/* * * * * * * * * * * * * * * * * * * *
* Windows按4字节分配内存
* 首先计算每行所需要的bit数,并按4字节对齐
* 对齐后的数据乘4,从DWORD转为BYTE
* 每行实际所占BYTE乘图像列数得到数据源大小
* * * * * * * * * * * * * * * * * * * */
DWORD dwSrcSize = ((iScreenWidth * hBmpInfo.biBitCount + 31) / 32) * 4 * iScreenHeight;
//截图总大小
DWORD dwPicSize = HEAD_SIZE + dwSrcSize;
//BMP图像文件头
BITMAPFILEHEADER hBmpFile;
hBmpFile.bfSize = dwPicSize;
hBmpFile.bfType = TYPE_BMP;
hBmpFile.bfOffBits = HEAD_SIZE;
hBmpFile.bfReserved1 = MUST_ZERO;
hBmpFile.bfReserved2 = MUST_ZERO;
//BMP图像数据源
char* bmpSrc = new char[dwSrcSize];
ZeroMemory(bmpSrc, dwSrcSize);
//检索指定的兼容位图中的所有位元数据
//并复制到指定格式的设备无关位图的缓存中
GetDIBits(hCmpDC, hBmp, 0, (UINT)iScreenHeight, bmpSrc, (BITMAPINFO*)&hBmpInfo, DIB_RGB_COLORS);
//汇总所有数据信息
char* szBmp = new char[dwPicSize];
ZeroMemory(szBmp, dwPicSize);
memcpy(szBmp, (void*)&hBmpFile, FILE_HEAD);
memcpy(szBmp + FILE_HEAD, (void*)&hBmpInfo, INFO_HEAD);
memcpy(szBmp + HEAD_SIZE, bmpSrc, dwSrcSize);
//保存BMP图像
FILE* hFile = fopen(szSavePath, "wb+");
if (nullptr != hFile)
{
size_t count = fwrite(szBmp, 1, dwPicSize, hFile);
fclose(hFile);
}
//释放资源
DeleteObject(hBmp);
DeleteObject(hCmpDC);
ReleaseDC(NULL, hCurrScreen);
delete[] szBmp;
delete[] bmpSrc;
szBmp = nullptr;
bmpSrc = nullptr;
return true;
}
调用
main.cpp
#include <iostream>
#include <stdio.h>
#include "Bmp2Jpeg.h"
#include "ScreenShot.h"
using namespace std;
int main()
{
ScreenShot("D:\\pic\\2.bmp");
CBmp2Jpeg bmp;
bmp.Bmp2Jpeg("D:\\pic\\2.bmp", "D:\\pic\\2.jpeg");
cout << "success." << endl;
cin.get();
return 0;
}
特别注意
生成DLL一定要用Release环境!!! 生成DLL一定要用Release环境!!! 生成DLL一定要用Release环境!!! 否则会依赖的DLL也会用Debug的DLL,在普通用户的环境中是没有这些DLL的。
现在我们要把方法导出
直接在原来的解决方案中添加项目,把之前的.cpp
和.h
复制过来,然后导出方法就行了
screenshot.def
LIBRARY screenshot
EXPORTS
ScreenShot @1,
MyBmp2Jpeg @2,
注意
文件名和
LIBRARY screenshot
都要和项目名保持一致 导出都要导出方法,不要导出类,导出类其他语言不支持,所以我们添加一个导出方法MyBmp2Jpeg
MyBmp2Jpeg.cpp
#include <iostream>
#include <stdio.h>
#include "../Bmp2Jpeg.h"
int MyBmp2Jpeg(const char* bmp, const char* jpeg) {
CBmp2Jpeg mbmp;
mbmp.Bmp2Jpeg(bmp, jpeg);
return 0;
}
同一个解决方案下dll会自动生成在debug目录下,所以dll不用再设置引用
调用方式
#include <iostream>
#include <stdio.h>
#include <Windows.h>
using namespace std;
int main()
{
typedef bool (*ScreenShot)(const char* szSavePath);
typedef int (*MyBmp2Jpeg)(const char* bmp, const char* jpeg);
HINSTANCE hDLL;
ScreenShot mScreenShot;
MyBmp2Jpeg mBmp2Jpeg;
hDLL = LoadLibrary(TEXT("screenshot.dll"));
if (hDLL != 0) {
mScreenShot = (ScreenShot)GetProcAddress(hDLL, "ScreenShot");
mBmp2Jpeg = (MyBmp2Jpeg)GetProcAddress(hDLL, "MyBmp2Jpeg");
mScreenShot("D:\\pic\\003.bmp");
mBmp2Jpeg("D:\\pic\\003.bmp", "D:\\pic\\003.jpeg");
cout << "success." << endl;
cin.get();
FreeLibrary(hDLL);//卸载dll文件;
}
return 0;
}
在VS目录中搜索dumpbin.exe
,添加到环境变量中
dumpbin /exports screenshot.dll
DLL导出类: 优点:导出的类可以被继承,调用层次也清晰,可以保留类的完整特性; 缺点:不能被其它语言调用(包括C语言),封装性并不是太好。
导出函数: 优点:可以被其它语言调用,使用简单,封装性相对较好; 缺点:调用层次不明显,尤其是在导出函数多的情况下,比较混乱,不能出现同名导出函数。
运行库
https://www.microsoft.com/zh-CN/download/details.aspx?id=48145
https://www.microsoft.com/zh-cn/download/details.aspx?id=26999
项目文件夹下创建DLLCPP文件夹,把screenshot.dll
放进来
属性
=>生成事件
=>生成前事件命令行
中添加
xcopy /Y /i /e $(ProjectDir)\DLLCPP $(TargetDir)\
页面中调用
[DllImport("screenshot.dll", EntryPoint = "ScreenShot", CallingConvention = CallingConvention.Cdecl)]
public extern static bool ScreenShot(string szSavePath);
[DllImport("screenshot.dll", EntryPoint = "MyBmp2Jpeg", CallingConvention = CallingConvention.Cdecl)]
public extern static int MyBmp2Jpeg(string bmp, string jpeg);
调用方式
ScreenShot("D:\\pic\\2.bmp");
MyBmp2Jpeg("D:\\pic\\2.bmp", "D:\\pic\\2.jpeg");