About This File
QrToPng.h:
// // Created by remy on 07-06-20. // #ifndef QR_TO_PNG_H #define QR_TO_PNG_H #include "QrCode.hpp" #include "TinyPngOut.hpp" #include <fstream> #include <iostream> #include <memory> #include <string> #include <stdio.h> #include <io.h> class QrToPng { public: /** * Gives an object containing all the data to create the QR code. When @writeToPNG() is called, * the actual file is constructed and written. * The image is scaled to fit in the given size as much as possible relative to the QR code * size. * @param fileName relative or absolute filename to write image to. Relative will be in CWD. * @param imgSize The height and width of the image. Image is square, so will be width and height. * @param minModulePixelSize How many pixels big should a qr module be (a white or black dot)? * @param text The text to encode in the QR code. * @param overwriteExistingFile Overwrite if a file with @fileName already exists? * @param ecc error correction (low,mid,high). */ QrToPng(std::string fileName, int imgSize, int minModulePixelSize, std::string text, bool overwriteExistingFile, qrcodegen::QrCode::Ecc ecc); /** Writes a QrToPng object to a png file at @_fileName. * @return true if file could be written, false if file could not be written */ bool writeToPNG(); private: std::string _fileName; int _size; int _minModulePixelSize; std::string _text; bool _overwriteExistingFile; qrcodegen::QrCode::Ecc _ecc; bool _writeToPNG(const qrcodegen::QrCode &qrData) const; uint32_t _imgSize(const qrcodegen::QrCode &qrData) const; uint32_t _imgSizeWithBorder(const qrcodegen::QrCode &qrData) const; bool file_exists(std::string file_name) const; }; #endif //QR_TO_PNG_H
QrToPng.cpp:
// // Created by remy on 02-06-20. // #include "QrToPng.h" QrToPng::QrToPng(std::string fileName, int imgSize, int minModulePixelSize, std::string text, bool overwriteExistingFile, qrcodegen::QrCode::Ecc ecc) : _fileName(std::move(fileName)), _size(imgSize), _minModulePixelSize(minModulePixelSize), _text(std::move(text)), _overwriteExistingFile(overwriteExistingFile), _ecc(ecc) { } bool QrToPng::writeToPNG() { /* text is required */ if (_text.empty()) return false; if (!_overwriteExistingFile and file_exists(_fileName)) return false; auto _qr = qrcodegen::QrCode::encodeText("", _ecc); try { _qr = qrcodegen::QrCode::encodeText(_text.c_str(), _ecc); } catch (const std::length_error &e) { std::cerr << "Failed to generate QR code, too much data. Decrease _ecc, enlarge size or give less text." << std::endl; std::cerr << "e.what(): " << e.what() << std::endl; return false; } if (_overwriteExistingFile and file_exists(_fileName)) { /*if (!fs::copy_file(_fileName, _fileName + ".tmp", fs::copy_options::overwrite_existing)) { return false; }*/ } auto result = _writeToPNG(_qr); if (result) { std::string tmp_file(_fileName + ".tmp"); remove(tmp_file.c_str()); } return result; } bool QrToPng::_writeToPNG(const qrcodegen::QrCode &qrData) const { std::ofstream out(_fileName.c_str(), std::ios::binary); int pngWH = _imgSizeWithBorder(qrData); TinyPngOut pngout(pngWH, pngWH, out); auto qrSize = qrData.getSize(); auto qrSizeWithBorder = qrData.getSize() + 2; if (qrSizeWithBorder > _size) return false; // qrcode doesn't fit int qrSizeFitsInMaxImgSizeTimes = _size / qrSizeWithBorder; int pixelsWHPerModule = qrSizeFitsInMaxImgSizeTimes; if (qrSizeFitsInMaxImgSizeTimes < _minModulePixelSize) return false; // image would be to small to scan std::vector<uint8_t> tmpData; const uint8_t blackPixel = 0x00; const uint8_t whitePixel = 0xFF; /* The below loop converts the qrData to RGB8.8.8 pixels and writes it with * the tinyPNGoutput library. since we probably have requested a larger * qr module pixel size we must transform the qrData modules to be larger * pixels (than just 1x1). */ // border above for (int i = 0; i < pngWH; i++) // row for (int j = 0; j < pixelsWHPerModule; j++) // module pixel (height) tmpData.insert(tmpData.end(), {whitePixel, whitePixel, whitePixel}); pngout.write(tmpData.data(), static_cast<size_t>(tmpData.size() / 3)); tmpData.clear(); for (int qrModuleAtY = 0; qrModuleAtY < qrSize; qrModuleAtY++) { for (int col = 0; col < pixelsWHPerModule; col++) { // border left for (int i = 0; i < qrSizeFitsInMaxImgSizeTimes; ++i) tmpData.insert(tmpData.end(), {whitePixel, whitePixel, whitePixel}); // qr module to pixel for (int qrModuleAtX = 0; qrModuleAtX < (qrSize); qrModuleAtX++) { for (int row = 0; row < qrSizeFitsInMaxImgSizeTimes; ++row) { if (qrData.getModule(qrModuleAtX, qrModuleAtY)) { // insert saves us a for loop or 3 times the same line. tmpData.insert(tmpData.end(), {blackPixel, blackPixel, blackPixel}); } else { tmpData.insert(tmpData.end(), {whitePixel, whitePixel, whitePixel}); } } } // border right for (int i = 0; i < qrSizeFitsInMaxImgSizeTimes; ++i) tmpData.insert(tmpData.end(), {whitePixel, whitePixel, whitePixel}); // write the entire row pngout.write(tmpData.data(), static_cast<size_t>(tmpData.size() / 3)); tmpData.clear(); } } // border below for (int i = 0; i < pngWH; i++) // row for (int j = 0; j < pixelsWHPerModule; j++) // module pixel (height) tmpData.insert(tmpData.end(), {whitePixel, whitePixel, whitePixel}); pngout.write(tmpData.data(), static_cast<size_t>(tmpData.size() / 3)); tmpData.clear(); return file_exists(_fileName); } uint32_t QrToPng::_imgSize(const qrcodegen::QrCode &qrData) const { return (_size / qrData.getSize()) * qrData.getSize(); } uint32_t QrToPng::_imgSizeWithBorder(const qrcodegen::QrCode &qrData) const { return (_size / (qrData.getSize() + 2)) * (qrData.getSize() + 2); } bool QrToPng::file_exists(std::string file_name) const { if ((_access(file_name.c_str(), 0) == 0) > 0) { return true; } return false; }
TinyPngOut.hpp:
/* * Tiny PNG Output (C++) * * Copyright (c) 2018 Project Nayuki * https://www.nayuki.io/page/tiny-png-output * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program (see COPYING.txt and COPYING.LESSER.txt). * If not, see <http://www.gnu.org/licenses/>. */ #pragma once #include <cstddef> #include <cstdint> #include <ostream> /* * Takes image pixel data in raw RGB8.8.8 format and writes a PNG file to a byte output stream. */ class TinyPngOut final { /*---- Fields ----*/ // Immutable configuration private: std::uint32_t width; // Measured in pixels private: std::uint32_t height; // Measured in pixels private: std::uint32_t lineSize; // Measured in bytes, equal to (width * 3 + 1) // Running state private: std::ostream &output; private: std::uint32_t positionX; // Next byte index in current line private: std::uint32_t positionY; // Line index of next byte private: std::uint32_t uncompRemain; // Number of uncompressed bytes remaining private: std::uint16_t deflateFilled; // Bytes filled in the current block (0 <= n < DEFLATE_MAX_BLOCK_SIZE) private: std::uint32_t crc; // Primarily for IDAT chunk private: std::uint32_t adler; // For DEFLATE data within IDAT /*---- Public constructor and method ----*/ /* * Creates a PNG writer with the given width and height (both non-zero) and byte output stream. * TinyPngOut will leave the output stream still open once it finishes writing the PNG file data. * Throws an exception if the dimensions exceed certain limits (e.g. w * h > 700 million). */ public: explicit TinyPngOut(std::uint32_t w, std::uint32_t h, std::ostream &out); /* * Writes 'count' pixels from the given array to the output stream. This reads count*3 * bytes from the array. Pixels are presented from top to bottom, left to right, and with * subpixels in RGB order. This object keeps track of how many pixels were written and * various position variables. It is an error to write more pixels in total than width*height. * Once exactly width*height pixels have been written with this TinyPngOut object, * there are no more valid operations on the object and it should be discarded. */ public: void write(const std::uint8_t pixels[], size_t count); /*---- Private checksum methods ----*/ // Reads the 'crc' field and updates its value based on the given array of new data. private: void crc32(const std::uint8_t data[], size_t len); // Reads the 'adler' field and updates its value based on the given array of new data. private: void adler32(const std::uint8_t data[], size_t len); /*---- Private utility members ----*/ private: template <std::size_t N> void write(const std::uint8_t(&data)[N]) { output.write(reinterpret_cast<const char*>(data), sizeof(data)); } private: static void putBigUint32(std::uint32_t val, std::uint8_t array[4]); private: static constexpr std::uint16_t DEFLATE_MAX_BLOCK_SIZE = 65535; };
TinyPngOut.cpp:
/* * Tiny PNG Output (C++) * * Copyright (c) 2018 Project Nayuki * https://www.nayuki.io/page/tiny-png-output * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program (see COPYING.txt and COPYING.LESSER.txt). * If not, see <http://www.gnu.org/licenses/>. */ #include <algorithm> #include <cassert> #include <limits> #include <stdexcept> #include "TinyPngOut.hpp" using std::uint8_t; using std::uint16_t; using std::uint32_t; using std::uint64_t; using std::size_t; TinyPngOut::TinyPngOut(uint32_t w, uint32_t h, std::ostream &out) : // Set most of the fields width(w), height(h), output(out), positionX(0), positionY(0), deflateFilled(0), adler(1) { // Check arguments if (width == 0 || height == 0) throw std::domain_error("Zero width or height"); // Compute and check data siezs uint64_t lineSz = static_cast<uint64_t>(width) * 3 + 1; if (lineSz > UINT32_MAX) throw std::length_error("Image too large"); lineSize = static_cast<uint32_t>(lineSz); uint64_t uncompRm = lineSize * height; if (uncompRm > UINT32_MAX) throw std::length_error("Image too large"); uncompRemain = static_cast<uint32_t>(uncompRm); uint32_t numBlocks = uncompRemain / DEFLATE_MAX_BLOCK_SIZE; if (uncompRemain % DEFLATE_MAX_BLOCK_SIZE != 0) numBlocks++; // Round up // 5 bytes per DEFLATE uncompressed block header, 2 bytes for zlib header, 4 bytes for zlib Adler-32 footer uint64_t idatSize = static_cast<uint64_t>(numBlocks) * 5 + 6; idatSize += uncompRemain; if (idatSize > static_cast<uint32_t>(INT32_MAX)) throw std::length_error("Image too large"); // Write header (not a pure header, but a couple of things concatenated together) uint8_t header[] = { // 43 bytes long // PNG header 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // IHDR chunk 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0, 0, 0, 0, // 'width' placeholder 0, 0, 0, 0, // 'height' placeholder 0x08, 0x02, 0x00, 0x00, 0x00, 0, 0, 0, 0, // IHDR CRC-32 placeholder // IDAT chunk 0, 0, 0, 0, // 'idatSize' placeholder 0x49, 0x44, 0x41, 0x54, // DEFLATE data 0x08, 0x1D, }; putBigUint32(width, &header[16]); putBigUint32(height, &header[20]); putBigUint32(idatSize, &header[33]); crc = 0; crc32(&header[12], 17); putBigUint32(crc, &header[29]); write(header); crc = 0; crc32(&header[37], 6); // 0xD7245B6B } void TinyPngOut::write(const uint8_t pixels[], size_t count) { if (count > SIZE_MAX / 3) throw std::length_error("Invalid argument"); count *= 3; // Convert pixel count to byte count while (count > 0) { if (pixels == nullptr) throw std::invalid_argument("Null pointer"); if (positionY >= height) throw std::logic_error("All image pixels already written"); if (deflateFilled == 0) { // Start DEFLATE block uint16_t size = DEFLATE_MAX_BLOCK_SIZE; if (uncompRemain < size) size = Create an account or sign in to download this
File Information
- 查看数 836
- 下载 7
- 提交于
- 更新日期
- File Size 1.01 MB
登录关注关注者 0