panoramix.c 40.1 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * panoramix.c : Wall panoramic video with edge blending plugin for vlc
 *****************************************************************************
 * Copyright (C) 2000, 2001, 2002, 2003 VideoLAN
Pierre's avatar
Pierre committed
5
 * $Id$
6 7 8 9
 *
 * Authors: Cedric Cocquebert <cedric.cocquebert@supelec.fr>
 *          based on Samuel Hocevar <sam@zoy.org>
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
10 11 12
 * This program 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
13 14 15 16
 * (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
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
17 18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
19
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
20 21 22
 * You should have received a copy of the GNU Lesser 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.
23 24 25 26 27 28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
32 33
#include <math.h>
#include <assert.h>
34

35
#include <vlc_common.h>
36
#include <vlc_plugin.h>
37 38 39
#include <vlc_video_splitter.h>

/* FIXME it is needed for VOUT_ALIGN_* only */
Clément Stenac's avatar
Clément Stenac committed
40
#include <vlc_vout.h>
41

42
#define OVERLAP
43 44

#ifdef OVERLAP
45
/* OS CODE DEPENDENT to get display dimensions */
46
#   ifdef _WIN32
47 48
#       include <windows.h>
#   else
49 50
#       include <xcb/xcb.h>
#       include <xcb/randr.h>
51
#   endif
52 53
#endif

Kaarlo Raiha's avatar
Kaarlo Raiha committed
54 55 56
#define ROW_MAX (15)
#define COL_MAX (15)

57 58 59 60 61 62 63 64 65 66 67 68
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
#define COLS_TEXT N_("Number of columns")
#define COLS_LONGTEXT N_("Select the number of horizontal video windows in " \
    "which to split the video")

#define ROWS_TEXT N_("Number of rows")
#define ROWS_LONGTEXT N_("Select the number of vertical video windows in " \
    "which to split the video")

#define ACTIVE_TEXT N_("Active windows")
69
#define ACTIVE_LONGTEXT N_("Comma-separated list of active windows, " \
70 71
    "defaults to all")

72 73
#define CFG_PREFIX "panoramix-"

74 75 76
#define PANORAMIX_HELP N_("Split the video in multiple windows to " \
    "display on a wall of screens")

77 78 79 80
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

vlc_module_begin()
81 82
    set_description( N_("Panoramix: wall with overlap video filter") )
    set_shortname( N_("Panoramix" ))
83
    set_help(PANORAMIX_HELP)
84
    set_capability( "video splitter", 0 )
85
    set_category( CAT_VIDEO )
86
    set_subcategory( SUBCAT_VIDEO_SPLITTER )
87

88
    add_integer( CFG_PREFIX "cols", -1, COLS_TEXT, COLS_LONGTEXT, true )
Kaarlo Raiha's avatar
Kaarlo Raiha committed
89
    change_integer_range( -1, COL_MAX )
90
    add_integer( CFG_PREFIX "rows", -1, ROWS_TEXT, ROWS_LONGTEXT, true )
Kaarlo Raiha's avatar
Kaarlo Raiha committed
91
    change_integer_range( -1, ROW_MAX )
92 93 94 95

#ifdef OVERLAP
#define LENGTH_TEXT N_("length of the overlapping area (in %)")
#define LENGTH_LONGTEXT N_("Select in percent the length of the blended zone")
96
    add_integer_with_range( CFG_PREFIX "bz-length", 100, 0, 100, LENGTH_TEXT, LENGTH_LONGTEXT, true )
97 98 99

#define HEIGHT_TEXT N_("height of the overlapping area (in %)")
#define HEIGHT_LONGTEXT N_("Select in percent the height of the blended zone (case of 2x2 wall)")
100
    add_integer_with_range( CFG_PREFIX "bz-height", 100, 0, 100, HEIGHT_TEXT, HEIGHT_LONGTEXT, true )
101 102 103

#define ATTENUATION_TEXT N_("Attenuation")
#define ATTENUATION_LONGTEXT N_("Check this option if you want attenuate blended zone by this plug-in (if option is unchecked, attenuate is made by opengl)")
104
    add_bool( CFG_PREFIX "attenuate", true, ATTENUATION_TEXT, ATTENUATION_LONGTEXT, false )
105 106

#define BEGIN_TEXT N_("Attenuation, begin (in %)")
Kaarlo Raiha's avatar
Kaarlo Raiha committed
107
#define BEGIN_LONGTEXT N_("Select in percent the Lagrange coefficient of the beginning blended zone")
108
    add_integer_with_range( CFG_PREFIX "bz-begin", 0, 0, 100, BEGIN_TEXT, BEGIN_LONGTEXT, true )
109 110

#define MIDDLE_TEXT N_("Attenuation, middle (in %)")
Kaarlo Raiha's avatar
Kaarlo Raiha committed
111
#define MIDDLE_LONGTEXT N_("Select in percent the Lagrange coefficient of the middle of blended zone")
112
    add_integer_with_range( CFG_PREFIX "bz-middle", 50, 0, 100, MIDDLE_TEXT, MIDDLE_LONGTEXT, false )
113 114

#define END_TEXT N_("Attenuation, end (in %)")
Kaarlo Raiha's avatar
Kaarlo Raiha committed
115
#define END_LONGTEXT N_("Select in percent the Lagrange coefficient of the end of blended zone")
116
    add_integer_with_range( CFG_PREFIX "bz-end", 100, 0, 100, END_TEXT, END_LONGTEXT, true )
117 118 119

#define MIDDLE_POS_TEXT N_("middle position (in %)")
#define MIDDLE_POS_LONGTEXT N_("Select in percent (50 is center) the position of the middle point (Lagrange) of blended zone")
120
    add_integer_with_range( CFG_PREFIX "bz-middle-pos", 50, 1, 99, MIDDLE_POS_TEXT, MIDDLE_POS_LONGTEXT, false )
121 122
#define RGAMMA_TEXT N_("Gamma (Red) correction")
#define RGAMMA_LONGTEXT N_("Select the gamma for the correction of blended zone (Red or Y component)")
123
    add_float_with_range( CFG_PREFIX "bz-gamma-red", 1, 0, 5, RGAMMA_TEXT, RGAMMA_LONGTEXT, true )
124 125 126

#define GGAMMA_TEXT N_("Gamma (Green) correction")
#define GGAMMA_LONGTEXT N_("Select the gamma for the correction of blended zone (Green or U component)")
127
    add_float_with_range( CFG_PREFIX "bz-gamma-green", 1, 0, 5, GGAMMA_TEXT, GGAMMA_LONGTEXT, true )
