Pārlūkot izejas kodu

PNGLoader: Support for color type 2 (RGB triplets) and multiple IDAT chunks.

Andreas Kling 6 gadi atpakaļ
vecāks
revīzija
65348e7dc1

BIN
Base/res/wallpapers/sunset-retro.png


+ 80 - 51
SharedGraphics/PNGLoader.cpp

@@ -33,8 +33,14 @@ struct PNGLoadingContext {
     byte compression_method { 0 };
     byte compression_method { 0 };
     byte filter_method { 0 };
     byte filter_method { 0 };
     byte interlace_method { 0 };
     byte interlace_method { 0 };
+    byte bytes_per_pixel { 0 };
+    bool has_seen_zlib_header { false };
+    bool has_alpha() const { return color_type & 4; }
     Vector<Scanline> scanlines;
     Vector<Scanline> scanlines;
     RetainPtr<GraphicsBitmap> bitmap;
     RetainPtr<GraphicsBitmap> bitmap;
+    byte* decompression_buffer { nullptr };
+    int decompression_buffer_size { 0 };
+    Vector<byte> compressed_data;
 };
 };
 
 
 class Streamer {
 class Streamer {
@@ -158,6 +164,30 @@ static RetainPtr<GraphicsBitmap> load_png_impl(const byte* data, int data_size)
         }
         }
     }
     }
 
 
+    unsigned long srclen = context.compressed_data.size() - 6;
+    unsigned long destlen = context.decompression_buffer_size;
+    int ret = puff(context.decompression_buffer, &destlen, context.compressed_data.data() + 2, &srclen);
+    if (ret < 0)
+        return nullptr;
+    context.compressed_data.clear();
+
+    {
+    Streamer streamer(context.decompression_buffer, context.decompression_buffer_size);
+    for (int y = 0; y < context.height; ++y) {
+        byte filter;
+        if (!streamer.read(filter))
+            return nullptr;
+
+        context.scanlines.append({ filter, ByteBuffer::create_uninitialized(context.width * context.bytes_per_pixel) });
+        auto& scanline_buffer = context.scanlines.last().data;
+        if (!streamer.read_bytes(scanline_buffer.pointer(), scanline_buffer.size()))
+            return nullptr;
+    }
+    munmap(context.decompression_buffer, context.decompression_buffer_size);
+    context.decompression_buffer = nullptr;
+    context.decompression_buffer_size = 0;
+    }
+
     context.bitmap = GraphicsBitmap::create(GraphicsBitmap::Format::RGBA32, { context.width, context.height });
     context.bitmap = GraphicsBitmap::create(GraphicsBitmap::Format::RGBA32, { context.width, context.height });
 
 
     union [[gnu::packed]] Pixel {
     union [[gnu::packed]] Pixel {
@@ -171,11 +201,28 @@ static RetainPtr<GraphicsBitmap> load_png_impl(const byte* data, int data_size)
     };
     };
     static_assert(sizeof(Pixel) == 4);
     static_assert(sizeof(Pixel) == 4);
 
 
-    printf("unfiltering %d scanlines\n", context.height);
     for (int y = 0; y < context.height; ++y) {
     for (int y = 0; y < context.height; ++y) {
         auto filter = context.scanlines[y].filter;
         auto filter = context.scanlines[y].filter;
-        printf("[%d] filter=%u\n", y, context.scanlines[y].filter);
-        memcpy(context.bitmap->scanline(y), context.scanlines[y].data.pointer(), context.scanlines[y].data.size());
+        switch (context.color_type) {
+        case 2: {
+            struct [[gnu::packed]] Triplet { byte r; byte g; byte b; };
+            auto* triplets = (Triplet*)context.scanlines[y].data.pointer();
+            for (int i = 0; i < context.width; ++i) {
+                auto& pixel = (Pixel&)context.bitmap->scanline(y)[i];
+                pixel.r = triplets[i].r;
+                pixel.g = triplets[i].g;
+                pixel.b = triplets[i].b;
+                pixel.a = 0xff;
+            }
+            break;
+        }
+        case 6:
+            memcpy(context.bitmap->scanline(y), context.scanlines[y].data.pointer(), context.scanlines[y].data.size());
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+            break;
+        }
         if (filter == 0)
         if (filter == 0)
             continue;
             continue;
         for (int i = 0; i < context.width; ++i) {
         for (int i = 0; i < context.width; ++i) {
@@ -192,28 +239,26 @@ static RetainPtr<GraphicsBitmap> load_png_impl(const byte* data, int data_size)
                 x.r += a.r;
                 x.r += a.r;
                 x.g += a.g;
                 x.g += a.g;
                 x.b += a.b;
                 x.b += a.b;
-                x.a += a.a;
-            }
-
-            if (filter == 2) {
+                if (context.has_alpha())
+                    x.a += a.a;
+            } else if (filter == 2) {
                 x.r += b.r;
                 x.r += b.r;
                 x.g += b.g;
                 x.g += b.g;
                 x.b += b.b;
                 x.b += b.b;
-                x.a += b.a;
-            }
-
-            if (filter == 3) {
+                if (context.has_alpha())
+                    x.a += b.a;
+            } if (filter == 3) {
                 x.r = x.r + ((a.r + b.r) / 2);
                 x.r = x.r + ((a.r + b.r) / 2);
                 x.g = x.g + ((a.g + b.g) / 2);
                 x.g = x.g + ((a.g + b.g) / 2);
                 x.b = x.b + ((a.b + b.b) / 2);
                 x.b = x.b + ((a.b + b.b) / 2);
-                x.a = x.a + ((a.a + b.a) / 2);
-            }
-
-            if (filter == 4) {
+                if (context.has_alpha())
+                    x.a = x.a + ((a.a + b.a) / 2);
+            } if (filter == 4) {
                 x.r += paeth_predictor(a.r, b.r, c.r);
                 x.r += paeth_predictor(a.r, b.r, c.r);
                 x.g += paeth_predictor(a.g, b.g, c.g);
                 x.g += paeth_predictor(a.g, b.g, c.g);
                 x.b += paeth_predictor(a.b, b.b, c.b);
                 x.b += paeth_predictor(a.b, b.b, c.b);
-                x.a += paeth_predictor(a.a, b.a, c.a);
+                if (context.has_alpha())
+                    x.a += paeth_predictor(a.a, b.a, c.a);
             }
             }
         }
         }
     }
     }
