ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_rabook_gray4.c
Go to the documentation of this file.
1
10
11#include "ra8_rabook_gray4.h"
12
13#include <stdint.h> // ra8-keep-include: `uint32_t` used directly
14#include <string.h>
15
16#include "ra8_attributes.h"
17#include "ra8_check.h"
18#include "ra8_err.h" // ra8-keep-include: `ra8_err_t` used directly
19#include "ra8_log.h" // ra8-keep-include: `ra8_log_error` used directly
20
21/* -------------------------------------------------------------------------- */
22/* Private constants */
23/* -------------------------------------------------------------------------- */
24
33typedef enum : uint32_t {
34 k_fp_shift = 16U,
35 k_fp_unit = 0x10000U,
36 k_fp_mask = 0xFFFFU,
39
45typedef enum : uint8_t {
49
50static const char* const s_tag = "ra8_rabook_gray4";
51
52/* -------------------------------------------------------------------------- */
53/* Private helpers */
54/* -------------------------------------------------------------------------- */
55
84static uint8_t internal_bilinear_sample(const uint8_t* src,
85 uint16_t src_w,
86 uint16_t src_h,
87 uint64_t sx_fp,
88 uint64_t sy_fp)
89{
90 RA8_ASSERT(src != nullptr, "Source pointer must not be null");
91 RA8_ASSERT(src_w > 0U, "Source width must be positive");
92 RA8_ASSERT(src_h > 0U, "Source height must be positive");
93
94 uint16_t sx0 = (uint16_t)(sx_fp >> k_fp_shift);
95 uint16_t sy0 = (uint16_t)(sy_fp >> k_fp_shift);
96 uint16_t sx1 =
97 (uint16_t)((((uint32_t)sx0 + 1U) < (uint32_t)src_w) ? ((uint32_t)sx0 + 1U) : (src_w - 1U));
98 uint16_t sy1 =
99 (uint16_t)((((uint32_t)sy0 + 1U) < (uint32_t)src_h) ? ((uint32_t)sy0 + 1U) : (src_h - 1U));
100 uint64_t fx = sx_fp & (uint64_t)k_fp_mask;
101 uint64_t fy = sy_fp & (uint64_t)k_fp_mask;
102 uint64_t ifx = (uint64_t)k_fp_unit - fx;
103 uint64_t ify = (uint64_t)k_fp_unit - fy;
104
105 uint64_t p00 = src[((uint32_t)sy0 * src_w) + sx0];
106 uint64_t p10 = src[((uint32_t)sy0 * src_w) + sx1];
107 uint64_t p01 = src[((uint32_t)sy1 * src_w) + sx0];
108 uint64_t p11 = src[((uint32_t)sy1 * src_w) + sx1];
109
110 uint64_t val = (p00 * ifx * ify) + (p10 * fx * ify) + (p01 * ifx * fy) + (p11 * fx * fy);
111 return (uint8_t)(val >> k_fp_norm_shift);
112}
113
137static void internal_pack_nibbles(const uint8_t* gray_pixels, uint32_t n_pixels, uint8_t* out)
138{
139 for (uint32_t i = 0U; i < n_pixels; i++) {
140 uint8_t nib = (uint8_t)((gray_pixels[i] + (uint8_t)k_ra8_rabook_gray4_round_half) /
142 if (nib > (uint8_t)k_ra8_rabook_gray4_nib_max) {
143 nib = (uint8_t)k_ra8_rabook_gray4_nib_max;
144 }
145
146 uint32_t byte_idx = i / k_ra8_rabook_gray4_nib_per_byte;
147 if ((i & (uint32_t)k_nib_lo_msk) == 0U) {
148 out[byte_idx] = (uint8_t)(nib << k_nib_shift);
149 } else {
150 out[byte_idx] |= nib;
151 }
152 }
153}
154
155/* -------------------------------------------------------------------------- */
156/* Public API */
157/* -------------------------------------------------------------------------- */
158
160 uint16_t src_h,
161 uint16_t max_edge,
162 uint16_t* out_w,
163 uint16_t* out_h)
164{
165 if ((out_w == nullptr) || (out_h == nullptr)) {
166 return;
167 }
168
169 if ((src_w == 0U) || (src_h == 0U) || (max_edge == 0U)) {
170 *out_w = 0U;
171 *out_h = 0U;
172 return;
173 }
174
175 uint16_t longer = (src_w >= src_h) ? src_w : src_h;
176 if (longer <= max_edge) {
177 *out_w = src_w;
178 *out_h = src_h;
179 return;
180 }
181
182 uint32_t half_longer = (uint32_t)longer / 2U;
183 *out_w = (uint16_t)((((uint32_t)src_w * max_edge) + half_longer) / longer);
184 *out_h = (uint16_t)((((uint32_t)src_h * max_edge) + half_longer) / longer);
185
186 if (*out_w == 0U) {
187 *out_w = 1U;
188 }
189 if (*out_h == 0U) {
190 *out_h = 1U;
191 }
192}
193
195 uint16_t src_w,
196 uint16_t src_h,
197 uint8_t* dst,
198 uint16_t dst_w,
199 uint16_t dst_h)
200{
201 RA8_CHECK_NULL_PTR(src, s_tag, "src");
202 RA8_CHECK_NULL_PTR(dst, s_tag, "dst");
203
204 if ((dst_w == 0U) || (dst_h == 0U)) {
205 ra8_log_error(s_tag, "dst_w or dst_h is zero");
207 }
208
209 if ((src_w == 0U) || (src_h == 0U)) {
210 (void)memset(dst, 0, (size_t)dst_w * (size_t)dst_h);
211 return k_ra8_ok;
212 }
213
214 uint64_t x_step = ((uint64_t)src_w << k_fp_shift) / dst_w;
215 uint64_t y_step = ((uint64_t)src_h << k_fp_shift) / dst_h;
216
217 for (uint16_t dy = 0U; dy < dst_h; dy++) {
218 uint64_t sy_fp = (uint64_t)dy * y_step;
219
220 for (uint16_t dx = 0U; dx < dst_w; dx++) {
221 uint64_t sx_fp = (uint64_t)dx * x_step;
222 dst[((uint32_t)dy * dst_w) + dx] = internal_bilinear_sample(src, src_w, src_h, sx_fp, sy_fp);
223 }
224 }
225
226 return k_ra8_ok;
227}
228
229ra8_err_t ra8_rabook_gray4_encode(const uint8_t* gray_pixels,
230 uint16_t w,
231 uint16_t h,
232 uint8_t* out,
233 uint32_t out_cap,
234 uint32_t* out_size)
235{
236 RA8_CHECK_NULL_PTR(gray_pixels, s_tag, "gray_pixels");
237 RA8_CHECK_NULL_PTR(out, s_tag, "out");
238 RA8_CHECK_NULL_PTR(out_size, s_tag, "out_size");
239
240 uint32_t n_pixels = (uint32_t)w * h;
241 uint32_t n_bytes = (n_pixels + 1U) / k_ra8_rabook_gray4_nib_per_byte;
242
243 if (n_pixels == 0U) {
244 *out_size = 0U;
245 return k_ra8_ok;
246 }
247
248 if (out_cap < n_bytes) {
249 ra8_log_error(s_tag, "output buffer too small");
250 return k_ra8_err_no_mem;
251 }
252
253 (void)memset(out, 0, n_bytes);
254 internal_pack_nibbles(gray_pixels, n_pixels, out);
255
256 *out_size = n_bytes;
257 return k_ra8_ok;
258}
259
260ra8_err_t ra8_rabook_gray8_encode(const uint8_t* gray_pixels,
261 uint16_t w,
262 uint16_t h,
263 uint8_t* out,
264 uint32_t out_cap,
265 uint32_t* out_size)
266{
267 RA8_CHECK_NULL_PTR(gray_pixels, s_tag, "gray_pixels");
268 RA8_CHECK_NULL_PTR(out, s_tag, "out");
269 RA8_CHECK_NULL_PTR(out_size, s_tag, "out_size");
270
271 uint32_t n_bytes = (uint32_t)w * h;
272
273 if (n_bytes == 0U) {
274 *out_size = 0U;
275 return k_ra8_ok;
276 }
277
278 if (out_cap < n_bytes) {
279 ra8_log_error(s_tag, "output buffer too small");
280 return k_ra8_err_no_mem;
281 }
282
283 (void)memcpy(out, gray_pixels, n_bytes);
284
285 *out_size = n_bytes;
286 return k_ra8_ok;
287}
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_ASSERT(condition, message)
Runtime invariant check.
Definition ra8_check.h:123
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
Error Code Definitions for ra8-firmware.
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
ra8_err_t ra8_rabook_gray4_encode(const uint8_t *gray_pixels, uint16_t w, uint16_t h, uint8_t *out, uint32_t out_cap, uint32_t *out_size)
Quantise a grayscale buffer to 16 levels and pack as 4-bpp nibbles.
ra8_err_t ra8_rabook_gray4_downscale(const uint8_t *src, uint16_t src_w, uint16_t src_h, uint8_t *dst, uint16_t dst_w, uint16_t dst_h)
Bilinear-interpolate a grayscale image from (src_w x src_h) to (dst_w x dst_h).
static uint8_t internal_bilinear_sample(const uint8_t *src, uint16_t src_w, uint16_t src_h, uint64_t sx_fp, uint64_t sy_fp)
Sample one bilinear-interpolated pixel at fixed-point source (sx_fp, sy_fp).
ra8_gray4_fp_t
Q16.16 fixed-point constants for the bilinear downscale kernel.
@ k_fp_norm_shift
Right-shift to normalise 64-bit product.
@ k_fp_shift
Integer part starts at bit 16.
@ k_fp_mask
Fractional-bits mask (low 16 bits).
@ k_fp_unit
1.0 in Q16.16 (65536).
ra8_err_t ra8_rabook_gray8_encode(const uint8_t *gray_pixels, uint16_t w, uint16_t h, uint8_t *out, uint32_t out_cap, uint32_t *out_size)
Copy a grayscale buffer out verbatim as 8-bpp (one byte per pixel).
static void internal_pack_nibbles(const uint8_t *gray_pixels, uint32_t n_pixels, uint8_t *out)
Quantise n_pixels grayscale bytes to 4-bpp nibbles packed into out.
ra8_gray4_nib_t
Nibble packing constants.
@ k_nib_shift
Shift even pixel into the high nibble of a byte.
@ k_nib_lo_msk
Bit 0: whether pixel index is odd (low nibble).
void ra8_rabook_gray4_output_dims(uint16_t src_w, uint16_t src_h, uint16_t max_edge, uint16_t *out_w, uint16_t *out_h)
Compute scaled output dimensions keeping the longer edge within max_edge.
Grayscale image transcode stage (4-bpp / 8-bpp) for the on-device EPUB compiler (#149).
@ k_ra8_rabook_gray4_quant_div
Palette step: 255 / 15 rounds to 17.
@ k_ra8_rabook_gray4_nib_per_byte
Pixels packed per output byte.
@ k_ra8_rabook_gray4_nib_max
Maximum nibble value (4 bits unsigned).
@ k_ra8_rabook_gray4_round_half
Added before quant divide to round-to-nearest.