mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-04 20:04:21 +08:00
446 lines
13 KiB
C++
446 lines
13 KiB
C++
|
|
/*
|
|
A* -------------------------------------------------------------------
|
|
B* This file contains source code for the PyMOL computer program
|
|
C* copyright 1998-2003 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_python.h"
|
|
|
|
#include "os_gl.h"
|
|
#include "Base.h"
|
|
#include "FontGLUT.h"
|
|
#include "Text.h"
|
|
#include "Ray.h"
|
|
#include "Character.h"
|
|
#include "Scene.h"
|
|
#include "Util.h"
|
|
#include "Matrix.h"
|
|
|
|
static void FontGLUTSave(CFontGLUT * I)
|
|
{
|
|
glGetIntegerv(GL_UNPACK_SWAP_BYTES, (GLint *) & I->swapbytes);
|
|
glGetIntegerv(GL_UNPACK_LSB_FIRST, (GLint *) & I->lsbfirst);
|
|
glGetIntegerv(GL_UNPACK_ROW_LENGTH, (GLint *) & I->rowlength);
|
|
glGetIntegerv(GL_UNPACK_SKIP_ROWS, (GLint *) & I->skiprows);
|
|
glGetIntegerv(GL_UNPACK_SKIP_PIXELS, (GLint *) & I->skippixels);
|
|
glGetIntegerv(GL_UNPACK_ALIGNMENT, (GLint *) & I->alignment);
|
|
|
|
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
|
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
|
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
}
|
|
|
|
static void FontGLUTRestore(CFontGLUT * I)
|
|
{
|
|
glPixelStorei(GL_UNPACK_SWAP_BYTES, I->swapbytes);
|
|
glPixelStorei(GL_UNPACK_LSB_FIRST, I->lsbfirst);
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, I->rowlength);
|
|
glPixelStorei(GL_UNPACK_SKIP_ROWS, I->skiprows);
|
|
glPixelStorei(GL_UNPACK_SKIP_PIXELS, I->skippixels);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, I->alignment);
|
|
}
|
|
|
|
/**
|
|
* check if masked `c` equals `value`
|
|
*/
|
|
static inline bool masked_byte_equals(char c, char mask, char value) {
|
|
return (c & mask) == value;
|
|
}
|
|
|
|
static inline bool byte_check_10xxxxxx(char c) {
|
|
return masked_byte_equals(c,
|
|
0xc0 /* mask 0b11000000 */,
|
|
0x80 /* value 0b10000000 */);
|
|
}
|
|
|
|
/**
|
|
* Read the next unicode point from a UTF-8 string and advance the string
|
|
* pointer. If decoding fails, set `error` to true. If `error` is already
|
|
* true, don't attempt to decode and return the next byte value as-is.
|
|
*
|
|
* Keep it simple and assume narrow build (up to 3 bytes).
|
|
*/
|
|
static unsigned int next_utf8_character(const char * &st, bool &error) {
|
|
unsigned int c = st[0];
|
|
|
|
if (!error) {
|
|
// Byte 1:
|
|
// 0b0xxxxxxx -> 1 byte
|
|
// 0b110xxxxx -> 2 byte
|
|
// 0b1110xxxx -> 3 byte (max unicode value 0xFFFF)
|
|
// 0b11110xxx -> 4 byte (would need wide build)
|
|
|
|
if (masked_byte_equals(c, 0xe0 /* 0b11100000 */, 0xc0 /* 0b11000000 */)) {
|
|
if (byte_check_10xxxxxx(st[1])) {
|
|
c &= 0x1f /* 0b00011111 */;
|
|
c = (c << 6) | (st[1] & 0x3f) /* 0b00111111 */;
|
|
st += 1;
|
|
} else {
|
|
error = true;
|
|
}
|
|
} else if (masked_byte_equals(c, 0xf0 /* 0b11110000 */, 0xe0 /* 0b11100000 */)) {
|
|
if (byte_check_10xxxxxx(st[1]) && byte_check_10xxxxxx(st[2])) {
|
|
c &= 0x0f /* 0b00001111 */;
|
|
c = (c << 6) | (st[1] & 0x3f) /* 0b00111111 */;
|
|
c = (c << 6) | (st[2] & 0x3f) /* 0b00111111 */;
|
|
st += 2;
|
|
} else {
|
|
error = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
st += 1;
|
|
return c;
|
|
}
|
|
|
|
const char* CFontGLUT::RenderOpenGL(const RenderInfo* info, const char* st,
|
|
float size, const float* rpos, bool needSize, short relativeMode,
|
|
bool shouldRender, CGO* shaderCGO)
|
|
{
|
|
auto I = this;
|
|
PyMOLGlobals *G = I->G;
|
|
if(G->ValidContext) {
|
|
int c;
|
|
FontGLUTBitmapFontRec const *font_info = glutFont;
|
|
int first, last;
|
|
FontGLUTBitmapCharRec const *ch;
|
|
int textured = true && shaderCGO;
|
|
int pushed = OrthoGetPushed(G);
|
|
int sampling = 1;
|
|
const float _0 = 0.0F, _1 = 1.0F, _m1 = -1.0F;
|
|
float x_indent = 0.0F, y_indent = 0.0F, z_indent = 0.0F;
|
|
|
|
if(info)
|
|
sampling = info->sampling;
|
|
|
|
sampling = DIP2PIXEL(sampling);
|
|
|
|
if(st && (*st)) {
|
|
|
|
float v_scale = SceneGetScreenVertexScale(G, nullptr);
|
|
|
|
first = font_info->first;
|
|
last = first + font_info->num_chars;
|
|
|
|
if(rpos) {
|
|
if(rpos[0] < _1) { /* we need to measure the string width before starting to draw */
|
|
float factor = rpos[0] / 2.0F - 0.5F;
|
|
const char *sst = st;
|
|
if(factor < _m1)
|
|
factor = _m1;
|
|
if(factor > _0)
|
|
factor = _0;
|
|
bool utf8_error = false;
|
|
while((c = next_utf8_character(sst, utf8_error))) {
|
|
if(c < first || c >= last) {
|
|
c = '?';
|
|
}
|
|
{
|
|
ch = font_info->ch[c - first];
|
|
if(ch) {
|
|
x_indent -= factor * ch->advance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(rpos[0] < _m1) {
|
|
x_indent -= (rpos[0] + _1) / v_scale;
|
|
} else if(rpos[0] > _1) {
|
|
x_indent -= (rpos[0] - _1) / v_scale;
|
|
}
|
|
if(rpos[1] < _1) {
|
|
float factor = -rpos[1] / 2.0F + 0.5F;
|
|
if(factor > _1)
|
|
factor = _1;
|
|
if(factor < _0)
|
|
factor = _0;
|
|
y_indent = 0.75 * size * factor;
|
|
}
|
|
if(rpos[1] < _m1) {
|
|
y_indent -= (rpos[1] + _1) / v_scale;
|
|
} else if(rpos[1] > _1) {
|
|
y_indent -= (rpos[1] - _1) / v_scale;
|
|
}
|
|
z_indent = rpos[2];
|
|
if(z_indent < _0) { /* leave room for fonts of finite depth */
|
|
z_indent += _1;
|
|
if(z_indent > _0)
|
|
z_indent = _0;
|
|
} else if(z_indent > _0) {
|
|
z_indent -= _1;
|
|
if(z_indent < _0)
|
|
z_indent = _0;
|
|
}
|
|
}
|
|
|
|
if(textured && !pushed) {
|
|
float *v = TextGetPos(G);
|
|
float loc[3];
|
|
float zero[3] = { 0.0F, 0.0F, 0.0F };
|
|
if(rpos) {
|
|
if(info->ortho) {
|
|
float orig[3];
|
|
SceneOriginGet(G, orig);
|
|
SceneGetEyeNormal(G, orig, loc);
|
|
} else {
|
|
SceneGetEyeNormal(G, v, loc);
|
|
}
|
|
scale3f(loc, z_indent, loc);
|
|
add3f(v, loc, loc);
|
|
v = loc;
|
|
}
|
|
ScenePushRasterMatrix(G, v);
|
|
TextSetPos(G, zero);
|
|
} else if(!textured) {
|
|
if(rpos) {
|
|
float *v = TextGetPos(G);
|
|
float loc[3];
|
|
if(info->ortho) {
|
|
float orig[3];
|
|
SceneOriginGet(G, orig);
|
|
SceneGetEyeNormal(G, orig, loc);
|
|
} else {
|
|
SceneGetEyeNormal(G, v, loc);
|
|
}
|
|
scale3f(loc, z_indent, loc);
|
|
add3f(v, loc, loc);
|
|
TextSetPos(G, loc);
|
|
}
|
|
}
|
|
|
|
if(rpos) {
|
|
if(textured) {
|
|
TextIndent(G, x_indent, y_indent);
|
|
} else {
|
|
float *v = TextGetPos(G);
|
|
float indent[3];
|
|
float loc[3];
|
|
float *matrix = SceneGetMatrix(G);
|
|
indent[0] = -v_scale * x_indent;
|
|
indent[1] = -v_scale * y_indent;
|
|
indent[2] = _0;
|
|
MatrixInvTransformC44fAs33f3f(matrix, indent, indent);
|
|
add3f(indent, v, loc);
|
|
TextSetPos(G, loc);
|
|
}
|
|
}
|
|
|
|
if(!textured) {
|
|
glColor3fv(TextGetColor(G));
|
|
|
|
glRasterPos4fv(TextGetPos(G));
|
|
FontGLUTSave(I);
|
|
}
|
|
|
|
if(textured)
|
|
CharacterRenderOpenGLPrime(G, info);
|
|
|
|
bool utf8_error = false;
|
|
while((c = next_utf8_character(st, utf8_error))) {
|
|
if(c < first || c >= last) {
|
|
c = '?';
|
|
}
|
|
{
|
|
ch = font_info->ch[c - first];
|
|
if(ch) {
|
|
if(!textured) {
|
|
|
|
#ifndef PURE_OPENGL_ES_2
|
|
glBitmap(ch->width, ch->height,
|
|
ch->xorig, ch->yorig, ch->advance, 0, ch->bitmap);
|
|
#endif
|
|
TextAdvance(G, ch->advance);
|
|
} else {
|
|
CharFngrprnt fprnt;
|
|
unsigned char *rgba;
|
|
UtilZeroMem(&fprnt, sizeof(fprnt));
|
|
fprnt.u.i.text_id = I->TextID;
|
|
fprnt.u.i.size = sampling;
|
|
rgba = fprnt.u.i.color;
|
|
TextGetColorUChar(G, rgba, rgba + 1, rgba + 2, rgba + 3);
|
|
fprnt.u.i.ch = (unsigned int) c;
|
|
{
|
|
int id = CharacterFind(G, &fprnt);
|
|
if(!id) {
|
|
id = CharacterNewFromBitmap(G, ch->width,
|
|
ch->height,
|
|
(unsigned char *) ch->bitmap,
|
|
(float) ch->xorig,
|
|
(float) ch->yorig,
|
|
(float) ch->advance, &fprnt, sampling);
|
|
}
|
|
if(id) {
|
|
CharacterRenderOpenGL(G, info, id, false, relativeMode, shaderCGO); /* handles advance */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(textured)
|
|
CharacterRenderOpenGLDone(G, info);
|
|
|
|
if(textured && !pushed) {
|
|
ScenePopRasterMatrix(G);
|
|
}
|
|
if(!textured) {
|
|
FontGLUTRestore(I);
|
|
glFlush(); /* workaround for screen flashes on late-model nVidia hardware */
|
|
}
|
|
}
|
|
}
|
|
return st;
|
|
}
|
|
|
|
const char* CFontGLUT::RenderOpenGLFlat(const RenderInfo* info, const char* st,
|
|
float size, const float* rpos, bool needSize, short relativeMode,
|
|
bool shouldRender, CGO* shaderCGO)
|
|
{
|
|
return RenderOpenGL(
|
|
info, st, size, rpos, needSize, relativeMode, shouldRender, shaderCGO);
|
|
}
|
|
|
|
const char* CFontGLUT::RenderRay(CRay* ray, const char* st, float size,
|
|
const float* rpos, bool needSize, short relativeMode)
|
|
{
|
|
auto I = this;
|
|
PyMOLGlobals *G = I->G;
|
|
int c;
|
|
FontGLUTBitmapFontRec const *font_info = glutFont;
|
|
int first, last;
|
|
FontGLUTBitmapCharRec const *ch;
|
|
CharFngrprnt fprnt;
|
|
unsigned char *rgba;
|
|
int sampling = 1;
|
|
float xn[3], yn[3], x_adj[3], y_adj[3], pos[3], *v;
|
|
const float _0 = 0.0F, _1 = 1.0F, _m1 = -1.0F;
|
|
float x_indent = 0.0F, y_indent = 0.0F, z_indent = 0.0F;
|
|
sampling = ray->Sampling;
|
|
|
|
if(st && (*st)) {
|
|
float v_scale = SceneGetScreenVertexScale(G, nullptr);
|
|
|
|
if(rpos) {
|
|
float loc[3];
|
|
v = TextGetPos(G);
|
|
if(ray->Ortho) {
|
|
float orig[3];
|
|
SceneOriginGet(G, orig);
|
|
SceneGetEyeNormal(G, orig, loc);
|
|
} else {
|
|
SceneGetEyeNormal(G, v, loc);
|
|
}
|
|
scale3f(loc, rpos[2], loc);
|
|
add3f(v, loc, loc);
|
|
TextSetPos(G, loc);
|
|
}
|
|
|
|
RayGetScaledAxes(ray, xn, yn);
|
|
|
|
UtilZeroMem(&fprnt, sizeof(fprnt));
|
|
first = font_info->first;
|
|
last = first + font_info->num_chars;
|
|
fprnt.u.i.text_id = I->TextID;
|
|
fprnt.u.i.size = sampling;
|
|
rgba = fprnt.u.i.color;
|
|
TextGetColorUChar(G, rgba, rgba + 1, rgba + 2, rgba + 3);
|
|
|
|
if(rpos) {
|
|
|
|
if(rpos[0] < _1) { /* we need to measure the string width before starting to draw */
|
|
float factor = rpos[0] / 2.0F - 0.5F;
|
|
const char *sst = st;
|
|
if(factor < _m1)
|
|
factor = -_1;
|
|
if(factor > _0)
|
|
factor = _0;
|
|
|
|
while((c = *(sst++))) {
|
|
fprnt.u.i.ch = (unsigned int) c;
|
|
ch = font_info->ch[c - first];
|
|
if(ch) {
|
|
x_indent -= 2 * factor * ch->advance;
|
|
}
|
|
}
|
|
}
|
|
if(rpos[0] < _m1) {
|
|
x_indent -= 2 * (rpos[0] + _1) / v_scale;
|
|
} else if(rpos[0] > _1) {
|
|
x_indent -= 2 * (rpos[0] - _1) / v_scale;
|
|
}
|
|
if(rpos[1] < _1) {
|
|
float factor = -rpos[1] / 2.0F + 0.5F;
|
|
if(factor > _1)
|
|
factor = _1;
|
|
if(factor < _0)
|
|
factor = _0;
|
|
y_indent = 0.75F * sampling * size * factor;
|
|
}
|
|
if(rpos[1] < _m1) {
|
|
y_indent -= 2 * (rpos[1] + _1) / v_scale;
|
|
} else if(rpos[1] > _1) {
|
|
y_indent -= 2 * (rpos[1] - _1) / v_scale;
|
|
}
|
|
z_indent = rpos[2];
|
|
if(z_indent < _0) { /* leave room for fonts of finite depth */
|
|
z_indent += _1;
|
|
if(z_indent > _0)
|
|
z_indent = _0;
|
|
} else if(z_indent > _0) {
|
|
z_indent -= _1;
|
|
if(z_indent < _0)
|
|
z_indent = _0;
|
|
}
|
|
v = TextGetPos(G);
|
|
scale3f(xn, x_indent, x_adj);
|
|
scale3f(yn, y_indent, y_adj);
|
|
subtract3f(v, x_adj, pos);
|
|
subtract3f(pos, y_adj, pos);
|
|
TextSetPos(G, pos);
|
|
}
|
|
|
|
while((c = *(st++))) {
|
|
if((c >= first) && (c < last)) {
|
|
ch = font_info->ch[c - first];
|
|
if(ch) {
|
|
fprnt.u.i.ch = (unsigned int) c;
|
|
{
|
|
int id = CharacterFind(G, &fprnt);
|
|
if(!id) {
|
|
id = CharacterNewFromBitmap(G, ch->width,
|
|
ch->height,
|
|
(unsigned char *) ch->bitmap,
|
|
(float) ch->xorig,
|
|
(float) ch->yorig,
|
|
(float) ch->advance, &fprnt, sampling);
|
|
}
|
|
if(id)
|
|
ray->character(id); /* handles advance */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return st;
|
|
}
|
|
|
|
CFontGLUT::CFontGLUT(PyMOLGlobals* G, const FontGLUTBitmapFontRec* rec)
|
|
: CFont(G)
|
|
, glutFont(rec)
|
|
{
|
|
}
|