1 /* This file is part of the GNU plotutils package.  Copyright (C) 1995,
2    1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3 
4    The GNU plotutils package is free software.  You may redistribute it
5    and/or modify it under the terms of the GNU General Public License as
6    published by the Free Software foundation; either version 2, or (at your
7    option) any later version.
8 
9    The GNU plotutils package is distributed in the hope that it will be
10    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with the GNU plotutils package; see the file COPYING.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17    Boston, MA 02110-1301, USA. */
18 
19 /* This file contains a special version of the function
20    _maybe_output_image, which is called by the BitmapPlotter closepl method
21    (see b_closepl.c).  Provided that the current page is the first, this
22    version writes out a PNM (i.e., PBM/PGM/PPM) file for it.  */
23 
24 #include "sys-defines.h"
25 #include "extern.h"
26 #include "xmi.h"
27 
28 /* line lengths in ASCII PBM/PGM/PPM formats (max. no of pixels per line) */
29 #define MAX_PBM_PIXELS_PER_LINE 70
30 #define MAX_PGM_PIXELS_PER_LINE 16
31 #define MAX_PPM_PIXELS_PER_LINE 5
32 
33 /* forward references */
34 static int best_image_type (miPixel **pixmap, int width, int height);
35 
36 /* do a rapid decimal printf of a nonnegative integer, in range 0..999
37    to a character buffer */
38 #define FAST_PRINT(integer_to_print, linebuf, pos) \
39 { \
40   int k, hundreds, tens, ones; \
41   bool force_tens; \
42   \
43   k = (integer_to_print); \
44   hundreds = k / 100; \
45   k -= (100 * hundreds); \
46   tens = k / 10; \
47   ones = k - (10 * tens); \
48   \
49   force_tens = false; \
50   if (hundreds) \
51     { \
52       linebuf[pos++] = hundreds + '0'; \
53       force_tens = true; \
54     } \
55   if (force_tens || tens) \
56     linebuf[pos++] = tens + '0'; \
57   linebuf[pos++] = ones + '0'; \
58 }
59 
60 int
_pl_n_maybe_output_image(S___ (Plotter * _plotter))61 _pl_n_maybe_output_image (S___(Plotter *_plotter))
62 {
63   /* Output the page as a PBM/PGM/PPM file, but only if it's page #1, since
64      PBM/PGM/PPM format supports only a single page of graphics. */
65   if (_plotter->data->page_number == 1)
66     /* emit PBM/PGM/PPM file */
67     _pl_n_write_pnm (S___(_plotter));
68 
69   return true;
70 }
71 
72 /* determine which sort of PNM (i.e. PBM/PGM/PPM) file should be output,
73    and output it */
74 void
_pl_n_write_pnm(S___ (Plotter * _plotter))75 _pl_n_write_pnm (S___(Plotter *_plotter))
76 {
77   int type;			/* 0,1,2 = PBM/PGM/PPM */
78   int width, height;
79   miPixel **pixmap;
80 
81   width = _plotter->b_xn;
82   height = _plotter->b_yn;
83   pixmap = ((miCanvas *)(_plotter->b_canvas))->drawable->pixmap;
84   type = best_image_type (pixmap, width, height);
85 
86   switch (type)
87     {
88     case 0:			/* PBM */
89       _pl_n_write_pbm (S___(_plotter));
90       break;
91     case 1:			/* PGM */
92       _pl_n_write_pgm (S___(_plotter));
93       break;
94     case 2:			/* PPM */
95     default:
96       _pl_n_write_ppm (S___(_plotter));
97       break;
98     }
99 }
100 
101 /* write output (header plus RGB values) in PBM format */
102 void
_pl_n_write_pbm(S___ (Plotter * _plotter))103 _pl_n_write_pbm (S___(Plotter *_plotter))
104 {
105   int i, j;
106   bool portable = _plotter->n_portable_output;
107   miPixel **pixmap = ((miCanvas *)(_plotter->b_canvas))->drawable->pixmap;
108   int width = _plotter->b_xn;
109   int height = _plotter->b_yn;
110   FILE *fp = _plotter->data->outfp;
111 #ifdef LIBPLOTTER
112   ostream *stream = _plotter->data->outstream;
113 #endif
114 
115 #ifdef LIBPLOTTER
116   if (fp == NULL && stream == NULL)
117     return;
118 #else
119   if (fp == NULL)
120     return;
121 #endif
122 
123   if (fp)
124     {
125       if (portable)			/* emit ascii format */
126 	{
127 	  unsigned char linebuf[MAX_PBM_PIXELS_PER_LINE];
128 	  int pos = 0;		/* position in line buffer */
129 
130 	  fprintf (fp, "\
131 P1\n\
132 # CREATOR: GNU libplot drawing library, version %s\n\
133 %d %d\n", PL_LIBPLOT_VER_STRING, width, height);
134 	  for (j = 0; j < height; j++)
135 	    for (i = 0; i < width; i++)
136 	      {
137 		if (pixmap[j][i].u.rgb[0] == 0)
138 		  linebuf[pos++] = '1';	/* 1 = black */
139 		else
140 		  linebuf[pos++] = '0';
141 		if (pos >= MAX_PBM_PIXELS_PER_LINE || i == (width - 1))
142 		  {
143 		    fwrite ((void *)linebuf, sizeof(unsigned char), pos, fp);
144 		    putc ('\n', fp);
145 		    pos = 0;
146 		  }
147 	      }
148 	}
149       else			/* emit binary format */
150 	{
151 	  int bitcount, bytecount;
152 	  unsigned char outbyte, set;
153 	  unsigned char *rowbuf;
154 
155 	  fprintf (fp, "\
156 P4\n\
157 # CREATOR: GNU libplot drawing library, version %s\n\
158 %d %d\n", PL_LIBPLOT_VER_STRING, width, height);
159 
160 	  /* row buffer contains bytes, each representing up to 8 pixels */
161 	  rowbuf = (unsigned char *)_pl_xmalloc (((width + 7) / 8) * sizeof (unsigned char));
162 	  for (j = 0; j < height; j++)
163 	    {
164 	      bitcount = 0;
165 	      bytecount = 0;
166 	      outbyte = 0;
167 	      for (i = 0; i < width; i++)
168 		{
169 		  set = (pixmap[j][i].u.rgb[0] == 0 ? 1 : 0); /* 1 = black */
170 		  outbyte = (outbyte << 1) | set;
171 		  bitcount++;
172 		  if (bitcount == 8)	/* write byte to row (8 bits) */
173 		    {
174 		      rowbuf[bytecount++] = outbyte;
175 		      outbyte = 0;
176 		      bitcount = 0;
177 		    }
178 		}
179 	      if (bitcount)	/* write final byte (not completely filled) */
180 		{
181 		  outbyte = (outbyte << (8 - bitcount));
182 		  rowbuf[bytecount++] = outbyte;
183 		}
184 	      /* emit row of bytes */
185 	      fwrite ((void *)rowbuf, sizeof(unsigned char), bytecount, fp);
186 	    }
187 
188 	  free (rowbuf);
189 	}
190     }
191 #ifdef LIBPLOTTER
192   else if (stream)
193     {
194       if (portable)			/* emit ascii format */
195 	{
196 	  unsigned char linebuf[MAX_PBM_PIXELS_PER_LINE];
197 	  int pos = 0;		/* position in line buffer */
198 
199 	  (*stream) << "\
200 P1\n\
201 # CREATOR: GNU libplot drawing library, version "
202 		 << PL_LIBPLOT_VER_STRING << '\n'
203 		 << width << ' ' << height << '\n';
204 
205 	  for (j = 0; j < height; j++)
206 	    for (i = 0; i < width; i++)
207 	      {
208 		if (pixmap[j][i].u.rgb[0] == 0)
209 		  linebuf[pos++] = '1';	/* 1 = black */
210 		else
211 		  linebuf[pos++] = '0';
212 		if (pos >= MAX_PBM_PIXELS_PER_LINE || i == (width - 1))
213 		  {
214 		    stream->write ((const char *)linebuf, pos);
215 		    stream->put ('\n');
216 
217 		    pos = 0;
218 		  }
219 	      }
220 	}
221       else			/* emit binary format */
222 	{
223 	  int bitcount, bytecount;
224 	  unsigned char outbyte, set;
225 	  unsigned char *rowbuf;
226 
227 	  (*stream) << "\
228 P4\n\
229 # CREATOR: GNU libplot drawing library, version "
230 		 << PL_LIBPLOT_VER_STRING << '\n'
231 		 << width << ' ' << height << '\n';
232 
233 
234 	  /* row buffer contains bytes, each representing up to 8 pixels */
235 	  rowbuf = (unsigned char *)_pl_xmalloc (((width + 7) / 8) * sizeof (unsigned char));
236 	  for (j = 0; j < height; j++)
237 	    {
238 	      bitcount = 0;
239 	      bytecount = 0;
240 	      outbyte = 0;
241 	      for (i = 0; i < width; i++)
242 		{
243 		  set = (pixmap[j][i].u.rgb[0] == 0 ? 1 : 0); /* 1 = black */
244 		  outbyte = (outbyte << 1) | set;
245 		  bitcount++;
246 		  if (bitcount == 8)	/* write byte to row (8 bits) */
247 		    {
248 		      rowbuf[bytecount++] = outbyte;
249 		      outbyte = 0;
250 		      bitcount = 0;
251 		    }
252 		}
253 	      if (bitcount)	/* write final byte (not completely filled) */
254 		{
255 		  outbyte = (outbyte << (8 - bitcount));
256 		  rowbuf[bytecount++] = outbyte;
257 		}
258 	      /* emit row of bytes */
259 	      stream->write ((const char *)rowbuf, bytecount);
260 	    }
261 
262 	  free (rowbuf);
263 	}
264     }
265 #endif
266 }
267 
268 /* write output (header plus RGB values) in PGM format */
269 void
_pl_n_write_pgm(S___ (Plotter * _plotter))270 _pl_n_write_pgm (S___(Plotter *_plotter))
271 {
272   int i, j;
273   bool portable = _plotter->n_portable_output;
274   miPixel **pixmap = ((miCanvas *)(_plotter->b_canvas))->drawable->pixmap;
275   int width = _plotter->b_xn;
276   int height = _plotter->b_yn;
277   FILE *fp = _plotter->data->outfp;
278 #ifdef LIBPLOTTER
279   ostream *stream = _plotter->data->outstream;
280 #endif
281 
282 #ifdef LIBPLOTTER
283   if (fp == NULL && stream == NULL)
284     return;
285 #else
286   if (fp == NULL)
287     return;
288 #endif
289 
290   if (fp)
291     {
292       if (portable)			/* emit ascii format */
293 	{
294 	  /* allow room for 3 decimal digits, plus a space, per pixel */
295 	  unsigned char linebuf[4 * MAX_PGM_PIXELS_PER_LINE];
296 	  int pos = 0;
297 	  int num_pixels = 0;
298 
299 	  fprintf (fp, "\
300 P2\n\
301 # CREATOR: GNU libplot drawing library, version %s\n\
302 %d %d\n\
303 255\n", PL_LIBPLOT_VER_STRING, width, height);
304 
305 	  for (j = 0; j < height; j++)
306 	    for (i = 0; i < width; i++)
307 	      {
308 		/* emit <=3 decimal digits per grayscale pixel */
309 		FAST_PRINT (pixmap[j][i].u.rgb[0], linebuf, pos)
310 		num_pixels++;
311 		if (num_pixels >= MAX_PGM_PIXELS_PER_LINE || i == (width - 1))
312 		  {
313 		    fwrite ((void *)linebuf, sizeof(unsigned char), pos, fp);
314 		    putc ('\n', fp);
315 		    num_pixels = 0;
316 		    pos = 0;
317 		  }
318 		else
319 		  linebuf[pos++] = ' ';
320 	      }
321 	}
322       else				/* emit binary format */
323 	{
324 	  unsigned char *rowbuf;
325 
326 	  rowbuf = (unsigned char *)_pl_xmalloc (width * sizeof (unsigned char));
327 	  fprintf (fp, "\
328 P5\n\
329 # CREATOR: GNU libplot drawing library, version %s\n\
330 %d %d\n\
331 255\n", PL_LIBPLOT_VER_STRING, width, height);
332 
333 	  for (j = 0; j < height; j++)
334 	    {
335 	      for (i = 0; i < width; i++)
336 		rowbuf[i] = pixmap[j][i].u.rgb[0];
337 	      fwrite ((void *)rowbuf, sizeof(unsigned char), width, fp);
338 	    }
339 	  free (rowbuf);
340 	}
341     }
342 #ifdef LIBPLOTTER
343   else if (stream)
344     {
345       if (portable)			/* emit ascii format */
346 	{
347 	  /* allow room for 3 decimal digits, plus a space, per pixel */
348 	  unsigned char linebuf[4 * MAX_PGM_PIXELS_PER_LINE];
349 	  int pos = 0;
350 	  int num_pixels = 0;
351 
352 	  (*stream) << "\
353 P2\n\
354 # CREATOR: GNU libplot drawing library, version "
355 		 << PL_LIBPLOT_VER_STRING << '\n'
356 		 << width << ' ' << height << '\n'
357 	         << "255" << '\n';
358 
359 	  for (j = 0; j < height; j++)
360 	    for (i = 0; i < width; i++)
361 	      {
362 		/* emit <=3 decimal digits per grayscale pixel */
363 		FAST_PRINT (pixmap[j][i].u.rgb[0], linebuf, pos)
364 		num_pixels++;
365 		if (num_pixels >= MAX_PGM_PIXELS_PER_LINE || i == (width - 1))
366 		  {
367 		    stream->write ((const char *)linebuf, pos);
368 		    stream->put ('\n');
369 
370 		    num_pixels = 0;
371 		    pos = 0;
372 		  }
373 		else
374 		  linebuf[pos++] = ' ';
375 	      }
376 	}
377       else				/* emit binary format */
378 	{
379 	  unsigned char *rowbuf;
380 
381 	  (*stream) << "\
382 P5\n\
383 # CREATOR: GNU libplot drawing library, version "
384 		 << PL_LIBPLOT_VER_STRING << '\n'
385 		 << width << ' ' << height << '\n'
386 	         << "255" << '\n';
387 
388 	  rowbuf = (unsigned char *)_pl_xmalloc (width * sizeof (unsigned char));
389 	  for (j = 0; j < height; j++)
390 	    {
391 	      for (i = 0; i < width; i++)
392 		rowbuf[i] = pixmap[j][i].u.rgb[0];
393 	      stream->write ((const char *)rowbuf, width);
394 	    }
395 	  free (rowbuf);
396 	}
397     }
398 #endif
399 }
400 
401 /* write output (header plus RGB values) in PPM format */
402 void
_pl_n_write_ppm(S___ (Plotter * _plotter))403 _pl_n_write_ppm (S___(Plotter *_plotter))
404 {
405   int i, j;
406   bool portable = _plotter->n_portable_output;
407   miPixel **pixmap = ((miCanvas *)(_plotter->b_canvas))->drawable->pixmap;
408   int width = _plotter->b_xn;
409   int height = _plotter->b_yn;
410   FILE *fp = _plotter->data->outfp;
411 #ifdef LIBPLOTTER
412   ostream *stream = _plotter->data->outstream;
413 #endif
414 
415 #ifdef LIBPLOTTER
416   if (fp == NULL && stream == NULL)
417     return;
418 #else
419   if (fp == NULL)
420     return;
421 #endif
422 
423   if (fp)
424     {
425       if (portable)			/* emit ascii format */
426 	{
427 	  /* allow room for 3 decimal digits, plus a space, per pixel */
428 	  unsigned char linebuf[4 * MAX_PGM_PIXELS_PER_LINE];
429 	  int pos = 0;
430 	  int num_pixels = 0;
431 
432 	  fprintf (fp, "\
433 P3\n\
434 # CREATOR: GNU libplot drawing library, version %s\n\
435 %d %d\n\
436 255\n", PL_LIBPLOT_VER_STRING, width, height);
437 
438 	  for (j = 0; j < height; j++)
439 	    for (i = 0; i < width; i++)
440 	      {
441 		/* emit <=3 decimal digits per RGB component */
442 		FAST_PRINT (pixmap[j][i].u.rgb[0], linebuf, pos)
443 		linebuf[pos++] = ' ';
444 		FAST_PRINT (pixmap[j][i].u.rgb[1], linebuf, pos)
445 		linebuf[pos++] = ' ';
446 		FAST_PRINT (pixmap[j][i].u.rgb[2], linebuf, pos)
447 		num_pixels++;
448 		if (num_pixels >= MAX_PPM_PIXELS_PER_LINE || i == (width - 1))
449 		  {
450 		    fwrite ((void *)linebuf, sizeof(unsigned char), pos, fp);
451 		    putc ('\n', fp);
452 		    num_pixels = 0;
453 		    pos = 0;
454 		  }
455 		else
456 		  linebuf[pos++] = ' ';
457 	      }
458 	}
459       else			/* emit binary format */
460 	{
461 	  unsigned char *rowbuf;
462 	  int component;
463 
464 	  fprintf (fp, "\
465 P6\n\
466 # CREATOR: GNU libplot drawing library, version %s\n\
467 %d %d\n\
468 255\n", PL_LIBPLOT_VER_STRING, width, height);
469 
470 	  rowbuf = (unsigned char *)_pl_xmalloc (3 * width * sizeof (unsigned char));
471 	  for (j = 0; j < height; j++)
472 	    {
473 	      for (i = 0; i < width; i++)
474 		for (component = 0; component < 3; component++)
475 		  rowbuf[3 * i + component] = pixmap[j][i].u.rgb[component];
476 	      fwrite ((void *)rowbuf, sizeof(unsigned char), 3 * width, fp);
477 	    }
478 	  free (rowbuf);
479 	}
480     }
481 #ifdef LIBPLOTTER
482   else if (stream)
483     {
484       if (portable)			/* emit ascii format */
485 	{
486 	  /* allow room for 3 decimal digits, plus a space, per pixel */
487 	  unsigned char linebuf[4 * MAX_PGM_PIXELS_PER_LINE];
488 	  int pos = 0;
489 	  int num_pixels = 0;
490 
491 	  (*stream) << "\
492 P3\n\
493 # CREATOR: GNU libplot drawing library, version "
494 		 << PL_LIBPLOT_VER_STRING << '\n'
495 		 << width << ' ' << height << '\n'
496 	         << "255" << '\n';
497 
498 	  for (j = 0; j < height; j++)
499 	    for (i = 0; i < width; i++)
500 	      {
501 		/* emit <=3 decimal digits per RGB component */
502 		FAST_PRINT (pixmap[j][i].u.rgb[0], linebuf, pos)
503 		linebuf[pos++] = ' ';
504 		FAST_PRINT (pixmap[j][i].u.rgb[1], linebuf, pos)
505 		linebuf[pos++] = ' ';
506 		FAST_PRINT (pixmap[j][i].u.rgb[2], linebuf, pos)
507 		num_pixels++;
508 		if (num_pixels >= MAX_PPM_PIXELS_PER_LINE || i == (width - 1))
509 		  {
510 		    stream->write ((const char *)linebuf, pos);
511 		    stream->put ('\n');
512 
513 		    num_pixels = 0;
514 		    pos = 0;
515 		  }
516 		else
517 		  linebuf[pos++] = ' ';
518 	      }
519 	}
520       else			/* emit binary format */
521 	{
522 	  unsigned char *rowbuf;
523 	  int component;
524 
525 	  (*stream) << "\
526 P6\n\
527 # CREATOR: GNU libplot drawing library, version "
528 		 << PL_LIBPLOT_VER_STRING << '\n'
529 		 << width << ' ' << height << '\n'
530 	         << "255" << '\n';
531 
532 	  rowbuf = (unsigned char *)_pl_xmalloc (3 * width * sizeof (unsigned char));
533 	  for (j = 0; j < height; j++)
534 	    {
535 	      for (i = 0; i < width; i++)
536 		for (component = 0; component < 3; component++)
537 		  rowbuf[3 * i + component] = pixmap[j][i].u.rgb[component];
538 	      stream->write ((const char *)rowbuf, 3 * width);
539 	    }
540 	  free (rowbuf);
541 	}
542     }
543 #endif
544 }
545 
546 /* return best type for writing an image (0=mono, 1=grey, 2=color) */
547 static int
best_image_type(miPixel ** pixmap,int width,int height)548 best_image_type (miPixel **pixmap, int width, int height)
549 {
550   int i, j;
551   int type = 0;			/* default is mono */
552 
553   for (j = 0; j < height; j++)
554     for (i = 0; i < width; i++)
555       {
556 	unsigned char red, green, blue;
557 
558 	red = pixmap[j][i].u.rgb[0];
559 	green = pixmap[j][i].u.rgb[1];
560 	blue = pixmap[j][i].u.rgb[2];
561 	if (type == 0)		/* up to now, all pixels are black or white */
562 	  {
563 	    if (! ((red == (unsigned char)0 && green == (unsigned char)0
564 		    && blue == (unsigned char)0)
565 		   || (red == (unsigned char)255 && green == (unsigned char)255
566 		    && blue == (unsigned char)255)))
567 	      {
568 		if (red == green && red == blue)
569 		  type = 1;	/* need grey */
570 		else
571 		  {
572 		    type = 2;	/* need color */
573 		    return type;
574 		  }
575 	      }
576 	  }
577 	else if (type == 1)
578 	  {
579 	    if (red != green || red != blue)
580 	      {
581 		type = 2;	/* need color */
582 		return type;
583 	      }
584 	  }
585       }
586   return type;
587 }
588