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 
00041 sfImage::sfImage() :
00042 myWidth        (0),
00043 myHeight       (0),
00044 myTextureWidth (0),
00045 myTextureHeight(0),
00046 myGLTexture    (0)
00047 {
00048 
00049 }
00050 
00051 
00055 sfImage::sfImage(const sfImage& Copy) :
00056 myWidth        (Copy.myWidth),
00057 myHeight       (Copy.myHeight),
00058 myTextureWidth (Copy.myTextureWidth),
00059 myTextureHeight(Copy.myTextureHeight),
00060 myPixels       (Copy.myPixels),
00061 myGLTexture    (0)
00062 {
00063     CreateTexture();
00064 }
00065 
00066 
00070 sfImage::sfImage(unsigned int Width, unsigned int Height, const sfColor& Color) :
00071 myWidth        (0),
00072 myHeight       (0),
00073 myTextureWidth (0),
00074 myTextureHeight(0),
00075 myGLTexture    (0)
00076 {
00077     Create(Width, Height, Color);
00078 }
00079 
00080 
00084 sfImage::sfImage(unsigned int Width, unsigned int Height, const void* Data) :
00085 myWidth        (0),
00086 myHeight       (0),
00087 myTextureWidth (0),
00088 myTextureHeight(0),
00089 myGLTexture    (0)
00090 {
00091     LoadFromMemory(Width, Height, Data);
00092 }
00093 
00094 
00098 sfImage::~sfImage()
00099 {
00100     // Destroy video resources
00101     DestroyVideoResources();
00102 }
00103 
00104 
00108 bool sfImage::LoadFromFile(const std::string& Filename)
00109 {
00110     // Let the image loader load the image into our pixel array
00111     bool Success = sf_private::sfImageLoader::GetInstance().LoadImageFromFile(Filename, myPixels, myWidth, myHeight);
00112 
00113     if (Success)
00114     {
00115         // Loading succeeded : we can create the texture
00116         CreateTexture();
00117 
00118         return true;
00119     }
00120     else
00121     {
00122         // Loading failed : reset texture values
00123         myWidth         = 0;
00124         myHeight        = 0;
00125         myTextureWidth  = 0;
00126         myTextureHeight = 0;
00127         myGLTexture     = 0;
00128         myPixels.clear();
00129 
00130         return false;
00131     }
00132 }
00133 
00134 
00138 bool sfImage::SaveToFile(const std::string& Filename) const
00139 {
00140     // Let the image loader save our pixel array into the image
00141     return sf_private::sfImageLoader::GetInstance().SaveImageToFile(Filename, myPixels, myWidth, myHeight);
00142 }
00143 
00144 
00148 void sfImage::Create(unsigned int Width, unsigned int Height, const sfColor& Color)
00149 {
00150     // Store the texture dimensions
00151     myWidth  = Width;
00152     myHeight = Height;
00153 
00154     // Recreate the pixel buffer and fill it with the specified color
00155     myPixels.clear();
00156     myPixels.resize(Width * Height, Color.ToRGBA());
00157 
00158     // We can create the texture
00159     CreateTexture();
00160 }
00161 
00162 
00166 void sfImage::LoadFromMemory(unsigned int Width, unsigned int Height, const void* Data)
00167 {
00168     if (Data)
00169     {
00170         // Store the texture dimensions
00171         myWidth  = Width;
00172         myHeight = Height;
00173 
00174         // Fill the pixel buffer with the specified raw data
00175         const sfUint32* Ptr = reinterpret_cast<const sfUint32*>(Data);
00176         myPixels.assign(Ptr, Ptr + Width * Height);
00177 
00178         // We can create the texture
00179         CreateTexture();
00180     }
00181     else
00182     {
00183         // No data provided : create a white image
00184         Create(Width, Height, sfColor(255, 255, 255, 255));
00185     }
00186 }
00187 
00188 
00192 void sfImage::CreateMaskFromColor(const sfColor& ColorKey)
00193 {
00194     // Calculate new color (old color with no alpha)
00195     sfUint32 OldColor = ColorKey.ToRGBA();
00196     sfUint32 NewColor = OldColor & 0x00FFFFFF;
00197 
00198     // Replace the old color by the new one
00199     std::replace(myPixels.begin(), myPixels.end(), OldColor, NewColor);
00200 
00201     // Update internal texture
00202     Update();
00203 }
00204 
00205 
00210 void sfImage::Resize(unsigned int Width, unsigned int Height, const sfColor& Color)
00211 {
00212     // Check size
00213     if ((Width == 0) || (Height == 0))
00214     {
00215         std::cerr << "Invalid new size for image (width = " << Width << ", height = " << Height << ")" << std::endl;
00216         return;
00217     }
00218 
00219     // Create a new pixel array with the desired size
00220     std::vector<sfUint32> Pixels(Width * Height, Color.ToRGBA());
00221 
00222     // Copy the old pixel buffer into the new one
00223     for (unsigned int i = 0; i < std::min(Width, myWidth); ++i)
00224         for (unsigned int j = 0; j < std::min(Height, myHeight); ++j)
00225             Pixels[i + j * Width] = myPixels[i + j * myWidth];
00226     Pixels.swap(myPixels);
00227 
00228     // Store the new texture dimensions
00229     myWidth  = Width;
00230     myHeight = Height;
00231 
00232     // We can create the texture
00233     CreateTexture();
00234 }
00235 
00236 
00241 void sfImage::SetPixel(unsigned int X, unsigned int Y, const sfColor& Color)
00242 {
00243     // Check if pixel is whithin the image bounds
00244     if ((X >= myWidth) || (Y >= myHeight))
00245     {
00246         std::cerr << "Cannot set pixel (" << X << "," << Y << ") for image "
00247                   << "(width = " << myWidth << ", height = " << myHeight << ")" << std::endl;
00248         return;
00249     }
00250 
00251     myPixels[X + Y * myWidth] = Color.ToRGBA();
00252 }
00253 
00254 
00258 sfColor sfImage::GetPixel(unsigned int X, unsigned int Y) const
00259 {
00260     // Check if pixel is whithin the image bounds
00261     if ((X >= myWidth) || (Y >= myHeight))
00262     {
00263         std::cerr << "Cannot get pixel (" << X << "," << Y << ") for image "
00264                   << "(width = " << myWidth << ", height = " << myHeight << ")" << std::endl;
00265         return sfColor::Black;
00266     }
00267 
00268     return sfColor(myPixels[X + Y * myWidth]);
00269 }
00270 
00271 
00277 const sfUint32* sfImage::GetPixelsPtr() const
00278 {
00279     if (!myPixels.empty())
00280     {
00281         return &myPixels[0];
00282     }
00283     else
00284     {
00285         std::cerr << "Trying to access the pixels of an empty image" << std::endl;
00286         return NULL;
00287     }
00288 }
00289 
00290 
00294 void sfImage::Update()
00295 {
00296     if (myGLTexture && myWidth && myHeight && !myPixels.empty())
00297     {
00298         // Update texture pixels
00299         sfGLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
00300         sfGLCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, myWidth, myHeight, GL_RGBA, GL_UNSIGNED_BYTE, &myPixels[0]));
00301         sfGLCheck(glBindTexture(GL_TEXTURE_2D, 0));
00302     }
00303 }
00304 
00305 
00309 void sfImage::Bind() const
00310 {
00311     // Bind the OpenGL texture
00312     if (myGLTexture)
00313         sfGLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
00314 }
00315 
00316 
00320 void sfImage::SetSmooth(bool Smooth) const
00321 {
00322     if (myGLTexture)
00323     {
00324         // Change OpenGL texture filter
00325         sfGLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
00326         sfGLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, Smooth ? GL_LINEAR : GL_NEAREST));
00327         sfGLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Smooth ? GL_LINEAR : GL_NEAREST));
00328         sfGLCheck(glBindTexture(GL_TEXTURE_2D, 0));
00329     }
00330 }
00331 
00332 
00337 void sfImage::SetRepeat(bool Repeat) const
00338 {
00339     if (myGLTexture)
00340     {
00341         // Change OpenGL texture wrap mode
00342         sfGLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
00343         sfGLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, Repeat ? GL_REPEAT : GL_CLAMP));
00344         sfGLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, Repeat ? GL_REPEAT : GL_CLAMP));
00345         sfGLCheck(glBindTexture(GL_TEXTURE_2D, 0));
00346     }
00347 }
00348 
00349 
00353 unsigned int sfImage::GetWidth() const
00354 {
00355     return myWidth;
00356 }
00357 
00358 
00362 unsigned int sfImage::GetHeight() const
00363 {
00364     return myHeight;
00365 }
00366 
00367 
00372 sfFloatRect sfImage::GetTexCoords(const sfIntRect& Rect) const
00373 {
00374     return sfFloatRect((Rect.Left   + 0.5f) / myTextureWidth,
00375                        (Rect.Top    + 0.5f) / myTextureHeight,
00376                        (Rect.Right  - 0.5f) / myTextureWidth,
00377                        (Rect.Bottom - 0.5f) / myTextureHeight);
00378 }
00379 
00380 
00384 unsigned int sfImage::GetValidTextureSize(unsigned int Size)
00385 {
00386     if (sfOpenGLCaps::CheckExtension("GL_ARB_texture_non_power_of_two"))
00387     {
00388         // If hardware supports NPOT textures, then just return the unmodified size
00389         return Size;
00390     }
00391     else
00392     {
00393         // If hardware doesn't support NPOT textures, we calculate the nearest power of two
00394         unsigned int PowerOfTwo = 1;
00395         while (PowerOfTwo < Size)
00396             PowerOfTwo *= 2;
00397 
00398         return PowerOfTwo;
00399     }
00400 }
00401 
00402 
00406 sfImage& sfImage::operator =(const sfImage& Other)
00407 {
00408     sfImage Temp(Other);
00409 
00410     std::swap(myWidth,         Temp.myWidth);
00411     std::swap(myHeight,        Temp.myHeight);
00412     std::swap(myTextureWidth,  Temp.myTextureWidth);
00413     std::swap(myTextureHeight, Temp.myTextureHeight);
00414     std::swap(myGLTexture,     Temp.myGLTexture);
00415     myPixels.swap(Temp.myPixels);
00416 
00417     return *this;
00418 }
00419 
00420 
00424 void sfImage::CreateTexture()
00425 {
00426     // Check if texture parameters are valid before creating it
00427     if (!myWidth || !myHeight || myPixels.empty())
00428         return;
00429 
00430     // Destroy previous OpenGL texture if it was loaded
00431     if (myGLTexture)
00432     {
00433         sfGLCheck(glDeleteTextures(1, &myGLTexture));
00434         myGLTexture = 0;
00435     }
00436 
00437     // Adjust internal texture dimensions depending on NPOT textures support
00438     myTextureWidth  = GetValidTextureSize(myWidth);
00439     myTextureHeight = GetValidTextureSize(myHeight);
00440 
00441     // Create the OpenGL texture
00442     sfGLCheck(glGenTextures(1, &myGLTexture));
00443     sfGLCheck(glBindTexture(GL_TEXTURE_2D, myGLTexture));
00444     sfGLCheck(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, myTextureWidth, myTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL));
00445 
00446     // Set texture parameters for 2D rendering
00447     sfGLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,     GL_REPEAT));
00448     sfGLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,     GL_REPEAT));
00449     sfGLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
00450     sfGLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
00451 
00452     // Copy the pixel buffer to the OpenGL texture
00453     Update();
00454 }
00455 
00456 
00460 void sfImage::DestroyVideoResources()
00461 {
00462     // Destroy textures
00463     if (myGLTexture)
00464     {
00465         sfGLCheck(glDeleteTextures(1, &myGLTexture));
00466         myGLTexture = 0;
00467     }
00468 }