1
1

Compare commits

..

167 Commits

Author SHA1 Message Date
24d1c352e8 Make branch compile after recent multifunction network removal 2021-08-23 14:41:44 -05:00
b3639670ee Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-23 14:10:39 -05:00
be1891e895 Cleanup: move the buffer list to 'MeshBufferCache'
The cache is used to fill the buffer list.
2021-08-23 13:44:31 -03:00
cbe4036406 Cleanup: Isolate the batch list struct into a struct called MeshBatchList
This allows for a simplification of macros and combines with
`MeshBufferList`.
2021-08-23 13:43:45 -03:00
eb0c50ac78 Cleanup: rename 'MeshBufferExtractionCache' to 'MeshBufferCache'
Matches the existing `MeshBatchCache`.
2021-08-23 13:43:42 -03:00
6e51ef9531 Cleanup: rename 'MeshBufferCache' to 'MeshBufferList'
`MeshBufferList` is more specific and can avoid confusion with
`MeshBufferExtractionCache`.
2021-08-23 13:41:03 -03:00
Germano Cavalcante
ebdae75736 Cleanup: Move 'tris_per_mat' member out of 'MeshBufferCache'
`MeshBufferCache` is a struct representing a list of buffers.

As such, `GPUIndexBuf **tris_per_mat` is out of place as it does not
represent one of the buffers in the list.

In fact this member should be close to `GPUBatch **surface_per_mat` as
they are related.

The code for dependencies between buffer and batch had to be reworked
as it relies on the member's position.

Differential Revision: https://developer.blender.org/D12227
2021-08-23 13:37:32 -03:00
7d17f2addf Fix T89998: Cryptomatte node output values doubled with Multi-View
When using a Cryptomatte node and selecting 2 views in Multi-View,
its output values are doubled. When selecting 3 tripled and so on.
This causes incorrect compositing results for all the views.

The node creates an input operation for each rendered cryptomatte
pass. In Multi-View, passes are rendered for each view but compositor
is executed per view and should only create operations for those
corresponding to the current view being executed. Otherwise duplicated
operations add up later in cryptomatte operation.

Reviewed By: jbakker

Maniphest Tasks: T89998

Differential Revision: https://developer.blender.org/D12216
2021-08-23 17:09:59 +02:00
4c6d207343 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-23 17:09:59 +02:00
42f89b9212 Compositor: Fix incorrect copying of uchar buffers
Row stride and the area x coordinate offset were not taken into
account.
2021-08-23 17:09:59 +02:00
153b45037f Compositor: Full frame matte nodes
Adds full frame implementation to Channel Key, Chroma Key, Color Key,
Color Spill, Cryptomatte, Difference Key, Distance Key, Keying,
Keying Screen and Luminance Key nodes. The other nodes
in "Matte" sub-menu are submitted separately.

No functional changes.

Part of T88150.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D12220
2021-08-23 17:09:59 +02:00
daa7c59e38 Compositor: Full frame Bokeh Blur and Blur nodes
Adds full frame implementation to these nodes operations.

When enabling "extend bounds" node option, tiled implementation
result is slightly different because it's using `TranslateOperation`
with bilinear sampling for centering.
Full frame always uses nearest to don't lose image quality.
It has the disadvantage of causing image jiggling on backdrop
when switching size values as it's not pixel perfect.
This is fixed by rounding to even.

No functional changes.

Part of T88150.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D12167
2021-08-23 17:08:45 +02:00
344aca3b1b Compositor: Full frame distort nodes
Adds full frame implementation to "Displace", "Crop", "Flip",
"Plane Track Deform", "Corner Pin", "Movie Distortion",
"Lens Distortion" and "Map UV" nodes.

The other nodes in "Distort" sub-menu are implemented
separately in other commits.

No functional changes.

Part of T88150.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D12166
2021-08-23 17:07:37 +02:00
064167fce7 Compositor: Full frame transform nodes
Adds full frame implementation to "Rotate", "Transform" and
"Stabilize2D" nodes.
To avoid sampling twice when concatenating scale and rotate
operations, a `TransformOperation` is implemented with all
the functionality.
The nodes have no functional changes.

Part of T88150.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D12165
2021-08-23 16:36:09 +02:00
a95e56b741 Compositor: Add sampling methods for full frame
Current sampling methods have off by one issues on full frame:
- Bilinear sampling do not fully sample bottom and left image border,
 creating edges.
- Single elem buffers are not sampled at all when they should be
 at least on the borders to smooth edges.
- EWA filtering is partially implemented on `ReadBufferOperation`, it
 needs to be moved to `MemoryBuffer` on full frame.

In order to not affect tiled implementation, this commit creates
specific sampling methods for full frame needs.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D12164
2021-08-23 16:36:09 +02:00
8f4730e66f Compositor: Full frame convert nodes
Adds full frame implementation to all nodes in "Converter" sub-menu
except "ID Mask" which is implemented separately.
No functional changes.

Part of T88150.

Reviewed By: jbakker

Differential Revision: https://developer.blender.org/D12095
2021-08-23 16:36:08 +02:00
James Partsafas
21d4a888b8 Fix T88107: rename Convertor to Converter nodes to correct spelling
Differential Revision: https://developer.blender.org/D11198
2021-08-23 16:27:33 +02:00
Mikhail Matrosov
cd118c5581 Fix T90423: black pixels after shadow terminator geometry offset
Solves an error in the principled diffuse BSDF, where it was not correctly
rejecting directions outside the hemisphere.

Differential Revision: https://developer.blender.org/D12283
2021-08-23 15:47:34 +02:00
27f138f4c8 Cleanup: rename parameter in transform utility
`inv_unit_scale` is not descriptive.
2021-08-23 09:49:01 -03:00
2f0e350ffd Fix T90872: Dopesheet messes up keyframe handles
Y coordinate was not being constrained.

Caused by {rBb0d9e6797fb866e7a58876c7977c98a190070310}
2021-08-23 09:49:01 -03:00
b4b3f518aa GPencil: Fix memory leak in split & trim functions
Authored by Henrik Dick (weasel)

Reviewed By YimingWu (NicksBest), Antonio Vazquez (antoniov)

Differential Revision: https://developer.blender.org/D12284
2021-08-23 20:46:38 +08:00
5aa3167e48 Fix T90772: Image Editor not sampling color from the the currently
selected pass

Caused by {rBebaa3fcedd23}.

Seems this above commit assumed an ImageUser's multi_index is only used
for Multiview/Stereo? This is not the case, multi_index also stores the
index for layer/pass combination.

If we call both BKE_image_multilayer_index and BKE_image_multiview_index
(even though this is not appropriate/needed for multilayer images?), we
might end up overwriting multi_index again.

note: looking at this I was also wondering why we update the ImageUser
in image-buffer-aquiring funnctions [and not from the UI, e.g.
template_image_layers, but that is a whole different story I guess, see
comment in T90772 as well]

note2: this could also use a utility function (this is not the only
place where this is done), this is fo a cleanup commit.

Maniphest Tasks: T90772

Differential Revision: https://developer.blender.org/D12267
2021-08-23 13:54:52 +02:00
8165333de9 Pipeline: Use more explicit cuda versions. 2021-08-23 13:47:40 +02:00
9564b6cf23 Fix T90651: camera reconstruction crash without scene camera
This was working differently in 2.79, tried tracking this down and it
seems this was wrong since the 2.8 beginning in {rB7907dfc40018}.

This would not only crash without an active scene camera, but would also
result in different tracks from different camera's constraints could not
be selected.

So select id depends on corresponding camera, remove the dependency on
scene camera completely.

Maniphest Tasks: T90651

Differential Revision: https://developer.blender.org/D12230
2021-08-23 12:52:23 +02:00
0682af0d63 RNA: add length augmented to RNA_string_get_alloc
This was noted as a TODO as it wraps RNA_property_string_get_alloc
which takes a length return argument.
2021-08-23 15:08:48 +10:00
62f2204d65 Cleanup: rename len to str_len for BLF functions
Make it obvious which variable this is the length of.
2021-08-23 15:08:28 +10:00
aa067bef5e Cleanup: use BLI_str_utf8 prefix
Rename:

- BLI_str_utf8_invalid_byte  (was BLI_utf8_invalid_byte)
- BLI_str_utf8_invalid_strip (was BLI_utf8_invalid_strip)
2021-08-23 15:02:13 +10:00
0de3d4e8c7 Fix T90847: snap to face of Add Primitive tool not working in edit mode
BVHTree was being created but not balanced.
Error introduced in {rBfcc844f8fbd0}.
2021-08-22 23:48:54 -03:00
b477333473 Cleanup: fix comment about compiler support.
Differential Revision: https://developer.blender.org/D12288
2021-08-22 22:15:28 +05:30
a1e91fbef3 BLF: Remove space_userpref.py font_kerning_style
Remove `font_kerning_style` from `space_userpref.py` since this is no
longer valid.

See more details in D12276

Differential Revision: https://developer.blender.org/D12276

Reviewed by Campbell Barton
2021-08-22 09:26:06 -07:00
ea3ee04fa8 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-20 12:42:11 +02:00
4838436387 fix extrude node 2021-08-20 12:42:07 +02:00
62d485e470 initial evaluate curve node 2021-08-18 14:53:34 +02:00
cd6eb65482 cleanup 2021-08-18 14:44:16 +02:00
d337c20cf9 fix use after free in socket inspection
For now I just evaluate constant fields immediately. The issue was that
parts of the fields was freed already when socket inspection runs.
2021-08-18 13:31:41 +02:00
a715aec6a3 initial sample mesh surface
This is a field based alternative for the attribute transfer node.
The main goal here is to experiment with how attribute transfer
should be done with fields.
2021-08-18 12:53:23 +02:00
eba32b5f4a cleanup 2021-08-18 10:29:40 +02:00
1d859c9183 fix after merge 2021-08-18 10:20:59 +02:00
a3d02965da fix after merge 2021-08-18 10:17:28 +02:00
45e58a7e92 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-18 10:17:15 +02:00
c920b83b84 Fix curve parameter node with multiple splines 2021-08-10 12:09:19 -05:00
34e0d8079f Add 1D noise to noise texture node 2021-08-10 11:58:11 -05:00
a376073f1e Fix curve length node (output a field, otherwise it crashes) 2021-08-10 11:57:42 -05:00
29ac510198 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-10 10:56:57 -05:00
d7cf8babf2 Merge branch 'temp-geometry-nodes-fields-prototype' of git.blender.org:blender into temp-geometry-nodes-fields-prototype 2021-08-10 10:56:23 -05:00
3becd4cb61 Fix curve parameter field with cyclic poly splines 2021-08-10 10:56:17 -05:00
52bb0d42b3 correct socket shape 2021-08-10 17:23:15 +02:00
acae8f430d simplify switching between value and attribute in modifier 2021-08-10 16:55:41 +02:00
bbb692ffb5 add Set Position node 2021-08-10 16:10:06 +02:00
df6a819982 rename to Attribute Freeze 2021-08-10 15:53:41 +02:00
48f2643556 remove dead code 2021-08-10 15:42:15 +02:00
d4b441851f new Position node 2021-08-10 15:33:19 +02:00
e0406d029d Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-10 15:11:56 +02:00
c5c82f801a Merge remote-tracking branch 'origin/temp-geometry-nodes-fields-prototype' into temp-geometry-nodes-fields-prototype 2021-08-10 10:45:30 +02:00
78c0e7c015 Fix: Extrude and Move Edge out selection 2021-08-10 10:45:23 +02:00
fd0ac1aec5 fix node id 2021-08-10 10:08:06 +02:00
0827cce9de Merge remote-tracking branch 'origin/temp-geometry-nodes-fields-prototype' into temp-geometry-nodes-fields-prototype
# Conflicts:
#	source/blender/blenkernel/BKE_node.h
2021-08-10 09:34:04 +02:00
2bba77045f Extrude and Move Node
This adds an additional version of the extrude node, which operates on
vertices, edges and faces and moves the extruded region by a given
offset vector.
2021-08-10 09:33:24 +02:00
00ecf29ec4 fix curve parameter type 2021-08-10 08:25:33 +02:00
172e713cc6 Add curve parameter node 2021-08-09 17:12:19 -05:00
5c32227025 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-09 15:35:06 -05:00
fa9d1cb2a9 properly adapt selection domain 2021-08-09 19:31:32 +02:00
577a12f840 add Normal node 2021-08-09 11:29:04 +02:00
8dad29d0ea new Attribute Extract node 2021-08-09 11:10:57 +02:00
4eda4fd49a support field in point separate node 2021-08-09 09:49:53 +02:00
1db53a8923 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-09 09:12:48 +02:00
20aba8a4c0 Merge remote-tracking branch 'origin/temp-geometry-nodes-fields-prototype' into temp-geometry-nodes-fields-prototype 2021-08-08 04:59:25 +02:00
1d53bf0412 Cleanup: Remove unused variable 2021-08-06 19:00:58 -05:00
1177debb01 Support selection in the point translate node 2021-08-06 17:56:41 -05:00
047daf5442 Don't draw special "constant" sockets in more cases 2021-08-06 17:33:58 -05:00
f2112199a4 Add utilities to enable and disable output sockets with a type 2021-08-06 17:08:22 -05:00
36de290807 Fix incorrect SOCK_FIELD cases 2021-08-06 16:58:24 -05:00
c09a535333 Cleanup: Rename flag
This tends to fit on one line more often
2021-08-06 16:24:22 -05:00
4e78bb3f0c Draw constant input sockets that don't support fields differently
This is a really incomplete way to do this (it doesn't propogate to
linked nodes, and I haven't though about outputs), but it still provides
a very nice benefit to readability.
2021-08-06 16:23:34 -05:00
49f33e9820 Merge remote-tracking branch 'origin/temp-geometry-nodes-fields-prototype' into temp-geometry-nodes-fields-prototype
# Conflicts:
#	source/blender/blenkernel/BKE_node.h
#	source/blender/bmesh/intern/bmesh_mesh.h
#	source/blender/nodes/geometry/nodes/node_geo_extrude.cc
2021-08-06 22:36:31 +02:00
6f525e0d98 Upgrade extrude node with field inputs and selection outputs 2021-08-06 14:55:32 -05:00
963448a2af Cleanup: Remove unused code 2021-08-06 14:53:35 -05:00
9506ef320e Merge branch 'soc-2021-porting-modifiers-to-nodes-extrude' into temp-geometry-nodes-fields-prototype
# Conflicts:
#	source/blender/blenkernel/BKE_node.h
#	source/blender/blenkernel/intern/node.cc
#	source/blender/bmesh/intern/bmesh_mesh.c
#	source/blender/bmesh/intern/bmesh_mesh.h
#	source/blender/nodes/CMakeLists.txt
#	source/blender/nodes/NOD_geometry.h
#	source/blender/nodes/NOD_static_types.h
#	source/blender/nodes/geometry/nodes/node_geo_extrude.cc
2021-08-06 20:25:44 +02:00
1700fde0dc Output anonymous attribute in select by handle node 2021-08-06 12:45:21 -05:00
d5a83a5a32 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-06 12:33:00 -05:00
a7dc3d1e90 Merge remote-tracking branch 'origin/soc-2021-porting-modifiers-to-nodes-extrude' into soc-2021-porting-modifiers-to-nodes-extrude 2021-08-06 07:53:56 +02:00
6fd836f53b Merged Master 2021-08-06 07:48:40 +02:00
40455c21fa Support switching between two fields in the switch node 2021-08-05 18:10:28 -05:00
2d0442e943 Use "true" defaults for selection fields 2021-08-05 17:50:33 -05:00
d55046b167 Support field for curve set spline type selection 2021-08-05 17:50:23 -05:00
4233128fa5 Add a node to store a field in a geometry and output an anonymous attribute 2021-08-05 17:43:15 -05:00
798496233a Rename the "Attribute Fill" node to "Store Persistent Attribute" 2021-08-05 17:42:57 -05:00
991f6b15f3 Merge branch 'master' into soc-2021-porting-modifiers-to-nodes-extrude
# Conflicts:
#	source/blender/blenkernel/BKE_node.h
2021-08-05 23:50:08 +02:00
aef45a4ef2 Merge remote-tracking branch 'origin/soc-2021-porting-modifiers-to-nodes-extrude' into soc-2021-porting-modifiers-to-nodes-extrude
# Conflicts:
#	source/blender/bmesh/intern/bmesh_mesh.c
#	source/blender/bmesh/intern/bmesh_mesh.h
#	source/blender/nodes/NOD_static_types.h
#	source/blender/nodes/geometry/nodes/node_geo_extrude.cc
2021-08-05 23:48:58 +02:00
504e3c563f added side selection. 2021-08-05 23:47:44 +02:00
8645ea16de Remove attribute nodes that have been made redundant 2021-08-05 14:48:18 -05:00
5e292d923f Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-05 14:13:16 -05:00
1ab1d6665c Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-04 18:13:45 -05:00
b1c98ad8ee Fix compile warning 2021-08-03 16:51:01 -04:00
e9d95fddf9 Add tweaked mesh extrude node from Fabian 2021-08-03 16:30:48 -04:00
782d240a1b Fix crash in the Is Viewport node 2021-08-03 16:12:18 -04:00
36055ba366 Remove density max socket from the point distribute node 2021-08-03 16:05:10 -04:00
236576fc91 Support anonymous attributes in the proximity node 2021-08-03 15:49:20 -04:00
e7d57e84bb Move "Align Rotation to Vector" to a function node without geometry
The node is still slightly finnicky, I may have done something wrong here.
But it generally works.
2021-08-03 15:07:55 -04:00
b5573bfbf4 Merge branch 'master' into soc-2021-porting-modifiers-to-nodes-extrude 2021-08-03 21:02:56 +02:00
3b2522650b Don't show face corner domain in the geometry delete node 2021-08-03 12:56:26 -04:00
eacc1f1ff4 Fix raycast and geometry delete node for multi-spline curves 2021-08-03 12:51:13 -04:00
9562a90632 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-03 09:01:23 -04:00
2ceeaf95a1 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-03 10:44:41 +02:00
c8de9dc2a4 Cleanup: Unused variable warning 2021-08-03 00:28:03 -04:00
f8f2873b44 Support fields in the curve set handles node 2021-08-03 00:11:20 -04:00
648fa6d89e Support fields in the curve subdivide node 2021-08-02 23:58:33 -04:00
d24419564e Fix unhandled switch cases 2021-08-02 23:58:19 -04:00
184be40aa1 Support field selection in the mesh to curve node 2021-08-02 23:49:33 -04:00
6131506689 Support anonymous attributse in the select by material node 2021-08-02 23:42:01 -04:00
e5f43cf4b7 Support fields in the material assign node 2021-08-02 23:33:36 -04:00
770d70127f Support fields and anonymous attributes in the raycast node 2021-08-02 23:29:32 -04:00
801034ba6a Support field selection in the geometry delete node 2021-08-02 22:42:34 -04:00
17442bcb7b Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-02 21:54:16 -04:00
ba9561ab0d Geometry Nodes: Extrude
Differential Revision: https://developer.blender.org/D12108
2021-08-02 19:04:04 +02:00
c659af0c13 Geometry Nodes: Extrude 2021-08-02 19:02:38 +02:00
dd8be48466 cleanup after merge 2021-08-02 13:07:31 +02:00
5914b8ed6b Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-02 12:56:18 +02:00
50da3cfbd6 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-02 10:39:37 +02:00
131e8c0d59 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-08-02 10:00:49 +02:00
aedd43b33f support field in Points to Volume node 2021-07-30 17:37:07 +02:00
adb437ad3f support socket inspection for fields 2021-07-30 17:16:00 +02:00
c106ed8fcd fix 2021-07-30 15:22:03 +02:00
11a6a56982 support fields in point translate node 2021-07-30 15:10:41 +02:00
0a7ab583a5 fix noise node 2021-07-30 15:04:14 +02:00
09009ce062 automatically change type in attribute node when selecting attribute 2021-07-30 13:56:40 +02:00
dc502fad43 support passing attribute field from modifier 2021-07-30 13:42:07 +02:00
3abce9e633 support modifier inputs again 2021-07-30 12:30:32 +02:00
dad5c3991c cleanup 2021-07-30 12:23:19 +02:00
dab3a07659 cleanup 2021-07-30 12:02:05 +02:00
53a724c804 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-30 11:39:59 +02:00
bacde38b67 refactor point distribute node 2021-07-29 21:41:01 +02:00
d9b128baf2 don't show anonymous attributes in attribute search 2021-07-29 21:40:50 +02:00
ae6c063b33 don't show anonymous attributes in spreadsheet 2021-07-29 21:03:52 +02:00
67e7aee853 fixes 2021-07-29 20:56:59 +02:00
302ab3ff7c cleanup 2021-07-29 19:56:54 +02:00
4352d1beb5 support anonymous attribute field input 2021-07-29 19:38:39 +02:00
32cb953b9b anonymous attribute fields 2021-07-29 19:28:58 +02:00
1d92a4d1a0 initial anonymous attributes implementation 2021-07-29 19:16:09 +02:00
2472b0f0ef Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-29 10:21:22 +02:00
626be25646 support attribute search in Attribute node 2021-07-28 15:41:00 +02:00
8132383662 new index node and fixes 2021-07-28 15:12:53 +02:00
f95214e9ae support implicit conversion of fields 2021-07-28 14:04:14 +02:00
dde42e19e3 support cpptype inheritance for templates 2021-07-28 13:37:29 +02:00
f90a83f816 support field in Point Instance node 2021-07-28 12:30:56 +02:00
433b4ae22a better field support in attribute fill node 2021-07-28 12:05:53 +02:00
23e217eafc support position input in noise node 2021-07-28 11:49:30 +02:00
87153cf019 progress 2021-07-28 11:33:00 +02:00
08122a7794 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-28 10:54:16 +02:00
58af171bc0 initial support for noise node 2021-07-27 17:15:13 +02:00
c98a535f5f fix 2021-07-27 17:06:57 +02:00
8262e6a0c1 progress 2021-07-27 17:00:37 +02:00
c00cc9fd60 improve 2021-07-27 16:29:34 +02:00
0a31b1f044 initial attribute field 2021-07-27 16:01:37 +02:00
5e3b33dfe8 Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-27 15:49:46 +02:00
f716060408 improve 2021-07-27 14:33:35 +02:00
aa092f4e6f Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-27 13:54:17 +02:00
ad8dfb3823 cleanup 2021-07-26 18:18:11 +02:00
bb0e0fde32 cleanup 2021-07-26 17:17:23 +02:00
3472f83f00 cleanup 2021-07-26 17:07:45 +02:00
1b2b07fb18 progress 2021-07-26 16:34:28 +02:00
64a11ba6a2 progress 2021-07-26 15:21:15 +02:00
26d8d2884c Merge branch 'master' into temp-geometry-nodes-fields-prototype 2021-07-26 11:44:31 +02:00
12014ccfbc progress 2021-07-10 12:14:48 +02:00
34cebfbb78 progress 2021-07-10 11:22:48 +02:00
2cc4e1572a progress 2021-07-09 18:32:57 +02:00
ceb9e7d71e initial commit 2021-07-09 17:48:51 +02:00
305 changed files with 11004 additions and 5416 deletions

View File

@@ -1598,8 +1598,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_UNUSED_PARAMETER -Wunused-parameter)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_ALL -Wall)
# Designated initializer is a C++20 feature & breaks MSVC build. Dropping MSVC 2019 or
# updating to C++20 allows removing this.
# Using C++20 features while having C++17 as the project language isn't allowed by MSVC.
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_CXX20_DESIGNATOR -Wc++20-designator)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_AUTOLOGICAL_COMPARE -Wno-tautological-compare)

