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 
49 using namespace openshot;
50 
51 QtImageReader::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;
99  info.has_single_image = 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;
121  info.video_timebase.num = 1;
122  info.video_timebase.den = 30;
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.
162 std::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
205 QSize 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
266 QSize 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
325 std::string QtImageReader::Json() const {
326 
327  // Return formatted string
328  return JsonValue().toStyledString();
329 }
330 
331 // Generate Json::Value for this object
332 Json::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
344 void 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
361 void 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.
QtImageReader(std::string path, bool inspect_reader=true)
Constructor for QtImageReader.
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