@@ -233,46 +278,30 @@ static bool process_IHDR(const ByteBuffer& data, PNGLoadingContext& context)
     context.compression_method = ihdr.compression_method;
     context.compression_method = ihdr.compression_method;
     context.filter_method = ihdr.filter_method;
     context.filter_method = ihdr.filter_method;
     context.interlace_method = ihdr.interlace_method;
     context.interlace_method = ihdr.interlace_method;
+
+    switch (context.color_type) {
+    case 2:
+        context.bytes_per_pixel = 3;
+        break;
+    case 6:
+        context.bytes_per_pixel = 4;
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+    }
+
+    printf("PNG: %dx%d (%d bpp)\n", context.width, context.height, context.bit_depth);
+    printf("     Color type: %b\n", context.color_type);
+    printf(" Interlace type: %b\n", context.interlace_method);
+
+    context.decompression_buffer_size = (context.width * context.height * context.bytes_per_pixel + context.height);
+    context.decompression_buffer = (byte*)mmap(nullptr, context.decompression_buffer_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
     return true;
     return true;
 }
 }
 
 
 static bool process_IDAT(const ByteBuffer& data, PNGLoadingContext& context)
 static bool process_IDAT(const ByteBuffer& data, PNGLoadingContext& context)
 {
 {
-    for (int i = 0; i < data.size(); ++i) {
-        printf("%c", data[i]);
-    }
-    printf("\n");
-
-    printf("first byte into puff: %c (%b)\n", data[0], data[0]);
-
-    struct [[gnu::packed]] ZlibHeader {
-        byte compression_method : 4;
-        byte compression_info : 4;
-        byte flags;
-    };
-    static_assert(sizeof(ZlibHeader) == 2);
-
-    auto& zhdr = *(ZlibHeader*)data.pointer();
-    printf("compression_method: %u\n", zhdr.compression_method);
-    printf("compression_info: %u\n", zhdr.compression_info);
-    printf("flags: %b\n", zhdr.flags);
-
-    unsigned long destlen = (context.width * context.height * sizeof(RGBA32)) + context.height;
-    unsigned long srclen = data.size() - 2;
-    auto decompression_buffer = ByteBuffer::create_uninitialized(destlen + context.height);
-    int ret = puff(decompression_buffer.pointer(), &destlen, data.pointer() + 2, &srclen);
-    if (ret != 0)
-        return false;
-    Streamer streamer(decompression_buffer.pointer(), decompression_buffer.size());
-    for (int y = 0; y < context.height; ++y) {
-        byte filter;
-        if (!streamer.read(filter))
-            return false;
-        context.scanlines.append({ filter, ByteBuffer::create_uninitialized(context.width * sizeof(RGBA32)) });
-        auto& scanline_buffer = context.scanlines.last().data;
-        if (!streamer.read_bytes(scanline_buffer.pointer(), scanline_buffer.size()))
-            return false;
-    }
+    context.compressed_data.append(data.pointer(), data.size());
     return true;
     return true;
 }
 }
 
 

+ 1 - 1
SharedGraphics/Size.h

@@ -48,7 +48,7 @@ public:
 
 
     operator WSAPI_Size() const;
     operator WSAPI_Size() const;
 
 
-    String to_string() const { return String::format("[%d,%d]", m_width, m_height); }
+    String to_string() const { return String::format("[%dx%d]", m_width, m_height); }
 
 
 private:
 private:
     int m_width { 0 };
     int m_width { 0 };

+ 2 - 2
Userland/qs.cpp

@@ -15,7 +15,7 @@ int main(int argc, char** argv)
     }
     }
 #endif
 #endif
 
 
-    const char* path = "/res/icons/folder32.png";
+    const char* path = "/res/wallpapers/sunset-retro.png";
     if (argc > 1)
     if (argc > 1)
         path = argv[1];
         path = argv[1];
 
 
@@ -26,7 +26,7 @@ int main(int argc, char** argv)
     }
     }
 
 
     auto* window = new GWindow;
     auto* window = new GWindow;
-    window->set_title(String::format("qs: %s", path));
+    window->set_title(String::format("QuickShow: %s %s", path, bitmap->size().to_string().characters()));
     window->set_rect(200, 200, bitmap->width(), bitmap->height());
     window->set_rect(200, 200, bitmap->width(), bitmap->height());
 
 
     auto* widget = new GWidget;
     auto* widget = new GWidget;