1554 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1554 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * This program is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU General Public License
 | |
|  * as published by the Free Software Foundation; either version 2
 | |
|  * of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This program 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 General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, write to the Free Software Foundation,
 | |
|  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 | |
|  *
 | |
|  * The Original Code is Copyright (C) 2012 Blender Foundation.
 | |
|  * All rights reserved.
 | |
|  */
 | |
| 
 | |
| /** \file
 | |
|  * \ingroup bke
 | |
|  */
 | |
| 
 | |
| #include <stddef.h>
 | |
| #include <string.h>
 | |
| 
 | |
| #include "MEM_guardedalloc.h"
 | |
| 
 | |
| #include "BLI_listbase.h"
 | |
| #include "BLI_math.h"
 | |
| #include "BLI_string.h"
 | |
| #include "BLI_string_utils.h"
 | |
| #include "BLI_utildefines.h"
 | |
| 
 | |
| #include "BLT_translation.h"
 | |
| 
 | |
| #include "DNA_mask_types.h"
 | |
| #include "DNA_scene_types.h"
 | |
| #include "DNA_sequence_types.h"
 | |
| 
 | |
| #include "BKE_colortools.h"
 | |
| 
 | |
| #include "IMB_colormanagement.h"
 | |
| #include "IMB_imbuf.h"
 | |
| #include "IMB_imbuf_types.h"
 | |
| 
 | |
| #include "SEQ_modifier.h"
 | |
| #include "SEQ_render.h"
 | |
| 
 | |
| #include "BLO_read_write.h"
 | |
| 
 | |
| #include "render.h"
 | |
| 
 | |
| static SequenceModifierTypeInfo *modifiersTypes[NUM_SEQUENCE_MODIFIER_TYPES];
 | |
| static bool modifierTypesInit = false;
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Modifier Multi-Threading Utilities
 | |
