PageRenderTime 74ms CodeModel.GetById 14ms app.highlight 54ms RepoModel.GetById 2ms app.codeStats 0ms

/src/FreeImage/Source/FreeImage/PluginPNM.cpp

https://bitbucket.org/cabalistic/ogredeps/
C++ | 831 lines | 558 code | 171 blank | 102 comment | 170 complexity | 7b0175fe43a3cbb02f30f193bf1a6a05 MD5 | raw file
  1// ==========================================================
  2// PNM (PPM, PGM, PBM) Loader and Writer
  3//
  4// Design and implementation by
  5// - Floris van den Berg (flvdberg@wxs.nl)
  6// - Hervé Drolon (drolon@infonie.fr)
  7//
  8// This file is part of FreeImage 3
  9//
 10// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
 11// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
 12// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
 13// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
 14// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
 15// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
 16// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
 17// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
 18// THIS DISCLAIMER.
 19//
 20// Use at your own risk!
 21// ==========================================================
 22
 23#include "FreeImage.h"
 24#include "Utilities.h"
 25
 26// ==========================================================
 27// Internal functions
 28// ==========================================================
 29
 30/**
 31Get an integer value from the actual position pointed by handle
 32*/
 33static int
 34GetInt(FreeImageIO *io, fi_handle handle) {
 35    char c = 0;
 36	BOOL firstchar;
 37
 38    // skip forward to start of next number
 39
 40    if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
 41
 42    while (1) {
 43        // eat comments
 44
 45        if (c == '#') {
 46			// if we're at a comment, read to end of line
 47
 48            firstchar = TRUE;
 49
 50            while (1) {
 51				if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
 52
 53				if (firstchar && c == ' ') {
 54					// loop off 1 sp after #
 55
 56					firstchar = FALSE;
 57				} else if (c == '\n') {
 58					break;
 59				}
 60			}
 61		}
 62
 63        if (c >= '0' && c <='9') {
 64			// we've found what we were looking for
 65
 66            break;
 67		}
 68
 69        if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
 70    }
 71
 72    // we're at the start of a number, continue until we hit a non-number
 73
 74    int i = 0;
 75
 76    while (1) {
 77        i = (i * 10) + (c - '0');
 78
 79        if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
 80
 81        if (c < '0' || c > '9')
 82            break;
 83    }
 84
 85    return i;
 86}
 87
 88/**
 89Read a WORD value taking into account the endianess issue
 90*/
 91static inline WORD 
 92ReadWord(FreeImageIO *io, fi_handle handle) {
 93	WORD level = 0;
 94	io->read_proc(&level, 2, 1, handle); 
 95#ifndef FREEIMAGE_BIGENDIAN
 96	SwapShort(&level);	// PNM uses the big endian convention
 97#endif
 98	return level;
 99}
