Jelajahi Sumber

LibGfx: Improve ImageDecoder construction

Previously, ImageDecoder::create() would return a NonnullRefPtr and
could not "fail", although the returned decoder may be "invalid" which
you then had to check anyway.

The new interface looks like this:

    static RefPtr<Gfx::ImageDecoder> try_create(ReadonlyBytes);

This simplifies ImageDecoder since it no longer has to worry about its
validity. Client code gets slightly clearer as well.
Andreas Kling 3 tahun lalu
induk
melakukan
d01b4327fa

+ 3 - 1
Userland/Libraries/LibGUI/ImageWidget.cpp

@@ -78,7 +78,9 @@ void ImageWidget::load_from_file(const StringView& path)
         return;
 
     auto& mapped_file = *file_or_error.value();
-    m_image_decoder = Gfx::ImageDecoder::create((const u8*)mapped_file.data(), mapped_file.size());
+    m_image_decoder = Gfx::ImageDecoder::try_create(mapped_file.bytes());
+    VERIFY(m_image_decoder);
+
     auto bitmap = m_image_decoder->bitmap();
     VERIFY(bitmap);
 

+ 46 - 32
Userland/Libraries/LibGfx/ImageDecoder.cpp

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -17,45 +17,61 @@
 
 namespace Gfx {
 
-ImageDecoder::ImageDecoder(const u8* data, size_t size)
+RefPtr<ImageDecoder> ImageDecoder::try_create(ReadonlyBytes bytes)
 {
-    m_plugin = make<PNGImageDecoderPlugin>(data, size);
-    if (m_plugin->sniff())
-        return;
+    auto* data = bytes.data();
+    auto size = bytes.size();
 
-    m_plugin = make<GIFImageDecoderPlugin>(data, size);
-    if (m_plugin->sniff())
-        return;
+    auto plugin = [](auto* data, auto size) -> OwnPtr<ImageDecoderPlugin> {
+        OwnPtr<ImageDecoderPlugin> plugin;
 
-    m_plugin = make<BMPImageDecoderPlugin>(data, size);
-    if (m_plugin->sniff())
-        return;
+        plugin = make<PNGImageDecoderPlugin>(data, size);
+        if (plugin->sniff())
+            return plugin;
 
-    m_plugin = make<PBMImageDecoderPlugin>(data, size);
-    if (m_plugin->sniff())
-        return;
+        plugin = make<GIFImageDecoderPlugin>(data, size);
+        if (plugin->sniff())
+            return plugin;
 
-    m_plugin = make<PGMImageDecoderPlugin>(data, size);
-    if (m_plugin->sniff())
-        return;
+        plugin = make<BMPImageDecoderPlugin>(data, size);
+        if (plugin->sniff())
+            return plugin;
 
-    m_plugin = make<PPMImageDecoderPlugin>(data, size);
-    if (m_plugin->sniff())
-        return;
+        plugin = make<PBMImageDecoderPlugin>(data, size);
+        if (plugin->sniff())
+            return plugin;
 
-    m_plugin = make<ICOImageDecoderPlugin>(data, size);
-    if (m_plugin->sniff())
-        return;
+        plugin = make<PGMImageDecoderPlugin>(data, size);
+        if (plugin->sniff())
+            return plugin;
 
-    m_plugin = make<JPGImageDecoderPlugin>(data, size);
-    if (m_plugin->sniff())
-        return;
+        plugin = make<PPMImageDecoderPlugin>(data, size);
+        if (plugin->sniff())
+            return plugin;
 
-    m_plugin = make<DDSImageDecoderPlugin>(data, size);
-    if (m_plugin->sniff())
-        return;
+        plugin = make<ICOImageDecoderPlugin>(data, size);
+        if (plugin->sniff())
+            return plugin;
 
-    m_plugin = nullptr;
+        plugin = make<JPGImageDecoderPlugin>(data, size);
+        if (plugin->sniff())
+            return plugin;
+
+        plugin = make<DDSImageDecoderPlugin>(data, size);
+        if (plugin->sniff())
+            return plugin;
+
+        return {};
+    }(data, size);
+
+    if (!plugin)
+        return {};
+    return adopt_ref_if_nonnull(new (nothrow) ImageDecoder(plugin.release_nonnull()));
+}
+
+ImageDecoder::ImageDecoder(NonnullOwnPtr<ImageDecoderPlugin> plugin)
+    : m_plugin(move(plugin))
+{
 }
 
 ImageDecoder::~ImageDecoder()
@@ -64,8 +80,6 @@ ImageDecoder::~ImageDecoder()
 
 RefPtr<Gfx::Bitmap> ImageDecoder::bitmap() const
 {
-    if (!m_plugin)
-        return nullptr;
     return m_plugin->bitmap();
 }
 

+ 12 - 19
Userland/Libraries/LibGfx/ImageDecoder.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -48,32 +48,25 @@ protected:
 
 class ImageDecoder : public RefCounted<ImageDecoder> {
 public:
-    static NonnullRefPtr<ImageDecoder> create(const u8* data, size_t size) { return adopt_ref(*new ImageDecoder(data, size)); }
-    static NonnullRefPtr<ImageDecoder> create(const ByteBuffer& data) { return adopt_ref(*new ImageDecoder(data.data(), data.size())); }
+    static RefPtr<ImageDecoder> try_create(ReadonlyBytes);
     ~ImageDecoder();
 
-    bool is_valid() const { return m_plugin; }
-
-    IntSize size() const { return m_plugin ? m_plugin->size() : IntSize(); }
+    IntSize size() const { return m_plugin->size(); }
     int width() const { return size().width(); }
     int height() const { return size().height(); }
     RefPtr<Gfx::Bitmap> bitmap() const;
-    void set_volatile()
-    {
-        if (m_plugin)
-            m_plugin->set_volatile();
-    }
-    [[nodiscard]] bool set_nonvolatile(bool& was_purged) { return m_plugin ? m_plugin->set_nonvolatile(was_purged) : false; }
-    bool sniff() const { return m_plugin ? m_plugin->sniff() : false; }
-    bool is_animated() const { return m_plugin ? m_plugin->is_animated() : false; }
-    size_t loop_count() const { return m_plugin ? m_plugin->loop_count() : 0; }
-    size_t frame_count() const { return m_plugin ? m_plugin->frame_count() : 0; }
-    ImageFrameDescriptor frame(size_t i) const { return m_plugin ? m_plugin->frame(i) : ImageFrameDescriptor(); }
+    void set_volatile() { m_plugin->set_volatile(); }
+    [[nodiscard]] bool set_nonvolatile(bool& was_purged) { return m_plugin->set_nonvolatile(was_purged); }
+    bool sniff() const { return m_plugin->sniff(); }
+    bool is_animated() const { return m_plugin->is_animated(); }
+    size_t loop_count() const { return m_plugin->loop_count(); }
+    size_t frame_count() const { return m_plugin->frame_count(); }
+    ImageFrameDescriptor frame(size_t i) const { return m_plugin->frame(i); }
 
 private:
-    ImageDecoder(const u8*, size_t);
+    explicit ImageDecoder(NonnullOwnPtr<ImageDecoderPlugin>);
 
-    mutable OwnPtr<ImageDecoderPlugin> m_plugin;
+    NonnullOwnPtr<ImageDecoderPlugin> mutable m_plugin;
 };
 
 }

