OpenShot Library | libopenshot 0.3.3
Loading...
Searching...
No Matches
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
26using namespace openshot;
27
28QtImageReader::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
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;
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;
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.
148std::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
192QSize 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
253QSize 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
320std::string QtImageReader::Json() const {
321
322 // Return formatted string
323 return JsonValue().toStyledString();
324}
325
326// Generate Json::Value for this object
327Json::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
339void 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
356void 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}
Header file for CacheMemory class.
Header file for Clip class.
Header file for all Exception classes.
Header file for QtImageReader class.
Header file for Timeline class.
This class represents a clip (used to arrange readers on the timeline)
Definition Clip.h:89
openshot::Keyframe scale_x
Curve representing the horizontal scaling in percent (0 to 1)
Definition Clip.h:306
openshot::TimelineBase * ParentTimeline() override
Get the associated Timeline pointer (if any)
Definition Clip.h:284
openshot::Keyframe scale_y
Curve representing the vertical scaling in percent (0 to 1)
Definition Clip.h:307
openshot::ScaleType scale
The scale determines how a clip should be resized to fit its parent.
Definition Clip.h:168
double Y
The Y value of the coordinate (usually representing the value of the property being animated)
Definition Coordinate.h:41
This class represents a fraction.
Definition Fraction.h:30
int num
Numerator for the fraction.
Definition Fraction.h:32
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
Definition Fraction.cpp:40
void Reduce()
Reduce this fraction (i.e. 640/480 = 4/3)
Definition Fraction.cpp:65
int den
Denominator for the fraction.
Definition Fraction.h:33
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
Exception for files that can not be found or opened.
Definition Exceptions.h:188
Exception for invalid JSON.
Definition Exceptions.h:218
Point GetMaxPoint() const
Get max point (by Y coordinate)
Definition KeyFrame.cpp:245
Coordinate co
This is the primary coordinate.
Definition Point.h:66
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
openshot::ReaderInfo info
Information about the current media file.
Definition ReaderBase.h:88
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
std::recursive_mutex getFrameMutex
Mutex for multiple threads.
Definition ReaderBase.h:79
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL)
Exception when a reader is closed, and a frame is requested.
Definition Exceptions.h:364
int preview_height
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
int preview_width
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
This class represents a timeline.
Definition Timeline.h:148
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
@ SCALE_FIT
Scale the clip until either height or width fills the canvas (with no cropping)
Definition Enums.h:38
@ SCALE_STRETCH
Scale the clip until both height and width fill the canvas (distort to fit)
Definition Enums.h:39
@ SCALE_CROP
Scale the clip until both height and width fill the canvas (cropping the overlap)
Definition Enums.h:37
@ SCALE_NONE
Do not scale the clip.
Definition Enums.h:40
const Json::Value stringToJson(const std::string value)
Definition Json.cpp:16
bool has_single_image
Determines if this file only contains a single image.
Definition ReaderBase.h:42
float duration
Length of time (in seconds)
Definition ReaderBase.h:43
int width
The width of the video (in pixesl)
Definition ReaderBase.h:46
int channels
The number of audio channels used in the audio stream.
Definition ReaderBase.h:61
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition ReaderBase.h:48
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
int height
The height of the video (in pixels)
Definition ReaderBase.h:45
int64_t video_length
The number of frames in the video stream.
Definition ReaderBase.h:53
std::string acodec
The name of the audio codec used to encode / decode the video stream.
Definition ReaderBase.h:58
std::string vcodec
The name of the video codec used to encode / decode the video stream.
Definition ReaderBase.h:52
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
bool has_video
Determines if this file has a video stream.
Definition ReaderBase.h:40
bool has_audio
Determines if this file has an audio stream.
Definition ReaderBase.h:41
openshot::Fraction video_timebase
The video timebase determines how long each frame stays on the screen.
Definition ReaderBase.h:55
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition ReaderBase.h:60
int64_t file_size
Size of file (in bytes)
Definition ReaderBase.h:44