add GifSprite

This commit is contained in:
Nomango 2019-07-31 16:22:33 +08:00
parent 2f2cbcd4f1
commit 961b864dee
11 changed files with 375 additions and 226 deletions

View File

@ -21,27 +21,19 @@
#include "GifImage.h" #include "GifImage.h"
#include "../base/logs.h" #include "../base/logs.h"
#include "../platform/modules.h" #include "../platform/modules.h"
#include "../utils/FileUtil.h"
namespace kiwano namespace kiwano
{ {
GifImage::GifImage() GifImage::GifImage()
: animating_(false) : frames_count_(0)
, next_index_(0)
, total_loop_count_(1)
, loop_count_(0)
, frames_count_(0)
, disposal_type_(DisposalType::Unknown) , disposal_type_(DisposalType::Unknown)
, width_in_pixels_(0) , width_in_pixels_(0)
, height_in_pixels_(0) , height_in_pixels_(0)
, frame_delay_(0)
, frame_position_{} , frame_position_{}
, bg_color_{} , bg_color_{}
{ {
factory_ = Renderer::Instance()->GetD2DDeviceResources()->GetWICImagingFactory();
auto ctx = Renderer::Instance()->GetD2DDeviceResources()->GetDeviceContext();
ThrowIfFailed(
ctx->CreateCompatibleRenderTarget(&frame_rt_)
);
} }
GifImage::GifImage(Resource const& res) GifImage::GifImage(Resource const& res)
@ -54,23 +46,25 @@ namespace kiwano
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
next_index_ = 0;
loop_count_ = 0;
frames_count_ = 0; frames_count_ = 0;
disposal_type_ = DisposalType::None; disposal_type_ = DisposalType::None;
saved_frame_.Reset(); saved_frame_.Reset();
decoder_.Reset(); decoder_.Reset();
auto factory = Renderer::Instance()->GetD2DDeviceResources()->GetWICImagingFactory();
if (res.IsFileType()) 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()); KGE_WARNING_LOG(L"Gif file '%s' not found!", res.GetFileName().c_str());
return false; return false;
} }
#endif
hr = factory_->CreateDecoderFromFilename( hr = factory->CreateDecoderFromFilename(
res.GetFileName().c_str(), res.GetFileName().c_str(),
nullptr, nullptr,
GENERIC_READ, GENERIC_READ,
@ -87,7 +81,7 @@ namespace kiwano
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
hr = factory_->CreateStream(&stream); hr = factory->CreateStream(&stream);
} }
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
@ -100,7 +94,7 @@ namespace kiwano
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
hr = factory_->CreateDecoderFromStream( hr = factory->CreateDecoderFromStream(
stream.Get(), stream.Get(),
nullptr, nullptr,
WICDecodeMetadataCacheOnLoad, WICDecodeMetadataCacheOnLoad,
@ -114,54 +108,9 @@ namespace kiwano
hr = GetGlobalMetadata(); hr = GetGlobalMetadata();
} }
if (SUCCEEDED(hr))
{
if (frames_count_ > 0)
{
hr = ComposeNextFrame();
}
}
return SUCCEEDED(hr); 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) HRESULT GifImage::GetRawFrame(UINT frame_index)
{ {
ComPtr<IWICFormatConverter> converter; ComPtr<IWICFormatConverter> converter;
@ -176,7 +125,8 @@ namespace kiwano
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
// Format convert to 32bppPBGRA which D2D expects // Format convert to 32bppPBGRA which D2D expects
hr = factory_->CreateFormatConverter(&converter); auto factory = Renderer::Instance()->GetD2DDeviceResources()->GetWICImagingFactory();
hr = factory->CreateFormatConverter(&converter);
} }
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
@ -269,7 +219,7 @@ namespace kiwano
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
unsigned int frame_delay = 0; frame_delay_ = 0;
hr = metadata_reader->GetMetadataByName( hr = metadata_reader->GetMetadataByName(
L"/grctlext/Delay", L"/grctlext/Delay",
@ -280,24 +230,22 @@ namespace kiwano
hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
hr = UIntMult(prop_val.uiVal, 10, &frame_delay); hr = UIntMult(prop_val.uiVal, 10, &frame_delay_);
} }
PropVariantClear(&prop_val); PropVariantClear(&prop_val);
} }
else else
{ {
frame_delay = 0; frame_delay_ = 0;
} }
if (SUCCEEDED(hr)) 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)) if (SUCCEEDED(hr))
{ {
hr = decoder_->GetMetadataQueryReader( hr = decoder_->GetMetadataQueryReader(&metadata_reader);
&metadata_reader);
} }
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
@ -428,8 +375,6 @@ namespace kiwano
width_in_pixels_ = width; width_in_pixels_ = width;
height_in_pixels_ = height; height_in_pixels_ = height;
} }
SetSize(static_cast<float>(width_in_pixels_), static_cast<float>(height_in_pixels_));
} }
::PropVariantClear(&prop_val); ::PropVariantClear(&prop_val);
} }
@ -439,28 +384,7 @@ namespace kiwano
return hr; return hr;
} }
HRESULT GifImage::ComposeNextFrame() HRESULT GifImage::DisposeCurrentFrame(ComPtr<ID2D1BitmapRenderTarget> frame_rt)
{
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; HRESULT hr = S_OK;
@ -471,11 +395,11 @@ namespace kiwano
break; break;
case DisposalType::Background: case DisposalType::Background:
// 用背景颜色清除当前原始帧覆盖的区域 // 用背景颜色清除当前原始帧覆盖的区域
hr = ClearCurrentFrameArea(); hr = ClearCurrentFrameArea(frame_rt);
break; break;
case DisposalType::Previous: case DisposalType::Previous:
// 恢复先前构图的帧 // 恢复先前构图的帧
hr = RestoreSavedFrame(); hr = RestoreSavedFrame(frame_rt);
break; break;
default: default:
hr = E_FAIL; hr = E_FAIL;
@ -483,59 +407,13 @@ namespace kiwano
return hr; return hr;
} }
HRESULT GifImage::OverlayNextFrame() HRESULT GifImage::SaveComposedFrame(ComPtr<ID2D1BitmapRenderTarget> frame_rt)
{
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; HRESULT hr = S_OK;
ComPtr<ID2D1Bitmap> frame_to_be_saved; 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 (SUCCEEDED(hr))
{ {
if (saved_frame_ == nullptr) if (saved_frame_ == nullptr)
@ -543,7 +421,7 @@ namespace kiwano
auto size = frame_to_be_saved->GetPixelSize(); auto size = frame_to_be_saved->GetPixelSize();
auto prop = D2D1::BitmapProperties(frame_to_be_saved->GetPixelFormat()); 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; return hr;
} }
HRESULT GifImage::RestoreSavedFrame() HRESULT GifImage::RestoreSavedFrame(ComPtr<ID2D1BitmapRenderTarget> frame_rt)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
@ -564,7 +442,7 @@ namespace kiwano
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
hr = frame_rt_->GetBitmap(&frame_to_copy_to); hr = frame_rt->GetBitmap(&frame_to_copy_to);
} }
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
@ -575,15 +453,15 @@ namespace kiwano
return hr; 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->PushAxisAlignedClip(&frame_position_, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
frame_rt_->Clear(bg_color_); frame_rt->Clear(bg_color_);
frame_rt_->PopAxisAlignedClip(); frame_rt->PopAxisAlignedClip();
return frame_rt_->EndDraw(); return frame_rt->EndDraw();
} }
HRESULT GifImage::GetBackgroundColor(IWICMetadataQueryReader* metadata_reader) HRESULT GifImage::GetBackgroundColor(IWICMetadataQueryReader* metadata_reader)
@ -627,7 +505,8 @@ namespace kiwano
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
hr = factory_->CreatePalette(&wic_palette); auto factory = Renderer::Instance()->GetD2DDeviceResources()->GetWICImagingFactory();
hr = factory->CreatePalette(&wic_palette);
} }
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))

