1 //
2 // Cross-platform free Puyo-Puyo clone.
3 // Copyright (C) 2006, 2007 Emma's Software
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 #if defined (HAVE_CONFIG_H)
20 #include <config.h>
21 #endif // HAVE_CONFIG_H
22 #include <assert.h>
23 #include <math.h>
24 #include <SDL.h>
25 #include <SDL_image.h>
26 #include <stdexcept>
27 #include "Surface.h"
28 #include "System.h"
29
30 using namespace Amoebax;
31
32 ///
33 /// \brief Surface's default constructor.
34 ///
35 /// Makes an empty (NULL) surface.
36 ///
Surface(void)37 Surface::Surface (void):
38 m_SDLSurface (0)
39 {
40 }
41
42 ///
43 /// \brief Creates a new surface with an already loaded SDL surface.
44 ///
45 /// \param SDLSurface The SDL surface that the class will have
46 /// ownership of.
47 ///
Surface(SDL_Surface * SDLSurface)48 Surface::Surface (SDL_Surface *SDLSurface):
49 m_SDLSurface (SDLSurface)
50 {
51 }
52
53 ///
54 /// \brief Surface's copy constructor.
55 ///
56 /// \param surface The surface to copy from.
57 ///
Surface(const Surface & surface)58 Surface::Surface (const Surface &surface):
59 m_SDLSurface (0)
60 {
61 if ( 0 != surface.m_SDLSurface )
62 {
63 SDL_Surface *tempSDLSurface =
64 SDL_CreateRGBSurface (surface.m_SDLSurface->flags | SDL_SRCALPHA,
65 surface.m_SDLSurface->w,
66 surface.m_SDLSurface->h,
67 surface.m_SDLSurface->format->BitsPerPixel,
68 surface.m_SDLSurface->format->Rmask,
69 surface.m_SDLSurface->format->Gmask,
70 surface.m_SDLSurface->format->Bmask,
71 surface.m_SDLSurface->format->Amask);
72 if ( NULL == tempSDLSurface )
73 {
74 throw std::runtime_error (SDL_GetError ());
75 }
76 if ( SDL_BlitSurface (surface.m_SDLSurface, NULL, tempSDLSurface, NULL) < 0 )
77 {
78 throw std::runtime_error (SDL_GetError ());
79 }
80 std::swap (m_SDLSurface, tempSDLSurface);
81 SDL_FreeSurface (tempSDLSurface);
82 }
83 }
84
85 ///
86 /// \brief Blits the whole surface to the left-top corner of destination.
87 ///
88 /// \param destination The distantion surface to blit this surface to.
89 ///
90 void
blit(SDL_Surface * destination)91 Surface::blit (SDL_Surface *destination)
92 {
93 blit (0, 0, getWidth (), getHeight (), 0, 0, destination);
94 }
95
96 ///
97 /// \brief Blits the whole surface to a position in destination.
98 ///
99 /// \param destinationX The X position where to blit the source into the
100 /// destination.
101 /// \param destinationY The Y position where to blit the source into the
102 /// destination.
103 /// \param destination The destination surface.
104 ///
105 void
blit(uint16_t destinationX,uint16_t destinationY,SDL_Surface * destination)106 Surface::blit (uint16_t destinationX, uint16_t destinationY,
107 SDL_Surface *destination)
108 {
109 blit (0, 0, getWidth (), getHeight (), destinationX, destinationY,
110 destination);
111 }
112
113 ///
114 /// \brief Blits a part of the surface into another surface.
115 ///
116 /// \param sourceX The X origin of the source surface to blit.
117 /// \param sourceY The X origin of the source surface to blit.
118 /// \param sourceWidth The width of the source surface part to blit.
119 /// \param sourceHeight The height of the source surface part to blit.
120 /// \param destinationX The X position where to blit the source into the
121 /// destination.
122 /// \param destinationY The Y position where to blit the source into the
123 /// destination.
124 /// \param destination The destination surface.
125 ///
126 void
blit(uint16_t sourceX,uint16_t sourceY,uint16_t sourceWidth,uint16_t sourceHeight,uint16_t destinationX,uint16_t destinationY,SDL_Surface * destination)127 Surface::blit (uint16_t sourceX, uint16_t sourceY,
128 uint16_t sourceWidth, uint16_t sourceHeight,
129 uint16_t destinationX, uint16_t destinationY,
130 SDL_Surface *destination)
131 {
132 SDL_Rect sourceRect;
133 sourceRect.x = sourceX;
134 sourceRect.y = sourceY;
135 sourceRect.w = sourceWidth;
136 sourceRect.h = sourceHeight;
137
138 SDL_Rect destinationRect;
139 destinationRect.x = destinationX;
140 destinationRect.y = destinationY;
141 destinationRect.w = sourceWidth;
142 destinationRect.h = sourceHeight;
143
144 SDL_BlitSurface (m_SDLSurface, &sourceRect, destination, &destinationRect);
145 System::getInstance ().invalidateScreenRegion (&destinationRect);
146 }
147
148 ///
149 /// \brief Surface's destructor.
150 ///
151 /// Frees the memory used by the image's surface.
152 ///
~Surface(void)153 Surface::~Surface (void)
154 {
155 SDL_FreeSurface (m_SDLSurface);
156 }
157
158 ///
159 /// \brief Gets the height of the surface.
160 ///
161 /// \return Surface's height.
162 ///
163 uint16_t
getHeight(void) const164 Surface::getHeight (void) const
165 {
166 return toSDLSurface ()->h;
167 }
168
169
170 ///
171 /// \brief Gets the pixel color from a surface's position.
172 ///
173 /// \param x The x position to get the color from.
174 /// \param y The y position to get the color from.
175 /// \return The color at surface's \a x, \a y position.
176 ///
177 uint32_t
getPixel(uint16_t x,uint16_t y)178 Surface::getPixel (uint16_t x, uint16_t y)
179 {
180 assert (x < m_SDLSurface->w && "Tried to get a color outside the surface.");
181 assert (y < m_SDLSurface->h && "Tried to get a color outside the surface.");
182
183 uint32_t pixelColor = 0;
184 SDL_LockSurface (m_SDLSurface);
185 switch (m_SDLSurface->format->BytesPerPixel)
186 {
187 case 1:
188 pixelColor = static_cast<uint32_t>(*(static_cast<uint8_t *>(m_SDLSurface->pixels) + y * m_SDLSurface->pitch + x));
189 break;
190 case 2:
191 pixelColor = static_cast<uint32_t>(*(static_cast<uint16_t *>(m_SDLSurface->pixels) + y * m_SDLSurface->pitch / 2 + x));
192 break;
193 case 3:
194 {
195 uint8_t *pixels = static_cast<uint8_t *>(m_SDLSurface->pixels) +
196 y * m_SDLSurface->pitch +
197 x * m_SDLSurface->format->BytesPerPixel;
198 uint8_t red = *(pixels + m_SDLSurface->format->Rshift / 8);
199 uint8_t green = *(pixels + m_SDLSurface->format->Gshift / 8);
200 uint8_t blue = *(pixels + m_SDLSurface->format->Bshift / 8);
201 pixelColor = SDL_MapRGB (m_SDLSurface->format, red, green, blue);
202 }
203 break;
204 case 4:
205 pixelColor = *(static_cast<uint32_t *>(m_SDLSurface->pixels) +
206 y * m_SDLSurface->pitch / 4 + x);
207 break;
208 }
209 SDL_UnlockSurface (m_SDLSurface);
210
211 return pixelColor;
212 }
213
214
215 ///
216 /// \brief Gets the width of the surface.
217 ///
218 /// \return Surface's width.
219 ///
220 uint16_t
getWidth(void) const221 Surface::getWidth (void) const
222 {
223 return toSDLSurface ()->w;
224 }
225
226
227 ///
228 /// \brief Creates a new surface from a file.
229 ///
230 /// \param fileName The file name to use to load the surface from.
231 /// \return The Surface class with the file loaded.
232 ///
233 Surface *
fromFile(std::string fileName)234 Surface::fromFile (std::string fileName)
235 {
236 SDL_Surface *loadedSurface = IMG_Load (fileName.c_str ());
237 if ( NULL == loadedSurface )
238 {
239 throw std::runtime_error (IMG_GetError ());
240 }
241 SDL_Surface *optimizedSurface = SDL_DisplayFormatAlpha (loadedSurface);
242 SDL_FreeSurface (loadedSurface);
243 if ( NULL == optimizedSurface )
244 {
245 throw std::runtime_error (SDL_GetError ());
246 }
247
248 return new Surface (optimizedSurface);
249 }
250
251 ///
252 /// \brief Creates a new surface as a copy of the screen.
253 ///
254 /// \return The Surface object with a copy of the screen.
255 ///
256 Surface *
fromScreen(void)257 Surface::fromScreen (void)
258 {
259 SDL_Surface *screen = System::getInstance ().getScreenSDLSurface ();
260 SDL_Surface *copy =
261 SDL_CreateRGBSurface (SDL_SWSURFACE | SDL_SRCALPHA,
262 screen->w, screen->h,
263 screen->format->BitsPerPixel,
264 screen->format->Rmask, screen->format->Gmask,
265 screen->format->Bmask, screen->format->Amask);
266 if ( 0 == copy )
267 {
268 throw std::runtime_error (IMG_GetError ());
269 }
270 SDL_BlitSurface (screen, NULL, copy, NULL);
271
272 return new Surface (copy);
273 }
274
275 ///
276 /// \brief Surface's assign operator.
277 ///
278 /// \param surface The surface to assign to the current surface.
279 ///
280 Surface &
operator =(const Surface & surface)281 Surface::operator= (const Surface &surface)
282 {
283 Surface tempSurface (surface);
284 swap (*this, tempSurface);
285
286 return *this;
287 }
288
289 ///
290 /// \brief Scales the image using an scale factor.
291 ///
292 /// \param scaleFactor The scale factor to use to resize the image.
293 ///
294 void
resize(float scaleFactor)295 Surface::resize (float scaleFactor)
296 {
297 // In GP2X we use already scaled graphics. No need to scale them
298 // by software (too slow.)
299 #if !defined (IS_GP2X_HOST)
300 // Can only resize 32-bit surfaces.
301 assert ( 0 != m_SDLSurface && "Tried to resize an unloaded surface" );
302 assert ( 32 == m_SDLSurface->format->BitsPerPixel &&
303 "Tried to resize a non 32-bit surface." );
304
305 // If the scale factor is approximaltey 1.0f, then we don't need
306 // to do anything!.
307 if ( fabs (scaleFactor - 1.0f) < 1e-3 )
308 {
309 return;
310 }
311
312 // Create the new scaled surface.
313 SDL_Surface *scaledSDLSurface =
314 SDL_CreateRGBSurface (m_SDLSurface->flags,
315 static_cast<int>(m_SDLSurface->w * scaleFactor),
316 static_cast<int>(m_SDLSurface->h * scaleFactor),
317 m_SDLSurface->format->BitsPerPixel,
318 m_SDLSurface->format->Rmask,
319 m_SDLSurface->format->Gmask,
320 m_SDLSurface->format->Bmask,
321 m_SDLSurface->format->Amask);
322 if ( 0 == scaledSDLSurface )
323 {
324 throw std::runtime_error (SDL_GetError ());
325 }
326
327 // Now, for each pixel in the scaled surface, get the corresponding
328 // coordinate on the original surface and using a bilinear filter,
329 // smooth the color for the scaled pixel.
330 SDL_LockSurface (m_SDLSurface);
331 SDL_LockSurface (scaledSDLSurface);
332
333 uint32_t *scaledPixels = static_cast<uint32_t *>(scaledSDLSurface->pixels);
334 uint32_t *originalPixels = static_cast<uint32_t *>(m_SDLSurface->pixels);
335 int scaledPitch = scaledSDLSurface->pitch / sizeof (uint32_t);
336 int originalPitch = m_SDLSurface->pitch / sizeof (uint32_t);
337 for ( int scaledY = 0 ; scaledY < scaledSDLSurface->h ; ++scaledY )
338 {
339 float originalY = scaledY / scaleFactor;
340 int destinationY = static_cast<int>(floorf (originalY));
341 float yRatio = originalY - destinationY;
342 float yOpposite = 1 - yRatio;
343
344 uint32_t *currentScaledPixel = scaledPixels + scaledY * scaledPitch;
345
346 for ( int scaledX = 0 ; scaledX < scaledSDLSurface->w ; ++scaledX )
347 {
348 float scaledAlpha = 0.0f;
349 float scaledBlue = 0.0f;
350 float scaledGreen = 0.0f;
351 float scaledRed = 0.0f;
352
353 float originalX = scaledX / scaleFactor;
354 int destinationX = static_cast<int>(floorf (originalX));
355 float xRatio = originalX - destinationX;
356 float xOpposite = 1 - xRatio;
357
358 uint8_t originalAlpha = 0;
359 uint8_t originalBlue = 0;
360 uint8_t originalGreen = 0;
361 uint8_t originalRed = 0;
362
363 // Left-top pixel.
364 SDL_GetRGBA (originalPixels[destinationY * originalPitch + destinationX],
365 m_SDLSurface->format,
366 &originalRed, &originalGreen, &originalBlue,
367 &originalAlpha);
368 scaledAlpha += originalAlpha * xOpposite * yOpposite;
369 scaledBlue += originalBlue * xOpposite * yOpposite;
370 scaledGreen += originalGreen * xOpposite * yOpposite;
371 scaledRed += originalRed * xOpposite * yOpposite;
372
373 // Right-top pixel.
374 SDL_GetRGBA (originalPixels[destinationY * originalPitch + destinationX + 1],
375 m_SDLSurface->format,
376 &originalRed, &originalGreen, &originalBlue,
377 &originalAlpha);
378 scaledAlpha += originalAlpha * xRatio * yOpposite;
379 scaledBlue += originalBlue * xRatio * yOpposite;
380 scaledGreen += originalGreen * xRatio * yOpposite;
381 scaledRed += originalRed * xRatio * yOpposite;
382
383 // Left-bottom pixel.
384 SDL_GetRGBA (originalPixels[(destinationY + 1) * originalPitch + destinationX],
385 m_SDLSurface->format,
386 &originalRed, &originalGreen, &originalBlue,
387 &originalAlpha);
388 scaledAlpha += originalAlpha * xOpposite * yRatio;
389 scaledBlue += originalBlue * xOpposite * yRatio;
390 scaledGreen += originalGreen * xOpposite * yRatio;
391 scaledRed += originalRed * xOpposite * yRatio;
392
393 // Right-bottom pixel.
394 SDL_GetRGBA (originalPixels[(destinationY + 1) * originalPitch + destinationX + 1],
395 m_SDLSurface->format,
396 &originalRed, &originalGreen, &originalBlue,
397 &originalAlpha);
398 scaledAlpha += originalAlpha * xRatio * yRatio;
399 scaledBlue += originalBlue * xRatio * yRatio;
400 scaledGreen += originalGreen * xRatio * yRatio;
401 scaledRed += originalRed * xRatio * yRatio;
402
403 *currentScaledPixel =
404 SDL_MapRGBA (scaledSDLSurface->format,
405 static_cast<uint8_t>(scaledRed),
406 static_cast<uint8_t>(scaledGreen),
407 static_cast<uint8_t>(scaledBlue),
408 static_cast<uint8_t>(scaledAlpha));
409 currentScaledPixel++;
410 }
411 }
412
413 SDL_UnlockSurface (scaledSDLSurface);
414 SDL_UnlockSurface (m_SDLSurface);
415
416 // Done. Swap the surfaces and free the original surface.
417 std::swap (m_SDLSurface, scaledSDLSurface);
418 SDL_FreeSurface (scaledSDLSurface);
419 #endif // !IS_GP2X_HOST
420 }
421
422 ///
423 /// \brief Sets the alpha value.
424 ///
425 /// \param alpha The alpha value to set to the surface.
426 ///
427 void
setAlpha(uint8_t alpha)428 Surface::setAlpha (uint8_t alpha)
429 {
430 SDL_SetAlpha (toSDLSurface (), SDL_SRCALPHA, alpha);
431 }
432
433 ///
434 /// \brief Sets the transparent color.
435 ///
436 /// \param colorKey The color that will be transparent while blitting.
437 ///
438 void
setColorKey(uint32_t colorKey)439 Surface::setColorKey (uint32_t colorKey)
440 {
441 SDL_SetColorKey (m_SDLSurface, SDL_SRCCOLORKEY, colorKey);
442 }
443
444 ///
445 /// \brief Swaps two surfaces.
446 ///
447 /// \param lhs The left hand side surface of the swap operation.
448 /// \param rhs The right hand side surface of the swap operation.
449 ///
450 void
swap(Surface & lhs,Surface & rhs)451 Surface::swap (Surface &lhs, Surface &rhs)
452 {
453 std::swap (lhs.m_SDLSurface, rhs.m_SDLSurface);
454 }
455