]> git.lizzy.rs Git - minetest.git/blob - src/client/guiscalingfilter.cpp
Don't ignore server disconnects in client code
[minetest.git] / src / client / guiscalingfilter.cpp
1 /*
2 Copyright (C) 2015 Aaron Suen <warr1024@gmail.com>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include "guiscalingfilter.h"
20 #include "imagefilters.h"
21 #include "porting.h"
22 #include "settings.h"
23 #include "util/numeric.h"
24 #include <cstdio>
25 #include "client/renderingengine.h"
26
27 /* Maintain a static cache to store the images that correspond to textures
28  * in a format that's manipulable by code.  Some platforms exhibit issues
29  * converting textures back into images repeatedly, and some don't even
30  * allow it at all.
31  */
32 std::map<io::path, video::IImage *> g_imgCache;
33
34 /* Maintain a static cache of all pre-scaled textures.  These need to be
35  * cleared as well when the cached images.
36  */
37 std::map<io::path, video::ITexture *> g_txrCache;
38
39 /* Manually insert an image into the cache, useful to avoid texture-to-image
40  * conversion whenever we can intercept it.
41  */
42 void guiScalingCache(const io::path &key, video::IVideoDriver *driver, video::IImage *value)
43 {
44         if (!g_settings->getBool("gui_scaling_filter"))
45                 return;
46
47         if (g_imgCache.find(key) != g_imgCache.end())
48                 return; // Already cached.
49
50         video::IImage *copied = driver->createImage(value->getColorFormat(),
51                         value->getDimension());
52         value->copyTo(copied);
53         g_imgCache[key] = copied;
54 }
55
56 // Manually clear the cache, e.g. when switching to different worlds.
57 void guiScalingCacheClear()
58 {
59         for (auto &it : g_imgCache) {
60                 if (it.second)
61                         it.second->drop();
62         }
63         g_imgCache.clear();
64         for (auto &it : g_txrCache) {
65                 if (it.second)
66                         RenderingEngine::get_video_driver()->removeTexture(it.second);
67         }
68         g_txrCache.clear();
69 }
70
71 /* Get a cached, high-quality pre-scaled texture for display purposes.  If the
72  * texture is not already cached, attempt to create it.  Returns a pre-scaled texture,
73  * or the original texture if unable to pre-scale it.
74  */
75 video::ITexture *guiScalingResizeCached(video::IVideoDriver *driver,
76                 video::ITexture *src, const core::rect<s32> &srcrect,
77                 const core::rect<s32> &destrect)
78 {
79         if (src == NULL)
80                 return src;
81         if (!g_settings->getBool("gui_scaling_filter"))
82                 return src;
83
84         // Calculate scaled texture name.
85         char rectstr[200];
86         porting::mt_snprintf(rectstr, sizeof(rectstr), "%d:%d:%d:%d:%d:%d",
87                 srcrect.UpperLeftCorner.X,
88                 srcrect.UpperLeftCorner.Y,
89                 srcrect.getWidth(),
90                 srcrect.getHeight(),
91                 destrect.getWidth(),
92                 destrect.getHeight());
93         io::path origname = src->getName().getPath();
94         io::path scalename = origname + "@guiScalingFilter:" + rectstr;
95
96         // Search for existing scaled texture.
97         auto it_txr = g_txrCache.find(scalename);
98         video::ITexture *scaled = (it_txr != g_txrCache.end()) ? it_txr->second : nullptr;
99         if (scaled)
100                 return scaled;
101
102         // Try to find the texture converted to an image in the cache.
103         // If the image was not found, try to extract it from the texture.
104         auto it_img = g_imgCache.find(origname);
105         video::IImage *srcimg = (it_img != g_imgCache.end()) ? it_img->second : nullptr;
106         if (!srcimg) {
107                 if (!g_settings->getBool("gui_scaling_filter_txr2img"))
108                         return src;
109                 srcimg = driver->createImageFromData(src->getColorFormat(),
110                         src->getSize(), src->lock(video::ETLM_READ_ONLY), false);
111                 src->unlock();
112                 g_imgCache[origname] = srcimg;
113         }
114
115         // Create a new destination image and scale the source into it.
116         imageCleanTransparent(srcimg, 0);
117         video::IImage *destimg = driver->createImage(src->getColorFormat(),
118                         core::dimension2d<u32>((u32)destrect.getWidth(),
119                         (u32)destrect.getHeight()));
120         imageScaleNNAA(srcimg, srcrect, destimg);
121
122 #if ENABLE_GLES
123         // Some platforms are picky about textures being powers of 2, so expand
124         // the image dimensions to the next power of 2, if necessary.
125         if (!driver->queryFeature(video::EVDF_TEXTURE_NPOT)) {
126                 video::IImage *po2img = driver->createImage(src->getColorFormat(),
127                                 core::dimension2d<u32>(npot2((u32)destrect.getWidth()),
128                                 npot2((u32)destrect.getHeight())));
129                 po2img->fill(video::SColor(0, 0, 0, 0));
130                 destimg->copyTo(po2img);
131                 destimg->drop();
132                 destimg = po2img;
133         }
134 #endif
135
136         // Convert the scaled image back into a texture.
137         scaled = driver->addTexture(scalename, destimg);
138         destimg->drop();
139         g_txrCache[scalename] = scaled;
140
141         return scaled;
142 }
143
144 /* Convenience wrapper for guiScalingResizeCached that accepts parameters that
145  * are available at GUI imagebutton creation time.
146  */
147 video::ITexture *guiScalingImageButton(video::IVideoDriver *driver,
148                 video::ITexture *src, s32 width, s32 height)
149 {
150         if (src == NULL)
151                 return src;
152         return guiScalingResizeCached(driver, src,
153                 core::rect<s32>(0, 0, src->getSize().Width, src->getSize().Height),
154                 core::rect<s32>(0, 0, width, height));
155 }
156
157 /* Replacement for driver->draw2DImage() that uses the high-quality pre-scaled
158  * texture, if configured.
159  */
160 void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr,
161                 const core::rect<s32> &destrect, const core::rect<s32> &srcrect,
162                 const core::rect<s32> *cliprect, const video::SColor *const colors,
163                 bool usealpha)
164 {
165         // Attempt to pre-scale image in software in high quality.
166         video::ITexture *scaled = guiScalingResizeCached(driver, txr, srcrect, destrect);
167         if (scaled == NULL)
168                 return;
169
170         // Correct source rect based on scaled image.
171         const core::rect<s32> mysrcrect = (scaled != txr)
172                 ? core::rect<s32>(0, 0, destrect.getWidth(), destrect.getHeight())
173                 : srcrect;
174
175         driver->draw2DImage(scaled, destrect, mysrcrect, cliprect, colors, usealpha);
176 }
177
178 void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture,
179                 const core::rect<s32> &rect, const core::rect<s32> &middle,
180                 const core::rect<s32> *cliprect, const video::SColor *const colors)
181 {
182         auto originalSize = texture->getOriginalSize();
183         core::vector2di lowerRightOffset = core::vector2di(originalSize.Width, originalSize.Height) - middle.LowerRightCorner;
184
185         for (int y = 0; y < 3; ++y) {
186                 for (int x = 0; x < 3; ++x) {
187                         core::rect<s32> src({0, 0}, originalSize);
188                         core::rect<s32> dest = rect;
189
190                         switch (x) {
191                         case 0:
192                                 dest.LowerRightCorner.X = rect.UpperLeftCorner.X + middle.UpperLeftCorner.X;
193                                 src.LowerRightCorner.X = middle.UpperLeftCorner.X;
194                                 break;
195
196                         case 1:
197                                 dest.UpperLeftCorner.X += middle.UpperLeftCorner.X;
198                                 dest.LowerRightCorner.X -= lowerRightOffset.X;
199                                 src.UpperLeftCorner.X = middle.UpperLeftCorner.X;
200                                 src.LowerRightCorner.X = middle.LowerRightCorner.X;
201                                 break;
202
203                         case 2:
204                                 dest.UpperLeftCorner.X = rect.LowerRightCorner.X - lowerRightOffset.X;
205                                 src.UpperLeftCorner.X = middle.LowerRightCorner.X;
206                                 break;
207                         }
208
209                         switch (y) {
210                         case 0:
211                                 dest.LowerRightCorner.Y = rect.UpperLeftCorner.Y + middle.UpperLeftCorner.Y;
212                                 src.LowerRightCorner.Y = middle.UpperLeftCorner.Y;
213                                 break;
214
215                         case 1:
216                                 dest.UpperLeftCorner.Y += middle.UpperLeftCorner.Y;
217                                 dest.LowerRightCorner.Y -= lowerRightOffset.Y;
218                                 src.UpperLeftCorner.Y = middle.UpperLeftCorner.Y;
219                                 src.LowerRightCorner.Y = middle.LowerRightCorner.Y;
220                                 break;
221
222                         case 2:
223                                 dest.UpperLeftCorner.Y = rect.LowerRightCorner.Y - lowerRightOffset.Y;
224                                 src.UpperLeftCorner.Y = middle.LowerRightCorner.Y;
225                                 break;
226                         }
227
228                         draw2DImageFilterScaled(driver, texture, dest, src, cliprect, colors, true);
229                 }
230         }
231 }