UE4 Gif图插件

4 篇文章 0 订阅
3 篇文章 0 订阅

因为项目中用到了Gif格式的图片作为loading界面的资源,但UE本身不支持该格式,所以只能找找看是否有大佬写过相应的插件,果然被我找到了,是一个叫AnimatedTexture的插件。

虚幻4动画贴图插件 | 游戏程序员的自我修养

不过原插件Gift图的tick是在游戏线程中进行的。因为我们项目里面的loading是阻塞游戏线程加载,所以想要在加载界面中使用这个插件,就需要对原插件进行一部分修改,让Gif图资源在游戏线程阻塞时,也能够更新texture。

这里将原有的AnimatedTexture2D继承FTickableGameObject,修改为FAnimatedTextureResource继承FTickableObjectRenderThread,这样就可以在渲染线程中进行tick。我为了减小资源文件大小,把原有的导入时解析,改为了资源加载后解析。

同时对资源tick进行了一定优化,仅在纹理可见时才进行tick,不可见一段时间后不在进行纹理更新。

这里给出修改后的插件地址:

https://github.com/sorkNasrk/AnimatedTexture

多线程上可能没有做过多的保护,后期需要再优化一些

删除了原有的AnimatedGIFDecoder, UAnimatedTextureSource:

其中主要修改代码如下:

AnimatedTexture2D.h

/**
 * Copyright 2019 Neil Fang. All Rights Reserved.
 *
 * Animated Texture from GIF file
 *
 * Created by Neil Fang
 * GitHub Repo: https://github.com/neil3d/UnrealAnimatedTexturePlugin
 * GitHub Page: http://neil3d.github.io
 *
*/

#pragma once

#include "CoreMinimal.h"
#include "Tickable.h"	// Engine
#include "Engine/Texture.h"	// Engine

#include "AnimatedTexture2D.generated.h"

class FAnimatedTextureResource;
ANIMATEDTEXTURE_API bool isGifData(const void* data);

USTRUCT()
struct FGIFFrame
{
	GENERATED_BODY()
public:
	UPROPERTY()
		float Time;	// next frame delay in sec
	UPROPERTY()
		uint32 Index;	// 0-based index of the current frame
	UPROPERTY()
		uint32 Width;	// current frame width
	UPROPERTY()
		uint32 Height;	// current frame height
	UPROPERTY()
		uint32 OffsetX;	// current frame horizontal offset
	UPROPERTY()
		uint32 OffsetY;	// current frame vertical offset
	UPROPERTY()
		bool Interlacing;	// see: https://en.wikipedia.org/wiki/GIF#Interlacing
	UPROPERTY()
		uint8 Mode;	// next frame (sic next, not current) blending mode
	UPROPERTY()
		int16 TransparentIndex;	// 0-based transparent color index (or −1 when transparency is disabled)
	UPROPERTY()
		TArray<uint8> PixelIndices;	// pixel indices for the current frame
	UPROPERTY()
		TArray<FColor> Palette;	// the current palette

	FGIFFrame() :Time(0), Index(0), Width(0), Height(0), OffsetX(0), OffsetY(0),
		Interlacing(false), Mode(0), TransparentIndex(-1)
	{}
};


/**
 *
 */
UCLASS(BlueprintType, Category = AnimatedTexture, hideCategories = (Adjustments, Compression, LevelOfDetail))
class ANIMATEDTEXTURE_API UAnimatedTexture2D : public UTexture
{
	GENERATED_BODY()

public:
	friend FAnimatedTextureResource;

