Coverage Report

Created: 2026-09-14 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/modular/transform/squeeze.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
#include "lib/jxl/modular/transform/squeeze.h"
7
8
#include <jxl/memory_manager.h>
9
10
#include <algorithm>
11
#include <cstddef>
12
#include <cstdint>
13
#include <cstdlib>
14
#include <utility>
15
#include <vector>
16
17
#include "lib/jxl/base/common.h"
18
#include "lib/jxl/base/compiler_specific.h"
19
#include "lib/jxl/base/data_parallel.h"
20
#include "lib/jxl/base/printf_macros.h"
21
#include "lib/jxl/base/status.h"
22
#include "lib/jxl/modular/modular_image.h"
23
#include "lib/jxl/modular/transform/squeeze_params.h"
24
#undef HWY_TARGET_INCLUDE
25
#define HWY_TARGET_INCLUDE "lib/jxl/modular/transform/squeeze.cc"
26
#include <hwy/foreach_target.h>
27
#include <hwy/highway.h>
28
29
#include "lib/jxl/simd_util-inl.h"
30
31
HWY_BEFORE_NAMESPACE();
32
namespace jxl {
33
namespace HWY_NAMESPACE {
34
35
#if HWY_TARGET != HWY_SCALAR
36
37
// These templates are not found via ADL.
38
using hwy::HWY_NAMESPACE::Abs;
39
using hwy::HWY_NAMESPACE::Add;
40
using hwy::HWY_NAMESPACE::And;
41
using hwy::HWY_NAMESPACE::DupEven;
42
using hwy::HWY_NAMESPACE::DupOdd;
43
using hwy::HWY_NAMESPACE::Gt;
44
using hwy::HWY_NAMESPACE::IfThenElse;
45
using hwy::HWY_NAMESPACE::IfThenZeroElse;
46
using hwy::HWY_NAMESPACE::Lt;
47
using hwy::HWY_NAMESPACE::MulEven;
48
using hwy::HWY_NAMESPACE::MulOdd;
49
using hwy::HWY_NAMESPACE::Ne;
50
using hwy::HWY_NAMESPACE::Neg;
51
using hwy::HWY_NAMESPACE::OddEven;
52
using hwy::HWY_NAMESPACE::RebindToUnsigned;
53
using hwy::HWY_NAMESPACE::ShiftLeft;
54
using hwy::HWY_NAMESPACE::ShiftRight;
55
using hwy::HWY_NAMESPACE::Sub;
56
using hwy::HWY_NAMESPACE::Xor;
57
58
using D = HWY_CAPPED(pixel_type, 8);
59
using DU = RebindToUnsigned<D>;
60
constexpr D d;
61
constexpr DU du;
62
63
JXL_INLINE void FastUnsqueeze(const pixel_type *JXL_RESTRICT p_residual,
64
                              const pixel_type *JXL_RESTRICT p_avg,
65
                              const pixel_type *JXL_RESTRICT p_navg,
66
                              const pixel_type *p_pout,
67
                              pixel_type *JXL_RESTRICT p_out,
68
43.0M
                              pixel_type *p_nout) {
69
43.0M
  const size_t N = Lanes(d);
70
43.0M
  auto onethird = Set(d, 0x55555556);
71
86.1M
  for (size_t x = 0; x < 8; x += N) {
72
43.0M
    auto avg = Load(d, p_avg + x);
73
43.0M
    auto next_avg = Load(d, p_navg + x);
74
43.0M
    auto top = Load(d, p_pout + x);
75
    // Equivalent to SmoothTendency(top,avg,next_avg), but without branches
76
    // typo:off
77
43.0M
    auto Ba = Sub(top, avg);
78
43.0M
    auto an = Sub(avg, next_avg);
79
43.0M
    auto nonmono = Xor(Ba, an);
80
43.0M
    auto absBa = Abs(Ba);
81
43.0M
    auto absan = Abs(an);
82
43.0M
    auto absBn = Abs(Sub(top, next_avg));
83
    // Compute a3 = absBa / 3
84
43.0M
    auto a3eh = MulEven(absBa, onethird);
85
43.0M
    auto a3oh = MulOdd(absBa, onethird);
86
87
43.0M
#if (HWY_MAJOR > 1 || (HWY_MAJOR == 1 && HWY_MINOR >= 2))
88
43.0M
#if HWY_IS_LITTLE_ENDIAN
89
43.0M
    auto a3 = InterleaveOdd(d, BitCast(d, a3eh), BitCast(d, a3oh));
90
#else  // not little endian
91
    auto a3 = InterleaveEven(d, BitCast(d, a3eh), BitCast(d, a3oh));
92
#endif  // endianness
93
#else  // hwy < 1.2
94
#if HWY_IS_LITTLE_ENDIAN
95
    auto a3 = OddEven(BitCast(d, a3oh), DupOdd(BitCast(d, a3eh)));
96
#else  // not little endian
97
    auto a3 = OddEven(DupEven(BitCast(d, a3oh)), BitCast(d, a3eh));
98
#endif  // endianness
99
#endif  // hwy version
100
101
43.0M
    a3 = Add(a3, Add(absBn, Set(d, 2)));
102
43.0M
    auto absdiff = ShiftRight<2>(a3);
103
43.0M
    auto skipdiff = Ne(Ba, Zero(d));
104
43.0M
    skipdiff = And(skipdiff, Ne(an, Zero(d)));
105
43.0M
    skipdiff = And(skipdiff, Lt(nonmono, Zero(d)));
106
43.0M
    auto absBa2 = Add(ShiftLeft<1>(absBa), And(absdiff, Set(d, 1)));
107
43.0M
    absdiff = IfThenElse(Gt(absdiff, absBa2),
108
43.0M
                         Add(ShiftLeft<1>(absBa), Set(d, 1)), absdiff);
109
    // typo:on
110
43.0M
    auto absan2 = ShiftLeft<1>(absan);
111
43.0M
    absdiff = IfThenElse(Gt(Add(absdiff, And(absdiff, Set(d, 1))), absan2),
112
43.0M
                         absan2, absdiff);
113
43.0M
    auto diff1 = IfThenElse(Lt(top, next_avg), Neg(absdiff), absdiff);
114
43.0M
    auto tendency = IfThenZeroElse(skipdiff, diff1);
115
116
43.0M
    auto diff_minus_tendency = Load(d, p_residual + x);
117
43.0M
    auto diff = Add(diff_minus_tendency, tendency);
118
43.0M
    auto out =
119
43.0M
        Add(avg, ShiftRight<1>(
120
43.0M
                     Add(diff, BitCast(d, ShiftRight<31>(BitCast(du, diff))))));
121
43.0M
    Store(out, d, p_out + x);
122
43.0M
    Store(Sub(out, diff), d, p_nout + x);
123
43.0M
  }
124
43.0M
}
Unexecuted instantiation: jxl::N_SSE4::FastUnsqueeze(int const*, int const*, int const*, int const*, int*, int*)
jxl::N_AVX2::FastUnsqueeze(int const*, int const*, int const*, int const*, int*, int*)
Line
Count
Source
68
43.0M
                              pixel_type *p_nout) {
69
43.0M
  const size_t N = Lanes(d);
70
43.0M
  auto onethird = Set(d, 0x55555556);
71
86.1M
  for (size_t x = 0; x < 8; x += N) {
72
43.0M
    auto avg = Load(d, p_avg + x);
73
43.0M
    auto next_avg = Load(d, p_navg + x);
74
43.0M
    auto top = Load(d, p_pout + x);
75
    // Equivalent to SmoothTendency(top,avg,next_avg), but without branches
76
    // typo:off
77
43.0M
    auto Ba = Sub(top, avg);
78
43.0M
    auto an = Sub(avg, next_avg);
79
43.0M
    auto nonmono = Xor(Ba, an);
80
43.0M
    auto absBa = Abs(Ba);
81
43.0M
    auto absan = Abs(an);
82
43.0M
    auto absBn = Abs(Sub(top, next_avg));
83
    // Compute a3 = absBa / 3
84
43.0M
    auto a3eh = MulEven(absBa, onethird);
85
43.0M
    auto a3oh = MulOdd(absBa, onethird);
86
87
43.0M
#if (HWY_MAJOR > 1 || (HWY_MAJOR == 1 && HWY_MINOR >= 2))
88
43.0M
#if HWY_IS_LITTLE_ENDIAN
89
43.0M
    auto a3 = InterleaveOdd(d, BitCast(d, a3eh), BitCast(d, a3oh));
90
#else  // not little endian
91
    auto a3 = InterleaveEven(d, BitCast(d, a3eh), BitCast(d, a3oh));
92
#endif  // endianness
93
#else  // hwy < 1.2
94
#if HWY_IS_LITTLE_ENDIAN
95
    auto a3 = OddEven(BitCast(d, a3oh), DupOdd(BitCast(d, a3eh)));
96
#else  // not little endian
97
    auto a3 = OddEven(DupEven(BitCast(d, a3oh)), BitCast(d, a3eh));
98
#endif  // endianness
99
#endif  // hwy version
100
101
43.0M
    a3 = Add(a3, Add(absBn, Set(d, 2)));
102
43.0M
    auto absdiff = ShiftRight<2>(a3);
103
43.0M
    auto skipdiff = Ne(Ba, Zero(d));
104
43.0M
    skipdiff = And(skipdiff, Ne(an, Zero(d)));
105
43.0M
    skipdiff = And(skipdiff, Lt(nonmono, Zero(d)));
106
43.0M
    auto absBa2 = Add(ShiftLeft<1>(absBa), And(absdiff, Set(d, 1)));
107
43.0M
    absdiff = IfThenElse(Gt(absdiff, absBa2),
108
43.0M
                         Add(ShiftLeft<1>(absBa), Set(d, 1)), absdiff);
109
    // typo:on
110
43.0M
    auto absan2 = ShiftLeft<1>(absan);
111
43.0M
    absdiff = IfThenElse(Gt(Add(absdiff, And(absdiff, Set(d, 1))), absan2),
112
43.0M
                         absan2, absdiff);
113
43.0M
    auto diff1 = IfThenElse(Lt(top, next_avg), Neg(absdiff), absdiff);
114
43.0M
    auto tendency = IfThenZeroElse(skipdiff, diff1);
115
116
43.0M
    auto diff_minus_tendency = Load(d, p_residual + x);
117
43.0M
    auto diff = Add(diff_minus_tendency, tendency);
118
43.0M
    auto out =
119
43.0M
        Add(avg, ShiftRight<1>(
120
43.0M
                     Add(diff, BitCast(d, ShiftRight<31>(BitCast(du, diff))))));
121
43.0M
    Store(out, d, p_out + x);
122
43.0M
    Store(Sub(out, diff), d, p_nout + x);
123
43.0M
  }
124
43.0M
}
Unexecuted instantiation: jxl::N_AVX3::FastUnsqueeze(int const*, int const*, int const*, int const*, int*, int*)
Unexecuted instantiation: jxl::N_AVX3_ZEN4::FastUnsqueeze(int const*, int const*, int const*, int const*, int*, int*)
Unexecuted instantiation: jxl::N_AVX3_SPR::FastUnsqueeze(int const*, int const*, int const*, int const*, int*, int*)
Unexecuted instantiation: jxl::N_SSE2::FastUnsqueeze(int const*, int const*, int const*, int const*, int*, int*)
125
126
#endif  // HWY_TARGET != HWY_SCALAR
127
128
179k
Status InvHSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
129
179k
  JXL_ENSURE(c < input.channel.size());
130
179k
  JXL_ENSURE(rc < input.channel.size());
131
179k
  Channel &chin = input.channel[c];
132
179k
  const Channel &chin_residual = input.channel[rc];
133
  // These must be valid since we ran MetaApply already.
134
179k
  JXL_ENSURE(chin.w == DivCeil(chin.w + chin_residual.w, 2));
135
179k
  JXL_ENSURE(chin.h == chin_residual.h);
136
179k
  JxlMemoryManager *memory_manager = input.memory_manager();
137
138
179k
  if (chin_residual.w == 0) {
139
    // Short-circuit: output channel has same dimensions as input.
140
15.4k
    input.channel[c].hshift--;
141
15.4k
    return true;
142
15.4k
  }
143
144
  // Note: chin.w >= chin_residual.w and at most 1 different.
145
328k
  JXL_ASSIGN_OR_RETURN(Channel chout,
146
328k
                       Channel::Create(memory_manager, chin.w + chin_residual.w,
147
328k
                                       chin.h, chin.hshift - 1, chin.vshift));
148
328k
  JXL_DEBUG_V(4,
149
328k
              "Undoing horizontal squeeze of channel %i using residuals in "
150
328k
              "channel %i (going from width %" PRIuS " to %" PRIuS ")",
151
328k
              c, rc, chin.w, chout.w);
152
153
328k
  if (chin_residual.h == 0) {
154
    // Short-circuit: channel with no pixels.
155
0
    input.channel[c] = std::move(chout);
156
0
    return true;
157
0
  }
158
6.12M
  auto unsqueeze_row = [&](size_t y, size_t x0) {
159
6.12M
    const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y);
160
6.12M
    const pixel_type *JXL_RESTRICT p_avg = chin.Row(y);
161
6.12M
    pixel_type *JXL_RESTRICT p_out = chout.Row(y);
162
50.2M
    for (size_t x = x0; x < chin_residual.w; x++) {
163
44.1M
      pixel_type_w diff_minus_tendency = p_residual[x];
164
44.1M
      pixel_type_w avg = p_avg[x];
165
44.1M
      pixel_type_w next_avg = (x + 1 < chin.w ? p_avg[x + 1] : avg);
166
44.1M
      pixel_type_w left = (x ? p_out[(x << 1) - 1] : avg);
167
44.1M
      pixel_type_w tendency = SmoothTendency(left, avg, next_avg);
168
44.1M
      pixel_type_w diff = diff_minus_tendency + tendency;
169
44.1M
      pixel_type_w A = avg + (diff / 2);
170
44.1M
      p_out[(x << 1)] = A;
171
44.1M
      pixel_type_w B = A - diff;
172
44.1M
      p_out[(x << 1) + 1] = B;
173
44.1M
    }
174
6.12M
    if (chout.w & 1) p_out[chout.w - 1] = p_avg[chin.w - 1];
175
6.12M
  };
