OpenShot Library | libopenshot  0.7.0
FilmGrain.cpp
Go to the documentation of this file.
1 
9 // Copyright (c) 2008-2026 OpenShot Studios, LLC
10 //
11 // SPDX-License-Identifier: LGPL-3.0-or-later
12 
13 #include "FilmGrain.h"
14 #include "Clip.h"
15 #include "Exceptions.h"
16 #include "Timeline.h"
17 
18 #include <algorithm>
19 #include <array>
20 #include <cmath>
21 #include <cstdint>
22 
23 using namespace openshot;
24 
25 namespace {
26 constexpr float kInv255 = 1.0f / 255.0f;
27 constexpr float kInvU32Max = 2.0f / 4294967295.0f; // multiply instead of divide in hash_signed
28 
29 // Salt constants for each grain channel and noise layer
30 constexpr uint32_t kSaltLuma = 0x1000193u;
31 constexpr uint32_t kSaltRed = 0x8da6b343u;
32 constexpr uint32_t kSaltGreen = 0xd8163841u;
33 constexpr uint32_t kSaltBlue = 0xcb1ab31fu;
34 constexpr uint32_t kSoftXor = 0x51633e2du;
35 constexpr uint32_t kClumpXor = 0xa511e9b3u;
36 
37 static float clamp01(float value) {
38  return std::max(0.0f, std::min(1.0f, value));
39 }
40 
41 static int clampByte(float value) {
42  if (value <= 0.0f)
43  return 0;
44  if (value >= 255.0f)
45  return 255;
46  return static_cast<int>(value + 0.5f);
47 }
48 
49 static float lerp(float a, float b, float t) {
50  return a + ((b - a) * t);
51 }
52 
53 static uint32_t mix32(uint32_t value) {
54  value ^= value >> 16;
55  value *= 0x7feb352du;
56  value ^= value >> 15;
57  value *= 0x846ca68bu;
58  value ^= value >> 16;
59  return value;
60 }
61 
62 static uint32_t hash_string(const std::string& value) {
63  uint32_t h = 2166136261u;
64  for (unsigned char c : value) {
65  h ^= c;
66  h *= 16777619u;
67  }
68  return h;
69 }
70 
71 static uint32_t hash_coords(uint32_t seed, int x, int y, int t, uint32_t salt) {
72  uint32_t h = seed ^ salt;
73  h ^= static_cast<uint32_t>(x) * 0x9e3779b9u;
74  h ^= static_cast<uint32_t>(y) * 0x85ebca6bu;
75  h ^= static_cast<uint32_t>(t) * 0xc2b2ae35u;
76  return mix32(h);
77 }
78 
79 static float hash_signed(uint32_t seed, int x, int y, int t, uint32_t salt) {
80  return hash_coords(seed, x, y, t, salt) * kInvU32Max - 1.0f;
81 }
82 
83 static float tonal_weight(float luma, float shadow_strength, float midtone_strength, float highlight_strength) {
84  const float shadow = (1.0f - luma) * (1.0f - luma);
85  const float highlight = luma * luma;
86  const float centered = std::abs(luma - 0.5f) * 2.0f;
87  const float midtone = clamp01(1.0f - centered * centered);
88  return (shadow * shadow_strength) + (midtone * midtone_strength) + (highlight * highlight_strength);
89 }
90 }
91 
93  : amount(0.25),
94  size(0.20),
95  softness(0.25),
96  clump(0.20),
97  shadows(0.80),
98  midtones(1.00),
99  highlights(0.55),
100  color_amount(0.20),
101  color_variation(0.35),
102  evolution(0.65),
103  coherence(0.55),
104  seed(1)
105 {
106  init_effect_details();
107 }
108 
109 void FilmGrain::init_effect_details() {
110  InitEffectInfo();
111  info.class_name = "FilmGrain";
112  info.name = "Film Grain";
113  info.description = "Film-inspired grain texture with tonal, color, and temporal controls.";
114  info.has_audio = false;
115  info.has_video = true;
116 }
117 
118 std::shared_ptr<openshot::Frame> FilmGrain::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number) {
119  std::shared_ptr<QImage> frame_image = frame->GetImage();
120  if (!frame_image)
121  return frame;
122 
123  const float amount_value = clamp01(static_cast<float>(amount.GetValue(frame_number)));
124  if (amount_value <= 0.00001f)
125  return frame;
126 
127  const float size_value = clamp01(static_cast<float>(size.GetValue(frame_number)));
128  const float softness_value = clamp01(static_cast<float>(softness.GetValue(frame_number)));
129  const float clump_value = clamp01(static_cast<float>(clump.GetValue(frame_number)));
130  const float shadows_value = clamp01(static_cast<float>(shadows.GetValue(frame_number)));
131  const float midtones_value = clamp01(static_cast<float>(midtones.GetValue(frame_number)));
132  const float highlights_value = clamp01(static_cast<float>(highlights.GetValue(frame_number)));
133  const float color_amount_value = clamp01(static_cast<float>(color_amount.GetValue(frame_number)));
134  const float color_variation_value = clamp01(static_cast<float>(color_variation.GetValue(frame_number)));
135  const float evolution_value = clamp01(static_cast<float>(evolution.GetValue(frame_number)));
136  const float coherence_value = clamp01(static_cast<float>(coherence.GetValue(frame_number)));
137 
138  const float cell_size = lerp(1.0f, 9.0f, size_value);
139  const float fine_frequency = 1.0f / cell_size;
140  const float soft_frequency = fine_frequency * lerp(0.45f, 0.12f, softness_value);
141  const float clump_frequency = fine_frequency * lerp(0.35f, 0.08f, clump_value);
142  const float temporal_rate = lerp(0.0f, 1.0f, evolution_value) * lerp(1.0f, 8.0f, 1.0f - coherence_value);
143  const float temporal_position = static_cast<float>(frame_number) * temporal_rate;
144  const int time0 = static_cast<int>(std::floor(temporal_position));
145  const int time1 = time0 + 1;
146  const float temporal_mix = temporal_rate <= 0.00001f ? 0.0f : clamp01(temporal_position - static_cast<float>(time0));
147  const float smooth_temporal_mix = temporal_mix * temporal_mix * (3.0f - 2.0f * temporal_mix);
148  const uint32_t base_seed = static_cast<uint32_t>(seed) ^ mix32(hash_string(Id()));
149  const float intensity_scale = amount_value * 0.18f;
150  const bool use_temporal_blend = temporal_rate > 0.00001f && smooth_temporal_mix > 0.00001f;
151  const bool use_softness = softness_value > 0.00001f;
152  const bool use_clump = clump_value > 0.00001f;
153  const bool use_color = color_amount_value > 0.00001f;
154  const bool use_color_variation = use_color && color_variation_value > 0.00001f;
155 
156  // How many temporal samples and how many channel salts to compute
157  const int num_times = use_temporal_blend ? 2 : 1;
158  const int num_salts = use_color_variation ? 4 : 1;
159  const int time_buckets[2] = { time0, time1 };
160  const uint32_t base_salts[4] = { kSaltLuma, kSaltRed, kSaltGreen, kSaltBlue };
161 
162  static const std::array<float, 256> inv_alpha = [] {
163  std::array<float, 256> lut{};
164  lut[0] = 0.0f;
165  for (int i = 1; i < 256; ++i)
166  lut[i] = 255.0f / static_cast<float>(i);
167  return lut;
168  }();
169 
170  unsigned char* pixels = reinterpret_cast<unsigned char*>(frame_image->bits());
171  const int width = frame_image->width();
172  const int height = frame_image->height();
173  const int stride = frame_image->bytesPerLine();
174  const int pixel_count = width * height;
175  int reference_width = width;
176  int reference_height = height;
177 
178  Clip* clip = static_cast<Clip*>(ParentClip());
179  Timeline* timeline = nullptr;
180  if (clip && clip->ParentTimeline())
181  timeline = static_cast<Timeline*>(clip->ParentTimeline());
182  else if (ParentTimeline())
183  timeline = static_cast<Timeline*>(ParentTimeline());
184  if (timeline && timeline->info.width > 0 && timeline->info.height > 0) {
185  reference_width = timeline->info.width;
186  reference_height = timeline->info.height;
187  }
188  const float reference_scale_x = width > 0 ? static_cast<float>(reference_width) / static_cast<float>(width) : 1.0f;
189  const float reference_scale_y = height > 0 ? static_cast<float>(reference_height) / static_cast<float>(height) : 1.0f;
190 
191  #pragma omp parallel for if(pixel_count >= 16384) schedule(static)
192  for (int y = 0; y < height; ++y) {
193  unsigned char* row = pixels + (y * stride);
194 
195  // Pre-compute all y-dependent values once per row (not per pixel).
196  // reference_x/y coords are always non-negative, so (int) cast is safe instead of floor.
197  const float reference_y = static_cast<float>(y) * reference_scale_y;
198  const int fine_iy = static_cast<int>(reference_y * fine_frequency);
199 
200  // Soft noise: y-fractional components are constant across the row
201  int soft_iy0 = 0;
202  float soft_sy_fac = 0.0f; // smoothstep of the y fractional part
203  if (use_softness) {
204  const float sry = reference_y * soft_frequency;
205  soft_iy0 = static_cast<int>(sry);
206  const float t = sry - static_cast<float>(soft_iy0);
207  soft_sy_fac = t * t * (3.0f - 2.0f * t);
208  }
209 
210  // Clump noise: same idea
211  int clump_iy0 = 0;
212  float clump_sy_fac = 0.0f;
213  if (use_clump) {
214  const float cry = reference_y * clump_frequency;
215  clump_iy0 = static_cast<int>(cry);
216  const float t = cry - static_cast<float>(clump_iy0);
217  clump_sy_fac = t * t * (3.0f - 2.0f * t);
218  }
219 
220  // Per-row x-cell caches. Initialized to -1 so the first pixel always
221  // triggers a fill. All computed cell indices are >= 0, so -1 is a safe sentinel.
222  int cached_fine_ix = -1;
223  int cached_soft_ix0 = -1;
224  int cached_clump_ix0 = -1;
225 
226  // fine_cache[time_idx][salt_idx]: direct hash value per fine-grid cell
227  float fine_cache[2][4] = {};
228 
229  // soft_col0/col1[time_idx][salt_idx]: value_noise y-axis columns pre-lerped.
230  // Per pixel we only need one lerp across x instead of 4 hash calls + 3 lerps.
231  float soft_col0[2][4] = {}, soft_col1[2][4] = {};
232 
233  // clump_col0/col1[salt_idx]: clump only uses time0
234  float clump_col0[4] = {}, clump_col1[4] = {};
235 
236  for (int x = 0; x < width; ++x) {
237  const int idx = x * 4;
238  const int A = row[idx + 3];
239  if (A <= 0)
240  continue;
241 
242  float R, G, B;
243  if (A == 255) {
244  R = row[idx + 0] * kInv255;
245  G = row[idx + 1] * kInv255;
246  B = row[idx + 2] * kInv255;
247  } else {
248  const float inv_a = inv_alpha[A];
249  R = (row[idx + 0] * inv_a) * kInv255;
250  G = (row[idx + 1] * inv_a) * kInv255;
251  B = (row[idx + 2] * inv_a) * kInv255;
252  }
253 
254  const float luma = (0.299f * R) + (0.587f * G) + (0.114f * B);
255  const float tone = tonal_weight(luma, shadows_value, midtones_value, highlights_value);
256  if (tone <= 0.00001f)
257  continue;
258 
259  const float reference_x = static_cast<float>(x) * reference_scale_x;
260 
261  // Fine grain: one hash per (cell, time, salt). Update when the x-cell changes.
262  const int fine_ix = static_cast<int>(reference_x * fine_frequency);
263  if (fine_ix != cached_fine_ix) {
264  cached_fine_ix = fine_ix;
265  for (int t = 0; t < num_times; ++t)
266  for (int s = 0; s < num_salts; ++s)
267  fine_cache[t][s] = hash_signed(base_seed, fine_ix, fine_iy, time_buckets[t], base_salts[s]);
268  }
269 
270  // Soft noise: bilinear value_noise, but y-axis columns are pre-lerped into
271  // soft_col0/col1. Only 4 hashes needed when the x-cell changes (~every 7 px),
272  // then 1 lerp per pixel instead of 4 hashes + 3 lerps every pixel.
273  float soft_sx = 0.0f;
274  if (use_softness) {
275  const float srx = reference_x * soft_frequency;
276  const int soft_ix0 = static_cast<int>(srx);
277  if (soft_ix0 != cached_soft_ix0) {
278  cached_soft_ix0 = soft_ix0;
279  const int soft_ix1 = soft_ix0 + 1;
280  const int soft_iy1 = soft_iy0 + 1;
281  for (int t = 0; t < num_times; ++t) {
282  for (int s = 0; s < num_salts; ++s) {
283  const uint32_t salt = base_salts[s] ^ kSoftXor;
284  const float n00 = hash_signed(base_seed, soft_ix0, soft_iy0, time_buckets[t], salt);
285  const float n10 = hash_signed(base_seed, soft_ix1, soft_iy0, time_buckets[t], salt);
286  const float n01 = hash_signed(base_seed, soft_ix0, soft_iy1, time_buckets[t], salt);
287  const float n11 = hash_signed(base_seed, soft_ix1, soft_iy1, time_buckets[t], salt);
288  // y-interpolate left and right columns separately; x-interpolation is per-pixel
289  soft_col0[t][s] = n00 + (n01 - n00) * soft_sy_fac;
290  soft_col1[t][s] = n10 + (n11 - n10) * soft_sy_fac;
291  }
292  }
293  }
294  const float tx = srx - static_cast<float>(cached_soft_ix0);
295  soft_sx = tx * tx * (3.0f - 2.0f * tx);
296  }
297 
298  // Clump noise: same column-caching strategy, only time0
299  float clump_sx = 0.0f;
300  if (use_clump) {
301  const float crx = reference_x * clump_frequency;
302  const int clump_ix0 = static_cast<int>(crx);
303  if (clump_ix0 != cached_clump_ix0) {
304  cached_clump_ix0 = clump_ix0;
305  const int clump_ix1 = clump_ix0 + 1;
306  const int clump_iy1 = clump_iy0 + 1;
307  for (int s = 0; s < num_salts; ++s) {
308  const uint32_t salt = base_salts[s] ^ kClumpXor;
309  const float n00 = hash_signed(base_seed, clump_ix0, clump_iy0, time0, salt);
310  const float n10 = hash_signed(base_seed, clump_ix1, clump_iy0, time0, salt);
311  const float n01 = hash_signed(base_seed, clump_ix0, clump_iy1, time0, salt);
312  const float n11 = hash_signed(base_seed, clump_ix1, clump_iy1, time0, salt);
313  clump_col0[s] = n00 + (n01 - n00) * clump_sy_fac;
314  clump_col1[s] = n10 + (n11 - n10) * clump_sy_fac;
315  }
316  }
317  const float tx = crx - static_cast<float>(cached_clump_ix0);
318  clump_sx = tx * tx * (3.0f - 2.0f * tx);
319  }
320 
321  float grains[4];
322  for (int s = 0; s < num_salts; ++s) {
323  float g0 = fine_cache[0][s];
324  if (use_softness) {
325  const float soft0 = soft_col0[0][s] + (soft_col1[0][s] - soft_col0[0][s]) * soft_sx;
326  g0 += (soft0 - g0) * softness_value;
327  }
328  float grain;
329  if (use_temporal_blend) {
330  float g1 = fine_cache[1][s];
331  if (use_softness) {
332  const float soft1 = soft_col0[1][s] + (soft_col1[1][s] - soft_col0[1][s]) * soft_sx;
333  g1 += (soft1 - g1) * softness_value;
334  }
335  grain = g0 + (g1 - g0) * smooth_temporal_mix;
336  } else {
337  grain = g0;
338  }
339  if (use_clump) {
340  const float cluster = clump_col0[s] + (clump_col1[s] - clump_col0[s]) * clump_sx;
341  grain *= lerp(1.0f, 0.45f + (std::abs(cluster) * 1.35f), clump_value);
342  }
343  grains[s] = grain;
344  }
345 
346  const float strength = intensity_scale * tone;
347  const float luma_grain = grains[0];
348  if (use_color_variation) {
349  const float red_grain = luma_grain + (grains[1] - luma_grain) * color_variation_value;
350  const float green_grain = luma_grain + (grains[2] - luma_grain) * color_variation_value;
351  const float blue_grain = luma_grain + (grains[3] - luma_grain) * color_variation_value;
352  R = clamp01(R + (luma_grain + (red_grain - luma_grain) * color_amount_value) * strength);
353  G = clamp01(G + (luma_grain + (green_grain - luma_grain) * color_amount_value) * strength);
354  B = clamp01(B + (luma_grain + (blue_grain - luma_grain) * color_amount_value) * strength);
355  } else {
356  const float delta = luma_grain * strength;
357  R = clamp01(R + delta);
358  G = clamp01(G + delta);
359  B = clamp01(B + delta);
360  }
361 
362  if (A == 255) {
363  row[idx + 0] = static_cast<unsigned char>(clampByte(R * 255.0f));
364  row[idx + 1] = static_cast<unsigned char>(clampByte(G * 255.0f));
365  row[idx + 2] = static_cast<unsigned char>(clampByte(B * 255.0f));
366  } else {
367  const float alpha_percent = static_cast<float>(A) * kInv255;
368  row[idx + 0] = static_cast<unsigned char>(clampByte(R * 255.0f * alpha_percent));
369  row[idx + 1] = static_cast<unsigned char>(clampByte(G * 255.0f * alpha_percent));
370  row[idx + 2] = static_cast<unsigned char>(clampByte(B * 255.0f * alpha_percent));
371  }
372  }
373  }
374 
375  return frame;
376 }
377 
378 std::string FilmGrain::Json() const {
379  return JsonValue().toStyledString();
380 }
381 
382 Json::Value FilmGrain::JsonValue() const {
383  Json::Value root = EffectBase::JsonValue();
384  root["type"] = info.class_name;
385  root["amount"] = amount.JsonValue();
386  root["size"] = size.JsonValue();
387  root["softness"] = softness.JsonValue();
388  root["clump"] = clump.JsonValue();
389  root["shadows"] = shadows.JsonValue();
390  root["midtones"] = midtones.JsonValue();
391  root["highlights"] = highlights.JsonValue();
392  root["color_amount"] = color_amount.JsonValue();
393  root["color_variation"] = color_variation.JsonValue();
394  root["evolution"] = evolution.JsonValue();
395  root["coherence"] = coherence.JsonValue();
396  root["seed"] = seed;
397  return root;
398 }
399 
400 void FilmGrain::SetJson(const std::string value) {
401  try {
402  const Json::Value root = openshot::stringToJson(value);
403  SetJsonValue(root);
404  } catch (const std::exception&) {
405  throw InvalidJSON("Invalid JSON for FilmGrain effect");
406  }
407 }
408 
409 void FilmGrain::SetJsonValue(const Json::Value root) {
411  if (!root["amount"].isNull()) amount.SetJsonValue(root["amount"]);
412  if (!root["size"].isNull()) size.SetJsonValue(root["size"]);
413  if (!root["softness"].isNull()) softness.SetJsonValue(root["softness"]);
414  if (!root["clump"].isNull()) clump.SetJsonValue(root["clump"]);
415  if (!root["shadows"].isNull()) shadows.SetJsonValue(root["shadows"]);
416  if (!root["midtones"].isNull()) midtones.SetJsonValue(root["midtones"]);
417  if (!root["highlights"].isNull()) highlights.SetJsonValue(root["highlights"]);
418  if (!root["color_amount"].isNull()) color_amount.SetJsonValue(root["color_amount"]);
419  if (!root["color_variation"].isNull()) color_variation.SetJsonValue(root["color_variation"]);
420  if (!root["evolution"].isNull()) evolution.SetJsonValue(root["evolution"]);
421  if (!root["coherence"].isNull()) coherence.SetJsonValue(root["coherence"]);
422  if (!root["seed"].isNull()) seed = root["seed"].asInt();
423 }
424 
425 std::string FilmGrain::PropertiesJSON(int64_t requested_frame) const {
426  Json::Value root = BasePropertiesJSON(requested_frame);
427  root["amount"] = add_property_json("Amount", amount.GetValue(requested_frame), "float", "Overall grain intensity.", &amount, 0.0, 1.0, false, requested_frame);
428  root["size"] = add_property_json("Size", size.GetValue(requested_frame), "float", "Fine to coarse grain scale.", &size, 0.0, 1.0, false, requested_frame);
429  root["softness"] = add_property_json("Softness", softness.GetValue(requested_frame), "float", "Hard crisp grain to softer organic grain.", &softness, 0.0, 1.0, false, requested_frame);
430  root["clump"] = add_property_json("Clump", clump.GetValue(requested_frame), "float", "Even grain to clustered irregular grain.", &clump, 0.0, 1.0, false, requested_frame);
431  root["shadows"] = add_property_json("Shadows", shadows.GetValue(requested_frame), "float", "Grain strength in dark regions.", &shadows, 0.0, 1.0, false, requested_frame);
432  root["midtones"] = add_property_json("Midtones", midtones.GetValue(requested_frame), "float", "Grain strength in middle tonal regions.", &midtones, 0.0, 1.0, false, requested_frame);
433  root["highlights"] = add_property_json("Highlights", highlights.GetValue(requested_frame), "float", "Grain strength in bright regions.", &highlights, 0.0, 1.0, false, requested_frame);
434  root["color_amount"] = add_property_json("Color Amount", color_amount.GetValue(requested_frame), "float", "How much grain affects chroma instead of mostly luma.", &color_amount, 0.0, 1.0, false, requested_frame);
435  root["color_variation"] = add_property_json("Color Variation", color_variation.GetValue(requested_frame), "float", "Correlated to independently varied color grain.", &color_variation, 0.0, 1.0, false, requested_frame);
436  root["evolution"] = add_property_json("Evolution", evolution.GetValue(requested_frame), "float", "How much grain renews over time.", &evolution, 0.0, 1.0, false, requested_frame);
437  root["coherence"] = add_property_json("Coherence", coherence.GetValue(requested_frame), "float", "How stable and smooth grain remains between frames.", &coherence, 0.0, 1.0, false, requested_frame);
438  root["seed"] = add_property_json("Seed", seed, "int", "Deterministic grain variation.", NULL, 0, 1000000, false, requested_frame);
439  return root.toStyledString();
440 }
openshot::ClipBase::add_property_json
Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe *keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const
Generate JSON for a property.
Definition: ClipBase.cpp:96
openshot::stringToJson
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:16
openshot::FilmGrain::GetFrame
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number) override
This method is required for all derived classes of ClipBase, and returns a new openshot::Frame object...
Definition: FilmGrain.h:55
openshot::FilmGrain::color_variation
Keyframe color_variation
RGB channel independence.
Definition: FilmGrain.h:48
openshot::ClipBase::timeline
openshot::TimelineBase * timeline
Pointer to the parent timeline instance (if any)
Definition: ClipBase.h:40
openshot::EffectBase::info
EffectInfoStruct info
Information about the current effect.
Definition: EffectBase.h:110
Clip.h
Header file for Clip class.
openshot::FilmGrain::clump
Keyframe clump
Even vs clustered irregular structure.
Definition: FilmGrain.h:43
openshot::FilmGrain::amount
Keyframe amount
Overall grain intensity.
Definition: FilmGrain.h:40
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: AnimatedCurve.h:24
openshot::EffectBase::ParentClip
openshot::ClipBase * ParentClip()
Parent clip object of this effect (which can be unparented and NULL)
Definition: EffectBase.cpp:549
openshot::Clip
This class represents a clip (used to arrange readers on the timeline)
Definition: Clip.h:89
openshot::EffectBase::JsonValue
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: EffectBase.cpp:96
FilmGrain.h
Header file for FilmGrain effect.
Timeline.h
Header file for Timeline class.
openshot::FilmGrain::seed
int seed
Deterministic grain identity.
Definition: FilmGrain.h:51
openshot::Keyframe::SetJsonValue
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: KeyFrame.cpp:372
openshot::FilmGrain::Json
std::string Json() const override
Generate JSON string of this object.
Definition: FilmGrain.cpp:378
openshot::Keyframe::JsonValue
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: KeyFrame.cpp:339
openshot::FilmGrain::PropertiesJSON
std::string PropertiesJSON(int64_t requested_frame) const override
Definition: FilmGrain.cpp:425
openshot::EffectBase::BasePropertiesJSON
Json::Value BasePropertiesJSON(int64_t requested_frame) const
Generate JSON object of base properties (recommended to be used by all effects)
Definition: EffectBase.cpp:236
openshot::FilmGrain::JsonValue
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition: FilmGrain.cpp:382
openshot::InvalidJSON
Exception for invalid JSON.
Definition: Exceptions.h:223
openshot::FilmGrain::FilmGrain
FilmGrain()
Definition: FilmGrain.cpp:92
openshot::Timeline
This class represents a timeline.
Definition: Timeline.h:153
openshot::EffectBase::InitEffectInfo
void InitEffectInfo()
Definition: EffectBase.cpp:37
openshot::EffectInfoStruct::has_audio
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition: EffectBase.h:44
openshot::FilmGrain::highlights
Keyframe highlights
Grain strength in bright regions.
Definition: FilmGrain.h:46
openshot::FilmGrain::coherence
Keyframe coherence
Smoothness/stability between frames.
Definition: FilmGrain.h:50
openshot::FilmGrain::softness
Keyframe softness
Crisp vs soft organic grain.
Definition: FilmGrain.h:42
openshot::FilmGrain::shadows
Keyframe shadows
Grain strength in dark regions.
Definition: FilmGrain.h:44
openshot::FilmGrain::size
Keyframe size
Fine to coarse grain scale.
Definition: FilmGrain.h:41
openshot::EffectInfoStruct::class_name
std::string class_name
The class name of the effect.
Definition: EffectBase.h:39
openshot::EffectInfoStruct::description
std::string description
The description of this effect and what it does.
Definition: EffectBase.h:41
openshot::FilmGrain::SetJson
void SetJson(const std::string value) override
Load JSON string into this object.
Definition: FilmGrain.cpp:400
openshot::EffectInfoStruct::has_video
bool has_video
Determines if this effect manipulates the image of a frame.
Definition: EffectBase.h:43
openshot::ClipBase::Id
void Id(std::string value)
Definition: ClipBase.h:94
openshot::FilmGrain::midtones
Keyframe midtones
Grain strength in middle tonal regions.
Definition: FilmGrain.h:45
openshot::EffectInfoStruct::name
std::string name
The name of the effect.
Definition: EffectBase.h:40
openshot::FilmGrain::evolution
Keyframe evolution
How much the grain renews over time.
Definition: FilmGrain.h:49
openshot::FilmGrain::SetJsonValue
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition: FilmGrain.cpp:409
Exceptions.h
Header file for all Exception classes.
openshot::FilmGrain::color_amount
Keyframe color_amount
Chroma contribution amount.
Definition: FilmGrain.h:47
openshot::EffectBase::SetJsonValue
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: EffectBase.cpp:139
openshot::Keyframe::GetValue
double GetValue(int64_t index) const
Get the value at a specific index.
Definition: KeyFrame.cpp:258
openshot::EffectBase::clip
openshot::ClipBase * clip
Pointer to the parent clip instance (if any)
Definition: EffectBase.h:73