Skip to content

Instantly share code, notes, and snippets.

@viniciusfbb
Last active October 15, 2024 03:45
Show Gist options
  • Save viniciusfbb/699a18e9fe982ad99e5dd65a7342893c to your computer and use it in GitHub Desktop.
Save viniciusfbb/699a18e9fe982ad99e5dd65a7342893c to your computer and use it in GitHub Desktop.
Delphi capture screenshot from android Firemonkey form
unit PixelCopy.Android;
interface
uses
Androidapi.JNI.GraphicsContentViewText,
FMX.Graphics;
function CaptureWindowPixels(const AWindow: JWindow): TBitmap;
function CaptureSurfaceHolderPixels(const ASurfaceHolder: JSurfaceHolder): TBitmap;
implementation
uses
System.SysUtils,
System.SyncObjs,
Androidapi.JNIBridge,
Androidapi.JNI.Os,
Androidapi.JNI.JavaTypes,
Androidapi.Helpers,
FMX.Helpers.Android;
type
JPixelCopy = interface;
JPixelCopy_OnPixelCopyFinishedListener = interface;
JPixelCopyClass = interface(JObjectClass)
['{A6FA8C65-EBA0-4674-9C55-59F196FEC13C}']
procedure request(source: JSurfaceView; dest: JBitmap; listener: JPixelCopy_OnPixelCopyFinishedListener; handler: JHandler); cdecl; overload;
procedure request(source: JSurface; dest: JBitmap; listener: JPixelCopy_OnPixelCopyFinishedListener; handler: JHandler); cdecl; overload;
procedure request(source: JWindow; dest: JBitmap; listener: JPixelCopy_OnPixelCopyFinishedListener; handler: JHandler); cdecl; overload;
function _GetSUCCESS: Integer; cdecl;
property SUCCESS: Integer read _GetSUCCESS;
end;
[JavaSignature('android/view/PixelCopy')]
JPixelCopy = interface(JObject)
['{C7F027B5-12F5-4AA0-9154-2FF48CC46EFD}']
end;
TJPixelCopy = class(TJavaGenericImport<JPixelCopyClass, JPixelCopy>) end;
JPixelCopy_OnPixelCopyFinishedListenerClass = interface(IJavaClass)
['{4508A3EB-846E-4517-B91E-718D1C4DA8BD}']
end;
[JavaSignature('android/view/PixelCopy$OnPixelCopyFinishedListener')]
JPixelCopy_OnPixelCopyFinishedListener = interface(IJavaInstance)
['{9F08ECAF-3A32-43F5-935D-5AA013C48BFE}']
procedure onPixelCopyFinished(copyResult: Integer); cdecl;
end;
TJPixelCopy_OnPixelCopyFinishedListener = class(TJavaGenericImport<JPixelCopy_OnPixelCopyFinishedListenerClass, JPixelCopy_OnPixelCopyFinishedListener>) end;
type
TPixelCopyFinishedListener = class(TJavaLocal, JPixelCopy_OnPixelCopyFinishedListener)
private
FOnFinished: TProc<Integer>;
public
constructor Create(const AOnFinished: TProc<Integer>);
procedure onPixelCopyFinished(copyResult: Integer); cdecl;
end;
{ TPixelCopyFinishedListener }
constructor TPixelCopyFinishedListener.Create(const AOnFinished: TProc<Integer>);
begin
inherited Create;
FOnFinished := AOnFinished;
end;
procedure TPixelCopyFinishedListener.onPixelCopyFinished(copyResult: Integer);
begin
FOnFinished(copyResult);
end;
const
CAPTURE_TIMEOUT_MS = 3000;
function CaptureWindowPixels(const AWindow: JWindow): TBitmap;
var
Bitmap: JBitmap;
DecorView: JView;
Event: TEvent;
Handler: JHandler;
Listener: JPixelCopy_OnPixelCopyFinishedListener;
Success: Boolean;
SuccessPointer: PBoolean;
Thread: JHandlerThread;
begin
if AWindow = nil then
Exit(nil);
DecorView := AWindow.getDecorView;
if (DecorView = nil) or (DecorView.getWidth <= 0) or (DecorView.getHeight <= 0) then
Exit(nil);
Bitmap := TJBitmap.JavaClass.createBitmap(DecorView.getWidth, DecorView.getHeight,
TJBitmap_Config.JavaClass.ARGB_8888);
Event := TEvent.Create(nil, False, False, '');
try
Thread := TJHandlerThread.JavaClass.init(StringToJString('FMXScreenshot'));
Thread.start;
try
Handler := TJHandler.JavaClass.init(Thread.getLooper);
SuccessPointer := @Success;
Listener := TPixelCopyFinishedListener.Create(
procedure(ACopyResult: Integer)
begin
SuccessPointer^ := ACopyResult = TJPixelCopy.JavaClass.SUCCESS;
Event.SetEvent;
end);
TJPixelCopy.JavaClass.request(AWindow, Bitmap, Listener, Handler);
if (Event.WaitFor(CAPTURE_TIMEOUT_MS) = TWaitResult.wrSignaled) and Success then
begin
Result := TBitmap.Create;
if not JBitmapToBitmap(Bitmap, Result) then
FreeAndNil(Result);
end
else
Result := nil;
finally
Thread.quit;
end;
finally
Event.Free;
end;
end;
function CaptureSurfaceHolderPixels(const ASurfaceHolder: JSurfaceHolder): TBitmap;
var
Bitmap: JBitmap;
DecorView: JView;
Event: TEvent;
Handler: JHandler;
Listener: JPixelCopy_OnPixelCopyFinishedListener;
Success: Boolean;
SuccessPointer: PBoolean;
Thread: JHandlerThread;
begin
if (ASurfaceHolder = nil) or (ASurfaceHolder.getSurfaceFrame.width <= 0) or
(ASurfaceHolder.getSurfaceFrame.height <= 0) then
begin
Exit(nil);
end;
Bitmap := TJBitmap.JavaClass.createBitmap(ASurfaceHolder.getSurfaceFrame.width, ASurfaceHolder.getSurfaceFrame.height,
TJBitmap_Config.JavaClass.ARGB_8888);
Event := TEvent.Create(nil, False, False, '');
try
Thread := TJHandlerThread.JavaClass.init(StringToJString('FMXScreenshot'));
Thread.start;
try
Handler := TJHandler.JavaClass.init(Thread.getLooper);
SuccessPointer := @Success;
Listener := TPixelCopyFinishedListener.Create(
procedure(ACopyResult: Integer)
begin
SuccessPointer^ := ACopyResult = TJPixelCopy.JavaClass.SUCCESS;
Event.SetEvent;
end);
TJPixelCopy.JavaClass.request(ASurfaceHolder.getSurface, Bitmap, Listener, Handler);
if (Event.WaitFor(CAPTURE_TIMEOUT_MS) = TWaitResult.wrSignaled) and Success then
begin
Result := TBitmap.Create;
if not JBitmapToBitmap(Bitmap, Result) then
FreeAndNil(Result);
end
else
Result := nil;
finally
Thread.quit;
end;
finally
Event.Free;
end;
end;
end.
...
uses
PixelCopy.Android, FMX.Platform.Android, FMX.Platform.UI.Android;
procedure TForm1.Button1Click(Sender: TObject);
begin
Rectangle1.Fill.Bitmap.Bitmap := CaptureWindowPixels(MainActivity.getWindow);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Rectangle1.Fill.Bitmap.Bitmap := CaptureSurfaceHolderPixels(TAndroidWindowHandle(Screen.ActiveForm.Handle).Holder);
end;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment