support GIT image
This commit is contained in:
		
							parent
							
								
									2ab6a7bd29
								
							
						
					
					
						commit
						6b84e909f7
					
				|  | @ -97,7 +97,12 @@ namespace kiwano | ||||||
| 		 | 		 | ||||||
| 		if (bitmap_cached_) | 		if (bitmap_cached_) | ||||||
| 		{ | 		{ | ||||||
| 			Renderer::Instance().DrawBitmap(bitmap_cached_); | 			Rect bitmap_rect(0.f, 0.f, bitmap_cached_->GetSize().width, bitmap_cached_->GetSize().height); | ||||||
|  | 			Renderer::Instance().DrawBitmap( | ||||||
|  | 				bitmap_cached_, | ||||||
|  | 				bitmap_rect, | ||||||
|  | 				bitmap_rect | ||||||
|  | 			); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,662 @@ | ||||||
|  | // 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_; | ||||||
|  | 				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)) | ||||||
|  | 			{ | ||||||
|  | 				// 插入一个强制延迟
 | ||||||
|  | 				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 | ||||||
|  | 			{ | ||||||
|  | 				// 获取 DisposalType 失败,可能图片是只有一帧的图片
 | ||||||
|  | 				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; | ||||||
|  | 
 | ||||||
|  | 		// 获取帧数量
 | ||||||
|  | 		HRESULT hr = decoder_->GetFrameCount(&frames_count_); | ||||||
|  | 
 | ||||||
|  | 		if (SUCCEEDED(hr)) | ||||||
|  | 		{ | ||||||
|  | 			hr = decoder_->GetMetadataQueryReader( | ||||||
|  | 				&metadata_reader); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (SUCCEEDED(hr)) | ||||||
|  | 		{ | ||||||
|  | 			// 获取背景色
 | ||||||
|  | 			if (FAILED(GetBackgroundColor(metadata_reader.Get()))) | ||||||
|  | 			{ | ||||||
|  | 				// 如果未能获得颜色,则默认为透明
 | ||||||
|  | 				bg_color_ = D2D1::ColorF(0, 0.f); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// 获取全局 frame 大小
 | ||||||
|  | 		if (SUCCEEDED(hr)) | ||||||
|  | 		{ | ||||||
|  | 			// 获取宽度
 | ||||||
|  | 			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)) | ||||||
|  | 		{ | ||||||
|  | 			// 获取高度
 | ||||||
|  | 			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)) | ||||||
|  | 		{ | ||||||
|  | 			// 获得像素纵横比
 | ||||||
|  | 			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) | ||||||
|  | 					{ | ||||||
|  | 						// 需要计算比率
 | ||||||
|  | 						// 最高像素 1:4,最宽像素 4:1,增量为 1/64
 | ||||||
|  | 						float pixel_asp_ratio = (prop_val.bVal + 15.f) / 64.f; | ||||||
|  | 
 | ||||||
|  | 						// 根据像素长宽比计算像素中的图像宽度和高度,只缩小图像
 | ||||||
|  | 						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, 所以像素比为 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_) | ||||||
|  | 		{ | ||||||
|  | 			// 找到延迟大于 0 的帧 (0 延迟帧是不可见的中间帧)
 | ||||||
|  | 			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: | ||||||
|  | 			// 用背景颜色清除当前原始帧覆盖的区域
 | ||||||
|  | 			hr = ClearCurrentFrameArea(); | ||||||
|  | 			break; | ||||||
|  | 		case DisposalType::Previous: | ||||||
|  | 			// 恢复先前构图的帧
 | ||||||
|  | 			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) | ||||||
|  | 			{ | ||||||
|  | 				// 重新绘制背景
 | ||||||
|  | 				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)) | ||||||
|  | 		{ | ||||||
|  | 			// 检查下标
 | ||||||
|  | 			hr = (bg_index >= colors_copied) ? E_FAIL : S_OK; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (SUCCEEDED(hr)) | ||||||
|  | 		{ | ||||||
|  | 			bgcolor = bgcolors[bg_index]; | ||||||
|  | 
 | ||||||
|  | 			// 转换为 ARGB 格式
 | ||||||
|  | 			float alpha = (bgcolor >> 24) / 255.f; | ||||||
|  | 			bg_color_ = D2D1::ColorF(bgcolor, alpha); | ||||||
|  | 		} | ||||||
|  | 		return hr; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,114 @@ | ||||||
|  | // 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.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include "Node.h" | ||||||
|  | #include "../base/Resource.h" | ||||||
|  | #include "../renderer/render.h" | ||||||
|  | 
 | ||||||
|  | namespace kiwano | ||||||
|  | { | ||||||
|  | 	class KGE_API GifImage | ||||||
|  | 		: public VisualNode | ||||||
|  | 	{ | ||||||
|  | 	public: | ||||||
|  | 		typedef Closure<void(int)> LoopDoneCallback; | ||||||
|  | 		typedef Closure<void()> DoneCallback; | ||||||
|  | 
 | ||||||
|  | 		GifImage(); | ||||||
|  | 
 | ||||||
|  | 		GifImage( | ||||||
|  | 			Resource const& res | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 		bool Load( | ||||||
|  | 			Resource const& res | ||||||
|  | 		); | ||||||
|  | 
 | ||||||
|  | 		// 设置 GIF 动画循环次数
 | ||||||
|  | 		inline void SetLoopCount(int loops)								{ total_loop_count_ = loops; } | ||||||
|  | 
 | ||||||
|  | 		// 设置 GIF 动画每次循环结束回调函数
 | ||||||
|  | 		inline void SetLoopDoneCallback(LoopDoneCallback const& cb)		{ loop_cb_ = cb; } | ||||||
|  | 
 | ||||||
|  | 		// 设置 GIF 动画结束回调函数
 | ||||||
|  | 		inline void SetDoneCallback(DoneCallback const& cb)				{ done_cb_ = cb; } | ||||||
|  | 
 | ||||||
|  | 		// 重新播放动画
 | ||||||
|  | 		void Restart(); | ||||||
|  | 
 | ||||||
|  | 		inline int GetFramesCount() const								{ return static_cast<int>(frames_count_); } | ||||||
|  | 		inline int GetLoopCount() const									{ return total_loop_count_; } | ||||||
|  | 		inline LoopDoneCallback GetLoopDoneCallback() const				{ return loop_cb_; } | ||||||
|  | 		inline DoneCallback GetDoneCallback() const						{ return done_cb_; } | ||||||
|  | 
 | ||||||
|  | 		void OnRender() override; | ||||||
|  | 
 | ||||||
|  | 	protected: | ||||||
|  | 		void Update(Duration dt) override; | ||||||
|  | 
 | ||||||
|  | 		HRESULT GetRawFrame(UINT frame_index); | ||||||
|  | 		HRESULT GetGlobalMetadata(); | ||||||
|  | 		HRESULT GetBackgroundColor(IWICMetadataQueryReader* metadata_reader); | ||||||
|  | 
 | ||||||
|  | 		HRESULT ComposeNextFrame(); | ||||||
|  | 		HRESULT DisposeCurrentFrame(); | ||||||
|  | 		HRESULT OverlayNextFrame(); | ||||||
|  | 
 | ||||||
|  | 		HRESULT SaveComposedFrame(); | ||||||
|  | 		HRESULT RestoreSavedFrame(); | ||||||
|  | 		HRESULT ClearCurrentFrameArea(); | ||||||
|  | 
 | ||||||
|  | 		inline bool IsLastFrame() const		{ return (next_index_ == 0); } | ||||||
|  | 		inline bool EndOfAnimation() const	{ return IsLastFrame() && loop_count_ == total_loop_count_ + 1; } | ||||||
|  | 
 | ||||||
|  | 	protected: | ||||||
|  | 		Duration frame_delay_; | ||||||
|  | 		Duration frame_elapsed_; | ||||||
|  | 
 | ||||||
|  | 		ComPtr<IWICImagingFactory>		factory_; | ||||||
|  | 		ComPtr<ID2D1BitmapRenderTarget>	frame_rt_; | ||||||
|  | 		ComPtr<ID2D1Bitmap>				raw_frame_; | ||||||
|  | 		ComPtr<ID2D1Bitmap>				saved_frame_; | ||||||
|  | 		ComPtr<IWICBitmapDecoder>		decoder_; | ||||||
|  | 
 | ||||||
|  | 		enum class DisposalType | ||||||
|  | 		{ | ||||||
|  | 			Unknown, | ||||||
|  | 			None, | ||||||
|  | 			Background, | ||||||
|  | 			Previous | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		bool			animating_; | ||||||
|  | 		int				total_loop_count_; | ||||||
|  | 		int				loop_count_; | ||||||
|  | 		unsigned int	next_index_; | ||||||
|  | 		unsigned int	frames_count_; | ||||||
|  | 		unsigned int	width_in_pixels_; | ||||||
|  | 		unsigned int	height_in_pixels_; | ||||||
|  | 		DisposalType	disposal_type_; | ||||||
|  | 		D2D1_RECT_F		frame_position_; | ||||||
|  | 		D2D1_COLOR_F	bg_color_; | ||||||
|  | 
 | ||||||
|  | 		LoopDoneCallback	loop_cb_; | ||||||
|  | 		DoneCallback		done_cb_; | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  | @ -395,7 +395,7 @@ namespace kiwano | ||||||
| 	protected: | 	protected: | ||||||
| 		virtual void PrepareRender() {} | 		virtual void PrepareRender() {} | ||||||
| 
 | 
 | ||||||
| 		void Update(Duration dt); | 		virtual void Update(Duration dt); | ||||||
| 
 | 
 | ||||||
| 		void Render(); | 		void Render(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ | ||||||
|     <ClInclude Include="2d\Frames.h" /> |     <ClInclude Include="2d\Frames.h" /> | ||||||
|     <ClInclude Include="2d\Geometry.h" /> |     <ClInclude Include="2d\Geometry.h" /> | ||||||
|     <ClInclude Include="2d\GeometryNode.h" /> |     <ClInclude Include="2d\GeometryNode.h" /> | ||||||
|  |     <ClInclude Include="2d\GifImage.h" /> | ||||||
|     <ClInclude Include="2d\Image.h" /> |     <ClInclude Include="2d\Image.h" /> | ||||||
|     <ClInclude Include="2d\Layer.h" /> |     <ClInclude Include="2d\Layer.h" /> | ||||||
|     <ClInclude Include="2d\Node.h" /> |     <ClInclude Include="2d\Node.h" /> | ||||||
|  | @ -110,6 +111,7 @@ | ||||||
|     <ClCompile Include="2d\Frames.cpp" /> |     <ClCompile Include="2d\Frames.cpp" /> | ||||||
|     <ClCompile Include="2d\Geometry.cpp" /> |     <ClCompile Include="2d\Geometry.cpp" /> | ||||||
|     <ClCompile Include="2d\GeometryNode.cpp" /> |     <ClCompile Include="2d\GeometryNode.cpp" /> | ||||||
|  |     <ClCompile Include="2d\GifImage.cpp" /> | ||||||
|     <ClCompile Include="2d\Image.cpp" /> |     <ClCompile Include="2d\Image.cpp" /> | ||||||
|     <ClCompile Include="2d\Layer.cpp" /> |     <ClCompile Include="2d\Layer.cpp" /> | ||||||
|     <ClCompile Include="2d\Node.cpp" /> |     <ClCompile Include="2d\Node.cpp" /> | ||||||
|  |  | ||||||
|  | @ -318,6 +318,9 @@ | ||||||
|     <ClInclude Include="audio\Sound.h"> |     <ClInclude Include="audio\Sound.h"> | ||||||
|       <Filter>audio</Filter> |       <Filter>audio</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|  |     <ClInclude Include="2d\GifImage.h"> | ||||||
|  |       <Filter>2d</Filter> | ||||||
|  |     </ClInclude> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClCompile Include="ui\Button.cpp"> |     <ClCompile Include="ui\Button.cpp"> | ||||||
|  | @ -485,5 +488,8 @@ | ||||||
|     <ClCompile Include="audio\Sound.cpp"> |     <ClCompile Include="audio\Sound.cpp"> | ||||||
|       <Filter>audio</Filter> |       <Filter>audio</Filter> | ||||||
|     </ClCompile> |     </ClCompile> | ||||||
|  |     <ClCompile Include="2d\GifImage.cpp"> | ||||||
|  |       <Filter>2d</Filter> | ||||||
|  |     </ClCompile> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
| </Project> | </Project> | ||||||
|  | @ -62,6 +62,14 @@ namespace kiwano | ||||||
| 			// 时长是否是零
 | 			// 时长是否是零
 | ||||||
| 			inline bool IsZero() const				{ return milliseconds_ == 0LL; } | 			inline bool IsZero() const				{ return milliseconds_ == 0LL; } | ||||||
| 
 | 
 | ||||||
|  | 			inline void SetMilliseconds(long ms)	{ milliseconds_ = ms; } | ||||||
|  | 
 | ||||||
|  | 			inline void SetSeconds(float seconds)	{ milliseconds_ = static_cast<long>(seconds * 1000.f); } | ||||||
|  | 
 | ||||||
|  | 			inline void SetMinutes(float minutes)	{ milliseconds_ = static_cast<long>(minutes * 60 * 1000.f); } | ||||||
|  | 
 | ||||||
|  | 			inline void SetHours(float hours)		{ milliseconds_ = static_cast<long>(hours * 60 * 60 * 1000.f); } | ||||||
|  | 
 | ||||||
| 			// 转为字符串
 | 			// 转为字符串
 | ||||||
| 			String ToString() const; | 			String ToString() const; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -68,21 +68,22 @@ | ||||||
| #include "platform/modules.h" | #include "platform/modules.h" | ||||||
| #include "platform/Application.h" | #include "platform/Application.h" | ||||||
| 
 | 
 | ||||||
|  | #include "base/Object.h" | ||||||
| #include "base/Event.hpp" | #include "base/Event.hpp" | ||||||
| #include "base/EventListener.h" | #include "base/EventListener.h" | ||||||
| #include "base/EventDispatcher.h" | #include "base/EventDispatcher.h" | ||||||
| #include "base/Timer.h" | #include "base/Timer.h" | ||||||
| #include "base/TimerManager.h" | #include "base/TimerManager.h" | ||||||
| #include "base/AsyncTask.h" | #include "base/AsyncTask.h" | ||||||
|  | #include "base/Resource.h" | ||||||
| 
 | 
 | ||||||
| #include "2d/Font.hpp" | #include "2d/Font.hpp" | ||||||
| #include "2d/Color.h" | #include "2d/Color.h" | ||||||
| #include "2d/Transform.hpp" | #include "2d/Transform.hpp" | ||||||
| #include "2d/TextStyle.hpp" | #include "2d/TextStyle.hpp" | ||||||
| #include "base/Resource.h" |  | ||||||
| 
 | 
 | ||||||
| #include "base/Object.h" |  | ||||||
| #include "2d/Image.h" | #include "2d/Image.h" | ||||||
|  | #include "2d/GifImage.h" | ||||||
| #include "2d/Frames.h" | #include "2d/Frames.h" | ||||||
| #include "2d/Geometry.h" | #include "2d/Geometry.h" | ||||||
| #include "2d/Action.h" | #include "2d/Action.h" | ||||||
|  |  | ||||||
|  | @ -259,7 +259,7 @@ namespace kiwano | ||||||
| 		return S_OK; | 		return S_OK; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	HRESULT Renderer::DrawBitmap(ComPtr<ID2D1Bitmap> const & bitmap) | 	HRESULT Renderer::DrawBitmap(ComPtr<ID2D1Bitmap> const & bitmap, Rect const& src_rect, Rect const& dest_rect) | ||||||
| 	{ | 	{ | ||||||
| 		if (!device_context_) | 		if (!device_context_) | ||||||
| 			return E_UNEXPECTED; | 			return E_UNEXPECTED; | ||||||
|  | @ -268,13 +268,12 @@ namespace kiwano | ||||||
| 			return S_OK; | 			return S_OK; | ||||||
| 
 | 
 | ||||||
| 		// Do not crop bitmap 
 | 		// Do not crop bitmap 
 | ||||||
| 		D2D_RECT_F rect = D2D1::RectF(0.f, 0.f, bitmap->GetSize().width, bitmap->GetSize().height); |  | ||||||
| 		device_context_->DrawBitmap( | 		device_context_->DrawBitmap( | ||||||
| 			bitmap.Get(), | 			bitmap.Get(), | ||||||
| 			rect, | 			DX::ConvertToRectF(dest_rect), | ||||||
| 			opacity_, | 			opacity_, | ||||||
| 			D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, | 			D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, | ||||||
| 			rect | 			DX::ConvertToRectF(src_rect) | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 		if (collecting_data_) | 		if (collecting_data_) | ||||||
|  |  | ||||||
|  | @ -71,7 +71,9 @@ namespace kiwano | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 		HRESULT DrawBitmap( | 		HRESULT DrawBitmap( | ||||||
| 			ComPtr<ID2D1Bitmap> const& bitmap | 			ComPtr<ID2D1Bitmap> const& bitmap, | ||||||
|  | 			Rect const& src_rect, | ||||||
|  | 			Rect const& dest_rect | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 		HRESULT DrawTextLayout( | 		HRESULT DrawTextLayout( | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue