add GifSprite
This commit is contained in:
parent
2f2cbcd4f1
commit
961b864dee
|
|
@ -21,27 +21,19 @@
|
|||
#include "GifImage.h"
|
||||
#include "../base/logs.h"
|
||||
#include "../platform/modules.h"
|
||||
#include "../utils/FileUtil.h"
|
||||
|
||||
namespace kiwano
|
||||
{
|
||||
GifImage::GifImage()
|
||||
: animating_(false)
|
||||
, next_index_(0)
|
||||
, total_loop_count_(1)
|
||||
, loop_count_(0)
|
||||
, frames_count_(0)
|
||||
: frames_count_(0)
|
||||
, disposal_type_(DisposalType::Unknown)
|
||||
, width_in_pixels_(0)
|
||||
, height_in_pixels_(0)
|
||||
, frame_delay_(0)
|
||||
, frame_position_{}
|
||||
, bg_color_{}
|
||||
{
|
||||
factory_ = Renderer::Instance()->GetD2DDeviceResources()->GetWICImagingFactory();
|
||||
auto ctx = Renderer::Instance()->GetD2DDeviceResources()->GetDeviceContext();
|
||||
|
||||
ThrowIfFailed(
|
||||
ctx->CreateCompatibleRenderTarget(&frame_rt_)
|
||||
);
|
||||
}
|
||||
|
||||
GifImage::GifImage(Resource const& res)
|
||||
|
|
@ -54,23 +46,25 @@ namespace kiwano
|
|||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
next_index_ = 0;
|
||||
loop_count_ = 0;
|
||||
frames_count_ = 0;
|
||||
disposal_type_ = DisposalType::None;
|
||||
|
||||
saved_frame_.Reset();
|
||||
decoder_.Reset();
|
||||
|
||||
auto factory = Renderer::Instance()->GetD2DDeviceResources()->GetWICImagingFactory();
|
||||
|
||||
if (res.IsFileType())
|
||||
{
|
||||
if (!modules::Shlwapi::Get().PathFileExistsW(res.GetFileName().c_str()))
|
||||
#ifdef KGE_DEBUG
|
||||
if (!FileUtil::ExistsFile(res.GetFileName().c_str()))
|
||||
{
|
||||
KGE_WARNING_LOG(L"Gif file '%s' not found!", res.GetFileName().c_str());
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
hr = factory_->CreateDecoderFromFilename(
|
||||
hr = factory->CreateDecoderFromFilename(
|
||||
res.GetFileName().c_str(),
|
||||
nullptr,
|
||||
GENERIC_READ,
|
||||
|
|
@ -87,7 +81,7 @@ namespace kiwano
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = factory_->CreateStream(&stream);
|
||||
hr = factory->CreateStream(&stream);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
|
|
@ -100,7 +94,7 @@ namespace kiwano
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = factory_->CreateDecoderFromStream(
|
||||
hr = factory->CreateDecoderFromStream(
|
||||
stream.Get(),
|
||||
nullptr,
|
||||
WICDecodeMetadataCacheOnLoad,
|
||||
|
|
@ -114,54 +108,9 @@ namespace kiwano
|
|||
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;
|
||||
|
|
@ -176,7 +125,8 @@ namespace kiwano
|
|||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Format convert to 32bppPBGRA which D2D expects
|
||||
hr = factory_->CreateFormatConverter(&converter);
|
||||
auto factory = Renderer::Instance()->GetD2DDeviceResources()->GetWICImagingFactory();
|
||||
hr = factory->CreateFormatConverter(&converter);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
|
|
@ -269,7 +219,7 @@ namespace kiwano
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
unsigned int frame_delay = 0;
|
||||
frame_delay_ = 0;
|
||||
|
||||
hr = metadata_reader->GetMetadataByName(
|
||||
L"/grctlext/Delay",
|
||||
|
|
@ -280,24 +230,22 @@ namespace kiwano
|
|||
hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = UIntMult(prop_val.uiVal, 10, &frame_delay);
|
||||
hr = UIntMult(prop_val.uiVal, 10, &frame_delay_);
|
||||
}
|
||||
PropVariantClear(&prop_val);
|
||||
}
|
||||
else
|
||||
{
|
||||
frame_delay = 0;
|
||||
frame_delay_ = 0;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// 插入一个强制延迟
|
||||
if (frame_delay < 90)
|
||||
if (frame_delay_ < 90)
|
||||
{
|
||||
frame_delay = 90;
|
||||
frame_delay_ = 90;
|
||||
}
|
||||
|
||||
frame_delay_.SetMilliseconds(static_cast<long>(frame_delay));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -341,8 +289,7 @@ namespace kiwano
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = decoder_->GetMetadataQueryReader(
|
||||
&metadata_reader);
|
||||
hr = decoder_->GetMetadataQueryReader(&metadata_reader);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
|
|
@ -428,8 +375,6 @@ namespace kiwano
|
|||
width_in_pixels_ = width;
|
||||
height_in_pixels_ = height;
|
||||
}
|
||||
|
||||
SetSize(static_cast<float>(width_in_pixels_), static_cast<float>(height_in_pixels_));
|
||||
}
|
||||
::PropVariantClear(&prop_val);
|
||||
}
|
||||
|
|
@ -439,28 +384,7 @@ namespace kiwano
|
|||
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 GifImage::DisposeCurrentFrame(ComPtr<ID2D1BitmapRenderTarget> frame_rt)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
|
|
@ -471,11 +395,11 @@ namespace kiwano
|
|||
break;
|
||||
case DisposalType::Background:
|
||||
// 用背景颜色清除当前原始帧覆盖的区域
|
||||
hr = ClearCurrentFrameArea();
|
||||
hr = ClearCurrentFrameArea(frame_rt);
|
||||
break;
|
||||
case DisposalType::Previous:
|
||||
// 恢复先前构图的帧
|
||||
hr = RestoreSavedFrame();
|
||||
hr = RestoreSavedFrame(frame_rt);
|
||||
break;
|
||||
default:
|
||||
hr = E_FAIL;
|
||||
|
|
@ -483,59 +407,13 @@ namespace kiwano
|
|||
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 GifImage::SaveComposedFrame(ComPtr<ID2D1BitmapRenderTarget> frame_rt)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
ComPtr<ID2D1Bitmap> frame_to_be_saved;
|
||||
|
||||
hr = frame_rt_->GetBitmap(&frame_to_be_saved);
|
||||
hr = frame_rt->GetBitmap(&frame_to_be_saved);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
if (saved_frame_ == nullptr)
|
||||
|
|
@ -543,7 +421,7 @@ namespace kiwano
|
|||
auto size = frame_to_be_saved->GetPixelSize();
|
||||
auto prop = D2D1::BitmapProperties(frame_to_be_saved->GetPixelFormat());
|
||||
|
||||
hr = frame_rt_->CreateBitmap(size, prop, &saved_frame_);
|
||||
hr = frame_rt->CreateBitmap(size, prop, &saved_frame_);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -554,7 +432,7 @@ namespace kiwano
|
|||
return hr;
|
||||
}
|
||||
|
||||
HRESULT GifImage::RestoreSavedFrame()
|
||||
HRESULT GifImage::RestoreSavedFrame(ComPtr<ID2D1BitmapRenderTarget> frame_rt)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
|
|
@ -564,7 +442,7 @@ namespace kiwano
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = frame_rt_->GetBitmap(&frame_to_copy_to);
|
||||
hr = frame_rt->GetBitmap(&frame_to_copy_to);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
|
|
@ -575,15 +453,15 @@ namespace kiwano
|
|||
return hr;
|
||||
}
|
||||
|
||||
HRESULT GifImage::ClearCurrentFrameArea()
|
||||
HRESULT GifImage::ClearCurrentFrameArea(ComPtr<ID2D1BitmapRenderTarget> frame_rt)
|
||||
{
|
||||
frame_rt_->BeginDraw();
|
||||
frame_rt->BeginDraw();
|
||||
|
||||
frame_rt_->PushAxisAlignedClip(&frame_position_, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
frame_rt_->Clear(bg_color_);
|
||||
frame_rt_->PopAxisAlignedClip();
|
||||
frame_rt->PushAxisAlignedClip(&frame_position_, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
frame_rt->Clear(bg_color_);
|
||||
frame_rt->PopAxisAlignedClip();
|
||||
|
||||
return frame_rt_->EndDraw();
|
||||
return frame_rt->EndDraw();
|
||||
}
|
||||
|
||||
HRESULT GifImage::GetBackgroundColor(IWICMetadataQueryReader* metadata_reader)
|
||||
|
|
@ -627,7 +505,8 @@ namespace kiwano
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = factory_->CreatePalette(&wic_palette);
|
||||
auto factory = Renderer::Instance()->GetD2DDeviceResources()->GetWICImagingFactory();
|
||||
hr = factory->CreatePalette(&wic_palette);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
|
|
|
|||
|
|
@ -19,19 +19,16 @@
|
|||
// THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
#include "Node.h"
|
||||
#include "include-forwards.h"
|
||||
#include "../base/Resource.h"
|
||||
#include "../renderer/render.h"
|
||||
|
||||
namespace kiwano
|
||||
{
|
||||
class KGE_API GifImage
|
||||
: public VisualNode
|
||||
: public Object
|
||||
{
|
||||
public:
|
||||
typedef Closure<void(int)> LoopDoneCallback;
|
||||
typedef Closure<void()> DoneCallback;
|
||||
|
||||
GifImage();
|
||||
|
||||
GifImage(
|
||||
|
|
@ -42,53 +39,15 @@ namespace kiwano
|
|||
Resource const& res
|
||||
);
|
||||
|
||||
// 设置 GIF 动画循环次数
|
||||
inline void SetLoopCount(int loops) { total_loop_count_ = loops; }
|
||||
inline unsigned int GetWidthInPixels() const { return width_in_pixels_; }
|
||||
|
||||
// 设置 GIF 动画每次循环结束回调函数
|
||||
inline void SetLoopDoneCallback(LoopDoneCallback const& cb) { loop_cb_ = cb; }
|
||||
inline unsigned int GetHeightInPixels() const { return height_in_pixels_; }
|
||||
|
||||
// 设置 GIF 动画结束回调函数
|
||||
inline void SetDoneCallback(DoneCallback const& cb) { done_cb_ = cb; }
|
||||
inline unsigned int GetFrameDelay() const { return frame_delay_; }
|
||||
|
||||
// 重新播放动画
|
||||
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_;
|
||||
inline unsigned int GetFramesCount() const { return frames_count_; }
|
||||
|
||||
public:
|
||||
enum class DisposalType
|
||||
{
|
||||
Unknown,
|
||||
|
|
@ -97,18 +56,37 @@ namespace kiwano
|
|||
Previous
|
||||
};
|
||||
|
||||
bool animating_;
|
||||
int total_loop_count_;
|
||||
int loop_count_;
|
||||
unsigned int next_index_;
|
||||
inline DisposalType GetDisposalType() const { return disposal_type_; }
|
||||
|
||||
inline D2D1_COLOR_F GetBackgroundColor() const { return bg_color_; }
|
||||
|
||||
inline D2D1_RECT_F const& GetFramePosition() const { return frame_position_; }
|
||||
|
||||
inline ComPtr<ID2D1Bitmap> GetRawFrame() const { return raw_frame_; }
|
||||
|
||||
inline void SetDisposalType(DisposalType type) { disposal_type_ = type; }
|
||||
|
||||
public:
|
||||
HRESULT GetRawFrame(UINT frame_index);
|
||||
HRESULT GetGlobalMetadata();
|
||||
HRESULT GetBackgroundColor(IWICMetadataQueryReader* metadata_reader);
|
||||
|
||||
HRESULT DisposeCurrentFrame(ComPtr<ID2D1BitmapRenderTarget> frame_rt);
|
||||
HRESULT SaveComposedFrame(ComPtr<ID2D1BitmapRenderTarget> frame_rt);
|
||||
HRESULT RestoreSavedFrame(ComPtr<ID2D1BitmapRenderTarget> frame_rt);
|
||||
HRESULT ClearCurrentFrameArea(ComPtr<ID2D1BitmapRenderTarget> frame_rt);
|
||||
|
||||
protected:
|
||||
ComPtr<ID2D1Bitmap> raw_frame_;
|
||||
ComPtr<ID2D1Bitmap> saved_frame_;
|
||||
ComPtr<IWICBitmapDecoder> decoder_;
|
||||
|
||||
unsigned int frames_count_;
|
||||
unsigned int frame_delay_;
|
||||
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_;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,194 @@
|
|||
// 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 "GifSprite.h"
|
||||
#include "GifImage.h"
|
||||
#include "../base/logs.h"
|
||||
#include "../platform/modules.h"
|
||||
|
||||
namespace kiwano
|
||||
{
|
||||
GifSprite::GifSprite()
|
||||
: animating_(false)
|
||||
, next_index_(0)
|
||||
, total_loop_count_(1)
|
||||
, loop_count_(0)
|
||||
{
|
||||
}
|
||||
|
||||
GifSprite::GifSprite(Resource const& res)
|
||||
: GifSprite()
|
||||
{
|
||||
Load(res);
|
||||
}
|
||||
|
||||
GifSprite::GifSprite(GifImagePtr image)
|
||||
{
|
||||
Load(image);
|
||||
}
|
||||
|
||||
bool GifSprite::Load(Resource const& res)
|
||||
{
|
||||
GifImagePtr image = new (std::nothrow) GifImage;
|
||||
if (image->Load(res))
|
||||
{
|
||||
return Load(image);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GifSprite::Load(GifImagePtr image)
|
||||
{
|
||||
if (image && image_ != image)
|
||||
{
|
||||
image_ = image;
|
||||
|
||||
next_index_ = 0;
|
||||
loop_count_ = 0;
|
||||
|
||||
SetSize(
|
||||
static_cast<float>(image_->GetWidthInPixels()),
|
||||
static_cast<float>(image_->GetHeightInPixels())
|
||||
);
|
||||
|
||||
if (!frame_rt_)
|
||||
{
|
||||
auto ctx = Renderer::Instance()->GetD2DDeviceResources()->GetDeviceContext();
|
||||
ThrowIfFailed(
|
||||
ctx->CreateCompatibleRenderTarget(&frame_rt_)
|
||||
);
|
||||
}
|
||||
|
||||
if (image_->GetFramesCount() > 0)
|
||||
{
|
||||
ComposeNextFrame();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GifSprite::Update(Duration dt)
|
||||
{
|
||||
VisualNode::Update(dt);
|
||||
|
||||
if (image_ && animating_)
|
||||
{
|
||||
frame_elapsed_ += dt;
|
||||
if (frame_delay_ <= frame_elapsed_)
|
||||
{
|
||||
frame_delay_ -= frame_elapsed_;
|
||||
frame_elapsed_ = 0;
|
||||
ComposeNextFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GifSprite::OnRender()
|
||||
{
|
||||
if (frame_to_render_)
|
||||
{
|
||||
Rect bounds = GetBounds();
|
||||
Renderer::Instance()->DrawBitmap(frame_to_render_, bounds, bounds);
|
||||
}
|
||||
}
|
||||
|
||||
void GifSprite::RestartAnimation()
|
||||
{
|
||||
animating_ = true;
|
||||
next_index_ = 0;
|
||||
loop_count_ = 0;
|
||||
image_->SetDisposalType(GifImage::DisposalType::None);
|
||||
}
|
||||
|
||||
void GifSprite::ComposeNextFrame()
|
||||
{
|
||||
if (frame_rt_)
|
||||
{
|
||||
// 找到延迟大于 0 的帧 (0 延迟帧是不可见的中间帧)
|
||||
HRESULT hr = E_FAIL;
|
||||
do
|
||||
{
|
||||
hr = image_->DisposeCurrentFrame(frame_rt_);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = OverlayNextFrame();
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
frame_delay_.SetMilliseconds(static_cast<long>(image_->GetFrameDelay()));
|
||||
}
|
||||
} while (SUCCEEDED(hr) && frame_delay_.IsZero() && !IsLastFrame());
|
||||
|
||||
animating_ = (SUCCEEDED(hr) && !EndOfAnimation() && image_->GetFramesCount() > 1);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT GifSprite::OverlayNextFrame()
|
||||
{
|
||||
HRESULT hr = image_->GetRawFrame(next_index_);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
if (image_->GetDisposalType() == GifImage::DisposalType::Previous)
|
||||
{
|
||||
hr = image_->SaveComposedFrame(frame_rt_);
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
frame_rt_->BeginDraw();
|
||||
|
||||
if (next_index_ == 0)
|
||||
{
|
||||
// 重新绘制背景
|
||||
frame_rt_->Clear(image_->GetBackgroundColor());
|
||||
loop_count_++;
|
||||
}
|
||||
|
||||
frame_rt_->DrawBitmap(image_->GetRawFrame().Get(), image_->GetFramePosition());
|
||||
hr = frame_rt_->EndDraw();
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
frame_to_render_ = nullptr;
|
||||
hr = frame_rt_->GetBitmap(&frame_to_render_);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
next_index_ = (++next_index_) % image_->GetFramesCount();
|
||||
}
|
||||
|
||||
if (IsLastFrame() && loop_cb_)
|
||||
{
|
||||
loop_cb_(loop_count_ - 1);
|
||||
}
|
||||
|
||||
if (EndOfAnimation() && done_cb_)
|
||||
{
|
||||
done_cb_();
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// 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 GifSprite
|
||||
: public VisualNode
|
||||
{
|
||||
public:
|
||||
using LoopDoneCallback = Closure<void(int)>;
|
||||
using DoneCallback = Closure<void()>;
|
||||
|
||||
GifSprite();
|
||||
|
||||
GifSprite(
|
||||
Resource const& res
|
||||
);
|
||||
|
||||
GifSprite(
|
||||
GifImagePtr image
|
||||
);
|
||||
|
||||
bool Load(
|
||||
Resource const& res
|
||||
);
|
||||
|
||||
bool Load(
|
||||
GifImagePtr image
|
||||
);
|
||||
|
||||
// 设置 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 RestartAnimation();
|
||||
|
||||
inline LoopDoneCallback GetLoopDoneCallback() const { return loop_cb_; }
|
||||
|
||||
inline DoneCallback GetDoneCallback() const { return done_cb_; }
|
||||
|
||||
void OnRender() override;
|
||||
|
||||
protected:
|
||||
void Update(Duration dt) override;
|
||||
|
||||
void ComposeNextFrame();
|
||||
|
||||
HRESULT OverlayNextFrame();
|
||||
|
||||
inline bool IsLastFrame() const { return (next_index_ == 0); }
|
||||
|
||||
inline bool EndOfAnimation() const { return IsLastFrame() && loop_count_ == total_loop_count_ + 1; }
|
||||
|
||||
protected:
|
||||
bool animating_;
|
||||
int total_loop_count_;
|
||||
int loop_count_;
|
||||
unsigned int next_index_;
|
||||
Duration frame_delay_;
|
||||
Duration frame_elapsed_;
|
||||
|
||||
LoopDoneCallback loop_cb_;
|
||||
DoneCallback done_cb_;
|
||||
|
||||
GifImagePtr image_;
|
||||
ComPtr<ID2D1Bitmap> frame_to_render_;
|
||||
ComPtr<ID2D1BitmapRenderTarget> frame_rt_;
|
||||
};
|
||||
}
|
||||
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
#include "Image.h"
|
||||
#include "../base/logs.h"
|
||||
#include "../renderer/render.h"
|
||||
#include "../platform/modules.h"
|
||||
#include "../utils/FileUtil.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#pragma once
|
||||
#include "include-forwards.h"
|
||||
#include "../base/Resource.h"
|
||||
#include <d2d1.h>
|
||||
#include "../renderer/render.h"
|
||||
|
||||
namespace kiwano
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ namespace kiwano
|
|||
|
||||
bool Sprite::Load(ImagePtr image)
|
||||
{
|
||||
if (image)
|
||||
if (image && image_ != image)
|
||||
{
|
||||
image_ = image;
|
||||
|
||||
|
|
@ -65,18 +65,10 @@ namespace kiwano
|
|||
|
||||
bool Sprite::Load(Resource const& res)
|
||||
{
|
||||
if (!image_)
|
||||
ImagePtr image = new (std::nothrow) Image;
|
||||
if (image->Load(res))
|
||||
{
|
||||
image_ = new (std::nothrow) Image;
|
||||
}
|
||||
|
||||
if (image_)
|
||||
{
|
||||
if (image_->Load(res))
|
||||
{
|
||||
Node::SetSize(image_->GetWidth(), image_->GetHeight());
|
||||
return true;
|
||||
}
|
||||
return Load(image);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
namespace kiwano
|
||||
{
|
||||
KGE_DECLARE_SMART_PTR(Image);
|
||||
KGE_DECLARE_SMART_PTR(GifImage);
|
||||
KGE_DECLARE_SMART_PTR(Frames);
|
||||
|
||||
KGE_DECLARE_SMART_PTR(Geometry);
|
||||
|
|
@ -49,7 +50,7 @@ namespace kiwano
|
|||
KGE_DECLARE_SMART_PTR(Scene);
|
||||
KGE_DECLARE_SMART_PTR(Layer);
|
||||
KGE_DECLARE_SMART_PTR(Sprite);
|
||||
KGE_DECLARE_SMART_PTR(GifImage);
|
||||
KGE_DECLARE_SMART_PTR(GifSprite);
|
||||
KGE_DECLARE_SMART_PTR(Text);
|
||||
KGE_DECLARE_SMART_PTR(Canvas);
|
||||
KGE_DECLARE_SMART_PTR(GeometryNode);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClInclude Include="2d\GifSprite.h" />
|
||||
<ClInclude Include="base\types.h" />
|
||||
<ClInclude Include="kiwano.h" />
|
||||
<ClInclude Include="config.h" />
|
||||
|
|
@ -93,6 +94,7 @@
|
|||
<ClCompile Include="2d\Geometry.cpp" />
|
||||
<ClCompile Include="2d\GeometryNode.cpp" />
|
||||
<ClCompile Include="2d\GifImage.cpp" />
|
||||
<ClCompile Include="2d\GifSprite.cpp" />
|
||||
<ClCompile Include="2d\Image.cpp" />
|
||||
<ClCompile Include="2d\Layer.cpp" />
|
||||
<ClCompile Include="2d\Node.cpp" />
|
||||
|
|
|
|||
|
|
@ -261,6 +261,9 @@
|
|||
<ClInclude Include="base\types.h">
|
||||
<Filter>base</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="2d\GifSprite.h">
|
||||
<Filter>2d</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ui\Button.cpp">
|
||||
|
|
@ -395,5 +398,8 @@
|
|||
<ClCompile Include="utils\FileUtil.cpp">
|
||||
<Filter>utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="2d\GifSprite.cpp">
|
||||
<Filter>2d</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -98,6 +98,7 @@
|
|||
#include "2d/Scene.h"
|
||||
#include "2d/Layer.h"
|
||||
#include "2d/Sprite.h"
|
||||
#include "2d/GifSprite.h"
|
||||
#include "2d/Text.h"
|
||||
#include "2d/Canvas.h"
|
||||
#include "2d/GeometryNode.h"
|
||||
|
|
|
|||
Loading…
Reference in New Issue