pixelart 0.2.1 → 1.1.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 +4 -0
- data/Rakefile +1 -0
- data/lib/pixelart/base.rb +37 -2
- data/lib/pixelart/blur.rb +19 -0
- data/lib/pixelart/composite.rb +1 -1
- data/lib/pixelart/image.rb +15 -0
- data/lib/pixelart/sketch.rb +69 -0
- data/lib/pixelart/spots.rb +146 -0
- data/lib/pixelart/vector.rb +108 -0
- data/lib/pixelart/version.rb +2 -2
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba25604fe4925484c00f133e26e2d3d67aec4b23af8daa2cc3306235a2c704a6
|
4
|
+
data.tar.gz: 570abba81b211dc9aa1df3939b15e9e6845676884c01197c25d9cb23d3b4ba0f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1af47c73f13001e103d1ad0a4965d8125763935c2b962d50769f48cba1728ea351645bfb381721425c776b20ccd484acde470a41044b055552bb929e18a8e98d
|
7
|
+
data.tar.gz: e60473974839ade310e105c148f98b7a1e5232f6e0491df1108cb0f50028d8ff1b02af8e9c2ba61ede00c6396b4d5bf8833dd3c757692190f101ecdc6e6072f8
|
data/Manifest.txt
CHANGED
@@ -4,6 +4,7 @@ README.md
|
|
4
4
|
Rakefile
|
5
5
|
lib/pixelart.rb
|
6
6
|
lib/pixelart/base.rb
|
7
|
+
lib/pixelart/blur.rb
|
7
8
|
lib/pixelart/color.rb
|
8
9
|
lib/pixelart/composite.rb
|
9
10
|
lib/pixelart/gradient.rb
|
@@ -12,4 +13,7 @@ lib/pixelart/led.rb
|
|
12
13
|
lib/pixelart/misc.rb
|
13
14
|
lib/pixelart/palette.rb
|
14
15
|
lib/pixelart/pixelator.rb
|
16
|
+
lib/pixelart/sketch.rb
|
17
|
+
lib/pixelart/spots.rb
|
18
|
+
lib/pixelart/vector.rb
|
15
19
|
lib/pixelart/version.rb
|
data/Rakefile
CHANGED
data/lib/pixelart/base.rb
CHANGED
@@ -1,12 +1,24 @@
|
|
1
|
-
|
1
|
+
###############
|
2
|
+
# 3rd party
|
2
3
|
require 'chunky_png'
|
3
4
|
|
5
|
+
# optional
|
6
|
+
# note: requires installed imagemagick command line installed for usage
|
7
|
+
require 'mini_magick'
|
8
|
+
|
9
|
+
|
10
|
+
|
4
11
|
## stdlib
|
5
12
|
require 'pp'
|
6
13
|
require 'time'
|
7
14
|
require 'date'
|
8
15
|
require 'fileutils'
|
9
16
|
|
17
|
+
require 'json'
|
18
|
+
require 'yaml'
|
19
|
+
|
20
|
+
|
21
|
+
|
10
22
|
|
11
23
|
## our own code
|
12
24
|
require 'pixelart/version' # note: let version always go first
|
@@ -16,11 +28,34 @@ require 'pixelart/palette'
|
|
16
28
|
require 'pixelart/image'
|
17
29
|
require 'pixelart/composite'
|
18
30
|
|
31
|
+
|
19
32
|
require 'pixelart/pixelator'
|
20
33
|
|
21
34
|
require 'pixelart/misc' ## misc helpers
|
22
35
|
|
23
|
-
|
36
|
+
#########################
|
37
|
+
# (special) effects / filters
|
38
|
+
require 'pixelart/led'
|
39
|
+
require 'pixelart/sketch'
|
40
|
+
|
41
|
+
## (special) effects / filters that require imagemagick
|
42
|
+
|
43
|
+
|
44
|
+
## todo/check - use a config block or such - why? why not?
|
45
|
+
module Pixelart
|
46
|
+
MAGICK_SCRIPT = './tmp/magick_script.txt'
|
47
|
+
MAGICK_INPUT = './tmp/magick_input.png'
|
48
|
+
MAGICK_OUTPUT = './tmp/magick_output.png'
|
49
|
+
end
|
50
|
+
|
51
|
+
require 'pixelart/vector' ## vector graphics helpers
|
52
|
+
|
53
|
+
|
54
|
+
require 'pixelart/spots'
|
55
|
+
require 'pixelart/blur'
|
56
|
+
|
57
|
+
|
58
|
+
|
24
59
|
|
25
60
|
|
26
61
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Pixelart
|
2
|
+
|
3
|
+
class Image
|
4
|
+
|
5
|
+
def blur( blur=2 )
|
6
|
+
@img.save( MAGICK_INPUT )
|
7
|
+
|
8
|
+
MiniMagick::Tool::Magick.new do |magick|
|
9
|
+
magick << MAGICK_INPUT
|
10
|
+
magick.blur( "#{blur}x#{blur}" )
|
11
|
+
magick << MAGICK_OUTPUT
|
12
|
+
end
|
13
|
+
|
14
|
+
Image.read( MAGICK_OUTPUT )
|
15
|
+
end
|
16
|
+
|
17
|
+
end # class Image
|
18
|
+
end # class Pixelart
|
19
|
+
|
data/lib/pixelart/composite.rb
CHANGED
@@ -60,7 +60,7 @@ class ImageComposite < Image # check: (re)name to Collage, Sheet, Sprites, or s
|
|
60
60
|
# get tile
|
61
61
|
|
62
62
|
def tile( index )
|
63
|
-
y, x = index.divmod( @
|
63
|
+
y, x = index.divmod( @tile_cols )
|
64
64
|
img = @img.crop( x*@tile_width, y*@tile_height, @tile_width, @tile_height )
|
65
65
|
Image.new( img.width, img.height, img ) ## wrap in pixelart image
|
66
66
|
end
|
data/lib/pixelart/image.rb
CHANGED
@@ -95,6 +95,21 @@ def grayscale
|
|
95
95
|
Image.new( img.width, img.height, img )
|
96
96
|
end
|
97
97
|
|
98
|
+
def mirror
|
99
|
+
img = @img.mirror
|
100
|
+
Image.new( img.width, img.height, img )
|
101
|
+
end
|
102
|
+
alias_method :flip_vertically, :mirror
|
103
|
+
|
104
|
+
def flip
|
105
|
+
img = @img.flip
|
106
|
+
Image.new( img.width, img.height, img )
|
107
|
+
end
|
108
|
+
alias_method :flip_horizontally, :flip
|
109
|
+
|
110
|
+
|
111
|
+
|
112
|
+
|
98
113
|
## add replace_colors alias too? - why? why not?
|
99
114
|
def change_colors( color_map )
|
100
115
|
color_map = _parse_color_map( color_map )
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Pixelart
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
class Image
|
6
|
+
def sketch( sketch=4, line: 1 )
|
7
|
+
# todo: line - find a better name eg. line_strenght/width or such?
|
8
|
+
width = @img.width*sketch + (@img.width+1)*line
|
9
|
+
height = @img.height*sketch + (@img.height+1)*line
|
10
|
+
|
11
|
+
puts " #{width}x#{height}"
|
12
|
+
|
13
|
+
img = Image.new( width, height, Color::WHITE )
|
14
|
+
|
15
|
+
@img.width.times do |x|
|
16
|
+
@img.height.times do |y|
|
17
|
+
pixel = @img[x,y]
|
18
|
+
|
19
|
+
## get surrounding pixels - if "out-of-bound" use transparent (0)
|
20
|
+
left = x == 0 ? Color::TRANSPARENT : @img[x-1,y]
|
21
|
+
top = y == 0 ? Color::TRANSPARENT : @img[x ,y-1]
|
22
|
+
|
23
|
+
if pixel != left ## draw vertical line
|
24
|
+
(sketch+line*2).times do |n|
|
25
|
+
line.times do |m|
|
26
|
+
img[ x*sketch + line*x + m,
|
27
|
+
n + y*sketch + line*y] = Color::BLACK
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if pixel != top ## draw horizontal line
|
33
|
+
(sketch+line*2).times do |n|
|
34
|
+
line.times do |m|
|
35
|
+
img[n + x*sketch + line*x,
|
36
|
+
y*sketch + line*y + m] = Color::BLACK
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
## check special edge case for x and y to add "finish-up" right and bottom line
|
43
|
+
if x == @img.width-1 && pixel != Color::TRANSPARENT
|
44
|
+
## draw vertical line
|
45
|
+
(sketch+line*2).times do |n|
|
46
|
+
line.times do |m|
|
47
|
+
img[ (x+1)*sketch + line*(x+1) + m,
|
48
|
+
n + y*sketch + line*y] = Color::BLACK
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if y== @img.height-1 && pixel != Color::TRANSPARENT
|
54
|
+
## draw horizontal line
|
55
|
+
(sketch+line*2).times do |n|
|
56
|
+
line.times do |m|
|
57
|
+
img[n + x*sketch + line*x,
|
58
|
+
(y+1)*sketch + line*(y+1) + m] = Color::BLACK
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
img
|
66
|
+
end
|
67
|
+
end # class Image
|
68
|
+
end # module Pixelart
|
69
|
+
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module Pixelart
|
2
|
+
|
3
|
+
class Image
|
4
|
+
|
5
|
+
|
6
|
+
def spots_hidef( spot=10,
|
7
|
+
spacing: 0,
|
8
|
+
center: nil,
|
9
|
+
radius: nil,
|
10
|
+
background: nil,
|
11
|
+
lightness: nil,
|
12
|
+
odd: false )
|
13
|
+
|
14
|
+
width = @img.width*spot+(@img.width-1)*spacing
|
15
|
+
height = @img.height*spot+(@img.height-1)*spacing
|
16
|
+
|
17
|
+
## puts " #{width}x#{height}"
|
18
|
+
|
19
|
+
## settings in a hash for "pretty printing" in comments
|
20
|
+
settings = { spot: spot
|
21
|
+
}
|
22
|
+
|
23
|
+
settings[ :spacing ] = spacing if spacing
|
24
|
+
settings[ :center ] = center if center
|
25
|
+
settings[ :radius ] = radius if radius
|
26
|
+
settings[ :background ] = background if background
|
27
|
+
settings[ :lightness ] = lightness if lightness
|
28
|
+
settings[ :odd ] = odd if odd
|
29
|
+
|
30
|
+
|
31
|
+
v = Vector.new( width, height, header: <<TXT )
|
32
|
+
generated by pixelart/v#{VERSION} on #{Time.now.utc}
|
33
|
+
|
34
|
+
spots_hidef with settings:
|
35
|
+
#{settings.to_json}
|
36
|
+
TXT
|
37
|
+
|
38
|
+
|
39
|
+
min_center, max_center = center ? center : [0,0]
|
40
|
+
min_radius, max_radius = radius ? radius : [0,0]
|
41
|
+
|
42
|
+
## note: allow passing in array of colors (will get randomally picked)
|
43
|
+
background_colors = if background
|
44
|
+
## check for array; otherwise assume single color passed in
|
45
|
+
background_ary = background.is_a?( Array) ? background : [background]
|
46
|
+
background_ary.map { |background| Color.parse( background ) }
|
47
|
+
else
|
48
|
+
[0] # transparent (default - no background)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
min_lightness, max_lightness = lightness ? lightness : [0.0,0.0]
|
53
|
+
|
54
|
+
|
55
|
+
@img.width.times do |x|
|
56
|
+
@img.height.times do |y|
|
57
|
+
color = @img[ x, y ]
|
58
|
+
|
59
|
+
if color == 0 ## transparent
|
60
|
+
next if background.nil?
|
61
|
+
|
62
|
+
color = if background_colors.size == 1
|
63
|
+
background_colors[0]
|
64
|
+
else ## pick random background color
|
65
|
+
background_colors[ rand( background_colors.size ) ]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
if lightness
|
71
|
+
## todo/check: make it work with alpha too
|
72
|
+
h,s,l = Color.to_hsl( color, include_alpha: false )
|
73
|
+
|
74
|
+
h = h % 360 ## make sure h(ue) is always positive!!!
|
75
|
+
|
76
|
+
## note: rand() return between 0.0 and 1.0
|
77
|
+
l_diff = min_lightness +
|
78
|
+
(max_lightness-min_lightness)*rand()
|
79
|
+
|
80
|
+
lnew = [1.0, l+l_diff].min
|
81
|
+
lnew = [0.0, lnew].max
|
82
|
+
|
83
|
+
## print " #{l}+#{l_diff}=#{lnew} "
|
84
|
+
|
85
|
+
color = Color.from_hsl( h,
|
86
|
+
[1.0, s].min,
|
87
|
+
lnew )
|
88
|
+
end
|
89
|
+
|
90
|
+
## note: return hexstring with leading #
|
91
|
+
# e.g. 0 becomes #00000000
|
92
|
+
# and so on
|
93
|
+
color_hex = Color.to_hex( color, include_alpha: true )
|
94
|
+
|
95
|
+
cx_offset,
|
96
|
+
cy_offset = if center ## randomize (offset off center +/-)
|
97
|
+
[(spot/2 + min_center) + rand( max_center-min_center ),
|
98
|
+
(spot/2 + min_center) + rand( max_center-min_center )]
|
99
|
+
else
|
100
|
+
[spot/2, ## center
|
101
|
+
spot/2]
|
102
|
+
end
|
103
|
+
|
104
|
+
cx = x*spot + x*spacing + cx_offset
|
105
|
+
cy = y*spot + y*spacing + cx_offset
|
106
|
+
|
107
|
+
r = if radius ## randomize (radius +/-)
|
108
|
+
min_radius + rand( max_radius-min_radius )
|
109
|
+
else
|
110
|
+
spot/2
|
111
|
+
end
|
112
|
+
|
113
|
+
cx += spot/2 if odd && (y % 2 == 1) ## add odd offset
|
114
|
+
|
115
|
+
|
116
|
+
v.circle( cx: cx, cy: cy, r: r, fill: color_hex)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
v
|
120
|
+
end ## method spots_hidef
|
121
|
+
alias_method :spots_hd, :spots_hidef
|
122
|
+
|
123
|
+
|
124
|
+
def spots( spot=10,
|
125
|
+
spacing: 0,
|
126
|
+
center: nil,
|
127
|
+
radius: nil,
|
128
|
+
background: nil,
|
129
|
+
lightness: nil,
|
130
|
+
odd: false )
|
131
|
+
|
132
|
+
v = spots_hidef( spot,
|
133
|
+
spacing: spacing,
|
134
|
+
center: center,
|
135
|
+
radius: radius,
|
136
|
+
background: background,
|
137
|
+
lightness: lightness,
|
138
|
+
odd: odd )
|
139
|
+
|
140
|
+
v.to_image
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
end # class Image
|
145
|
+
end # class Pixelart
|
146
|
+
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Pixelart
|
2
|
+
|
3
|
+
|
4
|
+
class Vector # holds a vector graphics image (in source)
|
5
|
+
|
6
|
+
class Shape; end
|
7
|
+
class Circle < Shape
|
8
|
+
def initialize( cx, cy, r, fill: )
|
9
|
+
@cx = cx
|
10
|
+
@cy = cy
|
11
|
+
@r = r
|
12
|
+
@fill = fill
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_svg
|
16
|
+
%Q{<circle cx="#{@cx}" cy="#{@cy}" r="#{@r}" fill="#{@fill}" />}
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_magick
|
20
|
+
## circle
|
21
|
+
## give the center and any point on the perimeter (boundary)
|
22
|
+
px = @cx+@r
|
23
|
+
py = @cy
|
24
|
+
"-fill '#{@fill}' -draw 'circle #{@cx},#{@cy},#{px},#{py}'"
|
25
|
+
end
|
26
|
+
end # class Circle
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
def initialize( width, height, header: nil )
|
31
|
+
@width = width
|
32
|
+
@height = height
|
33
|
+
|
34
|
+
@header = header
|
35
|
+
@shapes = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def circle( cx:, cy:, r:, fill: )
|
39
|
+
@shapes << Circle.new( cx, cy, r, fill: fill )
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
def to_image
|
45
|
+
## use an empty image (canvas) with transparent background
|
46
|
+
## as magick input image
|
47
|
+
canvas = Image.new( @width, @height )
|
48
|
+
canvas.save( MAGICK_INPUT )
|
49
|
+
|
50
|
+
## note: magick command line might get way too long, thus,
|
51
|
+
## save commands to a script
|
52
|
+
## note: save magick input first (see above) before save script
|
53
|
+
## will (auto-)create missing directories in path (if missing)
|
54
|
+
|
55
|
+
File.open( MAGICK_SCRIPT, 'w:utf-8' ) do |f|
|
56
|
+
f.write( "#{MAGICK_INPUT} \\\n" )
|
57
|
+
@shapes.each do |shape|
|
58
|
+
f.write( "#{shape.to_magick} \\\n" )
|
59
|
+
end
|
60
|
+
f.write( "-write #{MAGICK_OUTPUT}\n" )
|
61
|
+
end
|
62
|
+
|
63
|
+
MiniMagick::Tool::Magick.new do |magick|
|
64
|
+
magick.script( MAGICK_SCRIPT )
|
65
|
+
end
|
66
|
+
|
67
|
+
Image.read( MAGICK_OUTPUT )
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def to_svg
|
72
|
+
buf = String.new('')
|
73
|
+
|
74
|
+
if @header
|
75
|
+
buf << "<!--\n"
|
76
|
+
## auto-indent lines by five (5) spaces for now
|
77
|
+
@header.each_line do |line|
|
78
|
+
buf << " #{line}"
|
79
|
+
end
|
80
|
+
buf << "\n-->\n\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
buf << %Q{<svg version="1.1" width="#{@width}" height="#{@height}" xmlns="http://www.w3.org/2000/svg">\n}
|
84
|
+
@shapes.each do |shape|
|
85
|
+
buf << " #{shape.to_svg}\n"
|
86
|
+
end
|
87
|
+
buf << "</svg>"
|
88
|
+
buf
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def save( path, format: nil )
|
93
|
+
if format && format.downcase == 'png' ## support png with image magick
|
94
|
+
img = to_image
|
95
|
+
img.save( path )
|
96
|
+
else
|
97
|
+
# make sure outdir exits
|
98
|
+
outdir = File.dirname( path )
|
99
|
+
FileUtils.mkdir_p( outdir ) unless Dir.exist?( outdir )
|
100
|
+
File.open( path, 'w:utf-8' ) do |f|
|
101
|
+
f.write( to_svg )
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
alias_method :write, :save
|
106
|
+
|
107
|
+
end # class Vector
|
108
|
+
end # module Pixelart
|
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:
|
4
|
+
version: 1.1.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: 2021-
|
11
|
+
date: 2021-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chunky_png
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mini_magick
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rdoc
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,6 +88,7 @@ files:
|
|
74
88
|
- Rakefile
|
75
89
|
- lib/pixelart.rb
|
76
90
|
- lib/pixelart/base.rb
|
91
|
+
- lib/pixelart/blur.rb
|
77
92
|
- lib/pixelart/color.rb
|
78
93
|
- lib/pixelart/composite.rb
|
79
94
|
- lib/pixelart/gradient.rb
|
@@ -82,6 +97,9 @@ files:
|
|
82
97
|
- lib/pixelart/misc.rb
|
83
98
|
- lib/pixelart/palette.rb
|
84
99
|
- lib/pixelart/pixelator.rb
|
100
|
+
- lib/pixelart/sketch.rb
|
101
|
+
- lib/pixelart/spots.rb
|
102
|
+
- lib/pixelart/vector.rb
|
85
103
|
- lib/pixelart/version.rb
|
86
104
|
homepage: https://github.com/rubycoco/pixel
|
87
105
|
licenses:
|