rich text: Optionally return cursor position from dry run & allow indenting text
Some checks failed
Documentation / Generate website (push) Has been cancelled
Documentation / Publish website with GitHub pages (push) Has been cancelled

The goal of the changes made in this PR is to create something with this
effect ("Jonko" is in in green, the rest is in white):

    "Dear god." Jonko quickly replied,
    crestfallen.

In order to do this, I need to call `NE_RichTextRender3D` or
`NE_RichTextRender3DAlpha` three times: once to draw the first part of
the sentence with the white font, once to draw "Jonko" in turquoise, and
once to draw the remainder of the sentence in white again. In order to
ensure that text is being drawn in the correct location, I need the
position where the previous call stopped drawing, which leads to the
first change this PR makes: I have added a version of
`DSF_StringRenderDryRun` which accepts pointers to `final_x` and
`final_y` and stores the last position of `font->pointer_x` and
`font->pointer_y` in them and I have added a new variant of
`NE_RichTextRenderDryRun` called `NE_RichTextRenderDryRunWithPos` which
uses that DSF function and has analogous parameters.

These functions get us the position where we need to place the next
cursor, but the next issue is that currently NitroEngine and libDSF only
allow for specifying the position of the overall box to draw text in,
but not where to offset the cursor in that box. This leads to the
following problem ("Jonko" is in in green, the rest is in white):

    "Dear god." Jonko quickly replied,
                        crestfallen.

Thus, the second change: I have introduced new versions of
`DSF_StringRender3D` and `DSF_StringRender3DAlpha` which accept an
`xStart` position that is added to the `pointerX`. Correspondingly, I
have added versions of `NE_RichTextRender3D` and
`NE_RichTextRender3DAlpha` that use these new functions as well.

Let me know if there are any issues with how I've written this (C is not
my forte, after all), but the first screenshot is this being tested in
production code.
This commit is contained in:
jonko0493 2025-06-06 16:08:02 +09:00 committed by Antonio Niño Díaz
parent 86955bc0a0
commit 8932de1fff
4 changed files with 211 additions and 41 deletions

View File

