ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_gfx_text.c
Go to the documentation of this file.
1
19
20#include <stddef.h>
21#include <stdint.h>
22#include <string.h>
23
24#include "ra8_err.h"
25#include "ra8_gfx.h"
26#include "ra8_gfx_internal.h"
27
29typedef enum : uint32_t {
30 k_565_r_mask = 0x1FU,
31 k_565_g_mask = 0x3FU,
32 k_565_b_mask = 0x1FU,
34
45
50typedef enum : uint32_t {
51 k_mask_byte = 0xFFU,
53
65
79
88
90typedef enum : uint8_t {
93
107
108/* ------------------------------------------------------------------ */
109/* Helpers */
110/* ------------------------------------------------------------------ */
111
127static inline uint8_t internal_color_r(uint32_t color)
128{
129 return (uint8_t)((color >> k_shift_red) & k_mask_byte);
130}
131
145static inline uint8_t internal_color_g(uint32_t color)
146{
147 return (uint8_t)((color >> k_shift_green) & k_mask_byte);
148}
149
163static inline uint8_t internal_color_b(uint32_t color)
164{
165 return (uint8_t)((color >> k_shift_blue) & k_mask_byte);
166}
167
181static inline uint8_t internal_color_a(uint32_t color)
182{
183 return (uint8_t)((color >> k_shift_alpha) & k_mask_byte);
184}
185
187uint16_t priv_gfx_text_pack_565(uint32_t color)
188{
189 const uint16_t r = (uint16_t)(internal_color_r(color) >> k_565_r_shift_in);
190 const uint16_t g = (uint16_t)(internal_color_g(color) >> k_565_g_shift_in);
191 const uint16_t b = (uint16_t)(internal_color_b(color) >> k_565_b_shift_in);
192 return (uint16_t)((r << k_565_r_shift_out) | (g << k_565_g_shift_out) | b);
193}
194
210static inline uint8_t internal_bpp(ra8_gfx_format_t format)
211{
212 return (uint8_t)format;
213}
214
231{
232 return (f == k_ra8_gfx_format_rgb565) || (f == k_ra8_gfx_format_rgb888) ||
234}
235
255static void internal_put_pixel(uint8_t* dst,
256 size_t stride,
257 ra8_gfx_format_t format,
258 size_t x,
259 size_t y,
260 uint32_t color)
261{
262 uint8_t* p = dst + (y * stride) + (x * (size_t)internal_bpp(format));
263 switch (format) {
265 const uint16_t v = priv_gfx_text_pack_565(color);
266 p[k_idx_r] = (uint8_t)(v & k_mask_byte);
267 p[k_idx_g] = (uint8_t)((v >> k_glyph_bits_per_byte) & k_mask_byte);
268 break;
269 }
271 p[k_idx_r] = internal_color_r(color);
272 p[k_idx_g] = internal_color_g(color);
273 p[k_idx_b] = internal_color_b(color);
274 break;
275 }
277 p[k_idx_argb_b] = internal_color_b(color);
278 p[k_idx_argb_g] = internal_color_g(color);
279 p[k_idx_argb_r] = internal_color_r(color);
280 p[k_idx_argb_a] = internal_color_a(color);
281 break;
282 }
283 }
284}
285
305static uint32_t
306internal_get_pixel(const uint8_t* src, size_t stride, ra8_gfx_format_t format, size_t x, size_t y)
307{
308 const uint8_t* p = src + (y * stride) + (x * (size_t)internal_bpp(format));
309 switch (format) {
311 const uint16_t v = (uint16_t)(p[k_idx_r] | ((uint16_t)p[k_idx_g] << k_glyph_bits_per_byte));
312 const uint32_t r = ((uint32_t)(v >> k_565_r_shift_out) & k_565_r_mask) << k_565_r_shift_in;
313 const uint32_t g = ((uint32_t)(v >> k_565_g_shift_out) & k_565_g_mask) << k_565_g_shift_in;
314 const uint32_t b = ((uint32_t)v & k_565_b_mask) << k_565_b_shift_in;
315 return (r << k_shift_red) | (g << k_shift_green) | (b << k_shift_blue);
316 }
318 const uint32_t r = p[k_idx_r];
319 const uint32_t g = p[k_idx_g];
320 const uint32_t b = p[k_idx_b];
321 return (r << k_shift_red) | (g << k_shift_green) | (b << k_shift_blue);
322 }
324 const uint32_t bb = p[k_idx_argb_b];
325 const uint32_t gg = p[k_idx_argb_g];
326 const uint32_t rr = p[k_idx_argb_r];
327 const uint32_t aa = p[k_idx_argb_a];
328 return (aa << k_shift_alpha) | (rr << k_shift_red) | (gg << k_shift_green) | bb;
329 }
330 }
331 return 0U;
332}
333
335void priv_gfx_text_plot(int32_t x, int32_t y, uint32_t color)
336{
337 /* The clip rectangle is always within the framebuffer, so this single test
338 * enforces both the clip and the framebuffer bounds. With the default clip
339 * (0,0,width,height) it is identical to a plain bounds check. */
340 if ((x < g_gfx_text_state.clip_x0) || (y < g_gfx_text_state.clip_y0)) {
341 return;
342 }
343 if ((x >= g_gfx_text_state.clip_x1) || (y >= g_gfx_text_state.clip_y1)) {
344 return;
345 }
347 (size_t)g_gfx_text_state.width * (size_t)g_gfx_text_state.bpp,
348 g_gfx_text_state.format,
349 (size_t)x,
350 (size_t)y,
351 color);
352}
353
376static inline void internal_fill_565(uint8_t* p, size_t count, uint8_t lo, uint8_t hi)
377{
378 if (lo == hi) {
379 /* Symmetric packed colour (e.g. white paper 0xFFFF, black 0x0000): every
380 * byte of the run is the same value, so the optimised libc memset replaces
381 * the byte loop. Byte-identical -- both write `lo` to every byte. This is the
382 * common full-screen clear, so it is the hot path. */
383 (void)memset(p, (int)lo, count * (size_t)k_rgb565_bpp);
384 return;
385 }
386 for (size_t i = 0; i < count; i++) {
387 *p++ = lo;
388 *p++ = hi;
389 }
390}
391
415static void internal_fill_rect_565(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
416{
417 /* Clip against the active clip rectangle (which is itself within the
418 * framebuffer), so this is both the on-screen clip and the dirty-region clip.
419 * With the default full-framebuffer clip it reduces to min/max against the
420 * framebuffer, i.e. byte-identical to the unclipped fill. */
421 const int32_t cl = g_gfx_text_state.clip_x0;
422 const int32_t ct = g_gfx_text_state.clip_y0;
423 const int32_t cr = g_gfx_text_state.clip_x1;
424 const int32_t cb = g_gfx_text_state.clip_y1;
425 const int32_t x0 = (x >= cl) ? x : cl;
426 const int32_t y0 = (y >= ct) ? y : ct;
427 /* Right/bottom edges in 64-bit so x+w / y+h cannot overflow int32 for a
428 * pathological caller; then clamped to the clip. */
429 const int64_t xr = (int64_t)x + (int64_t)w;
430 const int64_t yr = (int64_t)y + (int64_t)h;
431 const int32_t x1 = (xr < (int64_t)cr) ? (int32_t)xr : cr;
432 const int32_t y1 = (yr < (int64_t)cb) ? (int32_t)yr : cb;
433 if ((x1 <= x0) || (y1 <= y0)) {
434 return; /* fully clipped -- nothing to fill. */
435 }
436 const uint16_t v = priv_gfx_text_pack_565(color);
437 const uint8_t lo = (uint8_t)(v & (uint16_t)k_mask_byte);
438 const uint8_t hi = (uint8_t)((v >> k_glyph_bits_per_byte) & (uint16_t)k_mask_byte);
439 const size_t bpp = (size_t)g_gfx_text_state.bpp;
440 const size_t stride = (size_t)g_gfx_text_state.width * bpp;
441 const size_t count = (size_t)(x1 - x0);
442 for (int32_t row = y0; row < y1; row++) {
443 internal_fill_565(g_gfx_text_state.fb + ((size_t)row * stride) + ((size_t)x0 * bpp),
444 count,
445 lo,
446 hi);
447 }
448}
449
470static void internal_fill_rect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
471{
473 internal_fill_rect_565(x, y, w, h, color);
474 return;
475 }
476 for (int32_t row = 0; row < h; row++) {
477 for (int32_t col = 0; col < w; col++) {
478 priv_gfx_text_plot(x + col, y + row, color);
479 }
480 }
481}
482
506static void internal_rect_outline(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
507{
508 for (int32_t col = 0; col < w; col++) {
509 priv_gfx_text_plot(x + col, y, color);
510 priv_gfx_text_plot(x + col, y + h - 1, color);
511 }
512 for (int32_t row = 0; row < h; row++) {
513 priv_gfx_text_plot(x, y + row, color);
514 priv_gfx_text_plot(x + w - 1, y + row, color);
515 }
516}
517
518/* ------------------------------------------------------------------ */
519/* Public API */
520/* ------------------------------------------------------------------ */
521
522ra8_err_t ra8_gfx_init(void* fb, uint16_t width, uint16_t height, ra8_gfx_format_t format)
523{
524 if (fb == nullptr) {
525 return k_ra8_err_null_ptr;
526 }
527 if ((width < k_ra8_gfx_min_dim) || (width > k_ra8_gfx_max_dim)) {
529 }
530 if ((height < k_ra8_gfx_min_dim) || (height > k_ra8_gfx_max_dim)) {
532 }
533 if (!internal_format_ok(format)) {
535 }
536 g_gfx_text_state.fb = (uint8_t*)fb;
537 g_gfx_text_state.width = width;
538 g_gfx_text_state.height = height;
539 g_gfx_text_state.format = format;
540 g_gfx_text_state.bpp = internal_bpp(format);
541 g_gfx_text_state.clip_x0 = 0;
542 g_gfx_text_state.clip_y0 = 0;
543 g_gfx_text_state.clip_x1 = (int32_t)width; /* default clip = whole framebuffer. */
544 g_gfx_text_state.clip_y1 = (int32_t)height;
545 g_gfx_text_state.initialized = true;
546 return k_ra8_ok;
547}
548
550{
551 if (!g_gfx_text_state.initialized) {
553 }
554 /* "Clear" fills the active clip region (the whole framebuffer by default).
555 * For RGB565 this is the span-fill path -- pack once, memset each row when the
556 * bytes are symmetric -- restricted to the clip, so an incremental repaint
557 * only touches the damaged area. With the default clip the rect is the whole
558 * framebuffer and the bytes written are identical to the old contiguous fill. */
561 0,
562 (int32_t)g_gfx_text_state.width,
563 (int32_t)g_gfx_text_state.height,
564 color);
565 return k_ra8_ok;
566 }
567 for (int32_t y = g_gfx_text_state.clip_y0; y < g_gfx_text_state.clip_y1; y++) {
568 for (int32_t x = g_gfx_text_state.clip_x0; x < g_gfx_text_state.clip_x1; x++) {
570 (size_t)g_gfx_text_state.width * (size_t)g_gfx_text_state.bpp,
571 g_gfx_text_state.format,
572 (size_t)x,
573 (size_t)y,
574 color);
575 }
576 }
577 return k_ra8_ok;
578}
579
580ra8_err_t ra8_gfx_set_clip(int32_t x, int32_t y, int32_t w, int32_t h)
581{
582 if (!g_gfx_text_state.initialized) {
584 }
585 const int32_t fbw = (int32_t)g_gfx_text_state.width;
586 const int32_t fbh = (int32_t)g_gfx_text_state.height;
587 /* Clamp the top-left into [0, fb]; the bottom-right into [top-left, fb]. The
588 * edges are computed in 64-bit so x+w / y+h cannot overflow int32, and a
589 * non-positive size or fully off-screen rect collapses to an empty clip
590 * (x1==x0 or y1==y0), which makes every subsequent draw a no-op. */
591 int32_t x0 = (x > 0) ? x : 0;
592 int32_t y0 = (y > 0) ? y : 0;
593 if (x0 > fbw) {
594 x0 = fbw;
595 }
596 if (y0 > fbh) {
597 y0 = fbh;
598 }
599 const int64_t xr = (int64_t)x + (int64_t)w;
600 const int64_t yr = (int64_t)y + (int64_t)h;
601 int32_t x1 = (xr < (int64_t)fbw) ? (int32_t)xr : fbw;
602 int32_t y1 = (yr < (int64_t)fbh) ? (int32_t)yr : fbh;
603 if (x1 < x0) {
604 x1 = x0;
605 }
606 if (y1 < y0) {
607 y1 = y0;
608 }
609 g_gfx_text_state.clip_x0 = x0;
610 g_gfx_text_state.clip_y0 = y0;
611 g_gfx_text_state.clip_x1 = x1;
612 g_gfx_text_state.clip_y1 = y1;
613 return k_ra8_ok;
614}
615
617{
618 if (!g_gfx_text_state.initialized) {
620 }
621 g_gfx_text_state.clip_x0 = 0;
622 g_gfx_text_state.clip_y0 = 0;
623 g_gfx_text_state.clip_x1 = (int32_t)g_gfx_text_state.width;
624 g_gfx_text_state.clip_y1 = (int32_t)g_gfx_text_state.height;
625 return k_ra8_ok;
626}
627
628ra8_err_t ra8_gfx_pixel(int32_t x, int32_t y, uint32_t color)
629{
630 if (!g_gfx_text_state.initialized) {
632 }
633 if ((x < 0) || (y < 0) || (x >= (int32_t)g_gfx_text_state.width) ||
634 (y >= (int32_t)g_gfx_text_state.height)) {
636 }
637 priv_gfx_text_plot(x, y, color);
638 return k_ra8_ok;
639}
640
664static inline uint32_t internal_gray_to_color(uint32_t g)
665{
666 return (g << (uint32_t)k_shift_red) | (g << (uint32_t)k_shift_green) |
667 (g << (uint32_t)k_shift_blue);
668}
669
699static void internal_blit_gray8_565(const uint8_t* src,
700 int32_t w,
701 int32_t dst_x,
702 int32_t dst_y,
703 int32_t x0,
704 int32_t y0,
705 int32_t x1,
706 int32_t y1)
707{
708 const size_t stride = (size_t)g_gfx_text_state.width * (size_t)g_gfx_text_state.bpp;
709 for (int32_t y = y0; y < y1; ++y) {
710 uint8_t* p = g_gfx_text_state.fb + ((size_t)y * stride) + ((size_t)x0 * (size_t)k_rgb565_bpp);
711 const uint8_t* s = src + ((size_t)(y - dst_y) * (size_t)w) + (size_t)(x0 - dst_x);
712 for (int32_t x = x0; x < x1; ++x) {
713 const uint16_t v = priv_gfx_text_pack_565(internal_gray_to_color((uint32_t)*s));
714 p[k_idx_r] = (uint8_t)(v & k_mask_byte);
715 p[k_idx_g] = (uint8_t)((v >> k_glyph_bits_per_byte) & k_mask_byte);
716 p += k_rgb565_bpp;
717 ++s;
718 }
719 }
720}
721
746static void
747internal_blit_gray8_slow(const uint8_t* src, int32_t w, int32_t h, int32_t dx, int32_t dy)
748{
749 for (int32_t y = 0; y < h; ++y) {
750 for (int32_t x = 0; x < w; ++x) {
751 priv_gfx_text_plot(dx + x,
752 dy + y,
753 internal_gray_to_color(src[((size_t)y * (size_t)w) + (size_t)x]));
754 }
755 }
756}
757
758ra8_err_t ra8_gfx_blit_gray8(const uint8_t* src, int32_t w, int32_t h, int32_t dst_x, int32_t dst_y)
759{
760 if (!g_gfx_text_state.initialized) {
762 }
763 if ((src == nullptr) || (w <= 0) || (h <= 0)) {
765 }
766 /* Non-panel formats keep the exact per-pixel path; the RGB565 panel resolves
767 * the clip once and runs a tight row loop. */
769 internal_blit_gray8_slow(src, w, h, dst_x, dst_y);
770 return k_ra8_ok;
771 }
772 /* Clip the block to the clip rectangle ONCE (always within the framebuffer). */
773 const int32_t x0 = (dst_x > g_gfx_text_state.clip_x0) ? dst_x : g_gfx_text_state.clip_x0;
774 const int32_t y0 = (dst_y > g_gfx_text_state.clip_y0) ? dst_y : g_gfx_text_state.clip_y0;
775 const int32_t x1 =
776 ((dst_x + w) < g_gfx_text_state.clip_x1) ? (dst_x + w) : g_gfx_text_state.clip_x1;
777 const int32_t y1 =
778 ((dst_y + h) < g_gfx_text_state.clip_y1) ? (dst_y + h) : g_gfx_text_state.clip_y1;
779 if ((x0 < x1) && (y0 < y1)) {
780 internal_blit_gray8_565(src, w, dst_x, dst_y, x0, y0, x1, y1);
781 }
782 return k_ra8_ok;
783}
784
785ra8_err_t ra8_gfx_line(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color)
786{
787 if (!g_gfx_text_state.initialized) {
789 }
790 /* Bresenham. */
791 int32_t dx = (x1 >= x0) ? (x1 - x0) : (x0 - x1);
792 int32_t dy = (y1 >= y0) ? -(y1 - y0) : -(y0 - y1);
793 const int32_t sx = (x0 < x1) ? 1 : -1;
794 const int32_t sy = (y0 < y1) ? 1 : -1;
795 int32_t err = dx + dy;
796 int32_t x = x0;
797 int32_t y = y0;
798 /* Bound the iteration count statically (NASA Rule 2). */
799 const int32_t max_iters = (int32_t)(k_ra8_gfx_max_dim * 2);
800 for (int32_t i = 0; i < max_iters; i++) {
801 priv_gfx_text_plot(x, y, color);
802 if ((x == x1) && (y == y1)) {
803 break;
804 }
805 const int32_t e2 = err * 2;
806 if (e2 >= dy) {
807 err += dy;
808 x += sx;
809 }
810 if (e2 <= dx) {
811 err += dx;
812 y += sy;
813 }
814 }
815 return k_ra8_ok;
816}
817
818ra8_err_t ra8_gfx_rect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color, bool filled)
819{
820 if (!g_gfx_text_state.initialized) {
822 }
823 if ((w <= 0) || (h <= 0)) {
824 return k_ra8_ok;
825 }
826 if (filled) {
827 internal_fill_rect(x, y, w, h, color);
828 } else {
829 internal_rect_outline(x, y, w, h, color);
830 }
831 return k_ra8_ok;
832}
833
851static void
852internal_circle_outline_step(int32_t cx, int32_t cy, int32_t x, int32_t y, uint32_t color)
853{
854 priv_gfx_text_plot(cx + x, cy + y, color);
855 priv_gfx_text_plot(cx - x, cy + y, color);
856 priv_gfx_text_plot(cx + x, cy - y, color);
857 priv_gfx_text_plot(cx - x, cy - y, color);
858 priv_gfx_text_plot(cx + y, cy + x, color);
859 priv_gfx_text_plot(cx - y, cy + x, color);
860 priv_gfx_text_plot(cx + y, cy - x, color);
861 priv_gfx_text_plot(cx - y, cy - x, color);
862}
863
881static void
882internal_circle_filled_step(int32_t cx, int32_t cy, int32_t x, int32_t y, uint32_t color)
883{
884 for (int32_t col = -x; col <= x; col++) {
885 priv_gfx_text_plot(cx + col, cy + y, color);
886 priv_gfx_text_plot(cx + col, cy - y, color);
887 }
888 for (int32_t col = -y; col <= y; col++) {
889 priv_gfx_text_plot(cx + col, cy + x, color);
890 priv_gfx_text_plot(cx + col, cy - x, color);
891 }
892}
893
894ra8_err_t ra8_gfx_circle(int32_t cx, int32_t cy, int32_t r, uint32_t color, bool filled)
895{
896 if (!g_gfx_text_state.initialized) {
898 }
899 if (r < 0) {
901 }
902 /* Midpoint circle algorithm. */
903 int32_t x = r;
904 int32_t y = 0;
905 int32_t err = 1 - r;
906 /* Static loop bound. */
907 const int32_t max_iters = (int32_t)k_ra8_gfx_max_dim;
908 for (int32_t i = 0; i < max_iters; i++) {
909 if (filled) {
910 internal_circle_filled_step(cx, cy, x, y, color);
911 } else {
912 internal_circle_outline_step(cx, cy, x, y, color);
913 }
914 if (x <= y) {
915 break;
916 }
917 y++;
918 if (err < 0) {
919 err += (2 * y) + 1;
920 } else {
921 x--;
922 err += (2 * (y - x)) + 1;
923 }
924 }
925 return k_ra8_ok;
926}
927
928ra8_err_t ra8_gfx_blit(const void* src_buf,
929 uint16_t src_w,
930 uint16_t src_h,
931 ra8_gfx_format_t src_format,
932 int32_t dst_x,
933 int32_t dst_y)
934{
935 if (src_buf == nullptr) {
936 return k_ra8_err_null_ptr;
937 }
938 if (!g_gfx_text_state.initialized) {
940 }
941 if ((src_w == 0) || (src_h == 0) || !internal_format_ok(src_format)) {
943 }
944 const uint8_t* src = (const uint8_t*)src_buf;
945 const size_t src_stride = (size_t)src_w * (size_t)internal_bpp(src_format);
946 for (uint32_t row = 0; row < src_h; row++) {
947 for (uint32_t col = 0; col < src_w; col++) {
948 const uint32_t color = internal_get_pixel(src, src_stride, src_format, col, row);
949 priv_gfx_text_plot(dst_x + (int32_t)col, dst_y + (int32_t)row, color);
950 }
951 }
952 return k_ra8_ok;
953}
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_range_check_failed
Value outside range enforced by RA8_CHECK_RANGE / RA8_CHECK_RANGE_TAG.
Definition ra8_err.h:471
@ k_ra8_err_not_initialized
Module not initialized – _init() not yet called successfully.
Definition ra8_err.h:235
@ 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
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.
Software 2D graphics primitives layered on top of a caller-ownedframebuffer (DRW / D/AVE 2D / GLCDC r...
ra8_gfx_format_t
Pixel format of the framebuffer bound by ra8_gfx_init().
Definition ra8_gfx.h:39
@ k_ra8_gfx_format_rgb565
16-bit RGB565, little-endian in memory.
Definition ra8_gfx.h:40
@ k_ra8_gfx_format_argb8888
32-bit ARGB, A in MSB.
Definition ra8_gfx.h:42
@ k_ra8_gfx_format_rgb888
24-bit packed R,G,B bytes.
Definition ra8_gfx.h:41
@ k_ra8_gfx_min_dim
Minimum width or height in pixels.
Definition ra8_gfx.h:50
@ k_ra8_gfx_max_dim
Maximum supported edge length.
Definition ra8_gfx.h:51
Module-private declarations shared across the ra8_gfx software rasteriser TUs.
ra8_gfx_state_t g_gfx_text_state
Single module-wide framebuffer binding shared by every ra8_gfx TU.
ra8_gfx_rgb565_bpp_t
RGB565 bytes per pixel (used to size span byte counts).
@ k_rgb565_bpp
Rgb565 bpp.
ra8_err_t ra8_gfx_circle(int32_t cx, int32_t cy, int32_t r, uint32_t color, bool filled)
Draw a circle using midpoint algorithm.
static uint8_t internal_color_b(uint32_t color)
Internal helper.
gfx_565_mask_t
RGB565 channel masks.
@ k_565_g_mask
6-bit green.
@ k_565_b_mask
5-bit blue.
@ k_565_r_mask
5-bit red.
static void internal_fill_rect_565(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
Span-fill an axis-aligned RGB565 rectangle, clipped to the framebuffer.
static uint8_t internal_color_a(uint32_t color)
Internal helper.
ra8_err_t ra8_gfx_line(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color)
Draw a line from (x0,y0) to (x1,y1) with Bresenham's algorithm.
ra8_err_t ra8_gfx_reset_clip(void)
Reset the clip rectangle to the whole framebuffer.
static uint32_t internal_get_pixel(const uint8_t *src, size_t stride, ra8_gfx_format_t format, size_t x, size_t y)
Read one pixel from a source buffer and return it as 0xAARRGGBB.
static void internal_fill_565(uint8_t *p, size_t count, uint8_t lo, uint8_t hi)
Fill count consecutive RGB565 pixels at p with packed bytes lo,hi.
static uint32_t internal_gray_to_color(uint32_t g)
Expand an 8-bit gray sample to the 0x00RRGGBB colour word priv_gfx_text_pack_565 expects.
static void internal_blit_gray8_565(const uint8_t *src, int32_t w, int32_t dst_x, int32_t dst_y, int32_t x0, int32_t y0, int32_t x1, int32_t y1)
RGB565 fast path for ra8_gfx_blit_gray8(): clip pre-resolved, tight rows.
ra8_gfx_color_shifts_t
Bit positions for unpacking 0xAARRGGBB colour values.
@ k_shift_blue
Shift blue.
@ k_shift_green
Shift green.
@ k_shift_red
Shift red.
@ k_shift_alpha
Shift alpha.
static uint8_t internal_color_r(uint32_t color)
Extract red byte from a 0xAARRGGBB colour.
static void internal_circle_outline_step(int32_t cx, int32_t cy, int32_t x, int32_t y, uint32_t color)
Plot the eight symmetric points of a midpoint-circle step.
ra8_err_t ra8_gfx_blit(const void *src_buf, uint16_t src_w, uint16_t src_h, ra8_gfx_format_t src_format, int32_t dst_x, int32_t dst_y)
Copy a sub-image from src_buf into the framebuffer at (dst_x,dst_y).
ra8_gfx_glyph_bits_t
Constants for monochrome glyph bit extraction.
@ k_glyph_bits_per_byte
Glyph bits per byte.
@ k_glyph_msb_index
Glyph msb index.
ra8_err_t ra8_gfx_set_clip(int32_t x, int32_t y, int32_t w, int32_t h)
Restrict all subsequent drawing to a rectangle (dirty-region updates).
ra8_err_t ra8_gfx_clear(uint32_t color)
Fill the bound framebuffer's clip region with a single colour.
ra8_gfx_byte_idx_t
Per-byte indices for RGB888 packing.
@ k_idx_argb_r
Index argb r.
@ k_idx_b
Index b.
@ k_idx_r
Index r.
@ k_idx_argb_b
Index argb b.
@ k_idx_argb_a
Index argb a.
@ k_idx_argb_g
Index argb g.
@ k_idx_g
Index g.
ra8_gfx_color_masks_t
Per-channel masks for colour conversion.
@ k_mask_byte
Mask byte.
ra8_gfx_565_t
RGB565 packing constants.
@ k_565_b_shift_in
drop 3 LSBs of B (8 -> 5 bits).
@ k_565_g_shift_in
drop 2 LSBs of G (8 -> 6 bits).
@ k_565_g_shift_out
565 g shift out.
@ k_565_r_shift_out
565 r shift out.
@ k_565_r_shift_in
drop 3 LSBs of R (8 -> 5 bits).
static bool internal_format_ok(ra8_gfx_format_t f)
Validate a format enum.
static void internal_put_pixel(uint8_t *dst, size_t stride, ra8_gfx_format_t format, size_t x, size_t y, uint32_t color)
Write one pixel into a generic destination buffer.
static void internal_circle_filled_step(int32_t cx, int32_t cy, int32_t x, int32_t y, uint32_t color)
Plot the two horizontal scan-lines for a midpoint-circle step.
void priv_gfx_text_plot(int32_t x, int32_t y, uint32_t color)
Implementation of priv_gfx_text_plot() – promoted for cross-TU use.
ra8_err_t ra8_gfx_pixel(int32_t x, int32_t y, uint32_t color)
Set a single pixel.
static uint8_t internal_bpp(ra8_gfx_format_t format)
Bytes per pixel for a given format.
static uint8_t internal_color_g(uint32_t color)
Internal helper.
static void internal_fill_rect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
Fill a solid rectangle (RGB565 span fast path, else per-pixel).
uint16_t priv_gfx_text_pack_565(uint32_t color)
Implementation of priv_gfx_text_pack_565() – promoted for cross-TU use.
static void internal_blit_gray8_slow(const uint8_t *src, int32_t w, int32_t h, int32_t dx, int32_t dy)
Per-pixel fallback for non-RGB565 formats, matching priv_gfx_text_plot() exactly.
static void internal_rect_outline(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
Draw the four edges of a 1-pixel-wide rectangle outline.
ra8_err_t ra8_gfx_init(void *fb, uint16_t width, uint16_t height, ra8_gfx_format_t format)
Bind ra8_gfx to a caller-owned framebuffer.
ra8_err_t ra8_gfx_blit_gray8(const uint8_t *src, int32_t w, int32_t h, int32_t dst_x, int32_t dst_y)
Blit an 8-bit grayscale image to a framebuffer rectangle.
ra8_err_t ra8_gfx_rect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color, bool filled)
Draw an axis-aligned rectangle.
Internal module state populated by ra8_gfx_init().