libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

dec_group_border.cc (8284B)


      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 void GroupBorderAssigner::Init(const FrameDimensions& frame_dim) {
     13   frame_dim_ = frame_dim;
     14   size_t num_corners =
     15       (frame_dim_.xsize_groups + 1) * (frame_dim_.ysize_groups + 1);
     16   counters_.reset(new std::atomic<uint8_t>[num_corners]);
     17   // Initialize counters.
     18   for (size_t y = 0; y < frame_dim_.ysize_groups + 1; y++) {
     19     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       uint8_t init_value = 0;
     23       if (x == 0) {
     24         init_value |= kTopLeft | kBottomLeft;
     25       }
     26       if (x == frame_dim_.xsize_groups) {
     27         init_value |= kTopRight | kBottomRight;
     28       }
     29       if (y == 0) {
     30         init_value |= kTopLeft | kTopRight;
     31       }
     32       if (y == frame_dim_.ysize_groups) {
     33         init_value |= kBottomLeft | kBottomRight;
     34       }
     35       counters_[y * (frame_dim_.xsize_groups + 1) + x] = init_value;
     36     }
     37   }
     38 }
     39 
     40 void GroupBorderAssigner::ClearDone(size_t group_id) {
     41   size_t x = group_id % frame_dim_.xsize_groups;
     42   size_t y = group_id / frame_dim_.xsize_groups;
     43   size_t top_left_idx = y * (frame_dim_.xsize_groups + 1) + x;
     44   size_t top_right_idx = y * (frame_dim_.xsize_groups + 1) + x + 1;
     45   size_t bottom_right_idx = (y + 1) * (frame_dim_.xsize_groups + 1) + x + 1;
     46   size_t bottom_left_idx = (y + 1) * (frame_dim_.xsize_groups + 1) + x;
     47   counters_[top_left_idx].fetch_and(~kBottomRight);
     48   counters_[top_right_idx].fetch_and(~kBottomLeft);
     49   counters_[bottom_left_idx].fetch_and(~kTopRight);
     50   counters_[bottom_right_idx].fetch_and(~kTopLeft);
     51 }
     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                                     size_t* num_to_finalize) {
     64   size_t x = group_id % frame_dim_.xsize_groups;
     65   size_t y = group_id / frame_dim_.xsize_groups;
     66   Rect block_rect(x * frame_dim_.group_dim / kBlockDim,
     67                   y * frame_dim_.group_dim / kBlockDim,
     68                   frame_dim_.group_dim / kBlockDim,
     69                   frame_dim_.group_dim / kBlockDim, frame_dim_.xsize_blocks,
     70                   frame_dim_.ysize_blocks);
     71 
     72   size_t top_left_idx = y * (frame_dim_.xsize_groups + 1) + x;
     73   size_t top_right_idx = y * (frame_dim_.xsize_groups + 1) + x + 1;
     74   size_t bottom_right_idx = (y + 1) * (frame_dim_.xsize_groups + 1) + x + 1;
     75   size_t bottom_left_idx = (y + 1) * (frame_dim_.xsize_groups + 1) + x;
     76 
     77   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     size_t status = counters_[idx].fetch_or(bit);
     81     JXL_DASSERT((bit & status) == 0);
     82     return bit | status;
     83   };
     84 
     85   size_t top_left_status = fetch_status(top_left_idx, kBottomRight);
     86   size_t top_right_status = fetch_status(top_right_idx, kBottomLeft);
     87   size_t bottom_right_status = fetch_status(bottom_right_idx, kTopLeft);
     88   size_t bottom_left_status = fetch_status(bottom_left_idx, kTopRight);
     89 
     90   size_t x1 = block_rect.x0() + block_rect.xsize();
     91   size_t y1 = block_rect.y0() + block_rect.ysize();
     92 
     93   bool is_last_group_x = frame_dim_.xsize_groups == x + 1;
     94   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   size_t xpos[4] = {
     99       block_rect.x0() == 0 ? 0 : block_rect.x0() * kBlockDim - padx,
    100       block_rect.x0() == 0
    101           ? 0
    102           : std::min(frame_dim_.xsize, block_rect.x0() * kBlockDim + padx),
    103       is_last_group_x ? frame_dim_.xsize : x1 * kBlockDim - padx,
    104       std::min(frame_dim_.xsize, x1 * kBlockDim + padx)};
    105   size_t ypos[4] = {
    106       block_rect.y0() == 0 ? 0 : block_rect.y0() * kBlockDim - pady,
    107       block_rect.y0() == 0
    108           ? 0
    109           : std::min(frame_dim_.ysize, block_rect.y0() * kBlockDim + pady),
    110       is_last_group_y ? frame_dim_.ysize : y1 * kBlockDim - pady,
    111       std::min(frame_dim_.ysize, y1 * kBlockDim + pady)};
    112 
    113   *num_to_finalize = 0;
    114   auto append_rect = [&](size_t x0, size_t x1, size_t y0, size_t y1) {
    115     Rect rect(xpos[x0], ypos[y0], xpos[x1] - xpos[x0], ypos[y1] - ypos[y0]);
    116     if (rect.xsize() == 0 || rect.ysize() == 0) return;
    117     JXL_DASSERT(*num_to_finalize < kMaxToFinalize);
    118     rects_to_finalize[(*num_to_finalize)++] = rect;
    119   };
    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   bool available_parts_mask[3][3] = {};  // [x][y]
    129   // Center
    130   available_parts_mask[1][1] = true;
    131   // Corners
    132   if (top_left_status == 0xF) available_parts_mask[0][0] = true;
    133   if (top_right_status == 0xF) available_parts_mask[2][0] = true;
    134   if (bottom_right_status == 0xF) available_parts_mask[2][2] = true;
    135   if (bottom_left_status == 0xF) available_parts_mask[0][2] = true;
    136   // Other borders
    137   if (top_left_status & kTopRight) available_parts_mask[1][0] = true;
    138   if (top_left_status & kBottomLeft) available_parts_mask[0][1] = true;
    139   if (top_right_status & kBottomRight) available_parts_mask[2][1] = true;
    140   if (bottom_left_status & kBottomRight) available_parts_mask[1][2] = true;
    141 
    142   // Collect horizontal ranges.
    143   constexpr size_t kNoSegment = 3;
    144   std::pair<size_t, size_t> horizontal_segments[3] = {{kNoSegment, kNoSegment},
    145                                                       {kNoSegment, kNoSegment},
    146                                                       {kNoSegment, kNoSegment}};
    147   for (size_t y = 0; y < 3; y++) {
    148     for (size_t x = 0; x < 3; x++) {
    149       if (!available_parts_mask[x][y]) continue;
    150       JXL_DASSERT(horizontal_segments[y].second == kNoSegment ||
    151                   horizontal_segments[y].second == x);
    152       JXL_DASSERT((horizontal_segments[y].first == kNoSegment) ==
    153                   (horizontal_segments[y].second == kNoSegment));
    154       if (horizontal_segments[y].first == kNoSegment) {
    155         horizontal_segments[y].first = x;
    156       }
    157       horizontal_segments[y].second = x + 1;
    158     }
    159   }
    160   if (horizontal_segments[0] == horizontal_segments[1] &&
    161       horizontal_segments[0] == horizontal_segments[2]) {
    162     append_rect(horizontal_segments[0].first, horizontal_segments[0].second, 0,
    163                 3);
    164   } else if (horizontal_segments[0] == horizontal_segments[1]) {
    165     append_rect(horizontal_segments[0].first, horizontal_segments[0].second, 0,
    166                 2);
    167     append_rect(horizontal_segments[2].first, horizontal_segments[2].second, 2,
    168                 3);
    169   } else if (horizontal_segments[1] == horizontal_segments[2]) {
    170     append_rect(horizontal_segments[0].first, horizontal_segments[0].second, 0,
    171                 1);
    172     append_rect(horizontal_segments[1].first, horizontal_segments[1].second, 1,
    173                 3);
    174   } else {
    175     append_rect(horizontal_segments[0].first, horizontal_segments[0].second, 0,
    176                 1);
    177     append_rect(horizontal_segments[1].first, horizontal_segments[1].second, 1,
    178                 2);
    179     append_rect(horizontal_segments[2].first, horizontal_segments[2].second, 2,
    180                 3);
    181   }
    182 }
    183 
    184 }  // namespace jxl