/******************************************************************************
*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*****************************************************************************
* Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
*/
/**
*******************************************************************************
* @file
* ideint_api.c
*
* @brief
* This file contains the definitions of the core processing of the de-
* interlacer.
*
* @author
* Ittiam
*
* @par List of Functions:
*
* @remarks
* None
*
*******************************************************************************
*/
/*****************************************************************************/
/* File Includes */
/*****************************************************************************/
/* System include files */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
/* User include files */
#include "icv_datatypes.h"
#include "icv_macros.h"
#include "icv_platform_macros.h"
#include "icv.h"
#include "icv_variance.h"
#include "icv_sad.h"
#include "ideint.h"
#include "ideint_defs.h"
#include "ideint_structs.h"
#include "ideint_utils.h"
#include "ideint_cac.h"
#include "ideint_debug.h"
#include "ideint_function_selector.h"
/**
*******************************************************************************
*
* @brief
* Return deinterlacer context size
*
* @par Description
* Return deinterlacer context size, application will allocate this memory
* and send it as context to process call
*
* @param[in] None
*
* @returns
* Size of deinterlacer context
*
* @remarks
* None
*
*******************************************************************************
*/
WORD32 ideint_ctxt_size(void)
{
return sizeof(ctxt_t);
}
/**
*******************************************************************************
*
* @brief
* Deinterlace given fields and produce a frame
*
* @par Description
* Deinterlacer function that deinterlaces given fields and produces a frame
*
* @param[in] pv_ctxt
* Deinterlacer context returned by ideint_create()
*
* @param[in] ps_prv_fld
* Previous field (can be null, in which case spatial filtering is done
* unconditionally)
*
* @param[in] ps_cur_fld
* Current field
*
* @param[in] ps_nxt_fld
* Next field
*
* @param[in] ps_out_frm
* Output frame
*
* @param[in] ps_params
* Parameters
*
* @param[in] start_row
* Start row
*
* @param[in] num_rows
* Number of rows to be processed
*
* @returns
* IDEINT_ERROR_T
*
* @remarks
*
*******************************************************************************
*/
IDEINT_ERROR_T ideint_process(void *pv_ctxt,
icv_pic_t *ps_prv_fld,
icv_pic_t *ps_cur_fld,
icv_pic_t *ps_nxt_fld,
icv_pic_t *ps_out_frm,
ideint_params_t *ps_params,
WORD32 start_row,
WORD32 num_rows)
{
ctxt_t *ps_ctxt;
WORD32 num_blks_x, num_blks_y;
WORD32 num_comp;
WORD32 i, row, col;
WORD32 rows_remaining;
if(NULL == pv_ctxt)
return IDEINT_INVALID_CTXT;
ps_ctxt = (ctxt_t *)pv_ctxt;
/* Copy the parameters */
if(ps_params)
{
ps_ctxt->s_params = *ps_params;
}
else
{
/* Use default params if ps_params is NULL */
ps_ctxt->s_params.i4_cur_fld_top = 1;
ps_ctxt->s_params.e_mode = IDEINT_MODE_SPATIAL;
ps_ctxt->s_params.e_arch = ideint_default_arch();
ps_ctxt->s_params.e_soc = ICV_SOC_GENERIC;
ps_ctxt->s_params.i4_disable_weave = 0;
ps_ctxt->s_params.pf_aligned_alloc = NULL;
ps_ctxt->s_params.pf_aligned_free = NULL;
}
/* Start row has to be multiple of 8 */
if(start_row & 0x7)
{
return IDEINT_START_ROW_UNALIGNED;
}
/* Initialize variances */
ps_ctxt->ai4_vrnc_avg_fb[0] = VAR_AVG_LUMA;
ps_ctxt->ai4_vrnc_avg_fb[1] = VAR_AVG_CHROMA;
ps_ctxt->ai4_vrnc_avg_fb[2] = VAR_AVG_CHROMA;
ideint_init_function_ptr(ps_ctxt);
rows_remaining = ps_out_frm->ai4_ht[0] - start_row;
num_rows = MIN(num_rows,
rows_remaining);
IDEINT_CORRUPT_PIC(ps_out_frm, 0xCD);
//Weave two fields to get a frame
if(IDEINT_MODE_WEAVE == ps_ctxt->s_params.e_mode)
{
if(0 == ps_ctxt->s_params.i4_disable_weave)
{
if(ps_ctxt->s_params.i4_cur_fld_top)
ideint_weave_pic(ps_cur_fld, ps_nxt_fld, ps_out_frm,
start_row,
num_rows);
else
ideint_weave_pic(ps_nxt_fld, ps_cur_fld, ps_out_frm,
start_row,
num_rows);
}
return IDEINT_ERROR_NONE;
}
num_comp = 3;
for(i = 0; i < num_comp; i++)
{
UWORD8 *pu1_prv, *pu1_out;
UWORD8 *pu1_top, *pu1_bot, *pu1_dst;
WORD32 cur_strd, out_strd, dst_strd;
WORD32 st_thresh;
WORD32 vrnc_avg_st;
WORD32 disable_cac_sad;
WORD32 comp_row_start, comp_row_end;
num_blks_x = ALIGN8(ps_out_frm->ai4_wd[i]) >> 3;
num_blks_y = ALIGN8(ps_out_frm->ai4_ht[i]) >> 3;
comp_row_start = start_row;
comp_row_end = comp_row_start + num_rows;
if(i)
{
comp_row_start >>= 1;
comp_row_end >>= 1;
}
comp_row_end = MIN(comp_row_end, ps_out_frm->ai4_ht[i]);
comp_row_start = ALIGN8(comp_row_start) >> 3;
comp_row_end = ALIGN8(comp_row_end) >> 3;
st_thresh = ST_THRESH;
vrnc_avg_st = VAR_AVG_LUMA;
if(i)
{
st_thresh = ST_THRESH >> 1;
vrnc_avg_st = VAR_AVG_CHROMA;
}
out_strd = ps_out_frm->ai4_strd[i];
if(ps_ctxt->s_params.i4_cur_fld_top)
{
cur_strd = ps_cur_fld->ai4_strd[i];
}
else
{
cur_strd = ps_nxt_fld->ai4_strd[i];
}
disable_cac_sad = 0;
/* If previous field is not provided, then change to SPATIAL mode */
if(ps_prv_fld->apu1_buf[i] == NULL)
{
disable_cac_sad = 1;
}
for(row = comp_row_start; row < comp_row_end; row++)
{
pu1_out = ps_out_frm->apu1_buf[i];
pu1_out += (ps_out_frm->ai4_strd[i] * row << 3);
pu1_prv = ps_prv_fld->apu1_buf[i];
pu1_prv += (ps_prv_fld->ai4_strd[i] * row << 2);
if(ps_ctxt->s_params.i4_cur_fld_top)
{
pu1_top = ps_cur_fld->apu1_buf[i];
pu1_bot = ps_nxt_fld->apu1_buf[i];
}
else
{
pu1_top = ps_nxt_fld->apu1_buf[i];
pu1_bot = ps_cur_fld->apu1_buf[i];
}
pu1_top += (cur_strd * row << 2);
pu1_bot += (cur_strd * row << 2);
for(col = 0; col < num_blks_x; col++)
{
WORD32 cac, sad, vrnc;
WORD32 th_num, th_den;
UWORD8 au1_dst[BLK_WD * BLK_HT];
WORD32 blk_wd, blk_ht;
WORD32 input_boundary;
cac = 0;
sad = 0;
th_den = 0;
th_num = st_thresh;
vrnc = 0;
disable_cac_sad = 0;
/* If previous field is not provided, then change to SPATIAL mode */
if(ps_prv_fld->apu1_buf[i] == NULL)
{
disable_cac_sad = 1;
}
/* For boundary blocks when input dimensions are not multiple of 8,
* then change to spatial mode */
input_boundary = 0;
blk_wd = BLK_WD;
blk_ht = BLK_HT;
if((((num_blks_x - 1) == col) && (ps_out_frm->ai4_wd[i] & 0x7)) ||
(((num_blks_y - 1) == row) && (ps_out_frm->ai4_ht[i] & 0x7)))
{
disable_cac_sad = 1;
input_boundary = 1;
if(((num_blks_x - 1) == col) && (ps_out_frm->ai4_wd[i] & 0x7))
blk_wd = (ps_out_frm->ai4_wd[i] & 0x7);
if(((num_blks_y - 1) == row) && (ps_out_frm->ai4_ht[i] & 0x7))
blk_ht = (ps_out_frm->ai4_ht[i] & 0x7);
}
if(0 == disable_cac_sad)
{
/* Compute SAD */
PROFILE_DISABLE_SAD
sad = ps_ctxt->pf_sad_8x4(pu1_prv, pu1_bot, cur_strd,
cur_strd,
BLK_WD,
BLK_HT >> 1);
/* Compute Variance */
PROFILE_DISABLE_VARIANCE
vrnc = ps_ctxt->pf_variance_8x4(pu1_top, cur_strd, BLK_WD,
BLK_HT >> 1);
th_num = st_thresh;
th_num *= vrnc_avg_st +
((MOD_IDX_ST_NUM * vrnc) >> MOD_IDX_ST_SHIFT);
th_den = vrnc +
((MOD_IDX_ST_NUM * vrnc_avg_st) >> MOD_IDX_ST_SHIFT);
if((sad * th_den) <= th_num)
{
/* Calculate Combing Artifact if SAD test fails */
PROFILE_DISABLE_CAC
cac = ps_ctxt->pf_cac_8x8(pu1_top, pu1_bot, cur_strd, cur_strd);
}
}
pu1_dst = pu1_out;
dst_strd = out_strd;
/* In case boundary blocks are not complete (dimensions non-multiple of 8)
* Use intermediate buffer as destination and copy required pixels to output
* buffer later
*/
if(input_boundary)
{
pu1_dst = au1_dst;
dst_strd = BLK_WD;
ideint_weave_blk(pu1_top, pu1_bot, pu1_dst, dst_strd,
cur_strd, blk_wd, blk_ht);
}
/* Weave the two fields unconditionally */
if(0 == ps_ctxt->s_params.i4_disable_weave)
{
ideint_weave_blk(pu1_top, pu1_bot, pu1_dst, dst_strd,
cur_strd, blk_wd, blk_ht);
}
if(disable_cac_sad || cac || (sad * th_den > th_num))
{
/* Pad the input fields in an intermediate buffer if required */
if((0 == row) || (0 == col) ||
((num_blks_x - 1) == col) || ((num_blks_y - 1) == row))
{
UWORD8 *pu1_dst_top;
UWORD8 au1_pad[(BLK_HT + 4) * (BLK_WD + 4)];
ideint_pad_blk(pu1_top, pu1_bot, au1_pad, cur_strd, row,
col, num_blks_y, num_blks_x, blk_wd, blk_ht);
pu1_dst_top = au1_pad + 2 * (BLK_WD + 4) + 2;
PROFILE_DISABLE_SPATIAL
ps_ctxt->pf_spatial_filter(pu1_dst_top, pu1_dst + dst_strd,
(BLK_WD + 4) * 2,
dst_strd * 2);
}
else
{
PROFILE_DISABLE_SPATIAL
ps_ctxt->pf_spatial_filter(pu1_top, pu1_dst + dst_strd,
cur_strd, dst_strd * 2);
}
}
/* copy required pixels to output buffer for boundary blocks
* when dimensions are not multiple of 8
*/
if(input_boundary)
{
WORD32 j;
for(j = 0; j < blk_ht; j++)
{
memcpy(pu1_out + j * out_strd, au1_dst + j * BLK_WD, blk_wd);
}
}
pu1_prv += 8;
pu1_top += 8;
pu1_bot += 8;
pu1_out += 8;
}
}
}
return IDEINT_ERROR_NONE;
}