1 /*
2 Copyright (C) 2005 Matthias Braun <matze@braunis.de>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program 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
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 #include <config.h>
19
20 #include "PainterSDL.hpp"
21
22 #include <iostream>
23 #include <assert.h>
24 #include <math.h>
25 #include <typeinfo>
26 #include <SDL_opengl.h>
27 #include <SDL_gfxPrimitives.h>
28 #include <SDL_rotozoom.h>
29 #include <SDL_video.h>
30 #include <stdlib.h>
31
32 #include "TextureSDL.hpp"
33
34 #ifdef _MSC_VER
35 #define lrint(x) (long int)x
36 #define lroundf(x) (long int)(x + .5)
37 #endif
38
PainterSDL(SDL_Surface * _target)39 PainterSDL::PainterSDL(SDL_Surface* _target)
40 : target(_target)
41 {
42 }
43
PainterSDL(TextureSDL * texture)44 PainterSDL::PainterSDL(TextureSDL* texture)
45 : target(texture->surface)
46 {
47 }
48
~PainterSDL()49 PainterSDL::~PainterSDL()
50 {
51 }
52
53 //ERM this function seems to account for SOME of the slowdown
54 void
drawTexture(const Texture * texture,const Vector2 & pos)55 PainterSDL::drawTexture(const Texture* texture, const Vector2& pos)
56 {
57 assert(typeid(*texture) == typeid(TextureSDL));
58 const TextureSDL* textureSDL = static_cast<const TextureSDL*> (texture);
59
60 if(texture == 0) {
61 std::cerr << "Trying to render 0 texture.";
62 #ifdef DEBUG
63 assert(false);
64 #endif
65 return;
66 }
67
68 Vector2 screenpos = transform.apply(pos);
69
70 SDL_Rect drect;
71 drect.x = lrint(screenpos.x);
72 drect.y = lrint(screenpos.y);
73 SDL_BlitSurface(textureSDL->surface, 0, target, &drect);
74 }
75
76 //RectIntersection checks to see if two SDL_Rects intersect each other
77 // This is actually stolen from SDL code (SDL_HasIntersection function in SDL_rect.c),
78 // but I think that's a very recent addition which is not accessible here.
RectIntersection(const SDL_Rect * A,const SDL_Rect * B)79 bool RectIntersection(const SDL_Rect * A, const SDL_Rect * B)
80 {
81 int Amin, Amax, Bmin, Bmax;
82
83 // Horizontal intersection
84 Amin = A->x;
85 Amax = Amin + A->w;
86 Bmin = B->x;
87 Bmax = Bmin + B->w;
88 if (Bmin > Amin)
89 Amin = Bmin;
90 if (Bmax < Amax)
91 Amax = Bmax;
92 if (Amax <= Amin)
93 return false;
94
95 // Vertical intersection
96 Amin = A->y;
97 Amax = Amin + A->h;
98 Bmin = B->y;
99 Bmax = Bmin + B->h;
100 if (Bmin > Amin)
101 Amin = Bmin;
102 if (Bmax < Amax)
103 Amax = Bmax;
104 if (Amax <= Amin)
105 return false;
106
107 return true;
108 }
109
110 //ERM this function seems to account for MOST of the slowdown
111 void
drawStretchTexture(Texture * texture,const Rect2D & rect)112 PainterSDL::drawStretchTexture(Texture* texture, const Rect2D& rect)
113 {
114 assert(typeid(*texture) == typeid(TextureSDL));
115 TextureSDL* textureSDL = static_cast< TextureSDL*> (texture);
116
117 if(texture == 0 || texture->getWidth() == 0 || texture->getHeight() == 0) {
118 std::cerr << "Trying to render 0 texture.";
119 #ifdef DEBUG
120 assert(false);
121 #endif
122 return;
123 }
124
125 Vector2 screenpos = transform.apply(rect.p1);
126
127 SDL_Rect drect, cliprect;
128 drect.x = lroundf(screenpos.x);
129 drect.y = lroundf(screenpos.y);
130 // kinda hacky... but zoomSurface sometimes produces 1 pixel too small
131 // images
132 drect.w = lroundf(rect.getWidth()) /*+ 1*/;
133 drect.h = lroundf(rect.getHeight()) /*+ 1*/;
134
135 SDL_GetClipRect(target, &cliprect); //get the current cliprect for the target
136
137 //This intersection test would not normally be necessary since SDL_BlitSurface
138 // will only blit to the cliprect and skip others.
139 // The problem here is that we are zooming all surfaces before blitting, so
140 // even the clipped rects get zoomed.
141 // So, the solution is to do the clipping ourselves so that we don't zoom
142 // surfaces ultimately destined to be clipped.
143 if(!RectIntersection(&drect, &cliprect))
144 return;
145
146 double zoomx = drect.w / textureSDL->getWidth();
147 double zoomy = drect.h / textureSDL->getHeight();
148
149 //This code caches zoomed surfaces so that they do not need to be zoomed each blit
150 if(textureSDL->zoomSurface == NULL || zoomx != textureSDL->zoomx || zoomy != textureSDL->zoomy)
151 {
152 textureSDL->setZoomSurface(zoomSurface(textureSDL->surface, zoomx, zoomy, SMOOTHING_OFF), zoomx, zoomy);
153 }
154 SDL_BlitSurface(textureSDL->zoomSurface, 0, target, &drect);
155
156 /*
157 //This was the original code that would zoom a surface, blit it, and then free it.
158 SDL_Surface *tmp;
159 tmp = zoomSurface(textureSDL->surface, zoomx, zoomy, SMOOTHING_OFF);
160 SDL_BlitSurface(tmp, 0, target, &drect);
161 SDL_FreeSurface(tmp);
162 */
163 }
164
165
166 void
fillPolygon(int numberPoints,const Vector2 * points)167 PainterSDL::fillPolygon(int numberPoints, const Vector2* points)
168 {
169 Vector2 screenpos;
170 Sint16* vx = new Sint16[numberPoints];
171 Sint16* vy = new Sint16[numberPoints];
172 for(int i = 0; i < numberPoints; i++ ) {
173 screenpos = transform.apply( points[ i ] );
174 vx[ i ] = (int) screenpos.x;
175 vy[ i ] = (int) screenpos.y;
176 }
177 filledPolygonRGBA( target, vx, vy, numberPoints,
178 fillColor.r, fillColor.g, fillColor.b, fillColor.a);
179 delete[] vx;
180 delete[] vy;
181 }
182
183 void
drawPolygon(int numberPoints,const Vector2 * points)184 PainterSDL::drawPolygon(int numberPoints, const Vector2* points)
185 {
186 Vector2 screenpos;
187 Sint16* vx = new Sint16[numberPoints];
188 Sint16* vy = new Sint16[numberPoints];
189 for(int i = 0; i < numberPoints; i++ ) {
190 screenpos = transform.apply( points[ i ] );
191 vx[ i ] = (int) screenpos.x;
192 vy[ i ] = (int) screenpos.y;
193 }
194 aapolygonRGBA( target, vx, vy, numberPoints,
195 lineColor.r, lineColor.g, lineColor.b, lineColor.a);
196 delete[] vx;
197 delete[] vy;
198 }
199
200 void
drawLine(const Vector2 pointA,const Vector2 pointB)201 PainterSDL::drawLine( const Vector2 pointA, const Vector2 pointB )
202 {
203 Vector2 screenpos = transform.apply( pointA );
204 Vector2 screenpos2 = transform.apply( pointB );
205 aalineRGBA( target, (int) screenpos.x, (int) screenpos.y,
206 (int) screenpos2.x, (int) screenpos2.y,
207 lineColor.r, lineColor.g, lineColor.b, lineColor.a);
208
209
210 }
211
212 void
fillRectangle(const Rect2D & rect)213 PainterSDL::fillRectangle(const Rect2D& rect)
214 {
215 Vector2 screenpos = transform.apply(rect.p1);
216 Vector2 screenpos2 = transform.apply(rect.p2);
217 boxRGBA(target, (int) screenpos.x, (int) screenpos.y,
218 (int) screenpos2.x, (int) screenpos2.y,
219 fillColor.r, fillColor.g, fillColor.b, fillColor.a);
220 }
221
222 void
drawRectangle(const Rect2D & rect)223 PainterSDL::drawRectangle(const Rect2D& rect)
224 {
225 Vector2 screenpos = transform.apply(rect.p1);
226 Vector2 screenpos2 = transform.apply(rect.p2);
227 rectangleRGBA(target, (int) screenpos.x, (int) screenpos.y,
228 (int) screenpos2.x, (int) screenpos2.y,
229 lineColor.r, lineColor.g, lineColor.b, lineColor.a);
230 }
231
232 void
setFillColor(Color color)233 PainterSDL::setFillColor(Color color)
234 {
235 fillColor = color;
236 }
237
238 void
setLineColor(Color color)239 PainterSDL::setLineColor(Color color)
240 {
241 lineColor = color;
242 }
243
244 void
translate(const Vector2 & vec)245 PainterSDL::translate(const Vector2& vec)
246 {
247 transform.translation -= vec;
248 }
249
250 void
pushTransform()251 PainterSDL::pushTransform()
252 {
253 transformStack.push_back(transform);
254 }
255
256 void
popTransform()257 PainterSDL::popTransform()
258 {
259 transform = transformStack.back();
260 transformStack.pop_back();
261 }
262
263 void
setClipRectangle(const Rect2D & rect)264 PainterSDL::setClipRectangle(const Rect2D& rect)
265 {
266 Vector2 screenpos = transform.apply(rect.p1);
267 SDL_Rect cliprect;
268 cliprect.x = (int) screenpos.x;
269 cliprect.y = (int) screenpos.y;
270 cliprect.w = (int) rect.getWidth();
271 cliprect.h = (int) rect.getHeight();
272 SDL_SetClipRect(target, &cliprect);
273 }
274
275 void
clearClipRectangle()276 PainterSDL::clearClipRectangle()
277 {
278 SDL_SetClipRect(target, 0);
279 }
280
281 Painter*
createTexturePainter(Texture * texture)282 PainterSDL::createTexturePainter(Texture* texture)
283 {
284 assert(typeid(*texture) == typeid(TextureSDL));
285 TextureSDL* textureSDL = static_cast<TextureSDL*> (texture);
286
287 return new PainterSDL(textureSDL);
288 }
289
290