diff --git a/include/Effects.h b/include/Effects.h index 739ca20c..0a7f9ff5 100644 --- a/include/Effects.h +++ b/include/Effects.h @@ -29,10 +29,12 @@ */ /* Effects */ +#include "effects/Brightness.h" #include "effects/ChromaKey.h" #include "effects/Deinterlace.h" #include "effects/Mask.h" #include "effects/Negate.h" +#include "effects/Saturation.h" #endif diff --git a/include/effects/Brightness.h b/include/effects/Brightness.h new file mode 100644 index 00000000..15a0d01e --- /dev/null +++ b/include/effects/Brightness.h @@ -0,0 +1,108 @@ +/** + * @file + * @brief Header file for Brightness 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_BRIGHTNESS_EFFECT_H +#define OPENSHOT_BRIGHTNESS_EFFECT_H + +#include "../EffectBase.h" + +#include +#include +#include +#include +#include +#include +#include "Magick++.h" +#include "../Color.h" +#include "../Exceptions.h" +#include "../Json.h" +#include "../KeyFrame.h" +#include "../ReaderBase.h" +#include "../FFmpegReader.h" +#include "../ImageReader.h" +#include "../QtImageReader.h" +#include "../ChunkReader.h" + +using namespace std; + +namespace openshot +{ + + /** + * @brief This class adjusts the brightness and contrast of an image, and can be animated + * with openshot::Keyframe curves over time. + * + * Adjusting the brightness and contrast over time can create many different powerful effects. + */ + 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(); + + public: + Keyframe brightness; ///< Brightness keyframe. A constant value here will prevent animation. + Keyframe contrast; ///< Contrast keyframe. + + /// Blank constructor, useful when using Json to load the effect properties + Brightness(); + + /// Default constructor, which takes 2 curves. The curves adjust the brightness and + // contrast of a frame's image. + /// + /// @param new_brightness The curve to adjust the brightness (between 100 and -100) + /// @param new_contrast The curve to adjust the contrast (3 is typical, 20 is a lot, 0 is invalid) + Brightness(Keyframe new_brightness, Keyframe new_contrast); + + /// @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. + tr1::shared_ptr GetFrame(tr1::shared_ptr frame, int frame_number); + + /// Get and Set JSON methods + string Json(); ///< Generate JSON string of this object + void SetJson(string value) throw(InvalidJSON); ///< 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(int requested_frame); + }; + +} + +#endif diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h new file mode 100644 index 00000000..e47a5650 --- /dev/null +++ b/include/effects/Saturation.h @@ -0,0 +1,105 @@ +/** + * @file + * @brief Header file for Saturation 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_SATURATION_EFFECT_H +#define OPENSHOT_SATURATION_EFFECT_H + +#include "../EffectBase.h" + +#include +#include +#include +#include +#include +#include +#include "Magick++.h" +#include "../Color.h" +#include "../Exceptions.h" +#include "../Json.h" +#include "../KeyFrame.h" +#include "../ReaderBase.h" +#include "../FFmpegReader.h" +#include "../ImageReader.h" +#include "../QtImageReader.h" +#include "../ChunkReader.h" + +using namespace std; + +namespace openshot +{ + + /** + * @brief This class adjusts the saturation of color on a frame's image. + * + * This can be animated by passing in a Keyframe. Animating the color saturation can create + * some very cool effects. + */ + 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(); + + public: + Keyframe saturation; ///< The color saturation: 0.0 = black and white, 1.0 = normal, 2.0 = double saturation + + /// Blank constructor, useful when using Json to load the effect properties + Saturation(); + + /// Default constructor, which takes 1 curve, to adjust the color saturation over time. + /// + /// @param new_saturation The curve to adjust the saturation of the frame's image (0.0 = black and white, 1.0 = normal, 2.0 = double saturation) + Saturation(Keyframe new_saturation); + + /// @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. + tr1::shared_ptr GetFrame(tr1::shared_ptr frame, int frame_number); + + /// Get and Set JSON methods + string Json(); ///< Generate JSON string of this object + void SetJson(string value) throw(InvalidJSON); ///< 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(int requested_frame); + }; + +} + +#endif diff --git a/src/Clip.cpp b/src/Clip.cpp index 737a9f16..59f9a05e 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -718,7 +718,10 @@ void Clip::SetJsonValue(Json::Value root) { if (!existing_effect["type"].isNull()) // Init the matching effect object - if (existing_effect["type"].asString() == "ChromaKey") + if (existing_effect["type"].asString() == "Brightness") + e = new Brightness(); + + else if (existing_effect["type"].asString() == "ChromaKey") e = new ChromaKey(); else if (existing_effect["type"].asString() == "Deinterlace") @@ -730,6 +733,9 @@ void Clip::SetJsonValue(Json::Value root) { else if (existing_effect["type"].asString() == "Negate") e = new Negate(); + else if (existing_effect["type"].asString() == "Saturation") + e = new Saturation(); + // Load Json into Effect e->SetJsonValue(existing_effect); diff --git a/src/EffectInfo.cpp b/src/EffectInfo.cpp index 844d220c..de8ab4ab 100644 --- a/src/EffectInfo.cpp +++ b/src/EffectInfo.cpp @@ -45,10 +45,12 @@ Json::Value EffectInfo::JsonValue() { Json::Value root; // Append info JSON from each supported effect + root.append(Brightness().JsonInfo()); root.append(ChromaKey().JsonInfo()); root.append(Deinterlace().JsonInfo()); root.append(Mask().JsonInfo()); root.append(Negate().JsonInfo()); + root.append(Saturation().JsonInfo()); // return JsonValue return root; diff --git a/src/bindings/python/openshot.i b/src/bindings/python/openshot.i index 561cc007..df815664 100644 --- a/src/bindings/python/openshot.i +++ b/src/bindings/python/openshot.i @@ -131,10 +131,12 @@ %include "../../../include/Timeline.h" /* Effects */ +%include "../../../include/effects/Brightness.h" %include "../../../include/effects/ChromaKey.h" %include "../../../include/effects/Deinterlace.h" %include "../../../include/effects/Mask.h" %include "../../../include/effects/Negate.h" +%include "../../../include/effects/Saturation.h" /* Wrap std templates (list, vector, etc...) */ diff --git a/src/bindings/ruby/openshot.i b/src/bindings/ruby/openshot.i index 9dd77d06..c35dfbd0 100644 --- a/src/bindings/ruby/openshot.i +++ b/src/bindings/ruby/openshot.i @@ -137,10 +137,12 @@ namespace tr1 %include "../../../include/Timeline.h" /* Effects */ +%include "../../../include/effects/Brightness.h" %include "../../../include/effects/ChromaKey.h" %include "../../../include/effects/Deinterlace.h" %include "../../../include/effects/Mask.h" %include "../../../include/effects/Negate.h" +%include "../../../include/effects/Saturation.h" /* Wrap std templates (list, vector, etc...) */ diff --git a/src/effects/Brightness.cpp b/src/effects/Brightness.cpp new file mode 100644 index 00000000..a60d67b7 --- /dev/null +++ b/src/effects/Brightness.cpp @@ -0,0 +1,198 @@ +/** + * @file + * @brief Source file for Brightness 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/Brightness.h" + +using namespace openshot; + +/// Blank constructor, useful when using Json to load the effect properties +Brightness::Brightness() { + // Init effect properties + init_effect_details(); + + // Init curves + brightness = Keyframe(0.0); + contrast = Keyframe(3.0); +} + +// Default constructor +Brightness::Brightness(Keyframe new_brightness, Keyframe new_contrast) : brightness(new_brightness), contrast(new_contrast) +{ + // Init effect properties + init_effect_details(); +} + +// Init effect settings +void Brightness::init_effect_details() +{ + /// Initialize the values of the EffectInfo struct. + InitEffectInfo(); + + /// Set the effect info + info.class_name = "Brightness"; + info.name = "Brightness & Contrast"; + info.description = "Adjust the brightness and contrast of the frame's image."; + info.has_audio = false; + 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 +tr1::shared_ptr Brightness::GetFrame(tr1::shared_ptr frame, int frame_number) +{ + // Get the frame's image + tr1::shared_ptr frame_image = frame->GetImage(); + + // 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]; + + // Adjust the contrast + int factor = (259 * (contrast.GetValue(frame_number) + 255)) / (255 * (259 - contrast.GetValue(frame_number))); + R = constrain((factor * (R - 128)) + 128); + G = constrain((factor * (G - 128)) + 128); + B = constrain((factor * (B - 128)) + 128); + + // Adjust the brightness + R += (255 * brightness.GetValue(frame_number)); + G += (255 * brightness.GetValue(frame_number)); + B += (255 * brightness.GetValue(frame_number)); + + // Constrain the value from 0 to 255 + R = constrain(R); + G = constrain(G); + B = constrain(B); + + // Set all pixels to new value + pixels[byte_index] = R; + pixels[byte_index + 1] = G; + pixels[byte_index + 2] = B; + pixels[byte_index + 3] = A; // leave the alpha value alone + } + + // return the modified frame + return frame; +} + +// Generate JSON string of this object +string Brightness::Json() { + + // Return formatted string + return JsonValue().toStyledString(); +} + +// Generate Json::JsonValue for this object +Json::Value Brightness::JsonValue() { + + // Create root json object + Json::Value root = EffectBase::JsonValue(); // get parent properties + root["type"] = info.class_name; + root["brightness"] = brightness.JsonValue(); + root["contrast"] = contrast.JsonValue(); + + // return JsonValue + return root; +} + +// Load JSON string into this object +void Brightness::SetJson(string value) throw(InvalidJSON) { + + // 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 Brightness::SetJsonValue(Json::Value root) { + + // Set parent data + EffectBase::SetJsonValue(root); + + // Set data from Json (if key is found) + if (!root["brightness"].isNull()) + brightness.SetJsonValue(root["brightness"]); + if (!root["contrast"].isNull()) + contrast.SetJsonValue(root["contrast"]); +} + +// Get all properties for a specific frame +string Brightness::PropertiesJSON(int requested_frame) { + + // Requested Point + Point requested_point(requested_frame, requested_frame); + + // Generate JSON properties list + Json::Value root; + root["id"] = add_property_json("ID", 0.0, "string", Id(), false, 0, -1, -1, CONSTANT, -1, true); + root["position"] = add_property_json("Position", Position(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, false); + root["layer"] = add_property_json("Layer", Layer(), "int", "", false, 0, 0, 1000, CONSTANT, -1, false); + root["start"] = add_property_json("Start", Start(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, false); + root["end"] = add_property_json("End", End(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, false); + root["duration"] = add_property_json("Duration", Duration(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, true); + + // Keyframes + root["brightness"] = add_property_json("Brightness", brightness.GetValue(requested_frame), "float", "", brightness.Contains(requested_point), brightness.GetCount(), -10000, 10000, brightness.GetClosestPoint(requested_point).interpolation, brightness.GetClosestPoint(requested_point).co.X, false); + root["contrast"] = add_property_json("Contrast", contrast.GetValue(requested_frame), "float", "", contrast.Contains(requested_point), contrast.GetCount(), -10000, 10000, contrast.GetClosestPoint(requested_point).interpolation, contrast.GetClosestPoint(requested_point).co.X, false); + + // Return formatted string + return root.toStyledString(); +} + diff --git a/src/effects/Saturation.cpp b/src/effects/Saturation.cpp new file mode 100644 index 00000000..c094c6ff --- /dev/null +++ b/src/effects/Saturation.cpp @@ -0,0 +1,196 @@ +/** + * @file + * @brief Source file for Saturation 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/Saturation.h" + +using namespace openshot; + +/// Blank constructor, useful when using Json to load the effect properties +Saturation::Saturation() { + // Init effect properties + init_effect_details(); + + // Init curves + saturation = Keyframe(1.0); +} + +// Default constructor +Saturation::Saturation(Keyframe new_saturation) : saturation(new_saturation) +{ + // Init effect properties + init_effect_details(); +} + +// Init effect settings +void Saturation::init_effect_details() +{ + /// Initialize the values of the EffectInfo struct. + InitEffectInfo(); + + /// Set the effect info + info.class_name = "Saturation"; + info.name = "Color Saturation"; + info.description = "Adjust the color saturation."; + info.has_audio = false; + 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 +tr1::shared_ptr Saturation::GetFrame(tr1::shared_ptr frame, int frame_number) +{ + // Get the frame's image + tr1::shared_ptr frame_image = frame->GetImage(); + + // Constants used for color saturation formula + double pR = .299; + double pG = .587; + double pB = .114; + + // 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]; + + // Calculate the saturation multiplier + double p = sqrt( (R * R * pR) + + (G * G * pG) + + (B * B * pB) ); + + // Adjust the saturation + R = p + (R - p) * saturation.GetValue(frame_number); + G = p + (G - p) * saturation.GetValue(frame_number); + B = p + (B - p) * saturation.GetValue(frame_number); + + // Constrain the value from 0 to 255 + R = constrain(R); + G = constrain(G); + B = constrain(B); + + // Set all pixels to new value + pixels[byte_index] = R; + pixels[byte_index + 1] = G; + pixels[byte_index + 2] = B; + pixels[byte_index + 3] = A; // leave the alpha value alone + } + + // return the modified frame + return frame; +} + +// Generate JSON string of this object +string Saturation::Json() { + + // Return formatted string + return JsonValue().toStyledString(); +} + +// Generate Json::JsonValue for this object +Json::Value Saturation::JsonValue() { + + // Create root json object + Json::Value root = EffectBase::JsonValue(); // get parent properties + root["type"] = info.class_name; + root["saturation"] = saturation.JsonValue(); + + // return JsonValue + return root; +} + +// Load JSON string into this object +void Saturation::SetJson(string value) throw(InvalidJSON) { + + // 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 Saturation::SetJsonValue(Json::Value root) { + + // Set parent data + EffectBase::SetJsonValue(root); + + // Set data from Json (if key is found) + if (!root["saturation"].isNull()) + saturation.SetJsonValue(root["saturation"]); +} + +// Get all properties for a specific frame +string Saturation::PropertiesJSON(int requested_frame) { + + // Requested Point + Point requested_point(requested_frame, requested_frame); + + // Generate JSON properties list + Json::Value root; + root["id"] = add_property_json("ID", 0.0, "string", Id(), false, 0, -1, -1, CONSTANT, -1, true); + root["position"] = add_property_json("Position", Position(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, false); + root["layer"] = add_property_json("Layer", Layer(), "int", "", false, 0, 0, 1000, CONSTANT, -1, false); + root["start"] = add_property_json("Start", Start(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, false); + root["end"] = add_property_json("End", End(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, false); + root["duration"] = add_property_json("Duration", Duration(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, true); + + // Keyframes + root["saturation"] = add_property_json("Saturation", saturation.GetValue(requested_frame), "float", "", saturation.Contains(requested_point), saturation.GetCount(), -10000, 10000, saturation.GetClosestPoint(requested_point).interpolation, saturation.GetClosestPoint(requested_point).co.X, false); + + // Return formatted string + return root.toStyledString(); +} +