/*
* 2ooM: The Master of Orion II Reverse Engineering Project
* Netpbm output routines for lbximg extration.
- * Copyright © 2013 Nick Bowler
+ * Copyright © 2013-2014 Nick Bowler
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <stdarg.h>
#include <assert.h>
#include <inttypes.h>
+#include <limits.h>
#include "image.h"
#include "tools.h"
#include "imgoutput.h"
+/* PNM formats are ASCII, check if C exec charset is too */
+#if '\a' == 0x07 && '\b' == 0x08 && '\t' == 0x09 && '\n' == 0x0a && \
+ '\v' == 0x0b && '\f' == 0x0c && '\r' == 0x0d && ' ' == 0x20 && \
+ '!' == 0x21 && '"' == 0x22 && '#' == 0x23 && '%' == 0x25 && \
+ '&' == 0x26 && '\'' == 0x27 && '(' == 0x28 && ')' == 0x29 && \
+ '*' == 0x2a && '+' == 0x2b && ',' == 0x2c && '-' == 0x2d && \
+ '.' == 0x2e && '/' == 0x2f && '0' == 0x30 && '1' == 0x31 && \
+ '2' == 0x32 && '3' == 0x33 && '4' == 0x34 && '5' == 0x35 && \
+ '6' == 0x36 && '7' == 0x37 && '8' == 0x38 && '9' == 0x39 && \
+ ':' == 0x3a && ';' == 0x3b && '<' == 0x3c && '=' == 0x3d && \
+ '>' == 0x3e && '?' == 0x3f && 'A' == 0x41 && 'B' == 0x42 && \
+ 'C' == 0x43 && 'D' == 0x44 && 'E' == 0x45 && 'F' == 0x46 && \
+ 'G' == 0x47 && 'H' == 0x48 && 'I' == 0x49 && 'J' == 0x4a && \
+ 'K' == 0x4b && 'L' == 0x4c && 'M' == 0x4d && 'N' == 0x4e && \
+ 'O' == 0x4f && 'P' == 0x50 && 'Q' == 0x51 && 'R' == 0x52 && \
+ 'S' == 0x53 && 'T' == 0x54 && 'U' == 0x55 && 'V' == 0x56 && \
+ 'W' == 0x57 && 'X' == 0x58 && 'Y' == 0x59 && 'Z' == 0x5a && \
+ '[' == 0x5b && '\\' == 0x5c && ']' == 0x5d && '^' == 0x5e && \
+ '_' == 0x5f && 'a' == 0x61 && 'b' == 0x62 && 'c' == 0x63 && \
+ 'd' == 0x64 && 'e' == 0x65 && 'f' == 0x66 && 'g' == 0x67 && \
+ 'h' == 0x68 && 'i' == 0x69 && 'j' == 0x6a && 'k' == 0x6b && \
+ 'l' == 0x6c && 'm' == 0x6d && 'n' == 0x6e && 'o' == 0x6f && \
+ 'p' == 0x70 && 'q' == 0x71 && 'r' == 0x72 && 's' == 0x73 && \
+ 't' == 0x74 && 'u' == 0x75 && 'v' == 0x76 && 'w' == 0x77 && \
+ 'x' == 0x78 && 'y' == 0x79 && 'z' == 0x7a && '{' == 0x7b && \
+ '|' == 0x7c && '}' == 0x7d && '~' == 0x7e
+# define NATIVE_ASCII 1
+#else
+# define NATIVE_ASCII 0
+#endif
+
/* Convert c from the basic execution character set to ASCII. */
static unsigned char to_ascii(unsigned char c)
{
return c;
}
-/* Printf variant which converts all output to ASCII. */
+#if NATIVE_ASCII && defined(NDEBUG)
+/* fprintf can be used directly. */
+#define fprintf_ascii fprintf
+#else
+/* Variant of fprintf which converts all output to ASCII. */
static int fprintf_ascii(FILE *f, char *fmt, ...)
{
- unsigned char *buf;
+ char buf[100];
va_list ap;
- int rc, len;
- size_t ret;
+ int i, len;
va_start(ap, fmt);
- rc = vsnprintf(NULL, 0, fmt, ap);
+ len = vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap);
- if (rc < 0)
- return -1;
-
- assert(rc < SIZE_MAX);
- buf = malloc(rc+1u);
- if (!buf)
+ if (len < 0 || len >= sizeof buf)
return -1;
- va_start(ap, fmt);
- len = vsprintf((char *)buf, fmt, ap);
- va_end(ap);
-
- assert(rc == len);
- for (int i = 0; i < len; i++) {
- buf[i] = to_ascii(buf[i]);
+ for (i = 0; i < len; i++) {
+ if (fputc(to_ascii(buf[i]), f) == EOF)
+ return -1;
}
- ret = fwrite(buf, 1, len, f);
- free(buf);
-
- if (ret < len)
- return -(int)ret;
- return ret;
+ return len;
}
+#endif
/*
* Output filter for Netpbm's "plain" PBM format. This is a bitmap format
*/
int img_output_pbm(FILE *f, const char *filename,
unsigned width, unsigned height,
- unsigned char **framedata, unsigned char **mask,
- struct lbx_colour *palette)
+ unsigned char *pixels, unsigned char *mask,
+ struct lbx_colour *palette)
{
unsigned x, y;
goto err;
for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
+ unsigned long offset = (unsigned long) y * width + x;
+ bool vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
+
if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF)
goto err;
- if (mask[y][x]) {
- if (fputc(to_ascii('0'), f) == EOF)
- goto err;
- } else {
- if (fputc(to_ascii('1'), f) == EOF)
- goto err;
- }
+ if (fputc(to_ascii(vis ? '0' : '1'), f) == EOF)
+ goto err;
}
if (fputc(to_ascii('\n'), f) == EOF)
* normal output format.
*/
static int write_pgm(FILE *f, unsigned width, unsigned height,
- unsigned char **framedata, unsigned char **mask)
+ unsigned char *pixels, unsigned char *mask)
{
unsigned x, y;
return -1;
for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
+ unsigned long offset = (unsigned long) y * width + x;
+ bool vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
+
if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF)
return -1;
- if (!mask[y][x]) {
- if (fprintf_ascii(f, " 0") < 0)
- return -1;
- } else {
- if (fprintf_ascii(f, "%3hhu", framedata[y][x]) < 0)
- return -1;
- }
+ if (fprintf_ascii(f, "%3hhu", vis ? pixels[offset] : 0) < 0)
+ return -1;
}
if (fputc(to_ascii('\n'), f) == EOF)
*/
int img_output_ppm(FILE *f, const char *filename,
unsigned width, unsigned height,
- unsigned char **framedata, unsigned char **mask,
- struct lbx_colour *palette)
+ unsigned char *pixels, unsigned char *mask,
+ struct lbx_colour *palette)
{
unsigned x, y;
* For no-palette mode, write a PGM instead which is basically
* the same format but has only one value per pixel.
*/
- if (write_pgm(f, width, height, framedata, mask) < 0)
+ if (write_pgm(f, width, height, pixels, mask) < 0)
goto err;
return 0;
}
goto err;
for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
+ unsigned long offset = (unsigned long) y * width + x;
+ bool vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
+ struct lbx_colour c = { 0 };
+
if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF)
goto err;
- if (!mask[y][x]) {
- if (fprintf_ascii(f, " 0 0 0") < 0)
- goto err;
- } else {
- struct lbx_colour *c = &palette[framedata[y][x]];
+ if (vis)
+ c = palette[pixels[offset]];
- if (fprintf_ascii(f, "%2d %2d %2d",
- c->red, c->green, c->blue) < 0)
- goto err;
- }
+ if (fprintf_ascii(f, "%2d %2d %2d", c.red, c.green, c.blue) < 0)
+ goto err;
}
if (fputc(to_ascii('\n'), f) == EOF)
*/
int img_output_pam(FILE *f, const char *filename,
unsigned width, unsigned height,
- unsigned char **framedata, unsigned char **mask,
- struct lbx_colour *palette)
+ unsigned char *pixels, unsigned char *mask,
+ struct lbx_colour *palette)
{
bool masked = img_is_masked(mask, width, height);
- unsigned x, y;
- size_t depth;
+ unsigned x, y, depth;
if (fprintf_ascii(f, "P7\nWIDTH %u\nHEIGHT %u\n", width, height) < 0)
goto err;
if (palette) {
depth = masked ? 4 : 3;
- if (fprintf_ascii(f, "DEPTH %zu\nMAXVAL 63\n"
+ if (fprintf_ascii(f, "DEPTH %u\nMAXVAL 63\n"
"TUPLTYPE RGB%s\nENDHDR\n",
depth, masked ? "_ALPHA" : "") < 0)
goto err;
for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
- struct lbx_colour *c = &palette[framedata[y][x]];
+ unsigned long offset = (unsigned long) y * width + x;
+ struct lbx_colour *c = &palette[pixels[offset]];
unsigned char buf[4];
+ bool vis;
+ vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
buf[0] = c->red;
buf[1] = c->green;
buf[2] = c->blue;
- buf[3] = mask[y][x] ? 63 : 0;
+ buf[3] = vis ? 63 : 0;
if (fwrite(buf, 1, depth, f) < depth)
goto err;
} else {
depth = masked ? 2 : 1;
- if (fprintf_ascii(f, "DEPTH %zu\nMAXVAL 255\n"
+ if (fprintf_ascii(f, "DEPTH %u\nMAXVAL 255\n"
"TUPLTYPE GRAYSCALE%s\nENDHDR\n",
depth, masked ? "_ALPHA" : "") < 0)
goto err;
for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
+ unsigned long offset = (unsigned long) y * width + x;
unsigned char buf[2];
+ bool vis;
- buf[0] = framedata[y][x];
- buf[1] = mask[y][x] ? 0xff : 0;
+ vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
+ buf[0] = pixels[offset];
+ buf[1] = vis ? 255 : 0;
if (fwrite(buf, 1, depth, f) < depth)
goto err;