2 Converts a tile set from bmp to png for use with the Mac OS X frontend.
5 python osx_bmp2png.py input_file output_file [options]
9 Reads the mask (zero for transparent pixels; non-zero for opaque pixels)
10 from the bmp file, maskfile. maskfile must have the same x and y
11 dimensions as input_file but need not match the color space or bit depth.
12 --mask overrides --tcoord, --terrain, and --transparent.
14 --tcoord hcoord vcoord
15 Reads the color to be treated as transparent for non-terrain tiles
16 from the position (hcoord, vcoord) in the tile set. Both coordinates
17 are non-negative integers indexed from zero. Is overridden by
18 --transparent or --mask if either of those options are set. When not
19 set, the default is to read the color to be treated as transparent from
22 --terrain lx1 ly1 ux1 uy1 ...
23 Sets bounding boxes for parts of the tile set that are to be treated
24 as completely opaque (i.e. as background terrain). Each bound box is
25 given by the coordinates for the lower corner, (lx,ly), and upper
26 corner, (ux,uy), with ux > lx and uy > ly. If not set, the default
27 is to use seven bounding boxes, (0,0) to (256,16), (64,16) to
28 (256,24), (216,80) to (240,88), (48,272) to (152,280), (176,272) to
29 (192,280), (0,632) to (256,688), and (0,712) to (256,736) which are the
30 terrain tiles in Hengband's old 8x8 tile set. Is overridden by the
31 --mask option if that option is set.
33 --transparent red green blue
34 Sets the color, as RGB values between 0 and 255, which will be treated
35 as completely transparent for non-terrain tiles. If not set, uses
36 --tcoord or its default to get the color. Is overridden by the --mask
37 option if that option is set.
40 This is the conversion that was used to prepare the old tile set for the
42 python osx_bmp2png.py 8x8.bmp 8x8.png
44 python osx_bmp2png.py 8x8.bmp 8x8.png -tcoord 0 0 \
45 -terrain 0 0 256 16 64 16 256 24 216 80 240 88 48 272 152 280 \
46 176 272 192 280 0 632 256 688 0 712 256 736
48 This is the conversion that was used to prepare Adam Bolt's tile set for the
50 python osx_bmp2png.py 16x16.bmp 16x16.png --mask 16x16-mask.bmp
53 argparse, PIL (Python imaging library) or equivalent, and
56 The conversions are required because CGImageSourceCreateWithURL() does not
57 handle the bmp format.
64 aparser = argparse.ArgumentParser(description='Converts bmp tile set to png.')
65 aparser.add_argument('input_file', type=str,
66 help='the path to the input tile set')
67 aparser.add_argument('output_file', type=str,
68 help='the path to the converted tile set')
69 aparser.add_argument('--mask', default='',
70 help='File name for BMP file with transparency mask')
71 aparser.add_argument('--tcoord', type=int, nargs=2, default=(0,32),
72 help='Position to be read for transparent color')
73 aparser.add_argument('--terrain', type=int, nargs='+',
74 default=(0,0,256,16,64,16,256,24,216,80,240,88,48,272,152,280,176,272,192,280,0,632,256,688,0,712,256,736),
75 help='Bounding box coordinates for terrain tiles')
76 aparser.add_argument('--transparent', type=int, nargs=3,
77 help='RGB value to be treated as transparent')
79 args = aparser.parse_args()
81 tin = PIL.Image.open(args.input_file)
83 if args.mask == '' and len(args.terrain) % 4 != 0:
84 raise IndexError('Need four values for each bounding box.')
85 for i in range(0, len(args.terrain), 4):
86 if (args.terrain[i] > args.terrain[i + 2] or
87 args.terrain[i + 1] > args.terrain[i + 3]):
88 raise RuntimeError('Lower bounding box coordinate exceeds the upper one')
89 if (args.terrain[i] < 0 or args.terrain[i + 2] > tin.width or
90 args.terrain[i + 1] < 0 or
91 args.terrain[i + 3] > tin.height):
92 raise IndexError('Bounding box outside of image bounds')
94 palette = tin.getpalette()
99 trarr = numpy.array(args.transparent)
101 if (args.tcoord[0] < 0 or args.tcoord[0] >= tin.width or
102 args.tcoord[1] < 0 or
103 args.tcoord[1] >= tin.height):
104 raise IndexError('Coordinates for transparent pixel are out of bounds')
106 trind = tin.getpixel(tuple(args.tcoord))
108 trarr = numpy.array(tin.getpixel(args.tcoord)[0:3])
110 arrin = numpy.array(tin)
112 # Copy over the RGB components.
113 arrout = numpy.empty((tin.height,tin.width,4), dtype='u1')
115 parr = numpy.reshape(numpy.array(palette), (len(palette) // 3, 3))
116 arrout[:,:,0:3] = parr[arrin]
118 arrout[:,:,0:3] = arrin[:,:,0:3]
121 # Set up the alpha channel.
123 # Set up mask for portions of the data set not containing terrain.
124 arrt = numpy.ones((tin.height, tin.width), dtype=numpy.dtype(bool))
125 ind0, ind1 = numpy.meshgrid(numpy.linspace(0, tin.height - 1, tin.height),
126 numpy.linspace(0, tin.width - 1, tin.width), indexing='ij')
127 for i in range(0, len(args.terrain), 4):
128 arrt = numpy.logical_and(arrt,
129 numpy.logical_or(numpy.logical_or(
130 ind1 < args.terrain[i], ind0 < args.terrain[i + 1]),
131 numpy.logical_or(ind1 >= args.terrain[i + 2],
132 ind0 >= args.terrain[i + 3])))
135 # Combine with where the input is equal to transparent color to get
136 # the transparency mask.
138 if trind is not None:
139 arrout[:,:,3] = numpy.where(
140 numpy.logical_and(arrt, arrin == trind),
141 numpy.zeros((tin.height, tin.width), dtype='u1'),
142 255 * numpy.ones((tin.height, tin.width), dtype='u1'))
144 arrout[:,:,3] = numpy.where(
145 numpy.logical_and(arrt, numpy.logical_and(
146 arrout[:,:,0] == trarr[0], numpy.logical_and(
147 arrout[:,:,1] == trarr[1], arrout[:,:,2] == trarr[2]))),
148 numpy.zeros((tin.height, tin.width), dtype='u1'),
149 255 * numpy.ones((tin.height, tin.width), dtype='u1'))
151 arrout[:,:,3] = numpy.where(
152 numpy.logical_and(arrt, numpy.logical_and(
153 arrin[:,:,0] == trarr[0], numpy.logical_and(
154 arrin[:,:,1] == trarr[1], arrin[:,:,2] == trarr[2]))),
155 numpy.zeros((tin.height, tin.width), dtype='u1'),
156 255 * numpy.ones((tin.height, tin.width), dtype='u1'))
160 tmask = PIL.Image.open(args.mask)
161 if tmask.height != tin.height or tmask.width != tin.width:
162 raise RuntimeError('Dimensions of mask do not match input dimensions')
163 arrmask = numpy.array(tmask)
164 if tmask.getpalette():
165 arrout[:,:,3] = numpy.where(arrmask == 0,
166 numpy.zeros((tin.height, tin.width), dtype='u1'),
167 255 * numpy.ones((tin.height, tin.width), dtype='u1'))
169 arrout[:,:,3] = numpy.where(
170 numpy.logical_and(arrmask[:,:,0] == 0,
171 numpy.logical_and(arrmask[:,:,1] == 0,
172 arrmask[:,:,2] == 0)),
173 numpy.zeros((tin.height, tin.width), dtype='u1'),
174 255 * numpy.ones((tin.height, tin.width), dtype='u1'))
178 tout = PIL.Image.fromarray(arrout)
179 tout.save(args.output_file)