ScreenshotUtility.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // Copyright 2022 RLoris
  2. #include "ScreenshotUtility.h"
  3. #include "ImageUtils.h"
  4. #include "UnrealClient.h"
  5. #include "Components/SceneCaptureComponent2D.h"
  6. #include "Kismet/GameplayStatics.h"
  7. #include "Engine/TextureRenderTarget2D.h"
  8. UScreenshotUtility* UScreenshotUtility::TakeScreenshot(UObject* WorldContextObject, FString Filename, bool PrefixTimestamp, bool ShowUI)
  9. {
  10. UScreenshotUtility* Node = NewObject<UScreenshotUtility>();
  11. Node->Active = false;
  12. Node->WorldContextObject = WorldContextObject;
  13. Node->ShowUI = ShowUI;
  14. Node->Filename = FPaths::GetCleanFilename(Filename);
  15. Node->PrefixTimestamp = PrefixTimestamp;
  16. // not garbage collected
  17. Node->AddToRoot();
  18. return Node;
  19. }
  20. void UScreenshotUtility::Activate()
  21. {
  22. if (nullptr == this->WorldContextObject)
  23. {
  24. FFrame::KismetExecutionMessage(TEXT("Invalid WorldContextObject. Cannot execute ScreenshotUtility"), ELogVerbosity::Error);
  25. _Failed();
  26. return;
  27. }
  28. if (this->Active)
  29. {
  30. FFrame::KismetExecutionMessage(TEXT("ScreenshotUtility is already running"), ELogVerbosity::Warning);
  31. _Failed();
  32. return;
  33. }
  34. FText ErrorFilename;
  35. Filename = FPaths::GetCleanFilename(Filename);
  36. if (!FFileHelper::IsFilenameValidForSaving(Filename, ErrorFilename))
  37. {
  38. FFrame::KismetExecutionMessage(TEXT("Filename is not valid"), ELogVerbosity::Warning);
  39. _Failed();
  40. return;
  41. }
  42. const FString FinalFilename = (this->PrefixTimestamp ? (FDateTime::Now().ToString(TEXT("%Y_%m_%d__%H_%M_%S__"))) : "") + this->Filename;
  43. if (!FScreenshotRequest::IsScreenshotRequested())
  44. {
  45. this->Active = true;
  46. ScreenshotTexture = nullptr;
  47. if (ShowUI)
  48. {
  49. FScreenshotRequest::Reset();
  50. FScreenshotRequest::RequestScreenshot(FinalFilename, this->ShowUI, false);
  51. FScreenshotRequest::OnScreenshotRequestProcessed().RemoveAll(this);
  52. FScreenshotRequest::OnScreenshotRequestProcessed().AddUObject(this, &UScreenshotUtility::_Completed, FScreenshotRequest::GetFilename());
  53. }
  54. else
  55. {
  56. CreateScreenshotWithoutUI(FinalFilename);
  57. }
  58. }
  59. }
  60. void UScreenshotUtility::_Completed(const FString Path)
  61. {
  62. IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
  63. if (!ScreenshotTexture && FileManager.FileExists(*Path))
  64. {
  65. ScreenshotTexture = FImageUtils::ImportFileAsTexture2D(Path);
  66. }
  67. if (ScreenshotTexture)
  68. {
  69. Completed.Broadcast(ScreenshotTexture, Path);
  70. }
  71. else
  72. {
  73. ScreenshotTexture = nullptr;
  74. Failed.Broadcast(ScreenshotTexture, TEXT(""));
  75. }
  76. this->Active = false;
  77. FScreenshotRequest::OnScreenshotRequestProcessed().RemoveAll(this);
  78. RemoveFromRoot();
  79. }
  80. void UScreenshotUtility::_Failed()
  81. {
  82. ScreenshotTexture = nullptr;
  83. Failed.Broadcast(ScreenshotTexture, TEXT(""));
  84. this->Active = false;
  85. FScreenshotRequest::OnScreenshotRequestProcessed().RemoveAll(this);
  86. RemoveFromRoot();
  87. }
  88. void UScreenshotUtility::CreateScreenshotWithoutUI(const FString& FinalName)
  89. {
  90. const UWorld* World = WorldContextObject->GetWorld();
  91. if (!World || !World->GetGameViewport() || !World->GetGameViewport()->Viewport)
  92. {
  93. _Failed();
  94. return;
  95. }
  96. const APlayerCameraManager * PlayerCamera = UGameplayStatics::GetPlayerCameraManager(WorldContextObject, 0);
  97. if (!PlayerCamera)
  98. {
  99. _Failed();
  100. return;
  101. }
  102. const FViewport* GameViewport = World->GetGameViewport()->Viewport;
  103. const FIntRect ViewRect(0, 0, GameViewport->GetSizeXY().X, GameViewport->GetSizeXY().Y);
  104. USceneCaptureComponent2D* SceneComponent = NewObject<USceneCaptureComponent2D>(this, TEXT("SceneComponent"));
  105. SceneComponent->RegisterComponentWithWorld(WorldContextObject->GetWorld());
  106. SceneComponent->bCaptureEveryFrame = false;
  107. SceneComponent->bCaptureOnMovement = false;
  108. SceneComponent->bAlwaysPersistRenderingState = true;
  109. SceneComponent->CaptureSource = ESceneCaptureSource::SCS_FinalColorHDR;
  110. SceneComponent->FOVAngle = PlayerCamera->GetFOVAngle();
  111. const FVector CameraLocation = PlayerCamera->GetCameraLocation();
  112. const FRotator CameraRotation = PlayerCamera->GetCameraRotation();
  113. SceneComponent->SetWorldLocationAndRotation(CameraLocation, CameraRotation);
  114. UTextureRenderTarget2D * TextureRenderTarget = NewObject<UTextureRenderTarget2D>();
  115. TextureRenderTarget->InitCustomFormat(ViewRect.Width(),ViewRect.Height(),PF_B8G8R8A8,false);
  116. TextureRenderTarget->UpdateResourceImmediate();
  117. SceneComponent->TextureTarget = TextureRenderTarget;
  118. SceneComponent->CaptureScene();
  119. TArray<FColor> OutColors;
  120. OutColors.Reserve(ViewRect.Width() * ViewRect.Height());
  121. TextureRenderTarget->GameThread_GetRenderTargetResource()->ReadPixels(OutColors);
  122. OutColors.Shrink();
  123. SceneComponent->UnregisterComponent();
  124. if (OutColors.Num() == 0)
  125. {
  126. _Failed();
  127. return;
  128. }
  129. const FString ScreenshotPath = FPaths::ScreenShotDir() + FinalName;
  130. TArray<uint8> OutImage;
  131. FImageUtils::ThumbnailCompressImageArray(ViewRect.Width(), ViewRect.Height(), OutColors, OutImage);
  132. if (OutImage.Num() == 0)
  133. {
  134. _Failed();
  135. return;
  136. }
  137. if(!FFileHelper::SaveArrayToFile(OutImage, *ScreenshotPath))
  138. {
  139. _Failed();
  140. return;
  141. }
  142. ScreenshotTexture = FImageUtils::ImportBufferAsTexture2D(OutImage);
  143. _Completed(ScreenshotPath);
  144. }