Узнайте в обработчике WM_PAINT, какой регион является недействительным InvalidateRgn - PullRequest
0 голосов
/ 22 октября 2019

Моя задача - создать индикатор с фоновой шкалой и указателем. Когда указатель перемещается, я хотел бы перерисовать часть фона на прежнем месте, а затем нарисовать новый указатель.

Форма указателя определяется как многоугольник, из которого я создал область GDI +.

Итак, у меня есть 2 объекта области: старый и новый.

Раз в 50 миллисекунд я вызываю следующую подпрограмму:

procedure TForm1._timerTick(Sender: TObject);
var
  g: TGPGraphics;
  mx: TGPMatrix;
  hr: HRGN;
begin
  if _newRegion <> nil then _newRegion.Free();
  if _oldRegion <> nil then _oldRegion.Free();
  _newRegion := _baseRegion.Clone();
  _oldRegion := _baseRegion.Clone();
  mx := TGPMatrix.Create();
  try
    mx.RotateAt(_angle, MakePoint(250.0, 120.0));
    _oldRegion.Transform(mx);
    mx.Reset();
    Inc(_angle);
    if _angle >= 360 then _angle := 0;
    mx.RotateAt(_angle, MakePoint(250.0, 120.0));
    _newRegion.Transform(mx);
  finally
    mx.Free();
  end;
  g := TGPGraphics.Create(Canvas.Handle);
  try
    hr := _oldRegion.GetHRGN(g);
    try
      InvalidateRgn(Handle, hr, False); //Restore background
    finally
      DeleteObject(hr);
    end;
    hr := _newRegion.GetHRGN(g);
    try
      InvalidateRgn(Handle, hr, False); //Draw new region
    finally
      DeleteObject(hr);
    end;
  finally
    g.Free();
  end;
end;

Как видите, у меня естьНет ничего лучше, чем вызвать InvalidateRgn дважды: один раз для восстановления фона и один раз для рисования указателя.

Обработчик WM_PAINT выглядит следующим образом:

procedure TForm1.Paint();
var
  g: TGPGraphics;
  mx: TGPMatrix;
  brs: TGPSolidBrush;
begin
  inherited;
  g := TGPGraphics.Create(Canvas.Handle);
  mx := TGPMatrix.Create();
  brs := TGPSolidBrush.Create(MakeColor(255, 255, 0));
  try
    if _fullDraw then begin
      _fullDraw := False;
    end
    else begin
      g.IntersectClip(_oldRegion);
    end;
    g.DrawImage(_img, 0, 0);
    if _newRegion <> nil then begin
      g.ResetClip();
      g.FillRegion(brs, _newRegion);
    end;
  finally
    brs.Free();
    mx.Free();
    g.Free();
  end;
end;

Он всегда выполняет две операции: восстанавливает фони рисует указатель.

Если я выполню InvalidateRgn дважды, тогда Paint также будет вызван дважды, что приведет к четырем операциям вместо двух.

Есть ли способ узнать на уровне Windowsвнутри метода Paint, какая область признана недействительной?

1 Ответ

1 голос
/ 22 октября 2019

Внутри вашего обработчика WM_PAINT вы можете использовать:

В любом случае вы получите область обновления / отсечения, в пределах которой HDC должна быть закрашена. Любой рисунок, который вы делаете за пределами этой области, будет просто отброшен. Таким образом, вы можете использовать это как оптимизацию, чтобы не тратить усилия на рисование всего, что будет просто отброшено.

Если я выполню InvalidateRgn дважды, тогда Paint также будет вызываться дважды, что приведет к четырем операциям вместодва.

Не верно. Недействительные окна кэшируются и консолидируются до тех пор, пока окно не будет действительно нарисовано и проверено. Вы можете вызывать InvalidateRect() / InvalidateRgn() столько раз, сколько захотите, и окно не будет окрашено до тех пор, пока вы не вернете управление в цикл обработки сообщений, чтобы затем можно было генерировать сообщение WM_PAINT (есливы заставляете краску звонить UpdateWindow() или RedrawWindow()). WM_PAINT - это сообщение с низким приоритетом, оно генерируется очередью сообщений только тогда, когда в окне есть непустое значение Обновить область и другие сообщения с более высоким приоритетом не ожидают. Окно не проверяется до тех пор, пока не будет обработано WM_PAINT (если вы не вызовете его, вызвав ValidateRect() / ValidateRgn()). Для получения дополнительной информации см. Аннулирование и проверка региона обновления в MSDN.

...