|
|
|
@ -27,131 +27,77 @@
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
Texture::Texture(int width, int height, int channels, uchar *pixels)
|
|
|
|
|
{
|
|
|
|
|
// generate opengl texture
|
|
|
|
|
internalLoadGLTexture(pixels, channels, width, height);
|
|
|
|
|
}
|
|
|
|
|
if(!setupSize(size))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Texture::~Texture()
|
|
|
|
|
{
|
|
|
|
|
// free texture from gl memory
|
|
|
|
|
if(m_textureId > 0)
|
|
|
|
|
glDeleteTextures(1, &m_textureId);
|
|
|
|
|
createTexture();
|
|
|
|
|
bind();
|
|
|
|
|
setupPixels(0, m_glSize, nullptr, 4);
|
|
|
|
|
setupWrap();
|
|
|
|
|
setupFilters();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int height)
|
|
|
|
|
Texture::Texture(const ImagePtr& image, bool buildMipmaps)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
m_id = 0;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
if(!setupSize(image->getSize(), buildMipmaps))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// generate gl texture
|
|
|
|
|
GLuint id;
|
|
|
|
|
glGenTextures(1, &id);
|
|
|
|
|
assert(id != 0);
|
|
|
|
|
m_textureId = id;
|
|
|
|
|
bind();
|
|
|
|
|
createTexture();
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
ImagePtr glImage = image;
|
|
|
|
|
if(m_size != m_glSize) {
|
|
|
|
|
glImage = ImagePtr(new Image(m_glSize, image->getBpp()));
|
|
|
|
|
glImage->paste(image);
|
|
|
|
|
} else
|
|
|
|
|
glImage = image;
|
|
|
|
|
|
|
|
|
|
GLint texParam = GL_REPEAT;
|
|
|
|
|
if(g_graphics.canUseClampToEdge())
|
|
|
|
|
texParam = GL_CLAMP_TO_EDGE; // disable texture borders by default
|
|
|
|
|
bind();
|
|
|
|
|
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParam);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParam);
|
|
|
|
|
/*
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Texture::copyFromScreen(const Rect& screenRect)
|
|
|
|
|
Texture::~Texture()
|
|
|
|
|
{
|
|
|
|
|
bind();
|
|
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, screenRect.x(), screenRect.y(), screenRect.width(), screenRect.height());
|
|
|
|
|
// free texture from gl memory
|
|
|
|
|
if(m_id > 0)
|
|
|
|
|
glDeleteTextures(1, &m_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Texture::bind()
|
|
|
|
|
{
|
|
|
|
|
// must reset painter texture state
|
|
|
|
|
g_painter->setTexture(this);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_textureId);
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Texture::generateMipmaps()
|
|
|
|
|
void Texture::copyFromScreen(const Rect& screenRect)
|
|
|
|
|
{
|
|
|
|
|
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.");
|
|
|
|
|
}
|
|
|
|
|
bind();
|
|
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, screenRect.x(), screenRect.y(), screenRect.width(), screenRect.height());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
int mipmap = 1;
|
|
|
|
|
while(true) {
|
|
|
|
|
outPixels.resize(outSize.area()*4);
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
m_size = size;
|
|
|
|
|
m_glSize = glSize;
|
|
|
|
|
setupTranformMatrix();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inPixels = std::move(outPixels);
|
|
|
|
|
inSize /= 2;
|
|
|
|
|
outSize /= 2;
|
|
|
|
|
}
|
|
|
|
|
void Texture::setupWrap()
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|