]> git.sesse.net Git - x264/blob - filters/video/depth.c
Bump dates to 2012
[x264] / filters / video / depth.c
1 /*****************************************************************************
2  * depth.c: bit-depth conversion video filter
3  *****************************************************************************
4  * Copyright (C) 2010-2012 x264 project
5  *
6  * Authors: Oskar Arvidsson <oskar@irock.se>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
21  *
22  * This program is also available under a commercial proprietary license.
23  * For more information, contact us at licensing@x264.com.
24  *****************************************************************************/
25
26 #include "video.h"
27 #define NAME "depth"
28 #define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
29
30 cli_vid_filter_t depth_filter;
31
32 typedef struct
33 {
34     hnd_t prev_hnd;
35     cli_vid_filter_t prev_filter;
36
37     int bit_depth;
38     int dst_csp;
39     cli_pic_t buffer;
40     int16_t *error_buf;
41 } depth_hnd_t;
42
43 static int depth_filter_csp_is_supported( int csp )
44 {
45     int csp_mask = csp & X264_CSP_MASK;
46     return csp_mask == X264_CSP_I420 ||
47            csp_mask == X264_CSP_I422 ||
48            csp_mask == X264_CSP_I444 ||
49            csp_mask == X264_CSP_YV12 ||
50            csp_mask == X264_CSP_YV16 ||
51            csp_mask == X264_CSP_YV24 ||
52            csp_mask == X264_CSP_NV12 ||
53            csp_mask == X264_CSP_NV16;
54 }
55
56 static int csp_num_interleaved( int csp, int plane )
57 {
58     int csp_mask = csp & X264_CSP_MASK;
59     return ( (csp_mask == X264_CSP_NV12 || csp_mask == X264_CSP_NV16) && plane == 1 ) ? 2 : 1;
60 }
61
62 /* The dithering algorithm is based on Sierra-2-4A error diffusion. It has been
63  * written in such a way so that if the source has been upconverted using the
64  * same algorithm as used in scale_image, dithering down to the source bit
65  * depth again is lossless. */
66 #define DITHER_PLANE( pitch ) \
67 static void dither_plane_##pitch( pixel *dst, int dst_stride, uint16_t *src, int src_stride, \
68                                         int width, int height, int16_t *errors ) \
69 { \
70     const int lshift = 16-BIT_DEPTH; \
71     const int rshift = 2*BIT_DEPTH-16; \
72     const int pixel_max = (1 << BIT_DEPTH)-1; \
73     const int half = 1 << (16-BIT_DEPTH); \
74     memset( errors, 0, (width+1) * sizeof(int16_t) ); \
75     for( int y = 0; y < height; y++, src += src_stride, dst += dst_stride ) \
76     { \
77         int err = 0; \
78         for( int x = 0; x < width; x++ ) \
79         { \
80             err = err*2 + errors[x] + errors[x+1]; \
81             dst[x*pitch] = x264_clip3( (((src[x*pitch]+half)<<2)+err)*pixel_max >> 18, 0, pixel_max ); \
82             errors[x] = err = src[x*pitch] - (dst[x*pitch] << lshift) - (dst[x*pitch] >> rshift); \
83         } \
84     } \
85 }
86
87 DITHER_PLANE( 1 )
88 DITHER_PLANE( 2 )
89
90 static void dither_image( cli_image_t *out, cli_image_t *img, int16_t *error_buf )
91 {
92     int csp_mask = img->csp & X264_CSP_MASK;
93     for( int i = 0; i < img->planes; i++ )
94     {
95         int num_interleaved = csp_num_interleaved( img->csp, i );
96         int height = x264_cli_csps[csp_mask].height[i] * img->height;
97         int width = x264_cli_csps[csp_mask].width[i] * img->width / num_interleaved;
98
99 #define CALL_DITHER_PLANE( pitch, off ) \
100         dither_plane_##pitch( ((pixel*)out->plane[i])+off, out->stride[i]/sizeof(pixel), \
101                 ((uint16_t*)img->plane[i])+off, img->stride[i]/2, width, height, error_buf )
102
103         if( num_interleaved == 1 )
104         {
105             CALL_DITHER_PLANE( 1, 0 );
106         }
107         else
108         {
109             CALL_DITHER_PLANE( 2, 0 );
110             CALL_DITHER_PLANE( 2, 1 );
111         }
112     }
113 }
114
115 static void scale_image( cli_image_t *output, cli_image_t *img )
116 {
117     /* this function mimics how swscale does upconversion. 8-bit is converted
118      * to 16-bit through left shifting the orginal value with 8 and then adding
119      * the original value to that. This effectively keeps the full color range
120      * while also being fast. for n-bit we basically do the same thing, but we
121      * discard the lower 16-n bits. */
122     int csp_mask = img->csp & X264_CSP_MASK;
123     const int shift = 16-BIT_DEPTH;
124     for( int i = 0; i < img->planes; i++ )
125     {
126         uint8_t *src = img->plane[i];
127         uint16_t *dst = (uint16_t*)output->plane[i];
128         int height = x264_cli_csps[csp_mask].height[i] * img->height;
129         int width = x264_cli_csps[csp_mask].width[i] * img->width;
130
131         for( int j = 0; j < height; j++ )
132         {
133             for( int k = 0; k < width; k++ )
134                 dst[k] = ((src[k] << 8) + src[k]) >> shift;
135
136             src += img->stride[i];
137             dst += output->stride[i]/2;
138         }
139     }
140 }
141
142 static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
143 {
144     depth_hnd_t *h = handle;
145
146     if( h->prev_filter.get_frame( h->prev_hnd, output, frame ) )
147         return -1;
148
149     if( h->bit_depth < 16 && output->img.csp & X264_CSP_HIGH_DEPTH )
150     {
151         dither_image( &h->buffer.img, &output->img, h->error_buf );
152         output->img = h->buffer.img;
153     }
154     else if( h->bit_depth > 8 && !(output->img.csp & X264_CSP_HIGH_DEPTH) )
155     {
156         scale_image( &h->buffer.img, &output->img );
157         output->img = h->buffer.img;
158     }
159     return 0;
160 }
161
162 static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
163 {
164     depth_hnd_t *h = handle;
165     return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
166 }
167
168 static void free_filter( hnd_t handle )
169 {
170     depth_hnd_t *h = handle;
171     h->prev_filter.free( h->prev_hnd );
172     x264_cli_pic_clean( &h->buffer );
173     x264_free( h );
174 }
175
176 static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info,
177                  x264_param_t *param, char *opt_string )
178 {
179     int ret = 0;
180     int change_fmt = (info->csp ^ param->i_csp) & X264_CSP_HIGH_DEPTH;
181     int csp = ~(~info->csp ^ change_fmt);
182     int bit_depth = 8*x264_cli_csp_depth_factor( csp );
183
184     if( opt_string )
185     {
186         static const char *optlist[] = { "bit_depth", NULL };
187         char **opts = x264_split_options( opt_string, optlist );
188
189         if( opts )
190         {
191             char *str_bit_depth = x264_get_option( "bit_depth", opts );
192             bit_depth = x264_otoi( str_bit_depth, -1 );
193
194             ret = bit_depth < 8 || bit_depth > 16;
195             csp = bit_depth > 8 ? csp | X264_CSP_HIGH_DEPTH : csp & ~X264_CSP_HIGH_DEPTH;
196             change_fmt = (info->csp ^ csp) & X264_CSP_HIGH_DEPTH;
197             x264_free_string_array( opts );
198         }
199         else
200             ret = 1;
201     }
202
203     FAIL_IF_ERROR( bit_depth != BIT_DEPTH, "this build supports only bit depth %d\n", BIT_DEPTH )
204     FAIL_IF_ERROR( ret, "unsupported bit depth conversion.\n" )
205
206     /* only add the filter to the chain if it's needed */
207     if( change_fmt || bit_depth != 8 * x264_cli_csp_depth_factor( csp ) )
208     {
209         FAIL_IF_ERROR( !depth_filter_csp_is_supported(csp), "unsupported colorspace.\n" )
210         depth_hnd_t *h = x264_malloc( sizeof(depth_hnd_t) + (info->width+1)*sizeof(int16_t) );
211
212         if( !h )
213             return -1;
214
215         h->error_buf = (int16_t*)(h + 1);
216         h->dst_csp = csp;
217         h->bit_depth = bit_depth;
218         h->prev_hnd = *handle;
219         h->prev_filter = *filter;
220
221         if( x264_cli_pic_alloc( &h->buffer, h->dst_csp, info->width, info->height ) )
222         {
223             x264_free( h );
224             return -1;
225         }
226
227         *handle = h;
228         *filter = depth_filter;
229         info->csp = h->dst_csp;
230     }
231
232     return 0;
233 }
234
235 cli_vid_filter_t depth_filter = { NAME, NULL, init, get_frame, release_frame, free_filter, NULL };