e1d471f825446d07846b85fabaafe7302450569b
[blender.git] / release / scripts / op / image.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 import bpy
22 from bpy.props import StringProperty
23
24
25 class EditExternally(bpy.types.Operator):
26     '''Edit image in an external application'''
27     bl_idname = "image.external_edit"
28     bl_label = "Image Edit Externally"
29     bl_options = {'REGISTER'}
30
31     filepath = StringProperty(name="File Path", description="Path to an image file", maxlen=1024, default="")
32
33     def _editor_guess(self, context):
34         import platform
35         try:
36             system = platform.system()
37         except UnicodeDecodeError:
38             import sys
39             system = sys.platform
40
41         image_editor = context.user_preferences.filepaths.image_editor
42
43         # use image editor in the preferences when available.
44         if not image_editor:
45             if system in ('Windows', 'win32'):
46                 image_editor = ["start"]  # not tested!
47             elif system == 'Darwin':
48                 image_editor = ["open"]
49             else:
50                 image_editor = ["gimp"]
51         else:
52             if system == 'Darwin':
53                 # blender file selector treats .app as a folder
54                 # and will include a trailing backslash, so we strip it.
55                 image_editor.rstrip('\\')
56                 image_editor = ["open", "-a", image_editor]
57             else:
58                 image_editor = [image_editor]
59
60         return image_editor
61
62     def execute(self, context):
63         import os
64         import subprocess
65         filepath = bpy.path.abspath(self.filepath)
66
67         if not os.path.exists(filepath):
68             self.report('ERROR', "Image path '%s' not found." % filepath)
69             return {'CANCELLED'}
70
71         cmd = self._editor_guess(context) + [filepath]
72
73         subprocess.Popen(cmd)
74
75         return {'FINISHED'}
76
77     def invoke(self, context, event):
78         try:
79             filepath = context.space_data.image.filepath
80         except:
81             self.report({'ERROR'}, "Image not found on disk")
82             return {'CANCELLED'}
83
84         self.filepath = filepath
85         self.execute(context)
86
87         return {'FINISHED'}
88
89
90 class SaveDirty(bpy.types.Operator):
91     """Save all modified textures"""
92     bl_idname = "image.save_dirty"
93     bl_label = "Save Dirty"
94     bl_options = {'REGISTER', 'UNDO'}
95
96     def execute(self, context):
97         unique_paths = set()
98         for image in bpy.data.images:
99             if image.is_dirty:
100                 filepath = bpy.path.abspath(image.filepath)
101                 if "\\" not in filepath and "/" not in filepath:
102                     self.report({'WARNING'}, "Invalid path: " + filepath)
103                 elif filepath in unique_paths:
104                     self.report({'WARNING'}, "Path used by more then one image: " + filepath)
105                 else:
106                     unique_paths.add(filepath)
107                     image.save()
108         return {'FINISHED'}
109
110
111 class ProjectEdit(bpy.types.Operator):
112     """Edit a snapshot if the viewport in an external image editor"""
113     bl_idname = "image.project_edit"
114     bl_label = "Project Edit"
115     bl_options = {'REGISTER'}
116
117     _proj_hack = [""]
118
119     def execute(self, context):
120         import os
121         import subprocess
122
123         EXT = "png"  # could be made an option but for now ok
124
125         for image in bpy.data.images:
126             image.tag = True
127
128         if 'FINISHED' not in bpy.ops.paint.image_from_view():
129             return {'CANCELLED'}
130
131         image_new = None
132         for image in bpy.data.images:
133             if not image.tag:
134                 image_new = image
135                 break
136
137         if not image_new:
138             self.report({'ERROR'}, "Could not make new image")
139             return {'CANCELLED'}
140
141         filepath = os.path.basename(bpy.data.filepath)
142         filepath = os.path.splitext(filepath)[0]
143         # filepath = bpy.path.clean_name(filepath) # fixes <memory> rubbish, needs checking
144
145         if filepath.startswith(".") or filepath == "":
146             # TODO, have a way to check if the file is saved, assume startup.blend
147             tmpdir = context.user_preferences.filepaths.temporary_directory
148             filepath = os.path.join(tmpdir, "project_edit")
149         else:
150             filepath = "//" + filepath
151
152         obj = context.object
153
154         if obj:
155             filepath += "_" + bpy.path.clean_name(obj.name)
156
157         filepath_final = filepath + "." + EXT
158         i = 0
159
160         while os.path.exists(bpy.path.abspath(filepath_final)):
161             filepath_final = filepath + ("%.3d.%s" % (i, EXT))
162             i += 1
163
164         image_new.name = os.path.basename(filepath_final)
165         ProjectEdit._proj_hack[0] = image_new.name
166
167         image_new.filepath_raw = filepath_final  # TODO, filepath raw is crummy
168         image_new.file_format = 'PNG'
169         image_new.save()
170
171         bpy.ops.image.external_edit(filepath=filepath_final)
172
173         return {'FINISHED'}
174
175
176 class ProjectApply(bpy.types.Operator):
177     """Project edited image back onto the object"""
178     bl_idname = "image.project_apply"
179     bl_label = "Project Apply"
180     bl_options = {'REGISTER'}
181
182     def execute(self, context):
183         image_name = ProjectEdit._proj_hack[0]  # TODO, deal with this nicer
184
185         try:
186             image = bpy.data.images[image_name]
187         except KeyError:
188             self.report({'ERROR'}, "Could not find image '%s'" % image_name)
189             return {'CANCELLED'}
190
191         image.reload()
192         bpy.ops.paint.project_image(image=image_name)
193
194         return {'FINISHED'}
195
196
197 def register():
198     bpy.utils.register_module(__name__)
199
200
201 def unregister():
202     bpy.utils.unregister_module(__name__)
203
204 if __name__ == "__main__":
205     register()