i420_rgb.c 20.1 KB
Newer Older
1 2 3
/*****************************************************************************
 * i420_rgb.c : YUV to bitmap RGB conversion module for vlc
 *****************************************************************************
4
 * Copyright (C) 2000, 2001, 2004, 2008 the VideoLAN team
5
 * $Id$
6
 *
7 8
 * Authors: Sam Hocevar <sam@zoy.org>
 *          Damien Fouilleul <damienf@videolan.org>
9 10 11 12 13
 *
 * 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.
14
 *
15 16 17 18 19 20 21
 * 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
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25 26 27 28
 *****************************************************************************/

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

29 30 31 32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33 34
#include <math.h>                                            /* exp(), pow() */

35
#include <vlc_common.h>
36
#include <vlc_plugin.h>
37
#include <vlc_filter.h>
Clément Stenac's avatar
Clément Stenac committed
38
#include <vlc_vout.h>
39 40 41 42 43 44

#include "i420_rgb.h"
#if defined (MODULE_NAME_IS_i420_rgb)
#   include "i420_rgb_c.h"
#endif

45 46 47
/*****************************************************************************
 * RGB2PIXEL: assemble RGB components to a pixel value, returns a uint32_t
 *****************************************************************************/
48 49 50 51 52 53 54
#define RGB2PIXEL( p_filter, i_r, i_g, i_b )                 \
    (((((uint32_t)i_r) >> p_filter->fmt_out.video.i_rrshift) \
                       << p_filter->fmt_out.video.i_lrshift) \
   | ((((uint32_t)i_g) >> p_filter->fmt_out.video.i_rgshift) \
                       << p_filter->fmt_out.video.i_lgshift) \
   | ((((uint32_t)i_b) >> p_filter->fmt_out.video.i_rbshift) \
                       << p_filter->fmt_out.video.i_lbshift))
55

56 57 58 59 60 61 62 63
/*****************************************************************************
 * Local and extern prototypes.
 *****************************************************************************/
static int  Activate   ( vlc_object_t * );
static void Deactivate ( vlc_object_t * );

#if defined (MODULE_NAME_IS_i420_rgb)
static void SetGammaTable       ( int *pi_table, double f_gamma );
64 65
static void SetYUV              ( filter_t * );
static void Set8bppPalette      ( filter_t *, uint8_t * );
66 67 68 69 70 71 72
#endif

/*****************************************************************************
 * Module descriptor.
 *****************************************************************************/
vlc_module_begin();
#if defined (MODULE_NAME_IS_i420_rgb)
73
    set_description( N_("I420,IYUV,YV12 to "
74
                       "RGB2,RV15,RV16,RV24,RV32 conversions") );
75
    set_capability( "video filter2", 80 );
76
#elif defined (MODULE_NAME_IS_i420_rgb_mmx)
77
    set_description( N_( "MMX I420,IYUV,YV12 to "
78
                        "RV15,RV16,RV24,RV32 conversions") );
79
    set_capability( "video filter2", 100 );
80
    add_requirement( MMX );
81
#elif defined (MODULE_NAME_IS_i420_rgb_sse2)
82
    set_description( N_( "SSE2 I420,IYUV,YV12 to "
83
                        "RV15,RV16,RV24,RV32 conversions") );
84
    set_capability( "video filter2", 120 );
85
    add_requirement( SSE2 );
86 87 88 89 90 91 92 93 94 95 96
#endif
    set_callbacks( Activate, Deactivate );
vlc_module_end();

/*****************************************************************************
 * Activate: allocate a chroma function
 *****************************************************************************
 * This function allocates and initializes a chroma function
 *****************************************************************************/
