Viewing is open to everyone. Downloading and starring need a GitHub sign-in.
Sign in with GitHub
ckey_gallery.cpp
v0.1.0-build.85
Latest
Commit ede38f5
Sign in with GitHub to download. Each download is recorded with your GitHub username.
| 1 | // Renders the example symbols in docs/gallery: each one is drawn with the |
| 2 | // public API, read back with the real decoder, and certified. Writes one |
| 3 | // PNG per entry plus docs/gallery/README.md. |
| 4 | // |
| 5 | // cmake --build build --target ckey-gallery |
| 6 | // ./build/ckey-gallery docs/gallery |
| 7 | <ckey/ckey.h> |
| 8 | <cstdio> |
| 9 | <cstring> |
| 10 | <functional> |
| 11 | <string> |
| 12 | <vector> |
| 13 | |
| 14 | namespace { |
| 15 | |
| 16 | struct Entry { |
| 17 | const char *file; |
| 18 | const char *title; |
| 19 | const char *about; |
| 20 | std::string payload; |
| 21 | uint32_t version; // 0 = smallest that fits |
| 22 | ckey_ecc_level level; |
| 23 | ckey_scalable_layout layout; |
| 24 | std::function<void(ckey_style &)> style; |
| 25 | }; |
| 26 | |
| 27 | ckey_rgb hex(uint32_t v) { return ckey_rgb{uint8_t(v >> 16), uint8_t(v >> 8), uint8_t(v)}; } |
| 28 | |
| 29 | bool write_file(const std::string &path, const std::vector<uint8_t> &bytes) { |
| 30 | FILE *f = std::fopen(path.c_str(), "wb"); |
| 31 | if (!f) return false; |
| 32 | const bool ok = std::fwrite(bytes.data(), 1, bytes.size(), f) == bytes.size(); |
| 33 | return std::fclose(f) == 0 && ok; |
| 34 | } |
| 35 | |
| 36 | // Reads the symbol back from a GRAY8 rendering with the reader pipeline. |
| 37 | bool reads_back(const ckey_scalable_symbol &symbol, const ckey_style &style, const std::string &expected) { |
| 38 | const uint32_t size = 768; |
| 39 | ckey_raster_options options; |
| 40 | ckey_raster_options_default(&options); |
| 41 | options.size_px = size; |
| 42 | options.format = CKEY_PIXELS_GRAY8; |
| 43 | std::vector<uint8_t> gray(size_t(size) * size); |
| 44 | size_t needed = 0; |
| 45 | if (ckey_render_scalable_raster(&symbol, &style, &options, gray.data(), gray.size(), &needed) != CKEY_OK) return false; |
| 46 | double borders[8 * 8]; |
| 47 | double quad[8]; |
| 48 | const size_t found = ckey_find_borders_gray8(gray.data(), size, size, size, borders, 8); |
| 49 | std::vector<uint8_t> out(65535); |
| 50 | for (size_t i = 0; i < found; ++i) { |
| 51 | size_t len = 0; |
| 52 | uint32_t version = 0; |
| 53 | ckey_diagnostics diag{}; |
| 54 | if (ckey_refine_quad_gray8(gray.data(), size, size, size, borders + 8 * i, CKEY_FIELD_SCALABLE, quad) > 0 && |
| 55 | ckey_decode_scalable_gray8(gray.data(), size, size, size, quad, out.data(), out.size(), &len, &version, |
| 56 | nullptr, &diag) == CKEY_OK) |
| 57 | return std::string(reinterpret_cast<const char *>(out.data()), len) == expected; |
| 58 | } |
| 59 | return false; |
| 60 | } |
| 61 | |
| 62 | std::string grade_of(const char *message) { |
| 63 | for (const char *g : {"excellent", "good", "marginal", "fails"}) { |
| 64 | const std::string token = std::string(g) + " scan margin"; |
| 65 | if (std::strstr(message, token.c_str())) return g; |
| 66 | } |
| 67 | return "n/a"; |
| 68 | } |
| 69 | |
| 70 | } // namespace |
| 71 | |
| 72 | int main(int argc, char **argv) { |
| 73 | const std::string dir = argc > 1 ? argv[1] : "docs/gallery"; |
| 74 | const std::string license_excerpt = |
| 75 | "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and " |
| 76 | "associated documentation files (the \"Software\"), to deal in the Software without restriction, including " |
| 77 | "without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell " |
| 78 | "copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the " |
| 79 | "following conditions: The above copyright notice and this permission notice shall be included in all " |
| 80 | "copies or substantial portions of the Software."; |
| 81 | const std::string vcard = |
| 82 | "BEGIN:VCARD\nVERSION:3.0\nN:Hart;Amelia;;Dr.;\nFN:Dr. Amelia Hart\nORG:Cardiology\nTEL:+15550100\nEND:VCARD"; |
| 83 | |
| 84 | const std::vector<Entry> entries = { |
| 85 | {"link-plain.png", "A link, plain", "The default style: black on white.", "https://openckey.org", 0, |
| 86 | CKEY_ECC_M, CKEY_LAYOUT_STANDARD, [](ckey_style &) {}}, |
| 87 | {"link-print.png", "A link, print layout (Profile 0.1)", "Larger marks and gaps for small printed labels.", |
| 88 | "https://openckey.org", 0, CKEY_ECC_M, CKEY_LAYOUT_PRINT, [](ckey_style &) {}}, |
| 89 | {"contact-evergreen.png", "A contact card, evergreen on paper", "Custom ink and background.", vcard, 0, |
| 90 | CKEY_ECC_M, CKEY_LAYOUT_STANDARD, |
| 91 | [](ckey_style &s) { |
| 92 | s.ink = s.ink_shade = hex(0x215D3B); |
| 93 | s.background = s.background_edge = hex(0xFCFAF6); |
| 94 | }}, |
| 95 | {"concept.png", "The Concept preset", "Cream metallic ink on a dark vignette, with accents, depth and grain.", |
| 96 | "https://openckey.org/about", 0, CKEY_ECC_Q, CKEY_LAYOUT_STANDARD, |
| 97 | [](ckey_style &s) { ckey_style_preset_fill(&s, CKEY_PRESET_CONCEPT); }}, |
| 98 | {"dots-navy.png", "Dotted rings, circle modules", "Ring marks drawn as separate dots, circular modules.", |
| 99 | "Visiting hours: 8 am to 8 pm, every day.", 0, CKEY_ECC_M, CKEY_LAYOUT_STANDARD, |
| 100 | [](ckey_style &s) { |
| 101 | s.rings = CKEY_RING_DOTS; |
| 102 | s.modules = CKEY_MODULE_CIRCLE; |
| 103 | s.rays = CKEY_RAY_FLAT; |
| 104 | s.ink = s.ink_shade = hex(0x1C2B4A); |
| 105 | }}, |
| 106 | {"accents.png", "Accent colours", "A share of the data marks drawn in accent colours.", |
| 107 | "https://openckey.org/scan", 0, CKEY_ECC_M, CKEY_LAYOUT_STANDARD, |
| 108 | [](ckey_style &s) { |
| 109 | s.accents[0] = hex(0x9A3130); |
| 110 | s.accents[1] = hex(0x215D3B); |
| 111 | s.accent_count = 2; |
| 112 | s.accent_percent = 35; |
| 113 | }}, |
| 114 | {"dark.png", "Light on dark", "Paper-coloured ink on an ink-coloured background.", "https://openckey.org", |
| 115 | 0, CKEY_ECC_M, CKEY_LAYOUT_STANDARD, |
| 116 | [](ckey_style &s) { |
| 117 | s.ink = s.ink_shade = hex(0xFCFAF6); |
| 118 | s.background = s.background_edge = hex(0x18211A); |
| 119 | }}, |
| 120 | {"dense.png", "A dense symbol", "About 560 bytes of text at level M.", license_excerpt, 0, CKEY_ECC_M, |
| 121 | CKEY_LAYOUT_STANDARD, [](ckey_style &) {}}, |
| 122 | }; |
| 123 | |
| 124 | std::string md = |
| 125 | "# Gallery\n\n" |
| 126 | "Every image here is rendered by the CKey core with `tools/gallery/ckey_gallery.cpp`, read back with the " |
| 127 | "real decoder, and certified with the modelled photo sweep (a model, not a measurement). Rebuild them " |
| 128 | "with:\n\n```sh\ncmake --build build --target ckey-gallery\n./build/ckey-gallery docs/gallery\n```\n\n" |
| 129 | "| | Symbol | Holds | Version | Reads back | Certification |\n|---|---|---|---|---|---|\n"; |
| 130 | int failures = 0; |
| 131 | for (const Entry &e : entries) { |
| 132 | ckey_scalable_symbol symbol{}; |
| 133 | ckey_diagnostics diag{}; |
| 134 | if (ckey_encode_scalable(reinterpret_cast<const uint8_t *>(e.payload.data()), e.payload.size(), e.version, |
| 135 | e.level, e.layout, &symbol, &diag) != CKEY_OK) { |
| 136 | std::fprintf(stderr, "%s: encode failed: %s\n", e.file, diag.message); |
| 137 | return 1; |
| 138 | } |
| 139 | ckey_style style; |
| 140 | ckey_style_default(&style); |
| 141 | e.style(style); |
| 142 | |
| 143 | const uint32_t size = 640; |
| 144 | ckey_raster_options options; |
| 145 | ckey_raster_options_default(&options); |
| 146 | options.size_px = size; |
| 147 | std::vector<uint8_t> rgba(size_t(size) * size * 4); |
| 148 | size_t needed = 0; |
| 149 | if (ckey_render_scalable_raster(&symbol, &style, &options, rgba.data(), rgba.size(), &needed) != CKEY_OK) { |
| 150 | std::fprintf(stderr, "%s: render refused\n", e.file); |
| 151 | return 1; |
| 152 | } |
| 153 | ckey_encode_png(rgba.data(), size, size, 0, CKEY_PNG_RGBA8, 0, nullptr, 0, &needed); |
| 154 | std::vector<uint8_t> png(needed); |
| 155 | if (ckey_encode_png(rgba.data(), size, size, 0, CKEY_PNG_RGBA8, 0, png.data(), png.size(), &needed) != CKEY_OK || |
| 156 | !write_file(dir + "/" + e.file, png)) { |
| 157 | std::fprintf(stderr, "%s: could not write PNG\n", e.file); |
| 158 | return 1; |
| 159 | } |
| 160 | |
| 161 | const bool read = reads_back(symbol, style, e.payload); |
| 162 | ckey_style adapted{}; |
| 163 | ckey_certification report{}; |
| 164 | ckey_certify_scalable_style(&symbol, &style, 0, nullptr, nullptr, &adapted, &report); |
| 165 | const std::string grade = report.certified ? "certified, " + grade_of(report.message) : "not certified (" + grade_of(report.message) + ")"; |
| 166 | if (!read) ++failures; |
| 167 | |
| 168 | char version[48]; |
| 169 | std::snprintf(version, sizeof version, "%u%s", symbol.version, e.layout == CKEY_LAYOUT_PRINT ? " (print)" : ""); |
| 170 | md += "| <img src=\"" + std::string(e.file) + "\" width=\"180\" alt=\"" + e.title + "\"> | **" + e.title + |
| 171 | "**<br>" + e.about + " | " + std::to_string(e.payload.size()) + " bytes | " + version + " | " + |
| 172 | (read ? "yes" : "**no**") + " | " + grade + " |\n"; |
| 173 | std::printf("%-22s v%-3u %4zu bytes read=%s %s\n", e.file, symbol.version, e.payload.size(), |
| 174 | read ? "yes" : "NO", grade.c_str()); |
| 175 | } |
| 176 | md += "\nCKey "; |
| 177 | md += ckey_get_software_version(); |
| 178 | md += ".\n"; |
| 179 | write_file(dir + "/README.md", std::vector<uint8_t>(md.begin(), md.end())); |
| 180 | return failures == 0 ? 0 : 2; |
| 181 | } |