mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-04 20:04:21 +08:00
543 lines
15 KiB
C++
543 lines
15 KiB
C++
|
|
/*
|
|
A* -------------------------------------------------------------------
|
|
B* This file contains source code for the PyMOL computer program
|
|
C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
|
|
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_gl.h"
|
|
|
|
#include"Base.h"
|
|
#include"Character.h"
|
|
#include"Pixmap.h"
|
|
#include"Util.h"
|
|
#include"MemoryDebug.h"
|
|
#include"Vector.h"
|
|
#include"Text.h"
|
|
#include"Texture.h"
|
|
#include"CGO.h"
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
|
|
#define HASH_MASK 0x2FFF
|
|
|
|
static unsigned int get_hash(CharFngrprnt * fprnt)
|
|
{
|
|
unsigned int result = 0;
|
|
unsigned short int *data = fprnt->u.d.data;
|
|
result = (data[0] << 1) + data[1];
|
|
result = ((result << 4) + data[2]);
|
|
result = ((result << 7) + data[3]) + (result >> 16);
|
|
result = ((result << 10) + data[4]) + (result >> 16);
|
|
result = ((result << 13) + data[5]) + (result >> 16);
|
|
result = ((result << 15) + data[6]) + (result >> 16);
|
|
result = ((result << 15) + data[7]) + (result >> 16);
|
|
result = ((result << 15) + data[8]) + (result >> 16);
|
|
result = ((result << 1) + data[9]) + (result >> 16);
|
|
return (HASH_MASK & result);
|
|
}
|
|
|
|
static int equal_fprnt(CharFngrprnt * f1, CharFngrprnt * f2)
|
|
{
|
|
unsigned short int *data1 = f1->u.d.data;
|
|
unsigned short int *data2 = f2->u.d.data;
|
|
|
|
/* must compare all fields in fingerprint */
|
|
|
|
if(*(data1++) != *(data2++))
|
|
return 0;
|
|
if(*(data1++) != *(data2++))
|
|
return 0;
|
|
if(*(data1++) != *(data2++))
|
|
return 0;
|
|
if(*(data1++) != *(data2++))
|
|
return 0;
|
|
if(*(data1++) != *(data2++))
|
|
return 0;
|
|
if(*(data1++) != *(data2++))
|
|
return 0;
|
|
if(*(data1++) != *(data2++))
|
|
return 0;
|
|
if(*(data1++) != *(data2++))
|
|
return 0;
|
|
if(*(data1++) != *(data2++))
|
|
return 0;
|
|
if(*(data1++) != *(data2++))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int CharacterFind(PyMOLGlobals * G, CharFngrprnt * fprnt)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
unsigned int hash_code = get_hash(fprnt);
|
|
int id = I->Hash[hash_code];
|
|
/*
|
|
printf("seeking %d %d %d %d %d %d %d\n",
|
|
fprnt->u.i.text_id,
|
|
fprnt->u.i.ch,
|
|
fprnt->u.i.height,
|
|
fprnt->u.i.color[0],
|
|
fprnt->u.i.color[1],
|
|
fprnt->u.i.color[2],
|
|
fprnt->u.i.color[3]);
|
|
*/
|
|
|
|
while(id) {
|
|
if(equal_fprnt(fprnt, &I->Char[id].Fngrprnt)) {
|
|
|
|
/* pop character to top of retention list
|
|
(is this worth the effort?) */
|
|
CharRec *rec = I->Char + id;
|
|
int prev = rec->Prev;
|
|
int next = rec->Next;
|
|
|
|
if(prev && next) { /* only act if character is in middle of list */
|
|
I->Char[prev].Next = next;
|
|
I->Char[next].Prev = prev;
|
|
|
|
prev = I->NewestUsed;
|
|
I->NewestUsed = id;
|
|
I->Char[prev].Next = id;
|
|
rec->Prev = prev;
|
|
rec->Next = 0;
|
|
}
|
|
return id;
|
|
} else
|
|
id = I->Char[id].HashNext;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned char *CharacterGetPixmapBuffer(PyMOLGlobals * G, int id)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
if(id) {
|
|
CharRec *rec = I->Char + id;
|
|
return rec->Pixmap.buffer;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int CharacterNewFromBitmap(PyMOLGlobals * G, int width, int height,
|
|
unsigned char *bitmap,
|
|
float x_orig, float y_orig, float advance,
|
|
CharFngrprnt * fprnt, int sampling)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
int id = CharacterGetNew(G);
|
|
if((id > 0) && (id <= I->MaxAlloc)) {
|
|
CharRec *rec = I->Char + id;
|
|
PixmapInitFromBitmap(G, &rec->Pixmap, width, height, bitmap,
|
|
fprnt->u.i.color, sampling);
|
|
rec->Width = width * sampling;
|
|
rec->Height = height * sampling;
|
|
rec->XOrig = x_orig * sampling;
|
|
rec->YOrig = y_orig * sampling;
|
|
rec->Advance = advance * sampling;
|
|
{ /* add this character to the hash */
|
|
int hash_code = get_hash(fprnt);
|
|
int cur_entry;
|
|
rec->Fngrprnt = *(fprnt);
|
|
rec->Fngrprnt.hash_code = hash_code;
|
|
cur_entry = I->Hash[hash_code];
|
|
if(cur_entry) {
|
|
I->Char[cur_entry].HashPrev = id;
|
|
}
|
|
I->Char[id].HashNext = I->Hash[hash_code];
|
|
I->Hash[hash_code] = id;
|
|
}
|
|
}
|
|
return id;
|
|
}
|
|
|
|
int CharacterNewFromBytemap(PyMOLGlobals * G, int width, int height,
|
|
int pitch, unsigned char *bytemap,
|
|
float x_orig, float y_orig, float advance,
|
|
CharFngrprnt * fprnt)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
int id = CharacterGetNew(G);
|
|
if((id > 0) && (id <= I->MaxAlloc)) {
|
|
CharRec *rec = I->Char + id;
|
|
PixmapInitFromBytemap(G, &rec->Pixmap, width, height, pitch, bytemap,
|
|
fprnt->u.i.color, fprnt->u.i.outline_color, fprnt->u.i.flat);
|
|
rec->Width = width;
|
|
rec->Height = height;
|
|
rec->XOrig = x_orig;
|
|
rec->YOrig = y_orig;
|
|
rec->Advance = advance;
|
|
{ /* add this character to the hash */
|
|
int hash_code = get_hash(fprnt);
|
|
int cur_entry;
|
|
rec->Fngrprnt = *(fprnt);
|
|
rec->Fngrprnt.hash_code = hash_code;
|
|
cur_entry = I->Hash[hash_code];
|
|
if(cur_entry) {
|
|
I->Char[cur_entry].HashPrev = id;
|
|
}
|
|
I->Char[id].HashNext = I->Hash[hash_code];
|
|
I->Hash[hash_code] = id;
|
|
}
|
|
}
|
|
return id;
|
|
}
|
|
|
|
float CharacterGetAdvance(PyMOLGlobals * G, int sampling, int id)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
CharRec *rec = I->Char + id;
|
|
return rec->Advance / sampling;
|
|
}
|
|
|
|
void CharacterRenderOpenGLPrime(PyMOLGlobals * G, const RenderInfo * info)
|
|
{
|
|
if(G->HaveGUI && G->ValidContext) {
|
|
if ((info && !info->use_shaders) || (!info && !SettingGetGlobal_b(G, cSetting_use_shaders))){
|
|
glEnable(GL_TEXTURE_2D);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
}
|
|
/* glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); */
|
|
}
|
|
}
|
|
|
|
void CharacterRenderOpenGLDone(PyMOLGlobals * G, const RenderInfo * info)
|
|
{
|
|
if(G->HaveGUI && G->ValidContext) {
|
|
if ((info && !info->use_shaders) || (!info && !SettingGetGlobal_b(G, cSetting_use_shaders))){
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
/* glDisable(GL_BLEND); */
|
|
}
|
|
}
|
|
|
|
short CharacterRenderOpenGL(PyMOLGlobals * G, const RenderInfo * info, int id, short isworldlabel, short relativeMode, CGO* shaderCGO)
|
|
|
|
/* need orientation matrix */
|
|
{
|
|
CCharacter *I = G->Character;
|
|
CharRec *rec = I->Char + id;
|
|
short success = 1;
|
|
auto isTextured = TextureIsCharTextured(G, id, rec->extent);
|
|
float sampling = 1.0F;
|
|
|
|
if (G->HaveGUI && G->ValidContext && isTextured) {
|
|
if(info)
|
|
sampling = (float) info->sampling;
|
|
if (isTextured) {
|
|
/* if(glIsTexture(texture_id)) -- BAD -- impacts performance */
|
|
float *v, v0[3];
|
|
float v1[3];
|
|
if (!shaderCGO){
|
|
if (!TextGetIsPicking(G)) {
|
|
TextureBindTexture(G);
|
|
}
|
|
}
|
|
v = TextGetPos(G);
|
|
copy3f(v, v0);
|
|
v0[0] -= rec->XOrig / sampling;
|
|
v0[1] -= rec->YOrig / sampling;
|
|
copy3f(v0, v1);
|
|
v1[0] += rec->Width / sampling;
|
|
v1[1] += rec->Height / sampling;
|
|
/* glColor4f(0.5F,0.5F,0.5F,1.0F); */
|
|
if (shaderCGO){
|
|
float *worldPos = TextGetWorldPos(G);
|
|
if (isworldlabel){
|
|
float *targetPos = TextGetTargetPos(G);
|
|
float *screenWorldOffset = TextGetScreenWorldOffset(G);
|
|
shaderCGO->add<cgo::draw::label>(glm::make_vec3(worldPos),
|
|
glm::make_vec3(screenWorldOffset),
|
|
glm::make_vec3(v0),
|
|
glm::make_vec3(v1),
|
|
glm::make_vec4(rec->extent),
|
|
relativeMode,
|
|
glm::make_vec3(targetPos));
|
|
} else {
|
|
CGODrawTexture(shaderCGO, worldPos, v0, v1, rec->extent);
|
|
}
|
|
} else {
|
|
#ifndef PURE_OPENGL_ES_2
|
|
glBegin(GL_QUADS);
|
|
if (TextGetIsPicking(G)){
|
|
unsigned char *cptr = TextGetColorUChar4uv(G);
|
|
glColor4ubv(cptr);
|
|
glVertex3f(v0[0], v0[1], v0[2]);
|
|
glVertex3f(v0[0], v1[1], v0[2]);
|
|
glVertex3f(v1[0], v1[1], v0[2]);
|
|
glVertex3f(v1[0], v0[1], v0[2]);
|
|
glEnd();
|
|
} else {
|
|
glTexCoord2f(rec->extent[0], rec->extent[1]);
|
|
glVertex3f(v0[0], v0[1], v0[2]);
|
|
glTexCoord2f(rec->extent[0], rec->extent[3]);
|
|
glVertex3f(v0[0], v1[1], v0[2]);
|
|
glTexCoord2f(rec->extent[2], rec->extent[3]);
|
|
glVertex3f(v1[0], v1[1], v0[2]);
|
|
glTexCoord2f(rec->extent[2], rec->extent[1]);
|
|
glVertex3f(v1[0], v0[1], v0[2]);
|
|
glEnd();
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
TextAdvance(G, rec->Advance / sampling);
|
|
} else {
|
|
if (!isTextured)
|
|
success = 0;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
int CharacterGetWidth(PyMOLGlobals * G, int id)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
if((id > 0) && (id <= I->MaxAlloc)) {
|
|
return I->Char[id].Width;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const float _inv255 = 1.0F / 255.0F;
|
|
|
|
const unsigned char zerouc[4] = { 0, 0, 0, 0 };
|
|
|
|
|
|
/* CharacterInterpolate: This function implements bilinear interpolation
|
|
on looking up the pixel value in the texture.
|
|
*/
|
|
float CharacterInterpolate(PyMOLGlobals * G, int id, float *v)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
int x = (int) v[0];
|
|
int y = (int) v[1];
|
|
|
|
if((id > 0) && (id <= I->MaxAlloc)) {
|
|
CPixmap *pm = &I->Char[id].Pixmap;
|
|
if(pm) {
|
|
unsigned char *srcx0, *srcx1, *srcy0, *srcy1;
|
|
int x1 = x + 1, y1 = y + 1;
|
|
float xdiff = v[0] - x, ydiff = v[1] - y;
|
|
float xdiff1 = (1.f-xdiff), ydiff1 = (1.f-ydiff);
|
|
float interp0[4], interp1[4]; // interpolated in x for y0 and y1
|
|
|
|
if (x < 0 || x > (pm->width - 1))
|
|
srcx0 = (unsigned char *)zerouc;
|
|
else
|
|
srcx0 = pm->buffer + ((pm->width << 2) * y) + (x << 2);
|
|
if (x1 < 0 || x1 > (pm->width - 1))
|
|
srcx1 = (unsigned char *)zerouc;
|
|
else
|
|
srcx1 = pm->buffer + ((pm->width << 2) * y) + (x1 << 2);
|
|
|
|
if (y1 < 0 || y1 > (pm->height - 1))
|
|
srcy0 = (unsigned char *)zerouc;
|
|
else
|
|
srcy0 = pm->buffer + ((pm->width << 2) * y1) + (x << 2);
|
|
|
|
if (x1 < 0 || x1 > (pm->width - 1) || y1 < 0 || y1 > (pm->height - 1))
|
|
srcy1 = (unsigned char *)zerouc;
|
|
else
|
|
srcy1 = pm->buffer + ((pm->width << 2) * y1) + (x1 << 2);
|
|
|
|
interp0[0] = (xdiff1 * (*(srcx0++)) + (xdiff * (*(srcx1++))));
|
|
interp0[1] = (xdiff1 * (*(srcx0++)) + (xdiff * (*(srcx1++))));
|
|
interp0[2] = (xdiff1 * (*(srcx0++)) + (xdiff * (*(srcx1++))));
|
|
interp0[3] = (xdiff1 * (*(srcx0++)) + (xdiff * (*(srcx1++))));
|
|
|
|
interp1[0] = (xdiff1 * (*(srcy0++)) + (xdiff * (*(srcy1++))));
|
|
interp1[1] = (xdiff1 * (*(srcy0++)) + (xdiff * (*(srcy1++))));
|
|
interp1[2] = (xdiff1 * (*(srcy0++)) + (xdiff * (*(srcy1++))));
|
|
interp1[3] = (xdiff1 * (*(srcy0++)) + (xdiff * (*(srcy1++))));
|
|
|
|
v[0] = ((ydiff1 * interp0[0]) + (ydiff * interp1[0])) * _inv255;
|
|
v[1] = ((ydiff1 * interp0[1]) + (ydiff * interp1[1])) * _inv255;
|
|
v[2] = ((ydiff1 * interp0[2]) + (ydiff * interp1[2])) * _inv255;
|
|
return (255 - ((ydiff1 * interp0[3]) + (ydiff * interp1[3]))) * _inv255;
|
|
} else {
|
|
zero3f(v);
|
|
return 1.0F;
|
|
}
|
|
}
|
|
zero3f(v);
|
|
return 1.0F;
|
|
}
|
|
|
|
int CharacterGetHeight(PyMOLGlobals * G, int id)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
if((id > 0) && (id <= I->MaxAlloc)) {
|
|
return I->Char[id].Height;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int CharacterGetGeometry(PyMOLGlobals * G, int id,
|
|
int *width, int *height,
|
|
float *xorig, float *yorig, float *advance)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
if((id > 0) && (id <= I->MaxAlloc)) {
|
|
CharRec *ch = I->Char + id;
|
|
*width = ch->Width;
|
|
*height = ch->Height;
|
|
*xorig = ch->XOrig;
|
|
*yorig = ch->YOrig;
|
|
*advance = ch->Advance;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int CharacterInit(PyMOLGlobals * G)
|
|
{
|
|
CCharacter *I = nullptr;
|
|
if((I = (G->Character = pymol::calloc<CCharacter>(1)))) {
|
|
I->MaxAlloc = 5;
|
|
I->Char = VLACalloc(CharRec, I->MaxAlloc + 1);
|
|
{
|
|
int a;
|
|
for(a = 2; a <= I->MaxAlloc; a++)
|
|
I->Char[a].Prev = a - 1;
|
|
I->LastFree = I->MaxAlloc;
|
|
}
|
|
I->Hash = pymol::calloc<int>((HASH_MASK + 1));
|
|
I->TargetMaxUsage = 25000;
|
|
return 1;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
static void CharacterAllocMore(PyMOLGlobals * G)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
int new_max = I->MaxAlloc * 2;
|
|
VLACheck(I->Char, CharRec, new_max);
|
|
{
|
|
int a;
|
|
I->Char[I->MaxAlloc + 1].Prev = I->LastFree;
|
|
for(a = I->MaxAlloc + 2; a <= new_max; a++)
|
|
I->Char[a].Prev = a - 1;
|
|
I->LastFree = new_max;
|
|
I->MaxAlloc = new_max;
|
|
}
|
|
}
|
|
|
|
void CharacterSetRetention(PyMOLGlobals * G, int retain_all)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
I->RetainAll = retain_all;
|
|
}
|
|
|
|
static void CharacterPurgeOldest(PyMOLGlobals * G)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
int max_kill = 10;
|
|
|
|
while(I->NUsed > I->TargetMaxUsage) {
|
|
if(!(max_kill--))
|
|
break; /* if over, only purge a few entries at a time */
|
|
{
|
|
int id = I->OldestUsed;
|
|
|
|
if(id) {
|
|
int next;
|
|
|
|
/* trim from end of list */
|
|
|
|
if((next = I->Char[id].Next)) {
|
|
I->Char[next].Prev = 0;
|
|
I->OldestUsed = next;
|
|
}
|
|
|
|
{ /* excise character from hash table linked list */
|
|
int hash_code = I->Char[id].Fngrprnt.hash_code;
|
|
int hash_prev = I->Char[id].HashPrev;
|
|
int hash_next = I->Char[id].HashNext;
|
|
|
|
if(hash_prev) {
|
|
I->Char[hash_prev].HashNext = hash_next;
|
|
} else {
|
|
I->Hash[hash_code] = hash_next;
|
|
}
|
|
if(hash_next) {
|
|
I->Char[hash_next].HashPrev = hash_prev;
|
|
}
|
|
|
|
}
|
|
|
|
/* free and reinitialize */
|
|
|
|
PixmapPurge(&I->Char[id].Pixmap);
|
|
UtilZeroMem(I->Char + id, sizeof(CharRec));
|
|
|
|
/* add to free chain */
|
|
|
|
I->Char[id].Prev = I->LastFree;
|
|
I->LastFree = id;
|
|
I->NUsed--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int CharacterGetNew(PyMOLGlobals * G)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
int result = 0;
|
|
if(!I->LastFree)
|
|
CharacterAllocMore(G);
|
|
if(I->LastFree) {
|
|
|
|
/* remove from free chain */
|
|
result = I->LastFree;
|
|
I->LastFree = I->Char[result].Prev;
|
|
|
|
/* backwards-link (for continuous GC) */
|
|
|
|
if(I->NewestUsed) {
|
|
I->Char[I->NewestUsed].Next = result; /* double-link list */
|
|
} else {
|
|
I->OldestUsed = result;
|
|
}
|
|
|
|
/* forwards-link */
|
|
|
|
I->Char[result].Prev = I->NewestUsed;
|
|
I->NewestUsed = result;
|
|
|
|
I->NUsed++;
|
|
|
|
if(!I->RetainAll)
|
|
CharacterPurgeOldest(G);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void CharacterFree(PyMOLGlobals * G)
|
|
{
|
|
CCharacter *I = G->Character;
|
|
{
|
|
int a;
|
|
a = I->NewestUsed;
|
|
while(a) {
|
|
PixmapPurge(&I->Char[a].Pixmap);
|
|
a = I->Char[a].Prev;
|
|
}
|
|
}
|
|
FreeP(I->Hash);
|
|
VLAFreeP(I->Char);
|
|
FreeP(G->Character);
|
|
}
|