Coverage Report

Created: 2026-09-14 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/avm/av2/common/ccso.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2021, Alliance for Open Media. All rights reserved
3
 *
4
 * This source code is subject to the terms of the BSD 3-Clause Clear License
5
 * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6
 * License was not distributed with this source code in the LICENSE file, you
7
 * can obtain it at aomedia.org/license/software-license/bsd-3-c-c/.  If the
8
 * Alliance for Open Media Patent License 1.0 was not distributed with this
9
 * source code in the PATENTS file, you can obtain it at
10
 * aomedia.org/license/patent-license/.
11
 */
12
13
#include <assert.h>
14
#include <math.h>
15
#include <string.h>
16
17
#include "config/avm_scale_rtcd.h"
18
19
#include "avm/avm_integer.h"
20
#include "av2/common/ccso.h"
21
#include "av2/common/reconinter.h"
22
#include "av2/common/av2_common_int.h"
23
24
// Derivation of CCSO unit size, ccso unit shall not across tile boundaries
25
int get_ccso_unit_size_log2_adaptive_tile(const AV2_COMMON *cm,
26
                                          int sb_size_log2,
27
363k
                                          int unit_size_log2) {
28
363k
  if (cm->seq_params.ccso_unit_matches_sb_size)
29
270k
    return sb_size_log2;
30
93.2k
  else if (cm->tiles.cols == 1 && cm->tiles.rows == 1)
31
6.76k
    return unit_size_log2;
32
86.5k
  int unit_size = unit_size_log2;
33
86.5k
  if (sb_size_log2 < unit_size_log2) {
34
86.3k
    int e2 = 0, e4 = 0;
35
551k
    for (int i = 0; i < cm->tiles.cols - 1; ++i) {
36
465k
      const int size =
37
465k
          cm->tiles.col_start_sb[i + 1] - cm->tiles.col_start_sb[i];
38
465k
      e2 += size & 1;
39
465k
      e4 += size & 3;
40
465k
    }
41
99.3k
    for (int i = 0; i < cm->tiles.rows - 1; ++i) {
42
13.0k
      const int size =
43
13.0k
          cm->tiles.row_start_sb[i + 1] - cm->tiles.row_start_sb[i];
44
13.0k
      e2 += size & 1;
45
13.0k
      e4 += size & 3;
46
13.0k
    }
47
86.3k
    if (e4 == 0)
48
807
      unit_size = AVMMIN(sb_size_log2 + 2, unit_size_log2);
49
85.5k
    else if (e2 == 0)
50
1.15k
      unit_size = AVMMIN(sb_size_log2 + 1, unit_size_log2);
51
84.4k
    else
52
84.4k
      unit_size = AVMMIN(sb_size_log2, unit_size_log2);
53
86.3k
  } else {
54
147
    unit_size = sb_size_log2;
55
147
  }
56
86.5k
  return unit_size;
57
363k
}
58
59
/* Pad the border of a frame */
60
void extend_ccso_border(const YV12_BUFFER_CONFIG *frame, uint16_t *buf,
61
9
                        const int d) {
62
9
  int s = frame->y_width + (CCSO_PADDING_SIZE << 1);
63
9
  int h = frame->y_height;
64
9
  int w = frame->y_width;
65
9
  uint16_t *p = &buf[d * s + d];
66
3.66k
  for (int y = 0; y < h; y++) {
67
21.9k
    for (int x = 0; x < d; x++) {
68
18.2k
      *(p - d + x) = p[0];
69
18.2k
      p[w + x] = p[w - 1];
70
18.2k
    }
71
3.65k
    p += s;
72
3.65k
  }
73
9
  p -= (s + d);
74
54
  for (int y = 0; y < d; y++) {
75
45
    memcpy(p + (y + 1) * s, p, sizeof(uint16_t) * (w + (d << 1)));
76
45
  }
77
9
  p -= ((h - 1) * s);
78
54
  for (int y = 0; y < d; y++) {
79
45
    memcpy(p - (y + 1) * s, p, sizeof(uint16_t) * (w + (d << 1)));
80
45
  }
81
9
}
82
83
void extend_ccso_tile_border(const int tile_height, const int tile_width,
84
                             const int tile_stride, uint16_t *buf,
85
17
                             const int d) {
86
17
  uint16_t *p = &buf[d * tile_stride + d];
87
7.32k
  for (int y = 0; y < tile_height; y++) {
88
43.8k
    for (int x = 0; x < d; x++) {
89
36.5k
      *(p - d + x) = p[0];
90
36.5k
      p[tile_width + x] = p[tile_width - 1];
91
36.5k
    }
92
7.30k
    p += tile_stride;
93
7.30k
  }
94
17
  p -= (tile_stride + d);
95
102
  for (int y = 0; y < d; y++) {
96
85
    memcpy(p + (y + 1) * tile_stride, p,
97
85
           sizeof(uint16_t) * (tile_width + (d << 1)));
98
85
  }
99
17
  p -= ((tile_height - 1) * tile_stride);
100
102
  for (int y = 0; y < d; y++) {
101
85
    memcpy(p - (y + 1) * tile_stride, p,
102
85
           sizeof(uint16_t) * (tile_width + (d << 1)));
103
85
  }
104
17
}
105
106
/* Derive the quantized index, later it can be used for retriving offset values
107
 * from the look-up table */