+ 8 - 2
Userland/Libraries/LibWeb/Loader/FrameLoader.cpp

@@ -68,7 +68,9 @@ static bool build_text_document(DOM::Document& document, const ByteBuffer& data)
 
 static bool build_image_document(DOM::Document& document, const ByteBuffer& data)
 {
-    auto image_decoder = Gfx::ImageDecoder::create(data.data(), data.size());
+    auto image_decoder = Gfx::ImageDecoder::try_create(data.bytes());
+    if (!image_decoder)
+        return false;
     auto bitmap = image_decoder->bitmap();
     if (!bitmap)
         return false;
@@ -164,7 +166,11 @@ bool FrameLoader::load(const LoadRequest& request, Type type)
             favicon_url,
             [this, favicon_url](auto data, auto&, auto) {
                 dbgln("Favicon downloaded, {} bytes from {}", data.size(), favicon_url);
-                auto decoder = Gfx::ImageDecoder::create(data.data(), data.size());
+                auto decoder = Gfx::ImageDecoder::try_create(data);
+                if (!decoder) {
+                    dbgln("No image decoder plugin for favicon {}", favicon_url);
+                    return;
+                }
                 auto bitmap = decoder->bitmap();
                 if (!bitmap) {
                     dbgln("Could not decode favicon {}", favicon_url);

+ 6 - 1
Userland/Services/ImageDecoder/ClientConnection.cpp

@@ -37,7 +37,12 @@ Messages::ImageDecoderServer::DecodeImageResponse ClientConnection::decode_image
         return nullptr;
     }
 
-    auto decoder = Gfx::ImageDecoder::create(encoded_buffer.data<u8>(), encoded_buffer.size());
+    auto decoder = Gfx::ImageDecoder::try_create(ReadonlyBytes { encoded_buffer.data<u8>(), encoded_buffer.size() });
+
+    if (!decoder) {
+        dbgln_if(IMAGE_DECODER_DEBUG, "Could not find suitable image decoder plugin for data");
+        return { false, 0, Vector<Gfx::ShareableBitmap> {}, Vector<u32> {} };
+    }
 
     if (!decoder->frame_count()) {
         dbgln_if(IMAGE_DECODER_DEBUG, "Could not decode image from encoded data");

+ 2 - 3
Userland/Utilities/file.cpp

@@ -28,9 +28,8 @@ static Optional<String> image_details(const String& description, const String& p
         return {};
 
     auto& mapped_file = *file_or_error.value();
-    auto image_decoder = Gfx::ImageDecoder::create((const u8*)mapped_file.data(), mapped_file.size());
-
-    if (!image_decoder->is_valid())
+    auto image_decoder = Gfx::ImageDecoder::try_create(mapped_file.bytes());
+    if (!image_decoder)
         return {};
 
     return String::formatted("{}, {} x {}", description, image_decoder->width(), image_decoder->height());