rbimg 0.1.3 → 0.1.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: 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