	UAnimatedTexture2D(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimatedTexture, meta = (DisplayName = "X-axis Tiling Method"), AssetRegistrySearchable, AdvancedDisplay)
		TEnumAsByte<enum TextureAddress> AddressX;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimatedTexture, meta = (DisplayName = "Y-axis Tiling Method"), AssetRegistrySearchable, AdvancedDisplay)
		TEnumAsByte<enum TextureAddress> AddressY;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimatedTexture)
		bool SupportsTransparency = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimatedTexture)
		float DefaultFrameDelay = 1.0f / 10;	// used while Frame.Delay==0

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimatedTexture)
		float PlayRate = 1.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimatedTexture)
		bool bLooping = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = AnimatedTexture)
		bool bAlwaysTickEvenNoSee = false;

	UPROPERTY(VisibleAnywhere, Transient,Category = AnimatedTexture)
		int FrameNum;


	virtual void PostLoad() override;


	virtual void BeginDestroy() override;


	bool ImportGIF(const uint8* Buffer, uint32 BufferSize);

	void ResetToInVaildGif()
	{
		GlobalWidth = 0;
		GlobalHeight = 0;
		Background = 0;
		Duration = 0.0f;
		Frames.Empty();
		FrameNum = 0;
	}

	void Import_Init(uint32 InGlobalWidth, uint32 InGlobalHeight, uint8 InBackground, uint32 InFrameCount);

	int GetFrameCount() const
	{ 
		return Frames.Num(); 
	}
	
	FGIFFrame& GetFrame(int32 Index) {
		return Frames[Index];
	}

	float GetFrameDelay(int FrameIndex) const
	{
		const FGIFFrame& Frame = Frames[FrameIndex];
		return Frame.Time;
	}

	float GetTotalDuration() const { return Duration; }


	void Import_Finished();

	void PostInitProperties() override;

private:
	bool ParseRawData();

public:
	UFUNCTION(BlueprintCallable, Category = AnimatedTexture)
		void Play();

	UFUNCTION(BlueprintCallable, Category = AnimatedTexture)
		void PlayFromStart();

	UFUNCTION(BlueprintCallable, Category = AnimatedTexture)
		void Stop();

	UFUNCTION(BlueprintCallable, Category = AnimatedTexture)
		bool IsPlaying() const { return bPlaying; }

	UFUNCTION(BlueprintCallable, Category = AnimatedTexture)
		void SetLooping(bool bNewLooping) { bLooping = bNewLooping; }

	UFUNCTION(BlueprintCallable, Category = AnimatedTexture)
		bool IsLooping() const { return bLooping; }

	UFUNCTION(BlueprintCallable, Category = AnimatedTexture)
		void SetPlayRate(float NewRate) { PlayRate = NewRate; }

	UFUNCTION(BlueprintCallable, Category = AnimatedTexture)
		float GetPlayRate() const { return PlayRate; }

	UFUNCTION(BlueprintCallable, Category = AnimatedTexture)
		float GetAnimationLength() const;


	//~ Begin UTexture Interface.
	virtual float GetSurfaceWidth() const override;
	virtual float GetSurfaceHeight() const override;
	virtual FTextureResource* CreateResource() override;
	virtual EMaterialValueType GetMaterialType() const override { return MCT_Texture2D; }
	virtual uint32 CalcTextureMemorySizeEnum(ETextureMipCount Enum) const override;


	//~ End UTexture Interface.


	//~ Begin UObject Interface.
#if WITH_EDITOR
	virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif // WITH_EDITOR
	virtual void GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) override;
	
	//~ End UObject Interface.

protected:
	UPROPERTY()
		bool bPlaying = true;
private:
	//UPROPERTY()
		uint32 GlobalWidth = 0;

	//UPROPERTY()
		uint32 GlobalHeight = 0;

	//UPROPERTY()
		uint8 Background = 0;	// 0-based background color index for the current palette

	//UPROPERTY()
		float Duration = 0.0f;
	//UPROPERTY()
	TArray<FGIFFrame> Frames;

	UPROPERTY()
	TArray<uint8> RawData;
};

AnimatedTexture2D.cpp

// Copyright 2019 Neil Fang. All Rights Reserved.

#include "AnimatedTexture2D.h"
#include "AnimatedTextureResource.h"
#include "gif_load/gif_load.h" // from: https://github.com/hidefromkgb/gif_load

bool isGifData(const void* data) {
	return FMemory::Memcmp(data, "GIF", 3) == 0;
}


//ANIMATEDTEXTURE_API bool LoadGIFBinary(UAnimatedTexture2D* OutGIF, const uint8* Buffer, uint32 BufferSize);

