Browse Source

libpng replaced by lodepng

Kajetan Johannes Hammerle 2 years ago
parent
commit
7aae9c7c66
8 changed files with 77 additions and 168 deletions
  1. 3 0
      .gitmodules
  2. 40 86
      images/PNGReader.cpp
  3. 9 14
      images/PNGReader.h
  4. 1 0
      lodepng
  5. 4 6
      meson.build
  6. 1 8
      rendering/FileTexture.cpp
  7. 16 53
      tests/PNGReaderTests.cpp
  8. 3 1
      utils/Color.h

+ 3 - 0
.gitmodules

@@ -1,3 +1,6 @@
 [submodule "enet"]
 	path = enet
 	url = https://github.com/zpl-c/enet.git
+[submodule "lodepng"]
+	path = lodepng
+	url = https://github.com/lvandeve/lodepng.git

+ 40 - 86
images/PNGReader.cpp

@@ -1,74 +1,42 @@
+#include <lodepng/lodepng.h>
+
 #include "images/PNGReader.h"
 
 PNGReader::PNGReader()
-    : path(""), width(0), height(0), channels(0), bitDepth(0), rowBytes(0),
-      file(nullptr), read(nullptr), info(nullptr), rowPointers(nullptr) {
+    : width(0), height(0), channels(0), rawData(nullptr), data(nullptr) {
 }
 
 PNGReader::~PNGReader() {
-    if(file != nullptr) {
-        fclose(file);
-    }
-    if(rowPointers != nullptr) {
-        png_free(read, rowPointers);
-    }
-    png_destroy_read_struct(&read, &info, nullptr);
+    clean();
 }
 
 Error PNGReader::load(const char* path) {
-    if(file != nullptr) {
-        Error error = {"file '"};
-        error.message.append(path).append("' is alreaded loaded");
-        return error;
-    }
-    PNGReader::path = path;
-    file = fopen(path, "rb");
-    if(file == nullptr) {
-        Error error = {"file '"};
-        error.message.append(path)
-            .append("' cannot be read: ")
-            .append(static_cast<const char*>(strerror(errno)));
-        return error;
-    }
-    png_byte buffer[8];
-    if(fread(buffer, sizeof(png_byte), 8, file) != 8) {
-        Error error = {"cannot read signature of file '"};
-        error.message.append(path).append("'");
-        return error;
-    }
-    if(png_sig_cmp(buffer, 0, 8)) {
-        Error error = {"file '"};
-        error.message.append(path).append("' is not a png");
-        return error;
-    }
-    read = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr,
-                                  nullptr);
-    if(read == nullptr) {
-        return {"cannot create png read data structure"};
-    }
-    info = png_create_info_struct(read);
-    if(info == nullptr) {
-        return {"cannot create png info structure"};
-    }
-    if(setjmp(png_jmpbuf(read))) {
-        Error error = {"png file '"};
-        error.message.append(path).append("' has used error callback");
-        return error;
+    clean();
+    Error error = loadIntern(path);
+    if(error.has()) {
+        clean();
     }
-    png_init_io(read, file);
-    png_set_sig_bytes(read, 8);
-    png_read_info(read, info);
+    return error;
+}
 
-    png_set_expand(read);
+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;
+    }
 
-    width = png_get_image_width(read, info);
-    height = png_get_image_height(read, info);
-    channels = png_get_channels(read, info);
-    bitDepth = png_get_bit_depth(read, info);
-    if(png_get_bit_depth(read, info) == 16) {
-        png_set_strip_16(read);
+    LodePNGState state;
+    lodepng_state_init(&state);
+    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;
     }
