2013-10-10 17:50:54 -05:00
/**
* @ file
2013-10-18 12:38:09 -05:00
* @ brief Source file for Mask class
2013-10-10 17:50:54 -05:00
* @ author Jonathan Thomas < jonathan @ openshot . org >
*
* @ section LICENSE
*
2014-03-29 18:49:22 -05:00
* Copyright ( c ) 2008 - 2014 OpenShot Studios , LLC
* < http : //www.openshotstudios.com/>. This file is part of
* OpenShot Library ( libopenshot ) , an open - source project dedicated to
* delivering high quality video editing and animation solutions to the
* world . For more information visit < http : //www.openshot.org/>.
2013-10-10 17:50:54 -05:00
*
2014-03-29 18:49:22 -05:00
* OpenShot Library ( libopenshot ) is free software : you can redistribute it
2014-07-11 16:52:14 -05:00
* and / or modify it under the terms of the GNU Lesser General Public License
2014-03-29 18:49:22 -05:00
* as published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
2013-10-10 17:50:54 -05:00
*
2014-03-29 18:49:22 -05:00
* OpenShot Library ( libopenshot ) is distributed in the hope that it will be
* useful , but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2014-07-11 16:52:14 -05:00
* GNU Lesser General Public License for more details .
2013-10-10 17:50:54 -05:00
*
2014-07-11 16:52:14 -05:00
* You should have received a copy of the GNU Lesser General Public License
2014-03-29 18:49:22 -05:00
* along with OpenShot Library . If not , see < http : //www.gnu.org/licenses/>.
2013-10-10 17:50:54 -05:00
*/
2014-04-10 22:38:01 -05:00
# include "../../include/effects/Mask.h"
2013-10-10 17:50:54 -05:00
using namespace openshot ;
2014-01-05 22:37:11 -06:00
/// Blank constructor, useful when using Json to load the effect properties
Mask : : Mask ( ) : reader ( NULL ) {
2015-02-25 17:39:59 -06:00
// Init effect properties
init_effect_details ( ) ;
2014-01-05 22:37:11 -06:00
}
2013-10-18 12:38:09 -05:00
// Default constructor
Mask : : Mask ( ReaderBase * mask_reader , Keyframe mask_brightness , Keyframe mask_contrast ) throw ( InvalidFile , ReaderClosed ) :
reader ( mask_reader ) , brightness ( mask_brightness ) , contrast ( mask_contrast )
2015-02-25 17:39:59 -06:00
{
// Init effect properties
init_effect_details ( ) ;
}
// Init effect settings
void Mask : : init_effect_details ( )
2013-10-10 17:50:54 -05:00
{
/// Initialize the values of the EffectInfo struct.
InitEffectInfo ( ) ;
/// Set the effect info
2013-10-18 12:38:09 -05:00
info . name = " Alpha Mask / Wipe Transition " ;
2015-02-25 17:39:59 -06:00
info . description = " Uses a grayscale mask image to gradually wipe / transition between 2 images. " ;
2013-10-10 17:50:54 -05:00
info . has_audio = false ;
info . has_video = true ;
}
2015-02-25 17:39:59 -06:00
// Set brightness and contrast (brightness between 100 and -100)
2013-10-18 12:38:09 -05:00
void Mask : : set_brightness_and_contrast ( tr1 : : shared_ptr < Magick : : Image > image , float brightness , float contrast )
2013-10-11 00:09:06 -05:00
{
// Determine if white or black image is needed
if ( brightness > = - 100.0 and brightness < = 0.0 )
{
// Make mask darker
double black_alpha = abs ( brightness ) / 100.0 ;
2013-10-11 15:54:56 -05:00
tr1 : : shared_ptr < Magick : : Image > black = tr1 : : shared_ptr < Magick : : Image > ( new Magick : : Image ( mask - > size ( ) , Magick : : Color ( " Black " ) ) ) ;
black - > matte ( true ) ;
2013-10-11 00:09:06 -05:00
black - > quantumOperator ( Magick : : OpacityChannel , Magick : : MultiplyEvaluateOperator , black_alpha ) ;
image - > composite ( * black . get ( ) , 0 , 0 , Magick : : OverCompositeOp ) ;
}
else if ( brightness > 0.0 and brightness < = 100.0 )
{
// Make mask whiter
double white_alpha = brightness / 100.0 ;
2013-10-11 15:54:56 -05:00
tr1 : : shared_ptr < Magick : : Image > white = tr1 : : shared_ptr < Magick : : Image > ( new Magick : : Image ( mask - > size ( ) , Magick : : Color ( " White " ) ) ) ;
white - > matte ( true ) ;
2013-10-11 00:09:06 -05:00
white - > quantumOperator ( Magick : : OpacityChannel , Magick : : MultiplyEvaluateOperator , white_alpha ) ;
image - > composite ( * white . get ( ) , 0 , 0 , Magick : : OverCompositeOp ) ;
}
// Set Contrast
image - > sigmoidalContrast ( true , contrast ) ;
}
2013-10-10 17:50:54 -05:00
// This method is required for all derived classes of EffectBase, and returns a
// modified openshot::Frame object
2013-10-18 12:38:09 -05:00
tr1 : : shared_ptr < Frame > Mask : : GetFrame ( tr1 : : shared_ptr < Frame > frame , int frame_number )
2013-10-10 17:50:54 -05:00
{
2013-10-18 12:38:09 -05:00
// Get the mask image (from the mask reader)
mask = reader - > GetFrame ( frame_number ) - > GetImage ( ) ;
mask - > type ( Magick : : GrayscaleType ) ; // convert to grayscale
mask - > matte ( false ) ; // Remove transparency from the image. This is required for the composite operator to copy the brightness of each pixel into the alpha channel
2013-10-10 17:50:54 -05:00
// Resize mask to match this frame size (if different)
2013-10-11 00:09:06 -05:00
if ( frame - > GetImage ( ) - > size ( ) ! = mask - > size ( ) )
{
Magick : : Geometry new_size ( frame - > GetImage ( ) - > size ( ) . width ( ) , frame - > GetImage ( ) - > size ( ) . height ( ) ) ;
new_size . aspect ( true ) ;
mask - > resize ( new_size ) ;
}
2015-02-25 17:39:59 -06:00
cout < < " brightness.GetValue( " < < frame_number < < " ): " < < brightness . GetValue ( frame_number ) < < endl ;
cout < < " contrast.GetValue( " < < frame_number < < " ): " < < contrast . GetValue ( frame_number ) < < endl ;
2013-10-10 17:50:54 -05:00
// Set the brightness of the mask (from a user-defined curve)
2013-10-18 12:38:09 -05:00
set_brightness_and_contrast ( mask , brightness . GetValue ( frame_number ) , contrast . GetValue ( frame_number ) ) ;
2013-10-10 17:50:54 -05:00
2013-10-15 00:45:23 -05:00
// Get copy of our source frame's image
2013-10-14 18:18:34 -05:00
tr1 : : shared_ptr < Magick : : Image > copy_source = tr1 : : shared_ptr < Magick : : Image > ( new Magick : : Image ( * frame - > GetImage ( ) . get ( ) ) ) ;
2013-10-15 00:45:23 -05:00
copy_source - > channel ( Magick : : MatteChannel ) ; // extract alpha channel as grayscale image
copy_source - > matte ( false ) ; // remove alpha channel
copy_source - > negate ( true ) ; // negate source alpha channel before multiplying mask
2013-10-18 12:38:09 -05:00
copy_source - > composite ( * mask . get ( ) , 0 , 0 , Magick : : MultiplyCompositeOp ) ; // multiply mask grayscale (i.e. combine the 2 grayscale images)
2013-10-11 15:54:56 -05:00
// Copy the combined alpha channel back to the frame
2013-10-15 00:45:23 -05:00
frame - > GetImage ( ) - > composite ( * copy_source . get ( ) , 0 , 0 , Magick : : CopyOpacityCompositeOp ) ;
2013-10-10 17:50:54 -05:00
// return the modified frame
return frame ;
}
2013-12-06 00:40:26 -06:00
2013-12-07 21:09:55 -06:00
// Generate JSON string of this object
string Mask : : Json ( ) {
// Return formatted string
return JsonValue ( ) . toStyledString ( ) ;
}
2013-12-06 00:40:26 -06:00
// Generate Json::JsonValue for this object
Json : : Value Mask : : JsonValue ( ) {
// Create root json object
Json : : Value root = EffectBase : : JsonValue ( ) ; // get parent properties
2014-01-05 22:37:11 -06:00
root [ " type " ] = " Mask " ;
2013-12-06 00:40:26 -06:00
root [ " brightness " ] = brightness . JsonValue ( ) ;
root [ " contrast " ] = contrast . JsonValue ( ) ;
2015-02-25 17:39:59 -06:00
root [ " reader " ] = reader - > JsonValue ( ) ;
2013-12-06 00:40:26 -06:00
// return JsonValue
return root ;
}
2013-12-07 21:09:55 -06:00
// Load JSON string into this object
void Mask : : SetJson ( string value ) throw ( InvalidJSON ) {
// Parse JSON string into JSON objects
Json : : Value root ;
Json : : Reader reader ;
bool success = reader . parse ( value , root ) ;
if ( ! success )
// Raise exception
throw InvalidJSON ( " JSON could not be parsed (or is invalid) " , " " ) ;
try
{
// Set all values that match
SetJsonValue ( root ) ;
}
catch ( exception e )
{
// Error parsing JSON (or missing keys)
throw InvalidJSON ( " JSON is invalid (missing keys or invalid data types) " , " " ) ;
}
}
2013-12-06 00:40:26 -06:00
// Load Json::JsonValue into this object
2013-12-07 21:09:55 -06:00
void Mask : : SetJsonValue ( Json : : Value root ) {
2013-12-06 00:40:26 -06:00
// Set parent data
2013-12-07 21:09:55 -06:00
EffectBase : : SetJsonValue ( root ) ;
2013-12-06 00:40:26 -06:00
// Set data from Json (if key is found)
2014-01-08 01:43:58 -06:00
if ( ! root [ " brightness " ] . isNull ( ) )
2013-12-07 21:09:55 -06:00
brightness . SetJsonValue ( root [ " brightness " ] ) ;
2014-01-08 01:43:58 -06:00
if ( ! root [ " contrast " ] . isNull ( ) )
2013-12-07 21:09:55 -06:00
contrast . SetJsonValue ( root [ " contrast " ] ) ;
2015-02-25 17:39:59 -06:00
if ( ! root [ " reader " ] . isNull ( ) ) // does Json contain a reader?
{
if ( ! root [ " reader " ] [ " type " ] . isNull ( ) ) // does the reader Json contain a 'type'?
{
// Close previous reader (if any)
if ( reader )
{
// Close and delete existing reader (if any)
reader - > Close ( ) ;
delete reader ;
reader = NULL ;
}
// Create new reader (and load properties)
string type = root [ " reader " ] [ " type " ] . asString ( ) ;
if ( type = = " FFmpegReader " ) {
// Create new reader
reader = new FFmpegReader ( root [ " reader " ] [ " path " ] . asString ( ) ) ;
reader - > SetJsonValue ( root [ " reader " ] ) ;
} else if ( type = = " ImageReader " ) {
// Create new reader
reader = new ImageReader ( root [ " reader " ] [ " path " ] . asString ( ) ) ;
reader - > SetJsonValue ( root [ " reader " ] ) ;
} else if ( type = = " ChunkReader " ) {
// Create new reader
reader = new ChunkReader ( root [ " reader " ] [ " path " ] . asString ( ) , ( ChunkVersion ) root [ " reader " ] [ " chunk_version " ] . asInt ( ) ) ;
reader - > SetJsonValue ( root [ " reader " ] ) ;
}
// Always Open reader
reader - > Open ( ) ;
}
}
2013-12-06 00:40:26 -06:00
}
2015-02-26 00:02:06 -06:00
// Get all properties for a specific frame
string Mask : : PropertiesJSON ( int requested_frame ) {
// Requested Point
Point requested_point ( requested_frame , requested_frame ) ;
// Generate JSON properties list
Json : : Value root ;
root [ " id " ] = add_property_json ( " ID " , 0.0 , " string " , Id ( ) , false , 0 , - 1 , - 1 , CONSTANT , - 1 , true ) ;
root [ " position " ] = add_property_json ( " Position " , Position ( ) , " float " , " " , false , 0 , 0 , 1000 * 60 * 30 , CONSTANT , - 1 , false ) ;
root [ " layer " ] = add_property_json ( " Layer " , Layer ( ) , " int " , " " , false , 0 , 0 , 1000 , CONSTANT , - 1 , false ) ;
root [ " start " ] = add_property_json ( " Start " , Start ( ) , " float " , " " , false , 0 , 0 , 1000 * 60 * 30 , CONSTANT , - 1 , false ) ;
root [ " end " ] = add_property_json ( " End " , End ( ) , " float " , " " , false , 0 , 0 , 1000 * 60 * 30 , CONSTANT , - 1 , false ) ;
root [ " duration " ] = add_property_json ( " Duration " , Duration ( ) , " float " , " " , false , 0 , 0 , 1000 * 60 * 30 , CONSTANT , - 1 , true ) ;
// Keyframes
root [ " brightness " ] = add_property_json ( " Brightness " , brightness . GetValue ( requested_frame ) , " float " , " " , brightness . Contains ( requested_point ) , brightness . GetCount ( ) , - 10000 , 10000 , brightness . GetClosestPoint ( requested_point ) . interpolation , brightness . GetClosestPoint ( requested_point ) . co . X , false ) ;
root [ " contrast " ] = add_property_json ( " Contrast " , contrast . GetValue ( requested_frame ) , " float " , " " , contrast . Contains ( requested_point ) , contrast . GetCount ( ) , - 10000 , 10000 , contrast . GetClosestPoint ( requested_point ) . interpolation , contrast . GetClosestPoint ( requested_point ) . co . X , false ) ;
// Keep track of settings string
stringstream properties ;
properties < < 0.0f < < Position ( ) < < Layer ( ) < < Start ( ) < < End ( ) < < Duration ( ) < <
brightness . GetValue ( requested_frame ) < < contrast . GetValue ( requested_frame ) ;
// Add Hash of All property values
root [ " hash " ] = add_property_json ( " hash " , 0.0 , " string " , properties . str ( ) , false , 0 , 0 , 1 , CONSTANT , - 1 , true ) ;
// Return formatted string
return root . toStyledString ( ) ;
}