Add decode command

This commit is contained in:
Andrew Cassidy 2021-04-09 01:33:30 -07:00
parent 8b6ea69300
commit 156880d430
4 changed files with 53 additions and 25 deletions

View File

@ -1,6 +1,6 @@
import click
from encode import encode
from decode import decode
from quicktex.cli.encode import encode
from quicktex.cli.decode import decode
@click.group()

View File

@ -1,16 +1,46 @@
import click
import io
import os
import os.path
import quicktex.dds as dds
from PIL import Image
from pathlib import Path
@click.command()
@click.option('-f', '--flip', type=bool, default=True, show_default=True, help="vertically flip image after converting")
@click.option('-f', '--flip', help="vertically flip image after converting")
@click.option('-r', '--remove', help="remove input images after converting")
@click.option('-o', '--output', help="output file name. Must only specify one input image.")
@click.option('-s', '--suffix', type=str, default='', help="suffix to append to output file(s).")
@click.argument('filenames', nargs=-1, type=click.Path(exists=True))
def decode(flip, remove, output, suffix, filenames):
@click.option('-x', '--extension',
type=str, default='png',
help="extension to use for output. ignored if output is a single file. output filetype is deduced from this")
@click.option('-o', '--output',
type=click.Path(exists=True, readable=True), default='.',
help="output file name or directory. If outputting to a file, input filenames must be only a single item.")
@click.argument('filenames',
nargs=-1,
type=click.Path(exists=True, readable=True))
def decode(flip, remove, suffix, extension, output, filenames):
"""Decode an input texture file to an image"""
assert len(filenames) != 0, 'No input files provided.'
outpath = Path(output)
if outpath.is_file():
assert len(filenames) == 1, 'Provided an output file with multiple inputs.'
def make_outpath(p):
return outpath
else:
def make_outpath(p):
return outpath / (p.stem + suffix + '.' + extension)
for filename in filenames:
assert filename.endswith(".dds"), "Incorrect file extension"
filepath = Path(filename)
outpath = make_outpath(filepath)
assert filepath.is_file(), f"{filename} is not a file!"
assert filepath.suffix == '.dds', f"{filename} is not a DDS file!"
ddsfile = dds.read(filepath)
image = ddsfile.decode()
image.save(outpath)

View File

@ -5,10 +5,10 @@ import os
import struct
import typing
import quicktex.image_utils
import quicktex.s3tc.bc1
import quicktex.s3tc.bc3
import quicktex.s3tc.bc4
import quicktex.s3tc.bc5
import quicktex.s3tc.bc1 as bc1
import quicktex.s3tc.bc3 as bc3
import quicktex.s3tc.bc4 as bc4
import quicktex.s3tc.bc5 as bc5
from PIL import Image
@ -22,10 +22,10 @@ class DDSFormat:
dds_formats = [
DDSFormat('BC1', quicktex.s3tc.bc1.BC1Texture, quicktex.s3tc.bc1.BC1Encoder, quicktex.s3tc.bc1.BC1Decoder, 'DXT1'),
DDSFormat('BC3', quicktex.s3tc.bc3.BC3Texture, quicktex.s3tc.bc3.BC3Encoder, quicktex.s3tc.bc3.BC3Decoder, 'DXT5'),
DDSFormat('BC4', quicktex.s3tc.bc4.BC4Texture, quicktex.s3tc.bc4.BC4Encoder, quicktex.s3tc.bc4.BC4Decoder, 'ATI1'),
DDSFormat('BC5', quicktex.s3tc.bc5.BC5Texture, quicktex.s3tc.bc5.BC5Encoder, quicktex.s3tc.bc5.BC5Decoder, 'ATI2'),
DDSFormat('BC1', bc1.BC1Texture, bc1.BC1Encoder, bc1.BC1Decoder, 'DXT1'),
DDSFormat('BC3', bc3.BC3Texture, bc3.BC3Encoder, bc3.BC3Decoder, 'DXT5'),
DDSFormat('BC4', bc4.BC4Texture, bc4.BC4Encoder, bc4.BC4Decoder, 'ATI1'),
DDSFormat('BC5', bc5.BC5Texture, bc5.BC5Encoder, bc5.BC5Decoder, 'ATI2'),
]
@ -194,16 +194,17 @@ def read(path: os.PathLike) -> DDSFile:
dds = DDSFile()
# READ HEADER
assert struct.unpack('<I', file.read(4)) == DDSFile.header_bytes, "Incorrect DDS header size."
header_bytes = struct.unpack('<I', file.read(4))[0]
assert header_bytes == DDSFile.header_bytes, "Incorrect DDS header size."
dds.flags = DDSFlags(struct.unpack('<I', file.read(4))) # read flags enum
dds.flags = DDSFlags(struct.unpack('<I', file.read(4))[0]) # read flags enum
dds.size = struct.unpack('<2I', file.read(8))[::-1] # read dimensions
dds.pitch, dds.depth, dds.mipmap_count = struct.unpack('<3I', file.read(12))
file.read(44) # skip 44 unused bytes of data
assert struct.unpack('<I', file.read(4)) == 32, "Incorrect pixel format size."
assert struct.unpack('<I', file.read(4))[0] == 32, "Incorrect pixel format size."
dds.pf_flags = PFFlags(struct.unpack('<I', file.read(4)))
dds.pf_flags = PFFlags(struct.unpack('<I', file.read(4))[0])
dds.four_cc = file.read(4).decode()
dds.pixel_size, *pixel_bitmasks = struct.unpack('<5I', file.read(20))
@ -235,7 +236,7 @@ def read(path: os.PathLike) -> DDSFile:
texture = dds.format.texture(*size) # make a new blocktexture of the current mip size
nbytes = file.readinto(texture)
assert nbytes == texture.size, 'Unexpected end of file'
assert nbytes == texture.nbytes, 'Unexpected end of file'
dds.textures.append(texture)

View File

@ -27,11 +27,8 @@ def mip_sizes(dimensions: typing.Tuple[int, int], mip_count: typing.Optional[int
for mip in range(mip_count):
chain.append(dimensions)
dimensions = tuple([max(dim // 2, 1) for dim in dimensions])
if all([dim == 1 for dim in dimensions]):
break # we've reached a 1x1 mip and can get no smaller
dimensions = tuple([max(dim // 2, 1) for dim in dimensions])
return chain