1 /*
2     Copyright © 2020, Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors: Luna Nielsen
6 */
7 module creator.io.psd;
8 import creator;
9 import creator.ext;
10 import creator.core.tasks;
11 import creator.widgets.dialog;
12 import inochi2d.math;
13 import inochi2d;
14 import psd;
15 import i18n;
16 import std.format;
17 import creator.io;
18 
19 private {
20 
21 }
22 
23 bool incImportShowPSDDialog() {
24     TFD_Filter[] filters = [{ ["*.psd"], "Photoshop Document (*.psd)" }];
25     string file = incShowImportDialog(filters);
26 
27     if (file) {
28         incImportPSD(file);
29         return true;
30     }
31     return false;
32 }
33 
34 /**
35     Imports a PSD file.
36 */
37 void incImportPSD(string file) {
38     incNewProject();
39     // TODO: Split this up to a seperate file and make it cleaner
40     try {
41         import psd : PSD, Layer, LayerType, LayerFlags, parseDocument, BlendingMode;
42         import std.array : join;
43         PSD doc = parseDocument(file);
44         vec2i docCenter = vec2i(doc.width/2, doc.height/2);
45         Puppet puppet = new ExPuppet();
46 
47         Layer[] layerGroupStack;
48         bool isLastStackItemHidden() {
49             return layerGroupStack.length > 0 ? (layerGroupStack[$-1].flags & LayerFlags.Visible) != 0 : false;
50         }
51 
52         string[] path;
53         string calcPath;
54         void pushGroupStackName(string layerName) {
55             path ~= layerName;
56             calcPath = "/"~path.join("/");
57         }
58 
59         void popGroupStackName() {
60             path.length--;
61             calcPath = "/"~path.join("/");
62         }
63 
64         foreach_reverse(i, Layer layer; doc.layers) {
65             import std.stdio : writeln;
66             debug writeln(layer.name, " ", layer.blendModeKey);
67 
68             // Skip folders ( for now )
69             if (layer.type != LayerType.Any) {
70                 if (layer.name != "</Layer set>" && layer.name != "</Layer group>") {
71                     layerGroupStack ~= layer;
72                     pushGroupStackName(layer.name);
73                 } else {
74                     layerGroupStack.length--;
75                     popGroupStackName();
76                 }
77 
78                 continue;
79             } else pushGroupStackName(layer.name);
80 
81             layer.extractLayerImage();
82             inTexPremultiply(layer.data);
83             auto tex = new Texture(layer.data, layer.width, layer.height);
84             ExPart part = incCreateExPart(tex, puppet.root, layer.name);
85             part.layerPath = calcPath;
86 
87             auto layerSize = cast(int[2])layer.size();
88             vec2i layerPosition = vec2i(
89                 layer.left,
90                 layer.top
91             );
92 
93             part.localTransform.translation = vec3(
94                 (layerPosition.x+(layerSize[0]/2))-docCenter.x,
95                 (layerPosition.y+(layerSize[1]/2))-docCenter.y,
96                 0
97             );
98 
99 
100             part.enabled = (layer.flags & LayerFlags.Visible) == 0;
101             part.opacity = (cast(float)layer.opacity)/255;
102             part.zSort = -(cast(float)i);
103             switch(layer.blendModeKey) {
104                 case BlendingMode.Multiply: 
105                     part.blendingMode = BlendMode.Multiply; break;
106                 case BlendingMode.LinearDodge: 
107                     part.blendingMode = BlendMode.LinearDodge; break;
108                 case BlendingMode.ColorDodge: 
109                     part.blendingMode = BlendMode.ColorDodge; break;
110                 case BlendingMode.Screen: 
111                     part.blendingMode = BlendMode.Screen; break;
112                 default:
113                     part.blendingMode = BlendMode.Normal; break;
114             }
115             debug writeln(part.name, ": ", part.blendingMode);
116 
117             // Handle layer stack stuff
118             if (layerGroupStack.length > 0) {
119                 if (isLastStackItemHidden()) part.enabled = false;
120                 if (layerGroupStack[$-1].blendModeKey != BlendingMode.PassThrough) {
121                     switch(layerGroupStack[$-1].blendModeKey) {
122                         case BlendingMode.Multiply: 
123                             part.blendingMode = BlendMode.Multiply; break;
124                         case BlendingMode.LinearDodge: 
125                             part.blendingMode = BlendMode.LinearDodge; break;
126                         case BlendingMode.ColorDodge: 
127                             part.blendingMode = BlendMode.ColorDodge; break;
128                         case BlendingMode.Screen: 
129                             part.blendingMode = BlendMode.Screen; break;
130                         default:
131                             part.blendingMode = BlendMode.Normal; break;
132                     }
133                 }
134             }
135 
136             puppet.root.addChild(part);
137 
138             if (layer.type == LayerType.Any) popGroupStackName();
139         }
140 
141         puppet.populateTextureSlots();
142         incActiveProject().puppet = puppet;
143         incFocusCamera(incActivePuppet().root);
144 
145         incSetStatus(_("%s was imported...".format(file)));
146     } catch (Exception ex) {
147 
148         incSetStatus(_("Import failed..."));
149         incDialog(__("Error"), _("An error occured during PSD import:\n%s").format(ex.msg));
150     }
151     incFreeMemory();
152 }