void GIFFrameLoader1(void* data, struct GIF_WHDR* whdr)
{
	UAnimatedTexture2D* OutGIF = (UAnimatedTexture2D*)data;

	//-- init on first frame
	if (OutGIF->GetFrameCount() == 0) {
		OutGIF->Import_Init(whdr->xdim, whdr->ydim, whdr->bkgd, whdr->nfrm);
	}

	//-- import frame
	int FrameIndex = whdr->ifrm;

	check(OutGIF->GetFrameCount() == whdr->nfrm);
	check(FrameIndex >= 0 && FrameIndex < OutGIF->GetFrameCount());

	FGIFFrame& Frame = OutGIF->GetFrame(FrameIndex);

	//-- copy properties
	if (whdr->time >= 0)
		Frame.Time = whdr->time * 0.01f;	// 1 GIF time units = 10 msec
	else
		Frame.Time = (-whdr->time - 1) * 0.01f;

	/** [TODO:] the frame is assumed to be inside global bounds,
			however it might exceed them in some GIFs; fix me. **/
	Frame.Index = whdr->ifrm;
	Frame.Width = whdr->frxd;
	Frame.Height = whdr->fryd;
	Frame.OffsetX = whdr->frxo;
	Frame.OffsetY = whdr->fryo;
	Frame.Interlacing = whdr->intr != 0;
	Frame.Mode = whdr->mode;
	Frame.TransparentIndex = whdr->tran;

	//-- copy pixel data
	int NumPixel = Frame.Width * Frame.Height;
	Frame.PixelIndices.SetNumUninitialized(NumPixel);
	FMemory::Memcpy(Frame.PixelIndices.GetData(), whdr->bptr, NumPixel);

	//-- copy pal
	int PaletteSize = whdr->clrs;
	Frame.Palette.Init(FColor(0, 0, 0, 255), PaletteSize);
	for (int i = 0; i < PaletteSize; i++)
	{
		FColor& uc = Frame.Palette[i];
		uc.R = whdr->cpal[i].R;
		uc.G = whdr->cpal[i].G;
		uc.B = whdr->cpal[i].B;
	}// end of for
}


bool LoadGIFBinary(UAnimatedTexture2D* OutGIF, const uint8* Buffer, uint32 BufferSize)
{
	int Ret = GIF_Load((void*)Buffer, BufferSize, GIFFrameLoader1, 0, (void*)OutGIF, 0L);
	OutGIF->Import_Finished();

	if (Ret < 0) {
		UE_LOG(LogTexture, Warning, TEXT("gif format error."));
		return false;
	}
	return true;
}

float UAnimatedTexture2D::GetSurfaceWidth() const
{
	return GlobalWidth;
}

float UAnimatedTexture2D::GetSurfaceHeight() const
{
	return GlobalHeight;
}

FTextureResource* UAnimatedTexture2D::CreateResource()
{
	FTextureResource* NewResource = new FAnimatedTextureResource(this);
	return NewResource;
}

uint32 UAnimatedTexture2D::CalcTextureMemorySizeEnum(ETextureMipCount Enum) const
{
	if(GlobalWidth>0 && GlobalHeight>0) 
	{

		uint32 Flags = SRGB ? TexCreate_SRGB : 0;
		uint32 NumMips = 1;
		uint32 NumSamples = 1;
		uint32 TextureAlign;
		FRHIResourceCreateInfo CreateInfo;
		uint32 Size = (uint32)RHICalcTexture2DPlatformSize(GlobalWidth, GlobalHeight, PF_B8G8R8A8, 1, 1, (ETextureCreateFlags)Flags, FRHIResourceCreateInfo(), TextureAlign);

		return Size;
	}

	return 4;
}

#if WITH_EDITOR
void UAnimatedTexture2D::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent)
{
	Super::PostEditChangeProperty(PropertyChangedEvent);

	bool RequiresNotifyMaterials = false;
	bool ResetAnimState = false;

	UProperty* PropertyThatChanged = PropertyChangedEvent.Property;
	if (PropertyThatChanged)
	{
		const FName PropertyName = PropertyThatChanged->GetFName();

		static const FName SupportsTransparencyName = GET_MEMBER_NAME_CHECKED(UAnimatedTexture2D, SupportsTransparency);

		if (PropertyName == SupportsTransparencyName)
		{
			RequiresNotifyMaterials = true;
			ResetAnimState = true;
		}
	}// end of if(prop is valid)

	if (ResetAnimState)
	{
		//AnimState = FAnmatedTextureState();
		//AnimSource->DecodeFrameToRHI(Resource, AnimState, SupportsTransparency);
	}

	if (RequiresNotifyMaterials)
		NotifyMaterials();
}
#endif // WITH_EDITOR

