Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions samples/text_drawing.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "cute_draw.h"
#include <cute.h>
using namespace Cute;

Expand All @@ -21,7 +22,7 @@ static void draw_text_boxed(const char* text, v2 pos, int len = -1)

int main(int argc, char* argv[])
{
make_app("Text Drawing", 0, 0, 0, 960, 700, CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT | CF_APP_OPTIONS_RESIZABLE_BIT, argv[0]);
make_app("Text Drawing", 0, 0, 0, 1280, 900, CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT | CF_APP_OPTIONS_RESIZABLE_BIT, argv[0]);

draw_push_shape_aa(1.5f);
make_font_from_memory(proggy_data, proggy_sz, "ProggyClean");
Expand Down Expand Up @@ -108,31 +109,47 @@ int main(int argc, char* argv[])


float gx = 130;
float gy = -60;
float step = 30;

// Horizontal: left-to-right red to blue.
push_font_size(30);
draw_text_boxed("<gradient left=#ff0000 right=#0055ff>Left-to-right gradient!</gradient>", V2(gx, -80));
push_font_size(26);
draw_text_boxed("<gradient left=#ff0000 right=#0055ff>Left-to-right gradient!</gradient>", V2(gx, gy));

// Vertical: top gold to bottom purple.
push_font_size(30);
draw_text_boxed("<gradient top=#ffd700 bottom=#8b00ff>Top-to-bottom gradient!</gradient>", V2(gx, -120));
draw_text_boxed("<gradient top=#ffd700 bottom=#8b00ff>Top-to-bottom gradient!</gradient>", V2(gx, gy - step));

// Corners: direct per-corner control.
push_font_size(30);
draw_text_boxed("<gradient topleft=#ff0000 topright=#00ff00 bottomright=#0000ff bottomleft=#ffff00>Per-corner colors</gradient>", V2(gx, -160));
draw_text_boxed("<gradient topleft=#ff0000 topright=#00ff00 bottomright=#0000ff bottomleft=#ffff00>Per-corner colors</gradient>", V2(gx, gy - step*2));

// Mix: edge + corner override.
push_font_size(30);
draw_text_boxed("<gradient left=#ff0000 right=#0000ff topleft=#00ff00>Edge + corner override</gradient>", V2(gx, -200));
draw_text_boxed("<gradient left=#ff0000 right=#0000ff topleft=#00ff00>Edge + corner override</gradient>", V2(gx, gy - step*3));

// Single edge: fades from red to the glyph's current color.
push_font_size(30);
draw_text_boxed("<gradient left=#ff0000>Fade from one color</gradient>", V2(gx, -240));
draw_text_boxed("<gradient left=#ff0000>Fade from one color</gradient>", V2(gx, gy - step*4));

// Short strings: gradient should still work on 1-2 glyphs.
push_font_size(40);
draw_text_boxed("<gradient left=#ff0000 right=#0000ff>AB</gradient>", V2(gx, -280));
draw_text_boxed("<gradient top=#ff0000 bottom=#0000ff>X</gradient>", V2(gx + 80, -280));
push_font_size(30);
draw_text_boxed("<gradient left=#ff0000 right=#0000ff>AB</gradient>", V2(gx, gy - step*5));
draw_text_boxed("<gradient top=#ff0000 bottom=#0000ff>X</gradient>", V2(gx + 80, gy - step*5));

// Composited text effects.
push_font_size(26);
draw_text_boxed("<wave><fade>wave + fade</fade></wave>", V2(gx, gy - step*6));
draw_text_boxed("<shake freq=35 x=2 y=2><gradient left=#ff0000 right=#0000ff>shake + gradient</gradient></shake>", V2(gx, gy - step*7));

cf_push_text_id(42);
push_font_blur(10);
draw_push_color(color_black());
draw_text_boxed("<wave><shake freq=30 x=1 y=1><fade><strike>every effect at once!</strike></fade></shake></wave>", V2(gx, gy - step*8));
draw_pop_color();
pop_font_blur();
push_font_blur(5);
draw_text_boxed("<wave><shake freq=30 x=1 y=1><fade><strike><gradient left=#ff0000 right=#0000ff>every effect at once!</gradient></strike></fade></shake></wave>", V2(gx, gy - step*8));
draw_pop_color();
pop_font_blur();
draw_text_boxed("<wave><shake freq=30 x=1 y=1><fade><strike><gradient left=#ff0000 right=#0000ff>every effect at once!</gradient></strike></fade></shake></wave>", V2(gx, gy - step*8));
cf_pop_text_id();

cf_push_text_id(1);
{
Expand All @@ -151,6 +168,13 @@ int main(int argc, char* argv[])
}
cf_pop_text_id();

// Strikethrough on proportional font (tests contiguous segments).
push_font("Calibri");
push_font_size(30);
draw_text_boxed("<strike>Strikethrough on proportional font</strike>", V2(gx, gy - step*9));
draw_text_boxed("<wave><strike>wavy strike on proportional font</strike></wave>", V2(gx, gy - step*10));
pop_font_size();

// Instructions
const char* instructions = "Press Space to toggle bounding boxes";
v2 size = text_size(instructions);
Expand Down
41 changes: 32 additions & 9 deletions src/cute_draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2460,14 +2460,9 @@ static bool s_text_fx_strike(CF_TextEffect* fx_ptr)
{
TextEffect* fx = (TextEffect*)fx_ptr;
if (!s_is_space(fx->character) || fx->character == ' ') {
v2 hw = V2((float)fx->xadvance, 0) * 0.5f;
float h = fx->font_size / 20.0f;
h = (float)fx->get_number("strike", (double)h);
CF_Strike strike;
strike.p0 = fx->center - hw;
strike.p1 = fx->center + hw;
strike.thickness = h;
s_draw->strikes.add(strike);
fx->strike_thickness = h;
}
return true;
}
Expand Down Expand Up @@ -2534,7 +2529,7 @@ static void s_parse_codes(CF_ParsedTextState* text_state, const char* text)
s->append(cp);
}
}
std::sort(text_state->codes.begin(), text_state->codes.end(),
std::stable_sort(text_state->codes.begin(), text_state->codes.end(),
[](const CF_TextCode& a, const CF_TextCode&b) {
return a.index_in_string < b.index_in_string;
}
Expand Down Expand Up @@ -2567,8 +2562,10 @@ static v2 s_draw_text(const char* text, CF_V2 position, int text_length, bool re
}

if (render || markups) {
if (!effect_state->alive) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand.

Is this included so that the elapsed time is only ticked once per frame?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's exactly right. Without it, an effect's elapsed time was being incremented multiple times per frame. I can't remember if this was because of the while loop I introduced to support compositing effects, or if it happened after I ran multiple effects with the same text_id via cf_draw_push_text_id

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're looking for something more explicit I could introduce another bool, but, this one seemed to have the same lifetime semantics that I needed.

effect_state->elapsed += CF_DELTA_TIME;
}
effect_state->alive = true;
effect_state->elapsed += CF_DELTA_TIME;
text_state->alive = true;
}

Expand Down Expand Up @@ -2608,7 +2605,7 @@ static v2 s_draw_text(const char* text, CF_V2 position, int text_length, bool re

// Called whenever text-effects need to be spawned, before going to the next glyph.
auto effect_spawn = [&]() {
if (code_index < text_state->codes.count()) {
while (code_index < text_state->codes.count()) {
CF_TextCode* code = text_state->codes + code_index;
if (index == code->index_in_string) {
Comment thread
waldnercharles marked this conversation as resolved.
++code_index;
Expand All @@ -2621,6 +2618,8 @@ static v2 s_draw_text(const char* text, CF_V2 position, int text_length, bool re
effect.params = &code->params;
effect.fn = code->fn;
text_state->effects.add(effect);
} else {
break;
}
}
};
Expand Down Expand Up @@ -2764,6 +2763,8 @@ static v2 s_draw_text(const char* text, CF_V2 position, int text_length, bool re

// Apply any active custom text effects.
bool use_corner_colors = false;
CF_Color corner_colors[4] = {};
float pre_fx_q0_y = q0.y;
for (int i = 0; i < text_state->effects.count();) {
TextEffect* effect = text_state->effects + i;
CF_TextEffectFn* fn = effect->fn;
Expand All @@ -2790,6 +2791,7 @@ static v2 s_draw_text(const char* text, CF_V2 position, int text_length, bool re
color = effect->color;
if (effect->use_colors) {
use_corner_colors = true;
for (int j = 0; j < 4; ++j) corner_colors[j] = effect->colors[j];
s.geom.text_colors[0] = premultiply(to_pixel(effect->colors[0]));
s.geom.text_colors[1] = premultiply(to_pixel(effect->colors[1]));
s.geom.text_colors[2] = premultiply(to_pixel(effect->colors[2]));
Expand Down Expand Up @@ -2823,6 +2825,24 @@ static v2 s_draw_text(const char* text, CF_V2 position, int text_length, bool re
}
}

// Collect deferred strikes using final position/color.
for (int i = 0; i < text_state->effects.count(); ++i) {
TextEffect* effect = text_state->effects + i;
if (effect->strike_thickness > 0) {
float y_mid = y + h * 0.25f + (q0.y - pre_fx_q0_y);
CF_Strike strike;
strike.p0 = V2(x, y_mid);
strike.p1 = V2(x + xadvance, y_mid);
strike.thickness = effect->strike_thickness;
strike.color = use_corner_colors
? cf_color_lerp(cf_color_lerp(corner_colors[0], corner_colors[1], 0.5f), cf_color_lerp(corner_colors[3], corner_colors[2], 0.5f), 0.5f)
: color;
strike.color.a *= s.geom.alpha;
s_draw->strikes.add(strike);
effect->strike_thickness = 0;
}
}

// Actually render the sprite.
if (visible && render) {
CF_M3x2 m = s_draw->mvp;
Expand Down Expand Up @@ -2854,7 +2874,10 @@ static v2 s_draw_text(const char* text, CF_V2 position, int text_length, bool re
v2 p0 = s_draw->strikes[i].p0;
v2 p1 = s_draw->strikes[i].p1;
float thickness = s_draw->strikes[i].thickness;
CF_Color c = s_draw->strikes[i].color;
draw_push_color(c);
cf_draw_line(p0, p1, thickness);
draw_pop_color();
}
}
s_draw->strikes.clear();
Expand Down
1 change: 1 addition & 0 deletions src/internal/cute_draw_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ struct CF_Strike
{
CF_V2 p0, p1;
float thickness;
CF_Color color;
};

struct CF_DrawUniform
Expand Down
1 change: 1 addition & 0 deletions src/internal/cute_font_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ struct TextEffect : public CF_TextEffect
int initial_index;
const Cute::Map<CF_TextCodeVal>* params;
CF_TextEffectFn* fn;
float strike_thickness = 0;
bool line_bound_init = false;
CF_Aabb line_bound;
Cute::Array<CF_Aabb> bounds;
Expand Down
Loading