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/progressive_split.h"
7
8 #include <string.h>
9
10 #include <algorithm>
11 #include <memory>
12
13 #include "lib/jxl/common.h"
14 #include "lib/jxl/image.h"
15
16 namespace jxl {
17
SuperblockIsSalient(size_t row_start,size_t col_start,size_t num_rows,size_t num_cols) const18 bool ProgressiveSplitter::SuperblockIsSalient(size_t row_start,
19 size_t col_start, size_t num_rows,
20 size_t num_cols) const {
21 if (saliency_map_ == nullptr || saliency_map_->xsize() == 0 ||
22 saliency_threshold_ == 0.0) {
23 // If we do not have a saliency-map, or the threshold says to include
24 // every block, we straightaway classify the superblock as 'salient'.
25 return true;
26 }
27 const size_t row_end = std::min(saliency_map_->ysize(), row_start + num_rows);
28 const size_t col_end = std::min(saliency_map_->xsize(), col_start + num_cols);
29 for (size_t num_row = row_start; num_row < row_end; num_row++) {
30 const float* JXL_RESTRICT map_row = saliency_map_->ConstRow(num_row);
31 for (size_t num_col = col_start; num_col < col_end; num_col++) {
32 if (map_row[num_col] >= saliency_threshold_) {
33 // One of the blocks covered by this superblock is above the saliency
34 // threshold.
35 return true;
36 }
37 }
38 }
39 // We did not see any block above the saliency threshold.
40 return false;
41 }
42
43 template <typename T>
SplitACCoefficients(const T * JXL_RESTRICT block,size_t size,const AcStrategy & acs,size_t bx,size_t by,size_t offset,T * JXL_RESTRICT output[kMaxNumPasses][3])44 void ProgressiveSplitter::SplitACCoefficients(
45 const T* JXL_RESTRICT block, size_t size, const AcStrategy& acs, size_t bx,
46 size_t by, size_t offset, T* JXL_RESTRICT output[kMaxNumPasses][3]) {
47 auto shift_right_round0 = [&](T v, int shift) {
48 T one_if_negative = static_cast<uint32_t>(v) >> 31;
49 T add = (one_if_negative << shift) - one_if_negative;
50 return (v + add) >> shift;
51 };
52 // Early quit for the simple case of only one pass.
53 if (mode_.num_passes == 1) {
54 for (size_t c = 0; c < 3; c++) {
55 memcpy(output[0][c] + offset, block + c * size, sizeof(T) * size);
56 }
57 return;
58 }
59 size_t ncoeffs_all_done_from_earlier_passes = 1;
60 size_t previous_pass_salient_only = false;
61
62 int previous_pass_shift = 0;
63 for (size_t num_pass = 0; num_pass < mode_.num_passes; num_pass++) { // pass
64 // Zero out output block.
65 for (size_t c = 0; c < 3; c++) {
66 memset(output[num_pass][c] + offset, 0, size * sizeof(T));
67 }
68 const bool current_pass_salient_only = mode_.passes[num_pass].salient_only;
69 const int pass_shift = mode_.passes[num_pass].shift;
70 size_t frame_ncoeffs = mode_.passes[num_pass].num_coefficients;
71 for (size_t c = 0; c < 3; c++) { // color-channel
72 size_t xsize = acs.covered_blocks_x();
73 size_t ysize = acs.covered_blocks_y();
74 CoefficientLayout(&ysize, &xsize);
75 if (current_pass_salient_only || previous_pass_salient_only) {
76 // Current or previous pass is salient-only.
77 const bool superblock_is_salient =
78 SuperblockIsSalient(by, bx, ysize, xsize);
79 if (current_pass_salient_only != superblock_is_salient) {
80 // Current pass is salient-only, but block is not salient,
81 // OR last pass was salient-only, and block is salient
82 // (hence was already included in last pass).
83 continue;
84 }
85 }
86 for (size_t y = 0; y < ysize * frame_ncoeffs; y++) { // superblk-y
87 for (size_t x = 0; x < xsize * frame_ncoeffs; x++) { // superblk-x
88 size_t pos = y * xsize * kBlockDim + x;
89 if (x < xsize * ncoeffs_all_done_from_earlier_passes &&
90 y < ysize * ncoeffs_all_done_from_earlier_passes) {
91 // This coefficient was already included in an earlier pass,
92 // which included a genuinely smaller set of coefficients
93 // (= is not about saliency-splitting).
94 continue;
95 }
96 T v = block[c * size + pos];
97 // Previous pass discarded some bits: do not encode them again.
98 if (previous_pass_shift != 0) {
99 T previous_v = shift_right_round0(v, previous_pass_shift) *
100 (1 << previous_pass_shift);
101 v -= previous_v;
102 }
103 output[num_pass][c][offset + pos] = shift_right_round0(v, pass_shift);
104 } // superblk-x
105 } // superblk-y
106 } // color-channel
107 if (!current_pass_salient_only) {
108 // We just finished a non-salient pass.
109 // Hence, we are now guaranteed to have included all coeffs up to
110 // frame_ncoeffs in every block, unless the current pass is shifted.
111 if (mode_.passes[num_pass].shift == 0) {
112 ncoeffs_all_done_from_earlier_passes = frame_ncoeffs;
113 }
114 }
115 previous_pass_salient_only = current_pass_salient_only;
116 previous_pass_shift = mode_.passes[num_pass].shift;
117 } // num_pass
118 }
119
120 template void ProgressiveSplitter::SplitACCoefficients<int32_t>(
121 const int32_t* JXL_RESTRICT, size_t, const AcStrategy&, size_t, size_t,
122 size_t, int32_t* JXL_RESTRICT[kMaxNumPasses][3]);
123
124 template void ProgressiveSplitter::SplitACCoefficients<int16_t>(
125 const int16_t* JXL_RESTRICT, size_t, const AcStrategy&, size_t, size_t,
126 size_t, int16_t* JXL_RESTRICT[kMaxNumPasses][3]);
127
128 } // namespace jxl
129