Coverage Report

Created: 2026-06-15 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/libde265/libde265/deblock.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 "deblock.h"
22
#include "util.h"
23
#include "transform.h"
24
#include "de265.h"
25
#include "decctx.h"
26
#include "fallback-deblk.h"
27
28
#include <assert.h>
29
30
31
32
// 8.7.2.1 for both EDGE_HOR and EDGE_VER at the same time
33
void markTransformBlockBoundary(de265_image* img, int x0,int y0,
34
                                int log2TrafoSize,int trafoDepth,
35
                                int filterLeftCbEdge, int filterTopCbEdge)
36
0
{
37
0
  logtrace(LogDeblock,"markTransformBlockBoundary(%d,%d, %d,%d, %d,%d)\n",x0,y0,
38
0
           log2TrafoSize,trafoDepth, filterLeftCbEdge,filterTopCbEdge);
39
40
0
  int split_transform = img->get_split_transform_flag(x0,y0,trafoDepth);
41
0
  if (split_transform) {
42
0
    int x1 = x0 + ((1<<log2TrafoSize)>>1);
43
0
    int y1 = y0 + ((1<<log2TrafoSize)>>1);
44
45
0
    markTransformBlockBoundary(img,x0,y0,log2TrafoSize-1,trafoDepth+1, filterLeftCbEdge,   filterTopCbEdge);
46
0
    markTransformBlockBoundary(img,x1,y0,log2TrafoSize-1,trafoDepth+1, DEBLOCK_FLAG_VERTI, filterTopCbEdge);
47
0
    markTransformBlockBoundary(img,x0,y1,log2TrafoSize-1,trafoDepth+1, filterLeftCbEdge,   DEBLOCK_FLAG_HORIZ);
48
0
    markTransformBlockBoundary(img,x1,y1,log2TrafoSize-1,trafoDepth+1, DEBLOCK_FLAG_VERTI, DEBLOCK_FLAG_HORIZ);
49
0
  }
50
0
  else {
51
    // VER
52
53
0
    for (int k=0;k<(1<<log2TrafoSize);k+=4) {
54
0
      img->set_deblk_flags(x0,y0+k, filterLeftCbEdge);
55
0
    }
56
57
    // HOR
58
59
0
    for (int k=0;k<(1<<log2TrafoSize);k+=4) {
60
0
      img->set_deblk_flags(x0+k,y0, filterTopCbEdge);
61
0
    }
62
0
  }
63
0
}
64
65
66
67
// 8.7.2.2 for both EDGE_HOR and EDGE_VER at the same time
68
void markPredictionBlockBoundary(de265_image* img, int x0,int y0,
69
                                 int log2CbSize,
70
                                 int filterLeftCbEdge, int filterTopCbEdge)
