OpenShot Library | libopenshot  0.2.4
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 "../include/QtImageReader.h"
32 #include "../include/Settings.h"
33 #include "../include/Clip.h"
34 #include "../include/CacheMemory.h"
35 #include <QtCore/QString>
36 #include <QtGui/QImage>
37 #include <QtGui/QPainter>
38 
39 #if USE_RESVG == 1
40  // If defined and found in CMake, utilize the libresvg for parsing
41  // SVG files and rasterizing them to QImages.
42  #include "ResvgQt.h"
43 #endif
44 
45 using namespace openshot;
46 
47 QtImageReader::QtImageReader(std::string path) : path{QString::fromStdString(path)}, is_open(false)
48 {
49  // Open and Close the reader, to populate its attributes (such as height, width, etc...)
50  Open();
51  Close();
52 }
53 
54 QtImageReader::QtImageReader(std::string path, bool inspect_reader) : path{QString::fromStdString(path)}, is_open(false)
55 {
56  // Open and Close the reader, to populate its attributes (such as height, width, etc...)
57  if (inspect_reader) {
58  Open();
59  Close();
60  }
61 }
62 
64 {
65 }
66 
67 // Open image file
69 {
70  // Open reader if not already open
71  if (!is_open)
72  {
73  bool success = true;
74  bool loaded = false;
75 
76 #if USE_RESVG == 1
77  // If defined and found in CMake, utilize the libresvg for parsing
78  // SVG files and rasterizing them to QImages.
79  // Only use resvg for files ending in '.svg' or '.svgz'
80  if (path.toLower().endsWith(".svg") || path.toLower().endsWith(".svgz")) {
81 
82  ResvgRenderer renderer(path);
83  if (renderer.isValid()) {
84 
85  image = std::shared_ptr<QImage>(new QImage(renderer.defaultSize(), QImage::Format_ARGB32_Premultiplied));
86  image->fill(Qt::transparent);
87 
88  QPainter p(image.get());
89  renderer.render(&p);
90  p.end();
91  loaded = true;
92  }
93  }
94 #endif
95 
96  if (!loaded) {
97  // Attempt to open file using Qt's build in image processing capabilities
98  image = std::shared_ptr<QImage>(new QImage());
99  success = image->load(path);
100  }
101 
102  if (!success) {
103  // raise exception
104  throw InvalidFile("File could not be opened.", path.toStdString());
105  }
106 
107  // Convert to proper format
108  image = std::shared_ptr<QImage>(new QImage(image->convertToFormat(QImage::Format_RGBA8888)));
109 
110  // Update image properties
111  info.has_audio = false;
112  info.has_video = true;
113  info.has_single_image = true;
114  info.file_size = image->byteCount();
115  info.vcodec = "QImage";
116  info.width = image->width();
117  info.height = image->height();
118  info.pixel_ratio.num = 1;
119  info.pixel_ratio.den = 1;
120  info.duration = 60 * 60 * 1; // 1 hour duration
121  info.fps.num = 30;
122  info.fps.den = 1;
123  info.video_timebase.num = 1;
124  info.video_timebase.den = 30;
126 
127  // Calculate the DAR (display aspect ratio)
129 
130  // Reduce size fraction
131  size.Reduce();
132 
133  // Set the ratio based on the reduced fraction
134  info.display_ratio.num = size.num;
135  info.display_ratio.den = size.den;
136 
137  // Set current max size
138  max_size.setWidth(info.width);
139  max_size.setHeight(info.height);
140 
141  // Mark as "open"
142  is_open = true;
143  }
144 }
145 
146 // Close image file
148 {
149  // Close all objects, if reader is 'open'
150  if (is_open)
151  {
152  // Mark as "closed"
153  is_open = false;
154 
155  // Delete the image
156  image.reset();
157 
158  info.vcodec = "";
159  info.acodec = "";
160  }
161 }
162 
163 // Get an openshot::Frame object for a specific frame number of this reader.
164 std::shared_ptr<Frame> QtImageReader::GetFrame(int64_t requested_frame)
165 {
166  // Check for open reader (or throw exception)
167  if (!is_open)
168  throw ReaderClosed("The Image is closed. Call Open() before calling this method.", path.toStdString());
169 
170  // Create a scoped lock, allowing only a single thread to run the following code at one time
171  const GenericScopedLock<CriticalSection> lock(getFrameCriticalSection);
172 
173  // Determine the max size of this source image (based on the timeline's size, the scaling mode,
174  // and the scaling keyframes). This is a performance improvement, to keep the images as small as possible,
175  // without losing quality. NOTE: We cannot go smaller than the timeline itself, or the add_layer timeline
176  // method will scale it back to timeline size before scaling it smaller again. This needs to be fixed in
177  // the future.
178  int max_width = Settings::Instance()->MAX_WIDTH;
179  if (max_width <= 0)
180  max_width = info.width;
181  int max_height = Settings::Instance()->MAX_HEIGHT;
182  if (max_height <= 0)
183  max_height = info.height;
184 
185  Clip* parent = (Clip*) GetClip();
186  if (parent) {
187  if (parent->scale == SCALE_FIT || parent->scale == SCALE_STRETCH) {
188  // Best fit or Stretch scaling (based on max timeline size * scaling keyframes)
189  float max_scale_x = parent->scale_x.GetMaxPoint().co.Y;
190  float max_scale_y = parent->scale_y.GetMaxPoint().co.Y;
191  max_width = std::max(float(max_width), max_width * max_scale_x);
192  max_height = std::max(float(max_height), max_height * max_scale_y);
193 
194  } else if (parent->scale == SCALE_CROP) {
195  // Cropping scale mode (based on max timeline size * cropped size * scaling keyframes)
196  float max_scale_x = parent->scale_x.GetMaxPoint().co.Y;
197  float max_scale_y = parent->scale_y.GetMaxPoint().co.Y;
198  QSize width_size(max_width * max_scale_x,
199  round(max_width / (float(info.width) / float(info.height))));
200  QSize height_size(round(max_height / (float(info.height) / float(info.width))),
201  max_height * max_scale_y);
202  // respect aspect ratio
203  if (width_size.width() >= max_width && width_size.height() >= max_height) {
204  max_width = std::max(max_width, width_size.width());
205  max_height = std::max(max_height, width_size.height());
206  }
207  else {
208  max_width = std::max(max_width, height_size.width());
209  max_height = std::max(max_height, height_size.height());
210  }
211 
212  } else {
213  // No scaling, use original image size (slower)
214  max_width = info.width;
215  max_height = info.height;
216  }
217  }
218 
219  // Scale image smaller (or use a previous scaled image)
220  if (!cached_image || (max_size.width() != max_width || max_size.height() != max_height)) {
221 
222  bool rendered = false;
223 #if USE_RESVG == 1
224  // If defined and found in CMake, utilize the libresvg for parsing
225  // SVG files and rasterizing them to QImages.
226  // Only use resvg for files ending in '.svg' or '.svgz'
227  if (path.toLower().endsWith(".svg") || path.toLower().endsWith(".svgz")) {
228 
229  ResvgRenderer renderer(path);
230  if (renderer.isValid()) {
231  // Scale SVG size to keep aspect ratio, and fill the max_size as best as possible
232  QSize svg_size(renderer.defaultSize().width(), renderer.defaultSize().height());
233  svg_size.scale(max_width, max_height, Qt::KeepAspectRatio);
234 
235  // Create empty QImage
236  cached_image = std::shared_ptr<QImage>(new QImage(QSize(svg_size.width(), svg_size.height()), QImage::Format_ARGB32_Premultiplied));
237  cached_image->fill(Qt::transparent);
238 
239  // Render SVG into QImage
240  QPainter p(cached_image.get());
241  renderer.render(&p);
242  p.end();
243  rendered = true;
244  }
245  }
246 #endif
247 
248  if (!rendered) {
249  // We need to resize the original image to a smaller image (for performance reasons)
250  // Only do this once, to prevent tons of unneeded scaling operations
251  cached_image = std::shared_ptr<QImage>(new QImage(image->scaled(max_width, max_height, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
252  }
253 
254  cached_image = std::shared_ptr<QImage>(new QImage(cached_image->convertToFormat(QImage::Format_RGBA8888)));
255 
256  // Set max size (to later determine if max_size is changed)
257  max_size.setWidth(max_width);
258  max_size.setHeight(max_height);
259  }
260 
261  // Create or get frame object
262  std::shared_ptr<Frame> image_frame(new Frame(requested_frame, cached_image->width(), cached_image->height(), "#000000", Frame::GetSamplesPerFrame(requested_frame, info.fps, info.sample_rate, info.channels), info.channels));
263 
264  // Add Image data to frame
265  image_frame->AddImage(cached_image);
266 
267  // return frame object
268  return image_frame;
269 }
270 
271 // Generate JSON string of this object
272 std::string QtImageReader::Json() {
273 
274  // Return formatted string
275  return JsonValue().toStyledString();
276 }
277 
278 // Generate Json::JsonValue for this object
280 
281  // Create root json object
282  Json::Value root = ReaderBase::JsonValue(); // get parent properties
283  root["type"] = "QtImageReader";
284  root["path"] = path.toStdString();
285 
286  // return JsonValue
287  return root;
288 }
289 
290 // Load JSON string into this object
291 void QtImageReader::SetJson(std::string value) {
292 
293  // Parse JSON string into JSON objects
294  Json::Value root;
295  Json::CharReaderBuilder rbuilder;
296  Json::CharReader* reader(rbuilder.newCharReader());
297 
298  std::string errors;
299  bool success = reader->parse( value.c_str(),
300  value.c_str() + value.size(), &root, &errors );
301  delete reader;
302 
303  if (!success)
304  // Raise exception
305  throw InvalidJSON("JSON could not be parsed (or is invalid)");
306 
307  try
308  {
309  // Set all values that match
310  SetJsonValue(root);
311  }
312  catch (const std::exception& e)
313  {
314  // Error parsing JSON (or missing keys)
315  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
316  }
317 }
318 
319 // Load Json::JsonValue into this object
320 void QtImageReader::SetJsonValue(Json::Value root) {
321 
322  // Set parent data
324 
325  // Set data from Json (if key is found)
326  if (!root["path"].isNull())
327  path = QString::fromStdString(root["path"].asString());
328 
329  // Re-Open path, and re-init everything (if needed)
330  if (is_open)
331  {
332  Close();
333  Open();
334  }
335 }
Json::Value JsonValue()
Generate Json::JsonValue for this object.
int MAX_HEIGHT
Maximum height for image data (useful for optimzing for a smaller preview or render) ...
Definition: Settings.h:101
int num
Numerator for the fraction.
Definition: Fraction.h:47
void Open()
Open File - which is called by the constructor automatically.
Point GetMaxPoint() const
Get max point (by Y coordinate)
Definition: KeyFrame.cpp:249
int width
The width of the video (in pixesl)
Definition: ReaderBase.h:68
This class represents a single frame of video (i.e. image & audio data)
Definition: Frame.h:106
float duration
Length of time (in seconds)
Definition: ReaderBase.h:65
void SetJson(std::string value)
Load JSON string into this object.
void SetJsonValue(Json::Value root)
Load Json::JsonValue into this object.
Scale the clip until both height and width fill the canvas (cropping the overlap) ...
Definition: Enums.h:54
openshot::Keyframe scale_x
Curve representing the horizontal scaling in percent (0 to 1)
Definition: Clip.h:212
openshot::Keyframe scale_y
Curve representing the vertical scaling in percent (0 to 1)
Definition: Clip.h:213
Exception when a reader is closed, and a frame is requested.
Definition: Exceptions.h:337
bool has_video
Determines if this file has a video stream.
Definition: ReaderBase.h:62
int64_t file_size
Size of file (in bytes)
Definition: ReaderBase.h:66
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:547
bool has_audio
Determines if this file has an audio stream.
Definition: ReaderBase.h:63
This class represents a clip (used to arrange readers on the timeline)
Definition: Clip.h:95
double Y
The Y value of the coordinate (usually representing the value of the property being animated) ...
Definition: Coordinate.h:58
int MAX_WIDTH
Maximum width for image data (useful for optimzing for a smaller preview or render) ...
Definition: Settings.h:98
int64_t video_length
The number of frames in the video stream.
Definition: ReaderBase.h:75
int height
The height of the video (in pixels)
Definition: ReaderBase.h:67
openshot::Fraction video_timebase
The video timebase determines how long each frame stays on the screen.
Definition: ReaderBase.h:77
Exception for files that can not be found or opened.
Definition: Exceptions.h:173
This class represents a fraction.
Definition: Fraction.h:45
void Close()
Close File.
juce::CriticalSection getFrameCriticalSection
Section lock for multiple threads.
Definition: ReaderBase.h:101
bool has_single_image
Determines if this file only contains a single image.
Definition: ReaderBase.h:64
virtual Json::Value JsonValue()=0
Generate Json::JsonValue for this object.
Definition: ReaderBase.cpp:116
QtImageReader(std::string path)
Current max_size as calculated with Clip properties
openshot::ClipBase * parent
Definition: ReaderBase.h:103
virtual void SetJsonValue(Json::Value root)=0
Load Json::JsonValue into this object.
Definition: ReaderBase.cpp:170
Scale the clip until both height and width fill the canvas (distort to fit)
Definition: Enums.h:56
openshot::ReaderInfo info
Information about the current media file.
Definition: ReaderBase.h:111
std::string vcodec
The name of the video codec used to encode / decode the video stream.
Definition: ReaderBase.h:74
This namespace is the default namespace for all code in the openshot library.
openshot::ClipBase * GetClip()
Parent clip object of this reader (which can be unparented and NULL)
Definition: ReaderBase.cpp:253
Coordinate co
This is the primary coordinate.
Definition: Point.h:84
Exception for invalid JSON.
Definition: Exceptions.h:205
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
openshot::ScaleType scale
The scale determines how a clip should be resized to fit its parent.
Definition: Clip.h:144
std::shared_ptr< Frame > GetFrame(int64_t requested_frame)
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method) ...
Definition: Settings.cpp:41
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
std::string Json()
Get and Set JSON methods.
int den
Denominator for the fraction.
Definition: Fraction.h:48
int channels
The number of audio channels used in the audio stream.
Definition: ReaderBase.h:83
Scale the clip until either height or width fills the canvas (with no cropping)
Definition: Enums.h:55
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition: ReaderBase.h:70
std::string acodec
The name of the audio codec used to encode / decode the video stream.
Definition: ReaderBase.h:80
double ToDouble()
Return this fraction as a double (i.e. 1/2 = 0.5)
Definition: Fraction.cpp:49
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition: ReaderBase.h:82