View File

@@ -51,9 +51,9 @@ buildbot:
gcc:
version: '9.0.0'
cuda10:
version: '10.1.0'
version: '10.1.243'
cuda11:
version: '11.4.0'
version: '11.4.1'
optix:
version: '7.1.0'
cmake:

View File

@@ -424,7 +424,7 @@ static inline void set_enum(PointerRNA &ptr, const char *name, const string &ide
static inline string get_string(PointerRNA &ptr, const char *name)
{
char cstrbuf[1024];
char *cstr = RNA_string_get_alloc(&ptr, name, cstrbuf, sizeof(cstrbuf));
char *cstr = RNA_string_get_alloc(&ptr, name, cstrbuf, sizeof(cstrbuf), NULL);
string str(cstr);
if (cstr != cstrbuf)
MEM_freeN(cstr);

View File

@@ -36,10 +36,10 @@ static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledDiffuseBsdf),
ccl_device float3 calculate_principled_diffuse_brdf(
const PrincipledDiffuseBsdf *bsdf, float3 N, float3 V, float3 L, float3 H, float *pdf)
{
float NdotL = max(dot(N, L), 0.0f);
float NdotV = max(dot(N, V), 0.0f);
float NdotL = dot(N, L);
float NdotV = dot(N, V);
if (NdotL < 0 || NdotV < 0) {
if (NdotL <= 0 || NdotV <= 0) {
*pdf = 0.0f;
return make_float3(0.0f, 0.0f, 0.0f);
}

View File

@@ -997,7 +997,6 @@ class USERPREF_PT_theme_text_style(ThemePanel, CenterAlignMixIn, Panel):
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
col = flow.column()
col.row().prop(font_style, "font_kerning_style", expand=True)
col.prop(font_style, "points")
col = flow.column(align=True)

View File

@@ -475,24 +475,15 @@ texture_node_categories = [
geometry_node_categories = [
# Geometry Nodes
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
NodeItem("GeometryNodeAttributeRandomize"),
NodeItem("GeometryNodeAttributeMath"),
NodeItem("GeometryNodeAttributeClamp"),
NodeItem("GeometryNodeAttributeCompare"),
NodeItem("GeometryNodeAttributeConvert"),
NodeItem("GeometryNodeAttributeCurveMap"),
NodeItem("GeometryNodeAttributeFill"),
NodeItem("GeometryNodeAttributeMix"),
NodeItem("GeometryNodeAttributeProximity"),
NodeItem("GeometryNodeAttributeColorRamp"),
NodeItem("GeometryNodeAttributeVectorMath"),
NodeItem("GeometryNodeAttributeVectorRotate"),
NodeItem("GeometryNodeAttributeSampleTexture"),
NodeItem("GeometryNodeAttributeCombineXYZ"),
NodeItem("GeometryNodeAttributeSeparateXYZ"),
NodeItem("GeometryNodeAttributeRemove"),
NodeItem("GeometryNodeAttributeMapRange"),
NodeItem("GeometryNodeAttributeTransfer"),
NodeItem("GeometryNodeAttributeExtract"),
NodeItem("GeometryNodeAttributeFreeze"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeMixRGB"),
@@ -514,6 +505,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeCurveSplineType"),
NodeItem("GeometryNodeCurveSetHandles"),
NodeItem("GeometryNodeCurveSelectHandles"),
NodeItem("GeometryNodeEvaluateCurve"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[
NodeItem("GeometryNodeCurvePrimitiveLine"),
@@ -532,6 +524,8 @@ geometry_node_categories = [
NodeItem("GeometryNodeJoinGeometry"),
NodeItem("GeometryNodeSeparateComponents"),
NodeItem("GeometryNodeRaycast"),
NodeItem("GeometryNodeAttributeProximity"),
NodeItem("GeometryNodeSetPosition"),
]),
GeometryNodeCategory("GEO_INPUT", "Input", items=[
NodeItem("GeometryNodeObjectInfo"),
@@ -542,6 +536,11 @@ geometry_node_categories = [
NodeItem("FunctionNodeInputVector"),
NodeItem("GeometryNodeInputMaterial"),
NodeItem("GeometryNodeIsViewport"),
NodeItem("GeometryNodeAttribute"),
NodeItem("GeometryNodeIndex"),
NodeItem("GeometryNodeNormal"),
NodeItem("GeometryNodePosition"),
NodeItem("GeometryNodeCurveParameter"),
]),
GeometryNodeCategory("GEO_MATERIAL", "Material", items=[
NodeItem("GeometryNodeMaterialAssign"),
@@ -554,6 +553,9 @@ geometry_node_categories = [
NodeItem("GeometryNodeEdgeSplit"),
NodeItem("GeometryNodeSubdivisionSurface"),
NodeItem("GeometryNodeMeshSubdivide"),
NodeItem("GeometryNodeExtrude"),
NodeItem("GeometryNodeExtrudeAndMove"),
NodeItem("GeometryNodeSampleMeshSurface"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[
NodeItem("GeometryNodeMeshCircle"),
@@ -565,7 +567,6 @@ geometry_node_categories = [
NodeItem("GeometryNodeMeshLine"),
NodeItem("GeometryNodeMeshUVSphere"),
]),
GeometryNodeCategory("GEO_POINT", "Point", items=[
NodeItem("GeometryNodePointDistribute"),
NodeItem("GeometryNodePointInstance"),
@@ -573,7 +574,6 @@ geometry_node_categories = [
NodeItem("GeometryNodePointScale"),
NodeItem("GeometryNodePointTranslate"),
NodeItem("GeometryNodeRotatePoints"),
NodeItem("GeometryNodeAlignRotationToVector"),
]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("ShaderNodeMapRange"),
@@ -583,6 +583,7 @@ geometry_node_categories = [
NodeItem("FunctionNodeFloatCompare"),
NodeItem("FunctionNodeFloatToInt"),
NodeItem("GeometryNodeSwitch"),
NodeItem("ShaderNodeTexNoise"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
NodeItem("ShaderNodeVectorCurve"),
@@ -590,6 +591,7 @@ geometry_node_categories = [
NodeItem("ShaderNodeCombineXYZ"),
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeVectorRotate"),
NodeItem("FunctionNodeAlignRotationToVector"),
]),
GeometryNodeCategory("GEO_OUTPUT", "Output", items=[
NodeItem("GeometryNodeViewer"),

View File

@@ -98,13 +98,13 @@ void BLF_batch_draw_flush(void);
void BLF_batch_draw_end(void);
/* Draw the string using the current font. */
void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
void BLF_draw_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
ATTR_NONNULL(2);
void BLF_draw(int fontid, const char *str, size_t len) ATTR_NONNULL(2);
void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
void BLF_draw(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2);
void BLF_draw_ascii_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
ATTR_NONNULL(2);
void BLF_draw_ascii(int fontid, const char *str, size_t len) ATTR_NONNULL(2);
int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth) ATTR_NONNULL(2);
void BLF_draw_ascii(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2);
int BLF_draw_mono(int fontid, const char *str, size_t str_len, int cwidth) ATTR_NONNULL(2);
typedef bool (*BLF_GlyphBoundsFn)(const char *str,
const size_t str_step_ofs,
@@ -116,43 +116,45 @@ typedef bool (*BLF_GlyphBoundsFn)(const char *str,
void BLF_boundbox_foreach_glyph_ex(int fontid,
const char *str,
size_t len,
size_t str_len,
BLF_GlyphBoundsFn user_fn,
void *user_data,
struct ResultBLF *r_info) ATTR_NONNULL(2);
void BLF_boundbox_foreach_glyph(int fontid,
const char *str,
size_t len,
size_t str_len,
BLF_GlyphBoundsFn user_fn,
void *user_data) ATTR_NONNULL(2);
/* Get the string byte offset that fits within a given width */
size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, float *r_width)
ATTR_NONNULL(2);
size_t BLF_width_to_strlen(
int fontid, const char *str, size_t str_len, float width, float *r_width) ATTR_NONNULL(2);
/* Same as BLF_width_to_strlen but search from the string end */
size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width, float *r_width)
ATTR_NONNULL(2);
size_t BLF_width_to_rstrlen(
int fontid, const char *str, size_t str_len, float width, float *r_width) ATTR_NONNULL(2);
/* This function return the bounding box of the string
* and are not multiplied by the aspect.
*/
void BLF_boundbox_ex(int fontid,
const char *str,
size_t len,
size_t str_len,
struct rctf *box,
struct ResultBLF *r_info) ATTR_NONNULL(2);
void BLF_boundbox(int fontid, const char *str, size_t len, struct rctf *box) ATTR_NONNULL();
void BLF_boundbox(int fontid, const char *str, size_t str_len, struct rctf *box) ATTR_NONNULL();
/* The next both function return the width and height
* of the string, using the current font and both value
* are multiplied by the aspect of the font.
*/
float BLF_width_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
float BLF_width_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2);
float BLF_width(int fontid, const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
float BLF_height_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
float BLF_width(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
float BLF_height_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2);
float BLF_height(int fontid, const char *str, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
float BLF_height(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
/* Return dimensions of the font without any sample text. */
int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT;
@@ -163,8 +165,8 @@ float BLF_ascender(int fontid) ATTR_WARN_UNUSED_RESULT;
/* The following function return the width and height of the string, but
* just in one call, so avoid extra freetype2 stuff.
*/
void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_width, float *r_height)
ATTR_NONNULL();
void BLF_width_and_height(
int fontid, const char *str, size_t str_len, float *r_width, float *r_height) ATTR_NONNULL();
/* For fixed width fonts only, returns the width of a
* character.
@@ -221,9 +223,9 @@ void BLF_buffer_col(int fontid, const float rgba[4]) ATTR_NONNULL(2);
/* Draw the string into the buffer, this function draw in both buffer,
* float and unsigned char _BUT_ it's not necessary set both buffer, NULL is valid here.
*/
void BLF_draw_buffer_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
void BLF_draw_buffer_ex(int fontid, const char *str, size_t str_len, struct ResultBLF *r_info)
ATTR_NONNULL(2);
void BLF_draw_buffer(int fontid, const char *str, size_t len) ATTR_NONNULL(2);
void BLF_draw_buffer(int fontid, const char *str, size_t str_len) ATTR_NONNULL(2);
/* Add a path to the font dir paths. */
void BLF_dir_add(const char *path) ATTR_NONNULL();
@@ -254,8 +256,9 @@ void BLF_default_dpi(int dpi);
void BLF_default_set(int fontid);
int BLF_default(void); /* get default font ID so we can pass it to other functions */
/* Draw the string using the default font, size and dpi. */
void BLF_draw_default(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL();
void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len) ATTR_NONNULL();
void BLF_draw_default(float x, float y, float z, const char *str, size_t str_len) ATTR_NONNULL();
void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t str_len)
ATTR_NONNULL();
/* Set size and DPI, and return default font ID. */
int BLF_set_default(void);

View File

@@ -521,7 +521,7 @@ static void blf_draw_gl__end(FontBLF *font)
}
}
void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
void BLF_draw_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
@@ -530,27 +530,27 @@ void BLF_draw_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_in
if (font) {
blf_draw_gl__start(font);
if (font->flags & BLF_WORD_WRAP) {
blf_font_draw__wrap(font, str, len, r_info);
blf_font_draw__wrap(font, str, str_len, r_info);
}
else {
blf_font_draw(font, str, len, r_info);
blf_font_draw(font, str, str_len, r_info);
}
blf_draw_gl__end(font);
}
}
void BLF_draw(int fontid, const char *str, size_t len)
void BLF_draw(int fontid, const char *str, const size_t str_len)
{
if (len == 0 || str[0] == '\0') {
if (str_len == 0 || str[0] == '\0') {
return;
}
/* Avoid bgl usage to corrupt BLF drawing. */
GPU_bgl_end();
BLF_draw_ex(fontid, str, len, NULL);
BLF_draw_ex(fontid, str, str_len, NULL);
}
void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
void BLF_draw_ascii_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
@@ -560,27 +560,27 @@ void BLF_draw_ascii_ex(int fontid, const char *str, size_t len, struct ResultBLF
blf_draw_gl__start(font);
if (font->flags & BLF_WORD_WRAP) {
/* Use non-ASCII draw function for word-wrap. */
blf_font_draw__wrap(font, str, len, r_info);
blf_font_draw__wrap(font, str, str_len, r_info);
}
else {
blf_font_draw_ascii(font, str, len, r_info);
blf_font_draw_ascii(font, str, str_len, r_info);
}
blf_draw_gl__end(font);
}
}
void BLF_draw_ascii(int fontid, const char *str, size_t len)
void BLF_draw_ascii(int fontid, const char *str, const size_t str_len)
{
if (len == 0 || str[0] == '\0') {
if (str_len == 0 || str[0] == '\0') {
return;
}
BLF_draw_ascii_ex(fontid, str, len, NULL);
BLF_draw_ascii_ex(fontid, str, str_len, NULL);
}
int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
int BLF_draw_mono(int fontid, const char *str, const size_t str_len, int cwidth)
{
if (len == 0 || str[0] == '\0') {
if (str_len == 0 || str[0] == '\0') {
return 0;
}
@@ -589,7 +589,7 @@ int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
if (font) {
blf_draw_gl__start(font);
columns = blf_font_draw_mono(font, str, len, cwidth);
columns = blf_font_draw_mono(font, str, str_len, cwidth);
blf_draw_gl__end(font);
}
@@ -606,7 +606,7 @@ int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
*/
void BLF_boundbox_foreach_glyph_ex(int fontid,
const char *str,
size_t len,
size_t str_len,
BLF_GlyphBoundsFn user_fn,
void *user_data,
struct ResultBLF *r_info)
@@ -621,25 +621,26 @@ void BLF_boundbox_foreach_glyph_ex(int fontid,
BLI_assert(0);
}
else {
blf_font_boundbox_foreach_glyph(font, str, len, user_fn, user_data, r_info);
blf_font_boundbox_foreach_glyph(font, str, str_len, user_fn, user_data, r_info);
}
}
}
void BLF_boundbox_foreach_glyph(
int fontid, const char *str, size_t len, BLF_GlyphBoundsFn user_fn, void *user_data)
int fontid, const char *str, const size_t str_len, BLF_GlyphBoundsFn user_fn, void *user_data)
{
BLF_boundbox_foreach_glyph_ex(fontid, str, len, user_fn, user_data, NULL);
BLF_boundbox_foreach_glyph_ex(fontid, str, str_len, user_fn, user_data, NULL);
}
size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width, float *r_width)
size_t BLF_width_to_strlen(
int fontid, const char *str, const size_t str_len, float width, float *r_width)
{
FontBLF *font = blf_get(fontid);
if (font) {
const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f;
size_t ret;
ret = blf_font_width_to_strlen(font, str, len, width / xa, r_width);
ret = blf_font_width_to_strlen(font, str, str_len, width / xa, r_width);
if (r_width) {
*r_width *= xa;
}
@@ -652,14 +653,15 @@ size_t BLF_width_to_strlen(int fontid, const char *str, size_t len, float width,
return 0;
}
size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width, float *r_width)
size_t BLF_width_to_rstrlen(
int fontid, const char *str, const size_t str_len, float width, float *r_width)
{
FontBLF *font = blf_get(fontid);
if (font) {
const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f;
size_t ret;
ret = blf_font_width_to_rstrlen(font, str, len, width / xa, r_width);
ret = blf_font_width_to_rstrlen(font, str, str_len, width / xa, r_width);
if (r_width) {
*r_width *= xa;
}
@@ -673,7 +675,7 @@ size_t BLF_width_to_rstrlen(int fontid, const char *str, size_t len, float width
}
void BLF_boundbox_ex(
int fontid, const char *str, size_t len, rctf *r_box, struct ResultBLF *r_info)
int fontid, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
@@ -681,47 +683,48 @@ void BLF_boundbox_ex(
if (font) {
if (font->flags & BLF_WORD_WRAP) {
blf_font_boundbox__wrap(font, str, len, r_box, r_info);
blf_font_boundbox__wrap(font, str, str_len, r_box, r_info);
}
else {
blf_font_boundbox(font, str, len, r_box, r_info);
blf_font_boundbox(font, str, str_len, r_box, r_info);
}
}
}
void BLF_boundbox(int fontid, const char *str, size_t len, rctf *r_box)
void BLF_boundbox(int fontid, const char *str, const size_t str_len, rctf *r_box)
{
BLF_boundbox_ex(fontid, str, len, r_box, NULL);
BLF_boundbox_ex(fontid, str, str_len, r_box, NULL);
}
void BLF_width_and_height(int fontid, const char *str, size_t len, float *r_width, float *r_height)
void BLF_width_and_height(
int fontid, const char *str, const size_t str_len, float *r_width, float *r_height)
{
FontBLF *font = blf_get(fontid);
if (font) {
blf_font_width_and_height(font, str, len, r_width, r_height, NULL);
blf_font_width_and_height(font, str, str_len, r_width, r_height, NULL);
}
else {
*r_width = *r_height = 0.0f;
}
}
float BLF_width_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
float BLF_width_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
BLF_RESULT_CHECK_INIT(r_info);
if (font) {
return blf_font_width(font, str, len, r_info);
return blf_font_width(font, str, str_len, r_info);
}
return 0.0f;
}
float BLF_width(int fontid, const char *str, size_t len)
float BLF_width(int fontid, const char *str, const size_t str_len)
{
return BLF_width_ex(fontid, str, len, NULL);
return BLF_width_ex(fontid, str, str_len, NULL);
}
float BLF_fixed_width(int fontid)
@@ -735,22 +738,22 @@ float BLF_fixed_width(int fontid)
return 0.0f;
}
float BLF_height_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
float BLF_height_ex(int fontid, const char *str, const size_t str_len, struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
BLF_RESULT_CHECK_INIT(r_info);
if (font) {
return blf_font_height(font, str, len, r_info);
return blf_font_height(font, str, str_len, r_info);
}
return 0.0f;
}
float BLF_height(int fontid, const char *str, size_t len)
float BLF_height(int fontid, const char *str, const size_t str_len)
{
return BLF_height_ex(fontid, str, len, NULL);
return BLF_height_ex(fontid, str, str_len, NULL);
}
int BLF_height_max(int fontid)
@@ -894,24 +897,27 @@ void blf_draw_buffer__end(void)
{
}
void BLF_draw_buffer_ex(int fontid, const char *str, size_t len, struct ResultBLF *r_info)
void BLF_draw_buffer_ex(int fontid,
const char *str,
const size_t str_len,
struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
if (font && (font->buf_info.fbuf || font->buf_info.cbuf)) {
blf_draw_buffer__start(font);
if (font->flags & BLF_WORD_WRAP) {
blf_font_draw_buffer__wrap(font, str, len, r_info);
blf_font_draw_buffer__wrap(font, str, str_len, r_info);
}
else {
blf_font_draw_buffer(font, str, len, r_info);
blf_font_draw_buffer(font, str, str_len, r_info);
}
blf_draw_buffer__end();
}
}
void BLF_draw_buffer(int fontid, const char *str, size_t len)
void BLF_draw_buffer(int fontid, const char *str, const size_t str_len)
{
BLF_draw_buffer_ex(fontid, str, len, NULL);
BLF_draw_buffer_ex(fontid, str, str_len, NULL);
}
char *BLF_display_name_from_file(const char *filename)

View File

@@ -68,23 +68,23 @@ int BLF_set_default(void)
return global_font_default;
}
void BLF_draw_default(float x, float y, float z, const char *str, size_t len)
void BLF_draw_default(float x, float y, float z, const char *str, const size_t str_len)
{
ASSERT_DEFAULT_SET;
const uiStyle *style = UI_style_get();
BLF_size(global_font_default, style->widgetlabel.points, global_font_dpi);
BLF_position(global_font_default, x, y, z);
BLF_draw(global_font_default, str, len);
BLF_draw(global_font_default, str, str_len);
}
/* same as above but call 'BLF_draw_ascii' */
void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t len)
void BLF_draw_default_ascii(float x, float y, float z, const char *str, const size_t str_len)
{
ASSERT_DEFAULT_SET;
const uiStyle *style = UI_style_get();
BLF_size(global_font_default, style->widgetlabel.points, global_font_dpi);
BLF_position(global_font_default, x, y, z);
BLF_draw_ascii(global_font_default, str, len); /* XXX, use real length */
BLF_draw_ascii(global_font_default, str, str_len); /* XXX, use real length */
}

View File

@@ -365,7 +365,7 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font,
static void blf_font_draw_ex(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
size_t len,
const size_t str_len,
struct ResultBLF *r_info,
int pen_y)
{
@@ -374,14 +374,14 @@ static void blf_font_draw_ex(FontBLF *font,
int pen_x = 0;
size_t i = 0;
if (len == 0) {
if (str_len == 0) {
/* early output, don't do any IMM OpenGL. */
return;
}
blf_batch_draw_begin(font);
while ((i < len) && str[i]) {
while ((i < str_len) && str[i]) {
g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
@@ -407,16 +407,16 @@ static void blf_font_draw_ex(FontBLF *font,
r_info->width = pen_x;
}
}
void blf_font_draw(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
void blf_font_draw(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info)
{
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
blf_font_draw_ex(font, gc, str, len, r_info, 0);
blf_font_draw_ex(font, gc, str, str_len, r_info, 0);
blf_glyph_cache_release(font);
}
/* faster version of blf_font_draw, ascii only for view dimensions */
static void blf_font_draw_ascii_ex(
FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info, int pen_y)
FontBLF *font, const char *str, size_t str_len, struct ResultBLF *r_info, int pen_y)
{
unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev = NULL;
@@ -426,7 +426,7 @@ static void blf_font_draw_ascii_ex(
blf_batch_draw_begin(font);
while ((c = *(str++)) && len--) {
while ((c = *(str++)) && str_len--) {
BLI_assert(c < GLYPH_ASCII_TABLE_SIZE);
g = gc->glyph_ascii_table[c];
if (UNLIKELY(g == NULL)) {
@@ -456,13 +456,16 @@ static void blf_font_draw_ascii_ex(
blf_glyph_cache_release(font);
}
void blf_font_draw_ascii(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
void blf_font_draw_ascii(FontBLF *font,
const char *str,
const size_t str_len,
struct ResultBLF *r_info)
{
blf_font_draw_ascii_ex(font, str, len, r_info, 0);
blf_font_draw_ascii_ex(font, str, str_len, r_info, 0);
}
/* use fixed column width, but an utf8 character may occupy multiple columns */
int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int cwidth)
{
unsigned int c;
GlyphBLF *g;
@@ -474,7 +477,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
blf_batch_draw_begin(font);
while ((i < len) && str[i]) {
while ((i < str_len) && str[i]) {
g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
@@ -512,7 +515,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
static void blf_font_draw_buffer_ex(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
size_t len,
const size_t str_len,
struct ResultBLF *r_info,
int pen_y)
{
@@ -531,7 +534,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
/* another buffer specific call for color conversion */
while ((i < len) && str[i]) {
while ((i < str_len) && str[i]) {
g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
@@ -646,10 +649,13 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
}
}
void blf_font_draw_buffer(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
void blf_font_draw_buffer(FontBLF *font,
const char *str,
const size_t str_len,
struct ResultBLF *r_info)
{
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
blf_font_draw_buffer_ex(font, gc, str, len, r_info, 0);
blf_font_draw_buffer_ex(font, gc, str, str_len, r_info, 0);
blf_glyph_cache_release(font);
}
@@ -685,7 +691,7 @@ static bool blf_font_width_to_strlen_glyph_process(FontBLF *font,
}
size_t blf_font_width_to_strlen(
FontBLF *font, const char *str, size_t len, float width, float *r_width)
FontBLF *font, const char *str, const size_t str_len, float width, float *r_width)
{
unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev;
@@ -695,7 +701,7 @@ size_t blf_font_width_to_strlen(
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
const int width_i = (int)width;
for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < len) && str[i];
for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < str_len) && str[i];
i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) {
g = blf_utf8_next_fast(font, gc, str, &i, &c);
@@ -713,7 +719,7 @@ size_t blf_font_width_to_strlen(
}
size_t blf_font_width_to_rstrlen(
FontBLF *font, const char *str, size_t len, float width, float *r_width)
FontBLF *font, const char *str, const size_t str_len, float width, float *r_width)
{
unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev;
@@ -724,7 +730,7 @@ size_t blf_font_width_to_rstrlen(
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
const int width_i = (int)width;
i = BLI_strnlen(str, len);
i = BLI_strnlen(str, str_len);
s = BLI_str_find_prev_char_utf8(str, &str[i]);
i = (size_t)((s != NULL) ? s - str : 0);
s_prev = BLI_str_find_prev_char_utf8(str, s);
@@ -765,7 +771,7 @@ size_t blf_font_width_to_rstrlen(
static void blf_font_boundbox_ex(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
size_t len,
const size_t str_len,
rctf *box,
struct ResultBLF *r_info,
int pen_y)
@@ -781,7 +787,7 @@ static void blf_font_boundbox_ex(FontBLF *font,
box->ymin = 32000.0f;
box->ymax = -32000.0f;
while ((i < len) && str[i]) {
while ((i < str_len) && str[i]) {
g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
@@ -829,16 +835,16 @@ static void blf_font_boundbox_ex(FontBLF *font,
}
}
void blf_font_boundbox(
FontBLF *font, const char *str, size_t len, rctf *r_box, struct ResultBLF *r_info)
FontBLF *font, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info)
{
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
blf_font_boundbox_ex(font, gc, str, len, r_box, r_info, 0);
blf_font_boundbox_ex(font, gc, str, str_len, r_box, r_info, 0);
blf_glyph_cache_release(font);
}
void blf_font_width_and_height(FontBLF *font,
const char *str,
size_t len,
const size_t str_len,
float *r_width,
float *r_height,
struct ResultBLF *r_info)
@@ -856,16 +862,19 @@ void blf_font_width_and_height(FontBLF *font,
}
if (font->flags & BLF_WORD_WRAP) {
blf_font_boundbox__wrap(font, str, len, &box, r_info);
blf_font_boundbox__wrap(font, str, str_len, &box, r_info);
}
else {
blf_font_boundbox(font, str, len, &box, r_info);
blf_font_boundbox(font, str, str_len, &box, r_info);
}
*r_width = (BLI_rctf_size_x(&box) * xa);
*r_height = (BLI_rctf_size_y(&box) * ya);
}
float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
float blf_font_width(FontBLF *font,
const char *str,
const size_t str_len,
struct ResultBLF *r_info)
{
float xa;
rctf box;
@@ -878,15 +887,18 @@ float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBL
}
if (font->flags & BLF_WORD_WRAP) {
blf_font_boundbox__wrap(font, str, len, &box, r_info);
blf_font_boundbox__wrap(font, str, str_len, &box, r_info);
}
else {
blf_font_boundbox(font, str, len, &box, r_info);
blf_font_boundbox(font, str, str_len, &box, r_info);
}
return BLI_rctf_size_x(&box) * xa;
}
float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
float blf_font_height(FontBLF *font,
const char *str,
const size_t str_len,
struct ResultBLF *r_info)
{
float ya;
rctf box;
@@ -899,10 +911,10 @@ float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultB
}
if (font->flags & BLF_WORD_WRAP) {
blf_font_boundbox__wrap(font, str, len, &box, r_info);
blf_font_boundbox__wrap(font, str, str_len, &box, r_info);
}
else {
blf_font_boundbox(font, str, len, &box, r_info);
blf_font_boundbox(font, str, str_len, &box, r_info);
}
return BLI_rctf_size_y(&box) * ya;
}
@@ -930,7 +942,7 @@ float blf_font_fixed_width(FontBLF *font)
static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
size_t len,
const size_t str_len,
BLF_GlyphBoundsFn user_fn,
void *user_data,
struct ResultBLF *r_info,
@@ -942,12 +954,12 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
size_t i = 0, i_curr;
rcti gbox;
if (len == 0) {
if (str_len == 0) {
/* early output. */
return;
}
while ((i < len) && str[i]) {
while ((i < str_len) && str[i]) {
i_curr = i;
g = blf_utf8_next_fast(font, gc, str, &i, &c);
@@ -981,13 +993,13 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
}
void blf_font_boundbox_foreach_glyph(FontBLF *font,
const char *str,
size_t len,
const size_t str_len,
BLF_GlyphBoundsFn user_fn,
void *user_data,
struct ResultBLF *r_info)
{
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0);
blf_font_boundbox_foreach_glyph_ex(font, gc, str, str_len, user_fn, user_data, r_info, 0);
blf_glyph_cache_release(font);
}
@@ -1008,12 +1020,12 @@ void blf_font_boundbox_foreach_glyph(FontBLF *font,
*/
static void blf_font_wrap_apply(FontBLF *font,
const char *str,
size_t len,
const size_t str_len,
struct ResultBLF *r_info,
void (*callback)(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
size_t len,
const size_t str_len,
int pen_y,
void *userdata),
void *userdata)
@@ -1032,8 +1044,8 @@ static void blf_font_wrap_apply(FontBLF *font,
size_t start, last[2];
} wrap = {font->wrap_width != -1 ? font->wrap_width : INT_MAX, 0, {0, 0}};
// printf("%s wrapping (%d, %d) `%s`:\n", __func__, len, strlen(str), str);
while ((i < len) && str[i]) {
// printf("%s wrapping (%d, %d) `%s`:\n", __func__, str_len, strlen(str), str);
while ((i < str_len) && str[i]) {
/* wrap vars */
size_t i_curr = i;
@@ -1061,7 +1073,7 @@ static void blf_font_wrap_apply(FontBLF *font,
if (UNLIKELY((pen_x_next >= wrap.wrap_width) && (wrap.start != wrap.last[0]))) {
do_draw = true;
}
else if (UNLIKELY(((i < len) && str[i]) == 0)) {
else if (UNLIKELY(((i < str_len) && str[i]) == 0)) {
/* need check here for trailing newline, else we draw it */
wrap.last[0] = i + ((g->c != '\n') ? 1 : 0);
wrap.last[1] = i;
@@ -1112,54 +1124,61 @@ static void blf_font_wrap_apply(FontBLF *font,
static void blf_font_draw__wrap_cb(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
size_t len,
const size_t str_len,
int pen_y,
void *UNUSED(userdata))
{
blf_font_draw_ex(font, gc, str, len, NULL, pen_y);
blf_font_draw_ex(font, gc, str, str_len, NULL, pen_y);
}
void blf_font_draw__wrap(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
void blf_font_draw__wrap(FontBLF *font,
const char *str,
const size_t str_len,
struct ResultBLF *r_info)
{
blf_font_wrap_apply(font, str, len, r_info, blf_font_draw__wrap_cb, NULL);
blf_font_wrap_apply(font, str, str_len, r_info, blf_font_draw__wrap_cb, NULL);
}
/* blf_font_boundbox__wrap */
static void blf_font_boundbox_wrap_cb(
FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t len, int pen_y, void *userdata)
static void blf_font_boundbox_wrap_cb(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
const size_t str_len,
int pen_y,
void *userdata)
{
rctf *box = userdata;
rctf box_single;
blf_font_boundbox_ex(font, gc, str, len, &box_single, NULL, pen_y);
blf_font_boundbox_ex(font, gc, str, str_len, &box_single, NULL, pen_y);
BLI_rctf_union(box, &box_single);
}
void blf_font_boundbox__wrap(
FontBLF *font, const char *str, size_t len, rctf *box, struct ResultBLF *r_info)
FontBLF *font, const char *str, const size_t str_len, rctf *box, struct ResultBLF *r_info)
{
box->xmin = 32000.0f;
box->xmax = -32000.0f;
box->ymin = 32000.0f;
box->ymax = -32000.0f;
blf_font_wrap_apply(font, str, len, r_info, blf_font_boundbox_wrap_cb, box);
blf_font_wrap_apply(font, str, str_len, r_info, blf_font_boundbox_wrap_cb, box);
}
/* blf_font_draw_buffer__wrap */
static void blf_font_draw_buffer__wrap_cb(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
size_t len,
const size_t str_len,
int pen_y,
void *UNUSED(userdata))
{
blf_font_draw_buffer_ex(font, gc, str, len, NULL, pen_y);
blf_font_draw_buffer_ex(font, gc, str, str_len, NULL, pen_y);
}
void blf_font_draw_buffer__wrap(FontBLF *font,
const char *str,
size_t len,
const size_t str_len,
struct ResultBLF *r_info)
{
blf_font_wrap_apply(font, str, len, r_info, blf_font_draw_buffer__wrap_cb, NULL);
blf_font_wrap_apply(font, str, str_len, r_info, blf_font_draw_buffer__wrap_cb, NULL);
}
/** \} */
@@ -1170,14 +1189,14 @@ void blf_font_draw_buffer__wrap(FontBLF *font,
int blf_font_count_missing_chars(FontBLF *font,
const char *str,
const size_t len,
const size_t str_len,
int *r_tot_chars)
{
int missing = 0;
size_t i = 0;
*r_tot_chars = 0;
while (i < len) {
while (i < str_len) {
unsigned int c;
if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) {

View File

@@ -53,46 +53,55 @@ struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem
void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size);
void blf_font_size(struct FontBLF *font, unsigned int size, unsigned int dpi);
void blf_font_draw(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info);
void blf_font_draw(struct FontBLF *font,
const char *str,
size_t str_len,
struct ResultBLF *r_info);
void blf_font_draw__wrap(struct FontBLF *font,
const char *str,
size_t len,
size_t str_len,
struct ResultBLF *r_info);
void blf_font_draw_ascii(struct FontBLF *font,
const char *str,
size_t len,
size_t str_len,
struct ResultBLF *r_info);
int blf_font_draw_mono(struct FontBLF *font, const char *str, size_t len, int cwidth);
int blf_font_draw_mono(struct FontBLF *font, const char *str, size_t str_len, int cwidth);
void blf_font_draw_buffer(struct FontBLF *font,
const char *str,
size_t len,
size_t str_len,
struct ResultBLF *r_info);
void blf_font_draw_buffer__wrap(struct FontBLF *font,
const char *str,
size_t len,
size_t str_len,
struct ResultBLF *r_info);
size_t blf_font_width_to_strlen(
struct FontBLF *font, const char *str, size_t len, float width, float *r_width);
struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width);
size_t blf_font_width_to_rstrlen(
struct FontBLF *font, const char *str, size_t len, float width, float *r_width);
struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width);
void blf_font_boundbox(struct FontBLF *font,
const char *str,
size_t len,
size_t str_len,
struct rctf *r_box,
struct ResultBLF *r_info);
void blf_font_boundbox__wrap(struct FontBLF *font,
const char *str,
size_t len,
size_t str_len,
struct rctf *r_box,
struct ResultBLF *r_info);
void blf_font_width_and_height(struct FontBLF *font,
const char *str,
size_t len,
size_t str_len,
float *r_width,
float *r_height,
struct ResultBLF *r_info);
float blf_font_width(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info);
float blf_font_height(struct FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info);
float blf_font_width(struct FontBLF *font,
const char *str,
size_t str_len,
struct ResultBLF *r_info);
float blf_font_height(struct FontBLF *font,
const char *str,
size_t str_len,
struct ResultBLF *r_info);
float blf_font_fixed_width(struct FontBLF *font);
int blf_font_height_max(struct FontBLF *font);
int blf_font_width_max(struct FontBLF *font);
@@ -103,7 +112,7 @@ char *blf_display_name(struct FontBLF *font);
void blf_font_boundbox_foreach_glyph(struct FontBLF *font,
const char *str,
size_t len,
size_t str_len,
bool (*user_fn)(const char *str,
const size_t str_step_ofs,
const struct rcti *glyph_step_bounds,
@@ -116,7 +125,7 @@ void blf_font_boundbox_foreach_glyph(struct FontBLF *font,
int blf_font_count_missing_chars(struct FontBLF *font,
const char *str,
const size_t len,
const size_t str_len,
int *r_tot_chars);
void blf_font_free(struct FontBLF *font);

View File

@@ -37,10 +37,12 @@
struct AttributeMetaData {
AttributeDomain domain;
CustomDataType data_type;
const AnonymousCustomDataLayerID *anonymous_layer_id = nullptr;
constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
{
return (a.domain == b.domain) && (a.data_type == b.data_type);
return (a.domain == b.domain) && (a.data_type == b.data_type) &&
(a.anonymous_layer_id == b.anonymous_layer_id);
}
};
@@ -354,6 +356,12 @@ class CustomDataAttributes {
bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer);
bool remove(const blender::StringRef name);
bool create_anonymous(const AnonymousCustomDataLayerID &id, const CustomDataType data_type);
std::optional<blender::fn::GSpan> get_anonymous_for_read(
const AnonymousCustomDataLayerID &id) const;
std::optional<blender::fn::GMutableSpan> get_anonymous_for_write(
const AnonymousCustomDataLayerID &id);
bool foreach_attribute(const AttributeForeachCallback callback,
const AttributeDomain domain) const;
};

View File

@@ -482,6 +482,14 @@ void CustomData_external_reload(struct CustomData *data,
CustomDataMask mask,
int totelem);
/* Anonymous layers. */
struct AnonymousCustomDataLayerID *CustomData_anonymous_id_new(const char *debug_name);
void CustomData_anonymous_id_strong_decrement(const struct AnonymousCustomDataLayerID *layer_id);
void CustomData_anonymous_id_strong_increment(const struct AnonymousCustomDataLayerID *layer_id);
void CustomData_anonymous_id_weak_decrement(const struct AnonymousCustomDataLayerID *layer_id);
void CustomData_anonymous_id_weak_increment(const struct AnonymousCustomDataLayerID *layer_id);
bool CustomData_layer_is_unused_anonymous(const struct CustomDataLayer *layer);
/* Mesh-to-mesh transfer data. */
struct CustomDataTransferLayerMap;

View File

@@ -0,0 +1,553 @@
/*
* 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.
*/
#pragma once
/** \file
* \ingroup bke
*/
#include <atomic>
#include "BLI_function_ref.hh"
#include "BLI_map.hh"
#include "BLI_optional_ptr.hh"
#include "BLI_user_counter.hh"
#include "BLI_vector.hh"
#include "BLI_virtual_array.hh"
#include "FN_cpp_type.hh"
#include "FN_cpp_type_make.hh"
#include "FN_multi_function.hh"
#include "BKE_customdata.h"
namespace blender::bke {
using fn::CPPType;
using fn::GMutableSpan;
using fn::GVArray;
using fn::GVArrayPtr;
using fn::MultiFunction;
class FieldInputKey {
public:
virtual ~FieldInputKey() = default;
virtual uint64_t hash() const = 0;
virtual const CPPType &type() const = 0;
friend bool operator==(const FieldInputKey &a, const FieldInputKey &b)
{
return a.is_same_as(b);
}
private:
virtual bool is_same_as(const FieldInputKey &other) const
{
UNUSED_VARS(other);
return false;
}
};
class FieldInputValue {
public:
virtual ~FieldInputValue() = default;
};
class IndexFieldInputKey : public FieldInputKey {
public:
uint64_t hash() const override
{
/* Arbitrary number. */
return 78582029;
}
const CPPType &type() const override
{
return CPPType::get<int>();
}
private:
bool is_same_as(const FieldInputKey &other) const override
{
return dynamic_cast<const IndexFieldInputKey *>(&other) != nullptr;
}
};
class CurveParameterFieldInputKey : public FieldInputKey {
public:
uint64_t hash() const override
{
/* Arbitrary number. */
return 928347504059;
}
const CPPType &type() const override
{
return CPPType::get<float>();
}
private:
bool is_same_as(const FieldInputKey &other) const override
{
return dynamic_cast<const CurveParameterFieldInputKey *>(&other) != nullptr;
}
};
class AnonymousAttributeFieldInputKey : public FieldInputKey {
private:
AnonymousCustomDataLayerID *layer_id_;
const CPPType &type_;
public:
AnonymousAttributeFieldInputKey(AnonymousCustomDataLayerID &layer_id, const CPPType &type)
: layer_id_(&layer_id), type_(type)
{
CustomData_anonymous_id_strong_increment(layer_id_);
}
~AnonymousAttributeFieldInputKey()
{
CustomData_anonymous_id_strong_decrement(layer_id_);
}
const CPPType &type() const override
{
return type_;
}
uint64_t hash() const override
{
return get_default_hash(layer_id_);
}
const AnonymousCustomDataLayerID &layer_id() const
{
return *layer_id_;
}
private:
bool is_same_as(const FieldInputKey &other) const override
{
if (const AnonymousAttributeFieldInputKey *other_typed =
dynamic_cast<const AnonymousAttributeFieldInputKey *>(&other)) {
return layer_id_ == other_typed->layer_id_ && type_ == other_typed->type_;
}
return false;
}
};
class PersistentAttributeFieldInputKey : public FieldInputKey {
private:
std::string name_;
const CPPType *type_;
public:
PersistentAttributeFieldInputKey(std::string name, const CPPType &type)
: name_(std::move(name)), type_(&type)
{
}
uint64_t hash() const override
{
return get_default_hash_2(name_, type_);
}
const CPPType &type() const override
{
return *type_;
}
StringRefNull name() const
{
return name_;
}
private:
bool is_same_as(const FieldInputKey &other) const override
{
if (const PersistentAttributeFieldInputKey *other_typed =
dynamic_cast<const PersistentAttributeFieldInputKey *>(&other)) {
return other_typed->type_ == type_ && other_typed->name_ == name_;
}
return false;
}
};
class GVArrayFieldInputValue : public FieldInputValue {
private:
optional_ptr<GVArray> varray_;
public:
GVArrayFieldInputValue(optional_ptr<GVArray> varray) : varray_(std::move(varray))
{
}
const GVArray &varray() const
{
return *varray_;
}
};
class FieldInputs {
private:
using InputMap = Map<std::reference_wrapper<const FieldInputKey>, const FieldInputValue *>;
InputMap inputs_;
friend class Field;
public:
InputMap::KeyIterator begin() const
{
return inputs_.keys().begin();
}
InputMap::KeyIterator end() const
{
return inputs_.keys().end();
}
int tot_inputs() const
{
return inputs_.size();
}
void set_input(const FieldInputKey &key, const FieldInputValue &value)
{
*inputs_.lookup_ptr(key) = &value;
}
const FieldInputValue *get(const FieldInputKey &key) const
{
return inputs_.lookup_default(key, nullptr);
}
template<typename ValueT> const ValueT *get(const FieldInputKey &key) const
{
return dynamic_cast<const ValueT *>(this->get(key));
}
};
class FieldOutput {
private:
optional_ptr<const GVArray> varray_;
public:
FieldOutput(optional_ptr<const GVArray> varray) : varray_(std::move(varray))
{
}
const GVArray &varray_ref() const
{
return *varray_;
}
};
class Field {
private:
mutable std::atomic<int> users_ = 1;
public:
virtual ~Field() = default;
FieldInputs prepare_inputs() const
{
FieldInputs inputs;
this->foreach_input_key([&](const FieldInputKey &key) { inputs.inputs_.add(key, nullptr); });
return inputs;
}
virtual void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const
{
UNUSED_VARS(callback);
}
virtual const CPPType &output_type() const = 0;
virtual FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const = 0;
void user_add() const
{
users_.fetch_add(1);
}
void user_remove() const
{
const int new_users = users_.fetch_sub(1) - 1;
if (new_users == 0) {
delete this;
}
}
};
using FieldPtr = UserCounter<Field>;
template<typename T> class ConstantField : public Field {
private:
T value_;
public:
ConstantField(T value) : value_(std::move(value))
{
}
const CPPType &output_type() const override
{
return CPPType::get<T>();
}
FieldOutput evaluate(IndexMask mask, const FieldInputs &UNUSED(inputs)) const
{
return optional_ptr<const GVArray>{std::make_unique<fn::GVArray_For_SingleValue>(
CPPType::get<T>(), mask.min_array_size(), &value_)};
}
};
template<typename KeyT> class GVArrayInputField : public Field {
private:
KeyT key_;
public:
template<typename... Args> GVArrayInputField(Args &&...args) : key_(std::forward<Args>(args)...)
{
}
void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const override
{
callback(key_);
}
const CPPType &output_type() const override
{
return key_.type();
}
FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const override
{
const GVArrayFieldInputValue *input = inputs.get<GVArrayFieldInputValue>(key_);
if (input == nullptr) {
return FieldOutput{
optional_ptr<const GVArray>{std::make_unique<fn::GVArray_For_SingleValueRef>(
key_.type(), mask.min_array_size(), key_.type().default_value())}};
}
return FieldOutput{optional_ptr<const GVArray>{input->varray()}};
}
};
class MultiFunctionField : public Field {
private:
Vector<FieldPtr> input_fields_;
optional_ptr<const MultiFunction> fn_;
const int output_param_index_;
public:
MultiFunctionField(Vector<FieldPtr> input_fields,
optional_ptr<const MultiFunction> fn,
const int output_param_index)
: input_fields_(std::move(input_fields)),
fn_(std::move(fn)),
output_param_index_(output_param_index)
{
}
const CPPType &output_type() const override
{
return fn_->param_type(output_param_index_).data_type().single_type();
}
void foreach_input_key(FunctionRef<void(const FieldInputKey &key)> callback) const override
{
for (const FieldPtr &field : input_fields_) {
field->foreach_input_key(callback);
}
}
FieldOutput evaluate(IndexMask mask, const FieldInputs &inputs) const final
{
fn::MFParamsBuilder params{*fn_, mask.min_array_size()};
fn::MFContextBuilder context;
ResourceScope &scope = params.resource_scope();
Vector<GMutableSpan> outputs;
int output_span_index = -1;
int input_index = 0;
for (const int param_index : fn_->param_indices()) {
fn::MFParamType param_type = fn_->param_type(param_index);
switch (param_type.category()) {
case fn::MFParamType::SingleInput: {
const Field &field = *input_fields_[input_index];
FieldOutput &output = scope.add_value(field.evaluate(mask, inputs), __func__);
params.add_readonly_single_input(output.varray_ref());
input_index++;
break;
}
case fn::MFParamType::SingleOutput: {
const CPPType &type = param_type.data_type().single_type();
void *buffer = MEM_mallocN_aligned(
mask.min_array_size() * type.size(), type.alignment(), __func__);
GMutableSpan span{type, buffer, mask.min_array_size()};
outputs.append(span);
params.add_uninitialized_single_output(span);
if (param_index == output_param_index_) {
output_span_index = outputs.size() - 1;
}
break;
}
case fn::MFParamType::SingleMutable:
case fn::MFParamType::VectorInput:
case fn::MFParamType::VectorMutable:
case fn::MFParamType::VectorOutput:
BLI_assert_unreachable();
break;
}
}
fn_->call(mask, params, context);
GMutableSpan output_span = outputs[output_span_index];
outputs.remove(output_span_index);
for (GMutableSpan span : outputs) {
span.type().destruct_indices(span.data(), mask);
MEM_freeN(span.data());
}
std::unique_ptr<GVArray> out_array = std::make_unique<fn::GVArray_For_OwnedGSpan>(output_span,
mask);
return FieldOutput{optional_ptr<const GVArray>{std::move(out_array)}};
}
};
class PersistentAttributeField : public GVArrayInputField<PersistentAttributeFieldInputKey> {
public:
PersistentAttributeField(std::string name, const CPPType &type)
: GVArrayInputField<PersistentAttributeFieldInputKey>(std::move(name), type)
{
}
};
class AnonymousAttributeField : public GVArrayInputField<AnonymousAttributeFieldInputKey> {
public:
AnonymousAttributeField(AnonymousCustomDataLayerID &layer_id, const CPPType &type)
: GVArrayInputField<AnonymousAttributeFieldInputKey>(layer_id, type)
{
}
};
class IndexField : public GVArrayInputField<IndexFieldInputKey> {
};
class CurveParameterField : public GVArrayInputField<CurveParameterFieldInputKey> {
};
class FieldRefBase {
protected:
FieldPtr field_;
public:
const FieldPtr &field() const
{
return field_;
}
};
template<typename T> class FieldRef : public FieldRefBase {
public:
FieldRef()
{
field_ = new ConstantField<T>(T());
}
FieldRef(FieldPtr field)
{
field_ = std::move(field);
}
const Field *operator->() const
{
return &*field_;
}
uint64_t hash() const
{
return get_default_hash(&*field_);
}
friend bool operator==(const FieldRef &a, const FieldRef &b)
{
return &*a.field_ == &*b.field_;
}
friend std::ostream &operator<<(std::ostream &stream, const FieldRef &a)
{
stream << &*a.field_;
return stream;
}
};
template<typename T> struct FieldRefCPPTypeParam {
};
class FieldRefCPPType : public CPPType {
private:
const CPPType &field_type_;
FieldPtr (*get_field_)(const void *field_ref);
void (*construct_)(void *dst, FieldPtr field);
public:
template<typename T>
FieldRefCPPType(FieldRefCPPTypeParam<FieldRef<T>> /* unused */, StringRef debug_name)
: CPPType(fn::CPPTypeParam<FieldRef<T>, CPPTypeFlags::BasicType>(), debug_name),
field_type_(CPPType::get<T>())
{
get_field_ = [](const void *field_ref) {
return ((const blender::bke::FieldRef<T> *)field_ref)->field();
};
construct_ = [](void *dst, blender::bke::FieldPtr field) {
new (dst) blender::bke::FieldRef<T>(std::move(field));
};
}
const CPPType &field_type() const
{
return field_type_;
};
FieldPtr get_field(const void *field_ref) const
{
return get_field_(field_ref);
}
void construct(void *dst, FieldPtr field) const
{
construct_(dst, std::move(field));
}
};
} // namespace blender::bke
#define MAKE_FIELD_REF_CPP_TYPE(DEBUG_NAME, FIELD_TYPE) \
template<> \
const blender::fn::CPPType & \
blender::fn::CPPType::get_impl<blender::bke::FieldRef<FIELD_TYPE>>() \
{ \
static blender::bke::FieldRefCPPType cpp_type{ \
blender::bke::FieldRefCPPTypeParam<blender::bke::FieldRef<FIELD_TYPE>>(), \
STRINGIFY(DEBUG_NAME)}; \
return cpp_type; \
}

View File

@@ -128,6 +128,28 @@ class GeometryComponent {
const CustomDataType data_type,
const AttributeInit &initializer);
bool attribute_try_create_anonymous(const AnonymousCustomDataLayerID &layer_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer);
blender::bke::ReadAttributeLookup attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &layer_id) const;
blender::fn::GVArrayPtr attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type) const;
blender::fn::GVArrayPtr attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value) const;
blender::bke::WriteAttributeLookup attribute_try_get_anonymous_for_write(
const AnonymousCustomDataLayerID &layer_id);
/* Try to create the builtin attribute with the given name. No data type or domain has to be
* provided, because those are fixed for builtin attributes. */
bool attribute_try_create_builtin(const blender::StringRef attribute_name,
@@ -139,8 +161,8 @@ class GeometryComponent {
virtual bool is_empty() const;
/* Get a virtual array to read the data of an attribute on the given domain and data type.
* Returns null when the attribute does not exist or cannot be converted to the requested domain
* and data type. */
* Returns null when the attribute does not exist or cannot be converted to the requested
* domain and data type. */
std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
@@ -181,14 +203,14 @@ class GeometryComponent {
}
/**
* Returns an "output attribute", which is essentially a mutable virtual array with some commonly
* used convince features. The returned output attribute might be empty if requested attribute
* cannot exist on the geometry.
* Returns an "output attribute", which is essentially a mutable virtual array with some
* commonly used convince features. The returned output attribute might be empty if requested
* attribute cannot exist on the geometry.
*
* The included convenience features are:
* - Implicit type conversion when writing to builtin attributes.
* - If the attribute name exists already, but has a different type/domain, a temporary attribute
* is created that will overwrite the existing attribute in the end.
* - If the attribute name exists already, but has a different type/domain, a temporary
* attribute is created that will overwrite the existing attribute in the end.
*/
blender::bke::OutputAttribute attribute_try_get_for_output(
const blender::StringRef attribute_name,
@@ -197,8 +219,8 @@ class GeometryComponent {
const void *default_value = nullptr);
/* Same as attribute_try_get_for_output, but should be used when the original values in the
* attributes are not read, i.e. the attribute is used only for output. Since values are not read
* from this attribute, no default value is necessary. */
* attributes are not read, i.e. the attribute is used only for output. Since values are not
* read from this attribute, no default value is necessary. */
blender::bke::OutputAttribute attribute_try_get_for_output_only(
const blender::StringRef attribute_name,
const AttributeDomain domain,
@@ -224,6 +246,35 @@ class GeometryComponent {
return this->attribute_try_get_for_output_only(attribute_name, domain, data_type);
}
blender::bke::OutputAttribute attribute_try_get_anonymous_for_output(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value = nullptr);
blender::bke::OutputAttribute attribute_try_get_anonymous_for_output_only(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type);
template<typename T>
blender::bke::OutputAttribute_Typed<T> attribute_try_get_anonymous_for_output(
const AnonymousCustomDataLayerID &id, const AttributeDomain domain, const T default_value)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_try_get_anonymous_for_output(id, domain, data_type, &default_value);
}
template<typename T>
blender::bke::OutputAttribute_Typed<T> attribute_try_get_anonymous_for_output_only(
const AnonymousCustomDataLayerID &id, const AttributeDomain domain)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
return this->attribute_try_get_anonymous_for_output_only(id, domain, data_type);
}
private:
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
};
@@ -232,12 +283,12 @@ template<typename T>
inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryComponent, T>;
/**
* A geometry set contains zero or more geometry components. There is at most one component of each
* type. Individual components might be shared between multiple geometries. Shared components are
* copied automatically when write access is requested.
* A geometry set contains zero or more geometry components. There is at most one component of
* each type. Individual components might be shared between multiple geometries. Shared
* components are copied automatically when write access is requested.
*
* Copying a geometry set is a relatively cheap operation, because it does not copy the referenced
* geometry components.
* Copying a geometry set is a relatively cheap operation, because it does not copy the
* referenced geometry components.
*/
struct GeometrySet {
private:
@@ -345,6 +396,10 @@ class MeshComponent : public GeometryComponent {
const AttributeDomain from_domain,
const AttributeDomain to_domain) const final;
blender::VArrayPtr<bool> adapt_selection(blender::VArrayPtr<bool> selection,
AttributeDomain from_domain,
AttributeDomain to_domain) const;
bool is_empty() const final;
bool owns_direct_data() const override;
@@ -441,8 +496,8 @@ class InstanceReference {
enum class Type {
/**
* An empty instance. This allows an `InstanceReference` to be default constructed without
* being in an invalid state. There might also be other use cases that we haven't explored much
* yet (such as changing the instance later on, and "disabling" some instances).
* being in an invalid state. There might also be other use cases that we haven't explored
* much yet (such as changing the instance later on, and "disabling" some instances).
*/
None,
Object,
@@ -513,8 +568,8 @@ class InstancesComponent : public GeometryComponent {
blender::Vector<int> instance_ids_;
/* These almost unique ids are generated based on `ids_`, which might not contain unique ids at
* all. They are *almost* unique, because under certain very unlikely circumstances, they are not
* unique. Code using these ids should not crash when they are not unique but can generally
* all. They are *almost* unique, because under certain very unlikely circumstances, they are
* not unique. Code using these ids should not crash when they are not unique but can generally
* expect them to be unique. */
mutable std::mutex almost_unique_ids_mutex_;
mutable blender::Array<int> almost_unique_ids_;

View File

@@ -346,7 +346,7 @@ typedef struct bNodeType {
#define NODE_CLASS_OP_FILTER 5
#define NODE_CLASS_GROUP 6
// #define NODE_CLASS_FILE 7
#define NODE_CLASS_CONVERTOR 8
#define NODE_CLASS_CONVERTER 8
#define NODE_CLASS_MATTE 9
#define NODE_CLASS_DISTORT 10
// #define NODE_CLASS_OP_DYNAMIC 11 /* deprecated */
@@ -1415,7 +1415,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_ATTRIBUTE_COMPARE 1015
#define GEO_NODE_POINT_ROTATE 1016
#define GEO_NODE_ATTRIBUTE_VECTOR_MATH 1017
#define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018
// #define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018
#define GEO_NODE_POINT_TRANSLATE 1019
#define GEO_NODE_POINT_SCALE 1020
#define GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE 1021
@@ -1472,6 +1472,18 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_SET_HANDLES 1072
#define GEO_NODE_CURVE_SPLINE_TYPE 1073
#define GEO_NODE_CURVE_SELECT_HANDLES 1074
#define GEO_NODE_ATTRIBUTE 1075
#define GEO_NODE_INDEX 1076
#define GEO_NODE_EXTRUDE 1077
#define GEO_NODE_ATTRIBUTE_FREEZE 1078
#define GEO_NODE_ATTRIBUTE_EXTRACT 1079
#define GEO_NODE_NORMAL 1080
#define GEO_NODE_CURVE_PARAMETER 1081
#define GEO_NODE_EXTRUDE_AND_MOVE 1082
#define GEO_NODE_POSITION 1083
#define GEO_NODE_SET_POSITION 1084
#define GEO_NODE_SAMPLE_MESH_SURFACE 1085
#define GEO_NODE_EVALUATE_CURVE 1086
/** \} */
@@ -1485,6 +1497,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define FN_NODE_INPUT_VECTOR 1207
#define FN_NODE_INPUT_STRING 1208
#define FN_NODE_FLOAT_TO_INT 1209
#define FN_NODE_ALIGN_ROTATION_TO_VECTOR 1210
/** \} */

View File

@@ -346,27 +346,33 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
if (layer.name != attribute_name) {
continue;
}
const CustomDataType data_type = (CustomDataType)layer.type;
switch (data_type) {
case CD_PROP_FLOAT:
return this->layer_to_read_attribute<float>(layer, domain_size);
case CD_PROP_FLOAT2:
return this->layer_to_read_attribute<float2>(layer, domain_size);
case CD_PROP_FLOAT3:
return this->layer_to_read_attribute<float3>(layer, domain_size);
case CD_PROP_INT32:
return this->layer_to_read_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_read_attribute<bool>(layer, domain_size);
default:
break;
}
return this->layer_to_read_attribute(layer, domain_size);
}
return {};
}
ReadAttributeLookup CustomDataAttributeProvider::layer_to_read_attribute(
const CustomDataLayer &layer, const int domain_size) const
{
const CustomDataType data_type = (CustomDataType)layer.type;
switch (data_type) {
case CD_PROP_FLOAT:
return this->layer_to_read_attribute<float>(layer, domain_size);
case CD_PROP_FLOAT2:
return this->layer_to_read_attribute<float2>(layer, domain_size);
case CD_PROP_FLOAT3:
return this->layer_to_read_attribute<float3>(layer, domain_size);
case CD_PROP_INT32:
return this->layer_to_read_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_read_attribute<bool>(layer, domain_size);
default:
return {};
}
}
WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
{
@@ -380,27 +386,33 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
continue;
}
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size);
const CustomDataType data_type = (CustomDataType)layer.type;
switch (data_type) {
case CD_PROP_FLOAT:
return this->layer_to_write_attribute<float>(layer, domain_size);
case CD_PROP_FLOAT2:
return this->layer_to_write_attribute<float2>(layer, domain_size);
case CD_PROP_FLOAT3:
return this->layer_to_write_attribute<float3>(layer, domain_size);
case CD_PROP_INT32:
return this->layer_to_write_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_write_attribute<bool>(layer, domain_size);
default:
break;
}
return this->layer_to_write_attribute(layer, domain_size);
}
return {};
}
WriteAttributeLookup CustomDataAttributeProvider::layer_to_write_attribute(
CustomDataLayer &layer, const int domain_size) const
{
const CustomDataType data_type = (CustomDataType)layer.type;
switch (data_type) {
case CD_PROP_FLOAT:
return this->layer_to_write_attribute<float>(layer, domain_size);
case CD_PROP_FLOAT2:
return this->layer_to_write_attribute<float2>(layer, domain_size);
case CD_PROP_FLOAT3:
return this->layer_to_write_attribute<float3>(layer, domain_size);
case CD_PROP_INT32:
return this->layer_to_write_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_write_attribute<bool>(layer, domain_size);
default:
return {};
}
}
bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
const StringRef attribute_name) const
{
@@ -487,6 +499,84 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
return true;
}
static std::string get_anonymous_attribute_name()
{
static std::atomic<int> index = 0;
const int next_index = index.fetch_add(1);
return "anonymous_attribute_" + std::to_string(next_index);
}
bool CustomDataAttributeProvider::try_create_anonymous(GeometryComponent &component,
const AnonymousCustomDataLayerID &layer_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer) const
{
if (domain_ != domain) {
return false;
}
if (!this->type_is_supported(data_type)) {
return false;
}
CustomData *custom_data = custom_data_access_.get_custom_data(component);
if (custom_data == nullptr) {
return false;
}
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (layer.anonymous_id == &layer_id) {
/* Don't create two layers with the same id. */
return false;
}
}
const int domain_size = component.attribute_domain_size(domain_);
const std::string attribute_name = get_anonymous_attribute_name();
add_named_custom_data_layer_from_attribute_init(
attribute_name, *custom_data, data_type, domain_size, initializer);
const int layer_index = CustomData_get_named_layer_index(
custom_data, data_type, attribute_name.c_str());
CustomDataLayer *layer = &custom_data->layers[layer_index];
layer->flag |= CD_FLAG_ANONYMOUS;
layer->anonymous_id = &layer_id;
CustomData_anonymous_id_weak_increment(&layer_id);
return true;
}
ReadAttributeLookup CustomDataAttributeProvider::try_get_anonymous_for_read(
const GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
if (custom_data == nullptr) {
return {};
}
const int domain_size = component.attribute_domain_size(domain_);
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
if (layer.anonymous_id != &layer_id) {
continue;
}
return this->layer_to_read_attribute(layer, domain_size);
}
return {};
}
WriteAttributeLookup CustomDataAttributeProvider::try_get_anonymous_for_write(
GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
if (custom_data == nullptr) {
return {};
}
const int domain_size = component.attribute_domain_size(domain_);
for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) {
if (layer.anonymous_id != &layer_id) {
continue;
}
CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size);
return this->layer_to_write_attribute(layer, domain_size);
}
return {};
}
bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const
{
@@ -497,7 +587,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) {
const CustomDataType data_type = (CustomDataType)layer.type;
if (this->type_is_supported(data_type)) {
AttributeMetaData meta_data{domain_, data_type};
AttributeMetaData meta_data{domain_, data_type, layer.anonymous_id};
if (!callback(layer.name, meta_data)) {
return false;
}
@@ -698,6 +788,56 @@ bool CustomDataAttributes::create_by_move(const blender::StringRef name,
return result != nullptr;
}
bool CustomDataAttributes::create_anonymous(const AnonymousCustomDataLayerID &id,
const CustomDataType data_type)
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
if (layer.anonymous_id == &id) {
/* Don't create two layers with the same id. */
return false;
}
}
const std::string name = get_anonymous_attribute_name();
if (!this->create(name, data_type)) {
return false;
}
const int layer_index = CustomData_get_named_layer_index(&data, data_type, name.c_str());
CustomDataLayer &layer = data.layers[layer_index];
layer.flag |= CD_FLAG_ANONYMOUS;
layer.anonymous_id = &id;
CustomData_anonymous_id_weak_increment(&id);
return true;
}
std::optional<GSpan> CustomDataAttributes::get_anonymous_for_read(
const AnonymousCustomDataLayerID &id) const
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
if (layer.anonymous_id == &id) {
const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
BLI_assert(cpp_type != nullptr);
return GSpan(*cpp_type, layer.data, size_);
}
}
return {};
}
std::optional<GMutableSpan> CustomDataAttributes::get_anonymous_for_write(
const AnonymousCustomDataLayerID &id)
{
for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
if (layer.anonymous_id == &id) {
const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
BLI_assert(cpp_type != nullptr);
return GMutableSpan(*cpp_type, layer.data, size_);
}
}
return {};
}
bool CustomDataAttributes::remove(const blender::StringRef name)
{
bool result = false;
@@ -721,7 +861,7 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call
const AttributeDomain domain) const
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
AttributeMetaData meta_data{domain, (CustomDataType)layer.type, layer.anonymous_id};
if (!callback(layer.name, meta_data)) {
return false;
}
@@ -735,6 +875,14 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call
/** \name Geometry Component
* \{ */
static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
{
const blender::nodes::DataTypeConversions &conversions =
blender::nodes::get_implicit_type_conversions();
return conversions.try_convert(std::move(varray), to_type);
}
const blender::bke::ComponentAttributeProviders *GeometryComponent::get_attribute_providers() const
{
return nullptr;
@@ -875,6 +1023,102 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
return false;
}
bool GeometryComponent::attribute_try_create_anonymous(const AnonymousCustomDataLayerID &layer_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer)
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
if (dynamic_provider->try_create_anonymous(*this, layer_id, domain, data_type, initializer)) {
return true;
}
}
return false;
}
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &layer_id) const
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
ReadAttributeLookup attribute = dynamic_provider->try_get_anonymous_for_read(*this, layer_id);
if (attribute) {
return attribute;
}
}
return {};
}
blender::fn::GVArrayPtr GeometryComponent::attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type) const
{
blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_anonymous_for_read(id);
if (!attribute) {
return {};
}
std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray);
if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) {
varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
if (!varray) {
return {};
}
}
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
if (varray->type() != *cpp_type) {
varray = try_adapt_data_type(std::move(varray), *cpp_type);
if (!varray) {
return {};
}
}
return varray;
}
blender::fn::GVArrayPtr GeometryComponent::attribute_try_get_anonymous_for_read(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value) const
{
blender::fn::GVArrayPtr varray = this->attribute_try_get_anonymous_for_read(
id, domain, data_type);
if (varray) {
return varray;
}
const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
if (default_value == nullptr) {
default_value = type->default_value();
}
const int domain_size = this->attribute_domain_size(domain);
return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value);
}
blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_anonymous_for_write(
const AnonymousCustomDataLayerID &layer_id)
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
for (const DynamicAttributesProvider *dynamic_providers :
providers->dynamic_attribute_providers()) {
WriteAttributeLookup attribute = dynamic_providers->try_get_anonymous_for_write(*this,
layer_id);
if (attribute) {
return attribute;
}
}
return {};
}
bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name,
const AttributeInit &initializer)
{
@@ -968,14 +1212,6 @@ std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
return result;
}
static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
{
const blender::nodes::DataTypeConversions &conversions =
blender::nodes::get_implicit_type_conversions();
return conversions.try_convert(std::move(varray), to_type);
}
std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name,
const AttributeDomain domain,
@@ -1227,3 +1463,122 @@ blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_on
{
return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr);
}
class GVMutableAttribute_For_AnonymousOutputAttribute
: public blender::fn::GVMutableArray_For_GMutableSpan {
public:
GeometryComponent *component;
const AnonymousCustomDataLayerID &final_id;
GVMutableAttribute_For_AnonymousOutputAttribute(GMutableSpan data,
GeometryComponent &component,
const AnonymousCustomDataLayerID &final_id)
: blender::fn::GVMutableArray_For_GMutableSpan(data),
component(&component),
final_id(final_id)
{
}
~GVMutableAttribute_For_AnonymousOutputAttribute() override
{
type_->destruct_n(data_, size_);
MEM_freeN(data_);
}
};
static void save_output_anonymous_attribute(blender::bke::OutputAttribute &output_attribute)
{
using namespace blender;
using namespace blender::fn;
using namespace blender::bke;
GVMutableAttribute_For_AnonymousOutputAttribute &varray =
dynamic_cast<GVMutableAttribute_For_AnonymousOutputAttribute &>(output_attribute.varray());
GeometryComponent &component = *varray.component;
WriteAttributeLookup write_attribute = component.attribute_try_get_anonymous_for_write(
varray.final_id);
BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
for (const int i : IndexRange(varray.size())) {
varray.get(i, buffer);
write_attribute.varray->set_by_relocate(i, buffer);
}
}
static blender::bke::OutputAttribute create_output_attribute_anonymous(
GeometryComponent &component,
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type,
const bool ignore_old_values,
const void *default_value)
{
using namespace blender;
using namespace blender::fn;
using namespace blender::bke;
const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
const int domain_size = component.attribute_domain_size(domain);
WriteAttributeLookup attribute = component.attribute_try_get_anonymous_for_write(id);
if (!attribute) {
if (default_value) {
const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
component.attribute_try_create_anonymous(
id, domain, data_type, AttributeInitVArray(&default_varray));
}
else {
component.attribute_try_create_anonymous(id, domain, data_type, AttributeInitDefault());
}
attribute = component.attribute_try_get_anonymous_for_write(id);
if (!attribute) {
/* Can't create the attribute. */
return {};
}
}
if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
/* Existing generic attribute matches exactly. */
return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values);
}
/* Allocate a new array that lives next to the existing attribute. It will overwrite the existing
* attribute after processing is done. */
void *data = MEM_mallocN_aligned(
cpp_type->size() * domain_size, cpp_type->alignment(), __func__);
if (ignore_old_values) {
/* This does nothing for trivially constructible types, but is necessary for correctness. */
cpp_type->default_construct_n(data, domain);
}
else {
BLI_assert_unreachable();
/* Fill the temporary array with values from the existing attribute. */
GVArrayPtr old_varray = component.attribute_try_get_anonymous_for_read(
id, domain, data_type, default_value);
old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
}
GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_AnonymousOutputAttribute>(
GMutableSpan{*cpp_type, data, domain_size}, component, id);
return OutputAttribute(std::move(varray), domain, save_output_anonymous_attribute, true);
}
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_anonymous_for_output(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type,
const void *default_value)
{
return create_output_attribute_anonymous(*this, id, domain, data_type, false, default_value);
}
blender::bke::OutputAttribute GeometryComponent::attribute_try_get_anonymous_for_output_only(
const AnonymousCustomDataLayerID &id,
const AttributeDomain domain,
const CustomDataType data_type)
{
return create_output_attribute_anonymous(*this, id, domain, data_type, true, nullptr);
}

View File

@@ -130,6 +130,30 @@ class DynamicAttributesProvider {
return false;
};
/** Returns the id of the new anonymous or null if no new attribute was created. */
virtual bool try_create_anonymous(GeometryComponent &UNUSED(component),
const AnonymousCustomDataLayerID &UNUSED(layer_id),
const AttributeDomain UNUSED(domain),
const CustomDataType UNUSED(data_type),
const AttributeInit &UNUSED(initializer)) const
{
return false;
}
virtual ReadAttributeLookup try_get_anonymous_for_read(
const GeometryComponent &UNUSED(component),
const AnonymousCustomDataLayerID &UNUSED(layer_id)) const
{
return {};
}
virtual WriteAttributeLookup try_get_anonymous_for_write(
GeometryComponent &UNUSED(component),
const AnonymousCustomDataLayerID &UNUSED(layer_id)) const
{
return {};
}
virtual bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const = 0;
virtual void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const = 0;
@@ -167,6 +191,18 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
const CustomDataType data_type,
const AttributeInit &initializer) const final;
bool try_create_anonymous(GeometryComponent &component,
const AnonymousCustomDataLayerID &layer_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer) const final;
ReadAttributeLookup try_get_anonymous_for_read(
const GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const final;
WriteAttributeLookup try_get_anonymous_for_write(
GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -176,6 +212,9 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
}
private:
ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
const int domain_size) const;
template<typename T>
ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
const int domain_size) const
@@ -185,6 +224,9 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
domain_};
}
WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
const int domain_size) const;
template<typename T>
WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
const int domain_size) const

View File

@@ -1254,11 +1254,10 @@ BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data,
bool in_cache = bvhcache_find(
bvh_cache_p, bvh_cache_type, &tree, &lock_started, mesh_eval_mutex);
BVHCache *bvh_cache = *bvh_cache_p;
bvhtree_balance(tree, true);
if (in_cache == false) {
tree = bvhtree_from_editmesh_looptri_create_tree(
epsilon, tree_type, axis, em, looptri_mask, looptri_num_active);
bvhtree_balance(tree, true);
/* Save on cache for later use */
// printf("BVHTree built and saved on cache\n");

View File

@@ -339,7 +339,11 @@ void CurveEval::assert_valid_point_attributes() const
name,
[&](AttributeMetaData *map_data) {
/* All unique attribute names should be added on the first spline. */
BLI_assert(spline == splines_.first());
/* TODO(Hans/Jacques): This check seems very bad, anonymous attributes with have
* different names on different splines. */
if (meta_data.anonymous_layer_id == nullptr) {
BLI_assert(spline == splines_.first());
}
*map_data = meta_data;
},
[&](AttributeMetaData *map_data) {

View File

@@ -57,6 +57,8 @@
#include "BLO_read_write.h"
#include "atomic_ops.h"
#include "bmesh.h"
#include "CLG_log.h"
@@ -2127,6 +2129,9 @@ bool CustomData_merge(const struct CustomData *source,
if (flag & CD_FLAG_NOCOPY) {
continue;
}
if (CustomData_layer_is_unused_anonymous(layer)) {
continue;
}
if (!(mask & CD_TYPE_AS_MASK(type))) {
continue;
}
@@ -2164,8 +2169,13 @@ bool CustomData_merge(const struct CustomData *source,
newlayer->active_rnd = lastrender;
newlayer->active_clone = lastclone;
newlayer->active_mask = lastmask;
newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY);
newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY | CD_FLAG_ANONYMOUS);
changed = true;
if (layer->flag & CD_FLAG_ANONYMOUS) {
CustomData_anonymous_id_weak_increment(layer->anonymous_id);
newlayer->anonymous_id = layer->anonymous_id;
}
}
}
@@ -2206,6 +2216,10 @@ static void customData_free_layer__internal(CustomDataLayer *layer, int totelem)
{
const LayerTypeInfo *typeInfo;
if (layer->flag & CD_FLAG_ANONYMOUS) {
CustomData_anonymous_id_weak_decrement(layer->anonymous_id);
layer->anonymous_id = NULL;
}
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
typeInfo = layerType_getInfo(layer->type);
@@ -4244,7 +4258,8 @@ void CustomData_blend_write_prepare(CustomData *data,
for (i = 0, j = 0; i < totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
if (layer->flag & CD_FLAG_NOCOPY) { /* Layers with this flag set are not written to file. */
/* Layers with this flag set are not written to file. */
if (layer->flag & (CD_FLAG_NOCOPY | CD_FLAG_ANONYMOUS)) {
data->totlayer--;
// CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name);
}
@@ -5031,6 +5046,68 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap,
MEM_SAFE_FREE(tmp_data_src);
}
AnonymousCustomDataLayerID *CustomData_anonymous_id_new(const char *debug_name)
{
AnonymousCustomDataLayerID *layer_id = MEM_callocN(sizeof(AnonymousCustomDataLayerID), __func__);
layer_id->debug_name = BLI_strdup(debug_name);
return layer_id;
}
static void CustomData_anonymous_id_free(AnonymousCustomDataLayerID *layer_id)
{
BLI_assert(layer_id->strong_references == 0);
BLI_assert(layer_id->tot_references == 0);
MEM_freeN(layer_id->debug_name);
MEM_freeN(layer_id);
}
void CustomData_anonymous_id_strong_decrement(const AnonymousCustomDataLayerID *layer_id)
{
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
int strong_references = atomic_sub_and_fetch_int32(&mutable_layer_id->strong_references, 1);
BLI_assert(strong_references >= 0);
UNUSED_VARS_NDEBUG(strong_references);
int tot_references = atomic_sub_and_fetch_int32(&mutable_layer_id->tot_references, 1);
BLI_assert(tot_references >= 0);
if (tot_references == 0) {
CustomData_anonymous_id_free(mutable_layer_id);
}
}
void CustomData_anonymous_id_strong_increment(const AnonymousCustomDataLayerID *layer_id)
{
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
atomic_add_and_fetch_int32(&mutable_layer_id->tot_references, 1);
atomic_add_and_fetch_int32(&mutable_layer_id->strong_references, 1);
}
void CustomData_anonymous_id_weak_decrement(const AnonymousCustomDataLayerID *layer_id)
{
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
int tot_references = atomic_sub_and_fetch_int32(&mutable_layer_id->tot_references, 1);
BLI_assert(tot_references >= 0);
if (tot_references == 0) {
CustomData_anonymous_id_free(mutable_layer_id);
}
}
void CustomData_anonymous_id_weak_increment(const AnonymousCustomDataLayerID *layer_id)
{
AnonymousCustomDataLayerID *mutable_layer_id = (AnonymousCustomDataLayerID *)layer_id;
atomic_add_and_fetch_int32(&mutable_layer_id->tot_references, 1);
}
bool CustomData_layer_is_unused_anonymous(const CustomDataLayer *layer)
{
if (layer->flag & CD_FLAG_ANONYMOUS) {
if (layer->anonymous_id->strong_references == 0) {
return true;
}
}
return false;
}
static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external)
{
if (mdlist) {

View File

@@ -1094,6 +1094,165 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
return true;
}
bool try_create_anonymous(GeometryComponent &component,
const AnonymousCustomDataLayerID &layer_id,
const AttributeDomain domain,
const CustomDataType data_type,
const AttributeInit &initializer) const
{
BLI_assert(this->type_is_supported(data_type));
if (domain != ATTR_DOMAIN_POINT) {
return false;
}
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr || curve->splines().size() == 0) {
return false;
}
MutableSpan<SplinePtr> splines = curve->splines();
/* Otherwise just create a custom data layer on each of the splines. */
for (const int i : splines.index_range()) {
if (!splines[i]->attributes.create_anonymous(layer_id, data_type)) {
/* If attribute creation fails on one of the splines, we cannot leave the custom data
* layers in the previous splines around, so delete them before returning. However,
* this is not an expected case. */
BLI_assert_unreachable();
return false;
}
}
/* With a default initializer type, we can keep the values at their initial values. */
if (initializer.type == AttributeInit::Type::Default) {
return true;
}
WriteAttributeLookup write_attribute = this->try_get_anonymous_for_write(component, layer_id);
/* We just created the attribute, it should exist. */
BLI_assert(write_attribute);
const int total_size = curve->control_point_offsets().last();
GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size);
/* TODO: When we can call a variant of #set_all with a virtual array argument,
* this theoretically unnecessary materialize step could be removed. */
GVArray_GSpan source_varray_span{*source_varray};
write_attribute.varray->set_all(source_varray_span.data());
if (initializer.type == AttributeInit::Type::MoveArray) {
MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
}
curve->assert_valid_point_attributes();
return true;
}
ReadAttributeLookup try_get_anonymous_for_read(const GeometryComponent &component,
const AnonymousCustomDataLayerID &layer_id) const
{
const CurveEval *curve = get_curve_from_component_for_read(component);
if (curve == nullptr || curve->splines().size() == 0) {
return {};
}
Span<SplinePtr> splines = curve->splines();
Vector<GSpan> spans; /* GSpan has no default constructor. */
spans.reserve(splines.size());
std::optional<GSpan> first_span = splines[0]->attributes.get_anonymous_for_read(layer_id);
if (!first_span) {
return {};
}
spans.append(*first_span);
for (const int i : IndexRange(1, splines.size() - 1)) {
std::optional<GSpan> span = splines[i]->attributes.get_anonymous_for_read(layer_id);
if (!span) {
/* All splines should have the same set of data layers. It would be possible to recover
* here and return partial data instead, but that would add a lot of complexity for a
* situation we don't even expect to encounter. */
BLI_assert_unreachable();
return {};
}
if (span->type() != spans.last().type()) {
/* Data layer types on separate splines do not match. */
BLI_assert_unreachable();
return {};
}
spans.append(*span);
}
/* First check for the simpler situation when we can return a simpler span virtual array. */
if (spans.size() == 1) {
return {std::make_unique<GVArray_For_GSpan>(spans.first()), ATTR_DOMAIN_POINT};
}
ReadAttributeLookup attribute = {};
Array<int> offsets = curve->control_point_offsets();
attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
using T = decltype(dummy);
Array<Span<T>> data(splines.size());
for (const int i : splines.index_range()) {
data[i] = spans[i].typed<T>();
BLI_assert(data[i].data() != nullptr);
}
attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
});
return attribute;
}
WriteAttributeLookup try_get_anonymous_for_write(
GeometryComponent &component, const AnonymousCustomDataLayerID &layer_id) const
{
CurveEval *curve = get_curve_from_component_for_write(component);
if (curve == nullptr || curve->splines().size() == 0) {
return {};
}
MutableSpan<SplinePtr> splines = curve->splines();
Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */
spans.reserve(splines.size());
std::optional<GMutableSpan> first_span = splines[0]->attributes.get_anonymous_for_write(
layer_id);
if (!first_span) {
return {};
}
spans.append(*first_span);
for (const int i : IndexRange(1, splines.size() - 1)) {
std::optional<GMutableSpan> span = splines[i]->attributes.get_anonymous_for_write(layer_id);
if (!span) {
/* All splines should have the same set of data layers. It would be possible to recover
* here and return partial data instead, but that would add a lot of complexity for a
* situation we don't even expect to encounter. */
BLI_assert_unreachable();
return {};
}
if (span->type() != spans.last().type()) {
/* Data layer types on separate splines do not match. */
BLI_assert_unreachable();
return {};
}
spans.append(*span);
}
/* First check for the simpler situation when we can return a simpler span virtual array. */
if (spans.size() == 1) {
return {std::make_unique<GVMutableArray_For_GMutableSpan>(spans.first()), ATTR_DOMAIN_POINT};
}
WriteAttributeLookup attribute = {};
Array<int> offsets = curve->control_point_offsets();
attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
using T = decltype(dummy);
Array<MutableSpan<T>> data(splines.size());
for (const int i : splines.index_range()) {
data[i] = spans[i].typed<T>();
BLI_assert(data[i].data() != nullptr);
}
attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
});
return attribute;
}
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final
{

View File

@@ -651,6 +651,207 @@ blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
return {};
}
namespace blender::bke::adapt_selection_domain {
static VArrayPtr<bool> varray_from_array(Array<bool> array)
{
return std::make_unique<VArray_For_ArrayContainer<Array<bool>>>(std::move(array));
}
static VArrayPtr<bool> adapt_selection_point_to_face(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totpoly);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
bool poly_is_selected = true;
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
if (!selection->get(loop.v)) {
poly_is_selected = false;
break;
}
}
new_selection[poly_index] = poly_is_selected;
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_point_to_corner(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totloop);
for (const int loop_index : IndexRange(mesh.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
new_selection[loop_index] = selection->get(loop.v);
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_point_to_edge(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totedge);
for (const int edge_index : IndexRange(mesh.totedge)) {
const MEdge &edge = mesh.medge[edge_index];
const bool edge_is_selected = selection->get(edge.v1) && selection->get(edge.v2);
new_selection[edge_index] = edge_is_selected;
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_edge_to_point(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totvert, false);
for (const int edge_index : IndexRange(mesh.totedge)) {
if (selection->get(edge_index)) {
const MEdge &edge = mesh.medge[edge_index];
new_selection[edge.v1] = true;
new_selection[edge.v2] = true;
}
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_edge_to_face(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totpoly);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
bool poly_is_selected = true;
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
if (!selection->get(loop.e)) {
poly_is_selected = false;
break;
}
}
new_selection[poly_index] = poly_is_selected;
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_face_to_point(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totvert, false);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
if (selection->get(poly_index)) {
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
BLI_assert(loop.v < mesh.totvert);
new_selection[loop.v] = true;
}
}
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_face_to_edge(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totedge, false);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
if (selection->get(poly_index)) {
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
new_selection[loop.e] = true;
}
}
}
return varray_from_array(std::move(new_selection));
}
static VArrayPtr<bool> adapt_selection_face_to_corner(const Mesh &mesh, VArrayPtr<bool> selection)
{
Array<bool> new_selection(mesh.totloop);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
const bool is_selected = selection->get(poly_index);
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
new_selection[loop_index] = is_selected;
}
}
return varray_from_array(std::move(new_selection));
}
} // namespace blender::bke::adapt_selection_domain
blender::VArrayPtr<bool> MeshComponent::adapt_selection(blender::VArrayPtr<bool> selection,
const AttributeDomain from_domain,
const AttributeDomain to_domain) const
{
using namespace blender::bke::adapt_selection_domain;
const int from_domain_size = this->attribute_domain_size(from_domain);
BLI_assert(selection->size() == from_domain_size);
if (from_domain == to_domain) {
return selection;
}
if (from_domain_size == 0) {
return selection;
}
if (selection->is_single()) {
return selection;
}
switch (from_domain) {
case ATTR_DOMAIN_CORNER: {
switch (to_domain) {
case ATTR_DOMAIN_POINT:
break;
case ATTR_DOMAIN_FACE:
break;
case ATTR_DOMAIN_EDGE:
break;
default:
break;
}
break;
}
case ATTR_DOMAIN_POINT: {
switch (to_domain) {
case ATTR_DOMAIN_CORNER:
return adapt_selection_point_to_corner(*mesh_, std::move(selection));
case ATTR_DOMAIN_FACE:
return adapt_selection_point_to_face(*mesh_, std::move(selection));
case ATTR_DOMAIN_EDGE:
return adapt_selection_point_to_edge(*mesh_, std::move(selection));
default:
break;
}
break;
}
case ATTR_DOMAIN_FACE: {
switch (to_domain) {
case ATTR_DOMAIN_POINT:
return adapt_selection_face_to_point(*mesh_, std::move(selection));
case ATTR_DOMAIN_CORNER:
return adapt_selection_face_to_corner(*mesh_, std::move(selection));
case ATTR_DOMAIN_EDGE:
return adapt_selection_face_to_edge(*mesh_, std::move(selection));
default:
break;
}
break;
}
case ATTR_DOMAIN_EDGE: {
switch (to_domain) {
case ATTR_DOMAIN_CORNER:
break;
case ATTR_DOMAIN_POINT:
return adapt_selection_edge_to_point(*mesh_, std::move(selection));
case ATTR_DOMAIN_FACE:
return adapt_selection_edge_to_face(*mesh_, std::move(selection));
default:
break;
}
break;
}
default:
break;
}
return {};
}
static Mesh *get_mesh_from_component_for_write(GeometryComponent &component)
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);

