ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fmt_stream_convert.c
Go to the documentation of this file.
1
11
12#include <stddef.h>
13#include <stdint.h>
14#include <string.h>
15
16#include "jof_produce.h"
17#include "ra8_attributes.h"
18#include "ra8_fmt_stream.h"
19#include "ra8_webp.h"
20
46
54
61
63typedef struct {
65 uint64_t offset;
67
86static ra8_err_t
87internal_read_exact(const ra8_fmt_source_t* source, uint64_t offset, uint8_t* bytes, size_t len)
88{
89 if ((offset > source->size) || ((uint64_t)len > (source->size - offset))) {
91 }
92 size_t done = 0U;
93 while (done < len) {
94 size_t got = 0U;
95 const ra8_err_t rc =
96 source->read_at(source->ctx, offset + done, &bytes[done], len - done, &got);
97 if (rc != k_ra8_ok) {
98 return rc;
99 }
100 if ((got == 0U) || (got > (len - done))) {
102 }
103 done += got;
104 }
105 return k_ra8_ok;
106}
107
122static uint16_t internal_be16(const uint8_t bytes[2])
123{
124 return (uint16_t)(((uint16_t)bytes[0] << 8U) | bytes[1]);
125}
126
141static uint32_t internal_be32(const uint8_t bytes[4])
142{
143 return ((uint32_t)bytes[0] << k_convert_u32_high_shift) | ((uint32_t)bytes[1] << 16U) |
144 ((uint32_t)bytes[2] << 8U) | bytes[3];
145}
146
162static bool internal_is_sof(uint8_t marker)
163{
164 const bool in_range =
165 (marker >= (uint8_t)k_convert_jpeg_sof0) && (marker <= (uint8_t)k_convert_jpeg_sof_last);
166 return in_range && (marker != (uint8_t)k_convert_jpeg_dht) &&
167 (marker != (uint8_t)k_convert_jpeg_jpg) && (marker != (uint8_t)k_convert_jpeg_dac);
168}
169
187static ra8_err_t
188internal_jpeg_marker(const ra8_fmt_source_t* source, uint64_t* offset, uint8_t* marker)
189{
190 uint8_t byte = 0U;
191 do {
192 const ra8_err_t rc = internal_read_exact(source, (*offset)++, &byte, 1U);
193 if (rc != k_ra8_ok) {
194 return rc;
195 }
196 } while (byte != (uint8_t)k_convert_marker);
197 do {
198 const ra8_err_t rc = internal_read_exact(source, (*offset)++, &byte, 1U);
199 if (rc != k_ra8_ok) {
200 return rc;
201 }
202 } while (byte == (uint8_t)k_convert_marker);
203 if (byte == 0U) {
205 }
206 *marker = byte;
207 return k_ra8_ok;
208}
209
230 uint64_t payload,
231 uint16_t segment_len,
232 uint16_t* out_w,
233 uint16_t* out_h)
234{
235 uint8_t fields[5U];
236 if (segment_len < 8U) {
238 }
239 ra8_err_t rc = internal_read_exact(source, payload, fields, sizeof(fields));
240 if (rc != k_ra8_ok) {
241 return rc;
242 }
243 const uint16_t height = internal_be16(&fields[1]);
244 const uint16_t width = internal_be16(&fields[3]);
245 if (fields[0] != (uint8_t)k_convert_jpeg_8bit) {
247 }
248 if ((width == 0U) || (height == 0U) || ((uint32_t)width > (uint32_t)k_jof_max_dim) ||
249 ((uint32_t)height > (uint32_t)k_jof_max_dim)) {
251 }
252 *out_w = width;
253 *out_h = height;
254 return k_ra8_ok;
255}
256
274static ra8_err_t
275internal_jpeg_dims(const ra8_fmt_source_t* source, uint16_t* out_w, uint16_t* out_h)
276{
277 uint64_t offset = 2U;
278 while (offset < source->size) {
279 uint8_t marker = 0U;
280 ra8_err_t rc = internal_jpeg_marker(source, &offset, &marker);
281 if (rc != k_ra8_ok) {
282 return rc;
283 }
284 const bool standalone =
285 (marker == (uint8_t)k_convert_jpeg_soi) || (marker == (uint8_t)k_convert_jpeg_tem) ||
286 ((marker >= (uint8_t)k_convert_jpeg_rst_lo) && (marker <= (uint8_t)k_convert_jpeg_rst_hi));
287 if (standalone) {
288 continue;
289 }
290 if ((marker == (uint8_t)k_convert_jpeg_eoi) || (marker == (uint8_t)k_convert_jpeg_sos)) {
292 }
293 uint8_t length_bytes[2U];
294 rc = internal_read_exact(source, offset, length_bytes, sizeof(length_bytes));
295 if (rc != k_ra8_ok) {
296 return rc;
297 }
298 const uint16_t segment_len = internal_be16(length_bytes);
299 if (segment_len < 2U) {
301 }
302 if (internal_is_sof(marker)) {
303 return (marker == (uint8_t)k_convert_jpeg_sof0)
304 ? internal_jpeg_sof0(source, offset + 2U, segment_len, out_w, out_h)
306 }
307 offset += segment_len;
308 if (offset > source->size) {
310 }
311 }
313}
314
332static ra8_err_t
333internal_png_dims(const uint8_t prefix[k_convert_png_dims_end], uint16_t* out_w, uint16_t* out_h)
334{
335 const uint32_t width = internal_be32(&prefix[k_convert_png_w]);
336 const uint32_t height = internal_be32(&prefix[k_convert_png_h]);
337 if ((width == 0U) || (height == 0U) || (width > (uint32_t)k_jof_max_dim) ||
338 (height > (uint32_t)k_jof_max_dim)) {
340 }
341 *out_w = (uint16_t)width;
342 *out_h = (uint16_t)height;
343 return k_ra8_ok;
344}
345
365 convert_kind_t* kind,
366 uint16_t* out_w,
367 uint16_t* out_h)
368{
369 static const uint8_t png_magic[8U] = {k_convert_png_sig_high,
370 'P',
371 'N',
372 'G',
377 uint8_t prefix[k_convert_probe_bytes];
378 size_t count = sizeof(prefix);
379 if (source->size < (uint64_t)count) {
380 count = (size_t)source->size;
381 }
382 if (count < k_convert_probe_min) {
384 }
385 ra8_err_t rc = internal_read_exact(source, 0U, prefix, count);
386 if (rc != k_ra8_ok) {
387 return rc;
388 }
389 if ((prefix[0] == (uint8_t)k_convert_marker) && (prefix[1] == (uint8_t)k_convert_jpeg_soi)) {
390 *kind = k_convert_kind_jpeg;
391 return internal_jpeg_dims(source, out_w, out_h);
392 }
393 if (memcmp(prefix, png_magic, sizeof(png_magic)) == 0) {
394 if (count < (size_t)k_convert_png_dims_end) {
396 }
397 *kind = k_convert_kind_png;
398 return internal_png_dims(prefix, out_w, out_h);
399 }
400 if ((memcmp(prefix, "RIFF", 4U) == 0) && (memcmp(&prefix[8], "WEBP", 4U) == 0)) {
401 uint32_t width = 0U;
402 uint32_t height = 0U;
403 rc = ra8_webp_get_info(prefix, count, &width, &height);
404 if (rc != k_ra8_ok) {
405 return rc;
406 }
407 if ((width == 0U) || (height == 0U) || (width > (uint32_t)k_jof_max_dim) ||
408 (height > (uint32_t)k_jof_max_dim)) {
410 }
411 *kind = k_convert_kind_webp;
412 *out_w = (uint16_t)width;
413 *out_h = (uint16_t)height;
414 return k_ra8_ok;
415 }
417}
418
437static ra8_err_t internal_pull(void* ctx, uint8_t* bytes, size_t cap, size_t* got)
438{
439 convert_pull_t* pull = (convert_pull_t*)ctx;
440 if ((pull == nullptr) || (got == nullptr) || ((bytes == nullptr) && (cap != 0U))) {
441 return k_ra8_err_null_ptr;
442 }
443 *got = 0U;
444 if ((cap == 0U) || (pull->offset >= pull->source->size)) {
445 return k_ra8_ok;
446 }
447 uint64_t remain = pull->source->size - pull->offset;
448 if ((uint64_t)cap > remain) {
449 cap = (size_t)remain;
450 }
451 const ra8_err_t rc = pull->source->read_at(pull->source->ctx, pull->offset, bytes, cap, got);
452 if ((rc == k_ra8_ok) && (*got <= cap)) {
453 pull->offset += *got;
454 } else if (rc == k_ra8_ok) {
456 }
457 return rc;
458}
459
477static ra8_err_t internal_append(void* ctx, const uint8_t* bytes, size_t len)
478{
479 ra8_fmt_transaction_t* transaction = (ra8_fmt_transaction_t*)ctx;
480 return transaction->ops->append(transaction->ctx, bytes, len);
481}
482
495static void internal_abort(ra8_fmt_transaction_t* transaction)
496{
497 if ((transaction != nullptr) && (transaction->ops != nullptr) &&
498 (transaction->ops->abort != nullptr)) {
499 transaction->ops->abort(transaction->ctx);
500 }
501}
502
518static ra8_err_t internal_text(const ra8_fmt_sink_t* report, const char* text)
519{
520 return report->write(report->ctx, (const uint8_t*)text, strlen(text));
521}
522
538static ra8_err_t internal_u64(const ra8_fmt_sink_t* report, uint64_t value)
539{
540 char reverse[k_convert_decimal_max];
541 size_t count = 0U;
542 do {
543 reverse[count++] = (char)('0' + (char)(value % k_convert_decimal_base));
544 value /= k_convert_decimal_base;
545 } while (value != 0U);
546 char text[k_convert_decimal_max];
547 for (size_t i = 0U; i < count; ++i) {
548 text[i] = reverse[count - i - 1U];
549 }
550 return report->write(report->ctx, (const uint8_t*)text, count);
551}
552
568static void
569internal_field(const ra8_fmt_sink_t* report, uint64_t value, const char* suffix, ra8_err_t* status)
570{
571 if (*status == k_ra8_ok) {
572 *status = internal_u64(report, value);
573 }
574 if (*status == k_ra8_ok) {
575 *status = internal_text(report, suffix);
576 }
577}
578
596static ra8_err_t
597internal_report(const ra8_fmt_sink_t* report, const jof_info_t* info, const char* output_name)
598{
599 ra8_err_t rc = internal_text(report, "convert: ");
600 internal_field(report, info->width, "x", &rc);
601 internal_field(report, info->height, " bpp=", &rc);
602 internal_field(report, info->bpp, " band=", &rc);
603 internal_field(report, info->tile_h, " tiles=", &rc);
604 internal_field(report, info->tile_count, " -> ", &rc);
605 if (rc == k_ra8_ok) {
606 rc = internal_text(report, output_name);
607 }
608 if (rc == k_ra8_ok) {
609 rc = internal_text(report, " (");
610 }
611 internal_field(report, info->total_size, " bytes)\n", &rc);
612 return rc;
613}
614
617{
618 if ((source == nullptr) || (source->read_at == nullptr) || (out == nullptr)) {
619 return k_ra8_err_null_ptr;
620 }
623 ra8_err_t rc = internal_probe(source, &kind, &out->width, &out->height);
624 if (rc != k_ra8_ok) {
625 return rc;
626 }
627 out->tile_width = out->width;
628 out->tile_height =
629 (out->height < (uint16_t)k_convert_band_height) ? out->height : (uint16_t)k_convert_band_height;
630 out->work_bytes = jof_work_bytes(out->width, out->height, out->tile_width, out->tile_height);
631 if (out->work_bytes == 0U) {
633 }
634 if (kind == k_convert_kind_webp) {
635 if (source->size > (uint64_t)UINT32_MAX) {
637 }
638 out->webp_work_bytes = jof_webp_work_bytes(out->width, out->height, (uint32_t)source->size);
639 if (out->webp_work_bytes == 0U) {
641 }
642 }
643 return k_ra8_ok;
644}
645
647 const ra8_fmt_jof_convert_requirements_t* requirements,
648 const ra8_fmt_jof_convert_workspace_t* workspace,
649 ra8_fmt_transaction_t* transaction,
650 const ra8_fmt_sink_t* report,
651 const char* output_name)
652{
653 if ((source == nullptr) || (source->read_at == nullptr) || (requirements == nullptr) ||
654 (workspace == nullptr) || (transaction == nullptr) || (transaction->ops == nullptr) ||
655 (transaction->ops->append == nullptr) || (transaction->ops->commit == nullptr) ||
656 (transaction->ops->abort == nullptr) || (report == nullptr) || (report->write == nullptr) ||
657 (output_name == nullptr)) {
658 internal_abort(transaction);
659 return k_ra8_err_null_ptr;
660 }
661 const bool webp_missing = (requirements->webp_work_bytes != 0U) &&
662 ((workspace->webp_work == nullptr) ||
663 (workspace->webp_work_cap < requirements->webp_work_bytes));
664 if ((workspace->work == nullptr) || (workspace->work_cap < requirements->work_bytes) ||
665 webp_missing) {
666 internal_abort(transaction);
668 }
669 convert_pull_t pull = {.source = source, .offset = 0U};
670 const jof_produce_cfg_t cfg = {
671 .pull = internal_pull,
672 .pull_ctx = &pull,
673 .sink = internal_append,
674 .sink_ctx = transaction,
675 .tile_w = requirements->tile_width,
676 .tile_h = requirements->tile_height,
677 .codec = (uint8_t)k_jof_codec_deflate,
678 .max_width = requirements->width,
679 .max_height = requirements->height,
680 .work = workspace->work,
681 .work_cap = workspace->work_cap,
682 .webp_work = workspace->webp_work,
683 .webp_work_cap = workspace->webp_work_cap,
684 };
685 jof_info_t info = {};
686 ra8_err_t rc = jof_produce(&cfg, &info);
687 if (rc != k_ra8_ok) {
688 internal_abort(transaction);
689 return rc;
690 }
691 rc = transaction->ops->commit(transaction->ctx);
692 if (rc != k_ra8_ok) {
693 internal_abort(transaction);
694 return rc;
695 }
696 return internal_report(report, &info, output_name);
697}
@ k_jof_max_dim
Max image width/height, pixels.
Definition jof.h:176
@ k_jof_codec_deflate
Tile stream is one raw-DEFLATE run.
Definition jof.h:192
Import-time transcode producer: JPEG/PNG/WebP -> JOF band-tile atlas in bounded RAM (#231,...
uint32_t jof_webp_work_bytes(uint16_t max_width, uint16_t max_height, uint32_t max_src_bytes)
Compute the whole-frame webp_work arena a WebP source needs (#290).
uint32_t jof_work_bytes(uint16_t max_width, uint16_t max_height, uint16_t tile_w, uint16_t tile_h)
Compute the work-arena size the producer needs for given caps.
ra8_err_t jof_produce(const jof_produce_cfg_t *cfg, jof_info_t *out_info)
Transcode one encoded JPEG/PNG/WebP source into a JOF atlas (#231, #290).
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ 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_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_protocol_error
Protocol-level error (e.g.
Definition ra8_err.h:429
@ 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
Caller-workspace I/O contracts for portable format-tool engines.
static uint16_t internal_be16(const uint8_t bytes[2])
Decode one big-endian uint16 field.
static void internal_abort(ra8_fmt_transaction_t *transaction)
Abort an active transaction after any pre-publication error.
ra8_err_t ra8_fmt_jof_convert_requirements(const ra8_fmt_source_t *source, ra8_fmt_jof_convert_requirements_t *out)
Derive source geometry and exact JOF conversion workspace needs.
static ra8_err_t internal_u64(const ra8_fmt_sink_t *report, uint64_t value)
Append one unsigned decimal to a report sink.
static bool internal_is_sof(uint8_t marker)
Classify JPEG SOF-range markers without mistaking table markers.
static ra8_err_t internal_append(void *ctx, const uint8_t *bytes, size_t len)
Producer sink adapter over the durable transaction append operation.
ra8_err_t ra8_fmt_jof_convert_stream(const ra8_fmt_source_t *source, const ra8_fmt_jof_convert_requirements_t *requirements, const ra8_fmt_jof_convert_workspace_t *workspace, ra8_fmt_transaction_t *transaction, const ra8_fmt_sink_t *report, const char *output_name)
Stream one encoded image into a durably published JOF artifact.
static uint32_t internal_be32(const uint8_t bytes[4])
Decode one big-endian uint32 field.
convert_kind_t
Accepted source encoding selected by the header probe.
@ k_convert_kind_jpeg
Baseline JPEG.
@ k_convert_kind_webp
WebP accepted by the firmware facade.
@ k_convert_kind_png
Non-interlaced PNG (producer validates body).
static ra8_err_t internal_jpeg_marker(const ra8_fmt_source_t *source, uint64_t *offset, uint8_t *marker)
Find the next non-fill JPEG marker code.
static ra8_err_t internal_jpeg_sof0(const ra8_fmt_source_t *source, uint64_t payload, uint16_t segment_len, uint16_t *out_w, uint16_t *out_h)
Parse and validate one baseline JPEG SOF0 segment.
convert_const_t
Image-probe and report constants.
@ k_convert_png_h
PNG IHDR height offset.
@ k_convert_jpeg_soi
JPEG start-of-image marker.
@ k_convert_u32_high_shift
Shift of a big-endian uint32 high byte.
@ k_convert_jpeg_eoi
JPEG end-of-image marker.
@ k_convert_jpeg_sof0
Baseline sequential frame marker.
@ k_convert_jpeg_dac
Arithmetic table non-frame marker.
@ k_convert_jpeg_tem
Standalone TEM marker.
@ k_convert_jpeg_sof_last
Last JPEG SOF-range marker.
@ k_convert_band_height
Established media-downloader band height.
@ k_convert_jpeg_rst_hi
Last restart marker.
@ k_convert_marker
JPEG marker introducer.
@ k_convert_jpeg_8bit
Supported JPEG sample precision.
@ k_convert_probe_min
Smallest supported image prefix.
@ k_convert_decimal_base
Report integer radix.
@ k_convert_png_dims_end
PNG prefix through IHDR dimensions.
@ k_convert_probe_bytes
WebP metadata prefix capacity.
@ k_convert_jpeg_sos
JPEG start-of-scan marker.
@ k_convert_jpeg_dht
Non-frame marker inside SOF number range.
@ k_convert_jpeg_jpg
Reserved non-frame marker.
@ k_convert_png_w
PNG IHDR width offset.
@ k_convert_decimal_max
Decimal digits in uint64_t.
@ k_convert_jpeg_rst_lo
First restart marker.
static ra8_err_t internal_jpeg_dims(const ra8_fmt_source_t *source, uint16_t *out_w, uint16_t *out_h)
Walk a JPEG marker chain to its baseline SOF0 dimensions.
static ra8_err_t internal_report(const ra8_fmt_sink_t *report, const jof_info_t *info, const char *output_name)
Emit the established successful-conversion report line.
static void internal_field(const ra8_fmt_sink_t *report, uint64_t value, const char *suffix, ra8_err_t *status)
Append one numeric report field and its suffix fail-fast.
convert_png_signature_t
Fixed non-ASCII bytes in the PNG signature.
@ k_convert_png_sig_cr
Carriage return byte.
@ k_convert_png_sig_high
High-bit signature byte.
@ k_convert_png_sig_sub
DOS EOF byte.
@ k_convert_png_sig_lf
Line feed byte.
static ra8_err_t internal_probe(const ra8_fmt_source_t *source, convert_kind_t *kind, uint16_t *out_w, uint16_t *out_h)
Probe the exact source kind and dimensions through positioned reads.
static ra8_err_t internal_read_exact(const ra8_fmt_source_t *source, uint64_t offset, uint8_t *bytes, size_t len)
Read an exact positioned source window.
static ra8_err_t internal_pull(void *ctx, uint8_t *bytes, size_t cap, size_t *got)
Producer pull adapter over positioned source reads.
static ra8_err_t internal_png_dims(const uint8_t prefix[k_convert_png_dims_end], uint16_t *out_w, uint16_t *out_h)
Probe PNG dimensions from the fixed IHDR prefix.
static ra8_err_t internal_text(const ra8_fmt_sink_t *report, const char *text)
Append a NUL-terminated text fragment to a report sink.
int memcmp(const void *a, const void *b, size_t n)
Compare bytes in two memory areas.
size_t strlen(const char *s)
Calculate string length.
static void internal_append(ra8_kbd_text_t *t, char ch)
Append character ch to the text buffer if capacity allows.
Zero-heap WebP (VP8 / VP8L) decode facade over the vendored libwebp.
ra8_err_t ra8_webp_get_info(const uint8_t *data, size_t size, uint32_t *out_w, uint32_t *out_h)
Read a WebP header's pixel dimensions without decoding the body.
Definition ra8_webp.c:33
Sequential cursor over one positioned source callback.
uint64_t offset
Next absolute byte offset.
const ra8_fmt_source_t * source
Borrowed immutable source.
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
uint16_t width
Image width, pixels.
Definition jof.h:209
uint16_t tile_h
Tile height, pixels.
Definition jof.h:212
uint32_t tile_count
Total tiles (== cols * rows).
Definition jof.h:217
uint16_t height
Image height, pixels.
Definition jof.h:210
uint32_t total_size
Whole atlas byte length (footer included).
Definition jof.h:219
Producer configuration: source, sink, tile geometry and work arena.
Source geometry and exact arenas required by one JOF conversion.
uint16_t width
Declared source width in pixels.
uint32_t webp_work_bytes
Exact WebP whole-frame bytes, or zero.
uint16_t height
Declared source height in pixels.
uint16_t tile_height
Height selected for emitted tiles.
uint32_t work_bytes
Exact streaming-producer arena bytes.
uint16_t tile_width
Width selected for emitted tiles.
Caller-owned arenas supplied to the streaming JOF converter.
uint32_t work_cap
Bytes available at work.
uint8_t * webp_work
Optional WebP decode arena.
uint32_t webp_work_cap
Bytes at webp_work.
Injected append-only sink.
Immutable, randomly readable input object.
uint64_t size
Exact object byte length.
void * ctx
Backend-owned context.
jof_pread_fn read_at
Positioned-read callback.
ra8_err_t(* commit)(void *ctx)
Sync and atomically install.
ra8_fmt_sink_write_fn append
Append artifact bytes.
void(* abort)(void *ctx)
Discard owned staging data.
One caller-owned artifact transaction.
void * ctx
Backend-owned state.
const ra8_fmt_transaction_ops_t * ops
Transaction implementation.