OpenShot Library | libopenshot 0.5.0
Loading...
Searching...
No Matches
Mask.cpp
Go to the documentation of this file.
1
8
9// Copyright (c) 2008-2019 OpenShot Studios, LLC
10//
11// SPDX-License-Identifier: LGPL-3.0-or-later
12
13#include "Mask.h"
14
15#include "Exceptions.h"
16
17#include "ReaderBase.h"
18#include "ChunkReader.h"
19#include "FFmpegReader.h"
20#include "QtImageReader.h"
21#include <omp.h>
22
23#ifdef USE_IMAGEMAGICK
24 #include "ImageReader.h"
25#endif
26
27using namespace openshot;
28
30Mask::Mask() : reader(NULL), replace_image(false), needs_refresh(true) {
31 // Init effect properties
32 init_effect_details();
33}
34
35// Default constructor
36Mask::Mask(ReaderBase *mask_reader, Keyframe mask_brightness, Keyframe mask_contrast) :
37 reader(mask_reader), brightness(mask_brightness), contrast(mask_contrast), replace_image(false), needs_refresh(true)
38{
39 // Init effect properties
40 init_effect_details();
41}
42
43// Init effect settings
44void Mask::init_effect_details()
45{
48
50 info.class_name = "Mask";
51 info.name = "Alpha Mask / Wipe Transition";
52 info.description = "Uses a grayscale mask image to gradually wipe / transition between 2 images.";
53 info.has_audio = false;
54 info.has_video = true;
55}
56
57// This method is required for all derived classes of EffectBase, and returns a
58// modified openshot::Frame object
59std::shared_ptr<openshot::Frame> Mask::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number) {
60 // Get the mask image (from the mask reader)
61 std::shared_ptr<QImage> frame_image = frame->GetImage();
62
63 // Check if mask reader is open
64 #pragma omp critical (open_mask_reader)
65 {
66 if (reader && !reader->IsOpen())
67 reader->Open();
68 }
69
70 // No reader (bail on applying the mask)
71 if (!reader)
72 return frame;
73
74 // Get mask image (if missing or different size than frame image)
75 #pragma omp critical (open_mask_reader)
76 {
77 if (!original_mask || !reader->info.has_single_image || needs_refresh ||
78 (original_mask && original_mask->size() != frame_image->size())) {
79
80 // Only get mask if needed
81 auto mask_without_sizing = std::make_shared<QImage>(
82 *reader->GetFrame(frame_number)->GetImage());
83
84 // Resize mask image to match frame size
85 original_mask = std::make_shared<QImage>(
86 mask_without_sizing->scaled(
87 frame_image->width(), frame_image->height(),
88 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
89 }
90 }
91
92 // Once we've done the necessary resizing, we no longer need to refresh again
93 needs_refresh = false;
94
95 // Grab raw pointers and dimensions one time
96 unsigned char* pixels = reinterpret_cast<unsigned char*>(frame_image->bits());
97 unsigned char* mask_pixels = reinterpret_cast<unsigned char*>(original_mask->bits());
98 int width = original_mask->width();
99 int height = original_mask->height();
100 int num_pixels = width * height; // total pixel count
101
102 // Evaluate brightness and contrast keyframes just once
103 double contrast_value = contrast.GetValue(frame_number);
104 double brightness_value = brightness.GetValue(frame_number);
105
106 int brightness_adj = static_cast<int>(255 * brightness_value);
107 float contrast_factor = 20.0f / std::max(0.00001f, 20.0f - static_cast<float>(contrast_value));
108
109 // Iterate over every pixel in parallel
110#pragma omp parallel for schedule(static)
111 for (int i = 0; i < num_pixels; ++i)
112 {
113 int idx = i * 4;
114
115 int R = mask_pixels[idx + 0];
116 int G = mask_pixels[idx + 1];
117 int B = mask_pixels[idx + 2];
118 int A = mask_pixels[idx + 3];
119
120 // Compute base gray, then apply brightness + contrast
121 int gray = qGray(R, G, B);
122 gray += brightness_adj;
123 gray = static_cast<int>(contrast_factor * (gray - 128) + 128);
124
125 // Clamp (A - gray) into [0, 255]
126 int diff = A - gray;
127 if (diff < 0) diff = 0;
128 else if (diff > 255) diff = 255;
129
130 // Calculate the % change in alpha
131 float alpha_percent = static_cast<float>(diff) / 255.0f;
132
133 // Set the alpha channel to the gray value
134 if (replace_image) {
135 // Replace frame pixels with gray value (including alpha channel)
136 auto new_val = static_cast<unsigned char>(diff);
137 pixels[idx + 0] = new_val;
138 pixels[idx + 1] = new_val;
139 pixels[idx + 2] = new_val;
140 pixels[idx + 3] = new_val;
141 } else {
142 // Premultiplied RGBA → multiply each channel by alpha_percent
143 pixels[idx + 0] = static_cast<unsigned char>(pixels[idx + 0] * alpha_percent);
144 pixels[idx + 1] = static_cast<unsigned char>(pixels[idx + 1] * alpha_percent);
145 pixels[idx + 2] = static_cast<unsigned char>(pixels[idx + 2] * alpha_percent);
146 pixels[idx + 3] = static_cast<unsigned char>(pixels[idx + 3] * alpha_percent);
147 }
148
149 }
150
151 // return the modified frame
152 return frame;
153}
154
155// Generate JSON string of this object
156std::string Mask::Json() const {
157
158 // Return formatted string
159 return JsonValue().toStyledString();
160}
161
162// Generate Json::Value for this object
163Json::Value Mask::JsonValue() const {
164
165 // Create root json object
166 Json::Value root = EffectBase::JsonValue(); // get parent properties
167 root["type"] = info.class_name;
168 root["brightness"] = brightness.JsonValue();
169 root["contrast"] = contrast.JsonValue();
170 if (reader)
171 root["reader"] = reader->JsonValue();
172 else
173 root["reader"] = Json::objectValue;
174 root["replace_image"] = replace_image;
175
176 // return JsonValue
177 return root;
178}
179
180// Load JSON string into this object
181void Mask::SetJson(const std::string value) {
182
183 // Parse JSON string into JSON objects
184 try
185 {
186 const Json::Value root = openshot::stringToJson(value);
187 // Set all values that match
188 SetJsonValue(root);
189 }
190 catch (const std::exception& e)
191 {
192 // Error parsing JSON (or missing keys)
193 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
194 }
195}
196
197// Load Json::Value into this object
198void Mask::SetJsonValue(const Json::Value root) {
199
200 // Set parent data
202
203 // Set data from Json (if key is found)
204 if (!root["replace_image"].isNull())
205 replace_image = root["replace_image"].asBool();
206 if (!root["brightness"].isNull())
207 brightness.SetJsonValue(root["brightness"]);
208 if (!root["contrast"].isNull())
209 contrast.SetJsonValue(root["contrast"]);
210 if (!root["reader"].isNull()) // does Json contain a reader?
211 {
212 #pragma omp critical (open_mask_reader)
213 {
214 // This reader has changed, so refresh cached assets
215 needs_refresh = true;
216
217 if (!root["reader"]["type"].isNull()) // does the reader Json contain a 'type'?
218 {
219 // Close previous reader (if any)
220 if (reader) {
221 // Close and delete existing reader (if any)
222 reader->Close();
223 delete reader;
224 reader = NULL;
225 }
226
227 // Create new reader (and load properties)
228 std::string type = root["reader"]["type"].asString();
229
230 if (type == "FFmpegReader") {
231
232 // Create new reader
233 reader = new FFmpegReader(root["reader"]["path"].asString());
234 reader->SetJsonValue(root["reader"]);
235
236 #ifdef USE_IMAGEMAGICK
237 } else if (type == "ImageReader") {
238
239 // Create new reader
240 reader = new ImageReader(root["reader"]["path"].asString());
241 reader->SetJsonValue(root["reader"]);
242 #endif
243
244 } else if (type == "QtImageReader") {
245
246 // Create new reader
247 reader = new QtImageReader(root["reader"]["path"].asString());
248 reader->SetJsonValue(root["reader"]);
249
250 } else if (type == "ChunkReader") {
251
252 // Create new reader
253 reader = new ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt());
254 reader->SetJsonValue(root["reader"]);
255
256 }
257 }
258
259 }
260 }
261
262}
263
264// Get all properties for a specific frame
265std::string Mask::PropertiesJSON(int64_t requested_frame) const {
266
267 // Generate JSON properties list
268 Json::Value root = BasePropertiesJSON(requested_frame);
269
270 // Add replace_image choices (dropdown style)
271 root["replace_image"] = add_property_json("Replace Image", replace_image, "int", "", NULL, 0, 1, false, requested_frame);
272 root["replace_image"]["choices"].append(add_property_choice_json("Yes", true, replace_image));
273 root["replace_image"]["choices"].append(add_property_choice_json("No", false, replace_image));
274
275 // Keyframes
276 root["brightness"] = add_property_json("Brightness", brightness.GetValue(requested_frame), "float", "", &brightness, -1.0, 1.0, false, requested_frame);
277 root["contrast"] = add_property_json("Contrast", contrast.GetValue(requested_frame), "float", "", &contrast, 0, 20, false, requested_frame);
278
279 if (reader)
280 root["reader"] = add_property_json("Source", 0.0, "reader", reader->Json(), NULL, 0, 1, false, requested_frame);
281 else
282 root["reader"] = add_property_json("Source", 0.0, "reader", "{}", NULL, 0, 1, false, requested_frame);
283
284 // Return formatted string
285 return root.toStyledString();
286}
Header file for ChunkReader class.
Header file for all Exception classes.
Header file for FFmpegReader class.
Header file for ImageReader class.
Header file for Mask class.
Header file for QtImageReader class.
Header file for ReaderBase class.
This class reads a special chunk-formatted file, which can be easily shared in a distributed environm...
Definition ChunkReader.h:79
Json::Value add_property_choice_json(std::string name, int value, int selected_value) const
Generate JSON choice for a property (dropdown properties).
Definition ClipBase.cpp:132
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.
Definition ClipBase.cpp:96
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.
Definition EffectBase.h:69
This class uses the FFmpeg libraries, to open video files and audio files, and return openshot::Frame...
This class uses the ImageMagick++ libraries, to open image files, and return openshot::Frame objects ...
Definition ImageReader.h:56
Exception for invalid JSON.
Definition Exceptions.h:218
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
Definition KeyFrame.h:53
void SetJson(const std::string value) override
Load JSON string into this object.
Definition Mask.cpp:181
Mask()
Blank constructor, useful when using Json to load the effect properties.
Definition Mask.cpp:30
bool replace_image
Replace the frame image with a grayscale image representing the mask. Great for debugging a mask.
Definition Mask.h:47
Keyframe brightness
Brightness keyframe to control the wipe / mask effect. A constant value here will prevent animation.
Definition Mask.h:48
std::string PropertiesJSON(int64_t requested_frame) const override
Definition Mask.cpp:265
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition Mask.cpp:163
std::string Json() const override
Generate JSON string of this object.
Definition Mask.cpp:156
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition Mask.cpp:198
Keyframe contrast
Contrast keyframe to control the hardness of the wipe effect / mask.
Definition Mask.h:49
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...
Definition Mask.h:69
This class uses the Qt library, to open image files, and return openshot::Frame objects containing th...
This abstract class is the base class, used by all readers in libopenshot.
Definition ReaderBase.h:76
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
ChunkVersion
This enumeration allows the user to choose which version of the chunk they would like (low,...
Definition ChunkReader.h:50
const Json::Value stringToJson(const std::string value)
Definition Json.cpp:16
bool has_video
Determines if this effect manipulates the image of a frame.
Definition EffectBase.h:40
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition EffectBase.h:41
std::string class_name
The class name of the effect.
Definition EffectBase.h:36
std::string name
The name of the effect.
Definition EffectBase.h:37
std::string description
The description of this effect and what it does.
Definition EffectBase.h:38