class AAQ::AAQ

Attributes

code[R]
data[R]
height[R]
img[R]
unit_horizontal[R]
unit_vertical[R]
width[R]

Public Class Methods

new(input_img, options: {}) click to toggle source
# File lib/aaq.rb, line 14
def initialize(input_img, options: {})
  org_img = Magick::ImageList.new(input_img)
  @unit_horizontal = options[:unit_horizontal] || UNIT_HORIZONTAL_DEFAULT
  @unit_vertical = options[:unit_vertical] || UNIT_VERTICAL_DEFAULT
  @img, @width, @height = resize(org_img)
end

Public Instance Methods

convert() click to toggle source
# File lib/aaq.rb, line 21
def convert
  qtz = quantize
  colors = Set.new(qtz.flatten).to_a.sort
  blank_color = remove_blank_color(colors)

  @data = separate_color(qtz, colors).map do |x|
    x.join.gsub(/0+|1+/) do |c|
      "#{c[0] == '0' ? ':' : '_'}#{c.size}"
    end
  end

  @code = encode(@data, colors, blank_color)
  self
end
encode(data, colors, blank_color) click to toggle source
# File lib/aaq.rb, line 36
    def encode(data, colors, blank_color)
      code = <<~"QUINE"
        eval $s = %w'
        b = #{data}.map{ |x|
          x.gsub(/:[0-9]+|_[0-9]+/){ |c|
            (c[0] == \":\" ? \"0\" : \"1\") * c[1..-1].to_i
          }.reverse.to_i(2)
        };
        e = \"eval $s = %w\" << 39 << $s;
        o = \"\";
        j = k = -1;
        d = \"\" << 39 << \".join\";
        #{width * height}.times{ |i|
          e += (i < e.size) ? "" : $s;
          c = #{Array.new(data.size) { |j| "b[#{j}][i] == 1 ? #{colors[j]} :" }.join(' ')} #{blank_color};
          ARGV.include?(\"--color\")
            ? o << \"\" << 27 << \"[38;5;%sm\" % c << 27 << \"[48;5;%sm\" % c
                << (i < 10 ? e[j+=1] : i > #{width * height - 7} ? d[k+=1] : c == #{blank_color} ? 32 : e[j+=1])
                << 27 << \"[0m\"
            : o << (i < 10 ? e[j+=1] : c == #{blank_color} ? 32 : e[j+=1]);
          o << (i % #{width} == #{width - 1} ? 10 : \"\");
        };
        ARGV.include?(\"--color\") ? \"\" : o[-7, 6] = d;
        puts(o)#'.join
      QUINE
      code.gsub(/\s/, '')
    end
to_s() click to toggle source
# File lib/aaq.rb, line 64
def to_s
  b = data.map do |x|
    x.gsub(/:[0-9]+|_[0-9]+/) { |c|
      (c[0] == ':' ? '0' : '1') * c[1..-1].to_i
    }.reverse.to_i(2)
  end

  str = "eval$s=%w'"
  k = -1
  c = co = @code.gsub("eval$s=%w'", '').gsub("'.join", '')
  height.times do |h|
    width.times do |w|
      i = h * width + w
      next if i < 10

      c += co if i >= c.size
      str += b.map { |x| x[i] }.include?(1) ? c[k += 1] : ' '
    end
    str += "\n"
  end
  str[-7, 6] = '' << 39 << '.join'
  str
end

Private Instance Methods

dist(a, b) click to toggle source
# File lib/aaq.rb, line 131
def dist(a, b)
  a.zip(b).map { |x| (x[0] - x[1])**2 }.sum
end
mode(x, y) click to toggle source
# File lib/aaq.rb, line 108
def mode(x, y)
  h = Hash.new(0)
  y.upto(y + @unit_vertical) do |dy|
    x.upto(x + @unit_horizontal) do |dx|
      h[img.pixel_color(dx, dy)] += 1
    end
  end
  h.max_by { |a| a[1] }[0]
end
quantize() click to toggle source
# File lib/aaq.rb, line 96
def quantize
  memo = {}

  Array.new(height) do |h|
    y = h * @unit_vertical
    Array.new(width) do |w|
      x = w * @unit_horizontal
      to_256color(mode(x, y), memo)
    end
  end
end
remove_blank_color(colors) click to toggle source
# File lib/aaq.rb, line 135
def remove_blank_color(colors)
  if colors.include?(-1)
    colors.delete(-1)
  elsif colors.include?(15)
    colors.delete(15)
  else
    -2
  end
end
resize(org_img) click to toggle source
# File lib/aaq.rb, line 90
def resize(org_img)
  w = org_img.columns / @unit_horizontal
  h = org_img.rows / @unit_vertical
  [org_img.resize(w * @unit_horizontal, h * @unit_vertical), w, h]
end
separate_color(qtz, colors) click to toggle source
# File lib/aaq.rb, line 145
def separate_color(qtz, colors)
  colors.each.map do |c|
    qtz.flatten.map do |q|
      q == c ? '1' : '0'
    end
  end
end
to_256(pix) click to toggle source
# File lib/aaq.rb, line 118
def to_256(pix)
  [pix.red / 256, pix.green / 256, pix.blue / 256]
end
to_256color(pix, memo) click to toggle source
# File lib/aaq.rb, line 122
def to_256color(pix, memo)
  return -1 if pix.opacity.positive?
  return memo[pix] if memo.include?(pix)

  memo[pix] = COLORS.min { |a, b|
    dist(a[1], to_256(pix)) <=> dist(b[1], to_256(pix))
  }[0]
end