Class: Pixelart::Image

Inherits:
Object
  • Object
show all
Defined in:
lib/pixelart/silhouette.rb,
lib/pixelart/led.rb,
lib/pixelart/blur.rb,
lib/pixelart/image.rb,
lib/pixelart/spots.rb,
lib/pixelart/circle.rb,
lib/pixelart/sketch.rb,
lib/pixelart/stripes.rb,
lib/pixelart/ukraine.rb,
lib/pixelart/transparent.rb

Overview

todo/check:

use a different name for silhouette
 - why not  - outline ???
        or  - shadow  ???
        or  - profile ???
        or  - figure  ???
        or  - shape   ???
        or  - form    ???

Direct Known Subclasses

ImageColorBar, ImageComposite, ImagePalette8bit

Constant Summary collapse

CHARS =

todo/check: rename to default chars or such? why? why not?

'.@xo^~%*+=:'
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,
}
RAINBOW_RED =

todo/check: move colors to (reusable) constants int Color !!!! why? why not?

Color.parse( '#E40303' )
RAINBOW_ORANGE =
Color.parse( '#FF8C00' )
RAINBOW_YELLOW =
Color.parse( '#FFED00' )
RAINBOW_GREEN =
Color.parse( '#008026' )
RAINBOW_BLUE =
Color.parse( '#004DFF' )
RAINBOW_VIOLET =
Color.parse( '#750787' )
UKRAINE_BLUE =

todo/check: move colors to (reusable) constants int Color !!!! why? why not?

Color.parse( '#0057b7' )
UKRAINE_YELLOW =
Color.parse( '#ffdd00' )

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of Image.



52
53
54
55
56
57
58
59
60
61
62
63
# 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

Class Method Details

.calc_stripes(length, n: 2, debug: false) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/pixelart/stripes.rb', line 5

def self.calc_stripes( length, n: 2, debug: false )
  stripes = []

  base_step = length / n    ## pixels per pixel

  err_step = (length % n) * 2   ## multiply by 2
  denominator =  n * 2   # denominator (in de - nenner  e.g. 1/nenner 4/nenner)

  overflow = err_step*n/denominator  ## todo/check - assert that div is always WITHOUT remainder!!!!!

  if debug
    puts
    puts "base_step (pixels per stripe):"
    puts "  #{base_step}     -  #{base_step}px * #{n} = #{base_step*n}px"
    puts "err_step  (in 1/#{length}*2):"
    puts "  #{err_step} / #{denominator}      - #{err_step*n} / #{denominator} = +#{err_step*n/denominator}px overflow"
    puts
  end

  err    = 0
  stripe = 0

  n.times do |i|
    stripe  = base_step
    err    += err_step

    if err >= denominator ## overflow
      puts "    -- overflow #{err}/#{denominator} - add +1 pixel to stripe #{i}"  if debug

      stripe += 1
      err   -= denominator
    end


    puts "  #{i} => #{stripe}  -- #{err} / #{denominator}"  if debug

    stripes[i] = stripe
  end

  ## note: assert calculation - sum of stripes MUST be equal length
  sum = stripes.sum
  puts "  sum: #{sum}"  if debug

  if sum != length
    puts "!! ERROR - stripes sum #{sum} calculation failed; expected #{length}:"
    pp stripes
    exit 1
  end

  stripes
end

.parse(pixels, colors:, chars: CHARS) ⇒ Object

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

or require user-defined chars to be passed in - why? why not?


17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 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) ⇒ Object



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/pixelart/image.rb', line 263

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



247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/pixelart/image.rb', line 247

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



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

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

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



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

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

#_change_colors!(img, color_map) ⇒ Object



194
195
196
197
198
199
200
201
202
# File 'lib/pixelart/image.rb', line 194

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



188
189
190
191
192
# File 'lib/pixelart/image.rb', line 188

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



184
185
186
# File 'lib/pixelart/image.rb', line 184

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

#blur(blur = 2) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
# File 'lib/pixelart/blur.rb', line 5

