dec_group_border.cc (8456B)
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_group_border.h" 7 8 #include <atomic> 9 #include <cstddef> 10 #include <cstdint> 11 #include <utility> 12 13 #include "lib/jxl/base/rect.h" 14 #include "lib/jxl/base/status.h" 15 #include "lib/jxl/frame_dimensions.h" 16 17 namespace jxl { 18 19 void GroupBorderAssigner::Init(const FrameDimensions& frame_dim) { 20 frame_dim_ = frame_dim; 21 size_t num_corners = 22 (frame_dim_.xsize_groups + 1) * (frame_dim_.ysize_groups + 1); 23 { std::vector<std::atomic<uint8_t>>(num_corners).swap(counters_); } 24 // Initialize counters. 25 for (size_t y = 0; y < frame_dim_.ysize_groups + 1; y++) { 26 for (size_t x = 0; x < frame_dim_.xsize_groups + 1; x++) { 27 // Counters at image borders don't have anything on the other side, we 28 // pre-fill their value to have more uniform handling afterwards. 29 uint8_t init_value = 0; 30 if (x == 0) { 31 init_value |= kTopLeft | kBottomLeft; 32 } 33 if (x == frame_dim_.xsize_groups) { 34 init_value |= kTopRight | kBottomRight; 35 } 36 if (y == 0) { 37 init_value |= kTopLeft | kTopRight; 38 } 39 if (y == frame_dim_.ysize_groups) { 40 init_value |= kBottomLeft | kBottomRight; 41 } 42 counters_[y * (frame_dim_.xsize_groups + 1) + x] = init_value; 43 } 44 } 45 } 46 47 void GroupBorderAssigner::ClearDone(size_t group_id) { 48 size_t x = group_id % frame_dim_.xsize_groups; 49 size_t y = group_id / frame_dim_.xsize_groups; 50 size_t top_left_idx = y * (frame_dim_.xsize_groups + 1) + x; 51 size_t top_right_idx = y * (frame_dim_.xsize_groups + 1) + x + 1; 52 size_t bottom_right_idx = (y + 1) * (frame_dim_.xsize_groups + 1) + x + 1; 53 size_t bottom_left_idx = (y + 1) * (frame_dim_.xsize_groups + 1) + x; 54 counters_[top_left_idx].fetch_and(~kBottomRight); 55 counters_[top_right_idx].fetch_and(~kBottomLeft); 56 counters_[bottom_left_idx].fetch_and(~kTopRight); 57 counters_[bottom_right_idx].fetch_and(~kTopLeft); 58 } 59 60 // Looking at each corner between groups, we can guarantee that the four 61 // involved groups will agree between each other regarding the order in which 62 // each of the four groups terminated. Thus, the last of the four groups 63 // gets the responsibility of handling the corner. For borders, every border 64 // is assigned to its top corner (for vertical borders) or to its left corner 65 // (for horizontal borders): the order as seen on those corners will decide who 66 // handles that border. 67 68 void GroupBorderAssigner::GroupDone(size_t group_id, size_t padx, size_t pady, 69 Rect* rects_to_finalize, 70 size_t* num_to_finalize) { 71 size_t x = group_id % frame_dim_.xsize_groups; 72 size_t y = group_id / frame_dim_.xsize_groups; 73 Rect block_rect(x * frame_dim_.group_dim / kBlockDim, 74 y * frame_dim_.group_dim / kBlockDim, 75 frame_dim_.group_dim / kBlockDim, 76 frame_dim_.group_dim / kBlockDim, frame_dim_.xsize_blocks, 77 frame_dim_.ysize_blocks); 78 79 size_t top_left_idx = y * (frame_dim_.xsize_groups + 1) + x; 80 size_t top_right_idx = y * (frame_dim_.xsize_groups + 1) + x + 1; 81 size_t bottom_right_idx = (y + 1) * (frame_dim_.xsize_groups + 1) + x + 1; 82 size_t bottom_left_idx = (y + 1) * (frame_dim_.xsize_groups + 1) + x; 83 84 auto fetch_status = [this](size_t idx, uint8_t bit) { 85 // Note that the acq-rel semantics of this fetch are actually needed to 86 // ensure that the pixel data of the group is already written to memory. 87 size_t status = counters_[idx].fetch_or(bit); 88 JXL_DASSERT((bit & status) == 0); 89 return bit | status; 90 }; 91 92 size_t top_left_status = fetch_status(top_left_idx, kBottomRight); 93 size_t top_right_status = fetch_status(top_right_idx, kBottomLeft); 94 size_t bottom_right_status = fetch_status(bottom_right_idx, kTopLeft); 95 size_t bottom_left_status = fetch_status(bottom_left_idx, kTopRight); 96 97 size_t x1 = block_rect.x0() + block_rect.xsize(); 98 size_t y1 = block_rect.y0() + block_rect.ysize(); 99 100 bool is_last_group_x = frame_dim_.xsize_groups == x + 1; 101 bool is_last_group_y = frame_dim_.ysize_groups == y + 1; 102 103 // Start of border of neighbouring group, end of border of this group, start 104 // of border of this group (on the other side), end of border of next group. 105 size_t xpos[4] = { 106 block_rect.x0() == 0 ? 0 : block_rect.x0() * kBlockDim - padx, 107 block_rect.x0() == 0 108 ? 0 109 : std::min(frame_dim_.xsize, block_rect.x0() * kBlockDim + padx), 110 is_last_group_x ? frame_dim_.xsize : x1 * kBlockDim - padx, 111 std::min(frame_dim_.xsize, x1 * kBlockDim + padx)}; 112 size_t ypos[4] = { 113 block_rect.y0() == 0 ? 0 : block_rect.y0() * kBlockDim - pady, 114 block_rect.y0() == 0 115 ? 0 116 : std::min(frame_dim_.ysize, block_rect.y0() * kBlockDim + pady), 117 is_last_group_y ? frame_dim_.ysize : y1 * kBlockDim - pady, 118 std::min(frame_dim_.ysize, y1 * kBlockDim + pady)}; 119 120 *num_to_finalize = 0; 121 auto append_rect = [&](size_t x0, size_t x1, size_t y0, size_t y1) { 122 Rect rect(xpos[x0], ypos[y0], xpos[x1] - xpos[x0], ypos[y1] - ypos[y0]); 123 if (rect.xsize() == 0 || rect.ysize() == 0) return; 124 JXL_DASSERT(*num_to_finalize < kMaxToFinalize); 125 rects_to_finalize[(*num_to_finalize)++] = rect; 126 }; 127 128 // Because of how group borders are assigned, it is impossible that we need to 129 // process the left and right side of some area but not the center area. Thus, 130 // we compute the first/last part to process in every horizontal strip and 131 // merge them together. We first collect a mask of what parts should be 132 // processed. 133 // We do this horizontally rather than vertically because horizontal borders 134 // are larger. 135 bool available_parts_mask[3][3] = {}; // [x][y] 136 // Center 137 available_parts_mask[1][1] = true; 138 // Corners 139 if (top_left_status == 0xF) available_parts_mask[0][0] = true; 140 if (top_right_status == 0xF) available_parts_mask[2][0] = true; 141 if (bottom_right_status == 0xF) available_parts_mask[2][2] = true; 142 if (bottom_left_status == 0xF) available_parts_mask[0][2] = true; 143 // Other borders 144 if (top_left_status & kTopRight) available_parts_mask[1][0] = true; 145 if (top_left_status & kBottomLeft) available_parts_mask[0][1] = true; 146 if (top_right_status & kBottomRight) available_parts_mask[2][1] = true; 147 if (bottom_left_status & kBottomRight) available_parts_mask[1][2] = true; 148 149 // Collect horizontal ranges. 150 constexpr size_t kNoSegment = 3; 151 std::pair<size_t, size_t> horizontal_segments[3] = {{kNoSegment, kNoSegment}, 152 {kNoSegment, kNoSegment}, 153 {kNoSegment, kNoSegment}}; 154 for (size_t y = 0; y < 3; y++) { 155 for (size_t x = 0; x < 3; x++) { 156 if (!available_parts_mask[x][y]) continue; 157 JXL_DASSERT(horizontal_segments[y].second == kNoSegment || 158 horizontal_segments[y].second == x); 159 JXL_DASSERT((horizontal_segments[y].first == kNoSegment) == 160 (horizontal_segments[y].second == kNoSegment)); 161 if (horizontal_segments[y].first == kNoSegment) { 162 horizontal_segments[y].first = x; 163 } 164 horizontal_segments[y].second = x + 1; 165 } 166 } 167 if (horizontal_segments[0] == horizontal_segments[1] && 168 horizontal_segments[0] == horizontal_segments[2]) { 169 append_rect(horizontal_segments[0].first, horizontal_segments[0].second, 0, 170 3); 171 } else if (horizontal_segments[0] == horizontal_segments[1]) { 172 append_rect(horizontal_segments[0].first, horizontal_segments[0].second, 0, 173 2); 174 append_rect(horizontal_segments[2].first, horizontal_segments[2].second, 2, 175 3); 176 } else if (horizontal_segments[1] == horizontal_segments[2]) { 177 append_rect(horizontal_segments[0].first, horizontal_segments[0].second, 0, 178 1); 179 append_rect(horizontal_segments[1].first, horizontal_segments[1].second, 1, 180 3); 181 } else { 182 append_rect(horizontal_segments[0].first, horizontal_segments[0].second, 0, 183 1); 184 append_rect(horizontal_segments[1].first, horizontal_segments[1].second, 1, 185 2); 186 append_rect(horizontal_segments[2].first, horizontal_segments[2].second, 2, 187 3); 188 } 189 } 190 191 } // namespace jxl