Coverage Report

Created: 2026-07-16 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/libde265/libde265/sao.cc
Line
Count
Source
1
/*
2
 * H.265 video codec.
3
 * Copyright (c) 2013-2014 struktur AG, Dirk Farin <farin@struktur.de>
4
 *
5
 * This file is part of libde265.
6
 *
7
 * libde265 is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libde265 is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libde265.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "sao.h"
22
#include "util.h"
23
24
#include <stdlib.h>
25
#include <string.h>
26
27
28
template <class pixel_t>
29
void apply_sao_internal(de265_image* img, int xCtb,int yCtb,
30
                        const slice_segment_header* shdr, int cIdx, int nSW,int nSH,
31
                        const pixel_t* in_img,  ptrdiff_t in_stride,
32
                        /* */ pixel_t* out_img, ptrdiff_t out_stride)
33
3.58M
{
34
3.58M
  const sao_info* saoinfo = img->get_sao_info(xCtb,yCtb);
35
36
3.58M
  int SaoTypeIdx = (saoinfo->SaoTypeIdx >> (2*cIdx)) & 0x3;
37
38
3.58M
  logtrace(LogSAO,"apply_sao CTB %d;%d cIdx:%d type=%d (%dx%d)\n",xCtb,yCtb,cIdx, SaoTypeIdx, nSW,nSH);
39
40
3.58M
  if (SaoTypeIdx==0) {
41
2.37M
    return;
42
2.37M
  }
43
44
1.21M
  const seq_parameter_set* sps = &img->get_sps();
45
1.21M
  const pic_parameter_set* pps = &img->get_pps();
46
1.21M
  const int bitDepth = (cIdx==0 ? sps->BitDepth_Y : sps->BitDepth_C);
47
1.21M
  const int maxPixelValue = (1<<bitDepth)-1;
48
49
  // top left position of CTB in pixels
50
1.21M
  const int xC = xCtb*nSW;
51
1.21M
  const int yC = yCtb*nSH;
52
53
1.21M
  const int width  = img->get_width(cIdx);
54
1.21M
  const int height = img->get_height(cIdx);
55
56
1.21M
  const int ctbSliceAddrRS = img->get_SliceHeader(xC,yC)->SliceAddrRS;
57
58
1.21M
  const int picWidthInCtbs = sps->PicWidthInCtbsY;
59
1.21M
  const int chromashiftW = sps->get_chroma_shift_W(cIdx);
60
1.21M
  const int chromashiftH = sps->get_chroma_shift_H(cIdx);
61
1.21M
  const int ctbshiftW = sps->Log2CtbSizeY - chromashiftW;
62
1.21M
  const int ctbshiftH = sps->Log2CtbSizeY - chromashiftH;
63
64
65
7.26M
  for (int i=0;i<5;i++)
66
6.05M
    {
67
6.05M
      logtrace(LogSAO,"offset[%d] = %d\n", i, i==0 ? 0 : saoinfo->saoOffsetVal[cIdx][i-1]);
68
6.05M
    }
69
70
71
  // actual size of CTB to be processed (can be smaller when partially outside of image)
72
1.21M
  const int ctbW = (xC+nSW>width)  ? width -xC : nSW;
73
1.21M
  const int ctbH = (yC+nSH>height) ? height-yC : nSH;
74
75
76
1.21M
  const bool extendedTests = img->get_CTB_has_pcm_or_cu_transquant_bypass(xCtb,yCtb);
77
78
1.21M
  if (SaoTypeIdx==2) {
79
173k
    int hPos[2], vPos[2];
80
173k
    ptrdiff_t vPosStride[2]; // vPos[] multiplied by image stride
81
173k
    int SaoEoClass = (saoinfo->SaoEoClass >> (2*cIdx)) & 0x3;
82
83
173k
    switch (SaoEoClass) {
84
34.6k
    case 0: hPos[0]=-1; hPos[1]= 1; vPos[0]= 0; vPos[1]=0; break;
85
41.2k
    case 1: hPos[0]= 0; hPos[1]= 0; vPos[0]=-1; vPos[1]=1; break;
86
48.8k
    case 2: hPos[0]=-1; hPos[1]= 1; vPos[0]=-1; vPos[1]=1; break;
87
48.9k
    case 3: hPos[0]= 1; hPos[1]=-1; vPos[0]=-1; vPos[1]=1; break;
88
173k
    }
89
90
173k
    vPosStride[0] = vPos[0] * in_stride;
91
173k
    vPosStride[1] = vPos[1] * in_stride;
92
93
    /* Reorder sao_info.saoOffsetVal[] array, so that we can index it
94
       directly with the sum of the two pixel-difference signs. */
95
173k
    int8_t  saoOffsetVal[5]; // [2] unused
96
173k
    saoOffsetVal[0] = saoinfo->saoOffsetVal[cIdx][1-1];
97
173k
    saoOffsetVal[1] = saoinfo->saoOffsetVal[cIdx][2-1];
98
173k
    saoOffsetVal[2] = 0;
99
173k
    saoOffsetVal[3] = saoinfo->saoOffsetVal[cIdx][3-1];
100
173k
    saoOffsetVal[4] = saoinfo->saoOffsetVal[cIdx][4-1];
101
102
103
2.70M
    for (int j=0;j<ctbH;j++) {
104
2.52M
      const pixel_t* in_ptr  = &in_img [xC+(yC+j)*in_stride];
105
2.52M
      /* */ pixel_t* out_ptr = &out_img[xC+(yC+j)*out_stride];
106
107
55.0M
      for (int i=0;i<ctbW;i++) {
108
52.5M
        int edgeIdx = -1;
109
110
52.5M
        logtrace(LogSAO, "pos %d,%d\n",xC+i,yC+j);
111
112
52.5M
        if ((extendedTests &&
113
32.4M
             (sps->pcm_loop_filter_disable_flag &&
114
2.85M
              img->get_pcm_flag((xC+i)<<chromashiftW,(yC+j)<<chromashiftH))) ||
115
52.5M
            img->get_cu_transquant_bypass((xC+i)<<chromashiftW,(yC+j)<<chromashiftH)) {
116
21.4M
          continue;
117
21.4M
        }
118
119
        // do the expensive test for boundaries only at the boundaries
120
31.0M
        bool testBoundary = (i==0 || j==0 || i==ctbW-1 || j==ctbH-1);
121
122
31.0M
        if (testBoundary)
123
16.4M
          for (int k=0;k<2;k++) {
124
11.2M
            int xS = xC+i+hPos[k];
125
11.2M
            int yS = yC+j+vPos[k];
126
127
11.2M
            if (xS<0 || yS<0 || xS>=width || yS>=height) {
128
671k
              edgeIdx=0;
129
671k
              break;
130
671k
            }
131
132
133
            // This part seems inefficient with all the get_SliceHeaderIndex() calls,
134
            // but removing this part (because the input was known to have only a single
135
            // slice anyway) reduced computation time only by 1.3%.
136
            // TODO: however, this may still be a big part of SAO itself.
137
138
10.6M
            slice_segment_header* sliceHeader = img->get_SliceHeader(xS<<chromashiftW,
139
10.6M
                                                                     yS<<chromashiftH);
140
10.6M
            if (sliceHeader==nullptr) { return; }
141
142
10.6M
            int sliceAddrRS = sliceHeader->SliceAddrRS;
143
10.6M
            if (sliceAddrRS <  ctbSliceAddrRS &&
144
11.6k
                img->get_SliceHeader((xC+i)<<chromashiftW,
145
11.6k
                                     (yC+j)<<chromashiftH)->slice_loop_filter_across_slices_enabled_flag==0) {
146
9.36k
              edgeIdx=0;
147
9.36k
              break;
148
9.36k
            }
149
150
10.6M
            if (sliceAddrRS >  ctbSliceAddrRS &&
151
14.8k
                img->get_SliceHeader(xS<<chromashiftW,
152
14.8k
                                     yS<<chromashiftH)->slice_loop_filter_across_slices_enabled_flag==0) {
153
9.59k
              edgeIdx=0;
154
9.59k
              break;
155
9.59k
            }
156
157
158
10.5M
            if (pps->loop_filter_across_tiles_enabled_flag==0 &&
159
9.63M
                pps->scan->TileIdRS[(xS>>ctbshiftW) + (yS>>ctbshiftH)*picWidthInCtbs] !=
160
9.63M
                pps->scan->TileIdRS[(xC>>ctbshiftW) + (yC>>ctbshiftH)*picWidthInCtbs]) {
161
18.7k
              edgeIdx=0;
162
18.7k
              break;
163
18.7k
            }
164
10.5M
          }
165
166
31.0M
        if (edgeIdx != 0) {
167
168
30.3M
          edgeIdx = ( Sign(in_ptr[i] - in_ptr[i+hPos[0]+vPosStride[0]]) +
169
30.3M
                      Sign(in_ptr[i] - in_ptr[i+hPos[1]+vPosStride[1]])   );
170
171
30.3M
          if (1) { // edgeIdx != 0) {   // seems to be faster without this check (zero in offset table)
172
30.3M
            int offset = saoOffsetVal[edgeIdx+2];
173
174
30.3M
            out_ptr[i] = Clip3(0,maxPixelValue,
175
30.3M
                               in_ptr[i] + offset);
176
30.3M
          }
177
30.3M
        }
178
31.0M
      }
179
2.52M
    }
180
173k
  }
181
1.03M
  else {
182
1.03M
    int bandShift = bitDepth-5;
183
1.03M
    int saoLeftClass = saoinfo->sao_band_position[cIdx];
184
1.03M
    logtrace(LogSAO,"saoLeftClass: %d\n",saoLeftClass);
185
186
1.03M
    int bandTable[32];
187
1.03M
    memset(bandTable, 0, sizeof(int)*32);
188
189
5.18M
    for (int k=0;k<4;k++) {
190
4.14M
      bandTable[ (k+saoLeftClass)&31 ] = k+1;
191
4.14M
    }
192
193
194
    /* If PCM or transquant_bypass is used in this CTB, we have to
195
       run all checks (A).
196
       Otherwise, we run a simplified version of the code (B).
197
198
       NOTE: this whole part of SAO does not seem to be a significant part of the time spent
199
    */
200
201
1.03M
    if (extendedTests) {
202
203
      // (A) full version with all checks
204
205
4.37M
      for (int j=0;j<ctbH;j++)
206
116M
        for (int i=0;i<ctbW;i++) {
207
208
112M
          if ((sps->pcm_loop_filter_disable_flag &&
209
51.9M
               img->get_pcm_flag((xC+i)<<chromashiftW,(yC+j)<<chromashiftH)) ||
210
112M
              img->get_cu_transquant_bypass((xC+i)<<chromashiftW,(yC+j)<<chromashiftH)) {
211
98.9M
            continue;
212
98.9M
          }
213
214
          // Shifts are a strange thing. On x86, >>x actually computes >>(x%64).
215
          // But this should never happen, because the maximum bit-depth is 16.
216
13.5M
          int pixel = in_img[xC + i + (yC + j) * in_stride];
217
218
          // Note: the input pixel value should never exceed the valid range, but it seems that it still does,
219
          // maybe when there was a decoding error and the pixels have not been filled in correctly.
220
          // Thus, we have to limit the pixel range to ensure that we have no illegal table access.
221
13.5M
          pixel = Clip3(0, maxPixelValue, pixel);
222
223
13.5M
          int bandIdx = bandTable[pixel >> bandShift];
224
225
13.5M
          if (bandIdx>0) {
226
1.92M
            int offset = saoinfo->saoOffsetVal[cIdx][bandIdx-1];
227
228
1.92M
            logtrace(LogSAO,"%d %d (%d) offset %d  %x -> %x\n",xC+i,yC+j,bandIdx,
229
1.92M
                     offset,
230
1.92M
                     in_img[xC+i+(yC+j)*in_stride],
231
1.92M
                     in_img[xC+i+(yC+j)*in_stride]+offset);
232
233
1.92M
            out_img[xC+i+(yC+j)*out_stride] = Clip3(0,maxPixelValue,
234
1.92M
                                                    in_img[xC+i+(yC+j)*in_stride] + offset);
235
1.92M
          }
236
13.5M
        }
237
197k
    }
238
839k
    else
239
839k
      {
240
        // (B) simplified version (only works if no PCM and transquant_bypass is active)
241
242
9.57M
        for (int j=0;j<ctbH;j++)
243
160M
          for (int i=0;i<ctbW;i++) {
244
245
152M
            int pixel = in_img[xC + i + (yC + j) * in_stride];
246
247
            // Note: the input pixel value should never exceed the valid range, but it seems that it still does,
248
            // maybe when there was a decoding error and the pixels have not been filled in correctly.
249
            // Thus, we have to limit the pixel range to ensure that we have no illegal table access.
250
152M
            pixel = Clip3(0, maxPixelValue, pixel);
251
252
152M
            int bandIdx = bandTable[pixel >> bandShift];
253
254
152M
            if (bandIdx>0) {
255
25.5M
              int offset = saoinfo->saoOffsetVal[cIdx][bandIdx-1];
256
257
25.5M
              out_img[xC+i+(yC+j)*out_stride] = Clip3(0,maxPixelValue,
258
25.5M
                                                      in_img[xC+i+(yC+j)*in_stride] + offset);
259
25.5M
            }
260
152M
          }
261
839k
      }
262
1.03M
  }
263
1.21M
}
void apply_sao_internal<unsigned short>(de265_image*, int, int, slice_segment_header const*, int, int, int, unsigned short const*, long, unsigned short*, long)
Line
Count
Source
33
1.01M
{
34
1.01M
  const sao_info* saoinfo = img->get_sao_info(xCtb,yCtb);
35
36
1.01M
  int SaoTypeIdx = (saoinfo->SaoTypeIdx >> (2*cIdx)) & 0x3;
37
38
1.01M
  logtrace(LogSAO,"apply_sao CTB %d;%d cIdx:%d type=%d (%dx%d)\n",xCtb,yCtb,cIdx, SaoTypeIdx, nSW,nSH);
39
40
1.01M
  if (SaoTypeIdx==0) {
41
370k
    return;
42
370k
  }
43
44
644k
  const seq_parameter_set* sps = &img->get_sps();
45
644k
  const pic_parameter_set* pps = &img->get_pps();
46
644k
  const int bitDepth = (cIdx==0 ? sps->BitDepth_Y : sps->BitDepth_C);
47
644k
  const int maxPixelValue = (1<<bitDepth)-1;
48
49
  // top left position of CTB in pixels
50
644k
  const int xC = xCtb*nSW;
51
644k
  const int yC = yCtb*nSH;
52
53
644k
  const int width  = img->get_width(cIdx);
54
644k
  const int height = img->get_height(cIdx);
55
56
644k
  const int ctbSliceAddrRS = img->get_SliceHeader(xC,yC)->SliceAddrRS;
57
58
644k
  const int picWidthInCtbs = sps->PicWidthInCtbsY;
59
644k
  const int chromashiftW = sps->get_chroma_shift_W(cIdx);
60
644k
  const int chromashiftH = sps->get_chroma_shift_H(cIdx);
61
644k
  const int ctbshiftW = sps->Log2CtbSizeY - chromashiftW;
62
644k
  const int ctbshiftH = sps->Log2CtbSizeY - chromashiftH;
63
64
65
3.86M
  for (int i=0;i<5;i++)
66
3.22M
    {
67
3.22M
      logtrace(LogSAO,"offset[%d] = %d\n", i, i==0 ? 0 : saoinfo->saoOffsetVal[cIdx][i-1]);
68
3.22M
    }
69
70
71
  // actual size of CTB to be processed (can be smaller when partially outside of image)
72
644k
  const int ctbW = (xC+nSW>width)  ? width -xC : nSW;
73
644k
  const int ctbH = (yC+nSH>height) ? height-yC : nSH;
74
75
76
644k
  const bool extendedTests = img->get_CTB_has_pcm_or_cu_transquant_bypass(xCtb,yCtb);
77
78
644k
  if (SaoTypeIdx==2) {
79
83.6k
    int hPos[2], vPos[2];
80
83.6k
    ptrdiff_t vPosStride[2]; // vPos[] multiplied by image stride
81
83.6k
    int SaoEoClass = (saoinfo->SaoEoClass >> (2*cIdx)) & 0x3;
82
83
83.6k
    switch (SaoEoClass) {
84
16.0k
    case 0: hPos[0]=-1; hPos[1]= 1; vPos[0]= 0; vPos[1]=0; break;
85
21.8k
    case 1: hPos[0]= 0; hPos[1]= 0; vPos[0]=-1; vPos[1]=1; break;
86
33.1k
    case 2: hPos[0]=-1; hPos[1]= 1; vPos[0]=-1; vPos[1]=1; break;
87
12.4k
    case 3: hPos[0]= 1; hPos[1]=-1; vPos[0]=-1; vPos[1]=1; break;
88
83.6k
    }
89
90
83.6k
    vPosStride[0] = vPos[0] * in_stride;
91
83.6k
    vPosStride[1] = vPos[1] * in_stride;
92
93
    /* Reorder sao_info.saoOffsetVal[] array, so that we can index it
94
       directly with the sum of the two pixel-difference signs. */
95
83.6k
    int8_t  saoOffsetVal[5]; // [2] unused
96
83.6k
    saoOffsetVal[0] = saoinfo->saoOffsetVal[cIdx][1-1];
97
83.6k
    saoOffsetVal[1] = saoinfo->saoOffsetVal[cIdx][2-1];
98
83.6k
    saoOffsetVal[2] = 0;
99
83.6k
    saoOffsetVal[3] = saoinfo->saoOffsetVal[cIdx][3-1];
100
83.6k
    saoOffsetVal[4] = saoinfo->saoOffsetVal[cIdx][4-1];
101
102
103
1.40M
    for (int j=0;j<ctbH;j++) {
104
1.32M
      const pixel_t* in_ptr  = &in_img [xC+(yC+j)*in_stride];
105
1.32M
      /* */ pixel_t* out_ptr = &out_img[xC+(yC+j)*out_stride];
106
107
36.5M
      for (int i=0;i<ctbW;i++) {
108
35.2M
        int edgeIdx = -1;
109
110
35.2M
        logtrace(LogSAO, "pos %d,%d\n",xC+i,yC+j);
111
112
35.2M
        if ((extendedTests &&
113
22.2M
             (sps->pcm_loop_filter_disable_flag &&
114
1.65M
              img->get_pcm_flag((xC+i)<<chromashiftW,(yC+j)<<chromashiftH))) ||
115
35.2M
            img->get_cu_transquant_bypass((xC+i)<<chromashiftW,(yC+j)<<chromashiftH)) {
116
14.3M
          continue;
117
14.3M
        }
118
119
        // do the expensive test for boundaries only at the boundaries
120
20.8M
        bool testBoundary = (i==0 || j==0 || i==ctbW-1 || j==ctbH-1);
121
122
20.8M
        if (testBoundary)
123
9.22M
          for (int k=0;k<2;k++) {
124
6.36M
            int xS = xC+i+hPos[k];
125
6.36M
            int yS = yC+j+vPos[k];
126
127
6.36M
            if (xS<0 || yS<0 || xS>=width || yS>=height) {
128
449k
              edgeIdx=0;
129
449k
              break;
130
449k
            }
131
132
133
            // This part seems inefficient with all the get_SliceHeaderIndex() calls,
134
            // but removing this part (because the input was known to have only a single
135
            // slice anyway) reduced computation time only by 1.3%.
136
            // TODO: however, this may still be a big part of SAO itself.
137
138
5.92M
            slice_segment_header* sliceHeader = img->get_SliceHeader(xS<<chromashiftW,
139
5.92M
                                                                     yS<<chromashiftH);
140
5.92M
            if (sliceHeader==nullptr) { return; }
141
142
5.92M
            int sliceAddrRS = sliceHeader->SliceAddrRS;
143
5.92M
            if (sliceAddrRS <  ctbSliceAddrRS &&
144
5.21k
                img->get_SliceHeader((xC+i)<<chromashiftW,
145
5.21k
                                     (yC+j)<<chromashiftH)->slice_loop_filter_across_slices_enabled_flag==0) {
146
4.26k
              edgeIdx=0;
147
4.26k
              break;
148
4.26k
            }
149
150
5.91M
            if (sliceAddrRS >  ctbSliceAddrRS &&
151
4.69k
                img->get_SliceHeader(xS<<chromashiftW,
152
4.69k
                                     yS<<chromashiftH)->slice_loop_filter_across_slices_enabled_flag==0) {
153
2.93k
              edgeIdx=0;
154
2.93k
              break;
155
2.93k
            }
156
157
158
5.91M
            if (pps->loop_filter_across_tiles_enabled_flag==0 &&
159
5.12M
                pps->scan->TileIdRS[(xS>>ctbshiftW) + (yS>>ctbshiftH)*picWidthInCtbs] !=
160
5.12M
                pps->scan->TileIdRS[(xC>>ctbshiftW) + (yC>>ctbshiftH)*picWidthInCtbs]) {
161
10.1k
              edgeIdx=0;
162
10.1k
              break;
163
10.1k
            }
164
5.91M
          }
165
166
20.8M
        if (edgeIdx != 0) {
167
168
20.3M
          edgeIdx = ( Sign(in_ptr[i] - in_ptr[i+hPos[0]+vPosStride[0]]) +
169
20.3M
                      Sign(in_ptr[i] - in_ptr[i+hPos[1]+vPosStride[1]])   );
170
171
20.3M
          if (1) { // edgeIdx != 0) {   // seems to be faster without this check (zero in offset table)
172
20.3M
            int offset = saoOffsetVal[edgeIdx+2];
173
174
20.3M
            out_ptr[i] = Clip3(0,maxPixelValue,
175
20.3M
                               in_ptr[i] + offset);
176
20.3M
          }
177
20.3M
        }
178
20.8M
      }
179
1.32M
    }
180
83.6k
  }
181
560k
  else {
182
560k
    int bandShift = bitDepth-5;
183
560k
    int saoLeftClass = saoinfo->sao_band_position[cIdx];
184
560k
    logtrace(LogSAO,"saoLeftClass: %d\n",saoLeftClass);
185
186
560k
    int bandTable[32];
187
560k
    memset(bandTable, 0, sizeof(int)*32);
188
189
2.80M
    for (int k=0;k<4;k++) {
190
2.24M
      bandTable[ (k+saoLeftClass)&31 ] = k+1;
191
2.24M
    }
192
193
194
    /* If PCM or transquant_bypass is used in this CTB, we have to
195
       run all checks (A).
196
       Otherwise, we run a simplified version of the code (B).
197
198
       NOTE: this whole part of SAO does not seem to be a significant part of the time spent
199
    */
200
201
560k
    if (extendedTests) {
202
203
      // (A) full version with all checks
204
205
2.95M
      for (int j=0;j<ctbH;j++)
206
89.9M
        for (int i=0;i<ctbW;i++) {
207
208
87.1M
          if ((sps->pcm_loop_filter_disable_flag &&
209
47.4M
               img->get_pcm_flag((xC+i)<<chromashiftW,(yC+j)<<chromashiftH)) ||
210
87.1M
              img->get_cu_transquant_bypass((xC+i)<<chromashiftW,(yC+j)<<chromashiftH)) {
211
78.7M
            continue;
212
78.7M
          }
213
214
          // Shifts are a strange thing. On x86, >>x actually computes >>(x%64).
215
          // But this should never happen, because the maximum bit-depth is 16.
216
8.36M
          int pixel = in_img[xC + i + (yC + j) * in_stride];
217
218
          // Note: the input pixel value should never exceed the valid range, but it seems that it still does,
219
          // maybe when there was a decoding error and the pixels have not been filled in correctly.
220
          // Thus, we have to limit the pixel range to ensure that we have no illegal table access.
221
8.36M
          pixel = Clip3(0, maxPixelValue, pixel);
222
223
8.36M
          int bandIdx = bandTable[pixel >> bandShift];
224
225
8.36M
          if (bandIdx>0) {
226
1.35M
            int offset = saoinfo->saoOffsetVal[cIdx][bandIdx-1];
227
228
1.35M
            logtrace(LogSAO,"%d %d (%d) offset %d  %x -> %x\n",xC+i,yC+j,bandIdx,
229
1.35M
                     offset,
230
1.35M
                     in_img[xC+i+(yC+j)*in_stride],
231
1.35M
                     in_img[xC+i+(yC+j)*in_stride]+offset);
232
233
1.35M
            out_img[xC+i+(yC+j)*out_stride] = Clip3(0,maxPixelValue,
234
1.35M
                                                    in_img[xC+i+(yC+j)*in_stride] + offset);
235
1.35M
          }
236
8.36M
        }
237
112k
    }
238
448k
    else
239
448k
      {
240
        // (B) simplified version (only works if no PCM and transquant_bypass is active)
241
242
5.25M
        for (int j=0;j<ctbH;j++)
243
112M
          for (int i=0;i<ctbW;i++) {
244
245
108M
            int pixel = in_img[xC + i + (yC + j) * in_stride];
246
247
            // Note: the input pixel value should never exceed the valid range, but it seems that it still does,
248
            // maybe when there was a decoding error and the pixels have not been filled in correctly.
249
            // Thus, we have to limit the pixel range to ensure that we have no illegal table access.
250
108M
            pixel = Clip3(0, maxPixelValue, pixel);
251
252
108M
            int bandIdx = bandTable[pixel >> bandShift];
253
254
108M
            if (bandIdx>0) {
255
20.8M
              int offset = saoinfo->saoOffsetVal[cIdx][bandIdx-1];
256
257
20.8M
              out_img[xC+i+(yC+j)*out_stride] = Clip3(0,maxPixelValue,
258
20.8M
                                                      in_img[xC+i+(yC+j)*in_stride] + offset);
259
20.8M
            }
260
108M
          }
261
448k
      }
262
560k
  }
263
644k
}
void apply_sao_internal<unsigned char>(de265_image*, int, int, slice_segment_header const*, int, int, int, unsigned char const*, long, unsigned char*, long)
Line
Count
Source
33
2.57M
{
34
2.57M
  const sao_info* saoinfo = img->get_sao_info(xCtb,yCtb);
35
36
2.57M
  int SaoTypeIdx = (saoinfo->SaoTypeIdx >> (2*cIdx)) & 0x3;
37
38
2.57M
  logtrace(LogSAO,"apply_sao CTB %d;%d cIdx:%d type=%d (%dx%d)\n",xCtb,yCtb,cIdx, SaoTypeIdx, nSW,nSH);
39
40
2.57M
  if (SaoTypeIdx==0) {
41
2.00M
    return;
42
2.00M
  }
43
44
565k
  const seq_parameter_set* sps = &img->get_sps();
45
565k
  const pic_parameter_set* pps = &img->get_pps();
46
565k
  const int bitDepth = (cIdx==0 ? sps->BitDepth_Y : sps->BitDepth_C);
47
565k
  const int maxPixelValue = (1<<bitDepth)-1;
48
49
  // top left position of CTB in pixels
50
565k
  const int xC = xCtb*nSW;
51
565k
  const int yC = yCtb*nSH;
52
53
565k
  const int width  = img->get_width(cIdx);
54
565k
  const int height = img->get_height(cIdx);
55
56
565k
  const int ctbSliceAddrRS = img->get_SliceHeader(xC,yC)->SliceAddrRS;
57
58
565k
  const int picWidthInCtbs = sps->PicWidthInCtbsY;
59
565k
  const int chromashiftW = sps->get_chroma_shift_W(cIdx);
60
565k
  const int chromashiftH = sps->get_chroma_shift_H(cIdx);
61
565k
  const int ctbshiftW = sps->Log2CtbSizeY - chromashiftW;
62
565k
  const int ctbshiftH = sps->Log2CtbSizeY - chromashiftH;
63
64
65
3.39M
  for (int i=0;i<5;i++)
66
2.82M
    {
67
2.82M
      logtrace(LogSAO,"offset[%d] = %d\n", i, i==0 ? 0 : saoinfo->saoOffsetVal[cIdx][i-1]);
68
2.82M
    }
69
70
71
  // actual size of CTB to be processed (can be smaller when partially outside of image)
72
565k
  const int ctbW = (xC+nSW>width)  ? width -xC : nSW;
73
565k
  const int ctbH = (yC+nSH>height) ? height-yC : nSH;
74
75
76
565k
  const bool extendedTests = img->get_CTB_has_pcm_or_cu_transquant_bypass(xCtb,yCtb);
77
78
565k
  if (SaoTypeIdx==2) {
79
90.1k
    int hPos[2], vPos[2];
80
90.1k
    ptrdiff_t vPosStride[2]; // vPos[] multiplied by image stride
81
90.1k
    int SaoEoClass = (saoinfo->SaoEoClass >> (2*cIdx)) & 0x3;
82
83
90.1k
    switch (SaoEoClass) {
84
18.5k
    case 0: hPos[0]=-1; hPos[1]= 1; vPos[0]= 0; vPos[1]=0; break;
85
19.3k
    case 1: hPos[0]= 0; hPos[1]= 0; vPos[0]=-1; vPos[1]=1; break;
86
15.6k
    case 2: hPos[0]=-1; hPos[1]= 1; vPos[0]=-1; vPos[1]=1; break;
87
36.5k
    case 3: hPos[0]= 1; hPos[1]=-1; vPos[0]=-1; vPos[1]=1; break;
88
90.1k
    }
89
90
90.1k
    vPosStride[0] = vPos[0] * in_stride;
91
90.1k
    vPosStride[1] = vPos[1] * in_stride;
92
93
    /* Reorder sao_info.saoOffsetVal[] array, so that we can index it
94
       directly with the sum of the two pixel-difference signs. */
95
90.1k
    int8_t  saoOffsetVal[5]; // [2] unused
96
90.1k
    saoOffsetVal[0] = saoinfo->saoOffsetVal[cIdx][1-1];
97
90.1k
    saoOffsetVal[1] = saoinfo->saoOffsetVal[cIdx][2-1];
98
90.1k
    saoOffsetVal[2] = 0;
99
90.1k
    saoOffsetVal[3] = saoinfo->saoOffsetVal[cIdx][3-1];
100
90.1k
    saoOffsetVal[4] = saoinfo->saoOffsetVal[cIdx][4-1];
101
102
103
1.29M
    for (int j=0;j<ctbH;j++) {
104
1.20M
      const pixel_t* in_ptr  = &in_img [xC+(yC+j)*in_stride];
105
1.20M
      /* */ pixel_t* out_ptr = &out_img[xC+(yC+j)*out_stride];
106
107
18.5M
      for (int i=0;i<ctbW;i++) {
108
17.3M
        int edgeIdx = -1;
109
110
17.3M
        logtrace(LogSAO, "pos %d,%d\n",xC+i,yC+j);
111
112
17.3M
        if ((extendedTests &&
113
10.2M
             (sps->pcm_loop_filter_disable_flag &&
114
1.19M
              img->get_pcm_flag((xC+i)<<chromashiftW,(yC+j)<<chromashiftH))) ||
115
17.3M
            img->get_cu_transquant_bypass((xC+i)<<chromashiftW,(yC+j)<<chromashiftH)) {
116
7.05M
          continue;
117
7.05M
        }
118
119
        // do the expensive test for boundaries only at the boundaries
120
10.2M
        bool testBoundary = (i==0 || j==0 || i==ctbW-1 || j==ctbH-1);
121
122
10.2M
        if (testBoundary)
123
7.19M
          for (int k=0;k<2;k++) {
124
4.91M
            int xS = xC+i+hPos[k];
125
4.91M
            int yS = yC+j+vPos[k];
126
127
4.91M
            if (xS<0 || yS<0 || xS>=width || yS>=height) {
128
221k
              edgeIdx=0;
129
221k
              break;
130
221k
            }
131
132
133
            // This part seems inefficient with all the get_SliceHeaderIndex() calls,
134
            // but removing this part (because the input was known to have only a single
135
            // slice anyway) reduced computation time only by 1.3%.
136
            // TODO: however, this may still be a big part of SAO itself.
137
138
4.69M
            slice_segment_header* sliceHeader = img->get_SliceHeader(xS<<chromashiftW,
139
4.69M
                                                                     yS<<chromashiftH);
140
4.69M
            if (sliceHeader==nullptr) { return; }
141
142
4.69M
            int sliceAddrRS = sliceHeader->SliceAddrRS;
143
4.69M
            if (sliceAddrRS <  ctbSliceAddrRS &&
144
6.48k
                img->get_SliceHeader((xC+i)<<chromashiftW,
145
6.48k
                                     (yC+j)<<chromashiftH)->slice_loop_filter_across_slices_enabled_flag==0) {
146
5.10k
              edgeIdx=0;
147
5.10k
              break;
148
5.10k
            }
149
150
4.68M
            if (sliceAddrRS >  ctbSliceAddrRS &&
151
10.1k
                img->get_SliceHeader(xS<<chromashiftW,
152
10.1k
                                     yS<<chromashiftH)->slice_loop_filter_across_slices_enabled_flag==0) {
153
6.66k
              edgeIdx=0;
154
6.66k
              break;
155
6.66k
            }
156
157
158
4.68M
            if (pps->loop_filter_across_tiles_enabled_flag==0 &&
159
4.51M
                pps->scan->TileIdRS[(xS>>ctbshiftW) + (yS>>ctbshiftH)*picWidthInCtbs] !=
160
4.51M
                pps->scan->TileIdRS[(xC>>ctbshiftW) + (yC>>ctbshiftH)*picWidthInCtbs]) {
161
8.61k
              edgeIdx=0;
162
8.61k
              break;
163
8.61k
            }
164
4.68M
          }
165
166
10.2M
        if (edgeIdx != 0) {
167
168
10.0M
          edgeIdx = ( Sign(in_ptr[i] - in_ptr[i+hPos[0]+vPosStride[0]]) +
169
10.0M
                      Sign(in_ptr[i] - in_ptr[i+hPos[1]+vPosStride[1]])   );
170
171
10.0M
          if (1) { // edgeIdx != 0) {   // seems to be faster without this check (zero in offset table)
172
10.0M
            int offset = saoOffsetVal[edgeIdx+2];
173
174
10.0M
            out_ptr[i] = Clip3(0,maxPixelValue,
175
10.0M
                               in_ptr[i] + offset);
176
10.0M
          }
177
10.0M
        }
178
10.2M
      }
179
1.20M
    }
180
90.1k
  }
181
475k
  else {
182
475k
    int bandShift = bitDepth-5;
183
475k
    int saoLeftClass = saoinfo->sao_band_position[cIdx];
184
475k
    logtrace(LogSAO,"saoLeftClass: %d\n",saoLeftClass);
185
186
475k
    int bandTable[32];
187
475k
    memset(bandTable, 0, sizeof(int)*32);
188
189
2.37M
    for (int k=0;k<4;k++) {
190
1.90M
      bandTable[ (k+saoLeftClass)&31 ] = k+1;
191
1.90M
    }
192
193
194
    /* If PCM or transquant_bypass is used in this CTB, we have to
195
       run all checks (A).
196
       Otherwise, we run a simplified version of the code (B).
197
198
       NOTE: this whole part of SAO does not seem to be a significant part of the time spent
199
    */
200
201
475k
    if (extendedTests) {
202
203
      // (A) full version with all checks
204
205
1.41M
      for (int j=0;j<ctbH;j++)
206
26.6M
        for (int i=0;i<ctbW;i++) {
207
208
25.3M
          if ((sps->pcm_loop_filter_disable_flag &&
209
4.50M
               img->get_pcm_flag((xC+i)<<chromashiftW,(yC+j)<<chromashiftH)) ||
210
25.3M
              img->get_cu_transquant_bypass((xC+i)<<chromashiftW,(yC+j)<<chromashiftH)) {
211
20.1M
            continue;
212
20.1M
          }
213
214
          // Shifts are a strange thing. On x86, >>x actually computes >>(x%64).
215
          // But this should never happen, because the maximum bit-depth is 16.
216
5.14M
          int pixel = in_img[xC + i + (yC + j) * in_stride];
217
218
          // Note: the input pixel value should never exceed the valid range, but it seems that it still does,
219
          // maybe when there was a decoding error and the pixels have not been filled in correctly.
220
          // Thus, we have to limit the pixel range to ensure that we have no illegal table access.
221
5.14M
          pixel = Clip3(0, maxPixelValue, pixel);
222
223
5.14M
          int bandIdx = bandTable[pixel >> bandShift];
224
225
5.14M
          if (bandIdx>0) {
226
563k
            int offset = saoinfo->saoOffsetVal[cIdx][bandIdx-1];
227
228
563k
            logtrace(LogSAO,"%d %d (%d) offset %d  %x -> %x\n",xC+i,yC+j,bandIdx,
229
563k
                     offset,
230
563k
                     in_img[xC+i+(yC+j)*in_stride],
231
563k
                     in_img[xC+i+(yC+j)*in_stride]+offset);
232
233
563k
            out_img[xC+i+(yC+j)*out_stride] = Clip3(0,maxPixelValue,
234
563k
                                                    in_img[xC+i+(yC+j)*in_stride] + offset);
235
563k
          }
236
5.14M
        }
237
84.5k
    }
238
391k
    else
239
391k
      {
240
        // (B) simplified version (only works if no PCM and transquant_bypass is active)
241
242
4.31M
        for (int j=0;j<ctbH;j++)
243
48.0M
          for (int i=0;i<ctbW;i++) {
244
245
44.1M
            int pixel = in_img[xC + i + (yC + j) * in_stride];
246
247
            // Note: the input pixel value should never exceed the valid range, but it seems that it still does,
248
            // maybe when there was a decoding error and the pixels have not been filled in correctly.
249
            // Thus, we have to limit the pixel range to ensure that we have no illegal table access.
250
44.1M
            pixel = Clip3(0, maxPixelValue, pixel);
251
252
44.1M
            int bandIdx = bandTable[pixel >> bandShift];
253
254
44.1M
            if (bandIdx>0) {
255
4.70M
              int offset = saoinfo->saoOffsetVal[cIdx][bandIdx-1];
256
257
4.70M
              out_img[xC+i+(yC+j)*out_stride] = Clip3(0,maxPixelValue,
258
4.70M
                                                      in_img[xC+i+(yC+j)*in_stride] + offset);
259
4.70M
            }
260
44.1M
          }
261
391k
      }
262
475k
  }
263
565k
}
264
265
266
template <class pixel_t>
267
void apply_sao(de265_image* img, int xCtb,int yCtb,
268
               const slice_segment_header* shdr, int cIdx, int nSW,int nSH,
269
               const pixel_t* in_img,  ptrdiff_t in_stride,
270
               /* */ pixel_t* out_img, ptrdiff_t out_stride)
