libjxl

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

simple_render_pipeline.cc (11161B)


      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/render_pipeline/simple_render_pipeline.h"
      7 
      8 #include <hwy/base.h>
      9 
     10 #include "lib/jxl/base/status.h"
     11 #include "lib/jxl/image_ops.h"
     12 #include "lib/jxl/render_pipeline/render_pipeline_stage.h"
     13 #include "lib/jxl/sanitizers.h"
     14 
     15 namespace jxl {
     16 
     17 Status SimpleRenderPipeline::PrepareForThreadsInternal(size_t num,
     18                                                        bool use_group_ids) {
     19   if (!channel_data_.empty()) {
     20     return true;
     21   }
     22   auto ch_size = [](size_t frame_size, size_t shift) {
     23     return DivCeil(frame_size, 1 << shift) + kRenderPipelineXOffset * 2;
     24   };
     25   for (size_t c = 0; c < channel_shifts_[0].size(); c++) {
     26     JXL_ASSIGN_OR_RETURN(
     27         ImageF ch, ImageF::Create(ch_size(frame_dimensions_.xsize_upsampled,
     28                                           channel_shifts_[0][c].first),
     29                                   ch_size(frame_dimensions_.ysize_upsampled,
     30                                           channel_shifts_[0][c].second)));
     31     channel_data_.push_back(std::move(ch));
     32     msan::PoisonImage(channel_data_.back());
     33   }
     34   return true;
     35 }
     36 
     37 Rect SimpleRenderPipeline::MakeChannelRect(size_t group_id, size_t channel) {
     38   size_t base_color_shift =
     39       CeilLog2Nonzero(frame_dimensions_.xsize_upsampled_padded /
     40                       frame_dimensions_.xsize_padded);
     41 
     42   const size_t gx = group_id % frame_dimensions_.xsize_groups;
     43   const size_t gy = group_id / frame_dimensions_.xsize_groups;
     44   size_t xgroupdim = (frame_dimensions_.group_dim << base_color_shift) >>
     45                      channel_shifts_[0][channel].first;
     46   size_t ygroupdim = (frame_dimensions_.group_dim << base_color_shift) >>
     47                      channel_shifts_[0][channel].second;
     48   return Rect(
     49       kRenderPipelineXOffset + gx * xgroupdim,
     50       kRenderPipelineXOffset + gy * ygroupdim, xgroupdim, ygroupdim,
     51       kRenderPipelineXOffset + DivCeil(frame_dimensions_.xsize_upsampled,
     52                                        1 << channel_shifts_[0][channel].first),
     53       kRenderPipelineXOffset +
     54           DivCeil(frame_dimensions_.ysize_upsampled,
     55                   1 << channel_shifts_[0][channel].second));
     56 }
     57 
     58 std::vector<std::pair<ImageF*, Rect>> SimpleRenderPipeline::PrepareBuffers(
     59     size_t group_id, size_t thread_id) {
     60   std::vector<std::pair<ImageF*, Rect>> ret;
     61   for (size_t c = 0; c < channel_data_.size(); c++) {
     62     ret.emplace_back(&channel_data_[c], MakeChannelRect(group_id, c));
     63   }
     64   return ret;
     65 }
     66 
     67 Status SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
     68   for (size_t c = 0; c < channel_data_.size(); c++) {
     69     Rect r = MakeChannelRect(group_id, c);
     70     (void)r;
     71     JXL_CHECK_PLANE_INITIALIZED(channel_data_[c], r, c);
     72   }
     73 
     74   if (PassesWithAllInput() <= processed_passes_) return true;
     75   processed_passes_++;
     76 
     77   for (size_t stage_id = 0; stage_id < stages_.size(); stage_id++) {
     78     const auto& stage = stages_[stage_id];
     79     // Prepare buffers for kInOut channels.
     80     std::vector<ImageF> new_channels(channel_data_.size());
     81     std::vector<ImageF*> output_channels(channel_data_.size());
     82 
     83     std::vector<std::pair<size_t, size_t>> input_sizes(channel_data_.size());
     84     for (size_t c = 0; c < channel_data_.size(); c++) {
     85       input_sizes[c] =
     86           std::make_pair(channel_data_[c].xsize() - kRenderPipelineXOffset * 2,
     87                          channel_data_[c].ysize() - kRenderPipelineXOffset * 2);
     88     }
     89 
     90     for (size_t c = 0; c < channel_data_.size(); c++) {
     91       if (stage->GetChannelMode(c) != RenderPipelineChannelMode::kInOut) {
     92         continue;
     93       }
     94       // Ensure that the newly allocated channels are large enough to avoid
     95       // problems with padding.
     96       JXL_ASSIGN_OR_RETURN(
     97           new_channels[c],
     98           ImageF::Create(frame_dimensions_.xsize_upsampled_padded +
     99                              kRenderPipelineXOffset * 2 +
    100                              hwy::kMaxVectorSize * 8,
    101                          frame_dimensions_.ysize_upsampled_padded +
    102                              kRenderPipelineXOffset * 2));
    103       new_channels[c].ShrinkTo(
    104           (input_sizes[c].first << stage->settings_.shift_x) +
    105               kRenderPipelineXOffset * 2,
    106           (input_sizes[c].second << stage->settings_.shift_y) +
    107               kRenderPipelineXOffset * 2);
    108       output_channels[c] = &new_channels[c];
    109     }
    110 
    111     auto get_row = [&](size_t c, int64_t y) {
    112       return channel_data_[c].Row(kRenderPipelineXOffset + y) +
    113              kRenderPipelineXOffset;
    114     };
    115 
    116     // Add mirrored pixes to all kInOut channels.
    117     for (size_t c = 0; c < channel_data_.size(); c++) {
    118       if (stage->GetChannelMode(c) != RenderPipelineChannelMode::kInOut) {
    119         continue;
    120       }
    121       // Horizontal mirroring.
    122       for (size_t y = 0; y < input_sizes[c].second; y++) {
    123         float* row = get_row(c, y);
    124         for (size_t ix = 0; ix < stage->settings_.border_x; ix++) {
    125           *(row - ix - 1) =
    126               row[Mirror(-static_cast<ssize_t>(ix) - 1, input_sizes[c].first)];
    127         }
    128         for (size_t ix = 0; ix < stage->settings_.border_x; ix++) {
    129           *(row + ix + input_sizes[c].first) =
    130               row[Mirror(ix + input_sizes[c].first, input_sizes[c].first)];
    131         }
    132       }
    133       // Vertical mirroring.
    134       for (int y = 0; y < static_cast<int>(stage->settings_.border_y); y++) {
    135         memcpy(get_row(c, -y - 1) - stage->settings_.border_x,
    136                get_row(c, Mirror(-static_cast<ssize_t>(y) - 1,
    137                                  input_sizes[c].second)) -
    138                    stage->settings_.border_x,
    139                sizeof(float) *
    140                    (input_sizes[c].first + 2 * stage->settings_.border_x));
    141       }
    142       for (int y = 0; y < static_cast<int>(stage->settings_.border_y); y++) {
    143         memcpy(
    144             get_row(c, input_sizes[c].second + y) - stage->settings_.border_x,
    145             get_row(c,
    146                     Mirror(input_sizes[c].second + y, input_sizes[c].second)) -
    147                 stage->settings_.border_x,
    148             sizeof(float) *
    149                 (input_sizes[c].first + 2 * stage->settings_.border_x));
    150       }
    151     }
    152 
    153     size_t ysize = 0;
    154     size_t xsize = 0;
    155     for (size_t c = 0; c < channel_data_.size(); c++) {
    156       if (stage->GetChannelMode(c) == RenderPipelineChannelMode::kIgnored) {
    157         continue;
    158       }
    159       ysize = std::max(input_sizes[c].second, ysize);
    160       xsize = std::max(input_sizes[c].first, xsize);
    161     }
    162 
    163     JXL_ASSERT(ysize != 0);
    164     JXL_ASSERT(xsize != 0);
    165 
    166     RenderPipelineStage::RowInfo input_rows(channel_data_.size());
    167     RenderPipelineStage::RowInfo output_rows(channel_data_.size());
    168 
    169     // Run the pipeline.
    170     {
    171       JXL_RETURN_IF_ERROR(stage->SetInputSizes(input_sizes));
    172       int border_y = stage->settings_.border_y;
    173       for (size_t y = 0; y < ysize; y++) {
    174         // Prepare input rows.
    175         for (size_t c = 0; c < channel_data_.size(); c++) {
    176           if (stage->GetChannelMode(c) == RenderPipelineChannelMode::kIgnored) {
    177             continue;
    178           }
    179           input_rows[c].resize(2 * border_y + 1);
    180           for (int iy = -border_y; iy <= border_y; iy++) {
    181             input_rows[c][iy + border_y] =
    182                 channel_data_[c].Row(y + kRenderPipelineXOffset + iy);
    183           }
    184         }
    185         // Prepare output rows.
    186         for (size_t c = 0; c < channel_data_.size(); c++) {
    187           if (!output_channels[c]) continue;
    188           output_rows[c].resize(1 << stage->settings_.shift_y);
    189           for (size_t iy = 0; iy < output_rows[c].size(); iy++) {
    190             output_rows[c][iy] = output_channels[c]->Row(
    191                 (y << stage->settings_.shift_y) + iy + kRenderPipelineXOffset);
    192           }
    193         }
    194         JXL_RETURN_IF_ERROR(stage->ProcessRow(input_rows, output_rows,
    195                                               /*xextra=*/0, xsize,
    196                                               /*xpos=*/0, y, thread_id));
    197       }
    198     }
    199 
    200     // Move new channels to current channels.
    201     for (size_t c = 0; c < channel_data_.size(); c++) {
    202       if (stage->GetChannelMode(c) != RenderPipelineChannelMode::kInOut) {
    203         continue;
    204       }
    205       channel_data_[c] = std::move(new_channels[c]);
    206     }
    207     for (size_t c = 0; c < channel_data_.size(); c++) {
    208       size_t next_stage = std::min(stage_id + 1, channel_shifts_.size() - 1);
    209       size_t xsize = DivCeil(frame_dimensions_.xsize_upsampled,
    210                              1 << channel_shifts_[next_stage][c].first);
    211       size_t ysize = DivCeil(frame_dimensions_.ysize_upsampled,
    212                              1 << channel_shifts_[next_stage][c].second);
    213       channel_data_[c].ShrinkTo(xsize + 2 * kRenderPipelineXOffset,
    214                                 ysize + 2 * kRenderPipelineXOffset);
    215       JXL_CHECK_PLANE_INITIALIZED(
    216           channel_data_[c],
    217           Rect(kRenderPipelineXOffset, kRenderPipelineXOffset, xsize, ysize),
    218           c);
    219     }
    220 
    221     if (stage->SwitchToImageDimensions()) {
    222       size_t image_xsize;
    223       size_t image_ysize;
    224       FrameOrigin frame_origin;
    225       stage->GetImageDimensions(&image_xsize, &image_ysize, &frame_origin);
    226       frame_dimensions_.Set(image_xsize, image_ysize, 0, 0, 0, false, 1);
    227       std::vector<ImageF> old_channels = std::move(channel_data_);
    228       channel_data_.clear();
    229       channel_data_.reserve(old_channels.size());
    230       for (size_t c = 0; c < old_channels.size(); c++) {
    231         JXL_ASSIGN_OR_RETURN(
    232             ImageF ch,
    233             ImageF::Create(2 * kRenderPipelineXOffset + image_xsize,
    234                            2 * kRenderPipelineXOffset + image_ysize));
    235         channel_data_.emplace_back(std::move(ch));
    236       }
    237       for (size_t y = 0; y < image_ysize; ++y) {
    238         for (size_t c = 0; c < channel_data_.size(); c++) {
    239           output_rows[c].resize(1);
    240           output_rows[c][0] = channel_data_[c].Row(kRenderPipelineXOffset + y);
    241         }
    242         // TODO(sboukortt): consider doing this only on the parts of the
    243         // background that won't be occluded.
    244         stage->ProcessPaddingRow(output_rows, image_xsize, 0, y);
    245       }
    246       ssize_t x0 = frame_origin.x0;
    247       ssize_t y0 = frame_origin.y0;
    248       size_t x0_fg = 0;
    249       size_t y0_fg = 0;
    250       if (x0 < 0) {
    251         xsize += x0;
    252         x0_fg -= x0;
    253         x0 = 0;
    254       }
    255       if (x0 + xsize > image_xsize) {
    256         xsize = image_xsize - x0;
    257       }
    258       if (y0 < 0) {
    259         ysize += y0;
    260         y0_fg -= x0;
    261         y0 = 0;
    262       }
    263       if (y0 + ysize > image_ysize) {
    264         ysize = image_ysize - y0;
    265       }
    266       const Rect rect_fg_relative_to_image =
    267           Rect(x0, y0, xsize, ysize)
    268               .Translate(kRenderPipelineXOffset, kRenderPipelineXOffset);
    269       const Rect rect_fg =
    270           Rect(x0_fg, y0_fg, xsize, ysize)
    271               .Translate(kRenderPipelineXOffset, kRenderPipelineXOffset);
    272       for (size_t c = 0; c < channel_data_.size(); c++) {
    273         CopyImageTo(rect_fg, old_channels[c], rect_fg_relative_to_image,
    274                     &channel_data_[c]);
    275       }
    276     }
    277   }
    278   return true;
    279 }
    280 }  // namespace jxl