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
Sign in with GitHub to download. Each download is recorded with your GitHub username.
| 1 | |
| 2 | |
| 3 | |
| 4 | <stddef.h> |
| 5 | <stdint.h> |
| 6 | |
| 7 | () &&() |
| 8 | |
| 9 | (dllexport) |
| 10 | |
| 11 | (dllimport) |
| 12 | |
| 13 | defined(__GNUC__) && defined(CKEY_SHARED) |
| 14 | ((("default"))) |
| 15 | |
| 16 | |
| 17 | |
| 18 | |
| 19 | |
| 20 | extern "C" { |
| 21 | |
| 22 | |
| 23 | 4u |
| 24 | "0.1.0-build.85" |
| 25 | 14u |
| 26 | 25u |
| 27 | 16u |
| 28 | 200u |
| 29 | 255u |
| 30 | 26u |
| 31 | 2048u |
| 32 | 10u |
| 33 | 223u /* legacy Format 0.10/0.16 */ |
| 34 | 32u /* legacy Format 0.10/0.16 */ |
| 35 | 255u |
| 36 | (CKEY_EXTENDED_BLOCK_COUNT) |
| 37 | 7u |
| 38 | |
| 39 | typedef 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 | 29u |
| 50 | 256u |
| 51 | (CKEY_LOCATOR_SIDE) |
| 52 | |
| 53 | typedef 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 | |
| 64 | typedef 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. */ |
| 73 | typedef 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. */ |
| 84 | typedef 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 | |
| 91 | typedef 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 | |
| 99 | CKEY_API uint32_t ckey_get_abi_version(void); |
| 100 | CKEY_API const char *ckey_get_software_version(void); |
| 101 | CKEY_API ckey_status ckey_encode_locator(const uint8_t *payload, size_t payload_len, |
| 102 | ckey_symbol *out_symbol, |
| 103 | ckey_diagnostics *out_diagnostics); |
| 104 | CKEY_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); |
| 107 | CKEY_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); |
| 110 | CKEY_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); |
| 114 | CKEY_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. */ |
| 120 | CKEY_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); |
| 124 | CKEY_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 | */ |
| 137 | CKEY_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. */ |
| 148 | CKEY_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); |
| 155 | CKEY_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 | */ |
| 168 | CKEY_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. */ |
| 174 | CKEY_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. */ |
| 180 | CKEY_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 | */ |
| 193 | typedef 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; |
| 199 | CKEY_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); |
| 201 | CKEY_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 | 27u |
| 214 | 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. */ |
| 219 | typedef enum ckey_scalable_layout { |
| 220 | CKEY_LAYOUT_STANDARD = 0, |
| 221 | CKEY_LAYOUT_PRINT = 1 |
| 222 | } ckey_scalable_layout; |
| 223 | 31u |
| 224 | 8192u |
| 225 | typedef 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; |
| 231 | typedef 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. */ |
| 242 | CKEY_API uint32_t ckey_scalable_modules_across(uint32_t version); |
| 243 | /* Maximum payload bytes for a version, level, and layout; 0 if invalid. */ |
| 244 | CKEY_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. */ |
| 246 | CKEY_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); |
| 251 | CKEY_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. */ |
| 257 | CKEY_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. */ |
| 266 | CKEY_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); |
| 270 | CKEY_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 | */ |
| 282 | typedef struct ckey_rgb { uint8_t r, g, b; } ckey_rgb; |
| 283 | typedef 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; |
| 289 | typedef 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; |
| 293 | typedef 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 | */ |
| 310 | typedef enum ckey_style_policy { |
| 311 | CKEY_POLICY_STRICT = 0, |
| 312 | CKEY_POLICY_ADAPTIVE = 1, |
| 313 | CKEY_POLICY_OVERRIDE = 2 |
| 314 | } ckey_style_policy; |
| 315 | typedef 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). */ |
| 335 | CKEY_API void ckey_style_default(ckey_style *style); |
| 336 | typedef 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. */ |
| 341 | CKEY_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). */ |
| 343 | CKEY_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. */ |
| 347 | CKEY_API double ckey_scalable_logo_load(const ckey_scalable_symbol *symbol, double logo_radius); |
| 348 | CKEY_API ckey_status ckey_check_scalable_style(const ckey_scalable_symbol *symbol, const ckey_style *style, |
| 349 | ckey_diagnostics *out_diagnostics); |
| 350 | CKEY_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 | */ |
| 361 | CKEY_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 | */ |
| 384 | typedef 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; |
| 388 | typedef 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 | () /* the style's ink (or its sheen) */ |
| 393 | () /* erases ink: the logo plate */ |
| 394 | typedef 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. */ |
| 404 | CKEY_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 | */ |
| 427 | typedef enum ckey_raster_policy { CKEY_RASTER_COVERAGE = 0, CKEY_RASTER_BINARY = 1, CKEY_RASTER_HYBRID = 2 } ckey_raster_policy; |
| 428 | typedef enum ckey_pixel_format { CKEY_PIXELS_RGBA8 = 0, CKEY_PIXELS_GRAY8 = 1 } ckey_pixel_format; |
| 429 | typedef 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; |
| 434 | typedef 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. */ |
| 446 | CKEY_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). */ |
| 449 | CKEY_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 | */ |
| 459 | typedef enum ckey_png_format { CKEY_PNG_RGBA8 = 0, CKEY_PNG_GRAY8 = 1, CKEY_PNG_GRAY1 = 2 } ckey_png_format; |
| 460 | CKEY_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 | */ |
| 472 | CKEY_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 | */ |
| 505 | typedef 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; |
| 512 | typedef 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 | 1u /* ink replaced by black or white */ |
| 521 | 2u /* accents removed */ |
| 522 | 4u /* bevel or shadow limited */ |
| 523 | 8u /* logo plated or shrunk */ |
| 524 | 16u /* the symbol was given its own background */ |
| 525 | 32u /* sheen, vignette, depth and grain removed after a failed model test */ |
| 526 | typedef 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; |
| 537 | CKEY_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 | */ |
| 549 | CKEY_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 | 1u |
| 569 | 65536u |
| 570 | CKEY_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); |
| 572 | CKEY_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. */ |
| 577 | CKEY_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); |
| 580 | CKEY_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); |
| 583 | CKEY_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 | */ |
| 594 | typedef 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 | |
| 599 | typedef 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. */ |
| 614 | CKEY_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. */ |
| 616 | CKEY_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 | */ |
| 625 | CKEY_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. */ |
| 629 | CKEY_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); |
| 632 | CKEY_API void ckey_ed25519_public_key(const uint8_t seed[32], uint8_t out_public_key[32]); |
| 633 | |
| 634 | |
| 635 | } |
| 636 | |
| 637 |