diff --git a/include/Effects.h b/include/Effects.h index 1245d741..224c48be 100644 --- a/include/Effects.h +++ b/include/Effects.h @@ -34,6 +34,7 @@ #include "effects/Brightness.h" #include "effects/ChromaKey.h" #include "effects/ColorShift.h" +#include "effects/Crop.h" #include "effects/Deinterlace.h" #include "effects/Hue.h" #include "effects/Mask.h" diff --git a/include/effects/Crop.h b/include/effects/Crop.h new file mode 100644 index 00000000..7921a78d --- /dev/null +++ b/include/effects/Crop.h @@ -0,0 +1,103 @@ +/** + * @file + * @brief Header file for Crop effect class + * @author Jonathan Thomas + * + * @section LICENSE + * + * Copyright (c) 2008-2014 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_CROP_EFFECT_H +#define OPENSHOT_CROP_EFFECT_H + +#include "../EffectBase.h" + +#include +#include +#include +#include "../Color.h" +#include "../Json.h" +#include "../KeyFrame.h" + + +using namespace std; + +namespace openshot +{ + + /** + * @brief This class crops a frame image (from any side), and can be animated with + * openshot::Keyframe curves over time. + * + * Cropping images can be useful when wanting to remove a border around an image or video, and animating + * the crop can create some very interesting effects. + */ + class Crop : public EffectBase + { + private: + /// Init effect settings + void init_effect_details(); + + + public: + Color color; ///< Color of bars + 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 + Crop(); + + /// Default constructor, which takes 4 curves. These curves animate the crop 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) + Crop(Keyframe left, Keyframe top, Keyframe right, Keyframe bottom); + + /// @brief This method is required for all derived classes of EffectBase, and returns a + /// modified openshot::Frame object + /// + /// The frame object is passed into this method, and a frame_number is passed in which + /// tells the effect which settings to use from it's keyframes (starting at 1). + /// + /// @returns The modified openshot::Frame object + /// @param frame The frame object that needs the effect applied to it + /// @param frame_number The frame number (starting at 1) of the effect on the timeline. + std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); + + /// Get and Set JSON methods + string Json(); ///< Generate JSON string of this object + void SetJson(string value); ///< Load JSON string into this object + Json::Value JsonValue(); ///< Generate Json::JsonValue for this object + void SetJsonValue(Json::Value root); ///< Load Json::JsonValue 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) + string PropertiesJSON(int64_t requested_frame); + }; + +} + +#endif diff --git a/src/EffectInfo.cpp b/src/EffectInfo.cpp index 5c9df283..23bc9d02 100644 --- a/src/EffectInfo.cpp +++ b/src/EffectInfo.cpp @@ -56,6 +56,9 @@ EffectBase* EffectInfo::CreateEffect(string effect_type) { else if (effect_type == "Color Shift") return new ColorShift(); + else if (effect_type == "Crop") + return new Crop(); + else if (effect_type == "Deinterlace") return new Deinterlace(); @@ -93,6 +96,7 @@ Json::Value EffectInfo::JsonValue() { root.append(Brightness().JsonInfo()); root.append(ChromaKey().JsonInfo()); root.append(ColorShift().JsonInfo()); + root.append(Crop().JsonInfo()); root.append(Deinterlace().JsonInfo()); root.append(Hue().JsonInfo()); root.append(Mask().JsonInfo()); diff --git a/src/bindings/python/openshot.i b/src/bindings/python/openshot.i index bd00f8aa..f338f18a 100644 --- a/src/bindings/python/openshot.i +++ b/src/bindings/python/openshot.i @@ -165,6 +165,7 @@ %include "../../../include/effects/Brightness.h" %include "../../../include/effects/ChromaKey.h" %include "../../../include/effects/ColorShift.h" +%include "../../../include/effects/Crop.h" %include "../../../include/effects/Deinterlace.h" %include "../../../include/effects/Hue.h" %include "../../../include/effects/Mask.h" diff --git a/src/bindings/ruby/openshot.i b/src/bindings/ruby/openshot.i index 08c00673..c2f6fdf9 100644 --- a/src/bindings/ruby/openshot.i +++ b/src/bindings/ruby/openshot.i @@ -159,6 +159,7 @@ namespace std { %include "../../../include/effects/Brightness.h" %include "../../../include/effects/ChromaKey.h" %include "../../../include/effects/ColorShift.h" +%include "../../../include/effects/Crop.h" %include "../../../include/effects/Deinterlace.h" %include "../../../include/effects/Hue.h" %include "../../../include/effects/Mask.h" diff --git a/src/effects/Crop.cpp b/src/effects/Crop.cpp new file mode 100644 index 00000000..53953ec2 --- /dev/null +++ b/src/effects/Crop.cpp @@ -0,0 +1,196 @@ +/** + * @file + * @brief Source file for Crop effect class + * @author Jonathan Thomas + * + * @section LICENSE + * + * Copyright (c) 2008-2014 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/Crop.h" + +using namespace openshot; + +/// Blank constructor, useful when using Json to load the effect properties +Crop::Crop() : left(0.1), top(0.1), right(0.1), bottom(0.1) { + // Init effect properties + init_effect_details(); +} + +// Default constructor +Crop::Crop(Keyframe left, Keyframe top, Keyframe right, Keyframe bottom) : + left(left), top(top), right(right), bottom(bottom) +{ + // Init effect properties + init_effect_details(); +} + +// Init effect settings +void Crop::init_effect_details() +{ + /// Initialize the values of the EffectInfo struct. + InitEffectInfo(); + + /// Set the effect info + info.class_name = "Crop"; + info.name = "Crop"; + info.description = "Crop out any part of your video."; + info.has_audio = false; + info.has_video = true; +} + +// This method is required for all derived classes of EffectBase, and returns a +// modified openshot::Frame object +std::shared_ptr Crop::GetFrame(std::shared_ptr frame, int64_t frame_number) +{ + // Get the frame's image + std::shared_ptr frame_image = frame->GetImage(); + + // Get transparent color (and create small transparent image) + std::shared_ptr tempColor = std::shared_ptr(new QImage(frame_image->width(), 1, QImage::Format_RGBA8888)); + tempColor->fill(QColor(QString::fromStdString("transparent"))); + + // Get current keyframe values + double left_value = left.GetValue(frame_number); + double top_value = top.GetValue(frame_number); + double right_value = right.GetValue(frame_number); + double bottom_value = bottom.GetValue(frame_number); + + // Get pixel array pointers + unsigned char *pixels = (unsigned char *) frame_image->bits(); + unsigned char *color_pixels = (unsigned char *) tempColor->bits(); + + // Get pixels sizes of all crop sides + int top_bar_height = top_value * frame_image->height(); + int bottom_bar_height = bottom_value * frame_image->height(); + int left_bar_width = left_value * frame_image->width(); + int right_bar_width = right_value * frame_image->width(); + + // Loop through rows + for (int row = 0; row < frame_image->height(); row++) { + + // Top & Bottom Crop + if ((top_bar_height > 0.0 && row <= top_bar_height) || (bottom_bar_height > 0.0 && row >= frame_image->height() - bottom_bar_height)) { + memcpy(&pixels[row * frame_image->width() * 4], color_pixels, sizeof(char) * frame_image->width() * 4); + } else { + // Left Crop + if (left_bar_width > 0.0) { + memcpy(&pixels[row * frame_image->width() * 4], color_pixels, sizeof(char) * left_bar_width * 4); + } + + // Right Crop + if (right_bar_width > 0.0) { + memcpy(&pixels[((row * frame_image->width()) + (frame_image->width() - right_bar_width)) * 4], color_pixels, sizeof(char) * right_bar_width * 4); + } + } + } + + // Cleanup colors and arrays + tempColor.reset(); + + // return the modified frame + return frame; +} + +// Generate JSON string of this object +string Crop::Json() { + + // Return formatted string + return JsonValue().toStyledString(); +} + +// Generate Json::JsonValue for this object +Json::Value Crop::JsonValue() { + + // Create root json object + Json::Value root = EffectBase::JsonValue(); // get parent properties + root["type"] = info.class_name; + root["left"] = left.JsonValue(); + root["top"] = top.JsonValue(); + root["right"] = right.JsonValue(); + root["bottom"] = bottom.JsonValue(); + + // return JsonValue + return root; +} + +// Load JSON string into this object +void Crop::SetJson(string value) { + + // Parse JSON string into JSON objects + Json::Value root; + Json::Reader reader; + bool success = reader.parse( value, root ); + if (!success) + // Raise exception + throw InvalidJSON("JSON could not be parsed (or is invalid)", ""); + + try + { + // Set all values that match + SetJsonValue(root); + } + catch (exception e) + { + // Error parsing JSON (or missing keys) + throw InvalidJSON("JSON is invalid (missing keys or invalid data types)", ""); + } +} + +// Load Json::JsonValue into this object +void Crop::SetJsonValue(Json::Value root) { + + // Set parent data + EffectBase::SetJsonValue(root); + + // Set data from Json (if key is found) + 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"]); +} + +// Get all properties for a specific frame +string Crop::PropertiesJSON(int64_t requested_frame) { + + // 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["left"] = add_property_json("Left Size", left.GetValue(requested_frame), "float", "", &left, 0.0, 1.0, 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, 1.0, false, requested_frame); + root["bottom"] = add_property_json("Bottom Size", bottom.GetValue(requested_frame), "float", "", &bottom, 0.0, 1.0, false, requested_frame); + + // Return formatted string + return root.toStyledString(); +} +