OpenShot Library | libopenshot 0.3.3
Loading...
Searching...
No Matches
Mask.cpp
Go to the documentation of this file.
1
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
22#ifdef USE_IMAGEMAGICK
23 #include "ImageReader.h"
24#endif
25
26using namespace openshot;
27
29Mask::Mask() : reader(NULL), replace_image(false), needs_refresh(true) {
30 // Init effect properties
31 init_effect_details();
32}
33
34// Default constructor
35Mask::Mask(ReaderBase *mask_reader, Keyframe mask_brightness, Keyframe mask_contrast) :
36 reader(mask_reader), brightness(mask_brightness), contrast(mask_contrast), replace_image(false), needs_refresh(true)
37{
38 // Init effect properties
39 init_effect_details();
40}
41
42// Init effect settings
43void Mask::init_effect_details()
44{
47
49 info.class_name = "Mask";
50 info.name = "Alpha Mask / Wipe Transition";
51 info.description = "Uses a grayscale mask image to gradually wipe / transition between 2 images.";
52 info.has_audio = false;
53 info.has_video = true;
54}
55
56// This method is required for all derived classes of EffectBase, and returns a
57// modified openshot::Frame object
58std::shared_ptr<openshot::Frame> Mask::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number) {
59 // Get the mask image (from the mask reader)
60 std::shared_ptr<QImage> frame_image = frame->GetImage();
61
62 // Check if mask reader is open
63 #pragma omp critical (open_mask_reader)
64 {
65 if (reader && !reader->IsOpen())
66 reader->Open();
67 }
68
69 // No reader (bail on applying the mask)
70 if (!reader)
71 return frame;
72
73 // Get mask image (if missing or different size than frame image)
74 #pragma omp critical (open_mask_reader)
75 {
76 if (!original_mask || !reader->info.has_single_image || needs_refresh ||
77 (original_mask && original_mask->size() != frame_image->size())) {
78
79 // Only get mask if needed
80 auto mask_without_sizing = std::make_shared<QImage>(
81 *reader->GetFrame(frame_number)->GetImage());
82
83 // Resize mask image to match frame size
84 original_mask = std::make_shared<QImage>(
85 mask_without_sizing->scaled(
86 frame_image->width(), frame_image->height(),
87 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
88 }
89 }
90
91 // Refresh no longer needed
92 needs_refresh = false;
93
94 // Get pixel arrays
95 unsigned char *pixels = (unsigned char *) frame_image->bits();
96 unsigned char *mask_pixels = (unsigned char *) original_mask->bits();
97
98 double contrast_value = (contrast.GetValue(frame_number));
99 double brightness_value = (brightness.GetValue(frame_number));
100
101 // Loop through mask pixels, and apply average gray value to frame alpha channel
102 for (int pixel = 0, byte_index=0; pixel < original_mask->width() * original_mask->height(); pixel++, byte_index+=4)
103 {
104 // Get the RGB values from the pixel
105 int R = mask_pixels[byte_index];
106 int G = mask_pixels[byte_index + 1];
107 int B = mask_pixels[byte_index + 2];
108 int A = mask_pixels[byte_index + 3];
109
110 // Get the average luminosity
111 int gray_value = qGray(R, G, B);
112
113 // Adjust the brightness
114 gray_value += (255 * brightness_value);
115
116 // Adjust the contrast
117 float factor = (20 / std::fmax(0.00001, 20.0 - contrast_value));
118 gray_value = (factor * (gray_value - 128) + 128);
119
120 // Calculate the % change in alpha
121 float alpha_percent = float(constrain(A - gray_value)) / 255.0;
122
123 // Set the alpha channel to the gray value
124 if (replace_image) {
125 // Replace frame pixels with gray value (including alpha channel)
126 pixels[byte_index + 0] = constrain(255 * alpha_percent);
127 pixels[byte_index + 1] = constrain(255 * alpha_percent);
128 pixels[byte_index + 2] = constrain(255 * alpha_percent);
129 pixels[byte_index + 3] = constrain(255 * alpha_percent);
130 } else {
131 // Multiply new alpha value with all the colors (since we are using a premultiplied
132 // alpha format)
133 pixels[byte_index + 0] *= alpha_percent;
134 pixels[byte_index + 1] *= alpha_percent;
135 pixels[byte_index + 2] *= alpha_percent;
136 pixels[byte_index + 3] *= alpha_percent;
137 }
138
139 }
140
141 // return the modified frame
142 return frame;
143}
144
145// Generate JSON string of this object
146std::string Mask::Json() const {
147
148 // Return formatted string
149 return JsonValue().toStyledString();
150}
151
152// Generate Json::Value for this object
153Json::Value Mask::JsonValue() const {
154
155 // Create root json object
156 Json::Value root = EffectBase::JsonValue(); // get parent properties
157 root["type"] = info.class_name;
158 root["brightness"] = brightness.JsonValue();
159 root["contrast"] = contrast.JsonValue();
160 if (reader)
161 root["reader"] = reader->JsonValue();
162 else
163 root["reader"] = Json::objectValue;
164 root["replace_image"] = replace_image;
165
166 // return JsonValue
167 return root;
168}
169
170// Load JSON string into this object
171void Mask::SetJson(const std::string value) {
172
173 // Parse JSON string into JSON objects
174 try
175 {
176 const Json::Value root = openshot::stringToJson(value);
177 // Set all values that match
178 SetJsonValue(root);
179 }
180 catch (const std::exception& e)
181 {
182 // Error parsing JSON (or missing keys)
183 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
184 }
185}
186
187// Load Json::Value into this object
188void Mask::SetJsonValue(const Json::Value root) {
189
190 // Set parent data
192
193 // Set data from Json (if key is found)
194 if (!root["replace_image"].isNull())
195 replace_image = root["replace_image"].asBool();
196 if (!root["brightness"].isNull())
197 brightness.SetJsonValue(root["brightness"]);
198 if (!root["contrast"].isNull())
199 contrast.SetJsonValue(root["contrast"]);
200 if (!root["reader"].isNull()) // does Json contain a reader?
201 {
202 #pragma omp critical (open_mask_reader)
203 {
204 // This reader has changed, so refresh cached assets
205 needs_refresh = true;
206
207 if (!root["reader"]["type"].isNull()) // does the reader Json contain a 'type'?
208 {
209 // Close previous reader (if any)
210 if (reader) {
211 // Close and delete existing reader (if any)
212 reader->Close();
213 delete reader;
214 reader = NULL;
215 }
216
217 // Create new reader (and load properties)
218 std::string type = root["reader"]["type"].asString();
219
220 if (type == "FFmpegReader") {
221
222 // Create new reader
223 reader = new FFmpegReader(root["reader"]["path"].asString());
224 reader->SetJsonValue(root["reader"]);
225
226 #ifdef USE_IMAGEMAGICK
227 } else if (type == "ImageReader") {
228
229 // Create new reader
230 reader = new ImageReader(root["reader"]["path"].asString());
231 reader->SetJsonValue(root["reader"]);
232 #endif
233
234 } else if (type == "QtImageReader") {
235
236 // Create new reader
237 reader = new QtImageReader(root["reader"]["path"].asString());
238 reader->SetJsonValue(root["reader"]);
239
240 } else if (type == "ChunkReader") {
241
242 // Create new reader
243 reader = new ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt());
244 reader->SetJsonValue(root["reader"]);
245
246 }
247 }
248
249 }
250 }
251
252}
253
254// Get all properties for a specific frame
255std::string Mask::PropertiesJSON(int64_t requested_frame) const {
256
257 // Generate JSON properties list
258 Json::Value root = BasePropertiesJSON(requested_frame);
259
260 // Add replace_image choices (dropdown style)
261 root["replace_image"] = add_property_json("Replace Image", replace_image, "int", "", NULL, 0, 1, false, requested_frame);
262 root["replace_image"]["choices"].append(add_property_choice_json("Yes", true, replace_image));
263 root["replace_image"]["choices"].append(add_property_choice_json("No", false, replace_image));
264
265 // Keyframes
266 root["brightness"] = add_property_json("Brightness", brightness.GetValue(requested_frame), "float", "", &brightness, -1.0, 1.0, false, requested_frame);
267 root["contrast"] = add_property_json("Contrast", contrast.GetValue(requested_frame), "float", "", &contrast, 0, 20, false, requested_frame);
268
269 if (reader)
270 root["reader"] = add_property_json("Source", 0.0, "reader", reader->Json(), NULL, 0, 1, false, requested_frame);
271 else
272 root["reader"] = add_property_json("Source", 0.0, "reader", "{}", NULL, 0, 1, false, requested_frame);
273
274 // Return formatted string
275 return root.toStyledString();
276}
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.
int constrain(int color_value)
Constrain a color value from 0 to 255.
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 SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition KeyFrame.cpp:372
double GetValue(int64_t index) const
Get the value at a specific index.
Definition KeyFrame.cpp:258
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition KeyFrame.cpp:339
void SetJson(const std::string value) override
Load JSON string into this object.
Definition Mask.cpp:171
Mask()
Blank constructor, useful when using Json to load the effect properties.
Definition Mask.cpp:29
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:255
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition Mask.cpp:153
std::string Json() const override
Generate JSON string of this object.
Definition Mask.cpp:146
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition Mask.cpp:188
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
virtual bool IsOpen()=0
Determine if reader is open or closed.
openshot::ReaderInfo info
Information about the current media file.
Definition ReaderBase.h:88
virtual std::string Json() const =0
Generate JSON string of this object.
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
virtual void Open()=0
Open the reader (and start consuming resources, such as images or video files)
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
virtual void Close()=0
Close the reader (and any resources it was consuming)
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
bool has_single_image
Determines if this file only contains a single image.
Definition ReaderBase.h:42