- added sprite rotation
[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 "image_texture.hpp"
36 #include "texture_manager.hpp"
37
38 Surface::Surface(const std::string& file)
39 {
40   texture = texture_manager->get(file);
41   texture->ref();
42   uv_left = 0;
43   uv_top = 0;
44   uv_right = texture->get_uv_right();
45   uv_bottom = texture->get_uv_bottom();
46
47   width = texture->get_image_width();
48   height = texture->get_image_height();
49 }
50
51 Surface::Surface(const std::string& file, int x, int y, int w, int h)
52 {
53   texture = texture_manager->get(file);
54   texture->ref();
55
56   float tex_w = static_cast<float> (texture->get_width());
57   float tex_h = static_cast<float> (texture->get_height());
58   uv_left = static_cast<float>(x) / tex_w;
59   uv_top = static_cast<float>(y) / tex_h;
60   uv_right = static_cast<float>(x+w) / tex_w;
61   uv_bottom = static_cast<float>(y+h) / tex_h;
62
63   width = w;
64   height = h;
65 }
66
67 Surface::Surface(const Surface& other)
68 {
69   texture = other.texture;
70   texture->ref();
71
72   uv_left = other.uv_left;
73   uv_top = other.uv_top;
74   uv_right = other.uv_right;
75   uv_bottom = other.uv_bottom;
76   width = other.width;
77   height = other.height;
78 }
79
80 const Surface&
81 Surface::operator= (const Surface& other)
82 {
83   other.texture->ref();
84   texture->unref();
85   texture = other.texture;
86
87   uv_left = other.uv_left;
88   uv_top = other.uv_top;
89   uv_right = other.uv_right;
90   uv_bottom = other.uv_bottom;
91   width = other.width;
92   height = other.height;
93
94   return *this;
95 }
96
97 Surface::~Surface()
98 {
99   texture->unref();
100 }
101
102 void
103 Surface::hflip()
104 {
105   std::swap(uv_left, uv_right);
106 }
107
108 static inline void intern_draw(float left, float top, float right, float bottom,                               float uv_left, float uv_top,
109                                float uv_right, float uv_bottom,
110                                DrawingEffect effect)
111 {
112   if(effect & HORIZONTAL_FLIP)
113     std::swap(uv_left, uv_right);
114   if(effect & VERTICAL_FLIP) {
115     std::swap(uv_top, uv_bottom);
116   }
117   
118   glBegin(GL_QUADS);
119   glTexCoord2f(uv_left, uv_top);
120   glVertex2f(left, top);
121   
122   glTexCoord2f(uv_right, uv_top);
123   glVertex2f(right, top);
124
125   glTexCoord2f(uv_right, uv_bottom);
126   glVertex2f(right, bottom);
127
128   glTexCoord2f(uv_left, uv_bottom);
129   glVertex2f(left, bottom);
130   glEnd();
131 }
132
133 static inline void intern_draw2(float left, float top, float right, float bottom,
134                                 float uv_left, float uv_top,
135                                 float uv_right, float uv_bottom,
136                                 float angle,
137                                 DrawingEffect effect)
138 {
139   if(effect & HORIZONTAL_FLIP)
140     std::swap(uv_left, uv_right);
141   if(effect & VERTICAL_FLIP) {
142     std::swap(uv_top, uv_bottom);
143   }
144   
145   float center_x = (left + right) / 2;
146   float center_y = (top + bottom) / 2;
147   
148   float sa = sinf(angle/180.0f*M_PI);
149   float ca = cosf(angle/180.0f*M_PI);
150
151   left  -= center_x;
152   right -= center_x;
153
154   top    -= center_y;
155   bottom -= center_y;
156
157   glBegin(GL_QUADS);
158   glTexCoord2f(uv_left, uv_top);
159   glVertex2f(left*ca - top*sa + center_x,
160              left*sa + top*ca + center_y);
161   
162   glTexCoord2f(uv_right, uv_top);
163   glVertex2f(right*ca - top*sa + center_x,
164              right*sa + top*ca + center_y);
165
166   glTexCoord2f(uv_right, uv_bottom);
167   glVertex2f(right*ca - bottom*sa + center_x,
168              right*sa + bottom*ca + center_y);
169
170   glTexCoord2f(uv_left, uv_bottom);
171   glVertex2f(left*ca - bottom*sa + center_x,
172              left*sa + bottom*ca + center_y);
173   glEnd();
174 }
175
176 void
177 Surface::draw(float x, float y, float alpha, float angle, DrawingEffect effect) const
178 {
179   glColor4f(1.0f, 1.0f, 1.0f, alpha);
180   glBindTexture(GL_TEXTURE_2D, texture->get_handle());
181
182   intern_draw2(x, y,
183                x + width, y + height,
184                uv_left, uv_top, uv_right, uv_bottom, 
185                angle,
186                effect);
187 }
188
189 void
190 Surface::draw(float x, float y, float alpha, DrawingEffect effect) const
191 {
192   glColor4f(1.0f, 1.0f, 1.0f, alpha);
193   glBindTexture(GL_TEXTURE_2D, texture->get_handle());
194
195   intern_draw(x, y,
196               x + width, y + height,
197               uv_left, uv_top, uv_right, uv_bottom, effect);
198 }
199
200 void
201 Surface::draw_part(float src_x, float src_y, float dst_x, float dst_y,
202                    float width, float height, float alpha,
203                    DrawingEffect effect) const
204 {
205   float uv_width = uv_right - uv_left;
206   float uv_height = uv_bottom - uv_top;
207   
208   float uv_left = this->uv_left + (uv_width * src_x) / this->width;
209   float uv_top = this->uv_top + (uv_height * src_y) / this->height;
210   float uv_right = this->uv_left + (uv_width * (src_x + width)) / this->width;
211   float uv_bottom = this->uv_top + (uv_height * (src_y + height)) / this->height;
212   
213   glColor4f(1.0f, 1.0f, 1.0f, alpha);
214   glBindTexture(GL_TEXTURE_2D, texture->get_handle());  
215   
216   intern_draw(dst_x, dst_y,
217               dst_x + width, dst_y + height,
218               uv_left, uv_top, uv_right, uv_bottom, effect);
219 }
220