1
1

Compare commits

...

169 Commits

Author SHA1 Message Date
1662bf8ebf Compositor: Initial support for canvas compositing 2021-08-26 18:54:51 +02:00
e8f41f1b93 Compositor: Replace resolution concept by canvas
This is a code refactor in preparation of supporting canvas
compositing and fix all cropping issues on full frame implementation.

No functional changes, all canvases are at (0, 0) position matching
tiled implementation.
2021-08-26 18:49:40 +02:00
c8897efa53 Merge branch 'master' into compositor-full-frame 2021-08-21 19:36:20 +02:00
9a0ed67e25 Compositor: Fix crash enabling buffer groups on full frame
Full frame doesn't support this option as all operations are already 
buffered. UI option will be removed in the future.
2021-08-21 12:36:59 +02:00
efafe7474d Compositor: Fix dilare erode reading input out of bounds
Happened when output area was partial horizontally, for example
when using a viewer border.
2021-08-20 17:12:48 +02:00
31fc76fa5b Compositor: Do not register constant input areas for rendering
If an input is only used as a constant it doesn't rendering
because its already calculated after constant folding.
2021-08-20 15:31:47 +02:00
f669932c93 Compositor: Fix plane distort operations incorrect areas of interest
The original implementation doesn't work for all cases.
2021-08-20 15:03:17 +02:00
7e840321c7 Compositor: Fix plane track wrong area of interest calculations
Perspective matrix in sample data was not initialized because corners
were not calculated at that point of execution.
2021-08-20 11:20:36 +02:00
c03814f945 Compositor: Fix incorrect area of interest in variable bokeh blur 2021-08-20 10:17:51 +02:00
fe53c04c51 Compositor: Fix incorrect copying of uchar buffers
Row stride and the area x coordinate offset were not taken into
account.
2021-08-20 09:54:21 +02:00
5a13cec6b9 Compositor: Enable can_be_constant on vector nodes 2021-08-16 19:13:31 +02:00
0b87a846a8 Compositor: Full frame Normalize node 2021-08-16 19:13:28 +02:00
802abbb0da Compositor: Full frame Normal node 2021-08-16 19:12:38 +02:00
e30d5d183c Compositor: Full frame Map Value node 2021-08-16 19:12:36 +02:00
c97795b43a Compositor: Full frame Map Range node 2021-08-16 19:10:31 +02:00
ddc2d379d4 Compositor: Enable can_be_constant on matte nodes where possible 2021-08-15 16:51:14 +02:00
1be813c8e5 Compositor: Full frame Luminance Key node 2021-08-15 16:33:09 +02:00
a9ba0e67cc Compositor: Full frame Keying Screen node 2021-08-15 16:02:31 +02:00
71eb303ad5 Compositor: Full frame Keying node 2021-08-15 12:10:00 +02:00
549df33a67 Compositor: Full frame Distance Key node 2021-08-13 17:37:23 +02:00
eab2443938 Compositor: Full frame Difference Key node 2021-08-13 14:26:34 +02:00
00632dbb2f Compositor: Full frame Cryptomatte node 2021-08-13 11:55:53 +02:00
c443b0d475 Compositor: Full frame Color Spill node 2021-08-13 11:21:29 +02:00
591e46c335 Compositor: Full frame Color Key node 2021-08-13 11:08:58 +02:00
d1553b4bbd Compositor: Full frame Chroma Key node 2021-08-13 10:23:07 +02:00
70cc988a90 Compositor: Full frame Channel Key node 2021-08-13 09:15:28 +02:00
1747c679ee Compositor: Full frame Vector blur node 2021-08-13 02:14:29 +02:00
eb05b26a69 Compositor: Full frame Inpaint node 2021-08-13 02:14:25 +02:00
561ed5a61f Cleanup: clang-format 2021-08-12 22:32:05 +02:00
aed85ac4b6 Cleanup: fix compiler warnings 2021-08-12 22:28:30 +02:00
bf725bbb92 Compositor: Full frame Glare node
Due to current limitation of scaling up causing cropping,
quality is always "High" independently of the selected
one. This will be fixed once scaling is implemented with
canvas adjustment.
2021-08-12 22:00:09 +02:00
ae8e9c6f48 Compositor: Ensure denoise output is only rendered once 2021-08-12 15:44:40 +02:00
1bf90d2b21 Compositor: Full frame Filter node 2021-08-12 14:58:13 +02:00
f76c2be114 Compositor: Full frame Directional Blur node 2021-08-12 12:40:39 +02:00
5c1650aa05 add TODOs 2021-08-12 11:07:56 +02:00
06c8ebdc7c Compositor: Full frame Dilate/Erode node 2021-08-12 01:04:53 +02:00
f4ea8f5d40 Compositor: Full frame Despeckle node 2021-08-11 11:18:07 +02:00
50b3766075 Compositor: Full frame Denoise node 2021-08-10 23:23:00 +02:00
1a0b2ef90f Compositor: Full frame Defocus node 2021-08-10 18:44:15 +02:00
868e0e3062 Merge branch 'master' into compositor-full-frame 2021-08-10 18:37:44 +02:00
c17d04765e Merge remote-tracking branch 'origin/master' into compositor-full-frame 2021-08-10 10:31:15 +02:00
c9fad771e9 Compositor: Fix transform randomly inverted
Uninitialized variable.
2021-08-08 18:52:20 +02:00
723c45a8e6 Compositor: Full frame Bokeh Blur and Blur nodes 2021-08-08 17:11:54 +02:00
8048479416 Avoid using floor function
It has an impact in performance when used in deep loops.
If area have negative coordinates it will round
toward zero but buffers should be displaced later on to always
work with positive coordinates.
2021-08-08 17:11:54 +02:00
97e667237a Fix compiler warnings: unused variables 2021-08-05 10:06:31 +02:00
dd512e0d3f Compositor: Full frame Anti-Aliasing node 2021-08-04 22:48:22 +02:00
3305e073b4 Compositor: Full frame Map UV node 2021-08-04 18:09:55 +02:00
e94326b67b Compositor: Use a single elem as area of interest where possible 2021-08-04 13:32:07 +02:00
c84a294c66 Compositor: Full frame Lens Distortion node 2021-08-04 10:38:51 +02:00
13590fba71 Compositor: Full frame Movie Distortion node 2021-08-03 23:17:40 +02:00
79ffaa4816 Compositor: Full frame Plane Track Deform and Corner Pin nodes 2021-08-03 21:01:40 +02:00
b02069ef4d Compositor: Sample single elem buffers at borders 2021-08-03 20:48:56 +02:00
70bff51e60 Compositor: Full frame Flip node 2021-08-02 23:01:12 +02:00
dbe753d1b3 Compositor: Fix memory leaks when initializing tiles multi-threaded 2021-08-02 22:39:40 +02:00
9fa4d37061 Compositor: Full frame Stabilize 2D node 2021-08-02 22:35:42 +02:00
d5655a8449 Compositor: Add invert option to TransformOperation 2021-08-02 22:35:42 +02:00
d017ff9ec1 Merge branch 'cmp-nodes-input' into compositor-full-frame 2021-08-02 21:21:15 +02:00
28502f2239 Make Track Position a constant operation 2021-08-02 17:45:26 +02:00
a570ad2e1b Add MovieClipAttribute as a constant operation 2021-08-02 17:45:05 +02:00
9ef0f7ab27 Merge branch 'master' into cmp-nodes-input 2021-08-02 17:20:45 +02:00
f330f43f10 Merge branch 'master' into compositor-full-frame 2021-08-02 13:41:29 +02:00
53d588a9d9 Compositor: Full frame input nodes
Adds full frame implementation to "Bokeh Image" node, "Track Position"
node and `SetVectorOperation`.
The other nodes in "Input" submenu are implemented separately.
No functional changes.

---
Note:
These nodes are very similar to already reviewed nodes. The patch
is meant for a quick look before committing.

Differential Revision: https://developer.blender.org/D12090
2021-07-30 21:57:39 +02:00
4908abda5f Compositor: Full frame Crop node
Adds full frame implementation to this node operation.
No functional changes.
2021-07-29 22:25:12 +02:00
9b02754383 Compositor: Full frame Displace node
Adds full frame implementation to this node operation.
No functional changes.
2021-07-29 22:25:12 +02:00
961d38f8fd Fix missing const 2021-07-29 22:25:12 +02:00
4b4be12881 Compositor: Full frame Set Alpha node
Adds full frame implementation to this node operations.
No functional changes.
2021-07-29 14:49:50 +02:00
e783811305 Compositor: Full frame Color Ramp node
Adds full frame implementation to this node operation.
No functional changes.
2021-07-29 13:54:44 +02:00
f690cd36b5 Compositor: Full frame Hue Saturation Value node
Adds full frame implementation to this node operation.
No functional changes.
2021-07-29 13:48:12 +02:00
6acbb08538 Compositor: Full frame Tonemap node
Adds full frame implementation to this node operations.
No functional changes.
2021-07-29 13:25:25 +02:00
417262e8f8 Cleanup: remove unneeded include 2021-07-28 23:50:45 +02:00
4745dfadde Compositor: Full frame Transform node
In order to avoid sampling twice when concatenating
scale and rotate operations, a transform operation
is implemented for full frame with all the functionality.
The node has no functional changes.
2021-07-28 23:24:44 +02:00
7fe3917f83 Cleanup: Unused variables 2021-07-27 23:12:43 +02:00
4147e09b44 Merge branch 'master' into compositor-full-frame 2021-07-27 22:15:48 +02:00
499fbfa0a6 Compositor: Full frame Rotate node
Adds full frame implementation to this node operation.
No functional changes.
2021-07-27 21:52:40 +02:00
64a1b02bda Compositor: Add sampling methods for full frame
Current sampling methods do not take into account area offsets
and have some off by 1 issues on full frame. For example on bilinear
bottom and left image border pixel is not fully sampled and
creates edges.
Other issue is they use `wrap_pixel` when most of the time is not
needed.

In order to not affect tiled implementation, this commit creates
specific sampling methods for full frame needs.
2021-07-27 21:52:40 +02:00
93718956fc Compositor: Full frame Scale node
Adds full frame implementation to this node operations.
No functional changes.

Includes a new operation method `init_data` used to initialize any data
needed after operations are linked and resolutions determined.
Once tiled implementation is removed `initExecution` may be renamed
to `init_rendering` and `init_data` to `init_execution`.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D11944
2021-07-27 21:52:40 +02:00
30baa0b7ff Compositor: Full frame ZCombine node
Adds full frame implementation to this node operations.
No functional changes.
2021-07-27 21:30:46 +02:00
9dcac5e859 Compositor: Full frame Invert node
Adds full frame implementation to this node operation.
No functional changes.
2021-07-27 21:30:45 +02:00
7f6a8e4156 Compositor: Full frame curve nodes
Adds full frame implementation to "RGB Curves",
"Vector Curves" and "Hue Correct" nodes.
No functional changes.
2021-07-27 21:30:45 +02:00
e09bae21e7 Compositor: Full frame Math node
Adds full frame implementation to this node operations.
No functional changes.
2021-07-27 21:30:43 +02:00
479c87c1d6 Cleanup: Remove unneeded default constructor definitions 2021-07-24 23:57:28 +02:00
fd04112033 Compositor: Full frame Convert operations
All nodes from "Converter" sub-menu use these operations except:
"Color Ramp", "ID Mask", "Math", "Set Alpha" and "Switch View".
2021-07-24 16:11:08 +02:00
543172f2f4 Cleanup: Move iterator increment to loop header 2021-07-23 22:57:25 +02:00
01e5042cd8 Compositor: Full frame Alpha Over node
Adds full frame implementation to this node operation.
No functional changes.
2021-07-23 22:47:38 +02:00
456a989d2e Compositor: Full frame Track Position node
Adds full frame implementation to this node operation.
No functional changes.
2021-07-23 19:06:38 +02:00
9062b71d4f Compositor: Full frame Composite node
Adds full frame implementation to this node operation.
No functional changes.
2021-07-23 17:45:55 +02:00
88d72a8f5c Compositor: Full frame Bokeh Image node
Adds full frame implementation to this node operation.
No functional changes.
2021-07-23 17:29:35 +02:00
e906650807 Compositor: Full frame Split Viewer node
Adds full frame implementation to this node operation.
No functional changes.
2021-07-23 17:16:43 +02:00
43394dada3 Compositor: Full frame File Output node
Adds full frame implementation to this node operations.
No functional changes.
2021-07-23 16:50:11 +02:00
bf27be8192 After merge fixes 2021-07-22 23:38:16 +02:00
585a891ab2 Merge branch 'master' into compositor-full-frame 2021-07-22 21:55:42 +02:00
89b5731f19 Merge branch 'master' into compositor-full-frame 2021-06-06 00:17:55 +02:00
61b0580cf5 tmp 2021-06-02 12:54:21 +02:00
0e255b2e7a Compositor: Fix image node alpha socket converted into operations twice
When translating image node sockets into operations, unavailable ones (with flag SOCK_UNAVAIL) are converted too. This causes an assertion to fail when selecting a multi-layer image with a "Combined" layer because it's mapping associated "Alpha" socket to operation output twice (as there is an unavailable "Image" socket used for combined too).

During compositing execution there is not need to create unavailable sockets as they are not linked or executed. Assertion fails since commit 93e2491, as now map add_new is used instead of just overwriting last mapped socket.
2021-06-02 12:24:13 +02:00
5f39310323 Revert "Fix: Unavailable sockets are used during compositing execution"
This reverts commit ce7f952e25.
2021-06-01 23:39:54 +02:00
dfc9bbd617 Compositor: Fix areas of interest outside operations bounds
They should be cropped instead of clamped.
2021-05-31 11:11:07 +02:00
f2a1a6f870 Compositor: Set current execution model in operations
Operations may share methods between tiled and full frame implementation and they may need to differentiate between them in some cases. This is just a temporal solution while keeping both execution models.
2021-05-31 11:01:27 +02:00
d81dd5a268 Compositor: Crop operations canvases to rendered areas
When calculating operations resolutions (canvas size), rendered areas are not known yet so worst case scenario is assumed which is full size. But they can be cropped to rendered areas bounds one they are known. This reduces memory usage as buffers size will be the same than rendered areas bounds.
2021-05-31 10:44:36 +02:00
b17c490043 Compositor: Do constant folding for full frame execution
All operations with constant pixel/elements are evaluated into primitive Value/Vector/Color constant operations prior rendering execution. This is the case of operations that are constant only when all their inputs are so. Such operations should set can_be_constant flag to true. is_constant flag is only used for primitive operations (Color/Vector/Value) which are the only constant operations during rendering execution.
2021-05-31 10:44:36 +02:00
89e22a99e7 Fix incorrect element size assertion 2021-05-27 23:48:27 +02:00
f00ee29407 Merge branch 'master' into compositor-full-frame 2021-05-27 22:20:51 +02:00
da1260a5fc Full frame value node
Adds full frame implementation to value node operations.
2021-05-27 22:15:33 +02:00
3671d16e29 Full frame color node
Adds full frame implementation to color node operations.
2021-05-27 22:10:36 +02:00
157d9f6661 Full frame image node
Adds full frame implementation to image node operations.
2021-05-27 21:23:44 +02:00
ce7f952e25 Fix: Unavailable sockets are used during compositing execution
When translating node tree into operations, unavailable sockets (with flag SOCK_UNAVAIL) are converted into operations sockets too. This causes an assertion to fail when selecting a multi-layer image with a "Combined" layer in image node because it maps associated "Alpha" socket to output operation socket twice (as there is an "Image" socket too but with SOCK_UNAVAIL flag).

ImageNode::convertToOperations assumes there is a single socket for combined  pass but in this case the unavailable "Image" socket is used too for combined pass. 
Image node sockets are marked with SOCK_UNAVAIL flag instead of being removed to keep connections of unused sockets but during compositing execution they shouldn't be created as they are not executed.
This problem has become visible after commit 93e2491, as now map add_new is used instead of just overwriting last mapped socket.
2021-05-27 21:23:44 +02:00
0642543a1b Create buffer utility methods for copying/filling rects 2021-05-27 16:24:36 +02:00
a8a957bff0 Add get_area_of_interest overloading and pass input_op 2021-05-22 16:12:18 +02:00
67d869cf41 Merge branch 'master' into cmp-full-frame 2021-05-22 15:51:52 +02:00
4a184c28ce Improve comments and naming 2021-05-20 18:07:32 +02:00
d0f5cb2b42 Improve SharedOperationBuffers methods naming and add doc comments 2021-05-20 16:39:07 +02:00
ce1dc77f32 Improve get_area_of_interest doc comments and signature 2021-05-20 16:38:26 +02:00
e7838d7b81 Move execute_work mutex and condition to class scope 2021-05-20 14:05:40 +02:00
c2ef885a2e Add TODO to improve is_render_registered 2021-05-18 23:58:23 +02:00
ddf70996b2 Add TODO for get_area_of_interest parameter overloading 2021-05-18 23:53:57 +02:00
1d33599e64 Add TODO to remove execute_work workaround. 2021-05-18 23:52:57 +02:00
783c28918b Use mutex lock instead of atomics 2021-05-18 23:32:43 +02:00
c4975c7191 Add TODO to implement MemoryBuffer get_size() 2021-05-18 23:12:13 +02:00
d6e038d90c Fix eNodeTreeExecutionMode typedef 2021-05-17 22:32:13 +02:00
64489ff903 Rename WorkPackage functions to *_fn 2021-05-17 22:28:54 +02:00
3a1e527804 Move assert before assignment 2021-05-17 22:25:10 +02:00
0a62ef0193 Move non NodeOperation responsabilities to FullFrameExecutionModel 2021-05-17 22:23:38 +02:00
f6f1940c45 Rename public getInputOperation to get_input_operation 2021-05-17 20:40:52 +02:00
e71ec02181 Rename multi_update_memory_buffer to update_memory_buffer_partial 2021-05-17 19:39:20 +02:00
76f03636cd Rename get_input_area_of_interest to get_area_of_interest 2021-05-17 19:38:01 +02:00
339d87b8f0 Move is_breaked to base class 2021-05-17 18:47:18 +02:00
1482e8ab03 Cast strange type values instead of normal ones. 2021-05-17 18:44:28 +02:00
b748b732cd Use num_* naming instead of n_* 2021-05-17 18:36:03 +02:00
7e4f53ffd9 Rename OutputStore to SharedOperationBuffers 2021-05-17 18:02:35 +02:00
85a38b38e9 Rename new class data members from m_* to *_ 2021-05-17 12:54:09 +02:00
7ddc3ab3ee Fix multi line comment open tag 2021-05-17 11:11:45 +02:00
502710009d Fix incorrect output border calculation 2021-05-16 10:33:48 +02:00
a5fcacf7b6 Use WorkScheduler::finish() for better perfomance 2021-05-15 13:43:55 +02:00
fc6f5bc0aa Use getInputOperation instead of socket links 2021-05-15 11:05:36 +02:00
acb6e36b75 Comments fixes 2021-05-14 21:53:39 +02:00
0185651831 Merge branch 'master' into cmp-full-frame 2021-05-14 17:13:55 +02:00
cb410346ac Fix comment formatting 2021-05-14 17:10:20 +02:00
c033475f46 Use pthread_signals and atomic counter 2021-05-14 16:52:19 +02:00
4d6dacb8dc Create ExecutionModel classes to reduce complexity 2021-05-14 14:56:16 +02:00
e9b1b49e17 Add a TODO to check operation is constant 2021-05-14 10:53:36 +02:00
d2860a49f6 User prefs option only enable panel option now 2021-05-14 10:44:23 +02:00
aa1aec663d Rename execution_model to execution_mode 2021-05-14 10:27:09 +02:00
633370c0a0 Rename var to has_outputs 2021-05-14 10:03:47 +02:00
982d8b19f5 Refactor render methods into NodeOperation 2021-05-14 00:45:21 +02:00
852fc4b1e9 Improve execute_work code readability 2021-05-13 18:48:47 +02:00
b75e5a0677 Use MIN2 and BLI_rcti_clamp 2021-05-13 18:05:05 +02:00
51025854b6 Use WorkScheduler for getting number of cpu threads 2021-05-13 16:46:58 +02:00
8673d06783 Add panel UI option for execution model 2021-05-13 16:13:58 +02:00
be3e8c6b27 Use work package type for more readable code 2021-05-12 18:17:36 +02:00
a3e5a06bea Add missing copyright notice 2021-05-12 17:19:34 +02:00
6025e482df Fix comments didn't conform style convention. 2021-05-12 17:11:47 +02:00
af6134046e Fix string format compiler warning 2021-05-11 14:03:16 +02:00
9df6486a06 Add phabricator task to UI option 2021-05-10 22:04:15 +02:00
a26966ccc5 Switch back to default ThreadingModel::Queue 2021-05-10 20:57:33 +02:00
7a543203db Take into account single elem buffers 2021-05-10 19:23:46 +02:00
c7274c5146 Move rendering logic to ExecutionSystem
Will make easier future implementations.
2021-05-10 18:50:26 +02:00
80efbb94a2 Merge branch 'master' into cmp-full-frame 2021-05-10 16:36:47 +02:00
569f24b543 Fix UI option label 2021-05-07 20:24:47 +02:00
442520ca09 Merge branch 'master' into cmp-full-frame 2021-05-07 20:08:05 +02:00
9217d073b5 Add TODO reminder to remove single_threaded flag 2021-05-07 20:01:18 +02:00
179901e854 Rename OutputManager to OutputStore 2021-05-07 19:34:40 +02:00
08e8c3fbbf Rename ExecutionModel enum to eExecutionModel 2021-05-07 18:21:00 +02:00
fa5391f9c5 Add UI option in experimental to change execution model 2021-05-07 18:15:19 +02:00
0d96f69510 Merge branch 'master' into cmp-full-frame 2021-05-04 09:59:46 +02:00
ff1fd68d79 Renames 2021-05-01 22:53:42 +02:00
f0d3358e9b Fix comments 2021-05-01 22:45:16 +02:00
ea623890dd Fix graphviz not working for FullFrame 2021-04-29 12:47:38 +02:00
4347e678fc Cleanups 2021-04-29 11:26:05 +02:00
7f6db5dde5 Set MultiThreadedOperation as fullframe operation 2021-04-29 10:46:55 +02:00
fff5aa5a9f [WIP] Compositor: Full-frame base system
This patch adds the base code needed to make the full-frame system work for both current tiled/per-pixel implementation of operations and full-frame.

