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