@ -166,6 +166,19 @@ int NE_RichTextBitmapSet(u32 slot, const void *texture_buffer,
NE_TextureFormat texture_fmt,
const void *palette_buffer, size_t palette_size);
/// Calculates the final size and cursor position of rendering the provided string.
///More actions
/// @param slot The slot to use.
/// @param str The string to render.
/// @param size_x Pointer to store the width of the resulting image.
/// @param size_y Pointer to store the height of the resulting image.
/// @param final_x Pointer to store the final X position of the cursor.
/// @param final_y Pointer to store the final Y position of the cursor.
/// @return Returns 1 on success, 0 on failure.
int NE_RichTextRenderDryRunWithPos(u32 slot, const char *str,
size_t *size_x, size_t *size_y,
size_t *final_x, size_t *final_y);
/// Calculates the final size of rendering the provided string.
///
/// @param slot The slot to use.
@ -176,6 +189,19 @@ int NE_RichTextBitmapSet(u32 slot, const void *texture_buffer,
int NE_RichTextRenderDryRun(u32 slot, const char *str,
size_t *size_x, size_t *size_y);
/// Render a string by rendering one 3D quad per codepoint.
///
/// This preserves the polygon format that is currently active.
///
/// @param slot The slot to use.
/// @param str The string to render.
/// @param x The left coordinate of the text.
/// @param y The top coordinate of the text.
/// @param xIndent The horizontal indentation to apply to the first line of text.
/// @return Returns 1 on success, 0 on failure.
int NE_RichTextRender3DWithIndent(u32 slot, const char *str, s32 x, s32 y,
s32 xIndent);
/// Render a string by rendering one 3D quad per codepoint.
///
/// This preserves the polygon format that is currently active.
@ -187,6 +213,48 @@ int NE_RichTextRenderDryRun(u32 slot, const char *str,
/// @return Returns 1 on success, 0 on failure.
int NE_RichTextRender3D(u32 slot, const char *str, s32 x, s32 y);
/// Render a string by rendering one 3D quad per codepoint.More actions
///
/// This preserves the polygon format that is currently active.
///
/// @param slot The slot to use.
/// @param str The string to render.
/// @param x The left coordinate of the text.
/// @param y The top coordinate of the text.
/// @param xIndent The horizontal indentation to apply to the first line of text.
/// @return Returns 1 on success, 0 on failure.
int NE_RichTextRender3DWithIndent(u32 slot, const char *str, s32 x, s32 y,
s32 xIndent);
/// Render a string by rendering one 3D quad per codepoint.
///
/// This preserves the polygon format that is currently active.
/// @return Returns 1 on success, 0 on failure.
int NE_RichTextRender3D(u32 slot, const char *str, s32 x, s32 y);
/// Render a string by rendering one 3D quad per codepoint with alternating
/// polygon IDs.
///
/// This function will alternate between polygon IDs so that alpha blending
/// works between multiple polygons when they overlap. This is a requirement of
/// the NDS 3D hardware.
///
/// It is required to pass the base polygon format as a parameter because the
/// polygon format data is write-only. Whenever the polygon ID needs to be
/// changed, the rest of the polygon format flags need to be set as well.
///
/// @param slot The slot to use.
/// @param str The string to render.
/// @param x The left coordinate of the text.
/// @param y The top coordinate of the text.
/// @param poly_fmt The polygon format values to be used for the quads.
/// @param poly_id_base The base polygon ID to use for the quads.
/// @param xIndent The horizontal indentation to apply to the first line of text.
/// @return Returns 1 on success, 0 on failure.
int NE_RichTextRender3DAlphaWithIndent(u32 slot, const char *str, s32 x, s32 y,
uint32_t poly_fmt, int poly_id_base,
s32 xIndent);
/// Render a string by rendering one 3D quad per codepoint with alternating
/// polygon IDs.
///

View File

@ -359,12 +359,15 @@ int NE_RichTextBitmapSet(u32 slot, const void *texture_buffer,
return 1;
}
int NE_RichTextRenderDryRun(u32 slot, const char *str,
size_t *size_x, size_t *size_y)
int NE_RichTextRenderDryRunWithPos(u32 slot, const char *str,
size_t *size_x, size_t *size_y,
size_t *final_x, size_t *final_y)
{
NE_AssertPointer(str, "NULL str pointer");
NE_AssertPointer(size_x, "NULL size X pointer");
NE_AssertPointer(size_y, "NULL size Y pointer");
NE_AssertPointer(final_x, "NULL final X pointer");
NE_AssertPointer(final_y, "NULL final Y pointer");
if (slot >= NE_NumRichTextSlots)
return 0;
@ -373,15 +376,24 @@ int NE_RichTextRenderDryRun(u32 slot, const char *str,
if (!info->active)
return 0;
dsf_error err = DSF_StringRenderDryRun(info->handle, str,
size_x, size_y);
dsf_error err = DSF_StringRenderDryRunWithCursor(info->handle, str,
size_x, size_y,
final_x, final_y);
if (err != DSF_NO_ERROR)
return 0;
return 1;
}
int NE_RichTextRender3D(u32 slot, const char *str, s32 x, s32 y)
int NE_RichTextRenderDryRun(u32 slot, const char *str,
size_t *size_x, size_t *size_y)
{
size_t final_x, final_y;
return NE_RichTextRenderDryRunWithPos(slot, str, size_x, size_y, &final_x, &final_y);
}
int NE_RichTextRender3DWithIndent(u32 slot, const char *str, s32 x, s32 y,
s32 xIndent)
{
NE_AssertPointer(str, "NULL str pointer");
@ -394,8 +406,37 @@ int NE_RichTextRender3D(u32 slot, const char *str, s32 x, s32 y)
NE_MaterialUse(info->material);
dsf_error err = DSF_StringRender3D(info->handle, str, x, y,
NE_RICH_TEXT_PRIORITY);
dsf_error err = DSF_StringRender3DWithIndent(info->handle, str, x, y,
NE_RICH_TEXT_PRIORITY, xIndent);
if (err != DSF_NO_ERROR)
return 0;
return 1;
}
int NE_RichTextRender3D(u32 slot, const char *str, s32 x, s32 y)
{
return NE_RichTextRender3DWithIndent(slot, str, x, y, 0);
}
int NE_RichTextRender3DAlphaWithIndent(u32 slot, const char *str, s32 x, s32 y,
uint32_t poly_fmt, int poly_id_base,
s32 xIndent)
{
NE_AssertPointer(str, "NULL str pointer");
if (slot >= NE_NumRichTextSlots)
return 0;
ne_rich_textinfo_t *info = &NE_RichTextInfo[slot];
if (!info->active)
return 0;
NE_MaterialUse(info->material);
dsf_error err = DSF_StringRender3DAlphaWithIndent(info->handle, str, x, y,
NE_RICH_TEXT_PRIORITY,
poly_fmt, poly_id_base, xIndent);
if (err != DSF_NO_ERROR)
return 0;
@ -405,24 +446,7 @@ int NE_RichTextRender3D(u32 slot, const char *str, s32 x, s32 y)
int NE_RichTextRender3DAlpha(u32 slot, const char *str, s32 x, s32 y,
uint32_t poly_fmt, int poly_id_base)
{
NE_AssertPointer(str, "NULL str pointer");
if (slot >= NE_NumRichTextSlots)
return 0;
ne_rich_textinfo_t *info = &NE_RichTextInfo[slot];
if (!info->active)
return 0;
NE_MaterialUse(info->material);
dsf_error err = DSF_StringRender3DAlpha(info->handle, str, x, y,
NE_RICH_TEXT_PRIORITY,
poly_fmt, poly_id_base);
if (err != DSF_NO_ERROR)
return 0;
return 1;
return NE_RichTextRender3DAlphaWithIndent(slot, str, x, y, poly_fmt, poly_id_base, 0);
}
int NE_RichTextRenderMaterial(u32 slot, const char *str, NE_Material **mat,

View File

@ -537,23 +537,27 @@ static dsf_error DSF_CodepointRender3D(dsf_handle handle, uint32_t codepoint,
return DSF_NO_ERROR;
}
dsf_error DSF_StringRenderDryRun(dsf_handle handle, const char *str,
size_t *size_x, size_t *size_y)
dsf_error DSF_StringRenderDryRunWithCursor(dsf_handle handle, const char *str,
size_t *size_x, size_t *size_y,
size_t *final_x, size_t *final_y)
{
if ((handle == 0) || (str == NULL) || (size_x == NULL) || (size_y == NULL))
if ((handle == 0) || (str == NULL) || (size_x == NULL) || (size_y == NULL) ||
(final_x == NULL) || (final_y == NULL))
return DSF_INVALID_ARGUMENT;
dsf_font_internal_state *font = (dsf_font_internal_state *)handle;
if (strlen(str) == 0)
{
*size_x = 0;
*size_y = 0;
*final_x = font->pointer_x;
*final_y = font->pointer_y;
return DSF_NO_ERROR;
}
dsf_error ret = DSF_NO_ERROR;
dsf_font_internal_state *font = (dsf_font_internal_state *)handle;
font->pointer_x = 0;
font->pointer_y = 0;
font->box_left = 0;
@ -587,8 +591,16 @@ dsf_error DSF_StringRenderDryRun(dsf_handle handle, const char *str,
return ret;
}
dsf_error DSF_StringRender3D(dsf_handle handle, const char *str,
int32_t x, int32_t y, int32_t z)
dsf_error DSF_StringRenderDryRun(dsf_handle handle, const char *str,
size_t *size_x, size_t *size_y)
{
size_t final_x, final_y;
return DSF_StringRenderDryRunWithCursor(handle, str, size_x, size_y, &final_x, &final_y);
}
dsf_error DSF_StringRender3DWithIndent(dsf_handle handle, const char *str,
int32_t x, int32_t y, int32_t z,
int32_t xStart)
{
if ((handle == 0) || (str == NULL))
return DSF_INVALID_ARGUMENT;
@ -597,7 +609,7 @@ dsf_error DSF_StringRender3D(dsf_handle handle, const char *str,
dsf_font_internal_state *font = (dsf_font_internal_state *)handle;
font->pointer_x = x;
font->pointer_x = x + xStart;
font->pointer_y = y;
font->box_left = x;
font->box_top = y;
@ -623,9 +635,16 @@ dsf_error DSF_StringRender3D(dsf_handle handle, const char *str,
return ret;
}
dsf_error DSF_StringRender3DAlpha(dsf_handle handle, const char *str,
int32_t x, int32_t y, int32_t z,
uint32_t poly_fmt, int poly_id_base)
dsf_error DSF_StringRender3D(dsf_handle handle, const char *str,
int32_t x, int32_t y, int32_t z)
{
return DSF_StringRender3DWithIndent(handle, str, x, y, z, 0);
}
dsf_error DSF_StringRender3DAlphaWithIndent(dsf_handle handle, const char *str,
int32_t x, int32_t y, int32_t z,
uint32_t poly_fmt, int poly_id_base,
int32_t xStart)
{
if ((handle == 0) || (str == NULL))
return DSF_INVALID_ARGUMENT;
@ -634,7 +653,7 @@ dsf_error DSF_StringRender3DAlpha(dsf_handle handle, const char *str,
dsf_font_internal_state *font = (dsf_font_internal_state *)handle;
font->pointer_x = x;
font->pointer_x = x + xStart;
font->pointer_y = y;
font->box_left = x;
font->box_top = y;
@ -663,6 +682,13 @@ dsf_error DSF_StringRender3DAlpha(dsf_handle handle, const char *str,
return ret;
}
dsf_error DSF_StringRender3DAlpha(dsf_handle handle, const char *str,
int32_t x, int32_t y, int32_t z,
uint32_t poly_fmt, int poly_id_base)
{
return DSF_StringRender3DAlphaWithIndent(handle, str, x, y, z, poly_fmt, poly_id_base, 0);
}
static dsf_error DSF_CodepointRenderBuffer(dsf_handle handle,
uint32_t codepoint, unsigned int texture_fmt,
const void *font_texture, size_t font_width, size_t font_height,

View File

@ -93,16 +93,42 @@ dsf_error DSF_FreeFont(dsf_handle *handle);
/// @defgroup libdsf_render Functions to draw text strings.
/// @{
/// @param handle Handler of the font to use.
/// @param str String to print.
/// @param size_x Pointer to a variable to store the size.
/// @param size_y Pointer to a variable to store the size.
/// @param final_x Pointer to a variable to store the final X cursor position.
/// @param final_y Pointer to a variable to store the final Y cursor position.
///
/// @return An error code or DSF_NO_ERROR on success.
dsf_error DSF_StringRenderDryRunWithCursor(dsf_handle handle, const char *str,
size_t *size_x, size_t *size_y,
size_t *final_x, size_t *final_y);
/// Pretend to render a string to calculate its final size once rendered.
///
/// @param handle Handler of the font to use.
/// @param str String to print.
/// @param size_x Pointer to a variable to store the size.
/// @param size_y Pointer to a variable to store the size.
/// @param handle Handler of the font to use.
/// @param str String to print.
/// @param size_x Pointer to a variable to store the size.
/// @param size_y Pointer to a variable to store the size.
///
/// @return An error code or DSF_NO_ERROR on success.
dsf_error DSF_StringRenderDryRun(dsf_handle handle, const char *str,
size_t *size_x, size_t *size_y);
/// Render a string by rendering one 3D quad per codepoint.
///
/// @param handle Handler of the font to use.
/// @param str String to print.
/// @param x Top x coordinate (0 to 255, but you can go outside of that).
/// @param y Left y coordinate (0 to 191, but you can go outside of that).
/// @param z Z coordinate (depth).
/// @param xStart The horizontal component of the cursor's starting offset (reset to x on line break)
///
/// @return An error code or DSF_NO_ERROR on success.
dsf_error DSF_StringRender3DWithIndent(dsf_handle handle, const char *str,
int32_t x, int32_t y, int32_t z,
int32_t xStart);
/// Render a string by rendering one 3D quad per codepoint.
///
@ -116,6 +142,32 @@ dsf_error DSF_StringRenderDryRun(dsf_handle handle, const char *str,
dsf_error DSF_StringRender3D(dsf_handle handle, const char *str,
int32_t x, int32_t y, int32_t z);
/// Render a string by rendering one 3D quad per codepoint with alternating
/// polygon IDs.
///
/// This function will alternate between polygon IDs so that alpha blending
/// works between multiple polygons when they overlap. This is a requirement of
/// the NDS 3D hardware.
///
/// It is required to pass the base polygon format as a parameter because the
/// polygon format data is write-only. Whenever the polygon ID needs to be
/// changed, the rest of the polygon format flags need to be set as well.
///
/// @param handle Handler of the font to use.
/// @param str String to print.
/// @param x Top x coordinate (0 to 255, but you can go outside of that).
/// @param y Left y coordinate (0 to 191, but you can go outside of that).
/// @param z Z coordinate (depth).
/// @param poly_fmt Polygon formats to apply to the characters.
/// @param poly_id_base poly_id_base and poly_id_base + 1 will be used.
/// @param xStart The horizontal component of the cursor's starting offset (reset to x on line break)
///
/// @return An error code or DSF_NO_ERROR on success.
dsf_error DSF_StringRender3DAlphaWithIndent(dsf_handle handle, const char *str,
int32_t x, int32_t y, int32_t z,
uint32_t poly_fmt, int poly_id_base,
int32_t xStart);
/// Render a string by rendering one 3D quad per codepoint with alternating
/// polygon IDs.
///