pixelart 1.3.2 → 1.3.5
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 -3
- data/README.md +6 -0
- data/Rakefile +2 -1
- data/lib/pixelart/base.rb +13 -34
- data/lib/pixelart/convert.rb +54 -0
- data/lib/pixelart/image.rb +55 -5
- data/lib/pixelart/version.rb +1 -1
- data/lib/pixelart.rb +10 -0
- metadata +20 -8
- data/lib/pixelart/color.rb +0 -131
- data/lib/pixelart/gradient.rb +0 -106
- data/lib/pixelart/palette.rb +0 -72
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 43bc0ad17527099593a49e143036c875bf93fc61873d551252a19376490734fd
|
4
|
+
data.tar.gz: c57fdb4d5241915767f4480a698f3f362d25c53f99805050b8c8dd4c2ca9ec27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 742db57f909288c2a19105244951f676c99723544be749e60544d3576f326f323ec00bcdf0ea9fbeef0e6a6a0d55771f5ad06a7e5fe1a32814b6f509bc472be4
|
7
|
+
data.tar.gz: 2b7d05f9e4ab9a4f4348b3fb7b7f1f5b36ee884b4db3bae901479c84c4dac0207f67f56ba140319a45bd1704c9af14075ef7dfc3ef20897476dfc5bf242a509c
|
data/Manifest.txt
CHANGED
@@ -6,14 +6,12 @@ lib/pixelart.rb
|
|
6
6
|
lib/pixelart/base.rb
|
7
7
|
lib/pixelart/blur.rb
|
8
8
|
lib/pixelart/circle.rb
|
9
|
-
lib/pixelart/color.rb
|
10
9
|
lib/pixelart/composite.rb
|
10
|
+
lib/pixelart/convert.rb
|
11
11
|
lib/pixelart/generator.rb
|
12
|
-
lib/pixelart/gradient.rb
|
13
12
|
lib/pixelart/image.rb
|
14
13
|
lib/pixelart/led.rb
|
15
14
|
lib/pixelart/misc.rb
|
16
|
-
lib/pixelart/palette.rb
|
17
15
|
lib/pixelart/pixelator.rb
|
18
16
|
lib/pixelart/sample.rb
|
19
17
|
lib/pixelart/silhouette.rb
|
data/README.md
CHANGED
@@ -288,3 +288,9 @@ Just install the gem:
|
|
288
288
|
|
289
289
|
The scripts are dedicated to the public domain.
|
290
290
|
Use it as you please with no restrictions whatsoever.
|
291
|
+
|
292
|
+
|
293
|
+
|
294
|
+
## Questions? Comments?
|
295
|
+
|
296
|
+
Post them on the [D.I.Y. Punk (Pixel) Art reddit](https://old.reddit.com/r/DIYPunkArt). Thanks.
|
data/Rakefile
CHANGED
data/lib/pixelart/base.rb
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
require 'cocos'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
|
5
|
+
###
|
6
|
+
# base module
|
7
|
+
require 'pixelart/colors'
|
8
|
+
|
9
|
+
|
10
|
+
|
1
11
|
###############
|
2
12
|
# 3rd party
|
3
13
|
require 'chunky_png'
|
@@ -7,27 +17,9 @@ require 'chunky_png'
|
|
7
17
|
require 'mini_magick'
|
8
18
|
|
9
19
|
|
10
|
-
# bonus / prologue / convenience 3rd party
|
11
|
-
require 'csvreader'
|
12
|
-
|
13
|
-
|
14
|
-
## stdlib
|
15
|
-
require 'pp'
|
16
|
-
require 'time'
|
17
|
-
require 'date'
|
18
|
-
require 'fileutils'
|
19
|
-
|
20
|
-
require 'json'
|
21
|
-
require 'yaml'
|
22
|
-
|
23
|
-
|
24
|
-
|
25
20
|
|
26
21
|
## our own code
|
27
22
|
require 'pixelart/version' # note: let version always go first
|
28
|
-
require 'pixelart/color'
|
29
|
-
require 'pixelart/gradient'
|
30
|
-
require 'pixelart/palette'
|
31
23
|
require 'pixelart/image'
|
32
24
|
require 'pixelart/composite'
|
33
25
|
|
@@ -71,22 +63,9 @@ require 'pixelart/blur'
|
|
71
63
|
|
72
64
|
|
73
65
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
##########
|
79
|
-
# add some spelling convenience variants
|
80
|
-
PixelArt = Pixelart
|
81
|
-
|
82
|
-
module Pixelart
|
83
|
-
Palette256 = Palette8Bit = Palette8bit
|
84
|
-
|
85
|
-
Palette256Image = Palette8BitImage = Palette8bitImage =
|
86
|
-
ImagePalette256 = ImagePalette8Bit = ImagePalette8bit
|
87
|
-
|
88
|
-
CompositeImage = ImageComposite
|
89
|
-
end
|
66
|
+
###
|
67
|
+
# mover helpers / utils
|
68
|
+
require 'pixelart/convert'
|
90
69
|
|
91
70
|
|
92
71
|
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Pixelart
|
2
|
+
|
3
|
+
class Image
|
4
|
+
|
5
|
+
|
6
|
+
## helper to convert (all) image in directory
|
7
|
+
## chech: move to ImageUtils.convert or such - why? why not?
|
8
|
+
##
|
9
|
+
## what about the name e.g. rename to convert_dir or
|
10
|
+
## batch_convert such - why? why not?
|
11
|
+
def self.convert( dir, from: 'jpg',
|
12
|
+
to: 'png',
|
13
|
+
outdir: nil,
|
14
|
+
overwrite: true )
|
15
|
+
|
16
|
+
outdir = dir if outdir.nil?
|
17
|
+
|
18
|
+
files = Dir.glob( "#{dir}/*.#{from}" )
|
19
|
+
puts "==> found #{files.size} image(s) to convert from #{from} to #{to} (overwrite mode set to: #{overwrite})"
|
20
|
+
|
21
|
+
files.each_with_index do |file,i|
|
22
|
+
dirname = File.dirname( file )
|
23
|
+
extname = File.extname( file )
|
24
|
+
basename = File.basename( file, extname )
|
25
|
+
|
26
|
+
## skip convert if target / dest file already exists
|
27
|
+
next if overwrite == false && File.exist?( "#{outdir}/#{basename}.#{to}" )
|
28
|
+
|
29
|
+
## note: make sure outdir exists (magic will not create it??)
|
30
|
+
FileUtils.mkdir_p( outdir ) unless Dir.exist?( outdir )
|
31
|
+
|
32
|
+
cmd = "magick convert #{dirname}/#{basename}.#{from} #{outdir}/#{basename}.#{to}"
|
33
|
+
|
34
|
+
puts " [#{i+1}/#{files.size}] - #{cmd}"
|
35
|
+
## todo/fix: check return value!!! magick comand not available (in path) and so on!!!
|
36
|
+
system( cmd )
|
37
|
+
|
38
|
+
if from == 'gif'
|
39
|
+
## assume multi-images for gif
|
40
|
+
## save image-0.png to image.png
|
41
|
+
path0 = "#{outdir}/#{basename}-0.#{to}"
|
42
|
+
path = "#{outdir}/#{basename}.#{to}"
|
43
|
+
|
44
|
+
puts " saving #{path0} to #{path}..."
|
45
|
+
|
46
|
+
blob = File.open( path0, 'rb' ) { |f| f.read }
|
47
|
+
File.open( path, 'wb' ) { |f| f.write( blob ) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end # class Image
|
54
|
+
end # class Pixelart
|
data/lib/pixelart/image.rb
CHANGED
@@ -2,12 +2,42 @@ module Pixelart
|
|
2
2
|
|
3
3
|
class Image
|
4
4
|
|
5
|
+
|
6
|
+
## keep track of all (inherited) subclasses via inherited hook
|
7
|
+
##
|
8
|
+
## change/rename to descendants - why? why not?
|
9
|
+
##
|
10
|
+
## note about rails (activesupport?)
|
11
|
+
## If you use rails >= 3, you have two options in place.
|
12
|
+
## Use .descendants if you want more than one level depth of children classes,
|
13
|
+
## or use .subclasses for the first level of child classes.
|
14
|
+
|
15
|
+
def self.subclasses
|
16
|
+
@subclasses ||= []
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.inherited( subclass )
|
20
|
+
subclasses << subclass
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
|
5
27
|
def self.read( path ) ## convenience helper
|
6
28
|
img_inner = ChunkyPNG::Image.from_file( path )
|
7
29
|
img = new( img_inner.width, img_inner.height, img_inner )
|
8
30
|
img
|
9
31
|
end
|
10
32
|
|
33
|
+
def self.parse_base64( str )
|
34
|
+
blob = Base64.decode64( str )
|
35
|
+
img_inner = ChunkyPNG::Image.from_blob( blob )
|
36
|
+
img = new( img_inner.width, img_inner.height, img_inner )
|
37
|
+
img
|
38
|
+
end
|
39
|
+
|
40
|
+
|
11
41
|
|
12
42
|
|
13
43
|
CHARS = '.@xo^~%*+=:' ## todo/check: rename to default chars or such? why? why not?
|
@@ -95,6 +125,16 @@ def crop( x, y, crop_width, crop_height )
|
|
95
125
|
end
|
96
126
|
|
97
127
|
|
128
|
+
## shift image n-pixels to the left (NOT changing width/height)
|
129
|
+
def left( left )
|
130
|
+
img = Image.new( width, height )
|
131
|
+
img.compose!( crop( 0, 0, width-left, height ), left, 0 )
|
132
|
+
img
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
|
98
138
|
|
99
139
|
#######################
|
100
140
|
## filter / effects
|
@@ -106,19 +146,29 @@ end
|
|
106
146
|
alias_method :greyscale, :grayscale
|
107
147
|
|
108
148
|
|
109
|
-
|
110
|
-
|
111
|
-
|
149
|
+
######################
|
150
|
+
# flip horizontally on x-axis (top-to-bottom/bottom-to-top)
|
151
|
+
# e.g. pixels on the top will now be pixels on the bottom
|
152
|
+
def flip_horizontally
|
153
|
+
img = @img.flip_horizontally
|
112
154
|
Image.new( img.width, img.height, img )
|
113
155
|
end
|
114
|
-
|
156
|
+
## keep flop? alias - why? why not?
|
157
|
+
## note: chunky_png use flip alias for flip_horizontally!!!!
|
158
|
+
alias_method :flop, :flip_horizontally
|
159
|
+
|
115
160
|
|
161
|
+
###
|
162
|
+
# flip vertially on y-axis (right-to-left/left-to-right)
|
163
|
+
# e.g. pixels on the left will now be pixels on the right
|
116
164
|
def mirror
|
117
165
|
img = @img.mirror
|
118
166
|
Image.new( img.width, img.height, img )
|
119
167
|
end
|
120
168
|
alias_method :flip_vertically, :mirror
|
121
|
-
alias_method :
|
169
|
+
alias_method :flip, :mirror ## note: chunky_png use flip alias for flip_horizontally (top-to-bottom)!!!!
|
170
|
+
alias_method :phlip, :mirror ## philip the intern ("hand-phlip one-by-one")
|
171
|
+
alias_method :hand_phlip, :mirror
|
122
172
|
|
123
173
|
|
124
174
|
def rotate_counter_clockwise # 90 degrees
|
data/lib/pixelart/version.rb
CHANGED
data/lib/pixelart.rb
CHANGED
@@ -9,4 +9,14 @@ require 'pixelart/base' # aka "strict(er)" version
|
|
9
9
|
include Pixelart
|
10
10
|
|
11
11
|
|
12
|
+
##########
|
13
|
+
# add some spelling convenience variants
|
14
|
+
|
15
|
+
|
16
|
+
module Pixelart
|
17
|
+
Palette256Image = Palette8BitImage = Palette8bitImage =
|
18
|
+
ImagePalette256 = ImagePalette8Bit = ImagePalette8bit
|
19
|
+
|
20
|
+
CompositeImage = ImageComposite
|
21
|
+
end
|
12
22
|
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
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.5
|
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-
|
11
|
+
date: 2022-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: cocos
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: pixelart-colors
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -39,7 +39,21 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: chunky_png
|
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'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mini_magick
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - ">="
|
@@ -104,14 +118,12 @@ files:
|
|
104
118
|
- lib/pixelart/base.rb
|
105
119
|
- lib/pixelart/blur.rb
|
106
120
|
- lib/pixelart/circle.rb
|
107
|
-
- lib/pixelart/color.rb
|
108
121
|
- lib/pixelart/composite.rb
|
122
|
+
- lib/pixelart/convert.rb
|
109
123
|
- lib/pixelart/generator.rb
|
110
|
-
- lib/pixelart/gradient.rb
|
111
124
|
- lib/pixelart/image.rb
|
112
125
|
- lib/pixelart/led.rb
|
113
126
|
- lib/pixelart/misc.rb
|
114
|
-
- lib/pixelart/palette.rb
|
115
127
|
- lib/pixelart/pixelator.rb
|
116
128
|
- lib/pixelart/sample.rb
|
117
129
|
- lib/pixelart/silhouette.rb
|
data/lib/pixelart/color.rb
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
module Pixelart
|
2
|
-
|
3
|
-
|
4
|
-
class Color
|
5
|
-
TRANSPARENT = 0 # rgba( 0, 0, 0, 0)
|
6
|
-
BLACK = 0xff # rgba( 0, 0, 0,255)
|
7
|
-
WHITE = 0xffffffff # rgba(255,255,255,255)
|
8
|
-
|
9
|
-
|
10
|
-
def self.parse( color )
|
11
|
-
if color.is_a?( Integer ) ## e.g. assumes ChunkyPNG::Color.rgb() or such
|
12
|
-
color ## pass through as is 1:1
|
13
|
-
elsif color.is_a?( Array ) ## assume array of hsl(a) e. g. [180, 0.86, 0.88]
|
14
|
-
from_hsl( *color )
|
15
|
-
elsif color.is_a?( String )
|
16
|
-
if color.downcase == 'transparent' ## special case for builtin colors
|
17
|
-
TRANSPARENT
|
18
|
-
else
|
19
|
-
## note: return an Integer !!! (not a Color class or such!!! )
|
20
|
-
from_hex( color )
|
21
|
-
end
|
22
|
-
else
|
23
|
-
raise ArgumentError, "unknown color format; cannot parse - expected rgb hex string e.g. d3d3d3"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.from_hex( hex )
|
28
|
-
## Creates a color by converting it from a string in hex notation.
|
29
|
-
##
|
30
|
-
## It supports colors with (#rrggbbaa) or without (#rrggbb)
|
31
|
-
## alpha channel as well as the 3-digit short format (#rgb)
|
32
|
-
## for those without. Color strings may include
|
33
|
-
## the prefix "0x" or "#"".
|
34
|
-
ChunkyPNG::Color.from_hex( hex )
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.from_hsl( hue, saturation, lightness, alpha=255)
|
38
|
-
ChunkyPNG::Color.from_hsl( hue,
|
39
|
-
saturation,
|
40
|
-
lightness,
|
41
|
-
alpha )
|
42
|
-
end
|
43
|
-
|
44
|
-
|
45
|
-
def self.to_hex( color, include_alpha: true )
|
46
|
-
ChunkyPNG::Color.to_hex( color, include_alpha )
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.to_hsl( color, include_alpha: true )
|
50
|
-
# Returns an array with the separate HSL components of a color.
|
51
|
-
ChunkyPNG::Color.to_hsl( color, include_alpha )
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.r( color ) ChunkyPNG::Color.r( color ); end
|
55
|
-
def self.g( color ) ChunkyPNG::Color.g( color ); end
|
56
|
-
def self.b( color ) ChunkyPNG::Color.b( color ); end
|
57
|
-
|
58
|
-
def self.rgb( r, g, b ) ChunkyPNG::Color.rgb( r, g, b); end
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
## known built-in color names
|
63
|
-
def self.build_names
|
64
|
-
names = {
|
65
|
-
'#00000000' => 'TRANSPARENT',
|
66
|
-
'#000000ff' => 'BLACK',
|
67
|
-
'#ffffffff' => 'WHITE',
|
68
|
-
}
|
69
|
-
|
70
|
-
## auto-add grayscale 1 to 254
|
71
|
-
(1..254).each do |n|
|
72
|
-
hex = "#" + ('%02x' % n)*3
|
73
|
-
hex << "ff" ## add alpha channel (255)
|
74
|
-
names[ hex ] = "8-BIT GRAYSCALE ##{n}"
|
75
|
-
end
|
76
|
-
|
77
|
-
names
|
78
|
-
end
|
79
|
-
|
80
|
-
NAMES = build_names
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
def self.format( color )
|
85
|
-
rgb = [r(color),
|
86
|
-
g(color),
|
87
|
-
b(color)]
|
88
|
-
|
89
|
-
# rgb in hex (string format)
|
90
|
-
# note: do NOT include alpha channel for now - why? why not?
|
91
|
-
hex = "#" + rgb.map{|num| '%02x' % num }.join
|
92
|
-
|
93
|
-
hsl = to_hsl( color )
|
94
|
-
## get alpha channel (transparency) for hsla
|
95
|
-
alpha = hsl[3]
|
96
|
-
|
97
|
-
|
98
|
-
buf = ''
|
99
|
-
buf << hex
|
100
|
-
buf << " / "
|
101
|
-
buf << "rgb("
|
102
|
-
buf << "%3d " % rgb[0]
|
103
|
-
buf << "%3d " % rgb[1]
|
104
|
-
buf << "%3d)" % rgb[2]
|
105
|
-
buf << " - "
|
106
|
-
buf << "hsl("
|
107
|
-
buf << "%3d° " % (hsl[0] % 360)
|
108
|
-
buf << "%3d%% " % (hsl[1]*100+0.5).to_i
|
109
|
-
buf << "%3d%%)" % (hsl[2]*100+0.5).to_i
|
110
|
-
|
111
|
-
if alpha != 255
|
112
|
-
buf << " - α(%3d%%)" % (alpha*100/255+0.5).to_i
|
113
|
-
else
|
114
|
-
buf << " " ## add empty for 255 (full opacity)
|
115
|
-
end
|
116
|
-
|
117
|
-
## note: add alpha channel to hex
|
118
|
-
alpha_hex = '%02x' % alpha
|
119
|
-
name = NAMES[ hex+alpha_hex ]
|
120
|
-
buf << " - #{name}" if name
|
121
|
-
|
122
|
-
buf
|
123
|
-
end
|
124
|
-
class << self
|
125
|
-
alias_method :fmt, :format
|
126
|
-
end
|
127
|
-
|
128
|
-
end # class Color
|
129
|
-
end # module Pixelart
|
130
|
-
|
131
|
-
|
data/lib/pixelart/gradient.rb
DELETED
@@ -1,106 +0,0 @@
|
|
1
|
-
|
2
|
-
## inspired / helped by
|
3
|
-
## https://en.wikipedia.org/wiki/List_of_software_palettes#Color_gradient_palettes
|
4
|
-
## https://github.com/mistic100/tinygradient
|
5
|
-
## https://mistic100.github.io/tinygradient/
|
6
|
-
## https://bsouthga.dev/posts/color-gradients-with-python
|
7
|
-
|
8
|
-
|
9
|
-
module Pixelart
|
10
|
-
|
11
|
-
class Gradient
|
12
|
-
|
13
|
-
def initialize( *stops )
|
14
|
-
## note: convert stop colors to rgb triplets e.g.
|
15
|
-
## from #ffffff to [255,255,255]
|
16
|
-
## #000000 to [0,0,0] etc.
|
17
|
-
@stops = stops.map do |stop|
|
18
|
-
stop = Color.parse( stop )
|
19
|
-
[Color.r(stop), Color.g(stop), Color.b(stop)]
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
def colors( steps )
|
25
|
-
segments = @stops.size - 1
|
26
|
-
|
27
|
-
## note: gradient will include start (first)
|
28
|
-
## AND stop color (last) - stop color is NOT excluded for now!!
|
29
|
-
if segments == 1
|
30
|
-
start = @stops[0]
|
31
|
-
stop = @stops[1]
|
32
|
-
|
33
|
-
gradient = linear_gradient( start, stop, steps,
|
34
|
-
include_stop: true )
|
35
|
-
else
|
36
|
-
steps_per_segment, mod = steps.divmod( segments )
|
37
|
-
raise ArgumentError, "steps (#{steps}) must be divisible by # of segments (#{segments}); expected mod of 0 but got #{mod}" if mod != 0
|
38
|
-
|
39
|
-
gradient = []
|
40
|
-
segments.times do |segment|
|
41
|
-
start = @stops[segment]
|
42
|
-
stop = @stops[segment+1]
|
43
|
-
include_stop = (segment == segments-1) ## note: only include stop if last segment!!
|
44
|
-
|
45
|
-
# print " segment #{segment+1}/#{segments} #{steps_per_segment} color(s) - "
|
46
|
-
# print " start: #{start.inspect} "
|
47
|
-
# print include_stop ? 'include' : 'exclude'
|
48
|
-
# print " stop: #{stop.inspect}"
|
49
|
-
# print "\n"
|
50
|
-
|
51
|
-
gradient += linear_gradient( start, stop, steps_per_segment,
|
52
|
-
include_stop: include_stop )
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
## convert to color (Integer)
|
57
|
-
gradient.map do |color|
|
58
|
-
Color.rgb( *color )
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
def interpolate( start, stop, steps, n )
|
65
|
-
## note: n - expected to start with 1,2,3,etc.
|
66
|
-
color = []
|
67
|
-
3.times do |i|
|
68
|
-
stepize = Float(stop[i] - start[i]) / Float(steps-1)
|
69
|
-
value = stepize * n
|
70
|
-
## convert back to Integer from Float
|
71
|
-
## add 0.5 for rounding up (starting with 0.5) - why? why not?
|
72
|
-
value = (value+0.5).to_i
|
73
|
-
value = start[i] + value
|
74
|
-
|
75
|
-
color << value
|
76
|
-
end
|
77
|
-
color
|
78
|
-
end
|
79
|
-
|
80
|
-
|
81
|
-
def linear_gradient( start, stop, steps,
|
82
|
-
include_stop: true )
|
83
|
-
|
84
|
-
gradient = [start] ## auto-add start color (first color in gradient)
|
85
|
-
|
86
|
-
if include_stop
|
87
|
-
1.upto( steps-2 ).each do |n| ## sub two (-2), that is, start and stop color
|
88
|
-
gradient << interpolate( start, stop, steps, n )
|
89
|
-
end
|
90
|
-
# note: use original passed in stop color (should match calculated)
|
91
|
-
gradient << stop
|
92
|
-
else
|
93
|
-
1.upto( steps-1 ).each do |n| ## sub one (-1), that is, start color only
|
94
|
-
## note: add one (+1) to steps because stop color gets excluded (not included)!!
|
95
|
-
gradient << interpolate( start, stop, steps+1, n )
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
gradient
|
100
|
-
end
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end # class Gradient
|
105
|
-
end # module Pixelart
|
106
|
-
|
data/lib/pixelart/palette.rb
DELETED
@@ -1,72 +0,0 @@
|
|
1
|
-
module Pixelart
|
2
|
-
|
3
|
-
|
4
|
-
class Palette8bit # or use Palette256 alias?
|
5
|
-
|
6
|
-
|
7
|
-
## auto-add grayscale 0 to 255
|
8
|
-
## e.g. rgb(0,0,0)
|
9
|
-
## rgb(1,1,1)
|
10
|
-
## rgb(2,2,2)
|
11
|
-
## ...
|
12
|
-
## rgb(255,255,255)
|
13
|
-
GRAYSCALE = (0..255).map { |n| Color.rgb( n, n, n ) }
|
14
|
-
|
15
|
-
|
16
|
-
## 8x32 gradient color stops
|
17
|
-
## see https://en.wikipedia.org/wiki/List_of_software_palettes#Color_gradient_palettes
|
18
|
-
|
19
|
-
SEPIA_STOPS = [
|
20
|
-
['080400', '262117'],
|
21
|
-
['272218', '453E2F'],
|
22
|
-
['463F30', '645C48'],
|
23
|
-
['655D48', '837A60'],
|
24
|
-
|
25
|
-
['847A60', 'A29778'],
|
26
|
-
['A39878', 'C1B590'],
|
27
|
-
['C2B691', 'E0D2A8'],
|
28
|
-
['E1D3A9', 'FEEFBF'],
|
29
|
-
]
|
30
|
-
|
31
|
-
BLUE_STOPS = [
|
32
|
-
['000000', '001F3E'],
|
33
|
-
['002040', '003F7E'],
|
34
|
-
['004080', '005FBD'],
|
35
|
-
['0060BF', '007FFD'],
|
36
|
-
|
37
|
-
['0080FF', '009FFF'],
|
38
|
-
['00A0FF', '00BFFF'],
|
39
|
-
['00C0FF', '00DFFF'],
|
40
|
-
['00E0FF', '00FEFF'],
|
41
|
-
]
|
42
|
-
|
43
|
-
FALSE_STOPS = [
|
44
|
-
['FF00FF', '6400FF'],
|
45
|
-
['5F00FF', '003CFF'],
|
46
|
-
['0041FF', '00DCFF'],
|
47
|
-
['00E1FF', '00FF82'],
|
48
|
-
|
49
|
-
['00FF7D', '1EFF00'],
|
50
|
-
['23FF00', 'BEFF00'],
|
51
|
-
['C3FF00', 'FFA000'],
|
52
|
-
['FF9B00', 'FF0000'],
|
53
|
-
]
|
54
|
-
|
55
|
-
|
56
|
-
def self.build_palette( gradients )
|
57
|
-
colors_per_gradient, mod = 256.divmod( gradients.size )
|
58
|
-
raise ArgumentError, "8bit palette - 256 must be divisible by # of gradients (#{gradients.size}; expected mod of 0 but got #{mod}" if mod != 0
|
59
|
-
|
60
|
-
colors = []
|
61
|
-
gradients.each do |stops|
|
62
|
-
colors += Gradient.new( *stops ).colors( colors_per_gradient )
|
63
|
-
end
|
64
|
-
colors
|
65
|
-
end
|
66
|
-
|
67
|
-
SEPIA = build_palette( SEPIA_STOPS )
|
68
|
-
BLUE = build_palette( BLUE_STOPS )
|
69
|
-
FALSE = build_palette( FALSE_STOPS )
|
70
|
-
end # class Palette8bit
|
71
|
-
end # module Pixelart
|
72
|
-
|