pixelart 1.3.1 → 1.3.4

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 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
-