You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Added gravity, scale type, and percentage based X,Y location settings. All the basic transformation methods are now working. You can layer, move, fade, snap, rotate, and scale clips on the screen.
This commit is contained in:
@@ -205,10 +205,10 @@ void FFmpegReader::UpdateAudioInfo()
|
||||
if (!info.has_video)
|
||||
{
|
||||
// Set a few important default video settings (so audio can be divided into frames)
|
||||
info.fps.num = 30;
|
||||
info.fps.num = 24;
|
||||
info.fps.den = 1;
|
||||
info.video_timebase.num = 1;
|
||||
info.video_timebase.den = 30;
|
||||
info.video_timebase.den = 24;
|
||||
info.video_length = info.duration * info.fps.ToDouble();
|
||||
|
||||
}
|
||||
|
||||
57
src/Main.cpp
57
src/Main.cpp
@@ -31,32 +31,52 @@ int main()
|
||||
Timeline t(640, 360, Framerate(24,1), 44100, 2);
|
||||
|
||||
// Add some clips
|
||||
Clip c1(new FFmpegReader("/home/jonathan/Videos/sintel_trailer-720p.mp4"));
|
||||
Clip c2(new ImageReader("/home/jonathan/apps/videcho_site/media/logos/watermark.png"));
|
||||
c1.Position(1.0);
|
||||
c2.Position(1.0);
|
||||
Clip c1(new FFmpegReader("/home/jonathan/Videos/sintel-1024-stereo.mp4"));
|
||||
//Clip c2(new ImageReader("/home/jonathan/Desktop/watermark.svg"));
|
||||
Clip c2(new ImageReader("/home/jonathan/Desktop/icon.png"));
|
||||
//Clip c3(new FFmpegReader("/home/jonathan/Desktop/IncognitoCory_-_April_Song.mp3"));
|
||||
c1.Position(0.0);
|
||||
c1.gravity = GRAVITY_CENTER;
|
||||
c1.scale = SCALE_CROP;
|
||||
c1.End(20);
|
||||
|
||||
c2.Position(0.0);
|
||||
c2.Layer(1);
|
||||
c2.gravity = GRAVITY_BOTTOM_RIGHT;
|
||||
c2.scale = SCALE_NONE;
|
||||
c2.End(20);
|
||||
|
||||
c1.rotation.AddPoint(1, 1);
|
||||
c1.rotation.AddPoint(300, 360);
|
||||
//c1.rotation.AddPoint(1, 1);
|
||||
//c1.rotation.AddPoint(300, 360);
|
||||
|
||||
//c1.alpha.AddPoint(1, 0);
|
||||
//c1.alpha.AddPoint(300, 1);
|
||||
//c2.scale_x.AddPoint(1, 1);
|
||||
//c2.scale_x.AddPoint(300, 3.5);
|
||||
|
||||
c2.alpha.AddPoint(1, 1);
|
||||
c2.alpha.AddPoint(30, 0);
|
||||
c2.alpha.AddPoint(100, 0, LINEAR);
|
||||
c2.alpha.AddPoint(150, 1);
|
||||
c2.End(6.25);
|
||||
//c2.scale_y.AddPoint(1, 1);
|
||||
//c2.scale_y.AddPoint(300, 3.5);
|
||||
|
||||
//c1.scale_x.AddPoint(1, 1);
|
||||
//c1.scale_x.AddPoint(300, 1.5);
|
||||
|
||||
//c1.scale_y.AddPoint(1, 1);
|
||||
//c1.scale_y.AddPoint(300, 1.5);
|
||||
|
||||
//c1.alpha.AddPoint(1, 1);
|
||||
//c1.alpha.AddPoint(30, 0);
|
||||
|
||||
//c2.alpha.AddPoint(1, 1);
|
||||
//c2.alpha.AddPoint(30, 0);
|
||||
//c2.alpha.AddPoint(100, 0, LINEAR);
|
||||
//c2.alpha.AddPoint(150, 1);
|
||||
|
||||
c2.location_x.AddPoint(1, 0);
|
||||
c2.location_x.AddPoint(300, 530);
|
||||
c2.location_x.AddPoint(300, -1.0);
|
||||
|
||||
c2.location_y.AddPoint(1, 0);
|
||||
c2.location_y.AddPoint(300, 300);
|
||||
//c2.location_y.AddPoint(1, 0);
|
||||
//c2.location_y.AddPoint(300, 1);
|
||||
|
||||
c2.rotation.AddPoint(60, 1, LINEAR);
|
||||
c2.rotation.AddPoint(150, 360);
|
||||
//c2.rotation.AddPoint(1, 0);
|
||||
//c2.rotation.AddPoint(300, 360);
|
||||
|
||||
|
||||
// LINEAR Reverse
|
||||
@@ -105,6 +125,7 @@ int main()
|
||||
// Add clips
|
||||
t.AddClip(&c1);
|
||||
t.AddClip(&c2);
|
||||
//t.AddClip(&c3);
|
||||
|
||||
|
||||
// Create a writer
|
||||
|
||||
109
src/Timeline.cpp
109
src/Timeline.cpp
@@ -43,42 +43,109 @@ void Timeline::add_layer(tr1::shared_ptr<Frame> new_frame, Clip* source_clip, in
|
||||
tr1::shared_ptr<Frame> source_frame = source_clip->GetFrame(clip_frame_number);
|
||||
tr1::shared_ptr<Magick::Image> source_image = source_frame->GetImage();
|
||||
|
||||
// Replace image (needed if this is the 1st layer)
|
||||
// Get some basic image properties
|
||||
int source_width = source_image->columns();
|
||||
int source_height = source_image->rows();
|
||||
|
||||
|
||||
/* CREATE BACKGROUND COLOR - needed if this is the 1st layer */
|
||||
if (new_frame->GetImage()->columns() == 1)
|
||||
new_frame->AddColor(width, height, "#000000");
|
||||
|
||||
// Apply image effects
|
||||
//if (source_clip->rotation.GetValue(clip_frame_number) != 0)
|
||||
// source_image->rotate(source_clip->rotation.GetValue(clip_frame_number));
|
||||
/* COPY AUDIO */
|
||||
for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++)
|
||||
new_frame->AddAudio(channel, 0, source_frame->GetAudioSamples(channel), source_frame->GetAudioSamplesCount(), 1.0f);
|
||||
|
||||
/* ALPHA & OPACITY */
|
||||
if (source_clip->alpha.GetValue(clip_frame_number) != 0)
|
||||
{
|
||||
// Calculate opacity of new image
|
||||
// Calculate & set opacity of new image
|
||||
int new_opacity = 65535.0f * source_clip->alpha.GetValue(clip_frame_number);
|
||||
if (new_opacity < 0) new_opacity = 0; // completely invisible
|
||||
source_image->opacity(new_opacity);
|
||||
}
|
||||
|
||||
// Copy audio from source frame
|
||||
for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++)
|
||||
new_frame->AddAudio(channel, 0, source_frame->GetAudioSamples(channel), source_frame->GetAudioSamplesCount(), 1.0f);
|
||||
/* RESIZE SOURCE IMAGE - based on scale type */
|
||||
Magick::Geometry new_size(width, height);
|
||||
switch (source_clip->scale)
|
||||
{
|
||||
case (SCALE_FIT):
|
||||
new_size.aspect(false); // respect aspect ratio
|
||||
source_image->resize(new_size);
|
||||
source_width = source_image->size().width();
|
||||
source_height = source_image->size().height();
|
||||
break;
|
||||
case (SCALE_STRETCH):
|
||||
new_size.aspect(true); // ignore aspect ratio
|
||||
source_image->resize(new_size);
|
||||
source_width = source_image->size().width();
|
||||
source_height = source_image->size().height();
|
||||
break;
|
||||
case (SCALE_CROP):
|
||||
Magick::Geometry width_size(width, round(width / (float(source_width) / float(source_height))));
|
||||
Magick::Geometry height_size(round(height / (float(source_height) / float(source_width))), height);
|
||||
new_size.aspect(false); // respect aspect ratio
|
||||
if (width_size.width() >= width && width_size.height() >= height)
|
||||
source_image->resize(width_size); // width is larger, so resize to it
|
||||
else
|
||||
source_image->resize(height_size); // height is larger, so resize to it
|
||||
source_width = source_image->size().width();
|
||||
source_height = source_image->size().height();
|
||||
break;
|
||||
}
|
||||
|
||||
// Location, Rotation, and Scale
|
||||
float r = source_clip->rotation.GetValue(clip_frame_number);
|
||||
float x = source_clip->location_x.GetValue(clip_frame_number);
|
||||
float y = source_clip->location_y.GetValue(clip_frame_number);
|
||||
float sx = source_clip->scale_x.GetValue(clip_frame_number);
|
||||
float sy = source_clip->scale_y.GetValue(clip_frame_number);
|
||||
/* GRAVITY LOCATION - Initialize X & Y to the correct values (before applying location curves) */
|
||||
float x = 0.0; // left
|
||||
float y = 0.0; // top
|
||||
switch (source_clip->gravity)
|
||||
{
|
||||
case (GRAVITY_TOP):
|
||||
x = (width - source_width) / 2.0; // center
|
||||
break;
|
||||
case (GRAVITY_TOP_RIGHT):
|
||||
x = width - source_width; // right
|
||||
break;
|
||||
case (GRAVITY_LEFT):
|
||||
y = (height - source_height) / 2.0; // center
|
||||
break;
|
||||
case (GRAVITY_CENTER):
|
||||
x = (width - source_width) / 2.0; // center
|
||||
y = (height - source_height) / 2.0; // center
|
||||
break;
|
||||
case (GRAVITY_RIGHT):
|
||||
x = width - source_width; // right
|
||||
y = (height - source_height) / 2.0; // center
|
||||
break;
|
||||
case (GRAVITY_BOTTOM_LEFT):
|
||||
y = (height - source_height); // bottom
|
||||
break;
|
||||
case (GRAVITY_BOTTOM):
|
||||
x = (width - source_width) / 2.0; // center
|
||||
y = (height - source_height); // bottom
|
||||
break;
|
||||
case (GRAVITY_BOTTOM_RIGHT):
|
||||
x = width - source_width; // right
|
||||
y = (height - source_height); // bottom
|
||||
break;
|
||||
}
|
||||
|
||||
// Resize source canvas the same size as timeline canvas
|
||||
Magick::Geometry source_original_size = source_image->size();
|
||||
source_image->size(Magick::Geometry(width, height, 0,0,false,false));
|
||||
/* RESIZE SOURCE CANVAS - to the same size as timeline canvas */
|
||||
source_image->borderColor(Magick::Color("none"));
|
||||
source_image->border(Magick::Geometry(1, 1, 0, 0, false, false)); // prevent stretching of edge pixels (during the canvas resize)
|
||||
source_image->size(Magick::Geometry(width, height, 0, 0, false, false)); // resize the canvas (to prevent clipping)
|
||||
|
||||
// Set the location (X,Y), rotation, and X-Scale, Y-Scale of the source image
|
||||
// X,Y Scale Angle NewX,NewY
|
||||
double distort_args[7] = {source_original_size.width()/2,source_original_size.height()/2, sx,sy, r, x,y };
|
||||
/* LOCATION, ROTATION, AND SCALE */
|
||||
float r = source_clip->rotation.GetValue(clip_frame_number); // rotate in degrees
|
||||
x += width * source_clip->location_x.GetValue(clip_frame_number); // move in percentage of final width
|
||||
y += height * source_clip->location_y.GetValue(clip_frame_number); // move in percentage of final height
|
||||
float sx = source_clip->scale_x.GetValue(clip_frame_number); // percentage X scale
|
||||
float sy = source_clip->scale_y.GetValue(clip_frame_number); // percentage Y scale
|
||||
|
||||
// origin X,Y Scale Angle NewX,NewY
|
||||
double distort_args[7] = {0,0, sx,sy, r, x-1,y-1 };
|
||||
source_image->distort(Magick::ScaleRotateTranslateDistortion, 7, distort_args, false);
|
||||
|
||||
// Composite images together
|
||||
/* COMPOSITE SOURCE IMAGE (LAYER) ONTO FINAL IMAGE */
|
||||
tr1::shared_ptr<Magick::Image> new_image = new_frame->GetImage();
|
||||
new_image->composite(*source_image.get(), 0, 0, Magick::BlendCompositeOp);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user