From 38504d6c04b6fc6f942d511b468ba316e6fa8058 Mon Sep 17 00:00:00 2001 From: Brian Matherly Date: Tue, 25 Mar 2014 16:54:56 -0500 Subject: [PATCH] Add lift_gamma_gain filter. This filter is equivalent to the movit.lift_gamma_gain filter but does not depend on opengl/movit. --- src/modules/plus/Makefile | 1 + src/modules/plus/factory.c | 3 + src/modules/plus/filter_lift_gamma_gain.c | 241 ++++++++++++++++++++ src/modules/plus/filter_lift_gamma_gain.yml | 89 ++++++++ 4 files changed, 334 insertions(+) create mode 100644 src/modules/plus/filter_lift_gamma_gain.c create mode 100644 src/modules/plus/filter_lift_gamma_gain.yml diff --git a/src/modules/plus/Makefile b/src/modules/plus/Makefile index c96f4b99..012e038a 100644 --- a/src/modules/plus/Makefile +++ b/src/modules/plus/Makefile @@ -12,6 +12,7 @@ OBJS = consumer_blipflash.o \ filter_charcoal.o \ filter_dynamictext.o \ filter_invert.o \ + filter_lift_gamma_gain.o \ filter_loudness.o \ filter_rgblut.o \ filter_sepia.o \ diff --git a/src/modules/plus/factory.c b/src/modules/plus/factory.c index 92989e34..8f491ad1 100644 --- a/src/modules/plus/factory.c +++ b/src/modules/plus/factory.c @@ -26,6 +26,7 @@ extern mlt_consumer consumer_blipflash_init( mlt_profile profile, mlt_service_ty extern mlt_filter filter_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_charcoal_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_dynamictext_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); +extern mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_invert_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); extern mlt_filter filter_rgblut_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ); @@ -48,6 +49,7 @@ MLT_REPOSITORY MLT_REGISTER( filter_type, "charcoal", filter_charcoal_init ); MLT_REGISTER( filter_type, "dynamictext", filter_dynamictext_init ); MLT_REGISTER( filter_type, "invert", filter_invert_init ); + MLT_REGISTER( filter_type, "lift_gamma_gain", filter_lift_gamma_gain_init ); MLT_REGISTER( filter_type, "loudness", filter_loudness_init ); MLT_REGISTER( filter_type, "rgblut", filter_rgblut_init ); MLT_REGISTER( filter_type, "sepia", filter_sepia_init ); @@ -60,6 +62,7 @@ MLT_REPOSITORY MLT_REGISTER_METADATA( filter_type, "charcoal", metadata, "filter_charcoal.yml" ); MLT_REGISTER_METADATA( filter_type, "dynamictext", metadata, "filter_dynamictext.yml" ); MLT_REGISTER_METADATA( filter_type, "invert", metadata, "filter_invert.yml" ); + MLT_REGISTER_METADATA( filter_type, "lift_gamma_gain", metadata, "filter_lift_gamma_gain.yml" ); MLT_REGISTER_METADATA( filter_type, "loudness", metadata, "filter_loudness.yml" ); MLT_REGISTER_METADATA( filter_type, "rgblut", metadata, "filter_rgblut.yml" ); MLT_REGISTER_METADATA( filter_type, "sepia", metadata, "filter_sepia.yml" ); diff --git a/src/modules/plus/filter_lift_gamma_gain.c b/src/modules/plus/filter_lift_gamma_gain.c new file mode 100644 index 00000000..5c1f7475 --- /dev/null +++ b/src/modules/plus/filter_lift_gamma_gain.c @@ -0,0 +1,241 @@ +/* + * filter_lift_gamma_gain.cpp + * Copyright (C) 2014 Brian Matherly + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +typedef struct +{ + uint8_t rlut[256]; + uint8_t glut[256]; + uint8_t blut[256]; + double rlift, glift, blift; + double rgamma, ggamma, bgamma; + double rgain, ggain, bgain; +} private_data; + +static void refresh_lut( mlt_filter filter, mlt_frame frame ) +{ + private_data* private = (private_data*)filter->child; + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + mlt_position position = mlt_filter_get_position( filter, frame ); + mlt_position length = mlt_filter_get_length2( filter, frame ); + double rlift = mlt_properties_anim_get_double( properties, "lift_r", position, length ); + double glift = mlt_properties_anim_get_double( properties, "lift_g", position, length ); + double blift = mlt_properties_anim_get_double( properties, "lift_b", position, length ); + double rgamma = mlt_properties_anim_get_double( properties, "gamma_r", position, length ); + double ggamma = mlt_properties_anim_get_double( properties, "gamma_g", position, length ); + double bgamma = mlt_properties_anim_get_double( properties, "gamma_b", position, length ); + double rgain = mlt_properties_anim_get_double( properties, "gain_r", position, length ); + double ggain = mlt_properties_anim_get_double( properties, "gain_g", position, length ); + double bgain = mlt_properties_anim_get_double( properties, "gain_b", position, length ); + + // Only regenerate the LUT if something changed. + if( private->rlift != rlift || private->glift != glift || private->blift != blift || + private->rgamma != rgamma || private->ggamma != ggamma || private->bgamma != bgamma || + private->rgain != rgain || private->ggain != ggain || private->bgain != bgain ) + { + int i = 0; + for( i = 0; i < 256; i++ ) + { + // Convert to gamma 2.2 + double gamma22 = pow( (double)i / 255.0, 1.0f / 2.2f ); + double r = gamma22; + double g = gamma22; + double b = gamma22; + + // Apply lift + r += rlift * ( 1 - r ); + g += glift * ( 1 - g ); + b += blift * ( 1 - b ); + + // Apply gamma + r = pow( r, 2.2f / rgamma ); + g = pow( g, 2.2f / ggamma ); + b = pow( b, 2.2f / bgamma ); + + // Apply gain + r *= pow( rgain, 1.0f / rgamma ); + g *= pow( ggain, 1.0f / ggamma ); + b *= pow( bgain, 1.0f / bgamma ); + + // Update LUT + private->rlut[ i ] = (int)(r * 255.0); + private->glut[ i ] = (int)(g * 255.0); + private->blut[ i ] = (int)(b * 255.0); + } + + // Store the values that created the LUT so that + // changes can be detected. + private->rlift = rlift; + private->glift = glift; + private->blift = blift; + private->rgamma = rgamma; + private->ggamma = ggamma; + private->bgamma = bgamma; + private->rgain = rgain; + private->ggain = ggain; + private->bgain = bgain; + } +} + +static void apply_lut( mlt_filter filter, uint8_t* image, mlt_image_format format, int width, int height ) +{ + private_data* private = (private_data*)filter->child; + int total = width * height + 1; + uint8_t* sample = image; + + switch( format ) + { + case mlt_image_rgb24: + while( --total ) + { + *sample = private->rlut[ *sample ]; + sample++; + *sample = private->glut[ *sample ]; + sample++; + *sample = private->blut[ *sample ]; + sample++; + } + break; + case mlt_image_rgb24a: + while( --total ) + { + *sample = private->rlut[ *sample ]; + sample++; + *sample = private->glut[ *sample ]; + sample++; + *sample = private->blut[ *sample ]; + sample++; + sample++; // Skip alpha + } + break; + default: + mlt_log_error( MLT_FILTER_SERVICE( filter ), "Invalid image format: %s\n", mlt_image_format_name( format ) ); + break; + } +} + +static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable ) +{ + mlt_filter filter = (mlt_filter) mlt_frame_pop_service( frame ); + int error = 0; + + mlt_service_lock( MLT_FILTER_SERVICE( filter ) ); + + // Regenerate the LUT if necessary + refresh_lut( filter, frame ); + + // Make sure the format is acceptable + if( *format != mlt_image_rgb24 && *format != mlt_image_rgb24a ) + { + *format = mlt_image_rgb24; + } + + // Get the image + writable = 1; + error = mlt_frame_get_image( frame, image, format, width, height, writable ); + + // Apply the LUT + if( !error ) + { + apply_lut( filter, *image, *format, *width, *height ); + } + + mlt_service_unlock( MLT_FILTER_SERVICE( filter ) ); + + return error; +} + +static mlt_frame filter_process( mlt_filter filter, mlt_frame frame ) +{ + mlt_frame_push_service( frame, filter ); + mlt_frame_push_get_image( frame, filter_get_image ); + return frame; +} + +static void filter_close( mlt_filter filter ) +{ + private_data* private = (private_data*)filter->child; + + if ( private ) + { + free( private ); + } + filter->child = NULL; + filter->close = NULL; + filter->parent.close = NULL; + mlt_service_close( &filter->parent ); +} + +mlt_filter filter_lift_gamma_gain_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg ) +{ + mlt_filter filter = mlt_filter_new(); + private_data* private = (private_data*)calloc( 1, sizeof(private_data) ); + int i = 0; + + if ( filter && private ) + { + mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); + + // Initialize private data + for( i = 0; i < 256; i++ ) + { + private->rlut[i] = i; + private->glut[i] = i; + private->blut[i] = i; + } + private->rlift = private->glift = private->blift = 0.0; + private->rgamma = private->ggamma = private->bgamma = 1.0; + private->rgain = private->ggain = private->bgain = 1.0; + + // Initialize filter properties + mlt_properties_set_double( properties, "lift_r", private->rlift ); + mlt_properties_set_double( properties, "lift_g", private->glift ); + mlt_properties_set_double( properties, "lift_b", private->blift ); + mlt_properties_set_double( properties, "gamma_r", private->rgamma ); + mlt_properties_set_double( properties, "gamma_g", private->ggamma ); + mlt_properties_set_double( properties, "gamma_b", private->bgamma ); + mlt_properties_set_double( properties, "gain_r", private->rgain ); + mlt_properties_set_double( properties, "gain_g", private->ggain ); + mlt_properties_set_double( properties, "gain_b", private->bgain ); + + filter->close = filter_close; + filter->process = filter_process; + filter->child = private; + } + else + { + mlt_log_error( MLT_FILTER_SERVICE(filter), "Filter lift_gamma_gain init failed\n" ); + + if( filter ) + { + mlt_filter_close( filter ); + filter = NULL; + } + + if( private ) + { + free( private ); + } + } + + return filter; +} diff --git a/src/modules/plus/filter_lift_gamma_gain.yml b/src/modules/plus/filter_lift_gamma_gain.yml new file mode 100644 index 00000000..a4a3be3a --- /dev/null +++ b/src/modules/plus/filter_lift_gamma_gain.yml @@ -0,0 +1,89 @@ +schema_version: 0.1 +type: filter +identifier: lift_gamma_gain +title: Lift, Gamma, and Gain +version: 1 +copyright: Brian Matherly +creator: Brian Matherly +license: LGPLv2.1 +language: en +tags: + - Video +description: > + A simple lift/gamma/gain effect, used for color grading. +notes: > + Very roughly speaking, lift=shadows, gamma=midtones and gain=highlights, + although all parameters affect the entire curve. Mathematically speaking, + it is a bit unusual to look at gamma as a color, but it works pretty well + in practice. + The classic formula is: output = (gain * (x + lift * (1-x)))^(1/gamma). + The lift is a case where we actually would _not_ want linear light; + since black by definition becomes equal to the lift color, we want lift to + be pretty close to black, but in linear light that means lift affects the + rest of the curve relatively little. Thus, we actually convert to gamma 2.2 + before lift, and then back again afterwards. (Gain and gamma are, + up to constants, commutative with the de-gamma operation.) + +parameters: + - identifier: lift_r + title: Lift Red + type: float + minimum: 0.0 + default: 0.0 + mutable: yes + + - identifier: lift_g + title: Lift Green + type: float + minimum: 0.0 + default: 0.0 + mutable: yes + + - identifier: lift_b + title: Lift Blue + type: float + minimum: 0.0 + default: 0.0 + mutable: yes + + - identifier: gamma_r + title: Gamma Red + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gamma_g + title: Gamma Green + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gamma_b + title: Gamma Blue + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gain_r + title: Gain Red + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gain_g + title: Gain Green + type: float + minimum: 0.0 + default: 1.0 + mutable: yes + + - identifier: gain_b + title: Gain Blue + type: float + minimum: 0.0 + default: 1.0 + mutable: yes -- 2.39.2