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