graphics fixes and improvements

This commit is contained in:
Eduardo Bart
2012-06-08 13:58:08 -03:00
parent 4f9ca15ef0
commit 1a7f2a44fc
39 changed files with 450 additions and 317 deletions

View File

@@ -24,7 +24,7 @@
#include "graphics.h"
#include <framework/core/eventdispatcher.h>
/*
AnimatedTexture::AnimatedTexture(int width, int height, int channels, int numFrames, uchar *framesPixels, int *framesDelay) :
Texture(),
m_numFrames(numFrames)
@@ -48,7 +48,7 @@ AnimatedTexture::AnimatedTexture(int width, int height, int channels, int numFra
AnimatedTexture::~AnimatedTexture()
{
glDeleteTextures(m_numFrames, &m_framesTextureId[0]);
m_textureId = 0;
m_id = 0;
}
void AnimatedTexture::enableBilinearFilter()
@@ -65,7 +65,7 @@ void AnimatedTexture::processAnimation()
m_currentFrame++;
if(m_currentFrame >= m_numFrames)
m_currentFrame = 0;
m_textureId = m_framesTextureId[m_currentFrame];
m_id = m_framesTextureId[m_currentFrame];
AnimatedTexturePtr self = asAnimatedTexture();
@@ -73,3 +73,4 @@ void AnimatedTexture::processAnimation()
if(self.use_count() > 1)
g_eventDispatcher.scheduleEvent(std::bind(&AnimatedTexture::processAnimation, self), m_framesDelay[m_currentFrame]);
}
*/

View File

@@ -24,7 +24,7 @@
#define ANIMATEDTEXTURE_H
#include "texture.h"
/*
class AnimatedTexture : public Texture
{
public:
@@ -43,5 +43,5 @@ private:
int m_currentFrame;
ticks_t m_lastAnimCheckTicks;
};
*/
#endif

View File

@@ -63,7 +63,7 @@ void FrameBuffer::resize(const Size& size)
if(m_texture && m_texture->getSize() == size)
return;
m_texture = TexturePtr(new Texture(size.width(), size.height(), 4));
m_texture = TexturePtr(new Texture(size));
m_texture->setSmooth(true);
m_texture->setUpsideDown(true);
@@ -76,8 +76,10 @@ void FrameBuffer::resize(const Size& size)
g_logger.fatal("Unable to setup framebuffer object");
internalRelease();
} else {
m_screenBackup = TexturePtr(new Texture(size.width(), size.height()));
m_screenBackup->setUpsideDown(true);
if(m_backuping) {
m_screenBackup = TexturePtr(new Texture(size));
m_screenBackup->setUpsideDown(true);
}
}
}
@@ -126,7 +128,7 @@ void FrameBuffer::internalBind()
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
m_prevBoundFbo = boundFbo;
boundFbo = m_fbo;
} else {
} else if(m_backuping) {
// backup screen color buffer into a texture
m_screenBackup->copyFromScreen(Rect(0, 0, getSize()));
}
@@ -145,10 +147,12 @@ void FrameBuffer::internalRelease()
m_texture->copyFromScreen(screenRect);
// restore screen original content
Painter::CompositionMode oldComposition = g_painter->getCompositionMode();
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
g_painter->drawTexturedRect(screenRect, m_screenBackup, screenRect);
g_painter->setCompositionMode(oldComposition);
if(m_backuping) {
Painter::CompositionMode oldComposition = g_painter->getCompositionMode();
g_painter->setCompositionMode(Painter::CompositionMode_Replace);
g_painter->drawTexturedRect(screenRect, m_screenBackup, screenRect);
g_painter->setCompositionMode(oldComposition);
}
}
}

View File

@@ -40,8 +40,11 @@ public:
void draw(const Rect& dest);
void draw(const Rect& dest, const Rect& src);
void setBackuping(bool enabled) { m_backuping = enabled; }
TexturePtr getTexture() { return m_texture; }
Size getSize();
bool isBackuping() { return m_backuping; }
private:
void internalCreate();
@@ -53,6 +56,7 @@ private:
Size m_oldViewportSize;
uint m_fbo;
uint m_prevBoundFbo;
Boolean<true> m_backuping;
static uint boundFbo;
};

View File

