diff libinterp/corefcn/gl-render.cc @ 29433:8811d8274118

Allow arbitrary text rotation onscreen (bug #33118) * gl-render.{h,cc} (opengl-renderer::draw_texture_image): New private method with code extracted from draw_image. (opengl-renderer::render_text (uint8NDArray ...)): New method to prepare text rendering and finally call draw_texture_image. Let the existing render_text(std::string,...) call this one after preparing the pixels array using text2pixels. (opengl-renderer::set_ortho_coordinates): New method to setup orthogonal transform. (opengl-renderer::restore_previous_coordinates): New method to restore previous transform. (opengl-renderer::draw_text): Handle clipping manually for the text as well. (opengl_texture::create): Add new case for uint8 RGBA image data. * graphics.{in.h,cc} (text::properties::get_extent_matrix): Add a bool argument "rotated" that defaults to "false". Re-compute the extent matrix if necessary. (text::properties::get_extent): Make use of get_extent_matrix. (text::properties::update_text_extent): Always call text2pixels with 0 degrees rotation. * __print_parse_opts__.m: By default, also use the opengl renderer for raster images that include rotated text.
author Pantxo Diribarne <pantxo.diribarne@gmail.com>
date Fri, 12 Mar 2021 20:56:19 +0100
parents c850a9cd28f6
children 2031c0f76d02
line wrap: on
line diff
--- a/libinterp/corefcn/gl-render.cc	Fri Mar 12 19:12:04 2021 +0100
+++ b/libinterp/corefcn/gl-render.cc	Fri Mar 12 20:56:19 2021 +0100
@@ -182,7 +182,7 @@
     dim_vector dv (data.dims ());
 
     // Expect RGB data
-    if (dv.ndims () == 3 && dv(2) == 3)
+    if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4))
       {
         // FIXME: dim_vectors hold octave_idx_type values.
         //        Should we check for dimensions larger than intmax?
@@ -274,7 +274,7 @@
             glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
                                  GL_RGB, GL_UNSIGNED_SHORT, a);
           }
-        else if (data.is_uint8_type ())
+        else if (data.is_uint8_type () && dv(2) == 3)
           {
             const uint8NDArray xdata = data.uint8_array_value ();
 
@@ -293,6 +293,26 @@
             glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
                                  GL_RGB, GL_UNSIGNED_BYTE, a);
           }
+        else if (data.is_uint8_type () && dv(2) == 4)
+          {
+            const uint8NDArray xdata = data.uint8_array_value ();
+
+            OCTAVE_LOCAL_BUFFER (GLubyte, a, (4*tw*th));
+
+            for (int i = 0; i < h; i++)
+              {
+                for (int j = 0, idx = i*tw*4; j < w; j++, idx += 4)
+                  {
+                    a[idx]   = xdata(i,j,0);
+                    a[idx+1] = xdata(i,j,1);
+                    a[idx+2] = xdata(i,j,2);
+                    a[idx+3] = xdata(i,j,3);
+                  }
+              }
+
+            glfcns.glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0,
+                                 GL_RGBA, GL_UNSIGNED_BYTE, a);
+          }
         else
           {
             ok = false;
@@ -578,8 +598,6 @@
 
     void combine (GLdouble xyz[3], void *data[4], GLfloat w[4], void **out_data)
     {
-      //printf ("patch_tessellator::combine\n");
-
       vertex_data::vertex_data_rep *v[4];
       int vmax = 4;
 
@@ -3839,6 +3857,52 @@
   }
 
   void
+  opengl_renderer::set_ortho_coordinates (void)
+  {
+#if defined (HAVE_OPENGL)
+
+    m_glfcns.glMatrixMode (GL_PROJECTION);
+    m_glfcns.glPushMatrix ();
+    m_glfcns.glLoadIdentity ();
+
+    Matrix vp = get_viewport_scaled ();
+    m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2);
+    m_glfcns.glMatrixMode (GL_MODELVIEW);
+    m_glfcns.glPushMatrix ();
+    m_glfcns.glLoadIdentity ();
+
+#else
+
+    // This shouldn't happen because construction of opengl_renderer
+    // objects is supposed to be impossible if OpenGL is not available.
+
+    panic_impossible ();
+
+#endif
+  }
+
+  void
+  opengl_renderer::restore_previous_coordinates (void)
+  {
+#if defined (HAVE_OPENGL)
+
+    // Restore previous coordinate system
+    m_glfcns.glMatrixMode (GL_MODELVIEW);
+    m_glfcns.glPopMatrix();
+    m_glfcns.glMatrixMode (GL_PROJECTION);
+    m_glfcns.glPopMatrix();
+
+#else
+
+    // This shouldn't happen because construction of opengl_renderer
+    // objects is supposed to be impossible if OpenGL is not available.
+
+    panic_impossible ();
+
+#endif
+  }
+
+  void
   opengl_renderer::draw_text (const text::properties& props)
   {
 #if defined (HAVE_OPENGL)
@@ -3848,31 +3912,22 @@
 
     Matrix pos = xform.scale (props.get_data_position ());
 
-    // Handle clipping manually when drawing text background
+    // Handle clipping manually when drawing text in ortho coordinates
     if (! props.is_clipping ()
         || (clip_code (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0) == 0x40))
       {
         set_clipping (false);
+
         draw_text_background (props);
+
+        set_font (props);
+
+        render_text (props.get_pixels (), props.get_extent_matrix (),
+                     pos(0), pos(1), pos(2), props.get_rotation ());
+
         set_clipping (props.is_clipping ());
       }
 
-    set_font (props);
-
-    const Matrix bbox = props.get_extent_matrix ();
-
-    bool blend = m_glfcns.glIsEnabled (GL_BLEND);
-
-    m_glfcns.glEnable (GL_BLEND);
-    m_glfcns.glEnable (GL_ALPHA_TEST);
-    m_glfcns.glRasterPos3d (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0);
-    m_glfcns.glBitmap (0, 0, 0, 0, bbox(0), bbox(1), nullptr);
-    m_glfcns.glDrawPixels (bbox(2), bbox(3), GL_RGBA, GL_UNSIGNED_BYTE,
-                           props.get_pixels ().data ());
-    m_glfcns.glDisable (GL_ALPHA_TEST);
-    if (! blend)
-      m_glfcns.glDisable (GL_BLEND);
-
 #else
 
     octave_unused_parameter (props);
@@ -3887,7 +3942,7 @@
 
   void
   opengl_renderer::draw_text_background (const text::properties& props,
-                                         bool do_rotate)
+                                         bool /*do_rotate*/)
   {
 #if defined (HAVE_OPENGL)
 
@@ -3902,15 +3957,7 @@
                                                       pos(2), true);
 
     // Save current transform matrices and set orthogonal window coordinates
