跳转到帖子

c++制作的二维码生成器 1.0.0

登录关注  

1 Screenshot

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

  • 查看数 769
  • 下载 7
  • 提交于
  • 更新日期
  • File Size 1.01 MB
  • 登录关注  



×
×
  • 创建新的...

重要信息

注册必须使用2-8个中文汉字作为账号