@@ -92,12 +92,6 @@ void Graphics::init()
if(m_maxTextureSize == -1 || m_maxTextureSize > maxTextureSize)
m_maxTextureSize = maxTextureSize;
// check if we have alpha channel in the color buffer, because we need alpha channel for glCopyTexSubImage2D
GLint alphaBits = 0;
glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
if(alphaBits <= 0)
g_logger.fatal("OpenGL visual doesn't have an alpha buffer");
selectPainterEngine(m_prefferedPainterEngine);
m_emptyTexture = TexturePtr(new Texture);
}
@@ -143,6 +137,8 @@ bool Graphics::parseOption(const std::string& option)
m_useNonPowerOfTwoTextures = false;
else if(option == "-no-clamp-to-edge")
m_useClampToEdge = false;
else if(option == "-no-backbuffer-cache")
m_cacheBackbuffer = false;
else if(option == "-opengl1")
m_prefferedPainterEngine = Painter_OpenGL1;
else if(option == "-opengl2")
@@ -368,3 +364,16 @@ bool Graphics::canUseBlendFuncSeparate()
return true;
#endif
}
bool Graphics::canCacheBackbuffer()
{
#if OPENGL_ES==2
return m_cacheBackbuffer;
#elif OPENGL_ES==1
return false;
#else
if(!GLEW_VERSION_1_4)
return false;
return m_cacheBackbuffer;
#endif
}

View File

@@ -70,6 +70,7 @@ public:
bool canUseHardwareMipmaps();
bool canUseClampToEdge();
bool canUseBlendFuncSeparate();
bool canCacheBackbuffer();
private:
Size m_viewportSize;
@@ -84,6 +85,7 @@ private:
Boolean<true> m_useMipmaps;
Boolean<true> m_useHardwareMipmaps;
Boolean<true> m_useClampToEdge;
Boolean<true> m_cacheBackbuffer;
PainterEngine m_prefferedPainterEngine;
PainterEngine m_selectedPainterEngine;
};

View File

@@ -30,7 +30,7 @@ Image::Image(const Size& size, int bpp, uint8 *pixels)
{
m_size = size;
m_bpp = bpp;
m_pixels.resize(size.area() * bpp);
m_pixels.resize(size.area() * bpp, 0);
if(pixels)
memcpy(&m_pixels[0], pixels, m_pixels.size());
}
@@ -84,8 +84,10 @@ void Image::overwriteMask(const Color& maskedColor, const Color& insideColor, co
}
}
void Image::append(const Point& dest, const ImagePtr& other)
void Image::blit(const Point& dest, const ImagePtr& other)
{
assert(m_bpp == 4);
if(!other)
return;
@@ -103,3 +105,109 @@ void Image::append(const Point& dest, const ImagePtr& other)
}
}
}
void Image::paste(const ImagePtr& other)
{
assert(m_bpp == 4);
if(!other)
return;
uint8* otherPixels = other->getPixelData();
for(int p = 0; p < other->getPixelCount(); ++p) {
int x = p % other->getWidth();
int y = p / other->getWidth();
int pos = (y * m_size.width() + x) * 4;
m_pixels[pos+0] = otherPixels[p*4+0];
m_pixels[pos+1] = otherPixels[p*4+1];
m_pixels[pos+2] = otherPixels[p*4+2];
m_pixels[pos+3] = otherPixels[p*4+3];
}
}
bool Image::nextMipmap()
{
assert(m_bpp == 4);
assert(stdext::is_power_of_two(m_size.width()) && stdext::is_power_of_two(m_size.height()));
if(m_size.width() == 1 || m_size.height() == 1)
return false;
Size size = m_size / 2;
std::vector<uint8> pixels(size.area()*4, 0xFF);
m_pixels = pixels;
m_size = size;
return true;
}
/*
*
void Texture::generateSoftwareMipmaps(std::vector<uint8> inPixels)
{
bind();
assert(stdext::is_power_of_two(m_glSize.width()) && stdext::is_power_of_two(m_glSize.height()));
Size inSize = m_glSize;
Size outSize = inSize / 2;
std::vector<uint8> outPixels;
int mipmap = 1;
while(true) {
outPixels.resize(outSize.area()*4, 0);
// this is a simple bilinear filtering algorithm, it combines every 4 pixels in one pixel
for(int x=0;x<outSize.width();++x) {
for(int y=0;y<outSize.height();++y) {
uint8 *inPixel[4];
inPixel[0] = &inPixels[((y*2)*inSize.width() + (x*2))*4];
inPixel[1] = &inPixels[((y*2)*inSize.width() + (x*2)+1)*4];
inPixel[2] = &inPixels[((y*2+1)*inSize.width() + (x*2))*4];
inPixel[3] = &inPixels[((y*2+1)*inSize.width() + (x*2)+1)*4];
uint8 *outPixel = &outPixels[(y*outSize.width() + x)*4];
int pixelsSum[4];
for(int i=0;i<4;++i)
pixelsSum[i] = 0;
int usedPixels = 0;
for(int j=0;j<4;++j) {
// ignore colors of complete alpha pixels
if(inPixel[j][3] < 16)
continue;
for(int i=0;i<4;++i)
pixelsSum[i] += inPixel[j][i];
usedPixels++;
}
// try to guess the alpha pixel more accurately
for(int i=0;i<4;++i) {
if(usedPixels > 0)
outPixel[i] = pixelsSum[i] / usedPixels;
else
outPixel[i] = 0;
}
outPixel[3] = pixelsSum[3]/4;
}
}
glTexImage2D(GL_TEXTURE_2D, mipmap++, GL_RGBA, outSize.width(), outSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, &outPixels[0]);
if(inSize.width() == 1 || inSize.height() == 1)
break;
inPixels = std::move(outPixels);
inSize /= 2;
outSize /= 2;
}
if(!m_hasMipmaps) {
m_hasMipmaps = true;
setupFilters();
}
}
*/

