Skip to content

Commit

Permalink
Implement blend modes (#320)
Browse files Browse the repository at this point in the history
  • Loading branch information
nilp0inter committed Jan 7, 2022
1 parent 3c8dc71 commit 0d1ae39
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 1 deletion.
33 changes: 32 additions & 1 deletion src/Clip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ void Clip::init_settings()
anchor = ANCHOR_CANVAS;
display = FRAME_DISPLAY_NONE;
mixing = VOLUME_MIX_NONE;
blend = BLEND_SOURCEOVER;
waveform = false;
previous_properties = "";
parentObjectId = "";
Expand Down Expand Up @@ -761,6 +762,7 @@ std::string Clip::PropertiesJSON(int64_t requested_frame) const {
root["scale"] = add_property_json("Scale", scale, "int", "", NULL, 0, 3, false, requested_frame);
root["display"] = add_property_json("Frame Number", display, "int", "", NULL, 0, 3, false, requested_frame);
root["mixing"] = add_property_json("Volume Mixing", mixing, "int", "", NULL, 0, 2, false, requested_frame);
root["blend"] = add_property_json("Blend Mode", blend, "int", "", NULL, 0, 23, false, requested_frame);
root["waveform"] = add_property_json("Waveform", waveform, "int", "", NULL, 0, 1, false, requested_frame);
if (!parentObjectId.empty()) {
root["parentObjectId"] = add_property_json("Parent", 0.0, "string", parentObjectId, NULL, -1, -1, false, requested_frame);
Expand Down Expand Up @@ -795,6 +797,32 @@ std::string Clip::PropertiesJSON(int64_t requested_frame) const {
root["mixing"]["choices"].append(add_property_choice_json("Average", VOLUME_MIX_AVERAGE, mixing));
root["mixing"]["choices"].append(add_property_choice_json("Reduce", VOLUME_MIX_REDUCE, mixing));

// Add video blend choices (dropdown style)
root["blend"]["choices"].append(add_property_choice_json("Source Over", BLEND_SOURCEOVER, blend));
root["blend"]["choices"].append(add_property_choice_json("Destination Over", BLEND_DESTINATIONOVER, blend));
root["blend"]["choices"].append(add_property_choice_json("Clear", BLEND_CLEAR, blend));
root["blend"]["choices"].append(add_property_choice_json("Source", BLEND_SOURCE, blend));
root["blend"]["choices"].append(add_property_choice_json("Destination", BLEND_DESTINATION, blend));
root["blend"]["choices"].append(add_property_choice_json("Source In", BLEND_SOURCEIN, blend));
root["blend"]["choices"].append(add_property_choice_json("Destination In", BLEND_DESTINATIONIN, blend));
root["blend"]["choices"].append(add_property_choice_json("Source Out", BLEND_SOURCEOUT, blend));
root["blend"]["choices"].append(add_property_choice_json("Destination Out", BLEND_DESTINATIONOUT, blend));
root["blend"]["choices"].append(add_property_choice_json("Source Atop", BLEND_SOURCEATOP, blend));
root["blend"]["choices"].append(add_property_choice_json("Destination Atop", BLEND_DESTINATIONATOP, blend));
root["blend"]["choices"].append(add_property_choice_json("Xor", BLEND_XOR, blend));
root["blend"]["choices"].append(add_property_choice_json("Plus", BLEND_PLUS, blend));
root["blend"]["choices"].append(add_property_choice_json("Multiply", BLEND_MULTIPLY, blend));
root["blend"]["choices"].append(add_property_choice_json("Screen", BLEND_SCREEN, blend));
root["blend"]["choices"].append(add_property_choice_json("Overlay", BLEND_OVERLAY, blend));
root["blend"]["choices"].append(add_property_choice_json("Darken", BLEND_DARKEN, blend));
root["blend"]["choices"].append(add_property_choice_json("Lighten", BLEND_LIGHTEN, blend));
root["blend"]["choices"].append(add_property_choice_json("Color Dodge", BLEND_COLORDODGE, blend));
root["blend"]["choices"].append(add_property_choice_json("Color Burn", BLEND_COLORBURN, blend));
root["blend"]["choices"].append(add_property_choice_json("Hard Light", BLEND_HARDLIGHT, blend));
root["blend"]["choices"].append(add_property_choice_json("Soft Light", BLEND_SOFTLIGHT, blend));
root["blend"]["choices"].append(add_property_choice_json("Difference", BLEND_DIFFERENCE, blend));
root["blend"]["choices"].append(add_property_choice_json("Exclusion", BLEND_EXCLUSION, blend));

// Add waveform choices (dropdown style)
root["waveform"]["choices"].append(add_property_choice_json("Yes", true, waveform));
root["waveform"]["choices"].append(add_property_choice_json("No", false, waveform));
Expand Down Expand Up @@ -907,6 +935,7 @@ Json::Value Clip::JsonValue() const {
root["anchor"] = anchor;
root["display"] = display;
root["mixing"] = mixing;
root["blend"] = blend;
root["waveform"] = waveform;
root["scale_x"] = scale_x.JsonValue();
root["scale_y"] = scale_y.JsonValue();
Expand Down Expand Up @@ -995,6 +1024,8 @@ void Clip::SetJsonValue(const Json::Value root) {
display = (FrameDisplayType) root["display"].asInt();
if (!root["mixing"].isNull())
mixing = (VolumeMixType) root["mixing"].asInt();
if (!root["blend"].isNull())
blend = (BlendType) root["blend"].asInt();
if (!root["waveform"].isNull())
waveform = root["waveform"].asBool();
if (!root["scale_x"].isNull())
Expand Down Expand Up @@ -1269,7 +1300,7 @@ void Clip::apply_keyframes(std::shared_ptr<Frame> frame, std::shared_ptr<QImage>
painter.setTransform(transform);

// Composite a new layer onto the image
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.setCompositionMode((QPainter::CompositionMode) blend);
painter.drawImage(0, 0, *source_image);

if (timeline) {
Expand Down
1 change: 1 addition & 0 deletions src/Clip.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ namespace openshot {
openshot::AnchorType anchor; ///< The anchor determines what parent a clip should snap to
openshot::FrameDisplayType display; ///< The format to display the frame number (if any)
openshot::VolumeMixType mixing; ///< What strategy should be followed when mixing audio with other clips
openshot::BlendType blend; ///< What strategy should be followed when mixing video with other clips

#ifdef USE_OPENCV
bool COMPILED_WITH_CV = true;
Expand Down
30 changes: 30 additions & 0 deletions src/Enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef OPENSHOT_ENUMS_H
#define OPENSHOT_ENUMS_H

#include <QPainter>

namespace openshot
{
Expand Down Expand Up @@ -133,6 +134,35 @@ enum ChromaKeyMethod
CHROMAKEY_LAST_METHOD = CHROMAKEY_YCBCR
};

/// This enumeration determines how clips are blended with lower layers
enum BlendType
{
BLEND_SOURCEOVER = QPainter::CompositionMode_SourceOver, ///< This is the default mode. The alpha of the current clip is used to blend the pixel on top of the lower layer.
BLEND_DESTINATIONOVER = QPainter::CompositionMode_DestinationOver, ///< The alpha of the lower layer is used to blend it on top of the current clip pixels. This mode is the inverse of BLEND_SOURCEOVER.
BLEND_CLEAR = QPainter::CompositionMode_Clear, ///< The pixels in the lower layer are cleared (set to fully transparent) independent of the current clip.
BLEND_SOURCE = QPainter::CompositionMode_Source, ///< The output is the current clip pixel. (This means a basic copy operation and is identical to SourceOver when the current clip pixel is opaque).
BLEND_DESTINATION = QPainter::CompositionMode_Destination, ///< The output is the lower layer pixel. This means that the blending has no effect. This mode is the inverse of BLEND_SOURCE.
BLEND_SOURCEIN = QPainter::CompositionMode_SourceIn, ///< The output is the current clip,where the alpha is reduced by that of the lower layer.
BLEND_DESTINATIONIN = QPainter::CompositionMode_DestinationIn, ///< The output is the lower layer,where the alpha is reduced by that of the current clip. This mode is the inverse of BLEND_SOURCEIN.
BLEND_SOURCEOUT = QPainter::CompositionMode_SourceOut, ///< The output is the current clip,where the alpha is reduced by the inverse of lower layer.
BLEND_DESTINATIONOUT = QPainter::CompositionMode_DestinationOut, ///< The output is the lower layer,where the alpha is reduced by the inverse of the current clip. This mode is the inverse of BLEND_SOURCEOUT.
BLEND_SOURCEATOP = QPainter::CompositionMode_SourceAtop, ///< The current clip pixel is blended on top of the lower layer,with the alpha of the current clip pixel reduced by the alpha of the lower layer pixel.
BLEND_DESTINATIONATOP = QPainter::CompositionMode_DestinationAtop, ///< The lower layer pixel is blended on top of the current clip,with the alpha of the lower layer pixel is reduced by the alpha of the lower layer pixel. This mode is the inverse of BLEND_SOURCEATOP.
BLEND_XOR = QPainter::CompositionMode_Xor, ///< The current clip,whose alpha is reduced with the inverse of the lower layer alpha,is merged with the lower layer,whose alpha is reduced by the inverse of the current clip alpha. BLEND_XOR is not the same as the bitwise Xor.
BLEND_PLUS = QPainter::CompositionMode_Plus, ///< Both the alpha and color of the current clip and lower layer pixels are added together.
BLEND_MULTIPLY = QPainter::CompositionMode_Multiply, ///< The output is the current clip color multiplied by the lower layer. Multiplying a color with white leaves the color unchanged,while multiplying a color with black produces black.
BLEND_SCREEN = QPainter::CompositionMode_Screen, ///< The current clip and lower layer colors are inverted and then multiplied. Screening a color with white produces white,whereas screening a color with black leaves the color unchanged.
BLEND_OVERLAY = QPainter::CompositionMode_Overlay, ///< Multiplies or screens the colors depending on the lower layer color. The lower layer color is mixed with the current clip color to reflect the lightness or darkness of the lower layer.
BLEND_DARKEN = QPainter::CompositionMode_Darken, ///< The darker of the current clip and lower layer colors is selected.
BLEND_LIGHTEN = QPainter::CompositionMode_Lighten, ///< The lighter of the current clip and lower layer colors is selected.
BLEND_COLORDODGE = QPainter::CompositionMode_ColorDodge, ///< The lower layer color is brightened to reflect the current clip color. A black current clip color leaves the lower layer color unchanged.
BLEND_COLORBURN = QPainter::CompositionMode_ColorBurn, ///< The lower layer color is darkened to reflect the current clip color. A white current clip color leaves the lower layer color unchanged.
BLEND_HARDLIGHT = QPainter::CompositionMode_HardLight, ///< Multiplies or screens the colors depending on the current clip color. A light current clip color will lighten the lower layer color,whereas a dark current clip color will darken the lower layer color.
BLEND_SOFTLIGHT = QPainter::CompositionMode_SoftLight, ///< Darkens or lightens the colors depending on the current clip color. Similar to BLEND_HARDLIGHT.
BLEND_DIFFERENCE = QPainter::CompositionMode_Difference, ///< Subtracts the darker of the colors from the lighter. Painting with white inverts the lower layer color,whereas painting with black leaves the lower layer color unchanged.
BLEND_EXCLUSION = QPainter::CompositionMode_Exclusion ///< Similar to BLEND_DIFFERENCE,but with a lower contrast. Painting with white inverts the lower layer color,whereas painting with black leaves the lower layer color unchanged.
};

} // namespace openshot

#endif

0 comments on commit 0d1ae39

Please sign in to comment.