zpng 0.1.2 → 0.2.0
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.
- data/README.md +72 -71
- data/TODO +10 -0
- data/VERSION +1 -1
- data/lib/zpng.rb +1 -0
- data/lib/zpng/adam7_decoder.rb +25 -17
- data/lib/zpng/chunk.rb +2 -0
- data/lib/zpng/cli.rb +21 -6
- data/lib/zpng/color.rb +95 -38
- data/lib/zpng/deep_copyable.rb +7 -0
- data/lib/zpng/image.rb +42 -4
- data/lib/zpng/scan_line.rb +34 -21
- data/misc/chars.png +0 -0
- data/misc/gen_ascii_map.rb +63 -0
- data/spec/adam7_spec.rb +61 -0
- data/spec/ascii_spec.rb +1 -1
- data/spec/color_spec.rb +30 -1
- data/spec/create_image_spec.rb +6 -6
- data/spec/crop_spec.rb +4 -4
- data/spec/modify_spec.rb +1 -1
- data/spec/running_pixel_spec.rb +1 -1
- data/zpng.gemspec +9 -3
- metadata +9 -3
data/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Usage
|
|
|
23
23
|
-S, --scanlines Show scanlines info
|
|
24
24
|
-P, --palette Show palette
|
|
25
25
|
-E, --extract-chunk ID extract a single chunk
|
|
26
|
-
-
|
|
26
|
+
-D, --imagedata dump unpacked Image Data (IDAT) chunk(s) to stdout
|
|
27
27
|
|
|
28
28
|
-c, --crop GEOMETRY crop image, {WIDTH}x{HEIGHT}+{X}+{Y},
|
|
29
29
|
puts results on stdout unless --ascii given
|
|
@@ -44,7 +44,7 @@ Usage
|
|
|
44
44
|
|
|
45
45
|
# zpng --chunks qr_aux_chunks.png
|
|
46
46
|
|
|
47
|
-
[.] <Chunk #00 IHDR size= 13, crc=36a28ef4,
|
|
47
|
+
[.] <Chunk #00 IHDR size= 13, crc=36a28ef4, width=35, height=35, depth=1, color=0, compression=0, filter=0, interlace=0, idx=0> CRC OK
|
|
48
48
|
[.] <Chunk #01 gAMA size= 4, crc=0bfc6105 > CRC OK
|
|
49
49
|
[.] <Chunk #02 sRGB size= 1, crc=aece1ce9 > CRC OK
|
|
50
50
|
[.] <Chunk #03 cHRM size= 32, crc=9cba513c > CRC OK
|
|
@@ -60,80 +60,81 @@ source image: .ceil] * (img.height/8.0).ceil, # pass1
|
|
11
|
+
[((img.width-4)/8.0).ceil] * (img.height/8.0).ceil, # pass2
|
|
12
|
+
[(img.width/4.0).ceil] * ((img.height-4)/8.0).ceil, # pass3
|
|
13
|
+
[((img.width-2)/4.0).ceil] * (img.height/4.0).ceil, # pass4
|
|
14
|
+
[(img.width/2.0).ceil] * ((img.height-2)/4.0).ceil, # pass5
|
|
15
|
+
[((img.width-1)/2.0).ceil] * (img.height/2.0).ceil, # pass6
|
|
16
|
+
[img.width] * ((img.height-1)/2.0).ceil # pass7
|
|
17
|
+
].map{ |x| x == [0] ? [] : x }
|
|
18
|
+
@scanlines_count = 0
|
|
19
|
+
# two leading zeroes added specially for convert_coords() code readability
|
|
20
|
+
@pass_starts = [0,0] + @widths.map(&:size).map{ |x| @scanlines_count+=x }
|
|
21
|
+
@widths.flatten!
|
|
7
22
|
end
|
|
8
23
|
|
|
9
|
-
def scanlines_count
|
|
10
|
-
(15*image.height/8.0).ceil
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
WIDTHS = [1,1,2,2,2,4,4,4,4,4,4,8,8,8,8]
|
|
14
|
-
|
|
15
24
|
# scanline width in pixels
|
|
16
25
|
def scanline_width idx
|
|
17
|
-
|
|
18
|
-
WIDTHS[idx*8/image.height]*(image.width/8)
|
|
26
|
+
@widths[idx]
|
|
19
27
|
end
|
|
20
28
|
|
|
21
29
|
# scanline size in bytes, INCLUDING leading filter byte
|
|
@@ -36,25 +44,25 @@ module ZPNG
|
|
|
36
44
|
|
|
37
45
|
if y%2 == 1
|
|
38
46
|
# 7th pass: last height/2 full scanlines
|
|
39
|
-
[x, y/2 +
|
|
47
|
+
[x, y/2 + @pass_starts[7]]
|
|
40
48
|
elsif x%2 == 1 && y%2 == 0
|
|
41
49
|
# 6th pass
|
|
42
|
-
[x/2, y/2 +
|
|
50
|
+
[x/2, y/2 + @pass_starts[6]]
|
|
43
51
|
elsif x%8 == 0 && y%8 == 0
|
|
44
|
-
# 1st pass
|
|
52
|
+
# 1st pass, starts at 0
|
|
45
53
|
[x/8, y/8]
|
|
46
54
|
elsif x%8 == 4 && y%8 == 0
|
|
47
55
|
# 2nd pass
|
|
48
|
-
[x/8, y/8 +
|
|
56
|
+
[x/8, y/8 + @pass_starts[2]]
|
|
49
57
|
elsif x%4 == 0 && y%8 == 4
|
|
50
58
|
# 3rd pass
|
|
51
|
-
[x/4, y/8 +
|
|
59
|
+
[x/4, y/8 + @pass_starts[3]]
|
|
52
60
|
elsif x%4 == 2 && y%4 == 0
|
|
53
61
|
# 4th pass
|
|
54
|
-
[x/4, y/4 +
|
|
62
|
+
[x/4, y/4 + @pass_starts[4]]
|
|
55
63
|
elsif x%2 == 0 && y%4 == 2
|
|
56
64
|
# 5th pass
|
|
57
|
-
[x/2, y/4 +
|
|
65
|
+
[x/2, y/4 + @pass_starts[5]]
|
|
58
66
|
else
|
|
59
67
|
raise "invalid coords"
|
|
60
68
|
end
|
data/lib/zpng/chunk.rb
CHANGED
data/lib/zpng/cli.rb
CHANGED
|
@@ -41,7 +41,7 @@ class ZPNG::CLI
|
|
|
41
41
|
opts.on "-E", "--extract-chunk ID", "extract a single chunk" do |id|
|
|
42
42
|
@actions << [:extract_chunk, id.to_i]
|
|
43
43
|
end
|
|
44
|
-
opts.on "-
|
|
44
|
+
opts.on "-D", "--imagedata", "dump unpacked Image Data (IDAT) chunk(s) to stdout" do
|
|
45
45
|
@actions << :unpack_imagedata
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -74,7 +74,10 @@ class ZPNG::CLI
|
|
|
74
74
|
@actions = DEFAULT_ACTIONS if @actions.empty?
|
|
75
75
|
|
|
76
76
|
argv.each_with_index do |fname,idx|
|
|
77
|
-
|
|
77
|
+
if argv.size > 1 && @options[:verbose] >= 0
|
|
78
|
+
puts if idx > 0
|
|
79
|
+
puts "[.] #{fname}".color(:green)
|
|
80
|
+
end
|
|
78
81
|
@file_idx = idx
|
|
79
82
|
@file_name = fname
|
|
80
83
|
|
|
@@ -124,7 +127,7 @@ class ZPNG::CLI
|
|
|
124
127
|
end
|
|
125
128
|
|
|
126
129
|
def info
|
|
127
|
-
puts "[.] image size #{@img.width || '?'}x#{@img.height || '?'}"
|
|
130
|
+
puts "[.] image size #{@img.width || '?'}x#{@img.height || '?'}, bpp=#{@img.bpp}"
|
|
128
131
|
puts "[.] uncompressed imagedata size = #{@img.imagedata.size} bytes"
|
|
129
132
|
puts "[.] palette = #{@img.palette}" if @img.palette
|
|
130
133
|
end
|
|
@@ -148,7 +151,7 @@ class ZPNG::CLI
|
|
|
148
151
|
spc = @options[:wide] ? " " : " "
|
|
149
152
|
@img.height.times do |y|
|
|
150
153
|
@img.width.times do |x|
|
|
151
|
-
print spc.background(@img[x,y].
|
|
154
|
+
print spc.background(@img[x,y].to_ansi)
|
|
152
155
|
end
|
|
153
156
|
puts
|
|
154
157
|
end
|
|
@@ -159,14 +162,26 @@ class ZPNG::CLI
|
|
|
159
162
|
spc = @options[:wide] ? " " : " "
|
|
160
163
|
@img.height.times do |y|
|
|
161
164
|
@img.width.times do |x|
|
|
162
|
-
print spc.background(@img[x,y].
|
|
165
|
+
print spc.background(@img[x,y].to_html)
|
|
163
166
|
end
|
|
164
167
|
puts
|
|
165
168
|
end
|
|
166
169
|
end
|
|
167
170
|
|
|
168
171
|
def scanlines
|
|
169
|
-
|
|
172
|
+
@img.scanlines.each do |sl|
|
|
173
|
+
p sl
|
|
174
|
+
case @options[:verbose]
|
|
175
|
+
when 1
|
|
176
|
+
Hexdump.dump(sl.raw_data)
|
|
177
|
+
when 2
|
|
178
|
+
Hexdump.dump(sl.decoded_bytes)
|
|
179
|
+
when 3..999
|
|
180
|
+
Hexdump.dump(sl.raw_data)
|
|
181
|
+
Hexdump.dump(sl.decoded_bytes)
|
|
182
|
+
puts
|
|
183
|
+
end
|
|
184
|
+
end
|
|
170
185
|
end
|
|
171
186
|
|
|
172
187
|
def palette
|
data/lib/zpng/color.rb
CHANGED
|
@@ -1,28 +1,54 @@
|
|
|
1
1
|
module ZPNG
|
|
2
|
-
class Color
|
|
2
|
+
class Color
|
|
3
|
+
attr_accessor :r, :g, :b, :a
|
|
4
|
+
attr_accessor :depth, :alpha_depth
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
include DeepCopyable
|
|
7
|
+
|
|
8
|
+
def initialize *a
|
|
9
|
+
h = a.last.is_a?(Hash) ? a.pop : {}
|
|
10
|
+
@r,@g,@b,@a = *a
|
|
11
|
+
|
|
12
|
+
# default ALPHA = 0xff - opaque
|
|
13
|
+
@a ||= h[:alpha] || 0xff
|
|
14
|
+
|
|
15
|
+
# default sample depth for r,g,b and alpha = 8 bits
|
|
16
|
+
@depth = h[:depth] || 8
|
|
17
|
+
@alpha_depth = h[:alpha_depth] || @depth
|
|
7
18
|
end
|
|
8
19
|
|
|
9
20
|
alias :alpha :a
|
|
10
|
-
def alpha=
|
|
21
|
+
def alpha= a; @a=a; end
|
|
11
22
|
|
|
12
|
-
BLACK = Color.new(0 , 0, 0
|
|
13
|
-
WHITE = Color.new(255,255,255
|
|
23
|
+
BLACK = Color.new(0 , 0, 0)
|
|
24
|
+
WHITE = Color.new(255,255,255)
|
|
14
25
|
|
|
15
|
-
RED = Color.new(255, 0, 0
|
|
16
|
-
GREEN = Color.new(0 ,255, 0
|
|
17
|
-
BLUE = Color.new(0 , 0,255
|
|
26
|
+
RED = Color.new(255, 0, 0)
|
|
27
|
+
GREEN = Color.new(0 ,255, 0)
|
|
28
|
+
BLUE = Color.new(0 , 0,255)
|
|
18
29
|
|
|
19
|
-
YELLOW= Color.new(255,255, 0
|
|
20
|
-
CYAN = Color.new( 0,255,255
|
|
30
|
+
YELLOW= Color.new(255,255, 0)
|
|
31
|
+
CYAN = Color.new( 0,255,255)
|
|
21
32
|
PURPLE= MAGENTA =
|
|
22
|
-
Color.new(255, 0,255
|
|
33
|
+
Color.new(255, 0,255)
|
|
23
34
|
|
|
24
35
|
ANSI_COLORS = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white]
|
|
25
36
|
|
|
37
|
+
#ASCII_MAP = %q_ .`,-:;~"!<+*^(LJ=?vctsxj12FuoCeyPSah5wVmXA4G9$OR0MQNW#&%@_
|
|
38
|
+
#ASCII_MAP = %q_ .`,-:;~"!<+*^=VXMQNW#&%@_
|
|
39
|
+
#ASCII_MAP = %q_ .,:"!*=7FZVXM#%@_
|
|
40
|
+
|
|
41
|
+
# see misc/gen_ascii_map.rb
|
|
42
|
+
ASCII_MAP =
|
|
43
|
+
[" '''''''```,,",
|
|
44
|
+
",,---:::::;;;;~~\"\"\"\"",
|
|
45
|
+
"\"!!!!!!<++*^^^(((LLJ",
|
|
46
|
+
"=??vvv]ts[j1122FFuoo",
|
|
47
|
+
"CeyyPEah55333VVmmXA4",
|
|
48
|
+
"G9$666666RRRRRR00MQQ",
|
|
49
|
+
"NNW####&&&&&%%%%%%%%",
|
|
50
|
+
"@@@@@@@"].join
|
|
51
|
+
|
|
26
52
|
# euclidian distance - http://en.wikipedia.org/wiki/Euclidean_distance
|
|
27
53
|
def euclidian other_color
|
|
28
54
|
r = (self.r.to_i - other_color.r.to_i)**2
|
|
@@ -31,12 +57,6 @@ module ZPNG
|
|
|
31
57
|
Math.sqrt r
|
|
32
58
|
end
|
|
33
59
|
|
|
34
|
-
def closest_ansi_color
|
|
35
|
-
a = ANSI_COLORS.map{|c| self.class.const_get(c.to_s.upcase) }
|
|
36
|
-
a.map!{ |c| self.euclidian(c) }
|
|
37
|
-
ANSI_COLORS[a.index(a.min)]
|
|
38
|
-
end
|
|
39
|
-
|
|
40
60
|
def white?
|
|
41
61
|
r == 0xff && g == 0xff && b == 0xff
|
|
42
62
|
end
|
|
@@ -53,42 +73,79 @@ module ZPNG
|
|
|
53
73
|
(r+g+b)/3
|
|
54
74
|
end
|
|
55
75
|
|
|
56
|
-
def self.from_grayscale value,
|
|
57
|
-
Color.new value,value,value,
|
|
76
|
+
def self.from_grayscale value, alpha_or_hash = nil
|
|
77
|
+
Color.new value,value,value, alpha_or_hash
|
|
58
78
|
end
|
|
59
79
|
|
|
60
80
|
def to_s
|
|
61
81
|
"%02X%02X%02X" % [r,g,b]
|
|
62
82
|
end
|
|
63
83
|
|
|
84
|
+
########################################################
|
|
85
|
+
|
|
64
86
|
# try to convert to pseudographics
|
|
65
|
-
def to_ascii
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
87
|
+
def to_ascii map=ASCII_MAP
|
|
88
|
+
#p self
|
|
89
|
+
map[self.to_grayscale*(map.size-1)/(2**@depth-1), 1]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def to_ansi
|
|
93
|
+
return to_depth(8).to_ansi if depth != 8
|
|
94
|
+
a = ANSI_COLORS.map{|c| self.class.const_get(c.to_s.upcase) }
|
|
95
|
+
a.map!{ |c| self.euclidian(c) }
|
|
96
|
+
ANSI_COLORS[a.index(a.min)]
|
|
97
|
+
end
|
|
69
98
|
|
|
70
|
-
|
|
99
|
+
def to_css
|
|
100
|
+
return to_depth(8).to_css if depth != 8
|
|
101
|
+
"#%02X%02X%02X" % [r,g,b]
|
|
71
102
|
end
|
|
103
|
+
alias :to_html :to_css
|
|
104
|
+
|
|
105
|
+
########################################################
|
|
72
106
|
|
|
73
107
|
def to_i
|
|
74
108
|
((a||0) << 24) + ((r||0) << 16) + ((g||0) << 8) + (b||0)
|
|
75
109
|
end
|
|
76
110
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
111
|
+
# change bit depth, return new Color
|
|
112
|
+
def to_depth new_depth
|
|
113
|
+
c = Color.new :depth => new_depth
|
|
114
|
+
if new_depth > self.depth
|
|
115
|
+
%w'r g b'.each do |part|
|
|
116
|
+
color = self.send(part)
|
|
117
|
+
if color%2 == 0
|
|
118
|
+
color <<= (new_depth-self.depth)
|
|
119
|
+
else
|
|
120
|
+
(new_depth-self.depth).times{ color = color*2 + 1 }
|
|
121
|
+
end
|
|
122
|
+
c.send("#{part}=", color)
|
|
123
|
+
end
|
|
80
124
|
else
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if a
|
|
85
|
-
# alpha is non-NULL
|
|
86
|
-
"#<ZPNG::Color #%s%s%s a=%d>" % [rs,gs,bs,a]
|
|
87
|
-
else
|
|
88
|
-
# alpha is NULL
|
|
89
|
-
"#<ZPNG::Color #%s%s%s>" % [rs,gs,bs]
|
|
125
|
+
# new_depth < self.depth
|
|
126
|
+
%w'r g b'.each do |part|
|
|
127
|
+
c.send("#{part}=", self.send(part)>>(self.depth-new_depth))
|
|
90
128
|
end
|
|
91
129
|
end
|
|
130
|
+
c
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def inspect
|
|
134
|
+
s = "#<ZPNG::Color"
|
|
135
|
+
if depth == 16
|
|
136
|
+
s << " r=" + (r ? "%04x" % r : "????")
|
|
137
|
+
s << " g=" + (g ? "%04x" % g : "????")
|
|
138
|
+
s << " b=" + (b ? "%04x" % b : "????")
|
|
139
|
+
else
|
|
140
|
+
s << " #"
|
|
141
|
+
s << (r ? "%02x" % r : "??")
|
|
142
|
+
s << (g ? "%02x" % g : "??")
|
|
143
|
+
s << (b ? "%02x" % b : "??")
|
|
144
|
+
end
|
|
145
|
+
s << " a=#{a}" if a && alpha_depth != 0
|
|
146
|
+
s << " depth=#{depth}" if depth != 8
|
|
147
|
+
s << " alpha_depth=#{alpha_depth}" if alpha_depth != 8 && alpha_depth != 0
|
|
148
|
+
s << ">"
|
|
92
149
|
end
|
|
93
150
|
end
|
|
94
151
|
end
|
data/lib/zpng/image.rb
CHANGED
|
@@ -3,6 +3,8 @@ module ZPNG
|
|
|
3
3
|
attr_accessor :data, :header, :chunks, :scanlines, :imagedata, :palette
|
|
4
4
|
alias :hdr :header
|
|
5
5
|
|
|
6
|
+
include DeepCopyable
|
|
7
|
+
|
|
6
8
|
PNG_HDR = "\x89PNG\x0d\x0a\x1a\x0a"
|
|
7
9
|
|
|
8
10
|
def initialize x
|
|
@@ -124,6 +126,10 @@ module ZPNG
|
|
|
124
126
|
@header && @header.interlace != 0
|
|
125
127
|
end
|
|
126
128
|
|
|
129
|
+
def alpha_used?
|
|
130
|
+
@header && @header.alpha_used?
|
|
131
|
+
end
|
|
132
|
+
|
|
127
133
|
def imagedata
|
|
128
134
|
@imagedata ||=
|
|
129
135
|
begin
|
|
@@ -165,12 +171,12 @@ module ZPNG
|
|
|
165
171
|
end
|
|
166
172
|
end
|
|
167
173
|
|
|
168
|
-
def
|
|
174
|
+
def to_ascii *args
|
|
169
175
|
if scanlines.any?
|
|
170
176
|
if interlaced?
|
|
171
|
-
height.times.map{ |y| width.times.map{ |x| self[x,y].to_ascii(
|
|
177
|
+
height.times.map{ |y| width.times.map{ |x| self[x,y].to_ascii(*args) }.join }.join("\n")
|
|
172
178
|
else
|
|
173
|
-
scanlines.map{ |l| l.
|
|
179
|
+
scanlines.map{ |l| l.to_ascii(*args) }.join("\n")
|
|
174
180
|
end
|
|
175
181
|
else
|
|
176
182
|
super()
|
|
@@ -244,8 +250,40 @@ module ZPNG
|
|
|
244
250
|
|
|
245
251
|
# returns new image
|
|
246
252
|
def crop params
|
|
253
|
+
decode_all_scanlines
|
|
247
254
|
# deep copy first, then crop!
|
|
248
|
-
|
|
255
|
+
deep_copy.crop!(params)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def each_pixel &block
|
|
259
|
+
height.times do |y|
|
|
260
|
+
width.times do |x|
|
|
261
|
+
yield(self[x,y], x, y)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# returns new deinterlaced image if deinterlaced
|
|
267
|
+
# OR returns self if no need to deinterlace
|
|
268
|
+
def deinterlace
|
|
269
|
+
return self unless interlaced?
|
|
270
|
+
require 'pp'
|
|
271
|
+
pp chunks
|
|
272
|
+
|
|
273
|
+
# copy all but 'interlace' header params
|
|
274
|
+
h = Hash[*%w'width height depth color compression filter'.map{ |k| [k.to_sym, hdr.send(k)] }.flatten]
|
|
275
|
+
new_img = Image.new h
|
|
276
|
+
chunks.each do |chunk|
|
|
277
|
+
next if chunk.is_a?(Chunk::IHDR)
|
|
278
|
+
next if chunk.is_a?(Chunk::IDAT)
|
|
279
|
+
next if chunk.is_a?(Chunk::IEND)
|
|
280
|
+
new_img.chunks << chunk.deep_copy
|
|
281
|
+
end
|
|
282
|
+
each_pixel do |c,x,y|
|
|
283
|
+
new_img[x,y] = c
|
|
284
|
+
end
|
|
285
|
+
p new_img.scanlines
|
|
286
|
+
new_img
|
|
249
287
|
end
|
|
250
288
|
end
|
|
251
289
|
end
|
data/lib/zpng/scan_line.rb
CHANGED
|
@@ -21,6 +21,7 @@ module ZPNG
|
|
|
21
21
|
if @image.new?
|
|
22
22
|
@decoded_bytes = "\x00" * (size-1)
|
|
23
23
|
@filter = FILTER_NONE
|
|
24
|
+
@offset = idx*size
|
|
24
25
|
else
|
|
25
26
|
@offset =
|
|
26
27
|
if image.interlaced?
|
|
@@ -33,7 +34,6 @@ module ZPNG
|
|
|
33
34
|
else
|
|
34
35
|
STDERR.puts "[!] #{self.class}: ##@idx: no data at pos 0, scanline dropped".red
|
|
35
36
|
end
|
|
36
|
-
@offset += 1
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -67,9 +67,9 @@ module ZPNG
|
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
def
|
|
70
|
+
def to_ascii *args
|
|
71
71
|
@image.width.times.map do |i|
|
|
72
|
-
decode_pixel(i).to_ascii(
|
|
72
|
+
decode_pixel(i).to_ascii(*args)
|
|
73
73
|
end.join
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -142,27 +142,39 @@ module ZPNG
|
|
|
142
142
|
when 8
|
|
143
143
|
[raw.ord, nil]
|
|
144
144
|
when 16
|
|
145
|
-
|
|
145
|
+
if image.alpha_used?
|
|
146
|
+
raw.unpack 'C2'
|
|
147
|
+
else
|
|
148
|
+
# 16-bit grayscale
|
|
149
|
+
raw.unpack 'n'
|
|
150
|
+
end
|
|
146
151
|
when 24
|
|
147
152
|
# RGB
|
|
148
153
|
return Color.new(*raw.unpack('C3'))
|
|
149
154
|
when 32
|
|
150
|
-
|
|
151
|
-
|
|
155
|
+
if image.grayscale? && image.alpha_used?
|
|
156
|
+
# 16-bit grayscale + 16-bit alpha
|
|
157
|
+
raw.unpack 'n2'
|
|
158
|
+
else
|
|
159
|
+
# RGBA
|
|
160
|
+
return Color.new(*raw.unpack('C4'))
|
|
161
|
+
end
|
|
162
|
+
when 48
|
|
163
|
+
# RGB 16 bits per sample
|
|
164
|
+
return Color.new(*raw.unpack('n3'), :depth => 16)
|
|
165
|
+
when 64
|
|
166
|
+
# RGB 16 bits per sample + 16-bit alpha
|
|
167
|
+
return Color.new(*raw.unpack('n4'), :depth => 16, :alpha_depth => 16)
|
|
152
168
|
else
|
|
153
169
|
raise "unexpected bpp #{@bpp}"
|
|
154
170
|
end
|
|
155
171
|
|
|
156
172
|
if image.grayscale?
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
(8-@bpp).times{ color = color*2 + 1 }
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
Color.from_grayscale(color, alpha)
|
|
173
|
+
Color.from_grayscale(color,
|
|
174
|
+
:alpha => alpha,
|
|
175
|
+
:depth => image.hdr.depth,
|
|
176
|
+
:alpha_depth => image.alpha_used? ? image.hdr.depth : 0
|
|
177
|
+
)
|
|
166
178
|
elsif image.palette
|
|
167
179
|
color = image.palette[color]
|
|
168
180
|
color.alpha = alpha
|
|
@@ -173,17 +185,14 @@ module ZPNG
|
|
|
173
185
|
end
|
|
174
186
|
|
|
175
187
|
def decoded_bytes
|
|
176
|
-
raise if caller.size > 50
|
|
188
|
+
#raise if caller.size > 50
|
|
177
189
|
@decoded_bytes ||=
|
|
178
190
|
begin
|
|
179
191
|
# number of bytes per complete pixel, rounding up to one
|
|
180
192
|
bpp1 = (@bpp/8.0).ceil
|
|
181
193
|
|
|
182
|
-
# bytes in one scanline
|
|
183
|
-
nbytes = (image.width*@bpp/8.0).ceil
|
|
184
|
-
|
|
185
194
|
s = ''
|
|
186
|
-
|
|
195
|
+
(size-1).times do |i|
|
|
187
196
|
b0 = (i-bpp1) >= 0 ? s[i-bpp1] : nil
|
|
188
197
|
s[i] = decode_byte(i, b0, bpp1)
|
|
189
198
|
end
|
|
@@ -197,9 +206,13 @@ module ZPNG
|
|
|
197
206
|
true
|
|
198
207
|
end
|
|
199
208
|
|
|
209
|
+
def raw_data
|
|
210
|
+
@offset ? @image.imagedata[@offset, size] : ''
|
|
211
|
+
end
|
|
212
|
+
|
|
200
213
|
private
|
|
201
214
|
def decode_byte x, b0, bpp1
|
|
202
|
-
raw = @image.imagedata[@offset+x]
|
|
215
|
+
raw = @image.imagedata[@offset+x+1]
|
|
203
216
|
|
|
204
217
|
unless raw
|
|
205
218
|
STDERR.puts "[!] #{self.class}: ##@idx: no data at pos #{x}".red
|
data/misc/chars.png
ADDED
|
Binary file
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
$: << "../lib"
|
|
3
|
+
require 'zpng'
|
|
4
|
+
|
|
5
|
+
h = Hash.new{ |k,v| k[v] = "" }
|
|
6
|
+
a = []
|
|
7
|
+
|
|
8
|
+
big_img = ZPNG::Image.load("chars.png").deinterlace
|
|
9
|
+
|
|
10
|
+
(big_img.width/13).times do |idx|
|
|
11
|
+
img = big_img.crop(:x=>idx*13, :y=>0, :width=>13, :height =>big_img.height)
|
|
12
|
+
s = img.to_ascii(' ##')
|
|
13
|
+
puts s
|
|
14
|
+
|
|
15
|
+
c = (idx+32).chr
|
|
16
|
+
next if c == "_"
|
|
17
|
+
n = s.count('#')
|
|
18
|
+
h[n] << c
|
|
19
|
+
a[n] ||= ''
|
|
20
|
+
a[n] << c
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
puts "[.] step1 results:"
|
|
24
|
+
p a
|
|
25
|
+
while a.index(nil)
|
|
26
|
+
prevset = false
|
|
27
|
+
0.upto(a.size-1) do |i|
|
|
28
|
+
c = a[i]
|
|
29
|
+
a[i] = c[0] if c && c.size > 1
|
|
30
|
+
if !c && a[i-1] && !prevset
|
|
31
|
+
a[i] = a[i-1]
|
|
32
|
+
prevset = true
|
|
33
|
+
else
|
|
34
|
+
prevset = false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
(a.size-1).downto(0) do |i|
|
|
38
|
+
c = a[i]
|
|
39
|
+
a[i] = c[0] if c && c.size > 1
|
|
40
|
+
if !c && a[i+1] && !prevset
|
|
41
|
+
a[i] = a[i+1]
|
|
42
|
+
prevset = true
|
|
43
|
+
else
|
|
44
|
+
prevset = false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
puts "[.] normalized:"
|
|
49
|
+
p a
|
|
50
|
+
puts
|
|
51
|
+
|
|
52
|
+
h.keys.sort.each do |n|
|
|
53
|
+
printf "[.] %3d: %s\n", n, h[n]
|
|
54
|
+
end
|
|
55
|
+
puts
|
|
56
|
+
|
|
57
|
+
require 'pp'
|
|
58
|
+
puts "[.] final array:"
|
|
59
|
+
puts "a = ["
|
|
60
|
+
a.each_slice(20).map(&:join).each do |slice|
|
|
61
|
+
puts " #{slice.inspect},"
|
|
62
|
+
end
|
|
63
|
+
puts "].join"
|
data/spec/adam7_spec.rb
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
|
+
|
|
3
|
+
describe ZPNG::Adam7Decoder do
|
|
4
|
+
describe "scanline_width" do
|
|
5
|
+
{
|
|
6
|
+
"8x8" => [1,1,2,2,2],
|
|
7
|
+
"8x16" => [1,1,1,1,2],
|
|
8
|
+
"16x8" => [2,2,4,4,4],
|
|
9
|
+
"16x16"=> [2,2,2,2,4],
|
|
10
|
+
"8x9" => [1,1,1,1,2,2],
|
|
11
|
+
"9x8" => [2,1,3,2,2,5],
|
|
12
|
+
"9x9" => [2,2,1,1,3,2]
|
|
13
|
+
}.each do |dims, slw|
|
|
14
|
+
it "should be right for #{dims} image" do
|
|
15
|
+
w,h = dims.split('x').map(&:to_i)
|
|
16
|
+
img = stub(:width => w, :height => h)
|
|
17
|
+
adam7 = ZPNG::Adam7Decoder.new(img)
|
|
18
|
+
slw.size.times.map{ |i| adam7.scanline_width(i) }.should == slw
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe "scanlines_count" do
|
|
24
|
+
{
|
|
25
|
+
"8x8" => 15,
|
|
26
|
+
"8x16" => 30,
|
|
27
|
+
"16x8" => 15,
|
|
28
|
+
"16x16"=> 30,
|
|
29
|
+
"8x9" => 19,
|
|
30
|
+
"9x8" => 15,
|
|
31
|
+
"9x9" => 19,
|
|
32
|
+
"1x1" => 1,
|
|
33
|
+
}.each do |dims, n|
|
|
34
|
+
it "should be right for #{dims} image" do
|
|
35
|
+
w,h = dims.split('x').map(&:to_i)
|
|
36
|
+
img = stub(:width => w, :height => h)
|
|
37
|
+
adam7 = ZPNG::Adam7Decoder.new(img)
|
|
38
|
+
adam7.scanlines_count.should == n
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe "@pass_starts" do
|
|
44
|
+
{
|
|
45
|
+
"8x8" => [0, 0, 1, 2, 3, 5, 7, 11, 15],
|
|
46
|
+
"8x16" => [0, 0, 2, 4, 6, 10, 14, 22, 30],
|
|
47
|
+
"16x8" => [0, 0, 1, 2, 3, 5, 7, 11, 15],
|
|
48
|
+
"16x16"=> [0, 0, 2, 4, 6, 10, 14, 22, 30],
|
|
49
|
+
"8x9" => [0, 0, 2, 4, 5, 8, 10, 15, 19],
|
|
50
|
+
"9x8" => [0, 0, 1, 2, 3, 5, 7, 11, 15],
|
|
51
|
+
"9x9" => [0, 0, 2, 4, 5, 8, 10, 15, 19]
|
|
52
|
+
}.each do |dims, pst|
|
|
53
|
+
it "should be right for #{dims} image" do
|
|
54
|
+
w,h = dims.split('x').map(&:to_i)
|
|
55
|
+
img = stub(:width => w, :height => h)
|
|
56
|
+
adam7 = ZPNG::Adam7Decoder.new(img)
|
|
57
|
+
adam7.instance_variable_get("@pass_starts").should == pst
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/spec/ascii_spec.rb
CHANGED
|
@@ -45,7 +45,7 @@ describe "ZPNG png2ascii" do
|
|
|
45
45
|
Dir[File.join(SAMPLES_DIR,'qr_*.png')].each do |fname|
|
|
46
46
|
describe fname do
|
|
47
47
|
it "generates a nice ascii img" do
|
|
48
|
-
ZPNG::Image.new(fname).
|
|
48
|
+
ZPNG::Image.new(fname).to_ascii('#.').strip.should == ASCII_QR.strip
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
end
|
data/spec/color_spec.rb
CHANGED
|
@@ -5,7 +5,36 @@ describe ZPNG::Color do
|
|
|
5
5
|
ZPNG::Color::ANSI_COLORS.each do |color_sym|
|
|
6
6
|
it "finds closest color for #{color_sym}" do
|
|
7
7
|
color = ZPNG::Color.const_get(color_sym.to_s.upcase)
|
|
8
|
-
color.
|
|
8
|
+
color.to_ansi.should == color_sym
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe "to_depth" do
|
|
13
|
+
it "decreases color depth" do
|
|
14
|
+
c = ZPNG::Color.new 0x10, 0x20, 0x30
|
|
15
|
+
c = c.to_depth(4)
|
|
16
|
+
c.depth.should == 4
|
|
17
|
+
c.r.should == 1
|
|
18
|
+
c.g.should == 2
|
|
19
|
+
c.b.should == 3
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "increases color depth" do
|
|
23
|
+
c = ZPNG::Color.new 0,2,3, :depth => 4
|
|
24
|
+
c = c.to_depth(8)
|
|
25
|
+
c.depth.should == 8
|
|
26
|
+
c.r.should == 0
|
|
27
|
+
c.g.should == 0x20
|
|
28
|
+
c.b.should == 0x3f
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "keeps color depth" do
|
|
32
|
+
c = ZPNG::Color.new 0x11, 0x22, 0x33
|
|
33
|
+
c = c.to_depth(8)
|
|
34
|
+
c.depth.should == 8
|
|
35
|
+
c.r.should == 0x11
|
|
36
|
+
c.g.should == 0x22
|
|
37
|
+
c.b.should == 0x33
|
|
9
38
|
end
|
|
10
39
|
end
|
|
11
40
|
end
|
data/spec/create_image_spec.rb
CHANGED
|
@@ -12,7 +12,7 @@ describe Image do
|
|
|
12
12
|
describe "new( :bpp => #{bpp}, :color => #{color} )" do
|
|
13
13
|
subject(:img){ _new_img(bpp,color) }
|
|
14
14
|
it("should export"){ img.export.should start_with(Image::PNG_HDR) }
|
|
15
|
-
it("should
|
|
15
|
+
it("should to_ascii") { img.to_ascii.split("\n").size.should == 8 }
|
|
16
16
|
|
|
17
17
|
subject{ img.hdr }
|
|
18
18
|
its(:depth) { should == bpp }
|
|
@@ -24,7 +24,7 @@ describe Image do
|
|
|
24
24
|
describe "new( :bpp => 16, :color => false )" do
|
|
25
25
|
subject(:img){ _new_img(16,false) }
|
|
26
26
|
it("should export"){ img.export.should start_with(Image::PNG_HDR) }
|
|
27
|
-
it("should
|
|
27
|
+
it("should to_ascii") { img.to_ascii.split("\n").size.should == 8 }
|
|
28
28
|
|
|
29
29
|
subject{ img.hdr }
|
|
30
30
|
its(:depth) { should == 8 } # 8 bits per color + 8 per alpha = 16 bpp
|
|
@@ -39,7 +39,7 @@ describe Image do
|
|
|
39
39
|
describe "new( :bpp => 24, :color => false )" do
|
|
40
40
|
subject(:img){ _new_img(24,false) }
|
|
41
41
|
it("should export"){ img.export.should start_with(Image::PNG_HDR) }
|
|
42
|
-
it("should
|
|
42
|
+
it("should to_ascii") { img.to_ascii.split("\n").size.should == 8 }
|
|
43
43
|
|
|
44
44
|
subject{ img.hdr }
|
|
45
45
|
its(:depth) { should == 8 } # each channel depth = 8
|
|
@@ -49,7 +49,7 @@ describe Image do
|
|
|
49
49
|
describe "new( :bpp => 24, :color => true )" do
|
|
50
50
|
subject(:img){ _new_img(24,true) }
|
|
51
51
|
it("should export"){ img.export.should start_with(Image::PNG_HDR) }
|
|
52
|
-
it("should
|
|
52
|
+
it("should to_ascii") { img.to_ascii.split("\n").size.should == 8 }
|
|
53
53
|
|
|
54
54
|
subject{ img.hdr }
|
|
55
55
|
its(:depth) { should == 8 } # each channel depth = 8
|
|
@@ -59,7 +59,7 @@ describe Image do
|
|
|
59
59
|
describe "new( :bpp => 32, :color => false )" do
|
|
60
60
|
subject(:img){ _new_img(32,false) }
|
|
61
61
|
it("should export"){ img.export.should start_with(Image::PNG_HDR) }
|
|
62
|
-
it("should
|
|
62
|
+
it("should to_ascii") { img.to_ascii.split("\n").size.should == 8 }
|
|
63
63
|
|
|
64
64
|
subject{ img.hdr }
|
|
65
65
|
its(:depth) { should == 8 } # each channel depth = 8
|
|
@@ -69,7 +69,7 @@ describe Image do
|
|
|
69
69
|
describe "new( :bpp => 32, :color => true )" do
|
|
70
70
|
subject(:img){ _new_img(32,true) }
|
|
71
71
|
it("should export"){ img.export.should start_with(Image::PNG_HDR) }
|
|
72
|
-
it("should
|
|
72
|
+
it("should to_ascii") { img.to_ascii.split("\n").size.should == 8 }
|
|
73
73
|
|
|
74
74
|
subject{ img.hdr }
|
|
75
75
|
its(:depth) { should == 8 } # each channel depth = 8
|
data/spec/crop_spec.rb
CHANGED
|
@@ -69,24 +69,24 @@ describe Image do
|
|
|
69
69
|
|
|
70
70
|
it "should extract left square" do
|
|
71
71
|
img.crop! :x => 1, :y => 1, :width => 7, :height => 7
|
|
72
|
-
img.
|
|
72
|
+
img.to_ascii('#.').strip.should == QR_SQUARE.strip
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
it "should extract right square" do
|
|
76
76
|
img.crop! :x => 27, :y => 1, :width => 7, :height => 7
|
|
77
|
-
img.
|
|
77
|
+
img.to_ascii('#.').strip.should == QR_SQUARE.strip
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
it "should extract bottom square" do
|
|
81
81
|
img.crop! :x => 1, :y => 27, :width => 7, :height => 7
|
|
82
|
-
img.
|
|
82
|
+
img.to_ascii('#.').strip.should == QR_SQUARE.strip
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
it "keeps whole original image if crop is larger than image" do
|
|
86
86
|
img2 = img.crop :x => 0, :y => 0, :width => 7000, :height => 7000
|
|
87
87
|
img2.width.should == img.width
|
|
88
88
|
img2.height.should == img.height
|
|
89
|
-
img2.
|
|
89
|
+
img2.to_ascii.should == img.to_ascii
|
|
90
90
|
end
|
|
91
91
|
end
|
|
92
92
|
end
|
data/spec/modify_spec.rb
CHANGED
|
@@ -49,7 +49,7 @@ describe "ZPNG modify" do
|
|
|
49
49
|
img.width.times do |x|
|
|
50
50
|
img[x,0] = (x%2==0) ? ZPNG::Color::WHITE : ZPNG::Color::BLACK
|
|
51
51
|
end
|
|
52
|
-
img.
|
|
52
|
+
img.to_ascii('#.').strip.should == ASCII_MODIFIED_QR.strip
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
end
|
data/spec/running_pixel_spec.rb
CHANGED
data/zpng.gemspec
CHANGED
|
@@ -5,17 +5,18 @@
|
|
|
5
5
|
|
|
6
6
|
Gem::Specification.new do |s|
|
|
7
7
|
s.name = "zpng"
|
|
8
|
-
s.version = "0.
|
|
8
|
+
s.version = "0.2.0"
|
|
9
9
|
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
11
|
s.authors = ["Andrey \"Zed\" Zaikin"]
|
|
12
|
-
s.date = "2012-12-
|
|
12
|
+
s.date = "2012-12-23"
|
|
13
13
|
s.email = "zed.0xff@gmail.com"
|
|
14
14
|
s.executables = ["zpng"]
|
|
15
15
|
s.extra_rdoc_files = [
|
|
16
16
|
"LICENSE.txt",
|
|
17
17
|
"README.md",
|
|
18
|
-
"README.md.tpl"
|
|
18
|
+
"README.md.tpl",
|
|
19
|
+
"TODO"
|
|
19
20
|
]
|
|
20
21
|
s.files = [
|
|
21
22
|
".document",
|
|
@@ -26,6 +27,7 @@ Gem::Specification.new do |s|
|
|
|
26
27
|
"README.md",
|
|
27
28
|
"README.md.tpl",
|
|
28
29
|
"Rakefile",
|
|
30
|
+
"TODO",
|
|
29
31
|
"VERSION",
|
|
30
32
|
"bin/zpng",
|
|
31
33
|
"lib/zpng.rb",
|
|
@@ -34,9 +36,12 @@ Gem::Specification.new do |s|
|
|
|
34
36
|
"lib/zpng/chunk.rb",
|
|
35
37
|
"lib/zpng/cli.rb",
|
|
36
38
|
"lib/zpng/color.rb",
|
|
39
|
+
"lib/zpng/deep_copyable.rb",
|
|
37
40
|
"lib/zpng/image.rb",
|
|
38
41
|
"lib/zpng/scan_line.rb",
|
|
39
42
|
"lib/zpng/string_ext.rb",
|
|
43
|
+
"misc/chars.png",
|
|
44
|
+
"misc/gen_ascii_map.rb",
|
|
40
45
|
"samples/captcha_4bpp.png",
|
|
41
46
|
"samples/modify.rb",
|
|
42
47
|
"samples/qr_aux_chunks.png",
|
|
@@ -47,6 +52,7 @@ Gem::Specification.new do |s|
|
|
|
47
52
|
"samples/qr_plte_bw.png",
|
|
48
53
|
"samples/qr_rgb.png",
|
|
49
54
|
"samples/qr_rgba.png",
|
|
55
|
+
"spec/adam7_spec.rb",
|
|
50
56
|
"spec/ascii_spec.rb",
|
|
51
57
|
"spec/color_spec.rb",
|
|
52
58
|
"spec/create_image_spec.rb",
|
metadata
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
name: zpng
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease:
|
|
5
|
-
version: 0.
|
|
5
|
+
version: 0.2.0
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
8
8
|
- Andrey "Zed" Zaikin
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-12-
|
|
12
|
+
date: 2012-12-23 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
prerelease: false
|
|
@@ -100,6 +100,7 @@ extra_rdoc_files:
|
|
|
100
100
|
- LICENSE.txt
|
|
101
101
|
- README.md
|
|
102
102
|
- README.md.tpl
|
|
103
|
+
- TODO
|
|
103
104
|
files:
|
|
104
105
|
- .document
|
|
105
106
|
- .rspec
|
|
@@ -109,6 +110,7 @@ files:
|
|
|
109
110
|
- README.md
|
|
110
111
|
- README.md.tpl
|
|
111
112
|
- Rakefile
|
|
113
|
+
- TODO
|
|
112
114
|
- VERSION
|
|
113
115
|
- bin/zpng
|
|
114
116
|
- lib/zpng.rb
|
|
@@ -117,9 +119,12 @@ files:
|
|
|
117
119
|
- lib/zpng/chunk.rb
|
|
118
120
|
- lib/zpng/cli.rb
|
|
119
121
|
- lib/zpng/color.rb
|
|
122
|
+
- lib/zpng/deep_copyable.rb
|
|
120
123
|
- lib/zpng/image.rb
|
|
121
124
|
- lib/zpng/scan_line.rb
|
|
122
125
|
- lib/zpng/string_ext.rb
|
|
126
|
+
- misc/chars.png
|
|
127
|
+
- misc/gen_ascii_map.rb
|
|
123
128
|
- samples/captcha_4bpp.png
|
|
124
129
|
- samples/modify.rb
|
|
125
130
|
- samples/qr_aux_chunks.png
|
|
@@ -130,6 +135,7 @@ files:
|
|
|
130
135
|
- samples/qr_plte_bw.png
|
|
131
136
|
- samples/qr_rgb.png
|
|
132
137
|
- samples/qr_rgba.png
|
|
138
|
+
- spec/adam7_spec.rb
|
|
133
139
|
- spec/ascii_spec.rb
|
|
134
140
|
- spec/color_spec.rb
|
|
135
141
|
- spec/create_image_spec.rb
|
|
@@ -154,7 +160,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
154
160
|
version: '0'
|
|
155
161
|
segments:
|
|
156
162
|
- 0
|
|
157
|
-
hash: -
|
|
163
|
+
hash: -93398394471482814
|
|
158
164
|
none: false
|
|
159
165
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
160
166
|
requirements:
|