Two execution models:
- Tiled: Current implementation. Renders execution groups in tiles from outputs to input. Not all operations are buffered. Runs the tiled/per-pixel implementation.
- FullFrame: All operations are buffered. Fully renders operations from inputs to outputs. Runs full-frame implementation of operations if available otherwise the current tiled/per-pixel. Creates output buffers on first read and free them as soon as all its readers have finished, reducing peak memory usage of complex/long trees.

This should allow us to convert operations to full-frame in small steps with the system already working and solve the problem of high memory usage.

FullFrame breaking changes respect Tiled system, mainly:
- Translate, Rotate, Scale, and Transform take effect immediately instead of next buffered operation.
- Any sampling is always done over inputs instead of last buffered operation.

Differential Revision: https://developer.blender.org/D11113
2021-04-28 17:13:04 +02:00
210 changed files with 6548 additions and 1107 deletions

View File

@@ -328,10 +328,14 @@ set(SRC
operations/COM_FastGaussianBlurOperation.h
operations/COM_GammaCorrectOperation.cc
operations/COM_GammaCorrectOperation.h
operations/COM_GaussianAlphaBlurBaseOperation.cc
operations/COM_GaussianAlphaBlurBaseOperation.h
operations/COM_GaussianAlphaXBlurOperation.cc
operations/COM_GaussianAlphaXBlurOperation.h
operations/COM_GaussianAlphaYBlurOperation.cc
operations/COM_GaussianAlphaYBlurOperation.h
operations/COM_GaussianBlurBaseOperation.cc
operations/COM_GaussianBlurBaseOperation.h
operations/COM_GaussianBokehBlurOperation.cc
operations/COM_GaussianBokehBlurOperation.h
operations/COM_GaussianXBlurOperation.cc
@@ -515,6 +519,8 @@ set(SRC
operations/COM_ScaleOperation.h
operations/COM_ScreenLensDistortionOperation.cc
operations/COM_ScreenLensDistortionOperation.h
operations/COM_TransformOperation.cc
operations/COM_TransformOperation.h
operations/COM_TranslateOperation.cc
operations/COM_TranslateOperation.h
operations/COM_WrapOperation.cc

View File

@@ -33,6 +33,8 @@ enum class eExecutionModel {
FullFrame
};
enum class eDimension { X, Y };
/**
* \brief possible data types for sockets
* \ingroup Model
@@ -119,6 +121,9 @@ constexpr float COM_PREVIEW_SIZE = 140.f;
constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
constexpr float COM_BLUR_BOKEH_PIXELS = 512;
constexpr rcti COM_AREA_NONE = {0, 0, 0, 0};
constexpr rcti COM_CONSTANT_INPUT_AREA_OF_INTEREST = COM_AREA_NONE;
constexpr IndexRange XRange(const rcti &area)
{
return IndexRange(area.xmin, area.xmax - area.xmin);

View File

@@ -24,12 +24,7 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type)
{
buffer_ = buffer;
inflated_buffer_ = nullptr;
/* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following
* code to: set_resolution(buffer.get_size()) */
unsigned int resolution[2];
resolution[0] = buffer->getWidth();
resolution[1] = buffer->getHeight();
setResolution(resolution);
set_canvas(buffer->get_rect());
addOutputSocket(data_type);
flags.is_constant_operation = buffer_->is_a_single_elem();
flags.is_fullframe_operation = false;

View File

@@ -456,14 +456,16 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from, const Node
return nullptr;
}
void COM_convert_resolution(NodeOperationBuilder &builder,
NodeOperationOutput *fromSocket,
NodeOperationInput *toSocket)
void COM_convert_canvas(NodeOperationBuilder &builder,
NodeOperationOutput *fromSocket,
NodeOperationInput *toSocket)
{
/* Data type conversions are executed before resolutions to ensure convert operations have
* resolution. This method have to ensure same datatypes are linked for new operations. */
BLI_assert(fromSocket->getDataType() == toSocket->getDataType());
ResizeMode mode = toSocket->getResizeMode();
BLI_assert(mode != ResizeMode::None);
NodeOperation *toOperation = &toSocket->getOperation();
const float toWidth = toOperation->getWidth();
@@ -473,13 +475,12 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
const float fromHeight = fromOperation->getHeight();
bool doCenter = false;
bool doScale = false;
float addX = (toWidth - fromWidth) / 2.0f;
float addY = (toHeight - fromHeight) / 2.0f;
float scaleX = 0;
float scaleY = 0;
switch (mode) {
case ResizeMode::None:
case ResizeMode::Align:
break;
case ResizeMode::Center:
doCenter = true;
@@ -514,63 +515,68 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
break;
}
if (doCenter) {
NodeOperation *first = nullptr;
ScaleOperation *scaleOperation = nullptr;
if (doScale) {
scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType());
scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
first = scaleOperation;
SetValueOperation *sxop = new SetValueOperation();
sxop->setValue(scaleX);
builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1));
SetValueOperation *syop = new SetValueOperation();
syop->setValue(scaleY);
builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2));
builder.addOperation(sxop);
builder.addOperation(syop);
NodeOperation *first = nullptr;
ScaleOperation *scaleOperation = nullptr;
if (doScale) {
scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType());
scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
first = scaleOperation;
SetValueOperation *sxop = new SetValueOperation();
sxop->setValue(scaleX);
builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1));
SetValueOperation *syop = new SetValueOperation();
syop->setValue(scaleY);
builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2));
builder.addOperation(sxop);
builder.addOperation(syop);
unsigned int resolution[2] = {fromOperation->getWidth(), fromOperation->getHeight()};
scaleOperation->setResolution(resolution);
sxop->setResolution(resolution);
syop->setResolution(resolution);
builder.addOperation(scaleOperation);
}
TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType());
translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
if (!first) {
first = translateOperation;
}
SetValueOperation *xop = new SetValueOperation();
xop->setValue(addX);
builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1));
SetValueOperation *yop = new SetValueOperation();
yop->setValue(addY);
builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2));
builder.addOperation(xop);
builder.addOperation(yop);
unsigned int resolution[2] = {toOperation->getWidth(), toOperation->getHeight()};
translateOperation->setResolution(resolution);
xop->setResolution(resolution);
yop->setResolution(resolution);
builder.addOperation(translateOperation);
if (doScale) {
translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
}
/* remove previous link and replace */
builder.removeInputLink(toSocket);
first->getInputSocket(0)->setResizeMode(ResizeMode::None);
toSocket->setResizeMode(ResizeMode::None);
builder.addLink(fromSocket, first->getInputSocket(0));
builder.addLink(translateOperation->getOutputSocket(), toSocket);
unsigned int resolution[2] = {fromOperation->getWidth(), fromOperation->getHeight()};
const rcti &scale_canvas = fromOperation->get_canvas();
scaleOperation->set_canvas(scale_canvas);
sxop->set_canvas(scale_canvas);
syop->set_canvas(scale_canvas);
builder.addOperation(scaleOperation);
}
TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType());
translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
if (!first) {
first = translateOperation;
}
const float addX = doCenter ? (toWidth - fromWidth) / 2.0f : 0.0f;
const float addY = doCenter ? (toHeight - fromHeight) / 2.0f : 0.0f;
SetValueOperation *xop = new SetValueOperation();
xop->setValue(addX);
builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1));
SetValueOperation *yop = new SetValueOperation();
yop->setValue(addY);
builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2));
builder.addOperation(xop);
builder.addOperation(yop);
rcti translate_canvas = toOperation->get_canvas();
if (mode == ResizeMode::Align) {
translate_canvas.xmax = translate_canvas.xmin + fromWidth;
translate_canvas.ymax = translate_canvas.ymin + fromHeight;
}
translateOperation->set_canvas(translate_canvas);
xop->set_canvas(translate_canvas);
yop->set_canvas(translate_canvas);
builder.addOperation(translateOperation);
if (doScale) {
translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
}
/* remove previous link and replace */
builder.removeInputLink(toSocket);
first->getInputSocket(0)->setResizeMode(ResizeMode::None);
toSocket->setResizeMode(ResizeMode::None);
builder.addLink(fromSocket, first->getInputSocket(0));
builder.addLink(translateOperation->getOutputSocket(), toSocket);
}
} // namespace blender::compositor

View File

@@ -65,8 +65,8 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from,
* \note Conversion logic is implemented in this function.
* \see InputSocketResizeMode for the possible conversions.
*/
void COM_convert_resolution(NodeOperationBuilder &builder,
NodeOperationOutput *fromSocket,
NodeOperationInput *toSocket);
void COM_convert_canvas(NodeOperationBuilder &builder,
NodeOperationOutput *fromSocket,
NodeOperationInput *toSocket);
} // namespace blender::compositor

View File

@@ -162,8 +162,10 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
len += snprintf(str + len,
maxlen > len ? maxlen - len : 0,
"#%d (%u,%u)",
"#%d (%i,%i) (%u,%u)",
operation->get_id(),
operation->get_canvas().xmin,
operation->get_canvas().ymin,
operation->getWidth(),
operation->getHeight());

View File

@@ -74,37 +74,61 @@ void FullFrameExecutionModel::determine_areas_to_render_and_reads()
}
}
Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op)
/**
* Returns input buffers with an offset relative to given output coordinates. Returned memory
* buffers must be deleted.
*/
Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op,
const int output_x,
const int output_y)
{
const int num_inputs = op->getNumberOfInputSockets();
Vector<MemoryBuffer *> inputs_buffers(num_inputs);
for (int i = 0; i < num_inputs; i++) {
NodeOperation *input_op = op->get_input_operation(i);
inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op);
NodeOperation *input = op->get_input_operation(i);
const int offset_x = (input->get_canvas().xmin - op->get_canvas().xmin) + output_x;
const int offset_y = (input->get_canvas().ymin - op->get_canvas().ymin) + output_y;
MemoryBuffer *buf = active_buffers_.get_rendered_buffer(input);
rcti rect = buf->get_rect();
BLI_rcti_translate(&rect, offset_x, offset_y);
inputs_buffers[i] = new MemoryBuffer(
buf->getBuffer(), buf->get_num_channels(), rect, buf->is_a_single_elem());
}
return inputs_buffers;
}
MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op)
MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op,
const int output_x,
const int output_y)
{
rcti op_rect;
BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight());
rcti rect;
BLI_rcti_init(&rect, output_x, output_x + op->getWidth(), output_y, output_y + op->getHeight());
const DataType data_type = op->getOutputSocket(0)->getDataType();
const bool is_a_single_elem = op->get_flags().is_constant_operation;
return new MemoryBuffer(data_type, op_rect, is_a_single_elem);
return new MemoryBuffer(data_type, rect, is_a_single_elem);
}
void FullFrameExecutionModel::render_operation(NodeOperation *op)
{
Vector<MemoryBuffer *> input_bufs = get_input_buffers(op);
/* Output has no offset for easier image algorithms implementation on operations. */
constexpr int output_x = 0;
constexpr int output_y = 0;
const bool has_outputs = op->getNumberOfOutputSockets() > 0;
MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr;
MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op, output_x, output_y) : nullptr;
if (op->getWidth() > 0 && op->getHeight() > 0) {
Span<rcti> areas = active_buffers_.get_areas_to_render(op);
Vector<MemoryBuffer *> input_bufs = get_input_buffers(op, output_x, output_y);
const int op_offset_x = output_x - op->get_canvas().xmin;
const int op_offset_y = output_y - op->get_canvas().ymin;
Span<rcti> areas = active_buffers_.get_areas_to_render(op, op_offset_x, op_offset_y);
op->render(op_buf, areas, input_bufs);
DebugInfo::operation_rendered(op, op_buf);
for (MemoryBuffer *buf : input_bufs) {
delete buf;
}
}
/* Even if operation has no resolution set the empty buffer. It will be clipped with a
* TranslateOperation from convert resolutions if linked to an operation with resolution. */
@@ -199,14 +223,15 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op
const int num_inputs = operation->getNumberOfInputSockets();
for (int i = 0; i < num_inputs; i++) {
NodeOperation *input_op = operation->get_input_operation(i);
rcti input_op_rect, input_area;
BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight());
rcti input_area;
operation->get_area_of_interest(input_op, render_area, input_area);
/* Ensure area of interest is within operation bounds, cropping areas outside. */
BLI_rcti_isect(&input_area, &input_op_rect, &input_area);
BLI_rcti_isect(&input_area, &input_op->get_canvas(), &input_area);
stack.append({input_op, input_area});
if (!BLI_rcti_is_empty(&input_area)) {
stack.append({input_op, input_area});
}
}
}
}
@@ -243,9 +268,8 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r
BLI_assert(output_op->isOutputOperation(context_.isRendering()));
/* By default return operation bounds (no border). */
const int op_width = output_op->getWidth();
const int op_height = output_op->getHeight();
BLI_rcti_init(&r_area, 0, op_width, 0, op_height);
rcti canvas = output_op->get_canvas();
r_area = canvas;
const bool has_viewer_border = border_.use_viewer_border &&
(output_op->get_flags().is_viewer_operation ||
@@ -255,12 +279,13 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r
/* Get border with normalized coordinates. */
const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border;
/* Return de-normalized border. */
BLI_rcti_init(&r_area,
norm_border->xmin * op_width,
norm_border->xmax * op_width,
norm_border->ymin * op_height,
norm_border->ymax * op_height);
/* Return de-normalized border within canvas. */
const int w = output_op->getWidth();
const int h = output_op->getHeight();
r_area.xmin = canvas.xmin + norm_border->xmin * w;
r_area.xmax = canvas.xmin + norm_border->xmax * w;
r_area.ymin = canvas.ymin + norm_border->ymin * h;
r_area.ymax = canvas.ymin + norm_border->ymax * h;
}
}

View File

@@ -61,8 +61,10 @@ class FullFrameExecutionModel : public ExecutionModel {
void determine_areas_to_render_and_reads();
void render_operations();
void render_output_dependencies(NodeOperation *output_op);
Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op);
MemoryBuffer *create_operation_buffer(NodeOperation *op);
Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op,
const int output_x,
const int output_y);
MemoryBuffer *create_operation_buffer(NodeOperation *op, const int output_x, const int output_y);
void render_operation(NodeOperation *op);
void operation_finished(NodeOperation *operation);

View File

