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