24 init_effect_details();
29 saturation(saturation), saturation_R(saturation_R), saturation_G(saturation_G), saturation_B(saturation_B),
33 init_effect_details();
42 std::shared_ptr<QImage> mask_image, int64_t frame_number)
const {
44 if (!original_image || !effected_image || !mask_image)
46 if (original_image->size() != effected_image->size() || effected_image->size() != mask_image->size())
49 unsigned char* original_pixels =
reinterpret_cast<unsigned char*
>(original_image->bits());
50 unsigned char* effected_pixels =
reinterpret_cast<unsigned char*
>(effected_image->bits());
51 unsigned char* mask_pixels =
reinterpret_cast<unsigned char*
>(mask_image->bits());
52 const int pixel_count = effected_image->width() * effected_image->height();
55 #pragma omp parallel for schedule(static)
56 for (
int i = 0; i < pixel_count; ++i) {
57 const int idx = i * 4;
58 float factor =
static_cast<float>(qGray(mask_pixels[idx], mask_pixels[idx + 1], mask_pixels[idx + 2])) / 255.0f;
59 factor = 1.0f - factor;
61 factor = factor * factor;
62 const float inverse = 1.0f - factor;
65 effected_pixels[idx] =
static_cast<unsigned char>(
66 (original_pixels[idx] * inverse) + (effected_pixels[idx] * factor));
67 effected_pixels[idx + 1] =
static_cast<unsigned char>(
68 (original_pixels[idx + 1] * inverse) + (effected_pixels[idx + 1] * factor));
69 effected_pixels[idx + 2] =
static_cast<unsigned char>(
70 (original_pixels[idx + 2] * inverse) + (effected_pixels[idx + 2] * factor));
71 effected_pixels[idx + 3] = original_pixels[idx + 3];
74 #pragma omp parallel for schedule(static)
75 for (
int i = 0; i < pixel_count; ++i) {
76 const int idx = i * 4;
77 float factor =
static_cast<float>(qGray(mask_pixels[idx], mask_pixels[idx + 1], mask_pixels[idx + 2])) / 255.0f;
79 factor = factor * factor;
80 const float inverse = 1.0f - factor;
83 effected_pixels[idx] =
static_cast<unsigned char>(
84 (original_pixels[idx] * inverse) + (effected_pixels[idx] * factor));
85 effected_pixels[idx + 1] =
static_cast<unsigned char>(
86 (original_pixels[idx + 1] * inverse) + (effected_pixels[idx + 1] * factor));
87 effected_pixels[idx + 2] =
static_cast<unsigned char>(
88 (original_pixels[idx + 2] * inverse) + (effected_pixels[idx + 2] * factor));
89 effected_pixels[idx + 3] = original_pixels[idx + 3];
95void Saturation::init_effect_details()
110std::shared_ptr<openshot::Frame>
Saturation::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number)
113 std::shared_ptr<QImage> frame_image = frame->GetImage();
118 const int pixel_count = frame_image->width() * frame_image->height();
127 const float pR = 0.299f;
128 const float pG = 0.587f;
129 const float pB = 0.114f;
130 const float sqrt_pR = std::sqrt(pR);
131 const float sqrt_pG = std::sqrt(pG);
132 const float sqrt_pB = std::sqrt(pB);
135 static const std::array<float, 65026> sqrt_lut = [] {
136 std::array<float, 65026> lut{};
137 for (
int i = 0; i <= 65025; ++i)
138 lut[i] = std::sqrt(
static_cast<float>(i));
143 unsigned char *pixels =
reinterpret_cast<unsigned char *
>(frame_image->bits());
145 static const std::array<float, 256> inv_alpha = [] {
146 std::array<float, 256> lut{};
148 for (
int i = 1; i < 256; ++i)
149 lut[i] = 255.0f /
static_cast<float>(i);
152 const auto clamp_i = [](
int value) ->
int {
153 if (value < 0)
return 0;
154 if (value > 255)
return 255;
158 const auto apply_saturation = [&](
int &R,
int &G,
int &B) {
161 const int weighted_sq = (77 * R * R + 150 * G * G + 29 * B * B + 128) >> 8;
162 const float p = sqrt_lut[weighted_sq];
165 R = clamp_i(
static_cast<int>(p + (R - p) * saturation_value));
166 G = clamp_i(
static_cast<int>(p + (G - p) * saturation_value));
167 B = clamp_i(
static_cast<int>(p + (B - p) * saturation_value));
170 const float p_r = R * sqrt_pR;
171 const float p_g = G * sqrt_pG;
172 const float p_b = B * sqrt_pB;
175 const int Rr =
static_cast<int>(p_r + (R - p_r) * saturation_value_R);
176 const int Gr =
static_cast<int>(p_r - p_r * saturation_value_R);
177 const int Br =
static_cast<int>(p_r - p_r * saturation_value_R);
179 const int Rg =
static_cast<int>(p_g - p_g * saturation_value_G);
180 const int Gg =
static_cast<int>(p_g + (G - p_g) * saturation_value_G);
181 const int Bg =
static_cast<int>(p_g - p_g * saturation_value_G);
183 const int Rb =
static_cast<int>(p_b - p_b * saturation_value_B);
184 const int Gb =
static_cast<int>(p_b - p_b * saturation_value_B);
185 const int Bb =
static_cast<int>(p_b + (B - p_b) * saturation_value_B);
188 R = clamp_i(Rr + Rg + Rb);
189 G = clamp_i(Gr + Gg + Gb);
190 B = clamp_i(Br + Bg + Bb);
193 #pragma omp parallel for if(pixel_count >= 16384) schedule(static) shared (pixels)
194 for (
int pixel = 0; pixel < pixel_count; ++pixel)
196 const int idx = pixel * 4;
199 const int A = pixels[idx + 3];
209 apply_saturation(R, G, B);
210 pixels[idx + 0] =
static_cast<unsigned char>(R);
211 pixels[idx + 1] =
static_cast<unsigned char>(G);
212 pixels[idx + 2] =
static_cast<unsigned char>(B);
214 const float alpha_percent =
static_cast<float>(A) * (1.0f / 255.0f);
215 const float inv_alpha_percent = inv_alpha[A];
218 R =
static_cast<int>(pixels[idx + 0] * inv_alpha_percent);
219 G =
static_cast<int>(pixels[idx + 1] * inv_alpha_percent);
220 B =
static_cast<int>(pixels[idx + 2] * inv_alpha_percent);
221 apply_saturation(R, G, B);
224 pixels[idx + 0] =
static_cast<unsigned char>(R * alpha_percent);
225 pixels[idx + 1] =
static_cast<unsigned char>(G * alpha_percent);
226 pixels[idx + 2] =
static_cast<unsigned char>(B * alpha_percent);
267 catch (
const std::exception& e)
270 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
281 if (!root[
"saturation"].isNull())
283 if (!root[
"saturation_R"].isNull())
285 if (!root[
"saturation_G"].isNull())
287 if (!root[
"saturation_B"].isNull())
289 if (!root[
"mask_mode"].isNull())
309 return root.toStyledString();
Header file for all Exception classes.
Header file for Saturation 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.
bool mask_invert
Invert grayscale mask values before blending.
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.
Exception for invalid JSON.
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
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.
bool UseCustomMaskBlend(int64_t frame_number) const override
Optional override for effects that need custom mask behavior.
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 saturation_G
Green color saturation.
std::string Json() const override
Generate JSON string of this object.
Keyframe saturation_B
Blue color saturation.
void ApplyCustomMaskBlend(std::shared_ptr< QImage > original_image, std::shared_ptr< QImage > effected_image, std::shared_ptr< QImage > mask_image, int64_t frame_number) const override
Optional override for effects with custom mask implementation.
int mask_mode
How to apply common masks to saturation (post-blend or drive-amount).
Saturation()
Blank constructor, useful when using Json to load the effect properties.
std::string PropertiesJSON(int64_t requested_frame) const override
Json::Value JsonValue() const override
Generate Json::Value for this object.
Keyframe saturation_R
Red color saturation.
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
void SetJson(const std::string value) override
Load JSON string into this object.
Keyframe saturation
Overall color saturation: 0.0 = greyscale, 1.0 = normal, 2.0 = double saturation.
This namespace is the default namespace for all code in the openshot library.
@ SATURATION_MASK_DRIVE_AMOUNT
@ SATURATION_MASK_POST_BLEND
const Json::Value stringToJson(const std::string value)
bool has_video
Determines if this effect manipulates the image of a frame.
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.