#include "ofTexture.h"
#include "ofGraphics.h"
#include "ofPixels.h"
#include "ofGLUtils.h"
#include "ofGLBaseTypes.h"
#include "ofBufferObject.h"
#include "ofMesh.h"
#include <map>
#ifdef TARGET_ANDROID
#include "ofAppAndroidWindow.h"
#endif
using namespace std;
static bool bTexHackEnabled = true;
static bool bUsingArbTex = true;
static bool bUsingNormalizedTexCoords = false;
static bool bUseCustomMinMagFilters = false;
void ofEnableTextureEdgeHack(){
bTexHackEnabled = true;
}
void ofDisableTextureEdgeHack(){
bTexHackEnabled = false;
}
bool ofIsTextureEdgeHackEnabled(){
return bTexHackEnabled;
}
bool ofGetUsingNormalizedTexCoords(){
return bUsingNormalizedTexCoords;
}
void ofEnableNormalizedTexCoords(){
bUsingNormalizedTexCoords = true;
}
void ofDisableNormalizedTexCoords(){
bUsingNormalizedTexCoords = false;
}
static bool bUseCustomTextureWrap = false;
void ofSetTextureWrap(GLfloat wrapS, GLfloat wrapT){
bUseCustomTextureWrap = true;
GLenum textureTarget = GL_TEXTURE_2D;
#ifndef TARGET_OPENGLES
if (ofGetUsingArbTex() && GL_ARB_texture_rectangle){
textureTarget = GL_TEXTURE_RECTANGLE_ARB;
};
#endif
glTexParameterf(textureTarget, GL_TEXTURE_WRAP_S, wrapS);
glTexParameterf(textureTarget, GL_TEXTURE_WRAP_T, wrapT);
}
bool ofGetUsingCustomTextureWrap(){
return bUseCustomTextureWrap;
}
void ofRestoreTextureWrap(){
bUseCustomTextureWrap = false;
}
void ofSetMinMagFilters(GLfloat minFilter, GLfloat magFilter){
bUseCustomMinMagFilters = true;
GLenum textureTarget = GL_TEXTURE_2D;
#ifndef TARGET_OPENGLES
if (ofGetUsingArbTex() && GL_ARB_texture_rectangle){
textureTarget = GL_TEXTURE_RECTANGLE_ARB;
};
#endif
glTexParameterf(textureTarget, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameterf(textureTarget, GL_TEXTURE_MAG_FILTER, magFilter);
}
bool ofGetUsingCustomMinMagFilters(){
return bUseCustomMinMagFilters;
}
void ofRestoreMinMagFilters(){
bUseCustomMinMagFilters = false;
}
bool ofGetUsingArbTex(){
return bUsingArbTex;
}
void ofEnableArbTex(){
bUsingArbTex = true;
}
void ofDisableArbTex(){
bUsingArbTex = false;
}
static map<GLuint,int> & getTexturesIndex(){
static map<GLuint,int> * textureReferences = new map<GLuint,int>;
return *textureReferences;
}
static void retain(GLuint id){
if(id!=0){
if(getTexturesIndex().find(id)!=getTexturesIndex().end()){
getTexturesIndex()[id]++;
}else{
getTexturesIndex()[id]=1;
}
}
}
static void release(GLuint id){
if (id != 0){
if(getTexturesIndex().find(id)!=getTexturesIndex().end()){
getTexturesIndex()[id]--;
if(getTexturesIndex()[id]==0){
#ifdef TARGET_ANDROID
if (!ofAppAndroidWindow::isSurfaceDestroyed())
#endif
glDeleteTextures(1, (GLuint *)&id);
getTexturesIndex().erase(id);
}
}else{
ofLogError("ofTexture") << "release(): something's wrong here, releasing unknown texture id " << id;
#ifdef TARGET_ANDROID
if (!ofAppAndroidWindow::isSurfaceDestroyed())
#endif
glDeleteTextures(1, (GLuint *)&id);
}
}
}
#ifdef TARGET_ANDROID
static set<ofTexture*> & allTextures(){
static set<ofTexture*> * allTextures = new set<ofTexture*>;
return *allTextures;
}
static void registerTexture(ofTexture * texture){
allTextures().insert(texture);
}
static void unregisterTexture(ofTexture * texture){
allTextures().erase(texture);
}
void ofRegenerateAllTextures(){
for(auto tex: allTextures()){
tex->clear();
}
}
#endif
ofTexture::ofTexture(){
resetAnchor();
bWantsMipmap = false;
}
ofTexture::ofTexture(const ofTexture & mom){
anchor = mom.anchor;
bAnchorIsPct = mom.bAnchorIsPct;
texData = mom.texData;
bWantsMipmap = mom.bWantsMipmap;
retain(texData.textureID);
#ifdef TARGET_ANDROID
registerTexture(this);
#endif
}
ofTexture::ofTexture(ofTexture && mom){
anchor = mom.anchor;
bAnchorIsPct = mom.bAnchorIsPct;
texData = mom.texData;
bWantsMipmap = mom.bWantsMipmap;
mom.texData.bAllocated = 0;
mom.texData.textureID = 0;
#ifdef TARGET_ANDROID
registerTexture(this);
#endif
}
ofTexture& ofTexture::operator=(const ofTexture & mom){
if(!texData.bUseExternalTextureID){
release(texData.textureID);
}
anchor = mom.anchor;
bAnchorIsPct = mom.bAnchorIsPct;
texData = mom.texData;
bWantsMipmap = mom.bWantsMipmap;
retain(texData.textureID);
#ifdef TARGET_ANDROID
unregisterTexture(this);
#endif
return *this;
}
ofTexture& ofTexture::operator=(ofTexture && mom){
if(!texData.bUseExternalTextureID){
release(texData.textureID);
}
anchor = mom.anchor;
bAnchorIsPct = mom.bAnchorIsPct;
texData = mom.texData;
bWantsMipmap = mom.bWantsMipmap;
mom.texData.bAllocated = 0;
mom.texData.textureID = 0;
#ifdef TARGET_ANDROID
unregisterTexture(this);
#endif
return *this;
}
bool ofTexture::bAllocated() const {
return texData.bAllocated;
}
bool ofTexture::isAllocated() const {
return texData.bAllocated;
}
ofTextureData& ofTexture::getTextureData(){
if(!texData.bAllocated){
ofLogError("ofTexture") << "getTextureData(): texture has not been allocated";
}
return texData;
}
const ofTextureData& ofTexture::getTextureData() const {
if(!texData.bAllocated){
ofLogError("ofTexture") << "getTextureData(): texture has not been allocated";
}
return texData;
}
ofTexture::~ofTexture(){
if(!texData.bUseExternalTextureID){
release(texData.textureID);
}
#ifdef TARGET_ANDROID
unregisterTexture(this);
#endif
}
void ofTexture::clear(){
if(!texData.bUseExternalTextureID){
release(texData.textureID);
}
texData.bUseExternalTextureID = false;
texData.textureID = 0;
texData.bAllocated = false;
}
void ofTexture::setUseExternalTextureID(GLuint externTexID){
clear();
texData.textureID = externTexID;
texData.bAllocated = true;
texData.bUseExternalTextureID = true;
}
void ofTexture::allocate(int w, int h, int glInternalFormat){
allocate(w, h, glInternalFormat, ofGetUsingArbTex(), ofGetGLFormatFromInternal(glInternalFormat), ofGetGLTypeFromInternal(glInternalFormat));
}
void ofTexture::allocate(int w, int h, int glInternalFormat, bool bUseARBExtension){
allocate(w, h, glInternalFormat, bUseARBExtension, ofGetGLFormatFromInternal(glInternalFormat), ofGetGLTypeFromInternal(glInternalFormat));
}
void ofTexture::allocate(int w, int h, int glInternalFormat, int glFormat, int pixelType){
allocate(w, h, glInternalFormat, ofGetUsingArbTex(), glFormat, pixelType);
}
void ofTexture::allocate(const ofPixels& pix){
allocate(pix.getWidth(), pix.getHeight(), ofGetGLInternalFormat(pix), ofGetUsingArbTex(), ofGetGLFormat(pix), ofGetGLType(pix));
if((pix.getPixelFormat()==OF_PIXELS_GRAY || pix.getPixelFormat()==OF_PIXELS_GRAY_ALPHA) && ofIsGLProgrammableRenderer()){
setRGToRGBASwizzles(true);
}
if(texData.bAllocated) loadData(pix);
}
void ofTexture::allocate(const ofPixels& pix, bool bUseARBExtention){
allocate(pix.getWidth(), pix.getHeight(), ofGetGLInternalFormat(pix), bUseARBExtention, ofGetGLFormat(pix), ofGetGLType(pix));
if((pix.getPixelFormat()==OF_PIXELS_GRAY || pix.getPixelFormat()==OF_PIXELS_GRAY_ALPHA) && ofIsGLProgrammableRenderer()){
setRGToRGBASwizzles(true);
}
if(texData.bAllocated) loadData(pix);
}
void ofTexture::allocate(const ofShortPixels& pix){
allocate(pix.getWidth(), pix.getHeight(), ofGetGLInternalFormat(pix), ofGetUsingArbTex(), ofGetGLFormat(pix), ofGetGLType(pix));
if((pix.getPixelFormat()==OF_PIXELS_GRAY || pix.getPixelFormat()==OF_PIXELS_GRAY_ALPHA) && ofIsGLProgrammableRenderer()){
setRGToRGBASwizzles(true);
}
if(texData.bAllocated) loadData(pix);
}
void ofTexture::allocate(const ofShortPixels& pix, bool bUseARBExtention){
allocate(pix.getWidth(), pix.getHeight(), ofGetGLInternalFormat(pix), bUseARBExtention, ofGetGLFormat(pix), ofGetGLType(pix));
if((pix.getPixelFormat()==OF_PIXELS_GRAY || pix.getPixelFormat()==OF_PIXELS_GRAY_ALPHA) && ofIsGLProgrammableRenderer()){
setRGToRGBASwizzles(true);
}
if(texData.bAllocated) loadData(pix);
}
void ofTexture::allocate(const ofFloatPixels& pix){
allocate(pix.getWidth(), pix.getHeight(), ofGetGLInternalFormat(pix), ofGetUsingArbTex(), ofGetGLFormat(pix), ofGetGLType(pix));
if((pix.getPixelFormat()==OF_PIXELS_GRAY || pix.getPixelFormat()==OF_PIXELS_GRAY_ALPHA) && ofIsGLProgrammableRenderer()){
setRGToRGBASwizzles(true);
}
if(texData.bAllocated) loadData(pix);
}
void ofTexture::allocate(const ofFloatPixels& pix, bool bUseARBExtention){
allocate(pix.getWidth(), pix.getHeight(), ofGetGLInternalFormat(pix), bUseARBExtention, ofGetGLFormat(pix), ofGetGLType(pix));
if((pix.getPixelFormat()==OF_PIXELS_GRAY || pix.getPixelFormat()==OF_PIXELS_GRAY_ALPHA) && ofIsGLProgrammableRenderer()){
setRGToRGBASwizzles(true);
}
if(texData.bAllocated) loadData(pix);
}
#ifndef TARGET_OPENGLES
void ofTexture::allocateAsBufferTexture(const ofBufferObject & buffer, int glInternalFormat){
texData.glInternalFormat = glInternalFormat;
texData.textureTarget = GL_TEXTURE_BUFFER;
texData.bufferId = buffer.getId();
allocate(texData,0,0);
buffer.bind(GL_TEXTURE_BUFFER);
}
#endif
void ofTexture::allocate(int w, int h, int glInternalFormat, bool bUseARBExtension, int glFormat, int pixelType){
texData.width = w;
texData.height = h;
texData.bFlipTexture = false;
texData.glInternalFormat = glInternalFormat;
#ifndef TARGET_OPENGLES
if (bUseARBExtension && GL_ARB_texture_rectangle){
texData.textureTarget = GL_TEXTURE_RECTANGLE_ARB;
} else
#endif
{
texData.textureTarget = GL_TEXTURE_2D;
}
allocate(texData,glFormat,pixelType);
}
void ofTexture::allocate(const ofTextureData & textureData){
allocate(textureData,ofGetGLFormatFromInternal(textureData.glInternalFormat),ofGetGLTypeFromInternal(textureData.glInternalFormat));
}
void ofTexture::allocate(const ofTextureData & textureData, int glFormat, int pixelType){
#ifndef TARGET_OPENGLES
if(texData.textureTarget == GL_TEXTURE_2D || texData.textureTarget == GL_TEXTURE_RECTANGLE_ARB){
#else
if(texData.textureTarget == GL_TEXTURE_2D){
#endif
if( textureData.width <= 0.0 || textureData.height <= 0.0 ){
ofLogError("ofTexture") << "allocate(): ofTextureData has 0 width and/or height: " << textureData.width << "x" << textureData.height;
return;
}
}
texData = textureData;
#ifndef TARGET_OPENGLES
if( texData.textureTarget==GL_TEXTURE_RECTANGLE_ARB && ofGLSupportsNPOTTextures() ){
texData.tex_w = texData.width;
texData.tex_h = texData.height;
texData.tex_t = texData.width;
texData.tex_u = texData.height;
}else if(texData.textureTarget == GL_TEXTURE_2D)
#endif
{
if(ofGLSupportsNPOTTextures()){
texData.tex_w = texData.width;
texData.tex_h = texData.height;
}else{
texData.tex_w = ofNextPow2(texData.width);
texData.tex_h = ofNextPow2(texData.height);
}
texData.tex_t = texData.width / texData.tex_w;
texData.tex_u = texData.height / texData.tex_h;
}
clear();
glGenTextures(1, (GLuint *)&texData.textureID);
retain(texData.textureID);
#ifndef TARGET_OPENGLES
if(texData.textureTarget == GL_TEXTURE_2D || texData.textureTarget == GL_TEXTURE_RECTANGLE_ARB){
#else
if(texData.textureTarget == GL_TEXTURE_2D){
#endif
glBindTexture(texData.textureTarget,texData.textureID);
glTexImage2D(texData.textureTarget, 0, texData.glInternalFormat, (GLint)texData.tex_w, (GLint)texData.tex_h, 0, glFormat, pixelType, 0);
glTexParameterf(texData.textureTarget, GL_TEXTURE_MAG_FILTER, texData.magFilter);
glTexParameterf(texData.textureTarget, GL_TEXTURE_MIN_FILTER, texData.minFilter);
glTexParameterf(texData.textureTarget, GL_TEXTURE_WRAP_S, texData.wrapModeHorizontal);
glTexParameterf(texData.textureTarget, GL_TEXTURE_WRAP_T, texData.wrapModeVertical);
#ifndef TARGET_PROGRAMMABLE_GL
if (!ofIsGLProgrammableRenderer()){
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
#endif
glBindTexture(texData.textureTarget,0);
}
texData.bAllocated = true;
#ifdef TARGET_ANDROID
registerTexture(this);
#endif
}
void ofTexture::setRGToRGBASwizzles(bool rToRGBSwizzles){
#ifndef TARGET_OPENGLES
glBindTexture(texData.textureTarget,texData.textureID);
if(rToRGBSwizzles){
if(texData.glInternalFormat==GL_R8 ||
texData.glInternalFormat==GL_R16 ||
texData.glInternalFormat==GL_R32F||
texData.glInternalFormat==GL_DEPTH_COMPONENT
#ifndef TARGET_OPENGLES
||
texData.glInternalFormat==GL_DEPTH_COMPONENT16 ||
texData.glInternalFormat==GL_DEPTH_COMPONENT24 ||
texData.glInternalFormat==GL_DEPTH_COMPONENT32
#endif
){
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_B, GL_RED);
}else if(texData.glInternalFormat==GL_RG8 ||
texData.glInternalFormat==GL_RG16 ||
texData.glInternalFormat==GL_RG32F){
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_B, GL_RED);
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_A, GL_GREEN);
}
}else{
if(texData.glInternalFormat==GL_R8 ||
texData.glInternalFormat==GL_R16 ||
texData.glInternalFormat==GL_R32F){
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_B, GL_BLUE);
}else if(texData.glInternalFormat==GL_RG8 ||
texData.glInternalFormat==GL_RG16 ||
texData.glInternalFormat==GL_RG32F){
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_B, GL_BLUE);
glTexParameteri(texData.textureTarget, GL_TEXTURE_SWIZZLE_A, GL_ALPHA);
}
}
glBindTexture(texData.textureTarget,0);
#endif
}
void ofTexture::setSwizzle(GLenum srcSwizzle, GLenum dstChannel){
#ifndef TARGET_OPENGLES
glBindTexture(texData.textureTarget,texData.textureID);
glTexParameteri(texData.textureTarget, srcSwizzle, dstChannel);
glBindTexture(texData.textureTarget,0);
#endif
}
void ofTexture::loadData(const uint8_t * data, int w, int h, int glFormat){
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,w,1,ofGetNumChannelsFromGLFormat(glFormat));
loadData(data, w, h, glFormat, GL_UNSIGNED_BYTE);
}
void ofTexture::loadData(const uint16_t * data, int w, int h, int glFormat){
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,w,2,ofGetNumChannelsFromGLFormat(glFormat));
loadData(data, w, h, glFormat, GL_UNSIGNED_SHORT);
}
void ofTexture::loadData(const uint32_t * data, int w, int h, int glFormat){
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,w,2,ofGetNumChannelsFromGLFormat(glFormat));
loadData(data, w, h, glFormat, GL_UNSIGNED_INT);
}
void ofTexture::loadData(const int8_t * data, int w, int h, int glFormat){
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,w,1,ofGetNumChannelsFromGLFormat(glFormat));
loadData(data, w, h, glFormat, GL_BYTE);
}
void ofTexture::loadData(const int16_t * data, int w, int h, int glFormat){
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,w,2,ofGetNumChannelsFromGLFormat(glFormat));
loadData(data, w, h, glFormat, GL_SHORT);
}
void ofTexture::loadData(const int32_t * data, int w, int h, int glFormat){
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,w,2,ofGetNumChannelsFromGLFormat(glFormat));
loadData(data, w, h, glFormat, GL_INT);
}
void ofTexture::loadData(const float * data, int w, int h, int glFormat){
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,w,4,ofGetNumChannelsFromGLFormat(glFormat));
loadData(data, w, h, glFormat, GL_FLOAT);
}
void ofTexture::loadData(const ofPixels & pix){
if(!isAllocated()){
allocate(pix);
}else{
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,pix.getBytesStride());
loadData(pix.getData(), pix.getWidth(), pix.getHeight(), ofGetGLFormat(pix), ofGetGLType(pix));
}
}
void ofTexture::loadData(const ofShortPixels & pix){
if(!isAllocated()){
allocate(pix);
}else{
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,pix.getBytesStride());
loadData(pix.getData(), pix.getWidth(), pix.getHeight(), ofGetGLFormat(pix), ofGetGLType(pix));
}
}
void ofTexture::loadData(const ofFloatPixels & pix){
if(!isAllocated()){
allocate(pix);
}else{
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,pix.getBytesStride());
loadData(pix.getData(), pix.getWidth(), pix.getHeight(), ofGetGLFormat(pix), ofGetGLType(pix));
}
}
void ofTexture::loadData(const ofPixels & pix, int glFormat){
if(!isAllocated()){
allocate(pix.getWidth(), pix.getHeight(), ofGetGLInternalFormat(pix), ofGetUsingArbTex(), glFormat, ofGetGLType(pix));
}
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,pix.getWidth(),pix.getBytesPerChannel(),ofGetNumChannelsFromGLFormat(glFormat));
loadData(pix.getData(), pix.getWidth(), pix.getHeight(), glFormat, ofGetGLType(pix));
}
void ofTexture::loadData(const ofShortPixels & pix, int glFormat){
if(!isAllocated()){
allocate(pix.getWidth(), pix.getHeight(), ofGetGLInternalFormat(pix), ofGetUsingArbTex(), glFormat, ofGetGLType(pix));
}
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,pix.getWidth(),pix.getBytesPerChannel(),ofGetNumChannelsFromGLFormat(glFormat));
loadData(pix.getData(), pix.getWidth(), pix.getHeight(), glFormat, ofGetGLType(pix));
}
void ofTexture::loadData(const ofFloatPixels & pix, int glFormat){
if(!isAllocated()){
allocate(pix.getWidth(), pix.getHeight(), ofGetGLInternalFormat(pix), ofGetUsingArbTex(), glFormat, ofGetGLType(pix));
}
ofSetPixelStoreiAlignment(GL_UNPACK_ALIGNMENT,pix.getWidth(),pix.getBytesPerChannel(),ofGetNumChannelsFromGLFormat(glFormat));
loadData(pix.getData(), pix.getWidth(), pix.getHeight(), glFormat, ofGetGLType(pix));
}
#ifndef TARGET_OPENGLES
void ofTexture::loadData(const ofBufferObject & buffer, int glFormat, int glType){
buffer.bind(GL_PIXEL_UNPACK_BUFFER);
loadData(0,texData.width,texData.height,glFormat,glType);
buffer.unbind(GL_PIXEL_UNPACK_BUFFER);
}
#endif
void ofTexture::loadData(const void * data, int w, int h, int glFormat, int glType){
if(w > texData.tex_w || h > texData.tex_h) {
if(isAllocated()){
allocate(w, h, texData.glInternalFormat, glFormat, glType);
}else{
allocate(w, h, glFormat, glFormat, glType);
}
}
#ifndef TARGET_OPENGLES
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;
}
glBindTexture(texData.textureTarget, (GLuint) texData.textureID);
glTexSubImage2D(texData.textureTarget, 0, 0, 0, w, h, glFormat, glType, data);
glBindTexture(texData.textureTarget, 0);
if (bWantsMipmap) {
generateMipmap();
}
}
void ofTexture::generateMipmap(){
bool isGlGenerateMipmapAvailable = false;
if(ofIsGLProgrammableRenderer()){
isGlGenerateMipmapAvailable = true;
}
if (!isGlGenerateMipmapAvailable && !ofGLCheckExtension("GL_EXT_framebuffer_object")) {
static bool versionWarningIssued = false;
if (!versionWarningIssued) ofLogWarning() << "Your current OpenGL version does not support mipmap generation via glGenerateMipmap().";
versionWarningIssued = true;
texData.hasMipmap = false;
return;
}
switch (texData.textureTarget) {
case GL_TEXTURE_2D:
case GL_TEXTURE_CUBE_MAP:
#ifndef TARGET_OPENGLES
case GL_TEXTURE_1D:
case GL_TEXTURE_3D:
case GL_TEXTURE_1D_ARRAY:
case GL_TEXTURE_2D_ARRAY:
#endif
{
glBindTexture(texData.textureTarget, (GLuint) texData.textureID);
glGenerateMipmap(texData.textureTarget);
glBindTexture(texData.textureTarget, 0);
texData.hasMipmap = true;
break;
}
default:
{
static bool warningIssuedAlready = false;
if (!warningIssuedAlready){
ofLogWarning() << "Mipmaps are not supported for textureTarget 0x" << hex << texData.textureTarget << endl
<< "Most probably you are trying to create mipmaps from a GL_TEXTURE_RECTANGLE texture." << endl
<< "Try ofDisableArbTex() before loading this texture.";
warningIssuedAlready = true;
}
texData.hasMipmap = false;
break;
}
}
}
void ofTexture::loadScreenData(int x, int y, int w, int h){
int screenHeight = ofGetViewportHeight();
y = screenHeight - y;
y -= h;
texData.bFlipTexture = true;
if ( w > texData.tex_w || h > texData.tex_h) {
ofLogError("ofTexture") << "loadScreenData(): " << w << "x" << h << " image data too big for "
<< texData.tex_w << "x " << texData.tex_h << " allocated texture, not uploading";
return;
}
texData.width = w;
texData.height = h;
#ifndef TARGET_OPENGLES
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;
}
glBindTexture(texData.textureTarget,texData.textureID);
glCopyTexSubImage2D(texData.textureTarget, 0,0,0,x,y,w,h);
glBindTexture(texData.textureTarget,0);
if (bWantsMipmap) {
generateMipmap();
}
}
void ofTexture::setAnchorPercent(float xPct, float yPct){
anchor.x = xPct;
anchor.y = yPct;
bAnchorIsPct = true;
}
void ofTexture::setAnchorPoint(float x, float y){
anchor.x = x;
anchor.y = y;
bAnchorIsPct = false;
}
void ofTexture::resetAnchor(){
anchor = {0.f, 0.f, 0.f};
bAnchorIsPct = false;
}
void ofTexture::bind(int textureLocation) const{
ofGetGLRenderer()->bind(*this,textureLocation);
}
void ofTexture::unbind(int textureLocation) const{
ofGetGLRenderer()->unbind(*this,textureLocation);
}
#if !defined(TARGET_OPENGLES) && defined(glBindImageTexture)
void ofTexture::bindAsImage(GLuint unit, GLenum access, GLint level, GLboolean layered, GLint layer){
glBindImageTexture(unit,texData.textureID,level,layered,layer,access,texData.glInternalFormat);
}
#endif
void ofTexture::setAlphaMask(ofTexture & mask){
if(mask.texData.textureTarget!=this->texData.textureTarget){
ofLogError("ofTexture") << "Cannot set alpha mask with different texture target";
}else{
texData.alphaMask = std::make_shared<ofTexture>(mask);
}
}
const ofTexture * ofTexture::getAlphaMask() const{
return texData.alphaMask.get();
}
void ofTexture::disableAlphaMask(){
if(texData.alphaMask){
texData.alphaMask.reset();
}
}
glm::vec2 ofTexture::getCoordFromPoint(float xPos, float yPos) const{
glm::vec2 temp(0);
if (!isAllocated()) return temp;
#ifndef TARGET_OPENGLES
if (texData.textureTarget == GL_TEXTURE_RECTANGLE_ARB){
temp = {xPos, yPos};
} else {
#endif
float pctx = xPos / texData.width;
float pcty = yPos / texData.height;
pctx *= texData.tex_t;
pcty *= texData.tex_u;
temp = {pctx, pcty};
#ifndef TARGET_OPENGLES
}
#endif
return temp;
}
void ofTexture::setTextureMatrix(const glm::mat4 & m){
texData.textureMatrix = m;
texData.useTextureMatrix = true;
}
void ofTexture::disableTextureMatrix(){
texData.useTextureMatrix = false;
texData.textureMatrix = glm::mat4(1.0);
}
const glm::mat4 & ofTexture::getTextureMatrix() const{
return texData.textureMatrix;
}
bool ofTexture::isUsingTextureMatrix() const{
return texData.useTextureMatrix;
}
glm::vec2 ofTexture::getCoordFromPercent(float xPct, float yPct) const{
glm::vec2 temp(0);
if (!isAllocated()) return temp;
#ifndef TARGET_OPENGLES
if (texData.textureTarget == GL_TEXTURE_RECTANGLE_ARB){
temp = {xPct * texData.width, yPct * texData.height};
} else {
#endif
xPct *= texData.tex_t;
yPct *= texData.tex_u;
temp = {xPct, yPct};
#ifndef TARGET_OPENGLES
}
#endif
return temp;
}
void ofTexture::setTextureWrap(GLint wrapModeHorizontal, GLint wrapModeVertical) {
glBindTexture(texData.textureTarget,texData.textureID);
glTexParameteri(texData.textureTarget, GL_TEXTURE_WRAP_S, wrapModeHorizontal);
glTexParameteri(texData.textureTarget, GL_TEXTURE_WRAP_T, wrapModeVertical);
texData.wrapModeVertical = wrapModeVertical;
texData.wrapModeHorizontal = wrapModeHorizontal;
glBindTexture(texData.textureTarget,0);
}
void ofTexture::setTextureMinMagFilter(GLint minFilter, GLint magFilter){
if ( (minFilter > GL_LINEAR) && texData.hasMipmap == false ){
static bool hasWarnedNoMipmapsForMinFilter = false;
if(!hasWarnedNoMipmapsForMinFilter) {
ofLogWarning() << "Texture has no mipmaps - but minFilter 0x"<< hex << minFilter << " requires mipmaps."
<< endl << "Call ofTexture::generateMipmaps() first.";
}
hasWarnedNoMipmapsForMinFilter = true;
return;
}
if ( (magFilter > GL_LINEAR ) ) {
static bool hasWarnedInvalidMagFilter = false;
if (!hasWarnedInvalidMagFilter) {
ofLogWarning() << "magFilter must be either GL_LINEAR or GL_NEAREST.";
}
hasWarnedInvalidMagFilter = true;
return;
}
glBindTexture(texData.textureTarget,texData.textureID);
glTexParameteri(texData.textureTarget, GL_TEXTURE_MAG_FILTER, magFilter);
glTexParameteri(texData.textureTarget, GL_TEXTURE_MIN_FILTER, minFilter);
texData.magFilter = magFilter;
texData.minFilter = minFilter;
glBindTexture(texData.textureTarget,0);
}
void ofTexture::setCompression(ofTexCompression compression){
texData.compressionType = compression;
}
void ofTexture::enableMipmap(){
bWantsMipmap = true;
texData.minFilter = GL_LINEAR_MIPMAP_LINEAR;
}
void ofTexture::disableMipmap(){
bWantsMipmap = false;
texData.minFilter = GL_LINEAR;
}
bool ofTexture::hasMipmap() const{
return texData.hasMipmap;
}
void ofTexture::draw(float x, float y) const{
draw(x,y,0,getWidth(),getHeight());
}
void ofTexture::draw(float x, float y, float z) const{
draw(x,y,z,getWidth(),getHeight());
}
void ofTexture::draw(const glm::vec3 & pos) const{
draw(pos.x,pos.y,pos.z,getWidth(),getHeight());
}
void ofTexture::draw(float x, float y, float w, float h) const{
draw(x,y,0,w,h);
}
void ofTexture::draw(const glm::vec3 & pos, float w, float h) const{
draw(pos.x,pos.y,pos.z,w,h);
}
void ofTexture::draw(float x, float y, float z, float w, float h) const{
drawSubsection(x,y,z,w,h,0,0,getWidth(),getHeight());
}
void ofTexture::drawSubsection(float x, float y, float w, float h, float sx, float sy) const{
drawSubsection(x,y,0,w,h,sx,sy,w,h);
}
void ofTexture::drawSubsection(float x, float y, float w, float h, float sx, float sy, float _sw, float _sh) const{
drawSubsection(x,y,0,w,h,sx,sy,_sw,_sh);
}
void ofTexture::drawSubsection(const ofRectangle& drawBounds, const ofRectangle& subsectionBounds) const {
drawSubsection(drawBounds.x,drawBounds.y,0,drawBounds.width,drawBounds.height,subsectionBounds.x,subsectionBounds.y,subsectionBounds.width,subsectionBounds.height);
}
void ofTexture::drawSubsection(float x, float y, float z, float w, float h, float sx, float sy) const{
drawSubsection(x,y,z,w,h,sx,sy,w,h);
}
void ofTexture::drawSubsection(float x, float y, float z, float w, float h, float sx, float sy, float sw, float sh) const{
shared_ptr<ofBaseGLRenderer> renderer = ofGetGLRenderer();
if(renderer){
renderer->draw(*this,x,y,z,w,h,sx,sy,sw,sh);
}
}
ofMesh ofTexture::getMeshForSubsection(float x, float y, float z, float w, float h, float sx, float sy, float sw, float sh, bool vflipped, ofRectMode rectMode) const{
ofMesh quad;
if(!texData.bAllocated){
return quad;
}
GLfloat px0 = x;
GLfloat py0 = y;
GLfloat px1 = w+x;
GLfloat py1 = h+y;
if (texData.bFlipTexture == vflipped){
swap(py0,py1);
}
if (rectMode == OF_RECTMODE_CENTER){
px0 -= w/2;
py0 -= h/2;
px1 -= w/2;
py1 -= h/2;
}
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;
GLfloat offsetw = 0.0f;
GLfloat offseth = 0.0f;
if (!ofGLSupportsNPOTTextures() && bTexHackEnabled) {
offsetw = 1.0f / (texData.tex_w);
offseth = 1.0f / (texData.tex_h);
}
auto topLeft = getCoordFromPoint(sx, sy);
auto bottomRight = getCoordFromPoint(sx + sw, sy + sh);
GLfloat tx0 = topLeft.x + offsetw;
GLfloat ty0 = topLeft.y + offseth;
GLfloat tx1 = bottomRight.x - offsetw;
GLfloat ty1 = bottomRight.y - offseth;
quad.setMode(OF_PRIMITIVE_TRIANGLE_FAN);
quad.getVertices().resize(4);
quad.getTexCoords().resize(4);
quad.getVertices()[0] = {px0,py0,z};
quad.getVertices()[1] = {px1,py0,z};
quad.getVertices()[2] = {px1,py1,z};
quad.getVertices()[3] = {px0,py1,z};
quad.getTexCoords()[0] = {tx0,ty0};
quad.getTexCoords()[1] = {tx1,ty0};
quad.getTexCoords()[2] = {tx1,ty1};
quad.getTexCoords()[3] = {tx0,ty1};
return quad;
}
void ofTexture::draw(const glm::vec3 & p1, const glm::vec3 & p2, const glm::vec3 & p3, const glm::vec3 & p4) const{
shared_ptr<ofBaseGLRenderer> renderer = ofGetGLRenderer();
if(renderer){
bind(0);
renderer->draw(getQuad(p1,p2,p3,p4),OF_MESH_FILL);
unbind(0);
}
}
ofMesh ofTexture::getQuad(const glm::vec3 & p1, const glm::vec3 & p2, const glm::vec3 & p3, const glm::vec3 & p4) const{
GLfloat offsetw = 0.0f;
GLfloat offseth = 0.0f;
if (texData.textureTarget == GL_TEXTURE_2D && bTexHackEnabled) {
offsetw = 1.0f / (texData.tex_w);
offseth = 1.0f / (texData.tex_h);
}
GLfloat tx0 = 0+offsetw;
GLfloat ty0 = 0+offseth;
GLfloat tx1 = texData.tex_t - offsetw;
GLfloat ty1 = texData.tex_u - offseth;
ofMesh quad;
quad.getVertices().resize(4);
quad.getTexCoords().resize(4);
quad.setMode(OF_PRIMITIVE_TRIANGLE_FAN);
quad.getVertices()[0] = {p1.x, p1.y, p1.z};
quad.getVertices()[1] = {p2.x, p2.y, p2.z};
quad.getVertices()[2] = {p3.x, p3.y, p3.z};
quad.getVertices()[3] = {p4.x, p4.y, p4.z};
quad.getTexCoords()[0] = {tx0,ty0};
quad.getTexCoords()[1] = {tx1,ty0};
quad.getTexCoords()[2] = {tx1,ty1};
quad.getTexCoords()[3] = {tx0,ty1};
return quad;
}
void ofTexture::readToPixels(ofPixels & pixels) const {
#ifndef TARGET_OPENGLES
pixels.allocate(texData.width,texData.height,ofGetImageTypeFromGLType(texData.glInternalFormat));
ofSetPixelStoreiAlignment(GL_PACK_ALIGNMENT,pixels.getWidth(),pixels.getBytesPerChannel(),pixels.getNumChannels());
glBindTexture(texData.textureTarget,texData.textureID);
glGetTexImage(texData.textureTarget,0,ofGetGLFormat(pixels),GL_UNSIGNED_BYTE, pixels.getData());
glBindTexture(texData.textureTarget,0);
#endif
}
void ofTexture::readToPixels(ofShortPixels & pixels) const {
#ifndef TARGET_OPENGLES
pixels.allocate(texData.width,texData.height,ofGetImageTypeFromGLType(texData.glInternalFormat));
ofSetPixelStoreiAlignment(GL_PACK_ALIGNMENT,pixels.getWidth(),pixels.getBytesPerChannel(),pixels.getNumChannels());
glBindTexture(texData.textureTarget,texData.textureID);
glGetTexImage(texData.textureTarget,0,ofGetGLFormat(pixels),GL_UNSIGNED_SHORT,pixels.getData());
glBindTexture(texData.textureTarget,0);
#endif
}
void ofTexture::readToPixels(ofFloatPixels & pixels) const {
#ifndef TARGET_OPENGLES
pixels.allocate(texData.width,texData.height,ofGetImageTypeFromGLType(texData.glInternalFormat));
ofSetPixelStoreiAlignment(GL_PACK_ALIGNMENT,pixels.getWidth(),pixels.getBytesPerChannel(),pixels.getNumChannels());
glBindTexture(texData.textureTarget,texData.textureID);
glGetTexImage(texData.textureTarget,0,ofGetGLFormat(pixels),GL_FLOAT,pixels.getData());
glBindTexture(texData.textureTarget,0);
#endif
}
#ifndef TARGET_OPENGLES
void ofTexture::copyTo(ofBufferObject & buffer) const{
ofSetPixelStoreiAlignment(GL_PACK_ALIGNMENT,getWidth(),ofGetBytesPerChannelFromGLType(ofGetGLTypeFromInternal(texData.glInternalFormat)),ofGetNumChannelsFromGLFormat(ofGetGLFormatFromInternal(texData.glInternalFormat)));
buffer.bind(GL_PIXEL_PACK_BUFFER);
glBindTexture(texData.textureTarget,texData.textureID);
glGetTexImage(texData.textureTarget,0,ofGetGLFormatFromInternal(texData.glInternalFormat),ofGetGLTypeFromInternal(texData.glInternalFormat),0);
glBindTexture(texData.textureTarget,0);
buffer.unbind(GL_PIXEL_PACK_BUFFER);
}
#endif
float ofTexture::getHeight() const {
return texData.height;
}
float ofTexture::getWidth() const {
return texData.width;
}
Comments