From 7f130cb65cff8e20a02bfaa0c87148b82c8d69e5 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Mon, 2 Jun 2025 16:09:30 -0500 Subject: [PATCH] Improving performance of Mask effect (transitions) using OMP + reducing floating point operations per pixel and caching variables --- src/effects/Mask.cpp | 72 +++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp index 6992eecc..ea5fa887 100644 --- a/src/effects/Mask.cpp +++ b/src/effects/Mask.cpp @@ -18,6 +18,7 @@ #include "ChunkReader.h" #include "FFmpegReader.h" #include "QtImageReader.h" +#include #ifdef USE_IMAGEMAGICK #include "ImageReader.h" @@ -88,52 +89,61 @@ std::shared_ptr Mask::GetFrame(std::shared_ptr } } - // Refresh no longer needed + // Once we've done the necessary resizing, we no longer need to refresh again needs_refresh = false; - // Get pixel arrays - unsigned char *pixels = (unsigned char *) frame_image->bits(); - unsigned char *mask_pixels = (unsigned char *) original_mask->bits(); + // Grab raw pointers and dimensions one time + unsigned char* pixels = reinterpret_cast(frame_image->bits()); + unsigned char* mask_pixels = reinterpret_cast(original_mask->bits()); + int width = original_mask->width(); + int height = original_mask->height(); + int num_pixels = width * height; // total pixel count - double contrast_value = (contrast.GetValue(frame_number)); - double brightness_value = (brightness.GetValue(frame_number)); + // Evaluate brightness and contrast keyframes just once + double contrast_value = contrast.GetValue(frame_number); + double brightness_value = brightness.GetValue(frame_number); - // Loop through mask pixels, and apply average gray value to frame alpha channel - for (int pixel = 0, byte_index=0; pixel < original_mask->width() * original_mask->height(); pixel++, byte_index+=4) + int brightness_adj = static_cast(255 * brightness_value); + float contrast_factor = 20.0f / std::max(0.00001f, 20.0f - static_cast(contrast_value)); + + // Iterate over every pixel in parallel +#pragma omp parallel for schedule(static) + for (int i = 0; i < num_pixels; ++i) { - // Get the RGB values from the pixel - int R = mask_pixels[byte_index]; - int G = mask_pixels[byte_index + 1]; - int B = mask_pixels[byte_index + 2]; - int A = mask_pixels[byte_index + 3]; + int idx = i * 4; - // Get the average luminosity - int gray_value = qGray(R, G, B); + int R = mask_pixels[idx + 0]; + int G = mask_pixels[idx + 1]; + int B = mask_pixels[idx + 2]; + int A = mask_pixels[idx + 3]; - // Adjust the brightness - gray_value += (255 * brightness_value); + // Compute base gray, then apply brightness + contrast + int gray = qGray(R, G, B); + gray += brightness_adj; + gray = static_cast(contrast_factor * (gray - 128) + 128); - // Adjust the contrast - float factor = (20 / std::fmax(0.00001, 20.0 - contrast_value)); - gray_value = (factor * (gray_value - 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 = float(constrain(A - gray_value)) / 255.0; + float alpha_percent = static_cast(diff) / 255.0f; // Set the alpha channel to the gray value if (replace_image) { // Replace frame pixels with gray value (including alpha channel) - pixels[byte_index + 0] = constrain(255 * alpha_percent); - pixels[byte_index + 1] = constrain(255 * alpha_percent); - pixels[byte_index + 2] = constrain(255 * alpha_percent); - pixels[byte_index + 3] = constrain(255 * alpha_percent); + auto new_val = static_cast(diff); + pixels[idx + 0] = new_val; + pixels[idx + 1] = new_val; + pixels[idx + 2] = new_val; + pixels[idx + 3] = new_val; } else { - // Multiply new alpha value with all the colors (since we are using a premultiplied - // alpha format) - pixels[byte_index + 0] *= alpha_percent; - pixels[byte_index + 1] *= alpha_percent; - pixels[byte_index + 2] *= alpha_percent; - pixels[byte_index + 3] *= alpha_percent; + // Premultiplied RGBA → multiply each channel by alpha_percent + pixels[idx + 0] = static_cast(pixels[idx + 0] * alpha_percent); + pixels[idx + 1] = static_cast(pixels[idx + 1] * alpha_percent); + pixels[idx + 2] = static_cast(pixels[idx + 2] * alpha_percent); + pixels[idx + 3] = static_cast(pixels[idx + 3] * alpha_percent); } }