-    m_glfcns.glMatrixMode (GL_PROJECTION);
-    m_glfcns.glPushMatrix ();
-    m_glfcns.glLoadIdentity ();
-
-    Matrix vp = get_viewport_scaled ();
-    m_glfcns.glOrtho (0, vp(2), vp(3), 0, xZ1, xZ2);
-    m_glfcns.glMatrixMode (GL_MODELVIEW);
-    m_glfcns.glPushMatrix ();
-    m_glfcns.glLoadIdentity ();
+    set_ortho_coordinates ();
 
     // Translate coordinates so that the text anchor is (0,0)
     m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2));
@@ -3919,9 +3966,7 @@
     //        Handle others here.
     double rotation = props.get_rotation ();
 
-    if (do_rotate && rotation != 0.0 && rotation != 90.0
-        && rotation != 180.0 && rotation != 270.0)
-      m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0);
+    m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0);
 
     double m = points_to_pixels (props.get_margin ());
     const Matrix bbox = props.get_extent_matrix ();
@@ -3967,10 +4012,7 @@
         set_linestyle ("-");
       }
 
-    // Restore previous coordinate system
-    m_glfcns.glPopMatrix();
-    m_glfcns.glMatrixMode (GL_PROJECTION);
-    m_glfcns.glPopMatrix();
+    restore_previous_coordinates ();
 
 #else
 
@@ -3991,12 +4033,34 @@
 #if defined (HAVE_OPENGL)
 
     octave_value cdata = props.get_color_data ();
