1 /* GG is a GUI for OpenGL.
2    Copyright (C) 2003-2008 T. Zachary Laine
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public License
6    as published by the Free Software Foundation; either version 2.1
7    of the License, or (at your option) any later version.
8 
9    This library 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 GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA
18 
19    If you do not wish to comply with the terms of the LGPL please
20    contact the author as other terms are available for a fee.
21 
22    Zach Laine
23    whatwasthataddress@gmail.com */
24 
25 #include <GG/Texture.h>
26 
27 #include <GG/GLClientAndServerBuffer.h>
28 #include <GG/Config.h>
29 #include <GG/utf8/checked.h>
30 
31 #include <boost/filesystem/operations.hpp>
32 #include <boost/gil/extension/dynamic_image/any_image.hpp>
33 #if GG_HAVE_LIBTIFF
34 # include <boost/gil/extension/io/tiff_dynamic_io.hpp>
35 #endif
36 #include <boost/algorithm/string/case_conv.hpp>
37 
38 #if GG_HAVE_LIBPNG
39 # if GIGI_CONFIG_USE_OLD_IMPLEMENTATION_OF_GIL_PNG_IO
40 #  include "gilext/io/png_dynamic_io.hpp"
41 #  include "gilext/io/png_io_v2_compat.hpp"
42 # else
43 #  include <boost/gil/extension/io/png.hpp>
44 # endif
45 #endif
46 
47 #include <iostream>
48 #include <iomanip>
49 
50 #if BOOST_VERSION >= 107400
51 #include <boost/variant2/variant.hpp>
52 #elif BOOST_VERSION >= 107000
53 #include <boost/variant/get.hpp>
54 #endif
55 
56 
57 using namespace GG;
58 
59 namespace {
60     template <typename T>
PowerOfTwo(T input)61     T PowerOfTwo(T input)
62     {
63         T value(1);
64         while (value < input)
65             value *= 2;
66         return value;
67     }
68 }
69 
70 ///////////////////////////////////////
71 // class GG::Texture
72 ///////////////////////////////////////
Texture()73 Texture::Texture()
74 { Clear(); }
75 
~Texture()76 Texture::~Texture()
77 { Clear(); }
78 
Path() const79 const boost::filesystem::path& Texture::Path() const
80 { return m_path; }
81 
WrapS() const82 GLenum Texture::WrapS() const
83 { return m_wrap_s; }
84 
WrapT() const85 GLenum Texture::WrapT() const
86 { return m_wrap_t; }
87 
MinFilter() const88 GLenum Texture::MinFilter() const
89 { return m_min_filter; }
90 
MagFilter() const91 GLenum Texture::MagFilter() const
92 { return m_mag_filter; }
93 
BytesPP() const94 unsigned int Texture::BytesPP() const
95 { return m_bytes_pp; }
96 
Width() const97 X Texture::Width() const
98 { return m_width; }
99 
Height() const100 Y Texture::Height() const
101 { return m_height; }
102 
MipMapped() const103 bool Texture::MipMapped() const
104 { return m_mipmaps; }
105 
OpenGLId() const106 GLuint Texture::OpenGLId() const
107 { return m_opengl_id; }
108 
DefaultTexCoords() const109 const GLfloat* Texture::DefaultTexCoords() const
110 { return m_tex_coords; }
111 
DefaultWidth() const112 X Texture::DefaultWidth() const
113 { return m_default_width; }
114 
DefaultHeight() const115 Y Texture::DefaultHeight() const
116 { return m_default_height; }
117 
OrthoBlit(const Pt & pt1,const Pt & pt2,const GLfloat * tex_coords) const118 void Texture::OrthoBlit(const Pt& pt1, const Pt& pt2,
119                         const GLfloat* tex_coords/* = 0*/) const
120 {
121     if (m_opengl_id == 0)
122         return;
123 
124     if (!tex_coords) // use default texture coords when not given any others
125         tex_coords = m_tex_coords;
126 
127     // HACK! This code ensures that unscaled textures are reproduced exactly, even
128     // though they theoretically should be even when using non-GL_NEAREST* scaling.
129     bool render_scaled = (pt2.x - pt1.x) != m_default_width || (pt2.y - pt1.y) != m_default_height;
130     bool need_min_filter_change = !render_scaled && m_min_filter != GL_NEAREST;
131     bool need_mag_filter_change = !render_scaled && m_mag_filter != GL_NEAREST;
132     if (need_min_filter_change)
133         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
134     if (need_mag_filter_change)
135         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
136 
137     // render texture
138     GL2DVertexBuffer vertex_buffer;
139     vertex_buffer.reserve(4);
140     vertex_buffer.store(pt2.x, pt1.y);
141     vertex_buffer.store(pt1.x, pt1.y);
142     vertex_buffer.store(pt2.x, pt2.y);
143     vertex_buffer.store(pt1.x, pt2.y);
144 
145     GLTexCoordBuffer tex_coord_buffer;
146     tex_coord_buffer.reserve(4);
147     if (tex_coords) {
148         tex_coord_buffer.store(tex_coords[2], tex_coords[1]);
149         tex_coord_buffer.store(tex_coords[0], tex_coords[1]);
150         tex_coord_buffer.store(tex_coords[2], tex_coords[3]);
151         tex_coord_buffer.store(tex_coords[0], tex_coords[3]);
152     } else {
153         tex_coord_buffer.store(1.0f, 0.0f);
154         tex_coord_buffer.store(0.0f, 0.0f);
155         tex_coord_buffer.store(1.0f, 1.0f);
156         tex_coord_buffer.store(0.0f, 1.0f);
157     }
158 
159     glPushAttrib(GL_ENABLE_BIT);
160     glEnable(GL_TEXTURE_2D);
161 
162     glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
163     glEnableClientState(GL_VERTEX_ARRAY);
164     glDisableClientState(GL_COLOR_ARRAY);
165     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
166 
167     glBindTexture(GL_TEXTURE_2D, m_opengl_id);
168     vertex_buffer.activate();
169     tex_coord_buffer.activate();
170     glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_buffer.size());
171 
172     //glDisableClientState(GL_VERTEX_ARRAY);
173     //glDisableClientState(GL_TEXTURE_COORD_ARRAY);
174 
175     if (need_min_filter_change)
176         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_min_filter);
177     if (need_mag_filter_change)
178         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_mag_filter);
179 
180     glPopClientAttrib();
181 
182     glPopAttrib();
183 }
184 
OrthoBlit(const Pt & pt) const185 void Texture::OrthoBlit(const Pt& pt) const
186 { OrthoBlit(pt, pt + Pt(m_default_width, m_default_height), m_tex_coords); }
187 
Load(const boost::filesystem::path & path,bool mipmap)188 void Texture::Load(const boost::filesystem::path& path, bool mipmap/* = false*/)
189 {
190     namespace gil = boost::gil;
191     namespace fs = boost::filesystem;
192 
193     if (m_opengl_id)
194         Clear();
195 
196     if (!fs::exists(path)) {
197         std::cerr << "Texture::Load passed non-existant path: " << path.generic_string() << std::endl;
198         throw BadFile("Texture file \"" + path.generic_string() + "\" does not exist");
199     }
200     if (!fs::is_regular_file(path)) {
201         std::cerr << "Texture::Load passed non-file path: " << path.generic_string() << std::endl;
202         throw BadFile("Texture \"file\" \"" + path.generic_string() + "\" is not a file");
203     }
204 
205     // convert path into UTF-8 format filename string
206 #if defined (_WIN32)
207     boost::filesystem::path::string_type path_native = path.native();
208     std::string filename;
209     utf8::utf16to8(path_native.begin(), path_native.end(), std::back_inserter(filename));
210 #else
211     std::string filename = path.generic_string();
212 #endif
213 
214     static_assert(sizeof(gil::gray8_pixel_t) == 1, "gray8 pixel type does not match expected type size");
215     static_assert(sizeof(gil::gray_alpha8_pixel_t) == 2, "gray_alpha8 pixel type does not match expected type size");
216     static_assert(sizeof(gil::rgb8_pixel_t) == 3, "rgb8 pixel type does not match expected type size");
217     static_assert(sizeof(gil::rgba8_pixel_t) == 4, "rgba8 pixel type does not match expected type size");
218 
219 #if BOOST_VERSION >= 107400
220     typedef gil::any_image<gil::gray8_image_t,
221         gil::gray_alpha8_image_t,
222         gil::rgb8_image_t,
223         gil::rgba8_image_t> ImageType;
224 #else
225 # ifdef BOOST_GIL_USES_MP11
226     typedef boost::mp11::mp_list<
227 # else
228     typedef boost::mpl::vector4<
229 # endif
230         gil::gray8_image_t,
231         gil::gray_alpha8_image_t,
232         gil::rgb8_image_t,
233         gil::rgba8_image_t
234     > ImageTypes;
235     typedef gil::any_image<ImageTypes> ImageType;
236 #endif
237     if (!fs::exists(path))
238         throw BadFile("Texture file \"" + filename + "\" does not exist");
239     if (!fs::is_regular_file(path))
240         throw BadFile("Texture \"file\" \"" + filename + "\" is not a file");
241 
242     std::string extension = boost::algorithm::to_lower_copy(path.extension().string());
243 
244     ImageType image;
245     try {
246         // First attempt -- try just to read the file in one of the default
247         // formats above.
248 #if GG_HAVE_LIBPNG
249         if (extension == ".png")
250             gil::read_image(filename, image, gil::image_read_settings<gil::png_tag>());
251         else
252 #endif
253 #if GG_HAVE_LIBTIFF
254         if (extension == ".tif" || extension == ".tiff")
255             gil::read_image(filename, image, gil::image_read_settings<gil::tiff_tag>());
256         else
257 #endif
258             throw BadFile("Texture file \"" + filename + "\" does not have a supported file extension");
259     } catch (const std::ios_base::failure&) {
260         // Second attempt -- If *_read_image() throws, see if we can convert
261         // the image to RGBA.  This is needed for color-indexed images.
262 #if GG_HAVE_LIBPNG
263         if (extension == ".png") {
264             gil::rgba8_image_t rgba_image;
265             gil::read_and_convert_image(filename, rgba_image, gil::image_read_settings<gil::png_tag>());
266             image = std::move(rgba_image);
267         }
268 #endif
269 #if GG_HAVE_LIBTIFF
270         if (extension == ".tif" || extension == ".tiff") {
271             gil::rgba8_image_t rgba_image;
272             gil::read_and_convert_image(filename, rgba_image, gil::image_read_settings<gil::tiff_tag>());
273             image = std::move(rgba_image);
274         }
275 #endif
276     }
277 
278     m_path = path;
279     m_default_width = X(image.width());
280     m_default_height = Y(image.height());
281     m_type = GL_UNSIGNED_BYTE;
282 
283 #if BOOST_VERSION >= 107400
284 #define IF_IMAGE_TYPE_IS(image_prefix)                                  \
285     if (boost::variant2::get_if<image_prefix ## _image_t>(&image)) {    \
286         m_bytes_pp = sizeof(image_prefix ## _pixel_t);                  \
287         image_data = interleaved_view_get_raw_data(                     \
288             const_view(boost::variant2::get<image_prefix ## _image_t>(image))); \
289     }
290 #elif BOOST_VERSION >= 107000
291 #define IF_IMAGE_TYPE_IS(image_prefix)                                  \
292     if (boost::get<image_prefix ## _image_t>(&image)) {                 \
293         m_bytes_pp = sizeof(image_prefix ## _pixel_t);                  \
294         image_data = interleaved_view_get_raw_data(                     \
295             const_view(boost::get<image_prefix ## _image_t>(image)));   \
296     }
297 #else
298 #define IF_IMAGE_TYPE_IS(image_prefix)                                  \
299     if (image.current_type_is<image_prefix ## _image_t>()) {            \
300         m_bytes_pp = sizeof(image_prefix ## _pixel_t);                  \
301         image_data = interleaved_view_get_raw_data(                     \
302             const_view(image._dynamic_cast<image_prefix ## _image_t>())); \
303     }
304 #endif
305 
306     const unsigned char* image_data = nullptr;
307 
308     IF_IMAGE_TYPE_IS(gil::gray8)
309     else IF_IMAGE_TYPE_IS(gil::gray_alpha8)
310     else IF_IMAGE_TYPE_IS(gil::rgb8)
311     else IF_IMAGE_TYPE_IS(gil::rgba8)
312 
313 #undef IF_IMAGE_TYPE_IS
314 
315     switch (m_bytes_pp) {
316     case 1:  m_format = GL_LUMINANCE; break;
317     case 2:  m_format = GL_LUMINANCE_ALPHA; break;
318     case 3:  m_format = GL_RGB; break;
319     case 4:  m_format = GL_RGBA; break;
320     default: throw BadFile("Texture file \"" + filename + "\" does not have a supported number of color channels (1-4)");
321     }
322 
323     assert(image_data);
324     Init(m_default_width, m_default_height, image_data, m_format, m_type, m_bytes_pp, mipmap);
325 }
326 
Init(X width,Y height,const unsigned char * image,GLenum format,GLenum type,unsigned int bytes_per_pixel,bool mipmap)327 void Texture::Init(X width, Y height, const unsigned char* image, GLenum format, GLenum type,
328                    unsigned int bytes_per_pixel, bool mipmap/* = false*/)
329 {
330     glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
331     glPixelStorei(GL_UNPACK_SWAP_BYTES, false);
332     glPixelStorei(GL_UNPACK_LSB_FIRST, false);
333     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
334     glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
335     glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
336     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
337 
338     try {
339         InitFromRawData(width, height, image, format, type, bytes_per_pixel, mipmap);
340     } catch (...) {
341         glPopClientAttrib();
342         throw;
343     }
344 
345     glPopClientAttrib();
346 }
347 
SetFilters(GLenum min,GLenum mag)348 void Texture::SetFilters(GLenum min, GLenum mag)
349 {
350     m_min_filter = min;
351     m_mag_filter = mag;
352     if (m_opengl_id) {
353         glBindTexture(GL_TEXTURE_2D, m_opengl_id);
354         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_min_filter);
355         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_mag_filter);
356     }
357 }
358 
Clear()359 void Texture::Clear()
360 {
361     if (m_opengl_id)
362         glDeleteTextures(1, &m_opengl_id);
363 
364     m_path.clear();
365 
366     m_bytes_pp = 4;
367     m_default_width = m_width = X0;
368     m_default_height = m_height = Y0;
369 
370     m_wrap_s = m_wrap_t = GL_REPEAT;
371     m_min_filter = GL_LINEAR_MIPMAP_LINEAR;
372     m_mag_filter = GL_LINEAR;
373 
374     m_mipmaps = false;
375     m_opengl_id = 0;
376     m_format = GL_INVALID_ENUM;
377     m_type = GL_INVALID_ENUM;
378 
379     m_tex_coords[0] = m_tex_coords[1] = 0.0f;   // min x, y
380     m_tex_coords[2] = m_tex_coords[3] = 1.0f;   // max x, y
381 }
382 
InitFromRawData(X width,Y height,const unsigned char * image,GLenum format,GLenum type,unsigned int bytes_per_pixel,bool mipmap)383 void Texture::InitFromRawData(X width, Y height, const unsigned char* image, GLenum format, GLenum type,
384                               unsigned int bytes_per_pixel, bool mipmap)
385 {
386     if (!image)
387         return;
388 
389     if (m_opengl_id)
390         Clear();
391 
392     X GL_texture_width = PowerOfTwo(width);
393     Y GL_texture_height = PowerOfTwo(height);
394 
395     glGenTextures(1, &m_opengl_id);
396     glBindTexture(GL_TEXTURE_2D, m_opengl_id);
397     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
398     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_min_filter);
399     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_mag_filter);
400     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrap_s);
401     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrap_t);
402 
403     if (mipmap) {
404         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
405     } else {
406         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
407         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
408     }
409 
410     glTexImage2D(GL_PROXY_TEXTURE_2D, 0, format, Value(GL_texture_width), Value(GL_texture_height), 0, format, type, nullptr);
411     GLint checked_format;
412     glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &checked_format);
413     if (!checked_format)
414         throw InsufficientResources("Insufficient resources to create requested OpenGL texture");
415     bool image_is_power_of_two = width == GL_texture_width && height == GL_texture_height;
416     if (image_is_power_of_two) {
417         glTexImage2D(GL_TEXTURE_2D, 0, format, Value(width), Value(height), 0, format, type, image);
418     } else {
419         std::vector<unsigned char> zero_data(bytes_per_pixel * Value(GL_texture_width) * Value(GL_texture_height));
420         glTexImage2D(GL_TEXTURE_2D, 0, format, Value(GL_texture_width), Value(GL_texture_height), 0, format, type, &zero_data[0]);
421         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Value(width), Value(height), format, type, image);
422     }
423 
424     m_mipmaps = mipmap;
425     m_default_width = width;
426     m_default_height = height;
427     m_bytes_pp = bytes_per_pixel;
428     {
429         GLint w, h;
430         glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
431         glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
432         m_width = X(w);
433         m_height = Y(h);
434     }
435     m_tex_coords[2] = Value(1.0 * m_default_width / m_width);
436     m_tex_coords[3] = Value(1.0 * m_default_height / m_height);
437 }
438 
GetRawBytes()439 unsigned char* Texture::GetRawBytes()
440 {
441     unsigned char* retval = nullptr;
442     glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
443     glPixelStorei(GL_PACK_SWAP_BYTES, false);
444     glPixelStorei(GL_PACK_LSB_FIRST, false);
445     glPixelStorei(GL_PACK_ROW_LENGTH, 0);
446     glPixelStorei(GL_PACK_SKIP_ROWS, 0);
447     glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
448     glPixelStorei(GL_PACK_ALIGNMENT, 1);
449 
450     // get pixel data
451     typedef unsigned char uchar;
452     retval = new uchar[Value(m_width) * Value(m_height) * m_bytes_pp];
453     glGetTexImage(GL_TEXTURE_2D, 0, m_format, m_type, retval);
454     glPopClientAttrib();
455     return retval;
456 }
457 
458 
459 ///////////////////////////////////////
460 // class GG::SubTexture
461 ///////////////////////////////////////
SubTexture()462 SubTexture::SubTexture() :
463     m_width(0),
464     m_height(0),
465     m_tex_coords()
466 {}
467 
SubTexture(const std::shared_ptr<const Texture> & texture,X x1,Y y1,X x2,Y y2)468 SubTexture::SubTexture(const std::shared_ptr<const Texture>& texture, X x1, Y y1, X x2, Y y2) :
469     m_texture(texture),
470     m_width(x2 - x1),
471     m_height(y2 - y1),
472     m_tex_coords()
473 {
474     if (!m_texture) throw BadTexture("Attempted to contruct subtexture from invalid texture");
475     if (x2 < x1 || y2 < y1) throw InvalidTextureCoordinates("Attempted to contruct subtexture from invalid coordinates");
476 
477     m_tex_coords[0] = Value(x1 * 1.0 / texture->Width());
478     m_tex_coords[1] = Value(y1 * 1.0 / texture->Height());
479     m_tex_coords[2] = Value(x2 * 1.0 / texture->Width());
480     m_tex_coords[3] = Value(y2 * 1.0 / texture->Height());
481 }
482 
SubTexture(const std::shared_ptr<const Texture> & texture)483 SubTexture::SubTexture(const std::shared_ptr<const Texture>& texture) :
484     m_texture(texture),
485     m_width(GG::X1),
486     m_height(GG::Y1),
487     m_tex_coords()
488 {
489     if (!m_texture) throw BadTexture("Attempted to contruct subtexture from invalid texture");
490 
491     m_width = texture->Width();
492     m_height = texture->Height();
493 
494     m_tex_coords[0] = 0.0f;
495     m_tex_coords[1] = 0.0f;
496     m_tex_coords[2] = 1.0f;
497     m_tex_coords[3] = 1.0f;
498 }
499 
~SubTexture()500 SubTexture::~SubTexture()
501 {}
502 
SubTexture(const SubTexture & rhs)503 SubTexture::SubTexture(const SubTexture& rhs)
504 { *this = rhs; }
505 
operator =(const SubTexture & rhs)506 const SubTexture& SubTexture::operator=(const SubTexture& rhs)
507 {
508     if (this != &rhs) {
509         m_texture = rhs.m_texture;
510         m_width = rhs.m_width;
511         m_height = rhs.m_height;
512         m_tex_coords[0] = rhs.m_tex_coords[0];
513         m_tex_coords[1] = rhs.m_tex_coords[1];
514         m_tex_coords[2] = rhs.m_tex_coords[2];
515         m_tex_coords[3] = rhs.m_tex_coords[3];
516     }
517     return *this;
518 }
519 
Empty() const520 bool SubTexture::Empty() const
521 { return !m_texture; }
522 
TexCoords() const523 const GLfloat* SubTexture::TexCoords() const
524 { return m_tex_coords; }
525 
Width() const526 X SubTexture::Width() const
527 { return m_width; }
528 
Height() const529 Y SubTexture::Height() const
530 { return m_height; }
531 
GetTexture() const532 const Texture* SubTexture::GetTexture() const
533 { return m_texture.get(); }
534 
OrthoBlit(const Pt & pt1,const Pt & pt2) const535 void SubTexture::OrthoBlit(const Pt& pt1, const Pt& pt2) const
536 { if (m_texture) m_texture->OrthoBlit(pt1, pt2, m_tex_coords); }
537 
OrthoBlit(const Pt & pt) const538 void SubTexture::OrthoBlit(const Pt& pt) const
539 { if (m_texture) m_texture->OrthoBlit(pt, pt + Pt(m_width, m_height), m_tex_coords); }
540 
Clear()541 void SubTexture::Clear()
542 {
543     m_texture.reset();
544     m_width = X0;
545     m_height = Y0;
546     m_tex_coords[0] = 0.0f;
547     m_tex_coords[1] = 0.0f;
548     m_tex_coords[2] = 1.0f;
549     m_tex_coords[3] = 1.0f;
550 }
551 
552 ///////////////////////////////////////
553 // class GG::TextureManager
554 ///////////////////////////////////////
TextureManager()555 TextureManager::TextureManager()
556 {}
557 
Textures() const558 const std::map<std::string, std::shared_ptr<Texture>>& TextureManager::Textures() const
559 { return m_textures; }
560 
StoreTexture(Texture * texture,const std::string & texture_name)561 std::shared_ptr<Texture> TextureManager::StoreTexture(Texture* texture, const std::string& texture_name)
562 {
563     std::shared_ptr<Texture> temp(texture);
564     return StoreTexture(temp, texture_name);
565 }
566 
StoreTexture(const std::shared_ptr<Texture> & texture,const std::string & texture_name)567 std::shared_ptr<Texture> TextureManager::StoreTexture(const std::shared_ptr<Texture>& texture, const std::string& texture_name)
568 { return (m_textures[texture_name] = texture); }
569 
GetTexture(const boost::filesystem::path & path,bool mipmap)570 std::shared_ptr<Texture> TextureManager::GetTexture(const boost::filesystem::path& path, bool mipmap/* = false*/)
571 {
572     std::map<std::string, std::shared_ptr<Texture>>::iterator it = m_textures.find(path.generic_string());
573     if (it == m_textures.end()) { // if no such texture was found, attempt to load it now, using name as the filename
574         //std::cout << "TextureManager::GetTexture storing new texture under name: " << path.generic_string();
575         return (m_textures[path.generic_string()] = LoadTexture(path, mipmap));
576     } else { // otherwise, just return the texture we found
577         return it->second;
578     }
579 }
580 
FreeTexture(const boost::filesystem::path & path)581 void TextureManager::FreeTexture(const boost::filesystem::path& path)
582 { FreeTexture(path.generic_string()); }
583 
FreeTexture(const std::string & name)584 void TextureManager::FreeTexture(const std::string& name)
585 {
586     std::map<std::string, std::shared_ptr<Texture>>::iterator it = m_textures.find(name);
587     if (it != m_textures.end())
588         m_textures.erase(it);
589 }
590 
LoadTexture(const boost::filesystem::path & path,bool mipmap)591 std::shared_ptr<Texture> TextureManager::LoadTexture(const boost::filesystem::path& path, bool mipmap)
592 {
593     auto temp = std::make_shared<Texture>();
594     temp->Load(path, mipmap);
595     return (m_textures[path.generic_string()] = temp);
596 }
597 
GetTextureManager()598 TextureManager& GG::GetTextureManager()
599 {
600     static TextureManager manager;
601     return manager;
602 }
603