unit MainFm;
(*
 * Copyright (c) 2015 - 2025 Dr. Yuriy Kotsarenko. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and limitations under the License.
 *)

// This example illustrates the following features:
//  * Using separate swap chains to render on multiple panels simultaneously.

interface

{$INCLUDE PXT.Config.inc}

uses
  Graphics, Classes, SysUtils, Forms, Controls, Dialogs, ComCtrls, ExtCtrls,
  PXT.Types, PXT.Graphics, PXT.Controls;

type
  TMainForm = class(TForm)
    LeftPanel: TPanel;
    RightPanel: TPanel;
    Splitter1: TSplitter;
    StatusBar1: TStatusBar;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure LeftPanelResize(Sender: TObject);
    procedure RightPanelResize(Sender: TObject);
  private
    FDisplayScale: Single;

    FSwapChain1: TSwapChain;
    FSwapChain2: TSwapChain;

    FDevice: TDevice;
    FCanvas: TCanvas;
    FTextRenderer: TTextRenderer;
    FTimer: TTimer;

    procedure ApplicationIdle(Sender: TObject; var Done: Boolean);
    procedure RenderLeftPanel;
    procedure RenderRightPanel;
  public
  end;

var
  MainForm: TMainForm;

implementation
{$R *.lfm}

procedure TMainForm.FormCreate(Sender: TObject);
begin
  FDisplayScale := PixelsPerInch / 96.0;
  try
    FDevice := TDevice.Create;

    FSwapChain1 := TSwapChain.Create(FDevice, HandleToWindowControlHandle(LeftPanel.Handle),
      Point2i(LeftPanel.ClientWidth, LeftPanel.ClientHeight), TPixelFormat.RGBA8, TPixelFormat.Unknown, 4);

    FSwapChain2 := TSwapChain.Create(FDevice, HandleToWindowControlHandle(RightPanel.Handle),
      Point2i(RightPanel.ClientWidth, RightPanel.ClientHeight), TPixelFormat.RGBA8, TPixelFormat.Unknown, 4);

    FCanvas := TCanvas.Create(FDevice);
    FTextRenderer := TTextRenderer.Create(FCanvas, Point2i(256, 256), TPixelFormat.LA8);
    FTimer := TTimer.Create;
  except
    on E: EAfterwarpException do
    begin
      MessageDlg(E.Message, mtError, [mbOK], 0);
      Application.Terminate;
      Exit;
    end;
  end;
  Application.OnIdle := ApplicationIdle;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  FTimer.Free;
  FTextRenderer.Free;
  FCanvas.Free;
  FSwapChain2.Free;
  FSwapChain1.Free;
  FDevice.Free;
end;

procedure TMainForm.LeftPanelResize(Sender: TObject);
begin
  FSwapChain1.Resize(Point2i(LeftPanel.ClientWidth, LeftPanel.ClientHeight));
end;

procedure TMainForm.RightPanelResize(Sender: TObject);
begin
  FSwapChain2.Resize(Point2i(RightPanel.ClientWidth, RightPanel.ClientHeight));
end;

procedure TMainForm.ApplicationIdle(Sender: TObject; var Done: Boolean);
begin
  RenderLeftPanel;
  RenderRightPanel;
  Done := False;
end;

procedure TMainForm.RenderLeftPanel;
var
  LFontParams: TFontParameters;
  LTechString: string;
  LTexture: TTexture;
  LSurface: TSurface;
begin
  FSwapChain1.BeginScene;
  try
    FDevice.Clear([TClearLayer.Color], FloatColor($FF303030));

    // Render some 2D stuff.
    FCanvas.BeginScene;
    try
      FCanvas.Attributes := [];
      FCanvas.ContextState := TCanvasContextState.FlatScene;

      FCanvas.Arc(Point2f(LeftPanel.ClientWidth * 0.5, LeftPanel.ClientHeight * 0.5),
        Point2f(LeftPanel.ClientWidth * 0.25, LeftPanel.ClientHeight * 0.25), Pi * 0.333, Pi * 1.72, 48,
        ColorRect($FF00FF00, $FFFF0000, $FF0000FF, $FFFFFFFF));

      FCanvas.SamplerState := TCanvasSamplerState.Create(TTextureFilter.Nearest, TTextureFilter.Nearest);
      FCanvas.ContextState := TCanvasContextState.FlatText;

      LFontParams := TFontParameters.Create('Arial', 16.0 * FDisplayScale, TFontWeight.SemiBold);
      LFontParams.Effect.BorderType := TFontBorder.SemiHeavy;
      LFontParams.Effect.BorderBrightness := 0.0;
      LFontParams.Effect.FillBrightness := 1.0;
      LFontParams.Effect.ShadowBrightness := 0.25;
      LFontParams.Effect.ShadowOpacity := 1.0;
      FTextRenderer.FontParameters := LFontParams;

      FTextRenderer.DrawAligned(Point2f(LeftPanel.ClientWidth * 0.5, LeftPanel.ClientHeight * 0.5),
        'This is left panel.', $FFFFFFFF, TTextAlignment.Middle, TTextAlignment.Middle);

      // Draw technology string.
      LFontParams := TFontParameters.Create('Segoe UI', 16.0 * FDisplayScale, TFontWeight.SemiBold);
      LFontParams.Effect.BorderType := TFontBorder.SemiHeavy;
      LFontParams.Effect.BorderBrightness := 0.0;
      LFontParams.Effect.FillBrightness := 1.0;
      LFontParams.Effect.ShadowBrightness := 0.25;
      LFontParams.Effect.ShadowOpacity := 1.0;
      FTextRenderer.FontParameters := LFontParams;

      FTextRenderer.DrawAligned(Point2f(LeftPanel.ClientWidth * 0.5, 4.0 * FDisplayScale),
        'Frame Rate: ' + IntToStr(Round(FTimer.FrameRate)), $FFFFFFFF,
        TTextAlignment.Middle, TTextAlignment.Start);

      if FDevice.Technology = TDeviceTechnology.Direct3D then
        LTechString := 'Direct3D ('
      else
        LTechString := 'OpenGL (';

      LTechString := LTechString + IntToStr(FDevice.TechVersion shr 8) + '.' +
        IntToStr((FDevice.TechVersion shr 4) and $0F) + '+), features: ';

      LTechString := LTechString + IntToStr(FDevice.TechFeatureVersion shr 8) + '.' +
        IntToStr((FDevice.TechFeatureVersion shr 4) and $0F);

      FTextRenderer.DrawAligned(Point2f(LeftPanel.ClientWidth * 0.5, LeftPanel.ClientHeight - 4.0 * FDisplayScale),
        LTechString, $FFFFFFFF, TTextAlignment.Middle, TTextAlignment.Ending);
    finally
      FCanvas.EndScene;
    end;
  finally
    FSwapChain1.EndScene;
  end;
  FTimer.Update;
