rbimg 0.1.3 → 0.1.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: 8777c22553d8cd7837e91cd6256a52f2ea1d5a97c56335b8c3ce5568068ecd86
4
- data.tar.gz: 9e88203b31a329c63774bb0c8150d6dbddf83d56de6117dac8100a6538b4cea8
3
+ metadata.gz: 866ea03a52665264e1685f5c058c86d757bd172302eb81a3ce0eeeb92f6b16e0
4
+ data.tar.gz: 34f65d90480aa2b55f49e0931dea21edaeb990b8ed46fc44e77b4cf144746277
5
5
  SHA512:
6
- metadata.gz: b65faba907fcb333d95d142ef55c6dfb37ac43e827320775f386725dc92a70817e02ed1d8208985095b771db155351de094be99cc9bc93be2ffcaa427a915443
7
- data.tar.gz: 5978647adb099633d64aa03394382d075b12e86022241a52bf736c1546c85f47a87383e342b2f1da436b6009de1642ca82a5cf2f415e799828177e9dacb06c30
6
+ metadata.gz: 32f94fd46bc3f7a63671364a3284efcd014e8801079a5453b1371554a8030965f45b63c09342cc103f5fc5bda981dc90eb2b2570d0d9b812a1284bc2e1889063
7
+ data.tar.gz: 630f3b9e6e68425badbb430c61e870b6d9a4ff0da74595ef27a1cfabdd54a7fa03d2646f0a03d4abbff48e38eda6dd104ce6893d8f3d610f83bddc2756fd5da0
data/Gemfile CHANGED
@@ -7,4 +7,4 @@ gem "rake", "~> 12.0"
7
7
  gem "rspec", "~> 3.0"
8
8
  gem 'gspec', '~> 0.1.3', group: :development
9
9
  gem "pry", "~> 0.13.1", group: :development
10
- gem "byteman", "~> 0.1.0"
10
+ gem "byteman", "~> 0.1.1"
@@ -1,15 +1,15 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbimg (0.1.3)
4
+ rbimg (0.1.4)
5
5
  byteman (~> 0.1.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- byteman (0.1.0)
10
+ byteman (0.1.1)
11
11
  coderay (1.1.3)
12
- diff-lcs (1.4.2)
12
+ diff-lcs (1.4.4)
13
13
  gspec (0.1.3)
14
14
  method_source (1.0.0)
15
15
  pry (0.13.1)
@@ -34,7 +34,7 @@ PLATFORMS
34
34
  ruby
35
35
 
36
36
  DEPENDENCIES
37
- byteman (~> 0.1.0)
37
+ byteman (~> 0.1.1)
38
38
  gspec (~> 0.1.3)
39
39
  pry (~> 0.13.1)
40
40
  rake (~> 12.0)
data/README.md CHANGED
@@ -4,7 +4,15 @@ Allows simple creation and reading of images (currently only PNG images).
4
4
 
5
5
  Supports all required chunk types (IHDR, PLTE, IDAT, IEND)
6
6
 
7
- Filtering is not currently supported.
7
+ All filtering methods for individual scanlines are supported for reading PNG images, but it will always write with a filtering method of 0 (so re-writing may give you a different format than the original image)
8
+
9
+ All bit depths for PNG images are supported. See PNG documentation or source code for specifics (ArgumentError thrown if an incorrect bit depth is used for a specific color type)
10
+
11
+ All PNG color types are supported: greyscale, greyscale with alpha, rgb, rgba, and palette. All with their respective bit_depth sizes.
12
+
13
+ As of now this gem will be able to retrieve the correct pixel information for all PNG images except for those with an interlace method other than 0, which, as far as I can tell, are most PNG images.
14
+
15
+ Non-required chunk types are not currently supported.
8
16
 
9
17
  This gem is primarily for simple manipulation of images using pixel data.
10
18
 
@@ -67,7 +75,7 @@ end
67
75
  png = Rbimg::PNG.new(pixels: pixels.flatten, type: :greyscale_alpha, width: 300, height: 500)
68
76
  png.write(path: './greyscale_alpha')
69
77
  ```
70
- Write a greyscale image from the MSNT dataset:
78
+ Write a greyscale image from the MNIST dataset:
71
79
 
72
80
  ```ruby
73
81
  img_data = File.read("./data/t10k-images-idx3-ubyte").bytes
@@ -77,7 +85,7 @@ num_rows = img_data[8...12]
77
85
  num_cols = img_data[12...16]
78
86
  pixels = img_data[16...(16 + 784)]
79
87
  png = Rbimg::PNG.new(pixels: pixels, type: :greyscale, width: 28, height: 28)
80
- png.write(path: 'mnst_test')
88
+ png.write(path: 'mnist_test')
81
89
  ```
82
90
 
83
91
  Read a PNG image and get the pixel array from a path
@@ -95,6 +103,22 @@ png = Rbimg::PNG.read(data: data)
95
103
  puts png.pixels
96
104
  ```
97
105
 
106
+ Write a 16-bit-depth rgba image with random colors and alpha:
107
+
108
+ ```ruby
109
+ height = 140
110
+ width = 123
111
+ r = Random.new
112
+ pixels = Array.new(3 * width * height) do |i|
113
+ r.rand(2 ** 16)
114
+ end
115
+
116
+ png = Rbimg::PNG.new(pixels: pixels, type: :rgb, width: width, height: height, bit_depth: 16)
117
+ png.write(path: "./rgba_16bit")
118
+ ```
119
+
120
+ Combine images in rows or columns:
121
+
98
122
 
99
123
 
100
124
  ## Installation
@@ -113,9 +137,7 @@ Or install it yourself as:
113
137
 
114
138
  $ gem install rbimg
115
139
 
116
- ## Usage
117
140
 
118
- TODO: Write usage instructions here
119
141
 
120
142
  ## Development
121
143
 
@@ -125,6 +147,9 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
125
147
 
126
148
  ## Contributing
127
149
 
150
+ The feature that will make this gem fully-functional for all PNG images is the implementation of interlaced PNGs. Any help is welcomed. This gem was developed using the documentation found and libpng.org/png/spec/1.2
151
+ Information about interlaced PNGs can be found there.
152
+
128
153
  Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rbimg. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/rbimg/blob/master/CODE_OF_CONDUCT.md).
