ff89f8c6f0a9d45b654c76cda2cce5ee96c90555
[supertux.git] / src / video / surface.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <config.h>
21
22 #include <cassert>
23 #include <iostream>
24 #include <algorithm>
25 #include <stdexcept>
26 #include <sstream>
27 #include <math.h>
28
29 #include <SDL.h>
30 #include <SDL_image.h>
31
32 #include "gameconfig.hpp"
33 #include "physfs/physfs_sdl.hpp"
34 #include "video/surface.hpp"
35 #include "video/drawing_context.hpp"
36 #include "video/color.hpp"
37 #include "image_texture.hpp"
38 #include "texture_manager.hpp"
39
40 Surface::Surface(const std::string& file)
41 {
42   texture = texture_manager->get(file);
43   texture->ref();
44   uv_left = 0;
45   uv_top = 0;
46   uv_right = texture->get_uv_right();
47   uv_bottom = texture->get_uv_bottom();
48
49   width = texture->get_image_width();
50   height = texture->get_image_height();
51 }
52
53 Surface::Surface(const std::string& file, int x, int y, int w, int h)
54 {
55   texture = texture_manager->get(file);
56   texture->ref();
57
58   float tex_w = static_cast<float> (texture->get_width());
59   float tex_h = static_cast<float> (texture->get_height());
60   uv_left = static_cast<float>(x) / tex_w;
61   uv_top = static_cast<float>(y) / tex_h;
62   uv_right = static_cast<float>(x+w) / tex_w;
63   uv_bottom = static_cast<float>(y+h) / tex_h;
64
65   width = w;
66   height = h;
67 }
68
69 Surface::Surface(const Surface& other)
70 {
71   texture = other.texture;
72   texture->ref();
73
74   uv_left = other.uv_left;
75   uv_top = other.uv_top;
76   uv_right = other.uv_right;
77   uv_bottom = other.uv_bottom;
78   width = other.width;
79   height = other.height;
80 }
81
82 const Surface&
83 Surface::operator= (const Surface& other)
84 {
85   other.texture->ref();
86   texture->unref();
87   texture = other.texture;
88
89   uv_left = other.uv_left;
90   uv_top = other.uv_top;
91   uv_right = other.uv_right;
92   uv_bottom = other.uv_bottom;
93   width = other.width;
94   height = other.height;
95
96   return *this;
97 }
98
99 Surface::~Surface()
100 {
101   texture->unref();
102 }
103
104 void
105 Surface::hflip()
106 {
107   std::swap(uv_left, uv_right);
108 }
109
110 static inline void intern_draw(float left, float top, float right, float bottom,                               float uv_left, float uv_top,
111                                float uv_right, float uv_bottom,
112                                DrawingEffect effect)
113 {
114   if(effect & HORIZONTAL_FLIP)
115     std::swap(uv_left, uv_right);
116   if(effect & VERTICAL_FLIP) {
117     std::swap(uv_top, uv_bottom);
118   }
119   
120   glBegin(GL_QUADS);
121   glTexCoord2f(uv_left, uv_top);
122   glVertex2f(left, top);
123   
124   glTexCoord2f(uv_right, uv_top);
125   glVertex2f(right, top);
126
127   glTexCoord2f(uv_right, uv_bottom);
128   glVertex2f(right, bottom);
129
130   glTexCoord2f(uv_left, uv_bottom);
131   glVertex2f(left, bottom);
132   glEnd();
133 }
134
135 static inline void intern_draw2(float left, float top, float right, float bottom,
136                                 float uv_left, float uv_top,
137                                 float uv_right, float uv_bottom,
138                                 float angle,
139                                 const Color& color,
140                                 const Blend& blend,
141                                 DrawingEffect effect)
142 {
143   if(effect & HORIZONTAL_FLIP)
144     std::swap(uv_left, uv_right);
145   if(effect & VERTICAL_FLIP) {
146     std::swap(uv_top, uv_bottom);
147   }
148   
149   float center_x = (left + right) / 2;
150   float center_y = (top + bottom) / 2;
151   
152   float sa = sinf(angle/180.0f*M_PI);
153   float ca = cosf(angle/180.0f*M_PI);
154
155   left  -= center_x;
156   right -= center_x;
157
158   top    -= center_y;
159   bottom -= center_y;
160
161   glBlendFunc(blend.sfactor, blend.dfactor);
162   glColor4f(color.red, color.green, color.blue, color.alpha);
163   glBegin(GL_QUADS);
164   glTexCoord2f(uv_left, uv_top);
165   glVertex2f(left*ca - top*sa + center_x,
166              left*sa + top*ca + center_y);
167   
168   glTexCoord2f(uv_right, uv_top);
169   glVertex2f(right*ca - top*sa + center_x,
170              right*sa + top*ca + center_y);
171
172   glTexCoord2f(uv_right, uv_bottom);
173   glVertex2f(right*ca - bottom*sa + center_x,
174              right*sa + bottom*ca + center_y);
175
176   glTexCoord2f(uv_left, uv_bottom);
177   glVertex2f(left*ca - bottom*sa + center_x,
178              left*sa + bottom*ca + center_y);
179   glEnd();
180   
181   // FIXME: find a better way to restore the blend mode
182   glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
183   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
184 }
185
186 void
187 Surface::draw(float x, float y, float alpha, float angle, const Color& color, const Blend& blend, DrawingEffect effect) const
188 {
189   glColor4f(1.0f, 1.0f, 1.0f, alpha);
190   glBindTexture(GL_TEXTURE_2D, texture->get_handle());
191
192   intern_draw2(x, y,
193                x + width, y + height,
194                uv_left, uv_top, uv_right, uv_bottom, 
195                angle,
196                color,
197                blend, 
198                effect);
199 }
200
201 void
202 Surface::draw(float x, float y, float alpha, DrawingEffect effect) const
203 {
204   glColor4f(1.0f, 1.0f, 1.0f, alpha);
205   glBindTexture(GL_TEXTURE_2D, texture->get_handle());
206
207   intern_draw(x, y,
208               x + width, y + height,
209               uv_left, uv_top, uv_right, uv_bottom, effect);
210 }
211
212 void
213 Surface::draw_part(float src_x, float src_y, float dst_x, float dst_y,
214                    float width, float height, float alpha,
215                    DrawingEffect effect) const
216 {
217   float uv_width = uv_right - uv_left;
218   float uv_height = uv_bottom - uv_top;
219   
220   float uv_left = this->uv_left + (uv_width * src_x) / this->width;
221   float uv_top = this->uv_top + (uv_height * src_y) / this->height;
222   float uv_right = this->uv_left + (uv_width * (src_x + width)) / this->width;
223   float uv_bottom = this->uv_top + (uv_height * (src_y + height)) / this->height;
224   
225   glColor4f(1.0f, 1.0f, 1.0f, alpha);
226   glBindTexture(GL_TEXTURE_2D, texture->get_handle());  
227   
228   intern_draw(dst_x, dst_y,
229               dst_x + width, dst_y + height,
230               uv_left, uv_top, uv_right, uv_bottom, effect);
231 }
232