386 lines
10 KiB
C++
386 lines
10 KiB
C++
// _________ __ __
|
|
// / _____// |_____________ _/ |______ ____ __ __ ______
|
|
// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
|
|
// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
|
|
// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
|
|
// \/ \/ \//_____/ \/
|
|
// ______________________ ______________________
|
|
// T H E W A R B E G I N S
|
|
// Utility for Stratagus - A free fantasy real time strategy game engine
|
|
//
|
|
// Copyright (C) 2002 Ingo Ruhnke <grumbel@gmx.de>
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
/* To compile this programm:
|
|
|
|
% g++ -o png2stratagus png2stratagus.cpp -lpng
|
|
*/
|
|
|
|
/* This programm can be used to fix the palette of a indexed png file
|
|
to be suitable for Stratagus. It works like this:
|
|
|
|
1) You create a RGBA image in Gimp
|
|
|
|
2) You convert it to indexed with 227 colors
|
|
|
|
[Generate Optimal Palette # Colors 227] (MAX_COLORS - 1)
|
|
|
|
3) You run png2stratagus on the image
|
|
|
|
4) The final images will be written to out.png in the current
|
|
directory
|
|
*/
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <iostream>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <png.h>
|
|
#include <assert.h>
|
|
|
|
#define MAX_COLORS 228
|
|
|
|
class Color
|
|
{
|
|
public:
|
|
int red;
|
|
int green;
|
|
int blue;
|
|
|
|
Color ()
|
|
: red (0), green (255), blue (0)
|
|
{
|
|
}
|
|
|
|
Color (int r, int g, int b)
|
|
: red (r), green (g), blue (b)
|
|
{
|
|
}
|
|
};
|
|
|
|
class Image
|
|
{
|
|
private:
|
|
int m_width;
|
|
int m_height;
|
|
int m_transcol;
|
|
std::vector<int> m_image;
|
|
std::vector<Color> m_palette;
|
|
|
|
public:
|
|
/** Load an image from a given png source */
|
|
Image (const std::string& filename)
|
|
{
|
|
FILE* fp;
|
|
png_structp png_ptr;
|
|
png_infop info_ptr;
|
|
png_uint_32 pwidth, pheight;
|
|
int bit_depth, color_type, interlace_type, compression_type, filter_type;
|
|
int row_bytes;
|
|
|
|
if ((fp = fopen(filename.c_str (), "rb")) == nullptr)
|
|
{
|
|
perror (filename.c_str ());
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
png_init_io(png_ptr, fp);
|
|
png_read_info(png_ptr, info_ptr);
|
|
png_get_IHDR(png_ptr, info_ptr, &pwidth, &pheight,
|
|
&bit_depth, &color_type, &interlace_type,
|
|
&compression_type, &filter_type);
|
|
row_bytes = png_get_rowbytes(png_ptr, info_ptr);
|
|
|
|
// Create the 'data' array
|
|
std::vector<png_bytep> row_pointers;
|
|
row_pointers.resize(pheight);
|
|
for (unsigned int i = 0; i < pheight; i++)
|
|
row_pointers[i] = new png_byte[row_bytes];
|
|
|
|
png_read_image(png_ptr, &row_pointers[0]);
|
|
|
|
if (color_type != PNG_COLOR_TYPE_PALETTE)
|
|
{
|
|
std::cout << "Unsupported color type" << std::endl;
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
int num_colors;
|
|
int num_trans = 0;
|
|
png_colorp lpalette;
|
|
png_bytep trans;
|
|
png_color_16p trans_values;
|
|
|
|
// Read some more data
|
|
png_get_PLTE(png_ptr, info_ptr, &lpalette, &num_colors);
|
|
png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
|
|
|
|
// not sure what trans_values stand for
|
|
if (num_trans > 1)
|
|
{
|
|
std::cout << "Multiple transcolors not supported" << std::endl;
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
else if (num_trans == 1)
|
|
m_transcol = trans[0];
|
|
else
|
|
m_transcol = -1;
|
|
|
|
for (int i = 0; i < num_trans; i++)
|
|
std::cout << "transcolor: " << int(trans[i]) << std::endl;
|
|
|
|
m_width = pwidth;
|
|
m_height = pheight;
|
|
|
|
m_image.resize (m_width * m_height);
|
|
|
|
// Convert the png into our internal data structure
|
|
for (int y = 0; y < m_height; y++)
|
|
{
|
|
for (int i = 0; i < row_bytes; i++)
|
|
{
|
|
m_image[i + (m_width * y)] = row_pointers[y][i];
|
|
}
|
|
}
|
|
|
|
assert (num_colors <= 256);
|
|
|
|
if (num_colors > MAX_COLORS)
|
|
{
|
|
std::cout << "WARNING: Image has more than " << MAX_COLORS
|
|
<< " colors (" << num_colors << ")" << std::endl;
|
|
std::cout << "Assuming colors > " << MAX_COLORS
|
|
<< " are unused" << std::endl;
|
|
num_colors = MAX_COLORS;
|
|
}
|
|
|
|
m_palette.resize (256);
|
|
for (int i = 0; i < num_colors; ++i)
|
|
{
|
|
m_palette[i].red = lpalette[i].red;
|
|
m_palette[i].green = lpalette[i].green;
|
|
m_palette[i].blue = lpalette[i].blue;
|
|
}
|
|
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)nullptr);
|
|
fclose (fp);
|
|
}
|
|
|
|
~Image ()
|
|
{
|
|
}
|
|
|
|
void write_png (std::string filename)
|
|
{
|
|
FILE* fp;
|
|
png_structp png_ptr;
|
|
png_infop info_ptr;
|
|
png_colorp palette;
|
|
//png_uint_32 bytes_per_pixel = 1;
|
|
|
|
fp = fopen(filename.c_str (), "wb");
|
|
if (fp == nullptr)
|
|
assert (false);
|
|
|
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
|
|
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
png_init_io(png_ptr, fp);
|
|
|
|
png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8 /* bitdepth */,
|
|
PNG_COLOR_TYPE_PALETTE,
|
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
|
|
|
palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH
|
|
* sizeof (png_color));
|
|
|
|
std::cout << "MAX: " << PNG_MAX_PALETTE_LENGTH << std::endl;
|
|
for (unsigned int i = 0; i < m_palette.size(); ++i)
|
|
{
|
|
palette[i].red = m_palette[i].red;
|
|
palette[i].green = m_palette[i].green;
|
|
palette[i].blue = m_palette[i].blue;
|
|
}
|
|
|
|
/** insert palette converter here */
|
|
png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
|
|
|
|
png_write_info(png_ptr, info_ptr);
|
|
|
|
|
|
const png_uint_32 height = m_height, width = m_width;
|
|
std::vector<png_byte> image;
|
|
image.resize(height * width /* *bytes_per_pixel */);
|
|
std::vector<png_bytep> row_pointers;
|
|
row_pointers.resize(height);
|
|
|
|
// fill the image with data
|
|
for (unsigned int i = 0; i < m_image.size (); ++i)
|
|
{
|
|
image[i] = m_image[i];
|
|
}
|
|
|
|
for (unsigned int k = 0; k < height; k++)
|
|
row_pointers[k] = &image[k * width /* * bytes_per_pixel*/];
|
|
|
|
png_write_image(png_ptr, &row_pointers[0]);
|
|
|
|
png_write_end(png_ptr, info_ptr);
|
|
png_free(png_ptr, palette);
|
|
//png_free(png_ptr, trans);
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
fclose(fp);
|
|
}
|
|
|
|
/** swaps color a and color b in both the palette and the image */
|
|
void swap_colors(int a, int b)
|
|
{
|
|
std::swap(m_palette[a], m_palette[b]);
|
|
|
|
for (int &e : m_image) {
|
|
if (e == a)
|
|
e = b;
|
|
else if (e == b)
|
|
e = a;
|
|
}
|
|
}
|
|
|
|
void set_color (int i, Color c) {
|
|
m_palette[i] = c;
|
|
}
|
|
|
|
int get_transcolor () {
|
|
return m_transcol;
|
|
}
|
|
|
|
int num_colors () {
|
|
return m_palette.size ();
|
|
}
|
|
|
|
int get_width () {
|
|
return m_width;
|
|
}
|
|
|
|
int get_height () {
|
|
return m_height;
|
|
}
|
|
};
|
|
|
|
int main (int argc, char* argv[])
|
|
{
|
|
if (argc != 3)
|
|
{
|
|
printf ("Usage: %s INFILE OUTFILE\n", argv[0]);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
else
|
|
{
|
|
Image mypng (argv[1]);
|
|
std::cout << "Width: " << mypng.get_width ()
|
|
<< " Height: " << mypng.get_height ()
|
|
<< " Colors: " << mypng.num_colors () << std::endl;
|
|
|
|
/*
|
|
#0 general background (RGB = 0,0,0)
|
|
|
|
#208 replaced by player color 1
|
|
#209 replaced by player color 2
|
|
#210 replaced by player color 3
|
|
#211 replaced by player color 4
|
|
|
|
#38-#47 color cycle
|
|
#240-244 color cycle
|
|
|
|
#253 general editors color (should NEVER be used in any image, RGB = 255,50,255)
|
|
|
|
#255 general transparency color (RGB, 255,255,255)
|
|
*/
|
|
|
|
if (mypng.get_transcolor () != -1)
|
|
mypng.swap_colors (mypng.get_transcolor (), 255);
|
|
|
|
// Swap all colors to un used palette positions
|
|
mypng.swap_colors (0, 254);
|
|
mypng.swap_colors (208, 252);
|
|
mypng.swap_colors (209, 251);
|
|
mypng.swap_colors (210, 250);
|
|
mypng.swap_colors (211, 249);
|
|
|
|
// Water Cycle
|
|
mypng.swap_colors (38, 248);
|
|
mypng.swap_colors (39, 247);
|
|
mypng.swap_colors (40, 246);
|
|
mypng.swap_colors (41, 245);
|
|
mypng.swap_colors (42, 239);
|
|
mypng.swap_colors (43, 238);
|
|
mypng.swap_colors (44, 237);
|
|
mypng.swap_colors (45, 236);
|
|
mypng.swap_colors (46, 235);
|
|
mypng.swap_colors (47, 234);
|
|
|
|
mypng.swap_colors (240, 233);
|
|
mypng.swap_colors (241, 232);
|
|
mypng.swap_colors (242, 231);
|
|
mypng.swap_colors (243, 230);
|
|
mypng.swap_colors (244, 229);
|
|
|
|
// This are in a range which shouldn't get touched, so changing
|
|
//them might not be needed
|
|
//mypng.swap_colors (253, 250);
|
|
//mypng.swap_colors (255, 249);
|
|
|
|
// Set all hard-coded colors to some usefull defaults for preview
|
|
mypng.set_color (0, Color (0, 0, 0));
|
|
|
|
mypng.set_color (208, Color ( 60, 60, 0));
|
|
mypng.set_color (209, Color (110, 110, 0));
|
|
mypng.set_color (210, Color (200, 200, 0));
|
|
mypng.set_color (211, Color (255, 255, 0));
|
|
|
|
// Button cycle
|
|
mypng.set_color (240, Color (255, 0, 255));
|
|
mypng.set_color (241, Color (255, 0, 255));
|
|
mypng.set_color (242, Color (255, 0, 255));
|
|
mypng.set_color (243, Color (255, 0, 255));
|
|
mypng.set_color (244, Color (255, 0, 255));
|
|
|
|
// Water cycle
|
|
mypng.set_color (38, Color (255, 0, 255));
|
|
mypng.set_color (39, Color (255, 0, 255));
|
|
mypng.set_color (40, Color (255, 0, 255));
|
|
mypng.set_color (41, Color (255, 0, 255));
|
|
mypng.set_color (42, Color (255, 0, 255));
|
|
mypng.set_color (43, Color (255, 0, 255));
|
|
mypng.set_color (44, Color (255, 0, 255));
|
|
mypng.set_color (45, Color (255, 0, 255));
|
|
mypng.set_color (46, Color (255, 0, 255));
|
|
mypng.set_color (47, Color (255, 0, 255));
|
|
|
|
mypng.set_color (253, Color (255, 50, 255));
|
|
mypng.set_color (255, Color (255, 255, 255));
|
|
|
|
mypng.write_png (argv[2]);
|
|
|
|
std::cout << "Convert complete" << std::endl;
|
|
}
|
|
}
|
|
|
|
|
|
// EOF //
|