def blur( blur=2 )
  @img.save( MAGICK_INPUT )

  MiniMagick::Tool::Magick.new do |magick|
    magick << MAGICK_INPUT
    magick.blur( "#{blur}x#{blur}" )
    magick << MAGICK_OUTPUT
  end

  Image.read( MAGICK_OUTPUT )
end

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

add replace_colors alias too? - why? why not?



139
140
141
142
143
144
145
146
147
# File 'lib/pixelart/image.rb', line 139

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



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/pixelart/image.rb', line 161

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

#circleObject



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/pixelart/circle.rb', line 10

def circle
  ### for radius use min of width / height
  r = [@img.width, @img.height].min / 2

  center_x = width  / 2
  center_y = height / 2

  ################
  #  try with 96x96
  #    center_x:  96 / 2 = 48
  #    center_y:  96 / 2 = 48
  #
  #     r:    96 / 2 = 48

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

  @img.width.times do |x|
    @img.height.times do |y|

      ## change to float calcuation (instead of ints) - why? why not?
      xx, yy, rr = x - center_x,
                   y - center_y,
                   r

      img[ x, y] = if xx*xx+yy*yy < rr*rr
                         @img[ x, y ]
                   else
                         0  ## transparent - alpha(0)
                   end
    end
  end

  img
end

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



221
222
223
# File 'lib/pixelart/image.rb', line 221

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

#crop(x, y, crop_width, crop_height) ⇒ Object



92
93
94
95
# File 'lib/pixelart/image.rb', line 92

def crop( x, y, crop_width, crop_height )
  Image.new( nil, nil,
              image.crop( x,y, crop_width, crop_height ) )
end

#flipObject Also known as: flip_horizontally



110
111
112
113
# File 'lib/pixelart/image.rb', line 110

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

#grayscaleObject Also known as: greyscale

filter / effects



102
103
104
105
# File 'lib/pixelart/image.rb', line 102

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

#heightObject



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

def height()       @img.height; end

#imageObject

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

change to to_image  - why? why not?


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

def image()        @img; end

#led(led = 8, spacing: 2, round_corner: false) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 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

#mirrorObject Also known as: flip_vertically, flop



116
117
118
119
# File 'lib/pixelart/image.rb', line 116

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

#pixelsObject



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

def pixels()       @img.pixels; end

#rainbowObject Also known as: pride



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/pixelart/stripes.rb', line 97

def rainbow
  ##
  # the most common variant consists of six stripes:
  #   red, orange, yellow, green, blue, and violet.
  # The flag is typically flown horizontally,
  #  with the red stripe on top, as it would be in a natural rainbow
  #
  #  see https://en.wikipedia.org/wiki/Rainbow_flag_(LGBT)
  stripes( RAINBOW_RED,
           RAINBOW_ORANGE,
           RAINBOW_YELLOW,
           RAINBOW_GREEN,
           RAINBOW_BLUE,
           RAINBOW_VIOLET )
end

#rotate_clockwiseObject Also known as: rotate_right

90 degrees



130
131
132
133
# File 'lib/pixelart/image.rb', line 130

def rotate_clockwise      # 90 degrees
  img = @img.rotate_clockwise
  Image.new( img.width, img.height, img )
end

#rotate_counter_clockwiseObject Also known as: rotate_left

90 degrees



124
125
126
127
# File 'lib/pixelart/image.rb', line 124

def rotate_counter_clockwise   # 90 degrees
  img = @img.rotate_counter_clockwise
  Image.new( img.width, img.height, img )
end

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

(image) delegates

todo/check: add some more??


210
211
212
213
214
215
216
217
# File 'lib/pixelart/image.rb', line 210

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

#silhouette(color = '#000000') ⇒ Object



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

def silhouette( color='#000000' )
    color = Color.parse( color )

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

    @img.width.times do |x|
      @img.height.times do |y|
        pixel = @img[x,y]

        img[x,y] = if pixel == Color::TRANSPARENT  # transparent (0)
                       Color::TRANSPARENT
                   else
                       color
                   end
    end
  end
  img
end

#sketch(sketch = 4, line: 1) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/pixelart/sketch.rb', line 6

