Казалось бы простое дело, а убил большую часть дня на это. И то сделал только благодаря помощи одного человека, автора программы для работы с битмапами, иконками и т.д. Приятно разговаривать с человеком который хорошо разбирается в теме:) В итоге узнал очень много сокровенных знаний, которым и буду делится.
Итак у нас есть данные в памяти в форматах CF_DIB/CF_DIBV5 (да я знаю что это форматы буфера обмена!) ну или другими словами у нас есть BITMAPINFO. А мы хотим получить wxBitmap.
Сначала нам нужно получить HBITMAP, который потом уже можно конвертить в wxBitmap используя wxBitmap::SetHBITMAP(да в документации ее нету).
Много писать мне уже сегодня лень, поэтому просто покажу код, там в комментариях описаны все проблемы и решения.
{
/* там с клипбоардом вообще мрак, MS сам свои спецификации не соблюдает, я тебе уже жаловался, когда в классы всё это барахло заворачивал
когда в biCompression указано BI_BITFIELDS за структурой следуют 12 байт маски цветовых каналов, надо на 12 байт смещать начало пиксельного массива
1) 12 байт пропускать надо? только если biCompression==BI_BITFIELDS
2) по-хорошему, их надо не пропускать, а анализировать и использовать
3) работает ли твой код в случае с 8-битным битмапом (т.е. таким, который включает палитру)?
в третьем с конца - нужно пропустить кол-во элементов палитры
так, а как узнать количество элементов палитры?
biClrUsed или (1<<biBitCount), если biClrUsed=0 и это делать только если biBitCount <= 8
*/
int offset = 0;
if (bi->bmiHeader.biBitCount <= 8)
{
offset = bi->bmiHeader.biClrUsed ? bi->bmiHeader.biClrUsed : (1 << bi->bmiHeader.biBitCount);
}
else
{
if (bi->bmiHeader.biCompression == BI_BITFIELDS) offset = 3;
}
/*
Правильно использовать independing bitmap - но похоже тут у wxBitmap проблемы, мы получаем правильный hBitmap (видно на экране), но в файле пробелмы - на определенной
цветности\форматах (тестировалось только для DIB)
поэтому использую DDB - тут теряется цветность, итоговая цветность не равна оригинальной, а зависит от текущих настроек экрана... но зато работает.
// (RGBQUAD*)((byte*)bi + bi->bmiHeader.biSize) + offset instead of &bi->bmiColors + shift - for support both versions DIB\DIBV5
*/
#if 0
// using independent bitmap
char * pBits;
HBITMAP hBitmap = CreateDIBSection(CreateCompatibleDC(NULL), bi, DIB_RGB_COLORS, (void**)&pBits, NULL, 0);
memcpy(pBits, (RGBQUAD*)((byte*)bi + bi->bmiHeader.biSize) + offset, abs(bi->bmiHeader.biHeight) * (((bi->bmiHeader.biWidth * bi->bmiHeader.biBitCount + 31) & ~31) / 8));
#else // use DDB (dependent) - we lost information about color depth - bad way
HBITMAP hBitmap = CreateDIBitmap(GetDC(NULL), &bi->bmiHeader, CBM_INIT, (RGBQUAD*)((byte*)bi + bi->bmiHeader.biSize) + offset, bi, DIB_RGB_COLORS);
#endif
// //------------------------------------------------------------------------------------------------------------------
// { // write to screen to test it
// HDC screen = GetDC(NULL);
// HDC bitmap = CreateCompatibleDC(screen);
// HBITMAP old = (HBITMAP)SelectObject(bitmap, hBitmap);
// int w = 0, h = 0, d = 0;
// w = bi->bmiHeader.biWidth; h = bi->bmiHeader.biHeight; d = bi->bmiHeader.biBitCount;
// BitBlt(screen, 100, 100, w, h, bitmap, 0, 0, SRCCOPY);
// SelectObject(bitmap, old);
// DeleteDC(bitmap);
// ReleaseDC(NULL, screen);
// }
// //------------------------------------------------------------------------------------------------------------------
wxBitmap bitmap;
bitmap.SetHBITMAP(hBitmap);
if (bitmap.IsOk())
{
bool res = bitmap.SaveFile(file, BegUtils::DetermineImageType(file));
wxASSERT(res);
}
else wxASSERT(0);
}
Да p->data.GetData() может содержать либо просто DIB либо 5ю версию.
Если кто знает другое решение, которое позволит обойти проблему и сохранять данные корректно, или кто пофиксит баг в wxBitmap или хотя бы сформулирует и закинет в трекер, пишите:)