├── .github
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── workflows
│ ├── codeql-analysis.yml
│ ├── crunch-bench.yml
│ ├── crunch-lints.yml
│ ├── crunch-linux-ci.yml
│ └── crunch-macos-ci.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE.md
├── Makefile
├── README.md
├── benchmarks
├── Makefile
├── PngSuite.LICENSE
├── README.md
└── img
│ ├── basi0g01.png
│ ├── basi0g02.png
│ ├── basi0g04.png
│ ├── basi0g08.png
│ ├── basi0g16.png
│ ├── basi2c08.png
│ ├── basi2c16.png
│ ├── basi3p01.png
│ ├── basi3p02.png
│ ├── basi3p04.png
│ ├── basi3p08.png
│ ├── basi4a08.png
│ ├── basi4a16.png
│ ├── basi6a08.png
│ ├── basi6a16.png
│ ├── basn0g01.png
│ ├── basn0g02.png
│ ├── basn0g04.png
│ ├── basn0g08.png
│ ├── basn0g16.png
│ ├── basn2c08.png
│ ├── basn2c16.png
│ ├── basn3p01.png
│ ├── basn3p02.png
│ ├── basn3p04.png
│ ├── basn3p08.png
│ ├── basn4a08.png
│ ├── basn4a16.png
│ ├── basn6a08.png
│ ├── basn6a16.png
│ ├── bench.py
│ ├── bgai4a08.png
│ ├── bgai4a16.png
│ ├── bgan6a08.png
│ ├── bgan6a16.png
│ ├── bgbn4a08.png
│ ├── bggn4a16.png
│ ├── bgwn6a08.png
│ ├── bgyn6a16.png
│ ├── ccwn2c08.png
│ ├── ccwn3p08.png
│ ├── cdfn2c08.png
│ ├── cdhn2c08.png
│ ├── cdsn2c08.png
│ ├── cdun2c08.png
│ ├── ch1n3p04.png
│ ├── ch2n3p08.png
│ ├── cm0n0g04.png
│ ├── cm7n0g04.png
│ ├── cm9n0g04.png
│ ├── cs3n2c16.png
│ ├── cs3n3p08.png
│ ├── cs5n2c08.png
│ ├── cs5n3p08.png
│ ├── cs8n2c08.png
│ ├── cs8n3p08.png
│ ├── ct0n0g04.png
│ ├── ct1n0g04.png
│ ├── cten0g04.png
│ ├── ctfn0g04.png
│ ├── ctgn0g04.png
│ ├── cthn0g04.png
│ ├── ctjn0g04.png
│ ├── ctzn0g04.png
│ ├── exif2c08.png
│ ├── f00n0g08.png
│ ├── f00n2c08.png
│ ├── f01n0g08.png
│ ├── f01n2c08.png
│ ├── f02n0g08.png
│ ├── f02n2c08.png
│ ├── f03n0g08.png
│ ├── f03n2c08.png
│ ├── f04n0g08.png
│ ├── f04n2c08.png
│ ├── f99n0g04.png
│ ├── g03n0g16.png
│ ├── g03n2c08.png
│ ├── g03n3p04.png
│ ├── g04n0g16.png
│ ├── g04n2c08.png
│ ├── g04n3p04.png
│ ├── g05n0g16.png
│ ├── g05n2c08.png
│ ├── g05n3p04.png
│ ├── g07n0g16.png
│ ├── g07n2c08.png
│ ├── g07n3p04.png
│ ├── g10n0g16.png
│ ├── g10n2c08.png
│ ├── g10n3p04.png
│ ├── g25n0g16.png
│ ├── g25n2c08.png
│ ├── g25n3p04.png
│ ├── oi1n0g16.png
│ ├── oi1n2c16.png
│ ├── oi2n0g16.png
│ ├── oi2n2c16.png
│ ├── oi4n0g16.png
│ ├── oi4n2c16.png
│ ├── oi9n0g16.png
│ ├── oi9n2c16.png
│ ├── pp0n2c16.png
│ ├── pp0n6a08.png
│ ├── ps1n0g08.png
│ ├── ps1n2c16.png
│ ├── ps2n0g08.png
│ ├── ps2n2c16.png
│ ├── s01i3p01.png
│ ├── s01n3p01.png
│ ├── s02i3p01.png
│ ├── s02n3p01.png
│ ├── s03i3p01.png
│ ├── s03n3p01.png
│ ├── s04i3p01.png
│ ├── s04n3p01.png
│ ├── s05i3p02.png
│ ├── s05n3p02.png
│ ├── s06i3p02.png
│ ├── s06n3p02.png
│ ├── s07i3p02.png
│ ├── s07n3p02.png
│ ├── s08i3p02.png
│ ├── s08n3p02.png
│ ├── s09i3p02.png
│ ├── s09n3p02.png
│ ├── s32i3p04.png
│ ├── s32n3p04.png
│ ├── s33i3p04.png
│ ├── s33n3p04.png
│ ├── s34i3p04.png
│ ├── s34n3p04.png
│ ├── s35i3p04.png
│ ├── s35n3p04.png
│ ├── s36i3p04.png
│ ├── s36n3p04.png
│ ├── s37i3p04.png
│ ├── s37n3p04.png
│ ├── s38i3p04.png
│ ├── s38n3p04.png
│ ├── s39i3p04.png
│ ├── s39n3p04.png
│ ├── s40i3p04.png
│ ├── s40n3p04.png
│ ├── tbbn0g04.png
│ ├── tbbn2c16.png
│ ├── tbbn3p08.png
│ ├── tbgn2c16.png
│ ├── tbgn3p08.png
│ ├── tbrn2c08.png
│ ├── tbwn0g16.png
│ ├── tbwn3p08.png
│ ├── tbyn3p08.png
│ ├── tm3n3p02.png
│ ├── tp0n0g08.png
│ ├── tp0n2c08.png
│ ├── tp0n3p08.png
│ ├── tp1n3p08.png
│ ├── z00n2c08.png
│ ├── z03n2c08.png
│ ├── z06n2c08.png
│ └── z09n2c08.png
├── bin
└── Crunch.app
│ └── Contents
│ ├── Info.plist
│ ├── MacOS
│ └── Crunch
│ └── Resources
│ ├── 01-Load-Up@2x.gif
│ ├── 02-Waiting@2x.gif
│ ├── 03-Crunching@2x.gif
│ ├── 04-Error@2x.gif
│ ├── 04-Success@2x.gif
│ ├── AppSettings.plist
│ ├── Credits.html
│ ├── MainMenu.nib
│ ├── designable.nib
│ └── keyedobjects.nib
│ ├── appIcon.icns
│ ├── clear.html
│ ├── complete-error.html
│ ├── complete-success.html
│ ├── crunch.py
│ ├── execution.html
│ ├── pngquant
│ ├── script
│ ├── start.html
│ ├── waiting.html
│ └── zopflipng
├── coverage.sh
├── dmg-builder.sh
├── docs
├── BENCHMARKS.md
├── Credits.html
├── EXECUTABLE.md
├── MACOSGUI.md
└── SERVICE.md
├── html
├── clear.html
├── complete-error.html
├── complete-success.html
├── execution.html
├── start.html
└── waiting.html
├── img
├── Crunch-icon-2.png
├── Crunch-icon-3.png
├── CrunchIcon.icns
├── CrunchIcon.iconset
│ ├── icon_128x128.png
│ ├── icon_128x128@2x.png
│ ├── icon_16x16.png
│ ├── icon_16x16@2x.png
│ ├── icon_256x256.png
│ ├── icon_256x256@2x.png
│ ├── icon_32x32.png
│ └── icon_32x32@2x.png
├── animations
│ ├── 01-Load-Up@1x.gif
│ ├── 01-Load-Up@2x.gif
│ ├── 02-Waiting@1x.gif
│ ├── 02-Waiting@2x.gif
│ ├── 03-Crunching@1x.gif
│ ├── 03-Crunching@2x.gif
│ ├── 04-Error@1x.gif
│ ├── 04-Error@2x.gif
│ ├── 04-Success@1x.gif
│ └── 04-Success@2x.gif
├── cat-1285634_640-crunch.png
├── cat-1285634_640.png
├── colors-157474_640-crunch.png
├── colors-157474_640.png
├── crunch-ss-2.gif
├── dmg-installer-bg.png
├── fastdots.gif
├── flowers-67839_640-crunch.png
├── flowers-67839_640.png
├── header-arrow-crunch.png
├── header-arrow.psd
├── header-img-crunch.png
├── header-img.psd
├── prairie-679014_640-crunch.png
├── prairie-679014_640.png
├── robot-1214536_640-crunch.png
├── robot-1214536_640.png
├── slowdots.gif
├── suns-rays-478249_640-crunch.png
└── suns-rays-478249_640.png
├── profile
└── Crunch.platypus
├── service
└── Crunch Image(s).workflow
│ └── Contents
│ ├── Info.plist
│ ├── QuickLook
│ └── Preview.png
│ └── document.wflow
├── setup.cfg
├── src
├── __init__.py
├── crunch-gui.sh
├── crunch.py
├── include
│ ├── licenses
│ │ ├── pngquant_license.txt
│ │ └── zopflipng_license.txt
│ ├── pngquant
│ └── zopflipng
├── install-dependencies.sh
├── test_crunch_errors.py
├── test_crunch_execution.py
├── test_crunch_obj.py
└── utils
│ ├── dssim-comparisons.sh
│ └── image-compare.py
├── testfiles
├── cat-cr.png
├── cat.png
├── img with spaces.png
├── robot.png
└── test.txt
├── tox.ini
└── ui
└── MainMenu.nib
├── designable.nib
└── keyedobjects.nib
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help the project improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is including the Crunch application that you are using and the version number of the Crunch application.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Open 'application...'
13 | 2. Do 'this...'
14 | 3. I see 'this...'
15 |
16 | **Expected behavior**
17 | A clear and concise description of what you expected to happen.
18 |
19 | **Screenshots and/or Log Text**
20 | If applicable, add screenshots to help explain your problem. For macOS GUI and macOS right-click menu service problems, please copy and paste the text from the log file `~/.crunch/crunch.log` into your bug report (requires Crunch v3.0.0 or higher).
21 |
22 | **Desktop (please complete the following information):**
23 | - OS: [e.g. macOS]
24 | - Version
25 |
26 | **Additional context**
27 | Add any other context about the problem here.
28 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | # ******** NOTE ********
12 |
13 | name: "CodeQL"
14 |
15 | on:
16 | push:
17 | branches: [ master ]
18 | pull_request:
19 | # The branches below must be a subset of the branches above
20 | branches: [ master ]
21 | schedule:
22 | - cron: '36 6 * * 4'
23 |
24 | jobs:
25 | analyze:
26 | name: Analyze
27 | runs-on: ubuntu-latest
28 |
29 | strategy:
30 | fail-fast: false
31 | matrix:
32 | language: [ 'python' ]
33 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
34 | # Learn more...
35 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
36 |
37 | steps:
38 | - name: Checkout repository
39 | uses: actions/checkout@v2
40 |
41 | # Initializes the CodeQL tools for scanning.
42 | - name: Initialize CodeQL
43 | uses: github/codeql-action/init@v1
44 | with:
45 | languages: ${{ matrix.language }}
46 | # If you wish to specify custom queries, you can do so here or in a config file.
47 | # By default, queries listed here will override any specified in a config file.
48 | # Prefix the list here with "+" to use these queries and those in the config file.
49 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
50 |
51 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
52 | # If this step fails, then you should remove it and run the build manually (see below)
53 | - name: Autobuild
54 | uses: github/codeql-action/autobuild@v1
55 |
56 | # ℹ️ Command-line programs to run using the OS shell.
57 | # 📚 https://git.io/JvXDl
58 |
59 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
60 | # and modify them (or add more) to build your code if your project
61 | # uses a compiled language
62 |
63 | #- run: |
64 | # make bootstrap
65 | # make release
66 |
67 | - name: Perform CodeQL Analysis
68 | uses: github/codeql-action/analyze@v1
69 |
--------------------------------------------------------------------------------
/.github/workflows/crunch-bench.yml:
--------------------------------------------------------------------------------
1 | name: Benchmarks
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | matrix:
10 | os: [ubuntu-latest, macos-latest]
11 | python-version: [3.9]
12 |
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: Set up Python ${{ matrix.python-version }}
16 | uses: actions/setup-python@v2
17 | with:
18 | python-version: ${{ matrix.python-version }}
19 | - name: Display Python version & architecture
20 | run: |
21 | python -c "import sys; print(sys.version)"
22 | python -c "import struct; print(struct.calcsize('P') * 8)"
23 | - name: Install testing dependencies
24 | run: |
25 | pip install --upgrade numpy
26 | - name: Install project dependencies and executable
27 | run: |
28 | make build-dependencies
29 | make install-executable
30 | - name: Run benchmarks
31 | run: |
32 | make benchmark
33 |
--------------------------------------------------------------------------------
/.github/workflows/crunch-lints.yml:
--------------------------------------------------------------------------------
1 | name: Lints
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | matrix:
10 | os: [ubuntu-latest]
11 | python-version: [3.9]
12 |
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: Set up Python ${{ matrix.python-version }}
16 | uses: actions/setup-python@v2
17 | with:
18 | python-version: ${{ matrix.python-version }}
19 | - name: Display Python version & architecture
20 | run: |
21 | python -c "import sys; print(sys.version)"
22 | python -c "import struct; print(struct.calcsize('P') * 8)"
23 | - name: Install testing dependencies
24 | run: |
25 | sudo apt-get install -y shellcheck
26 | pip install --upgrade flake8
27 | - name: Lints
28 | run: |
29 | flake8 --ignore=E501,W503 src/crunch.py
30 | shellcheck --exclude=2046 src/*.sh
31 |
--------------------------------------------------------------------------------
/.github/workflows/crunch-linux-ci.yml:
--------------------------------------------------------------------------------
1 | name: Linux CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | matrix:
10 | os: [ubuntu-latest]
11 | python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
12 |
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: Set up Python ${{ matrix.python-version }}
16 | uses: actions/setup-python@v2
17 | with:
18 | python-version: ${{ matrix.python-version }}
19 | - name: Display Python version & architecture
20 | run: |
21 | python -c "import sys; print(sys.version)"
22 | python -c "import struct; print(struct.calcsize('P') * 8)"
23 | - name: Install testing dependencies
24 | run: |
25 | pip install --upgrade pytest
26 | sudo apt-get install -y pngcheck
27 | - name: Install project dependencies and executable
28 | run: |
29 | make build-dependencies
30 | make install-executable
31 | - name: Python unit tests
32 | run: |
33 | pytest src
34 | make test-valid-png-output
35 |
--------------------------------------------------------------------------------
/.github/workflows/crunch-macos-ci.yml:
--------------------------------------------------------------------------------
1 | name: macOS CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | matrix:
10 | os: [macos-latest]
11 | python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
12 |
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: Set up Python ${{ matrix.python-version }}
16 | uses: actions/setup-python@v2
17 | with:
18 | python-version: ${{ matrix.python-version }}
19 | - name: Display Python version & architecture
20 | run: |
21 | python -c "import sys; print(sys.version)"
22 | python -c "import struct; print(struct.calcsize('P') * 8)"
23 | - name: Install testing dependencies
24 | run: |
25 | pip install --upgrade pytest
26 | brew install pngcheck
27 | - name: Install project dependencies and executable
28 | run: |
29 | make build-dependencies
30 | make install-executable
31 | - name: Python unit tests
32 | run: |
33 | pytest src
34 | make test-valid-png-output
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | .pytest_cache
3 | __pycache__
4 | *.pyc
5 | .tox
6 | .coverage
7 | \.DS_Store
8 | *.dmg
9 | *-checksum.txt
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## Changelog
2 |
3 | ### v5.0.0
4 |
5 | - Remove command line executable cPython 2 support
6 | - Fix GUI fails on recent versions of macOS (reported on macOS 12.3.1) (#92, fix #94)
7 | - Add Apple iconset application icons
8 | - Add icns application icon package and convert to use of this format for production Crunch application
9 | - Convert to use of https://github.com/sindresorhus/create-dmg for GUI macOS dmg installer prep
10 | - Add Makefile build-macos-icns target
11 | - Add Makefile build-macos-installer target
12 | - Add GitHub Actions CI testing
13 | - Remove Travis CI testing configuration file
14 | - Update GUI installation documentation
15 | - Remove dmg installer file from version control (will still be included in releases)
16 | - Remove dmg installer checksum file from version control (will still be included in releases)
17 |
18 | ### v4.0.0
19 |
20 | - Updated pngquant to v2.12.5
21 | - Updated libpng to v1.6.37
22 | - Updated zopflipng to v2.2.0 (@chrissimpkins derivative) - upstream updates through https://github.com/google/zopfli/commit/5d9b71b3c636e9e14a8f7a3f983ff93a1a3793ac
23 | - crunch executable : added ANSI color support in stdout / stderr messages
24 | - crunch.py : PEP 8 source code formatting refactor with `black`
25 | - crunch.py : refactor logging setup approach
26 | - FIX Crunch macOS service : fixed bug in processing of png image file paths that include spaces (thanks Changyoung!)
27 | - FIX crunch executable: command line error handling when no arguments are passed to the command line `crunch` executable
28 | - Added Makefile dist target
29 | - Added Makefile benchmark target
30 | - Added Makefile clean target
31 | - Updated Makefile flake8 linting target
32 | - Updated dmg-builder.sh dmg installer script
33 | - Added new image-compare.py script for comparison of test image file sizes
34 | - Added new dssim-comparisons.sh script for DSSIM analysis of pre/post compression test images
35 | - Added new suite of reference PNG images and benchmarking support in `bench.py` script
36 | - Added continuous benchmarking through Travis CI
37 |
38 | ### v3.0.1
39 |
40 | - modified the macOS GUI idle animation to reduce CPU usage during the application idle stage (issue report #66)
41 |
42 | ### v3.0.0
43 |
44 | - improved quality of pngquant quantization of PNG images across all file sizes
45 | - upgraded embedded pngquant to v2.12.0 (includes reduced pngquant optimization times)
46 | - converted to custom build of zopflipng that is modified for use in the Crunch applications (forked from google/zopfli at git tag zopfli-1.0.2) at git version tag v2.1.0 (source repository is chrissimpkins/zopfli)
47 | - improved zopfli compression ratios for post-quantized and non-quantized in-file sizes under 350kB. Many files are ~33% original file size after they are quantized with pngquant so this affects pre-optimization files up to just over ~1MB in size when the pngquant step is completed (the quantize step yields a modified image binary when it does not lead to larger file size or image quality below Crunch project thresholds, when this does not occur the original file at the original file size is used as the in-file to zopflipng)
48 | - improved zopfli compression speed for post-quantized and non-quantized in-file sizes over 750kB
49 | - eliminate optional PNG chunks by default in all files (reduces file size)
50 | - converted to use of PNG filter = 0 for zopflipng compression of all quantized files (increases compression speed)
51 | - use automated detection of best PNG filter for zopfli compression in all non-quantized files (improves compression)
52 | - remove hidden colors behind alpha channel 0 in files that are not quantized due to low quality or increased file size following pngquant runs
53 | - added new macOS GUI animations with success and fail indicators (thanks Gary Jacobs!)
54 | - added logging of compression data and errors in macOS GUI and macOS right-click menu service tools in a new log file that is generated on the path `~/.crunch/crunch.log`
55 | - updated redirect to /dev/null in install-dependencies.sh compile script for POSIX compliance
56 | - refactored command line option parsing code (thanks Chris Clauss!)
57 | - added new bug reporting template
58 |
59 | ### v2.1.0
60 |
61 | - added automated detection of png image types through read of PNG file signatures
62 | - removed testing for *.png file extension to process files
63 | - added support for simple modification of pnquant and zopflipng paths that are used for optimization, convert to system PATH installed versions by modification of the Python script (issue #40)
64 | - bugfix for macOS GUI and right-click menu service failures with absolute file paths that contain multiple directory levels with space characters
65 |
66 | ### v2.0.2
67 |
68 | - bugfix for failed image optimization with macOS GUI and right-click menu service tools when spaces are included in absolute file paths (issue report #30)
69 |
70 | ### v2.0.1
71 |
72 | - bugfix for failed image optimization on pngquant execution that leads to file sizes below min acceptable (issue report #25)
73 |
74 | ### v2.0.0
75 |
76 | - new `crunch` executable that supports parallel PNG image optimization on *nix platforms (including macOS, Linux, and POSIX compliant application environments on Windows such as Cygwin)
77 | - parallel image processing support added to the Crunch macOS GUI application
78 | - parallel image processing support added to the Crunch Image(s) macOS right-click menu service
79 | - updated embedded pngquant executable to v2.11.7 (January 2018) with statically compiled library dependencies
80 | - updated embedded zopflipng executable to git tag `zopfli-1.0.1`
81 | - convert Crunch macOS GUI files to binary (from XML text files)
82 | - new make target and shell script for pngquant dependency source compile support for *nix platforms
83 | - new make target and shell script for zopflipng dependency source compile support for *nix platforms
84 | - new make target for pngquant dependency uninstall
85 | - new make target for zopflipng depdendency uninstall
86 | - new make target for command line executable install
87 | - new make target for command line executable uninstall
88 | - new make target for Python script testing
89 | - new make target for shell script linting
90 | - fix for make target install of macOS right-click service over a previous install
91 |
92 | ### v1.1.0
93 |
94 | - added a new macOS right click menu service for Crunch PNG image optimization named "Crunch Image(s)"
95 | - added Makefile with target support for macOS service installs and uninstalls
96 | - reformatted About menu text
97 | - added Upgrade documentation to the About menu
98 | - new source repository macOS service documentation, `docs/SERVICE.md`
99 | - repository documentation updates
100 |
101 | ### v1.0.1
102 |
103 | - fix for pngquant library dependencies bug ([Issue report #7](https://github.com/chrissimpkins/Crunch/issues/7))
104 |
105 | ### v1.0.0
106 |
107 | - swanky new UI design with conversion to WebView and animations
108 | - new application icon colors
109 | - modified nib file to fix window size
110 |
111 | ### v0.10.0
112 |
113 | - updated zopflipng to commit https://github.com/google/zopfli/commit/64c6f362fefd56dccbf31906fdb3e31f6a6faf80
114 | - updated pngquant to commit https://github.com/kornelski/pngquant/commit/e50eb86a2f15f05da02fc4343a9d36ebb7d6d790
115 |
116 | ### v0.9.0
117 |
118 | - initial release
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | benchmark:
3 | cd benchmarks && $(MAKE) $@
4 |
5 | build-dependencies:
6 | src/install-dependencies.sh
7 |
8 | build-macos-icns:
9 | rm -rf img/CrunchIcon.iconset
10 | mkdir img/CrunchIcon.iconset
11 | sips -z 16 16 img/Crunch-icon-3.png --out img/CrunchIcon.iconset/icon_16x16.png
12 | sips -z 32 32 img/Crunch-icon-3.png --out img/CrunchIcon.iconset/icon_16x16@2x.png
13 | sips -z 32 32 img/Crunch-icon-3.png --out img/CrunchIcon.iconset/icon_32x32.png
14 | sips -z 64 64 img/Crunch-icon-3.png --out img/CrunchIcon.iconset/icon_32x32@2x.png
15 | sips -z 128 128 img/Crunch-icon-3.png --out img/CrunchIcon.iconset/icon_128x128.png
16 | sips -z 256 256 img/Crunch-icon-3.png --out img/CrunchIcon.iconset/icon_128x128@2x.png
17 | sips -z 256 256 img/Crunch-icon-3.png --out img/CrunchIcon.iconset/icon_256x256.png
18 | sips -z 512 512 img/Crunch-icon-3.png --out img/CrunchIcon.iconset/icon_256x256@2x.png
19 | cd img && iconutil -c icns CrunchIcon.iconset
20 |
21 | build-macos-installer:
22 | # https://github.com/sindresorhus/create-dmg
23 | -rm bin/*.dmg
24 | -cd bin && create-dmg Crunch.app
25 | # create checksum file for the installer
26 | cd bin && mv Crunch*.dmg Crunch-Installer.dmg
27 | cd bin && shasum -a 256 Crunch-Installer.dmg > Crunch-Installer-checksum.txt
28 |
29 | clean:
30 | rm benchmarks/img/*-crunch.png
31 |
32 | dist:
33 | ./dmg-builder.sh
34 |
35 | dist-homebrew:
36 | cask-repair crunch
37 |
38 | install-executable:
39 | sudo cp src/crunch.py /usr/local/bin/crunch
40 | @echo " "
41 | @echo "[*] crunch executable installed on path /usr/local/bin/crunch"
42 | @echo "[*] Usage: $ crunch [image path 1]...[image path n]"
43 |
44 | install-macos-service:
45 | - sudo rm -rf ~/Library/Services/Crunch\ Image\(s\).workflow
46 | sudo cp -R service/Crunch\ Image\(s\).workflow ~/Library/Services/Crunch\ Image\(s\).workflow
47 | @echo " "
48 | @echo "[*] Crunch Image(s) macOS service installed on the path ~/Library/Services/Crunch\ Image\(s\).workflow"
49 | @echo " "
50 | @echo "[*] You can use the Crunch service by right clicking on one or more PNG files, then select Services > Crunch Image(s)"
51 |
52 | uninstall-dependencies:
53 | sudo rm -rf ~/pngquant
54 | sudo rm -rf ~/zopfli
55 | @echo " "
56 | @echo "[*] Dependency removal complete."
57 |
58 | uninstall-executable:
59 | sudo rm /usr/local/bin/crunch
60 | @echo " "
61 | @echo "[*] crunch executable uninstall complete."
62 |
63 | uninstall-macos-service:
64 | sudo rm -rf ~/Library/Services/Crunch\ Image\(s\).workflow
65 | @echo " "
66 | @echo "[*] The Crunch Image(s) macOS service was removed from your system"
67 |
68 | test-coverage:
69 | ./coverage.sh
70 |
71 | test-python:
72 | tox
73 | flake8 src/crunch.py
74 |
75 | test-shell:
76 | shellcheck --exclude=2046 src/*.sh
77 |
78 | test-valid-png-output:
79 | crunch testfiles/*.png
80 | pngcheck testfiles/*-crunch.png
81 | rm testfiles/*-crunch.png
82 |
83 | test: test-python test-shell test-valid-png-output
84 |
85 |
86 | .PHONY: benchmark build-dependencies install-executable install-macos-service uninstall-executable uninstall-macos-service test test-coverage test-python test-shell test-valid-png-output dist
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | [](https://github.com/chrissimpkins/Crunch/releases/latest)
5 | 
6 | 
7 | 
8 | 
9 |
10 | ## About
11 |
12 | Crunch is a tool for lossy PNG image file optimization. It combines selective bit depth, color type, and color palette reduction with zopfli DEFLATE compression algorithm encoding using the pngquant and zopflipng PNG optimization tools. This approach leads to a significant file size gain relative to lossless approaches at the expense of a relatively modest decrease in image quality (see [example images](#examples) below).
13 |
14 | [Continuous benchmark testing](https://github.com/chrissimpkins/Crunch/actions/workflows/crunch-bench.yml) is available in our GitHub Actions CI. Please see the benchmarks directory of this repository for details about the benchmarking approach and instructions on how to execute benchmarks locally on the reference images distributed in this repository or with your own image files.
15 |
16 | Crunch PNG image optimization is available through the following applications that are distributed in this repository:
17 |
18 | - [`crunch`](docs/EXECUTABLE.md) - a *nix command line executable that can be used on macOS, Linux, and Windows POSIX application deployment environments such as Cygwin or the Windows subsystem for Linux
19 | - [Crunch GUI](docs/MACOSGUI.md) - a native macOS drag and drop GUI tool
20 | - [Crunch Image(s)](docs/SERVICE.md) service - a macOS right-click menu service for PNG images selected in the Finder
21 |
22 | ## Installation and Usage
23 |
24 | Installation and usage documentation links for each of the Crunch applications are available below.
25 |
26 | ## `crunch` Command Line Executable
27 |
28 | The `crunch` command line executable can be installed with `make` or manually. Please see the [Install documentation](docs/EXECUTABLE.md#install).
29 |
30 | Enter paths to your PNG image files as arguments to the `crunch` executable. Please see the [Usage documentation](docs/EXECUTABLE.md#usage).
31 |
32 | ## Crunch macOS GUI Application
33 |
34 | The Crunch native macOS GUI application can be installed with Homebrew or the dmg installer that can be downloaded from the [repository releases](https://github.com/chrissimpkins/Crunch/releases/latest). Please see the [Install documentation](docs/MACOSGUI.md#install).
35 |
36 | Drag and drop one or more images on the application window to process your PNG files. Please see the [Usage documentation](docs/MACOSGUI.md#usage).
37 |
38 | ## Crunch Image(s) macOS Right-Click Menu Service
39 |
40 | The macOS right-click menu service "Crunch Image(s)" can be installed with `make` or manually by drag and drop in the macOS Finder. Please see the [Install documentation](docs/SERVICE.md).
41 |
42 | Select one or more PNG images in the Finder, right-click, and select the `Services > Crunch Image(s)` menu item to process your files. Please see the [Usage documentation](docs/SERVICE.md).
43 |
44 | ## Contents
45 |
46 | - [Examples](#examples)
47 | - [Photography Examples](#photography-examples)
48 | - [Illustration Examples](#illustration-examples)
49 | - [Issue Reporting](#issue-reporting)
50 | - [Licenses](#licenses)
51 | - [Contributing](#contributing)
52 | - [Acknowlegments](#acknowledgments)
53 |
54 | ## Examples
55 |
56 | The following examples demonstrate the benefits and disadvantages of the current iteration of Crunch's aggressive space saving optimization strategy. The optimized image files are updated at every Crunch release. In many cases, the PNG optimization decreases file size with an imperceptible impact on image quality. In some cases, degradation of image quality is visible. Visual confirmation of image quality is highly recommended with lossy optimization tools in production settings.
57 |
58 | ## Photography Examples
59 |
60 | ### Cat Image
61 |
62 | - Original Size: 583,398 bytes
63 | - Optimized Size: 196,085 bytes
64 | - DSSIM similarity score: 0.001383
65 | - Percent original size: 33.61%
66 |
67 | ##### Original
68 |
69 |
70 |
71 | ##### Optimized
72 |
73 |
74 |
75 | ### Sun's Rays
76 |
77 | - Original Size: 138,272
78 | - Optimized Size: 66,593
79 | - DSSIM similarity score: 0.000920
80 | - Percent original size: 48.16%
81 |
82 | ##### Original
83 |
84 |
85 |
86 | ##### Optimized
87 |
88 |
89 |
90 |
91 | ### Prairie Image
92 |
93 | - Original Size: 196,794 bytes
94 | - Optimized Size: 77,965 bytes
95 | - DSSIM similarity score: 0.002923
96 | - Percent original size: 39.62%
97 |
98 | ##### Original
99 |
100 |
101 |
102 | ##### Optimized
103 |
104 |
105 |
106 |
107 |
108 | ## Illustration Examples
109 |
110 | ### Robot Image
111 |
112 | - Original Size: 197,193 bytes
113 | - Optimized Size: 67,596 bytes
114 | - DSSIM similarity score: 0.003047
115 | - Percent original size: 34.28%
116 |
117 | ##### Original
118 |
119 |
120 |
121 | ##### Optimized
122 |
123 |
124 |
125 | ### Color Circle Image
126 |
127 | - Original Size: 249,251 bytes
128 | - Optimized Size: 67,135 bytes
129 | - DSSIM similarity score: 0.002450
130 | - Percent original size: 26.93%
131 |
132 | ##### Original
133 |
134 |
135 |
136 | ##### Optimized
137 |
138 |
139 |
140 |
141 | ### Flowers Image
142 |
143 | - Original Size: 440,126 bytes
144 | - Optimized Size: 196,962 bytes
145 | - DSSIM similarity score: 0.001013
146 | - Percent original size: 44.75%
147 |
148 | ##### Original
149 |
150 |
151 |
152 | ##### Optimized
153 |
154 |
155 |
156 | All images above were obtained from [Pixabay](https://pixabay.com) and are dedicated to the public domain under the [CC0 Public Domain Dedication](https://creativecommons.org/publicdomain/zero/1.0/).
157 |
158 | DSSIM testing was performed with v2.10.0 of the [kornelski/dssim tool](https://github.com/kornelski/dssim).
159 |
160 | ## Issue Reporting
161 |
162 | Have you identified a problem? Please [create a new issue report](https://github.com/chrissimpkins/Crunch/issues/new/choose) on the Github issue tracker so that we can address it.
163 |
164 | ## Licenses
165 |
166 | Crunch is licensed under the [MIT license](https://github.com/chrissimpkins/Crunch/blob/master/LICENSE.md).
167 |
168 | ### Embedded Software
169 |
170 | pngquant is licensed under the [Gnu General Public License, version 3](https://github.com/pornel/pngquant/blob/master/COPYRIGHT). The pngquant source code is available [here](https://github.com/pornel/pngquant).
171 |
172 | zopflipng is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). The upstream zopflipng source code is available [here](https://github.com/google/zopfli). The source for the modified zopflipng fork that is used in this project is available [here](https://github.com/chrissimpkins/zopfli).
173 |
174 | See the [LICENSE.md](LICENSE.md) document for details and additional licensing information for this project.
175 |
176 | ## Contributing
177 |
178 | Contributions to the project are warmly welcomed. Please suggest enhancements as new issue reports on this repository. Source contributors should fork the git repository and submit changes as a Github pull request.
179 |
180 | ## Acknowledgments
181 |
182 | Crunch is a simple tool that makes excellent, free, open source software built by others more accessible. The hard work on the optimization engines that run Crunch has been performed by:
183 |
184 | - Lode Vandevenne, Jyrki Alakuijala, and the [zopfli project contributors](https://github.com/google/zopfli/graphs/contributors)
185 | - Kornel Lesiński and the [pngquant project contributors](https://github.com/kornelski/pngquant/graphs/contributors)
186 |
187 | The fantastic macOS GUI animations were designed by [Gary Jacobs](https://github.com/garyjacobs).
188 |
--------------------------------------------------------------------------------
/benchmarks/Makefile:
--------------------------------------------------------------------------------
1 |
2 | benchmark:
3 | cd img && /usr/bin/time -p crunch *.png
4 | cd img && python3 bench.py
5 |
6 |
7 | .PHONY: benchmark
--------------------------------------------------------------------------------
/benchmarks/PngSuite.LICENSE:
--------------------------------------------------------------------------------
1 | PngSuite
2 | --------
3 |
4 | Permission to use, copy, modify and distribute these images for any
5 | purpose and without fee is hereby granted.
6 |
7 |
8 | (c) Willem van Schaik, 1996, 2011
9 |
--------------------------------------------------------------------------------
/benchmarks/README.md:
--------------------------------------------------------------------------------
1 | ## Crunch Benchmarks
2 |
3 | This directory includes reference PNG files from http://www.schaik.com/pngsuite/ for benchmarking Crunch PNG optimization. The image files in this directory are distributed under the [PngSuite.LICENSE](PngSuite.LICENSE).
4 |
5 | Benchmarks are performed against all PNG image files released in the http://www.schaik.com/pngsuite/PngSuite-2017jul19.tgz archive *except* the [reference corrupted files](http://www.schaik.com/pngsuite/#corrupted).
6 |
7 | The [results of continuous benchmark testing are available on Travis CI in the "Benchmarks" build](https://travis-ci.org/chrissimpkins/Crunch).
8 |
9 | ## How to Execute Benchmarks Locally
10 |
11 | Clone this repository, build the dependency tools, install the `crunch` executable, and run benchmarks with the following set of commands:
12 |
13 | ```
14 | $ git clone https://github.com/chrissimpkins/Crunch.git
15 | $ make build-dependencies
16 | $ make install-executable
17 | $ make benchmark
18 | ```
19 |
20 | You will see the results in your terminal.
21 |
22 | The optimized image files remain in the `benchmarks/img` subdirectory after execution for your review. To clean the optimized files, use the following command:
23 |
24 | ```
25 | $ make clean
26 | ```
27 |
28 | ## How to Benchmark With Other Image Sets
29 |
30 | The Python 3 [`bench.py` script](img/bench.py) is portable. Download the script, drop it into the directory with your image files, execute `crunch` on all images in the directory, and execute the benchmark script with:
31 |
32 | ```
33 | $ python3 bench.py
34 | ```
35 |
36 | **Note**: The benchmarking script has a couple of requirements. Optimize **all files** that are included in the directory before you execute the script. Keep the original `*-crunch.png` file paths for all optimized image files.
37 |
--------------------------------------------------------------------------------
/benchmarks/img/basi0g01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi0g01.png
--------------------------------------------------------------------------------
/benchmarks/img/basi0g02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi0g02.png
--------------------------------------------------------------------------------
/benchmarks/img/basi0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/basi0g08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi0g08.png
--------------------------------------------------------------------------------
/benchmarks/img/basi0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/basi2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/basi2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/basi3p01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi3p01.png
--------------------------------------------------------------------------------
/benchmarks/img/basi3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/basi3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/basi3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/basi4a08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi4a08.png
--------------------------------------------------------------------------------
/benchmarks/img/basi4a16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi4a16.png
--------------------------------------------------------------------------------
/benchmarks/img/basi6a08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi6a08.png
--------------------------------------------------------------------------------
/benchmarks/img/basi6a16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basi6a16.png
--------------------------------------------------------------------------------
/benchmarks/img/basn0g01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn0g01.png
--------------------------------------------------------------------------------
/benchmarks/img/basn0g02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn0g02.png
--------------------------------------------------------------------------------
/benchmarks/img/basn0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/basn0g08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn0g08.png
--------------------------------------------------------------------------------
/benchmarks/img/basn0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/basn2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/basn2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/basn3p01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn3p01.png
--------------------------------------------------------------------------------
/benchmarks/img/basn3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/basn3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/basn3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/basn4a08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn4a08.png
--------------------------------------------------------------------------------
/benchmarks/img/basn4a16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn4a16.png
--------------------------------------------------------------------------------
/benchmarks/img/basn6a08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn6a08.png
--------------------------------------------------------------------------------
/benchmarks/img/basn6a16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/basn6a16.png
--------------------------------------------------------------------------------
/benchmarks/img/bench.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # ----------------------------------------
4 | #
5 | # Copyright (c) 2019 Christopher Simpkins
6 | # MIT license
7 | #
8 | # ---------------------------------------
9 |
10 |
11 | import glob
12 | import os
13 |
14 |
15 | def grouped(iterable, n):
16 | return zip(*[iter(iterable)] * n)
17 |
18 |
19 | paths = sorted(glob.glob("*.png"))
20 |
21 | percent_list = []
22 | pre_size_list = []
23 | post_size_list = []
24 |
25 | for path_a, path_b in grouped(paths, 2):
26 | if "-crunch" in path_a:
27 | post_path = path_a
28 | pre_path = path_b
29 | else:
30 | post_path = path_b
31 | pre_path = path_a
32 |
33 | # assert that we are testing the correct pairs of files
34 | assert f"{pre_path[:-4]}-crunch.png" == post_path
35 |
36 | pre_size = os.path.getsize(pre_path)
37 | post_size = os.path.getsize(post_path)
38 | percent_size = (post_size / pre_size) * 100
39 |
40 | percent_list.append(percent_size)
41 | pre_size_list.append(pre_size)
42 | post_size_list.append(post_size)
43 |
44 | print(f"{post_path}: {percent_size:.2f}%")
45 |
46 |
47 | mean = sum(percent_list) / len(percent_list)
48 | total_initial_size = sum(pre_size_list)
49 | total_final_size = sum(post_size_list)
50 | delta = total_initial_size - total_final_size
51 |
52 | print(f"\nInitial:\t{total_initial_size:>8} B")
53 | print(f"Final: \t{total_final_size:>8} B")
54 | print(f"Delta: -{delta} B")
55 | print(f"Mean: {mean:.2f}%")
56 | try:
57 | import numpy as np
58 |
59 | a = np.array(percent_list)
60 | stdev = np.std(a, dtype=np.float64)
61 | print(f"SD: {stdev:.2f}%")
62 | except Exception:
63 | pass
64 |
--------------------------------------------------------------------------------
/benchmarks/img/bgai4a08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/bgai4a08.png
--------------------------------------------------------------------------------
/benchmarks/img/bgai4a16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/bgai4a16.png
--------------------------------------------------------------------------------
/benchmarks/img/bgan6a08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/bgan6a08.png
--------------------------------------------------------------------------------
/benchmarks/img/bgan6a16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/bgan6a16.png
--------------------------------------------------------------------------------
/benchmarks/img/bgbn4a08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/bgbn4a08.png
--------------------------------------------------------------------------------
/benchmarks/img/bggn4a16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/bggn4a16.png
--------------------------------------------------------------------------------
/benchmarks/img/bgwn6a08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/bgwn6a08.png
--------------------------------------------------------------------------------
/benchmarks/img/bgyn6a16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/bgyn6a16.png
--------------------------------------------------------------------------------
/benchmarks/img/ccwn2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ccwn2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/ccwn3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ccwn3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/cdfn2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cdfn2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/cdhn2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cdhn2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/cdsn2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cdsn2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/cdun2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cdun2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/ch1n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ch1n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/ch2n3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ch2n3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/cm0n0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cm0n0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/cm7n0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cm7n0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/cm9n0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cm9n0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/cs3n2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cs3n2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/cs3n3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cs3n3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/cs5n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cs5n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/cs5n3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cs5n3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/cs8n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cs8n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/cs8n3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cs8n3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/ct0n0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ct0n0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/ct1n0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ct1n0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/cten0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cten0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/ctfn0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ctfn0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/ctgn0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ctgn0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/cthn0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/cthn0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/ctjn0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ctjn0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/ctzn0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ctzn0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/exif2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/exif2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/f00n0g08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/f00n0g08.png
--------------------------------------------------------------------------------
/benchmarks/img/f00n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/f00n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/f01n0g08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/f01n0g08.png
--------------------------------------------------------------------------------
/benchmarks/img/f01n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/f01n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/f02n0g08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/f02n0g08.png
--------------------------------------------------------------------------------
/benchmarks/img/f02n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/f02n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/f03n0g08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/f03n0g08.png
--------------------------------------------------------------------------------
/benchmarks/img/f03n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/f03n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/f04n0g08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/f04n0g08.png
--------------------------------------------------------------------------------
/benchmarks/img/f04n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/f04n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/f99n0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/f99n0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/g03n0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g03n0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/g03n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g03n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/g03n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g03n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/g04n0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g04n0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/g04n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g04n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/g04n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g04n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/g05n0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g05n0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/g05n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g05n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/g05n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g05n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/g07n0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g07n0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/g07n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g07n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/g07n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g07n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/g10n0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g10n0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/g10n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g10n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/g10n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g10n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/g25n0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g25n0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/g25n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g25n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/g25n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/g25n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/oi1n0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/oi1n0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/oi1n2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/oi1n2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/oi2n0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/oi2n0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/oi2n2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/oi2n2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/oi4n0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/oi4n0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/oi4n2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/oi4n2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/oi9n0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/oi9n0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/oi9n2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/oi9n2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/pp0n2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/pp0n2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/pp0n6a08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/pp0n6a08.png
--------------------------------------------------------------------------------
/benchmarks/img/ps1n0g08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ps1n0g08.png
--------------------------------------------------------------------------------
/benchmarks/img/ps1n2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ps1n2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/ps2n0g08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ps2n0g08.png
--------------------------------------------------------------------------------
/benchmarks/img/ps2n2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/ps2n2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/s01i3p01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s01i3p01.png
--------------------------------------------------------------------------------
/benchmarks/img/s01n3p01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s01n3p01.png
--------------------------------------------------------------------------------
/benchmarks/img/s02i3p01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s02i3p01.png
--------------------------------------------------------------------------------
/benchmarks/img/s02n3p01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s02n3p01.png
--------------------------------------------------------------------------------
/benchmarks/img/s03i3p01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s03i3p01.png
--------------------------------------------------------------------------------
/benchmarks/img/s03n3p01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s03n3p01.png
--------------------------------------------------------------------------------
/benchmarks/img/s04i3p01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s04i3p01.png
--------------------------------------------------------------------------------
/benchmarks/img/s04n3p01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s04n3p01.png
--------------------------------------------------------------------------------
/benchmarks/img/s05i3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s05i3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/s05n3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s05n3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/s06i3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s06i3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/s06n3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s06n3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/s07i3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s07i3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/s07n3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s07n3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/s08i3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s08i3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/s08n3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s08n3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/s09i3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s09i3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/s09n3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s09n3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/s32i3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s32i3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s32n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s32n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s33i3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s33i3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s33n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s33n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s34i3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s34i3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s34n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s34n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s35i3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s35i3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s35n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s35n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s36i3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s36i3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s36n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s36n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s37i3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s37i3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s37n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s37n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s38i3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s38i3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s38n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s38n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s39i3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s39i3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s39n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s39n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s40i3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s40i3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/s40n3p04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/s40n3p04.png
--------------------------------------------------------------------------------
/benchmarks/img/tbbn0g04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tbbn0g04.png
--------------------------------------------------------------------------------
/benchmarks/img/tbbn2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tbbn2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/tbbn3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tbbn3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/tbgn2c16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tbgn2c16.png
--------------------------------------------------------------------------------
/benchmarks/img/tbgn3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tbgn3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/tbrn2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tbrn2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/tbwn0g16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tbwn0g16.png
--------------------------------------------------------------------------------
/benchmarks/img/tbwn3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tbwn3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/tbyn3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tbyn3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/tm3n3p02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tm3n3p02.png
--------------------------------------------------------------------------------
/benchmarks/img/tp0n0g08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tp0n0g08.png
--------------------------------------------------------------------------------
/benchmarks/img/tp0n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tp0n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/tp0n3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tp0n3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/tp1n3p08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/tp1n3p08.png
--------------------------------------------------------------------------------
/benchmarks/img/z00n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/z00n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/z03n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/z03n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/z06n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/z06n2c08.png
--------------------------------------------------------------------------------
/benchmarks/img/z09n2c08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/benchmarks/img/z09n2c08.png
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Info.plist:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/Info.plist
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/MacOS/Crunch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/MacOS/Crunch
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/01-Load-Up@2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/Resources/01-Load-Up@2x.gif
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/02-Waiting@2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/Resources/02-Waiting@2x.gif
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/03-Crunching@2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/Resources/03-Crunching@2x.gif
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/04-Error@2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/Resources/04-Error@2x.gif
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/04-Success@2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/Resources/04-Success@2x.gif
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/AppSettings.plist:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/Resources/AppSettings.plist
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/Credits.html:
--------------------------------------------------------------------------------
1 |
3 |
A PNG file optimization tool built on pngquant and zopflipng
4 |
16 |
17 |
18 |
Upgrade
19 |
The latest release can be found
20 |
21 | here
22 | . A
23 |
24 | changelog
25 | is available for review of changes to the application since your installed version was released.
26 |
27 |
To upgrade with Homebrew, enter the following command in your terminal:
28 |
29 | $ brew cask upgrade
30 |
31 |
32 |
Other Crunch Tools
33 |
The Crunch image optimization approach is also available through a macOS right-click menu service and a command line
34 | executable. See the
35 |
36 | documentation on the repository README
37 | for installation and usage details.
38 |
39 |
40 |
Issue Reporting
41 |
Please use
42 |
43 | the issue tracker on the Github repository
44 | to report any problems that you have with the Crunch application.
45 |
46 |
Embedded Software Licenses
47 |
60 |
61 |
Animations
62 |
The Crunch GUI animations were designed by
63 | Gary Jacobs and are licensed under the MIT License.
64 |
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/MainMenu.nib/keyedobjects.nib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/Resources/MainMenu.nib/keyedobjects.nib
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/appIcon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/Resources/appIcon.icns
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/clear.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
8 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/complete-error.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
8 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |

27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/complete-success.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
8 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |

27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/crunch.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # ==================================================================
4 | # crunch
5 | # A PNG file optimization tool built on pngquant and zopflipng
6 | #
7 | # Copyright 2019 Christopher Simpkins
8 | # MIT License
9 | #
10 | # Source Repository: https://github.com/chrissimpkins/Crunch
11 | # ==================================================================
12 |
13 | import sys
14 | import os
15 | import shutil
16 | import struct
17 | import subprocess
18 | import time
19 | from subprocess import CalledProcessError
20 |
21 | from multiprocessing import Lock, Pool, cpu_count
22 |
23 | # Locks
24 | stdstream_lock = Lock()
25 | logging_lock = Lock()
26 |
27 | # Application Constants
28 | VERSION = "5.0.0"
29 | VERSION_STRING = "crunch v" + VERSION
30 |
31 | # Processor Constant
32 | # - Modify this to an integer value if you want to fix the number of
33 | # processes spawned during execution. The process number is
34 | # automatically defined during source execution when this is defined
35 | # as a value of 0
36 | PROCESSES = 0
37 |
38 | # Dependency Path Constants for Command Line Executable
39 | # - Redefine these path strings to use system-installed versions of
40 | # pngquant and zopflipng (e.g. to "/usr/local/bin/[executable]")
41 | PNGQUANT_CLI_PATH = os.path.join(os.path.expanduser("~"), "pngquant", "pngquant")
42 | ZOPFLIPNG_CLI_PATH = os.path.join(os.path.expanduser("~"), "zopfli", "zopflipng")
43 |
44 | # Crunch Directory (dot directory in $HOME)
45 | CRUNCH_DOT_DIRECTORY = os.path.join(os.path.expanduser("~"), ".crunch")
46 |
47 | # Log File Path Constants
48 | LOGFILE_PATH = os.path.join(CRUNCH_DOT_DIRECTORY, "crunch.log")
49 |
50 | HELP_STRING = """
51 | ==================================================
52 | crunch
53 | Copyright 2019 Christopher Simpkins
54 | MIT License
55 |
56 | Source: https://github.com/chrissimpkins/Crunch
57 | ==================================================
58 |
59 | crunch is a command line executable that performs lossy optimization of one or more png image files with pngquant and zopflipng.
60 |
61 | Usage:
62 | $ crunch [image path 1]...[image path n]
63 |
64 | Options:
65 | --help, -h application help
66 | --usage application usage
67 | --version, -v application version
68 | """
69 |
70 | USAGE = "$ crunch [image path 1]...[image path n]"
71 |
72 |
73 | def main(argv):
74 |
75 | # Create the Crunch dot directory in $HOME if it does not exist
76 | # Only used for macOS GUI and macOS right-click menu service execution
77 | if len(argv) > 0 and argv[0] in ("--gui", "--service"):
78 | if not os.path.isdir(CRUNCH_DOT_DIRECTORY):
79 | os.makedirs(CRUNCH_DOT_DIRECTORY)
80 | # clear the text in the log file before every script execution
81 | # logging is only maintained for the last execution of the script
82 | open(LOGFILE_PATH, "w").close()
83 |
84 | # ////////////////////////
85 | # ANSI COLOR DEFINITIONS
86 | # ////////////////////////
87 | if not is_gui(sys.argv):
88 | ERROR_STRING = "[ " + format_ansi_red("!") + " ]"
89 | else:
90 | ERROR_STRING = "[ ! ]"
91 |
92 | # //////////////////////////////////
93 | # CONFIRM ARGUMENT PRESENT
94 | # //////////////////////////////////
95 |
96 | if len(argv) == 0:
97 | sys.stderr.write(
98 | ERROR_STRING + " Please include one or more paths to PNG image files as "
99 | "arguments to the script." + os.linesep
100 | )
101 | sys.exit(1)
102 |
103 | # //////////////////////////////////////
104 | # HELP, USAGE, VERSION option handling
105 | # //////////////////////////////////////
106 |
107 | if argv[0] in ("-v", "--version"):
108 | print(VERSION_STRING)
109 | sys.exit(0)
110 | elif argv[0] in ("-h", "--help"):
111 | print(HELP_STRING)
112 | sys.exit(0)
113 | elif argv[0] == "--usage":
114 | print(USAGE)
115 | sys.exit(0)
116 |
117 | # ////////////////////////
118 | # DEFINE DEPENDENCY PATHS
119 | # ////////////////////////
120 | PNGQUANT_EXE_PATH = get_pngquant_path()
121 | ZOPFLIPNG_EXE_PATH = get_zopflipng_path()
122 |
123 | # ////////////////////
124 | # PARSE PNG_PATH_LIST
125 | # ////////////////////
126 |
127 | if is_gui(argv):
128 | png_path_list = argv[1:]
129 | else:
130 | png_path_list = argv
131 |
132 | # //////////////////////////////////
133 | # COMMAND LINE ERROR HANDLING
134 | # //////////////////////////////////
135 |
136 | NOTPNG_ERROR_FOUND = False
137 | for png_path in png_path_list:
138 | # Not a file test
139 | if not os.path.isfile(png_path): # is not an existing file
140 | sys.stderr.write(
141 | ERROR_STRING
142 | + " '"
143 | + png_path
144 | + "' does not appear to be a valid path to a PNG file"
145 | + os.linesep
146 | )
147 | sys.exit(1) # not a file, abort immediately
148 | # PNG validity test
149 | if not is_valid_png(png_path):
150 | sys.stderr.write(
151 | ERROR_STRING
152 | + " '"
153 | + png_path
154 | + "' is not a valid PNG file."
155 | + os.linesep
156 | )
157 | if is_gui(argv):
158 | log_error(png_path + " is not a valid PNG file.")
159 | NOTPNG_ERROR_FOUND = True
160 |
161 | # Exit after checking all file requests and reporting on all invalid file paths (above)
162 | if NOTPNG_ERROR_FOUND is True:
163 | sys.stderr.write(
164 | "The request was not executed successfully. Please try again with one or more valid PNG files."
165 | + os.linesep
166 | )
167 | if is_gui(argv):
168 | log_error(
169 | "The request was not executed successfully. Please try again with one or more valid PNG files."
170 | )
171 | sys.exit(1)
172 |
173 | # Dependency error handling
174 | if not os.path.exists(PNGQUANT_EXE_PATH):
175 | sys.stderr.write(
176 | ERROR_STRING
177 | + " pngquant executable was not identified on path '"
178 | + PNGQUANT_EXE_PATH
179 | + "'"
180 | + os.linesep
181 | )
182 | if is_gui(argv):
183 | log_error(
184 | "pngquant was not found on the expected path " + PNGQUANT_EXE_PATH
185 | )
186 | sys.exit(1)
187 | elif not os.path.exists(ZOPFLIPNG_EXE_PATH):
188 | sys.stderr.write(
189 | ERROR_STRING
190 | + " zopflipng executable was not identified on path '"
191 | + ZOPFLIPNG_EXE_PATH
192 | + "'"
193 | + os.linesep
194 | )
195 | if is_gui(argv):
196 | log_error(
197 | "zopflipng was not found on the expected path " + ZOPFLIPNG_EXE_PATH
198 | )
199 | sys.exit(1)
200 |
201 | # ////////////////////////////////////
202 | # OPTIMIZATION PROCESSING
203 | # ////////////////////////////////////
204 | print("Crunching ...")
205 |
206 | if len(png_path_list) == 1:
207 | # there is only one PNG file, skip spawning of processes and just optimize it
208 | optimize_png(png_path_list[0])
209 | else:
210 | processes = PROCESSES
211 | # if not defined by user, start by defining spawned processes as number of available cores
212 | if processes == 0:
213 | processes = cpu_count()
214 |
215 | # if total cores available is greater than number of files requested, limit to the latter number
216 | if processes > len(png_path_list):
217 | processes = len(png_path_list)
218 |
219 | print(
220 | "Spawning "
221 | + str(processes)
222 | + " processes to optimize "
223 | + str(len(png_path_list))
224 | + " image files..."
225 | )
226 | p = Pool(processes)
227 | try:
228 | p.map(optimize_png, png_path_list)
229 | except Exception as e:
230 | stdstream_lock.acquire()
231 | sys.stderr.write("-----" + os.linesep)
232 | sys.stderr.write(
233 | ERROR_STRING
234 | + " Error detected during execution of the request."
235 | + os.linesep
236 | )
237 | sys.stderr.write(str(e) + os.linesep)
238 | stdstream_lock.release()
239 | if is_gui(argv):
240 | log_error(str(e))
241 | sys.exit(1)
242 |
243 | # end of successful processing, exit code 0
244 | if is_gui(argv):
245 | log_info("Crunch execution ended.")
246 | sys.exit(0)
247 |
248 |
249 | # ///////////////////////
250 | # FUNCTION DEFINITIONS
251 | # ///////////////////////
252 |
253 |
254 | def optimize_png(png_path):
255 | img = ImageFile(png_path)
256 |
257 | # define pngquant and zopflipng paths
258 | PNGQUANT_EXE_PATH = get_pngquant_path()
259 | ZOPFLIPNG_EXE_PATH = get_zopflipng_path()
260 |
261 | # ////////////////////////
262 | # ANSI COLOR DEFINITIONS
263 | # ////////////////////////
264 | if not is_gui(sys.argv):
265 | ERROR_STRING = "[ " + format_ansi_red("!") + " ]"
266 | else:
267 | ERROR_STRING = "[ ! ]"
268 |
269 | # --------------
270 | # pngquant stage
271 | # --------------
272 | pngquant_options = (
273 | " --quality=80-98 --skip-if-larger --force --strip --speed 1 --ext -crunch.png "
274 | )
275 | pngquant_command = (
276 | PNGQUANT_EXE_PATH + pngquant_options + shellquote(img.pre_filepath)
277 | )
278 | try:
279 | subprocess.check_output(pngquant_command, stderr=subprocess.STDOUT, shell=True)
280 | except CalledProcessError as cpe:
281 | if cpe.returncode == 98:
282 | # this is the status code when file size increases with execution of pngquant.
283 | # ignore at this stage, original file copied at beginning of zopflipng processing
284 | # below if it is not present due to these errors
285 | pass
286 | elif cpe.returncode == 99:
287 | # this is the status code when the image quality falls below the set min value
288 | # ignore at this stage, original lfile copied at beginning of zopflipng processing
289 | # below if it is not present to these errors
290 | pass
291 | else:
292 | stdstream_lock.acquire()
293 | sys.stderr.write(
294 | ERROR_STRING
295 | + " "
296 | + img.pre_filepath
297 | + " processing failed at the pngquant stage."
298 | + os.linesep
299 | )
300 | stdstream_lock.release()
301 | if is_gui(sys.argv):
302 | log_error(
303 | img.pre_filepath
304 | + " processing failed at the pngquant stage. "
305 | + os.linesep
306 | + str(cpe)
307 | )
308 | return None
309 | else:
310 | raise cpe
311 | except Exception as e:
312 | if is_gui(sys.argv):
313 | log_error(
314 | img.pre_filepath
315 | + " processing failed at the pngquant stage. "
316 | + os.linesep
317 | + str(e)
318 | )
319 | return None
320 | else:
321 | raise e
322 |
323 | # ---------------
324 | # zopflipng stage
325 | # ---------------
326 | # use --filters=0 by default for quantized PNG files (based upon testing by CS)
327 | zopflipng_options = " -y --filters=0 "
328 | # confirm that a file with proper path was generated by pngquant
329 | # pngquant does not write expected file path if the file was larger after processing
330 | if not os.path.exists(img.post_filepath):
331 | shutil.copy(img.pre_filepath, img.post_filepath)
332 | # If pngquant did not quantize the file, permit zopflipng to attempt compression with mulitple
333 | # filters. This achieves better compression than the default approach for non-quantized PNG
334 | # files, but takes significantly longer (based upon testing by CS)
335 | zopflipng_options = " -y --lossy_transparent "
336 | zopflipng_command = (
337 | ZOPFLIPNG_EXE_PATH
338 | + zopflipng_options
339 | + shellquote(img.post_filepath)
340 | + " "
341 | + shellquote(img.post_filepath)
342 | )
343 | try:
344 | subprocess.check_output(zopflipng_command, stderr=subprocess.STDOUT, shell=True)
345 | except CalledProcessError as cpe:
346 | stdstream_lock.acquire()
347 | sys.stderr.write(
348 | ERROR_STRING
349 | + " "
350 | + img.pre_filepath
351 | + " processing failed at the zopflipng stage."
352 | + os.linesep
353 | )
354 | stdstream_lock.release()
355 | if is_gui(sys.argv):
356 | log_error(
357 | img.pre_filepath
358 | + " processing failed at the zopflipng stage. "
359 | + os.linesep
360 | + str(cpe)
361 | )
362 | return None
363 | else:
364 | raise cpe
365 | except Exception as e:
366 | if is_gui(sys.argv):
367 | log_error(
368 | img.pre_filepath
369 | + " processing failed at the pngquant stage. "
370 | + os.linesep
371 | + str(e)
372 | )
373 | return None
374 | else:
375 | raise e
376 |
377 | # Check file size post-optimization and report comparison with pre-optimization file
378 | img.get_post_filesize()
379 | percent = img.get_compression_percent()
380 | percent_string = "{0:.2f}%".format(percent)
381 | # if compression occurred, color the percent string green
382 | # otherwise, leave it default text color
383 | if not is_gui(sys.argv) and percent < 100:
384 | percent_string = format_ansi_green(percent_string)
385 |
386 | # report percent original file size / post file path / size (bytes) to stdout (command line executable)
387 | stdstream_lock.acquire()
388 | print(
389 | "[ "
390 | + percent_string
391 | + " ] "
392 | + img.post_filepath
393 | + " ("
394 | + str(img.post_size)
395 | + " bytes)"
396 | )
397 | stdstream_lock.release()
398 |
399 | # report percent original file size / post file path / size (bytes) to log file (macOS GUI + right-click service)
400 | if is_gui(sys.argv):
401 | log_info(
402 | "[ "
403 | + percent_string
404 | + " ] "
405 | + img.post_filepath
406 | + " ("
407 | + str(img.post_size)
408 | + " bytes)"
409 | )
410 |
411 |
412 | # -----------
413 | # Utilities
414 | # -----------
415 |
416 |
417 | def fix_filepath_args(args):
418 | arg_list = []
419 | parsed_filepath = ""
420 | for arg in args:
421 | if arg[0] == "-":
422 | # add command line options
423 | arg_list.append(arg)
424 | elif len(arg) > 2 and "." in arg[1:]:
425 | # if format is `\w+\.\w+`, then this is a filename, not directory
426 | # this is the end of a filepath string that may have had
427 | # spaces in directories prior to this level. Let's recreate
428 | # the entire original path
429 | filepath = parsed_filepath + arg
430 | arg_list.append(filepath)
431 | # reset the temp string that is used to reconstruct the filepaths
432 | parsed_filepath = ""
433 | else:
434 | # if the argument does not end with a .png, then there must have
435 | # been a space in the directory paths, let's add it back
436 | parsed_filepath = parsed_filepath + arg + " "
437 | # return new argument list with fixed filepaths to calling code
438 | return arg_list
439 |
440 |
441 | def get_pngquant_path():
442 | if sys.argv[1] == "--gui":
443 | return "./pngquant"
444 | elif sys.argv[1] == "--service":
445 | return "/Applications/Crunch.app/Contents/Resources/pngquant"
446 | else:
447 | return PNGQUANT_CLI_PATH
448 |
449 |
450 | def get_zopflipng_path():
451 | if sys.argv[1] == "--gui":
452 | return "./zopflipng"
453 | elif sys.argv[1] == "--service":
454 | return "/Applications/Crunch.app/Contents/Resources/zopflipng"
455 | else:
456 | return ZOPFLIPNG_CLI_PATH
457 |
458 |
459 | def is_gui(arglist):
460 | return "--gui" in arglist or "--service" in arglist
461 |
462 |
463 | def is_valid_png(filepath):
464 | # The PNG byte signature (https://www.w3.org/TR/PNG/#5PNG-file-signature)
465 | expected_signature = struct.pack("8B", 137, 80, 78, 71, 13, 10, 26, 10)
466 | # open the file and read first 8 bytes
467 | with open(filepath, "rb") as filer:
468 | signature = filer.read(8)
469 | # return boolean test result for first eight bytes == expected PNG byte signature
470 | return signature == expected_signature
471 |
472 |
473 | def log_error(errmsg):
474 | current_time = time.strftime("%m-%d-%y %H:%M:%S")
475 | logging_lock.acquire()
476 | with open(LOGFILE_PATH, "a") as filewriter:
477 | filewriter.write(current_time + "\tERROR\t" + errmsg + os.linesep)
478 | filewriter.flush()
479 | os.fsync(filewriter.fileno())
480 | logging_lock.release()
481 |
482 |
483 | def log_info(infomsg):
484 | current_time = time.strftime("%m-%d-%y %H:%M:%S")
485 | logging_lock.acquire()
486 | with open(LOGFILE_PATH, "a") as filewriter:
487 | filewriter.write(current_time + "\tINFO\t" + infomsg + os.linesep)
488 | filewriter.flush()
489 | os.fsync(filewriter.fileno())
490 | logging_lock.release()
491 | return None
492 |
493 |
494 | def shellquote(filepath):
495 | return "'" + filepath.replace("'", "'\\''") + "'"
496 |
497 |
498 | def format_ansi_red(text):
499 | if sys.stdout.isatty():
500 | return "\033[0;31m" + text + "\033[0m"
501 | else:
502 | return text
503 |
504 |
505 | def format_ansi_green(text):
506 | if sys.stdout.isatty():
507 | return "\033[0;32m" + text + "\033[0m"
508 | else:
509 | return text
510 |
511 |
512 | # ///////////////////////
513 | # OBJECT DEFINITIONS
514 | # ///////////////////////
515 |
516 |
517 | class ImageFile(object):
518 | def __init__(self, filepath):
519 | self.pre_filepath = filepath
520 | self.post_filepath = self._get_post_filepath()
521 | self.pre_size = self._get_filesize(self.pre_filepath)
522 | self.post_size = 0
523 |
524 | def _get_filesize(self, file_path):
525 | return os.path.getsize(file_path)
526 |
527 | def _get_post_filepath(self):
528 | path, extension = os.path.splitext(self.pre_filepath)
529 | return path + "-crunch" + extension
530 |
531 | def get_post_filesize(self):
532 | self.post_size = self._get_filesize(self.post_filepath)
533 |
534 | def get_compression_percent(self):
535 | ratio = float(self.post_size) / float(self.pre_size)
536 | percent = ratio * 100
537 | return percent
538 |
539 |
540 | if __name__ == "__main__":
541 | # bugfix for macOS GUI / right-click service filepath issue
542 | # when spaces are included in the absolute path to the image
543 | # file. https://github.com/chrissimpkins/Crunch/issues/30
544 | # This workaround reconstructs the original filepaths
545 | # that are split by the shell script into separate arguments
546 | # when there are spaces in the macOS file path
547 | if len(sys.argv) > 1 and sys.argv[1] in ("--gui", "--service"):
548 | arg_list = fix_filepath_args(sys.argv[1:])
549 | main(arg_list)
550 | else:
551 | # the command line executable assumes that users will appropriately quote
552 | # or escape special characters (including spaces) on the command line,
553 | # no need for the special parsing treatment above
554 | main(sys.argv[1:])
555 |
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/execution.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
8 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |

27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/pngquant:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/Resources/pngquant
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/script:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # //////////////////////////////////////////////////////////
4 | #
5 | # crunch-gui.sh
6 | # A shell script that executes Crunch PNG optimization
7 | # for the Crunch macOS GUI application
8 | # Copyright 2018 Christopher Simpkins and contributors
9 | # MIT License
10 | #
11 | # Source: https://github.com/chrissimpkins/Crunch
12 | #
13 | # Note: this file is not intended for direct execution
14 | # on the command line
15 | #
16 | # ///////////////////////////////////////////////////////////
17 |
18 | # UNCOMMENT FOR TESTING ONLY
19 | # python crunch.py --gui "$@"
20 | # exit 0
21 |
22 | # Message on application open (no arguments passed to script on initial open)
23 | if [ $# -eq 0 ]; then
24 | cat waiting.html
25 | exit 0
26 | fi
27 |
28 | cat execution.html
29 |
30 | if ./crunch.py --gui "$@" >/dev/null 2>&1; then
31 | cat clear.html
32 | cat complete-success.html
33 | sleep 2
34 | cat clear.html
35 | cat start.html
36 | sleep 0.8
37 | cat waiting.html
38 | exit 0
39 | else
40 | sleep 0.6
41 | cat clear.html
42 | cat complete-error.html
43 | sleep 2
44 | cat clear.html
45 | cat start.html
46 | sleep 0.8
47 | cat waiting.html
48 | exit 1
49 | fi
50 |
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
19 |
20 |
21 |
22 |
23 |
24 |

25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/waiting.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
18 |
19 |
20 |
21 |
22 |
23 |

24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/bin/Crunch.app/Contents/Resources/zopflipng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/bin/Crunch.app/Contents/Resources/zopflipng
--------------------------------------------------------------------------------
/coverage.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | coverage run --include="src/crunch.py" -m py.test src
4 | coverage report -m
5 |
--------------------------------------------------------------------------------
/dmg-builder.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # This script uses the `create-dmg` script from https://github.com/andreyvit/create-dmg
4 |
5 | create-dmg \
6 | --volname "Crunch Installer" \
7 | --volicon "/Users/chris/Library/Application Support/Platypus/PlatypusIcon-13299.icns" \
8 | --background "img/dmg-installer-bg.png" \
9 | --window-pos 200 120 \
10 | --window-size 800 400 \
11 | --icon-size 100 \
12 | --icon Crunch.app 200 190 \
13 | --hide-extension Crunch.app \
14 | --app-drop-link 600 185 \
15 | Crunch-Installer.dmg \
16 | /Users/chris/code/Crunch/bin
17 |
18 | # create checksum file for the installer
19 | mv Crunch-Installer.dmg installer/Crunch-Installer.dmg
20 | cd installer || exit 1
21 | shasum Crunch-Installer.dmg > Crunch-Installer-checksum.txt
22 | shasum -c Crunch-Installer-checksum.txt
--------------------------------------------------------------------------------
/docs/BENCHMARKS.md:
--------------------------------------------------------------------------------
1 | ## Crunch Benchmarks
2 |
3 | **This is of historical interest only. The versions of tools in this comparison are outdated (including Crunch) and these should no longer be considered valid comparisons of current era tools. Please see the benchmarks directory of the repository for updated benchmark results.**
4 |
5 | PNG optimization benchmarks were executed with Crunch v0.9.0. They use [the PNG Test Corpus files](http://css-ig.net/images/png-test-corpus.zip) that are maintained by Cédric Louvrier on css-ig.net. A broad analysis of available PNG optimization tools is available [here](http://css-ig.net/png-tools-overview) for additional reference.
6 |
7 | In the following table, `Size In` and `Size Out` represent the original and optimized file size in bytes, respectively. `%Original Size` is the percent of the original file size in bytes represented by the final optimized file size. A lower value is better.
8 |
9 | The `DSSIM` value is a measure of dissimilarity between two PNG images. It uses [the SSIM algorithm](https://ece.uwaterloo.ca/%7Ez70wang/research/ssim/) and is calculated with [the open source dssim tool](https://github.com/pornel/dssim) by [@pornel](https://github.com/pornel). This is calculated from the optimized file using the original file as a reference. Interpretation is as follows (from the dssim README):
10 |
11 | > The value returned is 1/SSIM-1, where 0 means identical image, and >0 (unbounded) is amount of difference.
12 |
13 |
14 | | File | Size In | Size Out | % Original Size | DSSIM |
15 | | :-----------------: | ------------:| ----------------:| :----------------: | :------------: |
16 | | 01-c3-c0.png | 18623 | 9325 | 50.07% | 0.00027151 |
17 | | 02-c4-c0.png | 2794 | 1100 | 39.37% | 0.00000754 |
18 | | 03-c6-c2.png | 12771 | 4175 | 32.69% | 0.00000769 |
19 | | 04-c2-f0-f5.png | 1539 | 569 | 36.97% | 0.00000000 |
20 | | 05-c3-c2.png | 1416 | 751 | 53.04% | 0.00012144 |
21 | | 06-c6-c3.png | 12346 | 6809 | 55.15% | 0.00008878 |
22 | | 07-c4-c3.png | 15280 | 7167 | 46.90% | 0.00002324 |
23 | | 08-c0-c3.png | 15855 | 11836 | 74.65% | 0.00008729 |
24 | | 09-c3-tronc.png | 3842 | 2437 | 63.43% | 0.00005157 |
25 | | 10-c3-t-first.png | 2893 | 1214 | 41.96% | 0.00001440 |
26 | | 11-8-to-4bit.png | 3036 | 1346 | 44.33% | 0.00000000 |
27 | | 12-4-to-8bit.png | 11667 | 9875 | 84.64% | 0.00006822 |
28 | | 13-idat.png | 7828 | 997 | 12.74% | 0.00000994 |
29 | | 14-chunks.png | 2032 | 997 | 49.06% | 0.00000994 |
30 | | 15-pal-org.png | 24551 | 16017 | 65.24% | 0.00016014 |
31 | | 16-pal-org-t.png | 24795 | 16133 | 65.07% | 0.00014012 |
32 | | 17-pal-dup.png | 3032 | 1346 | 44.39% | 0.00000000 |
33 | | 18-c2-c3-f.png | 28840 | 12206 | 42.32% | 0.00017952 |
34 | | 19-c6-c3-f.png | 31691 | 11829 | 37.33% | 0.00016088 |
35 | | 20-c6-c4.png | 34975 | 16717 | 47.80% | 0.00003876 |
36 | | 21-rgbdata.png | 49515 | 37759 | 76.26% | 0.00000000 |
37 | | **Mean** | -- | -- | **50.64%** | -- |
38 |
39 |
40 | ## Crunch Optimization vs. Other Commonly Used Applications
41 |
42 | The values in the following table are the percent of the original file size in the resulting image file following optimization with the respective tool. Lower values are better.
43 |
44 | Tests were performed using commands that permit direct comparison to the reference tables in the [PNG Optimization Tools Overview](http://css-ig.net/png-tools-overview) that is maintained by Cédric Louvrier.
45 |
46 | #### ImageOptim Tests
47 |
48 | ImageOptim v1.6.1 was executed via drag and drop into the GUI application.
49 |
50 | #### OptiPNG Tests
51 |
52 | `optipng` v0.7.5 was executed with the command:
53 |
54 | ```
55 | $ optipng -o3 [filepath]
56 | ```
57 |
58 | #### PNGOUT Tests
59 |
60 | `pngout` vSep 20 2015 was executed with the command:
61 |
62 | ```
63 | $ pngout [filepath]
64 | ```
65 |
66 | #### PNGCrush Tests
67 |
68 | `pngcrush` v1.8.0 was executed with the command:
69 |
70 | ```
71 | $ pngcrush -brute -blacken -reduce [filepath]
72 | ```
73 |
74 | Please note that Crunch uses a *lossy* PNG optimization approach that is based upon minimum acceptable image quality whereas the comparison tools are all executed with *lossless* PNG optimization. The selectively lossy approach in Crunch is intentional, explains the file size difference, and should be taken into consideration as you interpret the findings and make decisions about use cases for the tool.
75 |
76 |
77 | | File | Crunch | ImageOptim | OptiPNG | PNGOUT | PNGCrush |
78 | | :-----------------: | :------------:| :---------------:| :----------------: | :------------: | :---------: |
79 | | 01-c3-c0.png | 50.07% | 58.70% | 64.17% | 83.83% | 88.35% |
80 | | 02-c4-c0.png | 39.37% | 45.92% | 48.85% | 76.77% | 66.00% |
81 | | 03-c6-c2.png | 32.69% | 62.03% | 70.56% | 81.87% | 70.60% |
82 | | 04-c2-f0-f5.png | 36.97% | 35.61% | 37.23% | 83.82% | 37.17% |
83 | | 05-c3-c2.png | 53.04% | 37.50% | 78.67% | 100.00% | 78.53% |
84 | | 06-c6-c3.png | 55.15% | 61.03% | 63.67% | 75.06% | 80.77% |
85 | | 07-c4-c3.png | 46.90% | 68.63% | 74.24% | 79.99% | 85.62% |
86 | | 08-c0-c3.png | 74.65% | 85.64% | 88.47% | 93.52% | 88.49% |
87 | | 09-c3-tronc.png | 63.43% | 66.74% | 73.06% | 67.10% | 73.19% |
88 | | 10-c3-t-first.png | 41.96% | 76.36% | 79.47% | 81.68% | 79.47% |
89 | | 11-8-to-4bit.png | 44.33% | 44.01% | 73.62% | 50.92% | 57.35% |
90 | | 12-4-to-8bit.png | 84.64% | 82.72% | 89.23% | 87.98% | 89.23% |
91 | | 13-idat.png | 12.74% | 15.24% | 16.88% | 15.69% | 16.88% |
92 | | 14-chunks.png | 49.06% | 58.56% | 88.93% | 60.43% | 87.70% |
93 | | 15-pal-org.png | 65.24% | 90.17% | 96.51% | 91.54% | 95.65% |
94 | | 16-pal-org-t.png | 65.07% | 91.27% | 97.81% | 91.49% | 96.97% |
95 | | 17-pal-dup.png | 44.39% | 44.36% | 49.80% | 50.36% | 57.85% |
96 | | 18-c2-c3-f.png | 42.32% | 61.76% | 61.24% | 78.54% | 83.42% |
97 | | 19-c6-c3-f.png | 37.33% | 56.62% | 50.11% | 77.10% | 79.23% |
98 | | 20-c6-c4.png | 47.80% | 59.95% | 64.19% | 78.64% | 64.31% |
99 | | 21-rgbdata.png | 76.26% | 76.26% | 88.24% | 85.78% | 82.53% |
100 | | **Mean** | **50.64%** | **60.91%** | **69.28%** | **75.81%** | **74.25%** |
101 |
--------------------------------------------------------------------------------
/docs/Credits.html:
--------------------------------------------------------------------------------
1 |
3 |
A PNG file optimization tool built on pngquant and zopflipng
4 |
16 |
17 |
18 |
Upgrade
19 |
The latest release can be found
20 |
21 | here
22 | . A
23 |
24 | changelog
25 | is available for review of changes to the application since your installed version was released.
26 |
27 |
To upgrade with Homebrew, enter the following command in your terminal:
28 |
29 | $ brew cask upgrade
30 |
31 |
32 |
Other Crunch Tools
33 |
The Crunch image optimization approach is also available through a macOS right-click menu service and a command line
34 | executable. See the
35 |
36 | documentation on the repository README
37 | for installation and usage details.
38 |
39 |
40 |
Issue Reporting
41 |
Please use
42 |
43 | the issue tracker on the Github repository
44 | to report any problems that you have with the Crunch application.
45 |
46 |
Embedded Software Licenses
47 |
60 |
61 |
Animations
62 |
The Crunch GUI animations were designed by
63 | Gary Jacobs and are licensed under the MIT License.
64 |
--------------------------------------------------------------------------------
/docs/EXECUTABLE.md:
--------------------------------------------------------------------------------
1 | # The `crunch` Command Line Executable
2 |
3 | The `crunch` command line executable is a *nix executable that supports parallel PNG image optimization with local (off of the system PATH) installs of the pngquant and zopflipng project dependencies. Compilation of pngquant and zopflipng from source is supported (and mandatory) as part of the installation process. Please review the documentation below for details.
4 |
5 | ## Contents
6 |
7 | - [Install documentation](#install)
8 | - [Upgrade documentation](#upgrade)
9 | - [Usage documentation](#usage)
10 |
11 | ## What Happens During the Installation?
12 |
13 | Three executable files are installed on your machine. The `crunch` executable is installed on your system PATH at `/usr/local/bin/crunch`, `pngquant` is built on the path `$HOME/pngquant/pngquant`, and zopflipng is built on the path `$HOME/zopfli/zopflipng`. This involves source compiles of pngquant and zopflipng. You will see lengthy text output over ~30 seconds as these builds take place. Don't fret. This is perfectly normal and to be expected during the install. See the bottom of this document for uninstall documentation. It is simple to both install and uninstall all files and directories that are distributed with the Crunch tools.
14 |
15 | There is a method to the madness of the install paths. `crunch` is installed on your system PATH so that it can be executed in your terminal with a command like this:
16 |
17 | ```
18 | $ crunch myimage.png
19 | ```
20 |
21 | The `pngquant` and `zopflipng` executables are installed off of your system PATH (in subdirectories of your $HOME directory) in order to pin the versions of the applications to the same git commits that are distributed with the rest of the Crunch project tools. These may or may not be the most current releases of the two project dependencies. Maintaining an always current dependency state is less important to me than that they are tested as part of this project and will allow you to reproduce the same optimized images irrespective of the Crunch tool that you choose to use (and that the data that are displayed on the repository are valid across all of the tools). The off system PATH install approach for the project dependencies also provides you with the option to install different versions of `pngquant` and `zopflipng` on your system PATH (e.g., with a package manager) should you want to use different versions on the command line.
22 |
23 | ## Install
24 |
25 | *Please note: The `crunch` executable and build dependencies should be installed with the user account that is used to execute the application. See [issue #68](https://github.com/chrissimpkins/Crunch/issues/68#issuecomment-412406663) for an approach to installation if this is not possible.*
26 |
27 | Clone the Crunch repository with the following command:
28 |
29 | ```
30 | $ git clone https://github.com/chrissimpkins/Crunch.git
31 | ```
32 |
33 | Then install the `crunch` command line executable with one of the approaches below:
34 |
35 | ### 1. Install with `make` (Recommended)
36 |
37 | ```
38 | $ make build-dependencies
39 | $ make install-executable
40 | ```
41 |
42 | During the `make build-dependencies` step, your terminal window will display lengthy text output over the 30+ seconds that it takes to compile the `zopflipng` and `pngquant` applications. This is normal and expected during the installation process.
43 |
44 | sudo permissions are required to move the executable to your `/usr/bin/local` directory. Please enter your password when it is requested.
45 |
46 | ### 2. Install manually
47 |
48 | ```
49 | $ src/install-dependencies.sh
50 | $ sudo cp src/crunch.py /usr/local/bin/crunch
51 | ```
52 |
53 | During the `install-dependencies.sh` script execution, your terminal window will display lengthy text output over the 30+ seconds that it takes to compile the `zopflipng` and `pngquant` applications. This is normal and expected during the installation process.
54 |
55 | sudo permissions are required to move the executable to your `/usr/bin/local` directory. Please enter your password when it is requested.
56 |
57 | Confirm your installation with the following command:
58 |
59 | ```
60 | $ crunch --help
61 | ```
62 |
63 | This command should display the in-application help message for the `crunch` executable. If you see this text, you are all set to use `crunch`.
64 |
65 | ## Upgrade
66 |
67 | Upgrade the `crunch` executable by cloning the git repository, rebuilding the `pngquant` and `zopflipng` dependencies, and installing the `crunch` executable file.
68 |
69 | You can perform these steps with the following set of commands ([download this gist to execute as a shell script](https://gist.github.com/chrissimpkins/280e2590383a167665942a6a24c837e6)):
70 |
71 | ```
72 | $ git clone https://github.com/chrissimpkins/Crunch.git
73 | $ cd Crunch
74 | $ rm -rf ~/pngquant
75 | $ rm -rf ~/zopfli
76 | $ make build-dependencies
77 | $ make install-executable
78 | ```
79 |
80 | You will be prompted for your password with the final command above. Enter it and confirm the install by entering the following command:
81 |
82 | ```
83 | $ crunch --version
84 | ```
85 |
86 | You should see the expected version of the `crunch` tool installed on your system. You may delete the entire Crunch git repository after you complete these steps (or save it and pull new changes from the master branch to build the next version if you plan to stay current with releases). New releases are indicated with git tags.
87 |
88 |
89 |
90 | ## Usage
91 |
92 | Image processing is executed by requesting one or more PNG image paths as arguments to the `crunch` executable:
93 |
94 | ```
95 | $ crunch [PNG image path 1]...[PNG image path n]
96 | ```
97 |
98 | You can use shell wildcards with the executable. For instance, to process all PNG image files in the working directory, you can use the following:
99 |
100 | ```
101 | $ crunch *.png
102 | ```
103 |
104 | At the completion of processing of each of the requested images, the application reports the following data:
105 |
106 | - percent of original image size
107 | - optimized image file path
108 | - final optimized image size in bytes
109 |
110 | Optimized files are saved in the same directory as the original with the modified path `[original filename]-crunch.png`.
111 |
112 | ### Options
113 |
114 | The following options are available for use with the `crunch` executable:
115 |
116 | ```
117 | --help, -h application help
118 | --usage application usage
119 | --version, -v application version
120 | ```
121 |
122 | ## Uninstall `crunch` and Dependencies
123 |
124 | pngquant is compiled in a directory on the path `$HOME/pngquant`. zopflipng is compiled in a directory on the path `$HOME/zopfli`.
125 |
126 | You can uninstall the pngquant and zopflipng dependencies by executing the following command from the root of the Crunch git repository:
127 |
128 | ```
129 | $ make uninstall-dependencies
130 | ```
131 |
132 | The `crunch` executable file is installed on the path `/usr/local/bin/crunch`.
133 |
134 | You can uninstall the `crunch` executable by executing the following command from the root of the Crunch git repository:
135 |
136 | ```
137 | $ make uninstall-executable
138 | ```
139 |
--------------------------------------------------------------------------------
/docs/MACOSGUI.md:
--------------------------------------------------------------------------------
1 | # Crunch native macOS GUI Application
2 |
3 | The Crunch native macOS GUI application supports drag and drop PNG image optimization on macOS systems. The application provides support for parallel execution of image optimization on systems with multiple processors.
4 |
5 | ## Contents
6 |
7 | - [Install documentation](#install)
8 | - [Usage documentation](#usage)
9 |
10 | ## Install
11 |
12 | Choose one of the methods below:
13 |
14 | ### 1. Install with Homebrew Cask Package Manager (Recommended)
15 |
16 | This approach requires a previous install of the Homebrew package manager on your macOS system. Please refer to the Homebrew documentation for details.
17 |
18 | ```
19 | $ brew install --cask crunch
20 | ```
21 |
22 | Note that you must use `brew install --cask` and not `brew install`!
23 |
24 | Automatically upgrade a previous install to the latest version with:
25 |
26 | ```
27 | $ brew upgrade --cask
28 | ```
29 |
30 | ### 2. Install with dmg Installer
31 |
32 | [Download the latest release of the dmg installer](https://github.com/chrissimpkins/Crunch/releases/latest), click it, and drag the Crunch icon in the Installer window to the Applications directory displayed in the Installer window.
33 |
34 | Upgrade by following the same instructions and allowing the new version to replace the old version on your system.
35 |
36 | ## Usage
37 |
38 | Drag and drop your PNG images onto the Crunch window:
39 |
40 |
41 |
42 | Optimized files are saved in the same directory as the original with the modified path `[original filename]-crunch.png`.
43 |
44 | ## Uninstall Crunch
45 |
46 | If you used Homebrew to install Crunch, uninstall it with the following command:
47 |
48 | ```
49 | $ brew uninstall --cask crunch
50 | ```
51 |
52 | If you used the dmg installer to install Crunch, you can uninstall it by opening your Finder and selecting your Applications directory. Locate Crunch in the Finder window and move it to the Trash. Empty your Trash, and the uninstall is complete.
53 |
--------------------------------------------------------------------------------
/docs/SERVICE.md:
--------------------------------------------------------------------------------
1 | ## Crunch Image(s) macOS Right Click Menu Service
2 |
3 | The Crunch Image(s) macOS service supports parallel PNG image optimization through a right-click Services menu item after you select one or more images in the macOS Finder window. You must install the Crunch macOS GUI application before use of this macOS service because the GUI application package contains the pngquant and zopflipng dependencies that are required for execution of the image optimization.
4 |
5 | ### Quick macOS Service Install
6 |
7 | Install the Crunch GUI application (see [installation documentation for the macOS GUI application](MACOSGUI.md)). [Download the Crunch repository source](https://github.com/chrissimpkins/Crunch/releases/latest), unpack the source archive, and move the workflow directory located on the repository path `service/Crunch Image(s).workflow` to your system on the path `~/Library/Services/Crunch Image(s).workflow`.
8 |
9 | 
10 |
11 | The installation is complete, and the service is ready for use. You may now delete the Crunch source repository.
12 |
13 | ### Quick macOS Service Usage
14 |
15 | Select one or more PNG images in the Finder, right-click, and select "Crunch Image(s)" under the Services menu item. An animated gear appears in your status bar during the file processing and disappears when your optimized images are ready. Optimized files are saved in the same directory as the original with the modified path `[original filename]-crunch.png`.
16 |
17 | ## Detailed macOS Service Installation
18 |
19 | Detailed instructions follow if you need additional information to complete the macOS service installation process.
20 |
21 | ### Install Crunch
22 |
23 | To use the macOS service, you must install the Crunch GUI tool. The macOS service depends upon the embedded versions of the PNG image optimization applications that are used by Crunch to modify your image files.
24 |
25 | See the install instructions in the [Crunch macOS GUI application documentation](MACOSGUI.md). You may use either the Homebrew or .dmg installer approach. When the Crunch install is complete, continue with the instructions below. Please do not modify the default Crunch.app install location if you intend to use the macOS service.
26 |
27 | ### Install the Crunch Image(s) macOS Service
28 |
29 | First, [download the latest release version of the Crunch repository source](https://github.com/chrissimpkins/Crunch/releases/latest). You can use either the .zip or .tar.gz download link. Unpack the Crunch repository source archive in any directory on your system.
30 |
31 | #### 1) Install by Drag and Drop in the Finder
32 |
33 | - Open the Crunch source repository in the Finder. Open the `service` directory that is located in the root of the repository directory. The `Crunch Image(s).workflow` directory is contained in the `service` directory.
34 | - Open a new tab in the Finder with `CMD-T` and select Go > Go to Folder in the Finder menu (or type `SHIFT-CMD-G`)
35 | - In the open "Go to the folder:" free text prompt, enter the following: `~/Library/Services`
36 | - Switch back to the first tab that is located inside the Crunch source repository and drag the `Crunch Image(s).workflow` directory to the second tab so that it is installed on the path `~/Library/Services/Crunch Image(s).workflow`.
37 | - You may now delete the Crunch source repository.
38 |
39 | #### 2) Install with `make`
40 |
41 | If `make` is installed on your macOS system, you can use the Crunch Makefile to install the macOS service.
42 |
43 | - Open a terminal in the root of the Crunch source repository
44 | - Enter the following command to install the macOS service in the directory `~/Library/Services`:
45 |
46 | ```
47 | $ make install-macos-service
48 | ```
49 |
50 | `sudo` permission is necessary to complete the copy of the macOS service to this directory on your system. Enter your password when prompted during the install.
51 |
52 | You may delete the Crunch source repository after the completion of the above step.
53 |
54 | ## Detailed macOS Service Uninstall
55 |
56 | Use one of the following approaches to remove the installed macOS service from your system:
57 | #### 1) Uninstall with Finder
58 |
59 | - Open the Finder and select Go > Go to Folder in the Finder menu (or type `SHIFT-CMD-G`)
60 | - In the open "Go to the folder:" free text prompt, enter the following: `~/Library/Services`
61 | - Delete the workflow directory `Crunch Image(s).workflow` that is located in the `~/Library/Services` directory.
62 |
63 | #### 2) Uninstall with `make`
64 |
65 | Download the source repository. Enter the following command in a terminal at the root of the source repository:
66 |
67 | ```
68 | $ make uninstall-macos-service
69 | ```
70 |
71 | `sudo` permission is necessary to remove this directory. Enter your password when prompted.
72 |
--------------------------------------------------------------------------------
/html/clear.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
8 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/html/complete-error.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
8 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |

27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/html/complete-success.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
8 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |

27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/html/execution.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
8 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |

27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/html/start.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
19 |
20 |
21 |
22 |
23 |
24 |

25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/html/waiting.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Start
7 |
18 |
19 |
20 |
21 |
22 |
23 |

24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/img/Crunch-icon-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/Crunch-icon-2.png
--------------------------------------------------------------------------------
/img/Crunch-icon-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/Crunch-icon-3.png
--------------------------------------------------------------------------------
/img/CrunchIcon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/CrunchIcon.icns
--------------------------------------------------------------------------------
/img/CrunchIcon.iconset/icon_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/CrunchIcon.iconset/icon_128x128.png
--------------------------------------------------------------------------------
/img/CrunchIcon.iconset/icon_128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/CrunchIcon.iconset/icon_128x128@2x.png
--------------------------------------------------------------------------------
/img/CrunchIcon.iconset/icon_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/CrunchIcon.iconset/icon_16x16.png
--------------------------------------------------------------------------------
/img/CrunchIcon.iconset/icon_16x16@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/CrunchIcon.iconset/icon_16x16@2x.png
--------------------------------------------------------------------------------
/img/CrunchIcon.iconset/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/CrunchIcon.iconset/icon_256x256.png
--------------------------------------------------------------------------------
/img/CrunchIcon.iconset/icon_256x256@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/CrunchIcon.iconset/icon_256x256@2x.png
--------------------------------------------------------------------------------
/img/CrunchIcon.iconset/icon_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/CrunchIcon.iconset/icon_32x32.png
--------------------------------------------------------------------------------
/img/CrunchIcon.iconset/icon_32x32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/CrunchIcon.iconset/icon_32x32@2x.png
--------------------------------------------------------------------------------
/img/animations/01-Load-Up@1x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/animations/01-Load-Up@1x.gif
--------------------------------------------------------------------------------
/img/animations/01-Load-Up@2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/animations/01-Load-Up@2x.gif
--------------------------------------------------------------------------------
/img/animations/02-Waiting@1x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/animations/02-Waiting@1x.gif
--------------------------------------------------------------------------------
/img/animations/02-Waiting@2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/animations/02-Waiting@2x.gif
--------------------------------------------------------------------------------
/img/animations/03-Crunching@1x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/animations/03-Crunching@1x.gif
--------------------------------------------------------------------------------
/img/animations/03-Crunching@2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/animations/03-Crunching@2x.gif
--------------------------------------------------------------------------------
/img/animations/04-Error@1x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/animations/04-Error@1x.gif
--------------------------------------------------------------------------------
/img/animations/04-Error@2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/animations/04-Error@2x.gif
--------------------------------------------------------------------------------
/img/animations/04-Success@1x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/animations/04-Success@1x.gif
--------------------------------------------------------------------------------
/img/animations/04-Success@2x.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/animations/04-Success@2x.gif
--------------------------------------------------------------------------------
/img/cat-1285634_640-crunch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/cat-1285634_640-crunch.png
--------------------------------------------------------------------------------
/img/cat-1285634_640.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/cat-1285634_640.png
--------------------------------------------------------------------------------
/img/colors-157474_640-crunch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/colors-157474_640-crunch.png
--------------------------------------------------------------------------------
/img/colors-157474_640.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/colors-157474_640.png
--------------------------------------------------------------------------------
/img/crunch-ss-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/crunch-ss-2.gif
--------------------------------------------------------------------------------
/img/dmg-installer-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/dmg-installer-bg.png
--------------------------------------------------------------------------------
/img/fastdots.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/fastdots.gif
--------------------------------------------------------------------------------
/img/flowers-67839_640-crunch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/flowers-67839_640-crunch.png
--------------------------------------------------------------------------------
/img/flowers-67839_640.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/flowers-67839_640.png
--------------------------------------------------------------------------------
/img/header-arrow-crunch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/header-arrow-crunch.png
--------------------------------------------------------------------------------
/img/header-arrow.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/header-arrow.psd
--------------------------------------------------------------------------------
/img/header-img-crunch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/header-img-crunch.png
--------------------------------------------------------------------------------
/img/header-img.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/header-img.psd
--------------------------------------------------------------------------------
/img/prairie-679014_640-crunch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/prairie-679014_640-crunch.png
--------------------------------------------------------------------------------
/img/prairie-679014_640.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/prairie-679014_640.png
--------------------------------------------------------------------------------
/img/robot-1214536_640-crunch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/robot-1214536_640-crunch.png
--------------------------------------------------------------------------------
/img/robot-1214536_640.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/robot-1214536_640.png
--------------------------------------------------------------------------------
/img/slowdots.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/slowdots.gif
--------------------------------------------------------------------------------
/img/suns-rays-478249_640-crunch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/suns-rays-478249_640-crunch.png
--------------------------------------------------------------------------------
/img/suns-rays-478249_640.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/img/suns-rays-478249_640.png
--------------------------------------------------------------------------------
/profile/Crunch.platypus:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AcceptsFiles
6 |
7 | AcceptsText
8 |
9 | Authentication
10 |
11 | Author
12 | Christopher Simpkins
13 | BundledFiles
14 |
15 | /Users/chris/code/Crunch/docs/Credits.html
16 | /Users/chris/code/Crunch/html/execution.html
17 | /Users/chris/code/Crunch/html/start.html
18 | /Users/chris/code/Crunch/src/include/pngquant
19 | /Users/chris/code/Crunch/src/include/zopflipng
20 | /Users/chris/code/Crunch/html/clear.html
21 | /Users/chris/code/Crunch/ui/MainMenu.nib
22 | /Users/chris/code/Crunch/src/crunch.py
23 | /Users/chris/code/Crunch/img/animations/01-Load-Up@2x.gif
24 | /Users/chris/code/Crunch/img/animations/02-Waiting@2x.gif
25 | /Users/chris/code/Crunch/img/animations/03-Crunching@2x.gif
26 | /Users/chris/code/Crunch/img/animations/04-Error@2x.gif
27 | /Users/chris/code/Crunch/img/animations/04-Success@2x.gif
28 | /Users/chris/code/Crunch/html/waiting.html
29 | /Users/chris/code/Crunch/html/complete-error.html
30 | /Users/chris/code/Crunch/html/complete-success.html
31 |
32 | Creator
33 | Platypus-5.3
34 | DeclareService
35 |
36 | Destination
37 | /Users/chris/Desktop/Application.app
38 | DevelopmentVersion
39 |
40 | DocIconPath
41 |
42 | Droppable
43 |
44 | ExecutablePath
45 | /usr/local/share/platypus/ScriptExec
46 | IconPath
47 | /Users/chris/code/Crunch/img/CrunchIcon.icns
48 | Identifier
49 | com.csimpkins.Crunch
50 | InterfaceType
51 | Web View
52 | InterpreterArgs
53 |
54 | InterpreterPath
55 | /bin/sh
56 | Name
57 | Crunch
58 | NibPath
59 | /usr/local/share/platypus/MainMenu.nib
60 | OptimizeApplication
61 |
62 | Overwrite
63 |
64 | PromptForFileOnLaunch
65 |
66 | RemainRunning
67 |
68 | RunInBackground
69 |
70 | ScriptArgs
71 |
72 | ScriptPath
73 | /Users/chris/code/Crunch/src/crunch-gui.sh
74 | StatusItemDisplayType
75 | Text
76 | StatusItemIcon
77 |
78 | TU0AKgAADygAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
79 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
80 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
81 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
82 | AAAAAAAAAAAAAAAAACgAdACyANYA8wD6APoA9ADXALQAdwAqAAAAAAAAAAAAAAAAAAAA
83 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAHAA1gD/
84 | AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wDZAHQAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
85 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAHkA9QD/AP8A/wD/AP8A9gDkANYA1gDi
86 | APUA/wD/AP8A/wD/APcAgAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
87 | AAAAAAAAAAAAAEMA5wD/AP8A/wD/AMQAZgAvAAkAAAAAAAAAAAAIAC0AYwDBAP8A/wD/
88 | AP8A6gBJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAD/AP8A
89 | /wD+AJAAJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAIkA/AD/AP8A/wCBAAAAAAAA
90 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVAP8A/wD/ALgAIgAAAAAAAAAAAAAA
91 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AsAD/AP8A/wCfAAAAAAAAAAAAAAAAAAAAAAAA
92 | AAAAAAAAAAAAAAAAAJYA/wD/AP8AawAAAAAAAABuAEEAAAAAAAAAAAAAAAAAAAAAAAAA
93 | AAAAAAAAAAAAAAAAZAD/AP8A/wCiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwD/
94 | AP8A/wBEAAAAAAAdAOIAwgDzAEoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
95 | PAD/AP8A/wCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AP8A/wD/AEIAAAAAAAAAyACp
96 | AAAAgQDDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQD/AP8A/wA/AAAA
97 | AAAAAAAAAAAAAAAAAAAAAAAA8AD/AP8AZwAAAAAAAAALANgADQAAAFQAxQAAAAAAAAAA
98 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXwD/AP8A9QADAAAAAAAAAAAAAAAAAAAA
99 | AAB3AP8A/wC+AAAAAAAAAAAAGQDMAAcAAAA8ANEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
100 | AAAAAAAAAAAAAAAAAAAAsgD/AP8AhAAAAAAAAAAAAAAAAAAAAAoA/AD/AP8AFAAAAAAA
101 | AAAAAAMA0AAnAAAAEADeABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
102 | AA0A/AD/AP8AEQAAAAAAAAAAAAAAAABuAP8A/wCOAAAAAAAAAAAAAAAAAMcAdQAAAAAA
103 | 0QB3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIIA/wD/AHkAAAAA
104 | AAAAAAAAAAAA3QD/AP8AFQAAAAAAAAAAAAAAAACFAMEAAAAAAFsA5gAAAAAAAAAAAAAA
105 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAP8A/wDmAAAAAAAAAAAAAAAhAP8A/wDG
106 | AAAAAAAAAAAAAAAAAAAAIwDVAAgAAAAAAOUAbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
107 | AAAAAAAAAAAAAAAAAAC9AP8A/wAkAAAAAAAAAAAAdAD/AP8AYQAAAAAAAAAAAAAAAAAA
108 | AAAAzwAyAAAAAAApAP0AIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJQB3AAAAAAAAAAAA
109 | XAD/AP8AfwAAAAAAAAAAALQA/wD/ACgAAAAAAAAAAAAAAAAAAAAeAMwAAAAAAAAAIQD8
110 | ACIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKwA/wAAAAAAAAAAAB0A/wD/AMAAAAAAAAAA
111 | AADXAP8A9gADAAAAAAAAAAAAAAAAAAAAZwDBAD4AWwCQAvgS/wZqAAAAAAAAAAAAAAAA
112 | AAAAAAAAAAAAAADgAP8ACAAAAAAAAAAAAPAA/wDiAAAAAAAAAAAA9AD/AOEAAAAAAAAA
113 | AAAAAAAAAAAAAFYA/AD/AP8A/wL/Rf82oQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AD/
114 | ABEAAAAAAAAAAADYAP8A+AAAAAAAAAAAAP0A/wDTAAAAAAAAAAAAAAAAAAAAAAAHAPEq
115 | /0T/A/8A/wT/B68AAAAAAAAAAAAAAAAAAAAAAAAAAAAUAP8A/gALAAAAAAAAAAAAygD/
116 | APwAAAAAAAAAAAD9AP8A0wAAAAAAAAAAAAAAAAAAAAAACQDzGf88/wL/AP8A/wD8AAQA
117 | AAAAAAAAAAAAAAAAAAAAAAAAVgD/AOkAAAAAAAAAAAAAAMoA/wD8AAAAAAAAAAAA9QD/
118 | AOAAAAAAAAAAAAAAAAAAAAAAAC0A/wD/AP8A/wD/AP8A/wCdAAAAAAAAAAAAAAAAAAAA
119 | AAAAAM4A/wDKAAAAAAAAAAAAAADYAP8A+AAAAAAAAAAAANgA/wD2AAMAAAAAAAAAAAAA
120 | AAAAAAAnAP8A/wD/AP8A/wD/AP8A/wB5AAAAAAAAAAAAAAAAAAAAhAD/AP8AmwAAAAAA
121 | AAAAAAAA8AD/AOIAAAAAAAAAAAC2AP8A/wAmAAAAAAAAAAAAAAAAAAAABwD7AP8A/wD/
122 | AP8A/wD/AP8A/wC8AEgADQAAAAYANAClAP8A/wD/AF4AAAAAAAAAAAAaAP8A/wDBAAAA
123 | AAAAAAAAdwD/AP8AXwAAAAAAAAAAAAAAAAAAAAAAsQD/AP8A/wD/AP8A/wD/AP8A/wD/
124 | APMA3wDsAP8A/wD/AP8A/wAPAAAAAAAAAAAAWQD/AP8AggAAAAAAAAAAACMA/wD/AMIA
125 | AAAAAAAAAAAAAAAAAgARAA4A7AD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/
126 | AKQAAAAAAAAAAAAAALkA/wD/ACYAAAAAAAAAAAAAAOAA/wD/ABIAAAAAAAAAAAAAAEkA
127 | 9QCeAOAA/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wAYAAAAAAAAAAAACwD+
128 | AP8A6QAAAAAAAAAAAAAAAAByAP8A/wCJAAAAAAAAAAAAAABnAP8A/wD/AP8A/wD/AP8A
129 | /wD/AP8A/wD/AP8A/wD/AP8A/wD/AGQAAAAAAAAAAAAAAHwA/wD/AH8AAAAAAAAAAAAA
130 | AAAADQD9AP8A/gAQAAAAAAAAAAAADACAAP8A/wBrALoA/wD/AP8A/wD/AP8A/wD/AP8A
131 | /wD/AP8AgAAAAAAAAAAAAAAACQD7AP8A/wATAAAAAAAAAAAAAAAAAAAAfgD/AP8AtgAA
132 | AAAAAAAAAAAAAABTAN4AEAAAALEA/wD/AP8A/wD/AP8A/wD/AP8A1gBCAAAAAAAAAAAA
133 | AAAAAKkA/wD/AI0AAAAAAAAAAAAAAAAAAAAAAAEA9AD/AP8AYAAAAAAAAAAAAAAAAAAD
134 | AAQAAAAAAEQAoADaAP4A/wD/AP8A/wD/AMkAoACFAAAAAAAAAAAAWAD/AP8A+AAGAAAA
135 | AAAAAAAAAAAAAAAAAAAAAD4A/wD/AP8AOgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ
136 | ADAANAAjAJIA/wD/AP0ATQAAAAAAAAAxAP8A/wD/AEYAAAAAAAAAAAAAAAAAAAAAAAAA
137 | AAAAAIAA/wD/AP8AOgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACZAP8A3gBy
138 | AAAAAAAAADMA/AD/AP8AiwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAA/wD/AP8A
139 | YwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAvgCuABsACgAAAAAAXQD/AP8A/wCr
140 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKEA/wD/AP8ArQAZAAAAAAAAAAAA
141 | AAAAAAAAAAAAAAAAAAAmADsAAAAAAAAAFgCmAP8A/wD/AKoAAAAAAAAAAAAAAAAAAAAA
142 | AAAAAAAAAAAAAAAAAAAAAAAAAIIA/wD/AP8A+QCCAB0AAAAAAAAAAAAAAAAAAAAAAAAA
143 | AAAAAAAAGgB7APcA/wD/AP8AjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
144 | AAAAAAAAAEoA7AD/AP8A/wD9ALwAYQAmAAIAAAAAAAAAAAABACQAXgC4APwA/wD/AP8A
145 | 8ABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAhgD6
146 | AP8A/wD/AP8A/wDwANsAzQDMANsA7wD/AP8A/wD/AP8A/QCNAA0AAAAAAAAAAAAAAAAA
147 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGgB6AN4A/wD/AP8A/wD/
148 | AP8A/wD/AP8A/wD/AP8A4QCAAB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
149 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAfwC9AN8A9QD7APsA9gDgAL4AgwAu
150 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
151 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
152 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
153 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
154 | AAAAAAAQAQAAAwAAAAEALAAAAQEAAwAAAAEALAAAAQIAAwAAAAIACAAIAQMAAwAAAAEA
155 | AQAAAQYAAwAAAAEAAQAAAQoAAwAAAAEAAQAAAREABAAAAAEAAAAIARIAAwAAAAEAAQAA
156 | ARUAAwAAAAEAAgAAARYAAwAAAAEALAAAARcABAAAAAEAAA8gARwAAwAAAAEAAQAAASgA
157 | AwAAAAEAAgAAAVIAAwAAAAEAAgAAAVMAAwAAAAIAAQABh3MABwAAEZwAAA/uAAAAAAAA
158 | EZxhcHBsAgAAAG1udHJHUkFZWFlaIAfcAAgAFwAPAC4AD2Fjc3BBUFBMAAAAAG5vbmUA
159 | AAAAAAAAAAAAAAAAAAAAAAD21gABAAAAANMtYXBwbAAAAAAAAAAAAAAAAAAAAAAAAAAA
160 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABWRlc2MAAADAAAAAeWRzY20AAAE8AAAI
161 | GmNwcnQAAAlYAAAAI3d0cHQAAAl8AAAAFGtUUkMAAAmQAAAIDGRlc2MAAAAAAAAAH0dl
162 | bmVyaWMgR3JheSBHYW1tYSAyLjIgUHJvZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
163 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
164 | AAAAAAAAAABtbHVjAAAAAAAAAB8AAAAMc2tTSwAAAC4AAAGEZGFESwAAADoAAAGyY2FF
165 | UwAAADgAAAHsdmlWTgAAAEAAAAIkcHRCUgAAAEoAAAJkdWtVQQAAACwAAAKuZnJGVQAA
166 | AD4AAALaaHVIVQAAADQAAAMYemhUVwAAABoAAANMa29LUgAAACIAAANmbmJOTwAAADoA
167 | AAOIY3NDWgAAACgAAAPCaGVJTAAAACQAAAPqcm9STwAAACoAAAQOZGVERQAAAE4AAAQ4
168 | aXRJVAAAAE4AAASGc3ZTRQAAADgAAATUemhDTgAAABoAAAUMamFKUAAAACYAAAUmZWxH
169 | UgAAACoAAAVMcHRQTwAAAFIAAAV2bmxOTAAAAEAAAAXIZXNFUwAAAEwAAAYIdGhUSAAA
170 | ADIAAAZUdHJUUgAAACQAAAaGZmlGSQAAAEYAAAaqaHJIUgAAAD4AAAbwcGxQTAAAAEoA
171 | AAcuYXJFRwAAACwAAAd4cnVSVQAAADoAAAekZW5VUwAAADwAAAfeAFYBYQBlAG8AYgBl
172 | AGMAbgDhACAAcwBpAHYA4QAgAGcAYQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsA
173 | IABnAHIA5QAgADIALAAyACAAZwBhAG0AbQBhAC0AcAByAG8AZgBpAGwARwBhAG0AbQBh
174 | ACAAZABlACAAZwByAGkAcwBvAHMAIABnAGUAbgDoAHIAaQBjAGEAIAAyAC4AMgBDHqUA
175 | dQAgAGgA7ABuAGgAIABNAOAAdQAgAHgA4QBtACAAQwBoAHUAbgBnACAARwBhAG0AbQBh
176 | ACAAMgAuADIAUABlAHIAZgBpAGwAIABHAGUAbgDpAHIAaQBjAG8AIABkAGEAIABHAGEA
177 | bQBhACAAZABlACAAQwBpAG4AegBhAHMAIAAyACwAMgQXBDAEMwQwBDsETAQ9BDAAIABH
178 | AHIAYQB5AC0EMwQwBDwEMAAgADIALgAyAFAAcgBvAGYAaQBsACAAZwDpAG4A6QByAGkA
179 | cQB1AGUAIABnAHIAaQBzACAAZwBhAG0AbQBhACAAMgAsADIAwQBsAHQAYQBsAOEAbgBv
180 | AHMAIABzAHoA/AByAGsAZQAgAGcAYQBtAG0AYQAgADIALgAykBp1KHBwlo5RSV6mADIA
181 | LgAygnJfaWPPj/DHfLwYACDWjMDJACCsELnIACAAMgAuADIAINUEuFzTDMd8AEcAZQBu
182 | AGUAcgBpAHMAawAgAGcAcgDlACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkA
183 | bABPAGIAZQBjAG4A4QAgAWEAZQBkAOEAIABnAGEAbQBhACAAMgAuADIF0gXQBd4F1AAg
184 | BdAF5AXVBegAIAXbBdwF3AXZACAAMgAuADIARwBhAG0AYQAgAGcAcgBpACAAZwBlAG4A
185 | ZQByAGkAYwEDACAAMgAsADIAQQBsAGwAZwBlAG0AZQBpAG4AZQBzACAARwByAGEAdQBz
186 | AHQAdQBmAGUAbgAtAFAAcgBvAGYAaQBsACAARwBhAG0AbQBhACAAMgAsADIAUAByAG8A
187 | ZgBpAGwAbwAgAGcAcgBpAGcAaQBvACAAZwBlAG4AZQByAGkAYwBvACAAZABlAGwAbABh
188 | ACAAZwBhAG0AbQBhACAAMgAsADIARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwA
189 | MgAgAGcAYQBtAG0AYQBwAHIAbwBmAGkAbGZukBpwcF6mfPtlcAAyAC4AMmPPj/Blh072
190 | TgCCLDCwMOwwpDCsMPMw3gAgADIALgAyACAw1zDtMNUwoTCkMOsDkwO1A70DuQO6A8wA
191 | IAOTA7oDwQO5ACADkwOsA7wDvAOxACAAMgAuADIAUABlAHIAZgBpAGwAIABnAGUAbgDp
192 | AHIAaQBjAG8AIABkAGUAIABjAGkAbgB6AGUAbgB0AG8AcwAgAGQAYQAgAEcAYQBtAG0A
193 | YQAgADIALAAyAEEAbABnAGUAbQBlAGUAbgAgAGcAcgBpAGoAcwAgAGcAYQBtAG0AYQAg
194 | ADIALAAyAC0AcAByAG8AZgBpAGUAbABQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMA
195 | bwAgAGQAZQAgAGcAYQBtAG0AYQAgAGQAZQAgAGcAcgBpAHMAZQBzACAAMgAsADIOIw4x
196 | DgcOKg41DkEOAQ4hDiEOMg5ADgEOIw4iDkwOFw4xDkgOJw5EDhsAIAAyAC4AMgBHAGUA
197 | bgBlAGwAIABHAHIAaQAgAEcAYQBtAGEAIAAyACwAMgBZAGwAZQBpAG4AZQBuACAAaABh
198 | AHIAbQBhAGEAbgAgAGcAYQBtAG0AYQAgADIALAAyACAALQBwAHIAbwBmAGkAaQBsAGkA
199 | RwBlAG4AZQByAGkBDQBrAGkAIABHAHIAYQB5ACAARwBhAG0AbQBhACAAMgAuADIAIABw
200 | AHIAbwBmAGkAbABVAG4AaQB3AGUAcgBzAGEAbABuAHkAIABwAHIAbwBmAGkAbAAgAHMA
201 | egBhAHIAbwFbAGMAaQAgAGcAYQBtAG0AYQAgADIALAAyBjoGJwZFBicAIAAyAC4AMgAg
202 | BkQGSAZGACAGMQZFBicGLwZKACAGOQYnBkUEHgQxBEkEMARPACAEQQQ1BEAEMARPACAE
203 | MwQwBDwEPAQwACAAMgAsADIALQQ/BEAEPgREBDgEOwRMAEcAZQBuAGUAcgBpAGMAIABH
204 | AHIAYQB5ACAARwBhAG0AbQBhACAAMgAuADIAIABQAHIAbwBmAGkAbABlAAB0ZXh0AAAA
205 | AENvcHlyaWdodCBBcHBsZSBJbmMuLCAyMDEyAABYWVogAAAAAAAA81EAAQAAAAEWzGN1
206 | cnYAAAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4A
207 | YwBoAG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDg
208 | AOUA6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwB
209 | gwGLAZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJU
210 | Al0CZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oD
211 | ZgNyA34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSo
212 | BLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicG
213 | NwZIBlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4
214 | CAsIHwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsK
215 | EQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxc
216 | DHUMjgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4P
217 | CQ8lD0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHo
218 | EgcSJhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIV
219 | NBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihiv
220 | GNUY+hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHsc
221 | oxzMHPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDE
222 | IPAhHCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTgl
223 | aCWXJccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1
224 | KmgqmyrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ov
225 | kS/HL/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUT
226 | NU01hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87
227 | LTtrO6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFq
228 | QaxB7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVI
229 | S0iRSNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9J
230 | T5NP3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW
231 | 91dEV5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69
232 | Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhn
233 | PWeTZ+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/R
234 | cCtwhnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5
235 | KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKS
236 | gvSDV4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOM
237 | yo0xjZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cK
238 | l3WX4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobai
239 | JqKWowajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1E
240 | rbiuLa6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5
241 | SrnCuju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVL
242 | xcjGRsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7S
243 | P9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p
244 | 36/gNuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77Ibt
245 | Ee2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn
246 | +3f8B/yY/Sn9uv5L/tz/bf//
247 |
248 | StatusItemIconIsTemplate
249 |
250 | StatusItemTitle
251 | Title
252 | StatusItemUseSystemFont
253 |
254 | Suffixes
255 |
256 | TextBackground
257 | #ffffff
258 | TextFont
259 | Monaco
260 | TextForeground
261 | #000000
262 | TextSize
263 | 13
264 | URISchemes
265 |
266 | UniformTypes
267 |
268 | public.item
269 | public.folder
270 |
271 | Version
272 | 5.0.0
273 |
274 |
275 |
--------------------------------------------------------------------------------
/service/Crunch Image(s).workflow/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSServices
6 |
7 |
8 | NSMenuItem
9 |
10 | default
11 | Crunch Image(s)
12 |
13 | NSMessage
14 | runWorkflowAsService
15 | NSRequiredContext
16 |
17 | NSApplicationIdentifier
18 | com.apple.finder
19 |
20 | NSSendFileTypes
21 |
22 | public.image
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/service/Crunch Image(s).workflow/Contents/QuickLook/Preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/service/Crunch Image(s).workflow/Contents/QuickLook/Preview.png
--------------------------------------------------------------------------------
/service/Crunch Image(s).workflow/Contents/document.wflow:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AMApplicationBuild
6 | 428
7 | AMApplicationVersion
8 | 2.7
9 | AMDocumentVersion
10 | 2
11 | actions
12 |
13 |
14 | action
15 |
16 | AMAccepts
17 |
18 | Container
19 | List
20 | Optional
21 |
22 | Types
23 |
24 | com.apple.cocoa.string
25 |
26 |
27 | AMActionVersion
28 | 2.0.3
29 | AMApplication
30 |
31 | Automator
32 |
33 | AMParameterProperties
34 |
35 | COMMAND_STRING
36 |
37 | CheckedForUserDefaultShell
38 |
39 | inputMethod
40 |
41 | shell
42 |
43 | source
44 |
45 |
46 | AMProvides
47 |
48 | Container
49 | List
50 | Types
51 |
52 | com.apple.cocoa.string
53 |
54 |
55 | ActionBundlePath
56 | /System/Library/Automator/Run Shell Script.action
57 | ActionName
58 | Run Shell Script
59 | ActionParameters
60 |
61 | COMMAND_STRING
62 |
63 |
64 | /Applications/Crunch.app/Contents/Resources/crunch.py --service "$@"
65 | CheckedForUserDefaultShell
66 |
67 | inputMethod
68 | 1
69 | shell
70 | /bin/sh
71 | source
72 |
73 |
74 | BundleIdentifier
75 | com.apple.RunShellScript
76 | CFBundleVersion
77 | 2.0.3
78 | CanShowSelectedItemsWhenRun
79 |
80 | CanShowWhenRun
81 |
82 | Category
83 |
84 | AMCategoryUtilities
85 |
86 | Class Name
87 | RunShellScriptAction
88 | InputUUID
89 | 16953775-D408-4D20-961F-AAFA2FFB2EEA
90 | Keywords
91 |
92 | Shell
93 | Script
94 | Command
95 | Run
96 | Unix
97 |
98 | OutputUUID
99 | 2C1D5AD7-8CD0-48A9-A388-29534835C3BE
100 | UUID
101 | 4D4C0544-BBDF-40C8-8673-4203B660D33F
102 | UnlocalizedApplications
103 |
104 | Automator
105 |
106 | arguments
107 |
108 | 0
109 |
110 | default value
111 | 0
112 | name
113 | inputMethod
114 | required
115 | 0
116 | type
117 | 0
118 | uuid
119 | 0
120 |
121 | 1
122 |
123 | default value
124 |
125 | name
126 | source
127 | required
128 | 0
129 | type
130 | 0
131 | uuid
132 | 1
133 |
134 | 2
135 |
136 | default value
137 |
138 | name
139 | CheckedForUserDefaultShell
140 | required
141 | 0
142 | type
143 | 0
144 | uuid
145 | 2
146 |
147 | 3
148 |
149 | default value
150 |
151 | name
152 | COMMAND_STRING
153 | required
154 | 0
155 | type
156 | 0
157 | uuid
158 | 3
159 |
160 | 4
161 |
162 | default value
163 | /bin/sh
164 | name
165 | shell
166 | required
167 | 0
168 | type
169 | 0
170 | uuid
171 | 4
172 |
173 |
174 | isViewVisible
175 |
176 | location
177 | 504.500000:433.000000
178 | nibPath
179 | /System/Library/Automator/Run Shell Script.action/Contents/Resources/English.lproj/main.nib
180 |
181 | isViewVisible
182 |
183 |
184 |
185 | connectors
186 |
187 | workflowMetaData
188 |
189 | serviceApplicationBundleID
190 | com.apple.finder
191 | serviceApplicationPath
192 | /System/Library/CoreServices/Finder.app
193 | serviceInputTypeIdentifier
194 | com.apple.Automator.fileSystemObject.image
195 | serviceOutputTypeIdentifier
196 | com.apple.Automator.nothing
197 | serviceProcessesInput
198 | 0
199 | workflowTypeIdentifier
200 | com.apple.Automator.servicesMenu
201 |
202 |
203 |
204 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 90
3 |
--------------------------------------------------------------------------------
/src/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/src/__init__.py
--------------------------------------------------------------------------------
/src/crunch-gui.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # //////////////////////////////////////////////////////////
4 | #
5 | # crunch-gui.sh
6 | # A shell script that executes Crunch PNG optimization
7 | # for the Crunch macOS GUI application
8 | # Copyright 2018 Christopher Simpkins and contributors
9 | # MIT License
10 | #
11 | # Source: https://github.com/chrissimpkins/Crunch
12 | #
13 | # Note: this file is not intended for direct execution
14 | # on the command line
15 | #
16 | # ///////////////////////////////////////////////////////////
17 |
18 | # UNCOMMENT FOR TESTING ONLY
19 | # python crunch.py --gui "$@"
20 | # exit 0
21 |
22 | # Message on application open (no arguments passed to script on initial open)
23 | if [ $# -eq 0 ]; then
24 | cat waiting.html
25 | exit 0
26 | fi
27 |
28 | cat execution.html
29 |
30 | if ./crunch.py --gui "$@" >/dev/null 2>&1; then
31 | cat clear.html
32 | cat complete-success.html
33 | sleep 2
34 | cat clear.html
35 | cat start.html
36 | sleep 0.8
37 | cat waiting.html
38 | exit 0
39 | else
40 | sleep 0.6
41 | cat clear.html
42 | cat complete-error.html
43 | sleep 2
44 | cat clear.html
45 | cat start.html
46 | sleep 0.8
47 | cat waiting.html
48 | exit 1
49 | fi
50 |
--------------------------------------------------------------------------------
/src/crunch.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # ==================================================================
4 | # crunch
5 | # A PNG file optimization tool built on pngquant and zopflipng
6 | #
7 | # Copyright 2019 Christopher Simpkins
8 | # MIT License
9 | #
10 | # Source Repository: https://github.com/chrissimpkins/Crunch
11 | # ==================================================================
12 |
13 | import sys
14 | import os
15 | import shutil
16 | import struct
17 | import subprocess
18 | import time
19 | from subprocess import CalledProcessError
20 |
21 | from multiprocessing import Lock, Pool, cpu_count
22 |
23 | # Locks
24 | stdstream_lock = Lock()
25 | logging_lock = Lock()
26 |
27 | # Application Constants
28 | VERSION = "5.0.0"
29 | VERSION_STRING = "crunch v" + VERSION
30 |
31 | # Processor Constant
32 | # - Modify this to an integer value if you want to fix the number of
33 | # processes spawned during execution. The process number is
34 | # automatically defined during source execution when this is defined
35 | # as a value of 0
36 | PROCESSES = 0
37 |
38 | # Dependency Path Constants for Command Line Executable
39 | # - Redefine these path strings to use system-installed versions of
40 | # pngquant and zopflipng (e.g. to "/usr/local/bin/[executable]")
41 | PNGQUANT_CLI_PATH = os.path.join(os.path.expanduser("~"), "pngquant", "pngquant")
42 | ZOPFLIPNG_CLI_PATH = os.path.join(os.path.expanduser("~"), "zopfli", "zopflipng")
43 |
44 | # Crunch Directory (dot directory in $HOME)
45 | CRUNCH_DOT_DIRECTORY = os.path.join(os.path.expanduser("~"), ".crunch")
46 |
47 | # Log File Path Constants
48 | LOGFILE_PATH = os.path.join(CRUNCH_DOT_DIRECTORY, "crunch.log")
49 |
50 | HELP_STRING = """
51 | ==================================================
52 | crunch
53 | Copyright 2019 Christopher Simpkins
54 | MIT License
55 |
56 | Source: https://github.com/chrissimpkins/Crunch
57 | ==================================================
58 |
59 | crunch is a command line executable that performs lossy optimization of one
60 | or more png image files with pngquant and zopflipng.
61 |
62 | Usage:
63 | $ crunch [image path 1]...[image path n]
64 |
65 | Options:
66 | --help, -h application help
67 | --usage application usage
68 | --version, -v application version
69 | """
70 |
71 | USAGE = "$ crunch [image path 1]...[image path n]"
72 |
73 |
74 | def main(argv):
75 |
76 | # Create the Crunch dot directory in $HOME if it does not exist
77 | # Only used for macOS GUI and macOS right-click menu service execution
78 | if len(argv) > 0 and argv[0] in ("--gui", "--service"):
79 | if not os.path.isdir(CRUNCH_DOT_DIRECTORY):
80 | os.makedirs(CRUNCH_DOT_DIRECTORY)
81 | # clear the text in the log file before every script execution
82 | # logging is only maintained for the last execution of the script
83 | open(LOGFILE_PATH, "w").close()
84 |
85 | # ////////////////////////
86 | # ANSI COLOR DEFINITIONS
87 | # ////////////////////////
88 | if not is_gui(sys.argv):
89 | ERROR_STRING = "[ " + format_ansi_red("!") + " ]"
90 | else:
91 | ERROR_STRING = "[ ! ]"
92 |
93 | # //////////////////////////////////
94 | # CONFIRM ARGUMENT PRESENT
95 | # //////////////////////////////////
96 |
97 | if len(argv) == 0:
98 | sys.stderr.write(
99 | f"{ERROR_STRING} Please include one or more paths to PNG image files as "
100 | "arguments to the script.{os.linesep}"
101 | )
102 | sys.exit(1)
103 |
104 | # //////////////////////////////////////
105 | # HELP, USAGE, VERSION option handling
106 | # //////////////////////////////////////
107 |
108 | if argv[0] in ("-v", "--version"):
109 | print(VERSION_STRING)
110 | sys.exit(0)
111 | elif argv[0] in ("-h", "--help"):
112 | print(HELP_STRING)
113 | sys.exit(0)
114 | elif argv[0] == "--usage":
115 | print(USAGE)
116 | sys.exit(0)
117 |
118 | # ////////////////////////
119 | # DEFINE DEPENDENCY PATHS
120 | # ////////////////////////
121 | PNGQUANT_EXE_PATH = get_pngquant_path()
122 | ZOPFLIPNG_EXE_PATH = get_zopflipng_path()
123 |
124 | # ////////////////////
125 | # PARSE PNG_PATH_LIST
126 | # ////////////////////
127 |
128 | if is_gui(argv):
129 | png_path_list = argv[1:]
130 | else:
131 | png_path_list = argv
132 |
133 | # //////////////////////////////////
134 | # COMMAND LINE ERROR HANDLING
135 | # //////////////////////////////////
136 |
137 | NOTPNG_ERROR_FOUND = False
138 | for png_path in png_path_list:
139 | # Not a file test
140 | if not os.path.isfile(png_path): # is not an existing file
141 | sys.stderr.write(
142 | f"{ERROR_STRING} '{png_path}' does not appear to be a "
143 | f"valid path to a PNG file{os.linesep}"
144 | )
145 | sys.exit(1) # not a file, abort immediately
146 | # PNG validity test
147 | if not is_valid_png(png_path):
148 | sys.stderr.write(
149 | f"{ERROR_STRING} '{png_path}' is not a valid PNG file.{os.linesep}"
150 | )
151 | if is_gui(argv):
152 | log_error(f"{png_path} is not a valid PNG file.")
153 | NOTPNG_ERROR_FOUND = True
154 |
155 | # Exit after checking all file requests and reporting on all invalid
156 | # file paths (above)
157 | if NOTPNG_ERROR_FOUND is True:
158 | sys.stderr.write(
159 | f"The request was not executed successfully. Please try again "
160 | f"with one or more valid PNG files.{os.linesep}"
161 | )
162 | if is_gui(argv):
163 | log_error(
164 | "The request was not executed successfully. Please try "
165 | "again with one or more valid PNG files."
166 | )
167 | sys.exit(1)
168 |
169 | # Dependency error handling
170 | if not os.path.exists(PNGQUANT_EXE_PATH):
171 | sys.stderr.write(
172 | f"{ERROR_STRING} pngquant executable was not identified on path "
173 | f"'{PNGQUANT_EXE_PATH}'{os.linesep}"
174 | )
175 | if is_gui(argv):
176 | log_error(f"pngquant was not found on the expected path {PNGQUANT_EXE_PATH}")
177 | sys.exit(1)
178 | elif not os.path.exists(ZOPFLIPNG_EXE_PATH):
179 | sys.stderr.write(
180 | f"{ERROR_STRING} zopflipng executable was not identified on path "
181 | f"'{ZOPFLIPNG_EXE_PATH}'{os.linesep}"
182 | )
183 | if is_gui(argv):
184 | log_error(
185 | f"zopflipng was not found on the expected path {ZOPFLIPNG_EXE_PATH}"
186 | )
187 | sys.exit(1)
188 |
189 | # ////////////////////////////////////
190 | # OPTIMIZATION PROCESSING
191 | # ////////////////////////////////////
192 | print("Crunching ...")
193 |
194 | if len(png_path_list) == 1:
195 | # there is only one PNG file, skip spawning of processes and just optimize it
196 | optimize_png(png_path_list[0])
197 | else:
198 | processes = PROCESSES
199 | # if not defined by user, start by defining spawned processes as number
200 | # of available cores
201 | if processes == 0:
202 | processes = cpu_count()
203 |
204 | # if total cores available is greater than number of files requested,
205 | # limit to the latter number
206 | if processes > len(png_path_list):
207 | processes = len(png_path_list)
208 |
209 | print(
210 | f"Spawning {processes} processes to optimize {len(png_path_list)} "
211 | f"image files..."
212 | )
213 | p = Pool(processes)
214 | try:
215 | p.map(optimize_png, png_path_list)
216 | except Exception as e:
217 | stdstream_lock.acquire()
218 | sys.stderr.write(f"-----{os.linesep}")
219 | sys.stderr.write(
220 | f"{ERROR_STRING} Error detected during execution of the request."
221 | f"{os.linesep}"
222 | )
223 | sys.stderr.write(f"{e}{os.linesep}")
224 | stdstream_lock.release()
225 | if is_gui(argv):
226 | log_error(str(e))
227 | sys.exit(1)
228 |
229 | # end of successful processing, exit code 0
230 | if is_gui(argv):
231 | log_info("Crunch execution ended.")
232 | sys.exit(0)
233 |
234 |
235 | # ///////////////////////
236 | # FUNCTION DEFINITIONS
237 | # ///////////////////////
238 |
239 |
240 | def optimize_png(png_path):
241 | img = ImageFile(png_path)
242 |
243 | # define pngquant and zopflipng paths
244 | PNGQUANT_EXE_PATH = get_pngquant_path()
245 | ZOPFLIPNG_EXE_PATH = get_zopflipng_path()
246 |
247 | # ////////////////////////
248 | # ANSI COLOR DEFINITIONS
249 | # ////////////////////////
250 | if not is_gui(sys.argv):
251 | ERROR_STRING = "[ " + format_ansi_red("!") + " ]"
252 | else:
253 | ERROR_STRING = "[ ! ]"
254 |
255 | # --------------
256 | # pngquant stage
257 | # --------------
258 | pngquant_options = (
259 | " --quality=80-98 --skip-if-larger --force --strip --speed 1 --ext -crunch.png "
260 | )
261 | pngquant_command = PNGQUANT_EXE_PATH + pngquant_options + shellquote(img.pre_filepath)
262 | try:
263 | subprocess.check_output(pngquant_command, stderr=subprocess.STDOUT, shell=True)
264 | except CalledProcessError as cpe:
265 | if cpe.returncode == 98:
266 | # this is the status code when file size increases with execution of pngquant.
267 | # ignore at this stage, original file copied at beginning of zopflipng
268 | # processing below if it is not present due to these errors
269 | pass
270 | elif cpe.returncode == 99:
271 | # this is the status code when the image quality falls below the set min value
272 | # ignore at this stage, original lfile copied at beginning of zopflipng
273 | # processing below if it is not present to these errors
274 | pass
275 | else:
276 | stdstream_lock.acquire()
277 | sys.stderr.write(
278 | f"{ERROR_STRING} {img.pre_filepath} processing failed at the pngquant "
279 | f"stage.{os.linesep}"
280 | )
281 | stdstream_lock.release()
282 | if is_gui(sys.argv):
283 | log_error(
284 | f"{img.pre_filepath} processing failed at the pngquant stage."
285 | f"{os.linesep}{cpe}"
286 | )
287 | return None
288 | else:
289 | raise cpe
290 | except Exception as e:
291 | if is_gui(sys.argv):
292 | log_error(
293 | f"{img.pre_filepath} processing failed at the pngquant stage."
294 | f"{os.linesep}{e}"
295 | )
296 | return None
297 | else:
298 | raise e
299 |
300 | # ---------------
301 | # zopflipng stage
302 | # ---------------
303 | # use --filters=0 by default for quantized PNG files (based upon testing by CS)
304 | zopflipng_options = " -y --filters=0 "
305 | # confirm that a file with proper path was generated by pngquant
306 | # pngquant does not write expected file path if the file was larger after processing
307 | if not os.path.exists(img.post_filepath):
308 | shutil.copy(img.pre_filepath, img.post_filepath)
309 | # If pngquant did not quantize the file, permit zopflipng to attempt compression
310 | # with mulitple filters. This achieves better compression than the default
311 | # approach for non-quantized PNG files, but takes significantly longer
312 | # (based upon testing by CS)
313 | zopflipng_options = " -y --lossy_transparent "
314 | zopflipng_command = (
315 | ZOPFLIPNG_EXE_PATH
316 | + zopflipng_options
317 | + shellquote(img.post_filepath)
318 | + " "
319 | + shellquote(img.post_filepath)
320 | )
321 | try:
322 | subprocess.check_output(zopflipng_command, stderr=subprocess.STDOUT, shell=True)
323 | except CalledProcessError as cpe:
324 | stdstream_lock.acquire()
325 | sys.stderr.write(
326 | f"{ERROR_STRING} {img.pre_filepath} processing failed at the zopflipng "
327 | f"stage.{os.linesep}"
328 | )
329 | stdstream_lock.release()
330 | if is_gui(sys.argv):
331 | log_error(
332 | f"{img.pre_filepath} processing failed at the zopflipng stage."
333 | f"{os.linesep}{cpe}"
334 | )
335 | return None
336 | else:
337 | raise cpe
338 | except Exception as e:
339 | if is_gui(sys.argv):
340 | log_error(
341 | f"{img.pre_filepath} processing failed at the zopflipng stage."
342 | f"{os.linesep}{e}"
343 | )
344 | return None
345 | else:
346 | raise e
347 |
348 | # Check file size post-optimization and report comparison with pre-optimization file
349 | img.get_post_filesize()
350 | percent = img.get_compression_percent()
351 | percent_string = "{0:.2f}%".format(percent)
352 | # if compression occurred, color the percent string green
353 | # otherwise, leave it default text color
354 | if not is_gui(sys.argv) and percent < 100:
355 | percent_string = format_ansi_green(percent_string)
356 |
357 | # report percent original file size / post file path / size (bytes) to
358 | # stdout (command line executable)
359 | stdstream_lock.acquire()
360 | print(
361 | "[ "
362 | + percent_string
363 | + " ] "
364 | + img.post_filepath
365 | + " ("
366 | + str(img.post_size)
367 | + " bytes)"
368 | )
369 | stdstream_lock.release()
370 |
371 | # report percent original file size / post file path / size (bytes) to log file
372 | # (macOS GUI + right-click service)
373 | if is_gui(sys.argv):
374 | log_info(
375 | "[ "
376 | + percent_string
377 | + " ] "
378 | + img.post_filepath
379 | + " ("
380 | + str(img.post_size)
381 | + " bytes)"
382 | )
383 |
384 |
385 | # -----------
386 | # Utilities
387 | # -----------
388 |
389 |
390 | def fix_filepath_args(args):
391 | arg_list = []
392 | parsed_filepath = ""
393 | for arg in args:
394 | if arg[0] == "-":
395 | # add command line options
396 | arg_list.append(arg)
397 | elif len(arg) > 2 and "." in arg[1:]:
398 | # if format is `\w+\.\w+`, then this is a filename, not directory
399 | # this is the end of a filepath string that may have had
400 | # spaces in directories prior to this level. Let's recreate
401 | # the entire original path
402 | filepath = parsed_filepath + arg
403 | arg_list.append(filepath)
404 | # reset the temp string that is used to reconstruct the filepaths
405 | parsed_filepath = ""
406 | else:
407 | # if the argument does not end with a .png, then there must have
408 | # been a space in the directory paths, let's add it back
409 | parsed_filepath = parsed_filepath + arg + " "
410 | # return new argument list with fixed filepaths to calling code
411 | return arg_list
412 |
413 |
414 | def get_pngquant_path():
415 | if sys.argv[1] == "--gui":
416 | return "./pngquant"
417 | elif sys.argv[1] == "--service":
418 | return "/Applications/Crunch.app/Contents/Resources/pngquant"
419 | else:
420 | return PNGQUANT_CLI_PATH
421 |
422 |
423 | def get_zopflipng_path():
424 | if sys.argv[1] == "--gui":
425 | return "./zopflipng"
426 | elif sys.argv[1] == "--service":
427 | return "/Applications/Crunch.app/Contents/Resources/zopflipng"
428 | else:
429 | return ZOPFLIPNG_CLI_PATH
430 |
431 |
432 | def is_gui(arglist):
433 | return "--gui" in arglist or "--service" in arglist
434 |
435 |
436 | def is_valid_png(filepath):
437 | # The PNG byte signature (https://www.w3.org/TR/PNG/#5PNG-file-signature)
438 | expected_signature = struct.pack("8B", 137, 80, 78, 71, 13, 10, 26, 10)
439 | # open the file and read first 8 bytes
440 | with open(filepath, "rb") as filer:
441 | signature = filer.read(8)
442 | # return boolean test result for first eight bytes == expected PNG byte signature
443 | return signature == expected_signature
444 |
445 |
446 | def log_error(errmsg):
447 | current_time = time.strftime("%m-%d-%y %H:%M:%S")
448 | logging_lock.acquire()
449 | with open(LOGFILE_PATH, "a") as filewriter:
450 | filewriter.write(current_time + "\tERROR\t" + errmsg + os.linesep)
451 | filewriter.flush()
452 | os.fsync(filewriter.fileno())
453 | logging_lock.release()
454 |
455 |
456 | def log_info(infomsg):
457 | current_time = time.strftime("%m-%d-%y %H:%M:%S")
458 | logging_lock.acquire()
459 | with open(LOGFILE_PATH, "a") as filewriter:
460 | filewriter.write(current_time + "\tINFO\t" + infomsg + os.linesep)
461 | filewriter.flush()
462 | os.fsync(filewriter.fileno())
463 | logging_lock.release()
464 | return None
465 |
466 |
467 | def shellquote(filepath):
468 | return "'" + filepath.replace("'", "'\\''") + "'"
469 |
470 |
471 | def format_ansi_red(text):
472 | if sys.stdout.isatty():
473 | return "\033[0;31m" + text + "\033[0m"
474 | else:
475 | return text
476 |
477 |
478 | def format_ansi_green(text):
479 | if sys.stdout.isatty():
480 | return "\033[0;32m" + text + "\033[0m"
481 | else:
482 | return text
483 |
484 |
485 | # ///////////////////////
486 | # OBJECT DEFINITIONS
487 | # ///////////////////////
488 |
489 |
490 | class ImageFile(object):
491 | def __init__(self, filepath):
492 | self.pre_filepath = filepath
493 | self.post_filepath = self._get_post_filepath()
494 | self.pre_size = self._get_filesize(self.pre_filepath)
495 | self.post_size = 0
496 |
497 | def _get_filesize(self, file_path):
498 | return os.path.getsize(file_path)
499 |
500 | def _get_post_filepath(self):
501 | path, extension = os.path.splitext(self.pre_filepath)
502 | return path + "-crunch" + extension
503 |
504 | def get_post_filesize(self):
505 | self.post_size = self._get_filesize(self.post_filepath)
506 |
507 | def get_compression_percent(self):
508 | ratio = float(self.post_size) / float(self.pre_size)
509 | percent = ratio * 100
510 | return percent
511 |
512 |
513 | if __name__ == "__main__":
514 | # bugfix for macOS GUI / right-click service filepath issue
515 | # when spaces are included in the absolute path to the image
516 | # file. https://github.com/chrissimpkins/Crunch/issues/30
517 | # This workaround reconstructs the original filepaths
518 | # that are split by the shell script into separate arguments
519 | # when there are spaces in the macOS file path
520 | if len(sys.argv) > 1 and sys.argv[1] in ("--gui", "--service"):
521 | arg_list = fix_filepath_args(sys.argv[1:])
522 | main(arg_list)
523 | else:
524 | # the command line executable assumes that users will appropriately quote
525 | # or escape special characters (including spaces) on the command line,
526 | # no need for the special parsing treatment above
527 | main(sys.argv[1:])
528 |
--------------------------------------------------------------------------------
/src/include/licenses/zopflipng_license.txt:
--------------------------------------------------------------------------------
1 | Copyright 2013 Google Inc. All Rights Reserved.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
15 | // Author: lode.vandevenne@gmail.com (Lode Vandevenne)
16 | // Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
17 |
18 | // Library to recompress and optimize PNG images. Uses Zopfli as the compression
19 | // backend, chooses optimal PNG color model, and tries out several PNG filter
20 | // strategies.
21 |
--------------------------------------------------------------------------------
/src/include/pngquant:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/src/include/pngquant
--------------------------------------------------------------------------------
/src/include/zopflipng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/src/include/zopflipng
--------------------------------------------------------------------------------
/src/install-dependencies.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # ==================================================================
4 | # install-dependencies.sh
5 | # Install Crunch optimization binary dependencies
6 | #
7 | # Copyright 2018 Christopher Simpkins
8 | # MIT License
9 | #
10 | # Source Repository: https://github.com/chrissimpkins/Crunch
11 | # ==================================================================
12 |
13 | PNGQUANT_BUILD_DIR="$HOME/pngquant"
14 | PNGQUANT_EXE="$PNGQUANT_BUILD_DIR/pngquant"
15 | ZOPFLIPNG_BUILD_DIR="$HOME/zopfli"
16 | ZOPFLIPNG_EXE="$ZOPFLIPNG_BUILD_DIR/zopflipng"
17 |
18 | PNGQUANT_VERSION_TAG="2.12.5"
19 | ZOPFLIPNG_VERSION_TAG="v2.2.0"
20 | LIBPNG_VERSION="1.6.37"
21 | LIBPNG_VERSION_FILE="v$LIBPNG_VERSION.tar.gz"
22 |
23 | LITTLECMS_VERSION="2.9"
24 | LITTLECMS_FILE="lcms$LITTLECMS_VERSION.tar.gz"
25 |
26 |
27 | # ////////////////////
28 | #
29 | # BUILD pngquant
30 | #
31 | # ////////////////////
32 |
33 | # Clone pngquant source
34 | if [ -d "$PNGQUANT_BUILD_DIR" ]; then
35 | rm -rf "$PNGQUANT_BUILD_DIR"
36 | fi
37 |
38 | cd "$HOME" || exit 1
39 |
40 | git clone --recursive https://github.com/kornelski/pngquant.git
41 | cd "$PNGQUANT_BUILD_DIR" || exit 1
42 | git checkout $PNGQUANT_VERSION_TAG
43 | git submodule update
44 |
45 | # Clone libpng source as a subdirectory of pngquant source (as per pngquant static compilation documentation)
46 | curl -L -O "https://github.com/glennrp/libpng/archive/refs/tags/$LIBPNG_VERSION_FILE"
47 | tar -xf $LIBPNG_VERSION_FILE
48 | rm $LIBPNG_VERSION_FILE
49 | cd "libpng-$LIBPNG_VERSION" || exit 1
50 | # build local libpng
51 | ./configure && make
52 |
53 | # Clone local little-cms2 library source and compile
54 | cd "$PNGQUANT_BUILD_DIR" || exit 1
55 | curl -L -O "https://github.com/mm2/Little-CMS/archive/refs/tags/$LITTLECMS_FILE"
56 | tar -xf $LITTLECMS_FILE
57 | rm $LITTLECMS_FILE
58 |
59 | cd "Little-CMS-lcms$LITTLECMS_VERSION" || exit 1
60 | ./configure && make
61 |
62 | # Build local pngquant executable using local libpng
63 | cd "$PNGQUANT_BUILD_DIR" || exit 1
64 | LCMS2_STATIC=1 ./configure --with-libpng="libpng-$LIBPNG_VERSION" --with-lcms2 && make
65 |
66 |
67 | # /////////////////
68 | #
69 | # BUILD zopflipng
70 | #
71 | # /////////////////
72 |
73 | if [ -d "$ZOPFLIPNG_BUILD_DIR" ]; then
74 | rm -rf "$ZOPFLIPNG_BUILD_DIR"
75 | fi
76 |
77 | cd "$HOME" || exit 1
78 |
79 | git clone https://github.com/chrissimpkins/zopfli.git
80 | cd zopfli || exit 1
81 | git checkout "$ZOPFLIPNG_VERSION_TAG"
82 |
83 | make zopflipng
84 |
85 | # ///////////////////////
86 | #
87 | # Tests and user reports
88 | #
89 | # ///////////////////////
90 |
91 | # Test for expected install file paths and report outcome to user
92 |
93 | printf "\\n\\n------------------------------\\nTesting Builds...\\n------------------------------\\n"
94 |
95 | printf "[?] %s test...\\n\\n" "$PNGQUANT_EXE"
96 | if [ -f "$PNGQUANT_EXE" ]; then
97 | "$PNGQUANT_EXE" --help
98 | else
99 | printf "[ERROR]: pngquant executable was not found on the expected path: %s\\n" "$PNGQUANT_EXE"
100 | printf "The install attempt did not complete successfully. Please report this error.\\n"
101 | exit 1
102 | fi
103 |
104 | printf "\\n[?] %s test...\\n\\n" "$ZOPFLIPNG_EXE"
105 | if [ -f "$ZOPFLIPNG_EXE" ]; then
106 | "$ZOPFLIPNG_EXE" --help
107 | else
108 | printf "[ERROR]: zopflipng executable was not found on the expected path: %s\\n" "$PNGQUANT_EXE"
109 | printf "The install attempt did not complete successfully. Please report this error."
110 | exit 1
111 | fi
112 |
113 | printf "\\n\\n------------------------------\\nEnd Tests\\n------------------------------\\n"
114 |
115 | printf "\\n---------- BUILD PATHS ----------\\n"
116 | printf "[*] pngquant path: %s\\n" "$PNGQUANT_EXE"
117 | printf "[*] zopflipng path: %s\\n" "$ZOPFLIPNG_EXE"
118 | printf "\\n\\n[OK] Dependency installs complete.\\n"
119 | exit 0
120 |
--------------------------------------------------------------------------------
/src/test_crunch_errors.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import os
5 | import sys
6 | import pytest
7 |
8 | import src.crunch
9 |
10 | # ///////////////////////////////////////////////////////
11 | #
12 | # pytest capsys capture tests
13 | # confirms capture of std output and std error streams
14 | #
15 | # ///////////////////////////////////////////////////////
16 |
17 |
18 | def test_pytest_capsys(capsys):
19 | print("bogus text for a test")
20 | sys.stderr.write("more text for a test")
21 | out, err = capsys.readouterr()
22 | assert out == "bogus text for a test\n"
23 | assert out != "something else"
24 | assert err == "more text for a test"
25 | assert err != "something else"
26 |
27 |
28 | # ///////////////////////////////////////////////////////
29 | #
30 | # Command line error tests
31 | #
32 | # ///////////////////////////////////////////////////////
33 |
34 |
35 | def test_crunch_missing_argument_error(capsys):
36 | with pytest.raises(SystemExit) as exit_info:
37 | src.crunch.main([])
38 |
39 | out, err = capsys.readouterr()
40 | assert len(err) > 0
41 | assert err.startswith("[ ! ]") is True
42 | assert exit_info.value.code == 1
43 |
44 |
45 | def test_crunch_missing_file_error(capsys):
46 | with pytest.raises(SystemExit) as exit_info:
47 | src.crunch.main(["bogusfile.png"])
48 |
49 | out, err = capsys.readouterr()
50 | assert len(err) > 0
51 | assert err.startswith("[ ! ]") is True
52 | assert exit_info.value.code == 1
53 |
54 |
55 | def test_crunch_bad_filepath_error(capsys):
56 | with pytest.raises(SystemExit) as exit_info:
57 | src.crunch.main(["src/test_crunch_errors.py"])
58 |
59 | out, err = capsys.readouterr()
60 | assert len(err) > 0
61 | assert err.startswith("[ ! ]") is True
62 | assert exit_info.value.code == 1
63 |
64 |
65 | # ///////////////////////////////////////////////////////
66 | #
67 | # Missing dependency error tests
68 | #
69 | # ///////////////////////////////////////////////////////
70 |
71 | def test_crunch_missing_pngquant_error(capsys, monkeypatch):
72 | def return_bogus_path():
73 | return os.path.join("bogus", "pngquant")
74 | monkeypatch.setattr(src.crunch, 'get_pngquant_path', return_bogus_path)
75 | testpath = os.path.join("testfiles", "robot.png")
76 | with pytest.raises(SystemExit) as exit_info:
77 | src.crunch.main([testpath])
78 |
79 | out, err = capsys.readouterr()
80 | assert err.startswith("[ ! ]") is True
81 | assert exit_info.value.code == 1
82 |
83 |
84 | def test_crunch_missing_zopflipng_error(capsys, monkeypatch):
85 | def return_bogus_path():
86 | return os.path.join("bogus", "zopflipng")
87 | monkeypatch.setattr(src.crunch, 'get_zopflipng_path', return_bogus_path)
88 | testpath = os.path.join("testfiles", "robot.png")
89 | with pytest.raises(SystemExit) as exit_info:
90 | src.crunch.main([testpath])
91 |
92 | out, err = capsys.readouterr()
93 | assert err.startswith("[ ! ]") is True
94 | assert exit_info.value.code == 1
95 |
96 |
97 | # ///////////////////////////////////////////////////////
98 | #
99 | # Multiprocessing.Pool error tests
100 | #
101 | # ///////////////////////////////////////////////////////
102 |
103 | def test_crunch_exception_multiprocessing_pool(capsys, monkeypatch):
104 | def raise_ioerror():
105 | raise IOError
106 | monkeypatch.setattr(src.crunch, 'optimize_png', raise_ioerror)
107 | testpath1 = os.path.join("testfiles", "robot.png")
108 | testpath2 = os.path.join("testfiles", "robot.png")
109 | with pytest.raises(SystemExit) as exit_info:
110 | src.crunch.main([testpath1, testpath2])
111 |
112 | out, err = capsys.readouterr()
113 | assert "[ ! ]" in err
114 | assert exit_info.value.code == 1
115 |
--------------------------------------------------------------------------------
/src/test_crunch_obj.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import os.path
5 | import pytest
6 |
7 | from src.crunch import ImageFile
8 |
9 |
10 | def test_crunch_imagefile_obj_instantiation():
11 | imgfile = ImageFile(os.path.join("img", "robot-1214536_640.png"))
12 | assert imgfile.pre_filepath == os.path.join("img", "robot-1214536_640.png")
13 | assert imgfile.post_filepath == os.path.join("img", "robot-1214536_640-crunch.png")
14 | assert imgfile.pre_size == 197193
15 |
16 |
17 | def test_crunch_imagefile_obj_get_post_filesize_method():
18 | imgfile = ImageFile(os.path.join("img", "robot-1214536_640.png"))
19 | imgfile.get_post_filesize()
20 | assert imgfile.post_size == 67596
21 |
22 |
23 | def test_crunch_imagefile_obj_get_compression_percent_method():
24 | imgfile = ImageFile(os.path.join("img", "robot-1214536_640.png"))
25 | imgfile.pre_size = 100
26 | imgfile.post_size = 10
27 | percent = imgfile.get_compression_percent()
28 | assert percent == float(10)
29 |
30 |
--------------------------------------------------------------------------------
/src/utils/dssim-comparisons.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | echo "Cat image"
4 | dssim "../../img/cat-1285634_640.png" "../../img/cat-1285634_640-crunch.png"
5 |
6 | # sun's rays image
7 | echo "\n\nSun image"
8 | dssim "../../img/suns-rays-478249_640.png" "../../img/suns-rays-478249_640-crunch.png"
9 |
10 | # prairie image
11 | echo "\n\nPrairie image"
12 | dssim "../../img/prairie-679014_640.png" "../../img/prairie-679014_640-crunch.png"
13 |
14 | # Robot image
15 | echo "\n\nRobot image"
16 | dssim "../../img/robot-1214536_640.png" "../../img/robot-1214536_640-crunch.png"
17 |
18 | # Color circule image
19 | echo "\n\nColor circle image"
20 | dssim "../../img/colors-157474_640.png" "../../img/colors-157474_640-crunch.png"
21 |
22 | # Flowers image
23 | echo "\n\nFlowers image"
24 | dssim "../../img/flowers-67839_640.png" "../../img/flowers-67839_640-crunch.png"
25 |
26 |
--------------------------------------------------------------------------------
/src/utils/image-compare.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | from pathlib import Path
5 |
6 |
7 | def print_results(size_pre, size_post):
8 | print(f"Pre: {size_pre}")
9 | print(f"Post: {size_post}")
10 | percent = (size_post / size_pre) * 100
11 | print(f"{percent}%")
12 |
13 |
14 | p = Path(".")
15 |
16 | # cat image
17 | print("Cat image")
18 | pre = os.path.getsize(list(p.glob("../../img/cat-1285634_640.png"))[0])
19 | post = os.path.getsize(list(p.glob("../../img/cat-1285634_640-crunch.png"))[0])
20 | print_results(pre, post)
21 |
22 | # sun's rays image
23 | print("\n\nSun image")
24 | pre = os.path.getsize(list(p.glob("../../img/suns-rays-478249_640.png"))[0])
25 | post = os.path.getsize(list(p.glob("../../img/suns-rays-478249_640-crunch.png"))[0])
26 | print_results(pre, post)
27 |
28 | # prairie image
29 | print("\n\nPrairie image")
30 | pre = os.path.getsize(list(p.glob("../../img/prairie-679014_640.png"))[0])
31 | post = os.path.getsize(list(p.glob("../../img/prairie-679014_640-crunch.png"))[0])
32 | print_results(pre, post)
33 |
34 | # Robot image
35 | print("\n\nRobot image")
36 | pre = os.path.getsize(list(p.glob("../../img/robot-1214536_640.png"))[0])
37 | post = os.path.getsize(list(p.glob("../../img/robot-1214536_640-crunch.png"))[0])
38 | print_results(pre, post)
39 |
40 | # Color circule image
41 | print("\n\nColor circle image")
42 | pre = os.path.getsize(list(p.glob("../../img/colors-157474_640.png"))[0])
43 | post = os.path.getsize(list(p.glob("../../img/colors-157474_640-crunch.png"))[0])
44 | print_results(pre, post)
45 |
46 | # Flowers image
47 | print("\n\nFlowers image")
48 | pre = os.path.getsize(list(p.glob("../../img/flowers-67839_640.png"))[0])
49 | post = os.path.getsize(list(p.glob("../../img/flowers-67839_640-crunch.png"))[0])
50 | print_results(pre, post)
51 |
52 |
--------------------------------------------------------------------------------
/testfiles/cat-cr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/testfiles/cat-cr.png
--------------------------------------------------------------------------------
/testfiles/cat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/testfiles/cat.png
--------------------------------------------------------------------------------
/testfiles/img with spaces.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/testfiles/img with spaces.png
--------------------------------------------------------------------------------
/testfiles/robot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/testfiles/robot.png
--------------------------------------------------------------------------------
/testfiles/test.txt:
--------------------------------------------------------------------------------
1 | test file
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | skipsdist = True
3 | envlist = py310
4 |
5 | [testenv]
6 | deps = pytest
7 | commands =
8 | pytest src
9 |
--------------------------------------------------------------------------------
/ui/MainMenu.nib/keyedobjects.nib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrissimpkins/Crunch/a343532e128bef8d49636b7044b012bf05824658/ui/MainMenu.nib/keyedobjects.nib
--------------------------------------------------------------------------------