71
0
{
72
0
  logtrace(LogDeblock,"markPredictionBlockBoundary(%d,%d, %d, %d,%d)\n",x0,y0,
73
0
           log2CbSize, filterLeftCbEdge,filterTopCbEdge);
74
75
0
  enum PartMode partMode = img->get_PartMode(x0,y0);
76
77
0
  int cbSize = 1<<log2CbSize;
78
0
  int cbSize2 = 1<<(log2CbSize-1);
79
0
  int cbSize4 = 1<<(log2CbSize-2);
80
81
0
  switch (partMode) {
82
0
  case PART_NxN:
83
0
    for (int k=0;k<cbSize;k++) {
84
0
      img->set_deblk_flags(x0+cbSize2,y0+k, DEBLOCK_PB_EDGE_VERTI);
85
0
      img->set_deblk_flags(x0+k,y0+cbSize2, DEBLOCK_PB_EDGE_HORIZ);
86
0
    }
87
0
    break;
88
89
0
  case PART_Nx2N:
90
0
    for (int k=0;k<cbSize;k++) {
91
0
      img->set_deblk_flags(x0+cbSize2,y0+k, DEBLOCK_PB_EDGE_VERTI);
92
0
    }
93
0
    break;
94
95
0
  case PART_2NxN:
96
0
    for (int k=0;k<cbSize;k++) {
97
0
      img->set_deblk_flags(x0+k,y0+cbSize2, DEBLOCK_PB_EDGE_HORIZ);
98
0
    }
99
0
    break;
100
101
0
  case PART_nLx2N:
102
0
    for (int k=0;k<cbSize;k++) {
103
0
      img->set_deblk_flags(x0+cbSize4,y0+k, DEBLOCK_PB_EDGE_VERTI);
104
0
    }
105
0
    break;
106
107
0
  case PART_nRx2N:
108
0
    for (int k=0;k<cbSize;k++) {
109
0
      img->set_deblk_flags(x0+cbSize2+cbSize4,y0+k, DEBLOCK_PB_EDGE_VERTI);
110
0
    }
111
0
    break;
112
113
0
  case PART_2NxnU:
114
0
    for (int k=0;k<cbSize;k++) {
115
0
      img->set_deblk_flags(x0+k,y0+cbSize4, DEBLOCK_PB_EDGE_HORIZ);
116
0
    }
117
0
    break;
118
119
0
  case PART_2NxnD:
120
0
    for (int k=0;k<cbSize;k++) {
121
0
      img->set_deblk_flags(x0+k,y0+cbSize2+cbSize4, DEBLOCK_PB_EDGE_HORIZ);
122
0
    }
123
0
    break;
124
125
0
  case PART_2Nx2N:
126
    // NOP
127
0
    break;
128
0
  }
129
0
}
130
131
132
bool derive_edgeFlags_CTBRow(de265_image* img, uint16_t ctby)
133
0
{
134
0
  const seq_parameter_set& sps = img->get_sps();
135
0
  const pic_parameter_set& pps = img->get_pps();
136
137
0
  const int minCbSize = sps.MinCbSizeY;
138
0
  bool deblocking_enabled=false; // whether deblocking is enabled in some part of the image
139
140
0
  int ctb_mask = (1<<sps.Log2CtbSizeY)-1;
141
0
  int picWidthInCtbs = sps.PicWidthInCtbsY;
142
0
  int ctbshift = sps.Log2CtbSizeY;
143
144
145
0
  uint16_t cb_y_start = ( ctby    << sps.Log2CtbSizeY) >> sps.Log2MinCbSizeY;
146
0
  uint16_t cb_y_end   = ((ctby+1) << sps.Log2CtbSizeY) >> sps.Log2MinCbSizeY;
147
148
0
  cb_y_end = std::min(cb_y_end, sps.PicHeightInMinCbsY);
149
150
0
  for (int cb_y=cb_y_start;cb_y<cb_y_end;cb_y++)
151
0
    for (int cb_x=0;cb_x<img->get_sps().PicWidthInMinCbsY;cb_x++)
152
0
      {
153
0
        int log2CbSize = img->get_log2CbSize_cbUnits(cb_x,cb_y);
154
0
        if (log2CbSize==0) {
155
0
          continue;
156
0
        }
157
158
        // we are now at the top corner of a CB
159
160
0
        int x0 = cb_x * minCbSize;
161
0
        int y0 = cb_y * minCbSize;
162
163
0
        int x0ctb = x0 >> ctbshift;
164
0
        int y0ctb = y0 >> ctbshift;
165
166
        // check for corrupted streams
167
0
        if (img->is_SliceHeader_available(x0,y0)==false) {
168
0
          return false;
169
0
        }
170
171
        // check whether we should filter this slice
172
173
0
        slice_segment_header* shdr = img->get_SliceHeader(x0,y0);
174
175
        // check whether to filter left and top edge
176
177
0
        uint8_t filterLeftCbEdge = DEBLOCK_FLAG_VERTI;
178
0
        uint8_t filterTopCbEdge  = DEBLOCK_FLAG_HORIZ;
179
0
        if (x0 == 0) filterLeftCbEdge = 0;
180
0
        if (y0 == 0) filterTopCbEdge  = 0;
181
182
        // check for slice and tile boundaries (8.7.2, step 2 in both processes)
183
184
0
        if (x0 && ((x0 & ctb_mask) == 0)) { // left edge at CTB boundary
185
0
          if (shdr->slice_loop_filter_across_slices_enabled_flag == 0 &&
186
0
              img->is_SliceHeader_available(x0-1,y0) && // for corrupted streams
187
0
              shdr->SliceAddrRS != img->get_SliceHeader(x0-1,y0)->SliceAddrRS)
188
0
            {
189
0
              filterLeftCbEdge = 0;
190
0
            }
191
0
          else if (pps.loop_filter_across_tiles_enabled_flag == 0 &&
192
0
                   pps.scan->TileIdRS[  x0ctb           +y0ctb*picWidthInCtbs] !=
193
0
                   pps.scan->TileIdRS[((x0-1)>>ctbshift)+y0ctb*picWidthInCtbs]) {
194
0
            filterLeftCbEdge = 0;
195
0
          }
196
0
        }
197
198
0
        if (y0 && ((y0 & ctb_mask) == 0)) { // top edge at CTB boundary
199
0
          if (shdr->slice_loop_filter_across_slices_enabled_flag == 0 &&
200
0
              img->is_SliceHeader_available(x0,y0-1) && // for corrupted streams
201
0
              shdr->SliceAddrRS != img->get_SliceHeader(x0,y0-1)->SliceAddrRS)
202
0
            {
203
0
              filterTopCbEdge = 0;
204
0
            }
205
0
          else if (pps.loop_filter_across_tiles_enabled_flag == 0 &&
206
0
                   pps.scan->TileIdRS[x0ctb+  y0ctb           *picWidthInCtbs] !=
207
0
                   pps.scan->TileIdRS[x0ctb+((y0-1)>>ctbshift)*picWidthInCtbs]) {
208
0
            filterTopCbEdge = 0;
209
0
          }
210
0
        }
211
212
213
        // mark edges
214
215
0
        if (shdr->slice_deblocking_filter_disabled_flag==0) {
216
0
          deblocking_enabled=true;
217
218
0
          markTransformBlockBoundary(img, x0,y0, log2CbSize,0,
219
0
                                     filterLeftCbEdge, filterTopCbEdge);
220
221
0
          markPredictionBlockBoundary(img, x0,y0, log2CbSize,
222
0
                                      filterLeftCbEdge, filterTopCbEdge);
223
0
        }
224
0
      }
225
226
0
  return deblocking_enabled;
227
0
}
228
229
230
bool derive_edgeFlags(de265_image* img)
231
0
{
232
0
  bool deblocking_enabled=false;
233
234
0
  for (int y=0;y<img->get_sps().PicHeightInCtbsY;y++) {
235
0
    deblocking_enabled |= derive_edgeFlags_CTBRow(img,y);
236
0
  }
237
238
0
  return deblocking_enabled;
239
0
}
240
241
242
// 8.7.2.3 (both, EDGE_VER and EDGE_HOR)
243
void derive_boundaryStrength(de265_image* img, bool vertical, int yStart,int yEnd,
244
                             int xStart,int xEnd)
