quick code tutorial, how to get a screenshot and write the texture data to a file

Hi all! :slight_smile: It seems that some of the screenshot functionality isn’t working entirely as intended in the normal setup right now, particularly with regards to saving screenshots to a specific directory/filename. Therefore, I had to do a fair amount of research, came across a bunch of different things that didn’t exactly work right, and came up with the following, which I thought might serve to help both people who want to take screenshots, and people who want to save texture data, and neither of those operations seem to be 100% obvious.

To save bitmap data using this, you’ll need the X and Y dimensions of the bitmap data, a TArray<FColor> of the bitmap data, and the FString path you want to store it in.



// i threw this into a static utility class, because I have a feeling it's going to be very useful in the future
bool UUtils::SaveBitmapAsPNG(int32 sizeX, int32 sizeY, const TArray<FColor>& bitmapData, const FString& filePath) {
    TArray<uint8> compressedBitmap;
    FImageUtils::CompressImageArray(sizeX, sizeY, bitmapData, compressedBitmap);
    return FFileHelper::SaveArrayToFile(compressedBitmap, *filePath);
}


Then, in the class where you are wanting to handle your screenshotting, you’ll need to make two functions, one that requests the screenshot using FScreenshotRequest, and one that receives the screenshot data in the next frame, when the screenshot is actually completed.



static FString MyPlayerController::cachedScreenShotLocation = "";

void MyPlayerController::GetShot(FString path) {
    cachedScreenShotLocation = path;
    UGameViewportClient* GameViewportClient = GEngine->GameViewport;
    // register notification for when the next screenshot is completed
    GameViewportClient->OnScreenshotCaptured().AddUObject(this, &MyPlayerController::OnScreenShotCaptured);
    FScreenshotRequest::RequestScreenshot(path, false, false);
}

void MyPlayerController::OnScreenShotCaptured(int32 InSizeX, int32 InSizeY, const TArray<FColor>& InImageData) {
    // remove notification for when shot is completed, we don't care if someone else requested a screenshot, just
    // the most recent request that came in involving this object.
    GEngine->GameViewport->OnScreenshotCaptured().RemoveAll(this);
    if (!UIIDUtils::SaveBitmapAsPNG(InSizeX, InSizeY, InImageData, cachedScreenShotLocation)) {
        UE_LOG(LogTemp, Error, TEXT("Error saving screenshot to %s"), *cachedScreenShotLocation);
    } else {
        UE_LOG(LogTemp, Warning, TEXT("screenshot saved to %s"), *cachedScreenShotLocation);
    }
}


Hi, thanks for posting this code! I’m trying to implement it, but I’m new to Unreal. I’ve gotten my code to compile, and I can call the function, using the python command line in the output log, but the function GetShot (aka get_shot in Python) does not seem to actually run.

Here is my header…


#pragma once

#include "UnrealClient.h"
#include "ImageUtils.h"

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "EditorScreenshotWithUI.generated.h"

UCLASS(Blueprintable)
class AScreenshotWithUI : public AActor //UBlueprintFunctionLibrary
{
GENERATED_BODY()

public:

UFUNCTION(BlueprintCallable, Category = "Steve")
bool SaveBitmapAsPNG(int32 sizeX, int32 sizeY, const TArray<FColor>& bitmapData, const FString& filePath);

UFUNCTION(BlueprintCallable, Category="Steve")
void GetShot(FString path);

UFUNCTION(BlueprintCallable, Category = "Steve")
void OnScreenShotCaptured(int32 InSizeX, int32 InSizeY, const TArray<FColor>& InImageData);

static FString cachedScreenShotLocation;

};

…and here is my .cpp code…


#include "EditorScreenshotWithUI.h"
#include "Misc/FileHelper.h"
#include "GameFramework/PlayerController.h"
#include "Engine/GameEngine.h"
#include "Engine/GameViewportClient.h"


bool AScreenshotWithUI::SaveBitmapAsPNG(int32 sizeX, int32 sizeY, const TArray<FColor>& bitmapData, const FString& filePath) {
TArray<uint8> compressedBitmap;
FImageUtils::CompressImageArray(sizeX, sizeY, bitmapData, compressedBitmap);
return FFileHelper::SaveArrayToFile(compressedBitmap, *filePath);
}
// Then, in the class where you are wanting to handle your screenshotting, you'll need to make two functions,
// one that requests the screenshot using FScreenshotRequest, and one that receives the screenshot data in the
// next frame, when the screenshot is actually completed.

FString AScreenshotWithUI::cachedScreenShotLocation = ""; // this was static in the original. Hmmm.


void AScreenshotWithUI::GetShot(FString path) {
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "debug msg from GetShot");
UE_LOG(LogTemp, Warning, TEXT("GetShot received: %s"), *path);
cachedScreenShotLocation = path;
UGameViewportClient* GameViewportClient = GEngine->GameViewport;
// register notification for when the next screenshot is completed
//GameViewportClient->OnScreenshotCaptured().AddUObject(this, &SnapshotWithUI::OnScreenShotCaptured);
GameViewportClient->OnScreenshotCaptured().AddUObject(this, &AScreenshotWithUI::OnScreenShotCaptured);
FScreenshotRequest::RequestScreenshot(path, true, false);
}

void AScreenshotWithUI::OnScreenShotCaptured(int32 InSizeX, int32 InSizeY, const TArray<FColor>& InImageData) {
UE_LOG(LogTemp, Warning, TEXT("hello from OnScreenShotCaptured"));
// remove notification for when shot is completed, we don't care if someone else requested a screenshot, just
// the most recent request that came in involving this object.
GEngine->GameViewport->OnScreenshotCaptured().RemoveAll(this);
if (!SaveBitmapAsPNG(InSizeX, InSizeY, InImageData, cachedScreenShotLocation)) {
UE_LOG(LogTemp, Error, TEXT("Error saving screenshot to %s"), *cachedScreenShotLocation);
} else {
UE_LOG(LogTemp, Warning, TEXT("screenshot saved to %s"), *cachedScreenShotLocation);
}
}

To call get_shot I am creating an instance of ScreenshotWithUI…

snappy = unreal.ScreenshotWithUI()

…and then calling the get_shot() method on it…

snappy.get_shot(“C:/Users/jp/Documents//snappy.png”)

Questions:

  1. Why does get_shot not seem to run? I get no Log or OnScreen messages.
  2. Where is InImageData going to come from?
  3. help

I got the above code to work. Note that the InImageData, resolution etc. are provided automatically somehow. It just works.

Next question - I need to screenshot the stat GPU that is on the screen during PIE. fScreenshotRerquest::RequestScreenshot has a flag to turn on the capturing of SlateUI. I learned the hard way that stat GPU does not produce SlateUI. If anyone knows how to screenshot the viewport INCLUDING stat GPU, please advise.

Hey there, sorry I’ve been away for a long time. I have no idea on the python questions :smiley: Yes, what you’re doing there is calling GetShot which tells GameViewportClient to call OnScreenShotCaptured() with all the data that it gets.

I have doubts that there’s any way to get it to capture the stat stuff, unless you go even lower level somewhere. My gut feel is i’d use an external screenshot utility for that.