WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Conversation

@aperture147
Copy link
Contributor

@aperture147 aperture147 commented Mar 21, 2025

Currently there are no drop shadow effect implemented in libopenshot. To achieve that, people usually do this trick:

  1. Duplicate the clip that requires shadow dropping
  2. Use Brightness effect to make the duplicated clip pitch black
  3. Use Blur effect to make the black clip under looked like a spread out shadow
  4. Place the Clip below and a bit offset of the original clip

This approach has two disadvantages:

  1. Cannot change the color of the shadow: User might want to change the shadow color to a shift the color and no Hue could shift the the color of black
  2. Inconvenient to use: User has to duplicate the clip, rearrange and modify effects on it. This is not an easy task to do if user has more than one Clip to drop shadow (even when managing them programmatically)

My implementation of Shadow effect could solves those disadvantages by two features:

  1. No extra Clip required.
  2. Shadow color could be keyframed. User could change the color freely

Please check this out! All contributions are appreciated!

@aperture147
Copy link
Contributor Author

This effect uses code from #998, please also check it out @jonoomph!

@codecov
Copy link

codecov bot commented Mar 25, 2025

Codecov Report

❌ Patch coverage is 25.17986% with 104 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.35%. Comparing base (47b3081) to head (a239b94).

Files with missing lines Patch % Lines
src/effects/Shadow.cpp 0.00% 95 Missing ⚠️
src/Frame.cpp 80.00% 5 Missing ⚠️
src/EffectInfo.cpp 0.00% 3 Missing ⚠️
src/effects/Shadow.h 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #999      +/-   ##
===========================================
- Coverage    59.60%   59.35%   -0.25%     
===========================================
  Files          206      208       +2     
  Lines        20374    20496     +122     
===========================================
+ Hits         12143    12165      +22     
- Misses        8231     8331     +100     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@aperture147
Copy link
Contributor Author

Hi, this PR is related to #998. Please check it out first. I also included test cases for that pull request!

Copilot AI review requested due to automatic review settings December 23, 2025 07:57
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a new Shadow effect for libopenshot, allowing users to add drop shadows to clips with transparent backgrounds without duplicating clips. The implementation provides keyframeable shadow color, position, and blur radius.

Key changes:

  • New Shadow effect class with support for animated offset, blur radius, and color
  • Refactored Frame class to add BGRA-aware OpenCV Mat conversion methods
  • Updated Outline effect to use the new shared Frame methods for BGRA operations

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/effects/Shadow.h Header file defining the Shadow effect class with keyframeable properties
src/effects/Shadow.cpp Implementation of Shadow effect using OpenCV for alpha-aware shadow rendering
src/Frame.h Added BGRA OpenCV Mat member and conversion methods for alpha channel support
src/Frame.cpp Implemented BGRA conversion methods and memory management
src/effects/Outline.h Refactored to remove duplicate BGRA conversion methods
src/effects/Outline.cpp Updated to use shared Frame BGRA methods instead of local implementation
tests/CVShadow.cpp Unit tests for Shadow effect (currently disabled)
tests/Frame.cpp Added test for BGRA Mat conversion
tests/CMakeLists.txt Added commented-out Shadow test configuration
src/Effects.h Registered Shadow effect header
src/EffectInfo.cpp Added Shadow effect factory and info registration
src/CMakeLists.txt Added Shadow.cpp to build sources
bindings/python/openshot.i Exposed Shadow effect to Python bindings

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