Unexecuted instantiation: squeeze.cc:jxl::N_SSE4::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned long, unsigned long) const
squeeze.cc:jxl::N_AVX2::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned long, unsigned long) const
Line
Count
Source
158
6.12M
  auto unsqueeze_row = [&](size_t y, size_t x0) {
159
6.12M
    const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y);
160
6.12M
    const pixel_type *JXL_RESTRICT p_avg = chin.Row(y);
161
6.12M
    pixel_type *JXL_RESTRICT p_out = chout.Row(y);
162
50.2M
    for (size_t x = x0; x < chin_residual.w; x++) {
163
44.1M
      pixel_type_w diff_minus_tendency = p_residual[x];
164
44.1M
      pixel_type_w avg = p_avg[x];
165
44.1M
      pixel_type_w next_avg = (x + 1 < chin.w ? p_avg[x + 1] : avg);
166
44.1M
      pixel_type_w left = (x ? p_out[(x << 1) - 1] : avg);
167
44.1M
      pixel_type_w tendency = SmoothTendency(left, avg, next_avg);
168
44.1M
      pixel_type_w diff = diff_minus_tendency + tendency;
169
44.1M
      pixel_type_w A = avg + (diff / 2);
170
44.1M
      p_out[(x << 1)] = A;
171
44.1M
      pixel_type_w B = A - diff;
172
44.1M
      p_out[(x << 1) + 1] = B;
173
44.1M
    }