108
void cal_filter_support(int *rec_luma_idx, const uint16_t *rec_y,
109
                        const int quant_step_size, const int inv_quant_step,
110
128
                        const int *rec_idx, const int edge_clf) {
111
128
  if (edge_clf == 0) {
112
0
    for (int i = 0; i < 2; i++) {
113
0
      int d = rec_y[rec_idx[i]] - rec_y[0];
114
0
      if (d > quant_step_size)
115
0
        rec_luma_idx[i] = 2;
116
0
      else if (d < inv_quant_step)
117
0
        rec_luma_idx[i] = 0;
118
0
      else
119
0
        rec_luma_idx[i] = 1;
120
0
    }
121
128
  } else {  // if (edge_clf == 1)
122
384
    for (int i = 0; i < 2; i++) {
123
256
      int d = rec_y[rec_idx[i]] - rec_y[0];
124
256
      if (d < inv_quant_step)
125
0
        rec_luma_idx[i] = 0;
126
256
      else
127
256
        rec_luma_idx[i] = 1;
128
256
    }
129
128
  }
130
128
}
131
132
/* Derive sample locations for CCSO */
133
void derive_ccso_sample_pos(int *rec_idx, const int ccso_stride,
134
34
                            const uint8_t ext_filter_support) {
135
  // Input sample locations for CCSO
136
  // 4 2 0 3 5
137
  // 6 1 x 1 6
138
  // 5 3 0 2 4
139
34
  assert(ext_filter_support < 7);
140
34
  if (ext_filter_support == 0) {
141
28
    rec_idx[0] = -1 * ccso_stride;
142
28
    rec_idx[1] = 1 * ccso_stride;
143
28
  } else if (ext_filter_support == 1) {
144
0
    rec_idx[0] = -1;
145
0
    rec_idx[1] = 1;
146
6
  } else if (ext_filter_support == 4) {
147
0
    rec_idx[0] = -ccso_stride - 2;
148
0
    rec_idx[1] = ccso_stride + 2;
149
6
  } else if (ext_filter_support == 5) {
150
2
    rec_idx[0] = ccso_stride - 2;
151
2
    rec_idx[1] = -ccso_stride + 2;
152
4
  } else if (ext_filter_support == 2) {
153
0
    rec_idx[0] = -1 * ccso_stride - 1;
154
0
    rec_idx[1] = 1 * ccso_stride + 1;
155
4
  } else if (ext_filter_support == 3) {
156
0
    rec_idx[0] = -1 * ccso_stride + 1;
157
0
    rec_idx[1] = 1 * ccso_stride - 1;
158
4
  } else {  // ext_filter_support == 6
159
4
    rec_idx[0] = 2;
160
4
    rec_idx[1] = -2;
161
4
  }
162
34
}
163
164
/* Derive rect area of a tile for CCSO process */
165
INLINE static AV2PixelRect av2_get_tile_rect_ccso(const TileInfo *tile_info,
166
                                                  const AV2_COMMON *cm,
167
32
                                                  MACROBLOCKD *xd, int is_uv) {
168
32
  AV2PixelRect r;
169
170
  // Calculate position in the Y plane
171
32
  r.left = tile_info->mi_col_start * MI_SIZE;
172
32
  r.right = tile_info->mi_col_end * MI_SIZE;
173
32
  r.top = tile_info->mi_row_start * MI_SIZE;
174
32
  r.bottom = tile_info->mi_row_end * MI_SIZE;
175
176
32
  const int frame_w = xd->plane[0].dst.width;
177
32
  const int frame_h = xd->plane[0].dst.height;
178
179
  // Make sure we don't fall off the bottom-right of the frame.
180
32
  r.right = AVMMIN(r.right, frame_w);
181
32
  r.bottom = AVMMIN(r.bottom, frame_h);
182
183
  // Convert to coordinates in the appropriate plane
184
32
  const int ss_x = is_uv && cm->seq_params.subsampling_x;
185
32
  const int ss_y = is_uv && cm->seq_params.subsampling_y;
186
187
32
  r.left = ROUND_POWER_OF_TWO(r.left, ss_x);
188
32
  r.right = ROUND_POWER_OF_TWO(r.right, ss_x);
189
32
  r.top = ROUND_POWER_OF_TWO(r.top, ss_y);
190
32
  r.bottom = ROUND_POWER_OF_TWO(r.bottom, ss_y);
191
192
32
  return r;
193
32
}
194
195
// Cross-component Sample Offset band offset only case.
196
void ccso_filter_block_hbd_wo_buf_bo_only_c(
197
    const uint16_t *src_y, uint16_t *dst_yuv, const int x, const int y,
198
    const int pic_width, const int pic_height, const int8_t *offset_buf,
199
    const int src_y_stride, const int dst_stride, const int y_uv_hscale,
200
    const int y_uv_vscale, const int max_val, const int blk_size_x,
201
0
    const int blk_size_y, const bool isSingleBand, const uint8_t shift_bits) {
202
0
  const int y_end = AVMMIN(pic_height - y, blk_size_y);
203
0
  const int x_end = AVMMIN(pic_width - x, blk_size_x);
204
0
  for (int y_start = 0; y_start < y_end; y_start++) {
205
0
    const int y_pos = y_start;
206
0
    for (int x_start = 0; x_start < x_end; x_start++) {
207
0
      const int x_pos = x + x_start;
208
0
      const int band_num = isSingleBand
209
0
                               ? 0
210
0
                               : src_y[(y_pos << y_uv_vscale) * src_y_stride +
211
0
                                       (x_pos << y_uv_hscale)] >>
212
0
                                     shift_bits;
213
0
      const int lut_idx_ext = (band_num << 4);
214
0
      const int offset_val = offset_buf[lut_idx_ext];
215
0
      dst_yuv[y_pos * dst_stride + x_pos] =
216
0
          clamp(offset_val + dst_yuv[y_pos * dst_stride + x_pos], 0, max_val);
217
0
    }
218
0
  }
219
0
}
220
221
void ccso_filter_block_hbd_wo_buf_c(
222
    const uint16_t *src_y, uint16_t *dst_yuv, const int x, const int y,
223
    const int pic_width, const int pic_height, int *src_cls,
224
    const int8_t *offset_buf, const int src_y_stride, const int dst_stride,
225
    const int y_uv_hscale, const int y_uv_vscale, const int thr,
226
    const int neg_thr, const int *src_loc, const int max_val,
227
    const int blk_size_x, const int blk_size_y, const bool isSingleBand,
228
34
    const uint8_t shift_bits, const int edge_clf, const uint8_t ccso_bo_only) {
229
34
  const int y_end = AVMMIN(pic_height - y, blk_size_y);
230
34
  const int x_end = AVMMIN(pic_width - x, blk_size_x);
231
898
  for (int y_start = 0; y_start < y_end; y_start++) {
232
864
    const int y_pos = y_start;
233
14.6k
    for (int x_start = 0; x_start < x_end; x_start++) {
234
13.8k
      const int x_pos = x + x_start;
235
13.8k
      if (!ccso_bo_only) {
236
0
        cal_filter_support(src_cls,
237
0
                           &src_y[(y_pos << y_uv_vscale) * src_y_stride +
238
0
                                  (x_pos << y_uv_hscale)],
239
0
                           thr, neg_thr, src_loc, edge_clf);
240
13.8k
      } else {
241
13.8k
        src_cls[0] = 0;
242
13.8k
        src_cls[1] = 0;
243
13.8k
      }
244
13.8k
      const int band_num = isSingleBand
245
13.8k
                               ? 0
246
13.8k
                               : src_y[(y_pos << y_uv_vscale) * src_y_stride +
247
0
                                       (x_pos << y_uv_hscale)] >>
248
0
                                     shift_bits;
249
13.8k
      const int lut_idx_ext = (band_num << 4) + (src_cls[0] << 2) + src_cls[1];
250
13.8k
      const int offset_val = offset_buf[lut_idx_ext];
251
13.8k
      dst_yuv[y_pos * dst_stride + x_pos] =
252
13.8k
          clamp(offset_val + dst_yuv[y_pos * dst_stride + x_pos], 0, max_val);
253
13.8k
    }
254
864
  }
255
34
}
256
257
// If there is at-least 1 segment is lossless in a frame, we have
258
// to do 4x4 processing, because minimum lossless block can be 4x4
259
// size. Although, regardless the value of
260
// cm->features.has_lossless_segment, we can always do 4x4
261
// processing, however, for software optimization purpose we have
262
// used  full block processing for whole lossy frame.
263
void ccso_filter_block_hbd_wo_buf_4x4_c(
264
    AV2_COMMON *cm, const uint16_t *src_y, uint16_t *dst_yuv,
265
    int tile_col_start, int tile_row_start, const int x, const int y,
266
    const int pic_width, const int pic_height, int *src_cls,
267
    const int8_t *offset_buf, const int src_y_stride, const int dst_stride,
268
    const int y_uv_hscale, const int y_uv_vscale, const int thr,
269
    const int neg_thr, const int *src_loc, const int max_val,
270
    const int blk_size_x, const int blk_size_y, const bool isSingleBand,
271
    const uint8_t shift_bits, const int edge_clf, const uint8_t ccso_bo_only,
272
0
    int plane) {
273
0
  const CommonModeInfoParams *const mi_params = &cm->mi_params;
274
0
  const int y_end = AVMMIN(pic_height - y, blk_size_y);
275
0
  const int x_end = AVMMIN(pic_width - x, blk_size_x);
276
0
  int min_b_size_x = (1 << MI_SIZE_LOG2) >> y_uv_hscale;
277
0
  int min_b_size_y = (1 << MI_SIZE_LOG2) >> y_uv_vscale;
278
279
0
  for (int y_start = 0; y_start < y_end; y_start += min_b_size_y) {
280
0
    const int y_pos = y_start;
281
0
    for (int x_start = 0; x_start < x_end; x_start += min_b_size_x) {
282
0
      const int x_pos = x + x_start;
283
0
      const int this_mi_row =
284
0
          ((tile_row_start + y_pos + y) << y_uv_vscale) >> MI_SIZE_LOG2;
285
0
      const int this_mi_col =
286
0
          ((tile_col_start + x_pos) << y_uv_hscale) >> MI_SIZE_LOG2;
287
288
0
      MB_MODE_INFO **this_mbmi_ptr = mi_params->mi_grid_base +
289
0
                                     this_mi_row * mi_params->mi_stride +
290
0
                                     this_mi_col;
291
0
      MB_MODE_INFO **this_mbmi =
292
0
          get_mi_location_from_collocated_mi(cm, this_mbmi_ptr, plane);
293
294
0
      const int is_lossless =
295
0
          cm->features.lossless_segment[this_mbmi[0]->segment_id];
296
0
      if (!is_lossless) {
297
0
        int j_max = AVMMIN(x_pos + min_b_size_x, x + x_start + x_end);
298
0
        int i_max = AVMMIN(y_pos + min_b_size_y, y_end);
299
0
        for (int i_pos_4x4 = y_pos; i_pos_4x4 < i_max; i_pos_4x4++) {
300
0
          for (int j_pos_4x4 = x_pos; j_pos_4x4 < j_max; j_pos_4x4++) {
301
0
            if (!ccso_bo_only) {
302
0
              cal_filter_support(
303
0
                  src_cls,
304
0
                  &src_y[(i_pos_4x4 << y_uv_vscale) * src_y_stride +
305
0
                         (j_pos_4x4 << y_uv_hscale)],
306
0
                  thr, neg_thr, src_loc, edge_clf);
307
0
            } else {
308
0
              src_cls[0] = 0;
309
0
              src_cls[1] = 0;
310
0
            }
311
0
            const int band_num =
312
0
                isSingleBand ? 0
313
0
                             : src_y[(i_pos_4x4 << y_uv_vscale) * src_y_stride +
314
0
                                     (j_pos_4x4 << y_uv_hscale)] >>
315
0
                                   shift_bits;
316
0
            const int lut_idx_ext =
317
0
                (band_num << 4) + (src_cls[0] << 2) + src_cls[1];
318
0
            const int offset_val = offset_buf[lut_idx_ext];
319
0
            dst_yuv[i_pos_4x4 * dst_stride + j_pos_4x4] =
320
0
                clamp(offset_val + dst_yuv[i_pos_4x4 * dst_stride + j_pos_4x4],
321
0
                      0, max_val);
322
0
          }
323
0
        }
324
0
      }
325
0
    }
326
0
  }
327
0
}
328
329
// Apply CCSO for each process block row
330
void av2_apply_ccso_filter_for_row(AV2_COMMON *cm, MACROBLOCKD *xd,
331
                                   const uint16_t *src_y, uint16_t *dst_yuv,
332
                                   int *src_loc, int *src_cls, int blk_row,
333
                                   int thr, int blk_size, int blk_size_proc,
334
                                   int blk_log2_x, int blk_log2_y,
335
                                   int unit_log2_x, int unit_log2_y,
336
0
                                   int plane) {
337
0
  const CommonModeInfoParams *const mi_params = &cm->mi_params;
338
0
  const int src_y_stride = xd->plane[0].dst.width + (CCSO_PADDING_SIZE << 1);
339
0
  const uint8_t max_band_log2 = cm->ccso_info.max_band_log2[plane];
340
0
  const int dst_stride = xd->plane[plane].dst.stride;
341
0
  const int pic_height = xd->plane[plane].dst.height;
342
0
  const int pic_width = xd->plane[plane].dst.width;
343
0
  const int neg_thr = thr * -1;
344
0
  const bool is_single_band = !max_band_log2;
345
0
  const uint8_t shift_bits = cm->seq_params.bit_depth - max_band_log2;
346
0
  const int max_val = (1 << cm->seq_params.bit_depth) - 1;
347
0
  const int edge_clf = cm->ccso_info.edge_clf[plane];
348
0
  const int y_uv_hscale = xd->plane[plane].subsampling_x;
349
0
  const int y_uv_vscale = xd->plane[plane].subsampling_y;
350
351
0
  for (int blk_col = 0; blk_col < pic_width; blk_col += blk_size_proc) {
352
0
    const int ccso_blk_idx =
353
0
        (blk_size >> MI_SIZE_LOG2) * (blk_row >> blk_log2_y) *
354
0
            mi_params->mi_stride +
355
0
        (blk_size >> MI_SIZE_LOG2) * (blk_col >> blk_log2_x);
356
0
    const bool use_ccso =
357
0
        (plane == 0)   ? mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_y
358
0
        : (plane == 1) ? mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_u
359
0
                       : mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_v;
360
0
    if (!use_ccso) continue;
361
362
    // FPU level skip
363
0
    const int x_mbmi = (blk_col >> unit_log2_x) << unit_log2_x;
364
0
    const int y_mbmi = (blk_row >> unit_log2_y) << unit_log2_y;
365
0
    const int mbmi_idx =
366
0
        get_mi_grid_idx(mi_params, y_mbmi >> (MI_SIZE_LOG2 - y_uv_vscale),
367
0
                        x_mbmi >> (MI_SIZE_LOG2 - y_uv_hscale));
368
0
    const int use_ccso_local =
369
0
        mi_params->mi_grid_base[mbmi_idx]->local_ccso_blk_flag;
370
0
    if (!use_ccso_local) continue;
371
372
0
    if (cm->bru.enabled &&
373
0
        mi_params->mi_grid_base[mbmi_idx]->sb_active_mode != BRU_ACTIVE_SB) {
374
0
      avm_internal_error(
375
0
          &cm->error, AVM_CODEC_ERROR,
376
0
          "Invalid BRU activity in CCSO: only active SB can be filtered");
377
0
      return;
378
0
    }
379
0
    if (cm->bridge_frame_info.is_bridge_frame) {
380
0
      avm_internal_error(
381
0
          &cm->error, AVM_CODEC_ERROR,
382
0
          "Invalid Bridge frame activity in CCSO: can not be filtered");
383
0
      return;
384
0
    }
385
386
0
    if (cm->features.has_lossless_segment) {
387
0
      ccso_filter_block_hbd_wo_buf_4x4_c(
388
0
          cm, src_y, dst_yuv, 0, 0, blk_col, blk_row, pic_width, pic_height,
389
0
          src_cls, cm->ccso_info.filter_offset[plane], src_y_stride, dst_stride,
390
0
          y_uv_hscale, y_uv_vscale, thr, neg_thr, src_loc, max_val,
391
0
          blk_size_proc, blk_size_proc, is_single_band, shift_bits, edge_clf,
392
0
          cm->ccso_info.ccso_bo_only[plane], plane);
393
0
    } else {
394
0
      if (cm->ccso_info.ccso_bo_only[plane]) {
395
0
        ccso_filter_block_hbd_wo_buf_bo_only(
396
0
            src_y, dst_yuv, blk_col, blk_row, pic_width, pic_height,
397
0
            cm->ccso_info.filter_offset[plane], src_y_stride, dst_stride,
398
0
            y_uv_hscale, y_uv_vscale, max_val, blk_size_proc, blk_size_proc,
399
0
            is_single_band, shift_bits);
400
0
      } else {
401
0
        ccso_filter_block_hbd_wo_buf(
402
0
            src_y, dst_yuv, blk_col, blk_row, pic_width, pic_height, src_cls,
403
0
            cm->ccso_info.filter_offset[plane], src_y_stride, dst_stride,
404
0
            y_uv_hscale, y_uv_vscale, thr, neg_thr, src_loc, max_val,
405
0
            blk_size_proc, blk_size_proc, is_single_band, shift_bits, edge_clf,
406
0
            0);
407
0
      }
408
0
    }
409
0
  }
410
0
}
411
412
/* Apply CCSO on luma or chroma component when single or multiple bands are
413
 * applied */
