GNU Octave  4.0.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
__osmesa_print__.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2015 Andreas Weber <andy.weber.aw@gmail.com>
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 This code is based on Brian Pauls' src/osdemos/osdemo.c
22 from git://anongit.freedesktop.org/mesa/demos
23 
24 */
25 
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29 
30 #include "oct-locbuf.h"
31 #include "unwind-prot.h"
32 
33 #include "defun-dld.h"
34 #include "gl-render.h"
35 #include "gl2ps-renderer.h"
36 #include "graphics.h"
37 #include "gripes.h"
38 
39 #if defined (HAVE_OSMESA_H)
40 #include "osmesa.h"
41 #elif defined (HAVE_GL_OSMESA_H)
42 #include "GL/osmesa.h"
43 #endif
44 
45 #if defined (HAVE_OSMESA) && defined (HAVE_OPENGL)
46 static void
47 close_fcn (FILE *f)
48 {
49  gnulib::fclose (f);
50 }
51 
52 static void
53 reset_visibility (figure::properties *fp)
54 {
55  fp->set_visible ("on");
56 }
57 #endif
58 
59 DEFUN_DLD(__osmesa_print__, args, ,
60  "-*- texinfo -*-\n\
61 @deftypefn {Loadable Function} {} __osmesa_print__ (@var{h}, @var{file}, @var{term})\n\
62 @deftypefnx {Loadable Function} {@var{img} =} __osmesa_print__ (@var{h})\n\
63 Print figure @var{h} using OSMesa and gl2ps for vector formats.\n\
64 \n\
65 This is a private internal function.\n\
66 \n\
67 The first method calls gl2ps with the appropriate @var{term} and writes\n\
68 the output of gl2ps to @var{file}. If the first character of @var{file}\n\
69 is @qcode{|}, then a process is started and the output of gl2ps is piped\n\
70 to it.\n\
71 \n\
72 Valid options for @var{term}, which can be concatenated in one string, are:\n\
73 \n\
74 @table @asis\n\
75 @item @qcode{eps}, @qcode{pdf}, @qcode{ps}, @qcode{svg}, @qcode{pgf}, @qcode{tex}\n\
76 Select output format.\n\
77 \n\
78 @item @qcode{is2D}\n\
79 Use GL2PS_SIMPLE_SORT instead of GL2PS_BSP_SORT as Z-depth sorting algorithm.\n\
80 \n\
81 @item @qcode{notext}\n\
82 Don't render text.\n\
83 @end table\n\
84 \n\
85 The second method doesn't use gl2ps and returns a RGB image in @var{img}\n\
86 instead.\n\
87 \n\
88 @end deftypefn")
89 {
90  octave_value_list retval;
91 
92 #if ! defined (HAVE_OSMESA)
93  gripe_disabled_feature ("__osmesa_print__", "offscreen rendering");
94 #else
95 
96  int nargin = args.length ();
97 
98  if (! (nargin == 1 || nargin == 3))
99  {
100  print_usage ();
101  return retval;
102  }
103 
104  if (nargin == 3)
105  {
106  if (! (args(1).is_string () && args(2).is_string ()))
107  {
108  error ("__osmesa_print__: FILE and TERM must be strings");
109  return retval;
110  }
111 
112 #ifndef HAVE_GL2PS_H
113  error ("__osmesa_print__: Octave has been compiled without gl2ps");
114  return retval;
115 #endif
116  }
117 
118  int h = args(0).double_value ();
120  if (! (fobj && fobj.isa ("figure")))
121  {
122  error ("__osmesa_print__: H must be a valid figure handle");
123  return retval;
124  }
125 
126  figure::properties& fp =
127  dynamic_cast<figure::properties&> (fobj.get_properties ());
128 
129  bool internal = true;
130  Matrix bb = fp.get_boundingbox (internal);
131 
132  GLsizei Width = static_cast<GLsizei> (bb(2));
133  GLsizei Height = static_cast<GLsizei> (bb(3));
134 
135  // Create an RGBA-mode context, specify Z=16, stencil=0, accum=0 sizes
136  OSMesaContext ctx = OSMesaCreateContextExt (OSMESA_RGBA, 16, 0, 0, NULL);
137  if (! ctx)
138  {
139  error ("__osmesa_print__: OSMesaCreateContext failed!\n");
140  return retval;
141  }
142 
143  // Allocate the image buffer
144  OCTAVE_LOCAL_BUFFER (GLubyte, buffer, 4 * Width * Height);
145 
146  // Bind the buffer to the context and make it current
147  if (! OSMesaMakeCurrent (ctx, buffer, GL_UNSIGNED_BYTE, Width, Height))
148  {
149  error ("__osmesa_print__: OSMesaMakeCurrent failed!\n");
150  return retval;
151  }
152 
153  // Test for a bug in OSMesa with version < 9.0
154  //
155  // Unfortunately the macros OSMESA_MAJOR_VERSION and OSMESA_MINOR_VERSION
156  // weren't updated between many releases and can't be used for detection.
157  // (Version 8.0 until 9.1.4 all return MAJOR 6, MINOR 5)
158  GLint z, s;
159  glGetIntegerv (GL_DEPTH_BITS, &z);
160  glGetIntegerv (GL_STENCIL_BITS, &s);
161  if (z != 16 || s != 0)
162  error ("__osmesa_print__: Depth and stencil doesn't match,"
163  " are you sure you are using OSMesa >= 9.0?");
164 
165  unwind_protect outer_frame;
166 
167  bool v = fp.is_visible ();
168 
169  if (v)
170  {
171  outer_frame.add_fcn (reset_visibility, &fp);
172 
173  fp.set_visible ("off");
174  }
175 
176  if (nargin == 3)
177  {
178  // use gl2ps
179 #ifndef HAVE_GL2PS_H
180  gripe_disabled_feature ("__osmesa_print__", "gl2ps");
181 #else
182  std::string file = args(1).string_value ();
183  std::string term = args(2).string_value ();
184 
185  if (! error_state)
186  {
187  size_t pos_p = file.find_first_of ("|");
188  size_t pos_c = file.find_first_not_of ("| ");
189 
190  if (pos_p == std::string::npos && pos_c == std::string::npos)
191  error ("__osmesa_print__: empty output ''");
192  else if (pos_c == std::string::npos)
193  error ("__osmesa_print__: empty pipe '|'");
194  else if (pos_p != std::string::npos && pos_p < pos_c)
195  {
196  // create process and pipe gl2ps output to it
197  std::string cmd = file.substr (pos_c);
198  gl2ps_print (fobj, cmd, term);
199  }
200  else
201  {
202  // write gl2ps output directly to file
203  FILE *filep = gnulib::fopen (file.substr (pos_c).c_str (), "w");
204 
205  if (filep)
206  {
207  unwind_protect frame;
208 
209  frame.add_fcn (close_fcn, filep);
210 
211  glps_renderer rend (filep, term);
212  rend.draw (fobj, "");
213 
214  // Make sure buffered commands are finished!!!
215  glFinish ();
216  }
217  else
218  error ("__osmesa_print__: Couldn't create file \"%s\"", file.c_str ());
219  }
220  }
221 #endif
222  }
223  else
224  {
225  // return RGB image
226  opengl_renderer rend;
227  rend.draw (fobj);
228 
229  // Make sure buffered commands are finished!!!
230  glFinish ();
231 
232  dim_vector dv (4, Width, Height);
233 
234  // FIXME: We expect that GLubyte is 1 Byte long.
235  // Adapt code if this isn't always true
236  assert (sizeof (GLubyte) == 1);
237  uint8NDArray img (dv);
238  unsigned char *p = reinterpret_cast<unsigned char*>(img.fortran_vec ());
239  memcpy (p, buffer, (4 * Width * Height));
240 
241  Array<octave_idx_type> perm (dim_vector (3, 1));
242  perm(0) = 2;
243  perm(1) = 1;
244  perm(2) = 0;
245 
246  Array<idx_vector> idx (dim_vector (3, 1));
247 
248  // Flip Y
249  idx(0) = idx_vector::make_range (Height - 1, -1, Height);
250  idx(1) = idx_vector::colon;
251 
252  // Remove alpha channel
253  idx(2) = idx_vector (0, 3);
254  retval = octave_value (img.permute (perm). index(idx));
255  }
256 
257  OSMesaDestroyContext (ctx);
258 
259 #endif
260  return retval;
261 }
262 
263 /*
264 ## FIXME: osmesa does not work correctly on Windows platforms.
265 ## This is not critical, since this facility will mostly be used in
266 ## the future for generating the images in Octave's own documentation.
267 ## For the moment, disable these tests on PC's and Macs.
268 %!testif HAVE_OSMESA, HAVE_GL2PS_H
269 %! if (isunix ())
270 %! h = figure ("visible", "off");
271 %! fn = tempname ();
272 %! sombrero ();
273 %! __osmesa_print__ (h, fn, "svg");
274 %! assert (stat (fn).size, 2692270, -0.1);
275 %! unlink (fn);
276 %! img = __osmesa_print__ (h);
277 %! assert (size (img), [get(h, "position")([4, 3]), 3])
278 %! ## Use pixel sum per RGB channel as fingerprint
279 %! img_fp = squeeze (sum (sum (img), 2));
280 %! assert (img_fp, [52942515; 54167797; 56158178], -0.05);
281 %! endif
282 
283 %!testif HAVE_OSMESA, HAVE_GL2PS_H
284 %! if (isunix ())
285 %! h = figure ("visible", "off");
286 %! fn = tempname ();
287 %! plot (sin (0:0.1:2*pi));
288 %! __osmesa_print__ (h, fn, "svgis2d");
289 %! assert (stat (fn).size, 7438, -0.1);
290 %! unlink (fn);
291 %! img = __osmesa_print__ (h);
292 %! assert (size (img), [get(h, "position")([4, 3]), 3])
293 %! ## Use pixel sum per RGB channel as fingerprint
294 %! img_fp = squeeze (sum (sum (img), 2));
295 %! assert (img_fp, [59281711; 59281711; 59482179], -0.05);
296 %! endif
297 */
void set_visible(const octave_value &val)
Definition: graphics.cc:3847
bool is_visible(void) const
Definition: graphics.h:2758
MArray< T > permute(const Array< octave_idx_type > &vec, bool inv=false) const
Definition: MArray.h:74
bool isa(const std::string &go_name) const
Definition: graphics.h:3375
static const idx_vector colon
Definition: idx-vector.h:492
OCTINTERP_API void print_usage(void)
Definition: defun.cc:51
void error(const char *fmt,...)
Definition: error.cc:476
void gl2ps_print(const graphics_object &fig, const std::string &cmd, const std::string &term)
static void close_fcn(FILE *f)
F77_RET_T const double const double * f
void add_fcn(void(*fcn)(void))
void gripe_disabled_feature(const std::string &func, const std::string &feature, const std::string &pkg)
Definition: gripes.cc:242
int error_state
Definition: error.cc:101
base_properties & get_properties(void)
Definition: graphics.h:3377
Definition: dMatrix.h:35
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:197
static graphics_object get_object(double val)
Definition: graphics.h:13212
#define DEFUN_DLD(name, args_name, nargout_name, doc)
Definition: defun-dld.h:59
const T * fortran_vec(void) const
Definition: Array.h:481
Matrix get_boundingbox(bool internal=false, const Matrix &parent_pix_size=Matrix()) const
Definition: graphics.cc:3861
static idx_vector make_range(octave_idx_type start, octave_idx_type step, octave_idx_type len)
Definition: idx-vector.h:476
return octave_value(v1.char_array_value().concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string())? '\'': '"'))