Сохранить HBITMAP в *.bmp-файл, использующий только Win32
У меня есть HBITMAP в моем чистом проекте Win32 (внешние библиотеки не используются). Могу ли я экспортировать его в *.bmp-файл, использующий только функции Winapi и/или CRT, поэтому я не несу никаких дополнительных зависимостей?
4 ответов
нет API для сохранения в файл напрямую, потому что, как правило, наличие растрового дескриптора не означает, что у вас есть прямой доступ к растровым данным. Ваше решение-скопировать растровое изображение в другое растровое изображение с доступом к данным (DIB), а затем использовать его для записи в файл.
вы обычно либо создаете другое растровое изображение, используя CreateDIBSection
, или вы получаете растровые данные с GetDIBits
.
CreateFile
, WriteFile
записывает данные в файл.
вы пишете: BITMAPFILEHEADER
, потом BITMAPINFOHEADER
, тогда палитра (которой у вас обычно нет, когда бит/пиксель превышает 8), а затем сами данные.
Читайте также:
- C++: Hbitmap/BITMAP В.файл bmp (ответ)
- экономия .bmp-файл с использованием hBitmap = CreateDIBSection () в C Win32
- сохранение изображения на MSDN
Код
это код из статьи MSDN (Примечание что вам нужно определить
Я оставлю это самодостаточное доказательство концепции здесь, так как мне, вероятно, нужно будет посмотреть его позже, так как это не очевидно. Он делает скриншот окна рабочего стола и сохраняет его в растровое изображение.bmp:
#include <Windows.h>
#include <stdio.h>
#include <assert.h>
/* forward declarations */
PBITMAPINFO CreateBitmapInfoStruct(HBITMAP);
void CreateBMPFile(LPTSTR pszFile, HBITMAP hBMP);
int main(int argc, char **argv);
PBITMAPINFO CreateBitmapInfoStruct(HBITMAP hBmp)
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;
// Retrieve the bitmap color format, width, and height.
assert(GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp));
// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;
// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)
if (cClrBits < 24)
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * (1<< cClrBits));
// There is no RGBQUAD array for these formats: 24-bit-per-pixel or 32-bit-per-pixel
else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER));
// Initialize the fields in the BITMAPINFO structure.
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;
// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
// The width must be DWORD aligned unless the bitmap is RLE
// compressed.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8
* pbmi->bmiHeader.biHeight;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;
return pbmi;
}
void CreateBMPFile(LPTSTR pszFile, HBITMAP hBMP)
{
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;
PBITMAPINFO pbi;
HDC hDC;
hDC = CreateCompatibleDC(GetWindowDC(GetDesktopWindow()));
SelectObject(hDC, hBMP);
pbi = CreateBitmapInfoStruct(hBMP);
pbih = (PBITMAPINFOHEADER) pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
assert(lpBits) ;
// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
assert(GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi,
DIB_RGB_COLORS));
// Create the .BMP file.
hf = CreateFile(pszFile,
GENERIC_READ | GENERIC_WRITE,
(DWORD) 0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
assert(hf != INVALID_HANDLE_VALUE) ;
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof (RGBQUAD);
// Copy the BITMAPFILEHEADER into the .BMP file.
assert(WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
(LPDWORD) &dwTmp, NULL));
// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
assert(WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
+ pbih->biClrUsed * sizeof (RGBQUAD),
(LPDWORD) &dwTmp, ( NULL)));
// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
assert(WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL));
// Close the .BMP file.
assert(CloseHandle(hf));
// Free memory.
GlobalFree((HGLOBAL)lpBits);
}
int main(int argc, char **argv)
{
HWND hwnd;
HDC hdc[2];
HBITMAP hbitmap;
RECT rect;
hwnd = GetDesktopWindow();
GetClientRect(hwnd, &rect);
hdc[0] = GetWindowDC(hwnd);
hbitmap = CreateCompatibleBitmap(hdc[0], rect.right, rect.bottom);
hdc[1] = CreateCompatibleDC(hdc[0]);
SelectObject(hdc[1], hbitmap);
BitBlt (
hdc[1],
0,
0,
rect.right,
rect.bottom,
hdc[0],
0,
0,
SRCCOPY
);
CreateBMPFile("bitmap.bmp", hbitmap);
return 0;
}
Да, это возможно, используя Компонент Windows Imaging (WIC). WIC предлагает встроенные кодеры, так что вам не нужно вручную записывать заголовки растровых изображений и данные. Он также позволяет выбрать другой кодировщик (например, PNG), изменяя всего лишь одну строку кода.
процесс довольно прямо вперед. Он состоит из следующих шагов:
- получить свойства из источника
HBITMAP
используяGetObject
(размеры, глубина). - создать
IWICImagingFactory
экземпляра. - создать
IWICBitmap
экземплярHBITMAP
(IWICImagingFactory::CreateBitmapFromHBITMAP
). - создать
IWICStream
экземпляр (IWICImagingFactory::CreateStream
), и прикрепить его к файлу (IWICStream::InitializeFromFilename
). - создать
IWICBitmapEncoder
экземпляр (IWICImagingFactory::CreateEncoder
), и связать его с потоком (IWICBitmapEncoder::Initialize
). - создать
IWICBitmapFrameEncode
экземпляр (IWICBitmapEncoder::CreateNewFrame
), и инициализировать его в соответствии с ИсточникHBITMAP
(IWICBitmapFrameEncode::Initialize
,IWICBitmapFrameEncode::SetSize
,IWICBitmapFrameEncode::SetPixelFormat
). - запись растровых данных в кадр (
IWICBitmapFrameEncode::WriteSource
). - зафиксировать кадр и данные в потоке (
IWICBitmapFrameEncode::Commit
,IWICBitmapEncoder::Commit
).
переведен на код:
#define COBJMACROS
#include <Objbase.h>
#include <wincodec.h>
#include <Windows.h>
#include <Winerror.h>
#pragma comment(lib, "Windowscodecs.lib")
HRESULT WriteBitmap(HBITMAP bitmap, const wchar_t* pathname) {
HRESULT hr = S_OK;
// (1) Retrieve properties from the source HBITMAP.
BITMAP bm_info = { 0 };
if (!GetObject(bitmap, sizeof(bm_info), &bm_info))
hr = E_FAIL;
// (2) Create an IWICImagingFactory instance.
IWICImagingFactory* factory = NULL;
if (SUCCEEDED(hr))
hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
&IID_IWICImagingFactory, &factory);
// (3) Create an IWICBitmap instance from the HBITMAP.
IWICBitmap* wic_bitmap = NULL;
if (SUCCEEDED(hr))
hr = IWICImagingFactory_CreateBitmapFromHBITMAP(factory, bitmap, NULL,
WICBitmapIgnoreAlpha,
&wic_bitmap);
// (4) Create an IWICStream instance, and attach it to a filename.
IWICStream* stream = NULL;
if (SUCCEEDED(hr))
hr = IWICImagingFactory_CreateStream(factory, &stream);
if (SUCCEEDED(hr))
hr = IWICStream_InitializeFromFilename(stream, pathname, GENERIC_WRITE);
// (5) Create an IWICBitmapEncoder instance, and associate it with the stream.
IWICBitmapEncoder* encoder = NULL;
if (SUCCEEDED(hr))
hr = IWICImagingFactory_CreateEncoder(factory, &GUID_ContainerFormatBmp, NULL,
&encoder);
if (SUCCEEDED(hr))
hr = IWICBitmapEncoder_Initialize(encoder, (IStream*)stream,
WICBitmapEncoderNoCache);
// (6) Create an IWICBitmapFrameEncode instance, and initialize it
// in compliance with the source HBITMAP.
IWICBitmapFrameEncode* frame = NULL;
if (SUCCEEDED(hr))
hr = IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL);
if (SUCCEEDED(hr))
hr = IWICBitmapFrameEncode_Initialize(frame, NULL);
if (SUCCEEDED(hr))
hr = IWICBitmapFrameEncode_SetSize(frame, bm_info.bmWidth, bm_info.bmHeight);
if (SUCCEEDED(hr)) {
GUID pixel_format = GUID_WICPixelFormat24bppBGR;
hr = IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format);
}
// (7) Write bitmap data to the frame.
if (SUCCEEDED(hr))
hr = IWICBitmapFrameEncode_WriteSource(frame, (IWICBitmapSource*)wic_bitmap,
NULL);
// (8) Commit frame and data to stream.
if (SUCCEEDED(hr))
hr = IWICBitmapFrameEncode_Commit(frame);
if (SUCCEEDED(hr))
hr = IWICBitmapEncoder_Commit(encoder);
// Cleanup
if (frame)
IWICBitmapFrameEncode_Release(frame);
if (encoder)
IWICBitmapEncoder_Release(encoder);
if (stream)
IWICStream_Release(stream);
if (wic_bitmap)
IWICBitmap_Release(wic_bitmap);
if (factory)
IWICImagingFactory_Release(factory);
return hr;
}
вот товарищ тестовое приложение для демонстрации использования. Убедитесь, что #define OEMRESOURCE
до включения любых системных заголовков, чтобы разрешить использование OBM_
картинки.
int wmain(int argc, wchar_t** argv) {
HRESULT hr = S_OK;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(hr))
return -1;
HBITMAP bitmap = LoadImage(NULL, MAKEINTRESOURCE(OBM_ZOOM), IMAGE_BITMAP, 0, 0,
LR_DEFAULTCOLOR);
hr = WriteBitmap(bitmap, argv[1]);
// Cleanup
if (bitmap)
DeleteObject(bitmap);
CoUninitialize();
return 0;
}
это загрузит предоставленное системой растровое изображение и сохранит его в путь, указанный в качестве аргумента в командной строке.
ограничения:
- нет поддержки alpha каналы. Хотя растровые изображения версии 5 поддерживают Альфа-каналы, я не знаю никакого способа узнать, является ли
HBITMAP
относится к растровому изображению с альфа-каналом, и я не знаю, как определить, является ли он предварительно умноженным. Если вы хотите поддерживать альфа-канал, ставьEnableV5Header32bppBGRA
свойствоVARIANT_TRUE
(см. формат BMP: кодировка). - нет поддержки растровых изображений на паллетах (bpp HPALETTE в вызове
IWICImagingFactory::CreateBitmapFromHBITMAP
. - кодировщик инициализируется с помощью
GUID_WICPixelFormat24bppBGR
константа формата пиксела. Более универсальная реализация выведет совместимый формат пикселей из источникаHBITMAP
.
еще один минималистичный вариант-использовать Оле IPicture
. Это всегда было вокруг, все еще часть Win32 API:
#define _S(exp) (([](HRESULT hr) { if (FAILED(hr)) _com_raise_error(hr); return hr; })(exp));
PICTDESC pictdesc = {};
pictdesc.cbSizeofstruct = sizeof(pictdesc);
pictdesc.picType = PICTYPE_BITMAP;
pictdesc.bmp.hbitmap = hBitmap;
CComPtr<IPicture> picture;
_S( OleCreatePictureIndirect(&pictdesc, __uuidof(IPicture), FALSE, (LPVOID*)&picture) );
// Save to a stream
CComPtr<IStream> stream;
_S( CreateStreamOnHGlobal(NULL, TRUE, &stream) );
LONG cbSize = 0;
_S( picture->SaveAsFile(stream, TRUE, &cbSize) );
// Or save to a file
CComPtr<IPictureDisp> disp;
_S( picture->QueryInterface(&disp) );
_S( OleSavePictureFile(disp, CComBSTR("C:\Temp\File.bmp")) );