img2array.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. # Python tool to convert an image to C array for small3dlib.
  2. #
  3. # by drummyfish
  4. # released under CC0 1.0.
  5. import sys
  6. from PIL import Image
  7. def printHelp():
  8. print("Convert image to C array for small3dlib.")
  9. print("usage:\n")
  10. print(" python img2array.py [-xW -yH -h -nS -pT -5] file\n")
  11. print(" -xW set width of the output to W pixels")
  12. print(" -yH set height of the output to H pixels")
  13. print(" -h include header guards (for texture per file)")
  14. print(" -nS use the name S for the texture (defaut: \"texture\")")
  15. print(" -pT use palette from file T and indexed colors (otherwise direct colors)")
  16. print(" -5 use 565 format instead of RGB8")
  17. print(" -c compress (4 bpp, 16 color palette), only with -pT")
  18. print(" -t transpose (store by columns)")
  19. print("");
  20. print("by Miloslav \"drummyfish\" Ciz")
  21. print("released under CC0 1.0")
  22. def rgbTo565(rgb):
  23. return ((rgb[0] >> 3) << 11) | ((rgb[1] >> 2) << 5) | ((rgb[2] >> 3))
  24. if len(sys.argv) < 2:
  25. printHelp()
  26. quit()
  27. FILENAME = ""
  28. PALETTE = ""
  29. USE_PALETTE = False
  30. NAME = "texture"
  31. GUARDS = False
  32. OUT_WIDTH = 64
  33. OUT_HEIGHT = 64
  34. USE_565 = False
  35. TRANSPOSE = False
  36. COMPRESS = False
  37. for s in sys.argv:
  38. if s [:2] == "-x":
  39. OUT_WIDTH = int(s[2:])
  40. elif s [:2] == "-y":
  41. OUT_HEIGHT = int(s[2:])
  42. elif s == "-h":
  43. GUARDS = True
  44. elif s[:2] == "-n":
  45. NAME = s[2:]
  46. elif s[:2] == "-5":
  47. USE_565 = True
  48. elif s[:2] == "-p":
  49. PALETTE = s[2:]
  50. USE_PALETTE = True
  51. elif s[:2] == "-t":
  52. TRANSPOSE = True
  53. elif s[:2] == "-c":
  54. COMPRESS = True
  55. else:
  56. FILENAME = s
  57. if not USE_PALETTE:
  58. COMPRESS = False
  59. imageArray = []
  60. paletteColors = []
  61. paletteArray = []
  62. image = Image.open(FILENAME).convert("RGB")
  63. pixels = image.load()
  64. if USE_PALETTE > 0:
  65. palette = Image.open(PALETTE).convert("RGB")
  66. pixelsPal = palette.load()
  67. for y in range(palette.size[1]):
  68. for x in range(palette.size[0]):
  69. c = pixelsPal[x,y]
  70. paletteColors.append(c)
  71. if USE_565:
  72. paletteArray.append(rgbTo565(c))
  73. else:
  74. paletteArray.append(c[0])
  75. paletteArray.append(c[1])
  76. paletteArray.append(c[2])
  77. image2 = Image.new("RGB",(OUT_WIDTH,OUT_HEIGHT),color="white")
  78. pixels2 = image2.load()
  79. def findClosestColor(pixel,paletteColors):
  80. closestIndex = 0
  81. closestDiff = 1024
  82. # find the index of the closest color:
  83. for i in range(len(paletteColors)):
  84. c = paletteColors[i]
  85. diff = abs(pixel[0] - c[0]) + abs(pixel[1] - c[1]) + abs(pixel[2] - c[2])
  86. if diff < closestDiff:
  87. closestIndex = i
  88. closestDiff = diff
  89. return closestIndex
  90. for y in range(OUT_HEIGHT):
  91. for x in range(OUT_WIDTH):
  92. x2 = y if TRANSPOSE else x
  93. y2 = x if TRANSPOSE else y
  94. coord = (
  95. int(x2 / float(OUT_WIDTH) * image.size[0]),
  96. int(y2 / float(OUT_HEIGHT) * image.size[1]))
  97. pixel = pixels[coord]
  98. if USE_PALETTE:
  99. closestIndex = findClosestColor(pixel,paletteColors)
  100. imageArray.append(closestIndex)
  101. pixels2[x,y] = paletteColors[closestIndex]
  102. else:
  103. if USE_565:
  104. imageArray.append(rgbTo565(pixel))
  105. else:
  106. imageArray.append(pixel[0])
  107. imageArray.append(pixel[1])
  108. imageArray.append(pixel[2])
  109. pixels2[x,y] = pixel
  110. #-----------------------
  111. def compressImageArray(a):
  112. result = []
  113. reducedPalette = []
  114. histogram = [0 for i in range(256)]
  115. for c in a:
  116. histogram[c] += 1
  117. for i in range(16):
  118. maxValue = 0
  119. maxIndex = 0
  120. for j in range(256):
  121. if histogram[j] > maxValue:
  122. maxValue = histogram[j]
  123. maxIndex = j
  124. reducedPalette.append(maxIndex)
  125. histogram[maxIndex] = 0
  126. paletteMap = [findClosestColor(paletteColors[i],[paletteColors[j] for j in reducedPalette]) for i in range(256)]
  127. oneByte = 0
  128. byteCount = 0
  129. for c in a:
  130. if byteCount % 2 == 0:
  131. oneByte = paletteMap[c]
  132. else:
  133. oneByte = (oneByte << 4) | paletteMap[c]
  134. result.append(oneByte)
  135. byteCount += 1
  136. result = reducedPalette + result
  137. return result
  138. def printArray(array, name, sizeString, dataType="const uint8_t"):
  139. print(dataType + " " + name + "[" + sizeString + "] = {")
  140. arrayString = ""
  141. lineLen = 0
  142. for v in array:
  143. item = str(v) + ","
  144. lineLen += len(item)
  145. if lineLen > 80:
  146. arrayString += "\n"
  147. lineLen = len(item)
  148. arrayString += item
  149. print(arrayString[:-1])
  150. print("}; // " + name)
  151. if GUARDS:
  152. print("#ifndef " + NAME.upper() + "_TEXTURE_H")
  153. print("#define " + NAME.upper() + "_TEXTURE_H\n")
  154. if USE_PALETTE:
  155. printArray(paletteArray,NAME + "Palette",str(len(paletteArray)),"const uint16_t" if USE_565 else "const uint8_t")
  156. print("")
  157. print("#define " + NAME.upper() + "_TEXTURE_WIDTH " + str(OUT_WIDTH))
  158. print("#define " + NAME.upper() + "_TEXTURE_HEIGHT " + str(OUT_HEIGHT))
  159. print("")
  160. if COMPRESS:
  161. imageArray = compressImageArray(imageArray)
  162. printArray(imageArray,NAME + "Texture",str(len(imageArray)))
  163. if GUARDS:
  164. print("\n#endif // guard")
  165. image2.save(NAME + "_preview.png")