Split sf::Image into sf::Image and sf::Texture (implements issue #18)

This commit is contained in:
Laurent Gomila 2011-07-22 22:31:27 +02:00
parent d337a98321
commit e509f01180
40 changed files with 1585 additions and 1294 deletions

View file

@ -39,14 +39,10 @@ Sound::Sound()
////////////////////////////////////////////////////////////
Sound::Sound(const SoundBuffer& buffer, bool loop, float pitch, float volume, const Vector3f& position) :
Sound::Sound(const SoundBuffer& buffer) :
myBuffer(NULL)
{
SetBuffer(buffer);
SetLoop(loop);
SetPitch(pitch);
SetVolume(volume);
SetPosition(position);
}

View file

@ -25,14 +25,14 @@ set(SRC
${INCROOT}/Rect.inl
${SRCROOT}/Renderer.cpp
${INCROOT}/Renderer.hpp
${SRCROOT}/RenderImage.cpp
${INCROOT}/RenderImage.hpp
${SRCROOT}/RenderImageImpl.cpp
${SRCROOT}/RenderImageImpl.hpp
${SRCROOT}/RenderImageImplFBO.cpp
${SRCROOT}/RenderImageImplFBO.hpp
${SRCROOT}/RenderImageImplDefault.cpp
${SRCROOT}/RenderImageImplDefault.hpp
${SRCROOT}/RenderTexture.cpp
${INCROOT}/RenderTexture.hpp
${SRCROOT}/RenderTextureImpl.cpp
${SRCROOT}/RenderTextureImpl.hpp
${SRCROOT}/RenderTextureImplFBO.cpp
${SRCROOT}/RenderTextureImplFBO.hpp
${SRCROOT}/RenderTextureImplDefault.cpp
${SRCROOT}/RenderTextureImplDefault.hpp
${SRCROOT}/RenderTarget.cpp
${INCROOT}/RenderTarget.hpp
${SRCROOT}/RenderWindow.cpp
@ -45,6 +45,8 @@ set(SRC
${INCROOT}/Sprite.hpp
${SRCROOT}/Text.cpp
${INCROOT}/Text.hpp
${SRCROOT}/Texture.cpp
${INCROOT}/Texture.hpp
${SRCROOT}/View.cpp
${INCROOT}/View.hpp
${SRCROOT}/stb_image/stb_image.h

View file

@ -33,20 +33,6 @@
namespace sf
{
////////////////////////////////////////////////////////////
Drawable::Drawable(const Vector2f& position, const Vector2f& scale, float rotation, const Color& color) :
myPosition (position),
myScale (scale),
myOrigin (0, 0),
myRotation (rotation),
myColor (color),
myBlendMode (Blend::Alpha),
myMatrixUpdated (false),
myInvMatrixUpdated(false)
{
}
////////////////////////////////////////////////////////////
Drawable::~Drawable()
{
@ -267,6 +253,20 @@ Vector2f Drawable::TransformToGlobal(const Vector2f& point) const
}
////////////////////////////////////////////////////////////
Drawable::Drawable() :
myPosition (0, 0),
myScale (1, 1),
myOrigin (0, 0),
myRotation (0),
myColor (255, 255, 255, 255),
myBlendMode (Blend::Alpha),
myMatrixUpdated (false),
myInvMatrixUpdated(false)
{
}
////////////////////////////////////////////////////////////
const Matrix3& Drawable::GetMatrix() const
{

View file

@ -309,7 +309,7 @@ int Font::GetLineSpacing(unsigned int characterSize) const
////////////////////////////////////////////////////////////
const Image& Font::GetImage(unsigned int characterSize) const
const Texture& Font::GetTexture(unsigned int characterSize) const
{
return myPages[characterSize].Texture;
}
@ -493,12 +493,11 @@ Glyph Font::LoadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) c
}
// Write the pixels to the texture
IntRect subrect = glyph.SubRect;
subrect.Left += padding;
subrect.Top += padding;
subrect.Width -= 2 * padding;
subrect.Height -= 2 * padding;
page.Texture.UpdatePixels(&myPixelBuffer[0], subrect);
unsigned int x = glyph.SubRect.Left + padding;
unsigned int y = glyph.SubRect.Top + padding;
unsigned int width = glyph.SubRect.Width - 2 * padding;
unsigned int height = glyph.SubRect.Height - 2 * padding;
page.Texture.Update(&myPixelBuffer[0], width, height, x, y);
}
// Delete the FT glyph
@ -545,19 +544,17 @@ IntRect Font::FindGlyphRect(Page& page, unsigned int width, unsigned int height)
// Not enough space: resize the texture if possible
unsigned int textureWidth = page.Texture.GetWidth();
unsigned int textureHeight = page.Texture.GetHeight();
if ((textureWidth * 2 <= Image::GetMaximumSize()) && (textureHeight * 2 <= Image::GetMaximumSize()))
if ((textureWidth * 2 <= Texture::GetMaximumSize()) && (textureHeight * 2 <= Texture::GetMaximumSize()))
{
// Make the texture 2 times bigger
std::size_t size = textureWidth * textureHeight * 4;
std::vector<Uint8> pixels(size);
memcpy(&pixels[0], page.Texture.GetPixelsPtr(), size);
page.Texture.Create(textureWidth * 2, textureHeight * 2, Color(255, 255, 255, 0));
page.Texture.UpdatePixels(&pixels[0], IntRect(0, 0, textureWidth, textureHeight));
sf::Image pixels = page.Texture.CopyToImage();
page.Texture.Create(textureWidth * 2, textureHeight * 2);
page.Texture.Update(pixels);
}
else
{
// Oops, we've reached the maximum texture size...
Err() << "Failed to add a new character to the font: the maximum image size has been reached" << std::endl;
Err() << "Failed to add a new character to the font: the maximum texture size has been reached" << std::endl;
return IntRect(0, 0, 2, 2);
}
}
@ -603,13 +600,17 @@ Font::Page::Page() :
NextRow(2)
{
// Make sure that the texture is initialized by default
Texture.Create(128, 128, Color(255, 255, 255, 0));
Texture.SetSmooth(true);
sf::Image image;
image.Create(128, 128, Color(255, 255, 255, 0));
// Reserve a 2x2 white square for texturing underlines
for (int x = 0; x < 2; ++x)
for (int y = 0; y < 2; ++y)
Texture.SetPixel(x, y, Color(255, 255, 255, 255));
image.SetPixel(x, y, Color(255, 255, 255, 255));
// Create the texture
Texture.LoadFromImage(image);
Texture.SetSmooth(true);
}
} // namespace sf

View file

