Академический Документы
Профессиональный Документы
Культура Документы
h"
#include <fstream>
vector<unsigned char> readFileToVector(string filePath) {
string contents = readFileToString(filePath);
vector<unsigned char> returnVector;
returnVector.resize(contents.size());
for (unsigned int i = 0; i < contents.size(); i++) {
returnVector[i] = contents.at(i);
}
return returnVector;
}
string readFileToString(string filePath) {
ifstream file(filePath.c_str(), ios::binary);
if (file.fail()) {
return "";
}
string returnString;
while (file.good()) {
returnString += file.get();
}
file.close();
return returnString;
}
void saveStringToFile(string filePath, string contents) {
ofstream file(filePath.c_str(), ios::binary);
if (file.fail()) {
return;
}
file.write(contents.c_str(), contents.size());
file.close();
}
/*
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.
The std::vector is automatically resized to the correct size.
image_width: output_parameter, this will contain the width of the image in pixel
s.
image_height: output_parameter, this will contain the height of the image in pix
els.
in_png: pointer to the buffer of the PNG file in memory. To get it from a file o
n
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,
replength = 3 + readBitsFromStre
am(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 + readBitsFromStr
eam(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: so
mehow 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 (er
ror) 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 lo
ng btype)
{
if (btype == 1) { generateFixedTrees(codetree, c
odetreeD); }
else if (btype == 2) { getTreeInflateDynamic(cod
etree, 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.resiz
e((pos + 1) * 2); //reserve more room
out[pos++] = (unsigned char)(cod
e);
}
else if (code >= 257 && code <= 285) //l
ength code
{
size_t length = LENBASE[code - 2
57], numextrabits = LENEXTRA[code - 257];
if ((bp >> 3) >= inlength) { err
or = 51; return; } //error, bit pointer will jump past memory
length += readBitsFromStream(bp,
in, numextrabits);
unsigned long codeD = huffmanDec
odeSymbol(in, bp, codetreeD, inlength); if (error) return;
if (codeD > 29) { error = 18; re
turn; } //error: invalid dist code (30-31 are never used)
unsigned long dist = DISTBASE[co
deD], numextrabitsD = DISTEXTRA[codeD];
if ((bp >> 3) >= inlength) { err
or = 51; return; } //error, bit pointer will jump past memory
dist += readBitsFromStream(bp, i
n, 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>& ou
t, const unsigned char* in, size_t& bp, size_t& pos, size_t inlength)
{
while ((bp & 0x7) != 0) bp++; //go to first boun
dary 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], NLE
N = 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 + LE
N);
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::vecto
r<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; } //er
ror: 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 c
ompression method 8: inflate with sliding window of 32k is supported by the PNG
spec
if (FDICT != 0) { return 26; } //error: the specificatio
n of PNG says about the zlib stream: "The additional flags shall not specify a p
reset dictionary."
inflator.inflate(out, in, 2);
return inflator.error; //note: adler32 checksum was skip
ped and ignored
}
};
struct PNG //nested functions for PNG decoding
{
struct Info
{
unsigned long width, height, colorType, bitDepth, compre
ssionMethod, 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(std::vector<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 t
he header
std::vector<unsigned char> idat; //the data from idat ch
unks
bool IEND = false, known_type = true;
info.key_defined = false;
while (!IEND) //loop through the chunks, ignoring unknow
n chunks and stopping at IEND chunk. IDAT data is put at the start of the in buf
fer
{
if (pos + 8 >= size) { error = 30; return; } //e
rror: size of the in buffer too small to contain next chunk
size_t chunkLength = read32bitInt(&in[pos]); pos
+= 4;
if (chunkLength > 2147483647) { error = 63; retu
rn; }
if (pos + chunkLength >= size) { error = 35; ret
urn; } //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 im
age data
{
idat.insert(idat.end(), &in[pos + 4], &i
n[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
));
o.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) ret
urn; //stop if the zlib decompressor returned an error
size_t bytewidth = (bpp + 7) / 8, outlength = (info.heig
ht * info.width * bpp + 7) / 8;
out.resize(outlength); //time to fill the out buffer
unsigned char* out_ = outlength ? &out[0] : 0; //use a r
egular pointer to the std::vector for faster code if compiled without optimizati
on
if (info.interlaceMethod == 0) //no interlace, just filt
er
{
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.heigh
t; y++)
{
unsigned long filterType = scanl
ines[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
{
std::vector<unsigned char> templine((inf
o.width * bpp + 7) >> 3); //only used if bpp < 8
for (size_t y = 0, obp = 0; y < info.hei
ght; y++)
{
unsigned long filterType = scanl
ines[linestart];
const unsigned char* prevline =
(y == 0) ? 0 : &out_[(y - 1) * info.width * bytewidth];
unFilterScanline(&templine[0], &
scanlines[linestart + 1], prevline, bytewidth, filterType, linelength); if (erro
r) return;
for (size_t bp = 0; bp < info.wi
dth * bpp;) setBitOfReversedStream(obp, out_, readBitFromReversedStream(bp, &tem
pline[0]));
linestart += (1 + linelength); /
/go to start of next scanline
}
}
}
else //interlaceMethod is 1 (Adam7)
{
size_t passw[7] = { (info.width + 7) / 8, (info.
width + 3) / 8, (info.width + 3) / 4, (info.width + 1) / 4, (info.width + 1) / 2
, (info.width + 0) / 2, (info.width + 0) / 1 };
size_t passh[7] = { (info.height + 7) / 8, (info
.height + 7) / 8, (info.height + 3) / 8, (info.height + 3) / 4, (info.height + 1
) / 4, (info.height + 1) / 2, (info.height + 0) / 2 };
size_t passstart[7] = { 0 };
size_t pattern[28] = { 0, 4, 0, 2, 0, 1, 0, 0, 0
, 4, 0, 2, 0, 1, 8, 8, 4, 4, 2, 2, 1, 8, 8, 8, 4, 4, 2, 2 }; //values for the ad
am7 passes
for (int i = 0; i < 6; i++) passstart[i + 1] = p
assstart[i] + passh[i] * ((passw[i] ? 1 : 0) + (passw[i] * bpp + 7) / 8);
std::vector<unsigned char> scanlineo((info.width
* bpp + 7) / 8), scanlinen((info.width * bpp + 7) / 8); //"old" and "new" scanl
ine
for (int i = 0; i < 7; i++)
adam7Pass(&out_[0], &scanlinen[0], &scan
lineo[0], &scanlines[passstart[i]], info.width, pattern[i], pattern[i + 7], patt
ern[i + 14], pattern[i + 21], passw[i], passh[i], bpp);
}
if (convert_to_rgba32 && (info.colorType != 6 || info.bi
tDepth != 8)) //conversion needed
{
std::vector<unsigned char> data = out;
error = convert(out, &data[0], info, info.width,
info.height);
}
}
void readPngHeader(const unsigned char* in, size_t inlength) //r
ead 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' || i
n[15] != 'R') { error = 29; return; } //error: it doesn't start with a IHDR chun
k!
info.width = read32bitInt(&in[16]); info.height = read32
bitInt(&in[20]);
info.bitDepth = in[24]; info.colorType = in[25];
info.compressionMethod = in[26]; if (in[26] != 0) { erro
r = 32; return; } //error: only compression method 0 is allowed in the specifica
tion
info.filterMethod = in[27]; if (in[27] != 0) { error = 3
3; 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 specificatio
n
error = checkColorValidity(info.colorType, info.bitDepth
);
}
void unFilterScanline(unsigned char* recon, const unsigned char*
scanline, const unsigned char* precon, size_t bytewidth, unsigned long filterTy
pe, size_t length)
{
switch (filterType)
{
case 0: for (size_t i = 0; i < length; i++) recon[i] = s
canline[i]; break;
case 1:
for (size_t i = 0; i < bytewidth; i++) recon[i]
= scanline[i];
for (size_t i = bytewidth; i < length; i++) reco
n[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++) r
econ[i] = scanline[i];
break;
case 3:
if (precon)
{
for (size_t i = 0; i < bytewidth; i++) r
econ[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++) r
econ[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++) r
econ[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], pre
con[i - bytewidth]);
}
else
{
for (size_t i = 0; i < bytewidth; i++) r
econ[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
}
}
void adam7Pass(unsigned char* out, unsigned char* linen, unsigne
d char* lineo, const unsigned char* in, unsigned long w, size_t passleft, size_t
passtop, size_t spacex, size_t spacey, size_t passw, size_t passh, unsigned lon
g bpp)
{ //filter and reposition the pixels into the output when the im
age is Adam7 interlaced. This function can only do it after the full image is al
ready decoded. The out buffer must have the correct allocated memory size alread
y.
if (passw == 0) return;
size_t bytewidth = (bpp + 7) / 8, linelength = 1 + ((bpp
* passw + 7) / 8);
for (unsigned long y = 0; y < passh; y++)
{
unsigned char filterType = in[y * linelength], *
prevline = (y == 0) ? 0 : lineo;
unFilterScanline(linen, &in[y * linelength + 1],
{
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++)
{