@@ -122,6 +122,8 @@ void MemoryBuffer::set_strides()
this->elem_stride = m_num_channels;
this->row_stride = getWidth() * m_num_channels;
}
to_positive_x_stride_ = m_rect.xmin < 0 ? -m_rect.xmin + 1 : (m_rect.xmin == 0 ? 1 : 0);
to_positive_y_stride_ = m_rect.ymin < 0 ? -m_rect.ymin + 1 : (m_rect.ymin == 0 ? 1 : 0);
}
void MemoryBuffer::clear()
@@ -245,7 +247,9 @@ void MemoryBuffer::copy_from(const MemoryBuffer *src,
void MemoryBuffer::copy_from(const uchar *src, const rcti &area)
{
copy_from(src, area, 0, this->get_num_channels(), this->get_num_channels(), 0);
const int elem_stride = this->get_num_channels();
const int row_stride = elem_stride * getWidth();
copy_from(src, area, 0, this->get_num_channels(), elem_stride, row_stride, 0);
}
void MemoryBuffer::copy_from(const uchar *src,
@@ -253,10 +257,18 @@ void MemoryBuffer::copy_from(const uchar *src,
const int channel_offset,
const int elem_size,
const int elem_stride,
const int row_stride,
const int to_channel_offset)
{
copy_from(
src, area, channel_offset, elem_size, elem_stride, area.xmin, area.ymin, to_channel_offset);
copy_from(src,
area,
channel_offset,
elem_size,
elem_stride,
row_stride,
area.xmin,
area.ymin,
to_channel_offset);
}
void MemoryBuffer::copy_from(const uchar *src,
@@ -264,6 +276,7 @@ void MemoryBuffer::copy_from(const uchar *src,
const int channel_offset,
const int elem_size,
const int elem_stride,
const int row_stride,
const int to_x,
const int to_y,
const int to_channel_offset)
@@ -273,10 +286,9 @@ void MemoryBuffer::copy_from(const uchar *src,
const int width = BLI_rcti_size_x(&area);
const int height = BLI_rcti_size_y(&area);
const int src_row_stride = width * elem_stride;
const uchar *const src_start = src + area.ymin * src_row_stride + channel_offset;
const uchar *const src_start = src + area.ymin * row_stride + channel_offset;
for (int y = 0; y < height; y++) {
const uchar *from_elem = src_start + y * src_row_stride;
const uchar *from_elem = src_start + y * row_stride + area.xmin * elem_stride;
float *to_elem = &this->get_value(to_x, to_y + y, to_channel_offset);
const float *row_end = to_elem + width * this->elem_stride;
while (to_elem < row_end) {
@@ -346,7 +358,16 @@ void MemoryBuffer::copy_from(const ImBuf *src,
else if (src->rect) {
const uchar *uc_buf = (uchar *)src->rect;
const int elem_stride = src->channels;
copy_from(uc_buf, area, channel_offset, elem_size, elem_stride, to_x, to_y, to_channel_offset);
const int row_stride = elem_stride * src->x;
copy_from(uc_buf,
area,
channel_offset,
elem_size,
elem_stride,
row_stride,
to_x,
to_y,
to_channel_offset);
if (ensure_linear_space) {
colorspace_to_scene_linear(this, area, src->rect_colorspace);
}
@@ -405,12 +426,48 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4])
}
}
static void read_ewa_elem(void *userdata, int x, int y, float result[4])
{
const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
buffer->read_elem_checked(x, y, result);
}
void MemoryBuffer::read_elem_filtered(
const float x, const float y, float dx[2], float dy[2], float *out) const
{
BLI_assert(this->m_datatype == DataType::Color);
const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}};
float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
/* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
* but compositor uses pixel space. For now let's just divide the values and
* switch compositor to normalized space for EWA later.
*/
float uv_normal[2] = {get_relative_x(x) * inv_width, get_relative_y(y) * inv_height};
float du_normal[2] = {deriv[0][0] * inv_width, deriv[0][1] * inv_height};
float dv_normal[2] = {deriv[1][0] * inv_width, deriv[1][1] * inv_height};
BLI_ewa_filter(this->getWidth(),
this->getHeight(),
false,
true,
uv_normal,
du_normal,
dv_normal,
read_ewa_elem,
const_cast<MemoryBuffer *>(this),
out);
}
/* TODO(manzanilla): To be removed with tiled implementation. */
static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4])
{
MemoryBuffer *buffer = (MemoryBuffer *)userdata;
buffer->read(result, x, y);
}
/* TODO(manzanilla): To be removed with tiled implementation. */
void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2])
{
if (m_is_a_single_elem) {

View File

@@ -114,6 +114,12 @@ class MemoryBuffer {
*/
bool owns_data_;
/** Stride to make any x coordinate within buffer positive (non-zero). */
int to_positive_x_stride_;
/** Stride to make any y coordinate within buffer positive (non-zero). */
int to_positive_y_stride_;
public:
/**
* \brief construct new temporarily MemoryBuffer for an area
@@ -176,7 +182,7 @@ class MemoryBuffer {
*/
float *get_elem(int x, int y)
{
BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
BLI_assert(has_coords(x, y));
return m_buffer + get_coords_offset(x, y);
}
@@ -185,7 +191,7 @@ class MemoryBuffer {
*/
const float *get_elem(int x, int y) const
{
BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
BLI_assert(has_coords(x, y));
return m_buffer + get_coords_offset(x, y);
}
@@ -194,27 +200,94 @@ class MemoryBuffer {
memcpy(out, get_elem(x, y), m_num_channels * sizeof(float));
}
void read_elem_checked(int x, int y, float *out) const
{
if (!has_coords(x, y)) {
clear_elem(out);
}
else {
read_elem(x, y, out);
}
}
void read_elem_checked(float x, float y, float *out) const
{
read_elem_checked(floor_x(x), floor_y(y), out);
}
void read_elem_bilinear(float x, float y, float *out) const
{
/* Only clear past +/-1 borders to be able to smooth edges. */
if (x <= m_rect.xmin - 1.0f || x >= m_rect.xmax || y <= m_rect.ymin - 1.0f ||
y >= m_rect.ymax) {
clear_elem(out);
return;
}
if (m_is_a_single_elem) {
if (x >= m_rect.xmin && x < m_rect.xmax - 1.0f && y >= m_rect.ymin &&
y < m_rect.ymax - 1.0f) {
memcpy(out, m_buffer, get_elem_bytes_len());
return;
}
/* Do sampling at borders to smooth edges. */
const float last_x = getWidth() - 1.0f;
const float rel_x = get_relative_x(x);
float single_x = 0.0f;
if (rel_x < 0.0f) {
single_x = rel_x;
}
else if (rel_x > last_x) {
single_x = rel_x - last_x;
}
const float last_y = getHeight() - 1.0f;
const float rel_y = get_relative_y(y);
float single_y = 0.0f;
if (rel_y < 0.0f) {
single_y = rel_y;
}
else if (rel_y > last_y) {
single_y = rel_y - last_y;
}
BLI_bilinear_interpolation_fl(m_buffer, out, 1, 1, m_num_channels, single_x, single_y);
return;
}
BLI_bilinear_interpolation_fl(m_buffer,
out,
getWidth(),
getHeight(),
m_num_channels,
get_relative_x(x),
get_relative_y(y));
}
void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const
{
switch (sampler) {
case PixelSampler::Nearest:
this->read_elem(x, y, out);
read_elem_checked(x, y, out);
break;
case PixelSampler::Bilinear:
case PixelSampler::Bicubic:
/* No bicubic. Current implementation produces fuzzy results. */
this->readBilinear(out, x, y);
read_elem_bilinear(x, y, out);
break;
}
}
void read_elem_filtered(
const float x, const float y, float dx[2], float dy[2], float *out) const;
/**
* Get channel value at given coordinates.
*/
float &get_value(int x, int y, int channel)
{
BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
channel >= 0 && channel < m_num_channels);
BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels);
return m_buffer[get_coords_offset(x, y) + channel];
}
@@ -223,8 +296,7 @@ class MemoryBuffer {
*/
const float &get_value(int x, int y, int channel) const
{
BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
channel >= 0 && channel < m_num_channels);
BLI_assert(has_coords(x, y) && channel >= 0 && channel < m_num_channels);
return m_buffer[get_coords_offset(x, y) + channel];
}
@@ -233,7 +305,7 @@ class MemoryBuffer {
*/
const float *get_row_end(int y) const
{
BLI_assert(y >= 0 && y < getHeight());
BLI_assert(has_y(y));
return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y));
}
@@ -300,6 +372,12 @@ class MemoryBuffer {
return this->m_buffer;
}
float *release_buffer()
{
owns_data_ = false;
return this->m_buffer;
}
MemoryBuffer *inflate() const;
inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y)
@@ -403,6 +481,8 @@ class MemoryBuffer {
y = y + m_rect.ymin;
}
/* TODO(manzanilla): To be removed with tiled implementation. For applying `MemoryBufferExtend`
* use `wrap_pixel`. */
inline void read(float *result,
int x,
int y,
@@ -425,6 +505,7 @@ class MemoryBuffer {
}
}
/* TODO(manzanilla): To be removed with tiled implementation. */
inline void readNoCheck(float *result,
int x,
int y,
@@ -507,12 +588,14 @@ class MemoryBuffer {
int channel_offset,
int elem_size,
int elem_stride,
int row_stride,
int to_channel_offset);
void copy_from(const uchar *src,
const rcti &area,
int channel_offset,
int elem_size,
int elem_stride,
int row_stride,
int to_x,
int to_y,
int to_channel_offset);
@@ -582,6 +665,48 @@ class MemoryBuffer {
return get_memory_width() * get_memory_height();
}
void clear_elem(float *out) const
{
memset(out, 0, this->m_num_channels * sizeof(float));
}
template<typename T> T get_relative_x(T x) const
{
return x - m_rect.xmin;
}
template<typename T> T get_relative_y(T y) const
{
return y - m_rect.ymin;
}
template<typename T> bool has_coords(T x, T y) const
{
return has_x(x) && has_y(y);
}
template<typename T> bool has_x(T x) const
{
return x >= m_rect.xmin && x < m_rect.xmax;
}
template<typename T> bool has_y(T y) const
{
return y >= m_rect.ymin && y < m_rect.ymax;
}
/* Fast floor functions. The caller should check result is within buffer bounds. It ceils in near
* cases and when given coordinate is negative and less than buffer rect `min - 1`. */
int floor_x(float x) const
{
return (int)(x + to_positive_x_stride_) - to_positive_x_stride_;
}
int floor_y(float y) const
{
return (int)(y + to_positive_y_stride_) - to_positive_y_stride_;
}
void copy_single_elem_from(const MemoryBuffer *src,
int channel_offset,
int elem_size,

View File

@@ -248,7 +248,9 @@ void NodeGraph::add_proxies_group_inputs(bNode *b_node, bNode *b_node_io)
}
}
void NodeGraph::add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool use_buffer)
void NodeGraph::add_proxies_group_outputs(const CompositorContext &context,
bNode *b_node,
bNode *b_node_io)
{
bNodeTree *b_group_tree = (bNodeTree *)b_node->id;
BLI_assert(b_group_tree); /* should have been checked in advance */
@@ -261,7 +263,8 @@ void NodeGraph::add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool
b_sock_io = b_sock_io->next) {
bNodeSocket *b_sock_group = find_b_node_output(b_node, b_sock_io->identifier);
if (b_sock_group) {
if (use_buffer) {
if (context.isGroupnodeBufferEnabled() &&
context.get_execution_model() == eExecutionModel::Tiled) {
SocketBufferNode *buffer = new SocketBufferNode(b_node_io, b_sock_io, b_sock_group);
add_node(buffer, b_group_tree, key, is_active_group);
}
@@ -297,7 +300,7 @@ void NodeGraph::add_proxies_group(const CompositorContext &context,
}
if (b_node_io->type == NODE_GROUP_OUTPUT && (b_node_io->flag & NODE_DO_OUTPUT)) {
add_proxies_group_outputs(b_node, b_node_io, context.isGroupnodeBufferEnabled());
add_proxies_group_outputs(context, b_node, b_node_io);
}
}

View File

@@ -107,7 +107,9 @@ class NodeGraph {
bool is_active_group);
void add_proxies_group_inputs(bNode *b_node, bNode *b_node_io);
void add_proxies_group_outputs(bNode *b_node, bNode *b_node_io, bool use_buffer);
void add_proxies_group_outputs(const CompositorContext &context,
bNode *b_node,
bNode *b_node_io);
void add_proxies_group(const CompositorContext &context, bNode *b_node, bNodeInstanceKey key);
void add_proxies_reroute(bNodeTree *b_ntree,

View File

@@ -35,9 +35,8 @@ namespace blender::compositor {
NodeOperation::NodeOperation()
{
this->m_resolutionInputSocketIndex = 0;
this->m_width = 0;
this->m_height = 0;
canvas_input_index_ = 0;
canvas_ = COM_AREA_NONE;
this->m_btree = nullptr;
}
@@ -61,44 +60,46 @@ void NodeOperation::addOutputSocket(DataType datatype)
m_outputs.append(NodeOperationOutput(this, datatype));
}
void NodeOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
void NodeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
unsigned int used_resolution_index = 0;
if (m_resolutionInputSocketIndex == RESOLUTION_INPUT_ANY) {
unsigned int used_canvas_index = 0;
if (canvas_input_index_ == RESOLUTION_INPUT_ANY) {
for (NodeOperationInput &input : m_inputs) {
unsigned int any_resolution[2] = {0, 0};
input.determineResolution(any_resolution, preferredResolution);
if (any_resolution[0] * any_resolution[1] > 0) {
resolution[0] = any_resolution[0];
resolution[1] = any_resolution[1];
rcti any_area = COM_AREA_NONE;
const bool determined = input.determine_canvas(preferred_area, any_area);
if (determined) {
r_area = any_area;
break;
}
used_resolution_index += 1;
used_canvas_index += 1;
}
}
else if (m_resolutionInputSocketIndex < m_inputs.size()) {
NodeOperationInput &input = m_inputs[m_resolutionInputSocketIndex];
input.determineResolution(resolution, preferredResolution);
used_resolution_index = m_resolutionInputSocketIndex;
else if (canvas_input_index_ < m_inputs.size()) {
NodeOperationInput &input = m_inputs[canvas_input_index_];
input.determine_canvas(preferred_area, r_area);
used_canvas_index = canvas_input_index_;
}
unsigned int temp2[2] = {resolution[0], resolution[1]};
unsigned int temp[2];
if (modify_determined_canvas_fn_) {
modify_determined_canvas_fn_(r_area);
}
rcti unused_area;
const rcti &local_preferred_area = r_area;
for (unsigned int index = 0; index < m_inputs.size(); index++) {
if (index == used_resolution_index) {
if (index == used_canvas_index) {
continue;
}
NodeOperationInput &input = m_inputs[index];
if (input.isConnected()) {
input.determineResolution(temp, temp2);
input.determine_canvas(local_preferred_area, unused_area);
}
}
}
void NodeOperation::setResolutionInputSocketIndex(unsigned int index)
void NodeOperation::set_canvas_input_index(unsigned int index)
{
this->m_resolutionInputSocketIndex = index;
this->canvas_input_index_ = index;
}
void NodeOperation::init_data()
@@ -134,6 +135,23 @@ void NodeOperation::deinitExecution()
{
/* pass */
}
void NodeOperation::set_canvas(const rcti &canvas_area)
{
canvas_ = canvas_area;
flags.is_canvas_set = true;
}
const rcti &NodeOperation::get_canvas() const
{
return canvas_;
}
void NodeOperation::unset_canvas()
{
flags.is_canvas_set = false;
}
SocketReader *NodeOperation::getInputSocketReader(unsigned int inputSocketIndex)
{
return this->getInputSocket(inputSocketIndex)->getReader();
@@ -209,7 +227,7 @@ void NodeOperation::get_area_of_interest(const int input_idx,
/* Non full-frame operations never implement this method. To ensure correctness assume
* whole area is used. */
NodeOperation *input_op = getInputOperation(input_idx);
BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight());
r_input_area = input_op->get_canvas();
}
}
@@ -369,12 +387,16 @@ SocketReader *NodeOperationInput::getReader()
return nullptr;
}
void NodeOperationInput::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
/**
* \return Whether canvas area could be determined.
*/
bool NodeOperationInput::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (m_link) {
m_link->determineResolution(resolution, preferredResolution);
m_link->determine_canvas(preferred_area, r_area);
return !BLI_rcti_is_empty(&r_area);
}
return false;
}
/******************
@@ -386,18 +408,16 @@ NodeOperationOutput::NodeOperationOutput(NodeOperation *op, DataType datatype)
{
}
void NodeOperationOutput::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
void NodeOperationOutput::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
NodeOperation &operation = getOperation();
if (operation.get_flags().is_resolution_set) {
resolution[0] = operation.getWidth();
resolution[1] = operation.getHeight();
if (operation.get_flags().is_canvas_set) {
r_area = operation.get_canvas();
}
else {
operation.determineResolution(resolution, preferredResolution);
if (resolution[0] > 0 && resolution[1] > 0) {
operation.setResolution(resolution);
operation.determine_canvas(preferred_area, r_area);
if (!BLI_rcti_is_empty(&r_area)) {
operation.set_canvas(r_area);
}
}
}
@@ -419,8 +439,8 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operat
if (node_operation_flags.use_viewer_border) {
os << "view_border,";
}
if (node_operation_flags.is_resolution_set) {
os << "resolution_set,";
if (node_operation_flags.is_canvas_set) {
os << "canvas_set,";
}
if (node_operation_flags.is_set_operation) {
os << "set_operation,";

View File

@@ -60,9 +60,13 @@ enum class ResizeMode {
/** \brief Center the input image to the center of the working area of the node, no resizing
* occurs */
Center = NS_CR_CENTER,
/** \brief The bottom left of the input image is the bottom left of the working area of the node,
* no resizing occurs */
/** No resizing or translation. */
None = NS_CR_NONE,
/**
* Input image is translated so that its bottom left matches the bottom left of the working area
* of the node, no resizing occurs.
*/
Align = 100,
/** \brief Fit the width of the input image to the width of the working area of the node */
FitWidth = NS_CR_FIT_WIDTH,
/** \brief Fit the height of the input image to the height of the working area of the node */
@@ -128,7 +132,7 @@ class NodeOperationInput {
SocketReader *getReader();
void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
bool determine_canvas(const rcti &preferred_area, rcti &r_area);
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
@@ -156,12 +160,7 @@ class NodeOperationOutput {
return m_datatype;
}
/**
* \brief determine the resolution of this data going through this socket
* \param resolution: the result of this operation
* \param preferredResolution: the preferable resolution as no resolution could be determined
*/
void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
void determine_canvas(const rcti &preferred_area, rcti &r_area);
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
@@ -209,9 +208,9 @@ struct NodeOperationFlags {
bool use_viewer_border : 1;
/**
* Is the resolution of the operation set.
* Is the canvas of the operation set.
*/
bool is_resolution_set : 1;
bool is_canvas_set : 1;
/**
* Is this a set operation (value, color, vector).
@@ -255,7 +254,7 @@ struct NodeOperationFlags {
open_cl = false;
use_render_border = false;
use_viewer_border = false;
is_resolution_set = false;
is_canvas_set = false;
is_set_operation = false;
is_read_buffer_operation = false;
is_write_buffer_operation = false;
@@ -283,9 +282,11 @@ class NodeOperation {
Vector<NodeOperationOutput> m_outputs;
/**
* \brief the index of the input socket that will be used to determine the resolution
* \brief the index of the input socket that will be used to determine the canvas
*/
unsigned int m_resolutionInputSocketIndex;
unsigned int canvas_input_index_;
std::function<void(rcti &canvas)> modify_determined_canvas_fn_;
/**
* \brief mutex reference for very special node initializations
@@ -309,15 +310,7 @@ class NodeOperation {
*/
eExecutionModel execution_model_;
/**
* Width of the output of this operation.
*/
unsigned int m_width;
/**
* Height of the output of this operation.
*/
unsigned int m_height;
rcti canvas_;
/**
* Flags how to evaluate this operation.
@@ -331,10 +324,8 @@ class NodeOperation {
{
}
void set_execution_model(const eExecutionModel model)
{
execution_model_ = model;
}
/* Renturns false when input is not constant */
bool get_input_constant(const int input_idx, float *r_constant);
void set_name(const std::string name)
{
@@ -379,14 +370,7 @@ class NodeOperation {
return getInputOperation(index);
}
/**
* \brief determine the resolution of this node
* \note this method will not set the resolution, this is the responsibility of the caller
* \param resolution: the result of this operation
* \param preferredResolution: the preferable resolution as no resolution could be determined
*/
virtual void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]);
virtual void determine_canvas(const rcti &preferred_area, rcti &r_area);
/**
* \brief isOutputOperation determines whether this operation is an output of the
@@ -408,6 +392,11 @@ class NodeOperation {
return false;
}
void set_execution_model(const eExecutionModel model)
{
execution_model_ = model;
}
void setbNodeTree(const bNodeTree *tree)
{
this->m_btree = tree;
@@ -482,18 +471,9 @@ class NodeOperation {
}
virtual void deinitExecution();
/**
* \brief set the resolution
* \param resolution: the resolution to set
*/
void setResolution(unsigned int resolution[2])
{
if (!this->flags.is_resolution_set) {
this->m_width = resolution[0];
this->m_height = resolution[1];
this->flags.is_resolution_set = true;
}
}
void set_canvas(const rcti &canvas_area);
const rcti &get_canvas() const;
void unset_canvas();
/**
* \brief is this operation the active viewer output
@@ -512,10 +492,19 @@ class NodeOperation {
rcti *output);
/**
* \brief set the index of the input socket that will determine the resolution of this
* \brief set the index of the input socket that will determine the canvas of this
* operation \param index: the index to set
*/
void setResolutionInputSocketIndex(unsigned int index);
void set_canvas_input_index(unsigned int index);
/**
* Set a custom function to modify determined canvas from main input just before setting it
* as preferred for the other inputs.
*/
void set_determined_canvas_modifier(std::function<void(rcti &canvas)> fn)
{
modify_determined_canvas_fn_ = fn;
}
/**
* \brief get the render priority of this node.
@@ -541,12 +530,12 @@ class NodeOperation {
unsigned int getWidth() const
{
return m_width;
return BLI_rcti_size_x(&get_canvas());
}
unsigned int getHeight() const
{
return m_height;
return BLI_rcti_size_y(&get_canvas());
}
inline void readSampled(float result[4], float x, float y, PixelSampler sampler)
@@ -616,16 +605,18 @@ class NodeOperation {
void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center);
void addOutputSocket(DataType datatype);
/* TODO(manzanilla): to be removed with tiled implementation. */
void setWidth(unsigned int width)
{
this->m_width = width;
this->flags.is_resolution_set = true;
canvas_.xmax = canvas_.xmin + width;
this->flags.is_canvas_set = true;
}
void setHeight(unsigned int height)
{
this->m_height = height;
this->flags.is_resolution_set = true;
canvas_.ymax = canvas_.ymin + height;
this->flags.is_canvas_set = true;
}
SocketReader *getInputSocketReader(unsigned int inputSocketindex);
NodeOperation *getInputOperation(unsigned int inputSocketindex);

View File

@@ -33,6 +33,7 @@
#include "COM_SetValueOperation.h"
#include "COM_SetVectorOperation.h"
#include "COM_SocketProxyOperation.h"
#include "COM_TranslateOperation.h"
#include "COM_ViewerOperation.h"
#include "COM_WriteBufferOperation.h"
@@ -109,7 +110,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
folder.fold_operations();
}
determineResolutions();
determine_canvases();
if (m_context->get_execution_model() == eExecutionModel::Tiled) {
/* surround complex ops with read/write buffer */
@@ -417,41 +418,40 @@ void NodeOperationBuilder::resolve_proxies()
}
}
void NodeOperationBuilder::determineResolutions()
void NodeOperationBuilder::determine_canvases()
{
/* determine all resolutions of the operations (Width/Height) */
/* Determine all canvas areas of the operations. */
const rcti &preferred_area = COM_AREA_NONE;
for (NodeOperation *op : m_operations) {
if (op->isOutputOperation(m_context->isRendering()) && !op->get_flags().is_preview_operation) {
unsigned int resolution[2] = {0, 0};
unsigned int preferredResolution[2] = {0, 0};
op->determineResolution(resolution, preferredResolution);
op->setResolution(resolution);
rcti canvas = COM_AREA_NONE;
op->determine_canvas(preferred_area, canvas);
op->set_canvas(canvas);
}
}
for (NodeOperation *op : m_operations) {
if (op->isOutputOperation(m_context->isRendering()) && op->get_flags().is_preview_operation) {
unsigned int resolution[2] = {0, 0};
unsigned int preferredResolution[2] = {0, 0};
op->determineResolution(resolution, preferredResolution);
op->setResolution(resolution);
rcti canvas = COM_AREA_NONE;
op->determine_canvas(preferred_area, canvas);
op->set_canvas(canvas);
}
}
/* add convert resolution operations when needed */
/* Convert operation canvases when needed. */
{
Vector<Link> convert_links;
for (const Link &link : m_links) {
if (link.to()->getResizeMode() != ResizeMode::None) {
NodeOperation &from_op = link.from()->getOperation();
NodeOperation &to_op = link.to()->getOperation();
if (from_op.getWidth() != to_op.getWidth() || from_op.getHeight() != to_op.getHeight()) {
if (!BLI_rcti_compare(&from_op.get_canvas(), &to_op.get_canvas())) {
convert_links.append(link);
}
}
}
for (const Link &link : convert_links) {
COM_convert_resolution(*this, link.from(), link.to());
COM_convert_canvas(*this, link.from(), link.to());
}
}
}

View File

@@ -145,8 +145,8 @@ class NodeOperationBuilder {
/** Replace proxy operations with direct links */
void resolve_proxies();
/** Calculate resolution for each operation */
void determineResolutions();
/** Calculate canvas area for each operation. */
void determine_canvases();
/** Helper function to store connected inputs for replacement */
Vector<NodeOperationInput *> cache_output_links(NodeOperationOutput *output) const;

View File

@@ -76,9 +76,17 @@ void SharedOperationBuffers::register_read(NodeOperation *read_op)
/**
* Get registered areas given operation needs to render.
*/
blender::Span<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op)
Vector<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op,
const int offset_x,
const int offset_y)
{
return get_buffer_data(op).render_areas.as_span();
Span<rcti> render_areas = get_buffer_data(op).render_areas.as_span();
Vector<rcti> dst_areas;
for (rcti dst : render_areas) {
BLI_rcti_translate(&dst, offset_x, offset_y);
dst_areas.append(std::move(dst));
}
return dst_areas;
}
/**

View File

@@ -53,7 +53,7 @@ class SharedOperationBuffers {
bool has_registered_reads(NodeOperation *op);
void register_read(NodeOperation *read_op);
blender::Span<rcti> get_areas_to_render(NodeOperation *op);
Vector<rcti> get_areas_to_render(NodeOperation *op, int offset_x, int offset_y);
bool is_operation_rendered(NodeOperation *op);
void set_rendered_buffer(NodeOperation *op, std::unique_ptr<MemoryBuffer> buffer);
MemoryBuffer *get_rendered_buffer(NodeOperation *op);

View File

@@ -51,13 +51,13 @@ void AlphaOverNode::convertToOperations(NodeConverter &converter,
convertProg->setUseValueAlphaMultiply(false);
if (color1Socket->isLinked()) {
convertProg->setResolutionInputSocketIndex(1);
convertProg->set_canvas_input_index(1);
}
else if (color2Socket->isLinked()) {
convertProg->setResolutionInputSocketIndex(2);
convertProg->set_canvas_input_index(2);
}
else {
convertProg->setResolutionInputSocketIndex(0);
convertProg->set_canvas_input_index(0);
}
converter.addOperation(convertProg);

View File

@@ -62,7 +62,7 @@ void BoxMaskNode::convertToOperations(NodeConverter &converter,
scaleOperation->setOffset(0.0f, 0.0f);
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align);
converter.addOperation(scaleOperation);
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));

View File

@@ -37,16 +37,16 @@ void CombineColorNode::convertToOperations(NodeConverter &converter,
CombineChannelsOperation *operation = new CombineChannelsOperation();
if (inputRSocket->isLinked()) {
operation->setResolutionInputSocketIndex(0);
operation->set_canvas_input_index(0);
}
else if (inputGSocket->isLinked()) {
operation->setResolutionInputSocketIndex(1);
operation->set_canvas_input_index(1);
}
else if (inputBSocket->isLinked()) {
operation->setResolutionInputSocketIndex(2);
operation->set_canvas_input_index(2);
}
else {
operation->setResolutionInputSocketIndex(3);
operation->set_canvas_input_index(3);
}
converter.addOperation(operation);

View File

@@ -62,7 +62,7 @@ void EllipseMaskNode::convertToOperations(NodeConverter &converter,
scaleOperation->setOffset(0.0f, 0.0f);
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::Align);
converter.addOperation(scaleOperation);
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));

View File

@@ -66,7 +66,7 @@ void GlareNode::convertToOperations(NodeConverter &converter,
mixvalueoperation->setValue(glare->mix);
MixGlareOperation *mixoperation = new MixGlareOperation();
mixoperation->setResolutionInputSocketIndex(1);
mixoperation->set_canvas_input_index(1);
mixoperation->getInputSocket(2)->setResizeMode(ResizeMode::FitAny);
converter.addOperation(glareoperation);

View File

@@ -53,7 +53,7 @@ void HueSaturationValueCorrectNode::convertToOperations(
converter.addOperation(changeHSV);
MixBlendOperation *blend = new MixBlendOperation();
blend->setResolutionInputSocketIndex(1);
blend->set_canvas_input_index(1);
converter.addOperation(blend);
converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0));

View File

@@ -56,7 +56,7 @@ void HueSaturationValueNode::convertToOperations(NodeConverter &converter,
converter.addOperation(changeHSV);
MixBlendOperation *blend = new MixBlendOperation();
blend->setResolutionInputSocketIndex(1);
blend->set_canvas_input_index(1);
converter.addOperation(blend);
converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0));

View File

@@ -34,7 +34,7 @@ void MapUVNode::convertToOperations(NodeConverter &converter,
MapUVOperation *operation = new MapUVOperation();
operation->setAlpha((float)node->custom1);
operation->setResolutionInputSocketIndex(1);
operation->set_canvas_input_index(1);
converter.addOperation(operation);
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));

View File

@@ -30,20 +30,31 @@ RotateNode::RotateNode(bNode *editorNode) : Node(editorNode)
}
void RotateNode::convertToOperations(NodeConverter &converter,
const CompositorContext & /*context*/) const
const CompositorContext &context) const
{
NodeInput *inputSocket = this->getInputSocket(0);
NodeInput *inputDegreeSocket = this->getInputSocket(1);
NodeOutput *outputSocket = this->getOutputSocket(0);
RotateOperation *operation = new RotateOperation();
SetSamplerOperation *sampler = new SetSamplerOperation();
sampler->setSampler((PixelSampler)this->getbNode()->custom1);
converter.addOperation(sampler);
converter.addOperation(operation);
converter.addLink(sampler->getOutputSocket(), operation->getInputSocket(0));
converter.mapInputSocket(inputSocket, sampler->getInputSocket(0));
PixelSampler sampler = (PixelSampler)this->getbNode()->custom1;
switch (context.get_execution_model()) {
case eExecutionModel::Tiled: {
SetSamplerOperation *sampler_op = new SetSamplerOperation();
sampler_op->setSampler(sampler);
converter.addOperation(sampler_op);
converter.addLink(sampler_op->getOutputSocket(), operation->getInputSocket(0));
converter.mapInputSocket(inputSocket, sampler_op->getInputSocket(0));
break;
}
case eExecutionModel::FullFrame: {
operation->set_sampler(sampler);
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
break;
}
}
converter.mapInputSocket(inputDegreeSocket, operation->getInputSocket(1));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}