245
0
{
246
0
  int xIncr = vertical ? 2 : 1;
247
0
  int yIncr = vertical ? 1 : 2;
248
0
  int xOffs = vertical ? 1 : 0;
249
0
  int yOffs = vertical ? 0 : 1;
250
0
  int edgeMask = vertical ?
251
0
    (DEBLOCK_FLAG_VERTI | DEBLOCK_PB_EDGE_VERTI) :
252
0
    (DEBLOCK_FLAG_HORIZ | DEBLOCK_PB_EDGE_HORIZ);
253
0
  int transformEdgeMask = vertical ? DEBLOCK_FLAG_VERTI : DEBLOCK_FLAG_HORIZ;
254
255
0
  xEnd = std::min(xEnd,img->get_deblk_width());
256
0
  yEnd = std::min(yEnd,img->get_deblk_height());
257
258
  //int TUShift = img->get_sps().Log2MinTrafoSize;
259
  //int TUStride= img->get_sps().PicWidthInTbsY;
260
261
0
  for (int y=yStart;y<yEnd;y+=yIncr)
262
0
    for (int x=xStart;x<xEnd;x+=xIncr) {
263
0
      int xDi = x<<2;
264
0
      int yDi = y<<2;
265
266
0
      logtrace(LogDeblock,"%d %d %s = %s\n",xDi,yDi, vertical?"Vertical":"Horizontal",
267
0
               (img->get_deblk_flags(xDi,yDi) & edgeMask) ? "edge" : "...");
268
269
0
      uint8_t edgeFlags = img->get_deblk_flags(xDi,yDi);
270
271
0
      if (edgeFlags & edgeMask) {
272
0
        bool p_is_intra_pred = (img->get_pred_mode(xDi-xOffs, yDi-yOffs) == MODE_INTRA);
273
0
        bool q_is_intra_pred = (img->get_pred_mode(xDi,       yDi      ) == MODE_INTRA);
274
275
0
        int bS;
276
277
0
        if (p_is_intra_pred || q_is_intra_pred) {
278
0
          bS = 2;
279
0
        }
280
0
        else {
281
          // opposing site
282
0
          int xDiOpp = xDi-xOffs;
283
0
          int yDiOpp = yDi-yOffs;
284
285
0
          if ((edgeFlags & transformEdgeMask) &&
286
0
              (img->get_nonzero_coefficient(xDi   ,yDi) ||
287
0
               img->get_nonzero_coefficient(xDiOpp,yDiOpp))) {
288
0
            bS = 1;
289
0
          }
290
0
          else {
291
292
0
            bS = 0;
293
294
0
            const PBMotion& mviP = img->get_mv_info(xDiOpp,yDiOpp);
295
0
            const PBMotion& mviQ = img->get_mv_info(xDi   ,yDi);
296
297
0
            slice_segment_header* shdrP = img->get_SliceHeader(xDiOpp,yDiOpp);
298
0
            slice_segment_header* shdrQ = img->get_SliceHeader(xDi   ,yDi);
299
300
0
      if (shdrP && shdrQ) {
301
302
0
        int refPicP0 = mviP.predFlag[0] ? shdrP->RefPicList[0][ mviP.refIdx[0] ] : -1;
303
0
        int refPicP1 = mviP.predFlag[1] ? shdrP->RefPicList[1][ mviP.refIdx[1] ] : -1;
304
0
        int refPicQ0 = mviQ.predFlag[0] ? shdrQ->RefPicList[0][ mviQ.refIdx[0] ] : -1;
305
0
        int refPicQ1 = mviQ.predFlag[1] ? shdrQ->RefPicList[1][ mviQ.refIdx[1] ] : -1;
306
307
0
        bool samePics = ((refPicP0==refPicQ0 && refPicP1==refPicQ1) ||
308
0
             (refPicP0==refPicQ1 && refPicP1==refPicQ0));
309
310
0
        if (!samePics) {
311
0
    bS = 1;
312
0
        }
313
0
        else {
314
0
    MotionVector mvP0 = mviP.mv[0]; if (!mviP.predFlag[0]) { mvP0.x=mvP0.y=0; }
315
0
    MotionVector mvP1 = mviP.mv[1]; if (!mviP.predFlag[1]) { mvP1.x=mvP1.y=0; }
316
0
    MotionVector mvQ0 = mviQ.mv[0]; if (!mviQ.predFlag[0]) { mvQ0.x=mvQ0.y=0; }
317
0
    MotionVector mvQ1 = mviQ.mv[1]; if (!mviQ.predFlag[1]) { mvQ1.x=mvQ1.y=0; }
318
319
0
    int numMV_P = mviP.predFlag[0] + mviP.predFlag[1];
320
0
    int numMV_Q = mviQ.predFlag[0] + mviQ.predFlag[1];
321
322
0
    if (numMV_P!=numMV_Q) {
323
0
      img->decctx->add_warning(DE265_WARNING_NUMMVP_NOT_EQUAL_TO_NUMMVQ, false);
324
0
      img->integrity = INTEGRITY_DECODING_ERRORS;
325
0
    }
326
327
    // two different reference pictures or only one reference picture
328
0
    if (refPicP0 != refPicP1) {
329
330
0
      if (refPicP0 == refPicQ0) {
331
0
        if (std::abs(mvP0.x-mvQ0.x) >= 4 ||
332
0
      std::abs(mvP0.y-mvQ0.y) >= 4 ||
333
0
      std::abs(mvP1.x-mvQ1.x) >= 4 ||
334
0
      std::abs(mvP1.y-mvQ1.y) >= 4) {
335
0
          bS = 1;
336
0
        }
337
0
      }
338
0
      else {
339
0
        if (std::abs(mvP0.x-mvQ1.x) >= 4 ||
340
0
      std::abs(mvP0.y-mvQ1.y) >= 4 ||
341
0
      std::abs(mvP1.x-mvQ0.x) >= 4 ||
342
0
      std::abs(mvP1.y-mvQ0.y) >= 4) {
343
0
          bS = 1;
344
0
        }
345
0
      }
346
0
    }
347
0
    else {
348
0
      assert(refPicQ0==refPicQ1);
349
350
0
      if ((std::abs(mvP0.x-mvQ0.x) >= 4 ||
351
0
           std::abs(mvP0.y-mvQ0.y) >= 4 ||
352
0
           std::abs(mvP1.x-mvQ1.x) >= 4 ||
353
0
           std::abs(mvP1.y-mvQ1.y) >= 4)
354
0
          &&
355
0
          (std::abs(mvP0.x-mvQ1.x) >= 4 ||
356
0
           std::abs(mvP0.y-mvQ1.y) >= 4 ||
357
0
           std::abs(mvP1.x-mvQ0.x) >= 4 ||
358
0
           std::abs(mvP1.y-mvQ0.y) >= 4)) {
359
0
        bS = 1;
360
0
      }
361
0
    }
362
0
        }
363
0
      }
364
0
      else {
365
0
        bS = 0; // if shdrP==nullptr or shdrQ==nullptr
366
0
      }
367
368
            /*
369
              printf("unimplemented deblocking code for CU at %d;%d\n",xDi,yDi);
370
371
              logerror(LogDeblock, "unimplemented code reached (file %s, line %d)\n",
372
              __FILE__, __LINE__);
373
            */
374
0
          }
375
0
        }
376
377
0
        img->set_deblk_bS(xDi,yDi, bS);
378
0
      }
379
0
      else {
380
0
        img->set_deblk_bS(xDi,yDi, 0);
381
0
      }
382
0
    }
383
0
}
384
385
386
void derive_boundaryStrength_CTB(de265_image* img, bool vertical, int xCtb,int yCtb)
387
0
{
388
0
  int ctbSize = img->get_sps().CtbSizeY;
389
0
  int deblkSize = ctbSize/4;
390
391
0
  derive_boundaryStrength(img,vertical,
392
0
                          yCtb*deblkSize, (yCtb+1)*deblkSize,
393
0
                          xCtb*deblkSize, (xCtb+1)*deblkSize);
394
0
}
395
396
397
static uint8_t table_8_23_beta[52] = {
398
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8,
399
   9,10,11,12,13,14,15,16,17,18,20,22,24,26,28,30,32,34,36,
400
  38,40,42,44,46,48,50,52,54,56,58,60,62,64
401
};
402
403
static uint8_t table_8_23_tc[54] = {
404
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
405
   1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,
406
   5, 5, 6, 6, 7, 8, 9,10,11,13,14,16,18,20,22,24
407
};
408
409
410
411
// 8.7.2.4
412
template <class pixel_t>
413
void edge_filtering_luma_internal(de265_image* img, bool vertical,
414
                                  int yStart,int yEnd, int xStart,int xEnd)
