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