Skip to content

Commit 9465ca9

Browse files
Merge pull request #42 from Shxde1/main
Lib update
2 parents 2d86db0 + bae4434 commit 9465ca9

File tree

11 files changed

+3522
-724
lines changed

11 files changed

+3522
-724
lines changed

QRCode/pingout.cpp

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/*
2+
* Tiny PNG Output (C++)
3+
*
4+
* Copyright (c) 2018 Project Nayuki
5+
* https://www.nayuki.io/page/tiny-png-output
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU Lesser General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program (see COPYING.txt and COPYING.LESSER.txt).
19+
* If not, see <http://www.gnu.org/licenses/>.
20+
*/
21+
22+
#pragma warning(disable:4146)
23+
24+
#include <algorithm>
25+
#include <cassert>
26+
#include <limits>
27+
#include <stdexcept>
28+
#include "pngout.hpp"
29+
30+
#pragma warning(disable:C4146)
31+
32+
using std::uint8_t;
33+
using std::uint16_t;
34+
using std::uint32_t;
35+
using std::uint64_t;
36+
using std::size_t;
37+
38+
39+
TinyPngOut::TinyPngOut(uint32_t w, uint32_t h, std::ostream &out) :
40+
// Set most of the fields
41+
width(w),
42+
height(h),
43+
output(out),
44+
positionX(0),
45+
positionY(0),
46+
deflateFilled(0),
47+
adler(1) {
48+
49+
// Check arguments
50+
if (width == 0 || height == 0)
51+
throw std::domain_error("Zero width or height");
52+
53+
// Compute and check data siezs
54+
uint64_t lineSz = static_cast<uint64_t>(width) * 3 + 1;
55+
if (lineSz > UINT32_MAX)
56+
throw std::length_error("Image too large");
57+
lineSize = static_cast<uint32_t>(lineSz);
58+
59+
uint64_t uncompRm = lineSize * height;
60+
if (uncompRm > UINT32_MAX)
61+
throw std::length_error("Image too large");
62+
uncompRemain = static_cast<uint32_t>(uncompRm);
63+
64+
uint32_t numBlocks = uncompRemain / DEFLATE_MAX_BLOCK_SIZE;
65+
if (uncompRemain % DEFLATE_MAX_BLOCK_SIZE != 0)
66+
numBlocks++; // Round up
67+
// 5 bytes per DEFLATE uncompressed block header, 2 bytes for zlib header, 4 bytes for zlib Adler-32 footer
68+
uint64_t idatSize = static_cast<uint64_t>(numBlocks) * 5 + 6;
69+
idatSize += uncompRemain;
70+
if (idatSize > static_cast<uint32_t>(INT32_MAX))
71+
throw std::length_error("Image too large");
72+
73+
// Write header (not a pure header, but a couple of things concatenated together)
74+
uint8_t header[] = { // 43 bytes long
75+
// PNG header
76+
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
77+
// IHDR chunk
78+
0x00, 0x00, 0x00, 0x0D,
79+
0x49, 0x48, 0x44, 0x52,
80+
0, 0, 0, 0, // 'width' placeholder
81+
0, 0, 0, 0, // 'height' placeholder
82+
0x08, 0x02, 0x00, 0x00, 0x00,
83+
0, 0, 0, 0, // IHDR CRC-32 placeholder
84+
// IDAT chunk
85+
0, 0, 0, 0, // 'idatSize' placeholder
86+
0x49, 0x44, 0x41, 0x54,
87+
// DEFLATE data
88+
0x08, 0x1D,
89+
};
90+
putBigUint32(width, &header[16]);
91+
putBigUint32(height, &header[20]);
92+
putBigUint32(idatSize, &header[33]);
93+
crc = 0;
94+
crc32(&header[12], 17);
95+
putBigUint32(crc, &header[29]);
96+
write(header);
97+
98+
crc = 0;
99+
crc32(&header[37], 6); // 0xD7245B6B
100+
}
101+
102+
103+
void TinyPngOut::write(const uint8_t pixels[], size_t count) {
104+
if (count > SIZE_MAX / 3)
105+
throw std::length_error("Invalid argument");
106+
count *= 3; // Convert pixel count to byte count
107+
while (count > 0) {
108+
if (pixels == nullptr)
109+
throw std::invalid_argument("Null pointer");
110+
if (positionY >= height)
111+
throw std::logic_error("All image pixels already written");
112+
113+
if (deflateFilled == 0) { // Start DEFLATE block
114+
uint16_t size = DEFLATE_MAX_BLOCK_SIZE;
115+
if (uncompRemain < size)
116+
size = static_cast<uint16_t>(uncompRemain);
117+
const uint8_t header[] = { // 5 bytes long
118+
static_cast<uint8_t>(uncompRemain <= DEFLATE_MAX_BLOCK_SIZE ? 1 : 0),
119+
static_cast<uint8_t>(size >> 0),
120+
static_cast<uint8_t>(size >> 8),
121+
static_cast<uint8_t>((size >> 0) ^ 0xFF),
122+
static_cast<uint8_t>((size >> 8) ^ 0xFF),
123+
};
124+
write(header);
125+
crc32(header, sizeof(header) / sizeof(header[0]));
126+
}
127+
assert(positionX < lineSize && deflateFilled < DEFLATE_MAX_BLOCK_SIZE);
128+
129+
if (positionX == 0) { // Beginning of line - write filter method byte
130+
uint8_t b[] = {0};
131+
write(b);
132+
crc32(b, 1);
133+
adler32(b, 1);
134+
positionX++;
135+
uncompRemain--;
136+
deflateFilled++;
137+
138+
} else { // Write some pixel bytes for current line
139+
uint16_t n = DEFLATE_MAX_BLOCK_SIZE - deflateFilled;
140+
if (lineSize - positionX < n)
141+
n = static_cast<uint16_t>(lineSize - positionX);
142+
if (count < n)
143+
n = static_cast<uint16_t>(count);
144+
if (static_cast<std::make_unsigned<std::streamsize>::type>(std::numeric_limits<std::streamsize>::max()) < std::numeric_limits<decltype(n)>::max())
145+
n = std::min(n, static_cast<decltype(n)>(std::numeric_limits<std::streamsize>::max()));
146+
assert(n > 0);
147+
output.write(reinterpret_cast<const char*>(pixels), static_cast<std::streamsize>(n));
148+
149+
// Update checksums
150+
crc32(pixels, n);
151+
adler32(pixels, n);
152+
153+
// Increment positions
154+
count -= n;
155+
pixels += n;
156+
positionX += n;
157+
uncompRemain -= n;
158+
deflateFilled += n;
159+
}
160+
161+
if (deflateFilled >= DEFLATE_MAX_BLOCK_SIZE)
162+
deflateFilled = 0; // End current block
163+
164+
if (positionX == lineSize) { // Increment line
165+
positionX = 0;
166+
positionY++;
167+
if (positionY == height) { // Reached end of pixels
168+
uint8_t footer[] = { // 20 bytes long
169+
0, 0, 0, 0, // DEFLATE Adler-32 placeholder
170+
0, 0, 0, 0, // IDAT CRC-32 placeholder
171+
// IEND chunk
172+
0x00, 0x00, 0x00, 0x00,
173+
0x49, 0x45, 0x4E, 0x44,
174+
0xAE, 0x42, 0x60, 0x82,
175+
};
176+
putBigUint32(adler, &footer[0]);
177+
crc32(&footer[0], 4);
178+
putBigUint32(crc, &footer[4]);
179+
write(footer);
180+
}
181+
}
182+
}
183+
}
184+
185+
186+
void TinyPngOut::crc32(const uint8_t data[], size_t len) {
187+
crc = ~crc;
188+
for (size_t i = 0; i < len; i++) {
189+
for (int j = 0; j < 8; j++) { // Inefficient bitwise implementation, instead of table-based
190+
uint32_t bit = (crc ^ (data[i] >> j)) & 1;
191+
crc = (crc >> 1) ^ ((-bit) & UINT32_C(0xEDB88320));
192+
}
193+
}
194+
crc = ~crc;
195+
}
196+
197+
198+
void TinyPngOut::adler32(const uint8_t data[], size_t len) {
199+
uint32_t s1 = adler & 0xFFFF;
200+
uint32_t s2 = adler >> 16;
201+
for (size_t i = 0; i < len; i++) {
202+
s1 = (s1 + data[i]) % 65521;
203+
s2 = (s2 + s1) % 65521;
204+
}
205+
adler = s2 << 16 | s1;
206+
}
207+
208+
209+
void TinyPngOut::putBigUint32(uint32_t val, uint8_t array[4]) {
210+
for (int i = 0; i < 4; i++)
211+
array[i] = static_cast<uint8_t>(val >> ((3 - i) * 8));
212+
}
213+

