1use std::io::Write;
2
3use crate::owned::*;
4
5impl Puppet {
22 pub fn to_bytes(&self) -> Vec<u8> {
23 let mut buf = Vec::new();
24 self.write_to(&mut buf).expect("write to Vec never fails");
25 buf
26 }
27
28 pub fn write_to<W: Write>(&self, w: &mut W) -> std::io::Result<()> {
29 let mut json_buf = Vec::new();
31 {
32 let mut jw = JsonWriter::new(&mut json_buf);
33 self.write_json(&mut jw);
34 jw.finish()?;
35 }
36
37 w.write_all(b"TRNSRTS\0")?;
39 w.write_all(&(json_buf.len() as u32).to_be_bytes())?;
40 w.write_all(&json_buf)?;
41
42 w.write_all(b"TEX_SECT")?;
44 w.write_all(&(self.textures.len() as u32).to_be_bytes())?;
45
46 for tex in &self.textures {
47 let data = match &tex.data {
48 TextureData::Encoded(d) => d.as_slice(),
49 TextureData::Rgba(d) => d.as_slice(),
50 };
51 w.write_all(&(data.len() as u32).to_be_bytes())?;
52 w.write_all(&[tex.format as u8])?;
53 w.write_all(data)?;
54 }
55
56 if !self.vendors.is_empty() {
58 w.write_all(b"EXT_SECT")?;
59 w.write_all(&(self.vendors.len() as u32).to_be_bytes())?;
60
61 for vendor in &self.vendors {
62 let name_bytes = vendor.name.as_bytes();
63 w.write_all(&(name_bytes.len() as u32).to_be_bytes())?;
64 w.write_all(name_bytes)?;
65
66 let payload = json::stringify(vendor.data.clone()).into_bytes();
67 w.write_all(&(payload.len() as u32).to_be_bytes())?;
68 w.write_all(&payload)?;
69 }
70 }
71
72 Ok(())
73 }
74
75 pub fn save<P: AsRef<std::path::Path>>(&self, path: P) -> std::io::Result<()> {
76 let mut file = std::fs::File::create(path)?;
77 self.write_to(&mut file)
78 }
79}
80
81struct JsonWriter<'w, W: Write> {
82 w: &'w mut W,
83 err: Option<std::io::Error>,
86}
87
88impl<'w, W: Write> JsonWriter<'w, W> {
89 fn new(w: &'w mut W) -> Self {
90 Self { w, err: None }
91 }
92
93 fn write_bytes(&mut self, bytes: &[u8]) {
94 if self.err.is_some() {
95 return;
96 }
97 if let Err(e) = self.w.write_all(bytes) {
98 self.err = Some(e);
99 }
100 }
101
102 fn raw(&mut self, s: &str) {
103 self.write_bytes(s.as_bytes());
104 }
105
106 fn finish(self) -> std::io::Result<()> {
107 match self.err {
108 Some(e) => Err(e),
109 None => Ok(()),
110 }
111 }
112
113 fn comma(&mut self) {
114 self.raw(",");
115 }
116
117 fn key(&mut self, k: &str) {
118 self.raw("\"");
119 self.raw(k);
120 self.raw("\":");
121 }
122
123 fn str_val(&mut self, s: &str) {
124 self.raw("\"");
125 for c in s.chars() {
127 match c {
128 '"' => self.raw("\\\""),
129 '\\' => self.raw("\\\\"),
130 '\n' => self.raw("\\n"),
131 '\r' => self.raw("\\r"),
132 '\t' => self.raw("\\t"),
133 c if (c as u32) < 0x20 => {
134 self.raw(&format!("\\u{:04x}", c as u32));
135 }
136 c => {
137 let mut buf = [0u8; 4];
138 let s = c.encode_utf8(&mut buf);
139 self.write_bytes(s.as_bytes());
140 }
141 }
142 }
143 self.raw("\"");
144 }
145
146 fn null(&mut self) {
147 self.raw("null");
148 }
149
150 fn bool_val(&mut self, b: bool) {
151 self.raw(if b { "true" } else { "false" });
152 }
153
154 fn u32_val(&mut self, v: u32) {
155 self.raw(&v.to_string());
156 }
157
158 fn f32_val(&mut self, v: f32) {
161 if v.is_nan() {
162 self.raw("0.0");
163 return;
164 }
165 if v.is_infinite() {
166 if v > 0.0 {
167 self.raw("1e38");
168 } else {
169 self.raw("-1e38");
170 }
171 return;
172 }
173
174 let s = format!("{}", v);
176 if s.contains('.') || s.contains('e') || s.contains('E') {
177 self.raw(&s);
178 } else {
179 self.raw(&s);
181 self.raw(".0");
182 }
183 }
184
185 fn f32_arr(&mut self, vals: &[f32]) {
186 self.raw("[");
187 for (i, &v) in vals.iter().enumerate() {
188 if i > 0 {
189 self.comma();
190 }
191 self.f32_val(v);
192 }
193 self.raw("]");
194 }
195
196 fn vec2(&mut self, v: [f32; 2]) {
197 self.f32_arr(&v);
198 }
199
200 fn vec3(&mut self, v: [f32; 3]) {
201 self.f32_arr(&v);
202 }
203
204 fn opt_str(&mut self, s: Option<&str>) {
205 match s {
206 Some(s) => self.str_val(s),
207 None => self.null(),
208 }
209 }
210
211 fn begin_obj(&mut self) {
212 self.raw("{");
213 }
214
215 fn end_obj(&mut self) {
216 self.raw("}");
217 }
218
219 fn begin_arr(&mut self) {
220 self.raw("[");
221 }
222
223 fn end_arr(&mut self) {
224 self.raw("]");
225 }
226
227 fn json_value(&mut self, v: &json::JsonValue) {
229 let s = json::stringify(v.clone());
230 self.raw(&s);
231 }
232}
233
234impl Puppet {
235 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
236 j.begin_obj();
237 j.key("meta");
239 self.meta.write_json(j);
240 j.comma();
242 j.key("physics");
243 self.physics.write_json(j);
244 j.comma();
246 j.key("nodes");
247 self.nodes.write_json(j);
248 j.comma();
250 j.key("param");
251
252 j.begin_arr();
253 let mut params: Vec<&Param> = self.params.values().collect();
255 params.sort_by_key(|p| p.uuid);
256 for (i, p) in params.iter().enumerate() {
257 if i > 0 {
258 j.comma();
259 }
260 p.write_json(j);
261 }
262 j.end_arr();
263
264 j.comma();
266 j.key("automation");
267 j.null();
268 j.comma();
270 j.key("animations");
271 self.write_animations(j);
272 j.comma();
274 j.key("groups");
275 j.begin_arr();
276 for (i, g) in self.groups.iter().enumerate() {
277 if i > 0 {
278 j.comma();
279 }
280 g.write_json(j);
281 }
282 j.end_arr();
283
284 j.end_obj();
285 }
286
287 fn write_animations<W: Write>(&self, j: &mut JsonWriter<W>) {
288 j.begin_obj();
289 let mut anims: Vec<(&String, &Animation)> = self.animations.iter().collect();
291 anims.sort_by_key(|(name, _)| *name);
292 for (i, (name, anim)) in anims.iter().enumerate() {
293 if i > 0 {
294 j.comma();
295 }
296 j.key(name);
297 anim.write_json(j);
298 }
299 j.end_obj();
300 }
301}
302
303impl Meta {
304 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
305 j.begin_obj();
306 j.key("name");
307 j.opt_str(self.name.as_deref());
308 j.comma();
309 j.key("version");
310 j.str_val(&self.version);
311 j.comma();
312 j.key("rigger");
313 j.opt_str(self.rigger.as_deref());
314 j.comma();
315 j.key("artist");
316 j.opt_str(self.artist.as_deref());
317 j.comma();
318 j.key("rights");
319 j.opt_str(self.rights.as_deref());
320 j.comma();
321 j.key("copyright");
322 j.opt_str(self.copyright.as_deref());
323 j.comma();
324 j.key("licenseURL");
325 j.opt_str(self.license_url.as_deref());
326 j.comma();
327 j.key("contact");
328 j.opt_str(self.contact.as_deref());
329 j.comma();
330 j.key("reference");
331 j.opt_str(self.reference.as_deref());
332 j.comma();
333 j.key("thumbnailId");
334 j.u32_val(self.thumbnail_id);
335 j.comma();
336 j.key("preservePixels");
337 j.bool_val(self.preserve_pixels);
338 j.end_obj();
339 }
340}
341
342impl Physics {
343 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
344 j.begin_obj();
345 j.key("pixelsPerMeter");
346 j.f32_val(self.pixels_per_meter);
347 j.comma();
348 j.key("gravity");
349 j.f32_val(self.gravity);
350 j.end_obj();
351 }
352}
353
354impl Node {
355 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
356 j.begin_obj();
357 j.key("uuid");
358 j.u32_val(self.uuid);
359 j.comma();
360 j.key("name");
361 j.str_val(&self.name);
362 j.comma();
363 j.key("type");
364 j.str_val(self.type_node.type_str());
365 j.comma();
366 j.key("enabled");
367 j.bool_val(self.enabled);
368 j.comma();
369 j.key("zsort");
370 j.f32_val(self.zsort);
371 j.comma();
372 j.key("transform");
373 self.transform.write_json(j);
374 j.comma();
375 j.key("lockToRoot");
376 j.bool_val(self.lock_to_root);
377
378 self.type_node.write_fields(j);
379
380 if !self.children.is_empty() {
381 j.comma();
382 j.key("children");
383 j.begin_arr();
384 for (i, child) in self.children.iter().enumerate() {
385 if i > 0 {
386 j.comma();
387 }
388 child.write_json(j);
389 }
390 j.end_arr();
391 }
392
393 j.end_obj();
394 }
395}
396
397impl Transform {
398 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
399 j.begin_obj();
400 j.key("trans");
401 j.vec3(self.translation);
402 j.comma();
403 j.key("rot");
404 j.vec3(self.rotation);
405 j.comma();
406 j.key("scale");
407 j.vec2(self.scale);
408 j.end_obj();
409 }
410}
411
412impl NodeDataType {
413 fn type_str(&self) -> &'static str {
414 match self {
415 Self::Part(_) => "Part",
416 Self::Camera(_) => "Camera",
417 Self::SimplePhysics(_) => "SimplePhysics",
418 Self::Composite(_) => "Composite",
419 Self::Mask(_) => "Mask",
420 Self::MeshGroup(_) => "MeshGroup",
421 Self::Generic => "Node",
422 }
423 }
424
425 fn write_fields<W: Write>(&self, j: &mut JsonWriter<W>) {
426 match self {
427 Self::Part(d) => d.write_fields(j),
428 Self::Camera(d) => d.write_fields(j),
429 Self::SimplePhysics(d) => d.write_fields(j),
430 Self::Composite(d) => d.write_fields(j),
431 Self::Mask(d) => d.write_fields(j),
432 Self::MeshGroup(d) => d.write_fields(j),
433 Self::Generic => {}
434 }
435 }
436}
437
438impl PartData {
439 fn write_fields<W: Write>(&self, j: &mut JsonWriter<W>) {
440 j.comma();
441 j.key("mesh");
442 write_mesh(j, self.mesh.as_ref());
443 j.comma();
444 j.key("textures");
445 j.begin_arr();
446 j.u32_val(self.textures[0]);
447 j.comma();
448 j.u32_val(self.textures[1]);
449 j.comma();
450 j.u32_val(self.textures[2]);
451 j.end_arr();
452 j.comma();
453 j.key("blend_mode");
454 j.str_val(blend_mode_str(self.blend_mode));
455 j.comma();
456 j.key("tint");
457 j.vec3(self.tint);
458 j.comma();
459 j.key("screenTint");
460 j.vec3(self.screen_tint);
461 j.comma();
462 j.key("emissionStrength");
463 j.f32_val(self.emission_strength);
464 if !self.mask.is_empty() {
465 j.comma();
466 j.key("masks");
467 j.begin_arr();
468 for (i, m) in self.mask.iter().enumerate() {
469 if i > 0 {
470 j.comma();
471 }
472 m.write_json(j);
473 }
474 j.end_arr();
475 }
476 j.comma();
477 j.key("mask_threshold");
478 j.f32_val(self.mask_threshold);
479 j.comma();
480 j.key("opacity");
481 j.f32_val(self.opacity);
482 if let Some(ref path) = self.psd_layer_path {
483 j.comma();
484 j.key("psdLayerPath");
485 j.str_val(path);
486 }
487 }
488}
489
490impl CameraData {
491 fn write_fields<W: Write>(&self, j: &mut JsonWriter<W>) {
492 j.comma();
493 j.key("viewport");
494 j.vec2(self.viewport);
495 }
496}
497
498impl SimplePhysicsData {
499 fn write_fields<W: Write>(&self, j: &mut JsonWriter<W>) {
500 j.comma();
501 j.key("param");
502 j.u32_val(self.param);
503 j.comma();
504 j.key("model_type");
505 j.str_val(match self.model_type {
506 PhysicsModelType::Pendulum => "Pendulum",
507 PhysicsModelType::SpringPendulum => "SpringPendulum",
508 });
509 j.comma();
510 j.key("map_mode");
511 j.str_val(match self.map_mode {
512 PhysicsMapMode::AngleLength => "AngleLength",
513 PhysicsMapMode::XY => "XY",
514 PhysicsMapMode::LengthAngle => "LengthAngle",
515 PhysicsMapMode::YX => "YX",
516 });
517 j.comma();
518 j.key("gravity");
519 j.f32_val(self.gravity);
520 j.comma();
521 j.key("length");
522 j.f32_val(self.length);
523 j.comma();
524 j.key("frequency");
525 j.f32_val(self.frequency);
526 j.comma();
527 j.key("angle_damping");
528 j.f32_val(self.angle_damping);
529 j.comma();
530 j.key("length_damping");
531 j.f32_val(self.length_damping);
532 j.comma();
533 j.key("output_scale");
534 j.vec2(self.output_scale);
535 if let Some(lo) = self.local_only {
536 j.comma();
537 j.key("local_only");
538 j.bool_val(lo);
539 }
540 }
541}
542
543impl CompositeData {
544 fn write_fields<W: Write>(&self, j: &mut JsonWriter<W>) {
545 j.comma();
546 j.key("blend_mode");
547 j.str_val(blend_mode_str(self.blend_mode));
548 j.comma();
549 j.key("tint");
550 j.vec3(self.tint);
551 j.comma();
552 j.key("screenTint");
553 j.vec3(self.screen_tint);
554 j.comma();
555 j.key("opacity");
556 j.f32_val(self.opacity);
557 if !self.mask.is_empty() {
558 j.comma();
559 j.key("masks");
560 j.begin_arr();
561 for (i, m) in self.mask.iter().enumerate() {
562 if i > 0 {
563 j.comma();
564 }
565 m.write_json(j);
566 }
567 j.end_arr();
568 }
569 j.comma();
570 j.key("mask_threshold");
571 j.f32_val(self.mask_threshold);
572 if let Some(p) = self.propagate_meshgroup {
573 j.comma();
574 j.key("propagate_meshgroup");
575 j.bool_val(p);
576 }
577 }
578}
579
580impl MaskData {
581 fn write_fields<W: Write>(&self, j: &mut JsonWriter<W>) {
582 j.comma();
583 j.key("mesh");
584 write_mesh(j, self.mesh.as_ref());
585 }
586}
587
588impl MeshGroupData {
589 fn write_fields<W: Write>(&self, j: &mut JsonWriter<W>) {
590 j.comma();
591 j.key("mesh");
592 write_mesh(j, self.mesh.as_ref());
593 j.comma();
594 j.key("dynamic_deformation");
595 j.bool_val(self.dynamic_deformation);
596 j.comma();
597 j.key("translate_children");
598 j.bool_val(self.translate_children);
599 }
600}
601
602impl Mask {
603 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
604 j.begin_obj();
605 j.key("source");
606 j.u32_val(self.source);
607 j.comma();
608 j.key("mode");
609 j.str_val(match self.mode {
610 MaskMode::Mask => "Mask",
611 MaskMode::Dodge => "DodgeMask",
612 });
613 j.end_obj();
614 }
615}
616
617impl Param {
618 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
619 j.begin_obj();
620 if let Some(parent) = self.parent_uuid {
621 j.key("parentUUID");
622 j.u32_val(parent);
623 j.comma();
624 }
625 j.key("uuid");
626 j.u32_val(self.uuid);
627 j.comma();
628 j.key("name");
629 j.str_val(&self.name);
630 j.comma();
631 j.key("is_vec2");
632 j.bool_val(self.is_vec2);
633 j.comma();
634 j.key("min");
635 j.vec2(self.min);
636 j.comma();
637 j.key("max");
638 j.vec2(self.max);
639 j.comma();
640 j.key("defaults");
641 j.vec2(self.defaults);
642 j.comma();
643 j.key("axis_points");
644 j.begin_arr();
645 j.f32_arr(&self.axis_points[0]);
646 j.comma();
647 j.f32_arr(&self.axis_points[1]);
648 j.end_arr();
649 j.comma();
650 j.key("merge_mode");
651 j.str_val(merge_mode_str(self.merge_mode));
652 j.comma();
653 j.key("bindings");
654 j.begin_arr();
655 for (i, b) in self.bindings.iter().enumerate() {
656 if i > 0 {
657 j.comma();
658 }
659 b.write_json(j);
660 }
661 j.end_arr();
662 j.end_obj();
663 }
664}
665
666impl ParamBinding {
667 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
668 j.begin_obj();
669 j.key("node");
670 j.u32_val(self.node);
671 j.comma();
672 j.key("param_name");
673 j.str_val(param_name_str(&self.param_name));
674 j.comma();
675 j.key("interpolate_mode");
676 j.str_val(interpolation_str(self.interpolate_mode));
677 j.comma();
678 j.key("isSet");
679 j.begin_arr();
680 for (i, row) in self.is_set.iter().enumerate() {
681 if i > 0 {
682 j.comma();
683 }
684 j.begin_arr();
685 for (k, &b) in row.iter().enumerate() {
686 if k > 0 {
687 j.comma();
688 }
689 j.bool_val(b);
690 }
691 j.end_arr();
692 }
693 j.end_arr();
694 j.comma();
695 j.key("values");
696 self.write_values(j);
697 j.end_obj();
698 }
699
700 fn write_values<W: Write>(&self, j: &mut JsonWriter<W>) {
701 match &self.values {
702 BindingValues::Transform(flat) => flat.write_json(j),
703 BindingValues::Deform(flat) => {
704 let x_count = self.is_set.len().max(1);
705 let y_count = self.is_set.first().map(|r| r.len()).unwrap_or(1).max(1);
706 flat.write_json_shaped(j, x_count, y_count);
707 }
708 BindingValues::Other(v) => j.json_value(v),
709 }
710 }
711}
712
713impl FlatTransformValues {
714 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
715 j.begin_arr();
716 for f in 0..self.frames {
717 if f > 0 {
718 j.comma();
719 }
720 j.begin_arr();
721 let start = f * self.values_per_frame;
722 for k in 0..self.values_per_frame {
723 if k > 0 {
724 j.comma();
725 }
726 j.f32_val(self.data[start + k]);
727 }
728 j.end_arr();
729 }
730 j.end_arr();
731 }
732}
733
734impl FlatDeformValues {
735 fn write_json_shaped<W: Write>(&self, j: &mut JsonWriter<W>, x_count: usize, y_count: usize) {
736 let actual_vtx = if y_count > 0 {
743 self.vertices_per_frame / y_count
744 } else {
745 self.vertices_per_frame
746 };
747
748 j.begin_arr();
749 for x in 0..x_count {
750 if x > 0 {
751 j.comma();
752 }
753 j.begin_arr();
754 for y in 0..y_count {
755 if y > 0 {
756 j.comma();
757 }
758 j.begin_arr();
759 for v in 0..actual_vtx {
760 if v > 0 {
761 j.comma();
762 }
763 let idx = x * self.vertices_per_frame + y * actual_vtx + v;
764 let [dx, dy] = self.data.get(idx).copied().unwrap_or([0.0, 0.0]);
765 j.begin_arr();
766 j.f32_val(dx);
767 j.comma();
768 j.f32_val(dy);
769 j.end_arr();
770 }
771 j.end_arr();
772 }
773 j.end_arr();
774 }
775 j.end_arr();
776 }
777}
778
779impl Animation {
780 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
781 j.begin_obj();
782 j.key("timestep");
783 j.f32_val(self.timestep);
784 j.comma();
785 j.key("additive");
786 j.bool_val(self.additive);
787 j.comma();
788 j.key("length");
789 j.u32_val(self.length);
790 j.comma();
791 j.key("leadIn");
792 j.u32_val(self.lead_in);
793 j.comma();
794 j.key("leadOut");
795 j.u32_val(self.lead_out);
796 j.comma();
797 j.key("animationWeight");
798 j.f32_val(self.weight);
799 j.comma();
800 j.key("lanes");
801 j.begin_arr();
802 for (i, lane) in self.lanes.iter().enumerate() {
803 if i > 0 {
804 j.comma();
805 }
806 lane.write_json(j);
807 }
808 j.end_arr();
809 j.end_obj();
810 }
811}
812
813impl AnimationLane {
814 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
815 j.begin_obj();
816 j.key("interpolation");
817 j.str_val(interpolation_str(self.interpolation));
818 j.comma();
819 j.key("uuid");
820 j.u32_val(self.param_uuid);
821 j.comma();
822 j.key("target");
823 j.u32_val(self.target as u32);
824 j.comma();
825 j.key("merge_mode");
826 j.str_val(merge_mode_str(self.merge_mode));
827 j.comma();
828 j.key("keyframes");
829 j.begin_arr();
830 for (i, kf) in self.keyframes.iter().enumerate() {
831 if i > 0 {
832 j.comma();
833 }
834 kf.write_json(j);
835 }
836 j.end_arr();
837 j.end_obj();
838 }
839}
840
841impl Keyframe {
842 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
843 j.begin_obj();
844 j.key("frame");
845 j.u32_val(self.frame);
846 j.comma();
847 j.key("value");
848 j.f32_val(self.value);
849 j.comma();
850 j.key("tension");
851 j.f32_val(self.tension);
852 j.end_obj();
853 }
854}
855
856impl Group {
857 fn write_json<W: Write>(&self, j: &mut JsonWriter<W>) {
858 j.begin_obj();
859 j.key("groupUUID");
860 j.u32_val(self.group_uuid);
861 j.comma();
862 j.key("name");
863 j.str_val(&self.name);
864 j.comma();
865 j.key("color");
866 j.vec3(self.color);
867 j.end_obj();
868 }
869}
870
871fn write_mesh<W: Write>(j: &mut JsonWriter<W>, mesh: Option<&Mesh>) {
872 match mesh {
873 Some(m) => {
874 j.begin_obj();
875 j.key("verts");
876 j.f32_arr(&m.vertices);
877 j.comma();
878 j.key("indices");
879 j.begin_arr();
880 for (i, &idx) in m.indices.iter().enumerate() {
881 if i > 0 {
882 j.comma();
883 }
884 j.u32_val(idx);
885 }
886 j.end_arr();
887 j.comma();
888 j.key("uvs");
889 j.f32_arr(&m.uvs);
890 j.comma();
891 j.key("origin");
892 j.vec2(m.origin);
893 j.end_obj();
894 }
895 None => {
896 j.begin_obj();
898 j.key("verts");
899 j.begin_arr();
900 j.end_arr();
901 j.comma();
902 j.key("indices");
903 j.null();
904 j.comma();
905 j.key("origin");
906 j.vec2([0.0, 0.0]);
907 j.end_obj();
908 }
909 }
910}
911
912fn blend_mode_str(bm: BlendMode) -> &'static str {
913 match bm {
914 BlendMode::Normal => "Normal",
915 BlendMode::Multiply => "Multiply",
916 BlendMode::Screen => "Screen",
917 BlendMode::Overlay => "Overlay",
918 BlendMode::Darken => "Darken",
919 BlendMode::Lighten => "Lighten",
920 BlendMode::ColorDodge => "ColorDodge",
921 BlendMode::LinearDodge => "LinearDodge",
922 BlendMode::Add => "Add",
923 BlendMode::ColorBurn => "ColorBurn",
924 BlendMode::HardLight => "HardLight",
925 BlendMode::SoftLight => "SoftLight",
926 BlendMode::Subtract => "Subtract",
927 BlendMode::Difference => "Difference",
928 BlendMode::Exclusion => "Exclusion",
929 BlendMode::Inverse => "Inverse",
930 BlendMode::DestinationIn => "DestinationIn",
931 BlendMode::ClipToLower => "ClipToLower",
932 BlendMode::SliceFromLower => "SliceFromLower",
933 }
934}
935
936fn merge_mode_str(mm: MergeMode) -> &'static str {
937 match mm {
938 MergeMode::Additive => "Additive",
939 MergeMode::Multiplicative => "Multiply",
940 MergeMode::Override => "Override",
941 MergeMode::Forced => "Forced",
942 }
943}
944
945fn interpolation_str(i: Interpolation) -> &'static str {
946 match i {
947 Interpolation::Linear => "Linear",
948 Interpolation::Stepped => "Stepped",
949 Interpolation::Nearest => "Nearest",
950 Interpolation::Cubic => "Cubic",
951 }
952}
953
954fn param_name_str(pn: &ParamName) -> &str {
955 match pn {
956 ParamName::TransformTX => "transform.t.x",
957 ParamName::TransformTY => "transform.t.y",
958 ParamName::TransformTZ => "transform.t.z",
959 ParamName::TransformSX => "transform.s.x",
960 ParamName::TransformSY => "transform.s.y",
961 ParamName::TransformRX => "transform.r.x",
962 ParamName::TransformRY => "transform.r.y",
963 ParamName::TransformRZ => "transform.r.z",
964 ParamName::Deform => "deform",
965 ParamName::Opacity => "opacity",
966 ParamName::Other(s) => s.as_str(),
967 }
968}