415
0
{
416
  //printf("luma %d-%d %d-%d\n",xStart,xEnd,yStart,yEnd);
417
418
0
  const seq_parameter_set& sps = img->get_sps();
419
420
0
  int xIncr = vertical ? 2 : 1;
421
0
  int yIncr = vertical ? 1 : 2;
422
423
0
  const int stride = img->get_image_stride(0);
424
425
0
  int bitDepth_Y = sps.BitDepth_Y;
426
427
0
  xEnd = std::min(xEnd,img->get_deblk_width());
428
0
  yEnd = std::min(yEnd,img->get_deblk_height());
429
430
0
  for (int y=yStart;y<yEnd;y+=yIncr)
431
0
    for (int x=xStart;x<xEnd;x+=xIncr) {
432
      // x;y in deblocking units (4x4 pixels)
433
434
0
      int xDi = x<<2; // *4 -> pixel resolution
435
0
      int yDi = y<<2; // *4 -> pixel resolution
436
0
      int bS = img->get_deblk_bS(xDi,yDi);
437
438
      //printf("x,y:%d,%d  xDi,yDi:%d,%d\n",x,y,xDi,yDi);
439
440
0
      logtrace(LogDeblock,"deblock POC=%d %c --- x:%d y:%d bS:%d---\n",
441
0
               img->PicOrderCntVal,vertical ? 'V':'H',xDi,yDi,bS);
442
443
#if 0
444
      {
445
        uint8_t* ptr = img->y + stride*yDi + xDi;
446
447
        for (int dy=-4;dy<4;dy++) {
448
          for (int dx=-4;dx<4;dx++) {
449
            printf("%02x ", ptr[dy*stride + dx]);
450
            if (dx==-1) printf("| ");
451
          }
452
          printf("\n");
453
          if (dy==-1) printf("-------------------------\n");
454
        }
455
      }
456
#endif
457
458
#if 0
459
      if (!vertical)
460
        {
461
          uint8_t* ptr = img->y + stride*yDi + xDi;
462
463
          for (int dy=-4;dy<4;dy++) {
464
            for (int dx=0;dx<4;dx++) {
465
              printf("%02x ", ptr[dy*stride + dx]);
466
              if (dx==-1) printf("| ");
467
            }
468
            printf("\n");
469
            if (dy==-1) printf("-------------------------\n");
470
          }
471
        }
472
#endif
473
474
0
      if (bS>0) {
475
476
        // 8.7.2.4.3
477
478
0
        pixel_t* ptr = img->get_image_plane_at_pos_NEW<pixel_t>(0, xDi,yDi);
479
480
0
        pixel_t q[4][4], p[4][4];
481
0
        for (int k=0;k<4;k++)
482
0
          for (int i=0;i<4;i++)
483
0
            {
484
0
              if (vertical) {
485
0
                q[k][i] = ptr[ i  +k*stride];
486
0
                p[k][i] = ptr[-i-1+k*stride];
487
0
              }
488
0
              else {
489
0
                q[k][i] = ptr[k + i   *stride];
490
0
                p[k][i] = ptr[k -(i+1)*stride];
491
0
              }
492
0
            }
493
494
#if 0
495
        for (int k=0;k<4;k++)
496
          {
497
            for (int i=0;i<4;i++)
498
              {
499
                printf("%02x ", p[k][3-i]);
500
              }
501
502
            printf("| ");
503
504
            for (int i=0;i<4;i++)
505
              {
506
                printf("%02x ", q[k][i]);
507
              }
508
            printf("\n");
509
          }
510
#endif
511
512
513
0
        int QP_Q = img->get_QPY(xDi,yDi);
514
0
        int QP_P = (vertical ?
515
0
                    img->get_QPY(xDi-1,yDi) :
516
0
                    img->get_QPY(xDi,yDi-1) );
517
0
        int qP_L = (QP_Q+QP_P+1)>>1;
518
519
0
        logtrace(LogDeblock,"QP: %d & %d -> %d\n",QP_Q,QP_P,qP_L);
520
521
0
        int sliceIndexQ00 = img->get_SliceHeaderIndex(xDi,yDi);
522
0
        int beta_offset = img->slices[sliceIndexQ00]->slice_beta_offset;
523
0
        int tc_offset   = img->slices[sliceIndexQ00]->slice_tc_offset;
524
525
0
        int Q_beta = Clip3(0,51, qP_L + beta_offset);
526
0
        int betaPrime = table_8_23_beta[Q_beta];
527
0
        int beta = betaPrime * (1<<(bitDepth_Y - 8));
528
529
0
        int Q_tc = Clip3(0,53, qP_L + 2*(bS-1) + tc_offset);
530
0
        int tcPrime = table_8_23_tc[Q_tc];
531
0
        int tc = tcPrime * (1<<(bitDepth_Y - 8));
532
533
0
        logtrace(LogDeblock,"beta: %d (%d)  tc: %d (%d)\n",beta,beta_offset, tc,tc_offset);
534
535
0
        int dE=0, dEp=0, dEq=0;
536
537
0
        int dp0 = std::abs(p[0][2] - 2*p[0][1] + p[0][0]);
538
0
        int dp3 = std::abs(p[3][2] - 2*p[3][1] + p[3][0]);
539
0
        int dq0 = std::abs(q[0][2] - 2*q[0][1] + q[0][0]);
540
0
        int dq3 = std::abs(q[3][2] - 2*q[3][1] + q[3][0]);
541
542
0
        int dpq0 = dp0 + dq0;
543
0
        int dpq3 = dp3 + dq3;
544
545
0
        int dp = dp0 + dp3;
546
0
        int dq = dq0 + dq3;
547
0
        int d = dpq0 + dpq3;
548
549
0
        if (d < beta) {
550
          //int dpq = 2*dpq0;
551
0
          bool dSam0 = (2 * dpq0 < (beta >> 2) &&
552
0
                        std::abs(p[0][3]-p[0][0]) + std::abs(q[0][0]-q[0][3]) < (beta >> 3) &&
553
0
                        std::abs(p[0][0]-q[0][0]) < ((5 * tc + 1) >> 1));
554
555
0
          bool dSam3 = (2 * dpq3 < (beta >> 2) &&
556
0
                        std::abs(p[3][3]-p[3][0]) + std::abs(q[3][0]-q[3][3]) < (beta >> 3) &&
557
0
                        std::abs(p[3][0]-q[3][0]) < ((5 * tc + 1) >> 1));
558
559
0
          if (dSam0 && dSam3) {
560
0
            dE = 2;
561
0
          }
562
0
          else {
563
0
            dE = 1;
564
0
          }
565
566
0
          if (dp < ((beta + (beta >> 1)) >> 3)) { dEp = 1; }
567
0
          if (dq < ((beta + (beta >> 1)) >> 3)) { dEq = 1; }
568
569
0
          logtrace(LogDeblock, "dE:%d dEp:%d dEq:%d\n", dE, dEp, dEq);
570
0
        }
571
572
573
        // 8.7.2.4.4
574
575
0
        if (dE != 0) {
576
0
          bool filterP = true;
577
0
          bool filterQ = true;
578
579
0
          if (vertical) {
580
0
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(xDi-1,yDi)) filterP=false;
581
0
            if (img->get_cu_transquant_bypass(xDi-1,yDi)) filterP=false;
582
583
0
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(xDi,yDi)) filterQ=false;
584
0
            if (img->get_cu_transquant_bypass(xDi,yDi)) filterQ=false;
585
0
          }
586
0
          else {
587
0
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(xDi,yDi-1)) filterP=false;
588
0
            if (img->get_cu_transquant_bypass(xDi,yDi-1)) filterP=false;
589
590
0
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(xDi,yDi)) filterQ=false;
591
0
            if (img->get_cu_transquant_bypass(xDi,yDi)) filterQ=false;
592
0
          }
