Zum Inhalt

UE ermöglicht eine Vielzahl von Bild (UTexture2D) Operationen (Lesen, Speichern, Kopieren, Zwischenablage...).

The following code examples are based on UE5.3 version.

Quellcode

Weitere Details zum Quellcode finden Sie im UE-Marktplatz unter dem Plugin: AIChatPlus

Lesen Sie: UE liest lokale Systembilder ein, um sie als UTexture2D darzustellen.

Allgemeine Methode

Diese Methode funktioniert sowohl im Editor- als auch im GamePlay-Modus und unterstützt die Bildformate PNG, JPEG, BMP, ICO, EXR, ICNS, HDR, TIFF, DDS, TGA, sodass die meisten gängigen Bildtypen abgedeckt sind.

Der Code ist auch sehr sauber:

#include <Engine/Texture2D.h>
#include <ImageUtils.h>

UTexture2D* LoadImage(const FString& InLoadPath)
{
    FImage ImageInfo;
    FImageUtils::LoadImage(*InLoadPath, ImageInfo);
    return FImageUtils::CreateTexture2DFromImage(ImageInfo);
}

Das Ergebnis ist UTexture2D.

Spezialmethoden für Editoren

Diese Methode unterstützt zusätzlich weitere Bildtypen: UDIM-Texturdateien, IES-Dateien, PCX und PSD.

Die Umsetzung im Code wird etwas komplexer sein:

#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

Die Umsetzung verwendet die Funktion FactoryCreateBinary der UTextureFactory, die in der Lage ist, die erwähnten zusätzlichen Dateitypen zu lesen.

Kopie: UE erreicht das Kopieren von UTexture2D

Manchmal muss ein UTexture2D dupliziert werden, um dann Änderungen an diesem duplizierten Bild vorzunehmen. Das Kopieren des Bildes erfordert die Verwendung der vom Engine bereitgestellten Funktion FImageCore::CopyImage. Sobald die Parameter der beiden Bilder festgelegt sind, kann diese Schnittstelle aufgerufen werden.

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;
}

Speichern: UE ermöglicht das Speichern von UTexture2D in eine Datei.

Der Schlüssel liegt darin, die Engine-Funktion FImageUtils::SaveImageAutoFormat zu verwenden, um dies zu realisieren. Es ist ziemlich einfach umzusetzen, aber man sollte auf die Fälle von fehlgeschlagenen Wiederholungen achten.

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);
    }
}

Speichern: UE speichert das UTexture2D als Asset.

Speichern Sie das UTexture2D aus dem Speicher in ein Asset und betrachten Sie es im Ressourcenbrowser (Content Browser).

Die Kernfunktion benötigt die oben implementierte CopyTexture2D. Zuerst müssen wir ein neues Bild kopieren und dann UPackage::SavePackage aufrufen, um das Paket zu speichern, in dem sich das Bild befindet, als 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);
    }
}

Transferieren des Textes in die deutsche Sprache:

"Zwischenablage: UE ermöglicht das Kopieren von Bildern (UTexture2D) in die Windows-Zwischenablage (Clipboard)."

Wir werden die folgenden Funktionen des Windows-Zwischenspeichers verwenden:

  • OpenClipboardÖffnen Sie die Zwischenablage und erhalten Sie den Handler der Zwischenablage.
  • EmptyClipboardLeeren Sie die Zwischenablage und weisen Sie sie dem aktuellen Fenster zu.
  • SetClipboardDataÜbermitteln Sie die Daten des Bildes mit dieser Schnittstelle an die Zwischenablage, um den Zwischenablagetext festzulegen.
  • CloseClipboardNachdem die Daten eingestellt sind, schließen Sie die Zwischenablage.

Das Bildformat der Zwischenablage

(https://learn.microsoft.com/zh-cn/windows/win32/dataxchg/standard-clipboard-formats)Es werden verfügbare Zwischenablageformate vorgestellt, wobei CF_DIBV5 zum Einfügen von Bildern genutzt werden kann.

Die spezifische Definition des Formats, wie von CF_DIBV5 BITMAPV5HEADER-StrukturHier verwenden wir die folgende Konfiguration.

BITMAPV5HEADER Header;
Header.bV5CSType        = LCS_sRGB;
Header.bV5Compression   = BI_BITFIELDS;

UTexture2D setting

Wir haben im obigen Abschnitt den Farbraum des Zwischenablagebildes auf LCS_sRGB, also den sRGB-Farbraum, eingestellt. Daher muss auch das UTexture2D zuerst auf das entsprechende Format eingestellt werden:

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 is responsible for converting UTexture2D into standard formats: TC_VectorDisplacementmap (RGBA8) and SRGB color space. Once the image format of UTexture2D aligns with the Windows clipboard, we can then copy the image data to the clipboard.

Specific code

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;
}

Die Umwandlung zwischen UTexture2D und Base64.

Dies ist relativ einfach umzusetzen, lassen Sie uns direkt zum Code übergehen.

#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/de

This post is protected by CC BY-NC-SA 4.0 agreement, should be reproduced with attribution.

Dieser Beitrag wurde mit ChatGPT übersetzt, bitte FeedbackBitte weisen Sie auf etwaige Auslassungen hin.