View File

@@ -35,7 +35,9 @@ public:
static ImagePtr loadPNG(const std::string& file);
void overwriteMask(const Color& maskedColor, const Color& insideColor = Color::white, const Color& outsideColor = Color::alpha);
void append(const Point& dest, const ImagePtr &other);
void blit(const Point& dest, const ImagePtr& other);
void paste(const ImagePtr& other);
bool nextMipmap();
std::vector<uint8>& getPixels() { return m_pixels; }
uint8* getPixelData() { return &m_pixels[0]; }

View File

@@ -27,104 +27,68 @@
Texture::Texture()
{
m_textureId = 0;
m_id = 0;
}
Texture::Texture(const ImagePtr& image)
Texture::Texture(const Size& size)
{
internalLoadGLTexture(image->getPixelData(), image->getBpp(), image->getWidth(), image->getHeight());
m_id = 0;
if(!setupSize(size))
return;
createTexture();
bind();
setupPixels(0, m_glSize, nullptr, 4);
setupWrap();
setupFilters();
}
Texture::Texture(int width, int height, int channels, uchar *pixels)
Texture::Texture(const ImagePtr& image, bool buildMipmaps)
{
// generate opengl texture
internalLoadGLTexture(pixels, channels, width, height);
m_id = 0;
if(!setupSize(image->getSize(), buildMipmaps))
return;
createTexture();
ImagePtr glImage = image;
if(m_size != m_glSize) {
glImage = ImagePtr(new Image(m_glSize, image->getBpp()));
glImage->paste(image);
} else
glImage = image;
bind();
/*
if(buildMipmaps) {
int level = 0;
do {
setupPixels(level++, glImage->getSize(), glImage->getPixelData(), glImage->getBpp());
} while(glImage->nextMipmap());
m_hasMipmaps = true;
} else
*/
setupPixels(0, glImage->getSize(), glImage->getPixelData(), glImage->getBpp());
setupWrap();
setupFilters();
}
Texture::~Texture()
{
// free texture from gl memory
if(m_textureId > 0)
glDeleteTextures(1, &m_textureId);
if(m_id > 0)
glDeleteTextures(1, &m_id);
}
uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int height)
void Texture::bind()
{
m_size.resize(width, height);
// convert texture pixel data to power of two size, only required for OpenGL 1.5 or older
std::vector<uint8> tmp;
if(!g_graphics.canUseNonPowerOfTwoTextures()) {
int glWidth = 1;
while(glWidth < width)
glWidth = glWidth << 1;
int glHeight = 1;
while(glHeight < height)
glHeight = glHeight << 1;
if(m_size != m_glSize && pixels) {
tmp.resize(glHeight*glWidth*channels, 0);
for(int y=0; y<height; ++y)
for(int x=0; x<width; ++x)
for(int i=0; i<channels; ++i)
tmp[y*glWidth*channels+x*channels+i] = pixels[y*width*channels+x*channels+i];
pixels = &tmp[0];
}
m_glSize.resize(glWidth, glHeight);
} else
m_glSize = m_size;
setupTranformMatrix();
// checks texture max size
if(std::max(m_glSize.width(), m_glSize.height()) > g_graphics.getMaxTextureSize()) {
g_logger.error(stdext::format("loading texture with size %dx%d failed, "
"the maximum size allowed by the graphics card is %dx%d,"
"to prevent crashes the texture will be displayed as a blank texture",
width, height, g_graphics.getMaxTextureSize(), g_graphics.getMaxTextureSize()));
//TODO: make a workaround, could be bilinear scaling the texture
return 0;
}
// generate gl texture
GLuint id;
glGenTextures(1, &id);
assert(id != 0);
m_textureId = id;
bind();
// detect pixels GL format
GLenum format = 0;
switch(channels) {
case 4:
format = GL_RGBA;
break;
case 3:
format = GL_RGB;
break;
case 2:
format = GL_LUMINANCE_ALPHA;
break;
case 1:
format = GL_LUMINANCE;
break;
}
// load pixels into gl memory
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_glSize.width(), m_glSize.height(), 0, format, GL_UNSIGNED_BYTE, pixels);
GLint texParam = GL_REPEAT;
if(g_graphics.canUseClampToEdge())
texParam = GL_CLAMP_TO_EDGE; // disable texture borders by default
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParam);
setupFilters();
return id;
// must reset painter texture state
g_painter->setTexture(this);
glBindTexture(GL_TEXTURE_2D, m_id);
}
void Texture::copyFromScreen(const Rect& screenRect)
@@ -133,25 +97,7 @@ void Texture::copyFromScreen(const Rect& screenRect)
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, screenRect.x(), screenRect.y(), screenRect.width(), screenRect.height());
}
void Texture::bind()
{
// must reset painter texture state
g_painter->setTexture(this);
glBindTexture(GL_TEXTURE_2D, m_textureId);
}
void Texture::generateMipmaps()
{
if(!generateHardwareMipmaps()) {
// fallback to software mipmaps generation, this can be slow
//FIXME: disabled because mipmaps size needs to be in base of 2,
// and the current algorithmn does not support that
//generateSoftwareMipmaps(getPixels());
g_logger.traceError("non power of 2.");
}
}
bool Texture::generateHardwareMipmaps()
bool Texture::buildHardwareMipmaps()
{
if(!g_graphics.canUseHardwareMipmaps())
return false;
@@ -188,69 +134,43 @@ void Texture::setUpsideDown(bool upsideDown)
setupTranformMatrix();
}
void Texture::generateSoftwareMipmaps(std::vector<uint8> inPixels)
void Texture::createTexture()
{
bind();
glGenTextures(1, &m_id);
assert(m_id != 0);
}
if(!m_hasMipmaps) {
m_hasMipmaps = true;
setupFilters();
bool Texture::setupSize(const Size& size, bool forcePowerOfTwo)
{
Size glSize;
if(!g_graphics.canUseNonPowerOfTwoTextures() || forcePowerOfTwo)
glSize.resize(stdext::to_power_of_two(size.width()), stdext::to_power_of_two(size.height()));
else
glSize = size;
// checks texture max size
if(std::max(glSize.width(), glSize.height()) > g_graphics.getMaxTextureSize()) {
g_logger.error(stdext::format("loading texture with size %dx%d failed, "
"the maximum size allowed by the graphics card is %dx%d,"
"to prevent crashes the texture will be displayed as a blank texture",
size.width(), size.height(), g_graphics.getMaxTextureSize(), g_graphics.getMaxTextureSize()));
return false;
}
Size inSize = getSize();
Size outSize = inSize / 2;
std::vector<uint8> outPixels;
m_size = size;
m_glSize = glSize;
setupTranformMatrix();
return true;
}
int mipmap = 1;
while(true) {
outPixels.resize(outSize.area()*4);
void Texture::setupWrap()
{
GLint texParam = GL_REPEAT;
if(g_graphics.canUseClampToEdge())
texParam = GL_CLAMP_TO_EDGE; // disable texture borders by default
// this is a simple bilinear filtering algorithm, it combines every 4 pixels in one pixel
for(int x=0;x<outSize.width();++x) {
for(int y=0;y<outSize.height();++y) {
uint8 *inPixel[4];
inPixel[0] = &inPixels[((y*2)*inSize.width() + (x*2))*4];
inPixel[1] = &inPixels[((y*2)*inSize.width() + (x*2)+1)*4];
inPixel[2] = &inPixels[((y*2+1)*inSize.width() + (x*2))*4];
inPixel[3] = &inPixels[((y*2+1)*inSize.width() + (x*2)+1)*4];
uint8 *outPixel = &outPixels[(y*outSize.width() + x)*4];
int pixelsSum[4];
for(int i=0;i<4;++i)
pixelsSum[i] = 0;
int usedPixels = 0;
for(int j=0;j<4;++j) {
// ignore colors of complete alpha pixels
if(inPixel[j][3] < 16)
continue;
for(int i=0;i<4;++i)
pixelsSum[i] += inPixel[j][i];
usedPixels++;
}
// try to guess the alpha pixel more accurately
for(int i=0;i<4;++i) {
if(usedPixels > 0)
outPixel[i] = pixelsSum[i] / usedPixels;
else
outPixel[i] = 0;
}
outPixel[3] = pixelsSum[3]/4;
}
}
glTexImage2D(GL_TEXTURE_2D, mipmap++, GL_RGBA, outSize.width(), outSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, &outPixels[0]);
if(inSize.width() == 1 || inSize.height() == 1)
break;
inPixels = std::move(outPixels);
inSize /= 2;
outSize /= 2;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParam);
}
void Texture::setupFilters()
@@ -280,3 +200,24 @@ void Texture::setupTranformMatrix()
0.0f, 0.0f, 1.0f };
}
}
void Texture::setupPixels(int level, const Size& size, uchar* pixels, int channels)
{
GLenum format = 0;
switch(channels) {
case 4:
format = GL_RGBA;
break;
case 3:
format = GL_RGB;
break;
case 2:
format = GL_LUMINANCE_ALPHA;
break;
case 1:
format = GL_LUMINANCE;
break;
}
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, size.width(), size.height(), 0, format, GL_UNSIGNED_BYTE, pixels);
}

