diff options
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | doc/openscad.1 | 5 | ||||
-rw-r--r-- | doc/testing.txt | 7 | ||||
-rw-r--r-- | openscad.pro | 32 | ||||
-rw-r--r-- | src/CsgInfo.h | 42 | ||||
-rw-r--r-- | src/MainWindow.h | 1 | ||||
-rw-r--r-- | src/OffscreenContext.h | 19 | ||||
-rw-r--r-- | src/OffscreenContextAll.hpp | 74 | ||||
-rw-r--r-- | src/OffscreenContextCGL.mm (renamed from tests/OffscreenContext.mm) | 18 | ||||
-rw-r--r-- | src/OffscreenContextGLX.cc | 293 | ||||
-rw-r--r-- | src/OffscreenContextWGL.cc (renamed from tests/OffscreenContextWGL.cc) | 47 | ||||
-rw-r--r-- | src/OffscreenView.cc (renamed from tests/OffscreenView.cc) | 5 | ||||
-rw-r--r-- | src/OffscreenView.h (renamed from tests/OffscreenView.h) | 10 | ||||
-rw-r--r-- | src/export.cc | 2 | ||||
-rw-r--r-- | src/export.h | 2 | ||||
-rw-r--r-- | src/export_png.cc | 56 | ||||
-rw-r--r-- | src/fbo.cc (renamed from tests/fbo.cc) | 2 | ||||
-rw-r--r-- | src/fbo.h (renamed from tests/fbo.h) | 0 | ||||
-rw-r--r-- | src/imageutils-lodepng.cc | 26 | ||||
-rw-r--r-- | src/imageutils-macosx.cc (renamed from tests/imageutils-macosx.cc) | 42 | ||||
-rw-r--r-- | src/imageutils.cc | 27 | ||||
-rw-r--r-- | src/imageutils.h (renamed from tests/imageutils.h) | 2 | ||||
-rw-r--r-- | src/linalg.cc | 12 | ||||
-rw-r--r-- | src/linalg.h | 3 | ||||
-rw-r--r-- | src/lodepng.cpp (renamed from tests/lodepng.cpp) | 4 | ||||
-rw-r--r-- | src/lodepng.h (renamed from tests/lodepng.h) | 0 | ||||
-rw-r--r-- | src/mainwin.cc | 9 | ||||
-rw-r--r-- | src/node.cc | 12 | ||||
-rw-r--r-- | src/node.h | 1 | ||||
-rw-r--r-- | src/openscad.cc | 28 | ||||
-rw-r--r-- | src/system-gl.cc (renamed from tests/system-gl.cc) | 0 | ||||
-rw-r--r-- | src/system-gl.h | 5 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 51 | ||||
-rw-r--r-- | tests/EnforceConfig.cmake | 2 | ||||
-rw-r--r-- | tests/OffscreenContext.h | 13 | ||||
-rw-r--r-- | tests/OffscreenContextGLX.cc | 328 | ||||
-rw-r--r-- | tests/bboxhelp.cc | 21 | ||||
-rw-r--r-- | tests/cgalcachetest.cc | 9 | ||||
-rw-r--r-- | tests/cgalpngtest.cc | 9 | ||||
-rw-r--r-- | tests/cgalstlsanitytest.cc | 9 | ||||
-rw-r--r-- | tests/cgaltest.cc | 9 | ||||
-rw-r--r-- | tests/csgtestcore.cc | 11 | ||||
-rw-r--r-- | tests/guicgalpngtest.cc | 28 | ||||
-rw-r--r-- | tests/imageutils-lodepng.cc | 24 | ||||
-rw-r--r-- | tests/imageutils.cc | 16 | ||||
-rw-r--r-- | tests/system-gl.h | 10 | ||||
-rwxr-xr-x | tests/test_cmdline_tool.py | 8 |
47 files changed, 793 insertions, 547 deletions
@@ -202,6 +202,12 @@ complete, build OpenSCAD and package it to an installer: ./scripts/release-common.sh mingw32 +If you wish you can only build the openscad.exe binary: + + cd mingw32 + i686-pc-mingw32-qmake .. CONFIG+=mingw-cross-env + make + ### Compilation First, run 'qmake' from Qt4 to generate a Makefile. On some systems you need to diff --git a/doc/openscad.1 b/doc/openscad.1 index a4c03dd..aa03b37 100644 --- a/doc/openscad.1 +++ b/doc/openscad.1 @@ -19,7 +19,7 @@ the OpenSCAD user manual at http://en.wikibooks.org/wiki/OpenSCAD_User_Manual. .TP \fB-o\fP \fIoutputfile\fP -Export the given file to \fIoutputfile\fP in STL, OFF, DXF or CSG format, +Export the given file to \fIoutputfile\fP in STL, OFF, DXF, CSG, or PNG format, depending on file extension of \fIoutputfile\fP (which has to be lower case). If this option is given, the GUI will not be started. .TP @@ -40,6 +40,9 @@ More than one \fB-D\fP options can be given. .TP .B \-v, \-\-version Show version of program. +.TP +.B \-\-render +If exporting an image, use a full CGAL render. (Default is an OpenCSG compile) .SH AUTHOR OpenSCAD was written by Clifford Wolf, Marius Kintel, and others. .PP diff --git a/doc/testing.txt b/doc/testing.txt index bbd7c18..0b14903 100644 --- a/doc/testing.txt +++ b/doc/testing.txt @@ -3,6 +3,9 @@ Running regression tests: Prerequisites: cmake, python, ImageMagick 6.5.9.3 or newer +First, get a working qmake GUI build. It is used by the tests. +Next, get MCAD installed by using 'git submodule update --init' + A) Building test environment Linux, Mac: @@ -12,9 +15,7 @@ $ make Windows + MSVC: -First, get a normal build working by following instructions at -http://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Building_on_Windows -Then, from the QT command prompt: +From the QT command prompt: > cd tests > cmake . -DCMAKE_BUILD_TYPE=Release diff --git a/openscad.pro b/openscad.pro index a7088ec..ead5d1f 100644 --- a/openscad.pro +++ b/openscad.pro @@ -68,7 +68,7 @@ macx { APP_RESOURCES.path = Contents/Resources APP_RESOURCES.files = OpenSCAD.sdef QMAKE_BUNDLE_DATA += APP_RESOURCES - LIBS += -framework Carbon + LIBS += -framework Cocoa } else { TARGET = openscad @@ -229,7 +229,16 @@ HEADERS += src/version_check.h \ src/linalg.h \ src/system-gl.h \ src/stl-utils.h \ - src/svg.h + src/svg.h \ + \ + src/lodepng.h \ + src/OffscreenView.h \ + src/OffscreenContext.h \ + src/OffscreenContextAll.hpp \ + src/fbo.h \ + src/imageutils.h \ + src/system-gl.h \ + src/CsgInfo.h SOURCES += src/version_check.cc \ src/ProgressWidget.cc \ @@ -279,6 +288,7 @@ SOURCES += src/version_check.cc \ \ src/builtin.cc \ src/export.cc \ + src/export_png.cc \ src/import.cc \ src/renderer.cc \ src/ThrownTogetherRenderer.cc \ @@ -287,10 +297,28 @@ SOURCES += src/version_check.cc \ src/dxftess-cgal.cc \ src/CSGTermEvaluator.cc \ src/svg.cc \ + src/OffscreenView.cc \ + src/fbo.cc \ + src/system-gl.cc \ + src/imageutils.cc \ + src/lodepng.cpp \ \ src/openscad.cc \ src/mainwin.cc +unix:!macx { + SOURCES += src/imageutils-lodepng.cc + SOURCES += src/OffscreenContextGLX.cc +} +macx { + SOURCES += src/imageutils-macosx.cc + OBJECTIVE_SOURCES += src/OffscreenContextCGL.mm +} +win32* { + SOURCES += src/imageutils-lodepng.cc + SOURCES += src/OffscreenContextWGL.cc +} + opencsg { HEADERS += src/OpenCSGRenderer.h SOURCES += src/OpenCSGRenderer.cc diff --git a/src/CsgInfo.h b/src/CsgInfo.h new file mode 100644 index 0000000..889eefe --- /dev/null +++ b/src/CsgInfo.h @@ -0,0 +1,42 @@ +#ifndef __CSGINFO_H__ +#define __CSGINFO_H__ + +#include "OffscreenView.h" + +class CsgInfo +{ +public: + CsgInfo() { glview = NULL; } + OffscreenView *glview; +}; + + +#ifdef ENABLE_OPENCSG + +#include <opencsg.h> +#include "OpenCSGRenderer.h" +#include "csgterm.h" +#include "csgtermnormalizer.h" + +class CsgInfo_OpenCSG : public CsgInfo +{ +public: + CsgInfo_OpenCSG() + { + root_chain = NULL; + highlights_chain = NULL; + background_chain = NULL; + glview = NULL; + } + shared_ptr<CSGTerm> root_norm_term; // Normalized CSG products + class CSGChain *root_chain; + std::vector<shared_ptr<CSGTerm> > highlight_terms; + CSGChain *highlights_chain; + std::vector<shared_ptr<CSGTerm> > background_terms; + CSGChain *background_chain; +}; + +#endif // ENABLE_OPENCSG + +#endif + diff --git a/src/MainWindow.h b/src/MainWindow.h index a4835c2..4848db6 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -74,7 +74,6 @@ private slots: private: void openFile(const QString &filename); void refreshDocument(); - AbstractNode *find_root_tag(AbstractNode *n); void updateTemporalVariables(); bool fileChangedOnDisk(); bool includesChanged(); diff --git a/src/OffscreenContext.h b/src/OffscreenContext.h new file mode 100644 index 0000000..e74e104 --- /dev/null +++ b/src/OffscreenContext.h @@ -0,0 +1,19 @@ +#ifndef OFFSCREENCONTEXT_H_ +#define OFFSCREENCONTEXT_H_ + +// Here we implement a 'portability' pattern but since we are mixing +// Objective-C with C++, it is a bit different. The main struct +// isn't defined in the header, but instead inside the source code files + +#include <iostream> +#include <fstream> +#include <string> +#include "fbo.h" + +struct OffscreenContext *create_offscreen_context(int w, int h); +bool teardown_offscreen_context(OffscreenContext *ctx); +bool save_framebuffer(OffscreenContext *ctx, const char * filename); +bool save_framebuffer(OffscreenContext *ctx, std::ostream &output); +std::string offscreen_context_getinfo(OffscreenContext *ctx); + +#endif diff --git a/src/OffscreenContextAll.hpp b/src/OffscreenContextAll.hpp new file mode 100644 index 0000000..b07cea0 --- /dev/null +++ b/src/OffscreenContextAll.hpp @@ -0,0 +1,74 @@ +// Functions shared by OffscreenContext[platform].cc +// #include this directly after definition of struct OffscreenContext. + +#include <vector> +#include <ostream> + +void bind_offscreen_context(OffscreenContext *ctx) +{ + if (ctx) fbo_bind(ctx->fbo); +} + +/* + Capture framebuffer from OpenGL and write it to the given filename as PNG. + */ +bool save_framebuffer(OffscreenContext *ctx, const char *filename) +{ + std::ofstream fstream(filename,std::ios::out|std::ios::binary); + if (!fstream.is_open()) { + std::cerr << "Can't open file " << filename << " for writing"; + return false; + } else { + save_framebuffer(ctx, fstream); + fstream.close(); + } + return true; +} + +/*! + Capture framebuffer from OpenGL and write it to the given ostream. + Called by save_framebuffer() from platform-specific code. + */ +bool save_framebuffer_common(OffscreenContext *ctx, std::ostream &output) +{ + if (!ctx) return false; + int samplesPerPixel = 4; // R, G, B and A + std::vector<GLubyte> pixels(ctx->width * ctx->height * samplesPerPixel); + glReadPixels(0, 0, ctx->width, ctx->height, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]); + + // Flip it vertically - images read from OpenGL buffers are upside-down + int rowBytes = samplesPerPixel * ctx->width; + + unsigned char *flippedBuffer = (unsigned char *)malloc(rowBytes * ctx->height); + if (!flippedBuffer) { + std::cerr << "Unable to allocate flipped buffer for corrected image."; + return 1; + } + flip_image(&pixels[0], flippedBuffer, samplesPerPixel, ctx->width, ctx->height); + + bool writeok = write_png(output, flippedBuffer, ctx->width, ctx->height); + + free(flippedBuffer); + + return writeok; +} + +// Called by create_offscreen_context() from platform-specific code. +OffscreenContext *create_offscreen_context_common(OffscreenContext *ctx) +{ + if (!ctx) return NULL; + GLenum err = glewInit(); // must come after Context creation and before FBO c$ + if (GLEW_OK != err) { + std::cerr << "Unable to init GLEW: " << glewGetErrorString(err) << "\n"; + return NULL; + } + //cerr << glew_dump(0); + + ctx->fbo = fbo_new(); + if (!fbo_init(ctx->fbo, ctx->width, ctx->height)) { + return NULL; + } + + return ctx; +} + diff --git a/tests/OffscreenContext.mm b/src/OffscreenContextCGL.mm index a0995fa..76c5418 100644 --- a/tests/OffscreenContext.mm +++ b/src/OffscreenContextCGL.mm @@ -19,7 +19,9 @@ struct OffscreenContext fbo_t *fbo; }; -std::string offscreen_context_getinfo(OffscreenContext *ctx) +#include "OffscreenContextAll.hpp" + +std::string offscreen_context_getinfo(OffscreenContext *) { std::stringstream out; @@ -81,7 +83,7 @@ OffscreenContext *create_offscreen_context(int w, int h) std::cerr << "Unable to init GLEW: " << glewGetErrorString(err) << std::endl; return NULL; } - glew_dump(); + glew_dump(false); ctx->fbo = fbo_new(); if (!fbo_init(ctx->fbo, w, h)) { @@ -107,11 +109,11 @@ bool teardown_offscreen_context(OffscreenContext *ctx) } /*! - Capture framebuffer from OpenGL and write it to the given filename as PNG. + Capture framebuffer from OpenGL and write it to the given ostream */ -bool save_framebuffer(OffscreenContext *ctx, const char *filename) +bool save_framebuffer(OffscreenContext *ctx, std::ostream &output) { - if (!ctx || !filename) return false; + if (!ctx) return false; // Read pixels from OpenGL int samplesPerPixel = 4; // R, G, B and A int rowBytes = samplesPerPixel * ctx->width; @@ -132,7 +134,7 @@ bool save_framebuffer(OffscreenContext *ctx, const char *filename) } flip_image(bufferData, flippedBuffer, samplesPerPixel, ctx->width, ctx->height); - bool writeok = write_png(filename, flippedBuffer, ctx->width, ctx->height); + bool writeok = write_png(output, flippedBuffer, ctx->width, ctx->height); free(flippedBuffer); free(bufferData); @@ -140,7 +142,3 @@ bool save_framebuffer(OffscreenContext *ctx, const char *filename) return writeok; } -void bind_offscreen_context(OffscreenContext *ctx) -{ - fbo_bind(ctx->fbo); -} diff --git a/src/OffscreenContextGLX.cc b/src/OffscreenContextGLX.cc new file mode 100644 index 0000000..e76cae8 --- /dev/null +++ b/src/OffscreenContextGLX.cc @@ -0,0 +1,293 @@ +/* + +Create an OpenGL context without creating an OpenGL Window. for Linux. + +See also + +glxgears.c by Brian Paul from mesa-demos (mesa3d.org) +http://cgit.freedesktop.org/mesa/demos/tree/src/xdemos?id=mesa-demos-8.0.1 +http://www.opengl.org/sdk/docs/man/xhtml/glXIntro.xml +http://www.mesa3d.org/brianp/sig97/offscrn.htm +http://glprogramming.com/blue/ch07.html +OffscreenContext.mm (Mac OSX version) + +*/ + +/* + * Some portions of the code below are: + * Copyright (C) 1999-2001 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "OffscreenContext.h" +#include "printutils.h" +#include "imageutils.h" +#include "system-gl.h" +#include "fbo.h" + +#include <GL/gl.h> +#include <GL/glx.h> + +#include <assert.h> +#include <sstream> + +#include <sys/utsname.h> // for uname + +using namespace std; + +struct OffscreenContext +{ + GLXContext openGLContext; + Display *xdisplay; + Window xwindow; + int width; + int height; + fbo_t *fbo; +}; + +#include "OffscreenContextAll.hpp" + +void offscreen_context_init(OffscreenContext &ctx, int width, int height) +{ + ctx.width = width; + ctx.height = height; + ctx.openGLContext = NULL; + ctx.xdisplay = NULL; + ctx.xwindow = (Window)NULL; + ctx.fbo = NULL; +} + +string get_os_info() +{ + struct utsname u; + stringstream out; + + if (uname(&u) < 0) + out << "OS info: unknown, uname() error\n"; + else { + out << "OS info: " + << u.sysname << " " + << u.release << " " + << u.version << "\n"; + out << "Machine: " << u.machine; + } + return out.str(); +} + +string offscreen_context_getinfo(OffscreenContext *ctx) +{ + assert(ctx); + + if (!ctx->xdisplay) + return string("No GL Context initialized. No information to report\n"); + + int major, minor; + glXQueryVersion(ctx->xdisplay, &major, &minor); + + stringstream out; + out << "GL context creator: GLX\n" + << "PNG generator: lodepng\n" + << "GLX version: " << major << "." << minor << "\n" + << get_os_info(); + + return out.str(); +} + +static XErrorHandler original_xlib_handler = (XErrorHandler) NULL; +static bool XCreateWindow_failed = false; +static int XCreateWindow_error(Display *dpy, XErrorEvent *event) +{ + cerr << "XCreateWindow failed: XID: " << event->resourceid + << " request: " << (int)event->request_code + << " minor: " << (int)event->minor_code << "\n"; + char description[1024]; + XGetErrorText( dpy, event->error_code, description, 1023 ); + cerr << " error message: " << description << "\n"; + XCreateWindow_failed = true; + return 0; +} + +bool create_glx_dummy_window(OffscreenContext &ctx) +{ +/* + create a dummy X window without showing it. (without 'mapping' it) + and save information to the ctx. + + This purposely does not use glxCreateWindow, to avoid crashes, + "failed to create drawable" errors, and Mesa "WARNING: Application calling + GLX 1.3 function when GLX 1.3 is not supported! This is an application bug!" + + This function will alter ctx.openGLContext and ctx.xwindow if successfull + */ + + int attributes[] = { + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT | GLX_PBUFFER_BIT, //support all 3, for OpenCSG + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, 24, // depth-stencil for OpenCSG + GLX_STENCIL_SIZE, 8, + GLX_DOUBLEBUFFER, True, + None + }; + + Display *dpy = ctx.xdisplay; + + int num_returned = 0; + GLXFBConfig *fbconfigs = glXChooseFBConfig( dpy, DefaultScreen(dpy), attributes, &num_returned ); + if ( fbconfigs == NULL ) { + cerr << "glXChooseFBConfig failed\n"; + return false; + } + + XVisualInfo *visinfo = glXGetVisualFromFBConfig( dpy, fbconfigs[0] ); + if ( visinfo == NULL ) { + cerr << "glXGetVisualFromFBConfig failed\n"; + XFree( fbconfigs ); + return false; + } + + // can't depend on xWin==NULL at failure. use a custom Xlib error handler instead. + original_xlib_handler = XSetErrorHandler( XCreateWindow_error ); + + Window root = DefaultRootWindow( dpy ); + XSetWindowAttributes xwin_attr; + int width = ctx.width; + int height = ctx.height; + xwin_attr.background_pixmap = None; + xwin_attr.background_pixel = 0; + xwin_attr.border_pixel = 0; + xwin_attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone); + xwin_attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; + unsigned long int mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + Window xWin = XCreateWindow( dpy, root, 0, 0, width, height, + 0, visinfo->depth, InputOutput, + visinfo->visual, mask, &xwin_attr ); + + // Window xWin = XCreateSimpleWindow( dpy, DefaultRootWindow(dpy), 0,0,42,42, 0,0,0 ); + + XSync( dpy, false ); + if ( XCreateWindow_failed ) { + XFree( visinfo ); + XFree( fbconfigs ); + return false; + } + XSetErrorHandler( original_xlib_handler ); + + // Most programs would call XMapWindow here. But we don't, to keep the window hidden + // XMapWindow( dpy, xWin ); + + GLXContext context = glXCreateNewContext( dpy, fbconfigs[0], GLX_RGBA_TYPE, NULL, True ); + if ( context == NULL ) { + cerr << "glXCreateNewContext failed\n"; + XDestroyWindow( dpy, xWin ); + XFree( visinfo ); + XFree( fbconfigs ); + return false; + } + + //GLXWindow glxWin = glXCreateWindow( dpy, fbconfigs[0], xWin, NULL ); + + if (!glXMakeContextCurrent( dpy, xWin, xWin, context )) { + //if (!glXMakeContextCurrent( dpy, glxWin, glxWin, context )) { + cerr << "glXMakeContextCurrent failed\n"; + glXDestroyContext( dpy, context ); + XDestroyWindow( dpy, xWin ); + XFree( visinfo ); + XFree( fbconfigs ); + return false; + } + + ctx.openGLContext = context; + ctx.xwindow = xWin; + + XFree( visinfo ); + XFree( fbconfigs ); + + return true; + } + + Bool create_glx_dummy_context(OffscreenContext &ctx); + + OffscreenContext *create_offscreen_context(int w, int h) + { + OffscreenContext *ctx = new OffscreenContext; + offscreen_context_init( *ctx, w, h ); + + // before an FBO can be setup, a GLX context must be created + // this call alters ctx->xDisplay and ctx->openGLContext + // and ctx->xwindow if successfull + if (!create_glx_dummy_context( *ctx )) { + return NULL; + } + + return create_offscreen_context_common( ctx ); + } + + bool teardown_offscreen_context(OffscreenContext *ctx) + { + if (ctx) { + fbo_unbind(ctx->fbo); + fbo_delete(ctx->fbo); + XDestroyWindow( ctx->xdisplay, ctx->xwindow ); + glXDestroyContext( ctx->xdisplay, ctx->openGLContext ); + XCloseDisplay( ctx->xdisplay ); + return true; + } + return false; + } + + bool save_framebuffer(OffscreenContext *ctx, std::ostream &output) + { + glXSwapBuffers(ctx->xdisplay, ctx->xwindow); + return save_framebuffer_common(ctx, output); + } + +#pragma GCC diagnostic ignored "-Waddress" + Bool create_glx_dummy_context(OffscreenContext &ctx) + { + // This will alter ctx.openGLContext and ctx.xdisplay and ctx.xwindow if successfull + int major; + int minor; + Bool result = False; + + ctx.xdisplay = XOpenDisplay( NULL ); + if ( ctx.xdisplay == NULL ) { + cerr << "Unable to open a connection to the X server\n"; + return False; + } + + // glxQueryVersion is not always reliable. Use it, but then + // also check to see if GLX 1.3 functions exist + + glXQueryVersion(ctx.xdisplay, &major, &minor); + if ( major==1 && minor<=2 && glXGetVisualFromFBConfig==NULL ) { + cerr << "Error: GLX version 1.3 functions missing. " + << "Your GLX version: " << major << "." << minor << endl; + } else { + result = create_glx_dummy_window(ctx); + } + + if (!result) XCloseDisplay( ctx.xdisplay ); + return result; + } + diff --git a/tests/OffscreenContextWGL.cc b/src/OffscreenContextWGL.cc index 75a7ed2..4fe4c65 100644 --- a/tests/OffscreenContextWGL.cc +++ b/src/OffscreenContextWGL.cc @@ -38,6 +38,8 @@ struct OffscreenContext fbo_t *fbo; }; +#include "OffscreenContextAll.hpp" + void offscreen_context_init(OffscreenContext &ctx, int width, int height) { ctx.window = (HWND)NULL; @@ -80,6 +82,7 @@ string get_os_info() string offscreen_context_getinfo(OffscreenContext *ctx) { + // should probably get some info from WGL context here? stringstream out; out << "GL context creator: WGL\n" << "PNG generator: lodepng\n" @@ -184,19 +187,7 @@ OffscreenContext *create_offscreen_context(int w, int h) return NULL; } - GLenum err = glewInit(); // must come after Context creation and before FBO calls. - if (GLEW_OK != err) { - cerr << "Unable to init GLEW: " << glewGetErrorString(err) << "\n"; - return NULL; - } - //cerr << glew_dump(0); - - ctx->fbo = fbo_new(); - if (!fbo_init(ctx->fbo, w, h)) { - return NULL; - } - - return ctx; + return create_offscreen_context_common( ctx ); } bool teardown_offscreen_context(OffscreenContext *ctx) @@ -214,34 +205,10 @@ bool teardown_offscreen_context(OffscreenContext *ctx) return false; } -/*! - Capture framebuffer from OpenGL and write it to the given filename as PNG. -*/ -bool save_framebuffer(OffscreenContext *ctx, const char *filename) +bool save_framebuffer(OffscreenContext *ctx, std::ostream &output) { + if (!ctx) return false; wglSwapLayerBuffers( ctx->dev_context, WGL_SWAP_MAIN_PLANE ); - if (!ctx || !filename) return false; - int samplesPerPixel = 4; // R, G, B and A - vector<GLubyte> pixels(ctx->width * ctx->height * samplesPerPixel); - glReadPixels(0, 0, ctx->width, ctx->height, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]); - - // Flip it vertically - images read from OpenGL buffers are upside-down - int rowBytes = samplesPerPixel * ctx->width; - unsigned char *flippedBuffer = (unsigned char *)malloc(rowBytes * ctx->height); - if (!flippedBuffer) { - std::cerr << "Unable to allocate flipped buffer for corrected image."; - return 1; - } - flip_image(&pixels[0], flippedBuffer, samplesPerPixel, ctx->width, ctx->height); - - bool writeok = write_png(filename, flippedBuffer, ctx->width, ctx->height); - - free(flippedBuffer); - - return writeok; + return save_framebuffer_common( ctx, output ); } -void bind_offscreen_context(OffscreenContext *ctx) -{ - if (ctx) fbo_bind(ctx->fbo); -} diff --git a/tests/OffscreenView.cc b/src/OffscreenView.cc index 61d5818..12ebf3a 100644 --- a/tests/OffscreenView.cc +++ b/src/OffscreenView.cc @@ -142,6 +142,11 @@ bool OffscreenView::save(const char *filename) return save_framebuffer(this->ctx, filename); } +bool OffscreenView::save(std::ostream &output) +{ + return save_framebuffer(this->ctx, output); +} + std::string OffscreenView::getInfo() { std::stringstream out; diff --git a/tests/OffscreenView.h b/src/OffscreenView.h index 8b98b29..f401bef 100644 --- a/tests/OffscreenView.h +++ b/src/OffscreenView.h @@ -8,6 +8,8 @@ #ifndef _MSC_VER #include <stdint.h> #endif +#include "system-gl.h" +#include <iostream> class OffscreenView { @@ -23,6 +25,7 @@ public: void setupOrtho(bool offset=false); void paintGL(); bool save(const char *filename); + bool save(std::ostream &output); std::string getInfo(); GLint shaderinfo[11]; @@ -32,14 +35,15 @@ public: private: Renderer *renderer; double w_h_ratio; - Eigen::Vector3d object_rot; - Eigen::Vector3d camera_eye; - Eigen::Vector3d camera_center; bool orthomode; bool showaxes; bool showfaces; bool showedges; + + Eigen::Vector3d object_rot; + Eigen::Vector3d camera_eye; + Eigen::Vector3d camera_center; }; #endif diff --git a/src/export.cc b/src/export.cc index 80670ea..6ec28ef 100644 --- a/src/export.cc +++ b/src/export.cc @@ -195,7 +195,7 @@ void export_dxf(CGAL_Nef_polyhedron *root_N, std::ostream &output) setlocale(LC_NUMERIC, ""); // Set default locale } -#endif +#endif // ENABLE_CGAL #ifdef DEBUG #include <boost/foreach.hpp> diff --git a/src/export.h b/src/export.h index 3897be0..b140db6 100644 --- a/src/export.h +++ b/src/export.h @@ -8,6 +8,8 @@ void export_stl(class CGAL_Nef_polyhedron *root_N, std::ostream &output); void export_off(CGAL_Nef_polyhedron *root_N, std::ostream &output); void export_dxf(CGAL_Nef_polyhedron *root_N, std::ostream &output); +void export_png_with_cgal(CGAL_Nef_polyhedron *root_N, std::ostream &output); +void export_png_with_opencsg(CGAL_Nef_polyhedron *root_N, std::ostream &output); #endif diff --git a/src/export_png.cc b/src/export_png.cc new file mode 100644 index 0000000..2d6a4a2 --- /dev/null +++ b/src/export_png.cc @@ -0,0 +1,56 @@ +#include "export.h" +#include "printutils.h" +#include "OffscreenView.h" +#include "CsgInfo.h" +#include <stdio.h> +#include "polyset.h" + +#ifdef ENABLE_CGAL +#include "CGALRenderer.h" +#include "CGAL_renderer.h" +#include "cgal.h" + +void export_png_with_cgal(CGAL_Nef_polyhedron *root_N, std::ostream &output) +{ + CsgInfo csgInfo; + try { + csgInfo.glview = new OffscreenView(512,512); + } catch (int error) { + fprintf(stderr,"Can't create OpenGL OffscreenView. Code: %i.\n", error); + return; + } + CGALRenderer cgalRenderer(*root_N); + + BoundingBox bbox; + if (cgalRenderer.polyhedron) { + CGAL::Bbox_3 cgalbbox = cgalRenderer.polyhedron->bbox(); + bbox = BoundingBox( + Vector3d(cgalbbox.xmin(), cgalbbox.ymin(), cgalbbox.zmin()), + Vector3d(cgalbbox.xmax(), cgalbbox.ymax(), cgalbbox.zmax()) ); + } + else if (cgalRenderer.polyset) { + bbox = cgalRenderer.polyset->getBoundingBox(); + } + + Vector3d center = getBoundingCenter(bbox); + double radius = getBoundingRadius(bbox); + + Vector3d cameradir(1, 1, -0.5); + Vector3d camerapos = center - radius*2*cameradir; + //std::cerr << center << "\n"; + //std::cerr << radius << "\n"; + + csgInfo.glview->setCamera(camerapos, center); + csgInfo.glview->setRenderer(&cgalRenderer); + csgInfo.glview->paintGL(); + csgInfo.glview->save(output); +} + +void export_png_with_opencsg(CGAL_Nef_polyhedron *root_N, std::ostream &output) +{ + CsgInfo csgInfo; + PRINT("not implemented: solid OpenSCAD_Model opencsg png export\n"); +} + + +#endif // ENABLE_CGAL diff --git a/tests/fbo.cc b/src/fbo.cc index a6677c1..186cb95 100644 --- a/tests/fbo.cc +++ b/src/fbo.cc @@ -59,7 +59,7 @@ bool check_fbo_status() else if (status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT) cerr << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT\n"; else - cerr << "Unknown Code: glCheckFramebufferStatusEXT returned %i\n",status; + cerr << "Unknown Code: glCheckFramebufferStatusEXT returned:" << status <<"\n"; return result; } diff --git a/src/imageutils-lodepng.cc b/src/imageutils-lodepng.cc new file mode 100644 index 0000000..a034702 --- /dev/null +++ b/src/imageutils-lodepng.cc @@ -0,0 +1,26 @@ +#include "imageutils.h" +#include "lodepng.h" +#include <stdio.h> +#include <stdlib.h> + +bool write_png(std::ostream &output, unsigned char *pixels, int width, int height) +{ + //encoder.settings.zlibsettings.windowSize = 2048; + //LodePNG_Text_add(&encoder.infoPng.text, "Comment", "Created with LodePNG"); + size_t dataout_size = -1; + bool result = false; + unsigned char *dataout = (unsigned char *)malloc(width*height*4); + if (!dataout) { + perror("Error allocating memory while writing png\n"); + return result; + } + LodePNG_encode(&dataout, &dataout_size, pixels, width, height, LCT_RGBA, 8); + try { + output.write( reinterpret_cast<const char*>(dataout), dataout_size ); + result = true; + } catch (const std::ios_base::failure &e) { + std::cerr << "Error writing to ostream:" << e.what() << "\n"; + } + free( dataout ); + return result; +} diff --git a/tests/imageutils-macosx.cc b/src/imageutils-macosx.cc index 404052f..4c7c446 100644 --- a/tests/imageutils-macosx.cc +++ b/src/imageutils-macosx.cc @@ -1,7 +1,34 @@ #include <ApplicationServices/ApplicationServices.h> #include <iostream> +#include "imageutils.h" +#include <assert.h> -bool write_png(const char *filename, unsigned char *pixels, int width, int height) +CGDataConsumerCallbacks dc_callbacks; + +size_t write_bytes_to_ostream (void *info,const void *buffer,size_t count) +{ + assert( info && buffer ); + std::ostream *output = (std::ostream *)info; + size_t startpos = output->tellp(); + size_t endpos = startpos; + try { + output->write( (const char *)buffer, count ); + endpos = output->tellp(); + } catch (const std::ios_base::failure& e) { + std::cerr << "Error writing to ostream:" << e.what() << "\n"; + } + return (endpos-startpos); +} + +CGDataConsumerRef CGDataConsumerCreateWithOstream(std::ostream &output) +{ + dc_callbacks.putBytes = write_bytes_to_ostream; + dc_callbacks.releaseConsumer = NULL; // ostream closed by caller of write_png + CGDataConsumerRef dc = CGDataConsumerCreate ( (void *)(&output), &dc_callbacks ); + return dc; +} + +bool write_png(std::ostream &output, unsigned char *pixels, int width, int height) { size_t rowBytes = width * 4; // CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); @@ -22,6 +49,8 @@ bool write_png(const char *filename, unsigned char *pixels, int width, int heigh return false; } + CGDataConsumerRef dataconsumer = CGDataConsumerCreateWithOstream(output); + /* CFStringRef fname = CFStringCreateWithCString(kCFAllocatorDefault, filename, kCFStringEncodingUTF8); CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, fname, kCFURLPOSIXPathStyle, false); @@ -30,7 +59,9 @@ bool write_png(const char *filename, unsigned char *pixels, int width, int heigh return false; } - CGDataConsumerRef dataconsumer = CGDataConsumerCreateWithURL(fileURL); + CGDataConsumerRef dataconsumer = CGDataConsumerCreateWithURL(fileURL); + */ + CFIndex fileImageIndex = 1; CFMutableDictionaryRef fileDict = NULL; CFStringRef fileUTType = kUTTypePNG; @@ -55,10 +86,13 @@ bool write_png(const char *filename, unsigned char *pixels, int width, int heigh CFRelease(imageDest); CFRelease(dataconsumer); - CFRelease(fileURL); - CFRelease(fname); + //CFRelease(fileURL); + //CFRelease(fname); CFRelease(imageProps); CGColorSpaceRelease(colorSpace); CGImageRelease(imageRef); return true; } + + + diff --git a/src/imageutils.cc b/src/imageutils.cc new file mode 100644 index 0000000..a1acaac --- /dev/null +++ b/src/imageutils.cc @@ -0,0 +1,27 @@ +#include "imageutils.h" +#include <assert.h> +#include <string.h> +#include <fstream> + +void flip_image(const unsigned char *src, unsigned char *dst, size_t pixelsize, size_t width, size_t height) +{ + assert( src && dst ); + size_t rowBytes = pixelsize * width; + for (size_t i = 0 ; i < height ; i++) { + memmove(dst + (height - i - 1) * rowBytes, src + i * rowBytes, rowBytes); + } +} + +bool write_png(const char *filename, unsigned char *pixels, int width, int height) { + assert( filename && pixels ); + std::ofstream fstream( filename, std::ios::binary ); + if (fstream.is_open()) { + write_png( fstream, pixels, width, height ); + fstream.close(); + return true; + } else { + std::cerr << "Can't open file " << filename << " for export."; + return false; + } +} + diff --git a/tests/imageutils.h b/src/imageutils.h index 72602a2..c9bb8de 100644 --- a/tests/imageutils.h +++ b/src/imageutils.h @@ -2,8 +2,10 @@ #define IMAGEUTILS_H_ #include <stdlib.h> +#include <iostream> bool write_png(const char *filename, unsigned char *pixels, int width, int height); +bool write_png(std::ostream &output, unsigned char *pixels, int width, int height); void flip_image(const unsigned char *src, unsigned char *dst, size_t pixelsize, size_t width, size_t height); #endif diff --git a/src/linalg.cc b/src/linalg.cc index 2f368f9..590e01b 100644 --- a/src/linalg.cc +++ b/src/linalg.cc @@ -46,3 +46,15 @@ bool matrix_contains_nan( const Transform3d &m ) return false; } +double getBoundingRadius(BoundingBox bbox) +{ + double radius = (bbox.max() - bbox.min()).norm() / 2; + return radius; // 0; +} + +Vector3d getBoundingCenter(BoundingBox bbox) +{ + Vector3d center = (bbox.min() + bbox.max()) / 2; + return center; // Vector3d(0,0,0); +} + diff --git a/src/linalg.h b/src/linalg.h index 48960b7..1f9ed30 100644 --- a/src/linalg.h +++ b/src/linalg.h @@ -22,6 +22,9 @@ bool matrix_contains_infinity( const Transform3d &m ); bool matrix_contains_nan( const Transform3d &m ); BoundingBox operator*(const Transform3d &m, const BoundingBox &box); +Vector3d getBoundingCenter(BoundingBox bbox); +double getBoundingRadius(BoundingBox bbox); + class Color4f : public Eigen::Vector4f { diff --git a/tests/lodepng.cpp b/src/lodepng.cpp index 831194f..9d21668 100644 --- a/tests/lodepng.cpp +++ b/src/lodepng.cpp @@ -1497,7 +1497,7 @@ static unsigned encodeLZ77(uivector* out, const unsigned char* in, size_t insize unsigned length, tablepos; #ifdef LAZY_MATCHING unsigned lazy = 0; - unsigned lazylength, lazyoffset; + unsigned lazylength = 0, lazyoffset = 0; #endif /*LAZY_MATCHING*/ unsigned hash, initialZeros = 0; unsigned backpos, current_offset, t1, t2, t11, current_length; @@ -4679,7 +4679,7 @@ static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, This is very slow and gives only slightly smaller, sometimes even larger, result*/ size_t size[5]; ucvector attempt[5]; /*five filtering attempts, one for each filter type*/ - size_t smallest; + size_t smallest = 0; unsigned type = 0, bestType = 0; unsigned char* dummy; LodePNG_CompressSettings zlibsettings = settings->zlibsettings; diff --git a/tests/lodepng.h b/src/lodepng.h index a0a654f..a0a654f 100644 --- a/tests/lodepng.h +++ b/src/lodepng.h diff --git a/src/mainwin.cc b/src/mainwin.cc index 17b9ef7..d5af643 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -584,15 +584,6 @@ void MainWindow::refreshDocument() setCurrentOutput(); } -AbstractNode *MainWindow::find_root_tag(AbstractNode *n) -{ - BOOST_FOREACH (AbstractNode *v, n->children) { - if (v->modinst->isRoot()) return v; - if (AbstractNode *vroot = find_root_tag(v)) return vroot; - } - return NULL; -} - /*! Parse and evaluate the design => this->root_node diff --git a/src/node.cc b/src/node.cc index e61174f..a7a7630 100644 --- a/src/node.cc +++ b/src/node.cc @@ -32,6 +32,7 @@ #include <iostream> #include <algorithm> +#include <boost/foreach.hpp> size_t AbstractNode::idx_counter; @@ -101,3 +102,14 @@ std::ostream &operator<<(std::ostream &stream, const AbstractNode &node) stream << node.toString(); return stream; } + +// Do we have an explicit root node (! modifier)? +AbstractNode *find_root_tag(AbstractNode *n) +{ + BOOST_FOREACH(AbstractNode *v, n->children) { + if (v->modinst->tag_root) return v; + if (AbstractNode *vroot = find_root_tag(v)) return vroot; + } + return NULL; +} + @@ -84,5 +84,6 @@ public: }; std::ostream &operator<<(std::ostream &stream, const AbstractNode &node); +AbstractNode *find_root_tag(AbstractNode *n); #endif diff --git a/src/openscad.cc b/src/openscad.cc index 880aa0d..0baba24 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -71,7 +71,7 @@ namespace fs = boost::filesystem; static void help(const char *progname) { fprintf(stderr, "Usage: %s [ -o output_file [ -d deps_file ] ]\\\n" - "%*s[ -m make_command ] [ -D var=val [..] ] filename\n", + "%*s[ -m make_command ] [ -D var=val [..] ] [ --render ] filename\n", progname, int(strlen(progname))+8, ""); exit(1); } @@ -132,6 +132,7 @@ int main(int argc, char **argv) desc.add_options() ("help,h", "help message") ("version,v", "print the version") + ("render", "if exporting an image, do a full CGAL render") ("o,o", po::value<string>(), "out-file") ("s,s", po::value<string>(), "stl-file") ("x,x", po::value<string>(), "dxf-file") @@ -242,12 +243,14 @@ int main(int argc, char **argv) const char *off_output_file = NULL; const char *dxf_output_file = NULL; const char *csg_output_file = NULL; + const char *png_output_file = NULL; QString suffix = QFileInfo(output_file).suffix().toLower(); if (suffix == "stl") stl_output_file = output_file; else if (suffix == "off") off_output_file = output_file; else if (suffix == "dxf") dxf_output_file = output_file; else if (suffix == "csg") csg_output_file = output_file; + else if (suffix == "png") png_output_file = output_file; else { fprintf(stderr, "Unknown suffix for output file %s\n", output_file); exit(1); @@ -261,6 +264,7 @@ int main(int argc, char **argv) Module *root_module; ModuleInstantiation root_inst; AbstractNode *root_node; + AbstractNode *absolute_root_node; handle_dep(filename); @@ -282,7 +286,13 @@ int main(int argc, char **argv) fs::current_path(fparent); AbstractNode::resetIndexCounter(); + absolute_root_node = root_module->evaluate(&root_ctx, &root_inst); root_node = root_module->evaluate(&root_ctx, &root_inst); + + // Do we have an explicit root node (! modifier)? + if (!(root_node = find_root_tag(absolute_root_node))) + root_node = absolute_root_node; + tree.setRoot(root_node); if (csg_output_file) { @@ -309,6 +319,7 @@ int main(int argc, char **argv) if ( stl_output_file ) geom_out = std::string(stl_output_file); else if ( off_output_file ) geom_out = std::string(off_output_file); else if ( dxf_output_file ) geom_out = std::string(dxf_output_file); + else if ( png_output_file ) geom_out = std::string(png_output_file); else { PRINTB("Output file:%s\n",output_file); PRINT("Sorry, don't know how to write deps for that file type. Exiting\n"); @@ -373,6 +384,21 @@ int main(int argc, char **argv) fstream.close(); } } + + if (png_output_file) { + std::ofstream fstream(png_output_file,std::ios::out|std::ios::binary); + if (!fstream.is_open()) { + PRINTB("Can't open file \"%s\" for export", png_output_file); + } + else { + if (vm.count("render")) { + export_png_with_cgal(&root_N, fstream); + } else { + export_png_with_opencsg(&root_N, fstream); + } + fstream.close(); + } + } #else fprintf(stderr, "OpenSCAD has been compiled without CGAL support!\n"); exit(1); diff --git a/tests/system-gl.cc b/src/system-gl.cc index 2e3f3bc..2e3f3bc 100644 --- a/tests/system-gl.cc +++ b/src/system-gl.cc diff --git a/src/system-gl.h b/src/system-gl.h index d7de3c6..57e9080 100644 --- a/src/system-gl.h +++ b/src/system-gl.h @@ -13,4 +13,9 @@ #endif #endif +#include <string> + +std::string glew_dump(bool dumpall); +bool report_glerror(const char * function); + #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8b7a252..bcdd6af 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -458,28 +458,32 @@ set(COMMON_SOURCES ../src/PolySetEvaluator.cc ../src/PolySetCache.cc ../src/Tree.cc - lodepng.cpp) + ../src/lodepng.cpp) # # Offscreen OpenGL context source code # if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") message(STATUS "Offscreen OpenGL Context - using Apple CGL") - set(OFFSCREEN_CTX_SOURCE "OffscreenContext.mm" CACHE TYPE STRING) + set(OFFSCREEN_CTX_SOURCE "OffscreenContextCGL.mm" CACHE TYPE STRING) + set(OFFSCREEN_IMGUTILS_SOURCE "imageutils-macosx.cc" CACHE TYPE STRING) elseif(UNIX) message(STATUS "Offscreen OpenGL Context - using Unix GLX") set(OFFSCREEN_CTX_SOURCE "OffscreenContextGLX.cc" CACHE TYPE STRING) + set(OFFSCREEN_IMGUTILS_SOURCE "imageutils-lodepng.cc" CACHE TYPE STRING) elseif(WIN32) message(STATUS "Offscreen OpenGL Context - using Microsoft WGL") set(OFFSCREEN_CTX_SOURCE "OffscreenContextWGL.cc" CACHE TYPE STRING) + set(OFFSCREEN_IMGUTILS_SOURCE "imageutils-lodepng.cc" CACHE TYPE STRING) endif() set(OFFSCREEN_SOURCES - OffscreenView.cc - ${OFFSCREEN_CTX_SOURCE} - imageutils.cc - fbo.cc - system-gl.cc) + ../src/OffscreenView.cc + ../src/${OFFSCREEN_CTX_SOURCE} + ../src/${OFFSCREEN_IMGUTILS_SOURCE} + ../src/imageutils.cc + ../src/fbo.cc + ../src/system-gl.cc) add_library(tests-core STATIC ${CORE_SOURCES}) target_link_libraries(tests-core ${OPENGL_LIBRARY}) @@ -546,14 +550,14 @@ target_link_libraries(cgalstlsanitytest tests-cgal ${TESTS-CGAL-LIBRARIES}) # # cgalpngtest # -add_executable(cgalpngtest cgalpngtest.cc bboxhelp.cc ../src/CGALRenderer.cc ../src/renderer.cc ../src/rendersettings.cc) +add_executable(cgalpngtest cgalpngtest.cc ../src/CGALRenderer.cc ../src/renderer.cc ../src/rendersettings.cc) set_target_properties(cgalpngtest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") target_link_libraries(cgalpngtest tests-offscreen tests-cgal ${OPENCSG_LIBRARY} ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${COCOA_LIBRARY}) # # cgalcachetest # -add_executable(cgalcachetest cgalcachetest.cc bboxhelp.cc) +add_executable(cgalcachetest cgalcachetest.cc) set_target_properties(cgalcachetest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") target_link_libraries(cgalcachetest tests-cgal ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${COCOA_LIBRARY}) @@ -574,6 +578,26 @@ set_target_properties(throwntogethertest PROPERTIES COMPILE_FLAGS "-DENABLE_OPEN target_link_libraries(throwntogethertest tests-offscreen tests-cgal ${OPENCSG_LIBRARY} ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${COCOA_LIBRARY}) # +# GUI binary tests +# +if(APPLE) + set(GUI_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../OpenSCAD.app/Contents/MacOS/OpenSCAD") +elseif(WIN32) + set(GUI_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../Release/openscad.exe") +else() + set(GUI_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../openscad") +endif() + +if(EXISTS "${GUI_BINPATH}") + add_executable(guicgalpngtest guicgalpngtest.cc) + set_target_properties(guicgalpngtest PROPERTIES COMPILE_FLAGS "-DBINPATH=${GUI_BINPATH}") + message(STATUS "Found OpenSCAD GUI binary: ${GUI_BINPATH}") +else() + message(STATUS "Couldn't find the OpenSCAD GUI binary: ${GUI_BINPATH}") + message(FATAL_ERROR "Please build the OpenSCAD GUI binary and place it here: ${GUI_BINPATH}" ) +endif() + +# # Tags tests as disabled. This is more convenient than removing them manually # from the lists of filenames # @@ -725,6 +749,8 @@ list(APPEND THROWNTOGETHERTEST_FILES ${OPENCSGTEST_FILES}) list(APPEND CGALSTLSANITYTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/normal-nan.scad) +list(APPEND GUICGALPNGTEST_FILES ${CGALPNGTEST_FILES}) + # Disable tests which are known to cause floating point comparison issues # Once we're capable of comparing these across platforms, we can put these back in disable_tests(dumptest_transform-tests @@ -757,6 +783,9 @@ disable_tests(opencsgtest_example006 cgalpngtest_example006) disable_tests(cgalpngtest_child-background cgalpngtest_highlight-and-background-modifier cgalpngtest_testcolornames + guicgalpngtest_child-background + guicgalpngtest_highlight-and-background-modifier + guicgalpngtest_testcolornames throwntogethertest_child-background throwntogethertest_highlight-and-background-modifier throwntogethertest_testcolornames) @@ -783,6 +812,8 @@ foreach(FILE ${EXAMPLE_FILES}) set_test_config(Examples ${TEST_FULLNAME}) get_test_fullname(throwntogethertest ${FILE} TEST_FULLNAME) set_test_config(Examples ${TEST_FULLNAME}) + get_test_fullname(guicgalpngtest ${FILE} TEST_FULLNAME) + set_test_config(Examples ${TEST_FULLNAME}) endforeach() # Workaround Gallium bugs @@ -833,6 +864,8 @@ add_cmdline_test(csgtermtest txt ${MINIMAL_FILES}) add_cmdline_test(cgalpngtest png ${CGALPNGTEST_FILES}) add_cmdline_test(opencsgtest png ${OPENCSGTEST_FILES}) add_cmdline_test(throwntogethertest png ${THROWNTOGETHERTEST_FILES}) +add_cmdline_test(guicgalpngtest png ${GUICGALPNGTEST_FILES}) + # FIXME: We don't actually need to compare the output of cgalstlsanitytest # with anything. It's self-contained and returns != 0 on error add_cmdline_test(cgalstlsanitytest txt ${CGALSTLSANITYTEST_FILES}) diff --git a/tests/EnforceConfig.cmake b/tests/EnforceConfig.cmake index 8e2ebf7..8ef7be8 100644 --- a/tests/EnforceConfig.cmake +++ b/tests/EnforceConfig.cmake @@ -1,4 +1,4 @@ -message("Enforcing config") if(NOT CTEST_CONFIGURATION_TYPE) + message("Enforcing Default test configuration. Use ctest -C <config> to override") set(CTEST_CONFIGURATION_TYPE Default) endif() diff --git a/tests/OffscreenContext.h b/tests/OffscreenContext.h deleted file mode 100644 index 6eebcba..0000000 --- a/tests/OffscreenContext.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef OFFSCREENCONTEXT_H_ -#define OFFSCREENCONTEXT_H_ - -#include <iostream> // for error output -#include <string> - -struct OffscreenContext *create_offscreen_context(int w, int h); -void bind_offscreen_context(OffscreenContext *ctx); -bool teardown_offscreen_context(OffscreenContext *ctx); -bool save_framebuffer(OffscreenContext *ctx, const char *filename); -std::string offscreen_context_getinfo(OffscreenContext *ctx); - -#endif diff --git a/tests/OffscreenContextGLX.cc b/tests/OffscreenContextGLX.cc deleted file mode 100644 index e607593..0000000 --- a/tests/OffscreenContextGLX.cc +++ /dev/null @@ -1,328 +0,0 @@ -/* - -Create an OpenGL context without creating an OpenGL Window. for Linux. - -See Also - - glxgears.c by Brian Paul from mesa-demos (mesa3d.org) - http://cgit.freedesktop.org/mesa/demos/tree/src/xdemos?id=mesa-demos-8.0.1 - http://www.opengl.org/sdk/docs/man/xhtml/glXIntro.xml - http://www.mesa3d.org/brianp/sig97/offscrn.htm - http://glprogramming.com/blue/ch07.html - OffscreenContext.mm (Mac OSX version) - -*/ - -/* - * Some portions of the code below are: - * Copyright (C) 1999-2001 Brian Paul All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "OffscreenContext.h" -#include "printutils.h" -#include "imageutils.h" -#include "system-gl.h" -#include "fbo.h" - -#include <GL/gl.h> -#include <GL/glx.h> - -#include <assert.h> -#include <sstream> - -#include <sys/utsname.h> // for uname - -using namespace std; - -struct OffscreenContext -{ - GLXContext openGLContext; - Display *xdisplay; - Window xwindow; - int width; - int height; - fbo_t *fbo; -}; - -void offscreen_context_init(OffscreenContext &ctx, int width, int height) -{ - ctx.width = width; - ctx.height = height; - ctx.openGLContext = NULL; - ctx.xdisplay = NULL; - ctx.xwindow = (Window)NULL; - ctx.fbo = NULL; -} - -string get_os_info() -{ - struct utsname u; - stringstream out; - - if (uname(&u) < 0) - out << "OS info: unknown, uname() error\n"; - else { - out << "OS info: " - << u.sysname << " " - << u.release << " " - << u.version << "\n"; - out << "Machine: " << u.machine; - } - return out.str(); -} - -string offscreen_context_getinfo(OffscreenContext *ctx) -{ - assert(ctx); - - if (!ctx->xdisplay) - return string("No GL Context initialized. No information to report\n"); - - int major, minor; - glXQueryVersion(ctx->xdisplay, &major, &minor); - - stringstream out; - out << "GL context creator: GLX\n" - << "PNG generator: lodepng\n" - << "GLX version: " << major << "." << minor << "\n" - << get_os_info(); - - return out.str(); -} - -static XErrorHandler original_xlib_handler = (XErrorHandler) NULL; -static bool XCreateWindow_failed = false; -static int XCreateWindow_error(Display *dpy, XErrorEvent *event) -{ - cerr << "XCreateWindow failed: XID: " << event->resourceid - << " request: " << (int)event->request_code - << " minor: " << (int)event->minor_code << "\n"; - char description[1024]; - XGetErrorText( dpy, event->error_code, description, 1023 ); - cerr << " error message: " << description << "\n"; - XCreateWindow_failed = true; - return 0; -} - -bool create_glx_dummy_window(OffscreenContext &ctx) -{ - /* - create a dummy X window without showing it. (without 'mapping' it) - and save information to the ctx. - - This purposely does not use glxCreateWindow, to avoid crashes, - "failed to create drawable" errors, and Mesa "WARNING: Application calling - GLX 1.3 function when GLX 1.3 is not supported! This is an application bug!" - - This function will alter ctx.openGLContext and ctx.xwindow if successfull - */ - - int attributes[] = { - GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT | GLX_PBUFFER_BIT, //support all 3, for OpenCSG - GLX_RENDER_TYPE, GLX_RGBA_BIT, - GLX_RED_SIZE, 8, - GLX_GREEN_SIZE, 8, - GLX_BLUE_SIZE, 8, - GLX_ALPHA_SIZE, 8, - GLX_DEPTH_SIZE, 24, // depth-stencil for OpenCSG - GLX_STENCIL_SIZE, 8, - GLX_DOUBLEBUFFER, True, - None - }; - - Display *dpy = ctx.xdisplay; - - int num_returned = 0; - GLXFBConfig *fbconfigs = glXChooseFBConfig( dpy, DefaultScreen(dpy), attributes, &num_returned ); - if ( fbconfigs == NULL ) { - cerr << "glXChooseFBConfig failed\n"; - return false; - } - - XVisualInfo *visinfo = glXGetVisualFromFBConfig( dpy, fbconfigs[0] ); - if ( visinfo == NULL ) { - cerr << "glXGetVisualFromFBConfig failed\n"; - XFree( fbconfigs ); - return false; - } - - // can't depend on xWin==NULL at failure. use a custom Xlib error handler instead. - original_xlib_handler = XSetErrorHandler( XCreateWindow_error ); - - Window root = DefaultRootWindow( dpy ); - XSetWindowAttributes xwin_attr; - int width = ctx.width; - int height = ctx.height; - xwin_attr.background_pixmap = None; - xwin_attr.background_pixel = 0; - xwin_attr.border_pixel = 0; - xwin_attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone); - xwin_attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; - unsigned long int mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; - - Window xWin = XCreateWindow( dpy, root, 0, 0, width, height, - 0, visinfo->depth, InputOutput, - visinfo->visual, mask, &xwin_attr ); - - // Window xWin = XCreateSimpleWindow( dpy, DefaultRootWindow(dpy), 0,0,42,42, 0,0,0 ); - - XSync( dpy, false ); - if ( XCreateWindow_failed ) { - XFree( visinfo ); - XFree( fbconfigs ); - return false; - } - XSetErrorHandler( original_xlib_handler ); - - // Most programs would call XMapWindow here. But we don't, to keep the window hidden - // XMapWindow( dpy, xWin ); - - GLXContext context = glXCreateNewContext( dpy, fbconfigs[0], GLX_RGBA_TYPE, NULL, True ); - if ( context == NULL ) { - cerr << "glXCreateNewContext failed\n"; - XDestroyWindow( dpy, xWin ); - XFree( visinfo ); - XFree( fbconfigs ); - return false; - } - - //GLXWindow glxWin = glXCreateWindow( dpy, fbconfigs[0], xWin, NULL ); - - if (!glXMakeContextCurrent( dpy, xWin, xWin, context )) { - //if (!glXMakeContextCurrent( dpy, glxWin, glxWin, context )) { - cerr << "glXMakeContextCurrent failed\n"; - glXDestroyContext( dpy, context ); - XDestroyWindow( dpy, xWin ); - XFree( visinfo ); - XFree( fbconfigs ); - return false; - } - - ctx.openGLContext = context; - ctx.xwindow = xWin; - - XFree( visinfo ); - XFree( fbconfigs ); - - return true; -} - - -Bool create_glx_dummy_context(OffscreenContext &ctx) -{ - // This will alter ctx.openGLContext and ctx.xdisplay and ctx.xwindow if successfull - int major; - int minor; - Bool result = False; - - ctx.xdisplay = XOpenDisplay( NULL ); - if ( ctx.xdisplay == NULL ) { - cerr << "Unable to open a connection to the X server\n"; - return False; - } - - // glxQueryVersion is not always reliable. Use it, but then - // also check to see if GLX 1.3 functions exist - - glXQueryVersion(ctx.xdisplay, &major, &minor); - - if ( major==1 && minor<=2 && glXGetVisualFromFBConfig==NULL ) { - cerr << "Error: GLX version 1.3 functions missing. " - << "Your GLX version: " << major << "." << minor << endl; - } else { - result = create_glx_dummy_window(ctx); - } - - if (!result) XCloseDisplay( ctx.xdisplay ); - return result; -} - -OffscreenContext *create_offscreen_context(int w, int h) -{ - OffscreenContext *ctx = new OffscreenContext; - offscreen_context_init( *ctx, w, h ); - - // before an FBO can be setup, a GLX context must be created - // this call alters ctx->xDisplay and ctx->openGLContext - // and ctx->xwindow if successfull - if (!create_glx_dummy_context( *ctx )) { - return NULL; - } - - // glewInit must come after Context creation and before FBO calls. - GLenum err = glewInit(); - if (GLEW_OK != err) { - cerr << "Unable to init GLEW: " << glewGetErrorString(err) << endl; - return NULL; - } - - ctx->fbo = fbo_new(); - if (!fbo_init(ctx->fbo, w, h)) { - cerr << "GL Framebuffer Object init failed; dumping GLEW info" << endl; - return NULL; - } - - return ctx; -} - -bool teardown_offscreen_context(OffscreenContext *ctx) -{ - if (ctx) { - fbo_unbind(ctx->fbo); - fbo_delete(ctx->fbo); - XDestroyWindow( ctx->xdisplay, ctx->xwindow ); - glXDestroyContext( ctx->xdisplay, ctx->openGLContext ); - XCloseDisplay( ctx->xdisplay ); - return true; - } - return false; -} - -/*! - Capture framebuffer from OpenGL and write it to the given filename as PNG. -*/ -bool save_framebuffer(OffscreenContext *ctx, const char *filename) -{ - glXSwapBuffers(ctx->xdisplay, ctx->xwindow); - if (!ctx || !filename) return false; - int samplesPerPixel = 4; // R, G, B and A - GLubyte pixels[ctx->width * ctx->height * samplesPerPixel]; - glReadPixels(0, 0, ctx->width, ctx->height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Flip it vertically - images read from OpenGL buffers are upside-down - int rowBytes = samplesPerPixel * ctx->width; - unsigned char *flippedBuffer = (unsigned char *)malloc(rowBytes * ctx->height); - if (!flippedBuffer) { - cerr << "Unable to allocate flipped buffer for corrected image."; - return 1; - } - flip_image(pixels, flippedBuffer, samplesPerPixel, ctx->width, ctx->height); - - bool writeok = write_png(filename, flippedBuffer, ctx->width, ctx->height); - - free(flippedBuffer); - - return writeok; -} - -void bind_offscreen_context(OffscreenContext *ctx) -{ - if (ctx) fbo_bind(ctx->fbo); -} diff --git a/tests/bboxhelp.cc b/tests/bboxhelp.cc deleted file mode 100644 index b70ddcf..0000000 --- a/tests/bboxhelp.cc +++ /dev/null @@ -1,21 +0,0 @@ -/* - Work around bugs in MSVC compiler with Eigen AlignmentBox - bbox.min and bbox.max will fail with Syntax Errors if placed inside - of cgalpngtest.cc -*/ - -#include "linalg.h" - -Vector3d getBoundingCenter(BoundingBox bbox) -{ - Vector3d center = (bbox.min() + bbox.max()) / 2; - return center; // Vector3d(0,0,0); -} - -double getBoundingRadius(BoundingBox bbox) -{ - double radius = (bbox.max() - bbox.min()).norm() / 2; - return radius; // 0; -} - - diff --git a/tests/cgalcachetest.cc b/tests/cgalcachetest.cc index 46e0e9a..3a0a855 100644 --- a/tests/cgalcachetest.cc +++ b/tests/cgalcachetest.cc @@ -70,15 +70,6 @@ void cgalTree(Tree &tree) evaluate.execute(); } -AbstractNode *find_root_tag(AbstractNode *n) -{ - foreach(AbstractNode *v, n->children) { - if (v->modinst->tag_root) return v; - if (AbstractNode *vroot = find_root_tag(v)) return vroot; - } - return NULL; -} - po::variables_map parse_options(int argc, char *argv[]) { po::options_description desc("Allowed options"); diff --git a/tests/cgalpngtest.cc b/tests/cgalpngtest.cc index 56861c6..ac4ebdf 100644 --- a/tests/cgalpngtest.cc +++ b/tests/cgalpngtest.cc @@ -70,15 +70,6 @@ void cgalTree(Tree &tree) evaluate.execute(); } -AbstractNode *find_root_tag(AbstractNode *n) -{ - foreach(AbstractNode *v, n->children) { - if (v->modinst->tag_root) return v; - if (AbstractNode *vroot = find_root_tag(v)) return vroot; - } - return NULL; -} - struct CsgInfo { OffscreenView *glview; diff --git a/tests/cgalstlsanitytest.cc b/tests/cgalstlsanitytest.cc index 52cfb41..2815463 100644 --- a/tests/cgalstlsanitytest.cc +++ b/tests/cgalstlsanitytest.cc @@ -69,15 +69,6 @@ void cgalTree(Tree &tree) evaluate.execute(); } -AbstractNode *find_root_tag(AbstractNode *n) -{ - foreach(AbstractNode *v, n->children) { - if (v->modinst->tag_root) return v; - if (AbstractNode *vroot = find_root_tag(v)) return vroot; - } - return NULL; -} - int main(int argc, char **argv) { int retval = 0; diff --git a/tests/cgaltest.cc b/tests/cgaltest.cc index b546286..4a15050 100644 --- a/tests/cgaltest.cc +++ b/tests/cgaltest.cc @@ -65,15 +65,6 @@ void cgalTree(Tree &tree) evaluate.execute(); } -AbstractNode *find_root_tag(AbstractNode *n) -{ - foreach(AbstractNode *v, n->children) { - if (v->modinst->tag_root) return v; - if (AbstractNode *vroot = find_root_tag(v)) return vroot; - } - return NULL; -} - int main(int argc, char **argv) { if (argc != 2) { diff --git a/tests/csgtestcore.cc b/tests/csgtestcore.cc index acc7c31..36e94e7 100644 --- a/tests/csgtestcore.cc +++ b/tests/csgtestcore.cc @@ -65,15 +65,6 @@ CsgInfo::CsgInfo() { glview = NULL; } -AbstractNode *find_root_tag(AbstractNode *n) -{ - foreach(AbstractNode *v, n->children) { - if (v->modinst->tag_root) return v; - if (AbstractNode *vroot = find_root_tag(v)) return vroot; - } - return NULL; -} - string info_dump(OffscreenView *glview) { assert(glview); @@ -370,7 +361,7 @@ int csgtestcore(int argc, char *argv[], test_type_e test_type) csgInfo.glview->paintGL(); - csgInfo.glview->save(outfilename); + if (outfilename) csgInfo.glview->save(outfilename); delete root_node; delete root_module; diff --git a/tests/guicgalpngtest.cc b/tests/guicgalpngtest.cc new file mode 100644 index 0000000..0a63ae8 --- /dev/null +++ b/tests/guicgalpngtest.cc @@ -0,0 +1,28 @@ +// Wrapper around openscad gui binary, so it can act like a 'test' + +#include <unistd.h> +#include <stdio.h> +#ifndef BINPATH +#error please define BINPATH=/some/path/openscad when compiling +#endif +#define PREQUOTE(x) #x +#define QUOTE(x) PREQUOTE(x) +int main( int argc, char * argv[] ) +{ + fprintf(stderr,"%s: wrapper for OpenSCAD at %s\n", argv[0], QUOTE( BINPATH ) ); + if ( argc != 3 ) { + fprintf(stderr,"%s: bad number of arguments: %i\n", argv[0], argc); + return 1; + } + char *newargs[6]; + char *scadfilename = argv[1]; + char *pngfilename = argv[2]; + newargs[0] = const_cast<char *>(QUOTE( BINPATH )); + newargs[1] = scadfilename; + newargs[2] = const_cast<char *>("-o"); + newargs[3] = pngfilename; + newargs[4] = const_cast<char *>("--render"); + newargs[5] = NULL; + return execv( newargs[0], newargs ); +} + diff --git a/tests/imageutils-lodepng.cc b/tests/imageutils-lodepng.cc deleted file mode 100644 index 8460d9e..0000000 --- a/tests/imageutils-lodepng.cc +++ /dev/null @@ -1,24 +0,0 @@ -#include "lodepng.h" -#include <stdio.h> - -bool write_png(const char *filename, unsigned char *pixels, int width, int height) -{ - //encoder.settings.zlibsettings.windowSize = 2048; - //LodePNG_Text_add(&encoder.infoPng.text, "Comment", "Created with LodePNG"); - - size_t dataout_size = -1; - unsigned char *dataout = (unsigned char *)malloc(width*height*4); - LodePNG_encode(&dataout, &dataout_size, pixels, width, height, LCT_RGBA, 8); - //LodePNG_saveFile(dataout, dataout_size, "blah2.png"); - - FILE *f = fopen(filename, "wb"); - if (!f) { - free(dataout); - return false; - } - - fwrite(dataout, 1, dataout_size, f); - fclose(f); - free(dataout); - return true; -} diff --git a/tests/imageutils.cc b/tests/imageutils.cc deleted file mode 100644 index e15ba2b..0000000 --- a/tests/imageutils.cc +++ /dev/null @@ -1,16 +0,0 @@ -#include "imageutils.h" -#include <string.h> - -void flip_image(const unsigned char *src, unsigned char *dst, size_t pixelsize, size_t width, size_t height) -{ - size_t rowBytes = pixelsize * width; - for (size_t i = 0 ; i < height ; i++) { - memmove(dst + (height - i - 1) * rowBytes, src + i * rowBytes, rowBytes); - } -} - -#ifdef __APPLE__ -#include "imageutils-macosx.cc" -#else -#include "imageutils-lodepng.cc" -#endif diff --git a/tests/system-gl.h b/tests/system-gl.h deleted file mode 100644 index 4a8ccac..0000000 --- a/tests/system-gl.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef SYSTEMGL_H_ -#define SYSTEMGL_H_ - -#include <GL/glew.h> -#include <string> - -std::string glew_dump(bool dumpall=false); -bool report_glerror(const char *task); - -#endif diff --git a/tests/test_cmdline_tool.py b/tests/test_cmdline_tool.py index 889c429..ebe60df 100755 --- a/tests/test_cmdline_tool.py +++ b/tests/test_cmdline_tool.py @@ -28,13 +28,19 @@ import shutil import platform import string +share_expected_imgs = {} +share_expected_imgs["guicgalpngtest"] = "cgalpngtest" + def initialize_environment(): if not options.generate: options.generate = bool(os.getenv("TEST_GENERATE")) return True def init_expected_filename(testname, cmd): global expecteddir, expectedfilename - expecteddir = os.path.join(options.regressiondir, os.path.split(cmd)[1]) + testbinary_filename = os.path.split(cmd)[1] + if testbinary_filename in share_expected_imgs: + testbinary_filename = share_expected_imgs[testbinary_filename] + expecteddir = os.path.join(options.regressiondir, testbinary_filename ) expectedfilename = os.path.join(expecteddir, testname + "-expected." + options.suffix) expectedfilename = os.path.normpath( expectedfilename ) |