def sketch( sketch=4, line: 1 )
     # todo: line - find a better name eg. line_strenght/width or such?
    width  = @img.width*sketch  + (@img.width+1)*line
    height = @img.height*sketch + (@img.height+1)*line

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

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

    @img.width.times do |x|
      @img.height.times do |y|
        pixel = @img[x,y]

        ## get surrounding pixels - if "out-of-bound" use transparent (0)
        left   =  x == 0  ? Color::TRANSPARENT : @img[x-1,y]
        top    =  y == 0  ? Color::TRANSPARENT : @img[x  ,y-1]

        if pixel != left   ## draw vertical line
            (sketch+line*2).times do |n|
              line.times do |m|
                img[    x*sketch + line*x + m,
                    n + y*sketch + line*y] = Color::BLACK
              end
            end
        end

        if pixel != top   ## draw horizontal line
           (sketch+line*2).times do |n|
             line.times do |m|
               img[n + x*sketch + line*x,
                       y*sketch + line*y + m] = Color::BLACK
              end
           end
        end


        ## check special edge case for x and y to add "finish-up" right and bottom line
        if x == @img.width-1 && pixel != Color::TRANSPARENT
           ## draw vertical line
           (sketch+line*2).times do |n|
            line.times do |m|
              img[    (x+1)*sketch + line*(x+1) + m,
                  n + y*sketch + line*y] = Color::BLACK
            end
           end
        end

        if y== @img.height-1 && pixel != Color::TRANSPARENT
          ## draw horizontal line
          (sketch+line*2).times do |n|
            line.times do |m|
              img[n + x*sketch + line*x,
                      (y+1)*sketch + line*(y+1) + m] = Color::BLACK
            end
          end
        end
      end
    end

    img
end

#spots(spot = 10, spacing: 0, center: nil, radius: nil, background: nil, lightness: nil, odd: false) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/pixelart/spots.rb', line 124

def spots( spot=10,
              spacing: 0,
              center: nil,
              radius: nil,
              background: nil,
              lightness: nil,
              odd: false )

  v = spots_hidef( spot,
        spacing: spacing,
        center: center,
        radius: radius,
        background: background,
        lightness: lightness,
        odd: odd )

  v.to_image
end

#spots_hidef(spot = 10, spacing: 0, center: nil, radius: nil, background: nil, lightness: nil, odd: false) ⇒ Object Also known as: spots_hd



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/pixelart/spots.rb', line 6

def spots_hidef( spot=10,
                   spacing: 0,
                   center: nil,
                   radius: nil,
                   background: nil,
                   lightness: nil,
                   odd: false )

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

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

  ## settings in a hash for "pretty printing" in comments
  settings = { spot: spot
             }

  settings[ :spacing ] = spacing  if spacing
  settings[ :center ]  = center  if center
  settings[ :radius ] = radius  if radius
  settings[ :background ] = background  if background
  settings[ :lightness ] = lightness  if lightness
  settings[ :odd ] = odd   if odd


  v = Vector.new( width, height, header: <<TXT )
generated by pixelart/v#{VERSION} on #{Time.now.utc}

spots_hidef with settings:
    #{settings.to_json}