static int Activate( vlc_object_t *p_this )
{
97
    filter_t *p_filter = (filter_t *)p_this;
98 99 100 101
#if defined (MODULE_NAME_IS_i420_rgb)
    size_t i_tables_size;
#endif

102 103
    if( p_filter->fmt_out.video.i_width & 1
     || p_filter->fmt_out.video.i_height & 1 )
104
    {
105
        return VLC_EGENERIC;
106 107
    }

108
    switch( p_filter->fmt_in.video.i_chroma )
109 110 111 112
    {
        case VLC_FOURCC('Y','V','1','2'):
        case VLC_FOURCC('I','4','2','0'):
        case VLC_FOURCC('I','Y','U','V'):
113
            switch( p_filter->fmt_out.video.i_chroma )
114 115 116
            {
#if defined (MODULE_NAME_IS_i420_rgb)
                case VLC_FOURCC('R','G','B','2'):
117
                    p_filter->pf_video_filter = I420_RGB8_Filter;
118 119 120 121
                    break;
#endif
                case VLC_FOURCC('R','V','1','5'):
                case VLC_FOURCC('R','V','1','6'):
122
#if ! defined (MODULE_NAME_IS_i420_rgb)
123
                    /* If we don't have support for the bitmasks, bail out */
124 125 126
                    if( ( p_filter->fmt_out.video.i_rmask == 0x7c00
                       && p_filter->fmt_out.video.i_gmask == 0x03e0
                       && p_filter->fmt_out.video.i_bmask == 0x001f ) )
127
                    {
128 129
                        /* R5G5B6 pixel format */
                        msg_Dbg(p_this, "RGB pixel format is R5G5B5");
130
                        p_filter->pf_video_filter = I420_R5G5B5_Filter;
131
                    }
132 133 134
                    else if( ( p_filter->fmt_out.video.i_rmask == 0xf800
                            && p_filter->fmt_out.video.i_gmask == 0x07e0
                            && p_filter->fmt_out.video.i_bmask == 0x001f ) )
135 136 137
                    {
                        /* R5G6B5 pixel format */
                        msg_Dbg(p_this, "RGB pixel format is R5G6B5");
138
                        p_filter->pf_video_filter = I420_R5G6B5_Filter;
139 140
                    }
                    else
141
                        return VLC_EGENERIC;
142 143
#else
                    // generic C chroma converter */
144
                    p_filter->pf_video_filter = I420_RGB16_Filter;
145
#endif
146 147
                    break;

148 149
#if 0
                /* Hmmm, is there only X11 using 32bits per pixel for RV24 ? */
150
                case VLC_FOURCC('R','V','2','4'):
151
#endif
152

153
                case VLC_FOURCC('R','V','3','2'):
154
#if ! defined (MODULE_NAME_IS_i420_rgb)
155
                    /* If we don't have support for the bitmasks, bail out */
156 157 158
                    if( p_filter->fmt_out.video.i_rmask == 0x00ff0000
                     && p_filter->fmt_out.video.i_gmask == 0x0000ff00
                     && p_filter->fmt_out.video.i_bmask == 0x000000ff )
159
                    {
160 161
                        /* A8R8G8B8 pixel format */
                        msg_Dbg(p_this, "RGB pixel format is A8R8G8B8");
162
                        p_filter->pf_video_filter = I420_A8R8G8B8_Filter;
163
                    }
164 165 166
                    else if( p_filter->fmt_out.video.i_rmask == 0xff000000
                          && p_filter->fmt_out.video.i_gmask == 0x00ff0000
                          && p_filter->fmt_out.video.i_bmask == 0x0000ff00 )
167 168 169
                    {
                        /* R8G8B8A8 pixel format */
                        msg_Dbg(p_this, "RGB pixel format is R8G8B8A8");
170
                        p_filter->pf_video_filter = I420_R8G8B8A8_Filter;
171
                    }
172 173 174
                    else if( p_filter->fmt_out.video.i_rmask == 0x0000ff00
                          && p_filter->fmt_out.video.i_gmask == 0x00ff0000
                          && p_filter->fmt_out.video.i_bmask == 0xff000000 )
175 176 177
                    {
                        /* B8G8R8A8 pixel format */
                        msg_Dbg(p_this, "RGB pixel format is B8G8R8A8");
178
                        p_filter->pf_video_filter = I420_B8G8R8A8_Filter;
179
                    }
180 181 182
                    else if( p_filter->fmt_out.video.i_rmask == 0x000000ff
                          && p_filter->fmt_out.video.i_gmask == 0x0000ff00
                          && p_filter->fmt_out.video.i_bmask == 0x00ff0000 )
183 184 185
                    {
                        /* A8B8G8R8 pixel format */
                        msg_Dbg(p_this, "RGB pixel format is A8B8G8R8");
186
                        p_filter->pf_video_filter = I420_A8B8G8R8_Filter;
187
                    }
188
                    else
189
                        return VLC_EGENERIC;
190
#else
191
                    /* generic C chroma converter */
192
                    p_filter->pf_video_filter = I420_RGB32_Filter;
193
#endif
194 195 196
                    break;

                default:
197
                    return VLC_EGENERIC;
198 199 200 201
            }
            break;

        default:
202
            return VLC_EGENERIC;
203
    }
204

205 206
    p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
    if( p_filter->p_sys == NULL )
207
    {
208
        return VLC_EGENERIC;
209 210
    }

211
    switch( p_filter->fmt_out.video.i_chroma )
212 213 214
    {
#if defined (MODULE_NAME_IS_i420_rgb)
        case VLC_FOURCC('R','G','B','2'):
215
            p_filter->p_sys->p_buffer = malloc( VOUT_MAX_WIDTH );
216 217 218 219 220
            break;
#endif

        case VLC_FOURCC('R','V','1','5'):
        case VLC_FOURCC('R','V','1','6'):
221
            p_filter->p_sys->p_buffer = malloc( VOUT_MAX_WIDTH * 2 );
222 223 224 225
            break;

        case VLC_FOURCC('R','V','2','4'):
        case VLC_FOURCC('R','V','3','2'):
226
            p_filter->p_sys->p_buffer = malloc( VOUT_MAX_WIDTH * 4 );
227 228 229
            break;

        default:
230
            p_filter->p_sys->p_buffer = NULL;
231 232 233
            break;
    }

234
    if( p_filter->p_sys->p_buffer == NULL )
235
    {
236 237
        free( p_filter->p_sys );
        return VLC_EGENERIC;
238 239
    }

240 241
    p_filter->p_sys->p_offset = malloc( p_filter->fmt_out.video.i_width
                    * ( ( p_filter->fmt_out.video.i_chroma
242 243
                           == VLC_FOURCC('R','G','B','2') ) ? 2 : 1 )
                    * sizeof( int ) );
244
    if( p_filter->p_sys->p_offset == NULL )
245
    {
246 247 248
        free( p_filter->p_sys->p_buffer );
        free( p_filter->p_sys );
        return VLC_EGENERIC;
249 250 251
    }

#if defined (MODULE_NAME_IS_i420_rgb)
252
    switch( p_filter->fmt_out.video.i_chroma )
253 254
    {
    case VLC_FOURCC('R','G','B','2'):
255
        i_tables_size = sizeof( uint8_t ) * PALETTE_TABLE_SIZE;
256 257 258
        break;
    case VLC_FOURCC('R','V','1','5'):
    case VLC_FOURCC('R','V','1','6'):
259
        i_tables_size = sizeof( uint16_t ) * RGB_TABLE_SIZE;
260 261
        break;
    default: /* RV24, RV32 */
262
        i_tables_size = sizeof( uint32_t ) * RGB_TABLE_SIZE;
263 264 265
        break;
    }

266 267
    p_filter->p_sys->p_base = malloc( i_tables_size );
    if( p_filter->p_sys->p_base == NULL )
268
    {
269 270 271
        free( p_filter->p_sys->p_offset );
        free( p_filter->p_sys->p_buffer );
        free( p_filter->p_sys );
272 273 274
        return -1;
    }

275
    SetYUV( p_filter );
276 277
#endif

278
    return 0;
279 280 281 282 283 284 285 286 287
}