593
594
0
          if constexpr (sizeof(pixel_t)==1) {
595
0
            img->decctx->acceleration.deblock_luma_8((uint8_t*)ptr, stride, vertical,
596
0
                                                     dE,dEp,dEq,tc, filterP,filterQ);
597
          }
598
0
          else {
599
0
            deblock_luma_kernel<pixel_t>(ptr, stride, vertical,
600
0
                                         dE,dEp,dEq,tc, filterP,filterQ, bitDepth_Y);
601
0
          }
602
0
        }
603
0
      }
604
0
    }
605
0
}
Unexecuted instantiation: void edge_filtering_luma_internal<unsigned short>(de265_image*, bool, int, int, int, int)
Unexecuted instantiation: void edge_filtering_luma_internal<unsigned char>(de265_image*, bool, int, int, int, int)
606
607
608
void edge_filtering_luma(de265_image* img, bool vertical,
609
                         int yStart,int yEnd, int xStart,int xEnd)
610
0
{
611
0
  if (img->high_bit_depth(0)) {
612
0
    edge_filtering_luma_internal<uint16_t>(img,vertical,yStart,yEnd,xStart,xEnd);
613
0
  }
614
0
  else {
615
0
    edge_filtering_luma_internal<uint8_t>(img,vertical,yStart,yEnd,xStart,xEnd);
616
0
  }
617
0
}
618
619
void edge_filtering_luma_CTB(de265_image* img, bool vertical, int xCtb,int yCtb)
620
0
{
621
0
  int ctbSize = img->get_sps().CtbSizeY;
622
0
  int deblkSize = ctbSize/4;
623
624
0
  edge_filtering_luma(img,vertical,
625
0
                      yCtb*deblkSize, (yCtb+1)*deblkSize,
626
0
                      xCtb*deblkSize, (xCtb+1)*deblkSize);
627
0
}
628
629
630
631
632
// 8.7.2.4
633
/** ?Start and ?End values in 4-luma pixels resolution.
634
 */
