Image.cpp

00001 
00002 //
00003 // SFML - Simple and Fast Multimedia Library
00004 // Copyright (C) 2007 Laurent Gomila (laurent.gom@gmail.com)
00005 //
00006 // This software is provided 'as-is', without any express or implied warranty.
00007 // In no event will the authors be held liable for any damages arising from the use of this software.
00008 //
00009 // Permission is granted to anyone to use this software for any purpose,
00010 // including commercial applications, and to alter it and redistribute it freely,
00011 // subject to the following restrictions:
00012 //
00013 // 1. The origin of this software must not be misrepresented;
00014 //    you must not claim that you wrote the original software.
00015 //    If you use this software in a product, an acknowledgment
00016 //    in the product documentation would be appreciated but is not required.
00017 //
00018 // 2. Altered source versions must be plainly marked as such,
00019 //    and must not be misrepresented as being the original software.
00020 //
00021 // 3. This notice may not be removed or altered from any source distribution.
00022 //
00024 
00026 // Headers
00028 #include <SFML/Graphics/Image.hpp>
00029 #include <SFML/Graphics/ImageLoader.hpp>
00030 #include <SFML/Graphics/GraphicsDevice.hpp>
00031 #include <SFML/Graphics/OpenGL.hpp>
00032 #include <SFML/Window/OpenGLCaps.hpp>
00033 #include <algorithm>
00034 #include <iostream>
00035 #include <vector>
00036 
00037 
00038 namespace sf
00039 {
00043 Image::Image() :
00044 myWidth        (0),
00045 myHeight       (0),
00046 myTextureWidth (0),
00047 myTextureHeight(0),
00048 myGLTexture    (0)
00049 {
00050 
00051 }
00052 
00053 
00057 Image::Image(const Image& Copy) :
00058 VideoResource  (Copy),
00059 myWidth        (Copy.myWidth),
00060 myHeight       (Copy.myHeight),
00061 myTextureWidth (Copy.myTextureWidth),
00062 myTextureHeight(Copy.myTextureHeight),
00063 myPixels       (Copy.myPixels),
00064 myGLTexture    (0)
00065 {
00066     CreateTexture();
00067 }
00068 
00069 
00073 Image::Image(unsigned int Width, unsigned int Height, const Color& Col) :
00074 myWidth        (0),
00075 myHeight       (0),
00076 myTextureWidth (0),
00077 myTextureHeight(0),
00078 myGLTexture    (0)
00079 {
00080     Create(Width, Height, Col);
00081 }
00082 
00083 
00087 Image::Image(unsigned int Width, unsigned int Height, const void* Data) :
00088 myWidth        (0),
00089 myHeight       (0),
00090 myTextureWidth (0),
00091 myTextureHeight(0),
00092 myGLTexture    (0)
00093 {
00094     LoadFromMemory(Width, Height, Data);
00095 }
00096 
00097 
00101 Image::~Image()
00102 {
00103     // Destroy video resources
00104     DestroyVideoResources();
00105 }
00106 
00107 
00111 bool Image::LoadFromFile(const std::string& Filename)
00112 {
00113     // Let the image loader load the image into our pixel array
00114     bool Success = priv::ImageLoader::GetInstance().LoadImageFromFile(Filename, myPixels, myWidth, myHeight);
00115 
00116     if (Success)
00117     {
00118         // Loading succeeded : we can create the texture
00119         CreateTexture();
00120 
00121         return true;
00122     }
00123     else
00124     {
00125         // Loading failed : reset texture values
00126         myWidth         = 0;
00127         myHeight        = 0;
00128         myTextureWidth  = 0;
00129         myTextureHeight = 0;
00130         myGLTexture     = 0;
00131         myPixels.clear();
00132 
00133         return false;
00134     }
00135 }
00136 
00137 
00141 bool Image::SaveToFile(const std::string& Filename) const
00142 {
00143     // Let the image loader save our pixel array into the image
00144     return priv::ImageLoader::GetInstance().SaveImageToFile(Filename, myPixels, myWidth, myHeight);
00145 }
00146 
00147 
00151 void Image::Create(unsigned int Width, unsigned int Height, const Color& Col)
00152 {
00153     // Store the texture dimensions
00154     myWidth  = Width;
00155     myHeight = Height;
00156 
00157     // Recreate the pixel buffer and fill it with the specified color
00158     myPixels.clear();
00159     myPixels.resize(Width * Height, Col.ToRGBA());
00160 
00161     // We can create the texture
00162     CreateTexture();
00163 }
00164 
00165 
00169 void Image::LoadFromMemory(unsigned int Width, unsigned int Height, const void* Data)
00170 {
00171     if (Data)
00172     {
00173         // Store the texture dimensions
00174         myWidth  = Width;
00175         myHeight = Height;
00176 
00177         // Fill the pixel buffer with the specified raw data
00178         const Uint32* Ptr = reinterpret_cast<const Uint32*>(Data);
00179         myPixels.assign(Ptr, Ptr + Width * Height);
00180 
00181         // We can create the texture
00182         CreateTexture();
00183     }
00184     else
00185     {
00186         // No data provided : create a white image
00187         Create(Width, Height, Color(255, 255, 255, 255));
00188     }
00189 }
00190 
00191 
00195 void Image::CreateMaskFromColor(const Color& ColorKey, Uint8 Alpha)
00196 {
00197     // Calculate new color (old color with no alpha)
00198     Uint32 OldColor = ColorKey.ToRGBA();
00199     Uint32 NewColor = (OldColor & 0x00FFFFFF) | (Alpha << 24);
00200 
00201     // Replace the old color by the new one
00202     std::replace(myPixels.begin(), myPixels.end(), OldColor, NewColor);
00203 
00204     // Update internal texture
00205     Update();
00206 }
00207 
00208 
00213 void Image::Resize(unsigned int Width, unsigned int Height, const Color& Col)
00214 {
00215     // Check size
00216     if ((Width == 0) || (Height == 0))
00217     {
00218         std::cerr << "Invalid new size for image (width = " << Width << ", height = " << Height << ")" << std::endl;
00219         return;
00220     }
00221 
00222     // Create a new pixel array with the desired size
00223     std::vector<Uint32> Pixels(Width * Height, Col.ToRGBA());
00224 
00225     // Copy the old pixel buffer into the new one
00226     for (unsigned int i = 0; i < std::min(Width, myWidth); ++i)
00227         for (unsigned int j = 0; j < std::min(Height, myHeight); ++j)
00228             Pixels[i + j * Width] = myPixels[i + j * myWidth];
00229     Pixels.swap(myPixels);
00230 
00231     // Store the new texture dimensions
00232     myWidth  = Width;
00233     myHeight = Height;
00234 
00235     // We can create the texture
00236     CreateTexture();
00237 }
00238 
00239 
00244 void Image::SetPixel(unsigned int X, unsigned int Y, const Color& Col)
00245 {
00246     // Check if pixel is whithin the image bounds
00247     if ((X >= myWidth) || (Y >= myHeight))
00248     {
00249         std::cerr << "Cannot set pixel (" << X << "," << Y << ") for image "
00250                   << "(width = " << myWidth << ", height = " << myHeight << ")" << std::endl;
00251         return;
00252     }
00253 
00254     myPixels[X + Y * myWidth] = Col.ToRGBA();
00255 }
00256 
00257 
00261 Color Image::GetPixel(unsigned int X, unsigned int Y) const
00262 {
00263     // Check if pixel is whithin the image bounds
00264     if ((X >= myWidth) || (Y >= myHeight))
00265     {
00266         std::cerr << "Cannot get pixel (" << X << "," << Y << ") for image "
00267                   << "(width = " << myWidth << ", height = " << myHeight << ")" << std::endl;
00268         return Color::Black;
00269     }
00270 
00271     return Color(myPixels[X + Y * myWidth]);
00272 }
00273 
00274 
00280 const Uint32* Image::GetPixelsPtr() const
00281 {
00282     if (!myPixels.empty())
00283     {
00284         return &myPixels[0];
00285     }
00286     else
00287     {
00288         std::cerr << "Trying to access the pixels of an empty image" << std::endl;
00289         return NULL;
00290     }
00291 }
00292 
00293 
00297 void Image::Update()
00298 {
00299     if (myGLTexture && myWidth && myHeight && !myPixels.empty())
00300     {
00301         // Update texture pixels
00302         GLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
00303         GLCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, myWidth, myHeight, GL_RGBA, GL_UNSIGNED_BYTE, &myPixels[0]));
00304         GLCheck(glBindTexture(GL_TEXTURE_2D, 0));
00305     }
00306 }
00307 
00308 
00312 void Image::Bind() const
00313 {
00314     // Bind the OpenGL texture
00315     if (myGLTexture)
00316     {
00317         GLCheck(glEnable(GL_TEXTURE_2D));
00318         GLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
00319     }
00320 }
00321 
00322 
00326 void Image::SetSmooth(bool Smooth) const
00327 {
00328     if (myGLTexture)
00329     {
00330         // Change OpenGL texture filter
00331         GLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
00332         GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Smooth ? GL_LINEAR : GL_NEAREST));
00333         GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Smooth ? GL_LINEAR : GL_NEAREST));
00334         GLCheck(glBindTexture(GL_TEXTURE_2D, 0));
00335     }
00336 }
00337 
00338 
00343 void Image::SetRepeat(bool Repeat) const
00344 {
00345     if (myGLTexture)
00346     {
00347         // Change OpenGL texture wrap mode
00348         GLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
00349         GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, Repeat ? GL_REPEAT : GL_CLAMP));
00350         GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, Repeat ? GL_REPEAT : GL_CLAMP));
00351         GLCheck(glBindTexture(GL_TEXTURE_2D, 0));
00352     }
00353 }
00354 
00355 
00359 unsigned int Image::GetWidth() const
00360 {
00361     return myWidth;
00362 }
00363 
00364 
00368 unsigned int Image::GetHeight() const
00369 {
00370     return myHeight;
00371 }
00372 
00373 
00378 FloatRect Image::GetTexCoords(const IntRect& Rect) const
00379 {
00380     return FloatRect((Rect.Left   + 0.5f) / myTextureWidth,
00381                      (Rect.Top    + 0.5f) / myTextureHeight,
00382                      (Rect.Right  - 0.5f) / myTextureWidth,
00383                      (Rect.Bottom - 0.5f) / myTextureHeight);
00384 }
00385 
00386 
00390 unsigned int Image::GetValidTextureSize(unsigned int Size)
00391 {
00392     if (OpenGLCaps::CheckExtension("GL_ARB_texture_non_power_of_two"))
00393     {
00394         // If hardware supports NPOT textures, then just return the unmodified size
00395         return Size;
00396     }
00397     else
00398     {
00399         // If hardware doesn't support NPOT textures, we calculate the nearest power of two
00400         unsigned int PowerOfTwo = 1;
00401         while (PowerOfTwo < Size)
00402             PowerOfTwo *= 2;
00403 
00404         return PowerOfTwo;
00405     }
00406 }
00407 
00408 
00412 Image& Image::operator =(const Image& Other)
00413 {
00414     Image Temp(Other);
00415 
00416     std::swap(myWidth,         Temp.myWidth);
00417     std::swap(myHeight,        Temp.myHeight);
00418     std::swap(myTextureWidth,  Temp.myTextureWidth);
00419     std::swap(myTextureHeight, Temp.myTextureHeight);
00420     std::swap(myGLTexture,     Temp.myGLTexture);
00421     myPixels.swap(Temp.myPixels);
00422 
00423     return *this;
00424 }
00425 
00426 
00430 void Image::CreateTexture()
00431 {
00432     // Check if texture parameters are valid before creating it
00433     if (!myWidth || !myHeight || myPixels.empty())
00434         return;
00435 
00436     // Destroy previous OpenGL texture if it was loaded
00437     if (myGLTexture)
00438     {
00439         GLCheck(glDeleteTextures(1, (GLuint*)&myGLTexture));
00440         myGLTexture = 0;
00441     }
00442 
00443     // Adjust internal texture dimensions depending on NPOT textures support
00444     myTextureWidth  = GetValidTextureSize(myWidth);
00445     myTextureHeight = GetValidTextureSize(myHeight);
00446 
00447     // Create the OpenGL texture
00448     GLCheck(glGenTextures(1, (GLuint*)&myGLTexture));
00449     GLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
00450     GLCheck(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, myTextureWidth, myTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL));
00451 
00452     // Set texture parameters for 2D rendering
00453     GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,     GL_REPEAT));
00454     GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,     GL_REPEAT));
00455     GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
00456     GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
00457 
00458     // Copy the pixel buffer to the OpenGL texture
00459     Update();
00460 }
00461 
00462 
00466 void Image::DestroyVideoResources()
00467 {
00468     // Destroy textures
00469     if (myGLTexture)
00470     {
00471         GLCheck(glDeleteTextures(1, (GLuint*)&myGLTexture));
00472         myGLTexture = 0;
00473     }
00474 }
00475 
00476 } // namespace sf