174
6.12M
    if (chout.w & 1) p_out[chout.w - 1] = p_avg[chin.w - 1];
175
6.12M
  };
Unexecuted instantiation: squeeze.cc:jxl::N_AVX3::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned long, unsigned long) const
Unexecuted instantiation: squeeze.cc:jxl::N_AVX3_ZEN4::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned long, unsigned long) const
Unexecuted instantiation: squeeze.cc:jxl::N_AVX3_SPR::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned long, unsigned long) const
Unexecuted instantiation: squeeze.cc:jxl::N_SSE2::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned long, unsigned long) const
176
177
  // somewhat complicated trickery just to be able to SIMD this.
178
  // Horizontal unsqueeze has horizontal data dependencies, so we do
179
  // 8 rows at a time and treat it as a vertical unsqueeze of a
180
  // transposed 8x8 block (or 9x8 for one input).
181
164k
  static constexpr const size_t kRowsPerThread = 8;
182
164k
  const auto unsqueeze_span = [&](const uint32_t task,
183
822k
                                  size_t /* thread */) -> Status {
184
822k
    const size_t y0 = task * kRowsPerThread;
185
822k
    const size_t rows = std::min(kRowsPerThread, chin.h - y0);
186
822k
    size_t x = 0;
187
188
822k
#if HWY_TARGET != HWY_SCALAR
189
822k
    ptrdiff_t onerow_in = chin.plane.PixelsPerRow();
190
822k
    ptrdiff_t onerow_inr = chin_residual.plane.PixelsPerRow();
191
822k
    ptrdiff_t onerow_out = chout.plane.PixelsPerRow();
192
822k
    const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y0);
193
822k
    const pixel_type *JXL_RESTRICT p_avg = chin.Row(y0);
194
822k
    pixel_type *JXL_RESTRICT p_out = chout.Row(y0);
195
822k
    HWY_ALIGN pixel_type b_p_avg[9 * kRowsPerThread];
196
822k
    HWY_ALIGN pixel_type b_p_residual[8 * kRowsPerThread];
197
822k
    HWY_ALIGN pixel_type b_p_out_even[8 * kRowsPerThread];
198
822k
    HWY_ALIGN pixel_type b_p_out_odd[8 * kRowsPerThread];
199
822k
    HWY_ALIGN pixel_type b_p_out_evenT[8 * kRowsPerThread];
200
822k
    HWY_ALIGN pixel_type b_p_out_oddT[8 * kRowsPerThread];
201
822k
    const size_t N = Lanes(d);
202
822k
    if (chin_residual.w > 16 && rows == kRowsPerThread) {
203
3.15M
      for (; x < chin_residual.w - 9; x += 8) {
204
2.66M
        Transpose8x8Block(p_residual + x, b_p_residual, onerow_inr);
205
2.66M
        Transpose8x8Block(p_avg + x, b_p_avg, onerow_in);
206
23.9M
        for (size_t y = 0; y < kRowsPerThread; y++) {
207
21.3M
          b_p_avg[8 * 8 + y] = p_avg[x + 8 + onerow_in * y];
208
21.3M
        }
209
23.9M
        for (size_t i = 0; i < 8; i++) {
210
21.3M
          FastUnsqueeze(
211
21.3M
              b_p_residual + 8 * i, b_p_avg + 8 * i, b_p_avg + 8 * (i + 1),
212
21.3M
              (x + i ? b_p_out_odd + 8 * ((x + i - 1) & 7) : b_p_avg + 8 * i),
213
21.3M
              b_p_out_even + 8 * i, b_p_out_odd + 8 * i);
214
21.3M
        }
215
216
2.66M
        Transpose8x8Block(b_p_out_even, b_p_out_evenT, 8);
217
2.66M
        Transpose8x8Block(b_p_out_odd, b_p_out_oddT, 8);
218
23.9M
        for (size_t y = 0; y < kRowsPerThread; y++) {
219
42.6M
          for (size_t i = 0; i < kRowsPerThread; i += N) {
220
21.3M
            auto even = Load(d, b_p_out_evenT + 8 * y + i);
221
21.3M
            auto odd = Load(d, b_p_out_oddT + 8 * y + i);
222
21.3M
            StoreInterleaved(d, even, odd,
223
21.3M
                             p_out + ((x + i) << 1) + onerow_out * y);
224
21.3M
          }
225
21.3M
        }
226
2.66M
      }
227
488k
    }
228
822k
#endif  // HWY_TARGET != HWY_SCALAR
229
6.94M
    for (size_t y = 0; y < rows; y++) {
230
6.12M
      unsqueeze_row(y0 + y, x);
231
6.12M
    }
232
822k
    return true;
233
822k
  };
Unexecuted instantiation: squeeze.cc:jxl::N_SSE4::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_1::operator()(unsigned int, unsigned long) const
squeeze.cc:jxl::N_AVX2::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_1::operator()(unsigned int, unsigned long) const
Line
Count
Source
183
822k
                                  size_t /* thread */) -> Status {
184
822k
    const size_t y0 = task * kRowsPerThread;
185
822k
    const size_t rows = std::min(kRowsPerThread, chin.h - y0);
186
822k
    size_t x = 0;
187
188
822k
#if HWY_TARGET != HWY_SCALAR
189
822k
    ptrdiff_t onerow_in = chin.plane.PixelsPerRow();
190
822k
    ptrdiff_t onerow_inr = chin_residual.plane.PixelsPerRow();
191
822k
    ptrdiff_t onerow_out = chout.plane.PixelsPerRow();
192
822k
    const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y0);
193
822k
    const pixel_type *JXL_RESTRICT p_avg = chin.Row(y0);
194
822k
    pixel_type *JXL_RESTRICT p_out = chout.Row(y0);
195
822k
    HWY_ALIGN pixel_type b_p_avg[9 * kRowsPerThread];
196
822k
    HWY_ALIGN pixel_type b_p_residual[8 * kRowsPerThread];
197
822k
    HWY_ALIGN pixel_type b_p_out_even[8 * kRowsPerThread];
198
822k
    HWY_ALIGN pixel_type b_p_out_odd[8 * kRowsPerThread];
199
822k
    HWY_ALIGN pixel_type b_p_out_evenT[8 * kRowsPerThread];
200
822k
    HWY_ALIGN pixel_type b_p_out_oddT[8 * kRowsPerThread];
201
822k
    const size_t N = Lanes(d);
