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 width = effected_image->width();
53 const int height = effected_image->height();
54 const int original_stride = original_image->bytesPerLine();
55 const int effected_stride = effected_image->bytesPerLine();
56 const int mask_stride = mask_image->bytesPerLine();
59 #pragma omp parallel for schedule(static)
60 for (
int y = 0; y < height; ++y) {
61 unsigned char* original_row = original_pixels + y * original_stride;
62 unsigned char* effected_row = effected_pixels + y * effected_stride;
63 unsigned char* mask_row = mask_pixels + y * mask_stride;
64 for (
int x = 0; x < width; ++x) {
65 const int idx = x * 4;
66 float factor =
static_cast<float>(qGray(mask_row[idx], mask_row[idx + 1], mask_row[idx + 2])) / 255.0f;
67 factor = 1.0f - factor;
69 factor = factor * factor;
70 const float inverse = 1.0f - factor;
73 effected_row[idx] =
static_cast<unsigned char>(
74 (original_row[idx] * inverse) + (effected_row[idx] * factor));
75 effected_row[idx + 1] =
static_cast<unsigned char>(
76 (original_row[idx + 1] * inverse) + (effected_row[idx + 1] * factor));
77 effected_row[idx + 2] =
static_cast<unsigned char>(
78 (original_row[idx + 2] * inverse) + (effected_row[idx + 2] * factor));
79 effected_row[idx + 3] = original_row[idx + 3];
83 #pragma omp parallel for schedule(static)
84 for (
int y = 0; y < height; ++y) {
85 unsigned char* original_row = original_pixels + y * original_stride;
86 unsigned char* effected_row = effected_pixels + y * effected_stride;
87 unsigned char* mask_row = mask_pixels + y * mask_stride;
88 for (
int x = 0; x < width; ++x) {
89 const int idx = x * 4;
90 float factor =
static_cast<float>(qGray(mask_row[idx], mask_row[idx + 1], mask_row[idx + 2])) / 255.0f;
92 factor = factor * factor;
93 const float inverse = 1.0f - factor;
96 effected_row[idx] =
static_cast<unsigned char>(
97 (original_row[idx] * inverse) + (effected_row[idx] * factor));
98 effected_row[idx + 1] =
static_cast<unsigned char>(
99 (original_row[idx + 1] * inverse) + (effected_row[idx + 1] * factor));
100 effected_row[idx + 2] =
static_cast<unsigned char>(
101 (original_row[idx + 2] * inverse) + (effected_row[idx + 2] * factor));
102 effected_row[idx + 3] = original_row[idx + 3];
109 void Saturation::init_effect_details()
124 std::shared_ptr<openshot::Frame>
Saturation::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number)
127 std::shared_ptr<QImage> frame_image = frame->GetImage();
132 const int pixel_count = frame_image->width() * frame_image->height();
141 const float pR = 0.299f;
142 const float pG = 0.587f;
143 const float pB = 0.114f;
144 const float sqrt_pR = std::sqrt(pR);
145 const float sqrt_pG = std::sqrt(pG);
146 const float sqrt_pB = std::sqrt(pB);
149 static const std::array<float, 65026> sqrt_lut = [] {
150 std::array<float, 65026> lut{};
151 for (
int i = 0; i <= 65025; ++i)
152 lut[i] = std::sqrt(
static_cast<float>(i));
157 unsigned char *pixels =
reinterpret_cast<unsigned char *
>(frame_image->bits());
158 const int width = frame_image->width();
159 const int height = frame_image->height();
160 const int stride = frame_image->bytesPerLine();
162 static const std::array<float, 256> inv_alpha = [] {
163 std::array<float, 256> lut{};
165 for (
int i = 1; i < 256; ++i)
166 lut[i] = 255.0f /
static_cast<float>(i);
169 const auto clamp_i = [](
int value) ->
int {
170 if (value < 0)
return 0;
171 if (value > 255)
return 255;
175 const auto apply_saturation = [&](
int &R,
int &G,
int &B) {
178 const int weighted_sq = (77 * R * R + 150 * G * G + 29 * B * B + 128) >> 8;
179 const float p = sqrt_lut[weighted_sq];
182 R = clamp_i(
static_cast<int>(p + (R - p) * saturation_value));
183 G = clamp_i(
static_cast<int>(p + (G - p) * saturation_value));
184 B = clamp_i(
static_cast<int>(p + (B - p) * saturation_value));
187 const float p_r = R * sqrt_pR;
188 const float p_g = G * sqrt_pG;
189 const float p_b = B * sqrt_pB;
192 const int Rr =
static_cast<int>(p_r + (R - p_r) * saturation_value_R);
193 const int Gr =
static_cast<int>(p_r - p_r * saturation_value_R);
194 const int Br =
static_cast<int>(p_r - p_r * saturation_value_R);
196 const int Rg =
static_cast<int>(p_g - p_g * saturation_value_G);
197 const int Gg =
static_cast<int>(p_g + (G - p_g) * saturation_value_G);
198 const int Bg =
static_cast<int>(p_g - p_g * saturation_value_G);
200 const int Rb =
static_cast<int>(p_b - p_b * saturation_value_B);
201 const int Gb =
static_cast<int>(p_b - p_b * saturation_value_B);
202 const int Bb =
static_cast<int>(p_b + (B - p_b) * saturation_value_B);
205 R = clamp_i(Rr + Rg + Rb);
206 G = clamp_i(Gr + Gg + Gb);
207 B = clamp_i(Br + Bg + Bb);
210 #pragma omp parallel for if(pixel_count >= 16384) schedule(static) shared (pixels)
211 for (
int y = 0; y < height; ++y)
213 unsigned char* row = pixels + y * stride;
214 for (
int x = 0; x < width; ++x) {
215 const int idx = x * 4;
218 const int A = row[idx + 3];
228 apply_saturation(R, G, B);
229 row[idx + 0] =
static_cast<unsigned char>(R);
230 row[idx + 1] =
static_cast<unsigned char>(G);
231 row[idx + 2] =
static_cast<unsigned char>(B);
233 const float alpha_percent =
static_cast<float>(A) * (1.0f / 255.0f);
234 const float inv_alpha_percent = inv_alpha[A];
237 R =
static_cast<int>(row[idx + 0] * inv_alpha_percent);
238 G =
static_cast<int>(row[idx + 1] * inv_alpha_percent);
239 B =
static_cast<int>(row[idx + 2] * inv_alpha_percent);
240 apply_saturation(R, G, B);
243 row[idx + 0] =
static_cast<unsigned char>(R * alpha_percent);
244 row[idx + 1] =
static_cast<unsigned char>(G * alpha_percent);
245 row[idx + 2] =
static_cast<unsigned char>(B * alpha_percent);
287 catch (
const std::exception& e)
290 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
301 if (!root[
"saturation"].isNull())
303 if (!root[
"saturation_R"].isNull())
305 if (!root[
"saturation_G"].isNull())
307 if (!root[
"saturation_B"].isNull())
309 if (!root[
"mask_mode"].isNull())
329 return root.toStyledString();