|  * \{ */
 | |
| 
 | |
| typedef void (*modifier_apply_threaded_cb)(int width,
 | |
|                                            int height,
 | |
|                                            unsigned char *rect,
 | |
|                                            float *rect_float,
 | |
|                                            unsigned char *mask_rect,
 | |
|                                            const float *mask_rect_float,
 | |
|                                            void *data_v);
 | |
| 
 | |
| typedef struct ModifierInitData {
 | |
|   ImBuf *ibuf;
 | |
|   ImBuf *mask;
 | |
|   void *user_data;
 | |
| 
 | |
|   modifier_apply_threaded_cb apply_callback;
 | |
| } ModifierInitData;
 | |
| 
 | |
| typedef struct ModifierThread {
 | |
|   int width, height;
 | |
| 
 | |
|   unsigned char *rect, *mask_rect;
 | |
|   float *rect_float, *mask_rect_float;
 | |
| 
 | |
|   void *user_data;
 | |
| 
 | |
|   modifier_apply_threaded_cb apply_callback;
 | |
| } ModifierThread;
 | |
| 
 | |
| /**
 | |
|  * \a timeline_frame is offset by \a fra_offset only in case we are using a real mask.
 | |
|  */
 | |
| static ImBuf *modifier_render_mask_input(const SeqRenderData *context,
 | |
|                                          int mask_input_type,
 | |
|                                          Sequence *mask_sequence,
 | |
|                                          Mask *mask_id,
 | |
|                                          int timeline_frame,
 | |
|                                          int fra_offset,
 | |
|                                          bool make_float)
 | |
| {
 | |
|   ImBuf *mask_input = NULL;
 | |
| 
 | |
|   if (mask_input_type == SEQUENCE_MASK_INPUT_STRIP) {
 | |
|     if (mask_sequence) {
 | |
|       SeqRenderState state;
 | |
|       seq_render_state_init(&state);
 | |
| 
 | |
|       mask_input = seq_render_strip(context, &state, mask_sequence, timeline_frame);
 | |
| 
 | |
|       if (make_float) {
 | |
|         if (!mask_input->rect_float) {
 | |
|           IMB_float_from_rect(mask_input);
 | |
|         }
 | |
|       }
 | |
|       else {
 | |
|         if (!mask_input->rect) {
 | |
|           IMB_rect_from_float(mask_input);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   else if (mask_input_type == SEQUENCE_MASK_INPUT_ID) {
 | |
|     mask_input = seq_render_mask(context, mask_id, timeline_frame - fra_offset, make_float);
 | |
|   }
 | |
| 
 | |
|   return mask_input;
 | |
| }
 | |
| 
 | |
| static ImBuf *modifier_mask_get(SequenceModifierData *smd,
 | |
|                                 const SeqRenderData *context,
 | |
|                                 int timeline_frame,
 | |
|                                 int fra_offset,
 | |
|                                 bool make_float)
 | |
| {
 | |
|   return modifier_render_mask_input(context,
 | |
|                                     smd->mask_input_type,
 | |
|                                     smd->mask_sequence,
 | |
|                                     smd->mask_id,
 | |
|                                     timeline_frame,
 | |
|                                     fra_offset,
 | |
|                                     make_float);
 | |
| }
 | |
| 
 | |
| static void modifier_init_handle(void *handle_v, int start_line, int tot_line, void *init_data_v)
 | |
| {
 | |
|   ModifierThread *handle = (ModifierThread *)handle_v;
 | |
|   ModifierInitData *init_data = (ModifierInitData *)init_data_v;
 | |
|   ImBuf *ibuf = init_data->ibuf;
 | |
|   ImBuf *mask = init_data->mask;
 | |
| 
 | |
|   int offset = 4 * start_line * ibuf->x;
 | |
| 
 | |
|   memset(handle, 0, sizeof(ModifierThread));
 | |
| 
 | |
|   handle->width = ibuf->x;
 | |
|   handle->height = tot_line;
 | |
|   handle->apply_callback = init_data->apply_callback;
 | |
|   handle->user_data = init_data->user_data;
 | |
| 
 | |
|   if (ibuf->rect) {
 | |
|     handle->rect = (unsigned char *)ibuf->rect + offset;
 | |
|   }
 | |
| 
 | |
|   if (ibuf->rect_float) {
 | |
|     handle->rect_float = ibuf->rect_float + offset;
 | |
|   }
 | |
| 
 | |
|   if (mask) {
 | |
|     if (mask->rect) {
 | |
|       handle->mask_rect = (unsigned char *)mask->rect + offset;
 | |
|     }
 | |
| 
 | |
|     if (mask->rect_float) {
 | |
|       handle->mask_rect_float = mask->rect_float + offset;
 | |
|     }
 | |
|   }
 | |
|   else {
 | |
|     handle->mask_rect = NULL;
 | |
|     handle->mask_rect_float = NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void *modifier_do_thread(void *thread_data_v)
 | |
| {
 | |
|   ModifierThread *td = (ModifierThread *)thread_data_v;
 | |
| 
 | |
|   td->apply_callback(td->width,
 | |
|                      td->height,
 | |
|                      td->rect,
 | |
|                      td->rect_float,
 | |
|                      td->mask_rect,
 | |
|                      td->mask_rect_float,
 | |
|                      td->user_data);
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| static void modifier_apply_threaded(ImBuf *ibuf,
 | |
|                                     ImBuf *mask,
 | |
|                                     modifier_apply_threaded_cb apply_callback,
 | |
|                                     void *user_data)
 | |
| {
 | |
|   ModifierInitData init_data;
 | |
| 
 | |
|   init_data.ibuf = ibuf;
 | |
|   init_data.mask = mask;
 | |
|   init_data.user_data = user_data;
 | |
| 
 | |
|   init_data.apply_callback = apply_callback;
 | |
| 
 | |
|   IMB_processor_apply_threaded(
 | |
|       ibuf->y, sizeof(ModifierThread), &init_data, modifier_init_handle, modifier_do_thread);
 | |
| }
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Color Balance Modifier
 | |
|  * \{ */
 | |
| 
 | |
| static StripColorBalance calc_cb(StripColorBalance *cb_)
 | |
| {
 | |
|   StripColorBalance cb = *cb_;
 | |
|   int c;
 | |
| 
 | |
|   for (c = 0; c < 3; c++) {
 | |
|     cb.lift[c] = 2.0f - cb.lift[c];
 | |
|   }
 | |
| 
 | |
|   if (cb.flag & SEQ_COLOR_BALANCE_INVERSE_LIFT) {
 | |
|     for (c = 0; c < 3; c++) {
 | |
|       /* tweak to give more subtle results
 | |
|        * values above 1.0 are scaled */
 | |
|       if (cb.lift[c] > 1.0f) {
 | |
|         cb.lift[c] = pow(cb.lift[c] - 1.0f, 2.0) + 1.0;
 | |
|       }
 | |
| 
 | |
|       cb.lift[c] = 2.0f - cb.lift[c];
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (cb.flag & SEQ_COLOR_BALANCE_INVERSE_GAIN) {
 | |
|     for (c = 0; c < 3; c++) {
 | |
|       if (cb.gain[c] != 0.0f) {
 | |
|         cb.gain[c] = 1.0f / cb.gain[c];
 | |
|       }
 | |
|       else {
 | |
|         cb.gain[c] = 1000000; /* should be enough :) */
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!(cb.flag & SEQ_COLOR_BALANCE_INVERSE_GAMMA)) {
 | |
|     for (c = 0; c < 3; c++) {
 | |
|       if (cb.gamma[c] != 0.0f) {
 | |
|         cb.gamma[c] = 1.0f / cb.gamma[c];
 | |
|       }
 | |
|       else {
 | |
|         cb.gamma[c] = 1000000; /* should be enough :) */
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return cb;
 | |
| }
 | |
| 
 | |
| /* note: lift is actually 2-lift */
 | |
| MINLINE float color_balance_fl(
 | |
|     float in, const float lift, const float gain, const float gamma, const float mul)
 | |
| {
 | |
|   float x = (((in - 1.0f) * lift) + 1.0f) * gain;
 | |
| 
 | |
|   /* prevent NaN */
 | |
|   if (x < 0.0f) {
 | |
|     x = 0.0f;
 | |
|   }
 | |
| 
 | |
|   x = powf(x, gamma) * mul;
 | |
|   CLAMP(x, FLT_MIN, FLT_MAX);
 | |
|   return x;
 | |
| }
 | |
| 
 | |
| static void make_cb_table_float(float lift, float gain, float gamma, float *table, float mul)
 | |
| {
 | |
|   int y;
 | |
| 
 | |
|   for (y = 0; y < 256; y++) {
 | |
|     float v = color_balance_fl((float)y * (1.0f / 255.0f), lift, gain, gamma, mul);
 | |
| 
 | |
|     table[y] = v;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void color_balance_byte_byte(StripColorBalance *cb_,
 | |
|                                     unsigned char *rect,
 | |
|                                     unsigned char *mask_rect,
 | |
|                                     int width,
 | |
|                                     int height,
 | |
|                                     float mul)
 | |
| {
 | |
|   // unsigned char cb_tab[3][256];
 | |
|   unsigned char *cp = rect;
 | |
|   unsigned char *e = cp + width * 4 * height;
 | |
|   unsigned char *m = mask_rect;
 | |
| 
 | |
|   StripColorBalance cb = calc_cb(cb_);
 | |
| 
 | |
|   while (cp < e) {
 | |
|     float p[4];
 | |
|     int c;
 | |
| 
 | |
|     straight_uchar_to_premul_float(p, cp);
 | |
| 
 | |
|     for (c = 0; c < 3; c++) {
 | |
|       float t = color_balance_fl(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul);
 | |
| 
 | |
|       if (m) {
 | |
|         float m_normal = (float)m[c] / 255.0f;
 | |
| 
 | |
|         p[c] = p[c] * (1.0f - m_normal) + t * m_normal;
 | |
|       }
 | |
|       else {
 | |
|         p[c] = t;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     premul_float_to_straight_uchar(cp, p);
 | |
| 
 | |
|     cp += 4;
 | |
|     if (m) {
 | |
|       m += 4;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void color_balance_byte_float(StripColorBalance *cb_,
 | |
|                                      unsigned char *rect,
 | |
|                                      float *rect_float,
 | |
|                                      unsigned char *mask_rect,
 | |
|                                      int width,
 | |
|                                      int height,
 | |
|                                      float mul)
 | |
| {
 | |
|   float cb_tab[4][256];
 | |
|   int c, i;
 | |
|   unsigned char *p = rect;
 | |
|   unsigned char *e = p + width * 4 * height;
 | |
|   unsigned char *m = mask_rect;
 | |
|   float *o;
 | |
|   StripColorBalance cb;
 | |
| 
 | |
|   o = rect_float;
 | |
| 
 | |
|   cb = calc_cb(cb_);
 | |
| 
 | |
|   for (c = 0; c < 3; c++) {
 | |
|     make_cb_table_float(cb.lift[c], cb.gain[c], cb.gamma[c], cb_tab[c], mul);
 | |
|   }
 | |
| 
 | |
|   for (i = 0; i < 256; i++) {
 | |
|     cb_tab[3][i] = ((float)i) * (1.0f / 255.0f);
 | |
|   }
 | |
| 
 | |
|   while (p < e) {
 | |
|     if (m) {
 | |
|       const float t[3] = {m[0] / 255.0f, m[1] / 255.0f, m[2] / 255.0f};
 | |
| 
 | |
|       p[0] = p[0] * (1.0f - t[0]) + t[0] * cb_tab[0][p[0]];
 | |
|       p[1] = p[1] * (1.0f - t[1]) + t[1] * cb_tab[1][p[1]];
 | |
|       p[2] = p[2] * (1.0f - t[2]) + t[2] * cb_tab[2][p[2]];
 | |
| 
 | |
|       m += 4;
 | |
|     }
 | |
|     else {
 | |
|       o[0] = cb_tab[0][p[0]];
 | |
|       o[1] = cb_tab[1][p[1]];
 | |
|       o[2] = cb_tab[2][p[2]];
 | |
|     }
 | |
| 
 | |
|     o[3] = cb_tab[3][p[3]];
 | |
| 
 | |
|     p += 4;
 | |
|     o += 4;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void color_balance_float_float(StripColorBalance *cb_,
 | |
|                                       float *rect_float,
 | |
|                                       const float *mask_rect_float,
 | |
|                                       int width,
 | |
|                                       int height,
 | |
|                                       float mul)
 | |
| {
 | |
|   float *p = rect_float;
 | |
|   const float *e = rect_float + width * 4 * height;
 | |
|   const float *m = mask_rect_float;
 | |
|   StripColorBalance cb = calc_cb(cb_);
 | |
| 
 | |
|   while (p < e) {
 | |
|     int c;
 | |
|     for (c = 0; c < 3; c++) {
 | |
|       float t = color_balance_fl(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul);
 | |
| 
 | |
|       if (m) {
 | |
|         p[c] = p[c] * (1.0f - m[c]) + t * m[c];
 | |
|       }
 | |
|       else {
 | |
|         p[c] = t;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     p += 4;
 | |
|     if (m) {
 | |
|       m += 4;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| typedef struct ColorBalanceInitData {
 | |
|   StripColorBalance *cb;
 | |
|   ImBuf *ibuf;
 | |
|   float mul;
 | |
|   ImBuf *mask;
 | |
|   bool make_float;
 | |
| } ColorBalanceInitData;
 | |
| 
 | |
| typedef struct ColorBalanceThread {
 | |
|   StripColorBalance *cb;
 | |
|   float mul;
 | |
| 
 | |
|   int width, height;
 | |
| 
 | |
|   unsigned char *rect, *mask_rect;
 | |
|   float *rect_float, *mask_rect_float;
 | |
| 
 | |
|   bool make_float;
 | |
| } ColorBalanceThread;
 | |
| 
 | |
| static void color_balance_init_handle(void *handle_v,
 | |
|                                       int start_line,
 | |
|                                       int tot_line,
 | |
|                                       void *init_data_v)
 | |
| {
 | |
|   ColorBalanceThread *handle = (ColorBalanceThread *)handle_v;
 | |
|   ColorBalanceInitData *init_data = (ColorBalanceInitData *)init_data_v;
 | |
|   ImBuf *ibuf = init_data->ibuf;
 | |
|   ImBuf *mask = init_data->mask;
 | |
| 
 | |
|   int offset = 4 * start_line * ibuf->x;
 | |
| 
 | |
|   memset(handle, 0, sizeof(ColorBalanceThread));
 | |
| 
 | |
|   handle->cb = init_data->cb;
 | |
|   handle->mul = init_data->mul;
 | |
|   handle->width = ibuf->x;
 | |
|   handle->height = tot_line;
 | |
|   handle->make_float = init_data->make_float;
 | |
| 
 | |
|   if (ibuf->rect) {
 | |
|     handle->rect = (unsigned char *)ibuf->rect + offset;
 | |
|   }
 | |
| 
 | |
|   if (ibuf->rect_float) {
 | |
|     handle->rect_float = ibuf->rect_float + offset;
 | |
|   }
 | |
| 
 | |
|   if (mask) {
 | |
|     if (mask->rect) {
 | |
|       handle->mask_rect = (unsigned char *)mask->rect + offset;
 | |
|     }
 | |
| 
 | |
|     if (mask->rect_float) {
 | |
|       handle->mask_rect_float = mask->rect_float + offset;
 | |
|     }
 | |
|   }
 | |
|   else {
 | |
|     handle->mask_rect = NULL;
 | |
|     handle->mask_rect_float = NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void *color_balance_do_thread(void *thread_data_v)
 | |
| {
 | |
|   ColorBalanceThread *thread_data = (ColorBalanceThread *)thread_data_v;
 | |
|   StripColorBalance *cb = thread_data->cb;
 | |
|   int width = thread_data->width, height = thread_data->height;
 | |
|   unsigned char *rect = thread_data->rect;
 | |
|   unsigned char *mask_rect = thread_data->mask_rect;
 | |
|   float *rect_float = thread_data->rect_float;
 | |
|   float *mask_rect_float = thread_data->mask_rect_float;
 | |
|   float mul = thread_data->mul;
 | |
| 
 | |
|   if (rect_float) {
 | |
|     color_balance_float_float(cb, rect_float, mask_rect_float, width, height, mul);
 | |
|   }
 | |
|   else if (thread_data->make_float) {
 | |
|     color_balance_byte_float(cb, rect, rect_float, mask_rect, width, height, mul);
 | |
|   }
 | |
|   else {
 | |
|     color_balance_byte_byte(cb, rect, mask_rect, width, height, mul);
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| static void colorBalance_init_data(SequenceModifierData *smd)
 | |
| {
 | |
|   ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd;
 | |
|   int c;
 | |
| 
 | |
|   cbmd->color_multiply = 1.0f;
 | |
| 
 | |
|   for (c = 0; c < 3; c++) {
 | |
|     cbmd->color_balance.lift[c] = 1.0f;
 | |
|     cbmd->color_balance.gamma[c] = 1.0f;
 | |
|     cbmd->color_balance.gain[c] = 1.0f;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void modifier_color_balance_apply(
 | |
|     StripColorBalance *cb, ImBuf *ibuf, float mul, bool make_float, ImBuf *mask_input)
 | |
| {
 | |
|   ColorBalanceInitData init_data;
 | |
| 
 | |
|   if (!ibuf->rect_float && make_float) {
 | |
|     imb_addrectfloatImBuf(ibuf);
 | |
|   }
 | |
| 
 | |
|   init_data.cb = cb;
 | |
|   init_data.ibuf = ibuf;
 | |
|   init_data.mul = mul;
 | |
|   init_data.make_float = make_float;
 | |
|   init_data.mask = mask_input;
 | |
| 
 | |
|   IMB_processor_apply_threaded(ibuf->y,
 | |
|                                sizeof(ColorBalanceThread),
 | |
|                                &init_data,
 | |
|                                color_balance_init_handle,
 | |
|                                color_balance_do_thread);
 | |
| 
 | |
|   /* color balance either happens on float buffer or byte buffer, but never on both,
 | |
|    * free byte buffer if there's float buffer since float buffer would be used for
 | |
|    * color balance in favor of byte buffer
 | |
|    */
 | |
|   if (ibuf->rect_float && ibuf->rect) {
 | |
|     imb_freerectImBuf(ibuf);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void colorBalance_apply(SequenceModifierData *smd, ImBuf *ibuf, ImBuf *mask)
 | |
| {
 | |
|   ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd;
 | |
| 
 | |
|   modifier_color_balance_apply(&cbmd->color_balance, ibuf, cbmd->color_multiply, false, mask);
 | |
| }
 | |
| 
 | |
| static SequenceModifierTypeInfo seqModifier_ColorBalance = {
 | |
|     CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "Color Balance"), /* name */
 | |
|     "ColorBalanceModifierData",                           /* struct_name */
 | |
|     sizeof(ColorBalanceModifierData),                     /* struct_size */
 | |
|     colorBalance_init_data,                               /* init_data */
 | |
|     NULL,                                                 /* free_data */
 | |
|     NULL,                                                 /* copy_data */
 | |
|     colorBalance_apply,                                   /* apply */
 | |
| };
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name White Balance Modifier
 | |
|  * \{ */
 | |
| 
 | |
| static void whiteBalance_init_data(SequenceModifierData *smd)
 | |
| {
 | |
|   WhiteBalanceModifierData *cbmd = (WhiteBalanceModifierData *)smd;
 | |
|   copy_v3_fl(cbmd->white_value, 1.0f);
 | |
| }
 | |
| 
 | |
| typedef struct WhiteBalanceThreadData {
 | |
|   float white[3];
 | |
| } WhiteBalanceThreadData;
 | |
| 
 | |
| static void whiteBalance_apply_threaded(int width,
 | |
|                                         int height,
 | |
|                                         unsigned char *rect,
 | |
|                                         float *rect_float,
 | |
|                                         unsigned char *mask_rect,
 | |
|                                         const float *mask_rect_float,
 | |
|                                         void *data_v)
 | |
| {
 | |
|   int x, y;
 | |
|   float multiplier[3];
 | |
| 
 | |
|   WhiteBalanceThreadData *data = (WhiteBalanceThreadData *)data_v;
 | |
| 
 | |
|   multiplier[0] = (data->white[0] != 0.0f) ? 1.0f / data->white[0] : FLT_MAX;
 | |
|   multiplier[1] = (data->white[1] != 0.0f) ? 1.0f / data->white[1] : FLT_MAX;
 | |
|   multiplier[2] = (data->white[2] != 0.0f) ? 1.0f / data->white[2] : FLT_MAX;
 | |
| 
 | |
|   for (y = 0; y < height; y++) {
 | |
|     for (x = 0; x < width; x++) {
 | |
|       int pixel_index = (y * width + x) * 4;
 | |
|       float rgba[4], result[4], mask[3] = {1.0f, 1.0f, 1.0f};
 | |
| 
 | |
|       if (rect_float) {
 | |
|         copy_v3_v3(rgba, rect_float + pixel_index);
 | |
|       }
 | |
|       else {
 | |
|         straight_uchar_to_premul_float(rgba, rect + pixel_index);
 | |
|       }
 | |
| 
 | |
|       copy_v4_v4(result, rgba);
 | |
| #if 0
 | |
|       mul_v3_v3(result, multiplier);
 | |
| #else
 | |
|       /* similar to division without the clipping */
 | |
|       for (int i = 0; i < 3; i++) {
 | |
|         result[i] = 1.0f - powf(1.0f - rgba[i], multiplier[i]);
 | |
|       }
 | |
| #endif
 | |
| 
 | |
|       if (mask_rect_float) {
 | |
|         copy_v3_v3(mask, mask_rect_float + pixel_index);
 | |
|       }
 | |
|       else if (mask_rect) {
 | |
|         rgb_uchar_to_float(mask, mask_rect + pixel_index);
 | |
|       }
 | |
| 
 | |
|       result[0] = rgba[0] * (1.0f - mask[0]) + result[0] * mask[0];
 | |
|       result[1] = rgba[1] * (1.0f - mask[1]) + result[1] * mask[1];
 | |
|       result[2] = rgba[2] * (1.0f - mask[2]) + result[2] * mask[2];
 | |
| 
 | |
|       if (rect_float) {
 | |
|         copy_v3_v3(rect_float + pixel_index, result);
 | |
|       }
 | |
|       else {
 | |
|         premul_float_to_straight_uchar(rect + pixel_index, result);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void whiteBalance_apply(SequenceModifierData *smd, ImBuf *ibuf, ImBuf *mask)
 | |
| {
 | |
|   WhiteBalanceThreadData data;
 | |
|   WhiteBalanceModifierData *wbmd = (WhiteBalanceModifierData *)smd;
 | |
| 
 | |
|   copy_v3_v3(data.white, wbmd->white_value);
 | |
| 
 | |
|   modifier_apply_threaded(ibuf, mask, whiteBalance_apply_threaded, &data);
 | |
| }
 | |
| 
 | |
| static SequenceModifierTypeInfo seqModifier_WhiteBalance = {
 | |
|     CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "White Balance"), /* name */
 | |
|     "WhiteBalanceModifierData",                           /* struct_name */
 | |
|     sizeof(WhiteBalanceModifierData),                     /* struct_size */
 | |
|     whiteBalance_init_data,                               /* init_data */
 | |
|     NULL,                                                 /* free_data */
 | |
|     NULL,                                                 /* copy_data */
 | |
|     whiteBalance_apply,                                   /* apply */
 | |
| };
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Curves Modifier
 | |
|  * \{ */
 | |
| 
 | |
| static void curves_init_data(SequenceModifierData *smd)
 | |
| {
 | |
|   CurvesModifierData *cmd = (CurvesModifierData *)smd;
 | |
| 
 | |
|   BKE_curvemapping_set_defaults(&cmd->curve_mapping, 4, 0.0f, 0.0f, 1.0f, 1.0f);
 | |
| }
 | |
| 
 | |
| static void curves_free_data(SequenceModifierData *smd)
 | |
| {
 | |
|   CurvesModifierData *cmd = (CurvesModifierData *)smd;
 | |
| 
 | |
|   BKE_curvemapping_free_data(&cmd->curve_mapping);
 | |
| }
 | |
| 
 | |
| static void curves_copy_data(SequenceModifierData *target, SequenceModifierData *smd)
 | |
| {
 | |
|   CurvesModifierData *cmd = (CurvesModifierData *)smd;
 | |
|   CurvesModifierData *cmd_target = (CurvesModifierData *)target;
 | |
| 
 | |
|   BKE_curvemapping_copy_data(&cmd_target->curve_mapping, &cmd->curve_mapping);
 | |
| }
 | |
| 
 | |
| static void curves_apply_threaded(int width,
 | |
|                                   int height,
 | |
|                                   unsigned char *rect,
 | |
|                                   float *rect_float,
 | |
|                                   unsigned char *mask_rect,
 | |
|                                   const float *mask_rect_float,
 | |
|                                   void *data_v)
 | |
| {
 | |
|   CurveMapping *curve_mapping = (CurveMapping *)data_v;
 | |
|   int x, y;
 | |
| 
 | |
|   for (y = 0; y < height; y++) {
 | |
|     for (x = 0; x < width; x++) {
 | |
|       int pixel_index = (y * width + x) * 4;
 | |
| 
 | |
|       if (rect_float) {
 | |
|         float *pixel = rect_float + pixel_index;
 | |
|         float result[3];
 | |
| 
 | |
|         BKE_curvemapping_evaluate_premulRGBF(curve_mapping, result, pixel);
 | |
| 
 | |
|         if (mask_rect_float) {
 | |
|           const float *m = mask_rect_float + pixel_index;
 | |
| 
 | |
|           pixel[0] = pixel[0] * (1.0f - m[0]) + result[0] * m[0];
 | |
|           pixel[1] = pixel[1] * (1.0f - m[1]) + result[1] * m[1];
 | |
|           pixel[2] = pixel[2] * (1.0f - m[2]) + result[2] * m[2];
 | |
|         }
 | |
|         else {
 | |
|           pixel[0] = result[0];
 | |
|           pixel[1] = result[1];
 | |
|           pixel[2] = result[2];
 | |
|         }
 | |
|       }
 | |
|       if (rect) {
 | |
|         unsigned char *pixel = rect + pixel_index;
 | |
|         float result[3], tempc[4];
 | |
| 
 | |
|         straight_uchar_to_premul_float(tempc, pixel);
 | |
| 
 | |
|         BKE_curvemapping_evaluate_premulRGBF(curve_mapping, result, tempc);
 | |
| 
 | |
|         if (mask_rect) {
 | |
|           float t[3];
 | |
| 
 | |
|           rgb_uchar_to_float(t, mask_rect + pixel_index);
 | |
| 
 | |
|           tempc[0] = tempc[0] * (1.0f - t[0]) + result[0] * t[0];
 | |
|           tempc[1] = tempc[1] * (1.0f - t[1]) + result[1] * t[1];
 | |
|           tempc[2] = tempc[2] * (1.0f - t[2]) + result[2] * t[2];
 | |
|         }
 | |
|         else {
 | |
|           tempc[0] = result[0];
 | |
|           tempc[1] = result[1];
 | |
|           tempc[2] = result[2];
 | |
|         }
 | |
| 
 | |
|         premul_float_to_straight_uchar(pixel, tempc);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void curves_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImBuf *mask)
 | |
| {
 | |
|   CurvesModifierData *cmd = (CurvesModifierData *)smd;
 | |
| 
 | |
|   const float black[3] = {0.0f, 0.0f, 0.0f};
 | |
|   const float white[3] = {1.0f, 1.0f, 1.0f};
 | |
| 
 | |
|   BKE_curvemapping_init(&cmd->curve_mapping);
 | |
| 
 | |
|   BKE_curvemapping_premultiply(&cmd->curve_mapping, 0);
 | |
|   BKE_curvemapping_set_black_white(&cmd->curve_mapping, black, white);
 | |
| 
 | |
|   modifier_apply_threaded(ibuf, mask, curves_apply_threaded, &cmd->curve_mapping);
 | |
| 
 | |
|   BKE_curvemapping_premultiply(&cmd->curve_mapping, 1);
 | |
| }
 | |
| 
 | |
| static SequenceModifierTypeInfo seqModifier_Curves = {
 | |
|     CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "Curves"), /* name */
 | |
|     "CurvesModifierData",                          /* struct_name */
 | |
|     sizeof(CurvesModifierData),                    /* struct_size */
 | |
|     curves_init_data,                              /* init_data */
 | |
|     curves_free_data,                              /* free_data */
 | |
|     curves_copy_data,                              /* copy_data */
 | |
|     curves_apply,                                  /* apply */
 | |
| };
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Hue Correct Modifier
 | |
|  * \{ */
 | |
| 
 | |
| static void hue_correct_init_data(SequenceModifierData *smd)
 | |
| {
 | |
|   HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd;
 | |
|   int c;
 | |
| 
 | |
|   BKE_curvemapping_set_defaults(&hcmd->curve_mapping, 1, 0.0f, 0.0f, 1.0f, 1.0f);
 | |
|   hcmd->curve_mapping.preset = CURVE_PRESET_MID9;
 | |
| 
 | |
|   for (c = 0; c < 3; c++) {
 | |
|     CurveMap *cuma = &hcmd->curve_mapping.cm[c];
 | |
| 
 | |
|     BKE_curvemap_reset(
 | |
|         cuma, &hcmd->curve_mapping.clipr, hcmd->curve_mapping.preset, CURVEMAP_SLOPE_POSITIVE);
 | |
|   }
 | |
| 
 | |
|   /* default to showing Saturation */
 | |
|   hcmd->curve_mapping.cur = 1;
 | |
| }
 | |
| 
 | |
| static void hue_correct_free_data(SequenceModifierData *smd)
 | |
| {
 | |
|   HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd;
 | |
| 
 | |
|   BKE_curvemapping_free_data(&hcmd->curve_mapping);
 | |
| }
 | |
| 
 | |
| static void hue_correct_copy_data(SequenceModifierData *target, SequenceModifierData *smd)
 | |
| {
 | |
|   HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd;
 | |
|   HueCorrectModifierData *hcmd_target = (HueCorrectModifierData *)target;
 | |
| 
 | |
|   BKE_curvemapping_copy_data(&hcmd_target->curve_mapping, &hcmd->curve_mapping);
 | |
| }
 | |
| 
 | |
| static void hue_correct_apply_threaded(int width,
 | |
|                                        int height,
 | |
|                                        unsigned char *rect,
 | |
|                                        float *rect_float,
 | |
|                                        unsigned char *mask_rect,
 | |
|                                        const float *mask_rect_float,
 | |
|                                        void *data_v)
 | |
| {
 | |
|   CurveMapping *curve_mapping = (CurveMapping *)data_v;
 | |
|   int x, y;
 | |
| 
 | |
|   for (y = 0; y < height; y++) {
 | |
|     for (x = 0; x < width; x++) {
 | |
|       int pixel_index = (y * width + x) * 4;
 | |
|       float pixel[3], result[3], mask[3] = {1.0f, 1.0f, 1.0f};
 | |
|       float hsv[3], f;
 | |
| 
 | |
|       if (rect_float) {
 | |
|         copy_v3_v3(pixel, rect_float + pixel_index);
 | |
|       }
 | |
|       else {
 | |
|         rgb_uchar_to_float(pixel, rect + pixel_index);
 | |
|       }
 | |
| 
 | |
|       rgb_to_hsv(pixel[0], pixel[1], pixel[2], hsv, hsv + 1, hsv + 2);
 | |
| 
 | |
|       /* adjust hue, scaling returned default 0.5 up to 1 */
 | |
|       f = BKE_curvemapping_evaluateF(curve_mapping, 0, hsv[0]);
 | |
|       hsv[0] += f - 0.5f;
 | |
| 
 | |
|       /* adjust saturation, scaling returned default 0.5 up to 1 */
 | |
|       f = BKE_curvemapping_evaluateF(curve_mapping, 1, hsv[0]);
 | |
|       hsv[1] *= (f * 2.0f);
 | |
| 
 | |
|       /* adjust value, scaling returned default 0.5 up to 1 */
 | |
|       f = BKE_curvemapping_evaluateF(curve_mapping, 2, hsv[0]);
 | |
|       hsv[2] *= (f * 2.0f);
 | |
| 
 | |
|       hsv[0] = hsv[0] - floorf(hsv[0]); /* mod 1.0 */
 | |
|       CLAMP(hsv[1], 0.0f, 1.0f);
 | |
| 
 | |
|       /* convert back to rgb */
 | |
|       hsv_to_rgb(hsv[0], hsv[1], hsv[2], result, result + 1, result + 2);
 | |
| 
 | |
|       if (mask_rect_float) {
 | |
|         copy_v3_v3(mask, mask_rect_float + pixel_index);
 | |
|       }
 | |
|       else if (mask_rect) {
 | |
|         rgb_uchar_to_float(mask, mask_rect + pixel_index);
 | |
|       }
 | |
| 
 | |
|       result[0] = pixel[0] * (1.0f - mask[0]) + result[0] * mask[0];
 | |
|       result[1] = pixel[1] * (1.0f - mask[1]) + result[1] * mask[1];
 | |
|       result[2] = pixel[2] * (1.0f - mask[2]) + result[2] * mask[2];
 | |
| 
 | |
|       if (rect_float) {
 | |
|         copy_v3_v3(rect_float + pixel_index, result);
 | |
|       }
 | |
|       else {
 | |
|         rgb_float_to_uchar(rect + pixel_index, result);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void hue_correct_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImBuf *mask)
 | |
| {
 | |
|   HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd;
 | |
| 
 | |
|   BKE_curvemapping_init(&hcmd->curve_mapping);
 | |
| 
 | |
|   modifier_apply_threaded(ibuf, mask, hue_correct_apply_threaded, &hcmd->curve_mapping);
 | |
| }
 | |
| 
 | |
| static SequenceModifierTypeInfo seqModifier_HueCorrect = {
 | |
|     CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "Hue Correct"), /* name */
 | |
|     "HueCorrectModifierData",                           /* struct_name */
 | |
|     sizeof(HueCorrectModifierData),                     /* struct_size */
 | |
|     hue_correct_init_data,                              /* init_data */
 | |
|     hue_correct_free_data,                              /* free_data */
 | |
|     hue_correct_copy_data,                              /* copy_data */
 | |
|     hue_correct_apply,                                  /* apply */
 | |
| };
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Bright/Contrast Modifier
 | |
|  * \{ */
 | |
| 
 | |
| typedef struct BrightContrastThreadData {
 | |
|   float bright;
 | |
|   float contrast;
 | |
| } BrightContrastThreadData;
 | |
| 
 | |
| static void brightcontrast_apply_threaded(int width,
 | |
|                                           int height,
 | |
|                                           unsigned char *rect,
 | |
|                                           float *rect_float,
 | |
|                                           unsigned char *mask_rect,
 | |
|                                           const float *mask_rect_float,
 | |
|                                           void *data_v)
 | |
| {
 | |
|   BrightContrastThreadData *data = (BrightContrastThreadData *)data_v;
 | |
|   int x, y;
 | |
| 
 | |
|   float i;
 | |
|   int c;
 | |
|   float a, b, v;
 | |
|   float brightness = data->bright / 100.0f;
 | |
|   float contrast = data->contrast;
 | |
|   float delta = contrast / 200.0f;
 | |
|   /*
 | |
|    * The algorithm is by Werner D. Streidt
 | |
|    * (http://visca.com/ffactory/archives/5-99/msg00021.html)
 | |
|    * Extracted of OpenCV demhist.c
 | |
|    */
 | |
|   if (contrast > 0) {
 | |
|     a = 1.0f - delta * 2.0f;
 | |
|     a = 1.0f / max_ff(a, FLT_EPSILON);
 | |
|     b = a * (brightness - delta);
 | |
|   }
 | |
|   else {
 | |
|     delta *= -1;
 | |
|     a = max_ff(1.0f - delta * 2.0f, 0.0f);
 | |
|     b = a * brightness + delta;
 | |
|   }
 | |
| 
 | |
|   for (y = 0; y < height; y++) {
 | |
|     for (x = 0; x < width; x++) {
 | |
|       int pixel_index = (y * width + x) * 4;
 | |
| 
 | |
|       if (rect) {
 | |
|         unsigned char *pixel = rect + pixel_index;
 | |
| 
 | |
|         for (c = 0; c < 3; c++) {
 | |
|           i = (float)pixel[c] / 255.0f;
 | |
|           v = a * i + b;
 | |
| 
 | |
|           if (mask_rect) {
 | |
|             unsigned char *m = mask_rect + pixel_index;
 | |
|             float t = (float)m[c] / 255.0f;
 | |
| 
 | |
|             v = (float)pixel[c] / 255.0f * (1.0f - t) + v * t;
 | |
|           }
 | |
| 
 | |
|           pixel[c] = unit_float_to_uchar_clamp(v);
 | |
|         }
 | |
|       }
 | |
|       else if (rect_float) {
 | |
|         float *pixel = rect_float + pixel_index;
 | |
| 
 | |
|         for (c = 0; c < 3; c++) {
 | |
|           i = pixel[c];
 | |
|           v = a * i + b;
 | |
| 
 | |
|           if (mask_rect_float) {
 | |
|             const float *m = mask_rect_float + pixel_index;
 | |
| 
 | |
|             pixel[c] = pixel[c] * (1.0f - m[c]) + v * m[c];
 | |
|           }
 | |
|           else {
 | |
|             pixel[c] = v;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void brightcontrast_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImBuf *mask)
 | |
| {
 | |
|   BrightContrastModifierData *bcmd = (BrightContrastModifierData *)smd;
 | |
|   BrightContrastThreadData data;
 | |
| 
 | |
|   data.bright = bcmd->bright;
 | |
|   data.contrast = bcmd->contrast;
 | |
| 
 | |
|   modifier_apply_threaded(ibuf, mask, brightcontrast_apply_threaded, &data);
 | |
| }
 | |
| 
 | |
| static SequenceModifierTypeInfo seqModifier_BrightContrast = {
 | |
|     CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "Bright/Contrast"), /* name */
 | |
|     "BrightContrastModifierData",                           /* struct_name */
 | |
|     sizeof(BrightContrastModifierData),                     /* struct_size */
 | |
|     NULL,                                                   /* init_data */
 | |
|     NULL,                                                   /* free_data */
 | |
|     NULL,                                                   /* copy_data */
 | |
|     brightcontrast_apply,                                   /* apply */
 | |
| };
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Mask Modifier
 | |
|  * \{ */
 | |
| 
 | |
| static void maskmodifier_apply_threaded(int width,
 | |
|                                         int height,
 | |
|                                         unsigned char *rect,
 | |
|                                         float *rect_float,
 | |
|                                         unsigned char *mask_rect,
 | |
|                                         const float *mask_rect_float,
 | |
|                                         void *UNUSED(data_v))
 | |
| {
 | |
|   int x, y;
 | |
| 
 | |
|   if (rect && !mask_rect) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (rect_float && !mask_rect_float) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   for (y = 0; y < height; y++) {
 | |
|     for (x = 0; x < width; x++) {
 | |
|       int pixel_index = (y * width + x) * 4;
 | |
| 
 | |
|       if (rect) {
 | |
|         unsigned char *pixel = rect + pixel_index;
 | |
|         unsigned char *mask_pixel = mask_rect + pixel_index;
 | |
|         unsigned char mask = min_iii(mask_pixel[0], mask_pixel[1], mask_pixel[2]);
 | |
| 
 | |
|         /* byte buffer is straight, so only affect on alpha itself,
 | |
|          * this is the only way to alpha-over byte strip after
 | |
|          * applying mask modifier.
 | |
|          */
 | |
|         pixel[3] = (float)(pixel[3] * mask) / 255.0f;
 | |
|       }
 | |
|       else if (rect_float) {
 | |
|         int c;
 | |
|         float *pixel = rect_float + pixel_index;
 | |
|         const float *mask_pixel = mask_rect_float + pixel_index;
 | |
|         float mask = min_fff(mask_pixel[0], mask_pixel[1], mask_pixel[2]);
 | |
| 
 | |
|         /* float buffers are premultiplied, so need to premul color
 | |
|          * as well to make it easy to alpha-over masted strip.
 | |
|          */
 | |
|         for (c = 0; c < 4; c++) {
 | |
|           pixel[c] = pixel[c] * mask;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void maskmodifier_apply(struct SequenceModifierData *UNUSED(smd), ImBuf *ibuf, ImBuf *mask)
 | |
| {
 | |
|   // SequencerMaskModifierData *bcmd = (SequencerMaskModifierData *)smd;
 | |
| 
 | |
|   modifier_apply_threaded(ibuf, mask, maskmodifier_apply_threaded, NULL);
 | |
| }
 | |
| 
 | |
| static SequenceModifierTypeInfo seqModifier_Mask = {
 | |
|     CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "Mask"), /* name */
 | |
|     "SequencerMaskModifierData",                 /* struct_name */
 | |
|     sizeof(SequencerMaskModifierData),           /* struct_size */
 | |
|     NULL,                                        /* init_data */
 | |
|     NULL,                                        /* free_data */
 | |
|     NULL,                                        /* copy_data */
 | |
|     maskmodifier_apply,                          /* apply */
 | |
| };
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Tonemap Modifier
 | |
|  * \{ */
 | |
| 
 | |
| typedef struct AvgLogLum {
 | |
|   SequencerTonemapModifierData *tmmd;
 | |
|   struct ColorSpace *colorspace;
 | |
|   float al;
 | |
|   float auto_key;
 | |
|   float lav;
 | |
|   float cav[4];
 | |
|   float igm;
 | |
| } AvgLogLum;
 | |
| 
 | |
| static void tonemapmodifier_init_data(SequenceModifierData *smd)
 | |
| {
 | |
|   SequencerTonemapModifierData *tmmd = (SequencerTonemapModifierData *)smd;
 | |
|   /* Same as tonemap compositor node. */
 | |
|   tmmd->type = SEQ_TONEMAP_RD_PHOTORECEPTOR;
 | |
|   tmmd->key = 0.18f;
 | |
|   tmmd->offset = 1.0f;
 | |
|   tmmd->gamma = 1.0f;
 | |
|   tmmd->intensity = 0.0f;
 | |
|   tmmd->contrast = 0.0f;
 | |
|   tmmd->adaptation = 1.0f;
 | |
|   tmmd->correction = 0.0f;
 | |
| }
 | |
| 
 | |
| static void tonemapmodifier_apply_threaded_simple(int width,
 | |
|                                                   int height,
 | |
|                                                   unsigned char *rect,
 | |
|                                                   float *rect_float,
 | |
|                                                   unsigned char *mask_rect,
 | |
|                                                   const float *mask_rect_float,
 | |
|                                                   void *data_v)
 | |
| {
 | |
|   AvgLogLum *avg = (AvgLogLum *)data_v;
 | |
|   for (int y = 0; y < height; y++) {
 | |
|     for (int x = 0; x < width; x++) {
 | |
|       int pixel_index = (y * width + x) * 4;
 | |
|       float input[4], output[4], mask[3] = {1.0f, 1.0f, 1.0f};
 | |
|       /* Get input value. */
 | |
|       if (rect_float) {
 | |
|         copy_v4_v4(input, &rect_float[pixel_index]);
 | |
|       }
 | |
|       else {
 | |
|         straight_uchar_to_premul_float(input, &rect[pixel_index]);
 | |
|       }
 | |
|       IMB_colormanagement_colorspace_to_scene_linear_v3(input, avg->colorspace);
 | |
|       copy_v4_v4(output, input);
 | |
|       /* Get mask value. */
 | |
|       if (mask_rect_float) {
 | |
|         copy_v3_v3(mask, mask_rect_float + pixel_index);
 | |
|       }
 | |
|       else if (mask_rect) {
 | |
|         rgb_uchar_to_float(mask, mask_rect + pixel_index);
 | |
|       }
 | |
|       /* Apply correction. */
 | |
|       mul_v3_fl(output, avg->al);
 | |
|       float dr = output[0] + avg->tmmd->offset;
 | |
|       float dg = output[1] + avg->tmmd->offset;
 | |
|       float db = output[2] + avg->tmmd->offset;
 | |
|       output[0] /= ((dr == 0.0f) ? 1.0f : dr);
 | |
|       output[1] /= ((dg == 0.0f) ? 1.0f : dg);
 | |
|       output[2] /= ((db == 0.0f) ? 1.0f : db);
 | |
|       const float igm = avg->igm;
 | |
|       if (igm != 0.0f) {
 | |
|         output[0] = powf(max_ff(output[0], 0.0f), igm);
 | |
|         output[1] = powf(max_ff(output[1], 0.0f), igm);
 | |
|         output[2] = powf(max_ff(output[2], 0.0f), igm);
 | |
|       }
 | |
|       /* Apply mask. */
 | |
|       output[0] = input[0] * (1.0f - mask[0]) + output[0] * mask[0];
 | |
|       output[1] = input[1] * (1.0f - mask[1]) + output[1] * mask[1];
 | |
|       output[2] = input[2] * (1.0f - mask[2]) + output[2] * mask[2];
 | |
|       /* Copy result back. */
 | |
|       IMB_colormanagement_scene_linear_to_colorspace_v3(output, avg->colorspace);
 | |
|       if (rect_float) {
 | |
|         copy_v4_v4(&rect_float[pixel_index], output);
 | |
|       }
 | |
|       else {
 | |
|         premul_float_to_straight_uchar(&rect[pixel_index], output);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void tonemapmodifier_apply_threaded_photoreceptor(int width,
 | |
|                                                          int height,
 | |
|                                                          unsigned char *rect,
 | |
|                                                          float *rect_float,
 | |
|                                                          unsigned char *mask_rect,
 | |
|                                                          const float *mask_rect_float,
 | |
|                                                          void *data_v)
 | |
| {
 | |
|   AvgLogLum *avg = (AvgLogLum *)data_v;
 | |
|   const float f = expf(-avg->tmmd->intensity);
 | |
|   const float m = (avg->tmmd->contrast > 0.0f) ? avg->tmmd->contrast :
 | |
|                                                  (0.3f + 0.7f * powf(avg->auto_key, 1.4f));
 | |
|   const float ic = 1.0f - avg->tmmd->correction, ia = 1.0f - avg->tmmd->adaptation;
 | |
|   for (int y = 0; y < height; y++) {
 | |
|     for (int x = 0; x < width; x++) {
 | |
|       int pixel_index = (y * width + x) * 4;
 | |
|       float input[4], output[4], mask[3] = {1.0f, 1.0f, 1.0f};
 | |
|       /* Get input value. */
 | |
|       if (rect_float) {
 | |
|         copy_v4_v4(input, &rect_float[pixel_index]);
 | |
|       }
 | |
|       else {
 | |
|         straight_uchar_to_premul_float(input, &rect[pixel_index]);
 | |
|       }
 | |
|       IMB_colormanagement_colorspace_to_scene_linear_v3(input, avg->colorspace);
 | |
|       copy_v4_v4(output, input);
 | |
|       /* Get mask value. */
 | |
|       if (mask_rect_float) {
 | |
|         copy_v3_v3(mask, mask_rect_float + pixel_index);
 | |
|       }
 | |
|       else if (mask_rect) {
 | |
|         rgb_uchar_to_float(mask, mask_rect + pixel_index);
 | |
|       }
 | |
|       /* Apply correction. */
 | |
|       const float L = IMB_colormanagement_get_luminance(output);
 | |
|       float I_l = output[0] + ic * (L - output[0]);
 | |
|       float I_g = avg->cav[0] + ic * (avg->lav - avg->cav[0]);
 | |
|       float I_a = I_l + ia * (I_g - I_l);
 | |
|       output[0] /= (output[0] + powf(f * I_a, m));
 | |
|       I_l = output[1] + ic * (L - output[1]);
 | |
|       I_g = avg->cav[1] + ic * (avg->lav - avg->cav[1]);
 | |
|       I_a = I_l + ia * (I_g - I_l);
 | |
|       output[1] /= (output[1] + powf(f * I_a, m));
 | |
|       I_l = output[2] + ic * (L - output[2]);
 | |
|       I_g = avg->cav[2] + ic * (avg->lav - avg->cav[2]);
 | |
|       I_a = I_l + ia * (I_g - I_l);
 | |
|       output[2] /= (output[2] + powf(f * I_a, m));
 | |
|       /* Apply mask. */
 | |
|       output[0] = input[0] * (1.0f - mask[0]) + output[0] * mask[0];
 | |
|       output[1] = input[1] * (1.0f - mask[1]) + output[1] * mask[1];
 | |
|       output[2] = input[2] * (1.0f - mask[2]) + output[2] * mask[2];
 | |
|       /* Copy result back. */
 | |
|       IMB_colormanagement_scene_linear_to_colorspace_v3(output, avg->colorspace);
 | |
|       if (rect_float) {
 | |
|         copy_v4_v4(&rect_float[pixel_index], output);
 | |
|       }
 | |
|       else {
 | |
|         premul_float_to_straight_uchar(&rect[pixel_index], output);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void tonemapmodifier_apply(struct SequenceModifierData *smd, ImBuf *ibuf, ImBuf *mask)
 | |
| {
 | |
|   SequencerTonemapModifierData *tmmd = (SequencerTonemapModifierData *)smd;
 | |
|   AvgLogLum data;
 | |
|   data.tmmd = tmmd;
 | |
|   data.colorspace = (ibuf->rect_float != NULL) ? ibuf->float_colorspace : ibuf->rect_colorspace;
 | |
|   float lsum = 0.0f;
 | |
|   int p = ibuf->x * ibuf->y;
 | |
|   float *fp = ibuf->rect_float;
 | |
|   unsigned char *cp = (unsigned char *)ibuf->rect;
 | |
|   float avl, maxl = -FLT_MAX, minl = FLT_MAX;
 | |
|   const float sc = 1.0f / p;
 | |
|   float Lav = 0.0f;
 | |
|   float cav[4] = {0.0f, 0.0f, 0.0f, 0.0f};
 | |
|   while (p--) {
 | |
|     float pixel[4];
 | |
|     if (fp != NULL) {
 | |
|       copy_v4_v4(pixel, fp);
 | |
|     }
 | |
|     else {
 | |
|       straight_uchar_to_premul_float(pixel, cp);
 | |
|     }
 | |
|     IMB_colormanagement_colorspace_to_scene_linear_v3(pixel, data.colorspace);
 | |
|     float L = IMB_colormanagement_get_luminance(pixel);
 | |
|     Lav += L;
 | |
|     add_v3_v3(cav, pixel);
 | |
|     lsum += logf(max_ff(L, 0.0f) + 1e-5f);
 | |
|     maxl = (L > maxl) ? L : maxl;
 | |
|     minl = (L < minl) ? L : minl;
 | |
|     if (fp != NULL) {
 | |
|       fp += 4;
 | |
|     }
 | |
|     else {
 | |
|       cp += 4;
 | |
|     }
 | |
|   }
 | |
|   data.lav = Lav * sc;
 | |
|   mul_v3_v3fl(data.cav, cav, sc);
 | |
|   maxl = logf(maxl + 1e-5f);
 | |
|   minl = logf(minl + 1e-5f);
 | |
|   avl = lsum * sc;
 | |
|   data.auto_key = (maxl > minl) ? ((maxl - avl) / (maxl - minl)) : 1.0f;
 | |
|   float al = expf(avl);
 | |
|   data.al = (al == 0.0f) ? 0.0f : (tmmd->key / al);
 | |
|   data.igm = (tmmd->gamma == 0.0f) ? 1.0f : (1.0f / tmmd->gamma);
 | |
| 
 | |
|   if (tmmd->type == SEQ_TONEMAP_RD_PHOTORECEPTOR) {
 | |
|     modifier_apply_threaded(ibuf, mask, tonemapmodifier_apply_threaded_photoreceptor, &data);
 | |
|   }
 | |
|   else /* if (tmmd->type == SEQ_TONEMAP_RD_SIMPLE) */ {
 | |
|     modifier_apply_threaded(ibuf, mask, tonemapmodifier_apply_threaded_simple, &data);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static SequenceModifierTypeInfo seqModifier_Tonemap = {
 | |
|     CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "Tonemap"), /* name */
 | |
|     "SequencerTonemapModifierData",                 /* struct_name */
 | |
|     sizeof(SequencerTonemapModifierData),           /* struct_size */
 | |
|     tonemapmodifier_init_data,                      /* init_data */
 | |
|     NULL,                                           /* free_data */
 | |
|     NULL,                                           /* copy_data */
 | |
|     tonemapmodifier_apply,                          /* apply */
 | |
| };
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Public Modifier Functions
 | |
|  * \{ */
 | |
| 
 | |
| static void sequence_modifier_type_info_init(void)
 | |
| {
 | |
| #define INIT_TYPE(typeName) (modifiersTypes[seqModifierType_##typeName] = &seqModifier_##typeName)
 | |
| 
 | |
|   INIT_TYPE(ColorBalance);
 | |
|   INIT_TYPE(Curves);
 | |
|   INIT_TYPE(HueCorrect);
 | |
|   INIT_TYPE(BrightContrast);
 | |
|   INIT_TYPE(Mask);
 | |
|   INIT_TYPE(WhiteBalance);
 | |
|   INIT_TYPE(Tonemap);
 | |
| 
 | |
| #undef INIT_TYPE
 | |
| }
 | |
| 
 | |
| const SequenceModifierTypeInfo *SEQ_modifier_type_info_get(int type)
 | |
| {
 | |
|   if (!modifierTypesInit) {
 | |
|     sequence_modifier_type_info_init();
 | |
|     modifierTypesInit = true;
 | |
|   }
 | |
| 
 | |
|   return modifiersTypes[type];
 | |
| }
 | |
| 
 | |
| SequenceModifierData *SEQ_modifier_new(Sequence *seq, const char *name, int type)
 | |
| {
 | |
|   SequenceModifierData *smd;
 | |
|   const SequenceModifierTypeInfo *smti = SEQ_modifier_type_info_get(type);
 | |
| 
 | |
|   smd = MEM_callocN(smti->struct_size, "sequence modifier");
 | |
| 
 | |
|   smd->type = type;
 | |
|   smd->flag |= SEQUENCE_MODIFIER_EXPANDED;
 | |
| 
 | |
|   if (!name || !name[0]) {
 | |
|     BLI_strncpy(smd->name, smti->name, sizeof(smd->name));
 | |
|   }
 | |
|   else {
 | |
|     BLI_strncpy(smd->name, name, sizeof(smd->name));
 | |
|   }
 | |
| 
 | |
|   BLI_addtail(&seq->modifiers, smd);
 | |
| 
 | |
|   SEQ_modifier_unique_name(seq, smd);
 | |
| 
 | |
|   if (smti->init_data) {
 | |
|     smti->init_data(smd);
 | |
|   }
 | |
| 
 | |
|   return smd;
 | |
| }
 | |
| 
 | |
| bool SEQ_modifier_remove(Sequence *seq, SequenceModifierData *smd)
 | |
| {
 | |
|   if (BLI_findindex(&seq->modifiers, smd) == -1) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   BLI_remlink(&seq->modifiers, smd);
 | |
|   SEQ_modifier_free(smd);
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void SEQ_modifier_clear(Sequence *seq)
 | |
| {
 | |
|   SequenceModifierData *smd, *smd_next;
 | |
| 
 | |
|   for (smd = seq->modifiers.first; smd; smd = smd_next) {
 | |
|     smd_next = smd->next;
 | |
|     SEQ_modifier_free(smd);
 | |
|   }
 | |
| 
 | |
|   BLI_listbase_clear(&seq->modifiers);
 | |
| }
 | |
| 
 | |
| void SEQ_modifier_free(SequenceModifierData *smd)
 | |
| {
 | |
|   const SequenceModifierTypeInfo *smti = SEQ_modifier_type_info_get(smd->type);
 | |
| 
 | |
|   if (smti && smti->free_data) {
 | |
|     smti->free_data(smd);
 | |
|   }
 | |
| 
 | |
|   MEM_freeN(smd);
 | |
| }
 | |
| 
 | |
| void SEQ_modifier_unique_name(Sequence *seq, SequenceModifierData *smd)
 | |
| {
 | |
|   const SequenceModifierTypeInfo *smti = SEQ_modifier_type_info_get(smd->type);
 | |
| 
 | |
|   BLI_uniquename(&seq->modifiers,
 | |
|                  smd,
 | |
|                  CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, smti->name),
 | |
|                  '.',
 | |
|                  offsetof(SequenceModifierData, name),
 | |
|                  sizeof(smd->name));
 | |
| }
 | |
| 
 | |
| SequenceModifierData *SEQ_modifier_find_by_name(Sequence *seq, const char *name)
 | |
| {
 | |
|   return BLI_findstring(&(seq->modifiers), name, offsetof(SequenceModifierData, name));
 | |
| }
 | |
| 
 | |
| ImBuf *SEQ_modifier_apply_stack(const SeqRenderData *context,
 | |
|                                 Sequence *seq,
 | |
|                                 ImBuf *ibuf,
 | |
|                                 int timeline_frame)
 | |
| {
 | |
|   SequenceModifierData *smd;
 | |
|   ImBuf *processed_ibuf = ibuf;
 | |
| 
 | |
|   if (seq->modifiers.first && (seq->flag & SEQ_USE_LINEAR_MODIFIERS)) {
 | |
|     processed_ibuf = IMB_dupImBuf(ibuf);
 | |
|     SEQ_render_imbuf_from_sequencer_space(context->scene, processed_ibuf);
 | |
|   }
 | |
| 
 | |
|   for (smd = seq->modifiers.first; smd; smd = smd->next) {
 | |
|     const SequenceModifierTypeInfo *smti = SEQ_modifier_type_info_get(smd->type);
 | |
| 
 | |
|     /* could happen if modifier is being removed or not exists in current version of blender */
 | |
|     if (!smti) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     /* modifier is muted, do nothing */
 | |
|     if (smd->flag & SEQUENCE_MODIFIER_MUTE) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if (smti->apply) {
 | |
|       int frame_offset;
 | |
|       if (smd->mask_time == SEQUENCE_MASK_TIME_RELATIVE) {
 | |
|         frame_offset = seq->start;
 | |
|       }
 | |
|       else /*if (smd->mask_time == SEQUENCE_MASK_TIME_ABSOLUTE)*/ {
 | |
|         frame_offset = smd->mask_id ? ((Mask *)smd->mask_id)->sfra : 0;
 | |
|       }
 | |
| 
 | |
|       ImBuf *mask = modifier_mask_get(
 | |
|           smd, context, timeline_frame, frame_offset, ibuf->rect_float != NULL);
 | |
| 
 | |
|       if (processed_ibuf == ibuf) {
 | |
|         processed_ibuf = IMB_dupImBuf(ibuf);
 | |
|       }
 | |
| 
 | |
|       smti->apply(smd, processed_ibuf, mask);
 | |
| 
 | |
|       if (mask) {
 | |
|         IMB_freeImBuf(mask);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (seq->modifiers.first && (seq->flag & SEQ_USE_LINEAR_MODIFIERS)) {
 | |
|     seq_imbuf_to_sequencer_space(context->scene, processed_ibuf, false);
 | |
|   }
 | |
| 
 | |
|   return processed_ibuf;
 | |
| }
 | |
| 
 | |
| void SEQ_modifier_list_copy(Sequence *seqn, Sequence *seq)
 | |
| {
 | |
|   SequenceModifierData *smd;
 | |
| 
 | |
|   for (smd = seq->modifiers.first; smd; smd = smd->next) {
 | |
|     SequenceModifierData *smdn;
 | |
|     const SequenceModifierTypeInfo *smti = SEQ_modifier_type_info_get(smd->type);
 | |
| 
 | |
|     smdn = MEM_dupallocN(smd);
 | |
| 
 | |
|     if (smti && smti->copy_data) {
 | |
|       smti->copy_data(smdn, smd);
 | |
|     }
 | |
| 
 | |
|     smdn->next = smdn->prev = NULL;
 | |
|     BLI_addtail(&seqn->modifiers, smdn);
 | |
|   }
 | |
| }
 | |
| 
 | |
| int SEQ_sequence_supports_modifiers(Sequence *seq)
 | |
| {
 | |
|   return !ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD);
 | |
| }
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name .blend File I/O
 | |
|  * \{ */
 | |
| 
 | |
| void SEQ_modifier_blend_write(BlendWriter *writer, ListBase *modbase)
 | |
| {
 | |
|   LISTBASE_FOREACH (SequenceModifierData *, smd, modbase) {
 | |
|     const SequenceModifierTypeInfo *smti = SEQ_modifier_type_info_get(smd->type);
 | |
| 
 | |
|     if (smti) {
 | |
|       BLO_write_struct_by_name(writer, smti->struct_name, smd);
 | |
| 
 | |
|       if (smd->type == seqModifierType_Curves) {
 | |
|         CurvesModifierData *cmd = (CurvesModifierData *)smd;
 | |
| 
 | |
|         BKE_curvemapping_blend_write(writer, &cmd->curve_mapping);
 | |
|       }
 | |
|       else if (smd->type == seqModifierType_HueCorrect) {
 | |
|         HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd;
 | |
| 
 | |
|         BKE_curvemapping_blend_write(writer, &hcmd->curve_mapping);
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       BLO_write_struct(writer, SequenceModifierData, smd);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SEQ_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb)
 | |
| {
 | |
|   BLO_read_list(reader, lb);
 | |
| 
 | |
|   LISTBASE_FOREACH (SequenceModifierData *, smd, lb) {
 | |
|     if (smd->mask_sequence) {
 | |
|       BLO_read_data_address(reader, &smd->mask_sequence);
 | |
|     }
 | |
| 
 | |
|     if (smd->type == seqModifierType_Curves) {
 | |
|       CurvesModifierData *cmd = (CurvesModifierData *)smd;
 | |
| 
 | |
|       BKE_curvemapping_blend_read(reader, &cmd->curve_mapping);
 | |
|     }
 | |
|     else if (smd->type == seqModifierType_HueCorrect) {
 | |
|       HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd;
 | |
| 
 | |
|       BKE_curvemapping_blend_read(reader, &hcmd->curve_mapping);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SEQ_modifier_blend_read_lib(BlendLibReader *reader, Scene *scene, ListBase *lb)
 | |
| {
 | |
|   LISTBASE_FOREACH (SequenceModifierData *, smd, lb) {
 | |
|     if (smd->mask_id) {
 | |
|       BLO_read_id_address(reader, scene->id.lib, &smd->mask_id);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /** \} */
 |