OpenShot Library | libopenshot 0.2.7
QtImageReader.cpp
Go to the documentation of this file.
1/**
2 * @file
3 * @brief Source file for QtImageReader 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 "QtImageReader.h"
32#include "Exceptions.h"
33#include "Settings.h"
34#include "Clip.h"
35#include "CacheMemory.h"
36#include "Timeline.h"
37#include <QtCore/QString>
38#include <QtGui/QImage>
39#include <QtGui/QPainter>
40#include <QtGui/QIcon>
41#include <QtGui/QImageReader>
42
43#if USE_RESVG == 1
44 // If defined and found in CMake, utilize the libresvg for parsing
45 // SVG files and rasterizing them to QImages.
46 #include "ResvgQt.h"
47#endif
48
49using namespace openshot;
50
51QtImageReader::QtImageReader(std::string path, bool inspect_reader) : path{QString::fromStdString(path)}, is_open(false)
52{
53 // Open and Close the reader, to populate its attributes (such as height, width, etc...)
54 if (inspect_reader) {
55 Open();
56 Close();
57 }
58}
59
61{
62}
63
64// Open image file
66{
67 // Open reader if not already open
68 if (!is_open)
69 {
70 bool loaded = false;
71 QSize default_svg_size;
72
73 // Check for SVG files and rasterizing them to QImages
74 if (path.toLower().endsWith(".svg") || path.toLower().endsWith(".svgz")) {
75 default_svg_size = load_svg_path(path);
76 if (!default_svg_size.isEmpty()) {
77 loaded = true;
78 }
79 }
80
81 if (!loaded) {
82 // Attempt to open file using Qt's build in image processing capabilities
83 // AutoTransform enables exif data to be parsed and auto transform the image
84 // to the correct orientation
85 image = std::make_shared<QImage>();
86 QImageReader imgReader( path );
87 imgReader.setAutoTransform( true );
88 loaded = imgReader.read(image.get());
89 }
90
91 if (!loaded) {
92 // raise exception
93 throw InvalidFile("File could not be opened.", path.toStdString());
94 }
95
96 // Update image properties
97 info.has_audio = false;
98 info.has_video = true;
100 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
101 // byteCount() is deprecated from Qt 5.10
102 info.file_size = image->sizeInBytes();
103 #else
104 info.file_size = image->byteCount();
105 #endif
106 info.vcodec = "QImage";
107 if (!default_svg_size.isEmpty()) {
108 // Use default SVG size (if detected)
109 info.width = default_svg_size.width();
110 info.height = default_svg_size.height();
111 } else {
112 // Use Qt Image size as a fallback
113 info.width = image->width();
114 info.height = image->height();
115 }
116 info.pixel_ratio.num = 1;
117 info.pixel_ratio.den = 1;
118 info.duration = 60 * 60 * 1; // 1 hour duration
119 info.fps.num = 30;
120 info.fps.den = 1;
124
125 // Calculate the DAR (display aspect ratio)
127
128 // Reduce size fraction
129 size.Reduce();
130
131 // Set the ratio based on the reduced fraction
132 info.display_ratio.num = size.num;
133 info.display_ratio.den = size.den;
134
135 // Set current max size
136 max_size.setWidth(info.width);
137 max_size.setHeight(info.height);
138
139 // Mark as "open"
140 is_open = true;
141 }
142}
143
144// Close image file
146{
147 // Close all objects, if reader is 'open'
148 if (is_open)
149 {
150 // Mark as "closed"
151 is_open = false;
152
153 // Delete the image
154 image.reset();
155
156 info.vcodec = "";
157 info.acodec = "";
158 }
159}
160
161// Get an openshot::Frame object for a specific frame number of this reader.
162std::shared_ptr<Frame> QtImageReader::GetFrame(int64_t requested_frame)
163{
164 // Check for open reader (or throw exception)
165 if (!is_open)
166 throw ReaderClosed("The Image is closed. Call Open() before calling this method.", path.toStdString());
167
168 // Create a scoped lock, allowing only a single thread to run the following code at one time
169 const GenericScopedLock<CriticalSection> lock(getFrameCriticalSection);
170
171 // Calculate max image size
172 QSize current_max_size = calculate_max_size();
173
174 // Scale image smaller (or use a previous scaled image)
175 if (!cached_image || (max_size.width() != current_max_size.width() || max_size.height() != current_max_size.height())) {
176 // Check for SVG files and rasterize them to QImages
177 if (path.toLower().endsWith(".svg") || path.toLower().endsWith(".svgz")) {
178 load_svg_path(path);
179 }
180
181 // We need to resize the original image to a smaller image (for performance reasons)
182 // Only do this once, to prevent tons of unneeded scaling operations
183 cached_image = std::make_shared<QImage>(image->scaled(
184 current_max_size.width(), current_max_size.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
185
186 // Set max size (to later determine if max_size is changed)
187 max_size.setWidth(current_max_size.width());
188 max_size.setHeight(current_max_size.height());
189 }
190
191 // Create or get frame object
192 auto image_frame = std::make_shared<Frame>(
193 requested_frame, cached_image->width(), cached_image->height(), "#000000",
195 info.channels);
196
197 // Add Image data to frame
198 image_frame->AddImage(cached_image);
199
200 // return frame object
201 return image_frame;
202}
203
204// Calculate the max_size QSize, based on parent timeline and parent clip settings
205QSize QtImageReader::calculate_max_size() {
206 // Get max project size
207 int max_width = info.width;
208 int max_height = info.height;
209 if (max_width == 0 || max_height == 0) {
210 // If no size determined yet
211 max_width = 1920;
212 max_height = 1080;
213 }
214
215 Clip* parent = (Clip*) ParentClip();
216 if (parent) {
217 if (parent->ParentTimeline()) {
218 // Set max width/height based on parent clip's timeline (if attached to a timeline)
219 max_width = parent->ParentTimeline()->preview_width;
220 max_height = parent->ParentTimeline()->preview_height;
221 }
222 if (parent->scale == SCALE_FIT || parent->scale == SCALE_STRETCH) {
223 // Best fit or Stretch scaling (based on max timeline size * scaling keyframes)
224 float max_scale_x = parent->scale_x.GetMaxPoint().co.Y;
225 float max_scale_y = parent->scale_y.GetMaxPoint().co.Y;
226 max_width = std::max(float(max_width), max_width * max_scale_x);
227 max_height = std::max(float(max_height), max_height * max_scale_y);
228
229 } else if (parent->scale == SCALE_CROP) {
230 // Cropping scale mode (based on max timeline size * cropped size * scaling keyframes)
231 float max_scale_x = parent->scale_x.GetMaxPoint().co.Y;
232 float max_scale_y = parent->scale_y.GetMaxPoint().co.Y;
233 QSize width_size(max_width * max_scale_x,
234 round(max_width / (float(info.width) / float(info.height))));
235 QSize height_size(round(max_height / (float(info.height) / float(info.width))),
236 max_height * max_scale_y);
237 // respect aspect ratio
238 if (width_size.width() >= max_width && width_size.height() >= max_height) {
239 max_width = std::max(max_width, width_size.width());
240 max_height = std::max(max_height, width_size.height());
241 } else {
242 max_width = std::max(max_width, height_size.width());
243 max_height = std::max(max_height, height_size.height());
244 }
245 } else if (parent->scale == SCALE_NONE) {
246 // Scale images to equivalent unscaled size
247 // Since the preview window can change sizes, we want to always
248 // scale against the ratio of original image size to timeline size
249 float preview_ratio = 1.0;
250 if (parent->ParentTimeline()) {
251 Timeline *t = (Timeline *) parent->ParentTimeline();
252 preview_ratio = t->preview_width / float(t->info.width);
253 }
254 float max_scale_x = parent->scale_x.GetMaxPoint().co.Y;
255 float max_scale_y = parent->scale_y.GetMaxPoint().co.Y;
256 max_width = info.width * max_scale_x * preview_ratio;
257 max_height = info.height * max_scale_y * preview_ratio;
258 }
259 }
260
261 // Return new QSize of the current max size
262 return QSize(max_width, max_height);
263}
264
265// Load an SVG file with Resvg or fallback with Qt
266QSize QtImageReader::load_svg_path(QString) {
267 bool loaded = false;
268 QSize default_size(0,0);
269
270 // Calculate max image size
271 QSize current_max_size = calculate_max_size();
272
273#if USE_RESVG == 1
274 // Use libresvg for parsing/rasterizing SVG
275 ResvgRenderer renderer(path);
276 if (renderer.isValid()) {
277 // Set default SVG size
278 default_size.setWidth(renderer.defaultSize().width());
279 default_size.setHeight(renderer.defaultSize().height());
280
281 // Scale SVG size to keep aspect ratio, and fill the max_size as best as possible
282 QSize svg_size(default_size.width(), default_size.height());
283 svg_size.scale(current_max_size.width(), current_max_size.height(), Qt::KeepAspectRatio);
284
285 // Load SVG at max size
286 image = std::make_shared<QImage>(svg_size, QImage::Format_RGBA8888_Premultiplied);
287 image->fill(Qt::transparent);
288 QPainter p(image.get());
289 renderer.render(&p);
290 p.end();
291 loaded = true;
292 }
293#endif
294
295 if (!loaded) {
296 // Use Qt for parsing/rasterizing SVG
297 image = std::make_shared<QImage>();
298 loaded = image->load(path);
299
300 if (loaded) {
301 // Set default SVG size
302 default_size.setWidth(image->width());
303 default_size.setHeight(image->height());
304
305 if (image->width() < current_max_size.width() || image->height() < current_max_size.height()) {
306 // Load SVG into larger/project size (so image is not blurry)
307 QSize svg_size = image->size().scaled(current_max_size.width(), current_max_size.height(), Qt::KeepAspectRatio);
308 if (QCoreApplication::instance()) {
309 // Requires QApplication to be running (for QPixmap support)
310 // Re-rasterize SVG image to max size
311 image = std::make_shared<QImage>(QIcon(path).pixmap(svg_size).toImage());
312 } else {
313 // Scale image without re-rasterizing it (due to lack of QApplication)
314 image = std::make_shared<QImage>(image->scaled(
315 svg_size.width(), svg_size.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
316 }
317 }
318 }
319 }
320
321 return default_size;
322}
323
324// Generate JSON string of this object
325std::string QtImageReader::Json() const {
326
327 // Return formatted string
328 return JsonValue().toStyledString();
329}
330
331// Generate Json::Value for this object
332Json::Value QtImageReader::JsonValue() const {
333
334 // Create root json object
335 Json::Value root = ReaderBase::JsonValue(); // get parent properties
336 root["type"] = "QtImageReader";
337 root["path"] = path.toStdString();
338
339 // return JsonValue
340 return root;
341}
342
343// Load JSON string into this object
344void QtImageReader::SetJson(const std::string value) {
345
346 // Parse JSON string into JSON objects
347 try
348 {
349 const Json::Value root = openshot::stringToJson(value);
350 // Set all values that match
351 SetJsonValue(root);
352 }
353 catch (const std::exception& e)
354 {
355 // Error parsing JSON (or missing keys)
356 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
357 }
358}
359
360// Load Json::Value into this object
361void QtImageReader::SetJsonValue(const Json::Value root) {
362
363 // Set parent data
365
366 // Set data from Json (if key is found)
367 if (!root["path"].isNull())
368 path = QString::fromStdString(root["path"].asString());
369
370 // Re-Open path, and re-init everything (if needed)
371 if (is_open)
372 {
373 Close();
374 Open();
375 }
376}
Header file for CacheMemory class.
Header file for Clip class.
Header file for all Exception classes.
Header file for QtImageReader class.
Header file for global Settings class.
Header file for Timeline class.
openshot::TimelineBase * ParentTimeline()
Get the associated Timeline pointer (if any)
Definition: ClipBase.h:113
This class represents a clip (used to arrange readers on the timeline)
Definition: Clip.h:109
openshot::Keyframe scale_x
Curve representing the horizontal scaling in percent (0 to 1)
Definition: Clip.h:305
openshot::Keyframe scale_y
Curve representing the vertical scaling in percent (0 to 1)
Definition: Clip.h:306
openshot::ScaleType scale
The scale determines how a clip should be resized to fit its parent.
Definition: Clip.h:175
double Y
The Y value of the coordinate (usually representing the value of the property being animated)
Definition: Coordinate.h:57
This class represents a fraction.
Definition: Fraction.h:48
int num
Numerator for the fraction.
Definition: Fraction.h:50
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
Definition: Fraction.cpp:59
void Reduce()
Reduce this fraction (i.e. 640/480 = 4/3)
Definition: Fraction.cpp:84
int den
Denominator for the fraction.
Definition: Fraction.h:51
int GetSamplesPerFrame(openshot::Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number)
Definition: Frame.cpp:536
Exception for files that can not be found or opened.
Definition: Exceptions.h:174
Exception for invalid JSON.
Definition: Exceptions.h:206
Point GetMaxPoint() const
Get max point (by Y coordinate)
Definition: KeyFrame.cpp:255
Coordinate co
This is the primary coordinate.
Definition: Point.h:84
Json::Value JsonValue() const override
Generate Json::Value for this object.
std::string Json() const override
Generate JSON string of this object.
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
void Open() override
Open File - which is called by the constructor automatically.
void SetJson(const std::string value) override
Load JSON string into this object.
void Close() override
Close File.
std::shared_ptr< Frame > GetFrame(int64_t requested_frame) override
juce::CriticalSection getFrameCriticalSection
Section lock for multiple threads.
Definition: ReaderBase.h:101
openshot::ReaderInfo info
Information about the current media file.
Definition: ReaderBase.h:111
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
Definition: ReaderBase.cpp:171
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
Definition: ReaderBase.cpp:116
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL)
Definition: ReaderBase.cpp:254
Exception when a reader is closed, and a frame is requested.
Definition: Exceptions.h:338
int preview_height
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
Definition: TimelineBase.h:58
int preview_width
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
Definition: TimelineBase.h:57
This class represents a timeline.
Definition: Timeline.h:168
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:47
@ SCALE_FIT
Scale the clip until either height or width fills the canvas (with no cropping)
Definition: Enums.h:55
@ SCALE_STRETCH
Scale the clip until both height and width fill the canvas (distort to fit)
Definition: Enums.h:56
@ SCALE_CROP
Scale the clip until both height and width fill the canvas (cropping the overlap)
Definition: Enums.h:54
@ SCALE_NONE
Do not scale the clip.
Definition: Enums.h:57
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:34
bool has_single_image
Determines if this file only contains a single image.
Definition: ReaderBase.h:64
float duration
Length of time (in seconds)
Definition: ReaderBase.h:65
int width
The width of the video (in pixesl)
Definition: ReaderBase.h:68
int channels
The number of audio channels used in the audio stream.
Definition: ReaderBase.h:83
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition: ReaderBase.h:70
openshot::Fraction display_ratio
The ratio of width to height of the video stream (i.e. 640x480 has a ratio of 4/3)
Definition: ReaderBase.h:73
int height
The height of the video (in pixels)
Definition: ReaderBase.h:67
int64_t video_length
The number of frames in the video stream.
Definition: ReaderBase.h:75
std::string acodec
The name of the audio codec used to encode / decode the video stream.
Definition: ReaderBase.h:80
std::string vcodec
The name of the video codec used to encode / decode the video stream.
Definition: ReaderBase.h:74
openshot::Fraction pixel_ratio
The pixel ratio of the video stream as a fraction (i.e. some pixels are not square)
Definition: ReaderBase.h:72
bool has_video
Determines if this file has a video stream.
Definition: ReaderBase.h:62
bool has_audio
Determines if this file has an audio stream.
Definition: ReaderBase.h:63
openshot::Fraction video_timebase
The video timebase determines how long each frame stays on the screen.
Definition: ReaderBase.h:77
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition: ReaderBase.h:82
int64_t file_size
Size of file (in bytes)
Definition: ReaderBase.h:66