// Copyright (c) the JPEG XL Project Authors. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "lib/jxl/dec_cache.h" #include "lib/jxl/dec_reconstruct.h" namespace jxl { void PassesDecoderState::EnsureBordersStorage() { if (!EagerFinalizeImageRect()) return; size_t padding = FinalizeRectPadding(); size_t bordery = 2 * padding; size_t borderx = padding + group_border_assigner.PaddingX(padding); Rect horizontal = Rect(0, 0, shared->frame_dim.xsize_padded, bordery * shared->frame_dim.ysize_groups * 2); if (!SameSize(horizontal, borders_horizontal)) { borders_horizontal = Image3F(horizontal.xsize(), horizontal.ysize()); } Rect vertical = Rect(0, 0, borderx * shared->frame_dim.xsize_groups * 2, shared->frame_dim.ysize_padded); if (!SameSize(vertical, borders_vertical)) { borders_vertical = Image3F(vertical.xsize(), vertical.ysize()); } } namespace { void 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) { constexpr size_t kGroupDataXBorder = PassesDecoderState::kGroupDataXBorder; constexpr size_t kGroupDataYBorder = PassesDecoderState::kGroupDataYBorder; size_t x0 = DivCeil(block_rect.x0() * kBlockDim, 1 << hshift); size_t x1 = DivCeil((block_rect.x0() + block_rect.xsize()) * kBlockDim, 1 << hshift); size_t y0 = DivCeil(block_rect.y0() * kBlockDim, 1 << vshift); size_t y1 = DivCeil((block_rect.y0() + block_rect.ysize()) * kBlockDim, 1 << vshift); size_t gy = block_rect.y0() / kGroupDimInBlocks; size_t gx = block_rect.x0() / kGroupDimInBlocks; // TODO(veluca): this is too much with chroma upsampling. It's just // inefficient though. size_t borderx = GroupBorderAssigner::PaddingX(padding); size_t bordery = padding; size_t borderx_write = padding + borderx; size_t bordery_write = padding + bordery; CopyImageTo( Rect(kGroupDataXBorder, kGroupDataYBorder, x1 - x0, bordery_write), plane_in, Rect(x0, (gy * 2) * bordery_write, x1 - x0, bordery_write), border_storage_h); CopyImageTo( Rect(kGroupDataXBorder, kGroupDataYBorder + y1 - y0 - bordery_write, x1 - x0, bordery_write), plane_in, Rect(x0, (gy * 2 + 1) * bordery_write, x1 - x0, bordery_write), border_storage_h); CopyImageTo( Rect(kGroupDataXBorder, kGroupDataYBorder, borderx_write, y1 - y0), plane_in, Rect((gx * 2) * borderx_write, y0, borderx_write, y1 - y0), border_storage_v); CopyImageTo(Rect(kGroupDataXBorder + x1 - x0 - borderx_write, kGroupDataYBorder, borderx_write, y1 - y0), plane_in, Rect((gx * 2 + 1) * borderx_write, y0, borderx_write, y1 - y0), border_storage_v); } void 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) { constexpr size_t kGroupDataXBorder = PassesDecoderState::kGroupDataXBorder; constexpr size_t kGroupDataYBorder = PassesDecoderState::kGroupDataYBorder; size_t x0 = DivCeil(block_rect.x0() * kBlockDim, 1 << hshift); size_t x1 = DivCeil((block_rect.x0() + block_rect.xsize()) * kBlockDim, 1 << hshift); size_t y0 = DivCeil(block_rect.y0() * kBlockDim, 1 << vshift); size_t y1 = DivCeil((block_rect.y0() + block_rect.ysize()) * kBlockDim, 1 << vshift); size_t gy = block_rect.y0() / kGroupDimInBlocks; size_t gx = block_rect.x0() / kGroupDimInBlocks; size_t borderx = GroupBorderAssigner::PaddingX(padding); size_t bordery = padding; size_t borderx_write = padding + borderx; size_t bordery_write = padding + bordery; // Limits of the area to copy from, in image coordinates. JXL_DASSERT(r.x0() == 0 || r.x0() >= borderx); size_t x0src = DivCeil(r.x0() == 0 ? r.x0() : r.x0() - borderx, 1 << hshift); // r may be such that r.x1 (namely x0() + xsize()) is within borderx of the // right side of the image, so we use min() here. size_t x1src = DivCeil(std::min(r.x0() + r.xsize() + borderx, frame_dim.xsize_padded), 1 << hshift); JXL_DASSERT(r.y0() == 0 || r.y0() >= bordery); size_t y0src = DivCeil(r.y0() == 0 ? r.y0() : r.y0() - bordery, 1 << vshift); // Similar to x1, y1 might be closer than bordery from the bottom. size_t y1src = DivCeil(std::min(r.y0() + r.ysize() + bordery, frame_dim.ysize_padded), 1 << vshift); // Copy other groups' borders from the border storage. if (y0src < y0) { JXL_DASSERT(gy > 0); CopyImageTo( Rect(x0src, (gy * 2 - 1) * bordery_write, x1src - x0src, bordery_write), border_storage_h, Rect(kGroupDataXBorder + x0src - x0, kGroupDataYBorder - bordery_write, x1src - x0src, bordery_write), plane_out); } if (y1src > y1) { // When copying the bottom border we must not be on the bottom groups. JXL_DASSERT(gy + 1 < frame_dim.ysize_groups); CopyImageTo( Rect(x0src, (gy * 2 + 2) * bordery_write, x1src - x0src, bordery_write), border_storage_h, Rect(kGroupDataXBorder + x0src - x0, kGroupDataYBorder + y1 - y0, x1src - x0src, bordery_write), plane_out); } if (x0src < x0) { JXL_DASSERT(gx > 0); CopyImageTo( Rect((gx * 2 - 1) * borderx_write, y0src, borderx_write, y1src - y0src), border_storage_v, Rect(kGroupDataXBorder - borderx_write, kGroupDataYBorder + y0src - y0, borderx_write, y1src - y0src), plane_out); } if (x1src > x1) { // When copying the right border we must not be on the rightmost groups. JXL_DASSERT(gx + 1 < frame_dim.xsize_groups); CopyImageTo( Rect((gx * 2 + 2) * borderx_write, y0src, borderx_write, y1src - y0src), border_storage_v, Rect(kGroupDataXBorder + x1 - x0, kGroupDataYBorder + y0src - y0, borderx_write, y1src - y0src), plane_out); } } } // namespace Status PassesDecoderState::FinalizeGroup(size_t group_idx, size_t thread, Image3F* pixel_data, ImageBundle* output) { // Copy the group borders to the border storage. const Rect block_rect = shared->BlockGroupRect(group_idx); const YCbCrChromaSubsampling& cs = shared->frame_header.chroma_subsampling; size_t padding = FinalizeRectPadding(); for (size_t c = 0; c < 3; c++) { SaveBorders(block_rect, cs.HShift(c), cs.VShift(c), padding, pixel_data->Plane(c), &borders_horizontal.Plane(c), &borders_vertical.Plane(c)); } Rect fir_rects[GroupBorderAssigner::kMaxToFinalize]; size_t num_fir_rects = 0; group_border_assigner.GroupDone(group_idx, FinalizeRectPadding(), fir_rects, &num_fir_rects); for (size_t i = 0; i < num_fir_rects; i++) { const Rect& r = fir_rects[i]; for (size_t c = 0; c < 3; c++) { LoadBorders(block_rect, cs.HShift(c), cs.VShift(c), shared->frame_dim, padding, borders_horizontal.Plane(c), borders_vertical.Plane(c), r, &pixel_data->Plane(c)); } Rect pixel_data_rect( kGroupDataXBorder + r.x0() - block_rect.x0() * kBlockDim, kGroupDataYBorder + r.y0() - block_rect.y0() * kBlockDim, r.xsize(), r.ysize()); JXL_RETURN_IF_ERROR(FinalizeImageRect(pixel_data, pixel_data_rect, {}, this, thread, output, r)); } return true; } } // namespace jxl