1 /*============================================================================
2                                   libpamwrite.c
3 ==============================================================================
4    These are the library functions, which belong in the libnetpbm library,
5    that deal with writing the PAM (Portable Arbitrary Format) image format
6    raster (not the header).
7 
8    This file was originally written by Bryan Henderson and is contributed
9    to the public domain by him and subsequent authors.
10 =============================================================================*/
11 
12 /* See pmfileio.c for the complicated explanation of this 32/64 bit file
13    offset stuff.
14 */
15 #define _FILE_OFFSET_BITS 64
16 #define _LARGE_FILES
17 
18 #include <string.h>
19 #include <stdio.h>
20 #include <limits.h>
21 #include <assert.h>
22 #include <math.h>
23 
24 #include "netpbm/pm_config.h"
25 #include "netpbm/pm_c_util.h"
26 
27 #include "pam.h"
28 
29 
30 static __inline__ unsigned int
samplesPerPlainLine(sample const maxval,unsigned int const depth,unsigned int const lineLength)31 samplesPerPlainLine(sample       const maxval,
32                     unsigned int const depth,
33                     unsigned int const lineLength) {
34 /*----------------------------------------------------------------------------
35    Return the minimum number of samples that should go in a line
36    'lineLength' characters long in a plain format non-PBM PNM image
37    with depth 'depth' and maxval 'maxval'.
38 
39    Note that this number is just for aesthetics; the Netpbm formats allow
40    any number of samples per line.
41 -----------------------------------------------------------------------------*/
42     unsigned int const digitsForMaxval = (unsigned int)
43         (log(maxval + 0.1 ) / log(10.0));
44         /* Number of digits maxval has in decimal */
45         /* +0.1 is an adjustment to overcome precision problems */
46     unsigned int const fit = lineLength / (digitsForMaxval + 1);
47         /* Number of maxval-sized samples that fit in a line */
48     unsigned int const retval = (fit > depth) ? (fit - (fit % depth)) : fit;
49         /* 'fit', rounded down to a multiple of depth, if possible */
50 
51     return retval;
52 }
53 
54 
55 
56 static void
writePamPlainPbmRow(const struct pam * const pamP,const tuple * const tuplerow)57 writePamPlainPbmRow(const struct pam *  const pamP,
58                     const tuple *       const tuplerow) {
59 
60     int col;
61     unsigned int const samplesPerLine = 70;
62 
63     for (col = 0; col < pamP->width; ++col)
64         fprintf(pamP->file,
65                 ((col+1) % samplesPerLine == 0 || col == pamP->width-1)
66                     ? "%1u\n" : "%1u",
67                 tuplerow[col][0] == PAM_PBM_BLACK ? PBM_BLACK : PBM_WHITE);
68 }
69 
70 
71 
72 static void
writePamPlainRow(const struct pam * const pamP,const tuple * const tuplerow)73 writePamPlainRow(const struct pam *  const pamP,
74                     const tuple *       const tuplerow) {
75 
76     int const samplesPerLine =
77         samplesPerPlainLine(pamP->maxval, pamP->depth, 79);
78 
79     int col;
80     unsigned int samplesInCurrentLine;
81         /* number of samples written from start of line  */
82 
83     samplesInCurrentLine = 0;
84 
85     for (col = 0; col < pamP->width; ++col) {
86         unsigned int plane;
87         for (plane = 0; plane < pamP->depth; ++plane){
88             fprintf(pamP->file, "%lu ",tuplerow[col][plane]);
89 
90             ++samplesInCurrentLine;
91 
92             if (samplesInCurrentLine >= samplesPerLine) {
93                 fprintf(pamP->file, "\n");
94                 samplesInCurrentLine = 0;
95             }
96         }
97     }
98     fprintf(pamP->file, "\n");
99 }
100 
101 
102 
103 static void
formatPbmRow(const struct pam * const pamP,const tuple * const tuplerow,unsigned char * const outbuf,unsigned int * const rowSizeP)104 formatPbmRow(const struct pam * const pamP,
105              const tuple *      const tuplerow,
106              unsigned char *    const outbuf,
107              unsigned int *     const rowSizeP) {
108 
109     unsigned char accum;
110     int col;
111 
112     accum = 0;  /* initial value */
113 
114     for (col=0; col < pamP->width; ++col) {
115         accum |=
116             (tuplerow[col][0] == PAM_PBM_BLACK ? PBM_BLACK : PBM_WHITE)
117                 << (7-col%8);
118         if (col%8 == 7) {
119                 outbuf[col/8] = accum;
120                 accum = 0;
121         }
122     }
123     if (pamP->width % 8 != 0) {
124         unsigned int const lastByteIndex = pamP->width/8;
125         outbuf[lastByteIndex] = accum;
126         *rowSizeP = lastByteIndex + 1;
127     } else
128         *rowSizeP = pamP->width/8;
129 }
130 
131 
132 
133 /* Though it is possible to simplify the sampleToBytesN() and
134    formatNBpsRow() functions into a single routine that handles all
135    sample widths, we value efficiency higher here.  Earlier versions
136    of Netpbm (before 10.25) did that, with a loop, and performance
137    suffered visibly.
138 */
139 
140 static __inline__ void
sampleToBytes2(unsigned char buf[2],sample const sampleval)141 sampleToBytes2(unsigned char       buf[2],
142                sample        const sampleval) {
143 
144     buf[0] = (sampleval >> 8) & 0xff;
145     buf[1] = (sampleval >> 0) & 0xff;
146 }
147 
148 
149 
150 static __inline__ void
sampleToBytes3(unsigned char buf[3],sample const sampleval)151 sampleToBytes3(unsigned char       buf[3],
152                sample        const sampleval) {
153 
154     buf[0] = (sampleval >> 16) & 0xff;
155     buf[1] = (sampleval >>  8) & 0xff;
156     buf[2] = (sampleval >>  0) & 0xff;
157 }
158 
159 
160 
161 static __inline__ void
sampleToBytes4(unsigned char buf[4],sample const sampleval)162 sampleToBytes4(unsigned char       buf[4],
163                sample        const sampleval) {
164 
165     buf[0] = (sampleval >> 24 ) & 0xff;
166     buf[1] = (sampleval >> 16 ) & 0xff;
167     buf[2] = (sampleval >>  8 ) & 0xff;
168     buf[3] = (sampleval >>  0 ) & 0xff;
169 }
170 
171 
172 
173 static __inline__ void
format1BpsRow(const struct pam * const pamP,const tuple * const tuplerow,unsigned char * const outbuf,unsigned int * const rowSizeP)174 format1BpsRow(const struct pam * const pamP,
175               const tuple *      const tuplerow,
176               unsigned char *    const outbuf,
177               unsigned int *     const rowSizeP) {
178 /*----------------------------------------------------------------------------
179    Create the image of a row in the raster of a raw format Netpbm
180    image that has one byte per sample (ergo not PBM).
181 
182    Put the image at *outbuf; put the number of bytes of it at *rowSizeP.
183 -----------------------------------------------------------------------------*/
184     int col;
185     unsigned int bufferCursor;
186 
187     bufferCursor = 0;  /* initial value */
188 
189     for (col = 0; col < pamP->width; ++col) {
190         unsigned int plane;
191         for (plane=0; plane < pamP->depth; ++plane)
192             outbuf[bufferCursor++] = (unsigned char)tuplerow[col][plane];
193     }
194     *rowSizeP = pamP->width * 1 * pamP->depth;
195 }
196 
197 
198 
199 static __inline__ void
format2BpsRow(const struct pam * const pamP,const tuple * const tuplerow,unsigned char * const outbuf,unsigned int * const rowSizeP)200 format2BpsRow(const struct pam * const pamP,
201               const tuple *      const tuplerow,
202               unsigned char *    const outbuf,
203               unsigned int *     const rowSizeP) {
204 /*----------------------------------------------------------------------------
205   Analogous to format1BpsRow().
206 -----------------------------------------------------------------------------*/
207     unsigned char (* const ob)[2] = (unsigned char (*)[2]) outbuf;
208 
209     int col;
210     unsigned int bufferCursor;
211 
212     bufferCursor = 0;  /* initial value */
213 
214     for (col=0; col < pamP->width; ++col) {
215         unsigned int plane;
216         for (plane = 0; plane < pamP->depth; ++plane)
217             sampleToBytes2(ob[bufferCursor++], tuplerow[col][plane]);
218     }
219 
220     *rowSizeP = pamP->width * 2 * pamP->depth;
221 }
222 
223 
224 
225 static __inline__ void
format3BpsRow(const struct pam * const pamP,const tuple * const tuplerow,unsigned char * const outbuf,unsigned int * const rowSizeP)226 format3BpsRow(const struct pam * const pamP,
227               const tuple *      const tuplerow,
228               unsigned char *    const outbuf,
229               unsigned int *     const rowSizeP) {
230 /*----------------------------------------------------------------------------
231   Analogous to format1BpsRow().
232 -----------------------------------------------------------------------------*/
233     unsigned char (* const ob)[3] = (unsigned char (*)[3]) outbuf;
234 
235     int col;
236     unsigned int bufferCursor;
237 
238     bufferCursor = 0;  /* initial value */
239 
240     for (col=0; col < pamP->width; ++col) {
241         unsigned int plane;
242         for (plane = 0; plane < pamP->depth; ++plane)
243             sampleToBytes3(ob[bufferCursor++], tuplerow[col][plane]);
244     }
245 
246     *rowSizeP = pamP->width * 3 * pamP->depth;
247 }
248 
249 
250 
251 static __inline__ void
format4BpsRow(const struct pam * const pamP,const tuple * const tuplerow,unsigned char * const outbuf,unsigned int * const rowSizeP)252 format4BpsRow(const struct pam * const pamP,
253               const tuple *      const tuplerow,
254               unsigned char *    const outbuf,
255               unsigned int *     const rowSizeP) {
256 /*----------------------------------------------------------------------------
257   Analogous to format1BpsRow().
258 -----------------------------------------------------------------------------*/
259     unsigned char (* const ob)[4] = (unsigned char (*)[4]) outbuf;
260 
261     int col;
262     unsigned int bufferCursor;
263 
264     bufferCursor = 0;  /* initial value */
265 
266     for (col=0; col < pamP->width; ++col) {
267         unsigned int plane;
268         for (plane = 0; plane < pamP->depth; ++plane)
269             sampleToBytes4(ob[bufferCursor++], tuplerow[col][plane]);
270     }
271 
272     *rowSizeP = pamP->width * 4 * pamP->depth;
273 }
274 
275 
276 
277 void
pnm_formatpamrow(const struct pam * const pamP,const tuple * const tuplerow,unsigned char * const outbuf,unsigned int * const rowSizeP)278 pnm_formatpamrow(const struct pam * const pamP,
279                  const tuple *      const tuplerow,
280                  unsigned char *    const outbuf,
281                  unsigned int *     const rowSizeP) {
282 /*----------------------------------------------------------------------------
283    Create the image of a row in the raster of a raw (not plain) format
284    Netpbm image, as described by *pamP and tuplerow[].  Put the image
285    at *outbuf.
286 
287    'outbuf' must be the address of space allocated with pnm_allocrowimage().
288 
289    We return as *rowSizeP the number of bytes in the row image.
290 -----------------------------------------------------------------------------*/
291     if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE)
292         formatPbmRow(pamP, tuplerow, outbuf, rowSizeP);
293     else {
294         switch(pamP->bytes_per_sample){
295         case 1: format1BpsRow(pamP, tuplerow, outbuf, rowSizeP); break;
296         case 2: format2BpsRow(pamP, tuplerow, outbuf, rowSizeP); break;
297         case 3: format3BpsRow(pamP, tuplerow, outbuf, rowSizeP); break;
298         case 4: format4BpsRow(pamP, tuplerow, outbuf, rowSizeP); break;
299         default:
300             pm_error("invalid bytes per sample passed to "
301                      "pnm_formatpamrow(): %u",  pamP->bytes_per_sample);
302         }
303     }
304 }
305 
306 
307 
308 static void
writePamRawRow(const struct pam * const pamP,const tuple * const tuplerow,unsigned int const count)309 writePamRawRow(const struct pam * const pamP,
310                const tuple *      const tuplerow,
311                unsigned int       const count) {
312 /*----------------------------------------------------------------------------
313    Write multiple ('count') copies of the same row ('tuplerow') to the file,
314    in raw (not plain) format.
315 -----------------------------------------------------------------------------*/
316     jmp_buf jmpbuf;
317     jmp_buf * origJmpbufP;
318     unsigned int rowImageSize;
319     unsigned char * outbuf;  /* malloc'ed */
320 
321     outbuf = pnm_allocrowimage(pamP);
322 
323     pnm_formatpamrow(pamP, tuplerow, outbuf, &rowImageSize);
324 
325     if (setjmp(jmpbuf) != 0) {
326         pnm_freerowimage(outbuf);
327         pm_setjmpbuf(origJmpbufP);
328         pm_longjmp();
329     } else {
330         unsigned int i;
331 
332         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
333 
334         for (i = 0; i < count; ++i) {
335             size_t bytesWritten;
336 
337             bytesWritten = fwrite(outbuf, 1, rowImageSize, pamP->file);
338             if (bytesWritten != rowImageSize)
339                 pm_error("fwrite() failed to write an image row to the file.  "
340                          "errno=%d (%s)", errno, strerror(errno));
341         }
342         pm_setjmpbuf(origJmpbufP);
343     }
344     pnm_freerowimage(outbuf);
345 }
346 
347 
348 
349 void
pnm_writepamrow(const struct pam * const pamP,const tuple * const tuplerow)350 pnm_writepamrow(const struct pam * const pamP,
351                 const tuple *      const tuplerow) {
352 
353     /* For speed, we don't check any of the inputs for consistency
354        here (unless it's necessary to avoid crashing).  Any consistency
355        checking should have been done by a prior call to
356        pnm_writepaminit().
357     */
358 
359     if (pamP->format == PAM_FORMAT || !(pm_plain_output || pamP->plainformat))
360         writePamRawRow(pamP, tuplerow, 1);
361     else {
362         switch (PAM_FORMAT_TYPE(pamP->format)) {
363         case PBM_TYPE:
364             writePamPlainPbmRow(pamP, tuplerow);
365             break;
366         case PGM_TYPE:
367         case PPM_TYPE:
368             writePamPlainRow(pamP, tuplerow);
369             break;
370         case PAM_TYPE:
371             assert(false);
372             break;
373         default:
374             pm_error("Invalid 'format' value %u in pam structure",
375                      pamP->format);
376         }
377     }
378 }
379 
380 
381 
382 void
pnm_writepamrowmult(const struct pam * const pamP,const tuple * const tuplerow,unsigned int const count)383 pnm_writepamrowmult(const struct pam * const pamP,
384                     const tuple *      const tuplerow,
385                     unsigned int       const count) {
386 /*----------------------------------------------------------------------------
387    Write multiple ('count') copies of the same row ('tuplerow') to the file.
388 -----------------------------------------------------------------------------*/
389    if (pm_plain_output || pamP->plainformat) {
390        unsigned int i;
391        for (i = 0; i < count; ++i)
392            pnm_writepamrow(pamP, tuplerow);
393    } else
394        /* Simple common case - use fastpath */
395        writePamRawRow(pamP, tuplerow, count);
396 }
397 
398 
399 
400 void
pnm_writepam(struct pam * const pamP,tuple ** const tuplearray)401 pnm_writepam(struct pam * const pamP,
402              tuple **     const tuplearray) {
403 
404     int row;
405 
406     pnm_writepaminit(pamP);
407 
408     for (row = 0; row < pamP->height; ++row)
409         pnm_writepamrow(pamP, tuplearray[row]);
410 }
411 
412 
413