Reformatted all files

This commit is contained in:
Rebekah 2022-04-04 16:56:17 -04:00
parent 8c29b72961
commit 72d8bf1644
Signed by: oneechanhax
GPG Key ID: 183EB7902964DAE5
15 changed files with 856 additions and 748 deletions

View File

@ -1,84 +0,0 @@
#!/bin/bash
#
# clang-format-all: a tool to run clang-format on an entire project
# Copyright (C) 2016 Evan Klitzke <evan@eklitzke.org>
#
# 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 <http://www.gnu.org/licenses/>.
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

View File

@ -10,11 +10,15 @@ namespace glez {
struct rgba { struct rgba {
rgba() = default; rgba() = default;
inline constexpr rgba(int r, int g, int b) 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) 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 r;
float g; float g;

View File

@ -4,21 +4,19 @@
#pragma once #pragma once
#include <vector>
#include <cstdint> #include <cstdint>
#include <glez/texture.hpp> #include <freetype-gl.h>
#include <glez/font.hpp> #include <glez/font.hpp>
#include <glez/glez.hpp> #include <glez/glez.hpp>
#include <glez/texture.hpp>
#include <vector>
#include <vertex-buffer.h> #include <vertex-buffer.h>
#include <glez/font.hpp>
#include <freetype-gl.h>
namespace glez::detail::record { namespace glez::detail::record {
class RecordedCommands { class RecordedCommands {
public: public:
struct segment struct segment {
{
std::size_t start { 0 }; std::size_t start { 0 };
std::size_t size { 0 }; std::size_t size { 0 };
glez::texture* texture { nullptr }; glez::texture* texture { nullptr };

View File

@ -5,10 +5,10 @@
#pragma once #pragma once
#include <string>
#include "color.hpp" #include "color.hpp"
#include "font.hpp" #include "font.hpp"
#include "texture.hpp" #include "texture.hpp"
#include <string>
namespace glez::draw { namespace glez::draw {

View File

@ -23,9 +23,10 @@ public:
// 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); 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: public:
texture_font_t* m_font = nullptr; texture_font_t* m_font = nullptr;
texture_atlas_t* atlas = nullptr; texture_atlas_t* atlas = nullptr;
}; };
} // namespace glez::detail::font } // namespace glez

View File

@ -5,9 +5,9 @@
#pragma once #pragma once
#include <vertex-buffer.h>
#include <freetype-gl.h> #include <freetype-gl.h>
#include <glez/color.hpp> #include <glez/color.hpp>
#include <vertex-buffer.h>
namespace glez { namespace glez {
@ -33,4 +33,4 @@ struct vertex {
void bind(GLuint texture); void bind(GLuint texture);
}; // namespace glez::detail::program }; // namespace glez

View File

@ -4,16 +4,13 @@
#pragma once #pragma once
namespace glez::detail::record namespace glez::detail::record {
{
class RecordedCommands; class RecordedCommands;
} }
namespace glez::record namespace glez::record {
{
class Record class Record {
{
public: public:
Record(); Record();
~Record(); ~Record();

View File

@ -5,13 +5,12 @@
#pragma once #pragma once
#include <freetype-gl.h>
#include <string>
#include <limits>
#include <cstddef> #include <cstddef>
#include <freetype-gl.h>
#include <limits>
#include <string>
namespace glez namespace glez {
{
class texture { class texture {
public: public:
@ -26,6 +25,7 @@ public:
void bind(); void bind();
inline bool isLoaded() { return this->init; } inline bool isLoaded() { return this->init; }
public: public:
bool init = false; bool init = false;
bool bound = false; bool bound = false;
@ -36,4 +36,4 @@ public:
GLubyte* data; GLubyte* data;
}; };
} // namespace glez::texture } // namespace glez

View File

@ -3,23 +3,20 @@
*/ */
#include <cstddef> #include <cstddef>
#include <glez/detail/record.hpp>
#include <glez/record.hpp>
#include <glez/glez.hpp>
#include <cstring> #include <cstring>
#include <glez/detail/record.hpp>
#include <glez/glez.hpp>
#include <glez/record.hpp>
namespace glez::detail::record namespace glez::detail::record {
{
void RecordedCommands::render() void RecordedCommands::render() {
{
isReplaying = true; isReplaying = true;
vertex_buffer_render_setup(vertex_buffer, GL_TRIANGLES); vertex_buffer_render_setup(vertex_buffer, GL_TRIANGLES);
for (const auto& i : segments) { for (const auto& i : segments) {
if (i.texture) { if (i.texture) {
i.texture->bind(); i.texture->bind();
} } else if (i.font) {
else if (i.font) {
if (i.font->atlas->id == 0) { if (i.font->atlas->id == 0) {
glGenTextures(1, &i.font->atlas->id); glGenTextures(1, &i.font->atlas->id);
} }
@ -68,17 +65,14 @@ void RecordedCommands::bindTexture(glez::texture *tx) {
} }
} }
void RecordedCommands::bindFont(glez::font *font) void RecordedCommands::bindFont(glez::font* font) {
{ if (current.font != font) {
if (current.font != font)
{
cutSegment(); cutSegment();
current.font = font; current.font = font;
} }
} }
void RecordedCommands::cutSegment() void RecordedCommands::cutSegment() {
{
current.size = vertex_buffer->indices->size - current.start; current.size = vertex_buffer->indices->size - current.start;
if (current.size) if (current.size)
segments.push_back(current); segments.push_back(current);
@ -86,8 +80,7 @@ void RecordedCommands::cutSegment()
current.start = vertex_buffer->indices->size; current.start = vertex_buffer->indices->size;
} }
void RecordedCommands::end() void RecordedCommands::end() {
{
cutSegment(); cutSegment();
} }
@ -96,29 +89,24 @@ bool isReplaying{ false };
} // namespace glez::detail::record } // namespace glez::detail::record
glez::record::Record::Record() glez::record::Record::Record() {
{
commands = new glez::detail::record::RecordedCommands {}; commands = new glez::detail::record::RecordedCommands {};
} }
glez::record::Record::~Record() glez::record::Record::~Record() {
{
delete commands; delete commands;
} }
void glez::record::Record::begin() void glez::record::Record::begin() {
{
detail::record::currentRecord = commands; detail::record::currentRecord = commands;
commands->reset(); commands->reset();
} }
void glez::record::Record::end() void glez::record::Record::end() {
{
commands->end(); commands->end();
detail::record::currentRecord = nullptr; detail::record::currentRecord = nullptr;
} }
void glez::record::Record::replay() void glez::record::Record::replay() {
{
commands->render(); commands->render();
} }

View File

@ -4,18 +4,17 @@
*/ */
#include <cassert> #include <cassert>
#include <cmath>
#include <cstring>
#include <glez/detail/record.hpp>
#include <glez/detail/render.hpp>
#include <glez/draw.hpp> #include <glez/draw.hpp>
#include <glez/font.hpp> #include <glez/font.hpp>
#include <glez/detail/render.hpp>
#include <glez/glez.hpp> #include <glez/glez.hpp>
#include <vertex-buffer.h>
#include <cstring>
#include <glez/texture.hpp> #include <glez/texture.hpp>
#include <cmath> #include <vertex-buffer.h>
#include <glez/detail/record.hpp>
namespace indices namespace indices {
{
static GLuint rectangle[6] = { 0, 1, 2, 2, 3, 0 }; static GLuint rectangle[6] = { 0, 1, 2, 2, 3, 0 };
static GLuint triangle[3] = { 0, 1, 2 }; static GLuint triangle[3] = { 0, 1, 2 };

View File

@ -3,10 +3,10 @@
Copyright (c) 2018 nullworks. All rights reserved. Copyright (c) 2018 nullworks. All rights reserved.
*/ */
#include <glez/font.hpp>
#include <vector>
#include <memory>
#include <cassert> #include <cassert>
#include <glez/font.hpp>
#include <memory>
#include <vector>
namespace glez { namespace glez {
@ -46,8 +46,7 @@ void font::stringSize(const std::string& string, float* width, float* height) {
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 // 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) if (glyph == nullptr)
@ -68,4 +67,4 @@ void font::stringSize(const std::string& string, float* width, float* height) {
*height = size_y; *height = size_y;
} }
} // namespace glez::detail::font } // namespace glez

View File

@ -3,18 +3,17 @@
Copyright (c) 2018 nullworks. All rights reserved. Copyright (c) 2018 nullworks. All rights reserved.
*/ */
#include <freetype-gl.h>
#include <vertex-buffer.h>
#include <mat4.h>
#include <glez/glez.hpp>
#include <cstdio>
#include <stdexcept>
#include <cassert> #include <cassert>
#include <cstdio>
#include <cstring>
#include <freetype-gl.h>
#include <glez/detail/record.hpp>
#include <glez/detail/render.hpp> #include <glez/detail/render.hpp>
#include <glez/font.hpp> #include <glez/font.hpp>
#include <cstring>
#include <glez/glez.hpp> #include <glez/glez.hpp>
#include <glez/detail/record.hpp> #include <mat4.h>
#include <stdexcept>
#include <vertex-buffer.h>
static const char* shader_vertex = R"END( static const char* shader_vertex = R"END(
#version 130 #version 130
@ -70,8 +69,7 @@ static GLuint compile(const char *source, GLenum type) {
glCompileShader(result); glCompileShader(result);
glGetShaderiv(result, GL_COMPILE_STATUS, &status); glGetShaderiv(result, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) if (status != GL_TRUE) {
{
char error[512]; char error[512];
GLsizei length; GLsizei length;
glGetShaderInfoLog(result, 512, &length, error); glGetShaderInfoLog(result, 512, &length, error);
@ -113,8 +111,7 @@ void resize(int width, int height) {
glUseProgram(0); 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"); 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)); shader = link(compile(shader_vertex, GL_VERTEX_SHADER), compile(shader_fragment, GL_FRAGMENT_SHADER));
@ -132,7 +129,6 @@ void shutdown() {
static GLuint current_texture { 0 }; static GLuint current_texture { 0 };
void begin() { void begin() {
glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT); glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT);
@ -183,4 +179,4 @@ void bind(GLuint texture) {
} }
} }
} } // namespace glez

View File

@ -1,5 +1,5 @@
#include <vector>
#include <stddef.h> #include <stddef.h>
#include <vector>
/* /*
decodePNG: The picoPNG function, decodes a PNG file buffer in memory, into a raw pixel buffer. 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. out_image: output parameter, this will contain the raw pixels after decoding.
@ -20,8 +20,7 @@ convert_to_rgba32: optional parameter, true by default.
works for trusted PNG files. Use LodePNG instead of picoPNG if you need this information. 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. 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) 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 // picoPNG version 20101224
// Copyright (c) 2005-2010 Lode Vandevenne // Copyright (c) 2005-2010 Lode Vandevenne
// //
@ -55,41 +54,55 @@ int decodePNG(unsigned char* &out_image, int& image_width, int& image_height, co
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 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 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 readBitFromStream(size_t& bitp, const unsigned char* bits) {
static unsigned long readBitsFromStream(size_t& bitp, const unsigned char* bits, size_t nbits) unsigned long result = (bits[bitp >> 3] >> (bitp & 0x7)) & 1;
{ bitp++;
unsigned long result = 0;
for(size_t i = 0; i < nbits; i++) result += (readBitFromStream(bitp, bits)) << i;
return result; return result;
} }
struct HuffmanTree static unsigned long readBitsFromStream(size_t& bitp, const unsigned char* bits, size_t nbits) {
{ unsigned long result = 0;
int makeFromLengths(const std::vector<unsigned long>& bitlen, unsigned long maxbitlen) for (size_t i = 0; i < nbits; i++)
{ //make tree given the lengths result += (readBitFromStream(bitp, bits)) << i;
return result;
}
struct HuffmanTree {
int makeFromLengths(const std::vector<unsigned long>& bitlen, unsigned long maxbitlen) { // make tree given the lengths
unsigned long numcodes = (unsigned long)(bitlen.size()), treepos = 0, nodefilled = 0; unsigned long numcodes = (unsigned long)(bitlen.size()), treepos = 0, nodefilled = 0;
std::vector<unsigned long> tree1d(numcodes), blcount(maxbitlen + 1, 0), nextcode(maxbitlen + 1, 0); std::vector<unsigned long> 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 = 0; bits < numcodes; bits++)
for(unsigned long bits = 1; bits <= maxbitlen; bits++) nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1; blcount[bitlen[bits]]++; // count number of instances of each code length
for(unsigned long n = 0; n < numcodes; n++) if(bitlen[n] != 0) tree1d[n] = nextcode[bitlen[n]]++; //generate all the codes for (unsigned long bits = 1; bits <= maxbitlen; bits++)
tree2d.clear(); tree2d.resize(numcodes * 2, 32767); //32767 here means the tree2d isn't filled there yet 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 n = 0; n < numcodes; n++) // the codes
for (unsigned long i = 0; i < bitlen[n]; i++) // the bits for this code for (unsigned long i = 0; i < bitlen[n]; i++) // the bits for this code
{ {
unsigned long bit = (tree1d[n] >> (bitlen[n] - i - 1)) & 1; unsigned long bit = (tree1d[n] >> (bitlen[n] - i - 1)) & 1;
if(treepos > numcodes - 2) return 55; if (treepos > numcodes - 2)
return 55;
if (tree2d[2 * treepos + bit] == 32767) // not yet filled in if (tree2d[2 * treepos + bit] == 32767) // not yet filled in
{ {
if(i + 1 == bitlen[n]) { tree2d[2 * treepos + bit] = n; treepos = 0; } //last bit if (i + 1 == bitlen[n]) {
else { tree2d[2 * treepos + bit] = ++nodefilled + numcodes; treepos = nodefilled; } //addresses are encoded as values > numcodes tree2d[2 * treepos + bit] = n;
} treepos = 0;
else treepos = tree2d[2 * treepos + bit] - numcodes; //subtract numcodes from address to get address value } // 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; return 0;
} }
int decode(bool& decoded, unsigned long& result, size_t& treepos, unsigned long bit) const int decode(bool& decoded, unsigned long& result, size_t& treepos, unsigned long bit) const { // Decodes a symbol from the tree
{ //Decodes a symbol from the tree
unsigned long numcodes = (unsigned long)tree2d.size() / 2; unsigned long numcodes = (unsigned long)tree2d.size() / 2;
if(treepos >= numcodes) return 11; //error: you appeared outside the codetree if (treepos >= numcodes)
return 11; // error: you appeared outside the codetree
result = tree2d[2 * treepos + bit]; result = tree2d[2 * treepos + bit];
decoded = (result < numcodes); decoded = (result < numcodes);
treepos = decoded ? 0 : result - numcodes; treepos = decoded ? 0 : result - numcodes;
@ -97,219 +110,353 @@ int decodePNG(unsigned char* &out_image, int& image_width, int& image_height, co
} }
std::vector<unsigned long> tree2d; // 2D representation of a huffman tree: The one dimension is "0" or "1", the other contains all nodes and leaves of the tree. std::vector<unsigned long> 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 Inflator {
{
int error; int error;
void inflate(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, size_t inpos = 0) void inflate(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, size_t inpos = 0) {
{
size_t bp = 0, pos = 0; // bit pointer and byte pointer size_t bp = 0, pos = 0; // bit pointer and byte pointer
error = 0; error = 0;
unsigned long BFINAL = 0; unsigned long BFINAL = 0;
while(!BFINAL && !error) while (!BFINAL && !error) {
{ if (bp >> 3 >= in.size()) {
if(bp >> 3 >= in.size()) { error = 52; return; } //error, bit pointer will jump past memory error = 52;
return;
} // error, bit pointer will jump past memory
BFINAL = readBitFromStream(bp, &in[inpos]); BFINAL = readBitFromStream(bp, &in[inpos]);
unsigned long BTYPE = readBitFromStream(bp, &in[inpos]); BTYPE += 2 * readBitFromStream(bp, &in[inpos]); unsigned long BTYPE = readBitFromStream(bp, &in[inpos]);
if(BTYPE == 3) { error = 20; return; } //error: invalid BTYPE BTYPE += 2 * readBitFromStream(bp, &in[inpos]);
else if(BTYPE == 0) inflateNoCompression(out, &in[inpos], bp, pos, in.size()); if (BTYPE == 3) {
else inflateHuffmanBlock(out, &in[inpos], bp, pos, in.size(), BTYPE); 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 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 void generateFixedTrees(HuffmanTree& tree, HuffmanTree& treeD) // get the tree of a deflated block with fixed tree
{ {
std::vector<unsigned long> bitlen(288, 8), bitlenD(32, 5);; std::vector<unsigned long> 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; 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); tree.makeFromLengths(bitlen, 15);
treeD.makeFromLengths(bitlenD, 15); treeD.makeFromLengths(bitlenD, 15);
} }
HuffmanTree codetree, codetreeD, codelengthcodetree; // the code tree for Huffman codes, dist codes, and code length codes 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) 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
{ //decode a single symbol from given list of bits with given code tree. return value is the symbol bool decoded;
bool decoded; unsigned long ct; unsigned long ct;
for(size_t treepos = 0;;) for (size_t treepos = 0;;) {
{ if ((bp & 0x07) == 0 && (bp >> 3) > inlength) {
if((bp & 0x07) == 0 && (bp >> 3) > inlength) { error = 10; return 0; } //error: end reached without endcode error = 10;
error = codetree.decode(decoded, ct, treepos, readBitFromStream(bp, in)); if(error) return 0; //stop, an error happened return 0;
if(decoded) return ct; } // 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) 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
{ //get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree
std::vector<unsigned long> bitlen(288, 0), bitlenD(32, 0); std::vector<unsigned long> bitlen(288, 0), bitlenD(32, 0);
if(bp >> 3 >= inlength - 2) { error = 49; return; } //the bit pointer is or will go past the memory 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 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 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 size_t HCLEN = readBitsFromStream(bp, in, 4) + 4; // number of code length codes + 4
std::vector<unsigned long> codelengthcode(19); // lengths of tree to decode the lengths of the dynamic tree std::vector<unsigned long> 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; for (size_t i = 0; i < 19; i++)
error = codelengthcodetree.makeFromLengths(codelengthcode, 7); if(error) return; codelengthcode[CLCL[i]] = (i < HCLEN) ? readBitsFromStream(bp, in, 3) : 0;
error = codelengthcodetree.makeFromLengths(codelengthcode, 7);
if (error)
return;
size_t i = 0, replength; size_t i = 0, replength;
while(i < HLIT + HDIST) while (i < HLIT + HDIST) {
{ unsigned long code = huffmanDecodeSymbol(in, bp, codelengthcodetree, inlength);
unsigned long code = huffmanDecodeSymbol(in, bp, codelengthcodetree, inlength); if(error) return; if (error)
if(code <= 15) { if(i < HLIT) bitlen[i++] = code; else bitlenD[i++ - HLIT] = code; } //a length code return;
if (code <= 15) {
if (i < HLIT)
bitlen[i++] = code;
else
bitlenD[i++ - HLIT] = code;
} // a length code
else if (code == 16) // repeat previous else if (code == 16) // repeat previous
{ {
if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory if (bp >> 3 >= inlength) {
error = 50;
return;
} // error, bit pointer jumps past memory
replength = 3 + readBitsFromStream(bp, in, 2); replength = 3 + readBitsFromStream(bp, in, 2);
unsigned long value; // set value to the previous code unsigned long value; // set value to the previous code
if((i - 1) < HLIT) value = bitlen[i - 1]; if ((i - 1) < HLIT)
else value = bitlenD[i - HLIT - 1]; 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 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 + HDIST) {
if(i < HLIT) bitlen[i++] = value; else bitlenD[i++ - HLIT] = value; 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
else if(code == 17) //repeat "0" 3-10 times
{ {
if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory if (bp >> 3 >= inlength) {
error = 50;
return;
} // error, bit pointer jumps past memory
replength = 3 + readBitsFromStream(bp, in, 3); replength = 3 + readBitsFromStream(bp, in, 3);
for (size_t n = 0; n < replength; n++) // repeat this value in the next lengths 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 + HDIST) {
if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0; 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
else if(code == 18) //repeat "0" 11-138 times
{ {
if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory if (bp >> 3 >= inlength) {
error = 50;
return;
} // error, bit pointer jumps past memory
replength = 11 + readBitsFromStream(bp, in, 7); replength = 11 + readBitsFromStream(bp, in, 7);
for (size_t n = 0; n < replength; n++) // repeat this value in the next lengths 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 + HDIST) {
if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0; 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.
} }
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;
} }
if(bitlen[256] == 0) { error = 64; return; } //the length of the end code 256 must be larger than 0 void inflateHuffmanBlock(std::vector<unsigned char>& out, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength, unsigned long btype) {
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 if (btype == 1) {
error = treeD.makeFromLengths(bitlenD, 15); if(error) return; generateFixedTrees(codetree, codetreeD);
} else if (btype == 2) {
getTreeInflateDynamic(codetree, codetreeD, in, bp, inlength);
if (error)
return;
} }
void inflateHuffmanBlock(std::vector<unsigned char>& out, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength, unsigned long btype) for (;;) {
{ unsigned long code = huffmanDecodeSymbol(in, bp, codetree, inlength);
if(btype == 1) { generateFixedTrees(codetree, codetreeD); } if (error)
else if(btype == 2) { getTreeInflateDynamic(codetree, codetreeD, in, bp, inlength); if(error) return; } return;
for(;;) if (code == 256)
{ return; // end code
unsigned long code = huffmanDecodeSymbol(in, bp, codetree, inlength); if(error) return;
if(code == 256) return; //end code
else if (code <= 255) // literal symbol else if (code <= 255) // literal symbol
{ {
if(pos >= out.size()) out.resize((pos + 1) * 2); //reserve more room if (pos >= out.size())
out.resize((pos + 1) * 2); // reserve more room
out[pos++] = (unsigned char)(code); out[pos++] = (unsigned char)(code);
} } else if (code >= 257 && code <= 285) // length code
else if(code >= 257 && code <= 285) //length code
{ {
size_t length = LENBASE[code - 257], numextrabits = LENEXTRA[code - 257]; size_t length = LENBASE[code - 257], numextrabits = LENEXTRA[code - 257];
if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory if ((bp >> 3) >= inlength) {
error = 51;
return;
} // error, bit pointer will jump past memory
length += readBitsFromStream(bp, in, numextrabits); length += readBitsFromStream(bp, in, numextrabits);
unsigned long codeD = huffmanDecodeSymbol(in, bp, codetreeD, inlength); if(error) return; unsigned long codeD = huffmanDecodeSymbol(in, bp, codetreeD, inlength);
if(codeD > 29) { error = 18; return; } //error: invalid dist code (30-31 are never used) 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]; unsigned long dist = DISTBASE[codeD], numextrabitsD = DISTEXTRA[codeD];
if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory if ((bp >> 3) >= inlength) {
error = 51;
return;
} // error, bit pointer will jump past memory
dist += readBitsFromStream(bp, in, numextrabitsD); dist += readBitsFromStream(bp, in, numextrabitsD);
size_t start = pos, back = start - dist; // backwards size_t start = pos, back = start - dist; // backwards
if(pos + length >= out.size()) out.resize((pos + length) * 2); //reserve more room if (pos + length >= out.size())
for(size_t i = 0; i < length; i++) { out[pos++] = out[back++]; if(back >= start) back = start - dist; } 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<unsigned char>& out, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength) }
{ void inflateNoCompression(std::vector<unsigned char>& 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 while ((bp & 0x7) != 0)
bp++; // go to first boundary of byte
size_t p = bp / 8; size_t p = bp / 8;
if(p >= inlength - 4) { error = 52; return; } //error, bit pointer will jump past memory if (p >= inlength - 4) {
unsigned long LEN = in[p] + 256 * in[p + 1], NLEN = in[p + 2] + 256 * in[p + 3]; p += 4; error = 52;
if(LEN + NLEN != 65535) { error = 21; return; } //error: NLEN is not one's complement of LEN return;
if(pos + LEN >= out.size()) out.resize(pos + LEN); } // error, bit pointer will jump past memory
if(p + LEN > inlength) { error = 23; return; } //error: reading outside of in buffer unsigned long LEN = in[p] + 256 * in[p + 1], NLEN = in[p + 2] + 256 * in[p + 3];
for(unsigned long n = 0; n < LEN; n++) out[pos++] = in[p++]; //read LEN bytes of literal data 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; bp = p * 8;
} }
}; };
int decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in) // returns error value int decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in) // returns error value
{ {
Inflator inflator; Inflator inflator;
if(in.size() < 2) { return 53; } //error, size of zlib data too small if (in.size() < 2) {
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 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; 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 (CM != 8 || CINFO > 7) {
if(FDICT != 0) { return 26; } //error: the specification of PNG says about the zlib stream: "The additional flags shall not specify a preset dictionary." 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); inflator.inflate(out, in, 2);
return inflator.error; // note: adler32 checksum was skipped and ignored return inflator.error; // note: adler32 checksum was skipped and ignored
} }
}; };
struct PNG // nested functions for PNG decoding struct PNG // nested functions for PNG decoding
{ {
struct Info struct Info {
{
unsigned long width, height, colorType, bitDepth, compressionMethod, filterMethod, interlaceMethod, key_r, key_g, key_b; unsigned long width, height, colorType, bitDepth, compressionMethod, filterMethod, interlaceMethod, key_r, key_g, key_b;
bool key_defined; // is a transparent color key given? bool key_defined; // is a transparent color key given?
std::vector<unsigned char> palette; std::vector<unsigned char> palette;
} info; } info;
int error; int error;
void decode(unsigned char* &out, const unsigned char* in, size_t size, bool convert_to_rgba32) void decode(unsigned char*& out, const unsigned char* in, size_t size, bool convert_to_rgba32) {
{
error = 0; error = 0;
if(size == 0 || in == 0) { error = 48; return; } //the given data is empty if (size == 0 || in == 0) {
readPngHeader(&in[0], size); if(error) return; 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 size_t pos = 33; // first byte of the first chunk after the header
std::vector<unsigned char> idat; // the data from idat chunks std::vector<unsigned char> idat; // the data from idat chunks
bool IEND = false, known_type = true; bool IEND = false, known_type = true;
info.key_defined = false; 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 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 if (pos + 8 >= size) {
size_t chunkLength = read32bitInt(&in[pos]); pos += 4; error = 30;
if(chunkLength > 2147483647) { error = 63; return; } return;
if(pos + chunkLength >= size) { error = 35; return; } //error: size of the in buffer too small to contain next chunk } // 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 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]); idat.insert(idat.end(), &in[pos + 4], &in[pos + 4 + chunkLength]);
pos += (4 + chunkLength); pos += (4 + chunkLength);
} } else if (in[pos + 0] == 'I' && in[pos + 1] == 'E' && in[pos + 2] == 'N' && in[pos + 3] == 'D') {
else if(in[pos + 0] == 'I' && in[pos + 1] == 'E' && in[pos + 2] == 'N' && in[pos + 3] == 'D') { pos += 4; IEND = true; } pos += 4;
else if(in[pos + 0] == 'P' && in[pos + 1] == 'L' && in[pos + 2] == 'T' && in[pos + 3] == 'E') //palette chunk (PLTE) 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 pos += 4; // go after the 4 letters
info.palette.resize(4 * (chunkLength / 3)); info.palette.resize(4 * (chunkLength / 3));
if(info.palette.size() > (4 * 256)) { error = 38; return; } //error: palette too big if (info.palette.size() > (4 * 256)) {
for(size_t i = 0; i < info.palette.size(); i += 4) error = 38;
{ return;
for(size_t j = 0; j < 3; j++) info.palette[i + j] = in[pos++]; //RGB } // 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 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)
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 pos += 4; // go after the 4 letters
if(info.colorType == 3) if (info.colorType == 3) {
{ if (4 * chunkLength > info.palette.size()) {
if(4 * chunkLength > info.palette.size()) { error = 39; return; } //error: more alpha values given than there are palette entries error = 39;
for(size_t i = 0; i < chunkLength; i++) info.palette[4 * i + 3] = in[pos++]; return;
} } // error: more alpha values given than there are palette entries
else if(info.colorType == 0) for (size_t i = 0; i < chunkLength; i++)
{ info.palette[4 * i + 3] = in[pos++];
if(chunkLength != 2) { error = 40; return; } //error: this chunk must be 2 bytes for greyscale image } else if (info.colorType == 0) {
info.key_defined = 1; info.key_r = info.key_g = info.key_b = 256 * in[pos] + in[pos + 1]; pos += 2; if (chunkLength != 2) {
} error = 40;
else if(info.colorType == 2) return;
{ } // error: this chunk must be 2 bytes for greyscale image
if(chunkLength != 6) { error = 41; return; } //error: this chunk must be 6 bytes for RGB image
info.key_defined = 1; info.key_defined = 1;
info.key_r = 256 * in[pos] + in[pos + 1]; pos += 2; info.key_r = info.key_g = info.key_b = 256 * in[pos] + in[pos + 1];
info.key_g = 256 * in[pos] + in[pos + 1]; pos += 2; pos += 2;
info.key_b = 256 * in[pos] + in[pos + 1]; pos += 2; } else if (info.colorType == 2) {
} if (chunkLength != 6) {
else { error = 42; return; } //error: tRNS chunk not allowed for other color models error = 41;
} return;
else //it's not an implemented chunk type, so ignore it: skip over the data } // 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) 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 pos += (chunkLength + 4); // skip 4 letters and uninterpreted data of unimplemented chunk
known_type = false; known_type = false;
} }
@ -318,29 +465,34 @@ int decodePNG(unsigned char* &out_image, int& image_width, int& image_height, co
unsigned long bpp = getBpp(info); unsigned long bpp = getBpp(info);
std::vector<unsigned char> scanlines(((info.width * (info.height * bpp + 7)) / 8) + info.height); // now the out buffer will be filled std::vector<unsigned char> scanlines(((info.width * (info.height * bpp + 7)) / 8) + info.height); // now the out buffer will be filled
Zlib zlib; // decompress with the Zlib decompressor Zlib zlib; // decompress with the Zlib decompressor
error = zlib.decompress(scanlines, idat); if(error) return; //stop if the zlib decompressor returned an error 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; 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 out = new unsigned char[outlength + 1]; // time to fill the out buffer
if (info.interlaceMethod == 0) // no interlace, just filter 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 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 if (bpp >= 8) // byte per byte
for(unsigned long y = 0; y < info.height; y++) for (unsigned long y = 0; y < info.height; y++) {
{
unsigned long filterType = scanlines[linestart]; unsigned long filterType = scanlines[linestart];
const unsigned char* prevline = (y == 0) ? 0 : &out[(y - 1) * info.width * bytewidth]; 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; unFilterScanline(&out[linestart - y], &scanlines[linestart + 1], prevline, bytewidth, filterType, linelength);
if (error)
return;
linestart += (1 + linelength); // go to start of next scanline linestart += (1 + linelength); // go to start of next scanline
} }
else // less than 8 bits per pixel, so fill it up bit per bit 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 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++) for (size_t y = 0, obp = 0; y < info.height; y++) {
{
unsigned long filterType = scanlines[linestart]; unsigned long filterType = scanlines[linestart];
const unsigned char* prevline = (y == 0) ? 0 : &out[(y - 1) * info.width * bytewidth]; 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; unFilterScanline(templine, &scanlines[linestart + 1], prevline, bytewidth, filterType, linelength);
for(size_t bp = 0; bp < info.width * bpp;) setBitOfReversedStream(obp, out, readBitFromReversedStream(bp, &templine[0])); 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 linestart += (1 + linelength); // go to start of next scanline
} }
delete[] templine; delete[] templine;
@ -354,149 +506,210 @@ int decodePNG(unsigned char* &out_image, int& image_width, int& image_height, co
} }
void readPngHeader(const unsigned char* in, size_t inlength) // read the information from the header and store it in the Info 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 (inlength < 29) {
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 error = 27;
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! return;
info.width = read32bitInt(&in[16]); info.height = read32bitInt(&in[20]); } // error: the data length is smaller than the length of the header
info.bitDepth = in[24]; info.colorType = in[25]; 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) {
info.compressionMethod = in[26]; if(in[26] != 0) { error = 32; return; } //error: only compression method 0 is allowed in the specification error = 28;
info.filterMethod = in[27]; if(in[27] != 0) { error = 33; return; } //error: only filter method 0 is allowed in the specification return;
info.interlaceMethod = in[28]; if(in[28] > 1) { error = 34; return; } //error: only interlace methods 0 and 1 exist in the specification } // 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); 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) void unFilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned long filterType, size_t length) {
{ switch (filterType) {
switch(filterType) case 0:
{ for (size_t i = 0; i < length; i++)
case 0: for(size_t i = 0; i < length; i++) recon[i] = scanline[i]; break; recon[i] = scanline[i];
break;
case 1: case 1:
for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; for (size_t i = 0; i < bytewidth; i++)
for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth]; recon[i] = scanline[i];
for (size_t i = bytewidth; i < length; i++)
recon[i] = scanline[i] + recon[i - bytewidth];
break; break;
case 2: case 2:
if(precon) for(size_t i = 0; i < length; i++) recon[i] = scanline[i] + precon[i]; if (precon)
else for(size_t i = 0; i < length; i++) recon[i] = scanline[i]; 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; break;
case 3: case 3:
if(precon) if (precon) {
{ for (size_t i = 0; i < bytewidth; i++)
for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2; 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); for (size_t i = bytewidth; i < length; i++)
} recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2);
else } else {
{ for (size_t i = 0; i < bytewidth; i++)
for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; recon[i] = scanline[i];
for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2; for (size_t i = bytewidth; i < length; i++)
recon[i] = scanline[i] + recon[i - bytewidth] / 2;
} }
break; break;
case 4: case 4:
if(precon) if (precon) {
{ for (size_t i = 0; i < bytewidth; i++)
for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i] + paethPredictor(0, precon[i], 0); 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]); for (size_t i = bytewidth; i < length; i++)
} recon[i] = scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]);
else } else {
{ for (size_t i = 0; i < bytewidth; i++)
for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; recon[i] = scanline[i];
for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0); for (size_t i = bytewidth; i < length; i++)
recon[i] = scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0);
} }
break; break;
default: error = 36; return; //error: unexisting filter type given 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 readBitFromReversedStream(size_t& bitp, const unsigned char* bits) {
static unsigned long readBitsFromReversedStream(size_t& bitp, const unsigned char* bits, unsigned long nbits) unsigned long result = (bits[bitp >> 3] >> (7 - (bitp & 0x7))) & 1;
{ bitp++;
unsigned long result = 0;
for(size_t i = nbits - 1; i < nbits; i--) result += ((readBitFromReversedStream(bitp, bits)) << i);
return result; return result;
} }
void setBitOfReversedStream(size_t& bitp, unsigned char* bits, unsigned long bit) { bits[bitp >> 3] |= (bit << (7 - (bitp & 0x7))); bitp++; } 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]; } 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 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; } if ((colorType == 2 || colorType == 4 || colorType == 6)) {
else if(colorType == 0) { if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; else return 0; } if (!(bd == 8 || bd == 16))
else if(colorType == 3) { if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; else return 0; } return 37;
else return 31; //unexisting color type 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) unsigned long getBpp(const Info& info) {
{ if (info.colorType == 2)
if(info.colorType == 2) return (3 * info.bitDepth); return (3 * info.bitDepth);
else if(info.colorType >= 4) return (info.colorType - 2) * info.bitDepth; else if (info.colorType >= 4)
else return info.bitDepth; 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) 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
{ //converts from any color type to 32-bit. return value = LodePNG error code
size_t numpixels = w * h, bp = 0; size_t numpixels = w * h, bp = 0;
out = new unsigned char[numpixels * 4 + 1]; out = new unsigned char[numpixels * 4 + 1];
if (infoIn.bitDepth == 8 && infoIn.colorType == 0) // greyscale if (infoIn.bitDepth == 8 && infoIn.colorType == 0) // greyscale
for(size_t i = 0; i < numpixels; i++) 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 + 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; out[4 * i + 3] = (infoIn.key_defined && in[i] == infoIn.key_r) ? 0 : 255;
} }
else if (infoIn.bitDepth == 8 && infoIn.colorType == 2) // RGB color else if (infoIn.bitDepth == 8 && infoIn.colorType == 2) // RGB color
for(size_t i = 0; i < numpixels; i++) for (size_t i = 0; i < numpixels; i++) {
{ for (size_t c = 0; c < 3; c++)
for(size_t c = 0; c < 3; c++) out[4 * i + c] = in[3 * i + 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; 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) else if (infoIn.bitDepth == 8 && infoIn.colorType == 3) // indexed color (palette)
for(size_t i = 0; i < numpixels; i++) for (size_t i = 0; i < numpixels; i++) {
{ if (4U * in[i] >= infoIn.palette.size())
if(4U * in[i] >= infoIn.palette.size()) return 46; 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 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 else if (infoIn.bitDepth == 8 && infoIn.colorType == 4) // greyscale with alpha
for(size_t i = 0; i < numpixels; i++) 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 + 0] = out[4 * i + 1] = out[4 * i + 2] = in[2 * i + 0];
out[4 * i + 3] = in[2 * i + 1]; 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 == 8 && infoIn.colorType == 6)
else if(infoIn.bitDepth == 16 && infoIn.colorType == 0) //greyscale
for (size_t i = 0; i < numpixels; i++) 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 + 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; 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 else if (infoIn.bitDepth == 16 && infoIn.colorType == 2) // RGB color
for(size_t i = 0; i < numpixels; i++) for (size_t i = 0; i < numpixels; i++) {
{ for (size_t c = 0; c < 3; c++)
for(size_t c = 0; c < 3; c++) out[4 * i + c] = in[6 * i + 2 * 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; 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 else if (infoIn.bitDepth == 16 && infoIn.colorType == 4) // greyscale with alpha
for(size_t i = 0; i < numpixels; i++) 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 + 0] = out[4 * i + 1] = out[4 * i + 2] = in[4 * i]; // most significant byte
out[4 * i + 3] = in[4 * i + 2]; 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 == 16 && infoIn.colorType == 6)
else if(infoIn.bitDepth < 8 && infoIn.colorType == 0) //greyscale
for (size_t i = 0; i < numpixels; i++) 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 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 + 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; 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 else if (infoIn.bitDepth < 8 && infoIn.colorType == 3) // palette
for(size_t i = 0; i < numpixels; i++) for (size_t i = 0; i < numpixels; i++) {
{
unsigned long value = readBitsFromReversedStream(bp, in, infoIn.bitDepth); unsigned long value = readBitsFromReversedStream(bp, in, infoIn.bitDepth);
if(4 * value >= infoIn.palette.size()) return 47; if (4 * value >= infoIn.palette.size())
for(size_t c = 0; c < 4; c++) out[4 * i + c] = infoIn.palette[4 * value + c]; //get rgb colors from the palette 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; return 0;
} }
unsigned char paethPredictor(short a, short b, short c) // Paeth predicter, used by PNG filter type 4 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); 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); 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); PNG decoder;
image_width = decoder.info.width; image_height = decoder.info.height; decoder.decode(out_image, in_png, in_size, convert_to_rgba32);
image_width = decoder.info.width;
image_height = decoder.info.height;
return decoder.error; return decoder.error;
} }

View File

@ -3,21 +3,19 @@
Copyright (c) 2018 nullworks. All rights reserved. Copyright (c) 2018 nullworks. All rights reserved.
*/ */
#include <glez/texture.hpp>
#include <glez/glez.hpp>
#include <cassert> #include <cassert>
#include <vector>
#include <glez/picopng/picopng.hpp>
#include <memory>
#include <cstring> #include <cstring>
#include <glez/glez.hpp>
#include <glez/picopng/picopng.hpp>
#include <glez/texture.hpp>
#include <memory>
#include <vector>
#include <fstream> // required to load the file #include <fstream> // required to load the file
namespace glez namespace glez {
{
void texture::bind() void texture::bind() {
{
if (!bound) { if (!bound) {
glGenTextures(1, &id); glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id); glBindTexture(GL_TEXTURE_2D, id);
@ -62,7 +60,6 @@ texture texture::loadFromFile(const std::string &path) {
return ret; return ret;
} }
texture texture::loadFromMemory(const std::byte* mem, std::size_t size, unsigned w, unsigned h) { texture texture::loadFromMemory(const std::byte* mem, std::size_t size, unsigned w, unsigned h) {
if (size < 1) if (size < 1)
throw std::runtime_error("Unable to load texture from memory!"); throw std::runtime_error("Unable to load texture from memory!");
@ -105,4 +102,4 @@ texture& texture::operator=(texture&& var) {
return *this; return *this;
} }
} // namespace glez::detail::texture } // namespace glez