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:
@@ -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"
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user