You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Merge branch 'clip-refactor-keyframes' into opencv
This commit is contained in:
198
src/Frame.cpp
198
src/Frame.cpp
@@ -28,7 +28,22 @@
|
||||
* along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../include/Frame.h"
|
||||
#include "Frame.h"
|
||||
#include "JuceHeader.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
#include <QBitmap>
|
||||
#include <QColor>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QPainter>
|
||||
#include <QHBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QPointF>
|
||||
#include <QWidget>
|
||||
|
||||
#include <thread> // for std::this_thread::sleep_for
|
||||
#include <chrono> // for std::chrono::milliseconds
|
||||
@@ -36,57 +51,31 @@
|
||||
using namespace std;
|
||||
using namespace openshot;
|
||||
|
||||
// Constructor - blank frame (300x200 blank image, 48kHz audio silence)
|
||||
Frame::Frame() : number(1), pixel_ratio(1,1), channels(2), width(1), height(1), color("#000000"),
|
||||
channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
|
||||
max_audio_sample(0)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
audio = std::shared_ptr<juce::AudioSampleBuffer>(new juce::AudioSampleBuffer(channels, 0));
|
||||
|
||||
// initialize the audio samples to zero (silence)
|
||||
audio->clear();
|
||||
}
|
||||
|
||||
// Constructor - image only (48kHz audio silence)
|
||||
Frame::Frame(int64_t number, int width, int height, std::string color)
|
||||
: number(number), pixel_ratio(1,1), channels(2), width(width), height(height), color(color),
|
||||
channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
|
||||
max_audio_sample(0)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
audio = std::shared_ptr<juce::AudioSampleBuffer>(new juce::AudioSampleBuffer(channels, 0));
|
||||
|
||||
// initialize the audio samples to zero (silence)
|
||||
audio->clear();
|
||||
}
|
||||
|
||||
// Constructor - audio only (300x200 blank image)
|
||||
Frame::Frame(int64_t number, int samples, int channels) :
|
||||
number(number), pixel_ratio(1,1), channels(channels), width(1), height(1), color("#000000"),
|
||||
channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
|
||||
max_audio_sample(0)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
audio = std::shared_ptr<juce::AudioSampleBuffer>(new juce::AudioSampleBuffer(channels, samples));
|
||||
|
||||
// initialize the audio samples to zero (silence)
|
||||
audio->clear();
|
||||
}
|
||||
|
||||
// Constructor - image & audio
|
||||
Frame::Frame(int64_t number, int width, int height, std::string color, int samples, int channels)
|
||||
: number(number), pixel_ratio(1,1), channels(channels), width(width), height(height), color(color),
|
||||
channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
|
||||
: audio(std::make_shared<juce::AudioSampleBuffer>(channels, samples)),
|
||||
number(number), width(width), height(height),
|
||||
pixel_ratio(1,1), color(color), qbuffer(NULL),
|
||||
channels(channels), channel_layout(LAYOUT_STEREO),
|
||||
sample_rate(44100),
|
||||
has_audio_data(false), has_image_data(false),
|
||||
max_audio_sample(0)
|
||||
{
|
||||
// Init the image magic and audio buffer
|
||||
audio = std::shared_ptr<juce::AudioSampleBuffer>(new juce::AudioSampleBuffer(channels, samples));
|
||||
|
||||
// initialize the audio samples to zero (silence)
|
||||
// zero (fill with silence) the audio buffer
|
||||
audio->clear();
|
||||
}
|
||||
|
||||
// Delegating Constructor - blank frame
|
||||
Frame::Frame() : Frame::Frame(1, 1, 1, "#000000", 0, 2) {};
|
||||
|
||||
// Delegating Constructor - image only
|
||||
Frame::Frame(int64_t number, int width, int height, std::string color)
|
||||
: Frame::Frame(number, width, height, color, 0, 2) {};
|
||||
|
||||
// Delegating Constructor - audio only
|
||||
Frame::Frame(int64_t number, int samples, int channels)
|
||||
: Frame::Frame(number, 1, 1, "#000000", samples, channels) {};
|
||||
|
||||
|
||||
// Copy constructor
|
||||
Frame::Frame ( const Frame &other )
|
||||
@@ -120,11 +109,11 @@ void Frame::DeepCopy(const Frame& other)
|
||||
max_audio_sample = other.max_audio_sample;
|
||||
|
||||
if (other.image)
|
||||
image = std::shared_ptr<QImage>(new QImage(*(other.image)));
|
||||
image = std::make_shared<QImage>(*(other.image));
|
||||
if (other.audio)
|
||||
audio = std::shared_ptr<juce::AudioSampleBuffer>(new juce::AudioSampleBuffer(*(other.audio)));
|
||||
audio = std::make_shared<juce::AudioSampleBuffer>(*(other.audio));
|
||||
if (other.wave_image)
|
||||
wave_image = std::shared_ptr<QImage>(new QImage(*(other.wave_image)));
|
||||
wave_image = std::make_shared<QImage>(*(other.wave_image));
|
||||
}
|
||||
|
||||
// Destructor
|
||||
@@ -144,7 +133,7 @@ void Frame::Display()
|
||||
// Only create the QApplication once
|
||||
static int argc = 1;
|
||||
static char* argv[1] = {NULL};
|
||||
previewApp = std::shared_ptr<QApplication>(new QApplication(argc, argv));
|
||||
previewApp = std::make_shared<QApplication>(argc, argv);
|
||||
}
|
||||
|
||||
// Get preview image
|
||||
@@ -158,7 +147,8 @@ void Frame::Display()
|
||||
int new_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble();
|
||||
|
||||
// Resize to fix DAR
|
||||
previewImage = std::shared_ptr<QImage>(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
|
||||
previewImage = std::make_shared<QImage>(previewImage->scaled(
|
||||
new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
|
||||
// Create window
|
||||
@@ -234,7 +224,8 @@ std::shared_ptr<QImage> Frame::GetWaveform(int width, int height, int Red, int G
|
||||
}
|
||||
|
||||
// Create blank image
|
||||
wave_image = std::shared_ptr<QImage>(new QImage(total_width, total_height, QImage::Format_RGBA8888));
|
||||
wave_image = std::make_shared<QImage>(
|
||||
total_width, total_height, QImage::Format_RGBA8888_Premultiplied);
|
||||
wave_image->fill(QColor(0,0,0,0));
|
||||
|
||||
// Load QPainter with wave_image device
|
||||
@@ -259,13 +250,13 @@ std::shared_ptr<QImage> Frame::GetWaveform(int width, int height, int Red, int G
|
||||
// Resize Image (if requested)
|
||||
if (width != total_width || height != total_height) {
|
||||
QImage scaled_wave_image = wave_image->scaled(width, height, Qt::IgnoreAspectRatio, Qt::FastTransformation);
|
||||
wave_image = std::shared_ptr<QImage>(new QImage(scaled_wave_image));
|
||||
wave_image = std::make_shared<QImage>(scaled_wave_image);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No audio samples present
|
||||
wave_image = std::shared_ptr<QImage>(new QImage(width, height, QImage::Format_RGBA8888));
|
||||
wave_image = std::make_shared<QImage>(width, height, QImage::Format_RGBA8888_Premultiplied);
|
||||
wave_image->fill(QColor(QString::fromStdString("#000000")));
|
||||
}
|
||||
|
||||
@@ -300,7 +291,7 @@ void Frame::DisplayWaveform()
|
||||
// Only create the QApplication once
|
||||
static int argc = 1;
|
||||
static char* argv[1] = {NULL};
|
||||
previewApp = std::shared_ptr<QApplication>(new QApplication(argc, argv));
|
||||
previewApp = std::make_shared<QApplication>(argc, argv);
|
||||
}
|
||||
|
||||
// Create window
|
||||
@@ -605,11 +596,15 @@ void Frame::Save(std::string path, float scale, std::string format, int quality)
|
||||
int new_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble();
|
||||
|
||||
// Resize to fix DAR
|
||||
previewImage = std::shared_ptr<QImage>(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
|
||||
previewImage = std::make_shared<QImage>(previewImage->scaled(
|
||||
new_width, new_height,
|
||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
|
||||
// Resize image
|
||||
previewImage = std::shared_ptr<QImage>(new QImage(previewImage->scaled(new_width * scale, new_height * scale, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
|
||||
previewImage = std::make_shared<QImage>(previewImage->scaled(
|
||||
new_width * scale, new_height * scale,
|
||||
Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
|
||||
// Save image
|
||||
@@ -621,7 +616,8 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri
|
||||
std::string background_color, bool ignore_aspect, std::string format, int quality, float rotate) {
|
||||
|
||||
// Create blank thumbnail image & fill background color
|
||||
std::shared_ptr<QImage> thumbnail = std::shared_ptr<QImage>(new QImage(new_width, new_height, QImage::Format_RGBA8888));
|
||||
auto thumbnail = std::make_shared<QImage>(
|
||||
new_width, new_height, QImage::Format_RGBA8888_Premultiplied);
|
||||
thumbnail->fill(QColor(QString::fromStdString(background_color)));
|
||||
|
||||
// Create painter
|
||||
@@ -639,16 +635,22 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri
|
||||
int aspect_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble();
|
||||
|
||||
// Resize to fix DAR
|
||||
previewImage = std::shared_ptr<QImage>(new QImage(previewImage->scaled(aspect_width, aspect_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
|
||||
previewImage = std::make_shared<QImage>(previewImage->scaled(
|
||||
aspect_width, aspect_height,
|
||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
|
||||
// Resize frame image
|
||||
if (ignore_aspect)
|
||||
// Ignore aspect ratio
|
||||
previewImage = std::shared_ptr<QImage>(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
|
||||
previewImage = std::make_shared<QImage>(previewImage->scaled(
|
||||
new_width, new_height,
|
||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
else
|
||||
// Maintain aspect ratio
|
||||
previewImage = std::shared_ptr<QImage>(new QImage(previewImage->scaled(new_width, new_height, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
|
||||
previewImage = std::make_shared<QImage>(previewImage->scaled(
|
||||
new_width, new_height,
|
||||
Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
|
||||
// Composite frame image onto background (centered)
|
||||
int x = (new_width - previewImage->size().width()) / 2.0; // center
|
||||
@@ -672,14 +674,16 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri
|
||||
// Overlay Image (if any)
|
||||
if (overlay_path != "") {
|
||||
// Open overlay
|
||||
std::shared_ptr<QImage> overlay = std::shared_ptr<QImage>(new QImage());
|
||||
auto overlay = std::make_shared<QImage>();
|
||||
overlay->load(QString::fromStdString(overlay_path));
|
||||
|
||||
// Set pixel format
|
||||
overlay = std::shared_ptr<QImage>(new QImage(overlay->convertToFormat(QImage::Format_RGBA8888)));
|
||||
overlay = std::make_shared<QImage>(
|
||||
overlay->convertToFormat(QImage::Format_RGBA8888_Premultiplied));
|
||||
|
||||
// Resize to fit
|
||||
overlay = std::shared_ptr<QImage>(new QImage(overlay->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
|
||||
overlay = std::make_shared<QImage>(overlay->scaled(
|
||||
new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
|
||||
// Composite onto thumbnail
|
||||
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
@@ -690,14 +694,16 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri
|
||||
// Mask Image (if any)
|
||||
if (mask_path != "") {
|
||||
// Open mask
|
||||
std::shared_ptr<QImage> mask = std::shared_ptr<QImage>(new QImage());
|
||||
auto mask = std::make_shared<QImage>();
|
||||
mask->load(QString::fromStdString(mask_path));
|
||||
|
||||
// Set pixel format
|
||||
mask = std::shared_ptr<QImage>(new QImage(mask->convertToFormat(QImage::Format_RGBA8888)));
|
||||
mask = std::make_shared<QImage>(
|
||||
mask->convertToFormat(QImage::Format_RGBA8888_Premultiplied));
|
||||
|
||||
// Resize to fit
|
||||
mask = std::shared_ptr<QImage>(new QImage(mask->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
|
||||
mask = std::make_shared<QImage>(mask->scaled(
|
||||
new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
|
||||
// Negate mask
|
||||
mask->invertPixels();
|
||||
@@ -750,7 +756,7 @@ void Frame::AddColor(int new_width, int new_height, std::string new_color)
|
||||
const GenericScopedLock<juce::CriticalSection> lock(addingImageSection);
|
||||
#pragma omp critical (AddImage)
|
||||
{
|
||||
image = std::shared_ptr<QImage>(new QImage(new_width, new_height, QImage::Format_RGBA8888));
|
||||
image = std::make_shared<QImage>(new_width, new_height, QImage::Format_RGBA8888_Premultiplied);
|
||||
|
||||
// Fill with solid color
|
||||
image->fill(QColor(QString::fromStdString(color)));
|
||||
@@ -762,30 +768,31 @@ void Frame::AddColor(int new_width, int new_height, std::string new_color)
|
||||
}
|
||||
|
||||
// Add (or replace) pixel data to the frame
|
||||
void Frame::AddImage(int new_width, int new_height, int bytes_per_pixel, QImage::Format type, const unsigned char *pixels_)
|
||||
void Frame::AddImage(
|
||||
int new_width, int new_height, int bytes_per_pixel,
|
||||
QImage::Format type, const unsigned char *pixels_)
|
||||
{
|
||||
// Create new buffer
|
||||
const GenericScopedLock<juce::CriticalSection> lock(addingImageSection);
|
||||
int buffer_size = new_width * new_height * bytes_per_pixel;
|
||||
qbuffer = new unsigned char[buffer_size]();
|
||||
|
||||
// Copy buffer data
|
||||
memcpy((unsigned char*)qbuffer, pixels_, buffer_size);
|
||||
|
||||
// Create new image object, and fill with pixel data
|
||||
#pragma omp critical (AddImage)
|
||||
{
|
||||
image = std::shared_ptr<QImage>(new QImage(qbuffer, new_width, new_height, new_width * bytes_per_pixel, type, (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer, (void*) qbuffer));
|
||||
const GenericScopedLock<juce::CriticalSection> lock(addingImageSection);
|
||||
int buffer_size = new_width * new_height * bytes_per_pixel;
|
||||
qbuffer = new unsigned char[buffer_size]();
|
||||
|
||||
// Always convert to RGBA8888 (if different)
|
||||
if (image->format() != QImage::Format_RGBA8888)
|
||||
*image = image->convertToFormat(QImage::Format_RGBA8888);
|
||||
// Copy buffer data
|
||||
memcpy((unsigned char*)qbuffer, pixels_, buffer_size);
|
||||
|
||||
// Update height and width
|
||||
width = image->width();
|
||||
height = image->height();
|
||||
has_image_data = true;
|
||||
}
|
||||
} // Release addingImageSection lock
|
||||
|
||||
// Create new image object from pixel data
|
||||
auto new_image = std::make_shared<QImage>(
|
||||
qbuffer,
|
||||
new_width, new_height,
|
||||
new_width * bytes_per_pixel,
|
||||
type,
|
||||
(QImageCleanupFunction) &openshot::Frame::cleanUpBuffer,
|
||||
(void*) qbuffer
|
||||
);
|
||||
AddImage(new_image);
|
||||
}
|
||||
|
||||
// Add (or replace) pixel data to the frame
|
||||
@@ -801,9 +808,9 @@ void Frame::AddImage(std::shared_ptr<QImage> new_image)
|
||||
{
|
||||
image = new_image;
|
||||
|
||||
// Always convert to RGBA8888 (if different)
|
||||
if (image->format() != QImage::Format_RGBA8888)
|
||||
*image = image->convertToFormat(QImage::Format_RGBA8888);
|
||||
// Always convert to Format_RGBA8888_Premultiplied (if different)
|
||||
if (image->format() != QImage::Format_RGBA8888_Premultiplied)
|
||||
*image = image->convertToFormat(QImage::Format_RGBA8888_Premultiplied);
|
||||
|
||||
// Update height and width
|
||||
width = image->width();
|
||||
@@ -825,7 +832,6 @@ void Frame::AddImage(std::shared_ptr<QImage> new_image, bool only_odd_lines)
|
||||
AddImage(new_image);
|
||||
|
||||
} else {
|
||||
|
||||
// Ignore image of different sizes or formats
|
||||
bool ret=false;
|
||||
#pragma omp critical (AddImage)
|
||||
@@ -833,8 +839,9 @@ void Frame::AddImage(std::shared_ptr<QImage> new_image, bool only_odd_lines)
|
||||
if (image == new_image || image->size() != new_image->size()) {
|
||||
ret = true;
|
||||
}
|
||||
else if (new_image->format() != image->format()) {
|
||||
new_image = std::shared_ptr<QImage>(new QImage(new_image->convertToFormat(image->format())));
|
||||
else if (new_image->format() != QImage::Format_RGBA8888_Premultiplied) {
|
||||
new_image = std::make_shared<QImage>(
|
||||
new_image->convertToFormat(QImage::Format_RGBA8888_Premultiplied));
|
||||
}
|
||||
}
|
||||
if (ret) {
|
||||
@@ -992,7 +999,8 @@ std::shared_ptr<Magick::Image> Frame::GetMagickImage()
|
||||
const QRgb *tmpBits = (const QRgb*)image->constBits();
|
||||
|
||||
// Create new image object, and fill with pixel data
|
||||
std::shared_ptr<Magick::Image> magick_image = std::shared_ptr<Magick::Image>(new Magick::Image(image->width(), image->height(),"RGBA", Magick::CharPixel, tmpBits));
|
||||
auto magick_image = std::make_shared<Magick::Image>(
|
||||
image->width(), image->height(),"RGBA", Magick::CharPixel, tmpBits);
|
||||
|
||||
// Give image a transparent background color
|
||||
magick_image->backgroundColor(Magick::Color("none"));
|
||||
@@ -1021,7 +1029,9 @@ void Frame::AddMagickImage(std::shared_ptr<Magick::Image> new_image)
|
||||
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::shared_ptr<QImage>(new QImage(qbuffer, width, height, width * BPP, QImage::Format_RGBA8888, (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer));
|
||||
image = std::make_shared<QImage>(
|
||||
qbuffer, width, height, width * BPP, QImage::Format_RGBA8888_Premultiplied,
|
||||
(QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer);
|
||||
|
||||
// Update height and width
|
||||
width = image->width();
|
||||
|
||||
Reference in New Issue
Block a user