Skip to content

Commit 5a8ffe3

Browse files
authored
Merge pull request #724 from OpenShot/improved-crop-effect
Adding an X and Y offset to the current crop effect (large refactor)
2 parents d58febf + 5f91ddc commit 5a8ffe3

File tree

2 files changed

+68
-27
lines changed

2 files changed

+68
-27
lines changed

src/effects/Crop.cpp

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @file
3-
* @brief Source file for Crop effect class
3+
* @brief Source file for Crop effect class (cropping any side, with x/y offsets)
44
* @author Jonathan Thomas <[email protected]>
55
*
66
* @ref License
@@ -34,14 +34,14 @@
3434
using namespace openshot;
3535

3636
/// Blank constructor, useful when using Json to load the effect properties
37-
Crop::Crop() : left(0.0), top(0.0), right(0.0), bottom(0.0) {
37+
Crop::Crop() : left(0.0), top(0.0), right(0.0), bottom(0.0), x(0.0), y(0.0) {
3838
// Init effect properties
3939
init_effect_details();
4040
}
4141

4242
// Default constructor
4343
Crop::Crop(Keyframe left, Keyframe top, Keyframe right, Keyframe bottom) :
44-
left(left), top(top), right(right), bottom(bottom)
44+
left(left), top(top), right(right), bottom(bottom), x(0.0), y(0.0)
4545
{
4646
// Init effect properties
4747
init_effect_details();
@@ -68,48 +68,80 @@ std::shared_ptr<openshot::Frame> Crop::GetFrame(std::shared_ptr<openshot::Frame>
6868
// Get the frame's image
6969
std::shared_ptr<QImage> frame_image = frame->GetImage();
7070

71-
// Get transparent color (and create small transparent image)
72-
auto tempColor = std::make_shared<QImage>(
73-
frame_image->width(), 1, QImage::Format_RGBA8888_Premultiplied);
74-
tempColor->fill(QColor(QString::fromStdString("transparent")));
71+
// Get transparent color target image (which will become the cropped image)
72+
auto cropped_image = std::make_shared<QImage>(
73+
frame_image->width(), frame_image->height(), QImage::Format_RGBA8888_Premultiplied);
74+
cropped_image->fill(QColor(QString::fromStdString("transparent")));
7575

7676
// Get current keyframe values
7777
double left_value = left.GetValue(frame_number);
7878
double top_value = top.GetValue(frame_number);
7979
double right_value = right.GetValue(frame_number);
8080
double bottom_value = bottom.GetValue(frame_number);
8181

82+
// Get the current shift amount (if any... to slide the image around in the cropped area)
83+
double x_shift = x.GetValue(frame_number);
84+
double y_shift = y.GetValue(frame_number);
85+
8286
// Get pixel array pointers
8387
unsigned char *pixels = (unsigned char *) frame_image->bits();
84-
unsigned char *color_pixels = (unsigned char *) tempColor->bits();
88+
unsigned char *cropped_pixels = (unsigned char *) cropped_image->bits();
8589

8690
// Get pixels sizes of all crop sides
8791
int top_bar_height = top_value * frame_image->height();
8892
int bottom_bar_height = bottom_value * frame_image->height();
8993
int left_bar_width = left_value * frame_image->width();
9094
int right_bar_width = right_value * frame_image->width();
91-
92-
// Loop through rows
95+
int column_offset = x_shift * frame_image->width();
96+
int row_offset = y_shift * frame_image->height();
97+
98+
// Image copy variables
99+
int image_width = frame_image->width();
100+
int src_start = left_bar_width;
101+
int dst_start = left_bar_width;
102+
int copy_length = frame_image->width() - right_bar_width - left_bar_width;
103+
104+
// Adjust for x offset
105+
int copy_offset = 0;
106+
107+
if (column_offset < 0) {
108+
// dest to the right
109+
src_start += column_offset;
110+
if (src_start < 0) {
111+
int diff = 0 - src_start; // how far under 0 are we?
112+
src_start = 0;
113+
dst_start += diff;
114+
copy_offset = -diff;
115+
} else {
116+
copy_offset = 0;
117+
}
118+
119+
} else {
120+
// dest to the left
121+
src_start += column_offset;
122+
if (image_width - src_start >= copy_length) {
123+
// We have plenty pixels, use original copy-length
124+
copy_offset = 0;
125+
} else {
126+
// We don't have enough pixels, shorten copy-length
127+
copy_offset = (image_width - src_start) - copy_length;
128+
}
129+
}
130+
131+
// Loop through rows of pixels
93132
for (int row = 0; row < frame_image->height(); row++) {
94-
95-
// Top & Bottom Crop
96-
if ((top_bar_height > 0.0 && row <= top_bar_height) || (bottom_bar_height > 0.0 && row >= frame_image->height() - bottom_bar_height)) {
97-
memcpy(&pixels[row * frame_image->width() * 4], color_pixels, sizeof(char) * frame_image->width() * 4);
98-
} else {
99-
// Left Crop
100-
if (left_bar_width > 0.0) {
101-
memcpy(&pixels[row * frame_image->width() * 4], color_pixels, sizeof(char) * left_bar_width * 4);
102-
}
103-
104-
// Right Crop
105-
if (right_bar_width > 0.0) {
106-
memcpy(&pixels[((row * frame_image->width()) + (frame_image->width() - right_bar_width)) * 4], color_pixels, sizeof(char) * right_bar_width * 4);
107-
}
133+
int adjusted_row = row - row_offset;
134+
// Is this row visible?
135+
if (adjusted_row >= top_bar_height && adjusted_row < (frame_image->height() - bottom_bar_height) && (copy_length + copy_offset > 0)) {
136+
// Copy image (row by row, with offsets for x and y offset, and src/dst starting points for column filtering)
137+
memcpy(&cropped_pixels[((adjusted_row * frame_image->width()) + dst_start) * 4],
138+
&pixels[((row * frame_image->width()) + src_start) * 4],
139+
sizeof(char) * (copy_length + copy_offset) * 4);
108140
}
109141
}
110142

111-
// Cleanup colors and arrays
112-
tempColor.reset();
143+
// Set frame image
144+
frame->AddImage(cropped_image);
113145

114146
// return the modified frame
115147
return frame;
@@ -132,6 +164,8 @@ Json::Value Crop::JsonValue() const {
132164
root["top"] = top.JsonValue();
133165
root["right"] = right.JsonValue();
134166
root["bottom"] = bottom.JsonValue();
167+
root["x"] = x.JsonValue();
168+
root["y"] = y.JsonValue();
135169

136170
// return JsonValue
137171
return root;
@@ -169,6 +203,10 @@ void Crop::SetJsonValue(const Json::Value root) {
169203
right.SetJsonValue(root["right"]);
170204
if (!root["bottom"].isNull())
171205
bottom.SetJsonValue(root["bottom"]);
206+
if (!root["x"].isNull())
207+
x.SetJsonValue(root["x"]);
208+
if (!root["y"].isNull())
209+
y.SetJsonValue(root["y"]);
172210
}
173211

174212
// Get all properties for a specific frame
@@ -188,6 +226,8 @@ std::string Crop::PropertiesJSON(int64_t requested_frame) const {
188226
root["top"] = add_property_json("Top Size", top.GetValue(requested_frame), "float", "", &top, 0.0, 1.0, false, requested_frame);
189227
root["right"] = add_property_json("Right Size", right.GetValue(requested_frame), "float", "", &right, 0.0, 1.0, false, requested_frame);
190228
root["bottom"] = add_property_json("Bottom Size", bottom.GetValue(requested_frame), "float", "", &bottom, 0.0, 1.0, false, requested_frame);
229+
root["x"] = add_property_json("X Offset", x.GetValue(requested_frame), "float", "", &x, -1.0, 1.0, false, requested_frame);
230+
root["y"] = add_property_json("Y Offset", y.GetValue(requested_frame), "float", "", &y, -1.0, 1.0, false, requested_frame);
191231

192232
// Set the parent effect which properties this effect will inherit
193233
root["parent_effect_id"] = add_property_json("Parent", 0.0, "string", info.parent_effect_id, NULL, -1, -1, false, requested_frame);

src/effects/Crop.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,12 @@ namespace openshot
6060

6161

6262
public:
63-
Color color; ///< Color of bars
6463
Keyframe left; ///< Size of left bar
6564
Keyframe top; ///< Size of top bar
6665
Keyframe right; ///< Size of right bar
6766
Keyframe bottom; ///< Size of bottom bar
67+
Keyframe x; ///< X-offset
68+
Keyframe y; ///< Y-offset
6869

6970
/// Blank constructor, useful when using Json to load the effect properties
7071
Crop();

0 commit comments

Comments
 (0)