pixelart 1.3.1 → 1.3.4

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: a9cb1460263f319b98c4d333f6caa42118eb94f583f9cc6cdaa5bfa6bc4316b9
4
- data.tar.gz: 2deabe3c3bc35c62d249e0ff5178d23f986d1131c96daf53a8d36fde51cd6de8
3
+ metadata.gz: 07a749afe60b5174e207574ab916bfd4a41dd844897ec1375947b170cb155fb9
4
+ data.tar.gz: 4a5f82ec221fb71a3d4db47cb131d86a8f4c778b3eaaae22f42c55492025e4c5
5
5
  SHA512:
6
- metadata.gz: 57213049f46aac9a612adb3d9e236ce03aa336318705cabaed98f1f87557dc5dc08a73a7f50254852ab0c3c988ca0ed3d5b3ba6666925640ab837692c573be7c
7
- data.tar.gz: 9ff7ae16a77eca4b2d4deb65b98747aa053ab97ab15b0f8a40d373f5ecb96dcd2c18d2fdfd18beca0c06a5e9330719dd2df1a3be1efac07c8f008dfa0b4db614
6
+ metadata.gz: 10845c93bda5c7ab3186e84b4621028a435eda87dca9c5e651f5259dc482c9d3d00a3da72054a262a43baa79e18e3f82cca7960bb15b1076a7ed2c121caf1d17
7
+ data.tar.gz: 5c77a94549a22310c07f08e380a1029d538e7e2d5eced92b24ce65a64824e2ecfb9374f03184edfb480e1d8fa2ca6cbfc22cf8ba09d30459ca995080a0f9defd
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
@@ -18,6 +18,7 @@ Hoe.spec 'pixelart' do
18
18
  self.history_file = 'CHANGELOG.md'
19
19
 
20
20
  self.extra_deps = [
21
+ ['pixelart-colors'],
21
22
  ['chunky_png'],
22
23
  ['mini_magick'],
23
24
  ['csvreader'],
data/lib/pixelart/base.rb CHANGED
@@ -1,3 +1,9 @@
1
+ ###
2
+ # base module
3
+ require 'pixelart/colors'
4
+
5
+
6
+
1
7
  ###############
2
8
  # 3rd party
3
9
  require 'chunky_png'
@@ -11,23 +17,9 @@ require 'mini_magick'
11
17
  require 'csvreader'
12
18
 
13
19
 
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
@@ -170,24 +170,27 @@ module Pixelart
170
170
 
171
171
 
172
172
 
173
+ def generate_image( *values, background: nil, before: nil )
174
+ ## note: generate_image NO longer supports
175
+ ## - generate by integer number (indexes), sorry
173
176
 
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
-
177
+ recs = to_recs( *values )
182
178
 
179
+ ## note: first construct/generate image on transparent background
180
+ ## add background if present as LAST step
183
181
  img = Image.new( @width, @height )
184
182
 
185
- if background ## for now assume background is (simply) color
186
- img.compose!( Image.new( @width, @height, background ) )
183
+ recs.each do |rec|
184
+ ## note: before call(back) MUST change image INPLACE!!!!
185
+ before.call( img, rec ) if before
186
+ img.compose!( @sheet[ rec.id ] )
187
187
  end
188
188
 
189
- ids.each do |id|
190
- img.compose!( @sheet[ id ] )
189
+ if background ## for now assume background is (simply) color
190
+ img2 = Image.new( @width, @height )
191
+ img2.compose!( Image.new( @width, @height, background ) )
192
+ img2.compose!( img )
193
+ img = img2
191
194
  end
192
195
 
193
196
  img
@@ -3,7 +3,7 @@ module Pixelart
3
3
 
4
4
  MAJOR = 1
5
5
  MINOR = 3
6
- PATCH = 1
6
+ PATCH = 4
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
8
8
 
9
9
  def self.version
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,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pixelart
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.4
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-19 00:00:00.000000000 Z
11
+ date: 2022-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pixelart-colors
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: chunky_png
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -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
@@ -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
-
@@ -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
-
@@ -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
-