Module: Pdfrb::Filter::PNGPredictor

Defined in:
lib/pdfrb/filter/png_predictor.rb

Overview

PNG predictor (de)coding shared between FlateDecode and LZWDecode (s7.4.8.4 / RFC 2083 6.6). Extracted as a standalone module so both filters can use it without one calling the other's private methods.

Class Method Summary collapse

Class Method Details

.decode(bytes, predictor:, columns:, colors:, bits_per_component:) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/pdfrb/filter/png_predictor.rb', line 12

def decode(bytes, predictor:, columns:, colors:, bits_per_component:)
  return bytes unless (10..15).cover?(predictor)

  row_len = columns * colors * (bits_per_component / 8)
  row_len += 1 # leading filter-type byte per row
  rows = bytes.each_byte.each_slice(row_len).to_a
  prev_row = nil
  bpp = (colors * (bits_per_component / 8)) || 1
  bpp = 1 if bpp.zero?

  out = +""
  rows.each do |row|
    filter_type = row[0]
    data = row[1..]
    unfiltered = unfilter_row(filter_type, data, prev_row, bpp)
    out << unfiltered.pack("C*")
    prev_row = unfiltered
  end
  out.force_encoding(Encoding::BINARY)
end

.paeth(a, b, c) ⇒ Object



82
83
84
85
86
87
88
89
90
91
# File 'lib/pdfrb/filter/png_predictor.rb', line 82

def paeth(a, b, c)
  p = a + b - c
  pa = (p - a).abs
  pb = (p - b).abs
  pc = (p - c).abs
  return a if pa <= pb && pa <= pc
  return b if pb <= pc

  c
end

.png_average(data, prev_row, bpp) ⇒ Object



61
62
63
64
65
66
67
68
69
# File 'lib/pdfrb/filter/png_predictor.rb', line 61

def png_average(data, prev_row, bpp)
  bytes = data.dup
  (0...bytes.length).each do |i|
    left = bytes[i - bpp] || 0
    up = prev_row ? (prev_row[i] || 0) : 0
    bytes[i] = (bytes[i] + ((left + up) / 2)) & 0xFF
  end
  bytes
end

.png_paeth(data, prev_row, bpp) ⇒ Object



71
72
73
74
75
76
77
78
79
80
# File 'lib/pdfrb/filter/png_predictor.rb', line 71

def png_paeth(data, prev_row, bpp)
  bytes = data.dup
  (0...bytes.length).each do |i|
    left = bytes[i - bpp] || 0
    up = prev_row ? (prev_row[i] || 0) : 0
    up_left = prev_row ? (prev_row[i - bpp] || 0) : 0
    bytes[i] = (bytes[i] + paeth(left, up, up_left)) & 0xFF
  end
  bytes
end

.png_sub(data, bpp) ⇒ Object



43
44
45
46
47
48
49
# File 'lib/pdfrb/filter/png_predictor.rb', line 43

def png_sub(data, bpp)
  bytes = data.dup
  bpp.upto(bytes.length - 1).each do |i|
    bytes[i] = (bytes[i] + bytes[i - bpp]) & 0xFF
  end
  bytes
end

.png_up(data, prev_row) ⇒ Object



51
52
53
54
55
56
57
58
59
# File 'lib/pdfrb/filter/png_predictor.rb', line 51

def png_up(data, prev_row)
  return data unless prev_row

  bytes = data.dup
  (0...bytes.length).each do |i|
    bytes[i] = (bytes[i] + (prev_row[i] || 0)) & 0xFF
  end
  bytes
end

.unfilter_row(filter_type, data, prev_row, bpp) ⇒ Object



33
34
35
36
37
38
39
40
41
# File 'lib/pdfrb/filter/png_predictor.rb', line 33

def unfilter_row(filter_type, data, prev_row, bpp)
  case filter_type
  when 1 then png_sub(data, bpp)
  when 2 then png_up(data, prev_row)
  when 3 then png_average(data, prev_row, bpp)
  when 4 then png_paeth(data, prev_row, bpp)
  else data
  end
end