void UAnimatedTexture2D::GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize)
{
	Super::GetResourceSizeEx(CumulativeResourceSize);

	//if (CumulativeResourceSize.GetResourceSizeMode() == EResourceSizeMode::Exclusive)
	{
		CumulativeResourceSize.AddDedicatedSystemMemoryBytes(Frames.GetAllocatedSize());

		for (auto& Frame: Frames)
		{
			CumulativeResourceSize.AddDedicatedSystemMemoryBytes(Frame.Palette.GetAllocatedSize());
			CumulativeResourceSize.AddDedicatedSystemMemoryBytes(Frame.PixelIndices.GetAllocatedSize());
		}
	}
}



float UAnimatedTexture2D::GetAnimationLength() const
{
	return Duration;
}


UAnimatedTexture2D::UAnimatedTexture2D(const FObjectInitializer& ObjectInitializer /*= FObjectInitializer::Get()*/)
:Super(ObjectInitializer)
{

}

void UAnimatedTexture2D::PostLoad()
{
	ParseRawData();
	Super::PostLoad();
}

void UAnimatedTexture2D::BeginDestroy() 
{
	Super::BeginDestroy();
}


bool UAnimatedTexture2D::ImportGIF(const uint8* Buffer, uint32 BufferSize)
{
	Frames.Empty();
	RawData.SetNumUninitialized(BufferSize);
	FMemory::Memcpy(RawData.GetData(), Buffer, BufferSize);

	return ParseRawData();
}

void UAnimatedTexture2D::Import_Init(uint32 InGlobalWidth, uint32 InGlobalHeight, uint8 InBackground, uint32 InFrameCount)
{
	GlobalWidth = InGlobalWidth;
	GlobalHeight = InGlobalHeight;
	Background = InBackground;


	Frames.SetNum(InFrameCount);
	FrameNum = InFrameCount;
}

void UAnimatedTexture2D::Import_Finished()
{
	Duration = 0.0f;
	for (const auto& Frm : Frames)
		Duration += Frm.Time;
}

void UAnimatedTexture2D::PostInitProperties()
{
	Super::PostInitProperties();
}

bool UAnimatedTexture2D::ParseRawData()
{
	int Ret = GIF_Load((void*)RawData.GetData(), RawData.Num(), GIFFrameLoader1, 0, (void*)this, 0L);
	this->Import_Finished();

	if (Ret < 0) {
		UE_LOG(LogTexture, Warning, TEXT("gif format error."));
		return false;
	}
	return true;
}

void UAnimatedTexture2D::Play()
{
	bPlaying = true;
}

void UAnimatedTexture2D::PlayFromStart()
{
	bPlaying = true;
}

void UAnimatedTexture2D::Stop()
{
	bPlaying = false;
}

AnimatedTextureResource.h

/**
 * Copyright 2019 Neil Fang. All Rights Reserved.
 *
 * Animated Texture from GIF file
 *
 * Created by Neil Fang
 * GitHub Repo: https://github.com/neil3d/UnrealAnimatedTexturePlugin
 * GitHub Page: http://neil3d.github.io
 *
*/

#pragma once

#include "CoreMinimal.h"
#include "TextureResource.h"	// Engine

class UAnimatedTexture2D;

struct FAnmatedTextureState {
	int CurrentFrame;
	float FrameTime;

	FAnmatedTextureState() :CurrentFrame(0), FrameTime(0) {}
};

/**
 * FTextureResource implementation for animated 2D textures
 */
class FAnimatedTextureResource : public FTextureResource, public FTickableObjectRenderThread
{
public:
	FAnimatedTextureResource(UAnimatedTexture2D* InOwner);

	//~ Begin FTextureResource Interface.
	virtual uint32 GetSizeX() const override;
	virtual uint32 GetSizeY() const override;
	virtual void InitRHI() override;
	virtual void ReleaseRHI() override;
	//~ End FTextureResource Interface.

