diff --git a/tools/maskimg/Makefile b/tools/maskimg/Makefile new file mode 100755 index 000000000..87f4c9665 --- /dev/null +++ b/tools/maskimg/Makefile @@ -0,0 +1,34 @@ +######################################################################## +# This is a GNU makefile - tested on CYGWIN and Linux +# +# You may need to compile or install libpng and/or zlib before +# compiling this. +# + +TARGET = maskimg +# These flags are needed to get it to compile with libpngX.dll on CYGWIN +CYGWINFLAGS = -DPNG_USE_DLL +#CYGWINFLAGS = +PNGFLAGS = -I. + +CC = gcc +CFLAGS = -W -Wall -O0 # -g +LIBS = -L/usr/local/lib -lpng -lm +LDFLAGS= + +BINS = $(TARGET) $(TARGET).exe +SRCS = maskimg.c +OBJS = maskimg.o + +.SUFFIXES: .c .o + +.c.o: + $(CC) -c $(CFLAGS) -o $@ $*.c $(PNGFLAGS) $(CYGWINFLAGS) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -o $(TARGET) $(OBJS) $(LIBS) $(LDFLAGS) + +clean: + rm -f $(BINS) *.o diff --git a/tools/maskimg/config.h b/tools/maskimg/config.h new file mode 100755 index 000000000..0a70fe8f0 --- /dev/null +++ b/tools/maskimg/config.h @@ -0,0 +1,15 @@ +/* Dummy compile-time configuration options + * Only used when compiling in MS VC++ using UQM's getopt.c + */ + +#ifndef _CONFIG_H +#define _CONFIG_H + +/* Define if words are stored with the most significant byte first */ +#undef WORDS_BIGENDIAN + +/* Defined if your system has getopt.h */ +#undef HAVE_GETOPT_H + +#endif /* _CONFIG_H */ + diff --git a/tools/maskimg/fixplanets.sh b/tools/maskimg/fixplanets.sh new file mode 100755 index 000000000..504588581 --- /dev/null +++ b/tools/maskimg/fixplanets.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +if [ $# -ne 2 ]; then + echo "Syntax: fixplanets.sh " + exit 1 +fi + +INDIR="$1" +OUTDIR="$2" + +MASKIMG="$PWD/maskimg" + +if [ ! -d "$INDIR" ]; then + echo "\"${INDIR}\" is not a good source dir" + exit 1 +fi + +if [ ! -d "$OUTDIR" -o ! -w "$OUTDIR" ]; then + echo "\"${OUTDIR}\" is not a good destination" + exit 1 +fi + +INDIR="${INDIR%/}/" +OUTDIR="${OUTDIR%/}/" + +processone() { + FILE="$1" + FILE="${FILE#${INDIR}}" + DIR="${FILE%/*}" + echo "Now processing $FILE" + if [ "$DIR" != "$FILE" -a ! -d "${OUTDIR}${DIR}" ]; then + mkdir -p -- "${OUTDIR}${DIR}" + fi + if [ "$DIR" = "$FILE" ]; then + DIR="" + fi + "$MASKIMG" -vv -01 -m "$2" -o "$OUTDIR$FILE" "$INDIR$FILE" +} + +for FILE in `find "$INDIR" -type f -name "*__big.0.png"`; do + processone "$FILE" "mask_big.png" +done + +for FILE in `find "$INDIR" -type f -name "*__sml.0.png"`; do + processone "$FILE" "mask_sml.png" +done diff --git a/tools/maskimg/mask_big.png b/tools/maskimg/mask_big.png new file mode 100755 index 000000000..c512db104 Binary files /dev/null and b/tools/maskimg/mask_big.png differ diff --git a/tools/maskimg/mask_sml.png b/tools/maskimg/mask_sml.png new file mode 100755 index 000000000..ef42717b6 Binary files /dev/null and b/tools/maskimg/mask_sml.png differ diff --git a/tools/maskimg/maskimg.c b/tools/maskimg/maskimg.c new file mode 100755 index 000000000..02ef40394 --- /dev/null +++ b/tools/maskimg/maskimg.c @@ -0,0 +1,491 @@ +/* + * Truecolor image masking (transparency) tool + * By Alex Volkov (codepro@usa.net), 20050222 + * + * The GPL applies + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(WIN32) && defined(_MSC_VER) +# include +# include +# define inline __inline +#else +# include +#endif +#if defined(__GNUC__) +# include +# define inline __inline__ +#endif +#include + +#define countof(a) ( sizeof(a)/sizeof(*a) ) +#ifndef offsetof +# define offsetof(s,m) ( (size_t)(&((s*)0)->m) ) +#endif + +struct options +{ + const char *infile; + const char *outfile; + char *maskfile; + int maskoff; + int maskon; + int verbose; +}; + +typedef struct +{ + png_structp png_ptr; + png_infop info_ptr; + png_infop end_info; + + png_uint_32 w, h; + int bit_depth; + int color_type; + int interlace_type; + int compression_type; + int filter_type; + + png_color_8p sig_bit; + png_color_16p trans_values; + png_color_16 trans_values_buf; + png_color_16 trans_scaled; + png_bytep trans; + int num_trans; + int trans_index; + int srgb_intent; + + int channels; + int bpp; + png_bytep data; + png_bytep* lines; + +} image_t; + +int verbose_level = 0; +void verbose(int level, const char* fmt, ...); +void parse_arguments(int argc, char *argv[], struct options *opts); + +image_t* readImage(const char* filename); +int writeImage(image_t *, const char* filename); +void freeImage(image_t *); +int getImageTransIndex(image_t *); +void maskImage(image_t *, image_t* mask, int maskon, int maskoff); + + +int main(int argc, char *argv[]) +{ + struct options opts; + image_t* img; + image_t* mask; + + parse_arguments(argc, argv, &opts); + verbose_level = opts.verbose; + + img = readImage(opts.infile); + if (!img) + return EXIT_FAILURE; + if (img->color_type != PNG_COLOR_TYPE_RGB) + { + verbose(1, "Input image must be RGB, 8 bits per channel (or fix the code)\n"); + freeImage(img); + return EXIT_FAILURE; + } + if (!img->trans_values) + { + verbose(2, "%s has no tRNS chunk, assuming transparent black (RBG 0,0,0)\n", + opts.infile); + img->trans_values = &img->trans_values_buf; + } + img->num_trans = 0; + + mask = readImage(opts.maskfile); + if (!mask) + { + freeImage(img); + return EXIT_FAILURE; + } + if (mask->w < img->w || mask->h < img->h) + { + verbose(1, "Mask image dimensions are smaller than input image\n"); + freeImage(img); + freeImage(mask); + return EXIT_FAILURE; + } + else if (mask->w != img->w || mask->h != img->h) + verbose(2, "Warning: input image and mask have different dimensions\n"); + + if (mask->color_type != PNG_COLOR_TYPE_GRAY && mask->color_type != PNG_COLOR_TYPE_PALETTE) + { + verbose(1, "Mask image must be grayscale or paletted (or fix the code)\n"); + freeImage(img); + freeImage(mask); + return EXIT_FAILURE; + } + if (mask->trans) + { + mask->trans_index = getImageTransIndex(mask); + verbose(2, "Using mask image transparency info; trans index %d\n", mask->trans_index); + } + else + { + mask->trans_index = 0; + verbose(2, "Mask image has no transparency info; using index %d\n", mask->trans_index); + } + + if (!opts.maskon && !opts.maskoff) + { + freeImage(img); + freeImage(mask); + verbose(1, "Nothing to do; specify -0 or -1\n"); + return EXIT_SUCCESS; + } + + maskImage(img, mask, opts.maskon, opts.maskoff); + writeImage(img, opts.outfile); + + freeImage(img); + freeImage(mask); + + return EXIT_SUCCESS; +} + +void verbose(int level, const char* fmt, ...) +{ + va_list args; + + if (verbose_level < level) + return; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +void usage() +{ + fprintf(stderr, + "maskimg [-0] [-1] [-o ] -m \n" + "Options:\n" + "\t-o write resulting png into \n" + "\t-m specifies mask image to use (must be present)\n" + "\t-0 mask pixels off by setting them to transparent color\n" + "\t-1 mask pixels on by bumping red channel slightly\n" + "\t-v increase verbosity level (use more than once)\n" + ); +} + +void parse_arguments(int argc, char *argv[], struct options *opts) +{ + char ch; + + memset(opts, 0, sizeof (struct options)); + + while (-1 != (ch = getopt(argc, argv, "h?o:m:01v"))) + { + switch (ch) + { + case 'o': + opts->outfile = optarg; + break; + case 'm': + opts->maskfile = optarg; + break; + case '0': + opts->maskoff = 1; + break; + case '1': + opts->maskon = 1; + break; + case 'v': + opts->verbose++; + break; + case '?': + case 'h': + default: + usage(); + exit(EXIT_FAILURE); + } + } + argc -= optind; + argv += optind; + if (argc != 1) + { + usage(); + exit(EXIT_FAILURE); + } + opts->infile = argv[0]; + if (!opts->outfile) + opts->outfile = opts->infile; + + if (!opts->maskfile) + { + fprintf(stderr, "Not mask image specified, use -m\n"); + usage(); + exit(EXIT_FAILURE); + } +} + +image_t* readImage(const char* filename) +{ + image_t* img; + uint8_t header[8]; + size_t cbhead; + FILE* fp; + + fp = fopen(filename, "rb"); + if (!fp) + { + verbose(1, "Error: Could not open file %s: %s\n", + filename, strerror(errno)); + return NULL; + } + + cbhead = fread(header, 1, sizeof(header), fp); + if (png_sig_cmp(header, 0, cbhead)) + { + verbose(1, "Input file is not a PNG image\n"); + fclose(fp); + return NULL; + } + + img = malloc(sizeof(*img)); + if (!img) + { + verbose(1, "Out of memory reading image\n"); + fclose(fp); + return NULL; + } + memset(img, 0, sizeof(*img)); + + img->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!img->png_ptr) + { + verbose(1, "png_create_read_struct failed\n"); + fclose(fp); + return NULL; + } + img->info_ptr = png_create_info_struct(img->png_ptr); + if (!img->info_ptr) + { + verbose(1, "png_create_info_struct failed\n"); + png_destroy_read_struct(&img->png_ptr, NULL, NULL); + fclose(fp); + return NULL; + } + + img->end_info = png_create_info_struct(img->png_ptr); + if (!img->end_info) + { + verbose(1, "png_create_info_struct failed\n"); + png_destroy_read_struct(&img->png_ptr, &img->info_ptr, NULL); + fclose(fp); + return NULL; + } + + if (setjmp(png_jmpbuf(img->png_ptr))) + { + verbose(1, "PNG error\n"); + png_destroy_read_struct(&img->png_ptr, &img->info_ptr, &img->end_info); + fclose(fp); + return NULL; + } + + png_init_io(img->png_ptr, fp); + png_set_sig_bytes(img->png_ptr, cbhead); + + png_read_info(img->png_ptr, img->info_ptr); + + png_get_IHDR(img->png_ptr, img->info_ptr, &img->w, &img->h, &img->bit_depth, + &img->color_type, &img->interlace_type, &img->compression_type, + &img->filter_type); + png_get_tRNS(img->png_ptr, img->info_ptr, &img->trans, &img->num_trans, &img->trans_values); + if (img->trans_values) + img->trans_scaled = *img->trans_values; + png_get_sBIT(img->png_ptr, img->info_ptr, &img->sig_bit); + if (!png_get_sRGB(img->png_ptr, img->info_ptr, &img->srgb_intent)) + img->srgb_intent = -1; + img->channels = png_get_channels(img->png_ptr, img->info_ptr); + + if (img->bit_depth < 8) + { // make sure we get 1 pixel per byte + png_set_packing(img->png_ptr); + } + else if (img->bit_depth == 16) + { // or 1 byte per channel + png_set_strip_16(img->png_ptr); + img->bit_depth = 8; + if (img->sig_bit) + { + if (img->sig_bit->red > 8) img->sig_bit->red = 8; + if (img->sig_bit->green > 8) img->sig_bit->green = 8; + if (img->sig_bit->blue > 8) img->sig_bit->blue = 8; + if (img->sig_bit->alpha > 8) img->sig_bit->alpha = 8; + if (img->sig_bit->gray > 8) img->sig_bit->gray = 8; + } + img->trans_scaled.red >>= 8; + img->trans_scaled.green >>= 8; + img->trans_scaled.blue >>= 8; + } + else if (img->sig_bit) + { // bit_depth == 8 + png_set_shift(img->png_ptr, img->sig_bit); + img->trans_scaled.red >>= 8 - img->sig_bit->red; + img->trans_scaled.green >>= 8 - img->sig_bit->green; + img->trans_scaled.blue >>= 8 - img->sig_bit->blue; + } + + img->bpp = img->channels; + img->data = malloc(img->bpp * img->w * img->h); + if (!img->data) + { + verbose(1, "Out of memory reading image\n"); + png_destroy_read_struct(&img->png_ptr, &img->info_ptr, &img->end_info); + fclose(fp); + return NULL; + } + img->lines = malloc(img->h * sizeof(img->lines[0])); + if (!img->lines) + { + verbose(1, "Out of memory reading image\n"); + png_destroy_read_struct(&img->png_ptr, &img->info_ptr, &img->end_info); + fclose(fp); + return NULL; + } + { // init lines + png_uint_32 i; + for (i = 0; i < img->h; ++i) + img->lines[i] = img->data + img->w * img->bpp * i; + } + png_read_image(img->png_ptr, img->lines); + img->interlace_type = PNG_INTERLACE_NONE; + + png_read_end(img->png_ptr, img->end_info); + + fclose(fp); + + return img; +} + +void freeImage(image_t* img) +{ + if (!img) + return; + + png_destroy_read_struct(&img->png_ptr, &img->info_ptr, &img->end_info); + if (img->lines) + free(img->lines); + if (img->data) + free(img->data); + free(img); +} + +int writeImage(image_t* img, const char* filename) +{ + FILE *file; + png_structp png_ptr; + png_infop info_ptr; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + verbose(1, "png_create_write_struct failed.\n"); + return EXIT_FAILURE; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + verbose(1, "png_create_info_struct failed.\n"); + return EXIT_FAILURE; + } + if (setjmp(png_jmpbuf(png_ptr))) + { + verbose(1, "png error.\n"); + png_destroy_write_struct(&png_ptr, &info_ptr); + return EXIT_FAILURE; + } + + file = fopen(filename, "wb"); + if (!file) + { + verbose(1, "Could not open file '%s': %s\n", + filename, strerror(errno)); + png_destroy_write_struct(&png_ptr, &info_ptr); + return EXIT_FAILURE; + } + png_init_io(png_ptr, file); + png_set_IHDR(png_ptr, info_ptr, img->w, img->h, img->bit_depth, + img->color_type, img->interlace_type, img->compression_type, + img->filter_type); + png_set_sBIT(png_ptr, info_ptr, img->sig_bit); + png_set_shift(png_ptr, img->sig_bit); + png_set_tRNS(png_ptr, info_ptr, NULL, 0, img->trans_values); + png_write_info(png_ptr, info_ptr); + png_set_packing(png_ptr); + png_write_image(png_ptr, (png_byte **) img->lines); + png_write_end(png_ptr, img->end_info); + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(file); + + return EXIT_SUCCESS; +} + +int getImageTransIndex(image_t* img) +{ + int i; + + for (i = 0; i < img->num_trans && img->trans[i] != 0; ++i) + ; + + return (i < img->num_trans) ? i : 0; +} + +void maskImage(image_t* img, image_t* mask, int maskon, int maskoff) +{ + png_uint_32 x, y; + int img_pitch, mask_pitch; + png_bytep imgp, maskp; + + img_pitch = img->w * img->bpp; + mask_pitch = mask->w * mask->bpp; + + for (y = 0; y < img->h; ++y) + { + imgp = img->data + img_pitch * y; + maskp = mask->data + mask_pitch * y; + + for (x = 0; x < img->w; ++x, imgp += img->bpp, maskp += mask->bpp) + { + if (maskoff && maskp[0] == mask->trans_index) + { // set the pixel to transparent values + imgp[0] = (png_byte)img->trans_scaled.red; + imgp[1] = (png_byte)img->trans_scaled.green; + imgp[2] = (png_byte)img->trans_scaled.blue; + } + if (maskon && maskp[0] != mask->trans_index + && imgp[0] == img->trans_scaled.red + && imgp[1] == img->trans_scaled.green + && imgp[2] == img->trans_scaled.blue + ) + { // mask the pixel on by bumping the red channel away from transparent value + if (imgp[0] == 0xff) + --imgp[0]; + else + ++imgp[0]; + } + } + } +} diff --git a/tools/maskimg/maskimg.dsp b/tools/maskimg/maskimg.dsp new file mode 100755 index 000000000..c6199701e --- /dev/null +++ b/tools/maskimg/maskimg.dsp @@ -0,0 +1,114 @@ +# Microsoft Developer Studio Project File - Name="maskimg" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=maskimg - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "maskimg.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "maskimg.mak" CFG="maskimg - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "maskimg - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "maskimg - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=xicl6.exe +RSC=rc.exe + +!IF "$(CFG)" == "maskimg - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\src\getopt" /I "." /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "PNG_USE_DLL" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib advapi32.lib shell32.lib libpng10.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "maskimg - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MD /W3 /Gm /GX /ZI /Od /I "..\..\src\getopt" /I "." /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "PNG_USE_DLL" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib advapi32.lib shell32.lib libpng10.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "maskimg - Win32 Release" +# Name "maskimg - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\config.h +# End Source File +# Begin Source File + +SOURCE=..\..\src\getopt\getopt.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\getopt\getopt.h +# End Source File +# Begin Source File + +SOURCE=.\lzhuf.c +# End Source File +# Begin Source File + +SOURCE=.\lzhuf.h +# End Source File +# Begin Source File + +SOURCE=.\maskimg.c +# End Source File +# End Group +# End Target +# End Project diff --git a/tools/maskimg/maskimg.dsw b/tools/maskimg/maskimg.dsw new file mode 100755 index 000000000..27d94875a --- /dev/null +++ b/tools/maskimg/maskimg.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "maskimg"=.\maskimg.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### +