View File

@ -19,19 +19,16 @@
// THE SOFTWARE. // THE SOFTWARE.
#pragma once #pragma once
#include "Node.h" #include "include-forwards.h"
#include "../base/Resource.h" #include "../base/Resource.h"
#include "../renderer/render.h" #include "../renderer/render.h"
namespace kiwano namespace kiwano
{ {
class KGE_API GifImage class KGE_API GifImage
: public VisualNode : public Object
{ {
public: public:
typedef Closure<void(int)> LoopDoneCallback;
typedef Closure<void()> DoneCallback;
GifImage(); GifImage();
GifImage( GifImage(
@ -42,53 +39,15 @@ namespace kiwano
Resource const& res Resource const& res
); );
// 设置 GIF 动画循环次数 inline unsigned int GetWidthInPixels() const { return width_in_pixels_; }
inline void SetLoopCount(int loops) { total_loop_count_ = loops; }
// 设置 GIF 动画每次循环结束回调函数 inline unsigned int GetHeightInPixels() const { return height_in_pixels_; }
inline void SetLoopDoneCallback(LoopDoneCallback const& cb) { loop_cb_ = cb; }
// 设置 GIF 动画结束回调函数 inline unsigned int GetFrameDelay() const { return frame_delay_; }
inline void SetDoneCallback(DoneCallback const& cb) { done_cb_ = cb; }
// 重新播放动画 inline unsigned int GetFramesCount() const { return frames_count_; }
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_;
public:
enum class DisposalType enum class DisposalType
{ {
Unknown, Unknown,
@ -97,18 +56,37 @@ namespace kiwano
Previous Previous
}; };
bool animating_; inline DisposalType GetDisposalType() const { return disposal_type_; }
int total_loop_count_;
int loop_count_; inline D2D1_COLOR_F GetBackgroundColor() const { return bg_color_; }
unsigned int next_index_;
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 frames_count_;
unsigned int frame_delay_;
unsigned int width_in_pixels_; unsigned int width_in_pixels_;
unsigned int height_in_pixels_; unsigned int height_in_pixels_;
DisposalType disposal_type_; DisposalType disposal_type_;
D2D1_RECT_F frame_position_; D2D1_RECT_F frame_position_;
D2D1_COLOR_F bg_color_; D2D1_COLOR_F bg_color_;
LoopDoneCallback loop_cb_;
DoneCallback done_cb_;
}; };
} }

