convert_font2sd.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from __future__ import unicode_literals, print_function
  4. import os
  5. import math
  6. import sys
  7. from xml.dom import minidom
  8. import Image
  9. if sys.version_info[0] < 3:
  10. xrange = xrange
  11. else:
  12. xrange = range
  13. def _open_xml(path):
  14. file = open(path, "r")
  15. font_doc = minidom.parse(file)
  16. del file
  17. return font_doc
  18. class point:
  19. def __init__(self, x, y):
  20. self.dx = x
  21. self.dy = y
  22. def distSq(self):
  23. return self.dx * self.dx + self.dy * self.dy
  24. def copy(self):
  25. return point(self.dx, self.dy)
  26. def compare(g, p, x, y, offsetX, offsetY):
  27. other = g.get(x + offsetX, y + offsetY)
  28. other.dx += offsetX
  29. other.dy += offsetY
  30. if other.distSq() < p.distSq():
  31. return other
  32. return p
  33. class grid:
  34. def __init__(self, w, h):
  35. self.data = [None for x in range(w) for y in range(h)]
  36. self.w = w
  37. self.h = h
  38. def put(self, x, y, val):
  39. self.data[x + y * self.w] = val # .copy()
  40. def get(self, x, y):
  41. if x < 0 or y < 0:
  42. return point(9999, 9999)
  43. if x >= self.w or y >= self.h:
  44. return point(9999, 9999)
  45. return self.data[x + y * self.w].copy()
  46. def generateSD_pass(g):
  47. print("generateSD_pass...")
  48. get = g.get
  49. put = g.put
  50. w = g.w
  51. h = g.h
  52. cmp = compare
  53. xr = xrange
  54. for y in xr(h):
  55. for x in xr(w):
  56. p = get(x, y)
  57. p = cmp(g, p, x, y, -1, 0)
  58. p = cmp(g, p, x, y, 0, -1)
  59. p = cmp(g, p, x, y, -1, -1)
  60. p = cmp(g, p, x, y, 1, -1)
  61. put(x, y, p)
  62. for x in xr(w - 1, -1, -1):
  63. p = get(x, y)
  64. p = cmp(g, p, x, y, 1, 0)
  65. put(x, y, p)
  66. for y in xr(h - 1, -1, -1):
  67. for x in xr(w - 1, -1, -1):
  68. p = get(x, y)
  69. p = cmp(g, p, x, y, 1, 0)
  70. p = cmp(g, p, x, y, 0, 1)
  71. p = cmp(g, p, x, y, -1, 1)
  72. p = cmp(g, p, x, y, 1, 1)
  73. put(x, y, p)
  74. for x in xr(w):
  75. p = get(x, y)
  76. p = cmp(g, p, x, y, 1, 0)
  77. put(x, y, p)
  78. def generateSD(image):
  79. w = image.size[0]
  80. h = image.size[1]
  81. g = grid(w, h)
  82. grid1 = grid(w, h)
  83. grid2 = grid(w, h)
  84. data = image.load()
  85. getpixel = image.im.getpixel
  86. g1put = grid1.put
  87. g2put = grid2.put
  88. pZero = point(0, 0)
  89. pInf = point(9999, 9999)
  90. for y in range(h):
  91. for x in range(w):
  92. alpha = getpixel((x, y))[3]
  93. if alpha < 128:
  94. g1put(x, y, pZero)
  95. g2put(x, y, pInf)
  96. else:
  97. g1put(x, y, pInf)
  98. g2put(x, y, pZero)
  99. generateSD_pass(grid1)
  100. generateSD_pass(grid2)
  101. sd_image = Image.new("L", (w, h))
  102. data = sd_image.getdata()
  103. putpixel = sd_image.im.putpixel
  104. for y in range(h):
  105. for x in range(w):
  106. dist1 = int(math.sqrt(grid1.get(x, y).distSq()))
  107. dist2 = int(math.sqrt(grid2.get(x, y).distSq()))
  108. dist = dist1 - dist2
  109. dist = dist * 10
  110. dist += 128
  111. #dist = dist * 3 + 128
  112. if dist < 0:
  113. dist = 0
  114. if dist > 255:
  115. dist = 255
  116. putpixel((x, y), dist)
  117. return sd_image
  118. def downscaleAttr(el, attr, ds):
  119. v = el.getAttribute(attr)
  120. el.setAttribute(attr, str(int(v) / ds))
  121. def convert(args):
  122. xml_doc = _open_xml(args.bmfont)
  123. xml = xml_doc.documentElement
  124. folder, file = os.path.split(args.bmfont)
  125. if folder:
  126. folder += "/"
  127. pages = xml.getElementsByTagName("pages")[0].getElementsByTagName("page")
  128. downscale = args.scale
  129. for page in pages:
  130. image_file_name = page.getAttribute("file")
  131. image = Image.open(folder + image_file_name)
  132. image.load()
  133. #image = image.crop((0,0, 256, 256))
  134. w = image.size[0]
  135. h = image.size[1]
  136. sd_image = generateSD(image)
  137. sd_image = sd_image.resize(
  138. (w / downscale, h / downscale), Image.ANTIALIAS)
  139. sd_image_name = "sd_" + image_file_name
  140. sd_image.save(folder + sd_image_name, "PNG")
  141. page.setAttribute("file", sd_image_name)
  142. info_el = xml.getElementsByTagName("info")[0]
  143. downscaleAttr(info_el, "size", downscale)
  144. common_el = xml.getElementsByTagName("common")[0]
  145. downscaleAttr(common_el, "lineHeight", downscale)
  146. downscaleAttr(common_el, "base", downscale)
  147. downscaleAttr(common_el, "scaleW", downscale)
  148. downscaleAttr(common_el, "scaleH", downscale)
  149. new_font_xml = folder + "sd_" + file
  150. fileh = open(new_font_xml, "w")
  151. xml_doc.writexml(fileh)
  152. if __name__ == "__main__":
  153. import os
  154. import sys
  155. folder = os.path.split(os.path.abspath(__file__))[0] + "\\src"
  156. sys.path.append(folder)
  157. # print sys.path
  158. import argparse
  159. parser = argparse.ArgumentParser(
  160. description="convert xml bmfont to signed distance font")
  161. parser.add_argument("bmfont", help="src font")
  162. parser.add_argument(
  163. "-s", "--scale", help="scale factor", type=int, default=1)
  164. import xml_processor
  165. args = parser.parse_args()
  166. convert(args)
  167. #p = xml_processor.XmlProcessor(args)
  168. # p.process()