You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-06-08 22:17:28 -07:00
Added top/bottom/left/right margins to Blur effect, similar to Pixelate one (for consistency). Renamed the user-facing JsonProperty labels across all effects with similar keyframes to:
- Margin: Left - Margin: Top - Margin: Right - Margin: Bottom
This commit is contained in:
@@ -167,10 +167,10 @@ std::string Bars::PropertiesJSON(int64_t requested_frame) const {
|
||||
root["color"]["red"] = add_property_json("Red", color.red.GetValue(requested_frame), "float", "", &color.red, 0, 255, false, requested_frame);
|
||||
root["color"]["blue"] = add_property_json("Blue", color.blue.GetValue(requested_frame), "float", "", &color.blue, 0, 255, false, requested_frame);
|
||||
root["color"]["green"] = add_property_json("Green", color.green.GetValue(requested_frame), "float", "", &color.green, 0, 255, false, requested_frame);
|
||||
root["left"] = add_property_json("Left Size", left.GetValue(requested_frame), "float", "", &left, 0.0, 0.5, false, requested_frame);
|
||||
root["top"] = add_property_json("Top Size", top.GetValue(requested_frame), "float", "", &top, 0.0, 0.5, false, requested_frame);
|
||||
root["right"] = add_property_json("Right Size", right.GetValue(requested_frame), "float", "", &right, 0.0, 0.5, false, requested_frame);
|
||||
root["bottom"] = add_property_json("Bottom Size", bottom.GetValue(requested_frame), "float", "", &bottom, 0.0, 0.5, false, requested_frame);
|
||||
root["left"] = add_property_json("Margin: Left", left.GetValue(requested_frame), "float", "", &left, 0.0, 0.5, false, requested_frame);
|
||||
root["top"] = add_property_json("Margin: Top", top.GetValue(requested_frame), "float", "", &top, 0.0, 0.5, false, requested_frame);
|
||||
root["right"] = add_property_json("Margin: Right", right.GetValue(requested_frame), "float", "", &right, 0.0, 0.5, false, requested_frame);
|
||||
root["bottom"] = add_property_json("Margin: Bottom", bottom.GetValue(requested_frame), "float", "", &bottom, 0.0, 0.5, false, requested_frame);
|
||||
|
||||
// Return formatted string
|
||||
return root.toStyledString();
|
||||
|
||||
+84
-8
@@ -13,10 +13,28 @@
|
||||
#include "Blur.h"
|
||||
#include "Exceptions.h"
|
||||
|
||||
#include <QMargins>
|
||||
#include <QPainter>
|
||||
#include <QPoint>
|
||||
#include <QRect>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace openshot;
|
||||
|
||||
namespace {
|
||||
double clamp_margin(double value) {
|
||||
if (value < 0.0)
|
||||
return 0.0;
|
||||
if (value > 1.0)
|
||||
return 1.0;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Blank constructor, useful when using Json to load the effect properties
|
||||
Blur::Blur() : horizontal_radius(6.0), vertical_radius(6.0), sigma(3.0), iterations(3.0),
|
||||
left(0.0), top(0.0), right(0.0), bottom(0.0),
|
||||
mask_mode(BLUR_MASK_POST_BLEND) {
|
||||
// Init effect properties
|
||||
init_effect_details();
|
||||
@@ -25,7 +43,21 @@ Blur::Blur() : horizontal_radius(6.0), vertical_radius(6.0), sigma(3.0), iterati
|
||||
// Default constructor
|
||||
Blur::Blur(Keyframe new_horizontal_radius, Keyframe new_vertical_radius, Keyframe new_sigma, Keyframe new_iterations) :
|
||||
horizontal_radius(new_horizontal_radius), vertical_radius(new_vertical_radius),
|
||||
sigma(new_sigma), iterations(new_iterations), mask_mode(BLUR_MASK_POST_BLEND)
|
||||
sigma(new_sigma), iterations(new_iterations),
|
||||
left(0.0), top(0.0), right(0.0), bottom(0.0),
|
||||
mask_mode(BLUR_MASK_POST_BLEND)
|
||||
{
|
||||
// Init effect properties
|
||||
init_effect_details();
|
||||
}
|
||||
|
||||
// Default constructor
|
||||
Blur::Blur(Keyframe new_horizontal_radius, Keyframe new_vertical_radius, Keyframe new_sigma, Keyframe new_iterations,
|
||||
Keyframe new_left, Keyframe new_top, Keyframe new_right, Keyframe new_bottom) :
|
||||
horizontal_radius(new_horizontal_radius), vertical_radius(new_vertical_radius),
|
||||
sigma(new_sigma), iterations(new_iterations),
|
||||
left(new_left), top(new_top), right(new_right), bottom(new_bottom),
|
||||
mask_mode(BLUR_MASK_POST_BLEND)
|
||||
{
|
||||
// Init effect properties
|
||||
init_effect_details();
|
||||
@@ -61,33 +93,61 @@ std::shared_ptr<openshot::Frame> Blur::GetFrame(std::shared_ptr<openshot::Frame>
|
||||
|
||||
int w = frame_image->width();
|
||||
int h = frame_image->height();
|
||||
if (w <= 0 || h <= 0 || iteration_value <= 0)
|
||||
return frame;
|
||||
|
||||
// Define area we're working on in terms of a QRect with QMargins applied.
|
||||
QRect area(QPoint(0, 0), frame_image->size());
|
||||
area = area.marginsRemoved({
|
||||
int(clamp_margin(left.GetValue(frame_number)) * w),
|
||||
int(clamp_margin(top.GetValue(frame_number)) * h),
|
||||
int(clamp_margin(right.GetValue(frame_number)) * w),
|
||||
int(clamp_margin(bottom.GetValue(frame_number)) * h)
|
||||
});
|
||||
area = area.intersected(QRect(QPoint(0, 0), frame_image->size()));
|
||||
if (area.isEmpty())
|
||||
return frame;
|
||||
|
||||
// Grab two copies of the image pixel data
|
||||
QImage image_copy = frame_image->copy();
|
||||
QImage image_copy = frame_image->copy(area);
|
||||
std::shared_ptr<QImage> blur_image = std::make_shared<QImage>(image_copy);
|
||||
std::shared_ptr<QImage> frame_image_2 = std::make_shared<QImage>(image_copy);
|
||||
const int area_w = blur_image->width();
|
||||
const int area_h = blur_image->height();
|
||||
const int horizontal_area_radius = std::min(std::max(0, horizontal_radius_value), std::max(0, area_w - 1));
|
||||
const int vertical_area_radius = std::min(std::max(0, vertical_radius_value), std::max(0, area_h - 1));
|
||||
bool blurred = false;
|
||||
|
||||
// Loop through each iteration
|
||||
for (int iteration = 0; iteration < iteration_value; ++iteration)
|
||||
{
|
||||
// HORIZONTAL BLUR (if any)
|
||||
if (horizontal_radius_value > 0.0) {
|
||||
if (horizontal_area_radius > 0.0) {
|
||||
// Apply horizontal blur to target RGBA channels
|
||||
boxBlurH(frame_image->bits(), frame_image_2->bits(), w, h, horizontal_radius_value);
|
||||
boxBlurH(blur_image->bits(), frame_image_2->bits(), area_w, area_h, horizontal_area_radius);
|
||||
|
||||
// Swap output image back to input
|
||||
frame_image.swap(frame_image_2);
|
||||
blur_image.swap(frame_image_2);
|
||||
blurred = true;
|
||||
}
|
||||
|
||||
// VERTICAL BLUR (if any)
|
||||
if (vertical_radius_value > 0.0) {
|
||||
if (vertical_area_radius > 0.0) {
|
||||
// Apply vertical blur to target RGBA channels
|
||||
boxBlurT(frame_image->bits(), frame_image_2->bits(), w, h, vertical_radius_value);
|
||||
boxBlurT(blur_image->bits(), frame_image_2->bits(), area_w, area_h, vertical_area_radius);
|
||||
|
||||
// Swap output image back to input
|
||||
frame_image.swap(frame_image_2);
|
||||
blur_image.swap(frame_image_2);
|
||||
blurred = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (blurred) {
|
||||
QPainter painter(frame_image.get());
|
||||
painter.drawImage(area, *blur_image);
|
||||
painter.end();
|
||||
}
|
||||
|
||||
// return the modified frame
|
||||
return frame;
|
||||
}
|
||||
@@ -249,6 +309,10 @@ Json::Value Blur::JsonValue() const {
|
||||
root["vertical_radius"] = vertical_radius.JsonValue();
|
||||
root["sigma"] = sigma.JsonValue();
|
||||
root["iterations"] = iterations.JsonValue();
|
||||
root["left"] = left.JsonValue();
|
||||
root["top"] = top.JsonValue();
|
||||
root["right"] = right.JsonValue();
|
||||
root["bottom"] = bottom.JsonValue();
|
||||
root["mask_mode"] = mask_mode;
|
||||
|
||||
// return JsonValue
|
||||
@@ -287,6 +351,14 @@ void Blur::SetJsonValue(const Json::Value root) {
|
||||
sigma.SetJsonValue(root["sigma"]);
|
||||
if (!root["iterations"].isNull())
|
||||
iterations.SetJsonValue(root["iterations"]);
|
||||
if (!root["left"].isNull())
|
||||
left.SetJsonValue(root["left"]);
|
||||
if (!root["top"].isNull())
|
||||
top.SetJsonValue(root["top"]);
|
||||
if (!root["right"].isNull())
|
||||
right.SetJsonValue(root["right"]);
|
||||
if (!root["bottom"].isNull())
|
||||
bottom.SetJsonValue(root["bottom"]);
|
||||
if (!root["mask_mode"].isNull())
|
||||
mask_mode = root["mask_mode"].asInt();
|
||||
}
|
||||
@@ -302,6 +374,10 @@ std::string Blur::PropertiesJSON(int64_t requested_frame) const {
|
||||
root["vertical_radius"] = add_property_json("Vertical Radius", vertical_radius.GetValue(requested_frame), "float", "", &vertical_radius, 0, 100, false, requested_frame);
|
||||
root["sigma"] = add_property_json("Sigma", sigma.GetValue(requested_frame), "float", "", &sigma, 0, 100, false, requested_frame);
|
||||
root["iterations"] = add_property_json("Iterations", iterations.GetValue(requested_frame), "float", "", &iterations, 0, 100, false, requested_frame);
|
||||
root["left"] = add_property_json("Margin: Left", left.GetValue(requested_frame), "float", "", &left, 0.0, 1.0, false, requested_frame);
|
||||
root["top"] = add_property_json("Margin: Top", top.GetValue(requested_frame), "float", "", &top, 0.0, 1.0, false, requested_frame);
|
||||
root["right"] = add_property_json("Margin: Right", right.GetValue(requested_frame), "float", "", &right, 0.0, 1.0, false, requested_frame);
|
||||
root["bottom"] = add_property_json("Margin: Bottom", bottom.GetValue(requested_frame), "float", "", &bottom, 0.0, 1.0, false, requested_frame);
|
||||
root["mask_mode"] = add_property_json("Mask Mode", mask_mode, "int", "", NULL, 0, 1, false, requested_frame);
|
||||
root["mask_mode"]["choices"].append(add_property_choice_json("Limit to Mask", BLUR_MASK_POST_BLEND, mask_mode));
|
||||
root["mask_mode"]["choices"].append(add_property_choice_json("Vary Strength", BLUR_MASK_DRIVE_AMOUNT, mask_mode));
|
||||
|
||||
@@ -57,6 +57,10 @@ namespace openshot
|
||||
Keyframe vertical_radius; ///< Vertical blur radius keyframe. The size of the vertical blur operation in pixels.
|
||||
Keyframe sigma; ///< Sigma keyframe. The amount of spread in the blur operation. Should be larger than radius.
|
||||
Keyframe iterations; ///< Iterations keyframe. The # of blur iterations per pixel. 3 iterations = Gaussian.
|
||||
Keyframe left; ///< Size of left margin
|
||||
Keyframe top; ///< Size of top margin
|
||||
Keyframe right; ///< Size of right margin
|
||||
Keyframe bottom; ///< Size of bottom margin
|
||||
int mask_mode; ///< How to apply common masks to blur (post-blend or drive-amount).
|
||||
|
||||
/// Blank constructor, useful when using Json to load the effect properties
|
||||
@@ -71,6 +75,19 @@ namespace openshot
|
||||
/// @param new_iterations The curve to adjust the # of iterations (between 1 and 100)
|
||||
Blur(Keyframe new_horizontal_radius, Keyframe new_vertical_radius, Keyframe new_sigma, Keyframe new_iterations);
|
||||
|
||||
/// Default constructor, which takes blur curves and an affected area.
|
||||
///
|
||||
/// @param new_horizontal_radius The curve to adjust the horizontal blur radius (between 0 and 100, rounded to int)
|
||||
/// @param new_vertical_radius The curve to adjust the vertical blur radius (between 0 and 100, rounded to int)
|
||||
/// @param new_sigma The curve to adjust the sigma amount (the size of the blur brush (between 0 and 100), float values accepted)
|
||||
/// @param new_iterations The curve to adjust the # of iterations (between 1 and 100)
|
||||
/// @param new_left The curve to adjust the left margin size (between 0 and 1)
|
||||
/// @param new_top The curve to adjust the top margin size (between 0 and 1)
|
||||
/// @param new_right The curve to adjust the right margin size (between 0 and 1)
|
||||
/// @param new_bottom The curve to adjust the bottom margin size (between 0 and 1)
|
||||
Blur(Keyframe new_horizontal_radius, Keyframe new_vertical_radius, Keyframe new_sigma, Keyframe new_iterations,
|
||||
Keyframe new_left, Keyframe new_top, Keyframe new_right, Keyframe new_bottom);
|
||||
|
||||
/// @brief This method is required for all derived classes of ClipBase, and returns a
|
||||
/// new openshot::Frame object. All Clip keyframes and effects are resolved into
|
||||
/// pixels.
|
||||
|
||||
@@ -494,9 +494,9 @@ std::string Caption::PropertiesJSON(int64_t requested_frame) const {
|
||||
root["fade_in"] = add_property_json("Fade In (Seconds)", fade_in.GetValue(requested_frame), "float", "", &fade_in, 0.0, 3.0, false, requested_frame);
|
||||
root["fade_out"] = add_property_json("Fade Out (Seconds)", fade_out.GetValue(requested_frame), "float", "", &fade_out, 0.0, 3.0, false, requested_frame);
|
||||
root["line_spacing"] = add_property_json("Line Spacing", line_spacing.GetValue(requested_frame), "float", "", &line_spacing, 0.0, 5.0, false, requested_frame);
|
||||
root["left"] = add_property_json("Left Size", left.GetValue(requested_frame), "float", "", &left, 0.0, 0.5, false, requested_frame);
|
||||
root["top"] = add_property_json("Top Size", top.GetValue(requested_frame), "float", "", &top, 0.0, 1.0, false, requested_frame);
|
||||
root["right"] = add_property_json("Right Size", right.GetValue(requested_frame), "float", "", &right, 0.0, 0.5, false, requested_frame);
|
||||
root["left"] = add_property_json("Margin: Left", left.GetValue(requested_frame), "float", "", &left, 0.0, 0.5, false, requested_frame);
|
||||
root["top"] = add_property_json("Margin: Top", top.GetValue(requested_frame), "float", "", &top, 0.0, 1.0, false, requested_frame);
|
||||
root["right"] = add_property_json("Margin: Right", right.GetValue(requested_frame), "float", "", &right, 0.0, 0.5, false, requested_frame);
|
||||
root["caption_text"] = add_property_json("Captions", 0.0, "caption", caption_text, NULL, -1, -1, false, requested_frame);
|
||||
root["caption_font"] = add_property_json("Font", 0.0, "font", font_name, NULL, -1, -1, false, requested_frame);
|
||||
|
||||
|
||||
@@ -187,10 +187,10 @@ std::string Crop::PropertiesJSON(int64_t requested_frame) const {
|
||||
Json::Value root = BasePropertiesJSON(requested_frame);
|
||||
|
||||
// Keyframes
|
||||
root["left"] = add_property_json("Left Size", left.GetValue(requested_frame), "float", "", &left, 0.0, 1.0, false, requested_frame);
|
||||
root["top"] = add_property_json("Top Size", top.GetValue(requested_frame), "float", "", &top, 0.0, 1.0, false, requested_frame);
|
||||
root["right"] = add_property_json("Right Size", right.GetValue(requested_frame), "float", "", &right, 0.0, 1.0, false, requested_frame);
|
||||
root["bottom"] = add_property_json("Bottom Size", bottom.GetValue(requested_frame), "float", "", &bottom, 0.0, 1.0, false, requested_frame);
|
||||
root["left"] = add_property_json("Margin: Left", left.GetValue(requested_frame), "float", "", &left, 0.0, 1.0, false, requested_frame);
|
||||
root["top"] = add_property_json("Margin: Top", top.GetValue(requested_frame), "float", "", &top, 0.0, 1.0, false, requested_frame);
|
||||
root["right"] = add_property_json("Margin: Right", right.GetValue(requested_frame), "float", "", &right, 0.0, 1.0, false, requested_frame);
|
||||
root["bottom"] = add_property_json("Margin: Bottom", bottom.GetValue(requested_frame), "float", "", &bottom, 0.0, 1.0, false, requested_frame);
|
||||
root["x"] = add_property_json("X Offset", x.GetValue(requested_frame), "float", "", &x, -1.0, 1.0, false, requested_frame);
|
||||
root["y"] = add_property_json("Y Offset", y.GetValue(requested_frame), "float", "", &y, -1.0, 1.0, false, requested_frame);
|
||||
|
||||
|
||||
@@ -198,10 +198,10 @@ std::string Pixelate::PropertiesJSON(int64_t requested_frame) const {
|
||||
|
||||
// Keyframes
|
||||
root["pixelization"] = add_property_json("Pixelization", pixelization.GetValue(requested_frame), "float", "", &pixelization, 0.0, 0.9999, false, requested_frame);
|
||||
root["left"] = add_property_json("Left Margin", left.GetValue(requested_frame), "float", "", &left, 0.0, 1.0, false, requested_frame);
|
||||
root["top"] = add_property_json("Top Margin", top.GetValue(requested_frame), "float", "", &top, 0.0, 1.0, false, requested_frame);
|
||||
root["right"] = add_property_json("Right Margin", right.GetValue(requested_frame), "float", "", &right, 0.0, 1.0, false, requested_frame);
|
||||
root["bottom"] = add_property_json("Bottom Margin", bottom.GetValue(requested_frame), "float", "", &bottom, 0.0, 1.0, false, requested_frame);
|
||||
root["left"] = add_property_json("Margin: Left", left.GetValue(requested_frame), "float", "", &left, 0.0, 1.0, false, requested_frame);
|
||||
root["top"] = add_property_json("Margin: Top", top.GetValue(requested_frame), "float", "", &top, 0.0, 1.0, false, requested_frame);
|
||||
root["right"] = add_property_json("Margin: Right", right.GetValue(requested_frame), "float", "", &right, 0.0, 1.0, false, requested_frame);
|
||||
root["bottom"] = add_property_json("Margin: Bottom", bottom.GetValue(requested_frame), "float", "", &bottom, 0.0, 1.0, false, requested_frame);
|
||||
root["mask_mode"] = add_property_json("Mask Mode", mask_mode, "int", "", NULL, 0, 1, false, requested_frame);
|
||||
root["mask_mode"]["choices"].append(add_property_choice_json("Limit to Mask", PIXELATE_MASK_LIMIT_TO_AREA, mask_mode));
|
||||
root["mask_mode"]["choices"].append(add_property_choice_json("Vary Strength", PIXELATE_MASK_VARY_STRENGTH, mask_mode));
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Unit tests for Blur effect
|
||||
*
|
||||
* @ref License
|
||||
*/
|
||||
|
||||
// Copyright (c) 2008-2026 OpenShot Studios, LLC
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#include "openshot_catch.h"
|
||||
|
||||
#include "Frame.h"
|
||||
#include "effects/Blur.h"
|
||||
|
||||
#include <QColor>
|
||||
|
||||
using namespace openshot;
|
||||
|
||||
TEST_CASE("Blur margins limit affected area", "[effect][blur]") {
|
||||
auto frame = std::make_shared<Frame>(1, 8, 1, "#000000");
|
||||
auto image = frame->GetImage();
|
||||
|
||||
for (int x = 0; x < image->width(); ++x)
|
||||
image->setPixelColor(x, 0, (x % 2 == 0) ? QColor(255, 255, 255, 255) : QColor(0, 0, 0, 255));
|
||||
|
||||
Blur effect(Keyframe(1.0), Keyframe(0.0), Keyframe(3.0), Keyframe(1.0),
|
||||
Keyframe(0.5), Keyframe(0.0), Keyframe(0.0), Keyframe(0.0));
|
||||
auto out = effect.GetFrame(frame, 1);
|
||||
auto out_image = out->GetImage();
|
||||
|
||||
CHECK(out_image->pixelColor(0, 0) == QColor(255, 255, 255, 255));
|
||||
CHECK(out_image->pixelColor(1, 0) == QColor(0, 0, 0, 255));
|
||||
CHECK(out_image->pixelColor(2, 0) == QColor(255, 255, 255, 255));
|
||||
CHECK(out_image->pixelColor(3, 0) == QColor(0, 0, 0, 255));
|
||||
|
||||
CHECK(out_image->pixelColor(4, 0).red() < 255);
|
||||
CHECK(out_image->pixelColor(4, 0).red() > 0);
|
||||
CHECK(out_image->pixelColor(5, 0).red() > 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Blur margin properties serialize", "[effect][blur][json]") {
|
||||
Blur effect(Keyframe(6.0), Keyframe(6.0), Keyframe(3.0), Keyframe(3.0),
|
||||
Keyframe(0.1), Keyframe(0.2), Keyframe(0.3), Keyframe(0.4));
|
||||
|
||||
Json::Value json = effect.JsonValue();
|
||||
REQUIRE(json["left"].isObject());
|
||||
REQUIRE(json["top"].isObject());
|
||||
REQUIRE(json["right"].isObject());
|
||||
REQUIRE(json["bottom"].isObject());
|
||||
|
||||
Blur copy;
|
||||
copy.SetJsonValue(json);
|
||||
|
||||
CHECK(copy.left.GetValue(1) == Approx(0.1));
|
||||
CHECK(copy.top.GetValue(1) == Approx(0.2));
|
||||
CHECK(copy.right.GetValue(1) == Approx(0.3));
|
||||
CHECK(copy.bottom.GetValue(1) == Approx(0.4));
|
||||
}
|
||||
@@ -54,6 +54,7 @@ set(OPENSHOT_TESTS
|
||||
# Effects
|
||||
AudioVisualization
|
||||
BeatSync
|
||||
Blur
|
||||
ColorGrade
|
||||
ColorMap
|
||||
ChromaKey
|
||||
|
||||
Reference in New Issue
Block a user