194
kiwano/2d/GifSprite.cpp Normal file
View File

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

97
kiwano/2d/GifSprite.h Normal file
View File

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

View File

@ -20,7 +20,6 @@
#include "Image.h" #include "Image.h"
#include "../base/logs.h" #include "../base/logs.h"
#include "../renderer/render.h"
#include "../platform/modules.h" #include "../platform/modules.h"
#include "../utils/FileUtil.h" #include "../utils/FileUtil.h"

View File

@ -21,7 +21,7 @@
#pragma once #pragma once
#include "include-forwards.h" #include "include-forwards.h"
#include "../base/Resource.h" #include "../base/Resource.h"
#include <d2d1.h> #include "../renderer/render.h"
namespace kiwano namespace kiwano
{ {

View File

@ -53,7 +53,7 @@ namespace kiwano
bool Sprite::Load(ImagePtr image) bool Sprite::Load(ImagePtr image)
{ {
if (image) if (image && image_ != image)
{ {
image_ = image; image_ = image;
@ -65,18 +65,10 @@ namespace kiwano
bool Sprite::Load(Resource const& res) bool Sprite::Load(Resource const& res)
{ {
if (!image_) ImagePtr image = new (std::nothrow) Image;
if (image->Load(res))
{ {
image_ = new (std::nothrow) Image; return Load(image);
}
if (image_)
{
if (image_->Load(res))
{
Node::SetSize(image_->GetWidth(), image_->GetHeight());
return true;
}
} }
return false; return false;
} }

View File

@ -35,6 +35,7 @@
namespace kiwano namespace kiwano
{ {
KGE_DECLARE_SMART_PTR(Image); KGE_DECLARE_SMART_PTR(Image);
KGE_DECLARE_SMART_PTR(GifImage);
KGE_DECLARE_SMART_PTR(Frames); KGE_DECLARE_SMART_PTR(Frames);
KGE_DECLARE_SMART_PTR(Geometry); KGE_DECLARE_SMART_PTR(Geometry);
@ -49,7 +50,7 @@ namespace kiwano
KGE_DECLARE_SMART_PTR(Scene); KGE_DECLARE_SMART_PTR(Scene);
KGE_DECLARE_SMART_PTR(Layer); KGE_DECLARE_SMART_PTR(Layer);
KGE_DECLARE_SMART_PTR(Sprite); KGE_DECLARE_SMART_PTR(Sprite);
KGE_DECLARE_SMART_PTR(GifImage); KGE_DECLARE_SMART_PTR(GifSprite);
KGE_DECLARE_SMART_PTR(Text); KGE_DECLARE_SMART_PTR(Text);
KGE_DECLARE_SMART_PTR(Canvas); KGE_DECLARE_SMART_PTR(Canvas);
KGE_DECLARE_SMART_PTR(GeometryNode); KGE_DECLARE_SMART_PTR(GeometryNode);

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<ClInclude Include="2d\GifSprite.h" />
<ClInclude Include="base\types.h" /> <ClInclude Include="base\types.h" />
<ClInclude Include="kiwano.h" /> <ClInclude Include="kiwano.h" />
<ClInclude Include="config.h" /> <ClInclude Include="config.h" />
@ -93,6 +94,7 @@
<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\GifImage.cpp" />
<ClCompile Include="2d\GifSprite.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" />

View File

@ -261,6 +261,9 @@
<ClInclude Include="base\types.h"> <ClInclude Include="base\types.h">
<Filter>base</Filter> <Filter>base</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="2d\GifSprite.h">
<Filter>2d</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="ui\Button.cpp"> <ClCompile Include="ui\Button.cpp">
@ -395,5 +398,8 @@
<ClCompile Include="utils\FileUtil.cpp"> <ClCompile Include="utils\FileUtil.cpp">
<Filter>utils</Filter> <Filter>utils</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="2d\GifSprite.cpp">
<Filter>2d</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -98,6 +98,7 @@
#include "2d/Scene.h" #include "2d/Scene.h"
#include "2d/Layer.h" #include "2d/Layer.h"
#include "2d/Sprite.h" #include "2d/Sprite.h"
#include "2d/GifSprite.h"
#include "2d/Text.h" #include "2d/Text.h"
#include "2d/Canvas.h" #include "2d/Canvas.h"
#include "2d/GeometryNode.h" #include "2d/GeometryNode.h"