Files
pymol-open-source/layer1/Character.cpp
2024-05-20 09:07:33 -04:00

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);
}