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 }