View File

@@ -18,6 +18,7 @@
#include "BKE_attribute.h"
#include "BKE_attribute_access.hh"
#include "BKE_field.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"

View File

@@ -620,7 +620,10 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const
}
if (new_count == 1) {
BKE_gpencil_free_stroke_weights(gps);
if (gps->dvert) {
BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
}
MEM_freeN(gps->points);
gps->points = nullptr;
gps->dvert = nullptr;
@@ -628,27 +631,24 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const
return false;
}
new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
for (int i = 0; i < new_count; i++) {
memcpy(&new_pt[i], &pt[i + index_from], sizeof(bGPDspoint));
}
new_pt = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed");
memcpy(new_pt, &pt[index_from], sizeof(bGPDspoint) * new_count);
if (gps->dvert) {
new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count,
new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count,
"gp_stroke_dverts_trimmed");
for (int i = 0; i < new_count; i++) {
dv = &gps->dvert[i + index_from];
new_dv[i].flag = dv->flag;
new_dv[i].totweight = dv->totweight;
new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
"gp_stroke_dverts_dw_trimmed");
for (int j = 0; j < dv->totweight; j++) {
new_dv[i].dw[j].weight = dv->dw[j].weight;
new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
}
BKE_defvert_clear(dv);
}
BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
gps->dvert = new_dv;
}
@@ -692,25 +692,21 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd,
gpf, gps, gps->mat_nr, new_count, gps->thickness);
new_pt = new_gps->points; /* Allocated from above. */
for (int i = 0; i < new_count; i++) {
memcpy(&new_pt[i], &pt[i + before_index], sizeof(bGPDspoint));
}
memcpy(new_pt, &pt[before_index], sizeof(bGPDspoint) * new_count);
if (gps->dvert) {
new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count,
new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count,
"gp_stroke_dverts_remaining(MDeformVert)");
for (int i = 0; i < new_count; i++) {
dv = &gps->dvert[i + before_index];
new_dv[i].flag = dv->flag;
new_dv[i].totweight = dv->totweight;
new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
new_dv[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
"gp_stroke_dverts_dw_remaining(MDeformWeight)");
for (int j = 0; j < dv->totweight; j++) {
new_dv[i].dw[j].weight = dv->dw[j].weight;
new_dv[i].dw[j].def_nr = dv->dw[j].def_nr;
}
BKE_defvert_clear(dv);
}
new_gps->dvert = new_dv;
}

