ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_viewer_ppm.c
Go to the documentation of this file.
1
20
21#include <errno.h>
22#include <fcntl.h>
23#include <stddef.h>
24#include <stdint.h>
25#include <unistd.h>
26
27#include "ra8_err.h"
28#include "ra8_viewer_reader.h"
30
31#ifndef O_CLOEXEC
33#define O_CLOEXEC (0)
34#endif
35
52
58typedef enum : uint32_t {
63
69typedef enum : uint8_t {
75
86
87uint16_t ra8_viewer_pack565(uint8_t rr, uint8_t gg, uint8_t bb)
88{
89 const uint16_t r5 = (uint16_t)(rr >> (uint16_t)k_rgb565_r5_drop);
90 const uint16_t g6 = (uint16_t)(gg >> (uint16_t)k_rgb565_g6_drop);
91 const uint16_t b5 = (uint16_t)(bb >> (uint16_t)k_rgb565_r5_drop);
92 return (uint16_t)((r5 << (uint16_t)k_rgb565_r_shift) | (g6 << (uint16_t)k_rgb565_g_shift) | b5);
93}
94
95uint16_t ra8_viewer_pack565_le_pair(uint8_t lo, uint8_t hi)
96{
97 return (uint16_t)((uint16_t)lo | ((uint16_t)hi << (uint16_t)k_rgb565_hi_shift));
98}
99
112RA8_INTERNAL static void internal_unpack565(uint16_t px, uint8_t rgb[k_ppm_channels])
113{
114 const uint32_t r5 = ((uint32_t)px >> (uint32_t)k_rgb565_r_shift) & (uint32_t)k_rgb565_r5_mask;
115 const uint32_t g6 = ((uint32_t)px >> (uint32_t)k_rgb565_g_shift) & (uint32_t)k_rgb565_g6_mask;
116 const uint32_t b5 = (uint32_t)px & (uint32_t)k_rgb565_b5_mask;
117
118 rgb[k_ppm_idx_r] =
119 (uint8_t)((r5 << (uint32_t)k_rgb565_r5_drop) | (r5 >> (uint32_t)k_rgb565_r5_fill));
120 rgb[k_ppm_idx_g] =
121 (uint8_t)((g6 << (uint32_t)k_rgb565_g6_drop) | (g6 >> (uint32_t)k_rgb565_g6_fill));
122 rgb[k_ppm_idx_b] =
123 (uint8_t)((b5 << (uint32_t)k_rgb565_r5_drop) | (b5 >> (uint32_t)k_rgb565_r5_fill));
124}
125
143RA8_INTERNAL static bool internal_write_all(int fd, const uint8_t* bytes, size_t length)
144{
145 size_t offset = 0U;
146 while (offset < length) {
147 const ssize_t written = write(fd, &bytes[offset], length - offset);
148 if ((written < 0) && (errno == EINTR)) {
149 continue;
150 }
151 if (written <= 0) {
152 return false;
153 }
154 offset += (size_t)written;
155 }
156 return true;
157}
158
175RA8_INTERNAL static size_t
176internal_append_u32(char out[k_ppm_header_bytes], size_t offset, uint32_t value)
177{
178 char reverse[k_ppm_u32_digits];
179 size_t digits = 0U;
180 do {
181 reverse[digits] = (char)('0' + (value % (uint32_t)k_ppm_decimal_radix));
182 ++digits;
183 value /= (uint32_t)k_ppm_decimal_radix;
184 } while (value != 0U);
185 while (digits > 0U) {
186 --digits;
187 out[offset] = reverse[digits];
188 ++offset;
189 }
190 return offset;
191}
192
209RA8_INTERNAL static size_t
210internal_ppm_header(char out[k_ppm_header_bytes], uint32_t width, uint32_t height)
211{
212 size_t offset = 0U;
213 out[offset++] = 'P';
214 out[offset++] = '6';
215 out[offset++] = '\n';
216 offset = internal_append_u32(out, offset, width);
217 out[offset++] = ' ';
218 offset = internal_append_u32(out, offset, height);
219 out[offset++] = '\n';
220 out[offset++] = '2';
221 out[offset++] = '5';
222 out[offset++] = '5';
223 out[offset++] = '\n';
224 return offset;
225}
226
227ra8_err_t ra8_viewer_write_ppm565(const uint16_t* px, uint32_t w, uint32_t h, const char* path)
228{
229 if ((px == nullptr) || (path == nullptr)) {
230 return k_ra8_err_null_ptr;
231 }
232 if ((w == 0U) || (h == 0U)) {
234 }
235 if ((size_t)w > (SIZE_MAX / (size_t)h)) {
237 }
238 const int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, k_ppm_file_mode);
239 if (fd < 0) {
240 return k_ra8_err_not_found;
241 }
242 char header[k_ppm_header_bytes];
243 const size_t header_bytes = internal_ppm_header(header, w, h);
244 bool ok = internal_write_all(fd, (const uint8_t*)header, header_bytes);
245 const size_t pixels = (size_t)w * (size_t)h;
246 uint8_t chunk[k_ppm_chunk_bytes];
247 size_t consumed = 0U;
248 while (ok && (consumed < pixels)) {
249 size_t batch = pixels - consumed;
250 if (batch > (size_t)k_ppm_chunk_pixels) {
251 batch = (size_t)k_ppm_chunk_pixels;
252 }
253 for (size_t i = 0U; i < batch; ++i) {
254 internal_unpack565(px[consumed + i], &chunk[i * (size_t)k_ppm_channels]);
255 }
256 ok = internal_write_all(fd, chunk, batch * (size_t)k_ppm_channels);
257 consumed += batch;
258 }
259 const int closed = close(fd);
260 return (ok && (closed == 0)) ? k_ra8_ok : k_ra8_err_not_found;
261}
262
264{
265 if ((r == nullptr) || (path == nullptr)) {
266 return k_ra8_err_null_ptr;
267 }
268 return ra8_viewer_write_ppm565(r->fb,
269 (uint32_t)k_ra8_viewer_fb_width,
270 (uint32_t)k_ra8_viewer_fb_height,
271 path);
272}
#define O_CLOEXEC
Zero fallback when the host lacks close-on-exec open flags.
-proof
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ 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
ra8_viewer_rgb565_mask_t
Channel masks used to unpack an RGB565 word.
@ k_rgb565_b5_mask
5-bit blue field mask.
@ k_rgb565_r5_mask
5-bit red field mask.
@ k_rgb565_g6_mask
6-bit green field mask.
ra8_err_t ra8_viewer_write_ppm565(const uint16_t *px, uint32_t w, uint32_t h, const char *path)
Write arbitrary caller-owned RGB565 pixels as binary PPM.
ra8_viewer_ppm_io_t
Bounded buffers used by the descriptor-backed PPM publisher.
@ k_ppm_header_bytes
Complete P6 header capacity.
@ k_ppm_chunk_bytes
Conversion buffer capacity in bytes.
@ k_ppm_file_mode
Host output mode before process mask.
@ k_ppm_decimal_radix
Decimal numeric base.
@ k_ppm_chunk_pixels
Pixels converted before each write.
@ k_ppm_u32_digits
Decimal digits in UINT32_MAX.
uint16_t ra8_viewer_pack565(uint8_t rr, uint8_t gg, uint8_t bb)
Pack 8-bit RGB channels into one RGB565 word.
static RA8_INTERNAL bool internal_write_all(int fd, const uint8_t *bytes, size_t length)
Write exactly length bytes to one owned host descriptor.
ra8_err_t ra8_viewer_dump_ppm(const ra8_viewer_reader_t *r, const char *path)
Write the current fixed framebuffer as binary PPM.
ra8_viewer_rgb565_shift_t
Field positions and channel-width conversions for RGB565 packing.
@ k_rgb565_g_shift
Green field position in the 16-bit word.
@ k_rgb565_g6_fill
6->8 low-bit replication shift (green).
@ k_rgb565_r_shift
Red field position in the 16-bit word.
@ k_rgb565_r5_drop
8->5-bit reduction for red/blue.
@ k_rgb565_hi_shift
High-byte position of a little-endian pixel.
@ k_rgb565_g6_drop
8->6-bit reduction for green.
@ k_rgb565_r5_fill
5->8 low-bit replication shift (red/blue).
static RA8_INTERNAL size_t internal_ppm_header(char out[k_ppm_header_bytes], uint32_t width, uint32_t height)
Build the locale-independent binary-PPM header for width x height.
uint16_t ra8_viewer_pack565_le_pair(uint8_t lo, uint8_t hi)
Reassemble one little-endian RGB565 word.
static RA8_INTERNAL size_t internal_append_u32(char out[k_ppm_header_bytes], size_t offset, uint32_t value)
Append one unsigned decimal value to a fixed PPM header.
static RA8_INTERNAL void internal_unpack565(uint16_t px, uint8_t rgb[k_ppm_channels])
Expand one RGB565 word into three 8-bit channels.
ra8_viewer_ppm_t
Layout constants of the binary portable-pixmap (P6) output.
@ k_ppm_idx_r
Red byte index within a P6 pixel.
@ k_ppm_channels
Bytes per P6 pixel (R, G, B).
@ k_ppm_idx_b
Blue byte index within a P6 pixel.
@ k_ppm_idx_g
Green byte index within a P6 pixel.
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.
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.
uint16_t * fb
Fixed headless framebuffer.