202
822k
    if (chin_residual.w > 16 && rows == kRowsPerThread) {
203
3.15M
      for (; x < chin_residual.w - 9; x += 8) {
204
2.66M
        Transpose8x8Block(p_residual + x, b_p_residual, onerow_inr);
205
2.66M
        Transpose8x8Block(p_avg + x, b_p_avg, onerow_in);
206
23.9M
        for (size_t y = 0; y < kRowsPerThread; y++) {
207
21.3M
          b_p_avg[8 * 8 + y] = p_avg[x + 8 + onerow_in * y];
208
21.3M
        }
209
23.9M
        for (size_t i = 0; i < 8; i++) {
210
21.3M
          FastUnsqueeze(
211
21.3M
              b_p_residual + 8 * i, b_p_avg + 8 * i, b_p_avg + 8 * (i + 1),
212
21.3M
              (x + i ? b_p_out_odd + 8 * ((x + i - 1) & 7) : b_p_avg + 8 * i),
213
21.3M
              b_p_out_even + 8 * i, b_p_out_odd + 8 * i);
214
21.3M
        }
215
216
2.66M
        Transpose8x8Block(b_p_out_even, b_p_out_evenT, 8);
217
2.66M
        Transpose8x8Block(b_p_out_odd, b_p_out_oddT, 8);
218
23.9M
        for (size_t y = 0; y < kRowsPerThread; y++) {
219
42.6M
          for (size_t i = 0; i < kRowsPerThread; i += N) {
220
21.3M
            auto even = Load(d, b_p_out_evenT + 8 * y + i);
221
21.3M
            auto odd = Load(d, b_p_out_oddT + 8 * y + i);
222
21.3M
            StoreInterleaved(d, even, odd,
223
21.3M
                             p_out + ((x + i) << 1) + onerow_out * y);
224
21.3M
          }
225
21.3M
        }
226
2.66M
      }
227
488k
    }
228
822k
#endif  // HWY_TARGET != HWY_SCALAR
229
6.94M
    for (size_t y = 0; y < rows; y++) {
230
6.12M
      unsqueeze_row(y0 + y, x);
231
6.12M
    }
232
822k
    return true;
233
822k
  };
Unexecuted instantiation: squeeze.cc:jxl::N_AVX3::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_1::operator()(unsigned int, unsigned long) const
Unexecuted instantiation: squeeze.cc:jxl::N_AVX3_ZEN4::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_1::operator()(unsigned int, unsigned long) const
Unexecuted instantiation: squeeze.cc:jxl::N_AVX3_SPR::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_1::operator()(unsigned int, unsigned long) const
Unexecuted instantiation: squeeze.cc:jxl::N_SSE2::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_1::operator()(unsigned int, unsigned long) const
234
164k
  JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, DivCeil(chin.h, kRowsPerThread),
235
164k
                                ThreadPool::NoInit, unsqueeze_span,
236
164k
                                "InvHorizontalSqueeze"));
237
164k
  input.channel[c] = std::move(chout);
238
164k
  return true;
239
164k
}
Unexecuted instantiation: jxl::N_SSE4::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
jxl::N_AVX2::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
Line
Count
Source
128
179k
Status InvHSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
129
179k
  JXL_ENSURE(c < input.channel.size());
130
179k
  JXL_ENSURE(rc < input.channel.size());
131
179k
  Channel &chin = input.channel[c];
132
179k
  const Channel &chin_residual = input.channel[rc];
133
  // These must be valid since we ran MetaApply already.
134
179k
  JXL_ENSURE(chin.w == DivCeil(chin.w + chin_residual.w, 2));
135
179k
  JXL_ENSURE(chin.h == chin_residual.h);
136
179k
  JxlMemoryManager *memory_manager = input.memory_manager();
137
138
179k
  if (chin_residual.w == 0) {
139
    // Short-circuit: output channel has same dimensions as input.
140
15.4k
    input.channel[c].hshift--;
141
15.4k
    return true;
142
15.4k
  }
143
144
  // Note: chin.w >= chin_residual.w and at most 1 different.
145
328k
  JXL_ASSIGN_OR_RETURN(Channel chout,
146
328k
                       Channel::Create(memory_manager, chin.w + chin_residual.w,
147
328k
                                       chin.h, chin.hshift - 1, chin.vshift));
148
328k
  JXL_DEBUG_V(4,
149
328k
              "Undoing horizontal squeeze of channel %i using residuals in "
150
328k
              "channel %i (going from width %" PRIuS " to %" PRIuS ")",
151
328k
              c, rc, chin.w, chout.w);
152
153
328k
  if (chin_residual.h == 0) {
154
    // Short-circuit: channel with no pixels.
155
0
    input.channel[c] = std::move(chout);
156
0
    return true;
157
0
  }
158
164k
  auto unsqueeze_row = [&](size_t y, size_t x0) {
159
164k
    const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y);
160
164k
    const pixel_type *JXL_RESTRICT p_avg = chin.Row(y);
161
164k
    pixel_type *JXL_RESTRICT p_out = chout.Row(y);
162
164k
    for (size_t x = x0; x < chin_residual.w; x++) {
163
164k
      pixel_type_w diff_minus_tendency = p_residual[x];
164
164k
      pixel_type_w avg = p_avg[x];
165
164k
      pixel_type_w next_avg = (x + 1 < chin.w ? p_avg[x + 1] : avg);
166
164k
      pixel_type_w left = (x ? p_out[(x << 1) - 1] : avg);
167
164k
      pixel_type_w tendency = SmoothTendency(left, avg, next_avg);
168
164k
      pixel_type_w diff = diff_minus_tendency + tendency;
169
164k
      pixel_type_w A = avg + (diff / 2);
170
164k
      p_out[(x << 1)] = A;
171
164k
      pixel_type_w B = A - diff;
172
164k
      p_out[(x << 1) + 1] = B;
173
164k
    }
174
164k
    if (chout.w & 1) p_out[chout.w - 1] = p_avg[chin.w - 1];
175
164k
  };
176
177
  // somewhat complicated trickery just to be able to SIMD this.
178
  // Horizontal unsqueeze has horizontal data dependencies, so we do
179
  // 8 rows at a time and treat it as a vertical unsqueeze of a
180
  // transposed 8x8 block (or 9x8 for one input).
181
164k
  static constexpr const size_t kRowsPerThread = 8;
182
164k
  const auto unsqueeze_span = [&](const uint32_t task,
183
164k
                                  size_t /* thread */) -> Status {
184
164k
    const size_t y0 = task * kRowsPerThread;
185
164k
    const size_t rows = std::min(kRowsPerThread, chin.h - y0);
186
164k
    size_t x = 0;
187
188
164k
#if HWY_TARGET != HWY_SCALAR
189
164k
    ptrdiff_t onerow_in = chin.plane.PixelsPerRow();
190
164k
    ptrdiff_t onerow_inr = chin_residual.plane.PixelsPerRow();
191
164k
    ptrdiff_t onerow_out = chout.plane.PixelsPerRow();
192
164k
    const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y0);
193
164k
    const pixel_type *JXL_RESTRICT p_avg = chin.Row(y0);
194
164k
    pixel_type *JXL_RESTRICT p_out = chout.Row(y0);
195
164k
    HWY_ALIGN pixel_type b_p_avg[9 * kRowsPerThread];
196
164k
    HWY_ALIGN pixel_type b_p_residual[8 * kRowsPerThread];
197
164k
    HWY_ALIGN pixel_type b_p_out_even[8 * kRowsPerThread];
198
164k
    HWY_ALIGN pixel_type b_p_out_odd[8 * kRowsPerThread];
199
164k
    HWY_ALIGN pixel_type b_p_out_evenT[8 * kRowsPerThread];
