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