129
154
 
130
155
 
data/TODO.md ADDED
@@ -0,0 +1,7 @@
1
+ # TODO
2
+
3
+ - Convert between image types
4
+ - Allow image combination of different types and sizes
5
+ - Allow image scaling
6
+ - Quantize images to different bit_depths
7
+ - Support interlace PNGs
@@ -15,17 +15,109 @@ class Rbimg::PNG
15
15
  rgba: 6
16
16
  }
17
17
 
18
+ def self.combine(*images, divider: nil, as: :row)
19
+ raise ArgumentError.new("as: must be :row or :col") if as != :row && as != :col
20
+ raise ArgumentError.new("Images and divider must all be an Rbimg::PNG") if !images.all?{|i| i.is_a?(Rbimg::PNG)}
21
+ type = images.first.type
22
+ height = images.first.height
23
+ width = images.first.width
24
+ bit_depth = images.first.bit_depth
25
+
26
+ color_type = COLOR_TYPES[type]
27
+
28
+ logical_pixel_width = width * Rbimg::PNG.pixel_size_for(color_type: color_type)
29
+
30
+ if divider
31
+ width_multiplier = logical_pixel_width / width
32
+ divider_width = divider.width * width_multiplier
33
+ if as == :row
34
+ raise ArgumentError.new("divider must have the same height as images if aligning as a row") if divider.height != height
35
+ elsif as == :col
36
+ raise ArgumentError.new("divider must have the same width as images if aligning as a column") if divider.width != width
37
+ end
38
+ raise ArgumentError.new("divider must have the same type and bit_depth as images") if divider.type != type || divider.bit_depth != bit_depth
39
+ end
40
+
41
+ images.each do |i|
42
+ if i.type != type || i.height != height || i.width != width || i.bit_depth != bit_depth
43
+ raise ArgumentError.new("Currently all images must have the same type, height, width, and bit_depth to be combined")
44
+ end
45
+ end
46
+
47
+
48
+ if as == :row
49
+ new_width = images.length * width
50
+ new_height = height
51
+
52
+ if divider
53
+ new_width += (divider.width * (images.length - 1))
54
+ end
55
+
56
+ new_pixels = height.times.map do |row|
57
+ row_start = row * logical_pixel_width
58
+ divider_row_start = row * divider_width if divider
59
+ images.map do |img|
60
+ row_pixels = img.pixels[row_start...(row_start + logical_pixel_width)]
61
+ ((img == images.last) || divider.nil?) ? row_pixels : row_pixels + divider.pixels[divider_row_start...(divider_row_start + divider_width)]
62
+ end
63
+ end.flatten
64
+
65
+ else
66
+ new_width = width
67
+ new_height = images.length * height
68
+ if divider
69
+ new_height += (divider.height * (images.length - 1))
70
+ end
71
+
72
+ new_pixels = images.map do |img|
73
+ img_pixels = img.pixels
74
+ if divider && img != images.last
75
+ img_pixels + divider.pixels
76
+ else
77
+ img_pixels
78
+ end
79
+ end.flatten
80
+ end
81
+
82
+ begin
83
+ new_img = Rbimg::PNG.new(pixels: new_pixels, type: type, width: new_width, height: new_height, bit_depth: bit_depth)
84
+ rescue
85
+ binding.pry
86
+ end
87
+
88
+ end
89
+
90
+ def self.pixel_size_for(color_type:)
91
+ case color_type
92
+ when 0
93
+ 1
94
+ when 2
95
+ 3
96
+ when 3
97
+ 1
98
+ when 4
99
+ 2
100
+ when 6
101
+ 4
102
+ else
103
+ raise ArgumentError.new("#{color_type} is not a valid color type. Must be 0,2,3,4, or 6")
104
+ end
105
+ end
106
+
18
107
  def self.read(path: nil, data: nil)
