source: SHVCSoftware/branches/SHM-upgrade/source/Lib/TLibCommon/TComPicSym.cpp @ 915

Last change on this file since 915 was 823, checked in by seregin, 10 years ago

update to HM-15.0

  • Property svn:eol-style set to native
File size: 17.5 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-2014, 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     TComPicSym.cpp
35    \brief    picture symbol class
36*/
37
38#include "TComPicSym.h"
39#include "TComSampleAdaptiveOffset.h"
40#include "TComSlice.h"
41
42//! \ingroup TLibCommon
43//! \{
44
45// ====================================================================================================================
46// Constructor / destructor / create / destroy
47// ====================================================================================================================
48
49TComPicSym::TComPicSym()
50:m_uiWidthInCU(0)
51,m_uiHeightInCU(0)
52,m_uiMaxCUWidth(0)
53,m_uiMaxCUHeight(0)
54,m_uiMinCUWidth(0)
55,m_uiMinCUHeight(0)
56,m_uhTotalDepth(0)
57,m_uiNumPartitions(0)
58,m_uiNumPartInWidth(0)
59,m_uiNumPartInHeight(0)
60,m_uiNumCUsInFrame(0)
61,m_apcTComSlice(NULL)
62,m_uiNumAllocatedSlice (0)
63,m_apcTComDataCU (NULL)
64,m_iNumColumnsMinus1 (0)
65,m_iNumRowsMinus1(0)
66,m_puiCUOrderMap(0)
67,m_puiTileIdxMap(NULL)
68,m_puiInverseCUOrderMap(NULL)
69,m_saoBlkParams(NULL)
70{};
71
72
73Void TComPicSym::create  ( Int iPicWidth, Int iPicHeight, UInt uiMaxWidth, UInt uiMaxHeight, UInt uiMaxDepth )
74{
75  UInt i;
76
77  m_uhTotalDepth      = uiMaxDepth;
78  m_uiNumPartitions   = 1<<(m_uhTotalDepth<<1);
79 
80  m_uiMaxCUWidth      = uiMaxWidth;
81  m_uiMaxCUHeight     = uiMaxHeight;
82 
83  m_uiMinCUWidth      = uiMaxWidth  >> m_uhTotalDepth;
84  m_uiMinCUHeight     = uiMaxHeight >> m_uhTotalDepth;
85 
86  m_uiNumPartInWidth  = m_uiMaxCUWidth  / m_uiMinCUWidth;
87  m_uiNumPartInHeight = m_uiMaxCUHeight / m_uiMinCUHeight;
88 
89  m_uiWidthInCU       = ( iPicWidth %m_uiMaxCUWidth  ) ? iPicWidth /m_uiMaxCUWidth  + 1 : iPicWidth /m_uiMaxCUWidth;
90  m_uiHeightInCU      = ( iPicHeight%m_uiMaxCUHeight ) ? iPicHeight/m_uiMaxCUHeight + 1 : iPicHeight/m_uiMaxCUHeight;
91 
92  m_uiNumCUsInFrame   = m_uiWidthInCU * m_uiHeightInCU;
93  m_apcTComDataCU     = new TComDataCU*[m_uiNumCUsInFrame];
94 
95  if (m_uiNumAllocatedSlice>0)
96  {
97    for ( i=0; i<m_uiNumAllocatedSlice ; i++ )
98    {
99      delete m_apcTComSlice[i];
100    }
101    delete [] m_apcTComSlice;
102  }
103  m_apcTComSlice      = new TComSlice*[m_uiNumCUsInFrame];
104  m_apcTComSlice[0]   = new TComSlice;
105  m_uiNumAllocatedSlice = 1;
106  for ( i=0; i<m_uiNumCUsInFrame ; i++ )
107  {
108    m_apcTComDataCU[i] = new TComDataCU;
109    m_apcTComDataCU[i]->create( m_uiNumPartitions, m_uiMaxCUWidth, m_uiMaxCUHeight, false, m_uiMaxCUWidth >> m_uhTotalDepth
110#if ADAPTIVE_QP_SELECTION
111      , true
112#endif     
113      );
114  }
115
116  m_puiCUOrderMap = new UInt[m_uiNumCUsInFrame+1];
117  m_puiTileIdxMap = new UInt[m_uiNumCUsInFrame];
118#if N0383_IL_CONSTRAINED_TILE_SETS_SEI
119  m_piTileSetIdxMap = new Int[m_uiNumCUsInFrame];
120  m_pucTileSetType = new UChar[m_uiNumCUsInFrame];
121  m_pbSkippedTileSetFlag = new Bool[m_uiNumCUsInFrame];
122#endif
123  m_puiInverseCUOrderMap = new UInt[m_uiNumCUsInFrame+1];
124
125  for( i=0; i<m_uiNumCUsInFrame; i++ )
126  {
127    m_puiCUOrderMap[i] = i;
128    m_puiInverseCUOrderMap[i] = i;
129  }
130
131  m_saoBlkParams = new SAOBlkParam[m_uiNumCUsInFrame];
132}
133
134Void TComPicSym::destroy()
135{
136  if (m_uiNumAllocatedSlice>0)
137  {
138    for (Int i = 0; i<m_uiNumAllocatedSlice ; i++ )
139    {
140      delete m_apcTComSlice[i];
141    }
142    delete [] m_apcTComSlice;
143  }
144  m_apcTComSlice = NULL;
145 
146  for (Int i = 0; i < m_uiNumCUsInFrame; i++)
147  {
148    m_apcTComDataCU[i]->destroy();
149    delete m_apcTComDataCU[i];
150    m_apcTComDataCU[i] = NULL;
151  }
152  delete [] m_apcTComDataCU;
153  m_apcTComDataCU = NULL;
154
155  delete [] m_puiCUOrderMap;
156  m_puiCUOrderMap = NULL;
157
158  delete [] m_puiTileIdxMap;
159  m_puiTileIdxMap = NULL;
160#if N0383_IL_CONSTRAINED_TILE_SETS_SEI
161  delete [] m_piTileSetIdxMap;
162  m_piTileSetIdxMap = NULL;
163  delete [] m_pucTileSetType;
164  m_pucTileSetType = NULL;
165  delete [] m_pbSkippedTileSetFlag;
166  m_pbSkippedTileSetFlag = NULL;
167#endif
168
169  delete [] m_puiInverseCUOrderMap;
170  m_puiInverseCUOrderMap = NULL;
171 
172  if(m_saoBlkParams)
173  {
174    delete[] m_saoBlkParams; m_saoBlkParams = NULL;
175  }
176}
177
178Void TComPicSym::allocateNewSlice()
179{
180  assert ((m_uiNumAllocatedSlice + 1) <= m_uiNumCUsInFrame);
181  m_apcTComSlice[m_uiNumAllocatedSlice ++] = new TComSlice;
182  if (m_uiNumAllocatedSlice>=2)
183  {
184    m_apcTComSlice[m_uiNumAllocatedSlice-1]->copySliceInfo( m_apcTComSlice[m_uiNumAllocatedSlice-2] );
185#if SVC_EXTENSION
186    m_apcTComSlice[m_uiNumAllocatedSlice-1]->initSlice( m_apcTComSlice[m_uiNumAllocatedSlice-1]->getLayerId() );
187#else
188    m_apcTComSlice[m_uiNumAllocatedSlice-1]->initSlice();
189#endif
190  }
191}
192
193Void TComPicSym::clearSliceBuffer()
194{
195  UInt i;
196  for (i = 1; i < m_uiNumAllocatedSlice; i++)
197  {
198    delete m_apcTComSlice[i];
199  }
200  m_uiNumAllocatedSlice = 1;
201}
202
203UInt TComPicSym::getPicSCUEncOrder( UInt SCUAddr )
204{ 
205  return getInverseCUOrderMap(SCUAddr/m_uiNumPartitions)*m_uiNumPartitions + SCUAddr%m_uiNumPartitions; 
206}
207
208UInt TComPicSym::getPicSCUAddr( UInt SCUEncOrder )
209{
210  return getCUOrderMap(SCUEncOrder/m_uiNumPartitions)*m_uiNumPartitions + SCUEncOrder%m_uiNumPartitions;
211}
212
213Void TComPicSym::initTiles(TComPPS *pps)
214{
215  //set NumColumnsMinus1 and NumRowsMinus1
216  setNumColumnsMinus1( pps->getNumTileColumnsMinus1() );
217  setNumRowsMinus1( pps->getTileNumRowsMinus1() );
218
219  const Int numCols = pps->getNumTileColumnsMinus1() + 1;
220  const Int numRows = pps->getTileNumRowsMinus1() + 1;
221  const Int numTiles = numRows * numCols;
222
223  // allocate memory for tile parameters
224  m_tileParameters.resize(numTiles);
225
226  if( pps->getTileUniformSpacingFlag() )
227  {
228    //set width and height for each (uniform) tile
229    for(Int row=0; row < numRows; row++)
230  {
231      for(Int col=0; col < numCols; col++)
232      {
233        const Int tileIdx = row * numCols + col;
234        m_tileParameters[tileIdx].setTileWidth( (col+1)*getFrameWidthInCU()/numCols
235                                              - (col*getFrameWidthInCU())/numCols );
236        m_tileParameters[tileIdx].setTileHeight( (row+1)*getFrameHeightInCU()/numRows
237                                               - (row*getFrameHeightInCU())/numRows );
238      }
239    }
240  }
241  else
242  {
243    //set the width for each tile
244    for(Int row=0; row < numRows; row++)
245    {
246      Int cumulativeTileWidth = 0;
247      for(Int col=0; col < getNumColumnsMinus1(); col++)
248      {
249        m_tileParameters[row * numCols + col].setTileWidth( pps->getTileColumnWidth(col) );
250        cumulativeTileWidth += pps->getTileColumnWidth(col);
251      }
252      m_tileParameters[row * numCols + getNumColumnsMinus1()].setTileWidth( getFrameWidthInCU()-cumulativeTileWidth );
253  }
254
255    //set the height for each tile
256    for(Int col=0; col < numCols; col++)
257    {
258      Int cumulativeTileHeight = 0;
259      for(Int row=0; row < getNumRowsMinus1(); row++)
260      {
261        m_tileParameters[row * numCols + col].setTileHeight( pps->getTileRowHeight(row) );
262        cumulativeTileHeight += pps->getTileRowHeight(row);
263      }
264      m_tileParameters[getNumRowsMinus1() * numCols + col].setTileHeight( getFrameHeightInCU()-cumulativeTileHeight );
265    }
266  }
267
268#if TILE_SIZE_CHECK
269  Int minWidth  = 1;
270  Int minHeight = 1;
271  const Int profileIdc = pps->getSPS()->getPTL()->getGeneralPTL()->getProfileIdc();
272  if (  profileIdc == Profile::MAIN || profileIdc == Profile::MAIN10)
273  {
274    if (pps->getTilesEnabledFlag())
275    {
276      minHeight = 64  / g_uiMaxCUHeight;
277      minWidth  = 256 / g_uiMaxCUWidth;
278    }
279  }
280  for(Int row=0; row < numRows; row++)
281  {
282    for(Int col=0; col < numCols; col++)
283    {
284      const Int tileIdx = row * numCols + col;
285      assert (m_tileParameters[tileIdx].getTileWidth() >= minWidth);
286      assert (m_tileParameters[tileIdx].getTileHeight() >= minHeight);
287    }
288  }
289#endif
290
291  //initialize each tile of the current picture
292  for( Int row=0; row < numRows; row++ )
293  {
294    for( Int col=0; col < numCols; col++ )
295    {
296      const Int tileIdx = row * numCols + col;
297
298      //initialize the RightEdgePosInCU for each tile
299      Int rightEdgePosInCTU = 0;
300      for( Int i=0; i <= col; i++ )
301      {
302        rightEdgePosInCTU += m_tileParameters[row * numCols + i].getTileWidth();
303      }
304      m_tileParameters[tileIdx].setRightEdgePosInCU(rightEdgePosInCTU-1);
305
306      //initialize the BottomEdgePosInCU for each tile
307      Int bottomEdgePosInCTU = 0;
308      for( Int i=0; i <= row; i++ )
309      {
310        bottomEdgePosInCTU += m_tileParameters[i * numCols + col].getTileHeight();
311      }
312      m_tileParameters[tileIdx].setBottomEdgePosInCU(bottomEdgePosInCTU-1);
313
314      //initialize the FirstCUAddr for each tile
315      m_tileParameters[tileIdx].setFirstCUAddr( (m_tileParameters[tileIdx].getBottomEdgePosInCU() - m_tileParameters[tileIdx].getTileHeight() + 1) * getFrameWidthInCU() + 
316                                                 m_tileParameters[tileIdx].getRightEdgePosInCU() - m_tileParameters[tileIdx].getTileWidth() + 1);
317    }
318  }
319
320  Int  columnIdx = 0;
321  Int  rowIdx = 0;
322
323  //initialize the TileIdxMap
324  for( Int i=0; i<m_uiNumCUsInFrame; i++)
325  {
326    for( Int col=0; col < numCols; col++)
327    {
328      if(i % getFrameWidthInCU() <= m_tileParameters[col].getRightEdgePosInCU())
329      {
330        columnIdx = col;
331        break;
332      }
333    }
334    for(Int row=0; row < numRows; row++)
335    {
336      if(i / getFrameWidthInCU() <= m_tileParameters[row*numCols].getBottomEdgePosInCU())
337      {
338        rowIdx = row;
339        break;
340      }
341    }
342    m_puiTileIdxMap[i] = rowIdx * numCols + columnIdx;
343  }
344}
345
346UInt TComPicSym::xCalculateNxtCUAddr( UInt uiCurrCUAddr )
347{
348  UInt  uiNxtCUAddr;
349  UInt  uiTileIdx;
350 
351  //get the tile index for the current LCU
352  uiTileIdx = this->getTileIdxMap(uiCurrCUAddr);
353
354  //get the raster scan address for the next LCU
355  if( uiCurrCUAddr % m_uiWidthInCU == this->getTComTile(uiTileIdx)->getRightEdgePosInCU() && uiCurrCUAddr / m_uiWidthInCU == this->getTComTile(uiTileIdx)->getBottomEdgePosInCU() )
356  //the current LCU is the last LCU of the tile
357  {
358    if(uiTileIdx == (m_iNumColumnsMinus1+1)*(m_iNumRowsMinus1+1)-1)
359    {
360      uiNxtCUAddr = m_uiNumCUsInFrame;
361    }
362    else
363    {
364      uiNxtCUAddr = this->getTComTile(uiTileIdx+1)->getFirstCUAddr();
365    }
366  } 
367  else //the current LCU is not the last LCU of the tile
368  {
369    if( uiCurrCUAddr % m_uiWidthInCU == this->getTComTile(uiTileIdx)->getRightEdgePosInCU() )  //the current LCU is on the rightmost edge of the tile
370    {
371      uiNxtCUAddr = uiCurrCUAddr + m_uiWidthInCU - this->getTComTile(uiTileIdx)->getTileWidth() + 1;
372    }
373    else
374    {
375      uiNxtCUAddr = uiCurrCUAddr + 1;
376    }
377  }
378
379  return uiNxtCUAddr;
380}
381
382Void TComPicSym::deriveLoopFilterBoundaryAvailibility(Int ctu,
383                                                      Bool& isLeftAvail,
384                                                      Bool& isRightAvail,
385                                                      Bool& isAboveAvail,
386                                                      Bool& isBelowAvail,
387                                                      Bool& isAboveLeftAvail,
388                                                      Bool& isAboveRightAvail,
389                                                      Bool& isBelowLeftAvail,
390                                                      Bool& isBelowRightAvail
391                                                      )
392{
393
394  isLeftAvail      = (ctu % m_uiWidthInCU != 0);
395  isRightAvail     = (ctu % m_uiWidthInCU != m_uiWidthInCU-1);
396  isAboveAvail     = (ctu >= m_uiWidthInCU );
397  isBelowAvail     = (ctu <  m_uiNumCUsInFrame - m_uiWidthInCU);
398  isAboveLeftAvail = (isAboveAvail && isLeftAvail);
399  isAboveRightAvail= (isAboveAvail && isRightAvail);
400  isBelowLeftAvail = (isBelowAvail && isLeftAvail);
401  isBelowRightAvail= (isBelowAvail && isRightAvail);
402
403  Bool isLoopFiltAcrossTilePPS = getCU(ctu)->getSlice()->getPPS()->getLoopFilterAcrossTilesEnabledFlag();
404
405  {
406    TComDataCU* ctuCurr  = getCU(ctu);
407    TComDataCU* ctuLeft  = isLeftAvail ?getCU(ctu-1):NULL;
408    TComDataCU* ctuRight = isRightAvail?getCU(ctu+1):NULL;
409    TComDataCU* ctuAbove = isAboveAvail?getCU(ctu-m_uiWidthInCU):NULL;
410    TComDataCU* ctuBelow = isBelowAvail?getCU(ctu+m_uiWidthInCU):NULL;
411    TComDataCU* ctuAboveLeft  = isAboveLeftAvail ? getCU(ctu-m_uiWidthInCU-1):NULL;
412    TComDataCU* ctuAboveRigtht= isAboveRightAvail? getCU(ctu-m_uiWidthInCU+1):NULL;
413    TComDataCU* ctuBelowLeft  = isBelowLeftAvail ? getCU(ctu+m_uiWidthInCU-1):NULL;
414    TComDataCU* ctuBelowRight = isBelowRightAvail? getCU(ctu+m_uiWidthInCU+1):NULL;
415
416    {
417      //left
418      if(ctuLeft != NULL)
419      {
420        isLeftAvail = (ctuCurr->getSlice()->getSliceCurStartCUAddr() != ctuLeft->getSlice()->getSliceCurStartCUAddr())?ctuCurr->getSlice()->getLFCrossSliceBoundaryFlag():true;
421      }
422      //above
423      if(ctuAbove != NULL)
424      {
425        isAboveAvail = (ctuCurr->getSlice()->getSliceCurStartCUAddr() != ctuAbove->getSlice()->getSliceCurStartCUAddr())?ctuCurr->getSlice()->getLFCrossSliceBoundaryFlag():true;
426      }
427      //right
428      if(ctuRight != NULL)
429      {
430        isRightAvail = (ctuCurr->getSlice()->getSliceCurStartCUAddr() != ctuRight->getSlice()->getSliceCurStartCUAddr())?ctuRight->getSlice()->getLFCrossSliceBoundaryFlag():true;
431      }
432      //below
433      if(ctuBelow != NULL)
434      {
435        isBelowAvail = (ctuCurr->getSlice()->getSliceCurStartCUAddr() != ctuBelow->getSlice()->getSliceCurStartCUAddr())?ctuBelow->getSlice()->getLFCrossSliceBoundaryFlag():true;
436      }
437      //above-left
438      if(ctuAboveLeft != NULL)
439      {
440        isAboveLeftAvail = (ctuCurr->getSlice()->getSliceCurStartCUAddr() != ctuAboveLeft->getSlice()->getSliceCurStartCUAddr())?ctuCurr->getSlice()->getLFCrossSliceBoundaryFlag():true;
441      }
442      //below-right
443      if(ctuBelowRight != NULL)
444      {
445        isBelowRightAvail = (ctuCurr->getSlice()->getSliceCurStartCUAddr() != ctuBelowRight->getSlice()->getSliceCurStartCUAddr())?ctuBelowRight->getSlice()->getLFCrossSliceBoundaryFlag():true;
446      }
447
448
449      //above-right
450      if(ctuAboveRigtht != NULL)
451      {
452        Int curSliceStartEncOrder  = ctuCurr->getSlice()->getSliceCurStartCUAddr();
453        Int aboveRigthtSliceStartEncOrder = ctuAboveRigtht->getSlice()->getSliceCurStartCUAddr();
454
455        isAboveRightAvail = (curSliceStartEncOrder == aboveRigthtSliceStartEncOrder)?(true):
456          (
457          (curSliceStartEncOrder > aboveRigthtSliceStartEncOrder)?(ctuCurr->getSlice()->getLFCrossSliceBoundaryFlag())
458          :(ctuAboveRigtht->getSlice()->getLFCrossSliceBoundaryFlag())
459          );         
460      }
461      //below-left
462      if(ctuBelowLeft != NULL)
463      {
464        Int curSliceStartEncOrder  = ctuCurr->getSlice()->getSliceCurStartCUAddr();
465        Int belowLeftSliceStartEncOrder = ctuBelowLeft->getSlice()->getSliceCurStartCUAddr();
466
467        isBelowLeftAvail = (curSliceStartEncOrder == belowLeftSliceStartEncOrder)?(true):
468          (
469          (curSliceStartEncOrder > belowLeftSliceStartEncOrder)?(ctuCurr->getSlice()->getLFCrossSliceBoundaryFlag())
470          :(ctuBelowLeft->getSlice()->getLFCrossSliceBoundaryFlag())
471          );
472      }       
473    }
474
475    if(!isLoopFiltAcrossTilePPS)
476    {     
477      isLeftAvail      = (!isLeftAvail      ) ?false:(getTileIdxMap( ctuLeft->getAddr()         ) == getTileIdxMap( ctu ));
478      isAboveAvail     = (!isAboveAvail     ) ?false:(getTileIdxMap( ctuAbove->getAddr()        ) == getTileIdxMap( ctu ));
479      isRightAvail     = (!isRightAvail     ) ?false:(getTileIdxMap( ctuRight->getAddr()        ) == getTileIdxMap( ctu ));
480      isBelowAvail     = (!isBelowAvail     ) ?false:(getTileIdxMap( ctuBelow->getAddr()        ) == getTileIdxMap( ctu ));
481      isAboveLeftAvail = (!isAboveLeftAvail ) ?false:(getTileIdxMap( ctuAboveLeft->getAddr()    ) == getTileIdxMap( ctu ));
482      isAboveRightAvail= (!isAboveRightAvail) ?false:(getTileIdxMap( ctuAboveRigtht->getAddr()  ) == getTileIdxMap( ctu ));
483      isBelowLeftAvail = (!isBelowLeftAvail ) ?false:(getTileIdxMap( ctuBelowLeft->getAddr()    ) == getTileIdxMap( ctu ));
484      isBelowRightAvail= (!isBelowRightAvail) ?false:(getTileIdxMap( ctuBelowRight->getAddr()   ) == getTileIdxMap( ctu ));
485    }
486  }
487
488}
489
490TComTile::TComTile()
491: m_uiTileWidth         (0)
492, m_uiTileHeight        (0)
493, m_uiRightEdgePosInCU  (0)
494, m_uiBottomEdgePosInCU (0)
495, m_uiFirstCUAddr       (0)
496
497{
498}
499
500TComTile::~TComTile()
501{
502}
503//! \}
Note: See TracBrowser for help on using the repository browser.