/* * Copyright (c) 2020, Hüseyin ASLITÜRK * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include namespace Gfx { template struct PortableImageMapLoadingContext { using FormatDetails = TFormatDetails; enum class Type { Unknown, ASCII, RAWBITS }; enum class State { NotDecoded = 0, Error, MagicNumber, Width, Height, Maxval, Bitmap, Decoded }; Type type { Type::Unknown }; State state { State::NotDecoded }; size_t width { 0 }; size_t height { 0 }; FormatDetails format_details {}; RefPtr bitmap; NonnullOwnPtr stream; PortableImageMapLoadingContext(NonnullOwnPtr stream) : stream(move(stream)) { } }; template class PortableImageDecoderPlugin final : public ImageDecoderPlugin { public: static bool sniff(ReadonlyBytes); static ErrorOr> create(ReadonlyBytes); virtual ~PortableImageDecoderPlugin() override = default; virtual IntSize size() override; virtual void set_volatile() override; [[nodiscard]] virtual bool set_nonvolatile(bool& was_purged) override; virtual bool initialize() override { return true; } virtual bool is_animated() override; virtual size_t loop_count() override; virtual size_t frame_count() override; virtual ErrorOr frame(size_t index) override; virtual ErrorOr> icc_data() override; private: PortableImageDecoderPlugin(NonnullOwnPtr stream); OwnPtr m_context; }; template PortableImageDecoderPlugin::PortableImageDecoderPlugin(NonnullOwnPtr stream) { m_context = make(move(stream)); } template IntSize PortableImageDecoderPlugin::size() { if (m_context->state == TContext::State::Error) return {}; if (m_context->state < TContext::State::Decoded) { if (decode(*m_context).is_error()) { m_context->state = TContext::State::Error; // FIXME: We should propagate errors return {}; } } return { m_context->width, m_context->height }; } template void PortableImageDecoderPlugin::set_volatile() { if (m_context->bitmap) m_context->bitmap->set_volatile(); } template bool PortableImageDecoderPlugin::set_nonvolatile(bool& was_purged) { if (!m_context->bitmap) return false; return m_context->bitmap->set_nonvolatile(was_purged); } template ErrorOr> PortableImageDecoderPlugin::create(ReadonlyBytes data) { auto stream = TRY(try_make(data)); return adopt_nonnull_own_or_enomem(new (nothrow) PortableImageDecoderPlugin(move(stream))); } template bool PortableImageDecoderPlugin::sniff(ReadonlyBytes data) { using Context = TContext; if (data.size() < 2) return false; if (data.data()[0] == 'P' && data.data()[1] == Context::FormatDetails::ascii_magic_number) return true; if (data.data()[0] == 'P' && data.data()[1] == Context::FormatDetails::binary_magic_number) return true; return false; } template bool PortableImageDecoderPlugin::is_animated() { return false; } template size_t PortableImageDecoderPlugin::loop_count() { return 0; } template size_t PortableImageDecoderPlugin::frame_count() { return 1; } template ErrorOr PortableImageDecoderPlugin::frame(size_t index) { if (index > 0) return Error::from_string_literal("PortableImageDecoderPlugin: Invalid frame index"); if (m_context->state == TContext::State::Error) return Error::from_string_literal("PortableImageDecoderPlugin: Decoding failed"); if (m_context->state < TContext::State::Decoded) { if (decode(*m_context).is_error()) { m_context->state = TContext::State::Error; return Error::from_string_literal("PortableImageDecoderPlugin: Decoding failed"); } } VERIFY(m_context->bitmap); return ImageFrameDescriptor { m_context->bitmap, 0 }; } template ErrorOr> PortableImageDecoderPlugin::icc_data() { return OptionalNone {}; } }