Mercurial > octave-nkf
comparison libinterp/dldfcn/__osmesa_print__.cc @ 19737:a81177f4bfe6
Add __osmesa_print__.cc for offscreen rendering with OpenGL
* configure.ac: Add checks for libOSMesa
* libinterp/dldfcn/module-files: Add __osmesa_print__.cc
* __osmesa_print__.cc: New private function to print figures using OSMesa
and gl2ps for vector formats. Not yet used from "print".
author | Andreas Weber <andy.weber.aw@gmail.com> |
---|---|
date | Fri, 13 Feb 2015 00:31:10 +0100 |
parents | |
children | 59ad278cfb98 |
comparison
equal
deleted
inserted
replaced
19736:c048358da712 | 19737:a81177f4bfe6 |
---|---|
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 "defun-dld.h" | |
31 #include "gl-render.h" | |
32 #include "gl2ps-renderer.h" | |
33 #include "graphics.h" | |
34 | |
35 #include "gripes.h" | |
36 | |
37 #ifdef HAVE_OSMESA | |
38 #include "GL/osmesa.h" | |
39 #endif | |
40 | |
41 DEFUN_DLD(__osmesa_print__, args, , | |
42 "-*- texinfo -*-\n\ | |
43 @deftypefn {Loadable Function} __osmesa_print__ (@var{h}, @var{filename}, @var{term})\n\ | |
44 @deftypefnx {Loadable Function} {@var{img} =} __osmesa_print__ (@var{h})\n\ | |
45 Print figure @var{h} using OSMesa and gl2ps for vector formats.\n\ | |
46 \n\ | |
47 This is a private internal function.\n\ | |
48 The first method calls gl2ps with the appropriate @var{term} and writes\n\ | |
49 the output of gl2ps to @var{filename}.\n\ | |
50 \n\ | |
51 Valid options for @var{term}, which can be concatenated in one string, are:\n\ | |
52 @table @asis\n\ | |
53 @item @qcode{eps}, @qcode{pdf}, @qcode{ps}, @qcode{svg}, @qcode{pgf}, @qcode{tex}\n\ | |
54 Select output format.\n\ | |
55 @item @qcode{is2D}\n\ | |
56 Use GL2PS_SIMPLE_SORT instead of GL2PS_BSP_SORT as Z-depth sorting algorithm.\n\ | |
57 @item @qcode{notext}\n\ | |
58 Don't render text.\n\ | |
59 @end table\n\ | |
60 \n\ | |
61 The second method doesn't use gl2ps and returns a RGB image in @var{img} instead.\n\ | |
62 \n\ | |
63 @end deftypefn") | |
64 { | |
65 octave_value_list retval; | |
66 | |
67 #ifndef HAVE_OSMESA | |
68 gripe_disabled_feature ("__osmesa_print__", "Offscreen rendering"); | |
69 #else | |
70 | |
71 int nargin = args.length (); | |
72 | |
73 if (! (nargin == 1 || nargin == 3)) | |
74 { | |
75 print_usage (); | |
76 return retval; | |
77 } | |
78 | |
79 if ((nargin == 3)) | |
80 { | |
81 if(! (args(1).is_string () && args(2).is_string ())) | |
82 { | |
83 error ("__osmesa_print__: FILENAME and TERM has to be strings"); | |
84 return retval; | |
85 } | |
86 | |
87 #ifndef HAVE_GL2PS_H | |
88 error ("__osmesa_print__: Octave has been compiled without gl2ps"); | |
89 return retval; | |
90 #endif | |
91 } | |
92 | |
93 int h = args(0).double_value (); | |
94 graphics_object fobj = gh_manager::get_object (h); | |
95 if (! (fobj && fobj.isa ("figure"))) | |
96 { | |
97 error ("__osmesa_print__: H has to be a valid figure handle"); | |
98 return retval; | |
99 } | |
100 | |
101 figure::properties& fp = | |
102 dynamic_cast<figure::properties&> (fobj.get_properties ()); | |
103 | |
104 bool internal = true; | |
105 Matrix bb = fp.get_boundingbox (internal); | |
106 | |
107 int Width = bb(2); | |
108 int Height = bb(3); | |
109 | |
110 OSMesaContext ctx; | |
111 void *buffer; | |
112 | |
113 // Create an RGBA-mode context, specify Z=16, stencil=0, accum=0 sizes | |
114 ctx = OSMesaCreateContextExt (OSMESA_RGBA, 16, 0, 0, NULL); | |
115 if (! ctx) | |
116 { | |
117 error ("__osmesa_print__: OSMesaCreateContext failed!\n"); | |
118 return retval; | |
119 } | |
120 | |
121 // Allocate the image buffer | |
122 buffer = malloc (Width * Height * 4 * sizeof (GLubyte)); | |
123 if (! buffer) | |
124 { | |
125 error ("__osmesa_print__: Alloc image buffer failed!\n"); | |
126 return retval; | |
127 } | |
128 | |
129 // Bind the buffer to the context and make it current | |
130 if (! OSMesaMakeCurrent (ctx, buffer, GL_UNSIGNED_BYTE, Width, Height)) | |
131 { | |
132 error ("__osmesa_print__: OSMesaMakeCurrent failed!\n"); | |
133 return retval; | |
134 } | |
135 | |
136 // Test for a bug in OSMesa with version < 9.0 | |
137 // Unfortunately the macros OSMESA_MAJOR_VERSION and OSMESA_MINOR_VERSION weren't | |
138 // updated between many releases and can't be used for detection therefore. | |
139 // (Version 8.0 until 9.1.4 all return MAJOR 6, MINOR 5) | |
140 int z, s; | |
141 glGetIntegerv (GL_DEPTH_BITS, &z); | |
142 glGetIntegerv (GL_STENCIL_BITS, &s); | |
143 if ((z != 16) || (s != 0)) | |
144 error ("__osmesa_print__: Depth and stencil doesn't match," | |
145 " are you sure you are using OSMesa >= 9.0?"); | |
146 | |
147 // check if the figure is visible | |
148 bool v = fp.is_visible (); | |
149 if (v) | |
150 fp.set_visible ("off"); | |
151 | |
152 if (nargin == 3) | |
153 { | |
154 // write gl2ps output to file | |
155 std::string filename = args(1).string_value (); | |
156 std::string term = args(2).string_value (); | |
157 | |
158 FILE *filep; | |
159 filep = fopen (filename.c_str (), "w"); | |
160 if (filep) | |
161 { | |
162 glps_renderer rend (filep, term); | |
163 rend.draw (fobj, ""); | |
164 | |
165 // Make sure buffered commands are finished!!! | |
166 glFinish (); | |
167 | |
168 fclose (filep); | |
169 } | |
170 else | |
171 error ("__osmesa_print__: Couldn't create file \"%s\"", filename.c_str ()); | |
172 | |
173 } | |
174 else | |
175 { | |
176 // return RGB image | |
177 opengl_renderer rend; | |
178 rend.draw (fobj); | |
179 | |
180 // Make sure buffered commands are finished!!! | |
181 glFinish (); | |
182 | |
183 dim_vector dv (4, Width, Height); | |
184 | |
185 // FIXME: We expect that GLubyte is 1 Byte long. | |
186 // Adapt code if this isn't always true | |
187 assert (sizeof (GLubyte) == 1); | |
188 uint8NDArray img (dv); | |
189 unsigned char *p = reinterpret_cast<unsigned char*>(img.fortran_vec ()); | |
190 memcpy (p, buffer, (4 * Width * Height)); | |
191 | |
192 Array<octave_idx_type> perm (dim_vector (3, 1)); | |
193 perm(0) = 2; | |
194 perm(1) = 1; | |
195 perm(2) = 0; | |
196 | |
197 Array<idx_vector> idx (dim_vector (3, 1)); | |
198 | |
199 // Flip Y | |
200 idx(0) = idx_vector::make_range (Height - 1, -1, Height); | |
201 idx(1) = idx_vector::colon; | |
202 | |
203 // Remove alpha channel | |
204 idx(2) = idx_vector (0, 3); | |
205 retval = octave_value (img.permute (perm). index(idx)); | |
206 } | |
207 | |
208 // restore visibility if necessary | |
209 if (v) | |
210 fp.set_visible ("on"); | |
211 | |
212 free (buffer); | |
213 OSMesaDestroyContext (ctx); | |
214 | |
215 #endif | |
216 return retval; | |
217 } | |
218 | |
219 /* | |
220 %!testif HAVE_OSMESA | |
221 %! h = figure ("visible", "off"); | |
222 %! fn = tempname (); | |
223 %! sombrero (); | |
224 %! __osmesa_print__ (h, fn, "svg"); | |
225 %! assert (stat (fn).size, 2692270, -0.1); | |
226 %! unlink (fn); | |
227 %! img = __osmesa_print__ (h); | |
228 %! assert (size (img), [get(h, "position")([4, 3]), 3]) | |
229 %! ## Use pixel sum per RGB channel as fingerprint | |
230 %! img_fp = squeeze (sum (sum (img), 2)); | |
231 %! assert (img_fp, [52942515; 54167797; 56158178], -0.05); | |
232 | |
233 %!testif HAVE_OSMESA | |
234 %! h = figure ("visible", "off"); | |
235 %! fn = tempname (); | |
236 %! plot (sin (0:0.1:2*pi)); | |
237 %! __osmesa_print__ (h, fn, "svgis2d"); | |
238 %! assert (stat (fn).size, 7438, -0.05); | |
239 %! unlink (fn); | |
240 %! img = __osmesa_print__ (h); | |
241 %! assert (size (img), [get(h, "position")([4, 3]), 3]) | |
242 %! ## Use pixel sum per RGB channel as fingerprint | |
243 %! img_fp = squeeze (sum (sum (img), 2)); | |
244 %! assert (img_fp, [59281711; 59281711; 59482179], -0.05); | |
245 */ |