From d47a94f1a08db85a70d423570dcc04324edfccd2 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Mon, 12 Nov 2012 01:25:35 -0600 Subject: [PATCH] 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. --- src/FFmpegReader.cpp | 4 +- src/Main.cpp | 57 +++++++++++++++------- src/Timeline.cpp | 109 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 129 insertions(+), 41 deletions(-) diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 7160f24a..2b0ff5cf 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -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(); } diff --git a/src/Main.cpp b/src/Main.cpp index b29d9452..b8ec90be 100644 --- a/src/Main.cpp +++ b/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 diff --git a/src/Timeline.cpp b/src/Timeline.cpp index eb752503..cc6360d8 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -43,42 +43,109 @@ void Timeline::add_layer(tr1::shared_ptr new_frame, Clip* source_clip, in tr1::shared_ptr source_frame = source_clip->GetFrame(clip_frame_number); tr1::shared_ptr 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 new_image = new_frame->GetImage(); new_image->composite(*source_image.get(), 0, 0, Magick::BlendCompositeOp); }