/src/libvpx/vp8/common/postproc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2010 The WebM project authors. All Rights Reserved. |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license |
5 | | * that can be found in the LICENSE file in the root of the source |
6 | | * tree. An additional intellectual property rights grant can be found |
7 | | * in the file PATENTS. All contributing project authors may |
8 | | * be found in the AUTHORS file in the root of the source tree. |
9 | | */ |
10 | | |
11 | | #include "vpx_config.h" |
12 | | #include "vpx_dsp_rtcd.h" |
13 | | #include "vp8_rtcd.h" |
14 | | #include "vpx_dsp/postproc.h" |
15 | | #include "vpx_ports/system_state.h" |
16 | | #include "vpx_scale_rtcd.h" |
17 | | #include "vpx_scale/yv12config.h" |
18 | | #include "postproc.h" |
19 | | #include "common.h" |
20 | | #include "vpx_scale/vpx_scale.h" |
21 | | #include "systemdependent.h" |
22 | | |
23 | | #include <limits.h> |
24 | | #include <math.h> |
25 | | #include <stdlib.h> |
26 | | #include <stdio.h> |
27 | | |
28 | | /* clang-format off */ |
29 | | #define RGB_TO_YUV(t) \ |
30 | | (unsigned char)((0.257 * (float)(t >> 16)) + \ |
31 | | (0.504 * (float)(t >> 8 & 0xff)) + \ |
32 | | (0.098 * (float)(t & 0xff)) + 16), \ |
33 | | (unsigned char)(-(0.148 * (float)(t >> 16)) - \ |
34 | | (0.291 * (float)(t >> 8 & 0xff)) + \ |
35 | | (0.439 * (float)(t & 0xff)) + 128), \ |
36 | | (unsigned char)((0.439 * (float)(t >> 16)) - \ |
37 | | (0.368 * (float)(t >> 8 & 0xff)) - \ |
38 | | (0.071 * (float)(t & 0xff)) + 128) |
39 | | /* clang-format on */ |
40 | | |
41 | | extern void vp8_blit_text(const char *msg, unsigned char *address, |
42 | | const int pitch); |
43 | | extern void vp8_blit_line(int x0, int x1, int y0, int y1, unsigned char *image, |
44 | | const int pitch); |
45 | | /*********************************************************************************************************** |
46 | | */ |
47 | | #if CONFIG_POSTPROC |
48 | 0 | static int q2mbl(int x) { |
49 | 0 | if (x < 20) x = 20; |
50 | |
|
51 | 0 | x = 50 + (x - 50) * 10 / 8; |
52 | 0 | return x * x / 3; |
53 | 0 | } |
54 | | |
55 | 0 | static void vp8_de_mblock(YV12_BUFFER_CONFIG *post, int q) { |
56 | 0 | vpx_mbpost_proc_across_ip(post->y_buffer, post->y_stride, post->y_height, |
57 | 0 | post->y_width, q2mbl(q)); |
58 | 0 | vpx_mbpost_proc_down(post->y_buffer, post->y_stride, post->y_height, |
59 | 0 | post->y_width, q2mbl(q)); |
60 | 0 | } |
61 | | |
62 | | void vp8_deblock(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source, |
63 | 0 | YV12_BUFFER_CONFIG *post, int q) { |
64 | 0 | double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065; |
65 | 0 | int ppl = (int)(level + .5); |
66 | |
|
67 | 0 | const MODE_INFO *mode_info_context = cm->mi; |
68 | 0 | int mbr, mbc; |
69 | | |
70 | | /* The pixel thresholds are adjusted according to if or not the macroblock |
71 | | * is a skipped block. */ |
72 | 0 | unsigned char *ylimits = cm->pp_limits_buffer; |
73 | 0 | unsigned char *uvlimits = cm->pp_limits_buffer + 16 * cm->mb_cols; |
74 | |
|
75 | 0 | if (ppl > 0) { |
76 | 0 | for (mbr = 0; mbr < cm->mb_rows; ++mbr) { |
77 | 0 | unsigned char *ylptr = ylimits; |
78 | 0 | unsigned char *uvlptr = uvlimits; |
79 | 0 | for (mbc = 0; mbc < cm->mb_cols; ++mbc) { |
80 | 0 | unsigned char mb_ppl; |
81 | |
|
82 | 0 | if (mode_info_context->mbmi.mb_skip_coeff) { |
83 | 0 | mb_ppl = (unsigned char)ppl >> 1; |
84 | 0 | } else { |
85 | 0 | mb_ppl = (unsigned char)ppl; |
86 | 0 | } |
87 | |
|
88 | 0 | memset(ylptr, mb_ppl, 16); |
89 | 0 | memset(uvlptr, mb_ppl, 8); |
90 | |
|
91 | 0 | ylptr += 16; |
92 | 0 | uvlptr += 8; |
93 | 0 | mode_info_context++; |
94 | 0 | } |
95 | 0 | mode_info_context++; |
96 | |
|
97 | 0 | vpx_post_proc_down_and_across_mb_row( |
98 | 0 | source->y_buffer + 16 * mbr * source->y_stride, |
99 | 0 | post->y_buffer + 16 * mbr * post->y_stride, source->y_stride, |
100 | 0 | post->y_stride, source->y_width, ylimits, 16); |
101 | |
|
102 | 0 | vpx_post_proc_down_and_across_mb_row( |
103 | 0 | source->u_buffer + 8 * mbr * source->uv_stride, |
104 | 0 | post->u_buffer + 8 * mbr * post->uv_stride, source->uv_stride, |
105 | 0 | post->uv_stride, source->uv_width, uvlimits, 8); |
106 | 0 | vpx_post_proc_down_and_across_mb_row( |
107 | 0 | source->v_buffer + 8 * mbr * source->uv_stride, |
108 | 0 | post->v_buffer + 8 * mbr * post->uv_stride, source->uv_stride, |
109 | 0 | post->uv_stride, source->uv_width, uvlimits, 8); |
110 | 0 | } |
111 | 0 | } else { |
112 | 0 | vp8_yv12_copy_frame(source, post); |
113 | 0 | } |
114 | 0 | } |
115 | | |
116 | | void vp8_de_noise(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source, int q, |
117 | 0 | int uvfilter) { |
118 | 0 | int mbr; |
119 | 0 | double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065; |
120 | 0 | int ppl = (int)(level + .5); |
121 | 0 | int mb_rows = cm->mb_rows; |
122 | 0 | int mb_cols = cm->mb_cols; |
123 | 0 | unsigned char *limits = cm->pp_limits_buffer; |
124 | |
|
125 | 0 | memset(limits, (unsigned char)ppl, 16 * mb_cols); |
126 | | |
127 | | /* TODO: The original code don't filter the 2 outer rows and columns. */ |
128 | 0 | for (mbr = 0; mbr < mb_rows; ++mbr) { |
129 | 0 | vpx_post_proc_down_and_across_mb_row( |
130 | 0 | source->y_buffer + 16 * mbr * source->y_stride, |
131 | 0 | source->y_buffer + 16 * mbr * source->y_stride, source->y_stride, |
132 | 0 | source->y_stride, source->y_width, limits, 16); |
133 | 0 | if (uvfilter == 1) { |
134 | 0 | vpx_post_proc_down_and_across_mb_row( |
135 | 0 | source->u_buffer + 8 * mbr * source->uv_stride, |
136 | 0 | source->u_buffer + 8 * mbr * source->uv_stride, source->uv_stride, |
137 | 0 | source->uv_stride, source->uv_width, limits, 8); |
138 | 0 | vpx_post_proc_down_and_across_mb_row( |
139 | 0 | source->v_buffer + 8 * mbr * source->uv_stride, |
140 | 0 | source->v_buffer + 8 * mbr * source->uv_stride, source->uv_stride, |
141 | 0 | source->uv_stride, source->uv_width, limits, 8); |
142 | 0 | } |
143 | 0 | } |
144 | 0 | } |
145 | | #endif // CONFIG_POSTPROC |
146 | | |
147 | | #if CONFIG_POSTPROC |
148 | | int vp8_post_proc_frame(VP8_COMMON *oci, YV12_BUFFER_CONFIG *dest, |
149 | 0 | vp8_ppflags_t *ppflags) { |
150 | 0 | int q = oci->filter_level * 10 / 6; |
151 | 0 | int flags = ppflags->post_proc_flag; |
152 | 0 | int deblock_level = ppflags->deblocking_level; |
153 | 0 | int noise_level = ppflags->noise_level; |
154 | |
|
155 | 0 | if (!oci->frame_to_show) return -1; |
156 | | |
157 | 0 | if (q > 63) q = 63; |
158 | |
|
159 | 0 | if (!flags) { |
160 | 0 | *dest = *oci->frame_to_show; |
161 | | |
162 | | /* handle problem with extending borders */ |
163 | 0 | dest->y_width = oci->Width; |
164 | 0 | dest->y_height = oci->Height; |
165 | 0 | dest->uv_height = dest->y_height / 2; |
166 | 0 | oci->postproc_state.last_base_qindex = oci->base_qindex; |
167 | 0 | oci->postproc_state.last_frame_valid = 1; |
168 | 0 | return 0; |
169 | 0 | } |
170 | 0 | if (flags & VP8D_ADDNOISE) { |
171 | 0 | if (!oci->postproc_state.generated_noise) { |
172 | 0 | oci->postproc_state.generated_noise = vpx_calloc( |
173 | 0 | oci->Width + 256, sizeof(*oci->postproc_state.generated_noise)); |
174 | 0 | if (!oci->postproc_state.generated_noise) return 1; |
175 | 0 | } |
176 | 0 | } |
177 | | |
178 | | /* Allocate post_proc_buffer_int if needed */ |
179 | 0 | if ((flags & VP8D_MFQE) && !oci->post_proc_buffer_int_used) { |
180 | 0 | if ((flags & VP8D_DEBLOCK) || (flags & VP8D_DEMACROBLOCK)) { |
181 | 0 | int width = (oci->Width + 15) & ~15; |
182 | 0 | int height = (oci->Height + 15) & ~15; |
183 | |
|
184 | 0 | if (vp8_yv12_alloc_frame_buffer(&oci->post_proc_buffer_int, width, height, |
185 | 0 | VP8BORDERINPIXELS)) { |
186 | 0 | vpx_internal_error(&oci->error, VPX_CODEC_MEM_ERROR, |
187 | 0 | "Failed to allocate MFQE framebuffer"); |
188 | 0 | } |
189 | |
|
190 | 0 | oci->post_proc_buffer_int_used = 1; |
191 | | |
192 | | /* insure that postproc is set to all 0's so that post proc |
193 | | * doesn't pull random data in from edge |
194 | | */ |
195 | 0 | memset((&oci->post_proc_buffer_int)->buffer_alloc, 128, |
196 | 0 | (&oci->post_proc_buffer)->frame_size); |
197 | 0 | } |
198 | 0 | } |
199 | |
|
200 | 0 | vpx_clear_system_state(); |
201 | |
|
202 | 0 | if ((flags & VP8D_MFQE) && oci->postproc_state.last_frame_valid && |
203 | 0 | oci->current_video_frame > 10 && |
204 | 0 | oci->postproc_state.last_base_qindex < 60 && |
205 | 0 | oci->base_qindex - oci->postproc_state.last_base_qindex >= 20) { |
206 | 0 | vp8_multiframe_quality_enhance(oci); |
207 | 0 | if (((flags & VP8D_DEBLOCK) || (flags & VP8D_DEMACROBLOCK)) && |
208 | 0 | oci->post_proc_buffer_int_used) { |
209 | 0 | vp8_yv12_copy_frame(&oci->post_proc_buffer, &oci->post_proc_buffer_int); |
210 | 0 | if (flags & VP8D_DEMACROBLOCK) { |
211 | 0 | vp8_deblock(oci, &oci->post_proc_buffer_int, &oci->post_proc_buffer, |
212 | 0 | q + (deblock_level - 5) * 10); |
213 | 0 | vp8_de_mblock(&oci->post_proc_buffer, q + (deblock_level - 5) * 10); |
214 | 0 | } else if (flags & VP8D_DEBLOCK) { |
215 | 0 | vp8_deblock(oci, &oci->post_proc_buffer_int, &oci->post_proc_buffer, q); |
216 | 0 | } |
217 | 0 | } |
218 | | /* Move partially towards the base q of the previous frame */ |
219 | 0 | oci->postproc_state.last_base_qindex = |
220 | 0 | (3 * oci->postproc_state.last_base_qindex + oci->base_qindex) >> 2; |
221 | 0 | } else if (flags & VP8D_DEMACROBLOCK) { |
222 | 0 | vp8_deblock(oci, oci->frame_to_show, &oci->post_proc_buffer, |
223 | 0 | q + (deblock_level - 5) * 10); |
224 | 0 | vp8_de_mblock(&oci->post_proc_buffer, q + (deblock_level - 5) * 10); |
225 | |
|
226 | 0 | oci->postproc_state.last_base_qindex = oci->base_qindex; |
227 | 0 | } else if (flags & VP8D_DEBLOCK) { |
228 | 0 | vp8_deblock(oci, oci->frame_to_show, &oci->post_proc_buffer, q); |
229 | 0 | oci->postproc_state.last_base_qindex = oci->base_qindex; |
230 | 0 | } else { |
231 | 0 | vp8_yv12_copy_frame(oci->frame_to_show, &oci->post_proc_buffer); |
232 | 0 | oci->postproc_state.last_base_qindex = oci->base_qindex; |
233 | 0 | } |
234 | 0 | oci->postproc_state.last_frame_valid = 1; |
235 | |
|
236 | 0 | if (flags & VP8D_ADDNOISE) { |
237 | 0 | if (oci->postproc_state.last_q != q || |
238 | 0 | oci->postproc_state.last_noise != noise_level) { |
239 | 0 | double sigma; |
240 | 0 | struct postproc_state *ppstate = &oci->postproc_state; |
241 | 0 | vpx_clear_system_state(); |
242 | 0 | sigma = noise_level + .5 + .6 * q / 63.0; |
243 | 0 | ppstate->clamp = |
244 | 0 | vpx_setup_noise(sigma, ppstate->generated_noise, oci->Width + 256); |
245 | 0 | ppstate->last_q = q; |
246 | 0 | ppstate->last_noise = noise_level; |
247 | 0 | } |
248 | |
|
249 | 0 | vpx_plane_add_noise( |
250 | 0 | oci->post_proc_buffer.y_buffer, oci->postproc_state.generated_noise, |
251 | 0 | oci->postproc_state.clamp, oci->postproc_state.clamp, |
252 | 0 | oci->post_proc_buffer.y_width, oci->post_proc_buffer.y_height, |
253 | 0 | oci->post_proc_buffer.y_stride); |
254 | 0 | } |
255 | |
|
256 | 0 | *dest = oci->post_proc_buffer; |
257 | | |
258 | | /* handle problem with extending borders */ |
259 | 0 | dest->y_width = oci->Width; |
260 | 0 | dest->y_height = oci->Height; |
261 | 0 | dest->uv_height = dest->y_height / 2; |
262 | 0 | return 0; |
263 | 0 | } |
264 | | #endif |