Browse Source

png reader is now a generic image reader to support more formats in the
future, image structure uses a cleaner to prevent leaks

Kajetan Johannes Hammerle 2 years ago
parent
commit
fa96b9a846

+ 2 - 2
Main.cpp

@@ -10,11 +10,11 @@
 #include "tests/ComponentsTests.h"
 #include "tests/FrustumTests.h"
 #include "tests/HashMapTests.h"
+#include "tests/ImageReaderTests.h"
 #include "tests/ListTests.h"
 #include "tests/MatrixStackTests.h"
 #include "tests/MatrixTests.h"
 #include "tests/NetworkTests.h"
-#include "tests/PNGReaderTests.h"
 #include "tests/PlaneTests.h"
 #include "tests/QuaternionTests.h"
 #include "tests/RandomTests.h"
@@ -52,7 +52,7 @@ int main(int argAmount, char** args) {
     UtilsTests::test();
     ColorTests::test();
     ClockTests::test();
-    PNGReaderTests::test(args[1]);
+    ImageReaderTests::test(args[1]);
     BufferTests::test();
     TypedBufferTests::test();
     UniquePointerTests::test();

+ 36 - 0
images/ImageReader.cpp

@@ -0,0 +1,36 @@
+#include <lodepng/lodepng.h>
+
+#include "images/ImageReader.h"
+#include "utils/Cleaner.h"
+
+static void cleanRawData(ColorChannel*& c) {
+    free(c);
+}
+
+static void cleanState(LodePNGState& state) {
+    lodepng_state_cleanup(&state);
+}
+
+Error ImageReader::load(Image& image, const char* path) {
+    Cleaner<ColorChannel*, cleanRawData> rawData(nullptr);
+    size_t rawSize;
+    unsigned int error = lodepng_load_file(&rawData, &rawSize, path);
+    if(error) {
+        Error e{"cannot load file '"};
+        e.message.append(path).append("': ").append(lodepng_error_text(error));
+        return e;
+    }
+    Cleaner<LodePNGState, cleanState> state;
+    lodepng_state_init(&state);
+    state->decoder.color_convert = false;
+    error = lodepng_decode(&image.data, &image.width, &image.height, &state,
+                           rawData, rawSize);
+    if(error) {
+        Error e{"cannot decode file '"};
+        e.message.append(path).append("'").append(lodepng_error_text(error));
+        return e;
+    }
+    image.channels = lodepng_get_channels(&state->info_png.color);
+    image.bitdepth = state->info_png.color.bitdepth;
+    return {};
+}

+ 25 - 0
images/ImageReader.h

@@ -0,0 +1,25 @@
+#ifndef IMAGEREADER_H
+#define IMAGEREADER_H
+
+#include "utils/Cleaner.h"
+#include "utils/Color.h"
+#include "utils/Error.h"
+
+namespace ImageReader {
+    class Image final {
+        static void clean(ColorChannel*& c) {
+            free(c);
+        }
+
+    public:
+        Cleaner<ColorChannel*, clean> data;
+        unsigned int width;
+        unsigned int height;
+        unsigned int channels;
+        unsigned int bitdepth;
+    };
+
+    Error load(Image& image, const char* path);
+}
+
+#endif

+ 0 - 80
images/PNGReader.cpp

@@ -1,80 +0,0 @@
-#include <lodepng/lodepng.h>
-
-#include "images/PNGReader.h"
-
-PNGReader::PNGReader()
-    : width(0), height(0), channels(0), rawData(nullptr), data(nullptr) {
-}
-
-PNGReader::~PNGReader() {
-    clean();
-}
-
-Error PNGReader::load(const char* path) {
-    clean();
-    Error error = loadIntern(path);
-    if(error.has()) {
-        clean();
-    }
-    return error;
-}
-
-Error PNGReader::loadIntern(const char* path) {
-    size_t rawSize;
-    unsigned int error = lodepng_load_file(&rawData, &rawSize, path);
-    if(error) {
-        Error e = {"cannot load file '"};
-        e.message.append(path).append("': ").append(lodepng_error_text(error));
-        return e;
-    }
-
-    LodePNGState state;
-    lodepng_state_init(&state);
-    state.decoder.color_convert = false;
-    error = lodepng_decode(&data, &width, &height, &state, rawData, rawSize);
-    if(error) {
-        Error e = {"cannot decode file '"};
-        e.message.append(path).append("'").append(lodepng_error_text(error));
-        return e;
-    }
-    channels = lodepng_get_channels(&state.info_png.color);
-
-    if(channels < 1 || channels > 4) {
-        Error error = {"'"};
-        error.message.append(path)
-            .append("' has unsupported number of channels: ")
-            .append(channels);
-        return error;
-    } else if(state.info_png.color.bitdepth != 8) {
-        Error error = {"bit depth of '"};
-        error.message.append(path).append("' is not 8");
-        return error;
-    }
-    return {};
-}
-
-void PNGReader::clean() {
-    free(rawData);
-    free(data);
-    rawData = nullptr;
-    data = nullptr;
-    width = 0;
-    height = 0;
-    channels = 0;
-}
-
-int PNGReader::getWidth() const {
-    return width;
-}
-
-int PNGReader::getHeight() const {
-    return height;
-}
-
-int PNGReader::getChannels() const {
-    return channels;
-}
-
-const ColorChannel* PNGReader::getData() const {
-    return data;
-}

+ 0 - 34
images/PNGReader.h

@@ -1,34 +0,0 @@
-#ifndef PNGREADER_H
-#define PNGREADER_H
-
-#include "utils/Color.h"
-#include "utils/Error.h"
-
-class PNGReader final {
-    unsigned int width;
-    unsigned int height;
-    unsigned int channels;
-    ColorChannel* rawData;
-    ColorChannel* data;
-
-public:
-    PNGReader();
-    ~PNGReader();
-    PNGReader(const PNGReader& other) = delete;
-    PNGReader(PNGReader&& other) = delete;
-    PNGReader& operator=(const PNGReader& other) = delete;
-    PNGReader& operator=(PNGReader&& other) = delete;
-
-    Error load(const char* path);
-
-    int getWidth() const;
-    int getHeight() const;
-    int getChannels() const;
-    const ColorChannel* getData() const;
-
-private:
-    Error loadIntern(const char* path);
-    void clean();
-};
-
-#endif

+ 2 - 2
meson.build

@@ -1,7 +1,7 @@
 project('gamingcore', 'cpp')
 
 src = [
-    'images/PNGReader.cpp',
+    'images/ImageReader.cpp',
     'input/Button.cpp',
     'input/Buttons.cpp',
     'input/TextInput.cpp',
@@ -47,7 +47,7 @@ src_tests = [
     'tests/MatrixStackTests.cpp',
     'tests/MatrixTests.cpp',
     'tests/NetworkTests.cpp',
-    'tests/PNGReaderTests.cpp',
+    'tests/ImageReaderTests.cpp',
     'tests/PlaneTests.cpp',
     'tests/QuaternionTests.cpp',
     'tests/RandomTests.cpp',

+ 16 - 5
rendering/FileTexture.cpp

@@ -1,14 +1,25 @@
 #include "rendering/FileTexture.h"
-#include "images/PNGReader.h"
+#include "images/ImageReader.h"
 
 Error FileTexture::load(const char* path, int maxMipMaps) {
-    PNGReader png;
-    Error error = png.load(path);
+    ImageReader::Image image;
+    Error error = ImageReader::load(image, path);
     if(error.has()) {
         return error;
     }
-    texture.init(TextureFormat::color8(png.getChannels()), maxMipMaps);
-    texture.setData(png.getWidth(), png.getHeight(), png.getData());
+    if(image.channels < 1 || image.channels > 4) {
+        Error error = {"'"};
+        error.message.append(path)
+            .append("' has unsupported number of channels: ")
+            .append(image.channels);
+        return error;
+    } else if(image.bitdepth != 8) {
+        Error error = {"bit depth of '"};
+        error.message.append(path).append("' is not 8");
+        return error;
+    }
+    texture.init(TextureFormat::color8(image.channels), maxMipMaps);
+    texture.setData(image.width, image.height, image.data);
     return {};
 }
 

+ 36 - 0
tests/ImageReaderTests.cpp

@@ -0,0 +1,36 @@
+#include "tests/ImageReaderTests.h"
+#include "images/ImageReader.h"
+#include "tests/Test.h"
+#include "utils/StringBuffer.h"
+
+typedef StringBuffer<200> String;
+
+static void testReadPNG(Test& test, const char* path, const char* name,
+                        unsigned int channels, unsigned int bitdepth) {
+    ImageReader::Image image;
+    Error error =
+        ImageReader::load(image, String(path).append(name).append(".png"));
+    if(error.has()) {
+        test.checkEqual(false, true,
+                        String("read ").append(name).append(" error"));
+        return;
+    }
+    test.checkEqual(32u, image.width, String(name).append(" width"));
+    test.checkEqual(64u, image.height, String(name).append(" height"));
+    test.checkEqual(channels, image.channels, String(name).append(" channels"));
+    test.checkEqual(bitdepth, image.bitdepth, String(name).append(" bitdepth"));
+    test.checkEqual(true, image.data != nullptr, String(name).append(" data"));
+}
+
+void ImageReaderTests::test(const char* path) {
+    Test test("ImageReader");
+    testReadPNG(test, path, "rgb8", 3, 8);
+    testReadPNG(test, path, "rgb16", 3, 16);
+    testReadPNG(test, path, "rgba8", 4, 8);
+    testReadPNG(test, path, "rgba16", 4, 16);
+    testReadPNG(test, path, "gray8", 1, 8);
+    testReadPNG(test, path, "gray16", 1, 16);
+    testReadPNG(test, path, "graya8", 2, 8);
+    testReadPNG(test, path, "graya16", 2, 16);
+    test.finalize();
+}

+ 8 - 0
tests/ImageReaderTests.h

@@ -0,0 +1,8 @@
+#ifndef IMAGEREADERTESTS_H
+#define IMAGEREADERTESTS_H
+
+namespace ImageReaderTests {
+    void test(const char* path);
+}
+
+#endif

+ 0 - 94
tests/PNGReaderTests.cpp

@@ -1,94 +0,0 @@
-#include "tests/PNGReaderTests.h"
-#include "images/PNGReader.h"
-#include "tests/Test.h"
-#include "utils/List.h"
-#include "utils/StringBuffer.h"
-
-static void testReadRGB8(Test& test, const char* path) {
-    PNGReader png;
-    if(png.load(StringBuffer<200>(path).append("rgb8.png")).has()) {
-        test.checkEqual(false, true, "read rgb8 error");
-        return;
-    }
-    test.checkEqual(32, png.getWidth(), "rgb8 width");
-    test.checkEqual(64, png.getHeight(), "rgb8 height");
-    test.checkEqual(3, png.getChannels(), "rgb8 channels");
-    test.checkEqual(true, png.getData() != nullptr, "rgb8 data");
-}
-
-static void testReadRGB16(Test& test, const char* path) {
-    PNGReader png;
-    test.checkEqual(
-        true, png.load((StringBuffer<200>(path).append("rgb16.png"))).has(),
-        "read rgb16 error");
-}
-
-static void testReadRGBA8(Test& test, const char* path) {
-    PNGReader png;
-    if(png.load(StringBuffer<200>(path).append("rgba8.png")).has()) {
-        test.checkEqual(false, true, "read rgba8 error");
-        return;
-    }
-    test.checkEqual(32, png.getWidth(), "rgba8 width");
-    test.checkEqual(64, png.getHeight(), "rgba8 height");
-    test.checkEqual(4, png.getChannels(), "rgba8 channels");
-    test.checkEqual(true, png.getData() != nullptr, "rgba8 data");
-}
-
-static void testReadRGBA16(Test& test, const char* path) {
-    PNGReader png;
-    test.checkEqual(
-        true, png.load(StringBuffer<200>(path).append("rgba16.png")).has(),
-        "read rgba16 error");
-}
-
-static void testReadGray8(Test& test, const char* path) {
-    PNGReader png;
-    if(png.load(StringBuffer<200>(path).append("gray8.png")).has()) {
-        test.checkEqual(false, true, "read gray8 error");
-        return;
-    }
-    test.checkEqual(32, png.getWidth(), "gray8 width");
-    test.checkEqual(64, png.getHeight(), "gray8 height");
-    test.checkEqual(1, png.getChannels(), "gray8 channels");
-    test.checkEqual(true, png.getData() != nullptr, "gray8 data");
-}
-
-static void testReadGray16(Test& test, const char* path) {
-    PNGReader png;
-    test.checkEqual(
-        true, png.load(StringBuffer<200>(path).append("gray16.png")).has(),
-        "read gray16 error");
-}
-
-static void testReadGrayA8(Test& test, const char* path) {
-    PNGReader png;
-    if(png.load(StringBuffer<200>(path).append("graya8.png")).has()) {
-        test.checkEqual(false, true, "read graya8 error");
-        return;
-    }
-    test.checkEqual(32, png.getWidth(), "graya8 width");
-    test.checkEqual(64, png.getHeight(), "graya8 height");
-    test.checkEqual(2, png.getChannels(), "graya8 channels");
-    test.checkEqual(true, png.getData() != nullptr, "graya8 data");
-}
-
-static void testReadGrayA16(Test& test, const char* path) {
-    PNGReader png;
-    test.checkEqual(
-        true, png.load(StringBuffer<200>(path).append("graya16.png")).has(),
-        "read graya16 error");
-}
-
-void PNGReaderTests::test(const char* path) {
-    Test test("PNGReader");
-    testReadRGB8(test, path);
-    testReadRGB16(test, path);
-    testReadRGBA8(test, path);
-    testReadRGBA16(test, path);
-    testReadGray8(test, path);
-    testReadGray16(test, path);
-    testReadGrayA8(test, path);
-    testReadGrayA16(test, path);
-    test.finalize();
-}

+ 0 - 8
tests/PNGReaderTests.h

@@ -1,8 +0,0 @@
-#ifndef PNGREADERTESTS_H
-#define PNGREADERTESTS_H
-
-namespace PNGReaderTests {
-    void test(const char* path);
-}
-
-#endif

+ 41 - 0
utils/Cleaner.h

@@ -0,0 +1,41 @@
+#ifndef CLEANER_H
+#define CLEANER_H
+
+template<typename T, void (*F)(T&)>
+class Cleaner final {
+    T wrapped;
+
+public:
+    Cleaner() {
+    }
+
+    Cleaner(const T& t) : wrapped(t) {
+    }
+
+    ~Cleaner() {
+        F(wrapped);
+    }
+
+    Cleaner(const Cleaner&) = delete;
+    Cleaner(Cleaner&&) = delete;
+    Cleaner& operator=(const Cleaner&) = delete;
+    Cleaner& operator=(Cleaner&&) = delete;
+
+    T* operator->() {
+        return &wrapped;
+    }
+
+    T* operator&() {
+        return &wrapped;
+    }
+
+    operator const T() const {
+        return wrapped;
+    }
+
+    operator T() {
+        return wrapped;
+    }
+};
+
+#endif