1 /* libppm2.c - ppm utility library part 2
2 **
3 ** Copyright (C) 1989 by Jef Poskanzer.
4 **
5 ** Permission to use, copy, modify, and distribute this software and its
6 ** documentation for any purpose and without fee is hereby granted, provided
7 ** that the above copyright notice appear in all copies and that both that
8 ** copyright notice and this permission notice appear in supporting
9 ** documentation.  This software is provided "as is" without express or
10 ** implied warranty.
11 */
12 
13 #include <string.h>
14 #include <errno.h>
15 
16 
17 #include "netpbm/pm_c_util.h"
18 #include "netpbm/mallocvar.h"
19 #include "ppm.h"
20 
21 void
ppm_writeppminit(FILE * const fileP,int const cols,int const rows,pixval const maxval,int const forceplain)22 ppm_writeppminit(FILE*  const fileP,
23                  int    const cols,
24                  int    const rows,
25                  pixval const maxval,
26                  int    const forceplain) {
27 
28     bool const plainFormat = forceplain || pm_plain_output;
29 
30     if (maxval > PPM_OVERALLMAXVAL && !plainFormat)
31         pm_error("too-large maxval passed to ppm_writeppminit(): %d."
32                  "Maximum allowed by the PPM format is %d.",
33                  maxval, PPM_OVERALLMAXVAL);
34 
35     fprintf(fileP, "%c%c\n%d %d\n%d\n",
36             PPM_MAGIC1,
37             plainFormat || maxval >= 1<<16 ? PPM_MAGIC2 : RPPM_MAGIC2,
38             cols, rows, maxval );
39 }
40 
41 
42 
43 static void
putus(unsigned short const n,FILE * const fileP)44 putus(unsigned short const n,
45       FILE *         const fileP) {
46 
47     if (n >= 10)
48         putus(n / 10, fileP);
49     putc('0' + n % 10, fileP);
50 }
51 
52 
53 
54 static void
format1bpsRow(const pixel * const pixelrow,unsigned int const cols,unsigned char * const rowBuffer)55 format1bpsRow(const pixel *   const pixelrow,
56               unsigned int    const cols,
57               unsigned char * const rowBuffer) {
58 
59     /* single byte samples. */
60 
61     unsigned int col;
62     unsigned int bufferCursor;
63 
64     bufferCursor = 0;
65 
66     for (col = 0; col < cols; ++col) {
67         rowBuffer[bufferCursor++] = PPM_GETR(pixelrow[col]);
68         rowBuffer[bufferCursor++] = PPM_GETG(pixelrow[col]);
69         rowBuffer[bufferCursor++] = PPM_GETB(pixelrow[col]);
70     }
71 }
72 
73 
74 
75 
76 static void
format2bpsRow(const pixel * const pixelrow,unsigned int const cols,unsigned char * const rowBuffer)77 format2bpsRow(const pixel *   const pixelrow,
78               unsigned int    const cols,
79               unsigned char * const rowBuffer) {
80 
81     /* two byte samples. */
82 
83     unsigned int col;
84     unsigned int bufferCursor;
85 
86     bufferCursor = 0;
87 
88     for (col = 0; col < cols; ++col) {
89         pixval const r = PPM_GETR(pixelrow[col]);
90         pixval const g = PPM_GETG(pixelrow[col]);
91         pixval const b = PPM_GETB(pixelrow[col]);
92 
93         rowBuffer[bufferCursor++] = r >> 8;
94         rowBuffer[bufferCursor++] = (unsigned char)r;
95         rowBuffer[bufferCursor++] = g >> 8;
96         rowBuffer[bufferCursor++] = (unsigned char)g;
97         rowBuffer[bufferCursor++] = b >> 8;
98         rowBuffer[bufferCursor++] = (unsigned char)b;
99     }
100 }
101 
102 
103 
104 static void
ppm_writeppmrowraw(FILE * const fileP,const pixel * const pixelrow,unsigned int const cols,pixval const maxval)105 ppm_writeppmrowraw(FILE *        const fileP,
106                    const pixel * const pixelrow,
107                    unsigned int  const cols,
108                    pixval        const maxval ) {
109 
110     unsigned int const bytesPerSample = maxval < 256 ? 1 : 2;
111     unsigned int const bytesPerRow    = cols * 3 * bytesPerSample;
112 
113     unsigned char * rowBuffer;
114     ssize_t rc;
115 
116     MALLOCARRAY(rowBuffer, bytesPerRow);
117 
118     if (rowBuffer == NULL)
119         pm_error("Unable to allocate memory for row buffer "
120                  "for %u columns", cols);
121 
122     if (maxval < 256)
123         format1bpsRow(pixelrow, cols, rowBuffer);
124     else
125         format2bpsRow(pixelrow, cols, rowBuffer);
126 
127     rc = fwrite(rowBuffer, 1, bytesPerRow, fileP);
128 
129     if (rc < 0)
130         pm_error("Error writing row.  fwrite() errno=%d (%s)",
131                  errno, strerror(errno));
132     else {
133         size_t const bytesWritten = rc;
134 
135         if (bytesWritten != bytesPerRow)
136             pm_error("Error writing row.  Short write of %u bytes "
137                      "instead of %u", (unsigned)bytesWritten, bytesPerRow);
138     }
139     free(rowBuffer);
140 }
141 
142 
143 
144 
145 static void
ppm_writeppmrowplain(FILE * const fileP,const pixel * const pixelrow,unsigned int const cols,pixval const maxval)146 ppm_writeppmrowplain(FILE *        const fileP,
147                      const pixel * const pixelrow,
148                      unsigned int  const cols,
149                      pixval        const maxval) {
150 
151     unsigned int col;
152     unsigned int charcount;
153 
154     charcount = 0;
155 
156     for (col = 0; col < cols; ++col) {
157         if (charcount >= 65) {
158             putc('\n', fileP);
159             charcount = 0;
160         } else if (charcount > 0) {
161             putc(' ', fileP);
162             putc(' ', fileP);
163             charcount += 2;
164         }
165         putus(PPM_GETR(pixelrow[col]), fileP);
166         putc(' ', fileP);
167         putus(PPM_GETG(pixelrow[col]), fileP);
168         putc(' ', fileP);
169         putus(PPM_GETB(pixelrow[col]), fileP);
170         charcount += 11;
171     }
172     if (charcount > 0)
173         putc('\n', fileP);
174 }
175 
176 
177 
178 void
ppm_writeppmrow(FILE * const fileP,const pixel * const pixelrow,int const cols,pixval const maxval,int const forceplain)179 ppm_writeppmrow(FILE *        const fileP,
180                 const pixel * const pixelrow,
181                 int           const cols,
182                 pixval        const maxval,
183                 int           const forceplain) {
184 
185     if (forceplain || pm_plain_output || maxval >= 1<<16)
186         ppm_writeppmrowplain(fileP, pixelrow, cols, maxval);
187     else
188         ppm_writeppmrowraw(fileP, pixelrow, cols, maxval);
189 }
190 
191 
192 
193 void
ppm_writeppm(FILE * const file,pixel ** const pixels,int const cols,int const rows,pixval const maxval,int const forceplain)194 ppm_writeppm(FILE *  const file,
195              pixel** const pixels,
196              int     const cols,
197              int     const rows,
198              pixval  const maxval,
199              int     const forceplain)  {
200     int row;
201 
202     ppm_writeppminit(file, cols, rows, maxval, forceplain);
203 
204     for (row = 0; row < rows; ++row)
205         ppm_writeppmrow(file, pixels[row], cols, maxval, forceplain);
206 }
207