pixelart 0.1.1 → 0.1.2
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 +1 -0
- data/README.md +6 -6
- data/lib/pixelart.rb +1 -157
- data/lib/pixelart/image.rb +225 -0
- data/lib/pixelart/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71ac76ef187b8148a547f8dd7b76f2216bdd034f2ba67f7974b6e1a98abe3992
|
4
|
+
data.tar.gz: 6b44348938795087874a80fec20a6e1f87633ff282f62804605b42cbb232e46d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d94abebcda0b1d87bdc0bcdc0f6072d7ae88efed8b26a0733f4fcdccb9b8063068b9a55f24bf08ebe34705e30298ebaf5d1f619045e37fe5be3b2412a8019932
|
7
|
+
data.tar.gz: 63be655c80cf3703499aae7984a901888ca2ae3fe362ea66f5324e7abd98a0b207f5a150da9f2f1c6b7b2c29d52b71e44d252b6323eb8050160bac8c52e542f0
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
@@ -71,8 +71,8 @@ img3x.save( './i/mooncat_white-3x.png' )
|
|
71
71
|
|
72
72
|
Voila!
|
73
73
|
|
74
|
-

|
75
|
+

|
76
76
|
|
77
77
|
|
78
78
|
|
@@ -100,8 +100,8 @@ img3x.save( './i/mooncat_black-3x.png' )
|
|
100
100
|
|
101
101
|
Voila! Black is the new White!
|
102
102
|
|
103
|
-

|
104
|
+

|
105
105
|
|
106
106
|
|
107
107
|
|
@@ -227,8 +227,8 @@ img5x.save( './i/vader5x.png' )
|
|
227
227
|
|
228
228
|
Voila!
|
229
229
|
|
230
|
-

|
231
|
+