View File

@@ -1481,7 +1481,7 @@ static bool id_name_final_build(char *name, char *base_name, size_t base_name_le
/* Code above may have generated invalid utf-8 string, due to raw truncation.
* Ensure we get a valid one now. */
base_name_len -= (size_t)BLI_utf8_invalid_strip(base_name, base_name_len);
base_name_len -= (size_t)BLI_str_utf8_invalid_strip(base_name, base_name_len);
/* Also truncate orig name, and start the whole check again. */
name[base_name_len] = '\0';
@@ -1731,7 +1731,7 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const boo
else {
/* disallow non utf8 chars,
* the interface checks for this but new ID's based on file names don't */
BLI_utf8_invalid_strip(name, strlen(name));
BLI_str_utf8_invalid_strip(name, strlen(name));
}
ID *id_sorting_hint = NULL;

View File

@@ -5115,13 +5115,16 @@ static void registerGeometryNodes()
{
register_node_type_geo_group();
register_node_type_geo_align_rotation_to_vector();
register_node_type_geo_extrude();
register_node_type_geo_attribute();
register_node_type_geo_attribute_clamp();
register_node_type_geo_attribute_color_ramp();
register_node_type_geo_attribute_combine_xyz();
register_node_type_geo_attribute_compare();
register_node_type_geo_attribute_convert();
register_node_type_geo_attribute_curve_map();
register_node_type_geo_attribute_extract();
register_node_type_geo_attribute_fill();
register_node_type_geo_attribute_map_range();
register_node_type_geo_attribute_math();
@@ -5139,6 +5142,7 @@ static void registerGeometryNodes()
register_node_type_geo_convex_hull();
register_node_type_geo_curve_endpoints();
register_node_type_geo_curve_length();
register_node_type_geo_curve_parameter();
register_node_type_geo_curve_primitive_bezier_segment();
register_node_type_geo_curve_primitive_circle();
register_node_type_geo_curve_primitive_line();
@@ -5156,6 +5160,9 @@ static void registerGeometryNodes()
register_node_type_geo_curve_trim();
register_node_type_geo_delete_geometry();
register_node_type_geo_edge_split();
register_node_type_geo_evaluate_curve();
register_node_type_geo_extrude_and_move();
register_node_type_geo_index();
register_node_type_geo_input_material();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
@@ -5171,6 +5178,7 @@ static void registerGeometryNodes()
register_node_type_geo_mesh_primitive_uv_sphere();
register_node_type_geo_mesh_subdivide();
register_node_type_geo_mesh_to_curve();
register_node_type_geo_normal();
register_node_type_geo_object_info();
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
@@ -5179,17 +5187,21 @@ static void registerGeometryNodes()
register_node_type_geo_point_separate();
register_node_type_geo_point_translate();
register_node_type_geo_points_to_volume();
register_node_type_geo_position();
register_node_type_geo_raycast();
register_node_type_geo_sample_mesh_surface();
register_node_type_geo_sample_texture();
register_node_type_geo_select_by_handle_type();
register_node_type_geo_select_by_material();
register_node_type_geo_separate_components();
register_node_type_geo_set_position();
register_node_type_geo_subdivision_surface();
register_node_type_geo_switch();
register_node_type_geo_transform();
register_node_type_geo_triangulate();
register_node_type_geo_viewer();
register_node_type_geo_volume_to_mesh();
register_node_type_geo_attribute_freeze();
}
static void registerFunctionNodes()
@@ -5199,6 +5211,7 @@ static void registerFunctionNodes()
register_node_type_fn_float_to_int();
register_node_type_fn_input_string();
register_node_type_fn_input_vector();
register_node_type_fn_align_rotation_to_vector();
register_node_type_fn_random_float();
}

