• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include <atomic>
15 
16 #include "src/post_filter.h"
17 
18 namespace libgav1 {
19 namespace {
20 
HevThresh(int level)21 constexpr uint8_t HevThresh(int level) { return DivideBy16(level); }
22 
23 // GetLoopFilterSize* functions depend on this exact ordering of the
24 // LoopFilterSize enums.
25 static_assert(dsp::kLoopFilterSize4 == 0, "");
26 static_assert(dsp::kLoopFilterSize6 == 1, "");
27 static_assert(dsp::kLoopFilterSize8 == 2, "");
28 static_assert(dsp::kLoopFilterSize14 == 3, "");
29 
GetLoopFilterSizeY(int filter_length)30 dsp::LoopFilterSize GetLoopFilterSizeY(int filter_length) {
31   // |filter_length| must be a power of 2.
32   assert((filter_length & (filter_length - 1)) == 0);
33   // This code is the branch free equivalent of:
34   //   if (filter_length == 4) return kLoopFilterSize4;
35   //   if (filter_length == 8) return kLoopFilterSize8;
36   //   return kLoopFilterSize14;
37   return static_cast<dsp::LoopFilterSize>(
38       MultiplyBy2(static_cast<int>(filter_length > 4)) +
39       static_cast<int>(filter_length > 8));
40 }
41 
GetLoopFilterSizeUV(int filter_length)42 constexpr dsp::LoopFilterSize GetLoopFilterSizeUV(int filter_length) {
43   // For U & V planes, size is kLoopFilterSize4 if |filter_length| is 4,
44   // otherwise size is kLoopFilterSize6.
45   return static_cast<dsp::LoopFilterSize>(filter_length != 4);
46 }
47 
NonBlockBorderNeedsFilter(const BlockParameters & bp,int filter_id,uint8_t * const level)48 bool NonBlockBorderNeedsFilter(const BlockParameters& bp, int filter_id,
49                                uint8_t* const level) {
50   if (bp.deblock_filter_level[filter_id] == 0 || (bp.skip && bp.is_inter)) {
51     return false;
52   }
53   *level = bp.deblock_filter_level[filter_id];
54   return true;
55 }
56 
57 // 7.14.5.
ComputeDeblockFilterLevelsHelper(const ObuFrameHeader & frame_header,int segment_id,int level_index,const int8_t delta_lf[kFrameLfCount],uint8_t deblock_filter_levels[kNumReferenceFrameTypes][2])58 void ComputeDeblockFilterLevelsHelper(
59     const ObuFrameHeader& frame_header, int segment_id, int level_index,
60     const int8_t delta_lf[kFrameLfCount],
61     uint8_t deblock_filter_levels[kNumReferenceFrameTypes][2]) {
62   const int delta = delta_lf[frame_header.delta_lf.multi ? level_index : 0];
63   uint8_t level = Clip3(frame_header.loop_filter.level[level_index] + delta, 0,
64                         kMaxLoopFilterValue);
65   const auto feature = static_cast<SegmentFeature>(
66       kSegmentFeatureLoopFilterYVertical + level_index);
67   level =
68       Clip3(level + frame_header.segmentation.feature_data[segment_id][feature],
69             0, kMaxLoopFilterValue);
70   if (!frame_header.loop_filter.delta_enabled) {
71     static_assert(sizeof(deblock_filter_levels[0][0]) == 1, "");
72     memset(deblock_filter_levels, level, kNumReferenceFrameTypes * 2);
73     return;
74   }
75   assert(frame_header.loop_filter.delta_enabled);
76   const int shift = level >> 5;
77   deblock_filter_levels[kReferenceFrameIntra][0] = Clip3(
78       level +
79           LeftShift(frame_header.loop_filter.ref_deltas[kReferenceFrameIntra],
80                     shift),
81       0, kMaxLoopFilterValue);
82   // deblock_filter_levels[kReferenceFrameIntra][1] is never used. So it does
83   // not have to be populated.
84   for (int reference_frame = kReferenceFrameIntra + 1;
85        reference_frame < kNumReferenceFrameTypes; ++reference_frame) {
86     for (int mode_id = 0; mode_id < 2; ++mode_id) {
87       deblock_filter_levels[reference_frame][mode_id] = Clip3(
88           level +
89               LeftShift(frame_header.loop_filter.ref_deltas[reference_frame] +
90                             frame_header.loop_filter.mode_deltas[mode_id],
91                         shift),
92           0, kMaxLoopFilterValue);
93     }
94   }
95 }
96 
97 }  // namespace
98 
ComputeDeblockFilterLevels(const int8_t delta_lf[kFrameLfCount],uint8_t deblock_filter_levels[kMaxSegments][kFrameLfCount][kNumReferenceFrameTypes][2]) const99 void PostFilter::ComputeDeblockFilterLevels(
100     const int8_t delta_lf[kFrameLfCount],
101     uint8_t deblock_filter_levels[kMaxSegments][kFrameLfCount]
102                                  [kNumReferenceFrameTypes][2]) const {
103   if (!DoDeblock()) return;
104   for (int segment_id = 0;
105        segment_id < (frame_header_.segmentation.enabled ? kMaxSegments : 1);
106        ++segment_id) {
107     int level_index = 0;
108     for (; level_index < 2; ++level_index) {
109       ComputeDeblockFilterLevelsHelper(
110           frame_header_, segment_id, level_index, delta_lf,
111           deblock_filter_levels[segment_id][level_index]);
112     }
113     for (; level_index < kFrameLfCount; ++level_index) {
114       if (frame_header_.loop_filter.level[level_index] != 0) {
115         ComputeDeblockFilterLevelsHelper(
116             frame_header_, segment_id, level_index, delta_lf,
117             deblock_filter_levels[segment_id][level_index]);
118       }
119     }
120   }
121 }
122 
GetHorizontalDeblockFilterEdgeInfo(int row4x4,int column4x4,uint8_t * level,int * step,int * filter_length) const123 bool PostFilter::GetHorizontalDeblockFilterEdgeInfo(int row4x4, int column4x4,
124                                                     uint8_t* level, int* step,
125                                                     int* filter_length) const {
126   *step = kTransformHeight[inter_transform_sizes_[row4x4][column4x4]];
127   if (row4x4 == 0) return false;
128 
129   const BlockParameters* bp = block_parameters_.Find(row4x4, column4x4);
130   const int row4x4_prev = row4x4 - 1;
131   assert(row4x4_prev >= 0);
132   const BlockParameters* bp_prev =
133       block_parameters_.Find(row4x4_prev, column4x4);
134 
135   if (bp == bp_prev) {
136     // Not a border.
137     if (!NonBlockBorderNeedsFilter(*bp, 1, level)) return false;
138   } else {
139     const uint8_t level_this = bp->deblock_filter_level[1];
140     *level = level_this;
141     if (level_this == 0) {
142       const uint8_t level_prev = bp_prev->deblock_filter_level[1];
143       if (level_prev == 0) return false;
144       *level = level_prev;
145     }
146   }
147   const int step_prev =
148       kTransformHeight[inter_transform_sizes_[row4x4_prev][column4x4]];
149   *filter_length = std::min(*step, step_prev);
150   return true;
151 }
152 
GetHorizontalDeblockFilterEdgeInfoUV(int row4x4,int column4x4,uint8_t * level_u,uint8_t * level_v,int * step,int * filter_length) const153 void PostFilter::GetHorizontalDeblockFilterEdgeInfoUV(
154     int row4x4, int column4x4, uint8_t* level_u, uint8_t* level_v, int* step,
155     int* filter_length) const {
156   const int subsampling_x = subsampling_x_[kPlaneU];
157   const int subsampling_y = subsampling_y_[kPlaneU];
158   row4x4 = GetDeblockPosition(row4x4, subsampling_y);
159   column4x4 = GetDeblockPosition(column4x4, subsampling_x);
160   const BlockParameters* bp = block_parameters_.Find(row4x4, column4x4);
161   *level_u = 0;
162   *level_v = 0;
163   *step = kTransformHeight[bp->uv_transform_size];
164   if (row4x4 == subsampling_y) {
165     return;
166   }
167 
168   bool need_filter_u = frame_header_.loop_filter.level[kPlaneU + 1] != 0;
169   bool need_filter_v = frame_header_.loop_filter.level[kPlaneV + 1] != 0;
170   assert(need_filter_u || need_filter_v);
171   const int filter_id_u =
172       kDeblockFilterLevelIndex[kPlaneU][kLoopFilterTypeHorizontal];
173   const int filter_id_v =
174       kDeblockFilterLevelIndex[kPlaneV][kLoopFilterTypeHorizontal];
175   const int row4x4_prev = row4x4 - (1 << subsampling_y);
176   assert(row4x4_prev >= 0);
177   const BlockParameters* bp_prev =
178       block_parameters_.Find(row4x4_prev, column4x4);
179 
180   if (bp == bp_prev) {
181     // Not a border.
182     const bool skip = bp->skip && bp->is_inter;
183     need_filter_u =
184         need_filter_u && bp->deblock_filter_level[filter_id_u] != 0 && !skip;
185     need_filter_v =
186         need_filter_v && bp->deblock_filter_level[filter_id_v] != 0 && !skip;
187     if (!need_filter_u && !need_filter_v) return;
188     if (need_filter_u) *level_u = bp->deblock_filter_level[filter_id_u];
189     if (need_filter_v) *level_v = bp->deblock_filter_level[filter_id_v];
190     *filter_length = *step;
191     return;
192   }
193 
194   // It is a border.
195   if (need_filter_u) {
196     const uint8_t level_u_this = bp->deblock_filter_level[filter_id_u];
197     *level_u = level_u_this;
198     if (level_u_this == 0) {
199       *level_u = bp_prev->deblock_filter_level[filter_id_u];
200     }
201   }
202   if (need_filter_v) {
203     const uint8_t level_v_this = bp->deblock_filter_level[filter_id_v];
204     *level_v = level_v_this;
205     if (level_v_this == 0) {
206       *level_v = bp_prev->deblock_filter_level[filter_id_v];
207     }
208   }
209   const int step_prev = kTransformHeight[bp_prev->uv_transform_size];
210   *filter_length = std::min(*step, step_prev);
211 }
212 
GetVerticalDeblockFilterEdgeInfo(int row4x4,int column4x4,BlockParameters * const * bp_ptr,uint8_t * level,int * step,int * filter_length) const213 bool PostFilter::GetVerticalDeblockFilterEdgeInfo(
214     int row4x4, int column4x4, BlockParameters* const* bp_ptr, uint8_t* level,
215     int* step, int* filter_length) const {
216   const BlockParameters* bp = *bp_ptr;
217   *step = kTransformWidth[inter_transform_sizes_[row4x4][column4x4]];
218   if (column4x4 == 0) return false;
219 
220   const int filter_id = 0;
221   const int column4x4_prev = column4x4 - 1;
222   assert(column4x4_prev >= 0);
223   const BlockParameters* bp_prev = *(bp_ptr - 1);
224   if (bp == bp_prev) {
225     // Not a border.
226     if (!NonBlockBorderNeedsFilter(*bp, filter_id, level)) return false;
227   } else {
228     // It is a border.
229     const uint8_t level_this = bp->deblock_filter_level[filter_id];
230     *level = level_this;
231     if (level_this == 0) {
232       const uint8_t level_prev = bp_prev->deblock_filter_level[filter_id];
233       if (level_prev == 0) return false;
234       *level = level_prev;
235     }
236   }
237   const int step_prev =
238       kTransformWidth[inter_transform_sizes_[row4x4][column4x4_prev]];
239   *filter_length = std::min(*step, step_prev);
240   return true;
241 }
242 
GetVerticalDeblockFilterEdgeInfoUV(int column4x4,BlockParameters * const * bp_ptr,uint8_t * level_u,uint8_t * level_v,int * step,int * filter_length) const243 void PostFilter::GetVerticalDeblockFilterEdgeInfoUV(
244     int column4x4, BlockParameters* const* bp_ptr, uint8_t* level_u,
245     uint8_t* level_v, int* step, int* filter_length) const {
246   const int subsampling_x = subsampling_x_[kPlaneU];
247   column4x4 = GetDeblockPosition(column4x4, subsampling_x);
248   const BlockParameters* bp = *bp_ptr;
249   *level_u = 0;
250   *level_v = 0;
251   *step = kTransformWidth[bp->uv_transform_size];
252   if (column4x4 == subsampling_x) {
253     return;
254   }
255 
256   bool need_filter_u = frame_header_.loop_filter.level[kPlaneU + 1] != 0;
257   bool need_filter_v = frame_header_.loop_filter.level[kPlaneV + 1] != 0;
258   assert(need_filter_u || need_filter_v);
259   const int filter_id_u =
260       kDeblockFilterLevelIndex[kPlaneU][kLoopFilterTypeVertical];
261   const int filter_id_v =
262       kDeblockFilterLevelIndex[kPlaneV][kLoopFilterTypeVertical];
263   const BlockParameters* bp_prev = *(bp_ptr - (ptrdiff_t{1} << subsampling_x));
264 
265   if (bp == bp_prev) {
266     // Not a border.
267     const bool skip = bp->skip && bp->is_inter;
268     need_filter_u =
269         need_filter_u && bp->deblock_filter_level[filter_id_u] != 0 && !skip;
270     need_filter_v =
271         need_filter_v && bp->deblock_filter_level[filter_id_v] != 0 && !skip;
272     if (!need_filter_u && !need_filter_v) return;
273     if (need_filter_u) *level_u = bp->deblock_filter_level[filter_id_u];
274     if (need_filter_v) *level_v = bp->deblock_filter_level[filter_id_v];
275     *filter_length = *step;
276     return;
277   }
278 
279   // It is a border.
280   if (need_filter_u) {
281     const uint8_t level_u_this = bp->deblock_filter_level[filter_id_u];
282     *level_u = level_u_this;
283     if (level_u_this == 0) {
284       *level_u = bp_prev->deblock_filter_level[filter_id_u];
285     }
286   }
287   if (need_filter_v) {
288     const uint8_t level_v_this = bp->deblock_filter_level[filter_id_v];
289     *level_v = level_v_this;
290     if (level_v_this == 0) {
291       *level_v = bp_prev->deblock_filter_level[filter_id_v];
292     }
293   }
294   const int step_prev = kTransformWidth[bp_prev->uv_transform_size];
295   *filter_length = std::min(*step, step_prev);
296 }
297 
HorizontalDeblockFilter(int row4x4_start,int column4x4_start)298 void PostFilter::HorizontalDeblockFilter(int row4x4_start,
299                                          int column4x4_start) {
300   const int column_step = 1;
301   const int src_step = 4 << pixel_size_log2_;
302   const ptrdiff_t src_stride = frame_buffer_.stride(kPlaneY);
303   uint8_t* src = GetSourceBuffer(kPlaneY, row4x4_start, column4x4_start);
304   int row_step;
305   uint8_t level;
306   int filter_length;
307 
308   for (int column4x4 = 0; column4x4 < kNum4x4InLoopFilterUnit &&
309                           MultiplyBy4(column4x4_start + column4x4) < width_;
310        column4x4 += column_step, src += src_step) {
311     uint8_t* src_row = src;
312     for (int row4x4 = 0; row4x4 < kNum4x4InLoopFilterUnit &&
313                          MultiplyBy4(row4x4_start + row4x4) < height_;
314          row4x4 += row_step) {
315       const bool need_filter = GetHorizontalDeblockFilterEdgeInfo(
316           row4x4_start + row4x4, column4x4_start + column4x4, &level, &row_step,
317           &filter_length);
318       if (need_filter) {
319         const dsp::LoopFilterSize size = GetLoopFilterSizeY(filter_length);
320         dsp_.loop_filters[size][kLoopFilterTypeHorizontal](
321             src_row, src_stride, outer_thresh_[level], inner_thresh_[level],
322             HevThresh(level));
323       }
324       // TODO(chengchen): use shifts instead of multiplication.
325       src_row += row_step * src_stride;
326       row_step = DivideBy4(row_step);
327     }
328   }
329 
330   if (needs_chroma_deblock_) {
331     const int8_t subsampling_x = subsampling_x_[kPlaneU];
332     const int8_t subsampling_y = subsampling_y_[kPlaneU];
333     const int column_step = 1 << subsampling_x;
334     const ptrdiff_t src_stride_u = frame_buffer_.stride(kPlaneU);
335     const ptrdiff_t src_stride_v = frame_buffer_.stride(kPlaneV);
336     uint8_t* src_u = GetSourceBuffer(kPlaneU, row4x4_start, column4x4_start);
337     uint8_t* src_v = GetSourceBuffer(kPlaneV, row4x4_start, column4x4_start);
338     int row_step;
339     uint8_t level_u;
340     uint8_t level_v;
341     int filter_length;
342 
343     for (int column4x4 = 0; column4x4 < kNum4x4InLoopFilterUnit &&
344                             MultiplyBy4(column4x4_start + column4x4) < width_;
345          column4x4 += column_step, src_u += src_step, src_v += src_step) {
346       uint8_t* src_row_u = src_u;
347       uint8_t* src_row_v = src_v;
348       for (int row4x4 = 0; row4x4 < kNum4x4InLoopFilterUnit &&
349                            MultiplyBy4(row4x4_start + row4x4) < height_;
350            row4x4 += row_step) {
351         GetHorizontalDeblockFilterEdgeInfoUV(
352             row4x4_start + row4x4, column4x4_start + column4x4, &level_u,
353             &level_v, &row_step, &filter_length);
354         if (level_u != 0) {
355           const dsp::LoopFilterSize size = GetLoopFilterSizeUV(filter_length);
356           dsp_.loop_filters[size][kLoopFilterTypeHorizontal](
357               src_row_u, src_stride_u, outer_thresh_[level_u],
358               inner_thresh_[level_u], HevThresh(level_u));
359         }
360         if (level_v != 0) {
361           const dsp::LoopFilterSize size = GetLoopFilterSizeUV(filter_length);
362           dsp_.loop_filters[size][kLoopFilterTypeHorizontal](
363               src_row_v, src_stride_v, outer_thresh_[level_v],
364               inner_thresh_[level_v], HevThresh(level_v));
365         }
366         src_row_u += row_step * src_stride_u;
367         src_row_v += row_step * src_stride_v;
368         row_step = DivideBy4(row_step << subsampling_y);
369       }
370     }
371   }
372 }
373 
VerticalDeblockFilter(int row4x4_start,int column4x4_start)374 void PostFilter::VerticalDeblockFilter(int row4x4_start, int column4x4_start) {
375   const ptrdiff_t row_stride = MultiplyBy4(frame_buffer_.stride(kPlaneY));
376   const ptrdiff_t src_stride = frame_buffer_.stride(kPlaneY);
377   uint8_t* src = GetSourceBuffer(kPlaneY, row4x4_start, column4x4_start);
378   int column_step;
379   uint8_t level;
380   int filter_length;
381 
382   BlockParameters* const* bp_row_base =
383       block_parameters_.Address(row4x4_start, column4x4_start);
384   const int bp_stride = block_parameters_.columns4x4();
385   const int column_step_shift = pixel_size_log2_;
386   for (int row4x4 = 0; row4x4 < kNum4x4InLoopFilterUnit &&
387                        MultiplyBy4(row4x4_start + row4x4) < height_;
388        ++row4x4, src += row_stride, bp_row_base += bp_stride) {
389     uint8_t* src_row = src;
390     BlockParameters* const* bp = bp_row_base;
391     for (int column4x4 = 0; column4x4 < kNum4x4InLoopFilterUnit &&
392                             MultiplyBy4(column4x4_start + column4x4) < width_;
393          column4x4 += column_step, bp += column_step) {
394       const bool need_filter = GetVerticalDeblockFilterEdgeInfo(
395           row4x4_start + row4x4, column4x4_start + column4x4, bp, &level,
396           &column_step, &filter_length);
397       if (need_filter) {
398         const dsp::LoopFilterSize size = GetLoopFilterSizeY(filter_length);
399         dsp_.loop_filters[size][kLoopFilterTypeVertical](
400             src_row, src_stride, outer_thresh_[level], inner_thresh_[level],
401             HevThresh(level));
402       }
403       src_row += column_step << column_step_shift;
404       column_step = DivideBy4(column_step);
405     }
406   }
407 
408   if (needs_chroma_deblock_) {
409     const int8_t subsampling_x = subsampling_x_[kPlaneU];
410     const int8_t subsampling_y = subsampling_y_[kPlaneU];
411     const int row_step = 1 << subsampling_y;
412     uint8_t* src_u = GetSourceBuffer(kPlaneU, row4x4_start, column4x4_start);
413     uint8_t* src_v = GetSourceBuffer(kPlaneV, row4x4_start, column4x4_start);
414     const ptrdiff_t src_stride_u = frame_buffer_.stride(kPlaneU);
415     const ptrdiff_t src_stride_v = frame_buffer_.stride(kPlaneV);
416     const ptrdiff_t row_stride_u = MultiplyBy4(frame_buffer_.stride(kPlaneU));
417     const ptrdiff_t row_stride_v = MultiplyBy4(frame_buffer_.stride(kPlaneV));
418     const LoopFilterType type = kLoopFilterTypeVertical;
419     int column_step;
420     uint8_t level_u;
421     uint8_t level_v;
422     int filter_length;
423 
424     BlockParameters* const* bp_row_base = block_parameters_.Address(
425         GetDeblockPosition(row4x4_start, subsampling_y),
426         GetDeblockPosition(column4x4_start, subsampling_x));
427     const int bp_stride = block_parameters_.columns4x4() << subsampling_y;
428     for (int row4x4 = 0; row4x4 < kNum4x4InLoopFilterUnit &&
429                          MultiplyBy4(row4x4_start + row4x4) < height_;
430          row4x4 += row_step, src_u += row_stride_u, src_v += row_stride_v,
431              bp_row_base += bp_stride) {
432       uint8_t* src_row_u = src_u;
433       uint8_t* src_row_v = src_v;
434       BlockParameters* const* bp = bp_row_base;
435       for (int column4x4 = 0; column4x4 < kNum4x4InLoopFilterUnit &&
436                               MultiplyBy4(column4x4_start + column4x4) < width_;
437            column4x4 += column_step, bp += column_step) {
438         GetVerticalDeblockFilterEdgeInfoUV(column4x4_start + column4x4, bp,
439                                            &level_u, &level_v, &column_step,
440                                            &filter_length);
441         if (level_u != 0) {
442           const dsp::LoopFilterSize size = GetLoopFilterSizeUV(filter_length);
443           dsp_.loop_filters[size][type](
444               src_row_u, src_stride_u, outer_thresh_[level_u],
445               inner_thresh_[level_u], HevThresh(level_u));
446         }
447         if (level_v != 0) {
448           const dsp::LoopFilterSize size = GetLoopFilterSizeUV(filter_length);
449           dsp_.loop_filters[size][type](
450               src_row_v, src_stride_v, outer_thresh_[level_v],
451               inner_thresh_[level_v], HevThresh(level_v));
452         }
453         src_row_u += column_step << column_step_shift;
454         src_row_v += column_step << column_step_shift;
455         column_step = DivideBy4(column_step << subsampling_x);
456       }
457     }
458   }
459 }
460 
ApplyDeblockFilterForOneSuperBlockRow(int row4x4_start,int sb4x4)461 void PostFilter::ApplyDeblockFilterForOneSuperBlockRow(int row4x4_start,
462                                                        int sb4x4) {
463   assert(row4x4_start >= 0);
464   assert(DoDeblock());
465   for (int y = 0; y < sb4x4; y += 16) {
466     const int row4x4 = row4x4_start + y;
467     if (row4x4 >= frame_header_.rows4x4) break;
468     int column4x4;
469     for (column4x4 = 0; column4x4 < frame_header_.columns4x4;
470          column4x4 += kNum4x4InLoopFilterUnit) {
471       // First apply vertical filtering
472       VerticalDeblockFilter(row4x4, column4x4);
473 
474       // Delay one superblock to apply horizontal filtering.
475       if (column4x4 != 0) {
476         HorizontalDeblockFilter(row4x4, column4x4 - kNum4x4InLoopFilterUnit);
477       }
478     }
479     // Horizontal filtering for the last 64x64 block.
480     HorizontalDeblockFilter(row4x4, column4x4 - kNum4x4InLoopFilterUnit);
481   }
482 }
483 
484 template <LoopFilterType loop_filter_type>
DeblockFilterWorker(std::atomic<int> * row4x4_atomic)485 void PostFilter::DeblockFilterWorker(std::atomic<int>* row4x4_atomic) {
486   int row4x4;
487   while ((row4x4 = row4x4_atomic->fetch_add(kNum4x4InLoopFilterUnit,
488                                             std::memory_order_relaxed)) <
489          frame_header_.rows4x4) {
490     for (int column4x4 = 0; column4x4 < frame_header_.columns4x4;
491          column4x4 += kNum4x4InLoopFilterUnit) {
492       (this->*deblock_filter_func_[loop_filter_type])(row4x4, column4x4);
493     }
494   }
495 }
496 
497 template void PostFilter::DeblockFilterWorker<kLoopFilterTypeVertical>(
498     std::atomic<int>* row4x4_atomic);
499 template void PostFilter::DeblockFilterWorker<kLoopFilterTypeHorizontal>(
500     std::atomic<int>* row4x4_atomic);
501 
ApplyDeblockFilter(LoopFilterType loop_filter_type,int row4x4_start,int column4x4_start,int column4x4_end,int sb4x4)502 void PostFilter::ApplyDeblockFilter(LoopFilterType loop_filter_type,
503                                     int row4x4_start, int column4x4_start,
504                                     int column4x4_end, int sb4x4) {
505   assert(row4x4_start >= 0);
506   assert(DoDeblock());
507 
508   column4x4_end = std::min(column4x4_end, frame_header_.columns4x4);
509   if (column4x4_start >= column4x4_end) return;
510 
511   const DeblockFilter deblock_filter = deblock_filter_func_[loop_filter_type];
512   const int sb_height4x4 =
513       std::min(sb4x4, frame_header_.rows4x4 - row4x4_start);
514   for (int y = 0; y < sb_height4x4; y += kNum4x4InLoopFilterUnit) {
515     const int row4x4 = row4x4_start + y;
516     for (int column4x4 = column4x4_start; column4x4 < column4x4_end;
517          column4x4 += kNum4x4InLoopFilterUnit) {
518       (this->*deblock_filter)(row4x4, column4x4);
519     }
520   }
521 }
522 
523 }  // namespace libgav1
524