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/enc_squeeze.h"
7 
8 #include <stdlib.h>
9 
10 #include "lib/jxl/base/data_parallel.h"
11 #include "lib/jxl/common.h"
12 #include "lib/jxl/modular/modular_image.h"
13 #include "lib/jxl/modular/transform/squeeze.h"
14 #include "lib/jxl/modular/transform/transform.h"
15 
16 namespace jxl {
17 
FwdHSqueeze(Image & input,int c,int rc)18 void FwdHSqueeze(Image &input, int c, int rc) {
19   const Channel &chin = input.channel[c];
20 
21   JXL_DEBUG_V(4, "Doing horizontal squeeze of channel %i to new channel %i", c,
22               rc);
23 
24   Channel chout((chin.w + 1) / 2, chin.h, chin.hshift + 1, chin.vshift);
25   Channel chout_residual(chin.w - chout.w, chout.h, chin.hshift + 1,
26                          chin.vshift);
27 
28   for (size_t y = 0; y < chout.h; y++) {
29     const pixel_type *JXL_RESTRICT p_in = chin.Row(y);
30     pixel_type *JXL_RESTRICT p_out = chout.Row(y);
31     pixel_type *JXL_RESTRICT p_res = chout_residual.Row(y);
32     for (size_t x = 0; x < chout_residual.w; x++) {
33       pixel_type A = p_in[x * 2];
34       pixel_type B = p_in[x * 2 + 1];
35       pixel_type avg = (A + B + (A > B)) >> 1;
36       p_out[x] = avg;
37 
38       pixel_type diff = A - B;
39 
40       pixel_type next_avg = avg;
41       if (x + 1 < chout_residual.w) {
42         next_avg = (p_in[x * 2 + 2] + p_in[x * 2 + 3] +
43                     (p_in[x * 2 + 2] > p_in[x * 2 + 3])) >>
44                    1;  // which will be chout.value(y,x+1)
45       } else if (chin.w & 1)
46         next_avg = p_in[x * 2 + 2];
47       pixel_type left = (x > 0 ? p_in[x * 2 - 1] : avg);
48       pixel_type tendency = SmoothTendency(left, avg, next_avg);
49 
50       p_res[x] = diff - tendency;
51     }
52     if (chin.w & 1) {
53       int x = chout.w - 1;
54       p_out[x] = p_in[x * 2];
55     }
56   }
57   input.channel[c] = std::move(chout);
58   input.channel.insert(input.channel.begin() + rc, std::move(chout_residual));
59 }
60 
FwdVSqueeze(Image & input,int c,int rc)61 void FwdVSqueeze(Image &input, int c, int rc) {
62   const Channel &chin = input.channel[c];
63 
64   JXL_DEBUG_V(4, "Doing vertical squeeze of channel %i to new channel %i", c,
65               rc);
66 
67   Channel chout(chin.w, (chin.h + 1) / 2, chin.hshift, chin.vshift + 1);
68   Channel chout_residual(chin.w, chin.h - chout.h, chin.hshift,
69                          chin.vshift + 1);
70   intptr_t onerow_in = chin.plane.PixelsPerRow();
71   for (size_t y = 0; y < chout_residual.h; y++) {
72     const pixel_type *JXL_RESTRICT p_in = chin.Row(y * 2);
73     pixel_type *JXL_RESTRICT p_out = chout.Row(y);
74     pixel_type *JXL_RESTRICT p_res = chout_residual.Row(y);
75     for (size_t x = 0; x < chout.w; x++) {
76       pixel_type A = p_in[x];
77       pixel_type B = p_in[x + onerow_in];
78       pixel_type avg = (A + B + (A > B)) >> 1;
79       p_out[x] = avg;
80 
81       pixel_type diff = A - B;
82 
83       pixel_type next_avg = avg;
84       if (y + 1 < chout_residual.h) {
85         next_avg = (p_in[x + 2 * onerow_in] + p_in[x + 3 * onerow_in] +
86                     (p_in[x + 2 * onerow_in] > p_in[x + 3 * onerow_in])) >>
87                    1;  // which will be chout.value(y+1,x)
88       } else if (chin.h & 1) {
89         next_avg = p_in[x + 2 * onerow_in];
90       }
91       pixel_type top =
92           (y > 0 ? p_in[static_cast<ssize_t>(x) - onerow_in] : avg);
93       pixel_type tendency = SmoothTendency(top, avg, next_avg);
94 
95       p_res[x] = diff - tendency;
96     }
97   }
98   if (chin.h & 1) {
99     size_t y = chout.h - 1;
100     const pixel_type *p_in = chin.Row(y * 2);
101     pixel_type *p_out = chout.Row(y);
102     for (size_t x = 0; x < chout.w; x++) {
103       p_out[x] = p_in[x];
104     }
105   }
106   input.channel[c] = std::move(chout);
107   input.channel.insert(input.channel.begin() + rc, std::move(chout_residual));
108 }
109 
FwdSqueeze(Image & input,std::vector<SqueezeParams> parameters,ThreadPool * pool)110 Status FwdSqueeze(Image &input, std::vector<SqueezeParams> parameters,
111                   ThreadPool *pool) {
112   if (parameters.empty()) {
113     DefaultSqueezeParameters(&parameters, input);
114   }
115   // if nothing to do, don't do squeeze
116   if (parameters.empty()) return false;
117   for (size_t i = 0; i < parameters.size(); i++) {
118     JXL_RETURN_IF_ERROR(
119         CheckMetaSqueezeParams(parameters[i], input.channel.size()));
120     bool horizontal = parameters[i].horizontal;
121     bool in_place = parameters[i].in_place;
122     uint32_t beginc = parameters[i].begin_c;
123     uint32_t endc = parameters[i].begin_c + parameters[i].num_c - 1;
124     uint32_t offset;
125     if (in_place) {
126       offset = endc + 1;
127     } else {
128       offset = input.channel.size();
129     }
130     for (uint32_t c = beginc; c <= endc; c++) {
131       if (horizontal) {
132         FwdHSqueeze(input, c, offset + c - beginc);
133       } else {
134         FwdVSqueeze(input, c, offset + c - beginc);
135       }
136     }
137   }
138   return true;
139 }
140 
141 }  // namespace jxl
142