| 175 | } |
| 176 | |
| 177 | std::shared_ptr<openshot::Frame> |
| 178 | ColorMap::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number) |
| 179 | { |
| 180 | // Reload LUT when its path changed; no locking here |
| 181 | if (needs_refresh) { |
| 182 | load_cube_file(); |
| 183 | needs_refresh = false; |
| 184 | } |
| 185 | |
| 186 | if (lut_data.empty() || lut_size <= 0 || lut_type == LUTType::None) |
| 187 | return frame; |
| 188 | |
| 189 | auto image = frame->GetImage(); |
| 190 | int w = image->width(), h = image->height(); |
| 191 | unsigned char *pixels = image->bits(); |
| 192 | |
| 193 | float overall = float(intensity.GetValue(frame_number)); |
| 194 | float tR = float(intensity_r.GetValue(frame_number)) * overall; |
| 195 | float tG = float(intensity_g.GetValue(frame_number)) * overall; |
| 196 | float tB = float(intensity_b.GetValue(frame_number)) * overall; |
| 197 | |
| 198 | const bool use3d = (lut_type == LUTType::LUT3D); |
| 199 | const bool use1d = (lut_type == LUTType::LUT1D); |
| 200 | const int lut_dim = lut_size; |
| 201 | const std::vector<float> &table = lut_data; |
| 202 | const int data_count = int(table.size()); |
| 203 | |
| 204 | auto sample1d = [&](float value, int channel) -> float { |
| 205 | if (lut_dim <= 1) { |
| 206 | int base = std::min(channel, data_count - 1); |
| 207 | return table[base]; |
| 208 | } |
| 209 | float scaled = value * float(lut_dim - 1); |
| 210 | int i0 = int(floor(scaled)); |
| 211 | int i1 = std::min(i0 + 1, lut_dim - 1); |
| 212 | float t = scaled - i0; |
| 213 | int base0 = std::max(0, std::min(i0 * 3 + channel, data_count - 1)); |
| 214 | int base1 = std::max(0, std::min(i1 * 3 + channel, data_count - 1)); |
| 215 | float v0 = table[base0]; |
| 216 | float v1 = table[base1]; |
| 217 | return v0 * (1.0f - t) + v1 * t; |
| 218 | }; |
| 219 | |
| 220 | int pixel_count = w * h; |
| 221 | #pragma omp parallel for |
| 222 | for (int i = 0; i < pixel_count; ++i) { |
| 223 | int idx = i * 4; |
| 224 | int A = pixels[idx + 3]; |
| 225 | float alpha = A / 255.0f; |
| 226 | if (alpha == 0.0f) continue; |
| 227 | |
| 228 | // demultiply premultiplied RGBA |
| 229 | float R = pixels[idx + 0] / alpha; |
| 230 | float G = pixels[idx + 1] / alpha; |
| 231 | float B = pixels[idx + 2] / alpha; |
| 232 | |
| 233 | // normalize to [0,1] |
| 234 | float Rn = R * (1.0f / 255.0f); |