#!/usr/bin/env ruby # -*- ruby -*- # # Reads in a bitmap and outputs strings with terminal-escaped colors. # # WARNING: This has been tested only on a picture of spudtrooper, so # don't venture into more complicated images unless you are willing to # accept the consequences. # class Bitmap # HEADER attr_accessor :type # unsigned short: Magic identifier attr_accessor :filesize # unsigned int: File size in bytes attr_accessor :reserved1 # unsigned short reserved1; attr_accessor :reserved2 # unsigned short reserved2; attr_accessor :offset # unsigned int: Offset to image data, bytes # INFOHEADER attr_accessor :headersize # unsigned int: Header size in bytes attr_accessor :width # int: Width and height of image attr_accessor :height # int: Width and height of image attr_accessor :planes # unsigned short: Number of colour planes attr_accessor :bits # unsigned short: Bits per pixel attr_accessor :compression # unsigned int: Compression type attr_accessor :imagesize # unsigned int: Image size in bytes attr_accessor :xresolution # int: Pixels per meter attr_accessor :yresolution # int: Pixels per meter attr_accessor :ncolours # unsigned int: Number of colours attr_accessor :importantcolours # unsigned int: Important colours # IMAGE_DATA attr_accessor :data # int int -> Bitmap::Color def color(row,col) Color.new @data[height-1-row][col] end # string -> Bitmap def self.read(file) BitmapReader.new(file).read_bitmap end class Color def initialize(v) @v = v end def alpha Bitmap::alpha @v end def red Bitmap::red @v end def green Bitmap::green @v end def blue Bitmap::blue @v end end def self.alpha(v) (v >> 24) & 0xff end def self.red(v) (v >> 16) & 0xff end def self.green(v) (v >> 8) & 0xff end def self.blue(v) (v >> 0) & 0xff end private class BitmapReader def initialize(file) @file = File.new file,'r' end def read_bitmap file = @file res = Bitmap.new # header res.type, res.filesize, res.reserved1, res.reserved2, res.offset = file.read(2+4+2+2+4).unpack('SLSSL') # infoheader res.headersize, res.width, res.height, res.planes, res.bits, res.compression, res.imagesize, res.xresolution, res.yresolution, res.ncolours, res.importantcolours = file.read(4+4+4+2+2+4+4+4+4+4+4).unpack('LllSSLLllLL') # image data res.data = [] res.height.times do res.data << file.read(4*res.width).unpack('l*') end return res end end end class Color ESC = "\033" CLOSE = ESC + "[0m" def initialize(code,str) @code = code @str = str end def colorize(s) color + s.to_s + CLOSE end def backgroundize(s) background + s.to_s + CLOSE end def color "\033[" + @code.to_s + "m" end def background "\033[" + (@code+10).to_s + "m" end BLACK = Color.new 30,'BLACK' RED = Color.new 31,'RED' GREEN = Color.new 32,'GREEN' YELLOW = Color.new 33,'YELLOW' BLUE = Color.new 34,'BLUE' MAGENTA = Color.new 35,'MAGENTA' CYAN = Color.new 36,'CYAN' WHITE = Color.new 37,'WHITE' end class ToTerminal def main(argv) argv.each do |arg| to_terminal arg end end private def to_terminal(file) b = Bitmap.read file b.height.times do |row| b.width.times do |col| print find_nearest_color(b.color row,col).colorize '##' end puts end end # Bitmap::Color -> Color def find_nearest_color(c) if c.alpha < 10 return Color::CYAN end if c.red > 0xa0 and c.green > 0xa0 and c.blue > 0xa0 return Color::WHITE end if c.red < 0x10 and c.green < 0x10 and c.blue < 0x10 return Color::BLUE end if c.red > 0x70 return Color::RED end if c.red > 0x50 return Color::MAGENTA end return Color::BLACK end end ToTerminal.new.main ARGV