View File

@@ -81,7 +81,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
operation->setOffset(bnode->custom3, bnode->custom4);
operation->setNewWidth(rd->xsch * render_size_factor);
operation->setNewHeight(rd->ysch * render_size_factor);
operation->getInputSocket(0)->setResizeMode(ResizeMode::None);
operation->getInputSocket(0)->setResizeMode(ResizeMode::Align);
converter.addOperation(operation);
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));

View File

@@ -39,7 +39,7 @@ void SetAlphaNode::convertToOperations(NodeConverter &converter,
}
if (!this->getInputSocket(0)->isLinked() && this->getInputSocket(1)->isLinked()) {
operation->setResolutionInputSocketIndex(1);
operation->set_canvas_input_index(1);
}
converter.addOperation(operation);

View File

@@ -22,6 +22,7 @@
#include "COM_RotateOperation.h"
#include "COM_ScaleOperation.h"
#include "COM_SetSamplerOperation.h"
#include "COM_TransformOperation.h"
#include "COM_TranslateOperation.h"
#include "BKE_tracking.h"
@@ -42,18 +43,12 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
NodeInput *imageInput = this->getInputSocket(0);
MovieClip *clip = (MovieClip *)editorNode->id;
bool invert = (editorNode->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0;
const PixelSampler sampler = (PixelSampler)editorNode->custom1;
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
scaleOperation->setSampler((PixelSampler)editorNode->custom1);
RotateOperation *rotateOperation = new RotateOperation();
rotateOperation->setDoDegree2RadConversion(false);
TranslateOperation *translateOperation = new TranslateOperation();
MovieClipAttributeOperation *scaleAttribute = new MovieClipAttributeOperation();
MovieClipAttributeOperation *angleAttribute = new MovieClipAttributeOperation();
MovieClipAttributeOperation *xAttribute = new MovieClipAttributeOperation();
MovieClipAttributeOperation *yAttribute = new MovieClipAttributeOperation();
SetSamplerOperation *psoperation = new SetSamplerOperation();
psoperation->setSampler((PixelSampler)editorNode->custom1);
scaleAttribute->setAttribute(MCA_SCALE);
scaleAttribute->setFramenumber(context.getFramenumber());
@@ -79,38 +74,67 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
converter.addOperation(angleAttribute);
converter.addOperation(xAttribute);
converter.addOperation(yAttribute);
converter.addOperation(scaleOperation);
converter.addOperation(translateOperation);
converter.addOperation(rotateOperation);
converter.addOperation(psoperation);
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
switch (context.get_execution_model()) {
case eExecutionModel::Tiled: {
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
scaleOperation->setSampler(sampler);
RotateOperation *rotateOperation = new RotateOperation();
rotateOperation->setDoDegree2RadConversion(false);
TranslateOperation *translateOperation = new TranslateOperation();
SetSamplerOperation *psoperation = new SetSamplerOperation();
psoperation->setSampler(sampler);
converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
converter.addOperation(scaleOperation);
converter.addOperation(translateOperation);
converter.addOperation(rotateOperation);
converter.addOperation(psoperation);
converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1));
converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1));
if (invert) {
// Translate -> Rotate -> Scale.
converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1));
converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2));
converter.addLink(translateOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket());
converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0));
}
else {
// Scale -> Rotate -> Translate.
converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
if (invert) {
// Translate -> Rotate -> Scale.
converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0));
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
converter.addLink(translateOperation->getOutputSocket(),
rotateOperation->getInputSocket(0));
converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0));
converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0));
}
else {
// Scale -> Rotate -> Translate.
converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0));
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
converter.addLink(rotateOperation->getOutputSocket(),
translateOperation->getInputSocket(0));
converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
}
break;
}
case eExecutionModel::FullFrame: {
TransformOperation *transform_op = new TransformOperation();
transform_op->set_sampler(sampler);
transform_op->set_convert_rotate_degree_to_rad(false);
transform_op->set_invert(invert);
converter.addOperation(transform_op);
converter.mapInputSocket(imageInput, transform_op->getInputSocket(0));
converter.addLink(xAttribute->getOutputSocket(), transform_op->getInputSocket(1));
converter.addLink(yAttribute->getOutputSocket(), transform_op->getInputSocket(2));
converter.addLink(angleAttribute->getOutputSocket(), transform_op->getInputSocket(3));
converter.addLink(scaleAttribute->getOutputSocket(), transform_op->getInputSocket(4));
converter.mapOutputSocket(getOutputSocket(), transform_op->getOutputSocket());
}
}
}

View File

@@ -22,6 +22,7 @@
#include "COM_ScaleOperation.h"
#include "COM_SetSamplerOperation.h"
#include "COM_SetValueOperation.h"
#include "COM_TransformOperation.h"
#include "COM_TranslateOperation.h"
namespace blender::compositor {
@@ -32,7 +33,7 @@ TransformNode::TransformNode(bNode *editorNode) : Node(editorNode)
}
void TransformNode::convertToOperations(NodeConverter &converter,
const CompositorContext & /*context*/) const
const CompositorContext &context) const
{
NodeInput *imageInput = this->getInputSocket(0);
NodeInput *xInput = this->getInputSocket(1);
@@ -40,33 +41,51 @@ void TransformNode::convertToOperations(NodeConverter &converter,
NodeInput *angleInput = this->getInputSocket(3);
NodeInput *scaleInput = this->getInputSocket(4);
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
converter.addOperation(scaleOperation);
switch (context.get_execution_model()) {
case eExecutionModel::Tiled: {
ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation();
converter.addOperation(scaleOperation);
RotateOperation *rotateOperation = new RotateOperation();
rotateOperation->setDoDegree2RadConversion(false);
converter.addOperation(rotateOperation);
RotateOperation *rotateOperation = new RotateOperation();
rotateOperation->setDoDegree2RadConversion(false);
converter.addOperation(rotateOperation);
TranslateOperation *translateOperation = new TranslateOperation();
converter.addOperation(translateOperation);
TranslateOperation *translateOperation = new TranslateOperation();
converter.addOperation(translateOperation);
SetSamplerOperation *sampler = new SetSamplerOperation();
sampler->setSampler((PixelSampler)this->getbNode()->custom1);
converter.addOperation(sampler);
SetSamplerOperation *sampler = new SetSamplerOperation();
sampler->setSampler((PixelSampler)this->getbNode()->custom1);
converter.addOperation(sampler);
converter.mapInputSocket(imageInput, sampler->getInputSocket(0));
converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0));
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1));
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale
converter.mapInputSocket(imageInput, sampler->getInputSocket(0));
converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0));
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1));
converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1));
converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0));
converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1));
converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0));
converter.mapInputSocket(xInput, translateOperation->getInputSocket(1));
converter.mapInputSocket(yInput, translateOperation->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
break;
}
case eExecutionModel::FullFrame: {
TransformOperation *op = new TransformOperation();
op->set_sampler((PixelSampler)this->getbNode()->custom1);
converter.addOperation(op);
converter.mapInputSocket(imageInput, op->getInputSocket(0));
converter.mapInputSocket(xInput, op->getInputSocket(1));
converter.mapInputSocket(yInput, op->getInputSocket(2));
converter.mapInputSocket(angleInput, op->getInputSocket(3));
converter.mapInputSocket(scaleInput, op->getInputSocket(4));
converter.mapOutputSocket(getOutputSocket(), op->getOutputSocket());
break;
}
}
}
} // namespace blender::compositor

View File

@@ -41,7 +41,9 @@ void TranslateNode::convertToOperations(NodeConverter &converter,
NodeInput *inputYSocket = this->getInputSocket(2);
NodeOutput *outputSocket = this->getOutputSocket(0);
TranslateOperation *operation = new TranslateOperation();
TranslateOperation *operation = context.get_execution_model() == eExecutionModel::Tiled ?
new TranslateOperation() :
new TranslateCanvasOperation();
operation->set_wrapping(data->wrap_axis);
if (data->relative) {
const RenderData *rd = context.getRenderData();

View File

@@ -60,10 +60,10 @@ void ViewerNode::convertToOperations(NodeConverter &converter,
viewerOperation->setViewSettings(context.getViewSettings());
viewerOperation->setDisplaySettings(context.getDisplaySettings());
viewerOperation->setResolutionInputSocketIndex(0);
viewerOperation->set_canvas_input_index(0);
if (!imageSocket->isLinked()) {
if (alphaSocket->isLinked()) {
viewerOperation->setResolutionInputSocketIndex(1);
viewerOperation->set_canvas_input_index(1);
}
}

View File

@@ -17,6 +17,8 @@
*/
#include "COM_BlurBaseOperation.h"
#include "COM_ConstantOperation.h"
#include "BLI_math.h"
#include "MEM_guardedalloc.h"
@@ -36,11 +38,15 @@ BlurBaseOperation::BlurBaseOperation(DataType data_type)
this->m_size = 1.0f;
this->m_sizeavailable = false;
this->m_extend_bounds = false;
use_variable_size_ = false;
}
void BlurBaseOperation::initExecution()
void BlurBaseOperation::init_data()
{
this->m_inputProgram = this->getInputSocketReader(0);
this->m_inputSize = this->getInputSocketReader(1);
if (execution_model_ == eExecutionModel::FullFrame) {
updateSize();
}
this->m_data.image_in_width = this->getWidth();
this->m_data.image_in_height = this->getHeight();
if (this->m_data.relative) {
@@ -61,6 +67,12 @@ void BlurBaseOperation::initExecution()
this->m_data.sizex = round_fl_to_int(this->m_data.percentx * 0.01f * sizex);
this->m_data.sizey = round_fl_to_int(this->m_data.percenty * 0.01f * sizey);
}
}
void BlurBaseOperation::initExecution()
{
this->m_inputProgram = this->getInputSocketReader(0);
this->m_inputSize = this->getInputSocketReader(1);
QualityStepHelper::initExecution(COM_QH_MULTIPLY);
}
@@ -165,23 +177,86 @@ void BlurBaseOperation::setData(const NodeBlurData *data)
memcpy(&m_data, data, sizeof(NodeBlurData));
}
int BlurBaseOperation::get_blur_size(eDimension dim) const
{
switch (dim) {
case eDimension::X:
return m_data.sizex;
case eDimension::Y:
return m_data.sizey;
}
return -1;
}
void BlurBaseOperation::updateSize()
{
if (!this->m_sizeavailable) {
float result[4];
this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
this->m_size = result[0];
this->m_sizeavailable = true;
if (this->m_sizeavailable || use_variable_size_) {
return;
}
switch (execution_model_) {
case eExecutionModel::Tiled: {
float result[4];
this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
this->m_size = result[0];
break;
}
case eExecutionModel::FullFrame: {
NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX);
if (size_input->get_flags().is_constant_operation) {
m_size = *static_cast<ConstantOperation *>(size_input)->get_constant_elem();
} /* Else use default. */
break;
}
}
this->m_sizeavailable = true;
}
static int round_to_even(float value)
{
return ceilf(value * 0.5f) * 2.0f;
}
void BlurBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (!m_extend_bounds) {
NodeOperation::determine_canvas(preferred_area, r_area);
return;
}
switch (execution_model_) {
case eExecutionModel::Tiled: {
NodeOperation::determine_canvas(preferred_area, r_area);
r_area.xmax += 2 * m_size * m_data.sizex;
r_area.ymax += 2 * m_size * m_data.sizey;
break;
}
case eExecutionModel::FullFrame: {
/* Setting a modifier ensures all non main inputs have extended bounds as preferred
* canvas, avoiding unnecessary canvas convertions that would hide constant
* operations. */
set_determined_canvas_modifier([=](rcti &canvas) {
/* Rounding to even prevents jiggling in backdrop while switching size values. */
canvas.xmax += round_to_even(2 * m_size * m_data.sizex);
canvas.ymax += round_to_even(2 * m_size * m_data.sizey);
});
NodeOperation::determine_canvas(preferred_area, r_area);
break;
}
}
}
void BlurBaseOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
void BlurBaseOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
NodeOperation::determineResolution(resolution, preferredResolution);
if (this->m_extend_bounds) {
resolution[0] += 2 * this->m_size * m_data.sizex;
resolution[1] += 2 * this->m_size * m_data.sizey;
switch (input_idx) {
case 0:
r_input_area = output_area;
break;
case 1:
r_input_area = use_variable_size_ ? output_area : COM_CONSTANT_INPUT_AREA_OF_INTEREST;
break;
}
}

View File

@@ -18,7 +18,7 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
#include "COM_QualityStepHelper.h"
#define MAX_GAUSSTAB_RADIUS 30000
@@ -27,10 +27,16 @@
namespace blender::compositor {
class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelper {
private:
bool m_extend_bounds;
protected:
BlurBaseOperation(DataType data_type);
static constexpr int IMAGE_INPUT_INDEX = 0;
static constexpr int SIZE_INPUT_INDEX = 1;
protected:
BlurBaseOperation(DataType data_type8);
float *make_gausstab(float rad, int size);
#ifdef BLI_HAVE_SSE2
__m128 *convert_gausstab_sse(const float *gausstab, int size);
@@ -49,9 +55,11 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
float m_size;
bool m_sizeavailable;
bool m_extend_bounds;
/* Flags for inheriting classes. */
bool use_variable_size_;
public:
virtual void init_data() override;
/**
* Initialize the execution
*/
@@ -75,8 +83,13 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
this->m_extend_bounds = extend_bounds;
}
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
int get_blur_size(eDimension dim) const;
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
virtual void get_area_of_interest(int input_idx,
const rcti &output_area,
rcti &r_input_area) override;
};
} // namespace blender::compositor

View File

@@ -17,6 +17,8 @@
*/
#include "COM_BokehBlurOperation.h"
#include "COM_ConstantOperation.h"
#include "BLI_math.h"
#include "COM_OpenCLDevice.h"
@@ -24,10 +26,15 @@
namespace blender::compositor {
constexpr int IMAGE_INPUT_INDEX = 0;
constexpr int BOKEH_INPUT_INDEX = 1;
constexpr int BOUNDING_BOX_INPUT_INDEX = 2;
constexpr int SIZE_INPUT_INDEX = 3;
BokehBlurOperation::BokehBlurOperation()
{
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
@@ -44,6 +51,23 @@ BokehBlurOperation::BokehBlurOperation()
this->m_extend_bounds = false;
}
void BokehBlurOperation::init_data()
{
if (execution_model_ == eExecutionModel::FullFrame) {
updateSize();
}
NodeOperation *bokeh = get_input_operation(BOKEH_INPUT_INDEX);
const int width = bokeh->getWidth();
const int height = bokeh->getHeight();
const float dimension = MIN2(width, height);
m_bokehMidX = width / 2.0f;
m_bokehMidY = height / 2.0f;
m_bokehDimension = dimension / 2.0f;
}
void *BokehBlurOperation::initializeTileData(rcti * /*rect*/)
{
lockMutex();
@@ -58,18 +82,11 @@ void *BokehBlurOperation::initializeTileData(rcti * /*rect*/)
void BokehBlurOperation::initExecution()
{
initMutex();
this->m_inputProgram = getInputSocketReader(0);
this->m_inputBokehProgram = getInputSocketReader(1);
this->m_inputBoundingBoxReader = getInputSocketReader(2);
int width = this->m_inputBokehProgram->getWidth();
int height = this->m_inputBokehProgram->getHeight();
float dimension = MIN2(width, height);
this->m_bokehMidX = width / 2.0f;
this->m_bokehMidY = height / 2.0f;
this->m_bokehDimension = dimension / 2.0f;
QualityStepHelper::initExecution(COM_QH_INCREASE);
}
@@ -225,23 +242,147 @@ void BokehBlurOperation::executeOpenCL(OpenCLDevice *device,
void BokehBlurOperation::updateSize()
{
if (!this->m_sizeavailable) {
float result[4];
this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest);
this->m_size = result[0];
CLAMP(this->m_size, 0.0f, 10.0f);
this->m_sizeavailable = true;
if (this->m_sizeavailable) {
return;
}
switch (execution_model_) {
case eExecutionModel::Tiled: {
float result[4];
this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest);
this->m_size = result[0];
CLAMP(this->m_size, 0.0f, 10.0f);
break;
}
case eExecutionModel::FullFrame: {
NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX);
if (size_input->get_flags().is_constant_operation) {
m_size = *static_cast<ConstantOperation *>(size_input)->get_constant_elem();
CLAMP(m_size, 0.0f, 10.0f);
} /* Else use default. */
break;
}
}
this->m_sizeavailable = true;
}
static float round_to_even(float value)
{
return roundf(value * 0.5f) * 2.0f;
}
void BokehBlurOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
if (!m_extend_bounds) {
NodeOperation::determine_canvas(preferred_area, r_area);
return;
}
switch (execution_model_) {
case eExecutionModel::Tiled: {
NodeOperation::determine_canvas(preferred_area, r_area);
const float max_dim = MAX2(BLI_rcti_size_x(&r_area), BLI_rcti_size_y(&r_area));
r_area.xmax += 2 * this->m_size * max_dim / 100.0f;
r_area.ymax += 2 * this->m_size * max_dim / 100.0f;
break;
}
case eExecutionModel::FullFrame: {
set_determined_canvas_modifier([=](rcti &canvas) {
const float max_dim = MAX2(BLI_rcti_size_x(&canvas), BLI_rcti_size_y(&canvas));
/* Rounding to even prevents image jiggling in backdrop while switching size values. */
float add_size = round_to_even(2 * this->m_size * max_dim / 100.0f);
canvas.xmax += add_size;
canvas.ymax += add_size;
});
NodeOperation::determine_canvas(preferred_area, r_area);
break;
}
}
}
void BokehBlurOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
void BokehBlurOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
NodeOperation::determineResolution(resolution, preferredResolution);
if (this->m_extend_bounds) {
const float max_dim = MAX2(resolution[0], resolution[1]);
resolution[0] += 2 * this->m_size * max_dim / 100.0f;
resolution[1] += 2 * this->m_size * max_dim / 100.0f;
switch (input_idx) {
case IMAGE_INPUT_INDEX: {
const float max_dim = MAX2(this->getWidth(), this->getHeight());
const float add_size = m_size * max_dim / 100.0f;
r_input_area.xmin = output_area.xmin - add_size;
r_input_area.xmax = output_area.xmax + add_size;
r_input_area.ymin = output_area.ymin - add_size;
r_input_area.ymax = output_area.ymax + add_size;
break;
}
case BOKEH_INPUT_INDEX: {
NodeOperation *bokeh_input = getInputOperation(BOKEH_INPUT_INDEX);
r_input_area = bokeh_input->get_canvas();
break;
}
case BOUNDING_BOX_INPUT_INDEX:
r_input_area = output_area;
break;
case SIZE_INPUT_INDEX: {
r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST;
break;
}
}
}
void BokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const float max_dim = MAX2(this->getWidth(), this->getHeight());
const int pixel_size = m_size * max_dim / 100.0f;
const float m = m_bokehDimension / pixel_size;
const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX];
const MemoryBuffer *bokeh_input = inputs[BOKEH_INPUT_INDEX];
MemoryBuffer *bounding_input = inputs[BOUNDING_BOX_INPUT_INDEX];
BuffersIterator<float> it = output->iterate_with({bounding_input}, area);
const rcti &image_rect = image_input->get_rect();
for (; !it.is_end(); ++it) {
const int x = it.x;
const int y = it.y;
const float bounding_box = *it.in(0);
if (bounding_box <= 0.0f) {
image_input->read_elem(x, y, it.out);
continue;
}
float color_accum[4] = {0};
float multiplier_accum[4] = {0};
if (pixel_size < 2) {
image_input->read_elem(x, y, color_accum);
multiplier_accum[0] = 1.0f;
multiplier_accum[1] = 1.0f;
multiplier_accum[2] = 1.0f;
multiplier_accum[3] = 1.0f;
}
const int miny = MAX2(y - pixel_size, image_rect.ymin);
const int maxy = MIN2(y + pixel_size, image_rect.ymax);
const int minx = MAX2(x - pixel_size, image_rect.xmin);
const int maxx = MIN2(x + pixel_size, image_rect.xmax);
const int step = getStep();
const int elem_stride = image_input->elem_stride * step;
const int row_stride = image_input->row_stride * step;
const float *row_color = image_input->get_elem(minx, miny);
for (int ny = miny; ny < maxy; ny += step, row_color += row_stride) {
const float *color = row_color;
const float v = m_bokehMidY - (ny - y) * m;
for (int nx = minx; nx < maxx; nx += step, color += elem_stride) {
const float u = m_bokehMidX - (nx - x) * m;
float bokeh[4];
bokeh_input->read_elem_checked(u, v, bokeh);
madd_v4_v4v4(color_accum, bokeh, color);
add_v4_v4(multiplier_accum, bokeh);
}
}
it.out[0] = color_accum[0] * (1.0f / multiplier_accum[0]);
it.out[1] = color_accum[1] * (1.0f / multiplier_accum[1]);
it.out[2] = color_accum[2] * (1.0f / multiplier_accum[2]);
it.out[3] = color_accum[3] * (1.0f / multiplier_accum[3]);
}
}

