nix-archive-1(type directoryentry(nameCMakeLists.txtnode(typeregularcontentsv# This file is part of Desktop App Toolkit, # a set of libraries for developing nice desktop applications. # # For license and copyright information please follow this link: # https://github.com/desktop-app/legal/blob/master/LEGAL add_library(lib_qr OBJECT) add_library(desktop-app::lib_qr ALIAS lib_qr) init_target(lib_qr) get_filename_component(src_loc . REALPATH) nice_target_sources(lib_qr ${src_loc} PRIVATE qr/qr_generate.cpp qr/qr_generate.h ) target_include_directories(lib_qr PUBLIC ${src_loc} ) target_link_libraries(lib_qr PUBLIC desktop-app::lib_base desktop-app::external_qr_code_generator ) ))entry(nameqrnode(type directoryentry(nameqr_generate.cppnode(typeregularcontents|// This file is part of Desktop App Toolkit, // a set of libraries for developing nice desktop applications. // // For license and copyright information please follow this link: // https://github.com/desktop-app/legal/blob/master/LEGAL // #include "qr/qr_generate.h" #include "base/assertion.h" #if __has_include() #include #else #include #endif #include #include namespace Qr { namespace { using namespace qrcodegen; [[nodiscard]] int ReplaceElements(const Data &data) { const auto elements = [&] { switch (data.redundancy) { case Redundancy::Low: return data.size / 5; case Redundancy::Medium: return data.size / 4; case Redundancy::Quartile: return data.size / 3; case Redundancy::High: return data.size / 2; } Unexpected("Redundancy value in Qr::ReplaceElements."); }(); const auto close = (data.redundancy != Redundancy::Quartile); const auto shift = (data.size - elements) % 2; return elements + (close ? -1 : 1) * shift; } [[nodiscard]] QrCode::Ecc RedundancyToEcc(Redundancy redundancy) { switch (redundancy) { case Redundancy::Low: return QrCode::Ecc::LOW; case Redundancy::Medium: return QrCode::Ecc::MEDIUM; case Redundancy::Quartile: return QrCode::Ecc::QUARTILE; case Redundancy::High: return QrCode::Ecc::HIGH; } Unexpected("Redundancy value in Qr::RedundancyToEcc."); } } // namespace Data Encode(const QString &text, Redundancy redundancy) { Expects(!text.isEmpty()); auto result = Data(); result.redundancy = redundancy; const auto utf8 = text.toStdString(); const auto qr = QrCode::encodeText( utf8.c_str(), RedundancyToEcc(redundancy)); result.size = qr.getSize(); Assert(result.size > 0); result.values.reserve(result.size * result.size); for (auto row = 0; row != result.size; ++row) { for (auto column = 0; column != result.size; ++column) { result.values.push_back(qr.getModule(row, column)); } } return result; } void PrepareForRound(QPainter &p) { p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing); p.setPen(Qt::NoPen); } QImage GenerateSingle(int size, QColor bg, QColor color) { auto result = QImage(size, size, QImage::Format_ARGB32_Premultiplied); result.fill(bg); { auto p = QPainter(&result); p.setCompositionMode(QPainter::CompositionMode_Source); PrepareForRound(p); p.setBrush(color); p.drawRoundedRect( QRect{ 0, 0, size, size }, size / 2., size / 2.); } return result; } int ReplaceSize(const Data &data, int pixel) { return ReplaceElements(data) * pixel; } QImage Generate(const Data &data, int pixel, QColor fg, QColor bg) { Expects(data.size > 0); Expects(data.values.size() == data.size * data.size); const auto replaceElements = ReplaceElements(data); const auto replaceFrom = (data.size - replaceElements) / 2; const auto replaceTill = (data.size - replaceFrom); const auto black = GenerateSingle(pixel, bg, fg); const auto white = GenerateSingle(pixel, fg, bg); const auto value = [&](int row, int column) { return (row >= 0) && (row < data.size) && (column >= 0) && (column < data.size) && (row < replaceFrom || row >= replaceTill || column < replaceFrom || column >= replaceTill) && data.values[row * data.size + column]; }; const auto blackFull = [&](int row, int column) { return (value(row - 1, column) && value(row + 1, column)) || (value(row, column - 1) && value(row, column + 1)); }; const auto whiteCorner = [&](int row, int column, int dx, int dy) { return !value(row + dy, column) || !value(row, column + dx) || !value(row + dy, column + dx); }; const auto whiteFull = [&](int row, int column) { return whiteCorner(row, column, -1, -1) && whiteCorner(row, column, 1, -1) && whiteCorner(row, column, 1, 1) && whiteCorner(row, column, -1, 1); }; auto result = QImage( data.size * pixel, data.size * pixel, QImage::Format_ARGB32_Premultiplied); result.fill(bg); { auto p = QPainter(&result); p.setCompositionMode(QPainter::CompositionMode_Source); const auto skip = pixel - pixel / 2; const auto brect = [&](int x, int y, int width, int height) { p.fillRect(x, y, width, height, fg); }; const auto wrect = [&](int x, int y, int width, int height) { p.fillRect(x, y, width, height, bg); }; const auto large = [&](int x, int y) { p.setBrush(fg); p.drawRoundedRect( QRect{ x, y, pixel * 7, pixel * 7 }, pixel * 2., pixel * 2.); p.setBrush(bg); p.drawRoundedRect( QRect{ x + pixel, y + pixel, pixel * 5, pixel * 5 }, pixel * 1.5, pixel * 1.5); p.setBrush(fg); p.drawRoundedRect( QRect{ x + pixel * 2, y + pixel * 2, pixel * 3, pixel * 3 }, pixel, pixel); }; for (auto row = 0; row != data.size; ++row) { for (auto column = 0; column != data.size; ++column) { if ((row < 7 && (column < 7 || column >= data.size - 7)) || (column < 7 && (row < 7 || row >= data.size - 7))) { continue; } const auto x = column * pixel; const auto y = row * pixel; if (value(row, column)) { if (blackFull(row, column)) { brect(x, y, pixel, pixel); } else { p.drawImage(x, y, black); if (value(row - 1, column)) { brect(x, y, pixel, pixel / 2); } else if (value(row + 1, column)) { brect(x, y + skip, pixel, pixel / 2); } if (value(row, column - 1)) { brect(x, y, pixel / 2, pixel); } else if (value(row, column + 1)) { brect(x + skip, y, pixel / 2, pixel); } } } else if (whiteFull(row, column)) { wrect(x, y, pixel, pixel); } else { p.drawImage(x, y, white); if (whiteCorner(row, column, -1, -1) && whiteCorner(row, column, 1, -1)) { wrect(x, y, pixel, pixel / 2); } else if (whiteCorner(row, column, -1, 1) && whiteCorner(row, column, 1, 1)) { wrect(x, y + skip, pixel, pixel / 2); } if (whiteCorner(row, column, -1, -1) && whiteCorner(row, column, -1, 1)) { wrect(x, y, pixel / 2, pixel); } else if (whiteCorner(row, column, 1, -1) && whiteCorner(row, column, 1, 1)) { wrect(x + skip, y, pixel / 2, pixel); } if (whiteCorner(row, column, -1, -1)) { wrect(x, y, pixel / 2, pixel / 2); } if (whiteCorner(row, column, 1, -1)) { wrect(x + skip, y, pixel / 2, pixel / 2); } if (whiteCorner(row, column, 1, 1)) { wrect(x + skip, y + skip, pixel / 2, pixel / 2); } if (whiteCorner(row, column, -1, 1)) { wrect(x, y + skip, pixel / 2, pixel / 2); } } } } PrepareForRound(p); large(0, 0); large((data.size - 7) * pixel, 0); large(0, (data.size - 7) * pixel); } return result; } QImage ReplaceCenter(QImage qr, const QImage ¢er) { { auto p = QPainter(&qr); const auto x = (qr.width() - center.width()) / 2; const auto y = (qr.height() - center.height()) / 2; p.drawImage(x, y, center); } return qr; } } // namespace Qr ))entry(name qr_generate.hnode(typeregularcontentsť// This file is part of Desktop App Toolkit, // a set of libraries for developing nice desktop applications. // // For license and copyright information please follow this link: // https://github.com/desktop-app/legal/blob/master/LEGAL // #pragma once #include #include #include #include namespace Qr { enum class Redundancy { Low, Medium, Quartile, High, Default = Medium }; struct Data { int size = 0; Redundancy redundancy = Redundancy::Default; std::vector values; // size x size }; [[nodiscard]] Data Encode( const QString &text, Redundancy redundancy = Redundancy::Default); [[nodiscard]] QImage Generate( const Data &data, int pixel, QColor fg = Qt::black, QColor bg = Qt::transparent); [[nodiscard]] int ReplaceSize(const Data &data, int pixel); [[nodiscard]] QImage ReplaceCenter(QImage qr, const QImage ¢er); } // namespace Qr )))))