1 /**
2  * @file
3  * @brief Source file for Color Shift effect class
4  * @author Jonathan Thomas <jonathan@openshot.org>
5  *
6  * @ref License
7  */
8 
9 /* LICENSE
10  *
11  * Copyright (c) 2008-2019 OpenShot Studios, LLC
12  * <http://www.openshotstudios.com/>. This file is part of
13  * OpenShot Library (libopenshot), an open-source project dedicated to
14  * delivering high quality video editing and animation solutions to the
15  * world. For more information visit <http://www.openshot.org/>.
16  *
17  * OpenShot Library (libopenshot) is free software: you can redistribute it
18  * and/or modify it under the terms of the GNU Lesser General Public License
19  * as published by the Free Software Foundation, either version 3 of the
20  * License, or (at your option) any later version.
21  *
22  * OpenShot Library (libopenshot) is distributed in the hope that it will be
23  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25  * GNU Lesser General Public License for more details.
26  *
27  * You should have received a copy of the GNU Lesser General Public License
28  * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
29  */
30 
31 #include "ColorShift.h"
32 #include "Exceptions.h"
33 
34 using namespace openshot;
35 
36 /// Blank constructor, useful when using Json to load the effect properties
ColorShift()37 ColorShift::ColorShift() : red_x(0.0), red_y(0.0), green_x(0.0), green_y(0.0), blue_x(0.0), blue_y(0.0), alpha_x(0.0), alpha_y(0.0) {
38 	// Init effect properties
39 	init_effect_details();
40 }
41 
42 // Default constructor
ColorShift(Keyframe red_x,Keyframe red_y,Keyframe green_x,Keyframe green_y,Keyframe blue_x,Keyframe blue_y,Keyframe alpha_x,Keyframe alpha_y)43 ColorShift::ColorShift(Keyframe red_x, Keyframe red_y, Keyframe green_x, Keyframe green_y, Keyframe blue_x, Keyframe blue_y, Keyframe alpha_x, Keyframe alpha_y) :
44 		red_x(red_x), red_y(red_y), green_x(green_x), green_y(green_y), blue_x(blue_x), blue_y(blue_y), alpha_x(alpha_x), alpha_y(alpha_y)
45 {
46 	// Init effect properties
47 	init_effect_details();
48 }
49 
50 // Init effect settings
init_effect_details()51 void ColorShift::init_effect_details()
52 {
53 	/// Initialize the values of the EffectInfo struct.
54 	InitEffectInfo();
55 
56 	/// Set the effect info
57 	info.class_name = "ColorShift";
58 	info.name = "Color Shift";
59 	info.description = "Shift the colors of an image up, down, left, and right (with infinite wrapping).";
60 	info.has_audio = false;
61 	info.has_video = true;
62 }
63 
64 // This method is required for all derived classes of EffectBase, and returns a
65 // modified openshot::Frame object
GetFrame(std::shared_ptr<openshot::Frame> frame,int64_t frame_number)66 std::shared_ptr<openshot::Frame> ColorShift::GetFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number)
67 {
68 	// Get the frame's image
69 	std::shared_ptr<QImage> frame_image = frame->GetImage();
70 	unsigned char *pixels = (unsigned char *) frame_image->bits();
71 
72 	// Get image size
73 	int frame_image_width = frame_image->width();
74 	int frame_image_height = frame_image->height();
75 
76 	// Get the current shift amount, and clamp to range (-1 to 1 range)
77 	// Red Keyframes
78 	float red_x_shift = red_x.GetValue(frame_number);
79 	int red_x_shift_limit = round(frame_image_width * fmod(fabs(red_x_shift), 1.0));
80 	float red_y_shift = red_y.GetValue(frame_number);
81 	int red_y_shift_limit = round(frame_image_height * fmod(fabs(red_y_shift), 1.0));
82 	// Green Keyframes
83 	float green_x_shift = green_x.GetValue(frame_number);
84 	int green_x_shift_limit = round(frame_image_width * fmod(fabs(green_x_shift), 1.0));
85 	float green_y_shift = green_y.GetValue(frame_number);
86 	int green_y_shift_limit = round(frame_image_height * fmod(fabs(green_y_shift), 1.0));
87 	// Blue Keyframes
88 	float blue_x_shift = blue_x.GetValue(frame_number);
89 	int blue_x_shift_limit = round(frame_image_width * fmod(fabs(blue_x_shift), 1.0));
90 	float blue_y_shift = blue_y.GetValue(frame_number);
91 	int blue_y_shift_limit = round(frame_image_height * fmod(fabs(blue_y_shift), 1.0));
92 	// Alpha Keyframes
93 	float alpha_x_shift = alpha_x.GetValue(frame_number);
94 	int alpha_x_shift_limit = round(frame_image_width * fmod(fabs(alpha_x_shift), 1.0));
95 	float alpha_y_shift = alpha_y.GetValue(frame_number);
96 	int alpha_y_shift_limit = round(frame_image_height * fmod(fabs(alpha_y_shift), 1.0));
97 
98 	// Make temp copy of pixels
99 	unsigned char *temp_image = new unsigned char[frame_image_width * frame_image_height * 4]();
100 	memcpy(temp_image, pixels, sizeof(char) * frame_image_width * frame_image_height * 4);
101 
102 	// Init position of current row and pixel
103 	int starting_row_index = 0;
104 	int byte_index = 0;
105 
106 	// Init RGBA values
107 	unsigned char R = 0;
108 	unsigned char G = 0;
109 	unsigned char B = 0;
110 	unsigned char A = 0;
111 
112 	int red_starting_row_index = 0;
113 	int green_starting_row_index = 0;
114 	int blue_starting_row_index = 0;
115 	int alpha_starting_row_index = 0;
116 
117 	int red_pixel_offset = 0;
118 	int green_pixel_offset = 0;
119 	int blue_pixel_offset = 0;
120 	int alpha_pixel_offset = 0;
121 
122 	// Loop through rows of pixels
123 	for (int row = 0; row < frame_image_height; row++) {
124 		for (int col = 0; col < frame_image_width; col++) {
125 			// Get position of current row and pixel
126 			starting_row_index = row * frame_image_width * 4;
127 			byte_index = starting_row_index + (col * 4);
128 			red_starting_row_index = starting_row_index;
129 			green_starting_row_index = starting_row_index;
130 			blue_starting_row_index = starting_row_index;
131 			alpha_starting_row_index = starting_row_index;
132 
133 			red_pixel_offset = 0;
134 			green_pixel_offset = 0;
135 			blue_pixel_offset = 0;
136 			alpha_pixel_offset = 0;
137 
138 			// Get the RGBA value from each pixel (depending on offset)
139 			R = temp_image[byte_index];
140 			G = temp_image[byte_index + 1];
141 			B = temp_image[byte_index + 2];
142 			A = temp_image[byte_index + 3];
143 
144 			// Shift X
145 			if (red_x_shift > 0.0)
146 				red_pixel_offset = (col + red_x_shift_limit) % frame_image_width;
147 			if (red_x_shift < 0.0)
148 				red_pixel_offset = (frame_image_width + col - red_x_shift_limit) % frame_image_width;
149 			if (green_x_shift > 0.0)
150 				green_pixel_offset = (col + green_x_shift_limit) % frame_image_width;
151 			if (green_x_shift < 0.0)
152 				green_pixel_offset = (frame_image_width + col - green_x_shift_limit) % frame_image_width;
153 			if (blue_x_shift > 0.0)
154 				blue_pixel_offset = (col + blue_x_shift_limit) % frame_image_width;
155 			if (blue_x_shift < 0.0)
156 				blue_pixel_offset = (frame_image_width + col - blue_x_shift_limit) % frame_image_width;
157 			if (alpha_x_shift > 0.0)
158 				alpha_pixel_offset = (col + alpha_x_shift_limit) % frame_image_width;
159 			if (alpha_x_shift < 0.0)
160 				alpha_pixel_offset = (frame_image_width + col - alpha_x_shift_limit) % frame_image_width;
161 
162 			// Shift Y
163 			if (red_y_shift > 0.0)
164 				red_starting_row_index = ((row + red_y_shift_limit) % frame_image_height) * frame_image_width * 4;
165 			if (red_y_shift < 0.0)
166 				red_starting_row_index = ((frame_image_height + row - red_y_shift_limit) % frame_image_height) * frame_image_width * 4;
167 			if (green_y_shift > 0.0)
168 				green_starting_row_index = ((row + green_y_shift_limit) % frame_image_height) * frame_image_width * 4;
169 			if (green_y_shift < 0.0)
170 				green_starting_row_index = ((frame_image_height + row - green_y_shift_limit) % frame_image_height) * frame_image_width * 4;
171 			if (blue_y_shift > 0.0)
172 				blue_starting_row_index = ((row + blue_y_shift_limit) % frame_image_height) * frame_image_width * 4;
173 			if (blue_y_shift < 0.0)
174 				blue_starting_row_index = ((frame_image_height + row - blue_y_shift_limit) % frame_image_height) * frame_image_width * 4;
175 			if (alpha_y_shift > 0.0)
176 				alpha_starting_row_index = ((row + alpha_y_shift_limit) % frame_image_height) * frame_image_width * 4;
177 			if (alpha_y_shift < 0.0)
178 				alpha_starting_row_index = ((frame_image_height + row - alpha_y_shift_limit) % frame_image_height) * frame_image_width * 4;
179 
180 			// Copy new values to this pixel
181 			pixels[red_starting_row_index + 0 + (red_pixel_offset * 4)] = R;
182 			pixels[green_starting_row_index + 1 + (green_pixel_offset * 4)] = G;
183 			pixels[blue_starting_row_index + 2 + (blue_pixel_offset * 4)] = B;
184 			pixels[alpha_starting_row_index + 3 + (alpha_pixel_offset * 4)] = A;
185 		}
186 	}
187 
188 	// Delete arrays
189 	delete[] temp_image;
190 
191 	// return the modified frame
192 	return frame;
193 }
194 
195 // Generate JSON string of this object
Json() const196 std::string ColorShift::Json() const {
197 
198 	// Return formatted string
199 	return JsonValue().toStyledString();
200 }
201 
202 // Generate Json::Value for this object
JsonValue() const203 Json::Value ColorShift::JsonValue() const {
204 
205 	// Create root json object
206 	Json::Value root = EffectBase::JsonValue(); // get parent properties
207 	root["type"] = info.class_name;
208 	root["red_x"] = red_x.JsonValue();
209 	root["red_y"] = red_y.JsonValue();
210 	root["green_x"] = green_x.JsonValue();
211 	root["green_y"] = green_y.JsonValue();
212 	root["blue_x"] = blue_x.JsonValue();
213 	root["blue_y"] = blue_y.JsonValue();
214 	root["alpha_x"] = alpha_x.JsonValue();
215 	root["alpha_y"] = alpha_y.JsonValue();
216 
217 	// return JsonValue
218 	return root;
219 }
220 
221 // Load JSON string into this object
SetJson(const std::string value)222 void ColorShift::SetJson(const std::string value) {
223 
224 	// Parse JSON string into JSON objects
225 	try
226 	{
227 		const Json::Value root = openshot::stringToJson(value);
228 		// Set all values that match
229 		SetJsonValue(root);
230 	}
231 	catch (const std::exception& e)
232 	{
233 		// Error parsing JSON (or missing keys)
234 		throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
235 	}
236 }
237 
238 // Load Json::Value into this object
SetJsonValue(const Json::Value root)239 void ColorShift::SetJsonValue(const Json::Value root) {
240 
241 	// Set parent data
242 	EffectBase::SetJsonValue(root);
243 
244 	// Set data from Json (if key is found)
245 	if (!root["red_x"].isNull())
246 		red_x.SetJsonValue(root["red_x"]);
247 	if (!root["red_y"].isNull())
248 		red_y.SetJsonValue(root["red_y"]);
249 	if (!root["green_x"].isNull())
250 		green_x.SetJsonValue(root["green_x"]);
251 	if (!root["green_y"].isNull())
252 		green_y.SetJsonValue(root["green_y"]);
253 	if (!root["blue_x"].isNull())
254 		blue_x.SetJsonValue(root["blue_x"]);
255 	if (!root["blue_y"].isNull())
256 		blue_y.SetJsonValue(root["blue_y"]);
257 	if (!root["alpha_x"].isNull())
258 		alpha_x.SetJsonValue(root["alpha_x"]);
259 	if (!root["alpha_y"].isNull())
260 		alpha_y.SetJsonValue(root["alpha_y"]);
261 }
262 
263 // Get all properties for a specific frame
PropertiesJSON(int64_t requested_frame) const264 std::string ColorShift::PropertiesJSON(int64_t requested_frame) const {
265 
266 	// Generate JSON properties list
267 	Json::Value root;
268 	root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame);
269 	root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame);
270 	root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame);
271 	root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame);
272 	root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame);
273 	root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 1000 * 60 * 30, true, requested_frame);
274 
275 	// Keyframes
276 	root["red_x"] = add_property_json("Red X Shift", red_x.GetValue(requested_frame), "float", "", &red_x, -1, 1, false, requested_frame);
277 	root["red_y"] = add_property_json("Red Y Shift", red_y.GetValue(requested_frame), "float", "", &red_y, -1, 1, false, requested_frame);
278 	root["green_x"] = add_property_json("Green X Shift", green_x.GetValue(requested_frame), "float", "", &green_x, -1, 1, false, requested_frame);
279 	root["green_y"] = add_property_json("Green Y Shift", green_y.GetValue(requested_frame), "float", "", &green_y, -1, 1, false, requested_frame);
280 	root["blue_x"] = add_property_json("Blue X Shift", blue_x.GetValue(requested_frame), "float", "", &blue_x, -1, 1, false, requested_frame);
281 	root["blue_y"] = add_property_json("Blue Y Shift", blue_y.GetValue(requested_frame), "float", "", &blue_y, -1, 1, false, requested_frame);
282 	root["alpha_x"] = add_property_json("Alpha X Shift", alpha_x.GetValue(requested_frame), "float", "", &alpha_x, -1, 1, false, requested_frame);
283 	root["alpha_y"] = add_property_json("Alpha Y Shift", alpha_y.GetValue(requested_frame), "float", "", &alpha_y, -1, 1, false, requested_frame);
284 
285 	// Set the parent effect which properties this effect will inherit
286 	root["parent_effect_id"] = add_property_json("Parent", 0.0, "string", info.parent_effect_id, NULL, -1, -1, false, requested_frame);
287 
288 	// Return formatted string
289 	return root.toStyledString();
290 }
291