最近工作中使用UE
截图的需求,就寻找并学习了下,将实现记录一下,记个笔记!!!
工具版本
UE: 5.2.1
Rider: 2023.3.6
VS2022: 17.4.0
ScreenShotSubsystem.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "ScreenShotSubsystem.generated.h"
class UTexture2D;
UENUM(BlueprintType)
enum class EScreenShotType : uint8
{
ESST_Normal UMETA(DisplayName = "普通截图(不带UI)"),
ESST_NormalWithUI UMETA(DisplayName = "普通截图(带UI)"),
ESST_HighResShot UMETA(DisplayName = "高精度截图(不带UI)")
};
UENUM(BlueprintType)
enum class EScreenShotSuffix : uint8
{
ESSS_PNG UMETA(DisplayName = "PNG"),
ESSS_JPG UMETA(DisplayName = "JPG")
};
UENUM(BlueprintType)
enum class EFixType : uint8
{
EFT_Prefix UMETA(DisplayName = "前缀"),
EFT_Suffix UMETA(DisplayName = "后缀")
};
USTRUCT(BlueprintType)
struct FResolution
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ScreenSize")
int32 Width{0};
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ScreenSize")
int32 Height{0};
};
USTRUCT(BlueprintType)
struct FScreenShotParams
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, Category = "SSParams")
EScreenShotType ScreenShotType {EScreenShotType::ESST_Normal};
UPROPERTY(BlueprintReadWrite, Category = "SSParams")
EScreenShotSuffix SSSuffix {EScreenShotSuffix::ESSS_PNG};
UPROPERTY(BlueprintReadWrite, Category = "SSParams")
FString Filename {FString()};
UPROPERTY(BlueprintReadWrite, Category = "SSParams")
FString SavedFilePath {FString()};
UPROPERTY(BlueprintReadWrite, Category = "SSParams", meta=(EditConditionHides = "ScreenShotType=EScreenShotType::ESST_HighResShot"))
FResolution Resolution {FResolution()};
FString ReturnFilePath {FString()};
};
/**
*
*/
UCLASS()
class SCREENSHOT_API UScreenShotSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
static FDelegateHandle DelegateHandle;
// 从本地路径导入图片并转换为Texture2D格式
UFUNCTION(BlueprintCallable, Category = "LoadAssets")
UTexture2D* LoadTexture2DFromLocal(UPARAM(ref) FString& ImagePath) const;
// 实现截图
UFUNCTION(BlueprintCallable, Category = "ScreenShot")
FString CaptureImage(UPARAM(ref) FScreenShotParams& SSParams);
// 获取同意文件夹下的某前缀或后缀的文件
UFUNCTION(BlueprintCallable, Category = "Tools")
TArray<FString> GetLocalAllFiles(UPARAM(ref) FString& FilePath, const FString FileFix, const EFixType FixType = EFixType::EFT_Suffix);
private:
static void OnScreenshotCompleteImage(int32 InWidth, int32 InHeight, const TArray<FColor>& InColors, const FScreenShotParams SSParams);
};
ScreenShotSubsystem.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ScreenShot/Subsystems/ScreenShotSubsystem.h"
#include "IImageWrapper.h"
#include "IImageWrapperModule.h"
FDelegateHandle UScreenShotSubsystem::DelegateHandle = {};
void UScreenShotSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
}
void UScreenShotSubsystem::Deinitialize()
{
Super::Deinitialize();
}
bool UScreenShotSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
TArray<UClass*> ChildClasses;
GetDerivedClasses(GetClass(), ChildClasses, false);
return ChildClasses.Num() == 0;
}
UTexture2D* UScreenShotSubsystem::LoadTexture2DFromLocal(FString& ImagePath) const
{
TArray<FString> ImageFiles;
FPaths::NormalizeDirectoryName(ImagePath);
FString FinalPath = FPaths::ProjectContentDir() + ImagePath;
IFileManager& FileManager = IFileManager::Get();
FileManager.FindFiles(ImageFiles, *FinalPath, true, true);
if (ImageFiles.Num() > 0)
{
FString ImgPath = ImageFiles[0];
TArray<uint8> CompressedData;
if (FFileHelper::LoadFileToArray(CompressedData, *FinalPath))
{
EImageFormat ImageFormat = EImageFormat::Invalid;
if (ImgPath.EndsWith(".png"))
{
ImageFormat = EImageFormat::PNG;
}
else if (ImgPath.EndsWith(".jpg") || ImgPath.EndsWith(".jpeg") || ImgPath.EndsWith(".PNG") || ImgPath.
EndsWith(".JPEG"))
{
ImageFormat = EImageFormat::JPEG;
}
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(
FName("ImageWrapper"));
TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat);
if (ImageWrapper->SetCompressed(CompressedData.GetData(), CompressedData.Num()))
{
TArray<uint8> UncompressedRGBA;
if (ImageWrapper->GetRaw(ERGBFormat::RGBA, 8, UncompressedRGBA))
{
TObjectPtr<UTexture2D> Texture2D = UTexture2D::CreateTransient(
ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_R8G8B8A8);
void* TextureData = Texture2D->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
FMemory::Memcpy(TextureData, UncompressedRGBA.GetData(), UncompressedRGBA.Num());
Texture2D->PlatformData->Mips[0].BulkData.Unlock();
Texture2D->UpdateResource();
return Texture2D.Get();
}
}
}
}
return nullptr;
}
FString UScreenShotSubsystem::CaptureImage(FScreenShotParams& SSParams)
{
if (!GEngine || !GEngine->GameViewport)
{
return FString();
}
const FString SavedFilePath = !SSParams.SavedFilePath.IsEmpty()
? SSParams.SavedFilePath
: FString::Printf(TEXT("%s/Screenshots/"), *FPaths::ProjectContentDir());
FString ReturnFilePath = FString::Printf(TEXT("%s/%s"), *SavedFilePath, *SSParams.Filename);
if (SSParams.SSSuffix == EScreenShotSuffix::ESSS_JPG)
{
ReturnFilePath = FString::Printf(TEXT("%s%s"), *ReturnFilePath, *FString(".jpg"));
}
else if (SSParams.SSSuffix == EScreenShotSuffix::ESSS_PNG)
{
ReturnFilePath = FString::Printf(TEXT("%s%s"), *ReturnFilePath, *FString(".png"));
}
SSParams.ReturnFilePath = ReturnFilePath;
DelegateHandle = UGameViewportClient::OnScreenshotCaptured().AddStatic(&OnScreenshotCompleteImage, SSParams);
if (SSParams.ScreenShotType == EScreenShotType::ESST_Normal)
{
if (GEngine->GameViewport->Exec(nullptr, TEXT("Shot"), *GLog))
{
return ReturnFilePath;
}
}
else if (SSParams.ScreenShotType == EScreenShotType::ESST_NormalWithUI)
{
if (GEngine->GameViewport->Exec(nullptr, TEXT("Shot showui"), *GLog))
{
return ReturnFilePath;
}
}
else if (SSParams.ScreenShotType == EScreenShotType::ESST_HighResShot)
{
const FString Command = FString::Printf(
TEXT("HighResShot %dx%d"), SSParams.Resolution.Width, SSParams.Resolution.Height);
if (GEngine->GameViewport->Exec(nullptr, *Command, *GLog))
{
return ReturnFilePath;
}
}
return FString();
}
void UScreenShotSubsystem::OnScreenshotCompleteImage(int32 InWidth, int32 InHeight, const TArray<FColor>& InColors,
const FScreenShotParams SSParams)
{
IImageWrapperModule& ImageWrapperModule = FModuleManager::Get().LoadModuleChecked<IImageWrapperModule>(
FName("ImageWrapper"));
TSharedPtr<IImageWrapper> ImageWrapper = nullptr;
if (SSParams.SSSuffix == EScreenShotSuffix::ESSS_JPG)
{
ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG);
}
else if (SSParams.SSSuffix == EScreenShotSuffix::ESSS_PNG)
{
ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
}
if (ImageWrapper->SetRaw(InColors.GetData(), InColors.Num() * sizeof(FColor),
InWidth, InHeight, ERGBFormat::BGRA, 8))
{
FFileHelper::SaveArrayToFile(ImageWrapper->GetCompressed(), *SSParams.ReturnFilePath);
}
UGameViewportClient::OnScreenshotCaptured().Remove(DelegateHandle);
}
TArray<FString> UScreenShotSubsystem::GetLocalAllFiles(FString& FilePath, const FString FileFix,
const EFixType FixType)
{
TArray<FString> Files = TArray<FString>();
FPaths::NormalizeDirectoryName(FilePath);
IFileManager& FileManager = IFileManager::Get();
const FString FinalPath = FilePath / TEXT("*");
FileManager.FindFiles(Files, *FinalPath, true, true);
TArray<FString> ReturnFiles = TArray<FString>();
if (FixType == EFixType::EFT_Prefix)
{
for (const FString File : Files)
{
if(File.StartsWith(*FileFix))
{
ReturnFiles.Add(File);
}
}
}
else if (FixType == EFixType::EFT_Suffix)
{
for (const FString File : Files)
{
if(File.EndsWith(*FileFix))
{
ReturnFiles.Add(File);
}
}
}
return ReturnFiles;
}