100
101/**
102Write a WORD value taking into account the endianess issue
103*/
104static inline void 
105WriteWord(FreeImageIO *io, fi_handle handle, const WORD value) {
106	WORD level = value;
107#ifndef FREEIMAGE_BIGENDIAN
108	SwapShort(&level);	// PNM uses the big endian convention
109#endif
110	io->write_proc(&level, 2, 1, handle);
111}
112
113
114// ==========================================================
115// Plugin Interface
116// ==========================================================
117
118static int s_format_id;
119
120// ==========================================================
121// Plugin Implementation
122// ==========================================================
123
124static const char * DLL_CALLCONV
125Format() {
126	return "PNM";
127}
128
129static const char * DLL_CALLCONV
130Description() {
131	return "Portable Network Media";
132}
133
134static const char * DLL_CALLCONV
135Extension() {
136	return "pbm,pgm,ppm";
137}
138
139static const char * DLL_CALLCONV
140RegExpr() {
141	return NULL;
142}
143
144static const char * DLL_CALLCONV
145MimeType() {
146	return "image/freeimage-pnm";
147}
148
149static BOOL DLL_CALLCONV
150Validate(FreeImageIO *io, fi_handle handle) {
151	BYTE pbm_id1[] = { 0x50, 0x31 };
152	BYTE pbm_id2[] = { 0x50, 0x34 };
153	BYTE pgm_id1[] = { 0x50, 0x32 };
154	BYTE pgm_id2[] = { 0x50, 0x35 };
155	BYTE ppm_id1[] = { 0x50, 0x33 };
156	BYTE ppm_id2[] = { 0x50, 0x36 };
157	BYTE signature[2] = { 0, 0 };
158
159	io->read_proc(signature, 1, sizeof(pbm_id1), handle);
160
161	if (memcmp(pbm_id1, signature, sizeof(pbm_id1)) == 0)
162		return TRUE;
163
164	if (memcmp(pbm_id2, signature, sizeof(pbm_id2)) == 0)
165		return TRUE;
166
167	if (memcmp(pgm_id1, signature, sizeof(pgm_id1)) == 0)
168		return TRUE;
169
170	if (memcmp(pgm_id2, signature, sizeof(pgm_id2)) == 0)
171		return TRUE;
172
173	if (memcmp(ppm_id1, signature, sizeof(ppm_id1)) == 0)
174		return TRUE;
175
176	if (memcmp(ppm_id2, signature, sizeof(ppm_id2)) == 0)
177		return TRUE;
178
179	return FALSE;
180}
181
182static BOOL DLL_CALLCONV
183SupportsExportDepth(int depth) {
184	return (
185			(depth == 1) ||
186			(depth == 8) ||
187			(depth == 24)
188		);
189}
190
191static BOOL DLL_CALLCONV 
192SupportsExportType(FREE_IMAGE_TYPE type) {
193	return (
194		(type == FIT_BITMAP)  ||
195		(type == FIT_UINT16)  ||
196		(type == FIT_RGB16)
197	);
198}
199
200static BOOL DLL_CALLCONV
201SupportsNoPixels() {
202	return TRUE;
203}
204
205// ----------------------------------------------------------
206
207static FIBITMAP * DLL_CALLCONV
208Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
209	char id_one = 0, id_two = 0;
210	int x, y;
211	FIBITMAP *dib = NULL;
212	RGBQUAD *pal;	// pointer to dib palette
213	int i;
214
215	if (!handle) {
216		return NULL;
217	}
218
219	BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
220
221	try {
222		FREE_IMAGE_TYPE image_type = FIT_BITMAP;	// standard image: 1-, 8-, 24-bit
223
224		// Read the first two bytes of the file to determine the file format
225		// "P1" = ascii bitmap, "P2" = ascii greymap, "P3" = ascii pixmap,
226		// "P4" = raw bitmap, "P5" = raw greymap, "P6" = raw pixmap
227
228		io->read_proc(&id_one, 1, 1, handle);
229		io->read_proc(&id_two, 1, 1, handle);
230
231		if ((id_one != 'P') || (id_two < '1') || (id_two > '6')) {			
232			// signature error
233			throw FI_MSG_ERROR_MAGIC_NUMBER;
234		}
235
236		// Read the header information: width, height and the 'max' value if any
237
238		int width  = GetInt(io, handle);
239		int height = GetInt(io, handle);
240		int maxval = 1;
241
242		if((id_two == '2') || (id_two == '5') || (id_two == '3') || (id_two == '6')) {
243			maxval = GetInt(io, handle);
244			if((maxval <= 0) || (maxval > 65535)) {
245				FreeImage_OutputMessageProc(s_format_id, "Invalid max value : %d", maxval);
246				throw (const char*)NULL;
247			}
248		}
249
250		// Create a new DIB
251
252		switch (id_two) {
253			case '1':
254			case '4':
255				// 1-bit
256				dib = FreeImage_AllocateHeader(header_only, width, height, 1);
257				break;
258
259			case '2':
260			case '5':
261				if(maxval > 255) {
262					// 16-bit greyscale
263					image_type = FIT_UINT16;
264					dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height);
265				} else {
266					// 8-bit greyscale
267					dib = FreeImage_AllocateHeader(header_only, width, height, 8);
268				}
269				break;
270
271			case '3':
272			case '6':
273				if(maxval > 255) {
274					// 48-bit RGB
275					image_type = FIT_RGB16;
276					dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height);
277				} else {
278					// 24-bit RGB
279					dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
280				}
281				break;
282		}
283
284		if (dib == NULL) {
285			throw FI_MSG_ERROR_DIB_MEMORY;
286		}
287
288		// Build a greyscale palette if needed
289
290		if(image_type == FIT_BITMAP) {
291			switch(id_two)  {
292				case '1':
293				case '4':
294					pal = FreeImage_GetPalette(dib);
295					pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
296					pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
297					break;
298
299				case '2':
300				case '5':
301					pal = FreeImage_GetPalette(dib);
302					for (i = 0; i < 256; i++) {
303						pal[i].rgbRed	=
304						pal[i].rgbGreen =
305						pal[i].rgbBlue	= (BYTE)i;
306					}
307					break;
308
309				default:
310					break;
311			}
312		}
313
314		if(header_only) {
315			// header only mode
316			return dib;
317		}
318
319		// Read the image...
320
321		switch(id_two)  {
322			case '1':
323			case '4':
324				// write the bitmap data
325
326				if (id_two == '1') {	// ASCII bitmap
327					for (y = 0; y < height; y++) {		
328						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
329
330						for (x = 0; x < width; x++) {
331							if (GetInt(io, handle) == 0)
332								bits[x >> 3] |= (0x80 >> (x & 0x7));
333							else
334								bits[x >> 3] &= (0xFF7F >> (x & 0x7));
335						}
336					}
337				}  else {		// Raw bitmap
338					int line = CalculateLine(width, 1);
339
340					for (y = 0; y < height; y++) {	
341						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
342
343						for (x = 0; x < line; x++) {
344							io->read_proc(&bits[x], 1, 1, handle);
345
346							bits[x] = ~bits[x];
347						}
348					}
349				}
350
351				return dib;
352
353			case '2':
354			case '5':
355				if(image_type == FIT_BITMAP) {
356					// write the bitmap data
357
358					if(id_two == '2') {		// ASCII greymap
359						int level = 0;
360
361						for (y = 0; y < height; y++) {	
362							BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
363
364							for (x = 0; x < width; x++) {
365								level = GetInt(io, handle);
366								bits[x] = (BYTE)((255 * level) / maxval);
367							}
368						}
369					} else {		// Raw greymap
370						BYTE level = 0;
371
372						for (y = 0; y < height; y++) {		
373							BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
374
375							for (x = 0; x < width; x++) {
376								io->read_proc(&level, 1, 1, handle);
377								bits[x] = (BYTE)((255 * (int)level) / maxval);
378							}
379						}
380					}
381				}
382				else if(image_type == FIT_UINT16) {
383					// write the bitmap data
384
385					if(id_two == '2') {		// ASCII greymap
386						int level = 0;
387
388						for (y = 0; y < height; y++) {	
389							WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
390
391							for (x = 0; x < width; x++) {
392								level = GetInt(io, handle);
393								bits[x] = (WORD)((65535 * (double)level) / maxval);
394							}
395						}
396					} else {		// Raw greymap
397						WORD level = 0;
398
399						for (y = 0; y < height; y++) {		
400							WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
401
402							for (x = 0; x < width; x++) {
403								level = ReadWord(io, handle);
404								bits[x] = (WORD)((65535 * (double)level) / maxval);
405							}
406						}
407					}
408				}
409
410				return dib;
411
412			case '3':
413			case '6':
414				if(image_type == FIT_BITMAP) {
415					// write the bitmap data
416
417					if (id_two == '3') {		// ASCII pixmap
418						int level = 0;
419
420						for (y = 0; y < height; y++) {	
421							BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
422
423							for (x = 0; x < width; x++) {
424								level = GetInt(io, handle);
425								bits[FI_RGBA_RED] = (BYTE)((255 * level) / maxval);		// R
426								level = GetInt(io, handle);
427								bits[FI_RGBA_GREEN] = (BYTE)((255 * level) / maxval);	// G
428								level = GetInt(io, handle);
429								bits[FI_RGBA_BLUE] = (BYTE)((255 * level) / maxval);	// B
430
431								bits += 3;
432							}
433						}
434					}  else {			// Raw pixmap
435						BYTE level = 0;
436
437						for (y = 0; y < height; y++) {	
438							BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
439
440							for (x = 0; x < width; x++) {
441								io->read_proc(&level, 1, 1, handle); 
442								bits[FI_RGBA_RED] = (BYTE)((255 * (int)level) / maxval);	// R
443
444								io->read_proc(&level, 1, 1, handle);
445								bits[FI_RGBA_GREEN] = (BYTE)((255 * (int)level) / maxval);	// G
446
447								io->read_proc(&level, 1, 1, handle);
448								bits[FI_RGBA_BLUE] = (BYTE)((255 * (int)level) / maxval);	// B
449
450								bits += 3;
451							}
452						}
453					}
454				}
455				else if(image_type == FIT_RGB16) {
456					// write the bitmap data
457
458					if (id_two == '3') {		// ASCII pixmap
459						int level = 0;
460
461						for (y = 0; y < height; y++) {	
462							FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
463
464							for (x = 0; x < width; x++) {
465								level = GetInt(io, handle);
466								bits[x].red = (WORD)((65535 * (double)level) / maxval);		// R
467								level = GetInt(io, handle);
468								bits[x].green = (WORD)((65535 * (double)level) / maxval);	// G
469								level = GetInt(io, handle);
470								bits[x].blue = (WORD)((65535 * (double)level) / maxval);	// B
471							}
472						}
473					}  else {			// Raw pixmap
474						WORD level = 0;
475
476						for (y = 0; y < height; y++) {	
477							FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
478
479							for (x = 0; x < width; x++) {
480								level = ReadWord(io, handle);
481								bits[x].red = (WORD)((65535 * (double)level) / maxval);		// R
482								level = ReadWord(io, handle);
483								bits[x].green = (WORD)((65535 * (double)level) / maxval);	// G
484								level = ReadWord(io, handle);
485								bits[x].blue = (WORD)((65535 * (double)level) / maxval);	// B
486							}
487						}
488					}
489				}
490
491				return dib;
492		}
493
494	} catch (const char *text)  {
495		if(dib) FreeImage_Unload(dib);
496
497		if(NULL != text) {
498			switch(id_two)  {
499				case '1':
500				case '4':
501					FreeImage_OutputMessageProc(s_format_id, text);
502					break;
503
504				case '2':
505				case '5':
506					FreeImage_OutputMessageProc(s_format_id, text);
507					break;
508
509				case '3':
510				case '6':
511					FreeImage_OutputMessageProc(s_format_id, text);
512					break;
513			}
514		}
515	}
516		
517	return NULL;
518}
519
520static BOOL DLL_CALLCONV
521Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
522	// ----------------------------------------------------------
523	//   PNM Saving
524	// ----------------------------------------------------------
525	//
526	// Output format :
527	//
528	// Bit depth		flags			file format
529	// -------------    --------------  -----------
530	// 1-bit / pixel	PNM_SAVE_ASCII	PBM (P1)
531	// 1-bit / pixel	PNM_SAVE_RAW	PBM (P4)
532	// 8-bit / pixel	PNM_SAVE_ASCII	PGM (P2)
533	// 8-bit / pixel	PNM_SAVE_RAW	PGM (P5)
534	// 24-bit / pixel	PNM_SAVE_ASCII	PPM (P3)
535	// 24-bit / pixel	PNM_SAVE_RAW	PPM (P6)
536	// ----------------------------------------------------------
537
538	int x, y;
539
540	char buffer[256];	// temporary buffer whose size should be enough for what we need
541
542	if(!dib || !handle) return FALSE;
543	
544	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
545
546	int bpp		= FreeImage_GetBPP(dib);
547	int width	= FreeImage_GetWidth(dib);
548	int height	= FreeImage_GetHeight(dib);
549
550	// Find the appropriate magic number for this file type
551
552	int magic = 0;
553	int maxval = 255;
554
555	switch(image_type) {
556		case FIT_BITMAP:
557			switch (bpp) {
558				case 1 :
559					magic = 1;	// PBM file (B & W)
560					break;
561				case 8 : 			
562					magic = 2;	// PGM file	(Greyscale)
563					break;
564
565				case 24 :
566					magic = 3;	// PPM file (RGB)
567					break;
568
569				default:
570					return FALSE;	// Invalid bit depth
571			}
572			break;
573		
574		case FIT_UINT16:
575			magic = 2;	// PGM file	(Greyscale)
576			maxval = 65535;
577			break;
578
579		case FIT_RGB16:
580			magic = 3;	// PPM file (RGB)
581			maxval = 65535;
582			break;
583
584		default:
585			return FALSE;
586	}
587
588
589	if (flags == PNM_SAVE_RAW)
590		magic += 3;
591
592	// Write the header info
593
594	sprintf(buffer, "P%d\n%d %d\n", magic, width, height);
595	io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
596
597	if (bpp != 1) {
598		sprintf(buffer, "%d\n", maxval);
599		io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
600	}
601
602	// Write the image data
603	///////////////////////
604
605	if(image_type == FIT_BITMAP) {
606		switch(bpp)  {
607			case 24 :            // 24-bit RGB, 3 bytes per pixel
608			{
609				if (flags == PNM_SAVE_RAW)  {
610					for (y = 0; y < height; y++) {
611						// write the scanline to disc
612						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
613
614						for (x = 0; x < width; x++) {
615							io->write_proc(&bits[FI_RGBA_RED], 1, 1, handle);	// R
616							io->write_proc(&bits[FI_RGBA_GREEN], 1, 1, handle);	// G
617							io->write_proc(&bits[FI_RGBA_BLUE], 1, 1, handle);	// B
618
619							bits += 3;
620						}
621					}
622				} else {
623					int length = 0;
624
625					for (y = 0; y < height; y++) {
626						// write the scanline to disc
627						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
628						
629						for (x = 0; x < width; x++) {
630							sprintf(buffer, "%3d %3d %3d ", bits[FI_RGBA_RED], bits[FI_RGBA_GREEN], bits[FI_RGBA_BLUE]);
631
632							io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
633
634							length += 12;
635
636							if(length > 58) {
637								// No line should be longer than 70 characters
638								sprintf(buffer, "\n");
639								io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
640								length = 0;
641							}
642
643							bits += 3;
644						}					
645					}
646
647				}
648			}
649			break;
650
651			case 8:		// 8-bit greyscale
652			{
653				if (flags == PNM_SAVE_RAW)  {
654					for (y = 0; y < height; y++) {
655						// write the scanline to disc
656						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
657
658						for (x = 0; x < width; x++) {
659							io->write_proc(&bits[x], 1, 1, handle);
660						}
661					}
662				} else {
663					int length = 0;
664
665					for (y = 0; y < height; y++) {
666						// write the scanline to disc
667						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
668
669						for (x = 0; x < width; x++) {
670							sprintf(buffer, "%3d ", bits[x]);
671
672							io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
673
674							length += 4;
675
676							if (length > 66) {
677								// No line should be longer than 70 characters
678								sprintf(buffer, "\n");
679								io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
680								length = 0;
681							}
682						}
683					}
684				}
685			}
686			break;
687
688			case 1:		// 1-bit B & W
689			{
690				int color;
691
692				if (flags == PNM_SAVE_RAW)  {
693					for(y = 0; y < height; y++) {
694						// write the scanline to disc
695						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
696
697						for(x = 0; x < (int)FreeImage_GetLine(dib); x++)
698							io->write_proc(&bits[x], 1, 1, handle);
699					}
700				} else  {
701					int length = 0;
702
703					for (y = 0; y < height; y++) {
704						// write the scanline to disc
705						BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
706
707						for (x = 0; x < (int)FreeImage_GetLine(dib) * 8; x++)	{
708							color = (bits[x>>3] & (0x80 >> (x & 0x07))) != 0;
709
710							sprintf(buffer, "%c ", color ? '1':'0');
711
712							io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
713
714							length += 2;
715
716							if (length > 68) {
717								// No line should be longer than 70 characters
718								sprintf(buffer, "\n");
719								io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
720								length = 0;
721							}
722						}
723					}
724				}
725			}
726			
727			break;
728		}
729	} // if(FIT_BITMAP)
730
731	else if(image_type == FIT_UINT16) {		// 16-bit greyscale
732		if (flags == PNM_SAVE_RAW)  {
733			for (y = 0; y < height; y++) {
734				// write the scanline to disc
735				WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
736
737				for (x = 0; x < width; x++) {
738					WriteWord(io, handle, bits[x]);
739				}
740			}
741		} else {
742			int length = 0;
743
744			for (y = 0; y < height; y++) {
745				// write the scanline to disc
746				WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
747
748				for (x = 0; x < width; x++) {
749					sprintf(buffer, "%5d ", bits[x]);
750
751					io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
752
753					length += 6;
754
755					if (length > 64) {
756						// No line should be longer than 70 characters
757						sprintf(buffer, "\n");
758						io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
759						length = 0;
760					}
761				}
762			}
763		}
764	}
765
766	else if(image_type == FIT_RGB16) {		// 48-bit RGB
767		if (flags == PNM_SAVE_RAW)  {
768			for (y = 0; y < height; y++) {
769				// write the scanline to disc
770				FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
771
772				for (x = 0; x < width; x++) {
773					WriteWord(io, handle, bits[x].red);		// R
774					WriteWord(io, handle, bits[x].green);	// G
775					WriteWord(io, handle, bits[x].blue);	// B
776				}
777			}
778		} else {
779			int length = 0;
780
781			for (y = 0; y < height; y++) {
782				// write the scanline to disc
783				FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
784				
785				for (x = 0; x < width; x++) {
786					sprintf(buffer, "%5d %5d %5d ", bits[x].red, bits[x].green, bits[x].blue);
787
788					io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
789
790					length += 18;
791
792					if(length > 52) {
793						// No line should be longer than 70 characters
794						sprintf(buffer, "\n");
795						io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
796						length = 0;
797					}
798				}					
799			}
800
801		}
802	}
803
804	return TRUE;
805}
806
807// ==========================================================
808//   Init
809// ==========================================================
810
811void DLL_CALLCONV
812InitPNM(Plugin *plugin, int format_id) {
813	s_format_id = format_id;
814
815	plugin->format_proc = Format;
816	plugin->description_proc = Description;
817	plugin->extension_proc = Extension;
818	plugin->regexpr_proc = RegExpr;
819	plugin->open_proc = NULL;
820	plugin->close_proc = NULL;
821	plugin->pagecount_proc = NULL;
822	plugin->pagecapability_proc = NULL;
823	plugin->load_proc = Load;
824	plugin->save_proc = Save;
825	plugin->validate_proc = Validate;
826	plugin->mime_proc = MimeType;
827	plugin->supports_export_bpp_proc = SupportsExportDepth;
828	plugin->supports_export_type_proc = SupportsExportType;
829	plugin->supports_icc_profiles_proc = NULL;
830	plugin->supports_no_pixels_proc = SupportsNoPixels;
831}