mirror of
https://github.com/drewcassidy/quicktex.git
synced 2024-09-13 06:37:34 +00:00
More decode cleanup
This commit is contained in:
parent
8a85f5c920
commit
76da50d003
@ -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'
|
||||||
|
@ -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
16
quicktex/cli/quicktex.py
Normal 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()
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user