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