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
3434using 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
4343Crop::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);
0 commit comments