1 /**
2 * @file
3 * @brief Source file for TextReader 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 // Require ImageMagick support
32 #ifdef USE_IMAGEMAGICK
33
34 #include "TextReader.h"
35 #include "Exceptions.h"
36
37 using namespace openshot;
38
39 /// Default constructor (blank text)
TextReader()40 TextReader::TextReader() : width(1024), height(768), x_offset(0), y_offset(0), text(""), font("Arial"), size(10.0), text_color("#ffffff"), background_color("#000000"), is_open(false), gravity(GRAVITY_CENTER) {
41
42 // Open and Close the reader, to populate its attributes (such as height, width, etc...)
43 Open();
44 Close();
45 }
46
TextReader(int width,int height,int x_offset,int y_offset,GravityType gravity,std::string text,std::string font,double size,std::string text_color,std::string background_color)47 TextReader::TextReader(int width, int height, int x_offset, int y_offset, GravityType gravity, std::string text, std::string font, double size, std::string text_color, std::string background_color)
48 : width(width), height(height), x_offset(x_offset), y_offset(y_offset), text(text), font(font), size(size), text_color(text_color), background_color(background_color), is_open(false), gravity(gravity)
49 {
50 // Open and Close the reader, to populate its attributes (such as height, width, etc...)
51 Open();
52 Close();
53 }
54
SetTextBackgroundColor(std::string color)55 void TextReader::SetTextBackgroundColor(std::string color) {
56 text_background_color = color;
57
58 // Open and Close the reader, to populate it's attributes (such as height, width, etc...) plus the text background color
59 Open();
60 Close();
61 }
62
63 // Open reader
Open()64 void TextReader::Open()
65 {
66 // Open reader if not already open
67 if (!is_open)
68 {
69 // create image
70 image = std::make_shared<Magick::Image>(
71 Magick::Geometry(width,height), Magick::Color(background_color));
72
73 // Give image a transparent background color
74 image->backgroundColor(Magick::Color("none"));
75
76 // Set gravity (map between OpenShot and ImageMagick)
77 switch (gravity)
78 {
79 case GRAVITY_TOP_LEFT:
80 lines.push_back(Magick::DrawableGravity(Magick::NorthWestGravity));
81 break;
82 case GRAVITY_TOP:
83 lines.push_back(Magick::DrawableGravity(Magick::NorthGravity));
84 break;
85 case GRAVITY_TOP_RIGHT:
86 lines.push_back(Magick::DrawableGravity(Magick::NorthEastGravity));
87 break;
88 case GRAVITY_LEFT:
89 lines.push_back(Magick::DrawableGravity(Magick::WestGravity));
90 break;
91 case GRAVITY_CENTER:
92 lines.push_back(Magick::DrawableGravity(Magick::CenterGravity));
93 break;
94 case GRAVITY_RIGHT:
95 lines.push_back(Magick::DrawableGravity(Magick::EastGravity));
96 break;
97 case GRAVITY_BOTTOM_LEFT:
98 lines.push_back(Magick::DrawableGravity(Magick::SouthWestGravity));
99 break;
100 case GRAVITY_BOTTOM:
101 lines.push_back(Magick::DrawableGravity(Magick::SouthGravity));
102 break;
103 case GRAVITY_BOTTOM_RIGHT:
104 lines.push_back(Magick::DrawableGravity(Magick::SouthEastGravity));
105 break;
106 }
107
108 // Set stroke properties
109 lines.push_back(Magick::DrawableStrokeColor(Magick::Color("none")));
110 lines.push_back(Magick::DrawableStrokeWidth(0.0));
111 lines.push_back(Magick::DrawableFillColor(text_color));
112 lines.push_back(Magick::DrawableFont(font));
113 lines.push_back(Magick::DrawablePointSize(size));
114 lines.push_back(Magick::DrawableText(x_offset, y_offset, text));
115
116 if (!text_background_color.empty()) {
117 lines.push_back(Magick::DrawableTextUnderColor(Magick::Color(text_background_color)));
118 }
119
120 // Draw image
121 image->draw(lines);
122
123 // Update image properties
124 info.has_audio = false;
125 info.has_video = true;
126 info.file_size = image->fileSize();
127 info.vcodec = image->format();
128 info.width = image->size().width();
129 info.height = image->size().height();
130 info.pixel_ratio.num = 1;
131 info.pixel_ratio.den = 1;
132 info.duration = 60 * 60 * 1; // 1 hour duration
133 info.fps.num = 30;
134 info.fps.den = 1;
135 info.video_timebase.num = 1;
136 info.video_timebase.den = 30;
137 info.video_length = round(info.duration * info.fps.ToDouble());
138
139 // Calculate the DAR (display aspect ratio)
140 Fraction size(info.width * info.pixel_ratio.num, info.height * info.pixel_ratio.den);
141
142 // Reduce size fraction
143 size.Reduce();
144
145 // Set the ratio based on the reduced fraction
146 info.display_ratio.num = size.num;
147 info.display_ratio.den = size.den;
148
149 // Mark as "open"
150 is_open = true;
151 }
152 }
153
154 // Close reader
Close()155 void TextReader::Close()
156 {
157 // Close all objects, if reader is 'open'
158 if (is_open)
159 {
160 // Mark as "closed"
161 is_open = false;
162 }
163 }
164
165 // Get an openshot::Frame object for a specific frame number of this reader.
GetFrame(int64_t requested_frame)166 std::shared_ptr<Frame> TextReader::GetFrame(int64_t requested_frame)
167 {
168 if (image)
169 {
170 // Create or get frame object
171 auto image_frame = std::make_shared<Frame>(
172 requested_frame, image->size().width(), image->size().height(),
173 "#000000", 0, 2);
174
175 // Add Image data to frame
176 auto copy_image = std::make_shared<Magick::Image>(*image.get());
177 copy_image->modifyImage(); // actually copy the image data to this object
178 //TODO: Reimplement this with QImage
179 image_frame->AddMagickImage(copy_image);
180
181 // return frame object
182 return image_frame;
183 } else {
184 // return empty frame
185 auto image_frame = std::make_shared<Frame>(1, 640, 480, "#000000", 0, 2);
186
187 // return frame object
188 return image_frame;
189 }
190
191 }
192
193 // Generate JSON string of this object
Json() const194 std::string TextReader::Json() const {
195
196 // Return formatted string
197 return JsonValue().toStyledString();
198 }
199
200 // Generate Json::Value for this object
JsonValue() const201 Json::Value TextReader::JsonValue() const {
202
203 // Create root json object
204 Json::Value root = ReaderBase::JsonValue(); // get parent properties
205 root["type"] = "TextReader";
206 root["width"] = width;
207 root["height"] = height;
208 root["x_offset"] = x_offset;
209 root["y_offset"] = y_offset;
210 root["text"] = text;
211 root["font"] = font;
212 root["size"] = size;
213 root["text_color"] = text_color;
214 root["background_color"] = background_color;
215 root["text_background_color"] = text_background_color;
216 root["gravity"] = gravity;
217
218 // return JsonValue
219 return root;
220 }
221
222 // Load JSON string into this object
SetJson(const std::string value)223 void TextReader::SetJson(const std::string value) {
224 try
225 {
226 Json::Value root = openshot::stringToJson(value);
227 // Set all values that match
228 SetJsonValue(root);
229 }
230 catch (const std::exception& e)
231 {
232 // Error parsing JSON (or missing keys)
233 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
234 }
235 }
236
237 // Load Json::Value into this object
SetJsonValue(const Json::Value root)238 void TextReader::SetJsonValue(const Json::Value root) {
239
240 // Set parent data
241 ReaderBase::SetJsonValue(root);
242
243 // Set data from Json (if key is found)
244 if (!root["width"].isNull())
245 width = root["width"].asInt();
246 if (!root["height"].isNull())
247 height = root["height"].asInt();
248 if (!root["x_offset"].isNull())
249 x_offset = root["x_offset"].asInt();
250 if (!root["y_offset"].isNull())
251 y_offset = root["y_offset"].asInt();
252 if (!root["text"].isNull())
253 text = root["text"].asString();
254 if (!root["font"].isNull())
255 font = root["font"].asString();
256 if (!root["size"].isNull())
257 size = root["size"].asDouble();
258 if (!root["text_color"].isNull())
259 text_color = root["text_color"].asString();
260 if (!root["background_color"].isNull())
261 background_color = root["background_color"].asString();
262 if (!root["text_background_color"].isNull())
263 text_background_color = root["text_background_color"].asString();
264 if (!root["gravity"].isNull())
265 gravity = (GravityType) root["gravity"].asInt();
266
267 // Re-Open path, and re-init everything (if needed)
268 if (is_open)
269 {
270 Close();
271 Open();
272 }
273 }
274
275 #endif //USE_IMAGEMAGICK
276