2 * 2ooM: The Master of Orion II Reverse Engineering Project
3 * Netpbm output routines for lbximg extration.
4 * Copyright © 2013 Nick Bowler
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 #include "imgoutput.h"
32 /* Convert c from the basic execution character set to ASCII. */
33 static unsigned char to_ascii(unsigned char c)
36 case'\a': return 0x07;
37 case'\b': return 0x08;
38 case'\t': return 0x09;
39 case'\n': return 0x0a;
40 case'\v': return 0x0b;
41 case'\f': return 0x0c;
42 case'\r': return 0x0d;
43 case ' ': return 0x20;
44 case '!': return 0x21;
45 case '"': return 0x22;
46 case '#': return 0x23;
47 case '%': return 0x25;
48 case '&': return 0x26;
49 case'\'': return 0x27;
50 case '(': return 0x28;
51 case ')': return 0x29;
52 case '*': return 0x2a;
53 case '+': return 0x2b;
54 case ',': return 0x2c;
55 case '-': return 0x2d;
56 case '.': return 0x2e;
57 case '/': return 0x2f;
58 case '0': return 0x30;
59 case '1': return 0x31;
60 case '2': return 0x32;
61 case '3': return 0x33;
62 case '4': return 0x34;
63 case '5': return 0x35;
64 case '6': return 0x36;
65 case '7': return 0x37;
66 case '8': return 0x38;
67 case '9': return 0x39;
68 case ':': return 0x3a;
69 case ';': return 0x3b;
70 case '<': return 0x3c;
71 case '=': return 0x3d;
72 case '>': return 0x3e;
73 case '?': return 0x3f;
74 case 'A': return 0x41;
75 case 'B': return 0x42;
76 case 'C': return 0x43;
77 case 'D': return 0x44;
78 case 'E': return 0x45;
79 case 'F': return 0x46;
80 case 'G': return 0x47;
81 case 'H': return 0x48;
82 case 'I': return 0x49;
83 case 'J': return 0x4a;
84 case 'K': return 0x4b;
85 case 'L': return 0x4c;
86 case 'M': return 0x4d;
87 case 'N': return 0x4e;
88 case 'O': return 0x4f;
89 case 'P': return 0x50;
90 case 'Q': return 0x51;
91 case 'R': return 0x52;
92 case 'S': return 0x53;
93 case 'T': return 0x54;
94 case 'U': return 0x55;
95 case 'V': return 0x56;
96 case 'W': return 0x57;
97 case 'X': return 0x58;
98 case 'Y': return 0x59;
99 case 'Z': return 0x5a;
100 case '[': return 0x5b;
101 case'\\': return 0x5c;
102 case ']': return 0x5d;
103 case '^': return 0x5e;
104 case '_': return 0x5f;
105 case 'a': return 0x61;
106 case 'b': return 0x62;
107 case 'c': return 0x63;
108 case 'd': return 0x64;
109 case 'e': return 0x65;
110 case 'f': return 0x66;
111 case 'g': return 0x67;
112 case 'h': return 0x68;
113 case 'i': return 0x69;
114 case 'j': return 0x6a;
115 case 'k': return 0x6b;
116 case 'l': return 0x6c;
117 case 'm': return 0x6d;
118 case 'n': return 0x6e;
119 case 'o': return 0x6f;
120 case 'p': return 0x70;
121 case 'q': return 0x71;
122 case 'r': return 0x72;
123 case 's': return 0x73;
124 case 't': return 0x74;
125 case 'u': return 0x75;
126 case 'v': return 0x76;
127 case 'w': return 0x77;
128 case 'x': return 0x78;
129 case 'y': return 0x79;
130 case 'z': return 0x7a;
131 case '{': return 0x7b;
132 case '|': return 0x7c;
133 case '}': return 0x7d;
134 case '~': return 0x7e;
135 default: assert((tool_err(-1, "invalid codepoint: %hhx", c), 0));
141 /* Printf variant which converts all output to ASCII. */
142 static int fprintf_ascii(FILE *f, char *fmt, ...)
150 rc = vsnprintf(NULL, 0, fmt, ap);
156 assert(rc < SIZE_MAX);
162 len = vsprintf((char *)buf, fmt, ap);
166 for (int i = 0; i < len; i++) {
167 buf[i] = to_ascii(buf[i]);
170 ret = fwrite(buf, 1, len, f);
179 * Output filter for Netpbm's "plain" PBM format. This is a bitmap format
180 * which is not really suitable for image data, but it can output the image
181 * transparency mask, and thus complements the PPM output. The image colour
182 * data is lost in the conversion.
184 int img_output_pbm(FILE *f, const char *filename,
185 unsigned width, unsigned height,
186 unsigned char **framedata, unsigned char **mask,
187 struct lbx_colour *palette)
191 if (fprintf_ascii(f, "P1\n%u %u", width, height) < 0)
194 for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
195 if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF)
199 if (fputc(to_ascii('0'), f) == EOF)
202 if (fputc(to_ascii('1'), f) == EOF)
207 if (fputc(to_ascii('\n'), f) == EOF)
211 tool_err(0, "error writing %s", filename);
216 * Helper for Netpbm's "plain" PGM output. This is only meant for no-palette
217 * mode as normally LBX images are not grayscale, so it is not available as a
218 * normal output format.
220 static int write_pgm(FILE *f, unsigned width, unsigned height,
221 unsigned char **framedata, unsigned char **mask)
225 if (fprintf_ascii(f, "P2\n%u %u\n255", width, height) < 0)
228 for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
229 if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF)
233 if (fprintf_ascii(f, " 0") < 0)
236 if (fprintf_ascii(f, "%3hhu", framedata[y][x]) < 0)
241 if (fputc(to_ascii('\n'), f) == EOF)
248 * Output filter for Netpbm's "plain" PPM format. This supports RGB but not
249 * transparency. As a pure text format, it is useful for testing but is not
250 * particularly efficient in terms of storage. The image mask is lost in
251 * the conversion (output pixels will be black).
253 int img_output_ppm(FILE *f, const char *filename,
254 unsigned width, unsigned height,
255 unsigned char **framedata, unsigned char **mask,
256 struct lbx_colour *palette)
262 * For no-palette mode, write a PGM instead which is basically
263 * the same format but has only one value per pixel.
265 if (write_pgm(f, width, height, framedata, mask) < 0)
270 if (fprintf_ascii(f, "P3\n%u %u\n63", width, height) < 0)
273 for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
274 if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF)
278 if (fprintf_ascii(f, " 0 0 0") < 0)
281 struct lbx_colour *c = &palette[framedata[y][x]];
283 if (fprintf_ascii(f, "%2d %2d %2d",
284 c->red, c->green, c->blue) < 0)
289 if (fputc(to_ascii('\n'), f) == EOF)
293 tool_err(0, "error writing %s", filename);
298 * Output filter for Netpbm's PAM format. This format combines the features
299 * of all the other Netpbm formats, supporting RGB and grayscale images with
300 * or without alpha channels. This format supports all lbximg output.
302 int img_output_pam(FILE *f, const char *filename,
303 unsigned width, unsigned height,
304 unsigned char **framedata, unsigned char **mask,
305 struct lbx_colour *palette)
307 bool masked = img_is_masked(mask, width, height);
311 if (fprintf_ascii(f, "P7\nWIDTH %u\nHEIGHT %u\n", width, height) < 0)
315 depth = masked ? 4 : 3;
317 if (fprintf_ascii(f, "DEPTH %zu\nMAXVAL 63\n"
318 "TUPLTYPE RGB%s\nENDHDR\n",
319 depth, masked ? "_ALPHA" : "") < 0)
322 for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
323 struct lbx_colour *c = &palette[framedata[y][x]];
324 unsigned char buf[4];
329 buf[3] = mask[y][x] ? 63 : 0;
331 if (fwrite(buf, 1, depth, f) < depth)
335 depth = masked ? 2 : 1;
337 if (fprintf_ascii(f, "DEPTH %zu\nMAXVAL 255\n"
338 "TUPLTYPE GRAYSCALE%s\nENDHDR\n",
339 depth, masked ? "_ALPHA" : "") < 0)
342 for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
343 unsigned char buf[2];
345 buf[0] = framedata[y][x];
346 buf[1] = mask[y][x] ? 0xff : 0;
348 if (fwrite(buf, 1, depth, f) < depth)
355 tool_err(0, "error writing %s", filename);