@ -27,12 +27,8 @@
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/ImageLoader.hpp>
#include <SFML/Graphics/RenderImage.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#include <vector>
#include <cstring>
@ -40,54 +36,56 @@ namespace sf
{
////////////////////////////////////////////////////////////
Image::Image() :
myWidth (0),
myHeight (0),
myTextureWidth (0),
myTextureHeight (0),
myTexture (0),
myIsSmooth (false),
myTextureUpdated(true),
myArrayUpdated (true),
myPixelsFlipped (false)
myWidth (0),
myHeight(0)
{
}
////////////////////////////////////////////////////////////
Image::Image(const Image& copy) :
Resource<Image>()
void Image::Create(unsigned int width, unsigned int height, const Color& color)
{
// First make sure that the source image is up-to-date
copy.EnsureArrayUpdate();
// Assign the new size
myWidth = width;
myHeight = height;
// Copy all its members
myWidth = copy.myWidth;
myHeight = copy.myHeight;
myTextureWidth = copy.myTextureWidth;
myTextureHeight = copy.myTextureHeight;
myTexture = 0;
myIsSmooth = copy.myIsSmooth;
myPixels = copy.myPixels;
myTextureUpdated = true;
myArrayUpdated = true;
myPixelsFlipped = false; // pixels can't be flipped, this is handled in copy.EnsureArrayUpdate()
// Resize the pixel buffer
myPixels.resize(width * height * 4);
// Create the texture
CreateTexture(myWidth, myHeight);
}
////////////////////////////////////////////////////////////
Image::~Image()
{
// Destroy the OpenGL texture
if (myTexture)
// Fill it with the specified color
Uint8* ptr = &myPixels[0];
Uint8* end = ptr + myPixels.size();
while (ptr < end)
{
EnsureGlContext();
*ptr++ = color.r;
*ptr++ = color.g;
*ptr++ = color.b;
*ptr++ = color.a;
}
}
GLuint Texture = static_cast<GLuint>(myTexture);
GLCheck(glDeleteTextures(1, &Texture));
////////////////////////////////////////////////////////////
void Image::Create(unsigned int width, unsigned int height, const Uint8* pixels)
{
if (pixels)
{
// Assign the new size
myWidth = width;
myHeight = height;
// Copy the pixels
std::size_t size = width * height * 4;
myPixels.resize(size);
std::memcpy(&myPixels[0], pixels, size); // faster than vector::assign
}
else
{
// Create an empty image
myWidth = 0;
myHeight = 0;
myPixels.clear();
}
}
@ -95,142 +93,48 @@ Image::~Image()
////////////////////////////////////////////////////////////
bool Image::LoadFromFile(const std::string& filename)
{
// Forward the job to the image loader
std::vector<Uint8> pixels;
unsigned int width;
unsigned int height;
if (priv::ImageLoader::GetInstance().LoadImageFromFile(filename, pixels, width, height))
{
// Loading succeeded : we can create the texture
if (CreateTexture(width, height))
{
// Copy the pixels
myPixels.swap(pixels);
return true;
}
}
return false;
return priv::ImageLoader::GetInstance().LoadImageFromFile(filename, myPixels, myWidth, myHeight);
}
////////////////////////////////////////////////////////////
bool Image::LoadFromMemory(const void* data, std::size_t size)
{
// Forward the job to the image loader
std::vector<Uint8> pixels;
unsigned int width;
unsigned int height;
if (priv::ImageLoader::GetInstance().LoadImageFromMemory(data, size, pixels, width, height))
{
// Loading succeeded : we can create the texture
if (CreateTexture(width, height))
{
// Copy the pixels
myPixels.swap(pixels);
return true;
}
}
return false;
return priv::ImageLoader::GetInstance().LoadImageFromMemory(data, size, myPixels, myWidth, myHeight);
}
////////////////////////////////////////////////////////////
bool Image::LoadFromStream(InputStream& stream)
{
// Forward the job to the image loader
std::vector<Uint8> pixels;
unsigned int width;
unsigned int height;
if (priv::ImageLoader::GetInstance().LoadImageFromStream(stream, pixels, width, height))
{
// Loading succeeded : we can create the texture
if (CreateTexture(width, height))
{
// Copy the pixels
myPixels.swap(pixels);
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////
bool Image::LoadFromPixels(unsigned int width, unsigned int height, const Uint8* data)
{
if (data)
{
// Create the internal texture
if (CreateTexture(width, height))
{
// Copy the pixels
std::size_t size = width * height * 4;
myPixels.resize(size);
std::memcpy(&myPixels[0], data, size); // faster than vector::assign
return true;
}
else
{
return false;
}
}
else
{
// No data provided : create a white image
return Create(width, height, Color(255, 255, 255));
}
return priv::ImageLoader::GetInstance().LoadImageFromStream(stream, myPixels, myWidth, myHeight);
}
////////////////////////////////////////////////////////////
bool Image::SaveToFile(const std::string& filename) const
{
// Check if the array of pixels needs to be updated
EnsureArrayUpdate();
// Let the image loader save our pixel array into the image
return priv::ImageLoader::GetInstance().SaveImageToFile(filename, myPixels, myWidth, myHeight);
}
////////////////////////////////////////////////////////////
bool Image::Create(unsigned int width, unsigned int height, const Color& color)
unsigned int Image::GetWidth() const
{
// First create the texture
if (CreateTexture(width, height))
{
// Resize the pixel buffer
myPixels.resize(width * height * 4);
return myWidth;
}
// Fill it with the specified color
Uint8* ptr = &myPixels[0];
Uint8* end = ptr + myPixels.size();
while (ptr < end)
{
*ptr++ = color.r;
*ptr++ = color.g;
*ptr++ = color.b;
*ptr++ = color.a;
}
return true;
}
else
{
return false;
}
////////////////////////////////////////////////////////////
unsigned int Image::GetHeight() const
{
return myHeight;
}
////////////////////////////////////////////////////////////
void Image::CreateMaskFromColor(const Color& color, Uint8 alpha)
{
// Check if the array of pixels needs to be updated
EnsureArrayUpdate();
// Make sure that the image is not empty
if (!myPixels.empty())
{
@ -243,9 +147,6 @@ void Image::CreateMaskFromColor(const Color& color, Uint8 alpha)
ptr[3] = alpha;
ptr += 4;
}
// The texture will need to be updated
myTextureUpdated = false;
}
}
@ -257,10 +158,6 @@ void Image::Copy(const Image& source, unsigned int destX, unsigned int destY, co
if ((source.myWidth == 0) || (source.myHeight == 0) || (myWidth == 0) || (myHeight == 0))
return;
// Make sure both images have up-to-date arrays
EnsureArrayUpdate();
source.EnsureArrayUpdate();
// Adjust the source rectangle
IntRect srcRect = sourceRect;
if (srcRect.Width == 0 || (srcRect.Height == 0))
@ -330,84 +227,24 @@ void Image::Copy(const Image& source, unsigned int destX, unsigned int destY, co
dstPixels += dstStride;
}
}
// The texture will need an update
myTextureUpdated = false;
}
////////////////////////////////////////////////////////////
bool Image::CopyScreen(RenderWindow& window, const IntRect& sourceRect)
{
// Adjust the source rectangle
IntRect rect = sourceRect;
if (rect.Width == 0 || (rect.Height == 0))
{
rect.Left = 0;
rect.Top = 0;
rect.Width = window.GetWidth();
rect.Height = window.GetHeight();
}
else
{
if (rect.Left < 0) rect.Left = 0;
if (rect.Top < 0) rect.Top = 0;
if (rect.Width > static_cast<int>(window.GetWidth())) rect.Width = window.GetWidth();
if (rect.Height > static_cast<int>(window.GetHeight())) rect.Height = window.GetHeight();
}
// We can then create the texture
if (window.SetActive() && CreateTexture(rect.Width, rect.Height))
{
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
int y = window.GetHeight() - (rect.Top + rect.Height); // Y axis is inverted
GLCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rect.Left, y, myWidth, myHeight));
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
myTextureUpdated = true;
myArrayUpdated = false;
myPixelsFlipped = true;
return true;
}
else
{
return false;
}
}
////////////////////////////////////////////////////////////
void Image::SetPixel(unsigned int x, unsigned int y, const Color& color)
{
// First check if the array of pixels needs to be updated
EnsureArrayUpdate();
// Copy the color components
Uint8* pixel = &myPixels[(x + y * myWidth) * 4];
*pixel++ = color.r;
*pixel++ = color.g;
*pixel++ = color.b;
*pixel++ = color.a;
// The texture will need to be updated
myTextureUpdated = false;
}
////////////////////////////////////////////////////////////
Color Image::GetPixel(unsigned int x, unsigned int y) const
{
// First check if the array of pixels needs to be updated
EnsureArrayUpdate();
// Get the color at (x, y)
const Uint8* pixel = &myPixels[(x + y * myWidth) * 4];
return Color(pixel[0], pixel[1], pixel[2], pixel[3]);
}
@ -415,9 +252,6 @@ Color Image::GetPixel(unsigned int x, unsigned int y) const
////////////////////////////////////////////////////////////
const Uint8* Image::GetPixelsPtr() const
{
// First check if the array of pixels needs to be updated
EnsureArrayUpdate();
if (!myPixels.empty())
{
return &myPixels[0];
@ -429,339 +263,4 @@ const Uint8* Image::GetPixelsPtr() const
}
}
////////////////////////////////////////////////////////////
void Image::UpdatePixels(const Uint8* pixels)
{
if (pixels && myTexture)
{
EnsureGlContext();
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
// Update the texture from the array of pixels
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, myWidth, myHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
myArrayUpdated = false;
myTextureUpdated = true;
}
}
////////////////////////////////////////////////////////////
void Image::UpdatePixels(const Uint8* pixels, const IntRect& rectangle)
{
// Make sure that the texture is up-to-date
EnsureTextureUpdate();
if (pixels && myTexture)
{
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
// Update the texture from the array of pixels
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
// The pixel cache is no longer up-to-date
myArrayUpdated = false;
}
}
////////////////////////////////////////////////////////////
void Image::Bind() const
{
// First check if the texture needs to be updated
EnsureTextureUpdate();
// Bind it
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
}
////////////////////////////////////////////////////////////
void Image::SetSmooth(bool smooth)
{
if (smooth != myIsSmooth)
{
myIsSmooth = smooth;
if (myTexture)
{
EnsureGlContext();
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST));
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
}
}
}
////////////////////////////////////////////////////////////
unsigned int Image::GetWidth() const
{
return myWidth;
}
////////////////////////////////////////////////////////////
unsigned int Image::GetHeight() const
{
return myHeight;
}
////////////////////////////////////////////////////////////
bool Image::IsSmooth() const
{
return myIsSmooth;
}
////////////////////////////////////////////////////////////
FloatRect Image::GetTexCoords(const IntRect& rect) const
{
if ((myTextureWidth != 0) && (myTextureHeight != 0))
{
float width = static_cast<float>(myTextureWidth);
float height = static_cast<float>(myTextureHeight);
if (myPixelsFlipped)
{
return FloatRect( rect.Left / width,
(myHeight - rect.Top) / height,
rect.Width / width,
-rect.Height / height);
}
else
{
return FloatRect(rect.Left / width,
rect.Top / height,
rect.Width / width,
rect.Height / height);
}
}
else
{
return FloatRect(0, 0, 0, 0);
}
}
////////////////////////////////////////////////////////////
unsigned int Image::GetMaximumSize()
{
EnsureGlContext();
GLint size;
GLCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size));
return static_cast<unsigned int>(size);
}
////////////////////////////////////////////////////////////
Image& Image::operator =(const Image& right)
{
Image temp(right);
std::swap(myWidth, temp.myWidth);
std::swap(myHeight, temp.myHeight);
std::swap(myTextureWidth, temp.myTextureWidth);
std::swap(myTextureHeight, temp.myTextureHeight);
std::swap(myTexture, temp.myTexture);
std::swap(myIsSmooth, temp.myIsSmooth);
std::swap(myArrayUpdated, temp.myArrayUpdated);
std::swap(myTextureUpdated, temp.myTextureUpdated);
std::swap(myPixelsFlipped, temp.myPixelsFlipped);
std::swap(myPixels, temp.myPixels);
return *this;
}
////////////////////////////////////////////////////////////
unsigned int Image::GetValidSize(unsigned int size)
{
EnsureGlContext();
// Make sure that GLEW is initialized
priv::EnsureGlewInit();
if (GLEW_ARB_texture_non_power_of_two)
{
// If hardware supports NPOT textures, then just return the unmodified size
return size;
}
else
{
// If hardware doesn't support NPOT textures, we calculate the nearest power of two
unsigned int powerOfTwo = 1;
while (powerOfTwo < size)
powerOfTwo *= 2;
return powerOfTwo;
}
}
////////////////////////////////////////////////////////////
bool Image::CreateTexture(unsigned int width, unsigned int height)
{
// Check if texture parameters are valid before creating it
if (!width || !height)
return false;
// Compute the internal texture dimensions depending on NPOT textures support
unsigned int textureWidth = GetValidSize(width);
unsigned int textureHeight = GetValidSize(height);
// Check the maximum texture size
unsigned int maxSize = GetMaximumSize();
if ((textureWidth > maxSize) || (textureHeight > maxSize))
{
Err() << "Failed to create image, its internal size is too high "
<< "(" << textureWidth << "x" << textureHeight << ", "
<< "maximum is " << maxSize << "x" << maxSize << ")"
<< std::endl;
return false;
}
// All the validity checks passed, we can store the new texture settings
myWidth = width;
myHeight = height;
myTextureWidth = textureWidth;
myTextureHeight = textureHeight;
EnsureGlContext();
// Create the OpenGL texture if it doesn't exist yet
if (!myTexture)
{
GLuint texture;
GLCheck(glGenTextures(1, &texture));
myTexture = static_cast<unsigned int>(texture);
}
// Initialize the texture
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, myTextureWidth, myTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST));
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
myTextureUpdated = false;
return true;
}
////////////////////////////////////////////////////////////
void Image::EnsureTextureUpdate() const
{
if (!myTextureUpdated)
{
if (myTexture && !myPixels.empty())
{
EnsureGlContext();
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
// Update the texture with the pixels array in RAM
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, myWidth, myHeight, GL_RGBA, GL_UNSIGNED_BYTE, &myPixels[0]));
myPixelsFlipped = false;
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
}
myTextureUpdated = true;
}
}
////////////////////////////////////////////////////////////
void Image::EnsureArrayUpdate() const
{
if (!myArrayUpdated)
{
EnsureGlContext();
// Save the previous texture
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
// Resize the destination array of pixels
myPixels.resize(myWidth * myHeight * 4);
if ((myWidth == myTextureWidth) && (myHeight == myTextureHeight) && !myPixelsFlipped)
{
// Texture and array have the same size, we can use a direct copy
// Copy pixels from texture to array
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &myPixels[0]));
}
else
{
// Texture and array don't have the same size, we have to use a slower algorithm
// All the pixels will first be copied to a temporary array
ColorArray allPixels(myTextureWidth * myTextureHeight * 4);
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &allPixels[0]));
// Then we copy the useful pixels from the temporary array to the final one
const Uint8* src = &allPixels[0];
Uint8* dst = &myPixels[0];
int srcPitch = myTextureWidth * 4;
int dstPitch = myWidth * 4;
// Handle the case where source pixels are flipped vertically
if (myPixelsFlipped)
{
src += srcPitch * (myHeight - 1);
srcPitch = -srcPitch;
}
for (unsigned int i = 0; i < myHeight; ++i)
{
std::memcpy(dst, src, dstPitch);
src += srcPitch;
dst += dstPitch;
}
}
// Restore the previous texture
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
myArrayUpdated = true;
}
}
////////////////////////////////////////////////////////////
void Image::Use() const
{
EnsureTextureUpdate();
}
} // namespace sf

