Class: Pixelart::Image

Inherits:
Object
  • Object
show all
Defined in:
lib/pixelart/base.rb,
lib/pixelart/misc.rb,
lib/pixelart/image.rb

Direct Known Subclasses

Palette8bit

Defined Under Namespace

Classes: Palette8bit

Constant Summary collapse

Palette256 =
Palette8Bit = Palette8bit
PALETTE8BIT =

predefined palette8bit color maps

   (grayscale to sepia/blue/false/etc.)
- todo/check - keep "shortcut" convenience predefined map - why? why not?
{
  sepia: Palette8bit::GRAYSCALE.zip( Palette8bit::SEPIA ).to_h,
  blue:  Palette8bit::GRAYSCALE.zip( Palette8bit::BLUE ).to_h,
  false: Palette8bit::GRAYSCALE.zip( Palette8bit::FALSE ).to_h,
}

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(width, height, initial = Color::TRANSPARENT) ⇒ Image

Returns a new instance of Image.



33
34
35
36
37
38
39
40
41
# File 'lib/pixelart/image.rb', line 33

def initialize( width, height, initial=Color::TRANSPARENT )

  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

Class Method Details

.parse(pixels, colors:) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/pixelart/image.rb', line 12

def self.parse( pixels, colors: )
  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 = colors[color]
      img[x,y] = pixel
    end # each row
  end # each data

  img
end

.parse_colors(colors) ⇒ Object



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/pixelart/image.rb', line 200

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) ⇒ Object

helpers



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/pixelart/image.rb', line 182

def self.parse_pixels( pixels )
  data = []
  pixels.each_line do |line|
    line = line.strip
    if line.empty?
      puts "!! WARN: skipping empty line in pixel art source"
      next
    end

    ## 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) ⇒ Object

convenience helper



5
6
7
8
9
# 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

Instance Method Details

#[](x, y) ⇒ Object



168
# File 'lib/pixelart/image.rb', line 168

def []( x, y )          @img[x,y]; end

#[]=(x, y, value) ⇒ Object



169
# File 'lib/pixelart/image.rb', line 169

def []=( x, y, value )  @img[x,y]=value; end

#_change_colors!(img, color_map) ⇒ Object



132
133
134
135
136
137
138
139
140
# File 'lib/pixelart/image.rb', line 132

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) ⇒ Object



126
127
128
129
130
# File 'lib/pixelart/image.rb', line 126

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) ⇒ Object

private helpers



122
123
124
# File 'lib/pixelart/image.rb', line 122

def _parse_colors( colors )
  colors.map {|color| Color.parse( color ) }
end

#change_colors(color_map) ⇒ Object Also known as: recolor

add replace_colors alias too? - why? why not?



77
78
79
80
81
82
83
84
85
# File 'lib/pixelart/image.rb', line 77

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

#change_palette8bit(palette) ⇒ Object Also known as: change_palette256



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/pixelart/image.rb', line 99

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

#compose!(other, x = 0, y = 0) ⇒ Object Also known as: paste!



159
160
161
# File 'lib/pixelart/image.rb', line 159

def compose!( other, x=0, y=0 )
  @img.compose!( other.image, x, y )    ## note: "unwrap" inner image ref
end

#grayscaleObject

filter / effects



71
72
73
74
# File 'lib/pixelart/image.rb', line 71

def grayscale
  img = @img.grayscale
  Image.new( img.width, img.height, img )
end

#heightObject



166
# File 'lib/pixelart/image.rb', line 166

def height()       @img.height; end

#imageObject

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

change to to_image  - why? why not?


175
# File 'lib/pixelart/image.rb', line 175

def image()        @img; end

#pixelsObject



171
# File 'lib/pixelart/image.rb', line 171

def pixels()       @img.pixels; end

#save(path, constraints = {}) ⇒ Object Also known as: write

(image) delegates

todo/check: add some more??


148
149
150
151
152
153
154
155
# File 'lib/pixelart/image.rb', line 148

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

#widthObject



165
# File 'lib/pixelart/image.rb', line 165

def width()        @img.width; end

#zoom(zoom = 2) ⇒ Object Also known as: scale



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/pixelart/image.rb', line 45

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