diff --git a/.gitignore b/.gitignore index 8914997..ef8d87e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ builddir/ testout/ +.vagrant \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index ee1b42f..b94b33b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "C_Cpp.default.compileCommands": "builddir/compile_commands.json", + "C_Cpp.default.compileCommands": "/home/catmeow/Documents/MyProjects/X16/graphicsconverter/builddir/compile_commands.json", "mesonbuild.configureOnOpen": true, "files.associations": { "cstdint": "cpp", diff --git a/CAT.jpg b/CAT.jpg new file mode 100644 index 0000000..639fd89 Binary files /dev/null and b/CAT.jpg differ diff --git a/DPAL.BIN b/DPAL.BIN new file mode 100644 index 0000000..7cfdaef Binary files /dev/null and b/DPAL.BIN differ diff --git a/TEST.BMX b/TEST.BMX new file mode 100644 index 0000000..af0f28c Binary files /dev/null and b/TEST.BMX differ diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..d1dd5e4 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,85 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure("2") do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://vagrantcloud.com/search. + config.vm.box = "iznav/w10x64" + config.vm.communicator = "winssh" + + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # NOTE: This will enable public access to the opened port + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine and only allow access + # via 127.0.0.1 to disable public access + # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + # config.vm.network "private_network", ip: "192.168.33.10" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + # config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + # config.vm.synced_folder "../data", "/vagrant_data" + + # Disable the default share of the current code directory. Doing this + # provides improved isolation between the vagrant box and your host + # by making sure your Vagrantfile isn't accessable to the vagrant box. + # If you use this you may want to enable additional shared subfolders as + # shown above. + # config.vm.synced_folder ".", "/vagrant", disabled: true + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + # config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine + # vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + # end + # + # View the documentation for the provider you are using for more + # information on available options. + + # Enable provisioning with a shell script. Additional provisioners such as + # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the + # documentation for more information about their specific syntax and use. + config.vm.provision "shell", inline: <<-SHELL + # Download the archive + (New-Object System.Net.WebClient).DownloadFile('https://github.com/msys2/msys2-installer/releases/download/nightly-x86_64/msys2-base-x86_64-latest.sfx.exe', 'msys2.exe') + .\\msys2.exe -y -oC:\\ # Extract to C:\\msys64 + Remove-Item msys2.exe # Delete the archive again + # Run for the first time + C:\\msys64\\usr\\bin\\bash -lc ' ' + # Update MSYS2 + C:\\msys64\\usr\\bin\\bash -lc 'pacman --noconfirm -Syuu' # Core update (in case any core packages are outdated) + C:\\msys64\\usr\\bin\\bash -lc 'pacman --noconfirm -Syuu' # Normal update + SHELL +end diff --git a/bitmapx16.cpp b/bitmapx16.cpp index 50e4b68..202baa5 100644 --- a/bitmapx16.cpp +++ b/bitmapx16.cpp @@ -169,7 +169,7 @@ void BitmapX16::load_x16(const char *filename) { vector buf; size_t bufsize = 0; size_t bufpos = 0; - uint8_t palette_used = 0; + uint16_t palette_used = 0; uint8_t pixels_per_byte; uint16_t image_start = 0; bool compressed = false; @@ -210,6 +210,7 @@ void BitmapX16::load_x16(const char *filename) { h = buf[8] | (buf[9] << 8); printf("Image size: (%lu, %lu)\n", w, h); palette_used = buf[10]; + if (palette_used == 0) palette_used = 256; significant_start = buf[11]; significant_count = palette_used; image_palette_count = 0; @@ -306,7 +307,28 @@ BitmapX16::BitmapX16() { palette_entries = vector(); } void BitmapX16::generate_palette() { - uint16_t max = (uint16_t)image->colorMapSize(); + size_t min; + uint16_t max; + if (!generate_palette_enabled || !write_palette) { + significant_count = write_palette ? palette_entries.size() : 0; + max = significant_count; + min = 256 - max; + if (min >= 16 && write_palette) { + significant_start = 16; + } + if (max > 256) max = 256; + if (bpp == 0) { + if (max <= 4) { + bpp = 2; + } else if (max <= 16) { + bpp = 4; + } else { + bpp = 8; + } + } + return; + } + max = (uint16_t)image->colorMapSize(); if (max > 256) max = 256; if (bpp == 0) { if (max <= 4) { @@ -317,7 +339,7 @@ void BitmapX16::generate_palette() { bpp = 8; } } - size_t min = 256 - (1 << bpp); + min = 256 - (1 << bpp); if (min >= 16) { significant_start = 16; } @@ -399,4 +421,51 @@ void BitmapX16::enable_compression(bool enabled) { } bool BitmapX16::compression_enabled() const { return compress; +} +vector BitmapX16::get_palette() const { + return palette_entries; +} +vector BitmapX16::get_extra_entries() const { + return extra_palette_entries; +} +void BitmapX16::set_palette(vector entries) { + palette_entries = entries; + palette_entries.shrink_to_fit(); + extra_palette_entries.clear(); + generate_palette_enabled = false; + image->quantizeColors(entries.size()); + image->quantizeColorSpace(MagickCore::RGBColorspace); + image->colorMapSize(entries.size()); + for (uint16_t i = 0; i < entries.size(); i++) { + image->colorMap(i, entries[i].toColor()); + } + image->quantize(); +} +void BitmapX16::enable_palette_generation() { + generate_palette_enabled = true; +} +bool BitmapX16::palette_generation_enabled() const { + return generate_palette_enabled && write_palette; +} +void BitmapX16::read_palette(const char *filename) { + size_t fsize = std::filesystem::file_size(filename); + if (fsize > 512 || (fsize % 2 != 0)) { + printf("Invalid palette file size! Palette files must be raw VERA palette data, sized as a multiple of 2, and up to 512 bytes(256 entries)"); + throw std::exception(); + } + vector entries; + uint8_t entry[2]; + std::ifstream file(filename, std::ifstream::binary|std::ifstream::in); + for (size_t i = 0; i < fsize; i += 2) { + file.read((char*)entry, 2); + entries.push_back(PaletteEntry(&entry[0])); + if (file.eof()) { + break; + } + if (file.bad()) { + printf("Error reading file!"); + throw std::exception(); + } + } + set_palette(entries); } \ No newline at end of file diff --git a/bitmapx16.h b/bitmapx16.h index f010f30..9f278d4 100644 --- a/bitmapx16.h +++ b/bitmapx16.h @@ -26,6 +26,10 @@ class BitmapX16 { bool quantize_colors = false; /// \brief Enables LZSA compression bool compress = false; + /// \brief False to set the used palette to 0, and palette start also to 0. + bool write_palette = true; + /// \brief True if the palette should be generated, false if it has been set manually and shouldn't be regenerated. + bool generate_palette_enabled = true; /// \brief Current width size_t w = 0; /// \brief Current height @@ -80,6 +84,17 @@ class BitmapX16 { uint8_t extra_to_real_palette(uint8_t idx); float closeness_to_color(PaletteEntry a, PaletteEntry b); public: + vector get_palette() const; + vector get_extra_entries() const; + void read_palette(const char *filename); + /// \brief Sets the palette to use + /// \param entries The entries to replace the existing palette + void set_palette(vector entries); + /// \brief Enables palette generation after disabling it with set_palette. + void enable_palette_generation(); + /// \brief Checks the status of palette generation + /// \returns true if palette generation is enabled, false otherwise. + bool palette_generation_enabled() const; /// \brief Sets the border color extra palette entry. /// \param idx The index of the palette entry, must be an index into the extra palette entry list. void set_border_color(uint8_t idx); @@ -92,13 +107,13 @@ class BitmapX16 { uint8_t add_palette_entry(PaletteEntry entry); /// \brief Sets the bits per pixel of the image /// \param bpp The bits per pixel of the image, one of 1, 2, 4, or 8 - void set_bpp(uint8_t bpp); + void set_bpp(uint8_t bpp = 8); /// \brief Returns the bits per pixel of the image /// \returns The bits per pixel of the image, one of 1, 2, 4, or 8 uint8_t get_bpp() const; /// \brief Sets the maximum amount of colors to be used. /// \param value The maximum amount of colors to use. - void set_significant(uint8_t value); + void set_significant(uint8_t value = 0); /// \brief Returns the maximum amount of colors to be used /// \returns The maximum amount of colors once written uint8_t get_significant() const; @@ -117,7 +132,7 @@ class BitmapX16 { size_t get_height() const; /// \brief Enables or disables dithering /// \param enabled Pass true to enable, false to disable - void enable_dithering(bool enabled); + void enable_dithering(bool enabled = true); /// \brief Returns the status of the dithering flag /// \returns The value of the dithering flag bool dithering_enabled() const; diff --git a/main.cpp b/main.cpp index d41e359..aee9c01 100644 --- a/main.cpp +++ b/main.cpp @@ -8,13 +8,15 @@ #include #include #include +#include +#include #include "bitmapx16.h" using std::vector; using std::map; using std::stoi; using namespace Magick; void usage() { - printf("Usage: veraconvert: [options]\n"); + printf("Usage: bmxconvert: [options]\n"); printf("Options may be:\n"); printf("-in \n"); printf("\tSets the input file.\n"); @@ -30,6 +32,8 @@ void usage() { printf("\tEnables dithering of the output\n"); printf("-border \n"); printf("\tIf possible, adds a border color with the specified RGB values which are in the range of 0-15.\n"); + printf("-border-idx \n"); + printf("\tSets the border color by palette index\n"); printf("-reverse\n"); printf("\tConverts to PC formats. Incompatible with -dither, -type, and -significant - they will be ignored.\n"); printf("-debug \n"); @@ -38,6 +42,10 @@ void usage() { printf("\tUse an ! before a flag to disable it.\n"); printf("-compress\n"); printf("\tCompresses the image with LZSA compression\n"); + printf("-probe\n"); + printf("\tDisplays information about a file, assuming BMX format."); + printf("-palette-file \n"); + printf("\tSets the palette file to use.\n"); printf("-help\n"); printf("\tDisplays this help message.\n"); exit(1); @@ -48,11 +56,17 @@ int main(int argc, char **argv) { uint8_t tbpp = 0; uint16_t tcolorcount = 0; const char *error_msg_part = "load the image"; + const char *palette_file = NULL; bool dither = false; bool reverse = false; bool compress = false; uint8_t br, bg, bb; bool border_set = false; + bool probe_only = false; + std::filesystem::path executable_path = std::filesystem::weakly_canonical(*argv).parent_path(); +#ifdef _WIN32 + setenv("MAGICK_CODER_MODULE_PATH", executable_path.c_str(), 0); +#endif InitializeMagick(*argv); argc--; argv++; @@ -188,8 +202,21 @@ int main(int argc, char **argv) { compress = true; argc--; argv++; + } else if (!strcmp(argv[0], "-probe")) { + probe_only = true; + argc--; + argv++; } else if (!strcmp(argv[0], "-help")) { usage(); + } else if (!strcmp(argv[0], "-palette-file")) { + argc--; + argv++; + if (!argc || argv[0][0] == '-') { + usage(); + } + palette_file = argv[0]; + argc--; + argv++; } else if (!strcmp(argv[0], "-debug")) { argc--; argv++; @@ -219,13 +246,21 @@ int main(int argc, char **argv) { } argc--; argv++; + } else if (strlen(argv[0]) == 0) { + // Skip empty arguments. + argc--; + argv++; } else { - printf("Error: Invalid command line argument.\n"); + printf("Error: Invalid command line argument: '%s'\n", argv[0]); usage(); } } - if (input == NULL || output == NULL) { - printf("Input and output must be specified!\n"); + if (input == NULL || ((output != NULL) == probe_only)) { + if (probe_only) { + printf("Input must be specified and output must not be!\n"); + } else { + printf("Input and output must be specified!\n"); + } usage(); } if (tbpp == 0) tbpp = 8; @@ -247,6 +282,16 @@ int main(int argc, char **argv) { error_msg_part = "resize the image"; bitmap.queue_resize(tw, th); } + if (probe_only) { + error_msg_part = "obtain information"; + tbpp = bitmap.get_bpp(); + uint8_t border = bitmap.get_border_color(); + uint16_t w, h; + w = bitmap.get_width(); + h = bitmap.get_height(); + uint8_t entry_count = bitmap.get_significant(); + uint8_t entry_start = bitmap.get_significant_start(); + } if (reverse) { error_msg_part = "write the file"; printf("Writing PC image file...\n"); @@ -254,6 +299,9 @@ int main(int argc, char **argv) { } else { error_msg_part = "apply the settings"; printf("Applying settings...\n"); + if (palette_file != NULL) { + bitmap.read_palette(palette_file); + } bitmap.enable_dithering(dither); bitmap.set_bpp(tbpp); bitmap.set_significant(tcolorcount); @@ -272,6 +320,7 @@ int main(int argc, char **argv) { } } catch (std::exception &e) { printf("Failed to %s!\n", error_msg_part); + return 1; } return 0; } diff --git a/meson.build b/meson.build index ab1a9c5..262ab03 100644 --- a/meson.build +++ b/meson.build @@ -8,6 +8,19 @@ deps = [ dependency('Magick++', version : '>=6.9.11'), dependency('openmp') ] +ncurses = dependency('curses', required: get_option('use_terminfo')) +if ncurses.found() + deps += ncurses +endif +conf = configuration_data() +check_headers = [ + ['curses.h', 'HAVE_CURSES_H'] +] +foreach h : check_headers + if meson.get_compiler('cpp').has_header(h.get(0)) + conf.set(h.get(1), 1) + endif +endforeach srcs = [ 'palette.cpp', 'bitmapx16.cpp', diff --git a/meson.options b/meson.options new file mode 100644 index 0000000..7c12586 --- /dev/null +++ b/meson.options @@ -0,0 +1 @@ +option('use_terminfo', type: 'feature', value: 'auto') \ No newline at end of file diff --git a/test.sh b/test.sh index 5747dd9..cde06af 100755 --- a/test.sh +++ b/test.sh @@ -1,4 +1,32 @@ #!/bin/bash +bool_true() { + [ "$1" = "true" ] 2>/dev/null || [ "$1" -ne 0 ] 2>/dev/null + [ $? -ne 0 ] # Convert $? to 1 or 0 value + return $? +} +bool_false() { + ! bool_true + return $? +} +cmd_avail() { + [ -x "$(command -v "$1")" ] && return 0 || return 1 +} +tests=0 +failed=0 +succeeded=0 +color=1 +tput_avail=0 +if cmd_avail tput; then + tput_avail=1 +fi +if bool_true $tput_avail; then + COLORS=$(tput color 2>/dev/null) + if [ $? -eq 0 ] && [ $COLORS -gt 2 ]; then + color=1 + else + color=0 + fi +fi usage() { cat << EOF $0 usage: @@ -10,33 +38,49 @@ $0 usage: Adds a bitdepth to the list to test with -r|--resize x Adds a resize to the list to test with ---no-reverse +-R|--no-reverse Disables reverse operation testing ---no-dither +-D|--no-dither Disables dithering testing +-S|--no-compress + Disables compression testing +--no-generate-palette + Disables palette generation when testing and uses prebuilt palette files only +-p|--palette-file + Uses the specified palette file. -d|--debug Use debug flags with the converter program. -n|--no-defaults Disables default settings +-c|--color + Enables color. +-C|--no-color + Disables color. +-h|--help + Shows this message. + EOF exit 1 } oldpwd="$(pwd)" cd "$(dirname "$0")" -converter="./builddir/b16converter" +converter="./builddir/bmxconverter" prebuilt=0 images=() bpps=() resize=() +palettes=() +generate_palette=1 enable_defaults=1 dither=1 reverse=1 enable_compression=1 enable_reverse=1 enable_dither=1 +enable_probe=1 debug_flags="" outdir="testout" -OPTIONS=$(getopt -o "b:hp:i:r:no:d:" --long "help,use-program:,input-image:,output-bpp:,resize:,no-defaults,output-dir:,debug:,no-reverse,no-dither,no-compress" -- "$@") +OPTIONS=$(getopt -o "b:hp:i:r:no:d:p:cCDRS" --long "help,use-program:,input-image:,output-bpp:,resize:,no-defaults,output-dir:,debug:,no-reverse,no-dither,no-compress,palette-file,no-generate-palette,no-probe,color,no-color" -- "$@") if [ $? != 0 ]; then echo "Getopt error." usage @@ -72,15 +116,15 @@ while [ -n "$1" ]; do enable_defaults=0 shift ;; - --no-reverse) + -R|--no-reverse) enable_reverse=0 shift ;; - --no-dither) + -D|--no-dither) enable_dither=0 shift ;; - --no-compress) + -S|--no-compress) enable_compression=0 shift ;; @@ -88,6 +132,26 @@ while [ -n "$1" ]; do debug_flags="${debug_flags}$2" shift 2 ;; + -p|--palette-file) + palettes+="$2" + shift 2 + ;; + --no-generate-palette) + generate_palette=0 + shift + ;; + --no-probe) + enable_probe=0 + shift + ;; + -c|--color) + color=1 + shift + ;; + -C|--no-color) + color=0 + shift + ;; --) shift break @@ -98,8 +162,11 @@ while [ -n "$1" ]; do ;; esac done +if [ $generate_palette -ne 0 ]; then + palettes+="" +fi if [ $enable_defaults -ne 0 ]; then - images+=("TEST.png" "PACK.png") + images+=("TEST.png" "PACK.png" "CAT.jpg") bpps+=(1 2 4 8) resize+=("8x8" "16x16" "32x32" "64x64" "320x240" "640x480") fi @@ -107,40 +174,139 @@ if [ $prebuilt -eq 0 ]; then meson setup builddir meson compile -C builddir || exit $? fi +bold() { + if bool_false "$color"; then + return + fi + if bool_true "$tput_avail"; then + tput bold + else + printf "\033[1m" + fi +} +italic() { + if bool_false "$color"; then + return + fi + if bool_true "$tput_avail"; then + tput enter_italics_mode + else + printf "\033[3m" + fi +} +setfgcolor() { + if bool_false "$color"; then + return + fi + if bool_true "$tput_avail"; then + tput setaf "$1" + else + printf "\033[0;3%sm" "$1" + fi +} +setbgcolor() { + if bool_false "$color"; then + return + fi + if bool_true "$tput_avail"; then + tput setab "$1" + else + printf "\033[0;4%sm" "$1" + fi +} +resetcolor() { + if bool_false "$color"; then + return + fi + if bool_true "$tput_avail"; then + tput sgr0 + else + printf "\033[0m" + fi +} mkdir -p "$outdir" run() { - printf "Running: %s\n" "$*" - "$@" + converter="$1" + shift + infile="$1" + shift + outfile="$1" + shift + reverse="$1" + shift + extra_flags_run=() + if [ -n "$reverse" ]; then + extra_flags_run+=( "$reverse" ) + fi + setfgcolor 2 + bold + printf "Running: %s\n" "$converter $* -in $infile -out $outfile" + resetcolor + setfgcolor 8 + tests=$(($tests+1)) + "$converter" -in "$infile" -out "$outfile" "${extra_flags_run[@]}" "$@" + if [ $? -ne 0 ]; then + setfgcolor 1 + printf "Test failed.\n" + resetcolor + failed=$(($failed+1)) + else + succeeded=$(($succeeded+1)) + fi + resetcolor + if [ $enable_probe -ne 0 ]; then + setfgcolor 2 + bold + printf "Probing %s...\n" "$outfile" + resetcolor + setfgcolor 8 + "$converter" -in "$outfile" -probe "${extra_flags_run[@]}" + resetcolor + tests=$(($tests+1)) + if [ $? -ne 0 ]; then + setfgcolor 1 + printf "Test failed.\n" + resetcolor + failed=$(($failed+1)) + else + succeeded=$(($succeeded+1)) + fi + fi } for img in "${images[@]}"; do for bpp in "${bpps[@]}"; do for size in "${resize[@]}"; do for compressflag in -compress ""; do - width="$(echo -n "$size" | cut -dx -f1)" - height="$(echo -n "$size" | cut -dx -f2)" - name="$(basename "$img" | sed 's/\.png$//')" - name="$(printf "%s.%sP.%sB" "$name" "$width" "$bpp")" - extraflags=() - if [ -n "$compressflag" ]; then - if [ $enable_compression -eq 0 ]; then - continue + for palette in "${palettes[@]}"; do + width="$(echo -n "$size" | cut -dx -f1)" + height="$(echo -n "$size" | cut -dx -f2)" + name="$(basename "$img" | sed 's/\.png$//' | sed 's/\.jpg$//' | sed 's/\.jpeg$//')" + name="$(printf "%s.%sP.%sB" "$name" "$width" "$bpp")" + extraflags=() + if [ -n "$palette" ]; then + extraflags+=( "-palette-file" "$palette" ) fi - extraflags+=( "$compressflag" ) - name+=".C" - fi - run "$converter" "${extraflags[@]}" -in "$img" -out "$outdir/$name.BMX" -bpp "$bpp" -resize "$width" "$height" -border 15 0 15 -debug "$debug_flags" - if [ $enable_dither -ne 0 ]; then - run "$converter" "${extraflags[@]}" -in "$img" -out "$outdir/$name.D.BMX" -bpp "$bpp" -resize "$width" "$height" -dither -border 15 0 15 -debug "$debug_flags" - fi - if [ $enable_reverse -ne 0 ]; then - run "$converter" "${extraflags[@]}" -reverse -in "$outdir/$name.BMX" -out "$outdir/$name.PNG" -resize "$width" "$height" -debug "$debug_flags" + if [ -n "$compressflag" ]; then + if [ $enable_compression -eq 0 ]; then + continue + fi + extraflags+=( "$compressflag" ) + name+=".C" + fi + run "$converter" "$img" "$outdir/$name.BMX" "" "${extraflags[@]}" "" -bpp "$bpp" -resize "$width" "$height" -border 15 0 15 -debug "$debug_flags" if [ $enable_dither -ne 0 ]; then - run "$converter" "${extraflags[@]}" -reverse -in "$outdir/$name.D.BMX" -out "$outdir/$name.D.PNG" -resize "$width" "$height" -dither -debug "$debug_flags" + run "$converter" "$img" "$outdir/$name.D.BMX" "" "${extraflags[@]}" "" -bpp "$bpp" -resize "$width" "$height" -dither -border 15 0 15 -debug "$debug_flags" fi - fi + if [ $enable_reverse -ne 0 ]; then + run "$converter" "$outdir/$name.BMX" "$outdir/$name.PNG" -reverse "${extraflags[@]}" -resize "$width" "$height" -debug "$debug_flags" + if [ $enable_dither -ne 0 ]; then + run "$converter" "$outdir/$name.D.BMX" "$outdir/$name.D.PNG" -reverse "${extraflags[@]}" -resize "$width" "$height" -dither -debug "$debug_flags" + fi + fi + done done done done done - +printf "%s total test cases, %s failed, %s succeeded, %s%% succeeded and %s%% failed.\n" "$tests" "$failed" "$succeeded" "$((($succeeded*100)/$tests))" "$((($failed*100)/$tests))" cd "$oldpwd"