support GIT image
This commit is contained in:
parent
2ab6a7bd29
commit
6b84e909f7
|
|
@ -97,7 +97,12 @@ namespace kiwano
|
|||
|
||||
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:
|
||||
virtual void PrepareRender() {}
|
||||
|
||||
void Update(Duration dt);
|
||||
virtual void Update(Duration dt);
|
||||
|
||||
void Render();
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
<ClInclude Include="2d\Frames.h" />
|
||||
<ClInclude Include="2d\Geometry.h" />
|
||||
<ClInclude Include="2d\GeometryNode.h" />
|
||||
<ClInclude Include="2d\GifImage.h" />
|
||||
<ClInclude Include="2d\Image.h" />
|
||||
<ClInclude Include="2d\Layer.h" />
|
||||
<ClInclude Include="2d\Node.h" />
|
||||
|
|
@ -110,6 +111,7 @@
|
|||
<ClCompile Include="2d\Frames.cpp" />
|
||||
<ClCompile Include="2d\Geometry.cpp" />
|
||||
<ClCompile Include="2d\GeometryNode.cpp" />
|
||||
<ClCompile Include="2d\GifImage.cpp" />
|
||||
<ClCompile Include="2d\Image.cpp" />
|
||||
<ClCompile Include="2d\Layer.cpp" />
|
||||
<ClCompile Include="2d\Node.cpp" />
|
||||
|
|
|
|||
|
|
@ -318,6 +318,9 @@
|
|||
<ClInclude Include="audio\Sound.h">
|
||||
<Filter>audio</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="2d\GifImage.h">
|
||||
<Filter>2d</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ui\Button.cpp">
|
||||
|
|
@ -485,5 +488,8 @@
|
|||
<ClCompile Include="audio\Sound.cpp">
|
||||
<Filter>audio</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="2d\GifImage.cpp">
|
||||
<Filter>2d</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -62,6 +62,14 @@ namespace kiwano
|
|||
// 时长是否是零
|
||||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -68,21 +68,22 @@
|
|||
#include "platform/modules.h"
|
||||
#include "platform/Application.h"
|
||||
|
||||
#include "base/Object.h"
|
||||
#include "base/Event.hpp"
|
||||
#include "base/EventListener.h"
|
||||
#include "base/EventDispatcher.h"
|
||||
#include "base/Timer.h"
|
||||
#include "base/TimerManager.h"
|
||||
#include "base/AsyncTask.h"
|
||||
#include "base/Resource.h"
|
||||
|
||||
#include "2d/Font.hpp"
|
||||
#include "2d/Color.h"
|
||||
#include "2d/Transform.hpp"
|
||||
#include "2d/TextStyle.hpp"
|
||||
#include "base/Resource.h"
|
||||
|
||||
#include "base/Object.h"
|
||||
#include "2d/Image.h"
|
||||
#include "2d/GifImage.h"
|
||||
#include "2d/Frames.h"
|
||||
#include "2d/Geometry.h"
|
||||
#include "2d/Action.h"
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ namespace kiwano
|
|||
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_)
|
||||
return E_UNEXPECTED;
|
||||
|
|
@ -268,13 +268,12 @@ namespace kiwano
|
|||
return S_OK;
|
||||
|
||||
// Do not crop bitmap
|
||||
D2D_RECT_F rect = D2D1::RectF(0.f, 0.f, bitmap->GetSize().width, bitmap->GetSize().height);
|
||||
device_context_->DrawBitmap(
|
||||
bitmap.Get(),
|
||||
rect,
|
||||
DX::ConvertToRectF(dest_rect),
|
||||
opacity_,
|
||||
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
|
||||
rect
|
||||
DX::ConvertToRectF(src_rect)
|
||||
);
|
||||
|
||||
if (collecting_data_)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,9 @@ namespace kiwano
|
|||
);
|
||||
|
||||
HRESULT DrawBitmap(
|
||||
ComPtr<ID2D1Bitmap> const& bitmap
|
||||
ComPtr<ID2D1Bitmap> const& bitmap,
|
||||
Rect const& src_rect,
|
||||
Rect const& dest_rect
|
||||
);
|
||||
|
||||
HRESULT DrawTextLayout(
|
||||
|
|
|
|||
Loading…
Reference in New Issue