19
108
 
20
109
  raise ArgumentError.new(".read must be initialized with a path or a datastream") if (path.nil? && data.nil?) || (!path.nil? && !data.nil?)
21
110
  raise ArgumentError.new("data must be an array of byte integers or a byte string") if data && !data.is_a?(Array) && !data.is_a?(String)
22
111
  raise ArgumentError.new("data must be an array of byte integers or a byte string") if data && data.is_a?(Array) && !data.first.is_a?(Integer)
112
+ path += ".png" if path && !path.end_with?('.png')
23
113
  begin
114
+
24
115
  if path
25
116
  data = File.read(path).bytes
26
117
  else
27
118
  data = data.bytes if data.is_a?(String)
28
119
  end
120
+
29
121
  chunk_start = 8
30
122
  chunks = []
31
123
  loop do
@@ -46,6 +138,7 @@ class Rbimg::PNG
46
138
  end
47
139
 
48
140
  chunk_start = chunk_end
141
+ #TODO: Make sure last chunk is IEND
49
142
  break if chunk_end >= data.length - 1
50
143
 
51
144
  end
@@ -57,40 +150,125 @@ class Rbimg::PNG
57
150
  compression_method = chunks.first[:compression_method]
58
151
  filter_method = chunks.first[:filter_method]
59
152
  interlace_method = chunks.first[:interlace_method]
153
+
60
154
  all_idats = chunks.filter{ |c| c.is_a?(Hash) && c[:type] == "IDAT" }
61
- compressed_pixels = all_idats.reduce([]){ |mem, idat| mem + idat[:compressed_pixels] }
155
+ compressed_pixels = all_idats.reduce([]) { |mem, idat| mem + idat[:compressed_pixels] }
62
156
  pixels_and_filter = Zlib::Inflate.inflate(compressed_pixels.pack("C*")).unpack("C*")
157
+
63
158
 
