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/dec_upsample.h"
7
8 #include "lib/jxl/image_ops.h"
9
10 namespace jxl {
11 namespace {
12
13 template <size_t N>
InitKernel(const float * weights,float kernel[4][4][5][5])14 void InitKernel(const float* weights, float kernel[4][4][5][5]) {
15 static_assert(N == 1 || N == 2 || N == 4,
16 "Upsampling kernel init only implemented for N = 1,2,4");
17 for (size_t i = 0; i < 5 * N; i++) {
18 for (size_t j = 0; j < 5 * N; j++) {
19 size_t y = std::min(i, j);
20 size_t x = std::max(i, j);
21 kernel[j / 5][i / 5][j % 5][i % 5] =
22 weights[5 * N * y - y * (y - 1) / 2 + x - y];
23 }
24 }
25 }
26
27 template <size_t N>
Kernel(size_t x,size_t y,size_t ix,size_t iy,const float kernel[4][4][5][5])28 float Kernel(size_t x, size_t y, size_t ix, size_t iy,
29 const float kernel[4][4][5][5]) {
30 if (N == 2) {
31 return kernel[0][0][y % 2 ? 4 - iy : iy][x % 2 ? 4 - ix : ix];
32 }
33 if (N == 4) {
34 return kernel[y % 4 < 2 ? y % 2 : 1 - y % 2][x % 4 < 2 ? x % 2 : 1 - x % 2]
35 [y % 4 < 2 ? iy : 4 - iy][x % 4 < 2 ? ix : 4 - ix];
36 }
37 if (N == 8) {
38 return kernel[y % 8 < 4 ? y % 4 : 3 - y % 4][x % 8 < 4 ? x % 4 : 3 - x % 4]
39 [y % 8 < 4 ? iy : 4 - iy][x % 8 < 4 ? ix : 4 - ix];
40 }
41 JXL_ABORT("Invalid upsample");
42 }
43
44 template <int N>
Upsample(const ImageF & src,const Rect & src_rect,ImageF * dst,const Rect & dst_rect,const float kernel[4][4][5][5],ssize_t image_y_offset,size_t image_ysize)45 void Upsample(const ImageF& src, const Rect& src_rect, ImageF* dst,
46 const Rect& dst_rect, const float kernel[4][4][5][5],
47 ssize_t image_y_offset, size_t image_ysize) {
48 JXL_DASSERT(src_rect.x0() >= 2);
49 JXL_DASSERT(src_rect.x0() + src_rect.xsize() + 2 <= src.xsize());
50 for (size_t y = 0; y < dst_rect.ysize(); y++) {
51 float* dst_row = dst_rect.Row(dst, y);
52 const float* src_rows[5];
53 for (int iy = -2; iy <= 2; iy++) {
54 ssize_t image_y =
55 static_cast<ssize_t>(y / N + src_rect.y0() + iy) + image_y_offset;
56 src_rows[iy + 2] = src.Row(Mirror(image_y, image_ysize) - image_y_offset);
57 }
58 for (size_t x = 0; x < dst_rect.xsize(); x++) {
59 size_t xbase = x / N + src_rect.x0() - 2;
60 float result = 0;
61 float min = src_rows[0][xbase];
62 float max = src_rows[0][xbase];
63 for (size_t iy = 0; iy < 5; iy++) {
64 for (size_t ix = 0; ix < 5; ix++) {
65 float v = src_rows[iy][xbase + ix];
66 result += Kernel<N>(x, y, ix, iy, kernel) * v;
67 min = std::min(v, min);
68 max = std::max(v, max);
69 }
70 }
71 // Avoid overshooting.
72 dst_row[x] = std::min(std::max(result, min), max);
73 }
74 }
75 }
76 } // namespace
77
Init(size_t upsampling,const CustomTransformData & data)78 void Upsampler::Init(size_t upsampling, const CustomTransformData& data) {
79 upsampling_ = upsampling;
80 if (upsampling_ == 1) return;
81 if (upsampling_ == 2) {
82 InitKernel<1>(data.upsampling2_weights, kernel_);
83 } else if (upsampling_ == 4) {
84 InitKernel<2>(data.upsampling4_weights, kernel_);
85 } else if (upsampling_ == 8) {
86 InitKernel<4>(data.upsampling8_weights, kernel_);
87 } else {
88 JXL_ABORT("Invalid upsample");
89 }
90 }
91
UpsampleRect(const ImageF & src,const Rect & src_rect,ImageF * dst,const Rect & dst_rect,ssize_t image_y_offset,size_t image_ysize) const92 void Upsampler::UpsampleRect(const ImageF& src, const Rect& src_rect,
93 ImageF* dst, const Rect& dst_rect,
94 ssize_t image_y_offset, size_t image_ysize) const {
95 if (upsampling_ == 1) return;
96 JXL_ASSERT(DivCeil(dst_rect.xsize(), upsampling_) <= src_rect.xsize());
97 // TODO(eustas): add proper (src|dst) ysize check that accounts for mirroring.
98 if (upsampling_ == 2) {
99 Upsample<2>(src, src_rect, dst, dst_rect, kernel_, image_y_offset,
100 image_ysize);
101 } else if (upsampling_ == 4) {
102 Upsample<4>(src, src_rect, dst, dst_rect, kernel_, image_y_offset,
103 image_ysize);
104 } else if (upsampling_ == 8) {
105 Upsample<8>(src, src_rect, dst, dst_rect, kernel_, image_y_offset,
106 image_ysize);
107 } else {
108 JXL_ABORT("Not implemented");
109 }
110 }
111
UpsampleRect(const Image3F & src,const Rect & src_rect,Image3F * dst,const Rect & dst_rect,ssize_t image_y_offset,size_t image_ysize) const112 void Upsampler::UpsampleRect(const Image3F& src, const Rect& src_rect,
113 Image3F* dst, const Rect& dst_rect,
114 ssize_t image_y_offset, size_t image_ysize) const {
115 for (size_t c = 0; c < 3; c++) {
116 UpsampleRect(src.Plane(c), src_rect, &dst->Plane(c), dst_rect,
117 image_y_offset, image_ysize);
118 }
119 }
120
121 } // namespace jxl
122