View File

@@ -18,12 +18,12 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
#include "COM_QualityStepHelper.h"
namespace blender::compositor {
class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelper {
private:
SocketReader *m_inputProgram;
SocketReader *m_inputBokehProgram;
@@ -31,6 +31,7 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
void updateSize();
float m_size;
bool m_sizeavailable;
float m_bokehMidX;
float m_bokehMidY;
float m_bokehDimension;
@@ -39,6 +40,8 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
public:
BokehBlurOperation();
void init_data() override;
void *initializeTileData(rcti *rect) override;
/**
* The inner loop of this operation.
@@ -77,8 +80,12 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
this->m_extend_bounds = extend_bounds;
}
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -145,11 +145,13 @@ void BokehImageOperation::deinitExecution()
}
}
void BokehImageOperation::determineResolution(unsigned int resolution[2],
unsigned int /*preferredResolution*/[2])
void BokehImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
resolution[0] = COM_BLUR_BOKEH_PIXELS;
resolution[1] = COM_BLUR_BOKEH_PIXELS;
BLI_rcti_init(&r_area,
preferred_area.xmin,
preferred_area.xmin + COM_BLUR_BOKEH_PIXELS,
preferred_area.ymin,
preferred_area.ymin + COM_BLUR_BOKEH_PIXELS);
}
} // namespace blender::compositor

View File

@@ -128,8 +128,7 @@ class BokehImageOperation : public MultiThreadedOperation {
* \brief determine the resolution of this operation. currently fixed at [COM_BLUR_BOKEH_PIXELS,
* COM_BLUR_BOKEH_PIXELS] \param resolution: \param preferredResolution:
*/
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
/**
* \brief set the node data

View File

@@ -27,7 +27,7 @@ namespace blender::compositor {
CalculateMeanOperation::CalculateMeanOperation()
{
this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Value);
this->m_imageReader = nullptr;
this->m_iscalculated = false;
@@ -165,11 +165,7 @@ void CalculateMeanOperation::get_area_of_interest(int input_idx,
rcti &r_input_area)
{
BLI_assert(input_idx == 0);
NodeOperation *operation = getInputOperation(input_idx);
r_input_area.xmin = 0;
r_input_area.ymin = 0;
r_input_area.xmax = operation->getWidth();
r_input_area.ymax = operation->getHeight();
r_input_area = get_input_operation(input_idx)->get_canvas();
}
void CalculateMeanOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),

View File

@@ -27,6 +27,7 @@ ChannelMatteOperation::ChannelMatteOperation()
addOutputSocket(DataType::Value);
this->m_inputImageProgram = nullptr;
flags.can_be_constant = true;
}
void ChannelMatteOperation::initExecution()
@@ -121,4 +122,37 @@ void ChannelMatteOperation::executePixelSampled(float output[4],
output[0] = MIN2(alpha, inColor[3]);
}
void ChannelMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const float *color = it.in(0);
/* Matte operation. */
float alpha = color[this->m_ids[0]] - MAX2(color[this->m_ids[1]], color[this->m_ids[2]]);
/* Flip because 0.0 is transparent, not 1.0. */
alpha = 1.0f - alpha;
/* Test range. */
if (alpha > m_limit_max) {
alpha = color[3]; /* Whatever it was prior. */
}
else if (alpha < m_limit_min) {
alpha = 0.0f;
}
else { /* Blend. */
alpha = (alpha - m_limit_min) / m_limit_range;
}
/* Store matte(alpha) value in [0] to go with
* COM_SetAlphaMultiplyOperation and the Value output.
*/
/* Don't make something that was more transparent less transparent. */
*it.out = MIN2(alpha, color[3]);
}
}
} // namespace blender::compositor

View File

@@ -18,7 +18,7 @@
#pragma once
#include "COM_MixOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
class ChannelMatteOperation : public NodeOperation {
class ChannelMatteOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputImageProgram;
@@ -71,6 +71,10 @@ class ChannelMatteOperation : public NodeOperation {
this->m_limit_channel = nodeChroma->channel;
this->m_matte_channel = custom2;
}
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -29,6 +29,7 @@ ChromaMatteOperation::ChromaMatteOperation()
this->m_inputImageProgram = nullptr;
this->m_inputKeyProgram = nullptr;
flags.can_be_constant = true;
}
void ChromaMatteOperation::initExecution()
@@ -110,4 +111,58 @@ void ChromaMatteOperation::executePixelSampled(float output[4],
}
}
void ChromaMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const float acceptance = this->m_settings->t1; /* In radians. */
const float cutoff = this->m_settings->t2; /* In radians. */
const float gain = this->m_settings->fstrength;
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const float *in_image = it.in(0);
const float *in_key = it.in(1);
/* Store matte(alpha) value in [0] to go with
* #COM_SetAlphaMultiplyOperation and the Value output. */
/* Algorithm from book "Video Demystified", does not include the spill reduction part. */
/* Find theta, the angle that the color space should be rotated based on key. */
/* Rescale to `-1.0..1.0`. */
// const float image_Y = (in_image[0] * 2.0f) - 1.0f; // UNUSED
const float image_cb = (in_image[1] * 2.0f) - 1.0f;
const float image_cr = (in_image[2] * 2.0f) - 1.0f;
// const float key_Y = (in_key[0] * 2.0f) - 1.0f; // UNUSED
const float key_cb = (in_key[1] * 2.0f) - 1.0f;
const float key_cr = (in_key[2] * 2.0f) - 1.0f;
const float theta = atan2(key_cr, key_cb);
/* Rotate the cb and cr into x/z space. */
const float x_angle = image_cb * cosf(theta) + image_cr * sinf(theta);
const float z_angle = image_cr * cosf(theta) - image_cb * sinf(theta);
/* If within the acceptance angle. */
/* If kfg is <0 then the pixel is outside of the key color. */
const float kfg = x_angle - (fabsf(z_angle) / tanf(acceptance / 2.0f));
if (kfg > 0.0f) { /* Found a pixel that is within key color. */
const float beta = atan2(z_angle, x_angle);
float alpha = 1.0f - (kfg / gain);
/* Ff beta is within the cutoff angle. */
if (fabsf(beta) < (cutoff / 2.0f)) {
alpha = 0.0f;
}
/* Don't make something that was more transparent less transparent. */
it.out[0] = alpha < in_image[3] ? alpha : in_image[3];
}
else { /* Pixel is outside key color. */
it.out[0] = in_image[3]; /* Make pixel just as transparent as it was before. */
}
}
}
} // namespace blender::compositor

View File

@@ -18,7 +18,7 @@
#pragma once
#include "COM_MixOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
class ChromaMatteOperation : public NodeOperation {
class ChromaMatteOperation : public MultiThreadedOperation {
private:
NodeChroma *m_settings;
SocketReader *m_inputImageProgram;
@@ -50,6 +50,10 @@ class ChromaMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -40,7 +40,7 @@ ColorBalanceASCCDLOperation::ColorBalanceASCCDLOperation()
this->addOutputSocket(DataType::Color);
this->m_inputValueOperation = nullptr;
this->m_inputColorOperation = nullptr;
this->setResolutionInputSocketIndex(1);
this->set_canvas_input_index(1);
flags.can_be_constant = true;
}

View File

@@ -45,7 +45,7 @@ ColorBalanceLGGOperation::ColorBalanceLGGOperation()
this->addOutputSocket(DataType::Color);
this->m_inputValueOperation = nullptr;
this->m_inputColorOperation = nullptr;
this->setResolutionInputSocketIndex(1);
this->set_canvas_input_index(1);
flags.can_be_constant = true;
}

View File

@@ -37,7 +37,7 @@ ColorCurveOperation::ColorCurveOperation()
this->m_inputBlackProgram = nullptr;
this->m_inputWhiteProgram = nullptr;
this->setResolutionInputSocketIndex(1);
this->set_canvas_input_index(1);
}
void ColorCurveOperation::initExecution()
{
@@ -139,7 +139,7 @@ ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation()
this->m_inputFacProgram = nullptr;
this->m_inputImageProgram = nullptr;
this->setResolutionInputSocketIndex(1);
this->set_canvas_input_index(1);
}
void ConstantLevelColorCurveOperation::initExecution()
{

View File

@@ -29,6 +29,7 @@ ColorMatteOperation::ColorMatteOperation()
this->m_inputImageProgram = nullptr;
this->m_inputKeyProgram = nullptr;
flags.can_be_constant = true;
}
void ColorMatteOperation::initExecution()
@@ -82,4 +83,40 @@ void ColorMatteOperation::executePixelSampled(float output[4],
}
}
void ColorMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const float hue = m_settings->t1;
const float sat = m_settings->t2;
const float val = m_settings->t3;
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const float *in_color = it.in(0);
const float *in_key = it.in(1);
/* Store matte(alpha) value in [0] to go with
* COM_SetAlphaMultiplyOperation and the Value output.
*/
float h_wrap;
if (
/* Do hue last because it needs to wrap, and does some more checks. */
/* #sat */ (fabsf(in_color[1] - in_key[1]) < sat) &&
/* #val */ (fabsf(in_color[2] - in_key[2]) < val) &&
/* Multiply by 2 because it wraps on both sides of the hue,
* otherwise 0.5 would key all hue's. */
/* #hue */
((h_wrap = 2.0f * fabsf(in_color[0] - in_key[0])) < hue || (2.0f - h_wrap) < hue)) {
it.out[0] = 0.0f; /* Make transparent. */
}
else { /* Pixel is outside key color. */
it.out[0] = in_color[3]; /* Make pixel just as transparent as it was before. */
}
}
}
} // namespace blender::compositor

View File

@@ -18,7 +18,7 @@
#pragma once
#include "COM_MixOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
class ColorMatteOperation : public NodeOperation {
class ColorMatteOperation : public MultiThreadedOperation {
private:
NodeChroma *m_settings;
SocketReader *m_inputImageProgram;
@@ -50,6 +50,10 @@ class ColorMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -51,4 +51,13 @@ void ColorRampOperation::deinitExecution()
this->m_inputProgram = nullptr;
}
void ColorRampOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
BKE_colorband_evaluate(m_colorBand, *it.in(0), it.out);
}
}
} // namespace blender::compositor

View File

@@ -18,12 +18,12 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
#include "DNA_texture_types.h"
namespace blender::compositor {
class ColorRampOperation : public NodeOperation {
class ColorRampOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -53,6 +53,10 @@ class ColorRampOperation : public NodeOperation {
{
this->m_colorBand = colorBand;
}
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -32,6 +32,7 @@ ColorSpillOperation::ColorSpillOperation()
this->m_inputFacReader = nullptr;
this->m_spillChannel = 1; // GREEN
this->m_spillMethod = 0;
flags.can_be_constant = true;
}
void ColorSpillOperation::initExecution()
@@ -118,4 +119,36 @@ void ColorSpillOperation::executePixelSampled(float output[4],
}
}
void ColorSpillOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const float *color = it.in(0);
const float factor = MIN2(1.0f, *it.in(1));
float map;
switch (m_spillMethod) {
case 0: /* simple */
map = factor *
(color[m_spillChannel] - (m_settings->limscale * color[m_settings->limchan]));
break;
default: /* average */
map = factor * (color[m_spillChannel] -
(m_settings->limscale * AVG(color[m_channel2], color[m_channel3])));
break;
}
if (map > 0.0f) {
it.out[0] = color[0] + m_rmut * (m_settings->uspillr * map);
it.out[1] = color[1] + m_gmut * (m_settings->uspillg * map);
it.out[2] = color[2] + m_bmut * (m_settings->uspillb * map);
it.out[3] = color[3];
}
else {
copy_v4_v4(it.out, color);
}
}
}
} // namespace blender::compositor

View File

@@ -18,7 +18,7 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
class ColorSpillOperation : public NodeOperation {
class ColorSpillOperation : public MultiThreadedOperation {
protected:
NodeColorspill *m_settings;
SocketReader *m_inputImageReader;
@@ -65,6 +65,10 @@ class ColorSpillOperation : public NodeOperation {
}
float calculateMapValue(float fac, float *input);
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -236,8 +236,7 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp
depth_buf.copy_from(inputs[2], area);
}
void CompositorOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
void CompositorOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
int width = this->m_rd->xsch * this->m_rd->size / 100;
int height = this->m_rd->ysch * this->m_rd->size / 100;
@@ -254,13 +253,11 @@ void CompositorOperation::determineResolution(unsigned int resolution[2],
RE_ReleaseResult(re);
}
preferredResolution[0] = width;
preferredResolution[1] = height;
rcti local_preferred;
BLI_rcti_init(&local_preferred, 0, width, 0, height);
NodeOperation::determineResolution(resolution, preferredResolution);
resolution[0] = width;
resolution[1] = height;
NodeOperation::determine_canvas(local_preferred, r_area);
r_area = local_preferred;
}
} // namespace blender::compositor

View File

@@ -115,8 +115,7 @@ class CompositorOperation : public MultiThreadedOperation {
{
return eCompositorPriority::Medium;
}
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void setUseAlphaInput(bool value)
{
this->m_useAlphaInput = value;

View File

@@ -22,14 +22,14 @@ namespace blender::compositor {
ConstantOperation::ConstantOperation()
{
needs_resolution_to_get_constant_ = false;
needs_canvas_to_get_constant_ = false;
flags.is_constant_operation = true;
flags.is_fullframe_operation = true;
}
bool ConstantOperation::can_get_constant_elem() const
{
return !needs_resolution_to_get_constant_ || this->flags.is_resolution_set;
return !needs_canvas_to_get_constant_ || this->flags.is_canvas_set;
}
void ConstantOperation::update_memory_buffer(MemoryBuffer *output,

View File

@@ -31,7 +31,7 @@ namespace blender::compositor {
*/
class ConstantOperation : public NodeOperation {
protected:
bool needs_resolution_to_get_constant_;
bool needs_canvas_to_get_constant_;
public:
ConstantOperation();

View File

@@ -116,4 +116,31 @@ void ConvertDepthToRadiusOperation::deinitExecution()
this->m_inputOperation = nullptr;
}
void ConvertDepthToRadiusOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const float z = *it.in(0);
if (z == 0.0f) {
*it.out = 0.0f;
continue;
}
const float inv_z = (1.0f / z);
/* bug T6656 part 2b, do not re-scale. */
#if 0
bcrad = 0.5f * fabs(aperture * (dof_sp * (cam_invfdist - iZ) - 1.0f));
// scale crad back to original maximum and blend
crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad);
#endif
const float radius = 0.5f *
fabsf(m_aperture * (m_dof_sp * (m_inverseFocalDistance - inv_z) - 1.0f));
/* 'bug' T6615, limit minimum radius to 1 pixel,
* not really a solution, but somewhat mitigates the problem. */
*it.out = CLAMPIS(radius, 0.0f, m_maxRadius);
}
}
} // namespace blender::compositor

View File

@@ -19,7 +19,7 @@
#pragma once
#include "COM_FastGaussianBlurOperation.h"
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
#include "DNA_object_types.h"
namespace blender::compositor {
@@ -28,7 +28,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
class ConvertDepthToRadiusOperation : public NodeOperation {
class ConvertDepthToRadiusOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -83,6 +83,10 @@ class ConvertDepthToRadiusOperation : public NodeOperation {
{
this->m_blurPostOperation = operation;
}
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -39,6 +39,14 @@ void ConvertBaseOperation::deinitExecution()
this->m_inputOperation = nullptr;
}
void ConvertBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
BuffersIterator<float> it = output->iterate_with(inputs, area);
update_memory_buffer_partial(it);
}
/* ******** Value to Color ******** */
ConvertValueToColorOperation::ConvertValueToColorOperation() : ConvertBaseOperation()
@@ -58,6 +66,14 @@ void ConvertValueToColorOperation::executePixelSampled(float output[4],
output[3] = 1.0f;
}
void ConvertValueToColorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
it.out[0] = it.out[1] = it.out[2] = *it.in(0);
it.out[3] = 1.0f;
}
}
/* ******** Color to Value ******** */
ConvertColorToValueOperation::ConvertColorToValueOperation() : ConvertBaseOperation()
@@ -76,6 +92,14 @@ void ConvertColorToValueOperation::executePixelSampled(float output[4],
output[0] = (inputColor[0] + inputColor[1] + inputColor[2]) / 3.0f;
}
void ConvertColorToValueOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
const float *in = it.in(0);
it.out[0] = (in[0] + in[1] + in[2]) / 3.0f;
}
}
/* ******** Color to BW ******** */
ConvertColorToBWOperation::ConvertColorToBWOperation() : ConvertBaseOperation()
@@ -94,6 +118,13 @@ void ConvertColorToBWOperation::executePixelSampled(float output[4],
output[0] = IMB_colormanagement_get_luminance(inputColor);
}
void ConvertColorToBWOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
it.out[0] = IMB_colormanagement_get_luminance(it.in(0));
}
}
/* ******** Color to Vector ******** */
ConvertColorToVectorOperation::ConvertColorToVectorOperation() : ConvertBaseOperation()
@@ -112,6 +143,13 @@ void ConvertColorToVectorOperation::executePixelSampled(float output[4],
copy_v3_v3(output, color);
}
void ConvertColorToVectorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
copy_v3_v3(it.out, it.in(0));
}
}
/* ******** Value to Vector ******** */
ConvertValueToVectorOperation::ConvertValueToVectorOperation() : ConvertBaseOperation()
@@ -130,6 +168,13 @@ void ConvertValueToVectorOperation::executePixelSampled(float output[4],
output[0] = output[1] = output[2] = value;
}
void ConvertValueToVectorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
it.out[0] = it.out[1] = it.out[2] = *it.in(0);
}
}
/* ******** Vector to Color ******** */
ConvertVectorToColorOperation::ConvertVectorToColorOperation() : ConvertBaseOperation()
@@ -147,6 +192,14 @@ void ConvertVectorToColorOperation::executePixelSampled(float output[4],
output[3] = 1.0f;
}
void ConvertVectorToColorOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
copy_v3_v3(it.out, it.in(0));
it.out[3] = 1.0f;
}
}
/* ******** Vector to Value ******** */
ConvertVectorToValueOperation::ConvertVectorToValueOperation() : ConvertBaseOperation()
@@ -165,6 +218,14 @@ void ConvertVectorToValueOperation::executePixelSampled(float output[4],
output[0] = (input[0] + input[1] + input[2]) / 3.0f;
}
void ConvertVectorToValueOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
const float *in = it.in(0);
it.out[0] = (in[0] + in[1] + in[2]) / 3.0f;
}
}
/* ******** RGB to YCC ******** */
ConvertRGBToYCCOperation::ConvertRGBToYCCOperation() : ConvertBaseOperation()
@@ -207,6 +268,18 @@ void ConvertRGBToYCCOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
const float *in = it.in(0);
rgb_to_ycc(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], this->m_mode);
/* Normalize for viewing (#rgb_to_ycc returns 0-255 values). */
mul_v3_fl(it.out, 1.0f / 255.0f);
it.out[3] = in[3];
}
}
/* ******** YCC to RGB ******** */
ConvertYCCToRGBOperation::ConvertYCCToRGBOperation() : ConvertBaseOperation()
@@ -240,7 +313,6 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4],
this->m_inputOperation->readSampled(inputColor, x, y, sampler);
/* need to un-normalize the data */
/* R,G,B --> Y,Cb,Cr */
mul_v3_fl(inputColor, 255.0f);
ycc_to_rgb(inputColor[0],
@@ -253,6 +325,22 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
const float *in = it.in(0);
/* Multiply by 255 to un-normalize (#ycc_to_rgb needs input values in 0-255 range). */
ycc_to_rgb(in[0] * 255.0f,
in[1] * 255.0f,
in[2] * 255.0f,
&it.out[0],
&it.out[1],
&it.out[2],
this->m_mode);
it.out[3] = in[3];
}
}
/* ******** RGB to YUV ******** */
ConvertRGBToYUVOperation::ConvertRGBToYUVOperation() : ConvertBaseOperation()
@@ -278,6 +366,15 @@ void ConvertRGBToYUVOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
void ConvertRGBToYUVOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
const float *in = it.in(0);
rgb_to_yuv(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], BLI_YUV_ITU_BT709);
it.out[3] = in[3];
}
}
/* ******** YUV to RGB ******** */
ConvertYUVToRGBOperation::ConvertYUVToRGBOperation() : ConvertBaseOperation()
@@ -303,6 +400,15 @@ void ConvertYUVToRGBOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
void ConvertYUVToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
const float *in = it.in(0);
yuv_to_rgb(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], BLI_YUV_ITU_BT709);
it.out[3] = in[3];
}
}
/* ******** RGB to HSV ******** */
ConvertRGBToHSVOperation::ConvertRGBToHSVOperation() : ConvertBaseOperation()
@@ -322,6 +428,15 @@ void ConvertRGBToHSVOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
void ConvertRGBToHSVOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
const float *in = it.in(0);
rgb_to_hsv_v(in, it.out);
it.out[3] = in[3];
}
}
/* ******** HSV to RGB ******** */
ConvertHSVToRGBOperation::ConvertHSVToRGBOperation() : ConvertBaseOperation()
@@ -344,6 +459,18 @@ void ConvertHSVToRGBOperation::executePixelSampled(float output[4],
output[3] = inputColor[3];
}
void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
const float *in = it.in(0);
hsv_to_rgb_v(in, it.out);
it.out[0] = max_ff(it.out[0], 0.0f);
it.out[1] = max_ff(it.out[1], 0.0f);
it.out[2] = max_ff(it.out[2], 0.0f);
it.out[3] = in[3];
}
}
/* ******** Premul to Straight ******** */
ConvertPremulToStraightOperation::ConvertPremulToStraightOperation() : ConvertBaseOperation()
@@ -363,6 +490,13 @@ void ConvertPremulToStraightOperation::executePixelSampled(float output[4],
copy_v4_v4(output, converted);
}
void ConvertPremulToStraightOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
copy_v4_v4(it.out, ColorSceneLinear4f<eAlpha::Premultiplied>(it.in(0)).unpremultiply_alpha());
}
}
/* ******** Straight to Premul ******** */
ConvertStraightToPremulOperation::ConvertStraightToPremulOperation() : ConvertBaseOperation()
@@ -382,6 +516,13 @@ void ConvertStraightToPremulOperation::executePixelSampled(float output[4],
copy_v4_v4(output, converted);
}
void ConvertStraightToPremulOperation::update_memory_buffer_partial(BuffersIterator<float> &it)
{
for (; !it.is_end(); ++it) {
copy_v4_v4(it.out, ColorSceneLinear4f<eAlpha::Straight>(it.in(0)).premultiply_alpha());
}
}
/* ******** Separate Channels ******** */
SeparateChannelOperation::SeparateChannelOperation()
@@ -410,6 +551,15 @@ void SeparateChannelOperation::executePixelSampled(float output[4],
output[0] = input[this->m_channel];
}
void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
it.out[0] = it.in(0)[this->m_channel];
}
}
/* ******** Combine Channels ******** */
CombineChannelsOperation::CombineChannelsOperation()
@@ -419,7 +569,7 @@ CombineChannelsOperation::CombineChannelsOperation()
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
this->set_canvas_input_index(0);
this->m_inputChannel1Operation = nullptr;
this->m_inputChannel2Operation = nullptr;
this->m_inputChannel3Operation = nullptr;
@@ -466,4 +616,16 @@ void CombineChannelsOperation::executePixelSampled(float output[4],
}
}
void CombineChannelsOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
it.out[0] = *it.in(0);
it.out[1] = *it.in(1);
it.out[2] = *it.in(2);
it.out[3] = *it.in(3);
}
}
} // namespace blender::compositor

