dfbc223483f825989eea248c49ef10bd2932938f
[ffmpeg.git] / libavfilter / vf_crop.c
1 /*
2  * copyright (c) 2007 Bobby Bingham
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 /**
22  * @file
23  * video crop filter
24  */
25
26 #include "avfilter.h"
27 #include "libavcore/imgutils.h"
28
29 typedef struct {
30     int  x;             ///< x offset of the non-cropped area with respect to the input area
31     int  y;             ///< y offset of the non-cropped area with respect to the input area
32     int  w;             ///< width of the cropped area
33     int  h;             ///< height of the cropped area
34
35     int max_step[4];    ///< max pixel step for each plane, expressed as a number of bytes
36     int hsub, vsub;     ///< chroma subsampling
37 } CropContext;
38
39 static int query_formats(AVFilterContext *ctx)
40 {
41     static const enum PixelFormat pix_fmts[] = {
42         PIX_FMT_RGB48BE,      PIX_FMT_RGB48LE,
43         PIX_FMT_ARGB,         PIX_FMT_RGBA,
44         PIX_FMT_ABGR,         PIX_FMT_BGRA,
45         PIX_FMT_RGB24,        PIX_FMT_BGR24,
46         PIX_FMT_RGB565BE,     PIX_FMT_RGB565LE,
47         PIX_FMT_RGB555BE,     PIX_FMT_RGB555LE,
48         PIX_FMT_BGR565BE,     PIX_FMT_BGR565LE,
49         PIX_FMT_BGR555BE,     PIX_FMT_BGR555LE,
50         PIX_FMT_GRAY16BE,     PIX_FMT_GRAY16LE,
51         PIX_FMT_YUV420P16LE,  PIX_FMT_YUV420P16BE,
52         PIX_FMT_YUV422P16LE,  PIX_FMT_YUV422P16BE,
53         PIX_FMT_YUV444P16LE,  PIX_FMT_YUV444P16BE,
54         PIX_FMT_YUV444P,      PIX_FMT_YUV422P,
55         PIX_FMT_YUV420P,      PIX_FMT_YUV411P,
56         PIX_FMT_YUV410P,      PIX_FMT_YUV440P,
57         PIX_FMT_YUVJ444P,     PIX_FMT_YUVJ422P,
58         PIX_FMT_YUVJ420P,     PIX_FMT_YUVJ440P,
59         PIX_FMT_YUVA420P,
60         PIX_FMT_RGB8,         PIX_FMT_BGR8,
61         PIX_FMT_RGB4_BYTE,    PIX_FMT_BGR4_BYTE,
62         PIX_FMT_PAL8,         PIX_FMT_GRAY8,
63         PIX_FMT_NONE
64     };
65
66     avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
67
68     return 0;
69 }
70
71 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
72 {
73     CropContext *crop = ctx->priv;
74
75     if (args)
76         sscanf(args, "%d:%d:%d:%d", &crop->w, &crop->h, &crop->x, &crop->y);
77
78     return 0;
79 }
80
81 static int config_input(AVFilterLink *link)
82 {
83     AVFilterContext *ctx = link->dst;
84     CropContext *crop = ctx->priv;
85     const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[link->format];
86
87     av_image_fill_max_pixsteps(crop->max_step, NULL, pix_desc);
88     crop->hsub = av_pix_fmt_descriptors[link->format].log2_chroma_w;
89     crop->vsub = av_pix_fmt_descriptors[link->format].log2_chroma_h;
90
91     if (crop->w == 0)
92         crop->w = link->w - crop->x;
93     if (crop->h == 0)
94         crop->h = link->h - crop->y;
95
96     crop->x &= ~((1 << crop->hsub) - 1);
97     crop->y &= ~((1 << crop->vsub) - 1);
98
99     av_log(link->dst, AV_LOG_INFO, "w:%d h:%d x:%d y:%d\n",
100            crop->w, crop->h, crop->x, crop->y);
101
102     if (crop->x <  0 || crop->y <  0                    ||
103         crop->w <= 0 || crop->h <= 0                    ||
104         (unsigned)crop->x + (unsigned)crop->w > link->w ||
105         (unsigned)crop->y + (unsigned)crop->h > link->h) {
106         av_log(ctx, AV_LOG_ERROR,
107                "Output area %d:%d:%d:%d not within the input area 0:0:%d:%d or zero-sized\n",
108                crop->x, crop->y, crop->w, crop->h, link->w, link->h);
109         return AVERROR(EINVAL);
110     }
111
112     return 0;
113 }
114
115 static int config_output(AVFilterLink *link)
116 {
117     CropContext *crop = link->src->priv;
118
119     link->w = crop->w;
120     link->h = crop->h;
121
122     return 0;
123 }
124
125 static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
126 {
127     CropContext *crop = link->dst->priv;
128     AVFilterBufferRef *ref2 = avfilter_ref_buffer(picref, ~0);
129     int i;
130
131     picref->video->w = crop->w;
132     picref->video->h = crop->h;
133
134     ref2->data[0] += crop->y * ref2->linesize[0];
135     ref2->data[0] += crop->x * crop->max_step[0];
136
137     if (!(av_pix_fmt_descriptors[link->format].flags & PIX_FMT_PAL)) {
138         for (i = 1; i < 3; i ++) {
139             if (ref2->data[i]) {
140                 ref2->data[i] += (crop->y >> crop->vsub) * ref2->linesize[i];
141                 ref2->data[i] += (crop->x * crop->max_step[i]) >> crop->hsub;
142             }
143         }
144     }
145
146     /* alpha plane */
147     if (ref2->data[3]) {
148         ref2->data[3] += crop->y * ref2->linesize[3];
149         ref2->data[3] += crop->x * crop->max_step[3];
150     }
151
152     avfilter_start_frame(link->dst->outputs[0], ref2);
153 }
154
155 static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
156 {
157     AVFilterContext *ctx = link->dst;
158     CropContext *crop = ctx->priv;
159
160     if (y >= crop->y + crop->h || y + h <= crop->y)
161         return;
162
163     if (y < crop->y) {
164         h -= crop->y - y;
165         y  = crop->y;
166     }
167     if (y + h > crop->y + crop->h)
168         h = crop->y + crop->h - y;
169
170     avfilter_draw_slice(ctx->outputs[0], y - crop->y, h, slice_dir);
171 }
172
173 AVFilter avfilter_vf_crop = {
174     .name      = "crop",
175     .description = NULL_IF_CONFIG_SMALL("Crop the input video to width:height:x:y."),
176
177     .priv_size = sizeof(CropContext),
178
179     .query_formats = query_formats,
180     .init          = init,
181
182     .inputs    = (AVFilterPad[]) {{ .name             = "default",
183                                     .type             = AVMEDIA_TYPE_VIDEO,
184                                     .start_frame      = start_frame,
185                                     .draw_slice       = draw_slice,
186                                     .get_video_buffer = avfilter_null_get_video_buffer,
187                                     .config_props     = config_input, },
188                                   { .name = NULL}},
189     .outputs   = (AVFilterPad[]) {{ .name             = "default",
190                                     .type             = AVMEDIA_TYPE_VIDEO,
191                                     .config_props     = config_output, },
192                                   { .name = NULL}},
193 };