Управление растровым изображением. Структура Windows-приложения. Развитие стартовой заготовки, страница 8

работает в контексте hdc, то есть в том контексте, который связан с окном. Эта функция не только рисует контур прямоугольника пунктирным пером, но и закрашивает его внутреннюю область белой кистью. Напомним, что по умолчанию используется кисть цвета WINDOW (этот индекс соответствует белому цвету). Реальные же цвета изменяются в соответствии с включенной растровой операцией R2_NOT.

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

¨  Отменить закраску внутренности динамического прямоугольника в ветви WM_MOUSEMOVE. Для этого надо убрать два вызова функции Rectangle,

¨  При каждом событии WM_MOUSEMOVE вычислять новое положение изображения, и, не дожидаясь обработки события WM_LBUTTONUP (конец процесса перетаскивания), копировать изображение из контекста hMemDC (в памяти) в контекст hdc (связанный с окном).

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

Аккуратно внесите все перечисленные изменения. Они довольно сильно меняют алгоритм обработки сообщения 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);. Сделайте это и убедитесь, что мерцание не проявляется ни в каких ситуациях, но и фон теперь никогда не стирается. С этим недостатком можно бороться разными способами. Перечислим их.