Skip to content

Commit

Permalink
colorspace: split PL_ALPHA_NONE off from PL_ALPHA_UNKNOWN
Browse files Browse the repository at this point in the history
This field was doing double duty between marking the alpha mode as
unknown and marking the alpha channel as absent, with inconsistent
results. In particular, pl_renderer always inferred the alpha channel if
present in the texture, leaving users with no options for easily
manually disabling it.

This patch adds PL_ALPHA_NONE to rectify the situation, allowing API
users to set `target.repr.alpha = PL_ALPHA_NONE` to explicitly disable
alpha blending even when the target supports it.

See-Also: https://code.videolan.org/videolan/libplacebo/-/merge_requests/637
  • Loading branch information
haasn committed Feb 21, 2024
1 parent ac4db19 commit 0ee549a
Show file tree
Hide file tree
Showing 9 changed files with 46 additions and 16 deletions.
2 changes: 1 addition & 1 deletion demos/plplay.c
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ static bool render_loop(struct plplay *p)

// Disable background transparency by default if the swapchain does not
// appear to support alpha transaprency
if (frame.color_repr.alpha == PL_ALPHA_UNKNOWN)
if (frame.color_repr.alpha == PL_ALPHA_NONE)
opts->params.background_transparency = 0.0;

if (!render_frame(p, &frame, &mix))
Expand Down
9 changes: 5 additions & 4 deletions demos/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ void update_settings(struct plplay *p, const struct pl_frame *target)
dpar->address_mode = nk_combo(nk, address_modes, PL_TEX_ADDRESS_MODE_COUNT,
dpar->address_mode, 16, nk_vec2(nk_widget_width(nk), 100));
bool alpha = nk_check_label(nk, "Transparent background", dpar->alpha_mode);
dpar->alpha_mode = alpha ? PL_ALPHA_INDEPENDENT : PL_ALPHA_UNKNOWN;
dpar->alpha_mode = alpha ? PL_ALPHA_INDEPENDENT : PL_ALPHA_NONE;
nk_checkbox_label(nk, "Bicubic interpolation", &dpar->bicubic);

struct pl_transform2x2 *tf = &dpar->transform;
Expand Down Expand Up @@ -903,9 +903,10 @@ void update_settings(struct plplay *p, const struct pl_frame *target)
16, nk_vec2(nk_widget_width(nk), 200));

const char *alphas[PL_ALPHA_MODE_COUNT] = {
[PL_ALPHA_UNKNOWN] = "Auto (unknown, or no alpha)",
[PL_ALPHA_INDEPENDENT] = "Independent alpha channel",
[PL_ALPHA_PREMULTIPLIED] = "Premultiplied alpha channel",
[PL_ALPHA_UNKNOWN] = "Auto (unknown)",
[PL_ALPHA_INDEPENDENT] = "Independent",
[PL_ALPHA_PREMULTIPLIED] = "Premultiplied",
[PL_ALPHA_NONE] = "None",
};

