Battle planet images transparency masking tool; first version

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@1563 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
avolkov
2005-02-23 06:39:47 +00:00
parent 2216d058d1
commit 4d23cf4e78
8 changed files with 729 additions and 0 deletions
+491
View File
@@ -0,0 +1,491 @@
/*
* Truecolor image masking (transparency) tool
* By Alex Volkov (codepro@usa.net), 20050222
*
* The GPL applies
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <malloc.h>
#if defined(WIN32) && defined(_MSC_VER)
# include <getopt.h>
# include <direct.h>
# define inline __inline
#else
# include <unistd.h>
#endif
#if defined(__GNUC__)
# include <alloca.h>
# define inline __inline__
#endif
#include <png.h>
#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 <outfile>] -m <maskfile> <infile>\n"
"Options:\n"
"\t-o write resulting png into <outfile>\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];
}
}
}
}