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