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 #ifndef LIB_JXL_RENDER_PIPELINE_RENDER_PIPELINE_STAGE_H_
7 #define LIB_JXL_RENDER_PIPELINE_RENDER_PIPELINE_STAGE_H_
8 
9 #include <stdint.h>
10 
11 #include "lib/jxl/filters.h"
12 
13 namespace jxl {
14 
15 // The first pixel in the input to RenderPipelineStage will be located at
16 // this position. Pixels before this position may be accessed as padding.
17 constexpr size_t kRenderPipelineXOffset = 16;
18 
19 enum class RenderPipelineChannelMode {
20   // This channel is not modified by this stage.
21   kIgnored = 0,
22   // This channel is modified in-place.
23   kInPlace = 1,
24   // This channel is modified and written to a new buffer.
25   kInOut = 2,
26   // This channel is only read.
27   kInput = 3,
28 };
29 
30 class RenderPipeline;
31 
32 class RenderPipelineStage {
33  protected:
34   using Row = float*;
35   using ChannelRows = std::vector<Row>;
36   using RowInfo = std::vector<ChannelRows>;
37 
38  public:
39   struct Settings {
40     // Amount of padding required in the various directions by all channels
41     // that have kInOut mode.
42     size_t border_x = 0;
43     size_t border_y = 0;
44 
45     // Log2 of the number of columns/rows of output that this stage will produce
46     // for every input row for kInOut channels.
47     size_t shift_x = 0;
48     size_t shift_y = 0;
49 
50     // Size (in floats) of the (aligned) per-thread temporary buffer to pass to
51     // ProcessRow.
52     size_t temp_buffer_size = 0;
53 
ShiftXSettings54     static Settings ShiftX(size_t shift, size_t border) {
55       Settings settings;
56       settings.border_x = border;
57       settings.shift_x = shift;
58       return settings;
59     }
60 
ShiftYSettings61     static Settings ShiftY(size_t shift, size_t border) {
62       Settings settings;
63       settings.border_y = border;
64       settings.shift_y = shift;
65       return settings;
66     }
67 
68     static Settings Symmetric(size_t shift, size_t border,
69                               size_t temp_buffer_size = 0) {
70       Settings settings;
71       settings.border_x = settings.border_y = border;
72       settings.shift_x = settings.shift_y = shift;
73       return settings;
74     }
75 
SymmetricBorderOnlySettings76     static Settings SymmetricBorderOnly(size_t border) {
77       return Symmetric(0, border);
78     }
79   };
80 
81   virtual ~RenderPipelineStage() = default;
82 
83  protected:
IsInitialized()84   virtual Status IsInitialized() const { return true; }
85 
86   // Processes one row of input, producing the appropriate number of rows of
87   // output. Input/output rows can be obtained by calls to
88   // `GetInputRow`/`GetOutputRow`. `xsize+2*xextra` represents the total number
89   // of pixels to be processed in the input row, where the first pixel is at
90   // position `kRenderPipelineXOffset-xextra`. All pixels in the
91   // `[kRenderPipelineXOffset-xextra-border_x,
92   // kRenderPipelineXOffset+xsize+xextra+border_x)` range are initialized and
93   // accessible. `xpos` and `ypos` represent the position of the first
94   // (non-extra, i.e. in position kRenderPipelineXOffset) pixel in the center
95   // row of the input in the full image. `xpos` is a multiple of
96   // `GroupBorderAssigner::kPaddingXRound`. If `settings_.temp_buffer_size` is
97   // nonzero, `temp` will point to an HWY-aligned buffer of at least that number
98   // of floats; concurrent calls will have different buffers.
99   virtual void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
100                           size_t xextra, size_t xsize, size_t xpos, size_t ypos,
101                           float* JXL_RESTRICT temp) const = 0;
102 
103   // How each channel will be processed. Channels are numbered starting from
104   // color channels (always 3) and followed by all other channels.
105   virtual RenderPipelineChannelMode GetChannelMode(size_t c) const = 0;
106 
RenderPipelineStage(Settings settings)107   explicit RenderPipelineStage(Settings settings) : settings_(settings) {}
108 
109   // Returns a pointer to the input row of channel `c` with offset `y`.
110   // `y` must be in [-settings_.border_y, settings_.border_y]. `c` must be such
111   // that `GetChannelMode(c) != kIgnored`. The returned pointer points to the
112   // offset-ed row (i.e. kRenderPipelineXOffset has been applied).
GetInputRow(const RowInfo & input_rows,size_t c,int offset)113   float* GetInputRow(const RowInfo& input_rows, size_t c, int offset) const {
114     JXL_DASSERT(GetChannelMode(c) != RenderPipelineChannelMode::kIgnored);
115     JXL_DASSERT(-offset <= static_cast<int>(settings_.border_y));
116     JXL_DASSERT(offset <= static_cast<int>(settings_.border_y));
117     return input_rows[c][settings_.border_y + offset] + kRenderPipelineXOffset;
118   }
119   // Similar to `GetInputRow`, but can only be used if `GetChannelMode(c) ==
120   // kInOut`. Offset must be less than `1<<settings_.shift_y`.. The returned
121   // pointer points to the offset-ed row (i.e. kRenderPipelineXOffset has been
122   // applied).
GetOutputRow(const RowInfo & output_rows,size_t c,size_t offset)123   float* GetOutputRow(const RowInfo& output_rows, size_t c,
124                       size_t offset) const {
125     JXL_DASSERT(GetChannelMode(c) == RenderPipelineChannelMode::kInOut);
126     JXL_DASSERT(offset <= 1ul << settings_.shift_y);
127     return output_rows[c][offset] + kRenderPipelineXOffset;
128   }
129 
130   Settings settings_;
131   friend class RenderPipeline;
132   friend class SimpleRenderPipeline;
133   friend class LowMemoryRenderPipeline;
134 };
135 
136 }  // namespace jxl
137 
138 #endif  // LIB_JXL_RENDER_PIPELINE_RENDER_PIPELINE_STAGE_H_
139