croppadd.c 12 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*****************************************************************************
 * croppadd.c: Crop/Padd image filter
 *****************************************************************************
 * Copyright (C) 2008 the VideoLAN team
 * $Id$
 *
 * Authors: Antoine Cellerier <dionoea @t videolan dot org>
 *
 * 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.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
32
#include <limits.h> /* INT_MAX */

33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
ivoire's avatar
ivoire committed
35
#include <vlc_filter.h>
dionoea's avatar
dionoea committed
36
#include "filter_picture.h"
37
38
39
40
41
42
43
44
45

/****************************************************************************
 * Local prototypes
 ****************************************************************************/
static int  OpenFilter ( vlc_object_t * );
static void CloseFilter( vlc_object_t * );

static picture_t *Filter( filter_t *, picture_t * );

46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#define CROPTOP_TEXT N_( "Pixels to crop from top" )
#define CROPTOP_LONGTEXT N_( \
    "Number of pixels to crop from the top of the image." )
#define CROPBOTTOM_TEXT N_( "Pixels to crop from bottom" )
#define CROPBOTTOM_LONGTEXT N_( \
    "Number of pixels to crop from the bottom of the image." )
#define CROPLEFT_TEXT N_( "Pixels to crop from left" )
#define CROPLEFT_LONGTEXT N_( \
    "Number of pixels to crop from the left of the image." )
#define CROPRIGHT_TEXT N_( "Pixels to crop from right" )
#define CROPRIGHT_LONGTEXT N_( \
    "Number of pixels to crop from the right of the image." )

#define PADDTOP_TEXT N_( "Pixels to padd to top" )
#define PADDTOP_LONGTEXT N_( \
    "Number of pixels to padd to the top of the image after cropping." )
#define PADDBOTTOM_TEXT N_( "Pixels to padd to bottom" )
#define PADDBOTTOM_LONGTEXT N_( \
    "Number of pixels to padd to the bottom of the image after cropping." )
#define PADDLEFT_TEXT N_( "Pixels to padd to left" )
#define PADDLEFT_LONGTEXT N_( \
    "Number of pixels to padd to the left of the image after cropping." )
#define PADDRIGHT_TEXT N_( "Pixels to padd to right" )
#define PADDRIGHT_LONGTEXT N_( \
    "Number of pixels to padd to the right of the image after cropping." )

#define CFG_PREFIX "croppadd-"

74
75
76
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
77
vlc_module_begin ()
Christophe Mutricy's avatar
Christophe Mutricy committed
78
    set_shortname( N_("Cropadd") )
79
80
81
    set_description( N_("Video scaling filter") )
    set_capability( "video filter2", 0 )
    set_callbacks( OpenFilter, CloseFilter )
82

83
    set_category( CAT_VIDEO )
84
    set_subcategory( SUBCAT_VIDEO_VFILTER );
85

86
    set_section( N_("Crop"), NULL )
87
        add_integer_with_range( CFG_PREFIX "croptop", 0, 0, INT_MAX,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
88
                                CROPTOP_TEXT, CROPTOP_LONGTEXT, false )
89
        add_integer_with_range( CFG_PREFIX "cropbottom", 0, 0, INT_MAX,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
90
                                CROPBOTTOM_TEXT, CROPBOTTOM_LONGTEXT, false )
91
        add_integer_with_range( CFG_PREFIX "cropleft", 0, 0, INT_MAX,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
92
                                CROPLEFT_TEXT, CROPLEFT_LONGTEXT, false )
93
        add_integer_with_range( CFG_PREFIX "cropright", 0, 0, INT_MAX,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
94
                                CROPRIGHT_TEXT, CROPRIGHT_LONGTEXT, false )
95

96
    set_section( N_("Padd"), NULL )
97
        add_integer_with_range( CFG_PREFIX "paddtop", 0, 0, INT_MAX,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
98
                                PADDTOP_TEXT, PADDTOP_LONGTEXT, false )
99
        add_integer_with_range( CFG_PREFIX "paddbottom", 0, 0, INT_MAX,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
100
                                PADDBOTTOM_TEXT, PADDBOTTOM_LONGTEXT, false )
101
        add_integer_with_range( CFG_PREFIX "paddleft", 0, 0, INT_MAX,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
102
                                PADDLEFT_TEXT, PADDLEFT_LONGTEXT, false )
103
        add_integer_with_range( CFG_PREFIX "paddright", 0, 0, INT_MAX,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
104
                                PADDRIGHT_TEXT, PADDRIGHT_LONGTEXT, false )
