LibGfx: Add frame_duration() to ImageDecoderPlugin
Add a virtual method to query frame durations without decoding pixel data. This is needed by the ImageDecoder service to extract timing metadata upfront for streaming animation decode. Implement the method for GIF, PNG, WebP, AVIF, and JPEGXL decoders. For WebP, extract durations from the demuxer during header decode so they are available before any frames are decoded.
This commit is contained in:
parent
01334c4481
commit
2d811fb432
11 changed files with 79 additions and 0 deletions
|
|
@ -186,6 +186,13 @@ ErrorOr<ImageFrameDescriptor> AVIFImageDecoderPlugin::frame(size_t index, Option
|
|||
return m_context->frame_descriptors[index];
|
||||
}
|
||||
|
||||
int AVIFImageDecoderPlugin::frame_duration(size_t index)
|
||||
{
|
||||
if (index >= m_context->frame_descriptors.size())
|
||||
return 0;
|
||||
return m_context->frame_descriptors[index].duration;
|
||||
}
|
||||
|
||||
ErrorOr<Optional<ReadonlyBytes>> AVIFImageDecoderPlugin::icc_data()
|
||||
{
|
||||
if (m_context->state < AVIFLoadingContext::State::HeaderDecoded)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public:
|
|||
virtual size_t frame_count() override;
|
||||
virtual size_t first_animated_frame_index() override;
|
||||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index, Optional<IntSize> ideal_size = {}) override;
|
||||
virtual int frame_duration(size_t index) override;
|
||||
virtual ErrorOr<Optional<ReadonlyBytes>> icc_data() override;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -469,6 +469,27 @@ size_t GIFImageDecoderPlugin::first_animated_frame_index()
|
|||
return 0;
|
||||
}
|
||||
|
||||
int GIFImageDecoderPlugin::frame_duration(size_t index)
|
||||
{
|
||||
if (m_context->error_state != GIFLoadingContext::ErrorState::NoError)
|
||||
return 0;
|
||||
|
||||
if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) {
|
||||
if (load_gif_frame_descriptors(*m_context).is_error()) {
|
||||
m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (index >= m_context->images.size())
|
||||
return 0;
|
||||
|
||||
int duration = m_context->images[index]->duration * 10;
|
||||
if (duration <= 10)
|
||||
duration = 100;
|
||||
return duration;
|
||||
}
|
||||
|
||||
ErrorOr<ImageFrameDescriptor> GIFImageDecoderPlugin::frame(size_t index, Optional<IntSize>)
|
||||
{
|
||||
if (m_context->error_state == GIFLoadingContext::ErrorState::FailedToDecodeAnyFrame) {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public:
|
|||
virtual size_t frame_count() override;
|
||||
virtual size_t first_animated_frame_index() override;
|
||||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index, Optional<IntSize> ideal_size = {}) override;
|
||||
virtual int frame_duration(size_t index) override;
|
||||
|
||||
private:
|
||||
GIFImageDecoderPlugin(FixedMemoryStream);
|
||||
|
|
|
|||
|
|
@ -83,6 +83,14 @@ public:
|
|||
|
||||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index, Optional<IntSize> ideal_size = {}) = 0;
|
||||
|
||||
// Returns the duration of a frame in milliseconds without decoding pixel data.
|
||||
// Not all formats support this; the default returns 0.
|
||||
virtual int frame_duration(size_t index)
|
||||
{
|
||||
(void)index;
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual Optional<Metadata const&> metadata() { return OptionalNone {}; }
|
||||
|
||||
virtual ErrorOr<Optional<Media::CodingIndependentCodePoints>> cicp() { return OptionalNone {}; }
|
||||
|
|
@ -110,6 +118,7 @@ public:
|
|||
size_t first_animated_frame_index() const { return m_plugin->first_animated_frame_index(); }
|
||||
|
||||
ErrorOr<ImageFrameDescriptor> frame(size_t index, Optional<IntSize> ideal_size = {}) const { return m_plugin->frame(index, ideal_size); }
|
||||
int frame_duration(size_t index) const { return m_plugin->frame_duration(index); }
|
||||
|
||||
Optional<Metadata const&> metadata() const { return m_plugin->metadata(); }
|
||||
ErrorOr<ColorSpace> color_space();
|
||||
|
|
|
|||
|
|
@ -266,6 +266,13 @@ ErrorOr<ImageFrameDescriptor> JPEGXLImageDecoderPlugin::frame(size_t index, Opti
|
|||
return m_context->frame_descriptors()[index];
|
||||
}
|
||||
|
||||
int JPEGXLImageDecoderPlugin::frame_duration(size_t index)
|
||||
{
|
||||
if (index >= m_context->frame_descriptors().size())
|
||||
return 0;
|
||||
return m_context->frame_descriptors()[index].duration;
|
||||
}
|
||||
|
||||
ErrorOr<Optional<ReadonlyBytes>> JPEGXLImageDecoderPlugin::icc_data()
|
||||
{
|
||||
return OptionalNone {};
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public:
|
|||
virtual ErrorOr<Optional<ReadonlyBytes>> icc_data() override;
|
||||
|
||||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index, Optional<IntSize> ideal_size = {}) override;
|
||||
virtual int frame_duration(size_t index) override;
|
||||
|
||||
private:
|
||||
JPEGXLImageDecoderPlugin(OwnPtr<JPEGXLLoadingContext>);
|
||||
|
|
|
|||
|
|
@ -104,6 +104,13 @@ size_t PNGImageDecoderPlugin::frame_count()
|
|||
return m_context->frame_count;
|
||||
}
|
||||
|
||||
int PNGImageDecoderPlugin::frame_duration(size_t index)
|
||||
{
|
||||
if (index >= m_context->frame_descriptors.size())
|
||||
return 0;
|
||||
return m_context->frame_descriptors[index].duration;
|
||||
}
|
||||
|
||||
ErrorOr<ImageFrameDescriptor> PNGImageDecoderPlugin::frame(size_t index, Optional<IntSize>)
|
||||
{
|
||||
if (index >= m_context->frame_descriptors.size())
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ public:
|
|||
virtual size_t frame_count() override;
|
||||
virtual size_t first_animated_frame_index() override;
|
||||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index, Optional<IntSize> ideal_size = {}) override;
|
||||
virtual int frame_duration(size_t index) override;
|
||||
virtual Optional<Metadata const&> metadata() override;
|
||||
virtual ErrorOr<Optional<Media::CodingIndependentCodePoints>> cicp() override;
|
||||
virtual ErrorOr<Optional<ReadonlyBytes>> icc_data() override;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ struct WebPLoadingContext {
|
|||
size_t frame_count;
|
||||
size_t loop_count;
|
||||
ByteBuffer icc_data;
|
||||
Vector<int> frame_durations;
|
||||
|
||||
Vector<ImageFrameDescriptor> frame_descriptors;
|
||||
};
|
||||
|
|
@ -91,6 +92,19 @@ static ErrorOr<void> decode_webp_header(WebPLoadingContext& context)
|
|||
|
||||
context.frame_count = anim_info.frame_count;
|
||||
context.loop_count = anim_info.loop_count;
|
||||
|
||||
// Extract frame durations from the demuxer without decoding pixels.
|
||||
auto* demux = WebPDemux(&webp_data);
|
||||
if (demux) {
|
||||
WebPIterator iter {};
|
||||
if (WebPDemuxGetFrame(demux, 1, &iter)) {
|
||||
do {
|
||||
context.frame_durations.append(iter.duration);
|
||||
} while (WebPDemuxNextFrame(&iter));
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
}
|
||||
WebPDemuxDelete(demux);
|
||||
}
|
||||
}
|
||||
|
||||
WebPData webp_data { .bytes = context.data.data(), .size = context.data.size() };
|
||||
|
|
@ -222,6 +236,15 @@ ErrorOr<ImageFrameDescriptor> WebPImageDecoderPlugin::frame(size_t index, Option
|
|||
return m_context->frame_descriptors[index];
|
||||
}
|
||||
|
||||
int WebPImageDecoderPlugin::frame_duration(size_t index)
|
||||
{
|
||||
if (index < m_context->frame_durations.size())
|
||||
return m_context->frame_durations[index];
|
||||
if (index < m_context->frame_descriptors.size())
|
||||
return m_context->frame_descriptors[index].duration;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ErrorOr<Optional<ReadonlyBytes>> WebPImageDecoderPlugin::icc_data()
|
||||
{
|
||||
if (m_context->state < WebPLoadingContext::State::HeaderDecoded)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ public:
|
|||
virtual size_t frame_count() override;
|
||||
virtual size_t first_animated_frame_index() override;
|
||||
virtual ErrorOr<ImageFrameDescriptor> frame(size_t index, Optional<IntSize> ideal_size = {}) override;
|
||||
virtual int frame_duration(size_t index) override;
|
||||
virtual ErrorOr<Optional<ReadonlyBytes>> icc_data() override;
|
||||
|
||||
private:
|
||||
|
|
|
|||
Loading…
Reference in a new issue