200
164k
    HWY_ALIGN pixel_type b_p_out_oddT[8 * kRowsPerThread];
201
164k
    const size_t N = Lanes(d);
202
164k
    if (chin_residual.w > 16 && rows == kRowsPerThread) {
203
164k
      for (; x < chin_residual.w - 9; x += 8) {
204
164k
        Transpose8x8Block(p_residual + x, b_p_residual, onerow_inr);
205
164k
        Transpose8x8Block(p_avg + x, b_p_avg, onerow_in);
206
164k
        for (size_t y = 0; y < kRowsPerThread; y++) {
207
164k
          b_p_avg[8 * 8 + y] = p_avg[x + 8 + onerow_in * y];
208
164k
        }
209
164k
        for (size_t i = 0; i < 8; i++) {
210
164k
          FastUnsqueeze(
211
164k
              b_p_residual + 8 * i, b_p_avg + 8 * i, b_p_avg + 8 * (i + 1),
212
164k
              (x + i ? b_p_out_odd + 8 * ((x + i - 1) & 7) : b_p_avg + 8 * i),
213
164k
              b_p_out_even + 8 * i, b_p_out_odd + 8 * i);
214
164k
        }
215
216
164k
        Transpose8x8Block(b_p_out_even, b_p_out_evenT, 8);
217
164k
        Transpose8x8Block(b_p_out_odd, b_p_out_oddT, 8);
218
164k
        for (size_t y = 0; y < kRowsPerThread; y++) {
219
164k
          for (size_t i = 0; i < kRowsPerThread; i += N) {
220
164k
            auto even = Load(d, b_p_out_evenT + 8 * y + i);
221
164k
            auto odd = Load(d, b_p_out_oddT + 8 * y + i);
222
164k
            StoreInterleaved(d, even, odd,
223
164k
                             p_out + ((x + i) << 1) + onerow_out * y);
224
164k
          }
225
164k
        }
226
164k
      }
227
164k
    }
228
164k
#endif  // HWY_TARGET != HWY_SCALAR
229
164k
    for (size_t y = 0; y < rows; y++) {
230
164k
      unsqueeze_row(y0 + y, x);
231
164k
    }
232
164k
    return true;
233
164k
  };
234
164k
  JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, DivCeil(chin.h, kRowsPerThread),
235
164k
                                ThreadPool::NoInit, unsqueeze_span,
236
164k
                                "InvHorizontalSqueeze"));
237
164k
  input.channel[c] = std::move(chout);
238
164k
  return true;
239
164k
}
Unexecuted instantiation: jxl::N_AVX3::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
Unexecuted instantiation: jxl::N_AVX3_ZEN4::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
Unexecuted instantiation: jxl::N_AVX3_SPR::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
Unexecuted instantiation: jxl::N_SSE2::InvHSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
240
241
220k
Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
242
220k
  JXL_ENSURE(c < input.channel.size());
243
220k
  JXL_ENSURE(rc < input.channel.size());
244
220k
  const Channel &chin = input.channel[c];
245
220k
  const Channel &chin_residual = input.channel[rc];
246
  // These must be valid since we ran MetaApply already.
247
220k
  JXL_ENSURE(chin.h == DivCeil(chin.h + chin_residual.h, 2));
248
220k
  JXL_ENSURE(chin.w == chin_residual.w);
249
220k
  JxlMemoryManager *memory_manager = input.memory_manager();
250
251
220k
  if (chin_residual.h == 0) {
252
    // Short-circuit: output channel has same dimensions as input.
253
27.8k
    input.channel[c].vshift--;
254
27.8k
    return true;
255
27.8k
  }
256
257
  // Note: chin.h >= chin_residual.h and at most 1 different.
258
386k
  JXL_ASSIGN_OR_RETURN(
259
386k
      Channel chout,
260
386k
      Channel::Create(memory_manager, chin.w, chin.h + chin_residual.h,
261
386k
                      chin.hshift, chin.vshift - 1));
262
386k
  JXL_DEBUG_V(
263
386k
      4,
264
386k
      "Undoing vertical squeeze of channel %i using residuals in channel "
265
386k
      "%i (going from height %" PRIuS " to %" PRIuS ")",
266
386k
      c, rc, chin.h, chout.h);
267
268
386k
  if (chin_residual.w == 0) {
269
    // Short-circuit: channel with no pixels.
270
0
    input.channel[c] = std::move(chout);
271
0
    return true;
272
0
  }
273
274
193k
  static constexpr const int kColsPerThread = 64;
275
193k
  const auto unsqueeze_slice = [&](const uint32_t task,
276
205k
                                   size_t /* thread */) -> Status {
277
205k
    const size_t x0 = task * kColsPerThread;
278
205k
    const size_t x1 =
279
205k
        std::min(static_cast<size_t>(task + 1) * kColsPerThread, chin.w);
280
205k
    const size_t w = x1 - x0;
281
    // We only iterate up to std::min(chin_residual.h, chin.h) which is
282
    // always chin_residual.h.
283
5.59M
    for (size_t y = 0; y < chin_residual.h; y++) {
284
5.38M
      const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y) + x0;
285
5.38M
      const pixel_type *JXL_RESTRICT p_avg = chin.Row(y) + x0;
286
5.38M
      const pixel_type *JXL_RESTRICT p_navg =
287
5.38M
          chin.Row(y + 1 < chin.h ? y + 1 : y) + x0;
288
5.38M
      pixel_type *JXL_RESTRICT p_out = chout.Row(y << 1) + x0;
289
5.38M
      pixel_type *JXL_RESTRICT p_nout = chout.Row((y << 1) + 1) + x0;
290
5.38M
      const pixel_type *p_pout = y > 0 ? chout.Row((y << 1) - 1) + x0 : p_avg;
291
5.38M
      size_t x = 0;
292
5.38M
#if HWY_TARGET != HWY_SCALAR
293
27.1M
      for (; x + 7 < w; x += 8) {
294
21.7M
        FastUnsqueeze(p_residual + x, p_avg + x, p_navg + x, p_pout + x,
295
21.7M
                      p_out + x, p_nout + x);
296
21.7M
      }
297
5.38M
#endif
298
18.7M
      for (; x < w; x++) {
299
13.3M
        pixel_type_w avg = p_avg[x];
300
13.3M
        pixel_type_w next_avg = p_navg[x];
301
13.3M
        pixel_type_w top = p_pout[x];
302
13.3M
        pixel_type_w tendency = SmoothTendency(top, avg, next_avg);
303
13.3M
        pixel_type_w diff_minus_tendency = p_residual[x];
304
13.3M
        pixel_type_w diff = diff_minus_tendency + tendency;
305
13.3M
        pixel_type_w out = avg + (diff / 2);
306
13.3M
        p_out[x] = out;
307
        // If the chin_residual.h == chin.h, the output has an even number
308
        // of rows so the next line is fine. Otherwise, this loop won't
309
        // write to the last output row which is handled separately.
310
13.3M
        p_nout[x] = out - diff;
311
13.3M
      }
312
5.38M
    }
313
205k
    return true;
314
205k
  };
