From 1743558f99438f263bca4ef59a9dd1aa520b2a68 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 17 Sep 2016 17:14:27 -0500 Subject: [PATCH] Improved image caching logic, to better estimate max image sizes possible, based on clip scale and keyframe settings... so we are always dealing with the smallest possible frame sizes for performance (without losing quality) --- include/KeyFrame.h | 3 +++ src/Clip.cpp | 31 +++++++++++++++++++++++++------ src/KeyFrame.cpp | 19 +++++++++++++++++++ src/QtImageReader.cpp | 5 +++++ tests/KeyFrame_Tests.cpp | 26 ++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 6 deletions(-) diff --git a/include/KeyFrame.h b/include/KeyFrame.h index 5a5565a0..8a3fcfd5 100644 --- a/include/KeyFrame.h +++ b/include/KeyFrame.h @@ -141,6 +141,9 @@ namespace openshot { /// Get current point (or closest point) from the X coordinate (i.e. the frame number) Point GetClosestPoint(Point p); + /// Get max point (by Y coordinate) + Point GetMaxPoint(); + // Get the number of values (i.e. coordinates on the X axis) long int GetLength(); diff --git a/src/Clip.cpp b/src/Clip.cpp index a93db20a..2ce820e9 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -597,13 +597,32 @@ tr1::shared_ptr Clip::GetOrCreateFrame(long int number) // Debug output ZmqLogger::Instance()->AppendDebugMethod("Clip::GetOrCreateFrame (from reader)", "number", number, "samples_in_frame", samples_in_frame, "", -1, "", -1, "", -1, "", -1); - // Set max image size (used for performance optimization) - if (scale_x.GetValue(number) > 1.000001 || scale_y.GetValue(number) > 1.000001) - // Scaling larger, use original image size (slower but better quality) + // Determine the max size of this clips source image (based on the timeline's size, the scaling mode, + // and the scaling keyframes). This is a performance improvement, to keep the images as small as possible, + // without loosing quality. + if (scale == SCALE_FIT || scale == SCALE_STRETCH) { + // Best fit or Stretch scaling (based on max timeline size * scaling keyframes) + float max_scale_x = scale_x.GetMaxPoint().co.Y; + float max_scale_y = scale_y.GetMaxPoint().co.Y; + reader->SetMaxSize(max_width * max_scale_x, max_height * max_scale_y); + + } else if (scale == SCALE_CROP) { + // Cropping scale mode (based on max timeline size * cropped size * scaling keyframes) + float max_scale_x = scale_x.GetMaxPoint().co.Y; + float max_scale_y = scale_y.GetMaxPoint().co.Y; + QSize width_size(max_width * max_scale_x, round(max_width / (float(reader->info.width) / float(reader->info.height)))); + QSize height_size(round(max_height / (float(reader->info.height) / float(reader->info.width))), max_height * max_scale_y); + + // respect aspect ratio + if (width_size.width() >= max_width && width_size.height() >= max_height) + reader->SetMaxSize(width_size.width(), width_size.height()); + else + reader->SetMaxSize(height_size.width(), height_size.height()); + + } else { + // No scaling, use original image size (slower) reader->SetMaxSize(0, 0); - else - // No scaling applied, use max_size (usually the size of the timeline) - reader->SetMaxSize(max_width, max_height); + } // Attempt to get a frame (but this could fail if a reader has just been closed) new_frame = reader->GetFrame(number); diff --git a/src/KeyFrame.cpp b/src/KeyFrame.cpp index 6c3777df..4bb4b062 100644 --- a/src/KeyFrame.cpp +++ b/src/KeyFrame.cpp @@ -220,6 +220,25 @@ Point Keyframe::GetClosestPoint(Point p) { return closest; } +// Get max point (by Y coordinate) +Point Keyframe::GetMaxPoint() { + Point maxPoint(-1, -1); + + // loop through points, and find the largest Y value + for (long int x = 0; x < Points.size(); x++) { + // Get each point + Point existing_point = Points[x]; + + // Is point larger than max point + if (existing_point.co.Y >= maxPoint.co.Y) { + // New max point found + maxPoint = existing_point; + } + } + + return maxPoint; +} + // Get the value at a specific index float Keyframe::GetValue(long int index) { diff --git a/src/QtImageReader.cpp b/src/QtImageReader.cpp index 5bfecc59..8ee56374 100644 --- a/src/QtImageReader.cpp +++ b/src/QtImageReader.cpp @@ -123,6 +123,11 @@ tr1::shared_ptr QtImageReader::GetFrame(long int requested_frame) throw(R // A max_width/max_height = 0 means do not scale (probably because we are scaling the image larger than 100%) if (max_width != 0 && max_height != 0 && max_width < info.width && max_height < info.height) { + // Remove cache that is no longer valid (if needed) + if (cached_image && !(cached_image->width() == max_width || cached_image->height() == max_height)) + // Expire this cache + cached_image.reset(); + // Scale image smaller (or use a previous scaled image) if (!cached_image) { // Create a scoped lock, allowing only a single thread to run the following code at one time diff --git a/tests/KeyFrame_Tests.cpp b/tests/KeyFrame_Tests.cpp index 4d970465..0a8739c2 100644 --- a/tests/KeyFrame_Tests.cpp +++ b/tests/KeyFrame_Tests.cpp @@ -244,6 +244,32 @@ TEST(Keyframe_Get_Closest_Point) } + +TEST(Keyframe_Get_Max_Point) +{ + // Create a keyframe curve + Keyframe kf; + kf.AddPoint(1, 1.0); + + // Spot check values from the curve + CHECK_EQUAL(kf.GetMaxPoint().co.Y, 1.0); + + kf.AddPoint(2, 0.0); + + // Spot check values from the curve + CHECK_EQUAL(kf.GetMaxPoint().co.Y, 1.0); + + kf.AddPoint(3, 2.0); + + // Spot check values from the curve + CHECK_EQUAL(kf.GetMaxPoint().co.Y, 2.0); + + kf.AddPoint(4, 1.0); + + // Spot check values from the curve + CHECK_EQUAL(kf.GetMaxPoint().co.Y, 2.0); +} + TEST(Keyframe_Scale_Keyframe) { // Create a keyframe curve with 2 points