Coverage Report

Created: 2026-09-14 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/epf.cc
Line
Count
Source
1
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
2
//
3
// Use of this source code is governed by a BSD-style
4
// license that can be found in the LICENSE file.
5
6
// Edge-preserving smoothing: weighted average based on L1 patch similarity.
7
8
#include "lib/jxl/epf.h"
9
10
#include <algorithm>
11
#include <cstddef>
12
#include <cstdint>
13
#include <cstring>
14
15
#include "lib/jxl/ac_strategy.h"
16
#include "lib/jxl/base/compiler_specific.h"
17
#include "lib/jxl/base/rect.h"
18
#include "lib/jxl/base/status.h"
19
#include "lib/jxl/dec_cache.h"
20
#include "lib/jxl/loop_filter.h"
21
#include "lib/jxl/quantizer.h"
22
23
namespace jxl {
24
25
// Mirror n floats starting at *p and store them before p.
26
91.3k
JXL_INLINE void LeftMirror(float* p, size_t n) {
27
182k
  for (size_t i = 0; i < n; i++) {
28
91.4k
    *(p - 1 - i) = p[i];
29
91.4k
  }
30
91.3k
}
31
32
// Mirror n floats starting at *(p - n) and store them at *p.
33
91.5k
JXL_INLINE void RightMirror(float* p, size_t n) {
34
183k
  for (size_t i = 0; i < n; i++) {
35
91.6k
    p[i] = *(p - 1 - i);
36
91.6k
  }
37
91.5k
}
38
39
Status ComputeSigma(const LoopFilter& lf, const Rect& block_rect,
40
6.25k
                    PassesDecoderState* state) {
41
6.25k
  JXL_ENSURE(lf.epf_iters > 0);
42
6.25k
  const AcStrategyImage& ac_strategy = state->shared->ac_strategy;
43
6.25k
  const float quant_scale = state->shared->quantizer.Scale();
44
45
6.25k
  const size_t sigma_stride = state->sigma.PixelsPerRow();
46
6.25k
  const size_t sharpness_stride = state->shared->epf_sharpness.PixelsPerRow();
47
48
97.8k
  for (size_t by = 0; by < block_rect.ysize(); ++by) {
49
91.6k
    float* JXL_RESTRICT sigma_row = block_rect.Row(&state->sigma, by);
50
91.6k
    const uint8_t* JXL_RESTRICT sharpness_row =
51
91.6k
        block_rect.ConstRow(state->shared->epf_sharpness, by);
52
91.6k
    AcStrategyRow acs_row = ac_strategy.ConstRow(block_rect, by);
53
91.6k
    const int32_t* const JXL_RESTRICT row_quant =
54
91.6k
        block_rect.ConstRow(state->shared->raw_quant_field, by);
55
56
1.58M
    for (size_t bx = 0; bx < block_rect.xsize(); bx++) {
57
1.48M
      AcStrategy acs = acs_row[bx];
58
1.48M
      size_t llf_x = acs.covered_blocks_x();
59
1.48M
      if (!acs.IsFirstBlock()) continue;
60
      // quant_scale is smaller for low quality.
61
      // quant_scale is roughly 0.08 / butteraugli score.
62
      //
63
      // row_quant is smaller for low quality.
64
      // row_quant is a quantization multiplier of form 1.0 /
65
      // row_quant[bx]
66
      //
67
      // lf.epf_quant_mul is a parameter in the format
68
      // kInvSigmaNum is a constant
69
690k
      float sigma_quant =
70
690k
          lf.epf_quant_mul / (quant_scale * row_quant[bx] * kInvSigmaNum);
71
1.41M
      for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) {
72
2.20M
        for (size_t ix = 0; ix < acs.covered_blocks_x(); ix++) {
73
1.48M
          float sigma =
74
1.48M
              sigma_quant *
75
1.48M
              lf.epf_sharp_lut[sharpness_row[bx + ix + iy * sharpness_stride]];
76
          // Avoid infinities.
77
1.48M
          sigma = std::min(-1e-4f, sigma);  // TODO(veluca): remove this.
78
1.48M
          sigma_row[bx + ix + kSigmaPadding +
79
1.48M
                    (iy + kSigmaPadding) * sigma_stride] = 1.0f / sigma;
80
1.48M
        }
81
719k
      }
82
      // TODO(veluca): remove this padding.
83
      // Left padding with mirroring.
84
690k
      if (bx + block_rect.x0() == 0) {
85
158k
        for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) {
86
91.3k
          LeftMirror(
87
91.3k
              sigma_row + kSigmaPadding + (iy + kSigmaPadding) * sigma_stride,
88
91.3k
              kSigmaBorder);
89
91.3k
        }
90
66.8k
      }
91
      // Right padding with mirroring.
92
690k
      if (bx + block_rect.x0() + llf_x ==
93
690k
          state->shared->frame_dim.xsize_blocks) {
94
166k
        for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) {
95
91.5k
          RightMirror(sigma_row + kSigmaPadding + bx + llf_x +
96
91.5k
                          (iy + kSigmaPadding) * sigma_stride,
97
91.5k
                      kSigmaBorder);
98
91.5k
        }
99
74.8k
      }
100
      // Offsets for row copying, in blocks.
101
690k
      size_t offset_before = bx + block_rect.x0() == 0 ? 1 : bx + kSigmaPadding;
102
690k
      size_t offset_after =
103
690k
          bx + block_rect.x0() + llf_x == state->shared->frame_dim.xsize_blocks
104
690k
              ? kSigmaPadding + llf_x + bx + kSigmaBorder
105
690k
              : kSigmaPadding + llf_x + bx;
106
690k
      size_t num = offset_after - offset_before;
107
      // Above
108
690k
      if (by + block_rect.y0() == 0) {
109
73.3k
        for (size_t iy = 0; iy < kSigmaBorder; iy++) {
110
36.6k
          memcpy(
111
36.6k
              sigma_row + offset_before +
112
36.6k
                  (kSigmaPadding - 1 - iy) * sigma_stride,
113
36.6k
              sigma_row + offset_before + (kSigmaPadding + iy) * sigma_stride,
114
36.6k
              num * sizeof(*sigma_row));
115
36.6k
        }
116
36.6k
      }
117
      // Below
118
690k
      if (by + block_rect.y0() + acs.covered_blocks_y() ==
119
690k
          state->shared->frame_dim.ysize_blocks) {
120
94.4k
        for (size_t iy = 0; iy < kSigmaBorder; iy++) {
121
47.2k
          memcpy(
122
47.2k
              sigma_row + offset_before +
123
47.2k
                  sigma_stride * (acs.covered_blocks_y() + kSigmaPadding + iy),
124
47.2k
              sigma_row + offset_before +
125
47.2k
                  sigma_stride *
126
47.2k
                      (acs.covered_blocks_y() + kSigmaPadding - 1 - iy),
127
47.2k
              num * sizeof(*sigma_row));
128
47.2k
        }
129
47.2k
      }
130
690k
    }
131
91.6k
  }
132
6.25k
  return true;
133
6.25k
}
134
135
}  // namespace jxl