105
vlc_module_end ()
106

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
static const char *const ppsz_filter_options[] = {
    "croptop", "cropbottom", "cropleft", "cropright",
    "paddtop", "paddbottom", "paddleft", "paddright",
    NULL
};

struct filter_sys_t
{
    int i_croptop;
    int i_cropbottom;
    int i_cropleft;
    int i_cropright;
    int i_paddtop;
    int i_paddbottom;
    int i_paddleft;
    int i_paddright;
};

125
126
127
128
129
130
/*****************************************************************************
 * OpenFilter: probe the filter and return score
 *****************************************************************************/
static int OpenFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t*)p_this;
131
132
133
134
135
136
137
    filter_sys_t *p_sys;

    if( !p_filter->b_allow_fmt_out_change )
    {
        msg_Err( p_filter, "Picture format change isn't allowed" );
        return VLC_EGENERIC;
    }
138
139
140

    if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
    {
141
142
143
        msg_Err( p_filter, "Input and output chromas don't match" );
        /* In fact we don't really care about this since we're allowed
         * to change the output format ... FIXME? */
144
145
146
        return VLC_EGENERIC;
    }

147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
    p_filter->p_sys = (filter_sys_t *)malloc( sizeof( filter_sys_t ) );
    if( !p_filter->p_sys ) return VLC_ENOMEM;

    config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
                       p_filter->p_cfg );

    p_sys = p_filter->p_sys;
