Hi Droozle
Thanks for the code… I’ve certainly blown some C++ cobwebs off the brain today. Its good…
I ran into some errors when I tried to build the code. I included it here in full with the sections you posted in place (colored orange).
If you have a chance, and notice anything I might have entered incorrectly, I would greatly appreciate it…
Cheers
///////////////////////////////////////////////////////////
///////////////////// OfTexture.h /////////////////////////
#ifndef _IMAGE_TEXTURE_H_
#define _IMAGE_TEXTURE_H_
#include “ofConstants.h”
#include “ofGraphics.h”
typedef struct{
bool bAllocated;
int glType;
int glTypeInternal;
int textureTarget;
float tex_t;
float tex_u;
float tex_w;
float tex_h;
float width;
float height;
bool bFlipTexture;
//we do this because openGL expects an array
//but we don’t want people to work with textureName[1]
//so we make textureID point to the same location
union{
struct{
unsigned int textureID; //use me
};
unsigned int textureName[1]; //don’t use me
};
}ofTextureData;
//enable / disable the slight offset we add to ofTexture’s texture coords to compensate for bad edge artifiacts
//enabled by default
void ofEnableTextureEdgeHack();
void ofDisableTectureEdgeHack();
class ofTexture : public ofBaseDraws{
public :
ofTexture();
virtual ~ofTexture();
// -----------------------------------------------------------------------
// we allow pass by copy and assignment operator
// it does a straight copy but you are getting the textureID of mom’s texture
// so this means that your texture and mom’s texture are the same thing
// so in other words be careful! calling clear on your texture will trash mom’s
// texture and vice versa.
ofTexture(const ofTexture& mom);
ofTexture& operator=(const ofTexture& mom);
// -----------------------------------------------------------------------
void allocate(int w, int h, int internalGlDataType); //uses the currently set OF texture type - default ARB texture
void allocate(int w, int h, int internalGlDataType, bool bUseARBExtention); //lets you overide the default OF texture type
void clear();
void loadData(unsigned char * data, int w, int h, int glDataType);
void loadScreenData(int x, int y, int w, int h);
//the anchor is the point the image is drawn around.
//this can be useful if you want to rotate an image around a particular point.
void setAnchorPercent(float xPct, float yPct); //set the anchor as a percentage of the image width/height ( 0.0-1.0 range )
void setAnchorPoint(int x, int y); //set the anchor point in pixels
void resetAnchor(); //resets the anchor to (0, 0)
void draw(float x, float y, float w, float h);
void draw(float x, float y);
//for the advanced user who wants to draw textures in their own way
void bind();
void unbind();
bool bAllocated();
ofTextureData getTextureData();
float getHeight();
float getWidth();
ofTextureData texData;
protected:
ofPoint anchor;
bool bAnchorIsPct;
float left;//new droozel
float top;//new droozel
float right;//new droozel
float bottom;//new droozel
};
#endif
///////////////////// end OfTexture.h /////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
//////////////////////ofTexture.cpp/////////////////////////
#include “ofTexture.h”
#include “ofUtils.h” // for nextPow2()
#include “ofAppRunner.h” // for getWidth()
static bool bTexHackEnabled = true;
//---------------------------------
void ofEnableTextureEdgeHack(){
bTexHackEnabled = true;
}
//---------------------------------
void ofDisableTectureEdgeHack(){
bTexHackEnabled = false;
}
//----------------------------------------------------------
ofTexture::ofTexture(){
texData.bAllocated = false;
texData.textureName[0] = 0;
texData.textureID = 0;
texData.bFlipTexture = false;
texData.textureTarget = GL_TEXTURE_2D;
texData.glTypeInternal = 0;
texData.glType = 0;
texData.width = 0;
texData.height = 0;
texData.tex_w = 0;
texData.tex_h = 0;
texData.tex_t = 0;
texData.tex_u = 0;
resetAnchor();
}
//----------------------------------------------------------
bool ofTexture::bAllocated(){
return texData.bAllocated;
}
//----------------------------------------------------------
ofTexture::ofTexture(const ofTexture& mom){
texData = mom.texData;
}
//----------------------------------------------------------
ofTexture& ofTexture::operator=(const ofTexture& mom){
texData = mom.texData;
return *this;
}
//----------------------------------------------------------
ofTextureData ofTexture::getTextureData(){
if(!texData.bAllocated){
ofLog(OF_LOG_ERROR, “getTextureData() - texture has not been allocated”);
}
return texData;
}
//----------------------------------------------------------
ofTexture::~ofTexture(){
clear();
}
//----------------------------------------------------------
void ofTexture::clear(){
// try to free up the texture memory so we don’t reallocate
// http://www.opengl.org/documentation/spe-…-tures.html
if (texData.textureName[0] != 0){
glDeleteTextures(1, (GLuint *)texData.textureName);
}
texData.bAllocated = false;
}
//----------------------------------------------------------
void ofTexture::allocate(int w, int h, int internalGlDataType){
allocate(w, h, internalGlDataType, ofGetUsingArbTex());
}
//----------------------------------------------------------
void ofTexture::allocate(int w, int h, int internalGlDataType, bool bUseARBExtention){
//our graphics card might not support arb so we have to see if it is supported.
#ifndef TARGET_OF_IPHONE
if (bUseARBExtention && GLEE_ARB_texture_rectangle){
texData.tex_w = w;
texData.tex_h = h;
texData.tex_t = w;
texData.tex_u = h;
texData.textureTarget = GL_TEXTURE_RECTANGLE_ARB;
} else
#endif
{
//otherwise we need to calculate the next power of 2 for the requested dimensions
//ie (320x240) becomes (512x256)
texData.tex_w = ofNextPow2(w);
texData.tex_h = ofNextPow2(h);
texData.tex_t = 1.0f;
texData.tex_u = 1.0f;
texData.textureTarget = GL_TEXTURE_2D;
}
texData.glTypeInternal = internalGlDataType;
// attempt to free the previous bound texture, if we can:
clear();
glGenTextures(1, (GLuint *)texData.textureName); // could be more then one, but for now, just one
glEnable(texData.textureTarget);
glBindTexture(texData.textureTarget, (GLuint)texData.textureName[0]);
#ifndef TARGET_OF_IPHONE
// can’t do this on OpenGL ES: on full-blown OpenGL,
// internalGlDataType and glDataType (GL_LUMINANCE below)
// can be different; on ES they must be exactly the same.
glTexImage2D(texData.textureTarget, 0, texData.glTypeInternal, (GLint)texData.tex_w, (GLint)texData.tex_h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0); // init to black…
#else
glTexImage2D(texData.textureTarget, 0, texData.glTypeInternal, texData.tex_w, texData.tex_h, 0, texData.glTypeInternal, GL_UNSIGNED_BYTE, 0);
#endif
glTexParameterf(texData.textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(texData.textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(texData.textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(texData.textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glDisable(texData.textureTarget);
texData.width = w;
texData.height = h;
#ifdef TARGET_OF_IPHONE
texData.bFlipTexture = true; // textures need to be flipped for the iphone
#else
texData.bFlipTexture = false;
#endif
texData.bAllocated = true;
}
//----------------------------------------------------------
void ofTexture::loadData(unsigned char * data, int w, int h, int glDataType){
// can we allow for uploads bigger then texture and
// just take as much as the texture can?
//
// ie:
// int uploadW = MIN(w, tex_w);
// int uploadH = MIN(h, tex_h);
// but with a “step” size of w?
// check “glTexSubImage2D”
if ( w > texData.tex_w || h > texData.tex_h) {
ofLog(OF_LOG_ERROR,“image data too big for allocated texture. not uploading…”);
return;
}
//update our size with the new dimensions - this should be the same size or smaller than the allocated texture size
texData.width = w;
texData.height = h;
texData.glType = glDataType;
//compute new tex co-ords based on the ratio of data’s w, h to texture w,h;
#ifndef TARGET_OF_IPHONE
if (texData.textureTarget == GL_TEXTURE_RECTANGLE_ARB){
texData.tex_t = w;
texData.tex_u = h;
} else
#endif
{
texData.tex_t = (float)(w) / (float)texData.tex_w;
texData.tex_u = (float)(h) / (float)texData.tex_h;
}
// ok this is an ultra annoying bug :
// opengl texels and linear filtering -
// when we have a sub-image, and we scale it
// we can clamp the border pixels to the border,
// but the borders of the sub image get mixed with
// neighboring pixels…
// grr…
//
// the best solution would be to pad out the image
// being uploaded with 2 pixels on all sides, and
// recompute tex_t coordinates…
// another option is a gl_arb non pow 2 textures…
// the current hack is to alter the tex_t, tex_u calcs, but
// that makes the image slightly off…
// this is currently being done in draw…
//
// we need a good solution for this…
//
// http://www.opengl.org/discussion-boards-…-770#000001
// http://www.opengl.org/discussion-boards-…-770#000001
//------------------------ likely, we are uploading continuous data
GLint prevAlignment;
glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevAlignment);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// update the texture image:
glEnable(texData.textureTarget);
glBindTexture(texData.textureTarget, (GLuint)texData.textureName[0]);
glTexSubImage2D(texData.textureTarget,0,0,0,w,h,texData.glType,GL_UNSIGNED_BYTE,data);
glDisable(texData.textureTarget);
//------------------------ back to normal.
glPixelStorei(GL_UNPACK_ALIGNMENT, prevAlignment);
#ifdef TARGET_OF_IPHONE
texData.bFlipTexture = true; // textures need to be flipped for the iphone
#else
texData.bFlipTexture = false;
#endif
}
//----------------------------------------------------------
void ofTexture::loadScreenData(int x, int y, int w, int h){
int screenHeight = ofGetHeight();
y = screenHeight - y;
y -= h; // top, bottom issues
texData.bFlipTexture = true;
if ( w > texData.tex_w || h > texData.tex_h) {
ofLog(OF_LOG_ERROR,“image data too big for allocated texture. not uploading…”);
return;
}
//update our size with the new dimensions - this should be the same size or smaller than the allocated texture size
texData.width = w;
texData.height = h;
texData.glType = GL_RGB;
//compute new tex co-ords based on the ratio of data’s w, h to texture w,h;
#ifndef TARGET_OF_IPHONE // DAMIAN
if (texData.textureTarget == GL_TEXTURE_RECTANGLE_ARB){
texData.tex_t = (float)(w);
texData.tex_u = (float)(h);
} else
#endif
{
texData.tex_t = (float)(w) / (float)texData.tex_w;
texData.tex_u = (float)(h) / (float)texData.tex_h;
}
glEnable(texData.textureTarget);
glBindTexture(texData.textureTarget, (GLuint)texData.textureName[0]);
glCopyTexSubImage2D(texData.textureTarget, 0,0,0,x,y,w,h);
glDisable(texData.textureTarget);
}
//we could cap these values - but it might be more useful
//to be able to set anchor points outside the image
//----------------------------------------------------------
void ofTexture::setAnchorPercent(float xPct, float yPct){
anchor.x = xPct;
anchor.y = yPct;
bAnchorIsPct = true;
}
//----------------------------------------------------------
void ofTexture::setAnchorPoint(int x, int y){
anchor.x = x;
anchor.y = y;
bAnchorIsPct = false;
}
//----------------------------------------------------------
void ofTexture::resetAnchor(){
anchor = 0;
bAnchorIsPct = false;
}
//----------------------------------------------------------
void ofTexture::bind(){
//we could check if it has been allocated - but we don’t do that in draw()
glEnable(texData.textureTarget);
glBindTexture( texData.textureTarget, (GLuint)texData.textureName[0]);
}
//----------------------------------------------------------
void ofTexture::unbind(){
glDisable(texData.textureTarget);
}
//----------------------------------------------------------
void ofTexture::draw(float x, float y, float w, float h){
glEnable(texData.textureTarget);
// bind the texture
glBindTexture( texData.textureTarget, (GLuint)texData.textureName[0] );
GLfloat px0 = 0; // up to you to get the aspect ratio right
GLfloat py0 = 0;
GLfloat px1 = w;
GLfloat py1 = h;
if (texData.bFlipTexture == true){
GLint temp = (GLint)py0;
py0 = py1;
py1 = temp;
}
// for rect mode center, let’s do this:
if (ofGetRectMode() == OF_RECTMODE_CENTER){
px0 = -w/2;
py0 = -h/2;
px1 = +w/2;
py1 = +h/2;
}
//we translate our drawing points by our anchor point.
//we still respect ofRectMode so if you have rect mode set to
//OF_RECTMODE_CENTER your anchor will be relative to that.
GLfloat anchorX;
GLfloat anchorY;
if(bAnchorIsPct){
anchorX = anchor.x * w;
anchorY = anchor.y * h;
}else{
anchorX = anchor.x;
anchorY = anchor.y;
}
px0 -= anchorX;
py0 -= anchorY;
px1 -= anchorX;
py1 -= anchorY;
// -------------------------------------------------
// complete hack to remove border artifacts.
// slightly, slightly alters an image, scaling…
// to remove the border.
// we need a better solution for this, but
// to constantly add a 2 pixel border on all uploaded images
// is insane…
GLfloat offsetw = 0;
GLfloat offseth = 0;
if (texData.textureTarget == GL_TEXTURE_2D && bTexHackEnabled) {
offsetw = 1.0f / (texData.tex_w);
offseth = 1.0f / (texData.tex_h);
}
// -------------------------------------------------
//replaced by Droozle’s vars below///////////////////
//GLfloat tx0 = 0+offsetw;
//GLfloat ty0 = 0+offseth;
//GLfloat tx1 = texData.tex_t - offsetw;
//GLfloat ty1 = texData.tex_u - offseth;
//new////////Droozle///////////////////////////////
GLfloat tx0 = max(offsetw, left*texData.tex_t);
GLfloat ty0 = max(offseth, top*texData.tex_u);
GLfloat tx1 = min(right*texData.tex_t, texData.tex_t-offsetw);
GLfloat ty1 = min(bottom*texData.tex_u, texData.tex_u-offseth);
////////////////////////////////////////////////////////////////
glPushMatrix();
glTranslatef(x,y,0.0f);
GLfloat tex_coords[] = {
tx0,ty0,
tx1,ty0,
tx1,ty1,
tx0,ty1
};
GLfloat verts[] = {
px0,py0,
px1,py0,
px1,py1,
px0,py1
};
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glTexCoordPointer(2, GL_FLOAT, 0, tex_coords );
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, verts );
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glPopMatrix();
glDisable(texData.textureTarget);
}
//----------------------------------------------------------
void ofTexture::draw(float x, float y){
draw(x,y,texData.width, texData.height);
}
//----------------------------------------------------------
float ofTexture::getHeight(){
return texData.height;
}
//----------------------------------------------------------
float ofTexture::getWidth(){
return texData.width;
}
//new/////Droozle/////////////////////////
void ofTexture::setSubsection(float x, float y, float w, float h){
if(texData.tex_t == 0) return;
if(texData.tex_u == 0) return;
left = max(0.0f, min(1.0f, x / texData.width));
top = max(0.0f, min(1.0f, y / texData.height));
right = max(0.0f, min(1.0f, left + (w / texData.width)));
bottom = max(0.0f, min(1.0f, top + (h / texData.height)));
// disable texture hack for now
bTexHackEnabled = false;
}
void ofTexture::resetSubsection(){
left = 0.0f;
top = 0.0f;
right = 1.0f;
bottom = 1.0f;
// TODO: only enable texture hack if it was previously enabled
bTexHackEnabled = true;
}
/////////////////end ofTexture.cpp/////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////// testApp.cpp//////////////////////////
#include “testApp.h”
//--------------------------------------------------------------
void testApp::setup(){
ofBackground(150,150,150);
video.loadMovie(“vid0.mov”);//440px x 330px QTvideo
video.getTextureReference().setSubsection(0, 0, 160, 120);
}
//--------------------------------------------------------------
void testApp::update(){
}
//--------------------------------------------------------------
void testApp::draw(){
video.draw(100, 100, 160, 120);
video.getTextureReference().resetSubsection();
}
//////////////////end testApp.cpp//////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
////////////////////// testApp.h///////////////////////////
#ifndef _TEST_APP
#define _TEST_APP
#include “ofMain.h”
#include “ofxOpenCv.h”
class testApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
void keyPressed (int key);
void mouseMoved(int x, int y );
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void windowResized(int w, int h);
ofVideoPlayer video;
int vidWidth;
int vidHeight;
};
#endif
////////////////////// testApp.h///////////////////////////
///////////////////////////////////////////////////////////