pixelart 1.0.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c77200956faad2093e12cba99a8f8a3339941e612406701a795eecdcdd8248d8
4
- data.tar.gz: d3dfbf4c9d278db95219b695dc55cea1b31debd8b5a08a20305135526d3618d1
3
+ metadata.gz: e407d45e71520e9e7c34448811a80e796254aba70c791e3497df00b0fa700486
4
+ data.tar.gz: 55c324e6cf901bd8ed78fa48d97f27a3f66c60d314d070a457ef4e272ebe9f01
5
5
  SHA512:
6
- metadata.gz: db9abc2122d9727e2fee58c54913174983ff3dd03d2e2658c4b0a84d80114f0f482673d2b7e51fea61dbbb423f1df05eb5e027ba51d741ab86dc7e16d6f2eb72
7
- data.tar.gz: 36c0733c3c5e15f18cca6787185eb2229fa8d7a98c3d037cf781295b8d32fc74be542a433de1504981afb984d9234730191dcf14443b3f614cd3dc9139d4fa7e
6
+ metadata.gz: 6e36d0cb47e5e36b81b3a3f47d62fa119b898f9bbf11c422d53f5ab3cfdfe022a8f3dda3d82ea1db4f2eb422376ea82ac153922add94caeb5aff97214813a81a
7
+ data.tar.gz: b8957560272efd295ef73bdd450531dc9d631fd293215dbd0f9f835bdbdb50669018f1d24a27a6df45d8714a2633bc1d22a6b6761979dc7804b3c5290c2f1eaa
data/Manifest.txt CHANGED
@@ -4,6 +4,7 @@ README.md
4
4
  Rakefile
5
5
  lib/pixelart.rb
6
6
  lib/pixelart/base.rb
7
+ lib/pixelart/blur.rb
7
8
  lib/pixelart/color.rb
8
9
  lib/pixelart/composite.rb
9
10
  lib/pixelart/gradient.rb
@@ -13,4 +14,6 @@ lib/pixelart/misc.rb
13
14
  lib/pixelart/palette.rb
14
15
  lib/pixelart/pixelator.rb
15
16
  lib/pixelart/sketch.rb
17
+ lib/pixelart/spots.rb
18
+ lib/pixelart/vector.rb
16
19
  lib/pixelart/version.rb
data/Rakefile CHANGED
@@ -19,6 +19,8 @@ Hoe.spec 'pixelart' do
19
19
 
20
20
  self.extra_deps = [
21
21
  ['chunky_png'],
22
+ ['mini_magick'],
23
+ ['csvreader'],
22
24
  ]
23
25
 
24
26
  self.licenses = ['Public Domain']
data/lib/pixelart/base.rb CHANGED
@@ -1,12 +1,27 @@
1
- ## 3rd party
1
+ ###############
2
+ # 3rd party
2
3
  require 'chunky_png'
3
4
 
5
+ # optional
6
+ # note: requires installed imagemagick command line installed for usage
7
+ require 'mini_magick'
8
+
9
+
10
+ # bonus / prologue / convenience 3rd party
11
+ require 'csvreader'
12
+
13
+
4
14
  ## stdlib
5
15
  require 'pp'
6
16
  require 'time'
7
17
  require 'date'
8
18
  require 'fileutils'
9
19
 
20
+ require 'json'
21
+ require 'yaml'
22
+
23
+
24
+
10
25
 
11
26
  ## our own code
12
27
  require 'pixelart/version' # note: let version always go first
@@ -16,13 +31,36 @@ require 'pixelart/palette'
16
31
  require 'pixelart/image'
17
32
  require 'pixelart/composite'
18
33
 
34
+
19
35
  require 'pixelart/pixelator'
20
36
 
21
37
  require 'pixelart/misc' ## misc helpers
22
38
 
23
- require 'pixelart/led' ## (special) effects / filters
39
+ #########################
40
+ # (special) effects / filters
41
+ require 'pixelart/led'
24
42
  require 'pixelart/sketch'
25
43
 