// Blur the final image to simulate shadow blur. Ignore if blur_radius is 0
// FIXME: Not physically correct
if (blur_radiusValue > 0.0) {
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition has an unnecessary floating-point comparison. The variable blur_radiusValue is an integer, so comparing it to 0.0 is inconsistent. Use integer comparison (> 0) instead.

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +84
// The shadow is completely out of the frame
if ((abs_x_offset + blur_radiusValue > cv_image.cols) || (abs_y_offset + blur_radiusValue > cv_image.rows)) {
return frame;
}

Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for checking if the shadow is out of bounds appears incorrect. The condition checks if the absolute offset plus blur radius exceeds the image dimensions, but this doesn't properly account for the shadow position. A shadow offset in the positive direction with blur shouldn't necessarily be considered "completely out of the frame" using this logic. The calculation should consider the actual shadow region relative to the padded canvas and determine if any part of the shadow would be visible in the final frame.

Suggested change
// The shadow is completely out of the frame
if ((abs_x_offset + blur_radiusValue > cv_image.cols) || (abs_y_offset + blur_radiusValue > cv_image.rows)) {
return frame;
}

Copilot uses AI. Check for mistakes.
cv::Mat shadow_color_mat(cv::Size(paddedWidth, paddedHeight), CV_8UC4, cv::Scalar(redValue, greenValue, blueValue, alphaValue));

// only shifting image if shadow is not directly under the image
if (abs_x_offset && abs_y_offset) {
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition checking if both offsets are non-zero uses bitwise AND operator instead of logical AND. While 'abs_x_offset && abs_y_offset' works because non-zero values are truthy, the logic suggests this code path should execute only when EITHER offset is non-zero (not both). If the intention is to skip shifting when shadow is directly under the image (both offsets are 0), the condition should be '(abs_x_offset || abs_y_offset)' or 'abs_x_offset != 0 || abs_y_offset != 0' to handle cases where only one offset is zero.

Suggested change
if (abs_x_offset && abs_y_offset) {
if (abs_x_offset != 0 || abs_y_offset != 0) {

Copilot uses AI. Check for mistakes.
audio.reset();
#ifdef USE_OPENCV
imagecv.release();
brga_image_cv.release();
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in variable name: should be 'bgra_image_cv' not 'brga_image_cv'. This should match the corrected variable declaration.

Suggested change
brga_image_cv.release();
bgra_image_cv.release();

Copilot uses AI. Check for mistakes.
Comment on lines +944 to +949
brga_image_cv = QImage2BGRACvMat(image);
return brga_image_cv;
}

void Frame::SetBGRACvMat(cv::Mat _image) {
brga_image_cv = _image;
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in variable name: should be 'bgra_image_cv' not 'brga_image_cv'. This should match the corrected variable declaration.

Suggested change
brga_image_cv = QImage2BGRACvMat(image);
return brga_image_cv;
}
void Frame::SetBGRACvMat(cv::Mat _image) {
brga_image_cv = _image;
bgra_image_cv = QImage2BGRACvMat(image);
return bgra_image_cv;
}
void Frame::SetBGRACvMat(cv::Mat _image) {
bgra_image_cv = _image;

Copilot uses AI. Check for mistakes.
CVTracker
CVStabilizer
CVOutline
# CVShadow # Temporally disable CVShadow test
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: should be 'Temporarily' not 'Temporally'. 'Temporally' relates to time, while 'temporarily' means for a limited time period.

Suggested change
# CVShadow # Temporally disable CVShadow test
# CVShadow # Temporarily disable CVShadow test

Copilot uses AI. Check for mistakes.
Comment on lines +283 to +286
cv::Mat QImage2BGRACvMat(std::shared_ptr<QImage>& qimage);

/// Convert OpenCV Mat to QImage (alpha channel included)
std::shared_ptr<QImage> BGRACvMat2QImage(cv::Mat img);
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent naming convention: The method names 'QImage2BGRACvMat' and 'BGRACvMat2QImage' use '2' to mean "to", but the established naming pattern in the codebase is 'Mat2Qimage' which also uses '2'. However, these new methods inconsistently capitalize parts of the name (QImage vs Qimage, CvMat vs nothing). For consistency with the existing 'Mat2Qimage' method, consider naming these 'Qimage2BGRACvMat' and 'BGRACvMat2Qimage' or better yet, use more descriptive names like 'QImageToBGRACvMat' and 'BGRACvMatToQImage' which would be more readable.

Suggested change
cv::Mat QImage2BGRACvMat(std::shared_ptr<QImage>& qimage);
/// Convert OpenCV Mat to QImage (alpha channel included)
std::shared_ptr<QImage> BGRACvMat2QImage(cv::Mat img);
cv::Mat Qimage2BGRACvMat(std::shared_ptr<QImage>& qimage);
/// Convert OpenCV Mat to QImage (alpha channel included)
std::shared_ptr<QImage> BGRACvMat2Qimage(cv::Mat img);

Copilot uses AI. Check for mistakes.

#ifdef USE_OPENCV
cv::Mat imagecv; ///< OpenCV image. It will always be in BGR format
cv::Mat brga_image_cv; ///< OpenCV image. It will always be in BGR format
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment incorrectly states this member will be in BGR format, but the variable name and context indicate it should be BGRA format (with alpha channel). The comment should be updated to accurately reflect that this is for BGRA format.

Suggested change
cv::Mat brga_image_cv; ///< OpenCV image. It will always be in BGR format
cv::Mat brga_image_cv; ///< OpenCV image. It will always be in BGRA format (with alpha channel)

Copilot uses AI. Check for mistakes.

#ifdef USE_OPENCV
cv::Mat imagecv; ///< OpenCV image. It will always be in BGR format
cv::Mat brga_image_cv; ///< OpenCV image. It will always be in BGR format
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in variable name: should be 'bgra_image_cv' not 'brga_image_cv'. The letters are transposed, which is inconsistent with the BGRA naming convention used throughout the codebase.

Suggested change
cv::Mat brga_image_cv; ///< OpenCV image. It will always be in BGR format
cv::Mat bgra_image_cv; ///< OpenCV image. It will always be in BGR format

Copilot uses AI. Check for mistakes.
// The shadow drop directly under the image or completely transparent.
// No need to do anything here, return the original frame
if (
((x_offsetValue == 0.0) && (y_offsetValue == 0) && (blur_radiusValue == 0.0)) // shadow is directly under the image
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition has unnecessary floating-point comparisons. The variables x_offsetValue, y_offsetValue, and blur_radiusValue are all integers (assigned from GetValue which returns int for these keyframes), so comparing them to floating-point literals like 0.0 is inconsistent. Use integer comparison (== 0) instead.

Suggested change
((x_offsetValue == 0.0) && (y_offsetValue == 0) && (blur_radiusValue == 0.0)) // shadow is directly under the image
((x_offsetValue == 0) && (y_offsetValue == 0) && (blur_radiusValue == 0)) // shadow is directly under the image

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant