feat: add comprehensive GitHub workflow and development tools
This commit is contained in:
152
app/.venv/Lib/site-packages/zopfli/png.py
Normal file
152
app/.venv/Lib/site-packages/zopfli/png.py
Normal file
@@ -0,0 +1,152 @@
|
||||
import zopfli
|
||||
from zopfli.zopfli import png_optimize as optimize
|
||||
|
||||
__all__ = ["optimize"]
|
||||
|
||||
|
||||
def main(args=None):
|
||||
import argparse
|
||||
import os
|
||||
|
||||
parser = argparse.ArgumentParser(prog="python -m zopfli.png")
|
||||
parser.add_argument("infile")
|
||||
parser.add_argument("outfile")
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="print more info")
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
action="store_true",
|
||||
dest="compress_more",
|
||||
help="compress more: use more iterations (depending on file size).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-y",
|
||||
dest="overwrite",
|
||||
action="store_true",
|
||||
help="do not ask about overwriting files.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lossy_transparent",
|
||||
action="store_true",
|
||||
help="remove colors behind alpha channel 0. No visual difference.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lossy_8bit",
|
||||
action="store_true",
|
||||
help="convert 16-bit per channel image to 8-bit per channel.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--always_zopflify",
|
||||
action="store_true",
|
||||
help="always output the image encoded by Zopfli, even if bigger than original.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q",
|
||||
dest="use_zopfli",
|
||||
action="store_false",
|
||||
help="use quick, but not very good, compression.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--iterations",
|
||||
default=None,
|
||||
type=int,
|
||||
help=(
|
||||
"number of iterations, more iterations makes it slower but provides "
|
||||
"slightly better compression. Default: 15 for small files, 5 for large files."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--filters",
|
||||
dest="filter_strategies",
|
||||
help=(
|
||||
"filter strategies to try: "
|
||||
"0-4: give all scanlines PNG filter type 0-4; "
|
||||
"m: minimum sum; "
|
||||
"e: entropy; "
|
||||
"p: predefined (keep from input, this likely overlaps another strategy); "
|
||||
"b: brute force (experimental). "
|
||||
"By default, if this argument is not given, one that is most likely the best "
|
||||
"for this image is chosen by trying faster compression with each type. "
|
||||
"If this argument is used, all given filter types are tried with slow "
|
||||
"compression and the best result retained. "
|
||||
"A good set of filters to try is --filters=0me."
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--keepchunks",
|
||||
type=lambda s: s.split(","),
|
||||
help=(
|
||||
"keep metadata chunks with these names that would normally be removed, "
|
||||
"e.g. tEXt,zTXt,iTXt,gAMA, ... Due to adding extra data, this increases "
|
||||
"the result size. Keeping bKGD or sBIT chunks may cause additional worse "
|
||||
"compression due to forcing a certain color type, it is advised to not "
|
||||
"keep these for web images because web browsers do not use these chunks. "
|
||||
"By default ZopfliPNG only keeps (and losslessly modifies) the following "
|
||||
"chunks because they are essential: IHDR, PLTE, tRNS, IDAT and IEND."
|
||||
),
|
||||
)
|
||||
|
||||
options = parser.parse_args(args)
|
||||
|
||||
log = print if options.verbose else lambda *_: None
|
||||
|
||||
if options.iterations is not None:
|
||||
num_iterations = num_iterations_large = options.iterations
|
||||
else:
|
||||
# these constants are taken from zopflipng_lib.cc, unlikely to ever change
|
||||
num_iterations, num_iterations_large = 15, 5
|
||||
if options.compress_more:
|
||||
num_iterations *= 4
|
||||
num_iterations_large *= 4
|
||||
|
||||
with open(options.infile, "rb") as f:
|
||||
input_png = f.read()
|
||||
|
||||
log(f"Optimizing {options.infile}")
|
||||
|
||||
result_png = optimize(
|
||||
input_png,
|
||||
verbose=options.verbose,
|
||||
lossy_transparent=options.lossy_transparent,
|
||||
lossy_8bit=options.lossy_8bit,
|
||||
filter_strategies=options.filter_strategies,
|
||||
keepchunks=options.keepchunks,
|
||||
use_zopfli=options.use_zopfli,
|
||||
num_iterations=num_iterations,
|
||||
num_iterations_large=num_iterations_large,
|
||||
)
|
||||
|
||||
input_size = len(input_png)
|
||||
log(f"Input size: {input_size} ({input_size // 1024}K)")
|
||||
result_size = len(result_png)
|
||||
percentage = round(result_size / input_size * 100, 3)
|
||||
log(
|
||||
f"Result size: {result_size} ({result_size // 1024}K). "
|
||||
f"Percentage of original: {percentage}%"
|
||||
)
|
||||
|
||||
if result_size < input_size:
|
||||
log("Result is smaller")
|
||||
elif result_size == input_size:
|
||||
log("Result has exact same size")
|
||||
else:
|
||||
if options.always_zopflify:
|
||||
log("Original was smaller")
|
||||
else:
|
||||
log("Preserving original PNG since it was smaller")
|
||||
# Set output file to input since zopfli didn't improve it.
|
||||
result_png = input_png
|
||||
|
||||
if (
|
||||
not options.overwrite
|
||||
and os.path.isfile(options.outfile)
|
||||
and input(f"File {options.outfile} exists, overwrite? (y/N)\n").strip().lower()
|
||||
!= "y"
|
||||
):
|
||||
return 0
|
||||
|
||||
with open(options.outfile, "wb") as f:
|
||||
f.write(result_png)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user