Files
pymol-open-source/layer0/Texture.cpp
2021-07-07 12:15:36 -04:00

271 lines
8.4 KiB
C++

/*
A* -------------------------------------------------------------------
B* This file contains source code for the PyMOL computer program
C* Copyright (c) Schrodinger, LLC.
D* -------------------------------------------------------------------
E* It is unlawful to modify or remove this copyright notice.
F* -------------------------------------------------------------------
G* Please see the accompanying LICENSE file for further information.
H* --------------------------------------------------\-----------------
I* Additional authors of this source file include:
-*
-*
-*
Z* -------------------------------------------------------------------
*/
#include"os_python.h"
#include"os_gl.h"
#include "Base.h"
#include "PyMOLGlobals.h"
#include "Texture.h"
#include "Setting.h"
#include "Character.h"
#include "Util.h"
#include "Feedback.h"
#include "Executive.h"
#include "Ortho.h"
#include "ShaderMgr.h"
#define POS_START 2
typedef struct {
int id,dim;
} texture_info;
struct CTexture {
std::unordered_map<int, int> ch2tex;
GLuint text_texture_id{};
int xpos{};
int ypos{};
int maxypos{};
int num_chars{};
int text_texture_dim{};
};
GLuint TextureGetTextTextureID(PyMOLGlobals * G){
CTexture *I = G->Texture;
return I->text_texture_id;
}
int TextureGetTextTextureSize(PyMOLGlobals * G){
CTexture *I = G->Texture;
return I->text_texture_dim;
}
#define INIT_TEXTURE_SIZE 512
int TextureInit(PyMOLGlobals * G)
{
auto I = new CTexture();
G->Texture = I;
I->text_texture_dim = INIT_TEXTURE_SIZE;
I->ypos = I->maxypos = I->num_chars = 0;
I->xpos = POS_START;
return (I ? 1 : 0);
}
static
void TextureInitTextTextureImpl(PyMOLGlobals *G, int textureSize);
void TextureInitTextTexture(PyMOLGlobals *G){
TextureInitTextTextureImpl(G, INIT_TEXTURE_SIZE);
}
void TextureInvalidateTextTexture(PyMOLGlobals * G){
CTexture *I = G->Texture;
if (I->text_texture_id){
I->ch2tex.clear();
I->num_chars = 0;
glDeleteTextures(1, &I->text_texture_id);
I->text_texture_id = 0;
I->text_texture_dim = INIT_TEXTURE_SIZE;
I->xpos = POS_START; I->ypos = 0; I->maxypos = POS_START;
}
}
void TextureInitTextTextureImpl(PyMOLGlobals *G, int textureSizeArg){
short is_new = 0;
CTexture *I = G->Texture;
int textureSize = textureSizeArg;
if (!textureSize)
textureSize = INIT_TEXTURE_SIZE;
if (!I->text_texture_id){
glGenTextures(1, &I->text_texture_id);
is_new = 1;
}
if(I->text_texture_id){
if (G->ShaderMgr->ShadersPresent()){
glActiveTexture(GL_TEXTURE3);
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, I->text_texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
if (is_new){
int tex_dim = textureSize;
int buff_total = tex_dim * tex_dim;
unsigned char *temp_buffer = pymol::malloc<unsigned char>(buff_total * 4);
UtilZeroMem(temp_buffer, buff_total * 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
tex_dim, tex_dim, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)temp_buffer);
I->text_texture_dim = textureSize;
FreeP(temp_buffer);
I->xpos = POS_START; I->ypos = 0; I->maxypos = POS_START;
}
}
}
#include "Rep.h"
int TextureGetFromChar(PyMOLGlobals * G, int char_id, float *extent)
{
CTexture *I = G->Texture;
int is_new = false;
int tex_dim = I->text_texture_dim;
short use_shader = (short) SettingGetGlobal_b(G, cSetting_use_shaders);
if(G->HaveGUI && G->ValidContext) {
auto it = I->ch2tex.find(char_id);
if (it != I->ch2tex.end()) {
if(glIsTexture(I->text_texture_id))
return I->text_texture_id;
else {
I->ch2tex.erase(it);
}
}
{
unsigned char *buffer = NULL;
if (!I->text_texture_id){
is_new = true;
}
buffer = CharacterGetPixmapBuffer(G, char_id);
if(buffer) {
int w = CharacterGetWidth(G, char_id);
int h = CharacterGetHeight(G, char_id);
GLuint texture_id = 0;
int buff_incr = is_new ? tex_dim : w;
int buff_total = is_new ? tex_dim * tex_dim : w * h;
unsigned char *temp_buffer = pymol::malloc<unsigned char>(buff_total * 4);
{
int a, b;
unsigned char *p = buffer, *q;
int fa = 0, ta = w;
if (is_new){
fa += I->xpos; ta += I->xpos;
}
UtilZeroMem(temp_buffer, buff_total * 4);
for(b = 0; b < h; b++) {
for(a = fa; a < ta; a++) {
q = temp_buffer + (4 * buff_incr * b) + 4 * a;
*(q++) = *(p++);
*(q++) = *(p++);
*(q++) = *(p++);
*(q++) = *(p++);
}
}
if (I->xpos + w > tex_dim){
// if the size of the texture goes off the side, go to next row
I->xpos = 0;
I->ypos = I->maxypos;
}
if ((I->ypos + h) >= I->text_texture_dim){ // only need to check y since x gets reset above
int nrefreshes;
I->xpos = POS_START; I->ypos = 0; I->maxypos = POS_START;
I->ch2tex.clear();
I->num_chars = 0;
/* Also need to reload the selection markers into the texture, since
we are wiping everything out from the texture and starting from the origin */
if ((nrefreshes=SceneIncrementTextureRefreshes(G)) > 1){
/* Texture was refreshed more than once for this frame, increase size of texture */
int newDim = I->text_texture_dim * 2;
glDeleteTextures(1, &I->text_texture_id);
I->text_texture_id = 0;
TextureInitTextTextureImpl(G, newDim);
PRINTFB(G, FB_OpenGL, FB_Output)
" Texture OpenGL: nrefreshes=%d newDim=%d\n", nrefreshes, newDim ENDFB(G);
// printf("nrefreshes=%d newDim=%d\n", nrefreshes, newDim);
I->xpos = POS_START; I->ypos = 0; I->maxypos = POS_START;
SceneResetTextureRefreshes(G);
}
ExecutiveInvalidateRep(G, "all", cRepLabel, cRepInvRep);
ExecutiveInvalidateSelectionIndicators(G);
OrthoInvalidateDoDraw(G);
return 0;
}
extent[0] = (I->xpos / (float) tex_dim);
extent[1] = (I->ypos / (float) tex_dim);
extent[2] = ((I->xpos + w) / (float) tex_dim);
extent[3] = ((I->ypos + h) / (float) tex_dim);
}
if (!I->text_texture_id){
glGenTextures(1, &I->text_texture_id);
}
texture_id = I->text_texture_id;
if (I->text_texture_id) {
I->ch2tex[char_id] = I->num_chars++;
if (use_shader && G->ShaderMgr->ShadersPresent()){
glActiveTexture(GL_TEXTURE3);
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
if(is_new) {
I->text_texture_dim = tex_dim;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
tex_dim, tex_dim, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)temp_buffer);
} else {
int xoff = 0, yoff = 0;
xoff = I->xpos;
yoff = I->ypos;
glTexSubImage2D(GL_TEXTURE_2D, 0, xoff, yoff,
w, h, GL_RGBA, GL_UNSIGNED_BYTE, temp_buffer);
}
}
if (I->ypos + h > I->maxypos){
I->maxypos = I->ypos + h + 1; // added space for running on Ipad/Iphone (weird artifacts)
}
if (I->xpos + w > tex_dim){
I->xpos = 0;
I->ypos = I->maxypos;
} else {
I->xpos += w + 1; // added space for running on Ipad/Iphone (weird artifacts)
}
FreeP(temp_buffer);
return texture_id;
}
}
}
return 0;
}
void TextureGetPlacementForNewSubtexture(PyMOLGlobals * G, int new_texture_width, int new_texture_height, int *new_texture_posx, int *new_texture_posy){
CTexture *I = G->Texture;
if (I->xpos + new_texture_width > I->text_texture_dim){
I->xpos = 0;
I->ypos = I->maxypos;
}
if (I->ypos + new_texture_height > I->maxypos){
I->maxypos = I->ypos + new_texture_height + 1; // added space for running on Ipad/Iphone (weird artifacts)
}
*new_texture_posx = I->xpos;
*new_texture_posy = I->ypos;
I->xpos += new_texture_width + 1; // added space for running on Ipad/Iphone (weird artifacts)
}
void TextureFree(PyMOLGlobals * G)
{
/* TODO -- free all the resident textures */
DeleteP(G->Texture);
}