-    rowBytes = png_get_rowbytes(read, info);
+    channels = lodepng_get_channels(&state.info_png.color);
 
     if(channels < 1 || channels > 4) {
         Error error = {"'"};
@@ -76,22 +44,24 @@ Error PNGReader::load(const char* path) {
             .append("' has unsupported number of channels: ")
             .append(channels);
         return error;
-    } else if(width < 1 || width > 2048 || height < 1 || height > 2048) {
-        Error error = {"width and height of '"};
-        error.message.append(path).append("' are too big");
-        return error;
-    } else if(bitDepth != 8 && bitDepth != 16) {
+    } else if(state.info_png.color.bitdepth != 8) {
         Error error = {"bit depth of '"};
-        error.message.append(path).append("' is neither 8 or 16");
-        return error;
-    } else if(getBufferSize() != (rowBytes * height * 8 / bitDepth)) {
-        Error error = {"'"};
-        error.message.append(path).append("' needs an unexpected buffer size");
+        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;
 }
@@ -104,22 +74,6 @@ int PNGReader::getChannels() const {
     return channels;
 }
 
-int PNGReader::getBufferSize() const {
-    return width * height * channels;
-}
-
-Error PNGReader::readData(ColorChannel* buffer) {
-    if(setjmp(png_jmpbuf(read))) {
-        Error error = {"png file '"};
-        error.message.append(path).append("' has used error callback");
-        return error;
-    }
-    rowPointers = static_cast<ColorChannel**>(
-        png_malloc(read, height * sizeof(ColorChannel*)));
-    for(int i = 0; i < height; i++) {
-        rowPointers[i] = (buffer + i * width * channels);
-    }
-    png_set_rows(read, info, reinterpret_cast<png_bytepp>(rowPointers));
-    png_read_image(read, reinterpret_cast<png_bytepp>(rowPointers));
-    return {};
+const ColorChannel* PNGReader::getData() const {
+    return data;
 }

+ 9 - 14
images/PNGReader.h

@@ -1,22 +1,15 @@
 #ifndef PNGREADER_H
 #define PNGREADER_H
 
-#include <png.h>
-
 #include "utils/Color.h"
 #include "utils/Error.h"
 
 class PNGReader final {
-    const char* path;
-    int width;
-    int height;
-    int channels;
-    int bitDepth;
-    int rowBytes;
-    FILE* file;
-    png_structp read;
-    png_infop info;
-    ColorChannel** rowPointers;
+    unsigned int width;
+    unsigned int height;
+    unsigned int channels;
+    ColorChannel* rawData;
+    ColorChannel* data;
 
 public:
     PNGReader();
@@ -31,9 +24,11 @@ public:
     int getWidth() const;
     int getHeight() const;
     int getChannels() const;
-    int getBufferSize() const;
+    const ColorChannel* getData() const;
 
-    Error readData(ColorChannel* buffer);
+private:
+    Error loadIntern(const char* path);
+    void clean();
 };
 
 #endif

+ 1 - 0
lodepng

@@ -0,0 +1 @@
+Subproject commit 7fdcc96a5e5864eee72911c3ca79b1d9f0d12292

+ 4 - 6
meson.build

@@ -30,6 +30,7 @@ src = [
     'utils/Random.cpp',
     'utils/Size.cpp',
     'wrapper/GL.cpp',
+    'lodepng/lodepng.cpp',
 ]
 
 src_tests = [
@@ -66,8 +67,6 @@ args = compiler.get_supported_arguments(['-Wall', '-Wextra', '-pedantic', '-Werr
 
 thread_dep = dependency('threads')
 glew_dep = dependency('glew')
-zlib_dep = dependency('zlib')
-png_dep = dependency('libpng')
 glfw_dep = dependency('glfw3')
 gl_dep = dependency('GL')
 
@@ -77,13 +76,12 @@ glu_dep = compiler.find_library('glu32', required: false)
 
 libgamingcore_include = include_directories('.', 
     glew_dep.get_pkgconfig_variable('includedir'),
-    glfw_dep.get_pkgconfig_variable('includedir'),
-    png_dep.get_pkgconfig_variable('includedir'))
+    glfw_dep.get_pkgconfig_variable('includedir'))
 
 libgamingcore = static_library('gamingcore', 
     sources: src,
     include_directories : libgamingcore_include,
-    dependencies : [thread_dep, glew_dep, glfw_dep, png_dep, zlib_dep, gl_dep, ws2_32_dep, winmm_dep, glu_dep],
+    dependencies : [thread_dep, glew_dep, glfw_dep, gl_dep, ws2_32_dep, winmm_dep, glu_dep],
     cpp_args: args)
 libgamingcore_dep = declare_dependency(
     include_directories: libgamingcore_include, 
@@ -92,4 +90,4 @@ libgamingcore_dep = declare_dependency(
 executable('tests', 
     sources: src_tests,
     dependencies : libgamingcore_dep,
-    cpp_args: args + ['-DLOG_LEVEL=4'])
+    cpp_args: args + ['-DLOG_LEVEL=4'])

+ 1 - 8
rendering/FileTexture.cpp

@@ -1,6 +1,5 @@
 #include "rendering/FileTexture.h"
 #include "images/PNGReader.h"
-#include "utils/List.h"
 
 Error FileTexture::load(const char* path, int maxMipMaps) {
     PNGReader png;
@@ -8,14 +7,8 @@ Error FileTexture::load(const char* path, int maxMipMaps) {
     if(error.has()) {
         return error;
     }
-    List<ColorChannel> buffer;
-    buffer.resize(png.getBufferSize());
-    error = png.readData(buffer.begin());
-    if(error.has()) {
-        return error;
-    }
     texture.init(TextureFormat::color8(png.getChannels()), maxMipMaps);
-    texture.setData(png.getWidth(), png.getHeight(), buffer.begin());
+    texture.setData(png.getWidth(), png.getHeight(), png.getData());
     return {};
 }
 

+ 16 - 53
tests/PNGReaderTests.cpp

@@ -4,16 +4,6 @@
 #include "utils/List.h"
 #include "utils/StringBuffer.h"
 
-static void testRead(Test& test, PNGReader& png, const char* text) {
-    List<ColorChannel> buffer;
-    buffer.resize(png.getBufferSize());
-    test.checkEqual(true, png.getBufferSize() > 0, text);
-    test.checkEqual(png.getBufferSize(), buffer.getLength(), text);
-    if(png.getBufferSize() == buffer.getLength()) {
-        test.checkEqual(false, png.readData(buffer.begin()).has(), text);
-    }
-}
-
 static void testReadRGB8(Test& test, const char* path) {
     PNGReader png;
     if(png.load(StringBuffer<200>(path).append("rgb8.png")).has()) {
@@ -23,20 +13,14 @@ static void testReadRGB8(Test& test, const char* path) {
     test.checkEqual(32, png.getWidth(), "rgb8 width");
     test.checkEqual(64, png.getHeight(), "rgb8 height");
     test.checkEqual(3, png.getChannels(), "rgb8 channels");
-    testRead(test, png, "rgb8 read");
+    test.checkEqual(true, png.getData() != nullptr, "rgb8 data");
 }
 
 static void testReadRGB16(Test& test, const char* path) {
     PNGReader png;
-    if(png.load((StringBuffer<200>(path).append("rgb16.png"))).has()) {
-        test.checkEqual(false, true, "read rgb16 error");
-        return;
-    }
-    test.checkEqual(32, png.getWidth(), "rgb16 width");
-    test.checkEqual(64, png.getHeight(), "rgb16 height");
-    test.checkEqual(3, png.getChannels(), "rgb16 channels");
-    test.checkEqual(32 * 64 * 3, png.getBufferSize(), "rgb16 channels");
-    testRead(test, png, "rgb16 read");
+    test.checkEqual(
+        true, png.load((StringBuffer<200>(path).append("rgb16.png"))).has(),
+        "read rgb16 error");
 }
 
 static void testReadRGBA8(Test& test, const char* path) {
@@ -48,21 +32,14 @@ static void testReadRGBA8(Test& test, const char* path) {
     test.checkEqual(32, png.getWidth(), "rgba8 width");
     test.checkEqual(64, png.getHeight(), "rgba8 height");
     test.checkEqual(4, png.getChannels(), "rgba8 channels");
-    test.checkEqual(32 * 64 * 4, png.getBufferSize(), "rgba8 channels");
-    testRead(test, png, "rgba8 read");
+    test.checkEqual(true, png.getData() != nullptr, "rgba8 data");
 }
 
 static void testReadRGBA16(Test& test, const char* path) {
     PNGReader png;
-    if(png.load(StringBuffer<200>(path).append("rgba16.png")).has()) {
-        test.checkEqual(false, true, "read rgba16 error");
-        return;
-    }
-    test.checkEqual(32, png.getWidth(), "rgba16 width");
-    test.checkEqual(64, png.getHeight(), "rgba16 height");
-    test.checkEqual(4, png.getChannels(), "rgba16 channels");
-    test.checkEqual(32 * 64 * 4, png.getBufferSize(), "rgba16 channels");
-    testRead(test, png, "rgba16 read");
+    test.checkEqual(
+        true, png.load(StringBuffer<200>(path).append("rgba16.png")).has(),
+        "read rgba16 error");
 }
 
 static void testReadGray8(Test& test, const char* path) {
@@ -74,21 +51,14 @@ static void testReadGray8(Test& test, const char* path) {
     test.checkEqual(32, png.getWidth(), "gray8 width");
     test.checkEqual(64, png.getHeight(), "gray8 height");
     test.checkEqual(1, png.getChannels(), "gray8 channels");
-    test.checkEqual(32 * 64, png.getBufferSize(), "gray8 channels");
-    testRead(test, png, "gray8 read");
+    test.checkEqual(true, png.getData() != nullptr, "gray8 data");
 }
 
 static void testReadGray16(Test& test, const char* path) {
     PNGReader png;
-    if(png.load(StringBuffer<200>(path).append("gray16.png")).has()) {
-        test.checkEqual(false, true, "read gray16 error");
-        return;
-    }
-    test.checkEqual(32, png.getWidth(), "gray16 width");
-    test.checkEqual(64, png.getHeight(), "gray16 height");
-    test.checkEqual(1, png.getChannels(), "gray16 channels");
-    test.checkEqual(32 * 64, png.getBufferSize(), "gray16 channels");
-    testRead(test, png, "gray16 read");
+    test.checkEqual(
+        true, png.load(StringBuffer<200>(path).append("gray16.png")).has(),
+        "read gray16 error");
 }
 
 static void testReadGrayA8(Test& test, const char* path) {
@@ -100,21 +70,14 @@ static void testReadGrayA8(Test& test, const char* path) {
     test.checkEqual(32, png.getWidth(), "graya8 width");
     test.checkEqual(64, png.getHeight(), "graya8 height");
     test.checkEqual(2, png.getChannels(), "graya8 channels");
-    test.checkEqual(32 * 64 * 2, png.getBufferSize(), "graya8 channels");
-    testRead(test, png, "graya8 read");
+    test.checkEqual(true, png.getData() != nullptr, "graya8 data");
 }
 
 static void testReadGrayA16(Test& test, const char* path) {
     PNGReader png;
-    if(png.load(StringBuffer<200>(path).append("graya16.png")).has()) {
-        test.checkEqual(false, true, "read graya16 error");
-        return;
-    }
-    test.checkEqual(32, png.getWidth(), "graya16 width");
-    test.checkEqual(64, png.getHeight(), "graya16 height");
-    test.checkEqual(2, png.getChannels(), "graya16 channels");
-    test.checkEqual(32 * 64 * 2, png.getBufferSize(), "graya16 channels");
-    testRead(test, png, "graya16 read");
+    test.checkEqual(
+        true, png.load(StringBuffer<200>(path).append("graya16.png")).has(),
+        "read graya16 error");
 }
 
 void PNGReaderTests::test(const char* path) {

+ 3 - 1
utils/Color.h

@@ -16,7 +16,9 @@ public:
     Color(ColorChannel a, Args&&... args) {
         const int size = sizeof...(args) + 1;
         int init[size] = {a, args...};
-        static_assert(N == size, "color size and amount of channel arguments do not match");
+        static_assert(
+            N == size,
+            "color size and amount of channel arguments do not match");
         for(int i = 0; i < N; i++) {
             data[i] = init[i];
         }