128 129 130

#define BGAMMA_TEXT N_("Gamma (Blue) correction")
#define BGAMMA_LONGTEXT N_("Select the gamma for the correction of blended zone (Blue or V component)")
131
    add_float_with_range( CFG_PREFIX "bz-gamma-blue", 1, 0, 5, BGAMMA_TEXT, BGAMMA_LONGTEXT, true )
132

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
#define RGAMMA_BC_TEXT N_("Black Crush for Red")
#define RGAMMA_BC_LONGTEXT N_("Select the Black Crush of blended zone (Red or Y component)")
#define GGAMMA_BC_TEXT N_("Black Crush for Green")
#define GGAMMA_BC_LONGTEXT N_("Select the Black Crush of blended zone (Green or U component)")
#define BGAMMA_BC_TEXT N_("Black Crush for Blue")
#define BGAMMA_BC_LONGTEXT N_("Select the Black Crush of blended zone (Blue or V component)")

#define RGAMMA_WC_TEXT N_("White Crush for Red")
#define RGAMMA_WC_LONGTEXT N_("Select the White Crush of blended zone (Red or Y component)")
#define GGAMMA_WC_TEXT N_("White Crush for Green")
#define GGAMMA_WC_LONGTEXT N_("Select the White Crush of blended zone (Green or U component)")
#define BGAMMA_WC_TEXT N_("White Crush for Blue")
#define BGAMMA_WC_LONGTEXT N_("Select the White Crush of blended zone (Blue or V component)")

#define RGAMMA_BL_TEXT N_("Black Level for Red")
#define RGAMMA_BL_LONGTEXT N_("Select the Black Level of blended zone (Red or Y component)")
#define GGAMMA_BL_TEXT N_("Black Level for Green")
#define GGAMMA_BL_LONGTEXT N_("Select the Black Level of blended zone (Green or U component)")
#define BGAMMA_BL_TEXT N_("Black Level for Blue")
#define BGAMMA_BL_LONGTEXT N_("Select the Black Level of blended zone (Blue or V component)")

#define RGAMMA_WL_TEXT N_("White Level for Red")
#define RGAMMA_WL_LONGTEXT N_("Select the White Level of blended zone (Red or Y component)")
#define GGAMMA_WL_TEXT N_("White Level for Green")
#define GGAMMA_WL_LONGTEXT N_("Select the White Level of blended zone (Green or U component)")
#define BGAMMA_WL_TEXT N_("White Level for Blue")
#define BGAMMA_WL_LONGTEXT N_("Select the White Level of blended zone (Blue or V component)")
160 161 162 163 164 165 166 167 168 169 170 171
    add_integer_with_range( CFG_PREFIX "bz-blackcrush-red", 140, 0, 255, RGAMMA_BC_TEXT, RGAMMA_BC_LONGTEXT, true )
    add_integer_with_range( CFG_PREFIX "bz-blackcrush-green", 140, 0, 255, GGAMMA_BC_TEXT, GGAMMA_BC_LONGTEXT, true )
    add_integer_with_range( CFG_PREFIX "bz-blackcrush-blue", 140, 0, 255, BGAMMA_BC_TEXT, BGAMMA_BC_LONGTEXT, true )
    add_integer_with_range( CFG_PREFIX "bz-whitecrush-red", 200, 0, 255, RGAMMA_WC_TEXT, RGAMMA_WC_LONGTEXT, true )
    add_integer_with_range( CFG_PREFIX "bz-whitecrush-green", 200, 0, 255, GGAMMA_WC_TEXT, GGAMMA_WC_LONGTEXT, true )
    add_integer_with_range( CFG_PREFIX "bz-whitecrush-blue", 200, 0, 255, BGAMMA_WC_TEXT, BGAMMA_WC_LONGTEXT, true )
    add_integer_with_range( CFG_PREFIX "bz-blacklevel-red", 150, 0, 255, RGAMMA_BL_TEXT, RGAMMA_BL_LONGTEXT, true )
    add_integer_with_range( CFG_PREFIX "bz-blacklevel-green", 150, 0, 255, GGAMMA_BL_TEXT, GGAMMA_BL_LONGTEXT, true )
    add_integer_with_range( CFG_PREFIX "bz-blacklevel-blue", 150, 0, 255, BGAMMA_BL_TEXT, BGAMMA_BL_LONGTEXT, true )
    add_integer_with_range( CFG_PREFIX "bz-whitelevel-red", 0, 0, 255, RGAMMA_WL_TEXT, RGAMMA_WL_LONGTEXT, true )
    add_integer_with_range( CFG_PREFIX "bz-whitelevel-green", 0, 0, 255, GGAMMA_WL_TEXT, GGAMMA_WL_LONGTEXT, true )
    add_integer_with_range( CFG_PREFIX "bz-whitelevel-blue", 0, 0, 255, BGAMMA_WL_TEXT, BGAMMA_WL_LONGTEXT, true )
172
#ifndef _WIN32
173
    add_obsolete_bool( CFG_PREFIX "xinerama" );
174
#endif
175
    add_obsolete_bool( CFG_PREFIX "offset-x" )
176 177
#endif

178
    add_string( CFG_PREFIX "active", NULL, ACTIVE_TEXT, ACTIVE_LONGTEXT, true )
179

180
    add_shortcut( "panoramix" )
181 182 183
    set_callbacks( Open, Close )
vlc_module_end()

184

185 186 187
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
188
static const char *const ppsz_filter_options[] = {
189
    "cols", "rows", "bz-length", "bz-height", "attenuate",
190 191 192 193 194
    "bz-begin", "bz-middle", "bz-end", "bz-middle-pos", "bz-gamma-red",
    "bz-gamma-green", "bz-gamma-blue", "bz-blackcrush-red",
    "bz-blackcrush-green", "bz-blackcrush-blue", "bz-whitecrush-red",
    "bz-whitecrush-green", "bz-whitecrush-blue", "bz-blacklevel-red",
    "bz-blacklevel-green", "bz-blacklevel-blue", "bz-whitelevel-red",
195
    "bz-whitelevel-green", "bz-whitelevel-blue", "active",
196 197 198
    NULL
};

199 200 201 202
#define ACCURACY 1000

/* */
static inline int clip_accuracy( int a )
203
{
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
    return (a > ACCURACY) ? ACCURACY : (a < 0) ? 0 : a;
}
static inline float clip_unit( float f )
{
    return f < 0.0 ? 0.0 : ( f > 1.0 ? 1.0 : f );
}

