269 lines
6.4 KiB
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();
|
|
}
|