View File

@@ -18,11 +18,11 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
class ConvertBaseOperation : public NodeOperation {
class ConvertBaseOperation : public MultiThreadedOperation {
protected:
SocketReader *m_inputOperation;
@@ -31,6 +31,13 @@ class ConvertBaseOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) final;
protected:
virtual void update_memory_buffer_partial(BuffersIterator<float> &it) = 0;
};
class ConvertValueToColorOperation : public ConvertBaseOperation {
@@ -38,6 +45,9 @@ class ConvertValueToColorOperation : public ConvertBaseOperation {
ConvertValueToColorOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertColorToValueOperation : public ConvertBaseOperation {
@@ -45,6 +55,9 @@ class ConvertColorToValueOperation : public ConvertBaseOperation {
ConvertColorToValueOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertColorToBWOperation : public ConvertBaseOperation {
@@ -52,6 +65,9 @@ class ConvertColorToBWOperation : public ConvertBaseOperation {
ConvertColorToBWOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertColorToVectorOperation : public ConvertBaseOperation {
@@ -59,6 +75,9 @@ class ConvertColorToVectorOperation : public ConvertBaseOperation {
ConvertColorToVectorOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertValueToVectorOperation : public ConvertBaseOperation {
@@ -66,6 +85,9 @@ class ConvertValueToVectorOperation : public ConvertBaseOperation {
ConvertValueToVectorOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertVectorToColorOperation : public ConvertBaseOperation {
@@ -73,6 +95,9 @@ class ConvertVectorToColorOperation : public ConvertBaseOperation {
ConvertVectorToColorOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertVectorToValueOperation : public ConvertBaseOperation {
@@ -80,6 +105,9 @@ class ConvertVectorToValueOperation : public ConvertBaseOperation {
ConvertVectorToValueOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertRGBToYCCOperation : public ConvertBaseOperation {
@@ -94,6 +122,9 @@ class ConvertRGBToYCCOperation : public ConvertBaseOperation {
/** Set the YCC mode */
void setMode(int mode);
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertYCCToRGBOperation : public ConvertBaseOperation {
@@ -108,6 +139,9 @@ class ConvertYCCToRGBOperation : public ConvertBaseOperation {
/** Set the YCC mode */
void setMode(int mode);
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertRGBToYUVOperation : public ConvertBaseOperation {
@@ -115,6 +149,9 @@ class ConvertRGBToYUVOperation : public ConvertBaseOperation {
ConvertRGBToYUVOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertYUVToRGBOperation : public ConvertBaseOperation {
@@ -122,6 +159,9 @@ class ConvertYUVToRGBOperation : public ConvertBaseOperation {
ConvertYUVToRGBOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertRGBToHSVOperation : public ConvertBaseOperation {
@@ -129,6 +169,9 @@ class ConvertRGBToHSVOperation : public ConvertBaseOperation {
ConvertRGBToHSVOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertHSVToRGBOperation : public ConvertBaseOperation {
@@ -136,6 +179,9 @@ class ConvertHSVToRGBOperation : public ConvertBaseOperation {
ConvertHSVToRGBOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertPremulToStraightOperation : public ConvertBaseOperation {
@@ -143,6 +189,9 @@ class ConvertPremulToStraightOperation : public ConvertBaseOperation {
ConvertPremulToStraightOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class ConvertStraightToPremulOperation : public ConvertBaseOperation {
@@ -150,9 +199,12 @@ class ConvertStraightToPremulOperation : public ConvertBaseOperation {
ConvertStraightToPremulOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
protected:
void update_memory_buffer_partial(BuffersIterator<float> &it) override;
};
class SeparateChannelOperation : public NodeOperation {
class SeparateChannelOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputOperation;
int m_channel;
@@ -168,9 +220,13 @@ class SeparateChannelOperation : public NodeOperation {
{
this->m_channel = channel;
}
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
class CombineChannelsOperation : public NodeOperation {
class CombineChannelsOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputChannel1Operation;
SocketReader *m_inputChannel2Operation;
@@ -183,6 +239,10 @@ class CombineChannelsOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -95,4 +95,81 @@ void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y,
output[3] = MAX2(output[3], 0.0f);
}
void ConvolutionEdgeFilterOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX];
const int last_x = getWidth() - 1;
const int last_y = getHeight() - 1;
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const int left_offset = (it.x == 0) ? 0 : -image->elem_stride;
const int right_offset = (it.x == last_x) ? 0 : image->elem_stride;
const int down_offset = (it.y == 0) ? 0 : -image->row_stride;
const int up_offset = (it.y == last_y) ? 0 : image->row_stride;
const float *center_color = it.in(IMAGE_INPUT_INDEX);
float res1[4] = {0};
float res2[4] = {0};
const float *color = center_color + down_offset + left_offset;
madd_v3_v3fl(res1, color, m_filter[0]);
copy_v3_v3(res2, res1);
color = center_color + down_offset;
madd_v3_v3fl(res1, color, m_filter[1]);
madd_v3_v3fl(res2, color, m_filter[3]);
color = center_color + down_offset + right_offset;
madd_v3_v3fl(res1, color, m_filter[2]);
madd_v3_v3fl(res2, color, m_filter[6]);
color = center_color + left_offset;
madd_v3_v3fl(res1, color, m_filter[3]);
madd_v3_v3fl(res2, color, m_filter[1]);
{
float rgb_filtered[3];
mul_v3_v3fl(rgb_filtered, center_color, m_filter[4]);
add_v3_v3(res1, rgb_filtered);
add_v3_v3(res2, rgb_filtered);
}
color = center_color + right_offset;
madd_v3_v3fl(res1, color, m_filter[5]);
madd_v3_v3fl(res2, color, m_filter[7]);
color = center_color + up_offset + left_offset;
madd_v3_v3fl(res1, color, m_filter[6]);
madd_v3_v3fl(res2, color, m_filter[2]);
color = center_color + up_offset;
madd_v3_v3fl(res1, color, m_filter[7]);
madd_v3_v3fl(res2, color, m_filter[5]);
{
color = center_color + up_offset + right_offset;
float rgb_filtered[3];
mul_v3_v3fl(rgb_filtered, color, m_filter[8]);
add_v3_v3(res1, rgb_filtered);
add_v3_v3(res2, rgb_filtered);
}
it.out[0] = sqrt(res1[0] * res1[0] + res2[0] * res2[0]);
it.out[1] = sqrt(res1[1] * res1[1] + res2[1] * res2[1]);
it.out[2] = sqrt(res1[2] * res1[2] + res2[2] * res2[2]);
const float factor = *it.in(FACTOR_INPUT_INDEX);
const float m_factor = 1.0f - factor;
it.out[0] = it.out[0] * factor + center_color[0] * m_factor;
it.out[1] = it.out[1] * factor + center_color[1] * m_factor;
it.out[2] = it.out[2] * factor + center_color[2] * m_factor;
it.out[3] = center_color[3];
/* Make sure we don't return negative color. */
CLAMP4_MIN(it.out, 0.0f);
}
}
} // namespace blender::compositor

View File

@@ -25,6 +25,10 @@ namespace blender::compositor {
class ConvolutionEdgeFilterOperation : public ConvolutionFilterOperation {
public:
void executePixel(float output[4], int x, int y, void *data) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -29,7 +29,7 @@ ConvolutionFilterOperation::ConvolutionFilterOperation()
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
this->flags.complex = true;
}
@@ -127,4 +127,62 @@ bool ConvolutionFilterOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
void ConvolutionFilterOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
switch (input_idx) {
case IMAGE_INPUT_INDEX: {
const int add_x = (m_filterWidth - 1) / 2 + 1;
const int add_y = (m_filterHeight - 1) / 2 + 1;
r_input_area.xmin = output_area.xmin - add_x;
r_input_area.xmax = output_area.xmax + add_x;
r_input_area.ymin = output_area.ymin - add_y;
r_input_area.ymax = output_area.ymax + add_y;
break;
}
case FACTOR_INPUT_INDEX: {
r_input_area = output_area;
break;
}
}
}
void ConvolutionFilterOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX];
const int last_x = getWidth() - 1;
const int last_y = getHeight() - 1;
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const int left_offset = (it.x == 0) ? 0 : -image->elem_stride;
const int right_offset = (it.x == last_x) ? 0 : image->elem_stride;
const int down_offset = (it.y == 0) ? 0 : -image->row_stride;
const int up_offset = (it.y == last_y) ? 0 : image->row_stride;
const float *center_color = it.in(IMAGE_INPUT_INDEX);
zero_v4(it.out);
madd_v4_v4fl(it.out, center_color + down_offset + left_offset, m_filter[0]);
madd_v4_v4fl(it.out, center_color + down_offset, m_filter[1]);
madd_v4_v4fl(it.out, center_color + down_offset + right_offset, m_filter[2]);
madd_v4_v4fl(it.out, center_color + left_offset, m_filter[3]);
madd_v4_v4fl(it.out, center_color, m_filter[4]);
madd_v4_v4fl(it.out, center_color + right_offset, m_filter[5]);
madd_v4_v4fl(it.out, center_color + up_offset + left_offset, m_filter[6]);
madd_v4_v4fl(it.out, center_color + up_offset, m_filter[7]);
madd_v4_v4fl(it.out, center_color + up_offset + right_offset, m_filter[8]);
const float factor = *it.in(FACTOR_INPUT_INDEX);
const float m_factor = 1.0f - factor;
it.out[0] = it.out[0] * factor + center_color[0] * m_factor;
it.out[1] = it.out[1] * factor + center_color[1] * m_factor;
it.out[2] = it.out[2] * factor + center_color[2] * m_factor;
it.out[3] = it.out[3] * factor + center_color[3] * m_factor;
/* Make sure we don't return negative color. */
CLAMP4_MIN(it.out, 0.0f);
}
}
} // namespace blender::compositor

View File

@@ -18,11 +18,15 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
class ConvolutionFilterOperation : public NodeOperation {
class ConvolutionFilterOperation : public MultiThreadedOperation {
protected:
static constexpr int IMAGE_INPUT_INDEX = 0;
static constexpr int FACTOR_INPUT_INDEX = 1;
private:
int m_filterWidth;
int m_filterHeight;
@@ -43,6 +47,11 @@ class ConvolutionFilterOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final;
virtual void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -23,7 +23,7 @@ namespace blender::compositor {
CropBaseOperation::CropBaseOperation()
{
this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Color, ResizeMode::Align);
this->addOutputSocket(DataType::Color);
this->m_inputOperation = nullptr;
this->m_settings = nullptr;
@@ -95,6 +95,22 @@ void CropOperation::executePixelSampled(float output[4], float x, float y, Pixel
}
}
void CropOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
rcti crop_area;
BLI_rcti_init(&crop_area, m_xmin, m_xmax, m_ymin, m_ymax);
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
if (BLI_rcti_isect_pt(&crop_area, it.x, it.y)) {
copy_v4_v4(it.out, it.in(0));
}
else {
zero_v4(it.out);
}
}
}
CropImageOperation::CropImageOperation() : CropBaseOperation()
{
/* pass */
@@ -114,13 +130,24 @@ bool CropImageOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
void CropImageOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
void CropImageOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
NodeOperation::determineResolution(resolution, preferredResolution);
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
r_input_area.xmax = output_area.xmax + this->m_xmin;
r_input_area.xmin = output_area.xmin + this->m_xmin;
r_input_area.ymax = output_area.ymax + this->m_ymin;
r_input_area.ymin = output_area.ymin + this->m_ymin;
}
void CropImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
NodeOperation::determine_canvas(preferred_area, r_area);
updateArea();
resolution[0] = this->m_xmax - this->m_xmin;
resolution[1] = this->m_ymax - this->m_ymin;
r_area.xmax = r_area.xmin + (m_xmax - m_xmin);
r_area.ymax = r_area.ymin + (m_ymax - m_ymin);
}
void CropImageOperation::executePixelSampled(float output[4],
@@ -136,4 +163,21 @@ void CropImageOperation::executePixelSampled(float output[4],
}
}
void CropImageOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
rcti op_area;
BLI_rcti_init(&op_area, 0, getWidth(), 0, getHeight());
const MemoryBuffer *input = inputs[0];
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
if (BLI_rcti_isect_pt(&op_area, it.x, it.y)) {
input->read_elem_checked(it.x + this->m_xmin, it.y + this->m_ymin, it.out);
}
else {
zero_v4(it.out);
}
}
}
} // namespace blender::compositor

View File

@@ -18,11 +18,11 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
class CropBaseOperation : public NodeOperation {
class CropBaseOperation : public MultiThreadedOperation {
protected:
SocketReader *m_inputOperation;
NodeTwoXYs *m_settings;
@@ -53,6 +53,10 @@ class CropOperation : public CropBaseOperation {
public:
CropOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
class CropImageOperation : public CropBaseOperation {
@@ -62,9 +66,13 @@ class CropImageOperation : public CropBaseOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
void determine_canvas(const rcti &preferred_area, rcti &r_area) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -71,4 +71,34 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat
}
}
void CryptomatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
zero_v4(it.out);
for (int i = 0; i < it.get_num_inputs(); i++) {
const float *input = it.in(i);
if (i == 0) {
/* Write the front-most object as false color for picking. */
it.out[0] = input[0];
uint32_t m3hash;
::memcpy(&m3hash, &input[0], sizeof(uint32_t));
/* Since the red channel is likely to be out of display range,
* setting green and blue gives more meaningful images. */
it.out[1] = ((float)(m3hash << 8) / (float)UINT32_MAX);
it.out[2] = ((float)(m3hash << 16) / (float)UINT32_MAX);
}
for (const float hash : m_objectIndex) {
if (input[0] == hash) {
it.out[3] += input[1];
}
if (input[2] == hash) {
it.out[3] += input[3];
}
}
}
}
}
} // namespace blender::compositor

View File

@@ -18,11 +18,11 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
class CryptomatteOperation : public NodeOperation {
class CryptomatteOperation : public MultiThreadedOperation {
private:
Vector<float> m_objectIndex;
@@ -35,6 +35,10 @@ class CryptomatteOperation : public NodeOperation {
void executePixel(float output[4], int x, int y, void *data) override;
void addObjectIndex(float objectIndex);
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -35,6 +35,8 @@ DenoiseOperation::DenoiseOperation()
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
this->m_settings = nullptr;
flags.is_fullframe_operation = true;
output_rendered_ = false;
}
void DenoiseOperation::initExecution()
{
@@ -63,8 +65,7 @@ MemoryBuffer *DenoiseOperation::createMemoryBuffer(rcti *rect2)
rect.xmax = getWidth();
rect.ymax = getHeight();
MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect);
float *data = result->getBuffer();
this->generateDenoise(data, tileColor, tileNormal, tileAlbedo, this->m_settings);
this->generateDenoise(result, tileColor, tileNormal, tileAlbedo, this->m_settings);
return result;
}
@@ -84,23 +85,33 @@ bool DenoiseOperation::determineDependingAreaOfInterest(rcti * /*input*/,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
void DenoiseOperation::generateDenoise(float *data,
MemoryBuffer *inputTileColor,
MemoryBuffer *inputTileNormal,
MemoryBuffer *inputTileAlbedo,
void DenoiseOperation::generateDenoise(MemoryBuffer *output,
MemoryBuffer *input_color,
MemoryBuffer *input_normal,
MemoryBuffer *input_albedo,
NodeDenoise *settings)
{
float *inputBufferColor = inputTileColor->getBuffer();
BLI_assert(inputBufferColor);
if (!inputBufferColor) {
BLI_assert(input_color->getBuffer());
if (!input_color->getBuffer()) {
return;
}
#ifdef WITH_OPENIMAGEDENOISE
/* Always supported through Accelerate framework BNNS on macOS. */
# ifndef __APPLE__
if (BLI_cpu_support_sse41())
# endif
{
/* OpenImageDenoise needs full buffers. */
MemoryBuffer *buf_color = input_color->is_a_single_elem() ? input_color->inflate() :
input_color;
MemoryBuffer *buf_normal = input_normal && input_normal->is_a_single_elem() ?
input_normal->inflate() :
input_normal;
MemoryBuffer *buf_albedo = input_albedo && input_albedo->is_a_single_elem() ?
input_albedo->inflate() :
input_albedo;
/* Since it's memory intensive, it's better to run only one instance of OIDN at a time.
* OpenImageDenoise is multithreaded internally and should use all available cores nonetheless.
*/
@@ -111,35 +122,35 @@ void DenoiseOperation::generateDenoise(float *data,
oidn::FilterRef filter = device.newFilter("RT");
filter.setImage("color",
inputBufferColor,
buf_color->getBuffer(),
oidn::Format::Float3,
inputTileColor->getWidth(),
inputTileColor->getHeight(),
buf_color->getWidth(),
buf_color->getHeight(),
0,
sizeof(float[4]));
if (inputTileNormal && inputTileNormal->getBuffer()) {
if (buf_normal && buf_normal->getBuffer()) {
filter.setImage("normal",
inputTileNormal->getBuffer(),
buf_normal->getBuffer(),
oidn::Format::Float3,
inputTileNormal->getWidth(),
inputTileNormal->getHeight(),
buf_normal->getWidth(),
buf_normal->getHeight(),
0,
sizeof(float[3]));
}
if (inputTileAlbedo && inputTileAlbedo->getBuffer()) {
if (buf_albedo && buf_albedo->getBuffer()) {
filter.setImage("albedo",
inputTileAlbedo->getBuffer(),
buf_albedo->getBuffer(),
oidn::Format::Float3,
inputTileAlbedo->getWidth(),
inputTileAlbedo->getHeight(),
buf_albedo->getWidth(),
buf_albedo->getHeight(),
0,
sizeof(float[4]));
}
filter.setImage("output",
data,
output->getBuffer(),
oidn::Format::Float3,
inputTileColor->getWidth(),
inputTileColor->getHeight(),
buf_color->getWidth(),
buf_color->getHeight(),
0,
sizeof(float[4]));
@@ -153,19 +164,43 @@ void DenoiseOperation::generateDenoise(float *data,
filter.execute();
BLI_mutex_unlock(&oidn_lock);
/* copy the alpha channel, OpenImageDenoise currently only supports RGB */
size_t numPixels = inputTileColor->getWidth() * inputTileColor->getHeight();
for (size_t i = 0; i < numPixels; i++) {
data[i * 4 + 3] = inputBufferColor[i * 4 + 3];
/* Copy the alpha channel, OpenImageDenoise currently only supports RGB. */
output->copy_from(input_color, input_color->get_rect(), 3, COM_DATA_TYPE_VALUE_CHANNELS, 3);
/* Delete inflated buffers. */
if (input_color->is_a_single_elem()) {
delete buf_color;
}
if (input_normal && input_normal->is_a_single_elem()) {
delete buf_normal;
}
if (input_albedo && input_albedo->is_a_single_elem()) {
delete buf_albedo;
}
return;
}
#endif
/* If built without OIDN or running on an unsupported CPU, just pass through. */
UNUSED_VARS(inputTileAlbedo, inputTileNormal, settings);
::memcpy(data,
inputBufferColor,
sizeof(float[4]) * inputTileColor->getWidth() * inputTileColor->getHeight());
UNUSED_VARS(input_albedo, input_normal, settings);
output->copy_from(input_color, input_color->get_rect());
}
void DenoiseOperation::get_area_of_interest(const int UNUSED(input_idx),
const rcti &UNUSED(output_area),
rcti &r_input_area)
{
r_input_area = this->get_canvas();
}
void DenoiseOperation::update_memory_buffer(MemoryBuffer *output,
const rcti &UNUSED(area),
Span<MemoryBuffer *> inputs)
{
if (!output_rendered_) {
this->generateDenoise(output, inputs[0], inputs[1], inputs[2], m_settings);
output_rendered_ = true;
}
}
} // namespace blender::compositor

View File

@@ -37,6 +37,8 @@ class DenoiseOperation : public SingleThreadedOperation {
*/
NodeDenoise *m_settings;
bool output_rendered_;
public:
DenoiseOperation();
/**
@@ -57,11 +59,16 @@ class DenoiseOperation : public SingleThreadedOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
protected:
void generateDenoise(float *data,
MemoryBuffer *inputTileColor,
MemoryBuffer *inputTileNormal,
MemoryBuffer *inputTileAlbedo,
void generateDenoise(MemoryBuffer *output,
MemoryBuffer *input_color,
MemoryBuffer *input_normal,
MemoryBuffer *input_albedo,
NodeDenoise *settings);
MemoryBuffer *createMemoryBuffer(rcti *rect) override;

View File

@@ -29,7 +29,7 @@ DespeckleOperation::DespeckleOperation()
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
this->flags.complex = true;
}
@@ -127,6 +127,11 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da
else {
copy_v4_v4(output, color_org);
}
#undef TOT_DIV_ONE
#undef TOT_DIV_CNR
#undef WTOT
#undef COLOR_ADD
}
bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input,
@@ -144,4 +149,106 @@ bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
void DespeckleOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
switch (input_idx) {
case IMAGE_INPUT_INDEX: {
const int add_x = 2; //(this->m_filterWidth - 1) / 2 + 1;
const int add_y = 2; //(this->m_filterHeight - 1) / 2 + 1;
r_input_area.xmin = output_area.xmin - add_x;
r_input_area.xmax = output_area.xmax + add_x;
r_input_area.ymin = output_area.ymin - add_y;
r_input_area.ymax = output_area.ymax + add_y;
break;
}
case FACTOR_INPUT_INDEX: {
r_input_area = output_area;
break;
}
}
}
void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX];
const int last_x = getWidth() - 1;
const int last_y = getHeight() - 1;
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const int x1 = MAX2(it.x - 1, 0);
const int x2 = it.x;
const int x3 = MIN2(it.x + 1, last_x);
const int y1 = MAX2(it.y - 1, 0);
const int y2 = it.y;
const int y3 = MIN2(it.y + 1, last_y);
float w = 0.0f;
const float *color_org = it.in(IMAGE_INPUT_INDEX);
float color_mid[4];
float color_mid_ok[4];
const float *in1 = nullptr;
#define TOT_DIV_ONE 1.0f
#define TOT_DIV_CNR (float)M_SQRT1_2
#define WTOT (TOT_DIV_ONE * 4 + TOT_DIV_CNR * 4)
#define COLOR_ADD(fac) \
{ \
madd_v4_v4fl(color_mid, in1, fac); \
if (color_diff(in1, color_org, m_threshold)) { \
w += fac; \
madd_v4_v4fl(color_mid_ok, in1, fac); \
} \
}
zero_v4(color_mid);
zero_v4(color_mid_ok);
in1 = image->get_elem(x1, y1);
COLOR_ADD(TOT_DIV_CNR)
in1 = image->get_elem(x2, y1);
COLOR_ADD(TOT_DIV_ONE)
in1 = image->get_elem(x3, y1);
COLOR_ADD(TOT_DIV_CNR)
in1 = image->get_elem(x1, y2);
COLOR_ADD(TOT_DIV_ONE)
#if 0
const float* in2 = image->get_elem(x2, y2);
madd_v4_v4fl(color_mid, in2, this->m_filter[4]);
#endif
in1 = image->get_elem(x3, y2);
COLOR_ADD(TOT_DIV_ONE)
in1 = image->get_elem(x1, y3);
COLOR_ADD(TOT_DIV_CNR)
in1 = image->get_elem(x2, y3);
COLOR_ADD(TOT_DIV_ONE)
in1 = image->get_elem(x3, y3);
COLOR_ADD(TOT_DIV_CNR)
mul_v4_fl(color_mid, 1.0f / (4.0f + (4.0f * (float)M_SQRT1_2)));
// mul_v4_fl(color_mid, 1.0f / w);
if ((w != 0.0f) && ((w / WTOT) > (m_threshold_neighbor)) &&
color_diff(color_mid, color_org, m_threshold)) {
const float factor = *it.in(FACTOR_INPUT_INDEX);
mul_v4_fl(color_mid_ok, 1.0f / w);
interp_v4_v4v4(it.out, color_org, color_mid_ok, factor);
}
else {
copy_v4_v4(it.out, color_org);
}
#undef TOT_DIV_ONE
#undef TOT_DIV_CNR
#undef WTOT
#undef COLOR_ADD
}
}
} // namespace blender::compositor

View File

@@ -18,12 +18,15 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
class DespeckleOperation : public NodeOperation {
class DespeckleOperation : public MultiThreadedOperation {
private:
constexpr static int IMAGE_INPUT_INDEX = 0;
constexpr static int FACTOR_INPUT_INDEX = 1;
float m_threshold;
float m_threshold_neighbor;
@@ -52,6 +55,11 @@ class DespeckleOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -29,6 +29,7 @@ DifferenceMatteOperation::DifferenceMatteOperation()
this->m_inputImage1Program = nullptr;
this->m_inputImage2Program = nullptr;
flags.can_be_constant = true;
}
void DifferenceMatteOperation::initExecution()
@@ -86,4 +87,44 @@ void DifferenceMatteOperation::executePixelSampled(float output[4],
}
}
void DifferenceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const float *color1 = it.in(0);
const float *color2 = it.in(1);
float difference = (fabsf(color2[0] - color1[0]) + fabsf(color2[1] - color1[1]) +
fabsf(color2[2] - color1[2]));
/* Average together the distances. */
difference = difference / 3.0f;
const float tolerance = m_settings->t1;
const float falloff = m_settings->t2;
/* Make 100% transparent. */
if (difference <= tolerance) {
it.out[0] = 0.0f;
}
/* In the falloff region, make partially transparent. */
else if (difference <= falloff + tolerance) {
difference = difference - tolerance;
const float alpha = difference / falloff;
/* Only change if more transparent than before. */
if (alpha < color1[3]) {
it.out[0] = alpha;
}
else { /* Leave as before. */
it.out[0] = color1[3];
}
}
else {
/* Foreground object. */
it.out[0] = color1[3];
}
}
}
} // namespace blender::compositor

View File

@@ -18,7 +18,7 @@
#pragma once
#include "COM_MixOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
class DifferenceMatteOperation : public NodeOperation {
class DifferenceMatteOperation : public MultiThreadedOperation {
private:
NodeChroma *m_settings;
SocketReader *m_inputImage1Program;
@@ -50,6 +50,10 @@ class DifferenceMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -35,9 +35,9 @@ DilateErodeThresholdOperation::DilateErodeThresholdOperation()
this->m__switch = 0.5f;
this->m_distance = 0.0f;
}
void DilateErodeThresholdOperation::initExecution()
void DilateErodeThresholdOperation::init_data()
{
this->m_inputProgram = this->getInputSocketReader(0);
if (this->m_distance < 0.0f) {
this->m_scope = -this->m_distance + this->m_inset;
}
@@ -54,6 +54,11 @@ void DilateErodeThresholdOperation::initExecution()
}
}
void DilateErodeThresholdOperation::initExecution()
{
this->m_inputProgram = this->getInputSocketReader(0);
}
void *DilateErodeThresholdOperation::initializeTileData(rcti * /*rect*/)
{
void *buffer = this->m_inputProgram->initializeTileData(nullptr);
@@ -160,6 +165,112 @@ bool DilateErodeThresholdOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
void DilateErodeThresholdOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
r_input_area.xmin = output_area.xmin - m_scope;
r_input_area.xmax = output_area.xmax + m_scope;
r_input_area.ymin = output_area.ymin - m_scope;
r_input_area.ymax = output_area.ymax + m_scope;
}
struct DilateErodeThresholdOperation::PixelData {
int x;
int y;
int xmin;
int xmax;
int ymin;
int ymax;
const float *elem;
float distance;
int elem_stride;
int row_stride;
/** Switch. */
float sw;
};
template<template<typename> typename TCompare>
static float get_min_distance(DilateErodeThresholdOperation::PixelData &p)
{
/* TODO(manzanilla): bad performance, generate a table with relative offsets on operation
* initialization to loop from less to greater distance and break as soon as #compare is
* true. */
const TCompare compare;
float min_dist = p.distance;
const float *row = p.elem + ((intptr_t)p.ymin - p.y) * p.row_stride +
((intptr_t)p.xmin - p.x) * p.elem_stride;
for (int yi = p.ymin; yi < p.ymax; yi++) {
const float dy = yi - p.y;
const float dist_y = dy * dy;
const float *elem = row;
for (int xi = p.xmin; xi < p.xmax; xi++) {
if (compare(*elem, p.sw)) {
const float dx = xi - p.x;
const float dist = dx * dx + dist_y;
min_dist = MIN2(min_dist, dist);
}
elem += p.elem_stride;
}
row += p.row_stride;
}
return min_dist;
}
void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input = inputs[0];
const rcti &input_rect = input->get_rect();
const float rd = m_scope * m_scope;
const float inset = m_inset;
PixelData p;
p.sw = m__switch;
p.distance = rd * 2;
p.elem_stride = input->elem_stride;
p.row_stride = input->row_stride;
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
p.x = it.x;
p.y = it.y;
p.xmin = MAX2(p.x - m_scope, input_rect.xmin);
p.ymin = MAX2(p.y - m_scope, input_rect.ymin);
p.xmax = MIN2(p.x + m_scope, input_rect.xmax);
p.ymax = MIN2(p.y + m_scope, input_rect.ymax);
p.elem = it.in(0);
float pixel_value;
if (*p.elem > p.sw) {
pixel_value = -sqrtf(get_min_distance<std::less>(p));
}
else {
pixel_value = sqrtf(get_min_distance<std::greater>(p));
}
if (m_distance > 0.0f) {
const float delta = m_distance - pixel_value;
if (delta >= 0.0f) {
*it.out = delta >= inset ? 1.0f : delta / inset;
}
else {
*it.out = 0.0f;
}
}
else {
const float delta = -m_distance + pixel_value;
if (delta < 0.0f) {
*it.out = delta < -inset ? 1.0f : (-delta) / inset;
}
else {
*it.out = 0.0f;
}
}
}
}
// Dilate Distance
DilateDistanceOperation::DilateDistanceOperation()
{
@@ -170,15 +281,20 @@ DilateDistanceOperation::DilateDistanceOperation()
flags.complex = true;
flags.open_cl = true;
}
void DilateDistanceOperation::initExecution()
void DilateDistanceOperation::init_data()
{
this->m_inputProgram = this->getInputSocketReader(0);
this->m_scope = this->m_distance;
if (this->m_scope < 3) {
this->m_scope = 3;
}
}
void DilateDistanceOperation::initExecution()
{
this->m_inputProgram = this->getInputSocketReader(0);
}
void *DilateDistanceOperation::initializeTileData(rcti * /*rect*/)
{
void *buffer = this->m_inputProgram->initializeTileData(nullptr);
@@ -258,6 +374,92 @@ void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device,
device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this);
}
void DilateDistanceOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
r_input_area.xmin = output_area.xmin - m_scope;
r_input_area.xmax = output_area.xmax + m_scope;
r_input_area.ymin = output_area.ymin - m_scope;
r_input_area.ymax = output_area.ymax + m_scope;
}
struct DilateDistanceOperation::PixelData {
int x;
int y;
int xmin;
int xmax;
int ymin;
int ymax;
const float *elem;
float min_distance;
int scope;
int elem_stride;
int row_stride;
const rcti &input_rect;
PixelData(MemoryBuffer *input, const int distance, const int scope)
: min_distance(distance * distance),
scope(scope),
elem_stride(input->elem_stride),
row_stride(input->row_stride),
input_rect(input->get_rect())
{
}
void update(BuffersIterator<float> &it)
{
x = it.x;
y = it.y;
xmin = MAX2(x - scope, input_rect.xmin);
ymin = MAX2(y - scope, input_rect.ymin);
xmax = MIN2(x + scope, input_rect.xmax);
ymax = MIN2(y + scope, input_rect.ymax);
elem = it.in(0);
}
};
template<template<typename> typename TCompare>
static float get_distance_value(DilateDistanceOperation::PixelData &p, const float start_value)
{
/* TODO(manzanilla): bad performance, only loop elements within minimum distance removing
* coordinates and conditional if `dist <= min_dist`. May need to generate a table of offsets. */
const TCompare compare;
const float min_dist = p.min_distance;
float value = start_value;
const float *row = p.elem + ((intptr_t)p.ymin - p.y) * p.row_stride +
((intptr_t)p.xmin - p.x) * p.elem_stride;
for (int yi = p.ymin; yi < p.ymax; yi++) {
const float dy = yi - p.y;
const float dist_y = dy * dy;
const float *elem = row;
for (int xi = p.xmin; xi < p.xmax; xi++) {
const float dx = xi - p.x;
const float dist = dx * dx + dist_y;
if (dist <= min_dist) {
value = compare(*elem, value) ? *elem : value;
}
elem += p.elem_stride;
}
row += p.row_stride;
}
return value;
}
void DilateDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
PixelData p(inputs[0], m_distance, m_scope);
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
p.update(it);
*it.out = get_distance_value<std::greater>(p, 0.0f);
}
}
/* Erode Distance */
ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation()
{
@@ -318,6 +520,17 @@ void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device,
device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this);
}
void ErodeDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
PixelData p(inputs[0], m_distance, m_scope);
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
p.update(it);
*it.out = get_distance_value<std::less>(p, 1.0f);
}
}
/* Dilate step */
DilateStepOperation::DilateStepOperation()
{
@@ -475,6 +688,124 @@ bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
void DilateStepOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
r_input_area.xmin = output_area.xmin - m_iterations;
r_input_area.xmax = output_area.xmax + m_iterations;
r_input_area.ymin = output_area.ymin - m_iterations;
r_input_area.ymax = output_area.ymax + m_iterations;
}
template<typename TCompareSelector>
static void step_update_memory_buffer(MemoryBuffer *output,
const MemoryBuffer *input,
const rcti &area,
const int num_iterations,
const float compare_min_value)
{
TCompareSelector selector;
const int width = output->getWidth();
const int height = output->getHeight();
const int half_window = num_iterations;
const int window = half_window * 2 + 1;
const int xmin = MAX2(0, area.xmin - half_window);
const int ymin = MAX2(0, area.ymin - half_window);
const int xmax = MIN2(width, area.xmax + half_window);
const int ymax = MIN2(height, area.ymax + half_window);
const int bwidth = area.xmax - area.xmin;
const int bheight = area.ymax - area.ymin;
/* Create a buffer with the area needed for horizontal and vertical passes. */
rcti result_area;
BLI_rcti_init(&result_area, xmin, xmax, ymin, ymax);
MemoryBuffer result(DataType::Value, result_area);
/* #temp holds maxima for every step in the algorithm, #buf holds a
* single row or column of input values, padded with #limit values to
* simplify the logic. */
float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp");
float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window),
"dilate erode buf");
/* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. */
/* First pass, horizontal dilate/erode. */
for (int y = ymin; y < ymax; y++) {
for (int x = 0; x < bwidth + 5 * half_window; x++) {
buf[x] = compare_min_value;
}
for (int x = xmin; x < xmax; x++) {
buf[x - area.xmin + window - 1] = input->get_value(x, y, 0);
}
for (int i = 0; i < (bwidth + 3 * half_window) / window; i++) {
int start = (i + 1) * window - 1;
temp[window - 1] = buf[start];
for (int x = 1; x < window; x++) {
temp[window - 1 - x] = selector(temp[window - x], buf[start - x]);
temp[window - 1 + x] = selector(temp[window + x - 2], buf[start + x]);
}
start = half_window + (i - 1) * window + 1;
for (int x = -MIN2(0, start); x < window - MAX2(0, start + window - bwidth); x++) {
result.get_value(start + x + area.xmin, y, 0) = selector(temp[x], temp[x + window - 1]);
}
}
}
/* Second pass, vertical dilate/erode. */
for (int x = xmin; x < xmax; x++) {
for (int y = 0; y < bheight + 5 * half_window; y++) {
buf[y] = compare_min_value;
}
for (int y = ymin; y < ymax; y++) {
buf[y - area.ymin + window - 1] = result.get_value(x, y, 0);
}
for (int i = 0; i < (bheight + 3 * half_window) / window; i++) {
int start = (i + 1) * window - 1;
temp[window - 1] = buf[start];
for (int y = 1; y < window; y++) {
temp[window - 1 - y] = selector(temp[window - y], buf[start - y]);
temp[window - 1 + y] = selector(temp[window + y - 2], buf[start + y]);
}
start = half_window + (i - 1) * window + 1;
for (int y = -MIN2(0, start); y < window - MAX2(0, start + window - bheight); y++) {
result.get_value(x, y + start + area.ymin, 0) = selector(temp[y], temp[y + window - 1]);
}
}
}
MEM_freeN(temp);
MEM_freeN(buf);
output->copy_from(&result, area);
}
struct Max2Selector {
float operator()(float f1, float f2) const
{
return MAX2(f1, f2);
}
};
void DilateStepOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
step_update_memory_buffer<Max2Selector>(output, inputs[0], area, m_iterations, -FLT_MAX);
}
/* Erode step */
ErodeStepOperation::ErodeStepOperation() : DilateStepOperation()
{
@@ -571,4 +902,18 @@ void *ErodeStepOperation::initializeTileData(rcti *rect)
return result;
}
struct Min2Selector {
float operator()(float f1, float f2) const
{
return MIN2(f1, f2);
}
};
void ErodeStepOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
step_update_memory_buffer<Min2Selector>(output, inputs[0], area, m_iterations, FLT_MAX);
}
} // namespace blender::compositor

View File

@@ -18,11 +18,14 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
class DilateErodeThresholdOperation : public NodeOperation {
class DilateErodeThresholdOperation : public MultiThreadedOperation {
public:
struct PixelData;
private:
/**
* Cached reference to the inputProgram
@@ -47,6 +50,7 @@ class DilateErodeThresholdOperation : public NodeOperation {
*/
void executePixel(float output[4], int x, int y, void *data) override;
void init_data() override;
/**
* Initialize the execution
*/
@@ -74,10 +78,17 @@ class DilateErodeThresholdOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
class DilateDistanceOperation : public NodeOperation {
private:
class DilateDistanceOperation : public MultiThreadedOperation {
public:
struct PixelData;
protected:
/**
* Cached reference to the inputProgram
@@ -94,6 +105,7 @@ class DilateDistanceOperation : public NodeOperation {
*/
void executePixel(float output[4], int x, int y, void *data) override;
void init_data() override;
/**
* Initialize the execution
*/
@@ -119,7 +131,13 @@ class DilateDistanceOperation : public NodeOperation {
MemoryBuffer **inputMemoryBuffers,
std::list<cl_mem> *clMemToCleanUp,
std::list<cl_kernel> *clKernelsToCleanUp) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final;
virtual void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
class ErodeDistanceOperation : public DilateDistanceOperation {
public:
ErodeDistanceOperation();
@@ -135,9 +153,13 @@ class ErodeDistanceOperation : public DilateDistanceOperation {
MemoryBuffer **inputMemoryBuffers,
std::list<cl_mem> *clMemToCleanUp,
std::list<cl_kernel> *clKernelsToCleanUp) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
class DilateStepOperation : public NodeOperation {
class DilateStepOperation : public MultiThreadedOperation {
protected:
/**
* Cached reference to the inputProgram
@@ -174,6 +196,11 @@ class DilateStepOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final;
virtual void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
class ErodeStepOperation : public DilateStepOperation {
@@ -181,6 +208,9 @@ class ErodeStepOperation : public DilateStepOperation {
ErodeStepOperation();
void *initializeTileData(rcti *rect) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -146,4 +146,55 @@ bool DirectionalBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
void DirectionalBlurOperation::get_area_of_interest(const int input_idx,
const rcti &UNUSED(output_area),
rcti &r_input_area)
{
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
r_input_area = this->get_canvas();
}
void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input = inputs[0];
const int iterations = pow(2.0f, this->m_data->iter);
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
const int x = it.x;
const int y = it.y;
float color_accum[4];
input->read_elem_bilinear(x, y, color_accum);
/* Blur pixel. */
/* TODO(manzanilla): Many values used on iterations can be calculated beforehand. Create a
* table on operation initialization. */
float ltx = this->m_tx;
float lty = this->m_ty;
float lsc = this->m_sc;
float lrot = this->m_rot;
for (int i = 0; i < iterations; i++) {
const float cs = cosf(lrot), ss = sinf(lrot);
const float isc = 1.0f / (1.0f + lsc);
const float v = isc * (y - this->m_center_y_pix) + lty;
const float u = isc * (x - this->m_center_x_pix) + ltx;
float color[4];
input->read_elem_bilinear(
cs * u + ss * v + this->m_center_x_pix, cs * v - ss * u + this->m_center_y_pix, color);
add_v4_v4(color_accum, color);
/* Double transformations. */
ltx += this->m_tx;
lty += this->m_ty;
lrot += this->m_rot;
lsc += this->m_sc;
}
mul_v4_v4fl(it.out, color_accum, 1.0f / (iterations + 1));
}
}
} // namespace blender::compositor

View File

@@ -18,12 +18,12 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
#include "COM_QualityStepHelper.h"
namespace blender::compositor {
class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper {
class DirectionalBlurOperation : public MultiThreadedOperation, public QualityStepHelper {
private:
SocketReader *m_inputProgram;
NodeDBlurData *m_data;
@@ -65,6 +65,11 @@ class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper
MemoryBuffer **inputMemoryBuffers,
std::list<cl_mem> *clMemToCleanUp,
std::list<cl_kernel> *clKernelsToCleanUp) override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -32,20 +32,30 @@ DisplaceOperation::DisplaceOperation()
this->flags.complex = true;
this->m_inputColorProgram = nullptr;
this->m_inputVectorProgram = nullptr;
this->m_inputScaleXProgram = nullptr;
this->m_inputScaleYProgram = nullptr;
}
void DisplaceOperation::initExecution()
{
this->m_inputColorProgram = this->getInputSocketReader(0);
this->m_inputVectorProgram = this->getInputSocketReader(1);
this->m_inputScaleXProgram = this->getInputSocketReader(2);
this->m_inputScaleYProgram = this->getInputSocketReader(3);
NodeOperation *vector = this->getInputSocketReader(1);
NodeOperation *scale_x = this->getInputSocketReader(2);
NodeOperation *scale_y = this->getInputSocketReader(3);
if (execution_model_ == eExecutionModel::Tiled) {
vector_read_fn_ = [=](float x, float y, float *out) {
vector->readSampled(out, x, y, PixelSampler::Bilinear);
};
scale_x_read_fn_ = [=](float x, float y, float *out) {
scale_x->readSampled(out, x, y, PixelSampler::Nearest);
};
scale_y_read_fn_ = [=](float x, float y, float *out) {
scale_y->readSampled(out, x, y, PixelSampler::Nearest);
};
}
this->m_width_x4 = this->getWidth() * 4;
this->m_height_x4 = this->getHeight() * 4;
input_vector_width_ = vector->getWidth();
input_vector_height_ = vector->getHeight();
}
void DisplaceOperation::executePixelSampled(float output[4],
@@ -69,8 +79,8 @@ void DisplaceOperation::executePixelSampled(float output[4],
bool DisplaceOperation::read_displacement(
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v)
{
float width = m_inputVectorProgram->getWidth();
float height = m_inputVectorProgram->getHeight();
float width = input_vector_width_;
float height = input_vector_height_;
if (x < 0.0f || x >= width || y < 0.0f || y >= height) {
r_u = 0.0f;
r_v = 0.0f;
@@ -78,7 +88,7 @@ bool DisplaceOperation::read_displacement(
}
float col[4];
m_inputVectorProgram->readSampled(col, x, y, PixelSampler::Bilinear);
vector_read_fn_(x, y, col);
r_u = origin[0] - col[0] * xscale;
r_v = origin[1] - col[1] * yscale;
return true;
@@ -90,9 +100,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
float uv[2]; /* temporary variables for derivative estimation */
int num;
m_inputScaleXProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
scale_x_read_fn_(xy[0], xy[1], col);
float xs = col[0];
m_inputScaleYProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
scale_y_read_fn_(xy[0], xy[1], col);
float ys = col[0];
/* clamp x and y displacement to triple image resolution -
* to prevent hangs from huge values mistakenly plugged in eg. z buffers */
@@ -146,9 +156,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
void DisplaceOperation::deinitExecution()
{
this->m_inputColorProgram = nullptr;
this->m_inputVectorProgram = nullptr;
this->m_inputScaleXProgram = nullptr;
this->m_inputScaleYProgram = nullptr;
vector_read_fn_ = nullptr;
scale_x_read_fn_ = nullptr;
scale_y_read_fn_ = nullptr;
}
bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
@@ -195,4 +205,58 @@ bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
void DisplaceOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
switch (input_idx) {
case 0: {
r_input_area = getInputOperation(input_idx)->get_canvas();
break;
}
case 1: {
r_input_area = output_area;
expand_area_for_sampler(r_input_area, PixelSampler::Bilinear);
break;
}
default: {
r_input_area = output_area;
break;
}
}
}
void DisplaceOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
const rcti &UNUSED(area),
Span<MemoryBuffer *> inputs)
{
MemoryBuffer *vector = inputs[1];
MemoryBuffer *scale_x = inputs[2];
MemoryBuffer *scale_y = inputs[3];
vector_read_fn_ = [=](float x, float y, float *out) { vector->read_elem_bilinear(x, y, out); };
scale_x_read_fn_ = [=](float x, float y, float *out) { scale_x->read_elem_checked(x, y, out); };
scale_y_read_fn_ = [=](float x, float y, float *out) { scale_y->read_elem_checked(x, y, out); };
}
void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input_color = inputs[0];
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
const float xy[2] = {(float)it.x, (float)it.y};
float uv[2];
float deriv[2][2];
pixelTransform(xy, uv, deriv);
if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) {
input_color->read_elem_bilinear(uv[0], uv[1], it.out);
}
else {
/* EWA filtering (without nearest it gets blurry with NO distortion). */
input_color->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out);
}
}
}
} // namespace blender::compositor

View File

@@ -18,23 +18,27 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
class DisplaceOperation : public NodeOperation {
class DisplaceOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
*/
SocketReader *m_inputColorProgram;
SocketReader *m_inputVectorProgram;
SocketReader *m_inputScaleXProgram;
SocketReader *m_inputScaleYProgram;
float m_width_x4;
float m_height_x4;
int input_vector_width_;
int input_vector_height_;
std::function<void(float x, float y, float *out)> vector_read_fn_;
std::function<void(float x, float y, float *out)> scale_x_read_fn_;
std::function<void(float x, float y, float *out)> scale_y_read_fn_;
public:
DisplaceOperation();
@@ -62,6 +66,14 @@ class DisplaceOperation : public NodeOperation {
*/
void deinitExecution() override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_started(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
private:
bool read_displacement(
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v);

View File

@@ -132,4 +132,53 @@ bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
void DisplaceSimpleOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
switch (input_idx) {
case 0: {
r_input_area = get_input_operation(input_idx)->get_canvas();
break;
}
default: {
r_input_area = output_area;
break;
}
}
}
void DisplaceSimpleOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const float width = this->getWidth();
const float height = this->getHeight();
const MemoryBuffer *input_color = inputs[0];
for (BuffersIterator<float> it = output->iterate_with(inputs.drop_front(1), area); !it.is_end();
++it) {
float scale_x = *it.in(1);
float scale_y = *it.in(2);
/* Clamp x and y displacement to triple image resolution -
* to prevent hangs from huge values mistakenly plugged in eg. z buffers. */
CLAMP(scale_x, -m_width_x4, m_width_x4);
CLAMP(scale_y, -m_height_x4, m_height_x4);
/* Main displacement in pixel space. */
const float *vector = it.in(0);
const float p_dx = vector[0] * scale_x;
const float p_dy = vector[1] * scale_y;
/* Displaced pixel in uv coords, for image sampling. */
/* Clamp nodes to avoid glitches. */
float u = it.x - p_dx + 0.5f;
float v = it.y - p_dy + 0.5f;
CLAMP(u, 0.0f, width - 1.0f);
CLAMP(v, 0.0f, height - 1.0f);
input_color->read_elem_checked(u, v, it.out);
}
}
} // namespace blender::compositor

View File

@@ -18,11 +18,11 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
class DisplaceSimpleOperation : public NodeOperation {
class DisplaceSimpleOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -59,6 +59,11 @@ class DisplaceSimpleOperation : public NodeOperation {
* Deinitialize the execution
*/
void deinitExecution() override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -29,6 +29,7 @@ DistanceRGBMatteOperation::DistanceRGBMatteOperation()
this->m_inputImageProgram = nullptr;
this->m_inputKeyProgram = nullptr;
flags.can_be_constant = true;
}
void DistanceRGBMatteOperation::initExecution()
@@ -43,7 +44,7 @@ void DistanceRGBMatteOperation::deinitExecution()
this->m_inputKeyProgram = nullptr;
}
float DistanceRGBMatteOperation::calculateDistance(float key[4], float image[4])
float DistanceRGBMatteOperation::calculateDistance(const float key[4], const float image[4])
{
return len_v3v3(key, image);
}
@@ -93,4 +94,43 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4],
}
}
void DistanceRGBMatteOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const float *in_image = it.in(0);
const float *in_key = it.in(1);
float distance = this->calculateDistance(in_key, in_image);
const float tolerance = this->m_settings->t1;
const float falloff = this->m_settings->t2;
/* Store matte(alpha) value in [0] to go with
* COM_SetAlphaMultiplyOperation and the Value output.
*/
/* Make 100% transparent. */
if (distance < tolerance) {
it.out[0] = 0.0f;
}
/* In the falloff region, make partially transparent. */
else if (distance < falloff + tolerance) {
distance = distance - tolerance;
const float alpha = distance / falloff;
/* Only change if more transparent than before. */
if (alpha < in_image[3]) {
it.out[0] = alpha;
}
else { /* Leave as before. */
it.out[0] = in_image[3];
}
}
else {
/* Leave as before. */
it.out[0] = in_image[3];
}
}
}
} // namespace blender::compositor

View File

@@ -18,7 +18,7 @@
#pragma once
#include "COM_MixOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,13 +26,13 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
class DistanceRGBMatteOperation : public NodeOperation {
class DistanceRGBMatteOperation : public MultiThreadedOperation {
protected:
NodeChroma *m_settings;
SocketReader *m_inputImageProgram;
SocketReader *m_inputKeyProgram;
virtual float calculateDistance(float key[4], float image[4]);
virtual float calculateDistance(const float key[4], const float image[4]);
public:
/**
@@ -52,6 +52,10 @@ class DistanceRGBMatteOperation : public NodeOperation {
{
this->m_settings = nodeChroma;
}
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -21,7 +21,7 @@
namespace blender::compositor {
float DistanceYCCMatteOperation::calculateDistance(float key[4], float image[4])
float DistanceYCCMatteOperation::calculateDistance(const float key[4], const float image[4])
{
/* only measure the second 2 values */
return len_v2v2(key + 1, image + 1);

View File

@@ -29,7 +29,7 @@ namespace blender::compositor {
*/
class DistanceYCCMatteOperation : public DistanceRGBMatteOperation {
protected:
float calculateDistance(float key[4], float image[4]) override;
float calculateDistance(const float key[4], const float image[4]) override;
};
} // namespace blender::compositor

View File

@@ -25,9 +25,10 @@ DotproductOperation::DotproductOperation()
this->addInputSocket(DataType::Vector);
this->addInputSocket(DataType::Vector);
this->addOutputSocket(DataType::Value);
this->setResolutionInputSocketIndex(0);
this->set_canvas_input_index(0);
this->m_input1Operation = nullptr;
this->m_input2Operation = nullptr;
flags.can_be_constant = true;
}
void DotproductOperation::initExecution()
{
@@ -55,4 +56,15 @@ void DotproductOperation::executePixelSampled(float output[4],
output[0] = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]);
}
void DotproductOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const float *input1 = it.in(0);
const float *input2 = it.in(1);
*it.out = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]);
}
}
} // namespace blender::compositor

View File

@@ -18,11 +18,11 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
class DotproductOperation : public NodeOperation {
class DotproductOperation : public MultiThreadedOperation {
private:
SocketReader *m_input1Operation;
SocketReader *m_input2Operation;
@@ -33,6 +33,10 @@ class DotproductOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -1399,10 +1399,7 @@ void DoubleEdgeMaskOperation::get_area_of_interest(int UNUSED(input_idx),
const rcti &UNUSED(output_area),
rcti &r_input_area)
{
r_input_area.xmax = this->getWidth();
r_input_area.xmin = 0;
r_input_area.ymax = this->getHeight();
r_input_area.ymin = 0;
r_input_area = this->get_canvas();
}
void DoubleEdgeMaskOperation::update_memory_buffer(MemoryBuffer *output,

View File

@@ -62,6 +62,13 @@ bool FastGaussianBlurOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
void FastGaussianBlurOperation::init_data()
{
BlurBaseOperation::init_data();
this->m_sx = this->m_data.sizex * this->m_size / 2.0f;
this->m_sy = this->m_data.sizey * this->m_size / 2.0f;
}
void FastGaussianBlurOperation::initExecution()
{
BlurBaseOperation::initExecution();
@@ -117,6 +124,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
unsigned int chan,
unsigned int xy)
{
BLI_assert(!src->is_a_single_elem());
double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3];
double *X, *Y, *W;
const unsigned int src_width = src->getWidth();
@@ -257,6 +265,61 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src,
#undef YVV
}
void FastGaussianBlurOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
switch (input_idx) {
case IMAGE_INPUT_INDEX:
r_input_area = this->get_canvas();
break;
default:
BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
return;
}
}
void FastGaussianBlurOperation::update_memory_buffer_started(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
/* TODO(manzanilla): Add a render test and make #IIR_gauss multi-threaded with support for
* an output buffer. */
const MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
MemoryBuffer *image = nullptr;
const bool is_full_output = BLI_rcti_compare(&output->get_rect(), &area);
if (is_full_output) {
image = output;
}
else {
image = new MemoryBuffer(getOutputSocket()->getDataType(), area);
}
image->copy_from(input, area);
if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) {
for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
IIR_gauss(image, this->m_sx, c, 3);
}
}
else {
if (this->m_sx > 0.0f) {
for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
IIR_gauss(image, this->m_sx, c, 1);
}
}
if (this->m_sy > 0.0f) {
for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
IIR_gauss(image, this->m_sy, c, 2);
}
}
}
if (!is_full_output) {
output->copy_from(image, area);
delete image;
}
}
FastGaussianBlurValueOperation::FastGaussianBlurValueOperation()
{
this->addInputSocket(DataType::Value);
@@ -341,4 +404,41 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect)
return this->m_iirgaus;
}
void FastGaussianBlurValueOperation::get_area_of_interest(const int UNUSED(input_idx),
const rcti &UNUSED(output_area),
rcti &r_input_area)
{
r_input_area = this->get_canvas();
}
void FastGaussianBlurValueOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
const rcti &UNUSED(area),
Span<MemoryBuffer *> inputs)
{
if (m_iirgaus == nullptr) {
const MemoryBuffer *image = inputs[0];
MemoryBuffer *gauss = new MemoryBuffer(*image);
FastGaussianBlurOperation::IIR_gauss(gauss, m_sigma, 0, 3);
m_iirgaus = gauss;
}
}
void FastGaussianBlurValueOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
MemoryBuffer *image = inputs[0];
BuffersIterator<float> it = output->iterate_with({image, m_iirgaus}, area);
if (this->m_overlay == FAST_GAUSS_OVERLAY_MIN) {
for (; !it.is_end(); ++it) {
*it.out = MIN2(*it.in(0), *it.in(1));
}
}
else if (this->m_overlay == FAST_GAUSS_OVERLAY_MAX) {
for (; !it.is_end(); ++it) {
*it.out = MAX2(*it.in(0), *it.in(1));
}
}
}
} // namespace blender::compositor

