26 init_effect_details();
31 brightness(mask_brightness), contrast(mask_contrast), replace_image(false)
34 init_effect_details();
42 void Mask::init_effect_details()
49 info.
name =
"Alpha Mask / Wipe Transition";
50 info.
description =
"Uses a grayscale mask image to gradually wipe / transition between 2 images.";
57 std::shared_ptr<openshot::Frame>
Mask::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number) {
59 std::shared_ptr<QImage> frame_image = frame->GetImage();
60 if (!frame_image || frame_image->isNull())
65 if (!original_mask || original_mask->isNull())
69 unsigned char* pixels =
reinterpret_cast<unsigned char*
>(frame_image->bits());
70 unsigned char* mask_pixels =
reinterpret_cast<unsigned char*
>(original_mask->bits());
71 const int num_pixels = original_mask->width() * original_mask->height();
77 int brightness_adj =
static_cast<int>(255 * brightness_value);
78 float contrast_factor = 20.0f / std::max(0.00001f, 20.0f -
static_cast<float>(contrast_value));
80 const auto clamp_u8 = [](
int value) ->
unsigned char {
81 if (value < 0)
return 0;
82 if (value > 255)
return 255;
83 return static_cast<unsigned char>(value);
86 std::array<unsigned char, 256> adjusted_gray{};
87 for (
int gray = 0; gray < 256; ++gray) {
88 const int adjusted =
static_cast<int>(contrast_factor * ((gray + brightness_adj) - 128) + 128);
89 adjusted_gray[gray] = clamp_u8(adjusted);
92 static const std::array<std::array<unsigned char, 256>, 256> mul_lut = [] {
93 std::array<std::array<unsigned char, 256>, 256> lut{};
94 for (
int alpha = 0; alpha < 256; ++alpha) {
95 for (
int value = 0; value < 256; ++value) {
96 lut[alpha][value] =
static_cast<unsigned char>((value * alpha) / 255);
104 #pragma omp parallel for if(num_pixels >= 16384) schedule(static)
105 for (
int i = 0; i < num_pixels; ++i) {
106 const int idx = i * 4;
107 const int R = mask_pixels[idx + 0];
108 const int G = mask_pixels[idx + 1];
109 const int B = mask_pixels[idx + 2];
110 const int A = mask_pixels[idx + 3];
112 const int gray = ((R * 11) + (G * 16) + (B * 5)) >> 5;
113 const int diff = A - adjusted_gray[gray];
114 const unsigned char new_val = clamp_u8(diff);
115 pixels[idx + 0] = new_val;
116 pixels[idx + 1] = new_val;
117 pixels[idx + 2] = new_val;
118 pixels[idx + 3] = new_val;
121 #pragma omp parallel for if(num_pixels >= 16384) schedule(static)
122 for (
int i = 0; i < num_pixels; ++i) {
123 const int idx = i * 4;
124 const int R = mask_pixels[idx + 0];
125 const int G = mask_pixels[idx + 1];
126 const int B = mask_pixels[idx + 2];
127 const int A = mask_pixels[idx + 3];
129 const int gray = ((R * 11) + (G * 16) + (B * 5)) >> 5;
130 int alpha = A - adjusted_gray[gray];
131 if (alpha < 0) alpha = 0;
132 else if (alpha > 255) alpha = 255;
136 pixels[idx + 0] = mul_lut[alpha][pixels[idx + 0]];
137 pixels[idx + 1] = mul_lut[alpha][pixels[idx + 1]];
138 pixels[idx + 2] = mul_lut[alpha][pixels[idx + 2]];
139 pixels[idx + 3] = mul_lut[alpha][pixels[idx + 3]];
142 #pragma omp parallel for if(num_pixels >= 16384) schedule(static)
143 for (
int i = 0; i < num_pixels; ++i) {
144 const int idx = i * 4;
145 const int R = mask_pixels[idx + 0];
146 const int G = mask_pixels[idx + 1];
147 const int B = mask_pixels[idx + 2];
148 const int A = mask_pixels[idx + 3];
150 const int gray = ((R * 11) + (G * 16) + (B * 5)) >> 5;
151 int alpha = A - adjusted_gray[gray];
152 if (alpha < 0) alpha = 0;
153 else if (alpha > 255) alpha = 255;
156 pixels[idx + 0] = mul_lut[alpha][pixels[idx + 0]];
157 pixels[idx + 1] = mul_lut[alpha][pixels[idx + 1]];
158 pixels[idx + 2] = mul_lut[alpha][pixels[idx + 2]];
159 pixels[idx + 3] = mul_lut[alpha][pixels[idx + 3]];
198 catch (
const std::exception& e)
201 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
207 Json::Value normalized_root = root;
209 if (!normalized_root[
"reader"].isNull() && normalized_root[
"mask_reader"].isNull())
210 normalized_root[
"mask_reader"] = normalized_root[
"reader"];
216 if (!normalized_root[
"replace_image"].isNull())
218 if (!normalized_root[
"brightness"].isNull())
220 if (!normalized_root[
"contrast"].isNull())
241 return root.toStyledString();