Signal/Geometry Processing Library (SPL)  2.0.4
pnmCodec.hpp
Go to the documentation of this file.
1 // Copyright (c) 2011 Michael D. Adams
2 // All rights reserved.
3 
4 // __START_OF_LICENSE__
5 //
6 // Copyright (c) 2015 Michael D. Adams
7 // All rights reserved.
8 //
9 // This file is part of the Signal Processing Library (SPL).
10 //
11 // This program is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU General Public License as
13 // published by the Free Software Foundation; either version 3,
14 // or (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public
22 // License along with this program; see the file LICENSE. If not,
23 // see <http://www.gnu.org/licenses/>.
24 //
25 // __END_OF_LICENSE__
26 
32 #ifndef SPL_pnmCodec_hpp
33 #define SPL_pnmCodec_hpp
34 
36 //
38 
39 #include <SPL/config.hpp>
40 #include <iostream>
41 #include <sstream>
42 #include <cassert>
43 #include <cstdlib>
44 
46 //
48 
49 namespace SPL {
50 
52 //
54 
56 enum PnmMagic {
57  pnmMagicInvalid = 0,
58  pnmMagicTxtPbm = 0x5031,
59  pnmMagicTxtPgm = 0x5032,
60  pnmMagicTxtPpm = 0x5033,
61  pnmMagicBinPbm = 0x5034,
62  pnmMagicBinPgm = 0x5035,
63  pnmMagicBinPpm = 0x5036,
64  pnmMagicPam = 0x5037
65 };
66 
68 enum PnmType {
69  pnmTypeInvalid = 0,
70  pnmTypePbm,
71  pnmTypePgm,
72  pnmTypePpm
73 };
74 
76 enum PnmFmt {
77  pnmFmtInvalid = 0,
78  pnmFmtTxt,
79  pnmFmtBin
80 };
81 
83 const int pnmMaxLineLen = 80;
84 
86 struct PnmHeader
87 {
89  PnmMagic magic;
90 
92  int width;
93 
95  int height;
96 
98  int maxVal;
99 
101  bool sgnd;
102 };
103 
107 PnmType pnmGetType(PnmMagic magic);
108 
112 PnmFmt pnmGetFmt(PnmMagic magic);
113 
117 int pnmMaxValToPrec(int maxVal);
118 
122 int pnmGetNumComps(PnmType type);
123 
124 int pnmMinVal(bool sgnd, int maxVal);
125 int pnmMaxVal(bool sgnd, int maxVal);
126 
131 inline long pnmOnes(int n)
132 {
133  return (1UL << n) - 1;
134 }
135 
136 template <class T>
137 T pnmClipVal(T x, T minVal, T maxVal)
138 {
139  assert(minVal <= maxVal);
140  if (x < minVal) {
141  x = minVal;
142  } else if (x > maxVal) {
143  x = maxVal;
144  }
145  return x;
146 }
147 
149 //
151 
155 int pnmGetHeader(std::istream& in, PnmHeader& header);
156 
160 int pnmPutHeader(std::ostream& out, PnmHeader& header);
161 
165 int pnmGetChar(std::istream& in);
166 
170 int pnmGetTxtBit(std::istream& in);
171 
175 long pnmGetTxtInt(std::istream& in, bool sgnd, int& status);
176 
180 long pnmGetBinInt(std::istream& in, int wordSize, bool sgnd, int& status);
181 
185 int pnmPutBinInt(std::ostream& out, int wordSize, bool sgnd, long val);
186 
188 // Encoder
190 
194 template <class GetData>
195 int pnmEncode(std::ostream& outStream, int width, int height, int numComps,
196  int maxVal, bool sgnd, GetData& getData, bool binaryFormat)
197 {
198  PnmHeader header;
199  if (numComps == 1) {
200  if (maxVal == 1) {
201  header.magic = binaryFormat ? pnmMagicBinPbm : pnmMagicTxtPbm;
202  } else {
203  header.magic = binaryFormat ? pnmMagicBinPgm : pnmMagicTxtPgm;
204  }
205  } else if (numComps == 3) {
206  header.magic = binaryFormat ? pnmMagicBinPpm : pnmMagicTxtPpm;
207  } else {
208  return -1;
209  }
210  header.width = width;
211  header.height = height;
212  header.maxVal = maxVal;
213  header.sgnd = false;
214 
215  if (pnmPutHeader(outStream, header)) {
216  return -1;
217  }
218  if (putData(outStream, header, getData)) {
219  return -1;
220  }
221  return 0;
222 }
223 
227 template <class GetData>
228 int putData(std::ostream& out, PnmHeader& header, GetData& getData)
229 {
230  int prec = pnmMaxValToPrec(header.maxVal);
231  int fmt = pnmGetFmt(header.magic);
232  PnmType type = pnmGetType(header.magic);
233  int numComps = pnmGetNumComps(type);
234 
235  int lineLen = 0;
236  for (int y = header.height - 1; y >= 0; --y) {
237  if (fmt == pnmFmtBin) {
238  if (type == pnmTypePbm) {
239  int val = 0;
240  int numBits = 0;
241  for (int x = 0; x < header.width; ++x) {
242  long b = getData();
243  val = (val << 1) | (b & 1);
244  ++numBits;
245  if (numBits >= 8) {
246  if (!out.put(static_cast<unsigned char>(val))) {
247  return -1;
248  }
249  val = 0;
250  numBits = 0;
251  }
252  }
253  if (numBits > 0) {
254  assert(numBits <= 8);
255  val <<= 8 - numBits;
256  if (!out.put(static_cast<unsigned char>(val))) {
257  return -1;
258  }
259  }
260  } else {
261  for (int x = 0; x < header.width; ++x) {
262  for (int c = 0; c < numComps; ++c) {
263  //long val = img.get(c, x, y);
264  long val = getData();
265  if (pnmPutBinInt(out, prec, header.sgnd, val)) {
266  return -1;
267  }
268  }
269  }
270  }
271  } else {
272  for (int x = 0; x < header.width; ++x) {
273  for (int c = 0; c < numComps; ++c) {
274  //long val = img.get(c, x, y);
275  long val = getData();
276  std::ostringstream buf;
277  buf << val;
278  int n = buf.str().length();
279  if (lineLen && lineLen + n + 2 > pnmMaxLineLen) {
280  out << "\n";
281  lineLen = 0;
282  }
283  if (lineLen && prec > 1) {
284  out << " ";
285  ++lineLen;
286  }
287  out << buf.str();
288  lineLen += n;
289  }
290  }
291  }
292  if (fmt == pnmFmtTxt && lineLen) {
293  out << "\n";
294  lineLen = 0;
295  }
296  }
297 
298  return 0;
299 }
300 
302 // Decoder
304 
308 template <class Initialize>
309 int pnmDecode(std::istream& inStream, Initialize& initialize)
310 {
311  PnmHeader header;
312  if (pnmGetHeader(inStream, header)) {
313  return -1;
314  }
315 #if defined(PNM_DEBUG)
316  std::cerr
317  << "magic " << std::hex << header.magic << " "
318  << std::dec << " width " << header.width << " "
319  << "height " << header.height << " "
320  << "maxVal " << header.maxVal << "\n"
321  ;
322 #endif
323  int numComps = pnmGetNumComps(pnmGetType(header.magic));
324  typename Initialize::PutData putData;
325  initialize(header.width, header.height, numComps, header.maxVal,
326  header.sgnd, putData);
327  if (getData(inStream, header, putData)) {
328  return -1;
329  }
330  return 0;
331 }
332 
336 template <class PutData>
337 int getData(std::istream& in, PnmHeader& header, PutData& putData)
338 {
339  PnmType type = pnmGetType(header.magic);
340  PnmFmt fmt = pnmGetFmt(header.magic);
341  int numComps = pnmGetNumComps(type);
342  int prec = pnmMaxValToPrec(header.maxVal);
343 #if 1
344  int minVal = pnmMinVal(header.sgnd, header.maxVal);
345  int maxVal = pnmMaxVal(header.sgnd, header.maxVal);
346 #endif
347  for (int y = header.height - 1; y >= 0; --y) {
348  if (type == pnmTypePbm) {
349  if (fmt == pnmFmtBin) {
350  for (int x = 0; x < header.width;) {
351  int c;
352  if ((c = in.get()) == std::char_traits<char>::eof()) {
353  return -1;
354  }
355  int n = 8;
356  while (n > 0 && x < header.width) {
357  //img.set(0, x, y, (c >> 7) & 1);
358  putData((c >> 7) & 1);
359  c <<= 1;
360  --n;
361  ++x;
362  }
363  }
364  } else {
365  for (int x = 0; x < header.width; ++x) {
366  long val;
367  if ((val = pnmGetTxtBit(in)) < 0) {
368  return -1;
369  }
370  //img.set(0, x, y, val);
371  putData(val);
372  }
373  }
374  } else {
375  if (fmt == pnmFmtBin) {
376  for (int x = 0; x < header.width; ++x) {
377  for (int c = 0; c < numComps; ++c) {
378  long val;
379  int status;
380  if (val = pnmGetBinInt(in, prec, header.sgnd, status),
381  status) {
382  return -1;
383  }
384  // NOTE: Should I add range clipping here?
385 #if 0
386  if (val < minVal || val > maxVal) {
387  std::cerr << "warning: clipping out of range sample value\n";
388  }
389  val = pnmClipVal<int>(val, minVal, maxVal);
390 #else
391  assert(val >= minVal && val <= maxVal);
392 #endif
393  //img.set(c, x, y, val);
394  putData(val);
395  }
396  }
397  } else {
398  for (int x = 0; x < header.width; ++x) {
399  for (int c = 0; c < numComps; ++c) {
400  long val;
401  int status;
402  if (val = pnmGetTxtInt(in, header.sgnd, status), status) {
403  return -1;
404  }
405  // NOTE: Should I add range clipping here?
406 #if 0
407  if (val < minVal || val > maxVal) {
408  std::cerr << "warning: clipping out of range sample value\n";
409  }
410  val = pnmClipVal<int>(val, minVal, maxVal);
411 #else
412  assert(val >= minVal && val <= maxVal);
413 #endif
414  //img.set(c, x, y, val);
415  putData(val);
416  }
417  }
418  }
419  }
420  }
421  return 0;
422 }
423 
425 //
427 
428 }
429 
430 #endif
Definition: Arcball.hpp:48
bool sgnd
The signedness of the sample data.
Definition: pnmCodec.hpp:101
int height
The image height.
Definition: pnmCodec.hpp:95
int width
The image width.
Definition: pnmCodec.hpp:92
The header information for PNM data.
Definition: pnmCodec.hpp:86
PnmMagic magic
The magic number.
Definition: pnmCodec.hpp:89
int maxVal
The maximum sample value.
Definition: pnmCodec.hpp:98