前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++屏幕截图 图片转JPEG

C++屏幕截图 图片转JPEG

作者头像
码客说
发布2021-07-13 11:22:17
2.2K1
发布2021-07-13 11:22:17
举报
文章被收录于专栏:码客码客

前言

C#实现同屏的时候,频繁截屏内存并不能很好的释放,所以就打算用C++实现这部分的功能。

这里图片的压缩用到了JpegLib, JpegLib是一个用C编写的jpeg图像压缩免费库,许多应用程序对jepg的支持都依赖于该库。

编译库

下载

下载地址:https://www.ijg.org/

官方下载地址:jpegsr9d.zip

百度云链接:https://pan.baidu.com/s/13xhEjxWR9b6Bx7OL6tLTIw 提取码:6owy

编译

使用Vistual studio的命令行工具进入源码文件夹

如图

image-20210708142807989
image-20210708142807989

注意

不要用系统的CMD或者Powershell,无法编译。

源码中复制一份jconfig.vc,改变后缀变为jconfig.h

执行如下命令

代码语言:javascript
复制
nmake -f makefile.vc

提示找不到win32.mak文件,在C盘搜索这个文件名,没有发现该文件,可以直接下一个

链接:https://pan.baidu.com/s/1y-uAsb5e6KdWFRgY3MxaKg 提取码:kn30 把·win32.mak放在源码目录下即可(Win10环境下需要下载)

编译成功后再项目下创建libjpeg文件夹,里面再创建includelib

把编辑后的libjpeg.lib放在lib目录下,所有的.h的头文件放在include

处理好的文件如下

链接:https://pan.baidu.com/s/1vEjL6CHgFDuqQioLq3dhJw 提取码:qeb3

项目下引用

项目下创建modules文件夹,把之前的libjpeg文件夹放进来

项目右键属性

C/C++常规中的附加包含目录中添加modules\libjpeg\include

image-20210708144103726
image-20210708144103726

链接器=>常规=>附加库文件中添加modules\libjpeg\lib

image-20210708144357439
image-20210708144357439

链接器=>输入=>附加依赖项中添加libjpeg.lib

image-20210708144602929
image-20210708144602929

这样项目中就可以调用了

Bmp2Jpeg.h

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#pragma once
bool ScreenShot(const char* szSavePath);

ScreenShot.cpp

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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方法

特别注意

生成DLL一定要用Release环境!!! 生成DLL一定要用Release环境!!! 生成DLL一定要用Release环境!!! 否则会依赖的DLL也会用Debug的DLL,在普通用户的环境中是没有这些DLL的。

现在我们要把方法导出

直接在原来的解决方案中添加项目,把之前的.cpp.h复制过来,然后导出方法就行了

screenshot.def

代码语言:javascript
复制
LIBRARY screenshot
EXPORTS
ScreenShot @1,
MyBmp2Jpeg @2,

注意

文件名和LIBRARY screenshot都要和项目名保持一致 导出都要导出方法,不要导出类,导出类其他语言不支持,所以我们添加一个导出方法MyBmp2Jpeg

MyBmp2Jpeg.cpp

代码语言:javascript
复制
#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不用再设置引用

调用方式

代码语言:javascript
复制
#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,添加到环境变量中

代码语言:javascript
复制
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

C#调用DLL方法

项目文件夹下创建DLLCPP文件夹,把screenshot.dll放进来

属性=>生成事件=>生成前事件命令行中添加

代码语言:javascript
复制
xcopy /Y /i /e $(ProjectDir)\DLLCPP $(TargetDir)\

页面中调用

代码语言:javascript
复制
[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);

调用方式

代码语言:javascript
复制
ScreenShot("D:\\pic\\2.bmp");
MyBmp2Jpeg("D:\\pic\\2.bmp", "D:\\pic\\2.jpeg");
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-07-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 编译库
    • 下载
      • 编译
        • 项目下引用
        • 附录
        • 查看DLL方法
        • C#调用DLL方法
        相关产品与服务
        命令行工具
        腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档