1 /*=============================================================================
2 pamtoxvmini
3 ===============================================================================
4 Convert Netpbm image to XV mini thumbnail.
5
6 Written by Bryan Henderson in April 2006 and contributed to the public
7 domain.
8 =============================================================================*/
9
10 #include <assert.h>
11 #include <limits.h>
12 #include <string.h>
13
14 #include "pm_c_util.h"
15 #include "nstring.h"
16 #include "pam.h"
17 #include "pammap.h"
18
19 typedef struct xvPalette {
20 unsigned int red[256];
21 unsigned int grn[256];
22 unsigned int blu[256];
23 } xvPalette;
24
25
26 struct cmdlineInfo {
27 const char * inputFileName;
28 };
29
30
31
32 static void
parseCommandLine(int const argc,char * argv[],struct cmdlineInfo * const cmdlineP)33 parseCommandLine(int const argc,
34 char * argv[],
35 struct cmdlineInfo * const cmdlineP) {
36
37 if (argc-1 < 1)
38 cmdlineP->inputFileName = "-";
39 else {
40 cmdlineP->inputFileName = argv[1];
41
42 if (argc-1 > 1)
43 pm_error("Too many arguments: %u. Only argument is optional "
44 "input file name.", argc-1);
45 }
46 }
47
48
49
50 static void
makeXvPalette(xvPalette * const xvPaletteP)51 makeXvPalette(xvPalette * const xvPaletteP) {
52
53 unsigned int paletteIndex;
54 unsigned int r;
55
56 paletteIndex = 0;
57
58 for (r = 0; r < 8; ++r) {
59 unsigned int g;
60 for (g = 0; g < 8; ++g) {
61 unsigned int b;
62 for (b = 0; b < 4; ++b) {
63 xvPaletteP->red[paletteIndex] = (r*255)/7;
64 xvPaletteP->grn[paletteIndex] = (g*255)/7;
65 xvPaletteP->blu[paletteIndex] = (b*255)/3;
66 ++paletteIndex;
67 }
68 }
69 }
70 }
71
72
73
74 static void
writeXvHeader(FILE * const ofP,unsigned int const cols,unsigned int const rows)75 writeXvHeader(FILE * const ofP,
76 unsigned int const cols,
77 unsigned int const rows) {
78
79 fprintf(ofP, "P7 332\n");
80
81 fprintf(ofP, "# Created by Pamtoxvmini\n");
82 fprintf(ofP, "#END_OF_COMMENTS\n");
83
84 /* I don't know what the maxval number (3rd field) means here, since
85 the maxvals are fixed at red=7, grn=7, blu=3. We used to have
86 it put the maxval of the input image there. That generated an
87 output that Xv choked on when the input maxval was 65535.
88 */
89
90 fprintf(ofP, "%u %u 255\n", cols, rows);
91 }
92
93
94
95 static void
findClosestColor(struct pam * const pamP,tuple const tuple,const xvPalette * const xvPaletteP,unsigned int * const paletteIndexP)96 findClosestColor(struct pam * const pamP,
97 tuple const tuple,
98 const xvPalette * const xvPaletteP,
99 unsigned int * const paletteIndexP) {
100 /*----------------------------------------------------------------------------
101 Find the color in the palette *xvPaletteP that is closest to the color
102 'tuple' and return its index in the palette.
103 -----------------------------------------------------------------------------*/
104 unsigned int paletteIndex;
105 unsigned int bestPaletteIndex;
106 unsigned int bestDistanceSoFar;
107
108 /* An entry condition is that the tuple have the same form as the
109 colors in the XV palette:
110 */
111 assert(pamP->depth >= 3);
112 assert(pamP->maxval == 255);
113
114 bestPaletteIndex = 0;
115 bestDistanceSoFar = UINT_MAX;
116
117 for (paletteIndex = 0; paletteIndex < 256; ++paletteIndex) {
118 unsigned int const tupleRed = tuple[PAM_RED_PLANE];
119 unsigned int const tupleGrn = tuple[PAM_GRN_PLANE];
120 unsigned int const tupleBlu = tuple[PAM_BLU_PLANE];
121
122 unsigned int const paletteRed = xvPaletteP->red[paletteIndex];
123 unsigned int const paletteGrn = xvPaletteP->grn[paletteIndex];
124 unsigned int const paletteBlu = xvPaletteP->blu[paletteIndex];
125
126 unsigned int const distance =
127 SQR((int)tupleRed - (int)paletteRed) +
128 SQR((int)tupleGrn - (int)paletteGrn) +
129 SQR((int)tupleBlu - (int)paletteBlu);
130
131 if (distance < bestDistanceSoFar) {
132 bestDistanceSoFar = distance;
133 bestPaletteIndex = paletteIndex;
134 }
135 }
136 *paletteIndexP = bestPaletteIndex;
137 }
138
139
140
141 static void
getPaletteIndexThroughCache(struct pam * const pamP,tuple const tuple,const xvPalette * const xvPaletteP,tuplehash const paletteHash,unsigned int * const paletteIndexP)142 getPaletteIndexThroughCache(struct pam * const pamP,
143 tuple const tuple,
144 const xvPalette * const xvPaletteP,
145 tuplehash const paletteHash,
146 unsigned int * const paletteIndexP) {
147 /*----------------------------------------------------------------------------
148 Return as *paletteIndexP the index into the palette *xvPaletteP of
149 the color that most closely resembles the color 'tuple'.
150
151 Use the hash table *paletteIndexP as a cache to speed up the search.
152 If the tuple-index association is in *paletteIndexP, use it. If not,
153 find it the hard way and add it to *palettedIndexP for the next guy.
154 -----------------------------------------------------------------------------*/
155 int found;
156 int paletteIndex;
157
158 pnm_lookuptuple(pamP, paletteHash, tuple, &found, &paletteIndex);
159 if (found)
160 *paletteIndexP = paletteIndex;
161 else {
162 int fits;
163 findClosestColor(pamP, tuple, xvPaletteP, paletteIndexP);
164
165 pnm_addtotuplehash(pamP, paletteHash, tuple, *paletteIndexP, &fits);
166
167 if (!fits)
168 pm_error("Can't get memory for palette hash.");
169 }
170 }
171
172
173
174 static void
writeXvRaster(struct pam * const pamP,xvPalette * const xvPaletteP,FILE * const ofP)175 writeXvRaster(struct pam * const pamP,
176 xvPalette * const xvPaletteP,
177 FILE * const ofP) {
178 /*----------------------------------------------------------------------------
179 Write out the XV image, from the Netpbm input file ifP, which is
180 positioned to the raster.
181
182 The XV raster contains palette indices into the palette *xvPaletteP.
183
184 If there is any color in the image which is not in the palette, we
185 fail the program. We really should use the closest color in the palette
186 instead.
187 -----------------------------------------------------------------------------*/
188 tuplehash paletteHash;
189 tuple * tuplerow;
190 unsigned int row;
191 unsigned char * xvrow;
192 struct pam scaledPam;
193
194 paletteHash = pnm_createtuplehash();
195
196 tuplerow = pnm_allocpamrow(pamP);
197 xvrow = (unsigned char*)pm_allocrow(pamP->width, 1);
198
199 scaledPam = *pamP;
200 scaledPam.maxval = 255;
201
202 for (row = 0; row < pamP->height; ++row) {
203 unsigned int col;
204
205 pnm_readpamrow(pamP, tuplerow);
206 pnm_scaletuplerow(pamP, tuplerow, tuplerow, scaledPam.maxval);
207 pnm_makerowrgb(&scaledPam, tuplerow);
208
209 for (col = 0; col < scaledPam.width; ++col) {
210 unsigned int paletteIndex;
211
212 getPaletteIndexThroughCache(&scaledPam, tuplerow[col], xvPaletteP,
213 paletteHash, &paletteIndex);
214
215 assert(paletteIndex < 256);
216
217 xvrow[col] = paletteIndex;
218 }
219 fwrite(xvrow, 1, scaledPam.width, ofP);
220 }
221
222 pm_freerow((char*)xvrow);
223 pnm_freepamrow(tuplerow);
224
225 pnm_destroytuplehash(paletteHash);
226 }
227
228
229
230 int
main(int argc,char * argv[])231 main(int argc,
232 char * argv[]) {
233
234 struct cmdlineInfo cmdline;
235 FILE * ifP;
236 struct pam pam;
237 xvPalette xvPalette;
238
239 pnm_init(&argc, argv);
240
241 parseCommandLine(argc, argv, &cmdline);
242
243 ifP = pm_openr(cmdline.inputFileName);
244
245 makeXvPalette(&xvPalette);
246
247 pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(allocation_depth));
248
249 pnm_setminallocationdepth(&pam, 3);
250
251 writeXvHeader(stdout, pam.width, pam.height);
252
253 writeXvRaster(&pam, &xvPalette, stdout);
254
255 pm_close(ifP);
256
257 return 0;
258 }
259