25 inline float glow_clampf(
float value,
float low,
float high) {
33 inline void glow_blur_horizontal(
const std::vector<float>& src, std::vector<float>& dst,
int w,
int h,
int radius) {
39 const float inv = 1.0f /
static_cast<float>((radius * 2) + 1);
40 #pragma omp parallel for schedule(static)
41 for (
int y = 0; y < h; ++y) {
45 float first = src[ti];
46 float last = src[ti + w - 1];
47 float value =
static_cast<float>(radius + 1) * first;
48 for (
int j = 0; j < radius; ++j)
50 for (
int x = 0; x <= radius; ++x) {
51 value += src[ri++] - first;
52 dst[ti++] = value * inv;
54 for (
int x = radius + 1; x < w - radius; ++x) {
55 value += src[ri++] - src[li++];
56 dst[ti++] = value * inv;
58 for (
int x = w - radius; x < w; ++x) {
59 value += last - src[li++];
60 dst[ti++] = value * inv;
65 inline void glow_blur_vertical(
const std::vector<float>& src, std::vector<float>& dst,
int w,
int h,
int radius) {
71 const float inv = 1.0f /
static_cast<float>((radius * 2) + 1);
72 #pragma omp parallel for schedule(static)
73 for (
int x = 0; x < w; ++x) {
76 int ri = ti + (radius * w);
77 float first = src[ti];
78 float last = src[ti + (w * (h - 1))];
79 float value =
static_cast<float>(radius + 1) * first;
80 for (
int j = 0; j < radius; ++j)
81 value += src[ti + (j * w)];
82 for (
int y = 0; y <= radius; ++y) {
83 value += src[ri] - first;
84 dst[ti] = value * inv;
88 for (
int y = radius + 1; y < h - radius; ++y) {
89 value += src[ri] - src[li];
90 dst[ti] = value * inv;
95 for (
int y = h - radius; y < h; ++y) {
96 value += last - src[li];
97 dst[ti] = value * inv;
104 inline void glow_apply_box_blur(std::vector<float>& mask,
int w,
int h,
int radius,
int iterations) {
105 if (radius <= 0 || iterations <= 0 || w <= 0 || h <= 0)
108 std::vector<float> temp(mask.size());
109 for (
int i = 0; i < iterations; ++i) {
110 glow_blur_horizontal(mask, temp, w, h, radius);
111 glow_blur_vertical(temp, mask, w, h, radius);
115 inline void glow_spread_horizontal(
const std::vector<float>& src, std::vector<float>& dst,
int w,
int h,
int radius) {
121 #pragma omp parallel for schedule(static)
122 for (
int y = 0; y < h; ++y) {
123 const int row = y * w;
124 for (
int x = 0; x < w; ++x) {
125 const int left = std::max(0, x - radius);
126 const int right = std::min(w - 1, x + radius);
128 for (
int sample_x = left; sample_x <= right; ++sample_x)
129 value = std::max(value, src[row + sample_x]);
130 dst[row + x] = value;
135 inline void glow_spread_vertical(
const std::vector<float>& src, std::vector<float>& dst,
int w,
int h,
int radius) {
141 #pragma omp parallel for schedule(static)
142 for (
int y = 0; y < h; ++y) {
143 const int top = std::max(0, y - radius);
144 const int bottom = std::min(h - 1, y + radius);
145 for (
int x = 0; x < w; ++x) {
147 for (
int sample_y = top; sample_y <= bottom; ++sample_y)
148 value = std::max(value, src[(sample_y * w) + x]);
149 dst[(y * w) + x] = value;
154 inline void glow_apply_spread(std::vector<float>& mask,
int w,
int h,
int radius) {
155 if (radius <= 0 || w <= 0 || h <= 0)
158 std::vector<float> temp(mask.size());
159 glow_spread_horizontal(mask, temp, w, h, radius);
160 glow_spread_vertical(temp, mask, w, h, radius);
165 : mode(
GLOW_MODE_OUTER), opacity(0.45), blur_radius(20.0), spread(0.10), color(
"#fff4c2") {
166 init_effect_details();
170 : mode(
GLOW_MODE_OUTER), opacity(new_opacity), blur_radius(new_blur_radius), spread(new_spread), color(new_color) {
171 init_effect_details();
174 void Glow::init_effect_details()
179 info.
description =
"Add an outer or inner glow based on visible pixels.";
185 std::shared_ptr<openshot::Frame>
Glow::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number)
187 auto frame_image = frame->GetImage();
188 if (!frame_image || frame_image->isNull())
191 const int w = frame_image->width();
192 const int h = frame_image->height();
193 if (w <= 0 || h <= 0)
196 QImage source = frame_image->copy();
197 const unsigned char* source_pixels =
reinterpret_cast<const unsigned char*
>(source.constBits());
199 const float opacity_value = glow_clampf(
static_cast<float>(
opacity.
GetValue(frame_number)), 0.0f, 1.0f);
201 const float spread_value = glow_clampf(
static_cast<float>(
spread.
GetValue(frame_number)), 0.0f, 1.0f);
203 const float color_r =
static_cast<float>(rgba[0]);
204 const float color_g =
static_cast<float>(rgba[1]);
205 const float color_b =
static_cast<float>(rgba[2]);
206 const float color_a =
static_cast<float>(rgba[3]) / 255.0f;
208 std::vector<float> alpha_mask(
static_cast<size_t>(w * h));
209 #pragma omp parallel for schedule(static)
210 for (
int i = 0; i < (w * h); ++i) {
211 const float alpha =
static_cast<float>(source_pixels[(i * 4) + 3]) / 255.0f;
212 alpha_mask[i] = alpha;
215 const int spread_radius = std::max(0,
static_cast<int>(std::lround(
static_cast<float>(blur_value) * spread_value)));
216 if (spread_radius > 0)
217 glow_apply_spread(alpha_mask, w, h, spread_radius);
219 std::vector<float> blurred_mask = alpha_mask;
221 glow_apply_box_blur(blurred_mask, w, h, blur_value, 3);
223 QImage glow_overlay(w, h, QImage::Format_RGBA8888_Premultiplied);
224 glow_overlay.fill(Qt::transparent);
225 unsigned char* glow_pixels =
reinterpret_cast<unsigned char*
>(glow_overlay.bits());
228 #pragma omp parallel for schedule(static)
229 for (
int i = 0; i < (w * h); ++i) {
230 const float overlay_alpha = glow_clampf(blurred_mask[i] * opacity_value * color_a, 0.0f, 1.0f);
231 const int idx = i * 4;
232 glow_pixels[idx + 0] =
static_cast<unsigned char>(color_r * overlay_alpha);
233 glow_pixels[idx + 1] =
static_cast<unsigned char>(color_g * overlay_alpha);
234 glow_pixels[idx + 2] =
static_cast<unsigned char>(color_b * overlay_alpha);
235 glow_pixels[idx + 3] =
static_cast<unsigned char>(255.0f * overlay_alpha);
239 std::vector<float> inverse_mask(
static_cast<size_t>(w * h));
240 #pragma omp parallel for schedule(static)
241 for (
int i = 0; i < (w * h); ++i)
242 inverse_mask[i] = 1.0f - alpha_mask[i];
245 glow_apply_box_blur(inverse_mask, w, h, blur_value, 3);
247 #pragma omp parallel for schedule(static)
248 for (
int i = 0; i < (w * h); ++i) {
249 const float inner = glow_clampf(inverse_mask[i] * alpha_mask[i] * opacity_value * color_a, 0.0f, 1.0f);
250 const int idx = i * 4;
251 glow_pixels[idx + 0] =
static_cast<unsigned char>(color_r * inner);
252 glow_pixels[idx + 1] =
static_cast<unsigned char>(color_g * inner);
253 glow_pixels[idx + 2] =
static_cast<unsigned char>(color_b * inner);
254 glow_pixels[idx + 3] =
static_cast<unsigned char>(255.0f * inner);
258 QImage result(w, h, QImage::Format_RGBA8888_Premultiplied);
259 result.fill(Qt::transparent);
261 QPainter painter(&result);
262 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform,
true);
264 painter.drawImage(0, 0, glow_overlay);
265 painter.drawImage(0, 0, source);
267 painter.drawImage(0, 0, source);
268 painter.drawImage(0, 0, glow_overlay);
272 *frame_image = result;
296 catch (
const std::exception& e) {
297 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
303 if (!root[
"mode"].isNull())
304 mode = root[
"mode"].asInt();
305 if (!root[
"opacity"].isNull())
307 if (!root[
"blur_radius"].isNull())
309 if (!root[
"spread"].isNull())
311 if (!root[
"color"].isNull())
328 return root.toStyledString();
Header file for all Exception classes.
Header file for Glow effect class.
Json::Value add_property_choice_json(std::string name, int value, int selected_value) const
Generate JSON choice for a property (dropdown properties)
Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe *keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const
Generate JSON for a property.
This class represents a color (used on the timeline and clips)
openshot::Keyframe blue
Curve representing the red value (0 - 255)
openshot::Keyframe red
Curve representing the red value (0 - 255)
openshot::Keyframe green
Curve representing the green value (0 - 255)
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
openshot::Keyframe alpha
Curve representing the alpha value (0 - 255)
std::vector< int > GetColorRGBA(int64_t frame_number)
Json::Value JsonValue() const
Generate Json::Value for this object.
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
Json::Value BasePropertiesJSON(int64_t requested_frame) const
Generate JSON object of base properties (recommended to be used by all effects)
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
EffectInfoStruct info
Information about the current effect.
std::string Json() const override
Generate JSON string of this object.
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number) override
This method is required for all derived classes of ClipBase, and returns a new openshot::Frame object...
Keyframe blur_radius
Blur radius in pixels.
std::string PropertiesJSON(int64_t requested_frame) const override
void SetJson(const std::string value) override
Load JSON string into this object.
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Json::Value JsonValue() const override
Generate Json::Value for this object.
Keyframe opacity
Overall glow opacity.
Color color
Glow tint color.
Keyframe spread
Boosts the source alpha before blur.
int mode
Outer or inner glow mode.
Exception for invalid JSON.
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
int GetInt(int64_t index) const
Get the rounded INT value at a specific index.
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
double GetValue(int64_t index) const
Get the value at a specific index.
Json::Value JsonValue() const
Generate Json::Value for this object.
This namespace is the default namespace for all code in the openshot library.
const Json::Value stringToJson(const std::string value)
bool has_video
Determines if this effect manipulates the image of a frame.
bool apply_before_clip
Apply effect to source before we evaluate the clip's keyframes.
bool has_audio
Determines if this effect manipulates the audio of a frame.
std::string class_name
The class name of the effect.
std::string name
The name of the effect.
std::string description
The description of this effect and what it does.