/*
* Texture.cpp
* iphone-gl-app
*
* Created by John Ryland on 14/06/09.
* Copyright 2009 InvertedLogic. All rights reserved.
*
*/
#include "Debug.h"
#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "Texture.h"
#include "Image.h"
//#define USE_HEAP_FOR_LARGE_TEMPORARY_DATA 1
struct TexturePrivate
{
int shiftU, shiftV;
int textureCoords[8];
unsigned int textureId;
bool smooth;
void initWithData(unsigned int width, unsigned int height, unsigned char *data, bool smooth);
};
Texture::Texture(unsigned int width, unsigned int height, unsigned char *data, bool s)
{
TexturePrivate *d = new TexturePrivate;
d->initWithData(width, height, data, s);
dptr = (void *)d;
}
Texture::Texture(const char *file, bool s)
{
Image img(file);
TexturePrivate *d = new TexturePrivate;
d->initWithData(img.width(), img.height(), img.data(), s);
dptr = (void *)d;
}
Texture::~Texture()
{
glDeleteTextures(1, &((TexturePrivate *)dptr)->textureId);
delete (TexturePrivate *)dptr;
}
void TexturePrivate::initWithData(unsigned int width, unsigned int height, unsigned char *data, bool s)
{
// Generate an identifier for our texture and enable
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &textureId);
int oldTextureId;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTextureId); // Save current texture state before modify
glBindTexture(GL_TEXTURE_2D, textureId);
// Calculate factors used to convert integer pixel offsets
// in to fixed point offsets in OpenGL texture coordinates.
// Calculating shiftU and shiftV to contain the bit position of the MSB of width and height.
// shiftU = integerMSB(width);
// shiftV = integerMSB(height);
shiftU = 0;
shiftV = 0;
const int mask[5] = { 65536, 256, 16, 4, 2 };
for (int i = 0; i < 5; i++) {
shiftU += (width >= (mask[i] << shiftU)) ? (16>>i) : 0;
shiftV += (height >= (mask[i]<< shiftV)) ? (16>>i) : 0;
}
int texW = 65536;
int texH = 65536;
// If width is a power of 2, it will contain only one bit, eg equal to 1 << 'MSB of width'
if (width != (1 << shiftU) || height != (1 << shiftV)) {
// OpenGL requires the texture dimensions to be powers of 2. If our image
// doesn't have correct dimensions, we fix it by copying the data in to a
// temporary buffer that has dimensions of powers of 2.
DebugMessage::warning("Texture dimensions need to be powers of 2. Fixing.\n");
DebugMessage::warning("This wastes texture memory so consider optimizing the dimensions of your image resources.\n");
int oldW = width;
int oldH = height;
if (width != (1 << shiftU)) {
shiftU++;
width = (1 << shiftU); // width is now the next highest power of 2
}
if (height != (1 << shiftV)) {
shiftV++;
height = (1 << shiftV); // height also rounded up to next highest power of 2
}
const int dataSize = width * height * 4;
#ifdef USE_HEAP_FOR_LARGE_TEMPORARY_DATA
unsigned char *tmpData = (unsigned char *)malloc(dataSize); // Allocated on the heap
#else
unsigned char tmpData[dataSize]; // Allocated on the stack
#endif
for (int i = 0; i < oldH; i++)
memcpy(tmpData + width*i*4, data + oldW*i*4, oldW*4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, tmpData);
#ifdef USE_HEAP_FOR_LARGE_TEMPORARY_DATA
free(tmpData);
#endif
// Scale texture coordinates to only map the image bits
texW = oldW * 65536 / width;
texH = oldH * 65536 / height;
} else {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
const int defaultTextureCoords[8] = { 0, 0, texW, 0, 0, texH, texW, texH };
for (int i = 0; i < 8; i++)
textureCoords[i] = defaultTextureCoords[i];
smooth = s;
glBindTexture(GL_TEXTURE_2D, oldTextureId); // restore
}
void Texture::setExtents(int x, int y, int w, int h)
{
if (x == -1) {
/*
const int defaultTextureCoords[8] = { 0, 0, texW, 0, 0, texH, texW, texH };
for (int i = 0; i < 8; i++)
textureCoords[i] = defaultTextureCoords[i];
*/
return;
}
int shiftU = 16 - ((TexturePrivate *)dptr)->shiftU;
int shiftV = 16 - ((TexturePrivate *)dptr)->shiftV;
int x1 = x << shiftU;
int y1 = y << shiftV;
int x2 = (x+w) << shiftU;
int y2 = (y+h) << shiftV;
int texCoords[8] = { x1, y1, x2, y1, x1, y2, x2, y2 };
for (int i = 0; i < 8; i++)
((TexturePrivate *)dptr)->textureCoords[i] = texCoords[i];
}
void Texture::setSmooth(bool s)
{
((TexturePrivate *)dptr)->smooth = s;
}
void Texture::bind()
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_BLEND);
// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture(GL_TEXTURE_2D, ((TexturePrivate *)dptr)->textureId);
// The arguments below mean 2 fixed values per coord in a list that
// is tightly packed. Our list contains 4 coords which defines 2 triangles.
// So this code assumes that the texture is bound to a square made
// of 2 triangles such as from verticies from a Sprite object.
glTexCoordPointer(2, GL_FIXED, 0, ((TexturePrivate *)dptr)->textureCoords);
if ( ((TexturePrivate *)dptr)->smooth ) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
}