Запись текста в растровом формате, HBITMAP «несовместим» с HDC

Я пытаюсь использовать упомянутый код Записать текст на BITMAPINFO в память. Мой код ниже.

BITMAPINFO MyBMInfo = {0}; //debug
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader); //debug
int lines; //debug

HDC hdc = GetDC(0);
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateBitmap(10, 10, 1, 8, bmp_buffer);
GetDIBits(memdc, hbitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS); //debug
HBITMAP oldbmp = SelectObject(memdc, hbitmap);
TextOut(memdc, 0, 0, "123", 3);
SelectObject(memdc, oldbmp);
lines = GetDIBits(memdc, hbitmap, 0, 10, bmp_buffer, &MyBMInfo, DIB_RGB_COLORS); //<-here was used other BMInfo structure
DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(0, hdc);

Это не работает так, как ожидалось. Во время устранения неполадок я обнаружил, что GetDIBits возвращает 0 строк, например, не удалось скопировать содержимое растрового изображения в bmp_buffer, позже я попытался просмотреть текущее выбранное растровое изображение внутри memdc, выполнив GetCurrentObject(memdc, OBJ_BITMAP), и оно не изменилось, поэтому по какой-то причине hbitmap не «выбрано». " внутри memdc.

Потом я перепробовал много разных идей и обнаружил, что если hbitmap создан CreateCompatibleBitmap(), то строки bmp_buffer обновляются. Я запросил атрибуты такого созданного растрового изображения, и похоже, что оно имеет правильные размеры, но имеет настройку цвета в 1 бит. Следуя этому треку, если я перейду на hbitmat=CreateBitmap(10, 10, 1, 1, bmp_buffer), это будет работать, по крайней мере строки != 0 и некоторые байты в bmp_buffer обновятся. Это означает, что hbitmap, созданный CreateBitmap(10, 10, 1, 8, bmp_buffer), «несовместим» с memdc. Переход на монохромный — не решение, так как мне нужны цвета в растровом изображении.

В документации GDI говорится, что любой DC, созданный CreateCompatibleDC(), является монохромным и имеет размер 1x1 пиксель. В нем также говорится, что его необходимо обновить позже, чтобы использовать цветные изображения разных размеров, но нет никаких предложений, как изменить размер цвета. Что я делаю неправильно, код, которому я следую, использует только 24 бита на пиксель, и он работал в каком-то другом приложении.

Чтобы GetDIBits работало, вам необходимо правильно заполнить несколько полей в структуре MyBMInfo.bmiHeader, помимо размера.

500 - Internal Server Error 09.06.2024 15:36

@ 500-InternalServerError, можете ли вы подсказать, на что обращать внимание? Я подозреваю, что проблема в моем коде возникла немного раньше, чем GetDIBits, ближе к моменту создания внутреннего растрового изображения.

odo 09.06.2024 16:38

Вы использовали CreateCompatibleBitmap с оригинальным DC? Вы не должны использовать его с памятью DC.

ElderBug 09.06.2024 18:47

@ElderBug Что может быть «оригинальным DC», если я хочу сделать виртуальный холст TextOut, а затем сохранить его в файл? Я пытался использовать результат CreateDC(0) или CreateDC(hwnd to main window) и наткнулся на ту же стену, но на этот раз GetDIBits работает только для 32-битного цветового пространства.

odo 09.06.2024 19:11

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

ElderBug 09.06.2024 20:05

@ElderBug Похоже, вы используете неверный API. Как правильно создать DC для последующей работы с TextOut и создания растрового изображения в файл?

odo 09.06.2024 20:25

Какова именно ваша цель? Если вы никогда не собираетесь использовать вывод устройства, то GDI для этого не подойдет. Рисование текста без устройства не имеет смысла в GDI. Как для вас должен выглядеть текст? Для GDI это полностью зависит от устройства. В зависимости от монитора ClearType будет отображаться по-разному. GDI также предназначен для принтеров, где рендеринг снова сильно отличается. Вы не можете просто сказать «Нарисуй мне текст» без контекста. Чтобы упростить задачу, вы либо используете GDI с контроллером домена, который вам подходит (например, экран), либо используете другую библиотеку, такую ​​​​как DirectWrite или freetype.

ElderBug 09.06.2024 21:01

@ElderBug Моя цель — отобразить растровый файл с использованием шрифтов Windows. Я ожидаю, что контекст устройства памяти имеет такую ​​функциональность. Что касается моей проблемы с цветовым кодированием, в документации Microsoft говорится Learn.microsoft.com/en-us/windows/win32/gdi/… «Прежде чем приложение сможет начать рисование, оно должно выбрать растровое изображение с соответствующей шириной и высотой. в DC, вызвав функцию SelectObject». Похожая тема stackoverflow.com/questions/33181728/… но вызов CreateCompatibleDC(0) у меня не работает.

odo 09.06.2024 21:20

@ElderBug после рассмотрения моя проблема не в том, что растровое изображение, с которым я работаю, TextOut, совместимо с устройством/экраном или чем-то еще. Но в конце я хотел бы сохранить это изображение после добавления графиков и изображений из приложения в 8-битный файл BMP.

odo 09.06.2024 22:03

Честно говоря, вы могли бы просто работать с совместимым DC со скоростью 32 бита на пиксель, я не думаю, что возникнут какие-либо проблемы. Вероятно, вам следует попробовать отключить ClearType, но я не знаю, как это сделать.

