Как построить объект GDI + Bitmap из зависимого от устройства HBITMAP

Я хочу использовать метод 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

Решение

Во-первых (и это не связано с вашим основным вопросом):

При создании растрового изображения для снимка экрана не используйте память постоянного тока, потому что это создает монохромное растровое изображение. Это основная причина, по которой вы получаете черно-белое изображение (на моем компьютере я просто получаю черное изображение).

Не использовать GetDC(0) внутри другой функции. Каждый звонок GetDC совпадение ReleaseDC чтобы избежать утечки ресурсов.

После звонка BitBlt это хорошая практика, чтобы выбрать hbitmap снаружи dc потому что вы в основном закончили рисовать на 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
Дескриптор палитры GDI, используемой для определения цветов растрового изображения, если hbm не является независимым от устройства растровым изображением (DIB).

К тому же,

Не передавайте в метод Bitmap :: FromHBITMAP растровое изображение GDI или палитру GDI, которые в настоящее время (или ранее) были выбраны в контексте устройства.

Размещенный мною код подчиняется только одному правилу: в настоящее время растровое изображение GDI не выбрано в контексте устройства (но оно было выбрано ранее).

Документация может относиться к более старым версиям Windows. Насколько я могу видеть МФЦ CImage Класс не следует всем этим правилам. Новые компьютерные дисплеи все 24 или 32 бит, я не знаю, как вы получите палитру для этого.

Чтобы следовать документации к письму, вы можете конвертировать DDB в раздел DIB, используя CreateDIBSection а также GetDIBits, Используйте новый раздел DIB hbitmap_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

Другие решения

Как CreateCompatibleBitmap замечания, если вы имеете дело с цветными растровыми изображениями, мы также можем предположить, что hDC является контекстом устройства без памяти (поскольку контекст устройства памяти будет создавать только монохромные растровые изображения), а цветовая палитра, используемая этим растровым изображением, является той же цветовой палитрой, которая используется этим контекстом устройства. Вы можете запросить его, используя GetCurrentObject метод. Однако замечания к Bitmap.Bitmap(HBITMAP, HPALETTE) конструктор государство:

Не передавайте в конструктор GDI + Bitmap :: Bitmap растровое изображение GDI или палитру GDI, которые в настоящее время (или ранее) были выбраны в контексте устройства.

Таким образом, вы не можете использовать текущую палитру контекста устройства напрямую и вам нужно вместо этого создать ее копию.

/// <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);
}
0

Вы можете передать 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;
}
-2
По вопросам рекламы [email protected]