if (target->repr.alpha) {
Expand Down
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ project('libplacebo', ['c', 'cpp'],
7,
# API version
{
'344': 'add PL_ALPHA_NONE',
'343': 'add pl_map_avdovi_metadata and deprecate pl_frame_map_avdovi_metadata',
'342': 'add pl_cache_signature',
'341': 're-add pl_filter_function_{bicubic,bcspline,catmull_rom,mitchell,robidoux,robidouxsharp} as deprecated',
Expand Down
3 changes: 2 additions & 1 deletion src/include/libplacebo/colorspace.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,10 @@ enum pl_color_levels {

// The alpha representation mode.
enum pl_alpha_mode {
PL_ALPHA_UNKNOWN = 0, // or no alpha channel present
PL_ALPHA_UNKNOWN = 0,
PL_ALPHA_INDEPENDENT, // alpha channel is separate from the video
PL_ALPHA_PREMULTIPLIED, // alpha channel is multiplied into the colors
PL_ALPHA_NONE, // alpha channel explicitly ignored (or absent)
PL_ALPHA_MODE_COUNT,
};

Expand Down
2 changes: 1 addition & 1 deletion src/include/libplacebo/utils/libav_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ PL_LIBAV_API void pl_frame_from_avframe(struct pl_frame *out,
.levels = pl_levels_from_av(frame->color_range),
.alpha = (desc->flags & AV_PIX_FMT_FLAG_ALPHA)
? PL_ALPHA_INDEPENDENT
: PL_ALPHA_UNKNOWN,
: PL_ALPHA_NONE,

// For sake of simplicity, just use the first component's depth as
// the authoritative color depth for the whole image. Usually, this
Expand Down
2 changes: 1 addition & 1 deletion src/opengl/swapchain.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ static bool gl_sw_start_frame(pl_swapchain sw,
.levels = PL_COLOR_LEVELS_FULL,
.alpha = p->fb->params.format->num_components == 4
? PL_ALPHA_PREMULTIPLIED
: PL_ALPHA_UNKNOWN,
: PL_ALPHA_NONE,
.bits = {
// Just use the red channel in the absence of anything more
// sane to do, because the red channel is both guaranteed to
Expand Down
5 changes: 3 additions & 2 deletions src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -1109,9 +1109,10 @@ const struct pl_opt_t pl_option_list[] = {
{"repeat", PL_TEX_ADDRESS_REPEAT},
{"mirror", PL_TEX_ADDRESS_MIRROR})),
OPT_ENUM("distort_alpha_mode", "Distortion alpha blending mode", distort_params.alpha_mode, LIST(
{"none", PL_ALPHA_UNKNOWN},
{"unknown", PL_ALPHA_UNKNOWN},
{"independent", PL_ALPHA_INDEPENDENT},
{"premultiplied", PL_ALPHA_PREMULTIPLIED})),
{"premultiplied", PL_ALPHA_PREMULTIPLIED},
{"none", PL_ALPHA_NONE})),

// Misc renderer settings
OPT_NAMED("error_diffusion", "Error diffusion kernel", params.error_diffusion,
Expand Down
26 changes: 21 additions & 5 deletions src/renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1544,6 +1544,19 @@ static bool pass_read_image(struct pass_state *pass)
},
};

// Explicitly skip alpha channel when overridden
if (image->repr.alpha == PL_ALPHA_NONE) {
if (planes[i].type == PLANE_ALPHA) {
planes[i].type = PLANE_INVALID;
continue;
} else {
for (int j = 0; j < planes[i].plane.components; j++) {
if (planes[i].plane.component_mapping[j] == PL_CHANNEL_A)
planes[i].plane.component_mapping[j] = PL_CHANNEL_NONE;
}
}
}

// Deinterlace plane if needed
if (image->field != PL_FIELD_NONE && params->deinterlace_params &&
pass->fbofmt[4] && !(rr->errors & PL_RENDER_ERR_DEINTERLACING))
Expand Down Expand Up @@ -1828,7 +1841,7 @@ static bool pass_read_image(struct pass_state *pass)
.h = pl_rect_h(ref_rounded),
.repr = ref->img.repr,
.color = image->color,
.comps = ref->img.repr.alpha ? 4 : 3,
.comps = ref->img.repr.alpha == PL_ALPHA_NONE ? 3 : 4,
.rect = {
off_x,
off_y,
Expand Down Expand Up @@ -2304,6 +2317,7 @@ static bool pass_output_target(struct pass_state *pass)

switch (img->repr.alpha) {
case PL_ALPHA_UNKNOWN:
case PL_ALPHA_NONE:
GLSL("color.a = border; \n");
img->repr.alpha = PL_ALPHA_INDEPENDENT;
img->comps = 4;
Expand Down Expand Up @@ -2372,7 +2386,7 @@ static bool pass_output_target(struct pass_state *pass)
pass_hook(pass, img, PL_HOOK_PRE_OUTPUT);

bool need_blend = params->blend_against_tiles ||
(!target->repr.alpha && !params->blend_params);
(target->repr.alpha == PL_ALPHA_NONE && !params->blend_params);
if (img->comps == 4 && need_blend) {
if (params->blend_against_tiles) {
static const float zero[2][3] = {0};
Expand All @@ -2397,7 +2411,7 @@ static bool pass_output_target(struct pass_state *pass)

pl_shader_set_alpha(sh, &img->repr, PL_ALPHA_PREMULTIPLIED);
GLSL("color = vec4(color.rgb + bg_color * (1.0 - color.a), 1.0); \n");
img->repr.alpha = PL_ALPHA_UNKNOWN;
img->repr.alpha = PL_ALPHA_NONE;
img->comps = 3;
}

Expand Down Expand Up @@ -2917,6 +2931,7 @@ static void pass_fix_frames(struct pass_state *pass)
// premultiplied. (We also premultiply for internal rendering, so this
// way of doing it avoids a possible division-by-zero path!)
if (image && !image->repr.alpha) {
image->repr.alpha = PL_ALPHA_NONE;
for (int i = 0; i < image->num_planes; i++) {
const struct pl_plane *plane = &image->planes[i];
for (int c = 0; c < plane->components; c++) {
Expand All @@ -2927,6 +2942,7 @@ static void pass_fix_frames(struct pass_state *pass)
}

if (!target->repr.alpha) {
target->repr.alpha = PL_ALPHA_NONE;
for (int i = 0; i < target->num_planes; i++) {
const struct pl_plane *plane = &target->planes[i];
for (int c = 0; c < plane->components; c++) {
Expand Down Expand Up @@ -3616,7 +3632,7 @@ bool pl_render_image_mix(pl_renderer rr, const struct pl_frame_mix *images,
.repr = {
.sys = PL_COLOR_SYSTEM_RGB,
.levels = PL_COLOR_LEVELS_PC,
.alpha = comps >= 4 ? PL_ALPHA_PREMULTIPLIED : PL_ALPHA_UNKNOWN,
.alpha = comps >= 4 ? PL_ALPHA_PREMULTIPLIED : PL_ALPHA_NONE,
},
};

Expand Down Expand Up @@ -3693,7 +3709,7 @@ void pl_frame_from_swapchain(struct pl_frame *out_frame,
{
pl_tex fbo = frame->fbo;
int num_comps = fbo->params.format->num_components;
if (!frame->color_repr.alpha)
if (frame->color_repr.alpha == PL_ALPHA_NONE)
num_comps = PL_MIN(num_comps, 3);

*out_frame = (struct pl_frame) {
Expand Down
12 changes: 11 additions & 1 deletion src/shaders/colorspace.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ static const float SLOG_A = 0.432699,
void pl_shader_set_alpha(pl_shader sh, struct pl_color_repr *repr,
enum pl_alpha_mode mode)
{
if (repr->alpha == PL_ALPHA_PREMULTIPLIED && mode == PL_ALPHA_INDEPENDENT) {
bool src_has_alpha = repr->alpha == PL_ALPHA_INDEPENDENT ||
repr->alpha == PL_ALPHA_PREMULTIPLIED;
bool dst_not_premul = mode == PL_ALPHA_INDEPENDENT ||
mode == PL_ALPHA_NONE;

if (repr->alpha == PL_ALPHA_PREMULTIPLIED && dst_not_premul) {
GLSL("if (color.a > 1e-6) \n"
" color.rgb /= vec3(color.a); \n");
repr->alpha = PL_ALPHA_INDEPENDENT;
Expand All @@ -61,6 +66,11 @@ void pl_shader_set_alpha(pl_shader sh, struct pl_color_repr *repr,
GLSL("color.rgb *= vec3(color.a); \n");
repr->alpha = PL_ALPHA_PREMULTIPLIED;
}

if (src_has_alpha && mode == PL_ALPHA_NONE) {
GLSL("color.a = 1.0; \n");
repr->alpha = PL_ALPHA_NONE;
}
}

#ifdef PL_HAVE_DOVI
Expand Down

0 comments on commit 0ee549a

Please sign in to comment.