/* * The ManaPlus Client * Copyright (C) 2012-2013 The ManaPlus Developers * * This file is part of The ManaPlus Client. * * 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 * 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, see <http://www.gnu.org/licenses/>. */ #include "main.h" #ifdef USE_OPENGL #include "resources/atlasmanager.h" #include "client.h" #include "graphics.h" #include "graphicsmanager.h" #include "logger.h" #include "utils/mathutils.h" #include "utils/physfsrwops.h" #include "resources/dye.h" #include "resources/fboinfo.h" #include "resources/imagehelper.h" #include "resources/imagewriter.h" #include "resources/openglimagehelper.h" #include "resources/resourcemanager.h" #include "debug.h" AtlasManager::AtlasManager() { } AtlasResource *AtlasManager::loadTextureAtlas(const std::string &name, const StringVect &files) { std::vector<TextureAtlas*> atlases; std::vector<Image*> images; AtlasResource *resource = new AtlasResource; loadImages(files, images); int maxSize = OpenGLImageHelper::getTextureSize(); // int maxSize = 1024; // sorting images on atlases. simpleSort(name, atlases, images, maxSize); // int k = 0; FOR_EACH (std::vector<TextureAtlas*>::iterator, it, atlases) { TextureAtlas *atlas = *it; if (!atlas) continue; // create atlas base on sorted images SDL_Surface *surface = createSDLAtlas(atlas); if (!surface) continue; // debug save // ImageWriter::writePNG(surface, Client::getTempDirectory() // + "/atlas" + name + toString(k) + ".png"); // k ++; // convert SDL images to OpenGL convertAtlas(atlas); // free SDL atlas surface SDL_FreeSurface(surface); resource->atlases.push_back(atlas); } return resource; } void AtlasManager::loadImages(const StringVect &files, std::vector<Image*> &images) { ResourceManager *const resman = ResourceManager::getInstance(); FOR_EACH (StringVectCIter, it, files) { const std::string str = *it; // check is image with same name already in cache // and if yes, move it to deleted set Resource *const res = resman->getTempResource(str); if (res) { //logger->log("Resource %s already in cache", str.c_str()); // increase counter because in moveToDeleted it will be decreased. res->incRef(); resman->moveToDeleted(res); } std::string path = str; const size_t p = path.find('|'); Dye *d = nullptr; if (p != std::string::npos) { d = new Dye(path.substr(p + 1)); path = path.substr(0, p); } SDL_RWops *rw = PHYSFSRWOPS_openRead(path.c_str()); if (rw) { Image *image = d ? sdlImageHelper->load(rw, *d) : sdlImageHelper->load(rw); if (image) { image->mIdPath = str; images.push_back(image); } } delete d; } } void AtlasManager::simpleSort(const std::string &name, std::vector<TextureAtlas*> &atlases, std::vector<Image*> &images, int size) { int x = 0; int y = 0; int tempHeight = 0; TextureAtlas *atlas = new TextureAtlas(); std::vector<Image*>::iterator it = images.begin(); const std::vector<Image*>::const_iterator it_end = images.end(); for (it = images.begin(); it != it_end; ++ it) { Image *img = *it; if (img) { atlas->name = std::string("atlas_").append(name).append( "_").append(img->getIdPath()); break; } } for (it = images.begin(); it != it_end; ++ it) { Image *img = *it; if (img) { AtlasItem *item = new AtlasItem(img); item->name = img->getIdPath(); // start next line if (x + img->mBounds.w > size) { x = 0; y += tempHeight; tempHeight = 0; } // cant put image with this height if (y + img->mBounds.h > size) { x = 0; y = 0; atlases.push_back(atlas); atlas = new TextureAtlas(); atlas->name = std::string("atlas_").append(name).append( "_").append(img->getIdPath()); } if (img->mBounds.h > tempHeight) tempHeight = img->mBounds.h; // logger->log("image draw position: %d,%d (%d,%d)", // x, y, img->mBounds.w, img->mBounds.h); item->x = x; item->y = y; atlas->items.push_back(item); // continue put textures in line x += img->mBounds.w; if (x > atlas->width) atlas->width = x; if (y + img->mBounds.h > atlas->height) atlas->height = y + img->mBounds.h; } } if (!atlas->items.empty()) atlases.push_back(atlas); else delete atlas; } SDL_Surface *AtlasManager::createSDLAtlas(TextureAtlas *atlas) { #if SDL_BYTEORDER == SDL_BIG_ENDIAN const int rmask = 0xff000000; const int gmask = 0x00ff0000; const int bmask = 0x0000ff00; const int amask = 0x000000ff; #else const int rmask = 0x000000ff; const int gmask = 0x0000ff00; const int bmask = 0x00ff0000; const int amask = 0xff000000; #endif // do not create atlas based on only one image if (atlas->items.size() == 1) return nullptr; // using only power of two sizes. atlas->width = powerOfTwo(atlas->width); atlas->height = powerOfTwo(atlas->height); // temp SDL surface for atlas SDL_Surface *surface = SDL_CreateRGBSurface(SDL_SWSURFACE, atlas->width, atlas->height, 32, rmask, gmask, bmask, amask); if (!surface) return nullptr; Graphics *graphics = new Graphics(); graphics->setTarget(surface); graphics->_beginDraw(); // drawing SDL images to surface FOR_EACH (std::vector<AtlasItem*>::iterator, it, atlas->items) { AtlasItem *item = *it; Image *image = item->image; if (image) { SDL_SetAlpha(image->mSDLSurface, 0, SDL_ALPHA_OPAQUE); graphics->drawImage(image, item->x, item->y); } } delete graphics; atlas->surface = surface; return surface; } void AtlasManager::convertAtlas(TextureAtlas *atlas) { // convert surface to OpemGL image atlas->atlasImage = imageHelper->load(atlas->surface); Image *const image = atlas->atlasImage; if (image) { image->mIdPath = atlas->name; image->incRef(); } FOR_EACH (std::vector<AtlasItem*>::iterator, it, atlas->items) { AtlasItem *const item = *it; // delete SDL Image delete item->image; // store OpenGL image item->image = image->getSubImage(item->x, item->y, item->width, item->height); Image *const image2 = item->image; if (image2) { image2->mIdPath = item->name; image2->incRef(); } } } void AtlasManager::injectToResources(AtlasResource *resource) { ResourceManager *const resman = ResourceManager::getInstance(); FOR_EACH (std::vector<TextureAtlas*>::iterator, it, resource->atlases) { // add each atlas image to resources TextureAtlas *const atlas = *it; if (atlas) { Image *const image = atlas->atlasImage; if (image) resman->addResource(atlas->name, image); FOR_EACH (std::vector<AtlasItem*>::iterator, it2, atlas->items) { AtlasItem *const item = *it2; if (!item) continue; // add each atlas sub image to resources resman->addResource(item->name, item->image); } } } } void AtlasManager::moveToDeleted(AtlasResource *resource) { ResourceManager *const resman = ResourceManager::getInstance(); FOR_EACH (std::vector<TextureAtlas*>::iterator, it, resource->atlases) { // move each atlas image to deleted TextureAtlas *const atlas = *it; if (atlas) { Image *const image = atlas->atlasImage; if (image) { // move each atlas image to deleted resman->moveToDeleted(image); } FOR_EACH (std::vector<AtlasItem*>::iterator, it2, atlas->items) { AtlasItem *const item = *it2; if (item) { Image *const image2 = item->image; if (image2) { // move each atlas sub image to deleted resman->moveToDeleted(image2); } } } } } } AtlasResource::~AtlasResource() { FOR_EACH (std::vector<TextureAtlas*>::iterator, it, atlases) { TextureAtlas *const atlas = *it; if (atlas) { FOR_EACH (std::vector<AtlasItem*>::iterator, it2, atlas->items) { AtlasItem *const item = *it2; if (item) { Image *const image2 = item->image; if (image2) image2->decRef(); delete item; } } Image *const image = atlas->atlasImage; if (image) image->decRef(); delete atlas; } } ResourceManager *const resman = ResourceManager::getInstance(); resman->clearDeleted(false); } void AtlasResource::incRef() { if (!getRefCount()) AtlasManager::injectToResources(this); Resource::incRef(); } void AtlasResource::decRef() { Resource::decRef(); if (!getRefCount()) AtlasManager::moveToDeleted(this); } #endif