271
3.58M
{
272
3.58M
  if (img->high_bit_depth(cIdx)) {
273
1.01M
    apply_sao_internal<uint16_t>(img,xCtb,yCtb, shdr,cIdx,nSW,nSH,
274
1.01M
                                 reinterpret_cast<const uint16_t*>(in_img), in_stride,
275
1.01M
                                 reinterpret_cast<uint16_t*>(out_img),out_stride);
276
1.01M
  }
277
2.57M
  else {
278
2.57M
    apply_sao_internal<uint8_t>(img,xCtb,yCtb, shdr,cIdx,nSW,nSH,
279
2.57M
                                in_img, in_stride,
280
2.57M
                                out_img,out_stride);
281
2.57M
  }
282
3.58M
}
283
284
285
void apply_sample_adaptive_offset(de265_image* img)
286
0
{
287
0
  const seq_parameter_set& sps = img->get_sps();
288
289
0
  if (sps.sample_adaptive_offset_enabled_flag==0) {
290
0
    return;
291
0
  }
292
293
0
  de265_image inputCopy;
294
0
  de265_error err = inputCopy.copy_image(img);
295
0
  if (err != DE265_OK) {
296
0
    img->decctx->add_warning(DE265_WARNING_CANNOT_APPLY_SAO_OUT_OF_MEMORY,false);
297
0
    return;
298
0
  }
299
300
0
  for (int yCtb=0; yCtb<sps.PicHeightInCtbsY; yCtb++)
301
0
    for (int xCtb=0; xCtb<sps.PicWidthInCtbsY; xCtb++)
302
0
      {
303
0
        const slice_segment_header* shdr = img->get_SliceHeaderCtb(xCtb,yCtb);
304
305
0
        if (shdr->slice_sao_luma_flag) {
306
0
          apply_sao(img, xCtb,yCtb, shdr, 0, 1<<sps.Log2CtbSizeY, 1<<sps.Log2CtbSizeY,
307
0
                    inputCopy.get_image_plane(0), inputCopy.get_image_stride(0),
308
0
                    img->get_image_plane(0), img->get_image_stride(0));
309
0
        }
310
311
0
        if (shdr->slice_sao_chroma_flag) {
312
0
          int nSW = (1<<sps.Log2CtbSizeY) / sps.SubWidthC;
313
0
          int nSH = (1<<sps.Log2CtbSizeY) / sps.SubHeightC;
314
315
0
          apply_sao(img, xCtb,yCtb, shdr, 1, nSW,nSH,
316
0
                    inputCopy.get_image_plane(1), inputCopy.get_image_stride(1),
317
0
                    img->get_image_plane(1), img->get_image_stride(1));
318
319
0
          apply_sao(img, xCtb,yCtb, shdr, 2, nSW,nSH,
320
0
                    inputCopy.get_image_plane(2), inputCopy.get_image_stride(2),
321
0
                    img->get_image_plane(2), img->get_image_stride(2));
322
0
        }
323
0
      }
324
0
}
325
326
327
void apply_sample_adaptive_offset_sequential(de265_image* img)
328
0
{
329
0
  const seq_parameter_set& sps = img->get_sps();
330
331
0
  if (sps.sample_adaptive_offset_enabled_flag==0) {
332
0
    return;
333
0
  }
334
335
0
  size_t lumaImageSize   = static_cast<size_t>(img->get_image_stride(0)) * img->get_height(0) * img->get_bytes_per_pixel(0);
336
0
  size_t chromaImageSize = static_cast<size_t>(img->get_image_stride(1)) * img->get_height(1) * img->get_bytes_per_pixel(1);
337
338
0
  uint8_t* inputCopy = new uint8_t[ std::max(lumaImageSize, chromaImageSize) ];
339
0
  if (inputCopy == nullptr) {
340
0
    img->decctx->add_warning(DE265_WARNING_CANNOT_APPLY_SAO_OUT_OF_MEMORY,false);
341
0
    return;
342
0
  }
343
344
345
0
  int nChannels = 3;
346
0
  if (sps.ChromaArrayType == CHROMA_MONO) { nChannels=1; }
347
348
0
  for (int cIdx=0;cIdx<nChannels;cIdx++) {
349
350
0
    ptrdiff_t stride = img->get_image_stride(cIdx);
351
0
    int height = img->get_height(cIdx);
352
353
0
    memcpy(inputCopy, img->get_image_plane(cIdx), static_cast<size_t>(stride) * height * img->get_bytes_per_pixel(cIdx));
354
355
0
    for (int yCtb=0; yCtb<sps.PicHeightInCtbsY; yCtb++)
356
0
      for (int xCtb=0; xCtb<sps.PicWidthInCtbsY; xCtb++)
357
0
        {
358
0
          const slice_segment_header* shdr = img->get_SliceHeaderCtb(xCtb,yCtb);
359
0
          if (shdr==nullptr) {
360
0
      delete[] inputCopy;
361
0
      return;
362
0
    }
363
364
0
          if (cIdx==0 && shdr->slice_sao_luma_flag) {
365
0
            apply_sao(img, xCtb,yCtb, shdr, 0, 1<<sps.Log2CtbSizeY, 1<<sps.Log2CtbSizeY,
366
0
                      inputCopy, stride,
367
0
                      img->get_image_plane(0), img->get_image_stride(0));
368
0
          }
369
370
0
          if (cIdx!=0 && shdr->slice_sao_chroma_flag) {
371
0
            int nSW = (1<<sps.Log2CtbSizeY) / sps.SubWidthC;
372
0
            int nSH = (1<<sps.Log2CtbSizeY) / sps.SubHeightC;
373
374
0
            apply_sao(img, xCtb,yCtb, shdr, cIdx, nSW,nSH,
375
0
                      inputCopy, stride,
376
0
                      img->get_image_plane(cIdx), img->get_image_stride(cIdx));
377
0
          }
378
0
        }
379
0
  }
380
381
0
  delete[] inputCopy;
382
0
}
383
384
385
386
387
class thread_task_sao : public thread_task
388
{
389
public:
390
  int  ctb_y;
391
  de265_image* img; /* this is where we get the SPS from
392
                       (either inputImg or outputImg can be a dummy image)
393
                    */
394
395
  de265_image* inputImg;
396
  de265_image* outputImg;
397
  int inputProgress;
398
399
  virtual void work();
400
0
  virtual std::string name() const {
401
0
    char buf[100];
402
0
    sprintf(buf,"sao-%d",ctb_y);
403
0
    return buf;
404
0
  }
405
};
406
407
408
void thread_task_sao::work()
409
42.9k
{
410
42.9k
  state = Running;
411
42.9k
  img->thread_run(this);
412
413
42.9k
  const seq_parameter_set& sps = img->get_sps();
414
415
42.9k
  const int rightCtb = sps.PicWidthInCtbsY-1;
416
42.9k
  const int ctbSize  = (1<<sps.Log2CtbSizeY);
417
418
419
  // wait until also the CTB-rows below and above are ready
420
421
42.9k
  img->wait_for_progress(this, rightCtb,ctb_y,  inputProgress);
422
423
42.9k
  if (ctb_y>0) {
424
32.0k
    img->wait_for_progress(this, rightCtb,ctb_y-1, inputProgress);
425
32.0k
  }
426
427
42.9k
  if (ctb_y+1<sps.PicHeightInCtbsY) {
428
32.0k
    img->wait_for_progress(this, rightCtb,ctb_y+1, inputProgress);
429
32.0k
  }
430
431
432
  // copy input image to output for this CTB-row
433
434
42.9k
  outputImg->copy_lines_from(inputImg, ctb_y * ctbSize, (ctb_y+1) * ctbSize);
435
436
437
  // process SAO in the CTB-row
438
439
2.56M
  for (int xCtb=0; xCtb<sps.PicWidthInCtbsY; xCtb++)
440
2.52M
    {
441
2.52M
      const slice_segment_header* shdr = img->get_SliceHeaderCtb(xCtb,ctb_y);
442
2.52M
      if (shdr==nullptr) {
443
0
        break;
444
0
      }
445
446
2.52M
      if (shdr->slice_sao_luma_flag) {
447
1.25M
        apply_sao(img, xCtb,ctb_y, shdr, 0, ctbSize, ctbSize,
448
1.25M
                  inputImg ->get_image_plane(0), inputImg ->get_image_stride(0),
449
1.25M
                  outputImg->get_image_plane(0), outputImg->get_image_stride(0));
450
1.25M
      }
451
452
2.52M
      if (shdr->slice_sao_chroma_flag) {
453
1.16M
        int nSW = ctbSize / sps.SubWidthC;
454
1.16M
        int nSH = ctbSize / sps.SubHeightC;
455
456
1.16M
        apply_sao(img, xCtb,ctb_y, shdr, 1, nSW,nSH,
457
1.16M
                  inputImg ->get_image_plane(1), inputImg ->get_image_stride(1),
458
1.16M
                  outputImg->get_image_plane(1), outputImg->get_image_stride(1));
459
460
1.16M
        apply_sao(img, xCtb,ctb_y, shdr, 2, nSW,nSH,
461
1.16M
                  inputImg ->get_image_plane(2), inputImg ->get_image_stride(2),
462
1.16M
                  outputImg->get_image_plane(2), outputImg->get_image_stride(2));
463
1.16M
      }
464
2.52M
    }
465
466
467
  // mark SAO progress
468
469
2.56M
  for (int x=0;x<=rightCtb;x++) {
470
2.52M
    const int CtbWidth = sps.PicWidthInCtbsY;
471
2.52M
    img->ctb_progress[x+ctb_y*CtbWidth].set_progress(CTB_PROGRESS_SAO);
472
2.52M
  }
473
474
475
42.9k
  state = Finished;
476
42.9k
  img->thread_finishes(this);
477
42.9k
}
478
479
480
bool add_sao_tasks(image_unit* imgunit, int saoInputProgress)
481
12.5k
{
482
12.5k
  de265_image* img = imgunit->img;
483
12.5k
  const seq_parameter_set& sps = img->get_sps();
484
485
12.5k
  if (sps.sample_adaptive_offset_enabled_flag==0) {
486
1.59k
    return false;
487
1.59k
  }
488
489
490
10.9k
  decoder_context* ctx = img->decctx;
491
492
10.9k
  de265_error err = imgunit->sao_output.alloc_image(img->get_width(), img->get_height(),
493
10.9k
                                                    img->get_chroma_format(),
494
10.9k
                                                    img->get_shared_sps(),
495
10.9k
                                                    false,
496
10.9k
                                                    img->decctx, //img->encctx,
497
10.9k
                                                    img->pts, img->user_data, true);
498
10.9k
  if (err != DE265_OK) {
499
0
    img->decctx->add_warning(DE265_WARNING_CANNOT_APPLY_SAO_OUT_OF_MEMORY,false);
500
0
    return false;
501
0
  }
502
503
10.9k
  int nRows = sps.PicHeightInCtbsY;
504
505
10.9k
  img->thread_start(nRows);
506
507
53.9k
  for (int y=0;y<nRows;y++)
508
42.9k
    {
509
42.9k
      thread_task_sao* task = new thread_task_sao;
510
511
42.9k
      task->inputImg  = img;
512
42.9k
      task->outputImg = &imgunit->sao_output;
513
42.9k
      task->img = img;
514
42.9k
      task->ctb_y = y;
515
42.9k
      task->inputProgress = saoInputProgress;
516
517
42.9k
      imgunit->tasks.push_back(task);
518
42.9k
      ctx->thread_pool_.add_task(task);
519
42.9k
    }
520
521
  /* Currently need barrier here because when are finished, we have to swap the pixel
522
     data back into the main image. */
523
10.9k
  img->wait_for_completion();
524
525
10.9k
  img->exchange_pixel_data_with(imgunit->sao_output);
526
527
10.9k
  return true;
528
10.9k
}