diff --git a/clang-format-all b/clang-format-all deleted file mode 100755 index f2744b0..0000000 --- a/clang-format-all +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash -# -# clang-format-all: a tool to run clang-format on an entire project -# Copyright (C) 2016 Evan Klitzke -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -function usage { - echo "Usage: $0 DIR..." - exit 1 -} - -if [ $# -eq 0 ]; then - usage -fi - -# Variable that will hold the name of the clang-format command -FMT="" - -# Some distros just call it clang-format. Others (e.g. Ubuntu) are insistent -# that the version number be part of the command. We prefer clang-format if -# that's present, otherwise we work backwards from highest version to lowest -# version. -for clangfmt in clang-format{,-{4,3}.{9,8,7,6,5,4,3,2,1,0}}; do - if which "$clangfmt" &>/dev/null; then - FMT="$clangfmt" - break - fi -done - -# Check if we found a working clang-format -if [ -z "$FMT" ]; then - echo "failed to find clang-format" - exit 1 -fi - -# Check all of the arguments first to make sure they're all directories -for dir in "$@"; do - if [ ! -d "${dir}" ]; then - echo "${dir} is not a directory" - usage - fi -done - -# Find a dominating file, starting from a given directory and going up. -find-dominating-file() { - if [ -r "$1"/"$2" ]; then - return 0 - fi - if [ "$1" = "/" ]; then - return 1 - fi - find-dominating-file "$(realpath "$1"/..)" "$2" - return $? -} - -# Run clang-format -i on all of the things -for dir in "$@"; do - pushd "${dir}" &>/dev/null - if ! find-dominating-file . .clang-format; then - echo "Failed to find dominating .clang-format starting at $PWD" - continue - fi - find . \ - \( -name '*.c' \ - -o -name '*.cc' \ - -o -name '*.cpp' \ - -o -name '*.h' \ - -o -name '*.hh' \ - -o -name '*.hpp' \) \ - -exec "${FMT}" -i '{}' \; - popd &>/dev/null -done \ No newline at end of file diff --git a/include/glez/color.hpp b/include/glez/color.hpp index 9ba7e0b..3e81dc4 100644 --- a/include/glez/color.hpp +++ b/include/glez/color.hpp @@ -10,11 +10,15 @@ namespace glez { struct rgba { rgba() = default; inline constexpr rgba(int r, int g, int b) - : r(r / 255.0f), g(g / 255.0f), b(b / 255.0f), a(1.0f) - {} + : r(r / 255.0f) + , g(g / 255.0f) + , b(b / 255.0f) + , a(1.0f) { } inline constexpr rgba(int r, int g, int b, int a) - : r(r / 255.0f), g(g / 255.0f), b(b / 255.0f), a(a / 255.0f) - {} + : r(r / 255.0f) + , g(g / 255.0f) + , b(b / 255.0f) + , a(a / 255.0f) { } float r; float g; diff --git a/include/glez/detail/record.hpp b/include/glez/detail/record.hpp index c78fd3b..140211f 100644 --- a/include/glez/detail/record.hpp +++ b/include/glez/detail/record.hpp @@ -4,47 +4,45 @@ #pragma once -#include #include -#include +#include #include #include +#include +#include #include -#include -#include namespace glez::detail::record { class RecordedCommands { public: - struct segment - { - std::size_t start{ 0 }; - std::size_t size{ 0 }; - glez::texture *texture{ nullptr }; - glez::font *font{ nullptr }; + struct segment { + std::size_t start { 0 }; + std::size_t size { 0 }; + glez::texture* texture { nullptr }; + glez::font* font { nullptr }; }; RecordedCommands(); ~RecordedCommands(); void reset(); - void store(glez::vertex *vertices, size_t vcount, - uint32_t *indices, size_t icount); - void bindTexture(glez::texture *tx); - void bindFont(glez::font *font); + void store(glez::vertex* vertices, size_t vcount, + uint32_t* indices, size_t icount); + void bindTexture(glez::texture* tx); + void bindFont(glez::font* font); void render(); void end(); protected: void cutSegment(); - ftgl::vertex_buffer_t *vertex_buffer{}; - std::vector segments{}; - segment current{}; + ftgl::vertex_buffer_t* vertex_buffer {}; + std::vector segments {}; + segment current {}; }; -extern RecordedCommands *currentRecord; +extern RecordedCommands* currentRecord; extern bool isReplaying; } // namespace glez::detail::record diff --git a/include/glez/draw.hpp b/include/glez/draw.hpp index 1d2c9b1..716586c 100644 --- a/include/glez/draw.hpp +++ b/include/glez/draw.hpp @@ -5,10 +5,10 @@ #pragma once -#include #include "color.hpp" #include "font.hpp" #include "texture.hpp" +#include namespace glez::draw { @@ -17,12 +17,12 @@ void rect(float x, float y, float w, float h, rgba color); void triangle(float x, float y, float x2, float y2, float x3, float y3, rgba color); void rect_outline(float x, float y, float w, float h, rgba color, float thickness); void rect_textured(float x, float y, float w, float h, rgba color, - texture &texture, float tx, float ty, float tw, float th, - float angle); + texture& texture, float tx, float ty, float tw, float th, + float angle); void circle(float x, float y, float radius, rgba color, float thickness, int steps); -void string(float x, float y, const std::string &string, font &font, rgba color, - float *width, float *height); -void outlined_string(float x, float y, const std::string &string, font &font, - rgba color, rgba outline, float *width, float *height); +void string(float x, float y, const std::string& string, font& font, rgba color, + float* width, float* height); +void outlined_string(float x, float y, const std::string& string, font& font, + rgba color, rgba outline, float* width, float* height); } // namespace glez::draw diff --git a/include/glez/font.hpp b/include/glez/font.hpp index 6c6b754..33bc4ae 100644 --- a/include/glez/font.hpp +++ b/include/glez/font.hpp @@ -13,19 +13,20 @@ namespace glez { class font { public: - font(){} - static font loadFromFile(const std::string &path, float size); + font() { } + static font loadFromFile(const std::string& path, float size); font(font&&); ~font(); glez::font& operator=(glez::font&&); - //void stringSize(std::string_view string, float* width, float* height); + // void stringSize(std::string_view string, float* width, float* height); void stringSize(const std::string& string, float* width, float* height); - inline bool isLoaded() { return this->m_font != nullptr && this->atlas != nullptr; }; + inline bool isLoaded() { return this->m_font != nullptr && this->atlas != nullptr; }; + public: texture_font_t* m_font = nullptr; texture_atlas_t* atlas = nullptr; }; -} // namespace glez::detail::font +} // namespace glez diff --git a/include/glez/glez.hpp b/include/glez/glez.hpp index 8ef90d0..fcb4d84 100644 --- a/include/glez/glez.hpp +++ b/include/glez/glez.hpp @@ -5,9 +5,9 @@ #pragma once -#include #include #include +#include namespace glez { @@ -23,7 +23,7 @@ enum class mode { FREETYPE }; -extern ftgl::vertex_buffer_t *buffer; +extern ftgl::vertex_buffer_t* buffer; struct vertex { ftgl::vec2 position; ftgl::vec2 uv; @@ -33,4 +33,4 @@ struct vertex { void bind(GLuint texture); -}; // namespace glez::detail::program +}; // namespace glez diff --git a/include/glez/picopng/picopng.hpp b/include/glez/picopng/picopng.hpp index 1464929..76c280c 100644 --- a/include/glez/picopng/picopng.hpp +++ b/include/glez/picopng/picopng.hpp @@ -1,5 +1,5 @@ #pragma once #include -int decodePNG(unsigned char *&out_image, int &image_width, int &image_height, - const unsigned char *in_png, size_t in_size, - bool convert_to_rgba32 = true); +int decodePNG(unsigned char*& out_image, int& image_width, int& image_height, + const unsigned char* in_png, size_t in_size, + bool convert_to_rgba32 = true); diff --git a/include/glez/record.hpp b/include/glez/record.hpp index ac6485c..ee9cbde 100644 --- a/include/glez/record.hpp +++ b/include/glez/record.hpp @@ -4,16 +4,13 @@ #pragma once -namespace glez::detail::record -{ +namespace glez::detail::record { class RecordedCommands; } -namespace glez::record -{ +namespace glez::record { -class Record -{ +class Record { public: Record(); ~Record(); @@ -22,7 +19,7 @@ public: void end(); void replay(); - detail::record::RecordedCommands *commands{ nullptr }; + detail::record::RecordedCommands* commands { nullptr }; }; } // namespace glez::record diff --git a/include/glez/texture.hpp b/include/glez/texture.hpp index 63458fa..4c15a31 100644 --- a/include/glez/texture.hpp +++ b/include/glez/texture.hpp @@ -5,18 +5,17 @@ #pragma once -#include -#include -#include #include +#include +#include +#include -namespace glez -{ +namespace glez { class texture { public: - texture(){} - static texture loadFromFile(const std::string &path); + texture() { } + static texture loadFromFile(const std::string& path); static texture loadFromMemory(const std::byte* mem, std::size_t size, unsigned w, unsigned h); texture(texture&&); ~texture(); @@ -26,6 +25,7 @@ public: void bind(); inline bool isLoaded() { return this->init; } + public: bool init = false; bool bound = false; @@ -33,7 +33,7 @@ public: int height = 0; GLuint id; - GLubyte *data; + GLubyte* data; }; -} // namespace glez::texture +} // namespace glez diff --git a/src/detail/record.cpp b/src/detail/record.cpp index fee0a08..2255eb2 100644 --- a/src/detail/record.cpp +++ b/src/detail/record.cpp @@ -3,23 +3,20 @@ */ #include -#include -#include -#include #include +#include +#include +#include -namespace glez::detail::record -{ +namespace glez::detail::record { -void RecordedCommands::render() -{ +void RecordedCommands::render() { isReplaying = true; vertex_buffer_render_setup(vertex_buffer, GL_TRIANGLES); - for (const auto &i : segments) { + for (const auto& i : segments) { if (i.texture) { i.texture->bind(); - } - else if (i.font) { + } else if (i.font) { if (i.font->atlas->id == 0) { glGenTextures(1, &i.font->atlas->id); } @@ -32,18 +29,18 @@ void RecordedCommands::render() glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, i.font->atlas->width, - i.font->atlas->height, 0, GL_RED, GL_UNSIGNED_BYTE, - i.font->atlas->data); + i.font->atlas->height, 0, GL_RED, GL_UNSIGNED_BYTE, + i.font->atlas->data); i.font->atlas->dirty = 0; } } - glDrawElements(GL_TRIANGLES, i.size, GL_UNSIGNED_INT, (void *) (i.start * 4)); + glDrawElements(GL_TRIANGLES, i.size, GL_UNSIGNED_INT, (void*)(i.start * 4)); } vertex_buffer_render_finish(vertex_buffer); isReplaying = false; } -void RecordedCommands::store(glez::vertex *vertices, size_t vcount, uint32_t *indices, size_t icount) { +void RecordedCommands::store(glez::vertex* vertices, size_t vcount, uint32_t* indices, size_t icount) { vertex_buffer_push_back(vertex_buffer, vertices, vcount, indices, icount); } @@ -61,24 +58,21 @@ void RecordedCommands::reset() { memset(¤t, 0, sizeof(current)); } -void RecordedCommands::bindTexture(glez::texture *tx) { +void RecordedCommands::bindTexture(glez::texture* tx) { if (current.texture != tx) { cutSegment(); current.texture = tx; } } -void RecordedCommands::bindFont(glez::font *font) -{ - if (current.font != font) - { +void RecordedCommands::bindFont(glez::font* font) { + if (current.font != font) { cutSegment(); current.font = font; } } -void RecordedCommands::cutSegment() -{ +void RecordedCommands::cutSegment() { current.size = vertex_buffer->indices->size - current.start; if (current.size) segments.push_back(current); @@ -86,39 +80,33 @@ void RecordedCommands::cutSegment() current.start = vertex_buffer->indices->size; } -void RecordedCommands::end() -{ +void RecordedCommands::end() { cutSegment(); } -RecordedCommands *currentRecord{ nullptr }; -bool isReplaying{ false }; +RecordedCommands* currentRecord { nullptr }; +bool isReplaying { false }; } // namespace glez::detail::record -glez::record::Record::Record() -{ - commands = new glez::detail::record::RecordedCommands{}; +glez::record::Record::Record() { + commands = new glez::detail::record::RecordedCommands {}; } -glez::record::Record::~Record() -{ +glez::record::Record::~Record() { delete commands; } -void glez::record::Record::begin() -{ +void glez::record::Record::begin() { detail::record::currentRecord = commands; commands->reset(); } -void glez::record::Record::end() -{ +void glez::record::Record::end() { commands->end(); detail::record::currentRecord = nullptr; } -void glez::record::Record::replay() -{ +void glez::record::Record::replay() { commands->render(); } diff --git a/src/draw.cpp b/src/draw.cpp index 3908128..af0904e 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -4,29 +4,28 @@ */ #include +#include +#include +#include +#include #include #include -#include #include -#include -#include #include -#include -#include +#include -namespace indices -{ +namespace indices { static GLuint rectangle[6] = { 0, 1, 2, 2, 3, 0 }; -static GLuint triangle[3] = { 0, 1, 2 }; +static GLuint triangle[3] = { 0, 1, 2 }; } // namespace indices -void internal_draw_string(float x, float y, const std::string &string, - glez::font& font, glez::rgba color, float *width, float *height) { +void internal_draw_string(float x, float y, const std::string& string, + glez::font& font, glez::rgba color, float* width, float* height) { assert(font.isLoaded()); auto* fnt = font.m_font; - float pen_x = x; - float pen_y = y + fnt->height / 1.5f; + float pen_x = x; + float pen_y = y + fnt->height / 1.5f; float size_y = 0; if (glez::detail::record::currentRecord) { @@ -43,18 +42,18 @@ void internal_draw_string(float x, float y, const std::string &string, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, fnt->atlas->width, - fnt->atlas->height, 0, GL_RED, GL_UNSIGNED_BYTE, - fnt->atlas->data); + fnt->atlas->height, 0, GL_RED, GL_UNSIGNED_BYTE, + fnt->atlas->data); fnt->atlas->dirty = 0; } } - const char *sstring = string.c_str(); + const char* sstring = string.c_str(); - bool skipped{ false }; + bool skipped { false }; for (size_t i = 0; i < string.size(); ++i) { - texture_glyph_t *glyph = texture_font_find_glyph(fnt, &sstring[i]); + texture_glyph_t* glyph = texture_font_find_glyph(fnt, &sstring[i]); if (glyph == NULL) { texture_font_load_glyph(fnt, &sstring[i]); if (!skipped) @@ -64,7 +63,7 @@ void internal_draw_string(float x, float y, const std::string &string, } skipped = false; glez::vertex vertices[4]; - for (auto &vertex : vertices) { + for (auto& vertex : vertices) { vertex.color = color; vertex.mode = static_cast(glez::mode::FREETYPE); } @@ -72,26 +71,26 @@ void internal_draw_string(float x, float y, const std::string &string, if (i > 0) x += texture_glyph_get_kerning(glyph, &sstring[i - 1]); - float x0 = (int) (pen_x + glyph->offset_x); - float y0 = (int) (pen_y - glyph->offset_y); - float x1 = (int) (x0 + glyph->width); - float y1 = (int) (y0 + glyph->height); + float x0 = (int)(pen_x + glyph->offset_x); + float y0 = (int)(pen_y - glyph->offset_y); + float x1 = (int)(x0 + glyph->width); + float y1 = (int)(y0 + glyph->height); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; vertices[0].position = { x0, y0 }; - vertices[0].uv = { s0, t0 }; + vertices[0].uv = { s0, t0 }; vertices[1].position = { x0, y1 }; - vertices[1].uv = { s0, t1 }; + vertices[1].uv = { s0, t1 }; vertices[2].position = { x1, y1 }; - vertices[2].uv = { s1, t1 }; + vertices[2].uv = { s1, t1 }; vertices[3].position = { x1, y0 }; - vertices[3].uv = { s1, t0 }; + vertices[3].uv = { s1, t0 }; pen_x += glyph->advance_x; // pen_x = (int) pen_x + 1; @@ -123,8 +122,8 @@ void line(float x, float y, float dx, float dy, rgba color, float thickness) { vertex vertices[4]; - for (auto &vertex : vertices) { - vertex.mode = static_cast(mode::PLAIN); + for (auto& vertex : vertices) { + vertex.mode = static_cast(mode::PLAIN); vertex.color = color; } @@ -155,17 +154,17 @@ void line(float x, float y, float dx, float dy, rgba color, float thickness) { if (detail::record::currentRecord) detail::record::currentRecord->store(vertices, 4, indices::rectangle, - 6); + 6); else ftgl::vertex_buffer_push_back(buffer, vertices, 4, - indices::rectangle, 6); + indices::rectangle, 6); } void rect(float x, float y, float w, float h, rgba color) { vertex vertices[4]; - for (auto &vertex : vertices) { - vertex.mode = static_cast(mode::PLAIN); + for (auto& vertex : vertices) { + vertex.mode = static_cast(mode::PLAIN); vertex.color = color; } @@ -183,8 +182,8 @@ void rect(float x, float y, float w, float h, rgba color) { void triangle(float x, float y, float x2, float y2, float x3, float y3, rgba color) { vertex vertices[3]; - for (auto &vertex : vertices) { - vertex.mode = static_cast(mode::PLAIN); + for (auto& vertex : vertices) { + vertex.mode = static_cast(mode::PLAIN); vertex.color = color; } @@ -206,7 +205,7 @@ void rect_outline(float x, float y, float w, float h, rgba color, float thicknes } void circle(float x, float y, float radius, rgba color, float thickness, - int steps) { + int steps) { float px = 0; float py = 0; for (int i = 0; i <= steps; i++) { @@ -220,19 +219,19 @@ void circle(float x, float y, float radius, rgba color, float thickness, } } -void string(float x, float y, const std::string &string, font &font, rgba color, float *width, float *height) { - auto fnt = font.m_font; - fnt->rendermode = RENDER_NORMAL; +void string(float x, float y, const std::string& string, font& font, rgba color, float* width, float* height) { + auto fnt = font.m_font; + fnt->rendermode = RENDER_NORMAL; fnt->outline_thickness = 0.0f; internal_draw_string(x, y, string, font, color, width, height); } -void outlined_string(float x, float y, const std::string& string, font& font, rgba color, rgba outline, float *width, float *height) { - auto fnt = font.m_font; - fnt->rendermode = RENDER_OUTLINE_POSITIVE; +void outlined_string(float x, float y, const std::string& string, font& font, rgba color, rgba outline, float* width, float* height) { + auto fnt = font.m_font; + fnt->rendermode = RENDER_OUTLINE_POSITIVE; fnt->outline_thickness = 1.0f; internal_draw_string(x, y, string, font, outline, width, height); - fnt->rendermode = RENDER_NORMAL; + fnt->rendermode = RENDER_NORMAL; fnt->outline_thickness = 0.0f; internal_draw_string(x, y, string, font, color, width, height); } @@ -242,8 +241,8 @@ void rect_textured(float x, float y, float w, float h, rgba color, texture& text texture.load();*/ assert(texture.isLoaded()); - //if (!texture.canLoad()) - //return; + // if (!texture.canLoad()) + // return; if (glez::detail::record::currentRecord) glez::detail::record::currentRecord->bindTexture(&texture); @@ -252,8 +251,8 @@ void rect_textured(float x, float y, float w, float h, rgba color, texture& text vertex vertices[4]; - for (auto &vertex : vertices) { - vertex.mode = static_cast(mode::TEXTURED); + for (auto& vertex : vertices) { + vertex.mode = static_cast(mode::TEXTURED); vertex.color = color; } @@ -266,7 +265,7 @@ void rect_textured(float x, float y, float w, float h, rgba color, texture& text float cx = x + float(w) / 2.0f; float cy = y + float(h) / 2.0f; - for (auto &v : vertices) { + for (auto& v : vertices) { float ox = v.position.x; float oy = v.position.y; diff --git a/src/font.cpp b/src/font.cpp index 041e8a6..c242fdf 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -3,14 +3,14 @@ Copyright (c) 2018 nullworks. All rights reserved. */ -#include -#include -#include #include +#include +#include +#include namespace glez { -font font::loadFromFile(const std::string &path, float size) { +font font::loadFromFile(const std::string& path, float size) { assert(size > 0); font ret; @@ -36,7 +36,7 @@ glez::font& font::operator=(glez::font&& var) { return *this; } void font::stringSize(const std::string& string, float* width, float* height) { - assert(this->isLoaded()); + assert(this->isLoaded()); float penX = 0; float size_x = 0; @@ -44,12 +44,11 @@ void font::stringSize(const std::string& string, float* width, float* height) { texture_font_load_glyphs(m_font, string.c_str()); - const char *sstring = string.c_str(); + const char* sstring = string.c_str(); - for (size_t i = 0; i < string.size(); ++i) - { + for (size_t i = 0; i < string.size(); ++i) { // c_str guarantees a NULL terminator - texture_glyph_t *glyph = texture_font_find_glyph(m_font, &sstring[i]); + texture_glyph_t* glyph = texture_font_find_glyph(m_font, &sstring[i]); if (glyph == nullptr) continue; @@ -68,4 +67,4 @@ void font::stringSize(const std::string& string, float* width, float* height) { *height = size_y; } -} // namespace glez::detail::font +} // namespace glez diff --git a/src/glez.cpp b/src/glez.cpp index 4b919cf..50b564d 100644 --- a/src/glez.cpp +++ b/src/glez.cpp @@ -3,20 +3,19 @@ Copyright (c) 2018 nullworks. All rights reserved. */ -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include #include #include -#include #include -#include +#include +#include +#include -static const char *shader_vertex = R"END( +static const char* shader_vertex = R"END( #version 130 uniform mat4 projection; in vec2 vertex; @@ -35,7 +34,7 @@ void main() } )END"; -static const char *shader_fragment = R"END( +static const char* shader_fragment = R"END( #version 130 uniform sampler2D texture; in vec4 frag_Color; @@ -60,9 +59,9 @@ void main() } )END"; -static GLuint shader{ 0 }; +static GLuint shader { 0 }; -static GLuint compile(const char *source, GLenum type) { +static GLuint compile(const char* source, GLenum type) { GLint status; GLuint result = glCreateShader(type); @@ -70,8 +69,7 @@ static GLuint compile(const char *source, GLenum type) { glCompileShader(result); glGetShaderiv(result, GL_COMPILE_STATUS, &status); - if (status != GL_TRUE) - { + if (status != GL_TRUE) { char error[512]; GLsizei length; glGetShaderInfoLog(result, 512, &length, error); @@ -113,8 +111,7 @@ void resize(int width, int height) { glUseProgram(0); } -void init(int width, int height) -{ +void init(int width, int height) { buffer = ftgl::vertex_buffer_new("vertex:2f,tex_coord:2f,color:4f,drawmode:1i"); shader = link(compile(shader_vertex, GL_VERTEX_SHADER), compile(shader_fragment, GL_FRAGMENT_SHADER)); @@ -130,8 +127,7 @@ void shutdown() { glDeleteProgram(shader); } -static GLuint current_texture{ 0 }; - +static GLuint current_texture { 0 }; void begin() { glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT); @@ -183,4 +179,4 @@ void bind(GLuint texture) { } } -} +} // namespace glez diff --git a/src/picopng/picopng.cpp b/src/picopng/picopng.cpp index de0497d..f6e2b56 100644 --- a/src/picopng/picopng.cpp +++ b/src/picopng/picopng.cpp @@ -1,5 +1,5 @@ -#include #include +#include /* decodePNG: The picoPNG function, decodes a PNG file buffer in memory, into a raw pixel buffer. out_image: output parameter, this will contain the raw pixels after decoding. @@ -20,483 +20,696 @@ convert_to_rgba32: optional parameter, true by default. works for trusted PNG files. Use LodePNG instead of picoPNG if you need this information. return: 0 if success, not 0 if some error occured. */ -int decodePNG(unsigned char* &out_image, int& image_width, int& image_height, const unsigned char* in_png, size_t in_size, bool convert_to_rgba32 = true) -{ - // picoPNG version 20101224 - // Copyright (c) 2005-2010 Lode Vandevenne - // - // This software is provided 'as-is', without any express or implied - // warranty. In no event will the authors be held liable for any damages - // arising from the use of this software. - // - // Permission is granted to anyone to use this software for any purpose, - // including commercial applications, and to alter it and redistribute it - // freely, subject to the following restrictions: - // - // 1. The origin of this software must not be misrepresented; you must not - // claim that you wrote the original software. If you use this software - // in a product, an acknowledgment in the product documentation would be - // appreciated but is not required. - // 2. Altered source versions must be plainly marked as such, and must not be - // misrepresented as being the original software. - // 3. This notice may not be removed or altered from any source distribution. +int decodePNG(unsigned char*& out_image, int& image_width, int& image_height, const unsigned char* in_png, size_t in_size, bool convert_to_rgba32 = true) { + // picoPNG version 20101224 + // Copyright (c) 2005-2010 Lode Vandevenne + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgment in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. - // picoPNG is a PNG decoder in one C++ function of around 500 lines. Use picoPNG for - // programs that need only 1 .cpp file. Since it's a single function, it's very limited, - // it can convert a PNG to raw pixel data either converted to 32-bit RGBA color or - // with no color conversion at all. For anything more complex, another tiny library - // is available: LodePNG (lodepng.c(pp)), which is a single source and header file. - // Apologies for the compact code style, it's to make this tiny. + // picoPNG is a PNG decoder in one C++ function of around 500 lines. Use picoPNG for + // programs that need only 1 .cpp file. Since it's a single function, it's very limited, + // it can convert a PNG to raw pixel data either converted to 32-bit RGBA color or + // with no color conversion at all. For anything more complex, another tiny library + // is available: LodePNG (lodepng.c(pp)), which is a single source and header file. + // Apologies for the compact code style, it's to make this tiny. - static const unsigned long LENBASE[29] = {3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258}; - static const unsigned long LENEXTRA[29] = {0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; - static const unsigned long DISTBASE[30] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; - static const unsigned long DISTEXTRA[30] = {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; - static const unsigned long CLCL[19] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; //code length code lengths - struct Zlib //nested functions for zlib decompression - { - static unsigned long readBitFromStream(size_t& bitp, const unsigned char* bits) { unsigned long result = (bits[bitp >> 3] >> (bitp & 0x7)) & 1; bitp++; return result;} - static unsigned long readBitsFromStream(size_t& bitp, const unsigned char* bits, size_t nbits) + static const unsigned long LENBASE[29] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 }; + static const unsigned long LENEXTRA[29] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static const unsigned long DISTBASE[30] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 }; + static const unsigned long DISTEXTRA[30] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const unsigned long CLCL[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; // code length code lengths + struct Zlib // nested functions for zlib decompression { - unsigned long result = 0; - for(size_t i = 0; i < nbits; i++) result += (readBitFromStream(bitp, bits)) << i; - return result; - } - struct HuffmanTree - { - int makeFromLengths(const std::vector& bitlen, unsigned long maxbitlen) - { //make tree given the lengths - unsigned long numcodes = (unsigned long)(bitlen.size()), treepos = 0, nodefilled = 0; - std::vector tree1d(numcodes), blcount(maxbitlen + 1, 0), nextcode(maxbitlen + 1, 0); - for(unsigned long bits = 0; bits < numcodes; bits++) blcount[bitlen[bits]]++; //count number of instances of each code length - for(unsigned long bits = 1; bits <= maxbitlen; bits++) nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1; - for(unsigned long n = 0; n < numcodes; n++) if(bitlen[n] != 0) tree1d[n] = nextcode[bitlen[n]]++; //generate all the codes - tree2d.clear(); tree2d.resize(numcodes * 2, 32767); //32767 here means the tree2d isn't filled there yet - for(unsigned long n = 0; n < numcodes; n++) //the codes - for(unsigned long i = 0; i < bitlen[n]; i++) //the bits for this code - { - unsigned long bit = (tree1d[n] >> (bitlen[n] - i - 1)) & 1; - if(treepos > numcodes - 2) return 55; - if(tree2d[2 * treepos + bit] == 32767) //not yet filled in - { - if(i + 1 == bitlen[n]) { tree2d[2 * treepos + bit] = n; treepos = 0; } //last bit - else { tree2d[2 * treepos + bit] = ++nodefilled + numcodes; treepos = nodefilled; } //addresses are encoded as values > numcodes - } - else treepos = tree2d[2 * treepos + bit] - numcodes; //subtract numcodes from address to get address value + static unsigned long readBitFromStream(size_t& bitp, const unsigned char* bits) { + unsigned long result = (bits[bitp >> 3] >> (bitp & 0x7)) & 1; + bitp++; + return result; + } + static unsigned long readBitsFromStream(size_t& bitp, const unsigned char* bits, size_t nbits) { + unsigned long result = 0; + for (size_t i = 0; i < nbits; i++) + result += (readBitFromStream(bitp, bits)) << i; + return result; + } + struct HuffmanTree { + int makeFromLengths(const std::vector& bitlen, unsigned long maxbitlen) { // make tree given the lengths + unsigned long numcodes = (unsigned long)(bitlen.size()), treepos = 0, nodefilled = 0; + std::vector tree1d(numcodes), blcount(maxbitlen + 1, 0), nextcode(maxbitlen + 1, 0); + for (unsigned long bits = 0; bits < numcodes; bits++) + blcount[bitlen[bits]]++; // count number of instances of each code length + for (unsigned long bits = 1; bits <= maxbitlen; bits++) + nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1; + for (unsigned long n = 0; n < numcodes; n++) + if (bitlen[n] != 0) + tree1d[n] = nextcode[bitlen[n]]++; // generate all the codes + tree2d.clear(); + tree2d.resize(numcodes * 2, 32767); // 32767 here means the tree2d isn't filled there yet + for (unsigned long n = 0; n < numcodes; n++) // the codes + for (unsigned long i = 0; i < bitlen[n]; i++) // the bits for this code + { + unsigned long bit = (tree1d[n] >> (bitlen[n] - i - 1)) & 1; + if (treepos > numcodes - 2) + return 55; + if (tree2d[2 * treepos + bit] == 32767) // not yet filled in + { + if (i + 1 == bitlen[n]) { + tree2d[2 * treepos + bit] = n; + treepos = 0; + } // last bit + else { + tree2d[2 * treepos + bit] = ++nodefilled + numcodes; + treepos = nodefilled; + } // addresses are encoded as values > numcodes + } else + treepos = tree2d[2 * treepos + bit] - numcodes; // subtract numcodes from address to get address value + } + return 0; + } + int decode(bool& decoded, unsigned long& result, size_t& treepos, unsigned long bit) const { // Decodes a symbol from the tree + unsigned long numcodes = (unsigned long)tree2d.size() / 2; + if (treepos >= numcodes) + return 11; // error: you appeared outside the codetree + result = tree2d[2 * treepos + bit]; + decoded = (result < numcodes); + treepos = decoded ? 0 : result - numcodes; + return 0; + } + std::vector tree2d; // 2D representation of a huffman tree: The one dimension is "0" or "1", the other contains all nodes and leaves of the tree. + }; + struct Inflator { + int error; + void inflate(std::vector& out, const std::vector& in, size_t inpos = 0) { + size_t bp = 0, pos = 0; // bit pointer and byte pointer + error = 0; + unsigned long BFINAL = 0; + while (!BFINAL && !error) { + if (bp >> 3 >= in.size()) { + error = 52; + return; + } // error, bit pointer will jump past memory + BFINAL = readBitFromStream(bp, &in[inpos]); + unsigned long BTYPE = readBitFromStream(bp, &in[inpos]); + BTYPE += 2 * readBitFromStream(bp, &in[inpos]); + if (BTYPE == 3) { + error = 20; + return; + } // error: invalid BTYPE + else if (BTYPE == 0) + inflateNoCompression(out, &in[inpos], bp, pos, in.size()); + else + inflateHuffmanBlock(out, &in[inpos], bp, pos, in.size(), BTYPE); + } + if (!error) + out.resize(pos); // Only now we know the true size of out, resize it to that + } + void generateFixedTrees(HuffmanTree& tree, HuffmanTree& treeD) // get the tree of a deflated block with fixed tree + { + std::vector bitlen(288, 8), bitlenD(32, 5); + ; + for (size_t i = 144; i <= 255; i++) + bitlen[i] = 9; + for (size_t i = 256; i <= 279; i++) + bitlen[i] = 7; + tree.makeFromLengths(bitlen, 15); + treeD.makeFromLengths(bitlenD, 15); + } + HuffmanTree codetree, codetreeD, codelengthcodetree; // the code tree for Huffman codes, dist codes, and code length codes + unsigned long huffmanDecodeSymbol(const unsigned char* in, size_t& bp, const HuffmanTree& codetree, size_t inlength) { // decode a single symbol from given list of bits with given code tree. return value is the symbol + bool decoded; + unsigned long ct; + for (size_t treepos = 0;;) { + if ((bp & 0x07) == 0 && (bp >> 3) > inlength) { + error = 10; + return 0; + } // error: end reached without endcode + error = codetree.decode(decoded, ct, treepos, readBitFromStream(bp, in)); + if (error) + return 0; // stop, an error happened + if (decoded) + return ct; + } + } + void getTreeInflateDynamic(HuffmanTree& tree, HuffmanTree& treeD, const unsigned char* in, size_t& bp, size_t inlength) { // get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree + std::vector bitlen(288, 0), bitlenD(32, 0); + if (bp >> 3 >= inlength - 2) { + error = 49; + return; + } // the bit pointer is or will go past the memory + size_t HLIT = readBitsFromStream(bp, in, 5) + 257; // number of literal/length codes + 257 + size_t HDIST = readBitsFromStream(bp, in, 5) + 1; // number of dist codes + 1 + size_t HCLEN = readBitsFromStream(bp, in, 4) + 4; // number of code length codes + 4 + std::vector codelengthcode(19); // lengths of tree to decode the lengths of the dynamic tree + for (size_t i = 0; i < 19; i++) + codelengthcode[CLCL[i]] = (i < HCLEN) ? readBitsFromStream(bp, in, 3) : 0; + error = codelengthcodetree.makeFromLengths(codelengthcode, 7); + if (error) + return; + size_t i = 0, replength; + while (i < HLIT + HDIST) { + unsigned long code = huffmanDecodeSymbol(in, bp, codelengthcodetree, inlength); + if (error) + return; + if (code <= 15) { + if (i < HLIT) + bitlen[i++] = code; + else + bitlenD[i++ - HLIT] = code; + } // a length code + else if (code == 16) // repeat previous + { + if (bp >> 3 >= inlength) { + error = 50; + return; + } // error, bit pointer jumps past memory + replength = 3 + readBitsFromStream(bp, in, 2); + unsigned long value; // set value to the previous code + if ((i - 1) < HLIT) + value = bitlen[i - 1]; + else + value = bitlenD[i - HLIT - 1]; + for (size_t n = 0; n < replength; n++) // repeat this value in the next lengths + { + if (i >= HLIT + HDIST) { + error = 13; + return; + } // error: i is larger than the amount of codes + if (i < HLIT) + bitlen[i++] = value; + else + bitlenD[i++ - HLIT] = value; + } + } else if (code == 17) // repeat "0" 3-10 times + { + if (bp >> 3 >= inlength) { + error = 50; + return; + } // error, bit pointer jumps past memory + replength = 3 + readBitsFromStream(bp, in, 3); + for (size_t n = 0; n < replength; n++) // repeat this value in the next lengths + { + if (i >= HLIT + HDIST) { + error = 14; + return; + } // error: i is larger than the amount of codes + if (i < HLIT) + bitlen[i++] = 0; + else + bitlenD[i++ - HLIT] = 0; + } + } else if (code == 18) // repeat "0" 11-138 times + { + if (bp >> 3 >= inlength) { + error = 50; + return; + } // error, bit pointer jumps past memory + replength = 11 + readBitsFromStream(bp, in, 7); + for (size_t n = 0; n < replength; n++) // repeat this value in the next lengths + { + if (i >= HLIT + HDIST) { + error = 15; + return; + } // error: i is larger than the amount of codes + if (i < HLIT) + bitlen[i++] = 0; + else + bitlenD[i++ - HLIT] = 0; + } + } else { + error = 16; + return; + } // error: somehow an unexisting code appeared. This can never happen. + } + if (bitlen[256] == 0) { + error = 64; + return; + } // the length of the end code 256 must be larger than 0 + error = tree.makeFromLengths(bitlen, 15); + if (error) + return; // now we've finally got HLIT and HDIST, so generate the code trees, and the function is done + error = treeD.makeFromLengths(bitlenD, 15); + if (error) + return; + } + void inflateHuffmanBlock(std::vector& out, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength, unsigned long btype) { + if (btype == 1) { + generateFixedTrees(codetree, codetreeD); + } else if (btype == 2) { + getTreeInflateDynamic(codetree, codetreeD, in, bp, inlength); + if (error) + return; + } + for (;;) { + unsigned long code = huffmanDecodeSymbol(in, bp, codetree, inlength); + if (error) + return; + if (code == 256) + return; // end code + else if (code <= 255) // literal symbol + { + if (pos >= out.size()) + out.resize((pos + 1) * 2); // reserve more room + out[pos++] = (unsigned char)(code); + } else if (code >= 257 && code <= 285) // length code + { + size_t length = LENBASE[code - 257], numextrabits = LENEXTRA[code - 257]; + if ((bp >> 3) >= inlength) { + error = 51; + return; + } // error, bit pointer will jump past memory + length += readBitsFromStream(bp, in, numextrabits); + unsigned long codeD = huffmanDecodeSymbol(in, bp, codetreeD, inlength); + if (error) + return; + if (codeD > 29) { + error = 18; + return; + } // error: invalid dist code (30-31 are never used) + unsigned long dist = DISTBASE[codeD], numextrabitsD = DISTEXTRA[codeD]; + if ((bp >> 3) >= inlength) { + error = 51; + return; + } // error, bit pointer will jump past memory + dist += readBitsFromStream(bp, in, numextrabitsD); + size_t start = pos, back = start - dist; // backwards + if (pos + length >= out.size()) + out.resize((pos + length) * 2); // reserve more room + for (size_t i = 0; i < length; i++) { + out[pos++] = out[back++]; + if (back >= start) + back = start - dist; + } + } + } + } + void inflateNoCompression(std::vector& out, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength) { + while ((bp & 0x7) != 0) + bp++; // go to first boundary of byte + size_t p = bp / 8; + if (p >= inlength - 4) { + error = 52; + return; + } // error, bit pointer will jump past memory + unsigned long LEN = in[p] + 256 * in[p + 1], NLEN = in[p + 2] + 256 * in[p + 3]; + p += 4; + if (LEN + NLEN != 65535) { + error = 21; + return; + } // error: NLEN is not one's complement of LEN + if (pos + LEN >= out.size()) + out.resize(pos + LEN); + if (p + LEN > inlength) { + error = 23; + return; + } // error: reading outside of in buffer + for (unsigned long n = 0; n < LEN; n++) + out[pos++] = in[p++]; // read LEN bytes of literal data + bp = p * 8; + } + }; + int decompress(std::vector& out, const std::vector& in) // returns error value + { + Inflator inflator; + if (in.size() < 2) { + return 53; + } // error, size of zlib data too small + if ((in[0] * 256 + in[1]) % 31 != 0) { + return 24; + } // error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way + unsigned long CM = in[0] & 15, CINFO = (in[0] >> 4) & 15, FDICT = (in[1] >> 5) & 1; + if (CM != 8 || CINFO > 7) { + return 25; + } // error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec + if (FDICT != 0) { + return 26; + } // error: the specification of PNG says about the zlib stream: "The additional flags shall not specify a preset dictionary." + inflator.inflate(out, in, 2); + return inflator.error; // note: adler32 checksum was skipped and ignored } - return 0; - } - int decode(bool& decoded, unsigned long& result, size_t& treepos, unsigned long bit) const - { //Decodes a symbol from the tree - unsigned long numcodes = (unsigned long)tree2d.size() / 2; - if(treepos >= numcodes) return 11; //error: you appeared outside the codetree - result = tree2d[2 * treepos + bit]; - decoded = (result < numcodes); - treepos = decoded ? 0 : result - numcodes; - return 0; - } - std::vector tree2d; //2D representation of a huffman tree: The one dimension is "0" or "1", the other contains all nodes and leaves of the tree. }; - struct Inflator + struct PNG // nested functions for PNG decoding { - int error; - void inflate(std::vector& out, const std::vector& in, size_t inpos = 0) - { - size_t bp = 0, pos = 0; //bit pointer and byte pointer - error = 0; - unsigned long BFINAL = 0; - while(!BFINAL && !error) - { - if(bp >> 3 >= in.size()) { error = 52; return; } //error, bit pointer will jump past memory - BFINAL = readBitFromStream(bp, &in[inpos]); - unsigned long BTYPE = readBitFromStream(bp, &in[inpos]); BTYPE += 2 * readBitFromStream(bp, &in[inpos]); - if(BTYPE == 3) { error = 20; return; } //error: invalid BTYPE - else if(BTYPE == 0) inflateNoCompression(out, &in[inpos], bp, pos, in.size()); - else inflateHuffmanBlock(out, &in[inpos], bp, pos, in.size(), BTYPE); - } - if(!error) out.resize(pos); //Only now we know the true size of out, resize it to that - } - void generateFixedTrees(HuffmanTree& tree, HuffmanTree& treeD) //get the tree of a deflated block with fixed tree - { - std::vector bitlen(288, 8), bitlenD(32, 5);; - for(size_t i = 144; i <= 255; i++) bitlen[i] = 9; - for(size_t i = 256; i <= 279; i++) bitlen[i] = 7; - tree.makeFromLengths(bitlen, 15); - treeD.makeFromLengths(bitlenD, 15); - } - HuffmanTree codetree, codetreeD, codelengthcodetree; //the code tree for Huffman codes, dist codes, and code length codes - unsigned long huffmanDecodeSymbol(const unsigned char* in, size_t& bp, const HuffmanTree& codetree, size_t inlength) - { //decode a single symbol from given list of bits with given code tree. return value is the symbol - bool decoded; unsigned long ct; - for(size_t treepos = 0;;) - { - if((bp & 0x07) == 0 && (bp >> 3) > inlength) { error = 10; return 0; } //error: end reached without endcode - error = codetree.decode(decoded, ct, treepos, readBitFromStream(bp, in)); if(error) return 0; //stop, an error happened - if(decoded) return ct; - } - } - void getTreeInflateDynamic(HuffmanTree& tree, HuffmanTree& treeD, const unsigned char* in, size_t& bp, size_t inlength) - { //get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree - std::vector bitlen(288, 0), bitlenD(32, 0); - if(bp >> 3 >= inlength - 2) { error = 49; return; } //the bit pointer is or will go past the memory - size_t HLIT = readBitsFromStream(bp, in, 5) + 257; //number of literal/length codes + 257 - size_t HDIST = readBitsFromStream(bp, in, 5) + 1; //number of dist codes + 1 - size_t HCLEN = readBitsFromStream(bp, in, 4) + 4; //number of code length codes + 4 - std::vector codelengthcode(19); //lengths of tree to decode the lengths of the dynamic tree - for(size_t i = 0; i < 19; i++) codelengthcode[CLCL[i]] = (i < HCLEN) ? readBitsFromStream(bp, in, 3) : 0; - error = codelengthcodetree.makeFromLengths(codelengthcode, 7); if(error) return; - size_t i = 0, replength; - while(i < HLIT + HDIST) - { - unsigned long code = huffmanDecodeSymbol(in, bp, codelengthcodetree, inlength); if(error) return; - if(code <= 15) { if(i < HLIT) bitlen[i++] = code; else bitlenD[i++ - HLIT] = code; } //a length code - else if(code == 16) //repeat previous - { - if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory - replength = 3 + readBitsFromStream(bp, in, 2); - unsigned long value; //set value to the previous code - if((i - 1) < HLIT) value = bitlen[i - 1]; - else value = bitlenD[i - HLIT - 1]; - for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths + struct Info { + unsigned long width, height, colorType, bitDepth, compressionMethod, filterMethod, interlaceMethod, key_r, key_g, key_b; + bool key_defined; // is a transparent color key given? + std::vector palette; + } info; + int error; + void decode(unsigned char*& out, const unsigned char* in, size_t size, bool convert_to_rgba32) { + error = 0; + if (size == 0 || in == 0) { + error = 48; + return; + } // the given data is empty + readPngHeader(&in[0], size); + if (error) + return; + size_t pos = 33; // first byte of the first chunk after the header + std::vector idat; // the data from idat chunks + bool IEND = false, known_type = true; + info.key_defined = false; + while (!IEND) // loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer { - if(i >= HLIT + HDIST) { error = 13; return; } //error: i is larger than the amount of codes - if(i < HLIT) bitlen[i++] = value; else bitlenD[i++ - HLIT] = value; + if (pos + 8 >= size) { + error = 30; + return; + } // error: size of the in buffer too small to contain next chunk + size_t chunkLength = read32bitInt(&in[pos]); + pos += 4; + if (chunkLength > 2147483647) { + error = 63; + return; + } + if (pos + chunkLength >= size) { + error = 35; + return; + } // error: size of the in buffer too small to contain next chunk + if (in[pos + 0] == 'I' && in[pos + 1] == 'D' && in[pos + 2] == 'A' && in[pos + 3] == 'T') // IDAT chunk, containing compressed image data + { + idat.insert(idat.end(), &in[pos + 4], &in[pos + 4 + chunkLength]); + pos += (4 + chunkLength); + } else if (in[pos + 0] == 'I' && in[pos + 1] == 'E' && in[pos + 2] == 'N' && in[pos + 3] == 'D') { + pos += 4; + IEND = true; + } else if (in[pos + 0] == 'P' && in[pos + 1] == 'L' && in[pos + 2] == 'T' && in[pos + 3] == 'E') // palette chunk (PLTE) + { + pos += 4; // go after the 4 letters + info.palette.resize(4 * (chunkLength / 3)); + if (info.palette.size() > (4 * 256)) { + error = 38; + return; + } // error: palette too big + for (size_t i = 0; i < info.palette.size(); i += 4) { + for (size_t j = 0; j < 3; j++) + info.palette[i + j] = in[pos++]; // RGB + info.palette[i + 3] = 255; // alpha + } + } else if (in[pos + 0] == 't' && in[pos + 1] == 'R' && in[pos + 2] == 'N' && in[pos + 3] == 'S') // palette transparency chunk (tRNS) + { + pos += 4; // go after the 4 letters + if (info.colorType == 3) { + if (4 * chunkLength > info.palette.size()) { + error = 39; + return; + } // error: more alpha values given than there are palette entries + for (size_t i = 0; i < chunkLength; i++) + info.palette[4 * i + 3] = in[pos++]; + } else if (info.colorType == 0) { + if (chunkLength != 2) { + error = 40; + return; + } // error: this chunk must be 2 bytes for greyscale image + info.key_defined = 1; + info.key_r = info.key_g = info.key_b = 256 * in[pos] + in[pos + 1]; + pos += 2; + } else if (info.colorType == 2) { + if (chunkLength != 6) { + error = 41; + return; + } // error: this chunk must be 6 bytes for RGB image + info.key_defined = 1; + info.key_r = 256 * in[pos] + in[pos + 1]; + pos += 2; + info.key_g = 256 * in[pos] + in[pos + 1]; + pos += 2; + info.key_b = 256 * in[pos] + in[pos + 1]; + pos += 2; + } else { + error = 42; + return; + } // error: tRNS chunk not allowed for other color models + } else // it's not an implemented chunk type, so ignore it: skip over the data + { + if (!(in[pos + 0] & 32)) { + error = 69; + return; + } // error: unknown critical chunk (5th bit of first byte of chunk type is 0) + pos += (chunkLength + 4); // skip 4 letters and uninterpreted data of unimplemented chunk + known_type = false; + } + pos += 4; // step over CRC (which is ignored) } - } - else if(code == 17) //repeat "0" 3-10 times - { - if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory - replength = 3 + readBitsFromStream(bp, in, 3); - for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths + unsigned long bpp = getBpp(info); + std::vector scanlines(((info.width * (info.height * bpp + 7)) / 8) + info.height); // now the out buffer will be filled + Zlib zlib; // decompress with the Zlib decompressor + error = zlib.decompress(scanlines, idat); + if (error) + return; // stop if the zlib decompressor returned an error + size_t bytewidth = (bpp + 7) / 8, outlength = (info.height * info.width * bpp + 7) / 8; + out = new unsigned char[outlength + 1]; // time to fill the out buffer + if (info.interlaceMethod == 0) // no interlace, just filter { - if(i >= HLIT + HDIST) { error = 14; return; } //error: i is larger than the amount of codes - if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0; + size_t linestart = 0, linelength = (info.width * bpp + 7) / 8; // length in bytes of a scanline, excluding the filtertype byte + if (bpp >= 8) // byte per byte + for (unsigned long y = 0; y < info.height; y++) { + unsigned long filterType = scanlines[linestart]; + const unsigned char* prevline = (y == 0) ? 0 : &out[(y - 1) * info.width * bytewidth]; + unFilterScanline(&out[linestart - y], &scanlines[linestart + 1], prevline, bytewidth, filterType, linelength); + if (error) + return; + linestart += (1 + linelength); // go to start of next scanline + } + else // less than 8 bits per pixel, so fill it up bit per bit + { + unsigned char* templine = new unsigned char[((info.width * bpp + 7) >> 3) + 1]; // only used if bpp < 8 + for (size_t y = 0, obp = 0; y < info.height; y++) { + unsigned long filterType = scanlines[linestart]; + const unsigned char* prevline = (y == 0) ? 0 : &out[(y - 1) * info.width * bytewidth]; + unFilterScanline(templine, &scanlines[linestart + 1], prevline, bytewidth, filterType, linelength); + if (error) + return; + for (size_t bp = 0; bp < info.width * bpp;) + setBitOfReversedStream(obp, out, readBitFromReversedStream(bp, &templine[0])); + linestart += (1 + linelength); // go to start of next scanline + } + delete[] templine; + } } - } - else if(code == 18) //repeat "0" 11-138 times - { - if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory - replength = 11 + readBitsFromStream(bp, in, 7); - for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths + if (convert_to_rgba32 && (info.colorType != 6 || info.bitDepth != 8)) // conversion needed { - if(i >= HLIT + HDIST) { error = 15; return; } //error: i is larger than the amount of codes - if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0; + auto data = out; + error = convert(out, data, info, info.width, info.height); } - } - else { error = 16; return; } //error: somehow an unexisting code appeared. This can never happen. } - if(bitlen[256] == 0) { error = 64; return; } //the length of the end code 256 must be larger than 0 - error = tree.makeFromLengths(bitlen, 15); if(error) return; //now we've finally got HLIT and HDIST, so generate the code trees, and the function is done - error = treeD.makeFromLengths(bitlenD, 15); if(error) return; - } - void inflateHuffmanBlock(std::vector& out, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength, unsigned long btype) - { - if(btype == 1) { generateFixedTrees(codetree, codetreeD); } - else if(btype == 2) { getTreeInflateDynamic(codetree, codetreeD, in, bp, inlength); if(error) return; } - for(;;) + void readPngHeader(const unsigned char* in, size_t inlength) // read the information from the header and store it in the Info { - unsigned long code = huffmanDecodeSymbol(in, bp, codetree, inlength); if(error) return; - if(code == 256) return; //end code - else if(code <= 255) //literal symbol - { - if(pos >= out.size()) out.resize((pos + 1) * 2); //reserve more room - out[pos++] = (unsigned char)(code); - } - else if(code >= 257 && code <= 285) //length code - { - size_t length = LENBASE[code - 257], numextrabits = LENEXTRA[code - 257]; - if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory - length += readBitsFromStream(bp, in, numextrabits); - unsigned long codeD = huffmanDecodeSymbol(in, bp, codetreeD, inlength); if(error) return; - if(codeD > 29) { error = 18; return; } //error: invalid dist code (30-31 are never used) - unsigned long dist = DISTBASE[codeD], numextrabitsD = DISTEXTRA[codeD]; - if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory - dist += readBitsFromStream(bp, in, numextrabitsD); - size_t start = pos, back = start - dist; //backwards - if(pos + length >= out.size()) out.resize((pos + length) * 2); //reserve more room - for(size_t i = 0; i < length; i++) { out[pos++] = out[back++]; if(back >= start) back = start - dist; } - } + if (inlength < 29) { + error = 27; + return; + } // error: the data length is smaller than the length of the header + if (in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { + error = 28; + return; + } // no PNG signature + if (in[12] != 'I' || in[13] != 'H' || in[14] != 'D' || in[15] != 'R') { + error = 29; + return; + } // error: it doesn't start with a IHDR chunk! + info.width = read32bitInt(&in[16]); + info.height = read32bitInt(&in[20]); + info.bitDepth = in[24]; + info.colorType = in[25]; + info.compressionMethod = in[26]; + if (in[26] != 0) { + error = 32; + return; + } // error: only compression method 0 is allowed in the specification + info.filterMethod = in[27]; + if (in[27] != 0) { + error = 33; + return; + } // error: only filter method 0 is allowed in the specification + info.interlaceMethod = in[28]; + if (in[28] > 1) { + error = 34; + return; + } // error: only interlace methods 0 and 1 exist in the specification + error = checkColorValidity(info.colorType, info.bitDepth); + } + void unFilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned long filterType, size_t length) { + switch (filterType) { + case 0: + for (size_t i = 0; i < length; i++) + recon[i] = scanline[i]; + break; + case 1: + for (size_t i = 0; i < bytewidth; i++) + recon[i] = scanline[i]; + for (size_t i = bytewidth; i < length; i++) + recon[i] = scanline[i] + recon[i - bytewidth]; + break; + case 2: + if (precon) + for (size_t i = 0; i < length; i++) + recon[i] = scanline[i] + precon[i]; + else + for (size_t i = 0; i < length; i++) + recon[i] = scanline[i]; + break; + case 3: + if (precon) { + for (size_t i = 0; i < bytewidth; i++) + recon[i] = scanline[i] + precon[i] / 2; + for (size_t i = bytewidth; i < length; i++) + recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2); + } else { + for (size_t i = 0; i < bytewidth; i++) + recon[i] = scanline[i]; + for (size_t i = bytewidth; i < length; i++) + recon[i] = scanline[i] + recon[i - bytewidth] / 2; + } + break; + case 4: + if (precon) { + for (size_t i = 0; i < bytewidth; i++) + recon[i] = scanline[i] + paethPredictor(0, precon[i], 0); + for (size_t i = bytewidth; i < length; i++) + recon[i] = scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]); + } else { + for (size_t i = 0; i < bytewidth; i++) + recon[i] = scanline[i]; + for (size_t i = bytewidth; i < length; i++) + recon[i] = scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0); + } + break; + default: + error = 36; + return; // error: unexisting filter type given + } + } + static unsigned long readBitFromReversedStream(size_t& bitp, const unsigned char* bits) { + unsigned long result = (bits[bitp >> 3] >> (7 - (bitp & 0x7))) & 1; + bitp++; + return result; + } + static unsigned long readBitsFromReversedStream(size_t& bitp, const unsigned char* bits, unsigned long nbits) { + unsigned long result = 0; + for (size_t i = nbits - 1; i < nbits; i--) + result += ((readBitFromReversedStream(bitp, bits)) << i); + return result; + } + void setBitOfReversedStream(size_t& bitp, unsigned char* bits, unsigned long bit) { + bits[bitp >> 3] |= (bit << (7 - (bitp & 0x7))); + bitp++; + } + unsigned long read32bitInt(const unsigned char* buffer) { return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; } + int checkColorValidity(unsigned long colorType, unsigned long bd) // return type is a LodePNG error code + { + if ((colorType == 2 || colorType == 4 || colorType == 6)) { + if (!(bd == 8 || bd == 16)) + return 37; + else + return 0; + } else if (colorType == 0) { + if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) + return 37; + else + return 0; + } else if (colorType == 3) { + if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8)) + return 37; + else + return 0; + } else + return 31; // unexisting color type + } + unsigned long getBpp(const Info& info) { + if (info.colorType == 2) + return (3 * info.bitDepth); + else if (info.colorType >= 4) + return (info.colorType - 2) * info.bitDepth; + else + return info.bitDepth; + } + int convert(unsigned char*& out, const unsigned char* in, Info& infoIn, unsigned long w, unsigned long h) { // converts from any color type to 32-bit. return value = LodePNG error code + size_t numpixels = w * h, bp = 0; + out = new unsigned char[numpixels * 4 + 1]; + if (infoIn.bitDepth == 8 && infoIn.colorType == 0) // greyscale + for (size_t i = 0; i < numpixels; i++) { + out[4 * i + 0] = out[4 * i + 1] = out[4 * i + 2] = in[i]; + out[4 * i + 3] = (infoIn.key_defined && in[i] == infoIn.key_r) ? 0 : 255; + } + else if (infoIn.bitDepth == 8 && infoIn.colorType == 2) // RGB color + for (size_t i = 0; i < numpixels; i++) { + for (size_t c = 0; c < 3; c++) + out[4 * i + c] = in[3 * i + c]; + out[4 * i + 3] = (infoIn.key_defined == 1 && in[3 * i + 0] == infoIn.key_r && in[3 * i + 1] == infoIn.key_g && in[3 * i + 2] == infoIn.key_b) ? 0 : 255; + } + else if (infoIn.bitDepth == 8 && infoIn.colorType == 3) // indexed color (palette) + for (size_t i = 0; i < numpixels; i++) { + if (4U * in[i] >= infoIn.palette.size()) + return 46; + for (size_t c = 0; c < 4; c++) + out[4 * i + c] = infoIn.palette[4 * in[i] + c]; // get rgb colors from the palette + } + else if (infoIn.bitDepth == 8 && infoIn.colorType == 4) // greyscale with alpha + for (size_t i = 0; i < numpixels; i++) { + out[4 * i + 0] = out[4 * i + 1] = out[4 * i + 2] = in[2 * i + 0]; + out[4 * i + 3] = in[2 * i + 1]; + } + else if (infoIn.bitDepth == 8 && infoIn.colorType == 6) + for (size_t i = 0; i < numpixels; i++) + for (size_t c = 0; c < 4; c++) + out[4 * i + c] = in[4 * i + c]; // RGB with alpha + else if (infoIn.bitDepth == 16 && infoIn.colorType == 0) // greyscale + for (size_t i = 0; i < numpixels; i++) { + out[4 * i + 0] = out[4 * i + 1] = out[4 * i + 2] = in[2 * i]; + out[4 * i + 3] = (infoIn.key_defined && 256U * in[i] + in[i + 1] == infoIn.key_r) ? 0 : 255; + } + else if (infoIn.bitDepth == 16 && infoIn.colorType == 2) // RGB color + for (size_t i = 0; i < numpixels; i++) { + for (size_t c = 0; c < 3; c++) + out[4 * i + c] = in[6 * i + 2 * c]; + out[4 * i + 3] = (infoIn.key_defined && 256U * in[6 * i + 0] + in[6 * i + 1] == infoIn.key_r && 256U * in[6 * i + 2] + in[6 * i + 3] == infoIn.key_g && 256U * in[6 * i + 4] + in[6 * i + 5] == infoIn.key_b) ? 0 : 255; + } + else if (infoIn.bitDepth == 16 && infoIn.colorType == 4) // greyscale with alpha + for (size_t i = 0; i < numpixels; i++) { + out[4 * i + 0] = out[4 * i + 1] = out[4 * i + 2] = in[4 * i]; // most significant byte + out[4 * i + 3] = in[4 * i + 2]; + } + else if (infoIn.bitDepth == 16 && infoIn.colorType == 6) + for (size_t i = 0; i < numpixels; i++) + for (size_t c = 0; c < 4; c++) + out[4 * i + c] = in[8 * i + 2 * c]; // RGB with alpha + else if (infoIn.bitDepth < 8 && infoIn.colorType == 0) // greyscale + for (size_t i = 0; i < numpixels; i++) { + unsigned long value = (readBitsFromReversedStream(bp, in, infoIn.bitDepth) * 255) / ((1 << infoIn.bitDepth) - 1); // scale value from 0 to 255 + out[4 * i + 0] = out[4 * i + 1] = out[4 * i + 2] = (unsigned char)(value); + out[4 * i + 3] = (infoIn.key_defined && value && ((1U << infoIn.bitDepth) - 1U) == infoIn.key_r && ((1U << infoIn.bitDepth) - 1U)) ? 0 : 255; + } + else if (infoIn.bitDepth < 8 && infoIn.colorType == 3) // palette + for (size_t i = 0; i < numpixels; i++) { + unsigned long value = readBitsFromReversedStream(bp, in, infoIn.bitDepth); + if (4 * value >= infoIn.palette.size()) + return 47; + for (size_t c = 0; c < 4; c++) + out[4 * i + c] = infoIn.palette[4 * value + c]; // get rgb colors from the palette + } + return 0; + } + unsigned char paethPredictor(short a, short b, short c) // Paeth predicter, used by PNG filter type 4 + { + short p = a + b - c, pa = p > a ? (p - a) : (a - p), pb = p > b ? (p - b) : (b - p), pc = p > c ? (p - c) : (c - p); + return (unsigned char)((pa <= pb && pa <= pc) ? a : pb <= pc ? b + : c); } - } - void inflateNoCompression(std::vector& out, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength) - { - while((bp & 0x7) != 0) bp++; //go to first boundary of byte - size_t p = bp / 8; - if(p >= inlength - 4) { error = 52; return; } //error, bit pointer will jump past memory - unsigned long LEN = in[p] + 256 * in[p + 1], NLEN = in[p + 2] + 256 * in[p + 3]; p += 4; - if(LEN + NLEN != 65535) { error = 21; return; } //error: NLEN is not one's complement of LEN - if(pos + LEN >= out.size()) out.resize(pos + LEN); - if(p + LEN > inlength) { error = 23; return; } //error: reading outside of in buffer - for(unsigned long n = 0; n < LEN; n++) out[pos++] = in[p++]; //read LEN bytes of literal data - bp = p * 8; - } }; - int decompress(std::vector& out, const std::vector& in) //returns error value - { - Inflator inflator; - if(in.size() < 2) { return 53; } //error, size of zlib data too small - if((in[0] * 256 + in[1]) % 31 != 0) { return 24; } //error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way - unsigned long CM = in[0] & 15, CINFO = (in[0] >> 4) & 15, FDICT = (in[1] >> 5) & 1; - if(CM != 8 || CINFO > 7) { return 25; } //error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec - if(FDICT != 0) { return 26; } //error: the specification of PNG says about the zlib stream: "The additional flags shall not specify a preset dictionary." - inflator.inflate(out, in, 2); - return inflator.error; //note: adler32 checksum was skipped and ignored - } - }; - struct PNG //nested functions for PNG decoding - { - struct Info - { - unsigned long width, height, colorType, bitDepth, compressionMethod, filterMethod, interlaceMethod, key_r, key_g, key_b; - bool key_defined; //is a transparent color key given? - std::vector palette; - } info; - int error; - void decode(unsigned char* &out, const unsigned char* in, size_t size, bool convert_to_rgba32) - { - error = 0; - if(size == 0 || in == 0) { error = 48; return; } //the given data is empty - readPngHeader(&in[0], size); if(error) return; - size_t pos = 33; //first byte of the first chunk after the header - std::vector idat; //the data from idat chunks - bool IEND = false, known_type = true; - info.key_defined = false; - while(!IEND) //loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer - { - if(pos + 8 >= size) { error = 30; return; } //error: size of the in buffer too small to contain next chunk - size_t chunkLength = read32bitInt(&in[pos]); pos += 4; - if(chunkLength > 2147483647) { error = 63; return; } - if(pos + chunkLength >= size) { error = 35; return; } //error: size of the in buffer too small to contain next chunk - if(in[pos + 0] == 'I' && in[pos + 1] == 'D' && in[pos + 2] == 'A' && in[pos + 3] == 'T') //IDAT chunk, containing compressed image data - { - idat.insert(idat.end(), &in[pos + 4], &in[pos + 4 + chunkLength]); - pos += (4 + chunkLength); - } - else if(in[pos + 0] == 'I' && in[pos + 1] == 'E' && in[pos + 2] == 'N' && in[pos + 3] == 'D') { pos += 4; IEND = true; } - else if(in[pos + 0] == 'P' && in[pos + 1] == 'L' && in[pos + 2] == 'T' && in[pos + 3] == 'E') //palette chunk (PLTE) - { - pos += 4; //go after the 4 letters - info.palette.resize(4 * (chunkLength / 3)); - if(info.palette.size() > (4 * 256)) { error = 38; return; } //error: palette too big - for(size_t i = 0; i < info.palette.size(); i += 4) - { - for(size_t j = 0; j < 3; j++) info.palette[i + j] = in[pos++]; //RGB - info.palette[i + 3] = 255; //alpha - } - } - else if(in[pos + 0] == 't' && in[pos + 1] == 'R' && in[pos + 2] == 'N' && in[pos + 3] == 'S') //palette transparency chunk (tRNS) - { - pos += 4; //go after the 4 letters - if(info.colorType == 3) - { - if(4 * chunkLength > info.palette.size()) { error = 39; return; } //error: more alpha values given than there are palette entries - for(size_t i = 0; i < chunkLength; i++) info.palette[4 * i + 3] = in[pos++]; - } - else if(info.colorType == 0) - { - if(chunkLength != 2) { error = 40; return; } //error: this chunk must be 2 bytes for greyscale image - info.key_defined = 1; info.key_r = info.key_g = info.key_b = 256 * in[pos] + in[pos + 1]; pos += 2; - } - else if(info.colorType == 2) - { - if(chunkLength != 6) { error = 41; return; } //error: this chunk must be 6 bytes for RGB image - info.key_defined = 1; - info.key_r = 256 * in[pos] + in[pos + 1]; pos += 2; - info.key_g = 256 * in[pos] + in[pos + 1]; pos += 2; - info.key_b = 256 * in[pos] + in[pos + 1]; pos += 2; - } - else { error = 42; return; } //error: tRNS chunk not allowed for other color models - } - else //it's not an implemented chunk type, so ignore it: skip over the data - { - if(!(in[pos + 0] & 32)) { error = 69; return; } //error: unknown critical chunk (5th bit of first byte of chunk type is 0) - pos += (chunkLength + 4); //skip 4 letters and uninterpreted data of unimplemented chunk - known_type = false; - } - pos += 4; //step over CRC (which is ignored) - } - unsigned long bpp = getBpp(info); - std::vector scanlines(((info.width * (info.height * bpp + 7)) / 8) + info.height); //now the out buffer will be filled - Zlib zlib; //decompress with the Zlib decompressor - error = zlib.decompress(scanlines, idat); if(error) return; //stop if the zlib decompressor returned an error - size_t bytewidth = (bpp + 7) / 8, outlength = (info.height * info.width * bpp + 7) / 8; - out = new unsigned char[outlength + 1]; //time to fill the out buffer - if(info.interlaceMethod == 0) //no interlace, just filter - { - size_t linestart = 0, linelength = (info.width * bpp + 7) / 8; //length in bytes of a scanline, excluding the filtertype byte - if(bpp >= 8) //byte per byte - for(unsigned long y = 0; y < info.height; y++) - { - unsigned long filterType = scanlines[linestart]; - const unsigned char* prevline = (y == 0) ? 0 : &out[(y - 1) * info.width * bytewidth]; - unFilterScanline(&out[linestart - y], &scanlines[linestart + 1], prevline, bytewidth, filterType, linelength); if(error) return; - linestart += (1 + linelength); //go to start of next scanline - } - else //less than 8 bits per pixel, so fill it up bit per bit - { - unsigned char* templine = new unsigned char[((info.width * bpp + 7) >> 3) + 1]; //only used if bpp < 8 - for(size_t y = 0, obp = 0; y < info.height; y++) - { - unsigned long filterType = scanlines[linestart]; - const unsigned char* prevline = (y == 0) ? 0 : &out[(y - 1) * info.width * bytewidth]; - unFilterScanline(templine, &scanlines[linestart + 1], prevline, bytewidth, filterType, linelength); if(error) return; - for(size_t bp = 0; bp < info.width * bpp;) setBitOfReversedStream(obp, out, readBitFromReversedStream(bp, &templine[0])); - linestart += (1 + linelength); //go to start of next scanline - } - delete[] templine; - } - } - if(convert_to_rgba32 && (info.colorType != 6 || info.bitDepth != 8)) //conversion needed - { - auto data = out; - error = convert(out, data, info, info.width, info.height); - } - } - void readPngHeader(const unsigned char* in, size_t inlength) //read the information from the header and store it in the Info - { - if(inlength < 29) { error = 27; return; } //error: the data length is smaller than the length of the header - if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { error = 28; return; } //no PNG signature - if(in[12] != 'I' || in[13] != 'H' || in[14] != 'D' || in[15] != 'R') { error = 29; return; } //error: it doesn't start with a IHDR chunk! - info.width = read32bitInt(&in[16]); info.height = read32bitInt(&in[20]); - info.bitDepth = in[24]; info.colorType = in[25]; - info.compressionMethod = in[26]; if(in[26] != 0) { error = 32; return; } //error: only compression method 0 is allowed in the specification - info.filterMethod = in[27]; if(in[27] != 0) { error = 33; return; } //error: only filter method 0 is allowed in the specification - info.interlaceMethod = in[28]; if(in[28] > 1) { error = 34; return; } //error: only interlace methods 0 and 1 exist in the specification - error = checkColorValidity(info.colorType, info.bitDepth); - } - void unFilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned long filterType, size_t length) - { - switch(filterType) - { - case 0: for(size_t i = 0; i < length; i++) recon[i] = scanline[i]; break; - case 1: - for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; - for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth]; - break; - case 2: - if(precon) for(size_t i = 0; i < length; i++) recon[i] = scanline[i] + precon[i]; - else for(size_t i = 0; i < length; i++) recon[i] = scanline[i]; - break; - case 3: - if(precon) - { - for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2; - for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2); - } - else - { - for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; - for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2; - } - break; - case 4: - if(precon) - { - for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i] + paethPredictor(0, precon[i], 0); - for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]); - } - else - { - for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; - for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0); - } - break; - default: error = 36; return; //error: unexisting filter type given - } - } - static unsigned long readBitFromReversedStream(size_t& bitp, const unsigned char* bits) { unsigned long result = (bits[bitp >> 3] >> (7 - (bitp & 0x7))) & 1; bitp++; return result;} - static unsigned long readBitsFromReversedStream(size_t& bitp, const unsigned char* bits, unsigned long nbits) - { - unsigned long result = 0; - for(size_t i = nbits - 1; i < nbits; i--) result += ((readBitFromReversedStream(bitp, bits)) << i); - return result; - } - void setBitOfReversedStream(size_t& bitp, unsigned char* bits, unsigned long bit) { bits[bitp >> 3] |= (bit << (7 - (bitp & 0x7))); bitp++; } - unsigned long read32bitInt(const unsigned char* buffer) { return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; } - int checkColorValidity(unsigned long colorType, unsigned long bd) //return type is a LodePNG error code - { - if((colorType == 2 || colorType == 4 || colorType == 6)) { if(!(bd == 8 || bd == 16)) return 37; else return 0; } - else if(colorType == 0) { if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; else return 0; } - else if(colorType == 3) { if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; else return 0; } - else return 31; //unexisting color type - } - unsigned long getBpp(const Info& info) - { - if(info.colorType == 2) return (3 * info.bitDepth); - else if(info.colorType >= 4) return (info.colorType - 2) * info.bitDepth; - else return info.bitDepth; - } - int convert(unsigned char* &out, const unsigned char* in, Info& infoIn, unsigned long w, unsigned long h) - { //converts from any color type to 32-bit. return value = LodePNG error code - size_t numpixels = w * h, bp = 0; - out = new unsigned char[numpixels * 4 + 1]; - if(infoIn.bitDepth == 8 && infoIn.colorType == 0) //greyscale - for(size_t i = 0; i < numpixels; i++) - { - out[4 * i + 0] = out[4 * i + 1] = out[4 * i + 2] = in[i]; - out[4 * i + 3] = (infoIn.key_defined && in[i] == infoIn.key_r) ? 0 : 255; - } - else if(infoIn.bitDepth == 8 && infoIn.colorType == 2) //RGB color - for(size_t i = 0; i < numpixels; i++) - { - for(size_t c = 0; c < 3; c++) out[4 * i + c] = in[3 * i + c]; - out[4 * i + 3] = (infoIn.key_defined == 1 && in[3 * i + 0] == infoIn.key_r && in[3 * i + 1] == infoIn.key_g && in[3 * i + 2] == infoIn.key_b) ? 0 : 255; - } - else if(infoIn.bitDepth == 8 && infoIn.colorType == 3) //indexed color (palette) - for(size_t i = 0; i < numpixels; i++) - { - if(4U * in[i] >= infoIn.palette.size()) return 46; - for(size_t c = 0; c < 4; c++) out[4 * i + c] = infoIn.palette[4 * in[i] + c]; //get rgb colors from the palette - } - else if(infoIn.bitDepth == 8 && infoIn.colorType == 4) //greyscale with alpha - for(size_t i = 0; i < numpixels; i++) - { - out[4 * i + 0] = out[4 * i + 1] = out[4 * i + 2] = in[2 * i + 0]; - out[4 * i + 3] = in[2 * i + 1]; - } - else if(infoIn.bitDepth == 8 && infoIn.colorType == 6) for(size_t i = 0; i < numpixels; i++) for(size_t c = 0; c < 4; c++) out[4 * i + c] = in[4 * i + c]; //RGB with alpha - else if(infoIn.bitDepth == 16 && infoIn.colorType == 0) //greyscale - for(size_t i = 0; i < numpixels; i++) - { - out[4 * i + 0] = out[4 * i + 1] = out[4 * i + 2] = in[2 * i]; - out[4 * i + 3] = (infoIn.key_defined && 256U * in[i] + in[i + 1] == infoIn.key_r) ? 0 : 255; - } - else if(infoIn.bitDepth == 16 && infoIn.colorType == 2) //RGB color - for(size_t i = 0; i < numpixels; i++) - { - for(size_t c = 0; c < 3; c++) out[4 * i + c] = in[6 * i + 2 * c]; - out[4 * i + 3] = (infoIn.key_defined && 256U*in[6*i+0]+in[6*i+1] == infoIn.key_r && 256U*in[6*i+2]+in[6*i+3] == infoIn.key_g && 256U*in[6*i+4]+in[6*i+5] == infoIn.key_b) ? 0 : 255; - } - else if(infoIn.bitDepth == 16 && infoIn.colorType == 4) //greyscale with alpha - for(size_t i = 0; i < numpixels; i++) - { - out[4 * i + 0] = out[4 * i + 1] = out[4 * i + 2] = in[4 * i]; //most significant byte - out[4 * i + 3] = in[4 * i + 2]; - } - else if(infoIn.bitDepth == 16 && infoIn.colorType == 6) for(size_t i = 0; i < numpixels; i++) for(size_t c = 0; c < 4; c++) out[4 * i + c] = in[8 * i + 2 * c]; //RGB with alpha - else if(infoIn.bitDepth < 8 && infoIn.colorType == 0) //greyscale - for(size_t i = 0; i < numpixels; i++) - { - unsigned long value = (readBitsFromReversedStream(bp, in, infoIn.bitDepth) * 255) / ((1 << infoIn.bitDepth) - 1); //scale value from 0 to 255 - out[4 * i + 0] = out[4 * i + 1] = out[4 * i + 2] = (unsigned char)(value); - out[4 * i + 3] = (infoIn.key_defined && value && ((1U << infoIn.bitDepth) - 1U) == infoIn.key_r && ((1U << infoIn.bitDepth) - 1U)) ? 0 : 255; - } - else if(infoIn.bitDepth < 8 && infoIn.colorType == 3) //palette - for(size_t i = 0; i < numpixels; i++) - { - unsigned long value = readBitsFromReversedStream(bp, in, infoIn.bitDepth); - if(4 * value >= infoIn.palette.size()) return 47; - for(size_t c = 0; c < 4; c++) out[4 * i + c] = infoIn.palette[4 * value + c]; //get rgb colors from the palette - } - return 0; - } - unsigned char paethPredictor(short a, short b, short c) //Paeth predicter, used by PNG filter type 4 - { - short p = a + b - c, pa = p > a ? (p - a) : (a - p), pb = p > b ? (p - b) : (b - p), pc = p > c ? (p - c) : (c - p); - return (unsigned char)((pa <= pb && pa <= pc) ? a : pb <= pc ? b : c); - } - }; - PNG decoder; decoder.decode(out_image, in_png, in_size, convert_to_rgba32); - image_width = decoder.info.width; image_height = decoder.info.height; - return decoder.error; + PNG decoder; + decoder.decode(out_image, in_png, in_size, convert_to_rgba32); + image_width = decoder.info.width; + image_height = decoder.info.height; + return decoder.error; } diff --git a/src/texture.cpp b/src/texture.cpp index fb1de04..d80ad38 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -3,27 +3,25 @@ Copyright (c) 2018 nullworks. All rights reserved. */ -#include -#include #include -#include -#include -#include #include +#include +#include +#include +#include +#include #include // required to load the file -namespace glez -{ +namespace glez { -void texture::bind() -{ +void texture::bind() { if (!bound) { glGenTextures(1, &id); glBindTexture(GL_TEXTURE_2D, id); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, data); + GL_RGBA, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -33,7 +31,7 @@ void texture::bind() glez::bind(id); } -texture texture::loadFromFile(const std::string &path) { +texture texture::loadFromFile(const std::string& path) { std::ifstream file(path.c_str(), std::ios::in | std::ios::binary | std::ios::ate); std::streamsize size = 0; @@ -45,8 +43,8 @@ texture texture::loadFromFile(const std::string &path) { if (size < 1) throw std::runtime_error("Unable to load texture from file!"); - unsigned char *buffer = new unsigned char[(size_t) size + 1]; - file.read((char *) buffer, size); + unsigned char* buffer = new unsigned char[(size_t)size + 1]; + file.read((char*)buffer, size); file.close(); texture ret; int error = decodePNG(ret.data, ret.width, ret.height, buffer, size); @@ -56,13 +54,12 @@ texture texture::loadFromFile(const std::string &path) { printf("Error loading texture, error code %i\n", error); throw std::runtime_error("See above!"); } - ret.init = true; + ret.init = true; ret.bound = false; - ret.id = 0; + ret.id = 0; return ret; } - texture texture::loadFromMemory(const std::byte* mem, std::size_t size, unsigned w, unsigned h) { if (size < 1) throw std::runtime_error("Unable to load texture from memory!"); @@ -74,9 +71,9 @@ texture texture::loadFromMemory(const std::byte* mem, std::size_t size, unsigned ret.width = w; ret.height = h; ret.data = buffer; - ret.init = true; + ret.init = true; ret.bound = false; - ret.id = 0; + ret.id = 0; return ret; } @@ -89,20 +86,20 @@ texture::~texture() { } texture& texture::operator=(texture&& var) { - this->init = var.init; - this->bound = var.bound; - this->width = var.width; - this->height = var.height; - this->id = var.id; - this->data = var.data; + this->init = var.init; + this->bound = var.bound; + this->width = var.width; + this->height = var.height; + this->id = var.id; + this->data = var.data; - var.init = false; - var.bound = false; - var.width = 0; - var.height = 0; - var.id = 0; - var.data = nullptr; - return *this; + var.init = false; + var.bound = false; + var.width = 0; + var.height = 0; + var.id = 0; + var.data = nullptr; + return *this; } -} // namespace glez::detail::texture +} // namespace glez