GBAlatro
A Demake of Balatro for the GBA
Loading...
Searching...
No Matches
graphic_utils.c
1#include "graphic_utils.h"
2
3#include "layout.h"
4
5#include <stdio.h>
6#include <string.h>
7#include <tonc_core.h>
8#include <tonc_math.h>
9#include <tonc_tte.h>
10
11static const Rect FULL_SCREENBLOCK_RECT = {0, 0, SE_ROW_LEN - 1, SE_COL_LEN - 1};
12
13static void clip_se_rect_to_screenblock(Rect* rect);
14static void bg_se_copy_or_move_rect_1_tile_vert(
15 u16 bg_sbb,
16 Rect se_rect,
17 enum ScreenVertDir direction,
18 bool move
19);
20static void main_bg_se_copy_or_move_rect_1_tile_vert(
21 Rect se_rect,
22 enum ScreenVertDir direction,
23 bool move
24);
25
26// Clips a rect of screenblock entries to a specified rect
27// The bounding rect is not required to be within screenblock boundaries
28static inline void clip_se_rect_to_bounding_rect(Rect* rect, const Rect* bounding_rect)
29{
30 rect->right = min(rect->right, bounding_rect->right);
31 rect->bottom = min(rect->bottom, bounding_rect->bottom);
32 rect->left = max(rect->left, bounding_rect->left);
33 rect->top = max(rect->top, bounding_rect->top);
34}
35
36// Can be unstaticed if needed
37// Clips a rect of screenblock entries to screenblock boundaries
38static void clip_se_rect_to_screenblock(Rect* rect)
39{
40 clip_se_rect_to_bounding_rect(rect, &FULL_SCREENBLOCK_RECT);
41}
42
43// Clips a rect of screenblock entries to be within one step of
44// screenblock boundaries vertically depending on direction.
45static inline void clip_se_rect_within_step_of_full_screen_vert(
46 Rect* se_rect,
47 enum ScreenVertDir direction
48)
49{
50 Rect bounding_rect = FULL_SCREENBLOCK_RECT;
51 if (direction == SCREEN_UP)
52 {
53 bounding_rect.top += 1;
54 }
55 else if (direction == SCREEN_DOWN)
56 {
57 bounding_rect.bottom -= 1;
58 }
59
60 clip_se_rect_to_bounding_rect(se_rect, &bounding_rect);
61}
62
63// Internal static function to merge implementation of move/copy functions.
64static void bg_se_copy_or_move_rect_1_tile_vert(
65 u16 bg_sbb,
66 Rect se_rect,
67 enum ScreenVertDir direction,
68 bool move
69)
70{
71 if (se_rect.left > se_rect.right || (direction != SCREEN_UP && direction != SCREEN_DOWN))
72 return;
73
74 // Clip to avoid read/write overflow of the screenblock
75 clip_se_rect_within_step_of_full_screen_vert(&se_rect, direction);
76
77 int start = (direction == SCREEN_UP) ? se_rect.top : se_rect.bottom;
78 int end = (direction == SCREEN_UP) ? se_rect.bottom : se_rect.top;
79
80 for (int y = start; y != end - direction; y -= direction)
81 {
82 memcpy16(
83 &(se_mat[bg_sbb][y + direction][se_rect.left]),
84 &se_mat[bg_sbb][y][se_rect.left],
85 rect_width(&se_rect)
86 );
87 }
88
89 if (move)
90 {
91 memset16(&se_mat[bg_sbb][end][se_rect.left], 0x0000, rect_width(&se_rect));
92 }
93}
94
95static void main_bg_se_copy_or_move_rect_1_tile_vert(
96 Rect se_rect,
97 enum ScreenVertDir direction,
98 bool move
99)
100{
101 bg_se_copy_or_move_rect_1_tile_vert(MAIN_BG_SBB, se_rect, direction, move);
102}
103
104void bg_se_copy_rect_1_tile_vert(u16 bg_sbb, Rect se_rect, enum ScreenVertDir direction)
105{
106 bg_se_copy_or_move_rect_1_tile_vert(MAIN_BG_SBB, se_rect, direction, false);
107}
108
109void bg_se_move_rect_1_tile_vert(u16 bg_sbb, Rect se_rect, enum ScreenVertDir direction)
110{
111 bg_se_copy_or_move_rect_1_tile_vert(MAIN_BG_SBB, se_rect, direction, true);
112}
113
114void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, enum ScreenVertDir direction)
115{
116 main_bg_se_copy_or_move_rect_1_tile_vert(se_rect, direction, false);
117}
118
119void main_bg_se_move_rect_1_tile_vert(Rect se_rect, enum ScreenVertDir direction)
120{
121 main_bg_se_copy_or_move_rect_1_tile_vert(se_rect, direction, true);
122}
123
124void main_bg_se_copy_rect(Rect se_rect, BG_POINT dest_pos)
125{
126 if (se_rect.left > se_rect.right || se_rect.top > se_rect.bottom)
127 return;
128
129 // Clip to avoid screenblock overflow
130 clip_se_rect_to_screenblock(&se_rect);
131
132 int width = rect_width(&se_rect);
133 int height = rect_height(&se_rect);
134 SE tile_map[height][width];
135
136 // Copy the rect to the tile map
137 for (int sy = 0; sy < height; sy++)
138 {
139 memcpy16(&tile_map[sy][0], &se_mat[MAIN_BG_SBB][se_rect.top + sy][se_rect.left], width);
140 }
141
142 // TODO: Avoid overflow
143 // Copy the tilemap to the new rect position
144 for (int sy = 0; sy < height; sy++)
145 {
146 memcpy16(&se_mat[MAIN_BG_SBB][dest_pos.y + sy][dest_pos.x], &tile_map[sy][0], width);
147 }
148}
149
150static inline void main_bg_se_fill_rect_with_se(SE se, Rect se_rect)
151{
152 if (se_rect.left > se_rect.right || se_rect.top > se_rect.bottom)
153 return;
154
155 // Clip to avoid screenblock overflow
156 clip_se_rect_to_screenblock(&se_rect);
157
158 int width = rect_width(&se_rect);
159 int height = rect_height(&se_rect);
160
161 for (int sy = 0; sy < height; sy++)
162 {
163 memset16(&se_mat[MAIN_BG_SBB][se_rect.top + sy][se_rect.left], se, width);
164 }
165}
166
167void main_bg_se_copy_expand_tile(Rect se_rect_dest, BG_POINT se_tile_src)
168{
169 main_bg_se_fill_rect_with_se(se_mat[MAIN_BG_SBB][se_tile_src.y][se_tile_src.x], se_rect_dest);
170}
171
172void main_bg_se_copy_expand_3x3_rect(Rect se_dest_rect, BG_POINT src_top_left_pnt)
173{
174 // New implementation: uses a 9-patch to factorize as much code as we can
175 // clang-format off
176 NinePatchRect src_9_ptch = {
177 .patch_rect =
178 {
179 src_top_left_pnt.x,
180 src_top_left_pnt.y,
181 src_top_left_pnt.x + 2,
182 src_top_left_pnt.y + 2
183 },
184 .margins = {1, 1, 1, 1}
185 };
186 // clang-format on
187 main_bg_se_copy_expand_9_patch(se_dest_rect, &src_9_ptch);
188}
189
190// Helper: Copy the left and right sides of a 3x3 tile block
191static inline void main_bg_se_3w_copy_expand_left_right_sides(
192 const Rect* se_dest_rect,
193 const BG_POINT* src_left_pnt
194)
195{
196 SE middle_left_se = se_mat[MAIN_BG_SBB][src_left_pnt->y][src_left_pnt->x];
197 // Assuming width 3 so the right side is + 2
198 SE middle_right_se = se_mat[MAIN_BG_SBB][src_left_pnt->y][src_left_pnt->x + 2];
199 int dest_rect_width = rect_width(se_dest_rect);
200 int dest_rect_height = rect_height(se_dest_rect);
201 for (int y = 0; y < dest_rect_height; y++)
202 {
203 se_mat[MAIN_BG_SBB][se_dest_rect->top + y][se_dest_rect->left] = middle_left_se;
204 se_mat[MAIN_BG_SBB][se_dest_rect->top + y][se_dest_rect->left + dest_rect_width - 1] =
205 middle_right_se;
206 }
207}
208
209void main_bg_se_copy_expand_3w_row(Rect se_dest_rect, BG_POINT src_row_left_pnt)
210{
211 clip_se_rect_to_screenblock(&se_dest_rect);
212 int dest_rect_width = rect_width(&se_dest_rect);
213 if (dest_rect_width < 2)
214 return;
215
216 // Copy left and right sides
217 main_bg_se_3w_copy_expand_left_right_sides(&se_dest_rect, &src_row_left_pnt);
218
219 if (dest_rect_width > 2)
220 {
221 SE middle_fill_se = se_mat[MAIN_BG_SBB][src_row_left_pnt.y][src_row_left_pnt.x + 1];
222 Rect dest_inner_fill_rect = se_dest_rect;
223
224 // Avoid copying the sides when filling the rect.
225 dest_inner_fill_rect.left += 1;
226 dest_inner_fill_rect.right -= 1;
227 main_bg_se_fill_rect_with_se(middle_fill_se, dest_inner_fill_rect);
228 }
229}
230
231// Helper: Copy the four corners of a 9-patch
232static inline void main_bg_se_expand_9_patch_copy_corners(
233 Rect se_dest_rect,
234 const NinePatchRect* src_9_ptch
235)
236{
237 BG_POINT top_left_dest_pos = {se_dest_rect.left, se_dest_rect.top};
238 Rect top_left_src = {
239 src_9_ptch->patch_rect.left,
240 src_9_ptch->patch_rect.top,
241 src_9_ptch->patch_rect.left + src_9_ptch->margins.left - 1,
242 src_9_ptch->patch_rect.top + src_9_ptch->margins.top - 1
243 };
244
245 BG_POINT top_right_dest_pos = {
246 se_dest_rect.right - src_9_ptch->margins.right + 1,
247 se_dest_rect.top
248 };
249 Rect top_right_src = {
250 src_9_ptch->patch_rect.right - src_9_ptch->margins.right + 1,
251 src_9_ptch->patch_rect.top,
252 src_9_ptch->patch_rect.right,
253 src_9_ptch->patch_rect.top + src_9_ptch->margins.top - 1
254 };
255
256 BG_POINT bottom_right_dest_pos = {
257 se_dest_rect.right - src_9_ptch->margins.right + 1,
258 se_dest_rect.bottom - src_9_ptch->margins.bottom + 1
259 };
260 Rect bottom_right_src = {
261 src_9_ptch->patch_rect.right - src_9_ptch->margins.right + 1,
262 src_9_ptch->patch_rect.bottom - src_9_ptch->margins.bottom + 1,
263 src_9_ptch->patch_rect.right,
264 src_9_ptch->patch_rect.bottom
265 };
266
267 BG_POINT bottom_left_dest_pos = {
268 se_dest_rect.left,
269 se_dest_rect.bottom - src_9_ptch->margins.bottom + 1
270 };
271 Rect bottom_left_src = {
272 src_9_ptch->patch_rect.left,
273 src_9_ptch->patch_rect.bottom - src_9_ptch->margins.bottom + 1,
274 src_9_ptch->patch_rect.left + src_9_ptch->margins.left - 1,
275 src_9_ptch->patch_rect.bottom
276 };
277
278 main_bg_se_copy_rect(top_left_src, top_left_dest_pos);
279 main_bg_se_copy_rect(top_right_src, top_right_dest_pos);
280 main_bg_se_copy_rect(bottom_right_src, bottom_right_dest_pos);
281 main_bg_se_copy_rect(bottom_left_src, bottom_left_dest_pos);
282}
283
284// Helper: Copy the four sides of a 9-patch
285static inline void main_bg_se_expand_9_patch_stretch_sides(
286 Rect se_dest_rect,
287 const NinePatchRect* src_9_ptch,
288 int top_bottom_width,
289 int left_right_height
290)
291{
292 // We can take the Top and Bottom sides' tiles 1 by 1 and stretch them horizontally
293
294 // Top side
295 for (int i = 0; i < src_9_ptch->margins.top; i++)
296 {
297 SE top_side_tile = se_mat[MAIN_BG_SBB][src_9_ptch->patch_rect.top + i]
298 [src_9_ptch->patch_rect.left + src_9_ptch->margins.left];
299 memset16(
300 &se_mat[MAIN_BG_SBB][se_dest_rect.top + i]
301 [se_dest_rect.left + src_9_ptch->margins.left],
302 top_side_tile,
303 top_bottom_width
304 );
305 }
306
307 // Bottom side side
308 for (int i = 0; i < src_9_ptch->margins.bottom; i++)
309 {
310 SE bottom_side_tile = se_mat[MAIN_BG_SBB][src_9_ptch->patch_rect.bottom - i]
311 [src_9_ptch->patch_rect.left + src_9_ptch->margins.left];
312 memset16(
313 &se_mat[MAIN_BG_SBB][se_dest_rect.bottom - i]
314 [se_dest_rect.left + src_9_ptch->margins.left],
315 bottom_side_tile,
316 top_bottom_width
317 );
318 }
319
320 // Can't stretch the Left and Right sides with a wide memset16, and
321 // main_bg_se_fill_rect_with_se` does it rows-wise too. We'll have to copy rects by hand.
322
323 Rect left_side_tiles_src = {
324 src_9_ptch->patch_rect.left,
325 src_9_ptch->patch_rect.top + src_9_ptch->margins.top,
326 src_9_ptch->patch_rect.left + src_9_ptch->margins.left - 1,
327 src_9_ptch->patch_rect.top + src_9_ptch->margins.top
328 };
329 BG_POINT left_side_tiles_dest_pos = {
330 se_dest_rect.left,
331 se_dest_rect.top + src_9_ptch->margins.top
332 };
333
334 Rect right_side_tiles_src = {
335 src_9_ptch->patch_rect.right - src_9_ptch->margins.right + 1,
336 src_9_ptch->patch_rect.top + src_9_ptch->margins.top,
337 src_9_ptch->patch_rect.right,
338 src_9_ptch->patch_rect.top + src_9_ptch->margins.top
339 };
340 BG_POINT right_side_tiles_dest_pos = {
341 se_dest_rect.right - src_9_ptch->margins.right + 1,
342 se_dest_rect.top + src_9_ptch->margins.top
343 };
344
345 for (int i = 0; i < left_right_height; i++)
346 {
347 main_bg_se_copy_rect(left_side_tiles_src, left_side_tiles_dest_pos);
348 left_side_tiles_dest_pos.y++;
349
350 main_bg_se_copy_rect(right_side_tiles_src, right_side_tiles_dest_pos);
351 right_side_tiles_dest_pos.y++;
352 }
353}
354
355void main_bg_se_copy_expand_9_patch(Rect se_dest_rect, const NinePatchRect* src_9_ptch)
356{
357 clip_se_rect_to_screenblock(&se_dest_rect);
358
359 int dest_rect_width = rect_width(&se_dest_rect);
360 int dest_rect_height = rect_height(&se_dest_rect);
361
362 int src_9ptch_min_width = src_9_ptch->margins.left + src_9_ptch->margins.right;
363 int src_9ptch_min_height = src_9_ptch->margins.top + src_9_ptch->margins.bottom;
364
365 // Verify the dest rect is big enough to fit at least the 9-patch's corners
366 if (dest_rect_width < src_9ptch_min_width || dest_rect_height < src_9ptch_min_height)
367 return;
368
369 // Copy the corners
370 main_bg_se_expand_9_patch_copy_corners(se_dest_rect, src_9_ptch);
371
372 // Fill the sides and center if needed
373 if (dest_rect_width > src_9ptch_min_width && dest_rect_height > src_9ptch_min_height)
374 {
375 // Stretch sides
376 main_bg_se_expand_9_patch_stretch_sides(
377 se_dest_rect,
378 src_9_ptch,
379 dest_rect_width - src_9ptch_min_width,
380 dest_rect_height - src_9ptch_min_height
381 );
382
383 // Fill center
384 SE middle_fill_se =
385 se_mat[MAIN_BG_SBB][src_9_ptch->patch_rect.top + src_9_ptch->margins.top]
386 [src_9_ptch->patch_rect.left + src_9_ptch->margins.left];
387 Rect dest_inner_fill_rect = {
388 se_dest_rect.left + src_9_ptch->margins.left,
389 se_dest_rect.top + src_9_ptch->margins.top,
390 se_dest_rect.right - src_9_ptch->margins.right,
391 se_dest_rect.bottom - src_9_ptch->margins.bottom
392 };
393 main_bg_se_fill_rect_with_se(middle_fill_se, dest_inner_fill_rect);
394 }
395}
396
398{
399 tte_erase_rect(rect.left, rect.top, rect.right, rect.bottom);
400}
401
403 Rect* rect,
404 const char* str,
405 enum OverflowDir overflow_direction
406)
407{
408 // TODO: Allow passing string length to avoid calling strlen()?
409 int str_len = strlen(str);
410 if (overflow_direction == OVERFLOW_LEFT)
411 {
412 rect->left = max(0, rect->right - str_len * TTE_CHAR_SIZE);
413 }
414 else if (overflow_direction == OVERFLOW_RIGHT)
415 {
416 int num_fitting_chars = rect_width(rect) / TTE_CHAR_SIZE;
417 if (str_len < num_fitting_chars)
418 rect->left += (num_fitting_chars - str_len) * TTE_CHAR_SIZE;
419 // else nothing is to be updated, entire rect is filled and may overflow
420 }
421}
422
423void update_text_rect_to_center_str(Rect* rect, const char* str, enum ScreenHorzDir bias_direction)
424{
425 if (rect == NULL || str == NULL)
426 return;
427
428 int text_width_chars = strlen(str);
429 int rect_width_chars = rect_width(rect) / TTE_CHAR_SIZE;
430
431 bool bias_right = (bias_direction == SCREEN_RIGHT);
432
433 /* Adding bias_right makes sure that we round up when biased right
434 * but round down when biased left.
435 */
436 rect->left += max(0, (rect_width_chars - text_width_chars + bias_right) / 2) * TTE_CHAR_SIZE;
437}
438
439void memcpy16_tile8_with_palette_offset(u16* dst, const u16* src, uint hwcount, u8 palette_offset)
440{
441 const u16 offset = (((palette_offset) << 8) | (palette_offset));
442 for (int i = 0; i < hwcount; i++)
443 {
444 // Copying u8 data twice across u16 data
445 dst[i] = src[i] + offset;
446 }
447}
448
449void memcpy32_tile8_with_palette_offset(u32* dst, const u32* src, uint wcount, u8 palette_offset)
450{
451 const u32 offset =
452 (palette_offset << 24) | (palette_offset << 16) | (palette_offset << 8) | palette_offset;
453 for (int i = 0; i < wcount; i++)
454 {
455 // Copying u8 data 4 times across u32 data
456 dst[i] = src[i] + offset;
457 }
458}
459
460void toggle_windows(bool win0, bool win1)
461{
462 if (win0)
463 {
464 REG_DISPCNT |= DCNT_WIN0;
465 }
466 else
467 {
468 REG_DISPCNT &= ~DCNT_WIN0;
469 }
470
471 if (win1)
472 {
473 REG_DISPCNT |= DCNT_WIN1;
474 }
475 else
476 {
477 REG_DISPCNT &= ~DCNT_WIN1;
478 }
479
480 if (win0 || win1)
481 {
482 REG_BLDCNT = BLD_BUILD(BLD_BG1, BLD_BG2, 1);
483 }
484 else
485 {
486 REG_BLDCNT = 0;
487 }
488}
489
490void main_bg_se_clear_rect(Rect se_rect)
491{
492 if (se_rect.left > se_rect.right)
493 return;
494 // Clip to avoid screenblock overflow
495 clip_se_rect_to_screenblock(&se_rect);
496
497 for (int y = se_rect.top; y < se_rect.bottom; y++)
498 {
499 memset16(&(se_mat[MAIN_BG_SBB][y][se_rect.left]), 0x0000, rect_width(&se_rect));
500 }
501}
502
503// Width of the screen, in nb of tiles
504#define MAX_LINE_TEXT_LENGTH 30
505
507 const char* raw_text,
508 Rect dst_rect,
509 enum TextJustifyFlag justify_direction,
510 enum ScreenHorzDir bias_direction,
511 bool do_print
512)
513{
514 // These are the actual lengths of the line/token, which take the {TAGS} into account
515
516 int raw_text_len = strlen(raw_text);
517
518 int line_start = 0;
519 int token_start = 0;
520 int token_len = 0;
521
522 // These lengths correspond to what will be visible on screen
523
524 int max_line_text_len = rect_width(&dst_rect);
525
526 int line_text_len = 0;
527 int token_text_len = 0;
528
529 int line_x = 0;
530 int line_y = 0;
531
532 // Will exit when there are no more words
533 while (line_start < raw_text_len)
534 {
535 // Need to do everything by hand, as it seems tte_printf does NOT stop at \0
536 token_len = 0;
537 token_text_len = 0;
538
539 // Parse the `raw_text` and
540 while (line_text_len <= max_line_text_len && token_start < raw_text_len)
541 {
542 int current_char = token_start + token_len;
543
544 // Handle tags
545 if (raw_text[current_char] == '#' && (current_char + 1) < raw_text_len &&
546 raw_text[current_char + 1] == '{')
547 {
548 while (current_char < raw_text_len && raw_text[current_char] != '}')
549 {
550 token_len++;
551 current_char++;
552 }
553
554 if (current_char < raw_text_len)
555 {
556 token_len++;
557 current_char++;
558 }
559 }
560
561 // Handle special cases
562
563 // End of word/text detected
564 if (raw_text[current_char] == ' ' || raw_text[current_char] == '\0')
565 {
566 token_start = current_char + 1;
567 // Set these to -1 so that it becomes 0 when incremented for the
568 // next iteration since we need to continue.
569 token_len = -1;
570 token_text_len = -1;
571 }
572 // Early end of line detected, interrupt parsing immediately
573 else if (raw_text[current_char] == '\n')
574 {
575 token_start = current_char + 1;
576 // Set to 0, no need to compensate for continuing the loops since
577 // we're breaking early from it.
578 token_len = 0;
579 token_text_len = 0;
580 line_text_len++;
581 break;
582 }
583
584 token_len++;
585 token_text_len++;
586 line_text_len++;
587 };
588
589 // Do not print anything if we only need to compute the paragraph's total height
590 if (do_print)
591 {
592 // Length of the raw slice for this line (includes formatting tags)
593 int line_len = token_start - line_start;
594
595 // Trim trailing whitespace/newlines from the raw slice
596 while (line_len > 0)
597 {
598 char c = raw_text[line_start + line_len - 1];
599 if (c == ' ' || c == '\n' || c == '\0')
600 line_len--;
601 else
602 break;
603 }
604
605 // Remove the overflowing token's visible length from this line
606 line_text_len = line_text_len - token_text_len - 1;
607 if (line_text_len < 0)
608 line_text_len = 0;
609
610 // useful DEBUG
611 // tte_printf("#{P:0,%d}line %d - %d", 40 + (dst_rect.top + line_y) * TILE_SIZE, line_y,
612 // line_text_len);
613
614 // Now, we can print the chars from line_start to token_start
615 line_x = 0;
616 if (justify_direction == JUSTIFY_CENTER)
617 {
618 line_x = (max_line_text_len - line_text_len) / 2;
619 }
620
621 tte_printf(
622 "#{P:%d,%d}%.*s",
623 (dst_rect.left + line_x) * TILE_SIZE,
624 (dst_rect.top + line_y) * TILE_SIZE,
625 line_len,
626 raw_text + line_start
627 );
628 }
629
630 line_start = token_start;
631 line_text_len = 0;
632 line_y++;
633 }
634
635 return line_y;
636}
Graphic utility functions.
void main_bg_se_move_rect_1_tile_vert(Rect se_rect, enum ScreenVertDir direction)
Moves a rect in the main background vertically in direction by a single tile.
void memcpy16_tile8_with_palette_offset(u16 *dst, const u16 *src, uint hwcount, u8 palette_offset)
Copies 16 bit data from src to dst, applying a palette offset to the data.
INLINE int rect_height(const Rect *rect)
Get the height of a rectangle.
void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, enum ScreenVertDir direction)
Copies a rect in the main background vertically in direction by a single tile.
void main_bg_se_clear_rect(Rect se_rect)
Clears a rect in the main background.
void main_bg_se_copy_rect(Rect se_rect, BG_POINT dest_pos)
Copies a rect in the main background from se_rect to the position (x, y).
void toggle_windows(bool win0, bool win1)
Toggles the visibility of the window layers.
void update_text_rect_to_right_align_str(Rect *rect, const char *str, enum OverflowDir overflow_direction)
Changes rect->left so it fits the digits of num exactly when right aligned to rect->right....
void update_text_rect_to_center_str(Rect *rect, const char *str, enum ScreenHorzDir bias_direction)
Updates a rect so a string is centered within it.
#define TILE_SIZE
Tile size in pixels, both height and width as tiles are square.
void memcpy32_tile8_with_palette_offset(u32 *dst, const u32 *src, uint wcount, u8 palette_offset)
Copies 32 bit data from src to dst, applying a palette offset to the data.
void main_bg_se_copy_expand_3x3_rect(Rect se_rect_dest, BG_POINT se_rect_src_3x3_top_left)
Copies a 3x3 rect and expands it to fit a passed rect. Special case of the 9-patch.
#define TTE_CHAR_SIZE
By default TTE characters occupy a single tile.
void tte_erase_rect_wrapper(Rect rect)
A wrapper for tte_erase_rect that would use the rect struct.
int tte_printf_justified_in_rect(const char *raw_text, Rect dst_rect, enum TextJustifyFlag justify_direction, enum ScreenHorzDir bias_direction, bool do_print)
Justify a text with custom formatting tags according to the given justification and bias direction ta...
void main_bg_se_copy_expand_3w_row(Rect se_dest_rect, BG_POINT src_row_left_pnt)
Copies a 3-width SE rect and expands it to fit a passed rect.
void main_bg_se_copy_expand_9_patch(Rect se_dest_rect, const NinePatchRect *src_9_ptch)
Copies a 9-patch and expands it to fit a passed rect.
void bg_se_copy_rect_1_tile_vert(u16 bg_sbb, Rect se_rect, enum ScreenVertDir direction)
Copies an SE rect vertically in direction by a single tile.
INLINE int rect_width(const Rect *rect)
Get the width of a rectangle.
void main_bg_se_copy_expand_tile(Rect se_rect_dest, BG_POINT se_tile_src)
Copies a tile in the main background at se_tile_src to fill se_rect_dest.
Header of shared rects and points needed for ui.
Structure to represent arbitrary-sized 9-patches.