Class: Tb::PNMReader

Inherits:
Object
  • Object
show all
Defined in:
lib/tb/pnm.rb

Overview

practical only for (very) small images.

Constant Summary collapse

WSP =
/(?:[ \t\r\n]|\#[^\r\n]*[\r\n])+/

Instance Method Summary collapse

Constructor Details

#initialize(pnm_content) ⇒ PNMReader

Returns a new instance of PNMReader.

Raises:

  • (ArgumentError)


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
# File 'lib/tb/pnm.rb', line 66

def initialize(pnm_content)
  pnm_content.force_encoding("ASCII-8BIT") if pnm_content.respond_to? :force_encoding
  if /\A(P[63])(#{WSP})(\d+)(#{WSP})(\d+)(#{WSP})(\d+)[ \t\r\n]/on =~ pnm_content
    magic, wsp1, w, wsp2, h, wsp3, max, raster = $1, $2, $3.to_i, $4, $5.to_i, $6, $7.to_i, $'
    pixel_component = %w[R G B]
  elsif /\A(P[52])(#{WSP})(\d+)(#{WSP})(\d+)(#{WSP})(\d+)[ \t\r\n]/on =~ pnm_content
    magic, wsp1, w, wsp2, h, wsp3, max, raster = $1, $2, $3.to_i, $4, $5.to_i, $6, $7.to_i, $'
    pixel_component = %w[V]
  elsif /\A(P[41])(#{WSP})(\d+)(#{WSP})(\d+)[ \t\r\n]/on =~ pnm_content
    magic, wsp1, w, wsp2, h, raster = $1, $2, $3.to_i, $4, $5.to_i, $'
    wsp3 = nil
    max = 1
    pixel_component = %w[V]
  else
    raise ArgumentError, "not PNM format"
  end
  raise ArgumentError, "unsupported max value: #{max}" if 65535 < max

  @ary = [
    ['type', 'x', 'y', 'component', 'value'],
    ['meta', nil, nil, 'pnm_type', magic],
    ['meta', nil, nil, 'width', w],
    ['meta', nil, nil, 'height', h],
    ['meta', nil, nil, 'max', max]
  ]

  [wsp1, wsp2, wsp3].each {|wsp|
    next if !wsp
    wsp.scan(/\#([^\r\n]*)[\r\n]/) { @ary << ['meta', nil, nil, 'comment', $1] }
  }

  if /P[65]/ =~ magic # raw (binary) PPM/PGM
    if max < 0x100
      each_pixel_component = method(:raw_ppm_pgm_1byte_each_pixel_component)
    else
      each_pixel_component = method(:raw_ppm_pgm_2byte_each_pixel_component)
    end
  elsif /P4/ =~ magic # raw (binary) PBM
    each_pixel_component = make_raw_pbm_each_pixel_component(w)
  elsif /P[32]/ =~ magic # plain (ascii) PPM/PGM
    each_pixel_component = method(:plain_ppm_pgm_each_pixel_component)
  elsif /P1/ =~ magic # plain (ascii) PBM
    each_pixel_component = method(:plain_pbm_each_pixel_component)
  end
  n = w * h * pixel_component.length
  i = 0
  each_pixel_component.call(raster) {|value|
    break if i == n
    y, x = (i / pixel_component.length).divmod(w)
    c = pixel_component[i % pixel_component.length]
    @ary << ['pixel', x, y, c, value.to_f / max]
    i += 1
  }
  if i != n
    raise ArgumentError, "PNM raster data too short."
  end
end

Instance Method Details

#eachObject



170
171
172
173
174
175
# File 'lib/tb/pnm.rb', line 170

def each
  while ary = self.shift
    yield ary
  end
  nil
end

#make_raw_pbm_each_pixel_component(width) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/tb/pnm.rb', line 143

def make_raw_pbm_each_pixel_component(width)
  iter = Object.new
  iter.instance_variable_set(:@width, width)
  def iter.call(raster)
    numbytes = (@width + 7) / 8
    y = 0
    while true
      return if raster.size <= y * numbytes
      line = raster[y * numbytes, numbytes]
      x = 0
      while x < @width
        i, j = x.divmod(8)
        return if line.size <= i
        byte = line[x/8].ord
        yield 1 - ((byte >> (7-j)) & 1)
        x += 1
      end
      y += 1
    end
  end
  iter
end

#plain_pbm_each_pixel_component(raster) ⇒ Object



139
140
141
# File 'lib/tb/pnm.rb', line 139

def plain_pbm_each_pixel_component(raster)
  raster.scan(/[01]/) { yield 1 - $&.to_i }
end

#plain_ppm_pgm_each_pixel_component(raster) ⇒ Object



135
136
137
# File 'lib/tb/pnm.rb', line 135

def plain_ppm_pgm_each_pixel_component(raster)
  raster.scan(/\d+/) { yield $&.to_i }
end

#raw_ppm_pgm_1byte_each_pixel_component(raster, &b) ⇒ Object



124
125
126
# File 'lib/tb/pnm.rb', line 124

def raw_ppm_pgm_1byte_each_pixel_component(raster, &b)
  raster.each_byte(&b)
end

#raw_ppm_pgm_2byte_each_pixel_component(raster) ⇒ Object



128
129
130
131
132
133
# File 'lib/tb/pnm.rb', line 128

def raw_ppm_pgm_2byte_each_pixel_component(raster)
  raster.enum_for(:each_byte).each_slice(2) {|byte1, byte2|
    word = byte1 * 0x100 + byte2
    yield word
  }
end

#shiftObject



166
167
168
# File 'lib/tb/pnm.rb', line 166

def shift
  @ary.shift
end

#to_aObject



177
178
179
180
181
# File 'lib/tb/pnm.rb', line 177

def to_a
  result= []
  each {|ary| result << ary }
  result
end