414
void apply_ccso_filter(AV2_COMMON *cm, MACROBLOCKD *xd, int plane,
415
                       const uint16_t *src_y, uint16_t *dst_yuv, int dst_stride,
416
                       int proc_unit_log2, uint16_t thr, uint8_t filter_sup,
417
17
                       uint8_t max_band_log2, int edge_clf) {
418
17
  const int ccso_ext_stride = xd->plane[0].dst.width + (CCSO_PADDING_SIZE << 1);
419
17
  const int pic_height = xd->plane[plane].dst.height;
420
17
  int src_cls[2];
421
17
  int src_loc[2];
422
17
  const int y_uv_hscale = xd->plane[plane].subsampling_x;
423
17
  const int y_uv_vscale = xd->plane[plane].subsampling_y;
424
17
  derive_ccso_sample_pos(src_loc, ccso_ext_stride, filter_sup);
425
17
  const int ccso_blk_size = get_ccso_unit_size_log2_adaptive_tile(
426
17
      cm, cm->mib_size_log2 + MI_SIZE_LOG2, CCSO_BLK_SIZE);
427
17
  const int blk_log2 = ccso_blk_size;
428
17
  const int blk_size = 1 << blk_log2;
429
17
  const int blk_log2_x = blk_log2 - y_uv_hscale;
430
17
  const int blk_log2_y = blk_log2 - y_uv_vscale;
431
17
  src_y += CCSO_PADDING_SIZE * ccso_ext_stride + CCSO_PADDING_SIZE;
432
17
  const int unit_log2_x = AVMMIN(proc_unit_log2, blk_log2_x);
433
17
  const int unit_log2_y = AVMMIN(proc_unit_log2, blk_log2_y);
434
17
  const CommonModeInfoParams *const mi_params = &cm->mi_params;
435
17
  const uint8_t shift_bits = cm->seq_params.bit_depth - max_band_log2;
436
17
  const bool is_single_band = !max_band_log2;
437
17
  const int max_val = (1 << cm->seq_params.bit_depth) - 1;
438
17
  const int neg_thr = thr * -1;
439
17
  const int unit_size_x = 1 << unit_log2_x;
440
17
  const int unit_size_y = 1 << unit_log2_y;
441
17
  const int blk_size_x = 1 << blk_log2_x;
442
17
  const int blk_size_y = 1 << blk_log2_y;
443
17
  if (cm->seq_params.disable_loopfilters_across_tiles) {
444
17
    int tile_rows = cm->tiles.rows;
445
17
    int tile_cols = cm->tiles.cols;
446
17
    TileInfo tile_info_y;
447
17
    AV2PixelRect tile_rect_y;
448
34
    for (int tile_row = 0; tile_row < tile_rows; ++tile_row) {
449
17
      int tile_height = 0;
450
34
      for (int tile_col = 0; tile_col < tile_cols; ++tile_col) {
451
17
        av2_tile_init(&tile_info_y, cm, tile_row, tile_col);
452
17
        tile_rect_y = av2_get_tile_rect_ccso(&tile_info_y, cm, xd, 0);
453
454
17
        int tile_row_start = tile_rect_y.top;
455
17
        int tile_col_start = tile_rect_y.left;
456
17
        int tile_row_end = tile_rect_y.bottom;
457
17
        int tile_col_end = tile_rect_y.right;
458
17
        int tile_width = tile_col_end - tile_col_start;
459
17
        tile_height = tile_row_end - tile_row_start;
460
17
        const int ccso_ext_tile_stride = tile_width + (CCSO_PADDING_SIZE << 1);
461
17
        derive_ccso_sample_pos(src_loc, ccso_ext_tile_stride, filter_sup);
462
463
17
        uint16_t *ext_rec_tile_y = NULL;
464
        // const uint16_t *rec_y = cm->cur_frame->buf.y_buffer;
465
17
        ext_rec_tile_y = avm_malloc(sizeof(*ext_rec_tile_y) *
466
17
                                    (tile_height + (CCSO_PADDING_SIZE << 1)) *
467
17
                                    (tile_width + (CCSO_PADDING_SIZE << 1)));
468
7.32k
        for (int r = 0; r < tile_height; ++r) {
469
240k
          for (int c = 0; c < tile_width; ++c) {
470
233k
            ext_rec_tile_y[(r + CCSO_PADDING_SIZE) * ccso_ext_tile_stride + c +
471
233k
                           CCSO_PADDING_SIZE] =
472
233k
                src_y[(r + tile_row_start) * ccso_ext_stride +
473
233k
                      (c + tile_col_start)];
474
233k
          }
475
7.30k
        }
476
17
        extend_ccso_tile_border(tile_height, tile_width, ccso_ext_tile_stride,
477
17
                                ext_rec_tile_y, CCSO_PADDING_SIZE);
478
479
17
        uint16_t *src_tile_y = ext_rec_tile_y;
480
17
        src_tile_y +=
481
17
            CCSO_PADDING_SIZE * ccso_ext_tile_stride + CCSO_PADDING_SIZE;
482
17
        if (plane != 0) {
483
15
          TileInfo tile_info_uv;
484
15
          AV2PixelRect tile_rect_uv;
485
15
          av2_tile_init(&tile_info_uv, cm, tile_row, tile_col);
486
15
          tile_rect_uv = av2_get_tile_rect_ccso(&tile_info_uv, cm, xd, 1);
487
15
          tile_row_start = tile_rect_uv.top;
488
15
          tile_col_start = tile_rect_uv.left;
489
15
          tile_row_end = tile_rect_uv.bottom;
490
15
          tile_col_end = tile_rect_uv.right;
491
15
          tile_width = tile_col_end - tile_col_start;
492
15
          tile_height = tile_row_end - tile_row_start;
493
15
        }
494
17
        uint16_t *dst_tile_yuv = dst_yuv + tile_col_start;
495
146
        for (int frame_pxl_y = tile_row_start; frame_pxl_y < tile_row_end;
496
129
             frame_pxl_y += blk_size_y) {
497
258
          for (int frame_pxl_x = tile_col_start; frame_pxl_x < tile_col_end;
498
129
               frame_pxl_x += blk_size_x) {
499
            // int x = frame_pxl_x;
500
            // int y = frame_pxl_y;
501
129
            int tile_pxl_x = frame_pxl_x - tile_col_start;
502
129
            int tile_pxl_y = frame_pxl_y - tile_row_start;
503
504
129
            const int ccso_blk_idx =
505
129
                (blk_size >> MI_SIZE_LOG2) * (frame_pxl_y >> blk_log2_y) *
506
129
                    mi_params->mi_stride +
507
129
                (blk_size >> MI_SIZE_LOG2) * (frame_pxl_x >> blk_log2_x);
508
129
            const bool use_ccso =
509
129
                (plane == 0) ? mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_y
510
129
                : (plane == 1)
511
127
                    ? mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_u
512
127
                    : mi_params->mi_grid_base[ccso_blk_idx]->ccso_blk_v;
513
129
            if (!use_ccso) continue;
514
62
            const uint16_t *src_unit_y = src_tile_y;
515
62
            uint16_t *dst_unit_yuv = dst_tile_yuv;
516
62
            const int y_end = AVMMIN(tile_row_end - frame_pxl_y, blk_size_y);
517
62
            const int x_end = AVMMIN(tile_col_end - frame_pxl_x, blk_size_x);
518
124
            for (int unit_y = 0; unit_y < y_end; unit_y += unit_size_y) {
519
124
              for (int unit_x = 0; unit_x < x_end; unit_x += unit_size_x) {
520
                // FPU level skip
521
62
                const int mbmi_idx = get_mi_grid_idx(
522
62
                    mi_params,
523
62
                    (frame_pxl_y + unit_y) >> (MI_SIZE_LOG2 - y_uv_vscale),
524
62
                    (frame_pxl_x + unit_x) >> (MI_SIZE_LOG2 - y_uv_hscale));
525
62
                const int use_ccso_local =
526
62
                    mi_params->mi_grid_base[mbmi_idx]->local_ccso_blk_flag;
527
62
                if (!use_ccso_local) {
528
0
                  continue;
529
0
                }
530
62
                if (cm->bru.enabled &&
531
0
                    mi_params->mi_grid_base[mbmi_idx]->sb_active_mode !=
532
0
                        BRU_ACTIVE_SB) {
533
0
                  avm_internal_error(&cm->error, AVM_CODEC_ERROR,
534
0
                                     "Invalid BRU activity in CCSO: only "
535
0
                                     "active SB can be filtered");
536
0
                  return;
537
0
                }
538
62
                if (cm->features.has_lossless_segment) {
539
0
                  ccso_filter_block_hbd_wo_buf_4x4_c(
540
0
                      cm, src_unit_y, dst_unit_yuv, tile_col_start,
541
0
                      tile_row_start, tile_pxl_x + unit_x, tile_pxl_y + unit_y,
542
0
                      tile_width, tile_height, src_cls,
543
0
                      cm->ccso_info.filter_offset[plane], ccso_ext_tile_stride,
544
0
                      dst_stride, y_uv_hscale, y_uv_vscale, thr, neg_thr,
545
0
                      src_loc, max_val, unit_size_x, unit_size_y,
546
0
                      is_single_band, shift_bits, edge_clf,
547
0
                      cm->ccso_info.ccso_bo_only[plane], plane);
548
62
                } else {
549
62
                  if (cm->ccso_info.ccso_bo_only[plane]) {
550
34
                    ccso_filter_block_hbd_wo_buf_c(
551
34
                        src_unit_y, dst_unit_yuv, tile_pxl_x + unit_x,
552
34
                        tile_pxl_y + unit_y, tile_width, tile_height, src_cls,
553
34
                        cm->ccso_info.filter_offset[plane],
554
34
                        ccso_ext_tile_stride, dst_stride, y_uv_hscale,
555
34
                        y_uv_vscale, thr, neg_thr, src_loc, max_val,
556
34
                        unit_size_x, unit_size_y, is_single_band, shift_bits,
557
34
                        edge_clf, cm->ccso_info.ccso_bo_only[plane]);
558
34
                  } else {
559
28
                    ccso_filter_block_hbd_wo_buf(
560
28
                        src_unit_y, dst_unit_yuv, tile_pxl_x + unit_x,
561
28
                        tile_pxl_y + unit_y, tile_width, tile_height, src_cls,
562
28
                        cm->ccso_info.filter_offset[plane],
563
28
                        ccso_ext_tile_stride, dst_stride, y_uv_hscale,
564
28
                        y_uv_vscale, thr, neg_thr, src_loc, max_val,
565
28
                        unit_size_x, unit_size_y, is_single_band, shift_bits,
566
28
                        edge_clf, 0);
567
28
                  }
568
62
                }
569
62
              }
570
62
              dst_unit_yuv += (dst_stride << unit_log2_y);
571
62
              src_unit_y +=
572
62
                  (ccso_ext_tile_stride << (unit_log2_y + y_uv_vscale));
573
62
            }
574
62
          }
575
129
          dst_tile_yuv += (dst_stride << blk_log2_y);
576
129
          src_tile_y += (ccso_ext_tile_stride << (blk_log2_y + y_uv_vscale));
577
129
        }
578
17
        avm_free(ext_rec_tile_y);
579
17
      }
580
17
      dst_yuv += (dst_stride * tile_height);
581
      // src_y += (ccso_ext_stride * (cm->tiles.height << (MI_SIZE_LOG2)));
582
17
    }
583
17
    return;
584
17
  }
585
0
  const int blk_log2_proc = CCSO_PROC_BLK_LOG2;
586
0
  const int blk_size_proc = 1 << blk_log2_proc;
587
0
  for (int blk_row = 0; blk_row < pic_height; blk_row += blk_size_proc) {
588
0
    av2_apply_ccso_filter_for_row(
589
0
        cm, xd, src_y, dst_yuv, src_loc, src_cls, blk_row, thr, blk_size,
590
0
        blk_size_proc, blk_log2_x, blk_log2_y, unit_log2_x, unit_log2_y, plane);
591
0
    dst_yuv += dst_stride << blk_log2_proc;
592
0
    src_y += ccso_ext_stride << (blk_log2_proc + y_uv_vscale);
593
0
  }
594
0
}
595
596
/* Apply CCSO for one frame */
597
void ccso_frame(YV12_BUFFER_CONFIG *frame, AV2_COMMON *cm, MACROBLOCKD *xd,
598
9
                uint16_t *ext_rec_y) {
599
9
  const int num_planes = av2_num_planes(cm);
600
9
  av2_setup_dst_planes(xd->plane, frame, 0, 0, 0, num_planes, NULL);
601
602
36
  for (int plane = 0; plane < num_planes; plane++) {
603
27
    const int dst_stride = xd->plane[plane].dst.stride;
604
27
    const uint16_t quant_step_size = quant_sz[cm->ccso_info.scale_idx[plane]]
605
27
                                             [cm->ccso_info.quant_idx[plane]];
606
27
    if (cm->ccso_info.ccso_enable[plane]) {
607
17
      apply_ccso_filter(
608
17
          cm, xd, plane, ext_rec_y, &(xd->plane[plane].dst.buf)[0], dst_stride,
609
17
          cm->mib_size_log2 -
610
17
              AVMMAX(xd->plane[plane].subsampling_x,
611
17
                     xd->plane[plane].subsampling_y) +
612
17
              MI_SIZE_LOG2,
613
17
          quant_step_size, cm->ccso_info.ext_filter_support[plane],
614
17
          cm->ccso_info.max_band_log2[plane], cm->ccso_info.edge_clf[plane]);
615
17
    }
616
27
  }
617
9
}
618
619
// This function is to copy ccso filter parameters between frames when
620
// ccso_reuse is true.
621
void av2_copy_ccso_filters(CcsoInfo *to, CcsoInfo *from, int plane,
622
2.05k
                           bool frame_level, bool block_level, int sb_count) {
623
2.05k
  if (frame_level) {
624
2.05k
    memcpy(to->filter_offset[plane], from->filter_offset[plane],
625
2.05k
           sizeof(to->filter_offset[plane]));
626
2.05k
    to->quant_idx[plane] = from->quant_idx[plane];
627
2.05k
    to->ext_filter_support[plane] = from->ext_filter_support[plane];
628
2.05k
    to->edge_clf[plane] = from->edge_clf[plane];
629
2.05k
    to->ccso_bo_only[plane] = from->ccso_bo_only[plane];
630
2.05k
    to->max_band_log2[plane] = from->max_band_log2[plane];
631
2.05k
    to->scale_idx[plane] = from->scale_idx[plane];
632
2.05k
  }
633
634
2.05k
  if (block_level) {
635
0
    if (to->sb_filter_control[plane]) {
636
0
      memcpy(to->sb_filter_control[plane], from->sb_filter_control[plane],
637
0
             sizeof(*from->sb_filter_control[plane]) * sb_count);
638
0
    }
639
0
  }
640
641
2.05k
  to->ccso_enable[plane] = from->ccso_enable[plane];
642
2.05k
}