/*
 * Copyright (c) 2013 Paul B Mahol
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg 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.
 *
 * FFmpeg 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 FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef AVFILTER_BLEND_INIT_H
#define AVFILTER_BLEND_INIT_H

#include "config.h"
#include "libavutil/attributes.h"
#include "libavutil/imgutils.h"
#include "blend.h"

#define DEPTH 8
#include "blend_modes.c"

#undef DEPTH
#define DEPTH 9
#include "blend_modes.c"

#undef DEPTH
#define DEPTH 10
#include "blend_modes.c"

#undef DEPTH
#define DEPTH 12
#include "blend_modes.c"

#undef DEPTH
#define DEPTH 14
#include "blend_modes.c"

#undef DEPTH
#define DEPTH 16
#include "blend_modes.c"

#undef DEPTH
#define DEPTH 32
#include "blend_modes.c"

#define COPY(src, depth)                                                            \
static void blend_copy ## src##_##depth(const uint8_t *top, ptrdiff_t top_linesize,    \
                            const uint8_t *bottom, ptrdiff_t bottom_linesize,\
                            uint8_t *dst, ptrdiff_t dst_linesize,            \
                            ptrdiff_t width, ptrdiff_t height,               \
                            FilterParams *param, SliceParams *sliceparam)    \
{                                                                            \
    av_image_copy_plane(dst, dst_linesize, src, src ## _linesize,            \
                        width * depth / 8, height);                          \
}

COPY(top, 8)
COPY(bottom, 8)

COPY(top, 16)
COPY(bottom, 16)

COPY(top, 32)
COPY(bottom, 32)

#undef COPY

#define BLEND_NORMAL(name, type)                                                  \
static void blend_normal_##name(const uint8_t *_top, ptrdiff_t top_linesize,      \
                                const uint8_t *_bottom, ptrdiff_t bottom_linesize,\
                                uint8_t *_dst, ptrdiff_t dst_linesize,            \
                                ptrdiff_t width, ptrdiff_t height,                \
                                FilterParams *param, SliceParams *sliceparam)     \
{                                                                                 \
    const float opacity = param->opacity;                                         \
                                                                                  \
    for (int i = 0; i < height; i++) {                                            \
        const type *top = (const type*)_top;                                      \
        const type *bottom = (const type*)_bottom;                                \
        type *dst = (type*)_dst;                                                  \
        for (int j = 0; j < width; j++) {                                         \
            dst[j] = top[j] * opacity + bottom[j] * (1.f - opacity);              \
        }                                                                         \
        _dst    += dst_linesize;                                                  \
        _top    += top_linesize;                                                  \
        _bottom += bottom_linesize;                                               \
    }                                                                             \
}

BLEND_NORMAL(8bit,  uint8_t)
BLEND_NORMAL(16bit, uint16_t)
BLEND_NORMAL(32bit, float)

#define DEFINE_INIT_BLEND_FUNC(depth, nbits)                                          \
static av_cold void init_blend_func_##depth##_##nbits##bit(FilterParams *param)       \
{                                                                                     \
    switch (param->mode) {                                                            \
    case BLEND_ADDITION:     param->blend = blend_addition_##depth##bit;     break;   \
    case BLEND_GRAINMERGE:   param->blend = blend_grainmerge_##depth##bit;   break;   \
    case BLEND_AND:          param->blend = blend_and_##nbits##bit;          break;   \
    case BLEND_AVERAGE:      param->blend = blend_average_##nbits##bit;      break;   \
    case BLEND_BURN:         param->blend = blend_burn_##depth##bit;         break;   \
    case BLEND_DARKEN:       param->blend = blend_darken_##nbits##bit;       break;   \
    case BLEND_DIFFERENCE:   param->blend = blend_difference_##nbits##bit;   break;   \
    case BLEND_GRAINEXTRACT: param->blend = blend_grainextract_##depth##bit; break;   \
    case BLEND_DIVIDE:       param->blend = blend_divide_##depth##bit;       break;   \
    case BLEND_DODGE:        param->blend = blend_dodge_##depth##bit;        break;   \
    case BLEND_EXCLUSION:    param->blend = blend_exclusion_##depth##bit;    break;   \
    case BLEND_EXTREMITY:    param->blend = blend_extremity_##depth##bit;    break;   \
    case BLEND_FREEZE:       param->blend = blend_freeze_##depth##bit;       break;   \
    case BLEND_GLOW:         param->blend = blend_glow_##depth##bit;         break;   \
    case BLEND_HARDLIGHT:    param->blend = blend_hardlight_##depth##bit;    break;   \
    case BLEND_HARDMIX:      param->blend = blend_hardmix_##depth##bit;      break;   \
    case BLEND_HEAT:         param->blend = blend_heat_##depth##bit;         break;   \
    case BLEND_LIGHTEN:      param->blend = blend_lighten_##nbits##bit;      break;   \
    case BLEND_LINEARLIGHT:  param->blend = blend_linearlight_##depth##bit;  break;   \
    case BLEND_MULTIPLY:     param->blend = blend_multiply_##depth##bit;     break;   \
    case BLEND_MULTIPLY128:  param->blend = blend_multiply128_##depth##bit;  break;   \
    case BLEND_NEGATION:     param->blend = blend_negation_##depth##bit;     break;   \
    case BLEND_NORMAL:       param->blend = blend_normal_##nbits##bit;       break;   \
    case BLEND_OR:           param->blend = blend_or_##nbits##bit;           break;   \
    case BLEND_OVERLAY:      param->blend = blend_overlay_##depth##bit;      break;   \
    case BLEND_PHOENIX:      param->blend = blend_phoenix_##depth##bit;      break;   \
    case BLEND_PINLIGHT:     param->blend = blend_pinlight_##depth##bit;     break;   \
    case BLEND_REFLECT:      param->blend = blend_reflect_##depth##bit;      break;   \
    case BLEND_SCREEN:       param->blend = blend_screen_##depth##bit;       break;   \
    case BLEND_SOFTLIGHT:    param->blend = blend_softlight_##depth##bit;    break;   \
    case BLEND_SUBTRACT:     param->blend = blend_subtract_##nbits##bit;     break;   \
    case BLEND_VIVIDLIGHT:   param->blend = blend_vividlight_##depth##bit;   break;   \
    case BLEND_XOR:          param->blend = blend_xor_##nbits##bit;          break;   \
    case BLEND_SOFTDIFFERENCE:param->blend=blend_softdifference_##depth##bit;break;   \
    case BLEND_GEOMETRIC:    param->blend = blend_geometric_##nbits##bit;    break;   \
    case BLEND_HARMONIC:     param->blend = blend_harmonic_##nbits##bit;     break;   \
    case BLEND_BLEACH:       param->blend = blend_bleach_##depth##bit;       break;   \
    case BLEND_STAIN:        param->blend = blend_stain_##depth##bit;        break;   \
    case BLEND_INTERPOLATE:  param->blend = blend_interpolate_##depth##bit;  break;   \
    case BLEND_HARDOVERLAY:  param->blend = blend_hardoverlay_##depth##bit;  break;   \
    }                                                                                 \
}
DEFINE_INIT_BLEND_FUNC(8, 8)
DEFINE_INIT_BLEND_FUNC(9, 16)
DEFINE_INIT_BLEND_FUNC(10, 16)
DEFINE_INIT_BLEND_FUNC(12, 16)
DEFINE_INIT_BLEND_FUNC(14, 16)
DEFINE_INIT_BLEND_FUNC(16, 16)
DEFINE_INIT_BLEND_FUNC(32, 32)

av_unused static void ff_blend_init(FilterParams *param, int depth)
{
    switch (depth) {
    case 8:
        init_blend_func_8_8bit(param);
        break;
    case 9:
        init_blend_func_9_16bit(param);
        break;
    case 10:
        init_blend_func_10_16bit(param);
        break;
    case 12:
        init_blend_func_12_16bit(param);
        break;
    case 14:
        init_blend_func_14_16bit(param);
        break;
    case 16:
        init_blend_func_16_16bit(param);
        break;
    case 32:
        init_blend_func_32_32bit(param);
        break;
    }

    if (param->opacity == 0 && param->mode != BLEND_NORMAL) {
        param->blend = depth > 8 ? depth > 16 ? blend_copytop_32 : blend_copytop_16 : blend_copytop_8;
    } else if (param->mode == BLEND_NORMAL) {
        if (param->opacity == 1)
            param->blend = depth > 8 ? depth > 16 ? blend_copytop_32 : blend_copytop_16 : blend_copytop_8;
        else if (param->opacity == 0)
            param->blend = depth > 8 ? depth > 16 ? blend_copybottom_32 : blend_copybottom_16 : blend_copybottom_8;
    }

#if ARCH_X86 && HAVE_X86ASM
    ff_blend_init_x86(param, depth);
#endif
}

#endif /* AVFILTER_BLEND_INIT_H */
