2019-09-25 09:51:19 -04:00
|
|
|
# Python tool to convert an image to C array for small3dlib.
|
|
|
|
#
|
|
|
|
# by drummyfish
|
|
|
|
# released under CC0 1.0.
|
|
|
|
|
|
|
|
import sys
|
|
|
|
from PIL import Image
|
|
|
|
|
|
|
|
def printHelp():
|
|
|
|
print("Convert image to C array for small3dlib.")
|
|
|
|
print("usage:\n")
|
|
|
|
print(" python img2array.py [-xW -yH -h -nS -pT -5] file\n")
|
|
|
|
print(" -xW set width of the output to W pixels")
|
|
|
|
print(" -yH set height of the output to H pixels")
|
|
|
|
print(" -h include header guards (for texture per file)")
|
|
|
|
print(" -nS use the name S for the texture (defaut: \"texture\")")
|
|
|
|
print(" -pT use palette from file T and indexed colors (otherwise direct colors)")
|
|
|
|
print(" -5 use 565 format instead of RGB8")
|
2019-10-20 00:28:13 -04:00
|
|
|
print(" -c compress (4 bpp, 16 color palette), only with -pT")
|
2019-10-01 16:19:49 -04:00
|
|
|
print(" -t transpose (store by columns)")
|
2019-09-25 09:51:19 -04:00
|
|
|
print("");
|
|
|
|
print("by Miloslav \"drummyfish\" Ciz")
|
|
|
|
print("released under CC0 1.0")
|
|
|
|
|
|
|
|
def rgbTo565(rgb):
|
|
|
|
return ((rgb[0] >> 3) << 11) | ((rgb[1] >> 2) << 5) | ((rgb[2] >> 3))
|
|
|
|
|
|
|
|
if len(sys.argv) < 2:
|
|
|
|
printHelp()
|
|
|
|
quit()
|
|
|
|
|
|
|
|
FILENAME = ""
|
|
|
|
PALETTE = ""
|
|
|
|
USE_PALETTE = False
|
|
|
|
NAME = "texture"
|
|
|
|
GUARDS = False
|
|
|
|
OUT_WIDTH = 64
|
|
|
|
OUT_HEIGHT = 64
|
|
|
|
USE_565 = False
|
2019-10-01 16:19:49 -04:00
|
|
|
TRANSPOSE = False
|
2019-10-20 00:28:13 -04:00
|
|
|
COMPRESS = False
|
2019-09-25 09:51:19 -04:00
|
|
|
|
|
|
|
for s in sys.argv:
|
|
|
|
if s [:2] == "-x":
|
|
|
|
OUT_WIDTH = int(s[2:])
|
|
|
|
elif s [:2] == "-y":
|
|
|
|
OUT_HEIGHT = int(s[2:])
|
|
|
|
elif s == "-h":
|
|
|
|
GUARDS = True
|
|
|
|
elif s[:2] == "-n":
|
|
|
|
NAME = s[2:]
|
|
|
|
elif s[:2] == "-5":
|
|
|
|
USE_565 = True
|
|
|
|
elif s[:2] == "-p":
|
|
|
|
PALETTE = s[2:]
|
|
|
|
USE_PALETTE = True
|
2019-10-01 16:19:49 -04:00
|
|
|
elif s[:2] == "-t":
|
|
|
|
TRANSPOSE = True
|
2019-10-20 00:28:13 -04:00
|
|
|
elif s[:2] == "-c":
|
|
|
|
COMPRESS = True
|
2019-09-25 09:51:19 -04:00
|
|
|
else:
|
|
|
|
FILENAME = s
|
|
|
|
|
2019-10-20 00:28:13 -04:00
|
|
|
if not USE_PALETTE:
|
|
|
|
COMPRESS = False
|
|
|
|
|
2019-09-25 09:51:19 -04:00
|
|
|
imageArray = []
|
|
|
|
paletteColors = []
|
|
|
|
paletteArray = []
|
|
|
|
|
|
|
|
image = Image.open(FILENAME).convert("RGB")
|
|
|
|
pixels = image.load()
|
|
|
|
|
|
|
|
if USE_PALETTE > 0:
|
|
|
|
palette = Image.open(PALETTE).convert("RGB")
|
|
|
|
pixelsPal = palette.load()
|
|
|
|
|
|
|
|
for y in range(palette.size[1]):
|
|
|
|
for x in range(palette.size[0]):
|
|
|
|
c = pixelsPal[x,y]
|
|
|
|
paletteColors.append(c)
|
|
|
|
|
|
|
|
if USE_565:
|
|
|
|
paletteArray.append(rgbTo565(c))
|
|
|
|
else:
|
|
|
|
paletteArray.append(c[0])
|
|
|
|
paletteArray.append(c[1])
|
|
|
|
paletteArray.append(c[2])
|
|
|
|
|
|
|
|
image2 = Image.new("RGB",(OUT_WIDTH,OUT_HEIGHT),color="white")
|
|
|
|
pixels2 = image2.load()
|
|
|
|
|
2019-10-20 00:28:13 -04:00
|
|
|
def findClosestColor(pixel,paletteColors):
|
|
|
|
closestIndex = 0
|
|
|
|
closestDiff = 1024
|
|
|
|
|
|
|
|
# find the index of the closest color:
|
|
|
|
|
|
|
|
for i in range(len(paletteColors)):
|
|
|
|
c = paletteColors[i]
|
|
|
|
diff = abs(pixel[0] - c[0]) + abs(pixel[1] - c[1]) + abs(pixel[2] - c[2])
|
|
|
|
|
|
|
|
if diff < closestDiff:
|
|
|
|
closestIndex = i
|
|
|
|
closestDiff = diff
|
|
|
|
|
|
|
|
return closestIndex
|
|
|
|
|
2019-09-25 09:51:19 -04:00
|
|
|
for y in range(OUT_HEIGHT):
|
|
|
|
for x in range(OUT_WIDTH):
|
2019-10-01 16:19:49 -04:00
|
|
|
x2 = y if TRANSPOSE else x
|
|
|
|
y2 = x if TRANSPOSE else y
|
|
|
|
|
2019-09-25 09:51:19 -04:00
|
|
|
coord = (
|
2019-10-01 16:19:49 -04:00
|
|
|
int(x2 / float(OUT_WIDTH) * image.size[0]),
|
|
|
|
int(y2 / float(OUT_HEIGHT) * image.size[1]))
|
2019-09-25 09:51:19 -04:00
|
|
|
|
|
|
|
pixel = pixels[coord]
|
|
|
|
|
|
|
|
if USE_PALETTE:
|
2019-10-20 00:28:13 -04:00
|
|
|
closestIndex = findClosestColor(pixel,paletteColors)
|
2019-09-25 09:51:19 -04:00
|
|
|
imageArray.append(closestIndex)
|
|
|
|
pixels2[x,y] = paletteColors[closestIndex]
|
|
|
|
else:
|
|
|
|
if USE_565:
|
|
|
|
imageArray.append(rgbTo565(pixel))
|
|
|
|
else:
|
|
|
|
imageArray.append(pixel[0])
|
|
|
|
imageArray.append(pixel[1])
|
|
|
|
imageArray.append(pixel[2])
|
|
|
|
|
|
|
|
pixels2[x,y] = pixel
|
|
|
|
|
|
|
|
#-----------------------
|
|
|
|
|
2019-10-20 00:28:13 -04:00
|
|
|
def compressImageArray(a):
|
|
|
|
result = []
|
|
|
|
|
|
|
|
reducedPalette = []
|
|
|
|
|
|
|
|
histogram = [0 for i in range(256)]
|
|
|
|
|
|
|
|
for c in a:
|
|
|
|
histogram[c] += 1
|
|
|
|
|
|
|
|
for i in range(16):
|
|
|
|
maxValue = 0
|
|
|
|
maxIndex = 0
|
|
|
|
|
|
|
|
for j in range(256):
|
|
|
|
if histogram[j] > maxValue:
|
|
|
|
maxValue = histogram[j]
|
|
|
|
maxIndex = j
|
|
|
|
|
|
|
|
reducedPalette.append(maxIndex)
|
|
|
|
histogram[maxIndex] = 0
|
|
|
|
|
|
|
|
paletteMap = [findClosestColor(paletteColors[i],[paletteColors[j] for j in reducedPalette]) for i in range(256)]
|
|
|
|
|
|
|
|
oneByte = 0
|
|
|
|
byteCount = 0
|
|
|
|
|
|
|
|
for c in a:
|
|
|
|
if byteCount % 2 == 0:
|
|
|
|
oneByte = paletteMap[c]
|
|
|
|
else:
|
|
|
|
oneByte = (oneByte << 4) | paletteMap[c]
|
|
|
|
result.append(oneByte)
|
|
|
|
|
|
|
|
byteCount += 1
|
|
|
|
|
|
|
|
result = reducedPalette + result
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
2019-09-25 09:51:19 -04:00
|
|
|
def printArray(array, name, sizeString, dataType="const uint8_t"):
|
|
|
|
print(dataType + " " + name + "[" + sizeString + "] = {")
|
|
|
|
arrayString = ""
|
|
|
|
|
|
|
|
lineLen = 0
|
|
|
|
|
|
|
|
for v in array:
|
|
|
|
item = str(v) + ","
|
|
|
|
|
|
|
|
lineLen += len(item)
|
|
|
|
|
|
|
|
if lineLen > 80:
|
|
|
|
arrayString += "\n"
|
|
|
|
lineLen = len(item)
|
|
|
|
|
|
|
|
arrayString += item
|
|
|
|
|
|
|
|
print(arrayString[:-1])
|
|
|
|
print("}; // " + name)
|
|
|
|
|
|
|
|
if GUARDS:
|
|
|
|
print("#ifndef " + NAME.upper() + "_TEXTURE_H")
|
|
|
|
print("#define " + NAME.upper() + "_TEXTURE_H\n")
|
|
|
|
|
|
|
|
if USE_PALETTE:
|
|
|
|
printArray(paletteArray,NAME + "Palette",str(len(paletteArray)),"const uint16_t" if USE_565 else "const uint8_t")
|
|
|
|
print("")
|
|
|
|
|
|
|
|
print("#define " + NAME.upper() + "_TEXTURE_WIDTH " + str(OUT_WIDTH))
|
|
|
|
print("#define " + NAME.upper() + "_TEXTURE_HEIGHT " + str(OUT_HEIGHT))
|
|
|
|
print("")
|
2019-10-20 00:28:13 -04:00
|
|
|
|
|
|
|
if COMPRESS:
|
|
|
|
imageArray = compressImageArray(imageArray)
|
2019-09-25 09:51:19 -04:00
|
|
|
|
|
|
|
printArray(imageArray,NAME + "Texture",str(len(imageArray)))
|
|
|
|
|
|
|
|
if GUARDS:
|
|
|
|
print("\n#endif // guard")
|
|
|
|
|
|
|
|
image2.save(NAME + "_preview.png")
|