end;

procedure TMainForm.RenderRightPanel;
var
  LFontParams: TFontParameters;
  LTechString: string;
begin
  FSwapChain2.BeginScene;
  try
    FDevice.Clear([TClearLayer.Color], FloatColor($FFE0E0E0));

    // Render some 2D stuff.
    FCanvas.BeginScene;
    try
      FCanvas.Attributes := [];
      FCanvas.ContextState := TCanvasContextState.FlatScene;

      FCanvas.Ribbon(Point2f(RightPanel.ClientWidth * 0.5, RightPanel.ClientHeight * 0.5),
        Point2f(RightPanel.ClientWidth * 0.4, RightPanel.ClientHeight * 0.4),
        Point2f(RightPanel.ClientWidth * 0.6, RightPanel.ClientHeight * 0.6),
        64, ColorRect($FF00FF00, $FFFF0000, $FF0000FF, $FFFFFFFF));

      FCanvas.SamplerState := TCanvasSamplerState.Create(TTextureFilter.Nearest, TTextureFilter.Nearest);
      FCanvas.ContextState := TCanvasContextState.FlatText;

      LFontParams := TFontParameters.Create('Arial', 16.0 * FDisplayScale, TFontWeight.SemiBold);
      LFontParams.Effect.BorderType := TFontBorder.SemiHeavy;
      LFontParams.Effect.BorderBrightness := 1.0;
      LFontParams.Effect.FillBrightness := 0.0;
      LFontParams.Effect.ShadowBrightness := 0.75;
      LFontParams.Effect.ShadowOpacity := 1.0;
      FTextRenderer.FontParameters := LFontParams;

      FTextRenderer.DrawAligned(Point2f(RightPanel.ClientWidth * 0.5, RightPanel.ClientHeight * 0.5),
        'This is right panel.', $A0FFFFFF, TTextAlignment.Middle, TTextAlignment.Middle);

      // Draw technology string.
      LFontParams := TFontParameters.Create('Segoe UI', 16.0 * FDisplayScale, TFontWeight.SemiBold);
      LFontParams.Effect.BorderType := TFontBorder.SemiHeavy;
      LFontParams.Effect.BorderBrightness := 1.0;
      LFontParams.Effect.FillBrightness := 0.0;
      LFontParams.Effect.ShadowBrightness := 0.75;
      LFontParams.Effect.ShadowOpacity := 1.0;
      FTextRenderer.FontParameters := LFontParams;

      FTextRenderer.DrawAligned(Point2f(RightPanel.ClientWidth * 0.5, 4.0 * FDisplayScale),
        'Frame Rate: ' + IntToStr(Round(FTimer.FrameRate)), $A0FFFFFF,
        TTextAlignment.Middle, TTextAlignment.Start);

      if FDevice.Technology = TDeviceTechnology.Direct3D then
        LTechString := 'Direct3D ('
      else
        LTechString := 'OpenGL (';

      LTechString := LTechString + IntToStr(FDevice.TechVersion shr 8) + '.' +
        IntToStr((FDevice.TechVersion shr 4) and $0F) + '+), features: ';

      LTechString := LTechString + IntToStr(FDevice.TechFeatureVersion shr 8) + '.' +
        IntToStr((FDevice.TechFeatureVersion shr 4) and $0F);

      FTextRenderer.DrawAligned(Point2f(RightPanel.ClientWidth * 0.5, RightPanel.ClientHeight - 4.0 * FDisplayScale),
        LTechString, $A0FFFFFF, TTextAlignment.Middle, TTextAlignment.Ending);
    finally
      FCanvas.EndScene;
    end;
  finally
    FSwapChain2.EndScene;
  end;
end;

end.
