From 4de0001e530c1c17f384ed6396b3f2a90b49b4fd Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 6 Oct 2020 03:00:58 -0500 Subject: [PATCH 1/7] Adding initial caption effect, which supports VTT and SubRip formats (limited support, no formating, no regions) --- include/Effects.h | 1 + include/effects/Caption.h | 131 +++++++++++++++ src/CMakeLists.txt | 3 +- src/EffectInfo.cpp | 4 + src/effects/Caption.cpp | 332 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 470 insertions(+), 1 deletion(-) create mode 100644 include/effects/Caption.h create mode 100644 src/effects/Caption.cpp diff --git a/include/Effects.h b/include/Effects.h index 746da4c0..2acddd69 100644 --- a/include/Effects.h +++ b/include/Effects.h @@ -35,6 +35,7 @@ #include "effects/Bars.h" #include "effects/Blur.h" #include "effects/Brightness.h" +#include "effects/Caption.h" #include "effects/ChromaKey.h" #include "effects/ColorShift.h" #include "effects/Crop.h" diff --git a/include/effects/Caption.h b/include/effects/Caption.h new file mode 100644 index 00000000..f2f55126 --- /dev/null +++ b/include/effects/Caption.h @@ -0,0 +1,131 @@ +/** + * @file + * @brief Header file for Caption effect class + * @author Jonathan Thomas + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#ifndef OPENSHOT_CAPTION_EFFECT_H +#define OPENSHOT_CAPTION_EFFECT_H + +#include "../EffectBase.h" + +#include +#include +#include +#include +#include "../Color.h" +#include "../Fraction.h" +#include "../Json.h" +#include "../KeyFrame.h" + + + +namespace openshot +{ + + /** + * @brief This class adds captions/text over a video, based on timestamps. You can also animate some limited + * aspects, such as words appearing/disappearing. + * + * Adding captions can be an easy way to generate text overlays through-out a long clip. + */ + class Caption : public EffectBase + { + private: + std::vector matchedCaptions; ///< RegEx to capture cues and text + std::string caption_text; ///< Text of caption + std::string caption_format; ///< Format of caption (application/x-subrip, text/vtt) + bool is_dirty; + + /// Init effect settings + void init_effect_details(); + + /// Process regex capture + void process_regex(); + + + public: + Color color; ///< Color of caption text + Color stroke; ///< Color of text border / stroke + Keyframe stroke_width; ///< Width of text border / stroke + Keyframe font_size; ///< Font size in points + Keyframe left; ///< Size of left bar + Keyframe top; ///< Size of top bar + Keyframe right; ///< Size of right bar + Keyframe bottom; ///< Size of bottom bar + + /// Blank constructor, useful when using Json to load the effect properties + Caption(); + + /// Default constructor, which takes 4 curves and a color. These curves animated the bars over time. + /// + /// @param color The curve to adjust the color of bars + /// @param left The curve to adjust the left bar size (between 0 and 1) + /// @param top The curve to adjust the top bar size (between 0 and 1) + /// @param right The curve to adjust the right bar size (between 0 and 1) + /// @param bottom The curve to adjust the bottom bar size (between 0 and 1) + Caption(Color color, std::string captions, std::string format); + + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// new openshot::Frame object. All Clip keyframes and effects are resolved into + /// pixels. + /// + /// @returns A new openshot::Frame object + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(int64_t frame_number) override { return GetFrame(std::shared_ptr (new Frame()), frame_number); } + + /// @brief This method is required for all derived classes of ClipBase, and returns a + /// modified openshot::Frame object + /// + /// The frame object is passed into this method and used as a starting point (pixels and audio). + /// All Clip keyframes and effects are resolved into pixels. + /// + /// @returns The modified openshot::Frame object + /// @param frame The frame object that needs the clip or effect applied to it + /// @param frame_number The frame number (starting at 1) of the clip or effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number) override; + + // Get and Set caption data + std::string CaptionText(); ///< Set the caption string to use (see VTT format) + void CaptionText(std::string new_caption_text); ///< Get the caption string + std::string CaptionFormat(); ///< Set the caption format to use (only VTT format is currently supported) + void CaptionFormat(std::string new_caption_format); ///< Get the caption format + + /// Get and Set JSON methods + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value) override; ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root) override; ///< Load Json::Value into this object + + /// Get all properties for a specific frame (perfect for a UI to display the current state + /// of all properties at any time) + std::string PropertiesJSON(int64_t requested_frame) const override; + }; + +} + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8cc2b6f2..4f44e347 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -117,7 +117,7 @@ if (ENABLE_BLACKMAGIC) endif() ############### PROFILING ################# -#set(PROFILER "/usr/lib/libprofiler.so.0.3.2") +#set(PROFILER "/usr/lib//usr/lib/libprofiler.so.0.4.5") #set(PROFILER "/usr/lib/libtcmalloc.so.4") if(CMAKE_VERSION VERSION_LESS 3.3) @@ -186,6 +186,7 @@ set(EFFECTS_SOURCES effects/Bars.cpp effects/Blur.cpp effects/Brightness.cpp + effects/Caption.cpp effects/ChromaKey.cpp effects/ColorShift.cpp effects/Crop.cpp diff --git a/src/EffectInfo.cpp b/src/EffectInfo.cpp index 6829f4eb..9593d2f7 100644 --- a/src/EffectInfo.cpp +++ b/src/EffectInfo.cpp @@ -53,6 +53,9 @@ EffectBase* EffectInfo::CreateEffect(std::string effect_type) { else if (effect_type == "Brightness") return new Brightness(); + else if (effect_type == "Caption") + return new Caption(); + else if (effect_type == "ChromaKey") return new ChromaKey(); @@ -98,6 +101,7 @@ Json::Value EffectInfo::JsonValue() { root.append(Bars().JsonInfo()); root.append(Blur().JsonInfo()); root.append(Brightness().JsonInfo()); + root.append(Caption().JsonInfo()); root.append(ChromaKey().JsonInfo()); root.append(ColorShift().JsonInfo()); root.append(Crop().JsonInfo()); diff --git a/src/effects/Caption.cpp b/src/effects/Caption.cpp new file mode 100644 index 00000000..dfbebb53 --- /dev/null +++ b/src/effects/Caption.cpp @@ -0,0 +1,332 @@ +/** + * @file + * @brief Source file for Caption effect class + * @author Jonathan Thomas + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#include "../../include/effects/Caption.h" +#include "../../include/Clip.h" +#include "../../include/Timeline.h" + +using namespace openshot; + +/// Blank constructor, useful when using Json to load the effect properties +Caption::Caption() : color("#ffffff"), stroke("#a9a9a9"), left(0.25), top(0.8), right(0.1), bottom(0.1), stroke_width(0.001), font_size(30.0), is_dirty(true) { + // Init effect properties + init_effect_details(); +} + +// Default constructor +Caption::Caption(Color color, std::string captions, std::string format) : + color(color), caption_text(captions), caption_format(format), stroke("#a9a9a9"), left(0.25), top(0.8), right(0.1), bottom(0.1), stroke_width(0.001), font_size(30.0), is_dirty(true) +{ + // Init effect properties + init_effect_details(); +} + +// Init effect settings +void Caption::init_effect_details() +{ + /// Initialize the values of the EffectInfo struct. + InitEffectInfo(); + + /// Set the effect info + info.class_name = "Caption"; + info.name = "Caption"; + info.description = "Add text captions on top of your video."; + info.has_audio = false; + info.has_video = true; +} + +// Set the caption string to use (see VTT format) +std::string Caption::CaptionText() { + return caption_text; +} + +// Get the caption string +void Caption::CaptionText(std::string new_caption_text) { + caption_text = new_caption_text; + is_dirty = true; +} + +// Set the caption format to use (only VTT format is currently supported) +std::string Caption::CaptionFormat() { + return caption_format; +} + +// Get the caption format +void Caption::CaptionFormat(std::string new_caption_format) { + caption_format = new_caption_format; + is_dirty = true; +} + +// Process regex string only when dirty +void Caption::process_regex() { + if (is_dirty) { + is_dirty = false; + + // Clear existing matches + matchedCaptions.clear(); + + // Parse regex and find all matches + QRegularExpression allPathsRegex(QStringLiteral("(\\d{2})?:*(\\d{2}):(\\d{2}).(\\d{2,3})\\s*-->\\s*(\\d{2})?:*(\\d{2}):(\\d{2}).(\\d{2,3})([\\s\\S]*?)\\n(.*?)(?=\\n\\d{2,3}|\\Z)"), QRegularExpression::MultilineOption); + QRegularExpressionMatchIterator i = allPathsRegex.globalMatch(QString(caption_text.c_str())); + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + if (match.hasMatch()) { + // Push all match objects into a vector (so we can reverse them later) + matchedCaptions.push_back(match); + } + } + } +} + +// This method is required for all derived classes of EffectBase, and returns a +// modified openshot::Frame object +std::shared_ptr Caption::GetFrame(std::shared_ptr frame, int64_t frame_number) +{ + // Process regex (if needed) + process_regex(); + + // Get the Clip and Timeline pointers (if available) + Clip* clip = (Clip*) ParentClip(); + Timeline* timeline = NULL; + Fraction fps; + double scale_factor = 1.0; // amount of scaling needed for text (based on preview window size) + if (clip->ParentTimeline() != NULL) { + timeline = (Timeline*) clip->ParentTimeline(); + } else if (this->ParentTimeline() != NULL) { + timeline = (Timeline*) this->ParentTimeline(); + } + + // Get the FPS from the parent object (Timeline or Clip's Reader) + if (timeline != NULL) { + fps.num = timeline->info.fps.num; + fps.den = timeline->info.fps.den; + scale_factor = (double) timeline->preview_width / (double) timeline->info.width; + } else if (clip != NULL && clip->Reader() != NULL) { + fps.num = clip->Reader()->info.fps.num; + fps.den = clip->Reader()->info.fps.den; + scale_factor = 1.0; + } + + // Get the frame's image + std::shared_ptr frame_image = frame->GetImage(); + + // Load timeline's new frame image into a QPainter + QPainter painter(frame_image.get()); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing, true); + + // Composite a new layer onto the image + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + + // Stroke / border pen + if (stroke_width.GetValue(frame_number) > 0.0) { + QPen pen; + pen.setColor(QColor(QString(stroke.GetColorHex(frame_number).c_str()))); + pen.setWidth(stroke_width.GetValue(frame_number) * scale_factor); + painter.setPen(pen); + } + + // Fill color brush + QBrush brush; + brush.setColor(QColor(QString(color.GetColorHex(frame_number).c_str()))); + brush.setStyle(Qt::SolidPattern); + painter.setBrush(brush); + + // Font options for caption + // TODO: Allow more font options (family, bold, style) + QFont font; + if (font_size.GetValue(frame_number) > 0.0) { + font.setPointSizeF(font_size.GetValue(frame_number) * scale_factor); + } else { + // Font can't be 0 sized + font.setPointSizeF(1.0); + } + + // Loop through matches and find text to display (if any) + for (auto match = matchedCaptions.begin(); match != matchedCaptions.end(); match++) { + + // Build timestamp (00:00:04.000 --> 00:00:06.500) + int64_t start_frame = ((match->captured(1).toFloat() * 60.0 * 60.0 ) + (match->captured(2).toFloat() * 60.0 ) + + match->captured(3).toFloat() + (match->captured(4).toFloat() / 1000.0)) * fps.ToFloat(); + int64_t end_frame = ((match->captured(5).toFloat() * 60.0 * 60.0 ) + (match->captured(6).toFloat() * 60.0 ) + + match->captured(7).toFloat() + (match->captured(8).toFloat() / 1000.0)) * fps.ToFloat(); + + // Get current keyframe values + double left_value = left.GetValue(frame_number); + double top_value = top.GetValue(frame_number); + + // TODO: Use all 4 margins and wrap text + double right_value = right.GetValue(frame_number); + double bottom_value = bottom.GetValue(frame_number); + + // Parse WEBVTT caption format + double starting_x = frame_image->width() * left_value; + double starting_y = frame_image->height() * top_value;; + + // Split multiple lines into separate paths + QStringList lines = match->captured(9).split("\n"); + for(int index = 0; index < lines.length(); index++) { + // Multi-line + QString line = lines[index]; + // Ignore lines that start with NOTE, or are <= 1 char long + if (!line.startsWith(QStringLiteral("NOTE")) && + !line.isEmpty() && frame_number >= start_frame && frame_number <= end_frame && + !line.length() <= 1 ) { + + // Location for text + QPoint p(starting_x, starting_y); + + // Draw text onto path (for correct border and fill) + QPainterPath path1; + path1.addText(p, font, line); + painter.drawPath(path1); + + // Increment QPoint to height of text (for next line) + padding + starting_y += path1.boundingRect().height() + (10.0 * scale_factor); + } + } + } + + // End painter + painter.end(); + + // return the modified frame + return frame; +} + +// Generate JSON string of this object +std::string Caption::Json() const { + + // Return formatted string + return JsonValue().toStyledString(); +} + +// Generate Json::Value for this object +Json::Value Caption::JsonValue() const { + + // Create root json object + Json::Value root = EffectBase::JsonValue(); // get parent properties + root["type"] = info.class_name; + root["color"] = color.JsonValue(); + root["stroke"] = stroke.JsonValue(); + root["stroke_width"] = stroke_width.JsonValue(); + root["font_size"] = font_size.JsonValue(); + root["left"] = left.JsonValue(); + root["top"] = top.JsonValue(); + root["right"] = right.JsonValue(); + root["bottom"] = bottom.JsonValue(); + root["caption_text"] = caption_text; + root["caption_format"] = caption_format; + + // return JsonValue + return root; +} + +// Load JSON string into this object +void Caption::SetJson(const std::string value) { + + // Parse JSON string into JSON objects + try + { + const Json::Value root = openshot::stringToJson(value); + // Set all values that match + SetJsonValue(root); + } + catch (const std::exception& e) + { + // Error parsing JSON (or missing keys) + throw InvalidJSON("JSON is invalid (missing keys or invalid data types)"); + } +} + +// Load Json::Value into this object +void Caption::SetJsonValue(const Json::Value root) { + + // Set parent data + EffectBase::SetJsonValue(root); + + // Set data from Json (if key is found) + if (!root["color"].isNull()) + color.SetJsonValue(root["color"]); + if (!root["stroke"].isNull()) + stroke.SetJsonValue(root["stroke"]); + if (!root["stroke_width"].isNull()) + stroke_width.SetJsonValue(root["stroke_width"]); + if (!root["font_size"].isNull()) + font_size.SetJsonValue(root["font_size"]); + if (!root["left"].isNull()) + left.SetJsonValue(root["left"]); + if (!root["top"].isNull()) + top.SetJsonValue(root["top"]); + if (!root["right"].isNull()) + right.SetJsonValue(root["right"]); + if (!root["bottom"].isNull()) + bottom.SetJsonValue(root["bottom"]); + if (!root["caption_text"].isNull()) + caption_text = root["caption_text"].asString(); + if (!root["caption_format"].isNull()) + caption_format = root["caption_format"].asString(); + + // Mark effect as dirty to reparse Regex + is_dirty = true; +} + +// Get all properties for a specific frame +std::string Caption::PropertiesJSON(int64_t requested_frame) const { + + // Generate JSON properties list + Json::Value root; + root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame); + root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame); + root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame); + root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame); + root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame); + root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 1000 * 60 * 30, true, requested_frame); + + // Keyframes + root["color"] = add_property_json("Color", 0.0, "color", "", NULL, 0, 255, false, requested_frame); + root["color"]["red"] = add_property_json("Red", color.red.GetValue(requested_frame), "float", "", &color.red, 0, 255, false, requested_frame); + root["color"]["blue"] = add_property_json("Blue", color.blue.GetValue(requested_frame), "float", "", &color.blue, 0, 255, false, requested_frame); + root["color"]["green"] = add_property_json("Green", color.green.GetValue(requested_frame), "float", "", &color.green, 0, 255, false, requested_frame); + root["stroke"] = add_property_json("Border", 0.0, "color", "", NULL, 0, 255, false, requested_frame); + root["stroke"]["red"] = add_property_json("Red", stroke.red.GetValue(requested_frame), "float", "", &stroke.red, 0, 255, false, requested_frame); + root["stroke"]["blue"] = add_property_json("Blue", stroke.blue.GetValue(requested_frame), "float", "", &stroke.blue, 0, 255, false, requested_frame); + root["stroke"]["green"] = add_property_json("Green", stroke.green.GetValue(requested_frame), "float", "", &stroke.green, 0, 255, false, requested_frame); + root["stroke_width"] = add_property_json("Stroke Width", stroke_width.GetValue(requested_frame), "float", "", &stroke_width, 0, 10.0, false, requested_frame); + root["font_size"] = add_property_json("Font Size", font_size.GetValue(requested_frame), "float", "", &font_size, 0, 200.0, false, requested_frame); + root["left"] = add_property_json("Left Size", left.GetValue(requested_frame), "float", "", &left, 0.0, 0.5, false, requested_frame); + root["top"] = add_property_json("Top Size", top.GetValue(requested_frame), "float", "", &top, 0.0, 0.5, false, requested_frame); + root["right"] = add_property_json("Right Size", right.GetValue(requested_frame), "float", "", &right, 0.0, 0.5, false, requested_frame); + root["bottom"] = add_property_json("Bottom Size", bottom.GetValue(requested_frame), "float", "", &bottom, 0.0, 0.5, false, requested_frame); + root["caption_text"] = add_property_json("Captions", 0.0, "string", caption_text, NULL, -1, -1, false, requested_frame); + root["caption_format"] = add_property_json("Format", 0.0, "string", caption_format, NULL, -1, -1, false, requested_frame); + + // Return formatted string + return root.toStyledString(); +} From 9b2ca50f6d7708ac622a38c3a30fe23623285c9d Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 6 Oct 2020 03:11:59 -0500 Subject: [PATCH 2/7] Allow sub-pixel sized pen stroke --- src/effects/Caption.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/effects/Caption.cpp b/src/effects/Caption.cpp index dfbebb53..212c9e72 100644 --- a/src/effects/Caption.cpp +++ b/src/effects/Caption.cpp @@ -35,14 +35,14 @@ using namespace openshot; /// Blank constructor, useful when using Json to load the effect properties -Caption::Caption() : color("#ffffff"), stroke("#a9a9a9"), left(0.25), top(0.8), right(0.1), bottom(0.1), stroke_width(0.001), font_size(30.0), is_dirty(true) { +Caption::Caption() : color("#ffffff"), stroke("#a9a9a9"), left(0.25), top(0.8), right(0.1), bottom(0.1), stroke_width(0.5), font_size(30.0), is_dirty(true) { // Init effect properties init_effect_details(); } // Default constructor Caption::Caption(Color color, std::string captions, std::string format) : - color(color), caption_text(captions), caption_format(format), stroke("#a9a9a9"), left(0.25), top(0.8), right(0.1), bottom(0.1), stroke_width(0.001), font_size(30.0), is_dirty(true) + color(color), caption_text(captions), caption_format(format), stroke("#a9a9a9"), left(0.25), top(0.8), right(0.1), bottom(0.1), stroke_width(0.5), font_size(30.0), is_dirty(true) { // Init effect properties init_effect_details(); @@ -148,7 +148,7 @@ std::shared_ptr Caption::GetFrame(std::shared_ptr 0.0) { QPen pen; pen.setColor(QColor(QString(stroke.GetColorHex(frame_number).c_str()))); - pen.setWidth(stroke_width.GetValue(frame_number) * scale_factor); + pen.setWidthF(stroke_width.GetValue(frame_number) * scale_factor); painter.setPen(pen); } From 80a1fe8ca4731a30c681ecdf05879f34550dc759 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Mon, 12 Oct 2020 23:28:03 -0500 Subject: [PATCH 3/7] Initializing Clip info struct, and fixing clip cache settings --- include/Clip.h | 15 +++++++++------ src/Clip.cpp | 29 ++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/include/Clip.h b/include/Clip.h index fd28c162..0bc99e84 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -98,6 +98,15 @@ namespace openshot { /// Section lock for multiple threads juce::CriticalSection getFrameCriticalSection; + /// Init default settings for a clip + void init_settings(); + + /// Init reader info details + void init_reader_settings(); + + /// Update default rotation from reader + void init_reader_rotation(); + private: bool waveform; ///< Should a waveform be used instead of the clip's image std::list effects; /// frame, int64_t frame_number); - /// Init default settings for a clip - void init_settings(); - - /// Update default rotation from reader - void init_reader_rotation(); - /// Compare 2 floating point numbers bool isEqual(double a, double b); diff --git a/src/Clip.cpp b/src/Clip.cpp index 1d2b392d..d81b5c61 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -69,9 +69,6 @@ void Clip::init_settings() // Init alpha alpha = Keyframe(1.0); - // Init rotation - init_reader_rotation(); - // Init time & volume time = Keyframe(1.0); volume = Keyframe(1.0); @@ -101,8 +98,22 @@ void Clip::init_settings() has_audio = Keyframe(-1.0); has_video = Keyframe(-1.0); - // Initialize Clip cache - cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); + // Init reader info struct and cache size + init_reader_settings(); +} + +// Init reader info details +void Clip::init_reader_settings() { + if (reader) { + // Init rotation (if any) + init_reader_rotation(); + + // Initialize info struct + info = reader->info; + + // Initialize Clip cache + cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); + } } // Init reader's rotation (if any) @@ -208,8 +219,8 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N End(reader->info.duration); reader->ParentClip(this); allocated_reader = reader; - init_reader_rotation(); - } + // Init reader info struct and cache size + init_reader_settings(); } } // Destructor @@ -237,8 +248,8 @@ void Clip::Reader(ReaderBase* new_reader) // set parent reader->ParentClip(this); - // Init rotation (if any) - init_reader_rotation(); + // Init reader info struct and cache size + init_reader_settings(); } /// Get the current reader From 07a10e3bf6ae7abed5b1e42458db6dbd07b7c9ab Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 21 Oct 2020 01:27:49 -0500 Subject: [PATCH 4/7] - Added word-wrap (using adjustable left, top, and right side keyframes) - Added background color and alpha - Added font color alpha - Added fade in / out logic - Added background padding & rounded corners - Added adjustable font name --- src/effects/Caption.cpp | 214 ++++++++++++++++++++++++++++------------ src/effects/Caption.h | 26 ++--- 2 files changed, 163 insertions(+), 77 deletions(-) diff --git a/src/effects/Caption.cpp b/src/effects/Caption.cpp index 212c9e72..e48d0d7b 100644 --- a/src/effects/Caption.cpp +++ b/src/effects/Caption.cpp @@ -28,21 +28,26 @@ * along with OpenShot Library. If not, see . */ -#include "../../include/effects/Caption.h" -#include "../../include/Clip.h" -#include "../../include/Timeline.h" +#include "Caption.h" +#include "../Clip.h" +#include "../Timeline.h" using namespace openshot; /// Blank constructor, useful when using Json to load the effect properties -Caption::Caption() : color("#ffffff"), stroke("#a9a9a9"), left(0.25), top(0.8), right(0.1), bottom(0.1), stroke_width(0.5), font_size(30.0), is_dirty(true) { +Caption::Caption() : color("#ffffff"), stroke("#a9a9a9"), background("#ff000000"), background_alpha(0.0), left(0.25), top(0.7), right(0.1), + stroke_width(0.5), font_size(30.0), font_alpha(1.0), is_dirty(true), font_name("sans"), font(NULL), metrics(NULL), + fade_in(0.35), fade_out(0.35), background_corner(10.0), background_padding(20.0) +{ // Init effect properties init_effect_details(); } // Default constructor -Caption::Caption(Color color, std::string captions, std::string format) : - color(color), caption_text(captions), caption_format(format), stroke("#a9a9a9"), left(0.25), top(0.8), right(0.1), bottom(0.1), stroke_width(0.5), font_size(30.0), is_dirty(true) +Caption::Caption(Color color, std::string captions) : + color(color), caption_text(captions), stroke("#a9a9a9"), background("#ff000000"), background_alpha(0.0), + left(0.25), top(0.7), right(0.1), stroke_width(0.5), font_size(30.0), font_alpha(1.0), is_dirty(true), font_name("sans"), + font(NULL), metrics(NULL), fade_in(0.35), fade_out(0.35), background_corner(10.0), background_padding(20.0) { // Init effect properties init_effect_details(); @@ -73,17 +78,6 @@ void Caption::CaptionText(std::string new_caption_text) { is_dirty = true; } -// Set the caption format to use (only VTT format is currently supported) -std::string Caption::CaptionFormat() { - return caption_format; -} - -// Get the caption format -void Caption::CaptionFormat(std::string new_caption_format) { - caption_format = new_caption_format; - is_dirty = true; -} - // Process regex string only when dirty void Caption::process_regex() { if (is_dirty) { @@ -127,6 +121,7 @@ std::shared_ptr Caption::GetFrame(std::shared_ptrinfo.fps.num; fps.den = timeline->info.fps.den; + // preview window is sometimes smaller/larger than the timeline size scale_factor = (double) timeline->preview_width / (double) timeline->info.width; } else if (clip != NULL && clip->Reader() != NULL) { fps.num = clip->Reader()->info.fps.num; @@ -144,30 +139,59 @@ std::shared_ptr Caption::GetFrame(std::shared_ptr 0.0) { - QPen pen; - pen.setColor(QColor(QString(stroke.GetColorHex(frame_number).c_str()))); + // Font options and metrics for caption text + double font_size_value = font_size.GetValue(frame_number) * scale_factor; + QFont font(QString(font_name.c_str()), int(font_size_value)); + font.setPointSizeF(std::max(font_size_value, 1.0)); + QFontMetricsF metrics = QFontMetricsF(font); + + // Get current keyframe values + double left_value = left.GetValue(frame_number); + double top_value = top.GetValue(frame_number); + double fade_in_value = fade_in.GetValue(frame_number) * fps.ToDouble(); + double fade_out_value = fade_out.GetValue(frame_number) * fps.ToDouble(); + double right_value = right.GetValue(frame_number); + double background_corner_value = background_corner.GetValue(frame_number); + double padding_value = background_padding.GetValue(frame_number); + + // Calculate caption area (based on left, top, and right margin) + double left_margin_x = frame_image->width() * left_value; + double starting_y = (frame_image->height() * top_value) + (metrics.lineSpacing() * scale_factor); + double right_margin_x = frame_image->width() - (frame_image->width() * right_value); + double caption_area_width = right_margin_x - left_margin_x; + QRectF caption_area = QRectF(left_margin_x, starting_y, caption_area_width, frame_image->height()); + QRectF caption_area_with_padding = QRectF(left_margin_x - (padding_value / 2.0), starting_y - (padding_value / 2.0), caption_area_width + padding_value, frame_image->height() + padding_value); + + // Set background color of caption + QBrush brush; + QColor background_qcolor = QColor(QString(background.GetColorHex(frame_number).c_str())); + background_qcolor.setAlphaF(background_alpha.GetValue(frame_number)); + brush.setColor(background_qcolor); + brush.setStyle(Qt::SolidPattern); + painter.setBrush(brush); + painter.setPen(Qt::NoPen); + painter.drawRoundedRect(caption_area_with_padding, background_corner_value, background_corner_value); + + // Set text color of caption + QPen pen; + QColor stroke_qcolor; + if (stroke_width.GetValue(frame_number) <= 0.0) { + // No stroke + painter.setPen(Qt::NoPen); + } else { + // Stroke color + stroke_qcolor = QColor(QString(stroke.GetColorHex(frame_number).c_str())); + stroke_qcolor.setAlphaF(font_alpha.GetValue(frame_number)); + pen.setColor(stroke_qcolor); pen.setWidthF(stroke_width.GetValue(frame_number) * scale_factor); painter.setPen(pen); } - - // Fill color brush - QBrush brush; - brush.setColor(QColor(QString(color.GetColorHex(frame_number).c_str()))); - brush.setStyle(Qt::SolidPattern); + // Fill color of text + QColor font_qcolor = QColor(QString(color.GetColorHex(frame_number).c_str())); + font_qcolor.setAlphaF(font_alpha.GetValue(frame_number)); + brush.setColor(font_qcolor); painter.setBrush(brush); - // Font options for caption - // TODO: Allow more font options (family, bold, style) - QFont font; - if (font_size.GetValue(frame_number) > 0.0) { - font.setPointSizeF(font_size.GetValue(frame_number) * scale_factor); - } else { - // Font can't be 0 sized - font.setPointSizeF(1.0); - } - // Loop through matches and find text to display (if any) for (auto match = matchedCaptions.begin(); match != matchedCaptions.end(); match++) { @@ -177,18 +201,6 @@ std::shared_ptr Caption::GetFrame(std::shared_ptrcaptured(5).toFloat() * 60.0 * 60.0 ) + (match->captured(6).toFloat() * 60.0 ) + match->captured(7).toFloat() + (match->captured(8).toFloat() / 1000.0)) * fps.ToFloat(); - // Get current keyframe values - double left_value = left.GetValue(frame_number); - double top_value = top.GetValue(frame_number); - - // TODO: Use all 4 margins and wrap text - double right_value = right.GetValue(frame_number); - double bottom_value = bottom.GetValue(frame_number); - - // Parse WEBVTT caption format - double starting_x = frame_image->width() * left_value; - double starting_y = frame_image->height() * top_value;; - // Split multiple lines into separate paths QStringList lines = match->captured(9).split("\n"); for(int index = 0; index < lines.length(); index++) { @@ -199,16 +211,61 @@ std::shared_ptr Caption::GetFrame(std::shared_ptr= start_frame && frame_number <= end_frame && !line.length() <= 1 ) { - // Location for text - QPoint p(starting_x, starting_y); + // Calculate fade in/out ranges + double fade_in_percentage = ((float) frame_number - (float) start_frame) / fade_in_value; + double fade_out_percentage = 1.0 - (((float) frame_number - ((float) end_frame - fade_out_value)) / fade_out_value); + if (fade_in_percentage < 1.0) { + // Fade in + font_qcolor.setAlphaF(fade_in_percentage * font_alpha.GetValue(frame_number)); + stroke_qcolor.setAlphaF(fade_in_percentage * font_alpha.GetValue(frame_number)); + } else if (fade_out_percentage >= 0.0 && fade_out_percentage <= 1.0) { + // Fade out + font_qcolor.setAlphaF(fade_out_percentage * font_alpha.GetValue(frame_number)); + stroke_qcolor.setAlphaF(fade_out_percentage * font_alpha.GetValue(frame_number)); + } + pen.setColor(stroke_qcolor); + brush.setColor(font_qcolor); + painter.setPen(pen); + painter.setBrush(brush); - // Draw text onto path (for correct border and fill) - QPainterPath path1; - path1.addText(p, font, line); - painter.drawPath(path1); + // Loop through words, and find word-wrap boundaries + QStringList words = line.split(" "); + int words_remaining = words.length(); + while (words_remaining > 0) { + bool words_displayed = false; + for(int word_index = words.length(); word_index > 0; word_index--) { + // Current matched caption string (from the beginning to the current word index) + QString fitting_line = words.mid(0, word_index).join(" "); + + // Calculate size of text + QRectF textRect = metrics.boundingRect(caption_area, Qt::TextSingleLine, fitting_line); + if (textRect.width() <= caption_area.width()) { + // Location for text + QPoint p(left_margin_x, starting_y); + + // Draw text onto path (for correct border and fill) + QPainterPath path1; + QString fitting_line = words.mid(0, word_index).join(" "); + path1.addText(p, font, fitting_line); + painter.drawPath(path1); + + // Increment QPoint to height of text (for next line) + padding + starting_y += path1.boundingRect().height() + (metrics.lineSpacing() * scale_factor); + + // Update line (to remove words already drawn + words = words.mid(word_index, words.length()); + words_remaining = words.length(); + words_displayed = true; + break; + } + } + + if (words_displayed == false) { + // Exit loop if no words displayed + words_remaining = 0; + } + } - // Increment QPoint to height of text (for next line) + padding - starting_y += path1.boundingRect().height() + (10.0 * scale_factor); } } } @@ -235,14 +292,20 @@ Json::Value Caption::JsonValue() const { root["type"] = info.class_name; root["color"] = color.JsonValue(); root["stroke"] = stroke.JsonValue(); + root["background"] = background.JsonValue(); + root["background_alpha"] = background_alpha.JsonValue(); + root["background_corner"] = background_corner.JsonValue(); + root["background_padding"] = background_padding.JsonValue(); root["stroke_width"] = stroke_width.JsonValue(); root["font_size"] = font_size.JsonValue(); + root["font_alpha"] = font_alpha.JsonValue(); + root["fade_in"] = fade_in.JsonValue(); + root["fade_out"] = fade_out.JsonValue(); root["left"] = left.JsonValue(); root["top"] = top.JsonValue(); root["right"] = right.JsonValue(); - root["bottom"] = bottom.JsonValue(); root["caption_text"] = caption_text; - root["caption_format"] = caption_format; + root["caption_font"] = font_name; // return JsonValue return root; @@ -276,22 +339,34 @@ void Caption::SetJsonValue(const Json::Value root) { color.SetJsonValue(root["color"]); if (!root["stroke"].isNull()) stroke.SetJsonValue(root["stroke"]); + if (!root["background"].isNull()) + background.SetJsonValue(root["background"]); + if (!root["background_alpha"].isNull()) + background_alpha.SetJsonValue(root["background_alpha"]); + if (!root["background_corner"].isNull()) + background_corner.SetJsonValue(root["background_corner"]); + if (!root["background_padding"].isNull()) + background_padding.SetJsonValue(root["background_padding"]); if (!root["stroke_width"].isNull()) stroke_width.SetJsonValue(root["stroke_width"]); if (!root["font_size"].isNull()) font_size.SetJsonValue(root["font_size"]); + if (!root["font_alpha"].isNull()) + font_alpha.SetJsonValue(root["font_alpha"]); + if (!root["fade_in"].isNull()) + fade_in.SetJsonValue(root["fade_in"]); + if (!root["fade_out"].isNull()) + fade_out.SetJsonValue(root["fade_out"]); if (!root["left"].isNull()) left.SetJsonValue(root["left"]); if (!root["top"].isNull()) top.SetJsonValue(root["top"]); if (!root["right"].isNull()) right.SetJsonValue(root["right"]); - if (!root["bottom"].isNull()) - bottom.SetJsonValue(root["bottom"]); if (!root["caption_text"].isNull()) caption_text = root["caption_text"].asString(); - if (!root["caption_format"].isNull()) - caption_format = root["caption_format"].asString(); + if (!root["caption_font"].isNull()) + font_name = root["caption_font"].asString(); // Mark effect as dirty to reparse Regex is_dirty = true; @@ -318,14 +393,23 @@ std::string Caption::PropertiesJSON(int64_t requested_frame) const { root["stroke"]["red"] = add_property_json("Red", stroke.red.GetValue(requested_frame), "float", "", &stroke.red, 0, 255, false, requested_frame); root["stroke"]["blue"] = add_property_json("Blue", stroke.blue.GetValue(requested_frame), "float", "", &stroke.blue, 0, 255, false, requested_frame); root["stroke"]["green"] = add_property_json("Green", stroke.green.GetValue(requested_frame), "float", "", &stroke.green, 0, 255, false, requested_frame); + root["background_alpha"] = add_property_json("Background Alpha", background_alpha.GetValue(requested_frame), "float", "", &background_alpha, 0.0, 1.0, false, requested_frame); + root["background_corner"] = add_property_json("Background Corner Radius", background_corner.GetValue(requested_frame), "float", "", &background_corner, 0.0, 60.0, false, requested_frame); + root["background_padding"] = add_property_json("Background Padding", background_padding.GetValue(requested_frame), "float", "", &background_padding, 0.0, 60.0, false, requested_frame); + root["background"] = add_property_json("Background", 0.0, "color", "", NULL, 0, 255, false, requested_frame); + root["background"]["red"] = add_property_json("Red", background.red.GetValue(requested_frame), "float", "", &background.red, 0, 255, false, requested_frame); + root["background"]["blue"] = add_property_json("Blue", background.blue.GetValue(requested_frame), "float", "", &background.blue, 0, 255, false, requested_frame); + root["background"]["green"] = add_property_json("Green", background.green.GetValue(requested_frame), "float", "", &background.green, 0, 255, false, requested_frame); root["stroke_width"] = add_property_json("Stroke Width", stroke_width.GetValue(requested_frame), "float", "", &stroke_width, 0, 10.0, false, requested_frame); root["font_size"] = add_property_json("Font Size", font_size.GetValue(requested_frame), "float", "", &font_size, 0, 200.0, false, requested_frame); + root["font_alpha"] = add_property_json("Font Alpha", font_alpha.GetValue(requested_frame), "float", "", &font_alpha, 0.0, 1.0, false, requested_frame); + root["fade_in"] = add_property_json("Fade In (Seconds)", fade_in.GetValue(requested_frame), "float", "", &fade_in, 0.0, 3.0, false, requested_frame); + root["fade_out"] = add_property_json("Fade Out (Seconds)", fade_out.GetValue(requested_frame), "float", "", &fade_out, 0.0, 3.0, false, requested_frame); root["left"] = add_property_json("Left Size", left.GetValue(requested_frame), "float", "", &left, 0.0, 0.5, false, requested_frame); - root["top"] = add_property_json("Top Size", top.GetValue(requested_frame), "float", "", &top, 0.0, 0.5, false, requested_frame); + root["top"] = add_property_json("Top Size", top.GetValue(requested_frame), "float", "", &top, 0.0, 1.0, false, requested_frame); root["right"] = add_property_json("Right Size", right.GetValue(requested_frame), "float", "", &right, 0.0, 0.5, false, requested_frame); - root["bottom"] = add_property_json("Bottom Size", bottom.GetValue(requested_frame), "float", "", &bottom, 0.0, 0.5, false, requested_frame); root["caption_text"] = add_property_json("Captions", 0.0, "string", caption_text, NULL, -1, -1, false, requested_frame); - root["caption_format"] = add_property_json("Format", 0.0, "string", caption_format, NULL, -1, -1, false, requested_frame); + root["caption_font"] = add_property_json("Font", 0.0, "string", font_name, NULL, -1, -1, false, requested_frame); // Return formatted string return root.toStyledString(); diff --git a/src/effects/Caption.h b/src/effects/Caption.h index f2f55126..f52f9a99 100644 --- a/src/effects/Caption.h +++ b/src/effects/Caption.h @@ -31,13 +31,12 @@ #ifndef OPENSHOT_CAPTION_EFFECT_H #define OPENSHOT_CAPTION_EFFECT_H -#include "../EffectBase.h" - #include #include #include #include #include "../Color.h" +#include "../EffectBase.h" #include "../Fraction.h" #include "../Json.h" #include "../KeyFrame.h" @@ -57,8 +56,9 @@ namespace openshot { private: std::vector matchedCaptions; ///< RegEx to capture cues and text - std::string caption_text; ///< Text of caption - std::string caption_format; ///< Format of caption (application/x-subrip, text/vtt) + std::string caption_text; ///< Text of caption + QFontMetrics* metrics; ///< Font metrics object + QFont* font; ///< QFont object bool is_dirty; /// Init effect settings @@ -71,12 +71,19 @@ namespace openshot public: Color color; ///< Color of caption text Color stroke; ///< Color of text border / stroke + Color background; ///< Color of caption area background + Keyframe background_alpha; ///< Background color alpha + Keyframe background_corner; ///< Background cornder radius + Keyframe background_padding; ///< Background padding Keyframe stroke_width; ///< Width of text border / stroke Keyframe font_size; ///< Font size in points + Keyframe font_alpha; ///< Font color alpha Keyframe left; ///< Size of left bar Keyframe top; ///< Size of top bar Keyframe right; ///< Size of right bar - Keyframe bottom; ///< Size of bottom bar + Keyframe fade_in; ///< Fade in per caption (# of seconds) + Keyframe fade_out; ///< Fade in per caption (# of seconds) + std::string font_name; ///< Font string /// Blank constructor, useful when using Json to load the effect properties Caption(); @@ -84,11 +91,8 @@ namespace openshot /// Default constructor, which takes 4 curves and a color. These curves animated the bars over time. /// /// @param color The curve to adjust the color of bars - /// @param left The curve to adjust the left bar size (between 0 and 1) - /// @param top The curve to adjust the top bar size (between 0 and 1) - /// @param right The curve to adjust the right bar size (between 0 and 1) - /// @param bottom The curve to adjust the bottom bar size (between 0 and 1) - Caption(Color color, std::string captions, std::string format); + /// @param captions A string with VTT/Subrip format text captions + Caption(Color color, std::string captions); /// @brief This method is required for all derived classes of ClipBase, and returns a /// new openshot::Frame object. All Clip keyframes and effects are resolved into @@ -112,8 +116,6 @@ namespace openshot // Get and Set caption data std::string CaptionText(); ///< Set the caption string to use (see VTT format) void CaptionText(std::string new_caption_text); ///< Get the caption string - std::string CaptionFormat(); ///< Set the caption format to use (only VTT format is currently supported) - void CaptionFormat(std::string new_caption_format); ///< Get the caption format /// Get and Set JSON methods std::string Json() const override; ///< Generate JSON string of this object From 54a070438a4c8490cc34049ac2c23acf85fe1afe Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Thu, 22 Oct 2020 01:32:33 -0500 Subject: [PATCH 5/7] - Added default caption value, for demonstration purposes (to help users see a valid example) - Append some newlines onto the end of any caption text... needed by the regex for some reason - Updated font name and caption text to be a new type (font and caption), and we have corresponding UI changes for those on openshot-qt --- src/effects/Caption.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/effects/Caption.cpp b/src/effects/Caption.cpp index e48d0d7b..43b24dd4 100644 --- a/src/effects/Caption.cpp +++ b/src/effects/Caption.cpp @@ -65,6 +65,11 @@ void Caption::init_effect_details() info.description = "Add text captions on top of your video."; info.has_audio = false; info.has_video = true; + + // Init placeholder caption (for demo) + if (caption_text.length() == 0) { + caption_text = "00:00:00:000 --> 00:10:00:000\nEdit this caption with our caption editor"; + } } // Set the caption string to use (see VTT format) @@ -86,9 +91,15 @@ void Caption::process_regex() { // Clear existing matches matchedCaptions.clear(); + QString caption_prepared = QString(caption_text.c_str()); + if (caption_prepared.endsWith("\n\n") == false) { + // We need a couple line ends at the end of the caption string (for our regex to work correctly) + caption_prepared.append("\n\n"); + } + // Parse regex and find all matches QRegularExpression allPathsRegex(QStringLiteral("(\\d{2})?:*(\\d{2}):(\\d{2}).(\\d{2,3})\\s*-->\\s*(\\d{2})?:*(\\d{2}):(\\d{2}).(\\d{2,3})([\\s\\S]*?)\\n(.*?)(?=\\n\\d{2,3}|\\Z)"), QRegularExpression::MultilineOption); - QRegularExpressionMatchIterator i = allPathsRegex.globalMatch(QString(caption_text.c_str())); + QRegularExpressionMatchIterator i = allPathsRegex.globalMatch(caption_prepared); while (i.hasNext()) { QRegularExpressionMatch match = i.next(); if (match.hasMatch()) { @@ -408,8 +419,8 @@ std::string Caption::PropertiesJSON(int64_t requested_frame) const { root["left"] = add_property_json("Left Size", left.GetValue(requested_frame), "float", "", &left, 0.0, 0.5, false, requested_frame); root["top"] = add_property_json("Top Size", top.GetValue(requested_frame), "float", "", &top, 0.0, 1.0, false, requested_frame); root["right"] = add_property_json("Right Size", right.GetValue(requested_frame), "float", "", &right, 0.0, 0.5, false, requested_frame); - root["caption_text"] = add_property_json("Captions", 0.0, "string", caption_text, NULL, -1, -1, false, requested_frame); - root["caption_font"] = add_property_json("Font", 0.0, "string", font_name, NULL, -1, -1, false, requested_frame); + root["caption_text"] = add_property_json("Captions", 0.0, "caption", caption_text, NULL, -1, -1, false, requested_frame); + root["caption_font"] = add_property_json("Font", 0.0, "font", font_name, NULL, -1, -1, false, requested_frame); // Return formatted string return root.toStyledString(); From def8d9dd7bc2efb6671126c4521dd0f9c08b4139 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 27 Oct 2020 02:13:57 -0500 Subject: [PATCH 6/7] Updating method docs --- src/effects/Caption.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/effects/Caption.h b/src/effects/Caption.h index f52f9a99..89347415 100644 --- a/src/effects/Caption.h +++ b/src/effects/Caption.h @@ -88,9 +88,9 @@ namespace openshot /// Blank constructor, useful when using Json to load the effect properties Caption(); - /// Default constructor, which takes 4 curves and a color. These curves animated the bars over time. + /// Default constructor, which takes a string of VTT/Subrip formatted caption data, and displays them over time. /// - /// @param color The curve to adjust the color of bars + /// @param color The curve to adjust the color of caption text /// @param captions A string with VTT/Subrip format text captions Caption(Color color, std::string captions); From a3c20c8f447ab01619f8b452b36b5e88955da092 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 27 Oct 2020 02:50:02 -0500 Subject: [PATCH 7/7] Fixed a few codacy issues --- src/effects/Caption.cpp | 6 +++--- src/effects/Caption.h | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/effects/Caption.cpp b/src/effects/Caption.cpp index 43b24dd4..b9ce2249 100644 --- a/src/effects/Caption.cpp +++ b/src/effects/Caption.cpp @@ -44,8 +44,8 @@ Caption::Caption() : color("#ffffff"), stroke("#a9a9a9"), background("#ff000000" } // Default constructor -Caption::Caption(Color color, std::string captions) : - color(color), caption_text(captions), stroke("#a9a9a9"), background("#ff000000"), background_alpha(0.0), +Caption::Caption(std::string captions) : + color("#ffffff"), caption_text(captions), stroke("#a9a9a9"), background("#ff000000"), background_alpha(0.0), left(0.25), top(0.7), right(0.1), stroke_width(0.5), font_size(30.0), font_alpha(1.0), is_dirty(true), font_name("sans"), font(NULL), metrics(NULL), fade_in(0.35), fade_out(0.35), background_corner(10.0), background_padding(20.0) { @@ -220,7 +220,7 @@ std::shared_ptr Caption::GetFrame(std::shared_ptr= start_frame && frame_number <= end_frame && - !line.length() <= 1 ) { + line.length() > 1) { // Calculate fade in/out ranges double fade_in_percentage = ((float) frame_number - (float) start_frame) / fade_in_value; diff --git a/src/effects/Caption.h b/src/effects/Caption.h index 89347415..749b2c17 100644 --- a/src/effects/Caption.h +++ b/src/effects/Caption.h @@ -90,9 +90,8 @@ namespace openshot /// Default constructor, which takes a string of VTT/Subrip formatted caption data, and displays them over time. /// - /// @param color The curve to adjust the color of caption text /// @param captions A string with VTT/Subrip format text captions - Caption(Color color, std::string captions); + Caption(std::string captions); /// @brief This method is required for all derived classes of ClipBase, and returns a /// new openshot::Frame object. All Clip keyframes and effects are resolved into