/* */
typedef struct
{
    float f_black_crush;
    float f_black_level;

    float f_white_crush;
    float f_white_level;

    float f_gamma;
} panoramix_gamma_t;

typedef struct
{
    struct
    {
        int i_left;
        int i_right;
        int i_top;
        int i_bottom;
    } black;
    struct
    {
        int i_left;
        int i_right;
        int i_top;
        int i_bottom;
    } attenuate;
} panoramix_filter_t;

typedef struct
{
    bool b_active;
    int  i_output;

    /* Output position and size */
    int i_x;
    int i_y;
    int i_width;
    int i_height;
    int i_align;

    /* Source position and size */
    int  i_src_x;
    int  i_src_y;
    int  i_src_width;
    int  i_src_height;

    /* Filter configuration to use to create the output */
    panoramix_filter_t filter;

} panoramix_output_t;

typedef struct
{
    vlc_fourcc_t i_chroma;

    const int pi_div_w[VOUT_MAX_PLANES];
    const int pi_div_h[VOUT_MAX_PLANES];

    const int pi_black[VOUT_MAX_PLANES];

    bool b_planar;

} panoramix_chroma_t;

struct video_splitter_sys_t
{
    const panoramix_chroma_t *p_chroma;

    /* */
282
    bool   b_attenuate;
283 284 285
    unsigned int bz_length, bz_height;
    unsigned int bz_begin, bz_middle, bz_end;
    unsigned int bz_middle_pos;
286 287
    unsigned int a_0, a_1, a_2;

288 289 290 291 292 293 294 295 296 297 298 299
    int lambdav[VOUT_MAX_PLANES][2][ACCURACY/2]; /* [plane][position][?] */
    int lambdah[VOUT_MAX_PLANES][2][ACCURACY/2];

    unsigned int i_overlap_w2;  /* Half overlap width */
    unsigned int i_overlap_h2;  /* Half overlap height */
    uint8_t      p_lut[VOUT_MAX_PLANES][ACCURACY + 1][256];

    /* */
    int i_col;
    int i_row;
    panoramix_output_t pp_output[COL_MAX][ROW_MAX]; /* [x][y] */
};
300

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
/* */
static int Filter( video_splitter_t *, picture_t *pp_dst[], picture_t * );

static int Mouse( video_splitter_t *, vlc_mouse_t *,
                  int i_index,
                  const vlc_mouse_t *p_old, const vlc_mouse_t *p_new );


/* */
static int Configuration( panoramix_output_t pp_output[ROW_MAX][COL_MAX],
                          int i_col, int i_row,
                          int i_src_width, int i_src_height,
                          int i_half_w, int i_half_h,
                          bool b_attenuate,
                          const bool *pb_active );
static double GammaFactor( const panoramix_gamma_t *, float f_value );

static void FilterPlanar( uint8_t *p_out, int i_out_pitch,
                          const uint8_t *p_in, int i_in_pitch,
                          int i_copy_pitch,
                          int i_copy_lines,
                          int i_pixel_black,
                          const panoramix_filter_t *,
                          uint8_t p_lut[ACCURACY + 1][256],
                          int lambdav[2][ACCURACY/2],
                          int lambdah[2][ACCURACY/2] );

/* */
static const panoramix_chroma_t p_chroma_array[] = {
    /* Planar chroma */
    { VLC_CODEC_I410, { 1, 4, 4, }, { 1, 1, 1, }, { 0, 128, 128 }, true },
    { VLC_CODEC_I411, { 1, 4, 4, }, { 1, 4, 4, }, { 0, 128, 128 }, true },
    { VLC_CODEC_YV12, { 1, 2, 2, }, { 1, 2, 2, }, { 0, 128, 128 }, true },
    { VLC_CODEC_I420, { 1, 2, 2, }, { 1, 2, 2, }, { 0, 128, 128 }, true },
    { VLC_CODEC_J420, { 1, 2, 2, }, { 1, 2, 2, }, { 0, 128, 128 }, true },
    { VLC_CODEC_I422, { 1, 2, 2, }, { 1, 1, 1, }, { 0, 128, 128 }, true },
    { VLC_CODEC_J422, { 1, 2, 2, }, { 1, 1, 1, }, { 0, 128, 128 }, true },
    { VLC_CODEC_I440, { 1, 1, 1, }, { 1, 2, 2, }, { 0, 128, 128 }, true },
    { VLC_CODEC_J440, { 1, 1, 1, }, { 1, 2, 2, }, { 0, 128, 128 }, true },
    { VLC_CODEC_I444, { 1, 1, 1, }, { 1, 1, 1, }, { 0, 128, 128 }, true },
    /* TODO packed chroma (yuv/rgb) ? */

    { 0, {0, }, { 0, }, { 0, 0, 0 }, false }
};
345

346
#ifndef _WIN32
347
/* Get the number of outputs */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
348
static unsigned CountMonitors( vlc_object_t *obj )
349
{
350
    char *psz_display = var_InheritString( obj, "x11-display" );
351 352
    int snum;
    xcb_connection_t *conn = xcb_connect( psz_display, &snum );
Rémi Duraffort's avatar
Rémi Duraffort committed
353
    free( psz_display );
354 355 356 357 358 359 360 361
    if( xcb_connection_has_error( conn ) )
        return 0;

    const xcb_setup_t *setup = xcb_get_setup( conn );
    xcb_screen_t *scr = NULL;
    for( xcb_screen_iterator_t i = xcb_setup_roots_iterator( setup );
         i.rem > 0; xcb_screen_next( &i ) )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
362
         if( snum == 0 )
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
         {
             scr = i.data;
             break;
         }
         snum--;
    }

    unsigned n = 0;
    if( scr == NULL )
        goto error;

    xcb_randr_query_version_reply_t *v =
        xcb_randr_query_version_reply( conn,
            xcb_randr_query_version( conn, 1, 2 ), NULL );
    if( v == NULL )
        goto error;
    msg_Dbg( obj, "using X RandR extension v%"PRIu32".%"PRIu32,
             v->major_version, v->minor_version );
    free( v );

    xcb_randr_get_screen_resources_reply_t *r =
        xcb_randr_get_screen_resources_reply( conn,
            xcb_randr_get_screen_resources( conn, scr->root ), NULL );
    if( r == NULL )
        goto error;

    const xcb_randr_output_t *outputs =
        xcb_randr_get_screen_resources_outputs( r );
    for( unsigned i = 0; i < r->num_outputs; i++ )
    {
        xcb_randr_get_output_info_reply_t *output =
            xcb_randr_get_output_info_reply( conn,
                xcb_randr_get_output_info( conn, outputs[i], 0 ), NULL );
        if( output == NULL )
            continue;
        /* FIXME: do not count cloned outputs multiple times */
        /* XXX: what to do with UNKNOWN state connections? */
        n += output->connection == XCB_RANDR_CONNECTION_CONNECTED;
        free( output );
    }
    free( r );
    msg_Dbg( obj, "X randr has %u outputs", n );

