4

Раньше не занимался рисованием через WinAPI на DC, сейчас изучаю этот процесс. Хочу спросить, как правильно работать с объектами и рисовать на контролах?

Я думаю так: получаю размер клиентской части окна (контрола), на котором рисую (GetClientRect) создаю совместимый DC (CreateCompatibleDC), создаю совместимый Bitmap размером с клиентскую часть окна (CreateCompatibleBitmap), выбираю его в этом совместимом DC (SelectObject), чтобы DC имел нужный размер и цветность.

Далее вывод делаю в созданный совместимый DC, а в обработчике WM_PAINT делаю BeginPaint + BitBlt (перенос из совместимого DC в DC окна) + EndPaint. DeleteDC этого совместимого DC для рисования и DeleteObject созданного вначале Bitmap'а делаю при закрытии окна (именно в этом порядке: сначала DeleteDC(CompatibleDC), затем DeleteObject(Bitmap)).

Правилен ли этот процесс? Не нужно ли делать что-то ещё или можно ли пойти другим путём? Могу ли я удалить совместимый Bitmap (выбранный в DC) раньше, чем DC без ущерба для последнего?


И второй вопрос: создание карандашей, кистей и пр.

  1. CreatePen (CreateSolidBrush и пр)
  2. SelectObject
  3. Рисую
  4. DeleteObject(Pen/Brush/etc)

Если CreatePatternBrush, то:

  1. LoadBitmap
  2. CreatePatternBrush
  3. SelectObject
  4. Рисую
  5. DeleteObject(Brush)
  6. DeleteObject(Bitmap)

Всё верно или где-то есть ошибка? Можно ли удалять карандаши/кисти до начала рисования (сразу после SelectObject)?

В MSDN написано: Do not delete a drawing object (pen or brush) while it is still selected into a DC. Как же из тогда удалять? Как отменить их выбор? Относится ли это к Bitmap'ам (а не только к Pen/Brush)...

Jin X
  • 537
  • Можно рисовать без буферизации сразу на контроле – Anton Shchyrov Dec 14 '17 at 14:24
  • Это понятно, только при сдвиге контрола, его изображение будет потёрто. Для этого я буферизацию и делаю... – Jin X Dec 14 '17 at 14:26
  • 1
    SelectObject возвращает предыдущий объект - сохраните его и перед удалением своего Brush/Pen/Bitmap поставьте его обратно с помощью SelectObject. Это будет наиболее "чисто" и в соответствии с примерами. – Lyth Dec 14 '17 at 14:30
  • 1
    @Lyth, т.е. X=CreatePen, Z=SelectObject(X), Рисую, SelectObject(Z), DeleteObject(X), так? – Jin X Dec 14 '17 at 14:31
  • Потерто, вызовится новый WM_PAINT и нарисуется заново. Конечно, все зависит от сложности отрисовки – Anton Shchyrov Dec 14 '17 at 14:32
  • 1
    Да, именно так. Это относится и к кистям, и к битмапам. – Lyth Dec 14 '17 at 14:34
  • @Anton-Shchyrov, да можно, но это не всегда удобно, особенно когда рисунок создаётся постепенно или когда он сложен (например, график сложной функции – перерисовка будет менее трудоёмкой). – Jin X Dec 14 '17 at 14:38
  • @Lyth, спасибо! – Jin X Dec 14 '17 at 14:39
  • @Lyth, а процесс создания дополнительного DC я правильно описал? – Jin X Dec 14 '17 at 14:40
  • 1
    Насколько я помню, да, всё правильно. – Lyth Dec 14 '17 at 14:43
  • 1
    Главное, в CreateCompatibleBitmap указать hdc контрола, а не возвращённый CreateCompatibleDC, иначе получим моно-изображение (это для тех, кто интересуется темой и читает эти сообщения) :) – Jin X Dec 14 '17 at 14:58

1 Answers1

2

В целом всё верно, однако SelectObject возвращает предыдущий выбранный объект (для каждого типа: Bitmap, Brush, Font, Pen, Region) - сохраняйте их и выбирайте обратно перед удалением своего объекта:

HGDIOBJ original = NULL;
originalPen = SelectObject(hdc, myPen);
...
SelectObject(hdc, originalPen);
DeleteObject(myPen);

Некоторые виды объектов удалять необязательно - это те, которые можно получить функцией GetStockObject.

Lyth
  • 1,648
  • А если объект нужен на протяжении всей жизни DC? К примеру, создаём: CreateCompatibleDC + CreateCompatibleBitmap + SelectObject. Для удаления же достаточно будет сделать DeleteObject + DeleteDC (ну или наоборот)? Не возвращая старый Bitmap? Всё равно же DC удаляется... – Jin X Dec 14 '17 at 18:29
  • А если удалить этот Bitmap сразу после SelectObject? Код таким образом работает. Тем не менее, можно ли так делать? С Pen так не прокатывает, устанавливается сразу чёрный сплошной тонкий карандаш, а вот Bitmap нормально работает... – Jin X Dec 14 '17 at 18:32
  • И нужно ли восстанавливать прежний Pen, Brush и пр., если я выбрал нудный мне его через GetStockObject? Или можно выбрать и всё... я же его не удаляю? – Jin X Dec 14 '17 at 18:39
  • @JinX документация просит только не удалять выбранный объект; так что если вы удаляете DC до битмапа и потом не обращаетесь ни к DC, ни к битмапу, то вроде ничего и не произойдёт. Полагаю, что вообще DC, созданные через CreateCompatibleDC более лояльны к наличию объектов и порядку удаления, чем прямые, полученные через GetDC или BeginPaint. – Lyth Dec 14 '17 at 18:47
  • 1
    @JinX со стоковыми да, можно просто выбрать и не удалять (и сохранять прежний тоже не нужно, но это просто удобно в общем случае). Для стоковых Pen и Brush есть даже свои функции по установке цвета - SetDCPenColor, SetDCBrushColor. – Lyth Dec 14 '17 at 18:51
  • Спасибо! Прошу ещё обратить внимание на смежную тему: https://ru.stackoverflow.com/questions/758311 . А то там что-то молчат пока все... – Jin X Dec 14 '17 at 19:56
  • 1
    Кстати, Ваш код можно упросить, обойдясь без переменной myPen: originalPen = SelectObject(hdc, GreatePen(PS_DASH, 3, 0x80)); ... ; DeleteObject(SelectObject(hdc, originalPen)); :))) – Jin X Dec 14 '17 at 21:28