Управление растровым изображением. Альтернативные способы перерисовки динамического прямоугольника. Работа с растровыми изображениями, страница 9

¨  Не перерисовывать клиентскую область в конце процесса перетаскивания (событие WM_LBUTTONUP).

Создайте копию файла Step04.cpp, переименуйте ее в Step05.cpp, подключите ее к проекту, а старый файл отсоедините. Аккуратно внесите все перечисленные изменения. Они довольно сильно меняют алгоритм обработки сообщения WM_MOUSEMOVE, особенно той его ветви, где мы реагируем на перетаскивание изображения.

case WM_MOUSEMOVE:

{

if (!(wParam & MK_LBUTTON))

return 0;

short x = LOWORD(lParam),  y = HIWORD(lParam);

hdc = GetDC(hWnd);

if (bDrag)    // Изображаем прямоугольник перетаскиваемого объекта

{

OffsetRect (&rBmp, x - ptOld.x, y - ptOld.y); // Вычисляем новый прямоугольник

ptOld.x = x; ptOld.y = y;

StretchBlt (    // Копируем изображение в оконный контекст

hdc, rBmp.left, rBmp.top, rBmp.right-rBmp.left, rBmp.bottom-rBmp.top,

hMemDC, 0, 0, szSrc.cx, szSrc.cy, SRCCOPY);

}

else // Изображаем пустой прямоугольник - мишень для копирования изображения

{

if (!IsRectEmpty (&rTarget))

DrawFocusRect(hdc, &rTarget); // Сначала стираем старый прямоугольник мишени

SetRect (&rTarget, ptOld.x, ptOld.y, x, y); // Вычисляем новые координаты мишени

if (ptOld.x > x)

{

rTarget.left = x;

rTarget.right = ptOld.x;

}

if (ptOld.y > y)

{

rTarget.top = y;

rTarget.bottom = ptOld.y;

}

DrawFocusRect(hdc, &rTarget);    // Изображаем новую мишень

}

ReleaseDC(hWnd, hdc);

break;

}

case WM_LBUTTONUP:

ClipCursor (0); 

if (bDrag)

{

bDrag = false;

break;

}

if (IntersectRect(&rTmp, &rBmp, &rTarget))

{

CopyRect(&rBmp, &rTarget);

InvalidateRect(hWnd, 0, TRUE);

}

break;

Проверьте качество перерисовки внутренней области прямоугольника при перетаскивании его мышью. Изображение перестало мерцать, но вместо этого появился другой неприятный эффект, вызванный отказом от стирания фона, Напомним, сто стирание фона предшествует процессу перерисовки, но именно оно и вызывает мерцание.

Сделаем еще один шаг по направлению к главной цели — плавной (flicker-free) перерисовке изображения в процессе перетаскивания изображения. Теперь мы вовсе откажемся от стирания фона, но будем непревывно перерисовывать изображение при перетаскивании. Измените ветвь обработки сообщения WM_ERASEBKGND.

case WM_ERASEBKGND: return TRUE;

Такой вариант реакции на событие воспринимается операционной системой как приказ удалить WM_ERASEBKGND из очереди сообщений, так как процесс его обработки закончен. Результат этого действия—фон никогда не будет стираться, а значит с мерцанием покончено раз и навсегда. Его не будет даже при изменении размеров окна, когда Windows самостоятельно перерисовывает clip region. В таком сценарии нет необходимости повторять вызов StretchBlt в ветви WM_MOUSEMOVE (так как он присутствует в ветви WM_PAINT). Достаточно заменить его на: InvalidateRect (hWnd, 0, TRUE);. Сделайте это и убедитесь, что мерцание не проявляется ни в каких ситуациях, но и фон теперь никогда не стирается. С этим недостатком можно бороться разными способами. Перечислим их.

¨  Bitmap, копируемый в контекст, связанный с окном, следит за размерами клиентской области и периодически (в обработке WM_SIZE) подстраивает свои размеры под них. Само изображение при этом имеет меньший размер и является частью клиентского контекста. Изображение перемещается мышью в пределах большого bitmap'а. В этом сценарии важно, что перемещение изображения происходит в контексте, который не отображается в окне (он в памяти), а не в контексте hdc (на глазах у пользователя).

¨  Bitmap, копируемый в контекст, связанный с окном, по бокам имеет поля цвета окна, которые используются как бульдозер, заглаживающий поверхность для рисования. При перемещении они стирают остатки старого изображения. В этом сценарии важно первоначально (один раз) закрасить фон и не слишком быстро перемещать изображение, чтобы размеров полей хватило на закраску огрехов.