More decode cleanup

This commit is contained in:
Andrew Cassidy 2021-04-09 23:18:40 -07:00
parent 8a85f5c920
commit 76da50d003
5 changed files with 72 additions and 39 deletions

View File

@ -1,15 +1,25 @@
from PIL import Image
from typing import List
import pathlib
import click import click
from quicktex.cli.encode import encode
from quicktex.cli.decode import decode
@click.group() def get_decoded_extensions(feature: str = 'open') -> List[str]:
def cli(): extensions = Image.registered_extensions() # using registered_extensions() triggers lazy loading of format data
"""Encode and Decode various image formats""" formats = getattr(Image, feature.upper()).keys()
return [ext for ext, fmt in extensions.items() if fmt in formats]
cli.add_command(encode) def validate_decoded_extension(ctx, param, value):
cli.add_command(decode) if value[0] != '.':
value = '.' + value
if __name__ == '__main__': if value not in decoded_extensions:
cli() raise click.BadParameter(f'Invalid extension for decoded file. Valid extensions are:\n{decoded_extensions}')
return value
decoded_extensions = get_decoded_extensions()
encoded_extensions = '.dds'

View File

@ -1,7 +1,8 @@
import click import click
import io
import os.path import os.path
import quicktex.dds as dds import quicktex.dds as dds
import quicktex.image_utils
import quicktex.cli
from PIL import Image from PIL import Image
from pathlib import Path from pathlib import Path
@ -11,50 +12,56 @@ from pathlib import Path
@click.option('-r', '--remove', is_flag=True, help="Remove input images after converting.") @click.option('-r', '--remove', is_flag=True, help="Remove input images after converting.")
@click.option('-s', '--suffix', type=str, default='', help="Suffix to append to output file(s). Ignored if output is a single file.") @click.option('-s', '--suffix', type=str, default='', help="Suffix to append to output file(s). Ignored if output is a single file.")
@click.option('-x', '--extension', @click.option('-x', '--extension',
type=str, default='png', show_default=True, callback=quicktex.cli.validate_decoded_extension,
type=str, default='.png', show_default=True,
help="Extension to use for output. Ignored if output is a single file. Output filetype is deduced from this") help="Extension to use for output. Ignored if output is a single file. Output filetype is deduced from this")
@click.option('-o', '--output', @click.option('-o', '--output',
type=click.Path(exists=True, readable=True), default=None, type=click.Path(writable=True), default=None,
help="Output file name or directory. If outputting to a file, input filenames must be only a single item. By default, files are decoded in place.") help="Output file or directory. If outputting to a file, input filenames must be only a single item. By default, files are decoded in place.")
@click.argument('filenames', @click.argument('filenames', nargs=-1, type=click.Path(exists=True, readable=True, dir_okay=False))
nargs=-1,
type=click.Path(exists=True, readable=True))
def decode(flip, remove, suffix, extension, output, filenames): def decode(flip, remove, suffix, extension, output, filenames):
"""Decode an input texture file to an image""" """Decode DDS files to images."""
assert len(filenames) != 0, 'No input files provided.' if len(filenames) < 1:
raise click.BadArgumentUsage('No input files were provided.')
# decode in place
def make_outpath(p):
return p.with_name(p.stem + suffix + extension)
if output: if output:
outpath = Path(output) outpath = Path(output)
if outpath.is_file(): if outpath.is_file():
# decode to a file # decode to a file
assert len(filenames) == 1, 'Provided an output file with multiple inputs.' if len(filenames) > 1:
raise click.BadOptionUsage('output', 'Output is a single file, but multiple input files were provided.')
if outpath.suffix not in quicktex.cli.decoded_extensions:
raise click.BadOptionUsage('output', f'File has incorrect extension for decoded file. Valid extensions are:\n{quicktex.cli.decoded_extensions}')
# noinspection PyUnusedLocal
def make_outpath(p): def make_outpath(p):
return outpath return outpath
else: else:
# decode to directory # decode to directory
def make_outpath(p): def make_outpath(p):
return outpath / (p.stem + suffix + '.' + extension) return outpath / (p.stem + suffix + extension)
else:
# decode in place
def make_outpath(p):
return p.with_name(p.stem + suffix + '.' + extension)
for filename in filenames: with click.progressbar(filenames, show_eta=False, show_pos=True, item_show_func=lambda x: str(x) if x else '') as bar:
filepath = Path(filename) for filename in bar:
outpath = make_outpath(filepath) filepath = Path(filename)
if filepath.suffix != '.dds':
raise click.BadArgumentUsage(f"Input file '{filename}' is not a DDS file")
assert filepath.is_file(), f"{filename} is not a file!" image = dds.read(filepath).decode()
assert filepath.suffix == '.dds', f"{filename} is not a DDS file!"
ddsfile = dds.read(filepath) if flip:
image = ddsfile.decode() image = image.transpose(Image.FLIP_TOP_BOTTOM)
if flip: image.save(make_outpath(filepath))
image = image.transpose(Image.FLIP_TOP_BOTTOM)
image.save(outpath) if remove:
os.remove(filepath)
if remove:
os.remove(filepath) if __name__ == '__main__':
decode()

16
quicktex/cli/quicktex.py Normal file
View File

@ -0,0 +1,16 @@
import click
from quicktex.cli.encode import encode
from quicktex.cli.decode import decode
@click.group()
@click.version_option()
def cli():
"""Encode and Decode various image formats"""
cli.add_command(encode)
cli.add_command(decode)
if __name__ == '__main__':
cli()

View File

@ -1,11 +1,11 @@
"""Various utilities for working with Pillow images""" """Various utilities for working with Pillow images"""
from PIL import Image from PIL import Image
import typing from typing import List, Tuple, Optional
import math import math
def mip_sizes(dimensions: typing.Tuple[int, int], mip_count: typing.Optional[int] = None) -> typing.List[typing.Tuple[int, int]]: def mip_sizes(dimensions: Tuple[int, int], mip_count: Optional[int] = None) -> List[Tuple[int, int]]:
""" """
Create a chain of mipmap sizes for a given source source size, where each source is half the size of the one before. Create a chain of mipmap sizes for a given source source size, where each source is half the size of the one before.
Note that the division by 2 rounds down. So a 63x63 texture has as its next lowest mipmap level 31x31. And so on. Note that the division by 2 rounds down. So a 63x63 texture has as its next lowest mipmap level 31x31. And so on.

View File

@ -124,7 +124,7 @@ setup(
}, },
entry_points=''' entry_points='''
[console_scripts] [console_scripts]
quicktex=quicktex.cli:cli quicktex=quicktex.cli.quicktex:cli
''', ''',
zip_safe=False, zip_safe=False,
) )