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