error:
    xcb_disconnect( conn );
    return n;
}
#endif

412
/*****************************************************************************
413 414 415
 * Open: allocates Wall video thread output method
 *****************************************************************************
 * This function allocates and initializes a Wall vout method.
416
 *****************************************************************************/
417
static int Open( vlc_object_t *p_this )
418
{
419 420
    video_splitter_t *p_splitter = (video_splitter_t*)p_this;
    video_splitter_sys_t *p_sys;
421

422 423
    const panoramix_chroma_t *p_chroma;
    for( int i = 0; ; i++ )
424
    {
425 426
        vlc_fourcc_t i_chroma = p_chroma_array[i].i_chroma;
        if( !i_chroma )
427
        {
428 429 430 431 432 433 434
            msg_Err( p_splitter, "colorspace not supported by plug-in !" );
            return VLC_EGENERIC;
        }
        if( i_chroma == p_splitter->fmt.i_chroma )
        {
            p_chroma = &p_chroma_array[i];
            break;
435 436 437 438
        }
    }

    /* Allocate structure */
439 440
    p_splitter->p_sys = p_sys = malloc( sizeof( *p_sys ) );
    if( !p_sys )
441 442
        return VLC_ENOMEM;

443 444
    /* */
    p_sys->p_chroma = p_chroma;
445

446 447 448
    /* */
    config_ChainParse( p_splitter, CFG_PREFIX, ppsz_filter_options,
                       p_splitter->p_cfg );
449

450
    /* */
451 452
    p_sys->i_col = var_InheritInteger( p_splitter, CFG_PREFIX "cols" );
    p_sys->i_row = var_InheritInteger( p_splitter, CFG_PREFIX "rows" );
453

454 455
    /* Autodetect number of displays */
    if( p_sys->i_col < 0 || p_sys->i_row < 0 )
456
    {
457
#ifdef _WIN32
458 459
        const int i_monitor_count = GetSystemMetrics(SM_CMONITORS);
        if( i_monitor_count > 1 )
460
        {
461 462 463
            p_sys->i_col = GetSystemMetrics( SM_CXVIRTUALSCREEN ) / GetSystemMetrics( SM_CXSCREEN );
            p_sys->i_row = GetSystemMetrics( SM_CYVIRTUALSCREEN ) / GetSystemMetrics( SM_CYSCREEN );
            if( p_sys->i_col * p_sys->i_row != i_monitor_count )
464
            {
465 466
                p_sys->i_col = i_monitor_count;
                p_sys->i_row = 1;
467 468 469
            }
        }
#else
470 471 472 473 474 475 476 477 478
        const unsigned i_monitors = CountMonitors( p_this );
        if( i_monitors > 1 ) /* Find closest to square */
            for( unsigned w = 1; (i_monitors / w) >= w ; w++ )
            {
                if( i_monitors % w )
                    continue;
                p_sys->i_row = w;
                p_sys->i_col = i_monitors / w;
            }
479
#endif
480
        /* By default do 2x1 */
481
        if( p_sys->i_row < 0 )
482
            p_sys->i_row = 1;
483 484
        if( p_sys->i_col < 0 )
            p_sys->i_col = 2;
485 486 487
        var_SetInteger( p_splitter, CFG_PREFIX "cols", p_sys->i_col);
        var_SetInteger( p_splitter, CFG_PREFIX "rows", p_sys->i_row);
    }
488

489
    /* */
490 491 492 493 494 495 496
    p_sys->b_attenuate = var_InheritBool( p_splitter, CFG_PREFIX "attenuate");
    p_sys->bz_length = var_InheritInteger( p_splitter, CFG_PREFIX "bz-length" );
    p_sys->bz_height = var_InheritInteger( p_splitter, CFG_PREFIX "bz-height" );
    p_sys->bz_begin = var_InheritInteger( p_splitter, CFG_PREFIX "bz-begin" );
    p_sys->bz_middle = var_InheritInteger( p_splitter, CFG_PREFIX "bz-middle" );
    p_sys->bz_end = var_InheritInteger( p_splitter, CFG_PREFIX "bz-end" );
    p_sys->bz_middle_pos = var_InheritInteger( p_splitter, CFG_PREFIX "bz-middle-pos" );
497 498 499 500 501 502 503
    double d_p = 100.0 / p_sys->bz_middle_pos;

    p_sys->a_2 = d_p * p_sys->bz_begin - (double)(d_p * d_p / (d_p - 1)) * p_sys->bz_middle + (double)(d_p / (d_p - 1)) * p_sys->bz_end;
    p_sys->a_1 = -(d_p + 1) * p_sys->bz_begin + (double)(d_p * d_p / (d_p - 1)) * p_sys->bz_middle - (double)(1 / (d_p - 1)) * p_sys->bz_end;
    p_sys->a_0 =  p_sys->bz_begin;

    /* */
504 505
    p_sys->i_col = VLC_CLIP( p_sys->i_col, 1, COL_MAX );
    p_sys->i_row = VLC_CLIP( p_sys->i_row, 1, ROW_MAX );
506 507 508 509
    msg_Dbg( p_splitter, "opening a %i x %i wall",
             p_sys->i_col, p_sys->i_row );

    if( p_sys->bz_length > 0 && ( p_sys->i_row > 1 || p_sys->i_col > 1 ) )
510
    {
511 512 513
        const int i_overlap_w2_max = p_splitter->fmt.i_width  / p_sys->i_col / 2;
        const int i_overlap_h2_max = p_splitter->fmt.i_height / p_sys->i_row / 2;
        const int i_overlap2_max = __MIN( i_overlap_w2_max, i_overlap_h2_max );
514

515 516 517 518
        if( p_sys->i_col > 1 )
            p_sys->i_overlap_w2 = i_overlap2_max * p_sys->bz_length / 100;
        else
            p_sys->i_overlap_w2 = 0;
519

520 521 522 523 524 525 526 527 528
        if( p_sys->i_row > 1 )
            p_sys->i_overlap_h2 = i_overlap2_max * p_sys->bz_height / 100;
        else
            p_sys->i_overlap_h2 = 0;

        /* */
        int i_div_max_w = 1;
        int i_div_max_h = 1;
        for( int i = 0; i < VOUT_MAX_PLANES; i++ )
529
        {
530 531
            i_div_max_w = __MAX( i_div_max_w, p_chroma->pi_div_w[i] );
            i_div_max_h = __MAX( i_div_max_h, p_chroma->pi_div_h[i] );
532
        }
533 534
        p_sys->i_overlap_w2 = i_div_max_w * (p_sys->i_overlap_w2 / i_div_max_w);
        p_sys->i_overlap_h2 = i_div_max_h * (p_sys->i_overlap_h2 / i_div_max_h);
535 536 537
    }
    else
    {
538 539 540 541 542 543 544 545 546
        p_sys->i_overlap_w2 = 0;
        p_sys->i_overlap_h2 = 0;
    }

    /* Compute attenuate parameters */
    if( p_sys->b_attenuate )
    {
        panoramix_gamma_t p_gamma[VOUT_MAX_PLANES];

547 548 549
        p_gamma[0].f_gamma = var_InheritFloat( p_splitter, CFG_PREFIX "bz-gamma-red" );
        p_gamma[1].f_gamma = var_InheritFloat( p_splitter, CFG_PREFIX "bz-gamma-green" );
        p_gamma[2].f_gamma = var_InheritFloat( p_splitter, CFG_PREFIX "bz-gamma-blue" );
550

551 552 553 554 555 556
        p_gamma[0].f_black_crush = var_InheritInteger( p_splitter, CFG_PREFIX "bz-blackcrush-red" ) / 255.0;
        p_gamma[1].f_black_crush = var_InheritInteger( p_splitter, CFG_PREFIX "bz-blackcrush-green" ) / 255.0;
        p_gamma[2].f_black_crush = var_InheritInteger( p_splitter, CFG_PREFIX "bz-blackcrush-blue" ) / 255.0;
        p_gamma[0].f_white_crush = var_InheritInteger( p_splitter, CFG_PREFIX "bz-whitecrush-red" ) / 255.0;
        p_gamma[1].f_white_crush = var_InheritInteger( p_splitter, CFG_PREFIX "bz-whitecrush-green" ) / 255.0;
        p_gamma[2].f_white_crush = var_InheritInteger( p_splitter, CFG_PREFIX "bz-whitecrush-blue" ) / 255.0;
557

558 559 560 561 562 563
        p_gamma[0].f_black_level = var_InheritInteger( p_splitter, CFG_PREFIX "bz-blacklevel-red" ) / 255.0;
        p_gamma[1].f_black_level = var_InheritInteger( p_splitter, CFG_PREFIX "bz-blacklevel-green" ) / 255.0;
        p_gamma[2].f_black_level = var_InheritInteger( p_splitter, CFG_PREFIX "bz-blacklevel-blue" ) / 255.0;
        p_gamma[0].f_white_level = var_InheritInteger( p_splitter, CFG_PREFIX "bz-whitelevel-red" ) / 255.0;
        p_gamma[1].f_white_level = var_InheritInteger( p_splitter, CFG_PREFIX "bz-whitelevel-green" ) / 255.0;
        p_gamma[2].f_white_level = var_InheritInteger( p_splitter, CFG_PREFIX "bz-whitelevel-blue" ) / 255.0;
564 565

        for( int i = 3; i < VOUT_MAX_PLANES; i++ )
566
        {
567 568 569 570 571 572
            /* Initialize unsupported planes */
            p_gamma[i].f_gamma = 1.0;
            p_gamma[i].f_black_crush = 140.0/255.0;
            p_gamma[i].f_white_crush = 200.0/255.0;
            p_gamma[i].f_black_level = 150.0/255.0;
            p_gamma[i].f_white_level = 0.0/255.0;
573 574
        }

575 576 577 578 579 580 581
        if( p_chroma->i_chroma == VLC_CODEC_YV12 )
        {
            /* Exchange U and V */
            panoramix_gamma_t t = p_gamma[1];
            p_gamma[1] = p_gamma[2];
            p_gamma[2] = t;
        }
Kaarlo Raiha's avatar
Kaarlo Raiha committed
582

583
        for( int i_index = 0; i_index < 256; i_index++ )
584
        {
585
            for( int i_index2 = 0; i_index2 <= ACCURACY; i_index2++ )
586
            {
587 588 589
                for( int i_plane = 0; i_plane < VOUT_MAX_PLANES; i_plane++ )
                {
                    double f_factor = GammaFactor( &p_gamma[i_plane], (float)i_index / 255.0 );
590

591 592 593 594
                    float f_lut = clip_unit( 1.0 - ((ACCURACY - (float)i_index2) * f_factor / (ACCURACY - 1)) );

                    p_sys->p_lut[i_plane][i_index2][i_index] = f_lut * i_index + (int)( (1.0 - f_lut) * (float)p_chroma->pi_black[i_plane] );
                }
595
            }
596
        }
597

598 599 600 601 602 603 604 605
        for( int i_plane = 0; i_plane < VOUT_MAX_PLANES; i_plane++ )
        {
            if( !p_chroma->pi_div_w[i_plane] || !p_chroma->pi_div_h[i_plane] )
                continue;
            const int i_length_w = (2 * p_sys->i_overlap_w2) / p_chroma->pi_div_w[i_plane];
            const int i_length_h = (2 * p_sys->i_overlap_h2) / p_chroma->pi_div_h[i_plane];

            for( int i_dir = 0; i_dir < 2; i_dir++ )
606
            {
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
                const int i_length = i_dir == 0 ? i_length_w : i_length_h;
                const int i_den = i_length * i_length;
                const int a_2 = p_sys->a_2 * (ACCURACY / 100);
                const int a_1 = p_sys->a_1 * i_length * (ACCURACY / 100);
                const int a_0 = p_sys->a_0 * i_den * (ACCURACY / 100);

                for( int i_position = 0; i_position < 2; i_position++ )
                {
                    for( int i_index = 0; i_index < i_length; i_index++ )
                    {
                        const int v = i_position == 1 ? i_index : (i_length - i_index);
                        const int i_lambda = clip_accuracy( ACCURACY - (a_2 * v*v + a_1 * v + a_0) / i_den );

                        if( i_dir == 0 )
                            p_sys->lambdav[i_plane][i_position][i_index] = i_lambda;
                        else
                            p_sys->lambdah[i_plane][i_position][i_index] = i_lambda;
                    }
                }
626 627 628 629
            }
        }
    }

630
    /* */
631
    char *psz_state = var_InheritString( p_splitter, CFG_PREFIX "active" );
632

633 634
    /* */
    bool pb_active[COL_MAX*ROW_MAX];
635

636 637
    for( int i = 0; i < COL_MAX*ROW_MAX; i++ )
        pb_active[i] = psz_state == NULL;
638

639 640 641 642 643 644 645
    /* Parse active list if provided */
    char *psz_tmp = psz_state;
    while( psz_tmp && *psz_tmp )
    {
        char *psz_next = strchr( psz_tmp, ',' );
        if( psz_next )
            *psz_next++ = '\0';
646

647 648 649
        const int i_index = atoi( psz_tmp );
        if( i_index >= 0 && i_index < COL_MAX*ROW_MAX )
            pb_active[i_index] = true;
650

651
        psz_tmp = psz_next;
652
    }
653 654 655 656 657 658 659 660 661 662 663 664 665
    free( psz_state );

    /* */
    p_splitter->i_output =
        Configuration( p_sys->pp_output,
                       p_sys->i_col, p_sys->i_row,
                       p_splitter->fmt.i_width, p_splitter->fmt.i_height,
                       p_sys->i_overlap_w2, p_sys->i_overlap_h2,
                       p_sys->b_attenuate,
                       pb_active );
    p_splitter->p_output = calloc( p_splitter->i_output,
                                   sizeof(*p_splitter->p_output) );
    if( !p_splitter->p_output )
666
    {
667 668
        free( p_sys );
        return VLC_ENOMEM;
669
    }
670

671 672 673 674 675 676 677
    for( int y = 0; y < p_sys->i_row; y++ )
    {
        for( int x = 0; x < p_sys->i_col; x++ )
        {
            panoramix_output_t *p_output = &p_sys->pp_output[x][y];
            if( !p_output->b_active )
                continue;
678

679
            video_splitter_output_t *p_cfg = &p_splitter->p_output[p_output->i_output];
680

681 682 683 684 685 686
            /* */
            video_format_Copy( &p_cfg->fmt, &p_splitter->fmt );
            p_cfg->fmt.i_visible_width  =
            p_cfg->fmt.i_width          = p_output->i_width;
            p_cfg->fmt.i_visible_height =
            p_cfg->fmt.i_height         = p_output->i_height;
687

688 689 690
            p_cfg->window.i_x     = p_output->i_x;
            p_cfg->window.i_y     = p_output->i_y;
            p_cfg->window.i_align = p_output->i_align;
691

692
            p_cfg->psz_module = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
693 694
        }
    }
695 696


697 698 699
    /* */
    p_splitter->pf_filter = Filter;
    p_splitter->pf_mouse  = Mouse;
700

701 702
    return VLC_SUCCESS;
}
Laurent Aimar's avatar
Laurent Aimar committed
703

