Files
libopenshot/tests/Crop.cpp

175 lines
5.2 KiB
C++

/**
* @file
* @brief Unit tests for openshot::Crop effect
* @author Jonathan Thomas <jonathan@openshot.org>
* @author FeRD (Frank Dana) <ferdnyc@gmail.com>
*
* @ref License
*/
// Copyright (c) 2008-2025 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include <memory>
#include "openshot_catch.h"
#include "Frame.h"
#include "Clip.h"
#include "effects/Crop.h"
#include "effects/CropHelpers.h"
#include <QColor>
#include <QImage>
#include <QPainter>
#include <QSize>
TEST_CASE( "default constructor", "[libopenshot][effect][crop]" )
{
// solid green frame
auto f = std::make_shared<openshot::Frame>(1, 1280, 720, "#00ff00");
// Default constructor should have no cropping
openshot::Crop e;
auto f_out = e.GetFrame(f, 1);
std::shared_ptr<QImage> i = f_out->GetImage();
// Check pixels near edges (should all be green)
std::vector<QColor> pixels {
i->pixelColor(400, 2),
i->pixelColor(1279, 500),
i->pixelColor(800, 718),
i->pixelColor(1, 200)
};
QColor green{Qt::green};
CHECK(pixels[0] == green);
CHECK(pixels[1] == green);
CHECK(pixels[2] == green);
CHECK(pixels[3] == green);
}
TEST_CASE( "basic cropping", "[libopenshot][effect][crop]" )
{
auto frame = std::make_shared<openshot::Frame>(1, 1280, 720, "#00ff00");
// Crop 10% off the input frame on all four sides
openshot::Keyframe left(0.1);
openshot::Keyframe top(0.1);
openshot::Keyframe right(0.1);
openshot::Keyframe bottom(0.1);
openshot::Crop e(left, top, right, bottom);
auto frame_out = e.GetFrame(frame, 1);
std::shared_ptr<QImage> i = frame_out->GetImage();
QSize sz(1280, 720);
CHECK(i->size() == sz);
// Green inside the crop region, transparent outside
QColor green{Qt::green};
QColor trans{Qt::transparent};
QColor center_pixel = i->pixelColor(640, 360);
CHECK(center_pixel == green);
std::vector<QColor> edge_pixels {
i->pixelColor(50, 200),
i->pixelColor(400, 20),
i->pixelColor(1250, 500),
i->pixelColor(800, 715)
};
CHECK(edge_pixels[0] == trans);
CHECK(edge_pixels[1] == trans);
CHECK(edge_pixels[2] == trans);
CHECK(edge_pixels[3] == trans);
}
TEST_CASE( "region collapsing", "[libopenshot][effect][crop]" )
{
auto frame = std::make_shared<openshot::Frame>(1, 1920, 1080, "#ff00ff");
// Crop 50% off left and right sides (== crop out entire image)
openshot::Keyframe left(0.4);
openshot::Keyframe right(0.6);
openshot::Keyframe none(0.0);
openshot::Crop e(left, none, right, none);
auto frame_out = e.GetFrame(frame, 1);
auto i = frame_out->GetImage();
// Only true if all pixels have been cropped away (as expected)
CHECK(i->allGray());
}
TEST_CASE( "x/y offsets", "[libopenshot][effect][crop]" )
{
auto frame = std::make_shared<openshot::Frame>(1, 1280, 720, "#ff0000");
auto frame_img = frame->GetImage();
QImage img(*frame_img);
// Make input frame left-half red, right-half blue
QPainter p(&img);
p.fillRect(QRect(640, 0, 640, 720), Qt::blue);
p.end();
frame->AddImage(std::make_shared<QImage>(img));
// Crop 20% off all four sides, and shift the source window x +33⅓ %
openshot::Keyframe sides(0.2);
openshot::Keyframe x(0.3);
openshot::Crop e(sides, sides, sides, sides, x);
auto frame_out = e.GetFrame(frame, 1);
std::shared_ptr<QImage> i = frame_out->GetImage();
// Entire cropped region should be blue (due to x-offset), and will be
// off-center (due to being only 50% wide instead of 60%)
QColor blue{Qt::blue};
QColor trans{Qt::transparent};
std::vector<QColor> edge_pixels {
i->pixelColor(258, 146),
i->pixelColor(894, 146),
i->pixelColor(894, 574),
i->pixelColor(258, 574)
};
CHECK(edge_pixels[0] == blue);
CHECK(edge_pixels[1] == blue);
CHECK(edge_pixels[2] == blue);
CHECK(edge_pixels[3] == blue);
// This pixel would normally be inside the cropping.
// The x-offset moves it outside of the source image area,
// so it becomes a transparent pixel
CHECK(i->pixelColor(900, 360) == trans);
}
TEST_CASE( "crop resize scaling helper", "[libopenshot][effect][crop][scaling]" )
{
// Build a clip with a resizing crop effect
auto clip = std::make_unique<openshot::Clip>();
auto crop = new openshot::Crop(openshot::Keyframe(0.0), openshot::Keyframe(0.0),
openshot::Keyframe(0.0), openshot::Keyframe(0.75));
crop->resize = true;
clip->AddEffect(crop);
int max_width = 900;
int max_height = 500;
// 25% of the height is visible, so request 4x height (capped at source size)
openshot::ApplyCropResizeScale(clip.get(), 3840, 2160, max_width, max_height);
CHECK(max_width == 900);
CHECK(max_height == 2000);
// If scaling would exceed source, it should clamp at source dimensions
crop->bottom = openshot::Keyframe(0.9); // visible height = 10%
max_width = 900;
max_height = 500;
openshot::ApplyCropResizeScale(clip.get(), 1920, 1080, max_width, max_height);
CHECK(max_width == 900); // width unchanged (no horizontal crop)
CHECK(max_height == 1080); // scaled height would be 5000, so it clamps
}