/*****************************************************************************
 * Deactivate: free the chroma function
 *****************************************************************************
 * This function frees the previously allocated chroma function
 *****************************************************************************/
static void Deactivate( vlc_object_t *p_this )
{
288
    filter_t *p_filter = (filter_t *)p_this;
289 290

#if defined (MODULE_NAME_IS_i420_rgb)
291
    free( p_filter->p_sys->p_base );
292
#endif
293 294 295
    free( p_filter->p_sys->p_offset );
    free( p_filter->p_sys->p_buffer );
    free( p_filter->p_sys );
296 297
}

298 299 300 301 302 303 304 305 306 307 308 309 310
#if defined (MODULE_NAME_IS_i420_rgb)
VIDEO_FILTER_WRAPPER( I420_RGB8 )
VIDEO_FILTER_WRAPPER( I420_RGB16 )
VIDEO_FILTER_WRAPPER( I420_RGB32 )
#else
VIDEO_FILTER_WRAPPER( I420_R5G5B5 )
VIDEO_FILTER_WRAPPER( I420_R5G6B5 )
VIDEO_FILTER_WRAPPER( I420_A8R8G8B8 )
VIDEO_FILTER_WRAPPER( I420_R8G8B8A8 )
VIDEO_FILTER_WRAPPER( I420_B8G8R8A8 )
VIDEO_FILTER_WRAPPER( I420_A8B8G8R8 )
#endif