64
- case color_type
65
- when 0
66
- pixel_width = width
67
- when 2
68
- pixel_width = width * 3
69
- when 3
70
- pixel_width = width
71
- when 4
72
- pixel_width = width * 2
73
- when 6
74
- pixel_width = width * 4
159
+ logical_pixel_width = Rbimg::PNG.pixel_size_for(color_type: color_type) * width
160
+
161
+ pixel_width = (logical_pixel_width * (bit_depth / 8.0)).ceil
162
+
163
+
164
+ scanline_filters = Array.new(height, nil)
165
+ pixels = Array.new(pixels_and_filter.length - height, nil)
166
+
167
+ pixels_and_filter.each.with_index do |pixel,i|
168
+ scanline = i / (pixel_width + 1)
169
+ pixel_number = (i % (pixel_width + 1)) - 1
170
+ pixel_loc = scanline * pixel_width + pixel_number
171
+ if (pixel_number == -1)
172
+ scanline_filters[scanline] = pixel
173
+ else
174
+ case scanline_filters[scanline]
175
+ when 0
176
+ pixels[pixel_loc] = pixel
177
+ when 1
178
+ x = pixel_number
179
+ bpp = (pixel_width / width) * (bit_depth / 8.0).ceil
180
+ prev_raw = x - bpp < 0 ? 0 : pixels[pixel_loc - bpp]
181
+ new_pixel = (pixel + prev_raw) % 256
182
+ pixels[pixel_loc] = new_pixel
183
+ when 2
184
+ x = pixel_number
185
+ prev_raw = scanline == 0 ? 0 : pixels[pixel_loc - pixel_width]
186
+ new_pixel = (pixel + prev_raw) % 256
187
+ pixels[pixel_loc] = new_pixel
188
+ when 3
189
+ x = pixel_number
190
+ bpp = (pixel_width / width) * (bit_depth / 8.0).ceil
191
+ left_pix = x - bpp < 0 ? 0 : pixels[pixel_loc - bpp]
192
+ above_pix = scanline == 0 ? 0 : pixels[pixel_loc - pixel_width]
193
+ new_pixel = (pixel + ((left_pix + above_pix) / 2).floor) % 256
194
+ pixels[pixel_loc] = new_pixel
195
+ when 4
196
+ x = pixel_number
197
+ bpp = (pixel_width / width) * (bit_depth / 8.0).ceil
198
+
199
+ paeth_predictor = Proc.new do |left, above, upper_left|
200
+ p = left + above - upper_left
201
+ pa = (p - left).abs
202
+ pb = (p - above).abs
203
+ pc = (p - upper_left).abs
204
+ if pa <= pb && pa <= pc
205
+ left
206
+ elsif pb <= pc
207
+ above
208
+ else
209
+ upper_left
210
+ end
211
+ end
212
+
213
+ left_pix = x - bpp < 0 ? 0 : pixels[pixel_loc - bpp]
214
+ above_pix = scanline == 0 ? 0 : pixels[pixel_loc - pixel_width]
215
+ upper_left_pix = scanline == 0 ? 0 : x - bpp < 0 ? 0 : pixels[pixel_loc - pixel_width - bpp]
216
+ pp_out = paeth_predictor[left_pix, above_pix, upper_left_pix]
217
+ new_pixel = (pixel + pp_out) % 256
218
+ pixels[pixel_loc] = new_pixel
219
+ else
220
+ raise Rbimg::FormatError.new("Incorrect Filtering type used on this PNG file")
221
+ end
222
+ end
223
+ end
224
+
225
+
226
+
227
+
228
+ if bit_depth != 8
229
+ corrected_pixels = Array.new(logical_pixel_width * height, nil)
230
+ height.times do |row_num|
231
+ row_start = row_num * pixel_width
232
+ row_end = row_start + pixel_width
233
+ row_data = pixels[row_start...row_end]
234
+ binary_data = Byteman.buf2int(row_data).to_s(2)
235
+ pad_size = ((logical_pixel_width * bit_depth) / 8.0).ceil * 8
236
+ binary_data = Byteman.pad(num: binary_data, len: pad_size, type: :bits)
237
+ corrected_row_start = row_num * logical_pixel_width
238
+ logical_pixel_width.times do |pixel_num_in_row|
239
+ binary_segment_start = pixel_num_in_row * bit_depth
240
+ binary_segment_end = binary_segment_start + bit_depth
241
+ binary_segment = binary_data[binary_segment_start...binary_segment_end]
242
+ logical_pixel_value = binary_segment.to_i(2)
243
+ corrected_pixel_location = corrected_row_start + pixel_num_in_row
244
+ corrected_pixels[corrected_pixel_location] = logical_pixel_value
245
+ end
246
+ end
75
247
  else
