void cutter::load_masks(const config& conf) { const config::child_list& masks = conf.get_children("mask"); for(config::child_list::const_iterator itor = masks.begin(); itor != masks.end(); ++itor) { const std::string name = (**itor)["name"]; const std::string image = get_mask_dir() + "/" + (**itor)["image"]; if(verbose_) { std::cerr << "Adding mask " << name << "\n"; } if(image.empty()) throw exploder_failure("Missing image for mask " + name); const exploder_point shift((**itor)["shift"]); const exploder_rect cut((**itor)["cut"]); if(masks_.find(name) != masks_.end() && masks_[name].filename != image) { throw exploder_failure("Mask " + name + " correspond to two different files: " + name + " and " + masks_.find(name)->second.filename); } if(masks_.find(name) == masks_.end()) { mask& cur_mask = masks_[name]; cur_mask.name = name; cur_mask.shift = shift; cur_mask.cut = cut; cur_mask.filename = image; surface tmp(IMG_Load(image.c_str())); if(tmp == NULL) throw exploder_failure("Unable to load mask image " + image); cur_mask.image = surface(make_neutral_surface(tmp)); } if(masks_[name].image == NULL) throw exploder_failure("Unable to load mask image " + image); } }
void cutter::load_masks(const config& conf) { BOOST_FOREACH(const config &m, conf.child_range("mask")) { const std::string name = m["name"]; const std::string image = get_mask_dir() + "/" + std::string(m["image"]); if(verbose_) { std::cerr << "Adding mask " << name << "\n"; } if(image.empty()) throw exploder_failure("Missing image for mask " + name); const exploder_point shift(m["shift"]); const exploder_rect cut(m["cut"]); if(masks_.find(name) != masks_.end() && masks_[name].filename != image) { throw exploder_failure("Mask " + name + " correspond to two different files: " + name + " and " + masks_.find(name)->second.filename); } if(masks_.find(name) == masks_.end()) { mask& cur_mask = masks_[name]; cur_mask.name = name; cur_mask.shift = shift; cur_mask.cut = cut; cur_mask.filename = image; surface tmp(IMG_Load(image.c_str())); if(tmp == NULL) throw exploder_failure("Unable to load mask image " + image); cur_mask.image = surface(make_neutral_surface(tmp)); } if(masks_[name].image == NULL) throw exploder_failure("Unable to load mask image " + image); } }
void cutter::add_sub_image(const surface &surf, surface_map &map, const config* config) { const std::string name = (*config)["name"]; if(name.empty()) throw exploder_failure("Un-named sub-image"); if(masks_.find(name) == masks_.end()) throw exploder_failure("Unable to find mask corresponding to " + name); const cutter::mask& mask = masks_[name]; std::vector<std::string> pos = utils::split((*config)["pos"]); if(pos.size() != 2) throw exploder_failure("Invalid position " + (*config)["pos"].str()); int x = atoi(pos[0].c_str()); int y = atoi(pos[1].c_str()); const SDL_Rect cut = create_rect(x - mask.shift.x , y - mask.shift.y , mask.image->w , mask.image->h); typedef std::pair<std::string, positioned_surface> sme; positioned_surface ps; ps.image = surface(::cut_surface(surf, cut)); if(ps.image == NULL) throw exploder_failure("Unable to cut surface!"); ps.name = name; ps.mask = mask; ps.pos.x = x - mask.shift.x; ps.pos.y = y - mask.shift.y; map.insert(sme(name, ps)); if(verbose_) { std::cerr << "Extracting sub-image " << name << ", position (" << x << ", " << y << ")\n"; } }
const config cutter::load_config(const std::string &filename) { const std::string conf_string = find_configuration(filename); config res; try { scoped_istream stream = preprocess_file(conf_string); read(res, *stream); } catch(config::error& err) { throw exploder_failure("Unable to load the configuration for the file " + filename + ": "+ err.message); } return res; }
//saves the given SDL structure into a given filename. void save_image(surface surf, const std::string &filename) { //opens the actual file const util::scoped_FILE file(fopen(filename.c_str(),"wb")); //initializes PNG write structures //TODO: review whether providing NULL error handlers is something //sensible png_struct* png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, reinterpret_cast<png_voidp>(png_voidp_NULL), png_error_ptr_NULL, png_error_ptr_NULL); if(!png_ptr) throw exploder_failure("Unable to initialize the png write structure"); png_info* info_ptr = png_create_info_struct(png_ptr); if(!info_ptr) { png_destroy_write_struct(&png_ptr, static_cast<png_infopp>(NULL)); throw exploder_failure("Unable to initialize the png info structure"); } //instructs the PNG library to use the open file png_init_io(png_ptr, file); //sets compression level to the maximum png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); //configures the header png_set_IHDR(png_ptr, info_ptr, surf->w, surf->h, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); //puts the actual image data in the row_pointers array png_byte **row_pointers = new png_byte *[surf->h]; surface_lock lock(surf); //converts the data to the RGBA format. We cannot pass SDL data //directly to the png lib, even if we know its pixel format, because of //endianness problems. util::scoped_array<rgba> rgba_data(new rgba[surf->w * surf->h]); Uint32 *surf_data = lock.pixels(); int pos = 0; for(int y = 0; y < surf->h; ++y) { row_pointers[y] = reinterpret_cast<png_byte*>(rgba_data + pos); for(int x = 0; x < surf->w; ++x) { Uint8 red, green, blue, alpha; SDL_GetRGBA(*surf_data, surf->format, &red, &green, &blue, &alpha); rgba_data[pos].r = red; rgba_data[pos].g = green; rgba_data[pos].b = blue; rgba_data[pos].a = alpha; pos++; surf_data++; } } png_set_rows(png_ptr, info_ptr, row_pointers); //writes the actual image data png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); //cleans everything png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); delete [] row_pointers; }
surface composer::compose(const std::string &src, const std::string &dest) { cutter cut; cut.set_verbose(verbose_); const config src_conf = cut.load_config(src); const config dest_conf = cut.load_config(dest); if(verbose_) { std::cerr << "Loading masks...\n"; } cut.load_masks(src_conf); cut.load_masks(dest_conf); if(verbose_) { std::cerr << "Loading images...\n"; } const surface src_surface(make_neutral_surface(IMG_Load(src.c_str()))); if(src_surface == NULL) throw exploder_failure("Unable to load the source image " + src); const surface dest_surface(make_neutral_surface(IMG_Load(dest.c_str()))); if(dest_surface == NULL) throw exploder_failure("Unable to load the destination image " + dest); if(verbose_) { std::cerr << "Cutting images...\n"; } const cutter::surface_map src_surfaces = cut.cut_surface(src_surface, src_conf); const cutter::surface_map dest_surfaces = cut.cut_surface(dest_surface, dest_conf); for(cutter::surface_map::const_iterator itor = dest_surfaces.begin(); itor != dest_surfaces.end(); ++itor) { const std::string& name = itor->second.name; if(src_surfaces.find(name) == src_surfaces.end()) continue; const cutter::positioned_surface& src_ps = src_surfaces.find(name)->second; const cutter::positioned_surface& dest_ps = itor->second; if(!image_empty(dest_ps.image)) { if(interactive_) { //TODO: make "interactive" mode work } else { std::cerr << "Warning: element " << name << " not empty on destination image\n"; } } if(verbose_) { std::cerr << "Inserting image " << name << " on position (" << dest_ps.pos.x << ", " << dest_ps.pos.y << ")\n"; } masked_overwrite_surface(dest_surface, src_ps.image, src_ps.mask.image, dest_ps.pos.x, dest_ps.pos.y); } return dest_surface; }
int main(int argc, char* argv[]) { std::string src; std::string dest_dir; cutter cut; // Parse arguments that shouldn't require a display device int arg; for(arg = 1; arg != argc; ++arg) { const std::string val(argv[arg]); if(val.empty()) { continue; } if(val == "--help" || val == "-h") { print_usage(argv[0]); return 0; } else if(val == "--verbose" || val == "-v") { cut.set_verbose(true); } else if(val == "--directory" || val == "-d" ) { game_config::path = argv[++arg]; } else { if(src.empty()) { src = val; } else if(dest_dir.empty()) { dest_dir = val; } else { print_usage(argv[0]); return 1; } } } if(src.empty() || dest_dir.empty()) { print_usage(argv[0]); return 1; } try { const config conf = cut.load_config(src); cut.load_masks(conf); const surface src_surface(make_neutral_surface(IMG_Load(src.c_str()))); if(src_surface == NULL) throw exploder_failure("Unable to load the source image " + src); const cutter::surface_map surfaces = cut.cut_surface(src_surface, conf); for(cutter::surface_map::const_iterator itor = surfaces.begin(); itor != surfaces.end(); ++itor) { const cutter::mask &mask = itor->second.mask; surface surf = create_compatible_surface( itor->second.image , mask.cut.w , mask.cut.h); masked_overwrite_surface(surf, itor->second.image, mask.image, mask.cut.x - mask.shift.x, mask.cut.y - mask.shift.y); save_image(surf, dest_dir + "/" + mask.name + ".png"); } } catch(exploder_failure& err) { std::cerr << "Failed: " << err.message << "\n"; return 1; } return 0; }