1 // -*- C++ -*-
2 /* GG is a GUI for OpenGL.
3    Copyright (C) 2003-2008 T. Zachary Laine
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public License
7    as published by the Free Software Foundation; either version 2.1
8    of the License, or (at your option) any later version.
9 
10    This library 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 GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA
19 
20    If you do not wish to comply with the terms of the LGPL please
21    contact the author as other terms are available for a fee.
22 
23    Zach Laine
24    whatwasthataddress@gmail.com */
25 
26 /** \file Texture.h \brief Contains the Texture class, which encapsulates an
27     OpenGL texture object; the SubTexture class, which represents a portion of
28     an OpenGL texture object; and the TextureManager class, which provides
29     GUI-wide management of Texture objects. */
30 
31 #ifndef _GG_Texture_h_
32 #define _GG_Texture_h_
33 
34 #include <GG/Base.h>
35 #include <GG/Exception.h>
36 
37 #include <boost/filesystem/path.hpp>
38 
39 namespace GG {
40 
41 /** \brief This class encapsulates an OpenGL texture object.
42 
43     If the dimensions of the image used to initialize the texture are not both
44     powers of two, the texture is created with dimensions of the next largest
45     (or equal) powers of two.  The original image occupies the region near the
46     texture's origin, and the rest is zero-initialized.  This is done to
47     prevent the image from being scaled, since textures used in a GUI almost
48     always must maintain pixel accuracy.  The original image size and
49     corresponding texture coords are saved, and can be accessed through
50     DefaultWidth(), DefaultHeight(), and DefaultTexCoords(), respectively.
51     These are kept so that only the originally-loaded-image part of the
52     texture can be used, if desired.  All initialization functions first free
53     the OpenGL texture currently in use by the texture (if any) and create a
54     new one.  When the load filename is "" or the image parameter is 0, all
55     initialization functions fail silently, performing no initialization and
56     allocating no memory or OpenGL texture.  Serialized Textures save the
57     filename associated with the texture when available, so the originally
58     loaded file can be reloaded again later.  If no such file exists, such as
59     when a Texture is created from in-memory image data, the contents of the
60     Texture are read from video memory and saved as binary data.  A
61     default-constructed Texture will have niether a filename nor raw image
62     data. */
63 class GG_API Texture
64 {
65 public:
66     /** \name Structors */ ///@{
67     Texture();
68 
69     virtual ~Texture();
70     //@}
71 
72     /** \name Accessors */ ///@{
73     const boost::filesystem::path&  Path() const;   ///< returns the file path from which this texture was loaded (default / empty if this texture was not loaded from a file)
74 
75     GLenum           WrapS() const;             ///< returns S-wrap mode associated with this opengl texture
76     GLenum           WrapT() const;             ///< returns T-wrap mode associated with this opengl texture
77     GLenum           MinFilter() const;         ///< returns minimization filter modes associated with this opengl texture
78     GLenum           MagFilter() const;         ///< returns maximization filter modes associated with this opengl texture
79     unsigned int     BytesPP() const;           ///< returns the image's color depth in bytes
80     X                Width() const;             ///< returns width of entire texture
81     Y                Height() const;            ///< returns height of entire texture
82     bool             MipMapped() const;         ///< returns true if the texture has mipmaps
83     GLuint           OpenGLId() const;          ///< GLuint "name" of the opengl texture object associated with this object
84     const GLfloat*   DefaultTexCoords() const;  ///< texture coordinates to use by default when blitting this texture
85     X                DefaultWidth() const;      ///< returns width in pixels, based on initial image (0 if texture was not loaded)
86     Y                DefaultHeight() const;     ///< returns height in pixels, based on initial image (0 if texture was not loaded)
87 
88     /** Blit any portion of texture to any place on screen, scaling as
89         necessary*/
90     void OrthoBlit(const Pt& pt1, const Pt& pt2, const GLfloat* tex_coords = nullptr) const;
91 
92     /** Blit default portion of texture unscaled to \a pt (upper left
93         corner)*/
94     void OrthoBlit(const Pt& pt) const;
95     //@}
96 
97     /** \name Mutators */ ///@{
98     // intialization functions
99     /** Frees any currently-held memory and loads a texture from file \a
100         path.  \throw GG::Texture::BadFile Throws if the texture creation
101         fails. */
102     void Load(const boost::filesystem::path& path, bool mipmap = false);
103 
104     /** Frees any currently-held memory and creates a texture from supplied
105         array \a image.  \throw GG::Texture::Exception Throws applicable
106         subclass if the texture creation fails in one of the specified
107         ways. */
108     void Init(X width, Y height, const unsigned char* image, GLenum format, GLenum type,
109               unsigned bytes_per_pixel, bool mipmap = false);
110 
111     void SetFilters(GLenum min, GLenum mag);  ///< sets the opengl min/mag filter modes associated with opengl texture m_opengl_id
112     void Clear();  ///< frees the opengl texture object associated with this object
113     //@}
114 
115     /** \name Exceptions */ ///@{
116     /** The base class for Texture exceptions. */
117     GG_ABSTRACT_EXCEPTION(Exception);
118 
119     /** Thrown when valid texture data cannot be read from a file. */
120     GG_CONCRETE_EXCEPTION(BadFile, GG::Texture, Exception);
121 
122     /** Thrown when an unsupported number of color channels is requested. */
123     GG_CONCRETE_EXCEPTION(InvalidColorChannels, GG::Texture, Exception);
124 
125     /** Thrown when GL fails to provide a requested texture object. */
126     GG_CONCRETE_EXCEPTION(InsufficientResources, GG::Texture, Exception);
127     //@}
128 
129 private:
130     Texture(const Texture& rhs);             ///< disabled
131     Texture& operator=(const Texture& rhs);  ///< disabled
132     void InitFromRawData(X width, Y height, const unsigned char* image, GLenum format, GLenum type,
133                          unsigned int bytes_per_pixel, bool mipmap);
134     unsigned char* GetRawBytes();
135 
136     boost::filesystem::path m_path;     ///< file path from which this Texture was constructed
137 
138     unsigned int m_bytes_pp = 0;
139     X            m_width = GG::X0;
140     Y            m_height = GG::Y0;
141 
142     GLenum       m_wrap_s = GL_REPEAT;
143     GLenum       m_wrap_t = GL_REPEAT;
144     GLenum       m_min_filter = GL_LINEAR_MIPMAP_LINEAR;
145     GLenum       m_mag_filter = GL_LINEAR;
146 
147     bool         m_mipmaps = false;
148     GLuint       m_opengl_id = 0;   ///< OpenGL texture ID
149     GLenum       m_format = GL_INVALID_ENUM;
150     GLenum       m_type = GL_INVALID_ENUM;
151 
152     /// each of these is used for a non-power-of-two-sized graphic loaded into a power-of-two-sized texture
153     GLfloat      m_tex_coords[4] = {0.0f, 0.0f, 0.0f, 0.0f};    ///< the texture coords used to blit from this texture by default (reflecting original image width and height)
154     X            m_default_width = GG::X0;                      ///< the original width and height of this texture to be used in blitting
155     Y            m_default_height = GG::Y0;
156 };
157 
158 /** \brief This class is a convenient way to store the info needed to use a
159     portion of an OpenGL texture. */
160 class GG_API SubTexture
161 {
162 public:
163     /** \name Structors */ ///@{
164     SubTexture();
165 
166     /** Creates a SubTexture from a GG::Texture and coordinates into it.
167         \throw GG::SubTexture::BadTexture Throws if the given Texture is null.
168         \throw GG::SubTexture::InvalidTextureCoordinates Throws if the texture
169         coordinates are not well formed.*/
170     SubTexture(const std::shared_ptr<const Texture>& texture, X x1, Y y1, X x2, Y y2);
171 
172     /** Creates a SubTexture from a GG::Texture and uses coordinates to cover
173         the whole texture.
174         \throw GG::SubTexture::BadTexture Throws if the given Texture is null.*/
175     SubTexture(const std::shared_ptr<const Texture>& texture);
176 
177     SubTexture(const SubTexture& rhs);
178 
179     const SubTexture& operator=(const SubTexture& rhs);
180 
181     virtual ~SubTexture();
182     //@}
183 
184     /** \name Accessors */ ///@{
185     bool             Empty() const;     ///< returns true if this object has no associated GG::Texture
186     const GLfloat*   TexCoords() const; ///< texture coordinates to use when blitting this sub-texture
187     X                Width() const;     ///< width of sub-texture in pixels
188     Y                Height() const;    ///< height of sub-texture in pixels
189     const Texture*   GetTexture() const;///< returns the texture the SubTexture is a part of
190 
191     /** Blit sub-texture to any place on screen, scaling as necessary \see
192         GG::Texture::OrthoBlit*/
193     void OrthoBlit(const Pt& pt1, const Pt& pt2) const;
194 
195     /** Blit sub-texture unscaled to \a pt (upper left corner) \see
196         GG::Texture::OrthoBlit*/
197     void OrthoBlit(const Pt& pt) const;
198     //@}
199 
200     /** \name Mutators */ ///@{
201     void Clear();
202     //@}
203 
204     /** \name Exceptions */ ///@{
205     /** The base class for SubTexture exceptions. */
206     GG_ABSTRACT_EXCEPTION(Exception);
207 
208     /** Thrown when an attempt is made to create a SubTexture using a null
209         texture. */
210     GG_CONCRETE_EXCEPTION(BadTexture, GG::SubTexture, Exception);
211 
212     /** Thrown when invalid or out-of-order texture coordinates are
213         supplied.*/
214     GG_CONCRETE_EXCEPTION(InvalidTextureCoordinates, GG::SubTexture, Exception);
215     //@}
216 
217 private:
218     /** shared_ptr to texture object with entire image. */
219     std::shared_ptr<const Texture>  m_texture;
220     X                               m_width;
221     Y                               m_height;
222     GLfloat                         m_tex_coords[4];    ///< position of element within containing texture
223 };
224 
225 /** \brief A singleton that loads and stores textures for use by GG.
226 
227     This class is essentially a very thin wrapper around a map of Texture
228     smart pointers, keyed on std::string texture names.  The user need only
229     request a texture through GetTexture(); if the texture is not already
230     resident, it will be loaded.  If the user would like to create her own
231     images and store them in the manager, that can be accomplished via
232     StoreTexture() calls.*/
233 class GG_API TextureManager
234 {
235 public:
236     /** \name Accessors */ ///@{
237     const std::map<std::string, std::shared_ptr<Texture>>& Textures() const;
238     //@}
239 
240     /** \name Mutators */ ///@{
241     /** Stores a pre-existing GG::Texture in the manager's texture pool, and
242         returns a shared_ptr to it. \warning Calling code <b>must not</b>
243         delete \a texture; \a texture becomes the property of the manager,
244         which will eventually delete it. */
245     std::shared_ptr<Texture> StoreTexture(Texture* texture, const std::string& texture_name);
246 
247     /** Stores a pre-existing GG::Texture in the manager's texture pool, and
248         returns a shared_ptr to it. \warning Calling code <b>must not</b>
249         delete \a texture; \a texture becomes the property of the manager,
250         which will eventually delete it. */
251     std::shared_ptr<Texture> StoreTexture(const std::shared_ptr<Texture>& texture, const std::string& texture_name);
252 
253     /** Returns a shared_ptr to the texture created from image file \a path.
254         If the texture is not present in the manager's pool, it will be loaded
255         from disk. */
256     std::shared_ptr<Texture> GetTexture(const boost::filesystem::path& path, bool mipmap = false);
257 
258     /** Removes the manager's shared_ptr to the texture created from image
259         file \a path, if it exists.  \note Due to shared_ptr semantics, the
260         texture may not be deleted until much later. */
261     void                     FreeTexture(const boost::filesystem::path& path);
262 
263     /** Removes the manager's shared_ptr to the texture stored with the name
264         \a name, if it exists.  \note Due to shared_ptr semantics, the
265         texture may not be deleted until much later. */
266     void                     FreeTexture(const std::string& name);
267     //@}
268 
269 private:
270     TextureManager();
271     std::shared_ptr<Texture> LoadTexture(const boost::filesystem::path& path, bool mipmap);
272 
273     /** Indexed by string, not path, because some textures may be stored by a
274         name and not loaded from a path. */
275     std::map<std::string, std::shared_ptr<Texture>> m_textures;
276 
277     friend GG_API TextureManager& GetTextureManager();
278 };
279 
280 /** Returns the singleton TextureManager instance. */
281 GG_API TextureManager& GetTextureManager();
282 
283 } // namespace GG
284 
285 #endif
286