From a47fda722dd8484f1c049821eae257973f451697 Mon Sep 17 00:00:00 2001 From: avolkov Date: Fri, 23 Dec 2005 04:49:43 +0000 Subject: [PATCH] added a simple palette color arithmetic tool git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2103 8092fc87-c524-0410-9efc-e669fe64eaf9 --- tools/dos-pal/Makefile | 20 +- tools/dos-pal/dos-pal.dsw | 14 +- tools/dos-pal/palarith.c | 527 +++++++++++++++++++++++++++++++++++++ tools/dos-pal/palarith.dsp | 106 ++++++++ 4 files changed, 659 insertions(+), 8 deletions(-) create mode 100755 tools/dos-pal/palarith.c create mode 100755 tools/dos-pal/palarith.dsp diff --git a/tools/dos-pal/Makefile b/tools/dos-pal/Makefile index 45f4651d2..08f38da00 100755 --- a/tools/dos-pal/Makefile +++ b/tools/dos-pal/Makefile @@ -5,7 +5,9 @@ # compiling this. # -TARGET = pal2uqm +TARGET1 = pal2uqm +TARGET2 = palarith + # These flags are needed to get it to compile with libpngX.dll on CYGWIN #CYGWINFLAGS = @@ -14,19 +16,23 @@ CFLAGS = -W -Wall -O0 # -g LIBS = -L/usr/local/lib -lm LDFLAGS= -BINS = $(TARGET) $(TARGET).exe -SRCS = pal2uqm.c -OBJS = pal2uqm.o +BINS = $(TARGET1) $(TARGET2) $(TARGET1).exe $(TARGET2).exe +SRCS = pal2uqm.c palarith.c +OBJS1 = pal2uqm.o +OBJS2 = palarith.o .SUFFIXES: .c .o .c.o: $(CC) -c $(CFLAGS) -o $@ $*.c $(CYGWINFLAGS) -all: $(TARGET) +all: $(TARGET1) $(TARGET2) -$(TARGET): $(OBJS) - $(CC) -o $(TARGET) $(OBJS) $(LIBS) $(LDFLAGS) +$(TARGET1): $(OBJS1) + $(CC) -o $(TARGET1) $(OBJS1) $(LIBS) $(LDFLAGS) + +$(TARGET2): $(OBJS2) + $(CC) -o $(TARGET2) $(OBJS2) $(LIBS) $(LDFLAGS) clean: rm -f $(BINS) *.o diff --git a/tools/dos-pal/dos-pal.dsw b/tools/dos-pal/dos-pal.dsw index 9f95b4cd3..333d0a8b5 100755 --- a/tools/dos-pal/dos-pal.dsw +++ b/tools/dos-pal/dos-pal.dsw @@ -3,7 +3,19 @@ Microsoft Developer Studio Workspace File, Format Version 6.00 ############################################################################### -Project: "pal2uqm"=.\pal2uqm.dsp - Package Owner=<4> +Project: "pal2uqm"=".\pal2uqm.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "palarith"=".\palarith.dsp" - Package Owner=<4> Package=<5> {{{ diff --git a/tools/dos-pal/palarith.c b/tools/dos-pal/palarith.c new file mode 100755 index 000000000..cd4f1f86e --- /dev/null +++ b/tools/dos-pal/palarith.c @@ -0,0 +1,527 @@ +/* + * UQM palette arithmetic + * By Alex Volkov (codepro@usa.net), 20051222 + * + * The GPL applies (to this file). + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(WIN32) && defined(_MSC_VER) +# include +# include +# define makedir(d) _mkdir(d) +# define inline __inline +#else +# include +# define makedir(d) mkdir(d, 0777) +#endif +#if defined(__GNUC__) +# include +# define inline __inline__ +#endif + +#define countof(a) ( sizeof(a)/sizeof(*a) ) +#ifndef offsetof +# define offsetof(s,m) ( (size_t)(&((s*)0)->m) ) +#endif + +#if defined(_MSC_VER) +# define PACKED +# pragma pack(push, 1) +#elif defined(__GNUC__) +# define PACKED __attribute__((packed)) +#endif + +// Palette types +typedef uint8_t channel_t; + +typedef struct +{ + channel_t r, g, b; +} color_t; + +#define PAL_COLORS 0x100 + +typedef union +{ + color_t colors[PAL_COLORS]; + channel_t chans[PAL_COLORS * 3]; +} uqm_palette_t; + +typedef struct +{ + float y, u, v; +} yuv_t; + +#if defined(_MSC_VER) +# pragma pack(pop) +#endif + +struct options +{ + char *infile; + char *outfile; + int from, to; // inclusive bounds + int luma; + int add; + int sub; + int blend; + color_t color; + int color_set; + int prorate; + int verbose; +}; + + +int verbose_level = 0; +void verbose(int level, const char* fmt, ...); +void parse_arguments(int argc, char* argv[], struct options* opts); +int readPalette(uqm_palette_t*, const char* filename); +int writePalette(uqm_palette_t*, const char* filename); + +void setColor(uqm_palette_t*, int from, int to, color_t* clr); +void adjustLuma(uqm_palette_t*, int from, int to, int ratio); +void setColorLuma(uqm_palette_t*, int from, int to, int ratio, color_t* toclr); +void addColor(uqm_palette_t*, int from, int to, color_t* clr, int prorate); +void subColor(uqm_palette_t*, int from, int to, color_t* clr, int prorate); +void blendToColor(uqm_palette_t*, int from, int to, int ratio, color_t* toclr, int prorate); + +int main(int argc, char* argv[]) +{ + struct options opts; + int ret; + uqm_palette_t pal; + + parse_arguments(argc, argv, &opts); + verbose_level = opts.verbose; + + verbose(2, "Processing '%s', index %d-%d\n", opts.infile, opts.from, opts.to); + + memset(&pal, 0, sizeof(pal)); + + ret = readPalette(&pal, opts.infile); + if (ret != 0) + return ret; + + if (opts.luma < 0 && !opts.add && !opts.sub && !opts.blend) + setColor(&pal, opts.from, opts.to, &opts.color); + + if (opts.luma >= 0) + { + if (opts.color_set && !opts.add && !opts.sub && !opts.blend) + setColorLuma(&pal, opts.from, opts.to, opts.luma, &opts.color); + else + adjustLuma(&pal, opts.from, opts.to, opts.luma); + } + + if (opts.add) + addColor(&pal, opts.from, opts.to, &opts.color, opts.prorate); + else if (opts.sub) + subColor(&pal, opts.from, opts.to, &opts.color, opts.prorate); + else if (opts.blend) + blendToColor(&pal, opts.from, opts.to, opts.blend, &opts.color, + opts.prorate); + + ret = writePalette(&pal, opts.outfile); + + return ret; +} + +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, + "palarith [-r range] [-l level] [-a|-s|-b ratio] [-c RRGGBB] [-p]\n" + " [-o ] \n" + "Options:\n" + "\t-r color index range within palette to work on (form: XXX-YYY)\n" + "\t-l adjust luminosity to level (in percent)\n" + "\t-a add color (requires -c; exclusive with -s and -b)\n" + "\t-s subtract color (requires -c; exclusive with -a and -b)\n" + "\t-b blend with color, ratio is percent of color (requires -c)\n" + "\t-c target color for ops that use it (hex; 00ff00 for green)\n" + "\t-p pro-rate the added, subed or blended color to current luma\n" + "\t-o output palette filename\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)); + opts->to = PAL_COLORS - 1; + opts->luma = -1; + + while (-1 != (ch = getopt(argc, argv, "h?o:r:l:asb:c:pv"))) + { + switch (ch) + { + case 'r': + { + char* pto = strchr(optarg, ':'); + if (pto) + { + *pto = '\0'; + ++pto; + } + if (!*optarg && (!pto || !*pto)) + { + fprintf(stderr, "Invalid argument to -r\n"); + exit(EXIT_FAILURE); + } + if (*optarg) + opts->from = atoi(optarg); + if (pto && *pto) + opts->to = atoi(pto); + + if (opts->to < opts->from + || opts->from < 0 || opts->from >= PAL_COLORS + || opts->to < 0 || opts->to >= PAL_COLORS) + { + fprintf(stderr, "Out of range argument to -r\n"); + exit(EXIT_FAILURE); + } + break; + } + case 'c': + { + int r, g, b; + if (3 != sscanf(optarg, "%2x%2x%2x", &r, &g, &b)) + { + fprintf(stderr, "Bad -c RRGGBB option '%s'\n", optarg); + exit(EXIT_FAILURE); + } + opts->color.r = r; + opts->color.g = g; + opts->color.b = b; + opts->color_set = 1; + break; + } + case 'l': + opts->luma = atoi(optarg); + if (opts->luma < 0 || opts->luma > 255 * 100) + { + fprintf(stderr, "Out of range -i option '%s'\n", optarg); + exit(EXIT_FAILURE); + } + break; + case 'a': + opts->add = 1; + break; + case 's': + opts->sub = 1; + break; + case 'b': + opts->blend = atoi(optarg); + if (opts->blend < 0 || opts->blend > 100) + { + fprintf(stderr, "Out of range -b option '%s'\n", optarg); + exit(EXIT_FAILURE); + } + break; + case 'p': + opts->prorate = 1; + break; + case 'o': + opts->outfile = optarg; + 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->luma < 0 && !opts->color_set && !opts->add && !opts->sub && !opts->blend) + { + fprintf(stderr, "Please specify one or more of -i, -c, -a, -s or -b\n"); + exit(EXIT_FAILURE); + } + + if ((opts->add || opts->sub || opts->blend) && !opts->color_set) + { + fprintf(stderr, "Options -a, -s and -b require -c\n"); + exit(EXIT_FAILURE); + } +} + +int readPalette(uqm_palette_t* pal, const char* filename) +{ + FILE* in; + size_t inlen; + + in = fopen(filename, "rb"); + if (!in) + { + verbose(1, "Error: Could not open file %s: %s\n", filename, + strerror(errno)); + return EXIT_FAILURE; + } + + fseek(in, 0, SEEK_END); + inlen = ftell(in); + fseek(in, 0, SEEK_SET); + + if (inlen < sizeof(*pal)) + { + verbose(1, "Warning: palette '%s' is incomplete\n", filename); + } + else if (inlen > sizeof(*pal)) + { + verbose(1, "Warning: palette '%s' appears too large\n", filename); + inlen = sizeof(*pal); + } + + if (1 != fread(pal, inlen, 1, in)) + { + verbose(1, "Cannot read file '%s'\n", filename); + fclose(in); + return EXIT_FAILURE; + } + + fclose(in); + + return 0; +} + +int writePalette(uqm_palette_t* pal, const char* filename) +{ + FILE* f; + + f = fopen(filename, "wb"); + if (!f) + { + verbose(1, "Error: Could not open file %s: %s\n", filename, + strerror(errno)); + return EXIT_FAILURE; + } + if (1 != fwrite(pal, sizeof(*pal), 1, f)) + { + verbose(1, "Cannot write file '%s'\n", filename); + fclose(f); + return EXIT_FAILURE; + } + + fclose(f); + + return EXIT_SUCCESS; +} + +int calc_y(color_t rgb) +{ + int y; + + y = 0.299f * rgb.r + 0.587f * rgb.g + 0.114f * rgb.b; + + return y; +} + +inline int clamp_chan(int c) +{ + if (c > 255) + c = 255; + if (c < 0) + c = 0; + return c; +} + +int get_luma(color_t rgb) +{ + if (rgb.r >= rgb.g && rgb.r >= rgb.b) + return rgb.r; + else if (rgb.g >= rgb.r && rgb.g >= rgb.b) + return rgb.g; + else + return rgb.b; +} + +void adjustLuma(uqm_palette_t* pal, int from, int to, int ratio) +{ + int i; + color_t* sclr; + float fact = 0.01f * ratio; + int minl = 255, maxl = 0; + float boost = 0; + + // get some statistics first + for (i = from, sclr = pal->colors + from; i <= to; ++i, ++sclr) + { + int l = get_luma(*sclr); + if (l < minl) + minl = l; + if (l > maxl) + maxl = l; + } + + if (maxl * fact > 255) + { + boost = (maxl * fact - 255) / (255.0 * 255.0 / 100.0); + } + + if (ratio > 100) + fact = 1.0f + 0.0025f * (ratio - 100); + + for (i = from, sclr = pal->colors + from; i <= to; ++i, ++sclr) + { + color_t clr = *sclr; + + sclr->r = clamp_chan(fact * clr.r + boost * (maxl - clr.r)); + sclr->g = clamp_chan(fact * clr.g + boost * (maxl - clr.g)); + sclr->b = clamp_chan(fact * clr.b + boost * (maxl - clr.b)); + } +} + +void setColorLuma(uqm_palette_t* pal, int from, int to, + int ratio, color_t* toclr) +{ + int i; + color_t* sclr; + float fact = 0.01f * ratio; + int minl = 255, maxl = 0; + float boost = 0; + + // get some statistics first + for (i = from, sclr = pal->colors + from; i <= to; ++i, ++sclr) + { + int l = get_luma(*sclr); + if (l < minl) + minl = l; + if (l > maxl) + maxl = l; + } + + if (maxl * fact > 255) + { + boost = (maxl * fact - 255) / (255.0 * 255.0 / 100.0); + } + + if (ratio > 100) + fact = 1.0f + 0.0025f * (ratio - 100); + + for (i = from, sclr = pal->colors + from; i <= to; ++i, ++sclr) + { + color_t clr; + int l = get_luma(*sclr); + + clr.r = toclr->r * l / 255; + clr.g = toclr->g * l / 255; + clr.b = toclr->b * l / 255; + + sclr->r = clamp_chan(fact * clr.r + boost * (maxl - clr.r)); + sclr->g = clamp_chan(fact * clr.g + boost * (maxl - clr.g)); + sclr->b = clamp_chan(fact * clr.b + boost * (maxl - clr.b)); + } +} + +void setColor(uqm_palette_t* pal, int from, int to, color_t* clr) +{ + int i; + color_t* dclr; + + for (i = from, dclr = pal->colors + from; i <= to; ++i, ++dclr) + { + *dclr = *clr; + } +} + +void addColor(uqm_palette_t* pal, int from, int to, color_t* clr, int prorate) +{ + int i; + color_t* sclr; + + for (i = from, sclr = pal->colors + from; i <= to; ++i, ++sclr) + { + color_t dclr = *clr; + if (prorate) + { + int l = get_luma(*sclr); + dclr.r = dclr.r * l / 255; + dclr.g = dclr.g * l / 255; + dclr.b = dclr.b * l / 255; + } + sclr->r = clamp_chan(sclr->r + dclr.r); + sclr->g = clamp_chan(sclr->g + dclr.g); + sclr->b = clamp_chan(sclr->b + dclr.b); + } +} + +void subColor(uqm_palette_t* pal, int from, int to, color_t* clr, int prorate) +{ + int i; + color_t* sclr; + + for (i = from, sclr = pal->colors + from; i <= to; ++i, ++sclr) + { + color_t dclr = *clr; + if (prorate) + { + int l = get_luma(*sclr); + dclr.r = dclr.r * l / 255; + dclr.g = dclr.g * l / 255; + dclr.b = dclr.b * l / 255; + } + sclr->r = clamp_chan(sclr->r - dclr.r); + sclr->g = clamp_chan(sclr->g - dclr.g); + sclr->b = clamp_chan(sclr->b - dclr.b); + } +} + +void blendToColor(uqm_palette_t* pal, int from, int to, + int ratio, color_t* toclr, int prorate) +{ + int i; + color_t* sclr; + float dfact = 0.01f * ratio; + float sfact = 0.01f * (100 - ratio); + + for (i = from, sclr = pal->colors + from; i <= to; ++i, ++sclr) + { + color_t dclr = *toclr; + if (prorate) + { + int l = get_luma(*sclr); + dclr.r = dclr.r * l / 255; + dclr.g = dclr.g * l / 255; + dclr.b = dclr.b * l / 255; + } + sclr->r = clamp_chan(sclr->r * sfact + dclr.r * dfact); + sclr->g = clamp_chan(sclr->g * sfact + dclr.g * dfact); + sclr->b = clamp_chan(sclr->b * sfact + dclr.b * dfact); + } +} diff --git a/tools/dos-pal/palarith.dsp b/tools/dos-pal/palarith.dsp new file mode 100755 index 000000000..873e80fc7 --- /dev/null +++ b/tools/dos-pal/palarith.dsp @@ -0,0 +1,106 @@ +# Microsoft Developer Studio Project File - Name="palarith" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=palarith - 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 "palarith.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 "palarith.mak" CFG="palarith - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "palarith - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "palarith - 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)" == "palarith - 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" /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 /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "palarith - 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" /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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "palarith - Win32 Release" +# Name "palarith - 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=.\palarith.c +# End Source File +# End Group +# End Target +# End Project