+    Matrix x = props.get_xdata ().matrix_value ();
+    Matrix y = props.get_ydata ().matrix_value ();
+
+    draw_texture_image (cdata, x, y);
+
+#else
+
+    octave_unused_parameter (props);
+
+    // This shouldn't happen because construction of opengl_renderer
+    // objects is supposed to be impossible if OpenGL is not available.
+
+    panic_impossible ();
+
+#endif
+  }
+
+  void
+  opengl_renderer::draw_texture_image (const octave_value cdata, Matrix x,
+                                       Matrix y, bool ortho)
+  {
+#if defined (HAVE_OPENGL)
+
     dim_vector dv (cdata.dims ());
     int h = dv(0);
     int w = dv(1);
     double x0, x1, y0, y1;
 
-    Matrix x = props.get_xdata ().matrix_value ();
     double dx = 1.0;
     if (w > 1)
       dx = (x(1) - x(0)) / (w - 1);
@@ -4004,7 +4068,6 @@
     x0 = x(0)-dx/2;
     x1 = x(1)+dx/2;
 
-    Matrix y = props.get_ydata ().matrix_value ();
     double dy = 1.0;
     if (h > 1)
       dy = (y(1) - y(0)) / (h - 1);
@@ -4013,7 +4076,7 @@
     y1 = y(1)+dy/2;
 
     // Expect RGB data
-    if (dv.ndims () == 3 && dv(2) == 3)
+    if (dv.ndims () == 3 && (dv(2) == 3 || dv(2) == 4))
       {
         opengl_texture tex  = opengl_texture::create (m_glfcns, cdata);
         if (tex.is_valid ())
@@ -4025,16 +4088,28 @@
             m_glfcns.glBegin (GL_QUADS);
 
             tex.tex_coord (0.0, 0.0);
-            m_glfcns.glVertex3d (x0, y0, 0.0);
+            if (ortho)
+              m_glfcns.glVertex2d (x0, y0);
+            else
+              m_glfcns.glVertex3d (x0, y0, 0.0);
 
             tex.tex_coord (1.0, 0.0);
-            m_glfcns.glVertex3d (x1, y0, 0.0);
+            if (ortho)
+              m_glfcns.glVertex2d (x1, y0);
+            else
+              m_glfcns.glVertex3d (x1, y0, 0.0);
 
             tex.tex_coord (1.0, 1.0);
-            m_glfcns.glVertex3d (x1, y1, 0.0);
+            if (ortho)
+              m_glfcns.glVertex2d (x1, y1);
+            else
+              m_glfcns.glVertex3d (x1, y1, 0.0);
 
             tex.tex_coord (0.0, 1.0);
-            m_glfcns.glVertex3d (x0, y1, 0.0);
+            if (ortho)
+              m_glfcns.glVertex2d (x0, y1);
+            else
+              m_glfcns.glVertex3d (x0, y1, 0.0);
 
             m_glfcns.glEnd ();
             m_glfcns.glDisable (GL_TEXTURE_2D);
@@ -4045,7 +4120,10 @@
 
 #else
 
-    octave_unused_parameter (props);
+    octave_unused_parameter (cdata);
+    octave_unused_parameter (x);
+    octave_unused_parameter (y);
+    octave_unused_parameter (ortho);
 
     // This shouldn't happen because construction of opengl_renderer
     // objects is supposed to be impossible if OpenGL is not available.
@@ -4795,18 +4873,7 @@
         uint8NDArray pixels;
         text_to_pixels (txt, pixels, bbox, halign, valign, rotation);
 
-        bool blend = m_glfcns.glIsEnabled (GL_BLEND);
-
-        m_glfcns.glEnable (GL_BLEND);
-        m_glfcns.glEnable (GL_ALPHA_TEST);
-        m_glfcns.glRasterPos3d (x, y, z);
-        m_glfcns.glBitmap(0, 0, 0, 0, bbox(0), bbox(1), nullptr);
-        m_glfcns.glDrawPixels (bbox(2), bbox(3),
-                               GL_RGBA, GL_UNSIGNED_BYTE, pixels.data ());
-        m_glfcns.glDisable (GL_ALPHA_TEST);
-
-        if (! blend)
-          m_glfcns.glDisable (GL_BLEND);
+        render_text (pixels, bbox, x, y, z, rotation);
       }
 
     return bbox;
@@ -4828,4 +4895,60 @@
 
 #endif
   }
+
+  void
+  opengl_renderer::render_text (uint8NDArray pixels, Matrix bbox,
+                                double x, double y, double z, double rotation)
+  {
+#if defined (HAVE_OPENGL)
+
+    // Transform data coordinates to screen pixel ortho coordinates
+    ColumnVector pixpos = get_transform ().transform (x, y, z, false);
+    Matrix xdata(1, 2, bbox(0) / m_devpixratio);
+    xdata(1) += (bbox(2) - 1) / m_devpixratio;
+    Matrix ydata(1, 2, -bbox(1) / m_devpixratio);
+    ydata(1) -= (bbox(3) - 1) / m_devpixratio;
+
+    bool blend = m_glfcns.glIsEnabled (GL_BLEND);
+    m_glfcns.glEnable (GL_BLEND);
+    m_glfcns.glEnable (GL_ALPHA_TEST);
+
+    set_ortho_coordinates ();
+
+    // Translate coordinates so that the text anchor is (0,0)
+    m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2));
+
+    m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0);
+
+    // Permute pixels returned by freetype
+    Array<octave_idx_type> perm (dim_vector (3, 1));
+    perm(0) = 2;
+    perm(1) = 1;
+    perm(2) = 0;
+    draw_texture_image (pixels.permute (perm),
+                        xdata, ydata, true);
+
+    restore_previous_coordinates ();
+
+    m_glfcns.glDisable (GL_ALPHA_TEST);
+
+    if (! blend)
+      m_glfcns.glDisable (GL_BLEND);
+
+#else
+
+    octave_unused_parameter (pixels);
+    octave_unused_parameter (bbox);
+    octave_unused_parameter (x);
+    octave_unused_parameter (y);
+    octave_unused_parameter (z);
+    octave_unused_parameter (rotation);
+
+    // This shouldn't happen because construction of opengl_renderer
+    // objects is supposed to be impossible if OpenGL is not available.
+
+    panic_impossible ();
+
+#endif
+  }
 }