222std::shared_ptr<openshot::Frame>
ColorGrade::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number) {
223 std::shared_ptr<QImage> frame_image = frame->GetImage();
227 const float temperature_value = std::max(-1.0f, std::min(1.0f,
static_cast<float>(
temperature.
GetValue(frame_number))));
228 const float tint_value = std::max(-1.0f, std::min(1.0f,
static_cast<float>(
tint.
GetValue(frame_number))));
229 const float exposure_value = std::max(-4.0f, std::min(4.0f,
static_cast<float>(
exposure.
GetValue(frame_number))));
230 const float contrast_value = std::max(-1.0f, std::min(2.0f,
static_cast<float>(
contrast.
GetValue(frame_number))));
231 const float highlights_value = std::max(-1.0f, std::min(1.0f,
static_cast<float>(
highlights.
GetValue(frame_number))));
232 const float shadows_value = std::max(-1.0f, std::min(1.0f,
static_cast<float>(
shadows.
GetValue(frame_number))));
233 const float saturation_value = std::max(0.0f, std::min(4.0f,
static_cast<float>(
saturation.
GetValue(frame_number))));
234 const float vibrance_value = std::max(-1.0f, std::min(1.0f,
static_cast<float>(
vibrance.
GetValue(frame_number))));
235 const float mix_value = std::max(0.0f, std::min(1.0f,
static_cast<float>(
mix.
GetValue(frame_number))));
236 const float inverse_mix = 1.0f - mix_value;
238 if (temperature_value == 0.0f && tint_value == 0.0f &&
239 exposure_value == 0.0f && contrast_value == 0.0f &&
240 highlights_value == 0.0f && shadows_value == 0.0f &&
241 saturation_value == 1.0f && vibrance_value == 0.0f &&
242 mix_value == 1.0f && !wheels_enabled &&
249 return lut_effect.
GetFrame(frame, frame_number);
253 const float exposure_gain = std::pow(2.0f, exposure_value);
254 const float contrast_factor = std::max(0.0f, 1.0f + contrast_value);
255 const std::array<float, 256> curve_all_lut = build_curve_lut(
curve_all, frame_number);
256 const std::array<float, 256> curve_red_lut = build_curve_lut(
curve_red, frame_number);
257 const std::array<float, 256> curve_green_lut = build_curve_lut(
curve_green, frame_number);
258 const std::array<float, 256> curve_blue_lut = build_curve_lut(
curve_blue, frame_number);
259 const WheelBiasParams global_wheel = wheels_enabled ? build_wheel_bias(
wheels.
global, frame_number) : WheelBiasParams{0.0f, 0.0f, 0.0f};
260 const WheelBiasParams shadows_wheel = wheels_enabled ? build_wheel_bias(
wheels.
shadows, frame_number) : WheelBiasParams{0.0f, 0.0f, 0.0f};
261 const WheelBiasParams midtones_wheel = wheels_enabled ? build_wheel_bias(
wheels.
midtones, frame_number) : WheelBiasParams{0.0f, 0.0f, 0.0f};
262 const WheelBiasParams highlights_wheel = wheels_enabled ? build_wheel_bias(
wheels.
highlights, frame_number) : WheelBiasParams{0.0f, 0.0f, 0.0f};
264 static const std::array<float, 256> inv_alpha = [] {
265 std::array<float, 256> lut{};
267 for (
int i = 1; i < 256; ++i)
268 lut[i] = 255.0f /
static_cast<float>(i);
272 const auto apply_wheel_bias = [](
const WheelBiasParams& wheel,
float weight,
float& r,
float& g,
float& b) {
273 if (std::abs(weight) <= 0.00001f)
275 r += wheel.red_delta * weight;
276 g += wheel.green_delta * weight;
277 b += wheel.blue_delta * weight;
280 unsigned char* pixels =
reinterpret_cast<unsigned char*
>(frame_image->bits());
281 const int pixel_count = frame_image->width() * frame_image->height();
283 #pragma omp parallel for if(pixel_count >= 16384) schedule(static)
284 for (
int pixel = 0; pixel < pixel_count; ++pixel) {
285 const int idx = pixel * 4;
286 const int A = pixels[idx + 3];
290 const float alpha_percent =
static_cast<float>(A) * kInv255;
296 R = pixels[idx + 0] * kInv255;
297 G = pixels[idx + 1] * kInv255;
298 B = pixels[idx + 2] * kInv255;
300 const float inv_alpha_percent = inv_alpha[A];
301 R = (pixels[idx + 0] * inv_alpha_percent) * kInv255;
302 G = (pixels[idx + 1] * inv_alpha_percent) * kInv255;
303 B = (pixels[idx + 2] * inv_alpha_percent) * kInv255;
306 const float original_r = R;
307 const float original_g = G;
308 const float original_b = B;
311 R = Clamp01(R + (temperature_value * 0.125f));
312 B = Clamp01(B - (temperature_value * 0.125f));
313 G = Clamp01(G - (tint_value * 0.1f));
314 R = Clamp01(R + (tint_value * 0.05f));
315 B = Clamp01(B + (tint_value * 0.05f));
318 R = Clamp01(R * exposure_gain);
319 G = Clamp01(G * exposure_gain);
320 B = Clamp01(B * exposure_gain);
322 R = Clamp01(((R - 0.5f) * contrast_factor) + 0.5f);
323 G = Clamp01(((G - 0.5f) * contrast_factor) + 0.5f);
324 B = Clamp01(((B - 0.5f) * contrast_factor) + 0.5f);
326 float luma = (0.299f * R) + (0.587f * G) + (0.114f * B);
327 const float shadow_weight = (1.0f - luma) * (1.0f - luma);
328 const float highlight_weight = luma * luma;
331 const float shadow_adjust = shadows_value * shadow_weight * 0.35f;
332 const float highlight_adjust = highlights_value * highlight_weight * 0.35f;
333 R = Clamp01(R + shadow_adjust + highlight_adjust);
334 G = Clamp01(G + shadow_adjust + highlight_adjust);
335 B = Clamp01(B + shadow_adjust + highlight_adjust);
338 luma = (0.299f * R) + (0.587f * G) + (0.114f * B);
342 if (wheels_enabled) {
343 apply_wheel_bias(global_wheel, 1.0f, wheel_r, wheel_g, wheel_b);
344 apply_wheel_bias(shadows_wheel, (1.0f - luma) * (1.0f - luma), wheel_r, wheel_g, wheel_b);
345 apply_wheel_bias(midtones_wheel, smooth_midtones(luma), wheel_r, wheel_g, wheel_b);
346 apply_wheel_bias(highlights_wheel, luma * luma, wheel_r, wheel_g, wheel_b);
348 R = Clamp01(wheel_r);
349 G = Clamp01(wheel_g);
350 B = Clamp01(wheel_b);
353 luma = (0.299f * R) + (0.587f * G) + (0.114f * B);
354 const float max_channel = std::max(R, std::max(G, B));
355 const float min_channel = std::min(R, std::min(G, B));
356 const float colorfulness = max_channel - min_channel;
357 const float vibrance_factor = 1.0f + (vibrance_value * (1.0f - colorfulness));
358 const float sat_factor = saturation_value * std::max(0.0f, vibrance_factor);
359 R = Clamp01(luma + ((R - luma) * sat_factor));
360 G = Clamp01(luma + ((G - luma) * sat_factor));
361 B = Clamp01(luma + ((B - luma) * sat_factor));
364 R = sample_curve_lut(curve_all_lut, R);
365 G = sample_curve_lut(curve_all_lut, G);
366 B = sample_curve_lut(curve_all_lut, B);
367 R = sample_curve_lut(curve_red_lut, R);
368 G = sample_curve_lut(curve_green_lut, G);
369 B = sample_curve_lut(curve_blue_lut, B);
372 R = Clamp01((original_r * inverse_mix) + (R * mix_value));
373 G = Clamp01((original_g * inverse_mix) + (G * mix_value));
374 B = Clamp01((original_b * inverse_mix) + (B * mix_value));
377 pixels[idx + 0] =
static_cast<unsigned char>(clampByte(R * 255.0f));
378 pixels[idx + 1] =
static_cast<unsigned char>(clampByte(G * 255.0f));
379 pixels[idx + 2] =
static_cast<unsigned char>(clampByte(B * 255.0f));
381 pixels[idx + 0] =
static_cast<unsigned char>(clampByte(R * 255.0f * alpha_percent));
382 pixels[idx + 1] =
static_cast<unsigned char>(clampByte(G * 255.0f * alpha_percent));
383 pixels[idx + 2] =
static_cast<unsigned char>(clampByte(B * 255.0f * alpha_percent));
389 frame = lut_effect.
GetFrame(frame, frame_number);