	//~ Begin FTickableObjectRenderThread Interface.
	virtual void Tick(float DeltaTime) override;
	virtual bool IsTickable() const override
	{
		return true;
	}
	virtual TStatId GetStatId() const
	{
		RETURN_QUICK_DECLARE_CYCLE_STAT(UAnimatedTexture2D, STATGROUP_Tickables);
	}
	//~ End FTickableObjectRenderThread Interface.

	bool TickAnim(float DeltaTime);
	void DecodeFrameToRHI();


private:
	int32 GetDefaultMipMapBias() const;

	void CreateSamplerStates(float MipMapBias);

private:
	UAnimatedTexture2D* Owner;
	FAnmatedTextureState AnimState;
	TArray<FColor>	FrameBuffer[2];
	uint32 LastFrame;
};

 AnimatedTextureResource.cpp

// Copyright 2019 Neil Fang. All Rights Reserved.

#include "AnimatedTextureResource.h"
#include "AnimatedTexture2D.h"
#include "AnimatedTextureModule.h"

#include "DeviceProfiles/DeviceProfile.h"	// Engine
#include "DeviceProfiles/DeviceProfileManager.h"	// Engine

#include "gif_load/gif_load.h" // from: https://github.com/hidefromkgb/gif_load


FAnimatedTextureResource::FAnimatedTextureResource(UAnimatedTexture2D * InOwner) 
:FTickableObjectRenderThread(false, true),
Owner(InOwner),
LastFrame(0)
{
}

uint32 FAnimatedTextureResource::GetSizeX() const
{
	if (Owner)
	{
		return Owner->GlobalWidth;
	}
	else
	{
		return 2;
	}
	
}

uint32 FAnimatedTextureResource::GetSizeY() const
{
	if (Owner)
	{
		return Owner->GlobalHeight;
	}
	else
	{
		return 2;
	}
}

void FAnimatedTextureResource::InitRHI()
{
	//-- create FSamplerStateRHIRef FTexture::SamplerStateRHI
	CreateSamplerStates(
		GetDefaultMipMapBias()
	);

	//-- create FTextureRHIRef FTexture::TextureRHI
	//uint32 TexCreateFlags = Owner->SRGB ? TexCreate_SRGB : 0;
	uint32 Flags = Owner->SRGB ? TexCreate_SRGB : 0;
	uint32 NumMips = 1;
	uint32 NumSamples = 1;

	FRHIResourceCreateInfo CreateInfo;
	TextureRHI = RHICreateTexture2D(FMath::Max(GetSizeX(),1u), FMath::Max(GetSizeY(), 1u), (uint8)PF_B8G8R8A8, NumMips, NumSamples, (ETextureCreateFlags)Flags, CreateInfo);
	TextureRHI->SetName(Owner->GetFName());

	//TRefCountPtr<FRHITexture2D> ShaderTexture2D;
	//TRefCountPtr<FRHITexture2D> RenderableTexture;
	//FRHIResourceCreateInfo CreateInfo = { FClearValueBinding(FLinearColor(0.0f, 0.0f, 0.0f)) };

	//RHICreateTargetableShaderResource2D(
	//	GetSizeX(),
	//	GetSizeY(),
	//	PF_B8G8R8A8,
	//	1,
	//	TexCreate_None,
	//	TexCreate_RenderTargetable,
	//	false,
	//	CreateInfo,
	//	RenderableTexture,
	//	ShaderTexture2D
	//);




	RHIUpdateTextureReference(Owner->TextureReference.TextureReferenceRHI, TextureRHI);

	if(Owner->GlobalHeight > 0 && Owner->GlobalWidth > 0)
	{
		DecodeFrameToRHI();
	}
	

	Register();
}

void FAnimatedTextureResource::ReleaseRHI()
{
	Unregister();

	RHIUpdateTextureReference(Owner->TextureReference.TextureReferenceRHI, nullptr);
	FTextureResource::ReleaseRHI();
}

