pixelart 0.1.5 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3022f23a6bfd66ef0e1c6f266fe2fd2ad24c802844be57737c2f202988e45292
4
- data.tar.gz: b8d11d3128aa897ef91f21dc727f3f9f713a94343142bbeb0cce228dfdba0e6b
3
+ metadata.gz: 066a13d4aff234657b8685f611168ed012fd6b949a292add28e7729f31c1bdc7
4
+ data.tar.gz: a7f748b8c246bb4c944da848baef19d4fdc6cd9c74ac96050c38aa907e680247
5
5
  SHA512:
6
- metadata.gz: '086d0b53c8ef6a949025fcd2cda50f505deae181e53bc6cc6241beec8470bbc9f6869a03f189cbf56c6706635e83e16a799dda4e383823dd032a1bb0029717c2'
7
- data.tar.gz: 3f577a57cd276d6a9eefdcf5374e5ea69e4f0f8f75cd69957098ec8c051258f70755c0883f8287bcf6e3768e35934bca4863d7ac41f55df7d1ae2395b4e12eed
6
+ metadata.gz: de3a072fd7dd9fe939340f1914fdeab6cc16dd5a2c25fb86548a3d5d89eb043b8d236dd62e2be86ac191bc0a5689eec2d3d6a3b465e08853a85cc285f394701a
7
+ data.tar.gz: 6fc5ffec090a5fbdc29b990812168d7716b4a563f3faffe8bd69ed09da70ca079af6334fa523cfdf3ed96f1228fd35842bc44d72139eeb6dc1b1d6299bf08b09
data/Manifest.txt CHANGED
@@ -5,8 +5,11 @@ Rakefile
5
5
  lib/pixelart.rb
6
6
  lib/pixelart/base.rb
7
7
  lib/pixelart/color.rb
8
+ lib/pixelart/composite.rb
8
9
  lib/pixelart/gradient.rb
9
10
  lib/pixelart/image.rb
11
+ lib/pixelart/led.rb
10
12
  lib/pixelart/misc.rb
11
13
  lib/pixelart/palette.rb
14
+ lib/pixelart/pixelator.rb
12
15
  lib/pixelart/version.rb
data/README.md CHANGED
@@ -3,8 +3,8 @@
3
3
  pixelart - mint your own pixel art images off chain using any design (in ascii text) in any colors; incl. 2x/4x/8x zoom for bigger sizes
4
4
 
5
5
 
