11static const Rect FULL_SCREENBLOCK_RECT = {0, 0, SE_ROW_LEN - 1, SE_COL_LEN - 1};
13static void clip_se_rect_to_screenblock(Rect* rect);
14static void bg_se_copy_or_move_rect_1_tile_vert(
17 enum ScreenVertDir direction,
20static void main_bg_se_copy_or_move_rect_1_tile_vert(
22 enum ScreenVertDir direction,
28static inline void clip_se_rect_to_bounding_rect(Rect* rect,
const Rect* bounding_rect)
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);
38static void clip_se_rect_to_screenblock(Rect* rect)
40 clip_se_rect_to_bounding_rect(rect, &FULL_SCREENBLOCK_RECT);
45static inline void clip_se_rect_within_step_of_full_screen_vert(
47 enum ScreenVertDir direction
50 Rect bounding_rect = FULL_SCREENBLOCK_RECT;
51 if (direction == SCREEN_UP)
53 bounding_rect.top += 1;
55 else if (direction == SCREEN_DOWN)
57 bounding_rect.bottom -= 1;
60 clip_se_rect_to_bounding_rect(se_rect, &bounding_rect);
64static void bg_se_copy_or_move_rect_1_tile_vert(
67 enum ScreenVertDir direction,
71 if (se_rect.left > se_rect.right || (direction != SCREEN_UP && direction != SCREEN_DOWN))
75 clip_se_rect_within_step_of_full_screen_vert(&se_rect, direction);
77 int start = (direction == SCREEN_UP) ? se_rect.top : se_rect.bottom;
78 int end = (direction == SCREEN_UP) ? se_rect.bottom : se_rect.top;
80 for (
int y = start; y != end - direction; y -= direction)
83 &(se_mat[bg_sbb][y + direction][se_rect.left]),
84 &se_mat[bg_sbb][y][se_rect.left],
91 memset16(&se_mat[bg_sbb][end][se_rect.left], 0x0000,
rect_width(&se_rect));
95static void main_bg_se_copy_or_move_rect_1_tile_vert(
97 enum ScreenVertDir direction,
101 bg_se_copy_or_move_rect_1_tile_vert(MAIN_BG_SBB, se_rect, direction, move);
106 bg_se_copy_or_move_rect_1_tile_vert(MAIN_BG_SBB, se_rect, direction,
false);
109void bg_se_move_rect_1_tile_vert(u16 bg_sbb, Rect se_rect,
enum ScreenVertDir direction)
111 bg_se_copy_or_move_rect_1_tile_vert(MAIN_BG_SBB, se_rect, direction,
true);
116 main_bg_se_copy_or_move_rect_1_tile_vert(se_rect, direction,
false);
121 main_bg_se_copy_or_move_rect_1_tile_vert(se_rect, direction,
true);
126 if (se_rect.left > se_rect.right || se_rect.top > se_rect.bottom)
130 clip_se_rect_to_screenblock(&se_rect);
134 SE tile_map[height][width];
137 for (
int sy = 0; sy < height; sy++)
139 memcpy16(&tile_map[sy][0], &se_mat[MAIN_BG_SBB][se_rect.top + sy][se_rect.left], width);
144 for (
int sy = 0; sy < height; sy++)
146 memcpy16(&se_mat[MAIN_BG_SBB][dest_pos.y + sy][dest_pos.x], &tile_map[sy][0], width);
150static inline void main_bg_se_fill_rect_with_se(SE se, Rect se_rect)
152 if (se_rect.left > se_rect.right || se_rect.top > se_rect.bottom)
156 clip_se_rect_to_screenblock(&se_rect);
161 for (
int sy = 0; sy < height; sy++)
163 memset16(&se_mat[MAIN_BG_SBB][se_rect.top + sy][se_rect.left], se, width);
169 main_bg_se_fill_rect_with_se(se_mat[MAIN_BG_SBB][se_tile_src.y][se_tile_src.x], se_rect_dest);
181 src_top_left_pnt.x + 2,
182 src_top_left_pnt.y + 2
184 .margins = {1, 1, 1, 1}
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
196 SE middle_left_se = se_mat[MAIN_BG_SBB][src_left_pnt->y][src_left_pnt->x];
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);
201 for (
int y = 0; y < dest_rect_height; y++)
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] =
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)
217 main_bg_se_3w_copy_expand_left_right_sides(&se_dest_rect, &src_row_left_pnt);
219 if (dest_rect_width > 2)
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;
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);
232static inline void main_bg_se_expand_9_patch_copy_corners(
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
245 BG_POINT top_right_dest_pos = {
246 se_dest_rect.right - src_9_ptch->margins.right + 1,
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
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
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
267 BG_POINT bottom_left_dest_pos = {
269 se_dest_rect.bottom - src_9_ptch->margins.bottom + 1
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
285static inline void main_bg_se_expand_9_patch_stretch_sides(
288 int top_bottom_width,
289 int left_right_height
295 for (
int i = 0; i < src_9_ptch->margins.top; i++)
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];
300 &se_mat[MAIN_BG_SBB][se_dest_rect.top + i]
301 [se_dest_rect.left + src_9_ptch->margins.left],
308 for (
int i = 0; i < src_9_ptch->margins.bottom; i++)
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];
313 &se_mat[MAIN_BG_SBB][se_dest_rect.bottom - i]
314 [se_dest_rect.left + src_9_ptch->margins.left],
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
329 BG_POINT left_side_tiles_dest_pos = {
331 se_dest_rect.top + src_9_ptch->margins.top
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
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
345 for (
int i = 0; i < left_right_height; i++)
348 left_side_tiles_dest_pos.y++;
351 right_side_tiles_dest_pos.y++;
357 clip_se_rect_to_screenblock(&se_dest_rect);
359 int dest_rect_width =
rect_width(&se_dest_rect);
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;
366 if (dest_rect_width < src_9ptch_min_width || dest_rect_height < src_9ptch_min_height)
370 main_bg_se_expand_9_patch_copy_corners(se_dest_rect, src_9_ptch);
373 if (dest_rect_width > src_9ptch_min_width && dest_rect_height > src_9ptch_min_height)
376 main_bg_se_expand_9_patch_stretch_sides(
379 dest_rect_width - src_9ptch_min_width,
380 dest_rect_height - src_9ptch_min_height
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
393 main_bg_se_fill_rect_with_se(middle_fill_se, dest_inner_fill_rect);
399 tte_erase_rect(rect.left, rect.top, rect.right, rect.bottom);
405 enum OverflowDir overflow_direction
409 int str_len = strlen(str);
410 if (overflow_direction == OVERFLOW_LEFT)
414 else if (overflow_direction == OVERFLOW_RIGHT)
417 if (str_len < num_fitting_chars)
425 if (rect == NULL || str == NULL)
428 int text_width_chars = strlen(str);
431 bool bias_right = (bias_direction == SCREEN_RIGHT);
436 rect->left += max(0, (rect_width_chars - text_width_chars + bias_right) / 2) *
TTE_CHAR_SIZE;
441 const u16 offset = (((palette_offset) << 8) | (palette_offset));
442 for (
int i = 0; i < hwcount; i++)
445 dst[i] = src[i] + offset;
452 (palette_offset << 24) | (palette_offset << 16) | (palette_offset << 8) | palette_offset;
453 for (
int i = 0; i < wcount; i++)
456 dst[i] = src[i] + offset;
464 REG_DISPCNT |= DCNT_WIN0;
468 REG_DISPCNT &= ~DCNT_WIN0;
473 REG_DISPCNT |= DCNT_WIN1;
477 REG_DISPCNT &= ~DCNT_WIN1;
482 REG_BLDCNT = BLD_BUILD(BLD_BG1, BLD_BG2, 1);
492 if (se_rect.left > se_rect.right)
495 clip_se_rect_to_screenblock(&se_rect);
497 for (
int y = se_rect.top; y < se_rect.bottom; y++)
499 memset16(&(se_mat[MAIN_BG_SBB][y][se_rect.left]), 0x0000,
rect_width(&se_rect));
504#define MAX_LINE_TEXT_LENGTH 30
507 const char* raw_text,
509 enum TextJustifyFlag justify_direction,
510 enum ScreenHorzDir bias_direction,
516 int raw_text_len = strlen(raw_text);
524 int max_line_text_len =
rect_width(&dst_rect);
526 int line_text_len = 0;
527 int token_text_len = 0;
533 while (line_start < raw_text_len)
540 while (line_text_len <= max_line_text_len && token_start < raw_text_len)
542 int current_char = token_start + token_len;
545 if (raw_text[current_char] ==
'#' && (current_char + 1) < raw_text_len &&
546 raw_text[current_char + 1] ==
'{')
548 while (current_char < raw_text_len && raw_text[current_char] !=
'}')
554 if (current_char < raw_text_len)
564 if (raw_text[current_char] ==
' ' || raw_text[current_char] ==
'\0')
566 token_start = current_char + 1;
573 else if (raw_text[current_char] ==
'\n')
575 token_start = current_char + 1;
593 int line_len = token_start - line_start;
598 char c = raw_text[line_start + line_len - 1];
599 if (c ==
' ' || c ==
'\n' || c ==
'\0')
606 line_text_len = line_text_len - token_text_len - 1;
607 if (line_text_len < 0)
616 if (justify_direction == JUSTIFY_CENTER)
618 line_x = (max_line_text_len - line_text_len) / 2;
626 raw_text + line_start
630 line_start = token_start;
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.