44
+ ## (special) effects / filters that require imagemagick
45
+
46
+
47
+ ## todo/check - use a config block or such - why? why not?
48
+ module Pixelart
49
+ MAGICK_SCRIPT = './tmp/magick_script.txt'
50
+ MAGICK_INPUT = './tmp/magick_input.png'
51
+ MAGICK_OUTPUT = './tmp/magick_output.png'
52
+ end
53
+
54
+ require 'pixelart/vector' ## vector graphics helpers
55
+
56
+
57
+ require 'pixelart/spots'
58
+ require 'pixelart/blur'
59
+
60
+
61
+
62
+
63
+
26
64
 
27
65
 
28
66
  ##########
@@ -0,0 +1,19 @@
1
+ module Pixelart
2
+
3
+ class Image
4
+
5
+ def blur( blur=2 )
6
+ @img.save( MAGICK_INPUT )
7
+
8
+ MiniMagick::Tool::Magick.new do |magick|
9
+ magick << MAGICK_INPUT
10
+ magick.blur( "#{blur}x#{blur}" )
11
+ magick << MAGICK_OUTPUT
12
+ end
13
+
14
+ Image.read( MAGICK_OUTPUT )
15
+ end
16
+
17
+ end # class Image
18
+ end # class Pixelart
19
+
@@ -7,6 +7,13 @@ class ImageComposite < Image # check: (re)name to Collage, Sheet, Sprites, or s
7
7
  TILE_HEIGHT = 24
8
8
 
9
9
 
10
+ def self.read( path, width: TILE_WIDTH, height: TILE_WIDTH ) ## convenience helper
11
+ img = ChunkyPNG::Image.from_file( path )
12
+ new( img, width: width,
13
+ height: width )
14
+ end
15
+
16
+
10
17
  def initialize( *args, **kwargs )
11
18
  @tile_width = kwargs[:width] || kwargs[:tile_width] || TILE_WIDTH
12
19
  @tile_height = kwargs[:height] || kwargs[:tile_height] || TILE_HEIGHT
@@ -73,5 +80,21 @@ class ImageComposite < Image # check: (re)name to Collage, Sheet, Sprites, or s
73
80
  super ## e.g [x,y] --- get pixel
74
81
  end
75
82
  end
83
+
84
+
85
+ ## convenience helpers to loop over composite
86
+ def each( &block )
87
+ count.times do |i|
88
+ block.call( tile( i ) )
89
+ end
90
+ end
91
+
92
+ def each_with_index( &block )
93
+ count.times do |i|
94
+ block.call( tile( i ), i )
95
+ end
96
+ end
97
+
98
+
76
99
  end # class ImageComposite
77
100
  end # module Pixelart
@@ -64,18 +64,21 @@ end
64
64
 
65
65
 
66
66
 
67
- def zoom( zoom=2 )
67
+ def zoom( zoom=2, spacing: 0 )
68
68
  ## create a new zoom factor x image (2x, 3x, etc.)
69
69
 
70
- img = Image.new( @img.width*zoom,
71
- @img.height*zoom )
70
+ width = @img.width*zoom+(@img.width-1)*spacing
71
+ height = @img.height*zoom+(@img.height-1)*spacing
72
72
 
73
- @img.height.times do |y|
74
- @img.width.times do |x|
73
+ img = Image.new( width, height )
74
+
75
+ @img.width.times do |x|
76
+ @img.height.times do |y|
75
77
  pixel = @img[x,y]
76
78
  zoom.times do |n|
77
79
  zoom.times do |m|
78
- img[n+zoom*x,m+zoom*y] = pixel
80
+ img[n+zoom*x+spacing*x,
81
+ m+zoom*y+spacing*y] = pixel
79
82
  end
80
83
  end
81
84
  end # each x
@@ -94,12 +97,25 @@ def grayscale
94
97
  img = @img.grayscale
95
98
  Image.new( img.width, img.height, img )
96
99
  end
100
+ alias_method :greyscale, :grayscale
101
+
102
+
103
+
104
+ def flip
105
+ img = @img.flip
106
+ Image.new( img.width, img.height, img )
107
+ end
108
+ alias_method :flip_horizontally, :flip
97
109
 
