UEでは、さまざまな画像 (UTexture2D) の操作 (読み込み、保存、コピー、クリップボード...) を実現します。
以下のコードはすべてUE5.3バージョンを例にしています。
ソースコード
UE商店でAICChatPlus
指定テキストを日本語に翻訳してください:UEがローカルシステムの画像をUTexture2Dとして読み込む。
通用方法
本方法は、エディターおよびゲームプレイモードの両方で利用可能で、対応する画像ファイル形式は PNG, JPEG, BMP, ICO, EXR, ICNS, HDR, TIFF, DDS, TGA であり、一般的に使用される画像タイプの大部分をカバーしています。
コードも非常にシンプルです:
#include <Engine/Texture2D.h>
#include <ImageUtils.h>
UTexture2D* LoadImage(const FString& InLoadPath)
{
FImage ImageInfo;
FImageUtils::LoadImage(*InLoadPath, ImageInfo);
return FImageUtils::CreateTexture2DFromImage(ImageInfo);
}
戻り値は UTexture2D です。
エディター専用メソッド
この方法を使用すると、さらに多くの画像タイプをサポートできます:UDIMテクスチャマップ、IESファイル、PCX、PSD。
コードの実装は少し複雑になります:
#include <Engine/Texture2D.h>
#include <Misc/FileHelper.h>
#include <Misc/Paths.h>
#include <UObject/UObjectGlobals.h>
#if WITH_EDITOR
UTexture2D* LoadImage(const FString& InLoadPath)
{
TArray64<uint8> Buffer;
if (!FFileHelper::LoadFileToArray(Buffer, *InLoadPath))
{
return nullptr;
}
const FString TextureName;
const FString Extension = FPaths::GetExtension(InLoadPath).ToLower();
const uint8* BufferPtr = Buffer.GetData();
auto TextureFact = NewObject<UTextureFactory>();
UTexture2D* Ret = Cast<UTexture2D>(TextureFact->FactoryCreateBinary(
UTexture2D::StaticClass(), GetTransientPackage(), *TextureName, RF_Transient,
NULL, *Extension, BufferPtr, BufferPtr + Buffer.Num(), GWarn));
return Ret;
}
#endif
実装は UTextureFactory の FactoryCreateBinary 関数を使用して行われました。この関数は、前述の追加ファイルタイプを読み込むことができます。
コピー:UEがUTexture2Dを複製する
時には、UTexture2Dをコピーして、そのコピーした画像を修正する必要があります。画像をコピーするには、エンジンに組み込まれている FImageCore::CopyImage
関数を使用する必要があります。2つの画像のパラメータを設定し、このインターフェイスを呼び出すだけで良いです。
UTexture2D* CopyTexture2D(UTexture2D* InTexture, UObject* Outer, FName Name, EObjectFlags Flags)
{
// src texture info, src ImageView
FTextureMipDataLockGuard InTextureGuard(InTexture);
uint8* SrcMipData = InTextureGuard.Lock(LOCK_READ_ONLY); // Texture->GetPlatformData()->Mips[0].BulkData.Lock(InLockFlag)
const int32 InSizeX = InTexture->GetSizeX();
const int32 InSizeY = InTexture->GetSizeY();
const EPixelFormat InFormat = InTexture->GetPixelFormat();
const FImageView SrcMipImage(
SrcMipData, InSizeX, InSizeY, 1, GetRawImageFormat(InFormat), InTexture->GetGammaSpace());
// create dst texture
UTexture2D* NewTexture = NewObject<UTexture2D>(Outer, Name, Flags);
NewTexture->SetPlatformData(new FTexturePlatformData());
NewTexture->GetPlatformData()->SizeX = InSizeX;
NewTexture->GetPlatformData()->SizeY = InSizeY;
NewTexture->GetPlatformData()->SetNumSlices(1);
NewTexture->GetPlatformData()->PixelFormat = InFormat;
// Allocate first mipmap.
int32 NumBlocksX = InSizeX / GPixelFormats[InFormat].BlockSizeX;
int32 NumBlocksY = InSizeY / GPixelFormats[InFormat].BlockSizeY;
FTexture2DMipMap* Mip = new FTexture2DMipMap();
Mip->SizeX = InSizeX;
Mip->SizeY = InSizeY;
Mip->SizeX = 1;
NewTexture->GetPlatformData()->Mips.Add(Mip);
Mip->BulkData.Lock(LOCK_READ_WRITE);
Mip->BulkData.Realloc((int64)NumBlocksX * NumBlocksY * GPixelFormats[InFormat].BlockBytes);
Mip->BulkData.Unlock();
// dst texture ImageView
uint8* DstMipData = static_cast<uint8*>(NewTexture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE));
const FImageView DstMipImage(
DstMipData, InSizeX, InSizeY, 1, GetRawImageFormat(InFormat), InTexture->GetGammaSpace());
// run CopyImage
FImageCore::CopyImage(SrcMipImage,DstMipImage);
#if WITH_EDITORONLY_DATA
NewTexture->Source.Init(
InSizeX, InSizeY, 1, 1,
FImageCoreUtils::ConvertToTextureSourceFormat(GetRawImageFormat(InFormat)), DstMipData);
#endif
// cleanup
NewTexture->GetPlatformData()->Mips[0].BulkData.Unlock();
NewTexture->UpdateResource();
return NewTexture;
}
保存:UE が UTexture2D をファイルに保存する実装
主要はFImageUtils::SaveImageAutoFormat
というエンジン関数を使うことで、実装は比較的簡単ですが、失敗時のリトライに注意する必要があります。
void SaveImage(UTexture2D* InImage, const FString& InSavePath)
{
if (!InImage) return;
FImage ImageInfo;
if (FImageUtils::GetTexture2DSourceImage(InImage, ImageInfo))
{
FImageUtils::SaveImageAutoFormat(*InSavePath, ImageInfo);
}
else
{
// if prev save failed
// use ConvertTextureToStandard to change InImage to Standard format, and try again
// then revert InImage's origin format
// this is what FTextureMipDataLockGuard does
FTextureMipDataLockGuard InImageGuard(InImage);
uint8* MipData = InImageGuard.Lock(LOCK_READ_ONLY);
check( MipData != nullptr );
const FImageView MipImage(
MipData, InImage->GetSizeX(), InImage->GetSizeY(), 1,
GetRawImageFormat(InImage->GetPixelFormat()), InImage->GetGammaSpace());
FImageUtils::SaveImageAutoFormat(*InSavePath, MipImage);
}
}
保存:UE における UTexture2D のアセットへの保存
UTexture2Dをメモリ内に保存し、アセットとして保存し、コンテンツブラウザで表示できます。
The core function requires the use of the aforementioned "CopyTexture2D" implementation. We need to first create a copy of the image, and then call "UPackage::SavePackage" to save the package where the image is located as an asset.
void SaveTextureToAsset(UTexture2D* InTexture)
{
if (!InTexture) return;
// open save asset dialog, choose where/which to save
FSaveAssetDialogConfig SaveAssetDialogConfig;
SaveAssetDialogConfig.DefaultPath = FEditorDirectories::Get().GetLastDirectory(ELastDirectory::NEW_ASSET);
SaveAssetDialogConfig.AssetClassNames.Add(UTexture2D::StaticClass()->GetClassPathName());
SaveAssetDialogConfig.ExistingAssetPolicy = ESaveAssetDialogExistingAssetPolicy::AllowButWarn;
SaveAssetDialogConfig.DialogTitleOverride = FAIChatPlusEditor_Constants::FCText::SaveAsAsset;
const FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
const FString SaveObjectPath = ContentBrowserModule.Get().CreateModalSaveAssetDialog(SaveAssetDialogConfig);
if (SaveObjectPath.IsEmpty()) return;
// init save info
const FString PackageName = FPackageName::ObjectPathToPackageName(SaveObjectPath);
const FString PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
const FString PackagePath = FPaths::GetPath(PackageFileName);
const FString TextureName = FPaths::GetBaseFilename(PackageName);
// create new UPackage to put the new texture in
UPackage* const NewPackage = CreatePackage(*PackageName);
NewPackage->FullyLoad();
// copy texture
UTexture2D* NewTexture = UAIChatPlus_Util::CopyTexture2D(
InTexture, NewPackage, FName(TextureName), RF_Public | RF_Standalone | RF_Transactional);
// Generate the thumbnail
// if not doing so, the texture will not have thumbnail in content browser
FObjectThumbnail NewThumbnail;
ThumbnailTools::RenderThumbnail(
NewTexture, NewTexture->GetSizeX(), NewTexture->GetSizeY(),
ThumbnailTools::EThumbnailTextureFlushMode::NeverFlush, NULL,
&NewThumbnail);
ThumbnailTools::CacheThumbnail(NewTexture->GetFullName(), &NewThumbnail, NewPackage);
// setting up new package and new texture
NewPackage->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(NewTexture);
FEditorDirectories::Get().SetLastDirectory(ELastDirectory::NEW_ASSET, FPaths::GetPath(PackageName));
// save args
FSavePackageArgs SaveArgs;
SaveArgs.TopLevelFlags = RF_Public | RF_Standalone;
SaveArgs.bForceByteSwapping = true;
SaveArgs.bWarnOfLongFilename = true;
// save it
if (!UPackage::SavePackage(NewPackage, NewTexture, *PackageFileName, SaveArgs))
{
UE_LOG(AIChatPlusEditor, Error, TEXT("Failed to save Asset: [%s]\n"), *PackageFileName);
}
}
クリップボード:UEで画像(UTexture2D)をWindowsクリップボードにコピーする
Windowsに関連する関数
以下の Windows 操作におけるクリップボード関連の関数を使用します:
- OpenClipboardクリップボードを開いて、クリップボードのハンドラを取得します。
- EmptyClipboardクリップボードをクリアし、クリップボードの所有権を現在のウィンドウに割り当てます。
- SetClipboardDataクリップボードにデータを設定し、画像データはこのインターフェースを介してクリップボードに送信されます。
- CloseClipboardデータの設定が完了したら、クリップボードを閉じます。
クリップボードの画像形式
標準クリップボード形式中には利用可能なクリップボード形式が紹介されており、その中の CF_DIBV5
は画像設定に使用できます。
CF_DIBV5 requires a specific definition format BITMAPV5HEADER structureこちらでは、以下の設定を選択しています。
UTexture2Dの設定
上では、クリップボード画像の色空間に LCS_sRGB
、つまりsRGB色空間を選択しましたので、UTexture2Dも対応する形式に設定する必要があります:
bool ConvertTextureToStandard(UTexture2D* InTexture)
{
if (InTexture->CompressionSettings != TC_VectorDisplacementmap)
{
InTexture->CompressionSettings = TC_VectorDisplacementmap;
IsChanged = true;
}
if (InTexture->SRGB != true)
{
InTexture->SRGB = true;
IsChanged = true;
}
if (IsChanged)
{
InTexture->UpdateResource();
}
}
ConvertTextureToStandardは、UTexture2Dを標準形式に変換する責任があります:TC_VectorDisplacementmap (RGBA8)およびSRGBカラースペース。UTexture2DとWindowsクリップボードの画像形式を整合させた後、画像データをクリップボードにコピーすることができます。
具体コード
void CopyTexture2DToClipboard(UTexture2D* InTexture)
{
if (!InTexture) return;
FTextureMipDataLockGuard InTextureGuard(InTexture);
// get InTexture info
uint8* SrcMipData = InTextureGuard.Lock(LOCK_READ_ONLY);
const int32 InSizeX = InTexture->GetSizeX();
const int32 InSizeY = InTexture->GetSizeY();
const EPixelFormat InFormat = InTexture->GetPixelFormat();
const FImageView SrcMipImage(
SrcMipData, InSizeX, InSizeY, 1, GetRawImageFormat(InTexture), InTexture->GetGammaSpace());
// set clipboard Texture info
const EPixelFormat OutFormat = PF_B8G8R8A8;
const int32 NumBlocksX = InSizeX / GPixelFormats[OutFormat].BlockSizeX;
const int32 NumBlocksY = InSizeY / GPixelFormats[OutFormat].BlockSizeY;
const int64 BufSize = static_cast<int64>(NumBlocksX) * NumBlocksY * GPixelFormats[InFormat].BlockBytes;
// set header info
BITMAPV5HEADER Header;
Header.bV5Size = sizeof(BITMAPV5HEADER);
Header.bV5Width = InSizeX;
Header.bV5Height = -InSizeY;
Header.bV5Planes = 1;
Header.bV5BitCount = 32;
Header.bV5Compression = BI_BITFIELDS;
Header.bV5SizeImage = BufSize;
Header.bV5XPelsPerMeter = 0;
Header.bV5YPelsPerMeter = 0;
Header.bV5ClrUsed = 0;
Header.bV5ClrImportant = 0;
Header.bV5RedMask = 0x00FF0000;
Header.bV5GreenMask = 0x0000FF00;
Header.bV5BlueMask = 0x000000FF;
Header.bV5AlphaMask = 0xFF000000;
Header.bV5CSType = LCS_sRGB;
// Header.bV5Endpoints; // ignored
Header.bV5GammaRed = 0;
Header.bV5GammaGreen = 0;
Header.bV5GammaBlue = 0;
Header.bV5Intent = 0;
Header.bV5ProfileData = 0;
Header.bV5ProfileSize = 0;
Header.bV5Reserved = 0;
HGLOBAL WinBuf = GlobalAlloc(GMEM_MOVEABLE, sizeof(BITMAPV5HEADER) + BufSize);
if (WinBuf == NULL)
return;
HWND WinHandler = GetActiveWindow();
if (!OpenClipboard(WinHandler)) {
GlobalFree(WinBuf);
return;
}
verify(EmptyClipboard());
// copy InTexture into BGRA8 sRGB Standard Texture
FTexture2DMipMap* DstMip = new FTexture2DMipMap();
DstMip->SizeX = InSizeX;
DstMip->SizeY = InSizeY;
DstMip->SizeZ = 1;
DstMip->BulkData.Lock(LOCK_READ_WRITE);
uint8* DstMipData = static_cast<uint8*>(DstMip->BulkData.Realloc(BufSize));
const FImageView DstMipImage(
DstMipData, InSizeX, InSizeY, 1, ERawImageFormat::BGRA8, EGammaSpace::sRGB);
FImageCore::CopyImage(SrcMipImage,DstMipImage);
DstMip->BulkData.Unlock();
// copy Standard Texture data into Clipboard
void * WinLockedBuf = GlobalLock(WinBuf);
if (WinLockedBuf) {
memcpy(WinLockedBuf, &Header, sizeof(BITMAPV5HEADER));
memcpy((char*)WinLockedBuf + sizeof(BITMAPV5HEADER), DstMipData, BufSize);
}
GlobalUnlock(WinLockedBuf);
if (!SetClipboardData(CF_DIBV5, WinBuf))
{
UE_LOG(AIChatPlus_Internal, Fatal, TEXT("SetClipboardData failed with error code %i"), (uint32)GetLastError() );
}
// finish, close clipboard
verify(CloseClipboard());
delete DstMip;
}
UTexture2D と Base64 との変換
これは実装が比較的簡単なので、直接コードを見てみましょう。
#include <Misc/Base64.h>
#include <ImageUtils.h>
UTexture2D* B64ToImage(const FString& B64)
{
TArray<uint8> Data;
FBase64::Decode(B64, Data);
return FImageUtils::ImportBufferAsTexture2D(Data);
}
FString ImageToB64(UTexture2D* InTexture, const int32 InQuality)
{
FTextureMipDataLockGuard InTextureGuard(InTexture);
uint8* MipData = InTextureGuard.Lock(LOCK_READ_ONLY);
check(MipData != nullptr);
const FImageView InImage(
MipData, InTexture->GetSizeX(), InTexture->GetSizeY(), 1,
GetRawImageFormat(InTexture->GetPixelFormat()), InTexture->GetGammaSpace());
TArray64<uint8> Buffer;
FString Ret;
if (FImageUtils::CompressImage(Buffer, TEXT("png"), InImage, InQuality))
{
Ret = FBase64::Encode(Buffer.GetData(), Buffer.Num());
}
return Ret;
}
Original: https://wiki.disenone.site/ja
This post is protected by CC BY-NC-SA 4.0 agreement, should be reproduced with attribution.
Visitors. Total Visits. Page Visits.
この投稿はChatGPTを使用して翻訳されました。フィードバックはフィードバック中指出任何遗漏之处。 -> どんな抜け漏れでも指摘してください。