QRCode/pngout.hpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Tiny PNG Output (C++)
3+
*
4+
* Copyright (c) 2018 Project Nayuki
5+
* https://www.nayuki.io/page/tiny-png-output
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU Lesser General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program (see COPYING.txt and COPYING.LESSER.txt).
19+
* If not, see <http://www.gnu.org/licenses/>.
20+
*/
21+
22+
#pragma once
23+
24+
#include <cstddef>
25+
#include <cstdint>
26+
#include <ostream>
27+
28+
29+
/*
30+
* Takes image pixel data in raw RGB8.8.8 format and writes a PNG file to a byte output stream.
31+
*/
32+
class TinyPngOut final {
33+
34+
/*---- Fields ----*/
35+
36+
// Immutable configuration
37+
private: std::uint32_t width; // Measured in pixels
38+
private: std::uint32_t height; // Measured in pixels
39+
private: std::uint32_t lineSize; // Measured in bytes, equal to (width * 3 + 1)
40+
41+
// Running state
42+
private: std::ostream &output;
43+
private: std::uint32_t positionX; // Next byte index in current line
44+
private: std::uint32_t positionY; // Line index of next byte
45+
private: std::uint32_t uncompRemain; // Number of uncompressed bytes remaining
46+
private: std::uint16_t deflateFilled; // Bytes filled in the current block (0 <= n < DEFLATE_MAX_BLOCK_SIZE)
47+
private: std::uint32_t crc; // Primarily for IDAT chunk
48+
private: std::uint32_t adler; // For DEFLATE data within IDAT
49+
50+
51+
52+
/*---- Public constructor and method ----*/
53+
54+
/*
55+
* Creates a PNG writer with the given width and height (both non-zero) and byte output stream.
56+
* TinyPngOut will leave the output stream still open once it finishes writing the PNG file data.
57+
* Throws an exception if the dimensions exceed certain limits (e.g. w * h > 700 million).
58+
*/
59+
public: explicit TinyPngOut(std::uint32_t w, std::uint32_t h, std::ostream &out);
60+
61+
62+
/*
63+
* Writes 'count' pixels from the given array to the output stream. This reads count*3
64+
* bytes from the array. Pixels are presented from top to bottom, left to right, and with
65+
* subpixels in RGB order. This object keeps track of how many pixels were written and
66+
* various position variables. It is an error to write more pixels in total than width*height.
67+
* Once exactly width*height pixels have been written with this TinyPngOut object,
68+
* there are no more valid operations on the object and it should be discarded.
69+
*/
70+
public: void write(const std::uint8_t pixels[], size_t count);
71+
72+
73+
74+
/*---- Private checksum methods ----*/
75+
76+
// Reads the 'crc' field and updates its value based on the given array of new data.
77+
private: void crc32(const std::uint8_t data[], size_t len);
78+
79+
80+
// Reads the 'adler' field and updates its value based on the given array of new data.
81+
private: void adler32(const std::uint8_t data[], size_t len);
82+
83+
84+
85+
/*---- Private utility members ----*/
86+
87+
private: template <std::size_t N>
88+
void write(const std::uint8_t (&data)[N]) {
89+
output.write(reinterpret_cast<const char*>(data), sizeof(data));
90+
}
91+
92+
93+
private: static void putBigUint32(std::uint32_t val, std::uint8_t array[4]);
94+
95+
96+
private: static constexpr std::uint16_t DEFLATE_MAX_BLOCK_SIZE = 65535;
97+
98+
};

0 commit comments

Comments
 (0)