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