tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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