Mercurial > octave
diff libinterp/corefcn/gl2ps-print.cc @ 26728:f034b29320ad
Use OpenGl textures to render image objects (bug #55632).
* gl-render.[h,cc] (opengl_renderer): Change the following data members to
protected rather than private and allow easy access from gl2ps_renderer:
xmin, xmax, ymin, ymax, zmin, zmax, m_devpixratio, xform.
(opengl_renderer::draw_pixels): Remove virtual methods.
(opengl_renderer::draw_image): Remove unnecessary manual clipping code. Make
use of an opengl_texture to draw pixels rather than glDrawPixels.
(opengl_texture::create): Add support for single and uint16 type pixel data.
* gl2ps-print.cc (gl2ps_renderer::draw_image): New overload. Dump here all the
manual clipping code, which is still necessary since gl2ps does not support
textures only emulates glDrawPixels.
* print.m: Set figure "__printing__" property first and reset last. Set figure
"__modified__" to "off" after resetting all properties.
author | Pantxo Diribarne <pantxo.diribarne@gmail.com> |
---|---|
date | Fri, 08 Feb 2019 18:07:33 +0100 |
parents | 00f796120a6d |
children | 84ff9953faa1 |
line wrap: on
line diff
--- a/libinterp/corefcn/gl2ps-print.cc Wed Feb 13 15:38:23 2019 +0000 +++ b/libinterp/corefcn/gl2ps-print.cc Fri Feb 08 18:07:33 2019 +0100 @@ -175,6 +175,7 @@ void draw_text (const text::properties& props); + void draw_image (const image::properties& props); void draw_pixels (int w, int h, const float *data); void draw_pixels (int w, int h, const uint8_t *data); void draw_pixels (int w, int h, const uint16_t *data); @@ -1027,6 +1028,266 @@ } void + gl2ps_renderer::draw_image (const image::properties& props) + { + octave_value cdata = props.get_color_data (); + dim_vector dv (cdata.dims ()); + int h = dv(0); + int w = dv(1); + + Matrix x = props.get_xdata ().matrix_value (); + Matrix y = props.get_ydata ().matrix_value (); + + // Someone wants us to draw an empty image? No way. + if (x.isempty () || y.isempty ()) + return; + + // Sort x/ydata and mark flipped dimensions + bool xflip = false; + if (x(0) > x(1)) + { + std::swap (x(0), x(1)); + xflip = true; + } + else if (w > 1 && x(1) == x(0)) + x(1) = x(1) + (w-1); + + bool yflip = false; + if (y(0) > y(1)) + { + std::swap (y(0), y(1)); + yflip = true; + } + else if (h > 1 && y(1) == y(0)) + y(1) = y(1) + (h-1); + + + const ColumnVector p0 = xform.transform (x(0), y(0), 0); + const ColumnVector p1 = xform.transform (x(1), y(1), 0); + + if (math::isnan (p0(0)) || math::isnan (p0(1)) + || math::isnan (p1(0)) || math::isnan (p1(1))) + { + warning ("opengl_renderer: image X,Y data too large to draw"); + return; + } + + // image pixel size in screen pixel units + float pix_dx, pix_dy; + // image pixel size in normalized units + float nor_dx, nor_dy; + + if (w > 1) + { + pix_dx = (p1(0) - p0(0)) / (w-1); + nor_dx = (x(1) - x(0)) / (w-1); + } + else + { + const ColumnVector p1w = xform.transform (x(1) + 1, y(1), 0); + pix_dx = p1w(0) - p0(0); + nor_dx = 1; + } + + if (h > 1) + { + pix_dy = (p1(1) - p0(1)) / (h-1); + nor_dy = (y(1) - y(0)) / (h-1); + } + else + { + const ColumnVector p1h = xform.transform (x(1), y(1) + 1, 0); + pix_dy = p1h(1) - p0(1); + nor_dy = 1; + } + + // OpenGL won't draw any of the image if its origin is outside the + // viewport/clipping plane so we must do the clipping ourselves. + + int j0, j1, jj, i0, i1, ii; + j0 = 0, j1 = w; + i0 = 0, i1 = h; + + float im_xmin = x(0) - nor_dx/2; + float im_xmax = x(1) + nor_dx/2; + float im_ymin = y(0) - nor_dy/2; + float im_ymax = y(1) + nor_dy/2; + + // Clip to axes or viewport + bool do_clip = props.is_clipping (); + Matrix vp = get_viewport_scaled (); + + ColumnVector vp_lim_min = + xform.untransform (std::numeric_limits <float>::epsilon (), + std::numeric_limits <float>::epsilon ()); + ColumnVector vp_lim_max = xform.untransform (vp(2), vp(3)); + + if (vp_lim_min(0) > vp_lim_max(0)) + std::swap (vp_lim_min(0), vp_lim_max(0)); + + if (vp_lim_min(1) > vp_lim_max(1)) + std::swap (vp_lim_min(1), vp_lim_max(1)); + + float clip_xmin = + (do_clip ? (vp_lim_min(0) > xmin ? vp_lim_min(0) : xmin) : vp_lim_min(0)); + float clip_ymin = + (do_clip ? (vp_lim_min(1) > ymin ? vp_lim_min(1) : ymin) : vp_lim_min(1)); + + float clip_xmax = + (do_clip ? (vp_lim_max(0) < xmax ? vp_lim_max(0) : xmax) : vp_lim_max(0)); + float clip_ymax = + (do_clip ? (vp_lim_max(1) < ymax ? vp_lim_max(1) : ymax) : vp_lim_max(1)); + + if (im_xmin < clip_xmin) + j0 += (clip_xmin - im_xmin)/nor_dx + 1; + if (im_xmax > clip_xmax) + j1 -= (im_xmax - clip_xmax)/nor_dx; + + if (im_ymin < clip_ymin) + i0 += (clip_ymin - im_ymin)/nor_dy + 1; + if (im_ymax > clip_ymax) + i1 -= (im_ymax - clip_ymax)/nor_dy; + + if (i0 >= i1 || j0 >= j1) + return; + + float zoom_x; + m_glfcns.glGetFloatv (GL_ZOOM_X, &zoom_x); + float zoom_y; + m_glfcns.glGetFloatv (GL_ZOOM_Y, &zoom_y); + + m_glfcns.glPixelZoom (m_devpixratio * pix_dx, + - m_devpixratio * pix_dy); + m_glfcns.glRasterPos3d (im_xmin + nor_dx*j0, im_ymin + nor_dy*i0, 0); + + // Expect RGB data + if (dv.ndims () == 3 && dv(2) == 3) + { + if (cdata.is_double_type ()) + { + const NDArray xcdata = cdata.array_value (); + + OCTAVE_LOCAL_BUFFER (GLfloat, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + if (! yflip) + ii = i; + else + ii = h - i - 1; + + if (! xflip) + jj = j; + else + jj = w - j - 1; + + a[idx] = xcdata(ii,jj,0); + a[idx+1] = xcdata(ii,jj,1); + a[idx+2] = xcdata(ii,jj,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else if (cdata.is_single_type ()) + { + const FloatNDArray xcdata = cdata.float_array_value (); + + OCTAVE_LOCAL_BUFFER (GLfloat, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + if (! yflip) + ii = i; + else + ii = h - i - 1; + + if (! xflip) + jj = j; + else + jj = w - j - 1; + + a[idx] = xcdata(ii,jj,0); + a[idx+1] = xcdata(ii,jj,1); + a[idx+2] = xcdata(ii,jj,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else if (cdata.is_uint8_type ()) + { + const uint8NDArray xcdata = cdata.uint8_array_value (); + + OCTAVE_LOCAL_BUFFER (GLubyte, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + if (! yflip) + ii = i; + else + ii = h - i - 1; + + if (! xflip) + jj = j; + else + jj = w - j - 1; + + a[idx] = xcdata(ii,jj,0); + a[idx+1] = xcdata(ii,jj,1); + a[idx+2] = xcdata(ii,jj,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else if (cdata.is_uint16_type ()) + { + const uint16NDArray xcdata = cdata.uint16_array_value (); + + OCTAVE_LOCAL_BUFFER (GLushort, a, 3*(j1-j0)*(i1-i0)); + + for (int i = i0; i < i1; i++) + { + for (int j = j0, idx = (i-i0)*(j1-j0)*3; j < j1; j++, idx += 3) + { + if (! yflip) + ii = i; + else + ii = h - i - 1; + + if (! xflip) + jj = j; + else + jj = w - j - 1; + + a[idx] = xcdata(ii,jj,0); + a[idx+1] = xcdata(ii,jj,1); + a[idx+2] = xcdata(ii,jj,2); + } + } + + draw_pixels (j1-j0, i1-i0, a); + + } + else + warning ("opengl_renderer: invalid image data type (expected double, single, uint8, or uint16)"); + + m_glfcns.glPixelZoom (zoom_x, zoom_y); + + } + } + + void gl2ps_renderer::draw_pixels (int w, int h, const float *data) { // Clip data between 0 and 1 for float values