311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
#if defined (MODULE_NAME_IS_i420_rgb)
/*****************************************************************************
 * SetGammaTable: return intensity table transformed by gamma curve.
 *****************************************************************************
 * pi_table is a table of 256 entries from 0 to 255.
 *****************************************************************************/
static void SetGammaTable( int *pi_table, double f_gamma )
{
    int i_y;                                               /* base intensity */

    /* Use exp(gamma) instead of gamma */
    f_gamma = exp( f_gamma );

    /* Build gamma table */
    for( i_y = 0; i_y < 256; i_y++ )
    {
327
        pi_table[ i_y ] = (int)( pow( (double)i_y / 256, f_gamma ) * 256 );
328 329 330 331 332 333
    }
}

/*****************************************************************************
 * SetYUV: compute tables and set function pointers
 *****************************************************************************/
334
static void SetYUV( filter_t *p_filter )
335
{
336 337 338 339
    int          pi_gamma[256];                               /* gamma table */
    volatile int i_index;                                 /* index in tables */
                   /* We use volatile here to work around a strange gcc-3.3.4
                    * optimization bug */
340 341

    /* Build gamma table */
342
    SetGammaTable( pi_gamma, 0 ); //p_filter/*FIXME wasn't used anywhere anyway*/->f_gamma );
343 344 345 346 347 348

    /*
     * Set pointers and build YUV tables
     */

    /* Color: build red, green and blue tables */
349
    switch( p_filter->fmt_out.video.i_chroma )
350 351
    {
    case VLC_FOURCC('R','G','B','2'):
352 353
        p_filter->p_sys->p_rgb8 = (uint8_t *)p_filter->p_sys->p_base;
        Set8bppPalette( p_filter, p_filter->p_sys->p_rgb8 );
354 355 356 357
        break;

    case VLC_FOURCC('R','V','1','5'):
    case VLC_FOURCC('R','V','1','6'):
358
        p_filter->p_sys->p_rgb16 = (uint16_t *)p_filter->p_sys->p_base;
359 360
        for( i_index = 0; i_index < RED_MARGIN; i_index++ )
        {
361 362
            p_filter->p_sys->p_rgb16[RED_OFFSET - RED_MARGIN + i_index] = RGB2PIXEL( p_filter, pi_gamma[0], 0, 0 );
            p_filter->p_sys->p_rgb16[RED_OFFSET + 256 + i_index] =        RGB2PIXEL( p_filter, pi_gamma[255], 0, 0 );
363 364 365
        }
        for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
        {
366 367
            p_filter->p_sys->p_rgb16[GREEN_OFFSET - GREEN_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, pi_gamma[0], 0 );
            p_filter->p_sys->p_rgb16[GREEN_OFFSET + 256 + i_index] =          RGB2PIXEL( p_filter, 0, pi_gamma[255], 0 );
368 369 370
        }
        for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
        {
371 372
            p_filter->p_sys->p_rgb16[BLUE_OFFSET - BLUE_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, 0, pi_gamma[0] );
            p_filter->p_sys->p_rgb16[BLUE_OFFSET + BLUE_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, 0, pi_gamma[255] );
373 374 375
        }
        for( i_index = 0; i_index < 256; i_index++ )
        {
376 377 378
            p_filter->p_sys->p_rgb16[RED_OFFSET + i_index] =   RGB2PIXEL( p_filter, pi_gamma[ i_index ], 0, 0 );
            p_filter->p_sys->p_rgb16[GREEN_OFFSET + i_index] = RGB2PIXEL( p_filter, 0, pi_gamma[ i_index ], 0 );
            p_filter->p_sys->p_rgb16[BLUE_OFFSET + i_index] =  RGB2PIXEL( p_filter, 0, 0, pi_gamma[ i_index ] );
379 380 381 382 383
        }
        break;

    case VLC_FOURCC('R','V','2','4'):
    case VLC_FOURCC('R','V','3','2'):
384
        p_filter->p_sys->p_rgb32 = (uint32_t *)p_filter->p_sys->p_base;
385 386
        for( i_index = 0; i_index < RED_MARGIN; i_index++ )
        {
387 388
            p_filter->p_sys->p_rgb32[RED_OFFSET - RED_MARGIN + i_index] = RGB2PIXEL( p_filter, pi_gamma[0], 0, 0 );
            p_filter->p_sys->p_rgb32[RED_OFFSET + 256 + i_index] =        RGB2PIXEL( p_filter, pi_gamma[255], 0, 0 );
389 390 391
        }
        for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
        {
392 393
            p_filter->p_sys->p_rgb32[GREEN_OFFSET - GREEN_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, pi_gamma[0], 0 );
            p_filter->p_sys->p_rgb32[GREEN_OFFSET + 256 + i_index] =          RGB2PIXEL( p_filter, 0, pi_gamma[255], 0 );
394 395 396
        }
        for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
        {
397 398
            p_filter->p_sys->p_rgb32[BLUE_OFFSET - BLUE_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, 0, pi_gamma[0] );
            p_filter->p_sys->p_rgb32[BLUE_OFFSET + BLUE_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, 0, pi_gamma[255] );
399 400 401
        }
        for( i_index = 0; i_index < 256; i_index++ )
        {
402 403 404
            p_filter->p_sys->p_rgb32[RED_OFFSET + i_index] =   RGB2PIXEL( p_filter, pi_gamma[ i_index ], 0, 0 );
            p_filter->p_sys->p_rgb32[GREEN_OFFSET + i_index] = RGB2PIXEL( p_filter, 0, pi_gamma[ i_index ], 0 );
            p_filter->p_sys->p_rgb32[BLUE_OFFSET + i_index] =  RGB2PIXEL( p_filter, 0, 0, pi_gamma[ i_index ] );
405 406 407 408 409
        }
        break;
    }
}

