source: 3DVCSoftware/branches/HTM-14.1-update-dev3-MediaTek-Qualcomm/source/Lib/TLibEncoder/TEncSlice.cpp @ 1259

Last change on this file since 1259 was 1259, checked in by mediatek-htm, 9 years ago

Reactive IVMV by Mediatek

  • Property svn:eol-style set to native
File size: 52.3 KB
Line 
1/* The copyright in this software is being made available under the BSD
2 * License, included below. This software may be subject to other third party
3 * and contributor rights, including patent rights, and no such rights are
4 * granted under this license.
5 *
6 * Copyright (c) 2010-2015, ITU/ISO/IEC
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 *  * Redistributions of source code must retain the above copyright notice,
13 *    this list of conditions and the following disclaimer.
14 *  * Redistributions in binary form must reproduce the above copyright notice,
15 *    this list of conditions and the following disclaimer in the documentation
16 *    and/or other materials provided with the distribution.
17 *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
18 *    be used to endorse or promote products derived from this software without
19 *    specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/** \file     TEncSlice.cpp
35    \brief    slice encoder class
36*/
37
38#include "TEncTop.h"
39#include "TEncSlice.h"
40#include <math.h>
41
42//! \ingroup TLibEncoder
43//! \{
44
45// ====================================================================================================================
46// Constructor / destructor / create / destroy
47// ====================================================================================================================
48TEncSlice::TEncSlice()
49 : m_encCABACTableIdx(I_SLICE)
50{
51  m_apcPicYuvPred = NULL;
52  m_apcPicYuvResi = NULL;
53
54  m_pdRdPicLambda = NULL;
55  m_pdRdPicQp     = NULL;
56  m_piRdPicQp     = NULL;
57}
58
59TEncSlice::~TEncSlice()
60{
61}
62
63Void TEncSlice::create( Int iWidth, Int iHeight, ChromaFormat chromaFormat, UInt iMaxCUWidth, UInt iMaxCUHeight, UChar uhTotalDepth )
64{
65  // create prediction picture
66  if ( m_apcPicYuvPred == NULL )
67  {
68    m_apcPicYuvPred  = new TComPicYuv;
69    m_apcPicYuvPred->create( iWidth, iHeight, chromaFormat, iMaxCUWidth, iMaxCUHeight, uhTotalDepth, true );
70  }
71
72  // create residual picture
73  if( m_apcPicYuvResi == NULL )
74  {
75    m_apcPicYuvResi  = new TComPicYuv;
76    m_apcPicYuvResi->create( iWidth, iHeight, chromaFormat, iMaxCUWidth, iMaxCUHeight, uhTotalDepth, true );
77  }
78}
79
80Void TEncSlice::destroy()
81{
82  // destroy prediction picture
83  if ( m_apcPicYuvPred )
84  {
85    m_apcPicYuvPred->destroy();
86    delete m_apcPicYuvPred;
87    m_apcPicYuvPred  = NULL;
88  }
89
90  // destroy residual picture
91  if ( m_apcPicYuvResi )
92  {
93    m_apcPicYuvResi->destroy();
94    delete m_apcPicYuvResi;
95    m_apcPicYuvResi  = NULL;
96  }
97
98  // free lambda and QP arrays
99  if ( m_pdRdPicLambda )
100  {
101    xFree( m_pdRdPicLambda );
102    m_pdRdPicLambda = NULL;
103  }
104  if ( m_pdRdPicQp )
105  {
106    xFree( m_pdRdPicQp );
107    m_pdRdPicQp = NULL;
108  }
109  if ( m_piRdPicQp )
110  {
111    xFree( m_piRdPicQp );
112    m_piRdPicQp = NULL;
113  }
114}
115
116Void TEncSlice::init( TEncTop* pcEncTop )
117{
118  m_pcCfg             = pcEncTop;
119  m_pcListPic         = pcEncTop->getListPic();
120
121  m_pcGOPEncoder      = pcEncTop->getGOPEncoder();
122  m_pcCuEncoder       = pcEncTop->getCuEncoder();
123  m_pcPredSearch      = pcEncTop->getPredSearch();
124
125  m_pcEntropyCoder    = pcEncTop->getEntropyCoder();
126  m_pcSbacCoder       = pcEncTop->getSbacCoder();
127  m_pcBinCABAC        = pcEncTop->getBinCABAC();
128  m_pcTrQuant         = pcEncTop->getTrQuant();
129
130  m_pcRdCost          = pcEncTop->getRdCost();
131  m_pppcRDSbacCoder   = pcEncTop->getRDSbacCoder();
132  m_pcRDGoOnSbacCoder = pcEncTop->getRDGoOnSbacCoder();
133
134  // create lambda and QP arrays
135  m_pdRdPicLambda     = (Double*)xMalloc( Double, m_pcCfg->getDeltaQpRD() * 2 + 1 );
136  m_pdRdPicQp         = (Double*)xMalloc( Double, m_pcCfg->getDeltaQpRD() * 2 + 1 );
137  m_piRdPicQp         = (Int*   )xMalloc( Int,    m_pcCfg->getDeltaQpRD() * 2 + 1 );
138#if KWU_RC_MADPRED_E0227
139  if(m_pcCfg->getUseRateCtrl())
140  {
141    m_pcRateCtrl        = pcEncTop->getRateCtrl();
142  }
143  else
144  {
145    m_pcRateCtrl        = NULL;
146  }
147#else
148  m_pcRateCtrl        = pcEncTop->getRateCtrl();
149#endif
150
151}
152
153
154
155Void
156TEncSlice::setUpLambda(TComSlice* slice, const Double dLambda, Int iQP)
157{
158  // store lambda
159  m_pcRdCost ->setLambda( dLambda, slice->getSPS()->getBitDepths() );
160
161  // for RDO
162  // in RdCost there is only one lambda because the luma and chroma bits are not separated, instead we weight the distortion of chroma.
163  Double dLambdas[MAX_NUM_COMPONENT] = { dLambda };
164  for(UInt compIdx=1; compIdx<MAX_NUM_COMPONENT; compIdx++)
165  {
166    const ComponentID compID=ComponentID(compIdx);
167    Int chromaQPOffset = slice->getPPS()->getQpOffset(compID) + slice->getSliceChromaQpDelta(compID);
168    Int qpc=(iQP + chromaQPOffset < 0) ? iQP : getScaledChromaQP(iQP + chromaQPOffset, m_pcCfg->getChromaFormatIdc());
169    Double tmpWeight = pow( 2.0, (iQP-qpc)/3.0 );  // takes into account of the chroma qp mapping and chroma qp Offset
170    m_pcRdCost->setDistortionWeight(compID, tmpWeight);
171    dLambdas[compIdx]=dLambda/tmpWeight;
172  }
173
174#if RDOQ_CHROMA_LAMBDA
175// for RDOQ
176  m_pcTrQuant->setLambdas( dLambdas );
177#else
178  m_pcTrQuant->setLambda( dLambda );
179#endif
180
181// For SAO
182  slice   ->setLambdas( dLambdas );
183}
184
185
186
187/**
188 - non-referenced frame marking
189 - QP computation based on temporal structure
190 - lambda computation based on QP
191 - set temporal layer ID and the parameter sets
192 .
193 \param pcPic         picture class
194 \param pocLast       POC of last picture
195 \param pocCurr       current POC
196 \param iNumPicRcvd   number of received pictures
197 \param iGOPid        POC offset for hierarchical structure
198 \param rpcSlice      slice header class
199 \param isField       true for field coding
200 */
201
202#if NH_MV
203Void TEncSlice::initEncSlice( TComPic* pcPic, Int pocLast, Int pocCurr, Int iGOPid, TComSlice*& rpcSlice, TComVPS* pVPS, Int layerId, bool isField )
204#else
205Void TEncSlice::initEncSlice( TComPic* pcPic, Int pocLast, Int pocCurr, Int iGOPid, TComSlice*& rpcSlice, Bool isField )
206#endif
207{
208  Double dQP;
209  Double dLambda;
210
211  rpcSlice = pcPic->getSlice(0);
212
213#if NH_MV
214  rpcSlice->setVPS( pVPS ); 
215
216  rpcSlice->setLayerId     ( layerId );
217  rpcSlice->setViewId      ( pVPS->getViewId      ( layerId ) );   
218  rpcSlice->setViewIndex   ( pVPS->getViewIndex   ( layerId ) );
219#if NH_3D
220  rpcSlice->setIsDepth     ( pVPS->getDepthId     ( layerId ) != 0 );   
221#endif
222#endif
223  rpcSlice->setSliceBits(0);
224  rpcSlice->setPic( pcPic );
225  rpcSlice->initSlice();
226  rpcSlice->setPicOutputFlag( true );
227  rpcSlice->setPOC( pocCurr );
228#if H_3D_IC
229  rpcSlice->setApplyIC( false );
230#endif
231  // depth computation based on GOP size
232  Int depth;
233  {
234
235    Int poc = rpcSlice->getPOC();
236    if(isField)
237    {
238      poc = (poc/2) % (m_pcCfg->getGOPSize()/2);
239    }
240    else
241    {
242      poc = poc % m_pcCfg->getGOPSize();   
243    }
244    if ( poc == 0 )
245    {
246      depth = 0;
247    }
248    else
249    {
250      Int step = m_pcCfg->getGOPSize();
251      depth    = 0;
252      for( Int i=step>>1; i>=1; i>>=1 )
253      {
254        for ( Int j=i; j<m_pcCfg->getGOPSize(); j+=step )
255        {
256          if ( j == poc )
257          {
258            i=0;
259            break;
260          }
261        }
262        step >>= 1;
263        depth++;
264      }
265    }
266
267    if(m_pcCfg->getHarmonizeGopFirstFieldCoupleEnabled() && poc != 0)
268    {
269      if (isField && ((rpcSlice->getPOC() % 2) == 1))
270      {
271        depth ++;
272      }
273    }
274  }
275
276  // slice type
277#if NH_MV
278  SliceType eSliceTypeBaseView;
279  if( pocLast == 0 || pocCurr % m_pcCfg->getIntraPeriod() == 0 || m_pcGOPEncoder->getGOPSize() == 0 )
280  {
281    eSliceTypeBaseView = I_SLICE;
282  }
283  else
284  {
285    eSliceTypeBaseView = B_SLICE;
286  }
287  SliceType eSliceType = eSliceTypeBaseView;
288  if( eSliceTypeBaseView == I_SLICE && m_pcCfg->getGOPEntry(MAX_GOP).m_POC == 0 && m_pcCfg->getGOPEntry(MAX_GOP).m_sliceType != 'I' )
289  {
290    eSliceType = B_SLICE; 
291  }
292#else
293  SliceType eSliceType;
294
295  eSliceType=B_SLICE;
296  if(!(isField && pocLast == 1) || !m_pcCfg->getEfficientFieldIRAPEnabled())
297  {
298    if(m_pcCfg->getDecodingRefreshType() == 3)
299    {
300      eSliceType = (pocLast == 0 || pocCurr % m_pcCfg->getIntraPeriod() == 0             || m_pcGOPEncoder->getGOPSize() == 0) ? I_SLICE : eSliceType;
301    }
302    else
303    {
304      eSliceType = (pocLast == 0 || (pocCurr - (isField ? 1 : 0)) % m_pcCfg->getIntraPeriod() == 0 || m_pcGOPEncoder->getGOPSize() == 0) ? I_SLICE : eSliceType;
305    }
306  }
307#endif
308  rpcSlice->setSliceType    ( eSliceType );
309
310  // ------------------------------------------------------------------------------------------------------------------
311  // Non-referenced frame marking
312  // ------------------------------------------------------------------------------------------------------------------
313
314  if(pocLast == 0)
315  {
316    rpcSlice->setTemporalLayerNonReferenceFlag(false);
317  }
318  else
319  {
320#if 0 // Check this! NH_MV
321    rpcSlice->setTemporalLayerNonReferenceFlag(!m_pcCfg->getGOPEntry( (eSliceTypeBaseView == I_SLICE) ? MAX_GOP : iGOPid ).m_refPic);
322#else
323    rpcSlice->setTemporalLayerNonReferenceFlag(!m_pcCfg->getGOPEntry(iGOPid).m_refPic);
324#endif
325  }
326  rpcSlice->setReferenced(true);
327
328  // ------------------------------------------------------------------------------------------------------------------
329  // QP setting
330  // ------------------------------------------------------------------------------------------------------------------
331
332  dQP = m_pcCfg->getQP();
333  if(eSliceType!=I_SLICE)
334  {
335    if (!(( m_pcCfg->getMaxDeltaQP() == 0 ) && (dQP == -rpcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA) ) && (rpcSlice->getPPS()->getTransquantBypassEnableFlag())))
336    {
337#if NH_MV
338      dQP += m_pcCfg->getGOPEntry( (eSliceTypeBaseView == I_SLICE) ? MAX_GOP : iGOPid ).m_QPOffset;
339#else
340      dQP += m_pcCfg->getGOPEntry(iGOPid).m_QPOffset;
341#endif
342    }
343  }
344
345  // modify QP
346  Int* pdQPs = m_pcCfg->getdQPs();
347  if ( pdQPs )
348  {
349    dQP += pdQPs[ rpcSlice->getPOC() ];
350  }
351
352  if (m_pcCfg->getCostMode()==COST_LOSSLESS_CODING)
353  {
354    dQP=LOSSLESS_AND_MIXED_LOSSLESS_RD_COST_TEST_QP;
355    m_pcCfg->setDeltaQpRD(0);
356  }
357
358  // ------------------------------------------------------------------------------------------------------------------
359  // Lambda computation
360  // ------------------------------------------------------------------------------------------------------------------
361
362  Int iQP;
363  Double dOrigQP = dQP;
364
365  // pre-compute lambda and QP values for all possible QP candidates
366  for ( Int iDQpIdx = 0; iDQpIdx < 2 * m_pcCfg->getDeltaQpRD() + 1; iDQpIdx++ )
367  {
368    // compute QP value
369    dQP = dOrigQP + ((iDQpIdx+1)>>1)*(iDQpIdx%2 ? -1 : 1);
370
371    // compute lambda value
372    Int    NumberBFrames = ( m_pcCfg->getGOPSize() - 1 );
373    Int    SHIFT_QP = 12;
374
375    Double dLambda_scale = 1.0 - Clip3( 0.0, 0.5, 0.05*(Double)(isField ? NumberBFrames/2 : NumberBFrames) );
376
377#if FULL_NBIT
378    Int    bitdepth_luma_qp_scale = 6 * (rpcSlice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) - 8);
379#else
380    Int    bitdepth_luma_qp_scale = 0;
381#endif
382    Double qp_temp = (Double) dQP + bitdepth_luma_qp_scale - SHIFT_QP;
383#if FULL_NBIT
384    Double qp_temp_orig = (Double) dQP - SHIFT_QP;
385#endif
386    // Case #1: I or P-slices (key-frame)
387#if NH_MV
388    Double dQPFactor;
389    if( eSliceType != I_SLICE ) 
390    {
391      dQPFactor = m_pcCfg->getGOPEntry( (eSliceTypeBaseView == I_SLICE) ? MAX_GOP : iGOPid ).m_QPFactor;
392    }
393    else
394#else
395    Double dQPFactor = m_pcCfg->getGOPEntry(iGOPid).m_QPFactor;
396    if ( eSliceType==I_SLICE )
397#endif
398    {
399      dQPFactor=0.57*dLambda_scale;
400    }
401    dLambda = dQPFactor*pow( 2.0, qp_temp/3.0 );
402
403    if ( depth>0 )
404    {
405#if FULL_NBIT
406        dLambda *= Clip3( 2.00, 4.00, (qp_temp_orig / 6.0) ); // (j == B_SLICE && p_cur_frm->layer != 0 )
407#else
408        dLambda *= Clip3( 2.00, 4.00, (qp_temp / 6.0) ); // (j == B_SLICE && p_cur_frm->layer != 0 )
409#endif
410    }
411
412    // if hadamard is used in ME process
413    if ( !m_pcCfg->getUseHADME() && rpcSlice->getSliceType( ) != I_SLICE )
414    {
415      dLambda *= 0.95;
416    }
417
418    iQP = max( -rpcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), min( MAX_QP, (Int) floor( dQP + 0.5 ) ) );
419
420    m_pdRdPicLambda[iDQpIdx] = dLambda;
421    m_pdRdPicQp    [iDQpIdx] = dQP;
422    m_piRdPicQp    [iDQpIdx] = iQP;
423  }
424
425  // obtain dQP = 0 case
426  dLambda = m_pdRdPicLambda[0];
427  dQP     = m_pdRdPicQp    [0];
428  iQP     = m_piRdPicQp    [0];
429
430  if( rpcSlice->getSliceType( ) != I_SLICE )
431  {
432#if NH_MV
433    dLambda *= m_pcCfg->getLambdaModifier( m_pcCfg->getGOPEntry((eSliceTypeBaseView == I_SLICE) ? MAX_GOP : iGOPid).m_temporalId );
434#else
435    dLambda *= m_pcCfg->getLambdaModifier( m_pcCfg->getGOPEntry(iGOPid).m_temporalId );
436#endif
437  }
438
439  setUpLambda(rpcSlice, dLambda, iQP);
440
441#if NH_3D_VSO
442  m_pcRdCost->setUseLambdaScaleVSO  ( (m_pcCfg->getUseVSO() ||  m_pcCfg->getForceLambdaScaleVSO()) && m_pcCfg->getIsDepth() );
443  m_pcRdCost->setLambdaVSO          ( dLambda * m_pcCfg->getLambdaScaleVSO() );
444
445  // Should be moved to TEncTop
446 
447  // SAIT_VSO_EST_A0033
448  m_pcRdCost->setDisparityCoeff( m_pcCfg->getDispCoeff() );
449
450  // LGE_WVSO_A0119
451  if( m_pcCfg->getUseWVSO() && m_pcCfg->getIsDepth() )
452  {
453    m_pcRdCost->setDWeight  ( m_pcCfg->getDWeight()   );
454    m_pcRdCost->setVSOWeight( m_pcCfg->getVSOWeight() );
455    m_pcRdCost->setVSDWeight( m_pcCfg->getVSDWeight() );
456  }
457
458#endif
459
460  if (m_pcCfg->getFastMEForGenBLowDelayEnabled())
461  {
462  // restore original slice type
463#if NH_MV
464  eSliceType = eSliceTypeBaseView;
465  if( eSliceTypeBaseView == I_SLICE && m_pcCfg->getGOPEntry(MAX_GOP).m_POC == 0 && m_pcCfg->getGOPEntry(MAX_GOP).m_sliceType != 'I' )
466  {
467    eSliceType = B_SLICE;
468  }
469#else
470    if(!(isField && pocLast == 1) || !m_pcCfg->getEfficientFieldIRAPEnabled())
471  {
472    if(m_pcCfg->getDecodingRefreshType() == 3)
473    {
474      eSliceType = (pocLast == 0 || (pocCurr)                     % m_pcCfg->getIntraPeriod() == 0 || m_pcGOPEncoder->getGOPSize() == 0) ? I_SLICE : eSliceType;
475    }
476    else
477    {
478      eSliceType = (pocLast == 0 || (pocCurr - (isField ? 1 : 0)) % m_pcCfg->getIntraPeriod() == 0 || m_pcGOPEncoder->getGOPSize() == 0) ? I_SLICE : eSliceType;
479
480    }
481  }
482#endif
483
484  rpcSlice->setSliceType        ( eSliceType );
485}
486
487  if (m_pcCfg->getUseRecalculateQPAccordingToLambda())
488  {
489    dQP = xGetQPValueAccordingToLambda( dLambda );
490    iQP = max( -rpcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), min( MAX_QP, (Int) floor( dQP + 0.5 ) ) );
491  }
492
493  rpcSlice->setSliceQp           ( iQP );
494#if ADAPTIVE_QP_SELECTION
495  rpcSlice->setSliceQpBase       ( iQP );
496#endif
497  rpcSlice->setSliceQpDelta      ( 0 );
498  rpcSlice->setSliceChromaQpDelta( COMPONENT_Cb, 0 );
499  rpcSlice->setSliceChromaQpDelta( COMPONENT_Cr, 0 );
500  rpcSlice->setUseChromaQpAdj( rpcSlice->getPPS()->getPpsRangeExtension().getChromaQpOffsetListEnabledFlag() );
501#if NH_MV
502  rpcSlice->setNumRefIdx(REF_PIC_LIST_0,m_pcCfg->getGOPEntry( (eSliceTypeBaseView == I_SLICE) ? MAX_GOP : iGOPid ).m_numRefPicsActive);
503  rpcSlice->setNumRefIdx(REF_PIC_LIST_1,m_pcCfg->getGOPEntry( (eSliceTypeBaseView == I_SLICE) ? MAX_GOP : iGOPid ).m_numRefPicsActive);
504#else
505  rpcSlice->setNumRefIdx(REF_PIC_LIST_0,m_pcCfg->getGOPEntry(iGOPid).m_numRefPicsActive);
506  rpcSlice->setNumRefIdx(REF_PIC_LIST_1,m_pcCfg->getGOPEntry(iGOPid).m_numRefPicsActive);
507#endif
508
509  if ( m_pcCfg->getDeblockingFilterMetric() )
510  {
511    rpcSlice->setDeblockingFilterOverrideFlag(true);
512    rpcSlice->setDeblockingFilterDisable(false);
513    rpcSlice->setDeblockingFilterBetaOffsetDiv2( 0 );
514    rpcSlice->setDeblockingFilterTcOffsetDiv2( 0 );
515  }
516  else if (rpcSlice->getPPS()->getDeblockingFilterControlPresentFlag())
517  {
518    rpcSlice->setDeblockingFilterOverrideFlag( rpcSlice->getPPS()->getDeblockingFilterOverrideEnabledFlag() );
519    rpcSlice->setDeblockingFilterDisable( rpcSlice->getPPS()->getPicDisableDeblockingFilterFlag() );
520    if ( !rpcSlice->getDeblockingFilterDisable())
521    {
522      if ( rpcSlice->getDeblockingFilterOverrideFlag() && eSliceType!=I_SLICE)
523      {
524#if NH_MV
525        rpcSlice->setDeblockingFilterBetaOffsetDiv2( m_pcCfg->getGOPEntry((eSliceTypeBaseView == I_SLICE) ? MAX_GOP : iGOPid).m_betaOffsetDiv2 + m_pcCfg->getLoopFilterBetaOffset()  );
526        rpcSlice->setDeblockingFilterTcOffsetDiv2( m_pcCfg->getGOPEntry((eSliceTypeBaseView == I_SLICE) ? MAX_GOP : iGOPid).m_tcOffsetDiv2 + m_pcCfg->getLoopFilterTcOffset() );
527#else
528        rpcSlice->setDeblockingFilterBetaOffsetDiv2( m_pcCfg->getGOPEntry(iGOPid).m_betaOffsetDiv2 + m_pcCfg->getLoopFilterBetaOffset()  );
529        rpcSlice->setDeblockingFilterTcOffsetDiv2( m_pcCfg->getGOPEntry(iGOPid).m_tcOffsetDiv2 + m_pcCfg->getLoopFilterTcOffset() );
530#endif
531      }
532      else
533      {
534        rpcSlice->setDeblockingFilterBetaOffsetDiv2( m_pcCfg->getLoopFilterBetaOffset() );
535        rpcSlice->setDeblockingFilterTcOffsetDiv2( m_pcCfg->getLoopFilterTcOffset() );
536      }
537    }
538  }
539  else
540  {
541    rpcSlice->setDeblockingFilterOverrideFlag( false );
542    rpcSlice->setDeblockingFilterDisable( false );
543    rpcSlice->setDeblockingFilterBetaOffsetDiv2( 0 );
544    rpcSlice->setDeblockingFilterTcOffsetDiv2( 0 );
545  }
546
547  rpcSlice->setDepth            ( depth );
548
549#if NH_MV
550  pcPic->setTLayer( m_pcCfg->getGOPEntry( (eSliceTypeBaseView == I_SLICE) ? MAX_GOP : iGOPid ).m_temporalId );
551#else
552  pcPic->setTLayer( m_pcCfg->getGOPEntry(iGOPid).m_temporalId );
553#endif
554  if(eSliceType==I_SLICE)
555  {
556    pcPic->setTLayer(0);
557  }
558  rpcSlice->setTLayer( pcPic->getTLayer() );
559
560  assert( m_apcPicYuvPred );
561  assert( m_apcPicYuvResi );
562
563  pcPic->setPicYuvPred( m_apcPicYuvPred );
564  pcPic->setPicYuvResi( m_apcPicYuvResi );
565  rpcSlice->setSliceMode            ( m_pcCfg->getSliceMode()            );
566  rpcSlice->setSliceArgument        ( m_pcCfg->getSliceArgument()        );
567  rpcSlice->setSliceSegmentMode     ( m_pcCfg->getSliceSegmentMode()     );
568  rpcSlice->setSliceSegmentArgument ( m_pcCfg->getSliceSegmentArgument() );
569#if NH_3D_IV_MERGE
570#else
571  rpcSlice->setMaxNumMergeCand        ( m_pcCfg->getMaxNumMergeCand()        );
572#endif
573}
574
575Void TEncSlice::resetQP( TComPic* pic, Int sliceQP, Double lambda )
576{
577  TComSlice* slice = pic->getSlice(0);
578
579  // store lambda
580  slice->setSliceQp( sliceQP );
581#if ADAPTIVE_QP_SELECTION
582  slice->setSliceQpBase ( sliceQP );
583#endif
584  setUpLambda(slice, lambda, sliceQP);
585}
586
587// ====================================================================================================================
588// Public member functions
589// ====================================================================================================================
590
591Void TEncSlice::setSearchRange( TComSlice* pcSlice )
592{
593  Int iCurrPOC = pcSlice->getPOC();
594  Int iRefPOC;
595  Int iGOPSize = m_pcCfg->getGOPSize();
596  Int iOffset = (iGOPSize >> 1);
597  Int iMaxSR = m_pcCfg->getSearchRange();
598  Int iNumPredDir = pcSlice->isInterP() ? 1 : 2;
599
600  for (Int iDir = 0; iDir <= iNumPredDir; iDir++)
601  {
602    //RefPicList e = (RefPicList)iDir;
603    RefPicList  e = ( iDir ? REF_PIC_LIST_1 : REF_PIC_LIST_0 );
604    for (Int iRefIdx = 0; iRefIdx < pcSlice->getNumRefIdx(e); iRefIdx++)
605    {
606      iRefPOC = pcSlice->getRefPic(e, iRefIdx)->getPOC();
607      Int iNewSR = Clip3(8, iMaxSR, (iMaxSR*ADAPT_SR_SCALE*abs(iCurrPOC - iRefPOC)+iOffset)/iGOPSize);
608      m_pcPredSearch->setAdaptiveSearchRange(iDir, iRefIdx, iNewSR);
609    }
610  }
611}
612
613/**
614 Multi-loop slice encoding for different slice QP
615
616 \param pcPic    picture class
617 */
618Void TEncSlice::precompressSlice( TComPic* pcPic )
619{
620  // if deltaQP RD is not used, simply return
621  if ( m_pcCfg->getDeltaQpRD() == 0 )
622  {
623    return;
624  }
625
626  if ( m_pcCfg->getUseRateCtrl() )
627  {
628    printf( "\nMultiple QP optimization is not allowed when rate control is enabled." );
629    assert(0);
630    return;
631  }
632
633  TComSlice* pcSlice        = pcPic->getSlice(getSliceIdx());
634
635  if (pcSlice->getDependentSliceSegmentFlag())
636  {
637    // if this is a dependent slice segment, then it was optimised
638    // when analysing the entire slice.
639    return;
640  }
641
642  if (pcSlice->getSliceMode()==FIXED_NUMBER_OF_BYTES)
643  {
644    // TODO: investigate use of average cost per CTU so that this Slice Mode can be used.
645    printf( "\nUnable to optimise Slice-level QP if Slice Mode is set to FIXED_NUMBER_OF_BYTES\n" );
646    assert(0);
647    return;
648  }
649
650
651  Double     dPicRdCostBest = MAX_DOUBLE;
652  UInt       uiQpIdxBest = 0;
653
654  Double dFrameLambda;
655#if FULL_NBIT
656  Int    SHIFT_QP = 12 + 6 * (pcSlice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) - 8);
657#else
658  Int    SHIFT_QP = 12;
659#endif
660
661  // set frame lambda
662  if (m_pcCfg->getGOPSize() > 1)
663  {
664    dFrameLambda = 0.68 * pow (2, (m_piRdPicQp[0]  - SHIFT_QP) / 3.0) * (pcSlice->isInterB()? 2 : 1);
665  }
666  else
667  {
668    dFrameLambda = 0.68 * pow (2, (m_piRdPicQp[0] - SHIFT_QP) / 3.0);
669  }
670  m_pcRdCost      ->setFrameLambda(dFrameLambda);
671
672  // for each QP candidate
673  for ( UInt uiQpIdx = 0; uiQpIdx < 2 * m_pcCfg->getDeltaQpRD() + 1; uiQpIdx++ )
674  {
675    pcSlice       ->setSliceQp             ( m_piRdPicQp    [uiQpIdx] );
676#if ADAPTIVE_QP_SELECTION
677    pcSlice       ->setSliceQpBase         ( m_piRdPicQp    [uiQpIdx] );
678#endif
679    setUpLambda(pcSlice, m_pdRdPicLambda[uiQpIdx], m_piRdPicQp    [uiQpIdx]);
680
681    // try compress
682    compressSlice   ( pcPic, true );
683
684#if NH_3D_VSO
685    Dist64 uiPicDist        = m_uiPicDist;
686#else
687    UInt64 uiPicDist        = m_uiPicDist; // Distortion, as calculated by compressSlice.
688    // NOTE: This distortion is the chroma-weighted SSE distortion for the slice.
689    //       Previously a standard SSE distortion was calculated (for the entire frame).
690    //       Which is correct?
691
692    // TODO: Update loop filter, SAO and distortion calculation to work on one slice only.
693    // m_pcGOPEncoder->preLoopFilterPicAll( pcPic, uiPicDist );
694
695#endif
696
697    // compute RD cost and choose the best
698    Double dPicRdCost = m_pcRdCost->calcRdCost64( m_uiPicTotalBits, uiPicDist, true, DF_SSE_FRAME); // NOTE: Is the 'true' parameter really necessary?
699#if H_3D
700    // Above calculation need to be fixed for VSO, including frameLambda value.
701#endif
702
703    if ( dPicRdCost < dPicRdCostBest )
704    {
705      uiQpIdxBest    = uiQpIdx;
706      dPicRdCostBest = dPicRdCost;
707    }
708  }
709
710  // set best values
711  pcSlice       ->setSliceQp             ( m_piRdPicQp    [uiQpIdxBest] );
712#if ADAPTIVE_QP_SELECTION
713  pcSlice       ->setSliceQpBase         ( m_piRdPicQp    [uiQpIdxBest] );
714#endif
715  setUpLambda(pcSlice, m_pdRdPicLambda[uiQpIdxBest], m_piRdPicQp    [uiQpIdxBest]);
716}
717
718Void TEncSlice::calCostSliceI(TComPic* pcPic) // TODO: this only analyses the first slice segment. What about the others?
719{
720  Double            iSumHadSlice      = 0;
721  TComSlice * const pcSlice           = pcPic->getSlice(getSliceIdx());
722  const TComSPS    &sps               = *(pcSlice->getSPS());
723  const Int         shift             = sps.getBitDepth(CHANNEL_TYPE_LUMA)-8;
724  const Int         offset            = (shift>0)?(1<<(shift-1)):0;
725
726  pcSlice->setSliceSegmentBits(0);
727
728  UInt startCtuTsAddr, boundingCtuTsAddr;
729  xDetermineStartAndBoundingCtuTsAddr ( startCtuTsAddr, boundingCtuTsAddr, pcPic );
730
731  for( UInt ctuTsAddr = startCtuTsAddr, ctuRsAddr = pcPic->getPicSym()->getCtuTsToRsAddrMap( startCtuTsAddr);
732       ctuTsAddr < boundingCtuTsAddr;
733       ctuRsAddr = pcPic->getPicSym()->getCtuTsToRsAddrMap(++ctuTsAddr) )
734  {
735    // initialize CU encoder
736    TComDataCU* pCtu = pcPic->getCtu( ctuRsAddr );
737    pCtu->initCtu( pcPic, ctuRsAddr );
738
739    Int height  = min( sps.getMaxCUHeight(),sps.getPicHeightInLumaSamples() - ctuRsAddr / pcPic->getFrameWidthInCtus() * sps.getMaxCUHeight() );
740    Int width   = min( sps.getMaxCUWidth(), sps.getPicWidthInLumaSamples()  - ctuRsAddr % pcPic->getFrameWidthInCtus() * sps.getMaxCUWidth() );
741
742    Int iSumHad = m_pcCuEncoder->updateCtuDataISlice(pCtu, width, height);
743
744    (m_pcRateCtrl->getRCPic()->getLCU(ctuRsAddr)).m_costIntra=(iSumHad+offset)>>shift;
745    iSumHadSlice += (m_pcRateCtrl->getRCPic()->getLCU(ctuRsAddr)).m_costIntra;
746
747  }
748  m_pcRateCtrl->getRCPic()->setTotalIntraCost(iSumHadSlice);
749}
750
751/** \param pcPic   picture class
752 */
753Void TEncSlice::compressSlice( TComPic* pcPic, const Bool bCompressEntireSlice )
754{
755  // if bCompressEntireSlice is true, then the entire slice (not slice segment) is compressed,
756  //   effectively disabling the slice-segment-mode.
757
758  UInt   startCtuTsAddr;
759  UInt   boundingCtuTsAddr;
760  TComSlice* const pcSlice            = pcPic->getSlice(getSliceIdx());
761  pcSlice->setSliceSegmentBits(0);
762  xDetermineStartAndBoundingCtuTsAddr ( startCtuTsAddr, boundingCtuTsAddr, pcPic );
763  if (bCompressEntireSlice)
764  {
765    boundingCtuTsAddr = pcSlice->getSliceCurEndCtuTsAddr();
766    pcSlice->setSliceSegmentCurEndCtuTsAddr(boundingCtuTsAddr);
767  }
768
769  // initialize cost values - these are used by precompressSlice (they should be parameters).
770  m_uiPicTotalBits  = 0;
771  m_dPicRdCost      = 0; // NOTE: This is a write-only variable!
772  m_uiPicDist       = 0;
773
774  m_pcEntropyCoder->setEntropyCoder   ( m_pppcRDSbacCoder[0][CI_CURR_BEST] );
775  m_pcEntropyCoder->resetEntropy      ( pcSlice );
776
777  TEncBinCABAC* pRDSbacCoder = (TEncBinCABAC *) m_pppcRDSbacCoder[0][CI_CURR_BEST]->getEncBinIf();
778  pRDSbacCoder->setBinCountingEnableFlag( false );
779  pRDSbacCoder->setBinsCoded( 0 );
780
781  TComBitCounter  tempBitCounter;
782  const UInt      frameWidthInCtus = pcPic->getPicSym()->getFrameWidthInCtus();
783
784  //------------------------------------------------------------------------------
785  //  Weighted Prediction parameters estimation.
786  //------------------------------------------------------------------------------
787  // calculate AC/DC values for current picture
788  if( pcSlice->getPPS()->getUseWP() || pcSlice->getPPS()->getWPBiPred() )
789  {
790    xCalcACDCParamSlice(pcSlice);
791  }
792
793  const Bool bWp_explicit = (pcSlice->getSliceType()==P_SLICE && pcSlice->getPPS()->getUseWP()) || (pcSlice->getSliceType()==B_SLICE && pcSlice->getPPS()->getWPBiPred());
794
795  if ( bWp_explicit )
796  {
797    //------------------------------------------------------------------------------
798    //  Weighted Prediction implemented at Slice level. SliceMode=2 is not supported yet.
799    //------------------------------------------------------------------------------
800    if ( pcSlice->getSliceMode()==FIXED_NUMBER_OF_BYTES || pcSlice->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES )
801    {
802      printf("Weighted Prediction is not supported with slice mode determined by max number of bins.\n"); exit(0);
803    }
804
805    xEstimateWPParamSlice( pcSlice );
806    pcSlice->initWpScaling(pcSlice->getSPS());
807
808    // check WP on/off
809    xCheckWPEnable( pcSlice );
810  }
811
812#if ADAPTIVE_QP_SELECTION
813  if( m_pcCfg->getUseAdaptQpSelect() && !(pcSlice->getDependentSliceSegmentFlag()))
814  {
815    // TODO: this won't work with dependent slices: they do not have their own QP. Check fix to mask clause execution with && !(pcSlice->getDependentSliceSegmentFlag())
816    m_pcTrQuant->clearSliceARLCnt(); // TODO: this looks wrong for multiple slices - the results of all but the last slice will be cleared before they are used (all slices compressed, and then all slices encoded)
817    if(pcSlice->getSliceType()!=I_SLICE)
818    {
819      Int qpBase = pcSlice->getSliceQpBase();
820      pcSlice->setSliceQp(qpBase + m_pcTrQuant->getQpDelta(qpBase));
821    }
822  }
823#endif
824
825#if H_3D_IC
826  if ( pcEncTop->getViewIndex() && pcEncTop->getUseIC() &&
827       !( ( pcSlice->getSliceType() == P_SLICE && pcSlice->getPPS()->getUseWP() ) || ( pcSlice->getSliceType() == B_SLICE && pcSlice->getPPS()->getWPBiPred() ) )
828     )
829  {
830    pcSlice ->xSetApplyIC(pcEncTop->getUseICLowLatencyEnc());
831    if ( pcSlice->getApplyIC() )
832    {
833      pcSlice->setIcSkipParseFlag( pcSlice->getPOC() % m_pcCfg->getIntraPeriod() != 0 );
834    }
835  }
836#endif
837
838
839
840  // Adjust initial state if this is the start of a dependent slice.
841  {
842    const UInt      ctuRsAddr               = pcPic->getPicSym()->getCtuTsToRsAddrMap( startCtuTsAddr);
843    const UInt      currentTileIdx          = pcPic->getPicSym()->getTileIdxMap(ctuRsAddr);
844    const TComTile *pCurrentTile            = pcPic->getPicSym()->getTComTile(currentTileIdx);
845    const UInt      firstCtuRsAddrOfTile    = pCurrentTile->getFirstCtuRsAddr();
846    if( pcSlice->getDependentSliceSegmentFlag() && ctuRsAddr != firstCtuRsAddrOfTile )
847    {
848      // This will only occur if dependent slice-segments (m_entropyCodingSyncContextState=true) are being used.
849      if( pCurrentTile->getTileWidthInCtus() >= 2 || !m_pcCfg->getWaveFrontsynchro() )
850      {
851        m_pppcRDSbacCoder[0][CI_CURR_BEST]->loadContexts( &m_lastSliceSegmentEndContextState );
852      }
853    }
854  }
855
856  // for every CTU in the slice segment (may terminate sooner if there is a byte limit on the slice-segment)
857#if NH_3D_VSO
858  Int iLastPosY = -1;
859#endif
860
861  for( UInt ctuTsAddr = startCtuTsAddr; ctuTsAddr < boundingCtuTsAddr; ++ctuTsAddr )
862  {
863    const UInt ctuRsAddr = pcPic->getPicSym()->getCtuTsToRsAddrMap(ctuTsAddr);
864    // initialize CTU encoder
865    TComDataCU* pCtu = pcPic->getCtu( ctuRsAddr );
866    pCtu->initCtu( pcPic, ctuRsAddr );
867#if NH_3D_VSO
868    if ( m_pcRdCost->getUseRenModel() )
869    {
870      // updated renderer model if necessary
871      Int iCurPosX;
872      Int iCurPosY; 
873      pCtu->getPosInPic(0, iCurPosX, iCurPosY );
874      if ( iCurPosY != iLastPosY )
875      {
876        iLastPosY = iCurPosY;         
877        TEncTop* pcEncTop = (TEncTop*) m_pcCfg; // Fix this later.
878        pcEncTop->setupRenModel( pcSlice->getPOC() , pcSlice->getViewIndex(), pcSlice->getIsDepth() ? 1 : 0, iCurPosY, pcSlice->getSPS()->getMaxCUHeight() );
879      }
880    }
881#endif
882
883    // update CABAC state
884    const UInt firstCtuRsAddrOfTile = pcPic->getPicSym()->getTComTile(pcPic->getPicSym()->getTileIdxMap(ctuRsAddr))->getFirstCtuRsAddr();
885    const UInt tileXPosInCtus = firstCtuRsAddrOfTile % frameWidthInCtus;
886    const UInt ctuXPosInCtus  = ctuRsAddr % frameWidthInCtus;
887   
888    if (ctuRsAddr == firstCtuRsAddrOfTile)
889    {
890      m_pppcRDSbacCoder[0][CI_CURR_BEST]->resetEntropy(pcSlice);
891    }
892    else if ( ctuXPosInCtus == tileXPosInCtus && m_pcCfg->getWaveFrontsynchro())
893    {
894      // reset and then update contexts to the state at the end of the top-right CTU (if within current slice and tile).
895      m_pppcRDSbacCoder[0][CI_CURR_BEST]->resetEntropy(pcSlice);
896      // Sync if the Top-Right is available.
897      TComDataCU *pCtuUp = pCtu->getCtuAbove();
898      if ( pCtuUp && ((ctuRsAddr%frameWidthInCtus+1) < frameWidthInCtus)  )
899      {
900        TComDataCU *pCtuTR = pcPic->getCtu( ctuRsAddr - frameWidthInCtus + 1 );
901        if ( pCtu->CUIsFromSameSliceAndTile(pCtuTR) )
902        {
903          // Top-Right is available, we use it.
904          m_pppcRDSbacCoder[0][CI_CURR_BEST]->loadContexts( &m_entropyCodingSyncContextState );
905        }
906      }
907    }
908
909    // set go-on entropy coder (used for all trial encodings - the cu encoder and encoder search also have a copy of the same pointer)
910    m_pcEntropyCoder->setEntropyCoder ( m_pcRDGoOnSbacCoder );
911    m_pcEntropyCoder->setBitstream( &tempBitCounter );
912    tempBitCounter.resetBits();
913    m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[0][CI_CURR_BEST] ); // this copy is not strictly necessary here, but indicates that the GoOnSbacCoder
914                                                                     // is reset to a known state before every decision process.
915
916    ((TEncBinCABAC*)m_pcRDGoOnSbacCoder->getEncBinIf())->setBinCountingEnableFlag(true);
917
918    Double oldLambda = m_pcRdCost->getLambda();
919    if ( m_pcCfg->getUseRateCtrl() )
920    {
921      Int estQP        = pcSlice->getSliceQp();
922      Double estLambda = -1.0;
923      Double bpp       = -1.0;
924
925      if ( ( pcPic->getSlice( 0 )->getSliceType() == I_SLICE && m_pcCfg->getForceIntraQP() ) || !m_pcCfg->getLCULevelRC() )
926      {
927        estQP = pcSlice->getSliceQp();
928      }
929      else
930      {
931#if KWU_RC_MADPRED_E0227
932          if(pcSlice->getLayerId() != 0 && m_pcCfg->getUseDepthMADPred() && !pcSlice->getIsDepth())
933          {
934            Double zn, zf, focallength, position, camShift;
935            Double basePos;
936            Bool bInterpolated;
937            Int direction = pcSlice->getViewId() - pcCU->getSlice()->getIvPic(false, 0)->getViewId();
938            Int disparity;
939
940            pcEncTop->getCamParam()->xGetZNearZFar(pcEncTop->getCamParam()->getBaseViewNumbers()[pcSlice->getViewIndex()], pcSlice->getPOC(), zn, zf);
941            pcEncTop->getCamParam()->xGetGeometryData(pcEncTop->getCamParam()->getBaseViewNumbers()[0], pcSlice->getPOC(), focallength, basePos, camShift, bInterpolated);
942            pcEncTop->getCamParam()->xGetGeometryData(pcEncTop->getCamParam()->getBaseViewNumbers()[pcSlice->getViewIndex()], pcSlice->getPOC(), focallength, position, camShift, bInterpolated);
943            bpp       = m_pcRateCtrl->getRCPic()->getLCUTargetBppforInterView( m_pcRateCtrl->getPicList(), pcCU,
944              basePos, position, focallength, zn, zf, (direction > 0 ? 1 : -1), &disparity );
945          }
946          else
947          {
948#endif
949        bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType());
950        if ( pcPic->getSlice( 0 )->getSliceType() == I_SLICE)
951        {
952          estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP);
953        }
954        else
955        {
956          estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );
957          estQP     = m_pcRateCtrl->getRCPic()->getLCUEstQP    ( estLambda, pcSlice->getSliceQp() );
958        }
959#if KWU_RC_MADPRED_E0227
960          estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );
961          estQP     = m_pcRateCtrl->getRCPic()->getLCUEstQP    ( estLambda, pcSlice->getSliceQp() );
962#endif
963
964        estQP     = Clip3( -pcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, estQP );
965
966        m_pcRdCost->setLambda(estLambda, pcSlice->getSPS()->getBitDepths());
967
968#if RDOQ_CHROMA_LAMBDA
969        // set lambda for RDOQ
970        const Double chromaLambda = estLambda / m_pcRdCost->getChromaWeight();
971        const Double lambdaArray[MAX_NUM_COMPONENT] = { estLambda, chromaLambda, chromaLambda };
972        m_pcTrQuant->setLambdas( lambdaArray );
973#else
974        m_pcTrQuant->setLambda( estLambda );
975#endif
976      }
977
978      m_pcRateCtrl->setRCQP( estQP );
979#if ADAPTIVE_QP_SELECTION
980      pCtu->getSlice()->setSliceQpBase( estQP );
981#endif
982    }
983
984    // run CTU trial encoder
985    m_pcCuEncoder->compressCtu( pCtu );
986
987
988    // All CTU decisions have now been made. Restore entropy coder to an initial stage, ready to make a true encode,
989    // which will result in the state of the contexts being correct. It will also count up the number of bits coded,
990    // which is used if there is a limit of the number of bytes per slice-segment.
991
992    m_pcEntropyCoder->setEntropyCoder ( m_pppcRDSbacCoder[0][CI_CURR_BEST] );
993    m_pcEntropyCoder->setBitstream( &tempBitCounter );
994    pRDSbacCoder->setBinCountingEnableFlag( true );
995    m_pppcRDSbacCoder[0][CI_CURR_BEST]->resetBits();
996    pRDSbacCoder->setBinsCoded( 0 );
997
998    // encode CTU and calculate the true bit counters.
999    m_pcCuEncoder->encodeCtu( pCtu );
1000
1001
1002    pRDSbacCoder->setBinCountingEnableFlag( false );
1003
1004    const Int numberOfWrittenBits = m_pcEntropyCoder->getNumberOfWrittenBits();
1005
1006    // Calculate if this CTU puts us over slice bit size.
1007    // cannot terminate if current slice/slice-segment would be 0 Ctu in size,
1008    const UInt validEndOfSliceCtuTsAddr = ctuTsAddr + (ctuTsAddr == startCtuTsAddr ? 1 : 0);
1009    // Set slice end parameter
1010    if(pcSlice->getSliceMode()==FIXED_NUMBER_OF_BYTES && pcSlice->getSliceBits()+numberOfWrittenBits > (pcSlice->getSliceArgument()<<3))
1011    {
1012      pcSlice->setSliceSegmentCurEndCtuTsAddr(validEndOfSliceCtuTsAddr);
1013      pcSlice->setSliceCurEndCtuTsAddr(validEndOfSliceCtuTsAddr);
1014      boundingCtuTsAddr=validEndOfSliceCtuTsAddr;
1015    }
1016    else if((!bCompressEntireSlice) && pcSlice->getSliceSegmentMode()==FIXED_NUMBER_OF_BYTES && pcSlice->getSliceSegmentBits()+numberOfWrittenBits > (pcSlice->getSliceSegmentArgument()<<3))
1017    {
1018      pcSlice->setSliceSegmentCurEndCtuTsAddr(validEndOfSliceCtuTsAddr);
1019      boundingCtuTsAddr=validEndOfSliceCtuTsAddr;
1020    }
1021
1022    if (boundingCtuTsAddr <= ctuTsAddr)
1023    {
1024      break;
1025    }
1026
1027    pcSlice->setSliceBits( (UInt)(pcSlice->getSliceBits() + numberOfWrittenBits) );
1028    pcSlice->setSliceSegmentBits(pcSlice->getSliceSegmentBits()+numberOfWrittenBits);
1029
1030    // Store probabilities of second CTU in line into buffer - used only if wavefront-parallel-processing is enabled.
1031    if ( ctuXPosInCtus == tileXPosInCtus+1 && m_pcCfg->getWaveFrontsynchro())
1032    {
1033      m_entropyCodingSyncContextState.loadContexts(m_pppcRDSbacCoder[0][CI_CURR_BEST]);
1034    }
1035
1036
1037    if ( m_pcCfg->getUseRateCtrl() )
1038    {
1039#if KWU_RC_MADPRED_E0227
1040        UInt SAD    = m_pcCuEncoder->getLCUPredictionSAD();
1041        Int height  = min( pcSlice->getSPS()->getMaxCUHeight(),pcSlice->getSPS()->getPicHeightInLumaSamples() - uiCUAddr / rpcPic->getFrameWidthInCU() * pcSlice->getSPS()->getMaxCUHeight() );
1042        Int width   = min( pcSlice->getSPS()->getMaxCUWidth(),pcSlice->getSPS()->getPicWidthInLumaSamples() - uiCUAddr % rpcPic->getFrameWidthInCU() * pcSlice->getSPS()->getMaxCUWidth() );
1043        Double MAD = (Double)SAD / (Double)(height * width);
1044        MAD = MAD * MAD;
1045        ( m_pcRateCtrl->getRCPic()->getLCU(uiCUAddr) ).m_MAD = MAD;
1046#endif
1047
1048      Int actualQP        = g_RCInvalidQPValue;
1049      Double actualLambda = m_pcRdCost->getLambda();
1050      Int actualBits      = pCtu->getTotalBits();
1051      Int numberOfEffectivePixels    = 0;
1052      for ( Int idx = 0; idx < pcPic->getNumPartitionsInCtu(); idx++ )
1053      {
1054        if ( pCtu->getPredictionMode( idx ) != NUMBER_OF_PREDICTION_MODES && ( !pCtu->isSkipped( idx ) ) )
1055        {
1056          numberOfEffectivePixels = numberOfEffectivePixels + 16;
1057          break;
1058        }
1059      }
1060
1061      if ( numberOfEffectivePixels == 0 )
1062      {
1063        actualQP = g_RCInvalidQPValue;
1064      }
1065      else
1066      {
1067        actualQP = pCtu->getQP( 0 );
1068      }
1069      m_pcRdCost->setLambda(oldLambda, pcSlice->getSPS()->getBitDepths());
1070      m_pcRateCtrl->getRCPic()->updateAfterCTU( m_pcRateCtrl->getRCPic()->getLCUCoded(), actualBits, actualQP, actualLambda,
1071                                                pCtu->getSlice()->getSliceType() == I_SLICE ? 0 : m_pcCfg->getLCULevelRC() );
1072    }
1073
1074    m_uiPicTotalBits += pCtu->getTotalBits();
1075    m_dPicRdCost     += pCtu->getTotalCost();
1076    m_uiPicDist      += pCtu->getTotalDistortion();
1077  }
1078
1079  // store context state at the end of this slice-segment, in case the next slice is a dependent slice and continues using the CABAC contexts.
1080  if( pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag() )
1081  {
1082    m_lastSliceSegmentEndContextState.loadContexts( m_pppcRDSbacCoder[0][CI_CURR_BEST] );//ctx end of dep.slice
1083  }
1084
1085  // stop use of temporary bit counter object.
1086  m_pppcRDSbacCoder[0][CI_CURR_BEST]->setBitstream(NULL);
1087  m_pcRDGoOnSbacCoder->setBitstream(NULL); // stop use of tempBitCounter.
1088
1089  // TODO: optimise cabac_init during compress slice to improve multi-slice operation
1090  //if (pcSlice->getPPS()->getCabacInitPresentFlag() && !pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag())
1091  //{
1092  //  m_encCABACTableIdx = m_pcEntropyCoder->determineCabacInitIdx();
1093  //}
1094  //else
1095  //{
1096  //  m_encCABACTableIdx = pcSlice->getSliceType();
1097  //}
1098}
1099
1100Void TEncSlice::encodeSlice   ( TComPic* pcPic, TComOutputBitstream* pcSubstreams, UInt &numBinsCoded )
1101{
1102  TComSlice *const pcSlice           = pcPic->getSlice(getSliceIdx());
1103
1104  const UInt startCtuTsAddr          = pcSlice->getSliceSegmentCurStartCtuTsAddr();
1105  const UInt boundingCtuTsAddr       = pcSlice->getSliceSegmentCurEndCtuTsAddr();
1106
1107  const UInt frameWidthInCtus        = pcPic->getPicSym()->getFrameWidthInCtus();
1108  const Bool depSliceSegmentsEnabled = pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag();
1109  const Bool wavefrontsEnabled       = pcSlice->getPPS()->getEntropyCodingSyncEnabledFlag();
1110
1111  // initialise entropy coder for the slice
1112  m_pcSbacCoder->init( (TEncBinIf*)m_pcBinCABAC );
1113  m_pcEntropyCoder->setEntropyCoder ( m_pcSbacCoder );
1114  m_pcEntropyCoder->resetEntropy    ( pcSlice );
1115
1116  numBinsCoded = 0;
1117  m_pcBinCABAC->setBinCountingEnableFlag( true );
1118  m_pcBinCABAC->setBinsCoded(0);
1119
1120#if ENC_DEC_TRACE
1121  g_bJustDoIt = g_bEncDecTraceEnable;
1122#endif
1123  DTRACE_CABAC_VL( g_nSymbolCounter++ );
1124  DTRACE_CABAC_T( "\tPOC: " );
1125  DTRACE_CABAC_V( pcPic->getPOC() );
1126#if H_MV_ENC_DEC_TRAC
1127  DTRACE_CABAC_T( " Layer: " );
1128  DTRACE_CABAC_V( pcPic->getLayerId() );
1129#endif
1130  DTRACE_CABAC_T( "\n" );
1131#if ENC_DEC_TRACE
1132  g_bJustDoIt = g_bEncDecTraceDisable;
1133#endif
1134
1135
1136  if (depSliceSegmentsEnabled)
1137  {
1138    // modify initial contexts with previous slice segment if this is a dependent slice.
1139    const UInt ctuRsAddr        = pcPic->getPicSym()->getCtuTsToRsAddrMap( startCtuTsAddr );
1140    const UInt currentTileIdx=pcPic->getPicSym()->getTileIdxMap(ctuRsAddr);
1141    const TComTile *pCurrentTile=pcPic->getPicSym()->getTComTile(currentTileIdx);
1142    const UInt firstCtuRsAddrOfTile = pCurrentTile->getFirstCtuRsAddr();
1143
1144    if( pcSlice->getDependentSliceSegmentFlag() && ctuRsAddr != firstCtuRsAddrOfTile )
1145    {
1146      if( pCurrentTile->getTileWidthInCtus() >= 2 || !wavefrontsEnabled )
1147      {
1148        m_pcSbacCoder->loadContexts(&m_lastSliceSegmentEndContextState);
1149      }
1150    }
1151  }
1152
1153  // for every CTU in the slice segment...
1154
1155  for( UInt ctuTsAddr = startCtuTsAddr; ctuTsAddr < boundingCtuTsAddr; ++ctuTsAddr )
1156  {
1157    const UInt ctuRsAddr = pcPic->getPicSym()->getCtuTsToRsAddrMap(ctuTsAddr);
1158    const TComTile &currentTile = *(pcPic->getPicSym()->getTComTile(pcPic->getPicSym()->getTileIdxMap(ctuRsAddr)));
1159    const UInt firstCtuRsAddrOfTile = currentTile.getFirstCtuRsAddr();
1160    const UInt tileXPosInCtus       = firstCtuRsAddrOfTile % frameWidthInCtus;
1161    const UInt tileYPosInCtus       = firstCtuRsAddrOfTile / frameWidthInCtus;
1162    const UInt ctuXPosInCtus        = ctuRsAddr % frameWidthInCtus;
1163    const UInt ctuYPosInCtus        = ctuRsAddr / frameWidthInCtus;
1164    const UInt uiSubStrm=pcPic->getSubstreamForCtuAddr(ctuRsAddr, true, pcSlice);
1165    TComDataCU* pCtu = pcPic->getCtu( ctuRsAddr );
1166
1167    m_pcEntropyCoder->setBitstream( &pcSubstreams[uiSubStrm] );
1168
1169    // set up CABAC contexts' state for this CTU
1170    if (ctuRsAddr == firstCtuRsAddrOfTile)
1171    {
1172      if (ctuTsAddr != startCtuTsAddr) // if it is the first CTU, then the entropy coder has already been reset
1173      {
1174        m_pcEntropyCoder->resetEntropy(pcSlice);
1175      }
1176    }
1177    else if (ctuXPosInCtus == tileXPosInCtus && wavefrontsEnabled)
1178    {
1179      // Synchronize cabac probabilities with upper-right CTU if it's available and at the start of a line.
1180      if (ctuTsAddr != startCtuTsAddr) // if it is the first CTU, then the entropy coder has already been reset
1181      {
1182        m_pcEntropyCoder->resetEntropy(pcSlice);
1183      }
1184      TComDataCU *pCtuUp = pCtu->getCtuAbove();
1185      if ( pCtuUp && ((ctuRsAddr%frameWidthInCtus+1) < frameWidthInCtus)  )
1186      {
1187        TComDataCU *pCtuTR = pcPic->getCtu( ctuRsAddr - frameWidthInCtus + 1 );
1188        if ( pCtu->CUIsFromSameSliceAndTile(pCtuTR) )
1189        {
1190          // Top-right is available, so use it.
1191          m_pcSbacCoder->loadContexts( &m_entropyCodingSyncContextState );
1192        }
1193      }
1194    }
1195
1196#if H_3D_QTLPC
1197    rpcPic->setReduceBitsFlag(true);
1198#endif
1199    if ( pcSlice->getSPS()->getUseSAO() )
1200    {
1201      Bool bIsSAOSliceEnabled = false;
1202      Bool sliceEnabled[MAX_NUM_COMPONENT];
1203      for(Int comp=0; comp < MAX_NUM_COMPONENT; comp++)
1204      {
1205        ComponentID compId=ComponentID(comp);
1206        sliceEnabled[compId] = pcSlice->getSaoEnabledFlag(toChannelType(compId)) && (comp < pcPic->getNumberValidComponents());
1207        if (sliceEnabled[compId])
1208        {
1209          bIsSAOSliceEnabled=true;
1210        }
1211      }
1212      if (bIsSAOSliceEnabled)
1213      {
1214        SAOBlkParam& saoblkParam = (pcPic->getPicSym()->getSAOBlkParam())[ctuRsAddr];
1215
1216        Bool leftMergeAvail = false;
1217        Bool aboveMergeAvail= false;
1218        //merge left condition
1219        Int rx = (ctuRsAddr % frameWidthInCtus);
1220        if(rx > 0)
1221        {
1222          leftMergeAvail = pcPic->getSAOMergeAvailability(ctuRsAddr, ctuRsAddr-1);
1223        }
1224
1225        //merge up condition
1226        Int ry = (ctuRsAddr / frameWidthInCtus);
1227        if(ry > 0)
1228        {
1229          aboveMergeAvail = pcPic->getSAOMergeAvailability(ctuRsAddr, ctuRsAddr-frameWidthInCtus);
1230        }
1231
1232        m_pcEntropyCoder->encodeSAOBlkParam(saoblkParam, pcPic->getPicSym()->getSPS().getBitDepths(), sliceEnabled, leftMergeAvail, aboveMergeAvail);
1233      }
1234    }
1235
1236#if ENC_DEC_TRACE
1237    g_bJustDoIt = g_bEncDecTraceEnable;
1238#endif
1239      m_pcCuEncoder->encodeCtu( pCtu );
1240#if ENC_DEC_TRACE
1241    g_bJustDoIt = g_bEncDecTraceDisable;
1242#endif
1243
1244    //Store probabilities of second CTU in line into buffer
1245    if ( ctuXPosInCtus == tileXPosInCtus+1 && wavefrontsEnabled)
1246    {
1247      m_entropyCodingSyncContextState.loadContexts( m_pcSbacCoder );
1248    }
1249
1250    // terminate the sub-stream, if required (end of slice-segment, end of tile, end of wavefront-CTU-row):
1251    if (ctuTsAddr+1 == boundingCtuTsAddr ||
1252         (  ctuXPosInCtus + 1 == tileXPosInCtus + currentTile.getTileWidthInCtus() &&
1253          ( ctuYPosInCtus + 1 == tileYPosInCtus + currentTile.getTileHeightInCtus() || wavefrontsEnabled)
1254         )
1255       )
1256    {
1257      m_pcEntropyCoder->encodeTerminatingBit(1);
1258      m_pcEntropyCoder->encodeSliceFinish();
1259      // Byte-alignment in slice_data() when new tile
1260      pcSubstreams[uiSubStrm].writeByteAlignment();
1261
1262      // write sub-stream size
1263      if (ctuTsAddr+1 != boundingCtuTsAddr)
1264      {
1265        pcSlice->addSubstreamSize( (pcSubstreams[uiSubStrm].getNumberOfWrittenBits() >> 3) + pcSubstreams[uiSubStrm].countStartCodeEmulations() );
1266      }
1267    }
1268#if H_3D_QTLPC
1269    rpcPic->setReduceBitsFlag(false);
1270#endif
1271  } // CTU-loop
1272
1273  if( depSliceSegmentsEnabled )
1274  {
1275    m_lastSliceSegmentEndContextState.loadContexts( m_pcSbacCoder );//ctx end of dep.slice
1276  }
1277
1278#if ADAPTIVE_QP_SELECTION
1279  if( m_pcCfg->getUseAdaptQpSelect() )
1280  {
1281    m_pcTrQuant->storeSliceQpNext(pcSlice); // TODO: this will only be storing the adaptive QP state of the very last slice-segment that is not dependent in the frame... Perhaps this should be moved to the compress slice loop.
1282  }
1283#endif
1284
1285  if (pcSlice->getPPS()->getCabacInitPresentFlag() && !pcSlice->getPPS()->getDependentSliceSegmentsEnabledFlag())
1286  {
1287    m_encCABACTableIdx = m_pcEntropyCoder->determineCabacInitIdx(pcSlice);
1288  }
1289  else
1290  {
1291    m_encCABACTableIdx = pcSlice->getSliceType();
1292  }
1293 
1294  numBinsCoded = m_pcBinCABAC->getBinsCoded();
1295}
1296
1297Void TEncSlice::calculateBoundingCtuTsAddrForSlice(UInt &startCtuTSAddrSlice, UInt &boundingCtuTSAddrSlice, Bool &haveReachedTileBoundary,
1298                                                   TComPic* pcPic, const Int sliceMode, const Int sliceArgument)
1299{
1300  TComSlice* pcSlice = pcPic->getSlice(getSliceIdx());
1301  const UInt numberOfCtusInFrame = pcPic->getNumberOfCtusInFrame();
1302  const TComPPS &pps=*(pcSlice->getPPS());
1303  boundingCtuTSAddrSlice=0;
1304  haveReachedTileBoundary=false;
1305
1306  switch (sliceMode)
1307  {
1308    case FIXED_NUMBER_OF_CTU:
1309      {
1310        UInt ctuAddrIncrement    = sliceArgument;
1311        boundingCtuTSAddrSlice  = ((startCtuTSAddrSlice + ctuAddrIncrement) < numberOfCtusInFrame) ? (startCtuTSAddrSlice + ctuAddrIncrement) : numberOfCtusInFrame;
1312      }
1313      break;
1314    case FIXED_NUMBER_OF_BYTES:
1315      boundingCtuTSAddrSlice  = numberOfCtusInFrame; // This will be adjusted later if required.
1316      break;
1317    case FIXED_NUMBER_OF_TILES:
1318      {
1319        const UInt tileIdx        = pcPic->getPicSym()->getTileIdxMap( pcPic->getPicSym()->getCtuTsToRsAddrMap(startCtuTSAddrSlice) );
1320        const UInt tileTotalCount = (pcPic->getPicSym()->getNumTileColumnsMinus1()+1) * (pcPic->getPicSym()->getNumTileRowsMinus1()+1);
1321        UInt ctuAddrIncrement   = 0;
1322
1323        for(UInt tileIdxIncrement = 0; tileIdxIncrement < sliceArgument; tileIdxIncrement++)
1324        {
1325          if((tileIdx + tileIdxIncrement) < tileTotalCount)
1326          {
1327            UInt tileWidthInCtus   = pcPic->getPicSym()->getTComTile(tileIdx + tileIdxIncrement)->getTileWidthInCtus();
1328            UInt tileHeightInCtus  = pcPic->getPicSym()->getTComTile(tileIdx + tileIdxIncrement)->getTileHeightInCtus();
1329            ctuAddrIncrement    += (tileWidthInCtus * tileHeightInCtus);
1330          }
1331        }
1332
1333        boundingCtuTSAddrSlice  = ((startCtuTSAddrSlice + ctuAddrIncrement) < numberOfCtusInFrame) ? (startCtuTSAddrSlice + ctuAddrIncrement) : numberOfCtusInFrame;
1334      }
1335      break;
1336    default:
1337      boundingCtuTSAddrSlice    = numberOfCtusInFrame;
1338      break;
1339  }
1340
1341  // Adjust for tiles and wavefronts.
1342  const Bool wavefrontsAreEnabled = pps.getEntropyCodingSyncEnabledFlag();
1343
1344  if ((sliceMode == FIXED_NUMBER_OF_CTU || sliceMode == FIXED_NUMBER_OF_BYTES) &&
1345      (pps.getNumTileRowsMinus1() > 0 || pps.getNumTileColumnsMinus1() > 0))
1346  {
1347    const UInt ctuRSAddr                  = pcPic->getPicSym()->getCtuTsToRsAddrMap(startCtuTSAddrSlice);
1348    const UInt startTileIdx               = pcPic->getPicSym()->getTileIdxMap(ctuRSAddr);
1349
1350    const TComTile *pStartingTile         = pcPic->getPicSym()->getTComTile(startTileIdx);
1351    const UInt tileStartTsAddr            = pcPic->getPicSym()->getCtuRsToTsAddrMap(pStartingTile->getFirstCtuRsAddr());
1352    const UInt tileStartWidth             = pStartingTile->getTileWidthInCtus();
1353    const UInt tileStartHeight            = pStartingTile->getTileHeightInCtus();
1354    const UInt tileLastTsAddr_excl        = tileStartTsAddr + tileStartWidth*tileStartHeight;
1355    const UInt tileBoundingCtuTsAddrSlice = tileLastTsAddr_excl;
1356
1357    const UInt ctuColumnOfStartingTile    = ((startCtuTSAddrSlice-tileStartTsAddr)%tileStartWidth);
1358    if (wavefrontsAreEnabled && ctuColumnOfStartingTile!=0)
1359    {
1360      // WPP: if a slice does not start at the beginning of a CTB row, it must end within the same CTB row
1361      const UInt numberOfCTUsToEndOfRow            = tileStartWidth - ctuColumnOfStartingTile;
1362      const UInt wavefrontTileBoundingCtuAddrSlice = startCtuTSAddrSlice + numberOfCTUsToEndOfRow;
1363      if (wavefrontTileBoundingCtuAddrSlice < boundingCtuTSAddrSlice)
1364      {
1365        boundingCtuTSAddrSlice = wavefrontTileBoundingCtuAddrSlice;
1366      }
1367    }
1368
1369    if (tileBoundingCtuTsAddrSlice < boundingCtuTSAddrSlice)
1370    {
1371      boundingCtuTSAddrSlice = tileBoundingCtuTsAddrSlice;
1372      haveReachedTileBoundary = true;
1373    }
1374  }
1375  else if ((sliceMode == FIXED_NUMBER_OF_CTU || sliceMode == FIXED_NUMBER_OF_BYTES) && wavefrontsAreEnabled && ((startCtuTSAddrSlice % pcPic->getFrameWidthInCtus()) != 0))
1376  {
1377    // Adjust for wavefronts (no tiles).
1378    // WPP: if a slice does not start at the beginning of a CTB row, it must end within the same CTB row
1379    boundingCtuTSAddrSlice = min(boundingCtuTSAddrSlice, startCtuTSAddrSlice - (startCtuTSAddrSlice % pcPic->getFrameWidthInCtus()) + (pcPic->getFrameWidthInCtus()));
1380  }
1381}
1382
1383/** Determines the starting and bounding CTU address of current slice / dependent slice
1384 * \param [out] startCtuTsAddr
1385 * \param [out] boundingCtuTsAddr
1386 * \param [in]  pcPic
1387
1388 * Updates startCtuTsAddr, boundingCtuTsAddr with appropriate CTU address
1389 */
1390Void TEncSlice::xDetermineStartAndBoundingCtuTsAddr  ( UInt& startCtuTsAddr, UInt& boundingCtuTsAddr, TComPic* pcPic )
1391{
1392  TComSlice* pcSlice                 = pcPic->getSlice(getSliceIdx());
1393
1394  // Non-dependent slice
1395  UInt startCtuTsAddrSlice           = pcSlice->getSliceCurStartCtuTsAddr();
1396  Bool haveReachedTileBoundarySlice  = false;
1397  UInt boundingCtuTsAddrSlice;
1398  calculateBoundingCtuTsAddrForSlice(startCtuTsAddrSlice, boundingCtuTsAddrSlice, haveReachedTileBoundarySlice, pcPic,
1399                                     m_pcCfg->getSliceMode(), m_pcCfg->getSliceArgument());
1400  pcSlice->setSliceCurEndCtuTsAddr(   boundingCtuTsAddrSlice );
1401  pcSlice->setSliceCurStartCtuTsAddr( startCtuTsAddrSlice    );
1402
1403  // Dependent slice
1404  UInt startCtuTsAddrSliceSegment          = pcSlice->getSliceSegmentCurStartCtuTsAddr();
1405  Bool haveReachedTileBoundarySliceSegment = false;
1406  UInt boundingCtuTsAddrSliceSegment;
1407  calculateBoundingCtuTsAddrForSlice(startCtuTsAddrSliceSegment, boundingCtuTsAddrSliceSegment, haveReachedTileBoundarySliceSegment, pcPic,
1408                                     m_pcCfg->getSliceSegmentMode(), m_pcCfg->getSliceSegmentArgument());
1409  if (boundingCtuTsAddrSliceSegment>boundingCtuTsAddrSlice)
1410  {
1411    boundingCtuTsAddrSliceSegment = boundingCtuTsAddrSlice;
1412  }
1413  pcSlice->setSliceSegmentCurEndCtuTsAddr( boundingCtuTsAddrSliceSegment );
1414  pcSlice->setSliceSegmentCurStartCtuTsAddr(startCtuTsAddrSliceSegment);
1415
1416  // Make a joint decision based on reconstruction and dependent slice bounds
1417  startCtuTsAddr    = max(startCtuTsAddrSlice   , startCtuTsAddrSliceSegment   );
1418  boundingCtuTsAddr = boundingCtuTsAddrSliceSegment;
1419}
1420
1421Double TEncSlice::xGetQPValueAccordingToLambda ( Double lambda )
1422{
1423  return 4.2005*log(lambda) + 13.7122;
1424}
1425
1426//! \}
Note: See TracBrowser for help on using the repository browser.