Changeset 792 in 3DVCSoftware


Ignore:
Timestamp:
23 Jan 2014, 05:06:49 (10 years ago)
Author:
samsung-htm
Message:

Integration of JCT3V-G0101: InterSDC with multiple DC candidates

Location:
branches/HTM-9.3-dev3-Samsung
Files:
4 added
13 edited

Legend:

Unmodified
Added
Removed
  • branches/HTM-9.3-dev3-Samsung/source/Lib/TLibCommon/TComDataCU.cpp

    r782 r792  
    123123#if H_3D_DIM_SDC
    124124  m_pbSDCFlag             = NULL;
    125 #if QC_SDC_UNIFY_G0130
     125#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    126126  for( Int i = 0; i < 4; i++ )
    127127  {
     
    160160  }
    161161#endif
     162#if !SEC_INTER_SDC_G0101
    162163  m_pucInterSDCMask       = NULL;
     164#endif
    163165#endif
    164166}
     
    281283#if H_3D_DIM_SDC
    282284    m_pbSDCFlag             = (Bool*)xMalloc(Bool, uiNumPartition);
    283 #if QC_SDC_UNIFY_G0130
     285#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    284286    for( Int i = 0; i < 4; i++ )
    285287    {
     
    305307    m_acCUMvField[1].setNumPartition(uiNumPartition );
    306308  }
     309#if !SEC_INTER_SDC_G0101
    307310#if H_3D_INTER_SDC
    308311  m_pucInterSDCMask     = (UChar*  )xMalloc(UChar,    g_uiMaxCUHeight*g_uiMaxCUWidth);
     312#endif
    309313#endif
    310314  m_sliceStartCU        = (UInt*  )xMalloc(UInt, uiNumPartition);
     
    416420#if H_3D_DIM_SDC
    417421    if ( m_pbSDCFlag            ) { xFree(m_pbSDCFlag);             m_pbSDCFlag             = NULL; }
    418 #if QC_SDC_UNIFY_G0130
     422#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    419423    for( Int i = 0; i < 4; i++ )
    420424    {
     
    435439#endif
    436440  }
     441#if !SEC_INTER_SDC_G0101
    437442#if H_3D_INTER_SDC
    438443  if ( m_pucInterSDCMask     ) { xFree(m_pucInterSDCMask);      m_pucInterSDCMask    = NULL; }
     444#endif
    439445#endif
    440446  m_pcCUAboveLeft       = NULL;
     
    639645#if H_3D_DIM_SDC
    640646    memset( m_pbSDCFlag             + firstElement,     0,                numElements * sizeof( *m_pbSDCFlag            ) );
    641 #if QC_SDC_UNIFY_G0130
     647#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    642648    for( Int i = 0; i < 4; i++ )
    643649    {
     
    818824#if H_3D_DIM_SDC
    819825      m_pbSDCFlag           [ui] = false;
    820 #if QC_SDC_UNIFY_G0130
     826#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    821827      for( Int i = 0; i < 4; i++ )
    822828      {
     
    938944#if H_3D_DIM_SDC
    939945  memset( m_pbSDCFlag,            0, sizeof(Bool) * m_uiNumPartition  );
    940 #if QC_SDC_UNIFY_G0130
     946#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    941947  for( Int i = 0; i < 4; i++ )
    942948  {
     
    10221028#if H_3D_DIM_SDC
    10231029      m_pbSDCFlag           [ui] = pcCU->m_pbSDCFlag            [ uiPartOffset + ui ];
    1024 #if QC_SDC_UNIFY_G0130
     1030#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    10251031      for( Int i = 0; i < 4; i++ )
    10261032      {
     
    11851191#if H_3D_DIM_SDC
    11861192  m_pbSDCFlag               = pcCU->getSDCFlag()              + uiPart;
    1187 #if QC_SDC_UNIFY_G0130
     1193#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    11881194  for( Int i = 0; i < 4; i++ )
    11891195  {
     
    13861392#if H_3D_DIM_SDC
    13871393  memcpy( m_pbSDCFlag             + uiOffset, pcCU->getSDCFlag(),             iSizeInBool  );
    1388 #if QC_SDC_UNIFY_G0130
     1394#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    13891395  for( Int i = 0; i < 4; i++ )
    13901396  {
     
    15151521#if H_3D_DIM_SDC
    15161522  memcpy( rpcCU->getSDCFlag()             + m_uiAbsIdxInLCU, m_pbSDCFlag,      iSizeInBool  );
    1517 #if QC_SDC_UNIFY_G0130
     1523#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    15181524  for( Int i = 0; i < 4; i++ )
    15191525  {
     
    16301636#if H_3D_DIM_SDC
    16311637  memcpy( rpcCU->getSDCFlag()             + uiPartOffset, m_pbSDCFlag,      iSizeInBool  );
    1632 #if QC_SDC_UNIFY_G0130
     1638#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    16331639  for( Int i = 0; i < 4; i++ )
    16341640  {
     
    23842390#endif
    23852391
     2392#if !SEC_INTER_SDC_G0101
    23862393Void TComDataCU::xSetInterSDCCUMask( TComDataCU *pcCU, UChar *pMask )
    23872394{
     
    24802487
    24812488}
     2489#endif
    24822490#endif
    24832491
  • branches/HTM-9.3-dev3-Samsung/source/Lib/TLibCommon/TComDataCU.h

    r782 r792  
    215215#if H_3D_DIM_SDC
    216216  Bool*         m_pbSDCFlag;
    217 #if QC_SDC_UNIFY_G0130
     217#if QC_SDC_UNIFY_G0130 && !SEC_INTER_SDC_G0101
    218218  Pel*          m_apSegmentDCOffset[4];
    219219#else
     
    227227  Int*          m_apSegmentInterDCOffset[4];
    228228#endif
     229#if !SEC_INTER_SDC_G0101
    229230  UChar*        m_pucInterSDCMask;
     231#endif
    230232#endif
    231233#if H_3D
     
    593595  Void          setInterSDCSegmentDCOffset( Int pOffset, UInt uiSeg, UInt uiPartIdx) { m_apSegmentInterDCOffset[uiSeg][uiPartIdx] = pOffset; }
    594596#endif
     597#if !SEC_INTER_SDC_G0101
    595598  Void          xSetInterSDCCUMask( TComDataCU *pcCU, UChar *pMask );
    596599
    597600  UChar*        getInterSDCMask     ()                        { return m_pucInterSDCMask;              }
     601#endif
    598602#endif
    599603 
  • branches/HTM-9.3-dev3-Samsung/source/Lib/TLibCommon/TypeDef.h

    r782 r792  
    239239#define QC_SDC_UNIFY_G0130                1  // Unify intra SDC and inter SDC
    240240#endif
     241#define SEC_INTER_SDC_G0101               1  // Improved inter SDC with multiple DC candidates
    241242#define H_3D_DIM_ENC                      1   // Depth Intra encoder optimizations, includes:
    242243                                              // HHI_DEPTH_INTRA_SEARCH_RAU_C0160
  • branches/HTM-9.3-dev3-Samsung/source/Lib/TLibDecoder/TDecCu.cpp

    r782 r792  
    652652  UInt  uiWidth      = pcCU->getWidth ( 0 );
    653653  UInt  uiHeight     = pcCU->getHeight( 0 );
     654#if !SEC_INTER_SDC_G0101
    654655  UChar* pMask       = pcCU->getInterSDCMask();
    655656
    656657  memset( pMask, 0, uiWidth*uiHeight );
    657658  pcCU->xSetInterSDCCUMask( pcCU, pMask );
     659#endif
    658660
    659661  Pel  *pResi;
     
    666668    for( uiPelX = 0; uiPelX < uiWidth; uiPelX++ )
    667669    {
     670#if SEC_INTER_SDC_G0101
     671      pResi[ uiPelX ] = pcCU->getSDCSegmentDCOffset( 0, 0 );
     672#else
    668673      UChar uiSeg = pMask[ uiPelX + uiPelY*uiWidth ];
    669674#if QC_SDC_UNIFY_G0130
     
    671676#else
    672677      pResi[ uiPelX ] = pcCU->getInterSDCSegmentDCOffset( uiSeg, 0 );
     678#endif
    673679#endif
    674680    }
  • branches/HTM-9.3-dev3-Samsung/source/Lib/TLibDecoder/TDecEntropy.cpp

    r782 r792  
    814814  }
    815815
     816#if SEC_INTER_SDC_G0101
     817  if( !pcCU->getSlice()->getIsDepth() || pcCU->getPartitionSize( uiAbsPartIdx ) != SIZE_2Nx2N || pcCU->isSkipped( uiAbsPartIdx ) )
     818#else
    816819  if( !pcCU->getSlice()->getIsDepth() || ( pcCU->isIntra( uiAbsPartIdx ) && pcCU->getPartitionSize( uiAbsPartIdx ) != SIZE_2Nx2N ) || pcCU->isSkipped( uiAbsPartIdx ) )
    817   {
    818     return;
    819   }
    820 
     820#endif
     821  {
     822    return;
     823  }
     824
     825#if SEC_INTER_SDC_G0101
     826  assert( pcCU->getPartitionSize( uiAbsPartIdx ) == SIZE_2Nx2N || ( !pcCU->isIntra( uiAbsPartIdx ) && !pcCU->isSkipped( uiAbsPartIdx ) ) );
     827#else
    821828  assert( ( pcCU->isIntra( uiAbsPartIdx ) && pcCU->getPartitionSize( uiAbsPartIdx ) == SIZE_2Nx2N ) || ( !pcCU->isIntra( uiAbsPartIdx ) && !pcCU->isSkipped( uiAbsPartIdx ) ) );
     829#endif
    822830
    823831  m_pcEntropyDecoderIf->parseSDCFlag( pcCU, uiAbsPartIdx, uiDepth );
  • branches/HTM-9.3-dev3-Samsung/source/Lib/TLibDecoder/TDecSbac.cpp

    r782 r792  
    22102210  else
    22112211  {
     2212#if SEC_INTER_SDC_G0101
     2213    uiNumSegments = 1;
     2214#else
    22122215    PartSize cPartSize = pcCU->getPartitionSize( absPartIdx );
    2213 
    22142216    uiNumSegments = ( cPartSize == SIZE_2Nx2N ) ? 1 : ( cPartSize == SIZE_NxN ? 4 : 2 );
     2217#endif
    22152218    symbol = 1;
    22162219  }
  • branches/HTM-9.3-dev3-Samsung/source/Lib/TLibEncoder/TEncCu.cpp

    r782 r792  
    19791979          if( rpcTempCU->getSlice()->getVPS()->getInterSDCFlag( rpcTempCU->getSlice()->getLayerIdInVps() ) && rpcTempCU->getSlice()->getIsDepth() && !uiNoResidual )
    19801980          {
     1981#if SEC_INTER_SDC_G0101
     1982            for( Int uiOffest = -2 ; uiOffest <= 2 ; uiOffest++ )
     1983            {
     1984              if( rpcTempCU != rpcTempCUPre )
     1985              {
     1986                rpcTempCU->initEstData( uhDepth, orgQP );
     1987                rpcTempCU->copyPartFrom( rpcBestCU, 0, uhDepth );
     1988              }
     1989              rpcTempCU->setSkipFlagSubParts( false, 0, uhDepth );
     1990              rpcTempCU->setTrIdxSubParts( 0, 0, uhDepth );
     1991              rpcTempCU->setCbfSubParts( 1, 1, 1, 0, uhDepth );
     1992#if H_3D_VSO //M2
     1993              if( m_pcRdCost->getUseRenModel() )
     1994              { //Reset
     1995                UInt  uiWidth     = m_ppcOrigYuv[uhDepth]->getWidth    ();
     1996                UInt  uiHeight    = m_ppcOrigYuv[uhDepth]->getHeight   ();
     1997                Pel*  piSrc       = m_ppcOrigYuv[uhDepth]->getLumaAddr ();
     1998                UInt  uiSrcStride = m_ppcOrigYuv[uhDepth]->getStride   ();
     1999                m_pcRdCost->setRenModelData( rpcTempCU, 0, piSrc, uiSrcStride, uiWidth, uiHeight );
     2000              }
     2001#endif
     2002              m_pcPredSearch->encodeResAndCalcRdInterSDCCU( rpcTempCU,
     2003                m_ppcOrigYuv[uhDepth],
     2004                ( rpcTempCU != rpcTempCUPre ) ? m_ppcPredYuvBest[uhDepth] : m_ppcPredYuvTemp[uhDepth],
     2005                m_ppcResiYuvTemp[uhDepth],
     2006                m_ppcRecoYuvTemp[uhDepth],
     2007                uiOffest,
     2008                uhDepth );
     2009
     2010              xCheckDQP( rpcTempCU );
     2011              xCheckBestMode( rpcBestCU, rpcTempCU, uhDepth );
     2012            }
     2013#else
    19812014            if( rpcTempCU != rpcTempCUPre )
    19822015            {
     
    20062039            xCheckDQP( rpcTempCU );
    20072040            xCheckBestMode( rpcBestCU, rpcTempCU, uhDepth );
     2041#endif
    20082042          }
    20092043#endif
     
    22022236  xCheckBestMode(rpcBestCU, rpcTempCU, uhDepth);
    22032237#if H_3D_INTER_SDC
     2238#if SEC_INTER_SDC_G0101 // ONLY_2NX2N_SDC
     2239  if( rpcTempCU->getSlice()->getVPS()->getInterSDCFlag( rpcTempCU->getSlice()->getLayerIdInVps() ) && rpcTempCU->getSlice()->getIsDepth() && ePartSize == SIZE_2Nx2N)
     2240#else
    22042241  if( rpcTempCU->getSlice()->getVPS()->getInterSDCFlag( rpcTempCU->getSlice()->getLayerIdInVps() ) && rpcTempCU->getSlice()->getIsDepth() )
    2205   {
     2242#endif
     2243  {
     2244#if SEC_INTER_SDC_G0101
     2245    for( Int uiOffest = -2 ; uiOffest <= 2 ; uiOffest++ )
     2246    {
     2247      if( rpcTempCU != rpcTempCUPre )
     2248      {
     2249        Int orgQP = rpcBestCU->getQP( 0 );
     2250        rpcTempCU->initEstData( uhDepth, orgQP );
     2251        rpcTempCU->copyPartFrom( rpcBestCU, 0, uhDepth );
     2252      }
     2253      rpcTempCU->setSkipFlagSubParts( false, 0, uhDepth );
     2254      rpcTempCU->setTrIdxSubParts( 0, 0, uhDepth );
     2255      rpcTempCU->setCbfSubParts( 1, 1, 1, 0, uhDepth );
     2256#if H_3D_VSO // M3
     2257      if( m_pcRdCost->getUseRenModel() )
     2258      {
     2259        UInt  uiWidth     = m_ppcOrigYuv[uhDepth]->getWidth ( );
     2260        UInt  uiHeight    = m_ppcOrigYuv[uhDepth]->getHeight( );
     2261        Pel*  piSrc       = m_ppcOrigYuv[uhDepth]->getLumaAddr( );
     2262        UInt  uiSrcStride = m_ppcOrigYuv[uhDepth]->getStride();
     2263        m_pcRdCost->setRenModelData( rpcTempCU, 0, piSrc, uiSrcStride, uiWidth, uiHeight );
     2264      }
     2265#endif
     2266
     2267      m_pcPredSearch->encodeResAndCalcRdInterSDCCU( rpcTempCU,
     2268        m_ppcOrigYuv[uhDepth],
     2269        ( rpcTempCU != rpcTempCUPre ) ? m_ppcPredYuvBest[uhDepth] : m_ppcPredYuvTemp[uhDepth],
     2270        m_ppcResiYuvTemp[uhDepth],
     2271        m_ppcRecoYuvTemp[uhDepth],
     2272        uiOffest,
     2273        uhDepth );
     2274
     2275      xCheckDQP( rpcTempCU );
     2276      xCheckBestMode(rpcBestCU, rpcTempCU, uhDepth);
     2277    }
     2278#else
    22062279    if( rpcTempCU != rpcTempCUPre )
    22072280    {
     
    22332306  xCheckDQP( rpcTempCU );
    22342307  xCheckBestMode(rpcBestCU, rpcTempCU, uhDepth);
     2308#endif
    22352309  }
    22362310#endif
  • branches/HTM-9.3-dev3-Samsung/source/Lib/TLibEncoder/TEncEntropy.cpp

    r782 r792  
    882882  }
    883883
     884#if SEC_INTER_SDC_G0101
     885  if( !pcCU->getSlice()->getIsDepth() || pcCU->getPartitionSize( uiAbsPartIdx ) != SIZE_2Nx2N || pcCU->isSkipped( uiAbsPartIdx ) )
     886#else
    884887  if( !pcCU->getSlice()->getIsDepth() || ( pcCU->isIntra( uiAbsPartIdx ) && pcCU->getPartitionSize( uiAbsPartIdx ) != SIZE_2Nx2N ) || pcCU->isSkipped( uiAbsPartIdx ) )
    885   {
    886     return;
    887   }
    888 
     888#endif
     889  {
     890    return;
     891  }
     892
     893#if SEC_INTER_SDC_G0101
     894  assert( pcCU->getPartitionSize( uiAbsPartIdx ) == SIZE_2Nx2N || ( !pcCU->isIntra( uiAbsPartIdx ) && !pcCU->isSkipped( uiAbsPartIdx ) ) );
     895#else
    889896  assert( ( pcCU->isIntra( uiAbsPartIdx ) && pcCU->getPartitionSize( uiAbsPartIdx ) == SIZE_2Nx2N ) || ( !pcCU->isIntra( uiAbsPartIdx ) && !pcCU->isSkipped( uiAbsPartIdx ) ) );
     897#endif
    890898
    891899  if( bRD )
     
    905913
    906914  if( !pcCU->getSlice()->getIsDepth() || pcCU->isIntra( uiAbsPartIdx ) || pcCU->isSkipped( uiAbsPartIdx ) )
    907   {
    908915    return;
    909916  }
  • branches/HTM-9.3-dev3-Samsung/source/Lib/TLibEncoder/TEncSbac.cpp

    r782 r792  
    22592259  else //all-zero inter SDC is not allowed
    22602260  {
     2261#if SEC_INTER_SDC_G0101
     2262    uiNumSegments = 1;
     2263#else
    22612264    PartSize cPartSize = pcCU->getPartitionSize( absPartIdx );
    2262 
    22632265    uiNumSegments = ( cPartSize == SIZE_2Nx2N ) ? 1 : ( cPartSize == SIZE_NxN ? 4 : 2 );
     2266#endif
    22642267    dimDeltaDC = 1;
    22652268  }
  • branches/HTM-9.3-dev3-Samsung/source/Lib/TLibEncoder/TEncSearch.cpp

    r782 r792  
    56335633
    56345634#if H_3D_INTER_SDC
     5635#if SEC_INTER_SDC_G0101
     5636Void TEncSearch::encodeResAndCalcRdInterSDCCU( TComDataCU* pcCU, TComYuv* pcOrg, TComYuv* pcPred, TComYuv* pcResi, TComYuv* pcRec, Int uiOffest, const UInt uiDepth )
     5637#else
    56355638Void TEncSearch::encodeResAndCalcRdInterSDCCU( TComDataCU* pcCU, TComYuv* pcOrg, TComYuv* pcPred, TComYuv* pcResi, TComYuv* pcRec, const UInt uiDepth )
     5639#endif
    56365640{
    56375641  if( !pcCU->getSlice()->getIsDepth() || pcCU->isIntra( 0 ) )
     
    56475651  UInt  uiWidth      = pcCU->getWidth ( 0 );
    56485652  UInt  uiHeight     = pcCU->getHeight( 0 );
     5653#if SEC_INTER_SDC_G0101
     5654  UInt uiSegSize = 0;
     5655#else
    56495656  UChar* pMask       = pcCU->getInterSDCMask();
    56505657  memset( pMask, 0, uiWidth*uiHeight );
     
    56535660
    56545661  UInt uiSegSize[4] = { 0, 0, 0, 0 };
     5662#endif
    56555663  Pel *pPred, *pOrg;
    56565664  UInt uiPredStride = pcPred->getStride();
    56575665  UInt uiOrgStride  = pcOrg->getStride();
    56585666  UInt uiPelX, uiPelY;
     5667#if !SEC_INTER_SDC_G0101
    56595668  UInt uiPartitionSize = pcCU->getPartitionSize( 0 );
    56605669  UInt uiSegmentNum = ( uiPartitionSize == SIZE_2Nx2N ) ? 1 : ( uiPartitionSize == SIZE_NxN ? 4 : 2 );
     5670#endif
    56615671
    56625672  pPred = pcPred->getLumaAddr( 0 );
    56635673  pOrg  = pcOrg->getLumaAddr( 0 );
     5674#if SEC_INTER_SDC_G0101
     5675  Int pResDC = 0;
     5676#else
    56645677  Int pResDC[4] = { 0, 0, 0, 0};
     5678#endif
    56655679
    56665680  //calculate dc value for prediction and original signal, and calculate residual and reconstruction
     
    56695683    for( uiPelX = 0; uiPelX < uiWidth; uiPelX++ )
    56705684    {
     5685#if SEC_INTER_SDC_G0101
     5686      pResDC += (Int)( pOrg [uiPelX] - pPred[uiPelX] );
     5687      uiSegSize++;
     5688#else
    56715689      UChar uiSeg = pMask[ uiPelX + uiPelY*uiWidth ];
    56725690      pResDC[uiSeg] += (Int)( pOrg [uiPelX] - pPred[uiPelX] );
    56735691      uiSegSize[uiSeg]++;
     5692#endif
    56745693    }
    56755694    pOrg  += uiOrgStride;
     
    56775696  }
    56785697
     5698#if SEC_INTER_SDC_G0101
     5699  Int iResiOffset = ( pResDC  > 0 ? ( uiSegSize >> 1 ) : -1*( uiSegSize >> 1 ) );
     5700  pResDC          = ( pResDC + iResiOffset ) / (Int) uiSegSize;
     5701
     5702  pcCU->setSDCSegmentDCOffset( pResDC + uiOffest, 0, 0 );
     5703#else
    56795704  for( UInt uiSeg = 0; uiSeg < uiSegmentNum; uiSeg++ )
    56805705  {
     
    56875712#endif
    56885713  }
     5714#endif
    56895715
    56905716  Pel *pRec;
     
    56975723    for( uiPelX = 0; uiPelX < uiWidth; uiPelX++ )
    56985724    {
     5725#if SEC_INTER_SDC_G0101
     5726      pRec[ uiPelX ] = Clip3( 0, ( 1 << g_bitDepthY ) - 1, pPred[uiPelX] + pcCU->getSDCSegmentDCOffset(0, 0) );
     5727#else
    56995728      UChar uiSeg = pMask[ uiPelX + uiPelY*uiWidth ];
    57005729      assert( uiSeg < uiSegmentNum );
    57015730
    57025731      pRec[ uiPelX ] = Clip3( 0, ( 1 << g_bitDepthY ) - 1, pPred[uiPelX] + pResDC[uiSeg] );
     5732#endif
    57035733    }
    57045734    pPred     += uiPredStride;
     
    57455775  Bool bNonSkip = true;
    57465776#endif
     5777#if SEC_INTER_SDC_G0101
     5778  bNonSkip |= ( pcCU->getSDCSegmentDCOffset( 0, 0 ) != 0 ) ? 1 : 0;
     5779#else
    57475780  for( UInt uiSeg = 0; uiSeg < uiSegmentNum; uiSeg++ )
    57485781  {
     
    57535786#endif
    57545787  }
     5788#endif
    57555789
    57565790  if( !bNonSkip )
  • branches/HTM-9.3-dev3-Samsung/source/Lib/TLibEncoder/TEncSearch.h

    r781 r792  
    219219    TComYuv* pcResi,
    220220    TComYuv* pcRec,
     221#if SEC_INTER_SDC_G0101
     222    Int      uiOffset,
     223#endif
    221224    const UInt uiDepth );
    222225#endif
Note: See TracChangeset for help on using the changeset viewer.