1/* SPDX-License-Identifier: BSD-2-Clause */
2
3/* Copyright (C) 2022-2026 Mintsuki and contributors.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#ifdef __cplusplus
29#error "Please do not compile Flanterm as C++ code! Flanterm should be compiled as C99 or newer."
30#endif
31
32#ifndef __STDC_VERSION__
33#error "Flanterm must be compiled as C99 or newer."
34#endif
35
36#if defined(_MSC_VER)
37#define ALWAYS_INLINE __forceinline
38#elif defined(__GNUC__) || defined(__clang__)
39#define ALWAYS_INLINE __attribute__((always_inline)) inline
40#else
41#define ALWAYS_INLINE inline
42#endif
43
44#include <stdint.h>
45#include <stddef.h>
46#include <stdbool.h>
47
48#ifndef FLANTERM_IN_FLANTERM
49#define FLANTERM_IN_FLANTERM
50#endif
51
52#include "../flanterm.h"
53#include "fb.h"
54
55void *memset(void *, int, size_t);
56void *memcpy(void *, const void *, size_t);
57
58#if defined(__GNUC__) || defined(__clang__)
59#define memset __builtin_memset
60#define memcpy __builtin_memcpy
61#endif
62
63static bool mul_size_overflow(size_t a, size_t b, size_t *out) {
64 if (a != 0 && b > SIZE_MAX / a) {
65 return true;
66 }
67 *out = a * b;
68 return false;
69}
70
71#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC
72
73#ifndef FLANTERM_FB_BUMP_ALLOC_POOL_SIZE
74#define FLANTERM_FB_BUMP_ALLOC_POOL_SIZE 873000
75#endif
76
77#ifndef FLANTERM_FB_BUMP_ALLOC_ASPECT_WIDTH
78#define FLANTERM_FB_BUMP_ALLOC_ASPECT_WIDTH 16
79#endif
80
81#ifndef FLANTERM_FB_BUMP_ALLOC_ASPECT_HEIGHT
82#define FLANTERM_FB_BUMP_ALLOC_ASPECT_HEIGHT 10
83#endif
84
85static uint8_t bump_alloc_pool[FLANTERM_FB_BUMP_ALLOC_POOL_SIZE];
86static size_t bump_alloc_ptr = 0;
87static bool bump_alloc_base_offset_added = false;
88
89static void *bump_alloc(size_t s) {
90 if (!bump_alloc_base_offset_added) {
91 if ((uintptr_t)bump_alloc_pool & 0xf) {
92 bump_alloc_ptr += 0x10 - ((uintptr_t)bump_alloc_pool & 0xf);
93 }
94 bump_alloc_base_offset_added = true;
95 }
96
97 if ((s & 0xf) != 0) {
98 s += 0x10;
99 s &= ~(size_t)0xf;
100 }
101
102 size_t next_ptr = bump_alloc_ptr + s;
103 if (next_ptr > FLANTERM_FB_BUMP_ALLOC_POOL_SIZE) {
104 return NULL;
105 }
106 void *ret = &bump_alloc_pool[bump_alloc_ptr];
107 bump_alloc_ptr = next_ptr;
108 return ret;
109}
110
111static bool bump_allocated_instance = false;
112
113/* Compute the largest terminal with aspect ratio aw:ah (W = aw*k, H = ah*k)
114 * that the bump pool can hold assuming the built-in 8x16 font with its forced
115 * 1-pixel spacing (i.e. a 9x16 cell), so that the default quick-start path
116 * always fits. A custom font with a smaller glyph may make bump_alloc return
117 * NULL at init time, matching the pre-auto-calc hardcoded-limit behaviour.
118 * aw and ah are first reduced by their GCD so that k parameterises the
119 * tightest possible integer grid. */
120static void bump_pool_limits(size_t *width_limit, size_t *height_limit) {
121 const size_t fixed_bytes =
122 sizeof(struct flanterm_fb_context) +
123 FLANTERM_FB_FONT_GLYPHS * 16 + /* font_bits */
124 FLANTERM_FB_FONT_GLYPHS * 16 * 9 * sizeof(bool) + /* font_bool */
125 7 * 16; /* bump alignment padding */
126
127 const size_t per_cell_bytes =
128 sizeof(struct flanterm_fb_char) +
129 sizeof(struct flanterm_fb_queue_item) +
130 sizeof(struct flanterm_fb_queue_item *);
131
132 const size_t glyph_w = 9;
133 const size_t glyph_h = 16;
134
135 size_t aw = FLANTERM_FB_BUMP_ALLOC_ASPECT_WIDTH;
136 size_t ah = FLANTERM_FB_BUMP_ALLOC_ASPECT_HEIGHT;
137 {
138 size_t a = aw, b = ah;
139 while (b != 0) {
140 size_t t = b;
141 b = a % b;
142 a = t;
143 }
144 if (a != 0) {
145 aw /= a;
146 ah /= a;
147 }
148 }
149
150 size_t usable = FLANTERM_FB_BUMP_ALLOC_POOL_SIZE > fixed_bytes
151 ? FLANTERM_FB_BUMP_ALLOC_POOL_SIZE - fixed_bytes
152 : 0;
153 size_t cap_cells = per_cell_bytes != 0 ? usable / per_cell_bytes : 0;
154
155 /* Find the largest k for which the integer-floor cell count
156 * (aw*k / glyph_w) * (ah*k / glyph_h) still fits in cap_cells. Grows
157 * monotonically with k; step from 0 upward, guarding against overflow
158 * so the loop always terminates. */
159 size_t k = 0;
160 if (aw != 0 && ah != 0 && cap_cells != 0) {
161 for (;;) {
162 size_t next = k + 1;
163 size_t cells_w = aw * next / glyph_w;
164 size_t cells_h = ah * next / glyph_h;
165 if (cells_h != 0 && cells_w > (size_t)-1 / cells_h) break;
166 size_t cells = cells_w * cells_h;
167 if (cells > cap_cells) break;
168 k = next;
169 }
170 }
171
172 *width_limit = aw * k;
173 *height_limit = ah * k;
174}
175
176#endif
177
178// Builtin font originally taken from:
179// https://github.com/viler-int10h/vga-text-mode-fonts/raw/master/FONTS/PC-OTHER/TOSH-SAT.F16
180static const uint8_t builtin_font[] = {
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
182 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x81, 0x81, 0xa5, 0xa5, 0x81,
183 0x81, 0xa5, 0x99, 0x81, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x3c, 0x7e, 0xff,
184 0xff, 0xdb, 0xdb, 0xff, 0xff, 0xdb, 0xe7, 0xff, 0x7e, 0x3c, 0x00, 0x00,
185 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10,
186 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe,
187 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
188 0x3c, 0x3c, 0xdb, 0xff, 0xff, 0xdb, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
189 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0xff, 0x66, 0x18, 0x18,
190 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78,
191 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
192 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
193 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0x84, 0x84, 0xcc, 0x78, 0x00,
194 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd,
195 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1e,
196 0x0e, 0x1e, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
197 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc, 0x30, 0x30,
198 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x18, 0x1c, 0x1e, 0x16, 0x12,
199 0x10, 0x10, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x30, 0x38, 0x2c,
200 0x26, 0x32, 0x3a, 0x2e, 0x26, 0x22, 0x62, 0xe2, 0xc6, 0x0e, 0x0c, 0x00,
201 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18,
202 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe,
203 0xf8, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
204 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
205 0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78,
206 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
207 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb,
208 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00,
209 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c,
210 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
211 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78,
212 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x78, 0x30, 0xfc, 0x00, 0x00,
213 0x00, 0x00, 0x30, 0x78, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
214 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
215 0x30, 0x30, 0xfc, 0x78, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
216 0x00, 0x18, 0x0c, 0xfe, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
217 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0xfe, 0x60, 0x30, 0x00,
218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
219 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
220 0x00, 0x24, 0x66, 0xff, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
221 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe,
222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c,
223 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x00, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x30, 0x30, 0x00, 0x30,
226 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c,
228 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
229 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc0, 0xc0, 0x7c, 0x06, 0x06, 0xc6, 0x7c,
230 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x0c, 0x0c, 0x18, 0x38,
231 0x30, 0x60, 0x60, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
232 0x6c, 0x38, 0x30, 0x76, 0xde, 0xcc, 0xcc, 0xde, 0x76, 0x00, 0x00, 0x00,
233 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x60, 0x60, 0x60,
235 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30,
236 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x38, 0xfe, 0x38, 0x6c, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
239 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00,
241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00,
242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06,
244 0x0c, 0x0c, 0x18, 0x38, 0x30, 0x60, 0x60, 0xc0, 0xc0, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6,
246 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18,
247 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
248 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00,
249 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0x06, 0xc6,
250 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc,
251 0xfe, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0,
252 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
253 0x00, 0x00, 0x3c, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
254 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18,
255 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
256 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
257 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c,
258 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
259 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
260 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00,
261 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c,
262 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00,
263 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x60,
264 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00, 0x00, 0x00,
265 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x00, 0x30,
266 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde,
267 0xde, 0xde, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
268 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
269 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
270 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0,
271 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc,
272 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00, 0x00, 0x00,
273 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
274 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0,
275 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
276 0xc0, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
277 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6,
278 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30,
279 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c,
280 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
281 0x00, 0x00, 0xc6, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6,
282 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
283 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6,
284 0xee, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
285 0x00, 0x00, 0xc6, 0xc6, 0xe6, 0xe6, 0xf6, 0xde, 0xce, 0xce, 0xc6, 0xc6,
286 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
287 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc6,
288 0xc6, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
289 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xf6, 0xda,
290 0x6c, 0x06, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc,
291 0xd8, 0xcc, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
292 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
293 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
294 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
295 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6,
296 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00,
297 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c,
298 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x38,
299 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc,
300 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
301 0x00, 0x00, 0xfe, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc0, 0xc0,
302 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60,
303 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0,
304 0x60, 0x60, 0x30, 0x38, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x00, 0x00, 0x00,
305 0x00, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
306 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00,
307 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
309 0x00, 0x00, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
310 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x06, 0x06,
311 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0,
312 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xe6, 0xdc, 0x00, 0x00, 0x00,
313 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6,
314 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x76, 0xce, 0xc6,
315 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
316 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
317 0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x30,
318 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6,
319 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0xc0, 0xc0,
320 0xc0, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
321 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
322 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1e, 0x06, 0x06,
323 0x06, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0xc0, 0xc0,
324 0xc0, 0xc6, 0xcc, 0xd8, 0xf0, 0xf0, 0xd8, 0xcc, 0xc6, 0x00, 0x00, 0x00,
325 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
326 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6,
327 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
329 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
330 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc6,
331 0xc6, 0xc6, 0xe6, 0xdc, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
332 0x00, 0x76, 0xce, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x06, 0x06, 0x00,
333 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
334 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0,
335 0x70, 0x1c, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30,
336 0x30, 0xfe, 0x30, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00,
337 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
338 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6,
339 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00,
341 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6,
342 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6,
343 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
344 0x00, 0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00, 0x00, 0x00,
345 0x00, 0x00, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x30, 0x30,
346 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00,
347 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x30,
348 0x30, 0x30, 0x30, 0x1c, 0x30, 0x30, 0x30, 0x30, 0xe0, 0x00, 0x00, 0x00,
349 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
350 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x6c,
351 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66,
352 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x18, 0xcc, 0x78, 0x00,
353 0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
354 0x7c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6,
355 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c,
356 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
357 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6,
358 0x7e, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0x06, 0x06,
359 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38,
360 0x00, 0x7c, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00,
361 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc6,
362 0x7c, 0x18, 0x0c, 0x38, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6,
363 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c,
364 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0, 0x7e, 0x00, 0x00, 0x00,
365 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xfe, 0xc0, 0xc0, 0xc0,
366 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x38, 0x18, 0x18,
367 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c,
368 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
369 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
370 0x3c, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6,
371 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00,
372 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
373 0x18, 0x30, 0x60, 0x00, 0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0,
374 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x36, 0x36,
375 0x76, 0xde, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x3c,
376 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00,
377 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
378 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6,
379 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18,
380 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
381 0x00, 0x10, 0x38, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
382 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xc6, 0xc6, 0xc6,
383 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c,
384 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00,
385 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
386 0x7c, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
387 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
388 0x30, 0x78, 0xcc, 0xc0, 0xc0, 0xcc, 0x78, 0x30, 0x30, 0x00, 0x00, 0x00,
389 0x00, 0x00, 0x38, 0x6c, 0x60, 0x60, 0x60, 0xf8, 0x60, 0x60, 0x60, 0xe6,
390 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0xfc,
391 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc,
392 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00,
393 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18,
394 0x18, 0xd8, 0x70, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0x06, 0x06,
395 0x7e, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30,
396 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
397 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
398 0x7c, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0xc6, 0xc6, 0xc6,
399 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00,
400 0x00, 0xdc, 0xe6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
401 0x76, 0xdc, 0x00, 0xc6, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6,
402 0xc6, 0x00, 0x00, 0x00, 0x00, 0x78, 0xd8, 0xd8, 0x6c, 0x00, 0xfc, 0x00,
403 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c,
404 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
405 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xc6,
406 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
407 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
408 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00,
409 0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c,
410 0x18, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc2, 0xc6, 0xcc, 0xd8, 0x30,
411 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30,
412 0x00, 0x30, 0x30, 0x30, 0x78, 0x78, 0x78, 0x78, 0x30, 0x00, 0x00, 0x00,
413 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00,
414 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36,
415 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x88, 0x22, 0x88,
416 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
417 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
418 0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
419 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18,
420 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
421 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18,
422 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18,
423 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
424 0x36, 0x36, 0x36, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
425 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x36, 0x36, 0x36,
426 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18,
427 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
428 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36,
429 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
430 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x06,
431 0x06, 0xf6, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
432 0x36, 0xf6, 0xf6, 0x06, 0x06, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
433 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0xfe, 0x00, 0x00, 0x00,
434 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18,
435 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
436 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
437 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00,
438 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff,
439 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
440 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
441 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18,
442 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
443 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
444 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
445 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18,
446 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37,
447 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
448 0x36, 0x37, 0x37, 0x30, 0x30, 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
449 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x30, 0x30, 0x37, 0x37, 0x36,
450 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0xf7, 0x00,
451 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
452 0x00, 0xff, 0xff, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36,
453 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x30, 0x30, 0x37, 0x37, 0x36,
454 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
455 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36,
456 0x36, 0xf7, 0xf7, 0x00, 0x00, 0xf7, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36,
457 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00,
458 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff,
459 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
460 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18,
461 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x36, 0x36, 0x36,
462 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f,
463 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
464 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
465 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x1f, 0x1f, 0x18,
466 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
467 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
468 0x36, 0x36, 0x36, 0xff, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
469 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0xff, 0xff, 0x18,
470 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8,
471 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
473 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
474 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
475 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0,
476 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
477 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
478 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
479 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
480 0x00, 0x76, 0xd6, 0xdc, 0xc8, 0xc8, 0xdc, 0xd6, 0x76, 0x00, 0x00, 0x00,
481 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
482 0xd8, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0,
483 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x7e, 0xfe, 0x24, 0x24, 0x24, 0x24, 0x66, 0xc6, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0xfe, 0xfe, 0xc2, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc2, 0xfe,
486 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xc8, 0xcc,
487 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x76, 0x6c, 0x60, 0xc0, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xfc, 0x98, 0x18, 0x18, 0x18, 0x18,
490 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x30, 0x30, 0x78, 0xcc, 0xcc,
491 0xcc, 0x78, 0x30, 0x30, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
492 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c,
494 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0x60, 0x30, 0x78, 0xcc,
495 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x76, 0xbb, 0x99, 0x99, 0xdd, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x00, 0x02, 0x06, 0x3c, 0x6c, 0xce, 0xd6, 0xd6, 0xe6, 0x6c, 0x78,
498 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x60, 0xc0, 0xc0, 0xfe,
499 0xc0, 0xc0, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
500 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
501 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00,
502 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xfc,
503 0x30, 0x30, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
504 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, 0x00, 0x00,
505 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
506 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x36, 0x30, 0x30, 0x30,
507 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18, 0x18,
508 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00,
509 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00,
510 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00,
511 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xcc, 0xcc,
512 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
513 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00,
514 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
515 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c,
516 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c, 0x00, 0x00,
517 0x00, 0xd8, 0xec, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x00, 0x00, 0x00,
518 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x0c, 0x18, 0x30, 0x60, 0x7c,
519 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
520 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00,
521 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
522 0x00, 0x00, 0x00, 0x00
523};
524
525static ALWAYS_INLINE uint32_t convert_colour(struct flanterm_context *_ctx, uint32_t colour) {
526 struct flanterm_fb_context *ctx = (void *)_ctx;
527 uint32_t r = (colour >> 16) & 0xff;
528 uint32_t g = (colour >> 8) & 0xff;
529 uint32_t b = colour & 0xff;
530 uint32_t ret = (r << ctx->red_mask_shift) | (g << ctx->green_mask_shift) | (b << ctx->blue_mask_shift);
531 if (ctx->red_mask_size > 8) {
532 ret |= (r >> (16 - ctx->red_mask_size)) << (ctx->red_mask_shift - ctx->red_mask_size + 8);
533 }
534 if (ctx->green_mask_size > 8) {
535 ret |= (g >> (16 - ctx->green_mask_size)) << (ctx->green_mask_shift - ctx->green_mask_size + 8);
536 }
537 if (ctx->blue_mask_size > 8) {
538 ret |= (b >> (16 - ctx->blue_mask_size)) << (ctx->blue_mask_shift - ctx->blue_mask_size + 8);
539 }
540 return ret;
541}
542
543static void flanterm_fb_save_state(struct flanterm_context *_ctx) {
544 struct flanterm_fb_context *ctx = (void *)_ctx;
545 ctx->saved_state_text_fg = ctx->text_fg;
546 ctx->saved_state_text_bg = ctx->text_bg;
547 ctx->saved_state_text_fg_default = ctx->text_fg_default;
548 ctx->saved_state_text_bg_default = ctx->text_bg_default;
549 ctx->saved_state_cursor_x = ctx->cursor_x;
550 ctx->saved_state_cursor_y = ctx->cursor_y;
551}
552
553static void flanterm_fb_restore_state(struct flanterm_context *_ctx) {
554 struct flanterm_fb_context *ctx = (void *)_ctx;
555 ctx->text_fg = ctx->saved_state_text_fg;
556 ctx->text_bg = ctx->saved_state_text_bg;
557 ctx->text_fg_default = ctx->saved_state_text_fg_default;
558 ctx->text_bg_default = ctx->saved_state_text_bg_default;
559 ctx->cursor_x = ctx->saved_state_cursor_x;
560 ctx->cursor_y = ctx->saved_state_cursor_y;
561}
562
563static void flanterm_fb_swap_palette(struct flanterm_context *_ctx) {
564 struct flanterm_fb_context *ctx = (void *)_ctx;
565 uint32_t tmp = ctx->text_bg;
566 ctx->text_bg = ctx->text_fg;
567 ctx->text_fg = tmp;
568 bool tmp_default = ctx->text_bg_default;
569 ctx->text_bg_default = ctx->text_fg_default;
570 ctx->text_fg_default = tmp_default;
571}
572
573static void plot_char_scaled_canvas(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) {
574 struct flanterm_fb_context *ctx = (void *)_ctx;
575
576 if (x >= _ctx->cols || y >= _ctx->rows) {
577 return;
578 }
579
580 x = ctx->offset_x + x * ctx->glyph_width;
581 y = ctx->offset_y + y * ctx->glyph_height;
582
583 bool *glyph = &ctx->font_bool[c->c * ctx->font_height * ctx->font_width];
584
585 volatile uint32_t *dest;
586 int outer_stride, inner_stride;
587
588 switch (ctx->rotation) {
589 default:
590 case FLANTERM_FB_ROTATE_0:
591 dest = ctx->framebuffer + x + y * (ctx->pitch / 4);
592 outer_stride = ctx->pitch / 4;
593 inner_stride = 1;
594 break;
595 case FLANTERM_FB_ROTATE_90:
596 dest = ctx->framebuffer + (ctx->height - 1 - y) + x * (ctx->pitch / 4);
597 outer_stride = -1;
598 inner_stride = ctx->pitch / 4;
599 break;
600 case FLANTERM_FB_ROTATE_180:
601 dest = ctx->framebuffer + (ctx->width - 1 - x) + (ctx->height - 1 - y) * (ctx->pitch / 4);
602 outer_stride = -(ctx->pitch / 4);
603 inner_stride = -1;
604 break;
605 case FLANTERM_FB_ROTATE_270:
606 dest = ctx->framebuffer + y + (ctx->width - 1 - x) * (ctx->pitch / 4);
607 outer_stride = 1;
608 inner_stride = -(ctx->pitch / 4);
609 break;
610 }
611
612 // naming: fx,fy for font coordinates, gx,gy for glyph coordinates
613 for (size_t gy = 0; gy < ctx->glyph_height; gy++) {
614 uint8_t fy = gy / ctx->font_scale_y;
615 volatile uint32_t *fb_line = dest;
616 uint32_t *canvas_line = ctx->canvas + x + (y + gy) * ctx->width;
617 bool *glyph_pointer = glyph + (fy * ctx->font_width);
618 for (size_t fx = 0; fx < ctx->font_width; fx++) {
619 for (size_t i = 0; i < ctx->font_scale_x; i++) {
620 size_t gx = ctx->font_scale_x * fx + i;
621 uint32_t bg = c->bg_default ? canvas_line[gx] : c->bg;
622 uint32_t fg = c->fg_default ? canvas_line[gx] : c->fg;
623 *fb_line = *glyph_pointer ? fg : bg;
624 fb_line += inner_stride;
625 }
626 glyph_pointer++;
627 }
628 dest += outer_stride;
629 }
630}
631
632static void plot_char_scaled_uncanvas(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) {
633 struct flanterm_fb_context *ctx = (void *)_ctx;
634
635 if (x >= _ctx->cols || y >= _ctx->rows) {
636 return;
637 }
638
639 uint32_t default_bg = ctx->default_bg;
640
641 uint32_t bg = c->bg_default ? default_bg : c->bg;
642 uint32_t fg = c->fg_default ? default_bg : c->fg;
643
644 x = ctx->offset_x + x * ctx->glyph_width;
645 y = ctx->offset_y + y * ctx->glyph_height;
646
647 bool *glyph = &ctx->font_bool[c->c * ctx->font_height * ctx->font_width];
648
649 volatile uint32_t *dest;
650 int outer_stride, inner_stride;
651
652 switch (ctx->rotation) {
653 default:
654 case FLANTERM_FB_ROTATE_0:
655 dest = ctx->framebuffer + x + y * (ctx->pitch / 4);
656 outer_stride = ctx->pitch / 4;
657 inner_stride = 1;
658 break;
659 case FLANTERM_FB_ROTATE_90:
660 dest = ctx->framebuffer + (ctx->height - 1 - y) + x * (ctx->pitch / 4);
661 outer_stride = -1;
662 inner_stride = ctx->pitch / 4;
663 break;
664 case FLANTERM_FB_ROTATE_180:
665 dest = ctx->framebuffer + (ctx->width - 1 - x) + (ctx->height - 1 - y) * (ctx->pitch / 4);
666 outer_stride = -(ctx->pitch / 4);
667 inner_stride = -1;
668 break;
669 case FLANTERM_FB_ROTATE_270:
670 dest = ctx->framebuffer + y + (ctx->width - 1 - x) * (ctx->pitch / 4);
671 outer_stride = 1;
672 inner_stride = -(ctx->pitch / 4);
673 break;
674 }
675
676 // naming: fx,fy for font coordinates, gx,gy for glyph coordinates
677 for (size_t gy = 0; gy < ctx->glyph_height; gy++) {
678 uint8_t fy = gy / ctx->font_scale_y;
679 volatile uint32_t *fb_line = dest;
680 bool *glyph_pointer = glyph + (fy * ctx->font_width);
681 for (size_t fx = 0; fx < ctx->font_width; fx++) {
682 for (size_t i = 0; i < ctx->font_scale_x; i++) {
683 *fb_line = *glyph_pointer ? fg : bg;
684 fb_line += inner_stride;
685 }
686 glyph_pointer++;
687 }
688 dest += outer_stride;
689 }
690}
691
692static void plot_char_unscaled_canvas(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) {
693 struct flanterm_fb_context *ctx = (void *)_ctx;
694
695 if (x >= _ctx->cols || y >= _ctx->rows) {
696 return;
697 }
698
699 x = ctx->offset_x + x * ctx->glyph_width;
700 y = ctx->offset_y + y * ctx->glyph_height;
701
702 bool *glyph = &ctx->font_bool[c->c * ctx->font_height * ctx->font_width];
703
704 volatile uint32_t *dest;
705 int outer_stride, inner_stride;
706
707 switch (ctx->rotation) {
708 default:
709 case FLANTERM_FB_ROTATE_0:
710 dest = ctx->framebuffer + x + y * (ctx->pitch / 4);
711 outer_stride = ctx->pitch / 4;
712 inner_stride = 1;
713 break;
714 case FLANTERM_FB_ROTATE_90:
715 dest = ctx->framebuffer + (ctx->height - 1 - y) + x * (ctx->pitch / 4);
716 outer_stride = -1;
717 inner_stride = ctx->pitch / 4;
718 break;
719 case FLANTERM_FB_ROTATE_180:
720 dest = ctx->framebuffer + (ctx->width - 1 - x) + (ctx->height - 1 - y) * (ctx->pitch / 4);
721 outer_stride = -(ctx->pitch / 4);
722 inner_stride = -1;
723 break;
724 case FLANTERM_FB_ROTATE_270:
725 dest = ctx->framebuffer + y + (ctx->width - 1 - x) * (ctx->pitch / 4);
726 outer_stride = 1;
727 inner_stride = -(ctx->pitch / 4);
728 break;
729 }
730
731 // naming: fx,fy for font coordinates, gx,gy for glyph coordinates
732 for (size_t gy = 0; gy < ctx->glyph_height; gy++) {
733 volatile uint32_t *fb_line = dest;
734 uint32_t *canvas_line = ctx->canvas + x + (y + gy) * ctx->width;
735 bool *glyph_pointer = glyph + (gy * ctx->font_width);
736 for (size_t fx = 0; fx < ctx->font_width; fx++) {
737 uint32_t bg = c->bg_default ? canvas_line[fx] : c->bg;
738 uint32_t fg = c->fg_default ? canvas_line[fx] : c->fg;
739 *fb_line = *(glyph_pointer++) ? fg : bg;
740 fb_line += inner_stride;
741 }
742 dest += outer_stride;
743 }
744}
745
746static void plot_char_unscaled_uncanvas(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) {
747 struct flanterm_fb_context *ctx = (void *)_ctx;
748
749 if (x >= _ctx->cols || y >= _ctx->rows) {
750 return;
751 }
752
753 uint32_t default_bg = ctx->default_bg;
754
755 uint32_t bg = c->bg_default ? default_bg : c->bg;
756 uint32_t fg = c->fg_default ? default_bg : c->fg;
757
758 x = ctx->offset_x + x * ctx->glyph_width;
759 y = ctx->offset_y + y * ctx->glyph_height;
760
761 bool *glyph = &ctx->font_bool[c->c * ctx->font_height * ctx->font_width];
762
763 volatile uint32_t *dest;
764 int outer_stride, inner_stride;
765
766 switch (ctx->rotation) {
767 default:
768 case FLANTERM_FB_ROTATE_0:
769 dest = ctx->framebuffer + x + y * (ctx->pitch / 4);
770 outer_stride = ctx->pitch / 4;
771 inner_stride = 1;
772 break;
773 case FLANTERM_FB_ROTATE_90:
774 dest = ctx->framebuffer + (ctx->height - 1 - y) + x * (ctx->pitch / 4);
775 outer_stride = -1;
776 inner_stride = ctx->pitch / 4;
777 break;
778 case FLANTERM_FB_ROTATE_180:
779 dest = ctx->framebuffer + (ctx->width - 1 - x) + (ctx->height - 1 - y) * (ctx->pitch / 4);
780 outer_stride = -(ctx->pitch / 4);
781 inner_stride = -1;
782 break;
783 case FLANTERM_FB_ROTATE_270:
784 dest = ctx->framebuffer + y + (ctx->width - 1 - x) * (ctx->pitch / 4);
785 outer_stride = 1;
786 inner_stride = -(ctx->pitch / 4);
787 break;
788 }
789
790 // naming: fx,fy for font coordinates, gx,gy for glyph coordinates
791 for (size_t gy = 0; gy < ctx->glyph_height; gy++) {
792 volatile uint32_t *fb_line = dest;
793 bool *glyph_pointer = glyph + (gy * ctx->font_width);
794 for (size_t fx = 0; fx < ctx->font_width; fx++) {
795 *fb_line = *(glyph_pointer++) ? fg : bg;
796 fb_line += inner_stride;
797 }
798 dest += outer_stride;
799 }
800}
801
802static inline bool compare_char(struct flanterm_fb_char *a, struct flanterm_fb_char *b) {
803 if (a->c != b->c) {
804 return false;
805 }
806 if (a->fg_default != b->fg_default || a->bg_default != b->bg_default) {
807 return false;
808 }
809 if (!a->fg_default && a->fg != b->fg) {
810 return false;
811 }
812 if (!a->bg_default && a->bg != b->bg) {
813 return false;
814 }
815 return true;
816}
817
818static void push_to_queue(struct flanterm_context *_ctx, struct flanterm_fb_char *c, size_t x, size_t y) {
819 struct flanterm_fb_context *ctx = (void *)_ctx;
820
821 if (x >= _ctx->cols || y >= _ctx->rows) {
822 return;
823 }
824
825 size_t i = y * _ctx->cols + x;
826
827 struct flanterm_fb_queue_item *q = ctx->map[i];
828
829 if (q == NULL) {
830 if (compare_char(a: &ctx->grid[i], b: c)) {
831 return;
832 }
833 if (ctx->queue_i == _ctx->rows * _ctx->cols) {
834 return;
835 }
836 q = &ctx->queue[ctx->queue_i++];
837 q->x = x;
838 q->y = y;
839 ctx->map[i] = q;
840 }
841
842 q->c = *c;
843}
844
845static void flanterm_fb_revscroll(struct flanterm_context *_ctx) {
846 struct flanterm_fb_context *ctx = (void *)_ctx;
847
848 size_t start = _ctx->scroll_top_margin * _ctx->cols;
849 size_t end = (_ctx->scroll_bottom_margin - 1) * _ctx->cols;
850 for (size_t i = end; i > start; ) {
851 i--;
852 struct flanterm_fb_char *c;
853 struct flanterm_fb_queue_item *q = ctx->map[i];
854 if (q != NULL) {
855 c = &q->c;
856 } else {
857 c = &ctx->grid[i];
858 }
859 push_to_queue(_ctx, c, x: (i + _ctx->cols) % _ctx->cols, y: (i + _ctx->cols) / _ctx->cols);
860 }
861
862 // Clear the first line of the screen.
863 struct flanterm_fb_char empty;
864 empty.c = ' ';
865 empty.fg = ctx->text_fg;
866 empty.bg = ctx->text_bg;
867 empty.fg_default = ctx->text_fg_default;
868 empty.bg_default = ctx->text_bg_default;
869 for (size_t i = 0; i < _ctx->cols; i++) {
870 push_to_queue(_ctx, c: &empty, x: i, y: _ctx->scroll_top_margin);
871 }
872}
873
874static void flanterm_fb_scroll(struct flanterm_context *_ctx) {
875 struct flanterm_fb_context *ctx = (void *)_ctx;
876
877 for (size_t i = (_ctx->scroll_top_margin + 1) * _ctx->cols;
878 i < _ctx->scroll_bottom_margin * _ctx->cols; i++) {
879 struct flanterm_fb_char *c;
880 struct flanterm_fb_queue_item *q = ctx->map[i];
881 if (q != NULL) {
882 c = &q->c;
883 } else {
884 c = &ctx->grid[i];
885 }
886 push_to_queue(_ctx, c, x: (i - _ctx->cols) % _ctx->cols, y: (i - _ctx->cols) / _ctx->cols);
887 }
888
889 // Clear the last line of the screen.
890 struct flanterm_fb_char empty;
891 empty.c = ' ';
892 empty.fg = ctx->text_fg;
893 empty.bg = ctx->text_bg;
894 empty.fg_default = ctx->text_fg_default;
895 empty.bg_default = ctx->text_bg_default;
896 for (size_t i = 0; i < _ctx->cols; i++) {
897 push_to_queue(_ctx, c: &empty, x: i, y: _ctx->scroll_bottom_margin - 1);
898 }
899}
900
901static void flanterm_fb_clear(struct flanterm_context *_ctx, bool move) {
902 struct flanterm_fb_context *ctx = (void *)_ctx;
903
904 struct flanterm_fb_char empty;
905 empty.c = ' ';
906 empty.fg = ctx->text_fg;
907 empty.bg = ctx->text_bg;
908 empty.fg_default = ctx->text_fg_default;
909 empty.bg_default = ctx->text_bg_default;
910 for (size_t i = 0; i < _ctx->rows * _ctx->cols; i++) {
911 push_to_queue(_ctx, c: &empty, x: i % _ctx->cols, y: i / _ctx->cols);
912 }
913
914 if (move) {
915 ctx->cursor_x = 0;
916 ctx->cursor_y = 0;
917 }
918}
919
920static void flanterm_fb_set_cursor_pos(struct flanterm_context *_ctx, size_t x, size_t y) {
921 struct flanterm_fb_context *ctx = (void *)_ctx;
922
923 if (x >= _ctx->cols) {
924 if (x > SIZE_MAX / 2) {
925 x = 0;
926 } else {
927 x = _ctx->cols - 1;
928 }
929 }
930 if (y >= _ctx->rows) {
931 if (y > SIZE_MAX / 2) {
932 y = 0;
933 } else {
934 y = _ctx->rows - 1;
935 }
936 }
937 ctx->cursor_x = x;
938 ctx->cursor_y = y;
939}
940
941static void flanterm_fb_get_cursor_pos(struct flanterm_context *_ctx, size_t *x, size_t *y) {
942 struct flanterm_fb_context *ctx = (void *)_ctx;
943
944 *x = ctx->cursor_x >= _ctx->cols ? _ctx->cols - 1 : ctx->cursor_x;
945 *y = ctx->cursor_y >= _ctx->rows ? _ctx->rows - 1 : ctx->cursor_y;
946}
947
948static void flanterm_fb_move_character(struct flanterm_context *_ctx, size_t new_x, size_t new_y, size_t old_x, size_t old_y) {
949 struct flanterm_fb_context *ctx = (void *)_ctx;
950
951 if (old_x >= _ctx->cols || old_y >= _ctx->rows
952 || new_x >= _ctx->cols || new_y >= _ctx->rows) {
953 return;
954 }
955
956 size_t i = old_x + old_y * _ctx->cols;
957
958 struct flanterm_fb_char *c;
959 struct flanterm_fb_queue_item *q = ctx->map[i];
960 if (q != NULL) {
961 c = &q->c;
962 } else {
963 c = &ctx->grid[i];
964 }
965
966 push_to_queue(_ctx, c, x: new_x, y: new_y);
967}
968
969static void flanterm_fb_set_text_fg(struct flanterm_context *_ctx, size_t fg) {
970 struct flanterm_fb_context *ctx = (void *)_ctx;
971
972 ctx->text_fg = ctx->ansi_colours[fg];
973 ctx->text_fg_default = false;
974}
975
976static void flanterm_fb_set_text_bg(struct flanterm_context *_ctx, size_t bg) {
977 struct flanterm_fb_context *ctx = (void *)_ctx;
978
979 ctx->text_bg = ctx->ansi_colours[bg];
980 ctx->text_bg_default = false;
981}
982
983static void flanterm_fb_set_text_fg_bright(struct flanterm_context *_ctx, size_t fg) {
984 struct flanterm_fb_context *ctx = (void *)_ctx;
985
986 ctx->text_fg = ctx->ansi_bright_colours[fg];
987 ctx->text_fg_default = false;
988}
989
990static void flanterm_fb_set_text_bg_bright(struct flanterm_context *_ctx, size_t bg) {
991 struct flanterm_fb_context *ctx = (void *)_ctx;
992
993 ctx->text_bg = ctx->ansi_bright_colours[bg];
994 ctx->text_bg_default = false;
995}
996
997static void flanterm_fb_set_text_fg_rgb(struct flanterm_context *_ctx, uint32_t fg) {
998 struct flanterm_fb_context *ctx = (void *)_ctx;
999
1000 ctx->text_fg = convert_colour(_ctx, colour: fg);
1001 ctx->text_fg_default = false;
1002}
1003
1004static void flanterm_fb_set_text_bg_rgb(struct flanterm_context *_ctx, uint32_t bg) {
1005 struct flanterm_fb_context *ctx = (void *)_ctx;
1006
1007 ctx->text_bg = convert_colour(_ctx, colour: bg);
1008 ctx->text_bg_default = false;
1009}
1010
1011static void flanterm_fb_set_text_fg_default(struct flanterm_context *_ctx) {
1012 struct flanterm_fb_context *ctx = (void *)_ctx;
1013
1014 ctx->text_fg = ctx->default_fg;
1015 ctx->text_fg_default = false;
1016}
1017
1018static void flanterm_fb_set_text_bg_default(struct flanterm_context *_ctx) {
1019 struct flanterm_fb_context *ctx = (void *)_ctx;
1020
1021 ctx->text_bg_default = true;
1022}
1023
1024static void flanterm_fb_set_text_fg_default_bright(struct flanterm_context *_ctx) {
1025 struct flanterm_fb_context *ctx = (void *)_ctx;
1026
1027 ctx->text_fg = ctx->default_fg_bright;
1028 ctx->text_fg_default = false;
1029}
1030
1031static void flanterm_fb_set_text_bg_default_bright(struct flanterm_context *_ctx) {
1032 struct flanterm_fb_context *ctx = (void *)_ctx;
1033
1034 ctx->text_bg = ctx->default_bg_bright;
1035 ctx->text_bg_default = false;
1036}
1037
1038static void draw_cursor(struct flanterm_context *_ctx) {
1039 struct flanterm_fb_context *ctx = (void *)_ctx;
1040
1041 if (ctx->cursor_x >= _ctx->cols || ctx->cursor_y >= _ctx->rows) {
1042 return;
1043 }
1044
1045 size_t i = ctx->cursor_x + ctx->cursor_y * _ctx->cols;
1046
1047 struct flanterm_fb_char c;
1048 struct flanterm_fb_queue_item *q = ctx->map[i];
1049 if (q != NULL) {
1050 c = q->c;
1051 } else {
1052 c = ctx->grid[i];
1053 }
1054 uint32_t tmp = c.fg;
1055 c.fg = c.bg;
1056 c.bg = tmp;
1057 bool tmp_default = c.fg_default;
1058 c.fg_default = c.bg_default;
1059 c.bg_default = tmp_default;
1060 ctx->plot_char(_ctx, &c, ctx->cursor_x, ctx->cursor_y);
1061 if (q != NULL) {
1062 ctx->grid[i] = q->c;
1063 ctx->map[i] = NULL;
1064 }
1065}
1066
1067static void flanterm_fb_full_refresh(struct flanterm_context *_ctx);
1068
1069static void flanterm_fb_double_buffer_flush(struct flanterm_context *_ctx) {
1070 struct flanterm_fb_context *ctx = (void *)_ctx;
1071
1072 if (ctx->full_refresh_pending) {
1073 flanterm_fb_full_refresh(_ctx);
1074 }
1075
1076 if (_ctx->cursor_enabled) {
1077 draw_cursor(_ctx);
1078 }
1079
1080 for (size_t i = 0; i < ctx->queue_i; i++) {
1081 struct flanterm_fb_queue_item *q = &ctx->queue[i];
1082 size_t offset = q->y * _ctx->cols + q->x;
1083 if (ctx->map[offset] == NULL) {
1084 continue;
1085 }
1086 ctx->plot_char(_ctx, &q->c, q->x, q->y);
1087 ctx->grid[offset] = q->c;
1088 ctx->map[offset] = NULL;
1089 }
1090
1091 if ((ctx->old_cursor_x != ctx->cursor_x || ctx->old_cursor_y != ctx->cursor_y) || _ctx->cursor_enabled == false) {
1092 if (ctx->old_cursor_x < _ctx->cols && ctx->old_cursor_y < _ctx->rows) {
1093 ctx->plot_char(_ctx, &ctx->grid[ctx->old_cursor_x + ctx->old_cursor_y * _ctx->cols], ctx->old_cursor_x, ctx->old_cursor_y);
1094 }
1095 }
1096
1097 ctx->old_cursor_x = ctx->cursor_x;
1098 ctx->old_cursor_y = ctx->cursor_y;
1099
1100 ctx->queue_i = 0;
1101
1102 if (ctx->flush_callback) {
1103 ctx->flush_callback(ctx->framebuffer, ctx->pitch * ctx->phys_height);
1104 }
1105}
1106
1107static void flanterm_fb_raw_putchar(struct flanterm_context *_ctx, uint8_t c) {
1108 struct flanterm_fb_context *ctx = (void *)_ctx;
1109
1110 if (ctx->cursor_x >= _ctx->cols) {
1111 if (_ctx->wrap_enabled && (ctx->cursor_y < _ctx->scroll_bottom_margin - 1 || _ctx->scroll_enabled)) {
1112 ctx->cursor_x = 0;
1113 ctx->cursor_y++;
1114 if (ctx->cursor_y == _ctx->scroll_bottom_margin) {
1115 ctx->cursor_y--;
1116 flanterm_fb_scroll(_ctx);
1117 }
1118 if (ctx->cursor_y >= _ctx->rows) {
1119 ctx->cursor_y = _ctx->rows - 1;
1120 }
1121 } else {
1122 ctx->cursor_x = _ctx->cols - 1;
1123 }
1124 }
1125
1126 struct flanterm_fb_char ch;
1127 ch.c = c;
1128 ch.fg = ctx->text_fg;
1129 ch.bg = ctx->text_bg;
1130 ch.fg_default = ctx->text_fg_default;
1131 ch.bg_default = ctx->text_bg_default;
1132 push_to_queue(_ctx, c: &ch, x: ctx->cursor_x++, y: ctx->cursor_y);
1133}
1134
1135static void flanterm_fb_full_refresh(struct flanterm_context *_ctx) {
1136 struct flanterm_fb_context *ctx = (void *)_ctx;
1137
1138 ctx->full_refresh_pending = false;
1139
1140 uint32_t default_bg = ctx->default_bg;
1141
1142 for (size_t y = 0; y < ctx->height; y++) {
1143 for (size_t x = 0; x < ctx->width; x++) {
1144 size_t px, py;
1145 switch (ctx->rotation) {
1146 default:
1147 case FLANTERM_FB_ROTATE_0:
1148 px = x; py = y;
1149 break;
1150 case FLANTERM_FB_ROTATE_90:
1151 px = ctx->height - 1 - y; py = x;
1152 break;
1153 case FLANTERM_FB_ROTATE_180:
1154 px = ctx->width - 1 - x; py = ctx->height - 1 - y;
1155 break;
1156 case FLANTERM_FB_ROTATE_270:
1157 px = y; py = ctx->width - 1 - x;
1158 break;
1159 }
1160
1161 if (ctx->canvas != NULL) {
1162 ctx->framebuffer[py * (ctx->pitch / sizeof(uint32_t)) + px] = ctx->canvas[y * ctx->width + x];
1163 } else {
1164 ctx->framebuffer[py * (ctx->pitch / sizeof(uint32_t)) + px] = default_bg;
1165 }
1166 }
1167 }
1168
1169 for (size_t i = 0; i < (size_t)_ctx->rows * _ctx->cols; i++) {
1170 size_t x = i % _ctx->cols;
1171 size_t y = i / _ctx->cols;
1172
1173 ctx->plot_char(_ctx, &ctx->grid[i], x, y);
1174 }
1175
1176 if (_ctx->cursor_enabled) {
1177 draw_cursor(_ctx);
1178 }
1179
1180 if (ctx->flush_callback) {
1181 ctx->flush_callback(ctx->framebuffer, ctx->pitch * ctx->phys_height);
1182 }
1183}
1184
1185static void flanterm_fb_deinit(struct flanterm_context *_ctx, void (*_free)(void *, size_t)) {
1186 struct flanterm_fb_context *ctx = (void *)_ctx;
1187
1188 if (_free == NULL) {
1189#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC
1190 if (bump_allocated_instance == true) {
1191 bump_alloc_ptr = 0;
1192 bump_alloc_base_offset_added = false;
1193 bump_allocated_instance = false;
1194 }
1195#endif
1196 return;
1197 }
1198
1199 _free(ctx->font_bits, ctx->font_bits_size);
1200 _free(ctx->font_bool, ctx->font_bool_size);
1201 _free(ctx->grid, ctx->grid_size);
1202 _free(ctx->queue, ctx->queue_size);
1203 _free(ctx->map, ctx->map_size);
1204
1205 if (ctx->canvas != NULL) {
1206 _free(ctx->canvas, ctx->canvas_size);
1207 }
1208
1209 _free(ctx, sizeof(struct flanterm_fb_context));
1210}
1211
1212struct flanterm_context *flanterm_fb_init(
1213 void *(*_malloc)(size_t),
1214 void (*_free)(void *, size_t),
1215 uint32_t *framebuffer, size_t width, size_t height, size_t pitch,
1216 uint8_t red_mask_size, uint8_t red_mask_shift,
1217 uint8_t green_mask_size, uint8_t green_mask_shift,
1218 uint8_t blue_mask_size, uint8_t blue_mask_shift,
1219 uint32_t *canvas,
1220 uint32_t *ansi_colours, uint32_t *ansi_bright_colours,
1221 uint32_t *default_bg, uint32_t *default_fg,
1222 uint32_t *default_bg_bright, uint32_t *default_fg_bright,
1223 void *font, size_t font_width, size_t font_height, size_t font_spacing,
1224 size_t font_scale_x, size_t font_scale_y,
1225 size_t margin,
1226 int rotation,
1227 bool autoflush
1228) {
1229 size_t phys_height = height;
1230
1231 // The framebuffer is written a 32-bit word at a time, so the stride must be
1232 // a whole number of pixels and cover at least one scanline worth of data.
1233 {
1234 size_t min_pitch;
1235 if (pitch % sizeof(uint32_t) != 0) {
1236 return NULL;
1237 }
1238 if (mul_size_overflow(a: width, b: sizeof(uint32_t), out: &min_pitch) || pitch < min_pitch) {
1239 return NULL;
1240 }
1241 }
1242
1243 if (rotation == FLANTERM_FB_ROTATE_90 || rotation == FLANTERM_FB_ROTATE_270) {
1244 size_t tmp = width;
1245 width = height;
1246 height = tmp;
1247 }
1248
1249 if (font_scale_x == 0 || font_scale_y == 0) {
1250 font_scale_x = 1;
1251 font_scale_y = 1;
1252 if (width >= (1920 + 1920 / 3) && height >= (1080 + 1080 / 3)) {
1253 font_scale_x = 2;
1254 font_scale_y = 2;
1255 }
1256 if (width >= (3840 + 3840 / 3) && height >= (2160 + 2160 / 3)) {
1257 font_scale_x = 4;
1258 font_scale_y = 4;
1259 }
1260 }
1261
1262 if (red_mask_size < 8 || red_mask_size != green_mask_size || red_mask_size != blue_mask_size) {
1263 return NULL;
1264 }
1265
1266 // A channel whose field extends past the 32-bit pixel word would make
1267 // convert_colour invoke undefined behaviour (shift-count too large).
1268 if (red_mask_shift + red_mask_size > 32
1269 || green_mask_shift + green_mask_size > 32
1270 || blue_mask_shift + blue_mask_size > 32) {
1271 return NULL;
1272 }
1273
1274 if (_malloc == NULL) {
1275#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC
1276 if (bump_allocated_instance == true) {
1277 return NULL;
1278 }
1279 _malloc = bump_alloc;
1280 // Limit terminal size if needed
1281 size_t fb_width_limit, fb_height_limit;
1282 bump_pool_limits(width_limit: &fb_width_limit, height_limit: &fb_height_limit);
1283 if (width > fb_width_limit || height > fb_height_limit) {
1284 size_t width_limit = width > fb_width_limit ? fb_width_limit : width;
1285 size_t height_limit = height > fb_height_limit ? fb_height_limit : height;
1286
1287 // width/height are logical (post-rotation) dimensions. For the
1288 // centering offset, we need to map back to the physical layout.
1289 // For ROTATE_0/180: logical height = physical rows (pitch stride),
1290 // logical width = physical cols (4-byte stride).
1291 // For ROTATE_90/270: logical height = physical cols (4-byte stride),
1292 // logical width = physical rows (pitch stride).
1293 if (rotation == FLANTERM_FB_ROTATE_90 || rotation == FLANTERM_FB_ROTATE_270) {
1294 framebuffer = (uint32_t *)((uintptr_t)framebuffer + ((((height / 2) - (height_limit / 2)) * 4) + (((width / 2) - (width_limit / 2)) * pitch)));
1295 } else {
1296 framebuffer = (uint32_t *)((uintptr_t)framebuffer + ((((height / 2) - (height_limit / 2)) * pitch) + (((width / 2) - (width_limit / 2)) * 4)));
1297 }
1298
1299 width = width_limit;
1300 height = height_limit;
1301
1302 // phys_height must reflect the physical row count for flush_callback.
1303 // For ROTATE_0/180, logical height = physical rows.
1304 // For ROTATE_90/270, logical width = physical rows.
1305 if (rotation == FLANTERM_FB_ROTATE_90 || rotation == FLANTERM_FB_ROTATE_270) {
1306 phys_height = width;
1307 } else {
1308 phys_height = height;
1309 }
1310 }
1311
1312 // Force disable canvas
1313 canvas = NULL;
1314#else
1315 return NULL;
1316#endif
1317 }
1318
1319 struct flanterm_fb_context *ctx = NULL;
1320 ctx = _malloc(sizeof(struct flanterm_fb_context));
1321 if (ctx == NULL) {
1322 goto fail;
1323 }
1324
1325 struct flanterm_context *_ctx = (void *)ctx;
1326 memset(ctx, 0, sizeof(struct flanterm_fb_context));
1327
1328 ctx->red_mask_size = red_mask_size;
1329 ctx->red_mask_shift = red_mask_shift + (red_mask_size - 8);
1330 ctx->green_mask_size = green_mask_size;
1331 ctx->green_mask_shift = green_mask_shift + (green_mask_size - 8);
1332 ctx->blue_mask_size = blue_mask_size;
1333 ctx->blue_mask_shift = blue_mask_shift + (blue_mask_size - 8);
1334
1335 if (ansi_colours != NULL) {
1336 for (size_t i = 0; i < 8; i++) {
1337 ctx->ansi_colours[i] = convert_colour(_ctx, colour: ansi_colours[i]);
1338 }
1339 } else {
1340 ctx->ansi_colours[0] = convert_colour(_ctx, colour: 0x00000000); // black
1341 ctx->ansi_colours[1] = convert_colour(_ctx, colour: 0x00aa0000); // red
1342 ctx->ansi_colours[2] = convert_colour(_ctx, colour: 0x0000aa00); // green
1343 ctx->ansi_colours[3] = convert_colour(_ctx, colour: 0x00aa5500); // brown
1344 ctx->ansi_colours[4] = convert_colour(_ctx, colour: 0x000000aa); // blue
1345 ctx->ansi_colours[5] = convert_colour(_ctx, colour: 0x00aa00aa); // magenta
1346 ctx->ansi_colours[6] = convert_colour(_ctx, colour: 0x0000aaaa); // cyan
1347 ctx->ansi_colours[7] = convert_colour(_ctx, colour: 0x00aaaaaa); // grey
1348 }
1349
1350 if (ansi_bright_colours != NULL) {
1351 for (size_t i = 0; i < 8; i++) {
1352 ctx->ansi_bright_colours[i] = convert_colour(_ctx, colour: ansi_bright_colours[i]);
1353 }
1354 } else {
1355 ctx->ansi_bright_colours[0] = convert_colour(_ctx, colour: 0x00555555); // black
1356 ctx->ansi_bright_colours[1] = convert_colour(_ctx, colour: 0x00ff5555); // red
1357 ctx->ansi_bright_colours[2] = convert_colour(_ctx, colour: 0x0055ff55); // green
1358 ctx->ansi_bright_colours[3] = convert_colour(_ctx, colour: 0x00ffff55); // brown
1359 ctx->ansi_bright_colours[4] = convert_colour(_ctx, colour: 0x005555ff); // blue
1360 ctx->ansi_bright_colours[5] = convert_colour(_ctx, colour: 0x00ff55ff); // magenta
1361 ctx->ansi_bright_colours[6] = convert_colour(_ctx, colour: 0x0055ffff); // cyan
1362 ctx->ansi_bright_colours[7] = convert_colour(_ctx, colour: 0x00ffffff); // grey
1363 }
1364
1365 if (default_bg != NULL) {
1366 ctx->default_bg = convert_colour(_ctx, colour: *default_bg);
1367 } else {
1368 ctx->default_bg = 0x00000000; // background (black)
1369 }
1370
1371 if (default_fg != NULL) {
1372 ctx->default_fg = convert_colour(_ctx, colour: *default_fg);
1373 } else {
1374 ctx->default_fg = convert_colour(_ctx, colour: 0x00aaaaaa); // foreground (grey)
1375 }
1376
1377 if (default_bg_bright != NULL) {
1378 ctx->default_bg_bright = convert_colour(_ctx, colour: *default_bg_bright);
1379 } else {
1380 ctx->default_bg_bright = convert_colour(_ctx, colour: 0x00555555); // background (black)
1381 }
1382
1383 if (default_fg_bright != NULL) {
1384 ctx->default_fg_bright = convert_colour(_ctx, colour: *default_fg_bright);
1385 } else {
1386 ctx->default_fg_bright = convert_colour(_ctx, colour: 0x00ffffff); // foreground (grey)
1387 }
1388
1389 ctx->text_fg = ctx->default_fg;
1390 ctx->text_fg_default = false;
1391 ctx->text_bg_default = true;
1392 ctx->saved_state_text_fg = ctx->text_fg;
1393 ctx->saved_state_text_bg = ctx->text_bg;
1394 ctx->saved_state_text_fg_default = ctx->text_fg_default;
1395 ctx->saved_state_text_bg_default = ctx->text_bg_default;
1396
1397 ctx->rotation = rotation;
1398
1399 ctx->framebuffer = (void *)framebuffer;
1400 ctx->width = width;
1401 ctx->height = height;
1402 ctx->phys_height = phys_height;
1403 ctx->pitch = pitch;
1404
1405 // VGA fonts are always one byte per scanline regardless of font_width
1406#define FONT_BYTES (font_height * FLANTERM_FB_FONT_GLYPHS)
1407
1408 if (font != NULL) {
1409 ctx->font_width = font_width;
1410 ctx->font_height = font_height;
1411 if (mul_size_overflow(a: font_height, b: (size_t)FLANTERM_FB_FONT_GLYPHS, out: &ctx->font_bits_size)) {
1412 goto fail;
1413 }
1414 ctx->font_bits = _malloc(ctx->font_bits_size);
1415 if (ctx->font_bits == NULL) {
1416 goto fail;
1417 }
1418 memcpy(ctx->font_bits, font, ctx->font_bits_size);
1419 } else {
1420 ctx->font_width = font_width = 8;
1421 ctx->font_height = font_height = 16;
1422 ctx->font_bits_size = FONT_BYTES;
1423 font_spacing = 1;
1424 ctx->font_bits = _malloc(ctx->font_bits_size);
1425 if (ctx->font_bits == NULL) {
1426 goto fail;
1427 }
1428 memcpy(ctx->font_bits, builtin_font, ctx->font_bits_size);
1429 }
1430
1431#undef FONT_BYTES
1432
1433 if (font_spacing > SIZE_MAX - ctx->font_width) {
1434 goto fail;
1435 }
1436 ctx->font_width += font_spacing;
1437
1438 if (mul_size_overflow(a: (size_t)FLANTERM_FB_FONT_GLYPHS, b: font_height, out: &ctx->font_bool_size) ||
1439 mul_size_overflow(a: ctx->font_bool_size, b: ctx->font_width, out: &ctx->font_bool_size) ||
1440 mul_size_overflow(a: ctx->font_bool_size, b: sizeof(bool), out: &ctx->font_bool_size)) {
1441 goto fail;
1442 }
1443 ctx->font_bool = _malloc(ctx->font_bool_size);
1444 if (ctx->font_bool == NULL) {
1445 goto fail;
1446 }
1447
1448 for (size_t i = 0; i < FLANTERM_FB_FONT_GLYPHS; i++) {
1449 uint8_t *glyph = &ctx->font_bits[i * font_height];
1450
1451 for (size_t y = 0; y < font_height; y++) {
1452 // NOTE: the characters in VGA fonts are always one byte wide.
1453 // 9 dot wide fonts have 8 dots and one empty column, except
1454 // characters 0xC0-0xDF replicate column 9.
1455 for (size_t x = 0; x < 8; x++) {
1456 size_t offset = i * font_height * ctx->font_width + y * ctx->font_width + x;
1457
1458 if ((glyph[y] & (0x80 >> x))) {
1459 ctx->font_bool[offset] = true;
1460 } else {
1461 ctx->font_bool[offset] = false;
1462 }
1463 }
1464 // fill columns above 8 like VGA Line Graphics Mode does
1465 for (size_t x = 8; x < ctx->font_width; x++) {
1466 size_t offset = i * font_height * ctx->font_width + y * ctx->font_width + x;
1467
1468 if (i >= 0xc0 && i <= 0xdf) {
1469 ctx->font_bool[offset] = (glyph[y] & 1);
1470 } else {
1471 ctx->font_bool[offset] = false;
1472 }
1473 }
1474 }
1475 }
1476
1477 ctx->font_scale_x = font_scale_x;
1478 ctx->font_scale_y = font_scale_y;
1479
1480 if (mul_size_overflow(a: ctx->font_width, b: font_scale_x, out: &ctx->glyph_width) ||
1481 mul_size_overflow(a: font_height, b: font_scale_y, out: &ctx->glyph_height)) {
1482 goto fail;
1483 }
1484
1485 if (ctx->glyph_width == 0 || ctx->glyph_height == 0) {
1486 goto fail;
1487 }
1488
1489 if (margin > SIZE_MAX / 2 || margin * 2 >= ctx->width || margin * 2 >= ctx->height) {
1490 goto fail;
1491 }
1492
1493 _ctx->cols = (ctx->width - margin * 2) / ctx->glyph_width;
1494 _ctx->rows = (ctx->height - margin * 2) / ctx->glyph_height;
1495
1496 if (_ctx->cols == 0 || _ctx->rows == 0) {
1497 goto fail;
1498 }
1499
1500 ctx->offset_x = margin + ((ctx->width - margin * 2) % ctx->glyph_width) / 2;
1501 ctx->offset_y = margin + ((ctx->height - margin * 2) % ctx->glyph_height) / 2;
1502
1503 if (mul_size_overflow(a: _ctx->rows, b: _ctx->cols, out: &ctx->grid_size) ||
1504 mul_size_overflow(a: ctx->grid_size, b: sizeof(struct flanterm_fb_char), out: &ctx->grid_size)) {
1505 goto fail;
1506 }
1507 ctx->grid = _malloc(ctx->grid_size);
1508 if (ctx->grid == NULL) {
1509 goto fail;
1510 }
1511 for (size_t i = 0; i < _ctx->rows * _ctx->cols; i++) {
1512 ctx->grid[i].c = ' ';
1513 ctx->grid[i].fg = ctx->text_fg;
1514 ctx->grid[i].bg = ctx->text_bg;
1515 ctx->grid[i].fg_default = ctx->text_fg_default;
1516 ctx->grid[i].bg_default = ctx->text_bg_default;
1517 }
1518
1519 if (mul_size_overflow(a: _ctx->rows, b: _ctx->cols, out: &ctx->queue_size) ||
1520 mul_size_overflow(a: ctx->queue_size, b: sizeof(struct flanterm_fb_queue_item), out: &ctx->queue_size)) {
1521 goto fail;
1522 }
1523 ctx->queue = _malloc(ctx->queue_size);
1524 if (ctx->queue == NULL) {
1525 goto fail;
1526 }
1527 ctx->queue_i = 0;
1528 memset(ctx->queue, 0, ctx->queue_size);
1529
1530 if (mul_size_overflow(a: _ctx->rows, b: _ctx->cols, out: &ctx->map_size) ||
1531 mul_size_overflow(a: ctx->map_size, b: sizeof(struct flanterm_fb_queue_item *), out: &ctx->map_size)) {
1532 goto fail;
1533 }
1534 ctx->map = _malloc(ctx->map_size);
1535 if (ctx->map == NULL) {
1536 goto fail;
1537 }
1538 memset(ctx->map, 0, ctx->map_size);
1539
1540 if (canvas != NULL) {
1541 if (mul_size_overflow(a: ctx->width, b: ctx->height, out: &ctx->canvas_size) ||
1542 mul_size_overflow(a: ctx->canvas_size, b: sizeof(uint32_t), out: &ctx->canvas_size)) {
1543 goto fail;
1544 }
1545 ctx->canvas = _malloc(ctx->canvas_size);
1546 if (ctx->canvas == NULL) {
1547 goto fail;
1548 }
1549 for (size_t i = 0; i < ctx->width * ctx->height; i++) {
1550 ctx->canvas[i] = convert_colour(_ctx, colour: canvas[i]);
1551 }
1552 }
1553
1554 if (font_scale_x == 1 && font_scale_y == 1) {
1555 if (canvas == NULL) {
1556 ctx->plot_char = plot_char_unscaled_uncanvas;
1557 } else {
1558 ctx->plot_char = plot_char_unscaled_canvas;
1559 }
1560 } else {
1561 if (canvas == NULL) {
1562 ctx->plot_char = plot_char_scaled_uncanvas;
1563 } else {
1564 ctx->plot_char = plot_char_scaled_canvas;
1565 }
1566 }
1567
1568 _ctx->raw_putchar = flanterm_fb_raw_putchar;
1569 _ctx->clear = flanterm_fb_clear;
1570 _ctx->set_cursor_pos = flanterm_fb_set_cursor_pos;
1571 _ctx->get_cursor_pos = flanterm_fb_get_cursor_pos;
1572 _ctx->set_text_fg = flanterm_fb_set_text_fg;
1573 _ctx->set_text_bg = flanterm_fb_set_text_bg;
1574 _ctx->set_text_fg_bright = flanterm_fb_set_text_fg_bright;
1575 _ctx->set_text_bg_bright = flanterm_fb_set_text_bg_bright;
1576 _ctx->set_text_fg_rgb = flanterm_fb_set_text_fg_rgb;
1577 _ctx->set_text_bg_rgb = flanterm_fb_set_text_bg_rgb;
1578 _ctx->set_text_fg_default = flanterm_fb_set_text_fg_default;
1579 _ctx->set_text_bg_default = flanterm_fb_set_text_bg_default;
1580 _ctx->set_text_fg_default_bright = flanterm_fb_set_text_fg_default_bright;
1581 _ctx->set_text_bg_default_bright = flanterm_fb_set_text_bg_default_bright;
1582 _ctx->move_character = flanterm_fb_move_character;
1583 _ctx->scroll = flanterm_fb_scroll;
1584 _ctx->revscroll = flanterm_fb_revscroll;
1585 _ctx->swap_palette = flanterm_fb_swap_palette;
1586 _ctx->save_state = flanterm_fb_save_state;
1587 _ctx->restore_state = flanterm_fb_restore_state;
1588 _ctx->double_buffer_flush = flanterm_fb_double_buffer_flush;
1589 _ctx->full_refresh = flanterm_fb_full_refresh;
1590 _ctx->deinit = flanterm_fb_deinit;
1591
1592 flanterm_context_reinit(ctx: _ctx);
1593 _ctx->autoflush = autoflush;
1594
1595 // Refreshing here would overwrite a framebuffer the caller wants kept.
1596 if (autoflush) {
1597 flanterm_fb_full_refresh(_ctx);
1598 } else {
1599 ctx->full_refresh_pending = true;
1600 }
1601
1602#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC
1603 if (_malloc == bump_alloc) {
1604 bump_allocated_instance = true;
1605 }
1606#endif
1607
1608 return _ctx;
1609
1610fail:
1611 if (ctx == NULL) {
1612 return NULL;
1613 }
1614
1615#ifndef FLANTERM_FB_DISABLE_BUMP_ALLOC
1616 if (_malloc == bump_alloc) {
1617 bump_alloc_ptr = 0;
1618 bump_alloc_base_offset_added = false;
1619 return NULL;
1620 }
1621#endif
1622
1623 if (_free == NULL) {
1624 return NULL;
1625 }
1626
1627 if (ctx->canvas != NULL) {
1628 _free(ctx->canvas, ctx->canvas_size);
1629 }
1630 if (ctx->map != NULL) {
1631 _free(ctx->map, ctx->map_size);
1632 }
1633 if (ctx->queue != NULL) {
1634 _free(ctx->queue, ctx->queue_size);
1635 }
1636 if (ctx->grid != NULL) {
1637 _free(ctx->grid, ctx->grid_size);
1638 }
1639 if (ctx->font_bool != NULL) {
1640 _free(ctx->font_bool, ctx->font_bool_size);
1641 }
1642 if (ctx->font_bits != NULL) {
1643 _free(ctx->font_bits, ctx->font_bits_size);
1644 }
1645 if (ctx != NULL) {
1646 _free(ctx, sizeof(struct flanterm_fb_context));
1647 }
1648
1649 return NULL;
1650}
1651
1652void flanterm_fb_set_flush_callback(struct flanterm_context *_ctx, void (*flush_callback)(volatile void *address, size_t length)) {
1653 struct flanterm_fb_context *ctx = (void *)_ctx;
1654 ctx->flush_callback = flush_callback;
1655 if (flush_callback != NULL) {
1656 flush_callback(ctx->framebuffer, ctx->pitch * ctx->phys_height);
1657 }
1658}
1659