isurface textures (broken)
This commit is contained in:
parent
af7306f5a2
commit
f1c5ca9389
@ -22,15 +22,6 @@ namespace fonts
|
||||
extern std::unique_ptr<glez::font> esp;
|
||||
extern std::unique_ptr<glez::font> menu;
|
||||
extern unsigned long surface_font;
|
||||
} // namespace fonts
|
||||
|
||||
namespace fonts
|
||||
{
|
||||
|
||||
// FIXME add menu fonts
|
||||
extern unsigned long ESP;
|
||||
extern unsigned long MENU;
|
||||
extern unsigned long MENU_BIG;
|
||||
|
||||
void Update();
|
||||
|
||||
@ -61,10 +52,41 @@ extern float fov;
|
||||
|
||||
void Initialize();
|
||||
|
||||
#if ENABLE_ENGINE_DRAWING
|
||||
class Texture
|
||||
{
|
||||
public:
|
||||
explicit Texture(std::string path) : path{ path }
|
||||
{
|
||||
}
|
||||
bool load();
|
||||
unsigned int get();
|
||||
~Texture();
|
||||
unsigned int getWidth()
|
||||
{
|
||||
return m_width;
|
||||
}
|
||||
unsigned int getHeight()
|
||||
{
|
||||
return m_height;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string path;
|
||||
unsigned int texture_id = 0;
|
||||
unsigned char *data = nullptr;
|
||||
bool init = false;
|
||||
int m_width, m_height;
|
||||
};
|
||||
#else
|
||||
typedef glez::texture Texture;
|
||||
#endif
|
||||
|
||||
void Line(float x1, float y1, float x2, float y2, rgba_t color, float thickness);
|
||||
void String(int x, int y, rgba_t rgba, const char *text);
|
||||
void Rectangle(float x, float y, float w, float h, rgba_t color);
|
||||
void RectangleOutlined(float x, float y, float w, float h, rgba_t color, float thickness);
|
||||
void RectangleTextured(float x, float y, float w, float h, rgba_t color, Texture &texture, float tx, float ty, float tw, float th, float angle);
|
||||
|
||||
void UpdateWTS();
|
||||
bool WorldToScreen(const Vector &origin, Vector &screen);
|
||||
@ -73,4 +95,5 @@ bool EntityCenterToScreen(CachedEntity *entity, Vector &out);
|
||||
void InitGL();
|
||||
void BeginGL();
|
||||
void EndGL();
|
||||
|
||||
} // namespace draw
|
||||
|
5
include/visual/picopng.hpp
Normal file
5
include/visual/picopng.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
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);
|
@ -291,8 +291,8 @@ static void cm()
|
||||
}
|
||||
}
|
||||
|
||||
static glez::texture atlas{ DATA_PATH "/textures/atlas.png" };
|
||||
static glez::texture idspec{ DATA_PATH "/textures/idspec.png" };
|
||||
static draw::Texture atlas{ DATA_PATH "/textures/atlas.png" };
|
||||
static draw::Texture idspec{ DATA_PATH "/textures/idspec.png" };
|
||||
|
||||
Timer retry{};
|
||||
void Init()
|
||||
@ -342,7 +342,7 @@ void _FASTCALL emoji(CachedEntity *ent)
|
||||
if (g_IEngine->GetPlayerInfo(ent->m_IDX, &info))
|
||||
steamID = info.friendsID;
|
||||
if (playerlist::AccessData(steamID).state == playerlist::k_EState::CAT)
|
||||
glez::draw::rect_textured(head_scr.x - size / 2, head_scr.y - size / 2, size, size, glez::color::white, idspec, 2 * 64, 1 * 64, 64, 64, 0);
|
||||
draw::RectangleTextured(head_scr.x - size / 2, head_scr.y - size / 2, size, size, glez::color::white, idspec, 2 * 64, 1 * 64, 64, 64, 0);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (steamID == steamidarray[i])
|
||||
@ -353,12 +353,12 @@ void _FASTCALL emoji(CachedEntity *ent)
|
||||
ii++;
|
||||
i -= 4;
|
||||
}
|
||||
glez::draw::rect_textured(head_scr.x - size / 2, head_scr.y - size / 2, size, size, glez::color::white, idspec, i * 64, ii * 64, 64, 64, 0);
|
||||
draw::RectangleTextured(head_scr.x - size / 2, head_scr.y - size / 2, size, size, glez::color::white, idspec, i * 64, ii * 64, 64, 64, 0);
|
||||
hascall = true;
|
||||
}
|
||||
}
|
||||
if (!hascall)
|
||||
glez::draw::rect_textured(head_scr.x - size / 2, head_scr.y - size / 2, size, size, colors::white, textures::atlas().texture, (3 + (v9mode ? 3 : (int) emoji_esp)) * 64, (v9mode ? 3 : 4) * 64, 64, 64, 0);
|
||||
draw::RectangleTextured(head_scr.x - size / 2, head_scr.y - size / 2, size, size, colors::white, atlas, (3 + (v9mode ? 3 : (int) emoji_esp)) * 64, (v9mode ? 3 : 4) * 64, 64, 64, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,9 @@ target_sources(cathook PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}/drawmgr.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/EffectChams.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/EffectGlow.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/EventLogging.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/EventLogging.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/fidgetspinner.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/picopng.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/SDLHooks.cpp")
|
||||
|
||||
if(EnableGUI)
|
||||
|
@ -12,7 +12,8 @@
|
||||
#include <GL/glew.h>
|
||||
#include <SDL2/SDL_video.h>
|
||||
#include <SDLHooks.hpp>
|
||||
#include <menu/GuiInterface.hpp>
|
||||
#include "menu/GuiInterface.hpp"
|
||||
#include "picopng.hpp"
|
||||
|
||||
// String -> Wstring
|
||||
#include <locale>
|
||||
@ -201,6 +202,50 @@ void RectangleOutlined(float x, float y, float w, float h, rgba_t color, float t
|
||||
Rectangle(x, y + h - 1, w, 1, color);
|
||||
}
|
||||
|
||||
void RectangleTextured(float x, float y, float w, float h, rgba_t color, Texture &texture, float tx, float ty, float tw, float th, float angle)
|
||||
{
|
||||
#if !ENABLE_ENGINE_DRAWING
|
||||
glez::draw::rect_textured(x, y, w, h, color, texture, tx, ty, tw, th, angle);
|
||||
#else
|
||||
color = color * 255.0f;
|
||||
vgui::Vertex_t vertices[4];
|
||||
g_ISurface->DrawSetColor(color.r, color.g, color.b, color.a);
|
||||
g_ISurface->DrawSetTexture(texture.get());
|
||||
|
||||
vertices[0].m_Position = { x, y };
|
||||
vertices[1].m_Position = { x, y + h };
|
||||
vertices[2].m_Position = { x + w, y + h };
|
||||
vertices[3].m_Position = { x + w, y };
|
||||
|
||||
if (angle != 0.0f)
|
||||
{
|
||||
float cx = x + float(w) / 2.0f;
|
||||
float cy = y + float(h) / 2.0f;
|
||||
|
||||
for (auto &v : vertices)
|
||||
{
|
||||
float ox = v.m_Position.x;
|
||||
float oy = v.m_Position.y;
|
||||
|
||||
v.m_Position.x = cx + cosf(angle) * (ox - cx) - sinf(angle) * (oy - cy);
|
||||
v.m_Position.y = cy + sinf(angle) * (ox - cx) + cosf(angle) * (oy - cy);
|
||||
}
|
||||
}
|
||||
|
||||
float s0 = float(tx) / texture.getWidth();
|
||||
float s1 = float(tx + tw) / texture.getWidth();
|
||||
float t0 = float(ty) / texture.getHeight();
|
||||
float t1 = float(ty + th) / texture.getHeight();
|
||||
|
||||
vertices[0].m_TexCoord = { s0, t0 };
|
||||
vertices[1].m_TexCoord = { s0, t1 };
|
||||
vertices[2].m_TexCoord = { s1, t1 };
|
||||
vertices[3].m_TexCoord = { s1, t0 };
|
||||
|
||||
g_ISurface->DrawTexturedPolygon(4, vertices);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool EntityCenterToScreen(CachedEntity *entity, Vector &out)
|
||||
{
|
||||
Vector world, min, max;
|
||||
@ -227,6 +272,53 @@ bool WorldToScreen(const Vector &origin, Vector &screen)
|
||||
return g_IVDebugOverlay->ScreenPosition(origin, screen) == 0;
|
||||
}
|
||||
|
||||
bool Texture::load()
|
||||
{
|
||||
std::ifstream file(path.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
|
||||
|
||||
std::streamsize size = 0;
|
||||
if (file.seekg(0, std::ios::end).good())
|
||||
size = file.tellg();
|
||||
if (file.seekg(0, std::ios::beg).good())
|
||||
size -= file.tellg();
|
||||
|
||||
if (size < 1)
|
||||
return false;
|
||||
|
||||
unsigned char *buffer = new unsigned char[(size_t) size + 1];
|
||||
file.read((char *) buffer, size);
|
||||
file.close();
|
||||
int error = decodePNG(data, m_width, m_height, buffer, size);
|
||||
|
||||
// if there's an error, display it and return false to indicate failure
|
||||
if (error != 0)
|
||||
{
|
||||
logging::Info("Error loading texture, error code %i\n", error);
|
||||
return false;
|
||||
}
|
||||
texture_id = g_ISurface->CreateNewTextureID();
|
||||
g_ISurface->DrawSetTextureRGBA(texture_id, data, m_width, m_height, false, false);
|
||||
if (!g_ISurface->IsTextureIDValid(texture_id))
|
||||
return false;
|
||||
init = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
g_ISurface->DeleteTextureByID(texture_id);
|
||||
}
|
||||
|
||||
unsigned int Texture::get()
|
||||
{
|
||||
if (!g_ISurface->IsTextureIDValid(texture_id))
|
||||
{
|
||||
if (!load() || !g_ISurface->IsTextureIDValid(texture_id))
|
||||
throw std::runtime_error("Couldn't init texture!");
|
||||
}
|
||||
return texture_id;
|
||||
}
|
||||
|
||||
SDL_GLContext context = nullptr;
|
||||
|
||||
void InitGL()
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <glez/draw.hpp>
|
||||
#include <menu/wm/WMWindow.hpp>
|
||||
#include <config.h>
|
||||
#include "drawing.hpp"
|
||||
|
||||
/*
|
||||
Created on 07.07.18.
|
||||
|
502
src/visual/picopng.cpp
Normal file
502
src/visual/picopng.cpp
Normal file
@ -0,0 +1,502 @@
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
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.
|
||||
By default the output is 32-bit RGBA color.
|
||||
image_width: output_parameter, this will contain the width of the image in pixels.
|
||||
image_height: output_parameter, this will contain the height of the image in pixels.
|
||||
in_png: pointer to the buffer of the PNG file in memory. To get it from a file on
|
||||
disk, load it and store it in a memory buffer yourself first.
|
||||
in_size: size of the input PNG file in bytes.
|
||||
convert_to_rgba32: optional parameter, true by default.
|
||||
Set to true to get the output in RGBA 32-bit (8 bit per channel) color format
|
||||
no matter what color type the original PNG image had. This gives predictable,
|
||||
useable data from any random input PNG.
|
||||
Set to false to do no color conversion at all. The result then has the same data
|
||||
type as the PNG image, which can range from 1 bit to 64 bits per pixel.
|
||||
Information about the color type or palette colors are not provided. You need
|
||||
to know this information yourself to be able to use the data so this only
|
||||
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.
|
||||
|
||||
// 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)
|
||||
{
|
||||
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<unsigned long>& bitlen, unsigned long maxbitlen)
|
||||
{ //make tree given the lengths
|
||||
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);
|
||||
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<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
|
||||
{
|
||||
int error;
|
||||
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
|
||||
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<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;
|
||||
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<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
|
||||
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<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;
|
||||
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<unsigned char>& 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<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
|
||||
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<unsigned char>& out, const std::vector<unsigned char>& 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<unsigned char> 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<unsigned char> 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<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
|
||||
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;
|
||||
}
|
Reference in New Issue
Block a user