ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_viewer_reader.c
Go to the documentation of this file.
1
10
11#include "ra8_viewer_reader.h"
12
13#include <errno.h>
14#include <fcntl.h>
15#include <limits.h>
16#include <stdalign.h>
17#include <stddef.h>
18#include <stdint.h>
19#include <string.h>
20#include <sys/stat.h>
21#include <unistd.h>
22
23#include "jof.h"
24#include "ra8_attributes.h"
25#include "ra8_decomp_limits.h"
26#include "ra8_err.h"
28
29#ifndef O_CLOEXEC
31#define O_CLOEXEC (0)
32#endif
33
42
44typedef struct {
45 uint8_t* base;
46 size_t capacity;
47 size_t used;
48 bool valid;
50
67RA8_INTERNAL static bool internal_align_up(size_t value, size_t alignment, size_t* out)
68{
69 const size_t mask = alignment - 1U;
70 if ((alignment == 0U) || ((alignment & mask) != 0U) || (value > (SIZE_MAX - mask))) {
71 return false;
72 }
73 *out = (value + mask) & ~mask;
74 return true;
75}
76
93RA8_INTERNAL static void* internal_take(viewer_layout_t* layout, size_t bytes, size_t alignment)
94{
95 size_t start = 0U;
96 if (!layout->valid || !internal_align_up(layout->used, alignment, &start) ||
97 (start > SIZE_MAX - bytes)) {
98 layout->valid = false;
99 return nullptr;
100 }
101 const size_t end = start + bytes;
102 if ((layout->base != nullptr) && (end > layout->capacity)) {
103 layout->valid = false;
104 return nullptr;
105 }
106 void* result = (layout->base == nullptr) ? nullptr : &layout->base[start];
107 layout->used = end;
108 return result;
109}
110
127{
128 viewer_jof_t jof = {};
129 jof.cells = (uint8_t*)internal_take(layout, requirements->cell_bytes, alignof(max_align_t));
131 sizeof(ra8_keycache_cell_t),
132 alignof(ra8_keycache_cell_t));
133 jof.keys =
134 (ra8_tile_key_t*)internal_take(layout, sizeof(ra8_tile_key_t), alignof(ra8_tile_key_t));
135 jof.dims =
137 jof.buckets = (int32_t*)internal_take(layout,
138 (size_t)k_viewer_jof_buckets * sizeof(int32_t),
139 alignof(int32_t));
140 jof.scratch = (uint8_t*)internal_take(layout, requirements->scratch_bytes, alignof(max_align_t));
141 jof.cell_cap = requirements->cell_bytes;
142 jof.scratch_cap = requirements->scratch_bytes;
143 return jof;
144}
145
162{
163 viewer_comic_t comic = {};
164 comic.pages =
165 (comic_page_t*)internal_take(layout, requirements->comic_pages_bytes, alignof(comic_page_t));
166 comic.names = (char*)internal_take(layout, requirements->comic_names_bytes, alignof(char));
167 comic.page =
168 (uint8_t*)internal_take(layout, requirements->comic_page_bytes, alignof(max_align_t));
169 comic.arena =
170 (uint8_t*)internal_take(layout, requirements->comic_arena_bytes, alignof(max_align_t));
171 comic.page_cap = requirements->comic_page_bytes;
172 comic.arena_cap = requirements->comic_arena_bytes;
173 return comic;
174}
175
191RA8_INTERNAL static bool internal_ends_with(const char* path, const char* suffix)
192{
193 const size_t path_len = strlen(path);
194 const size_t suffix_len = strlen(suffix);
195 if (path_len < suffix_len) {
196 return false;
197 }
198 const char* tail = &path[path_len - suffix_len];
199 for (size_t index = 0U; index < suffix_len; ++index) {
200 char value = tail[index];
201 if ((value >= 'A') && (value <= 'Z')) {
202 value = (char)(value + ('a' - 'A'));
203 }
204 if (value != suffix[index]) {
205 return false;
206 }
207 }
208 return true;
209}
210
225{
226 if (internal_ends_with(path, ".jof")) {
227 return k_viewer_fmt_jof;
228 }
229 if (internal_ends_with(path, ".cbz") || internal_ends_with(path, ".cbr") ||
230 internal_ends_with(path, ".cbt")) {
231 return k_viewer_fmt_comic;
232 }
233 if (internal_ends_with(path, ".gz") || internal_ends_with(path, ".xz")) {
235 }
236 if (internal_ends_with(path, ".epub") || internal_ends_with(path, ".epb") ||
237 internal_ends_with(path, ".rabook") || internal_ends_with(path, ".rbk")) {
238 return k_viewer_fmt_reflow;
239 }
241}
242
254RA8_INTERNAL static void internal_stderr_write(const char* text)
255{
256 size_t offset = 0U;
257 const size_t length = strlen(text);
258 while (offset < length) {
259 const ssize_t written = write(STDERR_FILENO, &text[offset], length - offset);
260 if ((written < 0) && (errno == EINTR)) {
261 continue;
262 }
263 if (written <= 0) {
264 return;
265 }
266 offset += (size_t)written;
267 }
268}
269
284RA8_INTERNAL static ra8_err_t internal_reject(viewer_fmt_t format, const char* path)
285{
286 internal_stderr_write("ra8_viewer: '");
288 if (format == k_viewer_fmt_comic_wrap) {
289 internal_stderr_write("' recognised but wrapped comics require a streaming or "
290 "descriptor-spooled gzip/XZ output seam");
291 } else if (format == k_viewer_fmt_reflow) {
292 internal_stderr_write("' recognised but its reflow reader engine "
293 "(font + image loader) is not wired into the viewer yet");
294 } else {
295 internal_stderr_write("' has an unsupported file type");
296 }
299}
300
317{
318 const int descriptor = open(path, O_RDONLY | O_CLOEXEC);
319 if (descriptor < 0) {
320 return k_ra8_err_not_found;
321 }
322 struct stat metadata = {};
323 if ((fstat(descriptor, &metadata) != 0) || !S_ISREG(metadata.st_mode) ||
324 (metadata.st_size <= 0)) {
325 (void)close(descriptor);
326 return k_ra8_err_not_found;
327 }
328 *file =
329 (viewer_file_ctx_t){.size = (uint64_t)metadata.st_size, .fd = descriptor, .is_open = true};
330 return k_ra8_ok;
331}
332
334priv_viewer_pread(void* ctx, uint64_t offset, uint8_t* buffer, size_t length, size_t* out_read)
335{
337 if (out_read == nullptr) {
338 return k_ra8_err_null_ptr;
339 }
340 *out_read = 0U;
341 if ((file == nullptr) || (buffer == nullptr)) {
342 return k_ra8_err_null_ptr;
343 }
344 if (!file->is_open || (offset > (uint64_t)INT64_MAX)) {
346 }
347 if (offset >= file->size) {
348 return k_ra8_ok;
349 }
350 const uint64_t available = file->size - offset;
351 size_t requested = ((uint64_t)length > available) ? (size_t)available : length;
352 size_t completed = 0U;
353 while (completed < requested) {
354 size_t pass = requested - completed;
355 if (pass > (size_t)SSIZE_MAX) {
356 pass = (size_t)SSIZE_MAX;
357 }
358 const ssize_t got =
359 pread(file->fd, &((uint8_t*)buffer)[completed], pass, (off_t)(offset + (uint64_t)completed));
360 if ((got < 0) && (errno == EINTR)) {
361 continue;
362 }
363 if (got <= 0) {
364 if (got == 0) {
365 *out_read = completed;
366 return k_ra8_ok;
367 }
368 return k_ra8_err_not_found;
369 }
370 completed += (size_t)got;
371 }
372 *out_read = completed;
373 return k_ra8_ok;
374}
375
392internal_layout(const ra8_viewer_reader_requirements_t* need, uint8_t* base, size_t capacity)
393{
394 viewer_layout_t layout = {.base = base, .capacity = capacity, .used = 0U, .valid = true};
395 (void)internal_take(&layout, sizeof(ra8_viewer_reader_t), alignof(max_align_t));
396 (void)internal_take(&layout, need->framebuffer_bytes, alignof(uint16_t));
397 const size_t dimension_bytes = need->dimensions_bytes / 2U;
398 (void)internal_take(&layout, dimension_bytes, alignof(uint32_t));
399 (void)internal_take(&layout, dimension_bytes, alignof(uint32_t));
400 if (need->engine == (uint32_t)k_ra8_viewer_engine_jof) {
401 (void)internal_take_jof(&layout, need);
402 } else if (need->engine == (uint32_t)k_ra8_viewer_engine_comic) {
403 (void)internal_take_comic(&layout, need);
404 } else {
405 layout.valid = false;
406 }
407 return layout;
408}
409
424RA8_INTERNAL static bool
426{
427 if (need->engine == (uint32_t)k_ra8_viewer_engine_jof) {
428 return (need->cell_bytes != 0U) && (need->scratch_bytes != 0U) &&
429 (need->comic_pages_bytes == 0U) && (need->comic_names_bytes == 0U) &&
430 (need->comic_page_bytes == 0U) && (need->comic_arena_bytes == 0U);
431 }
432 if (need->engine == (uint32_t)k_ra8_viewer_engine_comic) {
433 return (need->cell_bytes == 0U) && (need->scratch_bytes == 0U) &&
434 (need->tile_count == (uint32_t)k_viewer_comic_page_cap) &&
435 (need->comic_pages_bytes == ((size_t)k_viewer_comic_page_cap * sizeof(comic_page_t))) &&
436 (need->comic_names_bytes == (size_t)k_viewer_comic_name_bytes) &&
437 (need->comic_page_bytes == (size_t)k_viewer_comic_page_bytes) &&
439 }
440 return false;
441}
442
458{
459 if ((need->layout_version != (uint32_t)k_viewer_layout_version) ||
460 (need->required_alignment != alignof(max_align_t)) || (need->tile_count == 0U) ||
461 (need->framebuffer_bytes !=
462 ((size_t)k_ra8_viewer_fb_width * (size_t)k_ra8_viewer_fb_height * sizeof(uint16_t))) ||
463 (need->dimensions_bytes != ((size_t)need->tile_count * 2U * sizeof(uint32_t))) ||
465 return false;
466 }
467 const viewer_layout_t layout = internal_layout(need, nullptr, 0U);
468 return layout.valid && (layout.used == need->required_bytes);
469}
470
489{
490 jof_info_t info = {};
491 ra8_err_t error = jof_parse(priv_viewer_pread, file, file->size, &info);
492 const uint64_t band = (uint64_t)info.tile_w * (uint64_t)info.tile_h * (uint64_t)info.bpp;
493 if ((error == k_ra8_ok) && (band == 0U)) {
495 }
497 if (error == k_ra8_ok) {
498 error = ra8_decomp_check_declared(&limits, file->size, band);
499 }
500 if (error != k_ra8_ok) {
501 return error;
502 }
503 const uint32_t tiles = ((uint32_t)info.height + (uint32_t)k_ra8_viewer_fb_height - 1U) /
504 (uint32_t)k_ra8_viewer_fb_height;
505 out->dimensions_bytes = (size_t)tiles * 2U * sizeof(uint32_t);
506 out->cell_bytes = (size_t)band;
507 out->scratch_bytes =
508 (info.codec == (uint8_t)k_jof_codec_raw) ? 1U : (size_t)jof_stored_bound((uint32_t)band);
509 out->tile_count = tiles;
510 out->engine = (uint32_t)k_ra8_viewer_engine_jof;
511 return k_ra8_ok;
512}
513
527{
528 out->tile_count = (uint32_t)k_viewer_comic_page_cap;
529 out->dimensions_bytes = (size_t)out->tile_count * 2U * sizeof(uint32_t);
530 out->comic_pages_bytes = (size_t)out->tile_count * sizeof(comic_page_t);
534 out->engine = (uint32_t)k_ra8_viewer_engine_comic;
535}
536
538{
539 if ((path == nullptr) || (out == nullptr)) {
540 return k_ra8_err_null_ptr;
541 }
543 const viewer_fmt_t format = internal_classify(path);
544 if ((format != k_viewer_fmt_jof) && (format != k_viewer_fmt_comic)) {
545 return internal_reject(format, path);
546 }
547 viewer_file_ctx_t file = {.fd = -1};
548 ra8_err_t error = internal_file_open(&file, path);
549 if (error == k_ra8_ok) {
550 out->required_alignment = alignof(max_align_t);
551 out->framebuffer_bytes =
552 (size_t)k_ra8_viewer_fb_width * (size_t)k_ra8_viewer_fb_height * sizeof(uint16_t);
554 if (format == k_viewer_fmt_jof) {
555 error = internal_require_jof(&file, out);
556 } else {
558 }
559 }
560 if (file.is_open) {
561 (void)close(file.fd);
562 }
563 if (error != k_ra8_ok) {
565 return error;
566 }
567 const viewer_layout_t layout = internal_layout(out, nullptr, 0U);
568 if (!layout.valid) {
571 }
572 out->required_bytes = layout.used;
573 return k_ra8_ok;
574}
575
577 void* workspace,
578 size_t workspace_bytes,
579 const ra8_viewer_reader_requirements_t* requirements,
581{
582 if ((out == nullptr) || (requirements == nullptr) || (report == nullptr)) {
583 return k_ra8_err_null_ptr;
584 }
585 *out = nullptr;
586 *report = (ra8_viewer_workspace_report_t){.required_bytes = requirements->required_bytes,
587 .supplied_bytes = workspace_bytes};
588 if ((workspace == nullptr) || !internal_requirements_valid(requirements) ||
589 (((uintptr_t)workspace % requirements->required_alignment) != 0U) ||
590 (workspace_bytes < requirements->required_bytes)) {
592 }
593 viewer_layout_t layout = {.base = (uint8_t*)workspace,
594 .capacity = workspace_bytes,
595 .used = 0U,
596 .valid = true};
597 ra8_viewer_reader_t* reader =
598 (ra8_viewer_reader_t*)internal_take(&layout, sizeof(*reader), alignof(max_align_t));
599 uint16_t* framebuffer =
600 (uint16_t*)internal_take(&layout, requirements->framebuffer_bytes, alignof(uint16_t));
601 const size_t dimension_bytes = requirements->dimensions_bytes / 2U;
602 uint32_t* widths = (uint32_t*)internal_take(&layout, dimension_bytes, alignof(uint32_t));
603 uint32_t* heights = (uint32_t*)internal_take(&layout, dimension_bytes, alignof(uint32_t));
604 viewer_jof_t jof = {};
605 viewer_comic_t comic = {};
606 if (requirements->engine == (uint32_t)k_ra8_viewer_engine_jof) {
607 jof = internal_take_jof(&layout, requirements);
608 } else {
609 comic = internal_take_comic(&layout, requirements);
610 }
611 if (!layout.valid || (layout.used != requirements->required_bytes)) {
613 }
614 *reader = (ra8_viewer_reader_t){.file = {.fd = -1},
615 .is_bound = true,
616 .engine = requirements->engine,
617 .limits = ra8_decomp_limits_default(),
618 .fb = framebuffer,
619 .tile_wpx = widths,
620 .tile_hpx = heights,
621 .tile_cap = requirements->tile_count,
622 .rt565 = framebuffer,
623 .rt_w = (uint32_t)k_ra8_viewer_fb_width,
624 .rt_h = (uint32_t)k_ra8_viewer_fb_height,
625 .jof = jof,
626 .comic = comic};
627 *out = reader;
628 return k_ra8_ok;
629}
630
647{
649 if (reader->engine == (uint32_t)k_ra8_viewer_engine_jof) {
650 error = priv_viewer_open_jof(reader);
651 if (error == k_ra8_ok) {
652 reader->tile_n =
653 ((uint32_t)reader->jof.dctx.info.height + (uint32_t)k_ra8_viewer_fb_height - 1U) /
654 (uint32_t)k_ra8_viewer_fb_height;
655 }
656 if ((error == k_ra8_ok) && ((reader->tile_n == 0U) || (reader->tile_n > reader->tile_cap))) {
658 }
659 if (error == k_ra8_ok) {
660 priv_viewer_size_jof_tiles(reader, reader->tile_n);
661 }
662 } else if (reader->engine == (uint32_t)k_ra8_viewer_engine_comic) {
663 error = priv_viewer_open_comic(reader);
664 if (error == k_ra8_ok) {
665 reader->tile_n = comic_page_count(&reader->comic.archive);
666 }
667 if ((error == k_ra8_ok) && ((reader->tile_n == 0U) || (reader->tile_n > reader->tile_cap))) {
669 }
670 if (error == k_ra8_ok) {
671 error = priv_viewer_size_comic_tiles(reader);
672 }
673 }
674 return error;
675}
676
694 viewer_fmt_t format)
695{
696 return ((reader->engine == (uint32_t)k_ra8_viewer_engine_jof) && (format == k_viewer_fmt_jof)) ||
697 ((reader->engine == (uint32_t)k_ra8_viewer_engine_comic) &&
698 (format == k_viewer_fmt_comic));
699}
700
702{
703 if ((reader == nullptr) || (path == nullptr)) {
704 return k_ra8_err_null_ptr;
705 }
706 if (!reader->is_bound || reader->is_open || reader->file.is_open) {
708 }
709 const viewer_fmt_t format = internal_classify(path);
710 if (!internal_engine_matches(reader, format)) {
712 }
713 ra8_err_t error = internal_file_open(&reader->file, path);
714 if (error == k_ra8_ok) {
715 error = internal_open_selected(reader);
716 }
717 if (error == k_ra8_ok) {
718 reader->is_open = true;
719 return k_ra8_ok;
720 }
721 if (reader->engine == (uint32_t)k_ra8_viewer_engine_comic) {
722 (void)comic_close(&reader->comic.archive);
723 }
724 if (reader->file.is_open) {
725 (void)close(reader->file.fd);
726 }
727 reader->file = (viewer_file_ctx_t){.fd = -1};
728 reader->tile_n = 0U;
729 return error;
730}
731
733{
734 return ((reader == nullptr) || !reader->is_open) ? 0U : reader->tile_n;
735}
736
738{
739 if (reader == nullptr) {
740 return k_ra8_err_null_ptr;
741 }
742 if (!reader->is_open) {
744 }
745 if (page >= reader->tile_n) {
747 }
748 return (reader->engine == (uint32_t)k_ra8_viewer_engine_comic)
749 ? priv_viewer_render_comic(reader, page)
750 : priv_viewer_render_jof(reader, page);
751}
752
754{
755 return ra8_viewer_page_count(reader);
756}
757
759 uint32_t index,
760 uint32_t* width,
761 uint32_t* height)
762{
763 if ((reader == nullptr) || (width == nullptr) || (height == nullptr)) {
764 return k_ra8_err_null_ptr;
765 }
766 if (!reader->is_open) {
768 }
769 if (index >= reader->tile_n) {
771 }
772 *width = reader->tile_wpx[index];
773 *height = reader->tile_hpx[index];
774 return k_ra8_ok;
775}
776
778 uint32_t index,
779 size_t* out_bytes,
780 size_t* out_alignment)
781{
782 uint32_t width = 0U;
783 uint32_t height = 0U;
784 const ra8_err_t error = ra8_viewer_tile_size(reader, index, &width, &height);
785 if ((out_bytes == nullptr) || (out_alignment == nullptr)) {
786 return k_ra8_err_null_ptr;
787 }
788 if (error != k_ra8_ok) {
789 return error;
790 }
791 *out_bytes = (size_t)width * (size_t)height * sizeof(uint16_t);
792 *out_alignment = alignof(uint16_t);
793 return k_ra8_ok;
794}
795
797 uint32_t index,
798 void* workspace,
799 size_t workspace_bytes,
800 uint32_t* width,
801 uint32_t* height,
802 uint16_t** out_pixels,
804{
805 if ((reader == nullptr) || (width == nullptr) || (height == nullptr) || (out_pixels == nullptr) ||
806 (report == nullptr)) {
807 return k_ra8_err_null_ptr;
808 }
809 *out_pixels = nullptr;
810 size_t required = 0U;
811 size_t alignment = 0U;
812 const ra8_err_t error = ra8_viewer_tile_requirements(reader, index, &required, &alignment);
813 *report =
814 (ra8_viewer_workspace_report_t){.required_bytes = required, .supplied_bytes = workspace_bytes};
815 if (error != k_ra8_ok) {
816 return error;
817 }
818 if ((workspace == nullptr) || (((uintptr_t)workspace % alignment) != 0U) ||
819 (workspace_bytes < required)) {
821 }
822 const ra8_err_t render =
823 (reader->engine == (uint32_t)k_ra8_viewer_engine_comic)
824 ? priv_viewer_tile_comic(reader, index, (uint16_t*)workspace, workspace_bytes, width, height)
825 : priv_viewer_tile_jof(reader, index, (uint16_t*)workspace, workspace_bytes, width, height);
826 if (render == k_ra8_ok) {
827 *out_pixels = (uint16_t*)workspace;
828 }
829 return render;
830}
831
833{
834 if (reader == nullptr) {
835 return;
836 }
837 if (reader->file.is_open) {
838 if (reader->engine == (uint32_t)k_ra8_viewer_engine_comic) {
839 (void)comic_close(&reader->comic.archive);
840 }
841 (void)close(reader->file.fd);
842 }
843 reader->file = (viewer_file_ctx_t){.fd = -1};
844 reader->is_open = false;
845 reader->tile_n = 0U;
846 reader->rt565 = reader->fb;
847 reader->rt_w = (uint32_t)k_ra8_viewer_fb_width;
848 reader->rt_h = (uint32_t)k_ra8_viewer_fb_height;
849}
ra8_err_t comic_close(comic_t *c)
Release an open comic's backend resources.
Definition comic.c:494
static uint32_t comic_page_count(const comic_t *c)
Number of pages in an open comic.
Definition comic.h:263
#define O_CLOEXEC
Zero fallback when the host lacks close-on-exec open flags.
JOF band-tile atlas: the display-native normalized image format (#231, shared with the longstrip scro...
ra8_err_t jof_parse(jof_pread_fn pread, void *pread_ctx, uint64_t total_size, jof_info_t *out_info)
Parse + validate a JOF atlas's header, footer and index bounds.
Definition jof.c:379
@ k_jof_codec_raw
Tile stream is the packed pixels.
Definition jof.h:191
uint32_t jof_stored_bound(uint32_t raw_bytes)
Worst-case stored-tile byte bound for scratch/cell sizing.
Definition jof.c:439
-proof
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Unified decompression-limits policy: one bound set for every decoder.
ra8_decomp_limits_t ra8_decomp_limits_default(void)
The owner-approved default decompression policy.
ra8_err_t ra8_decomp_check_declared(const ra8_decomp_limits_t *limits, uint64_t comp_size, uint64_t out_size)
Header-level check of a member's declared sizes against a policy.
Error Code Definitions for ra8-firmware.
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ k_ra8_err_out_of_range
Sensor or peripheral output out of valid range.
Definition ra8_err.h:337
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
size_t strlen(const char *s)
Calculate string length.
ra8_err_t priv_viewer_pread(void *ctx, uint64_t offset, uint8_t *buffer, size_t length, size_t *out_read)
JOF positional-reader seam over viewer_file_ctx_t.
uint32_t ra8_viewer_tile_count(const ra8_viewer_reader_t *reader)
Number of scroll tiles in reader, or zero for NULL/closed.
uint32_t ra8_viewer_page_count(const ra8_viewer_reader_t *reader)
Number of viewport pages in reader, or zero for NULL/closed.
static void internal_require_comic(ra8_viewer_reader_requirements_t *out)
Populate the fixed bounded bare-comic requirements shape.
static bool internal_requirements_valid(const ra8_viewer_reader_requirements_t *need)
Validate a requirements object before workspace mutation.
void ra8_viewer_close(ra8_viewer_reader_t *reader)
Close the descriptor and reset reader state without freeing workspace.
static ra8_err_t internal_require_jof(viewer_file_ctx_t *file, ra8_viewer_reader_requirements_t *out)
Populate JOF-specific requirements from one open descriptor.
ra8_err_t ra8_viewer_open(ra8_viewer_reader_t *reader, const char *path)
Open the document class used to size and bind reader.
static void internal_stderr_write(const char *text)
Best-effort exact diagnostic fragment over standard error.
static ra8_err_t internal_file_open(viewer_file_ctx_t *file, const char *path)
Open and size one non-empty regular host file.
static viewer_comic_t internal_take_comic(viewer_layout_t *layout, const ra8_viewer_reader_requirements_t *requirements)
Take the complete comic-specific workspace tail.
viewer_fmt_t
Recognised filename classes at the host composition edge.
@ k_viewer_fmt_reflow
EPUB/RABOOK engine not wired.
@ k_viewer_fmt_jof
Supported streamed JOF.
@ k_viewer_fmt_unknown
Unrecognised extension.
@ k_viewer_fmt_comic_wrap
Gzip/XZ whole-output seam remains.
@ k_viewer_fmt_comic
Supported bare CBZ/CBR/CBT.
static ra8_err_t internal_open_selected(ra8_viewer_reader_t *reader)
Open and size the reader's selected engine.
static viewer_fmt_t internal_classify(const char *path)
Classify a path without opening it.
ra8_err_t ra8_viewer_tile_requirements(const ra8_viewer_reader_t *reader, uint32_t index, size_t *out_bytes, size_t *out_alignment)
Report exact caller storage needed to render tile index.
static viewer_layout_t internal_layout(const ra8_viewer_reader_requirements_t *need, uint8_t *base, size_t capacity)
Charge the complete deterministic reader layout.
static bool internal_engine_requirements_valid(const ra8_viewer_reader_requirements_t *need)
Validate engine-specific requirement fields.
ra8_err_t ra8_viewer_render_tile565(ra8_viewer_reader_t *reader, uint32_t index, void *workspace, size_t workspace_bytes, uint32_t *width, uint32_t *height, uint16_t **out_pixels, ra8_viewer_workspace_report_t *report)
Render tile index into caller-owned RGB565 storage.
ra8_err_t ra8_viewer_render_page(ra8_viewer_reader_t *reader, uint32_t page)
Render one page into the reader's fixed RGB565 framebuffer.
static viewer_jof_t internal_take_jof(viewer_layout_t *layout, const ra8_viewer_reader_requirements_t *requirements)
Take the complete JOF-specific workspace tail.
static bool internal_ends_with(const char *path, const char *suffix)
Test one case-insensitive ASCII filename suffix.
static ra8_err_t internal_reject(viewer_fmt_t format, const char *path)
Emit the truthful unsupported-format reason.
ra8_err_t ra8_viewer_reader_bind(ra8_viewer_reader_t **out, void *workspace, size_t workspace_bytes, const ra8_viewer_reader_requirements_t *requirements, ra8_viewer_workspace_report_t *report)
Bind one reader state to caller-owned bytes.
ra8_err_t ra8_viewer_reader_requirements(const char *path, ra8_viewer_reader_requirements_t *out)
Inspect path and calculate its exact reader workspace.
static void * internal_take(viewer_layout_t *layout, size_t bytes, size_t alignment)
Take one aligned layout slice, or only charge it while sizing.
static bool internal_align_up(size_t value, size_t alignment, size_t *out)
Align value upward with overflow rejection.
static bool internal_engine_matches(const ra8_viewer_reader_t *reader, viewer_fmt_t format)
Whether a path class matches the engine frozen at bind time.
ra8_err_t ra8_viewer_tile_size(const ra8_viewer_reader_t *reader, uint32_t index, uint32_t *width, uint32_t *height)
Read native dimensions for tile index.
Caller-owned host JOF/comic reader and RGB565 render surface.
@ k_ra8_viewer_fb_height
Framebuffer height in pixels.
@ k_ra8_viewer_fb_width
Framebuffer width in pixels.
@ k_ra8_viewer_engine_comic
Bare CBZ/CBR/CBT comic engine.
@ k_ra8_viewer_engine_jof
Streamed JOF long-strip engine.
struct ra8_viewer_reader ra8_viewer_reader_t
Opaque state bound inside caller-owned workspace.
Cross-translation-unit state for the bounded JOF/comic viewer.
ra8_err_t priv_viewer_pread(void *ctx, uint64_t offset, uint8_t *buffer, size_t length, size_t *out_read)
JOF positional-reader seam over viewer_file_ctx_t.
void priv_viewer_size_jof_tiles(ra8_viewer_reader_t *reader, uint32_t count)
Populate native tile dimensions after the strip opens.
ra8_err_t priv_viewer_render_comic(ra8_viewer_reader_t *reader, uint32_t page)
Render one comic page into the fixed framebuffer.
ra8_err_t priv_viewer_tile_jof(ra8_viewer_reader_t *reader, uint32_t index, uint16_t *pixels, size_t pixel_bytes, uint32_t *width, uint32_t *height)
Render one JOF viewport into caller-owned RGB565 output.
@ k_viewer_layout_version
Workspace layout ABI guard.
@ k_viewer_jof_buckets
Power-of-two cache bucket count.
ra8_err_t priv_viewer_size_comic_tiles(ra8_viewer_reader_t *reader)
Probe every comic page into the bound dimension arrays.
ra8_err_t priv_viewer_open_jof(ra8_viewer_reader_t *reader)
Open and wire the JOF engine over a bound reader.
ra8_err_t priv_viewer_tile_comic(ra8_viewer_reader_t *reader, uint32_t index, uint16_t *pixels, size_t pixel_bytes, uint32_t *width, uint32_t *height)
Render one comic page into caller-owned RGB565 storage.
@ k_viewer_comic_page_cap
Maximum indexed pages.
@ k_viewer_comic_arena_bytes
stb decode arena capacity.
@ k_viewer_comic_page_bytes
Encoded-page resident cap.
@ k_viewer_comic_name_bytes
Member-name arena bytes.
ra8_err_t priv_viewer_open_comic(ra8_viewer_reader_t *reader)
Open a bare CBZ/CBR/CBT engine over a bound reader.
ra8_err_t priv_viewer_render_jof(ra8_viewer_reader_t *reader, uint32_t page)
Render one JOF viewport into the fixed framebuffer.
One entry in the resident, sorted page index.
Definition comic.h:140
Parsed + validated geometry of one JOF atlas.
Definition jof.h:208
uint8_t bpp
Bytes per pixel (1, 3 or 4).
Definition jof.h:215
uint8_t codec
jof_codec_t member.
Definition jof.h:216
uint16_t tile_w
Tile width, pixels.
Definition jof.h:211
uint16_t tile_h
Tile height, pixels.
Definition jof.h:212
uint16_t height
Image height, pixels.
Definition jof.h:210
jof_info_t info
Parsed atlas geometry.
Definition longstrip.h:140
One decompression policy: the five resource bounds decoders enforce.
Per-cell link metadata (one caller-owned array entry per cell).
Per-cell user descriptor: the decoded tile dimensions.
Identifies one decoded image tile.
Exact immutable layout returned for one document.
size_t comic_pages_bytes
Comic page-index storage, or zero.
size_t required_bytes
Complete workspace extent.
size_t comic_page_bytes
Resident encoded-page storage, or zero.
uint32_t layout_version
ABI guard consumed by bind.
size_t comic_arena_bytes
stb decode arena storage, or zero.
size_t scratch_bytes
One bounded compressed-band staging area.
size_t comic_names_bytes
Comic member-name arena, or zero.
size_t cell_bytes
One resident decoded JOF band.
uint32_t engine
ra8_viewer_reader_engine_t selection.
size_t framebuffer_bytes
Resident fixed RGB565 framebuffer.
uint32_t tile_count
Number of viewport tiles in the strip.
size_t dimensions_bytes
Two resident per-tile dimension arrays.
size_t required_alignment
Required base-address alignment.
uint32_t * tile_hpx
Native height per viewport tile.
uint32_t * tile_wpx
Native width per viewport tile.
uint16_t * rt565
Current composite target.
viewer_jof_t jof
JOF engine and caller-bound cache.
viewer_comic_t comic
Comic engine and caller slices.
uint32_t rt_w
Current target width.
uint16_t * fb
Fixed headless framebuffer.
bool is_open
Successful engine-open flag.
bool is_bound
Successful workspace bind flag.
uint32_t engine
ra8_viewer_reader_engine_t value.
viewer_file_ctx_t file
Raw source descriptor.
uint32_t tile_cap
Bound dimension entries.
uint32_t tile_n
Populated dimension entries.
uint32_t rt_h
Current target height.
Required-versus-supplied evidence from a bind attempt.
Bare comic engine and every caller-bound backing slice.
uint8_t * page
Resident encoded-image bytes.
comic_t archive
Open CBZ/CBR/CBT facade.
char * names
Resident member-name arena.
size_t page_cap
Encoded-image capacity in bytes.
uint8_t * arena
stb caller-owned decode arena.
comic_page_t * pages
Resident sorted page index.
size_t arena_cap
Decode-arena capacity in bytes.
Raw host descriptor behind the reusable positional-read callback.
int fd
Owned descriptor while open.
bool is_open
Descriptor ownership flag.
Caller-bound JOF engine and one-band cache backing.
size_t scratch_cap
Bound compressed staging bytes.
uint8_t * scratch
Compressed-band staging.
longstrip_decode_ctx_t dctx
JOF decoder callback state.
ra8_tile_dims_t * dims
Single cache dimensions entry.
uint8_t * cells
One decoded-band buffer.
ra8_tile_key_t * keys
Single cache key.
int32_t * buckets
Hash bucket heads.
ra8_keycache_cell_t * meta
Single cache metadata entry.
size_t cell_cap
Bound decoded-band bytes.
Mutable cursor used to calculate or bind one aligned layout.
bool valid
False after overflow/capacity failure.
uint8_t * base
Workspace base, or NULL during sizing.
size_t used
First unused byte.
size_t capacity
Accessible extent during binding.