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