diff --git a/include/EffectBase.h b/include/EffectBase.h index 6ca92151..209369a8 100644 --- a/include/EffectBase.h +++ b/include/EffectBase.h @@ -75,6 +75,9 @@ namespace openshot /// Display effect information in the standard output stream (stdout) void DisplayInfo(); + /// Constrain a color value from 0 to 255 + int constrain(int color_value); + /// @brief This method is required for all derived classes of EffectBase, and returns a /// modified openshot::Frame object /// diff --git a/include/Effects.h b/include/Effects.h index 32b801b8..81d15362 100644 --- a/include/Effects.h +++ b/include/Effects.h @@ -33,6 +33,7 @@ #include "effects/Brightness.h" #include "effects/ChromaKey.h" #include "effects/Deinterlace.h" +#include "effects/Hue.h" #include "effects/Mask.h" #include "effects/Negate.h" #include "effects/Saturation.h" diff --git a/include/effects/Brightness.h b/include/effects/Brightness.h index 3f13e97a..67ab4c9c 100644 --- a/include/effects/Brightness.h +++ b/include/effects/Brightness.h @@ -59,9 +59,6 @@ namespace openshot class Brightness : public EffectBase { private: - /// Constrain a color value from 0 to 255 - int constrain(int color_value); - /// Init effect settings void init_effect_details(); diff --git a/include/effects/Hue.h b/include/effects/Hue.h new file mode 100644 index 00000000..4f680047 --- /dev/null +++ b/include/effects/Hue.h @@ -0,0 +1,93 @@ +/** + * @file + * @brief Header file for Hue 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_HUE_EFFECT_H +#define OPENSHOT_HUE_EFFECT_H + +#include "../EffectBase.h" + +#include +#include +#include +#include "../Json.h" +#include "../KeyFrame.h" + + +using namespace std; + +namespace openshot +{ + + /** + * @brief This class shifts the hue of an image, and can be animated with openshot::Keyframe curves over time. + * + * Shifting hue can adjust the colors in an image towards red, blue, green, or anywhere in between. Animating hue + * can create some fun and interesting effects, but can also be used to change the mood of a scene, etc... + */ + class Hue : public EffectBase + { + private: + /// Init effect settings + void init_effect_details(); + + + public: + Keyframe hue; ///< Shift the hue coordinates (left or right) + + /// Blank constructor, useful when using Json to load the effect properties + Hue(); + + /// Default constructor, which takes 1 curve. The curves will shift the hue of the image. + /// + /// @param hue The curve to adjust the hue shift (between 0 and 1) + Hue(Keyframe hue); + + /// @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/include/effects/Mask.h b/include/effects/Mask.h index bb42743e..fbe5c320 100644 --- a/include/effects/Mask.h +++ b/include/effects/Mask.h @@ -66,9 +66,6 @@ namespace openshot ReaderBase *reader; std::shared_ptr original_mask; - /// Constrain a color value from 0 to 255 - int constrain(int color_value); - /// Get grayscale mask image void set_grayscale_mask(std::shared_ptr mask_frame_image, int width, int height, float brightness, float contrast); diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h index 1512c0a2..d49069a6 100644 --- a/include/effects/Saturation.h +++ b/include/effects/Saturation.h @@ -59,9 +59,6 @@ namespace openshot class Saturation : public EffectBase { private: - /// Constrain a color value from 0 to 255 - int constrain(int color_value); - /// Init effect settings void init_effect_details(); diff --git a/src/EffectBase.cpp b/src/EffectBase.cpp index f522d7c4..c0afded8 100644 --- a/src/EffectBase.cpp +++ b/src/EffectBase.cpp @@ -58,6 +58,18 @@ void EffectBase::DisplayInfo() { cout << "----------------------------" << endl; } +// Constrain a color value from 0 to 255 +int EffectBase::constrain(int color_value) +{ + // Constrain new color from 0 to 255 + if (color_value < 0) + color_value = 0; + else if (color_value > 255) + color_value = 255; + + return color_value; +} + // Generate JSON string of this object string EffectBase::Json() { diff --git a/src/EffectInfo.cpp b/src/EffectInfo.cpp index 28efb9ed..8c96031f 100644 --- a/src/EffectInfo.cpp +++ b/src/EffectInfo.cpp @@ -53,6 +53,9 @@ EffectBase* EffectInfo::CreateEffect(string effect_type) { else if (effect_type == "Deinterlace") return new Deinterlace(); + else if (effect_type == "Hue") + return new Hue(); + else if (effect_type == "Mask") return new Mask(); @@ -77,6 +80,7 @@ Json::Value EffectInfo::JsonValue() { root.append(Brightness().JsonInfo()); root.append(ChromaKey().JsonInfo()); root.append(Deinterlace().JsonInfo()); + root.append(Hue().JsonInfo()); root.append(Mask().JsonInfo()); root.append(Negate().JsonInfo()); root.append(Saturation().JsonInfo()); diff --git a/src/bindings/python/openshot.i b/src/bindings/python/openshot.i index d417b528..3ca600da 100644 --- a/src/bindings/python/openshot.i +++ b/src/bindings/python/openshot.i @@ -164,6 +164,7 @@ %include "../../../include/effects/Brightness.h" %include "../../../include/effects/ChromaKey.h" %include "../../../include/effects/Deinterlace.h" +%include "../../../include/effects/Hue.h" %include "../../../include/effects/Mask.h" %include "../../../include/effects/Negate.h" %include "../../../include/effects/Saturation.h" diff --git a/src/bindings/ruby/openshot.i b/src/bindings/ruby/openshot.i index 8893a301..2306065a 100644 --- a/src/bindings/ruby/openshot.i +++ b/src/bindings/ruby/openshot.i @@ -157,6 +157,7 @@ namespace std { %include "../../../include/effects/Brightness.h" %include "../../../include/effects/ChromaKey.h" %include "../../../include/effects/Deinterlace.h" +%include "../../../include/effects/Hue.h" %include "../../../include/effects/Mask.h" %include "../../../include/effects/Negate.h" %include "../../../include/effects/Saturation.h" diff --git a/src/effects/Brightness.cpp b/src/effects/Brightness.cpp index 8a791f8f..591bce6a 100644 --- a/src/effects/Brightness.cpp +++ b/src/effects/Brightness.cpp @@ -56,19 +56,6 @@ void Brightness::init_effect_details() info.has_video = true; } -// Constrain a color value from 0 to 255 -int Brightness::constrain(int color_value) -{ - // Constrain new color from 0 to 255 - if (color_value < 0) - color_value = 0; - else if (color_value > 255) - color_value = 255; - - return color_value; -} - - // This method is required for all derived classes of EffectBase, and returns a // modified openshot::Frame object std::shared_ptr Brightness::GetFrame(std::shared_ptr frame, int64_t frame_number) diff --git a/src/effects/Hue.cpp b/src/effects/Hue.cpp new file mode 100644 index 00000000..ae401a8b --- /dev/null +++ b/src/effects/Hue.cpp @@ -0,0 +1,173 @@ +/** + * @file + * @brief Source file for Hue 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/Hue.h" + +using namespace openshot; + +/// Blank constructor, useful when using Json to load the effect properties +Hue::Hue() : Hue(0.0) { + // Init effect properties + init_effect_details(); +} + +// Default constructor +Hue::Hue(Keyframe hue): hue(hue) +{ + // Init effect properties + init_effect_details(); +} + +// Init effect settings +void Hue::init_effect_details() +{ + /// Initialize the values of the EffectInfo struct. + InitEffectInfo(); + + /// Set the effect info + info.class_name = "Hue"; + info.name = "Hue"; + info.description = "Adjust the hue / color of the frame's image."; + 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 Hue::GetFrame(std::shared_ptr frame, int64_t frame_number) +{ + // Get the frame's image + std::shared_ptr frame_image = frame->GetImage(); + + // Get the current hue percentage shift amount, and convert to degrees + double degrees = 360.0 * hue.GetValue(frame_number); + float cosA = cos(degrees*3.14159265f/180); + float sinA = sin(degrees*3.14159265f/180); + + // Calculate a rotation matrix for the RGB colorspace (based on the current hue shift keyframe value) + float matrix[3][3] = {{cosA + (1.0f - cosA) / 3.0f, 1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA, 1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA}, + {1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA, cosA + 1.0f/3.0f*(1.0f - cosA), 1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA}, + {1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA, 1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA, cosA + 1.0f/3.0f * (1.0f - cosA)}}; + + // Loop through pixels + unsigned char *pixels = (unsigned char *) frame_image->bits(); + for (int pixel = 0, byte_index=0; pixel < frame_image->width() * frame_image->height(); pixel++, byte_index+=4) + { + // Get the RGB values from the pixel + int R = pixels[byte_index]; + int G = pixels[byte_index + 1]; + int B = pixels[byte_index + 2]; + int A = pixels[byte_index + 3]; + + // Multiply each color by the hue rotation matrix + float rx = constrain(R * matrix[0][0] + G * matrix[0][1] + B * matrix[0][2]); + float gx = constrain(R * matrix[1][0] + G * matrix[1][1] + B * matrix[1][2]); + float bx = constrain(R * matrix[2][0] + G * matrix[2][1] + B * matrix[2][2]); + + // Set all pixels to new value + pixels[byte_index] = rx; + pixels[byte_index + 1] = gx; + pixels[byte_index + 2] = bx; + pixels[byte_index + 3] = A; // leave the alpha value alone + } + + // return the modified frame + return frame; +} + +// Generate JSON string of this object +string Hue::Json() { + + // Return formatted string + return JsonValue().toStyledString(); +} + +// Generate Json::JsonValue for this object +Json::Value Hue::JsonValue() { + + // Create root json object + Json::Value root = EffectBase::JsonValue(); // get parent properties + root["type"] = info.class_name; + root["hue"] = hue.JsonValue(); + + // return JsonValue + return root; +} + +// Load JSON string into this object +void Hue::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 Hue::SetJsonValue(Json::Value root) { + + // Set parent data + EffectBase::SetJsonValue(root); + + // Set data from Json (if key is found) + if (!root["hue"].isNull()) + hue.SetJsonValue(root["hue"]); +} + +// Get all properties for a specific frame +string Hue::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["hue"] = add_property_json("Hue", hue.GetValue(requested_frame), "float", "", &hue, 0.0, 1.0, false, requested_frame); + + // Return formatted string + return root.toStyledString(); +} + diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp index f54abc11..5ee14a13 100644 --- a/src/effects/Mask.cpp +++ b/src/effects/Mask.cpp @@ -57,18 +57,6 @@ void Mask::init_effect_details() info.has_video = true; } -// Constrain a color value from 0 to 255 -int Mask::constrain(int color_value) -{ - // Constrain new color from 0 to 255 - if (color_value < 0) - color_value = 0; - else if (color_value > 255) - color_value = 255; - - return color_value; -} - // Get grayscale mask image void Mask::set_grayscale_mask(std::shared_ptr mask_frame_image, int width, int height, float brightness, float contrast) { diff --git a/src/effects/Saturation.cpp b/src/effects/Saturation.cpp index 582c1e91..ecb3165f 100644 --- a/src/effects/Saturation.cpp +++ b/src/effects/Saturation.cpp @@ -56,18 +56,6 @@ void Saturation::init_effect_details() info.has_video = true; } -// Constrain a color value from 0 to 255 -int Saturation::constrain(int color_value) -{ - // Constrain new color from 0 to 255 - if (color_value < 0) - color_value = 0; - else if (color_value > 255) - color_value = 255; - - return color_value; -} - // This method is required for all derived classes of EffectBase, and returns a // modified openshot::Frame object std::shared_ptr Saturation::GetFrame(std::shared_ptr frame, int64_t frame_number) diff --git a/src/effects/Shift.cpp b/src/effects/Shift.cpp index f581faa1..80a7b2d7 100644 --- a/src/effects/Shift.cpp +++ b/src/effects/Shift.cpp @@ -36,7 +36,7 @@ Shift::Shift() : x(0.0), y(0.0) { } // Default constructor -Shift::Shift(Keyframe x, Keyframe y) +Shift::Shift(Keyframe x, Keyframe y) : x(x), y(y) { // Init effect properties init_effect_details(); @@ -51,7 +51,7 @@ void Shift::init_effect_details() /// Set the effect info info.class_name = "Shift"; info.name = "Shift"; - info.description = "Shift the image up, down, left, and right (with infinite wrapping)"; + info.description = "Shift the image up, down, left, and right (with infinite wrapping)."; info.has_audio = false; info.has_video = true; }