VSE: Add Box Roundness option to text strips

The background box for VSE text strips can have rounded corners now.

Actual rounded shape is a superellipse with 2.1 exponent; this is
very close to a circle section but feels a bit nicer with more
continuity between the flat part and the rounded part of the box.

At very large rounding radius this is not very fast; optimization
for that case will come in a separate commit.

Pull Request: https://projects.blender.org/blender/blender/pulls/129665
This commit is contained in:
kitt
2024-11-17 12:07:16 +01:00
committed by Aras Pranckevicius
parent b4fc5754fd
commit 140ff12eae
4 changed files with 116 additions and 13 deletions

View File

@@ -1725,6 +1725,11 @@ class SEQUENCER_PT_effect_text_style(SequencerButtonsPanel, Panel):
sub.prop(strip, "box_margin")
sub.active = strip.use_box and (not strip.mute)
row = layout.row(align=True, heading="Box Roundness")
sub = row.row(align=True)
sub.prop(strip, "box_roundness")
sub.active = strip.use_box and (not strip.mute)
class SEQUENCER_PT_source(SequencerButtonsPanel, Panel):
bl_label = "Source"

View File

@@ -446,6 +446,7 @@ typedef struct TextVars {
float loc[2];
float wrap_width;
float box_margin;
float box_roundness;
float shadow_angle;
float shadow_offset;
float shadow_blur;
@@ -454,7 +455,7 @@ typedef struct TextVars {
char align;
char align_y DNA_DEPRECATED /* Only used for versioning. */;
char anchor_x, anchor_y;
char _pad[3];
char _pad[7];
} TextVars;
/** #TextVars.flag */

View File

@@ -3437,6 +3437,12 @@ static void rna_def_text(StructRNA *srna)
RNA_def_property_float_default(prop, 0.01f);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "box_roundness", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, nullptr, "box_roundness");
RNA_def_property_ui_text(prop, "Box Roundness", "Box corner radius as a factor of box height");
RNA_def_property_range(prop, 0, 1.0);
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "alignment_x", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "align");
RNA_def_property_enum_items(prop, text_alignment_x_items);

View File

@@ -2528,6 +2528,7 @@ static void init_text_effect(Sequence *seq)
data->box_color[2] = 0.2f;
data->box_color[3] = 0.7f;
data->box_margin = 0.01f;
data->box_roundness = 0.0f;
data->outline_color[3] = 0.7f;
data->outline_width = 0.05f;
@@ -3037,10 +3038,42 @@ static rcti draw_text_outline(const SeqRenderData *context,
return outline_rect;
}
static inline void fill_ellipse_alpha_under(const ImBuf *ibuf,
const float col[4],
int x1,
int y1,
int x2,
int y2,
float origin_x,
float origin_y,
float radius)
{
float curve_pow = 2.1f;
float4 color;
float4 premul_color;
for (int y = y1; y < y2; y++) {
uchar *dst = ibuf->byte_buffer.data + (size_t(ibuf->x) * y + x1) * 4;
for (int x = x1; x < x2; x++) {
color = col;
float r = powf(powf(abs(x - origin_x), curve_pow) + powf(abs(y - origin_y), curve_pow),
1.0f / curve_pow);
color.w = math::clamp(radius - r, 0.0f, color.w);
straight_to_premul_v4_v4(premul_color, color);
float4 pix = load_premul_pixel(dst);
float fac = 1.0f - pix.w;
float4 dst_fl = fac * premul_color + pix;
store_premul_pixel(dst_fl, dst);
dst += 4;
}
}
}
/* Similar to #IMB_rectfill_area but blends the given color under the
* existing image. Also only works on byte buffers. */
static void fill_rect_alpha_under(
const ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2)
const ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2, float corner_radius)
{
const int width = ibuf->x;
const int height = ibuf->y;
@@ -3058,17 +3091,74 @@ static void fill_rect_alpha_under(
return;
}
float4 premul_col = col;
straight_to_premul_v4(premul_col);
corner_radius = math::clamp(corner_radius, 0.0f, math::min(x2 - x1, y2 - y1) / 2.0f);
for (int y = y1; y < y2; y++) {
uchar *dst = ibuf->byte_buffer.data + (size_t(width) * y + x1) * 4;
for (int x = x1; x < x2; x++) {
float4 pix = load_premul_pixel(dst);
float fac = 1.0f - pix.w;
float4 dst_fl = fac * premul_col + pix;
store_premul_pixel(dst_fl, dst);
dst += 4;
if (corner_radius > 0.0f) {
int cr = (int)corner_radius;
/* bottom left */
fill_ellipse_alpha_under(ibuf,
col,
x1,
y1,
x1 + cr,
y1 + cr,
x1 + corner_radius - 1,
y1 + corner_radius - 1,
corner_radius);
/* top left */
fill_ellipse_alpha_under(ibuf,
col,
x1,
y2 - cr,
x1 + cr,
y2,
x1 + corner_radius - 1,
y2 - corner_radius,
corner_radius);
/* top right */
fill_ellipse_alpha_under(ibuf,
col,
x2 - cr,
y2 - cr,
x2,
y2,
x2 - corner_radius,
y2 - corner_radius,
corner_radius);
/* bottom right */
fill_ellipse_alpha_under(ibuf,
col,
x2 - cr,
y1,
x2,
y1 + cr,
x2 - corner_radius,
y1 + corner_radius - 1,
corner_radius);
/* fill in areas between corners */
/* bottom */
fill_rect_alpha_under(ibuf, col, x1 + cr, y1, x2 - cr, y1 + cr, 0.0f);
/* middle */
fill_rect_alpha_under(ibuf, col, x1, y1 + cr, x2, y2 - cr, 0.0f);
/* top */
fill_rect_alpha_under(ibuf, col, x1 + cr, y2, x2 - cr, y2 - cr, 0.0f);
}
else {
float4 premul_col;
straight_to_premul_v4_v4(premul_col, col);
for (int y = y1; y < y2; y++) {
uchar *dst = ibuf->byte_buffer.data + (size_t(width) * y + x1) * 4;
for (int x = x1; x < x2; x++) {
float4 pix = load_premul_pixel(dst);
float fac = 1.0f - pix.w;
float4 dst_fl = fac * premul_col + pix;
store_premul_pixel(dst_fl, dst);
dst += 4;
}
}
}
}
@@ -3331,7 +3421,8 @@ static ImBuf *do_text_effect(const SeqRenderData *context,
const int maxx = runtime.text_boundbox.xmax + margin;
const int miny = runtime.text_boundbox.ymin - margin;
const int maxy = runtime.text_boundbox.ymax + margin;
fill_rect_alpha_under(out, data->box_color, minx, miny, maxx, maxy);
float corner_radius = data->box_roundness * (maxy - miny) / 2.0f;
fill_rect_alpha_under(out, data->box_color, minx, miny, maxx, maxy, corner_radius);
}
}