Unexecuted instantiation: squeeze.cc:jxl::N_SSE4::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned int, unsigned long) const
squeeze.cc:jxl::N_AVX2::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned int, unsigned long) const
Line
Count
Source
276
205k
                                   size_t /* thread */) -> Status {
277
205k
    const size_t x0 = task * kColsPerThread;
278
205k
    const size_t x1 =
279
205k
        std::min(static_cast<size_t>(task + 1) * kColsPerThread, chin.w);
280
205k
    const size_t w = x1 - x0;
281
    // We only iterate up to std::min(chin_residual.h, chin.h) which is
282
    // always chin_residual.h.
283
5.59M
    for (size_t y = 0; y < chin_residual.h; y++) {
284
5.38M
      const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y) + x0;
285
5.38M
      const pixel_type *JXL_RESTRICT p_avg = chin.Row(y) + x0;
286
5.38M
      const pixel_type *JXL_RESTRICT p_navg =
287
5.38M
          chin.Row(y + 1 < chin.h ? y + 1 : y) + x0;
288
5.38M
      pixel_type *JXL_RESTRICT p_out = chout.Row(y << 1) + x0;
289
5.38M
      pixel_type *JXL_RESTRICT p_nout = chout.Row((y << 1) + 1) + x0;
290
5.38M
      const pixel_type *p_pout = y > 0 ? chout.Row((y << 1) - 1) + x0 : p_avg;
291
5.38M
      size_t x = 0;
292
5.38M
#if HWY_TARGET != HWY_SCALAR
293
27.1M
      for (; x + 7 < w; x += 8) {
294
21.7M
        FastUnsqueeze(p_residual + x, p_avg + x, p_navg + x, p_pout + x,
295
21.7M
                      p_out + x, p_nout + x);
296
21.7M
      }
297
5.38M
#endif
298
18.7M
      for (; x < w; x++) {
299
13.3M
        pixel_type_w avg = p_avg[x];
300
13.3M
        pixel_type_w next_avg = p_navg[x];
301
13.3M
        pixel_type_w top = p_pout[x];
302
13.3M
        pixel_type_w tendency = SmoothTendency(top, avg, next_avg);
303
13.3M
        pixel_type_w diff_minus_tendency = p_residual[x];
304
13.3M
        pixel_type_w diff = diff_minus_tendency + tendency;
305
13.3M
        pixel_type_w out = avg + (diff / 2);
306
13.3M
        p_out[x] = out;
307
        // If the chin_residual.h == chin.h, the output has an even number
308
        // of rows so the next line is fine. Otherwise, this loop won't
309
        // write to the last output row which is handled separately.
310
13.3M
        p_nout[x] = out - diff;
311
13.3M
      }
312
5.38M
    }
313
205k
    return true;
314
205k
  };
Unexecuted instantiation: squeeze.cc:jxl::N_AVX3::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned int, unsigned long) const
Unexecuted instantiation: squeeze.cc:jxl::N_AVX3_ZEN4::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned int, unsigned long) const
Unexecuted instantiation: squeeze.cc:jxl::N_AVX3_SPR::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned int, unsigned long) const
Unexecuted instantiation: squeeze.cc:jxl::N_SSE2::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)::$_0::operator()(unsigned int, unsigned long) const
315
193k
  JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, DivCeil(chin.w, kColsPerThread),
316
193k
                                ThreadPool::NoInit, unsqueeze_slice,
317
193k
                                "InvVertSqueeze"));
318
319
193k
  if (chout.h & 1) {
320
66.2k
    size_t y = chin.h - 1;
321
66.2k
    const pixel_type *p_avg = chin.Row(y);
322
66.2k
    pixel_type *p_out = chout.Row(y << 1);
323
1.73M
    for (size_t x = 0; x < chin.w; x++) {
324
1.66M
      p_out[x] = p_avg[x];
325
1.66M
    }
326
66.2k
  }
327
193k
  input.channel[c] = std::move(chout);
328
193k
  return true;
329
193k
}
Unexecuted instantiation: jxl::N_SSE4::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
jxl::N_AVX2::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
Line
Count
Source
241
220k
Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
242
220k
  JXL_ENSURE(c < input.channel.size());
243
220k
  JXL_ENSURE(rc < input.channel.size());
244
220k
  const Channel &chin = input.channel[c];
245
220k
  const Channel &chin_residual = input.channel[rc];
246
  // These must be valid since we ran MetaApply already.
247
220k
  JXL_ENSURE(chin.h == DivCeil(chin.h + chin_residual.h, 2));
248
220k
  JXL_ENSURE(chin.w == chin_residual.w);
249
220k
  JxlMemoryManager *memory_manager = input.memory_manager();
250
251
220k
  if (chin_residual.h == 0) {
252
    // Short-circuit: output channel has same dimensions as input.
253
27.8k
    input.channel[c].vshift--;
254
27.8k
    return true;
255
27.8k
  }
256
257
  // Note: chin.h >= chin_residual.h and at most 1 different.
258
386k
  JXL_ASSIGN_OR_RETURN(
259
386k
      Channel chout,
260
386k
      Channel::Create(memory_manager, chin.w, chin.h + chin_residual.h,
261
386k
                      chin.hshift, chin.vshift - 1));
262
386k
  JXL_DEBUG_V(
263
386k
      4,
264
386k
      "Undoing vertical squeeze of channel %i using residuals in channel "
265
386k
      "%i (going from height %" PRIuS " to %" PRIuS ")",
266
386k
      c, rc, chin.h, chout.h);
267
268
386k
  if (chin_residual.w == 0) {
269
    // Short-circuit: channel with no pixels.
270
0
    input.channel[c] = std::move(chout);
271
0
    return true;
272
0
  }
273
274
193k
  static constexpr const int kColsPerThread = 64;
275
193k
  const auto unsqueeze_slice = [&](const uint32_t task,
276
193k
                                   size_t /* thread */) -> Status {
277
193k
    const size_t x0 = task * kColsPerThread;
278
193k
    const size_t x1 =
279
193k
        std::min(static_cast<size_t>(task + 1) * kColsPerThread, chin.w);
280
193k
    const size_t w = x1 - x0;
281
    // We only iterate up to std::min(chin_residual.h, chin.h) which is
282
    // always chin_residual.h.
283
193k
    for (size_t y = 0; y < chin_residual.h; y++) {
284
193k
      const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y) + x0;
285
193k
      const pixel_type *JXL_RESTRICT p_avg = chin.Row(y) + x0;
286
193k
      const pixel_type *JXL_RESTRICT p_navg =
287
193k
          chin.Row(y + 1 < chin.h ? y + 1 : y) + x0;
288
193k
      pixel_type *JXL_RESTRICT p_out = chout.Row(y << 1) + x0;
289
193k
      pixel_type *JXL_RESTRICT p_nout = chout.Row((y << 1) + 1) + x0;
290
193k
      const pixel_type *p_pout = y > 0 ? chout.Row((y << 1) - 1) + x0 : p_avg;
291
193k
      size_t x = 0;
292
193k
#if HWY_TARGET != HWY_SCALAR
293
193k
      for (; x + 7 < w; x += 8) {
294
193k
        FastUnsqueeze(p_residual + x, p_avg + x, p_navg + x, p_pout + x,
295
193k
                      p_out + x, p_nout + x);
296
193k
      }
297
193k
#endif
298
193k
      for (; x < w; x++) {
299
193k
        pixel_type_w avg = p_avg[x];
300
193k
        pixel_type_w next_avg = p_navg[x];
301
193k
        pixel_type_w top = p_pout[x];
302
193k
        pixel_type_w tendency = SmoothTendency(top, avg, next_avg);
303
193k
        pixel_type_w diff_minus_tendency = p_residual[x];
304
193k
        pixel_type_w diff = diff_minus_tendency + tendency;
305
193k
        pixel_type_w out = avg + (diff / 2);
306
193k
        p_out[x] = out;
307
        // If the chin_residual.h == chin.h, the output has an even number
308
        // of rows so the next line is fine. Otherwise, this loop won't
309
        // write to the last output row which is handled separately.
310
193k
        p_nout[x] = out - diff;
311
193k
      }
312
193k
    }