View File

@@ -29,41 +29,35 @@ class Texture : public std::enable_shared_from_this<Texture>
{
public:
Texture();
Texture(const ImagePtr& image);
Texture(int width, int height, int channels = 4, uchar* pixels = NULL);
Texture(const Size& size);
Texture(const ImagePtr& image, bool buildMipmaps = false);
virtual ~Texture();
void copyFromScreen(const Rect& screenRect);
void bind();
void copyFromScreen(const Rect& screenRect);
bool buildHardwareMipmaps();
/// Tries to generate mipmaps via hardware, otherwise fallback to software implementation
void generateMipmaps();
/// Generate mipmaps via hardware if supported
bool generateHardwareMipmaps();
/// Generate mipmaps via software, which has a special algorithm for combining alpha pixels
void generateSoftwareMipmaps(std::vector<uint8> inPixels);
/// Activate texture anti-aliasing giving a better look when they are resized
void setSmooth(bool smooth);
void setUpsideDown(bool upsideDown);
GLuint getId() { return m_textureId; }
GLuint getId() { return m_id; }
int getWidth() { return m_size.width(); }
int getHeight() { return m_size.height(); }
const Size& getSize() { return m_size; }
const Size& getGlSize() { return m_glSize; }
const Matrix3& getTransformMatrix() { return m_transformMatrix; }
bool isEmpty() { return m_textureId == 0; }
bool isEmpty() { return m_id == 0; }
bool hasMipmaps() { return m_hasMipmaps; }
protected:
void createTexture();
bool setupSize(const Size& size, bool forcePowerOfTwo = false);
void setupWrap();
void setupFilters();
void setupTranformMatrix();
GLuint internalLoadGLTexture(uchar* pixels, int channels, int w, int h);
void setupPixels(int level, const Size& size, uchar *pixels, int channels = 4);
GLuint m_textureId;
GLuint m_id;
Size m_size;
Size m_glSize;
Matrix3 m_transformMatrix;

View File

@@ -23,6 +23,7 @@
#include "texturemanager.h"
#include "animatedtexture.h"
#include "graphics.h"
#include "image.h"
#include <framework/core/resourcemanager.h>
#include <framework/thirdparty/apngloader.h>
@@ -75,10 +76,13 @@ TexturePtr TextureManager::loadPNG(std::stringstream& file)
apng_data apng;
if(load_apng(file, &apng) == 0) {
if(apng.num_frames > 1) { // animated texture
uchar *framesdata = apng.pdata + (apng.first_frame * apng.width * apng.height * apng.bpp);
texture = TexturePtr(new AnimatedTexture(apng.width, apng.height, apng.bpp, apng.num_frames, framesdata, (int*)apng.frames_delay));
} else
texture = TexturePtr(new Texture(apng.width, apng.height, apng.bpp, apng.pdata));
//uchar *framesdata = apng.pdata + (apng.first_frame * apng.width * apng.height * apng.bpp);
//texture = TexturePtr(new AnimatedTexture(apng.width, apng.height, apng.bpp, apng.num_frames, framesdata, (int*)apng.frames_delay));
g_logger.error("animated textures is disabled for a while");
} else {
ImagePtr image = ImagePtr(new Image(Size(apng.width, apng.height), apng.bpp, apng.pdata));
texture = TexturePtr(new Texture(image));
}
free_apng(&apng);
}