pixelart 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Manifest.txt +2 -0
- data/lib/pixelart/base.rb +6 -0
- data/lib/pixelart/generator.rb +199 -0
- data/lib/pixelart/sample.rb +120 -0
- data/lib/pixelart/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9cb1460263f319b98c4d333f6caa42118eb94f583f9cc6cdaa5bfa6bc4316b9
|
4
|
+
data.tar.gz: 2deabe3c3bc35c62d249e0ff5178d23f986d1131c96daf53a8d36fde51cd6de8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57213049f46aac9a612adb3d9e236ce03aa336318705cabaed98f1f87557dc5dc08a73a7f50254852ab0c3c988ca0ed3d5b3ba6666925640ab837692c573be7c
|
7
|
+
data.tar.gz: 9ff7ae16a77eca4b2d4deb65b98747aa053ab97ab15b0f8a40d373f5ecb96dcd2c18d2fdfd18beca0c06a5e9330719dd2df1a3be1efac07c8f008dfa0b4db614
|
data/Manifest.txt
CHANGED
@@ -8,12 +8,14 @@ lib/pixelart/blur.rb
|
|
8
8
|
lib/pixelart/circle.rb
|
9
9
|
lib/pixelart/color.rb
|
10
10
|
lib/pixelart/composite.rb
|
11
|
+
lib/pixelart/generator.rb
|
11
12
|
lib/pixelart/gradient.rb
|
12
13
|
lib/pixelart/image.rb
|
13
14
|
lib/pixelart/led.rb
|
14
15
|
lib/pixelart/misc.rb
|
15
16
|
lib/pixelart/palette.rb
|
16
17
|
lib/pixelart/pixelator.rb
|
18
|
+
lib/pixelart/sample.rb
|
17
19
|
lib/pixelart/silhouette.rb
|
18
20
|
lib/pixelart/sketch.rb
|
19
21
|
lib/pixelart/spots.rb
|
data/lib/pixelart/base.rb
CHANGED
@@ -31,12 +31,18 @@ require 'pixelart/palette'
|
|
31
31
|
require 'pixelart/image'
|
32
32
|
require 'pixelart/composite'
|
33
33
|
|
34
|
+
require 'pixelart/sample' ## (down)sample / pixelate
|
35
|
+
|
34
36
|
|
35
37
|
require 'pixelart/pixelator'
|
36
38
|
|
37
39
|
require 'pixelart/misc' ## misc helpers
|
38
40
|
require 'pixelart/stripes'
|
39
41
|
|
42
|
+
|
43
|
+
require 'pixelart/generator' ## generate images from text via spritesheets
|
44
|
+
|
45
|
+
|
40
46
|
#########################
|
41
47
|
# (special) effects / filters / etc
|
42
48
|
require 'pixelart/circle'
|
@@ -0,0 +1,199 @@
|
|
1
|
+
####
|
2
|
+
# "simple" generator (no different sizes, genders, etc.)
|
3
|
+
# uses built-in spritesheet for (archetypes &) attributes
|
4
|
+
|
5
|
+
|
6
|
+
module Pixelart
|
7
|
+
|
8
|
+
class Metadata
|
9
|
+
class Sprite
|
10
|
+
attr_reader :id, :name, :type, :more_names
|
11
|
+
|
12
|
+
def initialize( id:,
|
13
|
+
name:,
|
14
|
+
type:,
|
15
|
+
more_names: [] )
|
16
|
+
@id = id # zero-based index eg. 0,1,2,3, etc.
|
17
|
+
@name = name
|
18
|
+
@type = type
|
19
|
+
@more_names = more_names
|
20
|
+
end
|
21
|
+
end # class Metadata::Sprite
|
22
|
+
end # class Metadata
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
class Generator
|
28
|
+
|
29
|
+
######
|
30
|
+
# static helpers - (turn into "true" static self.class methods - why? why not?)
|
31
|
+
#
|
32
|
+
def self.normalize_key( str )
|
33
|
+
## add & e.g. B&W
|
34
|
+
## add ' e.g. McDonald's Red
|
35
|
+
str.downcase.gsub(/[ ()&°'_-]/, '').strip
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.normalize_name( str )
|
39
|
+
## normalize spaces in more names
|
40
|
+
str.strip.gsub( /[ ]{2,}/, ' ' )
|
41
|
+
end
|
42
|
+
|
43
|
+
def normalize_key( str ) self.class.normalize_key( str ); end
|
44
|
+
def normalize_name( str ) self.class.normalize_name( str ); end
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
def build_attributes_by_name( recs )
|
49
|
+
h = {}
|
50
|
+
recs.each_with_index do |rec|
|
51
|
+
names = [rec.name] + rec.more_names
|
52
|
+
|
53
|
+
names.each do |name|
|
54
|
+
key = normalize_key( name )
|
55
|
+
|
56
|
+
if h[ key ]
|
57
|
+
puts "!!! ERROR - attribute name is not unique:"
|
58
|
+
pp rec
|
59
|
+
puts "duplicate:"
|
60
|
+
pp h[key]
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
h[ key ] = rec
|
64
|
+
end
|
65
|
+
end
|
66
|
+
## pp h
|
67
|
+
h
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def build_recs( recs ) ## build and normalize (meta data) records
|
72
|
+
## sort by id
|
73
|
+
recs = recs.sort do |l,r|
|
74
|
+
l['id'].to_i( 10 ) <=> r['id'].to_i( 10 ) # use base10 (decimal)
|
75
|
+
end
|
76
|
+
|
77
|
+
## assert all recs are in order by id (0 to size)
|
78
|
+
recs.each_with_index do |rec, exp_id|
|
79
|
+
id = rec['id'].to_i(10)
|
80
|
+
if id != exp_id
|
81
|
+
puts "!! ERROR - meta data record ids out-of-order - expected id #{exp_id}; got #{id}"
|
82
|
+
exit 1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
## convert to "wrapped / immutable" kind-of struct
|
87
|
+
recs = recs.map do |rec|
|
88
|
+
id = rec['id'].to_i( 10 )
|
89
|
+
name = normalize_name( rec['name'] )
|
90
|
+
type = rec['type']
|
91
|
+
|
92
|
+
more_names = (rec['more_names'] || '').split( '|' )
|
93
|
+
more_names = more_names.map {|str| normalize_name( str ) }
|
94
|
+
|
95
|
+
Metadata::Sprite.new(
|
96
|
+
id: id,
|
97
|
+
name: name,
|
98
|
+
type: type,
|
99
|
+
more_names: more_names )
|
100
|
+
end
|
101
|
+
recs
|
102
|
+
end # method build_recs
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
def initialize( image_path="./spritesheet.png",
|
107
|
+
meta_path="./spritesheet.csv",
|
108
|
+
width: 24,
|
109
|
+
height: 24 )
|
110
|
+
@width = width
|
111
|
+
@height = height
|
112
|
+
|
113
|
+
@sheet = ImageComposite.read( image_path, width: @width, height: @height )
|
114
|
+
recs = CsvHash.read( meta_path )
|
115
|
+
|
116
|
+
@recs = build_recs( recs )
|
117
|
+
|
118
|
+
## lookup by "case/space-insensitive" name / key
|
119
|
+
@attributes_by_name = build_attributes_by_name( @recs )
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
def spritesheet() @sheet; end
|
124
|
+
alias_method :sheet, :spritesheet
|
125
|
+
|
126
|
+
|
127
|
+
def records() @recs; end
|
128
|
+
alias_method :meta, :records
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
def find_meta( q )
|
134
|
+
key = normalize_key( q ) ## normalize q(uery) string/symbol
|
135
|
+
|
136
|
+
rec = @attributes_by_name[ key ]
|
137
|
+
if rec
|
138
|
+
puts " lookup >#{key}< => #{rec.id}: #{rec.name} / #{rec.type}"
|
139
|
+
else
|
140
|
+
puts "!! WARN - no lookup found for key >#{key}<"
|
141
|
+
end
|
142
|
+
|
143
|
+
rec
|
144
|
+
end
|
145
|
+
|
146
|
+
def find( q )
|
147
|
+
rec = find_meta( q )
|
148
|
+
|
149
|
+
## return image if record found
|
150
|
+
rec ? @sheet[ rec.id ] : nil
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
def to_recs( *values )
|
155
|
+
recs = []
|
156
|
+
|
157
|
+
attribute_names = values
|
158
|
+
|
159
|
+
attribute_names.each do |attribute_name|
|
160
|
+
attribute = find_meta( attribute_name )
|
161
|
+
if attribute.nil?
|
162
|
+
puts "!! ERROR - attribute >#{attribute_name}< not found; sorry"
|
163
|
+
exit 1
|
164
|
+
end
|
165
|
+
recs << attribute
|
166
|
+
end
|
167
|
+
|
168
|
+
recs
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
|
173
|
+
|
174
|
+
def generate_image( *values, background: nil )
|
175
|
+
|
176
|
+
ids = if values[0].is_a?( Integer ) ## assume integer number (indexes)
|
177
|
+
values
|
178
|
+
else ## assume strings (names)
|
179
|
+
to_recs( *values ).map { |rec| rec.id }
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
img = Image.new( @width, @height )
|
184
|
+
|
185
|
+
if background ## for now assume background is (simply) color
|
186
|
+
img.compose!( Image.new( @width, @height, background ) )
|
187
|
+
end
|
188
|
+
|
189
|
+
ids.each do |id|
|
190
|
+
img.compose!( @sheet[ id ] )
|
191
|
+
end
|
192
|
+
|
193
|
+
img
|
194
|
+
end
|
195
|
+
alias_method :generate, :generate_image
|
196
|
+
|
197
|
+
end # class Generator
|
198
|
+
|
199
|
+
end # module Pixelart
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Pixelart
|
2
|
+
|
3
|
+
class Image
|
4
|
+
|
5
|
+
def self.calc_sample_steps( width, new_width,
|
6
|
+
center: true,
|
7
|
+
debug: false )
|
8
|
+
## todo/fix: assert new_width is smaller than width
|
9
|
+
if debug
|
10
|
+
puts
|
11
|
+
puts "==> from: #{width}px to: #{new_width}px"
|
12
|
+
end
|
13
|
+
|
14
|
+
indexes = []
|
15
|
+
|
16
|
+
base_step = width / new_width ## pixels per pixel
|
17
|
+
|
18
|
+
err_step = (width % new_width) * 2 ## multiply by 2
|
19
|
+
denominator = new_width * 2 # denominator (in de - nenner e.g. 1/nenner 4/nenner)
|
20
|
+
|
21
|
+
overflow = err_step*new_width/denominator ## todo/check - assert that div is always WITHOUT remainder!!!!!
|
22
|
+
|
23
|
+
if debug
|
24
|
+
puts
|
25
|
+
puts "base_step (pixels per pixel):"
|
26
|
+
puts " #{base_step} - #{base_step} * #{new_width}px = #{base_step*new_width}px"
|
27
|
+
puts "err_step (in 1/#{width}*2):"
|
28
|
+
puts " #{err_step} / #{denominator} - #{err_step*new_width} / #{denominator} = +#{err_step*new_width/denominator}px overflow"
|
29
|
+
puts
|
30
|
+
end
|
31
|
+
|
32
|
+
# initial pixel offset
|
33
|
+
index = 0
|
34
|
+
err = err_step/2 ## note: start off with +err_step/2 to add overflow pixel in the "middle"
|
35
|
+
|
36
|
+
|
37
|
+
index += if center.is_a?( Integer )
|
38
|
+
center
|
39
|
+
elsif center
|
40
|
+
base_step/2
|
41
|
+
else
|
42
|
+
0 # use 0px offset
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
new_width.times do |i|
|
47
|
+
if err >= denominator ## overflow
|
48
|
+
puts " -- overflow #{err}/#{denominator} - add +1 pixel offset to #{i}" if debug
|
49
|
+
index += 1
|
50
|
+
err -= denominator
|
51
|
+
end
|
52
|
+
|
53
|
+
puts " #{i} => #{index} -- #{err} / #{denominator}" if debug
|
54
|
+
|
55
|
+
|
56
|
+
indexes[i] = index
|
57
|
+
|
58
|
+
index += base_step
|
59
|
+
err += err_step
|
60
|
+
end
|
61
|
+
|
62
|
+
indexes
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
## todo/check: rename to sample to resample or downsample - why? why not?
|
68
|
+
def sample( steps_x, steps_y=steps_x,
|
69
|
+
top_x: 0, top_y: 0 )
|
70
|
+
width = steps_x.size
|
71
|
+
height = steps_y.size
|
72
|
+
puts " downsampling from #{self.width}x#{self.height} to #{width}x#{height}..."
|
73
|
+
|
74
|
+
dest = Image.new( width, height )
|
75
|
+
|
76
|
+
steps_x.each_with_index do |step_x, x|
|
77
|
+
steps_y.each_with_index do |step_y, y|
|
78
|
+
pixel = self[top_x+step_x, top_y+step_y]
|
79
|
+
|
80
|
+
dest[x,y] = pixel
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
dest
|
85
|
+
end
|
86
|
+
alias_method :pixelate, :sample
|
87
|
+
|
88
|
+
|
89
|
+
def sample_debug( steps_x, steps_y=steps_x,
|
90
|
+
color: Color.parse( '#ffff00' ),
|
91
|
+
top_x: 0,
|
92
|
+
top_y: 0) ## add a yellow pixel
|
93
|
+
|
94
|
+
## todo/fix: get a clone of the image (DO NOT modify in place)
|
95
|
+
|
96
|
+
img = self
|
97
|
+
|
98
|
+
steps_x.each_with_index do |step_x, x|
|
99
|
+
steps_y.each_with_index do |step_y, y|
|
100
|
+
base_x = top_x+step_x
|
101
|
+
base_y = top_y+step_y
|
102
|
+
|
103
|
+
img[base_x,base_y] = color
|
104
|
+
|
105
|
+
## add more colors
|
106
|
+
img[base_x+1,base_y] = color
|
107
|
+
img[base_x+2,base_y] = color
|
108
|
+
|
109
|
+
img[base_x,base_y+1] = color
|
110
|
+
img[base_x,base_y+2] = color
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
self
|
115
|
+
end
|
116
|
+
alias_method :pixelate_debug, :sample_debug
|
117
|
+
|
118
|
+
end # class Image
|
119
|
+
end # module Pixelart
|
120
|
+
|
data/lib/pixelart/version.rb
CHANGED
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.3.
|
4
|
+
version: 1.3.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: 2022-05-
|
11
|
+
date: 2022-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chunky_png
|
@@ -106,12 +106,14 @@ files:
|
|
106
106
|
- lib/pixelart/circle.rb
|
107
107
|
- lib/pixelart/color.rb
|
108
108
|
- lib/pixelart/composite.rb
|
109
|
+
- lib/pixelart/generator.rb
|
109
110
|
- lib/pixelart/gradient.rb
|
110
111
|
- lib/pixelart/image.rb
|
111
112
|
- lib/pixelart/led.rb
|
112
113
|
- lib/pixelart/misc.rb
|
113
114
|
- lib/pixelart/palette.rb
|
114
115
|
- lib/pixelart/pixelator.rb
|
116
|
+
- lib/pixelart/sample.rb
|
115
117
|
- lib/pixelart/silhouette.rb
|
116
118
|
- lib/pixelart/sketch.rb
|
117
119
|
- lib/pixelart/spots.rb
|