98
110
  def mirror
99
111
  img = @img.mirror
100
112
  Image.new( img.width, img.height, img )
101
113
  end
102
114
  alias_method :flip_vertically, :mirror
115
+ alias_method :flop, :mirror
116
+
117
+
118
+
103
119
 
104
120
 
105
121
  ## add replace_colors alias too? - why? why not?
@@ -0,0 +1,146 @@
1
+ module Pixelart
2
+
3
+ class Image
4
+
5
+
6
+ def spots_hidef( spot=10,
7
+ spacing: 0,
8
+ center: nil,
9
+ radius: nil,
10
+ background: nil,
11
+ lightness: nil,
12
+ odd: false )
13
+
14
+ width = @img.width*spot+(@img.width-1)*spacing
15
+ height = @img.height*spot+(@img.height-1)*spacing
16
+
17
+ ## puts " #{width}x#{height}"
18
+
19
+ ## settings in a hash for "pretty printing" in comments
20
+ settings = { spot: spot
21
+ }
22
+
23
+ settings[ :spacing ] = spacing if spacing
24
+ settings[ :center ] = center if center
25
+ settings[ :radius ] = radius if radius
26
+ settings[ :background ] = background if background
27
+ settings[ :lightness ] = lightness if lightness
28
+ settings[ :odd ] = odd if odd
29
+
30
+
31
+ v = Vector.new( width, height, header: <<TXT )
32
+ generated by pixelart/v#{VERSION} on #{Time.now.utc}
33
+
34
+ spots_hidef with settings:
35
+ #{settings.to_json}
36
+ TXT
37
+
38
+
39
+ min_center, max_center = center ? center : [0,0]
40
+ min_radius, max_radius = radius ? radius : [0,0]
41
+
42
+ ## note: allow passing in array of colors (will get randomally picked)
43
+ background_colors = if background
44
+ ## check for array; otherwise assume single color passed in
45
+ background_ary = background.is_a?( Array) ? background : [background]
46
+ background_ary.map { |background| Color.parse( background ) }
47
+ else
48
+ [0] # transparent (default - no background)
49
+ end
50
+
51
+
52
+ min_lightness, max_lightness = lightness ? lightness : [0.0,0.0]
53
+
54
+
55
+ @img.width.times do |x|
56
+ @img.height.times do |y|
57
+ color = @img[ x, y ]
58
+
59
+ if color == 0 ## transparent
60
+ next if background.nil?
61
+
62
+ color = if background_colors.size == 1
63
+ background_colors[0]
64
+ else ## pick random background color
65
+ background_colors[ rand( background_colors.size ) ]
66
+ end
67
+ end
68
+
69
+
70
+ if lightness
71
+ ## todo/check: make it work with alpha too
72
+ h,s,l = Color.to_hsl( color, include_alpha: false )
73
+
74
+ h = h % 360 ## make sure h(ue) is always positive!!!
75
+
76
+ ## note: rand() return between 0.0 and 1.0
77
+ l_diff = min_lightness +
78
+ (max_lightness-min_lightness)*rand()
79
+
80
+ lnew = [1.0, l+l_diff].min
81
+ lnew = [0.0, lnew].max
82
+
83
+ ## print " #{l}+#{l_diff}=#{lnew} "
84
+
85
+ color = Color.from_hsl( h,
86
+ [1.0, s].min,
87
+ lnew )
88
+ end
89
+
90
+ ## note: return hexstring with leading #
91
+ # e.g. 0 becomes #00000000
92
+ # and so on
93
+ color_hex = Color.to_hex( color, include_alpha: true )
94
+
95
+ cx_offset,
96
+ cy_offset = if center ## randomize (offset off center +/-)
97
+ [(spot/2 + min_center) + rand( max_center-min_center ),
98
+ (spot/2 + min_center) + rand( max_center-min_center )]
99
+ else
100
+ [spot/2, ## center
101
+ spot/2]
102
+ end
103
+
104
+ cx = x*spot + x*spacing + cx_offset
105
+ cy = y*spot + y*spacing + cx_offset
106
+
107
+ r = if radius ## randomize (radius +/-)
108
+ min_radius + rand( max_radius-min_radius )
109
+ else
110
+ spot/2
111
+ end
112
+
113
+ cx += spot/2 if odd && (y % 2 == 1) ## add odd offset
114
+
115
+
116
+ v.circle( cx: cx, cy: cy, r: r, fill: color_hex)
117
+ end
118
+ end
119
+ v
120
+ end ## method spots_hidef
121
+ alias_method :spots_hd, :spots_hidef
122
+
123
+
124
+ def spots( spot=10,
125
+ spacing: 0,
126
+ center: nil,
127
+ radius: nil,
128
+ background: nil,
129
+ lightness: nil,
130
+ odd: false )
131
+
132
+ v = spots_hidef( spot,
133
+ spacing: spacing,
134
+ center: center,
135
+ radius: radius,
136
+ background: background,
137
+ lightness: lightness,
138
+ odd: odd )
139
+
140
+ v.to_image
141
+ end
142
+
143
+
144
+ end # class Image
145
+ end # class Pixelart
146
+
@@ -0,0 +1,163 @@
1
+ module Pixelart
2
+
3
+
4
+ class Vector # holds a vector graphics image (in source)
5
+
6
+ class Shape; end
7
+ class Circle < Shape
8
+ def initialize( cx, cy, r, fill: )
9
+ @cx = cx
10
+ @cy = cy
11
+ @r = r
12
+ @fill = fill
13
+ end
14
+
15
+ def to_svg
16
+ %Q{<circle cx="#{@cx}" cy="#{@cy}" r="#{@r}" fill="#{@fill}" />}
17
+ end
18
+
19
+ def to_magick
20
+ ## circle
21
+ ## give the center and any point on the perimeter (boundary)
22
+ px = @cx+@r
23
+ py = @cy
24
+ "-fill '#{@fill}' -draw 'circle #{@cx},#{@cy},#{px},#{py}'"
25
+ end
26
+ end # class Circle
27
+
28
+ class Path < Shape
29
+ def initialize( fill:, stroke: )
30
+ @commands = []
31
+ @fill = fill
32
+ @stroke = stroke
33
+ end
34
+
35
+ def move_to( x, y ) ## add a move_to instruction
36
+ @commands << ['M', x, y]
37
+ self
38
+ end
39
+
40
+ def line_to( x, y )
41
+ @commands << ['L', x, y]
42
+ self
43
+ end
44
+
45
+ def line( *coords ) ## move_to/line_to all-in-one shortcut
46
+ ## todo/check - assert coords has at least two x/y pairs - why? why not?
47
+ move_to( *coords[0..1] )
48
+ coords[2..-1].each_slice( 2) do |coord|
49
+ line_to( *coord )
50
+ end
51
+ self
52
+ end
53
+
54
+
55
+
56
+ def to_svg
57
+ buf = %Q{<path style="stroke: #{@stroke}; fill: #{@fill || 'none'};" }
58
+ buf << %Q{d="}
59
+ last_command = ''
60
+ @commands.each_with_index do |command,i|
61
+ buf << " " if i > 0 # add space separator
62
+
63
+ ## "optimize" - that is, do not repead command if same as before
64
+ buf << command[0] if command[0] != last_command
65
+ buf << "#{command[1]} #{command[2]}"
66
+ last_command = command[0]
67
+ end
68
+
69
+ buf << %Q{"}
70
+ buf << "/>"
71
+ buf
72
+ end
73
+ end # class Path
74
+
75
+
76
+
77
+ def initialize( width, height, header: nil )
78
+ @width = width
79
+ @height = height
80
+
81
+ @header = header
82
+ @shapes = []
83
+ end
84
+
85
+ def circle( cx:, cy:, r:, fill: )
86
+ @shapes << Circle.new( cx, cy, r, fill: fill )
87
+ end
88
+
89
+ ## note: default stroke (color) to black (#000000) for now - why? why not?
90
+ def path( stroke: '#000000', fill: nil )
91
+ path = Path.new( stroke: stroke, fill: fill )
92
+ @shapes << path
93
+
94
+ path ## note: MUST return "inner" path shape for chaining "dsl-like" methods / commands
95
+ end
96
+
97
+
98
+
99
+ def to_image
100
+ ## use an empty image (canvas) with transparent background
101
+ ## as magick input image
102
+ canvas = Image.new( @width, @height )
103
+ canvas.save( MAGICK_INPUT )
104
+
105
+ ## note: magick command line might get way too long, thus,
106
+ ## save commands to a script
107
+ ## note: save magick input first (see above) before save script
108
+ ## will (auto-)create missing directories in path (if missing)
109
+
110
+ File.open( MAGICK_SCRIPT, 'w:utf-8' ) do |f|
111
+ f.write( "#{MAGICK_INPUT} \\\n" )
112
+ @shapes.each do |shape|
113
+ f.write( "#{shape.to_magick} \\\n" )
114
+ end
115
+ f.write( "-write #{MAGICK_OUTPUT}\n" )
116
+ end
117
+
118
+ MiniMagick::Tool::Magick.new do |magick|
119
+ magick.script( MAGICK_SCRIPT )
120
+ end
121
+
122
+ Image.read( MAGICK_OUTPUT )
123
+ end
124
+
125
+
126
+ def to_svg
127
+ buf = String.new('')
128
+
129
+ if @header
130
+ buf << "<!--\n"
131
+ ## auto-indent lines by five (5) spaces for now
132
+ @header.each_line do |line|
133
+ buf << " #{line}"
134
+ end
135
+ buf << "\n-->\n\n"
136
+ end
137
+
138
+ buf << %Q{<svg version="1.1" width="#{@width}" height="#{@height}" xmlns="http://www.w3.org/2000/svg">\n}
139
+ @shapes.each do |shape|
140
+ buf << " #{shape.to_svg}\n"
141
+ end
142
+ buf << "</svg>"
143
+ buf
144
+ end
145
+
146
+
147
+ def save( path, format: nil )
148
+ if format && format.downcase == 'png' ## support png with image magick
149
+ img = to_image
150
+ img.save( path )
151
+ else
152
+ # make sure outdir exits
153
+ outdir = File.dirname( path )
154
+ FileUtils.mkdir_p( outdir ) unless Dir.exist?( outdir )
155
+ File.open( path, 'w:utf-8' ) do |f|
156
+ f.write( to_svg )
157
+ end
158
+ end
159
+ end
160
+ alias_method :write, :save
161
+
162
+ end # class Vector
163
+ end # module Pixelart
@@ -2,8 +2,8 @@
2
2
  module Pixelart
3
3
 
4
4
  MAJOR = 1
5
- MINOR = 0
6
- PATCH = 0
5
+ MINOR = 2
6
+ PATCH = 1
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
8
8
 
9
9
  def self.version
data/lib/pixelart.rb CHANGED
@@ -8,3 +8,5 @@ require 'pixelart/base' # aka "strict(er)" version
8
8
  # make Image, Color, Palette8bit, etc top-level
9
9
  include Pixelart
10
10
 
11
+
12
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pixelart
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-21 00:00:00.000000000 Z
11
+ date: 2021-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chunky_png
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mini_magick
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: csvreader
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: rdoc
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -74,6 +102,7 @@ files:
74
102
  - Rakefile
75
103
  - lib/pixelart.rb
76
104
  - lib/pixelart/base.rb
105
+ - lib/pixelart/blur.rb
77
106
  - lib/pixelart/color.rb
78
107
  - lib/pixelart/composite.rb
79
108
  - lib/pixelart/gradient.rb
@@ -83,6 +112,8 @@ files:
83
112
  - lib/pixelart/palette.rb
84
113
  - lib/pixelart/pixelator.rb
85
114
  - lib/pixelart/sketch.rb
115
+ - lib/pixelart/spots.rb
116
+ - lib/pixelart/vector.rb
86
117
  - lib/pixelart/version.rb
87
118
  homepage: https://github.com/rubycoco/pixel
88
119
  licenses: