LCOV - code coverage report
Current view: top level - src/client - tile.cpp (source / functions) Hit Total Coverage
Test: report Lines: 537 831 64.6 %
Date: 2015-07-11 18:23:49 Functions: 37 43 86.0 %

          Line data    Source code
       1             : /*
       2             : Minetest
       3             : Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
       4             : 
       5             : This program is free software; you can redistribute it and/or modify
       6             : it under the terms of the GNU Lesser General Public License as published by
       7             : the Free Software Foundation; either version 2.1 of the License, or
       8             : (at your option) any later version.
       9             : 
      10             : This program is distributed in the hope that it will be useful,
      11             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             : GNU Lesser General Public License for more details.
      14             : 
      15             : You should have received a copy of the GNU Lesser General Public License along
      16             : with this program; if not, write to the Free Software Foundation, Inc.,
      17             : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             : */
      19             : 
      20             : #include "tile.h"
      21             : 
      22             : #include <ICameraSceneNode.h>
      23             : #include "util/string.h"
      24             : #include "util/container.h"
      25             : #include "util/thread.h"
      26             : #include "util/numeric.h"
      27             : #include "irrlichttypes_extrabloated.h"
      28             : #include "debug.h"
      29             : #include "filesys.h"
      30             : #include "settings.h"
      31             : #include "mesh.h"
      32             : #include "log.h"
      33             : #include "gamedef.h"
      34             : #include "strfnd.h"
      35             : #include "util/string.h" // for parseColorString()
      36             : #include "imagefilters.h"
      37             : #include "guiscalingfilter.h"
      38             : 
      39             : #ifdef __ANDROID__
      40             : #include <GLES/gl.h>
      41             : #endif
      42             : 
      43             : /*
      44             :         A cache from texture name to texture path
      45             : */
      46           1 : MutexedMap<std::string, std::string> g_texturename_to_path_cache;
      47             : 
      48             : /*
      49             :         Replaces the filename extension.
      50             :         eg:
      51             :                 std::string image = "a/image.png"
      52             :                 replace_ext(image, "jpg")
      53             :                 -> image = "a/image.jpg"
      54             :         Returns true on success.
      55             : */
      56       64839 : static bool replace_ext(std::string &path, const char *ext)
      57             : {
      58       64839 :         if (ext == NULL)
      59        6460 :                 return false;
      60             :         // Find place of last dot, fail if \ or / found.
      61       58379 :         s32 last_dot_i = -1;
      62      233516 :         for (s32 i=path.size()-1; i>=0; i--)
      63             :         {
      64      233516 :                 if (path[i] == '.')
      65             :                 {
      66       58379 :                         last_dot_i = i;
      67       58379 :                         break;
      68             :                 }
      69             : 
      70      175137 :                 if (path[i] == '\\' || path[i] == '/')
      71           0 :                         break;
      72             :         }
      73             :         // If not found, return an empty string
      74       58379 :         if (last_dot_i == -1)
      75           0 :                 return false;
      76             :         // Else make the new path
      77       58379 :         path = path.substr(0, last_dot_i+1) + ext;
      78       58379 :         return true;
      79             : }
      80             : 
      81             : /*
      82             :         Find out the full path of an image by trying different filename
      83             :         extensions.
      84             : 
      85             :         If failed, return "".
      86             : */
      87        6699 : std::string getImagePath(std::string path)
      88             : {
      89             :         // A NULL-ended list of possible image extensions
      90             :         const char *extensions[] = {
      91             :                 "png", "jpg", "bmp", "tga",
      92             :                 "pcx", "ppm", "psd", "wal", "rgb",
      93             :                 NULL
      94        6699 :         };
      95             :         // If there is no extension, add one
      96        6699 :         if (removeStringEnd(path, extensions) == "")
      97           2 :                 path = path + ".png";
      98             :         // Check paths until something is found to exist
      99        6699 :         const char **ext = extensions;
     100       58140 :         do{
     101       64839 :                 bool r = replace_ext(path, *ext);
     102       64839 :                 if (r == false)
     103        6460 :                         return "";
     104       58379 :                 if (fs::PathExists(path))
     105         239 :                         return path;
     106             :         }
     107             :         while((++ext) != NULL);
     108             : 
     109           0 :         return "";
     110             : }
     111             : 
     112             : /*
     113             :         Gets the path to a texture by first checking if the texture exists
     114             :         in texture_path and if not, using the data path.
     115             : 
     116             :         Checks all supported extensions by replacing the original extension.
     117             : 
     118             :         If not found, returns "".
     119             : 
     120             :         Utilizes a thread-safe cache.
     121             : */
     122        3463 : std::string getTexturePath(const std::string &filename)
     123             : {
     124        3463 :         std::string fullpath = "";
     125             :         /*
     126             :                 Check from cache
     127             :         */
     128        3463 :         bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
     129        3463 :         if (incache)
     130           1 :                 return fullpath;
     131             : 
     132             :         /*
     133             :                 Check from texture_path
     134             :         */
     135        6924 :         std::string texture_path = g_settings->get("texture_path");
     136        3462 :         if (texture_path != "")
     137             :         {
     138        6924 :                 std::string testpath = texture_path + DIR_DELIM + filename;
     139             :                 // Check all filename extensions. Returns "" if not found.
     140        3462 :                 fullpath = getImagePath(testpath);
     141             :         }
     142             : 
     143             :         /*
     144             :                 Check from default data directory
     145             :         */
     146        3462 :         if (fullpath == "")
     147             :         {
     148        9702 :                 std::string base_path = porting::path_share + DIR_DELIM + "textures"
     149        9702 :                                 + DIR_DELIM + "base" + DIR_DELIM + "pack";
     150        6468 :                 std::string testpath = base_path + DIR_DELIM + filename;
     151             :                 // Check all filename extensions. Returns "" if not found.
     152        3234 :                 fullpath = getImagePath(testpath);
     153             :         }
     154             : 
     155             :         // Add to cache (also an empty result is cached)
     156        3462 :         g_texturename_to_path_cache.set(filename, fullpath);
     157             : 
     158             :         // Finally return it
     159        3462 :         return fullpath;
     160             : }
     161             : 
     162           1 : void clearTextureNameCache()
     163             : {
     164           1 :         g_texturename_to_path_cache.clear();
     165           1 : }
     166             : 
     167             : /*
     168             :         Stores internal information about a texture.
     169             : */
     170             : 
     171       16221 : struct TextureInfo
     172             : {
     173             :         std::string name;
     174             :         video::ITexture *texture;
     175             : 
     176        2677 :         TextureInfo(
     177             :                         const std::string &name_,
     178             :                         video::ITexture *texture_=NULL
     179             :                 ):
     180             :                 name(name_),
     181        2677 :                 texture(texture_)
     182             :         {
     183        2677 :         }
     184             : };
     185             : 
     186             : /*
     187             :         SourceImageCache: A cache used for storing source images.
     188             : */
     189             : 
     190           1 : class SourceImageCache
     191             : {
     192             : public:
     193           2 :         ~SourceImageCache() {
     194        6879 :                 for (std::map<std::string, video::IImage*>::iterator iter = m_images.begin();
     195        4586 :                                 iter != m_images.end(); iter++) {
     196        2292 :                         iter->second->drop();
     197             :                 }
     198           1 :                 m_images.clear();
     199           1 :         }
     200        2283 :         void insert(const std::string &name, video::IImage *img,
     201             :                         bool prefer_local, video::IVideoDriver *driver)
     202             :         {
     203             :                 assert(img); // Pre-condition
     204             :                 // Remove old image
     205        2283 :                 std::map<std::string, video::IImage*>::iterator n;
     206        2283 :                 n = m_images.find(name);
     207        2283 :                 if (n != m_images.end()){
     208           0 :                         if (n->second)
     209           0 :                                 n->second->drop();
     210             :                 }
     211             : 
     212        2283 :                 video::IImage* toadd = img;
     213        2283 :                 bool need_to_grab = true;
     214             : 
     215             :                 // Try to use local texture instead if asked to
     216        2283 :                 if (prefer_local){
     217        4566 :                         std::string path = getTexturePath(name);
     218        2283 :                         if (path != ""){
     219         227 :                                 video::IImage *img2 = driver->createImageFromFile(path.c_str());
     220         227 :                                 if (img2){
     221         227 :                                         toadd = img2;
     222         227 :                                         need_to_grab = false;
     223             :                                 }
     224             :                         }
     225             :                 }
     226             : 
     227        2283 :                 if (need_to_grab)
     228        2056 :                         toadd->grab();
     229        2283 :                 m_images[name] = toadd;
     230        2283 :         }
     231             :         video::IImage* get(const std::string &name)
     232             :         {
     233             :                 std::map<std::string, video::IImage*>::iterator n;
     234             :                 n = m_images.find(name);
     235             :                 if (n != m_images.end())
     236             :                         return n->second;
     237             :                 return NULL;
     238             :         }
     239             :         // Primarily fetches from cache, secondarily tries to read from filesystem
     240        3755 :         video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device)
     241             :         {
     242        3755 :                 std::map<std::string, video::IImage*>::iterator n;
     243        3755 :                 n = m_images.find(name);
     244        3755 :                 if (n != m_images.end()){
     245        3744 :                         n->second->grab(); // Grab for caller
     246        3744 :                         return n->second;
     247             :                 }
     248          11 :                 video::IVideoDriver* driver = device->getVideoDriver();
     249          22 :                 std::string path = getTexturePath(name);
     250          11 :                 if (path == ""){
     251           2 :                         infostream<<"SourceImageCache::getOrLoad(): No path found for \""
     252           2 :                                         <<name<<"\""<<std::endl;
     253           2 :                         return NULL;
     254             :                 }
     255           9 :                 infostream<<"SourceImageCache::getOrLoad(): Loading path \""<<path
     256           9 :                                 <<"\""<<std::endl;
     257           9 :                 video::IImage *img = driver->createImageFromFile(path.c_str());
     258             : 
     259           9 :                 if (img){
     260           9 :                         m_images[name] = img;
     261           9 :                         img->grab(); // Grab for caller
     262             :                 }
     263           9 :                 return img;
     264             :         }
     265             : private:
     266             :         std::map<std::string, video::IImage*> m_images;
     267             : };
     268             : 
     269             : /*
     270             :         TextureSource
     271             : */
     272             : 
     273             : class TextureSource : public IWritableTextureSource
     274             : {
     275             : public:
     276             :         TextureSource(IrrlichtDevice *device);
     277             :         virtual ~TextureSource();
     278             : 
     279             :         /*
     280             :                 Example case:
     281             :                 Now, assume a texture with the id 1 exists, and has the name
     282             :                 "stone.png^mineral1".
     283             :                 Then a random thread calls getTextureId for a texture called
     284             :                 "stone.png^mineral1^crack0".
     285             :                 ...Now, WTF should happen? Well:
     286             :                 - getTextureId strips off stuff recursively from the end until
     287             :                   the remaining part is found, or nothing is left when
     288             :                   something is stripped out
     289             : 
     290             :                 But it is slow to search for textures by names and modify them
     291             :                 like that?
     292             :                 - ContentFeatures is made to contain ids for the basic plain
     293             :                   textures
     294             :                 - Crack textures can be slow by themselves, but the framework
     295             :                   must be fast.
     296             : 
     297             :                 Example case #2:
     298             :                 - Assume a texture with the id 1 exists, and has the name
     299             :                   "stone.png^mineral_coal.png".
     300             :                 - Now getNodeTile() stumbles upon a node which uses
     301             :                   texture id 1, and determines that MATERIAL_FLAG_CRACK
     302             :                   must be applied to the tile
     303             :                 - MapBlockMesh::animate() finds the MATERIAL_FLAG_CRACK and
     304             :                   has received the current crack level 0 from the client. It
     305             :                   finds out the name of the texture with getTextureName(1),
     306             :                   appends "^crack0" to it and gets a new texture id with
     307             :                   getTextureId("stone.png^mineral_coal.png^crack0").
     308             : 
     309             :         */
     310             : 
     311             :         /*
     312             :                 Gets a texture id from cache or
     313             :                 - if main thread, generates the texture, adds to cache and returns id.
     314             :                 - if other thread, adds to request queue and waits for main thread.
     315             : 
     316             :                 The id 0 points to a NULL texture. It is returned in case of error.
     317             :         */
     318             :         u32 getTextureId(const std::string &name);
     319             : 
     320             :         // Finds out the name of a cached texture.
     321             :         std::string getTextureName(u32 id);
     322             : 
     323             :         /*
     324             :                 If texture specified by the name pointed by the id doesn't
     325             :                 exist, create it, then return the cached texture.
     326             : 
     327             :                 Can be called from any thread. If called from some other thread
     328             :                 and not found in cache, the call is queued to the main thread
     329             :                 for processing.
     330             :         */
     331             :         video::ITexture* getTexture(u32 id);
     332             : 
     333             :         video::ITexture* getTexture(const std::string &name, u32 *id);
     334             : 
     335             :         /*
     336             :                 Get a texture specifically intended for mesh
     337             :                 application, i.e. not HUD, compositing, or other 2D
     338             :                 use.  This texture may be a different size and may
     339             :                 have had additional filters applied.
     340             :         */
     341             :         video::ITexture* getTextureForMesh(const std::string &name, u32 *id);
     342             : 
     343             :         // Returns a pointer to the irrlicht device
     344           0 :         virtual IrrlichtDevice* getDevice()
     345             :         {
     346           0 :                 return m_device;
     347             :         }
     348             : 
     349      124688 :         bool isKnownSourceImage(const std::string &name)
     350             :         {
     351      124688 :                 bool is_known = false;
     352      124688 :                 bool cache_found = m_source_image_existence.get(name, &is_known);
     353      124688 :                 if (cache_found)
     354      123519 :                         return is_known;
     355             :                 // Not found in cache; find out if a local file exists
     356        1169 :                 is_known = (getTexturePath(name) != "");
     357        1169 :                 m_source_image_existence.set(name, is_known);
     358        1169 :                 return is_known;
     359             :         }
     360             : 
     361             :         // Processes queued texture requests from other threads.
     362             :         // Shall be called from the main thread.
     363             :         void processQueue();
     364             : 
     365             :         // Insert an image into the cache without touching the filesystem.
     366             :         // Shall be called from the main thread.
     367             :         void insertSourceImage(const std::string &name, video::IImage *img);
     368             : 
     369             :         // Rebuild images and textures from the current set of source images
     370             :         // Shall be called from the main thread.
     371             :         void rebuildImagesAndTextures();
     372             : 
     373             :         // Render a mesh to a texture.
     374             :         // Returns NULL if render-to-texture failed.
     375             :         // Shall be called from the main thread.
     376             :         video::ITexture* generateTextureFromMesh(
     377             :                         const TextureFromMeshParams &params);
     378             : 
     379             :         // Generates an image from a full string like
     380             :         // "stone.png^mineral_coal.png^[crack:1:0".
     381             :         // Shall be called from the main thread.
     382             :         video::IImage* generateImage(const std::string &name);
     383             : 
     384             :         video::ITexture* getNormalTexture(const std::string &name);
     385             :         video::SColor getTextureAverageColor(const std::string &name);
     386             : 
     387             : private:
     388             : 
     389             :         // The id of the thread that is allowed to use irrlicht directly
     390             :         threadid_t m_main_thread;
     391             :         // The irrlicht device
     392             :         IrrlichtDevice *m_device;
     393             : 
     394             :         // Cache of source images
     395             :         // This should be only accessed from the main thread
     396             :         SourceImageCache m_sourcecache;
     397             : 
     398             :         // Generate a texture
     399             :         u32 generateTexture(const std::string &name);
     400             : 
     401             :         // Generate image based on a string like "stone.png" or "[crack:1:0".
     402             :         // if baseimg is NULL, it is created. Otherwise stuff is made on it.
     403             :         bool generateImagePart(std::string part_of_name, video::IImage *& baseimg);
     404             : 
     405             :         // Thread-safe cache of what source images are known (true = known)
     406             :         MutexedMap<std::string, bool> m_source_image_existence;
     407             : 
     408             :         // A texture id is index in this array.
     409             :         // The first position contains a NULL texture.
     410             :         std::vector<TextureInfo> m_textureinfo_cache;
     411             :         // Maps a texture name to an index in the former.
     412             :         std::map<std::string, u32> m_name_to_id;
     413             :         // The two former containers are behind this mutex
     414             :         JMutex m_textureinfo_cache_mutex;
     415             : 
     416             :         // Queued texture fetches (to be processed by the main thread)
     417             :         RequestQueue<std::string, u32, u8, u8> m_get_texture_queue;
     418             : 
     419             :         // Textures that have been overwritten with other ones
     420             :         // but can't be deleted because the ITexture* might still be used
     421             :         std::vector<video::ITexture*> m_texture_trash;
     422             : 
     423             :         // Cached settings needed for making textures from meshes
     424             :         bool m_setting_trilinear_filter;
     425             :         bool m_setting_bilinear_filter;
     426             :         bool m_setting_anisotropic_filter;
     427             : };
     428             : 
     429           1 : IWritableTextureSource* createTextureSource(IrrlichtDevice *device)
     430             : {
     431           1 :         return new TextureSource(device);
     432             : }
     433             : 
     434           1 : TextureSource::TextureSource(IrrlichtDevice *device):
     435           1 :                 m_device(device)
     436             : {
     437             :         assert(m_device); // Pre-condition
     438             : 
     439           1 :         m_main_thread = get_current_thread_id();
     440             : 
     441             :         // Add a NULL TextureInfo as the first index, named ""
     442           1 :         m_textureinfo_cache.push_back(TextureInfo(""));
     443           1 :         m_name_to_id[""] = 0;
     444             : 
     445             :         // Cache some settings
     446             :         // Note: Since this is only done once, the game must be restarted
     447             :         // for these settings to take effect
     448           1 :         m_setting_trilinear_filter = g_settings->getBool("trilinear_filter");
     449           1 :         m_setting_bilinear_filter = g_settings->getBool("bilinear_filter");
     450           1 :         m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter");
     451           1 : }
     452             : 
     453           3 : TextureSource::~TextureSource()
     454             : {
     455           1 :         video::IVideoDriver* driver = m_device->getVideoDriver();
     456             : 
     457           1 :         unsigned int textures_before = driver->getTextureCount();
     458             : 
     459        8033 :         for (std::vector<TextureInfo>::iterator iter =
     460           1 :                         m_textureinfo_cache.begin();
     461        5356 :                         iter != m_textureinfo_cache.end(); iter++)
     462             :         {
     463             :                 //cleanup texture
     464        2677 :                 if (iter->texture)
     465        2676 :                         driver->removeTexture(iter->texture);
     466             :         }
     467           1 :         m_textureinfo_cache.clear();
     468             : 
     469          20 :         for (std::vector<video::ITexture*>::iterator iter =
     470           8 :                         m_texture_trash.begin(); iter != m_texture_trash.end();
     471             :                         iter++) {
     472           6 :                 video::ITexture *t = *iter;
     473             : 
     474             :                 //cleanup trashed texture
     475           6 :                 driver->removeTexture(t);
     476             :         }
     477             : 
     478           1 :         infostream << "~TextureSource() "<< textures_before << "/"
     479           2 :                         << driver->getTextureCount() << std::endl;
     480           2 : }
     481             : 
     482      116172 : u32 TextureSource::getTextureId(const std::string &name)
     483             : {
     484             :         //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
     485             : 
     486             :         {
     487             :                 /*
     488             :                         See if texture already exists
     489             :                 */
     490      118848 :                 JMutexAutoLock lock(m_textureinfo_cache_mutex);
     491      116173 :                 std::map<std::string, u32>::iterator n;
     492      116173 :                 n = m_name_to_id.find(name);
     493      116173 :                 if (n != m_name_to_id.end())
     494             :                 {
     495      113497 :                         return n->second;
     496             :                 }
     497             :         }
     498             : 
     499             :         /*
     500             :                 Get texture
     501             :         */
     502        2676 :         if (get_current_thread_id() == m_main_thread)
     503             :         {
     504        2674 :                 return generateTexture(name);
     505             :         }
     506             :         else
     507             :         {
     508           2 :                 infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
     509             : 
     510             :                 // We're gonna ask the result to be put into here
     511           2 :                 static ResultQueue<std::string, u32, u8, u8> result_queue;
     512             : 
     513             :                 // Throw a request in
     514           2 :                 m_get_texture_queue.add(name, 0, 0, &result_queue);
     515             : 
     516             :                 /*infostream<<"Waiting for texture from main thread, name=\""
     517             :                                 <<name<<"\""<<std::endl;*/
     518             : 
     519             :                 try
     520             :                 {
     521           0 :                         while(true) {
     522             :                                 // Wait result for a second
     523             :                                 GetResult<std::string, u32, u8, u8>
     524           2 :                                         result = result_queue.pop_front(1000);
     525             : 
     526           2 :                                 if (result.key == name) {
     527           2 :                                         return result.item;
     528             :                                 }
     529             :                         }
     530             :                 }
     531           0 :                 catch(ItemNotFoundException &e)
     532             :                 {
     533           0 :                         errorstream<<"Waiting for texture " << name << " timed out."<<std::endl;
     534           0 :                         return 0;
     535             :                 }
     536             :         }
     537             : 
     538             :         infostream<<"getTextureId(): Failed"<<std::endl;
     539             : 
     540             :         return 0;
     541             : }
     542             : 
     543             : // Draw an image on top of an another one, using the alpha channel of the
     544             : // source image
     545             : static void blit_with_alpha(video::IImage *src, video::IImage *dst,
     546             :                 v2s32 src_pos, v2s32 dst_pos, v2u32 size);
     547             : 
     548             : // Like blit_with_alpha, but only modifies destination pixels that
     549             : // are fully opaque
     550             : static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
     551             :                 v2s32 src_pos, v2s32 dst_pos, v2u32 size);
     552             : 
     553             : // Like blit_with_alpha overlay, but uses an int to calculate the ratio
     554             : // and modifies any destination pixels that are not fully transparent
     555             : static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst,
     556             :                 v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio);
     557             : 
     558             : // Apply a mask to an image
     559             : static void apply_mask(video::IImage *mask, video::IImage *dst,
     560             :                 v2s32 mask_pos, v2s32 dst_pos, v2u32 size);
     561             : 
     562             : // Draw or overlay a crack
     563             : static void draw_crack(video::IImage *crack, video::IImage *dst,
     564             :                 bool use_overlay, s32 frame_count, s32 progression,
     565             :                 video::IVideoDriver *driver);
     566             : 
     567             : // Brighten image
     568             : void brighten(video::IImage *image);
     569             : // Parse a transform name
     570             : u32 parseImageTransform(const std::string& s);
     571             : // Apply transform to image dimension
     572             : core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
     573             : // Apply transform to image data
     574             : void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
     575             : 
     576             : /*
     577             :         This method generates all the textures
     578             : */
     579        2676 : u32 TextureSource::generateTexture(const std::string &name)
     580             : {
     581             :         //infostream << "generateTexture(): name=\"" << name << "\"" << std::endl;
     582             : 
     583             :         // Empty name means texture 0
     584        2676 :         if (name == "") {
     585           0 :                 infostream<<"generateTexture(): name is empty"<<std::endl;
     586           0 :                 return 0;
     587             :         }
     588             : 
     589             :         {
     590             :                 /*
     591             :                         See if texture already exists
     592             :                 */
     593        5352 :                 JMutexAutoLock lock(m_textureinfo_cache_mutex);
     594        2676 :                 std::map<std::string, u32>::iterator n;
     595        2676 :                 n = m_name_to_id.find(name);
     596        2676 :                 if (n != m_name_to_id.end()) {
     597           0 :                         return n->second;
     598             :                 }
     599             :         }
     600             : 
     601             :         /*
     602             :                 Calling only allowed from main thread
     603             :         */
     604        2676 :         if (get_current_thread_id() != m_main_thread) {
     605             :                 errorstream<<"TextureSource::generateTexture() "
     606           0 :                                 "called not from main thread"<<std::endl;
     607           0 :                 return 0;
     608             :         }
     609             : 
     610        2676 :         video::IVideoDriver *driver = m_device->getVideoDriver();
     611        2676 :         sanity_check(driver);
     612             : 
     613        2676 :         video::IImage *img = generateImage(name);
     614             : 
     615        2676 :         video::ITexture *tex = NULL;
     616             : 
     617        2676 :         if (img != NULL) {
     618             : #ifdef __ANDROID__
     619             :                 img = Align2Npot2(img, driver);
     620             : #endif
     621             :                 // Create texture from resulting image
     622        2676 :                 tex = driver->addTexture(name.c_str(), img);
     623        2676 :                 guiScalingCache(io::path(name.c_str()), driver, img);
     624        2676 :                 img->drop();
     625             :         }
     626             : 
     627             :         /*
     628             :                 Add texture to caches (add NULL textures too)
     629             :         */
     630             : 
     631        5352 :         JMutexAutoLock lock(m_textureinfo_cache_mutex);
     632             : 
     633        2676 :         u32 id = m_textureinfo_cache.size();
     634        5352 :         TextureInfo ti(name, tex);
     635        2676 :         m_textureinfo_cache.push_back(ti);
     636        2676 :         m_name_to_id[name] = id;
     637             : 
     638        2676 :         return id;
     639             : }
     640             : 
     641           0 : std::string TextureSource::getTextureName(u32 id)
     642             : {
     643           0 :         JMutexAutoLock lock(m_textureinfo_cache_mutex);
     644             : 
     645           0 :         if (id >= m_textureinfo_cache.size())
     646             :         {
     647           0 :                 errorstream<<"TextureSource::getTextureName(): id="<<id
     648           0 :                                 <<" >= m_textureinfo_cache.size()="
     649           0 :                                 <<m_textureinfo_cache.size()<<std::endl;
     650           0 :                 return "";
     651             :         }
     652             : 
     653           0 :         return m_textureinfo_cache[id].name;
     654             : }
     655             : 
     656     1063916 : video::ITexture* TextureSource::getTexture(u32 id)
     657             : {
     658     2127833 :         JMutexAutoLock lock(m_textureinfo_cache_mutex);
     659             : 
     660     1063917 :         if (id >= m_textureinfo_cache.size())
     661           0 :                 return NULL;
     662             : 
     663     1063917 :         return m_textureinfo_cache[id].texture;
     664             : }
     665             : 
     666      116172 : video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id)
     667             : {
     668      116172 :         u32 actual_id = getTextureId(name);
     669      116173 :         if (id){
     670       68336 :                 *id = actual_id;
     671             :         }
     672      116173 :         return getTexture(actual_id);
     673             : }
     674             : 
     675      107505 : video::ITexture* TextureSource::getTextureForMesh(const std::string &name, u32 *id)
     676             : {
     677      107505 :         return getTexture(name + "^[applyfiltersformesh", id);
     678             : }
     679             : 
     680        1168 : void TextureSource::processQueue()
     681             : {
     682             :         /*
     683             :                 Fetch textures
     684             :         */
     685             :         //NOTE this is only thread safe for ONE consumer thread!
     686        1168 :         if (!m_get_texture_queue.empty())
     687             :         {
     688             :                 GetRequest<std::string, u32, u8, u8>
     689           4 :                                 request = m_get_texture_queue.pop();
     690             : 
     691             :                 /*infostream<<"TextureSource::processQueue(): "
     692             :                                 <<"got texture request with "
     693             :                                 <<"name=\""<<request.key<<"\""
     694             :                                 <<std::endl;*/
     695             : 
     696           2 :                 m_get_texture_queue.pushResult(request, generateTexture(request.key));
     697             :         }
     698        1168 : }
     699             : 
     700        2283 : void TextureSource::insertSourceImage(const std::string &name, video::IImage *img)
     701             : {
     702             :         //infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
     703             : 
     704        2283 :         sanity_check(get_current_thread_id() == m_main_thread);
     705             : 
     706        2283 :         m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
     707        2283 :         m_source_image_existence.set(name, true);
     708        2283 : }
     709             : 
     710           1 : void TextureSource::rebuildImagesAndTextures()
     711             : {
     712           2 :         JMutexAutoLock lock(m_textureinfo_cache_mutex);
     713             : 
     714           1 :         video::IVideoDriver* driver = m_device->getVideoDriver();
     715           1 :         sanity_check(driver);
     716             : 
     717             :         // Recreate textures
     718           7 :         for (u32 i=0; i<m_textureinfo_cache.size(); i++){
     719           6 :                 TextureInfo *ti = &m_textureinfo_cache[i];
     720           6 :                 video::IImage *img = generateImage(ti->name);
     721             : #ifdef __ANDROID__
     722             :                 img = Align2Npot2(img, driver);
     723             :                 sanity_check(img->getDimension().Height == npot2(img->getDimension().Height));
     724             :                 sanity_check(img->getDimension().Width == npot2(img->getDimension().Width));
     725             : #endif
     726             :                 // Create texture from resulting image
     727           6 :                 video::ITexture *t = NULL;
     728           6 :                 if (img) {
     729           6 :                         t = driver->addTexture(ti->name.c_str(), img);
     730           6 :                         guiScalingCache(io::path(ti->name.c_str()), driver, img);
     731           6 :                         img->drop();
     732             :                 }
     733           6 :                 video::ITexture *t_old = ti->texture;
     734             :                 // Replace texture
     735           6 :                 ti->texture = t;
     736             : 
     737           6 :                 if (t_old)
     738           5 :                         m_texture_trash.push_back(t_old);
     739             :         }
     740           1 : }
     741             : 
     742           1 : video::ITexture* TextureSource::generateTextureFromMesh(
     743             :                 const TextureFromMeshParams &params)
     744             : {
     745           1 :         video::IVideoDriver *driver = m_device->getVideoDriver();
     746           1 :         sanity_check(driver);
     747             : 
     748             : #ifdef __ANDROID__
     749             :         const GLubyte* renderstr = glGetString(GL_RENDERER);
     750             :         std::string renderer((char*) renderstr);
     751             : 
     752             :         // use no render to texture hack
     753             :         if (
     754             :                 (renderer.find("Adreno") != std::string::npos) ||
     755             :                 (renderer.find("Mali") != std::string::npos) ||
     756             :                 (renderer.find("Immersion") != std::string::npos) ||
     757             :                 (renderer.find("Tegra") != std::string::npos) ||
     758             :                 g_settings->getBool("inventory_image_hack")
     759             :                 ) {
     760             :                 // Get a scene manager
     761             :                 scene::ISceneManager *smgr_main = m_device->getSceneManager();
     762             :                 sanity_check(smgr_main);
     763             :                 scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
     764             :                 sanity_check(smgr);
     765             : 
     766             :                 const float scaling = 0.2;
     767             : 
     768             :                 scene::IMeshSceneNode* meshnode =
     769             :                                 smgr->addMeshSceneNode(params.mesh, NULL,
     770             :                                                 -1, v3f(0,0,0), v3f(0,0,0),
     771             :                                                 v3f(1.0 * scaling,1.0 * scaling,1.0 * scaling), true);
     772             :                 meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
     773             :                 meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
     774             :                 meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
     775             :                 meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter);
     776             :                 meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter);
     777             : 
     778             :                 scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
     779             :                                 params.camera_position, params.camera_lookat);
     780             :                 // second parameter of setProjectionMatrix (isOrthogonal) is ignored
     781             :                 camera->setProjectionMatrix(params.camera_projection_matrix, false);
     782             : 
     783             :                 smgr->setAmbientLight(params.ambient_light);
     784             :                 smgr->addLightSceneNode(0,
     785             :                                 params.light_position,
     786             :                                 params.light_color,
     787             :                                 params.light_radius*scaling);
     788             : 
     789             :                 core::dimension2d<u32> screen = driver->getScreenSize();
     790             : 
     791             :                 // Render scene
     792             :                 driver->beginScene(true, true, video::SColor(0,0,0,0));
     793             :                 driver->clearZBuffer();
     794             :                 smgr->drawAll();
     795             : 
     796             :                 core::dimension2d<u32> partsize(screen.Width * scaling,screen.Height * scaling);
     797             : 
     798             :                 irr::video::IImage* rawImage =
     799             :                                 driver->createImage(irr::video::ECF_A8R8G8B8, partsize);
     800             : 
     801             :                 u8* pixels = static_cast<u8*>(rawImage->lock());
     802             :                 if (!pixels)
     803             :                 {
     804             :                         rawImage->drop();
     805             :                         return NULL;
     806             :                 }
     807             : 
     808             :                 core::rect<s32> source(
     809             :                                 screen.Width /2 - (screen.Width  * (scaling / 2)),
     810             :                                 screen.Height/2 - (screen.Height * (scaling / 2)),
     811             :                                 screen.Width /2 + (screen.Width  * (scaling / 2)),
     812             :                                 screen.Height/2 + (screen.Height * (scaling / 2))
     813             :                         );
     814             : 
     815             :                 glReadPixels(source.UpperLeftCorner.X, source.UpperLeftCorner.Y,
     816             :                                 partsize.Width, partsize.Height, GL_RGBA,
     817             :                                 GL_UNSIGNED_BYTE, pixels);
     818             : 
     819             :                 driver->endScene();
     820             : 
     821             :                 // Drop scene manager
     822             :                 smgr->drop();
     823             : 
     824             :                 unsigned int pixelcount = partsize.Width*partsize.Height;
     825             : 
     826             :                 u8* runptr = pixels;
     827             :                 for (unsigned int i=0; i < pixelcount; i++) {
     828             : 
     829             :                         u8 B = *runptr;
     830             :                         u8 G = *(runptr+1);
     831             :                         u8 R = *(runptr+2);
     832             :                         u8 A = *(runptr+3);
     833             : 
     834             :                         //BGRA -> RGBA
     835             :                         *runptr = R;
     836             :                         runptr ++;
     837             :                         *runptr = G;
     838             :                         runptr ++;
     839             :                         *runptr = B;
     840             :                         runptr ++;
     841             :                         *runptr = A;
     842             :                         runptr ++;
     843             :                 }
     844             : 
     845             :                 video::IImage* inventory_image =
     846             :                                 driver->createImage(irr::video::ECF_A8R8G8B8, params.dim);
     847             : 
     848             :                 rawImage->copyToScaling(inventory_image);
     849             :                 rawImage->drop();
     850             : 
     851             :                 guiScalingCache(io::path(params.rtt_texture_name.c_str()), driver, inventory_image);
     852             : 
     853             :                 video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image);
     854             :                 inventory_image->drop();
     855             : 
     856             :                 if (rtt == NULL) {
     857             :                         errorstream << "TextureSource::generateTextureFromMesh(): failed to recreate texture from image: " << params.rtt_texture_name << std::endl;
     858             :                         return NULL;
     859             :                 }
     860             : 
     861             :                 driver->makeColorKeyTexture(rtt, v2s32(0,0));
     862             : 
     863             :                 if (params.delete_texture_on_shutdown)
     864             :                         m_texture_trash.push_back(rtt);
     865             : 
     866             :                 return rtt;
     867             :         }
     868             : #endif
     869             : 
     870           1 :         if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
     871             :         {
     872             :                 static bool warned = false;
     873           0 :                 if (!warned)
     874             :                 {
     875           0 :                         errorstream<<"TextureSource::generateTextureFromMesh(): "
     876           0 :                                 <<"EVDF_RENDER_TO_TARGET not supported."<<std::endl;
     877           0 :                         warned = true;
     878             :                 }
     879           0 :                 return NULL;
     880             :         }
     881             : 
     882             :         // Create render target texture
     883           2 :         video::ITexture *rtt = driver->addRenderTargetTexture(
     884             :                         params.dim, params.rtt_texture_name.c_str(),
     885           2 :                         video::ECF_A8R8G8B8);
     886           1 :         if (rtt == NULL)
     887             :         {
     888           0 :                 errorstream<<"TextureSource::generateTextureFromMesh(): "
     889           0 :                         <<"addRenderTargetTexture returned NULL."<<std::endl;
     890           0 :                 return NULL;
     891             :         }
     892             : 
     893             :         // Set render target
     894           1 :         if (!driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0))) {
     895           0 :                 driver->removeTexture(rtt);
     896           0 :                 errorstream<<"TextureSource::generateTextureFromMesh(): "
     897           0 :                         <<"failed to set render target"<<std::endl;
     898           0 :                 return NULL;
     899             :         }
     900             : 
     901             :         // Get a scene manager
     902           1 :         scene::ISceneManager *smgr_main = m_device->getSceneManager();
     903             :         assert(smgr_main);
     904           1 :         scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
     905             :         assert(smgr);
     906             : 
     907             :         scene::IMeshSceneNode* meshnode =
     908           2 :                         smgr->addMeshSceneNode(params.mesh, NULL,
     909           2 :                                         -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
     910           1 :         meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
     911           1 :         meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
     912           1 :         meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
     913           1 :         meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter);
     914           1 :         meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter);
     915             : 
     916           1 :         scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
     917           2 :                         params.camera_position, params.camera_lookat);
     918             :         // second parameter of setProjectionMatrix (isOrthogonal) is ignored
     919           1 :         camera->setProjectionMatrix(params.camera_projection_matrix, false);
     920             : 
     921           1 :         smgr->setAmbientLight(params.ambient_light);
     922           1 :         smgr->addLightSceneNode(0,
     923             :                         params.light_position,
     924             :                         params.light_color,
     925           2 :                         params.light_radius);
     926             : 
     927             :         // Render scene
     928           1 :         driver->beginScene(true, true, video::SColor(0,0,0,0));
     929           1 :         smgr->drawAll();
     930           1 :         driver->endScene();
     931             : 
     932             :         // Drop scene manager
     933           1 :         smgr->drop();
     934             : 
     935             :         // Unset render target
     936           1 :         driver->setRenderTarget(0, false, true, 0);
     937             : 
     938           1 :         if (params.delete_texture_on_shutdown)
     939           1 :                 m_texture_trash.push_back(rtt);
     940             : 
     941           1 :         return rtt;
     942             : }
     943             : 
     944        5561 : video::IImage* TextureSource::generateImage(const std::string &name)
     945             : {
     946             :         /*
     947             :                 Get the base image
     948             :         */
     949             : 
     950        5561 :         const char separator = '^';
     951        5561 :         const char paren_open = '(';
     952        5561 :         const char paren_close = ')';
     953             : 
     954             :         // Find last separator in the name
     955        5561 :         s32 last_separator_pos = -1;
     956        5561 :         u8 paren_bal = 0;
     957      151703 :         for (s32 i = name.size() - 1; i >= 0; i--) {
     958      146142 :                 switch(name[i]) {
     959             :                 case separator:
     960        2889 :                         if (paren_bal == 0) {
     961        2812 :                                 last_separator_pos = i;
     962        2812 :                                 i = -1; // break out of loop
     963             :                         }
     964        2889 :                         break;
     965             :                 case paren_open:
     966          77 :                         if (paren_bal == 0) {
     967           0 :                                 errorstream << "generateImage(): unbalanced parentheses"
     968           0 :                                                 << "(extranous '(') while generating texture \""
     969           0 :                                                 << name << "\"" << std::endl;
     970           0 :                                 return NULL;
     971             :                         }
     972          77 :                         paren_bal--;
     973          77 :                         break;
     974             :                 case paren_close:
     975          77 :                         paren_bal++;
     976          77 :                         break;
     977             :                 default:
     978      143099 :                         break;
     979             :                 }
     980             :         }
     981        5561 :         if (paren_bal > 0) {
     982           0 :                 errorstream << "generateImage(): unbalanced parentheses"
     983           0 :                                 << "(missing matching '(') while generating texture \""
     984           0 :                                 << name << "\"" << std::endl;
     985           0 :                 return NULL;
     986             :         }
     987             : 
     988             : 
     989        5561 :         video::IImage *baseimg = NULL;
     990             : 
     991             :         /*
     992             :                 If separator was found, make the base image
     993             :                 using a recursive call.
     994             :         */
     995        5561 :         if (last_separator_pos != -1) {
     996        2812 :                 baseimg = generateImage(name.substr(0, last_separator_pos));
     997             :         }
     998             : 
     999             : 
    1000        5561 :         video::IVideoDriver* driver = m_device->getVideoDriver();
    1001        5561 :         sanity_check(driver);
    1002             : 
    1003             :         /*
    1004             :                 Parse out the last part of the name of the image and act
    1005             :                 according to it
    1006             :         */
    1007             : 
    1008       11122 :         std::string last_part_of_name = name.substr(last_separator_pos + 1);
    1009             : 
    1010             :         /* 
    1011             :                 If this name is enclosed in parentheses, generate it
    1012             :                 and blit it onto the base image
    1013             :         */
    1014       11122 :         if (last_part_of_name[0] == paren_open
    1015        5561 :                         && last_part_of_name[last_part_of_name.size() - 1] == paren_close) {
    1016             :                 std::string name2 = last_part_of_name.substr(1,
    1017         134 :                                 last_part_of_name.size() - 2);
    1018          67 :                 video::IImage *tmp = generateImage(name2);
    1019          67 :                 if (!tmp) {
    1020             :                         errorstream << "generateImage(): "
    1021           0 :                                 "Failed to generate \"" << name2 << "\""
    1022           0 :                                 << std::endl;
    1023           0 :                         return NULL;
    1024             :                 }
    1025          67 :                 core::dimension2d<u32> dim = tmp->getDimension();
    1026          67 :                 if (!baseimg)
    1027          32 :                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
    1028          67 :                 blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim);
    1029          67 :                 tmp->drop();
    1030        5494 :         } else if (!generateImagePart(last_part_of_name, baseimg)) {
    1031             :                 // Generate image according to part of name
    1032             :                 errorstream << "generateImage(): "
    1033           0 :                                 "Failed to generate \"" << last_part_of_name << "\""
    1034           0 :                                 << std::endl;
    1035             :         }
    1036             : 
    1037             :         // If no resulting image, print a warning
    1038        5561 :         if (baseimg == NULL) {
    1039             :                 errorstream << "generateImage(): baseimg is NULL (attempted to"
    1040           0 :                                 " create texture \"" << name << "\")" << std::endl;
    1041             :         }
    1042             : 
    1043        5561 :         return baseimg;
    1044             : }
    1045             : 
    1046             : #ifdef __ANDROID__
    1047             : #include <GLES/gl.h>
    1048             : /**
    1049             :  * Check and align image to npot2 if required by hardware
    1050             :  * @param image image to check for npot2 alignment
    1051             :  * @param driver driver to use for image operations
    1052             :  * @return image or copy of image aligned to npot2
    1053             :  */
    1054             : video::IImage * Align2Npot2(video::IImage * image,
    1055             :                 video::IVideoDriver* driver)
    1056             : {
    1057             :         if (image == NULL) {
    1058             :                 return image;
    1059             :         }
    1060             : 
    1061             :         core::dimension2d<u32> dim = image->getDimension();
    1062             : 
    1063             :         std::string extensions = (char*) glGetString(GL_EXTENSIONS);
    1064             :         if (extensions.find("GL_OES_texture_npot") != std::string::npos) {
    1065             :                 return image;
    1066             :         }
    1067             : 
    1068             :         unsigned int height = npot2(dim.Height);
    1069             :         unsigned int width  = npot2(dim.Width);
    1070             : 
    1071             :         if ((dim.Height == height) &&
    1072             :                         (dim.Width == width)) {
    1073             :                 return image;
    1074             :         }
    1075             : 
    1076             :         if (dim.Height > height) {
    1077             :                 height *= 2;
    1078             :         }
    1079             : 
    1080             :         if (dim.Width > width) {
    1081             :                 width *= 2;
    1082             :         }
    1083             : 
    1084             :         video::IImage *targetimage =
    1085             :                         driver->createImage(video::ECF_A8R8G8B8,
    1086             :                                         core::dimension2d<u32>(width, height));
    1087             : 
    1088             :         if (targetimage != NULL) {
    1089             :                 image->copyToScaling(targetimage);
    1090             :         }
    1091             :         image->drop();
    1092             :         return targetimage;
    1093             : }
    1094             : 
    1095             : #endif
    1096             : 
    1097        5494 : bool TextureSource::generateImagePart(std::string part_of_name,
    1098             :                 video::IImage *& baseimg)
    1099             : {
    1100        5494 :         video::IVideoDriver* driver = m_device->getVideoDriver();
    1101        5494 :         sanity_check(driver);
    1102             : 
    1103             :         // Stuff starting with [ are special commands
    1104        5494 :         if (part_of_name.size() == 0 || part_of_name[0] != '[')
    1105             :         {
    1106        3020 :                 video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device);
    1107             : #ifdef __ANDROID__
    1108             :                 image = Align2Npot2(image, driver);
    1109             : #endif
    1110        3020 :                 if (image == NULL) {
    1111           2 :                         if (part_of_name != "") {
    1112           0 :                                 if (part_of_name.find("_normal.png") == std::string::npos){
    1113           0 :                                         errorstream<<"generateImage(): Could not load image \""
    1114           0 :                                                 <<part_of_name<<"\""<<" while building texture"<<std::endl;
    1115           0 :                                         errorstream<<"generateImage(): Creating a dummy"
    1116           0 :                                                 <<" image for \""<<part_of_name<<"\""<<std::endl;
    1117             :                                 } else {
    1118           0 :                                         infostream<<"generateImage(): Could not load normal map \""
    1119           0 :                                                 <<part_of_name<<"\""<<std::endl;
    1120           0 :                                         infostream<<"generateImage(): Creating a dummy"
    1121           0 :                                                 <<" normal map for \""<<part_of_name<<"\""<<std::endl;
    1122             :                                 }
    1123             :                         }
    1124             : 
    1125             :                         // Just create a dummy image
    1126             :                         //core::dimension2d<u32> dim(2,2);
    1127           2 :                         core::dimension2d<u32> dim(1,1);
    1128           2 :                         image = driver->createImage(video::ECF_A8R8G8B8, dim);
    1129           2 :                         sanity_check(image != NULL);
    1130             :                         /*image->setPixel(0,0, video::SColor(255,255,0,0));
    1131             :                         image->setPixel(1,0, video::SColor(255,0,255,0));
    1132             :                         image->setPixel(0,1, video::SColor(255,0,0,255));
    1133             :                         image->setPixel(1,1, video::SColor(255,255,0,255));*/
    1134           6 :                         image->setPixel(0,0, video::SColor(255,myrand()%256,
    1135           6 :                                         myrand()%256,myrand()%256));
    1136             :                         /*image->setPixel(1,0, video::SColor(255,myrand()%256,
    1137             :                                         myrand()%256,myrand()%256));
    1138             :                         image->setPixel(0,1, video::SColor(255,myrand()%256,
    1139             :                                         myrand()%256,myrand()%256));
    1140             :                         image->setPixel(1,1, video::SColor(255,myrand()%256,
    1141             :                                         myrand()%256,myrand()%256));*/
    1142             :                 }
    1143             : 
    1144             :                 // If base image is NULL, load as base.
    1145        3020 :                 if (baseimg == NULL)
    1146             :                 {
    1147             :                         //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
    1148             :                         /*
    1149             :                                 Copy it this way to get an alpha channel.
    1150             :                                 Otherwise images with alpha cannot be blitted on
    1151             :                                 images that don't have alpha in the original file.
    1152             :                         */
    1153        2701 :                         core::dimension2d<u32> dim = image->getDimension();
    1154        2701 :                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
    1155        2701 :                         image->copyTo(baseimg);
    1156             :                 }
    1157             :                 // Else blit on base.
    1158             :                 else
    1159             :                 {
    1160             :                         //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
    1161             :                         // Size of the copied area
    1162         319 :                         core::dimension2d<u32> dim = image->getDimension();
    1163             :                         //core::dimension2d<u32> dim(16,16);
    1164             :                         // Position to copy the blitted to in the base image
    1165         319 :                         core::position2d<s32> pos_to(0,0);
    1166             :                         // Position to copy the blitted from in the blitted image
    1167         319 :                         core::position2d<s32> pos_from(0,0);
    1168             :                         // Blit
    1169             :                         /*image->copyToWithAlpha(baseimg, pos_to,
    1170             :                                         core::rect<s32>(pos_from, dim),
    1171             :                                         video::SColor(255,255,255,255),
    1172             :                                         NULL);*/
    1173         319 :                         blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
    1174             :                 }
    1175             :                 //cleanup
    1176        3020 :                 image->drop();
    1177             :         }
    1178             :         else
    1179             :         {
    1180             :                 // A special texture modification
    1181             : 
    1182             :                 /*infostream<<"generateImage(): generating special "
    1183             :                                 <<"modification \""<<part_of_name<<"\""
    1184             :                                 <<std::endl;*/
    1185             : 
    1186             :                 /*
    1187             :                         [crack:N:P
    1188             :                         [cracko:N:P
    1189             :                         Adds a cracking texture
    1190             :                         N = animation frame count, P = crack progression
    1191             :                 */
    1192        2474 :                 if (str_starts_with(part_of_name, "[crack"))
    1193             :                 {
    1194           0 :                         if (baseimg == NULL) {
    1195           0 :                                 errorstream<<"generateImagePart(): baseimg == NULL "
    1196           0 :                                                 <<"for part_of_name=\""<<part_of_name
    1197           0 :                                                 <<"\", cancelling."<<std::endl;
    1198           0 :                                 return false;
    1199             :                         }
    1200             : 
    1201             :                         // Crack image number and overlay option
    1202           0 :                         bool use_overlay = (part_of_name[6] == 'o');
    1203           0 :                         Strfnd sf(part_of_name);
    1204           0 :                         sf.next(":");
    1205           0 :                         s32 frame_count = stoi(sf.next(":"));
    1206           0 :                         s32 progression = stoi(sf.next(":"));
    1207             : 
    1208             :                         /*
    1209             :                                 Load crack image.
    1210             : 
    1211             :                                 It is an image with a number of cracking stages
    1212             :                                 horizontally tiled.
    1213             :                         */
    1214           0 :                         video::IImage *img_crack = m_sourcecache.getOrLoad(
    1215           0 :                                         "crack_anylength.png", m_device);
    1216             : 
    1217           0 :                         if (img_crack && progression >= 0)
    1218             :                         {
    1219           0 :                                 draw_crack(img_crack, baseimg,
    1220             :                                                 use_overlay, frame_count,
    1221           0 :                                                 progression, driver);
    1222           0 :                                 img_crack->drop();
    1223             :                         }
    1224             :                 }
    1225             :                 /*
    1226             :                         [combine:WxH:X,Y=filename:X,Y=filename2
    1227             :                         Creates a bigger texture from an amount of smaller ones
    1228             :                 */
    1229        2474 :                 else if (str_starts_with(part_of_name, "[combine"))
    1230             :                 {
    1231          32 :                         Strfnd sf(part_of_name);
    1232          16 :                         sf.next(":");
    1233          16 :                         u32 w0 = stoi(sf.next("x"));
    1234          16 :                         u32 h0 = stoi(sf.next(":"));
    1235             :                         //infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
    1236          16 :                         core::dimension2d<u32> dim(w0,h0);
    1237          16 :                         if (baseimg == NULL) {
    1238          16 :                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
    1239          16 :                                 baseimg->fill(video::SColor(0,0,0,0));
    1240             :                         }
    1241        1486 :                         while (sf.atend() == false) {
    1242         735 :                                 u32 x = stoi(sf.next(","));
    1243         735 :                                 u32 y = stoi(sf.next("="));
    1244        1470 :                                 std::string filename = sf.next(":");
    1245         735 :                                 infostream<<"Adding \""<<filename
    1246         735 :                                                 <<"\" to combined ("<<x<<","<<y<<")"
    1247         735 :                                                 <<std::endl;
    1248         735 :                                 video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
    1249         735 :                                 if (img) {
    1250         735 :                                         core::dimension2d<u32> dim = img->getDimension();
    1251         735 :                                         infostream<<"Size "<<dim.Width
    1252        1470 :                                                         <<"x"<<dim.Height<<std::endl;
    1253         735 :                                         core::position2d<s32> pos_base(x, y);
    1254             :                                         video::IImage *img2 =
    1255         735 :                                                         driver->createImage(video::ECF_A8R8G8B8, dim);
    1256         735 :                                         img->copyTo(img2);
    1257         735 :                                         img->drop();
    1258             :                                         /*img2->copyToWithAlpha(baseimg, pos_base,
    1259             :                                                         core::rect<s32>(v2s32(0,0), dim),
    1260             :                                                         video::SColor(255,255,255,255),
    1261             :                                                         NULL);*/
    1262         735 :                                         blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
    1263         735 :                                         img2->drop();
    1264             :                                 } else {
    1265           0 :                                         errorstream << "generateImagePart(): Failed to load image \""
    1266           0 :                                                 << filename << "\" for [combine" << std::endl;
    1267             :                                 }
    1268             :                         }
    1269             :                 }
    1270             :                 /*
    1271             :                         "[brighten"
    1272             :                 */
    1273        2458 :                 else if (str_starts_with(part_of_name, "[brighten"))
    1274             :                 {
    1275          13 :                         if (baseimg == NULL) {
    1276           0 :                                 errorstream<<"generateImagePart(): baseimg==NULL "
    1277           0 :                                                 <<"for part_of_name=\""<<part_of_name
    1278           0 :                                                 <<"\", cancelling."<<std::endl;
    1279           0 :                                 return false;
    1280             :                         }
    1281             : 
    1282          13 :                         brighten(baseimg);
    1283             :                 }
    1284             :                 /*
    1285             :                         "[noalpha"
    1286             :                         Make image completely opaque.
    1287             :                         Used for the leaves texture when in old leaves mode, so
    1288             :                         that the transparent parts don't look completely black
    1289             :                         when simple alpha channel is used for rendering.
    1290             :                 */
    1291        2445 :                 else if (str_starts_with(part_of_name, "[noalpha"))
    1292             :                 {
    1293           0 :                         if (baseimg == NULL){
    1294           0 :                                 errorstream<<"generateImagePart(): baseimg==NULL "
    1295           0 :                                                 <<"for part_of_name=\""<<part_of_name
    1296           0 :                                                 <<"\", cancelling."<<std::endl;
    1297           0 :                                 return false;
    1298             :                         }
    1299             : 
    1300           0 :                         core::dimension2d<u32> dim = baseimg->getDimension();
    1301             : 
    1302             :                         // Set alpha to full
    1303           0 :                         for (u32 y=0; y<dim.Height; y++)
    1304           0 :                         for (u32 x=0; x<dim.Width; x++)
    1305             :                         {
    1306           0 :                                 video::SColor c = baseimg->getPixel(x,y);
    1307           0 :                                 c.setAlpha(255);
    1308           0 :                                 baseimg->setPixel(x,y,c);
    1309             :                         }
    1310             :                 }
    1311             :                 /*
    1312             :                         "[makealpha:R,G,B"
    1313             :                         Convert one color to transparent.
    1314             :                 */
    1315        2445 :                 else if (str_starts_with(part_of_name, "[makealpha:"))
    1316             :                 {
    1317          44 :                         if (baseimg == NULL) {
    1318           0 :                                 errorstream<<"generateImagePart(): baseimg == NULL "
    1319           0 :                                                 <<"for part_of_name=\""<<part_of_name
    1320           0 :                                                 <<"\", cancelling."<<std::endl;
    1321           0 :                                 return false;
    1322             :                         }
    1323             : 
    1324          88 :                         Strfnd sf(part_of_name.substr(11));
    1325          44 :                         u32 r1 = stoi(sf.next(","));
    1326          44 :                         u32 g1 = stoi(sf.next(","));
    1327          44 :                         u32 b1 = stoi(sf.next(""));
    1328          88 :                         std::string filename = sf.next("");
    1329             : 
    1330          44 :                         core::dimension2d<u32> dim = baseimg->getDimension();
    1331             : 
    1332             :                         /*video::IImage *oldbaseimg = baseimg;
    1333             :                         baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
    1334             :                         oldbaseimg->copyTo(baseimg);
    1335             :                         oldbaseimg->drop();*/
    1336             : 
    1337             :                         // Set alpha to full
    1338        1932 :                         for (u32 y=0; y<dim.Height; y++)
    1339      366176 :                         for (u32 x=0; x<dim.Width; x++)
    1340             :                         {
    1341      364288 :                                 video::SColor c = baseimg->getPixel(x,y);
    1342      364288 :                                 u32 r = c.getRed();
    1343      364288 :                                 u32 g = c.getGreen();
    1344      364288 :                                 u32 b = c.getBlue();
    1345      364288 :                                 if (!(r == r1 && g == g1 && b == b1))
    1346       10816 :                                         continue;
    1347      353472 :                                 c.setAlpha(0);
    1348      353472 :                                 baseimg->setPixel(x,y,c);
    1349             :                         }
    1350             :                 }
    1351             :                 /*
    1352             :                         "[transformN"
    1353             :                         Rotates and/or flips the image.
    1354             : 
    1355             :                         N can be a number (between 0 and 7) or a transform name.
    1356             :                         Rotations are counter-clockwise.
    1357             :                         0  I      identity
    1358             :                         1  R90    rotate by 90 degrees
    1359             :                         2  R180   rotate by 180 degrees
    1360             :                         3  R270   rotate by 270 degrees
    1361             :                         4  FX     flip X
    1362             :                         5  FXR90  flip X then rotate by 90 degrees
    1363             :                         6  FY     flip Y
    1364             :                         7  FYR90  flip Y then rotate by 90 degrees
    1365             : 
    1366             :                         Note: Transform names can be concatenated to produce
    1367             :                         their product (applies the first then the second).
    1368             :                         The resulting transform will be equivalent to one of the
    1369             :                         eight existing ones, though (see: dihedral group).
    1370             :                 */
    1371        2401 :                 else if (str_starts_with(part_of_name, "[transform"))
    1372             :                 {
    1373         134 :                         if (baseimg == NULL) {
    1374           0 :                                 errorstream<<"generateImagePart(): baseimg == NULL "
    1375           0 :                                                 <<"for part_of_name=\""<<part_of_name
    1376           0 :                                                 <<"\", cancelling."<<std::endl;
    1377           0 :                                 return false;
    1378             :                         }
    1379             : 
    1380         134 :                         u32 transform = parseImageTransform(part_of_name.substr(10));
    1381             :                         core::dimension2d<u32> dim = imageTransformDimension(
    1382         134 :                                         transform, baseimg->getDimension());
    1383         268 :                         video::IImage *image = driver->createImage(
    1384         402 :                                         baseimg->getColorFormat(), dim);
    1385         134 :                         sanity_check(image != NULL);
    1386         134 :                         imageTransform(transform, baseimg, image);
    1387         134 :                         baseimg->drop();
    1388         134 :                         baseimg = image;
    1389             :                 }
    1390             :                 /*
    1391             :                         [inventorycube{topimage{leftimage{rightimage
    1392             :                         In every subimage, replace ^ with &.
    1393             :                         Create an "inventory cube".
    1394             :                         NOTE: This should be used only on its own.
    1395             :                         Example (a grass block (not actually used in game):
    1396             :                         "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
    1397             :                 */
    1398        2267 :                 else if (str_starts_with(part_of_name, "[inventorycube"))
    1399             :                 {
    1400           0 :                         if (baseimg != NULL){
    1401           0 :                                 errorstream<<"generateImagePart(): baseimg != NULL "
    1402           0 :                                                 <<"for part_of_name=\""<<part_of_name
    1403           0 :                                                 <<"\", cancelling."<<std::endl;
    1404           0 :                                 return false;
    1405             :                         }
    1406             : 
    1407           0 :                         str_replace(part_of_name, '&', '^');
    1408           0 :                         Strfnd sf(part_of_name);
    1409           0 :                         sf.next("{");
    1410           0 :                         std::string imagename_top = sf.next("{");
    1411           0 :                         std::string imagename_left = sf.next("{");
    1412           0 :                         std::string imagename_right = sf.next("{");
    1413             : 
    1414             :                         // Generate images for the faces of the cube
    1415           0 :                         video::IImage *img_top = generateImage(imagename_top);
    1416           0 :                         video::IImage *img_left = generateImage(imagename_left);
    1417           0 :                         video::IImage *img_right = generateImage(imagename_right);
    1418             : 
    1419           0 :                         if (img_top == NULL || img_left == NULL || img_right == NULL) {
    1420           0 :                                 errorstream << "generateImagePart(): Failed to create textures"
    1421           0 :                                                 << " for inventorycube \"" << part_of_name << "\""
    1422           0 :                                                 << std::endl;
    1423           0 :                                 baseimg = generateImage(imagename_top);
    1424           0 :                                 return true;
    1425             :                         }
    1426             : 
    1427             : #ifdef __ANDROID__
    1428             :                         assert(img_top->getDimension().Height == npot2(img_top->getDimension().Height));
    1429             :                         assert(img_top->getDimension().Width == npot2(img_top->getDimension().Width));
    1430             : 
    1431             :                         assert(img_left->getDimension().Height == npot2(img_left->getDimension().Height));
    1432             :                         assert(img_left->getDimension().Width == npot2(img_left->getDimension().Width));
    1433             : 
    1434             :                         assert(img_right->getDimension().Height == npot2(img_right->getDimension().Height));
    1435             :                         assert(img_right->getDimension().Width == npot2(img_right->getDimension().Width));
    1436             : #endif
    1437             : 
    1438             :                         // Create textures from images
    1439           0 :                         video::ITexture *texture_top = driver->addTexture(
    1440           0 :                                         (imagename_top + "__temp__").c_str(), img_top);
    1441           0 :                         video::ITexture *texture_left = driver->addTexture(
    1442           0 :                                         (imagename_left + "__temp__").c_str(), img_left);
    1443           0 :                         video::ITexture *texture_right = driver->addTexture(
    1444           0 :                                         (imagename_right + "__temp__").c_str(), img_right);
    1445           0 :                         FATAL_ERROR_IF(!(texture_top && texture_left && texture_right), "");
    1446             : 
    1447             :                         // Drop images
    1448           0 :                         img_top->drop();
    1449           0 :                         img_left->drop();
    1450           0 :                         img_right->drop();
    1451             : 
    1452             :                         /*
    1453             :                                 Draw a cube mesh into a render target texture
    1454             :                         */
    1455           0 :                         scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
    1456           0 :                         setMeshColor(cube, video::SColor(255, 255, 255, 255));
    1457           0 :                         cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
    1458           0 :                         cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
    1459           0 :                         cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
    1460           0 :                         cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
    1461           0 :                         cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
    1462           0 :                         cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
    1463             : 
    1464           0 :                         TextureFromMeshParams params;
    1465           0 :                         params.mesh = cube;
    1466           0 :                         params.dim.set(64, 64);
    1467           0 :                         params.rtt_texture_name = part_of_name + "_RTT";
    1468             :                         // We will delete the rtt texture ourselves
    1469           0 :                         params.delete_texture_on_shutdown = false;
    1470           0 :                         params.camera_position.set(0, 1.0, -1.5);
    1471           0 :                         params.camera_position.rotateXZBy(45);
    1472           0 :                         params.camera_lookat.set(0, 0, 0);
    1473             :                         // Set orthogonal projection
    1474             :                         params.camera_projection_matrix.buildProjectionMatrixOrthoLH(
    1475           0 :                                         1.65, 1.65, 0, 100);
    1476             : 
    1477           0 :                         params.ambient_light.set(1.0, 0.2, 0.2, 0.2);
    1478           0 :                         params.light_position.set(10, 100, -50);
    1479           0 :                         params.light_color.set(1.0, 0.5, 0.5, 0.5);
    1480           0 :                         params.light_radius = 1000;
    1481             : 
    1482           0 :                         video::ITexture *rtt = generateTextureFromMesh(params);
    1483             : 
    1484             :                         // Drop mesh
    1485           0 :                         cube->drop();
    1486             : 
    1487             :                         // Free textures
    1488           0 :                         driver->removeTexture(texture_top);
    1489           0 :                         driver->removeTexture(texture_left);
    1490           0 :                         driver->removeTexture(texture_right);
    1491             : 
    1492           0 :                         if (rtt == NULL) {
    1493           0 :                                 baseimg = generateImage(imagename_top);
    1494           0 :                                 return true;
    1495             :                         }
    1496             : 
    1497             :                         // Create image of render target
    1498           0 :                         video::IImage *image = driver->createImage(rtt, v2s32(0, 0), params.dim);
    1499           0 :                         FATAL_ERROR_IF(!image, "Could not create image of render target");
    1500             : 
    1501             :                         // Cleanup texture
    1502           0 :                         driver->removeTexture(rtt);
    1503             : 
    1504           0 :                         baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim);
    1505             : 
    1506           0 :                         if (image) {
    1507           0 :                                 image->copyTo(baseimg);
    1508           0 :                                 image->drop();
    1509             :                         }
    1510             :                 }
    1511             :                 /*
    1512             :                         [lowpart:percent:filename
    1513             :                         Adds the lower part of a texture
    1514             :                 */
    1515        2267 :                 else if (str_starts_with(part_of_name, "[lowpart:"))
    1516             :                 {
    1517           0 :                         Strfnd sf(part_of_name);
    1518           0 :                         sf.next(":");
    1519           0 :                         u32 percent = stoi(sf.next(":"));
    1520           0 :                         std::string filename = sf.next(":");
    1521             :                         //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
    1522             : 
    1523           0 :                         if (baseimg == NULL)
    1524           0 :                                 baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
    1525           0 :                         video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
    1526           0 :                         if (img)
    1527             :                         {
    1528           0 :                                 core::dimension2d<u32> dim = img->getDimension();
    1529           0 :                                 core::position2d<s32> pos_base(0, 0);
    1530             :                                 video::IImage *img2 =
    1531           0 :                                                 driver->createImage(video::ECF_A8R8G8B8, dim);
    1532           0 :                                 img->copyTo(img2);
    1533           0 :                                 img->drop();
    1534           0 :                                 core::position2d<s32> clippos(0, 0);
    1535           0 :                                 clippos.Y = dim.Height * (100-percent) / 100;
    1536           0 :                                 core::dimension2d<u32> clipdim = dim;
    1537           0 :                                 clipdim.Height = clipdim.Height * percent / 100 + 1;
    1538           0 :                                 core::rect<s32> cliprect(clippos, clipdim);
    1539           0 :                                 img2->copyToWithAlpha(baseimg, pos_base,
    1540             :                                                 core::rect<s32>(v2s32(0,0), dim),
    1541             :                                                 video::SColor(255,255,255,255),
    1542           0 :                                                 &cliprect);
    1543           0 :                                 img2->drop();
    1544             :                         }
    1545             :                 }
    1546             :                 /*
    1547             :                         [verticalframe:N:I
    1548             :                         Crops a frame of a vertical animation.
    1549             :                         N = frame count, I = frame index
    1550             :                 */
    1551        2267 :                 else if (str_starts_with(part_of_name, "[verticalframe:"))
    1552             :                 {
    1553         546 :                         Strfnd sf(part_of_name);
    1554         273 :                         sf.next(":");
    1555         273 :                         u32 frame_count = stoi(sf.next(":"));
    1556         273 :                         u32 frame_index = stoi(sf.next(":"));
    1557             : 
    1558         273 :                         if (baseimg == NULL){
    1559           0 :                                 errorstream<<"generateImagePart(): baseimg != NULL "
    1560           0 :                                                 <<"for part_of_name=\""<<part_of_name
    1561           0 :                                                 <<"\", cancelling."<<std::endl;
    1562           0 :                                 return false;
    1563             :                         }
    1564             : 
    1565         273 :                         v2u32 frame_size = baseimg->getDimension();
    1566         273 :                         frame_size.Y /= frame_count;
    1567             : 
    1568         546 :                         video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
    1569         546 :                                         frame_size);
    1570         273 :                         if (!img){
    1571           0 :                                 errorstream<<"generateImagePart(): Could not create image "
    1572           0 :                                                 <<"for part_of_name=\""<<part_of_name
    1573           0 :                                                 <<"\", cancelling."<<std::endl;
    1574           0 :                                 return false;
    1575             :                         }
    1576             : 
    1577             :                         // Fill target image with transparency
    1578         273 :                         img->fill(video::SColor(0,0,0,0));
    1579             : 
    1580         273 :                         core::dimension2d<u32> dim = frame_size;
    1581         273 :                         core::position2d<s32> pos_dst(0, 0);
    1582         273 :                         core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
    1583         546 :                         baseimg->copyToWithAlpha(img, pos_dst,
    1584             :                                         core::rect<s32>(pos_src, dim),
    1585             :                                         video::SColor(255,255,255,255),
    1586         546 :                                         NULL);
    1587             :                         // Replace baseimg
    1588         273 :                         baseimg->drop();
    1589         273 :                         baseimg = img;
    1590             :                 }
    1591             :                 /*
    1592             :                         [mask:filename
    1593             :                         Applies a mask to an image
    1594             :                 */
    1595        1994 :                 else if (str_starts_with(part_of_name, "[mask:"))
    1596             :                 {
    1597           0 :                         if (baseimg == NULL) {
    1598           0 :                                 errorstream << "generateImage(): baseimg == NULL "
    1599           0 :                                                 << "for part_of_name=\"" << part_of_name
    1600           0 :                                                 << "\", cancelling." << std::endl;
    1601           0 :                                 return false;
    1602             :                         }
    1603           0 :                         Strfnd sf(part_of_name);
    1604           0 :                         sf.next(":");
    1605           0 :                         std::string filename = sf.next(":");
    1606             : 
    1607           0 :                         video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
    1608           0 :                         if (img) {
    1609           0 :                                 apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0),
    1610           0 :                                                 img->getDimension());
    1611             :                         } else {
    1612           0 :                                 errorstream << "generateImage(): Failed to load \""
    1613           0 :                                                 << filename << "\".";
    1614             :                         }
    1615             :                 }
    1616             :                 /*
    1617             :                         [colorize:color
    1618             :                         Overlays image with given color
    1619             :                         color = color as ColorString
    1620             :                 */
    1621        1994 :                 else if (str_starts_with(part_of_name, "[colorize:"))
    1622             :                 {
    1623         400 :                         Strfnd sf(part_of_name);
    1624         200 :                         sf.next(":");
    1625         400 :                         std::string color_str = sf.next(":");
    1626         400 :                         std::string ratio_str = sf.next(":");
    1627             : 
    1628         200 :                         if (baseimg == NULL) {
    1629           0 :                                 errorstream << "generateImagePart(): baseimg != NULL "
    1630           0 :                                                 << "for part_of_name=\"" << part_of_name
    1631           0 :                                                 << "\", cancelling." << std::endl;
    1632           0 :                                 return false;
    1633             :                         }
    1634             : 
    1635         200 :                         video::SColor color;
    1636         200 :                         int ratio = -1;
    1637             : 
    1638         200 :                         if (!parseColorString(color_str, color, false))
    1639           0 :                                 return false;
    1640             : 
    1641         200 :                         if (is_number(ratio_str))
    1642         199 :                                 ratio = mystoi(ratio_str, 0, 255);
    1643             : 
    1644         200 :                         core::dimension2d<u32> dim = baseimg->getDimension();
    1645         200 :                         video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
    1646             : 
    1647         200 :                         if (!img) {
    1648           0 :                                 errorstream << "generateImagePart(): Could not create image "
    1649           0 :                                                 << "for part_of_name=\"" << part_of_name
    1650           0 :                                                 << "\", cancelling." << std::endl;
    1651           0 :                                 return false;
    1652             :                         }
    1653             : 
    1654         200 :                         img->fill(video::SColor(color));
    1655             :                         // Overlay the colored image
    1656         200 :                         blit_with_interpolate_overlay(img, baseimg, v2s32(0,0), v2s32(0,0), dim, ratio);
    1657         200 :                         img->drop();
    1658             :                 }
    1659        1794 :                 else if (str_starts_with(part_of_name, "[applyfiltersformesh"))
    1660             :                 {
    1661             :                         // Apply the "clean transparent" filter, if configured.
    1662        1794 :                         if (g_settings->getBool("texture_clean_transparent"))
    1663           0 :                                 imageCleanTransparent(baseimg, 127);
    1664             : 
    1665             :                         /* Upscale textures to user's requested minimum size.  This is a trick to make
    1666             :                          * filters look as good on low-res textures as on high-res ones, by making
    1667             :                          * low-res textures BECOME high-res ones.  This is helpful for worlds that
    1668             :                          * mix high- and low-res textures, or for mods with least-common-denominator
    1669             :                          * textures that don't have the resources to offer high-res alternatives.
    1670             :                          */
    1671        1794 :                         s32 scaleto = g_settings->getS32("texture_min_size");
    1672        1794 :                         if (scaleto > 1) {
    1673        1794 :                                 const core::dimension2d<u32> dim = baseimg->getDimension();
    1674             : 
    1675             :                                 /* Calculate scaling needed to make the shortest texture dimension
    1676             :                                  * equal to the target minimum.  If e.g. this is a vertical frames
    1677             :                                  * animation, the short dimension will be the real size.
    1678             :                                  */
    1679        1794 :                                 u32 xscale = scaleto / dim.Width;
    1680        1794 :                                 u32 yscale = scaleto / dim.Height;
    1681        1794 :                                 u32 scale = (xscale > yscale) ? xscale : yscale;
    1682             : 
    1683             :                                 // Never downscale; only scale up by 2x or more.
    1684        1794 :                                 if (scale > 1) {
    1685        1472 :                                         u32 w = scale * dim.Width;
    1686        1472 :                                         u32 h = scale * dim.Height;
    1687        1472 :                                         const core::dimension2d<u32> newdim = core::dimension2d<u32>(w, h);
    1688        2944 :                                         video::IImage *newimg = driver->createImage(
    1689        4416 :                                                         baseimg->getColorFormat(), newdim);
    1690        1472 :                                         baseimg->copyToScaling(newimg);
    1691        1472 :                                         baseimg->drop();
    1692        1472 :                                         baseimg = newimg;
    1693             :                                 }
    1694             :                         }
    1695             :                 }
    1696             :                 else
    1697             :                 {
    1698             :                         errorstream << "generateImagePart(): Invalid "
    1699           0 :                                         " modification: \"" << part_of_name << "\"" << std::endl;
    1700             :                 }
    1701             :         }
    1702             : 
    1703        5494 :         return true;
    1704             : }
    1705             : 
    1706             : /*
    1707             :         Draw an image on top of an another one, using the alpha channel of the
    1708             :         source image
    1709             : 
    1710             :         This exists because IImage::copyToWithAlpha() doesn't seem to always
    1711             :         work.
    1712             : */
    1713        1121 : static void blit_with_alpha(video::IImage *src, video::IImage *dst,
    1714             :                 v2s32 src_pos, v2s32 dst_pos, v2u32 size)
    1715             : {
    1716       53014 :         for (u32 y0=0; y0<size.Y; y0++)
    1717    17230867 :         for (u32 x0=0; x0<size.X; x0++)
    1718             :         {
    1719    17178974 :                 s32 src_x = src_pos.X + x0;
    1720    17178974 :                 s32 src_y = src_pos.Y + y0;
    1721    17178974 :                 s32 dst_x = dst_pos.X + x0;
    1722    17178974 :                 s32 dst_y = dst_pos.Y + y0;
    1723    17178974 :                 video::SColor src_c = src->getPixel(src_x, src_y);
    1724    17178974 :                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
    1725    17178974 :                 dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
    1726    17178974 :                 dst->setPixel(dst_x, dst_y, dst_c);
    1727             :         }
    1728        1121 : }
    1729             : 
    1730             : /*
    1731             :         Draw an image on top of an another one, using the alpha channel of the
    1732             :         source image; only modify fully opaque pixels in destinaion
    1733             : */
    1734           0 : static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
    1735             :                 v2s32 src_pos, v2s32 dst_pos, v2u32 size)
    1736             : {
    1737           0 :         for (u32 y0=0; y0<size.Y; y0++)
    1738           0 :         for (u32 x0=0; x0<size.X; x0++)
    1739             :         {
    1740           0 :                 s32 src_x = src_pos.X + x0;
    1741           0 :                 s32 src_y = src_pos.Y + y0;
    1742           0 :                 s32 dst_x = dst_pos.X + x0;
    1743           0 :                 s32 dst_y = dst_pos.Y + y0;
    1744           0 :                 video::SColor src_c = src->getPixel(src_x, src_y);
    1745           0 :                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
    1746           0 :                 if (dst_c.getAlpha() == 255 && src_c.getAlpha() != 0)
    1747             :                 {
    1748           0 :                         dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
    1749           0 :                         dst->setPixel(dst_x, dst_y, dst_c);
    1750             :                 }
    1751             :         }
    1752           0 : }
    1753             : 
    1754             : /*
    1755             :         Draw an image on top of an another one, using the specified ratio
    1756             :         modify all partially-opaque pixels in the destination.
    1757             : */
    1758         200 : static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst,
    1759             :                 v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio)
    1760             : {
    1761        4696 :         for (u32 y0 = 0; y0 < size.Y; y0++)
    1762      153232 :         for (u32 x0 = 0; x0 < size.X; x0++)
    1763             :         {
    1764      148736 :                 s32 src_x = src_pos.X + x0;
    1765      148736 :                 s32 src_y = src_pos.Y + y0;
    1766      148736 :                 s32 dst_x = dst_pos.X + x0;
    1767      148736 :                 s32 dst_y = dst_pos.Y + y0;
    1768      148736 :                 video::SColor src_c = src->getPixel(src_x, src_y);
    1769      148736 :                 video::SColor dst_c = dst->getPixel(dst_x, dst_y);
    1770      148736 :                 if (dst_c.getAlpha() > 0 && src_c.getAlpha() != 0)
    1771             :                 {
    1772      116512 :                         if (ratio == -1)
    1773          60 :                                 dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
    1774             :                         else
    1775      116452 :                                 dst_c = src_c.getInterpolated(dst_c, (float)ratio/255.0f);
    1776      116512 :                         dst->setPixel(dst_x, dst_y, dst_c);
    1777             :                 }
    1778             :         }
    1779         200 : }
    1780             : 
    1781             : /*
    1782             :         Apply mask to destination
    1783             : */
    1784           0 : static void apply_mask(video::IImage *mask, video::IImage *dst,
    1785             :                 v2s32 mask_pos, v2s32 dst_pos, v2u32 size)
    1786             : {
    1787           0 :         for (u32 y0 = 0; y0 < size.Y; y0++) {
    1788           0 :                 for (u32 x0 = 0; x0 < size.X; x0++) {
    1789           0 :                         s32 mask_x = x0 + mask_pos.X;
    1790           0 :                         s32 mask_y = y0 + mask_pos.Y;
    1791           0 :                         s32 dst_x = x0 + dst_pos.X;
    1792           0 :                         s32 dst_y = y0 + dst_pos.Y;
    1793           0 :                         video::SColor mask_c = mask->getPixel(mask_x, mask_y);
    1794           0 :                         video::SColor dst_c = dst->getPixel(dst_x, dst_y);
    1795           0 :                         dst_c.color &= mask_c.color;
    1796           0 :                         dst->setPixel(dst_x, dst_y, dst_c);
    1797             :                 }
    1798             :         }
    1799           0 : }
    1800             : 
    1801           0 : static void draw_crack(video::IImage *crack, video::IImage *dst,
    1802             :                 bool use_overlay, s32 frame_count, s32 progression,
    1803             :                 video::IVideoDriver *driver)
    1804             : {
    1805             :         // Dimension of destination image
    1806           0 :         core::dimension2d<u32> dim_dst = dst->getDimension();
    1807             :         // Dimension of original image
    1808           0 :         core::dimension2d<u32> dim_crack = crack->getDimension();
    1809             :         // Count of crack stages
    1810           0 :         s32 crack_count = dim_crack.Height / dim_crack.Width;
    1811             :         // Limit frame_count
    1812           0 :         if (frame_count > (s32) dim_dst.Height)
    1813           0 :                 frame_count = dim_dst.Height;
    1814           0 :         if (frame_count < 1)
    1815           0 :                 frame_count = 1;
    1816             :         // Limit progression
    1817           0 :         if (progression > crack_count-1)
    1818           0 :                 progression = crack_count-1;
    1819             :         // Dimension of a single crack stage
    1820             :         core::dimension2d<u32> dim_crack_cropped(
    1821             :                 dim_crack.Width,
    1822             :                 dim_crack.Width
    1823           0 :         );
    1824             :         // Dimension of the scaled crack stage,
    1825             :         // which is the same as the dimension of a single destination frame
    1826             :         core::dimension2d<u32> dim_crack_scaled(
    1827             :                 dim_dst.Width,
    1828           0 :                 dim_dst.Height / frame_count
    1829           0 :         );
    1830             :         // Create cropped and scaled crack images
    1831             :         video::IImage *crack_cropped = driver->createImage(
    1832           0 :                         video::ECF_A8R8G8B8, dim_crack_cropped);
    1833             :         video::IImage *crack_scaled = driver->createImage(
    1834           0 :                         video::ECF_A8R8G8B8, dim_crack_scaled);
    1835             : 
    1836           0 :         if (crack_cropped && crack_scaled)
    1837             :         {
    1838             :                 // Crop crack image
    1839           0 :                 v2s32 pos_crack(0, progression*dim_crack.Width);
    1840           0 :                 crack->copyTo(crack_cropped,
    1841             :                                 v2s32(0,0),
    1842           0 :                                 core::rect<s32>(pos_crack, dim_crack_cropped));
    1843             :                 // Scale crack image by copying
    1844           0 :                 crack_cropped->copyToScaling(crack_scaled);
    1845             :                 // Copy or overlay crack image onto each frame
    1846           0 :                 for (s32 i = 0; i < frame_count; ++i)
    1847             :                 {
    1848           0 :                         v2s32 dst_pos(0, dim_crack_scaled.Height * i);
    1849           0 :                         if (use_overlay)
    1850             :                         {
    1851           0 :                                 blit_with_alpha_overlay(crack_scaled, dst,
    1852             :                                                 v2s32(0,0), dst_pos,
    1853           0 :                                                 dim_crack_scaled);
    1854             :                         }
    1855             :                         else
    1856             :                         {
    1857           0 :                                 blit_with_alpha(crack_scaled, dst,
    1858             :                                                 v2s32(0,0), dst_pos,
    1859           0 :                                                 dim_crack_scaled);
    1860             :                         }
    1861             :                 }
    1862             :         }
    1863             : 
    1864           0 :         if (crack_scaled)
    1865           0 :                 crack_scaled->drop();
    1866             : 
    1867           0 :         if (crack_cropped)
    1868           0 :                 crack_cropped->drop();
    1869           0 : }
    1870             : 
    1871          13 : void brighten(video::IImage *image)
    1872             : {
    1873          13 :         if (image == NULL)
    1874           0 :                 return;
    1875             : 
    1876          13 :         core::dimension2d<u32> dim = image->getDimension();
    1877             : 
    1878         221 :         for (u32 y=0; y<dim.Height; y++)
    1879        3536 :         for (u32 x=0; x<dim.Width; x++)
    1880             :         {
    1881        3328 :                 video::SColor c = image->getPixel(x,y);
    1882        3328 :                 c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
    1883        3328 :                 c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
    1884        3328 :                 c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
    1885        3328 :                 image->setPixel(x,y,c);
    1886             :         }
    1887             : }
    1888             : 
    1889         134 : u32 parseImageTransform(const std::string& s)
    1890             : {
    1891         134 :         int total_transform = 0;
    1892             : 
    1893         268 :         std::string transform_names[8];
    1894         134 :         transform_names[0] = "i";
    1895         134 :         transform_names[1] = "r90";
    1896         134 :         transform_names[2] = "r180";
    1897         134 :         transform_names[3] = "r270";
    1898         134 :         transform_names[4] = "fx";
    1899         134 :         transform_names[6] = "fy";
    1900             : 
    1901         134 :         std::size_t pos = 0;
    1902         402 :         while(pos < s.size())
    1903             :         {
    1904         134 :                 int transform = -1;
    1905         563 :                 for (int i = 0; i <= 7; ++i)
    1906             :                 {
    1907         563 :                         const std::string &name_i = transform_names[i];
    1908             : 
    1909         563 :                         if (s[pos] == ('0' + i))
    1910             :                         {
    1911           0 :                                 transform = i;
    1912           0 :                                 pos++;
    1913           0 :                                 break;
    1914             :                         }
    1915        2225 :                         else if (!(name_i.empty()) &&
    1916        2171 :                                 lowercase(s.substr(pos, name_i.size())) == name_i)
    1917             :                         {
    1918         134 :                                 transform = i;
    1919         134 :                                 pos += name_i.size();
    1920         134 :                                 break;
    1921             :                         }
    1922             :                 }
    1923         134 :                 if (transform < 0)
    1924           0 :                         break;
    1925             : 
    1926             :                 // Multiply total_transform and transform in the group D4
    1927         134 :                 int new_total = 0;
    1928         134 :                 if (transform < 4)
    1929          68 :                         new_total = (transform + total_transform) % 4;
    1930             :                 else
    1931          66 :                         new_total = (transform - total_transform + 8) % 4;
    1932         134 :                 if ((transform >= 4) ^ (total_transform >= 4))
    1933          66 :                         new_total += 4;
    1934             : 
    1935         134 :                 total_transform = new_total;
    1936             :         }
    1937         268 :         return total_transform;
    1938             : }
    1939             : 
    1940         134 : core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
    1941             : {
    1942         134 :         if (transform % 2 == 0)
    1943          81 :                 return dim;
    1944             :         else
    1945          53 :                 return core::dimension2d<u32>(dim.Height, dim.Width);
    1946             : }
    1947             : 
    1948         134 : void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
    1949             : {
    1950         134 :         if (src == NULL || dst == NULL)
    1951           0 :                 return;
    1952             : 
    1953         134 :         core::dimension2d<u32> dstdim = dst->getDimension();
    1954             : 
    1955             :         // Pre-conditions
    1956             :         assert(dstdim == imageTransformDimension(transform, src->getDimension()));
    1957             :         assert(transform <= 7);
    1958             : 
    1959             :         /*
    1960             :                 Compute the transformation from source coordinates (sx,sy)
    1961             :                 to destination coordinates (dx,dy).
    1962             :         */
    1963         134 :         int sxn = 0;
    1964         134 :         int syn = 2;
    1965         134 :         if (transform == 0)         // identity
    1966           0 :                 sxn = 0, syn = 2;  //   sx = dx, sy = dy
    1967         134 :         else if (transform == 1)    // rotate by 90 degrees ccw
    1968          39 :                 sxn = 3, syn = 0;  //   sx = (H-1) - dy, sy = dx
    1969          95 :         else if (transform == 2)    // rotate by 180 degrees
    1970          15 :                 sxn = 1, syn = 3;  //   sx = (W-1) - dx, sy = (H-1) - dy
    1971          80 :         else if (transform == 3)    // rotate by 270 degrees ccw
    1972          14 :                 sxn = 2, syn = 1;  //   sx = dy, sy = (W-1) - dx
    1973          66 :         else if (transform == 4)    // flip x
    1974          39 :                 sxn = 1, syn = 2;  //   sx = (W-1) - dx, sy = dy
    1975          27 :         else if (transform == 5)    // flip x then rotate by 90 degrees ccw
    1976           0 :                 sxn = 2, syn = 0;  //   sx = dy, sy = dx
    1977          27 :         else if (transform == 6)    // flip y
    1978          27 :                 sxn = 0, syn = 3;  //   sx = dx, sy = (H-1) - dy
    1979           0 :         else if (transform == 7)    // flip y then rotate by 90 degrees ccw
    1980           0 :                 sxn = 3, syn = 1;  //   sx = (H-1) - dy, sy = (W-1) - dx
    1981             : 
    1982        2614 :         for (u32 dy=0; dy<dstdim.Height; dy++)
    1983       71344 :         for (u32 dx=0; dx<dstdim.Width; dx++)
    1984             :         {
    1985       68864 :                 u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
    1986       68864 :                 u32 sx = entries[sxn];
    1987       68864 :                 u32 sy = entries[syn];
    1988       68864 :                 video::SColor c = src->getPixel(sx,sy);
    1989       68864 :                 dst->setPixel(dx,dy,c);
    1990             :         }
    1991             : }
    1992             : 
    1993       62340 : video::ITexture* TextureSource::getNormalTexture(const std::string &name)
    1994             : {
    1995             :         u32 id;
    1996       62340 :         if (isKnownSourceImage("override_normal.png"))
    1997           0 :                 return getTexture("override_normal.png", &id);
    1998      124680 :         std::string fname_base = name;
    1999      124680 :         std::string normal_ext = "_normal.png";
    2000       62340 :         size_t pos = fname_base.find(".");
    2001      124680 :         std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
    2002       62340 :         if (isKnownSourceImage(fname_normal)) {
    2003             :                 // look for image extension and replace it
    2004           0 :                 size_t i = 0;
    2005           0 :                 while ((i = fname_base.find(".", i)) != std::string::npos) {
    2006           0 :                         fname_base.replace(i, 4, normal_ext);
    2007           0 :                         i += normal_ext.length();
    2008             :                 }
    2009           0 :                 return getTexture(fname_base, &id);
    2010             :                 }
    2011       62340 :         return NULL;
    2012             : }
    2013             : 
    2014        5144 : video::SColor TextureSource::getTextureAverageColor(const std::string &name)
    2015             : {
    2016        5144 :         video::IVideoDriver *driver = m_device->getVideoDriver();
    2017        5144 :         video::SColor c(0, 0, 0, 0);
    2018             :         u32 id;
    2019        5144 :         video::ITexture *texture = getTexture(name, &id);
    2020       10288 :         video::IImage *image = driver->createImage(texture,
    2021             :                 core::position2d<s32>(0, 0),
    2022       10288 :                 texture->getOriginalSize());
    2023        5144 :         u32 total = 0;
    2024        5144 :         u32 tR = 0;
    2025        5144 :         u32 tG = 0;
    2026        5144 :         u32 tB = 0;
    2027        5144 :         core::dimension2d<u32> dim = image->getDimension();
    2028        5144 :         u16 step = 1;
    2029        5144 :         if (dim.Width > 16)
    2030         445 :                 step = dim.Width / 16;
    2031       86676 :         for (u16 x = 0; x < dim.Width; x += step) {
    2032     1385606 :                 for (u16 y = 0; y < dim.Width; y += step) {
    2033     1304074 :                         c = image->getPixel(x,y);
    2034     1304074 :                         if (c.getAlpha() > 0) {
    2035     1159976 :                                 total++;
    2036     1159976 :                                 tR += c.getRed();
    2037     1159976 :                                 tG += c.getGreen();
    2038     1159976 :                                 tB += c.getBlue();
    2039             :                         }
    2040             :                 }
    2041             :         }
    2042        5144 :         image->drop();
    2043        5144 :         if (total > 0) {
    2044        5118 :                 c.setRed(tR / total);
    2045        5118 :                 c.setGreen(tG / total);
    2046        5118 :                 c.setBlue(tB / total);
    2047             :         }
    2048        5144 :         c.setAlpha(255);
    2049        5144 :         return c;
    2050           3 : }

Generated by: LCOV version 1.11