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