635
template <class pixel_t>
636
void edge_filtering_chroma_internal(de265_image* img, bool vertical,
637
                                    int yStart,int yEnd,
638
                                    int xStart,int xEnd)
639
0
{
640
  //printf("chroma %d-%d %d-%d\n",xStart,xEnd,yStart,yEnd);
641
642
0
  const seq_parameter_set& sps = img->get_sps();
643
644
0
  const int SubWidthC  = sps.SubWidthC;
645
0
  const int SubHeightC = sps.SubHeightC;
646
647
0
  int xIncr = vertical ? 2 : 1;
648
0
  int yIncr = vertical ? 1 : 2;
649
650
0
  xIncr *= SubWidthC;
651
0
  yIncr *= SubHeightC;
652
653
0
  const int stride = img->get_image_stride(1);
654
655
0
  xEnd = std::min(xEnd,img->get_deblk_width());
656
0
  yEnd = std::min(yEnd,img->get_deblk_height());
657
658
0
  int bitDepth_C = sps.BitDepth_C;
659
660
0
  for (int y=yStart;y<yEnd;y+=yIncr)
661
0
    for (int x=xStart;x<xEnd;x+=xIncr) {
662
0
      int xDi = x << (3-SubWidthC);
663
0
      int yDi = y << (3-SubHeightC);
664
665
      //printf("x,y:%d,%d  xDi,yDi:%d,%d\n",x,y,xDi,yDi);
666
667
0
      int bS = img->get_deblk_bS(xDi*SubWidthC,yDi*SubHeightC);
668
669
0
      if (bS>1) {
670
        // 8.7.2.4.5
671
672
0
        for (int cplane=0;cplane<2;cplane++) {
673
0
          int cQpPicOffset = (cplane==0 ?
674
0
                              img->get_pps().pic_cb_qp_offset :
675
0
                              img->get_pps().pic_cr_qp_offset);
676
677
0
          pixel_t* ptr = img->get_image_plane_at_pos_NEW<pixel_t>(cplane+1, xDi,yDi);
678
679
680
#if 0
681
          for (int k=0;k<4;k++)
682
            {
683
              for (int i=0;i<2;i++)
684
                {
685
                  printf("%02x ", p[1-i][k]);
686
                }
687
688
              printf("| ");
689
690
              for (int i=0;i<2;i++)
691
                {
692
                  printf("%02x ", q[i][k]);
693
                }
694
              printf("\n");
695
            }
696
#endif
697
698
0
          int QP_Q = img->get_QPY(SubWidthC*xDi,SubHeightC*yDi);
699
0
          int QP_P = (vertical ?
700
0
                      img->get_QPY(SubWidthC*xDi-1,SubHeightC*yDi) :
701
0
                      img->get_QPY(SubWidthC*xDi,SubHeightC*yDi-1));
702
0
          int qP_i = ((QP_Q+QP_P+1)>>1) + cQpPicOffset;
703
0
          int QP_C;
704
0
          if (sps.ChromaArrayType == CHROMA_420) {
705
0
            QP_C = table8_22(qP_i);
706
0
          } else {
707
0
            QP_C = std::min(qP_i, 51);
708
0
          }
709
710
711
          //printf("POC=%d\n",ctx->img->PicOrderCntVal);
712
0
          logtrace(LogDeblock,"%d %d: ((%d+%d+1)>>1) + %d = qP_i=%d  (QP_C=%d)\n",
713
0
                   SubWidthC*xDi,SubHeightC*yDi, QP_Q,QP_P,cQpPicOffset,qP_i,QP_C);
714
715
0
          int sliceIndexQ00 = img->get_SliceHeaderIndex(SubWidthC*xDi,SubHeightC*yDi);
716
0
          int tc_offset   = img->slices[sliceIndexQ00]->slice_tc_offset;
717
718
0
          int Q = Clip3(0,53, QP_C + 2*(bS-1) + tc_offset);
719
720
0
          int tcPrime = table_8_23_tc[Q];
721
0
          int tc = tcPrime * (1<<(sps.BitDepth_C - 8));
722
723
0
          logtrace(LogDeblock,"tc_offset=%d Q=%d tc'=%d tc=%d\n",tc_offset,Q,tcPrime,tc);
724
725
0
          if (vertical) {
726
0
            bool filterP = true;
727
0
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(SubWidthC*xDi-1,SubHeightC*yDi)) filterP=false;
728
0
            if (img->get_cu_transquant_bypass(SubWidthC*xDi-1,SubHeightC*yDi)) filterP=false;
729
730
0
            bool filterQ = true;
731
0
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(SubWidthC*xDi,SubHeightC*yDi)) filterQ=false;
732
0
            if (img->get_cu_transquant_bypass(SubWidthC*xDi,SubHeightC*yDi)) filterQ=false;
733
734
735
0
            if constexpr (sizeof(pixel_t)==1) {
736
0
              img->decctx->acceleration.deblock_chroma_8((uint8_t*)ptr, stride, 1, tc, filterP,filterQ);
737
            }
738
0
            else {
739
0
              deblock_chroma_kernel<pixel_t>(ptr, stride, true, tc, filterP,filterQ, bitDepth_C);
740
0
            }
741
0
          }
742
0
          else {
743
0
            bool filterP = true;
744
0
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(SubWidthC*xDi,SubHeightC*yDi-1)) filterP=false;
745
0
            if (img->get_cu_transquant_bypass(SubWidthC*xDi,SubHeightC*yDi-1)) filterP=false;
746
747
0
            bool filterQ = true;
748
0
            if (sps.pcm_loop_filter_disable_flag && img->get_pcm_flag(SubWidthC*xDi,SubHeightC*yDi)) filterQ=false;
749
0
            if (img->get_cu_transquant_bypass(SubWidthC*xDi,SubHeightC*yDi)) filterQ=false;
750
751
0
            if constexpr (sizeof(pixel_t)==1) {
752
0
              img->decctx->acceleration.deblock_chroma_8((uint8_t*)ptr, stride, 0, tc, filterP,filterQ);
753
            }
754
0
            else {
755
0
              deblock_chroma_kernel<pixel_t>(ptr, stride, false, tc, filterP,filterQ, bitDepth_C);
756
0
            }
757
0
          }
758
0
        }
