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