/* 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) { }