Windows 系统将剪贴板图片从DIB格式转换为PNG格式
·
在Windows开发中,经常会遇到从剪贴板获取图片并转换格式的需求。DIB(Device-Independent Bitmap)是Windows剪贴板中常见的图片格式,但为了更高效的存储和传输,我们通常需要将其转换为PNG格式。本文将详细介绍实现方法。
什么是DIB格式?
DIB(Device-Independent Bitmap)是Windows操作系统中用于表示位图图像的标准格式。与设备相关的位图不同,DIB包含了颜色表信息,可以在不同设备上保持一致的显示效果。
剪贴板中的DIB格式通常以CF_DIB或CF_DIBV5格式存在,包含以下信息:
- 位图信息头(BITMAPINFOHEADER或BITMAPV5HEADER)
- 颜色表(可选)
- 位图数据
方法一:使用C++和GDI+实现
这是最原生的Windows API实现方式,适合需要集成到C++应用中的场景。
步骤1:获取剪贴板中的DIB数据
#include <windows.h>
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
HBITMAP GetDIBFromClipboard() {
if (!OpenClipboard(NULL)) {
return NULL;
}
HANDLE hData = GetClipboardData(CF_DIB);
if (hData == NULL) {
CloseClipboard();
return NULL;
}
BITMAPINFO* pBMI = (BITMAPINFO*)GlobalLock(hData);
if (pBMI == NULL) {
CloseClipboard();
return NULL;
}
// 创建DIB位图
HDC hDC = GetDC(NULL);
BYTE* pBits;
HBITMAP hBitmap = CreateDIBSection(hDC, pBMI, DIB_RGB_COLORS, (void**)&pBits, NULL, 0);
if (hBitmap) {
// 复制位图数据
int headerSize = pBMI->bmiHeader.biSize;
int colorTableSize = 0;
if (pBMI->bmiHeader.biBitCount <= 8) {
colorTableSize = pBMI->bmiHeader.biClrUsed * sizeof(RGBQUAD);
if (colorTableSize == 0) {
colorTableSize = (1 << pBMI->bmiHeader.biBitCount) * sizeof(RGBQUAD);
}
}
BYTE* pSrcBits = (BYTE*)pBMI + headerSize + colorTableSize;
int dataSize = pBMI->bmiHeader.biSizeImage;
if (dataSize == 0) {
int width = pBMI->bmiHeader.biWidth;
int height = abs(pBMI->bmiHeader.biHeight);
int bytesPerLine = ((width * pBMI->bmiHeader.biBitCount + 31) / 32) * 4;
dataSize = bytesPerLine * height;
}
CopyMemory(pBits, pSrcBits, dataSize);
}
GlobalUnlock(hData);
ReleaseDC(NULL, hDC);
CloseClipboard();
return hBitmap;
}
步骤2:将HBITMAP保存为PNG
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
GetImageEncodersSize(&num, &size);
if (size == 0) {
return -1; // Failure
}
ImageCodecInfo* pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL) {
return -1; // Failure
}
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j) {
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
bool SaveHBITMAPToPNG(HBITMAP hBitmap, const wchar_t* filename) {
// 初始化GDI+
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// 创建Bitmap对象
Bitmap* bitmap = Bitmap::FromHBITMAP(hBitmap, NULL);
if (bitmap == NULL) {
GdiplusShutdown(gdiplusToken);
return false;
}
// 获取PNG编码器CLSID
CLSID pngClsid;
int result = GetEncoderClsid(L"image/png", &pngClsid);
if (result == -1) {
delete bitmap;
GdiplusShutdown(gdiplusToken);
return false;
}
// 保存为PNG
Status status = bitmap->Save(filename, &pngClsid, NULL);
delete bitmap;
GdiplusShutdown(gdiplusToken);
return (status == Ok);
}
步骤3:完整调用示例
int main() {
// 获取剪贴板DIB
HBITMAP hBitmap = GetDIBFromClipboard();
if (hBitmap == NULL) {
MessageBox(NULL, L"剪贴板中没有DIB格式的图片", L"错误", MB_OK);
return 1;
}
// 保存为PNG
if (SaveHBITMAPToPNG(hBitmap, L"clipboard_image.png")) {
MessageBox(NULL, L"PNG图片保存成功!", L"成功", MB_OK);
} else {
MessageBox(NULL, L"PNG图片保存失败!", L"错误", MB_OK);
}
DeleteObject(hBitmap);
return 0;
}
方法二:使用C#和.NET实现
如果你使用的是C#,实现会更加简洁:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
class ClipboardImageConverter {
public static void ConvertDIBToPNG(string outputPath) {
// 检查剪贴板是否有图片
if (Clipboard.ContainsImage()) {
// 获取剪贴板图片
Image image = Clipboard.GetImage();
// 保存为PNG
image.Save(outputPath, ImageFormat.Png);
Console.WriteLine("PNG图片保存成功!");
} else {
Console.WriteLine("剪贴板中没有图片!");
}
}
}
方法三:使用Python实现
Python可以使用Pillow库和pywin32库实现:
import win32clipboard
from PIL import Image
import io
def dib_to_png(output_path):
try:
# 打开剪贴板
win32clipboard.OpenClipboard()
# 检查是否有DIB格式
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_DIB):
# 获取DIB数据
data = win32clipboard.GetClipboardData(win32clipboard.CF_DIB)
# 使用Pillow处理
image = Image.open(io.BytesIO(data))
image.save(output_path, 'PNG')
print(f"PNG图片已保存到: {output_path}")
else:
print("剪贴板中没有DIB格式的图片")
except Exception as e:
print(f"错误: {e}")
finally:
win32clipboard.CloseClipboard()
# 使用示例
dib_to_png("clipboard_output.png")
注意事项
- 剪贴板访问权限:确保在访问剪贴板前没有其他程序正在占用
- GDI+初始化:使用C++方案时,别忘了初始化和关闭GDI+
- 资源释放:正确释放HBITMAP等GDI对象,避免内存泄漏
- DIB格式变体:注意处理
CF_DIBV5等扩展格式 - PNG压缩:可以根据需要调整PNG压缩级别
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)