This method is required for all derived classes of EffectBase, and returns a modified openshot::Frame object
| 47 | // This method is required for all derived classes of EffectBase, and returns a |
| 48 | // modified openshot::Frame object |
| 49 | std::shared_ptr<openshot::Frame> Brightness::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number) |
| 50 | { |
| 51 | // Get the frame's image |
| 52 | std::shared_ptr<QImage> frame_image = frame->GetImage(); |
| 53 | if (!frame_image) |
| 54 | return frame; |
| 55 | |
| 56 | // Get keyframe values for this frame |
| 57 | const float brightness_value = brightness.GetValue(frame_number); |
| 58 | const float contrast_value = contrast.GetValue(frame_number); |
| 59 | const float contrast_factor = (259.0f * (contrast_value + 255.0f)) / (255.0f * (259.0f - contrast_value)); |
| 60 | const int brightness_offset_i = static_cast<int>(255.0f * brightness_value); |
| 61 | |
| 62 | // Loop through pixels |
| 63 | unsigned char *pixels = reinterpret_cast<unsigned char *>(frame_image->bits()); |
| 64 | const int pixel_count = frame_image->width() * frame_image->height(); |
| 65 | // LUT for undoing premultiplication without a per-pixel divide. |
| 66 | static const std::array<float, 256> inv_alpha = [] { |
| 67 | std::array<float, 256> lut{}; |
| 68 | lut[0] = 0.0f; |
| 69 | for (int i = 1; i < 256; ++i) |
| 70 | lut[i] = 255.0f / static_cast<float>(i); |
| 71 | return lut; |
| 72 | }(); |
| 73 | const auto clamp_u8 = [](int value) -> unsigned char { |
| 74 | if (value < 0) return 0; |
| 75 | if (value > 255) return 255; |
| 76 | return static_cast<unsigned char>(value); |
| 77 | }; |
| 78 | const auto clamp_i = [](int value) -> int { |
| 79 | if (value < 0) return 0; |
| 80 | if (value > 255) return 255; |
| 81 | return value; |
| 82 | }; |
| 83 | |
| 84 | const auto adjust_contrast_and_brightness = [&](int &R, int &G, int &B) { |
| 85 | R = clamp_u8(clamp_i(static_cast<int>((contrast_factor * (R - 128)) + 128.0f)) + brightness_offset_i); |
| 86 | G = clamp_u8(clamp_i(static_cast<int>((contrast_factor * (G - 128)) + 128.0f)) + brightness_offset_i); |
| 87 | B = clamp_u8(clamp_i(static_cast<int>((contrast_factor * (B - 128)) + 128.0f)) + brightness_offset_i); |
| 88 | }; |
| 89 | |
| 90 | #pragma omp parallel for if(pixel_count >= 16384) schedule(static) |
| 91 | for (int pixel = 0; pixel < pixel_count; ++pixel) |
| 92 | { |
| 93 | const int idx = pixel * 4; |
| 94 | |
| 95 | // Split hot paths by alpha to avoid unnecessary premultiply/unpremultiply work. |
| 96 | const int A = pixels[idx + 3]; |
| 97 | if (A <= 0) |
| 98 | continue; |
| 99 | int R = 0; |
| 100 | int G = 0; |
| 101 | int B = 0; |
| 102 | if (A == 255) { |
| 103 | R = pixels[idx + 0]; |
| 104 | G = pixels[idx + 1]; |
| 105 | B = pixels[idx + 2]; |
| 106 | adjust_contrast_and_brightness(R, G, B); |