Blender V4.3
sequencer/intern/utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 * SPDX-FileCopyrightText: 2003-2009 Blender Authors
3 * SPDX-FileCopyrightText: 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later */
6
10
11#include <algorithm>
12#include <cstdlib>
13#include <cstring>
14
15#include "MEM_guardedalloc.h"
16
17#include "DNA_scene_types.h"
18#include "DNA_sequence_types.h"
19
20#include "BLI_blenlib.h"
21#include "BLI_vector_set.hh"
22
23#include "BLT_translation.hh"
24
25#include "BKE_animsys.h"
26#include "BKE_image.hh"
27#include "BKE_main.hh"
28#include "BKE_scene.hh"
29
30#include "SEQ_channels.hh"
31#include "SEQ_edit.hh"
32#include "SEQ_iterator.hh"
33#include "SEQ_relations.hh"
34#include "SEQ_render.hh"
35#include "SEQ_select.hh"
36#include "SEQ_sequencer.hh"
37#include "SEQ_time.hh"
38#include "SEQ_utils.hh"
39
40#include "IMB_imbuf.hh"
41#include "IMB_imbuf_types.hh"
42
43#include "multiview.hh"
44#include "proxy.hh"
45#include "sequencer.hh"
46#include "utils.hh"
47
55
56static void seqbase_unique_name(ListBase *seqbasep, SeqUniqueInfo *sui)
57{
58 LISTBASE_FOREACH (Sequence *, seq, seqbasep) {
59 if ((sui->seq != seq) && STREQ(sui->name_dest, seq->name + 2)) {
60 /* SEQ_NAME_MAXSTR -4 for the number, -1 for \0, - 2 for r_prefix */
62 sui->name_dest, "%.*s.%03d", SEQ_NAME_MAXSTR - 4 - 1 - 2, sui->name_src, sui->count++);
63 sui->match = 1; /* be sure to re-scan */
64 }
65 }
66}
67
68static bool seqbase_unique_name_recursive_fn(Sequence *seq, void *arg_pt)
69{
70 if (seq->seqbase.first) {
72 }
73 return true;
74}
75
77{
78 SeqUniqueInfo sui;
79 char *dot;
80 sui.seq = seq;
81 STRNCPY(sui.name_src, seq->name + 2);
82 STRNCPY(sui.name_dest, seq->name + 2);
83
84 sui.count = 1;
85 sui.match = 1; /* assume the worst to start the loop */
86
87 /* Strip off the suffix */
88 if ((dot = strrchr(sui.name_src, '.'))) {
89 *dot = '\0';
90 dot++;
91
92 if (*dot) {
93 sui.count = atoi(dot) + 1;
94 }
95 }
96
97 while (sui.match) {
98 sui.match = 0;
99 seqbase_unique_name(seqbasep, &sui);
101 }
102
103 SEQ_edit_sequence_name_set(scene, seq, sui.name_dest);
104}
105
106static const char *give_seqname_by_type(int type)
107{
108 switch (type) {
109 case SEQ_TYPE_META:
111 case SEQ_TYPE_IMAGE:
112 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Image");
113 case SEQ_TYPE_SCENE:
114 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Scene");
115 case SEQ_TYPE_MOVIE:
116 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Movie");
119 case SEQ_TYPE_MASK:
122 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Audio");
123 case SEQ_TYPE_CROSS:
124 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Cross");
126 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Gamma Cross");
127 case SEQ_TYPE_ADD:
129 case SEQ_TYPE_SUB:
130 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Subtract");
131 case SEQ_TYPE_MUL:
132 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Multiply");
134 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Alpha Over");
136 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Alpha Under");
138 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Over Drop");
140 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Color Mix");
141 case SEQ_TYPE_WIPE:
143 case SEQ_TYPE_GLOW:
146 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Transform");
147 case SEQ_TYPE_COLOR:
148 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Color");
150 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Multicam");
152 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Adjustment");
153 case SEQ_TYPE_SPEED:
154 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Speed");
156 return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Gaussian Blur");
157 case SEQ_TYPE_TEXT:
159 default:
160 return nullptr;
161 }
162}
163
164const char *SEQ_sequence_give_name(const Sequence *seq)
165{
166 const char *name = give_seqname_by_type(seq->type);
167
168 if (!name) {
169 if (!(seq->type & SEQ_TYPE_EFFECT)) {
170 return seq->strip->dirpath;
171 }
172
173 return DATA_("Effect");
174 }
175 return name;
176}
177
178ListBase *SEQ_get_seqbase_from_sequence(Sequence *seq, ListBase **r_channels, int *r_offset)
179{
180 ListBase *seqbase = nullptr;
181
182 switch (seq->type) {
183 case SEQ_TYPE_META: {
184 seqbase = &seq->seqbase;
185 *r_channels = &seq->channels;
186 *r_offset = SEQ_time_start_frame_get(seq);
187 break;
188 }
189 case SEQ_TYPE_SCENE: {
190 if (seq->flag & SEQ_SCENE_STRIPS && seq->scene) {
191 Editing *ed = SEQ_editing_get(seq->scene);
192 if (ed) {
193 seqbase = &ed->seqbase;
194 *r_channels = &ed->channels;
195 *r_offset = seq->scene->r.sfra;
196 }
197 }
198 break;
199 }
200 }
201
202 return seqbase;
203}
204
206 StripAnim *sanim,
207 const char *filepath,
208 bool openfile)
209{
210 if (openfile) {
211 sanim->anim = openanim(filepath,
212 IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
213 seq->streamindex,
215 }
216 else {
217 sanim->anim = openanim_noload(filepath,
218 IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
219 seq->streamindex,
221 }
222}
223
224static bool use_proxy(Editing *ed, Sequence *seq)
225{
226 StripProxy *proxy = seq->strip->proxy;
227 return proxy && ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) != 0 ||
229}
230
231static void proxy_dir_get(Editing *ed, Sequence *seq, size_t str_len, char *r_proxy_dirpath)
232{
233 if (use_proxy(ed, seq)) {
235 if (ed->proxy_dir[0] == 0) {
236 BLI_strncpy(r_proxy_dirpath, "//BL_proxy", str_len);
237 }
238 else {
239 BLI_strncpy(r_proxy_dirpath, ed->proxy_dir, str_len);
240 }
241 }
242 else {
243 BLI_strncpy(r_proxy_dirpath, seq->strip->proxy->dirpath, str_len);
244 }
246 }
247}
248
249static void index_dir_set(Editing *ed, Sequence *seq, StripAnim *sanim)
250{
251 if (sanim->anim == nullptr || !use_proxy(ed, seq)) {
252 return;
253 }
254
255 char proxy_dirpath[FILE_MAX];
256 proxy_dir_get(ed, seq, sizeof(proxy_dirpath), proxy_dirpath);
257 seq_proxy_index_dir_set(sanim->anim, proxy_dirpath);
258}
259
260static bool open_anim_file_multiview(Scene *scene, Sequence *seq, const char *filepath)
261{
262 char prefix[FILE_MAX];
263 const char *ext = nullptr;
264 BKE_scene_multiview_view_prefix_get(scene, filepath, prefix, &ext);
265
266 if (seq->views_format != R_IMF_VIEWS_INDIVIDUAL || prefix[0] == '\0') {
267 return false;
268 }
269
270 Editing *ed = scene->ed;
271 bool is_multiview_loaded = false;
272 int totfiles = seq_num_files(scene, seq->views_format, true);
273
274 for (int i = 0; i < totfiles; i++) {
275 const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i);
276 char filepath_view[FILE_MAX];
277 SNPRINTF(filepath_view, "%s%s%s", prefix, suffix, ext);
278
279 StripAnim *sanim = static_cast<StripAnim *>(MEM_mallocN(sizeof(StripAnim), "Strip Anim"));
280 /* Multiview files must be loaded, otherwise it is not possible to detect failure. */
281 open_anim_filepath(seq, sanim, filepath_view, true);
282
283 if (sanim->anim == nullptr) {
285 return false; /* Multiview render failed. */
286 }
287
288 index_dir_set(ed, seq, sanim);
289 BLI_addtail(&seq->anims, sanim);
290 IMB_suffix_anim(sanim->anim, suffix);
291 is_multiview_loaded = true;
292 }
293
294 return is_multiview_loaded;
295}
296
297void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile)
298{
299 if ((seq->anims.first != nullptr) && (((StripAnim *)seq->anims.first)->anim != nullptr) &&
300 !openfile)
301 {
302 return;
303 }
304
305 /* Reset all the previously created anims. */
307
308 Editing *ed = scene->ed;
309 char filepath[FILE_MAX];
310 BLI_path_join(filepath, sizeof(filepath), seq->strip->dirpath, seq->strip->stripdata->filename);
311 BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&scene->id));
312
313 bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0;
314 bool multiview_is_loaded = false;
315
316 if (is_multiview) {
317 multiview_is_loaded = open_anim_file_multiview(scene, seq, filepath);
318 }
319
320 if (!is_multiview || !multiview_is_loaded) {
321 StripAnim *sanim = static_cast<StripAnim *>(MEM_mallocN(sizeof(StripAnim), "Strip Anim"));
322 BLI_addtail(&seq->anims, sanim);
323 open_anim_filepath(seq, sanim, filepath, openfile);
324 index_dir_set(ed, seq, sanim);
325 }
326}
327
328const Sequence *SEQ_get_topmost_sequence(const Scene *scene, int frame)
329{
330 Editing *ed = scene->ed;
331
332 if (!ed) {
333 return nullptr;
334 }
335
337 const Sequence *best_seq = nullptr;
338 int best_machine = -1;
339
340 LISTBASE_FOREACH (const Sequence *, seq, ed->seqbasep) {
341 if (SEQ_render_is_muted(channels, seq) || !SEQ_time_strip_intersects_frame(scene, seq, frame))
342 {
343 continue;
344 }
345 /* Only use strips that generate an image, not ones that combine
346 * other strips or apply some effect. */
347 if (ELEM(seq->type,
354 {
355 if (seq->machine > best_machine) {
356 best_seq = seq;
357 best_machine = seq->machine;
358 }
359 }
360 }
361 return best_seq;
362}
363
365{
366 Editing *ed = SEQ_editing_get(scene);
367 ListBase *main_seqbase = &ed->seqbase;
368 Sequence *seq_meta = seq_sequence_lookup_meta_by_seq(scene, seq);
369
370 if (seq_meta != nullptr) {
371 return &seq_meta->seqbase;
372 }
373 if (BLI_findindex(main_seqbase, seq) != -1) {
374 return main_seqbase;
375 }
376 return nullptr;
377}
378
380{
381 Sequence *iseq;
382
383 for (iseq = static_cast<Sequence *>(seqbase->first); iseq; iseq = iseq->next) {
384 Sequence *seq_found;
385 if ((iseq->strip && iseq->strip->stripdata) &&
386 ARRAY_HAS_ITEM(se, iseq->strip->stripdata, iseq->len))
387 {
388 break;
389 }
390 if ((seq_found = SEQ_sequence_from_strip_elem(&iseq->seqbase, se))) {
391 iseq = seq_found;
392 break;
393 }
394 }
395
396 return iseq;
397}
398
399Sequence *SEQ_get_sequence_by_name(ListBase *seqbase, const char *name, bool recursive)
400{
401 LISTBASE_FOREACH (Sequence *, iseq, seqbase) {
402 if (STREQ(name, iseq->name + 2)) {
403 return iseq;
404 }
405 if (recursive && !BLI_listbase_is_empty(&iseq->seqbase)) {
406 Sequence *rseq = SEQ_get_sequence_by_name(&iseq->seqbase, name, true);
407 if (rseq != nullptr) {
408 return rseq;
409 }
410 }
411 }
412
413 return nullptr;
414}
415
417{
418 Sequence *seq_act = SEQ_select_active_get(scene);
419
420 if (seq_act && seq_act->type == SEQ_TYPE_MASK) {
421 return seq_act->mask;
422 }
423
424 return nullptr;
425}
426
428{
429 if (seq->strip && seq->strip->stripdata) {
430 const char *filename = seq->strip->stripdata->filename;
432 }
433}
434
436{
437 switch (seq->type) {
438 case SEQ_TYPE_MASK:
439 return (seq->mask != nullptr);
441 return (seq->clip != nullptr);
442 case SEQ_TYPE_SCENE:
443 return (seq->scene != nullptr);
445 return (seq->sound != nullptr);
446 }
447
448 return true;
449}
450
452{
453 switch (seq->type) {
454 case SEQ_TYPE_IMAGE:
455 case SEQ_TYPE_SCENE:
456 case SEQ_TYPE_MOVIE:
458 case SEQ_TYPE_MASK:
459 case SEQ_TYPE_COLOR:
460 case SEQ_TYPE_TEXT:
461 return true;
462 }
463 return false;
464}
465
467 const int image_width,
468 const int image_height,
469 const int preview_width,
470 const int preview_height,
471 const eSeqImageFitMethod fit_method)
472{
474
475 switch (fit_method) {
476 case SEQ_SCALE_TO_FIT:
477 transform->scale_x = transform->scale_y = std::min(
478 float(preview_width) / float(image_width), float(preview_height) / float(image_height));
479
480 break;
482
483 transform->scale_x = transform->scale_y = std::max(
484 float(preview_width) / float(image_width), float(preview_height) / float(image_height));
485 break;
487 transform->scale_x = float(preview_width) / float(image_width);
488 transform->scale_y = float(preview_height) / float(image_height);
489 break;
491 transform->scale_x = 1.0f;
492 transform->scale_y = 1.0f;
493 break;
494 }
495}
496
498{
499 char name[SEQ_NAME_MAXSTR];
500
501 STRNCPY_UTF8(name, seq->name + 2);
504 scene->adt,
505 nullptr,
506 "sequence_editor.sequences_all",
507 name,
508 seq->name + 2,
509 0,
510 0,
511 false);
512
513 if (seq->type == SEQ_TYPE_META) {
514 LISTBASE_FOREACH (Sequence *, seq_child, &seq->seqbase) {
515 SEQ_ensure_unique_name(seq_child, scene);
516 }
517 }
518}
void BKE_animdata_fix_paths_rename(struct ID *owner_id, struct AnimData *adt, struct ID *ref_id, const char *prefix, const char *oldName, const char *newName, int oldSubscript, int newSubscript, bool verify_paths)
ImBufAnim * openanim_noload(const char *filepath, int flags, int streamindex, char colorspace[IMA_MAX_SPACE])
ImBufAnim * openanim(const char *filepath, int flags, int streamindex, char colorspace[IMA_MAX_SPACE])
char BKE_image_alpha_mode_from_extension_ex(const char *filepath)
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:837
const char * BKE_scene_multiview_view_id_suffix_get(const RenderData *rd, int view_id)
Definition scene.cc:3142
void BKE_scene_multiview_view_prefix_get(Scene *scene, const char *filepath, char *r_prefix, const char **r_ext)
Definition scene.cc:3152
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
static constexpr int image_width
static constexpr int image_height
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define FILE_MAX
#define BLI_path_join(...)
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
#define ARRAY_HAS_ITEM(arr_item, arr_start, arr_len)
#define ELEM(...)
#define STREQ(a, b)
#define BLT_I18NCONTEXT_ID_SEQUENCE
#define CTX_DATA_(context, msgid)
#define DATA_(msgid)
#define ID_BLEND_PATH_FROM_GLOBAL(_id)
Definition DNA_ID.h:649
eSeqImageFitMethod
@ SEQ_SCALE_TO_FILL
@ SEQ_STRETCH_TO_FILL
@ SEQ_USE_ORIGINAL_SIZE
@ SEQ_SCALE_TO_FIT
@ R_IMF_VIEWS_INDIVIDUAL
@ R_MULTIVIEW
@ SEQ_TYPE_TRANSFORM
@ SEQ_TYPE_SOUND_RAM
@ SEQ_TYPE_CROSS
@ SEQ_TYPE_GLOW
@ SEQ_TYPE_COLORMIX
@ SEQ_TYPE_WIPE
@ SEQ_TYPE_META
@ SEQ_TYPE_OVERDROP
@ SEQ_TYPE_ALPHAUNDER
@ SEQ_TYPE_SCENE
@ SEQ_TYPE_GAMCROSS
@ SEQ_TYPE_MULTICAM
@ SEQ_TYPE_MOVIECLIP
@ SEQ_TYPE_MUL
@ SEQ_TYPE_GAUSSIAN_BLUR
@ SEQ_TYPE_ADD
@ SEQ_TYPE_ALPHAOVER
@ SEQ_TYPE_TEXT
@ SEQ_TYPE_IMAGE
@ SEQ_TYPE_SUB
@ SEQ_TYPE_SPEED
@ SEQ_TYPE_COLOR
@ SEQ_TYPE_EFFECT
@ SEQ_TYPE_MOVIE
@ SEQ_TYPE_MASK
@ SEQ_TYPE_ADJUSTMENT
#define SEQ_NAME_MAXSTR
@ SEQ_STORAGE_PROXY_CUSTOM_DIR
@ SEQ_EDIT_PROXY_DIR_STORAGE
@ SEQ_FILTERY
@ SEQ_SCENE_STRIPS
@ SEQ_USE_VIEWS
void IMB_suffix_anim(ImBufAnim *anim, const char *suffix)
Contains defines and structs used throughout the imbuf module.
@ IB_animdeinterlace
@ IB_rect
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a producing a negative Combine Generate a color from its and blue channels(Deprecated)") DefNode(ShaderNode
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
ListBase * SEQ_channels_displayed_get(Editing *ed)
Definition channels.cc:23
draw_view in_light_buf[] float
void SEQ_for_each_callback(ListBase *seqbase, SeqForEachFunc callback, void *user_data)
Definition iterator.cc:43
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
int seq_num_files(Scene *scene, char views_format, const bool is_multiview)
Definition multiview.cc:27
void seq_proxy_index_dir_set(ImBufAnim *anim, const char *base_dir)
Definition proxy.cc:604
bool SEQ_render_is_muted(const ListBase *channels, const Sequence *seq)
Definition render.cc:2154
Sequence * seq_sequence_lookup_meta_by_seq(const Scene *scene, const Sequence *key)
bool sequencer_seq_generates_image(Sequence *seq)
const char * SEQ_sequence_give_name(const Sequence *seq)
static void proxy_dir_get(Editing *ed, Sequence *seq, size_t str_len, char *r_proxy_dirpath)
ListBase * SEQ_get_seqbase_by_seq(const Scene *scene, Sequence *seq)
Sequence * SEQ_get_sequence_by_name(ListBase *seqbase, const char *name, bool recursive)
void SEQ_sequence_base_unique_name_recursive(Scene *scene, ListBase *seqbasep, Sequence *seq)
static void seqbase_unique_name(ListBase *seqbasep, SeqUniqueInfo *sui)
void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile)
static const char * give_seqname_by_type(int type)
void SEQ_alpha_mode_from_file_extension(Sequence *seq)
ListBase * SEQ_get_seqbase_from_sequence(Sequence *seq, ListBase **r_channels, int *r_offset)
static bool open_anim_file_multiview(Scene *scene, Sequence *seq, const char *filepath)
static void open_anim_filepath(Sequence *seq, StripAnim *sanim, const char *filepath, bool openfile)
void SEQ_set_scale_to_fit(const Sequence *seq, const int image_width, const int image_height, const int preview_width, const int preview_height, const eSeqImageFitMethod fit_method)
Sequence * SEQ_sequence_from_strip_elem(ListBase *seqbase, StripElem *se)
static bool seqbase_unique_name_recursive_fn(Sequence *seq, void *arg_pt)
void SEQ_ensure_unique_name(Sequence *seq, Scene *scene)
bool SEQ_sequence_has_valid_data(const Sequence *seq)
const Sequence * SEQ_get_topmost_sequence(const Scene *scene, int frame)
Mask * SEQ_active_mask_get(Scene *scene)
static bool use_proxy(Editing *ed, Sequence *seq)
static void index_dir_set(Editing *ed, Sequence *seq, StripAnim *sanim)
Editing * SEQ_editing_get(const Scene *scene)
Definition sequencer.cc:262
void SEQ_edit_sequence_name_set(Scene *scene, Sequence *seq, const char *new_name)
void SEQ_relations_sequence_free_anim(Sequence *seq)
Sequence * SEQ_select_active_get(const Scene *scene)
bool SEQ_time_strip_intersects_frame(const Scene *scene, const Sequence *seq, const int timeline_frame)
float SEQ_time_start_frame_get(const Sequence *seq)
ListBase seqbase
ListBase * seqbasep
ListBase channels
char proxy_dir[1024]
void * first
struct Editing * ed
struct RenderData r
struct AnimData * adt
char name_dest[SEQ_NAME_MAXSTR]
char name_src[SEQ_NAME_MAXSTR]
struct MovieClip * clip
struct Scene * scene
struct Mask * mask
ListBase channels
struct bSound * sound
struct Sequence * next
struct ImBufAnim * anim
char filename[256]
char dirpath[768]
ColorManagedColorspaceSettings colorspace_settings
char dirpath[768]
StripProxy * proxy
StripTransform * transform
StripElem * stripdata