module CatpixMini
Provides a function to print images in the terminal. A range of different formats is supported (check out what ImageMagick supports). Under the hood, this module uses two components:
-
[rmagick](rmagick.github.io/) to read and scale the images and
-
[tco](github.com/pazdera/tco) to map their pixels on the extended colour palette in the terminal.
Some other minor features like centering and handling background colours are supplied directly by this module.
Copyright © 2015 Radek Pazdera <me@radek.io> Distributed under the MIT License (see LICENSE.txt)
Constants
- MAX_OPACITY
- VERSION
Version of the gem
Public Class Methods
Print an image to the terminal.
All formats supported by ImageMagick are supported. The image's colours will be mapped onto the extended 256 colour palette. Also by default, it will be scaled down to fit the width of the terminal while keeping its proportions. This can be changed using the `options` parameter.
@param [Hash] options Adjust some parameters of the image when printed. @option options [Float] :limit_x A factor of the terminal window's width.
If present, the image will be scaled down to fit (proportions are kept). Using 0 will disable the scaling. [default: 1.0]
@option options [Float] :limit_y A factor of the terminal window's height.
If present, the image will be scaled down to fit (proportions are kept). Using 0 will disable the scaling. [default: 0]
@option options [Boolean] :center_x Center the image horizontally in the
terminal window. [default: false]
@option options [Boolean] :center_y Center the image vertically in the
terminal window. [default: false]
@option options [String] :bg Background colour to use in case there are
any fully transparent pixels in the image. This can be a RGB value '#c0ffee' or a tco alias 'red' or 'blue'. [default: nil]
@option options [Boolean] :bg_fill Fill the margins around the image with
background colour. [default: false]
@option options [String] :resolution Determines the pixel size of the
rendered image. Can be set to `high`, `low` or `auto` (default). If set to `auto` the resolution will be picked automatically based on your terminal's support of unicode.
# File lib/catpix_mini.rb, line 51 def self.print_image(path, options={}) options = default_options.merge! options if options[:resolution] == 'auto' options[:resolution] = can_use_utf8? ? 'high' : 'low' end @@resolution = options[:resolution] img = load_image path resize! img, options[:limit_x], options[:limit_y] margins = get_margins img, options[:center_x], options[:center_y] margins[:colour] = options[:bg_fill] ? options[:bg] : nil if high_res? do_print_image_hr img, margins, options else do_print_image_lr img, margins, options end end
Private Class Methods
# File lib/catpix_mini/private.rb, line 31 def self.can_use_utf8? ENV.values_at("LC_ALL", "LC_CTYPE", "LANG").compact.first.include?("UTF-8") end
# File lib/catpix_mini/private.rb, line 13 def self.default_options { limit_x: 1.0, limit_y: 0, center_x: false, center_y: false, bg: nil, bg_fill: false, resolution: 'auto' } end
Print the image in high resolution (using unicode's upper half block)
# File lib/catpix_mini/private.rb, line 185 def self.do_print_image_hr(img, margins, options) pixels = img.get_pixels rows = img[:height] cols = pixels[0].length print prep_vert_margin margins[:top], margins[:colour] 0.step(rows - 1, 2) do |row| # line buffering makes it about 20% faster buffer = prep_horiz_margin margins[:left], margins[:colour] 0.upto(cols - 1) do |col| top_pixel = pixels[row][col] colour_top = top_pixel bottom_pixel = pixels[row+1][col] colour_bottom = bottom_pixel buffer += prep_hr_pixel colour_top, colour_bottom end buffer += prep_horiz_margin margins[:right], margins[:colour] puts buffer end print prep_vert_margin margins[:bottom], margins[:colour] end
Print the image in low resolution
# File lib/catpix_mini/private.rb, line 162 def self.do_print_image_lr(img, margins, options) pixels = img.get_pixels rows = img[:height] cols = pixels[0].length print prep_vert_margin margins[:top], margins[:colour] 0.upto(rows - 1) do |row| buffer = prep_horiz_margin margins[:left], margins[:colour] 0.upto(cols - 1) do |col| pixel = pixels[row][col] buffer += prep_lr_pixel pixel end buffer += prep_horiz_margin margins[:right], margins[:colour] puts buffer end print prep_vert_margin margins[:bottom], margins[:colour] end
Determine the margins based on the centering options
# File lib/catpix_mini/private.rb, line 96 def self.get_margins(img, center_x, center_y) margins = {} tw, th = get_screen_size x_space = tw - img[:width] if center_x margins[:left] = x_space / 2 margins[:right] = x_space / 2 + x_space % 2 else margins[:left] = 0 margins[:right] = x_space end y_space = th - img[:height] if center_y margins[:top] = y_space / 2 margins[:bottom] = y_space / 2 + y_space % 2 else margins[:top] = 0 margins[:bottom] = 0 end if high_res? and margins[:top] % 2 and margins[:bottom] % 2 margins[:top] -= 1 margins[:bottom] += 1 end margins end
Returns normalised size of the terminal window
Catpix can use either two blank spaces to approximate a pixel in the temrinal or the 'upper half block' and 'bottom half block' characters.
Depending on which of the above will be used, the screen size must be normalised accordingly.
# File lib/catpix_mini/private.rb, line 71 def self.get_screen_size th, tw = TermInfo.screen_size if high_res? then [tw, th * 2] else [tw / 2, th] end end
# File lib/catpix_mini/private.rb, line 27 def self.high_res? @@resolution == 'high' end
# File lib/catpix_mini/private.rb, line 76 def self.load_image(path) MiniMagick::Image.open(path) end
# File lib/catpix_mini/private.rb, line 150 def self.prep_horiz_margin(size, colour) buffer = "" if high_res? size.times { buffer += prep_hr_pixel nil, nil } else size.times { buffer += prep_lr_pixel nil } end buffer.bg colour end
# File lib/catpix_mini/private.rb, line 43 def self.prep_hr_pixel(colour_top, colour_bottom) upper = "\u2580" lower = "\u2584" return " " if colour_bottom.nil? and colour_top.nil? return lower.fg colour_bottom if colour_top.nil? return upper.fg colour_top if colour_bottom.nil? c_top = Tco::match_colour colour_top c_bottom = Tco::match_colour colour_bottom if c_top == c_bottom return " ".bg "@#{c_top}" end upper.fg("@#{c_top}").bg("@#{c_bottom}") end
# File lib/catpix_mini/private.rb, line 35 def self.prep_lr_pixel(colour) colour ? " ".bg(colour) : " " end
“DRAWING” METHODS
# File lib/catpix_mini/private.rb, line 130 def self.prep_vert_margin(size, colour) tw, th = get_screen_size buffer = "" if high_res? (size / 2).times do sub_buffer = "" tw.times { sub_buffer += prep_hr_pixel nil, nil } buffer += sub_buffer.bg(colour) + "\n" end else size.times do sub_buffer = "" tw.times { sub_buffer += prep_lr_pixel nil } buffer += sub_buffer.bg(colour) + "\n" end end buffer end
# File lib/catpix_mini/private.rb, line 60 def self.print_hr_pixel(colour_top, colour_bottom) print prep_hr_pixel colour_top, colour_bottom end
# File lib/catpix_mini/private.rb, line 39 def self.print_lr_pixel(colour) print prep_lr_pixel colour end
Scale the image down based on the limits while keeping the aspect ratio
# File lib/catpix_mini/private.rb, line 81 def self.resize!(img, limit_x=0, limit_y=0) tw, th = get_screen_size iw = img[:width] ih = img[:height] width = limit_x > 0 ? (tw * limit_x).to_i : iw height = limit_y > 0 ? (th * limit_y).to_i : ih # Resize the image if it's bigger than the limited viewport if iw > width or ih > height img.resize "#{width}x#{height}" end end