View file

@ -25,57 +25,57 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderImage.hpp>
#include <SFML/Graphics/RenderImageImplFBO.hpp>
#include <SFML/Graphics/RenderImageImplDefault.hpp>
#include <SFML/Graphics/RenderTexture.hpp>
#include <SFML/Graphics/RenderTextureImplFBO.hpp>
#include <SFML/Graphics/RenderTextureImplDefault.hpp>
#include <SFML/System/Err.hpp>
namespace sf
{
////////////////////////////////////////////////////////////
RenderImage::RenderImage() :
myRenderImage(NULL)
RenderTexture::RenderTexture() :
myRenderTexture(NULL)
{
}
////////////////////////////////////////////////////////////
RenderImage::~RenderImage()
RenderTexture::~RenderTexture()
{
delete myRenderImage;
delete myRenderTexture;
}
////////////////////////////////////////////////////////////
bool RenderImage::Create(unsigned int width, unsigned int height, bool depthBuffer)
bool RenderTexture::Create(unsigned int width, unsigned int height, bool depthBuffer)
{
// Create the image
if (!myImage.Create(width, height))
// Create the texture
if (!myTexture.Create(width, height))
{
Err() << "Impossible to create render image (failed to create the target image)" << std::endl;
Err() << "Impossible to create render texture (failed to create the target texture)" << std::endl;
return false;
}
// We disable smoothing by default for render images
// We disable smoothing by default for render textures
SetSmooth(false);
// Create the implementation
delete myRenderImage;
if (priv::RenderImageImplFBO::IsAvailable())
delete myRenderTexture;
if (priv::RenderTextureImplFBO::IsAvailable())
{
// Use frame-buffer object (FBO)
myRenderImage = new priv::RenderImageImplFBO;
myRenderTexture = new priv::RenderTextureImplFBO;
}
else
{
// Use default implementation
myRenderImage = new priv::RenderImageImplDefault;
myRenderTexture = new priv::RenderTextureImplDefault;
}
// Initialize the render image
if (!myRenderImage->Create(width, height, myImage.myTexture, depthBuffer))
// Initialize the render texture
if (!myRenderTexture->Create(width, height, myTexture.myTexture, depthBuffer))
return false;
// We can now initialize the render target part
@ -86,64 +86,61 @@ bool RenderImage::Create(unsigned int width, unsigned int height, bool depthBuff
////////////////////////////////////////////////////////////
void RenderImage::SetSmooth(bool smooth)
void RenderTexture::SetSmooth(bool smooth)
{
myImage.SetSmooth(smooth);
myTexture.SetSmooth(smooth);
}
////////////////////////////////////////////////////////////
bool RenderImage::IsSmooth() const
bool RenderTexture::IsSmooth() const
{
return myImage.IsSmooth();
return myTexture.IsSmooth();
}
////////////////////////////////////////////////////////////
bool RenderImage::SetActive(bool active)
bool RenderTexture::SetActive(bool active)
{
return myRenderImage && myRenderImage->Activate(active);
return myRenderTexture && myRenderTexture->Activate(active);
}
////////////////////////////////////////////////////////////
void RenderImage::Display()
void RenderTexture::Display()
{
// Update the target image
// Update the target texture
if (SetActive(true))
{
myRenderImage->UpdateTexture(myImage.myTexture);
myImage.myPixelsFlipped = true;
myImage.myArrayUpdated = false;
myImage.myTextureUpdated = true;
myRenderTexture->UpdateTexture(myTexture.myTexture);
myTexture.myPixelsFlipped = true;
}
}
////////////////////////////////////////////////////////////
unsigned int RenderImage::GetWidth() const
unsigned int RenderTexture::GetWidth() const
{
return myImage.GetWidth();
return myTexture.GetWidth();
}
////////////////////////////////////////////////////////////
unsigned int RenderImage::GetHeight() const
unsigned int RenderTexture::GetHeight() const
{
return myImage.GetHeight();
return myTexture.GetHeight();
}
////////////////////////////////////////////////////////////
const Image& RenderImage::GetImage() const
const Texture& RenderTexture::GetTexture() const
{
return myImage;
return myTexture;
}
////////////////////////////////////////////////////////////
bool RenderImage::Activate(bool active)
bool RenderTexture::Activate(bool active)
{
return SetActive(active);
}

View file

@ -25,7 +25,7 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderImageImpl.hpp>
#include <SFML/Graphics/RenderTextureImpl.hpp>
namespace sf
@ -33,7 +33,7 @@ namespace sf
namespace priv
{
////////////////////////////////////////////////////////////
RenderImageImpl::~RenderImageImpl()
RenderTextureImpl::~RenderTextureImpl()
{
// Nothing to do
}

View file

@ -22,8 +22,8 @@
//
////////////////////////////////////////////////////////////
#ifndef SFML_RENDERIMAGEIMPL_HPP
#define SFML_RENDERIMAGEIMPL_HPP
#ifndef SFML_RENDERTEXTUREIMPL_HPP
#define SFML_RENDERTEXTUREIMPL_HPP
////////////////////////////////////////////////////////////
// Headers
@ -36,10 +36,10 @@ namespace sf
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Abstract base class for render-image implementations
/// \brief Abstract base class for render-texture implementations
///
////////////////////////////////////////////////////////////
class RenderImageImpl : NonCopyable
class RenderTextureImpl : NonCopyable
{
public :
@ -47,14 +47,14 @@ public :
/// \brief Destructor
///
////////////////////////////////////////////////////////////
virtual ~RenderImageImpl();
virtual ~RenderTextureImpl();
////////////////////////////////////////////////////////////
/// \brief Create the render image implementation
/// \brief Create the render texture implementation
///
/// \param width Width of the image to render to
/// \param height Height of the image to render to
/// \param textureId OpenGL texture identifier of the target image
/// \param width Width of the texture to render to
/// \param height Height of the texture to render to
/// \param textureId OpenGL identifier of the target texture
/// \param depthBuffer Is a depth buffer requested?
///
/// \return True if creation has been successful
@ -63,7 +63,7 @@ public :
virtual bool Create(unsigned int width, unsigned int height, unsigned int textureId, bool depthBuffer) = 0;
////////////////////////////////////////////////////////////
/// \brief Activate or deactivate the render image for rendering
/// \brief Activate or deactivate the render texture for rendering
///
/// \param active True to activate, false to deactivate
///
@ -86,4 +86,4 @@ public :
} // namespace sf
#endif // SFML_RENDERIMAGEIMPL_HPP
#endif // SFML_RENDERTEXTUREIMPL_HPP

View file

@ -25,7 +25,7 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderImageImplDefault.hpp>
#include <SFML/Graphics/RenderTextureImplDefault.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/System/Err.hpp>
@ -36,7 +36,7 @@ namespace sf
namespace priv
{
////////////////////////////////////////////////////////////
RenderImageImplDefault::RenderImageImplDefault() :
RenderTextureImplDefault::RenderTextureImplDefault() :
myContext(0),
myWidth (0),
myHeight (0)
@ -46,7 +46,7 @@ myHeight (0)
////////////////////////////////////////////////////////////
RenderImageImplDefault::~RenderImageImplDefault()
RenderTextureImplDefault::~RenderTextureImplDefault()
{
// Destroy the context
delete myContext;
@ -54,33 +54,33 @@ RenderImageImplDefault::~RenderImageImplDefault()
////////////////////////////////////////////////////////////
bool RenderImageImplDefault::Create(unsigned int width, unsigned int height, unsigned int, bool depthBuffer)
bool RenderTextureImplDefault::Create(unsigned int width, unsigned int height, unsigned int, bool depthBuffer)
{
// Store the dimensions
myWidth = width;
myHeight = height;
// Create the in-memory OpenGL context
myContext = new Context(ContextSettings(depthBuffer ? 32 : 0, 0, 4), width, height);
myContext = new Context(ContextSettings(depthBuffer ? 32 : 0), width, height);
return true;
}
////////////////////////////////////////////////////////////
bool RenderImageImplDefault::Activate(bool active)
bool RenderTextureImplDefault::Activate(bool active)
{
return myContext->SetActive(active);
}
////////////////////////////////////////////////////////////
void RenderImageImplDefault::UpdateTexture(unsigned int textureId)
void RenderTextureImplDefault::UpdateTexture(unsigned int textureId)
{
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
// Copy the rendered pixels to the image
// Copy the rendered pixels to the texture
GLCheck(glBindTexture(GL_TEXTURE_2D, textureId));
GLCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, myWidth, myHeight));

View file

@ -22,13 +22,13 @@
//
////////////////////////////////////////////////////////////
#ifndef SFML_RENDERIMAGEIMPLDEFAULT_HPP
#define SFML_RENDERIMAGEIMPLDEFAULT_HPP
#ifndef SFML_RENDERTEXTUREIMPLDEFAULT_HPP
#define SFML_RENDERTEXTUREIMPLDEFAULT_HPP
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderImageImpl.hpp>
#include <SFML/Graphics/RenderTextureImpl.hpp>
#include <SFML/Window/GlResource.hpp>
#include <SFML/Window/Context.hpp>
@ -38,11 +38,11 @@ namespace sf
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Default specialization of RenderImageImpl,
/// \brief Default specialization of RenderTextureImpl,
/// using a in-memory context
///
////////////////////////////////////////////////////////////
class RenderImageImplDefault : public RenderImageImpl, GlResource
class RenderTextureImplDefault : public RenderTextureImpl, GlResource
{
public :
@ -50,22 +50,22 @@ public :
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
RenderImageImplDefault();
RenderTextureImplDefault();
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
~RenderImageImplDefault();
~RenderTextureImplDefault();
private :
////////////////////////////////////////////////////////////
/// \brief Create the render image implementation
/// \brief Create the render texture implementation
///
/// \param width Width of the image to render to
/// \param height Height of the image to render to
/// \param textureId OpenGL texture identifier of the target image
/// \param width Width of the texture to render to
/// \param height Height of the texture to render to
/// \param textureId OpenGL identifier of the target texture
/// \param depthBuffer Is a depth buffer requested?
///
/// \return True if creation has been successful
@ -74,7 +74,7 @@ private :
virtual bool Create(unsigned int width, unsigned int height, unsigned int textureId, bool depthBuffer);
////////////////////////////////////////////////////////////
/// \brief Activate or deactivate the render image for rendering
/// \brief Activate or deactivate the render texture for rendering
///
/// \param active True to activate, false to deactivate
///
@ -104,4 +104,4 @@ private :
} // namespace sf
#endif // SFML_RENDERIMAGEIMPLDEFAULT_HPP
#endif // SFML_RENDERTEXTUREIMPLDEFAULT_HPP

View file

@ -25,8 +25,8 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderImageImplFBO.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/RenderTextureImplFBO.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/System/Err.hpp>
@ -36,7 +36,7 @@ namespace sf
namespace priv
{
////////////////////////////////////////////////////////////
RenderImageImplFBO::RenderImageImplFBO() :
RenderTextureImplFBO::RenderTextureImplFBO() :
myFrameBuffer(0),
myDepthBuffer(0)
{
@ -45,7 +45,7 @@ myDepthBuffer(0)
////////////////////////////////////////////////////////////
RenderImageImplFBO::~RenderImageImplFBO()
RenderTextureImplFBO::~RenderTextureImplFBO()
{
EnsureGlContext();
@ -69,7 +69,7 @@ RenderImageImplFBO::~RenderImageImplFBO()
////////////////////////////////////////////////////////////
bool RenderImageImplFBO::IsAvailable()
bool RenderTextureImplFBO::IsAvailable()
{
EnsureGlContext();
@ -81,7 +81,7 @@ bool RenderImageImplFBO::IsAvailable()
////////////////////////////////////////////////////////////
bool RenderImageImplFBO::Create(unsigned int width, unsigned int height, unsigned int textureId, bool depthBuffer)
bool RenderTextureImplFBO::Create(unsigned int width, unsigned int height, unsigned int textureId, bool depthBuffer)
{
//Create the context
myContext = new Context;
@ -92,7 +92,7 @@ bool RenderImageImplFBO::Create(unsigned int width, unsigned int height, unsigne
myFrameBuffer = static_cast<unsigned int>(frameBuffer);
if (!myFrameBuffer)
{
Err() << "Impossible to create render image (failed to create the frame buffer object)" << std::endl;
Err() << "Impossible to create render texture (failed to create the frame buffer object)" << std::endl;
return false;
}
GLCheck(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, myFrameBuffer));
@ -105,7 +105,7 @@ bool RenderImageImplFBO::Create(unsigned int width, unsigned int height, unsigne
myDepthBuffer = static_cast<unsigned int>(depth);
if (!myDepthBuffer)
{
Err() << "Impossible to create render image (failed to create the attached depth buffer)" << std::endl;
Err() << "Impossible to create render texture (failed to create the attached depth buffer)" << std::endl;
return false;
}
GLCheck(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, myDepthBuffer));
@ -113,14 +113,14 @@ bool RenderImageImplFBO::Create(unsigned int width, unsigned int height, unsigne
GLCheck(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, myDepthBuffer));
}
// Link the image to the frame buffer
// Link the texture to the frame buffer
GLCheck(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, textureId, 0));
// A final check, just to be sure...
if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
{
GLCheck(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
Err() << "Impossible to create render image (failed to link the target image to the frame buffer)" << std::endl;
Err() << "Impossible to create render texture (failed to link the target texture to the frame buffer)" << std::endl;
return false;
}
@ -129,13 +129,13 @@ bool RenderImageImplFBO::Create(unsigned int width, unsigned int height, unsigne
////////////////////////////////////////////////////////////
bool RenderImageImplFBO::Activate(bool active)
bool RenderTextureImplFBO::Activate(bool active)
{
return myContext->SetActive(active);
}
////////////////////////////////////////////////////////////
void RenderImageImplFBO::UpdateTexture(unsigned int)
void RenderTextureImplFBO::UpdateTexture(unsigned int)
{
glFlush();
}

View file

@ -22,13 +22,13 @@
//
////////////////////////////////////////////////////////////
#ifndef SFML_RENDERIMAGEIMPLFBO_HPP
#define SFML_RENDERIMAGEIMPLFBO_HPP
#ifndef SFML_RENDERTEXTUREIMPLFBO_HPP
#define SFML_RENDERTEXTUREIMPLFBO_HPP
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderImageImpl.hpp>
#include <SFML/Graphics/RenderTextureImpl.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/Window/GlResource.hpp>
@ -38,11 +38,11 @@ namespace sf
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Specialization of RenderImageImpl using the
/// Frame Buffer Object OpenGL extension
/// \brief Specialization of RenderTextureImpl using the
/// FrameBuffer Object OpenGL extension
///
////////////////////////////////////////////////////////////
class RenderImageImplFBO : public RenderImageImpl, GlResource
class RenderTextureImplFBO : public RenderTextureImpl, GlResource
{
public :
@ -50,18 +50,18 @@ public :
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
RenderImageImplFBO();
RenderTextureImplFBO();
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
~RenderImageImplFBO();
~RenderTextureImplFBO();
////////////////////////////////////////////////////////////
/// \brief Check whether the system supports FBOs or not
///
/// \return True if FBO render images are supported
/// \return True if FBO render textures are supported
///
////////////////////////////////////////////////////////////
static bool IsAvailable();
@ -69,11 +69,11 @@ public :
private :
////////////////////////////////////////////////////////////
/// \brief Create the render image implementation
/// \brief Create the render texture implementation
///
/// \param width Width of the image to render to
/// \param height Height of the image to render to
/// \param textureId OpenGL texture identifier of the target image
/// \param width Width of the texture to render to
/// \param height Height of the texture to render to
/// \param textureId OpenGL identifier of the target texture
/// \param depthBuffer Is a depth buffer requested?
///
/// \return True if creation has been successful
@ -82,7 +82,7 @@ private :
virtual bool Create(unsigned int width, unsigned int height, unsigned int textureId, bool depthBuffer);
////////////////////////////////////////////////////////////
/// \brief Activate or deactivate the render image for rendering
/// \brief Activate or deactivate the render texture for rendering
///
/// \param active True to activate, false to deactivate
///
@ -112,4 +112,4 @@ private :
} // namespace sf
#endif // SFML_RENDERIMAGEIMPLFBO_HPP
#endif // SFML_RENDERTEXTUREIMPLFBO_HPP

View file

@ -26,6 +26,7 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/GLCheck.hpp>
namespace sf
@ -81,6 +82,30 @@ unsigned int RenderWindow::GetHeight() const
}
////////////////////////////////////////////////////////////
Image RenderWindow::Capture() const
{
Image image;
if (SetActive())
{
int width = static_cast<int>(GetWidth());
int height = static_cast<int>(GetHeight());
// copy rows one by one and flip them (OpenGL's origin is bottom while SFML's origin is top)
std::vector<Uint8> pixels(width * height * 4);
for (int i = 0; i < height; ++i)
{
Uint8* ptr = &pixels[i * width * 4];
GLCheck(glReadPixels(0, height - i - 1, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, ptr));
}
image.Create(width, height, &pixels[0]);
}
return image;
}
////////////////////////////////////////////////////////////
void RenderWindow::OnCreate()
{

View file

@ -27,7 +27,7 @@
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Renderer.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Shader.hpp>
#include <SFML/Graphics/GLCheck.hpp>
@ -202,7 +202,7 @@ void Renderer::SetBlendMode(Blend::Mode mode)
{
// Alpha blending
// glBlendFuncSeparateEXT is used when available to avoid an incorrect alpha value when the target
// is a RenderImage -- in this case the alpha value must be written directly to the target buffer
// is a RenderTexture -- in this case the alpha value must be written directly to the target buffer
default :
case Blend::Alpha :
if (GLEW_EXT_blend_func_separate)
@ -231,7 +231,7 @@ void Renderer::SetBlendMode(Blend::Mode mode)
////////////////////////////////////////////////////////////
void Renderer::SetTexture(const Image* texture)
void Renderer::SetTexture(const Texture* texture)
{
if ((texture != myTexture) || (texture && (texture->myTexture != myTextureId)) || !myTextureIsValid)
{
@ -246,12 +246,6 @@ void Renderer::SetTexture(const Image* texture)
myTextureId = texture ? texture->myTexture : 0;
myTextureIsValid = true;
}
else if (texture && myTextureIsValid)
{
// If the texture was already the current one, make sure that
// it is synchronized (in case it was modified since last use)
texture->Use();
}
}

View file

@ -225,7 +225,7 @@ void Shader::SetParameter(const std::string& name, const Vector3f& v)
////////////////////////////////////////////////////////////
void Shader::SetTexture(const std::string& name, const Image& texture)
void Shader::SetTexture(const std::string& name, const Texture& texture)
{
if (myShaderProgram)
{

View file

@ -26,7 +26,7 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Renderer.hpp>
#include <utility>
@ -35,6 +35,7 @@ namespace sf
{
////////////////////////////////////////////////////////////
Sprite::Sprite() :
Drawable (),
mySubRect (0, 0, 1, 1),
myIsFlippedX(false),
myIsFlippedY(false)
@ -44,31 +45,31 @@ myIsFlippedY(false)
////////////////////////////////////////////////////////////
Sprite::Sprite(const Image& image, const Vector2f& position, const Vector2f& scale, float rotation, const Color& color) :
Drawable (position, scale, rotation, color),
Sprite::Sprite(const Texture& texture) :
Drawable (),
mySubRect (0, 0, 1, 1),
myIsFlippedX(false),
myIsFlippedY(false)
{
SetImage(image);
SetTexture(texture);
}
////////////////////////////////////////////////////////////
void Sprite::SetImage(const Image& image, bool adjustToNewSize)
void Sprite::SetTexture(const Texture& texture, bool adjustToNewSize)
{
// If there was no valid image before, force adjusting to the new image size
if (!myImage)
// If there was no valid texture before, force adjusting to the new texture size
if (!myTexture)
adjustToNewSize = true;
// If we want to adjust the size and the new image is valid, we adjust the source rectangle
if (adjustToNewSize && (image.GetWidth() > 0) && (image.GetHeight() > 0))
// If we want to adjust the size and the new texture is valid, we adjust the source rectangle
if (adjustToNewSize && (texture.GetWidth() > 0) && (texture.GetHeight() > 0))
{
SetSubRect(IntRect(0, 0, image.GetWidth(), image.GetHeight()));
SetSubRect(IntRect(0, 0, texture.GetWidth(), texture.GetHeight()));
}
// Assign the new image
myImage = &image;
// Assign the new texture
myTexture = &texture;
}
@ -109,9 +110,9 @@ void Sprite::FlipY(bool flipped)
////////////////////////////////////////////////////////////
const Image* Sprite::GetImage() const
const Texture* Sprite::GetTexture() const
{
return myImage;
return myTexture;
}
@ -129,26 +130,6 @@ Vector2f Sprite::GetSize() const
}
////////////////////////////////////////////////////////////
Color Sprite::GetPixel(unsigned int x, unsigned int y) const
{
if (myImage)
{
unsigned int imageX = mySubRect.Left + x;
unsigned int imageY = mySubRect.Top + y;
if (myIsFlippedX) imageX = mySubRect.Width - imageX - 1;
if (myIsFlippedY) imageY = mySubRect.Height - imageY - 1;
return myImage->GetPixel(imageX, imageY) * GetColor();
}
else
{
return GetColor();
}
}
////////////////////////////////////////////////////////////
void Sprite::Render(RenderTarget&, Renderer& renderer) const
{
@ -156,10 +137,10 @@ void Sprite::Render(RenderTarget&, Renderer& renderer) const
float width = static_cast<float>(mySubRect.Width);
float height = static_cast<float>(mySubRect.Height);
// Check if the image is valid, and calculate the texture coordinates
// Check if the texture is valid, and calculate the texture coordinates
FloatRect coords;
if (myImage)
coords = myImage->GetTexCoords(mySubRect);
if (myTexture)
coords = myTexture->GetTexCoords(mySubRect);
// Compute the texture coordinates
float left = coords.Left;
@ -170,7 +151,7 @@ void Sprite::Render(RenderTarget&, Renderer& renderer) const
if (myIsFlippedY) std::swap(top, bottom);
// Bind the texture
renderer.SetTexture(myImage);
renderer.SetTexture(myTexture);
// Draw the sprite's geometry
renderer.Begin(Renderer::TriangleStrip);

View file

@ -26,7 +26,7 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Text.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Renderer.hpp>
@ -190,7 +190,7 @@ void Text::Render(RenderTarget&, Renderer& renderer) const
return;
// Bind the font texture
const Image& texture = myFont->GetImage(myCharacterSize);
const Texture& texture = myFont->GetTexture(myCharacterSize);
renderer.SetTexture(&texture);
// Computes values related to the text style

View file

@ -0,0 +1,506 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Window/Window.hpp>
#include <SFML/System/Err.hpp>
#include <cassert>
#include <cstring>
namespace sf
{
////////////////////////////////////////////////////////////
Texture::Texture() :
myWidth (0),
myHeight (0),
myTextureWidth (0),
myTextureHeight(0),
myTexture (0),
myIsSmooth (false),
myPixelsFlipped(false)
{
}
////////////////////////////////////////////////////////////
Texture::Texture(const Texture& copy) :
Resource<Texture>(),
myWidth (0),
myHeight (0),
myTextureWidth (0),
myTextureHeight(0),
myTexture (0),
myIsSmooth (false),
myPixelsFlipped(false)
{
LoadFromImage(copy.CopyToImage());
}
////////////////////////////////////////////////////////////
Texture::~Texture()
{
// Destroy the OpenGL texture
if (myTexture)
{
EnsureGlContext();
GLuint Texture = static_cast<GLuint>(myTexture);
GLCheck(glDeleteTextures(1, &Texture));
}
}
////////////////////////////////////////////////////////////
bool Texture::Create(unsigned int width, unsigned int height)
{
// Check if texture parameters are valid before creating it
if (!width || !height)
{
Err() << "Failed to create texture, invalid size (" << width << "x" << height << ")" << std::endl;
return false;
}
// Compute the internal texture dimensions depending on NPOT textures support
unsigned int textureWidth = GetValidSize(width);
unsigned int textureHeight = GetValidSize(height);
// Check the maximum texture size
unsigned int maxSize = GetMaximumSize();
if ((textureWidth > maxSize) || (textureHeight > maxSize))
{
Err() << "Failed to create texture, its internal size is too high "
<< "(" << textureWidth << "x" << textureHeight << ", "
<< "maximum is " << maxSize << "x" << maxSize << ")"
<< std::endl;
return false;
}
// All the validity checks passed, we can store the new texture settings
myWidth = width;
myHeight = height;
myTextureWidth = textureWidth;
myTextureHeight = textureHeight;
myPixelsFlipped = false;
EnsureGlContext();
// Create the OpenGL texture if it doesn't exist yet
if (!myTexture)
{
GLuint texture;
GLCheck(glGenTextures(1, &texture));
myTexture = static_cast<unsigned int>(texture);
}
// Save the current texture binding
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
// Initialize the texture
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, myTextureWidth, myTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST));
// Restore the previous texture
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
return true;
}
////////////////////////////////////////////////////////////
bool Texture::LoadFromFile(const std::string& filename, const IntRect& area)
{
Image image;
return image.LoadFromFile(filename) && LoadFromImage(image, area);
}
////////////////////////////////////////////////////////////
bool Texture::LoadFromMemory(const void* data, std::size_t size, const IntRect& area)
{
Image image;
return image.LoadFromMemory(data, size) && LoadFromImage(image, area);
}
////////////////////////////////////////////////////////////
bool Texture::LoadFromStream(InputStream& stream, const IntRect& area)
{
Image image;
return image.LoadFromStream(stream) && LoadFromImage(image, area);
}
////////////////////////////////////////////////////////////
bool Texture::LoadFromImage(const Image& image, const IntRect& area)
{
// Retrieve the image size
int width = static_cast<int>(image.GetWidth());
int height = static_cast<int>(image.GetHeight());
// Load the entire image if the source area is either empty or contains the whole image
if (area.Width == 0 || (area.Height == 0) ||
((area.Left <= 0) && (area.Top <= 0) && (area.Width >= width) && (area.Height >= height)))
{
// Load the entire image
if (Create(image.GetWidth(), image.GetHeight()))
{
Update(image);
return true;
}
else
{
return false;
}
}
else
{
// Load a sub-area of the image
// Adjust the rectangle to the size of the image
IntRect rectangle = area;
if (rectangle.Left < 0) rectangle.Left = 0;
if (rectangle.Top < 0) rectangle.Top = 0;
if (rectangle.Width > width) rectangle.Width = width;
if (rectangle.Height > height) rectangle.Height = height;
// Create the texture and upload the pixels
if (Create(rectangle.Width, rectangle.Height))
{
// Save the current texture binding
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
// Copy the pixels to the texture, row by row
const Uint8* pixels = image.GetPixelsPtr() + rectangle.Left + (width * rectangle.Top);
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
for (int i = 0; i < rectangle.Height; ++i)
{
GLCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i, myWidth, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
pixels += width;
}
myPixelsFlipped = false;
// Restore the previous texture
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
return true;
}
else
{
return false;
}
}
}
////////////////////////////////////////////////////////////
unsigned int Texture::GetWidth() const
{
return myWidth;
}
////////////////////////////////////////////////////////////
unsigned int Texture::GetHeight() const
{
return myHeight;
}
////////////////////////////////////////////////////////////
Image Texture::CopyToImage() const
{
// Easy case: empty texture
if (!myTexture)
return Image();
EnsureGlContext();
// Save the previous texture
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
// Create an array of pixels
std::vector<Uint8> pixels(myWidth * myHeight * 4);
if ((myWidth == myTextureWidth) && (myHeight == myTextureHeight) && !myPixelsFlipped)
{
// Texture is not padded nor flipped, we can use a direct copy
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]));
}
else
{
// Texture is either padded or flipped, we have to use a slower algorithm
// All the pixels will first be copied to a temporary array
std::vector<Uint8> allPixels(myTextureWidth * myTextureHeight * 4);
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &allPixels[0]));
// Then we copy the useful pixels from the temporary array to the final one
const Uint8* src = &allPixels[0];
Uint8* dst = &pixels[0];
int srcPitch = myTextureWidth * 4;
int dstPitch = myWidth * 4;
// Handle the case where source pixels are flipped vertically
if (myPixelsFlipped)
{
src += srcPitch * (myHeight - 1);
srcPitch = -srcPitch;
}
for (unsigned int i = 0; i < myHeight; ++i)
{
std::memcpy(dst, src, dstPitch);
src += srcPitch;
dst += dstPitch;
}
}
// Restore the previous texture
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
// Create the image
Image image;
image.Create(myWidth, myHeight, &pixels[0]);
return image;
}
////////////////////////////////////////////////////////////
void Texture::Update(const Uint8* pixels)
{
// Update the whole texture
Update(pixels, myWidth, myHeight, 0, 0);
}
////////////////////////////////////////////////////////////
void Texture::Update(const Uint8* pixels, unsigned int width, unsigned int height, unsigned int x, unsigned int y)
{
assert(x + width <= myWidth);
assert(y + height <= myHeight);
if (pixels && myTexture)
{
EnsureGlContext();
// Save the current texture binding
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
// Copy pixels from the given array to the texture
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
myPixelsFlipped = false;
// Restore the previous texture
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
}
}
////////////////////////////////////////////////////////////
void Texture::Update(const Image& image)
{
// Update the whole texture
Update(image.GetPixelsPtr(), image.GetWidth(), image.GetHeight(), 0, 0);
}
////////////////////////////////////////////////////////////
void Texture::Update(const Image& image, unsigned int x, unsigned int y)
{
Update(image.GetPixelsPtr(), image.GetWidth(), image.GetHeight(), x, y);
}
////////////////////////////////////////////////////////////
void Texture::Update(const Window& window)
{
Update(window, 0, 0);
}
////////////////////////////////////////////////////////////
void Texture::Update(const Window& window, unsigned int x, unsigned int y)
{
assert(x + window.GetWidth() <= myWidth);
assert(y + window.GetHeight() <= myHeight);
if (myTexture && window.SetActive(true))
{
// Save the current texture binding
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
// Copy pixels from the back-buffer to the texture
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x, y, 0, 0, window.GetWidth(), window.GetHeight()));
myPixelsFlipped = true;
// Restore the previous texture
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
}
}
////////////////////////////////////////////////////////////
void Texture::Bind() const
{
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
}
////////////////////////////////////////////////////////////
void Texture::SetSmooth(bool smooth)
{
if (smooth != myIsSmooth)
{
myIsSmooth = smooth;
if (myTexture)
{
EnsureGlContext();
GLint previous;
GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous));
GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST));
GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST));
GLCheck(glBindTexture(GL_TEXTURE_2D, previous));
}
}
}
////////////////////////////////////////////////////////////
bool Texture::IsSmooth() const
{
return myIsSmooth;
}
////////////////////////////////////////////////////////////
FloatRect Texture::GetTexCoords(const IntRect& rect) const
{
if ((myTextureWidth != 0) && (myTextureHeight != 0))
{
float width = static_cast<float>(myTextureWidth);
float height = static_cast<float>(myTextureHeight);
if (myPixelsFlipped)
{
return FloatRect( rect.Left / width,
(myHeight - rect.Top) / height,
rect.Width / width,
-rect.Height / height);
}
else
{
return FloatRect(rect.Left / width,
rect.Top / height,
rect.Width / width,
rect.Height / height);
}
}
else
{
return FloatRect(0, 0, 0, 0);
}
}
////////////////////////////////////////////////////////////
unsigned int Texture::GetMaximumSize()
{
EnsureGlContext();
GLint size;
GLCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size));
return static_cast<unsigned int>(size);
}
////////////////////////////////////////////////////////////
Texture& Texture::operator =(const Texture& right)
{
Texture temp(right);
std::swap(myWidth, temp.myWidth);
std::swap(myHeight, temp.myHeight);
std::swap(myTextureWidth, temp.myTextureWidth);
std::swap(myTextureHeight, temp.myTextureHeight);
std::swap(myTexture, temp.myTexture);
std::swap(myIsSmooth, temp.myIsSmooth);
std::swap(myPixelsFlipped, temp.myPixelsFlipped);
return *this;
}
////////////////////////////////////////////////////////////
unsigned int Texture::GetValidSize(unsigned int size)
{
EnsureGlContext();
// Make sure that GLEW is initialized
priv::EnsureGlewInit();
if (GLEW_ARB_texture_non_power_of_two)
{
// If hardware supports NPOT textures, then just return the unmodified size
return size;
}
else
{
// If hardware doesn't support NPOT textures, we calculate the nearest power of two
unsigned int powerOfTwo = 1;
while (powerOfTwo < size)
powerOfTwo *= 2;
return powerOfTwo;
}
}
} // namespace sf