#define GET_OPTION( name ) \
    p_sys->i_ ## name = var_CreateGetInteger( p_filter, CFG_PREFIX #name ); \
    if( p_sys->i_ ## name & 1 ) \
        msg_Warn( p_filter, "Using even values for `" #name "' is recommended" );
    GET_OPTION( croptop )
    GET_OPTION( cropbottom )
    GET_OPTION( cropleft )
    GET_OPTION( cropright )
    GET_OPTION( paddtop )
    GET_OPTION( paddbottom )
    GET_OPTION( paddleft )
    GET_OPTION( paddright )

    p_filter->fmt_out.video.i_height =
    p_filter->fmt_out.video.i_visible_height =
        p_filter->fmt_in.video.i_visible_height
        - p_sys->i_croptop - p_sys->i_cropbottom
        + p_sys->i_paddtop + p_sys->i_paddbottom;

    p_filter->fmt_out.video.i_width =
    p_filter->fmt_out.video.i_visible_width =
        p_filter->fmt_in.video.i_visible_width
        - p_sys->i_cropleft - p_sys->i_cropright
        + p_sys->i_paddleft + p_sys->i_paddright;

179
180
    p_filter->pf_video_filter = Filter;

181
182
183
184
185
186
187
188
189
190
191
    msg_Dbg( p_filter, "Crop: Top: %d, Bottom: %d, Left: %d, Right: %d",
             p_sys->i_croptop, p_sys->i_cropbottom, p_sys->i_cropleft,
             p_sys->i_cropright );
    msg_Dbg( p_filter, "Padd: Top: %d, Bottom: %d, Left: %d, Right: %d",
             p_sys->i_paddtop, p_sys->i_paddbottom, p_sys->i_paddleft,
             p_sys->i_paddright );
    msg_Dbg( p_filter, "%dx%d -> %dx%d",
             p_filter->fmt_in.video.i_width,
             p_filter->fmt_in.video.i_height,
             p_filter->fmt_out.video.i_width,
             p_filter->fmt_out.video.i_height );
192

193
194
195
    p_filter->fmt_out.video.i_sar_num = p_filter->fmt_in.video.i_sar_num * p_filter->fmt_out.video.i_visible_height;
    p_filter->fmt_out.video.i_sar_den = p_filter->fmt_in.video.i_sar_den * p_filter->fmt_out.video.i_visible_width;

196
197
198
199
200
201
202
203
    return VLC_SUCCESS;
}

/*****************************************************************************
 * CloseFilter: clean up the filter
 *****************************************************************************/
static void CloseFilter( vlc_object_t *p_this )
{
204
205
    filter_t *p_filter = (filter_t *)p_this;
    free( p_filter->p_sys );
206
207
208
209
210
211
212
}

/****************************************************************************
 * Filter: the whole thing
 ****************************************************************************/
static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
{
213
    filter_sys_t *p_sys = p_filter->p_sys;
214
215
216
217
218
219
220
221
222
223
    picture_t *p_outpic;
    int i_plane;
    int i_width, i_height, i_xcrop, i_ycrop,
        i_outwidth, i_outheight, i_xpadd, i_ypadd;

    const int p_padd_color[] = { 0x00, 0x80, 0x80, 0xff };

    if( !p_pic ) return NULL;

    /* Request output picture */
Laurent Aimar's avatar
Laurent Aimar committed
224
    p_outpic = filter_NewPicture( p_filter );
225
226
    if( !p_outpic )
    {
227
        picture_Release( p_pic );
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
        return NULL;
    }

    for( i_plane = 0; i_plane < p_pic->i_planes; i_plane++ )
    /* p_pic and p_outpic have the same chroma/number of planes but that's
     * about it. */
    {
        plane_t *p_plane = p_pic->p+i_plane;
        plane_t *p_outplane = p_outpic->p+i_plane;
        uint8_t *p_in = p_plane->p_pixels;
        uint8_t *p_out = p_outplane->p_pixels;
        int i_pixel_pitch = p_plane->i_pixel_pitch;
        int i_padd_color = i_plane > 3 ? p_padd_color[0]
                                       : p_padd_color[i_plane];

        /* These assignments assume that the first plane always has
         * a width and height equal to the picture's */
245
246
        i_width =     ( ( p_filter->fmt_in.video.i_visible_width
                          - p_sys->i_cropleft - p_sys->i_cropright )
247
248
                        * p_plane->i_visible_pitch )
                      / p_pic->p->i_visible_pitch;
249
250
        i_height =    ( ( p_filter->fmt_in.video.i_visible_height
                          - p_sys->i_croptop - p_sys->i_cropbottom )
251
252
                        * p_plane->i_visible_lines )
                      / p_pic->p->i_visible_lines;
253
        i_xcrop =     ( p_sys->i_cropleft * p_plane->i_visible_pitch)
254
                      / p_pic->p->i_visible_pitch;
255
        i_ycrop =     ( p_sys->i_croptop * p_plane->i_visible_lines)
256
257
258
259
260
261
262
                      / p_pic->p->i_visible_lines;
        i_outwidth =  ( p_filter->fmt_out.video.i_visible_width
                        * p_outplane->i_visible_pitch )
                      / p_outpic->p->i_visible_pitch;
        i_outheight = ( p_filter->fmt_out.video.i_visible_height
                        * p_outplane->i_visible_lines )
                      / p_outpic->p->i_visible_lines;
263
        i_xpadd =     ( p_sys->i_paddleft * p_outplane->i_visible_pitch )
264
                      / p_outpic->p->i_visible_pitch;
265
        i_ypadd =     ( p_sys->i_paddtop * p_outplane->i_visible_lines )
266
267
268
269
270
271
                       / p_outpic->p->i_visible_lines;

        /* Crop the top */
        p_in += i_ycrop * p_plane->i_pitch;

        /* Padd on the top */
Rafaël Carré's avatar
Rafaël Carré committed
272
        memset( p_out, i_padd_color, i_ypadd * p_outplane->i_pitch );
273
274
275
276
277
278
279
280
281
282
283
284
        p_out += i_ypadd * p_outplane->i_pitch;

        int i_line;
        for( i_line = 0; i_line < i_height; i_line++ )
        {
            uint8_t *p_in_next = p_in + p_plane->i_pitch;
            uint8_t *p_out_next = p_out + p_outplane->i_pitch;

            /* Crop on the left */
            p_in += i_xcrop * i_pixel_pitch;

            /* Padd on the left */
Rafaël Carré's avatar
Rafaël Carré committed
285
            memset( p_out, i_padd_color, i_xpadd * i_pixel_pitch );
286
287
288
            p_out += i_xpadd * i_pixel_pitch;

            /* Copy the image and crop on the right */
Rafaël Carré's avatar
Rafaël Carré committed
289
            memcpy( p_out, p_in, i_width * i_pixel_pitch );
290
291
292
293
            p_out += i_width * i_pixel_pitch;
            p_in += i_width * i_pixel_pitch;

            /* Padd on the right */
Rafaël Carré's avatar
Rafaël Carré committed
294
            memset( p_out, i_padd_color,
295
                        ( i_outwidth - i_xpadd - i_width ) * i_pixel_pitch );
296
297
298
299
300
301
302

            /* Got to begining of the next line */
            p_in = p_in_next;
            p_out = p_out_next;
        }

        /* Padd on the bottom */
Rafaël Carré's avatar
Rafaël Carré committed
303
        memset( p_out, i_padd_color,
304
305
306
                 ( i_outheight - i_ypadd - i_height ) * p_outplane->i_pitch );
    }

dionoea's avatar
dionoea committed
307
    return CopyInfoAndRelease( p_outpic, p_pic );
308
}