1 /*
2  * PGF: A PGF-codec demonstration
3  * $Date: 2006-05-09 20:13:33 +0200 (Di, 09 Mai 2006) $
4  * $Revision: 187 $
5 
6  * This file Copyright (C) 2006 xeraina GmbH, Switzerland
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22 
23 // PGF.cpp : Defines the entry point for the console application.
24 //
25 /////////////////////////////////////////////////////////////////////////////
26 // definitions
27 #define PGFConsoleVersion	"6.15.32"			// Major number, Minor number: Year (2) Week (2)
28 #define CurrentYear			"2015"
29 #define __PGFROISUPPORT__	// enables ROI support
30 
31 #include <iostream>
32 #include <cmath>	// or #include <math.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <ctime>
36 #include <fcntl.h>
37 #include <string>
38 
39 #if defined(__linux__) || defined(__APPLE__)
40 	#define __POSIX__
41 #endif
42 
43 #ifdef __POSIX__
44 	#include <libpgf/PGFimage.h>
45 	#include <unistd.h>	   // open, close
46 	#include <stdio.h>
47 	#include <errno.h>
48 	#include <string.h>
49 #else
50 	#include "PGFimage.h"
51 	#include <windows.h>
52 #endif
53 
54 // must be included after PGFimage.h to avoid type conflicts on POSIX systems
55 #include "CImage.h"
56 
57 /////////////////////////////////////////////////////////////////////////////
58 // definitions
59 #ifdef __APPLE__
60 	#define __stat64 stat
61 	#define _stat64 stat
62 #elif defined __POSIX__
63 	#define __stat64 stat64
64 	#define _stat64 stat64
65 #endif
66 
67 
68 using namespace std;
69 
70 /////////////////////////////////////////////////////////////////////////////
71 // static variables
72 static bool bQuiet = false;
73 static string PGFErrors[] = {
74 	"no error",
75 	"memory allocation was not successfull",
76 	"invalid memory stream position",
77 	"user break by ESC",
78 	"wrong pgf version",
79 	"wrong data file format",
80 	"image is too small",
81 	"error in zlib functions",
82 	"errors related to color table size",
83 	"errors in png functions",
84 	"expected data cannot be read",
85 };
86 
87 /////////////////////////////////////////////////////////////////////////////
FileSize(char * filename)88 static INT64 FileSize(char *filename) {
89 	struct __stat64 data;
90 
91  	if (_stat64(filename, &data) != -1) {
92 		return data.st_size;
93 	} else {
94 		return 0;
95 	}
96 }
97 
98 /////////////////////////////////////////////////////////////////////////////
PSNR(const CImage & image1,const CImage & image2,bool roi,PGFRect & rect)99 static void PSNR(const CImage& image1, const CImage& image2, bool roi, PGFRect& rect) {
100 	ASSERT(image1.GetChannelDepth() == image2.GetChannelDepth());
101 	ASSERT(image1.GetChannelDepth() == 8 || image1.GetChannelDepth() == 16);
102 
103 	const int channels1 = image1.GetChannels(); ASSERT(channels1 <= MaxChannels);
104 	#pragma warning(disable: 4189)
105 	const int channels2 = image2.GetChannels(); ASSERT(channels2 <= MaxChannels);
106 	ASSERT(channels1 == channels2);
107 	const UINT32 w = image2.GetWidth();
108 	const UINT32 h = image2.GetHeight();
109 	const UINT32 size = w*h;
110 	const int bypp1 = image1.GetBPP()/8;	// RGB mode has 3 channels but can use 24 or 32 bits per pixel
111 	const int bypp2 = image2.GetBPP()/8;
112 
113 	int pitch1 = image1.GetPitch();
114 	int pitch2 = image2.GetPitch();
115 	double sum[MaxChannels + 1] = { 0 };
116 	int tmp;
117 	int cnt1 = 0, cnt2 = 0, maxValue;
118 
119 	if (image1.GetChannelDepth() == 8) {
120 		UINT8* rgbBuff1 = (UINT8 *)image1.GetBits();
121 		UINT8* rgbBuff2 = (UINT8 *)image2.GetBits();
122 		maxValue = 255;
123 
124 	#ifdef __PGFROISUPPORT__
125 		if (roi) {
126 			ASSERT(w <= image1.GetWidth());
127 			ASSERT(h <= image1.GetHeight());
128 			rgbBuff1 += (image1.GetHeight() - h - rect.top)*pitch1 + rect.left*channels1;
129 		}
130 	#endif
131 		for (UINT32 j=0; j < h; j++) {
132 			cnt1 = cnt2 = 0;
133 			for (UINT32 i=0; i < w; i++) {
134 				for (int c=0; c < channels1; c++) {
135 					tmp = rgbBuff2[cnt2 + c] - rgbBuff1[cnt1 + c]; sum[c] += tmp*tmp;
136 				}
137 				cnt1 += bypp1;
138 				cnt2 += bypp2;
139 			}
140 			rgbBuff1 += pitch1;
141 			rgbBuff2 += pitch2;
142 		}
143 	} else if (image1.GetChannelDepth() == 16) {
144 		UINT16* rgbBuff1 = (UINT16 *)image1.GetBits();
145 		UINT16* rgbBuff2 = (UINT16 *)image2.GetBits();
146 
147 #ifdef __PNMEXSUPPORT__
148 		maxValue = image1.GetMaxValue();
149 #else
150 		maxValue = 65535;
151 #endif
152 		pitch1 /= 2;
153 		pitch2 /= 2;
154 
155 	#ifdef __PGFROISUPPORT__
156 		if (roi) {
157 			ASSERT(w <= image1.GetWidth());
158 			ASSERT(h <= image1.GetHeight());
159 			rgbBuff1 += (image1.GetHeight() - h - rect.top)*pitch1 + rect.left*channels1;
160 		}
161 	#endif
162 		for (UINT32 j=0; j < h; j++) {
163 			cnt1 = cnt2 = 0;
164 			for (UINT32 i=0; i < w; i++) {
165 				for (int c=0; c < channels1; c++) {
166 					tmp = rgbBuff2[cnt2 + c] - rgbBuff1[cnt1 + c]; sum[c] += tmp*tmp;
167 				}
168 				cnt1 += bypp1;
169 				cnt2 += bypp2;
170 			}
171 			rgbBuff1 += pitch1;
172 			rgbBuff2 += pitch2;
173 		}
174 	} else {
175 		return;
176 	}
177 
178 	for (int c=0; c < channels1; c++) {
179 		sum[MaxChannels] += sum[c];
180 	}
181 
182 	// output
183 	if (bQuiet) {
184 		cout << 10*log10(double(maxValue)*maxValue*size*channels1/sum[MaxChannels]);
185 //		cout << ((sum[MaxChannels] != 0) ? 10*log10(double(maxValue)*maxValue*size*channels1/sum[MaxChannels]) : 100);
186 		for (int c=0; c < channels1; c++) {
187 			cout << '\t' << 10*log10(double(maxValue)*maxValue*size/sum[c]);
188 			//cout << '\t' << ((sum[c] != 0) ? 10*log10(double(maxValue)*maxValue*size/sum[c]) : 100);
189 		}
190 		cout << '\t';
191 	} else {
192 		if (sum[MaxChannels] == 0) {
193 			cout << "PSNR: lossless" << endl;
194 		} else {
195 			cout << "PSNR: " << 10*log10(double(maxValue)*maxValue*size*channels1/sum[MaxChannels]) << " (";
196 			for (int c=0; c < channels1; c++) {
197 				cout << 'c' << c << ": " << 10*log10(double(maxValue)*maxValue*size/sum[c]);
198 				//cout << 'c' << c << ": " << ((sum[c] != 0) ? 10*log10(double(maxValue)*maxValue*size/sum[c]) : 100);
199 				if (c < channels1 - 1) cout << ", ";
200 			}
201 			cout << ')' << endl;
202 		}
203 		cout << endl;
204 	}
205 }
206 
207 /////////////////////////////////////////////////////////////////////////////
Encoding(CImage * & image,char * source,char * dest,int levels,int quality,bool roi,bool streaming,CPGFMemoryStream ** memStream)208 static bool Encoding(CImage*& image, char *source, char *dest, int levels, int quality, bool roi, bool streaming, CPGFMemoryStream** memStream) {
209 	ASSERT(source);
210 	ASSERT(0 <= quality && quality <= MaxQuality);
211 	bool returnValue = true;
212 	CPGFImage pgf;
213 	PGFHeader header;
214 	clock_t start = 0, mean = 0, end = 0;
215 	UINT32 writtenBytes = 0;
216 	CPGFStream *stream = nullptr;
217 	UINT8 bpp = 0;
218 #ifdef WIN32
219 	HANDLE fd = nullptr;
220 #elif defined(__POSIX__)
221 	int fd = 0;
222 #endif
223 
224 	if (!memStream) {
225 		ASSERT(dest);
226 	#ifdef WIN32
227 		// no other choice to get a "HANDLE"
228 		fd = CreateFile(dest, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
229 		if (fd == INVALID_HANDLE_VALUE) {
230 			cerr << "Error: Could not open destination file." << endl;
231 			fd = nullptr;
232 			returnValue = false;
233 			goto CleanUp;
234 		}
235 
236 		// create stream object
237 		stream = new CPGFFileStream(fd);
238 
239 	#elif defined(__POSIX__)
240 		fd = open(dest, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
241 		if (fd == -1) {
242 			cout << "Error: Could not open destination file." << endl;
243 			fd = 0;
244 			returnValue = false;
245 			goto CleanUp;
246 		}
247 
248 		// create stream object
249 		stream = new CPGFFileStream(fd);
250 	#endif
251 	}
252 
253 	if (memStream) {
254 		if (!bQuiet) cout << "Encoding PGF image to memory stream (quality: " << quality << ", levels: " << levels << "): " << source << endl;
255 	} else {
256 		if (!bQuiet) cout << "Encoding PGF image (quality: " << quality << ", levels: " << levels << "): " << source << endl;
257 	}
258 	if (roi || streaming) {
259 		if (memStream) {
260 			cout << "PGF image will support ROI." << endl;
261 		} else {
262 			cout << dest << " will support ROI." << endl;
263 		}
264 	}
265 
266 	start = clock();
267 
268 	// create new image object
269 	image = new CImage();
270 
271 	// load image
272 #ifdef WIN32
273 	if (!image->Load(source)) {
274 		LPTSTR lpMsgBuf;
275 		FormatMessage(
276 			FORMAT_MESSAGE_ALLOCATE_BUFFER |
277 			FORMAT_MESSAGE_FROM_SYSTEM |
278 			FORMAT_MESSAGE_IGNORE_INSERTS,
279 			nullptr,
280 			GetLastError(),
281 			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
282 			(LPTSTR)&lpMsgBuf,
283 			0,
284 			nullptr
285 		);
286 		cerr << "Error: " << lpMsgBuf << endl;
287 		LocalFree(lpMsgBuf);
288 		returnValue = false;
289 		goto CleanUp;
290 	}
291 #elif defined(__POSIX__)
292 	if (!image->Load(source) ){
293 		cerr << "Error: Could not load source file." << endl;
294 		returnValue = false;
295 		goto CleanUp;
296 	}
297 #endif
298 
299 	bpp = image->GetBPP();
300 	// minimum: 1-bit bitmap, maximum: 48-bit RGB (16-bit per channel)
301 	if ((bpp < 1) || (bpp > 48) || (bpp == 4)) {
302 		cerr << "Error: Unhandled image format." << endl;
303 		returnValue = false;
304 		goto CleanUp;
305 	}
306 
307 	ASSERT((bpp >= 1) && (bpp <= 48) && (bpp != 4));
308 
309 	if (memStream) {
310 		*memStream = new CPGFMemoryStream(abs(image->GetPitch())*image->GetHeight());
311 		stream = *memStream;
312 	}
313 
314 	mean = clock();
315 
316 	// optional PGF encoder configuration
317 	pgf.ConfigureEncoder(true, false); // true: use openMP (if codec is compiled with openMP), false: favorSpeedOverSize
318 
319 	header.width = image->GetWidth();
320 	header.height = image->GetHeight();
321 	header.nLevels = (UINT8)levels;
322 	header.quality = (UINT8)quality;
323 	header.mode = image->GetColorType();
324 	header.bpp = header.channels = 0; // depend on mode and will be set automatically
325 	header.usedBitsPerChannel = 0; // depend on mode and bpp and will be set automatically
326 
327 	try {
328 		if (bQuiet) {
329 			pgf.SetHeader(header, (roi || streaming) ? PGFROI : 0);
330 		} else {
331 			char data[] = "This is a free text annotation.";
332 			pgf.SetHeader(header, (roi || streaming) ? PGFROI : 0, (UINT8 *)data, sizeof(data));
333 		}
334 
335 #ifdef __PNMEXSUPPORT__
336 		if (image->GetChannelDepth() > 8) {
337 			// set maximum value
338 			pgf.SetMaxValue(image->GetMaxValue());
339 		}
340 #endif
341 
342 		// copy bits
343 		UINT8* buff = (UINT8 *)image->GetBits();
344 		if (pgf.Mode() == ImageModeRGB48) {
345 			int map[] = { 2, 1, 0 };
346 			pgf.ImportBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1)*image->GetPitch()]), bpp, map);
347 		} else {
348 #ifdef __BIG_ENDIAN__
349 			int map[] = { 2, 1, 0 };
350 			pgf.ImportBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1)*image->GetPitch()]), bpp, map);
351 #else
352 			pgf.ImportBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1)*image->GetPitch()]), bpp);
353 #endif
354 		}
355 
356 		// update color table if image is indexed
357 		if ((pgf.Mode() == ImageModeIndexedColor)) {
358 			int colorTableSize = image->GetMaxColorTableEntries();
359 			RGBQUAD* pColorTable = new RGBQUAD[colorTableSize];
360 
361 			image->GetColorTable(0, colorTableSize, pColorTable);
362 			pgf.SetColorTable(0, colorTableSize, pColorTable);
363 			delete[] pColorTable;
364 		}
365 
366 	} catch(IOException& e) {
367 		int err = e.error;
368 
369 		if (err >= AppError) {
370 			cerr << "Error: Importing input image failed\n(" << PGFErrors[err - AppError] << ")!" << endl;
371 		} else {
372 			cerr << "Error: Importing input image failed (" << err << ")!" << endl;
373 		}
374 		returnValue = false;
375 		goto CleanUp;
376 	}
377 
378 	try {
379 		if (streaming) {
380 			// process and write header
381 			writtenBytes = pgf.WriteHeader(stream);
382 			if (!bQuiet) cout << "Write header [" << writtenBytes << " bytes]" << endl;
383 			// encode each level separately
384 			for(int i = pgf.Levels() - 1; i >= 0; i--) {
385 				UINT32 size = pgf.Write(i);
386 				if (!bQuiet) cout << "Write level " << i << " [" << size << " bytes]" << endl;
387 				writtenBytes += size;
388 			}
389 		} else {
390 			// write image to pgf-file
391 			pgf.Write(stream, &writtenBytes);
392 		}
393 
394 	} catch(IOException& e) {
395 		int err = e.error;
396 		if (err >= AppError) {
397 			cerr << "Error: Writing PGF image failed\n(" << PGFErrors[err - AppError] << ")!" << endl;
398 		} else {
399 			cerr << "Error: Writing PGF image failed (" << err << ")!" << endl;
400 		}
401 		returnValue = false;
402 		goto CleanUp;
403 	}
404 
405 	end = clock();
406 
407 CleanUp:
408 	if (memStream) {
409 		// reset stream position
410 		pgf.ResetStreamPos(false);
411 	} else {
412 		// close file
413 #ifdef WIN32
414 		if (fd) CloseHandle(fd);
415 #elif defined(__POSIX__)
416 		if (fd) close(fd);
417 #endif
418 		delete stream;
419 	}
420 
421 	if (returnValue) {
422 		// output: output file has to be closed before FileSize
423 		double destSize = (memStream) ? writtenBytes : double(FileSize(dest));
424 		double ratio = double(FileSize(source))/destSize;
425 
426 		if (bQuiet) {
427 			cout << double(end - mean)/CLOCKS_PER_SEC << '\t' << double(end - start)/CLOCKS_PER_SEC << '\t' << ratio << '\t';
428 		} else {
429 			cout << "Written bytes: " << writtenBytes << endl;
430 			cout << "Encoding time (encoding, writing PGF): " << double(end - mean)/CLOCKS_PER_SEC << " s" << endl;
431 			cout << "Total time (reading source, encoding, writing PGF): " << double(end - start)/CLOCKS_PER_SEC << " s" << endl;
432 			cout << "Compression ratio: " << ratio << endl;
433 			cout << endl;
434 		}
435 	}
436 
437 	return returnValue;
438 }
439 
440 /////////////////////////////////////////////////////////////////////////////
Decoding(CImage * & image,char * source,char * dest,bool roi,PGFRect & rect,bool streaming,CPGFMemoryStream ** memStream)441 static bool Decoding(CImage*& image, char *source, char *dest, bool roi, PGFRect& rect, bool streaming, CPGFMemoryStream** memStream) {
442 	ASSERT(dest);
443 	bool returnValue = true;
444 	CPGFImage pgf;
445 	clock_t start = 0, mean = 0, mean2 = 0, mean3 = 0, end = 0;
446 	CPGFStream *stream = nullptr;
447 	UINT8* buff = nullptr;
448 	double sourceSize = 0;
449 #ifdef WIN32
450 	HANDLE fd = nullptr;
451 #elif defined(__POSIX__)
452 	int fd = 0;
453 #endif
454 
455 	if (memStream) {
456 		// use memory stream
457 		stream = *memStream;
458 	} else {
459 		ASSERT(source);
460 	#ifdef WIN32
461 		// no other choice to get a "HANDLE"
462 		fd = CreateFile(source, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
463 		if (fd == INVALID_HANDLE_VALUE) {
464 			cerr << "Error: Could not open source file." << endl;
465 			fd = nullptr;
466 			returnValue = false;
467 			goto CleanUp;
468 		}
469 
470 		// create stream object
471 		stream = new CPGFFileStream(fd);
472 
473 	#elif defined(__POSIX__)
474 		fd = open(source, O_RDONLY);
475 		if (fd == -1){
476 			cout << "Error: Could not open source file." << endl;
477 			fd = 0;
478 			returnValue = false;
479 			goto CleanUp;
480 		}
481 
482 		// create stream object
483 		stream = new CPGFFileStream(fd);
484 	#endif
485 	}
486 
487 	// optional PGF encoder configuration
488 	pgf.ConfigureDecoder(true); // true: use openMP (if codec is compiled with openMP)
489 
490 	if (memStream) {
491 		if (!bQuiet) cout << "Decoding PGF image from memory stream." << endl;
492 	} else {
493 		if (!bQuiet) cout << "Decoding PGF image: " << source << endl;
494 	}
495 
496 	start = clock();
497 	try {
498 		// open pgf image
499 		UINT64 startpos = stream->GetPos();
500 		pgf.Open(stream);
501 		if (!bQuiet) cout << "Read header and level info [" << stream->GetPos() - startpos << " bytes]" << endl;
502 
503 		// read annotations
504 		if (!bQuiet) {
505 			UINT32 len = 0;
506 			const UINT8 *data = pgf.GetUserData(len);
507 			if (data && len) {
508 				cout << (char *)data << endl;
509 			}
510 		}
511 
512 		// read pgf image
513 	#ifdef __PGFROISUPPORT__
514 		if (pgf.ROIisSupported()) {
515 			if (memStream) {
516 				if (!bQuiet) cout << "PGF image supports ROI." << endl;
517 			} else {
518 				if (!bQuiet) cout << source << " supports ROI." << endl;
519 			}
520 		}
521 		if (roi) {
522 			pgf.Read(rect); // ROI test
523 			// example of how to make a second read (with a different ROI) after resetting stream position to data
524 			//pgf.ResetStreamPos(true);
525 			//pgf.Read(rect);
526 		} else
527 	#else
528 		cout << "PGF Console: ROI supports has been disabled." << endl;
529 	#endif
530 		{
531 			if (streaming) {
532 				// decode each level separately
533 				for(int i = pgf.Levels() - 1; i >= 0; i--) {
534 					if (!bQuiet) cout << "Read level " << i;
535 					UINT64 pos = stream->GetPos();
536 					pgf.Read(i);
537 					if (!bQuiet) cout << " [" << stream->GetPos() - pos << " bytes]" << endl;
538 				}
539 			} else {
540 				// read entire file down to level 0
541 				pgf.Read();
542 			}
543 		}
544 		sourceSize = double(stream->GetPos() - startpos);
545 
546 	} catch(IOException& e) {
547 		int err = e.error;
548 		if (err >= AppError) {
549 			cerr << "Error: Opening and reading PGF image failed\n(" << PGFErrors[err - AppError] << ")!" << endl;
550 		} else {
551 			cerr << "Error: Opening and reading PGF image failed (" << err << ")!" << endl;
552 		}
553 
554 		returnValue = false;
555 		goto CleanUp;
556 	}
557 	mean = clock();
558 
559 	// create image
560 	image = new CImage();
561 #ifdef __PGFROISUPPORT__
562 	if (roi) {
563 		if (!image->Create(rect.Width(), rect.Height(), pgf.GetHeader()->mode)) {
564 			cerr << "Decoding is not supported for this PGF color format." << endl;
565 			returnValue = false;
566 			goto CleanUp;
567 		}
568 	} else
569 #endif
570 	{
571 		if (!image->Create(pgf.Width(), pgf.Height(), pgf.GetHeader()->mode)) {
572 			cerr << "Decoding is not supported for this PGF color format." << endl;
573 			returnValue = false;
574 			goto CleanUp;
575 		}
576 	}
577 
578 	// copy bits
579 	mean2 = clock();
580 	buff = (UINT8 *)image->GetBits();
581 	if (pgf.Mode() == ImageModeRGB48) {
582 		int map[] = { 2, 1, 0 };
583 		pgf.GetBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1) * image->GetPitch()]), image->GetBPP(), map);
584 	} else {
585 #ifdef __BIG_ENDIAN__
586 		int map[] = { 2, 1, 0 };
587 		pgf.GetBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1) * image->GetPitch()]), image->GetBPP(), map);
588 #else
589 		pgf.GetBitmap(-image->GetPitch(), &(buff[(image->GetHeight() - 1) * image->GetPitch()]), image->GetBPP());
590 #endif
591 	}
592 
593 	// update color table if image is indexed or bitmap
594 	if ((pgf.Mode() == ImageModeIndexedColor)) {
595 		// cannot get number of color table entries directly, so use 2^bitdepth
596 		image->SetColorTable(0, 1 << pgf.BPP(), pgf.GetColorTable());
597 	} else if (pgf.GetHeader()->mode == ImageModeBitmap) {
598 		RGBQUAD bw[2];
599 		bw[0].rgbRed = 255;
600 		bw[0].rgbGreen = 255;
601 		bw[0].rgbBlue = 255;
602 		bw[1].rgbRed = 0;
603 		bw[1].rgbGreen = 0;
604 		bw[1].rgbBlue = 0;
605 		image->SetColorTable(0, 2, bw);
606 	}
607 
608 	mean3 = clock();
609 
610 #ifdef __PNMEXSUPPORT__
611 	if (image->GetChannelDepth() > 8) {
612 		// set maximum value
613 		image->SetMaxValue(pgf.GetMaxValue());
614 	}
615 #endif
616 
617 	// save image
618 	if (image->Save(dest)) {
619 		if (!bQuiet) cout << "Written image file: " << dest << endl;
620 	} else {
621 		cerr << "Error: Output format not supported for this color depth." << endl;
622 		return false;
623 	}
624 
625 	end = clock();
626 
627 CleanUp:
628 	if (memStream) {
629 		stream->SetPos(FSFromStart, 0);
630 	} else {
631 		// close file
632 #ifdef WIN32
633 		if (fd) CloseHandle(fd);
634 #elif defined(__POSIX__)
635 		if (fd) close(fd);
636 #endif
637 		delete stream;
638 	}
639 
640 	if (!memStream) {
641 		// source has to be closed before FileSize
642 		sourceSize = (double)FileSize(source);
643 	}
644 
645 	if (returnValue) {
646 		// output
647 		double ratio = double(FileSize(dest))/sourceSize;
648 		if (bQuiet) {
649 			cout << double(mean3 - mean2 + mean - start)/CLOCKS_PER_SEC << '\t' << double(end - mean3 + mean3 - mean2 + mean - start)/CLOCKS_PER_SEC << '\t' << ratio << '\t';
650 		} else {
651 			cout << "Decoding time (reading PGF, decoding): " << double(mean3 - mean2 + mean - start)/CLOCKS_PER_SEC << " s" << endl;
652 			cout << "Total time (reading PGF, decoding, writing destination): " << double(end - mean3 + mean3 - mean2 + mean - start)/CLOCKS_PER_SEC << " s" << endl;
653 			cout << "Compression ratio: " << ratio << endl;
654 			cout << endl;
655 		}
656 	}
657 	return returnValue;
658 }
659 
660 /////////////////////////////////////////////////////////////////////////////
Measurement(char * source,char * dest,char * temp,int levels,int quality,bool roi,PGFRect & rect,bool streaming,bool useMemStream)661 static bool Measurement(char *source, char *dest, char *temp, int levels, int quality, bool roi, PGFRect& rect, bool streaming, bool useMemStream) {
662 	ASSERT(source);
663 	ASSERT(dest);
664 	CImage *image1 = nullptr, *image2 = nullptr;
665 	CPGFMemoryStream *memStream = nullptr;
666 
667 	if (!bQuiet) cout << "Measuring PSNR (quality: " << quality << ", levels: " << levels << "): " << source << endl;
668 	if (useMemStream) {
669 		if (!bQuiet) cout << "PGF image is written into memory stream." << endl;
670 	} else {
671 		ASSERT(temp);
672 	}
673 	if (!bQuiet) cout << endl;
674 
675 	if (!Encoding(image1, source, temp, levels, quality, roi, streaming, (useMemStream) ? &memStream : nullptr)) {
676 		delete image1;
677 		delete memStream;
678 		return false;
679 	}
680 	if (!Decoding(image2, temp, dest, roi, rect, streaming, (useMemStream) ? &memStream : nullptr)) {
681 		delete image2;
682 		delete memStream;
683 		return false;
684 	}
685 
686 	if (image1->GetChannelDepth() == 8 || image1->GetChannelDepth() == 16) {
687 		// output psnr
688 		PSNR(*image1, *image2, roi, rect);
689 	}
690 
691 	delete image1;
692 	delete image2;
693 	delete memStream;
694 	return true;
695 }
696 
697 /////////////////////////////////////////////////////////////////////////////
main(int argc,char * argv[])698 int main(int argc, char* argv[]) {
699 	int nRetCode = 0;
700 
701 	// parse arguments
702 	enum Operation {Encode, Decode, Measure} op = Encode;
703 	char *source=nullptr, *dest=nullptr, *temp=nullptr;
704 	int levels = 0, quality = 0;
705 	PGFRect rect;
706 	int arg = 1;			// argument 0 = pgf
707 	bool bSource = true;	// true: bSource, false: dest
708 	bool bStreaming = false;// true: level wise writing/reading is used
709 	bool bROI = false;		// true: ROI is used
710 	bool bMemStream = false;// true: use a memory stream during measuring
711 	bool bWrongArgs = (argc < 4);
712 
713 	while (!bWrongArgs && arg < argc) {
714 		if (argv[arg][0] == '-') {
715 			// options
716 			switch(argv[arg][1]) {
717 			case 'e': op = Encode; arg++; break;
718 			case 'd': op = Decode; arg++; break;
719 			case 'm': op = Measure;
720 				if (argv[arg][2] == 'm') {
721 					// -mm
722 					bMemStream = true;
723 					arg++;
724 				} else {
725 					arg++;
726 					if (arg == argc) {
727 						bWrongArgs = true;
728 					} else {
729 						temp = argv[arg]; arg++;
730 					}
731 				}
732 				break;
733 			case 'l': arg++;
734 				if (arg == argc) {
735 					bWrongArgs = true;
736 				} else {
737 					levels = atoi(argv[arg]); arg++;
738 					bWrongArgs = (levels < 1) || (levels > MaxLevel);
739 				}
740 				break;
741 			case 'q': arg++;
742 				if (arg == argc) {
743 					bWrongArgs = true;
744 				} else {
745 					quality = atoi(argv[arg]); arg++;
746 					bWrongArgs = (quality < 0) || (quality > MaxQuality);
747 				}
748 				break;
749 			case 'r': bROI = true;
750 				if (argv[arg][2] == 'e') {
751 					// -re[ct]
752 					arg++;
753 					if (arg + 4 > argc) {
754 						bWrongArgs = true;
755 					} else {
756 						rect = PGFRect(atoi(argv[arg]), atoi(argv[arg+1]), atoi(argv[arg+2]), atoi(argv[arg+3]));
757 						arg += 4;
758 					}
759 				} else {
760 					// -r
761 					arg++;
762 					if (arg == argc) bWrongArgs = true;
763 				}
764 				break;
765 			case 's': bStreaming = true; arg++; break;
766 			case 'v': bQuiet = true; arg++; break;
767 			default: arg++; bWrongArgs = true; break;
768 			}
769 		} else {
770 			if (bSource) {
771 				source = argv[arg];
772 				bSource = false;
773 			} else {
774 				dest = argv[arg];
775 			}
776 			arg++;
777 		}
778 	}
779 	if (!bQuiet) cout << "PGF Console - Copyright (c) 2001-" CurrentYear " xeraina GmbH, Switzerland" << endl <<
780 		"Console Version: " PGFConsoleVersion ", www.xeraina.ch" << endl <<
781 		"libpgf Version : " PGFCodecVersion ", www.libpgf.org" << endl << endl;
782 	if (bWrongArgs) {
783 		if (!bQuiet) {
784 			cout << "Usage: " << endl <<
785 				    "- Encoding: pgfconsole -e [-l levels] [-q quality] [-r] [-s] [-v] source dest" << endl <<
786 					"               Create from a source file a PGF image (dest)." << endl <<
787 					"               The most popular image file formats with the following image" << endl <<
788 					"               types are supported:" << endl <<
789 					"               - bitmap (1 bit)" << endl <<
790 					"               - grayscale (8 and 16 bit)" << endl <<
791 					"               - indexed color (8 bit)" << endl <<
792 					"               - RGB (16 [565], 24, 32, and 48 bit)" << endl <<
793 					"               - RGBA (32 bit)" << endl <<
794 					"  Options:" << endl <<
795 					"  -l levels    Number of hierarchical levels [1.." << MaxLevel << "]. Default is 0." << endl <<
796 					"               0 means the number of levels are automatically set." << endl <<
797 					"  -q quality   Quality [0.." << MaxQuality << "]. Default is 0." << endl <<
798 					"               0 means perfect quality (lossless compression)," << endl <<
799 					"               " << MaxQuality << " means maximum compression." << endl <<
800 					"  -r           Region of interest (ROI) encoding scheme is used." << endl <<
801 					"               This encoding scheme has a slightly worse compression ratio." << endl <<
802 					"  -s           Level wise encoding in separate writing calls." << endl <<
803 					"  -v           Numbers only: All text output is reduced to numbers." << endl <<
804 					endl <<
805 				    "- Decoding: pgfconsole -d [-rect left top width height] [-s] [-v] source dest" << endl <<
806 					"               Create from a PGF image (source) a new image (dest)." << endl <<
807 					"  Options:" << endl <<
808 					"  -rect rect   Read a rectangular region of a PGF image supporting Region of" << endl <<
809 					"               interests (ROI). The rectangle is defined by 4 blank-separated" << endl <<
810 					"               positive parameters: left top width height" << endl <<
811 					"  -s           Level wise decoding in separate reading calls." << endl <<
812 					"  -v           Numbers only: All text output is reduced to numbers." << endl <<
813 					endl <<
814 				    "- Measuring: pgfconsole -m temp-file [...] source destination" << endl <<
815 					"               Measure quality between source and destination bitmap." << endl <<
816 					"               Encode from an input image (source) a PGF image" << endl <<
817 					"               (temp-file) and decode from the temp-file a new output" << endl <<
818 					"               (destination image)." << endl <<
819 					"  Options:" << endl <<
820 					"  -mm          Instead of using the option -m temp-file you can use " << endl <<
821 					"               the option -mm (without temp-file). The latter writes the PGF" << endl <<
822 					"               image into a memory stream instead of a file stream." << endl <<
823 					"               In both cases all encoding and decoding options are valid." << endl <<
824 					endl <<
825 					endl;
826 		}
827 		nRetCode = 2;
828 	} else {
829 		CImage *image = 0;
830 
831 #ifdef __PNMEXSUPPORT__
832 		// register new PNM plugin
833 		CImage::RegisterPNM();
834 #endif
835 
836 		switch(op) {
837 		case Encode:
838 			if (!Encoding(image, source, dest, levels, quality, bROI, bStreaming, nullptr)) nRetCode = 3;
839 			break;
840 		case Decode:
841 			if (!Decoding(image, source, dest, bROI, rect, bStreaming, nullptr)) nRetCode = 4;
842 			break;
843 		case Measure:
844 			if (!Measurement(source, dest, temp, levels, quality, bROI, rect, bStreaming, bMemStream)) nRetCode = 5;
845 			break;
846 		default:
847 			nRetCode = 6;
848 			ASSERT(false); // unhandled operation
849 		}
850 		if (bQuiet) cout << endl;
851 
852 		delete image;
853 	}
854 	return nRetCode;
855 }
856 
857