76
- raise ArgumentError.new("#{color_type} is not a valid color type. Must be 0,2,3,4, or 6")
248
+ corrected_pixels = pixels
77
249
  end
78
- pixels = pixels_and_filter.filter.with_index{ |_,i| i % (pixel_width + 1) != 0}
79
- args = {pixels: pixels, type: color_type, width: width, height: height, bit_depth: bit_depth}
250
+
251
+ args = {pixels: corrected_pixels, type: color_type, width: width, height: height, bit_depth: bit_depth}
80
252
  plte = chunks.find{|c| c[:type] == "PLTE" unless c.is_a?(Array)}
81
253
  args[:palette] = plte[:chunk_data] if plte
82
254
  new(**args)
83
- rescue
84
- raise Rbimg::FormatError.new("This PNG file is not in the correct format or has been corrupted")
255
+ rescue Errno::ENOENT => e
256
+ raise ArgumentError.new("Invalid path #{path}")
257
+ rescue => e
258
+ raise e if e.is_a?(Rbimg::FormatError)
259
+ raise Rbimg::FormatError.new("This PNG file is not in the correct format or has been corrupted :)")
85
260
  end
86
261
  end
87
262
 
263
+
88
264
 
89
265
  attr_reader :pixels, :width, :height, :bit_depth, :compression_method, :filter_method, :interlace_method
90
-
91
- def initialize(pixels: nil, type: nil, width: , height: , bit_depth: 8, compression_method: 0, filter_method: 0, interlace_method: 0, palette: nil)
266
+ attr_reader :pixel_size
267
+ def initialize(pixels: nil, type: nil, width: nil, height: nil, bit_depth: 8, compression_method: 0, filter_method: 0, interlace_method: 0, palette: nil)
92
268
  @pixels, @width, @height, @compression_method, @filter_method, @interlace_method = pixels, width, height, compression_method, filter_method, interlace_method
93
269
  @bit_depth = bit_depth
270
+ type = :greyscale if type.nil?
271
+
94
272
  @type = type.is_a?(Integer) ? type : COLOR_TYPES[type]
95
273
  raise ArgumentError.new("#{type} is not a valid color type. Please use one of: #{COLOR_TYPES.keys}") if type.nil?
96
274
  raise ArgumentError.new("Palettes are not compatible with color types 0 and 4") if palette && (@type == 0 || @type == 4)
@@ -117,8 +295,25 @@ class Rbimg::PNG
117
295
  ]
118
296
  @chunks.insert(1, Chunk.PLTE(palette)) if !palette.nil?
119
297
 
298
+ @pixel_size = Rbimg::PNG.pixel_size_for(color_type: @type)
299
+
300
+ end
301
+
302
+ def pixel(num)
303
+ start = num * @pixel_size
304
+ pend = start + @pixel_size
305
+ pixels[start...pend]
120
306
  end
121
307
 
308
+ def row(rownum)
309
+ return nil if rownum > self.height
310
+ pix_width = pixel_size * width
311
+ start = rownum * pix_width
312
+ pend = start + pix_width
313
+ pixels[start...pend]
314
+ end
315
+
316
+
122
317
  def type
123
318
  COLOR_TYPES.each do |k,v|
124
319
  return k if v == @type
@@ -134,6 +329,8 @@ class Rbimg::PNG
134
329
  File.write(path + postscript, bytes)
135
330
  end
136
331
 
332
+
333
+
137
334
  private
138
335
 
139
336
  def all_data
@@ -196,7 +393,7 @@ class Rbimg::PNG
196
393
  test_length(width)
197
394
  test_length(height)
198
395
  raise ArgumentError.new('Color code types must be 0, 2, 3, 4, or 6') if ![0,2,3,4,6].include?(color_type)
199
- raise ArgumentError.new('Bit depth must be related to color_type as such: #{bit_depth_rules}') if !bit_depth_rules[color_type].include?(bit_depth)
396
+ raise ArgumentError.new("Bit depth must be related to color_type as such: color_value => bit_depth_options: #{bit_depth_rules}") if !bit_depth_rules[color_type].include?(bit_depth)
200
397
 
201
398
 
