This method is required for all derived classes of EffectBase, and returns a modified openshot::Frame object
| 108 | // This method is required for all derived classes of EffectBase, and returns a |
| 109 | // modified openshot::Frame object |
| 110 | std::shared_ptr<openshot::Frame> Saturation::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number) |
| 111 | { |
| 112 | // Get the frame's image |
| 113 | std::shared_ptr<QImage> frame_image = frame->GetImage(); |
| 114 | |
| 115 | if (!frame_image) |
| 116 | return frame; |
| 117 | |
| 118 | const int pixel_count = frame_image->width() * frame_image->height(); |
| 119 | |
| 120 | // Get keyframe values for this frame |
| 121 | const float saturation_value = saturation.GetValue(frame_number); |
| 122 | const float saturation_value_R = saturation_R.GetValue(frame_number); |
| 123 | const float saturation_value_G = saturation_G.GetValue(frame_number); |
| 124 | const float saturation_value_B = saturation_B.GetValue(frame_number); |
| 125 | |
| 126 | // Constants used for color saturation formula |
| 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); |
| 133 | // Rec.601 fixed-point luma weights used in many image/video pipelines. |
| 134 | // This avoids per-pixel sqrt() while keeping output stable. |
| 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)); |
| 139 | return lut; |
| 140 | }(); |
| 141 | |
| 142 | // Loop through pixels |
| 143 | unsigned char *pixels = reinterpret_cast<unsigned char *>(frame_image->bits()); |
| 144 | // LUT for undoing premultiplication without a per-pixel divide. |
| 145 | static const std::array<float, 256> inv_alpha = [] { |
| 146 | std::array<float, 256> lut{}; |
| 147 | lut[0] = 0.0f; |
| 148 | for (int i = 1; i < 256; ++i) |
| 149 | lut[i] = 255.0f / static_cast<float>(i); |
| 150 | return lut; |
| 151 | }(); |
| 152 | const auto clamp_i = [](int value) -> int { |
| 153 | if (value < 0) return 0; |
| 154 | if (value > 255) return 255; |
| 155 | return value; |
| 156 | }; |
| 157 | |
| 158 | const auto apply_saturation = [&](int &R, int &G, int &B) { |
| 159 | // Approximate sqrt(R^2*pR + G^2*pG + B^2*pB) with fixed-point weighted |
| 160 | // intensity and lookup table. 77/150/29 ~= 0.299/0.587/0.114. |
| 161 | const int weighted_sq = (77 * R * R + 150 * G * G + 29 * B * B + 128) >> 8; |
| 162 | const float p = sqrt_lut[weighted_sq]; |
| 163 | |
| 164 | // Adjust common saturation |
| 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)); |