joeylib/imgconvert/main.c

269 lines
6.4 KiB
C

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <limits.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <lz4.h>
#include "lib.h"
#include "joey.h"
void convertColor(char *filename, char *basename, bool showIt);
void convertStencil(char *filename, char *basename);
bool fileExists(char *filename);
Uint32 getPixel(SDL_Surface *surface, int x, int y);
SDL_Surface *loadImage(char *filename);
void convertColor(char *filename, char *basename, bool showIt) {
int colors = INT_MAX;
SDL_Color *c = NULL;
SDL_Surface *image = loadImage(filename);
if (image == NULL) return;
if (!image->format->palette) {
fprintf(stderr, "Not a palettized image.\n");
return;
}
colors = image->format->palette->ncolors;
c = image->format->palette->colors;
jlImgT *img = NULL;
jlImgCreate(img);
for (int x=0; x<colors; x++) {
img->palette[x].r = c[x].r / 16;
img->palette[x].g = c[x].g / 16;
img->palette[x].b = c[x].b / 16;
//printf("Palette %03d = R%03d G%03d B%03d\n", x, img->palette[x].r, img->palette[x].g, img->palette[x].b);
}
if (colors < 16) {
for (int x=colors; x<16; x++) {
img->palette[x].r = 0;
img->palette[x].g = 0;
img->palette[x].b = 0;
//printf("Palette %03d = R000 G000 B000 (added)\n", x);
}
}
int p = 0;
for (int y=0; y<image->h; y++) {
for (int x=0; x<image->w; x+=2) {
//printf("%02X %02X ", getPixel(image, x, y), getPixel(image, x + 1, y));
// These are backwards - no idea why yet.
img->pixels[p].r = (unsigned char)getPixel(image, x, y);
img->pixels[p++].l = (unsigned char)getPixel(image, x + 1, y);
}
}
SDL_FreeSurface(image);
jlImgSave(img, basename);
// Experimental LZ4 compression
const size_t srcSize = (sizeof(jlColorT) * 16) + (sizeof(jlPixelPairT) * 32000);
const int maxSize = LZ4_compressBound(srcSize);
char *compressedData = malloc((size_t)maxSize);
if (!compressedData) {
fprintf(stderr, "Unable to allocate space for compressed data.\n");
return;
}
const int compressedDataSize = LZ4_compress_default((char *)img->palette, compressedData, srcSize, maxSize);
if (compressedDataSize <= 0) {
fprintf(stderr, "Unable to compress data.\n");
return;
}
char name[2048];
FILE *out;
snprintf(name, 2048, "%s.imgz", basename);
out = fopen(name, "wb");
if (!out) {
fprintf(stderr, "Unable to save compressed file: %s\n", name);
return;
}
fwrite(img, 4, 1, out);
fwrite(compressedData, compressedDataSize, 1, out);
fclose(out);
// Experimental LZSA compression
const size_t srcSize2 = (sizeof(jlColorT) * 16) + (sizeof(jlPixelPairT) * 32000);
const int maxSize2 = lzsa_get_max_compressed_size_inmem(srcSize2);
unsigned char *compressedData2 = malloc((size_t)maxSize2);
if (!compressedData2) {
fprintf(stderr, "Unable to allocate space for compressed data.\n");
return;
}
const int compressedDataSize2 = lzsa_compress_inmem((unsigned char *)img->palette, compressedData2, srcSize2, maxSize2, LZSA_FLAG_FAVOR_RATIO, 3, 1);
if (compressedDataSize2 <= 0) {
fprintf(stderr, "Unable to compress data.\n");
return;
}
snprintf(name, 2048, "%s.imgz2", basename);
out = fopen(name, "wb");
if (!out) {
fprintf(stderr, "Unable to save compressed file: %s\n", name);
return;
}
fwrite(img, 4, 1, out);
fwrite(compressedData2, compressedDataSize2, 1, out);
fclose(out);
if (showIt) {
jlImgDisplay(img);
jlDisplayPresent();
jlKeyWaitForAny();
}
jlImgFree(img);
}
void convertStencil(char *filename, char *basename) {
// Stencil files aren't required
if (!fileExists(filename)) return;
int bytes = 0;
byte data = 0;
int bits = 0;
SDL_Surface *image = loadImage(filename);
char name[2048];
FILE *out;
snprintf(name, 2048, "%s.stn", basename);
out = fopen(name, "wb");
if (!out) {
fprintf(stderr, "Unable to save stencil: %s\n", name);
return;
}
fputc('S', out);
fputc('T', out);
fputc('N', out);
fputc( 0, out);
for (int y=0; y<image->h; y++) {
for (int x=0; x<image->w; x++) {
data <<= 1;
data |= (getPixel(image, x, y) != 0);
bits++;
if (bits > 7) {
bytes++;
bits = 0;
fputc(data, out);
}
}
}
fclose(out);
}
bool fileExists(char *filename) {
FILE *file;
if ((file = fopen(filename, "r+"))) {
fclose(file);
return true;
}
return false;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
Uint32 getPixel(SDL_Surface *surface, int x, int y) {
int bpp = surface->format->BytesPerPixel;
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp) {
case 1:
return *p;
break;
case 2:
// Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86.
return *(Uint16 *)p;
break;
case 3:
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
return (Uint32)(p[0] << 16 | p[1] << 8 | p[2]);
else
return (Uint32)(p[0] | p[1] << 8 | p[2] << 16);
break;
case 4:
// Generates cast increases required alignment of target type [-Wcast-align] warning. Harmless on x86.
return *(Uint32 *)p;
break;
default:
return 0;
}
}
#pragma GCC diagnostic pop
SDL_Surface *loadImage(char *filename) {
if (!fileExists(filename)) {
fprintf(stderr, "Unable to load image: %s\n", filename);
return NULL;
}
SDL_Surface *image = IMG_Load(filename);
int colors = INT_MAX;
if (image->format->palette != NULL) {
colors = image->format->palette->ncolors;
}
//printf("%s = %dx%d, %d bits per pixel, %d colors\n", filename, image->w, image->h, image->format->BitsPerPixel, colors);
if (image->w != 320 || image->h != 200) {
fprintf(stderr, "Image must be 320x200 pixels.\n");
free(image);
image = NULL;
}
if (colors > 16) {
fprintf(stderr, "Expecting 16 or fewer colors. Only using first 16.\n");
}
return image;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage: %s [color.png] [stencil.png|NO_STENCIL] [outfile] {anyting}\n", argv[0]);
printf("(If {anything} is provided, image will be displayed as converted until keypress.)\n");
return 1;
}
jlUtilStartup("IMG/STN Converter");
int imageFlags = IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF;
int imageResult = IMG_Init(imageFlags);
if ((imageResult & imageFlags) != imageFlags) {
fprintf(stderr, "Failed to initialize SDL2_Image: %s\n", IMG_GetError());
jlUtilShutdown();
}
convertColor(argv[1], argv[3], argc > 4);
convertStencil(argv[2], argv[3]);
IMG_Quit();
jlUtilShutdown();
}