313
193k
    return true;
314
193k
  };
315
193k
  JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, DivCeil(chin.w, kColsPerThread),
316
193k
                                ThreadPool::NoInit, unsqueeze_slice,
317
193k
                                "InvVertSqueeze"));
318
319
193k
  if (chout.h & 1) {
320
66.2k
    size_t y = chin.h - 1;
321
66.2k
    const pixel_type *p_avg = chin.Row(y);
322
66.2k
    pixel_type *p_out = chout.Row(y << 1);
323
1.73M
    for (size_t x = 0; x < chin.w; x++) {
324
1.66M
      p_out[x] = p_avg[x];
325
1.66M
    }
326
66.2k
  }
327
193k
  input.channel[c] = std::move(chout);
328
193k
  return true;
329
193k
}
Unexecuted instantiation: jxl::N_AVX3::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
Unexecuted instantiation: jxl::N_AVX3_ZEN4::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
Unexecuted instantiation: jxl::N_AVX3_SPR::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
Unexecuted instantiation: jxl::N_SSE2::InvVSqueeze(jxl::Image&, unsigned int, unsigned int, jxl::ThreadPool*)
330
331
Status InvSqueeze(Image &input, const std::vector<SqueezeParams> &parameters,
332
26.7k
                  ThreadPool *pool) {
333
167k
  for (int i = parameters.size() - 1; i >= 0; i--) {
334
140k
    JXL_RETURN_IF_ERROR(
335
140k
        CheckMetaSqueezeParams(parameters[i], input.channel.size()));
336
140k
    bool horizontal = parameters[i].horizontal;
337
140k
    bool in_place = parameters[i].in_place;
338
140k
    uint32_t beginc = parameters[i].begin_c;
339
140k
    uint32_t endc = parameters[i].begin_c + parameters[i].num_c - 1;
340
140k
    uint32_t offset;
341
140k
    if (in_place) {
342
78.4k
      offset = endc + 1;
343
78.4k
    } else {
344
62.1k
      offset = input.channel.size() + beginc - endc - 1;
345
62.1k
    }
346
140k
    if (beginc < input.nb_meta_channels) {
347
      // This is checked in MetaSqueeze.
348
4
      JXL_ENSURE(input.nb_meta_channels > parameters[i].num_c);
349
4
      input.nb_meta_channels -= parameters[i].num_c;
350
4
    }
351
352
541k
    for (uint32_t c = beginc; c <= endc; c++) {
353
400k
      uint32_t rc = offset + c - beginc;
354
      // MetaApply should imply that `rc` is within range, otherwise there's a
355
      // programming bug.
356
400k
      JXL_ENSURE(rc < input.channel.size());
357
400k
      if ((input.channel[c].w < input.channel[rc].w) ||
358
400k
          (input.channel[c].h < input.channel[rc].h)) {
359
0
        return JXL_FAILURE("Corrupted squeeze transform");
360
0
      }
361
400k
      if (horizontal) {
362
179k
        JXL_RETURN_IF_ERROR(InvHSqueeze(input, c, rc, pool));
363
220k
      } else {
364
220k
        JXL_RETURN_IF_ERROR(InvVSqueeze(input, c, rc, pool));
365
220k
      }
366
400k
    }
367
140k
    input.channel.erase(input.channel.begin() + offset,
368
140k
                        input.channel.begin() + offset + (endc - beginc + 1));
369
140k
  }
370
26.7k
  return true;
371
26.7k
}
Unexecuted instantiation: jxl::N_SSE4::InvSqueeze(jxl::Image&, std::__1::vector<jxl::SqueezeParams, std::__1::allocator<jxl::SqueezeParams> > const&, jxl::ThreadPool*)
jxl::N_AVX2::InvSqueeze(jxl::Image&, std::__1::vector<jxl::SqueezeParams, std::__1::allocator<jxl::SqueezeParams> > const&, jxl::ThreadPool*)
Line
Count
Source
332
26.7k
                  ThreadPool *pool) {
333
167k
  for (int i = parameters.size() - 1; i >= 0; i--) {
334
140k
    JXL_RETURN_IF_ERROR(
335
140k
        CheckMetaSqueezeParams(parameters[i], input.channel.size()));
336
140k
    bool horizontal = parameters[i].horizontal;
337
140k
    bool in_place = parameters[i].in_place;
338
140k
    uint32_t beginc = parameters[i].begin_c;
339
140k
    uint32_t endc = parameters[i].begin_c + parameters[i].num_c - 1;
340
140k
    uint32_t offset;
341
140k
    if (in_place) {
342
78.4k
      offset = endc + 1;
343
78.4k
    } else {
344
62.1k
      offset = input.channel.size() + beginc - endc - 1;
345
62.1k
    }
346
140k
    if (beginc < input.nb_meta_channels) {
347
      // This is checked in MetaSqueeze.
348
4
      JXL_ENSURE(input.nb_meta_channels > parameters[i].num_c);
349
4
      input.nb_meta_channels -= parameters[i].num_c;
350
4
    }
351
352
541k
    for (uint32_t c = beginc; c <= endc; c++) {
353
400k
      uint32_t rc = offset + c - beginc;
354
      // MetaApply should imply that `rc` is within range, otherwise there's a
355
      // programming bug.
356
400k
      JXL_ENSURE(rc < input.channel.size());
357
400k
      if ((input.channel[c].w < input.channel[rc].w) ||
358
400k
          (input.channel[c].h < input.channel[rc].h)) {
359
0
        return JXL_FAILURE("Corrupted squeeze transform");
360
0
      }
361
400k
      if (horizontal) {
362
179k
        JXL_RETURN_IF_ERROR(InvHSqueeze(input, c, rc, pool));
363
220k
      } else {
364
220k
        JXL_RETURN_IF_ERROR(InvVSqueeze(input, c, rc, pool));
365
220k
      }
366
400k
    }
367
140k
    input.channel.erase(input.channel.begin() + offset,
368
140k
                        input.channel.begin() + offset + (endc - beginc + 1));
369
140k
  }
370
26.7k
  return true;
371
26.7k
}
Unexecuted instantiation: jxl::N_AVX3::InvSqueeze(jxl::Image&, std::__1::vector<jxl::SqueezeParams, std::__1::allocator<jxl::SqueezeParams> > const&, jxl::ThreadPool*)
Unexecuted instantiation: jxl::N_AVX3_ZEN4::InvSqueeze(jxl::Image&, std::__1::vector<jxl::SqueezeParams, std::__1::allocator<jxl::SqueezeParams> > const&, jxl::ThreadPool*)
Unexecuted instantiation: jxl::N_AVX3_SPR::InvSqueeze(jxl::Image&, std::__1::vector<jxl::SqueezeParams, std::__1::allocator<jxl::SqueezeParams> > const&, jxl::ThreadPool*)
Unexecuted instantiation: jxl::N_SSE2::InvSqueeze(jxl::Image&, std::__1::vector<jxl::SqueezeParams, std::__1::allocator<jxl::SqueezeParams> > const&, jxl::ThreadPool*)
372
373
}  // namespace HWY_NAMESPACE
374
}  // namespace jxl
375
HWY_AFTER_NAMESPACE();
376
377
#if HWY_ONCE
378
379
namespace jxl {
380
381
HWY_EXPORT(InvSqueeze);
382
Status InvSqueeze(Image &input, const std::vector<SqueezeParams> &parameters,
383
26.7k
                  ThreadPool *pool) {
384
26.7k
  return HWY_DYNAMIC_DISPATCH(InvSqueeze)(input, parameters, pool);
385
26.7k
}
386
387
void DefaultSqueezeParameters(std::vector<SqueezeParams> *parameters,
388
25.2k
                              const Image &image) {
389
25.2k
  int nb_channels = image.channel.size() - image.nb_meta_channels;
390
391
25.2k
  parameters->clear();
392
25.2k
  size_t w = image.channel[image.nb_meta_channels].w;
393
25.2k
  size_t h = image.channel[image.nb_meta_channels].h;
394
25.2k
  JXL_DEBUG_V(
395
25.2k
      7, "Default squeeze parameters for %" PRIuS "x%" PRIuS " image: ", w, h);
396
397
  // do horizontal first on wide images; vertical first on tall images
398
25.2k
  bool wide = (w > h);
399
400
25.2k
  if (nb_channels > 2 && image.channel[image.nb_meta_channels + 1].w == w &&
401
18.1k
      image.channel[image.nb_meta_channels + 1].h == h) {
402
    // assume channels 1 and 2 are chroma, and can be squeezed first for 4:2:0
403
    // previews
404
17.8k
    JXL_DEBUG_V(7, "(4:2:0 chroma), %" PRIuS "x%" PRIuS " image", w, h);
405
17.8k
    SqueezeParams params;
406
    // horizontal chroma squeeze
407
17.8k
    params.horizontal = true;
408
17.8k
    params.in_place = false;
409
17.8k
    params.begin_c = image.nb_meta_channels + 1;
410
17.8k
    params.num_c = 2;
411
17.8k
    parameters->push_back(params);
412
17.8k
    params.horizontal = false;
413
    // vertical chroma squeeze
414
17.8k
    parameters->push_back(params);
415
17.8k
  }
416
25.2k
  SqueezeParams params;
417
25.2k
  params.begin_c = image.nb_meta_channels;
418
25.2k
  params.num_c = nb_channels;
419
25.2k
  params.in_place = true;
420
421
25.2k
  if (!wide) {
422
19.6k
    if (h > kMaxFirstPreviewSize) {
423
10.7k
      params.horizontal = false;
424
10.7k
      parameters->push_back(params);
425
10.7k
      h = (h + 1) / 2;
426
10.7k
      JXL_DEBUG_V(7, "Vertical (%" PRIuS "x%" PRIuS "), ", w, h);
427
10.7k
    }
428
19.6k
  }
429
66.2k
  while (w > kMaxFirstPreviewSize || h > kMaxFirstPreviewSize) {
430
40.9k
    if (w > kMaxFirstPreviewSize) {
431
37.4k
      params.horizontal = true;
432
37.4k
      parameters->push_back(params);
433
37.4k
      w = (w + 1) / 2;
434
37.4k
      JXL_DEBUG_V(7, "Horizontal (%" PRIuS "x%" PRIuS "), ", w, h);
435
37.4k
    }
436
40.9k
    if (h > kMaxFirstPreviewSize) {
437
33.7k
      params.horizontal = false;
438
33.7k
      parameters->push_back(params);
439
33.7k
      h = (h + 1) / 2;
440
33.7k
      JXL_DEBUG_V(7, "Vertical (%" PRIuS "x%" PRIuS "), ", w, h);
441
33.7k
    }
442
40.9k
  }
443
25.2k
  JXL_DEBUG_V(7, "that's it");
444
25.2k
}
445
446
Status CheckMetaSqueezeParams(const SqueezeParams &parameter,
447
293k
                              int num_channels) {
448
293k
  int c1 = parameter.begin_c;
449
293k
  int c2 = parameter.begin_c + parameter.num_c - 1;
450
293k
  if (c1 < 0 || c1 >= num_channels || c2 < 0 || c2 >= num_channels || c2 < c1) {
451
418
    return JXL_FAILURE("Invalid channel range");
452
418
  }
453
293k
  return true;
454
293k
}
455
456
32.4k
Status MetaSqueeze(Image &image, std::vector<SqueezeParams> *parameters) {
457
32.4k
  JxlMemoryManager *memory_manager = image.memory_manager();
458
32.4k
  if (parameters->empty()) {
459
25.2k
    DefaultSqueezeParameters(parameters, image);
460
25.2k
  }
461
462
152k
  for (auto &parameter : *parameters) {
463
152k
    JXL_RETURN_IF_ERROR(
464
152k
        CheckMetaSqueezeParams(parameter, image.channel.size()));
465
152k
    bool horizontal = parameter.horizontal;
466
152k
    bool in_place = parameter.in_place;
467
152k
    uint32_t beginc = parameter.begin_c;
468
152k
    uint32_t endc = parameter.begin_c + parameter.num_c - 1;
469
470
152k
    uint32_t offset;
471
152k
    if (beginc < image.nb_meta_channels) {
472
41
      if (endc >= image.nb_meta_channels) {
473
5
        return JXL_FAILURE("Invalid squeeze: mix of meta and nonmeta channels");
474
5
      }
475
36
      if (!in_place) {
476
11
        return JXL_FAILURE(
477
11
            "Invalid squeeze: meta channels require in-place residuals");
478
11
      }
479
25
      image.nb_meta_channels += parameter.num_c;
480
25
    }
481
152k
    if (in_place) {
482
82.3k
      offset = endc + 1;
483
82.3k
    } else {
484
70.2k
      offset = image.channel.size();
485
70.2k
    }
486
575k
    for (uint32_t c = beginc; c <= endc; c++) {
487
423k
      if (image.channel[c].hshift > 30 || image.channel[c].vshift > 30) {
488
5
        return JXL_FAILURE("Too many squeezes: shift > 30");
489
5
      }
490
423k
      size_t w = image.channel[c].w;
491
423k
      size_t h = image.channel[c].h;
492
423k
      if (w == 0 || h == 0) return JXL_FAILURE("Squeezing empty channel");
493
423k
      if (horizontal) {
494
188k
        image.channel[c].w = (w + 1) / 2;
495
188k
        if (image.channel[c].hshift >= 0) image.channel[c].hshift++;
496
188k
        w = w - (w + 1) / 2;
497
234k
      } else {
498
234k
        image.channel[c].h = (h + 1) / 2;
499
234k
        if (image.channel[c].vshift >= 0) image.channel[c].vshift++;
500
234k
        h = h - (h + 1) / 2;
501
234k
      }
502
423k
      JXL_RETURN_IF_ERROR(image.channel[c].shrink());
503
846k
      JXL_ASSIGN_OR_RETURN(Channel placeholder,
504
846k
                           Channel::Create(memory_manager, w, h));
505
846k
      placeholder.hshift = image.channel[c].hshift;
506
846k
      placeholder.vshift = image.channel[c].vshift;
507
846k
      placeholder.component = image.channel[c].component;
508
846k
      image.channel.insert(image.channel.begin() + offset + (c - beginc),
509
846k
                           std::move(placeholder));
510
846k
      JXL_DEBUG_V(0, "MetaSqueeze applied, current image: %s",
511
846k
                  image.DebugString().c_str());
512
846k
    }
513
152k
  }
514
31.8k
  return true;
515
32.4k
}
516
517
}  // namespace jxl
518
519
#endif