我希望在以下场景中使用GDI +方法Image::Save()
将DDB保存到文件中:
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, 200, 200) ;
...
//hBitmap is a DDB so I need to pass an HPALETTE
Gdiplus::Bitmap(hBitmap, ???HPALETTE??? ).Save(L"file.png", ...) ;
问题是当位图不是与设备无关的位图时,Bitmap
构造函数会要求HPALETTE
。
我从哪里获得必要的HPALETTE?
随访:
其中一个答案建议将NULL作为HPALETTE
参数传递
这是一个可行的例子。结果是纯黑白图像,所有颜色都丢失了。
#include <windows.h>
#include <gdiplus.h>
int main(){
using namespace Gdiplus ;
GdiplusStartupInput gdiplusStartupInput ;
ULONG_PTR gdiplusToken ;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) ;
CLSID pngEncoder = {0x557cf406, 0x1a04, 0x11d3, {0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e} } ;
HDC dcHndl = CreateCompatibleDC(NULL) ;
HBITMAP hBitmap = CreateCompatibleBitmap(dcHndl, 200, 200) ;
SelectObject(dcHndl, hBitmap) ;
BitBlt(dcHndl, 0,0, 200,200, GetDC(NULL), 0,0, SRCCOPY|CAPTUREBLT) ;
Bitmap(hBitmap, NULL).Save(L"file.png", &pngEncoder) ;
}
答案 0 :(得分:1)
首先(这与你的主要问题无关):
为屏幕截图创建位图时,请勿使用内存直流,因为这会创建单色位图。这是你获得黑白图像的主要原因(在我的电脑上我得到的是黑色图像)。
请勿在其他功能中使用GetDC(0)
。每次调用GetDC
匹配都会匹配ReleaseDC
以避免资源泄漏。
在致电BitBlt
之后,最好从hbitmap
中选择dc
,因为您基本上已经完成了直流绘图。
以下代码适用于Windows 10
int w = 800;
int h = 600;
HDC hdc = GetDC(HWND_DESKTOP);
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, w, h);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
BitBlt(memdc, 0, 0, w, h, hdc, 0, 0, SRCCOPY | CAPTUREBLT);
SelectObject(memdc, oldbmp);
Bitmap(hbitmap, NULL).Save(filename, &pngEncoder);
DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(HWND_DESKTOP, hdc);
返回有关文档的问题:
类型:HPALETTE
如果hbm不是与设备无关的位图(DIB),则处理用于定义位图颜色的GDI调色板。
此外,
不要将GDI位图或当前(或以前)选择的GDI调色板传递给Bitmap :: FromHBITMAP方法。
我发布的代码只服从一条规则,GDI位图当前未被选入设备上下文(但之前已被选中)。
该文档可能适用于旧版Windows。据我所知,MFC的CImage
课程并未遵循所有这些规则。新的计算机显示器都是24位或32位,我不知道如何获得它的调色板。
要按照文档的说明,您可以使用CreateDIBSection
和GetDIBits
将DDB转换为DIB部分。使用hbitmap_dib
中的新DIB部分Bitmap::FromHBITMAP
。这将满足所有条件:hbitmap
是dib,它不是(并且没有)被选入设备上下文。
或者,Gdiplus::Bitmap
还有另一种方法Bitmap::FromBITMAPINFO
。如果没有调色板,则可以使用此代码:
HDC hdc = GetDC(HWND_DESKTOP);
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, w, h);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
BitBlt(memdc, 0, 0, 800, 600, hdc, 0, 0, SRCCOPY | CAPTUREBLT);
SelectObject(memdc, oldbmp);
BITMAP bm;
GetObject(hbitmap, sizeof(bm), &bm);
int size = ((bm.bmWidth * bm.bmBitsPixel + 31) / 32) * 4 * bm.bmHeight;
BITMAPINFO info{ sizeof(info), bm.bmWidth, bm.bmHeight, 1, bm.bmBitsPixel, BI_RGB, size };
std::vector<char> bits(size);
GetDIBits(memdc, hbitmap, 0, bm.bmHeight, &bits[0], &info, DIB_RGB_COLORS);
Bitmap *bitmap = Bitmap::FromBITMAPINFO(&info, &bits[0]);
bitmap->Save(filename, &pngEncoder);
delete bitmap;
DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(HWND_DESKTOP, hdc);
答案 1 :(得分:0)
如CreateCompatibleBitmap所述,如果你正在处理颜色位图,我们也可以假设hDC
是一个非内存设备上下文(因为内存设备上下文只会创建单色位图)和使用的颜色调色板此位图与此设备上下文使用的颜色调色板相同。您可以使用GetCurrentObject
方法进行查询。但Bitmap.Bitmap(HBITMAP, HPALETTE)
constructor州的评论是:
不要将当前(或以前)选择的GDI位图或GDI调色板传递给GDI + Bitmap :: Bitmap构造函数。
因此,您无法直接使用当前设备上下文调色板,而是需要创建它的副本。
/// <returns>
/// Handle to palette currently selected into device context without granting ownership.
/// </returns>
_Check_return_ ::HPALETTE
Fetch_CurrentPalette(_In_ ::HDC const h_dc)
{
assert(h_dc);
::HGDIOBJ const h_palette_object{::GetCurrentObject(h_dc, OBJ_PAL)}; // not owned
assert(h_palette_object);
assert(OBJ_PAL == ::GetObjectType(h_palette_object));
// Perform unchecked conversion of generic GDI object descriptor to GDI palette descriptor.
::HPALETTE h_current_palette{}; // not owned
{
static_assert(sizeof(h_palette_object) == sizeof(h_current_palette), "wat");
::memcpy
(
::std::addressof(h_current_palette)
, ::std::addressof(h_palette_object)
, sizeof(h_current_palette)
);
}
return(h_current_palette);
}
/// <returns>
/// Handle to palette copy with granting ownership.
/// </returns>
_Check_return_ ::HPALETTE
Make_PaletteCopy(_In_ ::HPALETTE const h_palette)
{
assert(h_palette);
::UINT const first_entry_index{};
::UINT entries_count{};
::LPPALETTEENTRY p_entries{};
// Figure out how many entries palette contains.
entries_count = ::GetPaletteEntries(h_palette, first_entry_index, entries_count, p_entries);
assert(1 < entries_count);
assert(entries_count <= ::std::numeric_limits< decltype(LOGPALETTE::palNumEntries) >::max());
// This buffer will hold palette description which contains first PALETTEENTRY as last field.
// followed by the rest of PALETTEENTRY items.
::std::unique_ptr< ::std::uint8_t[] > const p_buffer
{
new ::std::uint8_t[sizeof(::LOGPALETTE) + (sizeof(::PALETTEENTRY) * (entries_count - 1u))]
};
// Perform unchecked conversion of buffer pointer to palette description pointer.
::LOGPALETTE * p_description{};
{
::std::uint8_t * const p_buffer_bytes{p_buffer.get()};
static_assert(sizeof(p_buffer_bytes) == sizeof(p_description), "wat");
::memcpy
(
::std::addressof(p_description)
, ::std::addressof(p_buffer_bytes)
, sizeof(p_description)
);
}
// Copy palette entries into buffer.
p_entries = static_cast< ::LPPALETTEENTRY >(p_description->palPalEntry);
::UINT const copied_entries_count
{
::GetPaletteEntries(h_palette, first_entry_index, entries_count, p_entries)
};
assert(copied_entries_count == entries_count);
// Create palette copy.
p_description->palVersion = 0x300; // magic
p_description->palNumEntries = static_cast< ::WORD >(copied_entries_count);
::HPALETTE const h_copied_palette{::CreatePalette(p_description)}; // owned
assert(h_copied_palette);
return(h_copied_palette);
}
::HPALETTE const hPal{Make_PaletteCopy(Fetch_CurrentPalette(hDC))}; // owned
assert(hPal);
::HBITMAP const hBitmap{::CreateCompatibleBitmap(hDC, 200, 200)}; // owned
assert(hBitmap);
{
::Gdiplus::Bitmap bmp{hBitmap, hPal};
assert(::Gdiplus::Status::Ok == bmp.GetLastStatus());
// Do something...
}
// Delete palette and bitmap after GDI+ bitmap object went out of scope.
if(FALSE == ::DeleteObject(hPal))
{
assert(false);
}
if(FALSE == ::DeleteObject(hBitmap))
{
assert(false);
}
答案 2 :(得分:-2)
您可以传递NULL。示例代码如下。
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
GUID encoder = {};
GetGdiplusEncoderClsid(L"image/png", &encoder); // https://stackoverflow.com/a/5346026/104458
HDC hdc = GetDC(NULL);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, 200, 200);
Bitmap bmp(hBitmap, NULL);
bmp.Save(L"File.png", &encoder);
return 0;
}