This file is indexed.

/usr/lib/ruby/vendor_ruby/chunky_png/canvas/png_decoding.rb is in ruby-chunky-png 1.2.8-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
module ChunkyPNG
  class Canvas

    # The PNGDecoding contains methods for decoding PNG datastreams to create a 
    # Canvas object. The datastream can be provided as filename, string or IO 
    # stream.
    #
    # Overview of the decoding process:
    #
    # * The optional PLTE and tRNS chunk are decoded for the color palette of
    #   the original image.
    # * The contents of the IDAT chunks is combined, and uncompressed using
    #   Inflate decompression to the image pixelstream.
    # * Based on the color mode, width and height of the original image, which
    #   is read from the PNG header (IHDR chunk), the amount of bytes
    #   per line is determined.
    # * For every line of pixels in the encoded image, the original byte values
    #   are restored by unapplying the milter method for that line.
    # * The read bytes are unfiltered given by the filter function specified by
    #   the first byte of the line.
    # * The unfiltered pixelstream are is into colored pixels, using the color mode.
    # * All lines combined to form the original image.
    #
    # For interlaced images, the original image was split into 7 subimages.
    # These images get decoded just like the process above (from step 3), and get 
    # combined to form the original images.
    #
    # @see ChunkyPNG::Canvas::PNGEncoding
    # @see http://www.w3.org/TR/PNG/ The W3C PNG format specification
    module PNGDecoding

      # The palette that is used to decode the image, loading from the PLTE and
      # tRNS chunk from the PNG stream. For RGB(A) images, no palette is required.
      # @return [ChunkyPNG::Palette]
      attr_accessor :decoding_palette
      
      # The color to be replaced with fully transparent pixels.
      attr_accessor :transparent_color

      # Decodes a Canvas from a PNG encoded string.
      # @param [String] str The string to read from.
      # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG encoded string.
      def from_blob(str)
        from_datastream(ChunkyPNG::Datastream.from_blob(str))
      end

      alias_method :from_string, :from_blob

      # Decodes a Canvas from a PNG encoded file.
      # @param [String] filename The file to read from.
      # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG file.
      def from_file(filename)
        from_datastream(ChunkyPNG::Datastream.from_file(filename))
      end

      # Decodes a Canvas from a PNG encoded stream.
      # @param [IO, #read] io The stream to read from.
      # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG stream.
      def from_io(io)
        from_datastream(ChunkyPNG::Datastream.from_io(io))
      end

      alias_method :from_stream, :from_io

      # Decodes the Canvas from a PNG datastream instance.
      # @param [ChunkyPNG::Datastream] ds The datastream to decode.
      # @return [ChunkyPNG::Canvas] The canvas decoded from the PNG datastream.
      def from_datastream(ds)
        width      = ds.header_chunk.width
        height     = ds.header_chunk.height
        color_mode = ds.header_chunk.color
        interlace  = ds.header_chunk.interlace
        depth      = ds.header_chunk.depth

        if width == 0 || height == 0
          raise ExpectationFailed, "Invalid image size, width: #{width}, height: #{height}"
        end

        case color_mode
          when ChunkyPNG::COLOR_INDEXED
            self.decoding_palette = ChunkyPNG::Palette.from_chunks(ds.palette_chunk, ds.transparency_chunk)
          when ChunkyPNG::COLOR_TRUECOLOR
            self.transparent_color = ds.transparency_chunk.truecolor_entry(depth) if ds.transparency_chunk
          when ChunkyPNG::COLOR_GRAYSCALE
            self.transparent_color = ds.transparency_chunk.grayscale_entry(depth) if ds.transparency_chunk
        end
            
        decode_png_pixelstream(ds.imagedata, width, height, color_mode, depth, interlace)
      end

      # Decodes a canvas from a PNG encoded pixelstream, using a given width, height, 
      # color mode and interlacing mode.
      # @param [String] stream The pixelstream to read from.
      # @param [Integer] width The width of the image.
      # @param [Integer] width The height of the image.
      # @param [Integer] color_mode The color mode of the encoded pixelstream.
      # @param [Integer] depth The bit depth of the pixel samples.
      # @param [Integer] interlace The interlace method of the encoded pixelstream.
      # @return [ChunkyPNG::Canvas] The decoded Canvas instance.
      def decode_png_pixelstream(stream, width, height, color_mode, depth, interlace)
        raise ChunkyPNG::ExpectationFailed, "This palette is not suitable for decoding!" if decoding_palette && !decoding_palette.can_decode?

        image = case interlace
          when ChunkyPNG::INTERLACING_NONE;  decode_png_without_interlacing(stream, width, height, color_mode, depth)
          when ChunkyPNG::INTERLACING_ADAM7; decode_png_with_adam7_interlacing(stream, width, height, color_mode, depth)
          else raise ChunkyPNG::NotSupported, "Don't know how the handle interlacing method #{interlace}!"
        end
        
        image.pixels.map! { |c| c == transparent_color ? ChunkyPNG::Color::TRANSPARENT : c } if transparent_color
        return image
      end

      protected

      # Decodes a canvas from a non-interlaced PNG encoded pixelstream, using a 
      # given width, height and color mode.
      # @param stream (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param width (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param height (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param color_mode (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param depth (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      def decode_png_without_interlacing(stream, width, height, color_mode, depth)
        decode_png_image_pass(stream, width, height, color_mode, depth, 0)
      end

      # Decodes a canvas from a Adam 7 interlaced PNG encoded pixelstream, using a 
      # given width, height and color mode.
      # @param stream (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param width (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param height (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param color_mode (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param depth (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      def decode_png_with_adam7_interlacing(stream, width, height, color_mode, depth)
        canvas = new(width, height)
        start_pos = 0
        for pass in 0...7
          sm_width, sm_height = adam7_pass_size(pass, width, height)
          sm = decode_png_image_pass(stream, sm_width, sm_height, color_mode, depth, start_pos)
          adam7_merge_pass(pass, canvas, sm)
          start_pos += ChunkyPNG::Color.pass_bytesize(color_mode, depth, sm_width, sm_height)
        end
        canvas
      end
      
      # Extract 4 consecutive bits from a byte.
      # @param [Integer] byte The byte (0..255) value to extract a 4 bit value from.
      # @param [Integer] index The index within the byte. This should be either 0 or 2; 
      #        the value will be modded by 2 to enforce this.
      # @return [Integer] The extracted 4bit value (0..15)
      def decode_png_extract_4bit_value(byte, index)
        (index & 0x01 == 0) ? ((byte & 0xf0) >> 4) : (byte & 0x0f)
      end
      
      # Extract 2 consecutive bits from a byte.
      # @param [Integer] byte The byte (0..255) value to extract a 2 bit value from.
      # @param [Integer] index The index within the byte. This should be either 0, 1, 2, or 3; 
      #        the value will be modded by 4 to enforce this.
      # @return [Integer] The extracted 2 bit value (0..3)
      def decode_png_extract_2bit_value(byte, index)
        bitshift = 6 - ((index & 0x03) << 1)
        (byte & (0x03 << bitshift)) >> bitshift
      end

      # Extract a bit from a byte on a given index.
      # @param [Integer] byte The byte (0..255) value to extract a bit from.
      # @param [Integer] index The index within the byte. This should be 0..7; 
      #        the value will be modded by 8 to enforce this.
      # @return [Integer] Either 1 or 0.
      def decode_png_extract_1bit_value(byte, index)
        bitshift = 7 - (index & 0x07)
        (byte & (0x01 << bitshift)) >> bitshift
      end
      
      # Resamples a 16 bit value to an 8 bit value. This will discard some color information.
      # @param [Integer] value The 16 bit value to resample.
      # @return [Integer] The 8 bit resampled value
      def decode_png_resample_16bit_value(value)
        value >> 8
      end
      
      # No-op - available for completeness sake only
      # @param [Integer] value The 8 bit value to resample.
      # @return [Integer] The 8 bit resampled value
      def decode_png_resample_8bit_value(value)
        value
      end
      
      # Resamples a 4 bit value to an 8 bit value.
      # @param [Integer] value The 4 bit value to resample.
      # @return [Integer] The 8 bit resampled value.
      def decode_png_resample_4bit_value(value)
        value << 4 | value
      end
      
      # Resamples a 2 bit value to an 8 bit value.
      # @param [Integer] value The 2 bit value to resample.
      # @return [Integer] The 8 bit resampled value.
      def decode_png_resample_2bit_value(value)
        value << 6 | value << 4 | value << 2 | value
      end
      
      # Resamples a 1 bit value to an 8 bit value.
      # @param [Integer] value The 1 bit value to resample.
      # @return [Integer] The 8 bit resampled value
      def decode_png_resample_1bit_value(value)
        value == 0x01 ? 0xff : 0x00
      end
      

      # Decodes a scanline of a 1-bit, indexed image into a row of pixels.
      # @param [String] stream The stream to decode from.
      # @param [Integer] pos The position in the stream on which the scanline starts (including the filter byte).
      # @param [Integer] width The width in pixels of the scanline.
      # @return [Array<Integer>] An array of decoded pixels.
      def decode_png_pixels_from_scanline_indexed_1bit(stream, pos, width)
        (0...width).map do |index|
          palette_pos = decode_png_extract_1bit_value(stream.getbyte(pos + 1 + (index >> 3)), index) 
          decoding_palette[palette_pos]
        end
      end

      # Decodes a scanline of a 2-bit, indexed image into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_indexed_2bit(stream, pos, width)
        (0...width).map do |index|
          palette_pos = decode_png_extract_2bit_value(stream.getbyte(pos + 1 + (index >> 2)), index)
          decoding_palette[palette_pos]
        end
      end

      # Decodes a scanline of a 4-bit, indexed image into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_indexed_4bit(stream, pos, width)
        (0...width).map do |index|
          palette_pos = decode_png_extract_4bit_value(stream.getbyte(pos + 1 + (index >> 1)), index)
          decoding_palette[palette_pos]
        end
      end

      # Decodes a scanline of a 8-bit, indexed image into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_indexed_8bit(stream, pos, width)
        (1..width).map { |i| decoding_palette[stream.getbyte(pos + i)] }
      end

      # Decodes a scanline of an 8-bit, true color image with transparency into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_truecolor_alpha_8bit(stream, pos, width)
        stream.unpack("@#{pos + 1}N#{width}")
      end

      # Decodes a scanline of a 16-bit, true color image with transparency into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_truecolor_alpha_16bit(stream, pos, width)
        pixels = []
        stream.unpack("@#{pos + 1}n#{width * 4}").each_slice(4) do |r, g, b, a|
          pixels << ChunkyPNG::Color.rgba(decode_png_resample_16bit_value(r), decode_png_resample_16bit_value(g),
                                          decode_png_resample_16bit_value(b), decode_png_resample_16bit_value(a))
        end
        return pixels
      end
      
      # Decodes a scanline of an 8-bit, true color image into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_truecolor_8bit(stream, pos, width)
        stream.unpack("@#{pos + 1}" << ('NX' * width)).map { |c| c | 0x000000ff }
      end
      
      # Decodes a scanline of a 16-bit, true color image into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_truecolor_16bit(stream, pos, width)
        pixels = []
        stream.unpack("@#{pos + 1}n#{width * 3}").each_slice(3) do |r, g, b|
          pixels << ChunkyPNG::Color.rgb(decode_png_resample_16bit_value(r), decode_png_resample_16bit_value(g), decode_png_resample_16bit_value(b))
        end
        return pixels
      end

      # Decodes a scanline of an 8-bit, grayscale image with transparency into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_grayscale_alpha_8bit(stream, pos, width)
        (0...width).map { |i| ChunkyPNG::Color.grayscale_alpha(stream.getbyte(pos + (i * 2) + 1), stream.getbyte(pos + (i * 2) + 2)) }
      end
      
      # Decodes a scanline of a 16-bit, grayscale image with transparency into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_grayscale_alpha_16bit(stream, pos, width)
        pixels = []
        stream.unpack("@#{pos + 1}n#{width * 2}").each_slice(2) do |g, a|
          pixels << ChunkyPNG::Color.grayscale_alpha(decode_png_resample_16bit_value(g), decode_png_resample_16bit_value(a))
        end
        return pixels
      end

      # Decodes a scanline of a 1-bit, grayscale image into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_grayscale_1bit(stream, pos, width)
        (0...width).map do |index|
          value = decode_png_extract_1bit_value(stream.getbyte(pos + 1 + (index >> 3)), index)
          value == 1 ? ChunkyPNG::Color::WHITE : ChunkyPNG::Color::BLACK
        end
      end

      # Decodes a scanline of a 2-bit, grayscale image into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_grayscale_2bit(stream, pos, width)
        (0...width).map do |index|
          value = decode_png_extract_2bit_value(stream.getbyte(pos + 1 + (index >> 2)), index)
          ChunkyPNG::Color.grayscale(decode_png_resample_2bit_value(value))
        end
      end

      # Decodes a scanline of a 4-bit, grayscale image into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_grayscale_4bit(stream, pos, width)
        (0...width).map do |index|
          value = decode_png_extract_4bit_value(stream.getbyte(pos + 1 + (index >> 1)), index)
          ChunkyPNG::Color.grayscale(decode_png_resample_4bit_value(value))
        end
      end
      
      # Decodes a scanline of an 8-bit, grayscale image into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_grayscale_8bit(stream, pos, width)
        (1..width).map { |i| ChunkyPNG::Color.grayscale(stream.getbyte(pos + i)) }
      end
      
      # Decodes a scanline of a 16-bit, grayscale image into a row of pixels.
      # @params (see #decode_png_pixels_from_scanline_indexed_1bit)
      # @return (see #decode_png_pixels_from_scanline_indexed_1bit)
      def decode_png_pixels_from_scanline_grayscale_16bit(stream, pos, width)
        values = stream.unpack("@#{pos + 1}n#{width}")
        values.map { |value| ChunkyPNG::Color.grayscale(decode_png_resample_16bit_value(value)) }
      end
      
      # Returns the method name to use to decode scanlines into pixels.
      # @param [Integer] color_mode The color mode of the image.
      # @param [Integer] depth The bit depth of the image.
      # @return [Symbol] The method name to use for decoding, to be called on the canvas class.
      # @raise [ChunkyPNG::NotSupported] when the color_mode and/or bit depth is not supported.
      def decode_png_pixels_from_scanline_method(color_mode, depth)
        decoder_method = case color_mode
          when ChunkyPNG::COLOR_TRUECOLOR;       :"decode_png_pixels_from_scanline_truecolor_#{depth}bit"
          when ChunkyPNG::COLOR_TRUECOLOR_ALPHA; :"decode_png_pixels_from_scanline_truecolor_alpha_#{depth}bit"
          when ChunkyPNG::COLOR_INDEXED;         :"decode_png_pixels_from_scanline_indexed_#{depth}bit"
          when ChunkyPNG::COLOR_GRAYSCALE;       :"decode_png_pixels_from_scanline_grayscale_#{depth}bit"
          when ChunkyPNG::COLOR_GRAYSCALE_ALPHA; :"decode_png_pixels_from_scanline_grayscale_alpha_#{depth}bit"
          else nil
        end
        
        raise ChunkyPNG::NotSupported, "No decoder found for color mode #{color_mode} and #{depth}-bit depth!" unless respond_to?(decoder_method, true)
        decoder_method
      end

      # Decodes a single PNG image pass width a given width, height and color 
      # mode, to a Canvas, starting at the given position in the stream.
      #
      # A non-interlaced image only consists of one pass, while an Adam7
      # image consists of 7 passes that must be combined after decoding.
      #
      # @param stream (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param width (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param height (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param color_mode (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      # @param [Integer] start_pos The position in the pixel stream to start reading.
      # @return (see ChunkyPNG::Canvas::PNGDecoding#decode_png_pixelstream)
      def decode_png_image_pass(stream, width, height, color_mode, depth, start_pos)
        
        pixels = []
        if width > 0 && height > 0
          
          stream << ChunkyPNG::EXTRA_BYTE if color_mode == ChunkyPNG::COLOR_TRUECOLOR
          pixel_decoder = decode_png_pixels_from_scanline_method(color_mode, depth)
          line_length   = ChunkyPNG::Color.scanline_bytesize(color_mode, depth, width)
          pixel_size    = ChunkyPNG::Color.pixel_bytesize(color_mode, depth)
          
          raise ChunkyPNG::ExpectationFailed, "Invalid stream length!" unless stream.bytesize - start_pos >= ChunkyPNG::Color.pass_bytesize(color_mode, depth, width, height)

          pos, prev_pos = start_pos, nil
          for line_no in 0...height do
            decode_png_str_scanline(stream, pos, prev_pos, line_length, pixel_size)
            pixels += send(pixel_decoder, stream, pos, width)

            prev_pos = pos
            pos += line_length + 1
          end
        end

        new(width, height, pixels)
      end

      # Decodes a scanline if it was encoded using filtering. 
      #
      # It will extract the filtering method from the first byte of the scanline, and uses the 
      # method to change the subsequent bytes to unfiltered values. This will modify the pixelstream.
      #
      # The bytes of the scanline can then be used to construct pixels, based on the color mode..
      #
      # @param [String] stream The pixelstream to undo the filtering in.
      # @param [Integer] pos The starting position of the scanline to decode.
      # @param [Integer, nil] prev_pos The starting position of the previously decoded scanline, or <tt>nil</tt>
      #     if this is the first scanline of the image.
      # @param [Integer] line_length The number of bytes in the scanline, discounting the filter method byte.
      # @param [Integer] pixel_size The number of bytes used per pixel, based on the color mode.
      # @return [void]
      def decode_png_str_scanline(stream, pos, prev_pos, line_length, pixel_size)
        case stream.getbyte(pos)
          when ChunkyPNG::FILTER_NONE;    # noop
          when ChunkyPNG::FILTER_SUB;     decode_png_str_scanline_sub(     stream, pos, prev_pos, line_length, pixel_size)
          when ChunkyPNG::FILTER_UP;      decode_png_str_scanline_up(      stream, pos, prev_pos, line_length, pixel_size)
          when ChunkyPNG::FILTER_AVERAGE; decode_png_str_scanline_average( stream, pos, prev_pos, line_length, pixel_size)
          when ChunkyPNG::FILTER_PAETH;   decode_png_str_scanline_paeth(   stream, pos, prev_pos, line_length, pixel_size)
          else raise ChunkyPNG::NotSupported, "Unknown filter type: #{stream.getbyte(pos)}!"
        end
      end

      # Decodes a scanline that wasn't encoded using filtering. This is a no-op.
      # @params (see #decode_png_str_scanline)
      # @return [void]
      def decode_png_str_scanline_sub_none(stream, pos, prev_pos, line_length, pixel_size)
        # noop - this method shouldn't get called.
      end

      # Decodes a scanline in a pixelstream that was encoded using SUB filtering.
      # This will change the pixelstream to have unfiltered values.
      # @params (see #decode_png_str_scanline)
      # @return [void]
      def decode_png_str_scanline_sub(stream, pos, prev_pos, line_length, pixel_size)
        for i in 1..line_length do
          stream.setbyte(pos + i, (stream.getbyte(pos + i) + (i > pixel_size ? stream.getbyte(pos + i - pixel_size) : 0)) & 0xff)
        end
      end

      # Decodes a scanline in a pixelstream that was encoded using UP filtering.
      # This will change the pixelstream to have unfiltered values.
      # @params (see #decode_png_str_scanline)
      # @return [void]
      def decode_png_str_scanline_up(stream, pos, prev_pos, line_length, pixel_size)
        for i in 1..line_length do
          up = prev_pos ? stream.getbyte(prev_pos + i) : 0
          stream.setbyte(pos + i, (stream.getbyte(pos + i) + up) & 0xff)
        end
      end

      # Decodes a scanline in a pixelstream that was encoded using AVERAGE filtering.
      # This will change the pixelstream to have unfiltered values.
      # @params (see #decode_png_str_scanline)
      # @return [void]
      def decode_png_str_scanline_average(stream, pos, prev_pos, line_length, pixel_size)
        for i in 1..line_length do
          a = (i > pixel_size) ? stream.getbyte(pos + i - pixel_size) : 0
          b = prev_pos ? stream.getbyte(prev_pos + i) : 0
          stream.setbyte(pos + i, (stream.getbyte(pos + i) + ((a + b) >> 1)) & 0xff)
        end
      end

      # Decodes a scanline in a pixelstream that was encoded using PAETH filtering.
      # This will change the pixelstream to have unfiltered values.
      # @params (see #decode_png_str_scanline)
      # @return [void]
      def decode_png_str_scanline_paeth(stream, pos, prev_pos, line_length, pixel_size)
        for i in 1..line_length do
          cur_pos = pos + i
          a = (i > pixel_size) ? stream.getbyte(cur_pos - pixel_size) : 0
          b = prev_pos ? stream.getbyte(prev_pos + i) : 0
          c = (prev_pos && i > pixel_size) ? stream.getbyte(prev_pos + i - pixel_size) : 0
          p = a + b - c
          pa = (p - a).abs
          pb = (p - b).abs
          pc = (p - c).abs
          pr = (pa <= pb) ? (pa <= pc ? a : c) : (pb <= pc ? b : c)
          stream.setbyte(cur_pos, (stream.getbyte(cur_pos) + pr) & 0xff)
        end
      end
    end
  end
end