View File

@@ -38,8 +38,19 @@ class FastGaussianBlurOperation : public BlurBaseOperation {
static void IIR_gauss(MemoryBuffer *src, float sigma, unsigned int channel, unsigned int xy);
void *initializeTileData(rcti *rect) override;
void init_data() override;
void deinitExecution() override;
void initExecution() override;
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_started(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
void update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
const rcti &UNUSED(area),
Span<MemoryBuffer *> UNUSED(inputs)) override
{
}
};
enum {
@@ -48,7 +59,7 @@ enum {
FAST_GAUSS_OVERLAY_MAX = 1,
};
class FastGaussianBlurValueOperation : public NodeOperation {
class FastGaussianBlurValueOperation : public MultiThreadedOperation {
private:
float m_sigma;
MemoryBuffer *m_iirgaus;
@@ -80,6 +91,14 @@ class FastGaussianBlurValueOperation : public NodeOperation {
{
this->m_overlay = overlay;
}
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_started(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -24,7 +24,7 @@ FlipOperation::FlipOperation()
{
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
this->set_canvas_input_index(0);
this->m_inputOperation = nullptr;
this->m_flipX = true;
this->m_flipY = false;
@@ -75,4 +75,42 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
void FlipOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
BLI_assert(input_idx == 0);
UNUSED_VARS_NDEBUG(input_idx);
if (this->m_flipX) {
const int w = (int)this->getWidth() - 1;
r_input_area.xmax = (w - output_area.xmin) + 1;
r_input_area.xmin = (w - output_area.xmax) - 1;
}
else {
r_input_area.xmin = output_area.xmin;
r_input_area.xmax = output_area.xmax;
}
if (this->m_flipY) {
const int h = (int)this->getHeight() - 1;
r_input_area.ymax = (h - output_area.ymin) + 1;
r_input_area.ymin = (h - output_area.ymax) - 1;
}
else {
r_input_area.ymin = output_area.ymin;
r_input_area.ymax = output_area.ymax;
}
}
void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input_img = inputs[0];
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
const int nx = this->m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x;
const int ny = this->m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y;
input_img->read_elem(nx, ny, it.out);
}
}
} // namespace blender::compositor

View File

@@ -18,11 +18,11 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
class FlipOperation : public NodeOperation {
class FlipOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputOperation;
bool m_flipX;
@@ -45,6 +45,11 @@ class FlipOperation : public NodeOperation {
{
this->m_flipY = flipY;
}
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -26,6 +26,7 @@ GammaCorrectOperation::GammaCorrectOperation()
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
this->m_inputProgram = nullptr;
flags.can_be_constant = true;
}
void GammaCorrectOperation::initExecution()
{
@@ -58,6 +59,34 @@ void GammaCorrectOperation::executePixelSampled(float output[4],
}
}
void GammaCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input = inputs[0];
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
float color[4];
input->read_elem(it.x, it.y, color);
if (color[3] > 0.0f) {
color[0] /= color[3];
color[1] /= color[3];
color[2] /= color[3];
}
/* Check for negative to avoid nan's. */
it.out[0] = color[0] > 0.0f ? color[0] * color[0] : 0.0f;
it.out[1] = color[1] > 0.0f ? color[1] * color[1] : 0.0f;
it.out[2] = color[2] > 0.0f ? color[2] * color[2] : 0.0f;
it.out[3] = color[3];
if (color[3] > 0.0f) {
it.out[0] *= color[3];
it.out[1] *= color[3];
it.out[2] *= color[3];
}
}
}
void GammaCorrectOperation::deinitExecution()
{
this->m_inputProgram = nullptr;
@@ -68,6 +97,7 @@ GammaUncorrectOperation::GammaUncorrectOperation()
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
this->m_inputProgram = nullptr;
flags.can_be_constant = true;
}
void GammaUncorrectOperation::initExecution()
{
@@ -100,6 +130,33 @@ void GammaUncorrectOperation::executePixelSampled(float output[4],
}
}
void GammaUncorrectOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input = inputs[0];
for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
float color[4];
input->read_elem(it.x, it.y, color);
if (color[3] > 0.0f) {
color[0] /= color[3];
color[1] /= color[3];
color[2] /= color[3];
}
it.out[0] = color[0] > 0.0f ? sqrtf(color[0]) : 0.0f;
it.out[1] = color[1] > 0.0f ? sqrtf(color[1]) : 0.0f;
it.out[2] = color[2] > 0.0f ? sqrtf(color[2]) : 0.0f;
it.out[3] = color[3];
if (color[3] > 0.0f) {
it.out[0] *= color[3];
it.out[1] *= color[3];
it.out[2] *= color[3];
}
}
}
void GammaUncorrectOperation::deinitExecution()
{
this->m_inputProgram = nullptr;

View File

@@ -18,11 +18,11 @@
#pragma once
#include "COM_NodeOperation.h"
#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
class GammaCorrectOperation : public NodeOperation {
class GammaCorrectOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -46,9 +46,13 @@ class GammaCorrectOperation : public NodeOperation {
* Deinitialize the execution
*/
void deinitExecution() override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
class GammaUncorrectOperation : public NodeOperation {
class GammaUncorrectOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -72,6 +76,10 @@ class GammaUncorrectOperation : public NodeOperation {
* Deinitialize the execution
*/
void deinitExecution() override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@@ -0,0 +1,168 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2021, Blender Foundation.
*/
#include "COM_GaussianAlphaBlurBaseOperation.h"
namespace blender::compositor {
GaussianAlphaBlurBaseOperation::GaussianAlphaBlurBaseOperation(eDimension dim)
: BlurBaseOperation(DataType::Value)
{
this->m_gausstab = nullptr;
this->m_filtersize = 0;
this->m_falloff = -1; /* Intentionally invalid, so we can detect uninitialized values. */
dimension_ = dim;
}
void GaussianAlphaBlurBaseOperation::init_data()
{
BlurBaseOperation::init_data();
if (execution_model_ == eExecutionModel::FullFrame) {
rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f);
rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS);
m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS);
}
}
void GaussianAlphaBlurBaseOperation::initExecution()
{
BlurBaseOperation::initExecution();
if (execution_model_ == eExecutionModel::FullFrame) {
m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize);
m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad_, m_filtersize, m_falloff);
}
}
void GaussianAlphaBlurBaseOperation::deinitExecution()
{
BlurBaseOperation::deinitExecution();
if (this->m_gausstab) {
MEM_freeN(this->m_gausstab);
this->m_gausstab = nullptr;
}
if (this->m_distbuf_inv) {
MEM_freeN(this->m_distbuf_inv);
this->m_distbuf_inv = nullptr;
}
}
void GaussianAlphaBlurBaseOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
if (input_idx != IMAGE_INPUT_INDEX) {
BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
return;
}
r_input_area = output_area;
switch (dimension_) {
case eDimension::X:
r_input_area.xmin = output_area.xmin - m_filtersize - 1;
r_input_area.xmax = output_area.xmax + m_filtersize + 1;
break;
case eDimension::Y:
r_input_area.ymin = output_area.ymin - m_filtersize - 1;
r_input_area.ymax = output_area.ymax + m_filtersize + 1;
break;
}
}
BLI_INLINE float finv_test(const float f, const bool test)
{
return (LIKELY(test == false)) ? f : 1.0f - f;
}
void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
const rcti &input_rect = input->get_rect();
BuffersIterator<float> it = output->iterate_with({input}, area);
int min_input_coord = -1;
int max_input_coord = -1;
int elem_stride = -1;
std::function<int()> get_current_coord;
switch (dimension_) {
case eDimension::X:
min_input_coord = input_rect.xmin;
max_input_coord = input_rect.xmax;
get_current_coord = [&] { return it.x; };
elem_stride = input->elem_stride;
break;
case eDimension::Y:
min_input_coord = input_rect.ymin;
max_input_coord = input_rect.ymax;
get_current_coord = [&] { return it.y; };
elem_stride = input->row_stride;
break;
}
for (; !it.is_end(); ++it) {
const int coord = get_current_coord();
const int coord_min = max_ii(coord - m_filtersize, min_input_coord);
const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord);
/* *** This is the main part which is different to #GaussianBlurBaseOperation. *** */
/* Gauss. */
float alpha_accum = 0.0f;
float multiplier_accum = 0.0f;
/* Dilate. */
const bool do_invert = m_do_subtract;
/* Init with the current color to avoid unneeded lookups. */
float value_max = finv_test(*it.in(0), do_invert);
float distfacinv_max = 1.0f; /* 0 to 1 */
const int step = QualityStepHelper::getStep();
const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride;
const int in_stride = elem_stride * step;
int index = (coord_min - coord) + m_filtersize;
const int index_end = index + (coord_max - coord_min);
for (; index < index_end; in += in_stride, index += step) {
float value = finv_test(*in, do_invert);
/* Gauss. */
float multiplier = m_gausstab[index];
alpha_accum += value * multiplier;
multiplier_accum += multiplier;
/* Dilate - find most extreme color. */
if (value > value_max) {
multiplier = m_distbuf_inv[index];
value *= multiplier;
if (value > value_max) {
value_max = value;
distfacinv_max = multiplier;
}
}
}
/* Blend between the max value and gauss blue - gives nice feather. */
const float value_blur = alpha_accum / multiplier_accum;
const float value_final = (value_max * distfacinv_max) +
(value_blur * (1.0f - distfacinv_max));
*it.out = finv_test(value_final, do_invert);
}
}
} // namespace blender::compositor

Some files were not shown because too many files have changed in this diff Show More