]> git.sesse.net Git - x264/blob - filters/video/depth.c
aa59d7e9cfb49a6748051f1c535f5359139df9c6
[x264] / filters / video / depth.c
1 /*****************************************************************************
2  * depth.c: bit-depth conversion video filter
3  *****************************************************************************
4  * Copyright (C) 2010-2016 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_NV21 ||
54            csp_mask == X264_CSP_NV16 ||
55            csp_mask == X264_CSP_BGR ||
56            csp_mask == X264_CSP_RGB ||
57            csp_mask == X264_CSP_BGRA;
58 }
59
60 static int csp_num_interleaved( int csp, int plane )
61 {
62     int csp_mask = csp & X264_CSP_MASK;
63     return (csp_mask == X264_CSP_NV12 || csp_mask == X264_CSP_NV21 || csp_mask == X264_CSP_NV16) && plane == 1 ? 2 :
64            csp_mask == X264_CSP_BGR || csp_mask == X264_CSP_RGB ? 3 :
65            csp_mask == X264_CSP_BGRA ? 4 :
66            1;
67 }
68
69 /* The dithering algorithm is based on Sierra-2-4A error diffusion. It has been
70  * written in such a way so that if the source has been upconverted using the
71  * same algorithm as used in scale_image, dithering down to the source bit
72  * depth again is lossless. */
73 #define DITHER_PLANE( pitch ) \
74 static void dither_plane_##pitch( pixel *dst, int dst_stride, uint16_t *src, int src_stride, \
75                                   int width, int height, int16_t *errors ) \
76 { \
77     const int lshift = 16-X264_BIT_DEPTH; \
78     const int rshift = 16-X264_BIT_DEPTH+2; \
79     const int half = 1 << (16-X264_BIT_DEPTH+1); \
80     const int pixel_max = (1 << X264_BIT_DEPTH)-1; \
81     memset( errors, 0, (width+1) * sizeof(int16_t) ); \
82     for( int y = 0; y < height; y++, src += src_stride, dst += dst_stride ) \
83     { \
84         int err = 0; \
85         for( int x = 0; x < width; x++ ) \
86         { \
87             err = err*2 + errors[x] + errors[x+1]; \
88             dst[x*pitch] = x264_clip3( ((src[x*pitch]<<2)+err+half) >> rshift, 0, pixel_max ); \
89             errors[x] = err = src[x*pitch] - (dst[x*pitch] << lshift); \
90         } \
91     } \
92 }
93
94 DITHER_PLANE( 1 )
95 DITHER_PLANE( 2 )
96 DITHER_PLANE( 3 )
97 DITHER_PLANE( 4 )
98
99 static void dither_image( cli_image_t *out, cli_image_t *img, int16_t *error_buf )
100 {
101     int csp_mask = img->csp & X264_CSP_MASK;
102     for( int i = 0; i < img->planes; i++ )
103     {
104         int num_interleaved = csp_num_interleaved( img->csp, i );
105         int height = x264_cli_csps[csp_mask].height[i] * img->height;
106         int width = x264_cli_csps[csp_mask].width[i] * img->width / num_interleaved;
107
108 #define CALL_DITHER_PLANE( pitch, off ) \
109         dither_plane_##pitch( ((pixel*)out->plane[i])+off, out->stride[i]/sizeof(pixel), \
110                 ((uint16_t*)img->plane[i])+off, img->stride[i]/2, width, height, error_buf )
111
112         if( num_interleaved == 4 )
113         {
114             CALL_DITHER_PLANE( 4, 0 );
115             CALL_DITHER_PLANE( 4, 1 );
116             CALL_DITHER_PLANE( 4, 2 );
117             CALL_DITHER_PLANE( 4, 3 ); //we probably can skip this one
118         }
119         else if( num_interleaved == 3 )
120         {
121             CALL_DITHER_PLANE( 3, 0 );
122             CALL_DITHER_PLANE( 3, 1 );
123             CALL_DITHER_PLANE( 3, 2 );
124         }
125         else if( num_interleaved == 2 )
126         {
127             CALL_DITHER_PLANE( 2, 0 );
128             CALL_DITHER_PLANE( 2, 1 );
129         }
130         else //if( num_interleaved == 1 )
131         {
132             CALL_DITHER_PLANE( 1, 0 );
133         }
134     }
135 }
136
137 static void scale_image( cli_image_t *output, cli_image_t *img )
138 {
139     int csp_mask = img->csp & X264_CSP_MASK;
140     const int shift = X264_BIT_DEPTH - 8;
141     for( int i = 0; i < img->planes; i++ )
142     {
143         uint8_t *src = img->plane[i];
144         uint16_t *dst = (uint16_t*)output->plane[i];
145         int height = x264_cli_csps[csp_mask].height[i] * img->height;
146         int width = x264_cli_csps[csp_mask].width[i] * img->width;
147
148         for( int j = 0; j < height; j++ )
149         {
150             for( int k = 0; k < width; k++ )
151                 dst[k] = src[k] << shift;
152
153             src += img->stride[i];
154             dst += output->stride[i]/2;
155         }
156     }
157 }
158
159 static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
160 {
161     depth_hnd_t *h = handle;
162
163     if( h->prev_filter.get_frame( h->prev_hnd, output, frame ) )
164         return -1;
165
166     if( h->bit_depth < 16 && output->img.csp & X264_CSP_HIGH_DEPTH )
167     {
168         dither_image( &h->buffer.img, &output->img, h->error_buf );
169         output->img = h->buffer.img;
170     }
171     else if( h->bit_depth > 8 && !(output->img.csp & X264_CSP_HIGH_DEPTH) )
172     {
173         scale_image( &h->buffer.img, &output->img );
174         output->img = h->buffer.img;
175     }
176     return 0;
177 }
178
179 static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
180 {
181     depth_hnd_t *h = handle;
182     return h->prev_filter.release_frame( h->prev_hnd, pic, frame );
183 }
184
185 static void free_filter( hnd_t handle )
186 {
187     depth_hnd_t *h = handle;
188     h->prev_filter.free( h->prev_hnd );
189     x264_cli_pic_clean( &h->buffer );
190     x264_free( h );
191 }
192
193 static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info,
194                  x264_param_t *param, char *opt_string )
195 {
196     int ret = 0;
197     int change_fmt = (info->csp ^ param->i_csp) & X264_CSP_HIGH_DEPTH;
198     int csp = ~(~info->csp ^ change_fmt);
199     int bit_depth = 8*x264_cli_csp_depth_factor( csp );
200
201     if( opt_string )
202     {
203         static const char *optlist[] = { "bit_depth", NULL };
204         char **opts = x264_split_options( opt_string, optlist );
205
206         if( opts )
207         {
208             char *str_bit_depth = x264_get_option( "bit_depth", opts );
209             bit_depth = x264_otoi( str_bit_depth, -1 );
210
211             ret = bit_depth < 8 || bit_depth > 16;
212             csp = bit_depth > 8 ? csp | X264_CSP_HIGH_DEPTH : csp & ~X264_CSP_HIGH_DEPTH;
213             change_fmt = (info->csp ^ csp) & X264_CSP_HIGH_DEPTH;
214             x264_free_string_array( opts );
215         }
216         else
217             ret = 1;
218     }
219
220     FAIL_IF_ERROR( bit_depth != X264_BIT_DEPTH, "this build supports only bit depth %d\n", X264_BIT_DEPTH )
221     FAIL_IF_ERROR( ret, "unsupported bit depth conversion.\n" )
222
223     /* only add the filter to the chain if it's needed */
224     if( change_fmt || bit_depth != 8 * x264_cli_csp_depth_factor( csp ) )
225     {
226         FAIL_IF_ERROR( !depth_filter_csp_is_supported(csp), "unsupported colorspace.\n" )
227         depth_hnd_t *h = x264_malloc( sizeof(depth_hnd_t) + (info->width+1)*sizeof(int16_t) );
228
229         if( !h )
230             return -1;
231
232         h->error_buf = (int16_t*)(h + 1);
233         h->dst_csp = csp;
234         h->bit_depth = bit_depth;
235         h->prev_hnd = *handle;
236         h->prev_filter = *filter;
237
238         if( x264_cli_pic_alloc( &h->buffer, h->dst_csp, info->width, info->height ) )
239         {
240             x264_free( h );
241             return -1;
242         }
243
244         *handle = h;
245         *filter = depth_filter;
246         info->csp = h->dst_csp;
247     }
248
249     return 0;
250 }
251
252 cli_vid_filter_t depth_filter = { NAME, NULL, init, get_frame, release_frame, free_filter, NULL };