Как перерисовать только область многослойного окна?
У меня есть многослойное окно, которое обычно рисуется следующим образом:
private void SelectBitmap(Bitmap bitmap)
{
IntPtr screenDc = GetDC(IntPtr.Zero);
IntPtr memDc = CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr hOldBitmap = IntPtr.Zero;
try
{
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
hOldBitmap = SelectObject(memDc, hBitmap);
POINT sourceLocation = new POINT(0, 0);
BLENDFUNCTION blend = new BLENDFUNCTION();
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
SIZE newSize = new SIZE(bitmap.Width, bitmap.Height);
POINT newLocation = new POINT(Location.X, Location.Y);
UpdateLayeredWindow(Handle, screenDc,
ref newLocation, ref newSize,
memDc,
ref sourceLocation, 0,
ref blend,
ULW_ALPHA);
}
finally
{
ReleaseDC(IntPtr.Zero, screenDc);
if (hBitmap != IntPtr.Zero)
{
SelectObject(memDc, hOldBitmap);
DeleteObject(hBitmap);
}
DeleteDC(memDc);
}
}
Однако это, очевидно, перерисовывает все окно каждый раз, когда оно вызывается. Это большое снижение производительности на большом окне. (даже на моем лучшем ПК, что заставляет меня задуматься, как люди справляются с этим в Win2K)
Если я читаю статью Microsoft в многослойном окне, она говорит: UpdateLayeredWindow всегда обновляет все окно. Чтобы обновить часть окна, используйте традиционный WM_PAINT и установите значение смешивания, используя SetLayeredWindowAttributes.
Я просто не могу понять вышесказанное. Как WM_PAINT должен получить доступ к многоуровневому оконному растровому изображению и перерисовать только его часть в окне? Из того, что я понял, многослойные окна просто отключают сообщение WM_PAINT и ожидают, что пользователь сам нарисовает окно. Очевидно, что нет способа привязать WM_PAINT к пользовательскому чертежу.
Я что-то упускаю очень очевидное?
1 ответ
После длительного профилирования я обнаружил, что узким местом было не многоуровневое обновление окна. Обновление всего экрана, описанный выше метод SelectBitmap, в 1920*1200 занимало около 6-8 мс. Конечно, не очень удивительно, но достаточно для обновления со скоростью 30 FPS+.
В моем случае производительность исходит из некоторого потока, требующего обновления почти сто раз за перерисовку, что делает все вялым. Решение состояло в том, чтобы разбить обновление / перерисовку и разделить их. Один обновляет (объединяет) регион, а другой, когда не рисует, берет этот регион, рисует и затем очищает его.