void FAnimatedTextureResource::Tick(float DeltaTime)
{
	float duration = FApp::GetCurrentTime() - Owner->GetLastRenderTimeForStreaming();
	bool bShouldTick = Owner->bAlwaysTickEvenNoSee || duration < 2.5f;
	if(bShouldTick && Owner && Owner->IsPlaying() && Owner->GlobalHeight >0 && Owner->GlobalWidth > 0)
	{
		TickAnim(DeltaTime * Owner->PlayRate);
	}
}

bool FAnimatedTextureResource::TickAnim(float DeltaTime)
{
	bool NextFrame = false;
	float FrameDelay = Owner->GetFrameDelay(AnimState.CurrentFrame);
	if (FrameDelay == 0.0f)
		FrameDelay = Owner->DefaultFrameDelay;
	AnimState.FrameTime += DeltaTime;

	// skip long duration
	float Duration = Owner->GetTotalDuration();
	if (AnimState.FrameTime > Duration)
	{
		float N = FMath::TruncToFloat(AnimState.FrameTime / Duration);
		AnimState.FrameTime -= N * Duration;
	}

	// step to next frame
	if (AnimState.FrameTime > FrameDelay) {
		AnimState.CurrentFrame++;
		AnimState.FrameTime -= FrameDelay;
		NextFrame = true;

		// loop
		int NumFrame = Owner->GetFrameCount();
		if (AnimState.CurrentFrame >= NumFrame)
			AnimState.CurrentFrame = Owner->bLooping ? 0 : NumFrame - 1;
	}
	if(NextFrame)
	{
		DecodeFrameToRHI();
	}

	return NextFrame;
}

int32 FAnimatedTextureResource::GetDefaultMipMapBias() const
{
	return 0;
}

void FAnimatedTextureResource::CreateSamplerStates(float MipMapBias)
{
	FSamplerStateInitializerRHI SamplerStateInitializer
	(
		(ESamplerFilter)UDeviceProfileManager::Get().GetActiveProfile()->GetTextureLODSettings()->GetSamplerFilter(Owner),
		Owner->AddressX == TA_Wrap ? AM_Wrap : (Owner->AddressX == TA_Clamp ? AM_Clamp : AM_Mirror),
		Owner->AddressY == TA_Wrap ? AM_Wrap : (Owner->AddressY == TA_Clamp ? AM_Clamp : AM_Mirror),
		AM_Wrap,
		MipMapBias
	);
	SamplerStateRHI = RHICreateSamplerState(SamplerStateInitializer);

	FSamplerStateInitializerRHI DeferredPassSamplerStateInitializer
	(
		(ESamplerFilter)UDeviceProfileManager::Get().GetActiveProfile()->GetTextureLODSettings()->GetSamplerFilter(Owner),
		Owner->AddressX == TA_Wrap ? AM_Wrap : (Owner->AddressX == TA_Clamp ? AM_Clamp : AM_Mirror),
		Owner->AddressY == TA_Wrap ? AM_Wrap : (Owner->AddressY == TA_Clamp ? AM_Clamp : AM_Mirror),
		AM_Wrap,
		MipMapBias,
		1,
		0,
		2
	);

	DeferredPassSamplerStateRHI = RHICreateSamplerState(DeferredPassSamplerStateInitializer);
}


