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
q2mbl(int x)48 static int q2mbl(int x) {
49   if (x < 20) x = 20;
50 
51   x = 50 + (x - 50) * 10 / 8;
52   return x * x / 3;
53 }
54 
vp8_de_mblock(YV12_BUFFER_CONFIG * post,int q)55 static void vp8_de_mblock(YV12_BUFFER_CONFIG *post, int q) {
56   vpx_mbpost_proc_across_ip(post->y_buffer, post->y_stride, post->y_height,
57                             post->y_width, q2mbl(q));
58   vpx_mbpost_proc_down(post->y_buffer, post->y_stride, post->y_height,
59                        post->y_width, q2mbl(q));
60 }
61 
vp8_deblock(VP8_COMMON * cm,YV12_BUFFER_CONFIG * source,YV12_BUFFER_CONFIG * post,int q)62 void vp8_deblock(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source,
63                  YV12_BUFFER_CONFIG *post, int q) {
64   double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065;
65   int ppl = (int)(level + .5);
66 
67   const MODE_INFO *mode_info_context = cm->mi;
68   int mbr, mbc;
69 
70   /* The pixel thresholds are adjusted according to if or not the macroblock
71    * is a skipped block.  */
72   unsigned char *ylimits = cm->pp_limits_buffer;
73   unsigned char *uvlimits = cm->pp_limits_buffer + 16 * cm->mb_cols;
74 
75   if (ppl > 0) {
76     for (mbr = 0; mbr < cm->mb_rows; ++mbr) {
77       unsigned char *ylptr = ylimits;
78       unsigned char *uvlptr = uvlimits;
79       for (mbc = 0; mbc < cm->mb_cols; ++mbc) {
80         unsigned char mb_ppl;
81 
82         if (mode_info_context->mbmi.mb_skip_coeff) {
83           mb_ppl = (unsigned char)ppl >> 1;
84         } else {
85           mb_ppl = (unsigned char)ppl;
86         }
87 
88         memset(ylptr, mb_ppl, 16);
89         memset(uvlptr, mb_ppl, 8);
90 
91         ylptr += 16;
92         uvlptr += 8;
93         mode_info_context++;
94       }
95       mode_info_context++;
96 
97       vpx_post_proc_down_and_across_mb_row(
98           source->y_buffer + 16 * mbr * source->y_stride,
99           post->y_buffer + 16 * mbr * post->y_stride, source->y_stride,
100           post->y_stride, source->y_width, ylimits, 16);
101 
102       vpx_post_proc_down_and_across_mb_row(
103           source->u_buffer + 8 * mbr * source->uv_stride,
104           post->u_buffer + 8 * mbr * post->uv_stride, source->uv_stride,
105           post->uv_stride, source->uv_width, uvlimits, 8);
106       vpx_post_proc_down_and_across_mb_row(
107           source->v_buffer + 8 * mbr * source->uv_stride,
108           post->v_buffer + 8 * mbr * post->uv_stride, source->uv_stride,
109           post->uv_stride, source->uv_width, uvlimits, 8);
110     }
111   } else {
112     vp8_yv12_copy_frame(source, post);
113   }
114 }
115 
vp8_de_noise(VP8_COMMON * cm,YV12_BUFFER_CONFIG * source,int q,int uvfilter)116 void vp8_de_noise(VP8_COMMON *cm, YV12_BUFFER_CONFIG *source, int q,
117                   int uvfilter) {
118   int mbr;
119   double level = 6.0e-05 * q * q * q - .0067 * q * q + .306 * q + .0065;
120   int ppl = (int)(level + .5);
121   int mb_rows = cm->mb_rows;
122   int mb_cols = cm->mb_cols;
123   unsigned char *limits = cm->pp_limits_buffer;
124 
125   memset(limits, (unsigned char)ppl, 16 * mb_cols);
126 
127   /* TODO: The original code don't filter the 2 outer rows and columns. */
128   for (mbr = 0; mbr < mb_rows; ++mbr) {
129     vpx_post_proc_down_and_across_mb_row(
130         source->y_buffer + 16 * mbr * source->y_stride,
131         source->y_buffer + 16 * mbr * source->y_stride, source->y_stride,
132         source->y_stride, source->y_width, limits, 16);
133     if (uvfilter == 1) {
134       vpx_post_proc_down_and_across_mb_row(
135           source->u_buffer + 8 * mbr * source->uv_stride,
136           source->u_buffer + 8 * mbr * source->uv_stride, source->uv_stride,
137           source->uv_stride, source->uv_width, limits, 8);
138       vpx_post_proc_down_and_across_mb_row(
139           source->v_buffer + 8 * mbr * source->uv_stride,
140           source->v_buffer + 8 * mbr * source->uv_stride, source->uv_stride,
141           source->uv_stride, source->uv_width, limits, 8);
142     }
143   }
144 }
145 #endif  // CONFIG_POSTPROC
146 
147 #if CONFIG_POSTPROC
vp8_post_proc_frame(VP8_COMMON * oci,YV12_BUFFER_CONFIG * dest,vp8_ppflags_t * ppflags)148 int vp8_post_proc_frame(VP8_COMMON *oci, YV12_BUFFER_CONFIG *dest,
149                         vp8_ppflags_t *ppflags) {
150   int q = oci->filter_level * 10 / 6;
151   int flags = ppflags->post_proc_flag;
152   int deblock_level = ppflags->deblocking_level;
153   int noise_level = ppflags->noise_level;
154 
155   if (!oci->frame_to_show) return -1;
156 
157   if (q > 63) q = 63;
158 
159   if (!flags) {
160     *dest = *oci->frame_to_show;
161 
162     /* handle problem with extending borders */
163     dest->y_width = oci->Width;
164     dest->y_height = oci->Height;
165     dest->uv_height = dest->y_height / 2;
166     oci->postproc_state.last_base_qindex = oci->base_qindex;
167     oci->postproc_state.last_frame_valid = 1;
168     return 0;
169   }
170   if (flags & VP8D_ADDNOISE) {
171     if (!oci->postproc_state.generated_noise) {
172       oci->postproc_state.generated_noise = vpx_calloc(
173           oci->Width + 256, sizeof(*oci->postproc_state.generated_noise));
174       if (!oci->postproc_state.generated_noise) return 1;
175     }
176   }
177 
178   /* Allocate post_proc_buffer_int if needed */
179   if ((flags & VP8D_MFQE) && !oci->post_proc_buffer_int_used) {
180     if ((flags & VP8D_DEBLOCK) || (flags & VP8D_DEMACROBLOCK)) {
181       int width = (oci->Width + 15) & ~15;
182       int height = (oci->Height + 15) & ~15;
183 
184       if (vp8_yv12_alloc_frame_buffer(&oci->post_proc_buffer_int, width, height,
185                                       VP8BORDERINPIXELS)) {
186         vpx_internal_error(&oci->error, VPX_CODEC_MEM_ERROR,
187                            "Failed to allocate MFQE framebuffer");
188       }
189 
190       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       memset((&oci->post_proc_buffer_int)->buffer_alloc, 128,
196              (&oci->post_proc_buffer)->frame_size);
197     }
198   }
199 
200   vpx_clear_system_state();
201 
202   if ((flags & VP8D_MFQE) && oci->postproc_state.last_frame_valid &&
203       oci->current_video_frame > 10 &&
204       oci->postproc_state.last_base_qindex < 60 &&
205       oci->base_qindex - oci->postproc_state.last_base_qindex >= 20) {
206     vp8_multiframe_quality_enhance(oci);
207     if (((flags & VP8D_DEBLOCK) || (flags & VP8D_DEMACROBLOCK)) &&
208         oci->post_proc_buffer_int_used) {
209       vp8_yv12_copy_frame(&oci->post_proc_buffer, &oci->post_proc_buffer_int);
210       if (flags & VP8D_DEMACROBLOCK) {
211         vp8_deblock(oci, &oci->post_proc_buffer_int, &oci->post_proc_buffer,
212                     q + (deblock_level - 5) * 10);
213         vp8_de_mblock(&oci->post_proc_buffer, q + (deblock_level - 5) * 10);
214       } else if (flags & VP8D_DEBLOCK) {
215         vp8_deblock(oci, &oci->post_proc_buffer_int, &oci->post_proc_buffer, q);
216       }
217     }
218     /* Move partially towards the base q of the previous frame */
219     oci->postproc_state.last_base_qindex =
220         (3 * oci->postproc_state.last_base_qindex + oci->base_qindex) >> 2;
221   } else if (flags & VP8D_DEMACROBLOCK) {
222     vp8_deblock(oci, oci->frame_to_show, &oci->post_proc_buffer,
223                 q + (deblock_level - 5) * 10);
224     vp8_de_mblock(&oci->post_proc_buffer, q + (deblock_level - 5) * 10);
225 
226     oci->postproc_state.last_base_qindex = oci->base_qindex;
227   } else if (flags & VP8D_DEBLOCK) {
228     vp8_deblock(oci, oci->frame_to_show, &oci->post_proc_buffer, q);
229     oci->postproc_state.last_base_qindex = oci->base_qindex;
230   } else {
231     vp8_yv12_copy_frame(oci->frame_to_show, &oci->post_proc_buffer);
232     oci->postproc_state.last_base_qindex = oci->base_qindex;
233   }
234   oci->postproc_state.last_frame_valid = 1;
235 
236   if (flags & VP8D_ADDNOISE) {
237     if (oci->postproc_state.last_q != q ||
238         oci->postproc_state.last_noise != noise_level) {
239       double sigma;
240       struct postproc_state *ppstate = &oci->postproc_state;
241       vpx_clear_system_state();
242       sigma = noise_level + .5 + .6 * q / 63.0;
243       ppstate->clamp =
244           vpx_setup_noise(sigma, ppstate->generated_noise, oci->Width + 256);
245       ppstate->last_q = q;
246       ppstate->last_noise = noise_level;
247     }
248 
249     vpx_plane_add_noise(
250         oci->post_proc_buffer.y_buffer, oci->postproc_state.generated_noise,
251         oci->postproc_state.clamp, oci->postproc_state.clamp,
252         oci->post_proc_buffer.y_width, oci->post_proc_buffer.y_height,
253         oci->post_proc_buffer.y_stride);
254   }
255 
256   *dest = oci->post_proc_buffer;
257 
258   /* handle problem with extending borders */
259   dest->y_width = oci->Width;
260   dest->y_height = oci->Height;
261   dest->uv_height = dest->y_height / 2;
262   return 0;
263 }
264 #endif
265