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_cache.h"
7 
8 #include "lib/jxl/dec_reconstruct.h"
9 
10 namespace jxl {
11 
EnsureBordersStorage()12 void PassesDecoderState::EnsureBordersStorage() {
13   if (!EagerFinalizeImageRect()) return;
14   size_t padding = FinalizeRectPadding();
15   size_t bordery = 2 * padding;
16   size_t borderx = padding + group_border_assigner.PaddingX(padding);
17   Rect horizontal = Rect(0, 0, shared->frame_dim.xsize_padded,
18                          bordery * shared->frame_dim.ysize_groups * 2);
19   if (!SameSize(horizontal, borders_horizontal)) {
20     borders_horizontal = Image3F(horizontal.xsize(), horizontal.ysize());
21   }
22   Rect vertical = Rect(0, 0, borderx * shared->frame_dim.xsize_groups * 2,
23                        shared->frame_dim.ysize_padded);
24   if (!SameSize(vertical, borders_vertical)) {
25     borders_vertical = Image3F(vertical.xsize(), vertical.ysize());
26   }
27 }
28 
29 namespace {
SaveBorders(const Rect & block_rect,size_t hshift,size_t vshift,size_t padding,const ImageF & plane_in,ImageF * border_storage_h,ImageF * border_storage_v)30 void SaveBorders(const Rect& block_rect, size_t hshift, size_t vshift,
31                  size_t padding, const ImageF& plane_in,
32                  ImageF* border_storage_h, ImageF* border_storage_v) {
33   constexpr size_t kGroupDataXBorder = PassesDecoderState::kGroupDataXBorder;
34   constexpr size_t kGroupDataYBorder = PassesDecoderState::kGroupDataYBorder;
35   size_t x0 = DivCeil(block_rect.x0() * kBlockDim, 1 << hshift);
36   size_t x1 =
37       DivCeil((block_rect.x0() + block_rect.xsize()) * kBlockDim, 1 << hshift);
38   size_t y0 = DivCeil(block_rect.y0() * kBlockDim, 1 << vshift);
39   size_t y1 =
40       DivCeil((block_rect.y0() + block_rect.ysize()) * kBlockDim, 1 << vshift);
41   size_t gy = block_rect.y0() / kGroupDimInBlocks;
42   size_t gx = block_rect.x0() / kGroupDimInBlocks;
43   // TODO(veluca): this is too much with chroma upsampling. It's just
44   // inefficient though.
45   size_t borderx = GroupBorderAssigner::PaddingX(padding);
46   size_t bordery = padding;
47   size_t borderx_write = padding + borderx;
48   size_t bordery_write = padding + bordery;
49   CopyImageTo(
50       Rect(kGroupDataXBorder, kGroupDataYBorder, x1 - x0, bordery_write),
51       plane_in, Rect(x0, (gy * 2) * bordery_write, x1 - x0, bordery_write),
52       border_storage_h);
53   CopyImageTo(
54       Rect(kGroupDataXBorder, kGroupDataYBorder + y1 - y0 - bordery_write,
55            x1 - x0, bordery_write),
56       plane_in, Rect(x0, (gy * 2 + 1) * bordery_write, x1 - x0, bordery_write),
57       border_storage_h);
58   CopyImageTo(
59       Rect(kGroupDataXBorder, kGroupDataYBorder, borderx_write, y1 - y0),
60       plane_in, Rect((gx * 2) * borderx_write, y0, borderx_write, y1 - y0),
61       border_storage_v);
62   CopyImageTo(Rect(kGroupDataXBorder + x1 - x0 - borderx_write,
63                    kGroupDataYBorder, borderx_write, y1 - y0),
64               plane_in,
65               Rect((gx * 2 + 1) * borderx_write, y0, borderx_write, y1 - y0),
66               border_storage_v);
67 }
68 
LoadBorders(const Rect & block_rect,size_t hshift,size_t vshift,const FrameDimensions & frame_dim,size_t padding,const ImageF & border_storage_h,const ImageF & border_storage_v,const Rect & r,ImageF * plane_out)69 void LoadBorders(const Rect& block_rect, size_t hshift, size_t vshift,
70                  const FrameDimensions& frame_dim, size_t padding,
71                  const ImageF& border_storage_h, const ImageF& border_storage_v,
72                  const Rect& r, ImageF* plane_out) {
73   constexpr size_t kGroupDataXBorder = PassesDecoderState::kGroupDataXBorder;
74   constexpr size_t kGroupDataYBorder = PassesDecoderState::kGroupDataYBorder;
75   size_t x0 = DivCeil(block_rect.x0() * kBlockDim, 1 << hshift);
76   size_t x1 =
77       DivCeil((block_rect.x0() + block_rect.xsize()) * kBlockDim, 1 << hshift);
78   size_t y0 = DivCeil(block_rect.y0() * kBlockDim, 1 << vshift);
79   size_t y1 =
80       DivCeil((block_rect.y0() + block_rect.ysize()) * kBlockDim, 1 << vshift);
81   size_t gy = block_rect.y0() / kGroupDimInBlocks;
82   size_t gx = block_rect.x0() / kGroupDimInBlocks;
83   size_t borderx = GroupBorderAssigner::PaddingX(padding);
84   size_t bordery = padding;
85   size_t borderx_write = padding + borderx;
86   size_t bordery_write = padding + bordery;
87   // Limits of the area to copy from, in image coordinates.
88   JXL_DASSERT(r.x0() == 0 || r.x0() >= borderx);
89   size_t x0src = DivCeil(r.x0() == 0 ? r.x0() : r.x0() - borderx, 1 << hshift);
90   // r may be such that r.x1 (namely x0() + xsize()) is within borderx of the
91   // right side of the image, so we use min() here.
92   size_t x1src =
93       DivCeil(std::min(r.x0() + r.xsize() + borderx, frame_dim.xsize_padded),
94               1 << hshift);
95   JXL_DASSERT(r.y0() == 0 || r.y0() >= bordery);
96   size_t y0src = DivCeil(r.y0() == 0 ? r.y0() : r.y0() - bordery, 1 << vshift);
97   // Similar to x1, y1 might be closer than bordery from the bottom.
98   size_t y1src =
99       DivCeil(std::min(r.y0() + r.ysize() + bordery, frame_dim.ysize_padded),
100               1 << vshift);
101   // Copy other groups' borders from the border storage.
102   if (y0src < y0) {
103     JXL_DASSERT(gy > 0);
104     CopyImageTo(
105         Rect(x0src, (gy * 2 - 1) * bordery_write, x1src - x0src, bordery_write),
106         border_storage_h,
107         Rect(kGroupDataXBorder + x0src - x0, kGroupDataYBorder - bordery_write,
108              x1src - x0src, bordery_write),
109         plane_out);
110   }
111   if (y1src > y1) {
112     // When copying the bottom border we must not be on the bottom groups.
113     JXL_DASSERT(gy + 1 < frame_dim.ysize_groups);
114     CopyImageTo(
115         Rect(x0src, (gy * 2 + 2) * bordery_write, x1src - x0src, bordery_write),
116         border_storage_h,
117         Rect(kGroupDataXBorder + x0src - x0, kGroupDataYBorder + y1 - y0,
118              x1src - x0src, bordery_write),
119         plane_out);
120   }
121   if (x0src < x0) {
122     JXL_DASSERT(gx > 0);
123     CopyImageTo(
124         Rect((gx * 2 - 1) * borderx_write, y0src, borderx_write, y1src - y0src),
125         border_storage_v,
126         Rect(kGroupDataXBorder - borderx_write, kGroupDataYBorder + y0src - y0,
127              borderx_write, y1src - y0src),
128         plane_out);
129   }
130   if (x1src > x1) {
131     // When copying the right border we must not be on the rightmost groups.
132     JXL_DASSERT(gx + 1 < frame_dim.xsize_groups);
133     CopyImageTo(
134         Rect((gx * 2 + 2) * borderx_write, y0src, borderx_write, y1src - y0src),
135         border_storage_v,
136         Rect(kGroupDataXBorder + x1 - x0, kGroupDataYBorder + y0src - y0,
137              borderx_write, y1src - y0src),
138         plane_out);
139   }
140 }
141 
142 }  // namespace
143 
FinalizeGroup(size_t group_idx,size_t thread,Image3F * pixel_data,ImageBundle * output)144 Status PassesDecoderState::FinalizeGroup(size_t group_idx, size_t thread,
145                                          Image3F* pixel_data,
146                                          ImageBundle* output) {
147   // Copy the group borders to the border storage.
148   const Rect block_rect = shared->BlockGroupRect(group_idx);
149   const YCbCrChromaSubsampling& cs = shared->frame_header.chroma_subsampling;
150   size_t padding = FinalizeRectPadding();
151   for (size_t c = 0; c < 3; c++) {
152     SaveBorders(block_rect, cs.HShift(c), cs.VShift(c), padding,
153                 pixel_data->Plane(c), &borders_horizontal.Plane(c),
154                 &borders_vertical.Plane(c));
155   }
156   Rect fir_rects[GroupBorderAssigner::kMaxToFinalize];
157   size_t num_fir_rects = 0;
158   group_border_assigner.GroupDone(group_idx, FinalizeRectPadding(), fir_rects,
159                                   &num_fir_rects);
160   for (size_t i = 0; i < num_fir_rects; i++) {
161     const Rect& r = fir_rects[i];
162     for (size_t c = 0; c < 3; c++) {
163       LoadBorders(block_rect, cs.HShift(c), cs.VShift(c), shared->frame_dim,
164                   padding, borders_horizontal.Plane(c),
165                   borders_vertical.Plane(c), r, &pixel_data->Plane(c));
166     }
167     Rect pixel_data_rect(
168         kGroupDataXBorder + r.x0() - block_rect.x0() * kBlockDim,
169         kGroupDataYBorder + r.y0() - block_rect.y0() * kBlockDim, r.xsize(),
170         r.ysize());
171     JXL_RETURN_IF_ERROR(FinalizeImageRect(pixel_data, pixel_data_rect, {}, this,
172                                           thread, output, r));
173   }
174   return true;
175 }
176 
177 }  // namespace jxl
178