410
static void Set8bppPalette( filter_t *p_filter, uint8_t *p_rgb8 )
411 412 413 414 415 416
{
    #define CLIP( x ) ( ((x < 0) ? 0 : (x > 255) ? 255 : x) << 8 )

    int y,u,v;
    int r,g,b;
    int i = 0, j = 0;
417 418 419
    uint16_t *p_cmap_r = p_filter->p_sys->p_rgb_r;
    uint16_t *p_cmap_g = p_filter->p_sys->p_rgb_g;
    uint16_t *p_cmap_b = p_filter->p_sys->p_rgb_b;
420

421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
    unsigned char p_lookup[PALETTE_TABLE_SIZE];

    /* This loop calculates the intersection of an YUV box and the RGB cube. */
    for ( y = 0; y <= 256; y += 16, i += 128 - 81 )
    {
        for ( u = 0; u <= 256; u += 32 )
        {
            for ( v = 0; v <= 256; v += 32 )
            {
                r = y + ( (V_RED_COEF*(v-128)) >> SHIFT );
                g = y + ( (U_GREEN_COEF*(u-128)
                         + V_GREEN_COEF*(v-128)) >> SHIFT );
                b = y + ( (U_BLUE_COEF*(u-128)) >> SHIFT );

                if( r >= 0x00 && g >= 0x00 && b >= 0x00
                        && r <= 0xff && g <= 0xff && b <= 0xff )
                {
                    /* This one should never happen unless someone
                     * fscked up my code */
                    if( j == 256 )
                    {
442
                        msg_Err( p_filter, "no colors left in palette" );
443 444 445 446
                        break;
                    }

                    /* Clip the colors */
447 448 449 450 451
                    p_cmap_r[ j ] = CLIP( r );
                    p_cmap_g[ j ] = CLIP( g );
                    p_cmap_b[ j ] = CLIP( b );

#if 0
452 453 454
            printf("+++Alloc RGB cmap %d (%d, %d, %d)\n", j,
               p_cmap_r[ j ] >>8, p_cmap_g[ j ] >>8,
               p_cmap_b[ j ] >>8);
455
#endif
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471

                    /* Allocate color */
                    p_lookup[ i ] = 1;
                    p_rgb8[ i++ ] = j;
                    j++;
                }
                else
                {
                    p_lookup[ i ] = 0;
                    p_rgb8[ i++ ] = 0;
                }
            }
        }
    }

    /* The colors have been allocated, we can set the palette */
472 473
    /* FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
    p_filter->fmt_out.video.pf_setpalette( p_filter, p_cmap_r, p_cmap_g, p_cmap_b );*/
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529

#if 0
    /* There will eventually be a way to know which colors
     * couldn't be allocated and try to find a replacement */
    p_vout->i_white_pixel = 0xff;
    p_vout->i_black_pixel = 0x00;
    p_vout->i_gray_pixel = 0x44;
    p_vout->i_blue_pixel = 0x3b;
#endif

    /* This loop allocates colors that got outside the RGB cube */
    for ( i = 0, y = 0; y <= 256; y += 16, i += 128 - 81 )
    {
        for ( u = 0; u <= 256; u += 32 )
        {
            for ( v = 0; v <= 256; v += 32, i++ )
            {
                int u2, v2, dist, mindist = 100000000;

                if( p_lookup[ i ] || y == 0 )
                {
                    continue;
                }

                /* Heavy. yeah. */
                for( u2 = 0; u2 <= 256; u2 += 32 )
                {
                    for( v2 = 0; v2 <= 256; v2 += 32 )
                    {
                        j = ((y>>4)<<7) + (u2>>5)*9 + (v2>>5);
                        dist = (u-u2)*(u-u2) + (v-v2)*(v-v2);

                        /* Find the nearest color */
                        if( p_lookup[ j ] && dist < mindist )
                        {
                            p_rgb8[ i ] = p_rgb8[ j ];
                            mindist = dist;
                        }

                        j -= 128;

                        /* Find the nearest color */
                        if( p_lookup[ j ] && dist + 128 < mindist )
                        {
                            p_rgb8[ i ] = p_rgb8[ j ];
                            mindist = dist + 128;
                        }
                    }
                }
            }
        }
    }
}

#endif