Need help to find FireMonkey equivalent of the VCL TpaintBox.Canvas.Handle in the context of building graph

1.5k views Asked by At

Preambule: I'm working with Black Magic Design (BMD) Decklink input card to acquire HD video signal. They provide C++ Sample with their SDK. I've successfully translated the c++ sample into Delphi (VCL). I've also isolated the API call in a TDecklink witch I want it to be available to the Delphi community. It work very well in VCL (I can provide the TDecklnk with a demo app to use it if requested).

Now I need to acquire the signal in a FMX form (but not crosscompile to other platform than Windows). I've tried to modify the TDecklink to be usable in FMX without success.

Core Question:
In my VCL version, I pass a TPaintBox refference to my TDecklink. The TPaintBox is used by the GraphBuilder as area to display the live video.

Here is some line of code I use in the VCL version to assign the TPaintBox to the GraphBuilder:

      pWnd := WindowFromDC(FpboxPreview.Canvas.Handle);   //WindowFromDC  retreive HWND from HDC
      hr:= pIVMRWindowlessCtrl.SetVideoClippingWindow(pWnd);   // set the bounds of the video to the preview window
      if hr = S_OK then
      begin
        previewRect.Left   := FpboxPreview.Left;
        previewRect.Right  := FpboxPreview.Width;
        previewRect.Top    := FpboxPreview.Top;
        previewRect.Bottom := FpboxPreview.Height;
        hr:= pIVMRWindowlessCtrl.SetVideoPosition(nil, @previewRect);   // show the whole of the source frame in the whole of the client area of the control
        hr:= pIVMRWindowlessCtrl.SetAspectRatioMode(VMR_ARMODE_LETTER_BOX); // maintain the aspect ratio of the video
        hr:= pIVMRWindowlessCtrl.SetBorderColor(GetSysColor(COLOR_BTNFACE));    // set the colour of the letter or pillar boxed area 

Where PWnd is a HWND

In FMX, what is the best component and parameter to use to provide what the GraphBuilder expect to receive ?

1

There are 1 answers

7
Remy Lebeau On BEST ANSWER

In VCL, TPaintBox is a TGraphicControl descendant that draws onto the HDC of its Parent control's HWND. When the Parent control receives a WM_PAINT message, it draws itself onto the provided HDC as needed, and then temporarily gives that same HDC to each child TGraphicControl when drawing them, clipping the HDC to each child's coordinates and rectangle accordingly. If you try to draw onto a TGraphicControl.Canvas from outside of its Parent control's WM_PAINT handler (which you should never do), TCanvas will temporarily grab the Parent control's HDC using the Win32 API GetDC() function.

Thus, this statement:

pWnd := WindowFromDC(FpboxPreview.Canvas.Handle);

Is effectively the same as this:

pWnd := FpboxPreview.Parent.Handle;

So, you are actually putting your video on the window of the TPaintBox.Parent control, not on the TPaintBox itself. If you want the video associated with its own control, consider using TPanel instead, as it is a TWinControl descendant with its own HWND.

FireMonkey, on the other hand, has no concept of TGraphicControl and TWinControl. Every control is a TControl descendant with an overridden Paint() method to handle any custom drawing onto a TCanvas that is provided by either the parent TForm or the caller of the TControl.PaintTo() method. FireMonkey does not even create an HWND for each control. Only the parent TForm has its own HWND (so it can interact with the OS). Child controls are drawn directly onto that window, adjusting the drawing coordinates and clipping rectangle accordingly as they go along (under the hood, FireMonkey uses DirectX (Windows) or OpenGL (other platforms) for all of its drawing).

So, if you really need an HWND for your video class to display on, you will have to either:

  1. use the HWND of a TForm, which you can get by either passing its Handle property to the FMX.Platform.Win.WindowHandleToPlatform() function (or the FMX.Platform.Win.FmxHandleToHWND() function on older FireMonkey versions):

    uses
      ..., FMX.Platform.Win;
    
    pWnd := WindowHandleToPlatform(Form1.Handle);
    

    Or passing the TForm itself to the FMX.Platform.Win.FormToHWND() function:

    uses
      ..., FMX.Platform.Win;
    
    pWnd := FormToHWND(Form1);
    
  2. use the Win32 API directly to create your own HWND as needed and then embed it inside the HWND of a TForm.

Otherwise, you will have to re-think your video UI in FireMonkey. For instance, assuming the video class can provide you with images of the video frames, you can draw them onto the TPaintBox.Canvas from within the TPaintBox.OnPaint event (which is how TPaintBox is meant to be used in the first place, in both VCL and FireMonkey). Or maybe derive your own custom TControl that pulls images from the video class in its own overridden Paint() method. I don't know what your GraphBuilder class is capable of, but BMD provides an SDK for controlling video recording/playback hardware and accessing video data (see this PDF).