TXT


  min_center, max_center = center ? center : [0,0]
  min_radius, max_radius = radius ? radius : [0,0]

  ## note: allow passing in array of colors (will get randomally picked)
  background_colors = if background
                        ## check for array; otherwise assume single color passed in
                        background_ary = background.is_a?( Array) ? background : [background]
                        background_ary.map { |background| Color.parse( background ) }
                      else
                        [0]   # transparent (default - no background)
                      end


  min_lightness, max_lightness = lightness ? lightness : [0.0,0.0]


   @img.width.times do |x|
      @img.height.times do |y|
         color = @img[ x, y ]

         if color == 0   ## transparent
           next if background.nil?

           color = if background_colors.size == 1
                     background_colors[0]
                   else  ## pick random background color
                     background_colors[ rand( background_colors.size ) ]
                   end
         end


         if lightness
          ## todo/check: make it work with alpha too
          h,s,l = Color.to_hsl( color, include_alpha: false )

           h = h % 360    ## make sure h(ue) is always positive!!!

           ## note: rand() return between 0.0 and 1.0
           l_diff = min_lightness +
                     (max_lightness-min_lightness)*rand()

           lnew = [1.0, l+l_diff].min
           lnew = [0.0, lnew].max

           ## print " #{l}+#{l_diff}=#{lnew} "

           color = Color.from_hsl( h,
                                   [1.0, s].min,
                                   lnew )
         end

         ## note: return hexstring with leading #
         # e.g.    0 becomes #00000000
         #        and so on
         color_hex = Color.to_hex( color, include_alpha: true )

         cx_offset,
         cy_offset = if center  ## randomize (offset off center +/-)
                       [(spot/2 + min_center) + rand( max_center-min_center ),
                        (spot/2 + min_center) + rand( max_center-min_center )]
                     else
                       [spot/2,   ## center
                        spot/2]
                     end

         cx = x*spot + x*spacing + cx_offset
         cy = y*spot + y*spacing + cx_offset

         r = if radius ## randomize (radius +/-)
                       min_radius + rand( max_radius-min_radius )
                     else
                       spot/2
                     end

         cx += spot/2   if odd && (y % 2 == 1)  ## add odd offset


         v.circle( cx: cx, cy: cy, r: r, fill: color_hex)
      end
    end
  v
end

#stripes_horizontal(*colors) ⇒ Object Also known as: stripes



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/pixelart/stripes.rb', line 58

def stripes_horizontal( *colors )
  colors = colors.map { |color| Color.parse( color ) }

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

  n = colors.size
  lengths = self.class.calc_stripes( @img.height, n: n )

  i      = 0
  length = lengths[0]
  color  = colors[0]

  @img.height.times do |y|
    if y >= length
      i      += 1
      length += lengths[i]
      color   = colors[i]
    end
    @img.width.times do |x|
      img[x,y] = color
    end
  end

  img.compose!( self )  ## paste/compose image onto backgorund
  img
end

#transparent(style = :solid, fuzzy: false) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/pixelart/transparent.rb', line 5

def transparent( style = :solid, fuzzy: false )
  img = Image.new( width, height )


  background = self[0,0]

  bh,bs,bl =  Color.to_hsl( background )
  bh = (bh % 360)  ## might might negative degree (always make positive)

  height.times do |y|
      if style == :linear
        background = self[0,y]

        bh,bs,bl =  Color.to_hsl( background )
        bh = (bh % 360)  ## might might negative degree (always make positive)
      end
    width.times do |x|
      pixel = self[x,y]

      if background == 0  ## special case if background is already transparent keep going
        img[x,y] =  pixel
      elsif fuzzy
        ## check for more transparents
          ##   not s  is 0.0 to 0.99  (100%)
          ##   and l  is 0.0 to 0.99  (100%)
        h,s,l =  Color.to_hsl( pixel )
        h = (h % 360)  ## might might negative degree (always make positive)

        ## try some kind-of fuzzy "heuristic" match on background color
        if ((h >= bh-5) && (h <= bh+5)) &&
           ((s >= bs-0.07) && (s <= bs+0.07)) &&
           ((l >= bl-0.07) && (l <= bl+0.07))
         img[x,y] = 0  ## Color::TRANSPARENT

         if h != bh || s != bs || l != bl
            # report fuzzy background color
            puts "  #{x}/#{y} fuzzy background #{[h,s,l]} ~= #{[bh,bs,bl]}"
         end
        else
          img[x,y] =  pixel
        end
      else
         if pixel == background
          img[x,y] = 0   ## Color::TRANSPARENT
         else
           img[x,y] =  pixel
         end
      end
    end
  end
  img
end

#ukraineObject



16
# File 'lib/pixelart/ukraine.rb', line 16

def ukraine() stripes( UKRAINE_BLUE, UKRAINE_YELLOW ); end

#widthObject



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

def width()        @img.width; end

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



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/pixelart/image.rb', line 67

def zoom( zoom=2, spacing: 0 )
  ## create a new zoom factor x image (2x, 3x, etc.)

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

  img = Image.new( width, height )

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

  img
end