// // Cross-platform free Puyo-Puyo clone. // Copyright (C) 2006, 2007 Emma's Software // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // #if defined (HAVE_CONFIG_H) #include #endif // HAVE_CONFIG_H #include #include #include #include #include #include "Surface.h" #include "System.h" using namespace Amoebax; /// /// \brief Surface's default constructor. /// /// Makes an empty (NULL) surface. /// Surface::Surface (void): m_SDLSurface (0) { } /// /// \brief Creates a new surface with an already loaded SDL surface. /// /// \param SDLSurface The SDL surface that the class will have /// ownership of. /// Surface::Surface (SDL_Surface *SDLSurface): m_SDLSurface (SDLSurface) { } /// /// \brief Surface's copy constructor. /// /// \param surface The surface to copy from. /// Surface::Surface (const Surface &surface): m_SDLSurface (0) { if ( 0 != surface.m_SDLSurface ) { SDL_Surface *tempSDLSurface = SDL_CreateRGBSurface (surface.m_SDLSurface->flags | SDL_SRCALPHA, surface.m_SDLSurface->w, surface.m_SDLSurface->h, surface.m_SDLSurface->format->BitsPerPixel, surface.m_SDLSurface->format->Rmask, surface.m_SDLSurface->format->Gmask, surface.m_SDLSurface->format->Bmask, surface.m_SDLSurface->format->Amask); if ( NULL == tempSDLSurface ) { throw std::runtime_error (SDL_GetError ()); } if ( SDL_BlitSurface (surface.m_SDLSurface, NULL, tempSDLSurface, NULL) < 0 ) { throw std::runtime_error (SDL_GetError ()); } std::swap (m_SDLSurface, tempSDLSurface); SDL_FreeSurface (tempSDLSurface); } } /// /// \brief Blits the whole surface to the left-top corner of destination. /// /// \param destination The distantion surface to blit this surface to. /// void Surface::blit (SDL_Surface *destination) { blit (0, 0, getWidth (), getHeight (), 0, 0, destination); } /// /// \brief Blits the whole surface to a position in destination. /// /// \param destinationX The X position where to blit the source into the /// destination. /// \param destinationY The Y position where to blit the source into the /// destination. /// \param destination The destination surface. /// void Surface::blit (uint16_t destinationX, uint16_t destinationY, SDL_Surface *destination) { blit (0, 0, getWidth (), getHeight (), destinationX, destinationY, destination); } /// /// \brief Blits a part of the surface into another surface. /// /// \param sourceX The X origin of the source surface to blit. /// \param sourceY The X origin of the source surface to blit. /// \param sourceWidth The width of the source surface part to blit. /// \param sourceHeight The height of the source surface part to blit. /// \param destinationX The X position where to blit the source into the /// destination. /// \param destinationY The Y position where to blit the source into the /// destination. /// \param destination The destination surface. /// void Surface::blit (uint16_t sourceX, uint16_t sourceY, uint16_t sourceWidth, uint16_t sourceHeight, uint16_t destinationX, uint16_t destinationY, SDL_Surface *destination) { SDL_Rect sourceRect; sourceRect.x = sourceX; sourceRect.y = sourceY; sourceRect.w = sourceWidth; sourceRect.h = sourceHeight; SDL_Rect destinationRect; destinationRect.x = destinationX; destinationRect.y = destinationY; destinationRect.w = sourceWidth; destinationRect.h = sourceHeight; SDL_BlitSurface (m_SDLSurface, &sourceRect, destination, &destinationRect); System::getInstance ().invalidateScreenRegion (&destinationRect); } /// /// \brief Surface's destructor. /// /// Frees the memory used by the image's surface. /// Surface::~Surface (void) { SDL_FreeSurface (m_SDLSurface); } /// /// \brief Gets the height of the surface. /// /// \return Surface's height. /// uint16_t Surface::getHeight (void) const { return toSDLSurface ()->h; } /// /// \brief Gets the pixel color from a surface's position. /// /// \param x The x position to get the color from. /// \param y The y position to get the color from. /// \return The color at surface's \a x, \a y position. /// uint32_t Surface::getPixel (uint16_t x, uint16_t y) { assert (x < m_SDLSurface->w && "Tried to get a color outside the surface."); assert (y < m_SDLSurface->h && "Tried to get a color outside the surface."); uint32_t pixelColor = 0; SDL_LockSurface (m_SDLSurface); switch (m_SDLSurface->format->BytesPerPixel) { case 1: pixelColor = static_cast(*(static_cast(m_SDLSurface->pixels) + y * m_SDLSurface->pitch + x)); break; case 2: pixelColor = static_cast(*(static_cast(m_SDLSurface->pixels) + y * m_SDLSurface->pitch / 2 + x)); break; case 3: { uint8_t *pixels = static_cast(m_SDLSurface->pixels) + y * m_SDLSurface->pitch + x * m_SDLSurface->format->BytesPerPixel; uint8_t red = *(pixels + m_SDLSurface->format->Rshift / 8); uint8_t green = *(pixels + m_SDLSurface->format->Gshift / 8); uint8_t blue = *(pixels + m_SDLSurface->format->Bshift / 8); pixelColor = SDL_MapRGB (m_SDLSurface->format, red, green, blue); } break; case 4: pixelColor = *(static_cast(m_SDLSurface->pixels) + y * m_SDLSurface->pitch / 4 + x); break; } SDL_UnlockSurface (m_SDLSurface); return pixelColor; } /// /// \brief Gets the width of the surface. /// /// \return Surface's width. /// uint16_t Surface::getWidth (void) const { return toSDLSurface ()->w; } /// /// \brief Creates a new surface from a file. /// /// \param fileName The file name to use to load the surface from. /// \return The Surface class with the file loaded. /// Surface * Surface::fromFile (std::string fileName) { SDL_Surface *loadedSurface = IMG_Load (fileName.c_str ()); if ( NULL == loadedSurface ) { throw std::runtime_error (IMG_GetError ()); } SDL_Surface *optimizedSurface = SDL_DisplayFormatAlpha (loadedSurface); SDL_FreeSurface (loadedSurface); if ( NULL == optimizedSurface ) { throw std::runtime_error (SDL_GetError ()); } return new Surface (optimizedSurface); } /// /// \brief Creates a new surface as a copy of the screen. /// /// \return The Surface object with a copy of the screen. /// Surface * Surface::fromScreen (void) { SDL_Surface *screen = System::getInstance ().getScreenSDLSurface (); SDL_Surface *copy = SDL_CreateRGBSurface (SDL_SWSURFACE | SDL_SRCALPHA, screen->w, screen->h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask); if ( 0 == copy ) { throw std::runtime_error (IMG_GetError ()); } SDL_BlitSurface (screen, NULL, copy, NULL); return new Surface (copy); } /// /// \brief Surface's assign operator. /// /// \param surface The surface to assign to the current surface. /// Surface & Surface::operator= (const Surface &surface) { Surface tempSurface (surface); swap (*this, tempSurface); return *this; } /// /// \brief Scales the image using an scale factor. /// /// \param scaleFactor The scale factor to use to resize the image. /// void Surface::resize (float scaleFactor) { // In GP2X we use already scaled graphics. No need to scale them // by software (too slow.) #if !defined (IS_GP2X_HOST) // Can only resize 32-bit surfaces. assert ( 0 != m_SDLSurface && "Tried to resize an unloaded surface" ); assert ( 32 == m_SDLSurface->format->BitsPerPixel && "Tried to resize a non 32-bit surface." ); // If the scale factor is approximaltey 1.0f, then we don't need // to do anything!. if ( fabs (scaleFactor - 1.0f) < 1e-3 ) { return; } // Create the new scaled surface. SDL_Surface *scaledSDLSurface = SDL_CreateRGBSurface (m_SDLSurface->flags, static_cast(m_SDLSurface->w * scaleFactor), static_cast(m_SDLSurface->h * scaleFactor), m_SDLSurface->format->BitsPerPixel, m_SDLSurface->format->Rmask, m_SDLSurface->format->Gmask, m_SDLSurface->format->Bmask, m_SDLSurface->format->Amask); if ( 0 == scaledSDLSurface ) { throw std::runtime_error (SDL_GetError ()); } // Now, for each pixel in the scaled surface, get the corresponding // coordinate on the original surface and using a bilinear filter, // smooth the color for the scaled pixel. SDL_LockSurface (m_SDLSurface); SDL_LockSurface (scaledSDLSurface); uint32_t *scaledPixels = static_cast(scaledSDLSurface->pixels); uint32_t *originalPixels = static_cast(m_SDLSurface->pixels); int scaledPitch = scaledSDLSurface->pitch / sizeof (uint32_t); int originalPitch = m_SDLSurface->pitch / sizeof (uint32_t); for ( int scaledY = 0 ; scaledY < scaledSDLSurface->h ; ++scaledY ) { float originalY = scaledY / scaleFactor; int destinationY = static_cast(floorf (originalY)); float yRatio = originalY - destinationY; float yOpposite = 1 - yRatio; uint32_t *currentScaledPixel = scaledPixels + scaledY * scaledPitch; for ( int scaledX = 0 ; scaledX < scaledSDLSurface->w ; ++scaledX ) { float scaledAlpha = 0.0f; float scaledBlue = 0.0f; float scaledGreen = 0.0f; float scaledRed = 0.0f; float originalX = scaledX / scaleFactor; int destinationX = static_cast(floorf (originalX)); float xRatio = originalX - destinationX; float xOpposite = 1 - xRatio; uint8_t originalAlpha = 0; uint8_t originalBlue = 0; uint8_t originalGreen = 0; uint8_t originalRed = 0; // Left-top pixel. SDL_GetRGBA (originalPixels[destinationY * originalPitch + destinationX], m_SDLSurface->format, &originalRed, &originalGreen, &originalBlue, &originalAlpha); scaledAlpha += originalAlpha * xOpposite * yOpposite; scaledBlue += originalBlue * xOpposite * yOpposite; scaledGreen += originalGreen * xOpposite * yOpposite; scaledRed += originalRed * xOpposite * yOpposite; // Right-top pixel. SDL_GetRGBA (originalPixels[destinationY * originalPitch + destinationX + 1], m_SDLSurface->format, &originalRed, &originalGreen, &originalBlue, &originalAlpha); scaledAlpha += originalAlpha * xRatio * yOpposite; scaledBlue += originalBlue * xRatio * yOpposite; scaledGreen += originalGreen * xRatio * yOpposite; scaledRed += originalRed * xRatio * yOpposite; // Left-bottom pixel. SDL_GetRGBA (originalPixels[(destinationY + 1) * originalPitch + destinationX], m_SDLSurface->format, &originalRed, &originalGreen, &originalBlue, &originalAlpha); scaledAlpha += originalAlpha * xOpposite * yRatio; scaledBlue += originalBlue * xOpposite * yRatio; scaledGreen += originalGreen * xOpposite * yRatio; scaledRed += originalRed * xOpposite * yRatio; // Right-bottom pixel. SDL_GetRGBA (originalPixels[(destinationY + 1) * originalPitch + destinationX + 1], m_SDLSurface->format, &originalRed, &originalGreen, &originalBlue, &originalAlpha); scaledAlpha += originalAlpha * xRatio * yRatio; scaledBlue += originalBlue * xRatio * yRatio; scaledGreen += originalGreen * xRatio * yRatio; scaledRed += originalRed * xRatio * yRatio; *currentScaledPixel = SDL_MapRGBA (scaledSDLSurface->format, static_cast(scaledRed), static_cast(scaledGreen), static_cast(scaledBlue), static_cast(scaledAlpha)); currentScaledPixel++; } } SDL_UnlockSurface (scaledSDLSurface); SDL_UnlockSurface (m_SDLSurface); // Done. Swap the surfaces and free the original surface. std::swap (m_SDLSurface, scaledSDLSurface); SDL_FreeSurface (scaledSDLSurface); #endif // !IS_GP2X_HOST } /// /// \brief Sets the alpha value. /// /// \param alpha The alpha value to set to the surface. /// void Surface::setAlpha (uint8_t alpha) { SDL_SetAlpha (toSDLSurface (), SDL_SRCALPHA, alpha); } /// /// \brief Sets the transparent color. /// /// \param colorKey The color that will be transparent while blitting. /// void Surface::setColorKey (uint32_t colorKey) { SDL_SetColorKey (m_SDLSurface, SDL_SRCCOLORKEY, colorKey); } /// /// \brief Swaps two surfaces. /// /// \param lhs The left hand side surface of the swap operation. /// \param rhs The right hand side surface of the swap operation. /// void Surface::swap (Surface &lhs, Surface &rhs) { std::swap (lhs.m_SDLSurface, rhs.m_SDLSurface); }