1 
2 /*
3 A* -------------------------------------------------------------------
4 B* This file contains source code for the PyMOL computer program
5 C* copyright 1998-2003 by Warren Lyford Delano of DeLano Scientific.
6 D* -------------------------------------------------------------------
7 E* It is unlawful to modify or remove this copyright notice.
8 F* -------------------------------------------------------------------
9 G* Please see the accompanying LICENSE file for further information.
10 H* --------------------------------------------------\-----------------
11 I* Additional authors of this source file include:
12 -*
13 -*
14 -*
15 Z* -------------------------------------------------------------------
16 */
17 #include"os_python.h"
18 
19 #include"MemoryDebug.h"
20 #include"Text.h"
21 #include"Font.h"
22 
23 #include"FontGLUT.h"
24 #include"FontType.h"
25 #include"Color.h"
26 #include"Vector.h"
27 #include"Executive.h"
28 #include"Picking.h"
29 
30 #ifdef _PYMOL_FREETYPE
31 #include "FontTTF.h"
32 #include "FontTTF2.h"
33 #endif
34 
35 #define FONT_NAME_MAX 255
36 
37 #define TEXT_DEFAULT_SIZE 12.0F
38 static const float _255 = 255.0F;
39 static const float _499 = 0.4999F;
40 
41 #define NFONTS 20
42 
43 struct CText {
44   float Pos[4];
45   float WorldPos[4];
46   float ScreenWorldOffset[3];
47   float TargetPos[3];
48   float LabelPushPos[3];
49   float LabelPos[3];
50   unsigned char LabelPosIsSet; // 1 for just z, 2 for 3f
51   float TextIndentFactor[2];
52   float Color[4];
53   unsigned char UColor[4];
54   unsigned char OutlineColor[4];
55   int Default_ID = 0;
56   float Height, Width;
57   float Spacing, Just;
58   float LabelBuf[2];
59 
60 #ifdef _WEBGL
61   bool XHRFetched[NFONTS] = {};
62   bool XHRFailed[NFONTS] = {};
63 #endif
64 
65   bool Flat = false;
66   bool IsPicking = false;
67 
68 private:
69   std::vector<std::unique_ptr<CFont>> m_fonts;
70 
71 public:
72   // TODO make const
getFontCText73   CFont* getFont(unsigned font_id) {
74     if (font_id < m_fonts.size()) {
75       return m_fonts[font_id].get();
76     }
77     return nullptr;
78   }
79 
80   //! Takes ownership of pointer
addFontCText81   void addFont(unsigned font_id, CFont* font) {
82     if (!font)
83       return;
84     VecCheck(m_fonts, font_id);
85     m_fonts[font_id].reset(font);
86     font->TextID = font_id + 1;
87   }
88 };
89 
TextUpdateUColor(CText * I)90 static void TextUpdateUColor(CText * I)
91 {
92   I->UColor[0] = (unsigned char) (_255 * I->Color[0] + _499);
93   I->UColor[1] = (unsigned char) (_255 * I->Color[1] + _499);
94   I->UColor[2] = (unsigned char) (_255 * I->Color[2] + _499);
95   I->UColor[3] = (unsigned char) (_255 * I->Color[3] + _499);
96 }
97 
TextSetLabelBkgrdInfo(PyMOLGlobals * G,float label_spacing,float label_just,const float * buff)98 void TextSetLabelBkgrdInfo(PyMOLGlobals * G, float label_spacing, float label_just, const float *buff){
99   CText *I = G->Text;
100   I->Spacing = label_spacing;
101   I->Just = label_just;
102   if (buff){
103     I->LabelBuf[0] = buff[0];
104     I->LabelBuf[1] = buff[1];
105   } else {
106     I->LabelBuf[0] = I->LabelBuf[1] = .2f;
107   }
108 }
109 
TextSetIsPicking(PyMOLGlobals * G,bool IsPicking)110 void TextSetIsPicking(PyMOLGlobals * G, bool IsPicking)
111 {
112   CText *I = G->Text;
113   I->IsPicking = IsPicking;
114 }
115 
TextGetIsPicking(PyMOLGlobals * G)116 bool TextGetIsPicking(PyMOLGlobals * G)
117 {
118   CText *I = G->Text;
119   return I->IsPicking;
120 }
121 
TextSetPosNColor(PyMOLGlobals * G,const float * pos,const float * color)122 void TextSetPosNColor(PyMOLGlobals * G, const float *pos, const float *color)
123 {
124   CText *I = G->Text;
125   copy3f(pos, I->Pos);
126   copy3f(color, I->Color);
127   I->Flat = false;
128   I->Pos[3] = 1.0F;
129   I->Color[3] = 1.0F;
130   TextUpdateUColor(I);
131 }
132 
TextAdvance(PyMOLGlobals * G,float advance)133 void TextAdvance(PyMOLGlobals * G, float advance)
134 {
135   G->Text->Pos[0] += advance;
136 }
137 
TextSetLabPos(PyMOLGlobals * G,const float * pos,const LabPosType * labpos,const char * text)138 void TextSetLabPos(PyMOLGlobals * G, const float *pos, const LabPosType * labpos, const char *text)
139 {
140   if((!labpos) || (!labpos->mode))
141     TextSetPos(G, pos);
142   else {
143     CText *I = G->Text;
144     switch (labpos->mode) {
145     default:
146       copy3f(pos, I->Pos);
147       add3f(labpos->offset, I->Pos, I->Pos);
148       break;
149     }
150   }
151 }
152 
TextIndent(PyMOLGlobals * G,float x,float y)153 void TextIndent(PyMOLGlobals * G, float x, float y)
154 {
155   CText *I = G->Text;
156   I->Pos[0] -= x;
157   I->Pos[1] -= y;
158 }
159 
TextSetPos(PyMOLGlobals * G,const float * pos)160 void TextSetPos(PyMOLGlobals * G, const float *pos)
161 {
162   CText *I = G->Text;
163   copy3f(pos, I->Pos);
164   I->Pos[3] = 1.0F;
165 }
166 
TextSetWorldPos(PyMOLGlobals * G,const float * pos)167 void TextSetWorldPos(PyMOLGlobals * G, const float *pos)
168 {
169   CText *I = G->Text;
170   copy3f(pos, I->WorldPos);
171   I->WorldPos[3] = 1.0F;
172 }
TextGetWorldPos(PyMOLGlobals * G)173 float *TextGetWorldPos(PyMOLGlobals * G){
174   CText *I = G->Text;
175   return I->WorldPos;
176 }
TextSetLabelPos(PyMOLGlobals * G,const float * pos)177 void TextSetLabelPos(PyMOLGlobals * G, const float *pos)
178 {
179   CText *I = G->Text;
180   copy3f(pos, I->LabelPos);
181 }
182 
TextGetLabelPos(PyMOLGlobals * G)183 float *TextGetLabelPos(PyMOLGlobals * G)
184 {
185   CText *I = G->Text;
186   return (I->LabelPos);
187 }
188 
TextSetLabelPosIsSet(PyMOLGlobals * G,unsigned char isSet)189 void TextSetLabelPosIsSet(PyMOLGlobals * G, unsigned char isSet)
190 {
191   CText *I = G->Text;
192   I->LabelPosIsSet = isSet;
193 }
194 
TextGetLabelPosIsSet(PyMOLGlobals * G)195 unsigned char TextGetLabelPosIsSet(PyMOLGlobals * G)
196 {
197   CText *I = G->Text;
198   return I->LabelPosIsSet;
199 }
200 
TextSetLabelPushPos(PyMOLGlobals * G,const float * pos)201 void TextSetLabelPushPos(PyMOLGlobals * G, const float *pos)
202 {
203   CText *I = G->Text;
204   copy3f(pos, I->LabelPushPos);
205 }
TextGetLabelPushPos(PyMOLGlobals * G)206 float *TextGetLabelPushPos(PyMOLGlobals * G){
207   CText *I = G->Text;
208   return I->LabelPushPos;
209 }
TextSetScreenWorldOffset(PyMOLGlobals * G,const float * pos)210 void TextSetScreenWorldOffset(PyMOLGlobals * G, const float *pos)
211 {
212   CText *I = G->Text;
213   I->ScreenWorldOffset[0] = -pos[0];
214   I->ScreenWorldOffset[1] = -pos[1];
215   I->ScreenWorldOffset[2] = -pos[2];
216 }
TextGetScreenWorldOffset(PyMOLGlobals * G)217 float *TextGetScreenWorldOffset(PyMOLGlobals * G){
218   CText *I = G->Text;
219   return I->ScreenWorldOffset;
220 }
TextSetTargetPos(PyMOLGlobals * G,const float * pos)221 void TextSetTargetPos(PyMOLGlobals * G, const float *pos){
222   CText *I = G->Text;
223   copy3f(pos, I->TargetPos);
224 }
TextGetTargetPos(PyMOLGlobals * G)225 float *TextGetTargetPos(PyMOLGlobals * G){
226   CText *I = G->Text;
227   return I->TargetPos;
228 }
229 
TextDrawSubStrFast(PyMOLGlobals * G,const char * c,int x,int y,int start,int n ORTHOCGOARG)230 void TextDrawSubStrFast(PyMOLGlobals * G, const char *c, int x, int y, int start, int n ORTHOCGOARG)
231 {
232   c += start;
233   TextSetPos2i(G, x, y);
234   if(n)
235     while(*c) {
236       n--;
237       TextDrawChar(G, *(c++) ORTHOCGOARGVAR);
238       if(n <= 0)
239         break;
240     }
241 }
242 
TextDrawCharRepeat(PyMOLGlobals * G,char c,int x,int y,int start,int n ORTHOCGOARG)243 void TextDrawCharRepeat(PyMOLGlobals * G, char c, int x, int y, int start, int n ORTHOCGOARG)
244 {
245   c += start;
246   TextSetPos2i(G, x, y);
247   while(n) {
248     n--;
249     TextDrawChar(G, c ORTHOCGOARGVAR);
250   }
251 }
252 
TextSetPos2i(PyMOLGlobals * G,int x,int y)253 void TextSetPos2i(PyMOLGlobals * G, int x, int y)
254 {
255   CText *I = G->Text;
256   I->Pos[0] = (float) x;
257   I->Pos[1] = (float) y;
258   I->Pos[2] = 0.0F;
259   I->Pos[3] = 1.0F;
260 }
261 
TextSetPos3f(PyMOLGlobals * G,float x,float y,float z)262 static void TextSetPos3f(PyMOLGlobals * G, float x, float y, float z)
263 {
264   CText *I = G->Text;
265   I->Pos[0] = x;
266   I->Pos[1] = y;
267   I->Pos[2] = z;
268   I->Pos[3] = 1.0F;
269 }
270 
TextSetColor(PyMOLGlobals * G,const float * color)271 void TextSetColor(PyMOLGlobals * G, const float *color)
272 {
273   CText *I = G->Text;
274   copy3f(color, I->Color);
275   I->Color[3] = 1.0F;
276   I->Flat = false;
277   TextUpdateUColor(I);
278 }
279 
TextSetColor3f(PyMOLGlobals * G,float red,float green,float blue)280 void TextSetColor3f(PyMOLGlobals * G, float red, float green, float blue)
281 {
282   CText *I = G->Text;
283   I->Flat = false;
284   I->Color[0] = red;
285   I->Color[1] = green;
286   I->Color[2] = blue;
287   I->Color[3] = 1.0F;
288   TextUpdateUColor(I);
289 }
290 
TextSetOutlineColor(PyMOLGlobals * G,int color)291 void TextSetOutlineColor(PyMOLGlobals * G, int color)
292 {
293   CText *I = G->Text;
294   if(color >= 0) {
295     const float *fcolor = ColorGet(G, color);
296     I->OutlineColor[0] = (unsigned char) (_255 * fcolor[0]);
297     I->OutlineColor[1] = (unsigned char) (_255 * fcolor[1]);
298     I->OutlineColor[2] = (unsigned char) (_255 * fcolor[2]);
299     I->OutlineColor[3] = 0xFF;
300   } else {
301     I->OutlineColor[3] = 0;
302   }
303 }
304 
305 static const float _inv255 = 1.0F / 255.0F;
306 
TextSetColorFromUColor(PyMOLGlobals * G)307 void TextSetColorFromUColor(PyMOLGlobals * G)
308 {
309   CText *I = G->Text;
310   I->Color[0] = I->UColor[0] * _inv255;
311   I->Color[1] = I->UColor[1] * _inv255;
312   I->Color[2] = I->UColor[2] * _inv255;
313   I->Color[3] = 1.0F;
314 }
315 
TextGetPos(PyMOLGlobals * G)316 float *TextGetPos(PyMOLGlobals * G)
317 {
318   CText *I = G->Text;
319   return I->Pos;
320 }
321 
TextGetWidth(PyMOLGlobals * G)322 float TextGetWidth(PyMOLGlobals * G)
323 {
324   CText *I = G->Text;
325   return I->Width;
326 }
327 
TextGetHeight(PyMOLGlobals * G)328 float TextGetHeight(PyMOLGlobals * G)
329 {
330   CText *I = G->Text;
331   return I->Height;
332 }
333 
TextSetIndentFactorX(PyMOLGlobals * G,float factor)334 void TextSetIndentFactorX(PyMOLGlobals * G, float factor){
335   CText *I = G->Text;
336   I->TextIndentFactor[0] = factor;
337 }
338 
TextSetIndentFactorY(PyMOLGlobals * G,float factor)339 void TextSetIndentFactorY(PyMOLGlobals * G, float factor){
340   CText *I = G->Text;
341   I->TextIndentFactor[1] = factor;
342 }
TextGetIndentFactor(PyMOLGlobals * G)343 float *TextGetIndentFactor(PyMOLGlobals * G){
344   CText *I = G->Text;
345   return I->TextIndentFactor;
346 }
347 
TextSetWidth(PyMOLGlobals * G,float text_width)348 void TextSetWidth(PyMOLGlobals * G, float text_width)
349 {
350   CText *I = G->Text;
351   I->Width = text_width;
352 }
353 
TextSetHeight(PyMOLGlobals * G,float text_height)354 void TextSetHeight(PyMOLGlobals * G, float text_height)
355 {
356   CText *I = G->Text;
357   I->Height = text_height;
358 }
359 
TextGetColor(PyMOLGlobals * G)360 float *TextGetColor(PyMOLGlobals * G)
361 {
362   CText *I = G->Text;
363   return I->Color;
364 }
365 
TextGetColorUChar4uv(PyMOLGlobals * G)366 unsigned char *TextGetColorUChar4uv(PyMOLGlobals * G){
367   CText *I = G->Text;
368   return I->UColor;
369 }
370 
TextGetColorUChar(PyMOLGlobals * G,unsigned char * red,unsigned char * green,unsigned char * blue,unsigned char * alpha)371 void TextGetColorUChar(PyMOLGlobals * G, unsigned char *red,
372                        unsigned char *green, unsigned char *blue, unsigned char *alpha)
373 {
374   CText *I = G->Text;
375   *red = I->UColor[0];
376   *green = I->UColor[1];
377   *blue = I->UColor[2];
378   *alpha = I->UColor[3];
379 }
380 
TextGetOutlineColor(PyMOLGlobals * G,unsigned char * red,unsigned char * green,unsigned char * blue,unsigned char * alpha)381 void TextGetOutlineColor(PyMOLGlobals * G,
382                          unsigned char *red,
383                          unsigned char *green, unsigned char *blue, unsigned char *alpha)
384 {
385   CText *I = G->Text;
386   *red = I->OutlineColor[0];
387   *green = I->OutlineColor[1];
388   *blue = I->OutlineColor[2];
389   *alpha = I->OutlineColor[3];
390 }
391 
TextRenderOpenGL(PyMOLGlobals * G,RenderInfo * info,int text_id,const char * st,float size,float * rpos,short needSize,short relativeMode,short shouldRender,CGO * shaderCGO)392 const char *TextRenderOpenGL(PyMOLGlobals * G, RenderInfo * info, int text_id,
393     const char *st, float size, float *rpos,
394     short needSize, short relativeMode, short shouldRender,
395     CGO *shaderCGO)
396 {
397   CText *I = G->Text;
398 
399   if(st && (*st)) {
400     auto font = I->getFont(text_id);
401 
402     if (font) {
403         if (I->Flat) {
404           return font->RenderOpenGLFlat(info, st, size, rpos, needSize,
405               relativeMode, shouldRender, shaderCGO);
406         } else {
407           return font->RenderOpenGL(info, st, size, rpos, needSize,
408               relativeMode, shouldRender, shaderCGO);
409         }
410     }
411     /* make sure we got to end of string */
412     if(*st)
413       while(*(st++));
414   }
415   return st;
416 }
417 
TextDrawStrAt(PyMOLGlobals * G,const char * st,int x,int y ORTHOCGOARG)418 void TextDrawStrAt(PyMOLGlobals * G, const char *st, int x, int y ORTHOCGOARG)
419 {
420   CText *I = G->Text;
421   TextSetPos3f(G, (float) x, (float) y, 0.0F);
422   TextRenderOpenGL(G, NULL, I->Default_ID, st, TEXT_DEFAULT_SIZE, NULL, false, 0, 1 ORTHOCGOARGVAR);
423 }
424 
TextDrawStr(PyMOLGlobals * G,const char * st ORTHOCGOARG)425 void TextDrawStr(PyMOLGlobals * G, const char *st ORTHOCGOARG)
426 {
427   CText *I = G->Text;
428   TextRenderOpenGL(G, NULL, I->Default_ID, st, TEXT_DEFAULT_SIZE, NULL, false, 0, 1 ORTHOCGOARGVAR);
429 }
430 
TextDrawChar(PyMOLGlobals * G,char ch ORTHOCGOARG)431 void TextDrawChar(PyMOLGlobals * G, char ch ORTHOCGOARG)
432 {
433   char st[2] = { 0, 0 };
434   CText *I = G->Text;
435   st[0] = ch;
436   TextRenderOpenGL(G, NULL, I->Default_ID, st, TEXT_DEFAULT_SIZE, NULL, false, 0, 1 ORTHOCGOARGVAR);
437 }
438 
TextRenderRay(PyMOLGlobals * G,CRay * ray,int text_id,const char * st,float size,float * rpos,short needSize,short relativeMode)439 const char *TextRenderRay(PyMOLGlobals * G, CRay * ray, int text_id,
440     const char *st, float size, float *rpos, short needSize, short relativeMode)
441 {
442   CText *I = G->Text;
443 
444   if(st && (*st)) {
445     auto font = I->getFont(text_id);
446     if (font) {
447       if(size >= 0.0F)
448         size *= ray->Magnified;
449 
450       return font->RenderRay(ray, st, size, rpos, needSize, relativeMode);
451     }
452     /* make sure we got to end of string */
453     if(*st)
454       while(*(st++));
455   }
456   return st;
457 }
458 
TextInit(PyMOLGlobals * G)459 int TextInit(PyMOLGlobals * G)
460 {
461   assert(!G->Text);
462   G->Text = new CText();
463   auto I = G->Text;
464 
465   I->addFont(0, new CFontGLUT(G, &FontGLUTBitmap8By13));
466   I->addFont(1, new CFontGLUT(G, &FontGLUTBitmap9By15));
467   I->addFont(2, new CFontGLUT(G, &FontGLUTBitmapHelvetica10));
468   I->addFont(3, new CFontGLUT(G, &FontGLUTBitmapHelvetica12));
469   I->addFont(4, new CFontGLUT(G, &FontGLUTBitmapHelvetica18));
470 
471 #ifdef _PYMOL_FREETYPE
472 #if !defined(_WEBGL) || defined(_WEBGL_INCLUDE_DEFAULT_FONT)
473   I->addFont(5, FontTypeNew(G, TTF_DejaVuSans_dat, TTF_DejaVuSans_len));
474 #endif
475 #ifndef _WEBGL
476   I->addFont(6, FontTypeNew(G, TTF_DejaVuSans_Oblique_dat, TTF_DejaVuSans_Oblique_len));
477   I->addFont(7, FontTypeNew(G, TTF_DejaVuSans_Bold_dat, TTF_DejaVuSans_Bold_len));
478   I->addFont(8, FontTypeNew(G, TTF_DejaVuSans_BoldOblique_dat, TTF_DejaVuSans_BoldOblique_len));
479   I->addFont(9, FontTypeNew(G, TTF_DejaVuSerif_dat, TTF_DejaVuSerif_len));
480   I->addFont(10, FontTypeNew(G, TTF_DejaVuSerif_Bold_dat, TTF_DejaVuSerif_Bold_len));
481   I->addFont(11, FontTypeNew(G, TTF_DejaVuSansMono_dat, TTF_DejaVuSansMono_len));
482   I->addFont(12, FontTypeNew(G, TTF_DejaVuSansMono_Oblique_dat, TTF_DejaVuSansMono_Oblique_len));
483   I->addFont(13, FontTypeNew(G, TTF_DejaVuSansMono_Bold_dat, TTF_DejaVuSansMono_Bold_len));
484   I->addFont(14, FontTypeNew(G, TTF_DejaVuSansMono_BoldOblique_dat, TTF_DejaVuSansMono_BoldOblique_len));
485   I->addFont(15, FontTypeNew(G, TTF_GenR102_dat, TTF_GenR102_len));
486   I->addFont(16, FontTypeNew(G, TTF_GenI102_dat, TTF_GenI102_len));
487   I->addFont(17, FontTypeNew(G, TTF_DejaVuSerif_Oblique_dat, TTF_DejaVuSerif_Oblique_len));
488   I->addFont(18, FontTypeNew(G, TTF_DejaVuSerif_BoldOblique_dat, TTF_DejaVuSerif_BoldOblique_len));
489 #endif
490 #endif
491 
492   return true;
493 }
494 
TextFree(PyMOLGlobals * G)495 void TextFree(PyMOLGlobals * G)
496 {
497   DeleteP(G->Text);
498 }
TextGetSpacing(PyMOLGlobals * G)499 float TextGetSpacing(PyMOLGlobals * G)
500 {
501   CText *I = G->Text;
502   return I->Spacing;
503 }
TextGetJustification(PyMOLGlobals * G)504 float TextGetJustification(PyMOLGlobals * G)
505 {
506   CText *I = G->Text;
507   return I->Just;
508 }
TextGetLabelBuffer(PyMOLGlobals * G)509 float *TextGetLabelBuffer(PyMOLGlobals * G)
510 {
511   CText *I = G->Text;
512   return I->LabelBuf;
513 }
514 
515 /*
516  * GUI elements like internal menus or the wizard prompt can handle text
517  * color markup in the form "\\RGB" where RGB are three digits (0-9) or
518  * "---" to reset the color.
519  *
520  * Return true if `p` starts with "\\RGB" or "\\---".
521  */
TextStartsWithColorCode(const char * p)522 bool TextStartsWithColorCode(const char *p)
523 {
524   if (p[0] != '\\') {
525     return false;
526   }
527 
528   if (p[1] == '-') {
529     return p[2] == '-' && p[3] == '-';
530   }
531 
532   return (
533       ('0' <= p[1] && p[1] <= '9') &&
534       ('0' <= p[2] && p[2] <= '9') &&
535       ('0' <= p[3] && p[3] <= '9'));
536 }
537 
538 /*
539  * Set text color from "\\RGB" code.
540  *
541  * "\\---" -> defaultcolor
542  *
543  * Return false if `p` does not start with a color code.
544  */
TextSetColorFromCode(PyMOLGlobals * G,const char * p,const float * defaultcolor)545 bool TextSetColorFromCode(PyMOLGlobals * G,
546     const char *p,
547     const float *defaultcolor)
548 {
549   if (!TextStartsWithColorCode(p)) {
550     return false;
551   }
552 
553   if (p[1] == '-') {
554     TextSetColor(G, defaultcolor);
555   } else {
556     TextSetColor3f(G,
557         (p[1] - '0') / 9.0F,
558         (p[2] - '0') / 9.0F,
559         (p[3] - '0') / 9.0F);
560   }
561 
562   return true;
563 }
564