Ладно .. это. У меня сейчас нет MS VS, поэтому я пишу простой пример в Delphi. Все имена функций одинаковы, так что я надеюсь, что вы поймаете идею ..
Вот переменные, которые я использую:
FFactory: ID2D1Factory; //ID2D1Factory* FFactory;
FHWNDRT: ID2D1HwndRenderTarget; //ID2D1HwndRenderTarget* FHWNDRT;
FBitmapRT: ID2D1BitmapRenderTarget; //Etc..
FBrush: ID2D1SolidColorBrush;
Вот пример:
function TForm1.InitializeD2D: HRESULT;
var
TargetRect: TRect;
BrushProps: D2D1_BRUSH_PROPERTIES;
begin
{ Create factory }
Result := D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, ID2D1Factory, nil, FFactory);
If Failed(Result) then Exit;
{ Get form's client rect }
Winapi.Windows.GetClientRect(Self.Handle, targetRect);
{ Create hwnd render target }
Result := FFactory.CreateHwndRenderTarget(
D2D1RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_HARDWARE, D2D1PixelFormat(DXGI_FORMAT_R8G8B8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), 96, 96, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_10),
D2D1HwndRenderTargetProperties(Self.Handle, D2D1SizeU(TargetRect.Width, TargetRect.Height)),
FHWNDRT
);
If Failed(Result) then Exit;
{ Create bitmap render target }
Result := FHWNDRT.CreateCompatibleRenderTarget(
nil,
nil,
nil,
D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE,
FBitmapRT
);
If Failed(Result) then Exit;
With BrushProps do Begin
opacity := 1;
transform := D2D1_MATRIX_3X2_F.Identity;
End;
{ Create brush so we can draw }
Result := FBitmapRT.CreateSolidColorBrush(D2D1ColorF(0, 0.25, 0.75, 1), @BrushProps, FBrush);
end;
function TForm1.UpdateStaticShapes: HRESULT;
begin
//Draw the static shapes on the offscreen bitmap
FBitmapRT.BeginDraw;
//Clear the offscreen bitmap
FBitmapRT.Clear(D2D1ColorF(0, 0, 0, 1));
//Draw a line
FBrush.SetColor(D2D1ColorF(1, 0, 0, 1));
FBitmapRT.DrawLine(D2D1PointF(Random(10)+10, Random(10)+10), D2D1PointF(50, 50), FBrush);
//Draw a filled rect
FBrush.SetColor(D2D1ColorF(0, 0.25, 0.75, 1));
FBitmapRT.FillRectangle(D2D1RectF(Random(50), Random(50), Random(250)+50, Random(300) + 50), FBrush);
//Etc.. (draw all your shapes)
Result := FBitmapRT.EndDraw();
end;
function TForm1.Render: HRESULT;
var
pBitmap: ID2D1Bitmap;
begin
FHWNDRT.BeginDraw;
FHWNDRT.Clear(D2D1ColorF(0, 0, 0, 1));
{ Draw the offscreen bitmap }
FBitmapRT.GetBitmap(pBitmap);
If pBitmap <> nil then Begin
FHWNDRT.DrawBitmap(pBitmap);
pBitmap := nil; //Equivalent to _Release()
End;
{ Draw the additional free hand drawing here }
FBrush.SetColor(D2D1ColorF(1, 1, 1, 1));
FHWNDRT.DrawRectangle(D2D1RectF(100, 100, 200, 200), FBrush);
Result := FHWNDRT.EndDraw();
end;
offscreen bitmap перерисовывается в UpdateStaticShapes() метод. И окончательный кадр отображается в Render() метод.
Редактировать: Я попытался сделать масштабирование и понял вашу проблему. Я думаю, что решение состоит в том, чтобы воссоздать цель рендеринга растрового изображения каждый раз, когда вы изменяете коэффициент масштабирования (также каждый раз, когда вы изменяете размер окна). Размер внеэкранного растрового изображения должен быть:
OffscreenBitmap.Width/Height = HwndRT.Width/Height * ScaleFactor;
Когда вы рисуете форму вы должны использовать координаты относительно внеэкранного размера растрового изображения. Например: вместо рисования линии (50,50, 100,100) вы должны нарисовать ее (50 * K, 50 * K, 100 * K, 100 * K) где K = ScaleFactor;
Вот как я создаю растровый RT:
//In C++ this should look like:
//HRESULT TForm::CreateBitmapRT(float ScaleFactor) {}
function TForm1.CreateBitmapRT(ScaleFactor: Single): HRESULT;
var
DesiredSize: D2D1_POINT_2F;
begin
FBitmapRT := nil; //FBitmapRT->_Release(); FBitmapRT = NULL;
{ Decide offscreen bitmap's size }
DesiredSize := D2D1PointF(FWindowRect.Width * ScaleFactor, FWindowRect.Height * ScaleFactor);
{ Create bitmap render target }
Result := FHWNDRT.CreateCompatibleRenderTarget(
@DesiredSize,
nil,
nil,
D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE,
FBitmapRT
);
end;
Здесь дополненная Рендер() метод:
function TForm1.Render: HRESULT;
var
pBitmap: ID2D1Bitmap;
SrcRect, DestRect: D2D1_RECT_F;
begin
FHWNDRT.BeginDraw;
FHWNDRT.Clear(D2D1ColorF(0, 0, 0, 1));
If FBitmapRT <> nil then Begin
{ Draw the offscreen bitmap }
FBitmapRT.GetBitmap(pBitmap);
If pBitmap <> nil then Begin
SrcRect := D2D1RectF(FZoomOffset.x, FZoomOffset.y, FZoomOffset.x + FWindowRect.Width, FZoomOffset.y + FWindowRect.Height);
DestRect := D2D1RectF(0, 0, FWindowRect.Width, FWindowRect.Height);
FHWNDRT.DrawBitmap(pBitmap, @DestRect, 1, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, @SrcRect);
pBitmap := nil; //pBitmap->_Release(); pBitmap = NULL;
End;
End;
{ Draw the additional free hand drawing here }
FBrush.SetColor(D2D1ColorF(1, 1, 1, 1));
FHWNDRT.DrawRectangle(D2D1RectF(100, 100, 200, 200), FBrush);
Result := FHWNDRT.EndDraw();
end;
Вот что произойдет, если изменить коэффициент масштабирования (увеличения или уменьшение масштаба):
function TForm1.ApplyZoom(fScaleFactor: Single): HRESULT;
begin
If fScaleFactor = FZoomFactor then Exit(S_OK);
If fScaleFactor = 0 then fScaleFactor := 0.1;
{ Recreate the offscreen bitmap }
Result := CreateBitmapRT(fScaleFactor);
If Failed(Result) then Exit;
{ Here you have to redraw the shapes once again }
Result := UpdateStaticShapes;
If Failed(Result) then Exit;
{ I save the new Zoom Factor in a class field }
FZoomFactor := fScaleFactor;
end;
Решение состоит в том, чтобы нарисовать статические фигуры на экране вне растра и перерисовать его только тогда, когда формы меняются. И в вашей функции рендеринга вы сначала рисуете заставку растрового изображения, а затем свободную руку, рисующую сверху. Какую визуализацию вы используете? hwnd render target? –
@AntonAngelov Да, я использую ID2D1HwndRenderTarget, можете ли вы предоставить решение с этим типом рендеринга? – Nick