704 705 706 707
/**
 * Terminate a splitter module
 */
static void Close( vlc_object_t *p_this )
708
{
709 710
    video_splitter_t *p_splitter = (video_splitter_t*)p_this;
    video_splitter_sys_t *p_sys = p_splitter->p_sys;
711

712 713 714
    free( p_splitter->p_output );
    free( p_sys );
}
715

716 717 718 719 720 721
/**
 * It creates multiples pictures from the source one
 */
static int Filter( video_splitter_t *p_splitter, picture_t *pp_dst[], picture_t *p_src )
{
    video_splitter_sys_t *p_sys = p_splitter->p_sys;
Laurent Aimar's avatar
Laurent Aimar committed
722

723
    if( video_splitter_NewPicture( p_splitter, pp_dst ) )
724
    {
725 726
        picture_Release( p_src );
        return VLC_EGENERIC;
727
    }
728

729
    for( int y = 0; y < p_sys->i_row; y++ )
730
    {
731
        for( int x = 0; x < p_sys->i_col; x++ )
732
        {
733 734 735
            const panoramix_output_t *p_output = &p_sys->pp_output[x][y];
            if( !p_output->b_active )
                continue;
736

737
            /* */
738
            picture_t *p_dst = pp_dst[p_output->i_output];
739

740 741
            /* */
            picture_CopyProperties( p_dst, p_src );
742

743
            /* */
744
            for( int i_plane = 0; i_plane < p_src->i_planes; i_plane++ )
745
            {
746 747
                const int i_div_w = p_sys->p_chroma->pi_div_w[i_plane];
                const int i_div_h = p_sys->p_chroma->pi_div_h[i_plane];
748

749 750
                if( !i_div_w || !i_div_h )
                    continue;
751

752 753
                const plane_t *p_srcp = &p_src->p[i_plane];
                const plane_t *p_dstp = &p_dst->p[i_plane];
754

755 756 757 758 759 760
                /* */
                panoramix_filter_t filter;
                filter.black.i_right  = p_output->filter.black.i_right / i_div_w;
                filter.black.i_left   = p_output->filter.black.i_left / i_div_w;
                filter.black.i_top    = p_output->filter.black.i_top / i_div_h;
                filter.black.i_bottom = p_output->filter.black.i_bottom / i_div_h;
761

762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
                filter.attenuate.i_right  = p_output->filter.attenuate.i_right / i_div_w;
                filter.attenuate.i_left   = p_output->filter.attenuate.i_left / i_div_w;
                filter.attenuate.i_top    = p_output->filter.attenuate.i_top / i_div_h;
                filter.attenuate.i_bottom = p_output->filter.attenuate.i_bottom / i_div_h;

                /* */
                const int i_x = p_output->i_src_x/i_div_w;
                const int i_y = p_output->i_src_y/i_div_h;

                assert( p_sys->p_chroma->b_planar );
                FilterPlanar( p_dstp->p_pixels, p_dstp->i_pitch,
                              &p_srcp->p_pixels[i_y * p_srcp->i_pitch + i_x * p_srcp->i_pixel_pitch], p_srcp->i_pitch,
                              p_output->i_src_width/i_div_w, p_output->i_src_height/i_div_h,
                              p_sys->p_chroma->pi_black[i_plane],
                              &filter,
                              p_sys->p_lut[i_plane],
                              p_sys->lambdav[i_plane],
                              p_sys->lambdah[i_plane] );
780 781 782 783
            }
        }
    }

784
    picture_Release( p_src );
785 786 787
    return VLC_SUCCESS;
}