759
0
      }
760
0
    }
761
0
}
Unexecuted instantiation: void edge_filtering_chroma_internal<unsigned short>(de265_image*, bool, int, int, int, int)
Unexecuted instantiation: void edge_filtering_chroma_internal<unsigned char>(de265_image*, bool, int, int, int, int)
762
763
764
void edge_filtering_chroma(de265_image* img, bool vertical, int yStart,int yEnd,
765
                           int xStart,int xEnd)
766
0
{
767
0
  if (img->high_bit_depth(1)) {
768
0
    edge_filtering_chroma_internal<uint16_t>(img,vertical,yStart,yEnd,xStart,xEnd);
769
0
  }
770
0
  else {
771
0
    edge_filtering_chroma_internal<uint8_t>(img,vertical,yStart,yEnd,xStart,xEnd);
772
0
  }
773
0
}
774
775
776
void edge_filtering_chroma_CTB(de265_image* img, bool vertical, int xCtb,int yCtb)
777
0
{
778
0
  int ctbSize = img->get_sps().CtbSizeY;
779
0
  int deblkSize = ctbSize/4;
780
781
0
  edge_filtering_chroma(img,vertical,
782
0
                        yCtb*deblkSize, (yCtb+1)*deblkSize,
783
0
                        xCtb*deblkSize, (xCtb+1)*deblkSize);
784
0
}
785
786
787
788
class thread_task_deblock_CTBRow : public thread_task
789
{
790
public:
791
  struct de265_image* img;
792
  int  ctb_y;
793
  bool vertical;
794
795
  virtual void work();
796
0
  virtual std::string name() const {
797
0
    char buf[100];
798
0
    sprintf(buf,"deblock-%d",ctb_y);
799
0
    return buf;
800
0
  }
801
};
802
803
804
void thread_task_deblock_CTBRow::work()
805
0
{
806
0
  state = Running;
807
0
  img->thread_run(this);
808
809
0
  int xStart=0;
810
0
  int xEnd = img->get_deblk_width();
811
812
0
  int ctbSize = img->get_sps().CtbSizeY;
813
0
  int deblkSize = ctbSize/4;
814
815
0
  int first =  ctb_y    * deblkSize;
816
0
  int last  = (ctb_y+1) * deblkSize;
817
0
  if (last > img->get_deblk_height()) {
818
0
    last = img->get_deblk_height();
819
0
  }
820
821
0
  int finalProgress = CTB_PROGRESS_DEBLK_V;
822
0
  if (!vertical) finalProgress = CTB_PROGRESS_DEBLK_H;
823
824
0
  int rightCtb = img->get_sps().PicWidthInCtbsY-1;
825
826
0
  if (vertical) {
827
    // pass 1: vertical
828
829
0
    int CtbRow = std::min(ctb_y+1 , img->get_sps().PicHeightInCtbsY-1);
830
0
    img->wait_for_progress(this, rightCtb,CtbRow, CTB_PROGRESS_PREFILTER);
831
0
  }
832
0
  else {
833
    // pass 2: horizontal
834
835
0
    if (ctb_y>0) {
836
0
      img->wait_for_progress(this, rightCtb,ctb_y-1, CTB_PROGRESS_DEBLK_V);
837
0
    }
838
839
0
    img->wait_for_progress(this, rightCtb,ctb_y,  CTB_PROGRESS_DEBLK_V);
840
841
0
    if (ctb_y+1<img->get_sps().PicHeightInCtbsY) {
842
0
      img->wait_for_progress(this, rightCtb,ctb_y+1, CTB_PROGRESS_DEBLK_V);
843
0
    }
844
0
  }
845
846
  //printf("deblock %d to %d orientation: %d\n",first,last,vertical);
847
848
0
  bool deblocking_enabled;
849
850
  // first pass: check edge flags and whether we have to deblock
851
0
  if (vertical) {
852
0
    deblocking_enabled = derive_edgeFlags_CTBRow(img, ctb_y);
853
854
    //for (int x=0;x<=rightCtb;x++) {
855
0
    int x=0; img->set_CtbDeblockFlag(x,ctb_y, deblocking_enabled);
856
    //}
857
0
  }
858
0
  else {
859
0
    int x=0; deblocking_enabled=img->get_CtbDeblockFlag(x,ctb_y);
860
0
  }
861
862
0
  if (deblocking_enabled) {
863
0
    derive_boundaryStrength(img, vertical, first,last, xStart,xEnd);
864
865
0
    edge_filtering_luma(img, vertical, first,last, xStart,xEnd);
866
867
0
    if (img->get_sps().ChromaArrayType != CHROMA_MONO) {
868
0
      edge_filtering_chroma(img, vertical, first,last, xStart,xEnd);
869
0
    }
870
0
  }
871
872
0
  for (int x=0;x<=rightCtb;x++) {
873
0
    const int CtbWidth = img->get_sps().PicWidthInCtbsY;
874
0
    img->ctb_progress[x+ctb_y*CtbWidth].set_progress(finalProgress);
875
0
  }
876
877
0
  state = Finished;
878
0
  img->thread_finishes(this);
879
0
}
880
881
882
void add_deblocking_tasks(image_unit* imgunit)
883
0
{
884
0
  de265_image* img = imgunit->img;
885
0
  decoder_context* ctx = img->decctx;
886
887
0
  int nRows = img->get_sps().PicHeightInCtbsY;
888
889
0
  img->thread_start(nRows*2);
890
891
0
  for (int pass=0;pass<2;pass++)
892
0
    {
893
0
      for (int y=0;y<img->get_sps().PicHeightInCtbsY;y++)
894
0
        {
895
0
          thread_task_deblock_CTBRow* task = new thread_task_deblock_CTBRow;
896
897
0
          task->img   = img;
898
0
          task->ctb_y = y;
899
0
          task->vertical = (pass==0);
900
901
0
          imgunit->tasks.push_back(task);
902
0
          ctx->thread_pool_.add_task(task);
903
0
        }
904
0
    }
905
0
}
906
907
908
void apply_deblocking_filter(de265_image* img) // decoder_context* ctx)
909
0
{
910
  //decoder_context* ctx = img->decctx;
911
912
0
  char enabled_deblocking = derive_edgeFlags(img);
913
914
0
  if (enabled_deblocking)
915
0
    {
916
      // vertical filtering
917
918
0
      logtrace(LogDeblock,"VERTICAL\n");
919
0
      derive_boundaryStrength(img, true ,0,img->get_deblk_height(),0,img->get_deblk_width());
920
0
      edge_filtering_luma    (img, true ,0,img->get_deblk_height(),0,img->get_deblk_width());
921
922
0
      if (img->get_sps().ChromaArrayType != CHROMA_MONO) {
923
0
        edge_filtering_chroma  (img, true ,0,img->get_deblk_height(),0,img->get_deblk_width());
924
0
      }
925
#if 0
926
      char buf[1000];
927
      sprintf(buf,"lf-after-V-%05d.yuv", ctx->img->PicOrderCntVal);
928
      write_picture_to_file(ctx->img, buf);
929
#endif
930
931
      // horizontal filtering
932
933
0
      logtrace(LogDeblock,"HORIZONTAL\n");
934
0
      derive_boundaryStrength(img, false ,0,img->get_deblk_height(),0,img->get_deblk_width());
935
0
      edge_filtering_luma    (img, false ,0,img->get_deblk_height(),0,img->get_deblk_width());
936
937
0
      if (img->get_sps().ChromaArrayType != CHROMA_MONO) {
938
0
        edge_filtering_chroma  (img, false ,0,img->get_deblk_height(),0,img->get_deblk_width());
939
0
      }
940
941
#if 0
942
      sprintf(buf,"lf-after-H-%05d.yuv", ctx->img->PicOrderCntVal);
943
      write_picture_to_file(ctx->img, buf);
944
#endif
945
0
    }
946
0
}