From f90307a9d6c97e667633a7400aeddb5038ff4bbb Mon Sep 17 00:00:00 2001 From: Narayan Kalaburgi <100876@ittiam.com> Date: Tue, 11 Aug 2026 16:05:27 +0530 Subject: [PATCH] libhevc: Implement 10-bit 444 Chroma Intra-Prediction This commit adds spatial intra-prediction support for 10-bit 444 chroma blocks during the decoder's prediction stage. Changes include: - Updated ihevc_hbd_chroma_intra_pred_filters.c to add 444 scaling (sub_sample = 2 vs sub_sample = 1). - Added support for 32x32 chroma block predictions (nt == 32), which is the maximum chroma transform unit size in the 444 profile. - Integrated the new filtering logic by updating the CTB intra-prediction loop in ihevcd_iquant_itrans_recon_ctb.c --- common/ihevc_chroma_intra_pred.h | 8 + common/ihevc_hbd_chroma_intra_pred_filters.c | 207 +++++++++++++++++-- decoder/ihevcd_iquant_itrans_recon_ctb.c | 2 +- 3 files changed, 194 insertions(+), 23 deletions(-) diff --git a/common/ihevc_chroma_intra_pred.h b/common/ihevc_chroma_intra_pred.h index 8ffa888..91ef727 100644 --- a/common/ihevc_chroma_intra_pred.h +++ b/common/ihevc_chroma_intra_pred.h @@ -135,6 +135,12 @@ typedef void ihevc_intra_pred_chroma_ref_filtering_ft(UWORD8 *pu1_src, WORD32 mode, WORD32 intra_smoothing_flags); +typedef void ihevc_hbd_intra_pred_chroma_ref_filtering_ft(UWORD16* pu2_src, + WORD32 nt, + UWORD16* pu2_dst, + WORD32 mode, + WORD32 intra_smoothing_flags); + typedef void ihevc_hbd_intra_pred_chroma_planar_ft( UWORD16 *pu2_ref, WORD32 src_strd, @@ -233,6 +239,7 @@ typedef void ihevc_hbd_intra_pred_chroma_ref_substitution_ft(UWORD16 *pu2_top_le WORD32 nbr_flags, UWORD16 *pu2_dst, WORD32 dst_strd, + WORD32 chroma_format_idc, UWORD8 bit_depth); /* C function declarations */ @@ -260,6 +267,7 @@ ihevc_hbd_intra_pred_chroma_mode_11_to_17_ft ihevc_hbd_intra_pred_chroma_mode_11 ihevc_hbd_intra_pred_chroma_mode_19_to_25_ft ihevc_hbd_intra_pred_chroma_mode_19_to_25; ihevc_hbd_intra_pred_chroma_mode_27_to_33_ft ihevc_hbd_intra_pred_chroma_mode_27_to_33; ihevc_hbd_intra_pred_chroma_ref_substitution_ft ihevc_hbd_intra_pred_chroma_ref_substitution; +ihevc_hbd_intra_pred_chroma_ref_filtering_ft ihevc_hbd_intra_pred_chroma_ref_filtering; /* C function declarations */ ihevc_intra_pred_chroma_planar_ft ihevc_intra_pred_chroma_planar; diff --git a/common/ihevc_hbd_chroma_intra_pred_filters.c b/common/ihevc_hbd_chroma_intra_pred_filters.c index 1a65e03..5d5190e 100644 --- a/common/ihevc_hbd_chroma_intra_pred_filters.c +++ b/common/ihevc_hbd_chroma_intra_pred_filters.c @@ -67,6 +67,7 @@ #include "ihevc_function_selector.h" #include "ihevc_platform_macros.h" #include "ihevc_intra_pred.h" +#include "ihevc_defs.h" #include "ihevc_chroma_intra_pred.h" #include "ihevc_common_tables.h" @@ -87,6 +88,97 @@ /* Function Definition */ /*****************************************************************************/ +/** +******************************************************************************* +* +* @brief +* Intra prediction interpolation filter for chroma ref_filtering (4:4:4) +* +* +* @par Description: +* Reference DC filtering for neighboring chroma samples dependent on TU size and +* mode Refer to section 8.4.4.2.3 in the standard +* +* @param[in] pu2_src +* UWORD16 pointer to the source +* +* @param[out] pu2_dst +* UWORD16 pointer to the destination +* +* @param[in] nt +* integer Transform Block size +* +* @param[in] mode +* integer intraprediction mode +* +* @param[in] intra_smoothing_flag +* integer containing intra_smoothing_disabled_flag and strong_smoothing_enable_flag +* +* @returns +* +* @remarks +* None +* +******************************************************************************* +*/ + +void ihevc_hbd_intra_pred_chroma_ref_filtering(UWORD16* pu2_src, + WORD32 nt, + UWORD16* pu2_dst, + WORD32 mode, + WORD32 intra_smoothing_flag) +{ + WORD32 filter_flag; + WORD32 i; /* Generic indexing variable */ + WORD32 four_nt = 4 * nt; + UWORD16 au2_flt[((4 * MAX_CU_SIZE) + 1) * 2]; + WORD32 intra_smoothing_disabled_flag = (intra_smoothing_flag >> 3) & 0x1; + WORD32 strong_intra_smoothing_enable_flag = intra_smoothing_flag & 0x1; + UNUSED(strong_intra_smoothing_enable_flag); + + if (intra_smoothing_disabled_flag) + { + if (pu2_src == pu2_dst) return; + for (i = 0; i < (2 * (four_nt + 1)); i += 2) + { + pu2_dst[i] = pu2_src[i]; + pu2_dst[i + 1] = pu2_src[i + 1]; + } + return; + } + + filter_flag = gau1_intra_pred_ref_filter[mode] & (1 << (CTZ(nt) - 2)); + if (0 == filter_flag) + { + if (pu2_src == pu2_dst) return; + for (i = 0; i < (2 * (four_nt + 1)); i += 2) + { + pu2_dst[i] = pu2_src[i]; + pu2_dst[i + 1] = pu2_src[i + 1]; + } + } + else + { + /* Extremities Untouched*/ + au2_flt[0] = pu2_src[0]; + au2_flt[1] = pu2_src[1]; + au2_flt[four_nt * 2] = pu2_src[four_nt * 2]; + au2_flt[(four_nt * 2) + 1] = pu2_src[(four_nt * 2) + 1]; + + for (i = 2; i < four_nt * 2; i += 2) + { + au2_flt[i] = (pu2_src[i - 2] + 2 * pu2_src[i] + pu2_src[i + 2] + 2) >> 2; + au2_flt[i + 1] = (pu2_src[i - 1] + 2 * pu2_src[i + 1] + pu2_src[i + 3] + 2) >> 2; + } + + for (i = 0; i < (2 * (four_nt + 1)); i += 2) + { + pu2_dst[i] = au2_flt[i]; + pu2_dst[i + 1] = au2_flt[i + 1]; + } + } +} + /** ******************************************************************************* * @@ -135,6 +227,7 @@ void ihevc_hbd_intra_pred_chroma_ref_substitution(UWORD16 * pu2_top_left, WORD32 nbr_flags, UWORD16 * pu2_dst, WORD32 dst_strd, + WORD32 chroma_format_idc, UWORD8 bit_depth) { UWORD16 pu2_ref_u, pu2_ref_v; @@ -213,7 +306,7 @@ void ihevc_hbd_intra_pred_chroma_ref_substitution(UWORD16 * pu2_top_left, pu2_dst[(4 * nt ) + 2 + i] = pu2_top[i]; // U-V interleaved Top-top right samples } } - if(nt == 4) + if (nt == 4 || (nt == 8 && chroma_format_idc == CHROMA_FMT_IDC_YUV444)) { /* 1 bit extraction for all the neighboring blocks */ tp_left = (nbr_flags & 0x10000) >> 16; @@ -292,9 +385,10 @@ void ihevc_hbd_intra_pred_chroma_ref_substitution(UWORD16 * pu2_top_left, } } } - else if(nt == 8) + else if (nt == 8 || (nt == 16 && chroma_format_idc == CHROMA_FMT_IDC_YUV444)) { WORD32 nbr_flags_temp = 0; + WORD32 sub_sample = chroma_format_idc == CHROMA_FMT_IDC_YUV444 ? 2 : 1; nbr_flags_temp = ((nbr_flags & 0xC)>>2) + ((nbr_flags & 0xC0) >> 4) + ((nbr_flags & 0x300) >> 4) + ((nbr_flags & 0x3000) >> 6) @@ -303,18 +397,18 @@ void ihevc_hbd_intra_pred_chroma_ref_substitution(UWORD16 * pu2_top_left, /* compute trailing zeors based on nbr_flag for substitution process of below left see section .*/ /* as each bit in nbr flags corresponds to 8 pels for bot_left, left, top and topright but 1 pel for topleft */ { - nbr_id_from_bl = look_up_trailing_zeros(nbr_flags_temp & 0XF) * 4; /* for bottom left and left */ - if(nbr_id_from_bl == 32) + nbr_id_from_bl = look_up_trailing_zeros(nbr_flags_temp & 0XF) * (4 * sub_sample); /* for bottom left and left */ + if (nbr_id_from_bl == (32 * sub_sample)) { - nbr_id_from_bl = 16; + nbr_id_from_bl = (16 * sub_sample); } - if(nbr_id_from_bl == 16) + if (nbr_id_from_bl == (16 * sub_sample)) { /* for top left : 1 pel per nbr bit */ if(!((nbr_flags_temp >> 8) & 0x1)) { nbr_id_from_bl++; - nbr_id_from_bl += look_up_trailing_zeros((nbr_flags_temp >> 4) & 0xF) * 4; /* top and top right; 8 pels per nbr bit */ + nbr_id_from_bl += look_up_trailing_zeros((nbr_flags_temp >> 4) & 0xF) * (4 * sub_sample); /* top and top right; 8 pels per nbr bit */ } } @@ -333,14 +427,14 @@ void ihevc_hbd_intra_pred_chroma_ref_substitution(UWORD16 * pu2_top_left, } /* for the loop of 4*Nt+1 pixels (excluding pixels computed from reverse substitution) */ - while(nbr_id_from_bl < ((T8C_4NT) + 1)) + while (nbr_id_from_bl < ((T8C_4NT * sub_sample) + 1)) { /* To Obtain the next unavailable idx flag after reverse neighbor substitution */ /* Divide by 8 to obtain the original index */ - frwd_nbr_flag = (nbr_id_from_bl >> 2);/*+ (nbr_id_from_bl & 0x1);*/ + frwd_nbr_flag = (nbr_id_from_bl >> (chroma_format_idc == CHROMA_FMT_IDC_YUV444 ? 3 : 2));/*+ (nbr_id_from_bl & 0x1);*/ /* The Top-left flag is at the last bit location of nbr_flags*/ - if(nbr_id_from_bl == (T8C_4NT / 2)) + if (nbr_id_from_bl == (T8C_4NT * sub_sample / 2)) { get_bits = GET_BIT(nbr_flags_temp, 8); @@ -359,24 +453,25 @@ void ihevc_hbd_intra_pred_chroma_ref_substitution(UWORD16 * pu2_top_left, /* 8 pel substitution (other than TL) */ pu2_ref_u = pu2_dst[(2 * nbr_id_from_bl) - 2]; pu2_ref_v = pu2_dst[(2 * nbr_id_from_bl) - 1]; - for(i = 0; i < 8; i+=2) + for (i = 0; i < 8 * sub_sample; i += 2) { pu2_dst[(2 * nbr_id_from_bl) + i] = pu2_ref_u; pu2_dst[(2 * nbr_id_from_bl) + 1 + i] = pu2_ref_v; } } } - nbr_id_from_bl += (nbr_id_from_bl == (T8C_4NT / 2)) ? 1 : 4; + nbr_id_from_bl += (nbr_id_from_bl == (T8C_4NT * sub_sample / 2)) ? 1 : 4 * sub_sample; } } - else if(nt == 16) + else if (nt == 16 || (nt == 32 && chroma_format_idc == CHROMA_FMT_IDC_YUV444)) { + WORD32 sub_sample = chroma_format_idc == CHROMA_FMT_IDC_YUV444 ? 2 : 1; /* compute trailing ones based on mbr_flag for substitution process of below left see section .*/ /* as each bit in nbr flags corresponds to 4 pels for bot_left, left, top and topright but 1 pel for topleft */ { - nbr_id_from_bl = look_up_trailing_zeros((nbr_flags & 0XFF)) * 4; /* for bottom left and left */ + nbr_id_from_bl = look_up_trailing_zeros((nbr_flags & 0XFF)) * 4 * sub_sample; /* for bottom left and left */ - if(nbr_id_from_bl == 32) + if (nbr_id_from_bl == 32 * sub_sample) { /* for top left : 1 pel per nbr bit */ if(!((nbr_flags >> 16) & 0x1)) @@ -384,7 +479,7 @@ void ihevc_hbd_intra_pred_chroma_ref_substitution(UWORD16 * pu2_top_left, /* top left not available */ nbr_id_from_bl++; /* top and top right; 4 pels per nbr bit */ - nbr_id_from_bl += look_up_trailing_zeros((nbr_flags >> 8) & 0xFF) * 4; + nbr_id_from_bl += look_up_trailing_zeros((nbr_flags >> 8) & 0xFF) * 4 * sub_sample; } } /* Reverse Substitution Process*/ @@ -402,14 +497,14 @@ void ihevc_hbd_intra_pred_chroma_ref_substitution(UWORD16 * pu2_top_left, } /* for the loop of 4*Nt+1 pixels (excluding pixels computed from reverse substitution) */ - while(nbr_id_from_bl < ((T16C_4NT) + 1)) + while (nbr_id_from_bl < ((T16C_4NT * sub_sample) + 1)) { /* To Obtain the next unavailable idx flag after reverse neighbor substitution */ - /* Devide by 4 to obtain the original index */ - frwd_nbr_flag = (nbr_id_from_bl >> 2);/*+ (nbr_id_from_bl & 0x1);*/ + /* Divide by 4 to obtain the original index */ + frwd_nbr_flag = (nbr_id_from_bl >> (chroma_format_idc == CHROMA_FMT_IDC_YUV444 ? 3 : 2));/*+ (nbr_id_from_bl & 0x1);*/ /* The Top-left flag is at the last bit location of nbr_flags*/ - if(nbr_id_from_bl == (T16C_4NT / 2)) + if (nbr_id_from_bl == (T16C_4NT * sub_sample / 2)) { get_bits = GET_BIT(nbr_flags, 16); /* only pel substitution for TL */ @@ -427,14 +522,82 @@ void ihevc_hbd_intra_pred_chroma_ref_substitution(UWORD16 * pu2_top_left, /* 4 pel substitution (other than TL) */ pu2_ref_u = pu2_dst[(2 * nbr_id_from_bl) - 2]; pu2_ref_v = pu2_dst[(2 * nbr_id_from_bl) - 1]; - for(i = 0; i < 8; i+=2) + for (i = 0; i < 4 * sub_sample; i++) + { + pu2_dst[(2 * nbr_id_from_bl) + 2 * i] = pu2_ref_u; + pu2_dst[(2 * nbr_id_from_bl) + 1 + 2 * i] = pu2_ref_v; + } + } + } + nbr_id_from_bl += (nbr_id_from_bl == (T16C_4NT * sub_sample / 2)) ? 1 : 4 * sub_sample; + } + } + else if (nt == 32) + { + /* compute trailing ones based on mbr_flag for substitution process of below left see section .*/ + /* as each bit in nbr flags corresponds to 8 pels for bot_left, left, top and topright but 1 pel for topleft */ + { + nbr_id_from_bl = look_up_trailing_zeros((nbr_flags & 0XFF)) * 8; /* for bottom left and left */ + + if (nbr_id_from_bl == 64) + { + /* for top left : 1 pel per nbr bit */ + if (!((nbr_flags >> 16) & 0x1)) + { + /* top left not available */ + nbr_id_from_bl++; + /* top and top right; 8 pels per nbr bit */ + nbr_id_from_bl += look_up_trailing_zeros((nbr_flags >> 8) & 0xFF) * 8; + } + } + /* Reverse Substitution Process*/ + if (nbr_id_from_bl) + { + /* Replicate the bottom-left and subsequent unavailable pixels with the 1st available pixel above */ + pu2_ref_u = pu2_dst[2 * nbr_id_from_bl]; + pu2_ref_v = pu2_dst[2 * nbr_id_from_bl + 1]; + for (i = (2 * (nbr_id_from_bl - 1)); i >= 0; i -= 2) + { + pu2_dst[i] = pu2_ref_u; + pu2_dst[i + 1] = pu2_ref_v; + } + } + } + + /* for the loop of 4*Nt+1 pixels (excluding pixels computed from reverse substitution) */ + while (nbr_id_from_bl < (128 + 1)) + { + /* To Obtain the next unavailable idx flag after reverse neighbor substitution */ + /* Devide by 8 to obtain the original index */ + frwd_nbr_flag = (nbr_id_from_bl >> 3); + + /* The Top-left flag is at the last bit location of nbr_flags*/ + if (nbr_id_from_bl == 64) + { + get_bits = GET_BIT(nbr_flags, 16); + /* only pel substitution for TL */ + if (!get_bits) + { + pu2_dst[2 * nbr_id_from_bl] = pu2_dst[(2 * nbr_id_from_bl) - 2]; + pu2_dst[(2 * nbr_id_from_bl) + 1] = pu2_dst[(2 * nbr_id_from_bl) - 1]; + } + } + else + { + get_bits = GET_BIT(nbr_flags, frwd_nbr_flag); + if (!get_bits) + { + /* 8 pel substitution (other than TL) */ + pu2_ref_u = pu2_dst[(2 * nbr_id_from_bl) - 2]; + pu2_ref_v = pu2_dst[(2 * nbr_id_from_bl) - 1]; + for (i = 0; i < 16; i += 2) { pu2_dst[(2 * nbr_id_from_bl) + i] = pu2_ref_u; pu2_dst[(2 * nbr_id_from_bl) + 1 + i] = pu2_ref_v; } } } - nbr_id_from_bl += (nbr_id_from_bl == (T16C_4NT / 2)) ? 1 : 4; + nbr_id_from_bl += (nbr_id_from_bl == T16C_4NT) ? 1 : 8; } } } diff --git a/decoder/ihevcd_iquant_itrans_recon_ctb.c b/decoder/ihevcd_iquant_itrans_recon_ctb.c index 1fbe152..401ab20 100644 --- a/decoder/ihevcd_iquant_itrans_recon_ctb.c +++ b/decoder/ihevcd_iquant_itrans_recon_ctb.c @@ -1746,7 +1746,7 @@ WORD32 ihevcd_iquant_itrans_recon_ctb(process_ctxt_t *ps_proc) { ps_codec->s_func_selector.ihevc_hbd_intra_pred_chroma_ref_substitution_fptr((UWORD16 *)pu1_top_left, (UWORD16 *)pu1_top, (UWORD16 *)pu1_left, ps_cb_tu->pred_strd, trans_size, - chroma_nbr_flags, (UWORD16 *)pu1_ref_sub_out, 1, + chroma_nbr_flags, (UWORD16 *)pu1_ref_sub_out, 1, ps_sps->i1_chroma_format_idc, (UWORD8) i4_bit_depth_chroma); /* use the look up to get the function idx */