2014-01-24 10:37:15 +00:00
/*****************************************************************************
2021-11-23 06:46:06 +00:00
* This file is part of uvg266 VVC encoder .
2014-02-21 13:00:20 +00:00
*
2021-10-07 08:32:59 +00:00
* Copyright ( c ) 2021 , Tampere University , ITU / ISO / IEC , project contributors
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without modification ,
* are permitted provided that the following conditions are met :
*
* * Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* * Redistributions in binary form must reproduce the above copyright notice , this
* list of conditions and the following disclaimer in the documentation and / or
* other materials provided with the distribution .
*
* * Neither the name of the Tampere University or ITU / ISO / IEC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS " AND
* ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
* LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
2014-01-24 10:37:15 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-09-18 09:16:03 +00:00
# include "filter.h"
2013-04-03 08:05:07 +00:00
# include <stdlib.h>
2013-09-18 09:16:03 +00:00
2016-04-01 14:14:23 +00:00
# include "cu.h"
# include "encoder.h"
2022-12-20 09:25:58 +00:00
# include "intra.h"
2022-04-28 11:26:05 +00:00
# include "uvg266.h"
2013-09-19 12:29:39 +00:00
# include "transform.h"
2016-04-01 14:14:23 +00:00
# include "videoframe.h"
2013-04-03 08:05:07 +00:00
2013-09-19 12:29:39 +00:00
//////////////////////////////////////////////////////////////////////////
// INITIALIZATIONS
2022-04-28 11:18:09 +00:00
const uint16_t uvg_g_tc_table_8x8 [ 66 ] =
2013-04-03 08:05:07 +00:00
{
2020-09-21 11:58:34 +00:00
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 3 , 4 , 4 , 4 ,
4 , 5 , 5 , 5 , 5 , 7 , 7 , 8 , 9 , 10 , 10 , 11 , 13 , 14 , 15 , 17 , 19 , 21 , 24 , 25 , 29 , 33 ,
36 , 41 , 45 , 51 , 57 , 64 , 71 , 80 , 89 , 100 , 112 , 125 , 141 , 157 , 177 , 198 , 222 , 250 , 280 , 314 , 352 , 395
2013-04-03 08:05:07 +00:00
} ;
2022-04-28 11:18:09 +00:00
const uint8_t uvg_g_beta_table_8x8 [ 64 ] =
2013-04-03 08:05:07 +00:00
{
2020-09-21 11:58:34 +00:00
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 20 , 22 , 24 ,
26 , 28 , 30 , 32 , 34 , 36 , 38 , 40 , 42 , 44 , 46 , 48 , 50 , 52 , 54 , 56 ,
58 , 60 , 62 , 64 , 66 , 68 , 70 , 72 , 74 , 76 , 78 , 80 , 82 , 84 , 86 , 88
2013-04-03 08:05:07 +00:00
} ;
2022-04-28 11:18:09 +00:00
const int8_t uvg_g_luma_filter [ 16 ] [ 8 ] =
2013-09-23 15:07:16 +00:00
{
2021-11-22 14:04:57 +00:00
{ 0 , 0 , 0 , 64 , 0 , 0 , 0 , 0 } ,
{ 0 , 1 , - 3 , 63 , 4 , - 2 , 1 , 0 } ,
{ - 1 , 2 , - 5 , 62 , 8 , - 3 , 1 , 0 } ,
{ - 1 , 3 , - 8 , 60 , 13 , - 4 , 1 , 0 } ,
{ - 1 , 4 , - 10 , 58 , 17 , - 5 , 1 , 0 } , //1/4
{ - 1 , 4 , - 11 , 52 , 26 , - 8 , 3 , - 1 } ,
{ - 1 , 3 , - 9 , 47 , 31 , - 10 , 4 , - 1 } ,
{ - 1 , 4 , - 11 , 45 , 34 , - 10 , 4 , - 1 } ,
{ - 1 , 4 , - 11 , 40 , 40 , - 11 , 4 , - 1 } , //1/2
{ - 1 , 4 , - 10 , 34 , 45 , - 11 , 4 , - 1 } ,
{ - 1 , 4 , - 10 , 31 , 47 , - 9 , 3 , - 1 } ,
{ - 1 , 3 , - 8 , 26 , 52 , - 11 , 4 , - 1 } ,
{ 0 , 1 , - 5 , 17 , 58 , - 10 , 4 , - 1 } , //3/4
{ 0 , 1 , - 4 , 13 , 60 , - 8 , 3 , - 1 } ,
{ 0 , 1 , - 3 , 8 , 62 , - 5 , 2 , - 1 } ,
{ 0 , 1 , - 2 , 4 , 63 , - 3 , 1 , 0 }
2013-09-23 15:07:16 +00:00
} ;
2022-04-28 11:18:09 +00:00
const int8_t uvg_g_chroma_filter [ 32 ] [ 4 ] =
2013-09-23 15:07:16 +00:00
{
{ 0 , 64 , 0 , 0 } ,
2021-11-22 14:04:57 +00:00
{ - 1 , 63 , 2 , 0 } ,
{ - 2 , 62 , 4 , 0 } ,
{ - 2 , 60 , 7 , - 1 } ,
2013-09-23 15:07:16 +00:00
{ - 2 , 58 , 10 , - 2 } ,
2021-11-22 14:04:57 +00:00
{ - 3 , 57 , 12 , - 2 } ,
{ - 4 , 56 , 14 , - 2 } ,
{ - 4 , 55 , 15 , - 2 } ,
2013-09-23 15:07:16 +00:00
{ - 4 , 54 , 16 , - 2 } ,
2021-11-22 14:04:57 +00:00
{ - 5 , 53 , 18 , - 2 } ,
{ - 6 , 52 , 20 , - 2 } ,
{ - 6 , 49 , 24 , - 3 } ,
2013-09-23 15:07:16 +00:00
{ - 6 , 46 , 28 , - 4 } ,
2021-11-22 14:04:57 +00:00
{ - 5 , 44 , 29 , - 4 } ,
{ - 4 , 42 , 30 , - 4 } ,
{ - 4 , 39 , 33 , - 4 } ,
2013-09-23 15:07:16 +00:00
{ - 4 , 36 , 36 , - 4 } ,
2021-11-22 14:04:57 +00:00
{ - 4 , 33 , 39 , - 4 } ,
{ - 4 , 30 , 42 , - 4 } ,
{ - 4 , 29 , 44 , - 5 } ,
2013-09-23 15:07:16 +00:00
{ - 4 , 28 , 46 , - 6 } ,
2021-11-22 14:04:57 +00:00
{ - 3 , 24 , 49 , - 6 } ,
{ - 2 , 20 , 52 , - 6 } ,
{ - 2 , 18 , 53 , - 5 } ,
2013-09-23 15:07:16 +00:00
{ - 2 , 16 , 54 , - 4 } ,
2021-11-22 14:04:57 +00:00
{ - 2 , 15 , 55 , - 4 } ,
{ - 2 , 14 , 56 , - 4 } ,
{ - 2 , 12 , 57 , - 3 } ,
{ - 2 , 10 , 58 , - 2 } ,
{ - 1 , 7 , 60 , - 2 } ,
{ 0 , 4 , 62 , - 2 } ,
{ 0 , 2 , 63 , - 1 } ,
2013-09-23 15:07:16 +00:00
} ;
2013-09-19 12:29:39 +00:00
//////////////////////////////////////////////////////////////////////////
// FUNCTIONS
2013-04-03 08:05:07 +00:00
2013-09-19 12:29:39 +00:00
/**
2017-01-17 19:31:48 +00:00
* \ brief Perform in strong luma filtering in place .
* \ param line line of 8 pixels , with center at index 4
* \ param tc tc treshold
* \ return Reach of the filter starting from center .
2013-09-19 12:29:39 +00:00
*/
2022-04-28 11:18:09 +00:00
static INLINE int uvg_filter_deblock_luma_strong (
uvg_pixel * line ,
2017-01-17 19:31:48 +00:00
int32_t tc )
2013-04-03 08:05:07 +00:00
{
2022-04-28 11:18:09 +00:00
const uvg_pixel m0 = line [ 0 ] ;
const uvg_pixel m1 = line [ 1 ] ;
const uvg_pixel m2 = line [ 2 ] ;
const uvg_pixel m3 = line [ 3 ] ;
const uvg_pixel m4 = line [ 4 ] ;
const uvg_pixel m5 = line [ 5 ] ;
const uvg_pixel m6 = line [ 6 ] ;
const uvg_pixel m7 = line [ 7 ] ;
2020-12-09 17:10:38 +00:00
const uint8_t tcW [ 3 ] = { 3 , 2 , 1 } ; //Wheights for tc
2017-01-17 19:31:48 +00:00
2020-12-11 17:34:56 +00:00
line [ 1 ] = CLIP ( m1 - tcW [ 2 ] * tc , m1 + tcW [ 2 ] * tc , ( 2 * m0 + 3 * m1 + m2 + m3 + m4 + 4 ) > > 3 ) ;
2020-12-09 17:10:38 +00:00
line [ 2 ] = CLIP ( m2 - tcW [ 1 ] * tc , m2 + tcW [ 1 ] * tc , ( m1 + m2 + m3 + m4 + 2 ) > > 2 ) ;
line [ 3 ] = CLIP ( m3 - tcW [ 0 ] * tc , m3 + tcW [ 0 ] * tc , ( m1 + 2 * m2 + 2 * m3 + 2 * m4 + m5 + 4 ) > > 3 ) ;
line [ 4 ] = CLIP ( m4 - tcW [ 0 ] * tc , m4 + tcW [ 0 ] * tc , ( m2 + 2 * m3 + 2 * m4 + 2 * m5 + m6 + 4 ) > > 3 ) ;
line [ 5 ] = CLIP ( m5 - tcW [ 1 ] * tc , m5 + tcW [ 1 ] * tc , ( m3 + m4 + m5 + m6 + 2 ) > > 2 ) ;
2020-12-11 17:34:56 +00:00
line [ 6 ] = CLIP ( m6 - tcW [ 2 ] * tc , m6 + tcW [ 2 ] * tc , ( m3 + m4 + m5 + 3 * m6 + 2 * m7 + 4 ) > > 3 ) ;
2017-01-17 19:31:48 +00:00
return 3 ;
}
2014-02-21 13:00:20 +00:00
2017-01-17 19:31:48 +00:00
/**
* \ brief Perform in weak luma filtering in place .
* \ param encoder Encoder
* \ param line Line of 8 pixels , with center at index 4
* \ param tc The tc treshold
* \ param p_2nd Whether to filter the 2 nd line of P
* \ param q_2nd Whether to filter the 2 nd line of Q
*/
2022-04-28 11:18:09 +00:00
static INLINE int uvg_filter_deblock_luma_weak (
2017-01-17 19:31:48 +00:00
const encoder_control_t * const encoder ,
2022-04-28 11:18:09 +00:00
uvg_pixel * line ,
2017-01-17 19:31:48 +00:00
int32_t tc ,
bool p_2nd ,
bool q_2nd )
{
2022-04-28 11:18:09 +00:00
const uvg_pixel m1 = line [ 1 ] ;
const uvg_pixel m2 = line [ 2 ] ;
const uvg_pixel m3 = line [ 3 ] ;
const uvg_pixel m4 = line [ 4 ] ;
const uvg_pixel m5 = line [ 5 ] ;
const uvg_pixel m6 = line [ 6 ] ;
2017-01-17 19:31:48 +00:00
int32_t delta = ( 9 * ( m4 - m3 ) - 3 * ( m5 - m2 ) + 8 ) > > 4 ;
if ( abs ( delta ) > = tc * 10 ) {
return 0 ;
2013-09-19 12:29:39 +00:00
} else {
2017-01-17 19:31:48 +00:00
int32_t tc2 = tc > > 1 ;
delta = CLIP ( - tc , tc , delta ) ;
line [ 3 ] = CLIP ( 0 , ( 1 < < encoder - > bitdepth ) - 1 , ( m3 + delta ) ) ;
line [ 4 ] = CLIP ( 0 , ( 1 < < encoder - > bitdepth ) - 1 , ( m4 - delta ) ) ;
if ( p_2nd ) {
int32_t delta1 = CLIP ( - tc2 , tc2 , ( ( ( m1 + m3 + 1 ) > > 1 ) - m2 + delta ) > > 1 ) ;
line [ 2 ] = CLIP ( 0 , ( 1 < < encoder - > bitdepth ) - 1 , m2 + delta1 ) ;
}
if ( q_2nd ) {
int32_t delta2 = CLIP ( - tc2 , tc2 , ( ( ( m6 + m4 + 1 ) > > 1 ) - m5 - delta ) > > 1 ) ;
line [ 5 ] = CLIP ( 0 , ( 1 < < encoder - > bitdepth ) - 1 , m5 + delta2 ) ;
}
if ( p_2nd | | q_2nd ) {
return 2 ;
} else {
return 1 ;
2013-04-03 08:05:07 +00:00
}
}
}
2013-09-19 12:29:39 +00:00
/**
2020-12-21 16:47:02 +00:00
* \ brief Performe strong / weak filtering for chroma
2013-09-19 12:29:39 +00:00
*/
2022-04-28 11:18:09 +00:00
static INLINE void uvg_filter_deblock_chroma ( const encoder_control_t * const encoder ,
uvg_pixel * src ,
2021-01-01 18:10:08 +00:00
int32_t offset ,
int32_t tc ,
int8_t part_P_nofilter ,
int8_t part_Q_nofilter ,
bool sw ,
bool large_boundary ,
bool is_chroma_hor_CTB_boundary )
2013-05-29 11:50:03 +00:00
{
int32_t delta ;
2020-12-18 09:06:41 +00:00
int16_t m0 = src [ - offset * 4 ] ;
int16_t m1 = src [ - offset * 3 ] ;
2013-09-19 12:29:39 +00:00
int16_t m2 = src [ - offset * 2 ] ;
int16_t m3 = src [ - offset ] ;
int16_t m4 = src [ 0 ] ;
2014-02-21 13:00:20 +00:00
int16_t m5 = src [ offset ] ;
2020-12-18 09:06:41 +00:00
int16_t m6 = src [ offset * 2 ] ;
int16_t m7 = src [ offset * 3 ] ;
if ( sw ) {
if ( is_chroma_hor_CTB_boundary ) {
src [ - offset * 1 ] = CLIP ( m3 - tc , m3 + tc , ( 3 * m2 + 2 * m3 + m4 + m5 + m6 + 4 ) > > 3 ) ;
2021-01-01 18:10:08 +00:00
src [ 0 ] = CLIP ( m4 - tc , m4 + tc , ( 2 * m2 + m3 + 2 * m4 + m5 + m6 + m7 + 4 ) > > 3 ) ;
} else {
2020-12-18 09:06:41 +00:00
src [ - offset * 3 ] = CLIP ( m1 - tc , m1 + tc , ( 3 * m0 + 2 * m1 + m2 + m3 + m4 + 4 ) > > 3 ) ;
src [ - offset * 2 ] = CLIP ( m2 - tc , m2 + tc , ( 2 * m0 + m1 + 2 * m2 + m3 + m4 + m5 + 4 ) > > 3 ) ;
src [ - offset * 1 ] = CLIP ( m3 - tc , m3 + tc , ( m0 + m1 + m2 + 2 * m3 + m4 + m5 + m6 + 4 ) > > 3 ) ;
src [ 0 ] = CLIP ( m4 - tc , m4 + tc , ( m1 + m2 + m3 + 2 * m4 + m5 + m6 + m7 + 4 ) > > 3 ) ;
}
2021-01-07 14:06:03 +00:00
src [ offset * 1 ] = CLIP ( m5 - tc , m5 + tc , ( m2 + m3 + m4 + 2 * m5 + m6 + 2 * m7 + 4 ) > > 3 ) ;
2020-12-18 09:06:41 +00:00
src [ offset * 2 ] = CLIP ( m6 - tc , m6 + tc , ( m3 + m4 + m5 + 2 * m6 + 3 * m7 + 4 ) > > 3 ) ;
2021-01-01 18:10:08 +00:00
} else {
delta = CLIP ( - tc , tc , ( ( ( m4 - m3 ) * 4 ) + m2 - m5 + 4 ) > > 3 ) ;
2014-04-17 08:28:20 +00:00
src [ - offset ] = CLIP ( 0 , ( 1 < < encoder - > bitdepth ) - 1 , m3 + delta ) ;
src [ 0 ] = CLIP ( 0 , ( 1 < < encoder - > bitdepth ) - 1 , m4 - delta ) ;
2013-05-29 11:50:03 +00:00
}
2021-01-01 18:10:08 +00:00
if ( part_P_nofilter ) {
if ( large_boundary ) {
2022-06-17 08:27:19 +00:00
src [ - offset * 3 ] = ( uvg_pixel ) m1 ;
src [ - offset * 2 ] = ( uvg_pixel ) m2 ;
2021-01-01 18:10:08 +00:00
}
2022-06-17 08:27:19 +00:00
src [ - offset * 1 ] = ( uvg_pixel ) m3 ;
2021-01-01 18:10:08 +00:00
}
if ( part_Q_nofilter ) {
if ( large_boundary ) {
2022-06-17 08:27:19 +00:00
src [ offset * 1 ] = ( uvg_pixel ) m5 ;
src [ offset * 2 ] = ( uvg_pixel ) m6 ;
2021-01-01 18:10:08 +00:00
}
2022-06-17 08:27:19 +00:00
src [ 0 ] = ( uvg_pixel ) m4 ;
2020-12-18 09:06:41 +00:00
}
2013-05-29 11:50:03 +00:00
}
2013-09-19 12:29:39 +00:00
/**
2015-11-18 08:46:31 +00:00
* \ brief Check whether an edge is a TU boundary .
2015-11-10 13:25:25 +00:00
*
* \ param state encoder state
* \ param x x - coordinate of the scu in pixels
* \ param y y - coordinate of the scu in pixels
* \ param dir direction of the edge to check
* \ return true , if the edge is a TU boundary , otherwise false
*/
2022-06-30 09:24:53 +00:00
static bool is_tu_boundary (
const encoder_state_t * const state ,
int32_t x ,
int32_t y ,
edge_dir dir ,
2022-12-13 12:51:38 +00:00
color_t color ,
2022-06-30 09:24:53 +00:00
enum uvg_tree_type tree_type )
2015-11-10 13:25:25 +00:00
{
2022-06-30 09:24:53 +00:00
// if (x & 3 || y & 3) return false;
2016-01-12 07:21:00 +00:00
const cu_info_t * const scu =
2022-06-30 09:24:53 +00:00
uvg_cu_array_at_const ( tree_type ! = UVG_CHROMA_T ? state - > tile - > frame - > cu_array : state - > tile - > frame - > chroma_cu_array , x , y ) ;
2015-11-10 13:25:25 +00:00
if ( dir = = EDGE_HOR ) {
2022-12-13 12:51:38 +00:00
return color = = COLOR_Y ? scu - > luma_deblocking & EDGE_HOR :
scu - > chroma_deblocking & EDGE_HOR ;
2015-11-10 13:25:25 +00:00
} else {
2022-12-13 12:51:38 +00:00
return color = = COLOR_Y ? scu - > luma_deblocking & EDGE_VER :
scu - > chroma_deblocking & EDGE_VER ;
2015-11-10 13:25:25 +00:00
}
}
/**
2015-11-18 08:46:31 +00:00
* \ brief Check whether an edge is a PU boundary .
*
* \ param state encoder state
* \ param x x - coordinate of the scu in pixels
* \ param y y - coordinate of the scu in pixels
* \ param dir direction of the edge to check
* \ return true , if the edge is a TU boundary , otherwise false
*/
static bool is_pu_boundary ( const encoder_state_t * const state ,
int32_t x ,
int32_t y ,
edge_dir dir )
{
2022-06-30 09:24:53 +00:00
/*
TODO : it appears that this function can never be true when is_tu_boundary
is false . Therefore it should be safe to remove this function but let ' s keep
it for now , in case some other tool requires it .
*/
2015-11-18 08:46:31 +00:00
return false ;
}
/**
* \ brief Check whether an edge is aligned on a 8 x8 grid .
2015-11-10 13:25:25 +00:00
*
* \ param x x - coordinate of the edge
* \ param y y - coordinate of the edge
* \ param dir direction of the edge
* \ return true , if the edge is aligned on a 8 x8 grid , otherwise false
*/
static bool is_on_8x8_grid ( int x , int y , edge_dir dir )
{
if ( dir = = EDGE_HOR ) {
2022-12-13 12:51:38 +00:00
return ( y & 7 ) = = 0 ;
2015-11-10 13:25:25 +00:00
} else {
2022-12-13 12:51:38 +00:00
return ( x & 7 ) = = 0 ;
2015-11-10 13:25:25 +00:00
}
}
2016-09-27 11:39:37 +00:00
static int8_t get_qp_y_pred ( const encoder_state_t * state , int x , int y , edge_dir dir )
{
2022-02-06 18:08:28 +00:00
if ( state - > frame - > max_qp_delta_depth < 0 ) {
2016-09-27 11:39:37 +00:00
return state - > qp ;
}
int32_t qp_p ;
if ( dir = = EDGE_HOR & & y > 0 ) {
2022-04-28 11:18:09 +00:00
qp_p = uvg_cu_array_at_const ( state - > tile - > frame - > cu_array , x , y - 1 ) - > qp ;
2016-09-27 11:39:37 +00:00
} else if ( dir = = EDGE_VER & & x > 0 ) {
2022-04-28 11:18:09 +00:00
qp_p = uvg_cu_array_at_const ( state - > tile - > frame - > cu_array , x - 1 , y ) - > qp ;
2016-09-27 11:39:37 +00:00
} else {
2019-05-22 14:26:10 +00:00
// TODO: This seems to be dead code. Investigate.
qp_p = state - > encoder_control - > cfg . set_qp_in_cu ? 26 : state - > frame - > QP ;
2016-09-27 11:39:37 +00:00
}
const int32_t qp_q =
2022-04-28 11:18:09 +00:00
uvg_cu_array_at_const ( state - > tile - > frame - > cu_array , x , y ) - > qp ;
2016-09-27 11:39:37 +00:00
return ( qp_p + qp_q + 1 ) > > 1 ;
}
2017-01-17 19:31:48 +00:00
/**
* \ brief Gather pixels needed for deblocking
*/
static INLINE void gather_deblock_pixels (
2022-04-28 11:18:09 +00:00
const uvg_pixel * src ,
2017-01-17 19:31:48 +00:00
int step ,
int stride ,
int reach ,
2022-04-28 11:18:09 +00:00
uvg_pixel * dst )
2017-01-17 19:31:48 +00:00
{
for ( int i = - reach ; i < + reach ; + + i ) {
dst [ i + 4 ] = src [ i * step + stride ] ;
}
}
2021-12-10 17:05:43 +00:00
/**
* \ brief Gather pixels from src to dst using a custom stride and step for src
*/
static INLINE void gather_pixels (
2022-04-28 11:18:09 +00:00
const uvg_pixel * src ,
2021-12-10 17:05:43 +00:00
int step ,
int stride ,
int numel ,
2022-04-28 11:18:09 +00:00
uvg_pixel * dst )
2021-12-10 17:05:43 +00:00
{
for ( int i = 0 ; i < numel ; + + i ) {
dst [ i ] = src [ i * step + stride ] ;
}
}
2017-01-17 19:31:48 +00:00
/**
* \ brief Scatter pixels
*/
static INLINE void scatter_deblock_pixels (
2022-04-28 11:18:09 +00:00
const uvg_pixel * src ,
2017-01-17 19:31:48 +00:00
int step ,
int stride ,
int reach ,
2022-04-28 11:18:09 +00:00
uvg_pixel * dst )
2017-01-17 16:47:38 +00:00
{
2017-01-17 19:31:48 +00:00
for ( int i = - reach ; i < + reach ; + + i ) {
dst [ i * step + stride ] = src [ i + 4 ] ;
2017-01-17 16:47:38 +00:00
}
}
2020-12-21 16:47:02 +00:00
/**
* \ brief Perform large block strong luma filtering in place .
* \ param line line of 8 pixels , with center at index 4
* \ param lineL extended pixels with P pixels in [ 0 , 3 ] and Q pixels in [ 4 , 7 ]
* \ param tc tc treshold
* \ param filter_length_P filter length in the P block
* \ param filter_length_Q filter length in the Q block
* \ return Reach of the filter starting from center .
*/
2022-04-28 11:18:09 +00:00
static INLINE int uvg_filter_deblock_large_block ( uvg_pixel * line , uvg_pixel * lineL , const int32_t tc ,
2020-12-10 16:03:18 +00:00
const uint8_t filter_length_P , const uint8_t filter_length_Q )
2020-12-09 17:10:38 +00:00
{
2020-12-11 17:34:56 +00:00
int ref_P = 0 ;
int ref_Q = 0 ;
int ref_middle = 0 ;
2020-12-10 16:03:18 +00:00
const int coeffs7 [ 7 ] = { 59 , 50 , 41 , 32 , 23 , 14 , 5 } ;
const int coeffs5 [ 5 ] = { 58 , 45 , 32 , 19 , 6 } ;
const int coeffs3 [ 3 ] = { 53 , 32 , 11 } ;
2020-12-22 12:06:20 +00:00
const int * coeffs_P = NULL ;
const int * coeffs_Q = NULL ;
2020-12-11 17:34:56 +00:00
//Form P/Q arrays that contain all of the samples to make things simpler later
2022-04-28 11:18:09 +00:00
const uvg_pixel lineP [ 8 ] = { line [ 3 ] , line [ 2 ] , line [ 1 ] , line [ 0 ] ,
2021-01-14 15:22:12 +00:00
lineL [ 3 ] , lineL [ 2 ] , lineL [ 1 ] , lineL [ 0 ] } ;
2022-04-28 11:18:09 +00:00
const uvg_pixel lineQ [ 8 ] = { line [ 4 ] , line [ 5 ] , line [ 6 ] , line [ 7 ] ,
2021-01-14 15:22:12 +00:00
lineL [ 4 ] , lineL [ 5 ] , lineL [ 6 ] , lineL [ 7 ] } ;
2020-12-11 17:34:56 +00:00
//Separate destination arrays with only six output pixels going in line and rest to lineL to simplify things later
2022-04-28 11:18:09 +00:00
uvg_pixel * dstP [ 7 ] = { line + 3 , line + 2 , line + 1 ,
2020-12-11 17:34:56 +00:00
lineL + 3 , lineL + 2 , lineL + 1 , lineL + 0 } ;
2022-04-28 11:18:09 +00:00
uvg_pixel * dstQ [ 7 ] = { line + 4 , line + 5 , line + 6 ,
2020-12-11 17:34:56 +00:00
lineL + 4 , lineL + 5 , lineL + 6 , lineL + 7 } ;
//Get correct filter coeffs and Q/P end samples
2020-12-10 16:03:18 +00:00
switch ( filter_length_P )
{
case 7 :
2021-01-14 15:22:12 +00:00
ref_P = ( lineP [ 6 ] + lineP [ 7 ] + 1 ) > > 1 ;
2020-12-11 17:34:56 +00:00
coeffs_P = coeffs7 ;
2020-12-10 16:03:18 +00:00
break ;
case 5 :
2021-01-14 15:22:12 +00:00
ref_P = ( lineP [ 4 ] + lineP [ 5 ] + 1 ) > > 1 ;
2020-12-11 17:34:56 +00:00
coeffs_P = coeffs5 ;
2020-12-10 16:03:18 +00:00
break ;
case 3 :
2021-01-14 15:22:12 +00:00
ref_P = ( lineP [ 2 ] + lineP [ 3 ] + 1 ) > > 1 ;
2020-12-11 17:34:56 +00:00
coeffs_P = coeffs3 ;
2020-12-10 16:03:18 +00:00
break ;
}
switch ( filter_length_Q )
{
case 7 :
2021-01-14 15:22:12 +00:00
ref_Q = ( lineQ [ 6 ] + lineQ [ 7 ] + 1 ) > > 1 ;
2020-12-11 17:34:56 +00:00
coeffs_Q = coeffs7 ;
2020-12-10 16:03:18 +00:00
break ;
case 5 :
2021-01-14 15:22:12 +00:00
ref_Q = ( lineQ [ 4 ] + lineQ [ 5 ] + 1 ) > > 1 ;
2020-12-11 17:34:56 +00:00
coeffs_Q = coeffs5 ;
2020-12-10 16:03:18 +00:00
break ;
case 3 :
2021-01-14 15:22:12 +00:00
ref_Q = ( lineQ [ 2 ] + lineQ [ 3 ] + 1 ) > > 1 ;
2020-12-11 17:34:56 +00:00
coeffs_Q = coeffs3 ;
2020-12-10 16:03:18 +00:00
break ;
}
2020-12-11 17:34:56 +00:00
//Get middle samples
2020-12-10 16:03:18 +00:00
if ( filter_length_P = = filter_length_Q ) {
if ( filter_length_P = = 7 ) {
2021-01-14 15:22:12 +00:00
ref_middle = ( lineP [ 6 ] + lineP [ 5 ] + lineP [ 4 ] + lineP [ 3 ] + lineP [ 2 ] + lineP [ 1 ]
+ 2 * ( lineP [ 0 ] + lineQ [ 0 ] )
+ lineQ [ 1 ] + lineQ [ 2 ] + lineQ [ 3 ] + lineQ [ 4 ] + lineQ [ 5 ] + lineQ [ 6 ] + 8 ) > > 4 ;
2020-12-10 16:03:18 +00:00
}
else { //filter_length_P == 5
2021-01-14 15:22:12 +00:00
ref_middle = ( lineP [ 4 ] + lineP [ 3 ]
+ 2 * ( lineP [ 2 ] + lineP [ 1 ] + lineP [ 0 ] + lineQ [ 0 ] + lineQ [ 1 ] + lineQ [ 2 ] )
+ lineQ [ 3 ] + lineQ [ 4 ] + 8 ) > > 4 ;
2020-12-10 16:03:18 +00:00
}
}
else {
2020-12-11 17:34:56 +00:00
const uint8_t lenS = MIN ( filter_length_P , filter_length_Q ) ;
const uint8_t lenL = MAX ( filter_length_P , filter_length_Q ) ;
2022-04-28 11:18:09 +00:00
const uvg_pixel * refS = filter_length_P < filter_length_Q ? lineP : lineQ ;
const uvg_pixel * refL = filter_length_P < filter_length_Q ? lineQ : lineP ;
2020-12-11 17:34:56 +00:00
if ( lenL = = 7 & & lenS = = 5 ) {
2021-01-14 15:22:12 +00:00
ref_middle = ( lineP [ 5 ] + lineP [ 4 ] + lineP [ 3 ] + lineP [ 2 ]
+ 2 * ( lineP [ 1 ] + lineP [ 0 ] + lineQ [ 0 ] + lineQ [ 1 ] )
+ lineQ [ 2 ] + lineQ [ 3 ] + lineQ [ 4 ] + lineQ [ 5 ] + 8 ) > > 4 ;
2020-12-11 17:34:56 +00:00
}
else if ( lenL = = 7 & & lenS = = 3 ) {
2021-01-14 15:22:12 +00:00
ref_middle = ( 3 * refS [ 0 ] + 2 * refL [ 0 ] + 3 * refS [ 1 ] + refL [ 1 ] + 2 * refS [ 2 ]
+ refL [ 2 ] + refL [ 3 ] + refL [ 4 ] + refL [ 5 ] + refL [ 6 ] + 8 ) > > 4 ;
2020-12-11 17:34:56 +00:00
}
else { //lenL == 5 && lenS == 3
2021-01-14 15:22:12 +00:00
ref_middle = ( lineP [ 3 ] + lineP [ 2 ] + lineP [ 1 ] + lineP [ 0 ]
+ lineQ [ 0 ] + lineQ [ 1 ] + lineQ [ 2 ] + lineQ [ 3 ] + 4 ) > > 3 ;
2020-12-11 17:34:56 +00:00
}
}
//Filter pixels in the line
const uint8_t tc7 [ 7 ] = { 6 , 5 , 4 , 3 , 2 , 1 , 1 } ;
const uint8_t tc3 [ 3 ] = { 6 , 4 , 2 } ;
const uint8_t * tc_coeff_P = ( filter_length_P = = 3 ) ? tc3 : tc7 ;
const uint8_t * tc_coeff_Q = ( filter_length_Q = = 3 ) ? tc3 : tc7 ;
for ( size_t i = 0 ; i < filter_length_P ; i + + )
{
int range = ( tc * tc_coeff_P [ i ] ) > > 1 ;
2021-01-14 15:22:12 +00:00
* dstP [ i ] = CLIP ( lineP [ i ] - range , lineP [ i ] + range , ( ref_middle * coeffs_P [ i ] + ref_P * ( 64 - coeffs_P [ i ] ) + 32 ) > > 6 ) ;
2020-12-11 17:34:56 +00:00
}
for ( size_t i = 0 ; i < filter_length_Q ; i + + )
{
2020-12-22 12:06:20 +00:00
int range = ( tc * tc_coeff_Q [ i ] ) > > 1 ;
2021-01-14 15:22:12 +00:00
* dstQ [ i ] = CLIP ( lineQ [ i ] - range , lineQ [ i ] + range , ( ref_middle * coeffs_Q [ i ] + ref_Q * ( 64 - coeffs_Q [ i ] ) + 32 ) > > 6 ) ;
2020-12-10 16:03:18 +00:00
}
2020-12-09 17:10:38 +00:00
return 3 ;
}
2020-12-04 16:31:48 +00:00
/**
* \ brief Determine if strong or weak filtering should be used
*/
2022-04-28 11:18:09 +00:00
static INLINE bool use_strong_filtering ( const uvg_pixel * const b0 , const uvg_pixel * const b3 ,
const uvg_pixel * const b0L , const uvg_pixel * const b3L ,
2020-12-22 12:06:20 +00:00
const int_fast32_t dp0 , const int_fast32_t dq0 ,
const int_fast32_t dp3 , const int_fast32_t dq3 ,
const int32_t tc , const int32_t beta ,
const bool is_side_P_large , const bool is_side_Q_large ,
const uint8_t max_filter_length_P , const uint8_t max_filter_length_Q ,
const bool is_chroma_CTB_boundary )
2020-12-04 16:31:48 +00:00
{
2020-12-15 12:10:09 +00:00
int_fast32_t sp0 = is_chroma_CTB_boundary ? abs ( b0 [ 2 ] - b0 [ 3 ] ) : abs ( b0 [ 0 ] - b0 [ 3 ] ) ;
int_fast32_t sp3 = is_chroma_CTB_boundary ? abs ( b3 [ 2 ] - b3 [ 3 ] ) : abs ( b3 [ 0 ] - b3 [ 3 ] ) ;
2020-12-21 16:47:02 +00:00
2020-12-11 17:34:56 +00:00
if ( is_side_P_large | | is_side_Q_large ) { //Large block decision
2020-12-08 16:38:05 +00:00
int_fast32_t sq0 = abs ( b0 [ 4 ] - b0 [ 7 ] ) ;
int_fast32_t sq3 = abs ( b3 [ 4 ] - b3 [ 7 ] ) ;
2022-04-28 11:18:09 +00:00
uvg_pixel tmp0 , tmp3 ;
2020-12-08 16:38:05 +00:00
if ( is_side_P_large ) {
if ( max_filter_length_P = = 7 ) {
tmp0 = b0L [ 0 ] ;
tmp3 = b3L [ 0 ] ;
sp0 = sp0 + abs ( b0L [ 3 ] - b0L [ 2 ] - b0L [ 1 ] + tmp0 ) ;
sp3 = sp3 + abs ( b3L [ 3 ] - b3L [ 2 ] - b3L [ 1 ] + tmp3 ) ;
2020-12-21 16:47:02 +00:00
} else {
2020-12-08 16:38:05 +00:00
tmp0 = b0L [ 2 ] ;
tmp3 = b3L [ 2 ] ;
}
sp0 = ( sp0 + abs ( b0 [ 0 ] - tmp0 ) + 1 ) > > 1 ;
sp3 = ( sp3 + abs ( b3 [ 0 ] - tmp3 ) + 1 ) > > 1 ;
}
if ( is_side_Q_large ) {
if ( max_filter_length_Q = = 7 ) {
tmp0 = b0L [ 7 ] ;
tmp3 = b3L [ 7 ] ;
sq0 = sq0 + abs ( b0L [ 4 ] - b0L [ 5 ] - b0L [ 6 ] + tmp0 ) ;
sq3 = sq3 + abs ( b3L [ 4 ] - b3L [ 5 ] - b3L [ 6 ] + tmp3 ) ;
} else {
tmp0 = b0L [ 5 ] ;
tmp3 = b3L [ 5 ] ;
}
sq0 = ( sq0 + abs ( tmp0 - b0 [ 7 ] ) + 1 ) > > 1 ;
sq3 = ( sq3 + abs ( tmp3 - b3 [ 7 ] ) + 1 ) > > 1 ;
}
return 2 * ( dp0 + dq0 ) < beta > > 4 & &
2020-12-21 16:47:02 +00:00
2 * ( dp3 + dq3 ) < beta > > 4 & &
abs ( b0 [ 3 ] - b0 [ 4 ] ) < ( 5 * tc + 1 ) > > 1 & &
abs ( b3 [ 3 ] - b3 [ 4 ] ) < ( 5 * tc + 1 ) > > 1 & &
sp0 + sq0 < ( beta * 3 > > 5 ) & &
sp3 + sq3 < ( beta * 3 > > 5 ) ;
} else { //Normal decision
2020-12-04 16:31:48 +00:00
return 2 * ( dp0 + dq0 ) < beta > > 2 & &
2020-12-21 16:47:02 +00:00
2 * ( dp3 + dq3 ) < beta > > 2 & &
abs ( b0 [ 3 ] - b0 [ 4 ] ) < ( 5 * tc + 1 ) > > 1 & &
abs ( b3 [ 3 ] - b3 [ 4 ] ) < ( 5 * tc + 1 ) > > 1 & &
sp0 + abs ( b0 [ 4 ] - b0 [ 7 ] ) < beta > > 3 & &
sp3 + abs ( b3 [ 4 ] - b3 [ 7 ] ) < beta > > 3 ;
}
}
static INLINE void get_max_filter_length ( uint8_t * filt_len_P , uint8_t * filt_len_Q ,
const encoder_state_t * const state , const uint32_t x , const uint32_t y ,
const edge_dir dir , const bool transform_edge ,
2020-12-22 12:06:20 +00:00
const int tu_size_P_side , const int tu_size_Q_side ,
const int pu_pos , const int pu_size ,
2022-06-30 09:24:53 +00:00
const bool merge_flag , const color_t comp ,
enum uvg_tree_type tree_type )
2020-12-21 16:47:02 +00:00
{
2020-12-22 12:06:20 +00:00
//const int tu_size_P_side = 0;
//const int tu_size_Q_side = 0;
2020-12-21 16:47:02 +00:00
//const int size = 0;
const int x_mul = dir = = EDGE_HOR ? 0 : 1 ;
const int y_mul = dir = = EDGE_HOR ? 1 : 0 ;
2020-12-22 12:06:20 +00:00
const int pos = dir = = EDGE_HOR ? y : x ;
const int len = EDGE_HOR ? state - > tile - > frame - > height : state - > tile - > frame - > width ;
2020-12-21 16:47:02 +00:00
//const bool transform_edge = is_tu_boundary(state, x, y, dir);
bool transform_edge_4x4 [ 2 ] = { false , false } ;
bool transform_edge_8x8 [ 2 ] = { false , false } ;
2020-12-22 12:06:20 +00:00
2022-12-13 12:51:38 +00:00
if ( pos > = 4 ) transform_edge_4x4 [ 0 ] = is_tu_boundary ( state , x - x_mul * 4 , y - y_mul * 4 , dir , comp , tree_type ) ;
if ( pos > = 8 ) transform_edge_8x8 [ 0 ] = is_tu_boundary ( state , x - x_mul * 8 , y - y_mul * 8 , dir , comp , tree_type ) ;
if ( pos + 4 < len ) transform_edge_4x4 [ 1 ] = is_tu_boundary ( state , x + x_mul * 4 , y + y_mul * 4 , dir , comp , tree_type ) ;
if ( pos + 8 < len ) transform_edge_8x8 [ 1 ] = is_tu_boundary ( state , x + x_mul * 8 , y + y_mul * 8 , dir , comp , tree_type ) ;
2020-12-21 16:47:02 +00:00
if ( comp = = COLOR_Y ) {
2021-04-22 05:52:06 +00:00
if ( tu_size_P_side < = 4 | | tu_size_Q_side < = 4 ) {
2020-12-21 16:47:02 +00:00
* filt_len_P = 1 ;
* filt_len_Q = 1 ;
}
else {
2020-12-22 12:06:20 +00:00
* filt_len_P = tu_size_P_side > = 32 ? 7 : 3 ;
* filt_len_Q = tu_size_Q_side > = 32 ? 7 : 3 ;
2020-12-21 16:47:02 +00:00
}
2020-12-29 15:57:35 +00:00
if ( ( merge_flag & & false ) | | false ) //TODO: Add merge_mode == SUBPU_ATMVP and cu.affine
{
if ( transform_edge ) {
* filt_len_Q = MIN ( * filt_len_Q , 5 ) ;
if ( pu_pos > 0 ) {
* filt_len_P = MIN ( * filt_len_P , 5 ) ;
}
} else if ( pu_pos > 0 & & ( transform_edge_4x4 [ 0 ] | | ( pu_pos + 4 ) > = pu_size | | transform_edge_4x4 [ 1 ] ) ) { //adjacent to transform edge (4x4 grid)
* filt_len_P = 1 ;
* filt_len_Q = 1 ;
} else if ( pu_pos > 0 & & ( pu_pos = = 8 | | transform_edge_8x8 [ 0 ] | | ( pu_pos + 8 ) > = pu_size | | transform_edge_8x8 [ 1 ] ) ) { //adjacent to transform edge (8x8 grid)
* filt_len_P = 2 ;
* filt_len_Q = 2 ;
} else {
* filt_len_P = 3 ;
* filt_len_Q = 3 ;
2020-12-21 16:47:02 +00:00
}
}
}
else {
2020-12-22 12:06:20 +00:00
* filt_len_P = ( tu_size_P_side > = 8 & & tu_size_Q_side > = 8 ) ? 3 : 1 ;
* filt_len_Q = ( tu_size_P_side > = 8 & & tu_size_Q_side > = 8 ) ? 3 : 1 ;
2020-12-04 16:31:48 +00:00
}
}
2015-11-10 13:25:25 +00:00
/**
* \ brief Apply the deblocking filter to luma pixels on a single edge .
*
* The caller should check that the edge is a TU boundary or a PU boundary .
*
\ verbatim
. - - filter this edge if dir = = EDGE_HOR
v
+ - - - - - - - - +
| o < - - pixel at ( x , y )
| |
| < - - filter this edge if dir = = EDGE_VER
| |
+ - - - - - - - - +
\ endverbatim
*
* \ param state encoder state
* \ param x x - coordinate in pixels ( see above )
* \ param y y - coordinate in pixels ( see above )
* \ param length length of the edge in pixels
* \ param dir direction of the edge to filter
2015-11-18 08:46:31 +00:00
* \ param tu_boundary whether the edge is a TU boundary
2013-09-19 12:29:39 +00:00
*/
2015-11-10 13:38:20 +00:00
static void filter_deblock_edge_luma ( encoder_state_t * const state ,
int32_t x ,
int32_t y ,
int32_t length ,
2015-11-18 08:46:31 +00:00
edge_dir dir ,
bool tu_boundary )
2013-04-03 08:05:07 +00:00
{
2015-04-23 12:12:48 +00:00
videoframe_t * const frame = state - > tile - > frame ;
2015-03-04 15:00:23 +00:00
const encoder_control_t * const encoder = state - > encoder_control ;
2021-04-19 09:46:12 +00:00
2013-04-03 08:05:07 +00:00
{
2014-06-12 05:50:24 +00:00
int32_t stride = frame - > rec - > stride ;
2017-02-06 11:00:25 +00:00
int32_t beta_offset_div2 = encoder - > cfg . deblock_beta ;
int32_t tc_offset_div2 = encoder - > cfg . deblock_tc ;
2014-03-21 08:50:47 +00:00
// TODO: support 10+bits
2022-04-28 11:18:09 +00:00
uvg_pixel * orig_src = & frame - > rec - > y [ x + y * stride ] ;
uvg_pixel * src = orig_src ;
2014-03-21 08:50:47 +00:00
2016-09-27 11:39:37 +00:00
const int32_t qp = get_qp_y_pred ( state , x , y , dir ) ;
2020-12-04 13:54:40 +00:00
const int MAX_QP = 63 ; //TODO: Make DEFAULT_INTRA_TC_OFFSET(=2) a define?
const int8_t lumaBitdepth = encoder - > bitdepth ;
2015-11-10 13:25:25 +00:00
int8_t strength = 0 ;
2020-12-04 13:54:40 +00:00
int32_t bitdepth_scale = 1 < < ( lumaBitdepth - 8 ) ;
int32_t b_index = CLIP ( 0 , MAX_QP , qp + ( beta_offset_div2 < < 1 ) ) ;
2022-04-28 11:18:09 +00:00
int32_t beta = uvg_g_beta_table_8x8 [ b_index ] * bitdepth_scale ;
2013-09-19 12:29:39 +00:00
int32_t side_threshold = ( beta + ( beta > > 1 ) ) > > 3 ;
2015-11-10 13:25:25 +00:00
int32_t tc_index ;
int32_t tc ;
2014-03-21 08:50:47 +00:00
2021-12-29 15:26:00 +00:00
//Deblock adapted to halve pixel mvd.
const int16_t mvdThreashold = 1 < < ( INTERNAL_MV_PREC - 1 ) ;
2020-12-04 13:54:40 +00:00
2015-11-10 13:25:25 +00:00
uint32_t num_4px_parts = length / 4 ;
2017-01-17 19:31:48 +00:00
// Transpose the image by swapping x and y strides when doing horizontal
// edges.
const int32_t x_stride = ( dir = = EDGE_VER ) ? 1 : stride ;
const int32_t y_stride = ( dir = = EDGE_VER ) ? stride : 1 ;
2014-03-21 08:50:47 +00:00
2014-02-21 13:00:20 +00:00
// TODO: add CU based QP calculation
2013-05-29 09:13:47 +00:00
2013-09-19 12:29:39 +00:00
// For each 4-pixel part in the edge
2015-11-10 13:25:25 +00:00
for ( uint32_t block_idx = 0 ; block_idx < num_4px_parts ; + + block_idx ) {
2020-12-21 16:47:02 +00:00
// CUs on both sides of the edge
cu_info_t * cu_p ;
cu_info_t * cu_q ;
2020-12-22 12:06:20 +00:00
int32_t y_coord = y ;
int32_t x_coord = x ;
2013-10-08 09:12:04 +00:00
{
2016-01-12 07:21:00 +00:00
if ( dir = = EDGE_VER ) {
2020-12-22 12:06:20 +00:00
y_coord = y + 4 * block_idx ;
2022-04-28 11:18:09 +00:00
cu_p = uvg_cu_array_at ( frame - > cu_array , x - 1 , y_coord ) ;
cu_q = uvg_cu_array_at ( frame - > cu_array , x , y_coord ) ;
2016-06-16 11:13:44 +00:00
2016-01-12 07:21:00 +00:00
} else {
2020-12-22 12:06:20 +00:00
x_coord = x + 4 * block_idx ;
2022-04-28 11:18:09 +00:00
cu_p = uvg_cu_array_at ( frame - > cu_array , x_coord , y - 1 ) ;
cu_q = uvg_cu_array_at ( frame - > cu_array , x_coord , y ) ;
2016-01-12 07:21:00 +00:00
}
2021-12-29 15:26:00 +00:00
2022-09-14 08:47:26 +00:00
bool nonzero_coeffs = cbf_is_set ( cu_q - > cbf , COLOR_Y )
| | cbf_is_set ( cu_p - > cbf , COLOR_Y ) ;
2015-11-18 08:46:31 +00:00
2013-10-08 09:12:04 +00:00
// Filter strength
2014-02-21 13:00:20 +00:00
strength = 0 ;
2021-12-29 15:26:00 +00:00
if ( cu_q - > type = = CU_INTRA | | cu_p - > type = = CU_INTRA ) { // Intra is used
2014-02-21 13:00:20 +00:00
strength = 2 ;
2021-12-29 15:26:00 +00:00
}
else if ( tu_boundary & & nonzero_coeffs ) {
2013-10-17 12:14:22 +00:00
// Non-zero residual/coeffs and transform boundary
2014-02-21 13:00:20 +00:00
strength = 1 ;
2013-10-09 13:59:55 +00:00
}
2022-04-28 11:18:09 +00:00
else if ( cu_p - > inter . mv_dir = = 3 | | cu_q - > inter . mv_dir = = 3 | | state - > frame - > slicetype = = UVG_SLICE_B ) { // B-slice related checks. TODO: Need to account for cu_p being in another slice?
2015-04-23 06:43:39 +00:00
// Zero all undefined motion vectors for easier usage
if ( ! ( cu_q - > inter . mv_dir & 1 ) ) {
cu_q - > inter . mv [ 0 ] [ 0 ] = 0 ;
cu_q - > inter . mv [ 0 ] [ 1 ] = 0 ;
}
if ( ! ( cu_q - > inter . mv_dir & 2 ) ) {
cu_q - > inter . mv [ 1 ] [ 0 ] = 0 ;
cu_q - > inter . mv [ 1 ] [ 1 ] = 0 ;
}
if ( ! ( cu_p - > inter . mv_dir & 1 ) ) {
cu_p - > inter . mv [ 0 ] [ 0 ] = 0 ;
cu_p - > inter . mv [ 0 ] [ 1 ] = 0 ;
}
if ( ! ( cu_p - > inter . mv_dir & 2 ) ) {
cu_p - > inter . mv [ 1 ] [ 0 ] = 0 ;
cu_p - > inter . mv [ 1 ] [ 1 ] = 0 ;
}
2017-06-26 12:31:57 +00:00
const int refP0 = ( cu_p - > inter . mv_dir & 1 ) ? state - > frame - > ref_LX [ 0 ] [ cu_p - > inter . mv_ref [ 0 ] ] : - 1 ;
const int refP1 = ( cu_p - > inter . mv_dir & 2 ) ? state - > frame - > ref_LX [ 1 ] [ cu_p - > inter . mv_ref [ 1 ] ] : - 1 ;
const int refQ0 = ( cu_q - > inter . mv_dir & 1 ) ? state - > frame - > ref_LX [ 0 ] [ cu_q - > inter . mv_ref [ 0 ] ] : - 1 ;
const int refQ1 = ( cu_q - > inter . mv_dir & 2 ) ? state - > frame - > ref_LX [ 1 ] [ cu_q - > inter . mv_ref [ 1 ] ] : - 1 ;
2021-11-22 08:38:18 +00:00
const mv_t * mvQ0 = cu_q - > inter . mv [ 0 ] ;
const mv_t * mvQ1 = cu_q - > inter . mv [ 1 ] ;
2015-04-23 06:43:39 +00:00
2021-11-22 08:38:18 +00:00
const mv_t * mvP0 = cu_p - > inter . mv [ 0 ] ;
const mv_t * mvP1 = cu_p - > inter . mv [ 1 ] ;
2015-04-23 06:43:39 +00:00
if ( ( refP0 = = refQ0 & & refP1 = = refQ1 ) | | ( refP0 = = refQ1 & & refP1 = = refQ0 ) )
{
// Different L0 & L1
if ( refP0 ! = refP1 ) {
if ( refP0 = = refQ0 ) {
2020-12-04 13:54:40 +00:00
strength = ( ( abs ( mvQ0 [ 0 ] - mvP0 [ 0 ] ) > = mvdThreashold ) | |
( abs ( mvQ0 [ 1 ] - mvP0 [ 1 ] ) > = mvdThreashold ) | |
( abs ( mvQ1 [ 0 ] - mvP1 [ 0 ] ) > = mvdThreashold ) | |
( abs ( mvQ1 [ 1 ] - mvP1 [ 1 ] ) > = mvdThreashold ) ) ? 1 : 0 ;
2015-04-23 06:43:39 +00:00
} else {
2020-12-04 13:54:40 +00:00
strength = ( ( abs ( mvQ1 [ 0 ] - mvP0 [ 0 ] ) > = mvdThreashold ) | |
( abs ( mvQ1 [ 1 ] - mvP0 [ 1 ] ) > = mvdThreashold ) | |
( abs ( mvQ0 [ 0 ] - mvP1 [ 0 ] ) > = mvdThreashold ) | |
( abs ( mvQ0 [ 1 ] - mvP1 [ 1 ] ) > = mvdThreashold ) ) ? 1 : 0 ;
2015-04-23 06:43:39 +00:00
}
// Same L0 & L1
} else {
2020-12-04 13:54:40 +00:00
strength = ( ( abs ( mvQ0 [ 0 ] - mvP0 [ 0 ] ) > = mvdThreashold ) | |
( abs ( mvQ0 [ 1 ] - mvP0 [ 1 ] ) > = mvdThreashold ) | |
( abs ( mvQ1 [ 0 ] - mvP1 [ 0 ] ) > = mvdThreashold ) | |
( abs ( mvQ1 [ 1 ] - mvP1 [ 1 ] ) > = mvdThreashold ) ) & &
( ( abs ( mvQ1 [ 0 ] - mvP0 [ 0 ] ) > = mvdThreashold ) | |
( abs ( mvQ1 [ 1 ] - mvP0 [ 1 ] ) > = mvdThreashold ) | |
( abs ( mvQ0 [ 0 ] - mvP1 [ 0 ] ) > = mvdThreashold ) | |
( abs ( mvQ0 [ 1 ] - mvP1 [ 1 ] ) > = mvdThreashold ) ) ? 1 : 0 ;
2015-04-23 06:43:39 +00:00
}
} else {
strength = 1 ;
}
}
2021-12-29 15:26:00 +00:00
else /*if (cu_p->inter.mv_dir != 3 && cu_q->inter.mv_dir != 3)*/ { //is P-slice
if ( cu_q - > inter . mv_ref [ cu_q - > inter . mv_dir - 1 ] ! = cu_p - > inter . mv_ref [ cu_p - > inter . mv_dir - 1 ] ) {
// Reference pictures are different
strength = 1 ;
} else if (
( ( abs ( cu_q - > inter . mv [ cu_q - > inter . mv_dir - 1 ] [ 0 ] - cu_p - > inter . mv [ cu_p - > inter . mv_dir - 1 ] [ 0 ] ) > = mvdThreashold ) | |
( abs ( cu_q - > inter . mv [ cu_q - > inter . mv_dir - 1 ] [ 1 ] - cu_p - > inter . mv [ cu_p - > inter . mv_dir - 1 ] [ 1 ] ) > = mvdThreashold ) ) ) {
// Absolute motion vector diff between blocks >= 0.5 (Integer pixel)
strength = 1 ;
}
}
2020-12-04 13:54:40 +00:00
tc_index = CLIP ( 0 , MAX_QP + 2 , ( int32_t ) ( qp + 2 * ( strength - 1 ) + ( tc_offset_div2 < < 1 ) ) ) ;
2022-04-28 11:18:09 +00:00
tc = lumaBitdepth < 10 ? ( ( uvg_g_tc_table_8x8 [ tc_index ] + ( 1 < < ( 9 - lumaBitdepth ) ) ) > > ( 10 - lumaBitdepth ) )
: ( ( uvg_g_tc_table_8x8 [ tc_index ] < < ( lumaBitdepth - 10 ) ) ) ;
2013-10-08 09:12:04 +00:00
}
2017-01-17 16:47:38 +00:00
if ( strength = = 0 ) continue ;
2020-12-07 16:54:43 +00:00
bool is_side_P_large = false ;
bool is_side_Q_large = false ;
2020-12-21 16:47:02 +00:00
uint8_t max_filter_length_P = 0 ;
uint8_t max_filter_length_Q = 0 ;
2022-09-14 08:29:47 +00:00
const int cu_width = 1 < < cu_q - > log2_width ;
const int cu_height = 1 < < cu_q - > log2_height ;
const int pu_size = dir = = EDGE_HOR ? cu_height : cu_width ;
const int pu_pos = dir = = EDGE_HOR ? y_coord : x_coord ;
2022-12-20 09:25:58 +00:00
int tu_size_q_side = 0 ;
if ( cu_q - > type = = CU_INTRA & & cu_q - > intra . isp_mode ! = ISP_MODE_NO_ISP ) {
if ( cu_q - > intra . isp_mode = = ISP_MODE_VER & & dir = = EDGE_VER ) {
tu_size_q_side = MAX ( 4 , cu_width > > 2 ) ;
2022-12-21 11:45:56 +00:00
} else if ( cu_q - > intra . isp_mode = = ISP_MODE_HOR & & dir = = EDGE_HOR ) {
tu_size_q_side = MAX ( 4 , cu_height > > 2 ) ;
2022-12-20 09:25:58 +00:00
} else {
tu_size_q_side = dir = = EDGE_HOR ?
MIN ( 1 < < cu_q - > log2_height , TR_MAX_WIDTH ) :
MIN ( 1 < < cu_q - > log2_width , TR_MAX_WIDTH ) ;
}
} else {
tu_size_q_side = dir = = EDGE_HOR ?
MIN ( 1 < < cu_q - > log2_height , TR_MAX_WIDTH ) :
MIN ( 1 < < cu_q - > log2_width , TR_MAX_WIDTH ) ;
}
2022-09-14 08:29:47 +00:00
2022-12-20 09:25:58 +00:00
int tu_size_p_side = 0 ;
if ( cu_p - > type = = CU_INTRA & & cu_p - > intra . isp_mode ! = ISP_MODE_NO_ISP ) {
if ( cu_p - > intra . isp_mode = = ISP_MODE_VER & & dir = = EDGE_VER ) {
tu_size_p_side = MAX ( 4 , ( 1 < < cu_p - > log2_width ) > > 2 ) ;
2022-12-21 11:45:56 +00:00
} else if ( cu_p - > intra . isp_mode = = ISP_MODE_HOR & & dir = = EDGE_HOR ) {
tu_size_p_side = MAX ( 4 , ( 1 < < cu_p - > log2_height ) > > 2 ) ;
2022-12-20 09:25:58 +00:00
} else {
tu_size_p_side = dir = = EDGE_HOR ?
MIN ( 1 < < cu_p - > log2_height , TR_MAX_WIDTH ) :
MIN ( 1 < < cu_p - > log2_width , TR_MAX_WIDTH ) ;
}
} else {
tu_size_p_side = dir = = EDGE_HOR ?
MIN ( 1 < < cu_p - > log2_height , TR_MAX_WIDTH ) :
MIN ( 1 < < cu_p - > log2_width , TR_MAX_WIDTH ) ;
}
2022-09-14 08:29:47 +00:00
2020-12-28 12:26:36 +00:00
get_max_filter_length ( & max_filter_length_P , & max_filter_length_Q , state , x_coord , y_coord ,
2022-06-30 09:24:53 +00:00
dir , tu_boundary ,
2022-09-14 08:29:47 +00:00
tu_size_p_side ,
tu_size_q_side ,
2022-06-30 09:24:53 +00:00
pu_pos , pu_size , cu_q - > merged , COLOR_Y ,
UVG_LUMA_T ) ;
2020-12-07 16:54:43 +00:00
if ( max_filter_length_P > 3 ) {
is_side_P_large = dir = = EDGE_HOR & & y % LCU_WIDTH = = 0 ? false : true ;
//TODO: Add affine/ATMVP related stuff
/*if (max_filter_length_P > 5 && cu_p->affine) {
max_filter_length_P = MIN ( max_filter_length_P , 5 ) ;
} */
}
if ( max_filter_length_Q > 3 ) {
is_side_Q_large = true ;
}
2020-12-31 17:23:33 +00:00
// +-- edge_src
// v
// line0 p7 p6 p5 p4 p3 p2 p1 p0 q0 q1 q2 q3 q4 q5 q6 q7
2022-04-28 11:18:09 +00:00
uvg_pixel * edge_src = & src [ block_idx * 4 * y_stride ] ;
2017-01-17 19:31:48 +00:00
// Gather the lines of pixels required for the filter on/off decision.
2020-12-07 16:54:43 +00:00
//TODO: May need to limit reach in small blocks?
2022-04-28 11:18:09 +00:00
uvg_pixel b [ 4 ] [ 8 ] ;
2017-01-17 19:31:48 +00:00
gather_deblock_pixels ( edge_src , x_stride , 0 * y_stride , 4 , & b [ 0 ] [ 0 ] ) ;
gather_deblock_pixels ( edge_src , x_stride , 3 * y_stride , 4 , & b [ 3 ] [ 0 ] ) ;
2017-01-17 16:47:38 +00:00
int_fast32_t dp0 = abs ( b [ 0 ] [ 1 ] - 2 * b [ 0 ] [ 2 ] + b [ 0 ] [ 3 ] ) ;
int_fast32_t dq0 = abs ( b [ 0 ] [ 4 ] - 2 * b [ 0 ] [ 5 ] + b [ 0 ] [ 6 ] ) ;
2017-01-17 19:31:48 +00:00
int_fast32_t dp3 = abs ( b [ 3 ] [ 1 ] - 2 * b [ 3 ] [ 2 ] + b [ 3 ] [ 3 ] ) ;
int_fast32_t dq3 = abs ( b [ 3 ] [ 4 ] - 2 * b [ 3 ] [ 5 ] + b [ 3 ] [ 6 ] ) ;
2017-01-17 16:47:38 +00:00
int_fast32_t dp = dp0 + dp3 ;
int_fast32_t dq = dq0 + dq3 ;
2020-12-07 16:54:43 +00:00
bool sw = false ;
2017-01-17 19:31:48 +00:00
2020-12-07 16:54:43 +00:00
if ( is_side_P_large | | is_side_Q_large ) {
int_fast32_t dp0L = dp0 ;
int_fast32_t dq0L = dq0 ;
int_fast32_t dp3L = dp3 ;
int_fast32_t dq3L = dq3 ;
2021-12-10 17:05:43 +00:00
//In case of large blocks, need to gather extra pixels
2020-12-09 17:10:38 +00:00
//bL:
2020-12-08 16:38:05 +00:00
//line0 p7 p6 p5 p4 q4 q5 q6 q7
2022-04-28 11:18:09 +00:00
uvg_pixel bL [ 4 ] [ 8 ] ;
2020-12-09 17:10:38 +00:00
2020-12-07 16:54:43 +00:00
if ( is_side_P_large ) {
2021-12-10 17:05:43 +00:00
gather_pixels ( edge_src - 8 * x_stride , x_stride , 0 * y_stride , 4 , & bL [ 0 ] [ 0 ] ) ;
gather_pixels ( edge_src - 8 * x_stride , x_stride , 3 * y_stride , 4 , & bL [ 3 ] [ 0 ] ) ;
2020-12-31 17:23:33 +00:00
dp0L = ( dp0L + abs ( bL [ 0 ] [ 2 ] - 2 * bL [ 0 ] [ 3 ] + b [ 0 ] [ 0 ] ) + 1 ) > > 1 ;
dp3L = ( dp3L + abs ( bL [ 3 ] [ 2 ] - 2 * bL [ 3 ] [ 3 ] + b [ 3 ] [ 0 ] ) + 1 ) > > 1 ;
2020-12-07 16:54:43 +00:00
}
if ( is_side_Q_large ) {
2021-12-10 17:05:43 +00:00
gather_pixels ( edge_src + 4 * x_stride , x_stride , 0 * y_stride , 4 , & bL [ 0 ] [ 4 ] ) ;
gather_pixels ( edge_src + 4 * x_stride , x_stride , 3 * y_stride , 4 , & bL [ 3 ] [ 4 ] ) ;
2020-12-31 17:23:33 +00:00
dq0L = ( dq0L + abs ( b [ 0 ] [ 7 ] - 2 * bL [ 0 ] [ 4 ] + bL [ 0 ] [ 5 ] ) + 1 ) > > 1 ;
dq3L = ( dq3L + abs ( b [ 3 ] [ 7 ] - 2 * bL [ 3 ] [ 4 ] + bL [ 3 ] [ 5 ] ) + 1 ) > > 1 ;
2020-12-07 16:54:43 +00:00
}
int_fast32_t dpL = dp0L + dp3L ;
int_fast32_t dqL = dq0L + dq3L ;
2013-09-19 12:29:39 +00:00
2020-12-07 16:54:43 +00:00
if ( dpL + dqL < beta ) {
2020-12-09 17:10:38 +00:00
sw = use_strong_filtering ( & b [ 0 ] [ 0 ] , & b [ 3 ] [ 0 ] , & bL [ 0 ] [ 0 ] , & bL [ 3 ] [ 0 ] ,
2020-12-08 16:38:05 +00:00
dp0L , dq0L , dp3L , dq3L , tc , beta ,
2020-12-07 16:54:43 +00:00
is_side_P_large , is_side_Q_large ,
2020-12-15 12:10:09 +00:00
max_filter_length_P , max_filter_length_Q , false ) ;
2017-01-17 19:31:48 +00:00
if ( sw ) {
2020-12-09 17:10:38 +00:00
gather_deblock_pixels ( edge_src , x_stride , 1 * y_stride , 4 , & b [ 1 ] [ 0 ] ) ;
gather_deblock_pixels ( edge_src , x_stride , 2 * y_stride , 4 , & b [ 2 ] [ 0 ] ) ;
if ( is_side_P_large )
{
2021-12-10 17:05:43 +00:00
gather_pixels ( edge_src - 8 * x_stride , x_stride , 1 * y_stride , 4 , & bL [ 1 ] [ 0 ] ) ;
gather_pixels ( edge_src - 8 * x_stride , x_stride , 2 * y_stride , 4 , & bL [ 2 ] [ 0 ] ) ;
2020-12-09 17:10:38 +00:00
}
if ( is_side_Q_large )
{
2021-12-10 17:05:43 +00:00
gather_pixels ( edge_src + 4 * x_stride , x_stride , 1 * y_stride , 4 , & bL [ 1 ] [ 4 ] ) ;
gather_pixels ( edge_src + 4 * x_stride , x_stride , 2 * y_stride , 4 , & bL [ 2 ] [ 4 ] ) ;
2020-12-09 17:10:38 +00:00
}
2020-12-07 16:54:43 +00:00
for ( int i = 0 ; i < 4 ; + + i ) {
int filter_reach ;
2022-04-28 11:18:09 +00:00
filter_reach = uvg_filter_deblock_large_block ( & b [ i ] [ 0 ] , & bL [ i ] [ 0 ] , tc ,
2020-12-09 17:10:38 +00:00
is_side_P_large ? max_filter_length_P : 3 ,
is_side_Q_large ? max_filter_length_Q : 3 ) ;
2020-12-07 16:54:43 +00:00
scatter_deblock_pixels ( & b [ i ] [ 0 ] , x_stride , i * y_stride , filter_reach , edge_src ) ;
2020-12-09 17:10:38 +00:00
if ( is_side_P_large ) {
const int diff_reach = ( max_filter_length_P - filter_reach ) > > 1 ;
2020-12-31 17:23:33 +00:00
const int dst_offset = ( filter_reach + diff_reach ) * x_stride ;
scatter_deblock_pixels ( & bL [ i ] [ 0 ] - diff_reach , x_stride , i * y_stride , diff_reach , edge_src - dst_offset ) ;
2020-12-09 17:10:38 +00:00
}
if ( is_side_Q_large ) {
2020-12-31 17:23:33 +00:00
const int diff_reach = ( max_filter_length_Q - filter_reach ) > > 1 ;
const int dst_offset = ( filter_reach + diff_reach ) * x_stride ;
scatter_deblock_pixels ( & bL [ i ] [ 0 ] + diff_reach , x_stride , i * y_stride , diff_reach , edge_src + dst_offset ) ;
2020-12-09 17:10:38 +00:00
}
2020-12-07 16:54:43 +00:00
}
}
}
}
if ( ! sw )
{
if ( dp + dq < beta ) {
if ( max_filter_length_P > 2 & & max_filter_length_Q > 2 ) {
// Strong filtering flag checking.
2020-12-08 16:38:05 +00:00
sw = use_strong_filtering ( b [ 0 ] , b [ 3 ] , NULL , NULL ,
dp0 , dq0 , dp3 , dq3 , tc , beta ,
2020-12-22 12:06:20 +00:00
false , false , 7 , 7 , false ) ;
2020-12-07 16:54:43 +00:00
}
// Read lines 1 and 2. Weak filtering doesn't use the outermost pixels
// but let's give them anyway to simplify control flow.
gather_deblock_pixels ( edge_src , x_stride , 1 * y_stride , 4 , & b [ 1 ] [ 0 ] ) ;
gather_deblock_pixels ( edge_src , x_stride , 2 * y_stride , 4 , & b [ 2 ] [ 0 ] ) ;
for ( int i = 0 ; i < 4 ; + + i ) {
int filter_reach ;
if ( sw ) {
2022-04-28 11:18:09 +00:00
filter_reach = uvg_filter_deblock_luma_strong ( & b [ i ] [ 0 ] , tc ) ;
2020-12-07 16:54:43 +00:00
} else {
bool p_2nd = false ;
bool q_2nd = false ;
if ( max_filter_length_P > 1 & & max_filter_length_Q > 1 ) {
p_2nd = dp < side_threshold ;
q_2nd = dq < side_threshold ;
}
2022-04-28 11:18:09 +00:00
filter_reach = uvg_filter_deblock_luma_weak ( encoder , & b [ i ] [ 0 ] , tc , p_2nd , q_2nd ) ;
2020-12-07 16:54:43 +00:00
}
scatter_deblock_pixels ( & b [ i ] [ 0 ] , x_stride , i * y_stride , filter_reach , edge_src ) ;
2017-01-17 19:31:48 +00:00
}
2015-11-18 08:57:19 +00:00
}
2013-04-03 08:05:07 +00:00
}
}
}
2013-04-04 12:08:28 +00:00
}
2013-09-19 12:29:39 +00:00
/**
2015-11-10 13:25:25 +00:00
* \ brief Apply the deblocking filter to chroma pixels on a single edge .
*
* The caller should check that the edge is a TU boundary or a PU boundary .
*
\ verbatim
. - - filter this edge if dir = = EDGE_HOR
v
+ - - - - - - - - +
| o < - - pixel at ( x , y )
| |
| < - - filter this edge if dir = = EDGE_VER
| |
+ - - - - - - - - +
\ endverbatim
*
2015-11-18 08:46:31 +00:00
* \ param state encoder state
* \ param x x - coordinate in chroma pixels ( see above )
* \ param y y - coordinate in chroma pixels ( see above )
* \ param length length of the edge in chroma pixels
* \ param dir direction of the edge to filter
* \ param tu_boundary whether the edge is a TU boundary
2013-09-19 12:29:39 +00:00
*/
2015-11-10 13:38:20 +00:00
static void filter_deblock_edge_chroma ( encoder_state_t * const state ,
int32_t x ,
int32_t y ,
int32_t length ,
2015-11-18 08:46:31 +00:00
edge_dir dir ,
2022-06-30 09:24:53 +00:00
bool tu_boundary ,
enum uvg_tree_type tree_type )
2013-04-04 12:08:28 +00:00
{
2015-03-04 15:00:23 +00:00
const encoder_control_t * const encoder = state - > encoder_control ;
const videoframe_t * const frame = state - > tile - > frame ;
2021-04-19 09:46:12 +00:00
2013-04-04 12:08:28 +00:00
// For each subpart
{
2014-06-12 05:50:24 +00:00
int32_t stride = frame - > rec - > stride > > 1 ;
2017-02-06 11:00:25 +00:00
int32_t tc_offset_div2 = encoder - > cfg . deblock_tc ;
2020-12-15 12:10:09 +00:00
int32_t beta_offset_div2 = encoder - > cfg . deblock_beta ;
2014-03-21 08:50:47 +00:00
// TODO: support 10+bits
2022-04-28 11:18:09 +00:00
uvg_pixel * src [ ] = {
2015-11-18 08:57:19 +00:00
& frame - > rec - > u [ x + y * stride ] ,
& frame - > rec - > v [ x + y * stride ] ,
} ;
2020-12-15 12:10:09 +00:00
const uint8_t MAX_QP = 63 ;
2014-03-21 08:50:47 +00:00
2016-09-27 11:39:37 +00:00
const int32_t luma_qp = get_qp_y_pred ( state , x < < 1 , y < < 1 , dir ) ;
2022-04-28 11:18:09 +00:00
int32_t QP = uvg_get_scaled_qp ( 1 , luma_qp , 0 , state - > encoder_control - > qp_map [ 0 ] ) ; //uvg_g_chroma_scale[luma_qp]; //TODO: Add BDOffset?
2020-12-15 12:10:09 +00:00
int32_t bitdepth_scale = 1 < < ( encoder - > bitdepth - 8 ) ;
2021-01-02 18:59:55 +00:00
//TU size should be in chroma samples (?)
2022-04-28 11:18:09 +00:00
const int chroma_shift = dir = = EDGE_HOR ? ( encoder - > chroma_format = = UVG_CSP_420 ? 1 : 0 )
: ( encoder - > chroma_format ! = UVG_CSP_444 ? 1 : 0 ) ;
2021-01-02 18:59:55 +00:00
//TODO: Replace two (2) with min CU log2 size when its updated to the correct value
2022-04-28 11:18:09 +00:00
const int min_chroma_width_log2 = 2 - ( encoder - > chroma_format = = UVG_CSP_420 ? 1 : 0 ) ;
const int min_chroma_height_log2 = 2 - ( encoder - > chroma_format ! = UVG_CSP_444 ? 1 : 0 ) ;
2021-01-02 18:59:55 +00:00
const int min_chroma_size_log2 = dir = = EDGE_HOR ? min_chroma_height_log2 : min_chroma_width_log2 ;
const int min_chroma_length = 1 < < min_chroma_size_log2 ;
const uint32_t num_parts = ( length > > min_chroma_size_log2 ) ;
2015-11-10 13:25:25 +00:00
const int32_t offset = ( dir = = EDGE_HOR ) ? stride : 1 ;
const int32_t step = ( dir = = EDGE_HOR ) ? 1 : stride ;
2014-03-21 08:50:47 +00:00
2021-01-02 18:59:55 +00:00
for ( uint32_t blk_idx = 0 ; blk_idx < num_parts ; + + blk_idx )
2014-02-21 13:00:20 +00:00
{
2016-06-16 11:13:44 +00:00
// CUs on both sides of the edge
cu_info_t * cu_p ;
cu_info_t * cu_q ;
2023-02-22 12:48:00 +00:00
int32_t x_coord = x < < 1 ;
int32_t y_coord = y < < 1 ;
2022-06-30 09:24:53 +00:00
cu_array_t * cua = tree_type ! = UVG_CHROMA_T ? frame - > cu_array : frame - > chroma_cu_array ;
2016-01-12 07:21:00 +00:00
if ( dir = = EDGE_VER ) {
2023-02-23 06:48:08 +00:00
y_coord = ( y + min_chroma_length * blk_idx ) < < ( 1 ) ;
2022-06-30 09:24:53 +00:00
cu_p = uvg_cu_array_at ( cua , x_coord - 1 , y_coord ) ;
cu_q = uvg_cu_array_at ( cua , x_coord , y_coord ) ;
2016-06-16 11:13:44 +00:00
2016-01-12 07:21:00 +00:00
} else {
2023-02-23 06:48:08 +00:00
x_coord = ( x + min_chroma_length * blk_idx ) < < ( 1 ) ;
2022-06-30 09:24:53 +00:00
cu_p = uvg_cu_array_at ( cua , x_coord , y_coord - 1 ) ;
cu_q = uvg_cu_array_at ( cua , x_coord , y_coord ) ;
2016-01-12 07:21:00 +00:00
}
2022-09-14 08:29:47 +00:00
2020-12-28 13:20:30 +00:00
uint8_t max_filter_length_P = 0 ;
uint8_t max_filter_length_Q = 0 ;
2021-01-02 18:59:55 +00:00
2022-12-13 12:51:38 +00:00
const int cu_width = 1 < < ( cu_q - > log2_chroma_width ) ;
const int cu_height = 1 < < ( cu_q - > log2_chroma_height ) ;
2022-09-14 08:29:47 +00:00
const int pu_size = dir = = EDGE_HOR ? cu_height : cu_width ;
const int pu_pos = dir = = EDGE_HOR ? y_coord : x_coord ;
const int tu_size_p_side = dir = = EDGE_HOR ?
2022-12-13 12:51:38 +00:00
MIN ( 1 < < ( cu_p - > log2_chroma_height ) , TR_MAX_WIDTH ) :
MIN ( 1 < < ( cu_p - > log2_chroma_width ) , TR_MAX_WIDTH ) ;
2022-09-14 08:29:47 +00:00
const int tu_size_q_side = dir = = EDGE_HOR ?
2022-12-13 12:51:38 +00:00
MIN ( 1 < < ( cu_q - > log2_chroma_height ) , TR_MAX_WIDTH ) :
MIN ( 1 < < ( cu_q - > log2_chroma_width ) , TR_MAX_WIDTH ) ;
2022-09-14 08:29:47 +00:00
2020-12-28 12:26:36 +00:00
get_max_filter_length ( & max_filter_length_P , & max_filter_length_Q , state , x_coord , y_coord ,
2022-09-14 08:29:47 +00:00
dir , tu_boundary , tu_size_p_side , tu_size_q_side ,
2022-06-30 09:24:53 +00:00
pu_pos , pu_size , cu_q - > merged , COLOR_U ,
tree_type ) ;
2020-12-28 13:20:30 +00:00
2020-12-28 12:26:36 +00:00
2020-12-15 12:10:09 +00:00
const bool large_boundary = ( max_filter_length_P > = 3 & & max_filter_length_Q > = 3 ) ;
2023-02-23 06:48:08 +00:00
const bool is_chroma_hor_CTB_boundary = ( dir = = EDGE_HOR & & y_coord % LCU_WIDTH = = 0 ) ;
2020-12-15 12:10:09 +00:00
uint8_t c_strength [ 2 ] = { 0 , 0 } ;
2021-01-06 17:06:14 +00:00
2020-12-15 12:10:09 +00:00
2013-11-12 10:00:30 +00:00
if ( cu_q - > type = = CU_INTRA | | cu_p - > type = = CU_INTRA ) {
2020-12-15 12:10:09 +00:00
c_strength [ 0 ] = 2 ;
c_strength [ 1 ] = 2 ;
}
else if ( tu_boundary ) { //TODO: Add ciip/IBC related stuff
2022-09-14 08:47:26 +00:00
bool nonzero_coeffs_U = cbf_is_set ( cu_q - > cbf , COLOR_U )
| | cbf_is_set ( cu_p - > cbf , COLOR_U ) ;
bool nonzero_coeffs_V = cbf_is_set ( cu_q - > cbf , COLOR_V )
| | cbf_is_set ( cu_p - > cbf , COLOR_V ) ;
2020-12-15 12:10:09 +00:00
c_strength [ 0 ] = nonzero_coeffs_U ? 1 : 0 ;
c_strength [ 1 ] = nonzero_coeffs_V ? 1 : 0 ;
}
for ( int component = 0 ; component < 2 ; component + + ) {
if ( c_strength [ component ] = = 2 | | ( large_boundary & & c_strength [ component ] = = 1 ) ) {
2021-01-02 18:59:55 +00:00
int32_t TC_index = CLIP ( 0 , MAX_QP + 2 , ( int32_t ) ( QP + 2 * ( c_strength [ component ] - 1 ) + ( tc_offset_div2 < < 1 ) ) ) ;
2022-04-28 11:18:09 +00:00
int32_t Tc = encoder - > bitdepth < 10 ? ( ( uvg_g_tc_table_8x8 [ TC_index ] + ( 1 < < ( 9 - encoder - > bitdepth ) ) ) > > ( 10 - encoder - > bitdepth ) )
: ( uvg_g_tc_table_8x8 [ TC_index ] < < ( encoder - > bitdepth - 10 ) ) ;
2021-01-02 18:59:55 +00:00
2021-01-06 17:06:14 +00:00
bool use_long_filter = false ;
2021-01-02 18:59:55 +00:00
// +-- edge_src
// v
// line0 p3 p2 p1 p0 q0 q1 q2 q3
2022-04-28 11:18:09 +00:00
uvg_pixel * edge_src = & src [ component ] [ blk_idx * min_chroma_length * step ] ;
2021-01-02 18:59:55 +00:00
2020-12-15 12:10:09 +00:00
if ( large_boundary ) {
const int beta_index = CLIP ( 0 , MAX_QP , QP + ( beta_offset_div2 < < 1 ) ) ;
2022-04-28 11:18:09 +00:00
const int beta = uvg_g_beta_table_8x8 [ beta_index ] * bitdepth_scale ;
2020-12-15 12:10:09 +00:00
2021-01-02 18:59:55 +00:00
2021-01-01 18:10:08 +00:00
const uint8_t sss = chroma_shift = = 1 ? 1 : 3 ;
2020-12-15 12:10:09 +00:00
// Gather the lines of pixels required for the filter on/off decision.
//TODO: May need to limit reach in small blocks?
2022-04-28 11:18:09 +00:00
uvg_pixel b [ 2 ] [ 8 ] ;
2020-12-15 12:10:09 +00:00
gather_deblock_pixels ( edge_src , offset , 0 * step , 4 , & b [ 0 ] [ 0 ] ) ;
gather_deblock_pixels ( edge_src , offset , sss * step , 4 , & b [ 1 ] [ 0 ] ) ;
const uint8_t p_ind = is_chroma_hor_CTB_boundary ? 2 : 1 ;
int_fast32_t dp0 = abs ( b [ 0 ] [ p_ind ] - 2 * b [ 0 ] [ 2 ] + b [ 0 ] [ 3 ] ) ;
int_fast32_t dq0 = abs ( b [ 0 ] [ 4 ] - 2 * b [ 0 ] [ 5 ] + b [ 0 ] [ 6 ] ) ;
int_fast32_t dp3 = abs ( b [ 1 ] [ p_ind ] - 2 * b [ 1 ] [ 2 ] + b [ 1 ] [ 3 ] ) ;
int_fast32_t dq3 = abs ( b [ 1 ] [ 4 ] - 2 * b [ 1 ] [ 5 ] + b [ 1 ] [ 6 ] ) ;
int_fast32_t dp = dp0 + dp3 ;
int_fast32_t dq = dq0 + dq3 ;
if ( dp + dq < beta ) {
use_long_filter = true ;
const bool sw = use_strong_filtering ( b [ 0 ] , b [ 1 ] , NULL , NULL ,
dp0 , dq0 , dp3 , dq3 , Tc , beta ,
false , false , 7 , 7 , is_chroma_hor_CTB_boundary ) ;
2021-01-02 18:59:55 +00:00
for ( int i = 0 ; i < min_chroma_length ; i + + ) {
2022-04-28 11:18:09 +00:00
uvg_filter_deblock_chroma ( encoder , edge_src + step * i , offset , Tc , 0 , 0 ,
2020-12-18 09:06:41 +00:00
sw , large_boundary , is_chroma_hor_CTB_boundary ) ;
2020-12-15 12:10:09 +00:00
}
}
}
if ( ! use_long_filter )
{
2021-01-02 18:59:55 +00:00
for ( int i = 0 ; i < min_chroma_length ; i + + ) {
2022-04-28 11:18:09 +00:00
uvg_filter_deblock_chroma ( encoder , edge_src + step * i , offset , Tc , 0 , 0 ,
2020-12-18 09:06:41 +00:00
false , large_boundary , is_chroma_hor_CTB_boundary ) ;
2020-12-15 12:10:09 +00:00
}
2015-11-18 08:57:19 +00:00
}
}
2013-11-12 10:00:30 +00:00
}
2013-04-04 12:08:28 +00:00
}
}
}
2015-11-18 08:46:31 +00:00
2014-02-21 13:00:20 +00:00
/**
2015-11-18 08:46:31 +00:00
* \ brief Filter edge of a single PU or TU
2015-11-04 09:56:57 +00:00
*
2015-11-18 08:46:31 +00:00
* \ param state encoder state
* \ param x block x - position in pixels
* \ param y block y - position in pixels
* \ param width block width in pixels
* \ param height block height in pixels
2015-11-04 09:56:57 +00:00
* \ param dir direction of the edges to filter
2015-11-18 08:46:31 +00:00
* \ param tu_boundary whether the edge is a TU boundary
2013-09-19 12:29:39 +00:00
*/
2022-06-30 09:24:53 +00:00
static void filter_deblock_unit (
encoder_state_t * const state ,
int x ,
int y ,
int width ,
int height ,
edge_dir dir ,
bool tu_boundary ,
bool previous_ctu ,
enum uvg_tree_type tree_type )
2013-04-04 12:08:28 +00:00
{
2013-09-19 12:29:39 +00:00
// no filtering on borders (where filter would use pixels outside the picture)
2015-11-18 08:46:31 +00:00
if ( x = = 0 & & dir = = EDGE_VER ) return ;
if ( y = = 0 & & dir = = EDGE_HOR ) return ;
2013-06-03 11:22:50 +00:00
2015-11-18 08:46:31 +00:00
// Length of luma and chroma edges.
int32_t length ;
if ( dir = = EDGE_HOR ) {
2021-04-14 05:18:54 +00:00
const videoframe_t * const frame = state - > tile - > frame ;
const int32_t x_right = x + width ;
2021-04-19 09:46:12 +00:00
const bool rightmost_8px_of_lcu = x_right % LCU_WIDTH = = 0 | | x_right % LCU_WIDTH = = LCU_WIDTH - width ;
2021-04-22 05:52:06 +00:00
const bool rightmost_8px_of_frame = x_right = = frame - > width | | x_right + width = = frame - > width ;
2015-11-10 13:25:25 +00:00
2021-04-19 09:46:12 +00:00
if ( rightmost_8px_of_lcu & & ! rightmost_8px_of_frame & & ! previous_ctu ) {
2021-01-13 16:04:56 +00:00
// The last 8 pixels will be deblocked when processing the next LCU.
2021-04-08 08:47:23 +00:00
length = width - 4 ;
2021-01-13 16:04:56 +00:00
if ( length = = 0 ) return ;
2015-11-10 13:25:25 +00:00
} else {
2015-11-18 08:46:31 +00:00
length = width ;
2015-11-10 13:25:25 +00:00
}
2015-11-18 08:46:31 +00:00
} else {
length = height ;
}
2022-06-30 09:24:53 +00:00
if ( tree_type ! = UVG_CHROMA_T ) {
filter_deblock_edge_luma ( state , x , y , length , dir , tu_boundary ) ;
}
2015-11-10 13:25:25 +00:00
2015-11-18 08:46:31 +00:00
// Chroma pixel coordinates.
const int32_t x_c = x > > 1 ;
const int32_t y_c = y > > 1 ;
2022-12-13 12:51:38 +00:00
if ( state - > encoder_control - > chroma_format ! = UVG_CSP_400 & &
is_tu_boundary ( state , x , y , dir , COLOR_UV , tree_type )
& & ( is_on_8x8_grid ( x_c , y_c , dir = = EDGE_HOR & & ( x_c + 4 ) % 32 ? EDGE_HOR : EDGE_VER )
| | ( x = = state - > tile - > frame - > width - 8 & & dir = = EDGE_HOR & & y_c % 8 = = 0 ) )
2022-06-30 09:24:53 +00:00
& & tree_type ! = UVG_LUMA_T ) {
2022-12-13 12:51:38 +00:00
filter_deblock_edge_chroma ( state , x_c , y_c , 2 , dir , tu_boundary , tree_type ) ;
2015-11-18 08:46:31 +00:00
}
}
2015-11-10 13:25:25 +00:00
2015-11-18 08:46:31 +00:00
/**
* \ brief Deblock PU and TU boundaries inside an LCU .
*
* \ param state encoder state
* \ param x_px block x - position in pixels
* \ param y_px block y - position in pixels
* \ param dir direction of the edges to filter
*
* Recursively traverse the CU / TU quadtree . At the lowest level , apply the
* deblocking filter to the left edge ( when dir = = EDGE_VER ) or the top edge
* ( when dir = = EDGE_HOR ) as needed . Both luma and chroma are filtered .
*/
static void filter_deblock_lcu_inside ( encoder_state_t * const state ,
int32_t x ,
int32_t y ,
edge_dir dir )
{
const int end_x = MIN ( x + LCU_WIDTH , state - > tile - > frame - > width ) ;
const int end_y = MIN ( y + LCU_WIDTH , state - > tile - > frame - > height ) ;
2022-06-30 09:24:53 +00:00
const enum uvg_tree_type luma_tree = state - > frame - > is_irap & & state - > encoder_control - > cfg . dual_tree ? UVG_LUMA_T : UVG_BOTH_T ;
const enum uvg_tree_type chroma_tree = state - > frame - > is_irap & & state - > encoder_control - > cfg . dual_tree ? UVG_CHROMA_T : UVG_BOTH_T ;
2021-04-08 08:47:23 +00:00
for ( int edge_y = y ; edge_y < end_y ; edge_y + = 4 ) {
for ( int edge_x = x ; edge_x < end_x ; edge_x + = 4 ) {
2022-12-13 12:51:38 +00:00
bool tu_boundary = is_tu_boundary ( state , edge_x , edge_y , dir , COLOR_Y , luma_tree ) ;
2015-11-18 08:46:31 +00:00
if ( tu_boundary | | is_pu_boundary ( state , edge_x , edge_y , dir ) ) {
2022-06-30 09:24:53 +00:00
filter_deblock_unit ( state , edge_x , edge_y , 4 , 4 , dir , tu_boundary , edge_x < x , luma_tree ) ;
}
2022-12-13 12:51:38 +00:00
if ( chroma_tree = = UVG_CHROMA_T & & is_tu_boundary ( state , edge_x , edge_y , dir , COLOR_UV , chroma_tree ) ) {
2022-06-30 09:24:53 +00:00
filter_deblock_unit ( state , edge_x , edge_y , 4 , 4 , dir , tu_boundary , edge_x < x , chroma_tree ) ;
2015-11-18 08:46:31 +00:00
}
2015-11-10 13:25:25 +00:00
}
}
2013-09-19 12:29:39 +00:00
}
2013-06-03 11:22:50 +00:00
2013-09-23 15:07:16 +00:00
2015-12-11 10:28:39 +00:00
/**
2021-01-13 16:04:56 +00:00
* \ brief Filter rightmost 8 pixels of the horizontal egdes of an LCU .
2015-12-11 10:28:39 +00:00
*
* \ param state encoder state
* \ param x_px x - coordinate of the * right * edge of the LCU in pixels
* \ param y_px y - coordinate of the top edge of the LCU in pixels
*/
static void filter_deblock_lcu_rightmost ( encoder_state_t * const state ,
int32_t x_px ,
int32_t y_px )
{
// Luma
2022-06-30 09:24:53 +00:00
const enum uvg_tree_type luma_tree = state - > frame - > is_irap & & state - > encoder_control - > cfg . dual_tree ? UVG_LUMA_T : UVG_BOTH_T ;
const enum uvg_tree_type chroma_tree = state - > frame - > is_irap & & state - > encoder_control - > cfg . dual_tree ? UVG_CHROMA_T : UVG_BOTH_T ;
2015-12-11 10:28:39 +00:00
const int end = MIN ( y_px + LCU_WIDTH , state - > tile - > frame - > height ) ;
2021-04-19 09:46:12 +00:00
for ( int x = x_px - 8 ; x < x_px ; x + = 4 ) {
for ( int y = y_px ; y < end ; y + = 4 ) {
// The top edge of the whole frame is not filtered.
2022-12-13 12:51:38 +00:00
bool tu_boundary = is_tu_boundary ( state , x , y , EDGE_HOR , COLOR_Y , luma_tree ) ;
2022-06-30 09:24:53 +00:00
if ( y > 0 & & ( tu_boundary | | is_pu_boundary ( state , x , y , EDGE_HOR ) ) ) {
2021-04-19 09:46:12 +00:00
filter_deblock_edge_luma ( state , x , y , 4 , EDGE_HOR , tu_boundary ) ;
}
2015-12-11 10:28:39 +00:00
}
}
// Chroma
2022-04-28 11:18:09 +00:00
if ( state - > encoder_control - > chroma_format ! = UVG_CSP_400 ) {
2016-08-25 13:05:46 +00:00
const int x_px_c = x_px > > 1 ;
const int y_px_c = y_px > > 1 ;
2022-12-13 12:51:38 +00:00
int x_c = x_px_c - 4 ;
const int end_c_y = MIN ( y_px_c + LCU_WIDTH_C , state - > tile - > frame - > height > > 1 ) ;
for ( ; x_c < x_px_c ; x_c + = 2 ) {
for ( int y_c = y_px_c ; y_c < end_c_y ; y_c + = 8 ) {
// The top edge of the whole frame is not filtered.
bool tu_boundary = is_tu_boundary ( state , x_c < < 1 , y_c < < 1 , EDGE_HOR , COLOR_UV , chroma_tree ) ;
if ( y_c > 0 & & ( tu_boundary | | is_pu_boundary ( state , x_c < < 1 , y_c < < 1 , EDGE_HOR ) ) ) {
filter_deblock_edge_chroma ( state , x_c , y_c , 2 , EDGE_HOR , tu_boundary , chroma_tree ) ;
}
2016-08-25 13:05:46 +00:00
}
2015-12-11 10:28:39 +00:00
}
}
}
2014-03-21 09:11:40 +00:00
/**
* \ brief Deblock a single LCU without using data from right or down .
*
2015-11-10 13:25:25 +00:00
* Filter the following vertical edges ( horizontal filtering ) :
* 1. The left edge of the LCU .
* 2. All vertical edges within the LCU .
*
* Filter the following horizontal edges ( vertical filtering ) :
2021-01-13 16:04:56 +00:00
* 1. The rightmost 8 pixels of the top edge of the LCU to the left .
* 2. The rightmost 8 pixels of all horizontal edges within the LCU to the
2015-11-10 13:25:25 +00:00
* left .
* 3. The top edge and all horizontal edges within the LCU , excluding the
2021-01-13 16:04:56 +00:00
* rightmost 8 pixels . If the LCU is the rightmost LCU of the frame , the
* last 8 pixels are also filtered .
2015-11-10 13:25:25 +00:00
*
* What is not filtered :
2021-01-13 16:04:56 +00:00
* - The rightmost 8 pixels of the top edge and all horizontal edges within
2015-11-10 13:25:25 +00:00
* the LCU , unless the LCU is the rightmost LCU of the frame .
* - The bottom edge of the LCU .
* - The right edge of the LCU .
*
* \ param state encoder state
* \ param x_px x - coordinate of the left edge of the LCU in pixels
* \ param y_px y - coordinate of the top edge of the LCU in pixels
2014-03-21 09:11:40 +00:00
*/
2020-12-04 13:54:40 +00:00
//TODO: Things to check/fix for VVC:
// - Strength calculation to include average Luma level (Luma Adaptive Deblocing Filter LADF) (optional)
2020-12-04 16:31:48 +00:00
// - Deblocking strength for CIIP and IBC modes (CIIP/IBC not currently used)
// - Handle new prediction modes (i.e. PLT) (PLT not currently used)
2020-12-01 11:53:52 +00:00
// - Deblocking filter for subblock boundaries
2020-12-04 13:54:40 +00:00
// - Allow loop filtering across slice/tile boundaries?
2022-04-28 11:18:09 +00:00
void uvg_filter_deblock_lcu ( encoder_state_t * const state , int x_px , int y_px )
2014-03-21 09:11:40 +00:00
{
2017-02-05 09:59:21 +00:00
assert ( ! state - > encoder_control - > cfg . lossless ) ;
2015-11-18 08:46:31 +00:00
filter_deblock_lcu_inside ( state , x_px , y_px , EDGE_VER ) ;
2015-11-10 13:25:25 +00:00
if ( x_px > 0 ) {
2015-12-11 10:28:39 +00:00
filter_deblock_lcu_rightmost ( state , x_px , y_px ) ;
2014-03-21 09:11:40 +00:00
}
2015-11-18 08:46:31 +00:00
filter_deblock_lcu_inside ( state , x_px , y_px , EDGE_HOR ) ;
2014-03-21 09:11:40 +00:00
}