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