OpenShot Library | libopenshot  0.7.0
Timer.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 "Timer.h"
14 #include "Exceptions.h"
15 #include "../Clip.h"
16 #include "../Timeline.h"
17 
18 #include <algorithm>
19 #include <cmath>
20 #include <iomanip>
21 #include <sstream>
22 #include <vector>
23 
24 #include <QBrush>
25 #include <QColor>
26 #include <QFont>
27 #include <QFontMetricsF>
28 #include <QImage>
29 #include <QPainter>
30 #include <QPainterPath>
31 #include <QPen>
32 #include <QPointF>
33 #include <QRectF>
34 #include <QString>
35 
36 using namespace openshot;
37 
38 namespace {
39  int ClampGravity(int gravity)
40  {
41  if (gravity < GRAVITY_TOP_LEFT || gravity > GRAVITY_BOTTOM_RIGHT)
42  return GRAVITY_BOTTOM;
43  return gravity;
44  }
45 }
46 
48  mode(TIMER_MODE_COUNT_UP),
49  time_source(TIMER_TIME_SOURCE),
50  format(TIMER_FORMAT_MM_SS),
51  clamp(1),
52  gravity(GRAVITY_BOTTOM),
53  show_background(1),
54  font_name("sans"),
55  prefix(""),
56  suffix(""),
57  color("#ffffff"),
58  stroke("#000000"),
59  background("#000000"),
60  start_time(0.0),
61  end_time(0.0),
62  font_size(48.0),
63  font_alpha(1.0),
64  stroke_width(2.0),
65  x_offset(0.0),
66  y_offset(0.0),
67  background_alpha(0.45),
68  background_padding(14.0),
69  background_corner(6.0)
70 {
71  init_effect_details();
72 }
73 
74 void Timer::init_effect_details()
75 {
77  info.class_name = "Timer";
78  info.name = "Timer";
79  info.description = "Render a styled count up, count down, clock, timecode, or frame number overlay.";
80  info.has_audio = false;
81  info.has_video = true;
82 }
83 
84 double Timer::ResolveFps() const
85 {
86  Clip* clip = (Clip*) const_cast<Timer*>(this)->ParentClip();
87  Timeline* timeline = NULL;
88 
89  if (clip && clip->ParentTimeline() != NULL) {
90  timeline = (Timeline*) clip->ParentTimeline();
91  } else if (const_cast<Timer*>(this)->ParentTimeline() != NULL) {
92  timeline = (Timeline*) const_cast<Timer*>(this)->ParentTimeline();
93  }
94 
95  if (time_source == TIMER_TIME_SOURCE && clip != NULL && clip->Reader() != NULL && clip->Reader()->info.fps.ToDouble() > 0.0)
96  return clip->Reader()->info.fps.ToDouble();
97  if (timeline != NULL && timeline->info.fps.ToDouble() > 0.0)
98  return timeline->info.fps.ToDouble();
99  if (clip != NULL && clip->Reader() != NULL && clip->Reader()->info.fps.ToDouble() > 0.0)
100  return clip->Reader()->info.fps.ToDouble();
101  return 30.0;
102 }
103 
104 int64_t Timer::EffectiveFrameNumber(int64_t frame_number) const
105 {
107  return std::max<int64_t>(1, frame_number);
108 
109  Clip* clip = (Clip*) const_cast<Timer*>(this)->ParentClip();
110  if (clip != NULL && clip->time.GetLength() > 1)
111  return std::max<int64_t>(1, clip->time.GetLong(frame_number));
112 
113  return std::max<int64_t>(1, frame_number);
114 }
115 
116 double Timer::CountdownDuration(int64_t frame_number) const
117 {
118  const double configured_duration = end_time.GetValue(frame_number);
119  if (configured_duration > 0.0)
120  return configured_duration;
121 
122  Clip* clip = (Clip*) const_cast<Timer*>(this)->ParentClip();
123  if (clip != NULL)
124  return std::max(0.0f, clip->End() - clip->Start());
125 
126  return 0.0;
127 }
128 
129 std::string Timer::FormatSeconds(double seconds, double fps, bool duration_style) const
130 {
131  if (duration_style && clamp && seconds < 0.0)
132  seconds = 0.0;
133 
134  const bool negative = seconds < 0.0;
135  const int64_t total_milliseconds = std::llround(std::fabs(seconds) * 1000.0);
136  const int milliseconds = static_cast<int>(total_milliseconds % 1000);
137  const int64_t total_seconds = total_milliseconds / 1000;
138  const int hours = static_cast<int>(total_seconds / 3600);
139  const int minutes = static_cast<int>((total_seconds / 60) % 60);
140  const int whole_seconds = static_cast<int>(total_seconds % 60);
141 
142  std::ostringstream text;
143  if (negative)
144  text << "-";
145 
147  text << std::setfill('0') << std::setw(2) << hours << ":"
148  << std::setw(2) << minutes << ":"
149  << std::setw(2) << whole_seconds << "."
150  << std::setw(3) << milliseconds;
151  } else if (format == TIMER_FORMAT_HH_MM_SS) {
152  text << std::setfill('0') << std::setw(2) << hours << ":"
153  << std::setw(2) << minutes << ":"
154  << std::setw(2) << whole_seconds;
155  } else if (format == TIMER_FORMAT_TIMECODE) {
156  text << FormatTimecode(seconds, fps);
157  } else if (format == TIMER_FORMAT_FRAMES) {
158  text << static_cast<int64_t>(std::llround(seconds * fps));
159  } else {
160  const int total_minutes = hours * 60 + minutes;
161  text << std::setfill('0') << std::setw(2) << total_minutes << ":"
162  << std::setw(2) << whole_seconds;
163  }
164 
165  return text.str();
166 }
167 
168 std::string Timer::FormatTimecode(double seconds, double fps) const
169 {
170  const bool negative = seconds < 0.0;
171  const int64_t total_frames = std::llround(std::fabs(seconds) * fps);
172  const int64_t rounded_fps = std::max<int64_t>(1, std::llround(fps));
173  const int64_t frames = total_frames % rounded_fps;
174  const int64_t total_seconds = total_frames / rounded_fps;
175  const int64_t secs = total_seconds % 60;
176  const int64_t mins = (total_seconds / 60) % 60;
177  const int64_t hours = total_seconds / 3600;
178 
179  std::ostringstream text;
180  if (negative)
181  text << "-";
182  text << std::setfill('0') << std::setw(2) << hours << ":"
183  << std::setw(2) << mins << ":"
184  << std::setw(2) << secs << ":"
185  << std::setw(2) << frames;
186  return text.str();
187 }
188 
189 double Timer::TimerSeconds(int64_t frame_number) const
190 {
191  const double fps = ResolveFps();
192  const int64_t effective_frame = EffectiveFrameNumber(frame_number);
193  const double elapsed = static_cast<double>(effective_frame - 1) / fps;
194 
196  return CountdownDuration(frame_number) - start_time.GetValue(frame_number) - elapsed;
197 
198  return start_time.GetValue(frame_number) + elapsed;
199 }
200 
201 std::string Timer::TimerText(int64_t frame_number) const
202 {
203  const double fps = ResolveFps();
204  const int64_t effective_frame = EffectiveFrameNumber(frame_number);
205  std::ostringstream text;
206  text << prefix;
207 
209  const int64_t frame_offset = std::llround(start_time.GetValue(frame_number) * fps);
210  text << (effective_frame + frame_offset);
212  text << FormatTimecode(TimerSeconds(frame_number), fps);
213  } else if (mode == TIMER_MODE_CLOCK) {
214  double seconds = std::fmod(TimerSeconds(frame_number), 24.0 * 60.0 * 60.0);
215  if (seconds < 0.0)
216  seconds += 24.0 * 60.0 * 60.0;
217  text << FormatSeconds(seconds, fps, false);
218  } else {
219  text << FormatSeconds(TimerSeconds(frame_number), fps, true);
220  }
221 
222  text << suffix;
223  return text.str();
224 }
225 
226 std::string Timer::TimerLayoutText(int64_t frame_number) const
227 {
229  return TimerText(frame_number);
230 
231  std::string timer_template;
233  timer_template = "88:88:88:88";
235  timer_template = "88:88:88.888";
236  else if (format == TIMER_FORMAT_HH_MM_SS)
237  timer_template = "88:88:88";
238  else
239  timer_template = "88:88";
240 
241  return prefix + timer_template + suffix;
242 }
243 
244 std::shared_ptr<openshot::Frame> Timer::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number)
245 {
246  Clip* clip = (Clip*) ParentClip();
247  Timeline* timeline = NULL;
248  QSize image_size(1280, 720);
249 
250  if (clip && clip->ParentTimeline() != NULL) {
251  timeline = (Timeline*) clip->ParentTimeline();
252  } else if (this->ParentTimeline() != NULL) {
253  timeline = (Timeline*) this->ParentTimeline();
254  }
255 
256  if (timeline != NULL) {
257  image_size = QSize(timeline->info.width, timeline->info.height);
258  } else if (clip != NULL && clip->Reader() != NULL) {
259  image_size = QSize(clip->Reader()->info.width, clip->Reader()->info.height);
260  }
261 
262  if (!frame->has_image_data)
263  frame->AddColor(image_size.width(), image_size.height(), "#00000000");
264 
265  std::shared_ptr<QImage> frame_image = frame->GetImage();
266  if (!frame_image || frame_image->isNull())
267  return frame;
268 
269  const double scale_factor = frame_image->width() / 600.0;
270  const double font_size_value = std::max(1.0, font_size.GetValue(frame_number) * scale_factor);
271  const double stroke_width_value = std::max(0.0, stroke_width.GetValue(frame_number) * scale_factor);
272  const double padding_value = std::max(0.0, background_padding.GetValue(frame_number) * scale_factor);
273  const double corner_value = std::max(0.0, background_corner.GetValue(frame_number) * scale_factor);
274  const QString timer_text = QString::fromStdString(TimerText(frame_number));
275  const QString layout_text = QString::fromStdString(TimerLayoutText(frame_number));
276 
277  QFont font(QString::fromStdString(font_name), int(font_size_value));
278  font.setPixelSize(font_size_value);
279  QFontMetricsF metrics(font);
280  QRectF text_bounds = metrics.tightBoundingRect(timer_text);
281  if (text_bounds.isEmpty())
282  text_bounds = metrics.boundingRect(timer_text);
283  QRectF layout_bounds = metrics.tightBoundingRect(layout_text);
284  if (layout_bounds.isEmpty())
285  layout_bounds = metrics.boundingRect(layout_text);
286 
287  const double text_width = std::max(1.0, text_bounds.width());
288  const double text_height = std::max(1.0, text_bounds.height());
289  double digit_slot_width = 0.0;
290  for (int digit = 0; digit <= 9; ++digit)
291  digit_slot_width = std::max(digit_slot_width, metrics.horizontalAdvance(QString::number(digit)));
292 
293  const bool use_slot_layout = timer_text.size() == layout_text.size() &&
295  std::vector<double> slot_widths;
296  double layout_width = 0.0;
297  if (use_slot_layout) {
298  slot_widths.reserve(layout_text.size());
299  for (int index = 0; index < layout_text.size(); ++index) {
300  const QString layout_char(layout_text[index]);
301  const double slot_width = layout_text[index].isDigit() ? digit_slot_width : metrics.horizontalAdvance(layout_char);
302  slot_widths.push_back(slot_width);
303  layout_width += slot_width;
304  }
305  } else {
306  layout_width = std::max(text_width, layout_bounds.width());
307  }
308  layout_width = std::max(1.0, layout_width);
309  const double rendered_box_width = layout_width + (padding_value * 2.0);
310  const double box_height = std::max(text_height, layout_bounds.height()) + (padding_value * 2.0);
311 
312  double x = padding_value;
313  double y = 0.0;
314  const int resolved_gravity = ClampGravity(gravity);
315  if (resolved_gravity == GRAVITY_TOP || resolved_gravity == GRAVITY_CENTER || resolved_gravity == GRAVITY_BOTTOM)
316  x = (frame_image->width() - rendered_box_width) / 2.0;
317  else if (resolved_gravity == GRAVITY_TOP_RIGHT || resolved_gravity == GRAVITY_RIGHT || resolved_gravity == GRAVITY_BOTTOM_RIGHT)
318  x = frame_image->width() - rendered_box_width - padding_value;
319 
320  switch (resolved_gravity) {
321  case GRAVITY_TOP_LEFT:
322  case GRAVITY_TOP:
323  case GRAVITY_TOP_RIGHT:
324  break;
325  case GRAVITY_LEFT:
326  case GRAVITY_CENTER:
327  case GRAVITY_RIGHT:
328  y = (frame_image->height() - box_height) / 2.0;
329  break;
330  case GRAVITY_BOTTOM_LEFT:
331  case GRAVITY_BOTTOM:
333  y = frame_image->height() - box_height;
334  break;
335  default:
336  break;
337  }
338  x += frame_image->width() * (x_offset.GetValue(frame_number) / 100.0);
339  y += frame_image->height() * (y_offset.GetValue(frame_number) / 100.0);
340  x = std::round(x);
341  y = std::round(y);
342 
343  double text_x = x + padding_value - text_bounds.left();
344  if (resolved_gravity == GRAVITY_TOP || resolved_gravity == GRAVITY_CENTER || resolved_gravity == GRAVITY_BOTTOM)
345  text_x = x + padding_value + ((layout_width - text_width) / 2.0) - text_bounds.left();
346  else if (resolved_gravity == GRAVITY_TOP_RIGHT || resolved_gravity == GRAVITY_RIGHT || resolved_gravity == GRAVITY_BOTTOM_RIGHT)
347  text_x = x + padding_value + layout_width - text_width - text_bounds.left();
348  text_x = std::round(text_x);
349  const double text_y = std::round(y + padding_value - text_bounds.top());
350 
351  QPainter painter(frame_image.get());
352  painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing, true);
353  painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
354 
355  if (show_background) {
356  QColor background_qcolor(QString::fromStdString(background.GetColorHex(frame_number)));
357  background_qcolor.setAlphaF(std::max(0.0, std::min(1.0, background_alpha.GetValue(frame_number))));
358  painter.setPen(Qt::NoPen);
359  painter.setBrush(QBrush(background_qcolor));
360  painter.drawRoundedRect(QRectF(x, y, rendered_box_width, box_height), corner_value, corner_value);
361  }
362 
363  QPainterPath path;
364  if (use_slot_layout) {
365  double cursor_x = x + padding_value;
366  for (int index = 0; index < timer_text.size(); ++index) {
367  const QString text_char(timer_text[index]);
368  QRectF char_bounds = metrics.tightBoundingRect(text_char);
369  if (char_bounds.isEmpty())
370  char_bounds = metrics.boundingRect(text_char);
371 
372  const double char_x = std::round(cursor_x);
373  path.addText(QPointF(char_x - char_bounds.left(), text_y), font, text_char);
374  cursor_x += slot_widths[index];
375  }
376  } else {
377  path.addText(QPointF(text_x, text_y), font, timer_text);
378  }
379 
380  QColor stroke_qcolor(QString::fromStdString(stroke.GetColorHex(frame_number)));
381  stroke_qcolor.setAlphaF(std::max(0.0, std::min(1.0, font_alpha.GetValue(frame_number))));
382  QPen pen(stroke_qcolor);
383  pen.setWidthF(stroke_width_value);
384  painter.setPen(stroke_width_value <= 0.0 ? Qt::NoPen : pen);
385 
386  QColor font_qcolor(QString::fromStdString(color.GetColorHex(frame_number)));
387  font_qcolor.setAlphaF(std::max(0.0, std::min(1.0, font_alpha.GetValue(frame_number))));
388  painter.setBrush(QBrush(font_qcolor));
389  painter.drawPath(path);
390  painter.end();
391 
392  return frame;
393 }
394 
395 std::string Timer::Json() const
396 {
397  return JsonValue().toStyledString();
398 }
399 
400 Json::Value Timer::JsonValue() const
401 {
402  Json::Value root = EffectBase::JsonValue();
403  root["type"] = info.class_name;
404  root["mode"] = mode;
405  root["time_source"] = time_source;
406  root["format"] = format;
407  root["clamp"] = clamp;
408  root["gravity"] = ClampGravity(gravity);
409  root["show_background"] = show_background;
410  root["font_name"] = font_name;
411  root["prefix"] = prefix;
412  root["suffix"] = suffix;
413  root["color"] = color.JsonValue();
414  root["stroke"] = stroke.JsonValue();
415  root["background"] = background.JsonValue();
416  root["start_time"] = start_time.JsonValue();
417  root["end_time"] = end_time.JsonValue();
418  root["font_size"] = font_size.JsonValue();
419  root["font_alpha"] = font_alpha.JsonValue();
420  root["stroke_width"] = stroke_width.JsonValue();
421  root["x_offset"] = x_offset.JsonValue();
422  root["y_offset"] = y_offset.JsonValue();
423  root["background_alpha"] = background_alpha.JsonValue();
424  root["background_padding"] = background_padding.JsonValue();
425  root["background_corner"] = background_corner.JsonValue();
426  return root;
427 }
428 
429 void Timer::SetJson(const std::string value)
430 {
431  try
432  {
433  const Json::Value root = openshot::stringToJson(value);
434  SetJsonValue(root);
435  }
436  catch (const std::exception& e)
437  {
438  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
439  }
440 }
441 
442 void Timer::SetJsonValue(const Json::Value root)
443 {
445 
446  if (!root["mode"].isNull())
447  mode = root["mode"].asInt();
448  if (!root["time_source"].isNull())
449  time_source = root["time_source"].asInt();
450  if (!root["format"].isNull())
451  format = root["format"].asInt();
452  if (!root["clamp"].isNull())
453  clamp = root["clamp"].asInt();
454  if (!root["gravity"].isNull())
455  gravity = ClampGravity(root["gravity"].asInt());
456  if (!root["show_background"].isNull())
457  show_background = root["show_background"].asInt();
458  if (!root["font_name"].isNull())
459  font_name = root["font_name"].asString();
460  if (!root["prefix"].isNull())
461  prefix = root["prefix"].asString();
462  if (!root["suffix"].isNull())
463  suffix = root["suffix"].asString();
464  if (!root["color"].isNull())
465  color.SetJsonValue(root["color"]);
466  if (!root["stroke"].isNull())
467  stroke.SetJsonValue(root["stroke"]);
468  if (!root["background"].isNull())
469  background.SetJsonValue(root["background"]);
470  if (!root["start_time"].isNull())
471  start_time.SetJsonValue(root["start_time"]);
472  if (!root["end_time"].isNull())
473  end_time.SetJsonValue(root["end_time"]);
474  if (!root["font_size"].isNull())
475  font_size.SetJsonValue(root["font_size"]);
476  if (!root["font_alpha"].isNull())
477  font_alpha.SetJsonValue(root["font_alpha"]);
478  if (!root["stroke_width"].isNull())
479  stroke_width.SetJsonValue(root["stroke_width"]);
480  if (!root["x_offset"].isNull())
481  x_offset.SetJsonValue(root["x_offset"]);
482  if (!root["y_offset"].isNull())
483  y_offset.SetJsonValue(root["y_offset"]);
484  if (!root["background_alpha"].isNull())
485  background_alpha.SetJsonValue(root["background_alpha"]);
486  if (!root["background_padding"].isNull())
487  background_padding.SetJsonValue(root["background_padding"]);
488  if (!root["background_corner"].isNull())
489  background_corner.SetJsonValue(root["background_corner"]);
490 }
491 
492 std::string Timer::PropertiesJSON(int64_t requested_frame) const
493 {
494  Json::Value root = BasePropertiesJSON(requested_frame);
495 
496  root["mode"] = add_property_json("Mode", mode, "int", "", NULL, TIMER_MODE_COUNT_UP, TIMER_MODE_FRAME_NUMBER, false, requested_frame);
497  root["mode"]["choices"].append(add_property_choice_json("Count Up", TIMER_MODE_COUNT_UP, mode));
498  root["mode"]["choices"].append(add_property_choice_json("Count Down", TIMER_MODE_COUNT_DOWN, mode));
499  root["mode"]["choices"].append(add_property_choice_json("Clock", TIMER_MODE_CLOCK, mode));
500  root["mode"]["choices"].append(add_property_choice_json("Timecode", TIMER_MODE_TIMECODE, mode));
501  root["mode"]["choices"].append(add_property_choice_json("Frame Number", TIMER_MODE_FRAME_NUMBER, mode));
502  root["time_source"] = add_property_json("Time Source", time_source, "int", "", NULL, TIMER_TIME_CLIP, TIMER_TIME_SOURCE, false, requested_frame);
503  root["time_source"]["choices"].append(add_property_choice_json("Clip Time", TIMER_TIME_CLIP, time_source));
504  root["time_source"]["choices"].append(add_property_choice_json("Source Time", TIMER_TIME_SOURCE, time_source));
505  root["format"] = add_property_json("Format", format, "int", "", NULL, TIMER_FORMAT_MM_SS, TIMER_FORMAT_FRAMES, false, requested_frame);
506  root["format"]["choices"].append(add_property_choice_json("MM:SS", TIMER_FORMAT_MM_SS, format));
507  root["format"]["choices"].append(add_property_choice_json("HH:MM:SS", TIMER_FORMAT_HH_MM_SS, format));
508  root["format"]["choices"].append(add_property_choice_json("HH:MM:SS.mmm", TIMER_FORMAT_HH_MM_SS_MILLISECONDS, format));
509  root["format"]["choices"].append(add_property_choice_json("Timecode", TIMER_FORMAT_TIMECODE, format));
510  root["format"]["choices"].append(add_property_choice_json("Frames", TIMER_FORMAT_FRAMES, format));
511  root["start_time"] = add_property_json("Start Time", start_time.GetValue(requested_frame), "float", "", &start_time, -86400.0, 86400.0, false, requested_frame);
512  root["end_time"] = add_property_json("Countdown Duration", end_time.GetValue(requested_frame), "float", "", &end_time, 0.0, 86400.0, false, requested_frame);
513  root["clamp"] = add_property_json("Clamp at Zero", clamp, "int", "", NULL, 0, 1, false, requested_frame);
514  root["clamp"]["choices"].append(add_property_choice_json("Yes", 1, clamp));
515  root["clamp"]["choices"].append(add_property_choice_json("No", 0, clamp));
516  root["prefix"] = add_property_json("Prefix", 0.0, "string", prefix, NULL, -1, -1, false, requested_frame);
517  root["suffix"] = add_property_json("Suffix", 0.0, "string", suffix, NULL, -1, -1, false, requested_frame);
518  root["font_name"] = add_property_json("Font", 0.0, "font", font_name, NULL, -1, -1, false, requested_frame);
519  root["font_size"] = add_property_json("Font Size", font_size.GetValue(requested_frame), "float", "", &font_size, 1.0, 300.0, false, requested_frame);
520  root["font_alpha"] = add_property_json("Font Alpha", font_alpha.GetValue(requested_frame), "float", "", &font_alpha, 0.0, 1.0, false, requested_frame);
521  root["color"] = add_property_json("Text Color", 0.0, "color", "", &color.red, 0, 255, false, requested_frame);
522  root["color"]["red"] = add_property_json("Red", color.red.GetValue(requested_frame), "float", "", &color.red, 0, 255, false, requested_frame);
523  root["color"]["blue"] = add_property_json("Blue", color.blue.GetValue(requested_frame), "float", "", &color.blue, 0, 255, false, requested_frame);
524  root["color"]["green"] = add_property_json("Green", color.green.GetValue(requested_frame), "float", "", &color.green, 0, 255, false, requested_frame);
525  root["stroke"] = add_property_json("Stroke Color", 0.0, "color", "", &stroke.red, 0, 255, false, requested_frame);
526  root["stroke"]["red"] = add_property_json("Red", stroke.red.GetValue(requested_frame), "float", "", &stroke.red, 0, 255, false, requested_frame);
527  root["stroke"]["blue"] = add_property_json("Blue", stroke.blue.GetValue(requested_frame), "float", "", &stroke.blue, 0, 255, false, requested_frame);
528  root["stroke"]["green"] = add_property_json("Green", stroke.green.GetValue(requested_frame), "float", "", &stroke.green, 0, 255, false, requested_frame);
529  root["stroke_width"] = add_property_json("Stroke Width", stroke_width.GetValue(requested_frame), "float", "", &stroke_width, 0.0, 20.0, false, requested_frame);
530  const int resolved_gravity = ClampGravity(gravity);
531  root["gravity"] = add_property_json("Gravity", resolved_gravity, "int", "", NULL, GRAVITY_TOP_LEFT, GRAVITY_BOTTOM_RIGHT, false, requested_frame);
532  root["gravity"]["choices"].append(add_property_choice_json("Top Left", GRAVITY_TOP_LEFT, resolved_gravity));
533  root["gravity"]["choices"].append(add_property_choice_json("Top Center", GRAVITY_TOP, resolved_gravity));
534  root["gravity"]["choices"].append(add_property_choice_json("Top Right", GRAVITY_TOP_RIGHT, resolved_gravity));
535  root["gravity"]["choices"].append(add_property_choice_json("Left", GRAVITY_LEFT, resolved_gravity));
536  root["gravity"]["choices"].append(add_property_choice_json("Center", GRAVITY_CENTER, resolved_gravity));
537  root["gravity"]["choices"].append(add_property_choice_json("Right", GRAVITY_RIGHT, resolved_gravity));
538  root["gravity"]["choices"].append(add_property_choice_json("Bottom Left", GRAVITY_BOTTOM_LEFT, resolved_gravity));
539  root["gravity"]["choices"].append(add_property_choice_json("Bottom Center", GRAVITY_BOTTOM, resolved_gravity));
540  root["gravity"]["choices"].append(add_property_choice_json("Bottom Right", GRAVITY_BOTTOM_RIGHT, resolved_gravity));
541  root["x_offset"] = add_property_json("X Offset (%)", x_offset.GetValue(requested_frame), "float", "", &x_offset, -100.0, 100.0, false, requested_frame);
542  root["y_offset"] = add_property_json("Y Offset (%)", y_offset.GetValue(requested_frame), "float", "", &y_offset, -100.0, 100.0, false, requested_frame);
543  root["show_background"] = add_property_json("Show Background", show_background, "int", "", NULL, 0, 1, false, requested_frame);
544  root["show_background"]["choices"].append(add_property_choice_json("Yes", 1, show_background));
545  root["show_background"]["choices"].append(add_property_choice_json("No", 0, show_background));
546  root["background"] = add_property_json("Background Color", 0.0, "color", "", &background.red, 0, 255, false, requested_frame);
547  root["background"]["red"] = add_property_json("Red", background.red.GetValue(requested_frame), "float", "", &background.red, 0, 255, false, requested_frame);
548  root["background"]["blue"] = add_property_json("Blue", background.blue.GetValue(requested_frame), "float", "", &background.blue, 0, 255, false, requested_frame);
549  root["background"]["green"] = add_property_json("Green", background.green.GetValue(requested_frame), "float", "", &background.green, 0, 255, false, requested_frame);
550  root["background_alpha"] = add_property_json("Background Alpha", background_alpha.GetValue(requested_frame), "float", "", &background_alpha, 0.0, 1.0, false, requested_frame);
551  root["background_padding"] = add_property_json("Background Padding", background_padding.GetValue(requested_frame), "float", "", &background_padding, 0.0, 100.0, false, requested_frame);
552  root["background_corner"] = add_property_json("Background Corner Radius", background_corner.GetValue(requested_frame), "float", "", &background_corner, 0.0, 100.0, false, requested_frame);
553 
554  return root.toStyledString();
555 }
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::Timer::background_alpha
Keyframe background_alpha
Definition: Timer.h:80
openshot::Timer::background_padding
Keyframe background_padding
Definition: Timer.h:81
openshot::TIMER_FORMAT_HH_MM_SS_MILLISECONDS
@ TIMER_FORMAT_HH_MM_SS_MILLISECONDS
Definition: Timer.h:44
openshot::ClipBase::timeline
openshot::TimelineBase * timeline
Pointer to the parent timeline instance (if any)
Definition: ClipBase.h:40
openshot::Timer::TimerSeconds
double TimerSeconds(int64_t frame_number) const
Definition: Timer.cpp:189
openshot::EffectBase::info
EffectInfoStruct info
Information about the current effect.
Definition: EffectBase.h:114
openshot::Timer::PropertiesJSON
std::string PropertiesJSON(int64_t requested_frame) const override
Definition: Timer.cpp:492
openshot::Timer::gravity
int gravity
Definition: Timer.h:65
openshot::Timer::y_offset
Keyframe y_offset
Definition: Timer.h:79
openshot::ClipBase::End
virtual void End(float value)
Set end position (in seconds) of clip (trim end of video)
Definition: ClipBase.cpp:53
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: AnimatedCurve.h:24
openshot::Timer::stroke_width
Keyframe stroke_width
Definition: Timer.h:77
openshot::EffectBase::ParentClip
openshot::ClipBase * ParentClip()
Parent clip object of this effect (which can be unparented and NULL)
Definition: EffectBase.cpp:676
openshot::Timer::stroke
Color stroke
Definition: Timer.h:71
openshot::ClipBase::add_property_choice_json
Json::Value add_property_choice_json(std::string name, int value, int selected_value) const
Generate JSON choice for a property (dropdown properties)
Definition: ClipBase.cpp:132
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:102
openshot::GRAVITY_TOP_LEFT
@ GRAVITY_TOP_LEFT
Align clip to the top left of its parent.
Definition: Enums.h:23
openshot::Timer::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: Timer.h:86
openshot::Timer::font_name
std::string font_name
Definition: Timer.h:67
openshot::GRAVITY_TOP_RIGHT
@ GRAVITY_TOP_RIGHT
Align clip to the top right of its parent.
Definition: Enums.h:25
openshot::Keyframe::SetJsonValue
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: KeyFrame.cpp:372
openshot::Timer::color
Color color
Definition: Timer.h:70
openshot::TIMER_TIME_SOURCE
@ TIMER_TIME_SOURCE
Definition: Timer.h:38
openshot::TIMER_MODE_TIMECODE
@ TIMER_MODE_TIMECODE
Definition: Timer.h:32
openshot::Timer::clamp
int clamp
Definition: Timer.h:64
openshot::Timer::background_corner
Keyframe background_corner
Definition: Timer.h:82
openshot::Timer::font_size
Keyframe font_size
Definition: Timer.h:75
openshot::GRAVITY_RIGHT
@ GRAVITY_RIGHT
Align clip to the right of its parent (middle aligned)
Definition: Enums.h:28
openshot::Timer::SetJson
void SetJson(const std::string value) override
Load JSON string into this object.
Definition: Timer.cpp:429
openshot::Keyframe::JsonValue
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: KeyFrame.cpp:339
openshot::GRAVITY_TOP
@ GRAVITY_TOP
Align clip to the top center of its parent.
Definition: Enums.h:24
openshot::Timer::TimerText
std::string TimerText(int64_t frame_number) const
Definition: Timer.cpp:201
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:257
openshot::TIMER_FORMAT_FRAMES
@ TIMER_FORMAT_FRAMES
Definition: Timer.h:46
openshot::Timer::SetJsonValue
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition: Timer.cpp:442
openshot::TIMER_FORMAT_HH_MM_SS
@ TIMER_FORMAT_HH_MM_SS
Definition: Timer.h:43
openshot::Timer::start_time
Keyframe start_time
Definition: Timer.h:73
openshot::Color::SetJsonValue
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: Color.cpp:117
openshot::GRAVITY_BOTTOM
@ GRAVITY_BOTTOM
Align clip to the bottom center of its parent.
Definition: Enums.h:30
Timer.h
Header file for Timer effect class.
openshot::InvalidJSON
Exception for invalid JSON.
Definition: Exceptions.h:223
openshot::Timeline
This class represents a timeline.
Definition: Timeline.h:153
openshot::Timer::prefix
std::string prefix
Definition: Timer.h:68
openshot::Timer::show_background
int show_background
Definition: Timer.h:66
openshot::EffectBase::InitEffectInfo
void InitEffectInfo()
Definition: EffectBase.cpp:42
openshot::Color::green
openshot::Keyframe green
Curve representing the green value (0 - 255)
Definition: Color.h:31
openshot::EffectInfoStruct::has_audio
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition: EffectBase.h:44
openshot::TIMER_MODE_FRAME_NUMBER
@ TIMER_MODE_FRAME_NUMBER
Definition: Timer.h:33
openshot::Timer::Json
std::string Json() const override
Generate JSON string of this object.
Definition: Timer.cpp:395
openshot::Timer::Timer
Timer()
Definition: Timer.cpp:47
openshot::ClipBase::Start
void Start(float value)
Set start position (in seconds) of clip (trim start of video)
Definition: ClipBase.cpp:42
path
path
Definition: FFmpegWriter.cpp:1578
openshot::Timer::time_source
int time_source
Definition: Timer.h:62
openshot::TIMER_MODE_COUNT_UP
@ TIMER_MODE_COUNT_UP
Definition: Timer.h:29
openshot::GRAVITY_BOTTOM_LEFT
@ GRAVITY_BOTTOM_LEFT
Align clip to the bottom left of its parent.
Definition: Enums.h:29
openshot::Timer::JsonValue
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition: Timer.cpp:400
openshot::TIMER_TIME_CLIP
@ TIMER_TIME_CLIP
Definition: Timer.h:37
openshot::EffectInfoStruct::class_name
std::string class_name
The class name of the effect.
Definition: EffectBase.h:39
openshot::GRAVITY_BOTTOM_RIGHT
@ GRAVITY_BOTTOM_RIGHT
Align clip to the bottom right of its parent.
Definition: Enums.h:31
openshot::Color::JsonValue
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: Color.cpp:86
openshot::Timer::background
Color background
Definition: Timer.h:72
openshot::EffectInfoStruct::description
std::string description
The description of this effect and what it does.
Definition: EffectBase.h:41
openshot::Timer::format
int format
Definition: Timer.h:63
openshot::Timer::end_time
Keyframe end_time
Definition: Timer.h:74
openshot::EffectInfoStruct::has_video
bool has_video
Determines if this effect manipulates the image of a frame.
Definition: EffectBase.h:43
openshot::Timer
Definition: Timer.h:49
openshot::Timer::mode
int mode
Definition: Timer.h:61
openshot::TIMER_FORMAT_MM_SS
@ TIMER_FORMAT_MM_SS
Definition: Timer.h:42
openshot::GRAVITY_LEFT
@ GRAVITY_LEFT
Align clip to the left of its parent (middle aligned)
Definition: Enums.h:26
openshot::EffectInfoStruct::name
std::string name
The name of the effect.
Definition: EffectBase.h:40
openshot::Timer::suffix
std::string suffix
Definition: Timer.h:69
openshot::TIMER_MODE_CLOCK
@ TIMER_MODE_CLOCK
Definition: Timer.h:31
openshot::GRAVITY_CENTER
@ GRAVITY_CENTER
Align clip to the center of its parent (middle aligned)
Definition: Enums.h:27
openshot::TIMER_FORMAT_TIMECODE
@ TIMER_FORMAT_TIMECODE
Definition: Timer.h:45
openshot::Timer::font_alpha
Keyframe font_alpha
Definition: Timer.h:76
openshot::Color::red
openshot::Keyframe red
Curve representing the red value (0 - 255)
Definition: Color.h:30
openshot::Timer::x_offset
Keyframe x_offset
Definition: Timer.h:78
openshot::Color::GetColorHex
std::string GetColorHex(int64_t frame_number)
Get the HEX value of a color at a specific frame.
Definition: Color.cpp:47
openshot::Color::blue
openshot::Keyframe blue
Curve representing the red value (0 - 255)
Definition: Color.h:32
Exceptions.h
Header file for all Exception classes.
openshot::TIMER_MODE_COUNT_DOWN
@ TIMER_MODE_COUNT_DOWN
Definition: Timer.h:30
openshot::EffectBase::SetJsonValue
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: EffectBase.cpp:146
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:77