From 3cfc21e49874adb5030af11d34cb35e070bd6b96 Mon Sep 17 00:00:00 2001 From: Brian Matherly Date: Sat, 19 Aug 2023 09:03:39 -0500 Subject: [PATCH] Add preserve_alpha to box_blur This allows the user to disable alpha blur so the filter works as expected with mask_start/mask_apply --- src/modules/core/filter_box_blur.c | 5 +- src/modules/core/filter_box_blur.yml | 9 +- src/modules/core/filter_pillar_echo.c | 4 +- src/modules/core/image_proc.c | 164 ++++++++++++++++++++++++-- src/modules/core/image_proc.h | 4 +- 5 files changed, 171 insertions(+), 15 deletions(-) diff --git a/src/modules/core/filter_box_blur.c b/src/modules/core/filter_box_blur.c index b44975098..45e32bd1b 100644 --- a/src/modules/core/filter_box_blur.c +++ b/src/modules/core/filter_box_blur.c @@ -1,6 +1,6 @@ /* * filter_box_blur.c - * Copyright (C) 2011-2021 Meltytech, LLC + * Copyright (C) 2011-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -41,6 +41,7 @@ static int filter_get_image(mlt_frame frame, mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); double hradius = mlt_properties_anim_get_double(properties, "hradius", position, length); double vradius = mlt_properties_anim_get_double(properties, "vradius", position, length); + int preserve_alpha = mlt_properties_get_int(properties, "preserve_alpha"); // Convert from percent to pixels as a factor of 10% image width. double pixelScale = (double) profile->width * mlt_profile_scale_width(profile, *width) / 1000.0; hradius = MAX(round(hradius * pixelScale), 0); @@ -56,7 +57,7 @@ static int filter_get_image(mlt_frame frame, if (error == 0) { struct mlt_image_s img; mlt_image_set_values(&img, *image, *format, *width, *height); - mlt_image_box_blur(&img, hradius, vradius); + mlt_image_box_blur(&img, hradius, vradius, preserve_alpha); } } return error; diff --git a/src/modules/core/filter_box_blur.yml b/src/modules/core/filter_box_blur.yml index 6ca2ad2b8..967db40b6 100644 --- a/src/modules/core/filter_box_blur.yml +++ b/src/modules/core/filter_box_blur.yml @@ -2,7 +2,7 @@ schema_version: 7.0 type: filter identifier: box_blur title: Box Blur -version: 1 +version: 2 copyright: Meltytech, LLC license: LGPLv2.1 language: en @@ -33,3 +33,10 @@ parameters: animation: yes minimum: 0 default: 1 + + - identifier: preserve_alpha + title: Preserve Alpha + type: boolean + description: Exclude the alpha channel from the blur operation + mutable: yes + default: 0 diff --git a/src/modules/core/filter_pillar_echo.c b/src/modules/core/filter_pillar_echo.c index f7cb77ab0..914f0504d 100644 --- a/src/modules/core/filter_pillar_echo.c +++ b/src/modules/core/filter_pillar_echo.c @@ -1,6 +1,6 @@ /* * filter_pillar_echo.c -- filter to interpolate pixels outside an area of interest - * Copyright (c) 2020-2021 Meltytech, LLC + * Copyright (c) 2020-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -265,7 +265,7 @@ static int filter_get_image(mlt_frame frame, bilinear_scale_rgba(&src, &dst, rect); if (blur != 0) { - mlt_image_box_blur(&dst, blur, blur); + mlt_image_box_blur(&dst, blur, blur, 0); } blit_rect(&src, &dst, rect); diff --git a/src/modules/core/image_proc.c b/src/modules/core/image_proc.c index 41b9f2a3d..cf8ea534e 100644 --- a/src/modules/core/image_proc.c +++ b/src/modules/core/image_proc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Meltytech, LLC + * Copyright (c) 2022-2023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -30,7 +30,7 @@ typedef struct int radius; } blur_slice_desc; -static int blur_h_proc(int id, int index, int jobs, void *data) +static int blur_h_proc_rgba(int id, int index, int jobs, void *data) { (void) id; // unused blur_slice_desc *desc = ((blur_slice_desc *) data); @@ -108,7 +108,7 @@ static int blur_h_proc(int id, int index, int jobs, void *data) return 0; } -static int blur_v_proc(int id, int index, int jobs, void *data) +static int blur_v_proc_rgba(int id, int index, int jobs, void *data) { (void) id; // unused blur_slice_desc *desc = ((blur_slice_desc *) data); @@ -186,6 +186,146 @@ static int blur_v_proc(int id, int index, int jobs, void *data) return 0; } +static int blur_h_proc_rgbx(int id, int index, int jobs, void *data) +{ + (void) id; // unused + blur_slice_desc *desc = ((blur_slice_desc *) data); + int slice_line_start, + slice_height = mlt_slices_size_slice(jobs, index, desc->src->height, &slice_line_start); + int slice_line_end = slice_line_start + slice_height; + int accumulator[] = {0, 0, 0}; + int x = 0; + int y = 0; + int step = 4; + int linesize = step * desc->src->width; + int radius = desc->radius; + + if (desc->radius > (desc->src->width / 2)) { + radius = desc->src->width / 2; + } + double diameter = (radius * 2) + 1; + + for (y = slice_line_start; y < slice_line_end; y++) { + uint8_t *first = desc->src->data + (y * linesize); + uint8_t *last = first + linesize - step; + uint8_t *s1 = first; + uint8_t *s2 = first; + uint8_t *d = desc->dst->data + (y * linesize); + accumulator[0] = first[0] * (radius + 1); + accumulator[1] = first[1] * (radius + 1); + accumulator[2] = first[2] * (radius + 1); + + for (x = 0; x < radius; x++) { + accumulator[0] += s1[0]; + accumulator[1] += s1[1]; + accumulator[2] += s1[2]; + s1 += step; + } + for (x = 0; x <= radius; x++) { + accumulator[0] += s1[0] - first[0]; + accumulator[1] += s1[1] - first[1]; + accumulator[2] += s1[2] - first[2]; + d[0] = lrint((double) accumulator[0] / diameter); + d[1] = lrint((double) accumulator[1] / diameter); + d[2] = lrint((double) accumulator[2] / diameter); + s1 += step; + d += step; + } + for (x = radius + 1; x < desc->src->width - radius; x++) { + accumulator[0] += s1[0] - s2[0]; + accumulator[1] += s1[1] - s2[1]; + accumulator[2] += s1[2] - s2[2]; + d[0] = lrint((double) accumulator[0] / diameter); + d[1] = lrint((double) accumulator[1] / diameter); + d[2] = lrint((double) accumulator[2] / diameter); + s1 += step; + s2 += step; + d += step; + } + for (x = desc->src->width - radius; x < desc->src->width; x++) { + accumulator[0] += last[0] - s2[0]; + accumulator[1] += last[1] - s2[1]; + accumulator[2] += last[2] - s2[2]; + d[0] = lrint((double) accumulator[0] / diameter); + d[1] = lrint((double) accumulator[1] / diameter); + d[2] = lrint((double) accumulator[2] / diameter); + s2 += step; + d += step; + } + } + return 0; +} + +static int blur_v_proc_rgbx(int id, int index, int jobs, void *data) +{ + (void) id; // unused + blur_slice_desc *desc = ((blur_slice_desc *) data); + int slice_row_start, + slice_width = mlt_slices_size_slice(jobs, index, desc->src->width, &slice_row_start); + int slice_row_end = slice_row_start + slice_width; + int accumulator[] = {0, 0, 0}; + int x = 0; + int y = 0; + int step = 4; + int linesize = step * desc->src->width; + int radius = desc->radius; + + if (desc->radius > (desc->src->height / 2)) { + radius = desc->src->height / 2; + } + double diameter = (radius * 2) + 1; + + for (x = slice_row_start; x < slice_row_end; x++) { + uint8_t *first = desc->src->data + (x * step); + uint8_t *last = first + (linesize * (desc->src->height - 1)); + uint8_t *s1 = first; + uint8_t *s2 = first; + uint8_t *d = desc->dst->data + (x * step); + accumulator[0] = first[0] * (radius + 1); + accumulator[1] = first[1] * (radius + 1); + accumulator[2] = first[2] * (radius + 1); + + for (y = 0; y < radius; y++) { + accumulator[0] += s1[0]; + accumulator[1] += s1[1]; + accumulator[2] += s1[2]; + s1 += linesize; + } + for (y = 0; y <= radius; y++) { + accumulator[0] += s1[0] - first[0]; + accumulator[1] += s1[1] - first[1]; + accumulator[2] += s1[2] - first[2]; + d[0] = lrint((double) accumulator[0] / diameter); + d[1] = lrint((double) accumulator[1] / diameter); + d[2] = lrint((double) accumulator[2] / diameter); + s1 += linesize; + d += linesize; + } + for (y = radius + 1; y < desc->src->height - radius; y++) { + accumulator[0] += s1[0] - s2[0]; + accumulator[1] += s1[1] - s2[1]; + accumulator[2] += s1[2] - s2[2]; + d[0] = lrint((double) accumulator[0] / diameter); + d[1] = lrint((double) accumulator[1] / diameter); + d[2] = lrint((double) accumulator[2] / diameter); + s1 += linesize; + s2 += linesize; + d += linesize; + } + for (y = desc->src->height - radius; y < desc->src->height; y++) { + accumulator[0] += last[0] - s2[0]; + accumulator[1] += last[1] - s2[1]; + accumulator[2] += last[2] - s2[2]; + d[0] = lrint((double) accumulator[0] / diameter); + d[1] = lrint((double) accumulator[1] / diameter); + d[2] = lrint((double) accumulator[2] / diameter); + s2 += linesize; + d += linesize; + } + } + return 0; +} + /** Perform a box blur * * This function uses a sliding window accumulator method - applied @@ -194,9 +334,10 @@ static int blur_v_proc(int id, int index, int jobs, void *data) * \param self the Image object * \param hradius the radius of the horizontal blur in pixels * \param vradius radius of the vertical blur in pixels + * \param preserve_alpha exclude the alpha channel from the blur operation */ -void mlt_image_box_blur(mlt_image self, int hradius, int vradius) +void mlt_image_box_blur(mlt_image self, int hradius, int vradius, int preserve_alpha) { if (self->format != mlt_image_rgba) { mlt_log(NULL, @@ -215,10 +356,17 @@ void mlt_image_box_blur(mlt_image self, int hradius, int vradius) } blur_slice_desc desc; - desc.src = self, desc.dst = &tmpimage, desc.radius = hradius, - mlt_slices_run_normal(0, blur_h_proc, &desc); - desc.src = &tmpimage, desc.dst = self, desc.radius = vradius, - mlt_slices_run_normal(0, blur_v_proc, &desc); + if (preserve_alpha) { + desc.src = self, desc.dst = &tmpimage, desc.radius = hradius, + mlt_slices_run_normal(0, blur_h_proc_rgbx, &desc); + desc.src = &tmpimage, desc.dst = self, desc.radius = vradius, + mlt_slices_run_normal(0, blur_v_proc_rgbx, &desc); + } else { + desc.src = self, desc.dst = &tmpimage, desc.radius = hradius, + mlt_slices_run_normal(0, blur_h_proc_rgba, &desc); + desc.src = &tmpimage, desc.dst = self, desc.radius = vradius, + mlt_slices_run_normal(0, blur_v_proc_rgba, &desc); + } mlt_image_close(&tmpimage); } diff --git a/src/modules/core/image_proc.h b/src/modules/core/image_proc.h index 213b02614..616f0594e 100644 --- a/src/modules/core/image_proc.h +++ b/src/modules/core/image_proc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Meltytech, LLC + * Copyright (c) 2022-20023 Meltytech, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,6 +22,6 @@ #include #include -void mlt_image_box_blur(mlt_image self, int hradius, int vradius); +void mlt_image_box_blur(mlt_image self, int hradius, int vradius, int preserve_alpha); #endif // IMAGE_PROC_H