class Pixelart::Image

Constants

CHARS
PALETTE8BIT

predefined palette8bit color maps

   (grayscale to sepia/blue/false/etc.)
- todo/check - keep "shortcut" convenience predefined map - why? why not?

Public Class Methods

new( width, height, initial=Color::TRANSPARENT ) click to toggle source
# File lib/pixelart/image.rb, line 52
def initialize( width, height, initial=Color::TRANSPARENT )
   ### todo/fix:
   ##  change params to *args only - why? why not?
   ##     make width/height optional if image passed in?

  if initial.is_a?( ChunkyPNG::Image )
    @img = initial
  else
    ## todo/check - initial - use parse_color here too e.g. allow "#fff" too etc.
    @img = ChunkyPNG::Image.new( width, height, initial )
  end
end
parse( pixels, colors:, chars: CHARS ) click to toggle source

todo/check: support default chars encoding auto-of-the-box always

or require user-defined chars to be passed in - why? why not?
# File lib/pixelart/image.rb, line 17
def self.parse( pixels, colors:, chars: CHARS )
  has_keys  = colors.is_a?(Hash)   ## check if passed-in user-defined keys (via hash table)?

  colors = parse_colors( colors )
  pixels = parse_pixels( pixels )

  width  = pixels.reduce(1) {|width,row| row.size > width ? row.size : width }
  height = pixels.size

  img = new( width, height )

  pixels.each_with_index do |row,y|
    row.each_with_index do |color,x|
      pixel = if has_keys     ## if passed-in user-defined keys check only the user-defined keys
                colors[color]
              else
                ## try map ascii art char (.@xo etc.) to color index (0,1,2)
                ##   if no match found - fallback on assuming draw by number (0 1 2 etc.) encoding
                pos = chars.index( color )
                if pos
                  colors[ pos.to_s ]
                else ## assume nil (not found)
                  colors[ color ]
                end
              end

      img[x,y] = pixel
    end # each row
  end # each data

  img
end
parse_colors( colors ) click to toggle source
# File lib/pixelart/image.rb, line 230
def self.parse_colors( colors )
  if colors.is_a?( Array )   ## convenience shortcut
    ## note: always auto-add color 0 as pre-defined transparent - why? why not?
    h = { '0' => Color::TRANSPARENT }
    colors.each_with_index do |color, i|
      h[ (i+1).to_s ] = Color.parse( color )
    end
    h
  else  ## assume hash table with color map
    ## convert into ChunkyPNG::Color
    colors.map do |key,color|
      ## always convert key to string why? why not?  use symbol?
      [ key.to_s, Color.parse( color ) ]
    end.to_h
  end
end
parse_pixels( pixels ) click to toggle source

helpers

# File lib/pixelart/image.rb, line 214
def self.parse_pixels( pixels )
  data = []
  pixels.each_line do |line|
    line = line.strip
    next if line.start_with?( '#' ) || line.empty?   ## note: allow comments and empty lines

    ## note: allow multiple spaces or tabs to separate pixel codes
    ##  e.g.   o o o o o o o o o o o o dg lg w w lg w lg lg dg dg w w  lg dg o o o o o o o o o o o
    ##    or
    data << line.split( /[ \t]+/)
 end
 data
end
read( path ) click to toggle source
# File lib/pixelart/image.rb, line 5
def self.read( path )   ## convenience helper
  img_inner = ChunkyPNG::Image.from_file( path )
  img = new( img_inner.width, img_inner.height, img_inner )
  img
end

Public Instance Methods

[]( x, y ) click to toggle source
# File lib/pixelart/image.rb, line 197
def []( x, y )          @img[x,y]; end
[]=( x, y, value ) click to toggle source
# File lib/pixelart/image.rb, line 198
def []=( x, y, value )  @img[x,y]=value; end
_change_colors!( img, color_map ) click to toggle source
# File lib/pixelart/image.rb, line 161
def _change_colors!( img, color_map )
  img.width.times do |x|
    img.height.times do |y|
      color = img[x,y]
      new_color = color_map[color]
      img[x,y] = new_color  if new_color
    end
  end
end
_parse_color_map( color_map ) click to toggle source
# File lib/pixelart/image.rb, line 155
def _parse_color_map( color_map )
  color_map.map do |k,v|
    [Color.parse(k),  Color.parse(v)]
  end.to_h
end
_parse_colors( colors ) click to toggle source

private helpers

# File lib/pixelart/image.rb, line 151
def _parse_colors( colors )
  colors.map {|color| Color.parse( color ) }
end
change_colors( color_map ) click to toggle source

add replace_colors alias too? - why? why not?