View File

@@ -123,7 +123,7 @@ int Spline::evaluated_edges_size() const
float Spline::length() const
{
Span<float> lengths = this->evaluated_lengths();
return (lengths.size() == 0) ? 0 : this->evaluated_lengths().last();
return lengths.is_empty() ? 0.0f : this->evaluated_lengths().last();
}
int Spline::segments_size() const

View File

@@ -308,7 +308,7 @@ int txt_extended_ascii_as_utf8(char **str)
int added = 0;
while ((*str)[i]) {
if ((bad_char = BLI_utf8_invalid_byte(*str + i, length - i)) == -1) {
if ((bad_char = BLI_str_utf8_invalid_byte(*str + i, length - i)) == -1) {
break;
}
@@ -322,7 +322,7 @@ int txt_extended_ascii_as_utf8(char **str)
i = 0;
while ((*str)[i]) {
if ((bad_char = BLI_utf8_invalid_byte((*str) + i, length - i)) == -1) {
if ((bad_char = BLI_str_utf8_invalid_byte((*str) + i, length - i)) == -1) {
memcpy(newstr + mi, (*str) + i, length - i + 1);
break;
}

View File

@@ -250,6 +250,20 @@ template<typename T> struct DefaultHash<std::unique_ptr<T>> {
}
};
template<typename T> struct DefaultHash<std::shared_ptr<T>> {
uint64_t operator()(const std::shared_ptr<T> &value) const
{
return get_default_hash(value.get());
}
};
template<typename T> struct DefaultHash<std::reference_wrapper<T>> {
uint64_t operator()(const std::reference_wrapper<T> &value) const
{
return get_default_hash(value.get());
}
};
template<typename T1, typename T2> struct DefaultHash<std::pair<T1, T2>> {
uint64_t operator()(const std::pair<T1, T2> &value) const
{

View File

@@ -196,6 +196,8 @@ MINLINE unsigned int log2_ceil_u(unsigned int x);
MINLINE int divide_round_i(int a, int b);
MINLINE int mod_i(int i, int n);
MINLINE float round_to_even(float f);
MINLINE signed char round_fl_to_char(float a);
MINLINE unsigned char round_fl_to_uchar(float a);
MINLINE short round_fl_to_short(float a);

View File

@@ -0,0 +1,88 @@
/*
* 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.
*/
#pragma once
/** \file
* \ingroup bli
*/
#include <memory>
#include "BLI_utildefines.h"
namespace blender {
template<typename T, typename OwnedTPtr = std::unique_ptr<T>> class optional_ptr {
private:
OwnedTPtr owned_ptr_;
T *ptr_ = nullptr;
public:
optional_ptr() = default;
optional_ptr(T &ptr) : ptr_(&ptr)
{
}
optional_ptr(OwnedTPtr owned_ptr) : owned_ptr_(std::move(owned_ptr)), ptr_(&*owned_ptr_)
{
}
bool is_owned() const
{
return static_cast<bool>(owned_ptr_);
}
OwnedTPtr extract_owned()
{
OwnedTPtr ptr = std::move(owned_ptr_);
owned_ptr_ = OwnedTPtr{nullptr};
ptr_ = nullptr;
return ptr;
}
T *operator->()
{
BLI_assert(ptr_ != nullptr);
return ptr_;
}
const T *operator->() const
{
BLI_assert(ptr_ != nullptr);
return ptr_;
}
T &operator*()
{
BLI_assert(ptr_ != nullptr);
return *ptr_;
}
const T &operator*() const
{
BLI_assert(ptr_ != nullptr);
return *ptr_;
}
operator bool() const
{
return ptr_ != nullptr;
}
};
} // namespace blender

View File

@@ -478,8 +478,8 @@ template<typename T> class MutableSpan {
using size_type = int64_t;
protected:
T *data_;
int64_t size_;
T *data_ = nullptr;
int64_t size_ = 0;
public:
constexpr MutableSpan() = default;

View File

@@ -31,8 +31,8 @@ char *BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t
ATTR_NONNULL();
size_t BLI_strncpy_utf8_rlen(char *__restrict dst, const char *__restrict src, size_t maxncpy)
ATTR_NONNULL();
ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length) ATTR_NONNULL();
int BLI_utf8_invalid_strip(char *str, size_t length) ATTR_NONNULL();
ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length) ATTR_NONNULL();
int BLI_str_utf8_invalid_strip(char *str, size_t length) ATTR_NONNULL();
/* warning, can return -1 on bad chars */
int BLI_str_utf8_size(const char *p) ATTR_NONNULL();

View File

@@ -84,12 +84,24 @@ template<typename T> class UserCounter {
return data_;
}
const T *operator->() const
{
BLI_assert(data_ != nullptr);
return data_;
}
T &operator*()
{
BLI_assert(data_ != nullptr);
return *data_;
}
const T &operator*() const
{
BLI_assert(data_ != nullptr);
return *data_;
}
operator bool() const
{
return data_ != nullptr;

View File

@@ -255,7 +255,7 @@ template<typename T> class VMutableArray : public VArray<T> {
}
};
template<typename T> using VArrayPtr = std::unique_ptr<VArray<T>>;
template<typename T> using VArrayPtr = std::unique_ptr<const VArray<T>>;
template<typename T> using VMutableArrayPtr = std::unique_ptr<VMutableArray<T>>;
/**

View File

@@ -302,7 +302,7 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf)
/* Get the name. */
if (face->family_name) {
BLI_snprintf(vfd->name, sizeof(vfd->name), "%s %s", face->family_name, face->style_name);
BLI_utf8_invalid_strip(vfd->name, strlen(vfd->name));
BLI_str_utf8_invalid_strip(vfd->name, strlen(vfd->name));
}
/* Select a character map. */

View File

@@ -363,6 +363,14 @@ MINLINE signed char round_db_to_char_clamp(double a){
#undef _round_clamp_fl_impl
#undef _round_clamp_db_impl
/**
* Round to closest even number, halfway cases are rounded away from zero.
*/
MINLINE float round_to_even(float f)
{
return roundf(f * 0.5f) * 2.0f;
}
/* integer division that rounds 0.5 up, particularly useful for color blending
* with integers, to avoid gradual darkening when rounding down */
MINLINE int divide_round_i(int a, int b)

View File

@@ -70,7 +70,7 @@ static const size_t utf8_skip_data[256] = {
*
* \return the offset of the first invalid byte.
*/
ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length)
ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length)
{
const unsigned char *p, *perr, *pend = (const unsigned char *)str + length;
unsigned char c;
@@ -200,14 +200,14 @@ utf8_error:
*
* \return number of stripped bytes.
*/
int BLI_utf8_invalid_strip(char *str, size_t length)
int BLI_str_utf8_invalid_strip(char *str, size_t length)
{
ptrdiff_t bad_char;
int tot = 0;
BLI_assert(str[length] == '\0');
while ((bad_char = BLI_utf8_invalid_byte(str, length)) != -1) {
while ((bad_char = BLI_str_utf8_invalid_byte(str, length)) != -1) {
str += bad_char;
length -= (size_t)(bad_char + 1);

View File

@@ -653,6 +653,18 @@ TEST(map, LookupKey)
EXPECT_EQ(map.lookup_key_ptr("a"), map.lookup_key_ptr_as("a"));
}
TEST(map, ReferenceWrapperKey)
{
Map<std::reference_wrapper<int>, int> map;
int a = 2;
int b = 5;
map.add(a, 10);
map.add(a, 20);
map.add(b, 20);
EXPECT_EQ(map.lookup(a), 10);
EXPECT_EQ(map.lookup(b), 20);
}
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/

View File

@@ -266,7 +266,7 @@ static const char *utf8_invalid_tests[][3] = {
};
/* clang-format on */
/* BLI_utf8_invalid_strip (and indirectly, BLI_utf8_invalid_byte). */
/* BLI_str_utf8_invalid_strip (and indirectly, BLI_str_utf8_invalid_byte). */
TEST(string, Utf8InvalidBytes)
{
for (int i = 0; utf8_invalid_tests[i][0] != nullptr; i++) {
@@ -277,7 +277,7 @@ TEST(string, Utf8InvalidBytes)
char buff[80];
memcpy(buff, tst, sizeof(buff));
const int num_errors_found = BLI_utf8_invalid_strip(buff, sizeof(buff) - 1);
const int num_errors_found = BLI_str_utf8_invalid_strip(buff, sizeof(buff) - 1);
printf("[%02d] -> [%02d] \"%s\" -> \"%s\"\n", num_errors, num_errors_found, tst, buff);
EXPECT_EQ(num_errors_found, num_errors);

View File

@@ -1446,4 +1446,141 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
}
}
/**
* Use to select bmesh vertex data based on an array of bool.
*/
void BM_select_vertices(BMesh *bm, const bool *mask)
{
BMIter iter;
BMVert *v;
int i = 0;
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (mask[i]) {
BM_elem_flag_set(v, BM_ELEM_SELECT, true);
}
else {
BM_elem_flag_set(v, BM_ELEM_SELECT, false);
}
i++;
}
}
/**
* Use to select bmesh edge data based on an array of bool.
*/
void BM_select_edges(BMesh *bm, const bool *mask)
{
BMIter iter;
BMEdge *e;
int i = 0;
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (mask[i]) {
BM_elem_flag_set(e, BM_ELEM_SELECT, true);
}
else {
BM_elem_flag_set(e, BM_ELEM_SELECT, false);
}
i++;
}
}
/**
* Use to select bmesh face data based on an array of bool.
*/
void BM_select_faces(BMesh *bm, const bool *mask)
{
BMIter iter;
BMFace *f;
int i = 0;
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
BM_elem_flag_set(f, BM_ELEM_SELECT, mask[i]);
}
}
void BM_tag_vertices(BMesh *bm, const bool *mask)
{
BMIter iter;
BMVert *v;
int i = 0;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
BM_elem_flag_set(v, BM_ELEM_TAG, mask[i]);
}
}
/**
* Use to temporary tag bmesh edge data based on an array of bool.
*/
void BM_tag_edges(BMesh *bm, const bool *mask)
{
BMIter iter;
BMEdge *e;
int i = 0;
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
BM_elem_flag_set(e, BM_ELEM_TAG, mask[i]);
}
}
/**
* Use to temporary tag bmesh face data based on an array of bool.
*/
void BM_tag_faces(BMesh *bm, const bool *mask)
{
BMIter iter;
BMFace *f;
int i = 0;
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
BM_elem_flag_set(f, BM_ELEM_TAG, mask[i]);
}
}
void BM_get_tagged_faces(BMesh *bm, bool *selection)
{
BMIter iter;
BMFace *f;
int i = 0;
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
selection[i] = BM_elem_flag_test(f, BM_ELEM_TAG);
}
}
void BM_tag_new_faces(BMesh *bm, BMOperator *b_mesh_operator)
{
BMOIter iter;
BMFace *f;
BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false);
BMO_ITER (f, &iter, b_mesh_operator->slots_out, "faces.out", BM_FACE) {
BM_elem_flag_enable(f, BM_ELEM_TAG);
}
}
void BM_get_selected_faces(BMesh *bm, bool *selection)
{
BMIter iter;
BMFace *f;
int i = 0;
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
selection[i] = BM_elem_flag_test(f, BM_ELEM_SELECT);
}
}
void BM_get_selected_edges(BMesh *bm, bool *selection)
{
BMIter iter;
BMEdge *e;
int i = 0;
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
selection[i] = BM_elem_flag_test(e, BM_ELEM_SELECT);
}
}
void BM_get_selected_vertices(BMesh *bm, bool *selection)
{
BMIter iter;
BMVert *v;
int i = 0;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
selection[i] = BM_elem_flag_test(v, BM_ELEM_SELECT);
}
}
/** \} */

