diff --git a/include/ChunkReader.h b/include/ChunkReader.h index 49825fc9..dd612573 100644 --- a/include/ChunkReader.h +++ b/include/ChunkReader.h @@ -41,7 +41,6 @@ #include #include #include "Json.h" -#include "Magick++.h" #include "Cache.h" #include "Exceptions.h" diff --git a/include/ChunkWriter.h b/include/ChunkWriter.h index 3f42e049..729713cf 100644 --- a/include/ChunkWriter.h +++ b/include/ChunkWriter.h @@ -41,7 +41,6 @@ #include #include #include -#include "Magick++.h" #include "Cache.h" #include "Exceptions.h" #include "Json.h" diff --git a/include/Clip.h b/include/Clip.h index 8092f895..c534d805 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -46,9 +46,11 @@ #include "FFmpegReader.h" #include "Fraction.h" #include "FrameMapper.h" -#include "ImageReader.h" +#ifdef USE_IMAGEMAGICK + #include "ImageReader.h" + #include "TextReader.h" +#endif #include "QtImageReader.h" -#include "TextReader.h" #include "ChunkReader.h" #include "KeyFrame.h" #include "ReaderBase.h" diff --git a/include/DecklinkOutput.h b/include/DecklinkOutput.h index 7801d6df..06ef6cf1 100644 --- a/include/DecklinkOutput.h +++ b/include/DecklinkOutput.h @@ -60,7 +60,6 @@ #include #include #include -#include "Magick++.h" #include "DeckLinkAPI.h" #include "../include/Cache.h" diff --git a/include/DecklinkReader.h b/include/DecklinkReader.h index 3ccfcfc1..ba36cb22 100644 --- a/include/DecklinkReader.h +++ b/include/DecklinkReader.h @@ -42,7 +42,6 @@ #include #include -#include "Magick++.h" #include "Cache.h" #include "Exceptions.h" #include "Frame.h" diff --git a/include/DecklinkWriter.h b/include/DecklinkWriter.h index acf9fb89..38ce109e 100644 --- a/include/DecklinkWriter.h +++ b/include/DecklinkWriter.h @@ -42,7 +42,6 @@ #include #include -#include "Magick++.h" #include "Cache.h" #include "Exceptions.h" #include "Frame.h" diff --git a/include/DummyReader.h b/include/DummyReader.h index ee8dba43..73783cfd 100644 --- a/include/DummyReader.h +++ b/include/DummyReader.h @@ -36,7 +36,6 @@ #include #include #include -#include "Magick++.h" #include "Cache.h" #include "Exceptions.h" #include "Fraction.h" diff --git a/include/FFmpegReader.h b/include/FFmpegReader.h index 01ac7c92..14a2e26e 100644 --- a/include/FFmpegReader.h +++ b/include/FFmpegReader.h @@ -41,7 +41,6 @@ #include #include #include -#include "Magick++.h" #include "Cache.h" #include "Exceptions.h" #include "OpenMPUtilities.h" diff --git a/include/FFmpegWriter.h b/include/FFmpegWriter.h index f1481b70..fab1c214 100644 --- a/include/FFmpegWriter.h +++ b/include/FFmpegWriter.h @@ -47,7 +47,6 @@ #include #include #include -#include "Magick++.h" #include "Cache.h" #include "Exceptions.h" #include "OpenMPUtilities.h" diff --git a/include/Frame.h b/include/Frame.h index d5de7c10..6d80577a 100644 --- a/include/Frame.h +++ b/include/Frame.h @@ -40,14 +40,21 @@ #include #include #include +#include #include #include +#include #include #include #include +#include +#include +#include #include #include -#include "Magick++.h" +#ifdef USE_IMAGEMAGICK + #include "Magick++.h" +#endif #include "JuceLibraryCode/JuceHeader.h" #include "ChannelLayouts.h" #include "AudioBufferSource.h" @@ -85,15 +92,6 @@ namespace openshot * "#000000" // HTML color code of background color * ); * - * // Image only from pixel array (48kHz audio silence) - * Frame(number, // Frame number - * 720, // Width of image - * 480, // Height of image - * "RGBA", // Color format / map - * Magick::CharPixel, // Storage format / data type - * buffer // Array of image data (pixels) - * ); - * * // Audio only (300x200 blank image) * Frame(number, // Frame number * 44100, // Sample rate of audio stream @@ -120,6 +118,7 @@ namespace openshot tr1::shared_ptr image; tr1::shared_ptr wave_image; tr1::shared_ptr audio; + tr1::shared_ptr previewApp; const unsigned char *qbuffer; Fraction pixel_ratio; int channels; @@ -128,6 +127,9 @@ namespace openshot int height; int sample_rate; + /// Constrain a color value from 0 to 255 + int constrain(int color_value); + public: long int number; ///< This is the frame number (starting at 1) bool has_audio_data; ///< This frame has been loaded with audio data @@ -139,9 +141,6 @@ namespace openshot /// Constructor - image only (48kHz audio silence) Frame(long int number, int width, int height, string color); - /// Constructor - image only from pixel array (48kHz audio silence) - Frame(long int number, int width, int height, const string map, const Magick::StorageType type, const void *pixels_); - /// Constructor - audio only (300x200 blank image) Frame(long int number, int samples, int channels); @@ -169,8 +168,10 @@ namespace openshot /// Add (or replace) pixel data to the frame (for only the odd or even lines) void AddImage(tr1::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(tr1::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); @@ -226,8 +227,10 @@ namespace openshot /// Get pointer to Qt QImage image object tr1::shared_ptr GetImage(); +#ifdef USE_IMAGEMAGICK /// Get pointer to ImageMagick image object tr1::shared_ptr GetMagickImage(); +#endif /// Set Pixel Aspect Ratio Fraction GetPixelRatio() { return pixel_ratio; }; @@ -265,8 +268,8 @@ namespace openshot /// Set the original sample rate of this frame's audio data void SampleRate(int orig_sample_rate) { sample_rate = orig_sample_rate; }; - /// Save the frame image to the specified path. The image format is determined from the extension (i.e. image.PNG, image.JPEG) - void Save(string path, float scale); + /// Save the frame image to the specified path. The image format can be BMP, JPG, JPEG, PNG, PPM, XBM, XPM + void Save(string path, float scale, string format="PNG", int quality=100); /// Set frame number void SetFrameNumber(int number); @@ -277,7 +280,7 @@ namespace openshot /// Thumbnail the frame image with tons of options to the specified path. The image format is determined from the extension (i.e. image.PNG, image.JPEG). /// This method allows for masks, overlays, background color, and much more accurate resizing (including padding and centering) void Thumbnail(string path, int new_width, int new_height, string mask_path, string overlay_path, - string background_color, bool ignore_aspect) throw(InvalidFile); + string background_color, bool ignore_aspect, string format="png", int quality=100) throw(InvalidFile); /// Play audio samples for this frame void Play(); diff --git a/include/OpenShot.h b/include/OpenShot.h index e1e9bb6d..64a02d1e 100644 --- a/include/OpenShot.h +++ b/include/OpenShot.h @@ -122,15 +122,17 @@ #include "Fraction.h" #include "Frame.h" #include "FrameMapper.h" -#include "ImageReader.h" -#include "ImageWriter.h" +#ifdef USE_IMAGEMAGICK + #include "ImageReader.h" + #include "ImageWriter.h" + #include "TextReader.h" +#endif #include "KeyFrame.h" #include "PlayerBase.h" #include "Point.h" #include "Profiles.h" #include "QtImageReader.h" #include "Sleep.h" -#include "TextReader.h" #include "Timeline.h" #endif diff --git a/include/Timeline.h b/include/Timeline.h index fd330bf0..8f895087 100644 --- a/include/Timeline.h +++ b/include/Timeline.h @@ -32,7 +32,6 @@ #include #include #include -#include "Magick++.h" #include "Cache.h" #include "Color.h" #include "Clip.h" diff --git a/include/effects/Blur.h b/include/effects/Blur.h index 8b8cc53f..02799bde 100644 --- a/include/effects/Blur.h +++ b/include/effects/Blur.h @@ -37,14 +37,12 @@ #include #include #include -#include "Magick++.h" #include "../Color.h" #include "../Exceptions.h" #include "../Json.h" #include "../KeyFrame.h" #include "../ReaderBase.h" #include "../FFmpegReader.h" -#include "../ImageReader.h" #include "../QtImageReader.h" #include "../ChunkReader.h" diff --git a/include/effects/Brightness.h b/include/effects/Brightness.h index 83efd8e5..801cf405 100644 --- a/include/effects/Brightness.h +++ b/include/effects/Brightness.h @@ -36,14 +36,12 @@ #include #include #include -#include "Magick++.h" #include "../Color.h" #include "../Exceptions.h" #include "../Json.h" #include "../KeyFrame.h" #include "../ReaderBase.h" #include "../FFmpegReader.h" -#include "../ImageReader.h" #include "../QtImageReader.h" #include "../ChunkReader.h" diff --git a/include/effects/ChromaKey.h b/include/effects/ChromaKey.h index a666339c..f2710ad4 100644 --- a/include/effects/ChromaKey.h +++ b/include/effects/ChromaKey.h @@ -36,7 +36,6 @@ #include #include #include -#include "Magick++.h" #include "../Color.h" #include "../Exceptions.h" #include "../KeyFrame.h" diff --git a/include/effects/Deinterlace.h b/include/effects/Deinterlace.h index 6c6b9f8b..82103998 100644 --- a/include/effects/Deinterlace.h +++ b/include/effects/Deinterlace.h @@ -36,7 +36,6 @@ #include #include #include -#include "Magick++.h" #include "../Color.h" #include "../Exceptions.h" #include "../Json.h" diff --git a/include/effects/Mask.h b/include/effects/Mask.h index 310e6992..63ee4270 100644 --- a/include/effects/Mask.h +++ b/include/effects/Mask.h @@ -36,14 +36,12 @@ #include #include #include -#include "Magick++.h" #include "../Color.h" #include "../Exceptions.h" #include "../Json.h" #include "../KeyFrame.h" #include "../ReaderBase.h" #include "../FFmpegReader.h" -#include "../ImageReader.h" #include "../QtImageReader.h" #include "../ChunkReader.h" diff --git a/include/effects/Negate.h b/include/effects/Negate.h index c29b6783..d8a85f27 100644 --- a/include/effects/Negate.h +++ b/include/effects/Negate.h @@ -36,7 +36,6 @@ #include #include #include -#include "Magick++.h" #include "../Color.h" #include "../Exceptions.h" #include "../KeyFrame.h" diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h index 7ade6135..35bff035 100644 --- a/include/effects/Saturation.h +++ b/include/effects/Saturation.h @@ -36,14 +36,12 @@ #include #include #include -#include "Magick++.h" #include "../Color.h" #include "../Exceptions.h" #include "../Json.h" #include "../KeyFrame.h" #include "../ReaderBase.h" #include "../FFmpegReader.h" -#include "../ImageReader.h" #include "../QtImageReader.h" #include "../ChunkReader.h" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f097d902..d4e5d220 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,10 +63,16 @@ ELSE (OPENSHOT_IMAGEMAGICK_COMPATIBILITY) ENDIF (OPENSHOT_IMAGEMAGICK_COMPATIBILITY) # Find the ImageMagick++ library -FIND_PACKAGE(ImageMagick REQUIRED COMPONENTS Magick++ MagickWand MagickCore) +FIND_PACKAGE(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) +IF (ImageMagick_FOUND) + # Include ImageMagick++ headers (needed for compile) + include_directories(${ImageMagick_INCLUDE_DIRS}) -# Include ImageMagick++ headers (needed for compile) -include_directories(${ImageMagick_INCLUDE_DIRS}) + # define a global var (used in the C++) + add_definitions( -DUSE_IMAGEMAGICK=1 ) + SET(CMAKE_SWIG_FLAGS "-DUSE_IMAGEMAGICK=1") + +ENDIF (ImageMagick_FOUND) ################### FFMPEG ##################### # Find FFmpeg libraries (used for video encoding / decoding) @@ -185,14 +191,11 @@ SET ( OPENSHOT_SOURCE_FILES Frame.cpp FrameMapper.cpp KeyFrame.cpp - ImageReader.cpp - ImageWriter.cpp PlayerBase.cpp Point.cpp Profiles.cpp QtImageReader.cpp QtPlayer.cpp - TextReader.cpp Timeline.cpp # Qt Video Player @@ -203,7 +206,16 @@ SET ( OPENSHOT_SOURCE_FILES ../thirdparty/jsoncpp/src/lib_json/json_reader.cpp ../thirdparty/jsoncpp/src/lib_json/json_value.cpp ../thirdparty/jsoncpp/src/lib_json/json_writer.cpp ) - + + # ImageMagic related files + IF (ImageMagick_FOUND) + SET ( OPENSHOT_SOURCE_FILES ${OPENSHOT_SOURCE_FILES} + ImageReader.cpp + ImageWriter.cpp + TextReader.cpp) + ENDIF (ImageMagick_FOUND) + + # BlackMagic related files IF (BLACKMAGIC_FOUND) SET ( OPENSHOT_SOURCE_FILES ${OPENSHOT_SOURCE_FILES} DecklinkInput.cpp @@ -235,7 +247,6 @@ set_target_properties(openshot ############### LINK LIBRARY ################# SET ( REQUIRED_LIBRARIES - ${ImageMagick_LIBRARIES} ${FFMPEG_LIBRARIES} ${LIBOPENSHOT_AUDIO_LIBRARIES} ${QT_LIBRARIES} @@ -246,6 +257,10 @@ SET ( REQUIRED_LIBRARIES SET ( REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES} ${OpenMP_CXX_FLAGS} ) ENDIF (OPENMP_FOUND) + IF (ImageMagick_FOUND) + SET ( REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES} ${ImageMagick_LIBRARIES} ) + ENDIF (ImageMagick_FOUND) + IF (BLACKMAGIC_FOUND) SET ( REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES} ${BLACKMAGIC_LIBRARY_DIR} ) ENDIF (BLACKMAGIC_FOUND) diff --git a/src/Clip.cpp b/src/Clip.cpp index 8b085bff..333c7210 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -866,6 +866,7 @@ void Clip::SetJsonValue(Json::Value root) { reader = new QtImageReader(root["reader"]["path"].asString()); reader->SetJsonValue(root["reader"]); +#ifdef USE_IMAGEMAGICK } else if (type == "ImageReader") { // Create new reader @@ -877,6 +878,7 @@ void Clip::SetJsonValue(Json::Value root) { // Create new reader reader = new TextReader(); reader->SetJsonValue(root["reader"]); +#endif } else if (type == "ChunkReader") { diff --git a/src/Frame.cpp b/src/Frame.cpp index d7da050e..226cb731 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -53,18 +53,6 @@ Frame::Frame(long int number, int width, int height, string color) audio->clear(); }; -// Constructor - image only from pixel array (48kHz audio silence) -Frame::Frame(long int number, int width, int height, const string map, const Magick::StorageType type, const void *pixels) - : number(number), pixel_ratio(1,1), channels(2), width(width), height(height), - channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false) -{ - // Init the image magic and audio buffer - audio = tr1::shared_ptr(new juce::AudioSampleBuffer(channels, 0)); - - // initialize the audio samples to zero (silence) - audio->clear(); -}; - // Constructor - audio only (300x200 blank image) Frame::Frame(long int number, int samples, int channels) : number(number), pixel_ratio(1,1), channels(channels), width(1), height(1), @@ -125,11 +113,42 @@ Frame::~Frame() { // Display the frame image to the screen (primarily used for debugging reasons) void Frame::Display() { - // Create new image object, and fill with pixel data - tr1::shared_ptr magick_image = GetMagickImage(); + if (!QApplication::instance()) { + // Only create the QApplication once + static int argc = 1; + static char* argv[1] = {NULL}; + previewApp = tr1::shared_ptr(new QApplication(argc, argv)); + } - // Disply image - magick_image->display(); + // Get preview image + tr1::shared_ptr previewImage = GetImage(); + + // Update the image to reflect the correct pixel aspect ration (i.e. to fix non-squar pixels) + if (pixel_ratio.num != 1 || pixel_ratio.den != 1) + { + // Calculate correct DAR (display aspect ratio) + int new_width = previewImage->size().width(); + int new_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble(); + + // Resize to fix DAR + previewImage = tr1::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); + } + + // Create window + QWidget previewWindow; + previewWindow.setStyleSheet("background-color: #000000;"); + QHBoxLayout layout; + + // Create label with current frame's image + QLabel previewLabel; + previewLabel.setPixmap(QPixmap::fromImage(*previewImage)); + previewLabel.setMask(QPixmap::fromImage(*previewImage).mask()); + layout.addWidget(&previewLabel); + + // Show the window + previewWindow.setLayout(&layout); + previewWindow.show(); + previewApp->exec(); } // Get an audio waveform image @@ -250,31 +269,28 @@ void Frame::DisplayWaveform() // Get audio wave form image GetWaveform(720, 480, 0, 123, 255, 255); - QRgb const *tmpBits = (const QRgb*)wave_image->bits(); - - // Create new image object, and fill with pixel data - tr1::shared_ptr magick_image = tr1::shared_ptr(new Magick::Image(wave_image->width(), wave_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->matte(true); - - // Resize image (if needed) - if (pixel_ratio.num != 1 || pixel_ratio.den != 1) - { - // Calculate correct DAR (display aspect ratio) - int new_width = magick_image->size().width(); - int new_height = magick_image->size().height() * pixel_ratio.Reciprocal().ToDouble(); - - // Resize image - Magick::Geometry new_size(new_width, new_height); - new_size.aspect(true); - magick_image->resize(new_size); + if (!QApplication::instance()) { + // Only create the QApplication once + static int argc = 1; + static char* argv[1] = {NULL}; + previewApp = tr1::shared_ptr(new QApplication(argc, argv)); } - // Disply image - magick_image->display(); + // Create window + QWidget previewWindow; + previewWindow.setStyleSheet("background-color: #000000;"); + QHBoxLayout layout; + + // Create label with current frame's waveform image + QLabel previewLabel; + previewLabel.setPixmap(QPixmap::fromImage(*wave_image)); + previewLabel.setMask(QPixmap::fromImage(*wave_image).mask()); + layout.addWidget(&previewLabel); + + // Show the window + previewWindow.setLayout(&layout); + previewWindow.show(); + previewApp->exec(); // Deallocate waveform image ClearWaveform(); @@ -497,181 +513,148 @@ ChannelLayout Frame::ChannelsLayout() // Save the frame image to the specified path. The image format is determined from the extension (i.e. image.PNG, image.JPEG) -void Frame::Save(string path, float scale) +void Frame::Save(string path, float scale, string format, int quality) { - // Create new image object, and fill with pixel data - tr1::shared_ptr copy = GetMagickImage(); - - // display the image (if any) - if (copy->size().width() > 1 && copy->size().height() > 1) - { - // Resize image (if needed) - if (pixel_ratio.num != 1 || pixel_ratio.den != 1) - { - // Calculate correct DAR (display aspect ratio) - int new_width = copy->size().width(); - int new_height = copy->size().height() * pixel_ratio.Reciprocal().ToDouble(); - - // Resize image - Magick::Geometry new_size(new_width, new_height); - new_size.aspect(true); - copy->resize(new_size); - } - } + // Get preview image + tr1::shared_ptr previewImage = GetImage(); // scale image if needed if (abs(scale) > 1.001 || abs(scale) < 0.999) { + int new_width = width; + int new_height = height; + + // Update the image to reflect the correct pixel aspect ration (i.e. to fix non-squar pixels) + if (pixel_ratio.num != 1 || pixel_ratio.den != 1) + { + // Calculate correct DAR (display aspect ratio) + int new_width = previewImage->size().width(); + int new_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble(); + + // Resize to fix DAR + previewImage = tr1::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); + } + // Resize image - Magick::Geometry new_size(copy->size().width() * scale, copy->size().height() * scale); - new_size.aspect(true); - copy->resize(new_size); + previewImage = tr1::shared_ptr(new QImage(previewImage->scaled(new_width * scale, new_height * scale, Qt::KeepAspectRatio, Qt::SmoothTransformation))); } - // save the image - copy->write(path); + // Save image + previewImage->save(QString::fromStdString(path), format.c_str(), quality); } // Thumbnail the frame image to the specified path. The image format is determined from the extension (i.e. image.PNG, image.JPEG) void Frame::Thumbnail(string path, int new_width, int new_height, string mask_path, string overlay_path, - string background_color, bool ignore_aspect) throw(InvalidFile) { + string background_color, bool ignore_aspect, string format, int quality) throw(InvalidFile) { - // Create new image object, and fill with pixel data - tr1::shared_ptr copy = GetMagickImage(); - copy->virtualPixelMethod(Magick::TransparentVirtualPixelMethod); + // Create blank thumbnail image & fill background color + tr1::shared_ptr thumbnail = tr1::shared_ptr(new QImage(new_width, new_height, QImage::Format_RGBA8888)); + thumbnail->fill(QColor(QString::fromStdString(background_color))); + + // Create transform and painter + QTransform transform; + QPainter painter(thumbnail.get()); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing, true); - // Set background color - if (background_color != "") - copy->backgroundColor(Magick::Color(background_color)); - else - { - copy->backgroundColor(Magick::Color("none")); - background_color = "none"; - } - - // Maintain alpha channel - copy->matte(true); + // Get preview image + tr1::shared_ptr previewImage = GetImage(); // Update the image to reflect the correct pixel aspect ration (i.e. to fix non-squar pixels) - if (copy->size().width() > 1 && copy->size().height() > 1) + if (pixel_ratio.num != 1 || pixel_ratio.den != 1) { - // Resize image (if needed) - if (pixel_ratio.num != 1 || pixel_ratio.den != 1) - { - // Calculate correct DAR (display aspect ratio) - int new_width = copy->size().width(); - int new_height = copy->size().height() * pixel_ratio.Reciprocal().ToDouble(); + // Calculate correct DAR (display aspect ratio) + int aspect_width = previewImage->size().width(); + int aspect_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble(); - // Resize image - Magick::Geometry new_size(new_width, new_height); - new_size.aspect(true); // ignore aspect - new_size.greater(false); // don't resize past size - new_size.less(true); // okay to resize less than requested size - copy->resize(new_size); - } + // Resize to fix DAR + previewImage = tr1::shared_ptr(new QImage(previewImage->scaled(aspect_width, aspect_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); } - // scale image if needed - if (width != new_width || height != new_height) - { - // Resize image - Magick::Geometry new_size(new_width, new_height); - new_size.aspect(ignore_aspect); - copy->resize(new_size); - } + // Resize frame image + if (ignore_aspect) + // Ignore aspect ratio + previewImage = tr1::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); + else + // Maintain aspect ratio + previewImage = tr1::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::KeepAspectRatio, Qt::SmoothTransformation))); - // extend image if too small - if (copy->size().width() != new_width || copy->size().height() != new_height ) - { - // extend canvas size and center image - //copy.extent(Magick::Geometry(new_width, new_height), background_color, Magick::EastGravity); - - // Create new background image - tr1::shared_ptr background = tr1::shared_ptr(new Magick::Image(Magick::Geometry(new_width, new_height), Magick::Color(background_color))); - background->matte(true); - - // Determine offset - int x = (new_width - copy->size().width()) / 2.0; // center - int y = (new_height - copy->size().height()) / 2.0; // center - - // Composite resized frame image onto background image - background->composite(*copy.get(), x, y, Magick::OverCompositeOp); - - // Update copy image - copy = background; - } + // Composite frame image onto background (centered) + int x = (new_width - previewImage->size().width()) / 2.0; // center + int y = (new_height - previewImage->size().height()) / 2.0; // center + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawImage(x, y, *previewImage); - // apply overlay (if any) + // Overlay Image (if any) if (overlay_path != "") { + // Open overlay + tr1::shared_ptr overlay = tr1::shared_ptr(new QImage()); + overlay->load(QString::fromStdString(overlay_path)); - // Attempt to open overlay_path file - tr1::shared_ptr overlay; - try { - // load image - overlay = tr1::shared_ptr(new Magick::Image(overlay_path)); + // Set pixel format + overlay = tr1::shared_ptr(new QImage(overlay->convertToFormat(QImage::Format_RGBA8888))); - // Give image a transparent background color - overlay->backgroundColor(Magick::Color("none")); - overlay->matte(true); + // Resize to fit + overlay = tr1::shared_ptr(new QImage(overlay->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); - } catch (Magick::Exception e) { - // raise exception - throw InvalidFile("Overlay could not be opened.", overlay_path); - } - - // Resize overlay to match this frame size (if different) - if (copy->size() != overlay->size()) - { - Magick::Geometry new_size(copy->size().width(), copy->size().height()); - new_size.aspect(true); // ignore aspect - overlay->resize(new_size); - } - - /* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */ - copy->composite(*overlay.get(), 0, 0, Magick::OverCompositeOp); + // Composite onto thumbnail + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawImage(0, 0, *overlay); } - // apply mask (if any) + + // Mask Image (if any) if (mask_path != "") { + // Open mask + tr1::shared_ptr mask = tr1::shared_ptr(new QImage()); + mask->load(QString::fromStdString(mask_path)); - // Attempt to open mask file - tr1::shared_ptr mask; - try { - // load image - mask = tr1::shared_ptr(new Magick::Image(mask_path)); + // Set pixel format + mask = tr1::shared_ptr(new QImage(mask->convertToFormat(QImage::Format_RGBA8888))); - // Give image a transparent background color - mask->backgroundColor(Magick::Color("none")); - mask->matte(true); + // Resize to fit + mask = tr1::shared_ptr(new QImage(mask->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); - } catch (Magick::Exception e) { - // raise exception - throw InvalidFile("Mask could not be opened.", mask_path); - } + // Negate mask + mask->invertPixels(); - // Resize mask to match this frame size (if different) - if (copy->size() != mask->size()) + // Get pixels + unsigned char *pixels = (unsigned char *) thumbnail->bits(); + unsigned char *mask_pixels = (unsigned char *) mask->bits(); + + // Convert the mask image to grayscale + // Loop through pixels + for (int pixel = 0, byte_index=0; pixel < new_width * new_height; pixel++, byte_index+=4) { - Magick::Geometry new_size(copy->size().width(), copy->size().height()); - new_size.aspect(true); // ignore aspect - mask->resize(new_size); + // Get the RGB values from the pixel + int gray_value = qGray(mask_pixels[byte_index], mask_pixels[byte_index] + 1, mask_pixels[byte_index] + 2); + int Frame_Alpha = pixels[byte_index + 3]; + int Mask_Value = constrain(Frame_Alpha - gray_value); + + // Set all alpha pixels to gray value + pixels[byte_index + 3] = Mask_Value; } - - // Apply mask to frame image - tr1::shared_ptr copy_source = tr1::shared_ptr(new Magick::Image(*copy)); - copy_source->channel(Magick::MatteChannel); // extract alpha channel as grayscale image - copy_source->matte(false); // remove alpha channel - copy_source->negate(true); // negate source alpha channel before multiplying mask - copy_source->composite(*mask.get(), 0, 0, Magick::MultiplyCompositeOp); // multiply mask grayscale (i.e. combine the 2 grayscale images) - - // Copy the combined alpha channel back to the frame - copy->composite(*copy_source.get(), 0, 0, Magick::CopyOpacityCompositeOp); } - // save the image - copy->write(path); + + // End painter + painter.end(); + + // Save image + thumbnail->save(QString::fromStdString(path), format.c_str(), quality); +} + +// Constrain a color value from 0 to 255 +int Frame::constrain(int color_value) +{ + // Constrain new color from 0 to 255 + if (color_value < 0) + color_value = 0; + else if (color_value > 255) + color_value = 255; + + return color_value; } // Add (or replace) pixel data to the frame (based on a solid color) @@ -818,6 +801,7 @@ tr1::shared_ptr Frame::GetImage() return image; } +#ifdef USE_IMAGEMAGICK // Get pointer to ImageMagick image object tr1::shared_ptr Frame::GetMagickImage() { @@ -839,7 +823,9 @@ tr1::shared_ptr Frame::GetMagickImage() return magick_image; } +#endif +#ifdef USE_IMAGEMAGICK // Get pointer to QImage of frame void Frame::AddMagickImage(tr1::shared_ptr new_image) { @@ -872,6 +858,7 @@ void Frame::AddMagickImage(tr1::shared_ptr new_image) height = image->height(); has_image_data = true; } +#endif // Play audio samples for this frame void Frame::Play() diff --git a/src/Timeline.cpp b/src/Timeline.cpp index f6c4f791..891d1489 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -362,8 +362,9 @@ void Timeline::add_layer(tr1::shared_ptr new_frame, Clip* source_clip, lo break; case (SCALE_CROP): - Magick::Geometry width_size(info.width, round(info.width / (float(source_width) / float(source_height)))); - Magick::Geometry height_size(round(info.height / (float(source_height) / float(source_width))), info.height); + QSize width_size(info.width, round(info.width / (float(source_width) / float(source_height)))); + QSize height_size(round(info.height / (float(source_height) / float(source_width))), info.height); + // respect aspect ratio if (width_size.width() >= info.width && width_size.height() >= info.height) source_image = tr1::shared_ptr(new QImage(source_image->scaled(width_size.width(), width_size.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation))); diff --git a/src/bindings/python/openshot.i b/src/bindings/python/openshot.i index e755c004..c6f57390 100644 --- a/src/bindings/python/openshot.i +++ b/src/bindings/python/openshot.i @@ -72,8 +72,6 @@ #include "../../../include/Fraction.h" #include "../../../include/Frame.h" #include "../../../include/FrameMapper.h" -#include "../../../include/ImageReader.h" -#include "../../../include/ImageWriter.h" #include "../../../include/PlayerBase.h" #include "../../../include/Point.h" #include "../../../include/Profiles.h" @@ -81,7 +79,6 @@ #include "../../../include/QtPlayer.h" #include "../../../include/KeyFrame.h" #include "../../../include/RendererBase.h" -#include "../../../include/TextReader.h" #include "../../../include/Timeline.h" %} @@ -93,6 +90,14 @@ %} #endif +#ifdef USE_IMAGEMAGICK + %{ + #include "../../../include/ImageReader.h" + #include "../../../include/ImageWriter.h" + #include "../../../include/TextReader.h" + %} +#endif + /* Generic language independent exception handler. */ %include "exception.i" %exception { @@ -130,8 +135,6 @@ %include "../../../include/Fraction.h" %include "../../../include/Frame.h" %include "../../../include/FrameMapper.h" -%include "../../../include/ImageReader.h" -%include "../../../include/ImageWriter.h" %include "../../../include/PlayerBase.h" %include "../../../include/Point.h" %include "../../../include/Profiles.h" @@ -139,8 +142,12 @@ %include "../../../include/QtPlayer.h" %include "../../../include/KeyFrame.h" %include "../../../include/RendererBase.h" -%include "../../../include/TextReader.h" %include "../../../include/Timeline.h" +#ifdef USE_IMAGEMAGICK + %include "../../../include/ImageReader.h" + %include "../../../include/ImageWriter.h" + %include "../../../include/TextReader.h" +#endif /* Effects */ %include "../../../include/effects/Blur.h" diff --git a/src/bindings/ruby/openshot.i b/src/bindings/ruby/openshot.i index be9d6c92..255b52f0 100644 --- a/src/bindings/ruby/openshot.i +++ b/src/bindings/ruby/openshot.i @@ -78,8 +78,6 @@ namespace tr1 #include "../../../include/Fraction.h" #include "../../../include/Frame.h" #include "../../../include/FrameMapper.h" -#include "../../../include/ImageReader.h" -#include "../../../include/ImageWriter.h" #include "../../../include/PlayerBase.h" #include "../../../include/Point.h" #include "../../../include/Profiles.h" @@ -87,7 +85,6 @@ namespace tr1 #include "../../../include/QtPlayer.h" #include "../../../include/KeyFrame.h" #include "../../../include/RendererBase.h" -#include "../../../include/TextReader.h" #include "../../../include/Timeline.h" %} @@ -99,6 +96,13 @@ namespace tr1 %} #endif +#ifdef USE_IMAGEMAGICK + %{ + #include "../../../include/ImageReader.h" + #include "../../../include/ImageWriter.h" + #include "../../../include/TextReader.h" + %} +#endif %include "../../../include/Version.h" %include "../../../include/ReaderBase.h" @@ -126,8 +130,6 @@ namespace tr1 %include "../../../include/Fraction.h" %include "../../../include/Frame.h" %include "../../../include/FrameMapper.h" -%include "../../../include/ImageReader.h" -%include "../../../include/ImageWriter.h" %include "../../../include/PlayerBase.h" %include "../../../include/Point.h" %include "../../../include/Profiles.h" @@ -135,9 +137,14 @@ namespace tr1 %include "../../../include/QtPlayer.h" %include "../../../include/KeyFrame.h" %include "../../../include/RendererBase.h" -%include "../../../include/TextReader.h" %include "../../../include/Timeline.h" +#ifdef USE_IMAGEMAGICK + %include "../../../include/ImageReader.h" + %include "../../../include/ImageWriter.h" + %include "../../../include/TextReader.h" +#endif + /* Effects */ %include "../../../include/effects/Blur.h" %include "../../../include/effects/Brightness.h" diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp index b9499749..48657fd4 100644 --- a/src/effects/Mask.cpp +++ b/src/effects/Mask.cpp @@ -247,11 +247,13 @@ void Mask::SetJsonValue(Json::Value root) { reader = new FFmpegReader(root["reader"]["path"].asString()); reader->SetJsonValue(root["reader"]); +#ifdef USE_IMAGEMAGICK } else if (type == "ImageReader") { // Create new reader reader = new ImageReader(root["reader"]["path"].asString()); reader->SetJsonValue(root["reader"]); +#endif } else if (type == "QtImageReader") { diff --git a/src/examples/Example.cpp b/src/examples/Example.cpp index df218dd7..714bb7dc 100644 --- a/src/examples/Example.cpp +++ b/src/examples/Example.cpp @@ -36,10 +36,19 @@ using namespace tr1; int main(int argc, char* argv[]) { + FFmpegReader rTest("/home/jonathan/Videos/sintel_trailer-720p.mp4"); + rTest.Open(); + rTest.GetFrame(300)->Thumbnail("frame1.png", 300, 100, "/home/jonathan/apps/openshot-qt-git/src/images/mask.png", "/home/jonathan/apps/openshot-qt-git/src/images/overlay.png", "#001100", false); + rTest.GetFrame(310)->Thumbnail("frame2.png", 100, 300, "/home/jonathan/apps/openshot-qt-git/src/images/mask.png", "/home/jonathan/apps/openshot-qt-git/src/images/overlay.png", "Red", false); + rTest.GetFrame(320)->Thumbnail("frame3.png", 50, 100, "/home/jonathan/apps/openshot-qt-git/src/images/mask.png", "/home/jonathan/apps/openshot-qt-git/src/images/overlay.png", "#000000", false); + rTest.GetFrame(330)->Thumbnail("frame4.png", 1920, 1080, "/home/jonathan/apps/openshot-qt-git/src/images/mask.png", "/home/jonathan/apps/openshot-qt-git/src/images/overlay.png", "#ffffff", false); + return 0; + + + Timeline r9(640, 480, Fraction(30, 1), 44100, 2, LAYOUT_STEREO); - int frame_count = 1; while (true) { // Create a timeline diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 60d1dafb..36faf8a9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -60,11 +60,16 @@ ELSE (OPENSHOT_IMAGEMAGICK_COMPATIBILITY) ENDIF (OPENSHOT_IMAGEMAGICK_COMPATIBILITY) # Find the ImageMagick++ library -FIND_PACKAGE(ImageMagick REQUIRED COMPONENTS Magick++ MagickWand MagickCore) +FIND_PACKAGE(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) +IF (ImageMagick_FOUND) + # Include ImageMagick++ headers (needed for compile) + include_directories(${ImageMagick_INCLUDE_DIRS}) -# Include ImageMagick++ headers (needed for compile) -include_directories(${ImageMagick_INCLUDE_DIRS}) - + # define a global var (used in the C++) + add_definitions( -DUSE_IMAGEMAGICK=1 ) + SET(CMAKE_SWIG_FLAGS "-DUSE_IMAGEMAGICK=1") + +ENDIF (ImageMagick_FOUND) ################### FFMPEG ##################### # Find FFmpeg libraries (used for video encoding / decoding) diff --git a/tests/ImageWriter_Tests.cpp b/tests/ImageWriter_Tests.cpp index 329d4e57..fdf4429b 100644 --- a/tests/ImageWriter_Tests.cpp +++ b/tests/ImageWriter_Tests.cpp @@ -32,6 +32,7 @@ using namespace std; using namespace openshot; +#ifdef USE_IMAGEMAGICK TEST(ImageWriter_Test_Gif) { // Reader @@ -77,3 +78,4 @@ TEST(ImageWriter_Test_Gif) CHECK_EQUAL(11, (int)pixels[pixel_index + 2]); CHECK_EQUAL(255, (int)pixels[pixel_index + 3]); } +#endif \ No newline at end of file