# File lib/pixelart/image.rb, line 106
def change_colors( color_map )
  color_map = _parse_color_map( color_map )

  img = @img.dup  ## note: make a deep copy!!!
  _change_colors!( img, color_map )

  ## wrap into Pixelart::Image - lets you use zoom() and such
  Image.new( img.width, img.height, img )
end
Also aliased as: recolor
change_palette256( palette )
Alias for: change_palette8bit
change_palette8bit( palette ) click to toggle source
# File lib/pixelart/image.rb, line 128
def change_palette8bit( palette )
  ## step 0: mapping from grayscale to new 8bit palette (256 colors)
  color_map = if palette.is_a?( String ) || palette.is_a?( Symbol )
                 PALETTE8BIT[ palette.to_sym ]
                 ## todo/fix: check for missing/undefined palette not found - why? why not?
              else
                 ##  make sure we have colors all in Integer not names, hex, etc.
                 palette = _parse_colors( palette )
                 Palette8bit::GRAYSCALE.zip( palette ).to_h
              end

  ## step 1: convert to grayscale (256 colors)
  img = @img.grayscale
  _change_colors!( img, color_map )

  ## wrap into Pixelart::Image - lets you use zoom() and such
  Image.new( img.width, img.height, img )
end
Also aliased as: change_palette256
compose!( other, x=0, y=0 ) click to toggle source
# File lib/pixelart/image.rb, line 188
def compose!( other, x=0, y=0 )
  @img.compose!( other.image, x, y )    ## note: "unwrap" inner image ref
end
Also aliased as: paste!
flip_vertically()
Alias for: mirror
grayscale() click to toggle source

filter / effects

# File lib/pixelart/image.rb, line 93
def grayscale
  img = @img.grayscale
  Image.new( img.width, img.height, img )
end
height() click to toggle source
# File lib/pixelart/image.rb, line 195
def height()       @img.height; end
image() click to toggle source

return image ref - use a different name - why? why not?

change to to_image  - why? why not?
# File lib/pixelart/image.rb, line 207
def image()        @img; end
led( led=8, spacing: 2, round_corner: false ) click to toggle source
# File lib/pixelart/led.rb, line 5
def led( led=8, spacing: 2, round_corner: false )

  width  = @img.width*led  + (@img.width-1)*spacing
  height = @img.height*led + (@img.height-1)*spacing

  puts "  #{width}x#{height}"

  img = Image.new( width, height, Color::BLACK )

  @img.width.times do |x|
    @img.height.times do |y|
      pixel = @img[x,y]
      pixel = Color::BLACK  if pixel == Color::TRANSPARENT
      led.times do |n|
        led.times do |m|
          ## round a little - drop all four corners for now
          next  if round_corner &&
                  [[0,0],[0,1],[1,0],[1,1],[0,2],[2,0],
                   [0,led-1],[0,led-2],[1,led-1],[1,led-2],[0,led-3],[2,led-1],
                   [led-1,0],[led-1,1],[led-2,0],[led-2,1],[led-1,2],[led-3,0],
                   [led-1,led-1],[led-1,led-2],[led-2,led-1],[led-2,led-2],[led-1,led-3],[led-3,led-1],
                  ].include?( [n,m] )
          img[x*led+n + spacing*x,
              y*led+m + spacing*y] = pixel
        end
      end
    end
  end
  img
end
mirror() click to toggle source
# File lib/pixelart/image.rb, line 98
def mirror
  img = @img.mirror
  Image.new( img.width, img.height, img )
end
Also aliased as: flip_vertically
paste!( other, x=0, y=0 )
Alias for: compose!
pixels() click to toggle source
# File lib/pixelart/image.rb, line 200
def pixels()       @img.pixels; end
recolor( color_map )
Alias for: change_colors
save( path, constraints = {} ) click to toggle source

(image) delegates

todo/check: add some more??
# File lib/pixelart/image.rb, line 177
def save( path, constraints = {} )
  # step 1: make sure outdir exits
  outdir = File.dirname( path )
  FileUtils.mkdir_p( outdir )  unless Dir.exist?( outdir )

  # step 2: save
  @img.save( path, constraints )
end
Also aliased as: write
scale( zoom=2 )
Alias for: zoom
width() click to toggle source
# File lib/pixelart/image.rb, line 194
def width()        @img.width; end
write( path, constraints = {} )
Alias for: save
zoom( zoom=2 ) click to toggle source
# File lib/pixelart/image.rb, line 67
def zoom( zoom=2 )
  ## create a new zoom factor x image (2x, 3x, etc.)

  img = Image.new( @img.width*zoom,
                   @img.height*zoom )

  @img.height.times do |y|
    @img.width.times do |x|
      pixel = @img[x,y]
      zoom.times do |n|
        zoom.times do |m|
          img[n+zoom*x,m+zoom*y] = pixel
        end
      end
    end # each x
  end # each y

  img
end
Also aliased as: scale