202
399
  wbytes = Byteman.pad(len: 4, num: Byteman.int2buf(width))
@@ -247,21 +444,35 @@ class Rbimg::PNG
247
444
 
248
445
  case bit_depth
249
446
  when 1
250
- [0] + Byteman.hex(bit_strm.map{|b| b.to_s(2)}.join('').to_i(2))
447
+ raise ArgumentError.new("If bit depth is 1, all pixel values must be a 1 or 0") if bit_strm.any?{ |b| b != 0 && b != 1 }
448
+ bits = bit_strm.join('') + ("0" * ((-1 * bit_strm.length % 8) % 8 ))
449
+ Byteman.hex(0) + Byteman.pad(num: Byteman.hex(bits.to_i(2)), len: bits.length / 8)
251
450
  when 2
252
- [0] + Byteman.hex(bit_strm.map{|b| Byteman.pad(num: b.to_s(2), len: 2)}.join('').to_i(2))
253
- when 4
254
- [0] + Byteman.hex(bit_strm.map{|b| Byteman.pad(num: b.to_s(2), len: 4)}.join('').to_i(2))
451
+ bits = bit_strm.map do |b|
452
+ raise ArgumentError.new("If bit depth is 2, all pixel values must be between 0 and 3") if !b.between?(0,3)
453
+ Byteman.pad(num: b, len: 2, type: :bits)
454
+ end.join('')
455
+ padded_bits = bits + ("0" * ((-1 * bit_strm.length * 2) % 8) % 8)
456
+ Byteman.hex(0) + Byteman.pad(num: Byteman.hex(padded_bits.to_i(2)), len: padded_bits.length / 8)
457
+ when 4
458
+ bits = bit_strm.map do |b|
459
+ raise ArgumentError.new("If bit depth is 4, all pixel values must be between 0 and 15") if !b.between?(0,15)
460
+ Byteman.pad(num: b, len: 4, type: :bits)
461
+ end.join('')
462
+ padded_bits = bits + ("0" * ((-1 * bit_strm.length * 4) % 8) % 8)
463
+ Byteman.hex(0) + Byteman.pad(num: Byteman.hex(padded_bits.to_i(2)), len: padded_bits.length / 8)
255
464
  when 8
465
+ raise ArgumentError.new("If bit depth is 8, all pixel values must be between 0 and 255") if bit_strm.any?{|b| !b.between?(0,255)}
256
466
  ([0] + bit_strm).pack("C*")
257
467
  when 16
258
- ([0] + bit_strm).pack("S*")
468
+ raise ArgumentError.new("If bit depth is 16, all pixel values must be between 0 and 65535") if bit_strm.any?{|b| !b.between?(0,65535)}
469
+ Byteman.hex(0) + bit_strm.map{|b| Byteman.pad(num: Byteman.hex(b), len: 2)}.join('')
259
470
  else
260
471
  ArgumentError.new("bit_depth can only be 1,2,4,8, or 16 bits")
261
472
  end
262
473
 
263
474
  end
264
-
475
+
265
476
  z = Zlib::Deflate.new(Zlib::BEST_COMPRESSION, Zlib::MAX_WBITS, Zlib::MAX_MEM_LEVEL, Zlib::RLE)
266
477
  zstrm = z.deflate(scanlines.join(''), Zlib::FINISH)
267
478
  z.close
@@ -317,4 +528,7 @@ class Rbimg::PNG
317
528
 
318
529
  end
319
530
 
531
+
532
+
533
+
320
534
  end
@@ -2,6 +2,7 @@ require_relative "./rbimg/version"
2
2
  require 'byteman'
3
3
  require 'zlib'
4
4
 
5
+
5
6
  module Rbimg
6
7
  class Error < StandardError; end
7
8
  # Your code goes here...
@@ -1,3 +1,3 @@
1
1
  module Rbimg
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbimg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - micahshute
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-02 00:00:00.000000000 Z
11
+ date: 2020-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: byteman
@@ -42,6 +42,7 @@ files:
42
42
  - LICENSE.txt
43
43
  - README.md
44
44
  - Rakefile
45
+ - TODO.md
45
46
  - bin/console
46
47
  - bin/setup
47
48
  - lib/errors/crc_error.rb