/src/vvdec/source/Lib/CommonLib/SampleAdaptiveOffset.cpp
Line | Count | Source |
1 | | /* ----------------------------------------------------------------------------- |
2 | | The copyright in this software is being made available under the Clear BSD |
3 | | License, included below. No patent rights, trademark rights and/or |
4 | | other Intellectual Property Rights other than the copyrights concerning |
5 | | the Software are granted under this license. |
6 | | |
7 | | The Clear BSD License |
8 | | |
9 | | Copyright (c) 2018-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVdeC Authors. |
10 | | All rights reserved. |
11 | | |
12 | | Redistribution and use in source and binary forms, with or without modification, |
13 | | are permitted (subject to the limitations in the disclaimer below) provided that |
14 | | the following conditions are met: |
15 | | |
16 | | * Redistributions of source code must retain the above copyright notice, |
17 | | this list of conditions and the following disclaimer. |
18 | | |
19 | | * Redistributions in binary form must reproduce the above copyright |
20 | | notice, this list of conditions and the following disclaimer in the |
21 | | documentation and/or other materials provided with the distribution. |
22 | | |
23 | | * Neither the name of the copyright holder nor the names of its |
24 | | contributors may be used to endorse or promote products derived from this |
25 | | software without specific prior written permission. |
26 | | |
27 | | NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY |
28 | | THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
29 | | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
30 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
31 | | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
32 | | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
33 | | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
34 | | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
35 | | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER |
36 | | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
37 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
38 | | POSSIBILITY OF SUCH DAMAGE. |
39 | | |
40 | | |
41 | | ------------------------------------------------------------------------------------------- */ |
42 | | |
43 | | /** \file SampleAdaptiveOffset.cpp |
44 | | \brief sample adaptive offset class |
45 | | */ |
46 | | |
47 | | #include "SampleAdaptiveOffset.h" |
48 | | |
49 | | #include "UnitTools.h" |
50 | | #include "UnitPartitioner.h" |
51 | | #include "CodingStructure.h" |
52 | | #include "CommonLib/dtrace_codingstruct.h" |
53 | | #include "CommonLib/dtrace_buffer.h" |
54 | | #include "CommonLib/TimeProfiler.h" |
55 | | |
56 | | #include <string.h> |
57 | | #include <stdlib.h> |
58 | | #include <stdio.h> |
59 | | #include <math.h> |
60 | | |
61 | | namespace vvdec |
62 | | { |
63 | | |
64 | | void SampleAdaptiveOffset::offsetBlock_core( const int channelBitDepth, |
65 | | const ClpRng& clpRng, |
66 | | int typeIdx, |
67 | | int* offset, |
68 | | int startIdx, |
69 | | const Pel* srcBlk, |
70 | | Pel* resBlk, |
71 | | ptrdiff_t srcStride, |
72 | | ptrdiff_t resStride, |
73 | | int width, |
74 | | int height, |
75 | | bool isLeftAvail, |
76 | | bool isRightAvail, |
77 | | bool isAboveAvail, |
78 | | bool isBelowAvail, |
79 | | bool isAboveLeftAvail, |
80 | | bool isAboveRightAvail, |
81 | | bool isBelowLeftAvail, |
82 | | bool isBelowRightAvail, |
83 | | std::vector<int8_t>* m_signLineBuf1, |
84 | | std::vector<int8_t>* m_signLineBuf2, |
85 | | bool isCtuCrossedByVirtualBoundaries, |
86 | | int horVirBndryPos[], |
87 | | int verVirBndryPos[], |
88 | | int numHorVirBndry, |
89 | | int numVerVirBndry ) |
90 | 0 | { |
91 | 0 | int x,y, startX, startY, endX, endY, edgeType; |
92 | 0 | int firstLineStartX, firstLineEndX, lastLineStartX, lastLineEndX; |
93 | 0 | int8_t signLeft, signRight, signDown; |
94 | | |
95 | |
|
96 | 0 | const Pel* srcLine = srcBlk; |
97 | 0 | Pel* resLine = resBlk; |
98 | |
|
99 | 0 | switch(typeIdx) |
100 | 0 | { |
101 | 0 | case SAO_TYPE_EO_0: |
102 | 0 | { |
103 | 0 | offset += 2; |
104 | 0 | startX = isLeftAvail ? 0 : 1; |
105 | 0 | endX = isRightAvail ? width : (width -1); |
106 | 0 | for (y=0; y< height; y++) |
107 | 0 | { |
108 | 0 | signLeft = (int8_t)sgn(srcLine[startX] - srcLine[startX-1]); |
109 | 0 | for (x=startX; x< endX; x++) |
110 | 0 | { |
111 | 0 | signRight = (int8_t)sgn(srcLine[x] - srcLine[x+1]); |
112 | 0 | if( isCtuCrossedByVirtualBoundaries && isProcessDisabled( x, y, numVerVirBndry, 0, verVirBndryPos, horVirBndryPos ) ) |
113 | 0 | { |
114 | 0 | signLeft = -signRight; |
115 | 0 | continue; |
116 | 0 | } |
117 | 0 | edgeType = signRight + signLeft; |
118 | 0 | signLeft = -signRight; |
119 | |
|
120 | 0 | resLine[x] = ClipPel<int>( srcLine[x] + offset[edgeType], clpRng); |
121 | 0 | } |
122 | |
|
123 | 0 | srcLine += srcStride; |
124 | 0 | resLine += resStride; |
125 | 0 | } |
126 | 0 | } |
127 | 0 | break; |
128 | | |
129 | 0 | case SAO_TYPE_EO_90: |
130 | 0 | { |
131 | 0 | offset += 2; |
132 | 0 | int8_t *signUpLine = &m_signLineBuf1->front(); |
133 | |
|
134 | 0 | startY = isAboveAvail ? 0 : 1; |
135 | 0 | endY = isBelowAvail ? height : height-1; |
136 | 0 | if (!isAboveAvail) |
137 | 0 | { |
138 | 0 | srcLine += srcStride; |
139 | 0 | resLine += resStride; |
140 | 0 | } |
141 | |
|
142 | 0 | const Pel* srcLineAbove= srcLine- srcStride; |
143 | 0 | for (x=0; x< width; x++) |
144 | 0 | { |
145 | 0 | signUpLine[x] = (int8_t)sgn(srcLine[x] - srcLineAbove[x]); |
146 | 0 | } |
147 | |
|
148 | 0 | const Pel* srcLineBelow; |
149 | 0 | for (y=startY; y<endY; y++) |
150 | 0 | { |
151 | 0 | srcLineBelow= srcLine+ srcStride; |
152 | |
|
153 | 0 | for (x=0; x< width; x++) |
154 | 0 | { |
155 | 0 | signDown = (int8_t)sgn(srcLine[x] - srcLineBelow[x]); |
156 | 0 | if( isCtuCrossedByVirtualBoundaries && isProcessDisabled( x, y, 0, numHorVirBndry, verVirBndryPos, horVirBndryPos ) ) |
157 | 0 | { |
158 | 0 | signUpLine[x] = -signDown; |
159 | 0 | continue; |
160 | 0 | } |
161 | 0 | edgeType = signDown + signUpLine[x]; |
162 | 0 | signUpLine[x]= -signDown; |
163 | |
|
164 | 0 | resLine[x] = ClipPel<int>(srcLine[x] + offset[edgeType], clpRng); |
165 | 0 | } |
166 | 0 | srcLine += srcStride; |
167 | 0 | resLine += resStride; |
168 | 0 | } |
169 | 0 | } |
170 | 0 | break; |
171 | 0 | case SAO_TYPE_EO_135: |
172 | 0 | { |
173 | 0 | offset += 2; |
174 | 0 | int8_t *signUpLine, *signDownLine, *signTmpLine; |
175 | |
|
176 | 0 | signUpLine = &m_signLineBuf1->front(); |
177 | 0 | signDownLine= &m_signLineBuf2->front(); |
178 | |
|
179 | 0 | startX = isLeftAvail ? 0 : 1 ; |
180 | 0 | endX = isRightAvail ? width : (width-1); |
181 | | |
182 | | //prepare 2nd line's upper sign |
183 | 0 | const Pel* srcLineBelow= srcLine+ srcStride; |
184 | 0 | for (x=startX; x< endX+1; x++) |
185 | 0 | { |
186 | 0 | signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x- 1]); |
187 | 0 | } |
188 | | |
189 | | //1st line |
190 | 0 | const Pel* srcLineAbove= srcLine- srcStride; |
191 | 0 | firstLineStartX = isAboveLeftAvail ? 0 : 1; |
192 | 0 | firstLineEndX = isAboveAvail? endX: 1; |
193 | 0 | for(x= firstLineStartX; x< firstLineEndX; x++) |
194 | 0 | { |
195 | 0 | if( isCtuCrossedByVirtualBoundaries && isProcessDisabled( x, 0, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos ) ) |
196 | 0 | { |
197 | 0 | continue; |
198 | 0 | } |
199 | 0 | edgeType = sgn(srcLine[x] - srcLineAbove[x- 1]) - signUpLine[x+1]; |
200 | |
|
201 | 0 | resLine[x] = ClipPel<int>( srcLine[x] + offset[edgeType], clpRng); |
202 | 0 | } |
203 | 0 | srcLine += srcStride; |
204 | 0 | resLine += resStride; |
205 | | |
206 | | |
207 | | //middle lines |
208 | 0 | for (y= 1; y< height-1; y++) |
209 | 0 | { |
210 | 0 | srcLineBelow= srcLine+ srcStride; |
211 | |
|
212 | 0 | for (x=startX; x<endX; x++) |
213 | 0 | { |
214 | 0 | signDown = (int8_t)sgn(srcLine[x] - srcLineBelow[x+ 1]); |
215 | 0 | if (isCtuCrossedByVirtualBoundaries && isProcessDisabled(x, y, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos)) |
216 | 0 | { |
217 | 0 | signDownLine[x + 1] = -signDown; |
218 | 0 | continue; |
219 | 0 | } |
220 | 0 | edgeType = signDown + signUpLine[x]; |
221 | 0 | resLine[x] = ClipPel<int>( srcLine[x] + offset[edgeType], clpRng); |
222 | |
|
223 | 0 | signDownLine[x+1] = -signDown; |
224 | 0 | } |
225 | 0 | signDownLine[startX] = (int8_t)sgn(srcLineBelow[startX] - srcLine[startX-1]); |
226 | |
|
227 | 0 | signTmpLine = signUpLine; |
228 | 0 | signUpLine = signDownLine; |
229 | 0 | signDownLine = signTmpLine; |
230 | |
|
231 | 0 | srcLine += srcStride; |
232 | 0 | resLine += resStride; |
233 | 0 | } |
234 | | |
235 | | //last line |
236 | 0 | srcLineBelow= srcLine+ srcStride; |
237 | 0 | lastLineStartX = isBelowAvail ? startX : (width -1); |
238 | 0 | lastLineEndX = isBelowRightAvail ? width : (width -1); |
239 | 0 | for(x= lastLineStartX; x< lastLineEndX; x++) |
240 | 0 | { |
241 | 0 | if (isCtuCrossedByVirtualBoundaries && isProcessDisabled(x, height - 1, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos)) |
242 | 0 | { |
243 | 0 | continue; |
244 | 0 | } |
245 | 0 | edgeType = sgn(srcLine[x] - srcLineBelow[x+ 1]) + signUpLine[x]; |
246 | 0 | resLine[x] = ClipPel<int>( srcLine[x] + offset[edgeType], clpRng); |
247 | 0 | } |
248 | 0 | } |
249 | 0 | break; |
250 | 0 | case SAO_TYPE_EO_45: |
251 | 0 | { |
252 | 0 | offset += 2; |
253 | 0 | int8_t *signUpLine = &m_signLineBuf1->at(1); |
254 | |
|
255 | 0 | startX = isLeftAvail ? 0 : 1; |
256 | 0 | endX = isRightAvail ? width : (width -1); |
257 | | |
258 | | //prepare 2nd line upper sign |
259 | 0 | const Pel* srcLineBelow= srcLine+ srcStride; |
260 | 0 | for (x=startX-1; x< endX; x++) |
261 | 0 | { |
262 | 0 | signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x+1]); |
263 | 0 | } |
264 | | |
265 | | |
266 | | //first line |
267 | 0 | const Pel* srcLineAbove= srcLine- srcStride; |
268 | 0 | firstLineStartX = isAboveAvail ? startX : (width -1 ); |
269 | 0 | firstLineEndX = isAboveRightAvail ? width : (width-1); |
270 | 0 | for(x= firstLineStartX; x< firstLineEndX; x++) |
271 | 0 | { |
272 | 0 | if (isCtuCrossedByVirtualBoundaries && isProcessDisabled(x, 0, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos)) |
273 | 0 | { |
274 | 0 | continue; |
275 | 0 | } |
276 | 0 | edgeType = sgn(srcLine[x] - srcLineAbove[x+1]) -signUpLine[x-1]; |
277 | 0 | resLine[x] = ClipPel<int>(srcLine[x] + offset[edgeType], clpRng); |
278 | 0 | } |
279 | 0 | srcLine += srcStride; |
280 | 0 | resLine += resStride; |
281 | | |
282 | | //middle lines |
283 | 0 | for (y= 1; y< height-1; y++) |
284 | 0 | { |
285 | 0 | srcLineBelow= srcLine+ srcStride; |
286 | |
|
287 | 0 | for(x= startX; x< endX; x++) |
288 | 0 | { |
289 | 0 | signDown = (int8_t)sgn(srcLine[x] - srcLineBelow[x-1]); |
290 | 0 | if( isCtuCrossedByVirtualBoundaries && isProcessDisabled( x, y, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos ) ) |
291 | 0 | { |
292 | 0 | signUpLine[x - 1] = -signDown; |
293 | 0 | continue; |
294 | 0 | } |
295 | 0 | edgeType = signDown + signUpLine[x]; |
296 | 0 | resLine[x] = ClipPel<int>(srcLine[x] + offset[edgeType], clpRng); |
297 | 0 | signUpLine[x-1] = -signDown; |
298 | 0 | } |
299 | 0 | signUpLine[endX-1] = (int8_t)sgn(srcLineBelow[endX-1] - srcLine[endX]); |
300 | 0 | srcLine += srcStride; |
301 | 0 | resLine += resStride; |
302 | 0 | } |
303 | | |
304 | | //last line |
305 | 0 | srcLineBelow= srcLine+ srcStride; |
306 | 0 | lastLineStartX = isBelowLeftAvail ? 0 : 1; |
307 | 0 | lastLineEndX = isBelowAvail ? endX : 1; |
308 | 0 | for(x= lastLineStartX; x< lastLineEndX; x++) |
309 | 0 | { |
310 | 0 | if( isCtuCrossedByVirtualBoundaries && isProcessDisabled( x, height - 1, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos ) ) |
311 | 0 | { |
312 | 0 | continue; |
313 | 0 | } |
314 | 0 | edgeType = sgn(srcLine[x] - srcLineBelow[x-1]) + signUpLine[x]; |
315 | 0 | resLine[x] = ClipPel<int>(srcLine[x] + offset[edgeType], clpRng); |
316 | |
|
317 | 0 | } |
318 | 0 | } |
319 | 0 | break; |
320 | 0 | case SAO_TYPE_BO: |
321 | 0 | { |
322 | 0 | const int shiftBits = channelBitDepth - NUM_SAO_BO_CLASSES_LOG2; |
323 | |
|
324 | 0 | for (y=0; y< height; y++) |
325 | 0 | { |
326 | 0 | for (x=0; x< width; x++) |
327 | 0 | { |
328 | 0 | resLine[x] = ClipPel<int>(srcLine[x] + offset[srcLine[x] >> shiftBits], clpRng ); |
329 | 0 | } |
330 | 0 | srcLine += srcStride; |
331 | 0 | resLine += resStride; |
332 | 0 | } |
333 | 0 | } |
334 | 0 | break; |
335 | 0 | default: |
336 | 0 | { |
337 | 0 | THROW_FATAL( "Not a supported SAO types\n" ); |
338 | 0 | } |
339 | 0 | } |
340 | 0 | } |
341 | | |
342 | | SampleAdaptiveOffset::~SampleAdaptiveOffset() |
343 | 0 | { |
344 | 0 | destroy(); |
345 | 0 | } |
346 | | |
347 | | void SampleAdaptiveOffset::create( int picWidth, int picHeight, ChromaFormat format, uint32_t maxCUWidth, uint32_t maxCUHeight, uint32_t maxCUDepth, uint32_t bitShift, PelUnitBuf& unitBuf ) |
348 | 0 | { |
349 | 0 | offsetBlock = offsetBlock_core; |
350 | 0 | #if ENABLE_SIMD_OPT_SAO && defined( TARGET_SIMD_X86 ) |
351 | 0 | initSampleAdaptiveOffsetX86(); |
352 | 0 | #endif |
353 | |
|
354 | 0 | m_tempBuf = unitBuf; |
355 | | |
356 | | //bit-depth related |
357 | 0 | m_offsetStepLog2 = bitShift; |
358 | 0 | m_numberOfComponents = getNumberValidComponents(format); |
359 | 0 | } |
360 | | |
361 | | void SampleAdaptiveOffset::destroy() |
362 | 0 | { |
363 | 0 | } |
364 | | |
365 | | #if 0 |
366 | | void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs ) |
367 | | { |
368 | | const PreCalcValues& pcv = *cs.pcv; |
369 | | for( uint32_t yPos = 0; yPos < pcv.lumaHeight; yPos += pcv.maxCUHeight ) |
370 | | { |
371 | | for( uint32_t xPos = 0; xPos < pcv.lumaWidth; xPos += pcv.maxCUWidth ) |
372 | | { |
373 | | const uint32_t width = (xPos + pcv.maxCUWidth > pcv.lumaWidth) ? (pcv.lumaWidth - xPos) : pcv.maxCUWidth; |
374 | | const uint32_t height = (yPos + pcv.maxCUHeight > pcv.lumaHeight) ? (pcv.lumaHeight - yPos) : pcv.maxCUHeight; |
375 | | |
376 | | SAOProcessCTU( cs, UnitArea(cs.area.chromaFormat, Area(xPos , yPos, width, height)) ); |
377 | | } |
378 | | } |
379 | | |
380 | | DTRACE_UPDATE(g_trace_ctx, (std::make_pair("poc", cs.slice->getPOC()))); |
381 | | DTRACE_PIC_COMP(D_REC_CB_LUMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Y); |
382 | | DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cb); |
383 | | DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cr); |
384 | | |
385 | | DTRACE ( g_trace_ctx, D_CRC, "SAO" ); |
386 | | DTRACE_CRC( g_trace_ctx, D_CRC, cs, cs.getRecoBuf() ); |
387 | | } |
388 | | #endif |
389 | | |
390 | | void SampleAdaptiveOffset::SAOPrepareCTULine( CodingStructure &cs, const UnitArea &lineArea ) |
391 | 0 | { |
392 | 0 | PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_SAO ); |
393 | |
|
394 | 0 | const PreCalcValues& pcv = *cs.pcv; |
395 | 0 | PelUnitBuf rec = cs.getRecoBuf(); |
396 | |
|
397 | 0 | const int height = lineArea.lumaSize().height; |
398 | 0 | const int y = lineArea.lumaPos().y; |
399 | |
|
400 | 0 | const int cpyY = y == 0 ? 0 : y + 2; |
401 | 0 | int cpyHeight = height; |
402 | 0 | cpyHeight -= y == 0 ? 0 : 2; |
403 | 0 | cpyHeight -= cpyY + cpyHeight >= pcv.lumaHeight ? 0 : 2; |
404 | 0 | cpyHeight = std::min<int>( cpyHeight, pcv.lumaHeight - cpyY ); |
405 | |
|
406 | 0 | for( int x = 0; x < pcv.lumaWidth; x += pcv.maxCUWidth ) |
407 | 0 | { |
408 | 0 | const int ctuRsAddr = getCtuAddr( Position( x, y ), *cs.pcv ); |
409 | |
|
410 | 0 | SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES] = { nullptr, nullptr }; |
411 | 0 | getMergeList( cs, ctuRsAddr, mergeList ); |
412 | |
|
413 | 0 | reconstructBlkSAOParam( cs.m_ctuData[ctuRsAddr].saoParam, mergeList ); |
414 | 0 | } |
415 | |
|
416 | 0 | const int numComp = getNumberValidComponents( pcv.chrFormat ); |
417 | |
|
418 | 0 | for( int i = 0; i < numComp; i++ ) |
419 | 0 | { |
420 | 0 | const ComponentID compID = ComponentID( i ); |
421 | |
|
422 | 0 | const int currCpyY = cpyY >> getComponentScaleY( compID, pcv.chrFormat ); |
423 | 0 | const int currCpyH = cpyHeight >> getComponentScaleY( compID, pcv.chrFormat ); |
424 | 0 | const int currCtuW = pcv.maxCUWidth >> getComponentScaleX( compID, pcv.chrFormat ); |
425 | 0 | const int currPicW = pcv.lumaWidth >> getComponentScaleX( compID, pcv.chrFormat ); |
426 | | |
427 | 0 | const ptrdiff_t src_stride = rec.bufs[ compID ].stride; |
428 | 0 | const ptrdiff_t dst_stride = m_tempBuf.bufs[ compID ].stride; |
429 | |
|
430 | 0 | const Pel* src = rec.bufs[ compID ].bufAt( 0, currCpyY ); |
431 | 0 | Pel* dst = m_tempBuf.bufs[ compID ].bufAt( 0, currCpyY ); |
432 | |
|
433 | 0 | for( int j = currCpyY; j < ( currCpyY + currCpyH ); j++, src += src_stride, dst += dst_stride ) |
434 | 0 | { |
435 | 0 | int numCpy = 0; |
436 | 0 | int cpyStart = 0; |
437 | |
|
438 | 0 | int ctuRsAddr = getCtuAddr( Position( 0, y ), *cs.pcv ); |
439 | |
|
440 | 0 | for( int x = 0, currCpyX = 0; x <= pcv.widthInCtus; x++, currCpyX += currCtuW, ctuRsAddr++ ) |
441 | 0 | { |
442 | 0 | if( x < pcv.widthInCtus && cs.m_ctuData[ctuRsAddr].saoParam[i].modeIdc != SAO_MODE_OFF ) |
443 | 0 | { |
444 | 0 | if( !numCpy ) cpyStart = currCpyX - !!x; |
445 | 0 | numCpy++; |
446 | 0 | } |
447 | 0 | else if( numCpy ) |
448 | 0 | { |
449 | | // copy |
450 | 0 | int cpyLen = numCpy * currCtuW + !!cpyStart + 1; |
451 | 0 | if( cpyStart + cpyLen > currPicW ) cpyLen = currPicW - cpyStart; |
452 | 0 | memcpy( dst + cpyStart, src + cpyStart, cpyLen * sizeof( Pel ) ); |
453 | 0 | numCpy = 0; |
454 | 0 | } |
455 | 0 | } |
456 | 0 | } |
457 | 0 | } |
458 | |
|
459 | 0 | const int cpyY2 = cpyY + cpyHeight; |
460 | 0 | const int cpyHeight2 = 4; |
461 | | |
462 | 0 | if( cpyY2 < pcv.lumaHeight ) |
463 | 0 | { |
464 | 0 | const UnitArea cpyLine2( pcv.chrFormat, Area( 0, cpyY2, pcv.lumaWidth, cpyHeight2 ) ); |
465 | | |
466 | 0 | m_tempBuf.subBuf( cpyLine2 ).copyFrom( rec.subBuf( cpyLine2 ) ); |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | | void SampleAdaptiveOffset::SAOProcessCTULine( CodingStructure &cs, const UnitArea &lineArea ) |
471 | 0 | { |
472 | 0 | PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_SAO ); |
473 | |
|
474 | 0 | const PreCalcValues& pcv = *cs.pcv; |
475 | 0 | PelUnitBuf rec = cs.getRecoBuf(); |
476 | |
|
477 | 0 | const int height = lineArea.lumaSize().height; |
478 | 0 | const int y = lineArea.lumaPos().y; |
479 | | |
480 | 0 | bool anySaoBlk = false; |
481 | | |
482 | 0 | for( int x = 0; x < pcv.lumaWidth; x += pcv.maxCUWidth ) |
483 | 0 | { |
484 | 0 | const int ctuRsAddr = getCtuAddr( Position( x, y ), *cs.pcv ); |
485 | |
|
486 | 0 | for( int i = 0; i < MAX_NUM_COMPONENT; i++ ) |
487 | 0 | { |
488 | 0 | if( cs.m_ctuData[ctuRsAddr].saoParam[i].modeIdc != SAO_MODE_OFF ) |
489 | 0 | { |
490 | 0 | anySaoBlk = true; |
491 | 0 | } |
492 | 0 | } |
493 | 0 | } |
494 | |
|
495 | 0 | if( !anySaoBlk ) return; |
496 | | |
497 | 0 | std::vector<int8_t> signLineBuf1; |
498 | 0 | std::vector<int8_t> signLineBuf2; |
499 | |
|
500 | 0 | for( int x = 0; x < pcv.lumaWidth; x += pcv.maxCUWidth ) |
501 | 0 | { |
502 | 0 | const int width = ( x + pcv.maxCUWidth > pcv.lumaWidth ) ? ( pcv.lumaWidth - x ) : pcv.maxCUWidth; |
503 | |
|
504 | 0 | const UnitArea ctuArea( pcv.chrFormat, Area( x, y, width, height ) ); |
505 | |
|
506 | 0 | const int ctuRsAddr = getCtuAddr( ctuArea.lumaPos(), *cs.pcv ); |
507 | |
|
508 | 0 | offsetCTU( ctuArea, m_tempBuf, rec, cs.m_ctuData[ctuRsAddr].saoParam, cs, signLineBuf1, signLineBuf2 ); |
509 | 0 | } |
510 | 0 | } |
511 | | |
512 | | void SampleAdaptiveOffset::SAOProcessCTU( CodingStructure &cs, const UnitArea &ctuArea ) |
513 | 0 | { |
514 | 0 | PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_SAO ); |
515 | |
|
516 | 0 | PelUnitBuf rec = cs.getRecoBuf(); |
517 | |
|
518 | 0 | const int ctuRsAddr = getCtuAddr( ctuArea.lumaPos(), *cs.pcv ); |
519 | |
|
520 | 0 | bool anySaoBlk = false; |
521 | |
|
522 | 0 | for( int i = 0; i < MAX_NUM_COMPONENT; i++ ) |
523 | 0 | { |
524 | 0 | if( cs.m_ctuData[ctuRsAddr].saoParam[i].modeIdc != SAO_MODE_OFF ) |
525 | 0 | { |
526 | 0 | anySaoBlk = true; |
527 | 0 | } |
528 | 0 | } |
529 | |
|
530 | 0 | if( !anySaoBlk ) return; |
531 | | |
532 | 0 | std::vector<int8_t> signLineBuf1; |
533 | 0 | std::vector<int8_t> signLineBuf2; |
534 | |
|
535 | 0 | offsetCTU( ctuArea, m_tempBuf, rec, cs.m_ctuData[ctuRsAddr].saoParam, cs, signLineBuf1, signLineBuf2 ); |
536 | 0 | } |
537 | | |
538 | | void SampleAdaptiveOffset::invertQuantOffsets(ComponentID compIdx, int typeIdc, int typeAuxInfo, int* dstOffsets, int* srcOffsets) const |
539 | 0 | { |
540 | 0 | int codedOffset[MAX_NUM_SAO_CLASSES]; |
541 | |
|
542 | 0 | ::memcpy(codedOffset, srcOffsets, sizeof(int)*MAX_NUM_SAO_CLASSES); |
543 | 0 | ::memset(dstOffsets, 0, sizeof(int)*MAX_NUM_SAO_CLASSES); |
544 | |
|
545 | 0 | if(typeIdc == SAO_TYPE_START_BO) |
546 | 0 | { |
547 | 0 | for(int i=0; i< 4; i++) |
548 | 0 | { |
549 | 0 | dstOffsets[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES] = codedOffset[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES]*(1<<m_offsetStepLog2); |
550 | 0 | } |
551 | 0 | } |
552 | 0 | else //EO |
553 | 0 | { |
554 | 0 | for(int i=0; i< NUM_SAO_EO_CLASSES; i++) |
555 | 0 | { |
556 | 0 | dstOffsets[i] = codedOffset[i] *(1<<m_offsetStepLog2); |
557 | 0 | } |
558 | 0 | CHECK(dstOffsets[SAO_CLASS_EO_PLAIN] != 0, "EO offset is not '0'"); //keep EO plain offset as zero |
559 | 0 | } |
560 | |
|
561 | 0 | } |
562 | | |
563 | | int SampleAdaptiveOffset::getMergeList(CodingStructure& cs, int ctuRsAddr, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES]) |
564 | 0 | { |
565 | 0 | const PreCalcValues& pcv = *cs.pcv; |
566 | |
|
567 | 0 | const int ctuX = ctuRsAddr % pcv.widthInCtus; |
568 | 0 | const int ctuY = ctuRsAddr / pcv.widthInCtus; |
569 | 0 | const CodingUnit& cu = *cs.getCtuData( ctuRsAddr ).cuPtr[CH_L][0]; |
570 | 0 | int mergedCTUPos; |
571 | 0 | int numValidMergeCandidates = 0; |
572 | |
|
573 | 0 | for( auto mergeType: { SAO_MERGE_LEFT, SAO_MERGE_ABOVE } ) |
574 | 0 | { |
575 | 0 | SAOBlkParam* mergeCandidate = NULL; |
576 | |
|
577 | 0 | switch(mergeType) |
578 | 0 | { |
579 | 0 | case SAO_MERGE_ABOVE: |
580 | 0 | if( ctuY > 0 ) |
581 | 0 | { |
582 | 0 | mergedCTUPos = ctuRsAddr - pcv.widthInCtus; |
583 | 0 | if( cu.above ) |
584 | 0 | { |
585 | 0 | mergeCandidate = &( cs.m_ctuData[mergedCTUPos].saoParam ); |
586 | 0 | } |
587 | 0 | } |
588 | 0 | break; |
589 | 0 | case SAO_MERGE_LEFT: |
590 | 0 | if( ctuX > 0 ) |
591 | 0 | { |
592 | 0 | mergedCTUPos = ctuRsAddr - 1; |
593 | 0 | if( cu.left ) |
594 | 0 | { |
595 | 0 | mergeCandidate = &( cs.m_ctuData[mergedCTUPos].saoParam ); |
596 | 0 | } |
597 | 0 | } |
598 | 0 | break; |
599 | 0 | default: |
600 | 0 | THROW_FATAL( "not a supported merge type" ); |
601 | 0 | } |
602 | | |
603 | 0 | mergeList[mergeType] = mergeCandidate; |
604 | 0 | if (mergeCandidate != NULL) |
605 | 0 | { |
606 | 0 | numValidMergeCandidates++; |
607 | 0 | } |
608 | 0 | } |
609 | | |
610 | 0 | return numValidMergeCandidates; |
611 | 0 | } |
612 | | |
613 | | |
614 | | void SampleAdaptiveOffset::reconstructBlkSAOParam(SAOBlkParam& recParam, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES]) const |
615 | 0 | { |
616 | 0 | const int numberOfComponents = m_numberOfComponents; |
617 | 0 | for(int compIdx = 0; compIdx < numberOfComponents; compIdx++) |
618 | 0 | { |
619 | 0 | const ComponentID component = ComponentID(compIdx); |
620 | 0 | SAOOffset& offsetParam = recParam[component]; |
621 | |
|
622 | 0 | if(offsetParam.modeIdc == SAO_MODE_OFF) |
623 | 0 | { |
624 | 0 | continue; |
625 | 0 | } |
626 | | |
627 | 0 | switch(offsetParam.modeIdc) |
628 | 0 | { |
629 | 0 | case SAO_MODE_NEW: |
630 | 0 | { |
631 | 0 | invertQuantOffsets(component, offsetParam.typeIdc, offsetParam.typeAuxInfo, offsetParam.offset, offsetParam.offset); |
632 | 0 | } |
633 | 0 | break; |
634 | 0 | case SAO_MODE_MERGE: |
635 | 0 | { |
636 | 0 | const SAOBlkParam* mergeTarget = mergeList[offsetParam.typeIdc]; |
637 | 0 | CHECK(mergeTarget == NULL, "Merge target does not exist"); |
638 | |
|
639 | 0 | offsetParam = (*mergeTarget)[component]; |
640 | 0 | } |
641 | 0 | break; |
642 | 0 | default: |
643 | 0 | { |
644 | 0 | THROW_RECOVERABLE( "Not a supported mode" ); |
645 | 0 | } |
646 | 0 | } |
647 | 0 | } |
648 | 0 | } |
649 | | |
650 | | |
651 | | void SampleAdaptiveOffset::offsetCTU( const UnitArea& area, const CPelUnitBuf& src, PelUnitBuf& res, SAOBlkParam& saoblkParam, CodingStructure& cs, std::vector<int8_t> &signLineBuf1, std::vector<int8_t> &signLineBuf2 ) |
652 | 0 | { |
653 | 0 | const uint32_t numberOfComponents = getNumberValidComponents( area.chromaFormat ); |
654 | 0 | if( std::all_of( &saoblkParam[0], |
655 | 0 | &saoblkParam[numberOfComponents], |
656 | 0 | [](const SAOOffset & p){ return p.modeIdc == SAO_MODE_OFF; } ) ) |
657 | 0 | { |
658 | 0 | return; |
659 | 0 | } |
660 | | |
661 | 0 | bool isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail, isBelowLeftAvail, isBelowRightAvail; |
662 | | |
663 | | // block boundary availability |
664 | 0 | deriveLoopFilterBoundaryAvailibility( cs, area.Y(), isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail, isBelowLeftAvail, isBelowRightAvail ); |
665 | |
|
666 | 0 | const size_t lineBufferSize = area.Y().width + 1; |
667 | 0 | if (signLineBuf1.size() < lineBufferSize) |
668 | 0 | { |
669 | 0 | signLineBuf1.resize(lineBufferSize); |
670 | 0 | signLineBuf2.resize(lineBufferSize); |
671 | 0 | } |
672 | |
|
673 | 0 | int numHorVirBndry = 0; |
674 | 0 | int numVerVirBndry = 0; |
675 | 0 | int horVirBndryPos[] = { -1, -1, -1 }; |
676 | 0 | int verVirBndryPos[] = { -1, -1, -1 }; |
677 | 0 | int horVirBndryPosComp[] = { -1, -1, -1 }; |
678 | 0 | int verVirBndryPosComp[] = { -1, -1, -1 }; |
679 | 0 | bool isCtuCrossedByVirtualBoundaries = isCrossedByVirtualBoundaries( cs.picHeader.get(), |
680 | 0 | area.Y(), |
681 | 0 | numHorVirBndry, numVerVirBndry, |
682 | 0 | horVirBndryPos, verVirBndryPos ); |
683 | 0 | if( isCtuCrossedByVirtualBoundaries ) |
684 | 0 | { |
685 | 0 | CHECK( numHorVirBndry >= (int)( sizeof(horVirBndryPos) / sizeof(horVirBndryPos[0]) ), "Too many virtual boundaries" ); |
686 | 0 | CHECK( numHorVirBndry >= (int)( sizeof(verVirBndryPos) / sizeof(verVirBndryPos[0]) ), "Too many virtual boundaries" ); |
687 | 0 | } |
688 | | |
689 | 0 | for(int compIdx = 0; compIdx < numberOfComponents; compIdx++) |
690 | 0 | { |
691 | 0 | const ComponentID compID = ComponentID(compIdx); |
692 | 0 | const CompArea& compArea = area.block(compID); |
693 | 0 | SAOOffset& ctbOffset = saoblkParam[compIdx]; |
694 | |
|
695 | 0 | if(ctbOffset.modeIdc != SAO_MODE_OFF) |
696 | 0 | { |
697 | 0 | ptrdiff_t srcStride = src.get(compID).stride; |
698 | 0 | const Pel* srcBlk = src.get(compID).bufAt(compArea); |
699 | 0 | ptrdiff_t resStride = res.get(compID).stride; |
700 | 0 | Pel* resBlk = res.get(compID).bufAt(compArea); |
701 | |
|
702 | 0 | for (int i = 0; i < numHorVirBndry; i++) |
703 | 0 | { |
704 | 0 | horVirBndryPosComp[i] = (horVirBndryPos[i] >> getComponentScaleY(compID, area.chromaFormat)) - compArea.y; |
705 | 0 | } |
706 | 0 | for (int i = 0; i < numVerVirBndry; i++) |
707 | 0 | { |
708 | 0 | verVirBndryPosComp[i] = (verVirBndryPos[i] >> getComponentScaleX(compID, area.chromaFormat)) - compArea.x; |
709 | 0 | } |
710 | |
|
711 | 0 | offsetBlock( cs.sps->getBitDepth(), |
712 | 0 | cs.getCtuData(cs.ctuRsAddr(area.Y().pos(), CH_L)).cuPtr[0][0]->slice->clpRng(compID), |
713 | 0 | ctbOffset.typeIdc, ctbOffset.offset,ctbOffset.typeAuxInfo |
714 | 0 | , srcBlk, resBlk, srcStride, resStride, compArea.width, compArea.height |
715 | 0 | , isLeftAvail, isRightAvail |
716 | 0 | , isAboveAvail, isBelowAvail |
717 | 0 | , isAboveLeftAvail, isAboveRightAvail |
718 | 0 | , isBelowLeftAvail, isBelowRightAvail |
719 | 0 | , &signLineBuf1 |
720 | 0 | , &signLineBuf2 |
721 | 0 | , isCtuCrossedByVirtualBoundaries |
722 | 0 | , horVirBndryPosComp |
723 | 0 | , verVirBndryPosComp |
724 | 0 | , numHorVirBndry |
725 | 0 | , numVerVirBndry |
726 | 0 | ); |
727 | 0 | } |
728 | 0 | } //compIdx |
729 | 0 | } |
730 | | |
731 | | void SampleAdaptiveOffset::deriveLoopFilterBoundaryAvailibility( CodingStructure& cs, |
732 | | const Position& pos, |
733 | | bool& isLeftAvail, |
734 | | bool& isRightAvail, |
735 | | bool& isAboveAvail, |
736 | | bool& isBelowAvail, |
737 | | bool& isAboveLeftAvail, |
738 | | bool& isAboveRightAvail, |
739 | | bool& isBelowLeftAvail, |
740 | | bool& isBelowRightAvail ) const |
741 | 0 | { |
742 | 0 | const int ctusz = cs.pcv->maxCUWidth; |
743 | 0 | const int ctuX = pos.x / ctusz; |
744 | 0 | const int ctuY = pos.y / ctusz; |
745 | 0 | const int width = cs.pcv->widthInCtus; |
746 | 0 | const int height = cs.pcv->heightInCtus; |
747 | |
|
748 | 0 | const CodingUnit* cuCurr = cs.getCtuData( ctuX, ctuY ).cuPtr[0][0]; |
749 | 0 | const CodingUnit* cuLeft = ctuX > 0 ? cs.getCtuData( ctuX - 1, ctuY ).cuPtr[0][0] : nullptr; |
750 | 0 | const CodingUnit* cuRight = ctuX + 1 < width ? cs.getCtuData( ctuX + 1, ctuY ).cuPtr[0][0] : nullptr; |
751 | 0 | const CodingUnit* cuAbove = ctuY > 0 ? cs.getCtuData( ctuX, ctuY - 1 ).cuPtr[0][0] : nullptr; |
752 | 0 | const CodingUnit* cuBelow = ctuY + 1 < height ? cs.getCtuData( ctuX, ctuY + 1 ).cuPtr[0][0] : nullptr; |
753 | 0 | const CodingUnit* cuAboveLeft = cuLeft && cuAbove ? cs.getCtuData( ctuX - 1, ctuY - 1 ).cuPtr[0][0] : nullptr; |
754 | 0 | const CodingUnit* cuAboveRight = cuRight && cuAbove ? cs.getCtuData( ctuX + 1, ctuY - 1 ).cuPtr[0][0] : nullptr; |
755 | 0 | const CodingUnit* cuBelowLeft = cuLeft && cuBelow ? cs.getCtuData( ctuX - 1, ctuY + 1 ).cuPtr[0][0] : nullptr; |
756 | 0 | const CodingUnit* cuBelowRight = cuRight && cuBelow ? cs.getCtuData( ctuX + 1, ctuY + 1 ).cuPtr[0][0] : nullptr; |
757 | |
|
758 | 0 | isLeftAvail = (cuLeft != NULL); |
759 | 0 | isAboveAvail = (cuAbove != NULL); |
760 | 0 | isRightAvail = (cuRight != NULL); |
761 | 0 | isBelowAvail = (cuBelow != NULL); |
762 | 0 | isAboveLeftAvail = (cuAboveLeft != NULL); |
763 | 0 | isAboveRightAvail = (cuAboveRight != NULL); |
764 | 0 | isBelowLeftAvail = (cuBelowLeft != NULL); |
765 | 0 | isBelowRightAvail = (cuBelowRight != NULL); |
766 | | |
767 | | // check cross slice flags |
768 | 0 | const bool isLoopFilterAcrossSlicePPS = cs.pps->getLoopFilterAcrossSlicesEnabledFlag(); |
769 | 0 | if (!isLoopFilterAcrossSlicePPS) |
770 | 0 | { |
771 | 0 | isLeftAvail = isLeftAvail && CU::isSameSlice(*cuCurr, *cuLeft); |
772 | 0 | isAboveAvail = isAboveAvail && CU::isSameSlice(*cuCurr, *cuAbove); |
773 | 0 | isRightAvail = isRightAvail && CU::isSameSlice(*cuCurr, *cuRight); |
774 | 0 | isBelowAvail = isBelowAvail && CU::isSameSlice(*cuCurr, *cuBelow); |
775 | 0 | isAboveLeftAvail = isAboveLeftAvail && CU::isSameSlice(*cuCurr, *cuAboveLeft); |
776 | 0 | isAboveRightAvail = isAboveRightAvail && CU::isSameSlice(*cuCurr, *cuAboveRight); |
777 | 0 | isBelowLeftAvail = isBelowLeftAvail && CU::isSameSlice(*cuCurr, *cuBelowLeft); |
778 | 0 | isBelowRightAvail = isBelowRightAvail && CU::isSameSlice(*cuCurr, *cuBelowRight); |
779 | 0 | } |
780 | | |
781 | | // check cross tile flags |
782 | 0 | const bool isLoopFilterAcrossTilePPS = cs.pps->getLoopFilterAcrossTilesEnabledFlag(); |
783 | 0 | if (!isLoopFilterAcrossTilePPS) |
784 | 0 | { |
785 | 0 | isLeftAvail = isLeftAvail && CU::isSameTile(*cuCurr, *cuLeft); |
786 | 0 | isAboveAvail = isAboveAvail && CU::isSameTile(*cuCurr, *cuAbove); |
787 | 0 | isRightAvail = isRightAvail && CU::isSameTile(*cuCurr, *cuRight); |
788 | 0 | isBelowAvail = isBelowAvail && CU::isSameTile(*cuCurr, *cuBelow); |
789 | 0 | isAboveLeftAvail = isAboveLeftAvail && CU::isSameTile(*cuCurr, *cuAboveLeft); |
790 | 0 | isAboveRightAvail = isAboveRightAvail && CU::isSameTile(*cuCurr, *cuAboveRight); |
791 | 0 | isBelowLeftAvail = isBelowLeftAvail && CU::isSameTile(*cuCurr, *cuBelowLeft); |
792 | 0 | isBelowRightAvail = isBelowRightAvail && CU::isSameTile(*cuCurr, *cuBelowRight); |
793 | 0 | } |
794 | | |
795 | | // check cross subpic flags |
796 | 0 | if( cs.sps->getSubPicInfoPresentFlag() ) |
797 | 0 | { |
798 | 0 | const SubPic& curSubPic = cs.pps->getSubPicFromCU(*cuCurr); |
799 | 0 | if( !curSubPic.getloopFilterAcrossSubPicEnabledFlag() ) |
800 | 0 | { |
801 | 0 | isLeftAvail = isLeftAvail && CU::isSameSubPic(*cuCurr, *cuLeft); |
802 | 0 | isAboveAvail = isAboveAvail && CU::isSameSubPic(*cuCurr, *cuAbove); |
803 | 0 | isRightAvail = isRightAvail && CU::isSameSubPic(*cuCurr, *cuRight); |
804 | 0 | isBelowAvail = isBelowAvail && CU::isSameSubPic(*cuCurr, *cuBelow); |
805 | 0 | isAboveLeftAvail = isAboveLeftAvail && CU::isSameSubPic(*cuCurr, *cuAboveLeft); |
806 | 0 | isAboveRightAvail = isAboveRightAvail && CU::isSameSubPic(*cuCurr, *cuAboveRight); |
807 | 0 | isBelowLeftAvail = isBelowLeftAvail && CU::isSameSubPic(*cuCurr, *cuBelowLeft); |
808 | 0 | isBelowRightAvail = isBelowRightAvail && CU::isSameSubPic(*cuCurr, *cuBelowRight); |
809 | 0 | } |
810 | 0 | } |
811 | 0 | } |
812 | | |
813 | | bool SampleAdaptiveOffset::isProcessDisabled( int xPos, int yPos, int numVerVirBndry, int numHorVirBndry, int verVirBndryPos[], int horVirBndryPos[] ) |
814 | 0 | { |
815 | 0 | for (int i = 0; i < numVerVirBndry; i++) |
816 | 0 | { |
817 | 0 | if ((xPos == verVirBndryPos[i]) || (xPos == verVirBndryPos[i] - 1)) |
818 | 0 | { |
819 | 0 | return true; |
820 | 0 | } |
821 | 0 | } |
822 | 0 | for (int i = 0; i < numHorVirBndry; i++) |
823 | 0 | { |
824 | 0 | if ((yPos == horVirBndryPos[i]) || (yPos == horVirBndryPos[i] - 1)) |
825 | 0 | { |
826 | 0 | return true; |
827 | 0 | } |
828 | 0 | } |
829 | | |
830 | 0 | return false; |
831 | 0 | } |
832 | | |
833 | | } |