1 /*
2  * This file Copyright (C) 2010 Christopher Stamm
3  *                              Fachhochschule Nordwestschweiz
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
7  * as published by the Free Software Foundation; either version 2.1
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
19 
20 #include "pnm.h"
21 #include <cstdio>
22 #include <fstream>
23 
24 #ifdef WIN32
25 	#include <io.h>
26 	#include <fcntl.h>
27 #endif
28 
29 using namespace std;
30 
31 /////////////////////////////////////////////////////////////////////////////
32 // constants
33 
34 /////////////////////////////////////////////////////////////////////////////
ByteSwap(UINT16 wX)35 __inline UINT16 ByteSwap(UINT16 wX) {
36 #ifdef __BIG_ENDIAN__
37 	return wX;
38 #else
39 	return ((wX & 0xFF00) >> 8) | ((wX & 0x00FF) << 8);
40 #endif
41 }
42 
43 /////////////////////////////////////////////////////////////////////////////
CPNM()44 CPNM::CPNM() : m_w(0), m_h(0), m_maxValue(0)
45 {
46 }
47 
48 /////////////////////////////////////////////////////////////////////////////
~CPNM()49 CPNM::~CPNM() {
50 	delete[] m_buffer;
51 }
52 
53 /////////////////////////////////////////////////////////////////////////////
ReadPNM(CPGFImage & pgf,int quality,int levels,bool roi,bool alpha)54 bool CPNM::ReadPNM(CPGFImage& pgf, int quality, int levels, bool roi, bool alpha) {
55 	int channels = 1, type = 0;
56 
57 	istream& in = cin;
58 
59 	// read magic
60 	if (!ReadMagic(in, type)) return false;
61 
62 #ifdef WIN32
63 	if (type >= 4) {
64 		// set binary mode for cin (= 0)
65 		_setmode(0, _O_BINARY);
66 	}
67 #endif
68 
69 	// read width and height
70 	if (!ReadSize(in, m_w, m_h)) return false;
71 
72 	// read further data depending on image type
73 	switch(type) {
74 	case 1: // Black and White
75 	case 4:
76 		if (in.good()) {
77 			m_mode = ImageModeBitmap;
78 			m_bpp = 1;
79 			m_pitch = (((m_w + 7)/8 + 3)/4)*4;
80 			channels = 1;
81 
82 			// set colortable
83 			RGBQUAD colors[2];
84 			FillInColorTable(colors, 2);
85 			pgf.SetColorTable(0, 2, colors);
86 
87 		} else {
88 			cerr << "Error: Wrong PNM header" << endl;
89 			return false;
90 		}
91 		break;
92 	case 2: // Gray
93 	case 5:
94 		// read max value
95 		if (!ReadMaxValue(in, m_maxValue)) return false;
96 		{
97 			// set colortable
98 			RGBQUAD colors[256];
99 			FillInColorTable(colors, 256);
100 			pgf.SetColorTable(0, 256, colors);
101 		}
102 		channels = 1;
103 
104 		if (m_maxValue == 255) {
105 			m_mode = ImageModeGrayScale;
106 			m_bpp = 8;
107 			m_pitch = ((m_w + 3)/4)*4;
108 		} else {
109 			m_mode = ImageModeGray16;
110 			m_bpp = 16;
111 			m_pitch = ((2*m_w + 3)/4)*4;
112 			pgf.SetMaxValue(m_maxValue);
113 		}
114 		break;
115 	case 3: // RGB
116 	case 6:
117 		// read max value
118 		if (!ReadMaxValue(in, m_maxValue)) return false;
119 		if (m_maxValue == 255) {
120 			if (alpha) {
121 				m_mode = ImageModeRGBA;
122 				m_bpp = 32;
123 				channels = 4;
124 				m_pitch = 4*m_w;
125 			} else {
126 				m_mode = ImageModeRGBColor;
127 				m_bpp = 24;
128 				channels = 3;
129 				m_pitch = ((3*m_w + 3)/4)*4;
130 			}
131 		} else {
132 			m_mode = ImageModeRGB48;
133 			m_bpp = 48;
134 			m_pitch = ((6*m_w + 3)/4)*4;
135 			pgf.SetMaxValue(m_maxValue);
136 		}
137 		break;
138 	default:
139 		ASSERT(false);
140 		return false;
141 	}
142 
143 	SkipComments(in);
144 	if (in.fail()) {
145 		cerr << "Error: Wrong PNM header" << endl;
146 		return false;
147 	}
148 
149 	// create buffer
150 	m_buffer = new UINT8[m_pitch*m_h];
151 	if (!m_buffer) cerr << "Error: out of memory" << endl;
152 
153 	// create pgf image
154 	PGFHeader header;
155 	header.bpp = m_bpp;
156 	header.channels = channels;
157 	header.height = m_h;
158 	header.width = m_w;
159 	header.mode = m_mode;
160 	header.nLevels = levels;
161 	header.quality = quality;
162 	//	header.background.rgbtBlue = header.background.rgbtGreen = header.background.rgbtRed = 255;
163 	pgf.SetHeader(header, (roi) ? PGFROI : 0);
164 
165 	// read image data depending on image type
166 	switch(type) {
167 		case 1: // ASCII Black and White
168 			if (!ReadP1(in)) return false;
169 			break;
170 		case 2: // ASCII Gray
171 			if (!ReadP2(in)) return false;
172 			break;
173 		case 3: // ASCII RGB
174 			if (!ReadP3(in)) return false;
175 			break;
176 		case 4: // binary Black and White
177 			if (!ReadP4(in)) return false;
178 			break;
179 		case 5: // binary Gray
180 			if (!ReadP5(in)) return false;
181 			break;
182 		case 6: // binary RGB
183 			if (!ReadP6(in)) return false;
184 			break;
185 		default:
186 			ASSERT(false);
187 			return false;
188 	}
189 
190 	if (m_mode == ImageModeRGBA) {
191 		// read alpha channel (PGM)
192 		int type2 = 0, width = 0, height = 0;
193 
194 		if (!ReadMagic(in, type2)) return false;
195 		if (type2 != 2 && type2 != 5) {
196 			cerr << "Error: PPM followed by PGM expected" << endl;
197 			return false;
198 		}
199 		if (!ReadSize(in, width, height)) return false;
200 		if (width != m_w || height != m_h) {
201 			cerr << "Error: dimensions for PPM and PGM do not agree" << endl;
202 			return false;
203 		}
204 		if (!ReadMaxValue(in, m_maxValue)) return false;
205 
206 		SkipComments(in);
207 
208 		if (type2 == 2) {
209 			if (!ReadP2(in)) return false;
210 		} else {
211 			if (!ReadP5(in)) return false;
212 		}
213 	}
214 
215 	// import bitmap data into pgf
216 	try {
217 		pgf.ImportBitmap(m_pitch, m_buffer, m_bpp);
218 	} catch(IOException&) {
219 		cerr << "Error: PGF import failed" << endl;
220 		return false;
221 	}
222 
223 	return true;
224 }
225 
226 /////////////////////////////////////////////////////////////////////////////
WritePNM(CPGFImage & pgf,bool roi,PGFRect & rect,bool binary)227 bool CPNM::WritePNM(CPGFImage& pgf, bool roi, PGFRect& rect, bool binary /*= true*/) {
228 	if (!ImportIsSupported(pgf.Mode())) {
229 		cerr << "Error: PGF image mode is not supported" << endl;
230 		return false;
231 	} else {
232 		m_mode = pgf.Mode();
233 	}
234 
235 	// store image size
236 	if (roi) {
237 		m_w = rect.Width();
238 		m_h = rect.Height();
239 	} else {
240 		m_w = pgf.Width();
241 		m_h = pgf.Height();
242 	}
243 
244 	// set pnm file type and pitch
245 	int type = 0;
246 	switch(m_mode) {
247 		case ImageModeBitmap:
248 			type = 1;
249 			m_bpp = 1;
250 			m_pitch = (((m_w + 7)/8 + 3)/4)*4;
251 			break;
252 		case ImageModeGrayScale:
253 			type = 2;
254 			m_bpp = 8;
255 			m_pitch = ((m_w + 3)/4)*4;
256 			break;
257 		case ImageModeGray16:
258 			type = 2;
259 			m_bpp = 16;
260 			m_pitch = ((2*m_w + 3)/4)*4;
261 			m_maxValue = pgf.GetMaxValue();
262 			break;
263 		case ImageModeRGBColor:
264 			type = 3;
265 			m_bpp = 24;
266 			m_pitch = ((3*m_w + 3)/4)*4;
267 			break;
268 		case ImageModeRGB48:
269 			type = 3;
270 			m_bpp = 48;
271 			m_pitch = ((6*m_w + 3)/4)*4;
272 			m_maxValue = pgf.GetMaxValue();
273 			break;
274 		case ImageModeRGBA:
275 			type = 3;
276 			m_bpp = 32;
277 			m_pitch = 4*m_w;
278 			break;
279 		default:
280 			ASSERT(false);
281 			return false;
282 	}
283 	if (binary) type += 3;
284 
285 	// create buffer
286 	m_buffer = new UINT8[m_pitch*m_h];
287 	if (!m_buffer) cerr << "Error: out of memory" << endl;
288 
289 	// copy pgf image buffer
290 	try {
291 		pgf.GetBitmap(m_pitch, m_buffer, m_bpp);
292 	} catch(IOException&) {
293 		cerr << "Error: PGF decompression failed" << endl;
294 		return false;
295 	}
296 
297 	// write pnm
298 	ostream& out = cout;
299 
300 #ifdef WIN32
301 	if (binary) {
302 		// set binary mode for cout (= 1)
303 		_setmode(1, _O_BINARY);
304 	}
305 #endif
306 
307 	// write magic
308 	ASSERT(type);
309 	out << "P" /*<< noshowpos*/ << type << endl;
310 
311 	// write width and height
312 	out << m_w << ' ' << m_h << endl;
313 
314 	// write further data depending on image type
315 	switch(type) {
316 		case 1:
317 			if (!WriteP1(out)) return false;
318 			break;
319 		case 2:
320 			if (!WriteP2(out)) return false;
321 			break;
322 		case 3:
323 			if (!WriteP3(out)) return false;
324 			break;
325 		case 4:
326 			if (!WriteP4(out)) return false;
327 			break;
328 		case 5:
329 			if (!WriteP5(out)) return false;
330 			break;
331 		case 6:
332 			if (!WriteP6(out)) return false;
333 			break;
334 		default:
335 			ASSERT(false);
336 			return false;
337 	}
338 
339 	if (m_mode == ImageModeRGBA) {
340 		// write alpha channel as PGM image
341 		if (binary) {
342 			out << endl << "P5" << endl;
343 		} else {
344 			out << "P2" << endl;
345 		}
346 
347 		// write width and height
348 		out << m_w << ' ' << m_h << endl;
349 
350 		if (binary) {
351 			if (!WriteP5(out)) return false;
352 		} else {
353 			if (!WriteP2(out)) return false;
354 		}
355 	}
356 
357 	return true;
358 }
359 
360 /////////////////////////////////////////////////////////////////////////////
361 // Read ASCII Black and White image file
362 // Pixel order of a row in file corresponds with pixel order on screen. But in memory it is different:
363 // |01234567|01234567|01234567| ... pixel number on sreen and in file
364 // | Byte 0 | Byte 1 | Byte 2 | ... bytes on screen and in file and memory
365 // |msb  lsb|msb  lsb|msb  lsb| ... bits in memory
366 //
ReadP1(std::istream & in)367 bool CPNM::ReadP1(std::istream& in) {
368 	// read data
369 	UINT8 *buff = m_buffer;
370 	int pos, val;
371 	int v;
372 	int numOfBytes = (m_w + 7)/8;
373 
374 	for(int i = 0; i < m_h; i++) {
375 		pos = 0;
376 		for(int j = 0; j < numOfBytes; j++) {
377 			val = 0;
378 			for(int k = 0; k < 8; k++) {
379 				val *= 2;
380 				if (pos < m_w) {
381 					in >> v; ASSERT(v == 0 || v == 1); // read msb first
382  					val += v;
383 				}
384 				pos++;
385 			}
386 			buff[j] = (UINT8)val;
387 		}
388 		buff += m_pitch;
389 	}
390 
391 	return true;
392 }
393 
394 /////////////////////////////////////////////////////////////////////////////
395 // Read ASCII Gray image file
ReadP2(std::istream & in)396 bool CPNM::ReadP2(std::istream& in) {
397 	// read data
398 	int v;
399 
400 	if (m_mode == ImageModeGray16) {
401 		int pitch16 = m_pitch/2;
402 		UINT16 *buff16 = (UINT16 *)m_buffer;
403 
404 		for(int i = 0; i < m_h; i++) {
405 			for(int j = 0; j < m_w; j++) {
406 				in >> v;
407 				buff16[j] = (UINT16)v;
408 			}
409 			buff16 += pitch16;
410 		}
411 	} else if (m_mode == ImageModeRGBA) {
412 		// read alpha channel
413 		ASSERT(m_bpp == 32);
414 		UINT8 *buff = m_buffer;
415 		int pos;
416 
417 		for(int i = 0; i < m_h; i++) {
418 			pos = 3;
419 			for(int j = 0; j < m_w; j++) {
420 				in >> v;
421 				buff[pos] = UINT8(v*255/m_maxValue);
422 				pos += 4;
423 			}
424 			buff += m_pitch;
425 		}
426 	} else {
427 		UINT8 *buff = m_buffer;
428 
429 		for(int i = 0; i < m_h; i++) {
430 			for(int j = 0; j < m_w; j++) {
431 				in >> v;
432 				buff[j] = UINT8(v*255/m_maxValue);
433 			}
434 			buff += m_pitch;
435 		}
436 	}
437 
438 	return true;
439 }
440 
441 /////////////////////////////////////////////////////////////////////////////
442 // Read ASCII RGB image file
ReadP3(std::istream & in)443 bool CPNM::ReadP3(std::istream& in) {
444 	// read data
445 	int pos, v;
446 
447 	if (m_mode == ImageModeRGB48) {
448 		int pitch16 = m_pitch/2;
449 		UINT16 *buff16 = (UINT16 *)m_buffer;
450 
451 		for(int i = 0; i < m_h; i++) {
452 			pos = 0;
453 			for(int j = 0; j < m_w; j++) {
454 				in >> v; buff16[pos + 2] = (UINT16)v; // R
455 				in >> v; buff16[pos + 1] = (UINT16)v; // G
456 				in >> v; buff16[pos]     = (UINT16)v; // B
457 				pos += 3;
458 			}
459 			buff16 += pitch16;
460 		}
461 	} else {
462 		UINT8 *buff = m_buffer;
463 		int bypp = 3;
464 		if (m_mode == ImageModeRGBA) {
465 			// read RGB of RGBA
466 			ASSERT(m_bpp == 32);
467 			bypp = 4;
468 		}
469 
470 		for(int i = 0; i < m_h; i++) {
471 			pos = 0;
472 			for(int j = 0; j < m_w; j++) {
473 				in >> v; buff[pos + 2] = UINT8(v*255/m_maxValue); // R
474 				in >> v; buff[pos + 1] = UINT8(v*255/m_maxValue); // G
475 				in >> v; buff[pos]     = UINT8(v*255/m_maxValue); // B
476 				pos += bypp;
477 			}
478 			buff += m_pitch;
479 		}
480 	}
481 
482 	return true;
483 }
484 
485 /////////////////////////////////////////////////////////////////////////////
486 // Read binary Black and White image file
ReadP4(std::istream & in)487 bool CPNM::ReadP4(std::istream& in) {
488 	// read data
489 	int numOfBytes = (m_w + 7)/8;
490 	char *buff = (char *)m_buffer;
491 
492 	for(int i = 0; i < m_h; i++) {
493 		in.read(buff, numOfBytes);
494 		buff += m_pitch;
495 	}
496 
497 	return true;
498 }
499 
500 /////////////////////////////////////////////////////////////////////////////
501 // Read binary Gray image file
ReadP5(std::istream & in)502 bool CPNM::ReadP5(std::istream& in) {
503 	// read data
504 	if (m_mode == ImageModeGray16) {
505 		// data is in big-endian format
506 		int pitch16 = m_pitch/2;
507 		UINT16 *buff16 = (UINT16 *)m_buffer;
508 
509 		for(int i = 0; i < m_h; i++) {
510 			in.read((char *)buff16, m_w*2);
511 			for(int j = 0; j < m_w; j++) {
512 				buff16[j] = ByteSwap(buff16[j]);
513 			}
514 			buff16 += pitch16;
515 		}
516 	} else if (m_mode == ImageModeRGBA) {
517 		// read alpha channel
518 		ASSERT(m_bpp == 32);
519 		UINT8 *buff = m_buffer;
520 		char *row = new char[m_w];
521 		int pos;
522 
523 		for(int i = 0; i < m_h; i++) {
524 			in.read(row, m_w);
525 			pos = 3;
526 			if (m_maxValue < 255) {
527 				for(int j = 0; j < m_w; j++) {
528 					buff[pos] = UINT8(row[j]*255/m_maxValue);
529 					pos += 4;
530 				}
531 			} else {
532 				for(int j = 0; j < m_w; j++) {
533 					buff[pos] = row[j];
534 					pos += 4;
535 				}
536 			}
537 			buff += m_pitch;
538 		}
539 		delete[] row;
540 	} else {
541 		char *buff = (char *)m_buffer;
542 
543 		for(int i = 0; i < m_h; i++) {
544 			in.read(buff, m_w);
545 			if (m_maxValue < 255) {
546 				for(int j = 0; j < m_w; j++) {
547 					buff[j] = UINT8(buff[j]*255/m_maxValue);
548 				}
549 			}
550 			buff += m_pitch;
551 		}
552 	}
553 
554 	return true;
555 }
556 
557 /////////////////////////////////////////////////////////////////////////////
558 // Read binary RGB image file
ReadP6(std::istream & in)559 bool CPNM::ReadP6(std::istream& in) {
560 	// read data
561 	UINT8 tmp;
562 	int pos;
563 
564 	if (m_mode == ImageModeRGB48) {
565 		// data is in big-endian format
566 		int pitch16 = m_pitch/2;
567 		UINT16 *buff16 = (UINT16 *)m_buffer;
568 		UINT16 t;
569 
570 		for(int i = 0; i < m_h; i++) {
571 			in.read((char *)buff16, m_w*6);
572 			pos = 0;
573 			for(int j = 0; j < m_w; j++) {
574 				// swap R and B
575 				t = buff16[pos];
576 				buff16[pos] = ByteSwap(buff16[pos + 2]);
577 				buff16[pos + 1] = ByteSwap(buff16[pos + 1]);
578 				buff16[pos + 2] = ByteSwap(t);
579 			}
580 			buff16 += pitch16;
581 		}
582 	} else if (m_mode == ImageModeRGBA) {
583 		// read alpha channel
584 		ASSERT(m_bpp == 32);
585 		UINT8 *buff = m_buffer;
586 		char *row = new char[3*m_w];
587 		int rpos;
588 
589 		for(int i = 0; i < m_h; i++) {
590 			in.read(row, 3*m_w);
591 			pos = 0;
592 			rpos = 0;
593 			if (m_maxValue < 255) {
594 				for(int j = 0; j < m_w; j++) {
595 					buff[pos]		= UINT8(row[rpos + 2]*255/m_maxValue); // B
596 					buff[pos + 1]	= UINT8(row[rpos + 1]*255/m_maxValue); // G
597 					buff[pos + 2]	= UINT8(row[rpos    ]*255/m_maxValue); // R
598 					pos += 4;
599 					rpos += 3;
600 				}
601 			} else {
602 				for(int j = 0; j < m_w; j++) {
603 					buff[pos]		= row[rpos + 2]; // B
604 					buff[pos + 1]	= row[rpos + 1]; // G
605 					buff[pos + 2]	= row[rpos];	 // R
606 					pos += 4;
607 					rpos += 3;
608 				}
609 			}
610 			buff += m_pitch;
611 		}
612 		delete[] row;
613 	} else {
614 		char *buff = (char *)m_buffer;
615 
616 		for(int i = 0; i < m_h; i++) {
617 			in.read((char *)buff, 3*m_w);
618 			pos = 0;
619 			if (m_maxValue < 255) {
620 				for(int j = 0; j < m_w; j++) {
621 					// swap R and B
622 					tmp = buff[pos];
623 					buff[pos] = buff[pos + 2];
624 					buff[pos + 2] = tmp;
625 					pos += 3;
626 				}
627 			} else {
628 				for(int j = 0; j < m_w; j++) {
629 					buff[pos + 1]	= UINT8(buff[pos + 1]*255/m_maxValue); // G
630 					// swap R and B
631 					tmp = UINT8(buff[pos]*255/m_maxValue);
632 					buff[pos] = UINT8(buff[pos + 2]*255/m_maxValue);
633 					buff[pos + 2] = tmp;
634 					pos += 3;
635 				}
636 			}
637 			buff += m_pitch;
638 		}
639 	}
640 
641 	return true;
642 }
643 
644 /////////////////////////////////////////////////////////////////////////////
645 // Write ASCII black and white file
646 // Pixel order of a row in file corresponds with pixel order on screen. But in memory it is different:
647 // |01234567|01234567|01234567| ... pixel number on sreen and in file
648 // | Byte 0 | Byte 1 | Byte 2 | ... bytes on screen and in file and memory
649 // |msb  lsb|msb  lsb|msb  lsb| ... bits in memory
650 //
WriteP1(std::ostream & out)651 bool CPNM::WriteP1(std::ostream& out) {
652 	ASSERT(m_buffer);
653 	const int numOfBytes = (m_w + 7)/8;
654 	UINT8 *buff8 = m_buffer;
655 	int pos;
656 	char z;
657 
658 	// write data
659 	for (int i = 0; i < m_h; i++) {
660 		pos = 0;
661 		for (int j = 0; j < numOfBytes; j++) {
662 			z = buff8[j];
663 			for(int k = 0; k < 8 && pos < m_w; k++) {
664 				out << ((z < 0) ? 1 : 0) << ' ';  // write msb first
665  				z <<= 1;
666 				pos++;
667 			}
668 		}
669 		buff8 += m_pitch;
670 		out << std::endl;
671 	}
672 	return true;
673 }
674 
675 /////////////////////////////////////////////////////////////////////////////
676 // Write ASCII gray image file
WriteP2(std::ostream & out)677 bool CPNM::WriteP2(std::ostream& out) {
678 	ASSERT(m_buffer);
679 
680 	if (m_mode == ImageModeGrayScale) {
681 		UINT8 *buff = m_buffer;
682 
683 		// write maxvalue
684 		out << 255 << std::endl;
685 
686 		// write data
687 		for (int i = 0; i < m_h; i++) {
688 			for (int j = 0; j < m_w; j++) {
689 				out << (int)buff[j] << ' ';
690 			}
691 			buff += m_pitch;
692 			out << std::endl;
693 		}
694 	} else if (m_mode == ImageModeRGBA) {
695 		// write alpha channel
696 		ASSERT(m_bpp == 32);
697 		UINT8 *buff = m_buffer;
698 
699 		// write maxvalue
700 		out << 255 << std::endl;
701 
702 		// write data
703 		for (int i = 0; i < m_h; i++) {
704 			for (int j = 0; j < m_w; j++) {
705 				out << (int)buff[j*4 + 3] << ' ';
706 			}
707 			buff += m_pitch;
708 			out << std::endl;
709 		}
710 	} else {
711 		ASSERT(m_mode == ImageModeGray16);
712 		int pitch = m_pitch/2;
713 		UINT16 *buff = (UINT16*)m_buffer;
714 
715 		// write maxvalue
716 		out << m_maxValue << std::endl;
717 
718 		// write data
719 		for (int i = 0; i < m_h; i++) {
720 			for (int j = 0; j < m_w; j++) {
721 				out << (int)buff[j] << ' ';
722 			}
723 			buff += pitch;
724 			out << std::endl;
725 		}
726 	}
727 	return true;
728 }
729 
730 /////////////////////////////////////////////////////////////////////////////
731 // Write ASCII RGB color file
WriteP3(std::ostream & out)732 bool CPNM::WriteP3(std::ostream& out) {
733 	ASSERT(m_buffer);
734 
735 	if (m_mode == ImageModeRGB48) {
736 		int pos, pitch = m_pitch/2;
737 		UINT16 *buff = (UINT16*)m_buffer;
738 
739 		// write maxvalue
740 		out << m_maxValue << std::endl;
741 
742 		// write data
743 		for (int i = 0; i < m_h; i++) {
744 			pos = 0;
745 			for (int j = 0; j < m_w; j++) {
746 				out << (int)buff[pos + 2] << ' '; // R
747 				out << (int)buff[pos + 1] << ' '; // G
748 				out << (int)buff[pos] << ' ';	  // B
749 				pos += 3;
750 			}
751 			buff += pitch;
752 			out << std::endl;
753 		}
754 	} else {
755 		int pos;
756 		UINT8 *buff = m_buffer;
757 		int bypp = 3;
758 		if (m_mode == ImageModeRGBA) {
759 			// write RGB of RGBA
760 			ASSERT(m_bpp == 32);
761 			bypp = 4;
762 		}
763 
764 		// write maxvalue
765 		out << 255 << std::endl;
766 
767 		// write data
768 		for (int i = 0; i < m_h; i++) {
769 			pos = 0;
770 			for (int j = 0; j < m_w; j++) {
771 				out << (int)buff[pos + 2] << ' '; // R
772 				out << (int)buff[pos + 1] << ' '; // G
773 				out << (int)buff[pos] << ' ';	  // B
774 				pos += bypp;
775 			}
776 			buff += m_pitch;
777 			out << std::endl;
778 		}
779 	}
780 	return true;
781 }
782 
783 /////////////////////////////////////////////////////////////////////////////
784 // Write binary black and white file
WriteP4(std::ostream & out)785 bool CPNM::WriteP4(std::ostream& out) {
786 	ASSERT(m_buffer);
787 	const int numOfBytes = (m_w + 7)/8;
788 	ASSERT(m_pitch >= numOfBytes);
789 	char *buff8 = (char *)m_buffer;
790 
791 	// write buffer
792 	for(int i = 0; i < m_h; i++) {
793 		out.write(buff8, numOfBytes);
794 		buff8 += m_pitch;
795 	}
796 	return true;
797 }
798 
799 /////////////////////////////////////////////////////////////////////////////
800 // Write binary gray image file
WriteP5(std::ostream & out)801 bool CPNM::WriteP5(std::ostream& out) {
802 	ASSERT(m_buffer);
803 
804 	// write maxvalue
805 	if (m_mode == ImageModeGrayScale) {
806 		char *buff = (char *)m_buffer;
807 
808 		// write max value
809 		out << 255 << std::endl;
810 
811 		// write data
812 		for(int i = 0; i < m_h; i++) {
813 			out.write(buff, m_w);
814 			buff += m_pitch;
815 		}
816 	} else if (m_mode == ImageModeRGBA) {
817 		// write alpha channel
818 		ASSERT(m_bpp == 32);
819 
820 		// write max value
821 		out << 255 << std::endl;
822 
823 		char *row = new char[m_w];
824 		char *buff = (char *)m_buffer;
825 
826 		// write data
827 		for (int i = 0; i < m_h; i++) {
828 			for(int j = 0; j < m_w; j++) {
829 				row[j] = buff[j*4 + 3];
830 			}
831 			out.write(row, m_w);
832 			buff += m_pitch;
833 		}
834 		delete[] row;
835 	} else {
836 		ASSERT(m_mode == ImageModeGray16);
837 		int pitch = m_pitch/2;
838 		UINT16 *buff = (UINT16 *)m_buffer;
839 		UINT16 val;
840 
841 		// write max value
842 		out << m_maxValue << std::endl;
843 
844 		// write data in big-endian format
845 		for(int i = 0; i < m_h; i++) {
846 			for(int j = 0; j < m_w; j++) {
847 				val = ByteSwap(buff[j]);
848 				out.write((char *)&val, 2);
849 			}
850 			buff += pitch;
851 		}
852 	}
853 	return true;
854 }
855 
856 /////////////////////////////////////////////////////////////////////////////
857 // Write binary RGB color file
WriteP6(std::ostream & out)858 bool CPNM::WriteP6(std::ostream& out) {
859 	ASSERT(m_buffer);
860 	int pos = 0;
861 
862 	if (m_mode == ImageModeRGB48) {
863 		int pitch = m_pitch/2;
864 		UINT16 *buff = (UINT16 *)m_buffer;
865 		UINT16 val;
866 
867 		// write maxvalue
868 		out << m_maxValue << std::endl;
869 
870 		// write data in big-endian format
871 		for(int i = 0; i < m_h; i++) {
872 			pos = 0;
873 			for(int j = 0; j < m_w; j++) {
874 				val = ByteSwap(buff[pos + 2]); out.write((char *)&val, 2); // R
875 				val = ByteSwap(buff[pos + 1]); out.write((char *)&val, 2); // G
876 				val = ByteSwap(buff[pos]);     out.write((char *)&val, 2); // B
877 				pos += 3;
878 			}
879 			buff += pitch;
880 		}
881 
882 	} else {
883 		char *buff = (char *)m_buffer;
884 		int bypp = 3;
885 		if (m_mode == ImageModeRGBA) {
886 			// write RGB of RGBA
887 			ASSERT(m_bpp == 32);
888 			bypp = 4;
889 		}
890 
891 		// write maxvalue
892 		out << 255 << std::endl;
893 
894 		// write data
895 		for(int i = 0; i < m_h; i++) {
896 			pos = 0;
897 			for(int j = 0; j < m_w; j++) {
898 				out.write(&buff[pos + 2], 1); // R
899 				out.write(&buff[pos + 1], 1); // G
900 				out.write(&buff[pos], 1);     // B
901 				pos += bypp;
902 			}
903 			buff += m_pitch;
904 		}
905 	}
906 	return true;
907 }
908 
909 //////////////////////////////////////////////////////////////////
910 // Return true if the given image mode is supported for import
ImportIsSupported(BYTE mode)911 bool CPNM::ImportIsSupported(BYTE mode) {
912 	switch(mode) {
913 		case ImageModeBitmap:
914 		case ImageModeGrayScale:
915 		case ImageModeRGBColor:
916 		case ImageModeGray16:
917 		case ImageModeRGB48:
918 		case ImageModeRGBA:
919 			return true;
920 	}
921 	return false;
922 }
923 
924 /////////////////////////////////////////////////////////////////////////////
FillInColorTable(RGBQUAD * table,int size)925 void CPNM::FillInColorTable(RGBQUAD* table, int size) {
926 	if (size == 2) {
927 		// bitmap color table
928 		table[0].rgbBlue = 255;
929 		table[0].rgbGreen = 255;
930 		table[0].rgbRed = 255;
931 		table[1].rgbBlue = 0;
932 		table[1].rgbGreen = 0;
933 		table[1].rgbRed = 0;
934 	} else if (size == 256) {
935 		// gray scale color table
936 		for (int i=0; i < 256; i++) {
937 			table[i].rgbBlue = (BYTE)i;
938 			table[i].rgbGreen = (BYTE)i;
939 			table[i].rgbRed = (BYTE)i;
940 		}
941 	} else {
942 		ASSERT(false);
943 	}
944 }
945 
946 /////////////////////////////////////////////////////////////////////////////
ReadMagic(istream & in,int & type)947 bool CPNM::ReadMagic(istream& in, int& type) {
948 	char m = 0;
949 
950 	SkipComments(in);
951 	if (in.good()) {
952 		in >> m >> type;
953 	}
954 	if (m != 'P' || type <= 0 || type > 6) {
955 		// unknown type
956 		cerr << "Error: Unknown PNM type specifier" << endl;
957 		return false;
958 	}
959 
960 	return true;
961 }
962 
963 /////////////////////////////////////////////////////////////////////////////
ReadSize(istream & in,int & width,int & height)964 bool CPNM::ReadSize(istream& in, int& width, int& height) {
965 	SkipComments(in);
966 	if (in.good()) {
967 		in >> width >> height;
968 		if (width <= 0 || height <= 0) {
969 			cerr << "Error: Wrong PNM width or height" << endl;
970 			return false;
971 		}
972 	} else {
973 		cerr << "Error: Wrong PNM width or height" << endl;
974 		return false;
975 	}
976 
977 	return true;
978 }
979 
980 /////////////////////////////////////////////////////////////////////////////
ReadMaxValue(istream & in,int & maxValue)981 bool CPNM::ReadMaxValue(istream& in, int& maxValue) {
982 	SkipComments(in);
983 	if (in.good()) {
984 		in >> maxValue;
985 		if (maxValue <= 0 || maxValue >= 65536) {
986 			cerr << "Error: Wrong PNM maximum value" << endl;
987 			return false;
988 		}
989 	} else {
990 		cerr << "Error: Wrong PNM maximum value" << endl;
991 		return false;
992 	}
993 
994 	return true;
995 }
996 
997 /////////////////////////////////////////////////////////////////////////////
SkipComments(istream & in)998 void CPNM::SkipComments(istream& in) {
999 	int c = in.peek();
1000 	while(in.good() && (c == '#' || c == '\n')) {
1001 		in.ignore(1000, '\n');
1002 		c = in.peek();
1003 	}
1004 }
1005 
1006