/**
* blueshift-demomode — Blueshift-effect demonstration tools
* Copyright Ⓒ 2014, 2015 Mattias Andrée (m@maandree.se)
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/**
* Value for `tupletype` when the image is black and white
* The bytes are: whitenames (note that it is not blackness as with P1 and P4)
* (note that it is bytes and not bits as with P4)
*/
#define BLACKANDWHITE 0
/**
* Value for `tupletype` when the image is grey
* The words are: whiteness
*/
#define GRAYSCALE 1
/**
* Value for `tupletype` when the image is coloured
* The words are: redness, greenness, blueness
*/
#define RGB 2
/**
* Value for `tupletype` when the image is black and white with an alpha channel
* The bytes are: blackness (note that it is not blackness as with P1 and P4), transparency
*/
#define BLACKANDWHITE_ALPHA 3
/**
* Value for `tupletype` when the image is grey with an alpha channel
* The words are: whiteness, transparency
*/
#define GRAYSCALE_ALPHA 4
/**
* Value for `tupletype` when the image is coloured with an alpha channel
* The words are: redness, greenness, blueness, transparency
*/
#define RGB_ALPHA 5
/**
* The width of image, in pixels
*/
static size_t width = 0;
static int have_width = 0;
/**
* The height of the image, in pixels
*/
static size_t height = 0;
static int have_height = 0;
/**
* The number of channals, 1 for black and white or greyscale, 3 for colour, plus 1 for alpha
*/
static int depth = 0;
static int have_depth = 0;
/**
* The maximum value a channel can have, the white point threshold
*/
static uint32_t maxval = 0;
static int have_maxval = 0;
/**
* The channel configuration
*/
static int tupltype = 0;
static int have_tupltype = 0;
static size_t red_n, green_n, blue_n;
static uint32_t* max = NULL;
static uint32_t* red = NULL;
static uint32_t* green = NULL;
static uint32_t* blue = NULL;
static char* execname;
static void strip(char* buffer)
{
size_t offset = 0;
size_t end = strlen(buffer);
while (buffer[offset] && ((buffer[offset] == ' ') || (buffer[offset] == '\t')))
offset++;
while (end && ((buffer[end - 1] == ' ') || (buffer[end - 1] == '\t')))
end--;
buffer[end] = '\0';
memmove(buffer, buffer + offset, end + 1 - offset);
}
static long long conv(char* buffer)
{
strip(buffer);
return atoll(buffer);
}
static uint32_t* int_split(char* buffer, size_t* out_n)
{
size_t n = 1;
char* buf = buffer;
uint32_t* rc = NULL;
uint32_t v = 0;
while (*buf)
if (*buf++ == ' ')
n++;
if (out_n)
*out_n = n;
rc = malloc(n * sizeof(uint32_t));
if (rc == NULL)
return NULL;
for (n = 0; *buf; buf++)
if (*buf == ' ')
rc[n++] = v, v = 0;
else
v = (v * 10) + (*buf & 15);
rc[n] = v;
return rc;
}
/* TODO precalculate */
static inline void conv1(unsigned char* in, unsigned char* out, uint32_t* lut, size_t lutsize, uint32_t lutmax)
{
size_t n = ((uint32_t)*in * lutsize + maxval / 2) / maxval;
if (n > lutsize)
n = lutsize;
*out = (unsigned char)(lut[n] * maxval / lutmax);
}
static inline void conv2(unsigned char* in, unsigned char* out, uint32_t* lut, size_t lutsize, uint32_t lutmax)
{
size_t n = (((uint32_t)(in[0])) << 8) | ((uint32_t)(in[1]));
n = (n * lutsize + maxval / 2) / maxval;
if (n > lutsize)
n = lutsize;
n = lut[n] * maxval / lutmax;
out[0] = (n >> 8) & 255;
out[1] = (n >> 0) & 255;
}
#define FILTER(FOR, SIZE, RGB, WPTR) \
for FOR \
{ \
conv##SIZE(data + ptr + RGB * SIZE * 0, data + WPTR + SIZE * 0, red, red_n, max[0]); \
conv##SIZE(data + ptr + RGB * SIZE * 1, data + WPTR + SIZE * 1, green, green_n, max[1]); \
conv##SIZE(data + ptr + RGB * SIZE * 2, data + WPTR + SIZE * 2, blue, blue_n, max[2]); \
}
static int filter(size_t size, int rgb, int alpha, int dual)
{
char* data = malloc(size * (rgb ? 1 : 3));
size_t ptr = 0;
ssize_t got;
if (data == NULL)
goto fail;
while (size)
{
got = read(0, data + ptr, size);
if (got < 0)
goto fail;
ptr += (size_t)got;
size -= (size_t)got;
}
size = ptr;
if (rgb && alpha && dual) FILTER((ptr = 0; ptr < size; ptr += 8), 2, 1, ptr)
else if (rgb && alpha) FILTER((ptr = 0; ptr < size; ptr += 4), 1, 1, ptr)
else if (rgb && dual) FILTER((ptr = 0; ptr < size; ptr += 6), 2, 1, ptr)
else if (rgb) FILTER((ptr = 0; ptr < size; ptr += 3), 1, 1, ptr)
else if (alpha && dual) FILTER((; ptr; ptr -= 4, size -= 8), 2, 0, size - 8)
else if (alpha) FILTER((; ptr; ptr -= 2, size -= 4), 1, 0, size - 4)
else if (dual) FILTER((; ptr; ptr -= 2, size -= 6), 2, 0, size - 6)
else FILTER((; ptr; ptr -= 1, size -= 3), 1, 0, size - 3)
return 0;
fail:
perror(execname);
free(data);
return -1;
}
int main(int argc, char** argv)
{
int ok = 0;
int stage = 0;
char buf[1024];
size_t ptr = 0, size;
int alpha;
execname = *argv;
for (;;)
{
int c = getchar();
if (c < 0)
goto bad;
if (c == '\n')
{
buf[ptr] = '\0';
ptr = 0;
strip(buf);
if (!strcmp(buf, "P7"))
{
if (stage++)
break;
}
else if (!strcmp(buf, "ENDHDR"))
{
ok = 1;
break;
}
else if (strstr(buf, "WIDTH") == buf)
{
if (have_width) break;
have_width = 1, width = (size_t)conv(buf + 5);
}
else if (strstr(buf, "HEIGHT") == buf)
{
if (have_height) break;
have_height = 1, height = (size_t)conv(buf + 6);
}
else if (strstr(buf, "DEPTH") == buf)
{
if (have_depth) break;
have_depth = 1, depth = (int)conv(buf + 5);
}
else if (strstr(buf, "MAXVAL") == buf)
{
if (have_maxval) break;
have_maxval = 1, maxval = (uint32_t)conv(buf + 6);
}
else if (strstr(buf, "TUPLTYPE") == buf)
{
if (have_tupltype) break;
strip(buf + 8);
have_tupltype = 1;
if (!strcmp(buf + 8, "BLACKANDWHITE")) tupltype = BLACKANDWHITE;
else if (!strcmp(buf + 8, "GRAYSCALE")) tupltype = GRAYSCALE;
else if (!strcmp(buf + 8, "RGB")) tupltype = RGB;
else if (!strcmp(buf + 8, "BLACKANDWHITE_ALPHA")) tupltype = BLACKANDWHITE_ALPHA;
else if (!strcmp(buf + 8, "GRAYSCALE_ALPHA")) tupltype = GRAYSCALE_ALPHA;
else if (!strcmp(buf + 8, "RGB_ALPHA")) tupltype = RGB_ALPHA;
else
goto bad;
}
}
else
if (ptr + 1 == sizeof(buf) / sizeof(*buf))
goto bad;
else
buf[ptr++] = (char)c;
}
if (!ok || !width || !height || !depth || !maxval || !tupltype)
ok = 0;
else if ((width < 1) || (height < 0)) ok = 0;
else if (tupltype == BLACKANDWHITE) ok = (depth == 1) && (1 <= maxval) && (maxval <= 1);
else if (tupltype == GRAYSCALE) ok = (depth == 1) && (1 <= maxval) && (maxval <= 65535);
else if (tupltype == RGB) ok = (depth == 3) && (1 <= maxval) && (maxval <= 65535);
else if (tupltype == BLACKANDWHITE_ALPHA) ok = (depth == 2) && (1 <= maxval) && (maxval <= 1);
else if (tupltype == GRAYSCALE_ALPHA) ok = (depth == 2) && (1 <= maxval) && (maxval <= 65535);
else if (tupltype == RGB_ALPHA) ok = (depth == 4) && (1 <= maxval) && (maxval <= 65535);
if (!ok)
goto bad;
if ((max = int_split(argv[1], NULL)) == NULL) goto fail;
if ((red = int_split(argv[2], &red_n)) == NULL) goto fail;
if ((green = int_split(argv[3], &green_n)) == NULL) goto fail;
if ((blue = int_split(argv[4], &blue_n)) == NULL) goto fail;
alpha = tupltype >= BLACKANDWHITE_ALPHA;
printf("P7\n");
printf("WIDTH %zu\n", width);
printf("HEIGHT %zu\n", height);
printf("DEPTH %i\n", alpha ? 4 : 3);
printf("MAXVAL %zu\n", (size_t)maxval);
printf("TUPLTYPE %s\n", alpha ? "RGB_ALPHA" : "RGB");
printf("ENDHDR\n");
red_n--;
green_n--;
blue_n--;
size = width * height * depth * (maxval > 255 ? 2 : 1);
if (filter(size, tupltype == (alpha ? RGB_ALPHA : RGB), alpha, maxval > 255))
goto fail;
free(max);
free(red);
free(green);
free(blue);
return 0;
bad:
fprintf(stderr, "%s: %s\n", execname, "Failed to load image");
return 1;
fail:
perror(execname);
free(max);
free(red);
free(green);
free(blue);
return 1;
}