View File

@@ -134,3 +134,16 @@ void BM_mesh_vert_coords_apply(BMesh *bm, const float (*vert_coords)[3]);
void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
const float (*vert_coords)[3],
const float mat[4][4]);
void BM_select_vertices(BMesh *bm, const bool *mask);
void BM_select_edges(BMesh *bm, const bool *mask);
void BM_select_faces(BMesh *bm, const bool *mask);
void BM_tag_vertices(BMesh *bm, const bool *mask);
void BM_tag_edges(BMesh *bm, const bool *mask);
void BM_tag_faces(BMesh *bm, const bool *mask);
void BM_get_tagged_faces(BMesh *bm, bool *selection);
void BM_get_selected_faces(BMesh *bm, bool *selection);
void BM_get_selected_edges(BMesh *bm, bool *selection);
void BM_get_selected_vertices(BMesh *bm, bool *selection);
void BM_tag_new_faces(BMesh *bm, BMOperator *b_mesh_operator);

View File

@@ -1898,6 +1898,9 @@ static BMOpDefine bmo_inset_individual_def = {
{{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* input faces */
{"thickness", BMO_OP_SLOT_FLT}, /* thickness */
{"depth", BMO_OP_SLOT_FLT}, /* depth */
{"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */
{"depth_array", BMO_OP_SLOT_PTR}, /* depth */
{"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */
{"use_even_offset", BMO_OP_SLOT_BOOL}, /* scale the offset to give more even thickness */
{"use_interpolate", BMO_OP_SLOT_BOOL}, /* blend face data across the inset */
{"use_relative_offset", BMO_OP_SLOT_BOOL}, /* scale the offset by surrounding geometry */
@@ -1929,6 +1932,9 @@ static BMOpDefine bmo_inset_region_def = {
{"use_edge_rail", BMO_OP_SLOT_BOOL}, /* inset the region along existing edges */
{"thickness", BMO_OP_SLOT_FLT}, /* thickness */
{"depth", BMO_OP_SLOT_FLT}, /* depth */
{"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */
{"depth_array", BMO_OP_SLOT_PTR}, /* depth */
{"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */
{"use_outset", BMO_OP_SLOT_BOOL}, /* outset rather than inset */
{{'\0'}},
},
@@ -1940,7 +1946,6 @@ static BMOpDefine bmo_inset_region_def = {
(BMO_OPTYPE_FLAG_NORMALS_CALC |
BMO_OPTYPE_FLAG_SELECT_FLUSH),
};
/*
* Edge-loop Offset.
*

View File

@@ -471,6 +471,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op)
for (e = BMO_iter_new(&siter, dupeop.slots_out, "boundary_map.out", 0); e;
e = BMO_iter_step(&siter)) {
BMVert *f_verts[4];
#ifdef USE_EDGE_REGION_FLAGS
BMEdge *f_edges[4];
#endif

View File

@@ -419,6 +419,9 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
BMOIter oiter;
MemArena *interp_arena = NULL;
const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes");
const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array");
const float *depth_array = BMO_slot_ptr_get(op->slots_in, "depth_array");
const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
const float depth = BMO_slot_float_get(op->slots_in, "depth");
const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
@@ -433,19 +436,37 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op)
if (use_interpolate) {
interp_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
}
int i = 0;
if (use_attributes) {
BMO_ITER_INDEX (f, &oiter, op->slots_in, "faces", BM_FACE, i) {
bmo_face_inset_individual(bm,
f,
interp_arena,
thickness_array[i],
depth_array[i],
use_even_offset,
use_relative_offset,
use_interpolate);
BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
bmo_face_inset_individual(bm,
f,
interp_arena,
thickness,
depth,
use_even_offset,
use_relative_offset,
use_interpolate);
if (use_interpolate) {
BLI_memarena_clear(interp_arena);
}
}
}
else {
BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
bmo_face_inset_individual(bm,
f,
interp_arena,
thickness,
depth,
use_even_offset,
use_relative_offset,
use_interpolate);
if (use_interpolate) {
BLI_memarena_clear(interp_arena);
if (use_interpolate) {
BLI_memarena_clear(interp_arena);
}
}
}
@@ -677,12 +698,16 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
const bool use_outset = BMO_slot_bool_get(op->slots_in, "use_outset");
const bool use_boundary = BMO_slot_bool_get(op->slots_in, "use_boundary") &&
(use_outset == false);
const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset");
const bool use_even_boundary = use_even_offset; /* could make own option */
const bool use_relative_offset = BMO_slot_bool_get(op->slots_in, "use_relative_offset");
const bool use_edge_rail = BMO_slot_bool_get(op->slots_in, "use_edge_rail");
const bool use_interpolate = BMO_slot_bool_get(op->slots_in, "use_interpolate");
const float thickness = BMO_slot_float_get(op->slots_in, "thickness");
const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes");
const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array");
// const float *depth_array = BMO_slot_ptr_get(op->slots_in, "depth_array");
const float depth = BMO_slot_float_get(op->slots_in, "depth");
#ifdef USE_LOOP_CUSTOMDATA_MERGE
const bool has_math_ldata = (use_interpolate && CustomData_has_math(&bm->ldata));
@@ -1096,7 +1121,12 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op)
}
/* apply the offset */
madd_v3_v3fl(v_split->co, tvec, thickness);
if (use_attributes) {
madd_v3_v3fl(v_split->co, tvec, thickness_array[v_split->head.index]);
}
else {
madd_v3_v3fl(v_split->co, tvec, thickness);
}
}
/* this saves expensive/slow glue check for common cases */

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,8 @@ 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_SINGLE_ELEM_AREA = {0, 1, 0, 1};
constexpr IndexRange XRange(const rcti &area)
{
return IndexRange(area.xmin, area.xmax - area.xmin);

View File

@@ -239,6 +239,12 @@ class CompositorContext {
this->m_hasActiveOpenCLDevices = hasAvtiveOpenCLDevices;
}
/** Whether it has a view with a specific name and not the default one. */
bool has_explicit_view() const
{
return m_viewName && m_viewName[0] != '\0';
}
/**
* \brief get the active rendering view
*/

View File

@@ -245,7 +245,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 +255,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 +274,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 +284,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 +356,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 +424,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

@@ -191,23 +191,96 @@ class MemoryBuffer {
void read_elem(int x, int y, float *out) const
{
memcpy(out, get_elem(x, y), m_num_channels * sizeof(float));
memcpy(out, get_elem(x, y), get_elem_bytes_len());
}
void read_elem_checked(int x, int y, float *out) const
{
if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
clear_elem(out);
}
else {
read_elem(x, y, out);
}
}
void read_elem_checked(float x, float y, float *out) const
{
if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
clear_elem(out);
}
else {
read_elem(x, 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.
*/
@@ -403,6 +476,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 +500,7 @@ class MemoryBuffer {
}
}
/* TODO(manzanilla): to be removed with tiled implementation. */
inline void readNoCheck(float *result,
int x,
int y,
@@ -507,12 +583,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 +660,21 @@ 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;
}
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

@@ -82,8 +82,12 @@ void NodeOperation::determineResolution(unsigned int resolution[2],
input.determineResolution(resolution, preferredResolution);
used_resolution_index = m_resolutionInputSocketIndex;
}
unsigned int temp2[2] = {resolution[0], resolution[1]};
if (modify_determined_resolution_fn_) {
modify_determined_resolution_fn_(resolution);
}
unsigned int temp2[2] = {resolution[0], resolution[1]};
unsigned int temp[2];
for (unsigned int index = 0; index < m_inputs.size(); index++) {
if (index == used_resolution_index) {

View File

@@ -287,6 +287,8 @@ class NodeOperation {
*/
unsigned int m_resolutionInputSocketIndex;
std::function<void(unsigned int resolution[2])> modify_determined_resolution_fn_;
/**
* \brief mutex reference for very special node initializations
* \note only use when you really know what you are doing.
@@ -517,6 +519,15 @@ class NodeOperation {
*/
void setResolutionInputSocketIndex(unsigned int index);
/**
* Set a custom function to modify determined resolution from main input just before setting it
* as preferred resolution for the other inputs.
*/
void set_determined_resolution_modifier(std::function<void(unsigned int resolution[2])> fn)
{
modify_determined_resolution_fn_ = fn;
}
/**
* \brief get the render priority of this node.
* \note only applicable for output operations like ViewerOperation

View File

@@ -124,6 +124,10 @@ void CryptomatteNode::input_operations_from_render_source(
RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name);
if (render_layer) {
LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) {
if (context.has_explicit_view() && !STREQ(render_pass->view, context.getViewName())) {
continue;
}
const std::string combined_name = combined_layer_pass_name(render_layer, render_pass);
if (blender::StringRef(combined_name).startswith(prefix)) {
RenderLayersProg *op = new RenderLayersProg(

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

@@ -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

@@ -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,82 @@ 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;
}
void BlurBaseOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
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;
if (!m_extend_bounds) {
NodeOperation::determineResolution(resolution, preferredResolution);
return;
}
switch (execution_model_) {
case eExecutionModel::Tiled: {
NodeOperation::determineResolution(resolution, preferredResolution);
resolution[0] += 2 * m_size * m_data.sizex;
resolution[1] += 2 * m_size * m_data.sizey;
break;
}
case eExecutionModel::FullFrame: {
/* Setting a modifier ensures all non main inputs have extended bounds as preferred
* resolution, avoiding unnecessary resolution convertions that would hide constant
* operations. */
set_determined_resolution_modifier([=](unsigned int res[2]) {
/* Rounding to even prevents jiggling in backdrop while switching size values. */
res[0] += round_to_even(2 * m_size * m_data.sizex);
res[1] += round_to_even(2 * m_size * m_data.sizey);
});
NodeOperation::determineResolution(resolution, preferredResolution);
break;
}
}
}
void BlurBaseOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
switch (input_idx) {
case 0:
r_input_area = output_area;
break;
case 1:
r_input_area = use_variable_size_ ? output_area : COM_SINGLE_ELEM_AREA;
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,14 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
this->m_extend_bounds = extend_bounds;
}
int get_blur_size(eDimension dim) const;
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) 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,6 +26,11 @@
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);
@@ -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,146 @@ 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;
}
void BokehBlurOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
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;
if (!m_extend_bounds) {
NodeOperation::determineResolution(resolution, preferredResolution);
return;
}
switch (execution_model_) {
case eExecutionModel::Tiled: {
NodeOperation::determineResolution(resolution, preferredResolution);
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;
break;
}
case eExecutionModel::FullFrame: {
set_determined_resolution_modifier([=](unsigned int res[2]) {
const float max_dim = MAX2(res[0], res[1]);
/* 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);
res[0] += add_size;
res[1] += add_size;
});
NodeOperation::determineResolution(resolution, preferredResolution);
break;
}
}
}
void BokehBlurOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
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.xmin = 0;
r_input_area.xmax = bokeh_input->getWidth();
r_input_area.ymin = 0;
r_input_area.ymax = bokeh_input->getHeight();
break;
}
case BOUNDING_BOX_INPUT_INDEX:
r_input_area = output_area;
break;
case SIZE_INPUT_INDEX: {
r_input_area = COM_SINGLE_ELEM_AREA;
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.
@@ -79,6 +82,11 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) 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

@@ -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

@@ -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

@@ -29,6 +29,7 @@ ColorRampOperation::ColorRampOperation()
this->m_inputProgram = nullptr;
this->m_colorBand = nullptr;
this->flags.can_be_constant = true;
}
void ColorRampOperation::initExecution()
{
@@ -51,4 +52,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

@@ -27,6 +27,7 @@ namespace blender::compositor {
ConvertBaseOperation::ConvertBaseOperation()
{
this->m_inputOperation = nullptr;
this->flags.can_be_constant = true;
}
void ConvertBaseOperation::initExecution()
@@ -39,6 +40,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 +67,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 +93,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 +119,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 +144,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 +169,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 +193,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 +219,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 +269,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()
@@ -253,6 +327,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 +368,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 +402,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 +430,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 +461,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 +492,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 +518,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 +553,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()
@@ -466,4 +618,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,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,6 +130,18 @@ bool CropImageOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
void CropImageOperation::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.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::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
@@ -136,4 +164,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 {
@@ -65,6 +69,11 @@ class CropImageOperation : public CropBaseOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) 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

@@ -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

@@ -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,61 @@ 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.xmin = 0;
r_input_area.ymin = 0;
r_input_area.xmax = getInputOperation(input_idx)->getWidth();
r_input_area.ymax = getInputOperation(input_idx)->getHeight();
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,56 @@ 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.xmin = 0;
r_input_area.ymin = 0;
r_input_area.xmax = getInputOperation(input_idx)->getWidth();
r_input_area.ymax = getInputOperation(input_idx)->getHeight();
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

@@ -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,64 @@ 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.xmin = 0;
r_input_area.xmax = getWidth();
r_input_area.ymin = 0;
r_input_area.ymax = getHeight();
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 +407,44 @@ 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.xmin = 0;
r_input_area.xmax = getWidth();
r_input_area.ymin = 0;
r_input_area.ymax = getHeight();
}
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

@@ -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

View File

@@ -0,0 +1,62 @@
/*
* 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.
*/
#pragma once
#include "COM_BlurBaseOperation.h"
namespace blender::compositor {
class GaussianAlphaBlurBaseOperation : public BlurBaseOperation {
protected:
float *m_gausstab;
float *m_distbuf_inv;
int m_falloff; /* Falloff for #distbuf_inv. */
bool m_do_subtract;
int m_filtersize;
float rad_;
eDimension dimension_;
public:
GaussianAlphaBlurBaseOperation(eDimension dim);
virtual void init_data() override;
virtual void initExecution() override;
virtual void deinitExecution() override;
void get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area) final;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) final;
/**
* Set subtract for Dilate/Erode functionality
*/
void setSubtract(bool subtract)
{
this->m_do_subtract = subtract;
}
void setFalloff(int falloff)
{
this->m_falloff = falloff;
}
};
} // namespace blender::compositor

View File

@@ -24,11 +24,9 @@
namespace blender::compositor {
GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation() : BlurBaseOperation(DataType::Value)
GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation()
: GaussianAlphaBlurBaseOperation(eDimension::X)
{
this->m_gausstab = nullptr;
this->m_filtersize = 0;
this->m_falloff = -1; /* intentionally invalid, so we can detect uninitialized values */
}
void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/)
@@ -44,12 +42,11 @@ void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/)
void GaussianAlphaXBlurOperation::initExecution()
{
/* Until we support size input - comment this. */
// BlurBaseOperation::initExecution();
GaussianAlphaBlurBaseOperation::initExecution();
initMutex();
if (this->m_sizeavailable) {
if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) {
float rad = max_ff(m_size * m_data.sizex, 0.0f);
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
@@ -144,7 +141,7 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo
void GaussianAlphaXBlurOperation::deinitExecution()
{
BlurBaseOperation::deinitExecution();
GaussianAlphaBlurBaseOperation::deinitExecution();
if (this->m_gausstab) {
MEM_freeN(this->m_gausstab);

View File

@@ -18,18 +18,13 @@
#pragma once
#include "COM_BlurBaseOperation.h"
#include "COM_NodeOperation.h"
#include "COM_GaussianAlphaBlurBaseOperation.h"
namespace blender::compositor {
class GaussianAlphaXBlurOperation : public BlurBaseOperation {
/* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */
class GaussianAlphaXBlurOperation : public GaussianAlphaBlurBaseOperation {
private:
float *m_gausstab;
float *m_distbuf_inv;
int m_falloff; /* falloff for distbuf_inv */
bool m_do_subtract;
int m_filtersize;
void updateGauss();
public:
@@ -54,18 +49,6 @@ class GaussianAlphaXBlurOperation : public BlurBaseOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
/**
* Set subtract for Dilate/Erode functionality
*/
void setSubtract(bool subtract)
{
this->m_do_subtract = subtract;
}
void setFalloff(int falloff)
{
this->m_falloff = falloff;
}
};
} // namespace blender::compositor

View File

@@ -24,11 +24,9 @@
namespace blender::compositor {
GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() : BlurBaseOperation(DataType::Value)
GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation()
: GaussianAlphaBlurBaseOperation(eDimension::Y)
{
this->m_gausstab = nullptr;
this->m_filtersize = 0;
this->m_falloff = -1; /* intentionally invalid, so we can detect uninitialized values */
}
void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/)
@@ -42,14 +40,14 @@ void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/)
return buffer;
}
/* TODO(manzanilla): to be removed with tiled implementation. */
void GaussianAlphaYBlurOperation::initExecution()
{
/* Until we support size input - comment this. */
// BlurBaseOperation::initExecution();
GaussianAlphaBlurBaseOperation::initExecution();
initMutex();
if (this->m_sizeavailable) {
if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) {
float rad = max_ff(m_size * m_data.sizey, 0.0f);
m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS);
@@ -58,6 +56,7 @@ void GaussianAlphaYBlurOperation::initExecution()
}
}
/* TODO(manzanilla): to be removed with tiled implementation. */
void GaussianAlphaYBlurOperation::updateGauss()
{
if (this->m_gausstab == nullptr) {
@@ -143,7 +142,7 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo
void GaussianAlphaYBlurOperation::deinitExecution()
{
BlurBaseOperation::deinitExecution();
GaussianAlphaBlurBaseOperation::deinitExecution();
if (this->m_gausstab) {
MEM_freeN(this->m_gausstab);

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