Viewing is open to everyone. Downloading and starring need a GitHub sign-in. Sign in with GitHub

ckey.h

v0.1.0-build.85 Latest Commit ede38f5

Size
36 KB
Lines
637
Language
cpp
SHA-256
9cfcc8b7d66a…
View on GitHub

Sign in with GitHub to download. Each download is recorded with your GitHub username.

1#ifndef CKEY_CKEY_H
2#define CKEY_CKEY_H
3
4#include <stddef.h>
5#include <stdint.h>
6
7#if defined(_WIN32) && defined(CKEY_SHARED)
8# ifdef CKEY_BUILDING
9# define CKEY_API __declspec(dllexport)
10# else
11# define CKEY_API __declspec(dllimport)
12# endif
13#elif defined(__GNUC__) && defined(CKEY_SHARED)
14# define CKEY_API __attribute__((visibility("default")))
15#else
16# define CKEY_API
17#endif
18
19#ifdef __cplusplus
20extern "C" {
21#endif
22
23#define CKEY_ABI_VERSION 4u
24#define CKEY_SOFTWARE_VERSION "0.1.0-build.85"
25#define CKEY_FORMAT_VERSION 14u
26#define CKEY_TEXT_FORMAT_VERSION 25u
27#define CKEY_LOCATOR_PAYLOAD_BYTES 16u
28#define CKEY_TEXT_PAYLOAD_MAX_BYTES 200u
29#define CKEY_TEXT_CODEWORD_BYTES 255u
30#define CKEY_EXTENDED_FORMAT_VERSION 26u
31#define CKEY_EXTENDED_PAYLOAD_MAX_BYTES 2048u
32#define CKEY_EXTENDED_BLOCK_COUNT 10u
33#define CKEY_EXTENDED_DATA_BYTES_PER_BLOCK 223u /* legacy Format 0.10/0.16 */
34#define CKEY_EXTENDED_PARITY_BYTES_PER_BLOCK 32u /* legacy Format 0.10/0.16 */
35#define CKEY_EXTENDED_CODEWORD_BYTES_PER_BLOCK 255u
36#define CKEY_EXTENDED_CODEWORD_BYTES (CKEY_EXTENDED_BLOCK_COUNT * CKEY_EXTENDED_CODEWORD_BYTES_PER_BLOCK)
37#define CKEY_EXTENDED_TIER_COUNT 7u
38
39typedef enum ckey_extended_tier {
40 CKEY_EXTENDED_TIER_AUTO = 0,
41 CKEY_EXTENDED_TIER_256 = 256,
42 CKEY_EXTENDED_TIER_384 = 384,
43 CKEY_EXTENDED_TIER_512 = 512,
44 CKEY_EXTENDED_TIER_768 = 768,
45 CKEY_EXTENDED_TIER_1024 = 1024,
46 CKEY_EXTENDED_TIER_1536 = 1536,
47 CKEY_EXTENDED_TIER_2048 = 2048
48} ckey_extended_tier;
49#define CKEY_LOCATOR_SIDE 29u
50#define CKEY_MAX_RENDER_SCALE 256u
51#define CKEY_MAX_SYMBOL_MODULES (CKEY_LOCATOR_SIDE * CKEY_LOCATOR_SIDE)
52
53typedef enum ckey_status {
54 CKEY_OK = 0,
55 CKEY_INVALID_ARGUMENT = 1,
56 CKEY_UNSUPPORTED_PROFILE = 2,
57 CKEY_INSUFFICIENT_BUFFER = 3,
58 CKEY_INTERNAL_ERROR = 4,
59 CKEY_UNCORRECTABLE = 5,
60 CKEY_INTEGRITY_FAILURE = 6,
61 CKEY_EXPIRED = 7
62} ckey_status;
63
64typedef struct ckey_symbol {
65 uint32_t abi_version;
66 uint32_t side;
67 /* Systematic RS codeword for the locator profile; geometry is derived. */
68 uint8_t codeword[77];
69} ckey_symbol;
70
71/* Profile 2: a high-capacity text/binary symbol. Its 255-byte codeword is a
72 * full-length RS(255,210) block and carries up to 200 payload bytes. */
73typedef struct ckey_text_symbol {
74 uint32_t abi_version;
75 uint32_t side;
76 uint32_t payload_length;
77 uint8_t codeword[CKEY_TEXT_CODEWORD_BYTES];
78} ckey_text_symbol;
79
80/* Profile 3: ten interleaved systematic RS(255,k) blocks. Tiered formats
81 * (0.17 and later) select k from the declared capacity/ECC tier; legacy
82 * Format 0.10/0.16 uses k=223.
83 * The public codeword is byte-interleaved by block. */
84typedef struct ckey_extended_symbol {
85 uint32_t abi_version;
86 uint32_t side;
87 uint32_t payload_length;
88 uint8_t codeword[CKEY_EXTENDED_CODEWORD_BYTES];
89} ckey_extended_symbol;
90
91typedef struct ckey_diagnostics {
92 uint32_t abi_version;
93 ckey_status status;
94 uint32_t corrected_symbols;
95 uint32_t erasures_used;
96 char message[96];
97} ckey_diagnostics;
98
99CKEY_API uint32_t ckey_get_abi_version(void);
100CKEY_API const char *ckey_get_software_version(void);
101CKEY_API ckey_status ckey_encode_locator(const uint8_t *payload, size_t payload_len,
102 ckey_symbol *out_symbol,
103 ckey_diagnostics *out_diagnostics);
104CKEY_API ckey_status ckey_decode_locator(const uint8_t *codeword, size_t codeword_len,
105 uint8_t *out_payload, size_t out_capacity,
106 ckey_diagnostics *out_diagnostics);
107CKEY_API ckey_status ckey_encode_text(const uint8_t *payload, size_t payload_len,
108 ckey_text_symbol *out_symbol,
109 ckey_diagnostics *out_diagnostics);
110CKEY_API ckey_status ckey_decode_text(const uint8_t *codeword, size_t codeword_len,
111 uint8_t *out_payload, size_t out_capacity,
112 size_t *out_payload_len,
113 ckey_diagnostics *out_diagnostics);
114CKEY_API ckey_status ckey_encode_extended(const uint8_t *payload, size_t payload_len,
115 ckey_extended_symbol *out_symbol,
116 ckey_diagnostics *out_diagnostics);
117/* Select a minimum payload-capacity tier (256..2048 bytes). AUTO selects the
118 * smallest tier that fits. Smaller tiers devote more of the fixed optical
119 * codeword to Reed-Solomon parity. */
120CKEY_API ckey_status ckey_encode_extended_tiered(const uint8_t *payload, size_t payload_len,
121 ckey_extended_tier tier,
122 ckey_extended_symbol *out_symbol,
123 ckey_diagnostics *out_diagnostics);
124CKEY_API ckey_status ckey_decode_extended(const uint8_t *codeword, size_t codeword_len,
125 uint8_t *out_payload, size_t out_capacity,
126 size_t *out_payload_len,
127 ckey_diagnostics *out_diagnostics);
128/*
129 * Decode an 8-bit grayscale image of one planar CKey locator. quad_xy contains
130 * four perimeter points (x0,y0,...,x3,y3) in cyclic order; either winding and
131 * any starting corner are accepted. They map to canonical (0,0), (29,0),
132 * (29,29), (0,29) coordinates. The caller supplies the detected symbol
133 * quadrilateral. This function rectifies perspective but does not locate a
134 * symbol in a full frame or correct lens distortion. The image is one-channel
135 * 8-bit grayscale; both contrast polarities are supported.
136 */
137CKEY_API ckey_status ckey_decode_gray8(const uint8_t *gray, size_t width,
138 size_t height, size_t stride,
139 const double quad_xy[8],
140 uint8_t *out_payload,
141 size_t out_capacity,
142 ckey_diagnostics *out_diagnostics);
143/* Projective grayscale decoders for the two dense profiles. These use the
144 * same caller-supplied quadrilateral contract as ckey_decode_gray8. They read
145 * the current optical layout only (Profile 2 Format 0.25, Profile 3 Format
146 * 0.26); the byte-codeword decoders above also accept legacy frames. Payload
147 * acceptance is gated by RS correction and CRC-32C. */
148CKEY_API ckey_status ckey_decode_text_gray8(const uint8_t *gray, size_t width,
149 size_t height, size_t stride,
150 const double quad_xy[8],
151 uint8_t *out_payload,
152 size_t out_capacity,
153 size_t *out_payload_len,
154 ckey_diagnostics *out_diagnostics);
155CKEY_API ckey_status ckey_decode_extended_gray8(const uint8_t *gray, size_t width,
156 size_t height, size_t stride,
157 const double quad_xy[8],
158 uint8_t *out_payload,
159 size_t out_capacity,
160 size_t *out_payload_len,
161 ckey_diagnostics *out_diagnostics);
162/*
163 * Returns a continuous positive fit score for the Grid, Ring, and Ray Keys
164 * under a proposed quadrilateral. Zero means no usable calibration evidence;
165 * a negative value means invalid arguments. This never accepts a payload and
166 * is intended for callers that refine localization before decoding.
167 */
168CKEY_API double ckey_calibration_score_gray8(const uint8_t *gray, size_t width,
169 size_t height, size_t stride,
170 const double quad_xy[8]);
171/* Equivalent continuous key-fit score for Profile 2's 29-unit dense field.
172 * It samples Profile 2's own Grid, Ring, and Ray keys and must not be
173 * substituted with the Profile 1 score. */
174CKEY_API double ckey_calibration_score_text_gray8(const uint8_t *gray, size_t width,
175 size_t height, size_t stride,
176 const double quad_xy[8]);
177/* Equivalent continuous key-fit score for Profile 3's 116-unit field. The
178 * locator uses this during projective geometry refinement; it never accepts a
179 * payload and does not depend on a fixed pixel size. */
180CKEY_API double ckey_calibration_score_extended_gray8(const uint8_t *gray, size_t width,
181 size_t height, size_t stride,
182 const double quad_xy[8]);
183/*
184 * Full-frame localization. ckey_find_borders_gray8 finds candidate CKey
185 * border outlines (either polarity) in a whole grayscale frame and writes up
186 * to max_quads perspective quadrilaterals (8 doubles each: the frame stroke's
187 * outer edge), most border-like first; it returns the count. Pass a border to
188 * ckey_refine_quad_gray8 to fit it to one profile's canonical field using
189 * that profile's fixed keys; it returns the calibration score (negative for
190 * invalid arguments) and writes a quadrilateral suitable for the matching
191 * ckey_decode_*_gray8 call. Neither function accepts a payload.
192 */
193typedef enum ckey_field {
194 CKEY_FIELD_SCALABLE = 0, /* Profile 0: any version, identified by its keys */
195 CKEY_FIELD_LOCATOR = 1,
196 CKEY_FIELD_TEXT = 2,
197 CKEY_FIELD_EXTENDED = 3
198} ckey_field;
199CKEY_API size_t ckey_find_borders_gray8(const uint8_t *gray, size_t width, size_t height,
200 size_t stride, double *out_quads, size_t max_quads);
201CKEY_API double ckey_refine_quad_gray8(const uint8_t *gray, size_t width, size_t height,
202 size_t stride, const double border_quad[8],
203 ckey_field field, double out_quad[8]);
204/*
205 * Profile 0: the scalable family (Format 0.27). One skeleton (frame, finders,
206 * polar centre) at every size; version 1..31 sets the module pitch, from
207 * Profile 1-like modules (v1, 29 across) to Profile 3 density (v31, 232
208 * across), about 7% finer per step. The Grid, Ring, and Ray Keys are drawn at
209 * each version's own module and slot pitch, so a reader identifies the
210 * version from them; the error-corrected frame repeats it. Every optical site
211 * carries a codeword bit at one of four correction levels.
212 */
213#define CKEY_SCALABLE_FORMAT_VERSION 27u
214#define CKEY_SCALABLE_PRINT_FORMAT_VERSION 28u
215/* Profile 0 layout families. STANDARD is Format 0.27. PRINT (Format 0.28,
216 * "P0.1") keeps every mark and gap at least about one module (equal-arc ring
217 * slots, larger dots, wider and fewer rays) so symbols survive small prints:
218 * lower capacity per version, readable at a smaller printed size. */
219typedef enum ckey_scalable_layout {
220 CKEY_LAYOUT_STANDARD = 0,
221 CKEY_LAYOUT_PRINT = 1
222} ckey_scalable_layout;
223#define CKEY_SCALABLE_VERSION_COUNT 31u
224#define CKEY_SCALABLE_MAX_CODEWORD_BYTES 8192u
225typedef enum ckey_ecc_level {
226 CKEY_ECC_L = 0, /* ~7% of each block correctable */
227 CKEY_ECC_M = 1, /* ~14% */
228 CKEY_ECC_Q = 2, /* ~22% */
229 CKEY_ECC_H = 3 /* ~30% */
230} ckey_ecc_level;
231typedef struct ckey_scalable_symbol {
232 uint32_t abi_version;
233 uint32_t version; /* 1..CKEY_SCALABLE_VERSION_COUNT */
234 uint32_t ecc_level; /* ckey_ecc_level */
235 uint32_t layout; /* ckey_scalable_layout */
236 uint32_t payload_length;
237 uint32_t codeword_length; /* bytes used in codeword[] */
238 uint8_t codeword[CKEY_SCALABLE_MAX_CODEWORD_BYTES];
239} ckey_scalable_symbol;
240/* Modules across the field for a version (29..232); 0 if invalid. The
241 * module pitch in the 29-unit canonical field is 29 / modules. */
242CKEY_API uint32_t ckey_scalable_modules_across(uint32_t version);
243/* Maximum payload bytes for a version, level, and layout; 0 if invalid. */
244CKEY_API size_t ckey_scalable_capacity(uint32_t version, ckey_ecc_level level, ckey_scalable_layout layout);
245/* version 0 selects the smallest version whose capacity fits the payload. */
246CKEY_API ckey_status ckey_encode_scalable(const uint8_t *payload, size_t payload_len,
247 uint32_t version, ckey_ecc_level level,
248 ckey_scalable_layout layout,
249 ckey_scalable_symbol *out_symbol,
250 ckey_diagnostics *out_diagnostics);
251CKEY_API ckey_status ckey_decode_scalable(const uint8_t *codeword, size_t codeword_len,
252 uint32_t version, ckey_scalable_layout layout,
253 uint8_t *out_payload, size_t out_capacity,
254 size_t *out_payload_len, ckey_diagnostics *out_diagnostics);
255/* Identifies the layout and version from the keys, then decodes.
256 * out_version and out_layout may be NULL. */
257CKEY_API ckey_status ckey_decode_scalable_gray8(const uint8_t *gray, size_t width,
258 size_t height, size_t stride,
259 const double quad_xy[8],
260 uint8_t *out_payload, size_t out_capacity,
261 size_t *out_payload_len, uint32_t *out_version,
262 uint32_t *out_layout,
263 ckey_diagnostics *out_diagnostics);
264/* Best key fit over all versions for a proposed quadrilateral (negative for
265 * invalid arguments); out_version (may be NULL) receives that version. */
266CKEY_API double ckey_calibration_score_scalable_gray8(const uint8_t *gray, size_t width,
267 size_t height, size_t stride,
268 const double quad_xy[8],
269 uint32_t *out_version);
270CKEY_API ckey_status ckey_render_scalable_svg(const ckey_scalable_symbol *symbol, uint32_t scale,
271 char *out_utf8, size_t out_capacity,
272 size_t *out_required);
273/*
274 * Profile 0 design system. Styling changes how a symbol is painted, never
275 * where a reader samples: site geometry, the frame, finders, keys, and the
276 * quiet margin are fixed. Payload marks may change shape within their own
277 * cells; colour, materials, and a logo are free within checked limits.
278 * ckey_check_scalable_style validates a style against a symbol (contrast,
279 * effect limits, logo error budget) and explains any refusal; the styled
280 * renderer refuses any style that fails the check.
281 */
282typedef struct ckey_rgb { uint8_t r, g, b; } ckey_rgb;
283typedef enum ckey_module_shape {
284 CKEY_MODULE_ROUNDED = 0, /* default rounded square */
285 CKEY_MODULE_SQUARE = 1,
286 CKEY_MODULE_CIRCLE = 2,
287 CKEY_MODULE_DIAMOND = 3
288} ckey_module_shape;
289typedef enum ckey_ring_style {
290 CKEY_RING_JOINED = 0, /* default: adjacent dots join into arcs */
291 CKEY_RING_DOTS = 1 /* every ring mark a separate dot */
292} ckey_ring_style;
293typedef enum ckey_ray_cap {
294 CKEY_RAY_ROUND = 0, /* default */
295 CKEY_RAY_FLAT = 1
296} ckey_ray_cap;
297/*
298 * Optical-safety policy (specification §4), carried by the style:
299 * STRICT (default) renderers refuse a style that fails the safety rules.
300 * ADAPTIVE renderers repair the style by fixed rules first (ink pushed to
301 * black or white, failing accents dropped, depth limited, logo
302 * plated or shrunk), then apply the strict rules.
303 * ckey_certify_scalable_style reports every change.
304 * OVERRIDE renderers draw the style as given, skipping the safety rules.
305 * The caller accepts an uncertified symbol; the style check and
306 * certification still report what fails.
307 * Values a renderer cannot draw (unknown shapes, NaN, out-of-range effects)
308 * are refused under every policy.
309 */
310typedef enum ckey_style_policy {
311 CKEY_POLICY_STRICT = 0,
312 CKEY_POLICY_ADAPTIVE = 1,
313 CKEY_POLICY_OVERRIDE = 2
314} ckey_style_policy;
315typedef struct ckey_style {
316 ckey_rgb background; /* centre of the background */
317 ckey_rgb background_edge; /* edge colour (radial vignette); equal = solid */
318 ckey_rgb ink; /* marks; light side of a metallic sheen */
319 ckey_rgb ink_shade; /* sheen shade across the symbol; equal = flat ink */
320 ckey_rgb accents[4]; /* optional accent colours for payload marks */
321 uint32_t accent_count; /* 0..4 */
322 uint32_t accent_percent; /* 0..100: share of payload marks drawn in accents */
323 ckey_module_shape modules;
324 ckey_ring_style rings;
325 ckey_ray_cap rays;
326 double bevel; /* 0..1 embossed highlight */
327 double shadow; /* 0..1 drop-shadow depth */
328 double texture; /* 0..1 background grain */
329 const char *logo_href; /* image URL or data: URI; NULL or "" for none */
330 double logo_radius; /* canonical units; 0 = the empty centre's size */
331 uint32_t logo_plate; /* 1: background disc behind the logo */
332 uint32_t policy; /* ckey_style_policy; 0 = strict (ABI 4) */
333} ckey_style;
334/* Fills the default style: black ink on a white background (spec §4). */
335CKEY_API void ckey_style_default(ckey_style *style);
336typedef enum ckey_style_preset {
337 CKEY_PRESET_PLAIN = 0, /* the default style */
338 CKEY_PRESET_CONCEPT = 1 /* cream metallic ink on a dark vignette, accents, depth, grain */
339} ckey_style_preset;
340/* Fills a named preset; CKEY_INVALID_ARGUMENT for an unknown preset. */
341CKEY_API ckey_status ckey_style_preset_fill(ckey_style *style, ckey_style_preset preset);
342/* Largest logo radius for a version (keeps the Ring Key and outer ring). */
343CKEY_API double ckey_scalable_logo_max_radius(uint32_t version, ckey_scalable_layout layout);
344/* Share (0..) of the worst RS block's correction capacity consumed by marks
345 * hidden under a logo of this radius; negative for invalid arguments. A
346 * style is accepted up to 0.5, leaving half the correction for damage. */
347CKEY_API double ckey_scalable_logo_load(const ckey_scalable_symbol *symbol, double logo_radius);
348CKEY_API ckey_status ckey_check_scalable_style(const ckey_scalable_symbol *symbol, const ckey_style *style,
349 ckey_diagnostics *out_diagnostics);
350CKEY_API ckey_status ckey_render_scalable_styled_svg(const ckey_scalable_symbol *symbol, const ckey_style *style,
351 uint32_t scale, char *out_utf8, size_t out_capacity,
352 size_t *out_required);
353/*
354 * Print rendering: black ink on paper on the printer's dot grid, width_in
355 * inches wide (the 31-unit canvas including the quiet margin) at dpi. Each
356 * out_gray byte is 0 (ink) or 255 (paper): there is no grey for a printer
357 * to halftone into noise. style may be NULL (default mark shapes); colours,
358 * materials, and logo images are screen styling and are not printed. Call
359 * with out_gray NULL to get *out_side; the buffer needs side * side bytes.
360 */
361CKEY_API ckey_status ckey_render_scalable_print(const ckey_scalable_symbol *symbol, const ckey_style *style,
362 double width_in, double dpi, uint8_t *out_gray,
363 size_t out_capacity, size_t *out_side);
364/*
365 * Canonical geometry (specification §2.1). A Profile 0 symbol, with a
366 * style's mark shapes, is a list of primitives in canonical units: the
367 * symbol field spans 0..29 on both axes and the full canvas, including the
368 * quiet margin, spans -1..30 (31 units). x grows right and y grows down.
369 * Every backend (raster, PDF, SVG) draws exactly these shapes, in this order
370 * (painter's order: a LOGO_PLATE shape erases the ink drawn before it).
371 *
372 * kind p[0..4] width
373 * RECT x, y, w, h, corner radius -
374 * RECT_OUTLINE x, y, w, h, corner radius stroke width (centred on the outline)
375 * CIRCLE cx, cy, r -
376 * DIAMOND cx, cy, d (vertices at ±d) -
377 * LINE x0, y0, x1, y1 stroke width; round_cap
378 * ARC cx, cy, r, start, sweep stroke width; round_cap
379 * LOGO cx, cy, r (circular clip) -
380 *
381 * Arc angles are radians in canvas coordinates: 0 points along +x, and a
382 * positive sweep turns clockwise on screen (toward +y).
383 */
384typedef enum ckey_shape_kind {
385 CKEY_SHAPE_RECT = 0, CKEY_SHAPE_RECT_OUTLINE = 1, CKEY_SHAPE_CIRCLE = 2, CKEY_SHAPE_DIAMOND = 3,
386 CKEY_SHAPE_LINE = 4, CKEY_SHAPE_ARC = 5, CKEY_SHAPE_LOGO = 6
387} ckey_shape_kind;
388typedef enum ckey_layer {
389 CKEY_LAYER_FRAME = 0, CKEY_LAYER_FINDERS = 1, CKEY_LAYER_GRID_KEY = 2, CKEY_LAYER_POLAR_KEYS = 3,
390 CKEY_LAYER_DATA_POLAR = 4, CKEY_LAYER_DATA_CARTESIAN = 5, CKEY_LAYER_LOGO_PLATE = 6, CKEY_LAYER_LOGO = 7
391} ckey_layer;
392#define CKEY_PAINT_INK (-1) /* the style's ink (or its sheen) */
393#define CKEY_PAINT_BACKGROUND (-2) /* erases ink: the logo plate */
394typedef struct ckey_shape {
395 uint32_t kind; /* ckey_shape_kind */
396 uint32_t layer; /* ckey_layer */
397 int32_t paint; /* CKEY_PAINT_INK, CKEY_PAINT_BACKGROUND, or accent index 0..3 */
398 uint32_t round_cap; /* LINE and ARC: 1 round caps, 0 flat (butt) caps */
399 double p[5];
400 double width;
401} ckey_shape;
402/* Writes the symbol's primitives (style may be NULL for the default shapes).
403 * Call with out NULL to get *out_count. The style must pass the style check. */
404CKEY_API ckey_status ckey_scalable_geometry(const ckey_scalable_symbol *symbol, const ckey_style *style,
405 ckey_shape *out, size_t out_capacity, size_t *out_count);
406
407/*
408 * Direct raster backend (specification §2.2): geometry to pixels, with no
409 * SVG, no file system, no network, and no graphics library. The output is
410 * size_px x size_px pixels covering the full 31-unit canvas, rows top to
411 * bottom with no padding.
412 *
413 * Policies (benchmark them; none is universally best):
414 * COVERAGE antialiased: each pixel's ink is its supersampled area coverage.
415 * BINARY each pixel is ink when at least half its samples are covered.
416 * HYBRID coverage, but marks thinner than min_feature_px (default 1 px)
417 * are widened to it so thin rays and dots stay continuous.
418 * HYBRID changes drawn widths, never the encoded topology.
419 *
420 * Formats: RGBA8 (straight, non-premultiplied sRGB alpha) or GRAY8 (Rec. 709
421 * luma of the opaque result). transparent = 1 (RGBA8 only) paints no
422 * background, vignette or texture: unpainted pixels have alpha 0. materials
423 * = 0 draws flat ink (no sheen, depth, or grain). logo (optional) is drawn
424 * into the style's logo circle; without it the logo area shows only the
425 * plate, if any.
426 */
427typedef enum ckey_raster_policy { CKEY_RASTER_COVERAGE = 0, CKEY_RASTER_BINARY = 1, CKEY_RASTER_HYBRID = 2 } ckey_raster_policy;
428typedef enum ckey_pixel_format { CKEY_PIXELS_RGBA8 = 0, CKEY_PIXELS_GRAY8 = 1 } ckey_pixel_format;
429typedef struct ckey_logo_image {
430 const uint8_t *rgba; /* straight-alpha RGBA8 */
431 uint32_t width, height;
432 size_t stride; /* bytes per row; 0 = width * 4 */
433} ckey_logo_image;
434typedef struct ckey_raster_options {
435 uint32_t abi_version; /* CKEY_ABI_VERSION */
436 uint32_t size_px; /* 16..16384 */
437 uint32_t policy; /* ckey_raster_policy */
438 uint32_t format; /* ckey_pixel_format */
439 uint32_t transparent;
440 uint32_t supersample; /* samples per pixel axis, 1..8 */
441 double min_feature_px; /* HYBRID only; > 0 */
442 uint32_t materials;
443 const ckey_logo_image *logo;
444} ckey_raster_options;
445/* Defaults: 512 px, COVERAGE, RGBA8, opaque, 4x4 samples, 1 px, materials on. */
446CKEY_API void ckey_raster_options_default(ckey_raster_options *options);
447/* style may be NULL (default style). Call with out NULL to get *out_required
448 * (size_px * size_px * 4 for RGBA8, size_px * size_px for GRAY8). */
449CKEY_API ckey_status ckey_render_scalable_raster(const ckey_scalable_symbol *symbol, const ckey_style *style,
450 const ckey_raster_options *options, uint8_t *out,
451 size_t out_capacity, size_t *out_required);
452
453/*
454 * PNG export (specification §2.5), from any pixel buffer. Compressed with
455 * DEFLATE (fixed Huffman codes and LZ77); no zlib dependency. GRAY1 writes a
456 * 1-bit image from GRAY8 input (< 128 is black) for print bitmaps. dpi > 0
457 * records the physical resolution (pHYs). Call with out NULL for the size.
458 */
459typedef enum ckey_png_format { CKEY_PNG_RGBA8 = 0, CKEY_PNG_GRAY8 = 1, CKEY_PNG_GRAY1 = 2 } ckey_png_format;
460CKEY_API ckey_status ckey_encode_png(const uint8_t *pixels, uint32_t width, uint32_t height, size_t stride,
461 ckey_png_format format, double dpi, uint8_t *out, size_t out_capacity,
462 size_t *out_required);
463
464/*
465 * PDF vector export (specification §2.3-2.4): a one-page PDF 1.4 whose page
466 * is width_pt points wide (72 points per inch) and holds the exact geometry
467 * as filled and stroked paths (arcs as cubic Béziers). Flat colours only:
468 * ink, accents and the background (omitted when transparent = 1). Screen
469 * materials (sheen, depth, grain) and logos are not exported. Call with out
470 * NULL for the size.
471 */
472CKEY_API ckey_status ckey_render_scalable_pdf(const ckey_scalable_symbol *symbol, const ckey_style *style,
473 double width_pt, uint32_t transparent, uint8_t *out,
474 size_t out_capacity, size_t *out_required);
475
476/*
477 * Optical certification (specification §4, acceptance criteria 5 and 6).
478 * A style is certified only by decoding it through modelled photographs,
479 * never from a contrast ratio alone, and it is judged against plain black on
480 * white for the same symbol.
481 *
482 * The sweep renders the symbol with the native raster, composites it over
483 * every backdrop, places it in a cluttered frame tilted by rotation and
484 * keystone, adds glare, defocus and sensor noise, and runs the full reader
485 * pipeline (ckey_find_borders_gray8, ckey_refine_quad_gray8, decode). It
486 * walks a ladder of camera resolutions (9, 7, 5.5, 4.5, 3.75, 3 pixels per
487 * module) to find the smallest one at which the style still decodes. The
488 * grade compares that with plain black on white:
489 * excellent decodes as small as plain does
490 * good needs one ladder step more than plain
491 * marginal needs two steps more
492 * fails three or more, never decodes, or needs more than
493 * model.px_per_module
494 * certified = 1 needs the safety rules to pass and a grade of good or
495 * better. trials and trials_passed count the modelled photos.
496 *
497 * Transparent symbols: the renderer cannot see what lies behind them, so
498 * transparent = 1 needs a backdrop (solid colours and/or the host's pixels
499 * behind the symbol). Without one, certification reports certified = 0
500 * ("background unknown") and never claims readability.
501 *
502 * The model is a model, not a measurement. Real-camera tests remain the
503 * evidence (spec §11.6).
504 */
505typedef struct ckey_backdrop {
506 const ckey_rgb *colours; /* solid backgrounds the symbol may sit on */
507 uint32_t colour_count; /* 0..16 */
508 const uint8_t *rgba; /* optional host pixels behind the symbol (straight RGBA8), */
509 uint32_t width, height; /* stretched over the symbol's full 31-unit canvas */
510 size_t stride; /* 0 = width * 4 */
511} ckey_backdrop;
512typedef struct ckey_scan_model {
513 uint32_t abi_version; /* CKEY_ABI_VERSION */
514 double px_per_module; /* the style must decode at this resolution or finer; default 8 */
515 double blur_px; /* Gaussian defocus sigma in camera pixels; default 0.9 */
516 double noise; /* sensor noise sigma, fraction of full scale; default 0.03 */
517 double glare; /* contrast compression 0..0.9: luma -> luma * (1 - g) + g; default 0.2 */
518 uint32_t trials; /* accepted for compatibility (1..16); the sweep sets its own scenes */
519} ckey_scan_model;
520#define CKEY_ADJUST_INK 1u /* ink replaced by black or white */
521#define CKEY_ADJUST_ACCENTS 2u /* accents removed */
522#define CKEY_ADJUST_EFFECTS 4u /* bevel or shadow limited */
523#define CKEY_ADJUST_LOGO 8u /* logo plated or shrunk */
524#define CKEY_ADJUST_BACKGROUND 16u /* the symbol was given its own background */
525#define CKEY_ADJUST_MATERIALS 32u /* sheen, vignette, depth and grain removed after a failed model test */
526typedef struct ckey_certification {
527 uint32_t abi_version;
528 uint32_t certified; /* 1: decoded in every modelled trial */
529 uint32_t policy; /* the policy that was applied */
530 uint32_t adjustments; /* CKEY_ADJUST_* bits (adaptive policy) */
531 uint32_t trials, trials_passed;
532 double min_contrast; /* informative WCAG-style ratio over all ink/background pairs */
533 double feature_separation; /* luma separation of mark vs gap pixels after compositing: 5th/95th percentile gap, 0..1; <= 0 means they overlap */
534 double logo_load; /* share of the worst block's correction hidden by the logo */
535 char message[256]; /* the reason for refusal, or the list of changes and warnings */
536} ckey_certification;
537CKEY_API void ckey_scan_model_default(ckey_scan_model *model);
538/*
539 * Certifies style (NULL = default) on symbol under style->policy:
540 * STRICT returns CKEY_INVALID_ARGUMENT unless the style is certified.
541 * ADAPTIVE repairs the style; if it still fails the model, removes
542 * materials and retries. On success writes the style to render
543 * to out_style (may be NULL) and returns CKEY_OK.
544 * OVERRIDE returns CKEY_OK whatever happens; out->certified says whether
545 * the symbol would scan under the model.
546 * backdrop may be NULL for an opaque symbol (its own background is used).
547 * model may be NULL for the default. transparent selects transparent output.
548 */
549CKEY_API ckey_status ckey_certify_scalable_style(const ckey_scalable_symbol *symbol, const ckey_style *style,
550 uint32_t transparent, const ckey_backdrop *backdrop,
551 const ckey_scan_model *model, ckey_style *out_style,
552 ckey_certification *out);
553
554/*
555 * .ckey interchange (specification §2.5; spec/CKey-Interchange-v1.md). A
556 * compact, optional container for a Profile 0 symbol and its style; it is
557 * never needed to render or display one. The codeword carries the payload
558 * in clear, so a .ckey file is exactly as sensitive as its payload.
559 *
560 * ckey_write_interchange: style may be NULL. Call with out NULL for the size.
561 * ckey_read_interchange: strict, bounded parser. It rejects anything
562 * malformed, oversized (CKEY_INTERCHANGE_MAX_BYTES), failing its CRC-32C,
563 * whose codeword does not decode, or with an unknown required record.
564 * out_style may be NULL. A logo reference is copied to logo_href (may be
565 * NULL; NUL-terminated, at most logo_cap bytes) and out_style->logo_href
566 * points at it; without a buffer the logo is dropped.
567 */
568#define CKEY_INTERCHANGE_VERSION 1u
569#define CKEY_INTERCHANGE_MAX_BYTES 65536u
570CKEY_API ckey_status ckey_write_interchange(const ckey_scalable_symbol *symbol, const ckey_style *style,
571 uint8_t *out, size_t out_capacity, size_t *out_required);
572CKEY_API ckey_status ckey_read_interchange(const uint8_t *data, size_t length, ckey_scalable_symbol *out_symbol,
573 ckey_style *out_style, char *logo_href, size_t logo_cap,
574 ckey_diagnostics *out_diagnostics);
575
576/* scale is pixels per module and is constrained to 1..CKEY_MAX_RENDER_SCALE. */
577CKEY_API ckey_status ckey_render_svg(const ckey_symbol *symbol, uint32_t scale,
578 char *out_utf8, size_t out_capacity,
579 size_t *out_required);
580CKEY_API ckey_status ckey_render_text_svg(const ckey_text_symbol *symbol, uint32_t scale,
581 char *out_utf8, size_t out_capacity,
582 size_t *out_required);
583CKEY_API ckey_status ckey_render_extended_svg(const ckey_extended_symbol *symbol, uint32_t scale,
584 char *out_utf8, size_t out_capacity,
585 size_t *out_required);
586
587/*
588 * Safe Actions (spec/CKey-Safe-Actions.md): a signed request, carried as the
589 * text payload "ckey-action:1:<base64url envelope>", for an action the
590 * receiving app has registered. The core parses, signs and verifies it; the
591 * SDKs decide whether it runs. A payload that fails any check is ordinary text
592 * and falls back to the show-and-approve rule.
593 */
594typedef enum ckey_safe_action_scope {
595 CKEY_SCOPE_SAME_DEVICE = 0, /* the result stays in the app on the scanning device */
596 CKEY_SCOPE_CROSS_DEVICE = 1 /* approves something elsewhere, e.g. signs in a browser */
597} ckey_safe_action_scope;
598
599typedef struct ckey_safe_action {
600 uint32_t abi_version;
601 uint32_t scope; /* ckey_safe_action_scope */
602 char issuer[254]; /* issuing site's DNS name, lower case */
603 char app[256]; /* receiving app ID, e.g. com.viadere */
604 char action[65]; /* action name registered by the receiving app */
605 char key_id[33]; /* which issuer key signed it */
606 char request_id[65]; /* single-use request ID, 8..64 characters */
607 uint64_t issued_at; /* Unix seconds */
608 uint64_t expires_at; /* Unix seconds */
609 uint8_t params[1024]; /* action parameters; UTF-8 JSON recommended */
610 size_t params_len;
611} ckey_safe_action;
612
613/* 1 when the payload starts with "ckey-action:1:", else 0. Says nothing about validity. */
614CKEY_API int ckey_is_safe_action(const uint8_t *payload, size_t len);
615/* Reads the fields without checking the signature, e.g. to look up the issuer's key. */
616CKEY_API ckey_status ckey_safe_action_parse(const uint8_t *payload, size_t len, ckey_safe_action *out,
617 ckey_diagnostics *diag);
618/*
619 * CKEY_OK only when the Ed25519 signature matches public_key, the lifetime is at
620 * most max_lifetime_seconds (0 = 600) and now (Unix seconds) is within it, with
621 * 120 s of clock tolerance. CKEY_INTEGRITY_FAILURE for a bad signature,
622 * CKEY_EXPIRED for a valid but out-of-time envelope, CKEY_INVALID_ARGUMENT if
623 * malformed. Single use (request_id) is enforced by the issuer's server.
624 */
625CKEY_API ckey_status ckey_safe_action_verify(const uint8_t *payload, size_t len, const uint8_t public_key[32],
626 uint64_t now, uint32_t max_lifetime_seconds,
627 ckey_safe_action *out, ckey_diagnostics *diag);
628/* Issuer side: signs fields with a 32-byte Ed25519 seed into NUL-terminated text. */
629CKEY_API ckey_status ckey_safe_action_sign(const ckey_safe_action *fields, const uint8_t seed[32],
630 char *out, size_t out_capacity, size_t *out_len,
631 ckey_diagnostics *diag);
632CKEY_API void ckey_ed25519_public_key(const uint8_t seed[32], uint8_t out_public_key[32]);
633
634#ifdef __cplusplus
635}
636#endif
637#endif