1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
22 This script dices up PNG into small files to store in version control.
27 --background -noaudio \
28 --python ./release/datafiles/icon_dice.py -- \
29 --image=./release/datafiles/blender_icons16.png \
30 --output=./release/datafiles/blender_icons16
31 --output_prefix=icon16_
33 --parts_x 26 --parts_y 32 \
34 --minx=10 --maxx 10 --miny 10 --maxy 10
35 --minx_icon 2 --maxx_icon 2 --miny_icon 2 --maxy_icon 2 \
36 --spacex_icon 1 --spacey_icon 1
42 SOURCE_DIR = os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
46 def image_from_file__bpy(filepath):
49 image = bpy.data.images.load(filepath)
52 pixel_w, pixel_h = image.size
53 pixels = image.pixels[:]
54 return pixels, pixel_w, pixel_h
57 def image_from_file(filepath):
59 Return pixels, w, h from an image.
61 note: bpy import is ONLY used here.
70 pixels, pixel_w, pixel_h = image_from_file__bpy(filepath)
72 # pixels, pixel_w, pixel_h = image_from_file__py(filepath)
74 return pixels, pixel_w, pixel_h
77 def write_subimage(sub_x, sub_y, sub_w, sub_h,
79 pixels, pixel_w, pixel_h):
82 # first check if the icon is worth writing
84 for y in range(sub_h):
85 for x in range(sub_w):
86 i = (sub_x + x) + ((sub_y + y) * pixel_w)
87 a = pixels[(i * 4) + 3]
93 # print("skipping:", filepath)
96 with open(filepath, 'wb') as f:
103 # redundant but including to maintain consistency
107 for y in range(sub_h):
108 for x in range(sub_w):
109 i = (sub_x + x) + ((sub_y + y) * pixel_w)
110 rgba = pixels[(i * 4):(i * 4) + 4]
111 c = sum((int(p * 255) << (8 * i)) for i, p in enumerate(rgba))
112 f.write(struct.pack("<I", c))
115 _dice_icon_name_cache = {}
119 x, y, parts_x, parts_y,
120 name_style=None, prefix=""):
122 How to name icons, this is mainly for what name we get in git,
123 the actual names don't really matter, its just nice to have the
124 name match up with something recognizable for commits.
126 if name_style == 'UI_ICONS':
129 if not _dice_icon_name_cache:
133 # Search for eg: DEF_ICON(BRUSH_NUDGE) --> BRUSH_NUDGE
134 re_icon = re.compile(r'^\s*DEF_ICON.*\(\s*([A-Za-z0-9_]+)\s*\).*$')
136 ui_icons_h = os.path.join(SOURCE_DIR, "source", "blender", "editors", "include", "UI_icons.h")
137 with open(ui_icons_h, 'r', encoding="utf-8") as f:
139 match = re_icon.search(l)
141 if l.find('DEF_ICON_BLANK') == -1:
142 icon_name = match.group(1).lower()
144 _dice_icon_name_cache[count] = icon_name
146 # ---- Done with icon cache
148 index = (y * parts_x) + x
149 if index not in _dice_icon_name_cache:
152 icon_name = _dice_icon_name_cache[index]
154 # for debugging its handy to sort by number
155 # ~ id_str = "%03d_%s%s.dat" % (index, prefix, icon_name)
157 id_str = "%s%s.dat" % (prefix, icon_name)
159 elif name_style == "":
160 # flip so icons are numbered from top-left
161 # because new icons will be added at the bottom
162 y_flip = parts_y - (y + 1)
163 id_str = "%s%02xx%02x.dat" % (prefix, x, y_flip)
165 raise Exception("Invalid '--name_style' arg")
171 filepath, output, output_prefix, name_style,
173 minx, miny, maxx, maxy,
174 minx_icon, miny_icon, maxx_icon, maxy_icon,
175 spacex_icon, spacey_icon,
179 minx, miny, maxx, maxy,
180 minx_icon, miny_icon, maxx_icon, maxy_icon,
181 spacex_icon, spacey_icon) == 0)
183 pixels, pixel_w, pixel_h = image_from_file(filepath)
185 if not (pixel_w and pixel_h):
186 print("Image not found %r!" % filepath)
189 if not os.path.exists(output):
193 pixels_w_clip = pixel_w
194 pixels_h_clip = pixel_h
196 icon_w = pixels_w_clip // parts_x
197 icon_h = pixels_h_clip // parts_y
201 pixels_w_clip = pixel_w - (minx + maxx)
202 pixels_h_clip = pixel_h - (miny + maxy)
204 icon_w = (pixels_w_clip - ((parts_x - 1) * spacex_icon)) // parts_x
205 icon_h = (pixels_h_clip - ((parts_y - 1) * spacey_icon)) // parts_y
206 icon_w_clip = icon_w - (minx_icon + maxx_icon)
207 icon_h_clip = icon_h - (miny_icon + maxy_icon)
209 print(pixel_w, pixel_h, icon_w, icon_h)
211 for x in range(parts_x):
212 for y in range(parts_y):
213 id_str = dice_icon_name(
216 name_style=name_style, prefix=output_prefix
221 filepath = os.path.join(output, id_str)
223 print(" writing:", filepath)
230 sub_x = minx + ((x * (icon_w + spacex_icon)) + minx_icon)
231 sub_y = miny + ((y * (icon_h + spacey_icon)) + miny_icon)
233 write_subimage(sub_x, sub_y, icon_w_clip, icon_h_clip,
235 pixels, pixel_w, pixel_h)
242 epilog = "Run this after updating the SVG file"
249 argv = argv[argv.index("--") + 1:]
251 parser = argparse.ArgumentParser(description=__doc__, epilog=epilog)
255 "--image", dest="image", metavar='FILE',
259 "--output", dest="output", metavar='DIR',
260 help="Output directory",
263 "--output_prefix", dest="output_prefix", metavar='STRING',
264 help="Output prefix",
269 "--name_style", dest="name_style", metavar='ENUM', type=str,
270 choices=('', 'UI_ICONS'),
271 help="The metod used for naming output data",
274 # Options for dicing up the image
276 "--parts_x", dest="parts_x", metavar='INT', type=int,
280 "--parts_y", dest="parts_y", metavar='INT', type=int,
284 _help = "Inset from the outer edge (in pixels)"
285 parser.add_argument("--minx", dest="minx", metavar='INT', type=int, help=_help)
286 parser.add_argument("--miny", dest="miny", metavar='INT', type=int, help=_help)
287 parser.add_argument("--maxx", dest="maxx", metavar='INT', type=int, help=_help)
288 parser.add_argument("--maxy", dest="maxy", metavar='INT', type=int, help=_help)
290 _help = "Inset from each icons bounds (in pixels)"
291 parser.add_argument("--minx_icon", dest="minx_icon", metavar='INT', type=int, help=_help)
292 parser.add_argument("--miny_icon", dest="miny_icon", metavar='INT', type=int, help=_help)
293 parser.add_argument("--maxx_icon", dest="maxx_icon", metavar='INT', type=int, help=_help)
294 parser.add_argument("--maxy_icon", dest="maxy_icon", metavar='INT', type=int, help=_help)
296 _help = "Empty space between icons"
297 parser.add_argument("--spacex_icon", dest="spacex_icon", metavar='INT', type=int, help=_help)
298 parser.add_argument("--spacey_icon", dest="spacey_icon", metavar='INT', type=int, help=_help)
302 args = parser.parse_args(argv)
305 print("No args given!")
309 dice(args.image, args.output, args.output_prefix, args.name_style,
310 args.parts_x, args.parts_y,
311 args.minx, args.miny, args.maxx, args.maxy,
312 args.minx_icon, args.miny_icon, args.maxx_icon, args.maxy_icon,
313 args.spacex_icon, args.spacey_icon,
317 if __name__ == "__main__":