diff --git a/pandatool/src/egg-palettize/attribFile.cxx b/pandatool/src/egg-palettize/attribFile.cxx index ab7aec706c..adacc4f112 100644 --- a/pandatool/src/egg-palettize/attribFile.cxx +++ b/pandatool/src/egg-palettize/attribFile.cxx @@ -13,6 +13,10 @@ #include "palette.h" #include "sourceEgg.h" +#include +#include +#include + #include #include #include @@ -53,6 +57,8 @@ AttribFile(const Filename &filename) { _default_margin = 2; _force_power_2 = false; _aggressively_clean_mapdir = false; + _color_type = (PNMFileType *)NULL; + _alpha_type = (PNMFileType *)NULL; } string AttribFile:: @@ -107,6 +113,12 @@ open_and_lock(bool lock) { if (lock) { if (fcntl(_txa_fd, F_SETLK, &fl) < 0) { + if (errno != EACCES) { + perror(_txa_filename.c_str()); + nout << "Unable to lock file; try running with -nolock.\n"; + return false; + } + nout << "Waiting for lock on " << _txa_filename << "\n"; while (fcntl(_txa_fd, F_SETLKW, &fl) < 0) { if (errno != EINTR) { @@ -252,6 +264,10 @@ update_params(EggPalettize *prog) { if (prog->_got_aggressively_clean_mapdir) { _aggressively_clean_mapdir = prog->_aggressively_clean_mapdir; } + if (prog->_got_image_type) { + _color_type = prog->_color_type; + _alpha_type = prog->_alpha_type; + } if (!_rel_dirname.empty()) { _rel_dirname.make_canonical(); @@ -655,6 +671,13 @@ transfer_unplaced_images(bool force_redo_all) { nout << "Deleting " << new_filename << "\n"; new_filename.unlink(); } + if (packing->has_alpha_filename()) { + Filename alpha_filename = packing->get_alpha_filename(); + if (alpha_filename.exists()) { + nout << "Deleting " << alpha_filename << "\n"; + alpha_filename.unlink(); + } + } } } } @@ -780,6 +803,110 @@ collect_statistics(int &num_textures, int &num_placed, int &num_palettes, } +//////////////////////////////////////////////////////////////////// +// Function: AttribFile::make_color_filename +// Access: Public +// Description: Adjusts an image filename to a suitable filename for +// saving the color channel. +//////////////////////////////////////////////////////////////////// +Filename AttribFile:: +make_color_filename(const Filename &filename) const { + Filename color_filename = filename; + if (_color_type != (PNMFileType *)NULL) { + color_filename.set_extension(_color_type->get_suggested_extension()); + } + return color_filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribFile::make_alpha_filename +// Access: Public +// Description: Adjusts an image filename to a suitable filename for +// saving the alpha channel, if one is to be saved. +//////////////////////////////////////////////////////////////////// +Filename AttribFile:: +make_alpha_filename(const Filename &filename) const { + Filename alpha_filename; + if (_alpha_type != (PNMFileType *)NULL) { + alpha_filename = filename; + alpha_filename.set_basename + (filename.get_basename_wo_extension() + "_alpha." + + _alpha_type->get_suggested_extension()); + } + return alpha_filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribFile::write_image_file +// Access: Public +// Description: Writes out the indicated image, either as a single +// file with or without alpha, or as two separate files +// if necessary. +//////////////////////////////////////////////////////////////////// +bool AttribFile:: +write_image_file(PNMImage &image, const Filename &filename, + const Filename &alpha_filename) const { + if (!image.has_alpha() || _alpha_type == (PNMFileType *)NULL) { + if (!alpha_filename.empty() && alpha_filename.exists()) { + alpha_filename.unlink(); + } + return image.write(filename, _color_type); + } + + // Write out a separate color image and an alpha channel image. + PNMImage alpha_image(image.get_x_size(), image.get_y_size(), 1, + image.get_maxval()); + for (int y = 0; y < image.get_y_size(); y++) { + for (int x = 0; x < image.get_x_size(); x++) { + alpha_image.set_gray_val(x, y, image.get_alpha_val(x, y)); + } + } + + image.remove_alpha(); + return + image.write(filename, _color_type) && + alpha_image.write(alpha_filename, _alpha_type); +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribFile::read_image_file +// Access: Public +// Description: Reads in the indicated image, either as a single +// file with or without alpha, or as two separate files +// if necessary. +//////////////////////////////////////////////////////////////////// +bool AttribFile:: +read_image_file(PNMImage &image, const Filename &filename, + const Filename &alpha_filename) const { + image.set_type(_color_type); + if (!image.read(filename)) { + return false; + } + + if (!alpha_filename.empty() && alpha_filename.exists()) { + // Read in a separate color image and an alpha channel image. + PNMImage alpha_image; + alpha_image.set_type(_alpha_type); + if (!alpha_image.read(alpha_filename)) { + return false; + } + if (image.get_x_size() != alpha_image.get_x_size() || + image.get_y_size() != alpha_image.get_y_size()) { + return false; + } + + image.add_alpha(); + for (int y = 0; y < image.get_y_size(); y++) { + for (int x = 0; x < image.get_x_size(); x++) { + image.set_alpha(x, y, alpha_image.get_gray(x, y)); + } + } + } + + return true; +} + + bool AttribFile:: read_txa(istream &infile) { string line; @@ -901,7 +1028,16 @@ write_pi(ostream &out) const { << " pal_ysize " << _pal_ysize << "\n" << " default_margin " << _default_margin << "\n" << " force_power_2 " << _force_power_2 << "\n" - << " aggressively_clean_mapdir " << _aggressively_clean_mapdir << "\n"; + << " aggressively_clean_mapdir " << _aggressively_clean_mapdir << "\n" + << " color_type"; + if (_color_type != (PNMFileType *)NULL) { + out << " " << _color_type->get_suggested_extension(); + } + out << "\n alpha_type"; + if (_alpha_type != (PNMFileType *)NULL) { + out << " " << _alpha_type->get_suggested_extension(); + } + out << "\n"; if (_optimal) { out << "\npacking is optimal\n"; @@ -1012,6 +1148,26 @@ parse_params(const vector_string &words, istream &infile, _force_power_2 = (atoi(value.c_str()) != 0); } else if (param == "aggressively_clean_mapdir") { _aggressively_clean_mapdir = (atoi(value.c_str()) != 0); + } else if (param == "color_type") { + if (value.empty()) { + _color_type = (PNMFileType *)NULL; + } else { + PNMFileTypeRegistry *registry = PNMFileTypeRegistry::get_ptr(); + _color_type = registry->get_type_from_extension(value); + if (_color_type == (PNMFileType *)NULL) { + nout << "Warning: unknown image file type: " << value << "\n"; + } + } + } else if (param == "alpha_type") { + if (value.empty()) { + _alpha_type = (PNMFileType *)NULL; + } else { + PNMFileTypeRegistry *registry = PNMFileTypeRegistry::get_ptr(); + _alpha_type = registry->get_type_from_extension(value); + if (_alpha_type == (PNMFileType *)NULL) { + nout << "Warning: unknown image file type: " << value << "\n"; + } + } } else { nout << "Unexpected keyword: " << param << "\n"; return false; diff --git a/pandatool/src/egg-palettize/attribFile.h b/pandatool/src/egg-palettize/attribFile.h index 45e066824a..b3023d4d01 100644 --- a/pandatool/src/egg-palettize/attribFile.h +++ b/pandatool/src/egg-palettize/attribFile.h @@ -21,6 +21,8 @@ class PaletteGroup; class Palette; class SourceEgg; class EggPalettize; +class PNMImage; +class PNMFileType; //////////////////////////////////////////////////////////////////// // Class : AttribFile @@ -77,6 +79,14 @@ public: int &orig_size, int &resized_size, int &palette_size, int &unplaced_size) const; + Filename make_color_filename(const Filename &filename) const; + Filename make_alpha_filename(const Filename &filename) const; + + bool write_image_file(PNMImage &image, const Filename &filename, + const Filename &alpha_filename) const; + bool read_image_file(PNMImage &image, const Filename &filename, + const Filename &alpha_filename) const; + private: typedef vector UserLines; UserLines _user_lines; @@ -141,6 +151,9 @@ public: bool _force_power_2; bool _aggressively_clean_mapdir; + PNMFileType *_color_type; + PNMFileType *_alpha_type; + int _txa_fd; fstream _txa_fstrm; diff --git a/pandatool/src/egg-palettize/eggPalettize.cxx b/pandatool/src/egg-palettize/eggPalettize.cxx index 1d9f19949f..aa37eb1067 100644 --- a/pandatool/src/egg-palettize/eggPalettize.cxx +++ b/pandatool/src/egg-palettize/eggPalettize.cxx @@ -11,6 +11,8 @@ #include "textureOmitReason.h" #include +#include +#include #include //////////////////////////////////////////////////////////////////// @@ -122,6 +124,15 @@ EggPalettize() : EggMultiFilter(true) { "Aggressively keep the map directory clean by deleting unused " "textures from previous passes.", &EggPalettize::dispatch_none, &_got_aggressively_clean_mapdir); + add_option + ("type", "imagetype[,alphatype]", 0, + "Specify the type of image file to output. All image files, whether " + "palettes or unplaced textures, will be converted to files of this " + "type. If the optional alpha type is specified, then an alpha channel, " + "if present, will be written as a separate file of the indicated " + "type--useful if the primary image type does not support alpha. " + "Use '-type list' to show the available image types.", + &EggPalettize::dispatch_string, &_got_image_type, &_image_type); add_option ("r", "", 0, "Respect any repeat/clamp flags given in the egg files. The " @@ -168,6 +179,8 @@ EggPalettize() : EggMultiFilter(true) { _fuzz_factor = 0.01; _aggressively_clean_mapdir = false; _force_power_2 = false; + _color_type = (PNMFileType *)NULL; + _alpha_type = (PNMFileType *)NULL; } @@ -186,6 +199,65 @@ handle_args(ProgramBase::Args &args) { exit(1); } + if (_got_image_type) { + PNMFileTypeRegistry *registry = PNMFileTypeRegistry::get_ptr(); + + if (_image_type == "list") { + nout << "Known image types are:\n"; + registry->write_types(nout, 2); + nout << "\n"; + exit(1); + } + + string color_name = _image_type; + string alpha_name; + size_t comma = _image_type.find(','); + if (comma != string::npos) { + // If we have a comma in the image_type, it's two types: a color + // type and an alpha type. + color_name = _image_type.substr(0, comma); + alpha_name = _image_type.substr(comma + 1); + } + + bool okflag = true; + + if (!color_name.empty()) { + _color_type = registry->get_type_from_extension(color_name); + if (_color_type == (PNMFileType *)NULL) { + nout << "Image file type '" << color_name << "' is unknown.\n"; + okflag = false; + } + } + + if (!alpha_name.empty()) { + _alpha_type = registry->get_type_from_extension(alpha_name); + if (_alpha_type == (PNMFileType *)NULL) { + nout << "Image file type '" << alpha_name << "' is unknown.\n"; + okflag = false; + } + } + + if (!okflag) { + nout << "\nKnown image types are:\n"; + registry->write_types(nout, 2); + nout << "\n"; + exit(1); + } + + /* + if (_color_type != (PNMFileType *)NULL && + _alpha_type != (PNMFileType *)NULL) { + nout << "Writing color components to " << _color_type->get_name() + << " and alpha components to " << _alpha_type->get_name() + << " files.\n"; + + } else if (_color_type != (PNMFileType *)NULL) { + nout << "Converting images to " << _color_type->get_name() + << " files.\n"; + } + */ + } + Args egg_names; Args txa_names; diff --git a/pandatool/src/egg-palettize/eggPalettize.h b/pandatool/src/egg-palettize/eggPalettize.h index 1660803909..b8d1b4ec56 100644 --- a/pandatool/src/egg-palettize/eggPalettize.h +++ b/pandatool/src/egg-palettize/eggPalettize.h @@ -14,6 +14,7 @@ #include class PTexture; +class PNMFileType; //////////////////////////////////////////////////////////////////// // Class : EggPalettize @@ -54,6 +55,10 @@ public: bool _got_force_power_2; bool _aggressively_clean_mapdir; bool _got_aggressively_clean_mapdir; + string _image_type; + bool _got_image_type; + PNMFileType *_color_type; + PNMFileType *_alpha_type; // The following values relate specifically to egg files. They're // not saved for future sessions. diff --git a/pandatool/src/egg-palettize/imageFile.cxx b/pandatool/src/egg-palettize/imageFile.cxx deleted file mode 100644 index a59953d7d9..0000000000 --- a/pandatool/src/egg-palettize/imageFile.cxx +++ /dev/null @@ -1,14 +0,0 @@ -// Filename: imageFile.cxx -// Created by: drose (07Sep99) -// -//////////////////////////////////////////////////////////////////// - -#include "imageFile.h" - -ImageFile:: -ImageFile() { -} - -ImageFile:: -~ImageFile() { -} diff --git a/pandatool/src/egg-palettize/imageFile.h b/pandatool/src/egg-palettize/imageFile.h deleted file mode 100644 index 8d0080bb0e..0000000000 --- a/pandatool/src/egg-palettize/imageFile.h +++ /dev/null @@ -1,28 +0,0 @@ -// Filename: imageFile.h -// Created by: drose (07Sep99) -// -//////////////////////////////////////////////////////////////////// - -#ifndef IMAGEFILE_H -#define IMAGEFILE_H - -#include - -#include - -//////////////////////////////////////////////////////////////////// -// Class : ImageFile -// Description : This is the base class for both Palette and PTexture. -//////////////////////////////////////////////////////////////////// -class ImageFile { -public: - ImageFile(); - virtual ~ImageFile(); - - virtual Filename get_filename() const=0; - virtual Filename get_basename() const=0; - -}; - -#endif - diff --git a/pandatool/src/egg-palettize/pTexture.cxx b/pandatool/src/egg-palettize/pTexture.cxx index d3680841e8..386ef90800 100644 --- a/pandatool/src/egg-palettize/pTexture.cxx +++ b/pandatool/src/egg-palettize/pTexture.cxx @@ -382,7 +382,7 @@ read_image() { } PNMImage *image = new PNMImage; - if (image->read(_filename)) { + if (_attrib_file->read_image_file(*image, _filename, Filename())) { return image; } @@ -431,5 +431,6 @@ read_image_header(const Filename &filename, int &xsize, int &ysize, xsize = header.get_x_size(); ysize = header.get_y_size(); zsize = header.get_num_channels(); + return true; } diff --git a/pandatool/src/egg-palettize/palette.cxx b/pandatool/src/egg-palettize/palette.cxx index 3f54cdbe21..8a83ee9ebe 100644 --- a/pandatool/src/egg-palettize/palette.cxx +++ b/pandatool/src/egg-palettize/palette.cxx @@ -12,6 +12,7 @@ #include #include +#include bool Palette::TexturePlacement:: intersects(int hleft, int htop, int xsize, int ysize) const { @@ -149,6 +150,7 @@ Palette(const Filename &filename, PaletteGroup *group, _index = -1; _palette_changed = false; _new_palette = false; + _uses_alpha = (_components == 2 || _components == 4); } Palette:: @@ -163,6 +165,7 @@ Palette(PaletteGroup *group, int index, { _palette_changed = false; _new_palette = true; + _uses_alpha = (_components == 2 || _components == 4); } Palette:: @@ -179,6 +182,29 @@ get_filename() const { return _filename; } +//////////////////////////////////////////////////////////////////// +// Function: Palette::has_alpha_filename +// Access: Public +// Description: Returns true if this texture requires a separate +// alpha image file. +//////////////////////////////////////////////////////////////////// +bool Palette:: +has_alpha_filename() const { + return _uses_alpha && (_attrib_file->_alpha_type != (PNMFileType *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: Palette::get_alpha_filename +// Access: Public +// Description: Returns the filename for the alpha channel to which +// this texture will be copied, assuming it is not +// placed on a palette. +//////////////////////////////////////////////////////////////////// +Filename Palette:: +get_alpha_filename() const { + return _attrib_file->make_alpha_filename(_filename); +} + Filename Palette:: get_basename() const { return _basename; @@ -331,15 +357,18 @@ finalize_palette() { char index_str[128]; sprintf(index_str, "%03d", _index); _basename = _group->get_name() + "-palette." + index_str + ".rgb"; + _basename = _attrib_file->make_color_filename(_basename); Filename dirname = _group->get_full_dirname(_attrib_file); _filename = _basename; _filename.set_dirname(dirname.get_fullpath()); } else { + _filename = _attrib_file->make_color_filename(_filename); _basename = _filename.get_basename(); } - _components = check_uses_alpha() ? 4 : 3; + _uses_alpha = check_uses_alpha(); + _components = _uses_alpha ? 4 : 3; if (_texplace.size() == 1) { // If we packed exactly one texture, never mind. @@ -388,7 +417,7 @@ generate_image() { } _filename.make_dir(); - if (!palette.write(_filename)) { + if (!_attrib_file->write_image_file(palette, _filename, get_alpha_filename())) { nout << "Error in writing.\n"; okflag = false; } @@ -402,11 +431,17 @@ refresh_image() { nout << "Palette image " << _filename << " does not exist, rebuilding.\n"; return generate_image(); } + Filename alpha_filename = get_alpha_filename(); + if (has_alpha_filename() && !alpha_filename.exists()) { + nout << "Palette alpha image " << alpha_filename + << " does not exist, rebuilding.\n"; + return generate_image(); + } bool okflag = true; PNMImage palette; - if (!palette.read(_filename)) { + if (!_attrib_file->read_image_file(palette, _filename, alpha_filename)) { nout << "Unable to read old palette image " << _filename << ", rebuilding.\n"; return generate_image(); @@ -440,7 +475,7 @@ refresh_image() { } if (any_changed) { - if (!palette.write(_filename)) { + if (!_attrib_file->write_image_file(palette, _filename, alpha_filename)) { nout << "Error in writing.\n"; okflag = false; } diff --git a/pandatool/src/egg-palettize/palette.h b/pandatool/src/egg-palettize/palette.h index 795f47fb12..8ce827e584 100644 --- a/pandatool/src/egg-palettize/palette.h +++ b/pandatool/src/egg-palettize/palette.h @@ -35,6 +35,8 @@ public: ~Palette(); Filename get_filename() const; + bool has_alpha_filename() const; + Filename get_alpha_filename() const; Filename get_basename() const; PaletteGroup *get_group() const; @@ -88,6 +90,7 @@ private: PaletteGroup *_group; int _index; int _xsize, _ysize, _components; + bool _uses_alpha; bool _palette_changed; bool _new_palette; diff --git a/pandatool/src/egg-palettize/sourceEgg.cxx b/pandatool/src/egg-palettize/sourceEgg.cxx index 7e11d49586..768cb06793 100644 --- a/pandatool/src/egg-palettize/sourceEgg.cxx +++ b/pandatool/src/egg-palettize/sourceEgg.cxx @@ -290,6 +290,11 @@ update_trefs() { // This texture wasn't palettized, so just rename the // reference to the new one. eggtex->set_filename(_attrib_file->write_egg_filename(packing->get_new_filename())); + if (packing->has_alpha_filename()) { + eggtex->set_alpha_file(_attrib_file->write_egg_filename(packing->get_alpha_filename())); + } else { + eggtex->clear_alpha_file(); + } } else { // This texture was palettized, so redirect the tref to point diff --git a/pandatool/src/egg-palettize/texturePacking.cxx b/pandatool/src/egg-palettize/texturePacking.cxx index fa52324b3d..5ddb7d9e16 100644 --- a/pandatool/src/egg-palettize/texturePacking.cxx +++ b/pandatool/src/egg-palettize/texturePacking.cxx @@ -414,6 +414,15 @@ needs_refresh() { } else { // Compare to the resized file. target_filename = get_new_filename(); + + if (has_alpha_filename()) { + // If we have an alpha file, compare to the older of the two + // files. + Filename alpha_filename = get_alpha_filename(); + if (target_filename.compare_timestamps(alpha_filename, true, true) > 0) { + target_filename = alpha_filename; + } + } } if (!any_change) { @@ -474,10 +483,34 @@ Filename TexturePacking:: get_new_filename() const { Filename dirname = _group->get_full_dirname(_attrib_file); Filename new_filename(dirname, _texture->get_name()); + new_filename = _attrib_file->make_color_filename(new_filename); new_filename.standardize(); return new_filename; } +//////////////////////////////////////////////////////////////////// +// Function: TexturePacking::has_alpha_filename +// Access: Public +// Description: Returns true if this texture requires a separate +// alpha image file. +//////////////////////////////////////////////////////////////////// +bool TexturePacking:: +has_alpha_filename() const { + return _uses_alpha && (_attrib_file->_alpha_type != (PNMFileType *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: TexturePacking::get_alpha_filename +// Access: Public +// Description: Returns the filename for the alpha channel to which +// this texture will be copied, assuming it is not +// placed on a palette. +//////////////////////////////////////////////////////////////////// +Filename TexturePacking:: +get_alpha_filename() const { + return _attrib_file->make_alpha_filename(get_new_filename()); +} + //////////////////////////////////////////////////////////////////// // Function: TexturePacking::get_old_filename // Access: Public @@ -556,7 +589,7 @@ transfer() { } new_filename.make_dir(); - if (!image->write(new_filename)) { + if (!_attrib_file->write_image_file(*image, new_filename, get_alpha_filename())) { nout << "Error in writing.\n"; okflag = false; } diff --git a/pandatool/src/egg-palettize/texturePacking.h b/pandatool/src/egg-palettize/texturePacking.h index c96839cec2..88994dfc4f 100644 --- a/pandatool/src/egg-palettize/texturePacking.h +++ b/pandatool/src/egg-palettize/texturePacking.h @@ -61,6 +61,8 @@ public: void write_unplaced(ostream &out) const; Filename get_new_filename() const; + bool has_alpha_filename() const; + Filename get_alpha_filename() const; Filename get_old_filename() const; bool transfer();