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