664 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			664 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
| // Copyright (c) 2016-2018 Kiwano - Nomango
 | ||
| // 
 | ||
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | ||
| // of this software and associated documentation files (the "Software"), to deal
 | ||
| // in the Software without restriction, including without limitation the rights
 | ||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | ||
| // copies of the Software, and to permit persons to whom the Software is
 | ||
| // furnished to do so, subject to the following conditions:
 | ||
| // 
 | ||
| // The above copyright notice and this permission notice shall be included in
 | ||
| // all copies or substantial portions of the Software.
 | ||
| // 
 | ||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | ||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | ||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | ||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | ||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | ||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | ||
| // THE SOFTWARE.
 | ||
| 
 | ||
| #include "GifImage.h"
 | ||
| #include "../base/logs.h"
 | ||
| #include "../platform/modules.h"
 | ||
| 
 | ||
| namespace kiwano
 | ||
| {
 | ||
| 	GifImage::GifImage()
 | ||
| 		: animating_(false)
 | ||
| 		, next_index_(0)
 | ||
| 		, total_loop_count_(1)
 | ||
| 		, loop_count_(0)
 | ||
| 		, frames_count_(0)
 | ||
| 		, disposal_type_(DisposalType::Unknown)
 | ||
| 		, width_in_pixels_(0)
 | ||
| 		, height_in_pixels_(0)
 | ||
| 		, frame_position_{}
 | ||
| 		, bg_color_{}
 | ||
| 	{
 | ||
| 		factory_ = Renderer::Instance().GetDeviceResources()->GetWICImagingFactory();
 | ||
| 		auto ctx = Renderer::Instance().GetDeviceResources()->GetD2DDeviceContext();
 | ||
| 
 | ||
| 		ThrowIfFailed(
 | ||
| 			ctx->CreateCompatibleRenderTarget(&frame_rt_)
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	GifImage::GifImage(Resource const& res)
 | ||
| 		: GifImage()
 | ||
| 	{
 | ||
| 		Load(res);
 | ||
| 	}
 | ||
| 
 | ||
| 	bool GifImage::Load(Resource const& res)
 | ||
| 	{
 | ||
| 		HRESULT hr = S_OK;
 | ||
| 
 | ||
| 		next_index_ = 0;
 | ||
| 		loop_count_ = 0;
 | ||
| 		frames_count_ = 0;
 | ||
| 		disposal_type_ = DisposalType::None;
 | ||
| 		
 | ||
| 		saved_frame_.Reset();
 | ||
| 		decoder_.Reset();
 | ||
| 
 | ||
| 		if (res.IsFileType())
 | ||
| 		{
 | ||
| 			if (!modules::Shlwapi::Get().PathFileExistsW(res.GetFileName().c_str()))
 | ||
| 			{
 | ||
| 				KGE_WARNING_LOG(L"Gif file '%s' not found!", res.GetFileName().c_str());
 | ||
| 				return false;
 | ||
| 			}
 | ||
| 
 | ||
| 			hr = factory_->CreateDecoderFromFilename(
 | ||
| 				res.GetFileName().c_str(),
 | ||
| 				nullptr,
 | ||
| 				GENERIC_READ,
 | ||
| 				WICDecodeMetadataCacheOnLoad,
 | ||
| 				&decoder_);
 | ||
| 		}
 | ||
| 		else
 | ||
| 		{
 | ||
| 			LPVOID buffer;
 | ||
| 			DWORD buffer_size;
 | ||
| 			HRESULT hr = res.Load(buffer, buffer_size) ? S_OK : E_FAIL;
 | ||
| 
 | ||
| 			ComPtr<IWICStream> stream;
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = factory_->CreateStream(&stream);
 | ||
| 			}
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = stream->InitializeFromMemory(
 | ||
| 					static_cast<WICInProcPointer>(buffer),
 | ||
| 					buffer_size
 | ||
| 				);
 | ||
| 			}
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = factory_->CreateDecoderFromStream(
 | ||
| 					stream.Get(),
 | ||
| 					nullptr,
 | ||
| 					WICDecodeMetadataCacheOnLoad,
 | ||
| 					&decoder_
 | ||
| 				);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = GetGlobalMetadata();
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			if (frames_count_ > 0)
 | ||
| 			{
 | ||
| 				hr = ComposeNextFrame();
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		return SUCCEEDED(hr);
 | ||
| 	}
 | ||
| 
 | ||
| 	void GifImage::Update(Duration dt)
 | ||
| 	{
 | ||
| 		VisualNode::Update(dt);
 | ||
| 
 | ||
| 		if (animating_)
 | ||
| 		{
 | ||
| 			frame_elapsed_ += dt;
 | ||
| 			if (frame_delay_ <= frame_elapsed_)
 | ||
| 			{
 | ||
| 				frame_delay_ -= frame_elapsed_;
 | ||
| 				frame_elapsed_ = 0;
 | ||
| 				ComposeNextFrame();
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	void GifImage::Restart()
 | ||
| 	{
 | ||
| 		animating_ = true;
 | ||
| 		next_index_ = 0;
 | ||
| 		loop_count_ = 0;
 | ||
| 		disposal_type_ = DisposalType::None;
 | ||
| 	}
 | ||
| 
 | ||
| 	void GifImage::OnRender()
 | ||
| 	{
 | ||
| 		if (frame_rt_)
 | ||
| 		{
 | ||
| 			ComPtr<ID2D1Bitmap> frame_to_render;
 | ||
| 			if (SUCCEEDED(frame_rt_->GetBitmap(&frame_to_render)))
 | ||
| 			{
 | ||
| 				Rect bounds = GetBounds();
 | ||
| 				Renderer::Instance().DrawBitmap(frame_to_render, bounds, bounds);
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT GifImage::GetRawFrame(UINT frame_index)
 | ||
| 	{
 | ||
| 		ComPtr<IWICFormatConverter> converter;
 | ||
| 		ComPtr<IWICBitmapFrameDecode> wic_frame;
 | ||
| 		ComPtr<IWICMetadataQueryReader> metadata_reader;
 | ||
| 
 | ||
| 		PROPVARIANT prop_val;
 | ||
| 		PropVariantInit(&prop_val);
 | ||
| 
 | ||
| 		// Retrieve the current frame
 | ||
| 		HRESULT hr = decoder_->GetFrame(frame_index, &wic_frame);
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			// Format convert to 32bppPBGRA which D2D expects
 | ||
| 			hr = factory_->CreateFormatConverter(&converter);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = converter->Initialize(
 | ||
| 				wic_frame.Get(),
 | ||
| 				GUID_WICPixelFormat32bppPBGRA,
 | ||
| 				WICBitmapDitherTypeNone,
 | ||
| 				nullptr,
 | ||
| 				0.f,
 | ||
| 				WICBitmapPaletteTypeCustom);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			auto ctx = Renderer::Instance().GetDeviceResources()->GetD2DDeviceContext();
 | ||
| 
 | ||
| 			// Create a D2DBitmap from IWICBitmapSource
 | ||
| 			raw_frame_.Reset();
 | ||
| 			hr = ctx->CreateBitmapFromWicBitmap(
 | ||
| 				converter.Get(),
 | ||
| 				nullptr,
 | ||
| 				&raw_frame_);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			// Get Metadata Query Reader from the frame
 | ||
| 			hr = wic_frame->GetMetadataQueryReader(&metadata_reader);
 | ||
| 		}
 | ||
| 
 | ||
| 		// Get the Metadata for the current frame
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = metadata_reader->GetMetadataByName(L"/imgdesc/Left", &prop_val);
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL);
 | ||
| 				if (SUCCEEDED(hr))
 | ||
| 				{
 | ||
| 					frame_position_.left = static_cast<float>(prop_val.uiVal);
 | ||
| 				}
 | ||
| 				PropVariantClear(&prop_val);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = metadata_reader->GetMetadataByName(L"/imgdesc/Top", &prop_val);
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL);
 | ||
| 				if (SUCCEEDED(hr))
 | ||
| 				{
 | ||
| 					frame_position_.top = static_cast<float>(prop_val.uiVal);
 | ||
| 				}
 | ||
| 				PropVariantClear(&prop_val);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = metadata_reader->GetMetadataByName(L"/imgdesc/Width", &prop_val);
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL);
 | ||
| 				if (SUCCEEDED(hr))
 | ||
| 				{
 | ||
| 					frame_position_.right = static_cast<float>(prop_val.uiVal)
 | ||
| 						+ frame_position_.left;
 | ||
| 				}
 | ||
| 				PropVariantClear(&prop_val);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = metadata_reader->GetMetadataByName(L"/imgdesc/Height", &prop_val);
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL);
 | ||
| 				if (SUCCEEDED(hr))
 | ||
| 				{
 | ||
| 					frame_position_.bottom = static_cast<float>(prop_val.uiVal)
 | ||
| 						+ frame_position_.top;
 | ||
| 				}
 | ||
| 				PropVariantClear(&prop_val);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			unsigned int frame_delay = 0;
 | ||
| 
 | ||
| 			hr = metadata_reader->GetMetadataByName(
 | ||
| 				L"/grctlext/Delay",
 | ||
| 				&prop_val);
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL);
 | ||
| 				if (SUCCEEDED(hr))
 | ||
| 				{
 | ||
| 					hr = UIntMult(prop_val.uiVal, 10, &frame_delay);
 | ||
| 				}
 | ||
| 				PropVariantClear(&prop_val);
 | ||
| 			}
 | ||
| 			else
 | ||
| 			{
 | ||
| 				frame_delay = 0;
 | ||
| 			}
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>ǿ<EFBFBD><C7BF><EFBFBD>ӳ<EFBFBD>
 | ||
| 				if (frame_delay < 90)
 | ||
| 				{
 | ||
| 					frame_delay = 90;
 | ||
| 				}
 | ||
| 
 | ||
| 				frame_delay_.SetMilliseconds(static_cast<long>(frame_delay));
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = metadata_reader->GetMetadataByName(
 | ||
| 				L"/grctlext/Disposal",
 | ||
| 				&prop_val);
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = (prop_val.vt == VT_UI1) ? S_OK : E_FAIL;
 | ||
| 				if (SUCCEEDED(hr))
 | ||
| 				{
 | ||
| 					disposal_type_ = DisposalType(prop_val.bVal);
 | ||
| 				}
 | ||
| 			}
 | ||
| 			else
 | ||
| 			{
 | ||
| 				// <20><>ȡ DisposalType ʧ<>ܣ<EFBFBD><DCA3><EFBFBD><EFBFBD><EFBFBD>ͼƬ<CDBC><C6AC>ֻ<EFBFBD><D6BB>һ֡<D2BB><D6A1>ͼƬ
 | ||
| 				disposal_type_ = DisposalType::Unknown;
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		::PropVariantClear(&prop_val);
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT GifImage::GetGlobalMetadata()
 | ||
| 	{
 | ||
| 		unsigned int width = 0;
 | ||
| 		unsigned int height = 0;
 | ||
| 
 | ||
| 		PROPVARIANT prop_val;
 | ||
| 		::PropVariantInit(&prop_val);
 | ||
| 
 | ||
| 		ComPtr<IWICMetadataQueryReader> metadata_reader;
 | ||
| 
 | ||
| 		// <20><>ȡ֡<C8A1><D6A1><EFBFBD><EFBFBD>
 | ||
| 		HRESULT hr = decoder_->GetFrameCount(&frames_count_);
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = decoder_->GetMetadataQueryReader(
 | ||
| 				&metadata_reader);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>ɫ
 | ||
| 			if (FAILED(GetBackgroundColor(metadata_reader.Get())))
 | ||
| 			{
 | ||
| 				// <20><><EFBFBD><EFBFBD>δ<EFBFBD>ܻ<EFBFBD><DCBB><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD>Ĭ<EFBFBD><C4AC>Ϊ<CEAA><CDB8>
 | ||
| 				bg_color_ = D2D1::ColorF(0, 0.f);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		// <20><>ȡȫ<C8A1><C8AB> frame <20><>С
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			// <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>
 | ||
| 			hr = metadata_reader->GetMetadataByName(
 | ||
| 				L"/logscrdesc/Width",
 | ||
| 				&prop_val);
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL);
 | ||
| 				if (SUCCEEDED(hr))
 | ||
| 				{
 | ||
| 					width = prop_val.uiVal;
 | ||
| 				}
 | ||
| 				::PropVariantClear(&prop_val);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			// <20><>ȡ<EFBFBD>߶<EFBFBD>
 | ||
| 			hr = metadata_reader->GetMetadataByName(
 | ||
| 				L"/logscrdesc/Height",
 | ||
| 				&prop_val);
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL);
 | ||
| 				if (SUCCEEDED(hr))
 | ||
| 				{
 | ||
| 					height = prop_val.uiVal;
 | ||
| 				}
 | ||
| 				::PropVariantClear(&prop_val);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݺ<EFBFBD><DDBA><EFBFBD>
 | ||
| 			hr = metadata_reader->GetMetadataByName(
 | ||
| 				L"/logscrdesc/PixelAspectRatio",
 | ||
| 				&prop_val);
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = (prop_val.vt == VT_UI1 ? S_OK : E_FAIL);
 | ||
| 				if (SUCCEEDED(hr))
 | ||
| 				{
 | ||
| 					if (prop_val.bVal != 0)
 | ||
| 					{
 | ||
| 						// <20><>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
 | ||
| 						// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1<><31>4<EFBFBD><34><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 4<><34>1<EFBFBD><31><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊ 1/64
 | ||
| 						float pixel_asp_ratio = (prop_val.bVal + 15.f) / 64.f;
 | ||
| 
 | ||
| 						// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>س<EFBFBD><D8B3><EFBFBD><EFBFBD>ȼ<EFBFBD><C8BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>е<EFBFBD>ͼ<EFBFBD><CDBC><EFBFBD><EFBFBD><EFBFBD>Ⱥ߶ȣ<DFB6>ֻ<EFBFBD><D6BB>Сͼ<D0A1><CDBC>
 | ||
| 						if (pixel_asp_ratio > 1.f)
 | ||
| 						{
 | ||
| 							width_in_pixels_ = width;
 | ||
| 							height_in_pixels_ = static_cast<unsigned int>(height / pixel_asp_ratio);
 | ||
| 						}
 | ||
| 						else
 | ||
| 						{
 | ||
| 							width_in_pixels_ = static_cast<unsigned int>(width * pixel_asp_ratio);
 | ||
| 							height_in_pixels_ = height;
 | ||
| 						}
 | ||
| 					}
 | ||
| 					else
 | ||
| 					{
 | ||
| 						// ֵΪ 0, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD>Ϊ 1
 | ||
| 						width_in_pixels_ = width;
 | ||
| 						height_in_pixels_ = height;
 | ||
| 					}
 | ||
| 
 | ||
| 					SetSize(static_cast<float>(width_in_pixels_), static_cast<float>(height_in_pixels_));
 | ||
| 				}
 | ||
| 				::PropVariantClear(&prop_val);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		::PropVariantClear(&prop_val);
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT GifImage::ComposeNextFrame()
 | ||
| 	{
 | ||
| 		HRESULT hr = E_FAIL;
 | ||
| 
 | ||
| 		if (frame_rt_)
 | ||
| 		{
 | ||
| 			// <20>ҵ<EFBFBD><D2B5>ӳٴ<D3B3><D9B4><EFBFBD> 0 <20><>֡ (0 <20>ӳ<EFBFBD>֡<EFBFBD>Dz<EFBFBD><C7B2>ɼ<EFBFBD><C9BC><EFBFBD><EFBFBD>м<EFBFBD>֡)
 | ||
| 			do
 | ||
| 			{
 | ||
| 				hr = DisposeCurrentFrame();
 | ||
| 				if (SUCCEEDED(hr))
 | ||
| 				{
 | ||
| 					hr = OverlayNextFrame();
 | ||
| 				}
 | ||
| 			} while (SUCCEEDED(hr) && frame_delay_.IsZero() && !IsLastFrame());
 | ||
| 
 | ||
| 			animating_ = (SUCCEEDED(hr) && !EndOfAnimation() && frames_count_ > 1);
 | ||
| 		}
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT GifImage::DisposeCurrentFrame()
 | ||
| 	{
 | ||
| 		HRESULT hr = S_OK;
 | ||
| 
 | ||
| 		switch (disposal_type_)
 | ||
| 		{
 | ||
| 		case DisposalType::Unknown:
 | ||
| 		case DisposalType::None:
 | ||
| 			break;
 | ||
| 		case DisposalType::Background:
 | ||
| 			// <20>ñ<EFBFBD><C3B1><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰԭʼ֡<CABC><D6A1><EFBFBD>ǵ<EFBFBD><C7B5><EFBFBD><EFBFBD><EFBFBD>
 | ||
| 			hr = ClearCurrentFrameArea();
 | ||
| 			break;
 | ||
| 		case DisposalType::Previous:
 | ||
| 			// <20>ָ<EFBFBD><D6B8><EFBFBD>ǰ<EFBFBD><C7B0>ͼ<EFBFBD><CDBC>֡
 | ||
| 			hr = RestoreSavedFrame();
 | ||
| 			break;
 | ||
| 		default:
 | ||
| 			hr = E_FAIL;
 | ||
| 		}
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT GifImage::OverlayNextFrame()
 | ||
| 	{
 | ||
| 		HRESULT hr = GetRawFrame(next_index_);
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			if (disposal_type_ == DisposalType::Previous)
 | ||
| 			{
 | ||
| 				hr = SaveComposedFrame();
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			frame_rt_->BeginDraw();
 | ||
| 
 | ||
| 			if (next_index_ == 0)
 | ||
| 			{
 | ||
| 				// <20><><EFBFBD>»<EFBFBD><C2BB>Ʊ<EFBFBD><C6B1><EFBFBD>
 | ||
| 				frame_rt_->Clear(bg_color_);
 | ||
| 
 | ||
| 				loop_count_++;
 | ||
| 			}
 | ||
| 
 | ||
| 			frame_rt_->DrawBitmap(raw_frame_.Get(), frame_position_);
 | ||
| 
 | ||
| 			hr = frame_rt_->EndDraw();
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			next_index_ = (++next_index_) % frames_count_;
 | ||
| 		}
 | ||
| 
 | ||
| 		if (IsLastFrame() && loop_cb_)
 | ||
| 		{
 | ||
| 			loop_cb_(loop_count_ - 1);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (EndOfAnimation() && done_cb_)
 | ||
| 		{
 | ||
| 			done_cb_();
 | ||
| 		}
 | ||
| 
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT GifImage::SaveComposedFrame()
 | ||
| 	{
 | ||
| 		HRESULT hr = S_OK;
 | ||
| 
 | ||
| 		ComPtr<ID2D1Bitmap> frame_to_be_saved;
 | ||
| 
 | ||
| 		hr = frame_rt_->GetBitmap(&frame_to_be_saved);
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			if (saved_frame_ == nullptr)
 | ||
| 			{
 | ||
| 				auto size = frame_to_be_saved->GetPixelSize();
 | ||
| 				auto prop = D2D1::BitmapProperties(frame_to_be_saved->GetPixelFormat());
 | ||
| 
 | ||
| 				hr = frame_rt_->CreateBitmap(size, prop, &saved_frame_);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = saved_frame_->CopyFromBitmap(nullptr, frame_to_be_saved.Get(), nullptr);
 | ||
| 		}
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT GifImage::RestoreSavedFrame()
 | ||
| 	{
 | ||
| 		HRESULT hr = S_OK;
 | ||
| 
 | ||
| 		ComPtr<ID2D1Bitmap> frame_to_copy_to;
 | ||
| 
 | ||
| 		hr = saved_frame_ ? S_OK : E_FAIL;
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = frame_rt_->GetBitmap(&frame_to_copy_to);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = frame_to_copy_to->CopyFromBitmap(nullptr, saved_frame_.Get(), nullptr);
 | ||
| 		}
 | ||
| 
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT GifImage::ClearCurrentFrameArea()
 | ||
| 	{
 | ||
| 		frame_rt_->BeginDraw();
 | ||
| 
 | ||
| 		frame_rt_->PushAxisAlignedClip(&frame_position_, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
 | ||
| 		frame_rt_->Clear(bg_color_);
 | ||
| 		frame_rt_->PopAxisAlignedClip();
 | ||
| 
 | ||
| 		return frame_rt_->EndDraw();
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT GifImage::GetBackgroundColor(IWICMetadataQueryReader* metadata_reader)
 | ||
| 	{
 | ||
| 		DWORD bgcolor = 0;
 | ||
| 		BYTE bg_index = 0;
 | ||
| 		WICColor bgcolors[256];
 | ||
| 		UINT colors_copied = 0;
 | ||
| 
 | ||
| 		PROPVARIANT prop_val;
 | ||
| 		PropVariantInit(&prop_val);
 | ||
| 
 | ||
| 		ComPtr<IWICPalette> wic_palette;
 | ||
| 
 | ||
| 		HRESULT hr = metadata_reader->GetMetadataByName(
 | ||
| 			L"/logscrdesc/GlobalColorTableFlag",
 | ||
| 			&prop_val);
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = (prop_val.vt != VT_BOOL || !prop_val.boolVal) ? E_FAIL : S_OK;
 | ||
| 			::PropVariantClear(&prop_val);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = metadata_reader->GetMetadataByName(
 | ||
| 				L"/logscrdesc/BackgroundColorIndex",
 | ||
| 				&prop_val);
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				hr = (prop_val.vt != VT_UI1) ? E_FAIL : S_OK;
 | ||
| 				if (SUCCEEDED(hr))
 | ||
| 				{
 | ||
| 					bg_index = prop_val.bVal;
 | ||
| 				}
 | ||
| 				::PropVariantClear(&prop_val);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = factory_->CreatePalette(&wic_palette);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = decoder_->CopyPalette(wic_palette.Get());
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = wic_palette->GetColors(
 | ||
| 				ARRAYSIZE(bgcolors),
 | ||
| 				bgcolors,
 | ||
| 				&colors_copied);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			// <20><><EFBFBD><EFBFBD><EFBFBD>±<EFBFBD>
 | ||
| 			hr = (bg_index >= colors_copied) ? E_FAIL : S_OK;
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			bgcolor = bgcolors[bg_index];
 | ||
| 
 | ||
| 			// ת<><D7AA>Ϊ ARGB <20><>ʽ
 | ||
| 			float alpha = (bgcolor >> 24) / 255.f;
 | ||
| 			bg_color_ = D2D1::ColorF(bgcolor, alpha);
 | ||
| 		}
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| }
 |