void FAnimatedTextureResource::DecodeFrameToRHI()
{
	if (FrameBuffer[0].Num() != Owner->GlobalHeight * Owner->GlobalWidth) {
		LastFrame = 0;

		FColor BGColor(0L);
		const FGIFFrame& GIFFrame = Owner->Frames[0];
		if (!Owner->SupportsTransparency)
			BGColor = GIFFrame.Palette[Owner->Background];

		for (int i = 0; i < 2; i++)
			FrameBuffer[i].Init(BGColor, Owner->GlobalHeight * Owner->GlobalWidth);
	}

	bool FirstFrame = AnimState.CurrentFrame == 0;
	FTexture2DRHIRef Texture2DRHI = TextureRHI->GetTexture2D();
	if (!Texture2DRHI)
		return;

	FGIFFrame& GIFFrame = Owner->Frames[AnimState.CurrentFrame];
	uint32& InLastFrame = LastFrame;
	bool bSupportsTransparency = Owner->SupportsTransparency;

	FColor* PICT = FrameBuffer[InLastFrame].GetData();
	uint32 InBackground = Owner->Background;

	TArray<FColor>& Pal = GIFFrame.Palette;

	uint32 TexWidth = Texture2DRHI->GetSizeX();
	uint32 TexHeight = Texture2DRHI->GetSizeY();

	//-- decode to frame buffer
	uint32 DDest = TexWidth * GIFFrame.OffsetY + GIFFrame.OffsetX;
	uint32 Src = 0;
	uint32 Iter = GIFFrame.Interlacing ? 0 : 4;
	uint32 Fin = !Iter ? 4 : 5;

	for (; Iter < Fin; Iter++) // interlacing support
	{
		uint32 YOffset = 16U >> ((Iter > 1) ? Iter : 1);

		for (uint32 Y = (8 >> Iter) & 7; Y < GIFFrame.Height; Y += YOffset)
		{
			for (uint32 X = 0; X < GIFFrame.Width; X++)
			{
				uint32 TexIndex = TexWidth * Y + X + DDest;
				uint8 ColorIndex = GIFFrame.PixelIndices[Src];

				if (ColorIndex != GIFFrame.TransparentIndex)
					PICT[TexIndex] = Pal[ColorIndex];
				else
				{
					int a = 0;
					a++;
				}

				Src++;
			}// end of for(x)
		}// end of for(y)
	}// end of for(iter)

	//-- write texture
	uint32 DestPitch = 0;
	FColor* SrcBuffer = PICT;
	FColor* DestBuffer = (FColor*)RHILockTexture2D(Texture2DRHI, 0, RLM_WriteOnly, DestPitch, false);
	if (DestBuffer)
	{
		uint32 MaxRow = TexHeight;
		int ColorSize = sizeof(FColor);

		//UE_LOG(LogTemp, Log, TEXT("%d"), ssss);

		if (DestPitch == TexWidth * ColorSize)
		{
			FMemory::Memcpy(DestBuffer, SrcBuffer, DestPitch * MaxRow);
		}
		else
		{
			// copy row by row
			uint32 SrcPitch = TexWidth * ColorSize;
			uint32 Pitch = FMath::Min(DestPitch, SrcPitch);
			for (uint32 y = 0; y < MaxRow; y++)
			{
				FMemory::Memcpy(DestBuffer, SrcBuffer, Pitch);
				DestBuffer += DestPitch / ColorSize;
				SrcBuffer += SrcPitch / ColorSize;
			}// end of for
		}// end of else

		RHIUnlockTexture2D(Texture2DRHI, 0, false);
	}// end of if
	else
	{
		UE_LOG(LogAnimTexture, Warning, TEXT("Unable to lock texture for write"));
	}// end of else

	//-- frame blending
	EGIF_Mode Mode = (EGIF_Mode)GIFFrame.Mode;

	if (Mode == GIF_PREV && FirstFrame)	// loop restart
		Mode = GIF_BKGD;

	switch (Mode)
	{
	case GIF_NONE:
	case GIF_CURR:
		break;
	case GIF_BKGD:	// restore background
	{
		FColor BGColor(0L);

		if (bSupportsTransparency)
		{
			if (GIFFrame.TransparentIndex == -1)
				BGColor = GIFFrame.Palette[InBackground];
			else
				BGColor = GIFFrame.Palette[GIFFrame.TransparentIndex];
			BGColor.A = 0;
		}
		else
		{
			BGColor = GIFFrame.Palette[InBackground];
		}

		uint32 BGWidth = GIFFrame.Width;
		uint32 BGHeight = GIFFrame.Height;
		uint32 XDest = DDest;

		if (FirstFrame)
		{
			BGWidth = TexWidth;
			BGHeight = TexHeight;
			XDest = 0;
		}

		for (uint32 Y = 0; Y < BGHeight; Y++)
		{
			for (uint32 X = 0; X < BGWidth; X++)
			{
				PICT[TexWidth * Y + X + XDest] = BGColor;
			}// end of for(x)
		}// end of for(y)
	}
	break;
	case GIF_PREV:	// restore prevous frame
		InLastFrame = (InLastFrame + 1) % 2;
		break;
	default:
		UE_LOG(LogAnimTexture, Warning, TEXT("Unknown GIF Mode"));
		break;
	}//end of switch
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值