comparison src/gl-render.cc @ 9403:4af6e29449c1

[mq]: graphics_text_engine
author Michael Goffioul <michael.goffioul@gmail.com>
date Fri, 26 Jun 2009 21:12:09 +0100
parents eb63fbe60fab
children 7cc35bc348cc
comparison
equal deleted inserted replaced
9402:cdfb9ad48080 9403:4af6e29449c1
24 #include <config.h> 24 #include <config.h>
25 #endif 25 #endif
26 26
27 #if defined (HAVE_OPENGL) 27 #if defined (HAVE_OPENGL)
28 28
29 #include <iostream>
30
29 #include <lo-mappers.h> 31 #include <lo-mappers.h>
30 #include "oct-locbuf.h" 32 #include "oct-locbuf.h"
31 #include "gl-render.h" 33 #include "gl-render.h"
34 #include "txt-eng.h"
35 #include "txt-eng-ft.h"
32 36
33 #define LIGHT_MODE GL_FRONT_AND_BACK 37 #define LIGHT_MODE GL_FRONT_AND_BACK
34 38
35 // Win32 API requires the CALLBACK attributes for 39 // Win32 API requires the CALLBACK attributes for
36 // GLU callback functions. Define it to empty on 40 // GLU callback functions. Define it to empty on
538 draw (dynamic_cast<const surface::properties&> (props)); 542 draw (dynamic_cast<const surface::properties&> (props));
539 else if (go.isa ("patch")) 543 else if (go.isa ("patch"))
540 draw (dynamic_cast<const patch::properties&> (props)); 544 draw (dynamic_cast<const patch::properties&> (props));
541 else if (go.isa ("hggroup")) 545 else if (go.isa ("hggroup"))
542 draw (dynamic_cast<const hggroup::properties&> (props)); 546 draw (dynamic_cast<const hggroup::properties&> (props));
547 else if (go.isa ("text"))
548 draw (dynamic_cast<const text::properties&> (props));
543 else 549 else
544 warning ("opengl_renderer: cannot render object of type `%s'", 550 warning ("opengl_renderer: cannot render object of type `%s'",
545 props.graphics_object_name ().c_str ()); 551 props.graphics_object_name ().c_str ());
546 } 552 }
547 553
553 // Initialize OpenGL context 559 // Initialize OpenGL context
554 560
555 glEnable (GL_DEPTH_TEST); 561 glEnable (GL_DEPTH_TEST);
556 glDepthFunc (GL_LEQUAL); 562 glDepthFunc (GL_LEQUAL);
557 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 563 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
564 glAlphaFunc (GL_GREATER, 0.0f);
558 glEnable (GL_NORMALIZE); 565 glEnable (GL_NORMALIZE);
559 566
560 if (props.is___enhanced__ ()) 567 if (props.is___enhanced__ ())
561 { 568 {
562 glEnable (GL_BLEND); 569 glEnable (GL_BLEND);
872 } 879 }
873 880
874 std::string gridstyle = props.get_gridlinestyle (); 881 std::string gridstyle = props.get_gridlinestyle ();
875 std::string minorgridstyle = props.get_minorgridlinestyle (); 882 std::string minorgridstyle = props.get_minorgridlinestyle ();
876 883
884 set_font (props);
885
877 // X grid 886 // X grid
878 887
879 if (visible && xstate != AXE_DEPTH_DIR) 888 if (visible && xstate != AXE_DEPTH_DIR)
880 { 889 {
881 bool do_xgrid = (props.is_xgrid () && (gridstyle != "none")); 890 bool do_xgrid = (props.is_xgrid () && (gridstyle != "none"));
882 bool do_xminorgrid = (props.is_xminorgrid () && (minorgridstyle != "none")); 891 bool do_xminorgrid = (props.is_xminorgrid () && (minorgridstyle != "none"));
883 bool do_xminortick = props.is_xminortick (); 892 bool do_xminortick = props.is_xminortick ();
884 Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ()); 893 Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ());
885 // FIXME: use pre-computed minor ticks 894 // FIXME: use pre-computed minor ticks
886 Matrix xmticks; 895 Matrix xmticks;
887 // FIXME: use xticklabels property 896 string_vector xticklabels = props.get_xticklabel ().all_strings ();
888 string_vector xticklabels;
889 int wmax = 0, hmax = 0; 897 int wmax = 0, hmax = 0;
890 bool tick_along_z = xisinf (fy); 898 bool tick_along_z = xisinf (fy);
891 Matrix tickpos (xticks.numel (), 3); 899 Matrix tickpos (xticks.numel (), 3);
892 900
893 set_color (props.get_xcolor_rgb ()); 901 set_color (props.get_xcolor_rgb ());
955 tickpos(i,2) = zPlane; 963 tickpos(i,2) = zPlane;
956 } 964 }
957 glEnd (); 965 glEnd ();
958 } 966 }
959 967
960 // FIXME: tick texts 968 // tick texts
969 if (xticklabels.numel () > 0)
970 {
971 int n = std::min (xticklabels.numel (), xticks.numel ());
972 int halign = (xstate == AXE_HORZ_DIR ? 1 : (xySym ? 0 : 2));
973 int valign = (xstate == AXE_VERT_DIR
974 ? 1
975 : (zd*zv(2) <= 0 && !x2Dtop ? 2 : 0));
976
977 for (int i = 0; i < n; i++)
978 {
979 // FIXME: as tick text is transparent, shouldn't be
980 // drawn after axes object, for correct rendering?
981 Matrix b = draw_text (xticklabels(i),
982 tickpos(i,0), tickpos(i,1), tickpos(i,2),
983 halign, valign);
984
985 wmax = std::max (wmax, static_cast<int> (b(2)));
986 hmax = std::max (hmax, static_cast<int> (b(3)));
987 }
988 }
961 989
962 // minor grid lines 990 // minor grid lines
963 if (do_xminorgrid) 991 if (do_xminorgrid)
964 { 992 {
965 set_linestyle (minorgridstyle, true); 993 set_linestyle (minorgridstyle, true);
1023 } 1051 }
1024 } 1052 }
1025 1053
1026 text::properties& xlabel_props = 1054 text::properties& xlabel_props =
1027 reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_xlabel ()).get_properties ()); 1055 reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_xlabel ()).get_properties ());
1056
1057 xlabel_props.set_visible ("on");
1028 1058
1029 // FIXME: auto-positioning should be disabled if the 1059 // FIXME: auto-positioning should be disabled if the
1030 // label has been positioned manually 1060 // label has been positioned manually
1031 if (! xlabel_props.get_string ().empty ()) 1061 if (! xlabel_props.get_string ().empty ())
1032 { 1062 {
1058 p = xform.untransform (p(0), p(1), p(2), true); 1088 p = xform.untransform (p(0), p(1), p(2), true);
1059 xlabel_props.set_position (p.extract_n (0, 3).transpose ()); 1089 xlabel_props.set_position (p.extract_n (0, 3).transpose ());
1060 xlabel_props.set_rotation (angle); 1090 xlabel_props.set_rotation (angle);
1061 } 1091 }
1062 } 1092 }
1093 else
1094 {
1095 gh_manager::get_object (props.get_xlabel ()).set ("visible", "off");
1096 }
1063 1097
1064 // Y grid 1098 // Y grid
1065 1099
1066 if (ystate != AXE_DEPTH_DIR && visible) 1100 if (ystate != AXE_DEPTH_DIR && visible)
1067 { 1101 {
1069 bool do_yminorgrid = (props.is_yminorgrid () && (minorgridstyle != "none")); 1103 bool do_yminorgrid = (props.is_yminorgrid () && (minorgridstyle != "none"));
1070 bool do_yminortick = props.is_yminortick (); 1104 bool do_yminortick = props.is_yminortick ();
1071 Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ()); 1105 Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ());
1072 // FIXME: use pre-computed minor ticks 1106 // FIXME: use pre-computed minor ticks
1073 Matrix ymticks; 1107 Matrix ymticks;
1074 // FIXME: use yticklabels property 1108 string_vector yticklabels = props.get_yticklabel ().all_strings ();
1075 string_vector yticklabels;
1076 int wmax = 0, hmax = 0; 1109 int wmax = 0, hmax = 0;
1077 bool tick_along_z = xisinf (fx); 1110 bool tick_along_z = xisinf (fx);
1078 Matrix tickpos (yticks.numel (), 3); 1111 Matrix tickpos (yticks.numel (), 3);
1079 1112
1080 set_color (props.get_ycolor_rgb ()); 1113 set_color (props.get_ycolor_rgb ());
1142 tickpos(i,2) = zPlane; 1175 tickpos(i,2) = zPlane;
1143 } 1176 }
1144 glEnd (); 1177 glEnd ();
1145 } 1178 }
1146 1179
1147 // FIXME: tick texts 1180 // tick texts
1181 if (yticklabels.numel () > 0)
1182 {
1183 int n = std::min (yticklabels.numel (), yticks.numel ());
1184 int halign = (ystate == AXE_HORZ_DIR ? 1 : (!xySym || y2Dright ? 0 : 2));
1185 int valign = (ystate == AXE_VERT_DIR ? 1 : (zd*zv(2) <= 0 ? 2 : 0));
1186
1187 for (int i = 0; i < n; i++)
1188 {
1189 // FIXME: as tick text is transparent, shouldn't be
1190 // drawn after axes object, for correct rendering?
1191 Matrix b = draw_text (yticklabels(i),
1192 tickpos(i,0), tickpos(i,1), tickpos(i,2),
1193 halign, valign);
1194
1195 wmax = std::max (wmax, static_cast<int> (b(2)));
1196 hmax = std::max (hmax, static_cast<int> (b(3)));
1197 }
1198 }
1148 1199
1149 // minor grid lines 1200 // minor grid lines
1150 if (do_yminorgrid) 1201 if (do_yminorgrid)
1151 { 1202 {
1152 set_linestyle (minorgridstyle, true); 1203 set_linestyle (minorgridstyle, true);
1210 } 1261 }
1211 } 1262 }
1212 1263
1213 text::properties& ylabel_props = 1264 text::properties& ylabel_props =
1214 reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_ylabel ()).get_properties ()); 1265 reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_ylabel ()).get_properties ());
1266
1267 ylabel_props.set_visible ("on");
1215 1268
1216 // FIXME: auto-positioning should be disabled if the 1269 // FIXME: auto-positioning should be disabled if the
1217 // label has been positioned manually 1270 // label has been positioned manually
1218 if (! ylabel_props.get_string ().empty ()) 1271 if (! ylabel_props.get_string ().empty ())
1219 { 1272 {
1245 p = xform.untransform(p(0), p(1), p(2), true); 1298 p = xform.untransform(p(0), p(1), p(2), true);
1246 ylabel_props.set_position (p.extract_n (0, 3).transpose ()); 1299 ylabel_props.set_position (p.extract_n (0, 3).transpose ());
1247 ylabel_props.set_rotation (angle); 1300 ylabel_props.set_rotation (angle);
1248 } 1301 }
1249 } 1302 }
1303 else
1304 {
1305 gh_manager::get_object (props.get_ylabel ()).set ("visible", "off");
1306 }
1250 1307
1251 // Z Grid 1308 // Z Grid
1252 1309
1253 if (zstate != AXE_DEPTH_DIR && visible) 1310 if (zstate != AXE_DEPTH_DIR && visible)
1254 { 1311 {
1256 bool do_zminorgrid = (props.is_zminorgrid () && (minorgridstyle != "none")); 1313 bool do_zminorgrid = (props.is_zminorgrid () && (minorgridstyle != "none"));
1257 bool do_zminortick = props.is_zminortick (); 1314 bool do_zminortick = props.is_zminortick ();
1258 Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ()); 1315 Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ());
1259 // FIXME: use pre-computed minor ticks 1316 // FIXME: use pre-computed minor ticks
1260 Matrix zmticks; 1317 Matrix zmticks;
1261 // FIXME: use zticklabels property 1318 string_vector zticklabels = props.get_zticklabel ().all_strings ();
1262 string_vector zticklabels;
1263 int wmax = 0, hmax = 0; 1319 int wmax = 0, hmax = 0;
1264 Matrix tickpos (zticks.numel (), 3); 1320 Matrix tickpos (zticks.numel (), 3);
1265 1321
1266 set_color (props.get_zcolor_rgb ()); 1322 set_color (props.get_zcolor_rgb ());
1267 1323
1362 glEnd (); 1418 glEnd ();
1363 } 1419 }
1364 } 1420 }
1365 1421
1366 // FIXME: tick texts 1422 // FIXME: tick texts
1423 if (zticklabels.numel () > 0)
1424 {
1425 int n = std::min (zticklabels.numel (), zticks.numel ());
1426 int halign = 2;
1427 int valign = (zstate == AXE_VERT_DIR ? 1 : (zd*zv(2) < 0 ? 3 : 2));
1428
1429 for (int i = 0; i < n; i++)
1430 {
1431 // FIXME: as tick text is transparent, shouldn't be
1432 // drawn after axes object, for correct rendering?
1433 Matrix b = draw_text (zticklabels(i),
1434 tickpos(i,0), tickpos(i,1), tickpos(i,2),
1435 halign, valign);
1436
1437 wmax = std::max (wmax, static_cast<int> (b(2)));
1438 hmax = std::max (hmax, static_cast<int> (b(3)));
1439 }
1440 }
1367 1441
1368 // minor grid lines 1442 // minor grid lines
1369 if (do_zminorgrid) 1443 if (do_zminorgrid)
1370 { 1444 {
1371 set_linestyle (minorgridstyle, true); 1445 set_linestyle (minorgridstyle, true);
1454 } 1528 }
1455 } 1529 }
1456 1530
1457 text::properties& zlabel_props = 1531 text::properties& zlabel_props =
1458 reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_zlabel ()).get_properties ()); 1532 reinterpret_cast<text::properties&> (gh_manager::get_object (props.get_zlabel ()).get_properties ());
1533
1534 zlabel_props.set_visible ("on");
1459 1535
1460 // FIXME: auto-positioning should be disabled if the 1536 // FIXME: auto-positioning should be disabled if the
1461 // label has been positioned manually 1537 // label has been positioned manually
1462 if (! zlabel_props.get_string ().empty ()) 1538 if (! zlabel_props.get_string ().empty ())
1463 { 1539 {
1510 p = xform.untransform (p(0), p(1), p(2), true); 1586 p = xform.untransform (p(0), p(1), p(2), true);
1511 zlabel_props.set_position (p.extract_n (0, 3).transpose ()); 1587 zlabel_props.set_position (p.extract_n (0, 3).transpose ());
1512 zlabel_props.set_rotation (angle); 1588 zlabel_props.set_rotation (angle);
1513 } 1589 }
1514 } 1590 }
1591 else
1592 {
1593 gh_manager::get_object (props.get_zlabel ()).set ("visible", "off");
1594 }
1515 1595
1516 set_linestyle ("-"); 1596 set_linestyle ("-");
1517 1597
1518 // Title 1598 // Title
1519 1599
1535 // Children 1615 // Children
1536 1616
1537 if (antialias == GL_TRUE) 1617 if (antialias == GL_TRUE)
1538 glEnable (GL_LINE_SMOOTH); 1618 glEnable (GL_LINE_SMOOTH);
1539 1619
1540 Matrix children = props.get_children (); 1620 Matrix children = props.get_all_children ();
1541 std::list<graphics_object> obj_list; 1621 std::list<graphics_object> obj_list;
1542 std::list<graphics_object>::iterator it; 1622 std::list<graphics_object>::iterator it;
1543 1623
1544 // 1st pass: draw light objects 1624 // 1st pass: draw light objects
1545 1625
1562 while (it != obj_list.end ()) 1642 while (it != obj_list.end ())
1563 { 1643 {
1564 graphics_object go = (*it); 1644 graphics_object go = (*it);
1565 1645
1566 // FIXME: check whether object has "units" property and it is set to "data" 1646 // FIXME: check whether object has "units" property and it is set to "data"
1567 if (! go.isa ("text") || go.get ("units").string_value () == "data") 1647 if (! go.isa ("text") || go.get (caseless_str ("units")).string_value () == "data")
1568 { 1648 {
1569 set_clipping (go.get_properties ().is_clipping ()); 1649 set_clipping (go.get_properties ().is_clipping ());
1570 draw (go); 1650 draw (go);
1571 1651
1572 it = obj_list.erase (it); 1652 it = obj_list.erase (it);
2570 { 2650 {
2571 draw (props.get_children ()); 2651 draw (props.get_children ());
2572 } 2652 }
2573 2653
2574 void 2654 void
2655 opengl_renderer::draw (const text::properties& props)
2656 {
2657 if (props.get_string ().empty ())
2658 return;
2659
2660 set_font (props);
2661 set_color (props.get_color_rgb ());
2662
2663 // FIXME: take "units" into account
2664 Matrix pos = props.get_position ().matrix_value ();
2665 int halign = 0, valign = 0;
2666
2667 if (props.horizontalalignment_is ("center"))
2668 halign = 1;
2669 else if (props.horizontalalignment_is ("right"))
2670 halign = 2;
2671
2672 if (props.verticalalignment_is ("top"))
2673 valign = 2;
2674 else if (props.verticalalignment_is ("baseline"))
2675 valign = 3;
2676 else if (props.verticalalignment_is ("middle"))
2677 valign = 1;
2678
2679 // FIXME: handle margin and surrounding box
2680
2681 draw_text (props.get_string (),
2682 pos(0), pos(1), pos(2),
2683 halign, valign, props.get_rotation ());
2684 }
2685
2686 void
2575 opengl_renderer::set_viewport (int w, int h) 2687 opengl_renderer::set_viewport (int w, int h)
2576 { 2688 {
2577 glViewport (0, 0, w, h); 2689 glViewport (0, 0, w, h);
2578 } 2690 }
2579 2691
2580 void 2692 void
2581 opengl_renderer::set_color (const Matrix& c) 2693 opengl_renderer::set_color (const Matrix& c)
2582 { 2694 {
2583 glColor3dv (c.data ()); 2695 glColor3dv (c.data ());
2696 #if HAVE_FREETYPE
2697 text_renderer.set_color (c);
2698 #endif
2699 }
2700
2701 void
2702 opengl_renderer::set_font (const base_properties& props)
2703 {
2704 #if HAVE_FREETYPE
2705 text_renderer.set_font (props);
2706 #endif
2584 } 2707 }
2585 2708
2586 void 2709 void
2587 opengl_renderer::set_polygon_offset (bool on, double offset) 2710 opengl_renderer::set_polygon_offset (bool on, double offset)
2588 { 2711 {
2867 glEndList (); 2990 glEndList ();
2868 2991
2869 return ID; 2992 return ID;
2870 } 2993 }
2871 2994
2995 Matrix
2996 opengl_renderer::draw_text (const std::string& txt,
2997 double x, double y, double z,
2998 int halign, int valign, double rotation)
2999 {
3000 #if HAVE_FREETYPE
3001 if (txt.empty ())
3002 return Matrix (1, 4, 0.0);
3003
3004 // FIXME: clip "rotation" between 0 and 360
3005
3006 int rot_mode = ft_render::ROTATION_0;
3007
3008 if (rotation == 90.0)
3009 rot_mode = ft_render::ROTATION_90;
3010 else if (rotation == 180.0)
3011 rot_mode = ft_render::ROTATION_180;
3012 else if (rotation == 270.0)
3013 rot_mode = ft_render::ROTATION_270;
3014
3015 text_element *elt = text_parser_none ().parse (txt);
3016 Matrix bbox;
3017 uint8NDArray pixels = text_renderer.render (elt, bbox, rot_mode);
3018 int x0 = 0, y0 = 0;
3019 int w = bbox(2), h = bbox(3);
3020
3021 switch (halign)
3022 {
3023 default: break;
3024 case 1: x0 = -bbox(2)/2; break;
3025 case 2: x0 = -bbox(2); break;
3026 }
3027 switch (valign)
3028 {
3029 default: break;
3030 case 1: y0 = -bbox(3)/2; break;
3031 case 2: y0 = -bbox(3); break;
3032 case 3: y0 = bbox(1); break;
3033 }
3034
3035 switch (rot_mode)
3036 {
3037 case ft_render::ROTATION_90:
3038 std::swap (x0, y0);
3039 std::swap (w, h);
3040 x0 -= bbox(3);
3041 break;
3042 case ft_render::ROTATION_180:
3043 x0 -= bbox(2);
3044 y0 -= bbox(3);
3045 break;
3046 case ft_render::ROTATION_270:
3047 std::swap (x0, y0);
3048 std::swap (w, h);
3049 y0 -= bbox(2);
3050 break;
3051 }
3052
3053 bool blend = glIsEnabled (GL_BLEND);
3054
3055 glEnable (GL_BLEND);
3056 glEnable (GL_ALPHA_TEST);
3057 glRasterPos3d (x, y, z);
3058 glBitmap(0, 0, 0, 0, x0, y0, 0);
3059 glDrawPixels (w, h,
3060 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data ());
3061 glDisable (GL_ALPHA_TEST);
3062 if (! blend)
3063 glDisable (GL_BLEND);
3064
3065 delete elt;
3066
3067 return bbox;
3068 #else
3069 ::error ("draw_text: cannot render text, Freetype library not available");
3070 return Matrix (1, 4, 0.0);
3071 #endif
3072 }
3073
2872 #endif 3074 #endif
2873 3075
2874 /* 3076 /*
2875 ;;; Local Variables: *** 3077 ;;; Local Variables: ***
2876 ;;; mode: C++ *** 3078 ;;; mode: C++ ***