ElderBug 09.06.2024 22:08

@ElderBug Но как тогда сопоставить растровое изображение 32 бит/пиксель с данными 8 бит/пиксель? Будет ли GetDIBits делать это автоматически?

odo 09.06.2024 22:53
GetDIBits должен конвертироваться, если не ошибаюсь. Вам необходимо изменить структуру информации о растровом изображении на желаемую.
ElderBug 09.06.2024 23:39

@ElderBug Это любезно, мне потребовалось несколько часов, чтобы обнаружить, что «BITMAPINFO», используемый в качестве аргумента, должен иметь палитру, без этого GetDiBits отказывается работать. Однако он по-прежнему не работает в полной мере, как ожидалось. Если я сделаю TextOut("123") и выгружу изображение в режиме 32bpp, я получу последовательности FF-FF-FF-00 и 00-00-00-00, это ожидаемо, белый и черный цвета. Но если я создам палитру, содержащую 2 цвета «bmiHeader.biClrImportant = 2», первый цвет FF-FF-FF-00, а второй 00-00-00-00, и попытаюсь сопоставить изображение с 8bpp, я получу два типа байтов. на выходе 0x00 и 0x13. Любой совет?

odo 10.06.2024 20:55

Размер таблицы указан в biClrUsed, а не в biClrImportant.

ElderBug 11.06.2024 19:38

Кроме того, кажется, вы пытаетесь ввести таблицу цветов, но я думаю, что таблица цветов является результатом. Если вы укажете формат 8bpp, GetDIBits заполнит 256 записей в таблице.

ElderBug 11.06.2024 19:48

@ElderBug Я попробовал установить как «biClrUsed», так и «biClrImportant». Кажется, у меня есть все части этого пазла, но я все еще пытаюсь что-то узнать, я все еще не знаю, откуда берутся эти значения, попытка вызвать GetDIBColorTable на `memdc` не приносит никакой ценности, так как memdc - это RGB, поэтому функция просто терпит неудачу. Два новых вывода: GetDIBits(... DIB_RGB_COLORS) получает пиксельные данные, начиная с FF FF 00 00 00 00 FF FF FF FF, а GetDIBits(... DIB_PAL_COLORS) получает 13 13 00 00 00 00 13 13 13 13 00 00 .... Таким образом, существует своего рода отображение палитры, используемое внутри GetDIBits.

odo 11.06.2024 22:06

@ElderBug Я еще раз прочитал документацию GetDiBtis на самом деле сгенерировал новую палитру и ЗАПИСАЛ в BITMAPINFO, не используя предоставленную палитру. Для DIB_RGB_COLORS палитра состоит из четырех байтов, RGB плюс один байт, для DIB_PAL_COLORS палитра состоит из 16-битных чисел (точно, как сказано в документе), но в позиции 0x13 находится значение 0x13. Существует 20-цветная палитра окон, где черный цвет находится в конце списка (0x13 = 19 dec), НО эта палитра построена так, что определены первые 10 и последние 10 (номера 245-255). Но если GetDiBtis попытается сгруппировать «знать» цвет в начале, это может объяснить мои наблюдения.

odo 11.06.2024 22:31

Да, как я уже сказал, таблица — это результат. Вам также необходимо установить bbiCompression на BI_RGB для палитры.

ElderBug 11.06.2024 23:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
18
118
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

У меня было мало недоразумений при чтении документации и примеров кода, используемых в вопросах, они кратко изложены ниже:

  1. GetDIBits не использует предоставленную палитру, а просто перезаписывает существующий массив памяти. В документации это прямо указано, я пропустил эту часть. Также не подчиняется ни biClrUsed ни biClrImportant, поэтому выделите достаточно памяти.
  2. Невозможно создать контекст с указанным количеством бит на пиксель, который унаследует совместимое растровое изображение. GetDC(0) возвращает контекст с рабочего стола, который составляет 32 бита на пиксель, и HDC memdc = CreateCompatibleDC(hdc) создает контекст с 1 бит на пиксель (как указано в документации).
  3. Лучше не писать непосредственно в буфер изображения файла bmp с помощью GetDIBits(), а использовать промежуточный буфер и скопировать изображение в буфер изображения файла bmp. Это дает возможность контролировать положение и цвет печатного текста.

Мой рабочий пример:

char* local_img = malloc(100*100);
BITMAPINFO local_bmi = malloc(sizeof(BITMAPINFO)+sizeof(COLORREF)*255);
local_bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
local_bmi->bmiHeader.biPlanes = 1;
local_bmi->bmiHeader.biBitCount = 8;
local_bmi->bmiHeader.biHeight = 100;
local_bmi->bmiHeader.biWidth = 100;
local_bmi->bmiHeader.biCompression = 0;
local_bmi->bmiHeader.biSizeImage = 100*100;
local_bmi->bmiHeader.biClrImportant = 256;
local_bmi->bmiHeader.biClrUsed = 256;

HDC hdc = GetDC(0);
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, 100, 100);
HBITMAP oldbmp = SelectObject(memdc, hbitmap);
TextOut(memdc, 0, 0, "123", 3);
SelectObject(memdc, oldbmp);
GetDIBits(memdc, hbitmap, 0, 100, local_img, local_bmi, DIB_RGB_COLORS);
DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(0, hdc);  
free(local_bmi);

//use local_img for own purpose, then free it!!!

Другие вопросы по теме