Line data Source code
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 : }
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 : post->y_width, q2mbl(q));
58 0 : vpx_mbpost_proc_down(post->y_buffer, post->y_stride, post->y_height,
59 : post->y_width, q2mbl(q));
60 0 : }
61 :
62 0 : void vp8_deblock(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source,
63 : YV12_BUFFER_CONFIG *post, int q, int low_var_thresh,
64 : int flag) {
65 0 : double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065;
66 0 : int ppl = (int)(level + .5);
67 :
68 0 : const MODE_INFO *mode_info_context = cm->show_frame_mi;
69 : int mbr, mbc;
70 :
71 : /* The pixel thresholds are adjusted according to if or not the macroblock
72 : * is a skipped block. */
73 0 : unsigned char *ylimits = cm->pp_limits_buffer;
74 0 : unsigned char *uvlimits = cm->pp_limits_buffer + 16 * cm->mb_cols;
75 : (void)low_var_thresh;
76 : (void)flag;
77 :
78 0 : if (ppl > 0) {
79 0 : for (mbr = 0; mbr < cm->mb_rows; ++mbr) {
80 0 : unsigned char *ylptr = ylimits;
81 0 : unsigned char *uvlptr = uvlimits;
82 0 : for (mbc = 0; mbc < cm->mb_cols; ++mbc) {
83 : unsigned char mb_ppl;
84 :
85 0 : if (mode_info_context->mbmi.mb_skip_coeff) {
86 0 : mb_ppl = (unsigned char)ppl >> 1;
87 : } else {
88 0 : mb_ppl = (unsigned char)ppl;
89 : }
90 :
91 0 : memset(ylptr, mb_ppl, 16);
92 0 : memset(uvlptr, mb_ppl, 8);
93 :
94 0 : ylptr += 16;
95 0 : uvlptr += 8;
96 0 : mode_info_context++;
97 : }
98 0 : mode_info_context++;
99 :
100 0 : vpx_post_proc_down_and_across_mb_row(
101 0 : source->y_buffer + 16 * mbr * source->y_stride,
102 0 : post->y_buffer + 16 * mbr * post->y_stride, source->y_stride,
103 : post->y_stride, source->y_width, ylimits, 16);
104 :
105 0 : vpx_post_proc_down_and_across_mb_row(
106 0 : source->u_buffer + 8 * mbr * source->uv_stride,
107 0 : post->u_buffer + 8 * mbr * post->uv_stride, source->uv_stride,
108 : post->uv_stride, source->uv_width, uvlimits, 8);
109 0 : vpx_post_proc_down_and_across_mb_row(
110 0 : source->v_buffer + 8 * mbr * source->uv_stride,
111 0 : post->v_buffer + 8 * mbr * post->uv_stride, source->uv_stride,
112 : post->uv_stride, source->uv_width, uvlimits, 8);
113 : }
114 : } else {
115 0 : vp8_yv12_copy_frame(source, post);
116 : }
117 0 : }
118 :
119 0 : void vp8_de_noise(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source,
120 : YV12_BUFFER_CONFIG *post, int q, int low_var_thresh, int flag,
121 : int uvfilter) {
122 : int mbr;
123 0 : double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065;
124 0 : int ppl = (int)(level + .5);
125 0 : int mb_rows = cm->mb_rows;
126 0 : int mb_cols = cm->mb_cols;
127 0 : unsigned char *limits = cm->pp_limits_buffer;
128 : (void)post;
129 : (void)low_var_thresh;
130 : (void)flag;
131 :
132 0 : memset(limits, (unsigned char)ppl, 16 * mb_cols);
133 :
134 : /* TODO: The original code don't filter the 2 outer rows and columns. */
135 0 : for (mbr = 0; mbr < mb_rows; ++mbr) {
136 0 : vpx_post_proc_down_and_across_mb_row(
137 0 : source->y_buffer + 16 * mbr * source->y_stride,
138 0 : source->y_buffer + 16 * mbr * source->y_stride, source->y_stride,
139 : source->y_stride, source->y_width, limits, 16);
140 0 : if (uvfilter == 1) {
141 0 : vpx_post_proc_down_and_across_mb_row(
142 0 : source->u_buffer + 8 * mbr * source->uv_stride,
143 0 : source->u_buffer + 8 * mbr * source->uv_stride, source->uv_stride,
144 : source->uv_stride, source->uv_width, limits, 8);
145 0 : vpx_post_proc_down_and_across_mb_row(
146 0 : source->v_buffer + 8 * mbr * source->uv_stride,
147 0 : source->v_buffer + 8 * mbr * source->uv_stride, source->uv_stride,
148 : source->uv_stride, source->uv_width, limits, 8);
149 : }
150 : }
151 0 : }
152 : #endif // CONFIG_POSTPROC
153 :
154 : /* Blend the macro block with a solid colored square. Leave the
155 : * edges unblended to give distinction to macro blocks in areas
156 : * filled with the same color block.
157 : */
158 0 : void vp8_blend_mb_inner_c(unsigned char *y, unsigned char *u, unsigned char *v,
159 : int y_1, int u_1, int v_1, int alpha, int stride) {
160 : int i, j;
161 0 : int y1_const = y_1 * ((1 << 16) - alpha);
162 0 : int u1_const = u_1 * ((1 << 16) - alpha);
163 0 : int v1_const = v_1 * ((1 << 16) - alpha);
164 :
165 0 : y += 2 * stride + 2;
166 0 : for (i = 0; i < 12; ++i) {
167 0 : for (j = 0; j < 12; ++j) {
168 0 : y[j] = (y[j] * alpha + y1_const) >> 16;
169 : }
170 0 : y += stride;
171 : }
172 :
173 0 : stride >>= 1;
174 :
175 0 : u += stride + 1;
176 0 : v += stride + 1;
177 :
178 0 : for (i = 0; i < 6; ++i) {
179 0 : for (j = 0; j < 6; ++j) {
180 0 : u[j] = (u[j] * alpha + u1_const) >> 16;
181 0 : v[j] = (v[j] * alpha + v1_const) >> 16;
182 : }
183 0 : u += stride;
184 0 : v += stride;
185 : }
186 0 : }
187 :
188 : /* Blend only the edge of the macro block. Leave center
189 : * unblended to allow for other visualizations to be layered.
190 : */
191 0 : void vp8_blend_mb_outer_c(unsigned char *y, unsigned char *u, unsigned char *v,
192 : int y_1, int u_1, int v_1, int alpha, int stride) {
193 : int i, j;
194 0 : int y1_const = y_1 * ((1 << 16) - alpha);
195 0 : int u1_const = u_1 * ((1 << 16) - alpha);
196 0 : int v1_const = v_1 * ((1 << 16) - alpha);
197 :
198 0 : for (i = 0; i < 2; ++i) {
199 0 : for (j = 0; j < 16; ++j) {
200 0 : y[j] = (y[j] * alpha + y1_const) >> 16;
201 : }
202 0 : y += stride;
203 : }
204 :
205 0 : for (i = 0; i < 12; ++i) {
206 0 : y[0] = (y[0] * alpha + y1_const) >> 16;
207 0 : y[1] = (y[1] * alpha + y1_const) >> 16;
208 0 : y[14] = (y[14] * alpha + y1_const) >> 16;
209 0 : y[15] = (y[15] * alpha + y1_const) >> 16;
210 0 : y += stride;
211 : }
212 :
213 0 : for (i = 0; i < 2; ++i) {
214 0 : for (j = 0; j < 16; ++j) {
215 0 : y[j] = (y[j] * alpha + y1_const) >> 16;
216 : }
217 0 : y += stride;
218 : }
219 :
220 0 : stride >>= 1;
221 :
222 0 : for (j = 0; j < 8; ++j) {
223 0 : u[j] = (u[j] * alpha + u1_const) >> 16;
224 0 : v[j] = (v[j] * alpha + v1_const) >> 16;
225 : }
226 0 : u += stride;
227 0 : v += stride;
228 :
229 0 : for (i = 0; i < 6; ++i) {
230 0 : u[0] = (u[0] * alpha + u1_const) >> 16;
231 0 : v[0] = (v[0] * alpha + v1_const) >> 16;
232 :
233 0 : u[7] = (u[7] * alpha + u1_const) >> 16;
234 0 : v[7] = (v[7] * alpha + v1_const) >> 16;
235 :
236 0 : u += stride;
237 0 : v += stride;
238 : }
239 :
240 0 : for (j = 0; j < 8; ++j) {
241 0 : u[j] = (u[j] * alpha + u1_const) >> 16;
242 0 : v[j] = (v[j] * alpha + v1_const) >> 16;
243 : }
244 0 : }
245 :
246 0 : void vp8_blend_b_c(unsigned char *y, unsigned char *u, unsigned char *v,
247 : int y_1, int u_1, int v_1, int alpha, int stride) {
248 : int i, j;
249 0 : int y1_const = y_1 * ((1 << 16) - alpha);
250 0 : int u1_const = u_1 * ((1 << 16) - alpha);
251 0 : int v1_const = v_1 * ((1 << 16) - alpha);
252 :
253 0 : for (i = 0; i < 4; ++i) {
254 0 : for (j = 0; j < 4; ++j) {
255 0 : y[j] = (y[j] * alpha + y1_const) >> 16;
256 : }
257 0 : y += stride;
258 : }
259 :
260 0 : stride >>= 1;
261 :
262 0 : for (i = 0; i < 2; ++i) {
263 0 : for (j = 0; j < 2; ++j) {
264 0 : u[j] = (u[j] * alpha + u1_const) >> 16;
265 0 : v[j] = (v[j] * alpha + v1_const) >> 16;
266 : }
267 0 : u += stride;
268 0 : v += stride;
269 : }
270 0 : }
271 :
272 : #if CONFIG_POSTPROC
273 0 : int vp8_post_proc_frame(VP8_COMMON *oci, YV12_BUFFER_CONFIG *dest,
274 : vp8_ppflags_t *ppflags) {
275 0 : int q = oci->filter_level * 10 / 6;
276 0 : int flags = ppflags->post_proc_flag;
277 0 : int deblock_level = ppflags->deblocking_level;
278 0 : int noise_level = ppflags->noise_level;
279 :
280 0 : if (!oci->frame_to_show) return -1;
281 :
282 0 : if (q > 63) q = 63;
283 :
284 0 : if (!flags) {
285 0 : *dest = *oci->frame_to_show;
286 :
287 : /* handle problem with extending borders */
288 0 : dest->y_width = oci->Width;
289 0 : dest->y_height = oci->Height;
290 0 : dest->uv_height = dest->y_height / 2;
291 0 : oci->postproc_state.last_base_qindex = oci->base_qindex;
292 0 : oci->postproc_state.last_frame_valid = 1;
293 0 : return 0;
294 : }
295 0 : if (flags & VP8D_ADDNOISE) {
296 0 : if (!oci->postproc_state.generated_noise) {
297 0 : oci->postproc_state.generated_noise = vpx_calloc(
298 0 : oci->Width + 256, sizeof(*oci->postproc_state.generated_noise));
299 0 : if (!oci->postproc_state.generated_noise) return 1;
300 : }
301 : }
302 :
303 : /* Allocate post_proc_buffer_int if needed */
304 0 : if ((flags & VP8D_MFQE) && !oci->post_proc_buffer_int_used) {
305 0 : if ((flags & VP8D_DEBLOCK) || (flags & VP8D_DEMACROBLOCK)) {
306 0 : int width = (oci->Width + 15) & ~15;
307 0 : int height = (oci->Height + 15) & ~15;
308 :
309 0 : if (vp8_yv12_alloc_frame_buffer(&oci->post_proc_buffer_int, width, height,
310 : VP8BORDERINPIXELS)) {
311 0 : vpx_internal_error(&oci->error, VPX_CODEC_MEM_ERROR,
312 : "Failed to allocate MFQE framebuffer");
313 : }
314 :
315 0 : oci->post_proc_buffer_int_used = 1;
316 :
317 : /* insure that postproc is set to all 0's so that post proc
318 : * doesn't pull random data in from edge
319 : */
320 0 : memset((&oci->post_proc_buffer_int)->buffer_alloc, 128,
321 0 : (&oci->post_proc_buffer)->frame_size);
322 : }
323 : }
324 :
325 0 : vpx_clear_system_state();
326 :
327 0 : if ((flags & VP8D_MFQE) && oci->postproc_state.last_frame_valid &&
328 0 : oci->current_video_frame >= 2 &&
329 0 : oci->postproc_state.last_base_qindex < 60 &&
330 0 : oci->base_qindex - oci->postproc_state.last_base_qindex >= 20) {
331 0 : vp8_multiframe_quality_enhance(oci);
332 0 : if (((flags & VP8D_DEBLOCK) || (flags & VP8D_DEMACROBLOCK)) &&
333 0 : oci->post_proc_buffer_int_used) {
334 0 : vp8_yv12_copy_frame(&oci->post_proc_buffer, &oci->post_proc_buffer_int);
335 0 : if (flags & VP8D_DEMACROBLOCK) {
336 0 : vp8_deblock(oci, &oci->post_proc_buffer_int, &oci->post_proc_buffer,
337 0 : q + (deblock_level - 5) * 10, 1, 0);
338 0 : vp8_de_mblock(&oci->post_proc_buffer, q + (deblock_level - 5) * 10);
339 0 : } else if (flags & VP8D_DEBLOCK) {
340 0 : vp8_deblock(oci, &oci->post_proc_buffer_int, &oci->post_proc_buffer, q,
341 : 1, 0);
342 : }
343 : }
344 : /* Move partially towards the base q of the previous frame */
345 0 : oci->postproc_state.last_base_qindex =
346 0 : (3 * oci->postproc_state.last_base_qindex + oci->base_qindex) >> 2;
347 0 : } else if (flags & VP8D_DEMACROBLOCK) {
348 0 : vp8_deblock(oci, oci->frame_to_show, &oci->post_proc_buffer,
349 0 : q + (deblock_level - 5) * 10, 1, 0);
350 0 : vp8_de_mblock(&oci->post_proc_buffer, q + (deblock_level - 5) * 10);
351 :
352 0 : oci->postproc_state.last_base_qindex = oci->base_qindex;
353 0 : } else if (flags & VP8D_DEBLOCK) {
354 0 : vp8_deblock(oci, oci->frame_to_show, &oci->post_proc_buffer, q, 1, 0);
355 0 : oci->postproc_state.last_base_qindex = oci->base_qindex;
356 : } else {
357 0 : vp8_yv12_copy_frame(oci->frame_to_show, &oci->post_proc_buffer);
358 0 : oci->postproc_state.last_base_qindex = oci->base_qindex;
359 : }
360 0 : oci->postproc_state.last_frame_valid = 1;
361 :
362 0 : if (flags & VP8D_ADDNOISE) {
363 0 : if (oci->postproc_state.last_q != q ||
364 0 : oci->postproc_state.last_noise != noise_level) {
365 : double sigma;
366 0 : struct postproc_state *ppstate = &oci->postproc_state;
367 0 : vpx_clear_system_state();
368 0 : sigma = noise_level + .5 + .6 * q / 63.0;
369 0 : ppstate->clamp =
370 0 : vpx_setup_noise(sigma, ppstate->generated_noise, oci->Width + 256);
371 0 : ppstate->last_q = q;
372 0 : ppstate->last_noise = noise_level;
373 : }
374 :
375 0 : vpx_plane_add_noise(
376 0 : oci->post_proc_buffer.y_buffer, oci->postproc_state.generated_noise,
377 : oci->postproc_state.clamp, oci->postproc_state.clamp,
378 : oci->post_proc_buffer.y_width, oci->post_proc_buffer.y_height,
379 : oci->post_proc_buffer.y_stride);
380 : }
381 :
382 0 : *dest = oci->post_proc_buffer;
383 :
384 : /* handle problem with extending borders */
385 0 : dest->y_width = oci->Width;
386 0 : dest->y_height = oci->Height;
387 0 : dest->uv_height = dest->y_height / 2;
388 0 : return 0;
389 : }
390 : #endif
|