Coverage Report

Created: 2023-08-28 07:24

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