788 789 790 791 792 793
/**
 * It converts mouse events
 */
static int Mouse( video_splitter_t *p_splitter, vlc_mouse_t *p_mouse,
                  int i_index,
                  const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
794
{
795 796
    video_splitter_sys_t *p_sys = p_splitter->p_sys;
    VLC_UNUSED(p_old);
797

798 799 800 801
    for( int y = 0; y < p_sys->i_row; y++ )
    {
        for( int x = 0; x < p_sys->i_col; x++ )
        {
802
            const panoramix_output_t *p_output = &p_sys->pp_output[x][y];
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
            if( p_output->b_active && p_output->i_output == i_index )
            {
                const int i_x = p_new->i_x - p_output->filter.black.i_left;
                const int i_y = p_new->i_y - p_output->filter.black.i_top;
                if( i_x >= 0 && i_x < p_output->i_width  - p_output->filter.black.i_right &&
                    i_y >= 0 && i_y < p_output->i_height - p_output->filter.black.i_bottom )
                {
                    *p_mouse = *p_new;
                    p_mouse->i_x = p_output->i_src_x + i_x;
                    p_mouse->i_y = p_output->i_src_y + i_y;
                    return VLC_SUCCESS;
                }
            }
        }
    }
    return VLC_EGENERIC;
819 820
}

821 822 823 824
/* It return a coefficient between 0.0 and 1.0 to be applied to the given
 * value
 */
static double GammaFactor( const panoramix_gamma_t *g, float f_value )
825
{
826
    if( f_value <= g->f_black_crush )
827
    {
828
        float f_input = f_value * g->f_black_level / g->f_black_crush + (1.0 - g->f_black_level);
829

830
        return pow( f_input, 1.0 / g->f_gamma );
831
    }
832 833 834 835 836 837
    else if( f_value >= g->f_white_crush )
    {
        float f_input = (f_value * (1.0 - (g->f_white_level + 1.0)) + (g->f_white_level + 1.0) * g->f_white_crush - 1.0) / (g->f_white_crush - 1.0);
        return pow( f_input, 1.0 / g->f_gamma );
    }
    return 1.0;
838 839
}

840 841 842 843 844 845 846 847 848
/**
 * It creates the panoramix configuration
 */
static int Configuration( panoramix_output_t pp_output[ROW_MAX][COL_MAX],
                          int i_col, int i_row,
                          int i_src_width, int i_src_height,
                          int i_half_w, int i_half_h,
                          bool b_attenuate,
                          const bool *pb_active )
849 850
{
#ifdef OVERLAP
851 852 853
    const bool b_overlap = true;
#else
    const bool b_overlap = false;
854 855
#endif

856 857 858
    /* */
    int i_output = 0;
    for( int y = 0, i_src_y = 0, i_dst_y = 0; y < i_row; y++ )
859
    {
860 861
        const bool b_row_first = y == 0;
        const bool b_row_last  = y == i_row - 1;
862

863 864 865 866
        /* Compute source height */
        int i_win_height = (i_src_height / i_row ) & ~1;
        if( b_row_last )
            i_win_height = i_src_height - y * i_win_height;
867

868 869 870 871
        for( int x = 0, i_src_x = 0, i_dst_x = 0; x < i_col; x++ )
        {
            const bool b_col_first = x == 0;
            const bool b_col_last  = x == i_col - 1;
872

873 874 875 876
            /* Compute source width */
            int i_win_width  = (i_src_width  / i_col ) & ~1;
            if( b_col_last )
                i_win_width  = i_src_width  - x * i_win_width;
877

878 879
            /* Compute filter configuration */
            panoramix_filter_t cfg;
880

881 882
            memset( &cfg, 0, sizeof(cfg) );
            if( b_overlap && b_attenuate )
883
            {
884
                if( i_col > 2 )
885
                {
886 887 888 889
                    if( b_col_first )
                        cfg.black.i_left   = i_half_w;
                    if( b_col_last )
                        cfg.black.i_right  = i_half_w;
890
                }
891
                if( i_row > 2 )
892
                {
893 894 895 896
                    if( b_row_first )
                        cfg.black.i_top    = i_half_h;
                    if( b_row_last )
                        cfg.black.i_bottom = i_half_h;
897
                }
898 899 900 901 902 903 904 905
                if( !b_col_first )
                    cfg.attenuate.i_left   = 2 * i_half_w;
                if( !b_col_last )
                    cfg.attenuate.i_right  = 2 * i_half_w;
                if( !b_row_first )
                    cfg.attenuate.i_top    = 2 * i_half_h;
                if( !b_row_last )
                    cfg.attenuate.i_bottom = 2 * i_half_h;
906 907
            }

908 909 910
            /* Compute alignment */
            int i_align = 0;
            if( i_col > 1 )
911
            {
912 913 914 915
                if( b_col_first )
                    i_align |= VOUT_ALIGN_RIGHT;
                if( b_col_last )
                    i_align |= VOUT_ALIGN_LEFT;
916
            }
917
            if( i_row > 1 )
918
            {
919 920 921 922
                if( b_row_first )
                    i_align |= VOUT_ALIGN_BOTTOM;
                if( b_row_last )
                    i_align |= VOUT_ALIGN_TOP;
923 924
            }

925 926
            /* */
            panoramix_output_t *p_output = &pp_output[x][y];
927

928 929 930 931 932
            /* */
            p_output->i_src_x = i_src_x - cfg.attenuate.i_left/2;
            p_output->i_src_y = i_src_y - cfg.attenuate.i_top/2;
            p_output->i_src_width  = i_win_width  + cfg.attenuate.i_left/2 + cfg.attenuate.i_right/2;
            p_output->i_src_height = i_win_height + cfg.attenuate.i_top/2  + cfg.attenuate.i_bottom/2;
933

934 935
            /* */
            p_output->filter = cfg;
936

937 938 939 940
            /* */
            p_output->i_align = i_align;
            p_output->i_x = i_dst_x;
            p_output->i_y = i_dst_y;
941

942 943 944 945 946 947 948 949 950 951 952 953 954
            p_output->i_width  = cfg.black.i_left + p_output->i_src_width  + cfg.black.i_right;
            p_output->i_height = cfg.black.i_top  + p_output->i_src_height + cfg.black.i_bottom;

            /* */
            p_output->b_active = pb_active[y * i_col + x] &&
                                 p_output->i_width > 0 &&
                                 p_output->i_height > 0;
            if( p_output->b_active )
                p_output->i_output = i_output++;

            /* */
            i_src_x += i_win_width;
            i_dst_x += p_output->i_width;
955
        }
956 957
        i_src_y += i_win_height;
        i_dst_y += pp_output[0][y].i_height;
958
    }
959
    return i_output;
960 961
}

962 963 964 965 966 967 968 969 970 971 972 973
/**
 * It filters a video plane
 */
static void FilterPlanar( uint8_t *p_out, int i_out_pitch,
                          const uint8_t *p_in, int i_in_pitch,
                          int i_copy_pitch,
                          int i_copy_lines,
                          int i_pixel_black,
                          const panoramix_filter_t *p_cfg,
                          uint8_t p_lut[ACCURACY + 1][256],
                          int lambdav[2][ACCURACY/2],
                          int lambdah[2][ACCURACY/2] )
974
{
975 976 977 978 979
    /* */
    assert( !p_cfg->black.i_left   || !p_cfg->attenuate.i_left );
    assert( !p_cfg->black.i_right  || !p_cfg->attenuate.i_right );
    assert( !p_cfg->black.i_top    || !p_cfg->attenuate.i_top );
    assert( !p_cfg->black.i_bottom || !p_cfg->attenuate.i_bottom );
980

981
    const int i_out_width = p_cfg->black.i_left + i_copy_pitch + p_cfg->black.i_right;
982

983 984
    /* Top black border */
    for( int b = 0; b < p_cfg->black.i_top; b++ )
985
    {
Rafaël Carré's avatar
Rafaël Carré committed
986
        memset( p_out, i_pixel_black, i_out_width );
987 988
        p_out += i_out_pitch;
    }
989

990 991 992 993
    for( int y = 0; y < i_copy_lines; y++ )
    {
        const uint8_t *p_src = p_in;
        uint8_t *p_dst = p_out;
994

995 996
        /* Black border on the left */
        if( p_cfg->black.i_left > 0 )
997
        {
998 999
            memset( p_dst, i_pixel_black, p_cfg->black.i_left );
            p_dst += p_cfg->black.i_left;
1000
        }
1001 1002 1003 1004 1005 1006
        /* Attenuated video on the left */
        for( int i = 0; i < p_cfg->attenuate.i_left; i++ )
            *p_dst++ = p_lut[lambdav[0][i]][*p_src++];

        /* Unmodified video */
        const int i_unmodified_width = i_copy_pitch - p_cfg->attenuate.i_left - p_cfg->attenuate.i_right;
Rafaël Carré's avatar
Rafaël Carré committed
1007
        memcpy( p_dst, p_src, i_unmodified_width );
1008 1009 1010 1011 1012 1013 1014 1015
        p_dst += i_unmodified_width;
        p_src += i_unmodified_width;

        /* Attenuated video on the right */
        for( int i = 0; i < p_cfg->attenuate.i_right; i++ )
            *p_dst++ = p_lut[lambdav[1][i]][*p_src++];
        /* Black border on the right */
        if( p_cfg->black.i_right > 0 )
1016
        {
1017 1018
            memset( p_dst, i_pixel_black, p_cfg->black.i_right );
            p_dst += p_cfg->black.i_right;
1019 1020
        }

1021 1022 1023 1024
        /* Attenuate complete line at top/bottom */
        const bool b_attenuate_top    = y < p_cfg->attenuate.i_top;
        const bool b_attenuate_bottom = y >= i_copy_lines - p_cfg->attenuate.i_bottom;
        if( b_attenuate_top || b_attenuate_bottom )
1025
        {
1026 1027
            const int i_index = b_attenuate_top ? lambdah[0][y] : lambdah[1][y - (i_copy_lines - p_cfg->attenuate.i_bottom)];
            for( int i = 0; i < i_out_width; i++)
1028
                p_out[i] = p_lut[i_index][p_out[i]];
1029
        }
1030

1031 1032 1033
        /* */
        p_in  += i_in_pitch;
        p_out += i_out_pitch;
1034
    }
1035 1036
    /* Bottom black border */
    for( int b = 0; b < p_cfg->black.i_bottom; b++ )
1037
    {
Rafaël Carré's avatar
Rafaël Carré committed
1038
        memset( p_out, i_pixel_black, i_out_width );
1039
        p_out += i_out_pitch;
1040
    }
1041
}
1042 1043


1044 1045 1046

#if 0
static void RenderPackedRGB( vout_thread_t *p_vout, picture_t *p_pic )
1047
{
1048 1049
    int length;
    length = 2 * p_sys->i_overlap_w2 * p_pic->p->i_pixel_pitch;
1050

1051
    if (p_sys->b_has_changed)
1052
    {
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
        int i_plane_;
        int i_col_mod;
        Denom = F2(length / p_pic->p->i_pixel_pitch);
        a_2 = p_sys->a_2 * (ACCURACY / 100);
        a_1 = p_sys->a_1 * 2 * p_sys->i_overlap_w2 * (ACCURACY / 100);
        a_0 = p_sys->a_0 * Denom * (ACCURACY / 100);
        for(i_col_mod = 0; i_col_mod < 2; i_col_mod++)
            for (i_index = 0; i_index < length / p_pic->p->i_pixel_pitch; i_index++)
                for (i_plane_ =  0; i_plane_ < p_pic->p->i_pixel_pitch; i_plane_++)
                    p_sys->lambda[i_col_mod][i_plane_][i_index] = clip_accuracy(!i_col_mod ? ACCURACY - (F4(a_2, a_1, i_index) + a_0) / Denom : ACCURACY - (F4(a_2, a_1,(length / p_pic->p->i_pixel_pitch) - i_index) + a_0) / Denom);
    }
1064

1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
    length = 2 * p_sys->i_overlap_h2;
    if (p_sys->b_has_changed)
    {
        int i_plane_;
        int i_row_mod;
        Denom = F2(length);
        a_2 = p_sys->a_2 * (ACCURACY / 100);
        a_1 = p_sys->a_1 * length * (ACCURACY / 100);
        a_0 = p_sys->a_0 * Denom * (ACCURACY / 100);
        for(i_row_mod = 0; i_row_mod < 2; i_row_mod++)
          for (i_index = 0; i_index < length; i_index++)
            for (i_plane_ =  0; i_plane_ < p_pic->p->i_pixel_pitch; i_plane_++)
                p_sys->lambda2[i_row_mod][i_plane_][i_index] = clip_accuracy(!i_row_mod ? ACCURACY - (F4(a_2, a_1, i_index) + a_0) / Denom : ACCURACY - (F4(a_2, a_1,(length) - i_index) + a_0) / Denom);
1078 1079
    }
}
1080 1081
#endif

1082