From b9309e23263ea71bad8a0151c56f384e4778a607 Mon Sep 17 00:00:00 2001 From: bitheaven Date: Mon, 20 Jul 2026 11:40:45 +0500 Subject: [PATCH] Fix transparency? --- CMakeLists.txt | 5 ++ src/plugin.c | 3 +- tests/module-load-test.c | 96 +++++++++++++++++++++++++++++++++++-- tests/renderer-smoke-test.c | 28 ++++++++--- 4 files changed, 119 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fad161c..ee95e7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,4 +115,9 @@ if(BUILD_TESTING) $ "${CMAKE_CURRENT_SOURCE_DIR}/data" $ audio-test) set_tests_properties(module-audio-test PROPERTIES TIMEOUT 20 SKIP_RETURN_CODE 77) + + add_test(NAME module-alpha-test COMMAND module-load-test + $ "${CMAKE_CURRENT_SOURCE_DIR}/data" + $ alpha-test) + set_tests_properties(module-alpha-test PROPERTIES TIMEOUT 20 SKIP_RETURN_CODE 77) endif() diff --git a/src/plugin.c b/src/plugin.c index 293eebc..a279e61 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -684,7 +684,8 @@ static struct obs_source_info browser_info = { .id = "webkit_browser_source", .type = OBS_SOURCE_TYPE_INPUT, .icon_type = OBS_ICON_TYPE_BROWSER, - .output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO | OBS_SOURCE_INTERACTION, + .output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO | OBS_SOURCE_INTERACTION | + OBS_SOURCE_SRGB, .get_name = browser_name, .create = browser_create, .destroy = browser_destroy, diff --git a/tests/module-load-test.c b/tests/module-load-test.c index 96b6aee..56078c0 100644 --- a/tests/module-load-test.c +++ b/tests/module-load-test.c @@ -19,6 +19,56 @@ struct audio_probe { atomic_bool signal; }; +static bool source_alpha_is_correct(obs_source_t *source) +{ + bool correct = false; + uint8_t solid[4] = {0}; + uint8_t half[4] = {0}; + uint8_t background[4] = {0}; + obs_set_output_source(0, source); + for (unsigned attempt = 0; attempt < 60 && !correct; attempt++) { + usleep(100000); + obs_enter_graphics(); + gs_texrender_t *target = gs_texrender_create(GS_BGRA, GS_ZS_NONE); + gs_stagesurf_t *stage = gs_stagesurface_create(64, 64, GS_BGRA); + if (target && stage && gs_texrender_begin(target, 64, 64)) { + struct vec4 clear = {0}; + gs_clear(GS_CLEAR_COLOR, &clear, 0.0f, 0); + gs_ortho(0.0f, 64.0f, 0.0f, 64.0f, -100.0f, 100.0f); + bool previous_srgb = gs_set_linear_srgb(true); + obs_source_video_render(source); + gs_set_linear_srgb(previous_srgb); + gs_texrender_end(target); + gs_stage_texture(stage, gs_texrender_get_texture(target)); + uint8_t *pixels = NULL; + uint32_t stride = 0; + if (gs_stagesurface_map(stage, &pixels, &stride)) { + memcpy(background, pixels, sizeof(background)); + memcpy(solid, pixels + (size_t)24 * stride + 24 * 4, sizeof(solid)); + memcpy(half, pixels + (size_t)40 * stride + 40 * 4, sizeof(half)); + gs_stagesurface_unmap(stage); + correct = solid[0] < 32 && solid[1] < 40 && solid[2] > 225 && + solid[3] == 255 && half[0] < 8 && half[1] < 8 && + half[2] > 120 && half[2] < 136 && half[3] > 120 && + half[3] < 136 && background[0] == 0 && + background[1] == 0 && background[2] == 0 && + background[3] == 0; + } + } + gs_stagesurface_destroy(stage); + gs_texrender_destroy(target); + obs_leave_graphics(); + } + obs_set_output_source(0, NULL); + if (!correct) + fprintf(stderr, + "OBS compositing changed browser alpha (solid BGRA=%u,%u,%u,%u; " + "half BGRA=%u,%u,%u,%u; background BGRA=%u,%u,%u,%u)\n", + solid[0], solid[1], solid[2], solid[3], half[0], half[1], half[2], + half[3], background[0], background[1], background[2], background[3]); + return correct; +} + static bool write_all(int fd, const void *data, size_t size) { const uint8_t *cursor = data; @@ -149,10 +199,13 @@ static void audio_captured(void *opaque, obs_source_t *source, int main(int argc, char **argv) { if (argc < 4 || argc > 5) { - fprintf(stderr, "usage: %s plugin.so data-directory renderer [audio-test]\n", argv[0]); + fprintf(stderr, + "usage: %s plugin.so data-directory renderer [audio-test | alpha-test]\n", + argv[0]); return 2; } bool audio_test = argc == 5 && strcmp(argv[4], "audio-test") == 0; + bool alpha_test = argc == 5 && strcmp(argv[4], "alpha-test") == 0; char temporary[] = "/tmp/obs-webkit-module-test-XXXXXX"; if (!mkdtemp(temporary)) { perror("mkdtemp"); @@ -180,6 +233,26 @@ int main(int argc, char **argv) obs_shutdown(); return 77; } + if (alpha_test) { + struct obs_video_info video_info = { + .graphics_module = "libobs-opengl", + .fps_num = 30, + .fps_den = 1, + .base_width = 64, + .base_height = 64, + .output_width = 64, + .output_height = 64, + .output_format = VIDEO_FORMAT_BGRA, + .gpu_conversion = false, + .colorspace = VIDEO_CS_DEFAULT, + .range = VIDEO_RANGE_FULL, + .scale_type = OBS_SCALE_DISABLE, + }; + if (obs_reset_video(&video_info) != OBS_VIDEO_SUCCESS) { + obs_shutdown(); + return 77; + } + } char audio_url[128] = {0}; pid_t audio_server = audio_test ? start_audio_server(audio_url, sizeof(audio_url)) : -1; if (audio_test && audio_server < 0) { @@ -200,17 +273,28 @@ int main(int argc, char **argv) const char *name = obs_source_get_display_name("webkit_browser_source"); uint32_t flags = obs_get_source_output_flags("webkit_browser_source"); bool valid = name && strstr(name, "WebKit") && (flags & OBS_SOURCE_ASYNC_VIDEO) && - (flags & OBS_SOURCE_AUDIO) && (flags & OBS_SOURCE_INTERACTION); + (flags & OBS_SOURCE_AUDIO) && (flags & OBS_SOURCE_INTERACTION) && + (flags & OBS_SOURCE_SRGB); if (!valid) fprintf(stderr, "source registration is incomplete (name=%s, flags=0x%x)\n", name ? name : "(null)", flags); obs_data_t *settings = obs_data_create(); - obs_data_set_string(settings, "url", audio_test ? audio_url : - "data:text/html,"); + const char *test_url = "data:text/html,"; + if (audio_test) + test_url = audio_url; + else if (alpha_test) + test_url = "data:text/html,
"; + obs_data_set_string(settings, "url", test_url); + if (alpha_test) + obs_data_set_string(settings, "css", + "html,body{margin:0}#box{position:fixed;left:16px;top:16px;" + "width:16px;height:16px;background:rgb(241,23,17)}" + "#box:after{content:'';position:fixed;left:32px;top:32px;" + "width:16px;height:16px;background:rgba(255,0,0,.5)}"); obs_data_set_int(settings, "width", 64); obs_data_set_int(settings, "height", 64); obs_data_set_int(settings, "fps", 10); - obs_data_set_int(settings, "zoom", 175); + obs_data_set_int(settings, "zoom", alpha_test ? 100 : 175); obs_data_set_bool(settings, "capture_audio", audio_test); obs_data_set_bool(settings, "transparent", true); obs_data_set_bool(settings, "hardware_acceleration", false); @@ -244,6 +328,8 @@ int main(int argc, char **argv) if (source && audio_test) obs_source_add_audio_capture_callback(source, audio_captured, &probe); if (source) { + if (alpha_test && !source_alpha_is_correct(source)) + valid = false; unsigned attempts = audio_test ? 100 : 5; for (unsigned attempt = 0; attempt < attempts && (!audio_test || !atomic_load(&probe.signal)); attempt++) diff --git a/tests/renderer-smoke-test.c b/tests/renderer-smoke-test.c index 9eb39a2..ff03ecd 100644 --- a/tests/renderer-smoke-test.c +++ b/tests/renderer-smoke-test.c @@ -104,8 +104,12 @@ int main(int argc, char **argv) const char internal_url[] = "data:text/html,
"; - const char transparent_css[] = "html,body{margin:0}" - "#box{position:fixed;inset:16px;background:rgba(255,0,0,.5)}"; + const char transparent_css[] = + "html,body{margin:0}" + "#box{position:fixed;left:16px;top:16px;width:16px;height:16px;" + "background:rgb(241,23,17)}" + "#box:after{content:'';position:fixed;left:32px;top:32px;width:16px;" + "height:16px;background:rgba(255,0,0,.5)}"; const char opaque_css[] = "html,body{margin:0;background:rgb(7,19,31)!important}" "#box{position:fixed;left:16px;top:16px;width:32px;height:32px;" "background:rgb(241,23,17);transform:translateZ(0);filter:blur(.1px)}"; @@ -136,6 +140,7 @@ int main(int argc, char **argv) write_exact(control[0], css, strlen(css)); uint8_t last_bgra[4] = {0}; uint8_t last_corner_bgra[4] = {0}; + uint8_t last_half_bgra[4] = {0}; bool found_red = false; int max_attempts = external_url ? 120 : 60; for (int attempt = 0; sent && attempt < max_attempts; attempt++) { @@ -168,10 +173,15 @@ int main(int argc, char **argv) continue; } size_t center = zoom ? ((size_t)5 * width + 15) * 4 - : ((size_t)(height / 2) * width + width / 2) * 4; + : opaque ? ((size_t)(height / 2) * width + width / 2) * 4 + : ((size_t)24 * width + 24) * 4; memcpy(last_bgra, frame + center, sizeof(last_bgra)); size_t corner = zoom ? ((size_t)5 * width + 25) * 4 : 0; memcpy(last_corner_bgra, frame + corner, sizeof(last_corner_bgra)); + if (!zoom && !opaque) { + size_t half = ((size_t)40 * width + 40) * 4; + memcpy(last_half_bgra, frame + half, sizeof(last_half_bgra)); + } bool expected = zoom ? last_bgra[0] < 32 && last_bgra[1] < 40 && last_bgra[2] > 210 && last_bgra[3] == 255 && last_corner_bgra[0] > 20 && @@ -180,8 +190,10 @@ int main(int argc, char **argv) : opaque ? last_bgra[0] < 32 && last_bgra[1] < 40 && last_bgra[2] > 210 && last_bgra[3] == 255 - : last_bgra[0] < 8 && last_bgra[1] < 8 && last_bgra[2] > 248 && - last_bgra[3] > 120 && last_bgra[3] < 136 && + : last_bgra[0] < 32 && last_bgra[1] < 40 && last_bgra[2] > 225 && + last_bgra[3] == 255 && last_half_bgra[0] < 8 && + last_half_bgra[1] < 8 && last_half_bgra[2] > 248 && + last_half_bgra[3] > 120 && last_half_bgra[3] < 136 && last_corner_bgra[0] == 0 && last_corner_bgra[1] == 0 && last_corner_bgra[2] == 0 && last_corner_bgra[3] == 0; if (expected) { @@ -207,10 +219,12 @@ int main(int argc, char **argv) } fprintf(stderr, "renderer did not produce the expected %s red BGRA frame " - "(center BGRA=%u,%u,%u,%u; corner BGRA=%u,%u,%u,%u)\n", + "(center BGRA=%u,%u,%u,%u; half BGRA=%u,%u,%u,%u; " + "corner BGRA=%u,%u,%u,%u)\n", zoom ? "200%-zoomed" : opaque ? "opaque" : "transparent", last_bgra[0], last_bgra[1], - last_bgra[2], last_bgra[3], last_corner_bgra[0], last_corner_bgra[1], + last_bgra[2], last_bgra[3], last_half_bgra[0], last_half_bgra[1], + last_half_bgra[2], last_half_bgra[3], last_corner_bgra[0], last_corner_bgra[1], last_corner_bgra[2], last_corner_bgra[3]); return 1; }