|
232
232
|
|
233
233
|
|
234
234
|
|
data/lib/pixelart.rb
CHANGED
@@ -10,166 +10,10 @@ require 'fileutils'
|
|
10
10
|
|
11
11
|
## our own code
|
12
12
|
require 'pixelart/version' # note: let version always go first
|
13
|
+
require 'pixelart/image'
|
13
14
|
|
14
15
|
|
15
16
|
|
16
|
-
module Pixelart
|
17
|
-
class Image
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def self.read( path ) ## convenience helper
|
22
|
-
img_inner = ChunkyPNG::Image.from_file( path )
|
23
|
-
img = new( img_inner.width, img_inner.height, img_inner )
|
24
|
-
img
|
25
|
-
end
|
26
|
-
|
27
|
-
|
28
|
-
def self.parse( pixels, colors: )
|
29
|
-
colors = parse_colors( colors )
|
30
|
-
pixels = parse_pixels( pixels )
|
31
|
-
|
32
|
-
width = pixels.reduce(1) {|width,row| row.size > width ? row.size : width }
|
33
|
-
height = pixels.size
|
34
|
-
|
35
|
-
img = new( width, height )
|
36
|
-
|
37
|
-
pixels.each_with_index do |row,y|
|
38
|
-
row.each_with_index do |color,x|
|
39
|
-
pixel = colors[color]
|
40
|
-
img[x,y] = pixel
|
41
|
-
end # each row
|
42
|
-
end # each data
|
43
|
-
|
44
|
-
img
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def initialize( width, height, initial=ChunkyPNG::Color::TRANSPARENT )
|
50
|
-
|
51
|
-
if initial.is_a?( ChunkyPNG::Image )
|
52
|
-
@img = initial
|
53
|
-
else
|
54
|
-
## todo/check - initial - use parse_color here too e.g. allow "#fff" too etc.
|
55
|
-
@img = ChunkyPNG::Image.new( width, height, initial )
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
def zoom( zoom=2 )
|
62
|
-
## create a new zoom factor x image (2x, 3x, etc.)
|
63
|
-
|
64
|
-
img = Image.new( @img.width*zoom,
|
65
|
-
@img.height*zoom )
|
66
|
-
|
67
|
-
@img.height.times do |y|
|
68
|
-
@img.width.times do |x|
|
69
|
-
pixel = @img[x,y]
|
70
|
-
zoom.times do |n|
|
71
|
-
zoom.times do |m|
|
72
|
-
img[n+zoom*x,m+zoom*y] = pixel
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end # each x
|
76
|
-
end # each y
|
77
|
-
|
78
|
-
img
|
79
|
-
end
|
80
|
-
alias_method :scale, :zoom
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
#####
|
86
|
-
# (image) delegates
|
87
|
-
## todo/check: add some more??
|
88
|
-
def save( path, constraints = {} )
|
89
|
-
# step 1: make sure outdir exits
|
90
|
-
outdir = File.dirname( path )
|
91
|
-
FileUtils.mkdir_p( outdir ) unless Dir.exist?( outdir )
|
92
|
-
|
93
|
-
# step 2: save
|
94
|
-
@img.save( path, constraints )
|
95
|
-
end
|
96
|
-
alias_method :write, :save
|
97
|
-
|
98
|
-
|
99
|
-
def compose!( other, x=0, y=0 )
|
100
|
-
@img.compose!( other.image, x, y ) ## note: "unwrap" inner image ref
|
101
|
-
end
|
102
|
-
alias_method :paste!, :compose!
|
103
|
-
|
104
|
-
|
105
|
-
def width() @img.width; end
|
106
|
-
def height() @img.height; end
|
107
|
-
|
108
|
-
def []( x, y ) @img[x,y]; end
|
109
|
-
def []=( x, y, value ) @img[x,y]=value; end
|
110
|
-
|
111
|
-
## return image ref - use a different name - why? why not?
|
112
|
-
def image() @img; end
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
######
|
118
|
-
# helpers
|
119
|
-
def self.parse_pixels( pixels )
|
120
|
-
data = []
|
121
|
-
pixels.each_line do |line|
|
122
|
-
line = line.strip
|
123
|
-
if line.empty?
|
124
|
-
puts "!! WARN: skipping empty line in pixel art source"
|
125
|
-
next
|
126
|
-
end
|
127
|
-
|
128
|
-
## note: allow multiple spaces or tabs to separate pixel codes
|
129
|
-
## 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
|
130
|
-
## or
|
131
|
-
data << line.split( /[ \t]+/)
|
132
|
-
end
|
133
|
-
data
|
134
|
-
end
|
135
|
-
|
136
|
-
|
137
|
-
def self.parse_colors( colors )
|
138
|
-
if colors.is_a?( Array ) ## convenience shortcut
|
139
|
-
## note: always auto-add color 0 as pre-defined transparent - why? why not?
|
140
|
-
h = { '0' => ChunkyPNG::Color::TRANSPARENT }
|
141
|
-
colors.each_with_index do |color, i|
|
142
|
-
h[ (i+1).to_s ] = parse_color( color )
|
143
|
-
end
|
144
|
-
h
|
145
|
-
else ## assume hash table with color map
|
146
|
-
## convert into ChunkyPNG::Color
|
147
|
-
colors.map do |key,color|
|
148
|
-
## always convert key to string why? why not? use symbol?
|
149
|
-
[ key.to_s, parse_color( color ) ]
|
150
|
-
end.to_h
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def self.parse_color( color )
|
155
|
-
if color.is_a?( Integer ) ## e.g. Assumess ChunkyPNG::Color.rgb() or such
|
156
|
-
color ## pass through as is 1:1
|
157
|
-
elsif color.is_a?(String)
|
158
|
-
if color.downcase == 'transparent' ## special case for builtin colors
|
159
|
-
ChunkyPNG::Color::TRANSPARENT
|
160
|
-
else
|
161
|
-
## note: return an Integer !!! (not a Color class or such!!! )
|
162
|
-
ChunkyPNG::Color.from_hex( color )
|
163
|
-
end
|
164
|
-
else
|
165
|
-
raise ArgumentError, "unknown color format; cannot parse - expected rgb hex string e.g. d3d3d3"
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
|
170
|
-
end # class Image
|
171
|
-
end # module Pixelart
|
172
|
-
|
173
17
|
|
174
18
|
|
175
19
|
### add some convenience shortcuts
|
@@ -0,0 +1,225 @@
|
|
1
|
+
module Pixelart
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
class Color
|
7
|
+
def self.parse( color )
|
8
|
+
if color.is_a?( Integer ) ## e.g. assumes ChunkyPNG::Color.rgb() or such
|
9
|
+
color ## pass through as is 1:1
|
10
|
+
elsif color.is_a?( Array ) ## assume array of hsl(a) e. g. [180, 0.86, 0.88]
|
11
|
+
ChunkyPNG::Color.from_hsl( *color )
|
12
|
+
elsif color.is_a?( String )
|
13
|
+
if color.downcase == 'transparent' ## special case for builtin colors
|
14
|
+
ChunkyPNG::Color::TRANSPARENT
|
15
|
+
else
|
16
|
+
## note: return an Integer !!! (not a Color class or such!!! )
|
17
|
+
ChunkyPNG::Color.from_hex( color )
|
18
|
+
end
|
19
|
+
else
|
20
|
+
raise ArgumentError, "unknown color format; cannot parse - expected rgb hex string e.g. d3d3d3"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.from_hex( hex )
|
25
|
+
## Creates a color by converting it from a string in hex notation.
|
26
|
+
##
|
27
|
+
## It supports colors with (#rrggbbaa) or without (#rrggbb)
|
28
|
+
## alpha channel as well as the 3-digit short format (#rgb)
|
29
|
+
## for those without. Color strings may include
|
30
|
+
## the prefix "0x" or "#"".
|
31
|
+
ChunkyPNG::Color.from_hex( hex )
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.from_hsl( hue, saturation, lightness, alpha=255)
|
35
|
+
ChunkyPNG::Color.from_hsl( hue,
|
36
|
+
saturation,
|
37
|
+
lightness,
|
38
|
+
alpha )
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def self.to_hex( color, include_alpha: true )
|
43
|
+
ChunkyPNG::Color.to_hex( color, include_alpha )
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.to_hsl( color, include_alpha: true )
|
47
|
+
# Returns an array with the separate HSL components of a color.
|
48
|
+
ChunkyPNG::Color.to_hsl( color, include_alpha )
|
49
|
+
end
|
50
|
+
end # class Color
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
class Image
|
56
|
+
|
57
|
+
def self.read( path ) ## convenience helper
|
58
|
+
img_inner = ChunkyPNG::Image.from_file( path )
|
59
|
+
img = new( img_inner.width, img_inner.height, img_inner )
|
60
|
+
img
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def self.parse( pixels, colors: )
|
65
|
+
colors = parse_colors( colors )
|
66
|
+
pixels = parse_pixels( pixels )
|
67
|
+
|
68
|
+
width = pixels.reduce(1) {|width,row| row.size > width ? row.size : width }
|
69
|
+
height = pixels.size
|
70
|
+
|
71
|
+
img = new( width, height )
|
72
|
+
|
73
|
+
pixels.each_with_index do |row,y|
|
74
|
+
row.each_with_index do |color,x|
|
75
|
+
pixel = colors[color]
|
76
|
+
img[x,y] = pixel
|
77
|
+
end # each row
|
78
|
+
end # each data
|
79
|
+
|
80
|
+
img
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
def initialize( width, height, initial=ChunkyPNG::Color::TRANSPARENT )
|
86
|
+
|
87
|
+
if initial.is_a?( ChunkyPNG::Image )
|
88
|
+
@img = initial
|
89
|
+
else
|
90
|
+
## todo/check - initial - use parse_color here too e.g. allow "#fff" too etc.
|
91
|
+
@img = ChunkyPNG::Image.new( width, height, initial )
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
def zoom( zoom=2 )
|
98
|
+
## create a new zoom factor x image (2x, 3x, etc.)
|
99
|
+
|
100
|
+
img = Image.new( @img.width*zoom,
|
101
|
+
@img.height*zoom )
|
102
|
+
|
103
|
+
@img.height.times do |y|
|
104
|
+
@img.width.times do |x|
|
105
|
+
pixel = @img[x,y]
|
106
|
+
zoom.times do |n|
|
107
|
+
zoom.times do |m|
|
108
|
+
img[n+zoom*x,m+zoom*y] = pixel
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end # each x
|
112
|
+
end # each y
|
113
|
+
|
114
|
+
img
|
115
|
+
end
|
116
|
+
alias_method :scale, :zoom
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
def parse_color_map( color_map )
|
122
|
+
color_map.map do |k,v|
|
123
|
+
[Color.parse(k), Color.parse(v)]
|
124
|
+
end.to_h
|
125
|
+
end
|
126
|
+
|
127
|
+
## add replace_colors alias too? - why? why not?
|
128
|
+
def change_colors( color_map )
|
129
|
+
img = @img.dup ## note: make a deep copy!!!
|
130
|
+
color_map = parse_color_map( color_map )
|
131
|
+
## pp color_map
|
132
|
+
|
133
|
+
img.width.times do |x|
|
134
|
+
img.height.times do |y|
|
135
|
+
color = img[x,y]
|
136
|
+
new_color = color_map[color]
|
137
|
+
img[x,y] = new_color if new_color
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
## wrap into Pixelart::Image - lets you use zoom() and such
|
142
|
+
Image.new( img.width, img.height, img )
|
143
|
+
end
|
144
|
+
alias_method :recolor, :change_colors
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
|
150
|
+
#####
|
151
|
+
# (image) delegates
|
152
|
+
## todo/check: add some more??
|
153
|
+
def save( path, constraints = {} )
|
154
|
+
# step 1: make sure outdir exits
|
155
|
+
outdir = File.dirname( path )
|
156
|
+
FileUtils.mkdir_p( outdir ) unless Dir.exist?( outdir )
|
157
|
+
|
158
|
+
# step 2: save
|
159
|
+
@img.save( path, constraints )
|
160
|
+
end
|
161
|
+
alias_method :write, :save
|
162
|
+
|
163
|
+
|
164
|
+
def compose!( other, x=0, y=0 )
|
165
|
+
@img.compose!( other.image, x, y ) ## note: "unwrap" inner image ref
|
166
|
+
end
|
167
|
+
alias_method :paste!, :compose!
|
168
|
+
|
169
|
+
|
170
|
+
def width() @img.width; end
|
171
|
+
def height() @img.height; end
|
172
|
+
|
173
|
+
def []( x, y ) @img[x,y]; end
|
174
|
+
def []=( x, y, value ) @img[x,y]=value; end
|
175
|
+
|
176
|
+
def pixels() @img.pixels; end
|
177
|
+
|
178
|
+
## return image ref - use a different name - why? why not?
|
179
|
+
def image() @img; end
|
180
|
+
|
181
|
+
|
182
|
+
|
183
|
+
|
184
|
+
######
|
185
|
+
# helpers
|
186
|
+
def self.parse_pixels( pixels )
|
187
|
+
data = []
|
188
|
+
pixels.each_line do |line|
|
189
|
+
line = line.strip
|
190
|
+
if line.empty?
|
191
|
+
puts "!! WARN: skipping empty line in pixel art source"
|
192
|
+
next
|
193
|
+
end
|
194
|
+
|
195
|
+
## note: allow multiple spaces or tabs to separate pixel codes
|
196
|
+
## 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
|
197
|
+
## or
|
198
|
+
data << line.split( /[ \t]+/)
|
199
|
+
end
|
200
|
+
data
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
def self.parse_colors( colors )
|
205
|
+
if colors.is_a?( Array ) ## convenience shortcut
|
206
|
+
## note: always auto-add color 0 as pre-defined transparent - why? why not?
|
207
|
+
h = { '0' => ChunkyPNG::Color::TRANSPARENT }
|
208
|
+
colors.each_with_index do |color, i|
|
209
|
+
h[ (i+1).to_s ] = Color.parse( color )
|
210
|
+
end
|
211
|
+
h
|
212
|
+
else ## assume hash table with color map
|
213
|
+
## convert into ChunkyPNG::Color
|
214
|
+
colors.map do |key,color|
|
215
|
+
## always convert key to string why? why not? use symbol?
|
216
|
+
[ key.to_s, Color.parse( color ) ]
|
217
|
+
end.to_h
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
|
223
|
+
end # class Image
|
224
|
+
end # module Pixelart
|
225
|
+
|
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: 0.1.
|
4
|
+
version: 0.1.2
|
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-04-
|
11
|
+
date: 2021-04-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chunky_png
|
@@ -73,6 +73,7 @@ files:
|
|
73
73
|
- README.md
|
74
74
|
- Rakefile
|
75
75
|
- lib/pixelart.rb
|
76
|
+
- lib/pixelart/image.rb
|
76
77
|
- lib/pixelart/version.rb
|
77
78
|
homepage: https://github.com/cryptocopycats/mooncats
|
78
79
|
licenses:
|