def png_to_sizes(input_filename, sizes_array, output_dirname=nil, append_filenames=APPEND_FILE_FORMAT, force_overwrite=false, clear=true, force_clear=false)
      basename = File.basename(input_filename, '.*')

      output_dirname ||= File.join(File.expand_path(File.dirname(input_filename)), "#{basename}_sizes")

      # ensure dir exists
      FileUtils.mkdir_p(output_dirname)

      # ensure dir empty
      if clear
        filename_array = Dir.glob(File.join(output_dirname, '**/*'))

        unless force_clear
          # protect from destructive action
          raise "more than ICO format files in #{output_dirname}" if contains_other_than_ext?(filename_array, :ico)
        end

        FileUtils.rm_rf(filename_array)
      end

      # import base image
      img = ChunkyPNG::Image.from_file(input_filename)

      # resize
      sizes_array.each do |x,y|
        y           ||= x
        img_attrs   = {:x => x, :y => y}
        bn          = basename + Kernel.sprintf(append_filenames, img_attrs)
        fn          = File.join(output_dirname, "#{bn}.png")
        img_out     = img.resample_nearest_neighbor(x, y)

        unless force_overwrite
          raise "File exists: #{fn}" if File.exist?(fn)
        end

        IO.write(fn, img_out)
      end

      return output_dirname
    end
def png_to_sizes(input_filename, sizes_array, output_dirname=nil, append_filenames=APPEND_FILE_FORMAT, force_overwrite=false, clear=true, force_clear=false)
      basename = File.basename(input_filename, '.*')

      output_dirname ||= File.join(File.expand_path(File.dirname(input_filename)), "#{basename}_sizes")

      # ensure dir exists
      FileUtils.mkdir_p(output_dirname)

      # ensure dir empty
      if clear
        filename_array = Dir.glob(File.join(output_dirname, '**/*'))

        unless force_clear
          # protect from destructive action
          raise "more than ICO format files in #{output_dirname}" if contains_other_than_ext?(filename_array, :ico)
        end

        FileUtils.rm_rf(filename_array)
      end

      # import base image
      img = ChunkyPNG::Image.from_file(input_filename)

      # resize
      sizes_array.each do |x,y|
        y           ||= x
        img_attrs   = {:x => x, :y => y}
        bn          = basename + Kernel.sprintf(append_filenames, img_attrs)
        fn          = File.join(output_dirname, "#{bn}.png")
        img_out     = img.resample_nearest_neighbor(x, y)

        unless force_overwrite
          raise "File exists: #{fn}" if File.exist?(fn)
        end

        IO.write(fn, img_out)
      end

      return output_dirname
    end
resize PNG file and write new sizes to directory

 @see https://ruby-doc.org/core-2.2.0/Kernel.html#method-i-sprintf
 @param input_filename   [String]        input filename; required: file is PNG file format
 @param sizes_array      [Array<Array<Integer,Integer]>>, Array<Integer>]
   rectangles use Array with XY: `[x,y]`
   squares use single Integer `N`
   mixed indices is valid
   example: `[24, [24,24], [480,270], 888] # a[0] => 24x24; a[1] => 24x24; a[2] => 480x270; a[3] => 888x888`
 @param output_dirname   [String]        (optional)
   directory name including expanded path
   default: new dir named input_filename's basename + "_sizes" in same dir as input_filename
 @param append_filenames [String]        (optional,required-with-supplied-default)
   append resized filenames with Kernel#sprintf format_string
   available args: `{:x => N, :y => N}`
   default: `"-%<x>dx%<y>d"`
 @param  force_overwrite [Boolean]       overwrite existing resized images
 @param  clear           [Boolean]       default: `true`
 @param  force_clear     [Boolean]       clear output_dirname of contents before write; default: false
 @return                 [String]        output_dirname; default: false