1 /* ----------------------------------------------------------------------
2  *
3  * Convert a Netpbm file to the GNU Octave image format
4  * by Scott Pakin <scott+pbm@pakin.org>
5  *
6  * ----------------------------------------------------------------------
7  *
8  * Copyright information is at end of file.
9  * ----------------------------------------------------------------------
10  */
11 
12 #include <assert.h>
13 #include <stdio.h>
14 
15 #include "pm_c_util.h"
16 #include "mallocvar.h"
17 #include "nstring.h"
18 #include "pam.h"
19 #include "pammap.h"
20 
21 typedef struct {
22     double comp[3];
23         /* comp[0] is red; comp[1] is green; comp[2] is blue */
24 } octaveColor;
25 
26 typedef struct {
27     struct pam pam;
28     unsigned int nColors;
29     tuplehash hash;
30     unsigned int paletteAlloc;
31         /* 'palette' array has this many slots allocated.  Only the first
32            'nColors' are meaningful.
33         */
34     octaveColor * palette;
35     double normalizer;
36         /* 1/maxval */
37 } cmap;
38 
39 
40 
41 static void
initCmap(cmap * const cmapP,sample const maxval)42 initCmap(cmap * const cmapP,
43          sample const maxval) {
44 
45     cmapP->pam.size             = sizeof(cmapP->pam.size);
46     cmapP->pam.len              = PAM_STRUCT_SIZE(tuple_type);
47     cmapP->pam.depth            = 3;
48     cmapP->pam.maxval           = maxval;
49     cmapP->pam.bytes_per_sample = pnm_bytespersample(maxval);
50 
51     cmapP->normalizer   = 1.0/maxval;
52     cmapP->nColors      = 0;
53     cmapP->paletteAlloc = 0;
54     cmapP->palette      = NULL;
55     cmapP->hash         = pnm_createtuplehash();
56 }
57 
58 
59 
60 static void
termCmap(cmap * const cmapP)61 termCmap(cmap * const cmapP) {
62     pnm_destroytuplehash(cmapP->hash);
63 
64     free(cmapP->palette);
65 }
66 
67 
68 
69 static void
findOrAddColor(tuple const color,cmap * const cmapP,unsigned int * const colorIndexP)70 findOrAddColor(tuple          const color,
71                cmap *         const cmapP,
72                unsigned int * const colorIndexP) {
73 /*----------------------------------------------------------------------------
74   Return as *colorIndexP the colormap index of color 'color' in
75   colormap *cmapP.  If the color isn't in the map, give it a new
76   colormap index, put it in the colormap, and return that.
77 -----------------------------------------------------------------------------*/
78     int found;
79     int colorIndex;
80 
81     pnm_lookuptuple(&cmapP->pam, cmapP->hash, color, &found, &colorIndex);
82 
83     if (!found) {
84         int fits;
85         unsigned int plane;
86 
87         colorIndex = cmapP->nColors++;
88 
89         if (cmapP->nColors > cmapP->paletteAlloc) {
90             cmapP->paletteAlloc *= 2;
91             REALLOCARRAY(cmapP->palette, cmapP->nColors);
92         }
93         for (plane = 0; plane < 3; ++plane)
94             cmapP->palette[colorIndex].comp[plane] =
95                 color[plane] * cmapP->normalizer;
96 
97         pnm_addtotuplehash(&cmapP->pam, cmapP->hash, color, colorIndex, &fits);
98 
99         if (!fits)
100             pm_error("Out of memory constructing color map, on %uth color",
101                      cmapP->nColors);
102     }
103     *colorIndexP = colorIndex;
104 }
105 
106 
107 
108 static void
outputColormap(FILE * const ofP,cmap const cmap)109 outputColormap(FILE * const ofP,
110                cmap   const cmap) {
111 /*----------------------------------------------------------------------------
112   Output the colormap as a GNU Octave matrix.
113 -----------------------------------------------------------------------------*/
114     unsigned int colorIndex;
115 
116     fprintf(ofP, "# name: map\n");
117     fprintf(ofP, "# type: matrix\n");
118     fprintf(ofP, "# rows: %u\n", cmap.nColors);
119     fprintf(ofP, "# columns: 3\n");
120 
121     for (colorIndex = 0; colorIndex < cmap.nColors; ++colorIndex) {
122         unsigned int plane;
123 
124         assert(cmap.pam.depth == 3);
125 
126         for (plane = 0; plane < 3; ++plane)
127             fprintf(ofP, " %.10f", cmap.palette[colorIndex].comp[plane]);
128 
129         fprintf(ofP, "\n");
130     }
131 }
132 
133 
134 
135 static void
convertToOctave(FILE * const ifP,FILE * const ofP)136 convertToOctave(FILE * const ifP,
137                 FILE * const ofP) {
138 
139     struct pam inpam;
140     tuple * inRow;
141     unsigned int row;
142     cmap cmap;
143 
144     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
145 
146     pnm_setminallocationdepth(&inpam, 3);
147 
148     /* Output the image as a GNU Octave matrix.  For each row of the
149      * input file we immediately output indexes into the colormap then,
150      * when we're finished, we output the colormap as a second
151      * matrix. */
152     fprintf(ofP, "# name: img\n");
153     fprintf(ofP, "# type: matrix\n");
154     fprintf(ofP, "# rows: %u\n", inpam.height);
155     fprintf(ofP, "# columns: %u\n", inpam.width);
156 
157     initCmap(&cmap, inpam.maxval);
158 
159     inRow = pnm_allocpamrow(&inpam);
160     for (row = 0; row < inpam.height; ++row) {
161         unsigned int col;
162         pnm_readpamrow(&inpam, inRow);
163 
164         pnm_makerowrgb(&inpam, inRow);
165 
166         for (col = 0; col < inpam.width; ++col) {
167             unsigned int colorIndex;
168             findOrAddColor(inRow[col], &cmap, &colorIndex);
169             fprintf(ofP, " %u", colorIndex + 1);
170         }
171         fprintf(ofP, "\n");
172     }
173     pm_message("%u colors in palette", cmap.nColors);
174 
175     pnm_freepamrow(inRow);
176     outputColormap(ofP, cmap);
177 
178     termCmap(&cmap);
179 }
180 
181 
182 
183 int
main(int argc,char * argv[])184 main(int argc, char *argv[]) {
185 
186     FILE * ifP;
187     const char * inputName;
188 
189     pnm_init(&argc, argv);
190 
191     inputName = argc-1 > 0 ? argv[1] : "-";
192 
193     ifP = pm_openr(inputName);
194 
195     if (streq(inputName, "-"))
196         fprintf(stdout, "# Created by pamtooctave\n");
197     else
198         fprintf(stdout, "# Created from '%s' by pamtooctave\n", inputName);
199 
200     convertToOctave(ifP, stdout);
201 
202     pm_close(ifP);
203 
204     return 0;
205 }
206 
207 
208 
209 /*
210  * Copyright (C) 2007 Scott Pakin <scott+pbm@pakin.org>
211  *
212  * All rights reserved.
213  *
214  * Redistribution and use in source and binary forms, with or without
215  * modification, are permitted provided that the following conditions
216  * are met:
217  *
218  * 1. Redistributions of source code must retain the above copyright
219  *    notice, this list of conditions and the following disclaimer.
220  * 2. Redistributions in binary form must reproduce the above
221  *    copyright notice, this list of conditions and the following
222  *    disclaimer in the documentation and/or other materials provided
223  *    with the distribution.
224  * 3. The name of the author may not be used to endorse or promote
225  *    products derived from this software without specific prior written
226  *    permission.
227  *
228  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
229  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
230  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
232  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
233  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
234  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
235  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
236  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
237  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
238  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
239  *
240  * ----------------------------------------------------------------------
241  */
242