6
- * home :: [github.com/cryptocopycats/mooncats](https://github.com/cryptocopycats/mooncats)
7
- * bugs :: [github.com/cryptocopycats/mooncats/issues](https://github.com/cryptocopycats/mooncats/issues)
6
+ * home :: [github.com/rubycoco/pixel](https://github.com/rubycoco/pixel)
7
+ * bugs :: [github.com/rubycoco/pixel/issues](https://github.com/rubycoco/pixel/issues)
8
8
  * gem :: [rubygems.org/gems/pixelart](https://rubygems.org/gems/pixelart)
9
9
  * rdoc :: [rubydoc.info/gems/pixelart](http://rubydoc.info/gems/pixelart)
10
10
 
@@ -71,8 +71,8 @@ img3x.save( './i/mooncat_white-3x.png' )
71
71
 
72
72
  Voila!
73
73
 
74
- ![](https://github.com/cryptocopycats/mooncats/raw/master/pixelart/i/mooncat_white.png)
75
- ![](https://github.com/cryptocopycats/mooncats/raw/master/pixelart/i/mooncat_white-3x.png)
74
+ ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/mooncat_white.png)
75
+ ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/mooncat_white-3x.png)
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
- ![](https://github.com/cryptocopycats/mooncats/raw/master/pixelart/i/mooncat_black.png)
104
- ![](https://github.com/cryptocopycats/mooncats/raw/master/pixelart/i/mooncat_black-3x.png)
103
+ ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/mooncat_black.png)
104
+ ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/mooncat_black-3x.png)
105
105
 
106
106
 
107
107
 
@@ -227,8 +227,8 @@ img5x.save( './i/vader5x.png' )
227
227
 
228
228
  Voila!
229
229
 
230
- ![](https://github.com/cryptocopycats/mooncats/raw/master/pixelart/i/vader.png)
231
- ![](https://github.com/cryptocopycats/mooncats/raw/master/pixelart/i/vader5x.png)
230
+ ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/vader.png)
231
+ ![](https://github.com/rubycoco/pixel/raw/master/pixelart/i/vader5x.png)
232
232
 
233
233
 
234
234
 
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ Hoe.spec 'pixelart' do
8
8
  self.summary = "pixelart - mint your own pixel art images off chain using any design (in ascii text) in any colors; incl. 2x/4x/8x zoom for bigger sizes"
9
9
  self.description = summary
10
10
 
11
- self.urls = { home: 'https://github.com/cryptocopycats/mooncats' }
11
+ self.urls = { home: 'https://github.com/rubycoco/pixel' }
12
12
 
13
13
  self.author = 'Gerald Bauer'
14
14
  self.email = 'wwwmake@googlegroups.com'
data/lib/pixelart/base.rb CHANGED
@@ -14,9 +14,16 @@ require 'pixelart/color'
14
14
  require 'pixelart/gradient'
15
15
  require 'pixelart/palette'
16
16
  require 'pixelart/image'
17
+ require 'pixelart/composite'
18
+
19
+ require 'pixelart/pixelator'
17
20
 
18
21
  require 'pixelart/misc' ## misc helpers
19
22
 
23
+ require 'pixelart/led' ## (special) effects / filters
24
+
25
+
26
+
20
27
 
21
28
  ##########
22
29
  # add some spelling convenience variants
@@ -27,6 +34,8 @@ module Pixelart
27
34
 
28
35
  Palette256Image = Palette8BitImage = Palette8bitImage =
29
36
  ImagePalette256 = ImagePalette8Bit = ImagePalette8bit
37
+
38
+ CompositeImage = ImageComposite
30
39
  end
31
40
 
32
41
 
@@ -7,7 +7,6 @@ class Color
7
7
  WHITE = 0xffffffff # rgba(255,255,255,255)
8
8
 
9
9
 
10
-
11
10
  def self.parse( color )
12
11
  if color.is_a?( Integer ) ## e.g. assumes ChunkyPNG::Color.rgb() or such
13
12
  color ## pass through as is 1:1
@@ -0,0 +1,77 @@
1
+ module Pixelart
2
+
3
+ class ImageComposite < Image # check: (re)name to Collage, Sheet, Sprites, or such?
4
+
5
+ ## default tile width / height in pixel -- check: (re)name to sprite or such? why? why not?
6
+ TILE_WIDTH = 24
7
+ TILE_HEIGHT = 24
8
+
9
+
10
+ def initialize( *args, **kwargs )
11
+ @tile_width = kwargs[:width] || kwargs[:tile_width] || TILE_WIDTH
12
+ @tile_height = kwargs[:height] || kwargs[:tile_height] || TILE_HEIGHT
13
+
14
+ ## todo/fix: check type - args[0] is Image!!!
15
+ if args.size == 1 ## assume "copy" c'tor with passed in image
16
+ img = args[0] ## pass image through as-is
17
+
18
+ @tile_cols = img.width / @tile_width ## e.g. 2400/24 = 100
19
+ @tile_rows = img.height / @tile_height ## e.g. 2400/24 = 100
20
+ @tile_count = @tile_cols * @tile_rows ## ## 10000 = 100x100 (2400x2400 pixel)
21
+ elsif args.size == 2 || args.size == 0 ## cols, rows
22
+ ## todo/fix: check type - args[0] & args[1] is Integer!!!!!
23
+ ## todo/check - find a better name for cols/rows - why? why not?
24
+ @tile_cols = args[0] || 3
25
+ @tile_rows = args[1] || 3
26
+ @tile_count = 0 # (track) current index (of added images)
27
+
28
+ img = ChunkyPNG::Image.new( @tile_cols * @tile_width,
29
+ @tile_rows * @tile_height )
30
+ else
31
+ raise ArgumentError, "cols, rows or image arguments expected; got: #{args.inspect}"
32
+ end
33
+
34
+ puts " #{img.height}x#{img.width} (height x width)"
35
+
36
+ super( nil, nil, img )
37
+ end
38
+
39
+
40
+ def count() @tile_count; end
41
+ alias_method :size, :count ## add size alias (confusing if starting with 0?) - why? why not?
42
+
43
+ #####
44
+ # set / add tile
45
+
46
+ def add( image )
47
+ y, x = @tile_count.divmod( @tile_cols )
48
+
49
+ puts " [#{@tile_count}] @ (#{x}/#{y}) #{image.width}x#{image.height} (height x width)"
50
+
51
+ ## note: image.image - "unwrap" the "raw" ChunkyPNG::Image
52
+ @img.compose!( image.image, x*@tile_width, y*@tile_height )
53
+ @tile_count += 1
54
+ end
55
+ alias_method :<<, :add
56
+
57
+
58
+
59
+ ######
60
+ # get tile
61
+
62
+ def tile( index )
63
+ y, x = index.divmod( @tile_rows )
64
+ img = @img.crop( x*@tile_width, y*@tile_height, @tile_width, @tile_height )
65
+ Image.new( img.width, img.height, img ) ## wrap in pixelart image
66
+ end
67
+
68
+ def []( *args ) ## overload - why? why not?
69
+ if args.size == 1
70
+ index = args[0]
71
+ tile( index )
72
+ else
73
+ super ## e.g [x,y] --- get pixel
74
+ end
75
+ end
76
+ end # class ImageComposite
77
+ end # module Pixelart
@@ -9,7 +9,14 @@ def self.read( path ) ## convenience helper
9
9
  end
10
10
 
11
11
 
12
- def self.parse( pixels, colors: )
12
+
13
+ CHARS = '.@xo^~%*+=:' ## todo/check: rename to default chars or such? why? why not?
14
+
15
+ ## todo/check: support default chars encoding auto-of-the-box always
16
+ ## or require user-defined chars to be passed in - why? why not?
17
+ def self.parse( pixels, colors:, chars: CHARS )
18
+ has_keys = colors.is_a?(Hash) ## check if passed-in user-defined keys (via hash table)?
19
+
13
20
  colors = parse_colors( colors )
14
21
  pixels = parse_pixels( pixels )
15
22
 
@@ -20,7 +27,19 @@ def self.parse( pixels, colors: )
20
27
 
21
28
  pixels.each_with_index do |row,y|
22
29
  row.each_with_index do |color,x|
23
- pixel = colors[color]
30
+ pixel = if has_keys ## if passed-in user-defined keys check only the user-defined keys
31
+ colors[color]
32
+ else
33
+ ## try map ascii art char (.@xo etc.) to color index (0,1,2)
34
+ ## if no match found - fallback on assuming draw by number (0 1 2 etc.) encoding
35
+ pos = chars.index( color )
36
+ if pos
37
+ colors[ pos.to_s ]
38
+ else ## assume nil (not found)
39
+ colors[ color ]
40
+ end
41
+ end
42
+
24
43
  img[x,y] = pixel
25
44
  end # each row
26
45
  end # each data
@@ -31,6 +50,9 @@ end
31
50
 
32
51
 
33
52
  def initialize( width, height, initial=Color::TRANSPARENT )
53
+ ### todo/fix:
54
+ ## change params to *args only - why? why not?
55
+ ## make width/height optional if image passed in?
34
56
 
35
57
  if initial.is_a?( ChunkyPNG::Image )
36
58
  @img = initial
@@ -170,6 +192,9 @@ def []=( x, y, value ) @img[x,y]=value; end
170
192
 
171
193
  def pixels() @img.pixels; end
172
194
 
195
+ ### todo/check: add colors() e.g. @img.pixels.uniq - why? why not?
196
+
197
+
173
198
  ## return image ref - use a different name - why? why not?
174
199
  ## change to to_image - why? why not?
175
200
  def image() @img; end
@@ -183,10 +208,7 @@ def self.parse_pixels( pixels )
183
208
  data = []
184
209
  pixels.each_line do |line|
185
210
  line = line.strip
186
- if line.empty?
187
- puts "!! WARN: skipping empty line in pixel art source"
188
- next
189
- end
211
+ next if line.start_with?( '#' ) || line.empty? ## note: allow comments and empty lines
190
212
 
191
213
  ## note: allow multiple spaces or tabs to separate pixel codes
192
214
  ## 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,12 +219,13 @@ def self.parse_pixels( pixels )
197
219
  end
198
220
 
199
221
 
222
+
200
223
  def self.parse_colors( colors )
201
224
  if colors.is_a?( Array ) ## convenience shortcut
202
225
  ## note: always auto-add color 0 as pre-defined transparent - why? why not?
203
226
  h = { '0' => Color::TRANSPARENT }
204
227
  colors.each_with_index do |color, i|
205
- h[ (i+1).to_s ] = Color.parse( color )
228
+ h[ (i+1).to_s ] = Color.parse( color )
206
229
  end
207
230
  h
208
231
  else ## assume hash table with color map
@@ -215,7 +238,6 @@ def self.parse_colors( colors )
215
238
  end
216
239
 
217
240
 
218
-
219
241
  end # class Image
220
242
  end # module Pixelart
221
243
 
@@ -0,0 +1,37 @@
1
+ module Pixelart
2
+
3
+
4
+ class Image
5
+ def led( led=8, spacing: 2, round_corner: false )
6
+
7
+ width = @img.width*led + (@img.width-1)*spacing
8
+ height = @img.height*led + (@img.height-1)*spacing
9
+
10
+ puts " #{width}x#{height}"
11
+
12
+ img = Image.new( width, height, Color::BLACK )
13
+
14
+ @img.width.times do |x|
15
+ @img.height.times do |y|
16
+ pixel = @img[x,y]
17
+ pixel = Color::BLACK if pixel == Color::TRANSPARENT
18
+ led.times do |n|
19
+ led.times do |m|
20
+ ## round a little - drop all four corners for now
21
+ next if round_corner &&
22
+ [[0,0],[0,1],[1,0],[1,1],[0,2],[2,0],
23
+ [0,led-1],[0,led-2],[1,led-1],[1,led-2],[0,led-3],[2,led-1],
24
+ [led-1,0],[led-1,1],[led-2,0],[led-2,1],[led-1,2],[led-3,0],
25
+ [led-1,led-1],[led-1,led-2],[led-2,led-1],[led-2,led-2],[led-1,led-3],[led-3,led-1],
26
+ ].include?( [n,m] )
27
+ img[x*led+n + spacing*x,
28
+ y*led+m + spacing*y] = pixel
29
+ end
30
+ end
31
+ end
32
+ end
33
+ img
34
+ end
35
+ end # class Image
36
+ end # module Pixelart
37
+
data/lib/pixelart/misc.rb CHANGED
@@ -15,6 +15,9 @@ class ImagePalette8bit < Image # or use Palette256 alias?
15
15
  img = ChunkyPNG::Image.new( 32*size+(32-1)*spacing,
16
16
  8*size+(8-1)*spacing )
17
17
 
18
+
19
+ colors =colors.map {|color| Color.parse( color ) }
20
+
18
21
  colors.each_with_index do |color,i|
19
22
  y,x = i.divmod( 32 )
20
23
  if size > 1
@@ -32,6 +35,32 @@ class ImagePalette8bit < Image # or use Palette256 alias?
32
35
  super( img.width, img.height, img )
33
36
  end
34
37
  end # class ImagePalette8bit
38
+
39
+
40
+
41
+ class ImageColorBar < Image
42
+ ## make a color bar
43
+ ## keep auto-zoom 24x or such - why? why not?
44
+ def initialize( colors, size: 24 )
45
+ img = ChunkyPNG::Image.new( colors.size*size,
46
+ size,
47
+ ChunkyPNG::Color::WHITE ) # why? why not?
48
+
49
+ colors = colors.map {|color| Color.parse( color ) }
50
+
51
+ colors.each_with_index do |color,i|
52
+ size.times do |x|
53
+ size.times do |y|
54
+ img[x+size*i,y] = color
55
+ end
56
+ end
57
+ end
58
+
59
+ super( img.width, img.height, img )
60
+ end
61
+ end # class ImageColorBar
62
+
63
+
35
64
  end # module Pixelart
36
65
 
37
66
 
@@ -0,0 +1,165 @@
1
+ module Pixelart
2
+
3
+
4
+ class Pixelator # or use Minifier or such - rename - why? why not?
5
+
6
+ def initialize( img, width=24, height=24 )
7
+ @img = img.is_a?( Image ) ? img.image : img ## "unwrap" if Pixelart::Image
8
+ @width = width
9
+ @height = height
10
+
11
+ ## calculate pixel size / density / resolution
12
+ ## how many pixels per pixel?
13
+ @xsize, @xoverflow = img.width.divmod( width )
14
+ @ysize, @yoverflow = img.height.divmod( height )
15
+
16
+ puts "minify image size from (#{@img.width}x#{@img.height}) to (#{width}x#{height})"
17
+ puts " pixel size (#{@xsize}x#{@ysize}) - #{@xsize*@ysize} pixel(s) per pixel"
18
+ puts " overflow x: #{@xoverflow}, y: #{@yoverflow} pixel(s)" if @xoverflow > 0 || @yoverflow > 0
19
+ end
20
+
21
+
22
+ def grid( spacing: 10 )
23
+ width = @img.width + (@width-1)*spacing
24
+ height = @img.height + (@height-1)*spacing
25
+
26
+ img = ChunkyPNG::Image.new( width, height, ChunkyPNG::Color::WHITE )
27
+
28
+ @img.width.times do |x|
29
+ xpixel = x/@xsize
30
+ @img.height.times do |y|
31
+ ypixel = y/@ysize
32
+
33
+ ## clip overflow pixels
34
+ xpixel = @width-1 if xpixel >= @width
35
+ ypixel = @height-1 if ypixel >= @height
36
+
37
+ color = @img[x,y]
38
+ img[x + spacing*xpixel,
39
+ y + spacing*ypixel] = color
40
+ end
41
+ end
42
+
43
+ Image.new( img.width, img.height, img ) ## wrap in Pixelart::Image - why? why not?
44
+ end
45
+
46
+
47
+ # pixels by coordinates (x/y) with color statistics / usage
48
+ def pixels
49
+ @pixels ||= begin
50
+ pixels = []
51
+ @img.width.times do |x|
52
+ xpixel = x/@xsize
53
+ @img.height.times do |y|
54
+ ypixel = y/@ysize
55
+
56
+ ## skip/cut off overflow pixels
57
+ next if xpixel >= @width || ypixel >= @height
58
+
59
+ color = @img[x,y]
60
+ colors = pixels[xpixel+ypixel*@width] ||= Hash.new(0)
61
+ colors[ color ] += 1
62
+ end
63
+ end
64
+
65
+ ## sort pixel colors by usage / count (highest first)
66
+ pixels = pixels.map do |pixel|
67
+ pixel.sort do |l,r|
68
+ r[1] <=> l[1]
69
+ end.to_h
70
+ end
71
+ pixels
72
+ end
73
+ end
74
+
75
+ def pixel(x,y) pixels[x+y*@width]; end
76
+ alias_method :[], :pixel
77
+
78
+
79
+ def can_pixelate?( threshold: 50 )
80
+ # check if any pixel has NOT a color with a 50% majority?
81
+ count = 0
82
+ @width.times do |x|
83
+ @height.times do |y|
84
+ pixel = pixel( x, y )
85
+ sum = pixel.values.sum
86
+ color_count = pixel.values[0]
87
+
88
+ threshold_count = sum / (100/threshold)
89
+ if color_count < threshold_count
90
+ count += 1
91
+ puts "!! #{color_count} < #{threshold_count} (#{threshold}%)"
92
+ ## todo/check: stor warn in a public errors or warns array - why? why not?
93
+ puts "!! WARN #{count} - pixel (#{x}/#{y}) - no majority (#{threshold}%) color:"
94
+ pp pixel
95
+ end
96
+ end
97
+ end
98
+
99
+ count == 0 ## return true if not warnings found
100
+ end
101
+ alias_method :pixelate?, :can_pixelate?
102
+
103
+
104
+ def pixelate
105
+ img = ChunkyPNG::Image.new( @width, @height )
106
+
107
+ @width.times do |x|
108
+ @height.times do |y|
109
+ pixel = pixel( x, y )
110
+ color = pixel.keys[0]
111
+ img[x,y] = color
112
+ end
113
+ end
114
+
115
+ Image.new( img.width, img.height, img ) ## wrap in Pixelart::Image - why? why not?
116
+ end
117
+
118
+ def outline
119
+ ## create a two color outline (transparent and non-transparent color)
120
+ img = ChunkyPNG::Image.new( @width, @height )
121
+
122
+ @width.times do |x|
123
+ @height.times do |y|
124
+ pixel = pixel( x, y )
125
+ ## calculate pixel count for transparent and non-transparent parts
126
+ ## note:
127
+ ## also count all colors with alpha channel < 200 to transparent!!
128
+ transparent_count, color_count = pixel.reduce([0,0]) do |mem, (color,count)|
129
+ hsl = Color.to_hsl( color )
130
+ ## get alpha channel (transparency) for hsla
131
+ ## 0-255 max.
132
+ alpha = hsl[3]
133
+ if color == 0x00 || alpha < 200
134
+ mem[0] += count
135
+ else
136
+ mem[1] += count
137
+ end
138
+ mem
139
+ end
140
+
141
+ print "."
142
+ if transparent_count > 0 && color_count > 0
143
+ print "(#{x}/#{y}=>#{transparent_count}/#{color_count})"
144
+ end
145
+
146
+ ## todo/check:
147
+ ## warn if sum_transparent == sum_color
148
+ ## or within "threshold" e.g. below 55% or 58% or such - why? why not?
149
+ ## or add treshold as param to outline?
150
+ color = if transparent_count > color_count
151
+ 0x0
152
+ else
153
+ 0x0000ffff ## use blue for now
154
+ end
155
+
156
+ img[x,y] = color
157
+ end
158
+ end
159
+ print "\n"
160
+
161
+ Image.new( img.width, img.height, img ) ## wrap in Pixelart::Image - why? why not?
162
+ end
163
+ end # class Pixelator
164
+ end # module Pixelart
165
+
@@ -2,8 +2,8 @@
2
2
  module Pixelart
3
3
 
4
4
  MAJOR = 0
5
- MINOR = 1
6
- PATCH = 5
5
+ MINOR = 2
6
+ PATCH = 1
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
8
8
 
9
9
  def self.version
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.5
4
+ version: 0.2.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-04-18 00:00:00.000000000 Z
11
+ date: 2021-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chunky_png
@@ -75,12 +75,15 @@ files:
75
75
  - lib/pixelart.rb
76
76
  - lib/pixelart/base.rb
77
77
  - lib/pixelart/color.rb
78
+ - lib/pixelart/composite.rb
78
79
  - lib/pixelart/gradient.rb
79
80
  - lib/pixelart/image.rb
81
+ - lib/pixelart/led.rb
80
82
  - lib/pixelart/misc.rb
81
83
  - lib/pixelart/palette.rb
84
+ - lib/pixelart/pixelator.rb
82
85
  - lib/pixelart/version.rb
83
- homepage: https://github.com/cryptocopycats/mooncats
86
+ homepage: https://github.com/rubycoco/pixel
84
87
  licenses:
85
88
  - Public Domain
86
89
  metadata: {}