Loading...
Searching...
No Matches
Mask.cpp
Go to the documentation of this file.
// Copyright (c) 2008-2019 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "Mask.h"
#include "Exceptions.h"
#include "ReaderBase.h"
#include "ChunkReader.h"
#include "FFmpegReader.h"
#include "QtImageReader.h"
#include <omp.h>
#ifdef USE_IMAGEMAGICK
#include "ImageReader.h"
#endif
using namespace openshot;
Mask::Mask() : reader(NULL), replace_image(false), needs_refresh(true) {
// Init effect properties
init_effect_details();
}
// Default constructor
Mask::Mask(ReaderBase *mask_reader, Keyframe mask_brightness, Keyframe mask_contrast) :
reader(mask_reader), brightness(mask_brightness), contrast(mask_contrast), replace_image(false), needs_refresh(true)
{
// Init effect properties
init_effect_details();
}
// Init effect settings
void Mask::init_effect_details()
{
InitEffectInfo();
info.class_name = "Mask";
info.name = "Alpha Mask / Wipe Transition";
info.description = "Uses a grayscale mask image to gradually wipe / transition between 2 images.";
info.has_audio = false;
info.has_video = true;
}
// This method is required for all derived classes of EffectBase, and returns a
// modified openshot::Frame object
std::shared_ptr<openshot::Frame> Mask::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number) {
// Get the mask image (from the mask reader)
std::shared_ptr<QImage> frame_image = frame->GetImage();
// Check if mask reader is open
#pragma omp critical (open_mask_reader)
{
if (reader && !reader->IsOpen())
reader->Open();
}
// No reader (bail on applying the mask)
if (!reader)
return frame;
// Get mask image (if missing or different size than frame image)
#pragma omp critical (open_mask_reader)
{
if (!original_mask || !reader->info.has_single_image || needs_refresh ||
(original_mask && original_mask->size() != frame_image->size())) {
// Only get mask if needed
auto mask_without_sizing = std::make_shared<QImage>(
*reader->GetFrame(frame_number)->GetImage());
// Resize mask image to match frame size
original_mask = std::make_shared<QImage>(
mask_without_sizing->scaled(
frame_image->width(), frame_image->height(),
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
}
// Once we've done the necessary resizing, we no longer need to refresh again
needs_refresh = false;
// Grab raw pointers and dimensions one time
unsigned char* pixels = reinterpret_cast<unsigned char*>(frame_image->bits());
unsigned char* mask_pixels = reinterpret_cast<unsigned char*>(original_mask->bits());
int width = original_mask->width();
int height = original_mask->height();
int num_pixels = width * height; // total pixel count
// Evaluate brightness and contrast keyframes just once
double contrast_value = contrast.GetValue(frame_number);
double brightness_value = brightness.GetValue(frame_number);
int brightness_adj = static_cast<int>(255 * brightness_value);
float contrast_factor = 20.0f / std::max(0.00001f, 20.0f - static_cast<float>(contrast_value));
// Iterate over every pixel in parallel
#pragma omp parallel for schedule(static)
for (int i = 0; i < num_pixels; ++i)
{
int idx = i * 4;
int R = mask_pixels[idx + 0];
int G = mask_pixels[idx + 1];
int B = mask_pixels[idx + 2];
int A = mask_pixels[idx + 3];
// Compute base gray, then apply brightness + contrast
int gray = qGray(R, G, B);
gray += brightness_adj;
gray = static_cast<int>(contrast_factor * (gray - 128) + 128);
// Clamp (A - gray) into [0, 255]
int diff = A - gray;
if (diff < 0) diff = 0;
else if (diff > 255) diff = 255;
// Calculate the % change in alpha
float alpha_percent = static_cast<float>(diff) / 255.0f;
// Set the alpha channel to the gray value
if (replace_image) {
// Replace frame pixels with gray value (including alpha channel)
auto new_val = static_cast<unsigned char>(diff);
pixels[idx + 0] = new_val;
pixels[idx + 1] = new_val;
pixels[idx + 2] = new_val;
pixels[idx + 3] = new_val;
} else {
// Premultiplied RGBA → multiply each channel by alpha_percent
pixels[idx + 0] = static_cast<unsigned char>(pixels[idx + 0] * alpha_percent);
pixels[idx + 1] = static_cast<unsigned char>(pixels[idx + 1] * alpha_percent);
pixels[idx + 2] = static_cast<unsigned char>(pixels[idx + 2] * alpha_percent);
pixels[idx + 3] = static_cast<unsigned char>(pixels[idx + 3] * alpha_percent);
}
}
// return the modified frame
return frame;
}
// Generate JSON string of this object
std::string Mask::Json() const {
// Return formatted string
return JsonValue().toStyledString();
}
// Generate Json::Value for this object
Json::Value Mask::JsonValue() const {
// Create root json object
Json::Value root = EffectBase::JsonValue(); // get parent properties
root["type"] = info.class_name;
root["brightness"] = brightness.JsonValue();
root["contrast"] = contrast.JsonValue();
if (reader)
root["reader"] = reader->JsonValue();
else
root["reader"] = Json::objectValue;
root["replace_image"] = replace_image;
// return JsonValue
return root;
}
// Load JSON string into this object
void Mask::SetJson(const std::string value) {
// Parse JSON string into JSON objects
try
{
const Json::Value root = openshot::stringToJson(value);
// Set all values that match
SetJsonValue(root);
}
catch (const std::exception& e)
{
// Error parsing JSON (or missing keys)
throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
}
}
// Load Json::Value into this object
void Mask::SetJsonValue(const Json::Value root) {
// Set parent data
EffectBase::SetJsonValue(root);
// Set data from Json (if key is found)
if (!root["replace_image"].isNull())
replace_image = root["replace_image"].asBool();
if (!root["brightness"].isNull())
brightness.SetJsonValue(root["brightness"]);
if (!root["contrast"].isNull())
contrast.SetJsonValue(root["contrast"]);
if (!root["reader"].isNull()) // does Json contain a reader?
{
#pragma omp critical (open_mask_reader)
{
// This reader has changed, so refresh cached assets
needs_refresh = true;
if (!root["reader"]["type"].isNull()) // does the reader Json contain a 'type'?
{
// Close previous reader (if any)
if (reader) {
// Close and delete existing reader (if any)
reader->Close();
delete reader;
reader = NULL;
}
// Create new reader (and load properties)
std::string type = root["reader"]["type"].asString();
if (type == "FFmpegReader") {
// Create new reader
reader = new FFmpegReader(root["reader"]["path"].asString());
reader->SetJsonValue(root["reader"]);
#ifdef USE_IMAGEMAGICK
} else if (type == "ImageReader") {
// Create new reader
reader = new ImageReader(root["reader"]["path"].asString());
reader->SetJsonValue(root["reader"]);
#endif
} else if (type == "QtImageReader") {
// Create new reader
reader = new QtImageReader(root["reader"]["path"].asString());
reader->SetJsonValue(root["reader"]);
} else if (type == "ChunkReader") {
// Create new reader
reader = new ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt());
reader->SetJsonValue(root["reader"]);
}
}
}
}
}
// Get all properties for a specific frame
std::string Mask::PropertiesJSON(int64_t requested_frame) const {
// Generate JSON properties list
Json::Value root = BasePropertiesJSON(requested_frame);
// Add replace_image choices (dropdown style)
root["replace_image"] = add_property_json("Replace Image", replace_image, "int", "", NULL, 0, 1, false, requested_frame);
root["replace_image"]["choices"].append(add_property_choice_json("Yes", true, replace_image));
root["replace_image"]["choices"].append(add_property_choice_json("No", false, replace_image));
// Keyframes
root["brightness"] = add_property_json("Brightness", brightness.GetValue(requested_frame), "float", "", &brightness, -1.0, 1.0, false, requested_frame);
root["contrast"] = add_property_json("Contrast", contrast.GetValue(requested_frame), "float", "", &contrast, 0, 20, false, requested_frame);
if (reader)
root["reader"] = add_property_json("Source", 0.0, "reader", reader->Json(), NULL, 0, 1, false, requested_frame);
else
root["reader"] = add_property_json("Source", 0.0, "reader", "{}", NULL, 0, 1, false, requested_frame);
// Return formatted string
return root.toStyledString();
}
ChunkReader.h
Header file for ChunkReader class.
Exceptions.h
Header file for all Exception classes.
FFmpegReader.h
Header file for FFmpegReader class.
ImageReader.h
Header file for ImageReader class.
Mask.h
Header file for Mask class.
QtImageReader.h
Header file for QtImageReader class.
ReaderBase.h
Header file for ReaderBase class.
openshot::ChunkReader
This class reads a special chunk-formatted file, which can be easily shared in a distributed environm...
Definition ChunkReader.h:79
openshot::ClipBase::add_property_choice_json
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
openshot::ClipBase::add_property_json
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
openshot::EffectBase::JsonValue
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
Definition EffectBase.cpp:79
openshot::EffectBase::InitEffectInfo
void InitEffectInfo()
Definition EffectBase.cpp:24
openshot::EffectBase::BasePropertiesJSON
Json::Value BasePropertiesJSON(int64_t requested_frame) const
Generate JSON object of base properties (recommended to be used by all effects).
Definition EffectBase.cpp:179
openshot::EffectBase::SetJsonValue
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition EffectBase.cpp:115
openshot::EffectBase::info
EffectInfoStruct info
Information about the current effect.
Definition EffectBase.h:69
openshot::FFmpegReader
This class uses the FFmpeg libraries, to open video files and audio files, and return openshot::Frame...
Definition FFmpegReader.h:103
openshot::ImageReader
This class uses the ImageMagick++ libraries, to open image files, and return openshot::Frame objects ...
Definition ImageReader.h:56
openshot::InvalidJSON
Exception for invalid JSON.
Definition Exceptions.h:218
openshot::Keyframe
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
Definition KeyFrame.h:53
openshot::Mask::SetJson
void SetJson(const std::string value) override
Load JSON string into this object.
Definition Mask.cpp:181
openshot::Mask::Mask
Mask()
Blank constructor, useful when using Json to load the effect properties.
Definition Mask.cpp:30
openshot::Mask::replace_image
bool replace_image
Replace the frame image with a grayscale image representing the mask. Great for debugging a mask.
Definition Mask.h:47
openshot::Mask::brightness
Keyframe brightness
Brightness keyframe to control the wipe / mask effect. A constant value here will prevent animation.
Definition Mask.h:48
openshot::Mask::PropertiesJSON
std::string PropertiesJSON(int64_t requested_frame) const override
Definition Mask.cpp:265
openshot::Mask::JsonValue
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition Mask.cpp:163
openshot::Mask::Json
std::string Json() const override
Generate JSON string of this object.
Definition Mask.cpp:156
openshot::Mask::SetJsonValue
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition Mask.cpp:198
openshot::Mask::contrast
Keyframe contrast
Contrast keyframe to control the hardness of the wipe effect / mask.
Definition Mask.h:49
openshot::Mask::GetFrame
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
openshot::QtImageReader
This class uses the Qt library, to open image files, and return openshot::Frame objects containing th...
Definition QtImageReader.h:75
openshot::ReaderBase
This abstract class is the base class, used by all readers in libopenshot.
Definition ReaderBase.h:76
openshot
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
openshot::ChunkVersion
ChunkVersion
This enumeration allows the user to choose which version of the chunk they would like (low,...
Definition ChunkReader.h:50
openshot::stringToJson
const Json::Value stringToJson(const std::string value)
Definition Json.cpp:16
openshot::EffectInfoStruct::has_video
bool has_video
Determines if this effect manipulates the image of a frame.
Definition EffectBase.h:40
openshot::EffectInfoStruct::has_audio
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition EffectBase.h:41
openshot::EffectInfoStruct::class_name
std::string class_name
The class name of the effect.
Definition EffectBase.h:36
openshot::EffectInfoStruct::name
std::string name
The name of the effect.
Definition EffectBase.h:37
openshot::EffectInfoStruct::description
std::string description
The description of this effect and what it does.
Definition EffectBase.h:38