From 5da5adc3f8dff52ead31f4462283925a1759e660 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Tue, 2 Nov 2021 09:12:23 -0400 Subject: [PATCH 1/5] Move ImageMagick code out of Frame class --- src/CMakeLists.txt | 11 +++-- src/Frame.cpp | 85 +++----------------------------------- src/Frame.h | 26 ++---------- src/ImageReader.cpp | 82 +++++++++++++++++-------------------- src/ImageReader.h | 24 +++++------ src/ImageWriter.cpp | 91 ++++++++++++++++++++++------------------- src/ImageWriter.h | 26 ++++++------ src/MagickUtilities.cpp | 73 +++++++++++++++++++++++++++++++++ src/MagickUtilities.h | 23 +++++++++-- src/QtUtilities.h | 13 ++++++ src/TextReader.cpp | 10 +++-- src/TextReader.h | 15 +++---- 12 files changed, 246 insertions(+), 233 deletions(-) create mode 100644 src/MagickUtilities.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 115f975a..70797f0d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,9 +70,7 @@ set(OPENSHOT_SOURCES FrameMapper.cpp Json.cpp KeyFrame.cpp - TrackedObjectBase.cpp OpenShotVersion.cpp - ZmqLogger.cpp PlayerBase.cpp Point.cpp Profiles.cpp @@ -82,7 +80,10 @@ set(OPENSHOT_SOURCES QtTextReader.cpp Settings.cpp TimelineBase.cpp - Timeline.cpp) + Timeline.cpp + TrackedObjectBase.cpp + ZmqLogger.cpp + ) # OpenCV related classes set(OPENSHOT_CV_SOURCES @@ -216,9 +217,11 @@ if (ENABLE_MAGICK) # Add optional ImageMagic-dependent sources target_sources(openshot PRIVATE + MagickUtilities.cpp ImageReader.cpp ImageWriter.cpp - TextReader.cpp) + TextReader.cpp + ) # define a preprocessor macro (used in the C++ source) target_compile_definitions(openshot PUBLIC USE_IMAGEMAGICK=1) diff --git a/src/Frame.cpp b/src/Frame.cpp index 155ba2b4..b95a83cf 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -16,6 +16,8 @@ #include #include "Frame.h" +#include "QtUtilities.h" + #include #include @@ -32,10 +34,6 @@ #include #include -#ifdef USE_IMAGEMAGICK - #include "MagickUtilities.h" -#endif - using namespace std; using namespace openshot; @@ -43,7 +41,7 @@ using namespace openshot; Frame::Frame(int64_t number, int width, int height, std::string color, int samples, int channels) : audio(std::make_shared(channels, samples)), number(number), width(width), height(height), - pixel_ratio(1,1), color(color), qbuffer(NULL), + pixel_ratio(1,1), color(color), channels(channels), channel_layout(LAYOUT_STEREO), sample_rate(44100), has_audio_data(false), has_image_data(false), @@ -749,20 +747,14 @@ void Frame::AddImage( int new_width, int new_height, int bytes_per_pixel, QImage::Format type, const unsigned char *pixels_) { - // Create new buffer - { - const juce::GenericScopedLock lock(addingImageSection); - qbuffer = pixels_; - } // Release addingImageSection lock - // Create new image object from pixel data auto new_image = std::make_shared( - qbuffer, + pixels_, new_width, new_height, new_width * bytes_per_pixel, type, - (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer, - (void*) qbuffer + (QImageCleanupFunction) &openshot::cleanUpBuffer, + (void*) pixels_ ); AddImage(new_image); } @@ -948,60 +940,6 @@ void Frame::SetImageCV(cv::Mat _image) } #endif -#ifdef USE_IMAGEMAGICK -// Get pointer to ImageMagick image object -std::shared_ptr Frame::GetMagickImage() -{ - // Check for blank image - if (!image) - // Fill with black - AddColor(width, height, "#000000"); - - // Get the pixels from the frame image - const QRgb *tmpBits = (const QRgb*)image->constBits(); - - // Create new image object, and fill with pixel data - auto magick_image = std::make_shared( - image->width(), image->height(),"RGBA", Magick::CharPixel, tmpBits); - - // Give image a transparent background color - magick_image->backgroundColor(Magick::Color("none")); - magick_image->virtualPixelMethod(Magick::TransparentVirtualPixelMethod); - MAGICK_IMAGE_ALPHA(magick_image, true); - - return magick_image; -} -#endif - -#ifdef USE_IMAGEMAGICK -// Get pointer to QImage of frame -void Frame::AddMagickImage(std::shared_ptr new_image) -{ - const int BPP = 4; - const std::size_t bufferSize = new_image->columns() * new_image->rows() * BPP; - - /// Use realloc for fast memory allocation. - /// TODO: consider locking the buffer for mt safety - //qbuffer = reinterpret_cast(realloc(qbuffer, bufferSize)); - qbuffer = new unsigned char[bufferSize](); - unsigned char *buffer = (unsigned char*)qbuffer; - - MagickCore::ExceptionInfo exception; - // TODO: Actually do something, if we get an exception here - MagickCore::ExportImagePixels(new_image->constImage(), 0, 0, new_image->columns(), new_image->rows(), "RGBA", Magick::CharPixel, buffer, &exception); - - // Create QImage of frame data - image = std::make_shared( - qbuffer, width, height, width * BPP, QImage::Format_RGBA8888_Premultiplied, - (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer); - - // Update height and width - width = image->width(); - height = image->height(); - has_image_data = true; -} -#endif - // Play audio samples for this frame void Frame::Play() { @@ -1073,17 +1011,6 @@ void Frame::Play() } -// Clean up buffer after QImage is deleted -void Frame::cleanUpBuffer(void *info) -{ - if (info) - { - // Remove buffer since QImage tells us to - unsigned char* ptr_to_qbuffer = (unsigned char*) info; - delete[] ptr_to_qbuffer; - } -} - // Add audio silence void Frame::AddAudioSilence(int numSamples) { diff --git a/src/Frame.h b/src/Frame.h index fdf78239..ac917eff 100644 --- a/src/Frame.h +++ b/src/Frame.h @@ -34,13 +34,6 @@ #include #include -#ifdef USE_IMAGEMAGICK -// Forward declare Magick::Image -namespace Magick { - class Image; -} -#endif - class QApplication; namespace openshot @@ -98,8 +91,7 @@ namespace openshot std::shared_ptr previewApp; juce::CriticalSection addingImageSection; - juce::CriticalSection addingAudioSection; - const unsigned char *qbuffer; + juce::CriticalSection addingAudioSection; openshot::Fraction pixel_ratio; int channels; ChannelLayout channel_layout; @@ -146,7 +138,8 @@ namespace openshot /// Add (or replace) pixel data to the frame (based on a solid color) void AddColor(int new_width, int new_height, std::string new_color); - /// Add (or replace) pixel data (filled with new_color) + + /// Add (or replace) pixel data (filled with new_color) void AddColor(const QColor& new_color); /// Add (or replace) pixel data to the frame @@ -158,11 +151,6 @@ namespace openshot /// Add (or replace) pixel data to the frame (for only the odd or even lines) void AddImage(std::shared_ptr new_image, bool only_odd_lines); -#ifdef USE_IMAGEMAGICK - /// Add (or replace) pixel data to the frame from an ImageMagick Image - void AddMagickImage(std::shared_ptr new_image); -#endif - /// Add audio samples to a specific channel void AddAudio(bool replaceSamples, int destChannel, int destStartSample, const float* source, int numSamples, float gainToApplyToSource); @@ -179,9 +167,6 @@ namespace openshot // Set the channel layout of audio samples (i.e. mono, stereo, 5 point surround, etc...) void ChannelsLayout(openshot::ChannelLayout new_channel_layout) { channel_layout = new_channel_layout; }; - /// Clean up buffer after QImage is deleted - static void cleanUpBuffer(void *info); - /// Clear the waveform image (and deallocate its memory) void ClearWaveform(); @@ -220,11 +205,6 @@ namespace openshot /// Get pointer to Qt QImage image object std::shared_ptr GetImage(); -#ifdef USE_IMAGEMAGICK - /// Get pointer to ImageMagick image object - std::shared_ptr GetMagickImage(); -#endif - /// Set Pixel Aspect Ratio openshot::Fraction GetPixelRatio() { return pixel_ratio; }; diff --git a/src/ImageReader.cpp b/src/ImageReader.cpp index 6f6cbe04..1240a7f6 100644 --- a/src/ImageReader.cpp +++ b/src/ImageReader.cpp @@ -13,6 +13,9 @@ // Require ImageMagick support #ifdef USE_IMAGEMAGICK +#include "MagickUtilities.h" +#include "QtUtilities.h" + #include "ImageReader.h" #include "Exceptions.h" #include "Frame.h" @@ -57,39 +60,31 @@ void ImageReader::Open() info.vcodec = image->format(); info.width = image->size().width(); info.height = image->size().height(); - info.pixel_ratio.num = 1; - info.pixel_ratio.den = 1; + info.pixel_ratio = openshot::Fraction(1, 1); info.duration = 60 * 60 * 1; // 1 hour duration - info.fps.num = 30; - info.fps.den = 1; - info.video_timebase.num = 1; - info.video_timebase.den = 30; - info.video_length = round(info.duration * info.fps.ToDouble()); + info.fps = openshot::Fraction(30, 1); + info.video_timebase = info.fps.Reciprocal(); + info.video_length = std::round(info.duration * info.fps.ToDouble()); // Calculate the DAR (display aspect ratio) - Fraction size(info.width * info.pixel_ratio.num, info.height * info.pixel_ratio.den); + Fraction dar( + info.width * info.pixel_ratio.num, + info.height * info.pixel_ratio.den); - // Reduce size fraction - size.Reduce(); - - // Set the ratio based on the reduced fraction - info.display_ratio.num = size.num; - info.display_ratio.den = size.den; + // Reduce DAR fraction & set ratio + dar.Reduce(); + info.display_ratio = dar; // Mark as "open" is_open = true; } } -// Close image file void ImageReader::Close() { - // Close all objects, if reader is 'open' if (is_open) { - // Mark as "closed" is_open = false; - // Delete the image image.reset(); } @@ -98,19 +93,21 @@ void ImageReader::Close() // Get an openshot::Frame object for a specific frame number of this reader. std::shared_ptr ImageReader::GetFrame(int64_t requested_frame) { - // Check for open reader (or throw exception) - if (!is_open) - throw ReaderClosed("The FFmpegReader is closed. Call Open() before calling this method.", path); + if (!is_open) { + throw ReaderClosed( + "The ImageReader is closed. " + "Call Open() before calling this method.", path); + } // Create or get frame object auto image_frame = std::make_shared( - requested_frame, image->size().width(), image->size().height(), + requested_frame, + image->size().width(), image->size().height(), "#000000", 0, 2); // Add Image data to frame - image_frame->AddMagickImage(image); - - // return frame object + auto qimage = openshot::Magick2QImage(image); + image_frame->AddImage(qimage); return image_frame; } @@ -124,30 +121,29 @@ std::string ImageReader::Json() const { // Generate Json::Value for this object Json::Value ImageReader::JsonValue() const { - // Create root json object - Json::Value root = ReaderBase::JsonValue(); // get parent properties + // get parent properties + Json::Value root = ReaderBase::JsonValue(); + root["type"] = "ImageReader"; root["path"] = path; - - // return JsonValue return root; } // Load JSON string into this object void ImageReader::SetJson(const std::string value) { - // Parse JSON string into JSON objects - try - { - const Json::Value root = openshot::stringToJson(value); - // Set all values that match - SetJsonValue(root); - } - catch (const std::exception& e) - { - // Error parsing JSON (or missing keys) - throw InvalidJSON("JSON is invalid (missing keys or invalid data types)"); - } + // Parse JSON string into JSON objects + try + { + const Json::Value root = openshot::stringToJson(value); + // Set all values that match + SetJsonValue(root); + } + catch (const std::exception& e) + { + throw InvalidJSON( + "JSON is invalid (missing keys or invalid data types)"); + } } // Load Json::Value into this object @@ -160,9 +156,7 @@ void ImageReader::SetJsonValue(const Json::Value root) { if (!root["path"].isNull()) path = root["path"].asString(); - // Re-Open path, and re-init everything (if needed) - if (is_open) - { + if (is_open) { Close(); Open(); } diff --git a/src/ImageReader.h b/src/ImageReader.h index d123f33b..897a051f 100644 --- a/src/ImageReader.h +++ b/src/ImageReader.h @@ -16,23 +16,23 @@ // Require ImageMagick support #ifdef USE_IMAGEMAGICK -#include "ReaderBase.h" - -#include -#include -#include -#include -#include #include -#include "CacheMemory.h" +#include -#include "MagickUtilities.h" +#include "ReaderBase.h" +#include "Json.h" + +// Forward decls +namespace Magick { + class Image; +} +namespace openshot { + class CacheBase; + class Frame; +} namespace openshot { - // Forward decls - class CacheBase; - /** * @brief This class uses the ImageMagick++ libraries, to open image files, and return * openshot::Frame objects containing the image. diff --git a/src/ImageWriter.cpp b/src/ImageWriter.cpp index eb98d7e7..a9aabd09 100644 --- a/src/ImageWriter.cpp +++ b/src/ImageWriter.cpp @@ -13,8 +13,12 @@ //Require ImageMagick support #ifdef USE_IMAGEMAGICK -#include "ImageWriter.h" +#include "MagickUtilities.h" +#include "QtUtilities.h" #include "Exceptions.h" +#include "Frame.h" + +#include "ImageWriter.h" using namespace openshot; @@ -22,47 +26,51 @@ ImageWriter::ImageWriter(std::string path) : path(path), cache_size(8), write_video_count(0), image_quality(75), number_of_loops(1), combine_frames(true), is_open(false) { - // Disable audio & video (so they can be independently enabled) info.has_audio = false; info.has_video = true; } // Set video export options -void ImageWriter::SetVideoOptions(std::string format, Fraction fps, int width, int height, - int quality, int loops, bool combine) +void ImageWriter::SetVideoOptions( + std::string format, Fraction fps, int width, int height, + int quality, int loops, bool combine) { - // Set frames per second (if provided) - info.fps.num = fps.num; - info.fps.den = fps.den; + // Set frames per second (if provided) + info.fps = fps; - // Set image magic properties - image_quality = quality; - number_of_loops = loops; - combine_frames = combine; - info.vcodec = format; + // Set image magic properties + image_quality = quality; + number_of_loops = loops; + combine_frames = combine; + info.vcodec = format; - // Set the timebase (inverse of fps) - info.video_timebase.num = info.fps.den; - info.video_timebase.den = info.fps.num; + // Set the timebase (inverse of fps) + info.video_timebase = fps.Reciprocal(); - if (width >= 1) - info.width = width; - if (height >= 1) - info.height = height; + info.width = std::max(1, width); + info.height = std::max(1, height); - info.video_bit_rate = quality; + info.video_bit_rate = quality; - // Calculate the DAR (display aspect ratio) - Fraction size(info.width * info.pixel_ratio.num, info.height * info.pixel_ratio.den); + // Calculate the DAR (display aspect ratio) + Fraction size( + info.width * info.pixel_ratio.num, + info.height * info.pixel_ratio.den); - // Reduce size fraction - size.Reduce(); + // Reduce size fraction + size.Reduce(); - // Set the ratio based on the reduced fraction - info.display_ratio.num = size.num; - info.display_ratio.den = size.den; + // Set the ratio based on the reduced fraction + info.display_ratio = size; - ZmqLogger::Instance()->AppendDebugMethod("ImageWriter::SetVideoOptions (" + format + ")", "width", width, "height", height, "size.num", size.num, "size.den", size.den, "fps.num", fps.num, "fps.den", fps.den); + ZmqLogger::Instance()->AppendDebugMethod( + "ImageWriter::SetVideoOptions (" + format + ")", + "width", width, + "height", height, + "size.num", size.num, + "size.den", size.den, + "fps.num", fps.num, + "fps.den", fps.den); } // Open the writer @@ -75,12 +83,15 @@ void ImageWriter::Open() void ImageWriter::WriteFrame(std::shared_ptr frame) { // Check for open reader (or throw exception) - if (!is_open) - throw WriterClosed("The ImageWriter is closed. Call Open() before calling this method.", path); - + if (!is_open) { + throw WriterClosed( + "The ImageWriter is closed. " + "Call Open() before calling this method.", path); + } // Copy and resize image - std::shared_ptr frame_image = frame->GetMagickImage(); + auto qimage = frame->GetImage(); + auto frame_image = openshot::QImage2Magick(qimage); frame_image->magick( info.vcodec ); frame_image->backgroundColor(Magick::Color("none")); MAGICK_IMAGE_ALPHA(frame_image, true); @@ -89,11 +100,10 @@ void ImageWriter::WriteFrame(std::shared_ptr frame) frame_image->animationIterations(number_of_loops); // Calculate correct DAR (display aspect ratio) - int new_width = info.width; int new_height = info.height * frame->GetPixelRatio().Reciprocal().ToDouble(); // Resize image - Magick::Geometry new_size(new_width, new_height); + Magick::Geometry new_size(info.width, new_height); new_size.aspect(true); frame_image->resize(new_size); @@ -108,7 +118,10 @@ void ImageWriter::WriteFrame(std::shared_ptr frame) // Write a block of frames from a reader void ImageWriter::WriteFrame(ReaderBase* reader, int64_t start, int64_t length) { - ZmqLogger::Instance()->AppendDebugMethod("ImageWriter::WriteFrame (from Reader)", "start", start, "length", length); + ZmqLogger::Instance()->AppendDebugMethod( + "ImageWriter::WriteFrame (from Reader)", + "start", start, + "length", length); // Loop through each frame (and encoded it) for (int64_t number = start; number <= length; number++) @@ -124,16 +137,12 @@ void ImageWriter::WriteFrame(ReaderBase* reader, int64_t start, int64_t length) // Close the writer and encode/output final image to the disk. void ImageWriter::Close() { - // Write frame's image to file + // Write frame images to file Magick::writeImages(frames.begin(), frames.end(), path, combine_frames); - // Clear frames vector + // Clear frames vector & counters, close writer frames.clear(); - - // Reset frame counters write_video_count = 0; - - // Close writer is_open = false; ZmqLogger::Instance()->AppendDebugMethod("ImageWriter::Close"); diff --git a/src/ImageWriter.h b/src/ImageWriter.h index dd2d2b8a..6ed0af10 100644 --- a/src/ImageWriter.h +++ b/src/ImageWriter.h @@ -15,20 +15,19 @@ #ifdef USE_IMAGEMAGICK -#include "ReaderBase.h" -#include "WriterBase.h" - #include #include -#include "CacheMemory.h" -#include "Exceptions.h" -#include "Fraction.h" -#include "OpenMPUtilities.h" +#include "WriterBase.h" #include "MagickUtilities.h" +#include "Fraction.h" + namespace openshot { + // Forward decls + class Frame; + class ReaderBase; /** * @brief This class uses the ImageMagick library to write image files (including animated GIFs) @@ -69,7 +68,7 @@ namespace openshot int number_of_loops; bool combine_frames; - std::shared_ptr last_frame; + std::shared_ptr last_frame; public: @@ -102,8 +101,9 @@ namespace openshot /// @param quality Quality of image (0 to 100, 70 is default) /// @param loops Number of times to repeat the image (used on certain multi-frame image formats, such as GIF) /// @param combine Combine frames into a single image (if possible), or save each frame as its own image - void SetVideoOptions(std::string format, Fraction fps, int width, int height, - int quality, int loops, bool combine); + void SetVideoOptions( + std::string format, Fraction fps, int width, int height, + int quality, int loops, bool combine); /// @brief Add a frame to the stack waiting to be encoded. /// @param frame The openshot::Frame object to write to this image @@ -117,7 +117,7 @@ namespace openshot }; -} +} // namespace -#endif //USE_IMAGEMAGICK -#endif //OPENSHOT_IMAGE_WRITER_H +#endif //USE_IMAGEMAGICK +#endif //OPENSHOT_IMAGE_WRITER_H diff --git a/src/MagickUtilities.cpp b/src/MagickUtilities.cpp new file mode 100644 index 00000000..aa22ec1f --- /dev/null +++ b/src/MagickUtilities.cpp @@ -0,0 +1,73 @@ +/** + * @file + * @brief Implementation for MagickUtilities (conversions) + * @author Jonathan Thomas + * @author FeRD (Frank Dana) + */ + +// Copyright (c) 2008-2021 OpenShot Studios, LLC +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifdef USE_IMAGEMAGICK + +#include "MagickUtilities.h" +#include "QtUtilities.h" + +#include + +// Get pointer to Magick::Image conversion of a QImage +std::shared_ptr +openshot::QImage2Magick(std::shared_ptr image) +{ + // Check for blank image + if (!image || image->isNull()) + return nullptr; + + // Get the pixels from the frame image + const QRgb *tmpBits = (const QRgb*)image->constBits(); + + // Create new image object, and fill with pixel data + auto magick_image = std::make_shared( + image->width(), image->height(), + "RGBA", Magick::CharPixel, tmpBits); + + // Give image a transparent background color + magick_image->backgroundColor(Magick::Color("none")); + magick_image->virtualPixelMethod( + Magick::TransparentVirtualPixelMethod); + MAGICK_IMAGE_ALPHA(magick_image, true); + + return magick_image; +} + +// Get pointer to QImage conversion of a Magick::Image +std::shared_ptr +openshot::Magick2QImage(std::shared_ptr image) +{ + if (!image) + return nullptr; + + const int BPP = 4; + const std::size_t size = image->columns() * image->rows() * BPP; + + auto* qbuffer = new unsigned char[size](); + + MagickCore::ExceptionInfo exception; + // TODO: Actually do something, if we get an exception here + MagickCore::ExportImagePixels( + image->constImage(), 0, 0, + image->columns(), image->rows(), + "RGBA", Magick::CharPixel, + qbuffer, &exception); + + auto qimage = std::make_shared( + qbuffer, image->columns(), image->rows(), + image->columns() * BPP, + QImage::Format_RGBA8888_Premultiplied, + (QImageCleanupFunction) &openshot::cleanUpBuffer, + (void*) qbuffer); + return qimage; +} + +#endif // USE_IMAGEMAGICK diff --git a/src/MagickUtilities.h b/src/MagickUtilities.h index 9246da1e..d782b1aa 100644 --- a/src/MagickUtilities.h +++ b/src/MagickUtilities.h @@ -21,6 +21,9 @@ // namespace instead of the global namespace #include + #include + #include + // Exclude a warning message with IM6 headers #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wignored-qualifiers" @@ -30,8 +33,8 @@ // Determine ImageMagick version, as IM7 isn't fully // backwards compatible #ifndef NEW_MAGICK - #define NEW_MAGICK (MagickLibVersion >= 0x700) - #endif + #define NEW_MAGICK (MagickLibVersion >= 0x700) + #endif // IM7: ->alpha(bool) // IM6: ->matte(bool) @@ -50,5 +53,17 @@ #define MAGICK_DRAWABLE std::list #endif -#endif -#endif +namespace openshot { + + /// Convert QImage to Magick::Image + std::shared_ptr + QImage2Magick(std::shared_ptr); + + /// Convert Magick::Image to QImage + std::shared_ptr + Magick2QImage(std::shared_ptr); + +} // namespace + +#endif // USE_IMAGEMAGICK +#endif // OPENSHOT_MAGICK_UTILITIES_H diff --git a/src/QtUtilities.h b/src/QtUtilities.h index f2e36ffc..a827bc37 100644 --- a/src/QtUtilities.h +++ b/src/QtUtilities.h @@ -23,4 +23,17 @@ namespace Qt { } #endif + +namespace openshot { + // Clean up buffer after QImage is deleted + static inline void cleanUpBuffer(void *info) + { + if (!info) + return; + // Remove buffer since QImage tells us to + auto* qbuffer = reinterpret_cast(info); + delete[] qbuffer; + } +} // namespace + #endif // OPENSHOT_QT_UTILITIES_H diff --git a/src/TextReader.cpp b/src/TextReader.cpp index 58b7b14e..f67db3c8 100644 --- a/src/TextReader.cpp +++ b/src/TextReader.cpp @@ -13,6 +13,9 @@ // Require ImageMagick support #ifdef USE_IMAGEMAGICK +#include "QtUtilities.h" +#include "MagickUtilities.h" + #include "TextReader.h" #include "Exceptions.h" #include "Frame.h" @@ -152,14 +155,15 @@ std::shared_ptr TextReader::GetFrame(int64_t requested_frame) { // Create or get frame object auto image_frame = std::make_shared( - requested_frame, image->size().width(), image->size().height(), + requested_frame, + image->size().width(), image->size().height(), "#000000", 0, 2); // Add Image data to frame auto copy_image = std::make_shared(*image.get()); copy_image->modifyImage(); // actually copy the image data to this object - //TODO: Reimplement this with QImage - image_frame->AddMagickImage(copy_image); + auto qimage = openshot::Magick2QImage(copy_image); + image_frame->AddImage(qimage); // return frame object return image_frame; diff --git a/src/TextReader.h b/src/TextReader.h index f55d4863..03392e9f 100644 --- a/src/TextReader.h +++ b/src/TextReader.h @@ -16,21 +16,16 @@ // Require ImageMagick support #ifdef USE_IMAGEMAGICK -#include "ReaderBase.h" - -#include -#include -#include -#include -#include #include -#include "CacheMemory.h" -#include "Enums.h" +#include "ReaderBase.h" +#include "Enums.h" #include "MagickUtilities.h" namespace openshot { + class CacheBase; + class Frame; /** * @brief This class uses the ImageMagick++ libraries, to create frames with "Text", and return @@ -108,7 +103,7 @@ namespace openshot void Close() override; /// Get the cache object used by this reader (always returns NULL for this object) - openshot::CacheMemory* GetCache() override { return NULL; }; + openshot::CacheBase* GetCache() override { return nullptr; }; /// Get an openshot::Frame object for a specific frame number of this reader. All numbers /// return the same Frame, since they all share the same image data. From 1c5e9dbe387c7c8bd0b68e5a2756e235d04a6540 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Tue, 2 Nov 2021 13:19:04 -0400 Subject: [PATCH 2/5] TimelineBase: Add virtual defaulted destructor --- src/TimelineBase.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/TimelineBase.h b/src/TimelineBase.h index 589df469..d3d3d067 100644 --- a/src/TimelineBase.h +++ b/src/TimelineBase.h @@ -50,6 +50,8 @@ namespace openshot { /// so we'll be able to access the Timeline::Clips() function from a pointer object of /// the TimelineBase class virtual std::list Clips() = 0; + + virtual ~TimelineBase() = default; }; } From cff2109316907503d6d09e488c6ced79ba5837c0 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Wed, 3 Nov 2021 06:57:14 -0400 Subject: [PATCH 3/5] tests/ImageWriter: Increase coverage --- tests/ImageWriter.cpp | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/tests/ImageWriter.cpp b/tests/ImageWriter.cpp index 59446ee4..4056a9ab 100644 --- a/tests/ImageWriter.cpp +++ b/tests/ImageWriter.cpp @@ -25,6 +25,26 @@ using namespace openshot; +TEST_CASE( "conversions", "[libopenshot][imagewriter]" ) +{ + auto magick1 = openshot::QImage2Magick(nullptr); + CHECK_FALSE(magick1); + + auto qimage1 = openshot::Magick2QImage(nullptr); + CHECK_FALSE(qimage1); + + std::stringstream path_overlay; + path_overlay << TEST_MEDIA_PATH << "front3.png"; + openshot::Clip overlay(path_overlay.str()); + overlay.Open(); + auto frame = overlay.Reader()->GetFrame(1); + auto qimage = frame->GetImage(); + + auto magick = openshot::QImage2Magick(qimage); + auto qimage_out = openshot::Magick2QImage(magick); + CHECK(qimage->pixelColor(100, 100) == qimage_out->pixelColor(100, 100)); +} + TEST_CASE( "Gif", "[libopenshot][imagewriter]" ) { // Reader --------------- @@ -49,16 +69,18 @@ TEST_CASE( "Gif", "[libopenshot][imagewriter]" ) CHECK_FALSE(w.IsOpen()); // Check for exception on write-before-open - CHECK_THROWS_AS(w.WriteFrame(&r, 500, 504), WriterClosed); + CHECK_THROWS_AS(w.WriteFrame(&r, 500, 509), WriterClosed); - // Set the image output settings (format, fps, width, height, quality, loops, combine) - w.SetVideoOptions("GIF", r.info.fps, r.info.width, r.info.height, 70, 1, true); + // Set the image output settings + // (format, fps, width, height, quality, loops, combine) + // loops=0 == infinite looping + w.SetVideoOptions("GIF", r.info.fps, r.info.width, r.info.height, 70, 0, true); // Open writer w.Open(); - // Write some frames (start on frame 500 and go to frame 510) - w.WriteFrame(&r, 500, 504); + // Write some frames + w.WriteFrame(&r, 500, 509); // Close writer & reader w.Close(); From 5662f15321155ff335fbef882500baf7140aeaf2 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Wed, 3 Nov 2021 06:57:31 -0400 Subject: [PATCH 4/5] tests/QtImageReader: Fix indents --- tests/QtImageReader.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/QtImageReader.cpp b/tests/QtImageReader.cpp index 2b33cd0b..4c45d070 100644 --- a/tests/QtImageReader.cpp +++ b/tests/QtImageReader.cpp @@ -32,7 +32,7 @@ TEST_CASE( "GetFrame_Before_Opening", "[libopenshot][qtimagereader]" ) // Create a reader std::stringstream path; path << TEST_MEDIA_PATH << "front.png"; - QtImageReader r(path.str()); + QtImageReader r(path.str()); // Check invalid path CHECK_THROWS_AS(r.GetFrame(1), ReaderClosed); @@ -40,15 +40,15 @@ TEST_CASE( "GetFrame_Before_Opening", "[libopenshot][qtimagereader]" ) TEST_CASE( "Check_SVG_Loading", "[libopenshot][qtimagereader]" ) { - // Create a reader - std::stringstream path; - path << TEST_MEDIA_PATH << "1F0CF.svg"; + // Create a reader + std::stringstream path; + path << TEST_MEDIA_PATH << "1F0CF.svg"; QtImageReader r(path.str()); - r.Open(); + r.Open(); - // Get frame, with no Timeline or Clip - // Size should be equal to default SVG size - std::shared_ptr f = r.GetFrame(1); + // Get frame, with no Timeline or Clip + // Size should be equal to default SVG size + std::shared_ptr f = r.GetFrame(1); CHECK(f->GetImage()->width() == 72); CHECK(f->GetImage()->height() == 72); @@ -79,7 +79,7 @@ TEST_CASE( "Check_SVG_Loading", "[libopenshot][qtimagereader]" ) CHECK(f->GetImage()->width() == 480 * 2); CHECK(f->GetImage()->height() == 480 * 2); - // Close reader - t1.Close(); - r.Close(); + // Close reader + t1.Close(); + r.Close(); } From 5ed6929a88e96d0707831e4c6b5fe96f67d9180c Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Wed, 10 Nov 2021 23:33:27 -0500 Subject: [PATCH 5/5] ImageWriter: Fix includes --- src/ImageWriter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ImageWriter.cpp b/src/ImageWriter.cpp index 2c872cb1..c56aab72 100644 --- a/src/ImageWriter.cpp +++ b/src/ImageWriter.cpp @@ -19,6 +19,7 @@ #include "ImageWriter.h" #include "Exceptions.h" #include "Frame.h" +#include "ReaderBase.h" #include "ZmqLogger.h" using namespace openshot;