8d27796aa6291ee2af53ab08b3bfa96c153acd0b
[supertux.git] / src / unison / src / video / opengl / Texture.cpp
1 //          Copyright Timothy Goya 2007.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5
6 #include "Texture.hpp"
7 #include <unison/video/Surface.hpp>
8 #include <unison/video/Window.hpp>
9 #include <unison/video/Renderers.hpp>
10 #include <unison/video/backend/Renderer.hpp>
11
12 #include <assert.h>
13 #include <algorithm>
14
15 namespace
16 {
17    int next_power_of_two(int val)
18    {
19       int result = 1;
20       while(result < val)
21          result *= 2;
22       return result;
23    }
24 }
25
26 namespace Unison
27 {
28    namespace Video
29    {
30       namespace OpenGL
31       {
32          Texture::Texture(const Surface &surface) :
33             Backend::Texture(surface),
34             handles()
35          {
36          }
37
38          /*Texture::Texture(const Surface &surface, const std::string &name) :
39             Backend::Texture(surface, name),
40             handles()
41          {
42          }
43
44          Texture::Texture(Backend::Texture *texture) :
45             Backend::Texture(texture),
46             handles()
47          {
48          }*/
49
50          Texture::~Texture()
51          {
52             for(std::vector<Handle>::iterator iter = handles.begin(), end = handles.end();iter != end;++iter)
53             {
54                glDeleteTextures(1, &iter->texture);
55             }
56          }
57
58          const Surface Texture::get_surface()
59          {
60             if(surface.get_size() == Area())
61             {
62                assert(!handles.empty());
63                surface = Surface(size);
64                for(std::vector<Handle>::iterator iter = handles.begin(), end = handles.end();iter != end;++iter)
65                {
66                   Surface section(iter->rect.size);
67                   glBindTexture(GL_TEXTURE_2D, iter->texture);
68                   glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, section.get_pixels());
69                   surface.blit(section, iter->rect.pos, Rect(), BLEND_NONE);
70                }
71                assert(!glGetError());
72             }
73             return surface;
74          }
75
76          void Texture::save()
77          {
78             get_surface();
79             for(std::vector<Handle>::iterator iter = handles.begin(), end = handles.end();iter != end;++iter)
80             {
81                glDeleteTextures(1, &iter->texture);
82             }
83             handles.clear();
84          }
85
86          void Texture::blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
87          {
88             save();
89             Renderers::get().get_renderer().blit(src, src_rect, surface, dst_pos, options);
90          }
91
92          void Texture::blit(const Video::Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
93          {
94             save();
95             Texture *texture = dynamic_cast<Texture *>(Backend::Texture::get_texture(src.get_id()));
96             Renderers::get().get_renderer().blit(texture, src_rect, surface, dst_pos, options);
97          }
98
99          void Texture::fill(const Color &color, const Rect &rect)
100          {
101             save();
102             Renderers::get().get_renderer().fill(surface, color, rect);
103          }
104
105          void Texture::fill_blend(const Color &color, const Rect &rect)
106          {
107             save();
108             Renderers::get().get_renderer().fill_blend(surface, color, rect);
109          }
110
111          void Texture::blit_draw_buffer(const Rect &src_rect, const Point &dst_pos, const RenderOptions &options)
112          {
113             if(handles.empty())
114             {
115                assert(surface.get_size() != Area());
116                GLint max_size;
117                glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
118                for(unsigned int y = 0;y < surface.get_size().y;y += max_size)
119                {
120                   for(unsigned int x = 0;x < surface.get_size().x;x += max_size)
121                   {
122                      Rect rect;
123                      rect.pos.x = x;
124                      rect.pos.y = y;
125                      rect.size.x = std::min(surface.get_size().x - x, (unsigned int)max_size);
126                      rect.size.y = std::min(surface.get_size().y - y, (unsigned int)max_size);
127                      handles.push_back(create_handle(surface, rect));
128                   }
129                }
130                surface = Surface();
131             }
132
133             glColor4ub(options.color.red, options.color.green, options.color.blue, options.alpha);
134             switch(options.blend)
135             {
136                case BLEND_NONE:
137                   glDisable(GL_BLEND);
138                   //glBlendFunc(GL_ONE, GL_ZERO);
139                   break;
140                case BLEND_MASK:
141                   assert(0 && "Mask blending not implemented");
142                   return;
143                case BLEND_ALPHA:
144                   glEnable(GL_BLEND);
145                   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
146                   break;
147                case BLEND_ADD:
148                   glEnable(GL_BLEND);
149                   glBlendFunc(GL_SRC_ALPHA, GL_ONE);
150                   break;
151                case BLEND_MOD:
152                   glEnable(GL_BLEND);
153                   glBlendFunc(GL_ZERO, GL_SRC_COLOR);
154                   break;
155                default:
156                   assert(0 && "Unrecognized blend mode");
157                   return;
158             }
159
160             glPushMatrix();
161
162             glTranslatef(dst_pos.x - src_rect.pos.x, dst_pos.y - src_rect.pos.y, 0);
163
164             for(std::vector<Handle>::iterator iter = handles.begin(), end = handles.end();iter != end;++iter)
165             {
166                Rect overlap = iter->rect.get_overlap(src_rect);
167                if(overlap != Rect())
168                {
169                   GLfloat uv_left = GLfloat(overlap.get_left()) / iter->rect.size.x;
170                   GLfloat uv_top = GLfloat(overlap.get_top()) / iter->rect.size.y;
171                   GLfloat uv_right = GLfloat(overlap.get_right()) / iter->rect.size.x;
172                   GLfloat uv_bottom = GLfloat(overlap.get_bottom()) / iter->rect.size.y;
173
174                   if(options.h_flip)
175                   {
176                      std::swap(uv_left, uv_right);
177                   }
178
179                   if(options.v_flip)
180                   {
181                      std::swap(uv_top, uv_bottom);
182                   }
183
184                   glPushMatrix();
185
186                   glTranslatef(overlap.pos.x, overlap.pos.y, 0);
187
188                   glBindTexture(GL_TEXTURE_2D, iter->texture);
189                   glBegin(GL_QUADS);
190                      glTexCoord2f(uv_left, uv_top);
191                      glVertex2i(0, 0);
192
193                      glTexCoord2f(uv_right, uv_top);
194                      glVertex2i(overlap.size.x, 0);
195
196                      glTexCoord2f(uv_right, uv_bottom);
197                      glVertex2i(overlap.size.x, overlap.size.y);
198
199                      glTexCoord2f(uv_left, uv_bottom);
200                      glVertex2i(0, overlap.size.y);
201                   glEnd();
202
203                   glPopMatrix();
204                }
205             }
206
207             glPopMatrix();
208
209             //glColor4ub(0xff, 0xff, 0xff, 0xff);
210             //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
211          }
212
213          Texture::Handle Texture::create_handle(const Surface &surface, const Rect &rect)
214          {
215             Texture::Handle handle;
216             handle.rect = rect;
217             handle.rect.size.x = next_power_of_two(rect.size.x);
218             handle.rect.size.y = next_power_of_two(rect.size.y);
219             Surface convert(handle.rect.size);
220             convert.blit(surface, Point(), rect, BLEND_NONE);
221
222
223             glGenTextures(1, &handle.texture);
224             assert(!glGetError());
225             try
226             {
227                glBindTexture(GL_TEXTURE_2D, handle.texture);
228                //glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
229
230                //surface.lock();
231                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
232                      handle.rect.size.x, handle.rect.size.y,
233                      0, GL_RGBA, GL_UNSIGNED_BYTE, convert.get_pixels());
234                //surface.unlock();
235
236                assert(!glGetError());
237
238                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
239                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
240                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
241                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
242             }
243             catch(...)
244             {
245                glDeleteTextures(1, &handle.texture);
246                throw;
247             }
248             return handle;
249          }
250       }
251    }
252 }