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(¶meters, 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