color_convert: add mipmap generation
This commit is contained in:
parent
5f4b0070a6
commit
9b07ba183c
@ -2,7 +2,7 @@ import struct
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from twiddle import texture as twiddle_texture
|
from twiddle import texture as twiddle_texture, npot
|
||||||
|
|
||||||
class color_format:
|
class color_format:
|
||||||
def gbgr1555(r, g, b, a): # nintendo ds
|
def gbgr1555(r, g, b, a): # nintendo ds
|
||||||
@ -59,18 +59,19 @@ def convert_indices(palette, pixels):
|
|||||||
d = pixels[i * 4 + 3]
|
d = pixels[i * 4 + 3]
|
||||||
assert a <= 3 and b <= 3 and c <= 3 and d <= 3, (a, b, c, d)
|
assert a <= 3 and b <= 3 and c <= 3 and d <= 3, (a, b, c, d)
|
||||||
pixel = (d << 6) | (c << 4) | (b << 2) | (a << 0)
|
pixel = (d << 6) | (c << 4) | (b << 2) | (a << 0)
|
||||||
f.write(struct.pack("<B", pixel))
|
assert False, len(palette)
|
||||||
|
yield pixel
|
||||||
elif len(palette) <= 16:
|
elif len(palette) <= 16:
|
||||||
for i in range(len(pixels) // 2):
|
for i in range(len(pixels) // 2):
|
||||||
a = pixels[i * 2 + 0]
|
a = pixels[i * 2 + 0]
|
||||||
b = pixels[i * 2 + 1]
|
b = pixels[i * 2 + 1]
|
||||||
assert a <= 15 and b <= 15, (a, b)
|
assert a <= 15 and b <= 15, (a, b)
|
||||||
pixel = (b << 4) | (a << 0)
|
pixel = (b << 4) | (a << 0)
|
||||||
f.write(struct.pack("<B", pixel))
|
yield pixel
|
||||||
elif len(palette) <= 256:
|
elif len(palette) <= 256:
|
||||||
for pixel in pixels:
|
for pixel in pixels:
|
||||||
assert pixel <= 255
|
assert pixel <= 255
|
||||||
f.write(struct.pack("<B", pixel))
|
yield pixel
|
||||||
else:
|
else:
|
||||||
assert False, len(palette)
|
assert False, len(palette)
|
||||||
|
|
||||||
@ -82,33 +83,74 @@ def pack_indices(f, indices):
|
|||||||
for value in indices:
|
for value in indices:
|
||||||
f.write(struct.pack("<B", value))
|
f.write(struct.pack("<B", value))
|
||||||
|
|
||||||
|
def mip_levels(n):
|
||||||
|
while True:
|
||||||
|
n = n >> 1
|
||||||
|
yield n
|
||||||
|
if n == 1:
|
||||||
|
break
|
||||||
|
|
||||||
|
def generate_mips(im):
|
||||||
|
w, h = im.size
|
||||||
|
assert w == h and npot(w) == w, (w, h)
|
||||||
|
assert w >= 8, (w, h)
|
||||||
|
|
||||||
|
images = [im] + [
|
||||||
|
im.resize(
|
||||||
|
size=(l, l),
|
||||||
|
resample=Image.Resampling.LANCZOS,
|
||||||
|
)
|
||||||
|
for l in mip_levels(w)
|
||||||
|
]
|
||||||
|
|
||||||
|
return list(reversed(images))
|
||||||
|
|
||||||
|
def write_mip(f, mip: Image, is_twiddled: bool, convert):
|
||||||
|
width, height = mip.size
|
||||||
|
print("mip", width, "offset", f"{f.tell():06x}")
|
||||||
|
pixels = list(im.convert("RGBA").getdata())
|
||||||
|
colors = list(convert_colors(convert, pixels))
|
||||||
|
if is_twiddled:
|
||||||
|
new_colors = [0] * width * height
|
||||||
|
max_twiddle_ix = twiddle_texture(new_colors, colors, width, height)
|
||||||
|
assert max_twiddle_ix + 1 == width * height, (max_twiddle_ix, width, height)
|
||||||
|
colors = new_colors[:max_twiddle_ix+1]
|
||||||
|
pack_colors(f, colors)
|
||||||
|
|
||||||
|
def write_mips(f, im: Image, is_twiddled: bool, convert):
|
||||||
|
for mip in generate_mips(im):
|
||||||
|
write_mip(f, mip, is_twiddled, convert)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
in_file = sys.argv[1]
|
in_file = sys.argv[1]
|
||||||
format = sys.argv[2]
|
format = sys.argv[2]
|
||||||
assert sys.argv[3] in {"twiddled", "non_twiddled"}
|
assert sys.argv[3] in {"twiddled", "non_twiddled"}
|
||||||
is_twiddled = sys.argv[3] == "twiddled"
|
is_twiddled = sys.argv[3] == "twiddled"
|
||||||
out_file = sys.argv[4]
|
assert sys.argv[4] in {"mipmapped", "non_mipmapped"}
|
||||||
|
is_mipmapped = sys.argv[4] == "mipmapped"
|
||||||
|
out_file = sys.argv[5]
|
||||||
|
|
||||||
convert = color_format.from_string(format)
|
convert = color_format.from_string(format)
|
||||||
|
|
||||||
with Image.open(in_file) as im:
|
with Image.open(in_file) as im:
|
||||||
width, height = im.size
|
width, height = im.size
|
||||||
if not im.palette:
|
assert width <= 1024 and height <= 1024, (width, height)
|
||||||
pixels = list(im.convert("RGBA").getdata())
|
assert not im.palette
|
||||||
colors = list(convert_colors(convert, pixels))
|
#if not im.palette:
|
||||||
if is_twiddled:
|
if True:
|
||||||
new_colors = [0] * len(colors)
|
|
||||||
twiddle_texture(new_colors, colors, width, height)
|
|
||||||
colors = new_colors
|
|
||||||
with open(out_file, 'wb') as f:
|
with open(out_file, 'wb') as f:
|
||||||
pack_colors(f, colors)
|
if is_mipmapped:
|
||||||
|
f.write(bytes([0] * 6))
|
||||||
|
write_mips(f, im, is_twiddled, convert)
|
||||||
|
else:
|
||||||
|
write_mip(f, im, is_twiddled, convert)
|
||||||
else:
|
else:
|
||||||
pixels = list(im.convert("P").getdata())
|
pixels = list(im.convert("P").getdata())
|
||||||
palette = list(im.palette.colors)
|
palette = list(im.palette.colors)
|
||||||
indices = list(convert_indices(palette, pixels))
|
indices = list(convert_indices(palette, pixels))
|
||||||
colors = list(convert_colors(convert, [(*c, 255) for c in palette]))
|
colors = list(convert_colors(convert, [(*c, 255) for c in palette]))
|
||||||
|
|
||||||
if twiddle:
|
if is_twiddled:
|
||||||
new_indices = [0] * len(indices)
|
new_indices = [0] * len(indices)
|
||||||
twiddle_texture(new_indices, indices, width, height)
|
twiddle_texture(new_indices, indices, width, height)
|
||||||
indices = new_indices
|
indices = new_indices
|
||||||
|
18
twiddle.py
18
twiddle.py
@ -1,5 +1,8 @@
|
|||||||
def log2(n):
|
def log2(n):
|
||||||
return {
|
return {
|
||||||
|
1: 0,
|
||||||
|
2: 1,
|
||||||
|
4: 2,
|
||||||
8: 3,
|
8: 3,
|
||||||
16: 4,
|
16: 4,
|
||||||
32: 5,
|
32: 5,
|
||||||
@ -35,9 +38,24 @@ def from_xy(x, y, width, height):
|
|||||||
|
|
||||||
return twiddle_ix
|
return twiddle_ix
|
||||||
|
|
||||||
|
def npot(v):
|
||||||
|
v -= 1;
|
||||||
|
v |= v >> 1;
|
||||||
|
v |= v >> 2;
|
||||||
|
v |= v >> 4;
|
||||||
|
v |= v >> 8;
|
||||||
|
v |= v >> 16;
|
||||||
|
v += 1;
|
||||||
|
return v
|
||||||
|
|
||||||
def texture(dst, src, width, height):
|
def texture(dst, src, width, height):
|
||||||
|
#pot_height = npot(height)
|
||||||
|
max_twiddle_ix = -1
|
||||||
for y in range(height):
|
for y in range(height):
|
||||||
for x in range(width):
|
for x in range(width):
|
||||||
twiddle_ix = from_xy(x, y, width, height)
|
twiddle_ix = from_xy(x, y, width, height)
|
||||||
value = src[y * width + x]
|
value = src[y * width + x]
|
||||||
dst[twiddle_ix] = value
|
dst[twiddle_ix] = value
|
||||||
|
if twiddle_ix > max_twiddle_ix:
|
||||||
|
max_twiddle_ix = twiddle_ix
|
||||||
|
return max_twiddle_ix
|
||||||
|
Loading…
x
Reference in New Issue
Block a user