Blender V4.3
cryptomatte.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BKE_cryptomatte.h"
10#include "BKE_cryptomatte.hh"
11#include "BKE_image.hh"
12#include "BKE_layer.hh"
13#include "BKE_main.hh"
14#include "BKE_material.h"
15
16#include "DNA_layer_types.h"
17#include "DNA_material_types.h"
18#include "DNA_node_types.h"
19#include "DNA_object_types.h"
20#include "DNA_scene_types.h"
21
22#include "BLI_compiler_attrs.h"
23#include "BLI_dynstr.h"
24#include "BLI_hash_mm3.hh"
25#include "BLI_listbase.h"
26#include "BLI_string.h"
27
28#include "RE_pipeline.h"
29
30#include "MEM_guardedalloc.h"
31
32#include <cctype>
33#include <cstring>
34#include <iomanip>
35#include <sstream>
36#include <string>
37#include <string_view>
38
41 /* Layer names in order of creation. */
43
44 CryptomatteSession() = default;
45 CryptomatteSession(const Main *bmain);
46 CryptomatteSession(StampData *stamp_data);
47 CryptomatteSession(const ViewLayer *view_layer);
48 CryptomatteSession(const Scene *scene, bool build_meta_data = false);
49 void init(const ViewLayer *view_layer, bool build_meta_data = false);
50
52 std::optional<std::string> operator[](float encoded_hash) const;
53
54#ifdef WITH_CXX_GUARDEDALLOC
55 MEM_CXX_CLASS_ALLOC_FUNCS("cryptomatte:CryptomatteSession")
56#endif
57};
58
60{
61 if (!BLI_listbase_is_empty(&bmain->objects)) {
64 LISTBASE_FOREACH (ID *, id, &bmain->objects) {
65 objects.add_ID(*id);
66 }
67 }
68 if (!BLI_listbase_is_empty(&bmain->materials)) {
71 LISTBASE_FOREACH (ID *, id, &bmain->materials) {
72 materials.add_ID(*id);
73 }
74 }
75}
76
92
94{
95 init(view_layer);
96}
97
98CryptomatteSession::CryptomatteSession(const Scene *scene, bool build_meta_data)
99{
100
101 if (build_meta_data) {
103 }
104
105 LISTBASE_FOREACH (const ViewLayer *, view_layer, &scene->view_layers) {
106 init(view_layer, build_meta_data);
107 }
108}
109
110void CryptomatteSession::init(const ViewLayer *view_layer, bool build_meta_data)
111{
114 if (cryptoflags == 0) {
115 cryptoflags = static_cast<eViewLayerCryptomatteFlags>(VIEW_LAYER_CRYPTOMATTE_ALL);
116 }
117
118 ListBase *object_bases = BKE_view_layer_object_bases_get(const_cast<ViewLayer *>(view_layer));
119
120 if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_OBJECT) {
123
124 if (build_meta_data) {
125 LISTBASE_FOREACH (Base *, base, object_bases) {
126 objects.add_ID(base->object->id);
127 }
128 }
129 }
130
131 if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_ASSET) {
133 }
134
135 if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_MATERIAL) {
138
139 if (build_meta_data) {
140 LISTBASE_FOREACH (Base *, base, object_bases) {
141 for (int i = 0; i < base->object->totcol; i++) {
142 Material *material = BKE_object_material_get(base->object, i + 1);
143 if (material) {
144 materials.add_ID(material->id);
145 }
146 }
147 }
148 }
149 }
150}
151
153{
154 if (!layer_names.contains(layer_name)) {
155 layer_names.append(layer_name);
156 }
157 return layers.lookup_or_add_default(layer_name);
158}
159
160std::optional<std::string> CryptomatteSession::operator[](float encoded_hash) const
161{
162 for (const blender::bke::cryptomatte::CryptomatteLayer &layer : layers.values()) {
163 std::optional<std::string> result = layer[encoded_hash];
164 if (result) {
165 return result;
166 }
167 }
168 return std::nullopt;
169}
170
172{
173 CryptomatteSession *session = new CryptomatteSession();
174 return session;
175}
176
178{
179 CryptomatteSession *session = new CryptomatteSession(render_result->stamp_data);
180 return session;
181}
182
183CryptomatteSession *BKE_cryptomatte_init_from_scene(const Scene *scene, bool build_meta_data)
184{
185 CryptomatteSession *session = new CryptomatteSession(scene, build_meta_data);
186 return session;
187}
188
190{
191 CryptomatteSession *session = new CryptomatteSession(view_layer);
192 return session;
193}
194
195void BKE_cryptomatte_add_layer(CryptomatteSession *session, const char *layer_name)
196{
197 session->add_layer(layer_name);
198}
199
201{
202 BLI_assert(session != nullptr);
203 delete session;
204}
205
206uint32_t BKE_cryptomatte_hash(const char *name, const int name_len)
207{
209 return hash.hash;
210}
211
213 const char *layer_name,
214 const Object *object)
215{
217 BLI_assert(layer);
218 return layer->add_ID(object->id);
219}
220
222 const char *layer_name,
223 const Material *material)
224{
225 if (material == nullptr) {
226 return 0.0f;
227 }
229 BLI_assert(layer);
230 return layer->add_ID(material->id);
231}
232
234 const char *layer_name,
235 const Object *object)
236{
237 const Object *asset_object = object;
238 while (asset_object->parent != nullptr) {
239 asset_object = asset_object->parent;
240 }
241 return BKE_cryptomatte_object_hash(session, layer_name, asset_object);
242}
243
245{
247}
248
250 const float encoded_hash,
251 char *r_name,
252 int name_maxncpy)
253{
254 std::optional<std::string> name = (*session)[encoded_hash];
255 if (!name) {
256 return false;
257 }
258
259 BLI_strncpy(r_name, name->c_str(), name_maxncpy);
260 return true;
261}
262
264{
265 DynStr *matte_id = BLI_dynstr_new();
266 bool first = true;
267 LISTBASE_FOREACH (CryptomatteEntry *, entry, &node_storage->entries) {
268 if (!first) {
269 BLI_dynstr_append(matte_id, ",");
270 }
271 if (BLI_strnlen(entry->name, sizeof(entry->name)) != 0) {
272 BLI_dynstr_nappend(matte_id, entry->name, sizeof(entry->name));
273 }
274 else {
275 BLI_dynstr_appendf(matte_id, "<%.9g>", entry->encoded_hash);
276 }
277 first = false;
278 }
279 char *result = BLI_dynstr_get_cstring(matte_id);
280 BLI_dynstr_free(matte_id);
281 return result;
282}
283
284void BKE_cryptomatte_matte_id_to_entries(NodeCryptomatte *node_storage, const char *matte_id)
285{
286 BLI_freelistN(&node_storage->entries);
287
288 if (matte_id == nullptr) {
289 MEM_SAFE_FREE(node_storage->matte_id);
290 return;
291 }
292 /* Update the matte_id so the files can be opened in versions that don't
293 * use `CryptomatteEntry`. */
294 if (matte_id != node_storage->matte_id && node_storage->matte_id &&
295 STREQ(node_storage->matte_id, matte_id))
296 {
297 MEM_SAFE_FREE(node_storage->matte_id);
298 node_storage->matte_id = static_cast<char *>(MEM_dupallocN(matte_id));
299 }
300
301 std::istringstream ss(matte_id);
302 while (ss.good()) {
303 CryptomatteEntry *entry = nullptr;
304 std::string token;
305 getline(ss, token, ',');
306 /* Ignore empty tokens. */
307 if (token.length() > 0) {
308 size_t first = token.find_first_not_of(' ');
309 size_t last = token.find_last_not_of(' ');
310 if (first == std::string::npos || last == std::string::npos) {
311 break;
312 }
313 token = token.substr(first, (last - first + 1));
314 if (*token.begin() == '<' && *(--token.end()) == '>') {
315 float encoded_hash = atof(token.substr(1, token.length() - 2).c_str());
316 entry = MEM_cnew<CryptomatteEntry>(__func__);
317 entry->encoded_hash = encoded_hash;
318 }
319 else {
320 const char *name = token.c_str();
321 int name_len = token.length();
322 entry = MEM_cnew<CryptomatteEntry>(__func__);
323 STRNCPY(entry->name, name);
324 uint32_t hash = BKE_cryptomatte_hash(name, name_len);
326 }
327 }
328 if (entry != nullptr) {
329 BLI_addtail(&node_storage->entries, entry);
330 }
331 }
332}
333
335{
336 return BLI_hash_mm3(reinterpret_cast<const uchar *>(name.data()), name.size(), 0);
337}
338
339static void add_render_result_meta_data(RenderResult *render_result,
340 const blender::StringRef layer_name,
341 const blender::StringRefNull key_name,
342 const blender::StringRefNull value)
343{
345 render_result,
347 value.data());
348}
349
351{
353 session->layers.items())
354 {
355 const blender::StringRefNull layer_name(item.key);
356 const blender::bke::cryptomatte::CryptomatteLayer &layer = item.value;
357
358 const std::string manifest = layer.manifest();
359
360 add_render_result_meta_data(render_result, layer_name, "name", layer_name);
361 add_render_result_meta_data(render_result, layer_name, "hash", "MurmurHash3_32");
362 add_render_result_meta_data(render_result, layer_name, "conversion", "uint32_to_float32");
363 add_render_result_meta_data(render_result, layer_name, "manifest", manifest);
364 }
365}
366
368namespace manifest {
369constexpr StringRef WHITESPACES = " \t\n\v\f\r";
370
372{
373 size_t skip = ref.find_first_not_of(WHITESPACES);
374 if (skip == blender::StringRef::not_found) {
375 return ref;
376 }
377 return ref.drop_prefix(skip);
378}
379
380static constexpr int quoted_string_len_(blender::StringRef ref)
381{
382 int len = 1;
383 bool skip_next = false;
384 while (len < ref.size()) {
385 char current_char = ref[len];
386 if (skip_next) {
387 skip_next = false;
388 }
389 else {
390 if (current_char == '\\') {
391 skip_next = true;
392 }
393 if (current_char == '\"') {
394 len += 1;
395 break;
396 }
397 }
398 len += 1;
399 }
400 return len;
401}
402
403static std::string unquote_(const blender::StringRef ref)
404{
405 std::ostringstream stream;
406 for (char c : ref) {
407 if (c != '\\') {
408 stream << c;
409 }
410 }
411 return stream.str();
412}
413
415{
416 StringRef ref = manifest;
417 ref = skip_whitespaces_(ref);
418 if (ref.is_empty() || ref.front() != '{') {
419 return false;
420 }
421 ref = ref.drop_prefix(1);
422 while (!ref.is_empty()) {
423 char front = ref.front();
424
425 if (front == '\"') {
426 const int quoted_name_len = quoted_string_len_(ref);
427 const int name_len = quoted_name_len - 2;
428 std::string name = unquote_(ref.substr(1, name_len));
429 ref = ref.drop_prefix(quoted_name_len);
430 ref = skip_whitespaces_(ref);
431
432 if (ref.is_empty()) {
433 return false;
434 }
435 char colon = ref.front();
436 if (colon != ':') {
437 return false;
438 }
439 ref = ref.drop_prefix(1);
440 ref = skip_whitespaces_(ref);
441
442 if (ref.is_empty() || ref.front() != '\"') {
443 return false;
444 }
445
446 const int quoted_hash_len = quoted_string_len_(ref);
447 if (quoted_hash_len < 2) {
448 return false;
449 }
450 const int hash_len = quoted_hash_len - 2;
452 ref = ref.drop_prefix(quoted_hash_len);
453 layer.add_hash(name, hash);
454 }
455 else if (front == ',') {
456 ref = ref.drop_prefix(1);
457 }
458 else if (front == '}') {
459 ref = ref.drop_prefix(1);
460 ref = skip_whitespaces_(ref);
461 break;
462 }
463 ref = skip_whitespaces_(ref);
464 }
465
466 if (!ref.is_empty()) {
467 return false;
468 }
469
470 return true;
471}
472
473static std::string to_manifest(const CryptomatteLayer *layer)
474{
475 std::stringstream manifest;
476
477 bool is_first = true;
478 const blender::Map<std::string, CryptomatteHash> &const_map = layer->hashes;
479 manifest << "{";
481 if (is_first) {
482 is_first = false;
483 }
484 else {
485 manifest << ",";
486 }
487 manifest << quoted(item.key) << ":\"" << item.value.hex_encoded() << "\"";
488 }
489 manifest << "}";
490 return manifest.str();
491}
492
493} // namespace manifest
494
495/* Return the hash of the given cryptomatte layer name.
496 *
497 * The cryptomatte specification limits the hash to 7 characters.
498 * The 7 position limitation solves issues when using cryptomatte together with OpenEXR.
499 * The specification suggests to use the first 7 chars of the hashed layer_name.
500 */
501static std::string cryptomatte_layer_name_hash(const StringRef layer_name)
502{
503 std::stringstream stream;
504 const uint32_t render_pass_identifier = cryptomatte_determine_identifier(layer_name);
505 stream << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex
506 << render_pass_identifier;
507 return stream.str().substr(0, 7);
508}
509
510std::string BKE_cryptomatte_meta_data_key(const StringRef layer_name, const StringRefNull key_name)
511{
512 return "cryptomatte/" + cryptomatte_layer_name_hash(layer_name) + "/" + key_name;
513}
514
516{
517 int64_t last_token = render_pass_name.size();
518 while (last_token > 0 && std::isdigit(render_pass_name[last_token - 1])) {
519 last_token -= 1;
520 }
521 return render_pass_name.substr(0, last_token);
522}
523
525
527{
529 std::istringstream(hex_encoded) >> std::hex >> result.hash;
530 return result;
531}
532
534{
535 std::stringstream encoded;
536 encoded << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex << hash;
537 return encoded.str();
538}
539
540std::unique_ptr<CryptomatteLayer> CryptomatteLayer::read_from_manifest(
541 blender::StringRefNull manifest)
542{
543 std::unique_ptr<CryptomatteLayer> layer = std::make_unique<CryptomatteLayer>();
545 return layer;
546}
547
548uint32_t CryptomatteLayer::add_ID(const ID &id)
549{
550 const char *name = &id.name[2];
551 const int name_len = BLI_strnlen(name, MAX_NAME - 2);
552 uint32_t cryptohash_int = BKE_cryptomatte_hash(name, name_len);
553
554 add_hash(blender::StringRef(name, name_len), cryptohash_int);
555
556 return cryptohash_int;
557}
558
559void CryptomatteLayer::add_hash(blender::StringRef name, CryptomatteHash cryptomatte_hash)
560{
561 hashes.add_overwrite(name, cryptomatte_hash);
562}
563
564std::optional<std::string> CryptomatteLayer::operator[](float encoded_hash) const
565{
568 if (BKE_cryptomatte_hash_to_float(item.value.hash) == encoded_hash) {
569 return std::make_optional(item.key);
570 }
571 }
572 return std::nullopt;
573}
574
575std::string CryptomatteLayer::manifest() const
576{
578}
579
581{
582 BLI_assert(key.startswith("cryptomatte/"));
583
584 size_t start_index = key.find_first_of('/');
585 size_t end_index = key.find_last_of('/');
586 if (start_index == blender::StringRef::not_found) {
587 return "";
588 }
589 if (end_index == blender::StringRef::not_found) {
590 return "";
591 }
592 if (end_index <= start_index) {
593 return "";
594 }
595 return key.substr(start_index + 1, end_index - start_index - 1);
596}
597
599 const char *propname,
600 char *propvalue,
601 int /*propvalue_maxncpy*/)
602{
604
605 blender::StringRefNull key(propname);
606 if (!key.startswith("cryptomatte/")) {
607 return;
608 }
609 if (!key.endswith("/name")) {
610 return;
611 }
612 blender::StringRef layer_hash = extract_layer_hash(key);
613 data->hash_to_layer_name.add(layer_hash, propvalue);
614}
615
617 const char *propname,
618 char *propvalue,
619 int /*propvalue_maxncpy*/)
620{
622
623 blender::StringRefNull key(propname);
624 if (!key.startswith("cryptomatte/")) {
625 return;
626 }
627 if (!key.endswith("/manifest")) {
628 return;
629 }
630 blender::StringRef layer_hash = extract_layer_hash(key);
631 if (!data->hash_to_layer_name.contains(layer_hash)) {
632 return;
633 }
634
635 blender::StringRef layer_name = data->hash_to_layer_name.lookup(layer_hash);
636 blender::bke::cryptomatte::CryptomatteLayer &layer = data->session->add_layer(layer_name);
638}
639
641 const CryptomatteSession &session)
642{
643 return session.layer_names;
644}
645
647{
648 return session.layers.lookup_ptr(layer_name);
649}
650
651} // namespace blender::bke::cryptomatte
float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash)
uint32_t BKE_cryptomatte_hash(const char *name, int name_len)
void BKE_stamp_info_callback(void *data, StampData *stamp_data, StampCallback callback, bool noskip)
void BKE_render_result_stamp_data(RenderResult *rr, const char *key, const char *value)
void BKE_scene_view_layers_synced_ensure(const Scene *scene)
ListBase * BKE_view_layer_object_bases_get(ViewLayer *view_layer)
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
#define BLI_assert(a)
Definition BLI_assert.h:50
A dynamically sized string ADT.
char * BLI_dynstr_get_cstring(const DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition BLI_dynstr.c:149
void BLI_dynstr_nappend(DynStr *__restrict ds, const char *cstr, int len) ATTR_NONNULL()
Definition BLI_dynstr.c:81
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.c:37
void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL()
Definition BLI_dynstr.c:174
void BLI_dynstr_appendf(DynStr *__restrict ds, const char *__restrict format,...) ATTR_PRINTF_FORMAT(2
void BLI_dynstr_append(DynStr *__restrict ds, const char *cstr) ATTR_NONNULL()
Definition BLI_dynstr.c:62
uint32_t BLI_hash_mm3(const unsigned char *data, size_t len, uint32_t seed)
Definition hash_mm3.cc:73
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
#define STRNCPY(dst, src)
Definition BLI_string.h:593
int char char int int int int size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.c:909
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
unsigned char uchar
#define STREQ(a, b)
#define MAX_NAME
Definition DNA_defs.h:50
eViewLayerCryptomatteFlags
@ VIEW_LAYER_CRYPTOMATTE_MATERIAL
@ VIEW_LAYER_CRYPTOMATTE_ASSET
@ VIEW_LAYER_CRYPTOMATTE_OBJECT
#define VIEW_LAYER_CRYPTOMATTE_ALL
Object is a sort of wrapper for general info.
#define RE_PASSNAME_CRYPTOMATTE_MATERIAL
#define RE_PASSNAME_CRYPTOMATTE_ASSET
#define RE_PASSNAME_CRYPTOMATTE_OBJECT
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
void init()
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:484
ItemIterator items() const
Definition BLI_map.hh:864
constexpr const char & front() const
static constexpr int64_t not_found
constexpr int64_t find_last_of(StringRef chars, int64_t pos=INT64_MAX) const
constexpr int64_t find_first_not_of(StringRef chars, int64_t pos=0) const
constexpr bool is_empty() const
constexpr StringRef substr(int64_t start, int64_t size) const
constexpr bool startswith(StringRef prefix) const
constexpr bool endswith(StringRef suffix) const
constexpr int64_t find_first_of(StringRef chars, int64_t pos=0) const
constexpr int64_t size() const
constexpr StringRef drop_prefix(int64_t n) const
bool contains(const T &value) const
void append(const T &value)
CryptomatteSession * BKE_cryptomatte_init_from_render_result(const RenderResult *render_result)
uint32_t BKE_cryptomatte_hash(const char *name, const int name_len)
void BKE_cryptomatte_store_metadata(const CryptomatteSession *session, RenderResult *render_result)
static void add_render_result_meta_data(RenderResult *render_result, const blender::StringRef layer_name, const blender::StringRefNull key_name, const blender::StringRefNull value)
bool BKE_cryptomatte_find_name(const CryptomatteSession *session, const float encoded_hash, char *r_name, int name_maxncpy)
CryptomatteSession * BKE_cryptomatte_init()
void BKE_cryptomatte_free(CryptomatteSession *session)
CryptomatteSession * BKE_cryptomatte_init_from_scene(const Scene *scene, bool build_meta_data)
void BKE_cryptomatte_add_layer(CryptomatteSession *session, const char *layer_name)
float BKE_cryptomatte_hash_to_float(uint32_t cryptomatte_hash)
uint32_t BKE_cryptomatte_asset_hash(CryptomatteSession *session, const char *layer_name, const Object *object)
static uint32_t cryptomatte_determine_identifier(const blender::StringRef name)
CryptomatteSession * BKE_cryptomatte_init_from_view_layer(const ViewLayer *view_layer)
void BKE_cryptomatte_matte_id_to_entries(NodeCryptomatte *node_storage, const char *matte_id)
char * BKE_cryptomatte_entries_to_matte_id(NodeCryptomatte *node_storage)
uint32_t BKE_cryptomatte_object_hash(CryptomatteSession *session, const char *layer_name, const Object *object)
uint32_t BKE_cryptomatte_material_hash(CryptomatteSession *session, const char *layer_name, const Material *material)
int len
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
static bool from_manifest(CryptomatteLayer &layer, blender::StringRefNull manifest)
static constexpr int quoted_string_len_(blender::StringRef ref)
static std::string unquote_(const blender::StringRef ref)
static constexpr blender::StringRef skip_whitespaces_(blender::StringRef ref)
static std::string to_manifest(const CryptomatteLayer *layer)
CryptomatteLayer * BKE_cryptomatte_layer_get(CryptomatteSession &session, const StringRef layer_name)
StringRef BKE_cryptomatte_extract_layer_name(const StringRef render_pass_name)
std::string BKE_cryptomatte_meta_data_key(const StringRef layer_name, const StringRefNull key_name)
static std::string cryptomatte_layer_name_hash(const StringRef layer_name)
const blender::Vector< std::string > & BKE_cryptomatte_layer_names_get(const CryptomatteSession &session)
#define hash
Definition noise.c:154
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
CryptomatteSession()=default
blender::Map< std::string, blender::bke::cryptomatte::CryptomatteLayer > layers
std::optional< std::string > operator[](float encoded_hash) const
void init(const ViewLayer *view_layer, bool build_meta_data=false)
blender::bke::cryptomatte::CryptomatteLayer & add_layer(std::string layer_name)
blender::Vector< std::string > layer_names
Definition DNA_ID.h:413
ListBase materials
Definition BKE_main.hh:216
ListBase objects
Definition BKE_main.hh:212
struct Object * parent
struct StampData * stamp_data
short cryptomatte_flag
char name[64]
static CryptomatteHash from_hex_encoded(blender::StringRef hex_encoded)
void add_hash(blender::StringRef name, CryptomatteHash cryptomatte_hash)
blender::Map< std::string, CryptomatteHash > hashes
static void extract_layer_manifest(void *_data, const char *propname, char *propvalue, int propvalue_maxncpy)
static blender::StringRef extract_layer_hash(blender::StringRefNull key)
static void extract_layer_names(void *_data, const char *propname, char *propvalue, int propvalue_maxncpy)